feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io#2614
feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io#2614
Conversation
- Docs: Export DrawIO option --output,-o -> --outdir,-o - generate-drawio: pageScale="1" for well-formed XML, mxUserObject after mxGeometry - generate-drawio: flattenMarkdownOrString and isEmptyish for description/technology - parse-drawio: infer system only for swimlane not shape=rectangle+rounded - parse-drawio: toId remove dot from allowed chars to avoid invalid FQN - Playground: DrawioContextMenu refactor with hook and presentational component Co-authored-by: Cursor <[email protected]>
…PR drafts Co-authored-by: Cursor <[email protected]>
…d export/import - Export: view title/description (diagram name + likec4ViewTitle/Description), summary, links, border, opacity, relationship kind/notation, likec4ColorName - Import: diagram name as view id, view title/description from root cell, edge strokeColor to style color, opacity, shape (cylinder/document), summary/links/border/relationshipKind/notation - Add CONVERSION-MAPPING.md documenting mapped and unmapped items - Add pako.d.ts for typecheck; export DiagramInfo from parse-drawio Co-authored-by: Cursor <[email protected]>
…pport - IMPLEMENTATION-PLAN.md: phased plan for full conversion coverage - parseDrawioToLikeC4Multi(xml): merge multiple diagrams into one model, one view per tab with include list - getAllDiagrams(xml): extract all diagram name/id/content from mxfile - First diagram without name attribute still yields view 'index' for backward compatibility - Test: two-tab drawio produces view overview (include A, B) and view detail (include A, C) Co-authored-by: Cursor <[email protected]>
…and single English doc
- Import: emit layout as comment block (likec4.layout.drawio) for round-trip
- Import: emit vertex strokeColor as comment block (DSL has no element strokeColor)
- Import: parse likec4Metadata on edges, emit metadata { } block
- Import: parse Draw.io native style 'link', emit element link when no likec4Links
- Export: add likec4StrokeColor on vertex, likec4Metadata on edge
- Docs: single Draw.io integration page (tooling/drawio.mdx) in English; link from CLI
- Remove CONVERSION-MAPPING.md and IMPLEMENTATION-PLAN.md; add changeset
Co-authored-by: Cursor <[email protected]>
…on, size/padding/textSize/iconPosition - 2.1: Import emit vertex strokeWidth as comment block (likec4.strokeWidth.vertices) - 2.4: Export likec4ViewNotation on root cell when view has notation - 2.5: Export/import element size, padding, textSize, iconPosition (likec4Size etc.) - Export likec4StrokeWidth on vertex for round-trip - Docs: drawio.mdx updated with new mappings Co-authored-by: Cursor <[email protected]>
…ustomData comment Co-authored-by: Cursor <[email protected]>
- Export: GenerateDrawioOptions with layoutOverride, strokeColorByNodeId, strokeWidthByNodeId, edgeWaypoints; view notation string; customData and waypoints in XML. - Import: view notation and edge waypoints comment blocks (single + multi); parseDrawioRoundtripComments() to read comment blocks for re-export. - Playground: getSourceContent from workspace files; export applies round-trip options when comment blocks present. - Playground generate: use PowerShell on Windows (usePowerShell + quotePowerShell) so generate runs without bash. - Generators: export getAllDiagrams, DrawioRoundtripData, parseDrawioRoundtripComments; @types/pako; fix view.notation type and layoutByView possibly undefined. - Docs: drawio.mdx updated with options, waypoints, re-export note. - Tests: parseDrawioRoundtripComments; generate snapshots updated. - Changeset: drawio-roundtrip-options.md. Co-authored-by: Cursor <[email protected]>
- CLI: likec4 export drawio --roundtrip reads .c4 source, parses comment blocks (layout, stroke, waypoints), applies options per view. - Generators: export generateDrawioMulti, parseDrawioRoundtripComments, parseDrawioToLikeC4Multi from main index for CLI/consumers. - Playground: DrawioContextMenu accepts optional getSourceContent for round-trip export when used with explicit props. - Docs: cli.mdx documents --roundtrip and --all-in-one for Export to DrawIO. - E2E: playwright.playground.config.ts and drawio-playground.spec.ts; test:playground script; e2e/.gitignore allows drawio-playground.spec.ts. - Changeset: drawio-cli-roundtrip-e2e.md (likec4, e2e patch). Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
… correctly Co-authored-by: Cursor <[email protected]>
- Use BBox, ThemeColorValues, RelationshipColorValues from @likec4/core - Add getEffectiveStyles(viewmodel) and resolveThemeColor for DRY - JSDoc for public and key internal helpers - Docs: drawio.mdx export behavior (theme colors, containers behind, edge anchors, layoutOverride BBox) Co-authored-by: Cursor <[email protected]>
…ut fallbacks - useDrawioContextMenuActions: build viewModels from likec4model.views() then fill via getLayoutedModel, viewStates, layoutViews so all tabs export - DrawioContextMenuDropdown: minor copy/tooltip tweaks - README and schema updates Co-authored-by: Cursor <[email protected]>
Export: - Container: keep rounded=0 (no rounded corners) - Elements: rounded=1 + arcSize=0.12 (subtle curve) - UserObject with link for navigateTo; style link=data:page/id,likec4-<viewId> - Docs: navigability and link format Import: - Parse UserObject wrapping mxCell: use id and link from UserObject - navigateTo from link attribute (data:page/id,likec4-<viewId>) for round-trip - Refactor: buildCellFromMxCell for reuse; skip duplicate id from UserObject Playground: - DrawIO context menu font size 10px Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
- Parse: container=1 -> system, child of container -> component; fillOpacity/likec4Opacity; view title/description/notation from root cell - Merge container title cells (text shape) into container element; exclude from vertex list - inferKind(style, parentCell); single and multi-diagram use containerIdToTitle and byId - generate-drawio.spec: ProcessedView type for viewWithNav; drawio-demo: expected nodes include container title count - CLI: likec4 import drawio described as experimental (import not yet fully validated) Co-authored-by: Cursor <[email protected]>
…r oxlint Co-authored-by: Cursor <[email protected]>
- Add devops/commands/postpack.ts: copy packed tgz to package.tgz (Node fs) - Replace cp ... || true in all 14 packages and styled-system with likec4ops postpack - Enables pnpm lint:package and validate on Windows Co-authored-by: Cursor <[email protected]>
- CI: single check-validate job runs pnpm validate (same as local); e2e/docs/playground depend on it - Root: pnpm validate script (generate -> typecheck -> core type tests -> lint -> build -> lint:package -> test) - Husky: pre-push runs validate when pushing to main - AGENTS.md: document pnpm validate - Changeset: drawio import alignment, CLI experimental, postpack, validate pipeline Co-authored-by: Cursor <[email protected]>
…hangeset" This reverts commit bdc881e.
…changes) Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
- Export: preserve node parent links (parentId from view hierarchy); container title cell parent = container id - Docs: mention --all-in-one early in Export section - Playground: use layouted diagram for single-view export when available; forEach -> for...of - generate-drawio: hex regex 3/4/6/8 only; remove sourcePoint/targetPoint from Array points; container margins: horizontal = xl, vertical = xl+md (vertical slightly larger) - Remove pako.d.ts, use @types/pako - parse-drawio: likec4LineType only 1 1 / 2 2 = dotted; getAllDiagrams guard empty/invalid content; DRY: stripHtml, stripHtmlForTitle, emitElementToLines, emitEdgesToLines, emitRoundtripComments(Single|Multi) - worker: errToString guard JSON.stringify circular refs - drawio-demo spec: assert reimportViews.length > 0; fix NodeId type in containerCount - Snapshots updated for parent/container changes Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
- generate-drawio: use view.bounds for page, zero offset when content fits - drawio-demo spec: increase timeout to 15s for vice versa test - handler: use loggable(err) instead of String(err) for error logging - worker: avoid String(err) on object in errToString (return literal) Co-authored-by: Cursor <[email protected]>
…iew LSP request, MonacoEditor props Co-authored-by: Cursor <[email protected]>
…etry Co-authored-by: Cursor <[email protected]>
- Wait for .react-flow.initialized (20s) like export snapshot tests - Increase test timeout to 35s for CI layout delay - Remove arbitrary sleep and fragile canvas wait Co-authored-by: Cursor <[email protected]>
- Remove CLI import drawio command and handlers - Playground: DrawIO menu export only (no Import item, no Monaco import action) - Docs: export-only sections in drawio.mdx and cli.mdx - Tests: skip import/round-trip specs (to re-enable in import PR) - E2E: assert Export to DrawIO and Export all only - Add DRAWIO-PR-SPLIT.md and .pr-description-export.md / .pr-description-import.md Co-authored-by: Cursor <[email protected]>
🦋 Changeset detectedLatest commit: 3f8346c The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughThis PR introduces Draw.io roundtrip export enhancements across CLI, Playground, and generators, including multi-diagram export (--all-in-one), layout/stroke customization (--roundtrip), and an infrastructure refactor replacing shell-based postpack with a centralized likec4ops command. Includes E2E tests, language-server error handling, and updated CI workflows. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI
participant Parser as Parser/Generator
participant Model as LikeC4 Model
participant Draw.io as Draw.io Export
participant Roundtrip as Roundtrip Cache
User->>CLI: likec4 export drawio --roundtrip<br/>--all-in-one
CLI->>Roundtrip: readWorkspaceSourceContent()<br/>(read .c4 files)
Roundtrip-->>CLI: c4 source + roundtrip blocks
CLI->>Parser: parseDrawioRoundtripComments(source)<br/>(layout, strokes, waypoints)
Parser-->>CLI: DrawioRoundtripData
CLI->>Model: getLayoutedModel()
Model-->>CLI: LayoutedLikeC4ModelData
CLI->>Parser: generateDrawioMulti(viewmodels,<br/>optionsByViewId)
Parser->>Parser: For each view:<br/>apply layout overrides<br/>apply stroke colors/widths<br/>apply edge waypoints
Parser->>Draw.io: Emit mxCell elements<br/>with customData/<br/>mxUserObject metadata
Draw.io-->>Parser: Single diagrams.drawio file<br/>(multiple tabs)
Parser-->>CLI: Compressed/uncompressed XML
CLI->>CLI: Write to disk
CLI-->>User: diagrams.drawio ✓
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f8346c7af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const json = dataLine.slice(3) | ||
| layoutByView = JSON.parse(json) as DrawioRoundtripData['layoutByView'] | ||
| } catch { |
There was a problem hiding this comment.
Merge layout comment blocks instead of overwriting
parseDrawioRoundtripComments replaces layoutByView every time it sees a // <likec4.layout.drawio> block, so when multiple files contribute blocks only the last one is kept. Because the CLI concatenates all .c4/.likec4 sources before parsing, this causes earlier view layouts to be silently dropped and --roundtrip exports without expected positions for those views.
Useful? React with 👍 / 👎.
| const sourceContent = await readWorkspaceSourceContent(resolve(args.path)) | ||
| const roundtripData = parseDrawioRoundtripComments(sourceContent) |
There was a problem hiding this comment.
Scope roundtrip source parsing to the selected project
When --project is used, layouting is done for that project, but roundtrip overrides are still read from every .c4 file under args.path. In multi-project workspaces, comment blocks from unrelated projects can collide on shared view/node IDs and apply wrong layout or stroke overrides to the exported project. The roundtrip input should be restricted to files that belong to the selected project.
Useful? React with 👍 / 👎.
feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io
Summary
This PR adds export of LikeC4 views to Draw.io (
.drawio) format. Users can export from the CLI (likec4 export drawio) and from the Playground (right-click on diagram → DrawIO → Export view / Export all). This allows editing diagrams in Draw.io and reusing them in tools that support the format.This PR does not include import. Import from Draw.io will be proposed in a separate PR after this one is merged.
What's in this PR
1. Generators (
@likec4/generators)packages/generators/src/drawio/generate-drawio.ts— Exports a single view or multiple views to Draw.io XML. Maps LikeC4 elements (title, description, shape, color, relationships, etc.) to mxCell vertices/edges. Supports optionallayoutOverride,strokeColorByNodeId,strokeWidthByNodeId,edgeWaypoints, andcompressed. Refactors:computeDiagramLayoutsplit into smaller helpers;getViewDescriptionStringextracted forbuildRootCellStyle; constants andbuildNodeCellXmlfor clarity (SOLID/DRY/KISS).packages/generators/src/drawio/parse-drawio.ts— Round-trip comment parsing (parseDrawioRoundtripComments) and parse-to-LikeC4 (parseDrawioToLikeC4/parseDrawioToLikeC4Multi) for future import. Refactors:parseDrawioToLikeC4Multisplit intomergeDiagramStatesIntoMaps,buildRootsFromFqnToCell,emitMultiDiagramModel(orchestrator ~50–60 lines);buildViewBlockLinesandescapeLikec4Quotesin view block; O(n²) cell deduplication replaced withparsedIdsSet; UserObject-level<data>preserved by usinginnerXmlforfullTagin UserObject-wrapped cells.packages/generators/src/drawio/index.ts— Public API:generateDrawio,generateDrawioMulti,GenerateDrawioOptions, plusgetAllDiagrams,parseDrawioRoundtripComments,parseDrawioToLikeC4,parseDrawioToLikeC4Multi.generate-drawio.spec.ts,parse-drawio.spec.ts; snapshots in__snapshots__/.2. CLI (
@likec4/likec4)packages/likec4/src/cli/export/drawio/handler.ts—likec4 export drawio [path]with options:--outdir, -o,--all-in-one,--roundtrip,--uncompressed,--project,--use-dot. DRY:DEFAULT_DRAWIO_ALL_FILENAMEfrom@likec4/generators.packages/likec4/src/cli/export/index.ts— Registers the drawio export command.3. Playground
Playground exports are uncompressed by default so files open reliably in Draw.io desktop.
4. Documentation
apps/docs/src/content/docs/tooling/drawio.mdx— Export only: mapping (LikeC4 → Draw.io), options, not preserved, multi-diagram, troubleshooting, re-export using comment blocks. No import sections.apps/docs/src/content/docs/tooling/cli.mdx— Export to DrawIO section only; no Import from DrawIO section.5. Tests
packages/likec4/src/drawio-demo-export-import.spec.ts— Export tests only; import/vice-versa test skipped in this PR.packages/likec4/src/drawio-tutorial-export-import.spec.ts— Export tests only; import and round-trip tests skipped in this PR.e2e/tests/drawio-playground.spec.ts— Asserts DrawIO menu shows Export to DrawIO (and Export all). Run withplaywright.playground.config.ts; main e2e config ignores this test.Refactors on this branch
parseDrawioToLikeC4Multirefactored into helpers:mergeDiagramStatesIntoMaps,buildRootsFromFqnToCell,emitMultiDiagramModel; main function is a short orchestrator (~50–60 lines).buildViewBlockLinesandescapeLikec4Quotesfor the view block. Cell deduplication uses aparsedIdsSet instead of O(n²).some(). UserObject-wrapped cells:fullTagnow usesinnerXmlso UserObject-level<data>siblings are preserved for round-trip.computeDiagramLayoutsplit into smaller functions;getViewDescriptionStringextracted forbuildRootCellStyle; constants andbuildNodeCellXmlfor clarity (SOLID/DRY/KISS).DEFAULT_DRAWIO_ALL_FILENAMEimported from@likec4/generators(DRY).What's not in this PR
likec4 import drawiocommand (nopackages/likec4/src/cli/import/).Checklist
mainbefore creating this PR.feat:,refactor:,fix:).pnpm testandpnpm typecheck(and e2e where applicable) pass.Notes for reviewers
.drawiofile per view by default;--all-in-onefor all views as tabs;--roundtripapplies layout/waypoints from comment blocks;--uncompressedfor Draw.io desktop compatibility.Review context
The original DrawIO bidirectional work (branch
feat/drawio-bidirectional-playground) was reviewed upstream in likec4/likec4 PR #2593. This export-only PR is a split from that work; feedback from that review has been incorporated (CLI docs, context menu structure, XML generation). Additional refactors on this branch: parse/generate drawio helpers and orchestrators, UserObject fullTag fix for sibling<data>round-trip, and generator CLI DRY.Summary by CodeRabbit
New Features
--all-in-oneoption to combine multiple views into a single DrawIO file.--roundtripoption for maintaining layout and stroke properties across re-exports.Improvements
--uncompressed,--roundtrip,--all-in-one).Documentation