Commit Graph
402 Commits
Author SHA1 Message Date
amitwh 5ef1610873 fix(ipc): wire preload bridge so menu Open file actually loads in editor + preview
Two related defects were breaking the 'open file' flow:

1. The BrowserWindow webPreferences had no preload path AND
   contextIsolation: false, so contextBridge.exposeInMainWorld
   threw on load and window.electronAPI was never set. Result:
   every renderer-side ipc.* call returned CHANNEL_MISSING and
   silently no-op'd. Toolbar Open/Save, the file menu, every
   dialog — all broken.

2. The 'file.opened' IPC bridge in register-menu-commands.ts
   dispatched to a 'file.opened' command that was never registered
   as a handler. Even if the preload had worked, the main menu's
   File -> Open would have been a no-op once it reached the
   command store.

Fixes:
- window/index.js: add preload path; set contextIsolation: true
  (required for contextBridge) and nodeIntegration: false
  (renderer no longer needs direct Node access; everything goes
  through the whitelisted preload bridge).
- file-store.ts: add openFileFromMain(path, content) that opens
  a tab + editor buffer from content the main process already read
  (skips the renderer's ipc.file.read since main has the bytes).
  Preserves existing buffer content if the file is already open.
- register-menu-commands.ts: register 'file.opened' to call
  openFileFromMain. Drop the dead 'command.palette' bridge
  (no consumer, no UI, no handler).
- tests: two new cases for openFileFromMain — verifies it does
  not call ipc.file.read and does not clobber user edits on
  re-open.

Verified end-to-end via the run-desktop driver: triggering
file-opened from the main process with a test .md now shows the
content in BOTH the CodeMirror editor and the rendered preview
pane (plus the Outline panel picks up the H1). 497 tests pass.
2026-06-07 22:35:30 +05:30
amitwh 69afd5fc54 chore(release): bump version to 5.0.1 v5.0.1 2026-06-06 23:08:20 +05:30
amitwh 9e315af1b9 fix(release): walk-and-find pandoc binary in extracted archive
Pandoc 3.9 macOS zip uses an arch-suffixed inner directory
(pandoc-3.9.0.2-x86_64/) where Linux/Windows use the bare
pandoc-3.9.0.2/ dir. Hard-coding the intermediate path broke
the macOS build in v5.0.1-rc1.

Replace the hard-coded path.join(tmpDir, PANDOC_VERSION, ...)
with a recursive walk that locates the binary by name. This
is robust to future pandoc layout shifts (e.g., universal
binaries renaming the inner dir).

Verified:
  - Linux: download + extract produces a working pandoc 3.9.0.2
  - macOS: walker finds pandoc in pandoc-3.9.0.2-x86_64/bin/
  - Windows: walker finds pandoc.exe in pandoc-3.9.0.2/
  - 306/306 tests pass
2026-06-06 23:07:30 +05:30
amitwh 78914a4d65 chore(renderer): default to light theme
User prefers light mode. next-themes previously defaulted to dark
which made first-load users (no localStorage) see a dark UI. With
'enableSystem' on, 'system' is still available as a choice — only
the initial value changed.

Existing users with 'dark' in localStorage keep dark until they toggle.
New users get light.
2026-06-06 22:43:16 +05:30
amitwh 3c4cc985fb feat(packaging): add macOS icon.icns + build:icon-icns script
electron-builder's mac block declared 'icon: assets/icon.icns' but the
file was never generated, so macOS builds shipped with the default
Electron icon. The build would succeed but the .app looked generic.

Add scripts/build-icon-icns.js — a 50-line node script that packs the
existing PNGs from assets/icons/ (16/32/64/128/256/512/1024 + 48 small
+ 16/32/128/256 @2x retina) into a valid ICNS file. No native deps;
works on Linux, mac, and Windows runners alike (avoids the mac-only
'sips' that png2icns requires).

Wired into npm via 'npm run build:icon-icns'. Local build:linux now
parses the ICNS correctly and produces AppImage/deb/snap.
2026-06-06 22:43:06 +05:30
amitwh 09d9f6bdd1 fix(window): renderer path off-by-one; use 'close' for state save
Two related bugs in src/main/window/index.js:

1. Renderer path was ../../dist/renderer/index.html which resolves to
   src/dist/renderer/index.html (one level too deep). Production builds
   load from <project>/dist/renderer/index.html. Fix: ../../..

   This was never caught because dev mode uses VITE_DEV_SERVER_URL and
   never touches the file path; only 'electron .' (no Vite) hits it.

2. win.on('closed', state.save) calls win.getBounds() on a destroyed
   BrowserWindow. The 'closed' event fires AFTER destruction, so
   getBounds() throws 'Object has been destroyed'. Use 'close' (fires
   before destruction) and guard with isDestroyed() as a belt-and-suspenders.

Both bugs surfaced when the user clicked File → Open. The renderer
loaded a missing HTML (blank body); the openFile handler then triggered
the closed-event state save which threw.
2026-06-06 22:42:54 +05:30
amitwh f36d918871 fix(menu): require('../main') → require('../index') after main.js→index.js rename
The menu items file was using require('../main') which resolved to a
non-existent src/main/main.js. After the 054ce23 refactor that renamed
main.js → index.js, every menu click tried to require a missing module.

All 65 occurrences fixed; fileItems() now loads cleanly at app startup
and the menu doesn't throw on construction.

Caught by user clicking File → Open: showOpenDialogSync emitted through
to the lazy require at items.js:52, which failed with module-not-found
before main process handlers ran.
2026-06-06 22:42:42 +05:30
amitwh ab16998922 feat(packaging): all-platform builds with GitHub Releases
Closes the gap from the previous handoff: `npm run dist:all` now
produces Linux .deb/AppImage/snap, Windows NSIS/portable/zip, and
macOS dmg/zip artifacts. `.github/workflows/release.yml` builds all
three on tag push (v*) and attaches the union to a GitHub Release.

Bug fix in the same commit (would have broken any packaged build):

* `build.files` was `["src/**/*", ...]` — shipped `src/renderer/`
  .tsx dev source into the asar. Replaced with a whitelist
  (`src/main/**/*`, `src/preload.js`, `src/plugins/**/*`,
  `package.json`) and moved the built renderer to
  `build.extraResources` (`dist/renderer` -> `renderer/`).

* `src/main/window/index.js` now uses `process.resourcesPath` when
  `app.isPackaged` is true, plus a friendly error if the renderer is
  missing. Verified by extracting the .AppImage and launching the
  binary — main process logs
  `Production mode — loading .../resources/renderer/index.html`.

New in `package.json` build config:

* `mac.target`: dmg + zip for x64 and arm64; `darkModeSupport: true`,
  `hardenedRuntime: false`, `gatekeeperAssess: false`,
  `entitlements: null`. `mac.identity: null` retained (unsigned).
* `publish`: GitHub Releases provider. CI overrides with
  `--publish=never`.

Known limitations (intentional follow-ups, not blockers):

* No `assets/icon.icns` — macOS app uses the default Electron icon.
  This box lacks `iconutil`/`png2icns`; generating a proper .icns is
  a one-line follow-up.
* No code signing — Windows SmartScreen will warn, macOS Gatekeeper
  will quarantine. `CSC_LINK_BASE64`/`CSC_KEY_PASSWORD` secrets are
  already wired in the Windows job for when the cert is added.
* No auto-update — user decision. The `publish` block writes the
  metadata that a future `electron-updater` integration will consume.

Verified on this Linux box:

* `npm run build:renderer` then `npm run build:linux` — produced
  AppImage (291M), deb (220M), snap (245M).
* `resources/app.asar` has 0 `src/renderer/*` entries (was the
  pre-fix bug). 44 .tsx files remain in asar — all from
  `node_modules/@hookform/resolvers` and `node_modules/immer` test
  fixtures, not our code.
* Extracted `squashfs-root/resources/renderer/index.html` exists and
  is what `window/index.js` loads.
* Binary runs for >12s without crashing — `loadFile` resolves
  `process.resourcesPath/renderer/index.html` correctly.

Tests: 306/306 pass. No regressions.

Plan doc: docs/plans/2026-06-06-packaging-all-platforms.md (the
task-by-task execution guide I followed; included for traceability).
2026-06-06 21:06:20 +05:30
amitwh 58ca3014d8 fix(modals): drop shadcn-style aria-describedby to silence Radix warning
Every DialogContent had aria-describedby="X-desc" and every
DialogDescription had a matching id="X-desc". That pattern is the
default shadcn/ui recipe, but it breaks Radix 1.1.x.

Root cause: Radix auto-generates a descriptionId via useId() (e.g. :r0:)
and stores it in the Dialog context. The DescriptionWarning effect does
`document.getElementById(descriptionId)` and warns if the element is
missing. When the description element's id is overridden to
"welcome-desc", the DOM has id="welcome-desc" but the context still has
:r0:, so the lookup misses and the warning fires on every dialog open.

Fix: drop both overrides. Let Radix manage the id and aria-describedby
itself. Screen-reader semantics are unchanged (Radix still wires
Content -> Description via the auto-generated id).

Touched: 12 modal files (11 DialogContent/DialogDescription pairs +
SettingsSheet which uses Sheet). +1 regression test in
WelcomeDialog.test.tsx that spies on console.warn and asserts the
"Missing Description" message is never emitted.

Tests: 306/306 pass (was 305).

Long-term memory: radix-aria-describedby-anti-pattern.md captures the
do-not-reintroduce rule for future agents.
2026-06-06 20:45:13 +05:30
amitwh 7c1a79c724 fix(app): add Vite dev workflow, fix onLayout prop, fix icon path, allow Google Fonts CSP
The v5.0.0 React UI redesign shipped without a working dev workflow —
`npm start` only ran `electron .` which loaded the source index.html
that references raw .tsx files. The browser couldn't execute them, so
the app rendered an empty #root and the user saw 'lot of errors'.

This commit restores a real dev workflow:

1. **Vite dev server wired into npm scripts** — added `concurrently`
   + `wait-on` as devDeps. `npm run dev` now runs Vite (port 5173)
   and Electron together with `wait-on tcp:5173` so Electron only
   starts after the dev server is ready.

2. **Main process loads Vite URL in dev, dist/ in prod** — window
   module now checks `process.env.VITE_DEV_SERVER_URL` and
   `app.isPackaged` to decide what to load. Production still
   loads the built `dist/renderer/index.html`.

3. **Fixed ReferenceError: app is not defined** — window module
   referenced `app.isPackaged` but only imported `BrowserWindow`.
   Added `app` to the destructured import.

4. **Fixed wrong icon path** — window creation used
   `../../assets/icon.png` from src/main/window/ which resolved
   to `src/assets/icon.png` (doesn't exist). Icon lives at
   project root, so path is now `../../../assets/icon.png`.

5. **Allowed Google Fonts in CSP** — the strict CSP
   (`font-src 'self' data:`, `connect-src 'self'`) blocked
   the preconnect/css link to fonts.googleapis.com / .gstatic.com.
   Added both domains so Plus Jakarta Sans loads.

6. **Renamed onLayout → onLayoutChange** in AppShell — react-resizable-panels
   v4 renamed the prop. The v1 name was being spread to a DOM
   div and React logged 'Unknown event handler property' warnings.
   Test mock updated for parity.

7. **show: false → show: true** — ready-to-show was hanging on
   this Wayland/container combo. Now the window is shown
   immediately on create.

Verified end-to-end:
- 305/305 vitest tests pass
- Vite dev server starts on :5173
- Electron loads localhost:5173 and React mounts
- Header 'MarkdownConverter' + sidebar with 'No files open' visible
- No onLayout warnings after HMR
- All blocked errors resolved
2026-06-06 16:17:16 +05:30
amitwh 8027d0b9b5 chore(release): bump version to 5.0.0 v5.0.0 2026-06-06 14:13:52 +05:30
amitwh c0988a0108 docs: add CHANGELOG.md (v5.0.0 entry, Keep a Changelog 1.1.0 format) 2026-06-06 14:12:27 +05:30
amitwh 18fc2317ae refactor: delete 17 legacy files (renderer.js, 8 stylesheets, 5 scripts, 2 htmls, src/index.html orphan, deprecated src/main.js) 2026-06-06 14:11:56 +05:30
amitwh 94dd62946a refactor(preload): remove 9 dead IPC channels (print-preview*, command-palette, open-*/show-* ascii/table) 2026-06-06 14:09:25 +05:30
amitwh 054ce2351a refactor(main): create src/main/{store,index}.js glue layer; new entrypoint at src/main/index.js 2026-06-06 14:08:13 +05:30
amitwh 6651dccfdf refactor(main): move WordTemplateExporter from src/wordTemplateExporter.js to src/main/word-template/ (preserved as single module) 2026-06-06 14:04:16 +05:30
amitwh 848102905e refactor(main): extract window creation to src/main/window/ (main only, ascii/table windows removed) 2026-06-06 14:03:12 +05:30
amitwh 6c7a86c99b refactor(main): extract menu to src/main/menu/ (remove dead print-preview/command-palette/ascii/table sends) 2026-06-06 14:00:29 +05:30
amitwh b46fa9abaa fix(main): remove duplicate list-directory handler from src/main/files/ (kept original in main.js) 2026-06-06 09:52:00 +05:30
amitwh 2a32e702f3 refactor(main): extract file ops to src/main/files/ (search, git, binary) 2026-06-06 09:43:31 +05:30
amitwh 356cfc5cc8 refactor(main): extract path helpers to src/main/utils/paths.js 2026-06-06 09:25:22 +05:30
amitwh a03144df5b docs(plan): Phase 10 polish + delete legacy — 10 tasks, main.js decomposition + 17 legacy file deletions + v5.0.0 2026-06-06 09:22:15 +05:30
amitwh 7fc11aed3b docs(spec): Phase 10 — fix index.html ambiguity (DELETE legacy root src/index.html, not rewrite; src/renderer/index.html is already live) 2026-06-06 09:16:02 +05:30
amitwh 095f77f5f1 docs(spec): Phase 10 polish + delete legacy design — main.js decomposition, file map, v5.0.0 2026-06-06 09:14:39 +05:30
amitwh 80afbb136b test(renderer): Phase 9 tools integration smoke test (6 commands) 2026-06-06 08:38:01 +05:30
amitwh 95bbe171b5 feat(renderer): GitStatusPanel sidebar tab + git.refresh event phase-9-advanced-tools 2026-06-06 08:28:37 +05:30
amitwh 2d77fa5456 feat(renderer): Breadcrumb shows heading symbols (settings toggleable) 2026-06-06 08:28:24 +05:30
amitwh fbaff37505 feat(renderer): Minimap (custom overview, wired to settings.minimap) 2026-06-06 08:20:48 +05:30
amitwh 1ccde4baa3 feat(renderer): Zen mode (Esc exits, hides chrome) 2026-06-06 08:13:38 +05:30
amitwh 8822c9a2f9 feat(renderer): PrintPreview overlay + App.tsx mount 2026-06-06 08:11:15 +05:30
amitwh 324e5676b8 feat(renderer): ReplPanel (markdown snippet preview) 2026-06-06 08:11:10 +05:30
amitwh a62511e7ed feat(renderer): FindInFilesDialog (cross-file search with regex) 2026-06-06 08:04:49 +05:30
amitwh a6c6e3696e feat(renderer): WordExportDialog (docx export with template picker) 2026-06-06 08:02:19 +05:30
amitwh 1b2ab437d1 feat(renderer): TableGeneratorDialog (markdown table generator) 2026-06-06 07:45:18 +05:30
amitwh efc7709f66 feat(renderer): AsciiGeneratorDialog (figlet text-to-ASCII-art) 2026-06-06 07:40:20 +05:30
amitwh a8f7547c7e feat(renderer): Phase 9 foundation — settings, modal union, ipc, commands, docx-export 2026-06-06 07:33:00 +05:30
amitwh 094b52a2b3 docs(spec): Phase 9 advanced tools design — 10 features across 3 mount strategies 2026-06-06 07:20:17 +05:30
amitwh 24c6675c82 fix(test): use mockRejectedValue for IPC error in ExportPdfDialog test phase-8-toasts 2026-06-05 23:17:57 +05:30
amitwh 929d72cf1d test(renderer): Phase 8 toasts integration smoke test 2026-06-05 23:12:26 +05:30
amitwh b40bff641c feat(renderer): export dialogs toast on success/failure 2026-06-05 23:05:32 +05:30
amitwh 338669f2db feat(renderer): file-store toasts on save/open/folder 2026-06-05 22:59:54 +05:30
amitwh 54615649f4 feat(renderer): mount Toaster in App.tsx 2026-06-05 22:52:04 +05:30
amitwh 3ff76c8c33 feat(renderer): add shadcn Toaster primitive 2026-06-05 22:50:07 +05:30
amitwh 8160ac05e0 feat(renderer): lib/toast.ts typed wrappers over sonner 2026-06-05 22:46:45 +05:30
amitwh 6762b5f9af docs(spec): Phase 8 toasts design — typed helpers, Toaster mount, 4 wire points 2026-06-05 22:41:28 +05:30
amitwh 00485d1bef feat(renderer): implement numeric right-alignment in ascii-table
Adds the actual right-alignment heuristic to toAsciiTable (was missing
from initial implementation). When any column header starts with a digit,
all columns are right-padded instead of left-padded. Matches the test
added in 53870d5 which exercises the right-alignment path with header
['Item', '7'].

All 6 ascii-table tests pass.
2026-06-05 22:18:15 +05:30
amitwh 24bb9aa9f5 test(renderer): Phase 7 modals integration smoke test phase-7-modals 2026-06-05 19:54:35 +05:30
amitwh b39bdcf475 feat(renderer): mount ModalLayer + first-launch welcome trigger 2026-06-05 19:48:37 +05:30
amitwh 9d154a8521 feat(renderer): AppHeader gets Settings + About icon buttons 2026-06-05 19:46:56 +05:30
amitwh fe62c8814c feat(renderer): register 9 modal-opening commands in command store 2026-06-05 19:44:13 +05:30