@particle-academy/agent-integrations 0.6.2 → 0.7.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 (59) hide show
  1. package/dist/bridges/artboard.d.cts +78 -0
  2. package/dist/bridges/artboard.d.ts +78 -0
  3. package/dist/bridges/charts.d.cts +3 -3
  4. package/dist/bridges/charts.d.ts +3 -3
  5. package/dist/bridges/code.d.cts +3 -3
  6. package/dist/bridges/code.d.ts +3 -3
  7. package/dist/bridges/flow.d.cts +3 -3
  8. package/dist/bridges/flow.d.ts +3 -3
  9. package/dist/bridges/forms.d.cts +3 -3
  10. package/dist/bridges/forms.d.ts +3 -3
  11. package/dist/bridges/scene.d.cts +3 -3
  12. package/dist/bridges/scene.d.ts +3 -3
  13. package/dist/bridges/screens.d.cts +3 -3
  14. package/dist/bridges/screens.d.ts +3 -3
  15. package/dist/bridges/sheets.d.cts +3 -3
  16. package/dist/bridges/sheets.d.ts +3 -3
  17. package/dist/bridges/slides.d.cts +58 -0
  18. package/dist/bridges/slides.d.ts +58 -0
  19. package/dist/bridges/whiteboard.d.cts +3 -3
  20. package/dist/bridges/whiteboard.d.ts +3 -3
  21. package/dist/bridges-artboard.cjs +576 -0
  22. package/dist/bridges-artboard.cjs.map +1 -0
  23. package/dist/bridges-artboard.js +418 -0
  24. package/dist/bridges-artboard.js.map +1 -0
  25. package/dist/bridges-slides.cjs +438 -0
  26. package/dist/bridges-slides.cjs.map +1 -0
  27. package/dist/bridges-slides.js +6 -0
  28. package/dist/bridges-slides.js.map +1 -0
  29. package/dist/chunk-NE3GIGQD.js +384 -0
  30. package/dist/chunk-NE3GIGQD.js.map +1 -0
  31. package/dist/components/SharedWhiteboard/index.d.cts +1 -1
  32. package/dist/components/SharedWhiteboard/index.d.ts +1 -1
  33. package/dist/index.cjs +377 -0
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +9 -7
  36. package/dist/index.d.ts +9 -7
  37. package/dist/index.js +3 -2
  38. package/dist/index.js.map +1 -1
  39. package/dist/mcp/index.d.cts +5 -5
  40. package/dist/mcp/index.d.ts +5 -5
  41. package/dist/presence/index.d.cts +1 -1
  42. package/dist/presence/index.d.ts +1 -1
  43. package/dist/{server-BsSwfemr.d.cts → server-C2OpfPEo.d.cts} +3 -3
  44. package/dist/{server-Du3-IGqM.d.ts → server-CKAqFTyc.d.ts} +3 -3
  45. package/dist/sharing/index.d.cts +4 -4
  46. package/dist/sharing/index.d.ts +4 -4
  47. package/dist/sheets-adapter.d.cts +3 -3
  48. package/dist/sheets-adapter.d.ts +3 -3
  49. package/dist/{token-CrJF76oH.d.cts → token-C1O22GxJ.d.cts} +1 -1
  50. package/dist/{token-CrJF76oH.d.ts → token-C1O22GxJ.d.ts} +1 -1
  51. package/dist/{tool-host-BQuUygLF.d.cts → tool-host-CX3WFXgh.d.cts} +1 -1
  52. package/dist/{tool-host-C8JMMGYq.d.ts → tool-host-DldwGNqR.d.ts} +1 -1
  53. package/dist/{types-aOQLTW0E.d.cts → types-C2zdUpzn.d.cts} +1 -1
  54. package/dist/{types-aOQLTW0E.d.ts → types-C2zdUpzn.d.ts} +1 -1
  55. package/dist/{types-CCSBGW9T.d.cts → types-Cq5u8MJ8.d.cts} +1 -1
  56. package/dist/{types-DIVNcIQO.d.ts → types-DyaHnqNC.d.ts} +1 -1
  57. package/dist/undo/index.d.cts +2 -2
  58. package/dist/undo/index.d.ts +2 -2
  59. package/package.json +17 -4
@@ -0,0 +1,576 @@
1
+ 'use strict';
2
+
3
+ // src/mcp/server.ts
4
+ function textResult(text, structured) {
5
+ return {
6
+ content: [{ type: "text", text }],
7
+ ...structured !== void 0 ? { structuredContent: structured } : {}
8
+ };
9
+ }
10
+ function errorResult(text) {
11
+ return { content: [{ type: "text", text }], isError: true };
12
+ }
13
+ var listeners = /* @__PURE__ */ new Set();
14
+ function emitActivity(event) {
15
+ for (const l of listeners) l(event);
16
+ }
17
+
18
+ // src/presence/wrap-tool-with-activity.ts
19
+ function wrapToolWithActivity(handler, options) {
20
+ return async (args) => {
21
+ const result = await handler(args);
22
+ if (result.isError) return result;
23
+ let target;
24
+ if (options.resolveTarget) {
25
+ target = options.resolveTarget({ toolName: options.toolName, args, result });
26
+ } else {
27
+ target = { kind: options.kind, screenId: options.screenId };
28
+ }
29
+ if (!target) return result;
30
+ emitActivity({
31
+ agentId: options.agent.id,
32
+ agentName: options.agent.name,
33
+ agentColor: options.agent.color,
34
+ target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
35
+ action: options.toolName,
36
+ timestamp: Date.now(),
37
+ meta: extractMeta(result),
38
+ ttlMs: options.ttlMs
39
+ });
40
+ return result;
41
+ };
42
+ }
43
+ function extractMeta(result) {
44
+ const sc = result.structuredContent;
45
+ if (sc && typeof sc === "object" && !Array.isArray(sc)) {
46
+ return sc;
47
+ }
48
+ return void 0;
49
+ }
50
+
51
+ // src/undo/undo-stack.ts
52
+ var stacks = /* @__PURE__ */ new Map();
53
+ var CAP = 200;
54
+ function getStack(agentId) {
55
+ let s = stacks.get(agentId);
56
+ if (!s) {
57
+ s = { past: [], future: [] };
58
+ stacks.set(agentId, s);
59
+ }
60
+ return s;
61
+ }
62
+ function pushUndoEntry(agentId, entry) {
63
+ const s = getStack(agentId);
64
+ s.past.push(entry);
65
+ if (s.past.length > CAP) s.past.splice(0, s.past.length - CAP);
66
+ s.future.length = 0;
67
+ }
68
+ async function undoOne(agentId) {
69
+ const s = getStack(agentId);
70
+ const entry = s.past.pop();
71
+ if (!entry) return null;
72
+ await entry.undo();
73
+ s.future.push(entry);
74
+ return entry;
75
+ }
76
+ async function redoOne(agentId) {
77
+ const s = getStack(agentId);
78
+ const entry = s.future.pop();
79
+ if (!entry) return null;
80
+ await entry.redo();
81
+ s.past.push(entry);
82
+ return entry;
83
+ }
84
+ function readHistory(agentId) {
85
+ return getStack(agentId).past.slice();
86
+ }
87
+
88
+ // src/undo/undo-tools.ts
89
+ var installedHosts = /* @__PURE__ */ new WeakSet();
90
+ function ensureUndoToolsRegistered(host, options = {}) {
91
+ if (installedHosts.has(host)) return;
92
+ installedHosts.add(host);
93
+ registerUndoTools(host, options);
94
+ }
95
+ function registerUndoTools(host, options = {}) {
96
+ const defaultAgent = options.defaultAgentId ?? "agent";
97
+ const disposers = [];
98
+ const agentOf = (args) => typeof args?.agentId === "string" ? args.agentId : defaultAgent;
99
+ disposers.push(
100
+ host.registerTool(
101
+ {
102
+ name: "agent_undo",
103
+ description: "Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.",
104
+ inputSchema: {
105
+ type: "object",
106
+ properties: { agentId: { type: "string" } },
107
+ additionalProperties: false
108
+ }
109
+ },
110
+ async (args) => {
111
+ const entry = await undoOne(agentOf(args));
112
+ if (!entry) return errorResult("Nothing to undo.");
113
+ return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });
114
+ }
115
+ )
116
+ );
117
+ disposers.push(
118
+ host.registerTool(
119
+ {
120
+ name: "agent_redo",
121
+ description: "Redo the most recently undone action.",
122
+ inputSchema: {
123
+ type: "object",
124
+ properties: { agentId: { type: "string" } },
125
+ additionalProperties: false
126
+ }
127
+ },
128
+ async (args) => {
129
+ const entry = await redoOne(agentOf(args));
130
+ if (!entry) return errorResult("Nothing to redo.");
131
+ return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });
132
+ }
133
+ )
134
+ );
135
+ disposers.push(
136
+ host.registerTool(
137
+ {
138
+ name: "agent_history",
139
+ description: "List the agent's undo stack (oldest first). Useful for understanding what's reversible.",
140
+ inputSchema: {
141
+ type: "object",
142
+ properties: { agentId: { type: "string" } },
143
+ additionalProperties: false
144
+ }
145
+ },
146
+ async (args) => {
147
+ const history2 = readHistory(agentOf(args)).map(serialize);
148
+ const text = history2.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join("\n");
149
+ return textResult(text || "(empty)", history2);
150
+ }
151
+ )
152
+ );
153
+ return () => disposers.forEach((d) => d());
154
+ }
155
+ function serialize(entry) {
156
+ return {
157
+ timestamp: entry.timestamp,
158
+ bridgeId: entry.bridgeId,
159
+ action: entry.action,
160
+ label: entry.label
161
+ };
162
+ }
163
+
164
+ // src/bridges/artboard.ts
165
+ var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
166
+ var num = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : fallback ?? 0;
167
+ var str = (v, fallback = "") => typeof v === "string" ? v : fallback;
168
+ var newId = (prefix) => `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
169
+ var clone = (v) => JSON.parse(JSON.stringify(v));
170
+ var coerceContent = (v) => {
171
+ if (v && typeof v === "object") {
172
+ const c = v;
173
+ if (c.kind === "image") return { kind: "image", src: str(c.src), ...c.alt !== void 0 ? { alt: str(c.alt) } : {} };
174
+ if (c.kind === "html") return { kind: "html", html: str(c.html) };
175
+ if (c.kind === "node") return { kind: "node" };
176
+ }
177
+ return { kind: "node" };
178
+ };
179
+ function registerArtboardBridge(host, options) {
180
+ const { adapter } = options;
181
+ const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
182
+ const disposers = [];
183
+ ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
184
+ const abTarget = (args, result) => ({
185
+ kind: "artboard",
186
+ id: result?.structuredContent?.id ?? args?.pieceId ?? args?.sectionId
187
+ });
188
+ const reg = (name, description, inputProperties, required, handler, resolveTarget) => {
189
+ const wrapped = async (args) => {
190
+ try {
191
+ return await handler(args);
192
+ } catch (e) {
193
+ return errorResult(e instanceof Error ? e.message : String(e));
194
+ }
195
+ };
196
+ const final = resolveTarget ? wrapToolWithActivity(wrapped, {
197
+ toolName: name,
198
+ agent: { id: agent.id, name: agent.name, color: agent.color },
199
+ kind: "artboard",
200
+ resolveTarget: ({ args, result }) => resolveTarget(args, result)
201
+ }) : wrapped;
202
+ disposers.push(
203
+ host.registerTool(
204
+ {
205
+ name,
206
+ description,
207
+ inputSchema: {
208
+ type: "object",
209
+ properties: inputProperties,
210
+ required,
211
+ additionalProperties: false
212
+ }
213
+ },
214
+ final
215
+ )
216
+ );
217
+ };
218
+ reg("artboard_get_state", "Get the full board state: viewport, focus, all sections and pieces.", {}, [], () => {
219
+ const state = {
220
+ viewport: adapter.getViewport(),
221
+ focus: adapter.getFocus(),
222
+ sections: adapter.getValue().sections
223
+ };
224
+ return textResult(JSON.stringify(state, null, 2), state);
225
+ });
226
+ reg("artboard_list_pieces", "List sections and their pieces with id, label, and content kind.", {}, [], () => {
227
+ const lines = [];
228
+ const summary = [];
229
+ for (const s of adapter.getValue().sections) {
230
+ lines.push(`section ${s.id}: "${s.title}"`);
231
+ const pieces = s.pieces.map((p) => ({
232
+ id: p.id,
233
+ ...p.label !== void 0 ? { label: p.label } : {},
234
+ kind: p.content.kind,
235
+ ...p.pending ? { pending: true } : {}
236
+ }));
237
+ for (const p of s.pieces) {
238
+ lines.push(` piece ${p.id}: ${p.label ? `"${p.label}" ` : ""}[${p.content.kind}]${p.pending ? " (pending)" : ""}`);
239
+ }
240
+ summary.push({ sectionId: s.id, title: s.title, pieces });
241
+ }
242
+ return textResult(lines.join("\n") || "(empty board)", summary);
243
+ });
244
+ reg(
245
+ "artboard_add_piece",
246
+ "Add a piece to a section. New agent-added pieces default to pending:true (trust-but-verify \u2014 a human confirms before it is final).",
247
+ {
248
+ sectionId: { type: "string" },
249
+ piece: {
250
+ type: "object",
251
+ description: "Piece spec: { label?, width?, height?, content }. content is { kind: 'image', src, alt? } | { kind: 'html', html } | { kind: 'node' }."
252
+ },
253
+ index: { type: "number", description: "Insertion index within the section. Defaults to the end." }
254
+ },
255
+ ["sectionId", "piece"],
256
+ (args) => {
257
+ const sectionId = str(args.sectionId);
258
+ const section = adapter.getValue().sections.find((s) => s.id === sectionId);
259
+ if (!section) return errorResult(`No section with id ${sectionId}`);
260
+ const spec = args.piece ?? {};
261
+ const piece = {
262
+ id: newId("p"),
263
+ ...spec.label !== void 0 ? { label: str(spec.label) } : {},
264
+ ...spec.width !== void 0 ? { width: num(spec.width) } : {},
265
+ ...spec.height !== void 0 ? { height: num(spec.height) } : {},
266
+ content: coerceContent(spec.content),
267
+ pending: true
268
+ };
269
+ const insertAt = args.index !== void 0 ? Math.max(0, Math.min(num(args.index), section.pieces.length)) : section.pieces.length;
270
+ adapter.setValue((prev) => ({
271
+ sections: prev.sections.map(
272
+ (s) => s.id !== sectionId ? s : { ...s, pieces: [...s.pieces.slice(0, insertAt), piece, ...s.pieces.slice(insertAt)] }
273
+ )
274
+ }));
275
+ pushUndoEntry(agent.id, {
276
+ timestamp: Date.now(),
277
+ bridgeId: "artboard",
278
+ action: "artboard_add_piece",
279
+ label: `Added piece ${piece.id} to ${sectionId}`,
280
+ undo: () => adapter.setValue((prev) => ({
281
+ sections: prev.sections.map(
282
+ (s) => s.id !== sectionId ? s : { ...s, pieces: s.pieces.filter((p) => p.id !== piece.id) }
283
+ )
284
+ })),
285
+ redo: () => adapter.setValue((prev) => ({
286
+ sections: prev.sections.map(
287
+ (s) => s.id !== sectionId ? s : { ...s, pieces: [...s.pieces.slice(0, insertAt), piece, ...s.pieces.slice(insertAt)] }
288
+ )
289
+ }))
290
+ });
291
+ return textResult(`Added piece ${piece.id} to section ${sectionId} (pending)`, piece);
292
+ },
293
+ abTarget
294
+ );
295
+ reg(
296
+ "artboard_remove_piece",
297
+ "Remove a piece by id.",
298
+ { pieceId: { type: "string" } },
299
+ ["pieceId"],
300
+ (args) => {
301
+ const pieceId = str(args.pieceId);
302
+ let removed = null;
303
+ let fromSection = null;
304
+ let oldIndex = -1;
305
+ for (const s of adapter.getValue().sections) {
306
+ const idx = s.pieces.findIndex((p) => p.id === pieceId);
307
+ if (idx !== -1) {
308
+ removed = clone(s.pieces[idx]);
309
+ fromSection = s.id;
310
+ oldIndex = idx;
311
+ break;
312
+ }
313
+ }
314
+ if (!removed || fromSection === null) return errorResult(`No piece with id ${pieceId}`);
315
+ const sectionId = fromSection;
316
+ const snapshot = removed;
317
+ const at = oldIndex;
318
+ adapter.setValue((prev) => ({
319
+ sections: prev.sections.map(
320
+ (s) => s.id !== sectionId ? s : { ...s, pieces: s.pieces.filter((p) => p.id !== pieceId) }
321
+ )
322
+ }));
323
+ pushUndoEntry(agent.id, {
324
+ timestamp: Date.now(),
325
+ bridgeId: "artboard",
326
+ action: "artboard_remove_piece",
327
+ label: `Removed piece ${pieceId}`,
328
+ undo: () => adapter.setValue((prev) => ({
329
+ sections: prev.sections.map(
330
+ (s) => s.id !== sectionId ? s : { ...s, pieces: [...s.pieces.slice(0, at), clone(snapshot), ...s.pieces.slice(at)] }
331
+ )
332
+ })),
333
+ redo: () => adapter.setValue((prev) => ({
334
+ sections: prev.sections.map(
335
+ (s) => s.id !== sectionId ? s : { ...s, pieces: s.pieces.filter((p) => p.id !== pieceId) }
336
+ )
337
+ }))
338
+ });
339
+ return textResult(`Removed piece ${pieceId}`, { id: pieceId });
340
+ },
341
+ abTarget
342
+ );
343
+ reg(
344
+ "artboard_reorder_piece",
345
+ "Move a piece to a new index, optionally into a different section.",
346
+ {
347
+ pieceId: { type: "string" },
348
+ sectionId: { type: "string", description: "Target section id. Defaults to the piece's current section." },
349
+ toIndex: { type: "number" }
350
+ },
351
+ ["pieceId", "toIndex"],
352
+ (args) => {
353
+ const pieceId = str(args.pieceId);
354
+ const before = adapter.getValue();
355
+ let piece = null;
356
+ let srcSection = null;
357
+ for (const s of before.sections) {
358
+ const found = s.pieces.find((p) => p.id === pieceId);
359
+ if (found) {
360
+ piece = found;
361
+ srcSection = s.id;
362
+ break;
363
+ }
364
+ }
365
+ if (!piece || srcSection === null) return errorResult(`No piece with id ${pieceId}`);
366
+ const dstSection = args.sectionId !== void 0 ? str(args.sectionId) : srcSection;
367
+ if (!before.sections.some((s) => s.id === dstSection)) return errorResult(`No section with id ${dstSection}`);
368
+ const toIndex = Math.max(0, num(args.toIndex));
369
+ const moving = piece;
370
+ const snapshot = clone(before);
371
+ adapter.setValue((prev) => {
372
+ const stripped = prev.sections.map((s) => ({ ...s, pieces: s.pieces.filter((p) => p.id !== pieceId) }));
373
+ return {
374
+ sections: stripped.map((s) => {
375
+ if (s.id !== dstSection) return s;
376
+ const clamped = Math.min(toIndex, s.pieces.length);
377
+ return { ...s, pieces: [...s.pieces.slice(0, clamped), moving, ...s.pieces.slice(clamped)] };
378
+ })
379
+ };
380
+ });
381
+ pushUndoEntry(agent.id, {
382
+ timestamp: Date.now(),
383
+ bridgeId: "artboard",
384
+ action: "artboard_reorder_piece",
385
+ label: `Reordered piece ${pieceId}`,
386
+ undo: () => adapter.setValue(() => clone(snapshot)),
387
+ redo: () => adapter.setValue((prev) => {
388
+ const stripped = prev.sections.map((s) => ({ ...s, pieces: s.pieces.filter((p) => p.id !== pieceId) }));
389
+ return {
390
+ sections: stripped.map((s) => {
391
+ if (s.id !== dstSection) return s;
392
+ const clamped = Math.min(toIndex, s.pieces.length);
393
+ return { ...s, pieces: [...s.pieces.slice(0, clamped), moving, ...s.pieces.slice(clamped)] };
394
+ })
395
+ };
396
+ })
397
+ });
398
+ return textResult(`Reordered piece ${pieceId} \u2192 ${dstSection}[${toIndex}]`, { id: pieceId, sectionId: dstSection, toIndex });
399
+ },
400
+ abTarget
401
+ );
402
+ reg(
403
+ "artboard_rename_piece",
404
+ "Set a piece's label.",
405
+ { pieceId: { type: "string" }, label: { type: "string" } },
406
+ ["pieceId", "label"],
407
+ (args) => {
408
+ const pieceId = str(args.pieceId);
409
+ const label = str(args.label);
410
+ const existing = adapter.getValue().sections.flatMap((s) => s.pieces).find((p) => p.id === pieceId);
411
+ if (!existing) return errorResult(`No piece with id ${pieceId}`);
412
+ const prevLabel = existing.label;
413
+ const apply = (l) => adapter.setValue((prev) => ({
414
+ sections: prev.sections.map((s) => ({
415
+ ...s,
416
+ pieces: s.pieces.map((p) => p.id !== pieceId ? p : { ...p, ...l !== void 0 ? { label: l } : {} })
417
+ }))
418
+ }));
419
+ apply(label);
420
+ pushUndoEntry(agent.id, {
421
+ timestamp: Date.now(),
422
+ bridgeId: "artboard",
423
+ action: "artboard_rename_piece",
424
+ label: `Renamed piece ${pieceId}`,
425
+ undo: () => apply(prevLabel),
426
+ redo: () => apply(label)
427
+ });
428
+ return textResult(`Renamed piece ${pieceId} \u2192 "${label}"`, { id: pieceId, label });
429
+ },
430
+ abTarget
431
+ );
432
+ reg(
433
+ "artboard_set_piece_content",
434
+ "Replace a piece's content. content is { kind: 'image', src, alt? } | { kind: 'html', html } | { kind: 'node' }.",
435
+ { pieceId: { type: "string" }, content: { type: "object" } },
436
+ ["pieceId", "content"],
437
+ (args) => {
438
+ const pieceId = str(args.pieceId);
439
+ const existing = adapter.getValue().sections.flatMap((s) => s.pieces).find((p) => p.id === pieceId);
440
+ if (!existing) return errorResult(`No piece with id ${pieceId}`);
441
+ const prevContent = clone(existing.content);
442
+ const nextContent = coerceContent(args.content);
443
+ const apply = (c) => adapter.setValue((prev) => ({
444
+ sections: prev.sections.map((s) => ({
445
+ ...s,
446
+ pieces: s.pieces.map((p) => p.id !== pieceId ? p : { ...p, content: clone(c) })
447
+ }))
448
+ }));
449
+ apply(nextContent);
450
+ pushUndoEntry(agent.id, {
451
+ timestamp: Date.now(),
452
+ bridgeId: "artboard",
453
+ action: "artboard_set_piece_content",
454
+ label: `Set content of piece ${pieceId}`,
455
+ undo: () => apply(prevContent),
456
+ redo: () => apply(nextContent)
457
+ });
458
+ return textResult(`Set content of piece ${pieceId} \u2192 ${nextContent.kind}`, { id: pieceId, content: nextContent });
459
+ },
460
+ abTarget
461
+ );
462
+ reg(
463
+ "artboard_focus_piece",
464
+ "Focus a piece by id (or pass null to clear focus).",
465
+ { pieceId: { type: ["string", "null"] } },
466
+ [],
467
+ (args) => {
468
+ const raw = args.pieceId;
469
+ const pieceId = typeof raw === "string" ? raw : null;
470
+ if (pieceId !== null && !adapter.getValue().sections.some((s) => s.pieces.some((p) => p.id === pieceId))) {
471
+ return errorResult(`No piece with id ${pieceId}`);
472
+ }
473
+ adapter.setFocus(pieceId);
474
+ return textResult(pieceId ? `Focused piece ${pieceId}` : "Cleared focus", { id: pieceId });
475
+ },
476
+ abTarget
477
+ );
478
+ reg(
479
+ "artboard_add_section",
480
+ "Add a section. Provide an explicit id or one is generated.",
481
+ {
482
+ section: { type: "object", description: "Section spec: { id?, title, subtitle? }." },
483
+ index: { type: "number", description: "Insertion index. Defaults to the end." }
484
+ },
485
+ ["section"],
486
+ (args) => {
487
+ const spec = args.section ?? {};
488
+ const section = {
489
+ id: spec.id !== void 0 ? str(spec.id) : newId("sec"),
490
+ title: str(spec.title),
491
+ ...spec.subtitle !== void 0 ? { subtitle: str(spec.subtitle) } : {},
492
+ pieces: []
493
+ };
494
+ if (adapter.getValue().sections.some((s) => s.id === section.id)) {
495
+ return errorResult(`Section id ${section.id} already exists`);
496
+ }
497
+ const insertAt = args.index !== void 0 ? Math.max(0, Math.min(num(args.index), adapter.getValue().sections.length)) : adapter.getValue().sections.length;
498
+ adapter.setValue((prev) => ({
499
+ sections: [...prev.sections.slice(0, insertAt), section, ...prev.sections.slice(insertAt)]
500
+ }));
501
+ pushUndoEntry(agent.id, {
502
+ timestamp: Date.now(),
503
+ bridgeId: "artboard",
504
+ action: "artboard_add_section",
505
+ label: `Added section ${section.id}`,
506
+ undo: () => adapter.setValue((prev) => ({ sections: prev.sections.filter((s) => s.id !== section.id) })),
507
+ redo: () => adapter.setValue((prev) => ({
508
+ sections: [...prev.sections.slice(0, insertAt), section, ...prev.sections.slice(insertAt)]
509
+ }))
510
+ });
511
+ return textResult(`Added section ${section.id}`, section);
512
+ },
513
+ abTarget
514
+ );
515
+ reg(
516
+ "artboard_rename_section",
517
+ "Set a section's title and optionally its subtitle.",
518
+ { sectionId: { type: "string" }, title: { type: "string" }, subtitle: { type: "string" } },
519
+ ["sectionId", "title"],
520
+ (args) => {
521
+ const sectionId = str(args.sectionId);
522
+ const existing = adapter.getValue().sections.find((s) => s.id === sectionId);
523
+ if (!existing) return errorResult(`No section with id ${sectionId}`);
524
+ const prevTitle = existing.title;
525
+ const prevSubtitle = existing.subtitle;
526
+ const title = str(args.title);
527
+ const hasSubtitle = args.subtitle !== void 0;
528
+ const subtitle = hasSubtitle ? str(args.subtitle) : prevSubtitle;
529
+ const apply = (t, sub) => adapter.setValue((prev) => ({
530
+ sections: prev.sections.map(
531
+ (s) => s.id !== sectionId ? s : { ...s, title: t, ...sub !== void 0 ? { subtitle: sub } : {} }
532
+ )
533
+ }));
534
+ apply(title, subtitle);
535
+ pushUndoEntry(agent.id, {
536
+ timestamp: Date.now(),
537
+ bridgeId: "artboard",
538
+ action: "artboard_rename_section",
539
+ label: `Renamed section ${sectionId}`,
540
+ undo: () => apply(prevTitle, prevSubtitle),
541
+ redo: () => apply(title, subtitle)
542
+ });
543
+ return textResult(`Renamed section ${sectionId} \u2192 "${title}"`, { id: sectionId, title, subtitle });
544
+ },
545
+ abTarget
546
+ );
547
+ reg(
548
+ "artboard_set_viewport",
549
+ "Pan / zoom the viewport.",
550
+ { x: { type: "number" }, y: { type: "number" }, zoom: { type: "number" } },
551
+ [],
552
+ (args) => {
553
+ const v = adapter.getViewport();
554
+ const next = {
555
+ x: args.x !== void 0 ? num(args.x) : v.x,
556
+ y: args.y !== void 0 ? num(args.y) : v.y,
557
+ zoom: args.zoom !== void 0 ? num(args.zoom) : v.zoom
558
+ };
559
+ adapter.setViewport(next);
560
+ return textResult(`Viewport \u2192 ${JSON.stringify(next)}`, next);
561
+ },
562
+ abTarget
563
+ );
564
+ return {
565
+ id: "artboard",
566
+ title: "Artboard",
567
+ dispose: () => {
568
+ for (const d of disposers) d();
569
+ adapter.setAgentCursor?.(null);
570
+ }
571
+ };
572
+ }
573
+
574
+ exports.registerArtboardBridge = registerArtboardBridge;
575
+ //# sourceMappingURL=bridges-artboard.cjs.map
576
+ //# sourceMappingURL=bridges-artboard.cjs.map