Skip to content

Commit c0b4e71

Browse files
authored
Merge pull request #5 from SPlayer-Dev/lyric-land
feat: 灵动岛歌词
2 parents 4a696c3 + e806b60 commit c0b4e71

32 files changed

Lines changed: 1846 additions & 126 deletions

File tree

electron.vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export default defineConfig({
5151
input: {
5252
index: resolve(__dirname, "index.html"),
5353
"desktop-lyric": resolve(__dirname, "windows/desktop-lyric/index.html"),
54+
"dynamic-island": resolve(__dirname, "windows/dynamic-island/index.html"),
5455
},
5556
},
5657
},

electron/main/ipc/config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import {
1111
setTaskbarProgress,
1212
applyDesktopLyricLock,
1313
applyDesktopLyricAlwaysOnTop,
14+
applyDynamicIslandAlwaysOnTop,
15+
applyDynamicIslandSnapCentered,
16+
applyDynamicIslandNonOcclusive,
1417
} from "@main/window";
1518
import { broadcast } from "@main/utils/broadcast";
1619

@@ -37,11 +40,24 @@ const applyConfigChange = (keyPath: string, value: unknown): void => {
3740
case "desktopLyric.alwaysOnTop":
3841
applyDesktopLyricAlwaysOnTop(value as boolean);
3942
break;
43+
case "dynamicIsland.alwaysOnTop":
44+
applyDynamicIslandAlwaysOnTop(value as boolean);
45+
break;
46+
case "dynamicIsland.snapCentered":
47+
applyDynamicIslandSnapCentered(value as boolean);
48+
break;
49+
case "dynamicIsland.nonOcclusive":
50+
applyDynamicIslandNonOcclusive(value as boolean);
51+
break;
4052
}
4153
// 桌面歌词配置变更广播到所有窗口
4254
if (keyPath.startsWith("desktopLyric.")) {
4355
broadcast("desktopLyric:configChange", store.get("desktopLyric"));
4456
}
57+
// 灵动岛配置变更广播到所有窗口
58+
if (keyPath.startsWith("dynamicIsland.")) {
59+
broadcast("dynamicIsland:configChange", store.get("dynamicIsland"));
60+
}
4561
};
4662

4763
/** 注册配置相关 IPC */

electron/main/ipc/window.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ipcMain } from "electron";
2+
import { store } from "@main/store";
23
import {
34
toggleDesktopLyricWindow,
45
closeDesktopLyricWindow,
@@ -7,6 +8,13 @@ import {
78
applyDesktopLyricMouseIgnore,
89
moveDesktopLyricWindow,
910
saveDesktopLyricState,
11+
toggleDynamicIslandWindow,
12+
closeDynamicIslandWindow,
13+
getDynamicIslandWindow,
14+
moveDynamicIslandWindow,
15+
saveDynamicIslandState,
16+
applyDynamicIslandWidth,
17+
applyDynamicIslandHeight,
1018
} from "@main/window";
1119

1220
/** 窗口管理 IPC */
@@ -39,4 +47,39 @@ export const registerWindowIpc = (): void => {
3947
ipcMain.on("desktopLyric:saveState", () => {
4048
saveDesktopLyricState();
4149
});
50+
51+
// 切换灵动岛窗口
52+
ipcMain.handle("window:toggleDynamicIsland", () => toggleDynamicIslandWindow());
53+
54+
// 关闭灵动岛窗口
55+
ipcMain.handle("window:closeDynamicIsland", () => closeDynamicIslandWindow());
56+
57+
// 查询灵动岛窗口是否打开
58+
ipcMain.handle("window:isDynamicIslandOpen", () => !!getDynamicIslandWindow());
59+
60+
// 灵动岛拖拽移动
61+
ipcMain.on("dynamicIsland:move", (_event, x: number, y: number) => {
62+
moveDynamicIslandWindow(x, y);
63+
});
64+
65+
// 灵动岛拖拽结束:主进程判定吸附并持久化
66+
ipcMain.on("dynamicIsland:saveState", () => {
67+
saveDynamicIslandState();
68+
});
69+
70+
// 灵动岛宽度变化:渲染端上报目标宽度
71+
ipcMain.on("dynamicIsland:resize", (_event, width: number) => {
72+
applyDynamicIslandWidth(width);
73+
});
74+
75+
// 灵动岛高度变化
76+
ipcMain.on("dynamicIsland:setHeight", (_event, height: number) => {
77+
applyDynamicIslandHeight(height);
78+
});
79+
80+
// 灵动岛查询当前吸附模式(HMR 后渲染端主动拉取)
81+
ipcMain.handle("dynamicIsland:getMode", () => {
82+
const saved = store.get("windowStates.dynamicIsland");
83+
return saved.mode === "floating" ? "floating" : "snapped";
84+
});
4285
};

electron/main/services/thumbar.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { broadcast } from "@main/utils/broadcast";
33
import { loadThemedIcon } from "@main/utils/icon";
44
import { t } from "@main/utils/i18n";
55
import { thumbarLog } from "@main/utils/logger";
6-
import { getMainWindow } from "@main/window/main";
76

87
export interface Thumbar {
98
clearThumbar(): void;
@@ -88,10 +87,8 @@ class ThumbarImpl implements Thumbar {
8887
}
8988

9089
/** 初始化缩略图工具栏 */
91-
export const initThumbar = (): Thumbar | null => {
90+
export const initThumbar = (win: BrowserWindow): Thumbar | null => {
9291
if (process.platform !== "win32") return null;
93-
const win = getMainWindow();
94-
if (!win) return null;
9592
try {
9693
thumbarLog.info("初始化缩略图工具栏");
9794
thumbar = new ThumbarImpl(win);

electron/main/services/tray.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { appName } from "@main/utils/config";
55
import { loadIcon, loadThemedIcon } from "@main/utils/icon";
66
import { t } from "@main/utils/i18n";
77
import { trayLog } from "@main/utils/logger";
8-
import { toggleDesktopLyricWindow, focusMainWindow } from "@main/window";
8+
import { toggleDesktopLyricWindow, toggleDynamicIslandWindow, focusMainWindow } from "@main/window";
99

1010
type PlayState = "playing" | "paused";
1111

@@ -15,6 +15,7 @@ let songName = "";
1515
let repeatMode: RepeatMode = "list";
1616
let shuffleMode: ShuffleMode = "off";
1717
let desktopLyricOpen = false;
18+
let dynamicIslandOpen = false;
1819

1920
const repeatLabel = (mode: RepeatMode): string =>
2021
({ list: t("repeatList"), one: t("repeatOne"), off: t("repeatOff") })[mode];
@@ -107,6 +108,11 @@ const buildMenu = (): Menu => {
107108
icon: menuIcon("lyric"),
108109
click: () => toggleDesktopLyricWindow(),
109110
},
111+
{
112+
label: dynamicIslandOpen ? t("closeDynamicIsland") : t("openDynamicIsland"),
113+
icon: menuIcon("lyric"),
114+
click: () => toggleDynamicIslandWindow(),
115+
},
110116
{ type: "separator" },
111117
{
112118
label: t("quit"),
@@ -184,6 +190,13 @@ export const setTrayDesktopLyric = (open: boolean): void => {
184190
refreshTray();
185191
};
186192

193+
/** 同步灵动岛窗口开关状态到托盘 */
194+
export const setTrayDynamicIsland = (open: boolean): void => {
195+
if (dynamicIslandOpen === open) return;
196+
dynamicIslandOpen = open;
197+
refreshTray();
198+
};
199+
187200
/** 销毁托盘 */
188201
export const destroyTray = (): void => {
189202
tray?.destroy();

electron/main/utils/i18n.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ interface MainMessages {
1212
repeatOff: string;
1313
openDesktopLyric: string;
1414
closeDesktopLyric: string;
15+
openDynamicIsland: string;
16+
closeDynamicIsland: string;
1517
quit: string;
1618
}
1719

@@ -28,6 +30,8 @@ const messages: Record<LocaleCode, MainMessages> = {
2830
repeatOff: "不循环",
2931
openDesktopLyric: "开启桌面歌词",
3032
closeDesktopLyric: "关闭桌面歌词",
33+
openDynamicIsland: "开启灵动岛",
34+
closeDynamicIsland: "关闭灵动岛",
3135
quit: "退出",
3236
},
3337
"en-US": {
@@ -42,6 +46,8 @@ const messages: Record<LocaleCode, MainMessages> = {
4246
repeatOff: "No Repeat",
4347
openDesktopLyric: "Open Desktop Lyric",
4448
closeDesktopLyric: "Close Desktop Lyric",
49+
openDynamicIsland: "Open Dynamic Island",
50+
closeDynamicIsland: "Close Dynamic Island",
4551
quit: "Quit",
4652
},
4753
};

0 commit comments

Comments
 (0)