From 855dfcd9f561f088be25505a5e0dd4f47211c3f4 Mon Sep 17 00:00:00 2001 From: Amit Haridas Date: Wed, 30 Sep 2026 21:22:22 +0530 Subject: [PATCH] feat(flowchart): replace window.prompt with inline modal (C1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/flowchart/flowchart-canvas.js | 8 +++-- src/renderer/flowchart-controller.js | 51 ++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/flowchart/flowchart-canvas.js b/src/flowchart/flowchart-canvas.js index cd9bc83..632d2d1 100644 --- a/src/flowchart/flowchart-canvas.js +++ b/src/flowchart/flowchart-canvas.js @@ -223,12 +223,16 @@ function createCanvas(container, store, opts = {}) { ev.preventDefault(); return; } - // Click on empty canvas: add a process node at the click point. + // Click on empty canvas: hand off to the controller via onEmptyClick so it + // can prompt the user for the shape kind + label. v4.13.0 — the canvas + // no longer auto-creates a process node with placeholder label "Node". if (ev.target === svg || ev.target === nodesLayer || ev.target === edgesLayer) { const p = getSvgPoint(ev.clientX, ev.clientY); const x = Math.max(0, p.x - DEFAULT_WIDTH / 2); const y = Math.max(0, p.y - DEFAULT_HEIGHT / 2); - store.addNode({ kind: 'process', x, y, label: 'Node' }); + if (typeof opts.onEmptyClick === 'function') { + opts.onEmptyClick(x, y, ev); + } ev.preventDefault(); } } diff --git a/src/renderer/flowchart-controller.js b/src/renderer/flowchart-controller.js index 187c7f1..26c8fbe 100644 --- a/src/renderer/flowchart-controller.js +++ b/src/renderer/flowchart-controller.js @@ -59,6 +59,8 @@ const { create: createStore } = window.FlowchartStore; const { createCanvas } = window.FlowchartCanvas; const { toMermaid } = window.FlowchartMermaid; + const { promptInline, confirmInline: _confirmInline } = window.FlowchartModals || {}; + void _confirmInline; // exposed by the bundle for future use (e.g. reset confirmation) // FlowchartShapes is intentionally unused here but its presence is // required for createCanvas() to function; the guard above guarantees it. @@ -66,6 +68,9 @@ const PERSIST_DEBOUNCE_MS = 500; const PERSISTENCE_FILENAME = 'flowchart-session.json'; + const NODE_KIND_OPTIONS_TEXT = 'process, decision, terminator, subroutine, document'; + const EDGE_KIND_OPTIONS_TEXT = 'solid, dotted, thick'; + let _userDataPath = null; let _persistenceFile = null; let _previewTimer = null; @@ -132,14 +137,25 @@ }); _canvas = createCanvas(els.canvasHost, _store, { - onEdgeClick: (edgeId) => { + onEdgeClick: async (edgeId) => { const edge = _store.getGraph().edges.find((e) => e.id === edgeId); if (!edge) return; - const nextKind = window.prompt('Edge kind (solid, dotted, thick):', edge.kind); + // v4.13.0 — replace window.prompt() with the inline modal helper. + // Two sequential prompts: kind first (with the current value as + // default), then label. + const nextKind = await promptInline({ + title: 'Edge Style', + message: `Style (${EDGE_KIND_OPTIONS_TEXT}):`, + defaultValue: edge.kind, + }); if (nextKind && ['solid', 'dotted', 'thick'].includes(nextKind)) { _store.setEdgeKind(edgeId, nextKind); } - const nextLabel = window.prompt('Edge label (empty to clear):', edge.label || ''); + const nextLabel = await promptInline({ + title: 'Edge Label', + message: 'Empty to clear:', + defaultValue: edge.label || '', + }); if (nextLabel !== null) { _store.setEdgeLabel(edgeId, nextLabel); } @@ -148,12 +164,33 @@ // Canvas already paints the .selected highlight; nothing else // needed here for selection state. }, - onShapeMenu: (nodeId) => { - const next = window.prompt( - 'New shape (process, decision, terminator, subroutine, document):' - ); + onShapeMenu: async (nodeId) => { + const node = _store.getGraph().nodes.find((n) => n.id === nodeId); + if (!node) return; + const next = await promptInline({ + title: 'Change Shape', + message: `New shape (${NODE_KIND_OPTIONS_TEXT}):`, + defaultValue: node.kind, + }); if (next) _store.setNodeKind(nodeId, next); }, + onEmptyClick: async (x, y) => { + // v4.13.0 — canvas click no longer auto-creates a process node + // with literal label "Node". User picks the shape kind first. + const kind = await promptInline({ + title: 'Add Node', + message: `Shape (${NODE_KIND_OPTIONS_TEXT}):`, + defaultValue: 'process', + }); + if (!kind) return; + const label = await promptInline({ + title: 'Node Label', + message: 'Label (empty for "Node"):', + defaultValue: '', + }); + const finalLabel = label && label.length > 0 ? label : 'Node'; + _store.addNode({ kind, x, y, label: finalLabel }); + }, }); _store.subscribe(() => {