feat: Phase O Windows-native integration + settings search
- Taskbar progress bar: renderer pushes summarizeQueue aggregate over a new taskbar:progress channel (App store subscription); setProgressBar normal/error - System tray (src/main/tray.ts) + minimize-to-tray (Settings.minimizeToTray); close hides to tray, isQuitting guards real exits; icon via getAppIconPath() (build/icon.ico added to extraResources) - Jump list: app.setUserTasks "Open AeroFetch" (thumbnail toolbar deferred) - Settings search: filters the ~11 SettingsView cards live by text match Overlay badge deferred (needs a drawn NativeImage). typecheck + test (192) + build all clean. Roadmap: Phase O COMPLETE. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* System tray + minimize-to-tray (Phase O). When Settings.minimizeToTray is on,
|
||||
* closing the window hides it to the tray instead of quitting; the tray's "Quit"
|
||||
* really exits. The tray gives the app a background presence (a natural home for
|
||||
* the watched-source sync) and a quick show/quit menu.
|
||||
*/
|
||||
import { app, Tray, Menu, nativeImage, type BrowserWindow } from 'electron'
|
||||
import { getAppIconPath } from './binaries'
|
||||
|
||||
let tray: Tray | null = null
|
||||
// True once the user has chosen to really quit (tray menu, or app.quit from the
|
||||
// updater) — lets the window 'close' handler tell "hide to tray" from "exit".
|
||||
let quitting = false
|
||||
|
||||
export function isQuitting(): boolean {
|
||||
return quitting
|
||||
}
|
||||
|
||||
export function markQuitting(): void {
|
||||
quitting = true
|
||||
}
|
||||
|
||||
function show(getWindow: () => BrowserWindow | null): void {
|
||||
const win = getWindow()
|
||||
if (!win) return
|
||||
if (win.isMinimized()) win.restore()
|
||||
win.show()
|
||||
win.focus()
|
||||
}
|
||||
|
||||
/** Create the tray icon + menu once. No-op if it already exists or the icon is missing. */
|
||||
export function createTray(getWindow: () => BrowserWindow | null): void {
|
||||
if (tray) return
|
||||
const icon = nativeImage.createFromPath(getAppIconPath())
|
||||
// Without a usable icon a Windows tray entry is invisible/unclickable, which is
|
||||
// worse than no tray — so skip it rather than ship a dead tray.
|
||||
if (icon.isEmpty()) return
|
||||
|
||||
tray = new Tray(icon)
|
||||
tray.setToolTip('AeroFetch')
|
||||
tray.setContextMenu(
|
||||
Menu.buildFromTemplate([
|
||||
{ label: 'Show AeroFetch', click: () => show(getWindow) },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Quit AeroFetch',
|
||||
click: () => {
|
||||
quitting = true
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
])
|
||||
)
|
||||
tray.on('click', () => show(getWindow))
|
||||
}
|
||||
Reference in New Issue
Block a user