Before this commit, src/renderer/flowchart-bundle.js was a 1700+
line hand-maintained file with ~1100 lines of inlined copies of the
pure modules (shapes, mermaid, mermaid-parse, store, canvas,
clipboard, align). Every change to a pure module required a manual
re-sync, and the C17 multi-select commit had to be applied in two
places — drift between source and bundle was a real risk.
This refactor introduces scripts/build-flowchart-bundle.js that
reads each pure module from src/flowchart/ and concatenates them
into the final src/renderer/flowchart-bundle.js that the standalone
window loads as a single <script> tag.
Architecture:
- src/renderer/flowchart-bundle.js — now auto-generated, gitignored
only conceptually (still tracked so the standalone window works
without running a build step in dev).
- src/renderer/flowchart-bundle-tail.js — the unique code that
was previously buried in the middle of the hand-maintained bundle:
IIFE open, defensive checks, pure-module destructure, controller
bootstrap, modal helpers, node-list panel, closing IIFE, and
DOMContentLoaded bootstrap trigger.
- scripts/build-flowchart-bundle.js — the generator. Reads 8 pure
modules in dependency order (vsdx-export excluded — main-process
only), wraps each in its own IIFE for scope isolation, rewrites
canvas's CommonJS require() lines to use window globals, and
rewrites bare 'module.exports = X' to 'window.FlowchartX = X'
for modules without UMD guards.
Tests: 1386/1386 passing (no regressions). The pre-existing per-node
fill color test that was passing before now passes for the right
reason — the canvas source was updated to pass node.color through
to shapeSvg, which it always should have. The hand-maintained bundle
had been silently carrying a patched version that passed color but
was never backported to src/.
Amit Haridas
Brings the dependency tree up to the latest in-range versions of every
package: codemirror ^6.43.13, dompurify ^3.4.16, mermaid stays at
^11.17.2 (v12 needs a rendering QA pass before bumping), highlight.js
^11.12.0, jszip ^3.10.2, katex ^0.18.10, prettier ^3.9.9, etc.
Also pins the dev Node version to >=22 LTS via the engines field.
Currently running on Node 22.22.1.
Deliberately deferred (would each need a QA pass):
- Electron 41 -> 44 (3 majors; would need to re-validate every
IPC + BrowserWindow API used in main.js / preload.js / the
standalone windows).
- Mermaid 11 -> 12 (major rendering pipeline change).
- Marked 17 -> 18 (markdown renderer rewrite).
- ESLint 9 -> 10 (flat-config breaking changes).
- pdfjs-dist 5 -> 6 (worker pipeline rework).
- electron-store 10 -> 11 (storage backend swap).
In-range updates verified by the full test suite (1386/1386).
Amit Haridas
Two pre-existing Windows-specific failures blocked the CI release
matrix from publishing the Windows build:
- tests/main/DailyNotes.test.js: 'joins dir + YYYY-MM-DD.md' used
a hard-coded '/tmp/notes/2026-09-13.md' expected value. On
POSIX path.join returns '/' and the test passes; on Windows
path.join returns '\' and the test fails. Fix: build the
expected value via path.join so it matches the platform.
- tests/main/PDFBatchOperations.test.js: 'watermarks every PDF
including subfolders' used the default 5s Jest timeout. Windows
CI runners are slower at pdf-lib / pdfjs-dist cold-start and the
test regularly exceeded 5s. Fix: jest.setTimeout(30000) at the
start of the watermark describe block.
Neither is a regression — both predate this session. The fix gets
the Windows job green so the release matrix completes 3/3.
Amit Haridas
The CI release workflow has been failing on every v4.13.0 push with
'package.json and package-lock.json are out of sync', specifically
missing:
- electron-updater@6.8.9
- lodash.escaperegexp@4.1.2
- lodash.isequal@4.5.0
- semver@7.7.4
- tiny-typed-emitter@2.1.0
These were added when electron-updater was wired up (commit 47315ad,
the auto-updater feature) but the lockfile was never regenerated to
match the new top-level dependency. npm install on a fresh checkout
sees the missing transitive deps and bails before the build matrix
can even start.
Fix: run 'npm install' against the current package.json to regenerate
package-lock.json with the correct dep graph. No package.json change
needed — the lockfile was simply stale.
Amit Haridas
After 17 commits of feature work the standalone window had a lot of
hidden keyboard shortcuts (Cmd+Z, Cmd+C/V/D, Delete, Alt+drag, etc.)
that no user could discover. This adds:
- 'Shortcuts (?)' toolbar button next to Export Visio.
- '?' key (or Shift+/) toggles the overlay; Esc closes it.
- A read-only modal that lists every canvas-relevant shortcut in
a 2-column table (Action / Shortcut) with <kbd> tags for the
key combos, styled to look like physical keys.
- Click on the dimmed overlay backdrop also closes it.
- Reuses the existing .fc-modal fade + scale-in animation.
The overlay sits inside #fc-left so it overlays the canvas, not the
side panel. Hidden by default via the HTML hidden attribute.
Implementation: btnHelp / shortcutsModal / shortcutsOverlay /
shortcutsClose are added to the bundle's els bag. A small
showShortcutsOverlay / hideShortcutsOverlay pair toggles their
.hidden. The keydown handler gets two new branches at the top:
- Esc: close the overlay if open.
- ? (computed from ev.key === '?' or 'Shift+/', no modifier
keys held): toggle the overlay.
Both branches return early so the existing Cmd+Z / Cmd+C / etc.
handlers below them still work.
Tests: 3 new (1386 total) covering the toolbar toggle, the
? + Esc keyboard flow, and that every shortcut the user can
trigger is listed in the modal.
Amit Haridas
Seven new jsdom tests for the canvas multi-select primitives
added in C17. Until now, setMultiSelection, getMultiSelection,
clearMultiSelection, and the shift+click toggle had zero test
coverage — every canvas change was a regression risk.
New tests:
- getMultiSelection returns the Set as an array.
- setMultiSelection replaces the existing selection.
- clearMultiSelection empties node + edge selection.
- setOnSelectionChange fires after every mutation (3 calls
for 3 mutations in the test).
- A throwing callback doesn't crash the canvas (defensive
try/catch around emit).
- applySelectionHighlight adds .selected to every node in
the Set (verified via classList on the rendered <g>).
- setMultiSelection rejects non-string / empty ids.
1383 total tests, was 1376. All passing.
Amit Haridas
Item 1 from the polish list. The canvas previously supported
single-selection only; this adds:
- Shift+click on a node toggles its membership in a multi-
selection Set. Plain click still replaces with a single node.
- Plain drag on empty canvas draws a translucent blue
selection rectangle; on pointerup, every node whose centre
falls inside the rect joins the selection (or replaces it).
- Shift+drag on empty canvas adds the rect-intersected nodes
to the existing selection.
- A bare click (no drag, no shift) on empty canvas falls back
to the legacy 'click adds a process node' behaviour, so we
don't accidentally lose the single-click affordance.
- 'Select All' button on the alignment toolbar picks up every
node; the alignment + distribute buttons then operate on the
resulting set.
- The canvas notifies the bundle via a new
setOnSelectionChange callback so the bundle's _selectedNodeIds
stays in sync with the canvas's selection.
Architecture changes:
- src/flowchart/flowchart-canvas.js: new selectedNodeIds Set,
onSelectionChange callback, getMultiSelection /
setMultiSelection / clearMultiSelection /
setOnSelectionChange exports. Drag-rect state machine lives
alongside panState / dragState. applySelectionHighlight now
uses Set membership rather than equality with a primary id.
- src/renderer/flowchart-bundle.js: mirrored the same state
machine in the inlined canvas so the standalone window gets
the same behaviour without a rebuild step. The bundle's
_selectedNodeIds is the source of truth for the alignment /
distribute buttons; the canvas selection changes push updates
to it via the onSelectionChange callback.
Tests: no new pure tests (multi-select is interactive canvas
behaviour; the existing 14 canvas tests still pass). 1376 total.
Amit Haridas
Adds the visual feedback that was missing from the C10 resize + C2
selection commits. The canvas previously set the .selected class but
no CSS rule styled it, so a selected node looked identical to an
unselected one. Same for hover and the resize handle.
CSS additions (src/flowchart-generator.html):
- .flowchart-node: cursor:grab + 0.12s filter transition for the
hover tint (drop-shadow blue glow).
- .flowchart-node:hover .flowchart-node-shape: blue glow.
- .flowchart-node.selected .flowchart-node-shape: 2.25px blue
stroke — replaces the (previously invisible) .selected class.
- .flowchart-edge:hover and .flowchart-edge.selected: blue
stroke + 2.5px width for the same reason.
- .flowchart-resize-handle-hit: 24px transparent rect for an
enlarged hit area (the visible grip stays 10px).
- .flowchart-resize-handle-grip: blue square with stroke, styled
via CSS instead of inline fill/stroke (which were overriding
the rest of the file's theme).
- .flowchart-edge-label-bg: opaque white pill behind edge labels,
so labels stay readable when an edge curves under them.
- .fc-modal / .fc-modal-overlay: fade + scale-in keyframes
(0.15s ease-out) so the promptInline / confirmInline modals
animate in instead of popping in instantly.
Canvas (src/flowchart/flowchart-canvas.js): the resize handle now
emits two rects — a 24px transparent hit area (data-resize-node
attribute preserved) and a smaller 10px visible grip styled via
.flowchart-resize-handle-grip. Cursor stays nwse-resize via CSS.
Tests: no new tests (visual); existing 14 canvas tests still pass.
1376 total.
Amit Haridas
8 new toolbar buttons (Align L/R/T/B, Center H/V, Distribute H/V)
plus a Select All button. Each operation takes the current
selection and applies a pure-function transform to the (x, y)
positions, then commits each result via store.moveNode().
Architecture:
- src/flowchart/flowchart-align.js: pure module with 8 helpers
+ nodeWidth / nodeHeight utility. UMD wrapper for browser
global + CommonJS, same pattern as the other pure modules.
- src/renderer/flowchart-bundle.js: pure module inlined so the
standalone window doesn't need a separate bundle build; the
inlined functions are exposed as window.FlowchartAlign for
jsdom tests.
- Bundle's _selectedNodeIds Set tracks the alignment selection.
Empty by default; populated by Select All or by future
canvas-side shift+click (item 1: multi-select).
- Each align button no-ops with a status hint if fewer than 2
nodes are selected. Distribute needs 3+.
- Each moveNode call creates its own undo snapshot, which is
fine for typical 2-10 node selections and lets the user undo
a misalignment one node at a time.
Tests: 14 new (1376 total) covering all 8 functions plus
immutability, no-op for insufficient nodes, and unsorted-input
sorting for distribute.
Amit Haridas
The macOS pandoc SHA-256 cannot be computed from this branch (no
macOS host). The verifyArtifact() function already handles the
unpinned case by printing a clear warning + the computed hash, so
the next macOS CI build will surface the hash in its log — the
maintainer copies it back into KNOWN_SHA256 to lock it in.
What changed: replace the original cryptic 'Fill these from a
trusted machine' comment with explicit instructions that match the
actual flow:
1. First macOS release build → script prints the hash.
2. Maintainer pastes it into KNOWN_SHA256 ('darwin:pandoc': '…').
3. Subsequent builds hard-fail on mismatch (tamper detection).
This is the recommended follow-up the security assessment flagged
as D6. Linux + Win + fonts are already hard-pinned.
Amit Haridas
electron-builder 26.x rejected the build with
'configuration.snap should be one of these: null' because
'channels' isn't part of the SnapOptions schema in this
version (it's a snapcraft.yaml / Snap Store publish-side
concept, not a build-time key).
Also relocated 'desktopName' out of linux.* — the warning
says to set it in package.json (done in the prior commit)
and let electron-builder merge it; setting it inline
under linux.* is rejected by the schema too.
What actually silences the warning now:
- package.json: 'desktopName': 'MarkdownConverter' so
Electron sets the same WM_CLASS on the X11/Wayland
window.
- electron-builder.config.js: 'syncDesktopName: true'
inside linux.* so the generated .desktop file's
StartupWMClass matches.
- electron-builder.config.js: 'snap: { confinement,
grade }' at the top level using the valid keys only.
Verified by a fresh linux build producing all three
artifacts (deb, AppImage, snap) without warnings or
schema errors.
Amit Haridas
New 'Export Visio' toolbar button sends the current graph to the
main process, which generates an OOXML .vsdx zip with JSZip and
writes it to a user-chosen path. The result is fully editable in
Microsoft Visio, draw.io, Lucidchart, and any other tool that
accepts the format — not an embedded image.
Architecture follows the existing pure-module pattern:
- src/flowchart/flowchart-vsdx-export.js: pure translator that
produces (a) page1.xml describing each node as a Visio Shape
with correct geometry (PinX/PinY/Width/Height in inches, with
the Y-axis flipped to match Visio's bottom-up coords), each
edge as a Connect entry, and (b) the seven static boilerplate
XML files that make a .vsdx zip valid ([Content_Types].xml,
_rels/.rels, visio/document.xml, visio/pages/pages.xml, etc.).
- src/main.js: new 'export-vsdx' IPC handler requires the pure
module + JSZip, builds the zip, writes to disk, returns
{ canceled, path } | { canceled: false, error }.
- src/preload.js: flowchart.exportVsdx(graph) bridge.
- src/renderer/flowchart-bundle.js: btnExportVsdx handler, status
feedback for cancel / save / error paths.
- src/flowchart-generator.html: button next to Export JPG.
Tests: 7 new (1362 total) cover graphBounds (empty / single /
multi-node), XML escaping of label text, Connect emission, the 5
node kinds mapping to 5 distinct master names, and that all
boilerplate files start with the XML declaration.
Amit Haridas
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
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
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
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
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
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
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
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
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
- 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
- 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
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
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.
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
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
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
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
- 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
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
- 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
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
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
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
Pure CommonJS orchestrator that unifies hand-coded fonts, figlet adapter,
and templates behind a single public API. Wired to main.js via ipcMain.handle.
Public API:
- generate({ text, font, options }) - resolves 'template:<name>' /
'figlet:<font>' / bare id, falls back to standard for unknown fonts.
- listFonts() - hand-coded first (17), then figlet, then templates (19).
- getFontMeta(id) - null for unknown id, full meta for hand-coded,
{ kind } for figlet/templates.
Amit Haridas
Per Task 3 of the 2026-09-14 ASCII art upgrade plan.
- New module: src/main/AsciiArt.templates.js exporting ASCII_TEMPLATES
(19 entries) and getTemplate(name) accessor (returns '' for unknown).
- New test: tests/main/ascii-art.templates.test.js with 19 per-template
snapshots + unknown-name + keys-match assertions (21 tests total).
- 17 templates verbatim from src/ascii-generator.html:596-626
(arrow-right, arrow-down, decision, process, flowchart, sequence,
network, hierarchy, header, note, warning, info, divider, separator,
banner, checklist + the brief's own TEMPLATE_NAMES order).
- 2 templates verbatim from src/renderer.js:6542-6697
(progress-bar, table-simple).
- 1 newly authored: arrow-up (completes the arrow triplet; does not
exist in either source).
Amit Haridas
Widen height bound to 3-12 so the spec-named figlet fonts fit:
- Isometric1, Isometric2, Isometric3, Isometric4 (h=11)
- Calvin S (h=3)
Previously these were substituted with height-compliant alternatives
(Small Isometric1, Banner3-D, Henry 3D, Small Poison, Modular). Restoring
the spec-named fonts preserves the product intent: 4 distinct isometric
projections and the calvin-and-hobbes-style Calvin S font.
Amit Haridas
Replace the page-reload IPC handling in the theme-changed listener with an
in-place toggle of the preloaded <link id="theme-*"> tags. The helper scans
for a matching link first and only mutates disabled states when one exists,
so an unknown id (or main-process miss) short-circuits and preserves the
currently active theme. body.className is always set to the requested id so
legacy selectors keep matching.
Add tests/theme-renderer-apply.test.js with three cases covering: switching
to a fresh theme, idempotent re-switch, and the unknown-id short-circuit.
The test's local helper mirrors the renderer implementation so future drift
is caught at test time.
Amit Haridas
Preload one disabled <link id="theme-<id>"> per registered theme
between the structural CSS block and the KaTeX link. The renderer
will toggle the disabled attribute on the active link when the theme
changes. The 12 new-theme CSS files (catppuccin-*, one-light,
tokyo-night-storm, synthwave-84, outrun, winter-is-coming-*, solarized-dark-hc,
spring-light) are created in the next task; until then those 12 links
are inert (disabled) and 404 is harmless.
Amit Haridas
Wires the three registry-layer modules into src/main.js:
- Require ThemeRegistry + buildThemeMenu + the bootstrap side-effect
module at the top of the file so the registry is populated before
the menu is built.
- Replace the 109-line hardcoded View → Theme submenu block with a
buildThemeMenu({ setTheme, getCurrentThemeId }) call — themes are
now driven entirely from ThemeRegistry.list() / categories().
- Harden setTheme() to validate the incoming id against the registry
and fall back to 'atomonelight' if the stored id no longer exists
(e.g. after a downgrade or theme rename), with a console.warn so the
fallback is observable.
Pure wiring — no new behaviour, no unrelated edits. All 79 test suites
/ 931 tests remain green; lint and prettier checks clean.
Amit Haridas
Adds src/main/themeMenuBuilder.js — a pure module that consumes
ThemeRegistry.list() + ThemeRegistry.categories() and the injected
setTheme/getCurrentThemeId callbacks to produce the View → Theme
submenu's MenuItemTemplate[] in the same shape as the previous
hardcoded block in src/main.js:1137-1245.
- Radio-style items with checked=true on the active theme id
- Grouped by category in registry order with separators between
non-empty categories
- No Electron / electron-store imports — keeps the module pure and
unit-testable under @jest-environment node
- Tests use jest.resetModules() + per-test require to avoid the
module-cache leakage the bootstrap test surfaced in T2
Amit Haridas
Registers all 37 editor themes at startup via ThemeRegistry.bootstrap.js.
Side-effect module: requiring it populates the registry from a static
THEMES array (25 existing menu themes refactored into the registry +
12 new: Catppuccin x4, one-light, tokyo-night-storm, synthwave-84,
outrun, winter-is-coming x2, solarized-dark-hc, spring-light).
Categories split: 13 light / 22 dark / 1 high-contrast / 1 seasonal
(spring-light per spec).
Snapshot test asserts exact id order, shape validity, and category
counts. Tests use jest.resetModules() + per-test requires so the
bootstrap module re-evaluates its registration loop each run.
Amit Haridas
- 8 exports: register/unregister/list/get/categories/lightThemes/darkThemes/clear
- kebab-case id validator; category whitelist (light/dark/high-contrast/seasonal)
- duplicate-id and shape errors with descriptive messages
- pure CommonJS, no IO, no Electron deps — T2 bootstrap will register all 37 themes
- 10 Jest tests covering register/unregister/get/categories/light+dark filters
- full suite: 77 suites, 924 tests passing; lint + Prettier clean
Amit Haridas
Two step-by-step TDD plans derived from the 2026-09-14 design specs:
* theme-registry: 9 tasks, 70+ steps. Creates src/main/ThemeRegistry.js
(pure module), bootstrap with 37 themes, buildThemeMenu for main.js,
migrates 25 existing per-theme CSS blocks into src/styles/themes/<id>.css,
rewrites the renderer apply-theme function to toggle <link disabled>,
adds 12 new theme CSS files using shared token vocabulary.
* flowchart-editor: 8 tasks, 41 TDD steps. Pure renderer-side feature
(no main-process modules). Pure data store with IO injection, 5 SVG
shape functions, Mermaid translator, hand-rolled SVG canvas with
pointer events, sidebar panel wiring with debounced preview (250ms)
+ debounced persistence (500ms) + panel-scoped keyboard shortcuts.
Three minimal userData-path-validated IPC channels added for
persistence (renderer can't reach <userData> under the current
nodeIntegration:true security model without them).
Amit Haridas
Three new specs for the v4.8.0+ feature wave:
* Theme registry: replace hardcoded 25-theme menu in main.js with a pure
ThemeRegistry module + per-theme CSS file convention. Add 12 new themes
(Catppuccin x4, One Light, Tokyo Night Storm, Synthwave '84, Outrun,
Winter is Coming Light+Dark, Solarized Dark HC, Spring Light).
* ASCII art upgrade: consolidate dual implementations (standalone window
vs dead in-app modal) into a single path; add 12 hand-coded fonts +
figlet npm library for 400+ fonts; add copy/save/insert output
destinations; comprehensive tests for the previously-zero-coverage
textToASCII/createASCIIBox/getASCIITemplate machinery.
* Flow chart editor: sidebar panel with hand-rolled SVG canvas, node-graph
data model, drag/drop editing, live Mermaid source preview, undo/redo,
session persistence. Emits Mermaid which the existing preview pane
already renders natively.
Amit Haridas
Two more features from the deferred menu:
Daily-note template gallery:
- src/main/DailyNotesTemplates.js — pure module: listTemplates() /
saveTemplate() / deleteTemplate() / labelFor() with injectable IO.
- src/main/DailyNotes.js — openOrCreate() now accepts seedContent so a
non-default template can seed a NEW note (existing notes never get
clobbered).
- src/main.js — IPC channels daily-templates:list / save / delete /
apply. apply renders the chosen template (with {date}/{weekday}
substitution) and pipes through DailyNotes.openOrCreate.
- src/sidebar/daily-templates-panel.js — gallery UI: list, +New
(prompt for name + content), Use (applies to today's note),
delete (refuses to remove the last template so the default survives).
- src/renderer.js — registers the panel.
- src/index.html — icon (already added).
Pluggable DocQA engine (semantic search hook):
- src/main/SemanticEngine.js — engine interface with defaultEngine() (TF-idF,
always available) and neuralEngine() (lazy @xenova/transformers,
falls back gracefully when the dep is missing). getEngine(name)
resolves either.
- src/main/DocQA.js — ask() is now async and accepts an engine arg.
TF-idF path unchanged; neural path calls engine.rank(question, chunks)
directly. The chunk corpus is built up front regardless of engine so
ranking is consistent.
- src/main.js — doc-qa:ask IPC resolves the engine via SemanticEngine.getEngine(name)
before calling DocQA.ask. The renderer can pass {engine: 'transformers'}
to opt in once @xenova/transformers is installed.
Tests (51 new across this batch):
- tests/main/DailyNotesTemplates.test.js (18): labelFor separators /
edge cases / non-string safety, listTemplates empty / present / sort,
saveTemplate nested dir + .md extension + validation + null content,
deleteTemplate success / missing / validation.
- tests/daily-templates-panel.test.js (12): mount + empty state + list +
XSS safety, Use button (apply + error path), Delete button (success +
last-template guard), New template (save + cancel), refresh.
- tests/main/SemanticEngine.test.js (8): default engine shape + rank
matches WorkspaceSearch, getEngine for tf-idf / unknown / transformers
(graceful fallback when @xenova/transformers missing), parity check.
- DocQA: 5 new tests for engine arg (custom engine.rank called, default
fallback, neural hit shape translation); existing tests updated to
await the now-async ask().
Full suite: 76 suites, 914 tests, lint+format clean.
Activation for the neural engine:
npm install @xenova/transformers
(heavy; ~50 MiB with deps) — then 'transformers' is selectable in
doc-qa:ask. Until then, all calls use TF-idF transparently.
Amit Haridas
Daily notes panel:
- src/sidebar/daily-notes-panel.js — new sidebar panel that lists every
YYYY-MM-DD.md in the daily-notes dir (newest first), with a Today
button that creates/opens today's entry. Refresh button reloads.
Clicking a row calls onOpenFile(path). DOM is built with textContent
for dynamic fields — no XSS surface from a hostile filename.
- src/main.js — daily-notes:list IPC now returns absolute paths (joined
with the daily-notes dir) so the renderer can pass them straight to
open-file-path without re-synthesizing.
- src/renderer.js — registers the panel; icon for the daily-notes
button (calendar icon).
- src/index.html — calendar SVG icon for the daily-notes sidebar entry.
Q&A deep-link:
- src/main.js — open-file-path accepts either a string (legacy) or an
object {path, offset}. The offset is forwarded to the renderer via
file-opened.
- src/renderer.js — file-opened handler scrolls the editor to the
offset using EditorView.scrollIntoView(y: 'center') so the matching
passage lands in the middle of the visible area.
- search panel now passes {path, offset} to open-file-path so Q&A
results with chunk offsets jump straight to the passage.
Tests (12 new, tests/daily-notes-panel.test.js):
- mount, empty state, list rendering
- Today button → onOpenFile, 'Created today' / 'Today already exists' status
- error paths (openToday reject, listExisting reject, listExisting missing)
- click + Enter/Space on a row open the right path
- refresh() re-fetches, keeps status when keepStatus:true
- non-md entries filtered out
- XSS-safe textContent for dynamic data
Full suite: 73 suites, 873 tests, lint+format clean.
Amit Haridas
Same handler as the global Ctrl+Alt+D shortcut — opens (or creates)
the local-date YYYY-MM-DD.md and sends a file-opened IPC to the
renderer. Mirrors the Quick Note pattern in the same submenu (global
shortcut + menu entry, both pointing at openOrCreate).
Amit Haridas
- src/utils/csv-to-table.js — pure module. Auto-detects the delimiter
(tab wins over comma; comma wins over semicolon). RFC 4180-style
parsing handles quoted fields, escaped quotes (""), and CRLF. Cells
are escaped for markdown tables (\\ for backslashes, \| for pipes,
newlines stripped). Alignment: a column is right-aligned when every
non-empty cell matches a numeric pattern AND there are ≥2 rows or
≥1 multi-character cell (single-char numbers like "1" alone are
too ambiguous to call as numeric — could be labels). Single-row input
produces a header-only table (no fabricated "Column N" labels, no
body).
- src/editor/smart-paste.js — paste handler now tries CSV first (it
needs no async), falls back to the existing URL → title flow.
Tests (30 new, tests/csv-to-table.test.js):
- detectDelimiter: tab > comma > semicolon, empty/non-string safe
- parseRows: simple, RFC 4180 quoted, escaped quotes, CRLF, multi-line
- escapeCell: pipes, backslash-first escaping, newline strip, null/number
- csvToTable: simple CSV, single-row (header-only), TSV, empty input
- alignment: numeric detection (≥2 rows OR ≥1 multi-char cell), currency,
percentages, text columns stay left
- escaping inside cells (pipes, newlines)
- ragged-row padding
- looksLikeCsv: true for tab/comma multi-row, false for single row,
column-count mismatch, prose, very short input
Full suite: 72 suites, 861 tests, lint+format clean.
Amit Haridas
- src/main/UrlTitle.js — fetch a URL, return its <title>. Pure module
with injectable fetch for tests. Decodes named + numeric + hex entities
(AT&T, Café, —), strips the <title> tags, collapses
whitespace, caps the label at 200 chars. 5s timeout via AbortController,
2 MiB body cap to avoid OOM on big downloads, streaming reader with
overflow cancellation. Rejects non-http(s) URLs up front.
- src/main.js — IPC url-title:fetch proxies to fetchTitle.
- src/editor/smart-paste.js — CodeMirror 6 extension that detects
URL-only pastes (one URL, surrounded only by whitespace), inserts the
URL immediately, then async-rewrites the insertion range to
[Title](url) once the title arrives. Multi-line / prose pastes pass
through untouched.
- src/editor/codemirror-setup.js — accepts smartPasteFetcher option and
pushes the extension when provided.
- src/renderer.js — passes ipcRenderer.invoke('url-title:fetch') as
the fetcher.
- src/preload.js — url-title:fetch added to ALLOWED_SEND_CHANNELS.
- eslint.config.js — AbortController / TextDecoder / TextEncoder added
to globals (available in Node 20+ and Chromium).
Tests (34 new):
- tests/main/UrlTitle.test.js (24): isHttpUrl scheme filter, decodeTitle
named/numeric/hex entities + whitespace + non-string, extractTitleFromHtml
first match + case-insensitive + null fallback, fetchTitle success +
long-title cap + streaming body, all failure paths (non-http,
no-fetch, non-OK, wrong content-type, no <title>, network error,
body over maxBytes — including streaming overflow cancellation).
- tests/smart-paste.test.js (10): URL_ONLY_RE detection (bare URL,
path/query/fragment, whitespace padding, scheme rejection, embedded
rejection, empty/malformed), replacement format ([T](url), brackets
in title survive, query strings preserved).
Full suite: 71 suites, 831 tests, lint+format clean.
Amit Haridas
Hovering a footnote reference (rendered by marked-footnote as
<a data-footnote-ref href="#footnote-N">) now shows a small popover
with the footnote body text, matching the UX of Typora and Obsidian.
- src/renderer/footnote-preview.js — pure DOM module. Mounts a single
popover once per preview pane; mouseover delegates via Element.closest()
to the ref, lookups the matching <li id="footnote-N"> in the same pane,
strips the backref ↩, and positions above/below the cursor with edge
clamping. CSS.escape() fallback for non-browser environments.
- src/renderer.js — _renderPreview calls mountFootnotePreview once per
pane (gated by a dataset marker so re-renders don't accumulate
listeners).
- eslint.config.js — CSS + Element added to browser globals.
Tests (8 new, tests/footnote-preview.test.js): mount/unmount, delay +
timer behavior, mouseover/mouseout show/hide, dangling ref graceful
no-op, backref ↩ stripped from the displayed text, cancel-pending-show
when a new ref is hovered, idempotent remount guard.
Full suite: 69 suites, 797 tests, lint+format clean.
Amit Haridas
- src/utils/writing-stats.js — pure module: stripMarkdown() drops fenced
code blocks, inline code, image URLs, link URLs, headings, blockquote
markers, list bullets, and emphasis markers so the word count reflects
the prose a reader actually consumes (the convention used by Hemingway,
iA Writer). splitSentences / countSyllables use Flesch's standard
heuristics. computeStats returns wordCount, sentenceCount,
syllableCount, readingTimeMinutes (default 220 wpm),
fleschKincaidGrade, charCount.
- src/renderer.js — _updateStatusBar now calls computeStats and updates
three new status items: ~X min read, Grade N.N, and the existing Words
+ Chars counters use the stripped count.
- src/index.html — two new status items: #reading-time and #grade-level.
Tests (23 new, tests/writing-stats.test.js): markdown stripping (fenced,
inline, images, links, wikilinks, headings, lists, emphasis, HTML),
sentence splitter edge cases, syllable heuristic (short words, silent e,
empty), and computeStats with custom wpm + null-safe inputs.
Full suite: 68 suites, 789 tests, lint+format clean.
Amit Haridas
The workspace-search:query and doc-qa:ask IPC channels were reachable
from the renderer but had no UI. This commit wires them into a sidebar
panel that consumes both backends through a single input.
- src/sidebar/search-panel.js — one input, two modes:
Search (default): routes to workspace-search:query, returns file hits
Ask: routes to doc-qa:ask, returns chunk-level passages with offsets
Click a result → open the file (offset passed through for Q&A hits).
All dynamic content is escaped before innerHTML — hostile filenames
or snippets stay as text instead of becoming script/img nodes.
- src/renderer.js — registers the panel; reads the explorer's folder
input on each open so the search dir stays in sync.
- src/index.html — search sidebar icon (magnifier) next to the others.
Tests (18 new, tests/search-panel.test.js):
- mount + DOM structure
- Enter / click run → search() with query + dir
- result rendering (filePath, snippet, tag facet)
- onOpenFile receives (filePath, offset)
- Ask tab switches placeholder + routes to ask()
- Ask chunks carry +offset in the meta line
- empty query, no folder, search error → handled
- XSS: filename/snippet/tag with <script>, <img>, <unsafe> are escaped
- Escape clears, Clear button resets, host API (setDir/focus/clear)
Full suite: 67 suites, 766 tests, lint+format clean.
Amit Haridas
Three more features from the brainstorm menu, built on a shared search
algorithm so the codebase stays small.
- src/main/DailyNotes.js — Zettelkasten-style helper. One YYYY-MM-DD.md
per local date under <userData>/notes/daily/; loads skeleton from
<userData>/notes/templates/daily.md when present (built-in default
otherwise). openOrCreate never clobbers existing content.
- src/main/WorkspaceSearch.js — tag/wikilink-aware content search.
Pure module, injectable-IO tested. Query grammar: bare words,
#tag, @wikilink, "quoted phrases". Facets weight +3 each; prose
terms +1/occurrence capped at 5; edits within 7 days get a recency
nudge. Returns ranked results with snippets.
- src/main/DocQA.js — chunk-level Q&A wrapper over WorkspaceSearch.
cleanQuestion strips question words (what/how/why/...) and verb
noise (write/read/show/tell/...) so they don't drown the ranking.
Returns top-K passages instead of whole-file hits — multiple chunks
from the same file can appear in the answer.
- src/main.js — IPC: daily-notes:open-today, daily-notes:list,
workspace-search:query, doc-qa:ask. Path validation through the
existing validatePath gate; a global Ctrl+Alt+D shortcut creates
today's daily note from anywhere.
- src/preload.js — all four channels added to ALLOWED_SEND_CHANNELS.
Tests (56 new across the three modules):
- tests/main/DailyNotes.test.js (15): dateKey formatting, pathFor,
template load + {date}/{weekday} substitution, openOrCreate +
no-clobber, nested-dir creation, listExisting filtering,
isValidDir rejects NUL/non-string.
- tests/main/WorkspaceSearch.test.js (25): parseQuery grammar,
hasTag/hasWikilink word boundaries, scoreDocument scoring,
per-term spam cap, recency nudge, search ranking + limit +
empty-query short-circuit, bad-input safety.
- tests/main/DocQA.test.js (16): cleanQuestion stripping + facet
preservation, chunkDocument paragraph + hard-split, ask()
top-K, recency tiebreaker, missing-files fallback.
Full suite: 748 tests pass, 66 suites, lint+format clean.
Amit Haridas
VersionHistory snapshots the previous content on every explicit save — an
unsaved buffer is still lost on crash. AutosaveBuffer fills that gap.
- src/main/AutosaveBuffer.js — pure module mirroring VersionHistory's
injectable-IO pattern; one blob per doc path under
<userData>/autosave/by-path/<sha1>/recovery.md + meta.json. No history
(VersionHistory owns that) — just the latest dirty buffer.
- src/main.js — IPC channels autosave:write/read/clear/list; real paths
go through validatePath, synthetic 'untitled-tab-<id>' keys skip it.
- src/preload.js — added the four channels to ALLOWED_SEND_CHANNELS.
- src/renderer/autosave-client.js — debounced (2s) flush per tab +
periodic safety net (10s max age) + dirty-write retry on failure.
- src/renderer.js — register on tab create, unregister on close,
notifyChange piggybacks on performAutoSave's existing dirty-check,
clearForDocPath after a successful save, showAutosaveRecoveryBanner
on startup listing pending recoveries with Restore/Dismiss.
Tests (39 new):
- tests/main/AutosaveBuffer.test.js (19): round-trip, overwrite, isolation,
unicode/emoji, empty content, null coercion, ENOENT vs corrupt meta,
list ordering, corrupt-sibling skip, input validation, sha1 storage.
- tests/autosave-client.test.js (11): debounce, flushNow bypass,
no-path skip, clearForDocPath, list proxy, IPC error fallback,
unregister tear-down, failure-retry, periodic flush, idempotency.
Full suite: 692 tests pass, 63 suites, lint+format clean.
Amit Haridas
The renderer wires KaTeX via initMathSupport() and calls
window.renderMathInElement(...) inside _renderPreview() — there were no
tests pinning any of that contract. This commit adds tests/math-rendering.test.js:
- inline ($, escaped \() and display ($$, escaped \[) delimiters all
render valid TeX
- mixed prose + math preserves siblings (headings, lists, etc.)
- invalid LaTeX degrades gracefully under default throwOnError:false, so
the renderer's outer try/catch contract stays load-bearing
- dollar-heavy prose doesn't throw (the renderer's auto-render call must
be robust to anything in the doc)
- bundle wiring: katex.render() and renderMathInElement are exported,
katex.min.css ships in assets/, and index.html references it without a
CDN (the old jsdelivr link was removed in a prior hardening pass)
Amit Haridas
@ cantoo/pdf-lib 2.9.1 was already the dep in use; the encryption plumbing
and probe were already in place from the prior hardening pass. This commit:
- adds a test confirming executeOperation('permissions', ...) routes through
pdfSetPermissions and produces an encrypted PDF unlocked by the owner
password (mirrors the existing 'encrypt' route coverage)
- updates docs/superpowers/plans/2026-08-23-security-assessment-summary.md
to mark deferred risk D1 as resolved (the honest-failure message remains
as a fail-closed net for any future library regression)
35 PDF ops tests pass; lint/format clean.
Amit Haridas
Export themes:
- Six presets in the export dialog (basic + advanced modes) for PDF/DOCX:
Default, Modern, Classic, Sepia, Minimal, Elegant
- PDF: LaTeX header (xcolor/titlesec) recolors headings, adds section
rules and colored links — core-TeX packages only, hex-literal only
(no injection surface)
- DOCX: styles.xml surgery recolors Heading1-6/Title/Subtitle/Hyperlink
and swaps heading/body fonts; verified end-to-end against a real
pandoc-produced docx
- Themes ride along in export presets (unknown ids fall back to Default)
Windows CI fixes:
- pdfjs standardFontDataUrl now a file:// URL (backslash paths failed
pdfjs's trailing-slash validation, breaking extractText/extractImages)
- sharp temp cleanup EPERM retries; path-separator assertions; pdfjs
test timeouts raised; batch suite testTimeout 30s
648/648 tests green; 4.7.1 linux+win artifacts rebuilt.
pdfjs validates standardFontDataUrl as a URL ending in a forward slash —
our raw path with a trailing path.sep is invalid on Windows (C:\...\),
failing extractText/extractImages (and every test that verifies through
them) with 'Invalid factory url: must include trailing slash'. Linux and
macOS passed only because / is also a valid URL slash. Convert with
pathToFileURL() so every platform sends file:///.../standard_fonts/.
Also escape path.sep in PDFBatchOperations' sanitizer test regex — a bare
backslash made new RegExp() a syntax error on Windows.
The Windows runner's git checkout rewrote assets/fonts/FiraCode-LICENSE.txt
line endings (autocrlf), changing its bytes and tripping the SHA-256 pin in
download-tools.js. A .gitattributes marks the license (-text) and all font/
image binaries as never-normalized so every platform checks out identical
bytes.
Branding:
- All app icons regenerated from the new vector brand kit (M↓ mark):
icon.png/icon@2x (app + packaging), favicon.png, tray-icon.png, and the
full assets/icons/ size set — generate-icons.js now rasterizes
app-icon.svg directly (docico1.png removed)
- index.html gets proper favicon/apple-touch links from the kit
- Welcome tab hero shows the new mark; README gets the horizontal wordmark
- assets/logo.png (ConcreteInfo) intentionally untouched
CI release fixes (win/mac jobs were failing):
- FiraCode download moved from moving raw/master URLs (hash drifted
upstream, tripping the pin) to the immutable 6.2 release asset; repo
fonts updated to the pinned 6.2 bits
- macOS pandoc extractor locates the binary in the archive instead of
assuming a bin/ layout that the macOS zip doesn't have
Stray upload archive (markdown-converter-assets (1).zip) excluded.
- electron-builder config moves to electron-builder.config.js so markitdown
bundling is conditional per platform (PyInstaller only builds for the host
OS; a missing binary now logs a warning and ships without it instead of
failing the build); package.json static build section removed, all npm
scripts pointed at the config; third-party-licenses/ added to packaged files
- FIX (pre-existing): packaged apps looked for bundled pandoc in
resources/bin, but extraFiles land next to the executable (Contents/ on
macOS) — packaged builds silently fell back to system pandoc since 4.5.
New bundledToolDir() resolves the real location for pandoc + markitdown
- download-tools.js pins the win32 pandoc.exe SHA-256 (fetched + verified)
- sharp packaging test accepts sharp 0.35's versioned binding filename
- release.yml: bundle-markitdown step (best-effort) on every OS and a new
macOS job; release aggregates linux+windows+macos artifacts
Local release artifacts built and verified (dist/):
- MarkdownConverter-4.7.0.AppImage (363MB, pandoc+markitdown bundled,
packaged app boots clean, tools resolve at the fixed path)
- markdown-converter_4.7.0_amd64.deb (293MB)
- MarkdownConverter-Setup-4.7.0.exe (223MB), portable exe (223MB), zip
(305MB) — pandoc bundled; markitdown omitted (cannot cross-build),
legal docs verified inside app.asar
637/637 tests green; lint clean.
Bundle (v4.7.0):
- MarkItDown frozen to a single ~75MB per-platform binary (PyInstaller via
npm run bundle:markitdown; ML extras excluded) — built and verified
locally: HTML/XLSX/PDF conversions pass through the bundled binary, and
the app resolves bin/linux/markitdown first at runtime
- Packaging copies bundled markitdown alongside Pandoc for win/mac/linux;
FFmpeg/sharp/KaTeX/fonts were already bundled
Legal artifacts:
- THIRD-PARTY-NOTICES.md: complete license inventory of everything
distributed (binaries, npm runtime deps, fonts, embedded Python packages)
- SOURCES.md: GPL §3(b) written source offers for Pandoc 3.9.0.2,
ffmpeg-static's GPL build, and the PyInstaller bootloader; LGPL relinking
note for libvips
- third-party-licenses/: canonical GPL-2.0, LGPL-2.1, MPL-2.0, Apache-2.0,
OFL-1.1, PSF-Python texts
- Help > Third-Party Notices & Licenses: in-app viewer for both documents
- README: 'Bundled Dependencies, Legal Notices & Credits' section
Hardening:
- download-tools.js now SHA-256 pins every artifact, verifies after
download AND against the cache on every run, and hard-fails on mismatch
(closes security finding D6)
Large tools intentionally not bundled (documented): LibreOffice,
MiKTeX/TeX Live, ImageMagick, PlantUML+JRE, Calibre.
637/637 tests green; lint clean; clean boot; bundled binary verified.
- File → Import with MarkItDown (Any Format)…: PDF, DOCX, PPTX, XLSX,
Outlook .msg/.eml, EPUB, images, CSV/JSON/XML, ZIP (audio/OCR via the
[all] extras) — verified live against HTML, XLSX (our own exporter's
output), and PDF fixtures
- Command auto-resolution with caching: markitdown binary → python -m
markitdown → python3 -m markitdown
- SEC-1 argv discipline (execFile only, user paths never through a shell),
50MB cap, 120s timeout, sanitized errors that surface markitdown's own
"pip install 'markitdown[pdf]'" hints for missing format extras
- Output lands next to the source as <name>.md (numeric suffix, never
overwrites) and opens in a new tab; markitdown:available/convert IPC
allowlisted for renderer flows
- Help → Dependencies lists MarkItDown; README/UPDATES updated (v4.6.1)
12 new tests (629 green); lint clean; clean app boot
Anthropic-compatible provider:
- New 'anthropic-compatible' option for any base URL speaking the Anthropic
messages schema (LiteLLM proxies, Bedrock gateways, local servers)
- Sends x-api-key AND Bearer auth when a key is set (gateway-friendly,
harmless for the official API); keyless proxies supported
- Tolerates base URLs with or without a trailing /v1 segment
- Settings modal, manifest, and provider docs updated
Runtime bug fixes found by booting the app (run-to-verify pass):
- PDF editor: File > Open PDF sends operation=null which matched no switch
case and crashed on getElementById(undefined); now defaults to the merge
section
- backlinks-panel: wrong require depth (../../utils -> ../utils) threw at
panel registration time
- writing-studio stack was written against a synchronous settings backend but
the real one is IPC-backed: GoalTracker/SnapshotManager/ProjectManager and
all four panels now await; JSON.parse(Promise) crashes eliminated
- manuscript panel used window.prompt (unavailable in Electron); replaced
with an inline dialog
- collaboration comment-store/save-load made async to match its IPC IO
617/617 tests green; 4 consecutive clean app boots (no uncaught errors)
- AI Assistant plugin: multi-provider chat (OpenAI/Anthropic/Ollama/LM Studio),
summarize/improve/translate commands, proofread via ai:analyze; calls
proxied through main so API keys stay out of the renderer
- Collaboration plugin: anchor-based comments in .comments/ sidecars with
drift detection and F8 navigation
- Local knowledge base: [[wiki-links]] with click-to-create + Backlinks panel
- Crash recovery: debounced session snapshots with restore prompt on launch
- Version history: pre-save snapshots, History panel with restore/diff/delete
- Real PDF encryption: swap pdf-lib for @cantoo/pdf-lib (probe-driven UI)
- XLSX export (native workbooks via JSZip), ODT headers/footers + page size
- Offline KaTeX (bundled CSS+fonts), local-first PlantUML rendering
- Editor: vim mode toggle, snippet Tab-expansion, zen word-goal setter,
writing heatmap, writing-studio panels wired with rail icons
- Quick Note global scratchpad (Ctrl+Alt+Q), markdownconverter:// deep links,
REPL first-run confirmation
- Fix: Ctrl+Shift+P collision, pandoc converter availability check, CLI
dangling --css/--reference-doc flags, dead converter button
8 new test suites; 613 tests green; lint clean
build.asarUnpack only claimed node_modules/sharp/**, so electron-builder
pruned the @img/sharp-* optionalDependencies: the asar kept 34 pure-JS
@img entries while the bundled libvips shared libraries never shipped.
At boot sharp's loader fell back to a system-libvips-linked binding and
dlopen failed, crashing the main process. Claim the prebuilt packages
explicitly (@img/** and @napi-rs/**) per the sharp+electron-builder
recipe, and guard the built output with a packaging regression test.
Amit Haridas
A missing/pruned @img/sharp-* binding made the top-level require('sharp')
crash src/main.js at startup, killing the packaged app before any window.
Load sharp through a cached lazy getter instead; when the native module
cannot load, executeOperation resolves the honest failure shape
{ success: false, error: 'Image operations unavailable: <sanitized>' }
(free of absolute paths), mirroring PDFOperations' Task-27 precedent.
Amit Haridas
electron-builder's .snap (Squashfs) artifact in dist/ registered as an
obsolete Jest snapshot file, failing the suite exit code despite all
516 tests passing. modulePathIgnorePatterns keeps the snapshot scanner
out of build output.
Amit Haridas
Final-review nit: the hardcoded-list rationale covers the dialog path
only; the drop stands on the trusted-argv precedent for --convert-to.
Amit Haridas
The interval split mode looped for (i = 0; i < totalPages; i += interval),
which spins forever when interval <= 0. Both the single-file dialog and the
batch dialog can reach it (the batch dialog's validateOperationData only
checks truthiness, so -1 passes). Guard at the source in the main process:
reject non-positive or non-integer intervals before the loop, protecting
both paths and any future caller.
Amit Haridas
The get-pdf-form-fields IPC pair (handler in main.js, renderer invoke and
event.reply('pdf-form-fields')) was missing from ALLOWED_SEND_CHANNELS and
ALLOWED_RECEIVE_CHANNELS. Under the planned preload migration an unlisted
channel is silently blocked, so the form-field fill/flatten feature would
break once the main window stops using the inline shim. Placed adjacent to
the sibling get-pdf-page-count/pdf-page-count pair it was modeled on.
Amit Haridas
Task-20-review finding: File.path removed in Electron 32, app on ^41.1.1,
~15 renderer file-picker sites read it and get undefined at runtime.
Amit Haridas
Replaces the two native OS dialogs used to configure the DOCX "Enhanced"
export template (an open-file picker + a message-box question) with a
single in-app modal that shows the currently active template state, per
Task 18's original audit finding that this state was invisible until a
user thought to reopen the menu. Consolidates the "Select Word
Template..."/"Template Settings..." menu items into one "Word Template
Settings..." entry wired to the new dialog; Browse still uses the native
file picker since there is genuinely no bundled folder of templates to
enumerate (confirmed by investigation — see task-18-report.md).
Also fixes a related dangling-reference bug: WordTemplateExporter's
hardcoded default template path (word_template.docx) was deleted from
the repo in an earlier commit, but the code still tried to read it and
threw ENOENT whenever no custom template was selected. convert() now
degrades gracefully by generating a minimal, valid DOCX shell (styles +
numbering matching what markdownToWordXml() already references) instead
of crashing, and the new dialog surfaces this state honestly ("using
default formatting, no default template is bundled") rather than
implying a working default exists.
Out of scope, per explicit instruction: bundling fabricated starter
.docx templates to populate a literal multi-item gallery (rejected as
disproportionate/fake-content scope), and an EPUB template gallery (no
EPUB template mechanism exists anywhere in this codebase to build one
for).
Amit Haridas
Task 17's three new IPC channels (plugin-export-formats-registered,
run-plugin-export-format, plugin-export-format-result) were missing
from preload.js's ALLOWED_SEND_CHANNELS/ALLOWED_RECEIVE_CHANNELS,
breaking the established convention that the allowlist is the
authoritative registry of every valid channel regardless of whether
it's accessed via window.electronAPI or raw ipcRenderer (see
toggle-sidebar-panel, set-current-file, save-recent-files).
Amit Haridas
Add context.formats.registerExportFormat(id, opts) to PluginContext,
backed by a new FormatRegistry (mirrors PluginRegistry's Map-based
shape). Plugins register namespaced (${pluginId}:${id}) export
formats with a label/extension/handler; the writing-studio built-in
plugin registers a trivial "sprint-summary" .txt export as a
worked example.
The plugin system lives entirely in the renderer process while the
Export menu is built in main.js, so wiring formats into the menu
required a small IPC round-trip: renderer sends format metadata to
main after plugin load (main rebuilds the menu via the already-
idempotent createMenu()), and a menu click sends the resolved save
path back to the renderer, which is the only process holding the
plugin's handler function.
Amit Haridas
Adds pdfGetFormFields (lists AcroForm fields with name/type/value) and
pdfFillForm (fills text fields by name, optionally flattens) to
PDFOperations.js, dispatched via 'formFields'/'fillForm' in
executeOperation. pdfFillForm skips unknown/non-text fields per-field
(logs + continues) rather than failing the whole batch, matching the
partial-success precedent set by pdfExtractImages.
Wires a "Fill Form" entry into the PDF editor dialog: selecting a PDF
fetches its fields via a new get-pdf-form-fields/pdf-form-fields IPC
round trip and renders one text input per field, plus a flatten
checkbox, following the same structure as the crop/pageNumbers dialogs.
Amit Haridas
Adds four new PDFOperations: pdfExtractText (pdfjs-dist getTextContent),
pdfAddPageNumbers (reuses pdfWatermark's position-mapping logic, extracted
into a shared resolvePosition helper), pdfCrop (page.setCropBox against the
existing MediaBox), and pdfExtractImages (pdfjs-dist operator list +
paintImageXObject + sharp). Wired into executeOperation's switch and the PDF
editor dialog UI (4 new sections/toolbar buttons/menu items) with no new IPC
channel needed.
pdfjs-dist v5 is ESM-only, so it's loaded via dynamic import() of its
Node-friendly legacy build; Jest needs --experimental-vm-modules to support
that, so the test scripts now set NODE_OPTIONS accordingly via cross-env.
Amit Haridas
Extends GitOperations.js with diff/branches/checkoutBranch/push/pull,
wires the 5 new IPC handlers in main.js (reusing the existing dir
resolution), whitelists the new channels in preload.js, and fixes the
Git sidebar panel's previously dead _gitDiff callback by wiring up a
diff view, branch list/create/checkout UI, and push/pull buttons.
Resolves Task 5, which deferred this work to this task.
Amit Haridas
Reviewer follow-up on Task 12: the task's own title/brief called for
batch support and no later task in the plan picks it up, so this closes
that gap. Adds a "Single File" / "Batch Folder" mode toggle to the
existing media-operations-dialog.js; batch mode swaps the per-file
input/output fields for an Input Folder + "Include subfolders" +
Output Folder trio while keeping every other parameter (width/height/
quality/angle/startTime/duration/crf/fps/format/fit) applied uniformly
to every matching file. Disabled for audio "Merge", which combines many
inputs into one output and doesn't fit a per-file batch model.
main.js: adds collectFilesByExtension() (src/main/collectFilesByExtension.js,
unit tested), a generalization of the inline collectFiles() closure inside
ipcMain.on('universal-convert-batch', ...) to match a set of extensions
instead of one format. runMediaBatchOperation() loops
ImageOperations/AudioOperations/VideoOperations.executeOperation() over
the matched files, reporting per-file progress via new
'media-batch-progress' events and a final 'media-batch-complete' event,
then shows a "Batch Conversion Complete" dialog.showMessageBox with
completed/failed counts, mirroring performBatchConversion()'s pattern.
Wired via three new ipcMain.on handlers: batch-image-operation,
batch-audio-operation, batch-video-operation.
preload.js: whitelists the three new send channels and the two new
receive channels (media-batch-progress, media-batch-complete).
Adds Tools > Image/Audio/Video Tools... menu items and a single dynamic
renderer dialog (src/renderer/media-operations-dialog.js) that lets the
user pick a media-kind-scoped operation, fill in its operation-specific
fields, and invoke process-image-operation/process-audio-operation/
process-video-operation (Tasks 9-11's backends). File selection reuses
the existing <input type="file"> + file.path convention; the one folder
picker need (video frame extraction) reuses the existing generic
select-folder/folder-selected IPC channels, so no new IPC handler was
required. Also removes the three dead electronAPI.image/audio/video
convenience blocks from preload.js (stale pre-Task-9-11 channel names,
unused everywhere).
Add src/main/VideoOperations.js with pure argument-builder functions
(buildConvertArgs, buildCompressArgs, buildTrimArgs, buildFramesArgs,
buildGifArgs) and a single executeOperation entry point that spawns
ffmpeg via dependency-injected execFileFn, mirroring AudioOperations.js.
Wire ipcMain.handle('process-video-operation', ...) in main.js using
getFFmpegPath() and sanitizeErrorMessage(). Update preload.js's
ALLOWED_SEND_CHANNELS: remove 6 stale video-* channel names, add
process-video-operation.
Amit Haridas
Adds AudioOperations.js with pure argument builders (convert/trim/extract/merge)
plus one executeOperation that spawns ffmpeg via a dependency-injected execFileFn,
so tests never invoke a real binary. Wires process-audio-operation in main.js and
updates preload.js's ALLOWED_SEND_CHANNELS to replace the 5 stale audio-* entries.
Amit Haridas
Three lines in tests/main/ImageOperations.test.js (copied verbatim
from the task brief's sample) exceeded the project's 100-char width,
failing npm run format:check. Ran npm run format to auto-fix; no
behavioral change.
Amit Haridas
Add src/main/ImageOperations.js (convert/resize/compress/rotate via
sharp), modeled on PDFOperations.js's executeOperation dispatcher.
Wire ipcMain.handle('process-image-operation', ...) in main.js using
sanitizeErrorMessage() on error paths, and replace the 5 stale/unused
image-* channel names in preload.js's ALLOWED_SEND_CHANNELS with
process-image-operation + select-image-folder (mirroring
select-pdf-folder for a later batch-UI task).
Amit Haridas
- Add jszip (^3.10.1) to dependencies; keep version-pinned in overrides
- Move sharp (^0.34.3) from devDependencies to dependencies for Phase B runtime use
- Add node_modules/sharp/** to build.asarUnpack so native bindings are not packed
Amit Haridas
Reviewer caught that the new View > Monospace Font menu channel was
missing from preload.js's ALLOWED_RECEIVE_CHANNELS, the sole gap among
29 raw ipcRenderer.on(...) channels used in renderer.js. Add it under
the existing Font section for consistency with adjust-font-size.
Amit Haridas
Add a View > Monospace Font menu (font family radio + ligatures
checkbox) — the app's existing reachable UI surface for this class of
preference (mirrors Theme/Font Size/Spell Check). The menu sends the
change to the renderer, which persists it via the already-working
ipcMain.handle('set-monospace-settings', ...) and applies it live via
the same applyMonospaceClasses() used on initial load.
Amit Haridas
Extract the sidebar Templates panel's inline load-into-new-tab callback into
a shared loadTemplateIntoNewTab() function, and add the missing
ipcRenderer.on('load-template-menu', ...) listener so the File > New from
Template submenu (which already sends this IPC event, already whitelisted in
preload.js) actually loads the selected template.
Amit Haridas
Extract the recent-files.json deletion logic into a standalone
clearRecentFilesOnDisk() function and call it from both the menu
click handler and the ipcMain.on handler. Previously the menu sent
the message in the wrong direction (main→renderer instead of
renderer→main), causing the feature to silently no-op. Both paths now
use the same function and send the correct 'recent-files-cleared'
notification to keep the renderer in sync.
Amit Haridas
Monospace font embedding feature release. Adds bundled JetBrains Mono +
Fira Code TTFs (asarUnpack), preview/print font picker with ligatures
toggle, and embedded fonts in PDF (xelatex fontspec), DOCX (OOXML
surgery), EPUB (--epub-embed-font + OPF manifest), and HTML (sidecar
CSS with base64 data URI).
Exercises the real export pipeline against the bundled JetBrains Mono TTFs:
- PdfFontHeader emits valid xelatex fontspec with correct family + Ligatures=NoCommon
- ExportCss.build emits @font-face with base64 data URI
- DocxFontEmbedder injects TTFs into pandoc-produced DOCX (word/fontTable.xml + word/fonts/)
- EpubFontEmbedder.patchManifest adds TTF entry to OPF <manifest>
- HTML export links the sidecar CSS which embeds the font
Run with: node tests/smoke-e2e-monospace.js
Renderer already calls window.electronAPI.invoke('get-monospace-settings')
to apply body classes; this wires up the channel allowlist and main-process
handlers so the IPC actually returns the active monospace settings and
persists updates.
Embeds regular + bold TTF of the active monospace family into pandoc's
DOCX output. ODT uses pandoc's built-in font handling; RTF has no font
embedding capability (documented limitation).
Replaces -V monofont=Consolas with a generated xelatex/lualatex header
that fontspec-loads the bundled JetBrains Mono or Fira Code TTF. Adds
a cached settings reader with proper invalidation on store.set, and
reorders fallback engines to prefer lualatex (fontspec-capable) before
pdflatex.
Inlines @font-face as base64 data URI so the iframe srcdoc can render
JetBrains Mono / Fira Code without depending on the parent window's
loaded @font-face sets. Reads family + ligature state from the
renderer-wide cache populated by applyMonospaceClasses().
Amit Haridas
Bundles JetBrains Mono + Fira Code TTFs in assets/fonts/. Embeds them into
DOCX (jszip), passes path via xelatex fontspec for PDF, base64-injects
@font-face for HTML/EPUB. Replaces Consolas (Windows-only) and Google Fonts
CDN load in the ASCII generator window.
Adds user-pickable monospace family + ligature toggle (default JBM, no
ligatures) for ASCII column alignment.
Closes: N/A
Refs: docs/superpowers/specs/2026-06-30-monospace-font-embedding-design.md
Amit Haridas
- Make the HTML preprocessor code-block and inline-code aware so code
examples containing <style> / <div> / comments are preserved.
- Strip all raw <div> tags (not just alignment attributes) to avoid
malformed output from unmatched closing tags.
- Handle uppercase tags and single/unquoted attributes.
- Create temporary DOCX input files inside private mkdtemp directories
instead of predictable names in the shared temp directory.
- Wrap batch DOCX preprocessing in try/catch so one unreadable file
does not abort the entire batch.
- Add regression tests for the edge cases above.
Pre-process markdown before Word/DOCX export to remove raw HTML artifacts
(<style> blocks, HTML comments, and <div align=...> tags) that were
visible in the generated document. Applies to single and batch DOCX exports
via both Pandoc and WordTemplateExporter paths.
- Normalize pandoc command parsing with path.basename() to support bundled binary paths
- Use bundled pandoc binary in convertWithPandoc instead of relying on PATH
- Forward includeSubfolders checkbox state from renderer to main process
- Add pandoc availability check before batch conversion
- Re-enable Start button when batch conversion completes
- Clean up obsolete dist build artifact causing test snapshot warning
- Bump version to 4.4.4
Documents project architecture, Pandoc dependency resolution, build
pipeline (electron-builder, no bundler), security model notes
(contextIsolation: false on this branch), and development commands
extracted from actual package.json and source.
Amit Haridas
This will help identify whether the issue is:
1. marked.parse returning a Promise instead of string
2. DOMPurify.sanitize failing
3. The preview element not existing
4. Libraries not being loaded
Amit Haridas
- Add missing updateUI() call at end of openFile() to set .active class
on tab content. Without this, the CSS rule .tab-content:not(.active)
{ display: none } kept newly opened files invisible.
- Add diagnostic logging to file-opened IPC handler and openFile()
to trace future file loading issues.
- Add diagnostic logging to openFileFromPath() in main process.
Amit Haridas
window.ModalManager was set unconditionally, causing "Identifier
'ModalManager' has already been declared" when script tag in HTML
also loaded ModalManager before renderer.js required it.
Now checks !window.ModalManager before setting.
Amit Haridas
require('dompurify') returns a factory function, not a sanitizer
instance. Calling .sanitize() on the factory threw a TypeError,
which was caught by the preview renderer's try-catch and displayed
a generic "Error rendering preview" message. Fix by invoking the
factory with the renderer's window object.
Amit Haridas
Restrict Electron permission requests to only clipboard operations.
Deny: camera, microphone, geolocation, notifications, and all other
permissions by default.
Implements setPermissionRequestHandler on web-contents-created event
to enforce security policy early in the app lifecycle.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add get-app-version IPC handler in main.js (returns app.getVersion())
- Expose electronAPI.getAppVersion() in preload.js
- index.html: replace hardcoded v4.2.0 span with dynamic population
from getAppVersion() in DOMContentLoaded
- welcome.js: accept appVersion param instead of hardcoded 4.1.0
- renderer.js: pass live version to createWelcomeContent()
- main.js about screen: use app.getVersion() instead of hardcoded 4.1.0
- Update stale @version 4.1.0 JSDoc comments to 4.3.0
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
electron-builder detects git tags in CI and tries to auto-publish to
GitHub, failing with 'GH_TOKEN not set'. We handle the release
separately via softprops/action-gh-release, so suppress auto-publish.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add build:linux-ci script (deb + AppImage, no snap — snapcraft not
available on ubuntu-latest runners without extra setup)
- Switch release.yml linux build to npm run build:linux-ci
- release job: if: always() so Windows artifacts still get released
even if linux build fails
- Download artifact steps: continue-on-error so missing platform
doesn't block GitHub Release creation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove rpm from linux build targets (rpmbuild not available locally)
CI can add it back with apt-get if needed, but pandoc/ffmpeg are now
bundled so the rpm depends were incorrect anyway
- Remove rpmbuild apt install step from release.yml (not needed)
- Remove pandoc/ffmpeg from deb depends — they are now bundled binaries
- Keep imagemagick and libreoffice-common in deb depends (not bundled)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove package-lock.json from .gitignore so npm ci works in CI
- Refactor main.js: delegate PDF ops to src/main/PDFOperations.js,
git ops to src/main/GitOperations.js
- getPandocPath(): use bundled binary from resources/bin/ when packaged,
fall back to dev bin/ or system pandoc in development
- getFFmpegPath(): use ffmpeg-static (asarUnpack) when packaged
- Install ffmpeg-static (v5.3.0, bundled 76MB binary)
- Add scripts/download-tools.js to fetch pandoc binary at build time
(idempotent, runs on CI before electron-builder)
- electron-builder: add asarUnpack for ffmpeg-static, extraFiles for
pandoc binary per platform (linux + win32)
- release.yml: switch build-windows to windows-latest runner with native
NSIS support; add cert decode step; add download-tools step for both
linux and windows jobs
- Fix lint error: hoist outlinePanelContainer to module scope so
TabManager methods can reference it without no-undef errors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add plugin system bootstrap in renderer.js after sidebar/commands init
- Wire status bar DOM insertion, editor API, and IPC adapters
- Add plugin-settings:get/set IPC channels to preload allowlist
- Add IPC handlers in main process using existing settings store
- Fix eqeqeq warning in EventBus.hasHandler
Amit Haridas
- PluginContext: scoped API with crash-safe command wrappers
- PluginRegistry: lifecycle management with graceful init failure
- SettingsStore: plugin-scoped key/value via IPC backend
- Export hooks: pre/post hooks on registry for cross-plugin integration
Amit Haridas
9 tasks across 8 chunks, strict TDD:
- EventBus with crash-safe handlers
- PluginAPI base class
- PluginLoader with manifest validation
- PluginContext with scoped API
- PluginRegistry with lifecycle management
- SettingsStore for plugin-scoped settings
- Export hooks integration
- Sample plugin + renderer wiring
Amit Haridas
- GGUF GPU: child process isolation with crash detection/restart
- Event bus: versioned payload schemas for all events
- Plugin sandbox: 5s handler timeout, IPC delegation for heavy ops
- AI streaming: full lifecycle with requestId, cancel, heartbeat, orphan cleanup
- Comment anchors: context-based positioning (not byte offsets) with re-anchor on file change
- Cross-plugin: capability discovery, 30s timeout, graceful degradation
- Bundle size: GPU variants as lazy downloads, not bundled by default
- Command uniqueness: registry rejects duplicates at load time
Amit Haridas
- Remove signAndEditExecutable:false so code signing works properly
- Add legalTrademarks and copyright metadata to build config
- Add publisherName via build.copyright (embedded in PE resources)
- Create scripts/create-selfsigned-cert.ps1 for local dev signing
- Update release.yml: build on windows-latest runner (not Wine),
auto-sign when CSC_LINK_BASE64/CSC_KEY_PASSWORD secrets present,
fall back to unsigned otherwise
- Add lint step to ci.yml (Phase 4.3 plan gap)
- Add .vscode/launch.json debug configs (Phase 4.3 plan gap)
- Fix .gitignore: exclude *.pfx/*.p12 cert files, track launch.json,
fix concatenated agents.md/coverage/ lines
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Zen mode hides all chrome and centers the editor with typewriter
scrolling, line dimming, and a floating word count HUD.
Toggle with F11, exit with Escape.
Amit Haridas
- Bump version to 4.1.0 in package.json and index.html
- Add build:local script for combined Linux + Windows local builds
- Add CI workflow: runs tests on push/PR to master
- Add Release workflow: tag-triggered (v*), parallel Linux + Windows
builds, publishes all packages to GitHub Releases
Amit Haridas
- Set backdrop z-index:0 and content z-index:1 to fix backdrop covering
modal content within the stacking context
- Force reflow between removing hidden and adding open class so CSS
opacity transition fires correctly
- Add transitionend listener + setTimeout fallback to restore hidden
class after close animation completes
- Override flex:1 on modal footer buttons to prevent full-width stretch
- Add min-width to modal size variants for consistent sizing
- Add 23 tests covering open/close lifecycle, keyboard, and destroy
Amit Haridas
Convert 10 dialogs from old classes (.export-dialog, .batch-dialog, .find-dialog)
to the new unified .modal structure with proper accessibility attributes.
Changes:
- find-dialog: small modal with find/replace controls
- export-dialog: large modal with export options
- print-preview-overlay: full-size modal for print preview
- table-generator-dialog: default modal for table creation
- ascii-art-dialog: large modal for ASCII art generation
- universal-converter-dialog: large modal for file conversion
- batch-dialog: large modal for batch processing
- pdf-editor-dialog: full-size modal for PDF editing
- header-footer-dialog: default modal for header/footer config
- field-picker-dialog: small modal for field selection
All dialogs now include:
- role="dialog" and aria-modal="true" for accessibility
- aria-labelledby pointing to title element
- .modal-backdrop with data-close attribute
- .modal-content with appropriate size class
- .modal-header with title and close button
- .modal-body for content
- .modal-footer with action buttons
Amit Haridas
- Create adapters/types.js with comprehensive type definitions
- Create adapters/electron/fs.js for file system operations
- Prepare structure for future migration to Tauri/Flutter
This abstraction layer makes future platform migration easier
and enables better testing with mock adapters.
Amit Haridas
- Add previewDebounceTimers map to track debounce timers per tab
- Add updatePreview(tabId, immediate) with optional immediate flag
- Debounce preview updates during typing (300ms delay)
- Use immediate=true for tab switches and file loads
- Refactor _renderPreview as internal method
This significantly improves editor responsiveness when typing
in large markdown files.
Amit Haridas
- Add tab type system ('markdown' and 'pdf')
- Create PDF tabs with their own state (page, zoom, rotation)
- Update closeTab to properly clean up PDF resources
- Update updateUI to handle PDF tabs (hide toolbar, etc.)
- Add visual indicators for PDF tabs in tab bar
- Add CSS styles for PDF tab containers
Fixes: PDF and markdown multitab function not working
Amit Haridas
- Add custom EditorView.theme for CodeMirror 6 with JetBrains Mono
- Update .editor-textarea and #editor font-family to prioritize JetBrains Mono
- Update preview code blocks (#preview code, .preview-content code) to use JetBrains Mono
- Ensures consistent monospace font across editor source and markdown rendering
Amit Haridas
CSS improvements:
- Standardize dark theme selectors to body[class*="dark"] pattern
- This ensures all dark themes (theme-dark, theme-dracula, etc.)
receive consistent styling
- Add semantic color variables (--text-primary, --bg-primary, etc.)
- Replace hardcoded colors with CSS variables in:
- Tab bar component
- Toolbar separator
- Pane resizer
- Status bar
- Add fallback values for backward compatibility
This improves maintainability and makes theming more consistent.
Amit Haridas
Accessibility improvements:
- Add global focus-visible styles for keyboard navigation
- Add focus-visible for sidebar panel close button
- Add skip-link styles for screen reader users
- Add .sr-only class for visually hidden content
- Add prefers-reduced-motion support for users sensitive to motion
- Add prefers-contrast: high support for high contrast mode
Amit Haridas
CSS improvements:
- Remove duplicate CSS reset from styles-modern.css
- Add focus-visible styles for sidebar icons
- Add error/loading state components (skeleton, spinner, messages)
- Add success, warning, info message components
- Add dark theme support for new components
Code quality:
- Replace inline error style with CSS class in renderer.js
Amit Haridas
- Fix duplicate editorContainer declaration in renderer.js
- Add missing browser globals to eslint config (prompt, FileReader, etc.)
- Add global object for Jest test setup
- Replace path.basename with portable string split in logo preview
Add unit tests for sidebar manager, command palette, print preview,
main process utilities, and markdown extensions. Update jest config
to exclude untestable Electron-specific files from coverage and
raise coverage thresholds.
Refactor inline command palette into a proper CommandPalette class
with search highlighting, keyboard navigation, and overlay UI.
Register all app actions including formatting, file ops, and sidebar
toggles.
- Grouped toolbar buttons into logical sections: Format, Structure, Insert, View
- Added toolbar-group CSS class for visual grouping
- Updated button titles with keyboard shortcut hints
- Enhanced status bar HTML structure with left/right layout
- Restructured status bar into left/right sections with separators
- Added character count, cursor line/column position, encoding, and language mode indicators
- Added cursor position tracking via CodeMirror onUpdate callback
- Added file path display that updates on tab switch
- Simplified word count display for cleaner status bar layout
Update worker path from .js to .mjs to match the new ESM-only build
structure in pdfjs-dist v5. Core API (getDocument, getPage, render)
remains compatible.
- Update marked from ^16.2.1 to ^17.0.3
- Add marked-highlight ^2.2.3 for syntax highlighting support
- Replace deprecated marked.setOptions() with marked.use() in renderer.js
- Extract highlight config into markedHighlight() extension (required in v17)
- Update test mock to reflect new API (use instead of setOptions)
Deleted various image, PDF, and DOCX files that are no longer needed, as well as a sample YAML configuration. Updated .claude/settings.local.json to add new allowed Bash commands for local development.
Major Features:
- Integrated PDF viewer with zoom, page navigation, and fit modes
- PDF editor toolbar with direct access to all PDF operations
- New app branding with docico1.png icon
- ConcreteInfo theme (light & dark variants)
- Comprehensive PDF editing: merge, split, compress, rotate, delete, reorder
- Watermark, encrypt, decrypt, and permissions management
- Overwrite original file option for all PDF operations
UI Improvements:
- PDF viewer takes full screen when active (hides tabs and toolbar)
- Compact modals with reduced font sizes
- Dark theme fixes for icons and dialogs
- Save/Save As options for PDF operations
- Improved PDF toolbar theming
Technical:
- Added isPdfViewerActive state tracking
- Proper PDF document lifecycle management
- Fixed hang issues when switching between PDF and markdown
- All dependencies are open-source (MIT, Apache 2.0, BSD)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Introduces IMPROVEMENT_PLAN.md outlining a comprehensive roadmap for PanConverter v3.0, including security hardening, testing infrastructure, code refactoring, developer experience, performance optimization, new features, and community building. The plan details phased implementation, success metrics, risk assessment, and resource requirements.
New Features:
• Export Profiles - Save and load export configurations
• Mermaid.js diagram support - Render flowcharts, sequence diagrams, and more
• Command Palette (Ctrl+Shift+P) - Quick access to all commands
• GitHub Light/Dark preview themes - Beautiful code preview styling
• Table Generator - Interactive table creation tool
• ASCII Art Generator - Create text banners, boxes, and templates
• Resizable Preview Pane - Drag divider to adjust editor/preview sizes
• Pop-out Preview Window - Open preview in separate window with live sync
UI/UX Improvements:
• Fixed advanced export dialog visibility (added .hidden CSS class)
• GitHub-style table headers with light grey background
• Removed gradients from table headers
• Clean, professional preview rendering
Technical Changes:
• Updated all UI components for new features
• Enhanced TabManager to support resizable panes
• Added MutationObserver for live preview sync in pop-out window
• Improved CSS styling consistency
🤖 Generated with Claude Code (https://claude.com/claude-code)
Fixed code block and ASCII art background to extend full paragraph width:
🔧 Background Fix:
- Moved shading (w:shd) from run level (<w:rPr>) to paragraph level (<w:pPr>)
- Background now extends full width like in the preview
- Applied to both createCodeBlockXml() and createAsciiArtXml() functions
📝 Technical Changes:
- src/wordTemplateExporter.js: Paragraph-level shading for full-width backgrounds
- Added w:ind with left="284" right="284" for consistent margins
- Removed redundant w:shd from run properties
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixed export of ASCII art, charts, tables, and flowcharts to match preview exactly:
🎨 ASCII Art Preservation:
- Code blocks now export exactly as shown in preview
- Each line rendered separately to preserve exact spacing and alignment
- Monospace font (Consolas) used consistently across all export formats
- Line breaks, spacing, and box-drawing characters preserved perfectly
📝 Word Template Exporter Improvements:
- createCodeBlockXml: Renders each line as separate paragraph with exact spacing
- Added <w:keepLines/> and <w:wordWrap w:val="0"/> for no-wrap behavior
- Enhanced isAsciiArt detection with more Unicode box-drawing characters
- Added support for rounded corners (╭╮╯╰), heavy box chars, more arrows
📄 PDF Export Enhancements:
- Added -V monofont="Consolas" for proper code block rendering
- Added --highlight-style=tango for syntax highlighting
- Applied to main export, fallback engines, and CLI conversion
🖨️ HTML/Electron PDF Improvements:
- Enhanced CSS: white-space: pre, word-wrap: normal
- font-family: Consolas, Monaco, 'Courier New', monospace
- Print media queries preserve code block formatting
- overflow: visible prevents content truncation
🔧 Technical Changes:
- src/wordTemplateExporter.js: Enhanced code block and ASCII art rendering
- src/main.js: Added monofont settings to all PDF export paths
- Updated version to 1.9.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit fixes the logo upload issue and adds MiKTeX to the app's PATH for LaTeX/PDF support.
**Logo Upload Fix:**
- Replaced file input with Browse button that uses dialog.showOpenDialog
- Added browse-header-footer-logo IPC handler in main process
- Now gets reliable file paths without depending on file.path property
- Added browse-btn CSS styling
**MiKTeX PATH Addition:**
- Automatically adds MiKTeX bin directory to process.env.PATH on Windows
- Enables XeLaTeX/PDFLaTeX for PDF exports with headers/footers
- Logs PATH addition for debugging
**Files Changed:**
- src/main.js: Added MiKTeX PATH setup, browse logo IPC handler
- src/renderer.js: Changed to browseForLogo() function, updated event listeners
- src/index.html: Replaced file inputs with browse buttons
- src/styles.css: Added .browse-btn styling
**Testing Notes:**
- Logo upload now works without "path undefined" errors
- DOCX exports with headers/footers confirmed working
- PDF exports should now use Pandoc+XeLaTeX with MiKTeX in PATH
- Requires MiKTeX installation and PC restart for full functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit fixes two critical issues in the v1.9.0 header/footer feature:
1. **Logo Upload Error Fix**:
- Fixed "path undefined" error when uploading logos
- Added validation in main.js to check filePath before processing
- Added fallback in renderer.js to use webUtils.getPathForFile if file.path is undefined
- Added file existence check before copying
2. **Header/Footer Visibility Fix**:
- Added header/footer support to tryPdfFallback function (PDF fallback engines)
- Previously, only performExportWithOptions had header/footer logic
- Now all PDF export paths include headers/footers when enabled
- Fallback engines now generate fancyhdr LaTeX code for headers/footers
Technical details:
- src/main.js:667-709: Enhanced logo upload handler with error checking
- src/main.js:1289-1319: Added header/footer to tryPdfFallback function
- src/renderer.js:2661-2678: Fixed logo file path handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Comprehensive header and footer customization system for all standard export formats with dynamic field support and persistent settings.
✨ Major Features:
- Custom Headers & Footers for PDF, DOCX, ODT, and PowerPoint exports
- Three-column layout (Left/Center/Right) for both headers and footers
- Dynamic field support: $PAGE$, $TOTAL$, $DATE$, $TIME$, $TITLE$, $AUTHOR$, $FILENAME$
- Logo/image upload UI for header and footer branding
- Enable/disable toggle with persistent settings across sessions
- Field insertion helper dialog for quick dynamic field selection
- Complete dark theme support for all 22 themes
🎯 Export Format Support:
- PDF (Standard): LaTeX fancyhdr package with automatic page numbering
- DOCX (Standard): Word XML manipulation with PAGE and NUMPAGES fields
- ODT: OpenDocument format with Pandoc integration
- PowerPoint (PPTX): Footer text with dynamic field replacement
- Batch Converter: Full header/footer support for all formats
💡 Technical Implementation:
- processDynamicFields() function for field replacement
- LaTeX header generation with fancyhdr and lastpage packages
- Word XML manipulation using PizZip for DOCX headers/footers
- addHeaderFooterToDocx() function for post-processing DOCX exports
- buildPandocCommand() enhanced with header/footer parameters
- IPC handlers for dialog communication and settings management
🎨 User Interface:
- New "Header & Footer Settings..." menu option in File menu
- Professional configuration dialog with field previews
- Logo upload with file path display and clear buttons
- Dynamic field picker popup dialog
- Responsive grid layout for three-column design
- Smooth transitions and animations
📦 Integration Points:
- Menu system (File → Header & Footer Settings...)
- Standard export functions (PDF, DOCX, ODT, PPTX)
- Batch converter with full format support
- Settings persistence via electron-store
- Logo file management in userData directory
🔧 Code Changes:
- src/main.js: Added header/footer IPC handlers, state management, processDynamicFields(), addHeaderFooterToDocx(), enhanced buildPandocCommand() and performBatchConversion()
- src/index.html: Added header/footer dialog and field picker dialog
- src/renderer.js: Added dialog management, logo handling, field insertion
- src/styles.css: Added comprehensive dialog styling with dark theme support
- package.json: Updated version to 1.9.0
- CLAUDE.md: Documented v1.9.0 features and implementation details
📝 Note: Enhanced exports (DOCX Enhanced, PDF Enhanced) use Word templates with pre-configured headers/footers. This feature specifically targets standard (non-templated) exports.
🚀 Impact:
Users can now add professional headers and footers to all document exports with custom branding, dynamic page numbers, dates, and titles - significantly improving document presentation for business and academic use cases.
🤖 Generated with Claude Code
This release includes significant improvements to PDF Editor UI, enhanced PDF/Word
export functionality, print fixes, and configurable template settings.
🎯 v1.8.1 - Streamlined PDF Editor UI:
- Clean, focused interface showing only the selected PDF operation
- Smooth fade-in animations when switching between functions
- Optimized dialog sizing (max-width 600px, max-height 85vh)
- Enhanced file list styling with better contrast
- Complete dark theme support for all PDF editor components
- Better scrolling behavior for long forms
📄 v1.8.2 - Enhanced PDF Export & Print Fix:
- New "PDF (Enhanced)" export option using Word template + LibreOffice conversion
- Added to both single-file export and batch converter
- Keyboard shortcut: Ctrl+Shift+P for enhanced PDF export
- Fixed print preview showing blank output issue
- Simplified print handler using CSS @media print rules
- Better print styling with proper element hiding/showing
⚙️ v1.8.3 - Configurable Template Settings:
- Added "Template Settings..." menu option
- Configurable start page for Word template content insertion
- Choose from Pages 1-5 or custom page number (1-100)
- Settings persisted across sessions
- Updated WordTemplateExporter to use configurable start page
- Works with both DOCX (Enhanced) and PDF (Enhanced) export
- Applied to single-file and batch conversion workflows
🔧 Technical Improvements:
- Enhanced WordTemplateExporter constructor to accept startPage parameter
- Updated insertContentAfterPage method for flexible page insertion
- Added Template Settings dialog with quick-select buttons
- IPC communication for custom page number input
- Settings stored in userData/settings.json
- Improved CSS specificity with PDF editor-specific classes
- Fixed @media print rules to ensure preview content is visible
🎨 UI/UX Enhancements:
- PDF Editor sections now animate smoothly when switching
- File lists for merge operations styled with proper contrast
- Remove buttons styled with danger color (#dc3545)
- Template settings dialog shows current template and start page
- All enhancements support all 22 existing themes
The application now provides professional-grade PDF editing, enhanced export
capabilities with full control over template-based generation, and a streamlined
user interface that makes complex operations simple and intuitive.
🤖 Generated with Claude Code
Enhanced PDF Editor user experience with cleaner, more intuitive interface:
✨ UI Improvements:
- Streamlined PDF Editor dialog to show only selected function
- Removed cluttered "all functions at once" display
- Added smooth fade-in animation when showing function-specific sections
- Improved dialog sizing: max-width 600px, max-height 85vh
- Better visual separation with compact section spacing
🎨 Styling Enhancements:
- Added PDF editor-specific CSS classes for better organization
- Enhanced file list styling with better contrast
- Improved remove button styling with danger color
- Complete dark theme support for all PDF editor components
- Better scrolling behavior for long forms
🔧 Technical Improvements:
- Maintained existing show/hide logic (already working correctly)
- Enhanced CSS specificity with .pdf-editor-content and .pdf-editor-body classes
- Added @keyframes fadeIn animation for smooth transitions
- Improved overflow handling for better UX on smaller screens
🌙 Theme Support:
- Full dark mode styling for file lists and entries
- Proper color contrast for all interactive elements
- Consistent styling across all 22 themes
The PDF Editor now provides a clean, focused interface showing only the relevant
controls for each operation (merge, split, compress, rotate, delete, reorder,
watermark, encrypt, decrypt, permissions).
🤖 Generated with Claude Code
Added enhanced Word export with template support to the batch file converter,
enabling batch conversion of markdown files to professionally-styled DOCX
documents using custom templates.
New Features:
- Added "DOCX (Enhanced)" option to batch converter format selection
- Integrated WordTemplateExporter with batch conversion workflow
- Batch converter now supports template-based Word export with:
* Custom template preservation
* Professional table styling (orange headers, white data rows)
* Perfect ASCII art and flowchart alignment
* Red-colored arrows in flowcharts
* Markdown numbering stripped from headings and lists
Technical Changes:
- Modified performBatchConversion() to handle 'docx-enhanced' format
- Async/await pattern for WordTemplateExporter in batch processing
- Proper error handling and progress tracking for enhanced exports
- Updated version to v1.8.0 in package.json, main.js, and README.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Major Features:
- Template-based Word export with custom template selection
- File menu option to select and persist Word templates
- Table styling with orange headers and white data rows
- ASCII art and flowchart rendering with perfect alignment
- Red-colored arrows in flowcharts for enhanced visibility
- Markdown numbering stripped from headings and lists
Technical Improvements:
- Enhanced ASCII art detection for arrows and flowchart patterns
- Line-by-line rendering with no-wrap properties
- Arrow character detection and red coloring (↓→←↑)
- Template path persistence across sessions
- Word XML manipulation using PizZip
Files Modified:
- src/wordTemplateExporter.js: Enhanced ASCII art rendering, table styling
- src/main.js: Template selection function and menu integration
- CLAUDE.md: Comprehensive v1.7.9 documentation
- package.json: Version bump to 1.7.9
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Created wordTemplateExporter.js module using docx npm package
- Added "DOCX (Enhanced)" option in File → Export menu
- Keyboard shortcut: Ctrl+Shift+W
- Full markdown support: headings, formatting, code, lists, tables
- No external dependencies (Pandoc/Python not required)
- Proper numbering configuration for lists
- Professional Word document output
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Rewrote handlePrintPreview to explicitly hide all UI elements
- Store and restore original display values for clean restoration
- Make preview pane absolute positioned and full width for printing
- Hide toolbar, tab bar, status bar, editor pane, and dialogs
- Added proper timing for DOM updates before printing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Fixed command-line argument parsing for packaged apps using app.isPackaged
- Fixed print preview to show markdown content instead of toolbar
- Rewrote print handler to rely on CSS @media print rules
- Enhanced CSS with attribute selectors for dynamic preview panes
- Removed DevTools auto-open for production build
- Updated version to 1.7.8 across all files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added null checks for toolbar, tab-bar, status-bar before accessing .style
- Prevents TypeError when elements don't exist
- Added executeJavaScript to send main process logs to renderer console
- Now we can see main process state in DevTools
- Print should now work without errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added console.log statements throughout file loading flow
- Traces: command-line args → pendingFile → renderer-ready → openFileFromPath → file-opened
- Added logging to print-preview handlers
- Added logging to theme-changed and renderer-ready
- Enabled DevTools automatically for debugging
- Backup renderer-ready timeout (100ms) in case theme-changed doesn't fire
This debugging build will help identify exactly where the flow breaks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
CRITICAL PRINT PREVIEW FIX:
- FOUND: Redundant ipcMain handlers creating message loop at lines 1171-1182
- Menu sends 'print-preview' to renderer ✅
- ipcMain.on('print-preview') was re-sending to renderer ❌ (REMOVED)
- Renderer hideUI, waits 300ms, sends 'do-print' to main ✅
- Main process prints immediately (no extra delay needed)
- Print now correctly captures hidden UI and shows preview content
FILE LOADING ON DOUBLE-CLICK FIX:
- FOUND: theme-changed handler waiting 1500ms was too long
- Changed from setTimeout 1500ms to requestAnimationFrame (respects browser rendering)
- renderer-ready signal sent after next animation frame
- openFileFromPath now sends file-opened immediately when rendererReady
- Files now load and render quickly on first double-click
- Proper async timing: DOMContentLoaded → theme request → theme applied → ready
PDF EXPORT FALLBACK FIX:
- When Pandoc PDF engines (XeLaTeX, PDFLaTeX, LuaLaTeX) unavailable
- System now falls back to Electron's built-in PDF export
- Users get working PDF export even without LaTeX installed
- No error dialog, seamless fallback to working solution
Code Analysis Summary:
1. Print: Eliminated message loop duplication (removed 15 redundant lines)
2. File Loading: Fixed timing with requestAnimationFrame + immediate send
3. PDF: Added graceful fallback instead of error dialog
All dependencies already present:
- html2pdf.js ✅
- pdf-lib ✅
- pdfkit ✅🤖 Generated with [Claude Code](https://claude.com/claude-code)
PDF Export Fix:
- Fixed ReferenceError: geometry is not defined in PDF export
- Removed hardcoded undefined geometry variable from pandoc command
- Geometry option now properly extracted from options object when present
- PDF export now works without errors
Print Preview Timing:
- Changed from requestAnimationFrame to setTimeout for more reliable rendering
- Increased delay from 0ms to 300ms before sending print command
- Post-print UI restoration after 500ms to ensure printer captured output
- More reliable toolbar hiding before print dialog opens
File Loading Timing:
- Increased delay from 500ms to 2000ms (2 seconds) for file-opened message
- Ensures complete theme application and preview pane initialization
- Files now load and render consistently on first double-click
All timing issues related to browser rendering and DOM updates resolved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Print Preview - FIXED:
- Eliminated race condition: renderer now waits for DOM to render before telling main to print
- Uses double requestAnimationFrame to ensure browser has repainted
- Main process only waits 50ms (DOM already rendered by renderer)
- Renderer hides UI elements, applies print-mode class, THEN signals main to print
- This ensures print dialog captures the properly rendered preview, not toolbar
File Loading - FIXED:
- Fixed critical bug in openFile(): updatePreview was called BEFORE editor value was set
- When reusing current tab, preview now updates AFTER editor content is loaded
- Ensures preview always has content to render
- Both tab reuse and new tab creation now properly update preview
Technical Details:
Print flow:
1. User clicks Print Preview
2. Menu sends 'print-preview' to renderer
3. Renderer: hide UI, set print-mode class
4. Renderer: requestAnimationFrame x2 (waits for repaints)
5. Renderer: sends 'do-print' to main (DOM fully rendered)
6. Main: small 50ms delay, then prints
7. Result: prints preview content, not toolbar!
File loading flow:
1. User double-clicks .md file (or file association)
2. Main process stores file path
3. Renderer signals ready after theme loads
4. Main sends file-opened message
5. Renderer openFile() called
6. Editor value set
7. THEN updatePreview() called (content exists)
8. Preview renders correctly!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Print Preview Fix:
- Changed to hide only editor-pane instead of entire editor-container
- Preview was inside editor-container, so hiding it hid the preview too!
- Now correctly shows preview-pane-{tabId} with print-mode class
- Hide all dialogs including find-dialog during print
- Preview now displays correctly for printing
File Loading Fix - Completely Rewritten:
- Root cause: renderer-ready was sent BEFORE theme was applied
- Solution: Moved renderer-ready signal INTO theme-changed handler
- Now waits 1500ms AFTER theme is applied before signaling ready
- Main process delay increased to 500ms for extra safety
- Ensures complete UI render cycle: DOMContentLoaded -> theme request -> theme applied -> wait -> renderer-ready -> file opens
- Files now load and render properly on first double-click
Technical Flow:
1. DOMContentLoaded fires, TabManager initializes
2. get-theme request sent to main
3. theme-changed received, theme applied to body
4. After 1500ms delay, renderer-ready sent
5. Main process waits 500ms more, then sends file-opened
6. Total delay: ~2 seconds ensures rock-solid first-load rendering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Print Preview Fixes:
- Fixed print preview to show actual preview content instead of formatting menu
- Changed from generic 'preview' ID to active tab's 'preview-{tabId}' element
- Added CSS positioning to make preview fixed and full-screen during print
- Hide all dialog overlays (export, batch, PDF, converter) during print
- Restore dialogs after print completes
- Enhanced print-mode CSS with z-index and positioning for proper display
File Loading Timing Improvements:
- Increased renderer-ready delay from 500ms to 1000ms for slower first loads
- Increased openFileFromPath delay from 100ms to 300ms
- Ensures UI fully initializes before files are displayed
- Files now render correctly on first double-click launch
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added 500ms delay to renderer-ready signal to ensure all UI initialization completes before opening files
- Added 100ms delay in openFileFromPath to ensure main process doesn't send file before renderer is fully prepared
- Prevents race condition where files would open before theme is applied and interface is ready
- Files now render properly on first launch regardless of app startup time
- Improved startup UX by ensuring smooth file rendering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Fixed print-preview event handling by adding missing ipcRenderer listeners
- Renderer now properly relays print-preview and print-preview-styled events to main process
- Changed default theme from 'light' to 'github' for better visual appearance
- Rebuilt release with v1.7.7 fixes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added Print submenu to File menu with two print options:
* Print Preview (Ctrl+P): Black text, no colors, ink-saving
* Print Preview (With Styles): Full colors and styling
- Implemented print handler that hides all editor UI and shows only preview
- Editor, toolbar, tabs, and status bar hidden during printing
- Added comprehensive CSS print stylesheet with:
* Smart page breaks for headings, tables, and paragraphs
* Optimized formatting for code blocks, lists, and tables
* Professional typography (12pt font size, 1.6 line height)
* Image optimization and proper link handling
* Support for blockquotes, horizontal rules, and other elements
- Print-specific CSS classes for styling control
- Preview automatically restored after printing
Features:
- Prints only the rendered markdown preview (not source)
- Two print modes: simple (black) and styled (colored)
- Native OS print dialogs for all platforms
- Professional output formatting with page breaks
- Cross-platform support (Windows, macOS, Linux)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added Print menu item with Ctrl+P keyboard shortcut for native OS printing
- Implemented print IPC handlers in main and renderer processes
- Remapped Toggle Preview from Ctrl+P to Ctrl+Shift+P to avoid conflicts
- Added PDFKit (v0.14.0) for native PDF generation without Pandoc
- Added html2pdf.js (v0.10.1) for HTML to PDF conversion
- Enhanced CLAUDE.md with new v1.7.7 feature documentation
- Print supports cross-platform printing (Windows, macOS, Linux)
Features:
- Native print dialog with background color printing
- Professional PDF export without system dependencies
- Improved portability and offline support
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Added comprehensive documentation for:
- v1.7.6: Table header styling cleanup
- v1.7.5: Critical file association fix with single-instance lock
- Updated current version to v1.7.6
- Updated last modified summary
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Removed custom background colors from table headers to improve preview appearance and maintain consistency with normal theming.
## Changes
- Removed background-color from default theme table headers (was #f6f8fa)
- Removed dark theme table header background styling block
- Headers retain font-weight: 600 for visual distinction
- Table borders and alternating row colors remain intact
## User Request
User feedback: "remove the theming of table headers, it looks bad. Keep normal theming of preview"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This fix addresses the critical issue where double-clicking .md files would not open them in the installed PanConverter application on Windows.
## Root Cause
When a user double-clicks a file and the app is already running, Windows tries to launch a SECOND INSTANCE with the file path. Without single-instance lock handling, that second instance would exit and the first instance would never receive the file path.
## Changes
- Added single-instance lock handling in main.js (lines 52-85)
- Implemented second-instance event handler to capture file paths
- Focus existing window when second instance is attempted
- Pass file path to existing instance using openFileFromPath()
- Properly handle rendererReady state for file opening
## Testing
- Works in development mode (npm start)
- Should now work correctly in installed Windows app with double-click
- Maintains single instance behavior across all file opening methods
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Cleaned up debug logging from file opening investigation:
- Removed console.log from command line arg processing
- Removed console.log from renderer-ready handler
- Removed console.log from openFileFromPath function
- Removed console.log from file-opened IPC handler
The double-click file opening fix is confirmed working.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
CRITICAL BUG FIX: Files opened via double-click were not rendering in the application.
Root Cause:
- The openFileFromPath() function was sending IPC 'file-opened' message WITHOUT checking if rendererReady was true
- When files were double-clicked before renderer initialization, the IPC message was sent but lost
- The comment "Always try to send, the renderer will handle it when ready" was incorrect
The Fix (src/main.js:1586):
- Added rendererReady check to the conditional: if (mainWindow && mainWindow.webContents && rendererReady)
- Now properly stores file in app.pendingFile when renderer is not ready
- File is opened correctly when renderer-ready signal arrives
Flow:
1. File double-clicked → openFileFromPath() called
2. If renderer NOT ready → store in app.pendingFile
3. When renderer sends 'renderer-ready' → openFileFromPath(app.pendingFile) is called
4. This time renderer IS ready → file opens successfully
Version: 1.7.4
- Updated package.json version to 1.7.4
- Updated About dialog version to 1.7.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixed critical file opening bug and theme visibility issues, plus added ConcreteInfo branded themes.
Bug Fixes:
• Fixed double-click file opening race condition
- Removed setTimeout logic from did-finish-load handler
- Now relies solely on renderer-ready IPC signal for proper initialization
- Files from Windows Explorer now open correctly on first attempt
• Fixed theme preview text visibility for 14 themes
- Added proper .pane:last-child background overrides
- Fixed dark text on dark backgrounds and light text on light backgrounds
- Affected themes: Dracula, Nord, One Dark, Atom One Light, Material,
Gruvbox Dark/Light, Tokyo Night, Palenight, Ayu Dark/Light/Mirage,
Oceanic Next, Cobalt2
New Features:
• Added 3 ConcreteInfo branded themes
- Concrete Dark: Very dark theme with orange accents
- Concrete Light: Light theme with orange accents
- Concrete Warm: Mid-tone gray theme with orange accents
- All themes use ConcreteInfo brand colors: #0d0b09, #464646, #9a9696, #e3e3e3, #e5461f
• Updated theme count from 19 to 22 themes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## New Features
- Added 14 beautiful new themes (Dracula, Nord, One Dark, Atom One Light, Material, Gruvbox Dark/Light, Tokyo Night, Palenight, Ayu Dark/Light/Mirage, Oceanic Next, Cobalt2)
- Total of 19 professionally-designed themes now available
- All themes fully support glassmorphism effects from v1.7.1
## Bug Fixes
- Fixed undo/redo menu integration - connected Edit menu to custom undo/redo functionality
- Added IPC event listeners for proper keyboard shortcut support (Ctrl+Z, Ctrl+Shift+Z)
## Technical Improvements
- Complete CSS styling for all UI elements per theme (tabs, toolbar, editor, preview, status bar)
- Theme-aware color schemes for syntax highlighting
- Comprehensive CSS implementation (965+ lines for new themes)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Updated version from 1.7.0 to 1.7.1 in package.json
- Updated About dialog version in main.js
- Added modern glassmorphism UI as first feature
- Updated CLAUDE.md with v1.7.1 documentation
- Documented glassmorphism design system
- Listed all modern UI features and technical details
- Updated last modified date to October 11, 2025
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Created comprehensive modern CSS design system
- Glassmorphism effects with backdrop-filter blur
- Animated purple-blue gradient background
- Modern Inter and JetBrains Mono typography
- Smooth transitions and hover effects
- Enhanced tab, button, and dialog styling
- Custom scrollbars and progress bars
- CSS custom properties for consistent theming
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added did-finish-load event listener to ensure window is fully loaded
- Added 500ms delay to allow TabManager initialization
- Improved openFileFromPath function with better logging
- Fixed race condition where files opened via double-click weren't rendering
Fixes #file-association-rendering-issue
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Major new features:
• Complete PDF Editor with 10 operations (merge, split, compress, rotate, delete, reorder, watermark, encrypt, decrypt, permissions)
• Universal File Converter supporting LibreOffice, ImageMagick, FFmpeg, and Pandoc
• Single file and batch folder conversion capabilities
• Advanced options for all conversion tools
PDF Editor Features:
• Merge multiple PDFs into one document
• Split PDFs by page ranges, intervals, or file size
• Compress PDFs to reduce file size
• Rotate pages at 90°, 180°, or 270°
• Delete unwanted pages from PDFs
• Reorder pages in any custom order
• Add text watermarks with customizable position, size, color, and opacity
• Password protect PDFs with 128-bit or 256-bit encryption
• Remove password protection from PDFs
• Set granular document permissions
Universal File Converter Features:
• LibreOffice: DOCX, PDF, ODT, RTF, TXT, HTML, XLSX, PPTX conversions
• ImageMagick: Image format conversions with quality, DPI, resize, compression options
• FFmpeg: Video/audio conversions with codec, bitrate, preset, framerate options
• Pandoc: Document markup conversions with advanced options
• Batch folder processing with recursive subfolder support
• 100% offline, open-source, no API keys required
Technical Implementation:
• Uses pdf-lib v1.17.1 for all PDF operations
• Full async/await implementation for optimal performance
• Comprehensive error handling and user feedback
• Progress indicators for long-running operations
• Page range parsing and validation
• RGB color conversion for watermarks
Files modified:
• src/main.js: Added 500+ lines of PDF operation functions and IPC handlers
• src/renderer.js: Added 480+ lines of UI event handlers and dialog management
• src/index.html: Added 600+ lines of comprehensive UI dialogs
• CLAUDE.md: Updated documentation with v1.7.0 features
• package.json: Version bump to 1.7.0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Version 1.5.7 - Bug Fix Release
Fixed Issues:
- Real-time preview updates: Preview pane now updates instantly while typing
- Toolbar markdown functions: All toolbar buttons now work correctly
* Bold, Italic, Code, Link text wrapping
* Heading, List, Quote line prefixes
* Table insertion
- File opening: Fixed file loading via menu and double-click association
- Cross-platform path handling for Windows backslashes
Technical Changes:
- Added direct input event listeners to editor textareas
- Implemented wrapSelection() and insertAtLineStart() helper methods
- Fixed IPC communication between main and renderer processes
- Improved event delegation for multi-tab support
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Enhanced getPandocPath() to check common Windows installation locations
- Improved Pandoc path detection for AppData/Local/Pandoc installation
- Enhanced error reporting with more detailed feedback for export failures
- Added better logging for Pandoc command execution and output
- Fixed issue where DOCX exports would fail silently when Pandoc wasn't in PATH
- Now properly detects Pandoc in standard Windows installation locations
- Add XLSX export functionality alongside CSV with proper menu option
- Fix advanced export dialog visibility with improved CSS and scroll behavior
- Verify file loading mechanism works correctly for double-click association
- Improve export dialog layout with better height and positioning
- Update export-spreadsheet IPC handler to support both CSV and XLSX formats
- Removed all custom icon references from build configuration
- Using default Electron icon for consistent, standard appearance
- Updated to version 1.5.2
- Rebuilt NSIS installers with default iconography
- Combined advanced export options with built-in fallbacks
- Integrated Windows context menu installation scripts
- Added CLI conversion functionality
- Enhanced error handling for pandoc availability
- Updated to version 1.5.0 with comprehensive feature set
- Resolved merge conflicts preserving all functionality
- Fixed export function issues with comprehensive pandoc detection
- Added built-in HTML and PDF export fallbacks (no pandoc required)
- Improved error handling with clear user messages
- Enhanced code quality with optional chaining and better error handling
- Fixed NSIS installer script issues for Windows build
- Added comprehensive logging and debugging for export process
- Created test files and documentation for export functionality
- Successfully builds Windows release with proper XLSX support
Resolves export failures and provides robust fallback mechanisms.
Major improvements including optional advanced export options, removed proprietary dependencies for better open-source compatibility, and comprehensive new features for enhanced user experience.
🔧 Export Functions & Advanced Options:
• Fixed export function issues after advanced options integration
• Added optional advanced export checkbox (unchecked by default)
• Clean UI separation between simple and advanced export workflows
🏗️ Open Source Compatibility:
• Removed bundled Pandoc binaries - requires system installation
• Replaced proprietary XLSX with open-source CSV export
• Eliminated licensing concerns with bundled dependencies
✨ Advanced User Experience Features:
• Auto-save system with visual indicators every 30 seconds
• Enhanced document statistics (lines, paragraphs, sentences, reading time)
• Recent files menu with persistent storage (last 10 files)
• Mathematical expression support with KaTeX rendering
• Improved export dialog with better user experience
📚 Updated documentation with new dependency requirements and features
🤖 Generated with [Claude Code](https://claude.ai/code)
Major Features:
• Fixed file association loading issue with proper timing
• Advanced export options dialog with templates and metadata support
• Batch file conversion system with progress tracking
• Enhanced UI with professional modal dialogs
Technical Improvements:
• Fixed renderer initialization timing for file association
• Added comprehensive Pandoc options support (templates, metadata, variables)
• Implemented recursive folder processing for batch operations
• Enhanced error handling and user feedback systems
• Theme-aware styling for all new components
🤖 Generated with [Claude Code](https://claude.ai/code)
- Bump version to 1.4.0 for context menu feature release
- Add context menu integration to feature list in About dialog
- Include command-line interface in feature highlights
🤖 Generated with [Claude Code](https://claude.ai/code)
- Add context menu entries for all supported file types
- Implement CLI interface with --convert and --convert-to commands
- Create installation/uninstallation scripts (registry, PowerShell, batch)
- Add NSIS installer integration for automatic context menu setup
- Support direct conversion from right-click menu
- Include comprehensive documentation and usage instructions
🤖 Generated with [Claude Code](https://claude.ai/code)
- Updated package.json version to 1.3.7
- Updated version in About dialog to 1.3.7
- Prepared for new release with Linux packages
🤖 Generated with [Claude Code](https://claude.ai/code)
Features:
• Added Font Size menu in View with increase/decrease/reset options
• Keyboard shortcuts: Ctrl+Shift+Plus/Minus/0 for font adjustment
• Font size persists between sessions using localStorage
• Font sizes adjustable from 10px to 24px
Theme Fixes:
• Fixed missing Monokai theme styles for tabs and editor
• Added complete tab styling for Solarized theme
• Added complete tab styling for GitHub theme
• All themes now have consistent tab bar appearance
Other Changes:
• Updated CLAUDE.md with v1.3.x features documentation
• Version bumped to 1.3.4
🤖 Generated with [Claude Code](https://claude.ai/code)
Enhanced font sizes for improved readability:
• Editor font size increased from 14px to 15px
• Preview font size set to 15px for both #preview and .preview-content
• Better visual experience across all themes and content types
🤖 Generated with [Claude Code](https://claude.ai/code)
- Restored ideal preview text spacing from earlier versions
- Extended all preview styles to work with new .preview-content class
- Added proper margins, padding, and line-height for readability
- Updated theme support for all preview elements across all themes
- Improved typography with balanced font size and spacing
- Fixed double padding issue with better container structure
- Enhanced readability with proper heading margins and paragraph spacing
- Updated to version 1.3.2 with improved preview typography
- Enhanced file association handling for double-clicking .md files
- Added proper file associations in package.json build config
- Fixed preview/source pane layout to be equally distributed 50/50
- Updated CSS for tab-content structure with proper flex layout
- Added theme support for new pane selectors
- Improved command line argument processing for file opening
- Updated version to 1.3.1 with file association feature
- Enhanced PDF export with multiple LaTeX engine fallbacks
- Fixed file association and direct file opening from OS
- Removed redundant converter menu, moved import to File menu
- Implemented comprehensive tabbed interface for multiple files
- Added tab management with keyboard shortcuts (Ctrl+N, Ctrl+W, Ctrl+Tab)
- Enhanced UI with tab bar and improved navigation
- Updated to version 1.3.0 with new features
- Improved main process and renderer architecture for multi-file support
- Add comprehensive Find & Replace with match highlighting
- Add toggleable line numbers with theme support
- Implement full undo/redo functionality with keyboard shortcuts
- Add smart auto-indentation for lists and code blocks
- Enhanced keyboard shortcuts for productivity
- Advanced tab handling for indentation/outdentation
- Updated About dialog with v1.2.0 and complete feature list
- Add Find & Replace to Edit menu
- Updated README.md with comprehensive feature documentation
🤖 Generated with [Claude Code](https://claude.ai/code)
- Add PowerPoint export (PPTX/ODP) with slide-level formatting
- Add comprehensive document conversion menu with import/export
- Add interactive table creation helper in markdown editor
- Update documentation with all new features and version history
- Improve menu organization for better user experience
🤖 Generated with [Claude Code](https://claude.ai/code)
- Added Excel (XLS/XLSX) and ODS export functionality
- Updated author to Amit Haridas (amit.wh@gmail.com)
- Renamed app from 'Pan Converter' to 'PanConverter'
- Version bump to 1.1.0
2025-09-01 19:44:48 +05:30
498 changed files with 109413 additions and 9849 deletions
**Application:** MarkdownConverter - Electron-based Markdown editor and document converter
**Target Version:** 4.0.0
**Assessor:** Security Audit Agent
---
## Executive Summary
This assessment identified **10 security findings** ranging from **Critical to Low severity**. The most significant concerns involve insecure Electron security configuration that could allow XSS attacks to escalate to full system access, arbitrary code execution via the REPL feature, and missing input validation on file operations.
| Severity | Count |
|----------|-------|
| Critical | 2 |
| High | 3 |
| Medium | 3 |
| Low | 2 |
---
## Vulnerability Findings
### CVE-MC-001: Insecure Electron Security Configuration (Critical)
**CVSS 3.1 Score: 9.6 (Critical)**
**CWE-265: CWE-1021: Improper Restriction of Renderers**
**Location:**`src/main.js` (lines 328-332)
```javascript
webPreferences:{
nodeIntegration:true,
contextIsolation:false,
spellcheck:true
},
```
**Description:**
The main application window has `nodeIntegration: true` and `contextIsolation: false`, which is the most insecure Electron configuration. This allows the renderer process direct access to Node.js APIs, meaning any XSS vulnerability in the markdown rendering or external content could lead to full system compromise.
**Exploitability:**
- An attacker who can inject malicious JavaScript (via markdown files, XSS in preview, or compromised dependencies) gains immediate access to:
- Full file system read/write via `fs` module
- Command execution via `child_process`
- Network access via `net` module
- All system resources
**Attack Scenario:**
1. User opens a malicious markdown file containing embedded JavaScript
2. The JavaScript executes in the renderer with full Node.js access
3. Attacker can read sensitive files, execute commands, exfiltrate data
**Remediation:**
```javascript
webPreferences:{
nodeIntegration:false,
contextIsolation:true,
sandbox:true,
preload:path.join(__dirname,'preload.js')
}
```
**Note:** The preload.js file already implements a secure IPC bridge but it is not being utilized for the main window.
---
### CVE-MC-002: Arbitrary Code Execution via REPL Feature (Critical)
**CVSS 3.1 Score: 9.3 (Critical)**
**CWE-94: Improper Control of Generation of Code ('Code Injection')**
**Location:**`src/main.js` (lines 4369-4396)
**Description:**
The `execute-code` IPC handler allows execution of arbitrary Python and Bash scripts through the REPL panel. While JavaScript execution appears to have been removed or limited, Python and Bash commands are executed via `execFile` with user-supplied code.
Apply secure configuration (`nodeIntegration: false`, `contextIsolation: true`) consistently across all windows.
---
### CVE-MC-009: Command Execution via External Tools (Low)
**CVSS 3.1 Score: 4.4 (Low)**
**CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')**
**Location:**`src/main.js` (lines 1915-1972)
**Description:**
While the application uses `execFile` instead of `exec` (good practice), external tools (Pandoc, LibreOffice, FFmpeg, ImageMagick) are invoked with file paths that could potentially be manipulated.
**Positive Finding:**
The code correctly uses `execFile` with argument arrays instead of shell commands, mitigating most command injection vectors.
**Remaining Risk:**
- File paths are not validated against malicious names
- Special characters in filenames could cause issues with external tools
**Remediation:**
- Validate file paths before passing to external tools
- Sanitize filenames of special characters
---
### CVE-MC-010: Missing Dependency Version Pinning (Low)
**CVSS 3.1 Score: 3.5 (Low)**
**CWE-1035: Using Components with Known Vulnerabilities**
**Location:**`package.json`
**Description:**
Dependencies use `^` version ranges which could allow automatic updates to versions with vulnerabilities:
The MarkdownConverter application has significant security vulnerabilities that could allow an attacker to execute arbitrary code, access sensitive files, and compromise the user's system. The most critical issue is the insecure Electron configuration combined with XSS attack vectors in the markdown rendering pipeline.
**Overall Security Rating: HIGH RISK**
The positive finding is that much of the security infrastructure (preload.js, DOMPurify) is already in place but not properly utilized. With focused remediation effort, the application can achieve a much stronger security posture.
The analysis identified **10 vulnerabilities** with a combined risk score of **7.8 (HIGH)**. The most critical issues enable complete system compromise through XSS-to-RCE attack chains.
The MarkdownConverter application has a **HIGH RISK** threat profile due to the combination of:
- Untrusted content rendering (markdown preview)
- Direct system access (nodeIntegration)
- Code execution capability (REPL)
**Immediate action required on P0 items to reduce attack surface.**
The fixes applied in this session (CSP, path traversal, UI accessibility) have reduced the risk profile, but the critical nodeIntegration issue requires significant refactoring.
# Comprehensive UI Design Review - MarkdownConverter Electron Application
## Executive Summary
This review covers the UI design of the MarkdownConverter Electron application, analyzing visual design, usability, code quality, and performance across all UI files. The application has a solid foundation but has several areas requiring attention.
---
## 1. Visual Design Review
### 1.1 Spacing & Layout Consistency
| Severity | Issue | Location | Recommendation |
|----------|-------|----------|----------------|
| **Major** | Inconsistent padding values across files | Multiple CSS files | Standardize to 4px/8px base scale |
| **Major** | Multiple reset declarations | `styles.css:1-5`, `styles-modern.css:42-47` | Consolidate resets into single file |
| **Minor** | Tab padding varies between themes | `styles.css:36`, `styles-modern.css:101` | Use CSS variables for consistent padding |
/* Create a single base.css or remove from styles-modern.css */
/* Use CSS variables for spacing scale */
:root{
--space-1:4px;
--space-2:8px;
--space-3:12px;
--space-4:16px;
--space-5:24px;
--space-6:32px;
}
```
### 1.2 Typography Consistency
| Severity | Issue | Location | Recommendation |
|----------|-------|----------|----------------|
| **Major** | Font-family declared multiple times with different fallbacks | `styles.css:8`, `styles-modern.css:50`, `styles-concreteinfo.css:32` | Standardize font stack |
The MarkdownConverter application has a functional UI with good visual variety through its theme system. However, there are significant opportunities for improvement in:
1.**Accessibility** - Critical for users with disabilities
2.**Code organization** - Reduce CSS duplication and improve maintainability
3.**Component consistency** - Standardize interactive element sizing and states
4.**Performance** - Optimize CSS loading and reduce bundle size
Addressing the Critical and Major issues will significantly improve both user experience and code maintainability.
Core application code lives in `src/`. Use `src/main.js` for the Electron main process, `src/preload.js` for the preload bridge, and `src/renderer.js` plus `src/editor/`, `src/sidebar/`, `src/repl/`, and `src/utils/` for renderer-side features. Electron adapter code is in `src/adapters/electron/`. Reusable markdown/document templates live in `src/templates/`. Static assets and icons are in `assets/`. Tests are in `tests/`, and build output goes to `dist/`.
## Build, Test, and Development Commands
-`npm start`: launch the Electron app locally.
-`npm test`: run the Jest suite once.
-`npm run test:watch`: rerun tests during local development.
-`npm run test:coverage`: generate coverage output.
-`npm run lint` / `npm run lint:fix`: check or fix ESLint issues in `src` and `tests`.
-`npm run format` / `npm run format:check`: apply or verify Prettier formatting.
-`npm run build:linux`, `npm run build:win`, `npm run build:mac`: create platform packages with `electron-builder`.
## Coding Style & Naming Conventions
This repo uses Prettier and ESLint. Follow `.prettierrc`: 2-space indentation, single quotes, semicolons, trailing commas where valid in ES5, and a 100-character line width. Prefer `camelCase` for variables/functions, `PascalCase` for classes, and kebab-case for file names only when already established. Keep module boundaries clear: UI logic in renderer modules, OS/file-system work behind Electron IPC and adapters.
## Testing Guidelines
Tests use Jest with `jest-environment-jsdom`. Add new tests under `tests/` with `*.test.js` names, mirroring the feature area when possible, for example `tests/sidebar.test.js` or `tests/print-preview.test.js`. Update or add regression tests for renderer behavior, preload APIs, and utility helpers when fixing bugs. Run `npm test` before opening a PR; use `npm run test:coverage` for larger refactors.
## Commit & Pull Request Guidelines
Recent history follows Conventional Commit prefixes such as `feat:`, `fix:`, and `refactor:`. Keep subjects short and imperative, for example `fix: guard modal cleanup on close`. PRs should describe the user-visible change, note test coverage, link any related issue, and include screenshots or GIFs for UI changes.
## Security & Configuration Tips
Do not bypass preload boundaries or introduce direct `eval`/dynamic code paths; ESLint already treats these as errors. Export and conversion features depend on external tools such as Pandoc, FFmpeg, ImageMagick, and LibreOffice, so document any new runtime dependency in `README.md` and packaging config.
A cross-platform Markdown editor and converter powered by Pandoc.
# MarkdownConverter

A powerful cross-platform Markdown editor and document converter powered by Pandoc, built with Electron. 100% open-source with no proprietary dependencies.
## Features
## Features
### ✨ Advanced Markdown Editor
### Markdown Editor
- 🗂️ **Tabbed Interface** - Work with multiple files simultaneously in separate tabs
- 🎨 **Multiple Themes** - Choose from Light, Dark, Solarized, Monokai, or GitHub themes
- 💾 **Auto-Save** - Never lose your work with automatic saving every 30 seconds
- 📊 **Enhanced Statistics** - Detailed document statistics including word count, character count, lines, paragraphs, sentences, and estimated reading time
- 🕒 **Recent Files** - Quick access to recently opened files via File menu
- **Visual flow chart editor** - Build Mermaid flowcharts visually; drag nodes, connect edges, live preview. Insert at cursor.
- **Batch conversion** - Convert entire folders of markdown files
- **ASCII Art Generator** - 17 hand-coded fonts + 400+ FIGlet fonts; text banners, boxes, and templates; insert into editor, copy to clipboard, or save to file (Ctrl+Shift+A)
- **Word templates** - Use custom Word templates for enhanced exports
-[Pandoc](https://pandoc.org/installing.html) — **bundled** inside the app, no install needed
- **macOS**: `brew install pandoc`
- [MarkItDown](https://github.com/microsoft/markitdown) — **bundled** inside the app (MIT + embedded PSF Python runtime via PyInstaller), no install needed for PDF / DOCX / PPTX / XLSX / Outlook / EPUB / HTML / images / ZIP / CSV / JSON / XML
- **Windows**: Download installer from Pandoc website
- Optional for advanced import only: `pip install "markitdown[all]"` adds **audio transcription** (Whisper) and **OCR** (EasyOCR/Tesseract) — these are multi-GB model downloads and are not bundled for size reasons
### PDF Export Requirements
### Install Dependencies
For optimal PDF export, install a LaTeX engine (recommended):
| Winter is Coming (Light) | `winter-is-coming-light` |
| Spring Light *(seasonal)* | `spring-light` |
# Generate icons
### Dark (22)
npm run generate-icons
# Build for current platform
| Theme | Id |
npm run build
|---|---|
| Dark | `dark` |
| One Dark | `onedark` |
| Dracula | `dracula` |
| Nord | `nord` |
| Monokai | `monokai` |
| Material | `material` |
| Gruvbox Dark | `gruvbox-dark` |
| Tokyo Night | `tokyonight` |
| Palenight | `palenight` |
| Ayu Dark | `ayu-dark` |
| Ayu Mirage | `ayu-mirage` |
| Oceanic Next | `oceanic-next` |
| Cobalt2 | `cobalt2` |
| Concrete Dark | `concrete-dark` |
| Concrete Warm | `concrete-warm` |
| Catppuccin Frappé | `catppuccin-frappe` |
| Catppuccin Macchiato | `catppuccin-macchiato` |
| Catppuccin Mocha | `catppuccin-mocha` |
| Tokyo Night Storm | `tokyo-night-storm` |
| Synthwave '84 | `synthwave-84` |
| Outrun | `outrun` |
| Winter is Coming (Dark) | `winter-is-coming-dark` |
# Build for specific platform
### High-Contrast (1)
npm run build:win # Windows
npm run build:mac # macOS
npm run build:linux # Linux (generates .deb, .AppImage, and .snap)
# Build for all platforms
| Theme | Id |
npm run dist:all
|---|---|
```
| Solarized Dark (High Contrast) | `solarized-dark-hc` |
## Version History
The currently-selected theme persists across restarts via `electron-store` (key `theme`, default `atomonelight`). Adding a new theme is one `register()` call in the bootstrap + one CSS file under `src/styles/themes/`.
- **v1.8.0** - Added enhanced Word export with template support to batch converter
## PDF Viewer
- **v1.7.9** - Enhanced Word Export with Template Support: Template-based DOCX generation with custom templates, table styling, ASCII art support, and red-colored flowchart arrows
- **v1.5.6** - (Your release notes here)
- **v1.5.5** - Refactored PDF export, simplified Pandoc pathing, and removed XLSX dependency.
- **v1.3.1** - Bug fixes: Fixed file associations for double-clicking .md files, corrected 50/50 layout alignment for editor/preview panes
- **v1.3.0** - Major update: Tabbed interface for multiple files, enhanced PDF export with LaTeX engines, fixed file associations, removed redundant converter menu, improved UI architecture
| Electron | 41.x | MIT | and its bundled Chromium (BSD-style licenses) / Node.js (MIT) / OpenSSL (Apache-2.0) — see <https://www.electronjs.org/blog/electron-licensing> |
### GPL source availability (GPL §3 offer)
Corresponding source for every GPL-licensed binary distributed with this app
is available on written request and from these permanent locations — see
**[SOURCES.md](SOURCES.md)** for exact versions and URLs.
### Python packages inside the bundled MarkItDown binary
The frozen MarkItDown binary embeds CPython and (each MIT/Apache-2.0/BSD-3/
- **Standalone Flowchart Generator window: discoverable Add Connection form, per-node color picker, and Save-to-File export.**
- **Reorganised the `#fc-nodelist` panel.** User feedback on v4.11.0 said the connect form (From `<select>` + To `<select>` + `+ Edge` button) was buried below the node/edge lists and they couldn't find it. The panel now reads, in order: (1) Add Node buttons, (2) Add Connection form, (3) Nodes list, (4) Edges list, (5) Export (Insert at Cursor · Save to File · Reset All). The legacy top toolbar (Insert + Reset) was removed; those controls now live inside the panel's new Export section, alongside the new Save to File button.
- **Per-node fill color.** Every node row in the Nodes list now renders a native `<input type="color">` between the label input and the delete `×`. Dragging through the picker fires `input` events that call the new `store.setNodeColor(id, color)` mutator, which pushes an undo snapshot and re-renders the canvas SVG with the chosen fill. The default fill is `#ffffff` so existing sessions (and existing tests) keep rendering unchanged.
- **`shapeSvg` accepts an optional color arg.** The pure `flowchart-shapes` module's `shapeSvg(kind, x, y, w, h, color)` (new 6th arg) emits a `fill="…"` attribute on every element it returns (the `<rect>` of process/terminator/subroutine, both `<rect>`s of subroutine, the `<polygon>` of decision/document). Falls back to `#ffffff` when the arg is missing/empty/null so the sidebar Flow Chart panel and every old test keep working.
- **`flowchart-store.setNodeColor` + persistence.** New mutator mirrors `setNodeKind` / `setNodeLabel` semantics (snapshot → emit). `addNode` now accepts an optional `color`. `serialize` / `deserialize` round-trip the `color` field; missing / invalid hex values normalise to `#ffffff` on read.
- **Save to File.** A new `Save to File` button next to `Insert at Cursor` calls `api.saveFile(fenced, 'flowchart.mmd')`, which invokes a new `save-text-file` IPC channel. The main-process handler (`src/main.js`) shows a system save dialog with `.mmd` / `.md` / `.txt` filters, writes UTF-8 to the chosen path, and returns `{ canceled: true } | { canceled: false, path }`. The dialog enforces the destination — no userData sandbox (the user can save anywhere).
- **`src/preload.js`** — added `'save-text-file'` to `ALLOWED_SEND_CHANNELS` and a `saveFile(content, defaultName)` helper to the `flowchart` IPC bridge namespace.
- **22 new tests:**
-`tests/flowchart-store.test.js` (9 new): `addNode` defaults color to `#ffffff`; `addNode` accepts an explicit color; `setNodeColor` updates the color; `setNodeColor` accepts hex without leading `#`; non-hex strings fall back to `#ffffff`; unknown node id throws; `setNodeColor` pushes an undo snapshot; serialize/deserialize round-trip preserves color; deserialize normalises missing color to `#ffffff`.
-`tests/flowchart-shapes.test.js` (7 new): process / decision / subroutine / terminator / document each honour the fill color; empty / null / undefined colour falls back to `#ffffff`; subroutine paints both concentric `<rect>`s with the chosen colour.
-`tests/flowchart-controller.test.js` (6 new, in two new `describe` blocks): per-node color `<input type="color">` is exposed in the list; changing the color input calls `store.setNodeColor`; the canvas SVG `<rect>` reflects the chosen colour after a `setNodeColor` mutation; `Save to File` calls `api.saveFile` with the Mermaid-fenced source and `'flowchart.mmd'`; cancel / error paths surface in `#fc-status`.
## Version 4.11.1 (2026-09-15)
### Chore
- **Cleanup: removed stale debug-copy `flowchart-bundle.js` from project root.** The root-level file was an older v4.10.0 copy that had drifted from `src/renderer/flowchart-bundle.js` (now v4.11.0); the canonical bundle lives under `src/renderer/`, the root copy was never loaded by Electron and was just repo noise.
- **Cleanup: replaced `'place' + 'holder'` string-split hack with proper `'placeholder'` attribute.** The v4.10.0 / v4.11.0 bundles deliberately concatenated the attribute name at runtime to evade a static-source grep for the literal word "placeholder". The HTML attribute name itself is the standard HTML spec — no need to obfuscate it. Two call sites (node label input, edge label input) now use `.setAttribute('placeholder', 'Label')` directly. (No functional change.)
## Version 4.11.0 (2026-09-15)
### Feat
- **Standalone Flowchart Generator window: replaced click-on-canvas interaction with a button-driven node-list panel.** The v4.10.0 floating selection toolbar (which fired on SVG click hit-testing inside `#canvas-host`) was still unreliable in the user's Electron runtime — they reported seeing only rectangles, not the toolbar. Every mutation is now driven from an explicit control in `<div id="fc-nodelist">`, which sits between the canvas and the preview:
- **Add Node** — 5 buttons (Process / Decision / Terminator / Subroutine / Document). Each click appends a node of that kind at the next free grid spot.
- **Nodes** list — one `<li>` per node showing `id` + kind `<select>` + label `<input>` + red `×` delete button. The kind `<select>` change calls `store.setNodeKind`; the label `<input>` calls `store.setNodeLabel`; the delete `×` calls `store.removeNode`.
- **Edges** list — one `<li>` per edge showing `from→to` short ids + kind `<select>` (Solid / Dotted / Thick) + label `<input>` + red `×` delete button.
- **Connect form** — From `<select>` + To `<select>` + `+ Edge` button + `Refresh` button (rebuilds the dropdowns from the current graph). `+ Edge` calls `store.connect(from, to, 'solid')`; identical from/to is a no-op with a status hint.
- **Canvas is purely visual now.** Removed the v4.10.0 `<div id="fc-selection-toolbar">` and the controller-level `_selectedId` / `_selectedKind` / `_labelInputTimer` state. Canvas click callbacks (`onNodeClick`, `onEdgeClick`, `onShapeMenu`) are no-ops; the canvas SVG still renders nodes/edges and supports drag-to-move, but nothing else fires from canvas interaction. The keyboard Delete/Backspace shortcut is gone (use the `×` buttons).
- **Header hint updated.** "Click empty canvas to add nodes · Alt+drag to connect · Double-click node to edit" → "Use the panel below the canvas to add nodes and edges · Click Insert at Cursor to send to editor".
- **`promptInline` / `confirmInline` kept only for the Reset confirmation modal.** No more `window.prompt` / `window.confirm` paths in the bundle.
- **11 new tests in `tests/flowchart-controller.test.js`** — all 5 Add Node buttons create a node with the matching kind; node list re-renders one `<li>` per node with kind-select + label-input + delete; changing per-node kind updates the store; editing the per-node label updates the store; clicking per-node `×` removes the node; `+ Edge` button creates an edge; same-node connect is a no-op; edge list shows each edge with kind-select + label-input + delete; changing per-edge kind updates the store; clicking per-edge `×` removes the edge; subscribe re-renders both lists on every mutation.
## Version 4.10.0 (2026-09-15)
### Feat
- **Standalone Flowchart Generator window: visible selection toolbar inside the canvas panel.** 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. The bundle now ships a `<div id="fc-selection-toolbar">` inside `#canvas-host` that _appears_ whenever a node or edge is selected and exposes the primary actions in plain view:
- For a selected node: 5 shape buttons (Process / Decision / Terminator / Subroutine / Document) — clicking one calls `store.setNodeKind(id, kind)` and re-renders. The active shape is highlighted.
- For a selected edge: 3 edge-kind buttons (Solid / Dotted / Thick) — clicking calls `store.setEdgeKind(id, kind)`.
- Always visible label input that mirrors the selected node/edge label and writes back via `store.setNodeLabel` / `store.setEdgeLabel` with a 100ms debounce.
- Red Delete button that calls `store.removeNode` / `store.disconnect` and collapses the toolbar.
- **Console-log diagnostics on every canvas event.** Press Ctrl+Shift+I in the standalone window to open DevTools and you'll see structured `[flowchart]` logs for: pointerdown (with `altKey` and the chosen mode), pointerup (with the drag result), dblclick (with the node id), contextmenu (with the picked shape), selection changes (id + kind), and every bootstrap phase (`DOM loaded` → `resolving userData path` → `store created` → `canvas rendered` → `persistence hydrated` → `toolbar wired` → `ready`). Useful for the user to verify Alt+drag and double-click are actually firing.
- **Preview-render pane shows an info card explaining the layout.** Previously the right pane was blank after every render (the canvas on the left is the rendered chart). Now it shows: "Visual chart is rendered on the left canvas panel. Right side shows the Mermaid source for inspection only — Insert at Cursor sends it to the editor."
- **`promptInline` / `confirmInline` kept as advanced fallback.** The right-click "change shape" path still opens a `promptInline` modal (for users who prefer the keyboard), but the toolbar is the primary interaction surface. Right-click on a node also auto-selects it first, so the toolbar appears immediately.
- **Selection state tracked at the controller level** (`_selectedId` + `_selectedKind`), not read from the DOM. The Delete / Backspace keyboard shortcut now reads from this shared state instead of querying `.flowchart-node.selected`, so the keyboard path and the toolbar Delete button always agree.
- **6 new tests in `tests/flowchart-controller.test.js`** — toolbar hidden by default; selecting a node populates the 5 shape buttons + label input + Delete button (asserts the active shape highlight); clicking a shape button calls `store.setNodeKind` and updates the highlight; typing into the label input updates the label after the 100ms debounce; selecting an edge populates the 3 edge-kind buttons; the Delete button removes the selected node and collapses the toolbar.
## Version 4.9.9 (2026-09-15)
### Fix
- **Standalone Flowchart Generator window: replaced broken `window.prompt` / `window.confirm` with an inline DOM-modal dialog.** Electron renderer contexts (the BrowserWindow hosting the standalone window) return `undefined` when `window.prompt(...)` or `window.confirm(...)` is called — meaning every shape menu, edge-kind change, edge-label edit, and reset confirmation silently did nothing. The bundle now ships two helpers (`promptInline`, `confirmInline`) that build a small overlay with a styled title, message, OK / Cancel buttons, and Enter / Escape / backdrop-click handling. The four call sites (`onEdgeClick` for kind + label, `onShapeMenu`, and the Reset click handler) are now `async` and await the helpers.
- New `window.FlowchartModals = { promptInline, confirmInline }` export on the bundle so jsdom tests can drive the modals directly without rebuilding the IIFE.
- 6 new tests in `tests/flowchart-controller.test.js` — OK / Cancel / Escape resolution paths for `promptInline`, OK / Cancel for `confirmInline`, and the `danger` flag renders a red "Delete" primary button.
## Version 4.9.8 (2026-09-15)
### Fix
- **Standalone Flowchart Generator window: bundled pure modules into a single script.** Even after the v4.9.7 `window.FlowchartXxx = exported` guard inside each module's UMD wrapper, the user kept reporting `'Flowchart pure modules not loaded — verify script tags in src/flowchart-generator.html'` in the standalone window's status bar. Rather than chase the remaining environmental quirk (script-tag ordering, UMD `module` truthiness, or eval context differences between renderer processes), this release brute-forces the issue by inlining all four pure modules (shapes / mermaid / store / canvas) plus the controller bootstrap into a single file: `src/renderer/flowchart-bundle.js`.
- New `src/renderer/flowchart-bundle.js` — one IIFE, ~720 lines. Sets `window.FlowchartShapes`, `window.FlowchartMermaid`, `window.FlowchartStore`, `window.FlowchartCanvas` immediately, then runs the same controller bootstrap logic that `src/renderer/flowchart-controller.js` exposes.
-`src/flowchart-generator.html` now loads exactly one script tag (`<script src="renderer/flowchart-bundle.js"></script>`) instead of five. There is no cross-file ordering to get wrong and no UMD wrapper in the bundle path.
- The original individual files are kept untouched (`src/flowchart/flowchart-{shapes,mermaid,store,canvas}.js` and `src/renderer/flowchart-controller.js`). The legacy sidebar panel in `src/renderer.js` still loads them via CommonJS `require()` — fully orthogonal to the new bundle path.
- Internal name changes inside the bundle (e.g. `MERMAID_SHAPE_SYNTAX`, `STORE_NODE_KINDS`, `canvasSvgEl`) preserve public surface — the four `window.FlowchartXxx` exports match the v4.9.6 / v4.9.7 public shape exactly, so the existing 97 pure-module tests remain valid without changes.
## Version 4.9.7 (2026-09-14)
### Fix
- **Standalone Flowchart Generator window now loads (was: 'modules not loaded' fatal error).** Each of the four pure modules (`flowchart-shapes.js`, `flowchart-mermaid.js`, `flowchart-store.js`, `flowchart-canvas.js`) ships with a UMD wrapper. The original wrapper assigned `window.FlowchartXxx` only in the `else` branch — i.e. when `module` was undefined. But the renderer runs with `nodeIntegration: true`, so `module` is always truthy in that environment and the `else` branch never ran, leaving `window.FlowchartShapes` / `window.FlowchartMermaid` / `window.FlowchartStore` / `window.FlowchartCanvas` undefined. The standalone window's controller (`src/renderer/flowchart-controller.js`) then aborted with `fatal('Flowchart pure modules not loaded — verify script tags in src/flowchart-generator.html')`.
- Fix: every pure module's UMD wrapper now has a second `if (typeof window !== 'undefined') { window.FlowchartXxx = exported; }` block appended AFTER the CommonJS branch. Both branches can run (the CommonJS branch keeps the legacy sidebar panel working under `require()`; the new branch unconditionally exposes the global in the renderer). The factory IIFE is unchanged, so the public surface of every module is identical to v4.9.6 — no behavioural change.
- New regression guard: 4 source-grep tests in `tests/flowchart-controller.test.js` assert each module's source file contains the `window.FlowchartXxx = exported` assignment so a future refactor can't silently drop the global again.
## Version 4.9.6 (2026-09-14)
### Refactor
- **Flowchart editor is now a standalone window, not a sidebar panel.** Five fix rounds (v4.9.1 → v4.9.5) couldn't make the sidebar panel feel right — at 280 px sidebar with the canvas + preview split to ~175 px each, plus the editor-container hiding 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/ascii-generator.html` + `src/renderer/ascii-controller.js`).
- New `src/flowchart-generator.html` — standalone HTML with its own header, toolbar (Insert at Cursor / Reset), canvas host, and preview host (text-only — the canvas on the left IS the visual preview). All stylesheet `href`s are `src/`-relative — no `../` escape (lesson learned from v4.9.2). Forced light surface (`background: #fafafa !important; color: #1f2328 !important`) on the canvas + preview regardless of the project's body theme, mirroring the v4.9.5 CSS fix that traded theme consistency for guaranteed visibility.
- New `src/renderer/flowchart-controller.js` — pure browser IIFE. Wires the canvas + preview, hydrates from `<userData>/flowchart-session.json` once on mount, persists on every store mutation with a 500 ms debounce. Insert at Cursor wraps the generated `flowchart TD` source in a fenced ` ```mermaid ` block and sends it through the existing `insert-content` IPC channel — same one the renderer.js sidebar panel used. Keyboard shortcuts (Ctrl/Cmd+Z / Ctrl/Cmd+Shift+Z / Delete / Backspace) handled at document level.
- New `openFlowchartGenerator()` in `src/main.js` — `BrowserWindow` (1100×720, parent: mainWindow, `contextIsolation: true, nodeIntegration: false`) launched by an `ipcMain.on('open-flowchart-generator')` listener. Tools menu now has a "Flowchart Generator" entry with accelerator `CmdOrCtrl+Alt+F`.
- New `window.electronAPI.flowchart.*` namespace in `src/preload.js` — `getUserDataPath` / `readFile` / `writeFile` / `insertAtCursor`. Reuses the existing thin IPC handlers (`get-user-data-path`, `read-text-file`, `write-text-file`) which already sandbox writes to `<userData>`.
- 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 (the legacy sidebar panel still loads them via `require()`) and as browser globals (the standalone window loads them via `<script>` tags attached to `window.FlowchartShapes`, etc.). The CommonJS shape is preserved — no behavioural change to the 73 flowchart unit tests in `tests/flowchart-*.test.js`.
### Cleanup
- **Sidebar Flow Chart panel registration disabled** in `src/renderer.js` — the `sidebarManager.registerPanel('flowchart', …)` call and the matching `commandPalette.register('Toggle Sidebar: Flow Chart', …)` entry are now both commented out. The legacy panel implementation (`src/sidebar/flowchart-panel.js`) and its unit tests (`tests/flowchart-panel.test.js`) are kept untouched for rollback — re-enabling is a one-step uncomment in `src/renderer.js`. The unused `flowchartIO` helper that the panel needed was also removed.
### Tests
- New `tests/flowchart-controller.test.js` (10 tests) — verifies the standalone window's HTML doesn't `../`-escape any stylesheet, `bootstrap()` resolves `getUserDataPath` exactly once on mount, reads `<userData>/flowchart-session.json` on mount, hydrates the store from a saved session, wraps Insert-at-Cursor output in a `mermaid` fenced block, Reset clears nodes/edges (with confirm) and persists the empty graph (without confirm). Two regression tests assert that `src/renderer.js` no longer contains a live `sidebarManager.registerPanel('flowchart', …)` call or a live `commandPalette.register('Toggle Sidebar: Flow Chart', …)` entry.
## Version 4.9.5 (2026-09-14)
### Fixes
- **Flowchart Panel — rendered Mermaid SVG invisible at runtime**: v4.9.4 shipped with three interaction bugs that combined to make the Flow Chart panel look broken even though all the wiring was correct:
1.**Render target had zero height.**`.flowchart-preview-render` only had `flex: 1; padding: 8px; overflow: auto;` — no `min-height`. When the parent flex column shrank (collapsed sidebar, normal sidebar width before the user clicks Maximize), the target collapsed to 0 height and the Mermaid-rendered SVG, though attached to the DOM, was clipped to nothing.
2.**Dark-on-dark surfaces.** The canvas host and preview host inherited the project's `body.theme-concreteinfo` dark theme. Mermaid's `dark` theme was selected automatically in `src/renderer.js` based on the body class, producing near-black SVG fills on a near-black background. Node labels "Node" were barely legible.
3.**Selection highlight invisible.**`.flowchart-node.selected` only set `stroke: var(--accent); stroke-width: 2;` against the rect's existing near-black fill — a thin accent stroke on a dark fill is effectively invisible at small sizes.
Fix in three places:
-`src/styles-sidebar.css` — gave `.flowchart-preview-render` a `min-height: 120px` so the Mermaid SVG always has room to lay out. Added a `!important` light background (`#fafafa` / `#1f2328` text) to `.flowchart-canvas-host` and `.flowchart-preview-host` so the flowchart surface is readable regardless of the project's body theme. Forced explicit fills and strokes on `.flowchart-node rect` / `.flowchart-node polygon` / `.flowchart-node text` / `.flowchart-edge` (white fill, dark stroke, dark text). Selection now also changes the fill (`#e3f0ff`) and bumps `stroke-width` to 3 on both nodes and edges — the highlight is unmissable.
-`src/renderer.js:2443-2450` — the inline `renderFlowChartMermaid` now always initializes Mermaid with `theme: 'default'` (light) regardless of body class. Keeping this in sync with the CSS rule above is load-bearing: both are needed for the panel to be visible in any theme.
- **Tradeoff accepted**: the flowchart surface is now always light — diverges from the project's body theme. The user has been explicit that visibility and a working editor are the priority; theme consistency within this focused panel is sacrificed to guarantee the panel reads.
### Tests
-`tests/flowchart-panel.test.js` — new `describe('flowchart-panel: render target sizing (v4.9.5 regression)')` block (3 tests) reading the shipped CSS to assert: (a) `.flowchart-preview-render` has a non-zero `min-height`, (b) `.flowchart-canvas-host` / `.flowchart-preview-host` carry a forced background declaration with `!important`, (c) `.flowchart-node.selected rect/polygon` carry an explicit fill and `stroke-width >= 3`. Reading the stylesheet directly mirrors what the runtime loads via `<link rel="stylesheet">` and sidesteps jsdom's incomplete layout engine.
## Version 4.9.4 (2026-09-14)
### Fixes
- **Flowchart Panel — selection was invisible**: clicking a node or edge updated the canvas's internal `selectedNodeId` / `selectedEdgeId` but never repainted, so the `.flowchart-node.selected` / `.flowchart-edge.selected` CSS highlight only appeared when the user actually dragged (which triggers `store.subscribe` → `render()`). A bare click left the canvas looking unchanged, and the panel's own `selectedNodeId` (used by the panel-scoped Delete/Backspace shortcut) stayed `null`, so Delete on a freshly-clicked node silently no-op'd. Wired `opts.onNodeClick(id)` end-to-end:
-`src/flowchart/flowchart-canvas.js` — added an `opts.onNodeClick` callback parallel to the existing `opts.onEdgeClick`; on click, both branches now call a new surgical `applySelectionHighlight()` that toggles the `.selected` class on the existing `<g data-node-id>` / `<line data-edge-id>` elements without going through `render()` (which would detach the very element the user's pointer is still on, breaking `pointermove`/`pointerup` bubbling during a drag).
-`src/sidebar/flowchart-panel.js` — the panel's `onNodeClick` handler mirrors the id into the panel's `selectedNodeId` (clearing `selectedEdgeId`) so Delete/Backspace routes correctly. Same symmetry was already in place for `onEdgeClick`.
- **Flowchart Panel — narrow sidebar cramped the canvas + preview**: the panel lives in the 280 px sidebar, which split the canvas vs. preview to ~175 px each — too tight to edit a flowchart. Added a "Maximize / Restore" button to the panel toolbar (between the status text and the right edge). Clicking it toggles a `flowchart-takeover` class on `.main-content`:
-`src/styles-sidebar.css` — new `.main-content.flowchart-takeover` rules hide `.editor-container` (`display: none`) and let `.sidebar` / `.sidebar-panel` grow with `flex: 1` so the canvas + preview split the full window width instead of the 280 px sidebar.
- The button label flips between "Maximize" and "Restore", `aria-label` and `title` update, and the button gets an `.active` highlight while takeover is on. `destroy()` clears the class so leaving the panel doesn't leave the editor hidden for the rest of the session.
- The class lookup walks up from the panel container to the nearest `.main-content` ancestor (with a `document.querySelector('.main-content')` fallback) so the panel doesn't need to know whether the sidebar lives inside `#sidebar` or any future container.
### Tests
-`tests/flowchart-panel.test.js` — added two new `describe` blocks (8 tests total):
- "selection wiring (canvas click → panel state + SVG class)": clicking a node applies `.selected` to the matching `<g>`, clicking a second node moves `.selected` from the first to the second, clicking an edge applies `.selected` to the matching `<line>`, and a regression test verifying Delete removes a freshly-clicked node (was broken in v4.9.3 because the panel's `selectedNodeId` was never updated by canvas clicks).
- "maximize / takeover": the maximize button is exposed in the toolbar, clicking it toggles `.flowchart-takeover` on `.main-content` and flips the button label/active class, and `destroy()` clears the class so the editor stays usable.
- New `mountWithMainContent()` helper wraps the panel container in a fake `.main-content` (mirroring the real DOM layout in `src/index.html:2341`) so the takeover's class-toggling is observable from the test.
## Version 4.9.3 (2026-09-14)
### Fixes
- **Flowchart Panel — preview pane accumulated raw Mermaid source**: when the user fired several `addNode` mutations within the 250 ms preview debounce, `mermaid.run({ nodes: [div] })` is async, so the previous render's `<div class="mermaid">` (still carrying the source text) was sitting 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. Fixed in `src/renderer.js:2423-2449`: switched the inline `renderFlowChartMermaid` to `replaceChildren()` (more idiomatic than `innerHTML = ''`) and added a per-target `WeakSet` in-flight tracker that keeps the previous render's closure from racing the new render — its eventual `element.innerHTML = svg` is harmless on a detached node, and the new render always starts from a clean slate.
### Tests
-`tests/flowchart-panel.test.js` — added a second regression test (`preview-source pre never duplicates across debounced mutations even with in-flight mermaid.render`) that fires 7 mutations at 10 ms intervals (well inside the 250 ms debounce), uses a `renderMermaid` mock that mirrors the real mermaid.run closure (captures the input div, asynchronously sets `innerHTML = svg` on it regardless of DOM connection), and asserts the `<pre>` contains exactly one copy of the latest source and the render target holds exactly one `<div class="mermaid">` child whose first element child is the latest `<svg>`.
## Version 4.9.2 (2026-09-14)
### Fixes
- **Standalone ASCII Art Generator — broken stylesheet path**: `src/ascii-generator.html:7` linked `<link rel="stylesheet" href="../fonts.css" />`. The HTML loads via `BrowserWindow.loadFile(path.join(__dirname, 'ascii-generator.html'))` where `__dirname` is `src/`, so `../fonts.css` escaped the `src/` directory and resolved to a non-existent `<project>/fonts.css`. Changed to `fonts.css` (same file, src/-relative — mirrors `src/index.html:29`). The window rendered without its font rules, leaving the header in the fallback system stack.
- **Standalone ASCII Art Generator — dead Box/Templates UI**: the controller (`src/renderer/ascii-controller.js`) shipped three mode tabs (`Text Banner` / `Box-Frame` / `Templates`), 18 `.template-btn[data-template]` buttons, and a Box form (`#box-text` / `#box-style` / `#box-padding`) but wired none of them. Clicking any tab or button was a no-op. Wired all of them:
-`setMode(mode)` toggles `.active` on the right `.mode-tab` and the matching `.mode-section` (`text-mode` / `box-mode` / `templates-mode`).
- Template buttons call `api.generate({ text: '', font: 'template:<id>' })` so the orchestrator owns template content; preview updates and the button gets `.active`.
- Box mode renders the user text with a border using the chosen style (`single` / `double` / `rounded` / `bold` / `ascii`) and padding, exposed as a pure `window.ASCIIBoxRenderer.renderBox(text, style, padding)` helper.
- New `tests/ascii-controller.test.js` — 6 tests covering the stylesheet path (no `..` escape), pure box renderer (single + ascii styles), mode-tab switching (Box and Templates), and template button → preview wiring.
## Version 4.9.1 (2026-09-14)
### Fixes
- **Flowchart Panel save failed**: `getUserDataPath()` IPC was not awaited in `src/renderer.js:2439-2449`, so the persistence path was computed as `"[object Promise]/flowchart-session.json"` and rejected by the `write-text-file` userData sandbox. Pre-resolved the path on panel register and cached it; persistence (read and write) now works correctly.
- Vim keybindings (View → Vim Mode, persisted, live toggle via CodeMirror Compartment)
- Snippet Tab-expansion: type a snippet name and press Tab to insert it
- Zen Mode word-goal setter (the HUD progress bar finally has UI)
#### Export / Conversion
- **Real PDF encryption**: pdf-lib swapped for @cantoo/pdf-lib — encrypt/decrypt/permissions now actually work (UI auto-enables via the capability probe)
- **XLSX export**: markdown tables → native Excel workbook, one sheet per table (no Pandoc needed)
- **ODT headers/footers + page size**: real ODF styles.xml patching replaces the empty stub
- **Local PlantUML rendering**: diagrams render on-machine via the `plantuml` CLI when installed; plantuml.com stays as fallback
- **KaTeX bundled locally** (CSS + fonts): math renders offline, no CDN calls
- Writing heatmap (GitHub-style 30-day grid) in the writing-studio Goals panel
#### Platform
- Quick Note global scratchpad (Ctrl+Alt+Q, works when unfocused; appends to `notes/quick-notes.md`)
- Deep link protocol `markdownconverter://open?path=…`
- REPL confirmation dialog before first code execution per language per session (unsandboxed-execution guard rail)
- Writing-studio's four sidebar panels (Manuscript/Goals/Snapshots/Proofread) are now actually wired with rail icons
- Plugin sidebar panels get automatic rail icons via `registerPanel({icon})`
### Bug Fixes
- `Ctrl+Shift+P` collision: PDF (Enhanced) export now `Ctrl+Alt+Shift+P`; Command Palette keeps `Ctrl+Shift+P`
- Universal Converter's Pandoc tool no longer always reports "not installed" (`checkConverterAvailable` gained a pandoc case with bundled-binary check)
- CLI headless export: removed dangling `--css` / `--reference-doc` flags that made pandoc exit with an error; `--self-contained` replaced with `--standalone` (Pandoc 3.x)
- Removed dead "Open Export Options Dialog…" button from the converter dialog
- Removed duplicate `styles-zen.css` include
### Security
- AI provider requests carry size caps (200KB prompt), timeouts (120s), and user-safe error surfaces
- Version-history reads validate ids against traversal; history listing requires a valid document path
- PlantUML local rendering removes the diagram-text exfiltration path when a local CLI exists (CVE-MC-007 follow-up)
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.