@pixerate/editor 0.10.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
@@ -38,6 +38,7 @@ __export(src_exports, {
38
38
  DependencyGraph: () => DependencyGraph,
39
39
  DirtyTracker: () => DirtyTracker,
40
40
  EDITOR_MCP_TOOLS: () => EDITOR_MCP_TOOLS,
41
+ EXPORT_EXCLUDED_SELECTORS: () => EXPORT_EXCLUDED_SELECTORS,
41
42
  EditorController: () => EditorController,
42
43
  FontSize: () => FontSize,
43
44
  FormulaEvaluator: () => FormulaEvaluator,
@@ -51,7 +52,9 @@ __export(src_exports, {
51
52
  MCP_TOOL_EDIT_CANVAS: () => MCP_TOOL_EDIT_CANVAS,
52
53
  MCP_TOOL_EDIT_PROMPT: () => MCP_TOOL_EDIT_PROMPT,
53
54
  MCP_TOOL_GET_EDITOR_STATE: () => MCP_TOOL_GET_EDITOR_STATE,
55
+ MCP_TOOL_VIEW_EDITOR: () => MCP_TOOL_VIEW_EDITOR,
54
56
  MENTION_REGEX: () => MENTION_REGEX,
57
+ ManualClock: () => ManualClock,
55
58
  Markdown: () => Markdown2,
56
59
  Mention: () => Mention,
57
60
  SlashCommands: () => SlashCommands,
@@ -61,13 +64,16 @@ __export(src_exports, {
61
64
  TEMPLATE_REGEX: () => TEMPLATE_REGEX,
62
65
  TemplateSuggestions: () => TemplateSuggestions,
63
66
  VARIABLE_REGEX: () => VARIABLE_REGEX,
67
+ blendLayout: () => blendLayout,
64
68
  boundsOf: () => boundsOf,
65
69
  calculateDagreReflowDisplacement: () => calculateDagreReflowDisplacement,
66
70
  calculateDirectionalDisplacement: () => calculateDirectionalDisplacement,
71
+ canvasHeight: () => canvasHeight,
67
72
  centerNodes: () => centerNodes,
68
73
  centre: () => centre,
69
74
  colNameToIndex: () => colNameToIndex,
70
75
  computeNodesBoundingBox: () => computeNodesBoundingBox,
76
+ connectedOnly: () => connectedOnly,
71
77
  createBezierTrajectory: () => createBezierTrajectory,
72
78
  createDirtyTracker: () => createDirtyTracker,
73
79
  createEditor: () => createEditor,
@@ -79,6 +85,7 @@ __export(src_exports, {
79
85
  defaultIsEqual: () => defaultIsEqual,
80
86
  ensureLayout: () => ensureLayout,
81
87
  escapeHtml: () => escapeHtml,
88
+ estimateCanvasHeight: () => estimateCanvasHeight,
82
89
  estimateNodeSize: () => estimateNodeSize,
83
90
  extractCellDependencies: () => extractCellDependencies,
84
91
  extractInstructionNames: () => extractInstructionNames,
@@ -102,12 +109,14 @@ __export(src_exports, {
102
109
  isEventFromTextInput: () => isEventFromTextInput,
103
110
  isValidCanvasNode: () => isValidCanvasNode,
104
111
  isValidUrl: () => isValidUrl,
112
+ layoutBounds: () => layoutBounds,
105
113
  linearPositionTrajectory: () => linearPositionTrajectory,
106
114
  linearTrajectory: () => linearTrajectory,
107
115
  mapRawOffsetToResolved: () => mapRawOffsetToResolved,
108
116
  mapResolvedOffsetToRaw: () => mapResolvedOffsetToRaw,
109
117
  markdownToTipTapHtml: () => markdownToTipTapHtml,
110
118
  overlaps: () => overlaps,
119
+ pageHidden: () => pageHidden,
111
120
  parseCanvasClipboardData: () => parseCanvasClipboardData,
112
121
  parseCellReference: () => parseCellReference,
113
122
  parseFormula: () => parseFormula,
@@ -118,6 +127,7 @@ __export(src_exports, {
118
127
  planAgentCommand: () => planAgentCommand,
119
128
  preprocessMarkdownFileLinks: () => preprocessMarkdownFileLinks,
120
129
  preprocessMarkdownMentions: () => preprocessMarkdownMentions,
130
+ realClock: () => realClock,
121
131
  remapPastedNodes: () => remapPastedNodes,
122
132
  removeImageNode: () => removeImageNode,
123
133
  repositionNodesTo: () => repositionNodesTo,
@@ -4585,6 +4595,79 @@ function placeBeside(rects, size, side, nearNodeId, options = {}) {
4585
4595
  };
4586
4596
  }
4587
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
+
4588
4671
  // src/canvas/trajectory.ts
4589
4672
  var defaultEasing = (t) => --t * t * t + 1;
4590
4673
  var linearTrajectory = (origin, target, progress) => {
@@ -4652,9 +4735,15 @@ function runMultiNodeTransition(transitions, options = {}) {
4652
4735
  return () => {
4653
4736
  };
4654
4737
  }
4655
- const { defaultDuration = 400, defaultEasing: easingFunc = defaultEasing, onUpdate, onComplete } = options;
4738
+ const {
4739
+ defaultDuration = 400,
4740
+ defaultEasing: easingFunc = defaultEasing,
4741
+ clock = realClock,
4742
+ onUpdate,
4743
+ onComplete
4744
+ } = options;
4656
4745
  let isCancelled = false;
4657
- const startTime = Date.now();
4746
+ const startTime = clock.now();
4658
4747
  const items = transitions.map((tr, i) => ({
4659
4748
  ...tr,
4660
4749
  index: tr.index ?? i,
@@ -4666,7 +4755,7 @@ function runMultiNodeTransition(transitions, options = {}) {
4666
4755
  }));
4667
4756
  function frame() {
4668
4757
  if (isCancelled) return;
4669
- const now = Date.now();
4758
+ const now = clock.now();
4670
4759
  let allCompleted = true;
4671
4760
  for (let i = 0; i < items.length; i++) {
4672
4761
  const item = items[i];
@@ -4698,9 +4787,7 @@ function runMultiNodeTransition(transitions, options = {}) {
4698
4787
  }
4699
4788
  onUpdate?.();
4700
4789
  if (!allCompleted) {
4701
- if (typeof requestAnimationFrame !== "undefined") {
4702
- requestAnimationFrame(frame);
4703
- }
4790
+ clock.frame().then(frame);
4704
4791
  } else {
4705
4792
  onComplete?.();
4706
4793
  }
@@ -4978,10 +5065,76 @@ function repositionNodesTo(nodes, targetOrigin) {
4978
5065
  }));
4979
5066
  }
4980
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
+
4981
5132
  // src/canvas/streaming.ts
4982
5133
  var StreamingLayoutManager = class {
4983
5134
  throttleMs;
4984
5135
  glideDurationMs;
5136
+ connectedOnly;
5137
+ clock;
4985
5138
  layoutOptions;
4986
5139
  onLayoutUpdated;
4987
5140
  pendingTimer = null;
@@ -4990,6 +5143,8 @@ var StreamingLayoutManager = class {
4990
5143
  constructor(options = {}) {
4991
5144
  this.throttleMs = options.throttleMs ?? 200;
4992
5145
  this.glideDurationMs = options.glideDurationMs ?? 300;
5146
+ this.connectedOnly = options.connectedOnly ?? false;
5147
+ this.clock = options.clock ?? realClock;
4993
5148
  this.layoutOptions = options.layoutOptions ?? { direction: "LR" };
4994
5149
  this.onLayoutUpdated = options.onLayoutUpdated;
4995
5150
  }
@@ -5017,7 +5172,8 @@ var StreamingLayoutManager = class {
5017
5172
  this.recalculateAndGlide(nodes, edges, currentPositions);
5018
5173
  }
5019
5174
  recalculateAndGlide(nodes, edges, currentPositions) {
5020
- if (nodes.length === 0) return;
5175
+ const targetNodes = this.connectedOnly ? connectedOnly(nodes, edges) : nodes;
5176
+ if (targetNodes.length === 0) return;
5021
5177
  if (this.activeTransitionCancel) {
5022
5178
  this.activeTransitionCancel();
5023
5179
  this.activeTransitionCancel = null;
@@ -5026,7 +5182,7 @@ var StreamingLayoutManager = class {
5026
5182
  for (const [id, pos] of currentPositions.entries()) {
5027
5183
  stored[id] = pos;
5028
5184
  }
5029
- const layouted = ensureLayout(nodes, edges, {
5185
+ const layouted = ensureLayout(targetNodes, edges, {
5030
5186
  ...this.layoutOptions,
5031
5187
  storedPositions: stored
5032
5188
  });
@@ -5050,6 +5206,7 @@ var StreamingLayoutManager = class {
5050
5206
  this.targetPositions = nextPositions;
5051
5207
  if (transitions.length > 0) {
5052
5208
  this.activeTransitionCancel = runMultiNodeTransition(transitions, {
5209
+ clock: this.clock,
5053
5210
  onUpdate: () => {
5054
5211
  const liveMap = new Map(currentPositions);
5055
5212
  for (const trans of transitions) {
@@ -5257,6 +5414,24 @@ function planAgentCommand(command, snapshot, options = {}) {
5257
5414
  }
5258
5415
  ];
5259
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
+ }
5260
5435
  default: {
5261
5436
  return [];
5262
5437
  }
@@ -5381,10 +5556,39 @@ var MCP_TOOL_EDIT_PROMPT = {
5381
5556
  required: ["operation"]
5382
5557
  }
5383
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
+ ];
5384
5587
  var EDITOR_MCP_TOOLS = [
5385
5588
  MCP_TOOL_GET_EDITOR_STATE,
5386
5589
  MCP_TOOL_EDIT_CANVAS,
5387
- MCP_TOOL_EDIT_PROMPT
5590
+ MCP_TOOL_EDIT_PROMPT,
5591
+ MCP_TOOL_VIEW_EDITOR
5388
5592
  ];
5389
5593
  // Annotate the CommonJS export names for ESM import in node:
5390
5594
  0 && (module.exports = {
@@ -5396,6 +5600,7 @@ var EDITOR_MCP_TOOLS = [
5396
5600
  DependencyGraph,
5397
5601
  DirtyTracker,
5398
5602
  EDITOR_MCP_TOOLS,
5603
+ EXPORT_EXCLUDED_SELECTORS,
5399
5604
  EditorController,
5400
5605
  FontSize,
5401
5606
  FormulaEvaluator,
@@ -5409,7 +5614,9 @@ var EDITOR_MCP_TOOLS = [
5409
5614
  MCP_TOOL_EDIT_CANVAS,
5410
5615
  MCP_TOOL_EDIT_PROMPT,
5411
5616
  MCP_TOOL_GET_EDITOR_STATE,
5617
+ MCP_TOOL_VIEW_EDITOR,
5412
5618
  MENTION_REGEX,
5619
+ ManualClock,
5413
5620
  Markdown,
5414
5621
  Mention,
5415
5622
  SlashCommands,
@@ -5419,13 +5626,16 @@ var EDITOR_MCP_TOOLS = [
5419
5626
  TEMPLATE_REGEX,
5420
5627
  TemplateSuggestions,
5421
5628
  VARIABLE_REGEX,
5629
+ blendLayout,
5422
5630
  boundsOf,
5423
5631
  calculateDagreReflowDisplacement,
5424
5632
  calculateDirectionalDisplacement,
5633
+ canvasHeight,
5425
5634
  centerNodes,
5426
5635
  centre,
5427
5636
  colNameToIndex,
5428
5637
  computeNodesBoundingBox,
5638
+ connectedOnly,
5429
5639
  createBezierTrajectory,
5430
5640
  createDirtyTracker,
5431
5641
  createEditor,
@@ -5437,6 +5647,7 @@ var EDITOR_MCP_TOOLS = [
5437
5647
  defaultIsEqual,
5438
5648
  ensureLayout,
5439
5649
  escapeHtml,
5650
+ estimateCanvasHeight,
5440
5651
  estimateNodeSize,
5441
5652
  extractCellDependencies,
5442
5653
  extractInstructionNames,
@@ -5460,12 +5671,14 @@ var EDITOR_MCP_TOOLS = [
5460
5671
  isEventFromTextInput,
5461
5672
  isValidCanvasNode,
5462
5673
  isValidUrl,
5674
+ layoutBounds,
5463
5675
  linearPositionTrajectory,
5464
5676
  linearTrajectory,
5465
5677
  mapRawOffsetToResolved,
5466
5678
  mapResolvedOffsetToRaw,
5467
5679
  markdownToTipTapHtml,
5468
5680
  overlaps,
5681
+ pageHidden,
5469
5682
  parseCanvasClipboardData,
5470
5683
  parseCellReference,
5471
5684
  parseFormula,
@@ -5476,6 +5689,7 @@ var EDITOR_MCP_TOOLS = [
5476
5689
  planAgentCommand,
5477
5690
  preprocessMarkdownFileLinks,
5478
5691
  preprocessMarkdownMentions,
5692
+ realClock,
5479
5693
  remapPastedNodes,
5480
5694
  removeImageNode,
5481
5695
  repositionNodesTo,