Skip to content

feat: per-diagram export to Mermaid / PlantUML / Structurizr DSL / JSON + UI#15

Merged
TheAlexPG merged 5 commits intoTheAlexPG:mainfrom
Gruku:feat/diagram-export
May 6, 2026
Merged

feat: per-diagram export to Mermaid / PlantUML / Structurizr DSL / JSON + UI#15
TheAlexPG merged 5 commits intoTheAlexPG:mainfrom
Gruku:feat/diagram-export

Conversation

@Gruku
Copy link
Copy Markdown
Contributor

@Gruku Gruku commented May 5, 2026

Summary

Adds GET /api/v1/diagrams/{id}/export?format=mermaid|plantuml|structurizr|json so AI agents and external tools can pull a single diagram as text, plus a frontend "Export" toolbar that exposes it from the canvas. Closes the "Export to Mermaid / PlantUML" roadmap item.

Backend

  • Mermaid C4 (and flowchart for custom), C4-PlantUML, Structurizr DSL, plus a per-diagram JSON that includes placement coords.
  • C4 macro vocabulary (Person / System / Container / Component / *_Boundary / Rel / BiRel) lives in a shared c4_common.py so Mermaid + PlantUML stay in lock-step. Mapping is diagram-type awareSTORE becomes SystemDb under context views and ContainerDb under container views, so the output is legal under each renderer's stdlib.
  • GROUP objects render as nested System_Boundary { ... } blocks (Mermaid, PlantUML) and g_x = group "..." { ... } blocks (Structurizr), with placed children inside. Round-trips through import_dsl.
  • Mermaid flowchart output is restricted to the subset that re-parses through mermaid_service.parse(). Per-node %% comments preserve original ObjectType for AI consumers.
  • Format-aware escaping helpers cover quotes, brackets, pipes, backslashes per format.
  • Draft scopingget_diagram_payload now scopes connections by Connection.draft_id (live ↔ live, fork ↔ live + own draft) so draft-scoped edges never leak into a live export.

Auth

Workspace-scoped diagrams require an authenticated workspace member with can_read_diagram. Anonymous → 401, non-members → 403. Workspace-less diagrams stay open (matches existing behaviour elsewhere).

Frontend

  • New ExportToolbar (top-right of the canvas) with a 280px popover listing the four formats, each with Copy and Save.
  • fetchDiagramExport() in use-api.ts uses responseType: 'text' + a transformResponse override so axios doesn't JSON-parse the text/plain payloads (Mermaid / PlantUML / DSL).
  • Filenames use a slugified diagram name + format-specific extension (.mmd / .puml / .dsl / .json).
  • 401 / 403 / 404 errors surface inline in the popover, not just the console.
  • Styling matches existing FilterToolbar / AddObjectToolbar patterns; no hover-motion, no colored left rails, no elevation shadows on the popover (per project UI rules).

Codex review

Two passes from Codex landed in this PR. All findings fixed:

  • Pass 1 (backend): 4 High + 2 Medium — auth bypass, level-mismatched C4 element types, broken Structurizr round-trip, GROUP rendering, flowchart round-trip, format-aware escaping.
  • Pass 2 (combined backend + UI): 1 Medium fixed (draft-scoped connection leak — see fix(export): scope diagram export connections by draft context); 1 Low intentionally deferred (the useDiagram filename fetch hits the same workspace auth path the export endpoint does, so it's not a real privilege escalation).

Test plan

  • pytest backend/tests/services/test_export_services.py — 27 unit tests
  • pytest backend/tests/api/test_export.py — 11 e2e tests (incl. the new draft-leak regression)
  • ruff check clean
  • tsc -b + eslint + vite build clean for the frontend
  • Manual browser smoke: click each format's Copy + Save on a real diagram; render Mermaid output through Mermaid Live + PlantUML output through plantuml.com

Gruku added 3 commits May 5, 2026 21:46
Adds GET /api/v1/diagrams/{id}/export?format=... so AI agents and
external tools can pull a single diagram as text instead of scraping the
whole-workspace JSON dump. The C4 macro vocabulary is shared between
Mermaid and PlantUML via app/services/c4_common.py and is diagram-type
aware (STORE -> SystemDb on context views, ContainerDb on container
views, etc.) so the output is legal under each renderer's stdlib.

Auth requires an authenticated workspace member with can_read_diagram
access for workspace-scoped diagrams (anonymous + non-members get 401 /
403). GROUP objects render as nested System_Boundary blocks with their
placed children inside; Structurizr DSL emits matching brace blocks so
import_dsl rebuilds parent_id on round-trip. Mermaid flowchart output
for `custom` diagrams is restricted to the subset that round-trips
through mermaid_service.parse(). Format-aware escaping covers quotes,
brackets, pipes, and backslashes per format.

Tests: 27 service-level unit tests + 10 e2e API tests, all green.
get_diagram_payload built the connection set only from endpoint object
IDs, ignoring Connection.draft_id. A draft-scoped Connection wired
between two live objects on the diagram would leak into the live
export. Live diagrams now only see Connection.draft_id IS NULL;
forked diagrams see live + their own draft's overlay, matching
connection_service.get_connections() semantics.

Adds a regression test that creates a draft-scoped connection between
two live objects and asserts it is absent from both the mermaid and
JSON exports of the live diagram.

Codex M1.
ExportToolbar adds a top-right "Export" button on the diagram canvas
with a popover offering Copy + Save for each of the four backend
formats (Mermaid, PlantUML, Structurizr DSL, JSON). Hits
GET /api/v1/diagrams/{id}/export from PR TheAlexPG#15.

- fetchDiagramExport() in use-api.ts uses responseType:'text' +
  transformResponse override so axios doesn't JSON-parse the
  text/plain payload (Mermaid/PlantUML/DSL).
- Filenames use a slugified diagram name + format-specific extension
  (.mmd / .puml / .dsl / .json).
- Errors surface inline in the popover (401/403/404 mapped to short
  labels) instead of console-only.
- Styling mirrors the existing inline-style toolbar pattern; no
  hover-motion, no colored left rails, no elevation shadows on the
  popover per the project UI rules.
@Gruku Gruku changed the title feat: per-diagram export to Mermaid / PlantUML / Structurizr DSL / JSON feat: per-diagram export to Mermaid / PlantUML / Structurizr DSL / JSON + UI May 6, 2026
@TheAlexPG TheAlexPG merged commit 6a5da17 into TheAlexPG:main May 6, 2026
4 checks passed
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