feat: per-diagram export to Mermaid / PlantUML / Structurizr DSL / JSON + UI#15
Merged
TheAlexPG merged 5 commits intoTheAlexPG:mainfrom May 6, 2026
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
GET /api/v1/diagrams/{id}/export?format=mermaid|plantuml|structurizr|jsonso 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
custom), C4-PlantUML, Structurizr DSL, plus a per-diagram JSON that includes placement coords.c4_common.pyso Mermaid + PlantUML stay in lock-step. Mapping is diagram-type aware —STOREbecomesSystemDbunder context views andContainerDbunder container views, so the output is legal under each renderer's stdlib.GROUPobjects render as nestedSystem_Boundary { ... }blocks (Mermaid, PlantUML) andg_x = group "..." { ... }blocks (Structurizr), with placed children inside. Round-trips throughimport_dsl.mermaid_service.parse(). Per-node%%comments preserve originalObjectTypefor AI consumers.get_diagram_payloadnow scopes connections byConnection.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
ExportToolbar(top-right of the canvas) with a 280px popover listing the four formats, each with Copy and Save.fetchDiagramExport()inuse-api.tsusesresponseType: 'text'+ atransformResponseoverride so axios doesn't JSON-parse thetext/plainpayloads (Mermaid / PlantUML / DSL)..mmd/.puml/.dsl/.json).FilterToolbar/AddObjectToolbarpatterns; 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:
fix(export): scope diagram export connections by draft context); 1 Low intentionally deferred (theuseDiagramfilename 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 testspytest backend/tests/api/test_export.py— 11 e2e tests (incl. the new draft-leak regression)ruff checkcleantsc -b+eslint+vite buildclean for the frontend