Skip to content

feat(jetbrains): render all Mermaid diagram types in chat - #13569

Merged
kirillk merged 2 commits into
mainfrom
swift-pebble
Aug 30, 2026
Merged

feat(jetbrains): render all Mermaid diagram types in chat#13569
kirillk merged 2 commits into
mainfrom
swift-pebble

Conversation

@kirillk

@kirillk kirillk commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Issue

No issue filed. Follow-up to a chat request asking for JetBrains support of all Mermaid diagram types; this PR was scoped and implemented directly from that conversation.

Context

The JetBrains plugin's in-process Kotlin Mermaid engine (packages/kilo-jetbrains/frontend/.../ui/diagram/) only rendered flowcharts and sequence diagrams. Every other diagram type — including ones Type already detected (class, state, ER, gantt, pie) — fell back to a soft "unsupported diagram" note over the raw source. JCEF/webview rendering is off-limits for this plugin (breaks remote dev split mode), so this had to be native Swing/Kotlin.

This PR extends the engine to draw all 22 Mermaid diagram types natively: class, state, ER, gantt, pie, user journey, quadrant, requirement, git graph, C4 (all five headers), mindmap, timeline, sankey, XY chart, block, packet, kanban, architecture, radar, and treemap.

Implementation

  • Fidelity: core syntax per type (the statements shown in Mermaid's own docs), not full grammar coverage. Unknown statements inside a supported type are parsed-and-discarded when they're known-but-out-of-scope (styling, click, UpdateElementStyle, ...) or silently skipped otherwise — matching how the existing Flow/Seq parsers already behave.
  • New shared primitives (Art.kt): a Mark.Sector for pie/radar wedges, and optional tone/soft fields on fill-bearing marks for categorical chart colors and translucent fills. Existing marks keep their old toString() when these are unset, so flowchart/sequence snapshot tests are untouched. Head gained UML/ER arrow variants (Triangle, Diamond, DiamondFilled, Crow, Bar, CircleOpen).
  • Shared helpers in mermaid/: Sheet (mark collection, origin normalization, text-fit/word-wrap), Layered (a small deterministic graph layout — longest-path ranks with cycle detection, one barycenter sweep each way — reused by class/state/ER/requirement/C4 instead of touching FlowLayout, so flowchart geometry is guaranteed unchanged), Scopes (LCA-based edge re-anchoring for composite states and C4 boundaries), Axis (nice-number chart ticks), Lex (bracket/quote-aware arg splitting and tokenizing).
  • 20 new engines, one file per type, each a self-contained parse+layout+draw pipeline dispatched from Mermaid.kt. Notable tradeoffs: mindmap uses a tidy right-growing tree instead of mermaid's radial layout; treemap uses alternating slice/dice instead of squarified; sankey bands are smoothstep curves sampled into a Poly rather than a new curve primitive; gantt date math is pinned to UTC/Locale.ROOT for determinism.
  • C4 covers all five headers (C4Context/Container/Component/Dynamic/Deployment) through one renderer.
  • Everything stays off-EDT and cooperatively cancellable (ensureActive between phases/lines), and respects the existing Spec.limits node/edge caps.
  • Types outside the 22 (ZenUML, and 11.9+ additions like Venn/Wardley/Cynefin) still show the existing "unsupported" note rather than an error.

Screenshots / Video

Diagram Screenshot
Flowchart Flowchart
Sequence Sequence
Class Class
State State
Entity Relationship Entity Relationship
User Journey User Journey
Gantt Gantt
Pie Pie
Quadrant Quadrant
Requirement Requirement
Git Graph Git Graph
C4 C4
Mindmap Mindmap
Timeline Timeline
Sankey Sankey
XY Chart XY Chart
Block Block
Packet Packet
Kanban Kanban
Architecture Architecture
Radar Radar
Treemap Treemap

How to Test

Manual/local verification

  • Agent-run ./gradlew typecheck from packages/kilo-jetbrains/ — passes.
  • Agent-run ./gradlew test from packages/kilo-jetbrains/ — full suite passes, including the extended diagram test suite (ConformanceTest, InvariantTest, TypeTest, SerializeTest, ScenePainterTest, the new EnginesTest with ~30 targeted cases per diagram family, and MdViewDiagramTest).
  • Agent-added corpus: one .mmd file per new diagram type under frontend/src/test/resources/diagram/, taken verbatim from Mermaid's documented examples, run through ConformanceTest's four contracts (non-empty finite scene, correct detected type, deterministic rendering, no empty text labels) and InvariantTest's bounds/no-overlap checks under both fake and real AWT font metrics.

Reviewer test steps

  1. From packages/kilo-jetbrains/, run ./gradlew runIde.
  2. Open the Kilo chat sidebar and send a message containing multiple ```mermaid fences, one per diagram type (class, state, erDiagram, gantt, pie, journey, quadrantChart, requirementDiagram, gitGraph, C4Context, mindmap, timeline, sankey-beta, xychart-beta, block-beta, packet-beta, kanban, architecture-beta, radar-beta, treemap-beta).
  3. Confirm every fence renders a diagram inline instead of the "unsupported diagram" note.
  4. Toggle the IDE theme between a dark and a light Look and Feel and confirm the diagrams re-render with theme-appropriate colors.
  5. Click "Open in Editor" on one diagram and confirm the Diagram/Source tab split still works.

Blocked checks and substitute verification

  • Could not run the sandboxed IDE (runIde) myself in this environment — no display/VNC available in the dev container. Substitute verification: the full Gradle test suite (parse, layout, invariant, serialization, and scene-painter tests). A human reviewer ran runIde locally and captured the screenshot table above.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Get in Touch

Extend the in-process Kotlin Mermaid engine to cover every diagram
type Type can detect, not just flowcharts and sequence diagrams:
class, state, ER, gantt, pie, user journey, quadrant, requirement,
git graph, C4 (all five headers), mindmap, timeline, sankey, XY
chart, block, packet, kanban, architecture, radar, and treemap.

Each type gets its own parser + layout file under mermaid/, sharing
a new Sheet (mark collection/normalization), Layered (deterministic
graph layout for the class/state/er/requirement/C4 family), Scopes
(composite/boundary edge re-anchoring), Axis (chart ticks), and Lex
(bracket/quote-aware tokenizing) so the per-type files stay small.

The Art model gains a Sector mark for pie wedges plus optional
tone/soft fields for categorical chart colors, and Head gains UML/ER
arrow variants (Triangle, Diamond, Crow, Bar, ...). Existing marks
keep their prior toString() output when the new fields are unset, so
flowchart/sequence snapshots are unchanged.

Unsupported types (zenuml, venn, ...) keep the existing soft
"unsupported" note over the source rather than an error.
@kilo-code-bot

kilo-code-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (9 files)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Packet.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Scopes.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/C4Dg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Layered.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Sankey.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Radar.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/ScenePainter.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/StateDg.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/diagram/mermaid/EnginesTest.kt
Previous Review Summary (commit 57b0d10)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 57b0d10)

Status: 8 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 3
WARNING 5
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Packet.kt 52 Bit-row loop hangs forever on Int overflow
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Scopes.kt 39 Parent-cycle walk never terminates
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/C4Dg.kt 79 Re-opened boundary recurses until StackOverflowError

WARNING

File Line Issue
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Layered.kt 37 Empty scope throws NoSuchElementException
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Layered.kt 40 Rows painted in insertion order, not rank order
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Sankey.kt 73 Over-limit sankey returns a blank success
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Radar.kt 156 coerceIn throws when roof < min
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/ScenePainter.kt 130 Outline heads inherit dashed edge stroke
Files Reviewed (64 files)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Packet.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Scopes.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/C4Dg.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Layered.kt - 2 issues
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Sankey.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Radar.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/ScenePainter.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/Art.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/Palette.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/Type.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/ui/DiagramTheme.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Mermaid.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Sheet.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Lex.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Axis.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/ClassDg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/StateDg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/ErDg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/ReqDg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Gantt.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Pie.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Journey.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Quadrant.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/XyChart.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Timeline.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/GitDg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Mindmap.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/BlockDg.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Kanban.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Arch.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/Treemap.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/diagram/mermaid/SeqLayout.kt
  • .changeset/jetbrains-mermaid-all-diagrams.md
  • tests (CancelTest, ConformanceTest, EnginesTest, InvariantTest, ScenePainterTest, corpus .mmd files)

Fix these issues in Kilo Cloud


Reviewed by grok-4.6 · Input: 225K · Output: 16.5K · Cached: 442.2K

Review guidance: REVIEW.md from base branch main

Addresses PR review. Three of these were unrecoverable rather than
merely wrong: the loops run outside any suspend point, so the render
timeout could not break them, and a StackOverflowError is an Error
that Diagrams.draw's catch(Exception) never converts to Out.Err.

- Packet: bit indexes are parsed with toIntOrNull and capped, and row
  splitting checks cancellation. A field ending at Int.MAX_VALUE used
  to overflow the row arithmetic into an endless loop.
- Scopes.open rejects nesting a scope inside itself and returns a
  syntax error; StateDg and C4Dg surface that instead of looping
  (state) or recursing to a StackOverflowError (C4 boundary). C4 also
  rejects duplicate element/boundary ids that caused double-draws.
- Layered lays an empty scope out as an empty frame instead of
  throwing, so 'state Empty { }' renders.
- Layered iterates rows in rank order. They are keyed by rank but
  filled in declaration order, so 'Circle --|> Shape' drew the child
  above the parent.
- Sankey returns Fault.Limit when unique nodes exceed the cap instead
  of a blank successful scene.
- Radar orders min/max before clamping, so all-negative curves no
  longer hand coerceIn an empty range.
- ScenePainter resets to a solid stroke before drawing heads, so
  outline heads on dashed edges (class realization, dashed ER) stop
  rendering as broken glyphs.

Each fix has a regression test in EnginesTest.
@kirillk
kirillk merged commit bbf6a27 into main Aug 30, 2026
24 checks passed
@kirillk
kirillk deleted the swift-pebble branch August 30, 2026 16:41
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.

2 participants