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