Replace the manual "jump to GitHub Release" update flow with native in-app auto-update powered by electron-updater. The app now silently downloads updates in the background and prompts the user to restart when ready. Browser mode (`npm run dev`) retains the existing polling fallback via `/api/app/updates`. Main process (electron/updater.ts — new): - Wrap electron-updater's autoUpdater, forward lifecycle events (checking / available / downloading / downloaded / error) to the renderer via IPC channel `updater:status` - Register `updater:check` and `updater:quit-and-install` IPC handlers - Auto-check 10s after launch, then every 4 hours - Only initialized in packaged mode (!isDev) Preload bridge (electron/preload.ts): - Expose `updater` namespace with checkForUpdates, quitAndInstall, and onStatus callback (same pattern as install.onProgress) Type declarations (src/types/electron.d.ts): - Add UpdateStatusEvent, ElectronUpdaterAPI interfaces - Add optional `updater` field to ElectronAPI Update hook (src/hooks/useUpdate.ts): - Extend UpdateInfo with downloadProgress, readyToInstall, isNativeUpdate - Add quitAndInstall to UpdateContextValue Renderer — dual-mode logic (src/components/layout/AppShell.tsx): - Detect `window.electronAPI?.updater` to choose between native IPC subscription (Electron) and HTTP polling (browser) - Map IPC events to UpdateInfo state, including download progress UI components: - UpdateDialog: three footer states — View Release (browser), progress bar + disabled button (downloading), Restart to Update (ready) - UpdateBanner (new): persistent top banner when update is downloaded, shown even after dialog is dismissed - NavRail: settings dot turns green + pulse when readyToInstall - GeneralSection: UpdateCard shows download %, restart button, or View Release depending on mode Build & CI: - electron-builder.yml: add `publish` config (github provider) to generate latest-mac.yml / latest.yml metadata + blockmap files - build.yml: change --publish never to --publish always with GH_TOKEN; release job now uses `gh release edit` instead of create i18n: - Add 6 translation keys (en + zh) for update UI strings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const { contextBridge, ipcRenderer } = require('electron');
|
|
|
|
contextBridge.exposeInMainWorld('electronAPI', {
|
|
versions: {
|
|
electron: process.versions.electron,
|
|
node: process.versions.node,
|
|
chrome: process.versions.chrome,
|
|
},
|
|
shell: {
|
|
openPath: (folderPath: string) => ipcRenderer.invoke('shell:open-path', folderPath),
|
|
},
|
|
dialog: {
|
|
openFolder: (options?: { defaultPath?: string; title?: string }) =>
|
|
ipcRenderer.invoke('dialog:open-folder', options),
|
|
},
|
|
install: {
|
|
checkPrerequisites: () => ipcRenderer.invoke('install:check-prerequisites'),
|
|
start: (options?: { includeNode?: boolean }) => ipcRenderer.invoke('install:start', options),
|
|
cancel: () => ipcRenderer.invoke('install:cancel'),
|
|
getLogs: () => ipcRenderer.invoke('install:get-logs'),
|
|
onProgress: (callback: (data: unknown) => void) => {
|
|
const listener = (_event: unknown, data: unknown) => callback(data);
|
|
ipcRenderer.on('install:progress', listener);
|
|
return () => { ipcRenderer.removeListener('install:progress', listener); };
|
|
},
|
|
},
|
|
updater: {
|
|
checkForUpdates: () => ipcRenderer.invoke('updater:check'),
|
|
quitAndInstall: () => ipcRenderer.invoke('updater:quit-and-install'),
|
|
onStatus: (callback: (data: unknown) => void) => {
|
|
const listener = (_event: unknown, data: unknown) => callback(data);
|
|
ipcRenderer.on('updater:status', listener);
|
|
return () => { ipcRenderer.removeListener('updater:status', listener); };
|
|
},
|
|
},
|
|
});
|