diagram-tool 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +64 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +239 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/context.d.ts +27 -0
  6. package/dist/context.js +55 -0
  7. package/dist/context.js.map +1 -0
  8. package/dist/diagram.d.ts +51 -0
  9. package/dist/diagram.js +519 -0
  10. package/dist/diagram.js.map +1 -0
  11. package/dist/dom-shim.d.ts +1 -0
  12. package/dist/dom-shim.js +59 -0
  13. package/dist/dom-shim.js.map +1 -0
  14. package/dist/guide.d.ts +9 -0
  15. package/dist/guide.js +99 -0
  16. package/dist/guide.js.map +1 -0
  17. package/dist/library.d.ts +76 -0
  18. package/dist/library.js +364 -0
  19. package/dist/library.js.map +1 -0
  20. package/dist/llm.d.ts +23 -0
  21. package/dist/llm.js +124 -0
  22. package/dist/llm.js.map +1 -0
  23. package/dist/mcp-install.d.ts +1 -0
  24. package/dist/mcp-install.js +68 -0
  25. package/dist/mcp-install.js.map +1 -0
  26. package/dist/mcp.d.ts +6 -0
  27. package/dist/mcp.js +262 -0
  28. package/dist/mcp.js.map +1 -0
  29. package/dist/public/Assistant-Bold-ZDZZ6JHA.woff2 +0 -0
  30. package/dist/public/Assistant-Medium-DZ25RZU3.woff2 +0 -0
  31. package/dist/public/Assistant-Regular-PLF2XOGW.woff2 +0 -0
  32. package/dist/public/Assistant-SemiBold-CZ5MX6FK.woff2 +0 -0
  33. package/dist/public/viewer.css +2 -0
  34. package/dist/public/viewer.css.map +7 -0
  35. package/dist/public/viewer.js +3092 -0
  36. package/dist/public/viewer.js.map +7 -0
  37. package/dist/server.d.ts +9 -0
  38. package/dist/server.js +166 -0
  39. package/dist/server.js.map +1 -0
  40. package/libraries/UML-ER-library.excalidrawlib +5380 -0
  41. package/libraries/awesome-icons.excalidrawlib +4081 -0
  42. package/libraries/dev_ops.excalidrawlib +35468 -0
  43. package/libraries/network-topology-icons.excalidrawlib +5290 -0
  44. package/libraries/system-icons.excalidrawlib +4521 -0
  45. package/libraries/systemdesignicons.excalidrawlib +1307 -0
  46. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Diagram Tool — agent-drawn Excalidraw on localhost, synced via a context file
2
+
3
+ No API key needed. Your coding agent is the LLM — `diagram-tool` only validates Mermaid, renders the scene, and serves the live UI.
4
+
5
+ ## Recommended: MCP server (any coding agent)
6
+
7
+ ```json
8
+ { "mcpServers": { "diagram-tool": { "command": "npx", "args": ["-y", "diagram-tool", "mcp"] } } }
9
+ ```
10
+
11
+ Or auto-write it:
12
+
13
+ ```bash
14
+ npm i -g diagram-tool # or npx -y diagram-tool mcp (no install)
15
+ diagram-tool mcp-install --agent auto # auto|opencode|claude|cursor|vscode
16
+ ```
17
+
18
+ Then ask your agent to draw something, open the returned `url` once — it live-updates on every render.
19
+
20
+ Tools: `render_diagram(mermaidSource, prompt?, decorations?)` · `get_diagram()` · `list_libraries(query?)` · `list_library_items(library)` · resource `diagram-tool://guide`. Full authoring doc: [GUIDE.md](GUIDE.md) (also served to agents via the guide resource, so no LLM setup is needed).
21
+
22
+ ## The context file (source of truth)
23
+
24
+ Everything revolves around `diagram-tool.context.json`:
25
+
26
+ ```json
27
+ { "version": 1, "rev": 3, "prompt": "...", "mermaidSource": "...",
28
+ "scene": { "type": "excalidraw", "version": 2, "elements": [...] } }
29
+ ```
30
+
31
+ - MCP/CLI writes it on every render (bumps `rev`, writes `.excalidraw.json` + `.mmd` sidecars).
32
+ - UI autosaves canvas edits back to it (`POST /api/scene`, debounced; stale `rev` → 409 + fresh copy).
33
+ - UI subscribes to SSE `GET /api/live` (Hono server watches the file) and refetches on rev bumps, so agent edits appear in the open canvas without reload.
34
+
35
+ ## Shell fallback (no MCP)
36
+
37
+ ```bash
38
+ diagram-tool render --no-serve # re-derive scene from the context file's mermaidSource
39
+ diagram-tool serve # just the UI (default http://localhost:3000)
40
+ ```
41
+
42
+ Agent loop without MCP: edit `mermaidSource` in the context file, run `diagram-tool render --no-serve`. Structural changes belong in `mermaidSource` + render; small canvas nudges can go straight into `scene.elements`.
43
+
44
+ ## Libraries (excalidraw-libs)
45
+
46
+ `libraries/*.excalidrawlib` (v1 + v2) are served at `GET /api/libraries` for hand drag-drop, and composited server-side via `decorations` for agent renders. 6 payloads ship offline (network-topology, dev-ops, systemdesignicons, UML-ER, awesome-icons, system-icons); the full ~100-library catalog is searchable via `list_libraries` and fetched on demand into `~/.cache/diagram-tool/libraries/`.
47
+
48
+ - `diagram-tool library list` — downloaded files + item count
49
+ - `diagram-tool library add <name>` — download from libraries.excalidraw.com (e.g. `diagram-tool library add aws`)
50
+ - Node icon classes also work: `class USER actor,icon_user` (slugs: `icon_user`, `icon_users`, `icon_home`, `icon_lock`, `icon_search`, `icon_chart`, `icon_email`, `icon_calendar`, `icon_location`, `icon_payment`)
51
+
52
+ Rule of thumb: cloud/architecture/network diagrams get ≤6 icons; logic flowcharts and sequence/ER/class diagrams stay pure mermaid.
53
+
54
+ ## BYOK fallback (optional, needs a key)
55
+
56
+ ```bash
57
+ cp .env.example .env # OPENROUTER_API_KEY=sk-or-...
58
+ diagram-tool "draw the request lifecycle of a REST API" --provider openrouter
59
+ diagram-tool edit "add a retry step" --provider openrouter
60
+ ```
61
+
62
+ Flags: `--provider openai|anthropic|google|ollama|openrouter`, `--model`, `--api-key`, `--base-url`, `--port`, `--no-serve`, `--context`.
63
+
64
+ Pipeline: few-shot prompt → strict Mermaid parse → up to 2 self-repairs (parser error fed back) → `mermaid-to-excalidraw`. Server-side geometry is approximated from text length (jsdom `getBBox` shim); browser is truth.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * diagram-tool — CLI + localhost React canvas around one context file.
4
+ *
5
+ * diagram-tool "draw a login flowchart" # generate -> context file -> serve UI
6
+ * diagram-tool edit "add a payment step" # follow-up via CLI
7
+ * diagram-tool render [file.mmd] # re-derive scene from context (or file) mermaid
8
+ * diagram-tool serve # just serve the UI
9
+ *
10
+ * UI edits autosave to the same context file. OpenCode loop: prompt OpenCode
11
+ * to edit diagram-tool.context.json's mermaidSource, then `diagram-tool render`.
12
+ */
13
+ import { readFile } from "node:fs/promises";
14
+ import { DEFAULT_CONTEXT, loadContext, saveContext } from "./context.js";
15
+ import { convertToScene, generate } from "./diagram.js";
16
+ import { serve } from "./server.js";
17
+ import { runMcp } from "./mcp.js";
18
+ import { DEFAULT_MODELS } from "./llm.js";
19
+ const PROVIDERS = ["openai", "anthropic", "google", "ollama", "openrouter"];
20
+ function help() {
21
+ return `diagram-tool — Mermaid -> Excalidraw on localhost, synced through a context file.
22
+ No API key needed for MCP / render / serve (the agent is the LLM).
23
+
24
+ Usage:
25
+ diagram-tool mcp [--context <file>] [--port <n>] [--no-serve]
26
+ MCP stdio server for any coding agent (recommended)
27
+ diagram-tool mcp-install [--agent auto|opencode|claude|cursor|vscode]
28
+ write the MCP snippet into your agent config
29
+ diagram-tool render [file.mmd] re-derive scene from context (or given) mermaid, no LLM
30
+ diagram-tool serve serve the UI for the context file
31
+ diagram-tool library list list downloaded libraries
32
+ diagram-tool library add <name> download a library from libraries.excalidraw.com
33
+ diagram-tool "..." [options] generate via BYOK provider (optional fallback, needs key)
34
+ diagram-tool edit "..." [options] follow-up edit via BYOK provider
35
+
36
+ Options:
37
+ --context <file> context file (default ${DEFAULT_CONTEXT})
38
+ --agent <a> mcp-install target (default auto)
39
+ --provider <p> BYOK only: ${PROVIDERS.join("|")} (default $DIAGRAM_TOOL_PROVIDER or openai)
40
+ --model <name> BYOK only (default per provider)
41
+ --api-key <key> BYOK (or $OPENAI_API_KEY / $ANTHROPIC_API_KEY / $GOOGLE_API_KEY / $OPENROUTER_API_KEY)
42
+ --base-url <url> proxy / Ollama host
43
+ --port <n> (default 3000, $DIAGRAM_TOOL_PORT)
44
+ --no-serve don't start the UI after generate/edit/render (mcp: file-only mode)
45
+
46
+ Agent loop (no key): connect diagram-tool mcp, ask the agent to draw, open the
47
+ returned url once — it live-updates. Shell fallback: edit "mermaidSource"
48
+ in ${DEFAULT_CONTEXT}, then \`diagram-tool render --no-serve\`.`;
49
+ }
50
+ function parseFlags(argv) {
51
+ const flags = {
52
+ context: process.env.DIAGRAM_TOOL_CONTEXT || process.env.FORGE_CONTEXT || DEFAULT_CONTEXT,
53
+ provider: (process.env.DIAGRAM_TOOL_PROVIDER || process.env.FORGE_PROVIDER) || "openai",
54
+ model: process.env.DIAGRAM_TOOL_MODEL || process.env.FORGE_MODEL || "gpt-4.1-mini",
55
+ modelSet: !!(process.env.DIAGRAM_TOOL_MODEL || process.env.FORGE_MODEL),
56
+ port: Number(process.env.DIAGRAM_TOOL_PORT || process.env.FORGE_PORT || 3000),
57
+ serveAfter: true,
58
+ agent: "auto",
59
+ };
60
+ if (process.env.DIAGRAM_TOOL_API_KEY || process.env.FORGE_API_KEY) {
61
+ flags.apiKey = process.env.DIAGRAM_TOOL_API_KEY || process.env.FORGE_API_KEY;
62
+ }
63
+ if (process.env.DIAGRAM_TOOL_BASE_URL || process.env.FORGE_BASE_URL) {
64
+ flags.baseUrl = process.env.DIAGRAM_TOOL_BASE_URL || process.env.FORGE_BASE_URL;
65
+ }
66
+ const rest = [];
67
+ for (let i = 0; i < argv.length; i++) {
68
+ const t = argv[i];
69
+ const next = () => {
70
+ const v = argv[++i];
71
+ if (v === undefined)
72
+ throw new Error(`flag ${t} needs a value`);
73
+ return v;
74
+ };
75
+ if (t === "--help" || t === "-h") {
76
+ console.log(help());
77
+ process.exit(0);
78
+ }
79
+ else if (t === "--context")
80
+ flags.context = next();
81
+ else if (t === "--agent")
82
+ flags.agent = next();
83
+ else if (t === "--provider")
84
+ flags.provider = next();
85
+ else if (t === "--model") {
86
+ flags.model = next();
87
+ flags.modelSet = true;
88
+ }
89
+ else if (t === "--api-key")
90
+ flags.apiKey = next();
91
+ else if (t === "--base-url")
92
+ flags.baseUrl = next();
93
+ else if (t === "--port")
94
+ flags.port = Number(next());
95
+ else if (t === "--no-serve")
96
+ flags.serveAfter = false;
97
+ else if (t.startsWith("--"))
98
+ throw new Error(`unknown flag ${t}`);
99
+ else
100
+ rest.push(t);
101
+ }
102
+ if (!flags.modelSet)
103
+ flags.model = DEFAULT_MODELS[flags.provider] ?? flags.model;
104
+ return { flags, rest };
105
+ }
106
+ async function main() {
107
+ try {
108
+ const env = await readFile(".env", "utf8");
109
+ for (const line of env.split("\n")) {
110
+ const m = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line.trim());
111
+ if (!m || process.env[m[1]] !== undefined)
112
+ continue;
113
+ let v = m[2].trim();
114
+ if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'")))
115
+ v = v.slice(1, -1);
116
+ process.env[m[1]] = v;
117
+ }
118
+ }
119
+ catch {
120
+ /* no .env — fine */
121
+ }
122
+ const { flags: f, rest } = parseFlags(process.argv.slice(2));
123
+ if (!PROVIDERS.includes(f.provider))
124
+ throw new Error(`unknown provider "${f.provider}" (${PROVIDERS.join("|")})`);
125
+ const [cmd, ...cmdRest] = rest;
126
+ if (!cmd) {
127
+ console.log(help());
128
+ process.exit(1);
129
+ }
130
+ if (cmd === "serve") {
131
+ await serve(f.context, f.port);
132
+ return;
133
+ }
134
+ if (cmd === "mcp") {
135
+ await runMcp({ context: f.context, port: f.port, noServe: !f.serveAfter });
136
+ return;
137
+ }
138
+ if (cmd === "mcp-install") {
139
+ const { installMcp } = await import("./mcp-install.js");
140
+ await installMcp(f.agent);
141
+ return;
142
+ }
143
+ if (cmd === "library") {
144
+ const { readdir } = await import("node:fs/promises");
145
+ const { existsSync } = await import("node:fs");
146
+ const sub = cmdRest[0];
147
+ if (sub === "list" || !sub) {
148
+ const { loadLibraryItems, libraryDir } = await import("./library.js");
149
+ const dir = libraryDir();
150
+ if (!existsSync(dir)) {
151
+ console.log("no libraries/ directory");
152
+ return;
153
+ }
154
+ const files = (await readdir(dir)).filter((x) => x.endsWith(".excalidrawlib"));
155
+ const items = await loadLibraryItems(dir);
156
+ console.log(`${files.length} files, ${items.length} items:`);
157
+ for (const x of files)
158
+ console.log(` ${x}`);
159
+ return;
160
+ }
161
+ if (sub === "add") {
162
+ const query = (cmdRest[1] ?? "").toLowerCase();
163
+ if (!query)
164
+ throw new Error("usage: diagram-tool library add <name> (e.g. diagram-tool library add aws)");
165
+ console.error("fetching library index…");
166
+ const res = await fetch("https://raw.githubusercontent.com/excalidraw/excalidraw-libs/main/libraries.json");
167
+ if (!res.ok)
168
+ throw new Error(`index fetch failed: HTTP ${res.status}`);
169
+ const index = (await res.json());
170
+ const hit = index.find((l) => l.name.toLowerCase().includes(query));
171
+ if (!hit)
172
+ throw new Error(`no library matching "${query}" (try a shorter term)`);
173
+ const { mkdir, writeFile } = await import("node:fs/promises");
174
+ await mkdir("libraries", { recursive: true });
175
+ const url = `https://raw.githubusercontent.com/excalidraw/excalidraw-libs/main/libraries/${hit.source}`;
176
+ console.error(`downloading ${hit.name}…`);
177
+ const dl = await fetch(url);
178
+ if (!dl.ok)
179
+ throw new Error(`download failed: HTTP ${dl.status}`);
180
+ const dest = `libraries/${hit.source.split("/").pop()}`;
181
+ await writeFile(dest, Buffer.from(await dl.arrayBuffer()));
182
+ console.log(`added ${hit.name} -> ${dest} (restart diagram-tool serve to load)`);
183
+ return;
184
+ }
185
+ throw new Error(`unknown library command "${sub}" (list|add)`);
186
+ }
187
+ if (cmd === "render") {
188
+ const mmdFile = cmdRest[0];
189
+ let mermaidSource;
190
+ if (mmdFile) {
191
+ mermaidSource = (await readFile(mmdFile, "utf8")).trim();
192
+ }
193
+ else {
194
+ mermaidSource = (await loadContext(f.context)).mermaidSource;
195
+ if (!mermaidSource.trim())
196
+ throw new Error(`no mermaidSource in ${f.context} (and no file given)`);
197
+ }
198
+ const scene = await convertToScene(mermaidSource);
199
+ const next = await saveContext(f.context, { mermaidSource, elements: scene.elements });
200
+ console.log(`rendered rev ${next.rev} into ${f.context} (${scene.elements.length} elements)`);
201
+ if (f.serveAfter)
202
+ await serve(f.context, f.port);
203
+ return;
204
+ }
205
+ const isEdit = cmd === "edit";
206
+ const prompt = (isEdit ? cmdRest : rest).join(" ");
207
+ if (!prompt) {
208
+ console.log(help());
209
+ process.exit(1);
210
+ }
211
+ let existingMermaid;
212
+ if (isEdit) {
213
+ existingMermaid = (await loadContext(f.context)).mermaidSource;
214
+ if (!existingMermaid.trim())
215
+ throw new Error(`${f.context} has no mermaidSource to edit yet — generate first.`);
216
+ }
217
+ console.error(`asking ${f.provider}/${f.model} ...`);
218
+ const t0 = Date.now();
219
+ const r = await generate(prompt, {
220
+ provider: f.provider,
221
+ model: f.model,
222
+ ...(f.apiKey ? { apiKey: f.apiKey } : {}),
223
+ ...(f.baseUrl ? { baseUrl: f.baseUrl } : {}),
224
+ ...(existingMermaid ? { existingMermaid } : {}),
225
+ });
226
+ const next = await saveContext(f.context, {
227
+ prompt,
228
+ mermaidSource: r.mermaidSource,
229
+ elements: r.scene.elements,
230
+ });
231
+ console.error(`done in ${((Date.now() - t0) / 1000).toFixed(1)}s — rev ${next.rev} in ${f.context} (${r.scene.elements.length} elements${r.repaired ? `, repaired after ${r.attempts} attempts` : ""})`);
232
+ if (f.serveAfter)
233
+ await serve(f.context, f.port);
234
+ }
235
+ main().catch((e) => {
236
+ console.error(`error: ${e instanceof Error ? e.message : String(e)}`);
237
+ process.exit(1);
238
+ });
239
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The context file is the single source of truth shared by CLI, UI and agents.
3
+ * OpenCode workflow: read it, change `mermaidSource` (or `elements`), then run
4
+ * `diagram-tool render` to re-derive the scene — or just edit elements directly.
5
+ */
6
+ export interface DiagramContext {
7
+ version: 1;
8
+ /** incremented on every write; UI polls this to pick up CLI/agent edits */
9
+ rev: number;
10
+ prompt: string;
11
+ mermaidSource: string;
12
+ scene: {
13
+ type: "excalidraw";
14
+ version: 2;
15
+ elements: unknown[];
16
+ };
17
+ updatedAt: string;
18
+ }
19
+ export declare const DEFAULT_CONTEXT = "diagram-tool.context.json";
20
+ /** Pre-rename default. Read as fallback; first save migrates to DEFAULT_CONTEXT. */
21
+ export declare const LEGACY_CONTEXT = "diagramforge.context.json";
22
+ export declare function blankContext(prompt?: string): DiagramContext;
23
+ export declare function loadContext(path: string): Promise<DiagramContext>;
24
+ /** Applies patch, bumps rev, writes file + sidecars. Returns the new context. */
25
+ export declare function saveContext(path: string, patch: Partial<Pick<DiagramContext, "prompt" | "mermaidSource">> & {
26
+ elements?: unknown[];
27
+ }): Promise<DiagramContext>;
@@ -0,0 +1,55 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ export const DEFAULT_CONTEXT = "diagram-tool.context.json";
4
+ /** Pre-rename default. Read as fallback; first save migrates to DEFAULT_CONTEXT. */
5
+ export const LEGACY_CONTEXT = "diagramforge.context.json";
6
+ export function blankContext(prompt = "") {
7
+ return {
8
+ version: 1,
9
+ rev: 0,
10
+ prompt,
11
+ mermaidSource: "",
12
+ scene: { type: "excalidraw", version: 2, elements: [] },
13
+ updatedAt: new Date().toISOString(),
14
+ };
15
+ }
16
+ export async function loadContext(path) {
17
+ if (!existsSync(path) && path === DEFAULT_CONTEXT && existsSync(LEGACY_CONTEXT)) {
18
+ path = LEGACY_CONTEXT; // pre-rename file: read it, next save migrates
19
+ }
20
+ if (!existsSync(path))
21
+ return blankContext();
22
+ const raw = await readFile(path, "utf8");
23
+ const c = JSON.parse(raw);
24
+ if (!c.scene)
25
+ c.scene = { type: "excalidraw", version: 2, elements: [] };
26
+ if (typeof c.rev !== "number")
27
+ c.rev = 0;
28
+ if (typeof c.mermaidSource !== "string")
29
+ c.mermaidSource = "";
30
+ return c;
31
+ }
32
+ /** Applies patch, bumps rev, writes file + sidecars. Returns the new context. */
33
+ export async function saveContext(path, patch) {
34
+ const prev = await loadContext(path);
35
+ const next = {
36
+ ...prev,
37
+ version: 1,
38
+ rev: prev.rev + 1,
39
+ updatedAt: new Date().toISOString(),
40
+ ...(patch.prompt !== undefined ? { prompt: patch.prompt } : {}),
41
+ ...(patch.mermaidSource !== undefined ? { mermaidSource: patch.mermaidSource } : {}),
42
+ scene: {
43
+ type: "excalidraw",
44
+ version: 2,
45
+ elements: patch.elements !== undefined ? patch.elements : prev.scene.elements,
46
+ },
47
+ };
48
+ await writeFile(path, JSON.stringify(next, null, 2));
49
+ // sidecars for direct import into excalidraw.com
50
+ const base = path.replace(/\.context\.json$/, "").replace(/\.json$/, "");
51
+ await writeFile(`${base}.excalidraw.json`, JSON.stringify({ ...next.scene, source: "diagram-tool", appState: {} }, null, 2));
52
+ await writeFile(`${base}.mmd`, (next.mermaidSource ? next.mermaidSource + "\n" : ""));
53
+ return next;
54
+ }
55
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import "./dom-shim.js";
2
+ import { type LlmOpts } from "./llm.js";
3
+ export interface Scene {
4
+ type: "excalidraw";
5
+ version: 2;
6
+ source: string;
7
+ elements: unknown[];
8
+ appState: Record<string, unknown>;
9
+ }
10
+ export declare function extractMermaid(raw: string): string;
11
+ export declare function validateMermaid(source: string): Promise<string>;
12
+ export declare function convertToScene(mermaidSource: string): Promise<Scene>;
13
+ /** nodeId -> icon slug from `class NODE icon_slug` statements in the source. */
14
+ export declare function parseIconClasses(mermaidSource: string): Map<string, string>;
15
+ /**
16
+ * Dress node cards with library artwork: icon glyph fitted into the top of
17
+ * the box, node caption pinned to the bottom strip, all grouped for UI drag.
18
+ * Unknown slugs / missing libraries degrade to plain boxes (never throw).
19
+ */
20
+ export declare function applyIcons(elements: Record<string, unknown>[], mermaidSource: string, libraryDir?: string): Promise<Record<string, unknown>[]>;
21
+ /**
22
+ * Merge parallel arrows (same source id -> same target id) into one. The
23
+ * model sometimes emits one edge per action (generate/edit/render/serve),
24
+ * which renders as stacked duplicate arrows. Keeper = first arrow; labels
25
+ * are concatenated with " · " and surplus arrows + labels removed.
26
+ */
27
+ export declare function dedupeArrows(elements: Record<string, unknown>[]): Record<string, unknown>[];
28
+ /**
29
+ * Breathing space pass. The converter packs nodes per its own (often wrong)
30
+ * measurements, so labels collide. This iteratively pushes overlapping boxes
31
+ * apart and drags attached arrow endpoints + labels along (arrows here are
32
+ * free-floating: start/end are plain id refs, no live bindings).
33
+ */
34
+ export declare function declutter(elements: Record<string, unknown>[]): Record<string, unknown>[];
35
+ /**
36
+ * @excalidraw/mermaid-to-excalidraw v2 emits node text as a non-standard
37
+ * `label: { text, fontSize }` prop that the canvas ignores. Rewrite each into
38
+ * a real bound `text` element wired via containerId/boundElements.
39
+ */
40
+ export declare function withBoundLabels(elements: Record<string, unknown>[]): Record<string, unknown>[];
41
+ export interface GenerateOpts extends LlmOpts {
42
+ existingMermaid?: string;
43
+ }
44
+ export interface GenerateResult {
45
+ scene: Scene;
46
+ mermaidSource: string;
47
+ attempts: number;
48
+ repaired: boolean;
49
+ }
50
+ /** Prompt -> Mermaid -> validate (2 repairs, error fed back) -> Excalidraw scene. */
51
+ export declare function generate(prompt: string, opts: GenerateOpts): Promise<GenerateResult>;