refactor(flowchart): standalone window (Cmd+Alt+F) replaces sidebar panel; v4.9.6

Five fix rounds (v4.9.1 → v4.9.5) couldn't make the sidebar flowchart
panel feel right — at 280 px sidebar with canvas + preview cramped to
~175 px each, plus the editor-container hide/show dance the
maximize/restore toggle required, the panel kept presenting as
cramped and unreliable at runtime. Strategy pivot: the flowchart
editor now lives in its own BrowserWindow, matching the ASCII Art
Generator pattern.

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

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

All 1122 tests pass; lint + format clean.

Amit Haridas
This commit is contained in:
2026-09-14 23:50:25 +05:30
parent 9557c0af4d
commit 5f846d1e5f
13 changed files with 848 additions and 21 deletions
+19 -2
View File
@@ -13,7 +13,12 @@
'use strict';
const { DEFAULT_WIDTH, DEFAULT_HEIGHT, shapeSvg, SHAPE_KINDS } = require('./flowchart-shapes');
// v4.9.6 — CommonJS sibling import when running under Node (the renderer.js
// sidebar panel still uses require()), browser global fallback when loaded as
// a <script> tag in the standalone window (no nodeIntegration).
const shapesModule =
(typeof window !== 'undefined' && window.FlowchartShapes) || require('./flowchart-shapes');
const { DEFAULT_WIDTH, DEFAULT_HEIGHT, shapeSvg, SHAPE_KINDS } = shapesModule;
const SVG_NS = 'http://www.w3.org/2000/svg';
@@ -322,4 +327,16 @@ function createCanvas(container, store, opts = {}) {
return { destroy, getSvg: () => svg };
}
module.exports = { createCanvas, SHAPE_KINDS };
// v4.9.6 UMD wrapper — same CommonJS export shape + browser global
// (window.FlowchartCanvas) so the standalone window's controller can load
// this module via <script> tag without nodeIntegration.
(function (root, factory) {
const exported = factory();
if (typeof module === 'object' && module.exports) {
module.exports = exported;
} else {
root.FlowchartCanvas = exported;
}
})(typeof window !== 'undefined' ? window : globalThis, function () {
return { createCanvas, SHAPE_KINDS };
});
+13 -1
View File
@@ -90,4 +90,16 @@ function toMermaid(graph) {
return lines.join('\n');
}
module.exports = { toMermaid, escapeLabel, nodeDeclaration, edgeDeclaration };
// v4.9.6 UMD wrapper — same CommonJS export shape + browser global
// (window.FlowchartMermaid) so the standalone window's controller can load
// this module via <script> tag without nodeIntegration.
(function (root, factory) {
const exported = factory();
if (typeof module === 'object' && module.exports) {
module.exports = exported;
} else {
root.FlowchartMermaid = exported;
}
})(typeof window !== 'undefined' ? window : globalThis, function () {
return { toMermaid, escapeLabel, nodeDeclaration, edgeDeclaration };
});
+24 -8
View File
@@ -55,11 +55,27 @@ function shapeSvg(kind, x, y, width, height) {
}
}
module.exports = {
shapeSvg,
SHAPE_KINDS,
DEFAULT_WIDTH,
DEFAULT_HEIGHT,
LABEL_PADDING_X,
LABEL_PADDING_Y,
};
// v4.9.6 UMD wrapper — exposes the same surface as a CommonJS module
// (used by src/sidebar/flowchart-panel.js via require()) AND as a browser
// global (used by src/renderer/flowchart-controller.js via <script> tag).
// The standalone BrowserWindow runs with contextIsolation:true +
// nodeIntegration:false, so the renderer cannot require() these modules;
// loading them as <script> tags in src/flowchart-generator.html attaches
// them to window.FlowchartShapes.
(function (root, factory) {
const exported = factory();
if (typeof module === 'object' && module.exports) {
module.exports = exported;
} else {
root.FlowchartShapes = exported;
}
})(typeof window !== 'undefined' ? window : globalThis, function () {
return {
shapeSvg,
SHAPE_KINDS,
DEFAULT_WIDTH,
DEFAULT_HEIGHT,
LABEL_PADDING_X,
LABEL_PADDING_Y,
};
});
+13 -1
View File
@@ -258,4 +258,16 @@ function create(io) {
};
}
module.exports = { create, NODE_KINDS, EDGE_KINDS };
// v4.9.6 UMD wrapper — same CommonJS export shape + browser global
// (window.FlowchartStore) so the standalone window's controller can load
// this module via <script> tag without nodeIntegration.
(function (root, factory) {
const exported = factory();
if (typeof module === 'object' && module.exports) {
module.exports = exported;
} else {
root.FlowchartStore = exported;
}
})(typeof window !== 'undefined' ? window : globalThis, function () {
return { create, NODE_KINDS, EDGE_KINDS };
});