@pixerate/editor 0.9.0 → 0.11.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.
package/dist/index.cjs CHANGED
@@ -31,9 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  ColorHighlighter: () => ColorHighlighter,
34
+ DEFAULT_NODE_HEIGHT: () => DEFAULT_NODE_HEIGHT,
35
+ DEFAULT_NODE_WIDTH: () => DEFAULT_NODE_WIDTH,
36
+ DEFAULT_PLACEMENT_GAP: () => DEFAULT_PLACEMENT_GAP,
34
37
  DataSourceBinder: () => DataSourceBinder,
35
38
  DependencyGraph: () => DependencyGraph,
36
39
  DirtyTracker: () => DirtyTracker,
40
+ EDITOR_MCP_TOOLS: () => EDITOR_MCP_TOOLS,
41
+ EXPORT_EXCLUDED_SELECTORS: () => EXPORT_EXCLUDED_SELECTORS,
37
42
  EditorController: () => EditorController,
38
43
  FontSize: () => FontSize,
39
44
  FormulaEvaluator: () => FormulaEvaluator,
@@ -44,23 +49,44 @@ __export(src_exports, {
44
49
  Image: () => Image2,
45
50
  LoadingNode: () => LoadingNode,
46
51
  MARKDOWN_MENTION_SPLIT_REGEX: () => MARKDOWN_MENTION_SPLIT_REGEX,
52
+ MCP_TOOL_EDIT_CANVAS: () => MCP_TOOL_EDIT_CANVAS,
53
+ MCP_TOOL_EDIT_PROMPT: () => MCP_TOOL_EDIT_PROMPT,
54
+ MCP_TOOL_GET_EDITOR_STATE: () => MCP_TOOL_GET_EDITOR_STATE,
55
+ MCP_TOOL_VIEW_EDITOR: () => MCP_TOOL_VIEW_EDITOR,
47
56
  MENTION_REGEX: () => MENTION_REGEX,
57
+ ManualClock: () => ManualClock,
48
58
  Markdown: () => Markdown2,
49
59
  Mention: () => Mention,
50
60
  SlashCommands: () => SlashCommands,
51
61
  SmilieReplacer: () => SmilieReplacer,
52
62
  SpreadsheetController: () => SpreadsheetController,
63
+ StreamingLayoutManager: () => StreamingLayoutManager,
53
64
  TEMPLATE_REGEX: () => TEMPLATE_REGEX,
54
65
  TemplateSuggestions: () => TemplateSuggestions,
55
66
  VARIABLE_REGEX: () => VARIABLE_REGEX,
67
+ blendLayout: () => blendLayout,
68
+ boundsOf: () => boundsOf,
69
+ calculateDagreReflowDisplacement: () => calculateDagreReflowDisplacement,
70
+ calculateDirectionalDisplacement: () => calculateDirectionalDisplacement,
71
+ canvasHeight: () => canvasHeight,
72
+ centerNodes: () => centerNodes,
73
+ centre: () => centre,
56
74
  colNameToIndex: () => colNameToIndex,
75
+ computeNodesBoundingBox: () => computeNodesBoundingBox,
76
+ connectedOnly: () => connectedOnly,
77
+ createBezierTrajectory: () => createBezierTrajectory,
57
78
  createDirtyTracker: () => createDirtyTracker,
58
79
  createEditor: () => createEditor,
80
+ createFanOutTrajectory: () => createFanOutTrajectory,
59
81
  createHistoryManager: () => createHistoryManager,
60
82
  createMentionExtension: () => createMentionExtension,
61
83
  createRichTextPreset: () => createRichTextPreset,
84
+ defaultEasing: () => defaultEasing,
62
85
  defaultIsEqual: () => defaultIsEqual,
86
+ ensureLayout: () => ensureLayout,
63
87
  escapeHtml: () => escapeHtml,
88
+ estimateCanvasHeight: () => estimateCanvasHeight,
89
+ estimateNodeSize: () => estimateNodeSize,
64
90
  extractCellDependencies: () => extractCellDependencies,
65
91
  extractInstructionNames: () => extractInstructionNames,
66
92
  extractMentions: () => extractMentions,
@@ -68,29 +94,48 @@ __export(src_exports, {
68
94
  extractTemplateNames: () => extractTemplateNames,
69
95
  extractVariableNames: () => extractVariableNames,
70
96
  findHexColors: () => findHexColors,
97
+ flowSide: () => flowSide,
98
+ getCenteredNodePosition: () => getCenteredNodePosition,
71
99
  getEditorMarkdown: () => getEditorMarkdown,
72
100
  getEditorText: () => getEditorText,
73
101
  getLatestVersion: () => getLatestVersion,
102
+ getLayoutedNodes: () => getLayoutedNodes,
74
103
  handleImageInsertion: () => handleImageInsertion,
75
104
  htmlToPlainText: () => htmlToPlainText,
76
105
  imagePastePluginKey: () => imagePastePluginKey,
77
106
  indexToColName: () => indexToColName,
78
107
  insertImageNode: () => insertImageNode,
79
108
  instructionRegex: () => instructionRegex,
109
+ isEventFromTextInput: () => isEventFromTextInput,
110
+ isValidCanvasNode: () => isValidCanvasNode,
111
+ isValidUrl: () => isValidUrl,
112
+ layoutBounds: () => layoutBounds,
113
+ linearPositionTrajectory: () => linearPositionTrajectory,
114
+ linearTrajectory: () => linearTrajectory,
80
115
  mapRawOffsetToResolved: () => mapRawOffsetToResolved,
81
116
  mapResolvedOffsetToRaw: () => mapResolvedOffsetToRaw,
82
117
  markdownToTipTapHtml: () => markdownToTipTapHtml,
118
+ overlaps: () => overlaps,
119
+ pageHidden: () => pageHidden,
120
+ parseCanvasClipboardData: () => parseCanvasClipboardData,
83
121
  parseCellReference: () => parseCellReference,
84
122
  parseFormula: () => parseFormula,
85
123
  parseMentionSegments: () => parseMentionSegments,
86
124
  parseRange: () => parseRange,
125
+ placeBeside: () => placeBeside,
87
126
  plainTextToTipTapHtml: () => plainTextToTipTapHtml,
127
+ planAgentCommand: () => planAgentCommand,
88
128
  preprocessMarkdownFileLinks: () => preprocessMarkdownFileLinks,
89
129
  preprocessMarkdownMentions: () => preprocessMarkdownMentions,
130
+ realClock: () => realClock,
131
+ remapPastedNodes: () => remapPastedNodes,
90
132
  removeImageNode: () => removeImageNode,
133
+ repositionNodesTo: () => repositionNodesTo,
91
134
  resolveTemplates: () => resolveTemplates,
92
135
  resolveTemplatesWithMapping: () => resolveTemplatesWithMapping,
93
136
  resolveVariables: () => resolveVariables,
137
+ runMultiNodeTransition: () => runMultiNodeTransition,
138
+ serializeCanvasNodes: () => serializeCanvasNodes,
94
139
  stripMarkdownCode: () => stripMarkdownCode,
95
140
  templateRegex: () => templateRegex,
96
141
  tokenizeFormula: () => tokenizeFormula,
@@ -129,10 +174,10 @@ function tokenizePrompt(text) {
129
174
  while ((match = varRegex.exec(text)) !== null) {
130
175
  const start = match.index;
131
176
  const end = start + match[0].length;
132
- const overlaps = rawMatches.some(
177
+ const overlaps2 = rawMatches.some(
133
178
  (m) => !(end <= m.start || start >= m.end)
134
179
  );
135
- if (!overlaps) {
180
+ if (!overlaps2) {
136
181
  rawMatches.push({
137
182
  type: "variable",
138
183
  raw: match[0],
@@ -149,10 +194,10 @@ function tokenizePrompt(text) {
149
194
  while ((match = instRegex.exec(text)) !== null) {
150
195
  const start = match.index;
151
196
  const end = start + match[0].length;
152
- const overlaps = rawMatches.some(
197
+ const overlaps2 = rawMatches.some(
153
198
  (m) => !(end <= m.start || start >= m.end)
154
199
  );
155
- if (!overlaps) {
200
+ if (!overlaps2) {
156
201
  rawMatches.push({
157
202
  type: "instruction",
158
203
  raw: match[0],
@@ -4343,12 +4388,1219 @@ var HistoryManager = class {
4343
4388
  function createHistoryManager(options = {}) {
4344
4389
  return new HistoryManager(options);
4345
4390
  }
4391
+
4392
+ // src/canvas/layout.ts
4393
+ var import_dagre = __toESM(require("@dagrejs/dagre"), 1);
4394
+ var DEFAULT_NODE_WIDTH = 200;
4395
+ var DEFAULT_NODE_HEIGHT = 60;
4396
+ var DEFAULT_PLACEMENT_GAP = 48;
4397
+ function getCenteredNodePosition(width = 0, height = 0, screenToFlowPosition, screenDimensions) {
4398
+ const rect = screenDimensions ?? (typeof document !== "undefined" ? { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } : { width: 1920, height: 1080 });
4399
+ const flowPos = screenToFlowPosition({ x: rect.width / 2, y: rect.height / 2 });
4400
+ return {
4401
+ x: flowPos.x - width / 2,
4402
+ y: flowPos.y - height / 2
4403
+ };
4404
+ }
4405
+ function centerNodes(nodes, screenToFlowPosition, getNodesBounds, screenDimensions) {
4406
+ if (nodes.length === 0) {
4407
+ return [];
4408
+ }
4409
+ const bounds = getNodesBounds(nodes);
4410
+ const boundingBoxOrigin = { x: bounds.x, y: bounds.y };
4411
+ const centeredPosition = getCenteredNodePosition(
4412
+ bounds.width,
4413
+ bounds.height,
4414
+ screenToFlowPosition,
4415
+ screenDimensions
4416
+ );
4417
+ const deltaX = centeredPosition.x - boundingBoxOrigin.x;
4418
+ const deltaY = centeredPosition.y - boundingBoxOrigin.y;
4419
+ return nodes.map((node) => ({
4420
+ ...node,
4421
+ position: {
4422
+ x: node.position.x + deltaX,
4423
+ y: node.position.y + deltaY
4424
+ }
4425
+ }));
4426
+ }
4427
+ function getLayoutedNodes(nodes, edges, getNodesBounds, options = {}) {
4428
+ if (nodes.length === 0) return [];
4429
+ const {
4430
+ direction = "LR",
4431
+ ranksep = 100,
4432
+ nodesep = 25,
4433
+ defaultNodeWidth = DEFAULT_NODE_WIDTH,
4434
+ defaultNodeHeight = DEFAULT_NODE_HEIGHT
4435
+ } = options;
4436
+ const dagreGraph = new import_dagre.default.graphlib.Graph();
4437
+ dagreGraph.setDefaultEdgeLabel(() => ({}));
4438
+ dagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });
4439
+ const nodeIds = new Set(nodes.map((node) => node.id));
4440
+ nodes.forEach((node) => {
4441
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
4442
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
4443
+ dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
4444
+ });
4445
+ edges.forEach((edge) => {
4446
+ if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
4447
+ dagreGraph.setEdge(edge.source, edge.target);
4448
+ }
4449
+ });
4450
+ import_dagre.default.layout(dagreGraph);
4451
+ return nodes.map((node) => {
4452
+ const nodeWithPosition = dagreGraph.node(node.id);
4453
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
4454
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
4455
+ return {
4456
+ ...node,
4457
+ position: {
4458
+ x: nodeWithPosition.x - nodeWidth / 2,
4459
+ y: nodeWithPosition.y - nodeHeight / 2
4460
+ }
4461
+ };
4462
+ });
4463
+ }
4464
+ function estimateNodeSize(node, defaultWidth = DEFAULT_NODE_WIDTH, defaultHeight = DEFAULT_NODE_HEIGHT) {
4465
+ const label = String(node.data?.label ?? node.data?.title ?? node.id ?? "");
4466
+ const lines = label.split(/\n|<br\s*\/?>/);
4467
+ const longest = Math.max(...lines.map((l) => l.replace(/<[^>]+>/g, "").length), 1);
4468
+ const width = Math.min(480, Math.max(defaultWidth, longest * 8 + 48));
4469
+ const height = Math.max(defaultHeight, lines.length * 20 + 24);
4470
+ return { width, height };
4471
+ }
4472
+ function boundsOf(rects) {
4473
+ if (!rects.length) return null;
4474
+ const minX = Math.min(...rects.map((r) => r.x));
4475
+ const minY = Math.min(...rects.map((r) => r.y));
4476
+ const maxX = Math.max(...rects.map((r) => r.x + r.width));
4477
+ const maxY = Math.max(...rects.map((r) => r.y + r.height));
4478
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
4479
+ }
4480
+ function centre(r) {
4481
+ return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
4482
+ }
4483
+ function overlaps(a, b, pad = 16) {
4484
+ return a.x < b.x + b.width + pad && a.x + a.width + pad > b.x && a.y < b.y + b.height + pad && a.y + a.height + pad > b.y;
4485
+ }
4486
+ function flowSide(direction = "LR") {
4487
+ switch (direction) {
4488
+ case "LR":
4489
+ return "right";
4490
+ case "RL":
4491
+ return "left";
4492
+ case "BT":
4493
+ return "above";
4494
+ case "TB":
4495
+ default:
4496
+ return "below";
4497
+ }
4498
+ }
4499
+ function ensureLayout(nodes, edges, options = {}) {
4500
+ if (nodes.length === 0) return [];
4501
+ const stored = options.storedPositions ?? {};
4502
+ const missing = nodes.filter((n) => !stored[n.id]);
4503
+ if (missing.length === 0) {
4504
+ return nodes.map((n) => ({
4505
+ ...n,
4506
+ position: { ...stored[n.id] }
4507
+ }));
4508
+ }
4509
+ const autoLayouted = getLayoutedNodes(nodes, edges, void 0, options);
4510
+ const autoMap = new Map(
4511
+ autoLayouted.map((n) => [n.id, n.position])
4512
+ );
4513
+ const both = nodes.filter((n) => stored[n.id] && autoMap.has(n.id));
4514
+ if (both.length === 0) {
4515
+ return autoLayouted;
4516
+ }
4517
+ let dx = 0;
4518
+ let dy = 0;
4519
+ for (const n of both) {
4520
+ const s = stored[n.id];
4521
+ const a = autoMap.get(n.id);
4522
+ dx += s.x - a.x;
4523
+ dy += s.y - a.y;
4524
+ }
4525
+ dx /= both.length;
4526
+ dy /= both.length;
4527
+ return nodes.map((node) => {
4528
+ if (stored[node.id]) {
4529
+ return {
4530
+ ...node,
4531
+ position: { ...stored[node.id] }
4532
+ };
4533
+ }
4534
+ const autoPos = autoMap.get(node.id) ?? { x: 0, y: 0 };
4535
+ return {
4536
+ ...node,
4537
+ position: {
4538
+ x: Math.round(autoPos.x + dx),
4539
+ y: Math.round(autoPos.y + dy)
4540
+ }
4541
+ };
4542
+ });
4543
+ }
4544
+ function placeBeside(rects, size, side, nearNodeId, options = {}) {
4545
+ const {
4546
+ gap = DEFAULT_PLACEMENT_GAP,
4547
+ ignore = [],
4548
+ maxAttempts = 32
4549
+ } = options;
4550
+ const skip = new Set([nearNodeId, ...ignore].filter(Boolean));
4551
+ const obstacles = Object.entries(rects).filter(([id]) => !skip.has(id)).map(([, r]) => r);
4552
+ const anchor = nearNodeId && rects[nearNodeId] || boundsOf(Object.values(rects)) || { x: 0, y: 0, width: 0, height: 0 };
4553
+ const anchorCenter = centre(anchor);
4554
+ let baseX;
4555
+ let baseY;
4556
+ switch (side) {
4557
+ case "right":
4558
+ baseX = anchor.x + anchor.width + gap;
4559
+ baseY = anchorCenter.y - size.height / 2;
4560
+ break;
4561
+ case "left":
4562
+ baseX = anchor.x - gap - size.width;
4563
+ baseY = anchorCenter.y - size.height / 2;
4564
+ break;
4565
+ case "above":
4566
+ baseX = anchorCenter.x - size.width / 2;
4567
+ baseY = anchor.y - gap - size.height;
4568
+ break;
4569
+ case "below":
4570
+ default:
4571
+ baseX = anchorCenter.x - size.width / 2;
4572
+ baseY = anchor.y + anchor.height + gap;
4573
+ break;
4574
+ }
4575
+ const isHorizontal = side === "right" || side === "left";
4576
+ const step = (isHorizontal ? size.height : size.width) + 20;
4577
+ for (let i = 0; i < maxAttempts; i++) {
4578
+ const offset = i === 0 ? 0 : Math.ceil(i / 2) * step * (i % 2 === 1 ? 1 : -1);
4579
+ const candidateBox = {
4580
+ x: isHorizontal ? baseX : baseX + offset,
4581
+ y: isHorizontal ? baseY + offset : baseY,
4582
+ width: size.width,
4583
+ height: size.height
4584
+ };
4585
+ if (!obstacles.some((obstacle) => overlaps(candidateBox, obstacle, 8))) {
4586
+ return {
4587
+ x: Math.round(candidateBox.x),
4588
+ y: Math.round(candidateBox.y)
4589
+ };
4590
+ }
4591
+ }
4592
+ return {
4593
+ x: Math.round(baseX),
4594
+ y: Math.round(baseY)
4595
+ };
4596
+ }
4597
+
4598
+ // src/canvas/clock.ts
4599
+ var pageHidden = () => typeof document !== "undefined" && document.hidden;
4600
+ var realClock = {
4601
+ live: true,
4602
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
4603
+ frame: () => new Promise((resolve) => {
4604
+ if (typeof requestAnimationFrame === "function" && !pageHidden()) {
4605
+ requestAnimationFrame(() => resolve());
4606
+ } else {
4607
+ setTimeout(resolve, 16);
4608
+ }
4609
+ }),
4610
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)))
4611
+ };
4612
+ var settle = () => new Promise((resolve) => setTimeout(resolve, 0));
4613
+ var ManualClock = class {
4614
+ live = false;
4615
+ time;
4616
+ frames = [];
4617
+ sleepers = [];
4618
+ seq = 0;
4619
+ constructor(start = 0) {
4620
+ this.time = start;
4621
+ }
4622
+ now() {
4623
+ return this.time;
4624
+ }
4625
+ frame() {
4626
+ return new Promise((resolve) => this.frames.push(resolve));
4627
+ }
4628
+ sleep(ms) {
4629
+ return new Promise(
4630
+ (resolve) => this.sleepers.push({
4631
+ at: this.time + Math.max(0, ms),
4632
+ seq: this.seq++,
4633
+ resolve
4634
+ })
4635
+ );
4636
+ }
4637
+ /** Whether any frames or sleep timers are currently pending. */
4638
+ get pending() {
4639
+ return this.frames.length > 0 || this.sleepers.length > 0;
4640
+ }
4641
+ /** Advances time by `ms`, resolving due sleep timers in order, followed by queued frames. */
4642
+ async advance(ms) {
4643
+ const end = this.time + Math.max(0, ms);
4644
+ await settle();
4645
+ while (true) {
4646
+ let next;
4647
+ for (const s of this.sleepers) {
4648
+ if (s.at <= end && (!next || s.at < next.at || s.at === next.at && s.seq < next.seq)) {
4649
+ next = s;
4650
+ }
4651
+ }
4652
+ if (!next) break;
4653
+ this.sleepers.splice(this.sleepers.indexOf(next), 1);
4654
+ this.time = Math.max(this.time, next.at);
4655
+ next.resolve();
4656
+ await settle();
4657
+ }
4658
+ this.time = end;
4659
+ const dueFrames = this.frames.splice(0, this.frames.length);
4660
+ for (const resolveFrame of dueFrames) {
4661
+ resolveFrame();
4662
+ }
4663
+ await settle();
4664
+ }
4665
+ /** Convenience method to advance by 1 frame at a given fps (default: 60fps = 16.67ms). */
4666
+ async tick(fps = 60) {
4667
+ await this.advance(Math.round(1e3 / fps));
4668
+ }
4669
+ };
4670
+
4671
+ // src/canvas/trajectory.ts
4672
+ var defaultEasing = (t) => --t * t * t + 1;
4673
+ var linearTrajectory = (origin, target, progress) => {
4674
+ return {
4675
+ position: {
4676
+ x: origin.x + (target.x - origin.x) * progress,
4677
+ y: origin.y + (target.y - origin.y) * progress
4678
+ },
4679
+ scale: progress,
4680
+ opacity: Math.min(1, progress * 1.5)
4681
+ };
4682
+ };
4683
+ var linearPositionTrajectory = (origin, target, progress) => {
4684
+ return {
4685
+ position: {
4686
+ x: origin.x + (target.x - origin.x) * progress,
4687
+ y: origin.y + (target.y - origin.y) * progress
4688
+ }
4689
+ };
4690
+ };
4691
+ function createFanOutTrajectory(options = {}) {
4692
+ const {
4693
+ curvature = 0.4,
4694
+ startScale = 0,
4695
+ endScale = 1,
4696
+ startOpacity = 0,
4697
+ endOpacity = 1
4698
+ } = options;
4699
+ return (origin, target, progress, index, total) => {
4700
+ const dx = target.x - origin.x;
4701
+ const dy = target.y - origin.y;
4702
+ const normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;
4703
+ const midX = origin.x + dx * 0.4;
4704
+ const midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;
4705
+ const t = progress;
4706
+ const oneMinusT = 1 - t;
4707
+ const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;
4708
+ const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;
4709
+ const scale = startScale + (endScale - startScale) * progress;
4710
+ const opacity = startOpacity + (endOpacity - startOpacity) * progress;
4711
+ return {
4712
+ position: { x, y },
4713
+ scale,
4714
+ opacity
4715
+ };
4716
+ };
4717
+ }
4718
+ function createBezierTrajectory(getControlPoint) {
4719
+ return (origin, target, progress) => {
4720
+ const cp = getControlPoint(origin, target);
4721
+ const t = progress;
4722
+ const oneMinusT = 1 - t;
4723
+ const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;
4724
+ const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;
4725
+ return {
4726
+ position: { x, y },
4727
+ scale: progress,
4728
+ opacity: progress
4729
+ };
4730
+ };
4731
+ }
4732
+ function runMultiNodeTransition(transitions, options = {}) {
4733
+ if (transitions.length === 0) {
4734
+ options.onComplete?.();
4735
+ return () => {
4736
+ };
4737
+ }
4738
+ const {
4739
+ defaultDuration = 400,
4740
+ defaultEasing: easingFunc = defaultEasing,
4741
+ clock = realClock,
4742
+ onUpdate,
4743
+ onComplete
4744
+ } = options;
4745
+ let isCancelled = false;
4746
+ const startTime = clock.now();
4747
+ const items = transitions.map((tr, i) => ({
4748
+ ...tr,
4749
+ index: tr.index ?? i,
4750
+ total: tr.total ?? transitions.length,
4751
+ duration: tr.duration ?? defaultDuration,
4752
+ easing: tr.easing ?? easingFunc,
4753
+ delay: tr.delay ?? 0,
4754
+ trajectory: tr.trajectory ?? linearTrajectory
4755
+ }));
4756
+ function frame() {
4757
+ if (isCancelled) return;
4758
+ const now = clock.now();
4759
+ let allCompleted = true;
4760
+ for (let i = 0; i < items.length; i++) {
4761
+ const item = items[i];
4762
+ const elapsed = now - (startTime + item.delay);
4763
+ if (elapsed < 0) {
4764
+ allCompleted = false;
4765
+ continue;
4766
+ }
4767
+ const rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);
4768
+ const progress = item.duration <= 0 ? 1 : item.easing(rawProgress);
4769
+ const point = item.trajectory(item.from, item.to, progress, item.index, item.total);
4770
+ item.node.position = { ...point.position };
4771
+ if (point.scale !== void 0 || point.opacity !== void 0) {
4772
+ if (!item.node.data) {
4773
+ item.node.data = {};
4774
+ }
4775
+ if (point.scale !== void 0) {
4776
+ item.node.data.scale = point.scale;
4777
+ }
4778
+ if (point.opacity !== void 0) {
4779
+ item.node.data.opacity = point.opacity;
4780
+ const currentStyle = (item.node.style || "").replace(/opacity:\s*[^;]+;?/g, "").trim();
4781
+ item.node.style = `${currentStyle ? currentStyle + "; " : ""}opacity: ${point.opacity};`;
4782
+ }
4783
+ }
4784
+ if (rawProgress < 1) {
4785
+ allCompleted = false;
4786
+ }
4787
+ }
4788
+ onUpdate?.();
4789
+ if (!allCompleted) {
4790
+ clock.frame().then(frame);
4791
+ } else {
4792
+ onComplete?.();
4793
+ }
4794
+ }
4795
+ frame();
4796
+ return () => {
4797
+ isCancelled = true;
4798
+ };
4799
+ }
4800
+
4801
+ // src/canvas/displacement.ts
4802
+ var import_dagre2 = __toESM(require("@dagrejs/dagre"), 1);
4803
+ function computeNodesBoundingBox(nodes, defaultWidth = 200, defaultHeight = 60) {
4804
+ if (nodes.length === 0) {
4805
+ return { x: 0, y: 0, width: 0, height: 0 };
4806
+ }
4807
+ let minX = Infinity;
4808
+ let minY = Infinity;
4809
+ let maxX = -Infinity;
4810
+ let maxY = -Infinity;
4811
+ for (const node of nodes) {
4812
+ const width = node.measured?.width ?? node.width ?? defaultWidth;
4813
+ const height = node.measured?.height ?? node.height ?? defaultHeight;
4814
+ minX = Math.min(minX, node.position.x);
4815
+ minY = Math.min(minY, node.position.y);
4816
+ maxX = Math.max(maxX, node.position.x + width);
4817
+ maxY = Math.max(maxY, node.position.y + height);
4818
+ }
4819
+ return {
4820
+ x: minX,
4821
+ y: minY,
4822
+ width: maxX - minX,
4823
+ height: maxY - minY
4824
+ };
4825
+ }
4826
+ function calculateDirectionalDisplacement(params) {
4827
+ const {
4828
+ existingNodes,
4829
+ originNode,
4830
+ childNodes: childNodes2,
4831
+ getNodesBounds,
4832
+ options = {}
4833
+ } = params;
4834
+ const {
4835
+ direction = "right",
4836
+ gap = 40,
4837
+ spreadSiblingLanes = false,
4838
+ siblingTolerance = 50,
4839
+ minY
4840
+ } = options;
4841
+ const displacedMap = /* @__PURE__ */ new Map();
4842
+ if (childNodes2.length === 0 || existingNodes.length === 0) {
4843
+ return displacedMap;
4844
+ }
4845
+ const childBounds = getNodesBounds ? getNodesBounds(childNodes2) : computeNodesBoundingBox(childNodes2);
4846
+ const originWidth = originNode.measured?.width ?? originNode.width ?? 200;
4847
+ const originHeight = originNode.measured?.height ?? originNode.height ?? 60;
4848
+ const deltaPrimary = direction === "right" || direction === "left" ? childBounds.width + gap : childBounds.height + gap;
4849
+ const deltaUp = Math.max(0, originNode.position.y - childBounds.y + gap);
4850
+ const deltaDown = Math.max(
4851
+ 0,
4852
+ childBounds.y + childBounds.height - (originNode.position.y + originHeight) + gap
4853
+ );
4854
+ const equalDelta = Math.max(deltaUp, deltaDown);
4855
+ for (const node of existingNodes) {
4856
+ if (node.id === originNode.id) continue;
4857
+ const isSiblingRow = Math.abs(node.position.y - originNode.position.y) < siblingTolerance;
4858
+ switch (direction) {
4859
+ case "right": {
4860
+ if (node.position.x > originNode.position.x) {
4861
+ displacedMap.set(node.id, {
4862
+ x: node.position.x + deltaPrimary,
4863
+ y: node.position.y
4864
+ });
4865
+ }
4866
+ break;
4867
+ }
4868
+ case "left": {
4869
+ if (node.position.x < originNode.position.x) {
4870
+ displacedMap.set(node.id, {
4871
+ x: node.position.x - deltaPrimary,
4872
+ y: node.position.y
4873
+ });
4874
+ }
4875
+ break;
4876
+ }
4877
+ case "down": {
4878
+ if (node.position.y > originNode.position.y) {
4879
+ displacedMap.set(node.id, {
4880
+ x: node.position.x,
4881
+ y: node.position.y + deltaPrimary
4882
+ });
4883
+ }
4884
+ break;
4885
+ }
4886
+ case "up": {
4887
+ if (node.position.y < originNode.position.y) {
4888
+ displacedMap.set(node.id, {
4889
+ x: node.position.x,
4890
+ y: node.position.y - deltaPrimary
4891
+ });
4892
+ }
4893
+ break;
4894
+ }
4895
+ case "vertical": {
4896
+ if (isSiblingRow) break;
4897
+ if (node.position.y < originNode.position.y) {
4898
+ displacedMap.set(node.id, {
4899
+ x: node.position.x,
4900
+ y: node.position.y - equalDelta
4901
+ });
4902
+ } else if (node.position.y > originNode.position.y) {
4903
+ displacedMap.set(node.id, {
4904
+ x: node.position.x,
4905
+ y: node.position.y + equalDelta
4906
+ });
4907
+ }
4908
+ break;
4909
+ }
4910
+ }
4911
+ }
4912
+ if (spreadSiblingLanes && (direction === "right" || direction === "left")) {
4913
+ for (const [nodeId, pos] of displacedMap.entries()) {
4914
+ const node = existingNodes.find((n) => n.id === nodeId);
4915
+ if (node && Math.abs(node.position.y - originNode.position.y) < siblingTolerance) {
4916
+ const isAbove = node.position.y < originNode.position.y;
4917
+ displacedMap.set(nodeId, {
4918
+ x: pos.x,
4919
+ y: pos.y + (isAbove ? -deltaUp : deltaDown)
4920
+ });
4921
+ }
4922
+ }
4923
+ }
4924
+ if (minY !== void 0) {
4925
+ for (const [nodeId, pos] of displacedMap.entries()) {
4926
+ if (pos.y < minY) {
4927
+ displacedMap.set(nodeId, { x: pos.x, y: minY });
4928
+ }
4929
+ }
4930
+ }
4931
+ return displacedMap;
4932
+ }
4933
+ function calculateDagreReflowDisplacement(params) {
4934
+ const {
4935
+ currentNodes,
4936
+ futureNodes,
4937
+ futureEdges,
4938
+ options
4939
+ } = params;
4940
+ const {
4941
+ anchorNodeId,
4942
+ direction = "LR",
4943
+ ranksep = 100,
4944
+ nodesep = 25,
4945
+ defaultNodeWidth = 200,
4946
+ defaultNodeHeight = 60
4947
+ } = options;
4948
+ const anchorNode = currentNodes.find((n) => n.id === anchorNodeId);
4949
+ if (!anchorNode) return /* @__PURE__ */ new Map();
4950
+ const g = new import_dagre2.default.graphlib.Graph();
4951
+ g.setDefaultEdgeLabel(() => ({}));
4952
+ g.setGraph({ rankdir: direction, ranksep, nodesep });
4953
+ const nodeIds = new Set(futureNodes.map((n) => n.id));
4954
+ futureNodes.forEach((node) => {
4955
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
4956
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
4957
+ g.setNode(node.id, { width: nodeWidth, height: nodeHeight });
4958
+ });
4959
+ futureEdges.forEach((edge) => {
4960
+ if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
4961
+ g.setEdge(edge.source, edge.target);
4962
+ }
4963
+ });
4964
+ import_dagre2.default.layout(g);
4965
+ const anchorLayoutPos = g.node(anchorNodeId);
4966
+ if (!anchorLayoutPos) return /* @__PURE__ */ new Map();
4967
+ const anchorWidth = anchorNode.measured?.width ?? anchorNode.width ?? defaultNodeWidth;
4968
+ const anchorHeight = anchorNode.measured?.height ?? anchorNode.height ?? defaultNodeHeight;
4969
+ const anchorLayoutUpperLeft = {
4970
+ x: anchorLayoutPos.x - anchorWidth / 2,
4971
+ y: anchorLayoutPos.y - anchorHeight / 2
4972
+ };
4973
+ const deltaX = anchorNode.position.x - anchorLayoutUpperLeft.x;
4974
+ const deltaY = anchorNode.position.y - anchorLayoutUpperLeft.y;
4975
+ const displacementMap = /* @__PURE__ */ new Map();
4976
+ currentNodes.forEach((node) => {
4977
+ if (node.id === anchorNodeId) return;
4978
+ const layoutNode = g.node(node.id);
4979
+ if (!layoutNode) return;
4980
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
4981
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
4982
+ const upperLeftX = layoutNode.x - nodeWidth / 2 + deltaX;
4983
+ const upperLeftY = layoutNode.y - nodeHeight / 2 + deltaY;
4984
+ if (Math.abs(upperLeftX - node.position.x) > 1 || Math.abs(upperLeftY - node.position.y) > 1) {
4985
+ displacementMap.set(node.id, { x: upperLeftX, y: upperLeftY });
4986
+ }
4987
+ });
4988
+ return displacementMap;
4989
+ }
4990
+
4991
+ // src/canvas/clipboard.ts
4992
+ function isEventFromTextInput(event) {
4993
+ const target = event.target;
4994
+ if (!target) return false;
4995
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable || target.closest?.('[contenteditable="true"]') || target.closest?.(".ProseMirror")) {
4996
+ return true;
4997
+ }
4998
+ return false;
4999
+ }
5000
+ function isValidCanvasNode(obj) {
5001
+ return typeof obj === "object" && obj !== null && "id" in obj && typeof obj.id === "string" && "position" in obj && typeof obj.position === "object" && obj.position !== null && "x" in obj.position && "y" in obj.position;
5002
+ }
5003
+ function isValidUrl(url) {
5004
+ try {
5005
+ const parsed = new URL(url);
5006
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
5007
+ } catch {
5008
+ return false;
5009
+ }
5010
+ }
5011
+ function serializeCanvasNodes(nodes, edges) {
5012
+ const payload = {
5013
+ version: 1,
5014
+ nodes,
5015
+ edges
5016
+ };
5017
+ return JSON.stringify(payload);
5018
+ }
5019
+ function parseCanvasClipboardData(raw) {
5020
+ try {
5021
+ const parsed = JSON.parse(raw);
5022
+ if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.nodes) && parsed.nodes.length > 0 && parsed.nodes.every(isValidCanvasNode)) {
5023
+ return parsed;
5024
+ }
5025
+ } catch {
5026
+ }
5027
+ return null;
5028
+ }
5029
+ function remapPastedNodes(nodes, edges = [], offset = { x: 30, y: 30 }, idGenerator = () => `node-${Math.random().toString(36).substring(2, 9)}`) {
5030
+ const idMap = /* @__PURE__ */ new Map();
5031
+ const newNodes = nodes.map((node) => {
5032
+ const newId = idGenerator();
5033
+ idMap.set(node.id, newId);
5034
+ return {
5035
+ ...node,
5036
+ id: newId,
5037
+ position: {
5038
+ x: node.position.x + offset.x,
5039
+ y: node.position.y + offset.y
5040
+ },
5041
+ selected: true
5042
+ };
5043
+ });
5044
+ const newEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((edge) => ({
5045
+ ...edge,
5046
+ id: `edge-${Math.random().toString(36).substring(2, 9)}`,
5047
+ source: idMap.get(edge.source),
5048
+ target: idMap.get(edge.target),
5049
+ selected: false
5050
+ }));
5051
+ return { nodes: newNodes, edges: newEdges };
5052
+ }
5053
+ function repositionNodesTo(nodes, targetOrigin) {
5054
+ if (nodes.length === 0) return [];
5055
+ const minX = Math.min(...nodes.map((n) => n.position.x));
5056
+ const minY = Math.min(...nodes.map((n) => n.position.y));
5057
+ const deltaX = targetOrigin.x - minX;
5058
+ const deltaY = targetOrigin.y - minY;
5059
+ return nodes.map((n) => ({
5060
+ ...n,
5061
+ position: {
5062
+ x: n.position.x + deltaX,
5063
+ y: n.position.y + deltaY
5064
+ }
5065
+ }));
5066
+ }
5067
+
5068
+ // src/canvas/view.ts
5069
+ function clampHeight(contentHeight, options = {}) {
5070
+ const { min = 320, max = 560, padding = 48 } = options;
5071
+ return Math.round(Math.min(max, Math.max(min, contentHeight + padding)));
5072
+ }
5073
+ function layoutBounds(nodes) {
5074
+ if (nodes.length === 0) return null;
5075
+ let minX = Infinity;
5076
+ let minY = Infinity;
5077
+ let maxX = -Infinity;
5078
+ let maxY = -Infinity;
5079
+ for (const node of nodes) {
5080
+ const x = node.position.x;
5081
+ const y = node.position.y;
5082
+ const width = node.width ?? 180;
5083
+ const height = node.height ?? 80;
5084
+ minX = Math.min(minX, x);
5085
+ minY = Math.min(minY, y);
5086
+ maxX = Math.max(maxX, x + width);
5087
+ maxY = Math.max(maxY, y + height);
5088
+ }
5089
+ if (minX === Infinity) return null;
5090
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
5091
+ }
5092
+ function canvasHeight(nodes, options) {
5093
+ return clampHeight(layoutBounds(nodes)?.height ?? 0, options);
5094
+ }
5095
+ var DEFAULT_RANK_HEIGHT = 80 + 72;
5096
+ var NODES_PER_RANK = 2;
5097
+ function estimateCanvasHeight(nodes, edges, options) {
5098
+ const ranks = Math.max(1, Math.ceil(nodes.length / NODES_PER_RANK));
5099
+ return clampHeight(ranks * DEFAULT_RANK_HEIGHT, options);
5100
+ }
5101
+ function mix(a, b, t) {
5102
+ return a + (b - a) * t;
5103
+ }
5104
+ function blendLayout(from, to, t) {
5105
+ if (t >= 1) return to;
5106
+ const fromMap = new Map(from.map((n) => [n.id, n]));
5107
+ return to.map((target) => {
5108
+ const origin = fromMap.get(target.id);
5109
+ if (!origin) return target;
5110
+ return {
5111
+ ...target,
5112
+ position: {
5113
+ x: mix(origin.position.x, target.position.x, t),
5114
+ y: mix(origin.position.y, target.position.y, t)
5115
+ },
5116
+ width: origin.width !== void 0 && target.width !== void 0 ? mix(origin.width, target.width, t) : target.width,
5117
+ height: origin.height !== void 0 && target.height !== void 0 ? mix(origin.height, target.height, t) : target.height
5118
+ };
5119
+ });
5120
+ }
5121
+ function connectedOnly(nodes, edges) {
5122
+ if (edges.length === 0) return nodes;
5123
+ const linkedNodeIds = /* @__PURE__ */ new Set();
5124
+ for (const edge of edges) {
5125
+ linkedNodeIds.add(edge.source);
5126
+ linkedNodeIds.add(edge.target);
5127
+ }
5128
+ const filtered = nodes.filter((node) => linkedNodeIds.has(node.id));
5129
+ return filtered.length > 0 ? filtered : nodes;
5130
+ }
5131
+
5132
+ // src/canvas/streaming.ts
5133
+ var StreamingLayoutManager = class {
5134
+ throttleMs;
5135
+ glideDurationMs;
5136
+ connectedOnly;
5137
+ clock;
5138
+ layoutOptions;
5139
+ onLayoutUpdated;
5140
+ pendingTimer = null;
5141
+ activeTransitionCancel = null;
5142
+ targetPositions = /* @__PURE__ */ new Map();
5143
+ constructor(options = {}) {
5144
+ this.throttleMs = options.throttleMs ?? 200;
5145
+ this.glideDurationMs = options.glideDurationMs ?? 300;
5146
+ this.connectedOnly = options.connectedOnly ?? false;
5147
+ this.clock = options.clock ?? realClock;
5148
+ this.layoutOptions = options.layoutOptions ?? { direction: "LR" };
5149
+ this.onLayoutUpdated = options.onLayoutUpdated;
5150
+ }
5151
+ /**
5152
+ * Feed incoming nodes and edges into the streaming manager.
5153
+ * Coalesces rapid streaming bursts and triggers smooth layout gliding.
5154
+ */
5155
+ pushStreamUpdate(nodes, edges, currentPositions) {
5156
+ if (this.pendingTimer) {
5157
+ clearTimeout(this.pendingTimer);
5158
+ }
5159
+ this.pendingTimer = setTimeout(() => {
5160
+ this.pendingTimer = null;
5161
+ this.recalculateAndGlide(nodes, edges, currentPositions);
5162
+ }, this.throttleMs);
5163
+ }
5164
+ /**
5165
+ * Forces immediate calculation and cancels pending throttled timers.
5166
+ */
5167
+ flush(nodes, edges, currentPositions) {
5168
+ if (this.pendingTimer) {
5169
+ clearTimeout(this.pendingTimer);
5170
+ this.pendingTimer = null;
5171
+ }
5172
+ this.recalculateAndGlide(nodes, edges, currentPositions);
5173
+ }
5174
+ recalculateAndGlide(nodes, edges, currentPositions) {
5175
+ const targetNodes = this.connectedOnly ? connectedOnly(nodes, edges) : nodes;
5176
+ if (targetNodes.length === 0) return;
5177
+ if (this.activeTransitionCancel) {
5178
+ this.activeTransitionCancel();
5179
+ this.activeTransitionCancel = null;
5180
+ }
5181
+ const stored = {};
5182
+ for (const [id, pos] of currentPositions.entries()) {
5183
+ stored[id] = pos;
5184
+ }
5185
+ const layouted = ensureLayout(targetNodes, edges, {
5186
+ ...this.layoutOptions,
5187
+ storedPositions: stored
5188
+ });
5189
+ const nextPositions = /* @__PURE__ */ new Map();
5190
+ const transitions = [];
5191
+ for (const node of layouted) {
5192
+ const target = node.position;
5193
+ nextPositions.set(node.id, target);
5194
+ const current = currentPositions.get(node.id) ?? target;
5195
+ const distance = Math.hypot(target.x - current.x, target.y - current.y);
5196
+ if (distance > 2) {
5197
+ transitions.push({
5198
+ node,
5199
+ from: current,
5200
+ to: target,
5201
+ trajectory: linearPositionTrajectory,
5202
+ duration: this.glideDurationMs
5203
+ });
5204
+ }
5205
+ }
5206
+ this.targetPositions = nextPositions;
5207
+ if (transitions.length > 0) {
5208
+ this.activeTransitionCancel = runMultiNodeTransition(transitions, {
5209
+ clock: this.clock,
5210
+ onUpdate: () => {
5211
+ const liveMap = new Map(currentPositions);
5212
+ for (const trans of transitions) {
5213
+ liveMap.set(trans.node.id, { ...trans.node.position });
5214
+ }
5215
+ this.onLayoutUpdated?.(liveMap);
5216
+ },
5217
+ onComplete: () => {
5218
+ this.activeTransitionCancel = null;
5219
+ this.onLayoutUpdated?.(nextPositions);
5220
+ }
5221
+ });
5222
+ } else {
5223
+ this.onLayoutUpdated?.(nextPositions);
5224
+ }
5225
+ }
5226
+ destroy() {
5227
+ if (this.pendingTimer) {
5228
+ clearTimeout(this.pendingTimer);
5229
+ this.pendingTimer = null;
5230
+ }
5231
+ if (this.activeTransitionCancel) {
5232
+ this.activeTransitionCancel();
5233
+ this.activeTransitionCancel = null;
5234
+ }
5235
+ }
5236
+ };
5237
+
5238
+ // src/agent/planner.ts
5239
+ function planAgentCommand(command, snapshot, options = {}) {
5240
+ const defaultNodeSize = options.defaultNodeSize ?? { width: 200, height: 60 };
5241
+ const idGen = options.idGenerator ?? (() => `node-${Math.random().toString(36).substring(2, 9)}`);
5242
+ const edgeIdGen = () => `edge-${Math.random().toString(36).substring(2, 9)}`;
5243
+ const nodes = snapshot.canvas?.nodes ?? [];
5244
+ const edges = snapshot.canvas?.edges ?? [];
5245
+ switch (command.type) {
5246
+ case "canvas:add_node": {
5247
+ let pos = command.node.position;
5248
+ if (!pos) {
5249
+ if (command.nearNodeId) {
5250
+ const rects = {};
5251
+ for (const n of nodes) {
5252
+ rects[n.id] = {
5253
+ x: n.position.x,
5254
+ y: n.position.y,
5255
+ width: n.measured?.width ?? n.width ?? defaultNodeSize.width,
5256
+ height: n.measured?.height ?? n.height ?? defaultNodeSize.height
5257
+ };
5258
+ }
5259
+ pos = placeBeside(
5260
+ rects,
5261
+ defaultNodeSize,
5262
+ command.side ?? "right",
5263
+ command.nearNodeId
5264
+ );
5265
+ } else if (nodes.length > 0) {
5266
+ const lastNode = nodes[nodes.length - 1];
5267
+ const rects = {};
5268
+ for (const n of nodes) {
5269
+ rects[n.id] = {
5270
+ x: n.position.x,
5271
+ y: n.position.y,
5272
+ width: n.measured?.width ?? n.width ?? defaultNodeSize.width,
5273
+ height: n.measured?.height ?? n.height ?? defaultNodeSize.height
5274
+ };
5275
+ }
5276
+ pos = placeBeside(rects, defaultNodeSize, "right", lastNode.id);
5277
+ } else {
5278
+ pos = { x: 100, y: 100 };
5279
+ }
5280
+ }
5281
+ const fullNode = {
5282
+ ...command.node,
5283
+ id: command.node.id || idGen(),
5284
+ position: pos
5285
+ };
5286
+ const steps = [
5287
+ {
5288
+ type: "step:canvas_add_node",
5289
+ node: fullNode
5290
+ }
5291
+ ];
5292
+ if (command.nearNodeId) {
5293
+ steps.push({
5294
+ type: "step:canvas_add_edge",
5295
+ edge: {
5296
+ id: edgeIdGen(),
5297
+ source: command.nearNodeId,
5298
+ target: fullNode.id
5299
+ }
5300
+ });
5301
+ }
5302
+ return steps;
5303
+ }
5304
+ case "canvas:remove_nodes": {
5305
+ return [
5306
+ {
5307
+ type: "step:canvas_remove_nodes",
5308
+ nodeIds: command.nodeIds
5309
+ }
5310
+ ];
5311
+ }
5312
+ case "canvas:connect": {
5313
+ return [
5314
+ {
5315
+ type: "step:canvas_add_edge",
5316
+ edge: {
5317
+ id: edgeIdGen(),
5318
+ source: command.source,
5319
+ target: command.target,
5320
+ label: command.label
5321
+ }
5322
+ }
5323
+ ];
5324
+ }
5325
+ case "canvas:update_node": {
5326
+ return [
5327
+ {
5328
+ type: "step:canvas_update_node",
5329
+ id: command.id,
5330
+ data: command.data
5331
+ }
5332
+ ];
5333
+ }
5334
+ case "canvas:layout": {
5335
+ const dir = command.direction ?? "LR";
5336
+ let layouted;
5337
+ if (command.incremental) {
5338
+ const stored = {};
5339
+ for (const n of nodes) {
5340
+ if (n.position && (n.position.x !== 0 || n.position.y !== 0)) {
5341
+ stored[n.id] = { ...n.position };
5342
+ }
5343
+ }
5344
+ layouted = ensureLayout(nodes, edges, {
5345
+ direction: dir,
5346
+ storedPositions: stored
5347
+ });
5348
+ } else {
5349
+ layouted = getLayoutedNodes(nodes, edges, void 0, { direction: dir });
5350
+ }
5351
+ const positions = {};
5352
+ for (const node of layouted) {
5353
+ positions[node.id] = { ...node.position };
5354
+ }
5355
+ return [
5356
+ {
5357
+ type: "step:canvas_move_nodes",
5358
+ positions
5359
+ }
5360
+ ];
5361
+ }
5362
+ case "canvas:select": {
5363
+ return [
5364
+ {
5365
+ type: "step:canvas_select",
5366
+ nodeIds: command.nodeIds
5367
+ }
5368
+ ];
5369
+ }
5370
+ case "canvas:highlight": {
5371
+ return [
5372
+ {
5373
+ type: "step:canvas_highlight",
5374
+ nodeIds: command.nodeIds,
5375
+ color: command.color ?? "#3b82f6",
5376
+ durationMs: command.durationMs ?? 2e3
5377
+ }
5378
+ ];
5379
+ }
5380
+ case "prompt:insert_text": {
5381
+ return [
5382
+ {
5383
+ type: "step:prompt_insert",
5384
+ text: command.text,
5385
+ at: command.at
5386
+ }
5387
+ ];
5388
+ }
5389
+ case "prompt:replace_range": {
5390
+ return [
5391
+ {
5392
+ type: "step:prompt_replace",
5393
+ from: command.from,
5394
+ to: command.to,
5395
+ text: command.text
5396
+ }
5397
+ ];
5398
+ }
5399
+ case "prompt:set_variable": {
5400
+ return [
5401
+ {
5402
+ type: "step:prompt_set_variable",
5403
+ name: command.name,
5404
+ value: command.value
5405
+ }
5406
+ ];
5407
+ }
5408
+ case "prompt:select": {
5409
+ return [
5410
+ {
5411
+ type: "step:prompt_select",
5412
+ from: command.from,
5413
+ to: command.to
5414
+ }
5415
+ ];
5416
+ }
5417
+ case "choreography:say": {
5418
+ return [
5419
+ {
5420
+ type: "step:say",
5421
+ text: command.text,
5422
+ atNodeId: command.atNodeId,
5423
+ position: command.position
5424
+ }
5425
+ ];
5426
+ }
5427
+ case "choreography:wait": {
5428
+ return [
5429
+ {
5430
+ type: "step:wait",
5431
+ ms: command.ms
5432
+ }
5433
+ ];
5434
+ }
5435
+ default: {
5436
+ return [];
5437
+ }
5438
+ }
5439
+ }
5440
+
5441
+ // src/agent/tools.ts
5442
+ var MCP_TOOL_GET_EDITOR_STATE = {
5443
+ name: "get_editor_state",
5444
+ description: "Retrieves the current state of the document, variables, selection, and canvas graph (nodes and edges).",
5445
+ inputSchema: {
5446
+ type: "object",
5447
+ properties: {
5448
+ includeDocument: {
5449
+ type: "boolean",
5450
+ description: "Whether to include the prompt document text and variables (default: true)."
5451
+ },
5452
+ includeCanvas: {
5453
+ type: "boolean",
5454
+ description: "Whether to include the canvas nodes and edges (default: true)."
5455
+ }
5456
+ }
5457
+ }
5458
+ };
5459
+ var MCP_TOOL_EDIT_CANVAS = {
5460
+ name: "edit_canvas",
5461
+ description: "Applies high-level structural operations to the visual canvas (adding nodes, connecting edges, organizing layout, or moving elements).",
5462
+ inputSchema: {
5463
+ type: "object",
5464
+ properties: {
5465
+ operation: {
5466
+ type: "string",
5467
+ enum: [
5468
+ "add_node",
5469
+ "remove_nodes",
5470
+ "connect",
5471
+ "update_node",
5472
+ "layout",
5473
+ "select",
5474
+ "highlight"
5475
+ ],
5476
+ description: "The canvas operation to execute."
5477
+ },
5478
+ node: {
5479
+ type: "object",
5480
+ description: "Node definition when operation is add_node. Can omit position to auto-place."
5481
+ },
5482
+ nearNodeId: {
5483
+ type: "string",
5484
+ description: "Anchor node ID to position beside using collision-aware placement."
5485
+ },
5486
+ side: {
5487
+ type: "string",
5488
+ enum: ["top", "right", "bottom", "left"],
5489
+ description: "Side of the anchor node to place the new node beside."
5490
+ },
5491
+ nodeIds: {
5492
+ type: "array",
5493
+ items: { type: "string" },
5494
+ description: "Array of node IDs for remove_nodes, select, or highlight."
5495
+ },
5496
+ source: {
5497
+ type: "string",
5498
+ description: "Source node ID for connect operation."
5499
+ },
5500
+ target: {
5501
+ type: "string",
5502
+ description: "Target node ID for connect operation."
5503
+ },
5504
+ label: {
5505
+ type: "string",
5506
+ description: "Optional edge label for connect operation."
5507
+ },
5508
+ direction: {
5509
+ type: "string",
5510
+ enum: ["LR", "TB", "RL", "BT"],
5511
+ description: "Layout direction (default LR)."
5512
+ },
5513
+ incremental: {
5514
+ type: "boolean",
5515
+ description: "Preserve existing node coordinates and blend layout (default true)."
5516
+ }
5517
+ },
5518
+ required: ["operation"]
5519
+ }
5520
+ };
5521
+ var MCP_TOOL_EDIT_PROMPT = {
5522
+ name: "edit_prompt",
5523
+ description: "Applies edits to the prompt template document, including inserting text, replacing ranges, and setting variables.",
5524
+ inputSchema: {
5525
+ type: "object",
5526
+ properties: {
5527
+ operation: {
5528
+ type: "string",
5529
+ enum: ["insert_text", "replace_range", "set_variable", "select"],
5530
+ description: "The prompt operation to execute."
5531
+ },
5532
+ text: {
5533
+ type: "string",
5534
+ description: "Text to insert or replace."
5535
+ },
5536
+ at: {
5537
+ type: "number",
5538
+ description: "Position to insert text at."
5539
+ },
5540
+ from: {
5541
+ type: "number",
5542
+ description: "Start offset for replace_range or select."
5543
+ },
5544
+ to: {
5545
+ type: "number",
5546
+ description: "End offset for replace_range or select."
5547
+ },
5548
+ name: {
5549
+ type: "string",
5550
+ description: "Variable name for set_variable."
5551
+ },
5552
+ value: {
5553
+ description: "Variable value for set_variable."
5554
+ }
5555
+ },
5556
+ required: ["operation"]
5557
+ }
5558
+ };
5559
+ var MCP_TOOL_VIEW_EDITOR = {
5560
+ name: "view_editor",
5561
+ description: "Captures a visual image snapshot (JPEG or PNG) of the editor canvas or viewport for multimodal evaluation. Active agent cursor indicators are automatically excluded from the snapshot.",
5562
+ inputSchema: {
5563
+ type: "object",
5564
+ properties: {
5565
+ scope: {
5566
+ type: "string",
5567
+ enum: ["viewport", "canvas"],
5568
+ description: "Whether to capture only the visible viewport or the full canvas bounding box (default: viewport)."
5569
+ },
5570
+ format: {
5571
+ type: "string",
5572
+ enum: ["jpeg", "png"],
5573
+ description: "Image format of the base64 snapshot (default: jpeg)."
5574
+ },
5575
+ maxEdge: {
5576
+ type: "integer",
5577
+ description: "Maximum width or height dimension in pixels (default: 1024)."
5578
+ }
5579
+ }
5580
+ }
5581
+ };
5582
+ var EXPORT_EXCLUDED_SELECTORS = [
5583
+ ".pixerate-presence",
5584
+ ".agent-presence",
5585
+ ".ursula-presence"
5586
+ ];
5587
+ var EDITOR_MCP_TOOLS = [
5588
+ MCP_TOOL_GET_EDITOR_STATE,
5589
+ MCP_TOOL_EDIT_CANVAS,
5590
+ MCP_TOOL_EDIT_PROMPT,
5591
+ MCP_TOOL_VIEW_EDITOR
5592
+ ];
4346
5593
  // Annotate the CommonJS export names for ESM import in node:
4347
5594
  0 && (module.exports = {
4348
5595
  ColorHighlighter,
5596
+ DEFAULT_NODE_HEIGHT,
5597
+ DEFAULT_NODE_WIDTH,
5598
+ DEFAULT_PLACEMENT_GAP,
4349
5599
  DataSourceBinder,
4350
5600
  DependencyGraph,
4351
5601
  DirtyTracker,
5602
+ EDITOR_MCP_TOOLS,
5603
+ EXPORT_EXCLUDED_SELECTORS,
4352
5604
  EditorController,
4353
5605
  FontSize,
4354
5606
  FormulaEvaluator,
@@ -4359,23 +5611,44 @@ function createHistoryManager(options = {}) {
4359
5611
  Image,
4360
5612
  LoadingNode,
4361
5613
  MARKDOWN_MENTION_SPLIT_REGEX,
5614
+ MCP_TOOL_EDIT_CANVAS,
5615
+ MCP_TOOL_EDIT_PROMPT,
5616
+ MCP_TOOL_GET_EDITOR_STATE,
5617
+ MCP_TOOL_VIEW_EDITOR,
4362
5618
  MENTION_REGEX,
5619
+ ManualClock,
4363
5620
  Markdown,
4364
5621
  Mention,
4365
5622
  SlashCommands,
4366
5623
  SmilieReplacer,
4367
5624
  SpreadsheetController,
5625
+ StreamingLayoutManager,
4368
5626
  TEMPLATE_REGEX,
4369
5627
  TemplateSuggestions,
4370
5628
  VARIABLE_REGEX,
5629
+ blendLayout,
5630
+ boundsOf,
5631
+ calculateDagreReflowDisplacement,
5632
+ calculateDirectionalDisplacement,
5633
+ canvasHeight,
5634
+ centerNodes,
5635
+ centre,
4371
5636
  colNameToIndex,
5637
+ computeNodesBoundingBox,
5638
+ connectedOnly,
5639
+ createBezierTrajectory,
4372
5640
  createDirtyTracker,
4373
5641
  createEditor,
5642
+ createFanOutTrajectory,
4374
5643
  createHistoryManager,
4375
5644
  createMentionExtension,
4376
5645
  createRichTextPreset,
5646
+ defaultEasing,
4377
5647
  defaultIsEqual,
5648
+ ensureLayout,
4378
5649
  escapeHtml,
5650
+ estimateCanvasHeight,
5651
+ estimateNodeSize,
4379
5652
  extractCellDependencies,
4380
5653
  extractInstructionNames,
4381
5654
  extractMentions,
@@ -4383,29 +5656,48 @@ function createHistoryManager(options = {}) {
4383
5656
  extractTemplateNames,
4384
5657
  extractVariableNames,
4385
5658
  findHexColors,
5659
+ flowSide,
5660
+ getCenteredNodePosition,
4386
5661
  getEditorMarkdown,
4387
5662
  getEditorText,
4388
5663
  getLatestVersion,
5664
+ getLayoutedNodes,
4389
5665
  handleImageInsertion,
4390
5666
  htmlToPlainText,
4391
5667
  imagePastePluginKey,
4392
5668
  indexToColName,
4393
5669
  insertImageNode,
4394
5670
  instructionRegex,
5671
+ isEventFromTextInput,
5672
+ isValidCanvasNode,
5673
+ isValidUrl,
5674
+ layoutBounds,
5675
+ linearPositionTrajectory,
5676
+ linearTrajectory,
4395
5677
  mapRawOffsetToResolved,
4396
5678
  mapResolvedOffsetToRaw,
4397
5679
  markdownToTipTapHtml,
5680
+ overlaps,
5681
+ pageHidden,
5682
+ parseCanvasClipboardData,
4398
5683
  parseCellReference,
4399
5684
  parseFormula,
4400
5685
  parseMentionSegments,
4401
5686
  parseRange,
5687
+ placeBeside,
4402
5688
  plainTextToTipTapHtml,
5689
+ planAgentCommand,
4403
5690
  preprocessMarkdownFileLinks,
4404
5691
  preprocessMarkdownMentions,
5692
+ realClock,
5693
+ remapPastedNodes,
4405
5694
  removeImageNode,
5695
+ repositionNodesTo,
4406
5696
  resolveTemplates,
4407
5697
  resolveTemplatesWithMapping,
4408
5698
  resolveVariables,
5699
+ runMultiNodeTransition,
5700
+ serializeCanvasNodes,
4409
5701
  stripMarkdownCode,
4410
5702
  templateRegex,
4411
5703
  tokenizeFormula,