- 5 functional buttons: Open file, Open folder, Save, Toggle sidebar, Toggle preview
- each dispatches via useCommandStore
- aria-pressed on toggle buttons reflects app store state
- formatting buttons (bold, italic, etc.) stay disabled (Phase 9)
- toolbar now has role='toolbar' and aria-label
- TDD: 9 component tests
- useRegisterMenuCommands: registers file.open, file.save, file.closeTab,
tab.next, tab.prev, view.toggleSidebar, view.togglePreview in command store
- useBridgeNativeMenu: useMenuAction for 13 native-menu channels
(file-save, toggle-preview, load-template-menu, etc.)
- AppShell calls both on mount
- integration test mock extended to include ipc.menu.on
- 145/145 tests pass (was 105)
- bridge between main-process menu events and the command dispatcher
- optional transform: convert raw IPC payload to command args
- extends ipc.ts with ipc.menu.on() helper
- TDD: 5 unit tests covering subscription, payload transform, no-op, unmount
- 'mod+s' / 'mod+shift+s' / 'Tab' / etc.
- mod = meta (Mac) or ctrl (others)
- suppresses when <input>/<textarea>/contentEditable focused
- preventDefault on match
- TDD: 15 unit tests covering parser, match, suppression, unmount
- useCommandStore: register/unregister/registerMany/dispatch/get
- keyed by id (e.g. 'file.open', 'view.toggleSidebar')
- TDD: 10 unit tests covering register, dispatch, unregister, batch
- foundation for Phase 6 native menu + toolbar wiring
- Renders tabs from useFileStore.openTabs with title + dirty dot + close button
- Uses Zustand selectors for granular re-renders
- Active tab highlighted with aria-current and accent background
- Close button stops propagation; does not trigger tab activation
- Horizontal scroll on overflow, h-9 fixed bar with border
- Empty state preserved when no tabs open
Tests: 6 cases (empty, render, highlight, click-switch, click-close, dirty indicator)
- 75 total tests (was 70, gained 5 new)
- All pass
- Build: succeeds
Amit Haridas
1. Source editor blank: Added ensureEditor() method with try-catch that
lazily creates the CodeMirror editor when setEditorContent is called
if the initial DOMContentLoaded creation failed or was skipped.
Replaced inline createEditor calls in createTabElements and
DOMContentLoaded with ensureEditor().
2. Export via pandoc failing: runPandocCmd parsed the full path
/bin/linux/pandoc as the command, then prepended it to args because
parsed.command !== 'pandoc'. This caused pandoc to receive its own
binary path as the first input file. Fix: use path.basename() to
check if the command ends with 'pandoc' (or 'pandoc.exe').
Amit Haridas
The main window uses nodeIntegration without preload, so window.electronAPI
was undefined. This caused the DOMContentLoaded handler to crash at:
await window.electronAPI.getAppVersion()
which prevented the CodeMirror editor from being created (blank source
window) and stopped renderer-ready from being sent (broken menu/options).
Fixes:
1. Add window.electronAPI shim at top of renderer.js wrapping ipcRenderer.
2. Update CSP meta tag to allow KaTeX CDN (style-src, script-src, font-src).
Amit Haridas