Commit Graph
375 Commits
Author SHA1 Message Date
amitwh e967af77a7 feat(flowchart): export canvas as SVG / PNG / JPG (C13)
Three new toolbar buttons next to Open from File let the user save
the current canvas to disk in the format their tooling expects:

  - Export SVG — straight clone + serialise of the canvas <svg>,
    with a minimal stylesheet inlined so the file renders without
    the editor's stylesheet. Goes through the existing 'save-text-file'
    IPC channel as a UTF-8 string.
  - Export PNG / JPG — rasterise the SVG through Image + Canvas at
    1000x700 + 24px padding, then send the data URL to the new
    'save-binary-file' IPC channel which strips the 'data:...;base64,'
    prefix and writes the raw bytes. JPEG gets a white background fill
    so transparent areas don't come out black.

New IPC + preload bridge:

  - src/main.js: 'save-binary-file' handler mirrors 'save-text-file'
    shape but writes a Buffer from base64. Returns { canceled, path } |
    { canceled: false, error } so the UI can surface write failures.
  - src/preload.js: flowchart.saveBinary(data, defaultName, filters)
    so the standalone window can call it.

Tests: 2 new (1355 total, was 1353) covering SVG export happy-path
and cancel. Raster PNG/JPG paths not jsdom-tested because
Image+Canvas in jsdom doesn't reliably fire onload for Blob URLs;
the SVG path exercises the same handler plumbing so coverage is
adequate.

Amit Haridas
2026-09-30 22:13:54 +05:30
amitwh 41ddaa17b3 build: desktopName + Linux snap channel config
electron-builder emits a pack-time warning on every Linux build that
desktopName is unset, which means the installed .desktop entry can't
be matched to a running window by most desktop environments (no
WM_CLASS / app_id link, so clicking the launcher icon while the app
is open does not raise / focus the window).

  - package.json: add top-level 'desktopName': 'MarkdownConverter'
    so Electron exposes the same value as its X11/Wayland window
    class and the .desktop file matches it.
  - electron-builder.config.js: mirror it under linux.* with
    syncDesktopName: true so the generated .desktop / .deb / snap
    entries all use the same identifier.
  - Add snap channel 'latest/edge' so the auto-updater feed from
    GitHub releases maps to a track snapd recognises. 'stable' would
    publish to a manually-managed channel and break the updater.

No source code changes; build only. Verified electron-builder accepts
the new keys without warnings on next pack.

Amit Haridas
2026-09-30 22:09:19 +05:30
amitwh 48c7420c7a fix(flowchart): UMD browser fallback for viewport + add to controller test mount
The 6 pre-existing flowchart-controller test failures
('ReferenceError: require is not defined') were caused by
flowchart-canvas.js falling through to require('./flowchart-viewport')
when window.FlowchartViewport was undefined — because flowchart-viewport
was added in C4a but the controller test's script loader was never
updated to include it, and viewport itself had no browser-global
fallback.

Two coordinated fixes:

  - Add the same UMD wrapper to flowchart-viewport.js that the other
    pure modules already use (CommonJS module.exports + window
    global fallback for the standalone window and jsdom). Now the
    canvas's 'window.FlowchartViewport || require(...)' short-circuits
    in the browser case, matching what shapes / store / mermaid do.

  - Load flowchart-viewport.js in the controller test mount helper,
    in dependency order between mermaid and store. The canvas now
    sees window.FlowchartViewport set and never falls through to
    require() under jsdom.

Test suite: 104 suites / 1353 tests passing (was 1347 with 6 failing).
The 6 'require is not defined' failures are gone.

Amit Haridas
2026-09-30 22:08:10 +05:30
amitwh dadfe52870 fix(preload): saveFile IPC payload was being shadowed by stray channel arg
C9 (22bc4dc) introduced a malformed edit when wiring openFile. The
saveFile arrow got a stray 'open-text-file-dialog' string between the
channel and payload, so the ipcMain handler destructured the string
(both fields undefined) instead of { content, defaultName }. saveFile
silently saved with no content and no default name — the Save to File
button worked through the dialog fallback but wrote whatever was in
the buffer.

Fix: drop the stray arg, restore the single-line invoke with the
payload object. openFile stays on its own line below. format:check
is now clean.

Discovered during the v4.13.0 release rebuild when prettier wanted
to collapse the malformed multi-line into one — the malformed shape
itself was the bug, not the formatting.

Amit Haridas
2026-09-30 21:51:13 +05:30
Amit Haridas 8fb61130f3 feat(flowchart): bundle-side clipboard wire-up (C11)
Cmd+C / Cmd+V / Cmd+D keyboard shortcuts now work in the standalone
window. The clipboard pure module (committed in C6a) is inlined
into the bundle alongside the mermaid parser (C9 inlining) so the
standalone window does not depend on a separate bundle rebuild.

  - copySelection / pasteSelection definitions added inside the
    bundle IIFE, exposed as window.FlowchartClipboard for jsdom
    tests.
  - Module-level _clipboard variable scoped to the keydown handler
    block so copy survives across Cmd+V and Cmd+D presses.
  - Cmd+C: serialises the canvas-owned selection (now read via
    getSelection(), not the old DOM .selected query) and stashes it.
  - Cmd+V: applies the stashed payload to the store via addNode +
    connect; nothing to do for the edge-only payload.
  - Cmd+D: copy + paste in one keystroke, with a 24px offset.
  - Delete/Backspace: reuses getSelection() to delete the selected
    node or edge; replaces the fragile DOM-querying path that the
    test for the bundle was no longer catching.

The pure module in src/flowchart/flowchart-clipboard.js stays the
source of truth for the algorithm (9 tests in C6a). Bundle inline
keeps the standalone window functional without a rebuild step;
a future build-time inclusion would collapse the duplication.

Amit Haridas
2026-09-30 21:43:49 +05:30
Amit Haridas ded17d0b6b feat(flowchart): node resizing (C10)
v4.13.0 — nodes can be resized by dragging the bottom-right handle.

  - New setNodeWidth(id, width) on the store. Clamps to [60, 600] so
    shapes stay readable. Throws on unknown id; pushes an undo
    snapshot like every other mutator.
  - New nodeWidth(node) helper on the canvas uses node.width with a
    fallback to DEFAULT_WIDTH for graphs loaded from older JSON
    that didn't have the field.
  - Canvas render uses the per-node width for shapeSvg + label
    positioning, and edge boundaryPoint() uses it too so connection
    geometry adapts to the new shape size.
  - Selected node renders a bottom-right resize handle (10px square).
    Clicking the handle starts a 'resize' dragState that calls
    setNodeWidth on every pointermove with the screen-to-SVG delta
    applied to the start width.
  - The handle is detected before the generic node click handler so
    it doesn't accidentally start a move drag.

3 new tests cover setNodeWidth update, clamp to [60, 600], and
unknown-id throw. The canvas change reuses the existing 14 tests
(which check rendering and pointer events) — they still pass.

Amit Haridas
2026-09-30 21:42:35 +05:30
Amit Haridas 22bc4dcf56 feat(flowchart): Open from file button + parse on load (C9)
v4.13.0 — round-trips Mermaid source through the editor:

  - new open-text-file-dialog invoke IPC in main.js opens a system
    Open dialog filtered for .mmd / .mermaid / .md / .markdown / .txt,
    reads the chosen file, returns { path, content } or null on cancel.
  - new openFile() on the flowchart preload bridge.
  - new Open from File button in the standalone window toolbar
    alongside Insert / Save / Reset.
  - On click: confirms overwrite when the current graph is non-empty,
    strips any mermaid fence, parses via the inlined fromMermaid()
    (the inverse of toMermaid), and calls _store.deserialize so the
    canvas redraws. Status bar reports Loaded path.

The fromMermaid() in the bundle is duplicated from
src/flowchart/flowchart-mermaid-parse.js (23 tests in C8 land the
same logic) so the standalone window does not depend on the bundle
rebuild step. Pure module + bundle inline stay in sync logically;
future work could collapse them via a build-time inclusion.

Amit Haridas
2026-09-30 21:40:59 +05:30
Amit Haridas 4123455863 feat(flowchart): Mermaid source → graph parser (C8)
Inverse of flowchart-mermaid.js's toMermaid(). Pure module — no DOM,
no globals — so the parsing logic is unit-tested in isolation.

Recognises the 5 node shapes:
  - process     [label]
  - decision    {label}
  - terminator  ([label])
  - subroutine  [[label]]
  - document    [/label/]

Recognises the 3 edge arrows:
  - -->  solid
  - -.-> dotted
  - ==>  thick

Edge labels may appear before OR after the arrow (Mermaid accepts
both:  and ).

Behaviour:
  - Auto-creates nodes referenced in edges but not declared explicitly.
  - Idempotent: declaring  then connecting  does not
    duplicate the A node.
  - Defensive: malformed lines are skipped silently so partial /
    hand-edited source still loads whatever it can.
  - Unescapes the standard Mermaid escapes (#quot; → ", \n → newline).

Wire-up into the renderer (Open from .mmd file → parse →
store.deserialize) is the next commit; this lands the testable math.

23 new tests cover shape parsing for all 5 kinds, edge parsing for
all 3 arrow types + label placement, unescaping, header skipping,
comment skipping, the subroutine-vs-process regression, auto-node
creation, and the null/non-string inputs.

Amit Haridas
2026-09-30 21:34:53 +05:30
Amit Haridas 7beb3effcf feat(flowchart): clipboard copy/paste/duplicate pure module (C6a)
New clipboard serialiser for the flowchart editor. Pure module —
no DOM, no globals — so it's unit-testable in isolation.

  - Captures the selected node + its connected edges to a JSON envelope
    that survives a paste-into-new-graph round trip. Edge-only selection
    is also supported.
  - Paste creates a new node offset from the original (default 24px)
    and re-attaches every connected edge to the new id. Only edges
    whose other endpoint still exists in the graph are recreated, so
    deleting a neighbour before pasting doesn't try to reconnect to a
    ghost.
  - Preserves node color on paste.

Wire-up into the renderer controller (Cmd+C / Cmd+V / Cmd+D handlers
+ canvas-owned selection reading) is the next commit; this one just
lands the testable math.

9 new tests cover selection capture, edge-only selection, paste with
offset, edge re-attachment, dangling-edge handling, color preservation,
and the null-payload no-op.

Amit Haridas
2026-09-30 21:32:03 +05:30
Amit Haridas 9412049f56 feat(flowchart): wire zoom/pan/snap into canvas (C5)
- All canvas content (nodes, edges, preview line) now lives inside a
    single <g class='flowchart-content' transform='...'> so a single
    attribute change moves/zooms everything together. The fixed viewBox
    (1000x700) stays — the transform handles everything.
  - Ctrl/Cmd+wheel zooms around the cursor (calls vpZoomAt, the pure
    helper committed in C4a).
  - Shift-drag (or middle-click) on empty canvas pans the view.
  - Snap-to-grid (10-unit) is opt-in via api.setSnapEnabled(true).
    When on, moveNode and addNode clamp coords to the grid.
  - Plain click on empty canvas still hands off to opts.onEmptyClick
    so the controller can prompt for shape kind + label.

destroy() now also removes the wheel listener.

Amit Haridas
2026-09-30 21:29:17 +05:30
Amit Haridas 98979a2cd2 feat(flowchart): pure viewport math for zoom/pan/snap (C4a)
New pure functions for the canvas viewport (v4.13.0):
  - zoomAt(view, screenX, screenY, factor) — zoom centred on a screen
    point, keeping the world point under cursor fixed. Clamped to
    [0.25, 4] scale range.
  - panBy(view, dx, dy) — additive pan
  - reset() — identity transform
  - wheelFactor(deltaY) — multiplicative factor per Ctrl+wheel notch
  - snap(value, gridSize) — round to nearest grid (0 disables)

The actual SVG transform wrapper + Ctrl+wheel handler + drag-to-pan
+ snap-on-move is the next canvas-layer commit; this commit lands
the testable math.

14 new tests cover: identity reset, cursor-stable zoom, MIN/MAX
clamping, additive pan, wheel factor reciprocity, snap-to-grid
(positive/negative/zero grid sizes).

Amit Haridas
2026-09-30 21:28:36 +05:30
Amit Haridas 4228ab78d8 feat(flowchart): undo/redo toolbar buttons + history counter (C3)
The flowchart editor had Ctrl+Z / Ctrl+Shift+Z keyboard shortcuts
but no discoverable buttons. New History section in the side panel:
  - Undo / Redo buttons wired to _store.undo() / _store.redo()
  - Counter chip showing ↶/↷ availability
  - Buttons auto-disable when stacks are empty (via _store.canUndo()
    + canRedo() evaluated on every store subscription)

The store already exposed undo/redo/canUndo/canRedo — this commit
only adds the UI surface.

Amit Haridas
2026-09-30 21:27:11 +05:30
Amit Haridas 0bb1091c47 feat(flowchart): edge geometry at shape boundary + connect preview (C2+C3)
Three UX/DI wins in one canvas pass:

1. Edge endpoints now land on the source/target shape's BOUNDARY
   rather than the centres. Lines no longer cut through nodes. New
   pure helpers boundaryPoint() + edgeEndpoints() do the geometry;
   nodeCenter() is still used internally for direction vectors.

2. Connect-mode preview line. Alt+drag now draws a dashed line from
   the source node's boundary to the live pointer position so the
   user can see where the edge will land before releasing. The original
   code marked this 'visual feedback deferred to v2'.

3. Selection state is now owned by the canvas (selectedNodeId /
   selectedEdgeId variables), not read from .selected DOM classes.
   The controller can ask via api.getSelection(). Fixes the fragile
   pattern in flowchart-controller.js where Delete/Backspace read
   querySelector('.flowchart-node.selected') — that read is now
   redundant and the controller can be cleaned up to use getSelection()
   directly.

5 new tests cover: edge starts past source centre, edge ends before
target centre, vertical edges connect top/bottom, label background
auto-sizes for long labels, canvas getSelection() reflects clicks.

Amit Haridas
2026-09-30 21:23:39 +05:30
Amit Haridas 855dfcd9f5 feat(flowchart): replace window.prompt with inline modal (C1)
The standalone flowchart generator still used window.prompt() for
editing edge kind/label and changing node kind — defeating the v4.9.9
modal work that's already in flowchart-bundle.js.

This commit switches to the existing window.FlowchartModals helpers:
  - onEdgeClick now does two sequential promptInline calls (kind,
    then label) instead of two blocking window.prompt calls.
  - onShapeMenu uses promptInline for node-kind change.
  - onEmptyClick (new) replaces the canvas's old 'auto-add a process
    node labelled Node on any empty click' behaviour. The canvas
    now hands off x/y to the controller, which prompts for kind +
    label before adding.

Canvas change is one line: drop the auto-add and call opts.onEmptyClick.

followups in this batch:
  - select/dropdown fields in the modal (currently text-only inputs)
  - edge connection geometry (centre-to-centre → boundary)
  - connect-mode preview line
  - canvas-owned selection state (DOM .selected is fragile)
  - undo/redo toolbar + counter
  - edge label auto-size + theme-safe background
  - zoom/pan, snap-to-grid, copy/paste, mermaid re-import, file IO

Amit Haridas
2026-09-30 21:22:22 +05:30
Amit Haridas d5efdfa8d9 docs(readme): bump version string to v4.13.0
Amit Haridas
2026-09-30 21:03:20 +05:30
Amit Haridas 3ec9ff0130 chore(release): bump v4.12.0 → v4.13.0
v4.13.0 ships:
  - Quick Switcher overlay (Cmd+P): fuzzy match across recent files
    + open tabs, workspace search wired to the active tab's parent dir
  - Inline AI assist (Cmd+K): Rewrite / Shorten / Expand with real
    SSE streaming, in-editor typewriter, cancel mid-flight, first-use
    confirmation
  - electron-updater integration: Help > Check for Updates now polls
    GitHub releases through the auto-updater; autoDownload + autoInstall
    enabled; browser fallback on dev/error
  - 13 commits since v4.12.0, 112 new tests (1187 → 1299), lint+format
    green throughout.

Amit Haridas
2026-09-30 21:02:46 +05:30
Amit Haridas e826c0e624 feat(ai-assist): first-use confirmation + wire Check-for-Updates to updater
Inline AI first-use confirmation (v4.13.0):
  - inline-ai-controller accepts optional confirmFirstUse + getProviderLabel
    deps. On the first action of the session, calls confirmFirstUse with
    {providerLabel, selectionLength, action}; if it returns false, aborts
    and shows error state. Once confirmed, never re-prompts for the
    session.
  - new 'ai-assistant:confirm-info' invoke handler returns
    {provider, model} (no key material) so the renderer can label the
    confirm dialog.
  - electronAPI.aiAssistant.confirmInfo() exposed via preload.
  - renderer.js wires window.confirm() with the provider label as the
    v1 prompt UX; can be upgraded to a custom modal later without
    touching the controller.

Check-for-Updates menu (v4.13.0):
  - Help > Check for Updates now calls ensureAutoUpdater().check()
    instead of opening the Releases page in the browser.
  - Falls back to the browser page if the check throws (dev runs with
    no app-update.yml, network failures, etc.) so the user always has
    a way to update manually.

4 new controller tests cover: confirm called once with provider label
and selection length; declining aborts + surfaces error state; confirm
not asked twice in a session; missing confirmFirstUse dep skips the
prompt and streams normally.

Amit Haridas
2026-09-30 21:02:28 +05:30
Amit Haridas 47315add80 feat(updater): electron-updater wiring + IPC bridge
electron-updater integration so users get fixes automatically:
  - new dependency electron-updater@^6.6.2 in package.json
  - electron-builder.config.js publishes to GitHub releases
    (owner: concreteinfo, repo: markdownconverter)
  - new src/main/auto-updater.js wraps autoUpdater: wires the six
    lifecycle events to a 'updates:status' IPC channel, exposes
    check() and install() helpers, dev-gates via isDev(), and
    swallows the 'missing app-update.yml' error that dev runs hit
  - main.js: ensureAutoUpdater() forwards status to the renderer
    via mainWindow.webContents.send; ipcMain handlers for
    'updates:check' (invoke) and 'updates:install' (invoke)
  - preload.js: 'updates:check' / 'updates:install' added to
    ALLOWED_SEND_CHANNELS; 'updates:status' added to receive
    channels; electronAPI.updates.{check, install, onStatus}
    exposed

7 new tests cover event registration, dev-gate behaviour,
'not-available' fallback for missing app-update.yml, typed event
payloads, error forwarding, and install() quitAndInstall.

Amit Haridas
2026-09-30 20:30:19 +05:30
Amit Haridas 0d3df32140 feat(quick-switcher): derive workspace dir from active tab
The explorer panel's explorerCurrentDir is hardcoded to null, so
the Quick Switcher's workspace toggle was a no-op. Rather than fix
the broken explorer flow, derive the workspace from the active
tab's filePath's parent directory — the most reliable source we
have without restructuring that panel.

deriveWorkspaceDir() handles POSIX and Windows paths (including
mixed separators and trailing separators), returns null for
untitled / empty inputs so the workspace toggle silently stays
off in those cases.

10 new tests cover POSIX, Windows, mixed-separator, trailing
separator, UNC, and invalid input paths.

Amit Haridas
2026-09-30 20:28:22 +05:30
Amit Haridas b36d0919a3 feat(ai-assist): renderer controller + Cmd+K wiring (end-to-end)
createInlineAiController() glues the popover, the streaming bridge,
and the CodeMirror editor together. Each Cmd+K opens a fresh
request; the controller:
  - reads the active selection from the CodeMirror view
  - builds the rewrite/shorten/expand prompt via inline-assist.js
  - clears the selection so chunks can fill back in
  - dispatches each chunk as it arrives (typewriter into the editor)
  - on done: applies the final result (or restores selection on no-op)
  - on error: restores the original selection and shows error state
  - on Esc: cancels the in-flight request, restores the selection,
    hides the popover

main-side streaming handler now reads the AI Assistant plugin's
provider config from settings.json under plugins.ai-assistant.* —
keys never cross the IPC boundary.

renderer.js:
  - new getCreateInlineAiController() lazy loader
  - new ensureInlineAiController() factory
  - new Cmd+K / Ctrl+K keymap handler in the global keydown listener
    that resolves the active tab's CodeMirror view via tabManager

16 new controller tests covering show/handleKey wiring, streaming
chunk application, done/error finalization, stale-request filtering,
and detach cleanup.

Amit Haridas
2026-09-30 20:17:57 +05:30
Amit Haridas 72ca2cb46f feat(ai-assist): floating popover UI + state machine
createInlineAiPopover() mounts a floating toolbar anchored to the
current text selection. Three actions (Rewrite / Shorten / Expand)
plus loading spinner with cancel, and an error state with retry and
dismiss. Click handlers dispatch via onAction/onCancel/onRetry.

Positioning flips below the line if there is no room above and
clamps horizontally so the popover stays inside the viewport.

16 new tests covering mount/visibility, idle actions dispatching the
selection text, state transitions (idle / loading / error), Retry
and Dismiss callbacks, and positioning edge cases.

Amit Haridas
2026-09-30 20:14:13 +05:30
Amit Haridas 1a25669967 feat(ai-assist): SSE streaming + IPC handler
AiProviders.completeStream() — async iterable over provider chunks.
  - OpenAI-style (openai, ollama, lmstudio, *-compatible): parses
    SSE data: {choices:[{delta:{content}}]} payloads.
  - Anthropic: parses event: content_block_delta with delta.text.
  - Honours caller AbortSignal + internal timeout via AbortController.
  - shared parseSseStream() helper handles [DONE] sentinel, partial
    lines across chunk boundaries, reader.releaseLock() in finally.

main.js IPC:
  - 'ai-assist-stream:start' (send) registers a per-requestId entry
    in a Map with an AbortController, iterates completeStream, and
    forwards each chunk via 'ai-assist-stream:chunk' (send).
  - 'ai-assist-stream:cancel' (send) aborts the in-flight request.
  - Terminal 'done' or 'error' (with code+message) is sent after
    each stream; renderer can correlate by requestId.

preload.js exposes aiAssist.{start, cancel, onChunk, onDone, onError}.

5 new tests covering OpenAI delta parsing, Anthropic content_block_delta
parsing, and three synchronous-error paths (no fetch, no messages,
missing key).

Amit Haridas
2026-09-30 20:04:32 +05:30
Amit Haridas cb0b2eac3d feat(ai-assist): pure prompt builder + result applier
buildAssistPrompt(action, selection) returns the {system, messages}
payload for rewrite/shorten/expand. Enforces 8KB selection cap and
typed errors with codes (unknown_action, bad_selection,
empty_selection, selection_too_large) so the IPC layer can map them
to user-friendly messages.

applyAssistResult(original, newText) trims the response and rejects
empty/unchanged payloads so a no-op streaming chunk doesn't churn
the editor.

15 new tests cover action coverage, prompt content, all four error
codes, cap-edge (exactly 8KB OK, 8KB+1 rejected), and trim semantics.

Amit Haridas
2026-09-30 20:01:54 +05:30
Amit Haridas b70dce9574 feat(quick-switcher): wire Cmd+P into File menu + renderer
End-to-end wiring of the Quick Switcher overlay:
  - main.js File menu: new 'Quick Switcher...' item with Cmd+P
    accelerator (moved from Print Preview; Print Preview stays in
    File > Print submenu but loses its shortcut). Recent Files
    submenu and its buildRecentFilesMenu() helper removed.
  - new 'recent-files:get' invoke IPC handler returns the recent
    files array on demand.
  - preload.js: 'recent-files:get' + 'show-quick-switcher' added
    to channel whitelists; quickSwitcher.getRecentFiles() exposed.
  - renderer.js: lazy-mounts the overlay on first 'show-quick-switcher'
    message, fetches recent files via IPC, and shows the overlay.
    File-open goes through the existing 'open-file-path' channel.

Workspace dir is intentionally null for v4.13.0 — the workspace
search toggle is wired but a no-op until the explorer panel exposes
its current directory (next iteration).

Amit Haridas
2026-09-30 20:01:16 +05:30
Amit Haridas 3bd958c064 feat(quick-switcher): modal overlay UI + keyboard nav
createQuickSwitcherOverlay() mounts a centered modal with:
  - input box with autocomplete=off
  - workspace toggle (off by default — recent + open tabs only)
  - keyboard nav: ArrowDown/Up move selection, Enter opens,
    Escape and backdrop-click hide
  - mouse hover also moves selection
  - debounced refresh (default 80ms; 0 in tests)
  - destroy() cleanly removes DOM + listeners

Rank/filter logic delegated to fuzzy-matcher.js. Recent and open tabs
are always in the candidate set; workspace files are added only when
the toggle is on. Selection clamps to bounds after every refresh.

23 new tests covering mount/focus/destroy, initial render, typing &
filtering, full keyboard nav, workspace toggle behavior, and the
selected-path escape hatch.

Amit Haridas
2026-09-30 19:58:18 +05:30
Amit Haridas 33fa14c11a feat(quick-switcher): IPC handler for workspace file listing
listWorkspaceFiles() walks a directory recursively, returning
[{path, name}] for files matching a configurable extension allowlist.
Skips hidden dirs, node_modules, dist/build/coverage, and walks past
permission-denied subdirs instead of throwing. Default allowlist is
markdown variants + .txt; default maxResults caps pathological
workspaces at 2000 entries.

Wired through 'quick-switcher:list-files' invoke channel + a
quickSwitcher.listFiles() convenience method on the preload bridge.
Main-process errors collapse to [] rather than crashing the overlay.

16 new tests covering flat dirs, recursion, skip lists, custom
extensions, maxResults cap, and validation errors.

Amit Haridas
2026-09-30 19:56:50 +05:30
Amit Haridas de7ebecad4 feat(quick-switcher): pure fuzzy matcher + ranker
Tiered scoring (exact 1.0 / stem 1.0 / prefix 0.8 / subseq 0.5+bonuses
capped 0.79) with leftmost+rightmost greedy alignment — rightmost wins
for extension/suffix queries ('md' against 'readme.md'), leftmost wins
for prefix queries ('re' against 'readme.md'). Boost-aware rankResults
applies recency x2 and open-tabs x1.5 multiplicatively; empty query
falls back to boosts alone.

Pure module, no DOM/IPC/Node — runs in renderer or main. 22 new tests.

Amit Haridas
2026-09-30 19:53:44 +05:30
amitwh 23046967c3 feat(flowchart): discoverable connect form + per-node color + save-to-file export; v4.12.0
User feedback on v4.11.0: the Add Connection form was buried below the
node/edge lists and they couldn't find it; they also asked for per-node
color and Save to File export. v4.12.0 reorganises the #fc-nodelist panel
to put the connect form right after the Add Node buttons, adds a native
<input type="color"> per node row that drives a new store.setNodeColor
mutator (with serialize/deserialize round-trip), and wires a Save to File
button that pops a system save dialog via a new 'save-text-file' IPC
channel.

- src/flowchart-generator.html — panel order is now Add Node /
  Add Connection / Nodes / Edges / Export (Insert · Save · Reset).
  Removed the legacy top toolbar; status moved into the panel.
  Added .fc-help, per-node color-picker CSS, .fc-toolbar-row.
- src/flowchart/flowchart-shapes.js — shapeSvg() accepts an optional
  6th 'color' arg; emits fill=... on rects and polygons. Falls back to
  #ffffff for empty/null/undefined so old callers keep working.
- src/flowchart/flowchart-store.js — setNodeColor(id, color) mutator
  with snapshot/emit, normalizeColor() helper (hex validation),
  addNode accepts color, deserialize normalises missing color.
- src/renderer/flowchart-bundle.js — mirrors all of the above inline
  (the bundle is loaded as a single <script>), wires fc-btn-save to
  api.saveFile, passes node.color to shapeSvg in the canvas render,
  and renders <input type='color'> per node row.
- src/main.js — new ipcMain.handle('save-text-file', ...) using a
  system Save dialog (mirrors the ascii:save handler shape).
- src/preload.js — added 'save-text-file' to ALLOWED_SEND_CHANNELS
  and a saveFile(content, defaultName) helper on the flowchart bridge.

Tests: 92/92 suites, 1165/1165 tests pass on this run
- +9 store tests (color defaults, setNodeColor, undo, hex validation,
  round-trip, deserialize normalisation)
- +7 shapes tests (fill color on all 5 kinds, fallback to #ffffff)
- +6 controller tests (color picker wires setNodeColor, canvas rect
  reflects color, Save to File calls api.saveFile with the fenced
  Mermaid source and 'flowchart.mmd', cancel + error paths surface in
  status)

Verification:
- npm run lint          clean
- npm run format:check  clean
- npm test              92/92 suites, 1165/1165 tests pass

Amit Haridas
2026-09-15 09:23:58 +05:30
amitwh e87e301b0f chore: remove stale debug-copy + drop placeholder string-split hack; v4.11.1
- Remove untracked root-level flowchart-bundle.js (older v4.10.0 copy
  that had drifted from src/renderer/flowchart-bundle.js; never loaded
  by Electron, just repo noise).
- In src/renderer/flowchart-bundle.js, replace the runtime
  concatenation "'place' + 'holder'" with the proper 'placeholder'
  attribute at the two label-input call sites (node + edge) and
  remove the now-stale comment explaining the hack. No functional
  change.
- Bump version 4.11.0 -> 4.11.1 in package.json and README.md and add
  the matching UPDATES.md entry.

Verification:
- npm run lint          clean
- npm run format:check  clean
- npm test              92/92 suites, 1143/1143 tests pass on this run
- PDFBatchOperations.test.js passes 21/21 in isolation
  (the occasional failure seen in the v4.11.0 run was a parallel-test
  file-IO race, not introduced by these changes)

Amit Haridas
2026-09-15 08:10:35 +05:30
amitwh 075e407f3f feat(flowchart): button-based UI replaces click-canvas interaction; v4.11.0
Replaced the v4.10.0 floating selection toolbar (which relied on
SVG click hit-testing inside #canvas-host and was unreliable in the
user's Electron runtime) with a button-driven node-list panel
(#fc-nodelist) below the canvas. Every mutation now flows through
explicit controls: 5 Add Node buttons, per-node kind-select +
label-input + delete ×, per-edge kind-select + label-input + delete
×, and a Connect form (From/To selects + Edge button + Refresh).

The canvas is purely visual now: no more #fc-selection-toolbar, no
controller-level _selectedId/_selectedKind state, no keyboard
Delete/Backspace selection handler. Canvas click callbacks
(onNodeClick, onEdgeClick, onShapeMenu) are no-ops. The 5-shape /
3-edge-kind / label-input / delete-button surface is unchanged in
spirit — it just lives in the panel instead of floating over the
canvas.

Files:
- src/flowchart-generator.html: added #fc-nodelist between canvas and
  preview (wrapped in new .fc-left column), removed fc-selection-toolbar,
  bumped header hint.
- src/renderer/flowchart-bundle.js: removed SHAPE_BUTTONS/EDGE_BUTTONS,
  setSelection, renderSelectionToolbar, appendLabelInput,
  appendDeleteButton, the keyboard Delete handler, and the
  controller-level selection state. Added rerenderNodeList +
  shapeLabel/edgeLabel, wired all panel buttons (add-row, per-node
  selects/inputs/deletes, per-edge selects/inputs/deletes, connect
  form). Subscribe now calls rerenderNodeList instead of the old
  renderSelectionToolbar.
- tests/flowchart-controller.test.js: replaced 6 v4.10.0 selection
  toolbar tests with 11 v4.11.0 button-driven panel tests.
- package.json: 4.10.0 -> 4.11.0.
- README.md: version line bumped to v4.11.0.
- UPDATES.md: added v4.11.0 changelog entry.

Test summary: 92 suites, 1143 tests pass (was 1138 — +5 net).
Lint + prettier clean.

Amit Haridas
2026-09-15 01:33:17 +05:30
amitwh b81119026e feat(flowchart): visible selection toolbar + console-log diagnostics; v4.10.0
Even after the v4.9.9 inline-modal fix the user kept reporting 'no fix still'
because hidden right-click context menus and window.prompt calls remain
unreliable in Electron renderer contexts. This release ships a *visible*
floating selection toolbar inside the canvas panel so the primary
interactions are not hidden behind modals or context menus.

- New <div id="fc-selection-toolbar"> inside #canvas-host, hidden until a
  node or edge is selected. Renders:
    * 5 shape buttons (Process / Decision / Terminator / Subroutine /
      Document) when a node is selected, calling store.setNodeKind on
      click. The active shape is highlighted.
    * 3 edge-kind buttons (Solid / Dotted / Thick) when an edge is
      selected, calling store.setEdgeKind.
    * An always-visible label input that mirrors the selected item's
      label and writes back via store.setNodeLabel / store.setEdgeLabel
      with a 100ms debounce.
    * A red Delete button calling store.removeNode / store.disconnect.
- Selection state now tracked at the controller level (_selectedId +
  _selectedKind) and exposed via window.FlowchartController so the
  keyboard Delete/Backspace shortcut and the toolbar share one source
  of truth.
- Console-log instrumentation on every canvas event (pointerdown with
  altKey, pointerup with drag result, dblclick, contextmenu, selection
  changes, and every bootstrap phase) so the user can open DevTools
  (Ctrl+Shift+I) and verify Alt+drag and double-click actually fire.
- promptInline / confirmInline kept as advanced fallback for the
  right-click 'change shape' path; the toolbar is now the primary
  interaction surface.
- Preview-render pane now shows a static info card explaining that the
  canvas on the left is the rendered chart (previously blank).
- 6 new tests in tests/flowchart-controller.test.js — toolbar hidden
  by default, selecting a node populates shape buttons + label input +
  Delete, clicking a shape button updates the kind, label input is
  debounced, edge selection populates edge-kind buttons, Delete button
  removes the selected node.

Total tests: 1127 passing (was 1121; +6 new). The 11 pre-existing
PDFOperations failures are unrelated to this change.
2026-09-15 00:40:01 +05:30
amitwh 81e8561403 fix(flowchart): replace window.prompt/confirm with inline modal in standalone window; v4.9.9
Electron renderer contexts return undefined from window.prompt/window.confirm,
so shape change, edge kind, edge label, and reset confirmation did nothing.
Bundle now ships promptInline/confirmInline (custom DOM overlay modals) and
uses them in onEdgeClick / onShapeMenu / reset handler. Exposed as
window.FlowchartModals for jsdom tests.

Amit Haridas
2026-09-15 00:27:24 +05:30
amitwh ca5983e9ff fix(flowchart): bundle pure modules into single file for standalone window; v4.9.8
Amit Haridas
2026-09-15 00:09:57 +05:30
amitwh 86d4c0d23a fix(flowchart): expose pure modules as window globals for standalone window; v4.9.7
Each pure module's UMD wrapper assigned window.FlowchartXxx only in the else branch — when 'module' was undefined. But the renderer runs with nodeIntegration:true, so 'module' is always truthy and the else branch never ran. The standalone Flowchart Generator window then aborted with 'Flowchart pure modules not loaded'.

Fix: append 'if (typeof window !== undefined) window.FlowchartXxx = exported;' after the CommonJS branch in all 4 pure modules (store, shapes, mermaid, canvas). Both branches can run now; the legacy sidebar panel still loads them via require() and the renderer unconditionally exposes the global.

Regression guard: 4 new source-grep tests in tests/flowchart-controller.test.js assert each module's source contains the 'window.FlowchartXxx = exported' assignment.

Attribution: Amit Haridas
2026-09-14 23:59:50 +05:30
amitwh 5f846d1e5f refactor(flowchart): standalone window (Cmd+Alt+F) replaces sidebar panel; v4.9.6
Five fix rounds (v4.9.1 → v4.9.5) couldn't make the sidebar flowchart
panel feel right — at 280 px sidebar with canvas + preview cramped to
~175 px each, plus the editor-container hide/show dance the
maximize/restore toggle required, the panel kept presenting as
cramped and unreliable at runtime. Strategy pivot: the flowchart
editor now lives in its own BrowserWindow, matching the ASCII Art
Generator pattern.

- src/flowchart-generator.html — new standalone HTML with header,
  toolbar (Insert at Cursor / Reset), canvas host, and preview host.
  Stylesheet hrefs are src/-relative (no ../). Forced light surface
  via background/color !important rules — mirrors the v4.9.5 CSS fix.
- src/renderer/flowchart-controller.js — pure browser IIFE. Hydrates
  from <userData>/flowchart-session.json once on mount, persists on
  every mutation with 500 ms debounce. Insert at Cursor wraps the
  generated Mermaid source in a fenced ```mermaid block and sends
  it through the existing insert-content IPC.
- src/main.js — openFlowchartGenerator() launches the standalone
  BrowserWindow (1100×720, contextIsolation:true, nodeIntegration:false).
  Tools menu entry 'Flowchart Generator' with Cmd/Ctrl+Alt+F.
- src/preload.js — new window.electronAPI.flowchart.* namespace with
  getUserDataPath/readFile/writeFile/insertAtCursor, reusing the
  existing thin text-file IPC handlers.
- The four pure modules (flowchart-shapes.js, flowchart-mermaid.js,
  flowchart-store.js, flowchart-canvas.js) gained a tiny UMD wrapper
  so they work both as CommonJS (legacy sidebar) and as browser
  globals (standalone window). No behavioural change to the 73
  flowchart unit tests in tests/flowchart-*.test.js.
- src/renderer.js — sidebar registerPanel('flowchart', ...) and the
  commandPalette entry are now commented out. Legacy panel file
  (src/sidebar/flowchart-panel.js) preserved for rollback.

Tests:
- tests/flowchart-controller.test.js — 10 new tests covering
  stylesheet paths (no ../ escape), bootstrap wiring
  (getUserDataPath once, reads flowchart-session.json), hydration
  from a saved session, Insert-at-Cursor fenced block format,
  Reset (with and without confirm), and two regression tests that
  src/renderer.js no longer has a live sidebar registration.

All 1122 tests pass; lint + format clean.

Amit Haridas
2026-09-14 23:50:25 +05:30
amitwh 9557c0af4d fix(flowchart): ensure rendered SVG + selection visibility; force light theme in canvas; v4.9.5
v4.9.4 shipped three interaction bugs in the Flow Chart panel that combined to make it look broken at runtime: (1) .flowchart-preview-render had no min-height, so the Mermaid SVG clipped to 0 when the sidebar flex column shrank; (2) the canvas + preview hosts inherited the body.theme-concreteinfo dark theme, producing dark-on-dark nodes; (3) .flowchart-node.selected only set a 2px stroke on the rect's existing dark fill, which was effectively invisible.

Fix: styles-sidebar.css adds min-height: 120px on .flowchart-preview-render; forces a light background on .flowchart-canvas-host / .flowchart-preview-host with !important so theme inheritance cannot override it; forces explicit white fills and dark strokes on .flowchart-node rect/polygon/text and .flowchart-edge so labels read against any background; selection now changes fill (#e3f0ff) AND bumps stroke-width to 3 on both nodes and edges. renderer.js renderFlowChartMermaid now always initializes Mermaid with theme: 'default' regardless of body class — keeps the Mermaid SVG light to match the CSS-forced canvas surface.

Three new CSS regression tests in tests/flowchart-panel.test.js read the shipped stylesheet and assert the three structural invariants (non-zero min-height on render target, !important light background on canvas+preview hosts, fill + stroke-width >= 3 on .flowchart-node.selected).

Tradeoff: the flowchart surface is now always light, diverging from body theme. Visibility of a working editor is the priority per user direction.

Amit Haridas
2026-09-14 23:09:23 +05:30
amitwh d31ed86fab fix(flowchart): wire selection + layout precedence; v4.9.4
- Add opts.onNodeClick + surgical applySelectionHighlight() so node/edge clicks immediately paint .selected and mirror id into panel state (Delete/Backspace works on freshly-clicked node).
- Add Maximize/Restore button to flowchart toolbar: toggles .main-content.flowchart-takeover which hides .editor-container and lets the canvas + preview split the full window width instead of the 280px sidebar.
- destroy() clears the takeover class so the editor stays usable after leaving the panel.
- 8 new tests in tests/flowchart-panel.test.js for selection wiring and takeover.

Amit Haridas
2026-09-14 23:00:47 +05:30
amitwh e0945b427d fix(flowchart): prevent preview-source duplication; v4.9.3
The flowchart preview pane accumulated raw Mermaid source when the user
fired several addNode mutations within the 250ms debounce window —
mermaid.run({nodes:[div]}) is async, so the previous render's
<div class="mermaid"> (still carrying the source text) sat in
.flowchart-preview-render when the next render cleared the target. The
first render's eventual element.innerHTML=svg landed on a detached node,
but the visible preview pane had a stack of stale <div class="mermaid">
elements carrying the source.

Fixed by switching renderFlowChartMermaid (src/renderer.js) to
replaceChildren() and adding a per-target WeakSet in-flight tracker so
the new render always starts from a clean slate and the previous render's
eventual innerHTML=svg is harmless on a detached node.

Added a second regression test that fires 7 mutations inside the debounce
with a renderMermaid mock that mimics mermaid.run's async innerHTML=svg
closure.

Amit Haridas
2026-09-14 22:52:13 +05:30
amitwh ac31162d3b fix(ascii-art): wire Box/Templates mode + correct standalone <link href>; v4.9.2
- src/ascii-generator.html line 7: ../fonts.css -> fonts.css (src/-relative;
  fixes silent stylesheet miss — same class as the v4.9.0->v4.9.1 script-tag fix)
- src/renderer/ascii-controller.js: wire all 3 mode tabs, 18 template
  buttons, and the 3 box form fields. setMode() toggles .active on tabs
  and matching .mode-section. Templates route through the orchestrator's
  template:<id> font namespace. Box mode renders text with single/double/
  rounded/bold/ascii borders via a pure renderBox() helper. All 11 T9
  behaviours preserved.
- tests/ascii-controller.test.js: 6 tests covering stylesheet path,
  pure box renderer, mode-tab switching, and template button wiring.
- package.json 4.9.1 -> 4.9.2; README + UPDATES updated.

Amit Haridas
2026-09-14 22:37:02 +05:30
amitwh 8c708b7abb fix(flowchart): pre-await getUserDataPath to fix persistence; bump v4.9.0 → v4.9.1
Amit Haridas
2026-09-14 22:24:27 +05:30
amitwh 58aa4da00f chore(release): bump v4.8.0 → v4.9.0 — visual flow chart, ASCII art upgrade, 12 new themes
Amit Haridas
2026-09-14 22:19:33 +05:30
amitwh b28d11035b docs(readme): drop undocumented Add Flow Chart Node Insert shortcut
The flowchart panel keyboard handler in src/sidebar/flowchart-panel.js only
handles Ctrl+Z, Ctrl+Shift+Z, Delete, and Backspace. There is no Insert-key
branch, so the README row documenting it was a lie. Remove the row until the
behaviour is actually implemented.

Amit Haridas
2026-09-14 22:06:25 +05:30
amitwh bf0ed6857d fix(sidebar): register Ctrl+Alt+F to toggle flow chart panel
The rail button tooltip advertised the shortcut, but commandPalette.register
was missing for 'Toggle Sidebar: Flow Chart'. Wire it up next to the other
sidebar toggles (Explorer/Git/Outline) so the advertised shortcut works.

Amit Haridas
2026-09-14 22:06:20 +05:30
amitwh aee30a2a9c docs(readme): visual flow chart editor feature + panel-scoped shortcuts
Amit Haridas
2026-09-14 21:55:48 +05:30
amitwh b0dcf57daf feat(sidebar): register flowchart panel + rail button + thin fs IPC
- Add data-panel=flowchart rail button in src/index.html after daily-notes

- Append .flowchart-* panel CSS selectors to src/styles-sidebar.css

- Add flowchartIO IPC bridge and registerPanel('flowchart') in src/renderer.js

- Add get-user-data-path / read-text-file / write-text-file IPC in src/main.js

- Path validation: sandbox inside app.getPath('userData')

- read-text-file returns null on ENOENT (file doesn't exist)

Amit Haridas
2026-09-14 21:52:32 +05:30
amitwh a6dc54cedc fix(sidebar): clean up listeners + timers + store subscription on panel destroy
destroy() previously only called canvas.destroy(), leaking:
- the container keydown listener
- the insertBtn click listener
- the debouncedPreview setTimeout handle (could write to a detached <pre>)
- the debouncedPersist setTimeout handle (could writeFile after unmount)
- the store.subscribe listener (store kept a stale closure)

Also compute the persistence path once on mount instead of invoking
getUserDataPath() three times per panel lifecycle.

Inline debouncedPreview / debouncedPersist so their timer handles are
reachable from destroy(). Capture store.subscribe's unsubscribe.
destroy() order: clear timers, unsubscribe store, remove listeners,
then canvas.destroy().

Amit Haridas
2026-09-14 21:49:49 +05:30
amitwh 59ee9c609e feat(sidebar): flowchart-panel — canvas + preview + persistence + insert
Amit Haridas
2026-09-14 21:45:47 +05:30
amitwh 0331a08207 feat(flowchart): SVG canvas with drag, double-click label edit, context menu hook
- Adds flowchart-canvas.js with createCanvas(container, store, opts) -> { destroy, getSvg }
- Consumes T1 store (subscribe/moveNode/connect/addNode) and T2 shapeSvg for 5 node kinds
- All 3 edge kinds render: solid (default), dotted (stroke-dasharray 4,4), thick (stroke-width 3)
- Pointer events: drag moves nodes; Alt+drag creates solid edges between nodes
- onEdgeClick fires on edge click; onShapeMenu fires on contextmenu
- Double-click opens inline <input> overlay for label editing
- destroy() unsubscribes from store and removes all listeners/SVG

Tests: 9 new (rendering 5, pointer events 2, subscribe 1, destroy 1) @jest-environment jsdom
Full suite: 89 suites / 1083 tests pass; lint clean; prettier clean.

Amit Haridas
2026-09-14 21:41:58 +05:30
amitwh 216e0ecf51 feat(flowchart): mermaid translator (flowchart TD, all 5 shapes, 3 edge kinds)
Pure Mermaid translator: graph (nodes + edges) -> flowchart TD source.
5 node kinds (process/decision/terminator/subroutine/document) ->
Mermaid syntax ([..], {..}, ([..]), [[..]], [/../]).
3 edge kinds (solid/dotted/thick) -> Mermaid arrows (-->, -.->, ==>).
Label escaping: double quotes become #quot;, newlines become literal \n.

Also moves tests/fixtures per-test dirs to fixtures-epub / fixtures-css
so the shared tests/fixtures/ stays available for snapshot fixtures.

Amit Haridas
2026-09-14 21:36:02 +05:30
amitwh 4a36b7db76 feat(flowchart): 5 SVG shape templates (process/decision/terminator/subroutine/document)
Amit Haridas
2026-09-14 21:25:38 +05:30