Skip to content

feat: DrawIO export (CLI, Playground, round-trip) + CodeRabbit fixes#25

Merged
sraphaz merged 2 commits intomainfrom
feat/drawio-export
Feb 14, 2026
Merged

feat: DrawIO export (CLI, Playground, round-trip) + CodeRabbit fixes#25
sraphaz merged 2 commits intomainfrom
feat/drawio-export

Conversation

@sraphaz
Copy link
Copy Markdown
Owner

@sraphaz sraphaz commented Feb 14, 2026

Summary

This PR adds DrawIO (draw.io) export to LikeC4: export views to .drawio XML (single or multi-file), with optional round-trip options (layout, stroke colors/widths, edge waypoints), CLI command, and Playground context menu integration. It also includes a cleanup pass: removal of build artifacts that were accidentally committed, and addressing CodeRabbit review (actionable fixes + nitpicks).


Features

Export to DrawIO

  • @likec4/generators: generateDrawio, generateDrawioMulti produce draw.io-compatible XML; optional GenerateDrawioOptions for layout override, stroke colors/widths per node, edge waypoints, and round-trip comment blocks in source.
  • Round-trip: Export can embed // likec4.layout.viewId, // likec4.strokeColor, // likec4.strokeWidth, // likec4.edge.waypoints in .c4 source; parseDrawioRoundtripComments reads them for re-export after editing in draw.io.
  • CLI: likec4 export drawio <path> -o <outdir> with options --all-in-one, --roundtrip, --uncompressed, --project, --use-dot.
  • Playground: Right-click on diagram canvas → “Export to DrawIO” (current view) or “Export all” (all views). Optional getSourceContent for round-trip when used outside the provider.

Tests & E2E

  • Unit: generate-drawio.spec.ts, drawio-demo-export-import.spec.ts (cloud-system demo, element/edge counts, loadable XML). Timeouts increased for fromWorkspace/layoutedModel tests.
  • E2E: Playwright tests for DrawIO context menu in Playground (pnpm test:playground from e2e/).

Cleanup & CodeRabbit fixes

Build artifacts removal

  • Removed ~1555 compiled .js/.d.ts and PR temp docs that had been added in a previous commit (git add -A). All were verified to match strict criteria (only artifacts).
  • .gitignore updated: *.spec.js, *.test.js, apps/playground/src/**/*.js, packages/*/src/**/*.js, .cursor/, and comments on scope.

Actionable (CodeRabbit)

  • packages/config filenames: isLikeC4Config / isLikeC4NonJsonConfig return boolean instead of type predicates (they only check basename; narrowing full path was unsound).
  • .cursor/: Directory gitignored; git-push-remotes.mdc removed from tracking. AGENTS.md notes that contributors may use .cursor/rules/ locally and should not commit it.
  • pako: pako and @types/pako added to pnpm workspace catalog; packages/generators uses catalog:.
  • parse-drawio: In computeContainerTitles, title cells are created with parent="${containerId}" by the generator; parse logic now uses v.parent === cont.id instead of v.parent === cont.parent. Self-closing tag detection uses full trimmed open tag. TODO added for round-trip comment parser deduplication.
  • logger: ViteLogger type now omits both debug and hasErrorLogged so they are optional for Vite.
  • ci-validate-in-docker: NODE_ENV=production moved to after pnpm install so devDependencies are installed; Corepack pnpm set to 10.29.3 (match .tool-versions); comment added for version sync.
  • vitest: Root config merges configDefaults.exclude with custom patterns so default exclusions are preserved.

Nitpicks

  • Scripts: ci-validate-in-docker.ps1 uses Write-Output instead of Write-Host.
  • documentation-provider: Null guards for parseDeploymentNode and parseDeployedInstance (aligned with parseElement).
  • JSDoc: HoverProvider (class + constructor), browser startLanguageServer, drawio handler (sequential export comment).
  • Playground: useDrawioContextMenuActions — pass onExportError directly; comment on stabilizing viewStates for useMemo. Monaco: pass setLayoutedModelApi directly. LanguageClientSync: logger.warn in catch blocks for layouted model / layout view failures.
  • generate-drawio: Removed duplicate html=1; at end of vertex style; comments for getDefaultStrokeWidth and node sort. generate-drawio.spec: Comment on duplication with drawio-test-utils; mockViewModel as plain function (no vi.fn). e2e: Removed redundant $.nothrow = false; simplified assertion message (dead branch). .gitignore: Comments on glob scope and personal workflow files.

Changesets

  • drawio-related changesets are in .changeset/ (e.g. drawio-cli-roundtrip-e2e, drawio-roundtrip-options, drawio-import-postpack).

Checklist

  • Build and typecheck pass
  • Tests updated/added (unit + e2e)
  • CodeRabbit actionable and nitpick comments addressed
  • No build artifacts or personal config committed

Summary by CodeRabbit

  • New Features

    • Added export operation state tracking to prevent concurrent diagram exports.
    • Added support for symbolic links in workspace file reading.
  • Bug Fixes

    • Improved color fallback handling in diagram generation to ensure valid values.
    • Added cell ID range validation to prevent diagram generation failures.
  • Improvements

    • Enhanced logging for failed view layout operations with failure counts.

…e color fallbacks, cellId range assert, exporting guard, parseStyle JSDoc, single-diagram no re-parse

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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

This PR adds an exporting state flag to prevent concurrent DrawIO exports in the Playground, enhances color handling and ID validation in the diagram generator, adds symlink support to the export handler, and introduces layout failure logging.

Changes

Cohort / File(s) Summary
DrawIO Export State Management
apps/playground/src/components/drawio/DrawioContextMenu.tsx, apps/playground/src/components/drawio/DrawioContextMenuDropdown.tsx, apps/playground/src/components/drawio/DrawioContextMenuProvider.tsx
Added exporting boolean prop to track ongoing export operations. Disables "Export all views" button when an export is already in progress by updating disabled state condition.
DrawIO Actions Hook
apps/playground/src/components/drawio/useDrawioContextMenuActions.ts
Introduced local exporting state with getter/setter. Prevents concurrent handleExportAllViews calls by short-circuiting when exporting is true. Sets exporting to true at start and false in finally block. Exposes exporting as public return property.
Diagram Generator & Color Handling
packages/generators/src/drawio/generate-drawio.ts
Enhanced color helpers with explicit string coercion and fallback defaults for fill, stroke, and font properties. Added ID range validation to throw when cell IDs would exceed CONTAINER_TITLE_CELL_ID_START.
Diagram Parsing & Export Handler
packages/generators/src/drawio/parse-drawio.ts, packages/likec4/src/cli/export/drawio/handler.ts
Added docblock clarifying parseStyle behavior. Implemented special-case parsing for single-diagram scenario. Enhanced file traversal with switch-based dispatch to support symbolic links pointing to source files.
Layout Failure Tracking
apps/playground/src/monaco/LanguageClientSync.tsx
Computes failed view count in layoutViews and logs warning with format "/ views failed to layout" when failures occur.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 With exporting flags held high and tight,
No double exports in the night!
Colors now bloom with safety's glow,
IDs checked before we go. ✨

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

⚔️ apps/playground/src/components/drawio/DrawioContextMenu.tsx (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)
⚔️ packages/generators/src/drawio/generate-drawio.ts (content)
⚔️ packages/generators/src/drawio/parse-drawio.ts (content)
⚔️ packages/likec4/src/cli/export/drawio/handler.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 summarizes the main change: DrawIO export feature addition to CLI, Playground, and round-trip functionality, plus cleanup of CodeRabbit issues.
Description check ✅ Passed The description is comprehensive and covers the feature set, implementation details, cleanup work, and checklist items. It aligns well with the PR objectives and changes made.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% 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. 🎉

🧹 Recent nitpick comments
packages/likec4/src/cli/export/drawio/handler.ts (1)

102-111: Consider adding symlink-to-directory traversal for consistency.

The current implementation handles symlinks to files but silently skips symlinks to directories. If a workspace uses symlinks to organize shared .c4 definitions across projects, those would be missed during roundtrip content gathering.

♻️ Optional: Add symlink-to-directory handling
         case e.isSymbolicLink(): {
           const st = await stat(full).catch(() => null)
           if (st?.isFile() && isSourceFile(e.name)) {
             const content = await readFile(full, 'utf-8').catch(err => {
               if (logger?.debug) logger.debug(`${k.dim('Roundtrip:')} readFile failed`, { file: full, err })
               return ''
             })
             if (content) chunks.push(content)
+          } else if (st?.isDirectory() && !ROUNDTRIP_IGNORED_DIRS.has(e.name)) {
+            // Follow symlinked directories (realpath cycle detection applies)
+            await walk(full, depth + 1)
           }
           break
         }

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

@sraphaz sraphaz merged commit cb709db into main Feb 14, 2026
28 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.

1 participant