Revert "feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io"#2629
Revert "feat(cli,playground,docs,generators): Export LikeC4 views to Draw.io"#2629
Conversation
|
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughThis PR removes Draw.io import/export functionality across the codebase, replaces the Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 12
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/playground/scripts/generate.mts (1)
8-12:⚠️ Potential issue | 🟡 MinorHardcoded
SESSION_ENCRYPTION_KEYin source control.Even for a local-dev bootstrap, committing a secret value to VCS means it'll persist in git history. If this key is only a placeholder for local development, consider noting that explicitly with a comment, or generating a random value at script runtime instead.
packages/config/src/filenames.ts (1)
30-35:⚠️ Potential issue | 🟡 MinorJSDoc comment is incomplete — missing CJS and CTS.
The comment lists
(JS, MJS, TS, MTS)but the array also includes.cjsand.cts.📝 Proposed fix
-/** - * Checks if the given filename is a LikeC4 non-JSON config file (JS, MJS, TS, MTS) - */ +/** + * Checks if the given filename is a LikeC4 non-JSON config file (JS, CJS, MJS, TS, CTS, MTS) + */
🤖 Fix all issues with AI agents
In `@BEGINNER_NONTES.md`:
- Around line 1-75: The file BEGINNER_NONTES.md appears to be accidental
developer notes — either remove it from the PR or clean and relocate it; if
keeping it, rename the file to BEGINNER_NOTES.md, fix title "Beginner Nontes" →
"Beginner Notes", correct typos "Ubluntu" → "Ubuntu" and "Avodi pnmp" → "Avoid
pnpm", remove the stray literal "shell" inside the code block and change the
fence to ```shell, remove the trailing backtick in the pnpm turbo command, and
add language specifiers to all fenced code blocks (e.g., ```bash or ```shell);
consider moving the cleaned content into CONTRIBUTING.md or project wiki instead
of this PR.
In `@packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx`:
- Around line 140-161: Restore the original revert behavior by removing the new
empty-state UI block: delete the conditional JSX that renders the <m.div
key={'empty'} ...> containing Tooltip, ThemeIcon and IconAlertTriangle when
!hasNotations, and instead reinstate the early-return pattern (if
(!hasNotations) return null) in NotationPanel (where hasNotations is computed)
so the component renders nothing when there are no notations; if an empty-state
is desired, move that JSX into a separate PR/component rather than keeping it in
this revert.
In `@packages/generators/src/drawio/generate-drawio.ts`:
- Around line 160-162: The current code builds userData as an <mxUserObject>
string and then injects it as a child inside the existing <mxCell>, which is
incorrect; instead, when description/technology exist (userData non-empty) wrap
the entire cell with an outer <mxUserObject> (or <UserObject>), put the <mxCell>
as its child, and place the description/technology <data> entries inside that
outer element; locate the assembly logic that builds userData and the place
where the <mxCell> is emitted (symbols: userData, mxUserObject, mxCell in
generate-drawio.ts) and change the output composition so that if userData is
non-empty you emit <mxUserObject>…<mxCell>…</mxCell></mxUserObject> rather than
embedding <mxUserObject> inside the <mxCell>.
- Line 210: The MXGraphModel element in the drawio XML output contains an
unquoted attribute pageScale=1 which produces invalid XML; update the
string/template that emits the MXGraphModel (look for the literal '<mxGraphModel
dx="800" ... pageScale=1 pageWidth="827" ...' in generate-drawio.ts) to quote
the value as pageScale="1" so all attributes are properly quoted and the
generated XML is well-formed.
In `@packages/generators/src/drawio/parse-drawio.ts`:
- Around line 56-63: parseUserData currently only searches for <data
key="likec4Description"> inside the mxCell inner content so it misses standard
Draw.io custom data which is stored in a wrapping <UserObject> or <object>;
update parseUserData to also detect and parse <UserObject> or <object> elements
that wrap an <mxCell> (extract their <data key="likec4Description"> and <data
key="likec4Technology">), and update the generator in generate-drawio (where
mxUserObject is currently nested inside mxCell) to emit the
mxUserObject/UserObject/object as a sibling/wrapper of <mxCell> consistent with
Draw.io export so round-trips preserve description and technology.
- Around line 159-173: The byId Map is built from all cells but never used;
remove the unused variable and its population loop (the const byId = new
Map<string, DrawioCell>() and the for (const c of cells) { byId.set(c.id, c) }
block) and keep idToCell (which is populated from vertices) as the authoritative
mapping; verify there are no remaining references to byId and ensure idToCell
continues to be used where vertex lookups occur (symbols: byId, idToCell,
DrawioCell, vertices).
In `@packages/language-server/src/model/model-locator.ts`:
- Around line 62-63: The local variables astNodeOrFqn and projectId in the
locateModel function are declared without types; add explicit type annotations
to remove implicit any by matching the function's overload signature—annotate
astNodeOrFqn with the same union/type used for the function's first overload
parameter (e.g., string | <AST node type>) and annotate projectId with the
overload's projectId type (e.g., string | undefined); update the declarations
for astNodeOrFqn and projectId in model-locator.ts accordingly.
In `@packages/likec4/src/cli/export/drawio/handler.ts`:
- Around line 43-46: The current call to fromWorkspace uses the
option-definition object useDotBin in the ternary (useDotBin ? 'binary' :
'wasm'), which is always truthy; update the graphviz selection to use the parsed
CLI value args['use-dot'] instead (e.g., graphviz: args['use-dot'] ? 'binary' :
'wasm') in the fromWorkspace invocation inside the handler (the block that
constructs likec4), leaving the imported useDotBin option definition unchanged.
In `@packages/likec4/src/cli/export/json/handler.ts`:
- Around line 52-55: The graphviz selection is using the imported option
definition useDotBin (always truthy) instead of the CLI value, so update the
call that creates languageServices (the fromWorkspace invocation that assigns
languageServices) to read the actual flag from args (use args['use-dot']) and
set graphviz: args['use-dot'] ? 'binary' : 'wasm'; keep the rest of the options
(watch: false) unchanged and ensure you reference the same symbols
(fromWorkspace, languageServices, graphviz) so the change is localized.
In `@packages/likec4/src/cli/export/png/handler.ts`:
- Around line 152-165: The Vite dev server started when serverUrl is falsy (via
viteDev in the server variable) may never be closed if the export loop
(pngHandler) throws; wrap the export loop and subsequent cleanup in a
try/finally so server.close() always runs. Concretely, around the code that
calls pngHandler (and any export loop that can throw), introduce try { /* export
logic */ } finally { if (server) await server.close(); } — reference the server
variable, viteDev(), resolveServerUrl(), and the pngHandler call so you close
the started server regardless of exceptions.
In `@packages/likec4/src/cli/index.ts`:
- Around line 94-96: The current catch on main() swallows the thrown error;
update the catch handler for main() to accept the error (e.g., err) and log it
before calling exit(1). Locate the invocation main().catch(() => { exit(1) })
and change it to a catch that prints the error (using console.error or
process.stderr) with a brief context message and then calls exit(1), so failures
like invalid workspace paths are visible to the user.
In `@packages/likec4/src/logger.ts`:
- Around line 49-51: The hasErrorLogged function currently throws an Error at
runtime; change its implementation to be a no-op that returns false instead.
Locate the hasErrorLogged method in logger.ts and replace the throw new
Error('Function not implemented.') with a simple return false so the function
satisfies Vite's logger interface without crashing during error deduplication.
🟡 Minor comments (12)
packages/language-server/CHANGELOG.md-19-19 (1)
19-19:⚠️ Potential issue | 🟡 MinorAdd a language tag to the fenced code block.
MD040 is triggered because the opening fence lacks a language. Consider restoring
likec4(ortext) for proper rendering.💡 Suggested fix
- ``` + ```likec4packages/log/src/formatters.ts-49-58 (1)
49-58:⚠️ Potential issue | 🟡 MinorTypo in variable name:
errorMessge→errorMessage.Used consistently across lines 49–58 so there's no runtime breakage, but it's clearly a misspelling.
Proposed fix
- let errorMessge = error.message + let errorMessage = error.message if (error.stack) { - errorMessge = errorMessge + '\n' + indent(error.stack.split('\n').slice(1)) + errorMessage = errorMessage + '\n' + indent(error.stack.split('\n').slice(1)) } if (color) { - errorMessge = `${ansiColors.red}${errorMessge}${RESET}` + errorMessage = `${ansiColors.red}${errorMessage}${RESET}` } return { ...values, - message: values.message + '\n' + indent(errorMessge), + message: values.message + '\n' + indent(errorMessage), }packages/log/src/formatters.ts-16-16 (1)
16-16:⚠️ Potential issue | 🟡 MinorTypo in function name:
gerErrorFromLogRecord→getErrorFromLogRecord.This appears to be an accidental rename introduced during the revert. The
tis missing fromget.Proposed fix
-function gerErrorFromLogRecord(record: LogRecord): Error | null { +function getErrorFromLogRecord(record: LogRecord): Error | null {Also update the two call sites (lines 39 and 47):
- const error = gerErrorFromLogRecord(record) + const error = getErrorFromLogRecord(record)packages/vscode/src/node/useMcpRegistration.ts-28-28 (1)
28-28:⚠️ Potential issue | 🟡 MinorNon-null assertion removes the previous safety guard.
The old code checked
extensionContext.valueand threw a descriptive error if unavailable. The!assertion will produce an opaqueTypeErrorifextensionContextis evernullhere. Since this is a revert PR, this is acceptable if it restores the prior state, but worth noting the loss of a clear error message.packages/vscode/src/useWorkspaceId.ts-7-21 (1)
7-21:⚠️ Potential issue | 🟡 MinorJSDoc is misleading — workspace state persistence is commented out.
Line 9 claims the ID is "generated once per workspace and stored in the workspace state," but the active code on Line 21 generates a fresh ID every session (the singleton ensures once-per-session, not once-per-workspace). The commented-out code (Lines 12–20) would have implemented persistence, but it's inactive.
Either update the JSDoc to reflect reality ("generated once per extension session") or uncomment the persistence logic. Also,
extensionContext(Line 4) is unused in active code since the workspace-state path is commented out.Option A: Fix the JSDoc to match active behavior
/** * Get or create a unique workspace ID. This is used to correlate the MCP server with the workspace. - * It is generated once per workspace and stored in the workspace state. + * It is generated once per extension session (not persisted across sessions). */packages/likec4/src/cli/export/json/handler.ts-94-98 (1)
94-98:⚠️ Potential issue | 🟡 MinorEdge case: all projects empty yields an empty array or
undefinedin the output.If every project is skipped (empty),
projectsModelshas length 0 and the ternary falls through to the array branch, writing[]to the JSON file. This is silently successful, which may surprise users. Consider warning or erroring when there's nothing to export.packages/likec4/src/cli/import/drawio/handler.ts-45-53 (1)
45-53:⚠️ Potential issue | 🟡 MinorOutput directory is not created before writing.
If the user specifies
--outfilein a directory that doesn't exist,writeFilewill throwENOENT. The sibling export handler (export/drawio/handler.ts, Line 59) callsmkdir(outdir, { recursive: true })before writing. Consider doing the same here.Proposed fix
+import { mkdir, readFile, writeFile } from 'node:fs/promises' -import { readFile, writeFile } from 'node:fs/promises'+ await mkdir(dirname(outfile), { recursive: true }) await writeFile(outfile, likec4Source)packages/likec4/src/cli/index.ts-23-23 (1)
23-23:⚠️ Potential issue | 🟡 MinorTypo:
configureloggershould beconfigureLogger(camelCase).The local wrapper function is named
configurelogger(all lowercase) while the imported function it wraps isconfigureLogger. This breaks camelCase convention and is inconsistent with the import on Line 3.Proposed fix
-function configurelogger(isDebug = isDevelopment) { +function configureLogger_(isDebug = isDevelopment) {Or simply rename to
configureLoggerif there's no shadowing concern (there is — the import has the same name). An alternative is to alias the import:-import { configureLogger, getAnsiColorFormatter, getConsoleFormatter, getConsoleSink } from '@likec4/log' +import { configureLogger as setupLogger, getAnsiColorFormatter, getConsoleFormatter, getConsoleSink } from '@likec4/log'and use
setupLoggerinside the local function body.apps/playground/src/components/drawio/useDrawioActions.ts-23-39 (1)
23-39:⚠️ Potential issue | 🟡 MinorMissing
reader.onerrorhandler — file-read failures are silently ignored.If
FileReader.readAsTextencounters an error (e.g., permission denied, file locked), no feedback is provided to the user.Proposed fix
const reader = new FileReader() + reader.onerror = () => { + console.error('Failed to read DrawIO file', reader.error) + } reader.onload = () => {packages/likec4/src/cli/export/drawio/handler.ts-50-57 (1)
50-57:⚠️ Potential issue | 🟡 MinorRemove unsafe
as ProjectIdcast — validate string input properly before passing to ensureProjectId.Line 51 casts
onlyProject(a yargs string option) directly toProjectIdwithout validation. WhileensureProjectIddoes validate via invariant check, relying on runtime exceptions for type safety is fragile. The cast should be replaced with proper validation, similar to theensureProjectIdhelper inpackages/likec4/src/cli/utils.tsthat validates string paths and returns a validProjectId.packages/generators/src/drawio/parse-drawio.ts-95-96 (1)
95-96:⚠️ Potential issue | 🟡 MinorDead branch:
styleMap.get('fillColor')can never match.
parseStylelowercases all keys on Line 49 (k.toLowerCase()), so the map will only ever contain'fillcolor'. The fallback?? styleMap.get('fillColor')is dead code. Same applies tostrokeColoron Line 96.Proposed fix
- const fillColor = styleMap.get('fillcolor') ?? styleMap.get('fillColor') - const strokeColor = styleMap.get('strokecolor') ?? styleMap.get('strokeColor') + const fillColor = styleMap.get('fillcolor') + const strokeColor = styleMap.get('strokecolor')packages/generators/src/drawio/generate-drawio.ts-199-205 (1)
199-205:⚠️ Potential issue | 🟡 MinorRemove
<mxGeometry>from the default parent cell (id="1").The default layer cell in Draw.io should not include geometry, as it is a container, not a drawable shape. Per the mxGraph/Draw.io standard structure,
id="1"is the default layer and typically has no<mxGeometry>. Including geometry may cause Draw.io to misinterpret the layer or offset child positions unexpectedly.
🧹 Nitpick comments (21)
e2e/src/likec4-cli-build.spec.ts (1)
10-10: Nit: extra space in comment.There's a double space before the URL.
Proposed fix
-// see https://github.com/likec4/likec4/issues/2076 +// see https://github.com/likec4/likec4/issues/2076packages/vscode/tsdown.config.ts (1)
112-113:process.exit(1)bypasses async cleanup; consider throwing instead.Using
process.exit(1)terminates immediately without lettingtsdown's hook runner handle the failure or run any cleanup. Throwing an error would propagate naturally and still fail the build with a clear message. That said, since this is a revert PR restoring prior behavior, this is a minor nit.♻️ Optional: throw instead of hard exit
- console.error(`"${vscodePreview}" not found`) - process.exit(1) + throw new Error(`"${vscodePreview}" not found`)packages/language-server/src/browser/index.ts (1)
21-22: Placeholder comment adds no value.
/* browser specific setup code */is a vague comment with no actionable information. If this was meant as a marker for future work, aTODOwith context would be clearer; otherwise, consider removing it.Proposed cleanup
- /* browser specific setup code */ - const messageReader = new BrowserMessageReader(port)packages/language-server/src/browser/worker.ts (1)
6-9: No error handling around worker startup.
startLanguageServer(self)is invoked unconditionally with notry/catchor global error listeners. If startup fails, the worker will silently die in many browser environments, making debugging difficult. Since this is a revert to a prior known state, this is non-blocking, but worth considering for a follow-up.Minimal error handling suggestion
-startLanguageServer(self) +try { + startLanguageServer(self) +} catch (e) { + console.error('Failed to start language server:', e) +}packages/log/src/index.ts (1)
61-61: Avoid<any, any>— use the function's own generic parameters instead.The function already declares
TSinkIdandTFilterId; threading them through avoids theanyescape hatch:Proposed fix
- configureLogtape<any, any>({ + configureLogtape<TSinkId | 'console', TFilterId>({As per coding guidelines, "TypeScript must use explicit types and avoid using
any".packages/vscode/src/node/mcp-server.ts (1)
39-43: Silent catch drops non-JSONLIKEC4_WORKSPACEvalues.Since this is a revert, this restores the prior behavior. However, note that if
LIKEC4_WORKSPACEis set to a plain path string (e.g./home/user/project) rather than a JSON-encoded value (e.g."\"/home/user/project\""),JSON.parsewill throw and the value will be silently ignored — falling through toprocess.cwd()on line 49. The reverted PR (#2622) had added a fallback for this case.If supporting plain path strings is desired, consider re-adding the fallback in a follow-up.
packages/log/src/formatters.ts (2)
87-87: Remove commented-out dead code.This leftover comment serves no purpose and adds noise.
27-28: Consider usingsafeStringify()for non-Error values in error properties to preserve information.In lines 27-28, when
error/errproperties contain non-Error values, wrapping withnew Error(\${err}`)relies on template literal coercion via.toString(). For complex objects, this produces"[object Object]"and fornull/undefinedit produces"null"/"undefined", losing valuable debugging information. UsingsafeStringify(err, { indentation: '\t' })` (already available from the utils module) would better preserve the structure of non-Error values.packages/likec4/src/cli/codegen/webcomponent/handler.ts (1)
76-97: Mixed logging surfaces:consolafor debug/warn,loggerfor info.The function now uses two different loggers —
consolafor debug-level and warning messages, andlogger(fromcreateLikeC4Logger) for info-level output. This split makes it harder to configure log levels consistently or filter output by source. Consider consolidating on one logger for the entire handler.apps/playground/src/monaco/index.tsx (1)
15-15: Remove the commented-out export.This dead comment adds no value and may confuse future readers.
🧹 Proposed fix
-// export { default as MonacoEditor } from './MonacoEditor'packages/likec4/src/cli/export/png/handler.ts (2)
144-144: DoublelanguageServices.languageServicesis confusing.The variable
languageServicesreturned byfromWorkspacehas a.languageServicesproperty, making this read awkwardly. Consider aliasing to improve clarity (e.g.,const lsp = languageServices.languageServices).
274-278: Redundantboolean: truealongsidetype: 'boolean'in yargs options.Both
seqandflatspecifyboolean: trueandtype: 'boolean'. One or the other suffices.🧹 Proposed fix
'seq': { - boolean: true, alias: ['sequence'], type: 'boolean', desc: 'use sequence layout for dynamic views', }, 'flat': { alias: ['flatten'], - boolean: true, type: 'boolean', desc: 'flatten all images in output directory ignoring sources structure', },packages/generators/src/drawio/parse-drawio.spec.ts (1)
49-54: Edge case: the "empty" XML is structurally invalid Draw.io (no<diagram>wrapper).The test input
<mxfile><root>...</root></mxfile>omits the<diagram><mxGraphModel>wrapper that Draw.io produces. This is a fine robustness test, but consider adding a comment clarifying that it's intentionally malformed to test graceful degradation.packages/generators/src/drawio/generate-drawio.spec.ts (1)
48-52: Mock uses unsafe double cast but works due to defensive code ingenerateDrawio.The mock only provides
$view, butgenerateDrawioalso accesses$styles(lines 66, 82) andbounds(line 220). While this works at runtime because both properties are checked defensively (safe guard with'$styles' in viewmodeland try-catch forbounds), the type safety is still compromised. Adding at least the$stylesproperty to the mock would improve type safety for future changes.apps/playground/src/routes/w.$workspaceId/route.tsx (1)
2-3: Merge duplicate imports from the same module.Lines 2 and 3 both import from
'$components/drawio/DrawioContextMenuProvider'. Combine into a single import statement.-import { DrawioContextMenuProvider } from '$components/drawio/DrawioContextMenuProvider' -import { useDrawioContextMenu } from '$components/drawio/DrawioContextMenuProvider' +import { DrawioContextMenuProvider, useDrawioContextMenu } from '$components/drawio/DrawioContextMenuProvider'packages/generators/src/drawio/generate-drawio.ts (3)
134-142: Production code accommodates test-onlyposition/sizefields.The
getBBoxhelper casts to a union withposition?: [number, number]andsize?: { width: number; height: number }solely to support "legacy mocks." Production code shouldn't carry compatibility shims for test fixtures. Instead, fix the test mocks to use the realDiagramNodeshape (x,y,width,height).
66-76: Fragile internal property access and bareas stringcasts.
'$styles' in viewmodelprobes an internal/undocumented property, andvalues.elements.fill as stringassumes a specific return shape without validation. If$stylesor.colors()return type changes, this will silently produceundefinedfill/stroke values baked into the XML.Consider narrowing the type or adding a runtime check (e.g.
typeof values.elements.fill === 'string').
124-132: Non-transitive sort comparator may produce unpredictable ordering.The comparator returns
0for many non-equal pairs (e.g. two nodes with different non-null parents that aren't ancestors of each other). Non-transitive comparators lead to implementation-dependent sort results. Consider a topological sort or sorting by depth first, then by parent, which guarantees parents are emitted before children.packages/generators/src/drawio/parse-drawio.ts (3)
28-32: Low risk: regex built fromnameparameter — safe in current usage but fragile.The static analysis tool flagged this as a potential ReDoS vector. All current callers pass hardcoded attribute names (
'id','value','x', etc.), so there's no exploitable risk today. However, if a future caller passes untrusted input, the unescapednamecould break the regex or enable ReDoS.Consider escaping
nameor switching to a simpleindexOf/sliceapproach:Alternative: indexOf-based extraction
function getAttr(attrs: string, name: string): string | undefined { - const re = new RegExp(`${name}="([^"]*)"`, 'i') - const m = attrs.match(re) - return m ? m[1] : undefined + const search = `${name}="` + const idx = attrs.toLowerCase().indexOf(search.toLowerCase()) + if (idx < 0) return undefined + const start = idx + search.length + const end = attrs.indexOf('"', start) + return end > start ? attrs.slice(start, end) : undefined }
293-300: Every element without children/desc/tech/color gets an empty block.When
hasBodyis false, the code emits an empty{ }block (Lines 296–297). This produces noisy output like:myElement = container 'My Element' { }Consider omitting the block entirely for leaf elements with no metadata.
Proposed fix
} else { - lines.push(`${pad}{`) - lines.push(`${pad}}`) + // No body needed — omit empty block }
72-72: Unnecessary indirection:geomAttris just an alias forgetAttr.
const geomAttr = (tag: string, name: string) => getAttr(tag, name)adds a wrapper that does nothing. UsegetAttrdirectly on Lines 91–94.
| # Beginner Nontes | ||
|
|
||
| ## Introduction | ||
|
|
||
| These are the notes for beginners to the project, or Javascript/Typescript eco-system. | ||
| (They can be ignored for the experienced and knowledgeable.) | ||
|
|
||
| ## For the required version of nodejs and pnpm, dprint | ||
|
|
||
| - Install asdf and use it, which will follow the ./.tool-versions satisfying the version requirements | ||
| - Note, the already installed version of nodejs (node) in Ubuntu might not be the expected | ||
| version, as there is no new install, asdf might not have the chance to enforce | ||
| the nodejs' version. | ||
| If that is the case, you should manually install the correct version and use it. | ||
|
|
||
| ## pnpm build failure of "FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory" | ||
|
|
||
| Try | ||
| ``` | ||
| NODE_OPTIONS="--max-old-space-size=4096" pnpm build | ||
| ``` | ||
|
|
||
| I had the error in Ubuntu/WSL 2, the solution worked. | ||
|
|
||
| To make it the setting permanent, add the following to ~/.bashrc | ||
|
|
||
| ``` | ||
| shell | ||
| # Avodi pnmp build failure: | ||
| export NODE_OPTIONS="--max-old-space-size=4096" | ||
| # end of Avodi pnmp build failure | ||
| ``` | ||
|
|
||
| ## dprint installation | ||
|
|
||
| Install locally, | ||
|
|
||
| ``` | ||
| npm install dprint | ||
| ``` | ||
|
|
||
| this would have the error in VS Code and its derivations that dprint is not accessible. | ||
|
|
||
| The local dprint has to be accessed via (which VS Code, etc. would not assumed so): | ||
|
|
||
| ``` | ||
| npx dprint | ||
| ``` | ||
|
|
||
| To install globally would not have the issue: | ||
|
|
||
| ``` | ||
| npm install -g dprint | ||
| ``` | ||
|
|
||
| It seems that the local installation does not have any problem with the | ||
| project's setup, as I have not seen any error when running the tasks in the project. | ||
|
|
||
| ## To propagate any update in the project to the VS Code extension and try with the modified extension in VS Code | ||
|
|
||
| Here are the steps: | ||
| 1. Execute at the project root, | ||
| ``` | ||
| pnpm turbo run build --filter='@likec4/vscode-preview' --filter='likec4-vscode'` | ||
| ``` | ||
| 2. In a VS Code session, in "Run and Debug view", select "Run Extension" and run it | ||
|
|
||
| Then in the newly spawn VS Code session, | ||
| the local extension with the modifications will be in effect. | ||
|
|
||
| ## Running VS Code with the local modified extension in Ubluntu/WSL2 might block | ||
| the Ubluntu/WSL2's operation | ||
|
|
||
| I found a git commit Ubluntu/WSL2 was too slow to complete when | ||
| Running VS Code with the local modified extension in the Ubluntu/WSL2 |
There was a problem hiding this comment.
This file appears to be personal developer notes accidentally included in the PR.
This is a revert PR, but BEGINNER_NONTES.md is a new file that doesn't belong to the reverted feature. It also has numerous issues:
- Filename typo:
BEGINNER_NONTES.md→BEGINNER_NOTES.md - Title typo (Line 1): "Nontes" → "Notes"
- "Ubluntu" (Lines 71, 72, 74): should be "Ubuntu"
- "Avodi pnmp" (Lines 29, 31): should be "Avoid pnpm"
- Stray
shellon Line 28 — likely meant as a code fence language specifier (\``shell`), not content inside the block - Trailing backtick on Line 64 in the command string
- All code blocks are missing language specifiers (flagged by markdownlint MD040)
Consider removing this file from the PR entirely, or if it's intended as project documentation, it needs significant cleanup and likely belongs in a CONTRIBUTING.md or wiki instead.
🧰 Tools
🪛 LanguageTool
[grammar] ~1-~1: Ensure spelling is correct
Context: # Beginner Nontes ## Introduction These are the notes for be...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[style] ~42-~42: Consider using “inaccessible” to avoid wordiness.
Context: ...Code and its derivations that dprint is not accessible. The local dprint has to be accessed v...
(NOT_ABLE_PREMIUM)
[style] ~55-~55: Consider a more formal alternative to strengthen your wording.
Context: ... issue: npm install -g dprint It seems that the local installation does not ha...
(SEEM_APPEAR)
[grammar] ~71-~71: Ensure spelling is correct
Context: ...de with the local modified extension in Ubluntu/WSL2 might block the Ubluntu/WSL2's ope...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~72-~72: Ensure spelling is correct
Context: ...tension in Ubluntu/WSL2 might block the Ubluntu/WSL2's operation I found a git commit...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.20.0)
[warning] 19-19: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 27-27: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 38-38: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 46-46: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 52-52: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 63-63: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@BEGINNER_NONTES.md` around lines 1 - 75, The file BEGINNER_NONTES.md appears
to be accidental developer notes — either remove it from the PR or clean and
relocate it; if keeping it, rename the file to BEGINNER_NOTES.md, fix title
"Beginner Nontes" → "Beginner Notes", correct typos "Ubluntu" → "Ubuntu" and
"Avodi pnmp" → "Avoid pnpm", remove the stray literal "shell" inside the code
block and change the fence to ```shell, remove the trailing backtick in the pnpm
turbo command, and add language specifiers to all fenced code blocks (e.g.,
```bash or ```shell); consider moving the cleaned content into CONTRIBUTING.md
or project wiki instead of this PR.
| {!hasNotations && ( | ||
| <m.div | ||
| key={'empty'} | ||
| initial={{ opacity: 0.75, translateX: '50%' }} | ||
| animate={{ opacity: 1, translateX: 0 }} | ||
| exit={{ | ||
| translateX: '100%', | ||
| opacity: 0.6, | ||
| }} | ||
| className={styles.container}> | ||
| <Tooltip label="View has no notations" color="orange" {...portalProps}> | ||
| <ThemeIcon | ||
| size={'lg'} | ||
| variant="light" | ||
| color="orange" | ||
| radius={'md'} | ||
| > | ||
| <IconAlertTriangle /> | ||
| </ThemeIcon> | ||
| </Tooltip> | ||
| </m.div> | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Get the file history with more detail
git log --oneline --all -- packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx | head -20Repository: likec4/likec4
Length of output: 229
🏁 Script executed:
#!/bin/bash
# Find the commit that introduced `#2622`
git log --all --grep="#2622" --oneline | head -5Repository: likec4/likec4
Length of output: 142
🏁 Script executed:
#!/bin/bash
# Get current file to see if it contains the empty-state
if [ -f packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx ]; then
echo "=== Current file lines 140-165 ==="
sed -n '140,165p' packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx
else
echo "File not found"
fiRepository: likec4/likec4
Length of output: 872
🏁 Script executed:
#!/bin/bash
# Get the parent commit of `#2622` to see the original state before the feature was added
git show fdd46ff9^:packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx | sed -n '130,170p'Repository: likec4/likec4
Length of output: 96
🏁 Script executed:
#!/bin/bash
# Get the file state right after `#2622` was applied
git show fdd46ff9:packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx | sed -n '130,170p'Repository: likec4/likec4
Length of output: 1216
🏁 Script executed:
#!/bin/bash
# Get what the revert commit changed - show the diff
git show c6a872f2 -- packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx | head -100Repository: likec4/likec4
Length of output: 2946
Empty-state UI is a net-new feature inconsistent with the revert scope.
Before #2622, this component had an early return (if (!hasNotations) return null) that rendered nothing when notations were absent. The revert should restore this behavior, but instead introduces a brand-new empty-state block with an orange alert triangle and tooltip. This is not a restoration of original code—it's a new feature being added under the guise of a revert.
Additionally, displaying a warning-style icon for views with no notations may mislead users into thinking an error occurred, when many views legitimately have no notations.
If this empty-state UI is intentional, split it into a separate PR for clarity; do not bundle it with the revert.
🤖 Prompt for AI Agents
In `@packages/diagram/src/likec4diagram/ui/notation/NotationPanel.tsx` around
lines 140 - 161, Restore the original revert behavior by removing the new
empty-state UI block: delete the conditional JSX that renders the <m.div
key={'empty'} ...> containing Tooltip, ThemeIcon and IconAlertTriangle when
!hasNotations, and instead reinstate the early-return pattern (if
(!hasNotations) return null) in NotationPanel (where hasNotations is computed)
so the component renders nothing when there are no notations; if an empty-state
is desired, move that JSX into a separate PR/component rather than keeping it in
this revert.
| const userData = desc !== '' || tech !== '' | ||
| ? `<mxUserObject><data key="likec4Description">${desc}</data><data key="likec4Technology">${tech}</data></mxUserObject>\n ` | ||
| : '' |
There was a problem hiding this comment.
mxUserObject placed incorrectly inside mxCell.
In Draw.io's schema, mxUserObject is a replacement for mxCell (it wraps the cell), not a child of it. Embedding <mxUserObject> inside <mxCell> content means Draw.io will ignore the custom data when re-importing. The description and technology metadata will be silently lost on round-trip.
The correct approach is to emit <UserObject … > (or <object>) as the outer tag when custom data is present, with the <mxCell> nested inside it.
🤖 Prompt for AI Agents
In `@packages/generators/src/drawio/generate-drawio.ts` around lines 160 - 162,
The current code builds userData as an <mxUserObject> string and then injects it
as a child inside the existing <mxCell>, which is incorrect; instead, when
description/technology exist (userData non-empty) wrap the entire cell with an
outer <mxUserObject> (or <UserObject>), put the <mxCell> as its child, and place
the description/technology <data> entries inside that outer element; locate the
assembly logic that builds userData and the place where the <mxCell> is emitted
(symbols: userData, mxUserObject, mxCell in generate-drawio.ts) and change the
output composition so that if userData is non-empty you emit
<mxUserObject>…<mxCell>…</mxCell></mxUserObject> rather than embedding
<mxUserObject> inside the <mxCell>.
| return `<?xml version="1.0" encoding="UTF-8"?> | ||
| <mxfile host="LikeC4" modified="${new Date().toISOString()}" agent="LikeC4" version="1.0" etag="" type="device"> | ||
| <diagram name="${escapeXml(view.id)}" id="likec4-${escapeXml(view.id)}"> | ||
| <mxGraphModel dx="800" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale=1 pageWidth="827" pageHeight="1169" math="0" shadow="0"> |
There was a problem hiding this comment.
Invalid XML: unquoted attribute value pageScale=1.
XML requires all attribute values to be quoted. pageScale=1 will produce malformed XML. Should be pageScale="1".
Proposed fix
- <mxGraphModel dx="800" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale=1 pageWidth="827" pageHeight="1169" math="0" shadow="0">
+ <mxGraphModel dx="800" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <mxGraphModel dx="800" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale=1 pageWidth="827" pageHeight="1169" math="0" shadow="0"> | |
| <mxGraphModel dx="800" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> |
🤖 Prompt for AI Agents
In `@packages/generators/src/drawio/generate-drawio.ts` at line 210, The
MXGraphModel element in the drawio XML output contains an unquoted attribute
pageScale=1 which produces invalid XML; update the string/template that emits
the MXGraphModel (look for the literal '<mxGraphModel dx="800" ... pageScale=1
pageWidth="827" ...' in generate-drawio.ts) to quote the value as pageScale="1"
so all attributes are properly quoted and the generated XML is well-formed.
| function parseUserData(fullTag: string): { description?: string; technology?: string } { | ||
| const out: { description?: string; technology?: string } = {} | ||
| const descMatch = fullTag.match(/<data\s+key="likec4Description"[^>]*>([\s\S]*?)<\/data>/i) | ||
| if (descMatch?.[1]) out.description = decodeXmlEntities(descMatch[1].trim()) | ||
| const techMatch = fullTag.match(/<data\s+key="likec4Technology"[^>]*>([\s\S]*?)<\/data>/i) | ||
| if (techMatch?.[1]) out.technology = decodeXmlEntities(techMatch[1].trim()) | ||
| return out | ||
| } |
There was a problem hiding this comment.
Round-trip with standard Draw.io files won't preserve user data.
parseUserData looks for <data key="likec4Description"> inside the <mxCell> inner content (Line 90 passes inner). However, in standard Draw.io, custom data lives in <UserObject> or <object> elements that wrap <mxCell>, not inside it. Combined with the generation-side issue in generate-drawio.ts (where mxUserObject is incorrectly nested inside mxCell), the self-round-trip might coincidentally work, but importing files created by the real Draw.io editor will silently lose description/technology metadata.
Also applies to: 86-90
🤖 Prompt for AI Agents
In `@packages/generators/src/drawio/parse-drawio.ts` around lines 56 - 63,
parseUserData currently only searches for <data key="likec4Description"> inside
the mxCell inner content so it misses standard Draw.io custom data which is
stored in a wrapping <UserObject> or <object>; update parseUserData to also
detect and parse <UserObject> or <object> elements that wrap an <mxCell>
(extract their <data key="likec4Description"> and <data
key="likec4Technology">), and update the generator in generate-drawio (where
mxUserObject is currently nested inside mxCell) to emit the
mxUserObject/UserObject/object as a sibling/wrapper of <mxCell> consistent with
Draw.io export so round-trips preserve description and technology.
| const likec4 = await fromWorkspace(args.path, { | ||
| graphviz: useDotBin ? 'binary' : 'wasm', | ||
| watch: false, | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd -t f 'options.ts' --full-path 'packages/likec4/src/cli' -exec head -100 {}Repository: likec4/likec4
Length of output: 156
🏁 Script executed:
cat -n packages/likec4/src/cli/export/drawio/handler.ts | head -60Repository: likec4/likec4
Length of output: 2560
🏁 Script executed:
rg -n "useDotBin|use-dot|use.dot" --type=ts packages/likec4/src/cli -B2 -A2Repository: likec4/likec4
Length of output: 24999
Bug: useDotBin is the yargs option-definition object, not the parsed boolean — graphviz will always be 'binary'.
useDotBin is imported from ../../options (Line 10) and is an option configuration object (e.g., { type: 'boolean', ... }). Since any non-null object is truthy, the ternary useDotBin ? 'binary' : 'wasm' on line 44 will always resolve to 'binary', ignoring the user's actual --use-dot flag.
Use args['use-dot'] instead to access the parsed boolean value.
Proposed fix
const likec4 = await fromWorkspace(args.path, {
- graphviz: useDotBin ? 'binary' : 'wasm',
+ graphviz: args['use-dot'] ? 'binary' : 'wasm',
watch: false,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const likec4 = await fromWorkspace(args.path, { | |
| graphviz: useDotBin ? 'binary' : 'wasm', | |
| watch: false, | |
| }) | |
| const likec4 = await fromWorkspace(args.path, { | |
| graphviz: args['use-dot'] ? 'binary' : 'wasm', | |
| watch: false, | |
| }) |
🤖 Prompt for AI Agents
In `@packages/likec4/src/cli/export/drawio/handler.ts` around lines 43 - 46, The
current call to fromWorkspace uses the option-definition object useDotBin in the
ternary (useDotBin ? 'binary' : 'wasm'), which is always truthy; update the
graphviz selection to use the parsed CLI value args['use-dot'] instead (e.g.,
graphviz: args['use-dot'] ? 'binary' : 'wasm') in the fromWorkspace invocation
inside the handler (the block that constructs likec4), leaving the imported
useDotBin option definition unchanged.
| const languageServices = await fromWorkspace(args.path, { | ||
| graphviz: useDotBin ? 'binary' : 'wasm', | ||
| watch: false, | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the options file and see what useDotBin is
fd 'options' -t f --extension ts -g '*/cli/*'Repository: likec4/likec4
Length of output: 151
🏁 Script executed:
# Now let's read the handler.ts file to see the full context
cat -n packages/likec4/src/cli/export/json/handler.ts | head -80Repository: likec4/likec4
Length of output: 3644
🏁 Script executed:
# Let's also check the options file to see the useDotBin definition
cat -n packages/likec4/src/cli/options.tsRepository: likec4/likec4
Length of output: 3279
🏁 Script executed:
# Let's check the png handler to compare the pattern
fd 'png.*handler' -t f --extension tsRepository: likec4/likec4
Length of output: 39
🏁 Script executed:
# Let's search for other usages of useDotBin in handler contexts
rg -n 'graphviz.*useDotBin' --type=ts -B5 -A5Repository: likec4/likec4
Length of output: 11767
🏁 Script executed:
# Let's check if HandlerParams is defined and what it contains
rg -n 'type HandlerParams|interface HandlerParams' --type=ts -A10Repository: likec4/likec4
Length of output: 6143
🏁 Script executed:
# Let's specifically check the json handler to see if it defines HandlerParams
cat -n packages/likec4/src/cli/export/json/handler.ts | tail -50Repository: likec4/likec4
Length of output: 2042
🏁 Script executed:
# Let's check the drawio handler to see the pattern
cat -n packages/likec4/src/cli/export/drawio/handler.ts | head -50Repository: likec4/likec4
Length of output: 2119
Bug: useDotBin refers to the yargs option definition, not the CLI argument value.
useDotBin imported from ../../options (line 10) is an option definition object, which is always truthy. This causes graphviz to always resolve to 'binary' regardless of the --use-dot flag passed by the user.
Should use args['use-dot'] instead.
🐛 Proposed fix
const timer = startTimer(logger)
const languageServices = await fromWorkspace(args.path, {
- graphviz: useDotBin ? 'binary' : 'wasm',
+ graphviz: args['use-dot'] ? 'binary' : 'wasm',
watch: false,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const languageServices = await fromWorkspace(args.path, { | |
| graphviz: useDotBin ? 'binary' : 'wasm', | |
| watch: false, | |
| }) | |
| const languageServices = await fromWorkspace(args.path, { | |
| graphviz: args['use-dot'] ? 'binary' : 'wasm', | |
| watch: false, | |
| }) |
🤖 Prompt for AI Agents
In `@packages/likec4/src/cli/export/json/handler.ts` around lines 52 - 55, The
graphviz selection is using the imported option definition useDotBin (always
truthy) instead of the CLI value, so update the call that creates
languageServices (the fromWorkspace invocation that assigns languageServices) to
read the actual flag from args (use args['use-dot']) and set graphviz:
args['use-dot'] ? 'binary' : 'wasm'; keep the rest of the options (watch: false)
unchanged and ensure you reference the same symbols (fromWorkspace,
languageServices, graphviz) so the change is localized.
| if (!serverUrl) { | ||
| logger.info(k.cyan(`start preview server`)) | ||
| server = await viteDev({ | ||
| languageServices, | ||
| buildWebcomponent: false, | ||
| openBrowser: false, | ||
| hmr: false, | ||
| }) | ||
| serverUrl = resolveServerUrl(server) | ||
| if (!serverUrl) { | ||
| logger.error('Vite server is not ready, no resolvedUrls') | ||
| throw new Error('Vite server is not ready, no resolvedUrls') | ||
| } | ||
| } |
There was a problem hiding this comment.
Server resource leak: server.close() is skipped when the export loop throws.
If pngHandler throws on line 228 (export failures), the Vite dev server started at line 154 is never closed because lines 232–237 are unreachable. Wrap the export loop and cleanup in a try/finally.
🐛 Proposed fix — wrap in try/finally
+ try {
for (const prj of projects) {
// ... export logic ...
}
+ } finally {
- if (server) {
- logger.info(k.cyan(`stop server`))
- await server.close().catch(e => {
- logger.error(e)
- })
- }
+ if (server) {
+ logger.info(k.cyan(`stop server`))
+ await server.close().catch(e => {
+ logger.error(e)
+ })
+ }
+ }Also applies to: 232-237
🤖 Prompt for AI Agents
In `@packages/likec4/src/cli/export/png/handler.ts` around lines 152 - 165, The
Vite dev server started when serverUrl is falsy (via viteDev in the server
variable) may never be closed if the export loop (pngHandler) throws; wrap the
export loop and subsequent cleanup in a try/finally so server.close() always
runs. Concretely, around the code that calls pngHandler (and any export loop
that can throw), introduce try { /* export logic */ } finally { if (server)
await server.close(); } — reference the server variable, viteDev(),
resolveServerUrl(), and the pngHandler call so you close the started server
regardless of exceptions.
| main().catch(() => { | ||
| exit(1) | ||
| } | ||
|
|
||
| main().catch(exitWithFailure) | ||
| }) |
There was a problem hiding this comment.
Errors from main() are silently swallowed.
The .catch(() => { exit(1) }) discards the error entirely. If the CLI fails (e.g., invalid workspace path, missing files), the user sees no output — just a non-zero exit code. Consider logging the error before exiting:
-main().catch(() => {
+main().catch((err) => {
+ console.error(err)
exit(1)
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| main().catch(() => { | |
| exit(1) | |
| } | |
| main().catch(exitWithFailure) | |
| }) | |
| main().catch((err) => { | |
| console.error(err) | |
| exit(1) | |
| }) |
🤖 Prompt for AI Agents
In `@packages/likec4/src/cli/index.ts` around lines 94 - 96, The current catch on
main() swallows the thrown error; update the catch handler for main() to accept
the error (e.g., err) and log it before calling exit(1). Locate the invocation
main().catch(() => { exit(1) }) and change it to a catch that prints the error
(using console.error or process.stderr) with a brief context message and then
calls exit(1), so failures like invalid workspace paths are visible to the user.
| hasErrorLogged: function(_error: any): boolean { | ||
| throw new Error('Function not implemented.') | ||
| }, |
There was a problem hiding this comment.
hasErrorLogged throws at runtime — return false instead.
Vite's logger interface may call hasErrorLogged during error deduplication. Throwing here will crash the process with an unhelpful message. A no-op stub returning false is safer and still satisfies the interface contract.
🐛 Proposed fix
- hasErrorLogged: function(_error: any): boolean {
- throw new Error('Function not implemented.')
+ hasErrorLogged: function(_error: any): boolean {
+ return false
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| hasErrorLogged: function(_error: any): boolean { | |
| throw new Error('Function not implemented.') | |
| }, | |
| hasErrorLogged: function(_error: any): boolean { | |
| return false | |
| }, |
🤖 Prompt for AI Agents
In `@packages/likec4/src/logger.ts` around lines 49 - 51, The hasErrorLogged
function currently throws an Error at runtime; change its implementation to be a
no-op that returns false instead. Locate the hasErrorLogged method in logger.ts
and replace the throw new Error('Function not implemented.') with a simple
return false so the function satisfies Vite's logger interface without crashing
during error deduplication.
… Draw.io" (likec4#2629) This reverts commit 654240a, reversing changes made to fdd46ff.
Reverts #2622
Summary by CodeRabbit
New Features
.drawiodiagrams to LikeC4 format via CLI commandBug Fixes
Documentation
Chores