From 1db163d695e4e7f33d1cd33afc75a3b773ecb5a3 Mon Sep 17 00:00:00 2001 From: Brad Groux <3053586+BradGroux@users.noreply.github.com> Date: Sun, 30 Aug 2026 02:49:21 -0500 Subject: [PATCH] feat: add standard macOS Window and Help menus --- desktop/src/main/__tests__/menu.test.ts | 60 ++++++++++++++++++++++++- desktop/src/main/index.ts | 5 +++ desktop/src/main/menu.ts | 23 ++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/__tests__/menu.test.ts b/desktop/src/main/__tests__/menu.test.ts index ed1f26b6..c138d43c 100644 --- a/desktop/src/main/__tests__/menu.test.ts +++ b/desktop/src/main/__tests__/menu.test.ts @@ -44,7 +44,12 @@ describe('desktop native menu', () => { it('exposes common actions with keyboard shortcuts', () => { const dispatch = vi.fn(); const copyVersionInfo = vi.fn(); - const template = createDesktopMenuTemplate({ status: status(), dispatch, copyVersionInfo }); + const template = createDesktopMenuTemplate({ + status: status(), + dispatch, + copyVersionInfo, + openHelp: vi.fn(), + }); const labels = template.flatMap((item) => Array.isArray(item.submenu) ? item.submenu.map((child) => child.label) : [] ); @@ -80,6 +85,7 @@ describe('desktop native menu', () => { status: status(), dispatch: vi.fn(), copyVersionInfo: vi.fn(), + openHelp: vi.fn(), }); expect(template.some((item) => item.role === 'editMenu')).toBe(true); @@ -90,6 +96,7 @@ describe('desktop native menu', () => { status: status('failed'), dispatch: vi.fn(), copyVersionInfo: vi.fn(), + openHelp: vi.fn(), }).find((item) => item.label === 'Desktop'); const externalTest = Array.isArray(desktopMenu?.submenu) ? desktopMenu.submenu.find((item) => item.label === 'Test External Delivery') @@ -104,6 +111,7 @@ describe('desktop native menu', () => { updateStatus: updateStatus('available'), dispatch: vi.fn(), copyVersionInfo: vi.fn(), + openHelp: vi.fn(), }).find((item) => item.label === 'Veritas Kanban'); const downloadUpdate = Array.isArray(appMenu?.submenu) ? appMenu.submenu.find((item) => item.label === 'Download Update') @@ -115,4 +123,54 @@ describe('desktop native menu', () => { expect(downloadUpdate?.enabled).toBe(true); expect(installUpdate?.enabled).toBe(false); }); + + it.each(['starting', 'ready', 'failed'] as const)( + 'keeps the standard macOS Window and Help menus available while the server is %s', + (serverState) => { + const dispatch = vi.fn(); + const openHelp = vi.fn(); + const template = createDesktopMenuTemplate({ + status: status(serverState), + dispatch, + copyVersionInfo: vi.fn(), + openHelp, + platform: 'darwin', + }); + + expect(template.map((item) => item.role ?? item.label)).toEqual([ + 'Veritas Kanban', + 'File', + 'editMenu', + 'Navigate', + 'Desktop', + 'windowMenu', + 'help', + ]); + + const helpMenu = template.find((item) => item.role === 'help'); + const helpItems = Array.isArray(helpMenu?.submenu) ? helpMenu.submenu : []; + const productHelp = helpItems.find((item) => item.label === 'Veritas Kanban Help'); + const onboarding = helpItems.find((item) => item.label === 'Show Setup & Diagnostics'); + + expect(productHelp?.enabled).not.toBe(false); + expect(onboarding?.enabled).toBe(true); + productHelp?.click?.(undefined as never, undefined as never, undefined as never); + onboarding?.click?.(undefined as never, undefined as never, undefined as never); + expect(openHelp).toHaveBeenCalledOnce(); + expect(dispatch).toHaveBeenCalledWith('open-onboarding'); + } + ); + + it('does not add macOS-only menus on other platforms', () => { + const template = createDesktopMenuTemplate({ + status: status(), + dispatch: vi.fn(), + copyVersionInfo: vi.fn(), + openHelp: vi.fn(), + platform: 'linux', + }); + + expect(template.some((item) => item.role === 'windowMenu')).toBe(false); + expect(template.some((item) => item.role === 'help')).toBe(false); + }); }); diff --git a/desktop/src/main/index.ts b/desktop/src/main/index.ts index efe1271c..46fe35ce 100644 --- a/desktop/src/main/index.ts +++ b/desktop/src/main/index.ts @@ -40,6 +40,8 @@ import { const require = createRequire(import.meta.url); const { autoUpdater } = require('electron-updater') as typeof import('electron-updater'); +const DESKTOP_HELP_URL = + 'https://github.com/BradGroux/veritas-kanban/blob/main/docs/GETTING-STARTED.md'; let mainWindow: BrowserWindow | null = null; let runtime: DesktopRuntime | null = null; @@ -184,6 +186,9 @@ function refreshDesktopMenu(): void { status: runtime.snapshot(), updateStatus: updateService?.snapshot(), copyVersionInfo: () => clipboard.writeText(formatDesktopVersionInfo(appInfo)), + openHelp: () => { + void openValidatedExternalUrl(shell, DESKTOP_HELP_URL); + }, dispatch: (command) => { if (commandDispatcher) { dispatchDesktopMenuCommand(commandDispatcher, command); diff --git a/desktop/src/main/menu.ts b/desktop/src/main/menu.ts index b805d493..b50f67d0 100644 --- a/desktop/src/main/menu.ts +++ b/desktop/src/main/menu.ts @@ -14,8 +14,10 @@ import type { export interface ConfigureDesktopMenuOptions { dispatch(command: DesktopCommandName): void; copyVersionInfo(): void; + openHelp(): void; status: DesktopStatusSnapshot; updateStatus?: DesktopUpdateStatus; + platform?: NodeJS.Platform; } export function configureDesktopMenu(options: ConfigureDesktopMenuOptions): void { @@ -35,6 +37,26 @@ export function createDesktopMenuTemplate( }; }; + const macosMenus: MenuItemConstructorOptions[] = + (options.platform ?? process.platform) === 'darwin' + ? [ + { role: 'windowMenu' }, + { + role: 'help', + submenu: [ + { + label: 'Veritas Kanban Help', + click: () => options.openHelp(), + }, + { + ...command('open-onboarding'), + label: 'Show Setup & Diagnostics', + }, + ], + }, + ] + : []; + return [ { label: 'Veritas Kanban', @@ -90,6 +112,7 @@ export function createDesktopMenuTemplate( command('copy-redacted-diagnostics'), ], }, + ...macosMenus, ]; }