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
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
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
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 — 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
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
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