-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathminiPlayer.ts
More file actions
83 lines (79 loc) Β· 3.05 KB
/
miniPlayer.ts
File metadata and controls
83 lines (79 loc) Β· 3.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { Window as TauriWindow, currentMonitor } from "@tauri-apps/api/window";
import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
const MINI_LABEL = "mini";
const WIDTH = 280;
const HEIGHT = 380;
/** Margin from the screen edge (logical pixels). Matches Spotify's
* bottom-right offset, leaving room for the Windows taskbar / macOS
* Dock. Smaller offsets feel cramped against the corner. */
const EDGE_MARGIN = 24;
/**
* Open the always-on-top mini-player window. If it already exists,
* just bring it to the front instead of creating a duplicate. Hides
* the main window so the user gets a clean swap.
*
* The mini-player loads the same bundle with `?mini=1` so
* [`main.tsx`] can boot into a stripped-down provider tree.
*/
export async function openMiniPlayer(): Promise<void> {
const existing = await TauriWindow.getByLabel(MINI_LABEL);
if (existing) {
await existing.show();
await existing.unminimize();
await existing.setFocus();
} else {
// The URL is resolved by the Tauri bundler β `index.html` is the
// single entry point Vite produces; the search param is what the
// frontend branches on.
// Anchor to the bottom-right of the primary monitor β Spotify
// does the same, and it's the corner least likely to overlap the
// user's active work area. `currentMonitor` returns physical size
// + scale; convert back to logical pixels for `x` / `y`.
let x: number | undefined;
let y: number | undefined;
try {
const monitor = await currentMonitor();
if (monitor) {
const scale = monitor.scaleFactor || 1;
const logicalW = monitor.size.width / scale;
const logicalH = monitor.size.height / scale;
x = Math.max(0, Math.round(logicalW - WIDTH - EDGE_MARGIN));
y = Math.max(0, Math.round(logicalH - HEIGHT - EDGE_MARGIN));
}
} catch (err) {
console.warn(
"[miniPlayer] monitor query failed, falling back to centered",
err,
);
}
const win = new WebviewWindow(MINI_LABEL, {
url: "index.html?mini=1",
title: "WaveFlow",
width: WIDTH,
height: HEIGHT,
minWidth: 240,
minHeight: 320,
alwaysOnTop: true,
// We render our own title bar (pin / drag-dots / close) inside
// the webview so the widget stays compact and on-brand.
decorations: false,
transparent: false,
resizable: true,
x,
y,
// Only center when we couldn't compute a corner position.
center: x == null || y == null,
skipTaskbar: false,
});
// Surface init errors via the standard event channel β `new` is
// sync but the underlying create is async on the Rust side.
await new Promise<void>((resolve, reject) => {
win.once("tauri://created", () => resolve());
win.once("tauri://error", (e) => reject(e.payload));
});
}
// Hide the main window so we don't have two players visible at
// once β the mini-player has a Maximize button to restore it.
const main = await TauriWindow.getByLabel("main");
if (main) await main.hide();
}