import { invoke } from '@tauri-apps/api/core' import { getCurrentWindow } from '@tauri-apps/api/window' import type { AppCommandId } from '../hooks/appCommandCatalog' import { APP_COMMAND_DEFINITIONS, APP_COMMAND_IDS } from '../hooks/appCommandCatalog' import { Button } from './ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from './ui/dropdown-menu' type MenuItem = | { kind: 'separator' } | { kind: 'command'; commandId: MenuCommandId; label: string } | { kind: 'action'; action: () => void; label: string; shortcut?: string } type MenuSection = { items: ReadonlyArray label: string } type MenuCommandId = AppCommandId | 'edit-toggle-note-list-search' const MENU_SECTIONS: ReadonlyArray = [ { label: 'File', items: [ { kind: 'command', label: 'New Note', commandId: APP_COMMAND_IDS.fileNewNote }, { kind: 'command', label: 'New Type', commandId: APP_COMMAND_IDS.fileNewType }, { kind: 'command', label: 'Quick Open', commandId: APP_COMMAND_IDS.fileQuickOpen }, { kind: 'separator' }, { kind: 'command', label: 'Save', commandId: APP_COMMAND_IDS.fileSave }, ], }, { label: 'Edit', items: [ { kind: 'command', label: 'Find in Note', commandId: APP_COMMAND_IDS.editFindInNote }, { kind: 'command', label: 'Replace in Note', commandId: APP_COMMAND_IDS.editReplaceInNote }, { kind: 'command', label: 'Find in Vault', commandId: APP_COMMAND_IDS.editFindInVault }, { kind: 'command', label: 'Paste without Formatting', commandId: APP_COMMAND_IDS.editPastePlainText }, { kind: 'command', label: 'Toggle Note List Search', commandId: 'edit-toggle-note-list-search' }, { kind: 'command', label: 'Toggle Diff Mode', commandId: APP_COMMAND_IDS.editToggleDiff }, ], }, { label: 'View', items: [ { kind: 'command', label: 'Editor Only', commandId: APP_COMMAND_IDS.viewEditorOnly }, { kind: 'command', label: 'Editor + Notes', commandId: APP_COMMAND_IDS.viewEditorList }, { kind: 'command', label: 'All Panels', commandId: APP_COMMAND_IDS.viewAll }, { kind: 'separator' }, { kind: 'command', label: 'Toggle Properties Panel', commandId: APP_COMMAND_IDS.viewToggleProperties }, { kind: 'separator' }, { kind: 'command', label: 'Zoom In', commandId: APP_COMMAND_IDS.viewZoomIn }, { kind: 'command', label: 'Zoom Out', commandId: APP_COMMAND_IDS.viewZoomOut }, { kind: 'command', label: 'Actual Size', commandId: APP_COMMAND_IDS.viewZoomReset }, { kind: 'separator' }, { kind: 'command', label: 'Command Palette', commandId: APP_COMMAND_IDS.viewCommandPalette }, ], }, { label: 'Go', items: [ { kind: 'command', label: 'All Notes', commandId: APP_COMMAND_IDS.goAllNotes }, { kind: 'command', label: 'Archived', commandId: APP_COMMAND_IDS.goArchived }, { kind: 'command', label: 'Changes', commandId: APP_COMMAND_IDS.goChanges }, { kind: 'command', label: 'Inbox', commandId: APP_COMMAND_IDS.goInbox }, { kind: 'separator' }, { kind: 'command', label: 'Go Back', commandId: APP_COMMAND_IDS.viewGoBack }, { kind: 'command', label: 'Go Forward', commandId: APP_COMMAND_IDS.viewGoForward }, ], }, { label: 'Note', items: [ { kind: 'command', label: 'Toggle Organized', commandId: APP_COMMAND_IDS.noteToggleOrganized }, { kind: 'command', label: 'Archive Note', commandId: APP_COMMAND_IDS.noteArchive }, { kind: 'command', label: 'Delete Note', commandId: APP_COMMAND_IDS.noteDelete }, { kind: 'command', label: 'Restore Deleted Note', commandId: APP_COMMAND_IDS.noteRestoreDeleted }, { kind: 'separator' }, { kind: 'command', label: 'Open in New Window', commandId: APP_COMMAND_IDS.noteOpenInNewWindow }, { kind: 'separator' }, { kind: 'command', label: 'Toggle Raw Editor', commandId: APP_COMMAND_IDS.editToggleRawEditor }, { kind: 'command', label: 'Toggle AI Panel', commandId: APP_COMMAND_IDS.viewToggleAiChat }, { kind: 'command', label: 'Toggle Backlinks', commandId: APP_COMMAND_IDS.viewToggleBacklinks }, ], }, { label: 'Vault', items: [ { kind: 'command', label: 'Open Vault…', commandId: APP_COMMAND_IDS.vaultOpen }, { kind: 'command', label: 'Remove Vault from List', commandId: APP_COMMAND_IDS.vaultRemove }, { kind: 'command', label: 'Restore Getting Started', commandId: APP_COMMAND_IDS.vaultRestoreGettingStarted }, { kind: 'separator' }, { kind: 'command', label: 'Add Remote…', commandId: APP_COMMAND_IDS.vaultAddRemote }, { kind: 'command', label: 'Commit & Push', commandId: APP_COMMAND_IDS.vaultCommitPush }, { kind: 'command', label: 'Pull from Remote', commandId: APP_COMMAND_IDS.vaultPull }, { kind: 'command', label: 'Resolve Conflicts', commandId: APP_COMMAND_IDS.vaultResolveConflicts }, { kind: 'command', label: 'View Pending Changes', commandId: APP_COMMAND_IDS.vaultViewChanges }, { kind: 'separator' }, { kind: 'command', label: 'Reload Vault', commandId: APP_COMMAND_IDS.vaultReload }, { kind: 'command', label: 'Repair Vault', commandId: APP_COMMAND_IDS.vaultRepair }, { kind: 'command', label: 'Set Up External AI Tools…', commandId: APP_COMMAND_IDS.vaultInstallMcp }, ], }, { label: 'Window', items: [ { kind: 'action', label: 'Minimize', action: () => void getCurrentWindow().minimize().catch(() => {}) }, { kind: 'action', label: 'Maximize', action: () => void getCurrentWindow().toggleMaximize().catch(() => {}) }, { kind: 'separator' }, { kind: 'action', label: 'Close', action: () => void getCurrentWindow().close().catch(() => {}) }, ], }, ] function formatShortcutKey(key: string): string { switch (key) { case 'ArrowLeft': return '←' case 'ArrowRight': return '→' default: return key.length === 1 ? key.toUpperCase() : key } } function getLinuxShortcut(commandId: MenuCommandId): string | null { if (commandId === 'edit-toggle-note-list-search') { return 'Ctrl+F' } const shortcut = APP_COMMAND_DEFINITIONS[commandId].shortcut if (!shortcut) return null const modifier = shortcut.combo === 'command-or-ctrl' ? 'Ctrl' : 'Ctrl+Shift' return `${modifier}+${formatShortcutKey(shortcut.key)}` } function triggerMenuCommand(commandId: MenuCommandId): void { void invoke('trigger_menu_command', { id: commandId }).catch(() => {}) } function HamburgerIcon() { return ( ) } export function LinuxMenuButton() { return ( {MENU_SECTIONS.map((section) => ( {section.label} {section.items.map((item, index) => { if (item.kind === 'separator') { return } if (item.kind === 'command') { const shortcut = getLinuxShortcut(item.commandId) return ( triggerMenuCommand(item.commandId)} > {item.label} {shortcut && ( {shortcut} )} ) } return ( {item.label} {item.shortcut && {item.shortcut}} ) })} ))} ) }