Skip to content

feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io#12

Merged
sraphaz merged 9 commits intomainfrom
feat/drawio-export
Feb 13, 2026
Merged

feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io#12
sraphaz merged 9 commits intomainfrom
feat/drawio-export

Conversation

@sraphaz
Copy link
Copy Markdown
Owner

@sraphaz sraphaz commented Feb 13, 2026

feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io

Summary

This PR adds export of LikeC4 views to Draw.io (.drawio) format from the CLI (likec4 export drawio) and the Playground (right-click on diagram → DrawIO → Export view / Export all). It does not include import; import will be proposed in a separate PR.

Context for maintainers: This work was first submitted in PR #2614, which received substantial review feedback focused on clean code, structure, and maintainability. We took that feedback seriously and ran a deliberate refactor pass (Uncle Bob / Clean Code) over the DrawIO-related code. This PR re-submits the same feature set with a cleaner, refactored codebase that we believe is easier to review and maintain. We’re grateful for the earlier review and have aimed to address those concerns in this iteration.


What’s in this PR

1. Generators (@likec4/generators)

  • generate-drawio.ts — Export single or multiple views to Draw.io XML; layout, styles, round-trip comments. Refactors: computeDiagramLayout split into smaller helpers; getViewDescriptionString extracted; buildNodeCellXml / buildEdgeCellXml and constants (SOLID/DRY/KISS); exported type DrawioViewModelLike.
  • parse-drawio.ts — Round-trip comment parsing and parse-to-LikeC4 for future import. Refactors: parseDrawioToLikeC4Multi split into mergeDiagramStatesIntoMaps, buildRootsFromFqnToCell, emitMultiDiagramModel (orchestrator ~50–60 lines); buildViewBlockLines / escapeLikec4Quotes; O(n²) deduplication replaced with parsedIds Set; UserObject fullTag uses innerXml so sibling <data> is preserved.
  • Tests: generate-drawio.spec.ts, parse-drawio.spec.ts; snapshots in __snapshots__/. Decompress error assertion accepts (base64 decode|inflate|URI decode) for Node/env behavior; snapshots updated for CI.

2. CLI (@likec4/likec4)

  • export/drawio/handler.tslikec4 export drawio with --outdir/-o, --all-in-one, --roundtrip, --uncompressed, --project, --use-dot. Uses DEFAULT_DRAWIO_ALL_FILENAME from @likec4/generators (DRY). Phase comments and thin handler pattern aligned with other export commands.
  • export/png/handler.ts — Type PngExportArgs, runExportPng(args, logger); PNG export supports --outdir/-o (docs updated).
  • No likec4 import drawio in this PR (no packages/likec4/src/cli/import/).

3. Playground

  • DrawIO context menu export only: Export view…, Export all… (DrawioContextMenuProvider, DrawioContextMenuDropdown, useDrawioContextMenuActions). Uses generateDrawio / generateDrawioMulti and parseDrawioRoundtripComments. No Import menu item or file input.
  • Monaco: only “Export to DrawIO” in editor context menu.

4. Documentation

  • drawio.mdx — Export only: mapping, options, multi-diagram, troubleshooting, re-export via comment blocks.
  • cli.mdx / docker.mdx — Export to DrawIO and PNG --outdir; no Import section.

5. E2E & tests

  • e2e/tests/drawio-playground.spec.ts — DrawIO menu (Export view / Export all). Run with playwright.playground.config.ts.
  • likec4: drawio-demo-export-import.spec.ts, drawio-tutorial-export-import.spec.ts — export tests; import/round-trip tests skipped in this PR.

What’s not in this PR

  • No likec4 import drawio command.
  • No Playground “Import from DrawIO” or Monaco Import action.
  • No docs for importing from Draw.io.
  • Import/round-trip tests remain skipped until the import PR.

Refactor summary (response to PR likec4#2614 feedback)

After the initial submission (PR likec4#2614), we applied a structured clean-code pass:

  • Generators: Smaller, single-responsibility functions; named types (DrawioViewModelLike); constants; phase comments; DRY (e.g. DEFAULT_DRAWIO_ALL_FILENAME, shared helpers).
  • CLI: Thin handlers; args types and runExport*(args, logger) pattern for drawio/PNG/JSON; consistent error handling and JSDoc.
  • Playground: Clear separation in useDrawioContextMenuActions (fillFromLayoutedModel, fillFromViewStates, etc.); constants for fonts/sizes.
  • E2E: Shared helpers (selectors, timeouts) in e2e/helpers/.

We believe this version is in better shape for review and long-term maintenance.


Correções após review (PR #11)

Incorporadas as sugestões do review anterior:

  • Changesets: Escopo export-only; removidas menções a import nos textos de release (drawio-implementation-plan, drawio-import-postpack).
  • DrawioContextMenuProvider: Valor do context api memoizado com useMemo(..., [actions.openMenu]) para evitar re-renders desnecessários.
  • LanguageClientSync: Uso de LayoutView.req em sendRequest (type-safe; removido cast manual).
  • E2E likec4-cli-export-drawio: Teste "empty workspace" agora verifica exitCode === 1 no erro rejeitado.
  • packages/config filenames.ts: basename remove barras finais (/ e \) e trata resultado vazio com fallback.
  • generate-drawio.ts: arcSize=12 (inteiro percentagem) em vez de 0.12 para cantos ligeiramente arredondados no Draw.io.
  • parse-drawio.ts: Lógica comum extraída para buildCommonDiagramStateFromCells; buildSingleDiagramState e buildDiagramState reutilizam (DRY).
  • drawio handler: useDot lido dos args e passado a runExportDrawio; graphviz binary vs wasm conforme flag --use-dot.
  • json handler: Campo useDot nos args e em runExportJson; guard quando projectsModels.length === 0 (warn + throw).

Checklist

  • I have read the latest contribution guidelines.
  • My branch is synced with main (merge/rebase as appropriate).
  • Commit messages follow Conventional Commits (e.g. feat:, refactor:, test:).
  • Tests added/updated; import-related tests skipped in this branch. pnpm ci:test (Vitest) passes.
  • Documentation updated (drawio.mdx, cli.mdx, docker.mdx for export only).
  • Changesets can be added for user-facing packages if maintainers request.

Verification

  • pnpm build (filter !./apps/*), pnpm typecheck, pnpm test (or pnpm ci:test) — pass.
  • E2E drawio-playground: run with playground Playwright config when validating.

Notes for reviewers

  • Export behavior is unchanged from the original feature; changes are structural (refactors, types, constants, tests).
  • Generators: DrawioViewModelLike is the public type for view models passed to generateDrawio / generateDrawioMulti.
  • CLI: Drawio and PNG export handlers follow the same pattern as other export commands (args type + runExport* + thin handler).
  • We’re happy to address any further feedback and to add a changeset for the export feature if desired.

Reference

  • Initial submission and review context: PR #2614.
  • Original DrawIO bidirectional discussion: PR #2593. This PR is export-only; import will follow in a separate PR.

Summary by CodeRabbit

  • New Features

    • Export-only Draw.io improvements and round-trip comment support.
    • New --use-dot CLI option to choose Graphviz implementation.
  • Bug Fixes

    • More robust path basename handling.
    • Improved error formatting and stricter CLI validation for missing projects.
  • Performance

    • Parallelized layout-view requests.
    • Stabilized playground context-menu behavior.
  • Chores

    • CI workflows migrated to ubuntu-24.04.
  • Documentation

    • Expanded playground troubleshooting and Playwright reporter configuration.
  • Tests

    • Adjusted tests for Draw.io export flows.

…e exitCode, basename, arcSize, parse DRY, useDot flags, json empty guard

- Changesets: export-only scope; remove import wording from release notes
- DrawioContextMenuProvider: memoize api with useMemo(..., [actions.openMenu])
- LanguageClientSync: use LayoutView.req for type-safe sendRequest (no cast)
- e2e likec4-cli-export-drawio: assert exitCode === 1 on empty workspace
- config/filenames: basename strip trailing slashes, empty fallback
- generate-drawio: arcSize=12 (percent integer) for rounded corners
- parse-drawio: buildCommonDiagramStateFromCells shared by buildSingleDiagramState/buildDiagramState
- drawio handler: useDot from args for graphviz (binary vs wasm)
- json handler: useDot from args; guard when projectsModels.length === 0

Co-authored-by: Cursor <[email protected]>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

sraphaz and others added 4 commits February 13, 2026 01:15
Implementado:
- generate-drawio.spec: flag g no replace de modified para multi-diagrama
- png handler: remover eslint-disable; output/projects const; seq/flat sem boolean redundante
- worker.ts (browser): remover ramo morto typeof err === 'object'
- e2e likec4-cli: limpar outDir com rmSync antes do export (evitar falsos positivos)
- drawio-playground.spec: readFile (fs/promises) em vez de readFileSync
- monaco index: re-exportar MonacoEditorProps de MonacoEditor; remover comentário
- MonacoEditor: setLayoutedModelApi como prop direta
- LanguageClientSync: layoutViews em paralelo com Promise.all
- useDrawioContextMenuActions: usar DrawioViewModelLike; Object.values sem ?? []; anchor append/remove no download
- generate-drawio.ts: remover variáveis não usadas; titleInner só no ramo navTo !== ''
- parse-drawio: linkLine em vez de line (shadowing); i+=1 antes de continue nos blocos STROKE_COLOR/WIDTH/WAYPOINTS
- cli index: exitWithFailure(err, prefix) reutilizado em unhandledRejection
- playwright.playground.config: reporter CI (github, list, html)
- README playground: título da secção troubleshooting mais claro
- parse-drawio.spec: describe(parseDrawioToLikeC4), parseDrawioToLikeC4Multi, parseDrawioRoundtripComments, decompressDrawioDiagram)

Não implementado (e porquê):
- Pin ubuntu-24.04 nos workflows: decisão de repo/CI; pode divergir do main; baixa prioridade.
- ExtendedNode type alias em generate-drawio.ts: refactor maior em vários acessores; deixa-se para PR dedicado.
- Cache do RegExp em getAttr (parse-drawio): impacto negligível; reviewer disse "consider caching if profiling identifies"; otimização prematura.
- Union discriminada canExportAllViews/onExportAllViews: alteração de tipos mais invasiva; consumidor atual já emparelha corretamente.
- Comentários "outside diff range": não aplicáveis inline neste commit.

Co-authored-by: Cursor <[email protected]>
…erate-drawio

- workflows: runs-on ubuntu-latest -> ubuntu-24.04 for reproducible CI

- generate-drawio: ExtendedNode type alias for optional node fields; accessors use single cast

Co-authored-by: Cursor <[email protected]>
…equest, exactOptionalPropertyTypes)

Co-authored-by: Cursor <[email protected]>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

Rescopes Draw.io work to export-only; removes an implementation-plan changeset; migrates CI runners to Ubuntu 24.04; introduces DrawioViewModelLike and related refactors across generators and playground; adds CLI --use-dot; refactors Draw.io parsing; and applies assorted e2e, utility, and minor docs/type updates.

Changes

Cohort / File(s) Summary
Changelog & plans
\.changeset/drawio-implementation-plan.md, \.changeset/drawio-import-postpack.md
Removed the detailed Draw.io implementation-plan changeset and replaced import-focused notes with an export-focused changeset (export-only message; import deferred).
CI workflows
.github/deprecated/auto-assign.yaml, .github/workflows/checks.yaml, .github/workflows/cleanup-cache.yml, .github/workflows/codeql.yml, .github/workflows/copilot-setup-steps.yml, .github/workflows/test-export-action.yml, .github/workflows/trigger-deploy-template.yaml
Uniformly switched runner from ubuntu-latest to ubuntu-24.04 across multiple workflows; no other step or logic edits.
Playground docs
apps/playground/README.md
Reworded Troubleshooting heading and expanded LSP connection troubleshooting steps.
Playground components
apps/playground/src/components/drawio/DrawioContextMenuProvider.tsx, apps/playground/src/components/drawio/useDrawioContextMenuActions.ts
Memoized context-menu API; replaced ViewModelLike with DrawioViewModelLike across export path; defaulted styles via LikeC4Styles; improved download anchor DOM handling; multiple signature/type updates and added JSDoc/types.
Monaco & language client
apps/playground/src/monaco/LanguageClientSync.tsx, apps/playground/src/monaco/index.tsx
Parallelized layout-view requests with Promise.all; replaced hard-coded request string with LayoutView.req.method; re-exported MonacoEditorProps from ./MonacoEditor and removed local type.
E2E / Playwright
e2e/playwright.playground.config.ts, e2e/src/likec4-cli-export-drawio.spec.ts, e2e/tests/drawio-playground.spec.ts
Added CI-aware Playwright reporter; test cleanup uses rmSync; switched file reads to async readFile; adjusted error assertion style.
Filename util
packages/config/src/filenames.ts
Improved basename to trim trailing separators (/ and \) robustly and return a defined basename.
Draw.io generation (core)
packages/generators/src/drawio/generate-drawio.ts, packages/generators/src/drawio/generate-drawio.spec.ts
Introduced DrawioViewModelLike and internal ExtendedNode; updated many exported functions to accept the new viewmodel type; adjusted container title handling and arcSize value; tests normalizer updated to stabilize layout/timestamps.
Draw.io parsing
packages/generators/src/drawio/parse-drawio.ts, packages/generators/src/drawio/parse-drawio.spec.ts
Extracted common diagram-state builder (buildCommonDiagramStateFromCells); added public DiagramInfo; improved round-trip comment parsing (advance past terminators); tests reorganized into describe groups.
Language server worker
packages/language-server/src/browser/worker.ts
Removed a redundant object branch in errToString; behavior otherwise unchanged.
CLI handlers & flags
packages/likec4/src/cli/export/drawio/handler.ts, packages/likec4/src/cli/export/json/handler.ts, packages/likec4/src/cli/export/png/handler.ts, packages/likec4/src/cli/index.ts
Added useDot: boolean flag to Drawio and JSON export args (selects Graphviz backend); JSON export now aborts when no models generated; PNG export made some locals const and added project-existence guard; exitWithFailure accepts optional prefix and unhandledRejection uses it.
Minor docs/types
apps/playground/src/components/drawio/DrawioContextMenuDropdown.tsx
Added JSDoc for dropdown props; no signature changes.

Sequence Diagram(s)

(No sequence diagrams generated.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibble XML, hop over types and tiny dots of light,
I swap runners, tidy comments, and set exports right,
CLI flags chatter while parsers hum a tune,
Tests and configs shine beneath the moon,
Hoppity-hop — the diagrams take flight! 🎨✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (29 files):

⚔️ .changeset/drawio-import-postpack.md (content)
⚔️ .github/deprecated/auto-assign.yaml (content)
⚔️ .github/workflows/checks.yaml (content)
⚔️ .github/workflows/cleanup-cache.yml (content)
⚔️ .github/workflows/codeql.yml (content)
⚔️ .github/workflows/copilot-setup-steps.yml (content)
⚔️ .github/workflows/test-export-action.yml (content)
⚔️ .github/workflows/trigger-deploy-template.yaml (content)
⚔️ apps/playground/README.md (content)
⚔️ apps/playground/src/components/drawio/DrawioContextMenuDropdown.tsx (content)
⚔️ apps/playground/src/components/drawio/DrawioContextMenuProvider.tsx (content)
⚔️ apps/playground/src/components/drawio/useDrawioContextMenuActions.ts (content)
⚔️ apps/playground/src/monaco/LanguageClientSync.tsx (content)
⚔️ apps/playground/src/monaco/index.tsx (content)
⚔️ e2e/playwright.playground.config.ts (content)
⚔️ e2e/src/likec4-cli-export-drawio.spec.ts (content)
⚔️ e2e/tests/drawio-playground.spec.ts (content)
⚔️ packages/config/src/filenames.ts (content)
⚔️ packages/generators/src/drawio/__snapshots__/generate-drawio.spec.ts.snap (content)
⚔️ packages/generators/src/drawio/__snapshots__/parse-drawio.spec.ts.snap (content)
⚔️ packages/generators/src/drawio/generate-drawio.spec.ts (content)
⚔️ packages/generators/src/drawio/generate-drawio.ts (content)
⚔️ packages/generators/src/drawio/parse-drawio.spec.ts (content)
⚔️ packages/generators/src/drawio/parse-drawio.ts (content)
⚔️ packages/language-server/src/browser/worker.ts (content)
⚔️ packages/likec4/src/cli/export/drawio/handler.ts (content)
⚔️ packages/likec4/src/cli/export/json/handler.ts (content)
⚔️ packages/likec4/src/cli/export/png/handler.ts (content)
⚔️ packages/likec4/src/cli/index.ts (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main feature: exporting LikeC4 views to Draw.io format across CLI, Playground, and generator packages.
Description check ✅ Passed The description is comprehensive and well-structured, covering implementation details, refactoring rationale, scope limitations (export-only), and verification steps. All required checklist items are marked complete.
Docstring Coverage ✅ Passed Docstring coverage is 82.61% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/drawio-export
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feat/drawio-export
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

No actionable comments were generated in the recent review. 🎉


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/generators/src/drawio/parse-drawio.ts (1)

1274-1352: ⚠️ Potential issue | 🟡 Minor

Guard empty diagram names to avoid view element IDs.

Line 1315 uses toId(diagramName). If a diagram has name="", toId returns element, so the view id becomes element instead of a sensible default like index/diagram_n. Suggest treating blank names as missing.

Suggested fix
-  const viewId = toId(diagramName) || 'index'
+  const safeName = diagramName?.trim() ? diagramName : 'index'
+  const viewId = toId(safeName) || 'index'
🧹 Nitpick comments (5)
apps/playground/README.md (1)

25-25: Documentation improvement looks good!

The updated heading with a specific error message example helps users identify LSP connection issues more quickly.

For slightly better readability, consider using single quotes for the inner error message example to avoid nested double quotes:

-### LSP connection errors (e.g. "Client likec4: connection to server is erroring")
+### LSP connection errors (e.g. 'Client likec4: connection to server is erroring')
e2e/tests/drawio-playground.spec.ts (1)

68-69: Consider moving the import to the top of the file.

The dynamic import of node:fs/promises inside the test function adds unnecessary overhead on each test run. Since this is a Node.js test environment, a static import at the top of the file would be cleaner and more performant.

♻️ Proposed refactor

Add at the top of the file:

import { readFile } from 'node:fs/promises'

Then simplify the test:

-    const { readFile } = await import('node:fs/promises')
-    const content = await readFile(path!, 'utf8')
+    const content = await readFile(path!, 'utf8')
.changeset/drawio-import-postpack.md (1)

10-10: Improve capitalization and clarity.

The phrase "export drawio" has inconsistent capitalization and could be clearer. Consider revising to "export to Draw.io" or "export Draw.io format" to match the capitalization used in line 8 and improve readability.

📝 Suggested improvement
-- **Draw.io export:** Generators and CLI export drawio (this PR is export-only; import will be proposed in a separate PR).
+- **Draw.io export:** Generators and CLI export to Draw.io (this PR is export-only; import will be proposed in a separate PR).
.changeset/drawio-implementation-plan.md (1)

8-8: Clarify "re-export from Playground/CLI" phrasing.

The phrase "re-export from Playground/CLI" at the end of the Export description is somewhat ambiguous. Does this mean the round-trip comment parsing functionality is exported by the generators and then used/re-exported by Playground/CLI, or that these components implement the parsing themselves? Consider rephrasing for clarity, e.g., "used by Playground/CLI for round-trip export" or "with round-trip support in Playground/CLI".

packages/generators/src/drawio/generate-drawio.spec.ts (1)

83-92: Handle <diagram> tags without attributes in snapshot normalization.

Line 92 requires a space + attributes, so <diagram> with no attributes won’t be normalized. If the generator ever emits attribute-less tags, snapshots could reintroduce layout-dependent diffs. Consider making the attributes optional.

Proposed tweak
-    .replace(/<diagram ([^>]*)>[\s\S]*?<\/diagram>/g, '<diagram $1>LAYOUT</diagram>')
+    .replace(/<diagram\b([^>]*)>[\s\S]*?<\/diagram>/g, '<diagram$1>LAYOUT</diagram>')

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