Skip to content

feat: finish Mermaid editor demo - #48

Open
samuelgja wants to merge 14 commits into
mainfrom
feat/mermaid-editor-finish
Open

samuelgja wants to merge 14 commits into
mainfrom
feat/mermaid-editor-finish

Conversation

@samuelgja

@samuelgja samuelgja commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

Finishes the mermaid-editor demo (mermaid-editor/react) — Mermaid's own parser reads the flowchart, JointJS+ draws it, and every edit on the canvas writes real Mermaid back into the source.

Editing

  • All 36 Mermaid v11 shapes in the picker (classic delimiters and @{ shape: … }), open by default; image nodes via @{ img }; subgraphs; hyperlinks (click).
  • Node toolbar: shape, fill, text colour, bold/italic, border style and colour, hyperlink, image. Edge toolbar on link click: line pattern, arrow heads (incl. both ends), colour (linkStyle), curve (interpolate), marching-dash animation (e1@{ animate: true }).
  • A "+" on the hovered/selected shape, placed by layout direction (below in TB, right in LR, …): click to append a connected step (selected at once), or DRAG it onto another shape to add an edge (Escape cancels). "+ Shape" builds a diagram from a bare flowchart TD; layout direction switch (TB/BT/LR/RL) rewrites the header and re-frames.
  • Delete / Backspace remove the selected nodes or edge (a trash button in each popup too); removal rewrites the source — chains cut, fans unrolled, edge ids and linkStyle indices kept consistent, style/class/click lines cleaned, subgraph ids unwrapped (21 tests). Canvas edits carry their own undo stop.
  • Monaco replaces CodeMirror: find/replace, multi-cursor, undo-preserving external edits, a Monarch Mermaid grammar, themed light/dark, and completions (statements and snippets, directions, arrow spellings, the diagram's own node ids, every @{ shape: } name).
  • Manual layout toggle with orthogonal routing; .mmd and SVG export.

Accessibility

  • One focus colour for controls and canvas selection (not the red accent); visible rings on swatches and toggles.
  • Source pane is a focus stop (Tab → frame, Enter → edit, Escape → out); nodes are keyboard-reachable (focus selects, Enter renames, Tab dives into the toolbar, Escape closes and returns focus); arrow keys walk the shape pickers; keyboard "+ Shape" focuses the new node's controls.
  • Pressed/checked chips (shape, line style, direction, layout) use a tinted fill instead of an accent-red ring, so the only ring on screen is the blue focus ring; custom colour inputs and the rename box use the same blue.
  • Toolbar flips/slides to stay inside the VISIBLE canvas (the scroller's box, not the paper's — a scrolled canvas used to open it under the source pane); the geometry is a pure function with node --test coverage; on phones its rows wrap to the screen width; mobile header no longer overlaps its logo and the example picker takes the free width; example picker opens on Enter.

Design — a light refresh toward the look of mermaid.ai: navy-tinted OKLCH neutrals (nothing pure white or black), node and edge labels in the same monospace face as the source, dark nodes with a light outline in dark mode, teal gutter and strings in the editor. The node and edge toolbars are one flat panel: hairline dividers instead of boxes inside boxes, one chip size, and delete as a quiet ghost button alone at the end of the last row that turns red only under the pointer or focus. Hover is a lift, not the red accent; 150 ms colour transitions, off under reduced motion. Plain rectangles get softly rounded corners; a node's own fill is blended into the canvas in dark mode and its authored color gives way to the theme's text (true dark mode), unchanged in light mode; the "+" glyph is optically centred. PRODUCT.md and DESIGN.md in the demo folder record the register, palette and rules (inferred, please correct).

Dark mode — navy rather than grey, a few steps up from black (canvas oklch(0.215 0.03 262), panels oklch(0.25 0.03 262), Monaco theme mirrored): easier on the eye over a long look, still unmistakably dark. Text, gutter and muted labels keep WCAG AA on both themes.

Robustness — arrow edits read the current token from the source (rapid clicks no longer corrupt it); the Lezer grammar mis-lexes -.->, so edge spans are located by regex between grammar-located ids (edge-spans.ts, Node-tested: parallel edges and edges into id@{ shape: … } nodes each resolve to their own token); a non-flowchart source is refused rather than corrupted.

Examples — "All node shapes" shows all 49 shapes (classic delimiters and every v11 @{ shape }) as a branching tree; picker icons are each shape's own node scaled down (verified against a 45-pair icon/node contact sheet), comment shapes get typographic { } { } icons.

Verified end-to-end in Chrome (desktop and 390px mobile), including a scripted keyboard walk.

Motivation and Context

Completes the demos.jointjs.com/mermaid-editor demo to a shippable state, matching the mermaid.live feature set that is relevant to a flowchart-only editor, and closes the review notes on editing, mobile and keyboard accessibility.

Screenshots (if appropriate):

mermaid-editor/screenshot.png in the repo shows the finished editor.

- Added a hit area for the 'text' shape to ensure it can be clicked and selected.
- Improved styling variants for filled and open shapes in SVG rendering.
- Introduced new CSS variables for group styling and enhanced accessibility check styles.
- Updated the editor's download button and tool layout for better usability.
- Implemented a new accessibility check component with animations.
- Enhanced node styling to support new properties like hyperlink and tooltip.
- Updated parsing logic to handle new shape definitions and animations in flowcharts.
- Added support for subgraphs and animated edges in the flowchart parser.
- Extended the node data structure to include parent-child relationships for subgraphs.
@samuelgja
samuelgja requested a review from kumilingus September 3, 2026 06:30
…editor

- edge toolbar on link click: line pattern, arrow heads, colour, curve and
  marching-dash animation, each written back into the Mermaid source
  (arrow token rewrite, linkStyle statements, edge id @-blocks)
- image nodes via @{ img } with a URL editor in the node toolbar
- text colour control writing style color: declarations
- all 36 v11 shapes in the picker, manual layout with orthogonal routing
- Monaco replaces CodeMirror: find/replace, multi-cursor, undo-preserving
  external edits, Monarch mermaid grammar, themed light/dark
…ault, standalone add-step

- layout direction (TB/BT/LR/RL) switchable from the canvas actions,
  rewriting the flowchart header; the camera re-frames after the reflow
  (fit requests are now consumed after the next successful layout pass,
  not against the pre-parse geometry)
- the extended shape grid shows by default instead of hiding behind the
  disclosure; the toggle only folds it away
- add-step is a labelled standalone action, set apart from the styling
  toggles
… header

- a bare `flowchart TD` is a valid empty diagram, and a "+ Shape" action
  appends top-level unconnected nodes, so a chart can be built with no
  typing at all; it refuses a source that is not a flowchart rather than
  corrupting it
- the add-step "+" moved out of the node popup to a button under the
  hovered or selected shape, matching the official editor
- header no longer overlaps its own logo on a phone: the example picker
  can shrink, the label and oversized title collapse under 720px, and the
  canvas actions wrap; the select carries its own accessible name
- a document write no longer reports its recovered caret position as a
  user selection, which had deselected freshly added nodes
…button

- the toolbar opens below the node when the space above cannot hold it,
  so selecting a node near the top of the canvas no longer draws the
  panel off-screen
- the add-step button's centre sits on the node's bottom edge instead of
  hanging below it
- drop the logo hover state
…olbars

- one focus colour for every control and for canvas selection, distinct
  from the red accent (a red ring on a node read as an error); swatches
  and toggles get a visible offset ring
- the source pane is a focus stop: Tab lands on the frame, Enter enters
  Monaco, Escape leaves it again
- nodes are keyboard-reachable: focus selects (opening the toolbar),
  Enter renames in place, Tab dives into the node's toolbar, Escape
  closes it and hands focus back to the node
- arrow keys walk the shape pickers; adding a shape from the keyboard
  focuses the new node's controls
- the toolbar slides inward when a node sits near the canvas edge
- the example picker opens on Enter and no longer crowds its arrow
… rings

- dark theme a few steps off near-black: canvas, panels, borders, overlays and the Monaco theme move together
- pressed/checked chips (shape, line style, direction, layout) use a tinted fill instead of an accent ring, so the only ring on screen is the blue focus ring
- custom colour inputs and the rename box use the focus-ring colour too
- phone header: the example picker takes the free width instead of clipping its value
- placement clamps to the scroller's box, not the paper's: inside a scroller the paper runs past the viewport, so a node near the left edge of a scrolled canvas opened its toolbar under the source pane
- placement geometry extracted to a pure function with node:test coverage (left/right clamp, flip below the header)
- phones: the toolbar caps at the screen width and its rows wrap instead of overflowing
… "+", every shape in the example

- the "+" under a shape can be dragged onto another shape to add an edge; a click still appends a connected step, a drop on empty canvas appends one too, Escape cancels; the press cancels the pending hover clear so the button (and its pointer capture) survives the drag
- the "+" follows the layout direction: bottom edge in TB, top in BT, right in LR, left in RL
- a step added from the "+" is selected at once, with its toolbar open
- `@{ … }` blocks are blanked before the Lezer parse: the grammar predates them, an id followed by `@{` was taken for an edge id and a `(` inside a label desynced the rest of the line, so edges into such nodes edited the wrong edge or nothing; covered by node:test, including every arrow of the new example
- a step added from the keyboard "+" moves focus into the new node's toolbar instead of dropping it on <body>; the drop-target highlight is dashed and wins over inline Mermaid styles; the "+" takes no touch scrolling
- "All node shapes" example shows all 49 shapes as a branching tree instead of a chain
- picker icons are the shapes' own nodes scaled down (natural size for a short label, non-scaling stroke), so tag corners, header rules, stacks, cylinder caps and circles look like the canvas node; brace / comment shapes get typographic `{` `}` `{ }` icons and the outline-less text shape an `Aa`, so no picker slot is blank
…not where it sits

The "+" follows the layout direction, so `node-add-below` and its "hangs under" comments were wrong for LR and RL.
…igner touches

- Delete / Backspace remove the selected nodes or edge (canvas and node focus
  only, never inside the source editor or a field); the node popup's "…"
  toggle gives way to a Delete button and the extended grid stays open; the
  edge popup gets one too. Removal rewrites the source: chains are cut and
  fans unrolled, edge ids and `linkStyle` indices follow, `style` / `class` /
  `click` lines lose the id, a subgraph id unwraps its block (21 tests)
- canvas edits get their own undo stop, so Cmd+Z in the editor no longer
  swallows the typing before them
- completions in the source editor: statements and snippets at line start,
  directions after the header, arrow spellings after a node, the diagram's own
  node ids after an arrow, every shape name inside `@{ shape: }` (7 tests);
  the shape vocabulary is shared with the picker
- designer notes: plain rectangles get softly rounded corners; a node's own
  `fill` is blended into the canvas in dark mode with the theme's text colour
  (true dark mode), unchanged in light mode; the "+" glyph sits centred
…ars, quiet delete

Navy-tinted OKLCH neutrals on both themes, one monospace face for the source
and the diagram's labels, dark nodes with a light outline in dark mode, teal
gutter and strings in the editor. The node and edge toolbars become one flat
panel: hairline dividers instead of nested wells, one chip size, and the
delete button set apart at the end of the last row, muted until hovered or
focused. Pressed chips carry their state in the tinted fill alone (the accent
glyph on the tint fell under 3:1); hover is a lift rather than the accent;
colour transitions are 150 ms through one `--motion` token, zeroed under
reduced motion. On a phone the last toolbar row drops its hairlines so it
fits the screen in one line.

Styled nodes: a class colour picked for the class's fill now travels with the
fill (`ownLabel`), so light mode shows it as written and dark mode swaps it
for the theme's text along with the blended fill; a node's own `color:` is
honoured in both themes, which keeps the text colour picker working in dark.
Derived label colours are theme tokens instead of fixed hex.

Monaco themes mirror the tokens in hex and hold the syntax palette (the
unused `--syntax-*` mirrors are gone); comment tokens, the selected-token
mark and subgraph outlines raised to WCAG AA / 3:1 on both themes.
PRODUCT.md and DESIGN.md record the register, tokens and rules for the demo
(inferred, to be confirmed). Repo screenshot refreshed.
…right margin

The native select arrow sits on the border with no margin and cannot be
moved, so the select paints a small chevron itself, 10 px from the edge,
in a grey that reads on both themes. Verified in Chrome, light and dark.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant