callman-mcp 0.2.0 → 0.3.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 (47) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +2 -2
  3. package/dist/client/api.d.ts +1 -0
  4. package/dist/client/api.d.ts.map +1 -1
  5. package/dist/client/api.js +2 -0
  6. package/dist/client/api.js.map +1 -1
  7. package/dist/domain/diagram/build.d.ts +112 -63
  8. package/dist/domain/diagram/build.d.ts.map +1 -1
  9. package/dist/domain/diagram/build.js +49 -165
  10. package/dist/domain/diagram/build.js.map +1 -1
  11. package/dist/domain/diagram/layout.d.ts +3 -4
  12. package/dist/domain/diagram/layout.d.ts.map +1 -1
  13. package/dist/domain/diagram/layout.js +12 -45
  14. package/dist/domain/diagram/layout.js.map +1 -1
  15. package/dist/domain/diagram/patch.d.ts +298 -80
  16. package/dist/domain/diagram/patch.d.ts.map +1 -1
  17. package/dist/domain/diagram/patch.js +60 -273
  18. package/dist/domain/diagram/patch.js.map +1 -1
  19. package/dist/domain/diagram/style.d.ts +4 -12
  20. package/dist/domain/diagram/style.d.ts.map +1 -1
  21. package/dist/domain/diagram/style.js +8 -84
  22. package/dist/domain/diagram/style.js.map +1 -1
  23. package/dist/domain/diagram/summarize.d.ts +25 -24
  24. package/dist/domain/diagram/summarize.d.ts.map +1 -1
  25. package/dist/domain/diagram/summarize.js +20 -35
  26. package/dist/domain/diagram/summarize.js.map +1 -1
  27. package/dist/domain/requestIntegrations.d.ts +20 -20
  28. package/dist/domain/requestShape.d.ts +32 -32
  29. package/dist/resources/docs/diagramSchema.d.ts +1 -1
  30. package/dist/resources/docs/diagramSchema.d.ts.map +1 -1
  31. package/dist/resources/docs/diagramSchema.js +55 -10
  32. package/dist/resources/docs/diagramSchema.js.map +1 -1
  33. package/dist/resources/generated/aiPacks.d.ts +1 -1
  34. package/dist/resources/generated/aiPacks.js +2 -2
  35. package/dist/tools/diagrams.d.ts.map +1 -1
  36. package/dist/tools/diagrams.js +244 -42
  37. package/dist/tools/diagrams.js.map +1 -1
  38. package/dist-cjs/client/api.js +2 -0
  39. package/dist-cjs/domain/diagram/build.js +53 -169
  40. package/dist-cjs/domain/diagram/layout.js +11 -44
  41. package/dist-cjs/domain/diagram/patch.js +57 -270
  42. package/dist-cjs/domain/diagram/style.js +11 -88
  43. package/dist-cjs/domain/diagram/summarize.js +22 -35
  44. package/dist-cjs/resources/docs/diagramSchema.js +55 -10
  45. package/dist-cjs/resources/generated/aiPacks.js +2 -2
  46. package/dist-cjs/tools/diagrams.js +240 -38
  47. package/package.json +2 -2
@@ -1,23 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildDiagramDocument = exports.edgeStyleFor = exports.assertNotConnector = exports.parentWarning = exports.containerTypes = exports.isContainerType = exports.suggestComponentTypes = exports.diagramInputSchema = exports.diagramEdgeInputSchema = exports.diagramNodeInputSchema = exports.EDGE_SEMANTICS_INPUT = void 0;
3
+ exports.buildDiagramDocument = exports.assertNotConnector = exports.suggestComponentTypes = exports.parentWarning = exports.isContainerType = exports.edgeStyleFor = exports.containerTypes = exports.toToolError = exports.diagramInputSchema = exports.diagramCanvasInputSchema = exports.diagramEdgeInputSchema = exports.diagramNodeInputSchema = exports.SIDES = exports.ROUTINGS = exports.EDGE_SEMANTICS_INPUT = void 0;
4
4
  const callman_core_1 = require("callman-core");
5
+ Object.defineProperty(exports, "containerTypes", { enumerable: true, get: function () { return callman_core_1.containerTypes; } });
6
+ Object.defineProperty(exports, "edgeStyleFor", { enumerable: true, get: function () { return callman_core_1.edgeStyleFor; } });
7
+ Object.defineProperty(exports, "isContainerType", { enumerable: true, get: function () { return callman_core_1.isContainerType; } });
8
+ Object.defineProperty(exports, "parentWarning", { enumerable: true, get: function () { return callman_core_1.parentWarning; } });
9
+ Object.defineProperty(exports, "suggestComponentTypes", { enumerable: true, get: function () { return callman_core_1.suggestComponentTypes; } });
5
10
  const zod_1 = require("zod");
6
11
  const errors_js_1 = require("../../client/errors.js");
7
- const ids_js_1 = require("../../lib/ids.js");
8
12
  const strict_js_1 = require("../../lib/strict.js");
9
13
  const style_js_1 = require("./style.js");
10
14
  // From the COMPACT diagram a model writes (typed nodes with labels, edges
11
- // with a meaning) to the DiagramDocument the Diagram Studio renders. Ids are
12
- // short and stable (n1…, e1…), component types are checked against the
13
- // shared catalog (unknown → suggestions, never a silent blank shape), edge
14
- // meaning becomes the studio's notation (sync / async / fallback / bus), and
15
- // the result is validated with the same parser the backend and desktop use.
15
+ // with a meaning) to the DiagramDocument the Diagram Studio renders. The
16
+ // zod schemas here are the tool contract (strict keys, descriptions); the
17
+ // building itself is callman-core's — the same code the desktop's AI edits
18
+ // and the studio use — so ids, sizes, notation and validation match
19
+ // everywhere. Ids are slugs of the label (`orders-service`, `-2` on a
20
+ // clash) so a model regenerating a diagram keeps them stable.
16
21
  exports.EDGE_SEMANTICS_INPUT = ["sync", "async", "fallback", "bus"];
17
22
  const ARROWS = ["none", "classic", "classicThin", "open", "block", "diamond", "oval"];
23
+ exports.ROUTINGS = ["orthogonal", "straight", "curved", "smart"];
24
+ exports.SIDES = ["top", "right", "bottom", "left"];
18
25
  exports.diagramNodeInputSchema = (0, strict_js_1.strictObject)({
19
- id: zod_1.z.string().min(1).max(64).optional().describe("Stable id (n1, api, db…); generated when omitted"),
20
- type: zod_1.z.string().min(1).max(80).describe("Component type from callman://diagram/components, e.g. net.service, net.api-gateway, db.postgres, mq.kafka, aws.lambda, flow.process"),
26
+ id: zod_1.z.string().min(1).max(64).optional().describe("Stable id (orders, api, db…); a slug of the label when omitted"),
27
+ type: zod_1.z.string().min(1).max(80).describe("Component type from callman://diagram/components, e.g. net.service, net.api-gateway, db.postgres, mq.kafka, aws.lambda, flow.process. On a BPMN diagram: a bpmn.* type (bpmn.user-task, bpmn.exclusive-gateway, bpmn.start-event, bpmn.pool, bpmn.lane …)"),
21
28
  label: zod_1.z.string().max(200).optional(),
22
29
  parent: zod_1.z.string().max(64).optional().describe("Id of a container node this node sits inside: frame, pool.horizontal or pool.vertical"),
23
30
  position: zod_1.z.object({ x: zod_1.z.number(), y: zod_1.z.number() }).strict().optional().describe("Top-left corner; relative to the parent container when `parent` is set. Omit to auto-layout"),
@@ -26,6 +33,7 @@ exports.diagramNodeInputSchema = (0, strict_js_1.strictObject)({
26
33
  rotation: zod_1.z.number().min(-360).max(360).optional().describe("Degrees, clockwise"),
27
34
  locked: zod_1.z.boolean().optional().describe("Locked nodes cannot be moved or resized in the studio"),
28
35
  hidden: zod_1.z.boolean().optional(),
36
+ collapsed: zod_1.z.boolean().optional().describe("Containers only: drawn folded with the children hidden"),
29
37
  data: zod_1.z.record(zod_1.z.unknown()).optional().describe("Free-form metadata kept with the node (not rendered), e.g. { owner: \"team-a\", repo: \"…\" }"),
30
38
  }, "node field");
31
39
  exports.diagramEdgeInputSchema = (0, strict_js_1.strictObject)({
@@ -35,181 +43,57 @@ exports.diagramEdgeInputSchema = (0, strict_js_1.strictObject)({
35
43
  label: zod_1.z.string().max(200).optional(),
36
44
  semantic: zod_1.z.enum(exports.EDGE_SEMANTICS_INPUT).optional().describe("sync (default, solid) = request/response, async (dashed) = event, fallback (dotted) = retry/optional, bus (heavy) = message bus; automatic bus when an end is a messaging component"),
37
45
  color: style_js_1.colorInputSchema.optional().describe('Line CSS color, e.g. "#8B5CF6". Independent of `semantic`'),
38
- routing: zod_1.z.enum(["orthogonal", "straight", "curved"]).optional().describe("orthogonal (default, right-angle auto-routing), straight, curved"),
46
+ routing: zod_1.z.enum(exports.ROUTINGS).optional().describe("orthogonal (default, right-angle auto-routing), straight, curved, smart (routed around other shapes)"),
39
47
  markerEnd: zod_1.z.enum(ARROWS).optional().describe("Arrow at the target (default classicThin)"),
40
48
  markerStart: zod_1.z.enum(ARROWS).optional().describe("Arrow at the source (default none) — set it for a two-way connection"),
49
+ sourceSide: zod_1.z.enum(exports.SIDES).optional().describe("Fix which side of the source node the line leaves from (default: floating — chosen from the relative position)"),
50
+ targetSide: zod_1.z.enum(exports.SIDES).optional().describe("Fix which side of the target node the line enters"),
51
+ data: zod_1.z.record(zod_1.z.unknown()).optional().describe('Free-form metadata kept with the edge. On a BPMN diagram: { bpmn: { flow?: "sequence"|"message", conditionExpression?: string, isDefault?: boolean } }'),
41
52
  }, "edge field");
53
+ exports.diagramCanvasInputSchema = zod_1.z
54
+ .object({
55
+ background: zod_1.z.enum(["grid", "dots", "blank"]).optional(),
56
+ gridSize: zod_1.z.number().int().positive().optional(),
57
+ defaultRouting: zod_1.z.enum(exports.ROUTINGS).optional().describe("Routing for edges that do not set their own (default orthogonal)"),
58
+ })
59
+ .strict()
60
+ .optional()
61
+ .describe("Canvas background (default dots), grid size (default 16) and the default edge routing");
42
62
  exports.diagramInputSchema = zod_1.z.object({
43
63
  title: zod_1.z.string().min(1).max(200),
44
64
  nodes: zod_1.z.array(exports.diagramNodeInputSchema).min(1).max(500),
45
65
  edges: zod_1.z.array(exports.diagramEdgeInputSchema).max(2000).default([]),
46
- canvas: zod_1.z
47
- .object({ background: zod_1.z.enum(["grid", "dots", "blank"]).optional(), gridSize: zod_1.z.number().int().positive().optional() })
48
- .strict()
49
- .optional()
50
- .describe("Canvas background (default dots) and grid size (default 16)"),
66
+ canvas: exports.diagramCanvasInputSchema,
51
67
  });
52
- const suggestComponentTypes = (type, limit = 5) => {
53
- const seen = new Set();
54
- const out = [];
55
- const push = (candidate) => {
56
- if (!seen.has(candidate) && out.length < limit) {
57
- seen.add(candidate);
58
- out.push(candidate);
59
- }
60
- };
61
- for (const entry of (0, callman_core_1.searchDiagramComponents)(type))
62
- push(entry.type);
63
- // A typo usually sits at the END of the last segment ("db.postgresqlx"):
64
- // shrink it until something in the catalog contains it.
65
- const tail = (type.split(".").pop() ?? type).toLowerCase();
66
- for (let length = tail.length; length >= 3 && out.length < limit; length -= 1) {
67
- const needle = tail.slice(0, length);
68
- for (const entry of callman_core_1.DIAGRAM_COMPONENT_CATALOG) {
69
- const haystack = [entry.type, entry.label, ...(entry.aliases ?? [])].join(" ").toLowerCase();
70
- if (haystack.includes(needle))
71
- push(entry.type);
72
- }
73
- }
74
- // Still nothing: offer the same namespace ("db.*", "aws.*").
75
- const namespace = type.includes(".") ? `${type.split(".")[0].toLowerCase()}.` : null;
76
- if (namespace) {
77
- for (const entry of callman_core_1.DIAGRAM_COMPONENT_CATALOG)
78
- if (entry.type.toLowerCase().startsWith(namespace))
79
- push(entry.type);
80
- }
81
- return out;
68
+ // Core's op errors carry MCP's own codes; only the class changes.
69
+ const toToolError = (error) => {
70
+ if (error instanceof callman_core_1.DiagramOpError)
71
+ throw new errors_js_1.ToolInputError(error.code, error.message, error.data);
72
+ throw error;
82
73
  };
83
- exports.suggestComponentTypes = suggestComponentTypes;
84
- // Containers hold children through `parent`: frames and swimlane pools.
85
- const isContainerType = (type) => {
86
- const def = type ? (0, callman_core_1.getDiagramComponent)(type) : undefined;
87
- return Boolean(def?.container || def?.swimlane);
88
- };
89
- exports.isContainerType = isContainerType;
90
- const containerTypes = () => callman_core_1.DIAGRAM_COMPONENT_CATALOG.filter((entry) => entry.container || entry.swimlane).map((entry) => entry.type);
91
- exports.containerTypes = containerTypes;
92
- const parentWarning = (id, parentId, parentType) => (0, exports.isContainerType)(parentType)
93
- ? null
94
- : `Node ${id} sits inside "${parentId}" (${parentType ?? "unknown"}), which is not a container — use ${(0, exports.containerTypes)().join(", ")} to group nodes.`;
95
- exports.parentWarning = parentWarning;
96
- // Edge input → the stored edge's visual fields: the semantic's line tuple
97
- // plus an optional color.
98
- // line / arrow / line.dashed are palette connectors (0-height), not nodes.
74
+ exports.toToolError = toToolError;
99
75
  const assertNotConnector = (type, id) => {
100
- if ((0, callman_core_1.getDiagramComponent)(type)?.variant === "line") {
101
- throw new errors_js_1.ToolInputError("DIAGRAM_CONNECTOR_NOT_NODE", `"${type}" on ${id} is a connector, not a node — draw it as an edge between two nodes.`);
76
+ try {
77
+ (0, callman_core_1.assertNotConnector)(type, id);
78
+ }
79
+ catch (error) {
80
+ (0, exports.toToolError)(error);
102
81
  }
103
82
  };
104
83
  exports.assertNotConnector = assertNotConnector;
105
- const edgeStyleFor = (semantic, color) => {
106
- const base = semantic ? (0, callman_core_1.semanticStyle)(semantic) : undefined;
107
- if (!color)
108
- return base;
109
- return { ...(base ?? {}), stroke: (0, style_js_1.toTokenRef)(color) };
110
- };
111
- exports.edgeStyleFor = edgeStyleFor;
112
84
  const buildDiagramDocument = (input, options = {}) => {
113
- const strict = options.strictTypes !== false;
114
- const warnings = [];
115
- const nextNodeId = (0, ids_js_1.sequentialIds)("n", input.nodes.map((node) => node.id).filter((id) => Boolean(id)));
116
- const nextEdgeId = (0, ids_js_1.sequentialIds)("e", input.edges.map((edge) => edge.id).filter((id) => Boolean(id)));
117
- // Ids first so parents/edges can reference nodes declared later.
118
- const ids = input.nodes.map((node) => node.id ?? nextNodeId());
119
- const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
120
- if (duplicates.length > 0) {
121
- throw new errors_js_1.ToolInputError("DIAGRAM_DUPLICATE_NODE_ID", `Duplicate node ids: ${[...new Set(duplicates)].join(", ")}`);
122
- }
123
- const idSet = new Set(ids);
124
- const unknown = [];
125
- const typeOf = new Map();
126
- const nodes = input.nodes.map((node, index) => {
127
- const id = ids[index];
128
- let type = node.type.trim();
129
- const known = (0, callman_core_1.getDiagramComponent)(type);
130
- if (!known) {
131
- const suggestions = (0, exports.suggestComponentTypes)(type);
132
- if (strict)
133
- unknown.push({ type, suggestions });
134
- else {
135
- warnings.push(`Unknown component type "${type}" on ${id} → drawn as a rectangle (suggestions: ${suggestions.join(", ") || "none"})`);
136
- type = "rect";
137
- }
138
- }
139
- if (known)
140
- (0, exports.assertNotConnector)(type, id);
141
- const def = (0, callman_core_1.getDiagramComponent)(type);
142
- typeOf.set(id, type);
143
- if (node.parent && !idSet.has(node.parent)) {
144
- throw new errors_js_1.ToolInputError("DIAGRAM_PARENT_NOT_FOUND", `Node ${id} references parent "${node.parent}" which is not in nodes.`);
145
- }
146
- return {
147
- id,
148
- type,
149
- position: node.position ?? { x: 0, y: 0 },
150
- size: node.size ?? def?.defaultSize ?? { width: 140, height: 60 },
151
- ...(node.label !== undefined || !known ? { label: node.label ?? node.type } : def?.defaultLabel ? { label: def.defaultLabel } : {}),
152
- ...(node.parent ? { parentId: node.parent } : {}),
153
- ...(node.style ? { style: (0, style_js_1.applyStyleInput)(undefined, node.style) } : {}),
154
- ...(node.rotation ? { rotation: node.rotation } : {}),
155
- ...(node.locked ? { locked: true } : {}),
156
- ...(node.hidden ? { hidden: true } : {}),
157
- ...(node.data ? { data: node.data } : {}),
158
- };
159
- });
160
- for (const node of nodes) {
161
- const warning = node.parentId ? (0, exports.parentWarning)(node.id, node.parentId, typeOf.get(node.parentId)) : null;
162
- if (warning)
163
- warnings.push(warning);
164
- }
165
- if (unknown.length > 0) {
166
- throw new errors_js_1.ToolInputError("DIAGRAM_UNKNOWN_COMPONENT_TYPE", `Unknown component type(s): ${unknown.map((entry) => entry.type).join(", ")}. Read callman://diagram/components for the catalog, or pass strictTypes:false to draw them as rectangles.`, { unknownTypes: unknown });
85
+ try {
86
+ const built = (0, callman_core_1.buildDiagramFromInput)(input, {
87
+ strictTypes: options.strictTypes !== false,
88
+ existingId: options.existingId,
89
+ createdAt: options.createdAt,
90
+ idStrategy: "slug",
91
+ ...(options.kind ? { kind: options.kind } : {}),
92
+ });
93
+ return { document: built.document, warnings: built.warnings, unpositionedNodeIds: built.unpositionedNodeIds };
167
94
  }
168
- const edgeIds = new Set();
169
- const edges = input.edges.map((edge) => {
170
- const id = edge.id ?? nextEdgeId();
171
- if (edgeIds.has(id))
172
- throw new errors_js_1.ToolInputError("DIAGRAM_DUPLICATE_EDGE_ID", `Duplicate edge id: ${id}`);
173
- edgeIds.add(id);
174
- for (const endpoint of [edge.source, edge.target]) {
175
- if (!idSet.has(endpoint)) {
176
- throw new errors_js_1.ToolInputError("DIAGRAM_EDGE_ENDPOINT_NOT_FOUND", `Edge ${id} references "${endpoint}" which is not a node id.`);
177
- }
178
- }
179
- let semantic = edge.semantic;
180
- // A fresh edge into a broker reads as a bus line — the studio's own rule.
181
- if (!semantic && ((0, callman_core_1.isBusComponentType)(typeOf.get(edge.source)) || (0, callman_core_1.isBusComponentType)(typeOf.get(edge.target)))) {
182
- semantic = "bus";
183
- }
184
- const style = (0, exports.edgeStyleFor)(semantic, edge.color);
185
- return {
186
- id,
187
- source: edge.source,
188
- target: edge.target,
189
- ...(edge.label ? { label: edge.label } : {}),
190
- routing: edge.routing ?? "orthogonal",
191
- markerEnd: edge.markerEnd ?? "classicThin",
192
- ...(edge.markerStart && edge.markerStart !== "none" ? { markerStart: edge.markerStart } : {}),
193
- ...(style && (!(0, callman_core_1.isDefaultEdgeStyle)(style) || semantic === "sync" || edge.color) ? { style } : {}),
194
- };
195
- });
196
- const timestamp = options.createdAt ?? (0, callman_core_1.nowIso)();
197
- const document = (0, callman_core_1.normalizeDiagramStyles)({
198
- schemaVersion: callman_core_1.SCHEMA_VERSION,
199
- id: options.existingId ?? `doc_${Math.random().toString(36).slice(2, 10)}`,
200
- meta: { title: input.title.trim(), createdAt: timestamp, updatedAt: (0, callman_core_1.nowIso)() },
201
- canvas: { background: input.canvas?.background ?? "dots", gridSize: input.canvas?.gridSize ?? 16 },
202
- nodes,
203
- edges,
204
- });
205
- const parsed = (0, callman_core_1.safeParseDiagramDocument)(document);
206
- if (!parsed.ok) {
207
- throw new errors_js_1.ToolInputError("DIAGRAM_DOCUMENT_INVALID", `The diagram document is invalid: ${parsed.errors.join("; ")}`, { errors: parsed.errors });
95
+ catch (error) {
96
+ return (0, exports.toToolError)(error);
208
97
  }
209
- return {
210
- document: parsed.document,
211
- warnings,
212
- unpositionedNodeIds: input.nodes.filter((node) => !node.position).map((_node, index) => ids[index]),
213
- };
214
98
  };
215
99
  exports.buildDiagramDocument = buildDiagramDocument;
@@ -45,54 +45,21 @@ const loadElk = () => {
45
45
  return defaultEngine;
46
46
  };
47
47
  const layoutDocument = async (doc, options = {}) => {
48
- const algorithm = options.algorithm ?? "layered";
49
- const direction = options.direction ?? "DOWN";
50
- try {
51
- const engine = options.engine ?? (await loadElk());
52
- const graph = (0, callman_core_1.buildHierarchicalElkGraph)(doc, algorithm, { direction });
53
- const result = await engine.layout(graph);
54
- const layout = (0, callman_core_1.applyHierarchicalElkResult)(result);
55
- return { document: touch((0, callman_core_1.applyDetailedLayout)(doc, layout)), engine: "elk" };
56
- }
57
- catch (error) {
58
- return {
59
- document: touch((0, callman_core_1.gridLayout)(doc)),
60
- engine: "grid",
61
- warning: `Automatic layout fell back to a grid: ${error instanceof Error ? error.message : String(error)}`,
62
- };
63
- }
48
+ const engine = options.engine ?? (await loadElk());
49
+ const result = await (0, callman_core_1.runLayout)(doc, {
50
+ algorithm: options.algorithm ?? "layered",
51
+ direction: options.direction ?? "DOWN",
52
+ mode: options.nodeIds && options.nodeIds.length > 0 ? "selection" : "full",
53
+ nodeIds: options.nodeIds,
54
+ }, engine);
55
+ const document = result.ops.length > 0 ? (0, callman_core_1.applyDiagramOps)(doc, result.ops, { now: new Date().toISOString() }).doc : doc;
56
+ return { document, engine: result.engine, ...(result.warning ? { warning: result.warning } : {}) };
64
57
  };
65
58
  exports.layoutDocument = layoutDocument;
66
59
  // Place ONLY the given nodes (new ones from a patch) below the current
67
60
  // bounding box, in rows — nothing that already has a position moves.
68
61
  const placeNewNodes = (doc, nodeIds, gap = 40) => {
69
- const wanted = new Set(nodeIds);
70
- if (wanted.size === 0)
71
- return doc;
72
- const placed = doc.nodes.filter((node) => !wanted.has(node.id) && !node.parentId);
73
- const maxY = placed.reduce((max, node) => Math.max(max, node.position.y + node.size.height), 0);
74
- const minX = placed.reduce((min, node) => Math.min(min, node.position.x), placed.length > 0 ? Infinity : 0);
75
- let cursorX = Number.isFinite(minX) ? minX : 0;
76
- let cursorY = placed.length > 0 ? maxY + gap * 2 : 0;
77
- let rowHeight = 0;
78
- const rowLimit = 1200;
79
- const nodes = doc.nodes.map((node) => {
80
- if (!wanted.has(node.id))
81
- return node;
82
- if (cursorX + node.size.width > rowLimit && cursorX > 0) {
83
- cursorX = Number.isFinite(minX) ? minX : 0;
84
- cursorY += rowHeight + gap;
85
- rowHeight = 0;
86
- }
87
- const positioned = { ...node, position: { x: cursorX, y: cursorY } };
88
- cursorX += node.size.width + gap;
89
- rowHeight = Math.max(rowHeight, node.size.height);
90
- return positioned;
91
- });
92
- return touch({ ...doc, nodes });
62
+ const ops = (0, callman_core_1.placeNewNodesOps)(doc, nodeIds, gap);
63
+ return ops.length > 0 ? (0, callman_core_1.applyDiagramOps)(doc, ops, { now: new Date().toISOString() }).doc : doc;
93
64
  };
94
65
  exports.placeNewNodes = placeNewNodes;
95
- const touch = (doc) => ({
96
- ...doc,
97
- meta: { ...doc.meta, updatedAt: new Date().toISOString() },
98
- });