import type { IslandLayoutSettings as ContractIslandLayoutSettings, IslandTimePointDto as ContractIslandTimePointDto, IslandWeather, ScheduleDto as ContractScheduleDto, } from '@classhelper/shared'; import { contextBridge, ipcRenderer } from 'electron'; import type { IslandAppearance, IslandState } from '@classhelper/shared'; /** * 灵动岛窗口的预加载脚本。 * 只暴露「订阅状态/外观」与「发送用户操作」两件事,渲染进程不接触任何 Node 能力。 */ export interface IslandBridge { getIslandWeather(coordinates: { latitude: number; longitude: number }): Promise; getLayout(): Promise; saveLayout(value: ContractIslandLayoutSettings): Promise; onLayout(handler: (value: ContractIslandLayoutSettings) => void): void; getEditing(): Promise; getVisible(): Promise; onVisibility(handler: (visible: boolean) => void): void; setEditing(value: boolean): Promise; onEditing(handler: (value: boolean) => void): void; reportSize(value: { width: number; height: number }): void; reportResidentControl(value: { x: number; y: number; width: number; height: number } | null): void; noteResidentTouch(): void; onViewport(handler: (value: { width: number; height: number }) => void): () => void; getSchedules(): Promise; onSchedules(handler: (value: ContractScheduleDto[]) => void): void; getTimePoints(): Promise; onTimePoints(handler: (value: ContractIslandTimePointDto[]) => void): void; onState(handler: (state: IslandState) => void): void; sendAction( action: | 'expand' | 'expand-list' | 'collapse' | 'dismiss' | 'dismiss-all' | 'mark-read' | 'mark-all-read' | 'open-app', id?: string, ): void; getState(): Promise; /** 订阅个性化外观(设置页改动后实时生效) */ onAppearance(handler: (appearance: IslandAppearance) => void): void; getAppearance(): Promise; /** 命中提示:指针在岛上 → 让窗口接收鼠标(只上报 true,关闭由主进程轮询决定) */ setInteractive(interactive: boolean): void; /** 上报岛体矩形(窗口内 CSS px):主进程据此按光标位置兜底校正命中 */ setHitRect(rect: { x: number; y: number; width: number; height: number } | null): void; /** * 上报"本机是触摸屏":主进程据此把窗口改为贴合岛体并始终接收输入。 * 触摸屏上按光标判定命中的那套永远打不开(触摸不产生 mousemove、也不动系统光标), * 不上报的话手指点不到岛(希沃白板等触摸设备)。 */ setTouchMode(enabled: boolean): void; /** 心跳:主进程据此发现"渲染进程卡死的幽灵窗口"(岛还在屏幕上但点不动)并重建窗口 */ alive(): void; } const bridge: IslandBridge = { getIslandWeather: (coordinates) => ipcRenderer.invoke('island:weather', coordinates), getLayout: () => ipcRenderer.invoke('island:get-layout'), saveLayout: (value) => ipcRenderer.invoke('island:save-layout', value), onLayout: (handler) => { ipcRenderer.on('island:layout', (_event, value) => handler(value)); }, getEditing: () => ipcRenderer.invoke('island:get-editing'), getVisible: () => ipcRenderer.invoke('island:get-visible'), onVisibility: (handler) => { ipcRenderer.on('island:visibility', (_event, value) => handler(value)); }, setEditing: (value) => ipcRenderer.invoke('island:set-editing', value), onEditing: (handler) => { ipcRenderer.on('island:editing', (_event, value) => handler(value)); }, reportSize: (value) => ipcRenderer.send('island:content-size', value), reportResidentControl: (value) => ipcRenderer.send('island:resident-control', value), noteResidentTouch: () => ipcRenderer.send('island:resident-touch'), onViewport: (handler) => { const listener = (_event: Electron.IpcRendererEvent, value: { width: number; height: number }) => handler(value); ipcRenderer.on('island:viewport', listener); return () => ipcRenderer.removeListener('island:viewport', listener); }, getSchedules: () => ipcRenderer.invoke('island:get-schedules'), onSchedules: (handler) => { ipcRenderer.on('island:schedules', (_event, value) => handler(value)); }, getTimePoints: () => ipcRenderer.invoke('island:get-time-points'), onTimePoints: (handler) => { ipcRenderer.on('island:time-points', (_event, value) => handler(value)); }, onState: (handler) => { ipcRenderer.on('island:state', (_event, state: IslandState) => handler(state)); }, sendAction: (action, id) => { ipcRenderer.send('island:action', { action, id }); }, getState: () => ipcRenderer.invoke('island:get-state'), onAppearance: (handler) => { ipcRenderer.on('island:appearance', (_event, appearance: IslandAppearance) => handler(appearance)); }, getAppearance: () => ipcRenderer.invoke('island:get-appearance'), setInteractive: (interactive) => { ipcRenderer.send('island:set-interactive', interactive); }, setHitRect: (rect) => { ipcRenderer.send('island:set-hit-rect', rect); }, setTouchMode: (enabled) => { ipcRenderer.send('island:set-touch-mode', enabled); }, alive: () => { ipcRenderer.send('island:alive'); }, }; contextBridge.exposeInMainWorld('island', bridge);