init: 鏅烘収鍐滀笟澶ф暟鎹彲瑙嗗寲鎺у埗涓績 v0.1.0锛圗lectron + React 19 + TS 閲嶅啓鐗堬級

- 缂栬緫鍣細椤圭洰鏂板缓/鎵撳紑/淇濆瓨/鍙﹀瓨涓猴紙璁颁綇鏈€鍚庢枃浠跺す锛夈€侀〉闈㈠鍒?鍓嶆彃/绉诲姩/澶嶅埗/鎴浘/椤甸潰閲嶆帓銆丅ase64 棰勮鍥俱€? 椤归〉闈㈠睘鎬?- 鍙戝竷鍣細棰勮鏈〉/鍗曞睆鍙戝竷(杞挱)/澶氬睆鍙戝竷(alsoMain)/鍋滄杞挱/鍏抽棴鎵€鏈?妫€娴嬪睆骞曪紝蹇嵎閿綋绯?1-9闀挎寜闃堝€?鈫戔啌/Esc/F5/閿佸畾閿?
- 閰嶇疆鍣細椤圭洰淇℃伅/閬ユ帶璁剧疆/蹇嵎鏂瑰紡x3/鍙戝竷璁剧疆/璁惧娴嬭瘯/鍏充簬 6 鑿滃崟
- 鏁版嵁瀹夊叏锛欽SON 宸ョ▼ AES-256-GCM 瀵嗙爜鍔犲瘑
- 鐗堟湰鍙峰崟涓€鏉ユ簮(package.json)锛岀豢鑹茬増 zip 鎵撳寘(electron-builder)
- 闄勫甫锛氶渶姹傛枃妗?v2.0銆佹寚浠ゆ竻鍗曘€乸rototype 澶у睆鍘熷瀷
This commit is contained in:
2026-08-23 10:57:14 +08:00
commit 68810be606
61 changed files with 14564 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
export interface PlayingInfo {
title: string
url: string
}
export interface ScreenInfo {
index: number
name: string
res: string
pos: string
primary: boolean
playing: PlayingInfo | null
}
export interface PageFile {
PageID: string
PageTitle: string
PageDescribe: string
ScreenIndex: number
ControllerIndex: number
IsDisplay: boolean
PageUrl: string
PageParams: string
PagePreview?: string
}
export interface ProjectJson {
appName?: string
version?: string
pages?: PageFile[]
}
export interface PublishDefaults {
mode: 'multi' | 'single' | 'main'
screenIndex: number
alsoMain: boolean
intervalSec: number
}
export interface AppSettings {
projectName: string
projectVersion: string
jsonPassword: string
keyThreshold: number
keyLockKey: string
publish: PublishDefaults
}
export type ShortcutKind = 'app' | 'edit' | 'publish'
export interface ShortcutResult {
ok: boolean
message?: string
}
export interface LoadPayload {
path: string
json: ProjectJson | null
mode: 'edit' | 'publish'
}
export interface AppInfo {
appVersion: string
electron: string
chrome: string
node: string
platform: string
}
export interface SmartAgriApi {
win: {
minimize(): void
toggleMaximize(): void
close(): void
onMaximized(cb: (v: boolean) => void): () => void
}
screens: {
list(): Promise<ScreenInfo[]>
states(): Promise<ScreenInfo[]>
publish(payload: { index: number; url: string; title: string }): Promise<ScreenInfo[]>
close(index: number): Promise<ScreenInfo[]>
closeAll(): Promise<ScreenInfo[]>
mainIndex(): Promise<number>
publishMain(payload: { url: string; title: string }): Promise<ScreenInfo[]>
restoreMain(): Promise<ScreenInfo[]>
probe(): Promise<ScreenInfo[]>
keyLock(locked: boolean): Promise<boolean>
keyLockState(): Promise<boolean>
onChanged(cb: (list: ScreenInfo[]) => void): () => void
}
carousel: {
start(payload: { index: number; pages: { url: string; title: string }[]; intervalSec?: number }): Promise<ScreenInfo[]>
stop(index: number): Promise<ScreenInfo[]>
}
project: {
saveAs(data: string, password?: string): Promise<string | null>
save(path: string, data: string, password?: string): Promise<string | null>
open(password?: string): Promise<{ path: string; json: ProjectJson | null; error?: string } | null>
openAt(path: string, password?: string): Promise<{ path: string; json: ProjectJson | null; error?: string }>
}
settings: {
get(): Promise<AppSettings>
set(patch: Partial<AppSettings>): Promise<AppSettings>
}
shortcuts: {
create(kind: ShortcutKind): Promise<ShortcutResult>
}
app: {
info(): Promise<AppInfo>
consumeLoad(): Promise<LoadPayload | null>
onLoadProject(cb: (payload: LoadPayload) => void): () => void
}
key: {
onScreenKey(cb: (n: number) => void): () => void
}
page: {
snapshot(url: string): Promise<string | null>
}
syncProject(pages: unknown[]): void
setDirty(d: boolean): void
beep(): Promise<void>
createShortcut(): Promise<boolean>
confirm(message: string): Promise<boolean>
}
declare global {
interface Window {
api: SmartAgriApi
}
}
export {}
+75
View File
@@ -0,0 +1,75 @@
import { contextBridge, ipcRenderer } from 'electron'
import type { IpcRendererEvent } from 'electron'
function on<T>(channel: string, cb: (payload: T) => void): () => void {
const listener = (_e: IpcRendererEvent, payload: T): void => cb(payload)
ipcRenderer.on(channel, listener)
return () => ipcRenderer.removeListener(channel, listener)
}
const api = {
win: {
minimize: (): void => ipcRenderer.send('win:minimize'),
toggleMaximize: (): void => ipcRenderer.send('win:toggle-maximize'),
close: (): void => ipcRenderer.send('win:close'),
onMaximized: (cb: (v: boolean) => void): (() => void) => on('win:maximized', cb)
},
screens: {
list: (): Promise<unknown[]> => ipcRenderer.invoke('screens:list'),
states: (): Promise<unknown[]> => ipcRenderer.invoke('screens:states'),
publish: (payload: { index: number; url: string; title: string }): Promise<unknown[]> =>
ipcRenderer.invoke('screen:publish', payload),
close: (index: number): Promise<unknown[]> => ipcRenderer.invoke('screen:close', index),
closeAll: (): Promise<unknown[]> => ipcRenderer.invoke('screen:closeAll'),
mainIndex: (): Promise<number> => ipcRenderer.invoke('screens:mainIndex'),
publishMain: (payload: { url: string; title: string }): Promise<unknown[]> =>
ipcRenderer.invoke('screen:publishMain', payload),
restoreMain: (): Promise<unknown[]> => ipcRenderer.invoke('screen:restoreMain'),
probe: (): Promise<unknown[]> => ipcRenderer.invoke('screens:probe'),
keyLock: (locked: boolean): Promise<boolean> => ipcRenderer.invoke('screens:keyLock', locked),
keyLockState: (): Promise<boolean> => ipcRenderer.invoke('screens:keyLockState'),
onChanged: (cb: (list: unknown[]) => void): (() => void) => on('screens:changed', cb)
},
carousel: {
start: (payload: { index: number; pages: { url: string; title: string }[]; intervalSec?: number }): Promise<unknown[]> =>
ipcRenderer.invoke('carousel:start', payload),
stop: (index: number): Promise<unknown[]> => ipcRenderer.invoke('carousel:stop', index)
},
project: {
saveAs: (data: string, password?: string): Promise<string | null> =>
ipcRenderer.invoke('project:saveAs', { data, password }),
save: (path: string, data: string, password?: string): Promise<string | null> =>
ipcRenderer.invoke('project:save', { path, data, password }),
open: (password?: string): Promise<{ path: string; json: unknown | null; error?: string } | null> =>
ipcRenderer.invoke('project:open', password),
openAt: (path: string, password?: string): Promise<{ path: string; json: unknown | null; error?: string }> =>
ipcRenderer.invoke('project:openAt', { path, password })
},
settings: {
get: (): Promise<unknown> => ipcRenderer.invoke('settings:get'),
set: (patch: unknown): Promise<unknown> => ipcRenderer.invoke('settings:set', patch)
},
shortcuts: {
create: (kind: unknown): Promise<unknown> => ipcRenderer.invoke('shortcut:create', kind)
},
app: {
info: (): Promise<unknown> => ipcRenderer.invoke('app:info'),
consumeLoad: (): Promise<unknown> => ipcRenderer.invoke('app:consumeLoad'),
onLoadProject: (cb: (payload: unknown) => void): (() => void) => on('app:loadProject', cb)
},
key: {
onScreenKey: (cb: (n: number) => void): (() => void) => on('key:switch', cb)
},
page: {
snapshot: (url: string): Promise<string | null> => ipcRenderer.invoke('page:snapshot', url)
},
syncProject: (pages: unknown[]): void => ipcRenderer.send('project:sync', pages),
setDirty: (d: boolean): void => ipcRenderer.send('window:dirty', d),
beep: (): Promise<void> => ipcRenderer.invoke('beep'),
createShortcut: (): Promise<boolean> => ipcRenderer.invoke('desktop:shortcut'),
confirm: (message: string): Promise<boolean> => ipcRenderer.invoke('dialog:confirm', message)
}
contextBridge.exposeInMainWorld('api', api)
export type Api = typeof api