@pixerate/editor 0.55.0 → 0.57.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 (75) hide show
  1. package/dist/agent/index.cjs +18 -17
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.js +3 -2
  4. package/dist/agent/index.js.map +1 -1
  5. package/dist/animation-editor/index.cjs +25 -7
  6. package/dist/animation-editor/index.cjs.map +1 -1
  7. package/dist/animation-editor/index.d.cts +21 -5
  8. package/dist/animation-editor/index.d.ts +21 -5
  9. package/dist/animation-editor/index.js +25 -7
  10. package/dist/animation-editor/index.js.map +1 -1
  11. package/dist/canvas/index.cjs +2105 -172
  12. package/dist/canvas/index.cjs.map +1 -1
  13. package/dist/canvas/index.d.cts +319 -23
  14. package/dist/canvas/index.d.ts +319 -23
  15. package/dist/canvas/index.js +2081 -148
  16. package/dist/canvas/index.js.map +1 -1
  17. package/dist/chart/index.cjs +32 -30
  18. package/dist/chart/index.cjs.map +1 -1
  19. package/dist/chart/index.d.cts +4 -2
  20. package/dist/chart/index.d.ts +4 -2
  21. package/dist/chart/index.js +20 -18
  22. package/dist/chart/index.js.map +1 -1
  23. package/dist/{chunk-C3JHII3M.cjs → chunk-3I6V7PI4.cjs} +45 -45
  24. package/dist/{chunk-C3JHII3M.cjs.map → chunk-3I6V7PI4.cjs.map} +1 -1
  25. package/dist/{chunk-P43HUQRX.cjs → chunk-AHCENCU4.cjs} +12 -10
  26. package/dist/chunk-AHCENCU4.cjs.map +1 -0
  27. package/dist/chunk-II77MLHN.cjs +11106 -0
  28. package/dist/chunk-II77MLHN.cjs.map +1 -0
  29. package/dist/{chunk-OIKYP7XC.js → chunk-LMUDHBWR.js} +2 -2
  30. package/dist/{chunk-VODBAO32.js → chunk-OEFCLWZ3.js} +4 -1425
  31. package/dist/chunk-OEFCLWZ3.js.map +1 -0
  32. package/dist/{chunk-NK3WL5TJ.cjs → chunk-P2CTDJNF.cjs} +27 -1448
  33. package/dist/chunk-P2CTDJNF.cjs.map +1 -0
  34. package/dist/chunk-TKPPIVW7.cjs +1443 -0
  35. package/dist/chunk-TKPPIVW7.cjs.map +1 -0
  36. package/dist/chunk-WJSCGBJ2.js +11106 -0
  37. package/dist/chunk-WJSCGBJ2.js.map +1 -0
  38. package/dist/{chunk-RIXTW3C4.js → chunk-WLVZ235X.js} +5 -3
  39. package/dist/{chunk-RIXTW3C4.js.map → chunk-WLVZ235X.js.map} +1 -1
  40. package/dist/chunk-ZOH6X3VR.js +1443 -0
  41. package/dist/chunk-ZOH6X3VR.js.map +1 -0
  42. package/dist/{controller-DscWQbjO.d.ts → controller-D2rj5umx.d.ts} +2 -362
  43. package/dist/{controller-CX0QfSv0.d.cts → controller-R2biKk_E.d.cts} +2 -362
  44. package/dist/diagram/index.cjs +1457 -47
  45. package/dist/diagram/index.cjs.map +1 -1
  46. package/dist/diagram/index.d.cts +1734 -15
  47. package/dist/diagram/index.d.ts +1734 -15
  48. package/dist/diagram/index.js +1436 -26
  49. package/dist/diagram/index.js.map +1 -1
  50. package/dist/extensions/index.cjs +5 -4
  51. package/dist/extensions/index.cjs.map +1 -1
  52. package/dist/extensions/index.d.cts +4 -3
  53. package/dist/extensions/index.d.ts +4 -3
  54. package/dist/extensions/index.js +4 -3
  55. package/dist/{index-DIWB6xMf.d.cts → index-BcHyG0Wq.d.cts} +2 -1
  56. package/dist/{index-CALDD35B.d.ts → index-CTCc3m3C.d.ts} +2 -1
  57. package/dist/index.cjs +10 -9
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.d.cts +4 -3
  60. package/dist/index.d.ts +4 -3
  61. package/dist/index.js +4 -3
  62. package/dist/index.js.map +1 -1
  63. package/dist/types-BRp-6zkY.d.cts +362 -0
  64. package/dist/types-BRp-6zkY.d.ts +362 -0
  65. package/dist/{types-BULrFyuQ.d.cts → types-DYkjhzdz.d.cts} +1 -1
  66. package/dist/{types-BULrFyuQ.d.ts → types-DYkjhzdz.d.ts} +1 -1
  67. package/package.json +1 -1
  68. package/dist/chunk-H5K77C7Y.cjs +0 -3848
  69. package/dist/chunk-H5K77C7Y.cjs.map +0 -1
  70. package/dist/chunk-NK3WL5TJ.cjs.map +0 -1
  71. package/dist/chunk-P43HUQRX.cjs.map +0 -1
  72. package/dist/chunk-VODBAO32.js.map +0 -1
  73. package/dist/chunk-WWJNC7SX.js +0 -3848
  74. package/dist/chunk-WWJNC7SX.js.map +0 -1
  75. /package/dist/{chunk-OIKYP7XC.js.map → chunk-LMUDHBWR.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/diagram/index.cjs","../../src/diagram/types.ts","../../src/diagram/source/find.ts","../../src/diagram/controller.ts","../../src/diagram/tools.ts","../../src/diagram/draft.ts","../../src/diagram/director.ts"],"names":[],"mappings":"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AAA;AAC9C,IAAA;AACmB,IAAA;AACb,MAAA;AACwC,QAAA;AACf,QAAA;AACtB,QAAA;AACK,MAAA;AAEe,QAAA;AACoB,QAAA;AACP,QAAA;AACsB,UAAA;AACnC,UAAA;AAC3B,QAAA;AACO,QAAA;AACR,MAAA;AACK,MAAA;AAC8E,QAAA;AAC3E,QAAA;AACH,MAAA;AACmC,QAAA;AAChC,QAAA;AACH,MAAA;AACoC,QAAA;AACjC,QAAA;AACK,MAAA;AAC4C,QAAA;AAC3B,QAAA;AACuD,QAAA;AAC5D,UAAA;AACiE,UAAA;AACP,UAAA;AAChD,UAAA;AACjC,QAAA;AACM,QAAA;AACR,MAAA;AACe,MAAA;AAGoD,QAAA;AAChB,QAAA;AACjB,QAAA;AAClC,MAAA;AACD,IAAA;AACD,EAAA;AACD;AAGyI;AAC3G,EAAA;AACH,EAAA;AACsC,IAAA;AACrC,IAAA;AACnB,IAAA;AACP,EAAA;AACF;AN8vB4F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/diagram/index.cjs","sourcesContent":[null,"/**\n * Core document model shared by every diagram family.\n *\n * Design goals:\n * - **Lossless**: a document parsed and serialised without edits is\n * byte-identical to its input. Anything the diagram module does not understand is kept\n * verbatim as a passthrough statement.\n * - **Graph-first**: every editable family projects onto the same\n * {@link DiagramGraph} so that one canvas can render all of them.\n * - **Mermaid-compatible metadata**: canvas state is stored in\n * `%% @pixerate …` comments (or frontmatter) that every Mermaid parser ignores.\n *\n * @module\n */\n\n/** Mermaid diagram families the diagram module knows how to detect. */\nexport type DiagramFamily =\n | \"flowchart\"\n | \"state\"\n | \"class\"\n | \"er\"\n | \"requirement\"\n | \"mindmap\"\n | \"sequence\"\n | \"gantt\"\n | \"pie\"\n | \"journey\"\n | \"timeline\"\n | \"gitGraph\"\n | \"c4\"\n | \"quadrant\"\n | \"sankey\"\n | \"xychart\"\n | \"block\"\n | \"architecture\"\n | \"packet\"\n | \"kanban\"\n | \"radar\"\n | \"treemap\"\n | \"info\"\n | \"unknown\";\n\n/**\n * How deeply the diagram module can edit a family.\n *\n * - `editable` — statement-level lossless editing on the canvas. Every\n * family the diagram module ships an adapter for is editable; the canvas model differs\n * per family (graph, tree, timeline, chart data…).\n * - `render-only` — source editing with a live Mermaid preview; no canvas.\n * Used for families without an adapter and for unknown input.\n */\nexport type SupportTier = \"editable\" | \"render-only\";\n\n/** `[x, y]` canvas position in React Flow coordinates. */\nexport type Point = readonly [number, number];\n\n/** Line pattern of an edge. Mirrors Mermaid's `thickness` / `pattern`. */\nexport type EdgePattern = \"solid\" | \"dotted\" | \"thick\" | \"invisible\";\n\n/**\n * Arrow head identifiers, aligned with Mermaid's `arrowTypeStart` /\n * `arrowTypeEnd` values so React Flow markers can be derived uniformly.\n */\nexport type ArrowHead =\n | \"none\"\n | \"arrow_point\"\n | \"arrow_open\"\n | \"arrow_cross\"\n | \"arrow_circle\"\n | \"arrow_barb\"\n | \"extension\"\n | \"composition\"\n | \"aggregation\"\n | \"dependency\"\n | \"lollipop\"\n | \"only_one\"\n | \"zero_or_one\"\n | \"one_or_more\"\n | \"zero_or_more\"\n | \"md_parent\"\n | \"requirement_arrow\"\n | \"requirement_contains\";\n\nexport interface GraphNode {\n /** Stable identifier (the Mermaid id, e.g. `A`). */\n id: string;\n /** Display label (raw Mermaid text, markdown strings included). */\n label: string;\n /**\n * Canonical shape identifier (`rect`, `diam`, `cyl`, `stateStart`,\n * `classBox`, `erBox`…). Mermaid aliases such as `squareRect` or `diamond`\n * are normalised on parse. See `FLOWCHART_SHAPES` for the flowchart set.\n */\n shape: string;\n /** Enclosing group (subgraph / composite state / namespace) id. */\n parentId?: string;\n /** CSS class names applied with `:::`, `class`, or `classDef` defaults. */\n classes: string[];\n /** Inline `style` declarations (`fill:#f9f`, …). */\n styles: string[];\n /** `click` href target. */\n link?: string;\n linkTarget?: string;\n tooltip?: string;\n /** Family-specific payload (class members, ER attributes, state notes…). */\n data?: Record<string, unknown>;\n}\n\nexport interface GraphGroup {\n id: string;\n label: string;\n parentId?: string;\n /** Local layout direction override (`TB`, `LR`, …). */\n direction?: string;\n classes: string[];\n styles: string[];\n data?: Record<string, unknown>;\n}\n\nexport interface GraphEdge {\n /** Mermaid-compatible id (`L_A_B_0` for auto ids, or the user id). */\n id: string;\n source: string;\n target: string;\n label?: string;\n pattern: EdgePattern;\n arrowStart: ArrowHead;\n arrowEnd: ArrowHead;\n /** Minimum rank length requested with extra dashes (`---->`). */\n length: number;\n classes: string[];\n styles: string[];\n animate?: boolean;\n animation?: \"fast\" | \"slow\";\n /** True when the id was written by the user (`A e1@--> B`). */\n userDefinedId: boolean;\n data?: Record<string, unknown>;\n}\n\n/** Normalised, family-independent view of a diagram. */\nexport interface DiagramGraph {\n direction: string;\n nodes: GraphNode[];\n edges: GraphEdge[];\n groups: GraphGroup[];\n /** `classDef` declarations keyed by class name. */\n classDefs: Record<string, string[]>;\n title?: string;\n /**\n * Diagram-level, family-specific settings that are not nodes or edges\n * (pie `showData`, quadrant axis labels, gantt `dateFormat`…). Edited with\n * `setGraphData`; the keys are documented per family in `FamilyUI.diagramFields`.\n */\n data?: Record<string, unknown>;\n}\n\n/** Canvas state that Mermaid itself does not model. Persisted as metadata. */\nexport interface DiagramMeta {\n /** Schema version of the metadata payload. */\n v: 1;\n /** Node / group positions keyed by id. */\n layout?: Record<string, Point>;\n /** Explicit node sizes for resized nodes/groups. */\n sizes?: Record<string, readonly [number, number]>;\n /** Viewport the user last looked at. */\n viewport?: { x: number; y: number; zoom: number };\n /** Free-form extension slot for host applications. */\n ext?: Record<string, unknown>;\n}\n\n/** Where metadata lives in the source. */\nexport type MetaCarrier = \"comment\" | \"frontmatter\";\n\n/**\n * Lines that precede the diagram header: YAML frontmatter, `%%{init}%%`\n * directives, comments and blank lines. Kept verbatim.\n */\nexport interface Preamble {\n /** Raw lines, including the `---` fences of any frontmatter. */\n lines: string[];\n /** Parsed frontmatter when present (title/config/pixerate…). */\n frontmatter?: Record<string, unknown>;\n}\n\n/** A parse problem reported by the diagram parser or by Mermaid. */\nexport interface Diagnostic {\n severity: \"error\" | \"warning\" | \"info\";\n message: string;\n /** 1-based line in the source, when known. */\n line?: number;\n column?: number;\n source: \"pixerate\" | \"mermaid\";\n}\n\n/**\n * The parsed document. Family adapters extend `body` with their own statement\n * list; consumers that only need the graph never look at it.\n */\nexport interface MermaidDocument<TBody = unknown> {\n family: DiagramFamily;\n tier: SupportTier;\n /** Source as given to {@link parseMermaid}. */\n source: string;\n preamble: Preamble;\n /** The header line (`flowchart LR`, `stateDiagram-v2`, …) exactly as written. */\n header: string;\n /** Trailing newline / whitespace after the last statement, kept for fidelity. */\n trailing: string;\n /** End-of-line sequence detected in the source. */\n eol: \"\\n\" | \"\\r\\n\";\n graph: DiagramGraph;\n meta: DiagramMeta;\n metaCarrier: MetaCarrier;\n body: TBody;\n diagnostics: Diagnostic[];\n /** True once any edit has been applied to this document. */\n dirty: boolean;\n}\n\n/** Directions understood by flowchart / state / class / ER. */\nexport const DIRECTIONS = [\"TB\", \"TD\", \"BT\", \"LR\", \"RL\"] as const;\nexport type Direction = (typeof DIRECTIONS)[number];\n\n// ---------------------------------------------------------------------------\n// Edit operations\n// ---------------------------------------------------------------------------\n\nexport type EditOp =\n | { type: \"addNode\"; node: Partial<GraphNode> & { id: string }; position?: Point }\n | { type: \"removeNodes\"; ids: string[] }\n | { type: \"renameNode\"; id: string; label: string }\n | { type: \"setNodeShape\"; id: string; shape: string }\n | { type: \"setNodeClasses\"; id: string; classes: string[] }\n | { type: \"setNodeStyles\"; id: string; styles: string[] }\n | { type: \"setNodeLink\"; id: string; link?: string; target?: string; tooltip?: string }\n | { type: \"setNodeData\"; id: string; data: Record<string, unknown> }\n | { type: \"moveNodes\"; moves: { id: string; position: Point }[] }\n | { type: \"resizeNode\"; id: string; size: readonly [number, number] | undefined }\n | { type: \"addEdge\"; edge: Partial<GraphEdge> & { source: string; target: string } }\n | { type: \"removeEdges\"; ids: string[] }\n | { type: \"setEdgeLabel\"; id: string; label: string }\n /** Family-specific edge payload (sequence activation, message order, gantt dependency type…). */\n | { type: \"setEdgeData\"; id: string; data: Record<string, unknown> }\n | {\n type: \"setEdgeStyle\";\n id: string;\n pattern?: EdgePattern;\n arrowStart?: ArrowHead;\n arrowEnd?: ArrowHead;\n length?: number;\n styles?: string[];\n }\n | { type: \"reconnectEdge\"; id: string; source: string; target: string }\n | { type: \"groupNodes\"; groupId: string; label: string; nodeIds: string[]; parentId?: string }\n | { type: \"ungroup\"; groupId: string }\n | { type: \"renameGroup\"; id: string; label: string }\n | { type: \"setGroupDirection\"; id: string; direction?: string }\n | { type: \"setNodeParent\"; ids: string[]; parentId?: string }\n | { type: \"setDirection\"; direction: string }\n | { type: \"setClassDef\"; name: string; styles: string[] | undefined }\n | { type: \"setTitle\"; title: string | undefined }\n /** Merge diagram-level settings into `graph.data` (see {@link DiagramGraph.data}). */\n | { type: \"setGraphData\"; data: Record<string, unknown> }\n | { type: \"setViewport\"; viewport: DiagramMeta[\"viewport\"] }\n | { type: \"setMetaExt\"; key: string; value: unknown }\n | { type: \"replaceSource\"; source: string };\n\n/** Family adapter contract. See `docs/architecture.md`. */\nexport interface DiagramAdapter<TBody = unknown> {\n family: DiagramFamily;\n tier: SupportTier;\n parse(source: string, context: AdapterParseContext): MermaidDocument<TBody>;\n serialize(doc: MermaidDocument<TBody>): string;\n apply(doc: MermaidDocument<TBody>, op: EditOp): MermaidDocument<TBody>;\n}\n\n/** Pre-split source handed to adapters so they do not repeat the work. */\nexport interface AdapterParseContext {\n source: string;\n eol: \"\\n\" | \"\\r\\n\";\n preamble: Preamble;\n header: string;\n /** Body lines after the header (metadata comments already removed). */\n bodyLines: string[];\n trailing: string;\n meta: DiagramMeta;\n metaCarrier: MetaCarrier;\n family: DiagramFamily;\n}\n","import type { DiagramFamily } from \"../types.js\";\nimport { detectFamilyFromHeader, splitSource } from \"./lines.js\";\n\n/** A Mermaid diagram found in some text by {@link findMermaidSources}. */\nexport interface FoundMermaid {\n /** The diagram's Mermaid text, without any surrounding code fence. */\n source: string;\n /** Its family (never `\"unknown\"`). */\n family: DiagramFamily;\n /** The frontmatter `title`, else the Markdown heading above its code fence. */\n title?: string;\n}\n\n/** Fence info strings that mark a Mermaid block. */\nconst MERMAID_INFO = /^(mermaid|mmd)\\b/i;\n\n/**\n * Finds the Mermaid diagrams in a piece of text: a `.mmd` file, a Markdown\n * document with ```` ```mermaid ```` blocks (GitHub, Notion and most AI\n * chats write them like this), or a diagram pasted with or without its fence.\n *\n * Fenced blocks are used when there are any: those marked `mermaid` / `mmd`,\n * and unmarked ones whose first line is a Mermaid header. Otherwise the whole\n * text is one diagram if it starts with one. Unmarked text must spell the\n * keyword as Mermaid does (`timeline`, not `Timeline`). Text that holds no\n * diagram of a known family returns `[]`.\n */\nexport function findMermaidSources(text: string): FoundMermaid[] {\n const clean = text.replace(/^\\uFEFF/, \"\");\n const blocks = fencedBlocks(clean);\n if (blocks.length) {\n const found: FoundMermaid[] = [];\n for (const block of blocks) {\n if (block.info && !MERMAID_INFO.test(block.info)) continue;\n const item = asDiagram(block.body, block.heading, !block.info);\n if (item) found.push(item);\n }\n if (found.length) return found;\n }\n const whole = asDiagram(clean.trim(), undefined, true);\n return whole ? [whole] : [];\n}\n\n/**\n * `strict` (unmarked text) also wants the keyword's own case, as Mermaid\n * writes it, so prose such as \"Timeline of events\" is not taken for a diagram.\n */\nfunction asDiagram(source: string, heading?: string, strict = false): FoundMermaid | undefined {\n if (!source.trim()) return undefined;\n const split = splitSource(source);\n const family = detectFamilyFromHeader(split.header);\n if (family === \"unknown\") return undefined;\n if (strict && !/^\\s*([a-z]|C4)/.test(split.header)) return undefined;\n const fmTitle = split.preamble.frontmatter?.title;\n const title = typeof fmTitle === \"string\" && fmTitle.trim() ? fmTitle.trim() : heading;\n return { source: source.trimEnd() + \"\\n\", family, ...(title ? { title } : {}) };\n}\n\ninterface FencedBlock {\n info: string;\n body: string;\n /** The last Markdown heading before the fence. */\n heading?: string;\n}\n\n/** CommonMark-style fenced code blocks (``` or ~~~, three or more, indented up to three spaces). */\nfunction fencedBlocks(text: string): FencedBlock[] {\n const lines = text.split(/\\r\\n|\\n/);\n const blocks: FencedBlock[] = [];\n let heading: string | undefined;\n for (let i = 0; i < lines.length; i++) {\n // Patterns kept free of overlapping runs, so long lines cannot make them backtrack.\n const open = /^ {0,3}(`{3,}(?!`)|~{3,}(?!~))([^`]*)$/.exec(lines[i]!);\n if (!open) {\n const h = headingText(lines[i]!);\n if (h) heading = h;\n continue;\n }\n const fence = open[1]!;\n const close = new RegExp(`^ {0,3}${fence[0] === \"`\" ? \"`\" : \"~\"}{${fence.length},}$`);\n const body: string[] = [];\n let j = i + 1;\n while (j < lines.length && !close.test(lines[j]!.trimEnd())) body.push(lines[j++]!);\n blocks.push({ info: open[2]!.trim(), body: body.join(\"\\n\"), heading });\n i = j;\n }\n return blocks;\n}\n\n/** The text of an ATX heading (`## Title ##`), without its markers. */\nfunction headingText(line: string): string | undefined {\n const m = /^ {0,3}#{1,6}([ \\t].*)$/.exec(line);\n if (!m) return undefined;\n let text = m[1]!.trim();\n // A closing run of `#` counts only after a space (or when it is all there is).\n let end = text.length;\n while (end > 0 && text[end - 1] === \"#\") end--;\n if (end === 0 || text[end - 1] === \" \" || text[end - 1] === \"\\t\") text = text.slice(0, end).trim();\n return text || undefined;\n}\n","import { HistoryManager } from '../history/HistoryManager.js';\nimport type { Command } from '../history/types.js';\nimport { applyEdits, parseMermaid } from './document.js';\nimport { FLOWCHART_SHAPES } from './flowchart/shapes.js';\nimport { STATE_SHAPES } from './lines-grammar/state.js';\nimport { DIRECTIONS, type DiagramFamily, type EditOp, type MermaidDocument } from './types.js';\n\n/** Families that can be edited on the canvas; every other family is kept as text. */\nexport const EDITABLE_DIAGRAM_FAMILIES = ['flowchart', 'state', 'class', 'er'] as const;\n\n/** The shapes a node of `family` can take (`setNodeShape`), as canonical names. */\nexport function diagramShapes(family: DiagramFamily): string[] {\n\tif (family === 'flowchart') return FLOWCHART_SHAPES.map((shape) => shape.name);\n\tif (family === 'state') return STATE_SHAPES.map((shape) => shape.name);\n\treturn [];\n}\n\n/** Whether the family has groups (subgraphs, composite states, namespaces). ER diagrams do not. */\nexport function diagramSupportsGroups(family: DiagramFamily): boolean {\n\treturn family === 'flowchart' || family === 'state' || family === 'class';\n}\n\nconst quote = (ids: Iterable<string>) => [...ids].map((id) => `\"${id}\"`).join(', ');\n\n/**\n * Checks an edit against the document before it is applied. Returns a sentence saying\n * what is wrong and how to fix it, or `null` when the edit is valid. Ids are checked\n * against nodes, groups and edges; shapes against {@link diagramShapes}.\n */\nexport function validateDiagramEdit(doc: MermaidDocument, op: EditOp): string | null {\n\tconst { graph, family } = doc;\n\tif (op.type === 'replaceSource') return null;\n\tif (doc.tier !== 'editable')\n\t\treturn `This ${family} diagram can only be edited as Mermaid text; replace the source instead.`;\n\tconst nodes = new Set(graph.nodes.map((n) => n.id));\n\tconst groups = new Set(graph.groups.map((g) => g.id));\n\tconst edges = new Set(graph.edges.map((e) => e.id));\n\tconst anyIds = () => (nodes.size ? `existing node ids are ${quote([...nodes].slice(0, 20))}` : 'the diagram has no nodes yet');\n\tconst node = (id: string, allowGroup = false) =>\n\t\tnodes.has(id) || (allowGroup && groups.has(id)) ? null : `There is no node \"${id}\"; ${anyIds()}.`;\n\tconst group = (id: string | undefined) =>\n\t\tid === undefined || groups.has(id)\n\t\t\t? null\n\t\t\t: `There is no group \"${id}\"; ${groups.size ? `existing groups are ${quote(groups)}` : 'the diagram has no groups'}.`;\n\tconst edge = (id: string) =>\n\t\tedges.has(id)\n\t\t\t? null\n\t\t\t: `There is no edge \"${id}\"; ${edges.size ? `existing edge ids are ${quote([...edges].slice(0, 20))}` : 'the diagram has no edges yet'}.`;\n\tconst shape = (name: string | undefined) => {\n\t\tif (name === undefined) return null;\n\t\tconst shapes = diagramShapes(family);\n\t\tif (!shapes.length) return `Nodes of a ${family} diagram have no shapes to choose from.`;\n\t\treturn shapes.includes(name) ? null : `Unknown shape \"${name}\"; use one of ${quote(shapes)}.`;\n\t};\n\tconst free = (id: string) =>\n\t\t!/^[A-Za-z0-9_][\\w.-]*$/.test(id)\n\t\t\t? `Id \"${id}\" is not a valid Mermaid id; use letters, digits, \"_\", \"-\" or \".\".`\n\t\t\t: nodes.has(id) || groups.has(id)\n\t\t\t\t? `Id \"${id}\" is already used; choose another.`\n\t\t\t\t: null;\n\tconst first = (...problems: (string | null)[]) => problems.find((p) => p) ?? null;\n\n\tswitch (op.type) {\n\t\tcase 'addNode':\n\t\t\treturn first(free(op.node.id), shape(op.node.shape), group(op.node.parentId));\n\t\tcase 'removeNodes':\n\t\t\treturn first(...op.ids.map((id) => node(id, true)));\n\t\tcase 'renameNode':\n\t\t\treturn node(op.id, true);\n\t\tcase 'setNodeShape':\n\t\t\treturn first(node(op.id), shape(op.shape));\n\t\tcase 'setNodeClasses':\n\t\tcase 'setNodeStyles':\n\t\tcase 'setNodeLink':\n\t\tcase 'setNodeData':\n\t\tcase 'resizeNode':\n\t\t\treturn node(op.id, true);\n\t\tcase 'moveNodes':\n\t\t\treturn first(...op.moves.map((m) => node(m.id, true)));\n\t\tcase 'addEdge':\n\t\t\treturn first(node(op.edge.source, true), node(op.edge.target, true));\n\t\tcase 'removeEdges':\n\t\t\treturn first(...op.ids.map(edge));\n\t\tcase 'setEdgeLabel':\n\t\tcase 'setEdgeStyle':\n\t\tcase 'setEdgeData':\n\t\t\treturn edge(op.id);\n\t\tcase 'reconnectEdge':\n\t\t\treturn first(edge(op.id), node(op.source, true), node(op.target, true));\n\t\tcase 'groupNodes':\n\t\t\tif (!diagramSupportsGroups(family)) return `${family} diagrams have no groups.`;\n\t\t\treturn first(free(op.groupId), ...op.nodeIds.map((id) => node(id, true)), group(op.parentId));\n\t\tcase 'ungroup':\n\t\t\treturn group(op.groupId);\n\t\tcase 'renameGroup':\n\t\tcase 'setGroupDirection':\n\t\t\treturn group(op.id);\n\t\tcase 'setNodeParent':\n\t\t\treturn first(...op.ids.map((id) => node(id, true)), group(op.parentId));\n\t\tcase 'setDirection':\n\t\t\treturn (DIRECTIONS as readonly string[]).includes(op.direction)\n\t\t\t\t? null\n\t\t\t\t: `Unknown direction \"${op.direction}\"; use one of ${quote(DIRECTIONS)}.`;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nexport interface DiagramControllerOptions {\n\t/** Starting Mermaid source (default: an empty `flowchart TD`). */\n\tsource?: string;\n\t/** History to record into, e.g. one shared with the rest of the app (default: a new one). */\n\thistory?: HistoryManager;\n\t/** Refuse edits (read tools still work). */\n\treadOnly?: boolean;\n\t/** Called after the source changes (edit, undo, redo, load). */\n\tonChange?: (source: string, document: MermaidDocument) => void;\n}\n\n/** A snapshot of the controller. The same object is returned until something changes. */\nexport interface DiagramState {\n\tdocument: MermaidDocument;\n\t/** The Mermaid text, always in step with `document`. */\n\tsource: string;\n\tfamily: DiagramFamily;\n\t/** True for flowchart, state, class and ER diagrams that are not read-only. */\n\teditable: boolean;\n\treadOnly: boolean;\n\t/** Selected node, group or edge ids. */\n\tselection: readonly string[];\n\t/** The problem with the last rejected edit, if any. */\n\tissue: string | null;\n\tcanUndo: boolean;\n\tcanRedo: boolean;\n\tundoLabel: string;\n\tredoLabel: string;\n\t/** Bumped on every change. */\n\trevision: number;\n}\n\nexport type DiagramActionResult =\n\t| { ok: true; document: MermaidDocument }\n\t| { ok: false; message: string };\n\n/**\n * Headless state for a Mermaid diagram editor: the parsed document, a selection and\n * undoable statement-level edits. Every `apply` is one undo step and only rewrites the\n * statements it touches, so untouched text (comments, directives, formatting) survives.\n * Canvas positions go into the `%% @pixerate {…}` comment. Drags merge into one undo\n * step between `beginGesture` and `endGesture`. `getState()` returns the same object\n * until something changes, so it works with `useSyncExternalStore` and Svelte runes.\n */\nexport class DiagramController {\n\treadonly history: HistoryManager;\n\tprivate doc: MermaidDocument;\n\tprivate selection: string[] = [];\n\tprivate issue: string | null = null;\n\tprivate readOnly: boolean;\n\tprivate revision = 0;\n\tprivate applying = false;\n\tprivate gestureKey: string | null = null;\n\tprivate gestureCount = 0;\n\tprivate readonly onChange?: DiagramControllerOptions['onChange'];\n\tprivate readonly listeners = new Set<(state: DiagramState) => void>();\n\tprivate unsubscribeHistory?: () => void;\n\tprivate state!: DiagramState;\n\n\tconstructor(options: DiagramControllerOptions = {}) {\n\t\tthis.history = options.history ?? new HistoryManager();\n\t\tthis.onChange = options.onChange;\n\t\tthis.readOnly = options.readOnly ?? false;\n\t\tthis.doc = parseMermaid(options.source ?? 'flowchart TD\\n');\n\t\tthis.state = this.computeState();\n\t\tthis.connect();\n\t}\n\n\tprivate connect() {\n\t\tthis.unsubscribeHistory?.();\n\t\tthis.unsubscribeHistory = this.history.subscribe((h) => {\n\t\t\tconst s = this.state;\n\t\t\tif (this.applying || (s.canUndo === h.canUndo && s.canRedo === h.canRedo && s.undoLabel === h.undoName && s.redoLabel === h.redoName)) return;\n\t\t\tthis.notify(false);\n\t\t});\n\t}\n\n\t/** Stops listening to the history (subscribing again reconnects). */\n\tdestroy() {\n\t\tthis.unsubscribeHistory?.();\n\t\tthis.unsubscribeHistory = undefined;\n\t}\n\n\t/** The current snapshot (stable until the next change). */\n\tgetState = (): DiagramState => this.state;\n\n\t/** Calls `listener` after every change. Returns an unsubscribe function. */\n\tsubscribe = (listener: (state: DiagramState) => void): (() => void) => {\n\t\tif (!this.unsubscribeHistory) this.connect();\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t};\n\n\t/** The current document (immutable; edit through the controller). */\n\tgetDocument(): MermaidDocument {\n\t\treturn this.doc;\n\t}\n\n\tgetSource(): string {\n\t\treturn this.doc.source;\n\t}\n\n\tprivate computeState(): DiagramState {\n\t\tconst h = this.history.getState();\n\t\treturn {\n\t\t\tdocument: this.doc,\n\t\t\tsource: this.doc.source,\n\t\t\tfamily: this.doc.family,\n\t\t\teditable: this.doc.tier === 'editable' && !this.readOnly,\n\t\t\treadOnly: this.readOnly,\n\t\t\tselection: this.selection,\n\t\t\tissue: this.issue,\n\t\t\tcanUndo: h.canUndo,\n\t\t\tcanRedo: h.canRedo,\n\t\t\tundoLabel: h.undoName,\n\t\t\tredoLabel: h.redoName,\n\t\t\trevision: this.revision\n\t\t};\n\t}\n\n\tprivate notify(sourceChanged: boolean) {\n\t\tthis.revision++;\n\t\tthis.state = this.computeState();\n\t\tif (sourceChanged) this.onChange?.(this.doc.source, this.doc);\n\t\tconst state = this.state;\n\t\tthis.listeners.forEach((listener) => {\n\t\t\ttry {\n\t\t\t\tlistener(state);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[DiagramController] Error in state listener:', error);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate setDocument(doc: MermaidDocument) {\n\t\tthis.applying = true;\n\t\ttry {\n\t\t\tthis.doc = doc;\n\t\t\tconst ids = new Set([...doc.graph.nodes, ...doc.graph.groups, ...doc.graph.edges].map((x) => x.id));\n\t\t\tthis.selection = this.selection.filter((id) => ids.has(id));\n\t\t} finally {\n\t\t\tthis.applying = false;\n\t\t}\n\t\tthis.notify(true);\n\t}\n\n\tprivate commit(label: string, next: MermaidDocument) {\n\t\tconst before = this.doc;\n\t\tthis.issue = null;\n\t\tif (next.source === before.source) {\n\t\t\tthis.notify(false);\n\t\t\treturn;\n\t\t}\n\t\tconst command: Command = {\n\t\t\tname: label,\n\t\t\ttag: 'diagram',\n\t\t\texecute: () => this.setDocument(next),\n\t\t\tundo: () => this.setDocument(before),\n\t\t\tredo: () => this.setDocument(next),\n\t\t\tbyteSize: (before.source.length + next.source.length) * 2\n\t\t};\n\t\tif (this.gestureKey !== null) this.history.executeMerged(command, { key: `diagram.gesture:${this.gestureKey}:${this.gestureCount}` });\n\t\telse this.history.execute(command);\n\t}\n\n\tprivate reject(message: string): DiagramActionResult {\n\t\tthis.issue = message;\n\t\tthis.notify(false);\n\t\treturn { ok: false, message };\n\t}\n\n\t/**\n\t * Applies edits as one undo step. All-or-nothing: every edit is checked against the\n\t * document as it stands after the edits before it, and nothing changes when one fails.\n\t */\n\tapply(ops: EditOp | EditOp[], label = 'Edit diagram'): DiagramActionResult {\n\t\tconst list = Array.isArray(ops) ? ops : [ops];\n\t\tif (this.readOnly) return this.reject('The diagram is read-only.');\n\t\tlet doc = this.doc;\n\t\tfor (const op of list) {\n\t\t\tconst problem = validateDiagramEdit(doc, op);\n\t\t\tif (problem) return this.reject(problem);\n\t\t\tdoc = applyEdits(doc, [op]);\n\t\t}\n\t\tthis.commit(label, doc);\n\t\treturn { ok: true, document: this.doc };\n\t}\n\n\t/**\n\t * Replaces the whole source. `clearHistory` drops the undo stack (opening a file);\n\t * otherwise the replacement is one undo step.\n\t */\n\tload(source: string, options: { clearHistory?: boolean } = {}): DiagramActionResult {\n\t\tconst next = parseMermaid(source, { metaCarrier: this.doc.metaCarrier });\n\t\tif (options.clearHistory) {\n\t\t\tthis.history.clear();\n\t\t\tthis.issue = null;\n\t\t\tthis.selection = [];\n\t\t\tthis.setDocument(next);\n\t\t\treturn { ok: true, document: next };\n\t\t}\n\t\tif (this.readOnly) return this.reject('The diagram is read-only.');\n\t\tthis.commit('Replace diagram', next);\n\t\treturn { ok: true, document: this.doc };\n\t}\n\n\tsetReadOnly(readOnly: boolean) {\n\t\tif (this.readOnly === readOnly) return;\n\t\tthis.readOnly = readOnly;\n\t\tthis.notify(false);\n\t}\n\n\t/** Selects node, group or edge ids (unknown ids are dropped). */\n\tselect(ids: readonly string[]) {\n\t\tconst known = new Set([...this.doc.graph.nodes, ...this.doc.graph.groups, ...this.doc.graph.edges].map((x) => x.id));\n\t\tconst next = ids.filter((id) => known.has(id));\n\t\tif (next.length === this.selection.length && next.every((id, i) => id === this.selection[i])) return;\n\t\tthis.selection = next;\n\t\tthis.notify(false);\n\t}\n\n\t/** Edits until `endGesture` (e.g. the moves of one drag) fold into one undo step. */\n\tbeginGesture(key: string) {\n\t\tthis.gestureKey = key;\n\t\tthis.gestureCount++;\n\t}\n\n\tendGesture() {\n\t\tthis.gestureKey = null;\n\t}\n\n\tundo(): boolean {\n\t\treturn this.history.undo();\n\t}\n\n\tredo(): boolean {\n\t\treturn this.history.redo();\n\t}\n\n\t/** A node id not used by any node or group: `prefix` followed by the next free number. */\n\tnextId(prefix = 'N'): string {\n\t\tconst used = new Set([...this.doc.graph.nodes, ...this.doc.graph.groups].map((x) => x.id));\n\t\tlet i = 1;\n\t\twhile (used.has(`${prefix}${i}`)) i++;\n\t\treturn `${prefix}${i}`;\n\t}\n}\n","import type { AgentTool, AgentToolProvider } from '../tools/registry.js';\nimport type { JsonSchema } from '../tools/schema.js';\nimport type { PresenceActorInfo } from '../animation/presence.js';\nimport { diagramShapes, diagramSupportsGroups, type DiagramController } from './controller.js';\nimport type { DiagramDirector } from './director.js';\nimport { detectFamily } from './document.js';\nimport { findMermaidSources } from './source/find.js';\nimport { DIRECTIONS, type EditOp, type MermaidDocument, type Point } from './types.js';\n\nexport interface DiagramToolProviderOptions {\n\t/** Provider id (unique per registry). Default `diagram`. */\n\tid?: string;\n\t/** Always prefix tool names with `<namespace>__`. */\n\tnamespace?: string;\n\t/**\n\t * The diagram cannot be edited: write tools are hidden and `get_diagram` says why. A\n\t * string is the reason (\"The report is locked\"). The controller's own `readOnly` counts too.\n\t */\n\treadOnly?: boolean | string | (() => boolean | string);\n\t/**\n\t * Where the canvas shows each node now (absolute top-left), e.g. `() => canvas.layout.positions`.\n\t * When given, a move pins every node that has no stored position yet, so moving one\n\t * node of an auto-laid diagram does not shift the others.\n\t */\n\tgetPositions?: () => Readonly<Record<string, Point>>;\n\t/**\n\t * Draws the diagram as standalone SVG (e.g. `(o) => mermaidToSvg(controller.getDocument(), o)`\n\t * from `@pixerate/editor/canvas`). Enables `render_diagram`.\n\t */\n\trenderSvg?: (options: { maxWidth?: number; maxHeight?: number }) => { svg: string; width: number; height: number };\n\t/** Turns that SVG into PNG (e.g. `rasterizeSvg`), so `render_diagram` can return an image. */\n\trasterize?: (svg: string, size: { width: number; height: number }) => Promise<{ data: string; mimeType: string }> | { data: string; mimeType: string };\n\t/**\n\t * Acts out every write with a visible cursor (see `DiagramDirector`): it moves to the node,\n\t * clicks, drags, connects and types, and a whole-diagram load is drawn statement by\n\t * statement. Each call is still one undo step. Without it, edits apply at once.\n\t */\n\tdirector?: DiagramDirector;\n\t/** Who the cursor belongs to (default `{ id: 'agent', name: 'Agent' }`). */\n\tactor?: PresenceActorInfo;\n}\n\nconst READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;\nconst WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false } as const;\nconst WRITE_IDEMPOTENT = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;\nconst DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false } as const;\n\nconst ID = { type: 'string', minLength: 1, description: 'A node id from get_diagram (e.g. \"A\").' } as const;\nconst IDS = (what: string) => ({ type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1, description: what }) as const;\nconst EDGE_ID = { type: 'string', minLength: 1, description: 'An edge id from get_diagram (e.g. \"L_A_B_0\").' } as const;\nconst LABEL = (what: string) => ({ type: 'string', description: what }) as const;\nconst object = (properties: Record<string, JsonSchema>, required: string[] = []) => ({\n\ttype: 'object' as const,\n\tproperties,\n\t...(required.length ? { required } : {}),\n\tadditionalProperties: false\n});\n\n/** What an agent needs to know about a document: ids, labels, shapes, structure. */\nexport function describeDiagram(doc: MermaidDocument) {\n\tconst { graph } = doc;\n\treturn {\n\t\tfamily: doc.family,\n\t\teditable: doc.tier === 'editable',\n\t\tdirection: graph.direction,\n\t\t...(graph.title ? { title: graph.title } : {}),\n\t\tnodes: graph.nodes.map((n) => ({\n\t\t\tid: n.id,\n\t\t\tlabel: n.label,\n\t\t\tshape: n.shape,\n\t\t\t...(n.parentId ? { parentId: n.parentId } : {}),\n\t\t\t...(n.classes.length ? { classes: n.classes } : {}),\n\t\t\t...(n.styles.length ? { styles: n.styles } : {}),\n\t\t\t...(n.data?.synthetic ? { synthetic: true } : {})\n\t\t})),\n\t\tedges: graph.edges.map((e) => ({\n\t\t\tid: e.id,\n\t\t\tsource: e.source,\n\t\t\ttarget: e.target,\n\t\t\t...(e.label ? { label: e.label } : {}),\n\t\t\tpattern: e.pattern,\n\t\t\tarrowEnd: e.arrowEnd\n\t\t})),\n\t\tgroups: graph.groups.map((g) => ({\n\t\t\tid: g.id,\n\t\t\tlabel: g.label,\n\t\t\t...(g.parentId ? { parentId: g.parentId } : {}),\n\t\t\t...(g.direction ? { direction: g.direction } : {})\n\t\t})),\n\t\tshapes: diagramShapes(doc.family),\n\t\tpositions: Object.fromEntries(Object.entries(doc.meta.layout ?? {}).map(([id, [x, y]]) => [id, { x: Math.round(x), y: Math.round(y) }])),\n\t\t...(doc.diagnostics.length ? { diagnostics: doc.diagnostics.map((d) => ({ severity: d.severity, message: d.message, ...(d.line ? { line: d.line } : {}) })) } : {})\n\t};\n}\n\n/**\n * Agent tools for a Mermaid diagram (`DiagramController`), for a `ToolRegistry`: one\n * tool per operation with strict schemas. Reads (`get_diagram`, `export_diagram_mermaid`,\n * `find_mermaid_diagrams`) are always there; writes are hidden while the diagram is\n * read-only. Every write is checked against the current diagram first (unknown ids,\n * taken ids, shapes the family does not have), applies as one undo step, and returns\n * what it created or changed. Untouched Mermaid statements are kept byte for byte.\n */\nexport function createDiagramToolProvider(controller: DiagramController, options: DiagramToolProviderOptions = {}): AgentToolProvider {\n\tconst readOnlyReason = (): string | null => {\n\t\tconst r = typeof options.readOnly === 'function' ? options.readOnly() : options.readOnly;\n\t\tif (r) return typeof r === 'string' ? r : 'This diagram is read-only.';\n\t\treturn controller.getState().readOnly ? 'This diagram is read-only.' : null;\n\t};\n\tconst doc = () => controller.getDocument();\n\tconst actor = options.actor ?? { id: 'agent', name: 'Agent' };\n\tconst apply = async (ops: EditOp | EditOp[], label: string) => {\n\t\tconst result = options.director ? await options.director.perform(actor, Array.isArray(ops) ? ops : [ops], label) : controller.apply(ops, label);\n\t\tif (!result.ok) throw new Error(result.message);\n\t\treturn result.document;\n\t};\n\tconst edgeIds = () => new Set(doc().graph.edges.map((e) => e.id));\n\tconst newEdges = (before: Set<string>, after: MermaidDocument) =>\n\t\tafter.graph.edges.filter((e) => !before.has(e.id)).map((e) => ({ id: e.id, source: e.source, target: e.target, ...(e.label ? { label: e.label } : {}) }));\n\tconst nodeOut = (d: MermaidDocument, id: string) => {\n\t\tconst n = d.graph.nodes.find((x) => x.id === id) ?? d.graph.groups.find((x) => x.id === id);\n\t\treturn n ? { id: n.id, label: n.label, ...('shape' in n ? { shape: n.shape } : {}), ...(n.parentId ? { parentId: n.parentId } : {}) } : { id };\n\t};\n\t/** Moves plus the current positions of nodes not stored yet (see `getPositions`). */\n\tconst pinned = (moves: Array<{ id: string; position: Point }>) => {\n\t\tconst shown = options.getPositions?.();\n\t\tif (!shown) return moves;\n\t\tconst stored = doc().meta.layout ?? {};\n\t\tconst taken = new Set(moves.map((m) => m.id));\n\t\treturn [...moves, ...Object.entries(shown).filter(([id]) => !taken.has(id) && !stored[id]).map(([id, position]) => ({ id, position }))];\n\t};\n\tconst requireEditable = () => {\n\t\tif (doc().tier !== 'editable')\n\t\t\tthrow new Error(`This ${doc().family} diagram can only be changed as Mermaid text; use load_diagram_mermaid.`);\n\t};\n\n\tconst getDiagram: AgentTool = {\n\t\tname: 'get_diagram',\n\t\ttitle: 'Read the diagram',\n\t\tdescription:\n\t\t\t'Returns the diagram: its Mermaid family (flowchart, state, class, er; other families are text only), direction, nodes (id, label, shape, parentId), edges (id, source, target, label), groups (subgraphs / composite states), the shapes nodes can take, stored canvas positions, any parse diagnostics and the selection. Use the ids it returns with the write tools.',\n\t\tinputSchema: object({ includeSource: { type: 'boolean', description: 'Also return the Mermaid text (default false).' } }),\n\t\tannotations: { title: 'Read the diagram', ...READ },\n\t\thandler: async ({ includeSource = false }) => {\n\t\t\tconst reason = readOnlyReason();\n\t\t\treturn {\n\t\t\t\t...describeDiagram(doc()),\n\t\t\t\tselection: controller.getState().selection,\n\t\t\t\t...(includeSource ? { source: doc().source } : {}),\n\t\t\t\t...(reason ? { readOnly: reason } : {})\n\t\t\t};\n\t\t}\n\t};\n\n\tconst exportMermaid: AgentTool = {\n\t\tname: 'export_diagram_mermaid',\n\t\ttitle: 'Export the diagram as Mermaid',\n\t\tdescription:\n\t\t\t'Returns the diagram as Mermaid text. Canvas positions are kept in a \"%% @pixerate {…}\" comment that Mermaid ignores; pass withLayout false to leave it out.',\n\t\tinputSchema: object({ withLayout: { type: 'boolean', description: 'Keep the %% @pixerate layout comment (default true).' } }),\n\t\tannotations: { title: 'Export the diagram as Mermaid', ...READ },\n\t\thandler: async ({ withLayout = true }) => {\n\t\t\tconst source = doc().source;\n\t\t\treturn { family: doc().family, source: withLayout ? source : source.replace(/^[ \\t]*%%\\s*@(?:pixerate|ursula)\\s+\\{.*\\}[ \\t]*(\\r?\\n|$)/gm, '') };\n\t\t}\n\t};\n\n\tconst findDiagrams: AgentTool = {\n\t\tname: 'find_mermaid_diagrams',\n\t\ttitle: 'Find Mermaid diagrams in text',\n\t\tdescription:\n\t\t\t'Finds the Mermaid diagrams in a piece of text (Markdown with ```mermaid blocks, a chat reply, or a bare diagram) without changing anything. Returns each diagram\\'s source, family and title; pass a source to load_diagram_mermaid to open it.',\n\t\tinputSchema: object({ text: { type: 'string', description: 'The text to search.' } }, ['text']),\n\t\tannotations: { title: 'Find Mermaid diagrams in text', ...READ },\n\t\thandler: ({ text }) => ({ diagrams: findMermaidSources(text) })\n\t};\n\n\tconst loadMermaid: AgentTool = {\n\t\tname: 'load_diagram_mermaid',\n\t\ttitle: 'Replace the diagram with Mermaid',\n\t\tdescription:\n\t\t\t'Replaces the whole diagram with Mermaid text (one undo step). Flowchart, state, class and ER diagrams become editable on the canvas; other families are kept as text. Returns the family, node and edge counts and any parse diagnostics. Prefer the per-operation tools for small changes: they keep the rest of the text as written.',\n\t\tinputSchema: object({ source: { type: 'string', minLength: 1, description: 'Mermaid text, starting with a header such as \"flowchart LR\".' } }, ['source']),\n\t\tannotations: { title: 'Replace the diagram with Mermaid', ...DESTRUCTIVE },\n\t\thandler: async ({ source }) => {\n\t\t\t// A fenced block (```mermaid … ```) is unwrapped; a bare diagram is taken as written.\n\t\t\tconst fenced = /^\\s*(`{3,}|~{3,})/m.test(source) ? findMermaidSources(source) : [];\n\t\t\tif (fenced.length > 1) throw new Error(`The text holds ${fenced.length} diagrams; pass one (find_mermaid_diagrams lists them).`);\n\t\t\tconst text = fenced[0]?.source ?? source;\n\t\t\tif (detectFamily(text) === 'unknown')\n\t\t\t\tthrow new Error('The text has no Mermaid header (e.g. \"flowchart LR\", \"stateDiagram-v2\", \"classDiagram\", \"erDiagram\"); nothing was changed.');\n\t\t\tconst result = options.director ? await options.director.playSource(actor, text) : controller.load(text);\n\t\t\tif (!result.ok) throw new Error(result.message);\n\t\t\tconst d = result.document;\n\t\t\treturn {\n\t\t\t\tfamily: d.family,\n\t\t\t\teditable: d.tier === 'editable',\n\t\t\t\tnodes: d.graph.nodes.length,\n\t\t\t\tedges: d.graph.edges.length,\n\t\t\t\tgroups: d.graph.groups.length,\n\t\t\t\t...(d.diagnostics.length ? { diagnostics: d.diagnostics.map((x) => x.message) } : {})\n\t\t\t};\n\t\t}\n\t};\n\n\tconst addNode: AgentTool = {\n\t\tname: 'add_diagram_node',\n\t\ttitle: 'Add a node',\n\t\tdescription:\n\t\t\t'Adds a node to the diagram, optionally inside a group and connected from an existing node. Without a position the canvas places it beside its neighbours. Returns the node and any edge created.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tlabel: LABEL('Text shown in the node.'),\n\t\t\t\tid: { type: 'string', pattern: '^[A-Za-z0-9_][\\\\w.-]*$', description: 'Mermaid id (default: the next free \"N<number>\").' },\n\t\t\t\tshape: { type: 'string', description: 'One of the shapes get_diagram lists (default \"rect\"). Flowchart and state diagrams only.' },\n\t\t\t\tparentId: { type: 'string', description: 'Group (subgraph) to add the node to.' },\n\t\t\t\tfrom: { type: 'string', description: 'An existing node to connect to the new node.' },\n\t\t\t\tedgeLabel: LABEL('Label of the edge from \"from\" (needs \"from\").'),\n\t\t\t\tx: { type: 'number', description: 'Left edge in canvas px (needs y).' },\n\t\t\t\ty: { type: 'number', description: 'Top edge in canvas px (needs x).' }\n\t\t\t},\n\t\t\t['label']\n\t\t),\n\t\tannotations: { title: 'Add a node', ...WRITE },\n\t\thandler: async ({ label, id, shape, parentId, from, edgeLabel, x, y }) => {\n\t\t\trequireEditable();\n\t\t\tif ((x === undefined) !== (y === undefined)) throw new Error('Give both x and y, or neither to place the node automatically.');\n\t\t\tif (edgeLabel !== undefined && from === undefined) throw new Error('edgeLabel needs \"from\", the node the edge starts at.');\n\t\t\tconst nodeId = id ?? controller.nextId();\n\t\t\tconst before = edgeIds();\n\t\t\tconst ops: EditOp[] = [\n\t\t\t\t{ type: 'addNode', node: { id: nodeId, label, ...(shape ? { shape } : {}), ...(parentId ? { parentId } : {}) }, ...(x !== undefined ? { position: [x, y] as const } : {}) }\n\t\t\t];\n\t\t\tif (from !== undefined) ops.push({ type: 'addEdge', edge: { source: from, target: nodeId, ...(edgeLabel ? { label: edgeLabel } : {}) } });\n\t\t\tconst d = await apply(ops, 'Add node');\n\t\t\tconst edges = newEdges(before, d);\n\t\t\treturn { node: nodeOut(d, nodeId), ...(edges.length ? { edge: edges[0] } : {}) };\n\t\t}\n\t};\n\n\tconst removeElements: AgentTool = {\n\t\tname: 'remove_diagram_nodes',\n\t\ttitle: 'Remove nodes or groups',\n\t\tdescription:\n\t\t\t'Deletes nodes with every edge that touches them. A group id removes the group but keeps its contents. Returns the removed ids and the ids of the edges that went with them.',\n\t\tinputSchema: object({ ids: IDS('Node or group ids from get_diagram.') }, ['ids']),\n\t\tannotations: { title: 'Remove nodes or groups', ...DESTRUCTIVE },\n\t\thandler: async ({ ids }) => {\n\t\t\trequireEditable();\n\t\t\tconst before = doc().graph.edges.map((e) => e.id);\n\t\t\tconst d = await apply({ type: 'removeNodes', ids }, ids.length === 1 ? 'Delete node' : 'Delete nodes');\n\t\t\tconst after = new Set(d.graph.edges.map((e) => e.id));\n\t\t\treturn { removed: ids, removedEdges: before.filter((id) => !after.has(id)) };\n\t\t}\n\t};\n\n\tconst renameNode: AgentTool = {\n\t\tname: 'rename_diagram_node',\n\t\ttitle: 'Rename a node or group',\n\t\tdescription: 'Changes the label of a node or group. Returns the node with its new label.',\n\t\tinputSchema: object({ id: { ...ID, description: 'A node or group id from get_diagram.' }, label: LABEL('The new label.') }, ['id', 'label']),\n\t\tannotations: { title: 'Rename a node or group', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, label }) => {\n\t\t\trequireEditable();\n\t\t\tconst isGroup = doc().graph.groups.some((g) => g.id === id);\n\t\t\tconst d = await apply(isGroup ? { type: 'renameGroup', id, label } : { type: 'renameNode', id, label }, 'Rename');\n\t\t\treturn { node: nodeOut(d, id) };\n\t\t}\n\t};\n\n\tconst setShape: AgentTool = {\n\t\tname: 'set_diagram_node_shape',\n\t\ttitle: 'Change a node\\'s shape',\n\t\tdescription: 'Changes a node\\'s shape (flowchart and state diagrams). Use one of the shapes get_diagram lists. Returns the node.',\n\t\tinputSchema: object({ id: ID, shape: { type: 'string', minLength: 1, description: 'Shape name, e.g. \"rect\", \"rounded\", \"diam\", \"cyl\", \"circle\".' } }, ['id', 'shape']),\n\t\tannotations: { title: 'Change a node\\'s shape', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, shape }) => {\n\t\t\trequireEditable();\n\t\t\treturn { node: nodeOut(await apply({ type: 'setNodeShape', id, shape }, 'Change shape'), id) };\n\t\t}\n\t};\n\n\tconst setStyle: AgentTool = {\n\t\tname: 'set_diagram_node_style',\n\t\ttitle: 'Style a node',\n\t\tdescription:\n\t\t\t'Replaces a node\\'s inline Mermaid styles (written as a `style` statement), e.g. [\"fill:#fde68a\", \"stroke:#b45309\", \"stroke-width:2px\"]. An empty list removes them. Returns the node with its styles.',\n\t\tinputSchema: object(\n\t\t\t{ id: ID, styles: { type: 'array', items: { type: 'string', pattern: '^[\\\\w-]+:[^,;]+$' }, description: 'CSS-like \"property:value\" pairs.' } },\n\t\t\t['id', 'styles']\n\t\t),\n\t\tannotations: { title: 'Style a node', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, styles }) => {\n\t\t\trequireEditable();\n\t\t\tconst d = await apply({ type: 'setNodeStyles', id, styles }, 'Style node');\n\t\t\treturn { id, styles: d.graph.nodes.find((n) => n.id === id)?.styles ?? [] };\n\t\t}\n\t};\n\n\tconst connect: AgentTool = {\n\t\tname: 'connect_diagram_nodes',\n\t\ttitle: 'Connect two nodes',\n\t\tdescription: 'Draws an edge from one existing node to another, with an optional label. Returns the new edge with its id.',\n\t\tinputSchema: object({ source: { ...ID, description: 'Node the edge starts at.' }, target: { ...ID, description: 'Node the edge ends at.' }, label: LABEL('Edge label (optional).') }, ['source', 'target']),\n\t\tannotations: { title: 'Connect two nodes', ...WRITE },\n\t\thandler: async ({ source, target, label }) => {\n\t\t\trequireEditable();\n\t\t\tconst before = edgeIds();\n\t\t\tconst d = await apply({ type: 'addEdge', edge: { source, target, ...(label ? { label } : {}) } }, 'Connect');\n\t\t\treturn { edge: newEdges(before, d)[0] ?? null };\n\t\t}\n\t};\n\n\tconst removeEdges: AgentTool = {\n\t\tname: 'remove_diagram_edges',\n\t\ttitle: 'Remove edges',\n\t\tdescription: 'Deletes edges by id; the nodes stay. Returns the removed ids.',\n\t\tinputSchema: object({ ids: IDS('Edge ids from get_diagram.') }, ['ids']),\n\t\tannotations: { title: 'Remove edges', ...DESTRUCTIVE },\n\t\thandler: async ({ ids }) => {\n\t\t\trequireEditable();\n\t\t\tawait apply({ type: 'removeEdges', ids }, ids.length === 1 ? 'Delete edge' : 'Delete edges');\n\t\t\treturn { removed: ids };\n\t\t}\n\t};\n\n\tconst labelEdge: AgentTool = {\n\t\tname: 'set_diagram_edge_label',\n\t\ttitle: 'Label an edge',\n\t\tdescription: 'Sets an edge\\'s label; an empty string removes it. Returns the edge.',\n\t\tinputSchema: object({ id: EDGE_ID, label: LABEL('The new label (\"\" removes it).') }, ['id', 'label']),\n\t\tannotations: { title: 'Label an edge', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, label }) => {\n\t\t\trequireEditable();\n\t\t\tconst d = await apply({ type: 'setEdgeLabel', id, label }, 'Label edge');\n\t\t\tconst e = d.graph.edges.find((x) => x.id === id);\n\t\t\treturn { edge: e ? { id: e.id, source: e.source, target: e.target, label: e.label ?? '' } : { id, label } };\n\t\t}\n\t};\n\n\tconst reconnect: AgentTool = {\n\t\tname: 'reconnect_diagram_edge',\n\t\ttitle: 'Reconnect an edge',\n\t\tdescription: 'Moves an edge to new endpoints, keeping its label and style. Returns the edge (its id may change with its endpoints).',\n\t\tinputSchema: object({ id: EDGE_ID, source: { ...ID, description: 'New start node.' }, target: { ...ID, description: 'New end node.' } }, ['id', 'source', 'target']),\n\t\tannotations: { title: 'Reconnect an edge', ...WRITE },\n\t\thandler: async ({ id, source, target }) => {\n\t\t\trequireEditable();\n\t\t\tconst index = doc().graph.edges.findIndex((e) => e.id === id);\n\t\t\tconst d = await apply({ type: 'reconnectEdge', id, source, target }, 'Reconnect edge');\n\t\t\tconst e = d.graph.edges.find((x) => x.id === id) ?? d.graph.edges.find((x, i) => i === index && x.source === source && x.target === target) ?? d.graph.edges.find((x) => x.source === source && x.target === target);\n\t\t\treturn { edge: e ? { id: e.id, source: e.source, target: e.target, ...(e.label ? { label: e.label } : {}) } : null };\n\t\t}\n\t};\n\n\tconst setDirection: AgentTool = {\n\t\tname: 'set_diagram_direction',\n\t\ttitle: 'Set the layout direction',\n\t\tdescription: 'Sets the direction the diagram flows in: TB / TD (top to bottom), BT, LR (left to right) or RL. Returns the direction.',\n\t\tinputSchema: object({ direction: { type: 'string', enum: [...DIRECTIONS], description: 'TB, TD, BT, LR or RL.' } }, ['direction']),\n\t\tannotations: { title: 'Set the layout direction', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ direction }) => {\n\t\t\trequireEditable();\n\t\t\treturn { direction: (await apply({ type: 'setDirection', direction }, 'Change direction')).graph.direction };\n\t\t}\n\t};\n\n\tconst group: AgentTool = {\n\t\tname: 'group_diagram_nodes',\n\t\ttitle: 'Group nodes',\n\t\tdescription: 'Puts nodes into a new group (a flowchart subgraph, composite state or class namespace). Returns the group id.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tnodeIds: IDS('Node ids to put in the group.'),\n\t\t\t\tlabel: LABEL('Group title.'),\n\t\t\t\tid: { type: 'string', pattern: '^[A-Za-z0-9_][\\\\w.-]*$', description: 'Group id (default: the next free \"G<number>\").' }\n\t\t\t},\n\t\t\t['nodeIds', 'label']\n\t\t),\n\t\tannotations: { title: 'Group nodes', ...WRITE },\n\t\thandler: async ({ nodeIds, label, id }) => {\n\t\t\trequireEditable();\n\t\t\tif (!diagramSupportsGroups(doc().family)) throw new Error(`${doc().family} diagrams have no groups.`);\n\t\t\tconst groupId = id ?? controller.nextId('G');\n\t\t\tawait apply({ type: 'groupNodes', groupId, label, nodeIds }, 'Group');\n\t\t\treturn { group: { id: groupId, label, nodeIds } };\n\t\t}\n\t};\n\n\tconst ungroup: AgentTool = {\n\t\tname: 'ungroup_diagram_group',\n\t\ttitle: 'Ungroup',\n\t\tdescription: 'Removes a group and keeps its nodes (they move to the group\\'s parent). Returns the ids that were in it.',\n\t\tinputSchema: object({ id: { type: 'string', minLength: 1, description: 'A group id from get_diagram.' } }, ['id']),\n\t\tannotations: { title: 'Ungroup', ...DESTRUCTIVE },\n\t\thandler: async ({ id }) => {\n\t\t\trequireEditable();\n\t\t\tconst members = [...doc().graph.nodes, ...doc().graph.groups].filter((x) => x.parentId === id).map((x) => x.id);\n\t\t\tawait apply({ type: 'ungroup', groupId: id }, 'Ungroup');\n\t\t\treturn { ungrouped: id, members };\n\t\t}\n\t};\n\n\tconst move: AgentTool = {\n\t\tname: 'move_diagram_nodes',\n\t\ttitle: 'Move nodes',\n\t\tdescription:\n\t\t\t'Moves nodes or groups on the canvas to absolute positions (top-left, canvas px). Positions are stored in the %% @pixerate comment; the Mermaid statements do not change. Returns the new positions.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tmoves: {\n\t\t\t\t\ttype: 'array',\n\t\t\t\t\tminItems: 1,\n\t\t\t\t\tdescription: 'One entry per node.',\n\t\t\t\t\titems: object({ id: { ...ID, description: 'Node or group id.' }, x: { type: 'number', description: 'Left edge in px.' }, y: { type: 'number', description: 'Top edge in px.' } }, ['id', 'x', 'y'])\n\t\t\t\t}\n\t\t\t},\n\t\t\t['moves']\n\t\t),\n\t\tannotations: { title: 'Move nodes', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ moves }) => {\n\t\t\tconst list = moves as Array<{ id: string; x: number; y: number }>;\n\t\t\trequireEditable();\n\t\t\tawait apply({ type: 'moveNodes', moves: pinned(list.map((m) => ({ id: m.id, position: [m.x, m.y] as const }))) }, 'Move');\n\t\t\treturn { moved: moves };\n\t\t}\n\t};\n\n\tconst undo: AgentTool = {\n\t\tname: 'undo_diagram',\n\t\ttitle: 'Undo a diagram edit',\n\t\tdescription: 'Undoes the last diagram edit. Returns whether something was undone and its label.',\n\t\tinputSchema: object({}),\n\t\tannotations: { title: 'Undo a diagram edit', ...WRITE },\n\t\thandler: async () => {\n\t\t\tconst label = controller.getState().undoLabel;\n\t\t\tconst undone = controller.undo();\n\t\t\treturn { undone, ...(undone ? { label } : {}) };\n\t\t}\n\t};\n\n\tconst redo: AgentTool = {\n\t\tname: 'redo_diagram',\n\t\ttitle: 'Redo a diagram edit',\n\t\tdescription: 'Redoes the last undone diagram edit. Returns whether something was redone and its label.',\n\t\tinputSchema: object({}),\n\t\tannotations: { title: 'Redo a diagram edit', ...WRITE },\n\t\thandler: async () => {\n\t\t\tconst label = controller.getState().redoLabel;\n\t\t\tconst redone = controller.redo();\n\t\t\treturn { redone, ...(redone ? { label } : {}) };\n\t\t}\n\t};\n\n\tconst renderDiagram: AgentTool = {\n\t\tname: 'render_diagram',\n\t\ttitle: 'Render the diagram',\n\t\tdescription:\n\t\t\t'Draws the diagram without changing it, to check it visually: standalone SVG text by default, or a PNG image (format \"png\") when the host can rasterize. Positions match the canvas.',\n\t\tinputSchema: object({\n\t\t\tformat: { type: 'string', enum: ['svg', 'png'], description: 'svg (default) or png (only when the host supports it).' },\n\t\t\tmaxWidth: { type: 'number', minimum: 16, description: 'Fit within this width in px (default: natural size).' },\n\t\t\tmaxHeight: { type: 'number', minimum: 16, description: 'Fit within this height in px (default: natural size).' }\n\t\t}),\n\t\tannotations: { title: 'Render the diagram', ...READ },\n\t\thandler: async ({ format = 'svg', maxWidth, maxHeight }) => {\n\t\t\tif (doc().tier !== 'editable') throw new Error(`A ${doc().family} diagram is kept as Mermaid text and cannot be drawn here; use export_diagram_mermaid.`);\n\t\t\tconst drawn = options.renderSvg!({ maxWidth, maxHeight });\n\t\t\tif (format === 'png') {\n\t\t\t\tif (!options.rasterize) throw new Error('This host cannot make PNG images; call render_diagram with format \"svg\".');\n\t\t\t\tconst image = await options.rasterize(drawn.svg, { width: drawn.width, height: drawn.height });\n\t\t\t\treturn { content: [{ type: 'image', data: image.data, mimeType: image.mimeType }] };\n\t\t\t}\n\t\t\treturn { format: 'svg', width: drawn.width, height: drawn.height, svg: drawn.svg };\n\t\t}\n\t};\n\n\tconst reads = [getDiagram, exportMermaid, findDiagrams, ...(options.renderSvg ? [renderDiagram] : [])];\n\tconst writes = [loadMermaid, addNode, removeElements, renameNode, setShape, setStyle, connect, removeEdges, labelEdge, reconnect, setDirection, group, ungroup, move, undo, redo];\n\n\treturn {\n\t\tid: options.id ?? 'diagram',\n\t\tnamespace: options.namespace,\n\t\ttools: () => (readOnlyReason() ? reads : [...reads, ...writes]),\n\t\tsubscribe: (notify) => {\n\t\t\tlet last = readOnlyReason();\n\t\t\treturn controller.subscribe(() => {\n\t\t\t\tconst now = readOnlyReason();\n\t\t\t\tif (now !== last) {\n\t\t\t\t\tlast = now;\n\t\t\t\t\tnotify();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}\n","import { parseMermaid } from \"./document.js\";\nimport type { DiagramGraph } from \"./types.js\";\n\n/** What a frame shows, for telling frames apart and finding what each one adds. */\nfunction signature(graph: DiagramGraph): string {\n return JSON.stringify([\n graph.title ?? \"\",\n graph.nodes.map((n) => [n.id, n.label, n.parentId ?? \"\", n.data ?? null]),\n graph.edges.map((e) => [e.source, e.target, e.label ?? \"\"]),\n graph.groups.map((g) => [g.id, g.label, g.parentId ?? \"\"]),\n graph.data ?? null,\n ]);\n}\n\nconst isMeta = (line: string) => /^\\s*%%\\s*@(?:pixerate|ursula)\\b/.test(line);\n\n/** Body lines (after the header), without the layout comment and trailing blanks. */\nfunction bodyLines(lines: string[], headerIndex: number): string[] {\n const body = lines.slice(headerIndex + 1).filter((l) => !isMeta(l));\n while (body.length && !body[body.length - 1]!.trim()) body.pop();\n return body;\n}\n\nfunction headerIndexOf(lines: string[], header: string): number {\n const wanted = header.trim();\n return wanted ? lines.findIndex((l) => l.trim() === wanted) : -1;\n}\n\nexport interface DraftOptions {\n /** Most frames to play; longer drafts are sampled evenly (default 40). */\n maxFrames?: number;\n}\n\n/**\n * Splits writing `target` over `current` into frames that grow the diagram\n * statement by statement, so a whole-diagram write can be played as the\n * diagram being drawn. Each frame is a valid prefix of `target` (it parses\n * as the same family without diagnostics) that shows something new; the last\n * frame is `target` itself.\n *\n * Returns null when `target` does not extend `current` (some existing line\n * changes), when the family changes, or when there is nothing to grow: the\n * write is then committed in one step.\n */\nexport function draftFrames(\n current: string,\n target: string,\n options: DraftOptions = {},\n): string[] | null {\n const maxFrames = Math.max(2, options.maxFrames ?? 40);\n const now = parseMermaid(current);\n const goal = parseMermaid(target);\n if (goal.family !== now.family || goal.diagnostics.length) return null;\n\n const eol = goal.eol;\n const lines = target.split(/\\r?\\n/);\n const at = headerIndexOf(lines, goal.header);\n if (at < 0) return null;\n const currentLines = current.split(/\\r?\\n/);\n const currentAt = headerIndexOf(currentLines, now.header);\n if (currentAt < 0) return null;\n\n // The target must keep every current body line, in order, as its start.\n const have = bodyLines(currentLines, currentAt);\n const body = lines\n .map((line, index) => ({ line, index }))\n .slice(at + 1)\n .filter(({ line }) => !isMeta(line));\n for (let i = 0; i < have.length; i++) {\n if (body[i]?.line !== have[i]) return null;\n }\n\n const frames: string[] = [];\n let last = signature(now.graph);\n for (let i = have.length; i < body.length; i++) {\n const end = body[i]!.index;\n const candidate = lines.slice(0, end + 1).join(eol) + eol;\n const doc = parseMermaid(candidate);\n if (doc.family !== goal.family || doc.diagnostics.length) continue;\n const sig = signature(doc.graph);\n if (sig === last) continue;\n frames.push(candidate);\n last = sig;\n }\n if (!frames.length) return null;\n // The last frame is the target exactly (its trailing lines and metadata included).\n if (last === signature(goal.graph)) frames[frames.length - 1] = target;\n else frames.push(target);\n\n if (frames.length <= maxFrames) return frames;\n const sampled: string[] = [];\n for (let k = 1; k <= maxFrames; k++) {\n sampled.push(frames[Math.round((k * frames.length) / maxFrames) - 1]!);\n }\n return sampled;\n}\n\n/** Ids of nodes and groups in `after` that are new or changed since `before`. */\nexport function changedItems(before: DiagramGraph, after: DiagramGraph): string[] {\n const old = new Map<string, string>();\n for (const n of before.nodes) old.set(n.id, JSON.stringify([n.label, n.parentId, n.data]));\n for (const g of before.groups) old.set(g.id, JSON.stringify([g.label, g.parentId]));\n const out: string[] = [];\n for (const n of after.nodes)\n if (old.get(n.id) !== JSON.stringify([n.label, n.parentId, n.data])) out.push(n.id);\n for (const g of after.groups)\n if (old.get(g.id) !== JSON.stringify([g.label, g.parentId])) out.push(g.id);\n return out;\n}\n","// Acting out diagram edits follows Ursula's remote control (src/remote/commands.ts `planCommand`,\n// src/remote/draft.ts): a pure planner turns each edit into cursor steps on the canvas, and a\n// whole-diagram write is drawn statement by statement. Shared pieces live in animation/presence.\nimport {\n\tPerformanceQueue,\n\tPresenceStore,\n\trealPresenceClock,\n\ttweenPresenceCursor,\n\ttype PresenceActorInfo,\n\ttype PresenceClock\n} from '../animation/presence.js';\nimport type { DiagramController } from './controller.js';\nimport { validateDiagramEdit } from './controller.js';\nimport { applyEdits, parseMermaid } from './document.js';\nimport { changedItems, draftFrames } from './draft.js';\nimport type { EditOp, MermaidDocument, Point } from './types.js';\n\n/** Where things are drawn on the canvas (absolute top-left and size), e.g. `mermaidToCanvas(...).layout`. */\nexport interface DiagramPlacement {\n\tpositions: Readonly<Record<string, Point>>;\n\tsizes: Readonly<Record<string, { width: number; height: number }>>;\n}\n\nexport type DiagramPerformanceStep =\n\t| { type: 'move'; to: { x: number; y: number }; ms: number }\n\t| { type: 'say'; text: string; ms: number }\n\t| { type: 'type'; text: string; ms: number }\n\t| { type: 'press' }\n\t| { type: 'release' }\n\t/** Drag a node (and the cursor) so its top-left ends at `to`; the node follows live. */\n\t| { type: 'drag'; id: string; to: { x: number; y: number }; position: Point; ms: number }\n\t| { type: 'commit' };\n\nconst center = (place: DiagramPlacement, id: string) => {\n\tconst p = place.positions[id];\n\tif (!p) return null;\n\tconst s = place.sizes[id] ?? { width: 160, height: 56 };\n\treturn { x: p[0] + s.width / 2, y: p[1] + s.height / 2 };\n};\n\n/** Bottom-right of everything drawn, where a node without a better place appears. */\nfunction freeSpot(place: DiagramPlacement): { x: number; y: number } {\n\tlet maxX = 0;\n\tlet maxY = 0;\n\tlet any = false;\n\tfor (const [id, [x, y]] of Object.entries(place.positions)) {\n\t\tconst s = place.sizes[id] ?? { width: 160, height: 56 };\n\t\tmaxX = Math.max(maxX, x + s.width);\n\t\tmaxY = Math.max(maxY, y + s.height);\n\t\tany = true;\n\t}\n\treturn any ? { x: maxX + 60, y: maxY / 2 } : { x: 80, y: 40 };\n}\n\nconst NEW_NODE = { width: 160, height: 56 };\n\n/**\n * Where a new node's centre goes beside `from`: one rank further along the diagram's\n * direction, then stepped sideways until it overlaps nothing drawn.\n */\nfunction besideFree(place: DiagramPlacement, from: { x: number; y: number }, direction: string): { x: number; y: number } {\n\tconst d = direction.toUpperCase();\n\tconst along = d === 'LR' ? { x: 1, y: 0 } : d === 'RL' ? { x: -1, y: 0 } : d === 'BT' ? { x: 0, y: -1 } : { x: 0, y: 1 };\n\tconst side = { x: along.y, y: along.x };\n\tconst rects = Object.entries(place.positions).map(([id, [x, y]]) => ({ x, y, ...(place.sizes[id] ?? NEW_NODE) }));\n\tconst free = (c: { x: number; y: number }) =>\n\t\t!rects.some((r) => c.x - NEW_NODE.width / 2 - 20 < r.x + r.width && c.x + NEW_NODE.width / 2 + 20 > r.x && c.y - NEW_NODE.height / 2 - 20 < r.y + r.height && c.y + NEW_NODE.height / 2 + 20 > r.y);\n\tconst base = { x: from.x + along.x * 240, y: from.y + along.y * 140 };\n\tfor (let i = 0; i < 24; i++) {\n\t\tconst k = i === 0 ? 0 : (i % 2 ? 1 : -1) * Math.ceil(i / 2);\n\t\tconst c = { x: base.x + side.x * k * 200, y: base.y + side.y * k * 90 };\n\t\tif (free(c)) return c;\n\t}\n\treturn base;\n}\n\n/**\n * Plans how an agent acts out one edit on the canvas: where the cursor goes, what it\n * clicks, drags or types, and when the edit commits. Pure: the same document, placement\n * and edit give the same steps. `next` is the edit that follows in the same call (a new\n * node is placed beside the node an edge will connect it from).\n */\nexport function planDiagramEdit(doc: MermaidDocument, place: DiagramPlacement, op: EditOp, speed = 1, next?: EditOp): DiagramPerformanceStep[] {\n\tconst ms = (n: number) => Math.round(n / Math.max(0.1, speed));\n\tconst at = (id: string) => center(place, id);\n\tconst click = (to: { x: number; y: number } | null): DiagramPerformanceStep[] =>\n\t\tto ? [{ type: 'move', to, ms: ms(380) }, { type: 'press' }, { type: 'release' }] : [];\n\tconst edgeMid = (id: string) => {\n\t\tconst e = doc.graph.edges.find((x) => x.id === id);\n\t\tconst a = e && at(e.source);\n\t\tconst b = e && at(e.target);\n\t\treturn a && b ? { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 } : null;\n\t};\n\tswitch (op.type) {\n\t\tcase 'addNode': {\n\t\t\tconst from = next?.type === 'addEdge' && next.edge.target === op.node.id ? at(next.edge.source) : null;\n\t\t\tconst to = op.position\n\t\t\t\t? { x: op.position[0] + 80, y: op.position[1] + 28 }\n\t\t\t\t: from\n\t\t\t\t\t? besideFree(place, from, doc.graph.direction)\n\t\t\t\t\t: freeSpot(place);\n\t\t\tconst label = op.node.label ?? op.node.id;\n\t\t\treturn [...click(to), { type: 'type', text: label, ms: ms(Math.min(900, 60 * label.length + 200)) }, { type: 'commit' }];\n\t\t}\n\t\tcase 'addEdge': {\n\t\t\tconst a = at(op.edge.source);\n\t\t\tconst b = at(op.edge.target) ?? (a ? { x: a.x + 260, y: a.y } : null);\n\t\t\tif (!a || !b) return [{ type: 'commit' }];\n\t\t\treturn [\n\t\t\t\t{ type: 'move', to: a, ms: ms(380) },\n\t\t\t\t{ type: 'press' },\n\t\t\t\t{ type: 'move', to: b, ms: ms(520) },\n\t\t\t\t{ type: 'release' },\n\t\t\t\t...(op.edge.label ? [{ type: 'type' as const, text: op.edge.label, ms: ms(500) }] : []),\n\t\t\t\t{ type: 'commit' }\n\t\t\t];\n\t\t}\n\t\tcase 'moveNodes': {\n\t\t\tconst steps: DiagramPerformanceStep[] = [];\n\t\t\tfor (const move of op.moves) {\n\t\t\t\tconst from = at(move.id);\n\t\t\t\tconst size = place.sizes[move.id] ?? { width: 160, height: 56 };\n\t\t\t\tif (!from) continue;\n\t\t\t\tsteps.push(\n\t\t\t\t\t{ type: 'move', to: from, ms: ms(380) },\n\t\t\t\t\t{ type: 'press' },\n\t\t\t\t\t{ type: 'drag', id: move.id, to: { x: move.position[0] + size.width / 2, y: move.position[1] + size.height / 2 }, position: move.position, ms: ms(520) },\n\t\t\t\t\t{ type: 'release' }\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn [...steps, { type: 'commit' }];\n\t\t}\n\t\tcase 'renameNode':\n\t\tcase 'renameGroup':\n\t\t\treturn [...click(at(op.id)), { type: 'press' }, { type: 'release' }, { type: 'type', text: op.label, ms: ms(Math.min(900, 60 * op.label.length + 200)) }, { type: 'commit' }];\n\t\tcase 'removeNodes':\n\t\t\treturn [...click(at(op.ids[0]!)), { type: 'say', text: 'Delete', ms: ms(300) }, { type: 'commit' }];\n\t\tcase 'removeEdges':\n\t\t\treturn [...click(edgeMid(op.ids[0]!)), { type: 'say', text: 'Delete', ms: ms(300) }, { type: 'commit' }];\n\t\tcase 'setEdgeLabel':\n\t\t\treturn [...click(edgeMid(op.id)), { type: 'type', text: op.label, ms: ms(500) }, { type: 'commit' }];\n\t\tcase 'reconnectEdge': {\n\t\t\tconst a = at(op.source);\n\t\t\tconst b = at(op.target);\n\t\t\treturn [...click(edgeMid(op.id)), ...(a && b ? [{ type: 'move' as const, to: a, ms: ms(300) }, { type: 'press' as const }, { type: 'move' as const, to: b, ms: ms(500) }, { type: 'release' as const }] : []), { type: 'commit' }];\n\t\t}\n\t\tcase 'setNodeShape':\n\t\tcase 'setNodeStyles':\n\t\tcase 'setNodeClasses':\n\t\tcase 'setNodeLink':\n\t\tcase 'setNodeData':\n\t\tcase 'resizeNode':\n\t\t\treturn [...click(at(op.id)), { type: 'commit' }];\n\t\tcase 'groupNodes': {\n\t\t\tconst points = op.nodeIds.map(at).filter((p): p is { x: number; y: number } => !!p);\n\t\t\tif (!points.length) return [{ type: 'commit' }];\n\t\t\tconst min = { x: Math.min(...points.map((p) => p.x)) - 90, y: Math.min(...points.map((p) => p.y)) - 40 };\n\t\t\tconst max = { x: Math.max(...points.map((p) => p.x)) + 90, y: Math.max(...points.map((p) => p.y)) + 40 };\n\t\t\treturn [{ type: 'move', to: min, ms: ms(380) }, { type: 'press' }, { type: 'move', to: max, ms: ms(520) }, { type: 'release' }, { type: 'type', text: op.label, ms: ms(500) }, { type: 'commit' }];\n\t\t}\n\t\tdefault:\n\t\t\treturn [{ type: 'commit' }];\n\t}\n}\n\nexport interface DiagramDirectorOptions {\n\t/** Where nodes are drawn now, e.g. `() => mermaidToCanvas(controller.getDocument()).layout`. */\n\tplacement: () => DiagramPlacement;\n\tclock?: PresenceClock;\n\t/** 1 = natural speed; 2 = twice as fast. */\n\tspeed?: number;\n\t/** Hide each actor's cursor this long after its last call (ms, default 1500; 0 keeps it). */\n\tlinger?: number;\n\t/** Most frames when a whole-diagram write is drawn statement by statement (default 40). */\n\tmaxDraftFrames?: number;\n}\n\nexport type DiagramPerformanceResult = { ok: true; document: MermaidDocument } | { ok: false; message: string };\n\n/**\n * Acts out agent edits on a Mermaid diagram: each actor's cursor moves to the node or edge,\n * clicks, drags, connects and types, the edit lands, and everything one call changes is one\n * undo step, while the user keeps editing. Calls queue per actor. A batch is checked against\n * the diagram before the cursor moves; if the diagram changed underneath and an edit no\n * longer applies, the call's earlier edits are rolled back and the call fails with a sentence.\n * A whole-diagram write (`playSource`) is drawn statement by statement.\n */\nexport class DiagramDirector {\n\treadonly presence: PresenceStore;\n\tprivate readonly clock: PresenceClock;\n\tprivate readonly queue: PerformanceQueue;\n\n\tconstructor(\n\t\tprivate readonly controller: DiagramController,\n\t\tpresence: PresenceStore,\n\t\tprivate readonly options: DiagramDirectorOptions\n\t) {\n\t\tthis.presence = presence;\n\t\tthis.clock = options.clock ?? realPresenceClock;\n\t\tthis.queue = new PerformanceQueue(presence, {\n\t\t\tclock: this.clock,\n\t\t\tlinger: options.linger,\n\t\t\tbeginGesture: (key) => controller.beginGesture(key),\n\t\t\tendGesture: () => controller.endGesture()\n\t\t});\n\t}\n\n\t/** Acts out `ops` in order for `actor` as one undo step. */\n\tperform(actor: PresenceActorInfo, ops: readonly EditOp[], label = 'Edit diagram'): Promise<DiagramPerformanceResult> {\n\t\t// All-or-nothing: the whole batch must apply before the cursor moves.\n\t\tlet check = this.controller.getDocument();\n\t\tif (this.controller.getState().readOnly) return Promise.resolve({ ok: false, message: 'The diagram is read-only.' });\n\t\tfor (const op of ops) {\n\t\t\tconst problem = validateDiagramEdit(check, op);\n\t\t\tif (problem) return Promise.resolve({ ok: false, message: problem });\n\t\t\tcheck = applyEdits(check, [op]);\n\t\t}\n\t\treturn this.queue.run(actor, async () => {\n\t\t\tconst mark = this.controller.history.mark();\n\t\t\tconst fail = (message: string): DiagramPerformanceResult => {\n\t\t\t\tthis.controller.history.revertTo(mark);\n\t\t\t\treturn { ok: false, message };\n\t\t\t};\n\t\t\t// Placing or dragging stores positions; pin the rest of an auto-laid diagram first so\n\t\t\t// it does not shift around them (see GOTCHAS.md).\n\t\t\tif (ops.some((op) => op.type === 'addNode' || op.type === 'moveNodes')) {\n\t\t\t\tconst problem = this.pin(label);\n\t\t\t\tif (problem) return fail(problem);\n\t\t\t}\n\t\t\tfor (let i = 0; i < ops.length; i++) {\n\t\t\t\tconst op = ops[i]!;\n\t\t\t\tconst steps = planDiagramEdit(this.controller.getDocument(), this.options.placement(), op, this.options.speed, ops[i + 1]);\n\t\t\t\tfor (const step of steps) {\n\t\t\t\t\tconst problem = await this.act(actor.id, step, op, label);\n\t\t\t\t\tif (problem) return fail(problem);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ok: true, document: this.controller.getDocument() };\n\t\t});\n\t}\n\n\t/**\n\t * Replaces the diagram with `source`, drawing it statement by statement when it extends\n\t * what is there (Ursula's draft playback): each frame lands, the cursor points at what\n\t * appeared, and the whole write is one undo step. Otherwise it lands at once.\n\t */\n\tplaySource(actor: PresenceActorInfo, source: string, label = 'Replace diagram'): Promise<DiagramPerformanceResult> {\n\t\tif (this.controller.getState().readOnly) return Promise.resolve({ ok: false, message: 'The diagram is read-only.' });\n\t\treturn this.queue.run(actor, async () => {\n\t\t\tconst frames = draftFrames(this.controller.getSource(), source, { maxFrames: this.options.maxDraftFrames }) ?? [source];\n\t\t\tconst pause = Math.round(260 / Math.max(0.1, this.options.speed ?? 1));\n\t\t\tfor (const frame of frames) {\n\t\t\t\tconst before = this.controller.getDocument().graph;\n\t\t\t\tconst result = this.controller.apply({ type: 'replaceSource', source: frame }, label);\n\t\t\t\tif (!result.ok) return result;\n\t\t\t\tconst fresh = changedItems(before, result.document.graph);\n\t\t\t\tconst target = fresh.length ? center(this.options.placement(), fresh[fresh.length - 1]!) : null;\n\t\t\t\tconst actorNow = this.presence.get(actor.id);\n\t\t\t\tif (target && actorNow) await tweenPresenceCursor(this.presence, this.clock, actor.id, actorNow, target, pause);\n\t\t\t\telse await this.clock.wait(pause);\n\t\t\t}\n\t\t\treturn { ok: true, document: this.controller.getDocument() };\n\t\t});\n\t}\n\n\t/** Stores the drawn position of every node and group that has none yet. */\n\tprivate pin(label: string): string | null {\n\t\tconst doc = this.controller.getDocument();\n\t\tconst stored = doc.meta.layout ?? {};\n\t\tconst known = new Set([...doc.graph.nodes, ...doc.graph.groups].map((x) => x.id));\n\t\tconst moves = Object.entries(this.options.placement().positions)\n\t\t\t.filter(([id]) => known.has(id) && !stored[id])\n\t\t\t.map(([id, position]) => ({ id, position }));\n\t\tif (!moves.length) return null;\n\t\tconst result = this.controller.apply({ type: 'moveNodes', moves }, label);\n\t\treturn result.ok ? null : result.message;\n\t}\n\n\t/** Runs one step; returns a problem sentence when the edit can no longer be applied. */\n\tprivate async act(id: string, step: DiagramPerformanceStep, op: EditOp, label: string): Promise<string | null> {\n\t\tconst where = () => {\n\t\t\tconst a = this.presence.get(id);\n\t\t\treturn a ? { x: a.x, y: a.y } : { x: 0, y: 0 };\n\t\t};\n\t\tswitch (step.type) {\n\t\t\tcase 'say':\n\t\t\t\tthis.presence.set({ id, message: step.text });\n\t\t\t\tawait this.clock.wait(step.ms);\n\t\t\t\treturn null;\n\t\t\tcase 'type': {\n\t\t\t\t// The note grows as if typed.\n\t\t\t\tconst chars = [...step.text];\n\t\t\t\tconst each = step.ms / Math.max(1, chars.length);\n\t\t\t\tfor (let i = 1; i <= chars.length; i++) {\n\t\t\t\t\tthis.presence.set({ id, message: chars.slice(0, i).join('') });\n\t\t\t\t\tawait this.clock.wait(each);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tcase 'move':\n\t\t\t\tawait tweenPresenceCursor(this.presence, this.clock, id, where(), step.to, step.ms);\n\t\t\t\treturn null;\n\t\t\tcase 'press':\n\t\t\t\tthis.presence.set({ id, pressed: true });\n\t\t\t\treturn null;\n\t\t\tcase 'release':\n\t\t\t\tthis.presence.set({ id, pressed: false });\n\t\t\t\treturn null;\n\t\t\tcase 'drag': {\n\t\t\t\tconst start = this.options.placement().positions[step.id];\n\t\t\t\tlet problem: string | null = null;\n\t\t\t\tawait tweenPresenceCursor(this.presence, this.clock, id, where(), step.to, step.ms, (e) => {\n\t\t\t\t\tif (problem || !start) return;\n\t\t\t\t\tconst position: Point = [start[0] + (step.position[0] - start[0]) * e, start[1] + (step.position[1] - start[1]) * e];\n\t\t\t\t\tconst result = this.controller.apply({ type: 'moveNodes', moves: [{ id: step.id, position }] }, label);\n\t\t\t\t\tif (!result.ok) problem = result.message;\n\t\t\t\t});\n\t\t\t\treturn problem;\n\t\t\t}\n\t\t\tcase 'commit': {\n\t\t\t\t// A new node lands where the cursor clicked.\n\t\t\t\tconst placed: EditOp =\n\t\t\t\t\top.type === 'addNode' && !op.position ? { ...op, position: [Math.round(where().x - 80), Math.round(where().y - 28)] } : op;\n\t\t\t\tconst result = this.controller.apply(placed, label);\n\t\t\t\treturn result.ok ? null : result.message;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Reads a document for planning without a controller (tests, previews). */\nexport function planDiagramEdits(source: string, place: DiagramPlacement, ops: readonly EditOp[], speed = 1): DiagramPerformanceStep[][] {\n\tlet doc = parseMermaid(source);\n\treturn ops.map((op, i) => {\n\t\tconst steps = planDiagramEdit(doc, place, op, speed, ops[i + 1]);\n\t\tdoc = applyEdits(doc, [op]);\n\t\treturn steps;\n\t});\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/diagram/index.cjs","../../src/diagram/types.ts","../../src/diagram/source/find.ts","../../src/diagram/families/pie.ts","../../src/diagram/families/mindmap.ts","../../src/diagram/families/quadrant.ts","../../src/diagram/families/c4.ts","../../src/diagram/families/architecture.ts","../../src/diagram/families/sequence.ts","../../src/diagram/families/gitgraph.ts","../../src/diagram/families/gantt.ts","../../src/diagram/families/timeline.ts","../../src/diagram/families/journey.ts","../../src/diagram/families/xychart.ts","../../src/diagram/families/radar.ts","../../src/diagram/families/fields.ts","../../src/diagram/families/index.ts","../../src/diagram/sankey/index.ts","../../src/diagram/treemap/index.ts","../../src/diagram/controller.ts","../../src/diagram/tools.ts","../../src/diagram/draft.ts","../../src/diagram/director.ts"],"names":["nextTaskId"],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACF,yDAA8B;AAC9B;AACA;AC0FO,IAAM,WAAA,EAAa,CAAC,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;ADxFvD;AACA;AEvHA,IAAM,aAAA,EAAe,mBAAA;AAad,SAAS,kBAAA,CAAmB,IAAA,EAA8B;AAC/D,EAAA,MAAM,MAAA,EAAQ,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA;AACxC,EAAA,MAAM,OAAA,EAAS,YAAA,CAAa,KAAK,CAAA;AACjC,EAAA,GAAA,CAAI,MAAA,CAAO,MAAA,EAAQ;AACjB,IAAA,MAAM,MAAA,EAAwB,CAAC,CAAA;AAC/B,IAAA,IAAA,CAAA,MAAW,MAAA,GAAS,MAAA,EAAQ;AAC1B,MAAA,GAAA,CAAI,KAAA,CAAM,KAAA,GAAQ,CAAC,YAAA,CAAa,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,EAAG,QAAA;AAClD,MAAA,MAAM,KAAA,EAAO,SAAA,CAAU,KAAA,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IAAA;AACnB,IAAA;AACP,EAAA;AACF;AtByhE4C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/diagram/index.cjs","sourcesContent":[null,"/**\n * Core document model shared by every diagram family.\n *\n * Design goals:\n * - **Lossless**: a document parsed and serialised without edits is\n * byte-identical to its input. Anything the diagram module does not understand is kept\n * verbatim as a passthrough statement.\n * - **Graph-first**: every editable family projects onto the same\n * {@link DiagramGraph} so that one canvas can render all of them.\n * - **Mermaid-compatible metadata**: canvas state is stored in\n * `%% @pixerate …` comments (or frontmatter) that every Mermaid parser ignores.\n *\n * @module\n */\n\n/** Mermaid diagram families the diagram module knows how to detect. */\nexport type DiagramFamily =\n | \"flowchart\"\n | \"state\"\n | \"class\"\n | \"er\"\n | \"requirement\"\n | \"mindmap\"\n | \"sequence\"\n | \"gantt\"\n | \"pie\"\n | \"journey\"\n | \"timeline\"\n | \"gitGraph\"\n | \"c4\"\n | \"quadrant\"\n | \"sankey\"\n | \"xychart\"\n | \"block\"\n | \"architecture\"\n | \"packet\"\n | \"kanban\"\n | \"radar\"\n | \"treemap\"\n | \"info\"\n | \"unknown\";\n\n/**\n * How deeply the diagram module can edit a family.\n *\n * - `editable` — statement-level lossless editing on the canvas. Every\n * family the diagram module ships an adapter for is editable; the canvas model differs\n * per family (graph, tree, timeline, chart data…).\n * - `render-only` — source editing with a live Mermaid preview; no canvas.\n * Used for families without an adapter and for unknown input.\n */\nexport type SupportTier = \"editable\" | \"render-only\";\n\n/** `[x, y]` canvas position in React Flow coordinates. */\nexport type Point = readonly [number, number];\n\n/** Line pattern of an edge. Mirrors Mermaid's `thickness` / `pattern`. */\nexport type EdgePattern = \"solid\" | \"dotted\" | \"thick\" | \"invisible\";\n\n/**\n * Arrow head identifiers, aligned with Mermaid's `arrowTypeStart` /\n * `arrowTypeEnd` values so React Flow markers can be derived uniformly.\n */\nexport type ArrowHead =\n | \"none\"\n | \"arrow_point\"\n | \"arrow_open\"\n | \"arrow_cross\"\n | \"arrow_circle\"\n | \"arrow_barb\"\n | \"extension\"\n | \"composition\"\n | \"aggregation\"\n | \"dependency\"\n | \"lollipop\"\n | \"only_one\"\n | \"zero_or_one\"\n | \"one_or_more\"\n | \"zero_or_more\"\n | \"md_parent\"\n | \"requirement_arrow\"\n | \"requirement_contains\";\n\nexport interface GraphNode {\n /** Stable identifier (the Mermaid id, e.g. `A`). */\n id: string;\n /** Display label (raw Mermaid text, markdown strings included). */\n label: string;\n /**\n * Canonical shape identifier (`rect`, `diam`, `cyl`, `stateStart`,\n * `classBox`, `erBox`…). Mermaid aliases such as `squareRect` or `diamond`\n * are normalised on parse. See `FLOWCHART_SHAPES` for the flowchart set.\n */\n shape: string;\n /** Enclosing group (subgraph / composite state / namespace) id. */\n parentId?: string;\n /** CSS class names applied with `:::`, `class`, or `classDef` defaults. */\n classes: string[];\n /** Inline `style` declarations (`fill:#f9f`, …). */\n styles: string[];\n /** `click` href target. */\n link?: string;\n linkTarget?: string;\n tooltip?: string;\n /** Family-specific payload (class members, ER attributes, state notes…). */\n data?: Record<string, unknown>;\n}\n\nexport interface GraphGroup {\n id: string;\n label: string;\n parentId?: string;\n /** Local layout direction override (`TB`, `LR`, …). */\n direction?: string;\n classes: string[];\n styles: string[];\n data?: Record<string, unknown>;\n}\n\nexport interface GraphEdge {\n /** Mermaid-compatible id (`L_A_B_0` for auto ids, or the user id). */\n id: string;\n source: string;\n target: string;\n label?: string;\n pattern: EdgePattern;\n arrowStart: ArrowHead;\n arrowEnd: ArrowHead;\n /** Minimum rank length requested with extra dashes (`---->`). */\n length: number;\n classes: string[];\n styles: string[];\n animate?: boolean;\n animation?: \"fast\" | \"slow\";\n /** True when the id was written by the user (`A e1@--> B`). */\n userDefinedId: boolean;\n data?: Record<string, unknown>;\n}\n\n/** Normalised, family-independent view of a diagram. */\nexport interface DiagramGraph {\n direction: string;\n nodes: GraphNode[];\n edges: GraphEdge[];\n groups: GraphGroup[];\n /** `classDef` declarations keyed by class name. */\n classDefs: Record<string, string[]>;\n title?: string;\n /**\n * Diagram-level, family-specific settings that are not nodes or edges\n * (pie `showData`, quadrant axis labels, gantt `dateFormat`…). Edited with\n * `setGraphData`; the keys are documented per family in `FamilyUI.diagramFields`.\n */\n data?: Record<string, unknown>;\n}\n\n/** Canvas state that Mermaid itself does not model. Persisted as metadata. */\nexport interface DiagramMeta {\n /** Schema version of the metadata payload. */\n v: 1;\n /** Node / group positions keyed by id. */\n layout?: Record<string, Point>;\n /** Explicit node sizes for resized nodes/groups. */\n sizes?: Record<string, readonly [number, number]>;\n /** Viewport the user last looked at. */\n viewport?: { x: number; y: number; zoom: number };\n /** Free-form extension slot for host applications. */\n ext?: Record<string, unknown>;\n}\n\n/** Where metadata lives in the source. */\nexport type MetaCarrier = \"comment\" | \"frontmatter\";\n\n/**\n * Lines that precede the diagram header: YAML frontmatter, `%%{init}%%`\n * directives, comments and blank lines. Kept verbatim.\n */\nexport interface Preamble {\n /** Raw lines, including the `---` fences of any frontmatter. */\n lines: string[];\n /** Parsed frontmatter when present (title/config/pixerate…). */\n frontmatter?: Record<string, unknown>;\n}\n\n/** A parse problem reported by the diagram parser or by Mermaid. */\nexport interface Diagnostic {\n severity: \"error\" | \"warning\" | \"info\";\n message: string;\n /** 1-based line in the source, when known. */\n line?: number;\n column?: number;\n source: \"pixerate\" | \"mermaid\";\n}\n\n/**\n * The parsed document. Family adapters extend `body` with their own statement\n * list; consumers that only need the graph never look at it.\n */\nexport interface MermaidDocument<TBody = unknown> {\n family: DiagramFamily;\n tier: SupportTier;\n /** Source as given to {@link parseMermaid}. */\n source: string;\n preamble: Preamble;\n /** The header line (`flowchart LR`, `stateDiagram-v2`, …) exactly as written. */\n header: string;\n /** Trailing newline / whitespace after the last statement, kept for fidelity. */\n trailing: string;\n /** End-of-line sequence detected in the source. */\n eol: \"\\n\" | \"\\r\\n\";\n graph: DiagramGraph;\n meta: DiagramMeta;\n metaCarrier: MetaCarrier;\n body: TBody;\n diagnostics: Diagnostic[];\n /** True once any edit has been applied to this document. */\n dirty: boolean;\n}\n\n/** Directions understood by flowchart / state / class / ER. */\nexport const DIRECTIONS = [\"TB\", \"TD\", \"BT\", \"LR\", \"RL\"] as const;\nexport type Direction = (typeof DIRECTIONS)[number];\n\n// ---------------------------------------------------------------------------\n// Edit operations\n// ---------------------------------------------------------------------------\n\nexport type EditOp =\n | { type: \"addNode\"; node: Partial<GraphNode> & { id: string }; position?: Point }\n | { type: \"removeNodes\"; ids: string[] }\n | { type: \"renameNode\"; id: string; label: string }\n | { type: \"setNodeShape\"; id: string; shape: string }\n | { type: \"setNodeClasses\"; id: string; classes: string[] }\n | { type: \"setNodeStyles\"; id: string; styles: string[] }\n | { type: \"setNodeLink\"; id: string; link?: string; target?: string; tooltip?: string }\n | { type: \"setNodeData\"; id: string; data: Record<string, unknown> }\n | { type: \"moveNodes\"; moves: { id: string; position: Point }[] }\n | { type: \"resizeNode\"; id: string; size: readonly [number, number] | undefined }\n | { type: \"addEdge\"; edge: Partial<GraphEdge> & { source: string; target: string } }\n | { type: \"removeEdges\"; ids: string[] }\n | { type: \"setEdgeLabel\"; id: string; label: string }\n /** Family-specific edge payload (sequence activation, message order, gantt dependency type…). */\n | { type: \"setEdgeData\"; id: string; data: Record<string, unknown> }\n | {\n type: \"setEdgeStyle\";\n id: string;\n pattern?: EdgePattern;\n arrowStart?: ArrowHead;\n arrowEnd?: ArrowHead;\n length?: number;\n styles?: string[];\n }\n | { type: \"reconnectEdge\"; id: string; source: string; target: string }\n | { type: \"groupNodes\"; groupId: string; label: string; nodeIds: string[]; parentId?: string }\n | { type: \"ungroup\"; groupId: string }\n | { type: \"renameGroup\"; id: string; label: string }\n | { type: \"setGroupDirection\"; id: string; direction?: string }\n | { type: \"setNodeParent\"; ids: string[]; parentId?: string }\n | { type: \"setDirection\"; direction: string }\n | { type: \"setClassDef\"; name: string; styles: string[] | undefined }\n | { type: \"setTitle\"; title: string | undefined }\n /** Merge diagram-level settings into `graph.data` (see {@link DiagramGraph.data}). */\n | { type: \"setGraphData\"; data: Record<string, unknown> }\n | { type: \"setViewport\"; viewport: DiagramMeta[\"viewport\"] }\n | { type: \"setMetaExt\"; key: string; value: unknown }\n | { type: \"replaceSource\"; source: string };\n\n/** Family adapter contract. See `docs/architecture.md`. */\nexport interface DiagramAdapter<TBody = unknown> {\n family: DiagramFamily;\n tier: SupportTier;\n parse(source: string, context: AdapterParseContext): MermaidDocument<TBody>;\n serialize(doc: MermaidDocument<TBody>): string;\n apply(doc: MermaidDocument<TBody>, op: EditOp): MermaidDocument<TBody>;\n}\n\n/** Pre-split source handed to adapters so they do not repeat the work. */\nexport interface AdapterParseContext {\n source: string;\n eol: \"\\n\" | \"\\r\\n\";\n preamble: Preamble;\n header: string;\n /** Body lines after the header (metadata comments already removed). */\n bodyLines: string[];\n trailing: string;\n meta: DiagramMeta;\n metaCarrier: MetaCarrier;\n family: DiagramFamily;\n}\n","import type { DiagramFamily } from \"../types.js\";\nimport { detectFamilyFromHeader, splitSource } from \"./lines.js\";\n\n/** A Mermaid diagram found in some text by {@link findMermaidSources}. */\nexport interface FoundMermaid {\n /** The diagram's Mermaid text, without any surrounding code fence. */\n source: string;\n /** Its family (never `\"unknown\"`). */\n family: DiagramFamily;\n /** The frontmatter `title`, else the Markdown heading above its code fence. */\n title?: string;\n}\n\n/** Fence info strings that mark a Mermaid block. */\nconst MERMAID_INFO = /^(mermaid|mmd)\\b/i;\n\n/**\n * Finds the Mermaid diagrams in a piece of text: a `.mmd` file, a Markdown\n * document with ```` ```mermaid ```` blocks (GitHub, Notion and most AI\n * chats write them like this), or a diagram pasted with or without its fence.\n *\n * Fenced blocks are used when there are any: those marked `mermaid` / `mmd`,\n * and unmarked ones whose first line is a Mermaid header. Otherwise the whole\n * text is one diagram if it starts with one. Unmarked text must spell the\n * keyword as Mermaid does (`timeline`, not `Timeline`). Text that holds no\n * diagram of a known family returns `[]`.\n */\nexport function findMermaidSources(text: string): FoundMermaid[] {\n const clean = text.replace(/^\\uFEFF/, \"\");\n const blocks = fencedBlocks(clean);\n if (blocks.length) {\n const found: FoundMermaid[] = [];\n for (const block of blocks) {\n if (block.info && !MERMAID_INFO.test(block.info)) continue;\n const item = asDiagram(block.body, block.heading, !block.info);\n if (item) found.push(item);\n }\n if (found.length) return found;\n }\n const whole = asDiagram(clean.trim(), undefined, true);\n return whole ? [whole] : [];\n}\n\n/**\n * `strict` (unmarked text) also wants the keyword's own case, as Mermaid\n * writes it, so prose such as \"Timeline of events\" is not taken for a diagram.\n */\nfunction asDiagram(source: string, heading?: string, strict = false): FoundMermaid | undefined {\n if (!source.trim()) return undefined;\n const split = splitSource(source);\n const family = detectFamilyFromHeader(split.header);\n if (family === \"unknown\") return undefined;\n if (strict && !/^\\s*([a-z]|C4)/.test(split.header)) return undefined;\n const fmTitle = split.preamble.frontmatter?.title;\n const title = typeof fmTitle === \"string\" && fmTitle.trim() ? fmTitle.trim() : heading;\n return { source: source.trimEnd() + \"\\n\", family, ...(title ? { title } : {}) };\n}\n\ninterface FencedBlock {\n info: string;\n body: string;\n /** The last Markdown heading before the fence. */\n heading?: string;\n}\n\n/** CommonMark-style fenced code blocks (``` or ~~~, three or more, indented up to three spaces). */\nfunction fencedBlocks(text: string): FencedBlock[] {\n const lines = text.split(/\\r\\n|\\n/);\n const blocks: FencedBlock[] = [];\n let heading: string | undefined;\n for (let i = 0; i < lines.length; i++) {\n // Patterns kept free of overlapping runs, so long lines cannot make them backtrack.\n const open = /^ {0,3}(`{3,}(?!`)|~{3,}(?!~))([^`]*)$/.exec(lines[i]!);\n if (!open) {\n const h = headingText(lines[i]!);\n if (h) heading = h;\n continue;\n }\n const fence = open[1]!;\n const close = new RegExp(`^ {0,3}${fence[0] === \"`\" ? \"`\" : \"~\"}{${fence.length},}$`);\n const body: string[] = [];\n let j = i + 1;\n while (j < lines.length && !close.test(lines[j]!.trimEnd())) body.push(lines[j++]!);\n blocks.push({ info: open[2]!.trim(), body: body.join(\"\\n\"), heading });\n i = j;\n }\n return blocks;\n}\n\n/** The text of an ATX heading (`## Title ##`), without its markers. */\nfunction headingText(line: string): string | undefined {\n const m = /^ {0,3}#{1,6}([ \\t].*)$/.exec(line);\n if (!m) return undefined;\n let text = m[1]!.trim();\n // A closing run of `#` counts only after a space (or when it is all there is).\n let end = text.length;\n while (end > 0 && text[end - 1] === \"#\") end--;\n if (end === 0 || text[end - 1] === \" \" || text[end - 1] === \"\\t\") text = text.slice(0, end).trim();\n return text || undefined;\n}\n","import type { FamilyUI } from \"./types.js\";\n\nexport const pieUI: FamilyUI = {\n family: \"pie\",\n title: \"Pie chart\",\n shapes: [{ value: \"slice\", label: \"Slice\" }],\n edgeKinds: [],\n patterns: [],\n arrowHeads: [],\n groups: { supported: false, label: \"Group\" },\n labels: true,\n nodeNoun: \"Slice\",\n nextId: (g) => `slice${g.nodes.length + 1}`,\n canDetach: false,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [{ key: \"value\", label: \"Value\", type: \"number\", min: 0 }],\n diagramFields: [{ key: \"showData\", label: \"Show values in legend\", type: \"boolean\" }],\n newNode: () => ({ label: \"New slice\", data: { value: 10 } }),\n};\n","import type { FamilyUI } from \"./types.js\";\n\n/**\n * Mind map vocabulary. Topics form a tree: \"Add\" attaches the new topic under\n * the selected one (`hierarchy`), and dragging a topic onto another topic\n * moves it (with its subtree) under that topic. The canvas draws Mermaid's own\n * picture (`canvases/mindmap.tsx`) with positions derived from the tree, so\n * there is no \"Arrange\", direction or connection handle. Branches cannot be\n * deleted on their own; delete the child topic instead.\n */\nexport const mindmapUI: FamilyUI = {\n family: \"mindmap\",\n title: \"Mind map\",\n shapes: [\n { value: \"text\", label: \"Topic\" },\n { value: \"rounded\", label: \"Rounded\" },\n { value: \"rect\", label: \"Square\" },\n { value: \"circle\", label: \"Circle\" },\n { value: \"odd\", label: \"Bang\" },\n { value: \"stadium\", label: \"Cloud\" },\n { value: \"hex\", label: \"Hexagon\" },\n ],\n edgeKinds: [\n { value: \"branch\", label: \"Branch\", arrowStart: \"none\", arrowEnd: \"none\", pattern: \"solid\" },\n ],\n patterns: [{ value: \"solid\", label: \"Solid\" }],\n arrowHeads: [{ value: \"none\", label: \"None\" }],\n groups: { supported: false, label: \"Group\" },\n labels: true,\n nodeNoun: \"Topic\",\n edgeNoun: \"Branch\",\n nextId: (g) => `topic${g.nodes.length + 1}`,\n canDetach: false,\n hierarchy: true,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [{ key: \"icon\", label: \"Icon\", type: \"text\", placeholder: \"fa fa-book\" }],\n newNode: () => ({ label: \"New topic\" }),\n};\n","import type { FamilyUI } from \"./types.js\";\n\n/**\n * Quadrant chart vocabulary. The canvas draws Mermaid's chart; points sit\n * where their coordinates put them (dragging one rewrites its `[x, y]`, and\n * its quadrant follows), the four quadrants are fixed (renamable on the\n * canvas, not movable) and there are no edges.\n */\nexport const quadrantUI: FamilyUI = {\n family: \"quadrant\",\n title: \"Quadrant chart\",\n shapes: [{ value: \"point\", label: \"Point\" }],\n edgeKinds: [],\n patterns: [],\n arrowHeads: [],\n groups: { supported: false, label: \"Quadrant\" },\n labels: true,\n nodeNoun: \"Point\",\n nextId: (g) => `point${g.nodes.length + 1}`,\n canDetach: false,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [\n { key: \"x\", label: \"X\", type: \"number\", min: 0, max: 1, step: 0.05 },\n { key: \"y\", label: \"Y\", type: \"number\", min: 0, max: 1, step: 0.05 },\n { key: \"radius\", label: \"Radius\", type: \"number\", min: 1 },\n { key: \"color\", label: \"Colour\", type: \"text\", placeholder: \"#ff9900\" },\n ],\n diagramFields: [\n { key: \"xAxisLeft\", label: \"X axis (left)\", type: \"text\" },\n { key: \"xAxisRight\", label: \"X axis (right)\", type: \"text\" },\n { key: \"yAxisBottom\", label: \"Y axis (bottom)\", type: \"text\" },\n { key: \"yAxisTop\", label: \"Y axis (top)\", type: \"text\" },\n ],\n newNode: () => ({ label: \"New point\", data: { x: 0.5, y: 0.5 } }),\n};\n","import type { FamilyUI } from \"./types.js\";\n\n/** Toolbar / inspector vocabulary for C4 diagrams (all five C4 types). */\nexport const c4UI: FamilyUI = {\n family: \"c4\",\n title: \"C4 diagram\",\n shapes: [\n { value: \"c4-person\", label: \"Person\" },\n { value: \"c4-person-ext\", label: \"External person\" },\n { value: \"c4-system\", label: \"System\" },\n { value: \"c4-system-ext\", label: \"External system\" },\n { value: \"c4-container\", label: \"Container\" },\n { value: \"c4-container-ext\", label: \"External container\" },\n { value: \"c4-component\", label: \"Component\" },\n { value: \"c4-component-ext\", label: \"External component\" },\n { value: \"c4-db\", label: \"Database\" },\n { value: \"c4-queue\", label: \"Queue\" },\n ],\n edgeKinds: [\n {\n value: \"Rel\",\n label: \"Relation\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n {\n value: \"BiRel\",\n label: \"Bidirectional\",\n arrowStart: \"arrow_point\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n {\n value: \"Rel_Back\",\n label: \"Reverse\",\n arrowStart: \"arrow_point\",\n arrowEnd: \"none\",\n pattern: \"solid\",\n },\n ],\n patterns: [{ value: \"solid\", label: \"Solid\" }],\n arrowHeads: [\n { value: \"none\", label: \"None\" },\n { value: \"arrow_point\", label: \"Arrow\" },\n ],\n groups: { supported: true, label: \"Boundary\" },\n labels: true,\n nodeNoun: \"Element\",\n edgeNoun: \"Relation\",\n nextId: (g) => {\n const taken = new Set([...g.nodes.map((n) => n.id), ...g.groups.map((x) => x.id)]);\n let i = g.nodes.length + 1;\n while (taken.has(`el${i}`)) i++;\n return `el${i}`;\n },\n canDetach: true,\n styling: false,\n directions: false,\n // Positions are Mermaid's own layout (plus where the user dragged): \"Arrange\"\n // would pin a dagre layout Mermaid never draws.\n // \"Tidy up\" resets dragged positions to Mermaid's own layout (the registered family layout).\n arrange: true,\n preview: false,\n connectable: true,\n nodeFields: [\n { key: \"tech\", label: \"Technology\", type: \"text\" },\n { key: \"descr\", label: \"Description\", type: \"text\" },\n ],\n edgeFields: [{ key: \"tech\", label: \"Technology\", type: \"text\" }],\n newNode: () => ({ label: \"New element\" }),\n};\n","import type { FamilyUI } from \"./types.js\";\n\nconst SIDE_OPTIONS = [\n { value: \"L\", label: \"Left\" },\n { value: \"R\", label: \"Right\" },\n { value: \"T\", label: \"Top\" },\n { value: \"B\", label: \"Bottom\" },\n];\n\n/** Toolbar / inspector vocabulary for `architecture-beta` diagrams. */\nexport const architectureUI: FamilyUI = {\n family: \"architecture\",\n title: \"Architecture diagram\",\n shapes: [\n { value: \"arch-service\", label: \"Service\" },\n { value: \"sm-circ\", label: \"Junction\" },\n ],\n edgeKinds: [\n { value: \"--\", label: \"Line\", arrowStart: \"none\", arrowEnd: \"none\", pattern: \"solid\" },\n { value: \"-->\", label: \"Arrow\", arrowStart: \"none\", arrowEnd: \"arrow_point\", pattern: \"solid\" },\n {\n value: \"<-->\",\n label: \"Bidirectional\",\n arrowStart: \"arrow_point\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n ],\n patterns: [{ value: \"solid\", label: \"Solid\" }],\n arrowHeads: [\n { value: \"none\", label: \"None\" },\n { value: \"arrow_point\", label: \"Arrow\" },\n ],\n groups: { supported: true, label: \"Group\" },\n labels: true,\n nodeNoun: \"Service\",\n edgeNoun: \"Connection\",\n nextId: (g) => {\n const taken = new Set([...g.nodes.map((n) => n.id), ...g.groups.map((x) => x.id)]);\n let i = g.nodes.length + 1;\n while (taken.has(`svc${i}`)) i++;\n return `svc${i}`;\n },\n canDetach: true,\n styling: false,\n directions: false,\n // Positions are Mermaid's own layout (plus where the user dragged): \"Arrange\"\n // would pin a dagre layout Mermaid never draws.\n // \"Tidy up\" resets dragged positions to Mermaid's own layout (the registered family layout).\n arrange: true,\n preview: false,\n connectable: true,\n nodeFields: [\n {\n key: \"icon\",\n label: \"Icon\",\n type: \"select\",\n options: [\"cloud\", \"database\", \"disk\", \"internet\", \"server\"].map((v) => ({\n value: v,\n label: v,\n })),\n },\n ],\n edgeFields: [\n { key: \"sourceSide\", label: \"From side\", type: \"select\", options: SIDE_OPTIONS },\n { key: \"targetSide\", label: \"To side\", type: \"select\", options: SIDE_OPTIONS },\n ],\n newNode: () => ({ label: \"New service\", data: { icon: \"server\" } }),\n};\n","import type { DiagramGraph } from \"../types.js\";\nimport type { EdgeKindOption, FamilyUI } from \"./types.js\";\n\n/** Suggests `P1`, `P2`… skipping ids already used by participants or boxes. */\nfunction nextParticipantId(graph: DiagramGraph): string {\n const taken = new Set([...graph.nodes.map((n) => n.id), ...graph.groups.map((g) => g.id)]);\n let i = 1;\n while (taken.has(`P${i}`)) i++;\n return `P${i}`;\n}\n\nconst kind = (\n value: string,\n label: string,\n pattern: EdgeKindOption[\"pattern\"],\n arrowEnd: EdgeKindOption[\"arrowEnd\"],\n arrowStart: EdgeKindOption[\"arrowStart\"] = \"none\",\n): EdgeKindOption => ({ value, label, pattern, arrowStart, arrowEnd });\n\n/** Toolbar / inspector vocabulary for `sequenceDiagram`. */\nexport const sequenceUI: FamilyUI = {\n family: \"sequence\",\n title: \"Sequence diagram\",\n shapes: [\n { value: \"rect\", label: \"Participant\" },\n { value: \"actor\", label: \"Actor\" },\n ],\n edgeKinds: [\n kind(\"->>\", \"Sync\", \"solid\", \"arrow_point\"),\n kind(\"-->>\", \"Reply\", \"dotted\", \"arrow_point\"),\n kind(\"->\", \"Line\", \"solid\", \"none\"),\n kind(\"-->\", \"Dotted line\", \"dotted\", \"none\"),\n kind(\"-x\", \"Lost\", \"solid\", \"arrow_cross\"),\n kind(\"--x\", \"Dotted lost\", \"dotted\", \"arrow_cross\"),\n kind(\"-)\", \"Async\", \"solid\", \"arrow_open\"),\n kind(\"--)\", \"Dotted async\", \"dotted\", \"arrow_open\"),\n kind(\"<<->>\", \"Bidirectional\", \"solid\", \"arrow_point\", \"arrow_point\"),\n ],\n patterns: [\n { value: \"solid\", label: \"Solid\" },\n { value: \"dotted\", label: \"Dotted\" },\n ],\n arrowHeads: [\n { value: \"none\", label: \"None\" },\n { value: \"arrow_point\", label: \"Arrow\" },\n { value: \"arrow_cross\", label: \"Cross\" },\n { value: \"arrow_open\", label: \"Open\" },\n ],\n groups: { supported: true, label: \"Box\" },\n labels: true,\n nodeNoun: \"Participant\",\n edgeNoun: \"Message\",\n nextId: nextParticipantId,\n canDetach: true,\n styling: false,\n directions: false,\n arrange: false,\n endpoints: true,\n preview: false,\n edgeFields: [\n { key: \"activate\", label: \"Activate target\", type: \"boolean\" },\n { key: \"deactivate\", label: \"Deactivate source\", type: \"boolean\" },\n { key: \"index\", label: \"Position (0 = first)\", type: \"number\", min: 0 },\n ],\n diagramFields: [{ key: \"autonumber\", label: \"Number messages\", type: \"boolean\" }],\n newNode: () => ({ label: \"New participant\" }),\n};\n","import type { DiagramGraph } from \"../types.js\";\nimport type { FamilyUI } from \"./types.js\";\n\n/** Suggests `commit1`, `commit2`… skipping ids already in use. */\nfunction nextCommitId(graph: DiagramGraph): string {\n const taken = new Set([...graph.nodes.map((n) => n.id), ...graph.groups.map((g) => g.id)]);\n let i = graph.nodes.length + 1;\n while (taken.has(`commit${i}`)) i++;\n return `commit${i}`;\n}\n\n/**\n * Toolbar / inspector vocabulary for `gitGraph`. Branches are groups: \"Group\"\n * starts a new branch at the end of the history (selected commits are not\n * moved), and connecting two branch tips appends a `merge`. The canvas draws\n * Mermaid's own picture (`canvases/gitgraph.tsx`): positions follow the commit\n * order, so there is no \"Arrange\"; the direction is the header's `LR`/`TB`/`BT`.\n */\nexport const gitGraphUI: FamilyUI = {\n family: \"gitGraph\",\n title: \"Git graph\",\n shapes: [{ value: \"commit\", label: \"Commit\" }],\n edgeKinds: [\n {\n value: \"parent\",\n label: \"Parent\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n ],\n patterns: [{ value: \"solid\", label: \"Solid\" }],\n arrowHeads: [{ value: \"arrow_point\", label: \"Arrow\" }],\n groups: { supported: true, label: \"Branch\" },\n labels: true,\n nodeNoun: \"Commit\",\n edgeNoun: \"Parent link\",\n nextId: nextCommitId,\n canDetach: false,\n styling: false,\n connectable: true,\n directions: true,\n groupDirections: false,\n arrange: false,\n preview: false,\n nodeFields: [\n { key: \"tag\", label: \"Tag\", type: \"text\" },\n {\n key: \"type\",\n label: \"Type\",\n type: \"select\",\n options: [\n { value: \"NORMAL\", label: \"Normal\" },\n { value: \"REVERSE\", label: \"Reverse\" },\n { value: \"HIGHLIGHT\", label: \"Highlight\" },\n ],\n },\n { key: \"msg\", label: \"Message\", type: \"text\" },\n ],\n newNode: () => ({ label: \"New commit\" }),\n};\n","import type { DiagramGraph } from \"../types.js\";\nimport type { FamilyUI } from \"./types.js\";\nimport { GANTT_TAGS, formatGanttDate } from \"../gantt/index.js\";\n\nfunction nextTaskId(graph: DiagramGraph): string {\n const taken = new Set([...graph.nodes.map((n) => n.id), ...graph.groups.map((g) => g.id)]);\n let i = graph.nodes.length + 1;\n while (taken.has(`task${i}`)) i++;\n return `task${i}`;\n}\n\n/**\n * Start date for a new task: right after the latest computed end, else the\n * earliest written start, else `2026-01-01`.\n */\nfunction newTaskStart(graph: DiagramGraph): string {\n const format = typeof graph.data?.dateFormat === \"string\" ? graph.data.dateFormat : undefined;\n const ends = graph.nodes\n .map((n) => n.data?.endTime)\n .filter((t): t is number => typeof t === \"number\" && Number.isFinite(t));\n if (ends.length) return formatGanttDate(Math.max(...ends), format);\n const starts = graph.nodes\n .map((n) => n.data?.startTime)\n .filter((t): t is number => typeof t === \"number\" && Number.isFinite(t));\n if (starts.length) return formatGanttDate(Math.min(...starts), format);\n return \"2026-01-01\";\n}\n\nexport const ganttUI: FamilyUI = {\n family: \"gantt\",\n title: \"Gantt chart\",\n shapes: [\n { value: \"task\", label: \"Task\" },\n { value: \"milestone\", label: \"Milestone\" },\n ],\n edgeKinds: [\n {\n value: \"depends\",\n label: \"Depends on\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n ],\n patterns: [],\n arrowHeads: [],\n groups: { supported: true, label: \"Section\" },\n labels: true,\n nodeNoun: \"Task\",\n edgeNoun: \"Dependency\",\n nextId: nextTaskId,\n canDetach: true,\n styling: false,\n // Connecting A → B writes `after A` into B's start.\n connectable: true,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [\n { key: \"start\", label: \"Start\", type: \"text\", placeholder: \"2026-10-01 or after ds\" },\n { key: \"end\", label: \"End or duration\", type: \"text\", placeholder: \"14d\" },\n {\n key: \"tags\",\n label: \"Tags\",\n type: \"tags\",\n hint: \"done, active, crit, milestone, vert\",\n options: GANTT_TAGS.map((t) => ({ value: t, label: t })),\n },\n { key: \"id\", label: \"Task id\", type: \"text\" },\n ],\n diagramFields: [\n { key: \"dateFormat\", label: \"Date format\", type: \"text\", placeholder: \"YYYY-MM-DD\" },\n { key: \"axisFormat\", label: \"Axis format\", type: \"text\", placeholder: \"%b %d\" },\n { key: \"excludes\", label: \"Excludes\", type: \"text\", placeholder: \"weekends\" },\n { key: \"tickInterval\", label: \"Tick interval\", type: \"text\", placeholder: \"1week\" },\n { key: \"todayMarker\", label: \"Today marker\", type: \"text\", placeholder: \"off\" },\n ],\n newNode: (graph) => ({\n label: \"New task\",\n shape: \"task\",\n data: { start: newTaskStart(graph), end: \"7d\" },\n }),\n};\n","import type { DiagramGraph } from \"../types.js\";\nimport type { FamilyUI } from \"./types.js\";\n\nfunction nextPeriodId(graph: DiagramGraph): string {\n const taken = new Set([...graph.nodes.map((n) => n.id), ...graph.groups.map((g) => g.id)]);\n let i = graph.nodes.length + 1;\n while (taken.has(`period${i}`)) i++;\n return `period${i}`;\n}\n\nexport const timelineUI: FamilyUI = {\n family: \"timeline\",\n title: \"Timeline\",\n shapes: [{ value: \"rounded\", label: \"Period\" }],\n edgeKinds: [],\n patterns: [],\n arrowHeads: [],\n groups: { supported: true, label: \"Section\" },\n labels: true,\n nodeNoun: \"Period\",\n nextId: nextPeriodId,\n canDetach: true,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [{ key: \"events\", label: \"Events (one per line)\", type: \"lines\" }],\n newNode: () => ({ label: \"New period\", shape: \"rounded\", data: { events: [\"Event\"] } }),\n};\n","import type { DiagramGraph } from \"../types.js\";\nimport type { FamilyUI } from \"./types.js\";\n\nfunction nextTaskId(graph: DiagramGraph): string {\n const taken = new Set([...graph.nodes.map((n) => n.id), ...graph.groups.map((g) => g.id)]);\n let i = graph.nodes.length + 1;\n while (taken.has(`task${i}`)) i++;\n return `task${i}`;\n}\n\nexport const journeyUI: FamilyUI = {\n family: \"journey\",\n title: \"User journey\",\n shapes: [{ value: \"rect\", label: \"Task\" }],\n edgeKinds: [],\n patterns: [],\n arrowHeads: [],\n groups: { supported: true, label: \"Section\" },\n labels: true,\n nodeNoun: \"Task\",\n nextId: nextTaskId,\n canDetach: true,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [\n { key: \"score\", label: \"Score\", type: \"number\", min: 1, max: 5, step: 1 },\n { key: \"actors\", label: \"Actors\", type: \"tags\" },\n ],\n newNode: () => ({ label: \"New task\", shape: \"rect\", data: { score: 3, actors: [] } }),\n};\n","import type { FamilyUI } from \"./types.js\";\n\nexport const xyChartUI: FamilyUI = {\n family: \"xychart\",\n title: \"XY chart\",\n shapes: [\n { value: \"bar\", label: \"Bar\" },\n { value: \"line\", label: \"Line\" },\n ],\n edgeKinds: [],\n patterns: [],\n arrowHeads: [],\n groups: { supported: false, label: \"Group\" },\n labels: true,\n nodeNoun: \"Series\",\n nextId: (g) => `series${g.nodes.length + 1}`,\n canDetach: false,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [\n { key: \"values\", label: \"Values\", type: \"numbers\", hint: \"One per x-axis category\" },\n ],\n diagramFields: [\n { key: \"categories\", label: \"X-axis categories\", type: \"lines\", placeholder: \"One per line\" },\n { key: \"xTitle\", label: \"X-axis title\", type: \"text\" },\n { key: \"yTitle\", label: \"Y-axis title\", type: \"text\" },\n { key: \"yMin\", label: \"Y-axis min\", type: \"number\" },\n { key: \"yMax\", label: \"Y-axis max\", type: \"number\" },\n { key: \"horizontal\", label: \"Horizontal\", type: \"boolean\" },\n ],\n newNode: () => ({ label: \"New series\", shape: \"bar\" }),\n};\n","import type { FamilyUI } from \"./types.js\";\n\nexport const radarUI: FamilyUI = {\n family: \"radar\",\n title: \"Radar chart\",\n shapes: [{ value: \"curve\", label: \"Curve\" }],\n edgeKinds: [],\n patterns: [],\n arrowHeads: [],\n groups: { supported: false, label: \"Group\" },\n labels: true,\n nodeNoun: \"Curve\",\n nextId: (g) => {\n const taken = new Set(g.nodes.map((n) => n.id));\n let i = g.nodes.length + 1;\n while (taken.has(`curve${i}`)) i++;\n return `curve${i}`;\n },\n canDetach: false,\n styling: false,\n connectable: false,\n directions: false,\n arrange: false,\n preview: false,\n nodeFields: [{ key: \"values\", label: \"Values\", type: \"numbers\", hint: \"One per axis\" }],\n diagramFields: [\n { key: \"axes\", label: \"Axes\", type: \"lines\", placeholder: \"One per line\" },\n { key: \"max\", label: \"Max\", type: \"number\" },\n { key: \"min\", label: \"Min\", type: \"number\" },\n { key: \"ticks\", label: \"Ticks\", type: \"number\", min: 1 },\n {\n key: \"graticule\",\n label: \"Grid\",\n type: \"select\",\n options: [\n { value: \"circle\", label: \"Circles\" },\n { value: \"polygon\", label: \"Polygons\" },\n ],\n },\n { key: \"showLegend\", label: \"Show legend\", type: \"boolean\" },\n ],\n newNode: () => ({ label: \"New curve\" }),\n};\n","import type { FieldSpec } from \"./types.js\";\n\n/** A field as agents and hosts see it: what can be written and how. */\nexport interface FieldDescription {\n key: string;\n label: string;\n type: FieldSpec[\"type\"];\n options?: string[];\n min?: number;\n max?: number;\n hint?: string;\n /** Only on nodes of these shapes. */\n shapes?: string[];\n}\n\n/** {@link FieldSpec}s without the inspector-only details (placeholders, steps, option labels). */\nexport function describeFields(fields: readonly FieldSpec[] | undefined): FieldDescription[] {\n return (fields ?? []).map((f) => ({\n key: f.key,\n label: f.label,\n type: f.type,\n ...(f.options ? { options: f.options.map((o) => o.value) } : {}),\n ...(f.min !== undefined ? { min: f.min } : {}),\n ...(f.max !== undefined ? { max: f.max } : {}),\n ...(f.hint ? { hint: f.hint } : {}),\n ...(f.shapes ? { shapes: f.shapes } : {}),\n }));\n}\n\n/** The values of `data` that `fields` declare (what a reader needs, without parser internals). */\nexport function pickFieldData(\n fields: readonly FieldSpec[] | undefined,\n data: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const f of fields ?? []) {\n const value = data?.[f.key];\n if (value !== undefined && value !== null && !(Array.isArray(value) && !value.length)) out[f.key] = value;\n }\n return out;\n}\n\nconst list = (items: readonly string[]) => items.map((s) => `\"${s}\"`).join(\", \");\n\n/**\n * Checks a data patch against a family's fields and returns it as the adapters expect it\n * (`null` or `\"\"` clears a field, so it becomes `undefined`). Throws a sentence naming the\n * problem and the allowed values when a key is unknown, does not apply to `shape`, or a\n * value has the wrong type or is out of range. Nothing is changed when it throws.\n *\n * Value types: `text` and `select` take a string, `number` a number, `boolean` a boolean,\n * `numbers` an array of numbers, `lines` and `tags` an array of strings.\n */\nexport function checkFieldData(\n fields: readonly FieldSpec[] | undefined,\n data: Record<string, unknown>,\n what: string,\n shape?: string,\n): Record<string, unknown> {\n const all = fields ?? [];\n const usable = all.filter((f) => !f.shapes || shape === undefined || f.shapes.includes(shape));\n if (!all.length) throw new Error(`${what} has no data fields to set.`);\n const keys = Object.keys(data);\n if (!keys.length) throw new Error(`Give at least one field to set; ${what} has ${list(usable.map((f) => f.key))}.`);\n const out: Record<string, unknown> = {};\n for (const key of keys) {\n const field = all.find((f) => f.key === key);\n if (!field) throw new Error(`Unknown field \"${key}\" for ${what}; use one of ${list(usable.map((f) => f.key))}.`);\n if (!usable.includes(field))\n throw new Error(`Field \"${key}\" only applies to ${list(field.shapes!)} nodes, not \"${shape}\".`);\n const value = data[key];\n if (value === null || value === \"\") {\n out[key] = undefined;\n continue;\n }\n const bad = (expected: string) => new Error(`Field \"${key}\" (${field.label}) takes ${expected}; got ${JSON.stringify(value)}.`);\n switch (field.type) {\n case \"number\": {\n if (typeof value !== \"number\" || !Number.isFinite(value)) throw bad(\"a number\");\n if (field.min !== undefined && value < field.min) throw bad(`a number of at least ${field.min}`);\n if (field.max !== undefined && value > field.max) throw bad(`a number of at most ${field.max}`);\n break;\n }\n case \"numbers\":\n if (!Array.isArray(value) || !value.every((n) => typeof n === \"number\" && Number.isFinite(n))) throw bad(\"an array of numbers\");\n break;\n case \"lines\":\n case \"tags\":\n if (!Array.isArray(value) || !value.every((s) => typeof s === \"string\")) throw bad(\"an array of strings\");\n break;\n case \"boolean\":\n if (typeof value !== \"boolean\") throw bad(\"true or false\");\n break;\n case \"select\": {\n const options = (field.options ?? []).map((o) => o.value);\n if (typeof value !== \"string\" || (options.length && !options.includes(value))) throw bad(`one of ${list(options)}`);\n break;\n }\n default:\n if (typeof value !== \"string\") throw bad(\"a string\");\n }\n out[key] = value;\n }\n return out;\n}\n","import { FLOWCHART_SHAPES } from \"../flowchart/shapes.js\";\nimport { CLASS_RELATIONS } from \"../lines-grammar/class.js\";\nimport { ER_CARDINALITIES } from \"../lines-grammar/er.js\";\nimport type { ArrowHead, DiagramFamily, DiagramGraph } from \"../types.js\";\nimport type { FamilyUI, FieldSpec } from \"./types.js\";\nimport { pieUI } from \"./pie.js\";\nimport { mindmapUI } from \"./mindmap.js\";\nimport { quadrantUI } from \"./quadrant.js\";\nimport { c4UI } from \"./c4.js\";\nimport { architectureUI } from \"./architecture.js\";\nimport { sequenceUI } from \"./sequence.js\";\nimport { gitGraphUI } from \"./gitgraph.js\";\nimport { ganttUI } from \"./gantt.js\";\nimport { timelineUI } from \"./timeline.js\";\nimport { journeyUI } from \"./journey.js\";\nimport { xyChartUI } from \"./xychart.js\";\nimport { radarUI } from \"./radar.js\";\n\n/**\n * Per-family vocabulary: the shapes, edge kinds and data fields each family offers, its\n * starter sources and titles. Inspectors and toolbars build on it, and the diagram agent\n * tools check `setNodeData` / `setEdgeData` / `setGraphData` keys against its fields.\n * Framework-free data; ported from Ursula's `src/react/families.ts`.\n *\n * @module\n */\n\nexport * from \"./types.js\";\nexport { pieUI, mindmapUI, quadrantUI, c4UI, architectureUI, sequenceUI, gitGraphUI, ganttUI, timelineUI, journeyUI, xyChartUI, radarUI };\n\nfunction counterId(prefix: string, graph: DiagramGraph): string {\n const taken = new Set([...graph.nodes.map((n) => n.id), ...graph.groups.map((g) => g.id)]);\n let i = graph.nodes.length + 1;\n while (taken.has(`${prefix}${i}`)) i++;\n return `${prefix}${i}`;\n}\n\nconst FLOW_HEADS: { value: ArrowHead; label: string }[] = [\n { value: \"none\", label: \"None\" },\n { value: \"arrow_point\", label: \"Arrow\" },\n { value: \"arrow_circle\", label: \"Circle\" },\n { value: \"arrow_cross\", label: \"Cross\" },\n];\n\n/** Mermaid's image-node keys (`A@{ img: \"…\", pos, w, h, constraint }`), on image nodes only. */\nconst IMAGE_FIELDS: FieldSpec[] = [\n {\n key: \"img\",\n label: \"Image URL\",\n type: \"text\",\n placeholder: \"https://…/picture.png\",\n shapes: [\"image\"],\n },\n {\n key: \"pos\",\n label: \"Label\",\n type: \"select\",\n options: [\n { value: \"b\", label: \"Below\" },\n { value: \"t\", label: \"Above\" },\n ],\n shapes: [\"image\"],\n },\n { key: \"w\", label: \"Width\", type: \"number\", min: 8, step: 1, shapes: [\"image\"] },\n { key: \"h\", label: \"Height\", type: \"number\", min: 8, step: 1, shapes: [\"image\"] },\n {\n key: \"constraint\",\n label: \"Keep aspect ratio\",\n type: \"select\",\n options: [\n { value: \"on\", label: \"On\" },\n { value: \"off\", label: \"Off\" },\n ],\n shapes: [\"image\"],\n },\n];\n\nexport const flowchartUI: FamilyUI = {\n family: \"flowchart\",\n nodeFields: IMAGE_FIELDS,\n title: \"Flowchart\",\n shapes: FLOWCHART_SHAPES.map((s) => ({ value: s.name, label: s.title, category: s.category })),\n edgeKinds: [\n {\n value: \"arrow\",\n label: \"Arrow\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n { value: \"open\", label: \"Line\", arrowStart: \"none\", arrowEnd: \"none\", pattern: \"solid\" },\n {\n value: \"dotted\",\n label: \"Dotted arrow\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_point\",\n pattern: \"dotted\",\n },\n {\n value: \"thick\",\n label: \"Thick arrow\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_point\",\n pattern: \"thick\",\n },\n {\n value: \"bidir\",\n label: \"Bidirectional\",\n arrowStart: \"arrow_point\",\n arrowEnd: \"arrow_point\",\n pattern: \"solid\",\n },\n {\n value: \"invisible\",\n label: \"Invisible (layout)\",\n arrowStart: \"none\",\n arrowEnd: \"none\",\n pattern: \"invisible\",\n },\n ],\n patterns: [\n { value: \"solid\", label: \"Solid\" },\n { value: \"dotted\", label: \"Dotted\" },\n { value: \"thick\", label: \"Thick\" },\n { value: \"invisible\", label: \"Invisible\" },\n ],\n arrowHeads: FLOW_HEADS,\n groups: { supported: true, label: \"Subgraph\" },\n labels: true,\n nodeNoun: \"Node\",\n nextId: (g) => counterId(\"n\", g),\n canDetach: false,\n};\n\nexport const stateUI: FamilyUI = {\n family: \"state\",\n title: \"State diagram\",\n shapes: [\n { value: \"rect\", label: \"State\" },\n { value: \"choice\", label: \"Choice\" },\n { value: \"fork\", label: \"Fork\" },\n { value: \"join\", label: \"Join\" },\n ],\n edgeKinds: [\n {\n value: \"transition\",\n label: \"Transition\",\n arrowStart: \"none\",\n arrowEnd: \"arrow_barb\",\n pattern: \"solid\",\n },\n ],\n patterns: [{ value: \"solid\", label: \"Solid\" }],\n arrowHeads: [{ value: \"arrow_barb\", label: \"Arrow\" }],\n groups: { supported: true, label: \"Composite state\" },\n labels: true,\n nodeNoun: \"State\",\n nextId: (g) => counterId(\"State\", g),\n canDetach: true,\n};\n\nexport const classUI: FamilyUI = {\n family: \"class\",\n title: \"Class diagram\",\n shapes: [{ value: \"classBox\", label: \"Class\" }],\n edgeKinds: CLASS_RELATIONS.filter((r) => !r.title.includes(\"reversed\")).map((r) => ({\n value: r.token,\n label: r.title,\n arrowStart: r.arrowStart,\n arrowEnd: r.arrowEnd,\n pattern: r.pattern,\n })),\n patterns: [\n { value: \"solid\", label: \"Solid\" },\n { value: \"dotted\", label: \"Dashed\" },\n ],\n arrowHeads: [\n { value: \"none\", label: \"None\" },\n { value: \"extension\", label: \"Inheritance\" },\n { value: \"composition\", label: \"Composition\" },\n { value: \"aggregation\", label: \"Aggregation\" },\n { value: \"dependency\", label: \"Arrow\" },\n { value: \"lollipop\", label: \"Lollipop\" },\n ],\n groups: { supported: true, label: \"Namespace\" },\n labels: true,\n nodeNoun: \"Class\",\n nextId: (g) => counterId(\"Class\", g),\n canDetach: true,\n};\n\nexport const erUI: FamilyUI = {\n family: \"er\",\n title: \"Entity relationship\",\n shapes: [{ value: \"erBox\", label: \"Entity\" }],\n edgeKinds: [\n {\n value: \"one-many\",\n label: \"One to many\",\n arrowStart: \"only_one\",\n arrowEnd: \"zero_or_more\",\n pattern: \"solid\",\n },\n {\n value: \"one-one\",\n label: \"One to one\",\n arrowStart: \"only_one\",\n arrowEnd: \"only_one\",\n pattern: \"solid\",\n },\n {\n value: \"many-many\",\n label: \"Many to many\",\n arrowStart: \"zero_or_more\",\n arrowEnd: \"zero_or_more\",\n pattern: \"solid\",\n },\n {\n value: \"one-many-ni\",\n label: \"One to many (non-identifying)\",\n arrowStart: \"only_one\",\n arrowEnd: \"zero_or_more\",\n pattern: \"dotted\",\n },\n ],\n patterns: [\n { value: \"solid\", label: \"Identifying\" },\n { value: \"dotted\", label: \"Non-identifying\" },\n ],\n arrowHeads: Array.from(\n new Map(ER_CARDINALITIES.map((c) => [c.head, { value: c.head, label: c.title }])).values(),\n ),\n groups: { supported: false, label: \"Group\" },\n labels: true,\n nodeNoun: \"Entity\",\n nextId: (g) => counterId(\"ENTITY_\", g),\n canDetach: true,\n};\n\nconst FAMILY_UI: Partial<Record<DiagramFamily, FamilyUI>> = {\n flowchart: flowchartUI,\n state: stateUI,\n class: classUI,\n er: erUI,\n pie: pieUI,\n mindmap: mindmapUI,\n quadrant: quadrantUI,\n c4: c4UI,\n architecture: architectureUI,\n sequence: sequenceUI,\n gitGraph: gitGraphUI,\n gantt: ganttUI,\n timeline: timelineUI,\n journey: journeyUI,\n xychart: xyChartUI,\n radar: radarUI,\n};\n\nexport function familyUI(family: DiagramFamily): FamilyUI | null {\n return FAMILY_UI[family] ?? null;\n}\n\nexport const FAMILY_TITLES: Record<DiagramFamily, string> = {\n flowchart: \"Flowchart\",\n state: \"State diagram\",\n class: \"Class diagram\",\n er: \"Entity relationship\",\n requirement: \"Requirement diagram\",\n mindmap: \"Mind map\",\n sequence: \"Sequence diagram\",\n gantt: \"Gantt chart\",\n pie: \"Pie chart\",\n journey: \"User journey\",\n timeline: \"Timeline\",\n gitGraph: \"Git graph\",\n c4: \"C4 diagram\",\n quadrant: \"Quadrant chart\",\n sankey: \"Sankey diagram\",\n xychart: \"XY chart\",\n block: \"Block diagram\",\n architecture: \"Architecture diagram\",\n packet: \"Packet diagram\",\n kanban: \"Kanban board\",\n radar: \"Radar chart\",\n treemap: \"Treemap\",\n info: \"Info\",\n unknown: \"Unknown diagram\",\n};\n\n/** Starter sources for new documents, by family. */\nexport const FAMILY_TEMPLATES: Partial<Record<DiagramFamily, string>> = {\n flowchart:\n \"flowchart TD\\n A[Start] --> B{Decision}\\n B -->|Yes| C[Do it]\\n B -->|No| D[Skip]\\n C --> E[End]\\n D --> E\\n\",\n state:\n \"stateDiagram-v2\\n [*] --> Idle\\n Idle --> Active : start\\n Active --> Idle : stop\\n Active --> [*] : finish\\n\",\n class:\n 'classDiagram\\n class Account {\\n +String id\\n +deposit(amount)\\n }\\n class Customer\\n Customer \"1\" --> \"*\" Account : owns\\n',\n er: \"erDiagram\\n CUSTOMER ||--o{ ORDER : places\\n ORDER ||--|{ LINE_ITEM : contains\\n CUSTOMER {\\n string name PK\\n string email\\n }\\n\",\n sequence:\n \"sequenceDiagram\\n participant U as User\\n participant S as Service\\n U->>S: Request\\n S-->>U: Response\\n\",\n pie: 'pie showData\\n title Share by segment\\n \"Segment A\" : 40\\n \"Segment B\" : 35\\n \"Segment C\" : 25\\n',\n mindmap: \"mindmap\\n root((Central idea))\\n Topic A\\n Detail\\n Topic B\\n Topic C\\n\",\n timeline:\n \"timeline\\n title Milestones\\n 2024 : First release\\n 2025 : Second release\\n : Expansion\\n 2026 : Next chapter\\n\",\n journey:\n \"journey\\n title Customer journey\\n section Discover\\n Find the product: 3: Customer\\n section Buy\\n Check out: 4: Customer\\n Confirm order: 5: Customer, Support\\n\",\n gantt:\n \"gantt\\n title Plan\\n dateFormat YYYY-MM-DD\\n section Phase 1\\n Research :a1, 2026-01-05, 10d\\n Design :a2, after a1, 7d\\n section Phase 2\\n Build :a3, after a2, 14d\\n\",\n gitGraph:\n 'gitGraph\\n commit id: \"init\"\\n branch develop\\n checkout develop\\n commit id: \"feature\"\\n checkout main\\n merge develop tag: \"v1.0\"\\n',\n c4: 'C4Context\\n title System context\\n Person(user, \"User\", \"Uses the system\")\\n System(sys, \"System\", \"Does the work\")\\n System_Ext(ext, \"External service\", \"Provides data\")\\n Rel(user, sys, \"Uses\")\\n Rel(sys, ext, \"Calls\")\\n',\n architecture:\n \"architecture-beta\\n group app(cloud)[Application]\\n service web(server)[Web] in app\\n service db(database)[Database] in app\\n service edge(internet)[Edge]\\n edge:R --> L:web\\n web:B --> T:db\\n\",\n quadrant:\n \"quadrantChart\\n title Prioritisation\\n x-axis Low effort --> High effort\\n y-axis Low value --> High value\\n quadrant-1 Plan\\n quadrant-2 Do first\\n quadrant-3 Drop\\n quadrant-4 Fill-ins\\n Idea A: [0.3, 0.8]\\n Idea B: [0.7, 0.6]\\n Idea C: [0.2, 0.3]\\n\",\n xychart:\n 'xychart-beta\\n title \"Monthly sales\"\\n x-axis [Jan, Feb, Mar, Apr]\\n y-axis \"Units\" 0 --> 100\\n bar [42, 58, 71, 64]\\n line \"Target\" [50, 55, 60, 65]\\n',\n radar:\n 'radar-beta\\n title Option comparison\\n axis cost[\"Cost\"], speed[\"Speed\"], quality[\"Quality\"], risk[\"Risk\"]\\n curve a[\"Option A\"]{80, 60, 90, 40}\\n curve b[\"Option B\"]{50, 90, 60, 70}\\n max 100\\n',\n};\n\n/**\n * The smallest source for each family in {@link FAMILY_TEMPLATES}: an empty\n * canvas Mermaid still accepts (class diagrams need a statement, C4 a title).\n * For \"start blank\" flows where the user or an agent fills the diagram in.\n */\nexport const FAMILY_BLANKS: Partial<Record<DiagramFamily, string>> = {\n flowchart: \"flowchart TD\\n\",\n state: \"stateDiagram-v2\\n\",\n class: \"classDiagram\\n direction TB\\n\",\n er: \"erDiagram\\n\",\n sequence: \"sequenceDiagram\\n\",\n pie: \"pie\\n\",\n mindmap: \"mindmap\\n\",\n timeline: \"timeline\\n\",\n journey: \"journey\\n\",\n gantt: \"gantt\\n dateFormat YYYY-MM-DD\\n\",\n gitGraph: \"gitGraph\\n\",\n c4: \"C4Context\\n title System context\\n\",\n architecture: \"architecture-beta\\n\",\n quadrant: \"quadrantChart\\n\",\n xychart: \"xychart-beta\\n\",\n radar: \"radar-beta\\n\",\n};\n\n/** One line on what each family in {@link FAMILY_TEMPLATES} is for, for type pickers. */\nexport const FAMILY_DESCRIPTIONS: Partial<Record<DiagramFamily, string>> = {\n flowchart: \"Steps, decisions and the paths between them.\",\n state: \"The states something moves through and what triggers each change.\",\n class: \"Types with their fields and methods, and how they relate.\",\n er: \"Entities, their attributes and the relationships between them.\",\n sequence: \"Messages exchanged between participants over time.\",\n pie: \"Parts of a whole as slices.\",\n mindmap: \"Ideas branching out from a central topic.\",\n timeline: \"Events grouped by period, in order.\",\n journey: \"A user's tasks, section by section, scored by how they feel.\",\n gantt: \"Tasks on a calendar with durations and dependencies.\",\n gitGraph: \"Branches, commits and merges of a repository.\",\n c4: \"People and software systems, and how they interact.\",\n architecture: \"Services and infrastructure grouped by where they run.\",\n quadrant: \"Items placed on two axes in four quadrants.\",\n xychart: \"Bars and lines over categories or a numeric axis.\",\n radar: \"Scores across several axes, one curve per item compared.\",\n};\nexport * from \"./fields.js\";\n","import { splitSource } from \"../source/lines.js\";\n\n/**\n * Reading Mermaid `sankey-beta` / `sankey` diagrams into data, for hosts that\n * draw them with their own components (`familyViews`). Sankey diagrams stay\n * render-only: the text is kept verbatim and not edited through `EditOp`s.\n *\n * The grammar follows Mermaid's: after the header, one CSV record per line,\n * `source,target,value`. Fields may be double-quoted, which allows commas and\n * line breaks inside them and `\"\"` for a quote. Fields are trimmed and the\n * value is read with `parseFloat`. Blank lines and `%%` comment lines are\n * skipped. Unlike Mermaid, characters outside ASCII are accepted.\n *\n * @module\n */\n\nexport interface SankeyLink {\n source: string;\n target: string;\n value: number;\n}\n\nexport interface SankeyData {\n /** Node names in order of first appearance (a link's source, then its target). */\n nodes: string[];\n links: SankeyLink[];\n}\n\n/** What went wrong reading a diagram, with its 1-based line in the source. */\nexport interface DiagramDataError {\n error: string;\n line?: number;\n}\n\nexport type SankeyParse = { data: SankeyData } | DiagramDataError;\n\n/**\n * Reads a sankey diagram's links. `source` is the whole Mermaid text (with\n * frontmatter and header), e.g. a passthrough document's `doc.source`.\n * Returns an error for text Mermaid would reject (a record without exactly\n * three fields, an unterminated quote, no records) or that has no usable\n * number as its value.\n */\nexport function parseSankey(source: string): SankeyParse {\n const split = splitSource(source);\n if (!/^\\s*sankey(?:-beta)?\\b/i.test(split.header)) {\n return { error: \"Not a sankey diagram: the first line should be `sankey-beta`.\" };\n }\n const firstLine = split.headerIndex + 2;\n // Comment lines go first, as Mermaid strips them before parsing.\n const kept: { text: string; line: number }[] = [];\n split.bodyLines.forEach((text, i) => {\n if (/^\\s*%%/.test(text)) return;\n kept.push({ text: text.trim(), line: firstLine + i });\n });\n\n const records = readRecords(kept);\n if (\"error\" in records) return records;\n\n const nodes: string[] = [];\n const seen = new Set<string>();\n const add = (name: string) => {\n if (!seen.has(name)) {\n seen.add(name);\n nodes.push(name);\n }\n };\n const links: SankeyLink[] = [];\n for (const { fields, line } of records) {\n if (fields.length !== 3) {\n return {\n error: `Expected three fields (source,target,value), found ${fields.length}.`,\n line,\n };\n }\n const [source, target, raw] = fields as [string, string, string];\n const value = Number.parseFloat(raw);\n if (!Number.isFinite(value)) {\n return { error: `\"${raw}\" is not a number.`, line };\n }\n add(source);\n add(target);\n links.push({ source, target, value });\n }\n if (!links.length) return { error: \"No links: add lines like `A,B,10`.\" };\n return { data: { nodes, links } };\n}\n\ninterface CsvRecord {\n fields: string[];\n line: number;\n}\n\n/** Splits CSV records; a quoted field may run over several lines. */\nfunction readRecords(lines: { text: string; line: number }[]): CsvRecord[] | DiagramDataError {\n const records: CsvRecord[] = [];\n let i = 0;\n while (i < lines.length) {\n const start = lines[i]!;\n if (start.text === \"\") {\n i++;\n continue;\n }\n const fields: string[] = [];\n let text = start.text;\n let pos = 0;\n let field = \"\";\n let quoted = false;\n for (;;) {\n if (quoted) {\n const close = text.indexOf('\"', pos);\n if (close < 0) {\n // The quoted field carries on over the next line.\n field += text.slice(pos);\n i++;\n if (i >= lines.length)\n return { error: \"A quoted field is never closed.\", line: start.line };\n field += \"\\n\";\n text = lines[i]!.text;\n pos = 0;\n continue;\n }\n field += text.slice(pos, close);\n if (text[close + 1] === '\"') {\n field += '\"';\n pos = close + 2;\n continue;\n }\n quoted = false;\n pos = close + 1;\n // Only spaces may sit between a closing quote and the next comma.\n const rest = /^[ \\t]*/.exec(text.slice(pos))![0].length;\n pos += rest;\n if (pos < text.length && text[pos] !== \",\") {\n return { error: \"Unexpected text after a quoted field.\", line: lines[i]!.line };\n }\n continue;\n }\n if (pos >= text.length) {\n fields.push(field.trim());\n break;\n }\n const ch = text[pos]!;\n if (ch === \",\") {\n fields.push(field.trim());\n field = \"\";\n pos++;\n } else if (ch === '\"' && field.trim() === \"\") {\n quoted = true;\n field = \"\";\n pos++;\n } else if (ch === '\"') {\n return { error: \"A quote inside an unquoted field.\", line: lines[i]!.line };\n } else {\n field += ch;\n pos++;\n }\n }\n records.push({ fields, line: start.line });\n i++;\n }\n return records;\n}\n","import { splitSource } from \"../source/lines.js\";\nimport type { DiagramDataError } from \"../sankey/index.js\";\n\n/**\n * Reading Mermaid `treemap-beta` / `treemap` diagrams into data, for hosts\n * that draw them with their own components (`familyViews`). Treemaps stay\n * render-only: the text is kept verbatim and not edited through `EditOp`s.\n *\n * The grammar follows Mermaid's: one item per line, nested by indentation.\n * A section is a quoted name (`\"Sales\"` or `'Sales'`); a leaf adds a value\n * after `:` or `,` (`\"Q1\": 1,200`, commas in the number are dropped). Either\n * may end with a `:::class`. `title`, `accTitle` and `accDescr` lines are\n * read; `classDef` lines and `%%` comments are skipped. Styling is not\n * returned.\n *\n * @module\n */\n\nexport interface TreemapItem {\n name: string;\n /** Leaves only. */\n value?: number;\n /** Sections only (possibly empty). */\n children?: TreemapItem[];\n /** The `:::class` the item names, if any. */\n className?: string;\n}\n\nexport interface TreemapData {\n /** `title` line in the body (frontmatter titles stay on `doc.graph.title`). */\n title?: string;\n accTitle?: string;\n accDescr?: string;\n /** Top-level items; Mermaid allows several. */\n items: TreemapItem[];\n}\n\nexport type TreemapParse = { data: TreemapData } | DiagramDataError;\n\nconst NAME = String.raw`(?:\"([^\"]*)\"|'([^']*)')`;\nconst CLASS = String.raw`(?:\\s*:::\\s*([A-Za-z_]\\w*))?`;\nconst SECTION_RE = new RegExp(String.raw`^${NAME}${CLASS}$`);\nconst LEAF_RE = new RegExp(String.raw`^${NAME}\\s*[:,]\\s*([0-9_.,]+)${CLASS}$`);\n\n/**\n * Reads a treemap's items. `source` is the whole Mermaid text (with\n * frontmatter and header), e.g. a passthrough document's `doc.source`.\n * Returns an error for a line Mermaid would reject (an unquoted name, a\n * missing or negative value) or when there are no items.\n */\nexport function parseTreemap(source: string): TreemapParse {\n const split = splitSource(source);\n if (!/^\\s*treemap(?:-beta)?\\b/i.test(split.header)) {\n return { error: \"Not a treemap: the first line should be `treemap-beta`.\" };\n }\n const data: TreemapData = { items: [] };\n const stack: { item: TreemapItem; indent: number }[] = [];\n const firstLine = split.headerIndex + 2;\n const lines = split.bodyLines;\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i]!;\n const line = firstLine + i;\n const indent = /^[ \\t]*/.exec(raw)![0].length;\n const text = stripComment(raw).trim();\n if (!text) continue;\n\n const accBlock = /^accDescr\\s*\\{(.*)$/.exec(text);\n if (accBlock) {\n // `accDescr { … }` may run over several lines.\n let body = accBlock[1]!;\n while (!body.includes(\"}\") && i + 1 < lines.length) body += `\\n${lines[++i]}`;\n if (!body.includes(\"}\")) return { error: \"`accDescr {` is never closed.\", line };\n data.accDescr = body.slice(0, body.indexOf(\"}\")).trim();\n continue;\n }\n const acc = /^(accTitle|accDescr)\\s*:(.*)$/.exec(text);\n if (acc) {\n data[acc[1] as \"accTitle\" | \"accDescr\"] = acc[2]!.trim();\n continue;\n }\n const title = /^title(?:[ \\t]+(.*))?$/.exec(text);\n if (title) {\n data.title = (title[1] ?? \"\").trim() || undefined;\n continue;\n }\n if (/^classDef\\s/.test(text)) continue;\n\n let item: TreemapItem;\n const leaf = LEAF_RE.exec(text);\n const section = leaf ? null : SECTION_RE.exec(text);\n if (leaf) {\n const value = Number.parseFloat(leaf[3]!.replace(/,/g, \"\"));\n if (!Number.isFinite(value)) return { error: `\"${leaf[3]}\" is not a number.`, line };\n item = { name: leaf[1] ?? leaf[2]!, value };\n if (leaf[4]) item.className = leaf[4];\n } else if (section) {\n item = { name: section[1] ?? section[2]!, children: [] };\n if (section[3]) item.className = section[3];\n } else {\n return {\n error: /^[\"']/.test(text)\n ? 'Expected `\"Name\"` for a section or `\"Name\": value` for a leaf.'\n : 'Names must be in quotes, e.g. `\"Sales\": 120`.',\n line,\n };\n }\n\n // As Mermaid does: the parent is the nearest section above with less indent.\n while (stack.length && stack[stack.length - 1]!.indent >= indent) stack.pop();\n const parent = stack[stack.length - 1]?.item;\n if (parent) parent.children!.push(item);\n else data.items.push(item);\n if (item.children) stack.push({ item, indent });\n }\n\n if (!data.items.length) return { error: 'No items: add lines like `\"Sales\": 120`.' };\n return { data };\n}\n\n/** Drops a `%%` comment, unless it sits inside a quoted name. */\nfunction stripComment(line: string): string {\n let quote: string | null = null;\n for (let i = 0; i < line.length; i++) {\n const ch = line[i]!;\n if (quote) {\n if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === \"%\" && line[i + 1] === \"%\") {\n return line.slice(0, i);\n }\n }\n return line;\n}\n\n/** A treemap item's value: a leaf's own, or the sum of a section's leaves. */\nexport function treemapValue(item: TreemapItem): number {\n if (!item.children) return item.value ?? 0;\n return item.children.reduce((sum, child) => sum + treemapValue(child), 0);\n}\n","import { HistoryManager } from '../history/HistoryManager.js';\nimport type { Command } from '../history/types.js';\nimport { applyEdits, parseMermaid } from './document.js';\nimport { FLOWCHART_SHAPES } from './flowchart/shapes.js';\nimport { STATE_SHAPES } from './lines-grammar/state.js';\nimport { familyUI } from './families/index.js';\nimport { DIRECTIONS, type DiagramFamily, type EditOp, type MermaidDocument } from './types.js';\n\n/** Families that can be edited statement by statement; every other family is kept as text. */\nexport const EDITABLE_DIAGRAM_FAMILIES = [\n\t'flowchart',\n\t'state',\n\t'class',\n\t'er',\n\t'pie',\n\t'mindmap',\n\t'quadrant',\n\t'c4',\n\t'architecture',\n\t'sequence',\n\t'gitGraph',\n\t'gantt',\n\t'timeline',\n\t'journey',\n\t'xychart',\n\t'radar'\n] as const;\n\n/**\n * The shapes a node of `family` can take (`setNodeShape`), as canonical names. Empty when\n * the family has nothing to choose between (class boxes, ER entities, pie slices…).\n */\nexport function diagramShapes(family: DiagramFamily): string[] {\n\tif (family === 'flowchart') return FLOWCHART_SHAPES.map((shape) => shape.name);\n\tif (family === 'state') return STATE_SHAPES.map((shape) => shape.name);\n\tif (family === 'class' || family === 'er') return [];\n\tconst shapes = familyUI(family)?.shapes ?? [];\n\treturn shapes.length > 1 ? shapes.map((shape) => shape.value) : [];\n}\n\n/** Whether the family has groups (subgraphs, composite states, namespaces, boxes, sections…). */\nexport function diagramSupportsGroups(family: DiagramFamily): boolean {\n\treturn familyUI(family)?.groups.supported ?? false;\n}\n\n/** Whether the family has a direction (`setDirection`): false for families whose positions follow their data (pie, sequence, Gantt…). */\nexport function diagramSupportsDirection(family: DiagramFamily): boolean {\n\treturn familyUI(family)?.directions !== false;\n}\n\nconst quote = (ids: Iterable<string>) => [...ids].map((id) => `\"${id}\"`).join(', ');\n\n/**\n * Checks an edit against the document before it is applied. Returns a sentence saying\n * what is wrong and how to fix it, or `null` when the edit is valid. Ids are checked\n * against nodes, groups and edges; shapes against {@link diagramShapes}.\n */\nexport function validateDiagramEdit(doc: MermaidDocument, op: EditOp): string | null {\n\tconst { graph, family } = doc;\n\tif (op.type === 'replaceSource') return null;\n\tif (doc.tier !== 'editable')\n\t\treturn `This ${family} diagram can only be edited as Mermaid text; replace the source instead.`;\n\tconst nodes = new Set(graph.nodes.map((n) => n.id));\n\tconst groups = new Set(graph.groups.map((g) => g.id));\n\tconst edges = new Set(graph.edges.map((e) => e.id));\n\tconst anyIds = () => (nodes.size ? `existing node ids are ${quote([...nodes].slice(0, 20))}` : 'the diagram has no nodes yet');\n\tconst node = (id: string, allowGroup = false) =>\n\t\tnodes.has(id) || (allowGroup && groups.has(id)) ? null : `There is no node \"${id}\"; ${anyIds()}.`;\n\tconst group = (id: string | undefined) =>\n\t\tid === undefined || groups.has(id)\n\t\t\t? null\n\t\t\t: `There is no group \"${id}\"; ${groups.size ? `existing groups are ${quote(groups)}` : 'the diagram has no groups'}.`;\n\tconst edge = (id: string) =>\n\t\tedges.has(id)\n\t\t\t? null\n\t\t\t: `There is no edge \"${id}\"; ${edges.size ? `existing edge ids are ${quote([...edges].slice(0, 20))}` : 'the diagram has no edges yet'}.`;\n\tconst shape = (name: string | undefined) => {\n\t\tif (name === undefined) return null;\n\t\tconst shapes = diagramShapes(family);\n\t\tif (!shapes.length) return `Nodes of a ${family} diagram have no shapes to choose from.`;\n\t\treturn shapes.includes(name) ? null : `Unknown shape \"${name}\"; use one of ${quote(shapes)}.`;\n\t};\n\tconst free = (id: string) =>\n\t\t!/^[A-Za-z0-9_][\\w.-]*$/.test(id)\n\t\t\t? `Id \"${id}\" is not a valid Mermaid id; use letters, digits, \"_\", \"-\" or \".\".`\n\t\t\t: nodes.has(id) || groups.has(id)\n\t\t\t\t? `Id \"${id}\" is already used; choose another.`\n\t\t\t\t: null;\n\tconst first = (...problems: (string | null)[]) => problems.find((p) => p) ?? null;\n\n\tswitch (op.type) {\n\t\tcase 'addNode':\n\t\t\treturn first(free(op.node.id), shape(op.node.shape), group(op.node.parentId));\n\t\tcase 'removeNodes':\n\t\t\treturn first(...op.ids.map((id) => node(id, true)));\n\t\tcase 'renameNode':\n\t\t\treturn node(op.id, true);\n\t\tcase 'setNodeShape':\n\t\t\treturn first(node(op.id), shape(op.shape));\n\t\tcase 'setNodeClasses':\n\t\tcase 'setNodeStyles':\n\t\tcase 'setNodeLink':\n\t\tcase 'setNodeData':\n\t\tcase 'resizeNode':\n\t\t\treturn node(op.id, true);\n\t\tcase 'moveNodes':\n\t\t\treturn first(...op.moves.map((m) => node(m.id, true)));\n\t\tcase 'addEdge':\n\t\t\treturn first(node(op.edge.source, true), node(op.edge.target, true));\n\t\tcase 'removeEdges':\n\t\t\treturn first(...op.ids.map(edge));\n\t\tcase 'setEdgeLabel':\n\t\tcase 'setEdgeStyle':\n\t\tcase 'setEdgeData':\n\t\t\treturn edge(op.id);\n\t\tcase 'reconnectEdge':\n\t\t\treturn first(edge(op.id), node(op.source, true), node(op.target, true));\n\t\tcase 'groupNodes':\n\t\t\tif (!diagramSupportsGroups(family)) return `${family} diagrams have no groups.`;\n\t\t\treturn first(free(op.groupId), ...op.nodeIds.map((id) => node(id, true)), group(op.parentId));\n\t\tcase 'ungroup':\n\t\t\treturn group(op.groupId);\n\t\tcase 'renameGroup':\n\t\tcase 'setGroupDirection':\n\t\t\treturn group(op.id);\n\t\tcase 'setNodeParent':\n\t\t\treturn first(...op.ids.map((id) => node(id, true)), group(op.parentId));\n\t\tcase 'setDirection':\n\t\t\tif (!diagramSupportsDirection(family)) return `${family} diagrams have no direction.`;\n\t\t\treturn (DIRECTIONS as readonly string[]).includes(op.direction)\n\t\t\t\t? null\n\t\t\t\t: `Unknown direction \"${op.direction}\"; use one of ${quote(DIRECTIONS)}.`;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nexport interface DiagramControllerOptions {\n\t/** Starting Mermaid source (default: an empty `flowchart TD`). */\n\tsource?: string;\n\t/** History to record into, e.g. one shared with the rest of the app (default: a new one). */\n\thistory?: HistoryManager;\n\t/** Refuse edits (read tools still work). */\n\treadOnly?: boolean;\n\t/** Called after the source changes (edit, undo, redo, load). */\n\tonChange?: (source: string, document: MermaidDocument) => void;\n}\n\n/** A snapshot of the controller. The same object is returned until something changes. */\nexport interface DiagramState {\n\tdocument: MermaidDocument;\n\t/** The Mermaid text, always in step with `document`. */\n\tsource: string;\n\tfamily: DiagramFamily;\n\t/** True for families with an adapter (see {@link EDITABLE_DIAGRAM_FAMILIES}) that are not read-only. */\n\teditable: boolean;\n\treadOnly: boolean;\n\t/** Selected node, group or edge ids. */\n\tselection: readonly string[];\n\t/** The problem with the last rejected edit, if any. */\n\tissue: string | null;\n\tcanUndo: boolean;\n\tcanRedo: boolean;\n\tundoLabel: string;\n\tredoLabel: string;\n\t/** Bumped on every change. */\n\trevision: number;\n}\n\nexport type DiagramActionResult =\n\t| { ok: true; document: MermaidDocument }\n\t| { ok: false; message: string };\n\n/**\n * Headless state for a Mermaid diagram editor: the parsed document, a selection and\n * undoable statement-level edits. Every `apply` is one undo step and only rewrites the\n * statements it touches, so untouched text (comments, directives, formatting) survives.\n * Canvas positions go into the `%% @pixerate {…}` comment. Drags merge into one undo\n * step between `beginGesture` and `endGesture`. `getState()` returns the same object\n * until something changes, so it works with `useSyncExternalStore` and Svelte runes.\n */\nexport class DiagramController {\n\treadonly history: HistoryManager;\n\tprivate doc: MermaidDocument;\n\tprivate selection: string[] = [];\n\tprivate issue: string | null = null;\n\tprivate readOnly: boolean;\n\tprivate revision = 0;\n\tprivate applying = false;\n\tprivate gestureKey: string | null = null;\n\tprivate gestureCount = 0;\n\tprivate readonly onChange?: DiagramControllerOptions['onChange'];\n\tprivate readonly listeners = new Set<(state: DiagramState) => void>();\n\tprivate unsubscribeHistory?: () => void;\n\tprivate state!: DiagramState;\n\n\tconstructor(options: DiagramControllerOptions = {}) {\n\t\tthis.history = options.history ?? new HistoryManager();\n\t\tthis.onChange = options.onChange;\n\t\tthis.readOnly = options.readOnly ?? false;\n\t\tthis.doc = parseMermaid(options.source ?? 'flowchart TD\\n');\n\t\tthis.state = this.computeState();\n\t\tthis.connect();\n\t}\n\n\tprivate connect() {\n\t\tthis.unsubscribeHistory?.();\n\t\tthis.unsubscribeHistory = this.history.subscribe((h) => {\n\t\t\tconst s = this.state;\n\t\t\tif (this.applying || (s.canUndo === h.canUndo && s.canRedo === h.canRedo && s.undoLabel === h.undoName && s.redoLabel === h.redoName)) return;\n\t\t\tthis.notify(false);\n\t\t});\n\t}\n\n\t/** Stops listening to the history (subscribing again reconnects). */\n\tdestroy() {\n\t\tthis.unsubscribeHistory?.();\n\t\tthis.unsubscribeHistory = undefined;\n\t}\n\n\t/** The current snapshot (stable until the next change). */\n\tgetState = (): DiagramState => this.state;\n\n\t/** Calls `listener` after every change. Returns an unsubscribe function. */\n\tsubscribe = (listener: (state: DiagramState) => void): (() => void) => {\n\t\tif (!this.unsubscribeHistory) this.connect();\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t};\n\n\t/** The current document (immutable; edit through the controller). */\n\tgetDocument(): MermaidDocument {\n\t\treturn this.doc;\n\t}\n\n\tgetSource(): string {\n\t\treturn this.doc.source;\n\t}\n\n\tprivate computeState(): DiagramState {\n\t\tconst h = this.history.getState();\n\t\treturn {\n\t\t\tdocument: this.doc,\n\t\t\tsource: this.doc.source,\n\t\t\tfamily: this.doc.family,\n\t\t\teditable: this.doc.tier === 'editable' && !this.readOnly,\n\t\t\treadOnly: this.readOnly,\n\t\t\tselection: this.selection,\n\t\t\tissue: this.issue,\n\t\t\tcanUndo: h.canUndo,\n\t\t\tcanRedo: h.canRedo,\n\t\t\tundoLabel: h.undoName,\n\t\t\tredoLabel: h.redoName,\n\t\t\trevision: this.revision\n\t\t};\n\t}\n\n\tprivate notify(sourceChanged: boolean) {\n\t\tthis.revision++;\n\t\tthis.state = this.computeState();\n\t\tif (sourceChanged) this.onChange?.(this.doc.source, this.doc);\n\t\tconst state = this.state;\n\t\tthis.listeners.forEach((listener) => {\n\t\t\ttry {\n\t\t\t\tlistener(state);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[DiagramController] Error in state listener:', error);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate setDocument(doc: MermaidDocument) {\n\t\tthis.applying = true;\n\t\ttry {\n\t\t\tthis.doc = doc;\n\t\t\tconst ids = new Set([...doc.graph.nodes, ...doc.graph.groups, ...doc.graph.edges].map((x) => x.id));\n\t\t\tthis.selection = this.selection.filter((id) => ids.has(id));\n\t\t} finally {\n\t\t\tthis.applying = false;\n\t\t}\n\t\tthis.notify(true);\n\t}\n\n\tprivate commit(label: string, next: MermaidDocument) {\n\t\tconst before = this.doc;\n\t\tthis.issue = null;\n\t\tif (next.source === before.source) {\n\t\t\tthis.notify(false);\n\t\t\treturn;\n\t\t}\n\t\tconst command: Command = {\n\t\t\tname: label,\n\t\t\ttag: 'diagram',\n\t\t\texecute: () => this.setDocument(next),\n\t\t\tundo: () => this.setDocument(before),\n\t\t\tredo: () => this.setDocument(next),\n\t\t\tbyteSize: (before.source.length + next.source.length) * 2\n\t\t};\n\t\tif (this.gestureKey !== null) this.history.executeMerged(command, { key: `diagram.gesture:${this.gestureKey}:${this.gestureCount}` });\n\t\telse this.history.execute(command);\n\t}\n\n\tprivate reject(message: string): DiagramActionResult {\n\t\tthis.issue = message;\n\t\tthis.notify(false);\n\t\treturn { ok: false, message };\n\t}\n\n\t/**\n\t * Applies edits as one undo step. All-or-nothing: every edit is checked against the\n\t * document as it stands after the edits before it, and nothing changes when one fails.\n\t */\n\tapply(ops: EditOp | EditOp[], label = 'Edit diagram'): DiagramActionResult {\n\t\tconst list = Array.isArray(ops) ? ops : [ops];\n\t\tif (this.readOnly) return this.reject('The diagram is read-only.');\n\t\tlet doc = this.doc;\n\t\tfor (const op of list) {\n\t\t\tconst problem = validateDiagramEdit(doc, op);\n\t\t\tif (problem) return this.reject(problem);\n\t\t\tdoc = applyEdits(doc, [op]);\n\t\t}\n\t\tthis.commit(label, doc);\n\t\treturn { ok: true, document: this.doc };\n\t}\n\n\t/**\n\t * Replaces the whole source. `clearHistory` drops the undo stack (opening a file);\n\t * otherwise the replacement is one undo step.\n\t */\n\tload(source: string, options: { clearHistory?: boolean } = {}): DiagramActionResult {\n\t\tconst next = parseMermaid(source, { metaCarrier: this.doc.metaCarrier });\n\t\tif (options.clearHistory) {\n\t\t\tthis.history.clear();\n\t\t\tthis.issue = null;\n\t\t\tthis.selection = [];\n\t\t\tthis.setDocument(next);\n\t\t\treturn { ok: true, document: next };\n\t\t}\n\t\tif (this.readOnly) return this.reject('The diagram is read-only.');\n\t\tthis.commit('Replace diagram', next);\n\t\treturn { ok: true, document: this.doc };\n\t}\n\n\tsetReadOnly(readOnly: boolean) {\n\t\tif (this.readOnly === readOnly) return;\n\t\tthis.readOnly = readOnly;\n\t\tthis.notify(false);\n\t}\n\n\t/** Selects node, group or edge ids (unknown ids are dropped). */\n\tselect(ids: readonly string[]) {\n\t\tconst known = new Set([...this.doc.graph.nodes, ...this.doc.graph.groups, ...this.doc.graph.edges].map((x) => x.id));\n\t\tconst next = ids.filter((id) => known.has(id));\n\t\tif (next.length === this.selection.length && next.every((id, i) => id === this.selection[i])) return;\n\t\tthis.selection = next;\n\t\tthis.notify(false);\n\t}\n\n\t/** Edits until `endGesture` (e.g. the moves of one drag) fold into one undo step. */\n\tbeginGesture(key: string) {\n\t\tthis.gestureKey = key;\n\t\tthis.gestureCount++;\n\t}\n\n\tendGesture() {\n\t\tthis.gestureKey = null;\n\t}\n\n\tundo(): boolean {\n\t\treturn this.history.undo();\n\t}\n\n\tredo(): boolean {\n\t\treturn this.history.redo();\n\t}\n\n\t/** A node id not used by any node or group: `prefix` followed by the next free number. */\n\tnextId(prefix = 'N'): string {\n\t\tconst used = new Set([...this.doc.graph.nodes, ...this.doc.graph.groups].map((x) => x.id));\n\t\tlet i = 1;\n\t\twhile (used.has(`${prefix}${i}`)) i++;\n\t\treturn `${prefix}${i}`;\n\t}\n}\n","import type { AgentTool, AgentToolProvider } from '../tools/registry.js';\nimport type { JsonSchema } from '../tools/schema.js';\nimport type { PresenceActorInfo } from '../animation/presence.js';\nimport { diagramShapes, diagramSupportsGroups, type DiagramController } from './controller.js';\nimport { checkFieldData, describeFields, familyUI, pickFieldData } from './families/index.js';\nimport type { DiagramDirector } from './director.js';\nimport { detectFamily } from './document.js';\nimport { findMermaidSources } from './source/find.js';\nimport { DIRECTIONS, type EditOp, type MermaidDocument, type Point } from './types.js';\n\nexport interface DiagramToolProviderOptions {\n\t/** Provider id (unique per registry). Default `diagram`. */\n\tid?: string;\n\t/** Always prefix tool names with `<namespace>__`. */\n\tnamespace?: string;\n\t/**\n\t * The diagram cannot be edited: write tools are hidden and `get_diagram` says why. A\n\t * string is the reason (\"The report is locked\"). The controller's own `readOnly` counts too.\n\t */\n\treadOnly?: boolean | string | (() => boolean | string);\n\t/**\n\t * Where the canvas shows each node now (absolute top-left), e.g. `() => canvas.layout.positions`.\n\t * When given, a move pins every node that has no stored position yet, so moving one\n\t * node of an auto-laid diagram does not shift the others.\n\t */\n\tgetPositions?: () => Readonly<Record<string, Point>>;\n\t/**\n\t * Draws the diagram as standalone SVG (e.g. `(o) => mermaidToSvg(controller.getDocument(), o)`\n\t * from `@pixerate/editor/canvas`). Enables `render_diagram`.\n\t */\n\trenderSvg?: (options: { maxWidth?: number; maxHeight?: number }) => { svg: string; width: number; height: number };\n\t/** Turns that SVG into PNG (e.g. `rasterizeSvg`), so `render_diagram` can return an image. */\n\trasterize?: (svg: string, size: { width: number; height: number }) => Promise<{ data: string; mimeType: string }> | { data: string; mimeType: string };\n\t/**\n\t * Acts out every write with a visible cursor (see `DiagramDirector`): it moves to the node,\n\t * clicks, drags, connects and types, and a whole-diagram load is drawn statement by\n\t * statement. Each call is still one undo step. Without it, edits apply at once.\n\t */\n\tdirector?: DiagramDirector;\n\t/** Who the cursor belongs to (default `{ id: 'agent', name: 'Agent' }`). */\n\tactor?: PresenceActorInfo;\n}\n\nconst READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;\nconst WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false } as const;\nconst WRITE_IDEMPOTENT = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;\nconst DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false } as const;\n\nconst ID = { type: 'string', minLength: 1, description: 'A node id from get_diagram (e.g. \"A\").' } as const;\nconst IDS = (what: string) => ({ type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1, description: what }) as const;\nconst EDGE_ID = { type: 'string', minLength: 1, description: 'An edge id from get_diagram (e.g. \"L_A_B_0\").' } as const;\nconst LABEL = (what: string) => ({ type: 'string', description: what }) as const;\n/** A field value: string, number, boolean, array (numbers, lines, tags) or null to clear. */\nconst FIELD_VALUE = { type: ['string', 'number', 'boolean', 'array', 'null'] } as const;\nconst object = (properties: Record<string, JsonSchema>, required: string[] = []) => ({\n\ttype: 'object' as const,\n\tproperties,\n\t...(required.length ? { required } : {}),\n\tadditionalProperties: false\n});\n\n/** What an agent needs to know about a document: ids, labels, shapes, structure. */\nexport function describeDiagram(doc: MermaidDocument) {\n\tconst { graph } = doc;\n\tconst ui = familyUI(doc.family);\n\tconst settings = pickFieldData(ui?.diagramFields, graph.data);\n\treturn {\n\t\tfamily: doc.family,\n\t\teditable: doc.tier === 'editable',\n\t\tdirection: graph.direction,\n\t\t...(graph.title ? { title: graph.title } : {}),\n\t\tnodes: graph.nodes.map((n) => ({\n\t\t\tid: n.id,\n\t\t\tlabel: n.label,\n\t\t\tshape: n.shape,\n\t\t\t...(n.parentId ? { parentId: n.parentId } : {}),\n\t\t\t...(n.classes.length ? { classes: n.classes } : {}),\n\t\t\t...(n.styles.length ? { styles: n.styles } : {}),\n\t\t\t...(n.data?.synthetic ? { synthetic: true } : {}),\n\t\t\t...(ui?.nodeFields && Object.keys(pickFieldData(ui.nodeFields, n.data)).length ? { data: pickFieldData(ui.nodeFields, n.data) } : {})\n\t\t})),\n\t\tedges: graph.edges.map((e) => ({\n\t\t\tid: e.id,\n\t\t\tsource: e.source,\n\t\t\ttarget: e.target,\n\t\t\t...(e.label ? { label: e.label } : {}),\n\t\t\tpattern: e.pattern,\n\t\t\tarrowEnd: e.arrowEnd,\n\t\t\t...(ui?.edgeFields && Object.keys(pickFieldData(ui.edgeFields, e.data)).length ? { data: pickFieldData(ui.edgeFields, e.data) } : {})\n\t\t})),\n\t\tgroups: graph.groups.map((g) => ({\n\t\t\tid: g.id,\n\t\t\tlabel: g.label,\n\t\t\t...(g.parentId ? { parentId: g.parentId } : {}),\n\t\t\t...(g.direction ? { direction: g.direction } : {})\n\t\t})),\n\t\tshapes: diagramShapes(doc.family),\n\t\t...(Object.keys(settings).length ? { settings } : {}),\n\t\t...(ui && (ui.nodeFields?.length || ui.edgeFields?.length || ui.diagramFields?.length)\n\t\t\t? {\n\t\t\t\t\tfields: {\n\t\t\t\t\t\t...(ui.nodeFields?.length ? { node: describeFields(ui.nodeFields) } : {}),\n\t\t\t\t\t\t...(ui.edgeFields?.length ? { edge: describeFields(ui.edgeFields) } : {}),\n\t\t\t\t\t\t...(ui.diagramFields?.length ? { diagram: describeFields(ui.diagramFields) } : {})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t: {}),\n\t\tpositions: Object.fromEntries(Object.entries(doc.meta.layout ?? {}).map(([id, [x, y]]) => [id, { x: Math.round(x), y: Math.round(y) }])),\n\t\t...(doc.diagnostics.length ? { diagnostics: doc.diagnostics.map((d) => ({ severity: d.severity, message: d.message, ...(d.line ? { line: d.line } : {}) })) } : {})\n\t};\n}\n\n/**\n * Agent tools for a Mermaid diagram (`DiagramController`), for a `ToolRegistry`: one\n * tool per operation with strict schemas. Reads (`get_diagram`, `export_diagram_mermaid`,\n * `find_mermaid_diagrams`) are always there; writes are hidden while the diagram is\n * read-only. Every write is checked against the current diagram first (unknown ids,\n * taken ids, shapes the family does not have), applies as one undo step, and returns\n * what it created or changed. Untouched Mermaid statements are kept byte for byte.\n */\nexport function createDiagramToolProvider(controller: DiagramController, options: DiagramToolProviderOptions = {}): AgentToolProvider {\n\tconst readOnlyReason = (): string | null => {\n\t\tconst r = typeof options.readOnly === 'function' ? options.readOnly() : options.readOnly;\n\t\tif (r) return typeof r === 'string' ? r : 'This diagram is read-only.';\n\t\treturn controller.getState().readOnly ? 'This diagram is read-only.' : null;\n\t};\n\tconst doc = () => controller.getDocument();\n\tconst actor = options.actor ?? { id: 'agent', name: 'Agent' };\n\tconst apply = async (ops: EditOp | EditOp[], label: string) => {\n\t\tconst result = options.director ? await options.director.perform(actor, Array.isArray(ops) ? ops : [ops], label) : controller.apply(ops, label);\n\t\tif (!result.ok) throw new Error(result.message);\n\t\treturn result.document;\n\t};\n\tconst edgeIds = () => new Set(doc().graph.edges.map((e) => e.id));\n\tconst newEdges = (before: Set<string>, after: MermaidDocument) =>\n\t\tafter.graph.edges.filter((e) => !before.has(e.id)).map((e) => ({ id: e.id, source: e.source, target: e.target, ...(e.label ? { label: e.label } : {}) }));\n\tconst nodeOut = (d: MermaidDocument, id: string) => {\n\t\tconst n = d.graph.nodes.find((x) => x.id === id) ?? d.graph.groups.find((x) => x.id === id);\n\t\treturn n ? { id: n.id, label: n.label, ...('shape' in n ? { shape: n.shape } : {}), ...(n.parentId ? { parentId: n.parentId } : {}) } : { id };\n\t};\n\t/** Moves plus the current positions of nodes not stored yet (see `getPositions`). */\n\tconst pinned = (moves: Array<{ id: string; position: Point }>) => {\n\t\tconst shown = options.getPositions?.();\n\t\tif (!shown) return moves;\n\t\tconst stored = doc().meta.layout ?? {};\n\t\tconst taken = new Set(moves.map((m) => m.id));\n\t\treturn [...moves, ...Object.entries(shown).filter(([id]) => !taken.has(id) && !stored[id]).map(([id, position]) => ({ id, position }))];\n\t};\n\tconst requireEditable = () => {\n\t\tif (doc().tier !== 'editable')\n\t\t\tthrow new Error(`This ${doc().family} diagram can only be changed as Mermaid text; use load_diagram_mermaid.`);\n\t};\n\n\tconst getDiagram: AgentTool = {\n\t\tname: 'get_diagram',\n\t\ttitle: 'Read the diagram',\n\t\tdescription:\n\t\t\t'Returns the diagram: its Mermaid family (flowchart, state, class, er, pie, mindmap, quadrant, c4, architecture, sequence, gitGraph, gantt, timeline, journey, xychart, radar; sankey, block, packet, kanban and treemap are text only), direction, title, nodes (id, label, shape, parentId, data), edges (id, source, target, label, data), groups (subgraphs, composite states, sections, boxes…), the shapes nodes can take, diagram settings, the data fields each family offers (\"fields\": node, edge and diagram field keys with their types), stored canvas positions, any parse diagnostics and the selection. Use the ids and field keys it returns with the write tools.',\n\t\tinputSchema: object({ includeSource: { type: 'boolean', description: 'Also return the Mermaid text (default false).' } }),\n\t\tannotations: { title: 'Read the diagram', ...READ },\n\t\thandler: async ({ includeSource = false }) => {\n\t\t\tconst reason = readOnlyReason();\n\t\t\treturn {\n\t\t\t\t...describeDiagram(doc()),\n\t\t\t\tselection: controller.getState().selection,\n\t\t\t\t...(includeSource ? { source: doc().source } : {}),\n\t\t\t\t...(reason ? { readOnly: reason } : {})\n\t\t\t};\n\t\t}\n\t};\n\n\tconst exportMermaid: AgentTool = {\n\t\tname: 'export_diagram_mermaid',\n\t\ttitle: 'Export the diagram as Mermaid',\n\t\tdescription:\n\t\t\t'Returns the diagram as Mermaid text. Canvas positions are kept in a \"%% @pixerate {…}\" comment that Mermaid ignores; pass withLayout false to leave it out.',\n\t\tinputSchema: object({ withLayout: { type: 'boolean', description: 'Keep the %% @pixerate layout comment (default true).' } }),\n\t\tannotations: { title: 'Export the diagram as Mermaid', ...READ },\n\t\thandler: async ({ withLayout = true }) => {\n\t\t\tconst source = doc().source;\n\t\t\treturn { family: doc().family, source: withLayout ? source : source.replace(/^[ \\t]*%%\\s*@(?:pixerate|ursula)\\s+\\{.*\\}[ \\t]*(\\r?\\n|$)/gm, '') };\n\t\t}\n\t};\n\n\tconst findDiagrams: AgentTool = {\n\t\tname: 'find_mermaid_diagrams',\n\t\ttitle: 'Find Mermaid diagrams in text',\n\t\tdescription:\n\t\t\t'Finds the Mermaid diagrams in a piece of text (Markdown with ```mermaid blocks, a chat reply, or a bare diagram) without changing anything. Returns each diagram\\'s source, family and title; pass a source to load_diagram_mermaid to open it.',\n\t\tinputSchema: object({ text: { type: 'string', description: 'The text to search.' } }, ['text']),\n\t\tannotations: { title: 'Find Mermaid diagrams in text', ...READ },\n\t\thandler: ({ text }) => ({ diagrams: findMermaidSources(text) })\n\t};\n\n\tconst loadMermaid: AgentTool = {\n\t\tname: 'load_diagram_mermaid',\n\t\ttitle: 'Replace the diagram with Mermaid',\n\t\tdescription:\n\t\t\t'Replaces the whole diagram with Mermaid text (one undo step). Flowchart, state, class, ER, pie, mindmap, quadrant, C4, architecture, sequence, gitGraph, Gantt, timeline, journey, XY and radar diagrams become editable; sankey, block, packet, kanban and treemap are kept as text. Returns the family, node and edge counts and any parse diagnostics. Prefer the per-operation tools for small changes: they keep the rest of the text as written.',\n\t\tinputSchema: object({ source: { type: 'string', minLength: 1, description: 'Mermaid text, starting with a header such as \"flowchart LR\".' } }, ['source']),\n\t\tannotations: { title: 'Replace the diagram with Mermaid', ...DESTRUCTIVE },\n\t\thandler: async ({ source }) => {\n\t\t\t// A fenced block (```mermaid … ```) is unwrapped; a bare diagram is taken as written.\n\t\t\tconst fenced = /^\\s*(`{3,}|~{3,})/m.test(source) ? findMermaidSources(source) : [];\n\t\t\tif (fenced.length > 1) throw new Error(`The text holds ${fenced.length} diagrams; pass one (find_mermaid_diagrams lists them).`);\n\t\t\tconst text = fenced[0]?.source ?? source;\n\t\t\tif (detectFamily(text) === 'unknown')\n\t\t\t\tthrow new Error('The text has no Mermaid header (e.g. \"flowchart LR\", \"stateDiagram-v2\", \"classDiagram\", \"erDiagram\"); nothing was changed.');\n\t\t\tconst result = options.director ? await options.director.playSource(actor, text) : controller.load(text);\n\t\t\tif (!result.ok) throw new Error(result.message);\n\t\t\tconst d = result.document;\n\t\t\treturn {\n\t\t\t\tfamily: d.family,\n\t\t\t\teditable: d.tier === 'editable',\n\t\t\t\tnodes: d.graph.nodes.length,\n\t\t\t\tedges: d.graph.edges.length,\n\t\t\t\tgroups: d.graph.groups.length,\n\t\t\t\t...(d.diagnostics.length ? { diagnostics: d.diagnostics.map((x) => x.message) } : {})\n\t\t\t};\n\t\t}\n\t};\n\n\tconst addNode: AgentTool = {\n\t\tname: 'add_diagram_node',\n\t\ttitle: 'Add a node',\n\t\tdescription:\n\t\t\t'Adds a node to the diagram, optionally inside a group and connected from an existing node. Without a position the canvas places it beside its neighbours. Returns the node and any edge created.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tlabel: LABEL('Text shown in the node.'),\n\t\t\t\tid: { type: 'string', pattern: '^[A-Za-z0-9_][\\\\w.-]*$', description: 'Mermaid id (default: the next free id for the family, e.g. \"n3\", \"slice4\", \"task2\").' },\n\t\t\t\tshape: { type: 'string', description: 'One of the shapes get_diagram lists (default: the family\\'s usual node, e.g. \"rect\").' },\n\t\t\t\tdata: { type: 'object', additionalProperties: FIELD_VALUE, description: 'Values for the family\\'s node fields (get_diagram \"fields.node\"), e.g. {\"value\": 40} for a pie slice. Defaults fill in the rest.' },\n\t\t\t\tparentId: { type: 'string', description: 'Group (subgraph) to add the node to.' },\n\t\t\t\tfrom: { type: 'string', description: 'An existing node to connect to the new node.' },\n\t\t\t\tedgeLabel: LABEL('Label of the edge from \"from\" (needs \"from\").'),\n\t\t\t\tx: { type: 'number', description: 'Left edge in canvas px (needs y).' },\n\t\t\t\ty: { type: 'number', description: 'Top edge in canvas px (needs x).' }\n\t\t\t},\n\t\t\t['label']\n\t\t),\n\t\tannotations: { title: 'Add a node', ...WRITE },\n\t\thandler: async ({ label, id, shape, parentId, from, edgeLabel, x, y, data }) => {\n\t\t\trequireEditable();\n\t\t\tif ((x === undefined) !== (y === undefined)) throw new Error('Give both x and y, or neither to place the node automatically.');\n\t\t\tif (edgeLabel !== undefined && from === undefined) throw new Error('edgeLabel needs \"from\", the node the edge starts at.');\n\t\t\tconst ui = familyUI(doc().family);\n\t\t\tconst nodeId = id ?? (ui && !['flowchart', 'state', 'class', 'er'].includes(ui.family) ? ui.nextId(doc().graph) : controller.nextId());\n\t\t\tconst defaults = ui?.newNode?.(doc().graph, parentId) ?? {};\n\t\t\tconst nodeShape = shape ?? defaults.shape;\n\t\t\tconst nodeData = { ...(defaults.data ?? {}), ...(data ? checkFieldData(ui?.nodeFields, data, `a ${doc().family} node`, nodeShape) : {}) };\n\t\t\tconst before = edgeIds();\n\t\t\tconst ops: EditOp[] = [\n\t\t\t\t{\n\t\t\t\t\ttype: 'addNode',\n\t\t\t\t\tnode: { ...defaults, id: nodeId, label, ...(nodeShape ? { shape: nodeShape } : {}), ...(parentId ? { parentId } : {}), ...(Object.keys(nodeData).length ? { data: nodeData } : {}) },\n\t\t\t\t\t...(x !== undefined ? { position: [x, y] as const } : {})\n\t\t\t\t}\n\t\t\t];\n\t\t\tif (from !== undefined) ops.push({ type: 'addEdge', edge: { source: from, target: nodeId, ...(edgeLabel ? { label: edgeLabel } : {}) } });\n\t\t\tconst d = await apply(ops, 'Add node');\n\t\t\tconst edges = newEdges(before, d);\n\t\t\treturn { node: nodeOut(d, nodeId), ...(edges.length ? { edge: edges[0] } : {}) };\n\t\t}\n\t};\n\n\tconst removeElements: AgentTool = {\n\t\tname: 'remove_diagram_nodes',\n\t\ttitle: 'Remove nodes or groups',\n\t\tdescription:\n\t\t\t'Deletes nodes with every edge that touches them. A group id removes the group but keeps its contents. Returns the removed ids and the ids of the edges that went with them.',\n\t\tinputSchema: object({ ids: IDS('Node or group ids from get_diagram.') }, ['ids']),\n\t\tannotations: { title: 'Remove nodes or groups', ...DESTRUCTIVE },\n\t\thandler: async ({ ids }) => {\n\t\t\trequireEditable();\n\t\t\tconst before = doc().graph.edges.map((e) => e.id);\n\t\t\tconst d = await apply({ type: 'removeNodes', ids }, ids.length === 1 ? 'Delete node' : 'Delete nodes');\n\t\t\tconst after = new Set(d.graph.edges.map((e) => e.id));\n\t\t\treturn { removed: ids, removedEdges: before.filter((id) => !after.has(id)) };\n\t\t}\n\t};\n\n\tconst renameNode: AgentTool = {\n\t\tname: 'rename_diagram_node',\n\t\ttitle: 'Rename a node or group',\n\t\tdescription: 'Changes the label of a node or group. Returns the node with its new label.',\n\t\tinputSchema: object({ id: { ...ID, description: 'A node or group id from get_diagram.' }, label: LABEL('The new label.') }, ['id', 'label']),\n\t\tannotations: { title: 'Rename a node or group', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, label }) => {\n\t\t\trequireEditable();\n\t\t\tconst isGroup = doc().graph.groups.some((g) => g.id === id);\n\t\t\tconst d = await apply(isGroup ? { type: 'renameGroup', id, label } : { type: 'renameNode', id, label }, 'Rename');\n\t\t\treturn { node: nodeOut(d, id) };\n\t\t}\n\t};\n\n\tconst setShape: AgentTool = {\n\t\tname: 'set_diagram_node_shape',\n\t\ttitle: 'Change a node\\'s shape',\n\t\tdescription: 'Changes a node\\'s shape (flowchart and state diagrams). Use one of the shapes get_diagram lists. Returns the node.',\n\t\tinputSchema: object({ id: ID, shape: { type: 'string', minLength: 1, description: 'Shape name, e.g. \"rect\", \"rounded\", \"diam\", \"cyl\", \"circle\".' } }, ['id', 'shape']),\n\t\tannotations: { title: 'Change a node\\'s shape', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, shape }) => {\n\t\t\trequireEditable();\n\t\t\treturn { node: nodeOut(await apply({ type: 'setNodeShape', id, shape }, 'Change shape'), id) };\n\t\t}\n\t};\n\n\tconst setStyle: AgentTool = {\n\t\tname: 'set_diagram_node_style',\n\t\ttitle: 'Style a node',\n\t\tdescription:\n\t\t\t'Replaces a node\\'s inline Mermaid styles (written as a `style` statement), e.g. [\"fill:#fde68a\", \"stroke:#b45309\", \"stroke-width:2px\"]. An empty list removes them. Returns the node with its styles.',\n\t\tinputSchema: object(\n\t\t\t{ id: ID, styles: { type: 'array', items: { type: 'string', pattern: '^[\\\\w-]+:[^,;]+$' }, description: 'CSS-like \"property:value\" pairs.' } },\n\t\t\t['id', 'styles']\n\t\t),\n\t\tannotations: { title: 'Style a node', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, styles }) => {\n\t\t\trequireEditable();\n\t\t\tconst d = await apply({ type: 'setNodeStyles', id, styles }, 'Style node');\n\t\t\treturn { id, styles: d.graph.nodes.find((n) => n.id === id)?.styles ?? [] };\n\t\t}\n\t};\n\n\tconst connect: AgentTool = {\n\t\tname: 'connect_diagram_nodes',\n\t\ttitle: 'Connect two nodes',\n\t\tdescription: 'Draws an edge from one existing node to another, with an optional label. Returns the new edge with its id.',\n\t\tinputSchema: object({ source: { ...ID, description: 'Node the edge starts at.' }, target: { ...ID, description: 'Node the edge ends at.' }, label: LABEL('Edge label (optional).') }, ['source', 'target']),\n\t\tannotations: { title: 'Connect two nodes', ...WRITE },\n\t\thandler: async ({ source, target, label }) => {\n\t\t\trequireEditable();\n\t\t\tconst before = edgeIds();\n\t\t\tconst d = await apply({ type: 'addEdge', edge: { source, target, ...(label ? { label } : {}) } }, 'Connect');\n\t\t\treturn { edge: newEdges(before, d)[0] ?? null };\n\t\t}\n\t};\n\n\tconst removeEdges: AgentTool = {\n\t\tname: 'remove_diagram_edges',\n\t\ttitle: 'Remove edges',\n\t\tdescription: 'Deletes edges by id; the nodes stay. Returns the removed ids.',\n\t\tinputSchema: object({ ids: IDS('Edge ids from get_diagram.') }, ['ids']),\n\t\tannotations: { title: 'Remove edges', ...DESTRUCTIVE },\n\t\thandler: async ({ ids }) => {\n\t\t\trequireEditable();\n\t\t\tawait apply({ type: 'removeEdges', ids }, ids.length === 1 ? 'Delete edge' : 'Delete edges');\n\t\t\treturn { removed: ids };\n\t\t}\n\t};\n\n\tconst labelEdge: AgentTool = {\n\t\tname: 'set_diagram_edge_label',\n\t\ttitle: 'Label an edge',\n\t\tdescription: 'Sets an edge\\'s label; an empty string removes it. Returns the edge.',\n\t\tinputSchema: object({ id: EDGE_ID, label: LABEL('The new label (\"\" removes it).') }, ['id', 'label']),\n\t\tannotations: { title: 'Label an edge', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, label }) => {\n\t\t\trequireEditable();\n\t\t\tconst d = await apply({ type: 'setEdgeLabel', id, label }, 'Label edge');\n\t\t\tconst e = d.graph.edges.find((x) => x.id === id);\n\t\t\treturn { edge: e ? { id: e.id, source: e.source, target: e.target, label: e.label ?? '' } : { id, label } };\n\t\t}\n\t};\n\n\tconst reconnect: AgentTool = {\n\t\tname: 'reconnect_diagram_edge',\n\t\ttitle: 'Reconnect an edge',\n\t\tdescription: 'Moves an edge to new endpoints, keeping its label and style. Returns the edge (its id may change with its endpoints).',\n\t\tinputSchema: object({ id: EDGE_ID, source: { ...ID, description: 'New start node.' }, target: { ...ID, description: 'New end node.' } }, ['id', 'source', 'target']),\n\t\tannotations: { title: 'Reconnect an edge', ...WRITE },\n\t\thandler: async ({ id, source, target }) => {\n\t\t\trequireEditable();\n\t\t\tconst index = doc().graph.edges.findIndex((e) => e.id === id);\n\t\t\tconst d = await apply({ type: 'reconnectEdge', id, source, target }, 'Reconnect edge');\n\t\t\tconst e = d.graph.edges.find((x) => x.id === id) ?? d.graph.edges.find((x, i) => i === index && x.source === source && x.target === target) ?? d.graph.edges.find((x) => x.source === source && x.target === target);\n\t\t\treturn { edge: e ? { id: e.id, source: e.source, target: e.target, ...(e.label ? { label: e.label } : {}) } : null };\n\t\t}\n\t};\n\n\tconst setDirection: AgentTool = {\n\t\tname: 'set_diagram_direction',\n\t\ttitle: 'Set the layout direction',\n\t\tdescription: 'Sets the direction the diagram flows in: TB / TD (top to bottom), BT, LR (left to right) or RL. Returns the direction.',\n\t\tinputSchema: object({ direction: { type: 'string', enum: [...DIRECTIONS], description: 'TB, TD, BT, LR or RL.' } }, ['direction']),\n\t\tannotations: { title: 'Set the layout direction', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ direction }) => {\n\t\t\trequireEditable();\n\t\t\treturn { direction: (await apply({ type: 'setDirection', direction }, 'Change direction')).graph.direction };\n\t\t}\n\t};\n\n\tconst group: AgentTool = {\n\t\tname: 'group_diagram_nodes',\n\t\ttitle: 'Group nodes',\n\t\tdescription: 'Puts nodes into a new group (a flowchart subgraph, composite state or class namespace). Returns the group id.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tnodeIds: IDS('Node ids to put in the group.'),\n\t\t\t\tlabel: LABEL('Group title.'),\n\t\t\t\tid: { type: 'string', pattern: '^[A-Za-z0-9_][\\\\w.-]*$', description: 'Group id (default: the next free \"G<number>\").' }\n\t\t\t},\n\t\t\t['nodeIds', 'label']\n\t\t),\n\t\tannotations: { title: 'Group nodes', ...WRITE },\n\t\thandler: async ({ nodeIds, label, id }) => {\n\t\t\trequireEditable();\n\t\t\tif (!diagramSupportsGroups(doc().family)) throw new Error(`${doc().family} diagrams have no groups.`);\n\t\t\tconst groupId = id ?? controller.nextId('G');\n\t\t\tawait apply({ type: 'groupNodes', groupId, label, nodeIds }, 'Group');\n\t\t\treturn { group: { id: groupId, label, nodeIds } };\n\t\t}\n\t};\n\n\tconst ungroup: AgentTool = {\n\t\tname: 'ungroup_diagram_group',\n\t\ttitle: 'Ungroup',\n\t\tdescription: 'Removes a group and keeps its nodes (they move to the group\\'s parent). Returns the ids that were in it.',\n\t\tinputSchema: object({ id: { type: 'string', minLength: 1, description: 'A group id from get_diagram.' } }, ['id']),\n\t\tannotations: { title: 'Ungroup', ...DESTRUCTIVE },\n\t\thandler: async ({ id }) => {\n\t\t\trequireEditable();\n\t\t\tconst members = [...doc().graph.nodes, ...doc().graph.groups].filter((x) => x.parentId === id).map((x) => x.id);\n\t\t\tawait apply({ type: 'ungroup', groupId: id }, 'Ungroup');\n\t\t\treturn { ungrouped: id, members };\n\t\t}\n\t};\n\n\tconst move: AgentTool = {\n\t\tname: 'move_diagram_nodes',\n\t\ttitle: 'Move nodes',\n\t\tdescription:\n\t\t\t'Moves nodes or groups on the canvas to absolute positions (top-left, canvas px). Positions are stored in the %% @pixerate comment; the Mermaid statements do not change. Returns the new positions.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tmoves: {\n\t\t\t\t\ttype: 'array',\n\t\t\t\t\tminItems: 1,\n\t\t\t\t\tdescription: 'One entry per node.',\n\t\t\t\t\titems: object({ id: { ...ID, description: 'Node or group id.' }, x: { type: 'number', description: 'Left edge in px.' }, y: { type: 'number', description: 'Top edge in px.' } }, ['id', 'x', 'y'])\n\t\t\t\t}\n\t\t\t},\n\t\t\t['moves']\n\t\t),\n\t\tannotations: { title: 'Move nodes', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ moves }) => {\n\t\t\tconst list = moves as Array<{ id: string; x: number; y: number }>;\n\t\t\trequireEditable();\n\t\t\tawait apply({ type: 'moveNodes', moves: pinned(list.map((m) => ({ id: m.id, position: [m.x, m.y] as const }))) }, 'Move');\n\t\t\treturn { moved: moves };\n\t\t}\n\t};\n\n\tconst undo: AgentTool = {\n\t\tname: 'undo_diagram',\n\t\ttitle: 'Undo a diagram edit',\n\t\tdescription: 'Undoes the last diagram edit. Returns whether something was undone and its label.',\n\t\tinputSchema: object({}),\n\t\tannotations: { title: 'Undo a diagram edit', ...WRITE },\n\t\thandler: async () => {\n\t\t\tconst label = controller.getState().undoLabel;\n\t\t\tconst undone = controller.undo();\n\t\t\treturn { undone, ...(undone ? { label } : {}) };\n\t\t}\n\t};\n\n\tconst redo: AgentTool = {\n\t\tname: 'redo_diagram',\n\t\ttitle: 'Redo a diagram edit',\n\t\tdescription: 'Redoes the last undone diagram edit. Returns whether something was redone and its label.',\n\t\tinputSchema: object({}),\n\t\tannotations: { title: 'Redo a diagram edit', ...WRITE },\n\t\thandler: async () => {\n\t\t\tconst label = controller.getState().redoLabel;\n\t\t\tconst redone = controller.redo();\n\t\t\treturn { redone, ...(redone ? { label } : {}) };\n\t\t}\n\t};\n\n\tconst fieldsOf = (kind: 'node' | 'edge' | 'diagram') => {\n\t\tconst ui = familyUI(doc().family);\n\t\treturn kind === 'node' ? ui?.nodeFields : kind === 'edge' ? ui?.edgeFields : ui?.diagramFields;\n\t};\n\n\tconst setNodeData: AgentTool = {\n\t\tname: 'set_diagram_node_data',\n\t\ttitle: 'Set a node\\'s data fields',\n\t\tdescription:\n\t\t\t'Sets values of a node\\'s data fields: a pie slice\\'s value, a Gantt task\\'s start and end, a quadrant point\\'s x and y, a journey task\\'s score, an XY series\\' values, a C4 element\\'s technology… Field keys and types are listed in get_diagram \"fields.node\"; null clears a field. Returns the node with its data.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tid: ID,\n\t\t\t\tdata: { type: 'object', additionalProperties: FIELD_VALUE, description: 'Field key → value, e.g. {\"value\": 40} or {\"x\": 0.3, \"y\": 0.8}. null clears the field.' }\n\t\t\t},\n\t\t\t['id', 'data']\n\t\t),\n\t\tannotations: { title: 'Set a node\\'s data fields', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, data }) => {\n\t\t\trequireEditable();\n\t\t\tconst node = doc().graph.nodes.find((n) => n.id === id);\n\t\t\tif (!node) throw new Error(`There is no node \"${id}\"; call get_diagram for the node ids.`);\n\t\t\tconst patch = checkFieldData(fieldsOf('node'), data, `a ${doc().family} node`, node.shape);\n\t\t\tconst d = await apply({ type: 'setNodeData', id, data: patch }, 'Edit data');\n\t\t\tconst after = d.graph.nodes.find((n) => n.id === id);\n\t\t\treturn { node: { ...nodeOut(d, id), data: pickFieldData(fieldsOf('node'), after?.data) } };\n\t\t}\n\t};\n\n\tconst setEdgeData: AgentTool = {\n\t\tname: 'set_diagram_edge_data',\n\t\ttitle: 'Set an edge\\'s data fields',\n\t\tdescription:\n\t\t\t'Sets values of an edge\\'s data fields: a sequence message\\'s activation, a C4 relation\\'s technology, an architecture edge\\'s sides… Field keys and types are listed in get_diagram \"fields.edge\"; null clears a field. Returns the edge with its data.',\n\t\tinputSchema: object(\n\t\t\t{\n\t\t\t\tid: EDGE_ID,\n\t\t\t\tdata: { type: 'object', additionalProperties: FIELD_VALUE, description: 'Field key → value. null clears the field.' }\n\t\t\t},\n\t\t\t['id', 'data']\n\t\t),\n\t\tannotations: { title: 'Set an edge\\'s data fields', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ id, data }) => {\n\t\t\trequireEditable();\n\t\t\tif (!doc().graph.edges.some((e) => e.id === id)) throw new Error(`There is no edge \"${id}\"; call get_diagram for the edge ids.`);\n\t\t\tconst patch = checkFieldData(fieldsOf('edge'), data, `a ${doc().family} edge`);\n\t\t\tconst d = await apply({ type: 'setEdgeData', id, data: patch }, 'Edit data');\n\t\t\tconst edge = d.graph.edges.find((e) => e.id === id);\n\t\t\treturn { edge: { id, ...(edge ? { source: edge.source, target: edge.target } : {}), data: pickFieldData(fieldsOf('edge'), edge?.data) } };\n\t\t}\n\t};\n\n\tconst setSettings: AgentTool = {\n\t\tname: 'set_diagram_settings',\n\t\ttitle: 'Change diagram settings',\n\t\tdescription:\n\t\t\t'Sets diagram-level fields: a pie chart\\'s showData, a Gantt chart\\'s dateFormat and excludes, a quadrant chart\\'s axis labels, an XY chart\\'s categories and axis range, sequence autonumbering… Field keys and types are listed in get_diagram \"fields.diagram\"; null clears a field. Returns all settings.',\n\t\tinputSchema: object(\n\t\t\t{ data: { type: 'object', additionalProperties: FIELD_VALUE, description: 'Field key → value. null clears the field.' } },\n\t\t\t['data']\n\t\t),\n\t\tannotations: { title: 'Change diagram settings', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ data }) => {\n\t\t\trequireEditable();\n\t\t\tconst patch = checkFieldData(fieldsOf('diagram'), data, `a ${doc().family} diagram`);\n\t\t\tconst d = await apply({ type: 'setGraphData', data: patch }, 'Edit settings');\n\t\t\treturn { settings: pickFieldData(fieldsOf('diagram'), d.graph.data) };\n\t\t}\n\t};\n\n\tconst setTitle: AgentTool = {\n\t\tname: 'set_diagram_title',\n\t\ttitle: 'Set the diagram title',\n\t\tdescription: 'Sets the title shown above the diagram (a title statement, or frontmatter for families without one), or removes it with null. Returns the title.',\n\t\tinputSchema: object({ title: { type: ['string', 'null'], description: 'The new title, or null to remove it.' } }, ['title']),\n\t\tannotations: { title: 'Set the diagram title', ...WRITE_IDEMPOTENT },\n\t\thandler: async ({ title }) => {\n\t\t\trequireEditable();\n\t\t\tconst d = await apply({ type: 'setTitle', title: title === null || title === '' ? undefined : title }, 'Set title');\n\t\t\treturn { title: d.graph.title ?? null };\n\t\t}\n\t};\n\n\tconst renderDiagram: AgentTool = {\n\t\tname: 'render_diagram',\n\t\ttitle: 'Render the diagram',\n\t\tdescription:\n\t\t\t'Draws the diagram without changing it, to check it visually: standalone SVG text by default, or a PNG image (format \"png\") when the host can rasterize. Positions match the canvas.',\n\t\tinputSchema: object({\n\t\t\tformat: { type: 'string', enum: ['svg', 'png'], description: 'svg (default) or png (only when the host supports it).' },\n\t\t\tmaxWidth: { type: 'number', minimum: 16, description: 'Fit within this width in px (default: natural size).' },\n\t\t\tmaxHeight: { type: 'number', minimum: 16, description: 'Fit within this height in px (default: natural size).' }\n\t\t}),\n\t\tannotations: { title: 'Render the diagram', ...READ },\n\t\thandler: async ({ format = 'svg', maxWidth, maxHeight }) => {\n\t\t\tif (doc().tier !== 'editable') throw new Error(`A ${doc().family} diagram is kept as Mermaid text and cannot be drawn here; use export_diagram_mermaid.`);\n\t\t\tconst drawn = options.renderSvg!({ maxWidth, maxHeight });\n\t\t\tif (format === 'png') {\n\t\t\t\tif (!options.rasterize) throw new Error('This host cannot make PNG images; call render_diagram with format \"svg\".');\n\t\t\t\tconst image = await options.rasterize(drawn.svg, { width: drawn.width, height: drawn.height });\n\t\t\t\treturn { content: [{ type: 'image', data: image.data, mimeType: image.mimeType }] };\n\t\t\t}\n\t\t\treturn { format: 'svg', width: drawn.width, height: drawn.height, svg: drawn.svg };\n\t\t}\n\t};\n\n\tconst reads = [getDiagram, exportMermaid, findDiagrams, ...(options.renderSvg ? [renderDiagram] : [])];\n\tconst writes = [loadMermaid, addNode, removeElements, renameNode, setShape, setStyle, setNodeData, connect, removeEdges, labelEdge, setEdgeData, reconnect, setDirection, setTitle, setSettings, group, ungroup, move, undo, redo];\n\n\treturn {\n\t\tid: options.id ?? 'diagram',\n\t\tnamespace: options.namespace,\n\t\ttools: () => (readOnlyReason() ? reads : [...reads, ...writes]),\n\t\tsubscribe: (notify) => {\n\t\t\tlet last = readOnlyReason();\n\t\t\treturn controller.subscribe(() => {\n\t\t\t\tconst now = readOnlyReason();\n\t\t\t\tif (now !== last) {\n\t\t\t\t\tlast = now;\n\t\t\t\t\tnotify();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}\n","import { parseMermaid } from \"./document.js\";\nimport type { DiagramGraph } from \"./types.js\";\n\n/** What a frame shows, for telling frames apart and finding what each one adds. */\nfunction signature(graph: DiagramGraph): string {\n return JSON.stringify([\n graph.title ?? \"\",\n graph.nodes.map((n) => [n.id, n.label, n.parentId ?? \"\", n.data ?? null]),\n graph.edges.map((e) => [e.source, e.target, e.label ?? \"\"]),\n graph.groups.map((g) => [g.id, g.label, g.parentId ?? \"\"]),\n graph.data ?? null,\n ]);\n}\n\nconst isMeta = (line: string) => /^\\s*%%\\s*@(?:pixerate|ursula)\\b/.test(line);\n\n/** Body lines (after the header), without the layout comment and trailing blanks. */\nfunction bodyLines(lines: string[], headerIndex: number): string[] {\n const body = lines.slice(headerIndex + 1).filter((l) => !isMeta(l));\n while (body.length && !body[body.length - 1]!.trim()) body.pop();\n return body;\n}\n\nfunction headerIndexOf(lines: string[], header: string): number {\n const wanted = header.trim();\n return wanted ? lines.findIndex((l) => l.trim() === wanted) : -1;\n}\n\nexport interface DraftOptions {\n /** Most frames to play; longer drafts are sampled evenly (default 40). */\n maxFrames?: number;\n}\n\n/**\n * Splits writing `target` over `current` into frames that grow the diagram\n * statement by statement, so a whole-diagram write can be played as the\n * diagram being drawn. Each frame is a valid prefix of `target` (it parses\n * as the same family without diagnostics) that shows something new; the last\n * frame is `target` itself.\n *\n * Returns null when `target` does not extend `current` (some existing line\n * changes), when the family changes, or when there is nothing to grow: the\n * write is then committed in one step.\n */\nexport function draftFrames(\n current: string,\n target: string,\n options: DraftOptions = {},\n): string[] | null {\n const maxFrames = Math.max(2, options.maxFrames ?? 40);\n const now = parseMermaid(current);\n const goal = parseMermaid(target);\n if (goal.family !== now.family || goal.diagnostics.length) return null;\n\n const eol = goal.eol;\n const lines = target.split(/\\r?\\n/);\n const at = headerIndexOf(lines, goal.header);\n if (at < 0) return null;\n const currentLines = current.split(/\\r?\\n/);\n const currentAt = headerIndexOf(currentLines, now.header);\n if (currentAt < 0) return null;\n\n // The target must keep every current body line, in order, as its start.\n const have = bodyLines(currentLines, currentAt);\n const body = lines\n .map((line, index) => ({ line, index }))\n .slice(at + 1)\n .filter(({ line }) => !isMeta(line));\n for (let i = 0; i < have.length; i++) {\n if (body[i]?.line !== have[i]) return null;\n }\n\n const frames: string[] = [];\n let last = signature(now.graph);\n for (let i = have.length; i < body.length; i++) {\n const end = body[i]!.index;\n const candidate = lines.slice(0, end + 1).join(eol) + eol;\n const doc = parseMermaid(candidate);\n if (doc.family !== goal.family || doc.diagnostics.length) continue;\n const sig = signature(doc.graph);\n if (sig === last) continue;\n frames.push(candidate);\n last = sig;\n }\n if (!frames.length) return null;\n // The last frame is the target exactly (its trailing lines and metadata included).\n if (last === signature(goal.graph)) frames[frames.length - 1] = target;\n else frames.push(target);\n\n if (frames.length <= maxFrames) return frames;\n const sampled: string[] = [];\n for (let k = 1; k <= maxFrames; k++) {\n sampled.push(frames[Math.round((k * frames.length) / maxFrames) - 1]!);\n }\n return sampled;\n}\n\n/** Ids of nodes and groups in `after` that are new or changed since `before`. */\nexport function changedItems(before: DiagramGraph, after: DiagramGraph): string[] {\n const old = new Map<string, string>();\n for (const n of before.nodes) old.set(n.id, JSON.stringify([n.label, n.parentId, n.data]));\n for (const g of before.groups) old.set(g.id, JSON.stringify([g.label, g.parentId]));\n const out: string[] = [];\n for (const n of after.nodes)\n if (old.get(n.id) !== JSON.stringify([n.label, n.parentId, n.data])) out.push(n.id);\n for (const g of after.groups)\n if (old.get(g.id) !== JSON.stringify([g.label, g.parentId])) out.push(g.id);\n return out;\n}\n","// Acting out diagram edits follows Ursula's remote control (src/remote/commands.ts `planCommand`,\n// src/remote/draft.ts): a pure planner turns each edit into cursor steps on the canvas, and a\n// whole-diagram write is drawn statement by statement. Shared pieces live in animation/presence.\nimport {\n\tPerformanceQueue,\n\tPresenceStore,\n\trealPresenceClock,\n\ttweenPresenceCursor,\n\ttype PresenceActorInfo,\n\ttype PresenceClock\n} from '../animation/presence.js';\nimport type { DiagramController } from './controller.js';\nimport { validateDiagramEdit } from './controller.js';\nimport { applyEdits, parseMermaid } from './document.js';\nimport { changedItems, draftFrames } from './draft.js';\nimport type { EditOp, MermaidDocument, Point } from './types.js';\n\n/** Where things are drawn on the canvas (absolute top-left and size), e.g. `mermaidToCanvas(...).layout`. */\nexport interface DiagramPlacement {\n\tpositions: Readonly<Record<string, Point>>;\n\tsizes: Readonly<Record<string, { width: number; height: number }>>;\n}\n\nexport type DiagramPerformanceStep =\n\t| { type: 'move'; to: { x: number; y: number }; ms: number }\n\t| { type: 'say'; text: string; ms: number }\n\t| { type: 'type'; text: string; ms: number }\n\t| { type: 'press' }\n\t| { type: 'release' }\n\t/** Drag a node (and the cursor) so its top-left ends at `to`; the node follows live. */\n\t| { type: 'drag'; id: string; to: { x: number; y: number }; position: Point; ms: number }\n\t| { type: 'commit' };\n\nconst center = (place: DiagramPlacement, id: string) => {\n\tconst p = place.positions[id];\n\tif (!p) return null;\n\tconst s = place.sizes[id] ?? { width: 160, height: 56 };\n\treturn { x: p[0] + s.width / 2, y: p[1] + s.height / 2 };\n};\n\n/** Bottom-right of everything drawn, where a node without a better place appears. */\nfunction freeSpot(place: DiagramPlacement): { x: number; y: number } {\n\tlet maxX = 0;\n\tlet maxY = 0;\n\tlet any = false;\n\tfor (const [id, [x, y]] of Object.entries(place.positions)) {\n\t\tconst s = place.sizes[id] ?? { width: 160, height: 56 };\n\t\tmaxX = Math.max(maxX, x + s.width);\n\t\tmaxY = Math.max(maxY, y + s.height);\n\t\tany = true;\n\t}\n\treturn any ? { x: maxX + 60, y: maxY / 2 } : { x: 80, y: 40 };\n}\n\nconst NEW_NODE = { width: 160, height: 56 };\n\n/**\n * Where a new node's centre goes beside `from`: one rank further along the diagram's\n * direction, then stepped sideways until it overlaps nothing drawn.\n */\nfunction besideFree(place: DiagramPlacement, from: { x: number; y: number }, direction: string): { x: number; y: number } {\n\tconst d = direction.toUpperCase();\n\tconst along = d === 'LR' ? { x: 1, y: 0 } : d === 'RL' ? { x: -1, y: 0 } : d === 'BT' ? { x: 0, y: -1 } : { x: 0, y: 1 };\n\tconst side = { x: along.y, y: along.x };\n\tconst rects = Object.entries(place.positions).map(([id, [x, y]]) => ({ x, y, ...(place.sizes[id] ?? NEW_NODE) }));\n\tconst free = (c: { x: number; y: number }) =>\n\t\t!rects.some((r) => c.x - NEW_NODE.width / 2 - 20 < r.x + r.width && c.x + NEW_NODE.width / 2 + 20 > r.x && c.y - NEW_NODE.height / 2 - 20 < r.y + r.height && c.y + NEW_NODE.height / 2 + 20 > r.y);\n\tconst base = { x: from.x + along.x * 240, y: from.y + along.y * 140 };\n\tfor (let i = 0; i < 24; i++) {\n\t\tconst k = i === 0 ? 0 : (i % 2 ? 1 : -1) * Math.ceil(i / 2);\n\t\tconst c = { x: base.x + side.x * k * 200, y: base.y + side.y * k * 90 };\n\t\tif (free(c)) return c;\n\t}\n\treturn base;\n}\n\n/**\n * Plans how an agent acts out one edit on the canvas: where the cursor goes, what it\n * clicks, drags or types, and when the edit commits. Pure: the same document, placement\n * and edit give the same steps. `next` is the edit that follows in the same call (a new\n * node is placed beside the node an edge will connect it from).\n */\nexport function planDiagramEdit(doc: MermaidDocument, place: DiagramPlacement, op: EditOp, speed = 1, next?: EditOp): DiagramPerformanceStep[] {\n\tconst ms = (n: number) => Math.round(n / Math.max(0.1, speed));\n\tconst at = (id: string) => center(place, id);\n\tconst click = (to: { x: number; y: number } | null): DiagramPerformanceStep[] =>\n\t\tto ? [{ type: 'move', to, ms: ms(380) }, { type: 'press' }, { type: 'release' }] : [];\n\tconst edgeMid = (id: string) => {\n\t\tconst e = doc.graph.edges.find((x) => x.id === id);\n\t\tconst a = e && at(e.source);\n\t\tconst b = e && at(e.target);\n\t\treturn a && b ? { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 } : null;\n\t};\n\tswitch (op.type) {\n\t\tcase 'addNode': {\n\t\t\tconst from = next?.type === 'addEdge' && next.edge.target === op.node.id ? at(next.edge.source) : null;\n\t\t\tconst to = op.position\n\t\t\t\t? { x: op.position[0] + 80, y: op.position[1] + 28 }\n\t\t\t\t: from\n\t\t\t\t\t? besideFree(place, from, doc.graph.direction)\n\t\t\t\t\t: freeSpot(place);\n\t\t\tconst label = op.node.label ?? op.node.id;\n\t\t\treturn [...click(to), { type: 'type', text: label, ms: ms(Math.min(900, 60 * label.length + 200)) }, { type: 'commit' }];\n\t\t}\n\t\tcase 'addEdge': {\n\t\t\tconst a = at(op.edge.source);\n\t\t\tconst b = at(op.edge.target) ?? (a ? { x: a.x + 260, y: a.y } : null);\n\t\t\tif (!a || !b) return [{ type: 'commit' }];\n\t\t\treturn [\n\t\t\t\t{ type: 'move', to: a, ms: ms(380) },\n\t\t\t\t{ type: 'press' },\n\t\t\t\t{ type: 'move', to: b, ms: ms(520) },\n\t\t\t\t{ type: 'release' },\n\t\t\t\t...(op.edge.label ? [{ type: 'type' as const, text: op.edge.label, ms: ms(500) }] : []),\n\t\t\t\t{ type: 'commit' }\n\t\t\t];\n\t\t}\n\t\tcase 'moveNodes': {\n\t\t\tconst steps: DiagramPerformanceStep[] = [];\n\t\t\tfor (const move of op.moves) {\n\t\t\t\tconst from = at(move.id);\n\t\t\t\tconst size = place.sizes[move.id] ?? { width: 160, height: 56 };\n\t\t\t\tif (!from) continue;\n\t\t\t\tsteps.push(\n\t\t\t\t\t{ type: 'move', to: from, ms: ms(380) },\n\t\t\t\t\t{ type: 'press' },\n\t\t\t\t\t{ type: 'drag', id: move.id, to: { x: move.position[0] + size.width / 2, y: move.position[1] + size.height / 2 }, position: move.position, ms: ms(520) },\n\t\t\t\t\t{ type: 'release' }\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn [...steps, { type: 'commit' }];\n\t\t}\n\t\tcase 'renameNode':\n\t\tcase 'renameGroup':\n\t\t\treturn [...click(at(op.id)), { type: 'press' }, { type: 'release' }, { type: 'type', text: op.label, ms: ms(Math.min(900, 60 * op.label.length + 200)) }, { type: 'commit' }];\n\t\tcase 'removeNodes':\n\t\t\treturn [...click(at(op.ids[0]!)), { type: 'say', text: 'Delete', ms: ms(300) }, { type: 'commit' }];\n\t\tcase 'removeEdges':\n\t\t\treturn [...click(edgeMid(op.ids[0]!)), { type: 'say', text: 'Delete', ms: ms(300) }, { type: 'commit' }];\n\t\tcase 'setEdgeLabel':\n\t\t\treturn [...click(edgeMid(op.id)), { type: 'type', text: op.label, ms: ms(500) }, { type: 'commit' }];\n\t\tcase 'reconnectEdge': {\n\t\t\tconst a = at(op.source);\n\t\t\tconst b = at(op.target);\n\t\t\treturn [...click(edgeMid(op.id)), ...(a && b ? [{ type: 'move' as const, to: a, ms: ms(300) }, { type: 'press' as const }, { type: 'move' as const, to: b, ms: ms(500) }, { type: 'release' as const }] : []), { type: 'commit' }];\n\t\t}\n\t\tcase 'setNodeShape':\n\t\tcase 'setNodeStyles':\n\t\tcase 'setNodeClasses':\n\t\tcase 'setNodeLink':\n\t\tcase 'setNodeData':\n\t\tcase 'resizeNode':\n\t\t\treturn [...click(at(op.id)), { type: 'commit' }];\n\t\tcase 'groupNodes': {\n\t\t\tconst points = op.nodeIds.map(at).filter((p): p is { x: number; y: number } => !!p);\n\t\t\tif (!points.length) return [{ type: 'commit' }];\n\t\t\tconst min = { x: Math.min(...points.map((p) => p.x)) - 90, y: Math.min(...points.map((p) => p.y)) - 40 };\n\t\t\tconst max = { x: Math.max(...points.map((p) => p.x)) + 90, y: Math.max(...points.map((p) => p.y)) + 40 };\n\t\t\treturn [{ type: 'move', to: min, ms: ms(380) }, { type: 'press' }, { type: 'move', to: max, ms: ms(520) }, { type: 'release' }, { type: 'type', text: op.label, ms: ms(500) }, { type: 'commit' }];\n\t\t}\n\t\tdefault:\n\t\t\treturn [{ type: 'commit' }];\n\t}\n}\n\nexport interface DiagramDirectorOptions {\n\t/** Where nodes are drawn now, e.g. `() => mermaidToCanvas(controller.getDocument()).layout`. */\n\tplacement: () => DiagramPlacement;\n\tclock?: PresenceClock;\n\t/** 1 = natural speed; 2 = twice as fast. */\n\tspeed?: number;\n\t/** Hide each actor's cursor this long after its last call (ms, default 1500; 0 keeps it). */\n\tlinger?: number;\n\t/** Most frames when a whole-diagram write is drawn statement by statement (default 40). */\n\tmaxDraftFrames?: number;\n}\n\nexport type DiagramPerformanceResult = { ok: true; document: MermaidDocument } | { ok: false; message: string };\n\n/**\n * Acts out agent edits on a Mermaid diagram: each actor's cursor moves to the node or edge,\n * clicks, drags, connects and types, the edit lands, and everything one call changes is one\n * undo step, while the user keeps editing. Calls queue per actor. A batch is checked against\n * the diagram before the cursor moves; if the diagram changed underneath and an edit no\n * longer applies, the call's earlier edits are rolled back and the call fails with a sentence.\n * A whole-diagram write (`playSource`) is drawn statement by statement.\n */\nexport class DiagramDirector {\n\treadonly presence: PresenceStore;\n\tprivate readonly clock: PresenceClock;\n\tprivate readonly queue: PerformanceQueue;\n\n\tconstructor(\n\t\tprivate readonly controller: DiagramController,\n\t\tpresence: PresenceStore,\n\t\tprivate readonly options: DiagramDirectorOptions\n\t) {\n\t\tthis.presence = presence;\n\t\tthis.clock = options.clock ?? realPresenceClock;\n\t\tthis.queue = new PerformanceQueue(presence, {\n\t\t\tclock: this.clock,\n\t\t\tlinger: options.linger,\n\t\t\tbeginGesture: (key) => controller.beginGesture(key),\n\t\t\tendGesture: () => controller.endGesture()\n\t\t});\n\t}\n\n\t/** Acts out `ops` in order for `actor` as one undo step. */\n\tperform(actor: PresenceActorInfo, ops: readonly EditOp[], label = 'Edit diagram'): Promise<DiagramPerformanceResult> {\n\t\t// All-or-nothing: the whole batch must apply before the cursor moves.\n\t\tlet check = this.controller.getDocument();\n\t\tif (this.controller.getState().readOnly) return Promise.resolve({ ok: false, message: 'The diagram is read-only.' });\n\t\tfor (const op of ops) {\n\t\t\tconst problem = validateDiagramEdit(check, op);\n\t\t\tif (problem) return Promise.resolve({ ok: false, message: problem });\n\t\t\tcheck = applyEdits(check, [op]);\n\t\t}\n\t\treturn this.queue.run(actor, async () => {\n\t\t\tconst mark = this.controller.history.mark();\n\t\t\tconst fail = (message: string): DiagramPerformanceResult => {\n\t\t\t\tthis.controller.history.revertTo(mark);\n\t\t\t\treturn { ok: false, message };\n\t\t\t};\n\t\t\t// Placing or dragging stores positions; pin the rest of an auto-laid diagram first so\n\t\t\t// it does not shift around them (see GOTCHAS.md).\n\t\t\tif (ops.some((op) => op.type === 'addNode' || op.type === 'moveNodes')) {\n\t\t\t\tconst problem = this.pin(label);\n\t\t\t\tif (problem) return fail(problem);\n\t\t\t}\n\t\t\tfor (let i = 0; i < ops.length; i++) {\n\t\t\t\tconst op = ops[i]!;\n\t\t\t\tconst steps = planDiagramEdit(this.controller.getDocument(), this.options.placement(), op, this.options.speed, ops[i + 1]);\n\t\t\t\tfor (const step of steps) {\n\t\t\t\t\tconst problem = await this.act(actor.id, step, op, label);\n\t\t\t\t\tif (problem) return fail(problem);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ok: true, document: this.controller.getDocument() };\n\t\t});\n\t}\n\n\t/**\n\t * Replaces the diagram with `source`, drawing it statement by statement when it extends\n\t * what is there (Ursula's draft playback): each frame lands, the cursor points at what\n\t * appeared, and the whole write is one undo step. Otherwise it lands at once.\n\t */\n\tplaySource(actor: PresenceActorInfo, source: string, label = 'Replace diagram'): Promise<DiagramPerformanceResult> {\n\t\tif (this.controller.getState().readOnly) return Promise.resolve({ ok: false, message: 'The diagram is read-only.' });\n\t\treturn this.queue.run(actor, async () => {\n\t\t\tconst frames = draftFrames(this.controller.getSource(), source, { maxFrames: this.options.maxDraftFrames }) ?? [source];\n\t\t\tconst pause = Math.round(260 / Math.max(0.1, this.options.speed ?? 1));\n\t\t\tfor (const frame of frames) {\n\t\t\t\tconst before = this.controller.getDocument().graph;\n\t\t\t\tconst result = this.controller.apply({ type: 'replaceSource', source: frame }, label);\n\t\t\t\tif (!result.ok) return result;\n\t\t\t\tconst fresh = changedItems(before, result.document.graph);\n\t\t\t\tconst target = fresh.length ? center(this.options.placement(), fresh[fresh.length - 1]!) : null;\n\t\t\t\tconst actorNow = this.presence.get(actor.id);\n\t\t\t\tif (target && actorNow) await tweenPresenceCursor(this.presence, this.clock, actor.id, actorNow, target, pause);\n\t\t\t\telse await this.clock.wait(pause);\n\t\t\t}\n\t\t\treturn { ok: true, document: this.controller.getDocument() };\n\t\t});\n\t}\n\n\t/** Stores the drawn position of every node and group that has none yet. */\n\tprivate pin(label: string): string | null {\n\t\tconst doc = this.controller.getDocument();\n\t\tconst stored = doc.meta.layout ?? {};\n\t\tconst known = new Set([...doc.graph.nodes, ...doc.graph.groups].map((x) => x.id));\n\t\tconst moves = Object.entries(this.options.placement().positions)\n\t\t\t.filter(([id]) => known.has(id) && !stored[id])\n\t\t\t.map(([id, position]) => ({ id, position }));\n\t\tif (!moves.length) return null;\n\t\tconst result = this.controller.apply({ type: 'moveNodes', moves }, label);\n\t\treturn result.ok ? null : result.message;\n\t}\n\n\t/** Runs one step; returns a problem sentence when the edit can no longer be applied. */\n\tprivate async act(id: string, step: DiagramPerformanceStep, op: EditOp, label: string): Promise<string | null> {\n\t\tconst where = () => {\n\t\t\tconst a = this.presence.get(id);\n\t\t\treturn a ? { x: a.x, y: a.y } : { x: 0, y: 0 };\n\t\t};\n\t\tswitch (step.type) {\n\t\t\tcase 'say':\n\t\t\t\tthis.presence.set({ id, message: step.text });\n\t\t\t\tawait this.clock.wait(step.ms);\n\t\t\t\treturn null;\n\t\t\tcase 'type': {\n\t\t\t\t// The note grows as if typed.\n\t\t\t\tconst chars = [...step.text];\n\t\t\t\tconst each = step.ms / Math.max(1, chars.length);\n\t\t\t\tfor (let i = 1; i <= chars.length; i++) {\n\t\t\t\t\tthis.presence.set({ id, message: chars.slice(0, i).join('') });\n\t\t\t\t\tawait this.clock.wait(each);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tcase 'move':\n\t\t\t\tawait tweenPresenceCursor(this.presence, this.clock, id, where(), step.to, step.ms);\n\t\t\t\treturn null;\n\t\t\tcase 'press':\n\t\t\t\tthis.presence.set({ id, pressed: true });\n\t\t\t\treturn null;\n\t\t\tcase 'release':\n\t\t\t\tthis.presence.set({ id, pressed: false });\n\t\t\t\treturn null;\n\t\t\tcase 'drag': {\n\t\t\t\tconst start = this.options.placement().positions[step.id];\n\t\t\t\tlet problem: string | null = null;\n\t\t\t\tawait tweenPresenceCursor(this.presence, this.clock, id, where(), step.to, step.ms, (e) => {\n\t\t\t\t\tif (problem || !start) return;\n\t\t\t\t\tconst position: Point = [start[0] + (step.position[0] - start[0]) * e, start[1] + (step.position[1] - start[1]) * e];\n\t\t\t\t\tconst result = this.controller.apply({ type: 'moveNodes', moves: [{ id: step.id, position }] }, label);\n\t\t\t\t\tif (!result.ok) problem = result.message;\n\t\t\t\t});\n\t\t\t\treturn problem;\n\t\t\t}\n\t\t\tcase 'commit': {\n\t\t\t\t// A new node lands where the cursor clicked.\n\t\t\t\tconst placed: EditOp =\n\t\t\t\t\top.type === 'addNode' && !op.position ? { ...op, position: [Math.round(where().x - 80), Math.round(where().y - 28)] } : op;\n\t\t\t\tconst result = this.controller.apply(placed, label);\n\t\t\t\treturn result.ok ? null : result.message;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Reads a document for planning without a controller (tests, previews). */\nexport function planDiagramEdits(source: string, place: DiagramPlacement, ops: readonly EditOp[], speed = 1): DiagramPerformanceStep[][] {\n\tlet doc = parseMermaid(source);\n\treturn ops.map((op, i) => {\n\t\tconst steps = planDiagramEdit(doc, place, op, speed, ops[i + 1]);\n\t\tdoc = applyEdits(doc, [op]);\n\t\treturn steps;\n\t});\n}\n"]}