Skip to content

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

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

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

Conversation

@sraphaz
Copy link
Copy Markdown
Owner

@sraphaz sraphaz commented Feb 13, 2026

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

  • Improvements

    • More reliable PNG exports with longer timeout and safer server lifecycle.
    • Improved Draw.io geometry/waypoints handling and safer Draw.io parsing to reduce DoS risk.
    • More stable editor/export callbacks and layout view requests.
    • Cleaner path/filename handling and robust navigation link parsing.
  • Chores

    • Safer project ID and URL handling when embedding icons.
    • Depth-limited workspace traversal to avoid excessive recursion.
  • Tests

    • Added decompression and navigate-to related test coverage.
  • Documentation

    • Clarified Draw.io export notes: import not included in this release.

@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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

Added safer, index-based XML/path parsing helpers, adjusted draw.io geometry emission, hardened server lifecycle and round‑trip traversal, sanitized embedded project IDs, and minor playground callback refactors. No public API signature changes. (50 words)

Changes

Cohort / File(s) Summary
Path utilities
packages/config/src/filenames.ts
Added trimTrailingSlashes() and splitPath(); refactored basename() to use them (removed regex). No public API changes.
Draw.io parsing & generation
packages/generators/src/drawio/parse-drawio.ts, packages/generators/src/drawio/generate-drawio.ts, packages/generators/src/drawio/generate-drawio.spec.ts, packages/generators/src/drawio/parse-drawio.spec.ts
Replaced regex-heavy XML extraction with index/string-scanning helpers (getAttr, extractMxGeometryOpenTag/Inner, etc.); waypoint parsing made explicit; mxGeometry now emits relative="1" when edge points exist; fixed non-Buffer base64 path; added decompression test.
PNG export & server lifecycle
packages/likec4/src/cli/export/png/handler.ts
Increased default timeoutMs (10_000 → 15_000); ensure server shutdown via try/finally; rename languageServiceslikec4; throw when a requested project is missing; adjust per-project URL/output handling.
Draw.io round-trip traversal
packages/likec4/src/cli/export/drawio/handler.ts
Add ROUNDTRIP_MAX_DEPTH and depth-limited DFS for workspace traversal; only recurse while depth < limit; skip unreadable files (debug) and aggregate only non-empty contents.
Playground language client & callbacks
apps/playground/src/monaco/LanguageClientSync.tsx
Added requestLayoutView wrapper and requestLayoutViewCallback; replaced inline sendRequest usages with the wrapper/callback.
Playground Drawio context menu
apps/playground/src/components/drawio/DrawioContextMenuProvider.tsx, apps/playground/src/components/drawio/useDrawioContextMenuActions.ts
Use useCallbackRef to stabilize export handler reference; remove a non-null assertion and add explicit DiagramView cast where appropriate.
Icons virtual module sanitization
packages/vite-plugin/src/virtuals/icons.ts
Add SAFE_PROJECT_ID_REGEX, embedProjectIdAsJsString, embedUrlAsJsString; filter/skip unsafe project IDs with warnings and embed sanitized literals in generated registry.
Docs / changelog
.changeset/drawio-import-postpack.md
Text clarifications stating "No import code in this PR." and that draw.io changes are export-only; no behavioral code changes.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I snipped the trailing slashes from wandering strings,
I mapped the points where drawn geometry springs,
I chased the regex shadows with patient scans,
I closed the server gently with steadier hands,
A hop of tidy code and carrots for plans.

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

⚔️ .changeset/drawio-import-postpack.md (content)
⚔️ apps/playground/src/components/drawio/DrawioContextMenuProvider.tsx (content)
⚔️ apps/playground/src/components/drawio/useDrawioContextMenuActions.ts (content)
⚔️ apps/playground/src/monaco/LanguageClientSync.tsx (content)
⚔️ packages/config/src/filenames.ts (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/likec4/src/cli/export/drawio/handler.ts (content)
⚔️ packages/likec4/src/cli/export/png/handler.ts (content)
⚔️ packages/vite-plugin/src/virtuals/icons.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 clearly and concisely summarizes the main feature: exporting LikeC4 views to Draw.io format across CLI, Playground, and documentation.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering the feature scope, refactors, what's included/excluded, checklist completion, and verification steps.
Docstring Coverage ✅ Passed Docstring coverage is 86.67% 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 docstrings
🧪 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.

sraphaz and others added 3 commits February 13, 2026 02:18
…utView.req, mxGeometry relative=1, parse trim, PNG timeout+try/finally)

Co-authored-by: Cursor <[email protected]>
…depth limit, normalizeEdgePoint type, JSDoc compressed, charCodeAt, parse fallback comment, decompress success test, spec comments)

Co-authored-by: Cursor <[email protected]>
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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/vite-plugin/src/virtuals/icons.ts`:
- Around line 68-77: embedAsJsString currently enforces SAFE_PROJECT_ID_REGEX
against the entire package path (the value constructed via joinURL like
'likec4:icons/someProject'), causing throws because ':' and '/' are valid in the
known-safe prefix; change the logic so only the dynamic project id is validated
with SAFE_PROJECT_ID_REGEX (e.g., validate p.id before building the path), then
construct the full package path using the known-safe prefix
(joinURL('likec4:icons', p.id)) and return JSON.stringify of that full path
without revalidating it in embedAsJsString; update references to
embedAsJsString, SAFE_PROJECT_ID_REGEX, joinURL and p.id accordingly so only the
dynamic portion is checked.
🧹 Nitpick comments (1)
apps/playground/src/monaco/LanguageClientSync.tsx (1)

8-8: Use typed sendRequest overload with LayoutView.req instead of string method.

The LayoutView.req property is a RequestType object (like FetchComputedModel.req, FetchLayoutedModel.req, and other requests in this file), so it can be passed directly to sendRequest to leverage the typed overload. This eliminates the need for an as LayoutViewProtocol.Res cast and the separate type-only import, while keeping full type inference.

♻️ Suggested update
-import type { LayoutView as LayoutViewProtocol } from '@likec4/language-server/protocol'
 import {
   BuildDocuments,
   ChangeView,
   DidChangeModelNotification,
   FetchComputedModel,
   FetchLayoutedModel,
   LayoutView,
   Locate,
 } from '@likec4/language-server/protocol'
@@
-            const res = (await c.sendRequest(LayoutView.req.method, {
-              viewId,
-            })) as LayoutViewProtocol.Res
+            const res = await c.sendRequest(LayoutView.req, { viewId })
@@
-      const res = (await languageClient().sendRequest(LayoutView.req.method, {
-        viewId,
-      })) as LayoutViewProtocol.Res
+      const res = await languageClient().sendRequest(LayoutView.req, { viewId })

Also applies to: 114-116, 130-132

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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/playground/src/monaco/LanguageClientSync.tsx`:
- Line 8: The type-only import "LayoutView as LayoutViewProtocol" is mixed with
value imports in LanguageClientSync.tsx; move this type-only import into the
dedicated type-only import block at the top of the file and split any mixed
imports so that value imports remain in their own block, keeping all imports
sorted alphabetically; specifically relocate the "import type { LayoutView as
LayoutViewProtocol } from '@likec4/language-server/protocol'" line into the
type-only section to comply with the repo import grouping guideline.

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