@particle-academy/agent-integrations 0.4.0 → 0.6.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 (56) hide show
  1. package/README.md +45 -0
  2. package/dist/bridges-flow.js +340 -3
  3. package/dist/bridges-flow.js.map +1 -1
  4. package/dist/{chunk-E4AICMFZ.js → chunk-5XELJIJR.js} +3 -3
  5. package/dist/chunk-5XELJIJR.js.map +1 -0
  6. package/dist/{chunk-6LTKCNLF.js → chunk-AFUULW5E.js} +3 -34
  7. package/dist/chunk-AFUULW5E.js.map +1 -0
  8. package/dist/chunk-G6N2TQVO.js +34 -0
  9. package/dist/chunk-G6N2TQVO.js.map +1 -0
  10. package/dist/chunk-IJ6JX5VC.js +3 -0
  11. package/dist/chunk-IJ6JX5VC.js.map +1 -0
  12. package/dist/{chunk-JMYPUAFH.js → chunk-LVQXIUJH.js} +2 -2
  13. package/dist/{chunk-JMYPUAFH.js.map → chunk-LVQXIUJH.js.map} +1 -1
  14. package/dist/chunk-OIX2ANFS.js +386 -0
  15. package/dist/chunk-OIX2ANFS.js.map +1 -0
  16. package/dist/chunk-ZHAK2DQR.js +289 -0
  17. package/dist/chunk-ZHAK2DQR.js.map +1 -0
  18. package/dist/components/SharedWhiteboard/index.d.cts +55 -0
  19. package/dist/components/SharedWhiteboard/index.d.ts +55 -0
  20. package/dist/components-shared-whiteboard.cjs +1533 -0
  21. package/dist/components-shared-whiteboard.cjs.map +1 -0
  22. package/dist/components-shared-whiteboard.js +285 -0
  23. package/dist/components-shared-whiteboard.js.map +1 -0
  24. package/dist/index.cjs +249 -1287
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +4 -55
  27. package/dist/index.d.ts +4 -55
  28. package/dist/index.js +9 -563
  29. package/dist/index.js.map +1 -1
  30. package/dist/mcp.js +2 -1
  31. package/dist/relay-server/index.d.cts +134 -0
  32. package/dist/relay-server/index.d.ts +134 -0
  33. package/dist/relay-server-cli.cjs +483 -0
  34. package/dist/relay-server-cli.cjs.map +1 -0
  35. package/dist/relay-server-cli.js +98 -0
  36. package/dist/relay-server-cli.js.map +1 -0
  37. package/dist/relay-server.cjs +389 -0
  38. package/dist/relay-server.cjs.map +1 -0
  39. package/dist/relay-server.js +3 -0
  40. package/dist/relay-server.js.map +1 -0
  41. package/dist/sharing/index.d.cts +2 -34
  42. package/dist/sharing/index.d.ts +2 -34
  43. package/dist/sharing.js +2 -1
  44. package/dist/sheets-adapter.cjs +1 -1
  45. package/dist/sheets-adapter.cjs.map +1 -1
  46. package/dist/sheets-adapter.d.cts +11 -7
  47. package/dist/sheets-adapter.d.ts +11 -7
  48. package/dist/sheets-adapter.js +1 -1
  49. package/dist/token-CrJF76oH.d.cts +34 -0
  50. package/dist/token-CrJF76oH.d.ts +34 -0
  51. package/docs/relay-server.md +126 -0
  52. package/package.json +66 -7
  53. package/dist/chunk-6LTKCNLF.js.map +0 -1
  54. package/dist/chunk-E4AICMFZ.js.map +0 -1
  55. package/dist/chunk-N3H4DXY5.js +0 -342
  56. package/dist/chunk-N3H4DXY5.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,16 +1,14 @@
1
1
  export { registerSceneBridge } from './chunk-XRAJSOPS.js';
2
2
  export { registerScreensBridge } from './chunk-57ZDHD53.js';
3
- export { useSheetsActivityHighlights, useSheetsAdapter } from './chunk-E4AICMFZ.js';
4
- import { buildShareUrl, buildShareConfig, createSessionDescriptor, attachSseRelay } from './chunk-JMYPUAFH.js';
5
- export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, createSessionDescriptor, describeSession, readSessionFromUrl } from './chunk-JMYPUAFH.js';
3
+ export { useSheetsActivityHighlights, useSheetsAdapter } from './chunk-5XELJIJR.js';
4
+ export { AgentActivityHighlight, AgentCursor, AgentPanel, ShareControls } from './chunk-ZHAK2DQR.js';
5
+ import './chunk-IJ6JX5VC.js';
6
+ export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, createSessionDescriptor, describeSession, readSessionFromUrl } from './chunk-LVQXIUJH.js';
6
7
  export { useAgentActivity, useAgentActivityForScreen } from './chunk-X66JWQBB.js';
7
8
  export { useUndoStack } from './chunk-RGO42EQ6.js';
8
- import { attachInProcess } from './chunk-6LTKCNLF.js';
9
- export { InProcessTransport, RelayTransport, attachInProcess, attachRelay } from './chunk-6LTKCNLF.js';
10
- import { registerWhiteboardBridge } from './chunk-3KSZNGNW.js';
11
- export { registerWhiteboardBridge } from './chunk-3KSZNGNW.js';
9
+ export { RelayTransport, attachRelay } from './chunk-G6N2TQVO.js';
10
+ export { InProcessTransport, attachInProcess } from './chunk-AFUULW5E.js';
12
11
  export { clearStack as clearUndoStack, ensureUndoToolsRegistered, pushUndoEntry, readHistory as readUndoHistory, redoOne, registerUndoTools, resetAllUndoStacks, undoOne } from './chunk-GQ7XXK7G.js';
13
- export { registerFlowBridge } from './chunk-N3H4DXY5.js';
14
12
  import { registerFormBridge } from './chunk-4BL5M3U3.js';
15
13
  export { registerFormBridge } from './chunk-4BL5M3U3.js';
16
14
  export { registerSheetsBridge } from './chunk-IANI25IT.js';
@@ -19,175 +17,10 @@ export { registerChartsBridge } from './chunk-NTDZWGYB.js';
19
17
  export { wrapToolWithActivity } from './chunk-52S7XYZK.js';
20
18
  import { onActivity } from './chunk-JU2N4KK6.js';
21
19
  export { emitActivity, onActivity, readActivityHistory, resetActivityRegistry } from './chunk-JU2N4KK6.js';
22
- import { MicroMcpServer } from './chunk-4KAIV6OD.js';
23
20
  export { MCP_PROTOCOL_VERSION, MicroMcpServer, ToolRegistry, errorResult, rpcError, textResult } from './chunk-4KAIV6OD.js';
24
- import { useRef, useEffect, useState, useMemo, useCallback } from 'react';
25
- import { jsxs, jsx } from 'react/jsx-runtime';
26
- import { Board, Connector, Shape, StickyNote, CursorLayer } from '@particle-academy/fancy-whiteboard';
21
+ import { useRef, useEffect, useMemo } from 'react';
22
+ import { jsx } from 'react/jsx-runtime';
27
23
 
28
- function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
29
- const scrollRef = useRef(null);
30
- const inputRef = useRef(null);
31
- useEffect(() => {
32
- const el = scrollRef.current;
33
- if (!el) return;
34
- el.scrollTop = el.scrollHeight;
35
- }, [activity.length]);
36
- const handleSubmit = (e) => {
37
- e.preventDefault();
38
- const value = inputRef.current?.value.trim();
39
- if (!value || !onSubmit) return;
40
- onSubmit(value);
41
- if (inputRef.current) inputRef.current.value = "";
42
- };
43
- const color = agent?.color ?? "#a855f7";
44
- const name = agent?.name ?? "Agent";
45
- return /* @__PURE__ */ jsxs("div", { className: ["fai-panel", className ?? ""].filter(Boolean).join(" "), style, children: [
46
- /* @__PURE__ */ jsxs("header", { className: "fai-panel__header", children: [
47
- /* @__PURE__ */ jsx(
48
- "div",
49
- {
50
- className: "fai-panel__avatar",
51
- style: { background: color },
52
- "aria-hidden": true,
53
- children: name.slice(0, 1)
54
- }
55
- ),
56
- /* @__PURE__ */ jsxs("div", { className: "fai-panel__title", children: [
57
- /* @__PURE__ */ jsx("strong", { children: name }),
58
- /* @__PURE__ */ jsx("span", { className: "fai-panel__subtitle", children: busy ? "Working\u2026" : `${activity.length} event${activity.length === 1 ? "" : "s"}` })
59
- ] }),
60
- actions && /* @__PURE__ */ jsx("div", { className: "fai-panel__actions", children: actions })
61
- ] }),
62
- /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "fai-panel__stream", children: activity.length === 0 ? /* @__PURE__ */ jsx("p", { className: "fai-panel__empty", children: "No activity yet." }) : activity.map((a) => /* @__PURE__ */ jsx(ActivityRow, { item: a }, a.id)) }),
63
- onSubmit && /* @__PURE__ */ jsxs("form", { className: "fai-panel__composer", onSubmit: handleSubmit, children: [
64
- /* @__PURE__ */ jsx(
65
- "textarea",
66
- {
67
- ref: inputRef,
68
- className: "fai-panel__input",
69
- placeholder: busy ? "Working\u2026" : "Ask the agent\u2026",
70
- disabled: busy,
71
- rows: 2,
72
- onKeyDown: (e) => {
73
- if (e.key === "Enter" && !e.shiftKey) {
74
- e.preventDefault();
75
- handleSubmit(e);
76
- }
77
- }
78
- }
79
- ),
80
- /* @__PURE__ */ jsx("button", { type: "submit", className: "fai-panel__send", disabled: busy, children: "Send" })
81
- ] })
82
- ] });
83
- }
84
- function ActivityRow({ item }) {
85
- const time = formatTime(item.at);
86
- return /* @__PURE__ */ jsxs("div", { className: `fai-row fai-row--${item.kind}`, children: [
87
- /* @__PURE__ */ jsxs("div", { className: "fai-row__meta", children: [
88
- /* @__PURE__ */ jsx("span", { className: "fai-row__source", children: item.source }),
89
- /* @__PURE__ */ jsx("span", { className: "fai-row__time", children: time })
90
- ] }),
91
- /* @__PURE__ */ jsx("div", { className: "fai-row__text", children: item.text }),
92
- item.detail !== void 0 && /* @__PURE__ */ jsxs("details", { className: "fai-row__detail", children: [
93
- /* @__PURE__ */ jsx("summary", { children: "details" }),
94
- /* @__PURE__ */ jsx("pre", { children: safeJson(item.detail) })
95
- ] })
96
- ] });
97
- }
98
- function formatTime(at) {
99
- const d = new Date(at);
100
- const hh = d.getHours().toString().padStart(2, "0");
101
- const mm = d.getMinutes().toString().padStart(2, "0");
102
- const ss = d.getSeconds().toString().padStart(2, "0");
103
- return `${hh}:${mm}:${ss}`;
104
- }
105
- function safeJson(v) {
106
- try {
107
- return JSON.stringify(v, null, 2);
108
- } catch {
109
- return String(v);
110
- }
111
- }
112
- function AgentCursor({ x, y, name, color = "#a855f7", status, className, style }) {
113
- return /* @__PURE__ */ jsxs(
114
- "div",
115
- {
116
- className: ["fai-cursor", className ?? ""].filter(Boolean).join(" "),
117
- style: {
118
- position: "absolute",
119
- left: x,
120
- top: y,
121
- pointerEvents: "none",
122
- transform: "translate(-2px, -2px)",
123
- ...style
124
- },
125
- children: [
126
- /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", "aria-hidden": true, children: /* @__PURE__ */ jsx(
127
- "path",
128
- {
129
- d: "M2 2 L2 17 L7 13 L10 19 L12 18 L9 12 L15 12 Z",
130
- fill: color,
131
- stroke: "white",
132
- strokeWidth: "1.2"
133
- }
134
- ) }),
135
- name && /* @__PURE__ */ jsxs(
136
- "span",
137
- {
138
- className: "fai-cursor__tag",
139
- style: { background: color },
140
- children: [
141
- name,
142
- status ? /* @__PURE__ */ jsxs("em", { className: "fai-cursor__status", children: [
143
- " \xB7 ",
144
- status
145
- ] }) : null
146
- ]
147
- }
148
- )
149
- ]
150
- }
151
- );
152
- }
153
- function AgentActivityHighlight({
154
- x,
155
- y,
156
- width,
157
- height,
158
- pulseKey,
159
- color = "#a855f7",
160
- duration = 1200,
161
- className,
162
- style
163
- }) {
164
- const [visible, setVisible] = useState(false);
165
- useEffect(() => {
166
- if (pulseKey === void 0) return;
167
- setVisible(true);
168
- const t = setTimeout(() => setVisible(false), duration);
169
- return () => clearTimeout(t);
170
- }, [pulseKey, duration]);
171
- if (!visible) return null;
172
- return /* @__PURE__ */ jsx(
173
- "div",
174
- {
175
- className: ["fai-highlight", className ?? ""].filter(Boolean).join(" "),
176
- style: {
177
- position: "absolute",
178
- left: x - 4,
179
- top: y - 4,
180
- width: width + 8,
181
- height: height + 8,
182
- borderRadius: 8,
183
- boxShadow: `0 0 0 2px ${color}, 0 0 16px ${color}66`,
184
- pointerEvents: "none",
185
- animation: `fai-pulse ${duration}ms ease-out forwards`,
186
- ...style
187
- }
188
- }
189
- );
190
- }
191
24
  function BridgedForm({
192
25
  id,
193
26
  title,
@@ -280,394 +113,7 @@ function ScreensActivityBridge({ system, fadeMs = 1500 }) {
280
113
  }, [system, fadeMs]);
281
114
  return null;
282
115
  }
283
- function ShareControls({
284
- session,
285
- onStart,
286
- onStop,
287
- status,
288
- shareBaseUrl,
289
- className,
290
- style
291
- }) {
292
- const [tab, setTab] = useState("url");
293
- if (!session) {
294
- return /* @__PURE__ */ jsxs("div", { className: ["fai-share fai-share--idle", className ?? ""].filter(Boolean).join(" "), style, children: [
295
- /* @__PURE__ */ jsx("button", { type: "button", className: "fai-share__start", onClick: onStart, children: "Start shared session" }),
296
- /* @__PURE__ */ jsx("p", { className: "fai-share__hint", children: "Generates a session id + secret token. Share the URL with humans, or hand the JSON config to an MCP-capable agent." })
297
- ] });
298
- }
299
- const url = buildShareUrl(session, shareBaseUrl);
300
- const config = buildShareConfig(session);
301
- const curl = buildCurlRecipe(session);
302
- return /* @__PURE__ */ jsxs("div", { className: ["fai-share fai-share--active", className ?? ""].filter(Boolean).join(" "), style, children: [
303
- /* @__PURE__ */ jsxs("div", { className: "fai-share__header", children: [
304
- /* @__PURE__ */ jsxs("div", { children: [
305
- /* @__PURE__ */ jsx("strong", { children: "Sharing" }),
306
- /* @__PURE__ */ jsxs("span", { className: "fai-share__id", children: [
307
- "session ",
308
- /* @__PURE__ */ jsx("code", { children: session.id }),
309
- " \xB7 token ",
310
- /* @__PURE__ */ jsxs("code", { children: [
311
- session.display,
312
- "\u2026"
313
- ] })
314
- ] })
315
- ] }),
316
- /* @__PURE__ */ jsxs("div", { className: "fai-share__header-actions", children: [
317
- status && /* @__PURE__ */ jsx("span", { className: "fai-share__status", children: status }),
318
- /* @__PURE__ */ jsx("button", { type: "button", className: "fai-share__stop", onClick: onStop, children: "Stop" })
319
- ] })
320
- ] }),
321
- /* @__PURE__ */ jsxs("div", { className: "fai-share__tabs", role: "tablist", children: [
322
- /* @__PURE__ */ jsx(TabButton, { tab: "url", active: tab, setTab, children: "URL" }),
323
- /* @__PURE__ */ jsx(TabButton, { tab: "json", active: tab, setTab, children: "JSON" }),
324
- /* @__PURE__ */ jsx(TabButton, { tab: "curl", active: tab, setTab, children: "cURL recipe" })
325
- ] }),
326
- /* @__PURE__ */ jsxs("div", { className: "fai-share__panel", children: [
327
- tab === "url" && /* @__PURE__ */ jsx(CopyBox, { label: "Open this URL in another tab to join the session", value: url }),
328
- tab === "json" && /* @__PURE__ */ jsx(
329
- CopyBox,
330
- {
331
- label: "Paste into Claude Desktop / Cline MCP server config",
332
- value: JSON.stringify(config, null, 2)
333
- }
334
- ),
335
- tab === "curl" && /* @__PURE__ */ jsx(
336
- CopyBox,
337
- {
338
- label: "Connect from a terminal (verifies the relay is reachable)",
339
- value: curl,
340
- multiline: true
341
- }
342
- )
343
- ] })
344
- ] });
345
- }
346
- function TabButton({ tab, active, setTab, children }) {
347
- return /* @__PURE__ */ jsx(
348
- "button",
349
- {
350
- type: "button",
351
- role: "tab",
352
- "aria-selected": tab === active,
353
- className: `fai-share__tab${tab === active ? " is-active" : ""}`,
354
- onClick: () => setTab(tab),
355
- children
356
- }
357
- );
358
- }
359
- function CopyBox({ label, value, multiline }) {
360
- const [copied, setCopied] = useState(false);
361
- const copy = async () => {
362
- try {
363
- await navigator.clipboard.writeText(value);
364
- setCopied(true);
365
- setTimeout(() => setCopied(false), 1200);
366
- } catch {
367
- }
368
- };
369
- return /* @__PURE__ */ jsxs("div", { children: [
370
- /* @__PURE__ */ jsx("div", { className: "fai-share__panel-label", children: label }),
371
- /* @__PURE__ */ jsxs("div", { className: "fai-share__copy", children: [
372
- /* @__PURE__ */ jsx("pre", { className: `fai-share__pre${multiline ? " is-multi" : ""}`, children: value }),
373
- /* @__PURE__ */ jsx("button", { type: "button", className: "fai-share__copy-btn", onClick: copy, children: copied ? "Copied" : "Copy" })
374
- ] })
375
- ] });
376
- }
377
- function buildCurlRecipe(session) {
378
- const base = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.host}` : "http://localhost";
379
- const inbox = `${base}/whiteboard-share/${session.id}/inbox?token=${session.token}`;
380
- const events = `${base}/whiteboard-share/${session.id}/events?token=${session.token}`;
381
- return [
382
- `# 1) In one terminal, subscribe to server-pushed frames (SSE)`,
383
- `curl -N "${events}"`,
384
- ``,
385
- `# 2) In another terminal, send an initialize handshake`,
386
- `curl -X POST "${inbox}" \\`,
387
- ` -H 'content-type: application/json' \\`,
388
- ` -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'`,
389
- ``,
390
- `# 3) List the tools the bridge exposes`,
391
- `curl -X POST "${inbox}" \\`,
392
- ` -H 'content-type: application/json' \\`,
393
- ` -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'`,
394
- ``,
395
- `# 4) Add a sticky note`,
396
- `curl -X POST "${inbox}" \\`,
397
- ` -H 'content-type: application/json' \\`,
398
- ` -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"whiteboard_add_sticky","arguments":{"x":300,"y":300,"text":"hello from curl"}}}'`
399
- ].join("\n");
400
- }
401
- var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
402
- function SharedWhiteboard({
403
- initialNotes = [],
404
- initialShapes = [],
405
- initialConnectors = [],
406
- initialStrokes = [],
407
- initialViewport = { x: 0, y: 0, zoom: 1 },
408
- agent = DEFAULT_AGENT,
409
- shareBaseUrl = "/whiteboard-share",
410
- onRegisterSession,
411
- showAgentPanel = true,
412
- showShareControls = true,
413
- broadcastEdits = true,
414
- height = 640,
415
- header,
416
- className,
417
- style
418
- }) {
419
- const [notes, setNotes] = useState(initialNotes);
420
- const [shapes, setShapes] = useState(initialShapes);
421
- const [connectors, setConnectors] = useState(initialConnectors);
422
- const [strokes, setStrokes] = useState(initialStrokes);
423
- const [viewport, setViewport] = useState(initialViewport);
424
- const [agentCursor, setAgentCursor] = useState(null);
425
- const [activity, setActivity] = useState([]);
426
- const [highlight, setHighlight] = useState(null);
427
- const stateRefs = useRef({ notes, shapes, connectors, strokes, viewport });
428
- useEffect(() => {
429
- stateRefs.current = { notes, shapes, connectors, strokes, viewport };
430
- }, [notes, shapes, connectors, strokes, viewport]);
431
- const serverRef = useRef(null);
432
- const inProcRef = useRef(null);
433
- const bridgeRef = useRef(null);
434
- useEffect(() => {
435
- const server = new MicroMcpServer({
436
- info: { name: "shared-whiteboard", version: "0.2.0" },
437
- instructions: "Collaborative whiteboard. Use whiteboard_* tools to read or modify the board."
438
- });
439
- bridgeRef.current = registerWhiteboardBridge(server, {
440
- adapter: {
441
- getNotes: () => stateRefs.current.notes,
442
- setNotes: (next) => setNotes(typeof next === "function" ? next : () => next),
443
- getShapes: () => stateRefs.current.shapes,
444
- setShapes: (next) => setShapes(typeof next === "function" ? next : () => next),
445
- getConnectors: () => stateRefs.current.connectors,
446
- setConnectors: (next) => setConnectors(typeof next === "function" ? next : () => next),
447
- getStrokes: () => stateRefs.current.strokes,
448
- setStrokes: (next) => setStrokes(typeof next === "function" ? next : () => next),
449
- getViewport: () => stateRefs.current.viewport,
450
- setViewport,
451
- setAgentCursor
452
- },
453
- agent
454
- });
455
- inProcRef.current = attachInProcess(server);
456
- serverRef.current = server;
457
- const off = inProcRef.current.onServerMessage((msg) => {
458
- if (msg?.id !== void 0 && "result" in msg && msg.result?.structuredContent?.id) {
459
- const id = msg.result.structuredContent.id;
460
- requestAnimationFrame(() => pulseFor(id));
461
- }
462
- });
463
- return () => {
464
- off();
465
- bridgeRef.current?.dispose();
466
- bridgeRef.current = null;
467
- if (inProcRef.current) server.detach(inProcRef.current);
468
- };
469
- }, []);
470
- const pulseFor = (id) => {
471
- const n = stateRefs.current.notes.find((x) => x.id === id);
472
- if (n) return setHighlight({ pulseKey: Date.now(), bounds: { x: n.x, y: n.y, width: n.width, height: n.height } });
473
- const s = stateRefs.current.shapes.find((x) => x.id === id);
474
- if (s) return setHighlight({ pulseKey: Date.now(), bounds: { x: s.x, y: s.y, width: s.width, height: s.height } });
475
- };
476
- const log = useCallback((entry) => {
477
- setActivity((all) => [...all.slice(-200), { id: `a_${Date.now()}_${all.length}`, at: Date.now(), ...entry }]);
478
- }, []);
479
- const [session, setSession] = useState(null);
480
- const [relayState, setRelayState] = useState("idle");
481
- const sseRef = useRef(null);
482
- const logEsRef = useRef(null);
483
- const startShare = async () => {
484
- if (session || !serverRef.current || !shareBaseUrl) return;
485
- const desc = createSessionDescriptor();
486
- try {
487
- if (onRegisterSession) {
488
- await onRegisterSession(desc);
489
- } else {
490
- const csrf = document.querySelector('meta[name="csrf-token"]')?.content ?? "";
491
- const reg = await fetch(`${shareBaseUrl}/register`, {
492
- method: "POST",
493
- headers: { "content-type": "application/json", "x-csrf-token": csrf, accept: "application/json" },
494
- body: JSON.stringify({ session: desc.id, token: desc.token })
495
- });
496
- if (!reg.ok) throw new Error(`registration failed (HTTP ${reg.status})`);
497
- }
498
- } catch (e) {
499
- log({ kind: "error", source: "share", text: e instanceof Error ? e.message : String(e) });
500
- return;
501
- }
502
- const relay = attachSseRelay(serverRef.current, {
503
- baseUrl: shareBaseUrl,
504
- sessionId: desc.id,
505
- token: desc.token
506
- });
507
- sseRef.current = relay;
508
- relay.onStateChange(setRelayState);
509
- const es = new EventSource(`${shareBaseUrl}/${desc.id}/events?token=${desc.token}&direction=inbound`);
510
- es.addEventListener("mcp", (ev) => {
511
- try {
512
- const frame = JSON.parse(ev.data);
513
- if (frame.method === "notifications/peer_joined") {
514
- setAgentCursor((c) => c ?? { userId: agent.id, name: agent.name, color: agent.color, x: 60, y: 60 });
515
- log({ kind: "info", source: "presence", text: `${agent.name ?? "Agent"} connected` });
516
- return;
517
- }
518
- if (frame.method === "notifications/peer_left") {
519
- setAgentCursor(null);
520
- log({ kind: "info", source: "presence", text: `${agent.name ?? "Agent"} disconnected` });
521
- return;
522
- }
523
- if (frame.method === "notifications/agent_message") {
524
- log({ kind: "message", source: agent.name ?? "Agent", text: String(frame.params?.text ?? "") });
525
- } else if (frame.method === "notifications/agent_status") {
526
- log({ kind: "info", source: agent.name ?? "Agent", text: String(frame.params?.text ?? "") });
527
- } else if (frame.method?.startsWith("notifications/")) {
528
- } else {
529
- log({ kind: "tool", source: "remote", text: `\u2190 ${frame.method ?? `id:${frame.id}`}`, detail: frame });
530
- }
531
- } catch {
532
- }
533
- });
534
- logEsRef.current = es;
535
- setSession(desc);
536
- log({ kind: "info", source: "share", text: `Sharing started \xB7 session ${desc.id}` });
537
- };
538
- const stopShare = async () => {
539
- if (!session) return;
540
- const desc = session;
541
- setSession(null);
542
- logEsRef.current?.close();
543
- logEsRef.current = null;
544
- if (sseRef.current && serverRef.current) serverRef.current.detach(sseRef.current);
545
- sseRef.current = null;
546
- setRelayState("closed");
547
- if (shareBaseUrl) {
548
- const csrf = document.querySelector('meta[name="csrf-token"]')?.content ?? "";
549
- await fetch(`${shareBaseUrl}/${desc.id}/unregister?token=${encodeURIComponent(desc.token)}`, {
550
- method: "POST",
551
- headers: { "x-csrf-token": csrf, accept: "application/json" }
552
- }).catch(() => {
553
- });
554
- }
555
- log({ kind: "info", source: "share", text: "Sharing stopped." });
556
- };
557
- const lastBroadcastRef = useRef(0);
558
- useEffect(() => {
559
- if (!broadcastEdits || !sseRef.current || !session) return;
560
- const now = Date.now();
561
- if (now - lastBroadcastRef.current < 80) return;
562
- lastBroadcastRef.current = now;
563
- sseRef.current.send({
564
- jsonrpc: "2.0",
565
- method: "notifications/state_update",
566
- params: { notes, shapes, connectors, viewport, ts: now }
567
- });
568
- }, [notes, shapes, connectors, viewport, session, broadcastEdits]);
569
- const handleSubmit = (text) => {
570
- if (!sseRef.current) {
571
- log({ kind: "error", source: "you", text: "Start a shared session first." });
572
- return;
573
- }
574
- sseRef.current.send({
575
- jsonrpc: "2.0",
576
- method: "notifications/user_message",
577
- params: { text, ts: Date.now() }
578
- });
579
- log({ kind: "message", source: "You", text });
580
- };
581
- const cursors = useMemo(() => [], []);
582
- const statusText = (() => {
583
- switch (relayState) {
584
- case "open":
585
- return "live";
586
- case "connecting":
587
- return "connecting\u2026";
588
- case "error":
589
- return "error";
590
- case "closed":
591
- return "closed";
592
- default:
593
- return void 0;
594
- }
595
- })();
596
- return /* @__PURE__ */ jsxs("div", { className: ["fai-shared-whiteboard", className ?? ""].filter(Boolean).join(" "), style, children: [
597
- header,
598
- showShareControls && shareBaseUrl !== null && /* @__PURE__ */ jsx("div", { className: "fai-shared-whiteboard__controls", children: /* @__PURE__ */ jsx(ShareControls, { session, onStart: startShare, onStop: stopShare, status: statusText }) }),
599
- /* @__PURE__ */ jsxs(
600
- "div",
601
- {
602
- className: "fai-shared-whiteboard__layout",
603
- style: {
604
- display: "grid",
605
- gap: 16,
606
- gridTemplateColumns: showAgentPanel ? "1fr 360px" : "1fr"
607
- },
608
- children: [
609
- /* @__PURE__ */ jsx(
610
- "div",
611
- {
612
- className: "fai-shared-whiteboard__board",
613
- style: {
614
- position: "relative",
615
- overflow: "hidden",
616
- borderRadius: 12,
617
- border: "1px solid #e4e4e7",
618
- background: "radial-gradient(circle at 1px 1px, rgba(0,0,0,0.07) 1px, transparent 0)",
619
- backgroundSize: "20px 20px",
620
- height
621
- },
622
- children: /* @__PURE__ */ jsxs(Board, { viewport, onViewportChange: setViewport, style: { width: "100%", height: "100%" }, children: [
623
- connectors.map((c) => {
624
- const a = resolveCenter(c.from, notes, shapes);
625
- const b = resolveCenter(c.to, notes, shapes);
626
- if (!a || !b) return null;
627
- return /* @__PURE__ */ jsx(Connector, { from: a, to: b, color: c.color ?? "#64748b" }, c.id);
628
- }),
629
- shapes.map((s) => /* @__PURE__ */ jsx(Shape, { item: s, onChange: (next) => setShapes((all) => all.map((x) => x.id === next.id ? next : x)) }, s.id)),
630
- notes.map((n) => /* @__PURE__ */ jsx(StickyNote, { item: n, onChange: (next) => setNotes((all) => all.map((x) => x.id === next.id ? next : x)) }, n.id)),
631
- /* @__PURE__ */ jsx(CursorLayer, { cursors }),
632
- agentCursor && /* @__PURE__ */ jsx(AgentCursor, { x: agentCursor.x, y: agentCursor.y, name: agentCursor.name, color: agentCursor.color }),
633
- highlight && /* @__PURE__ */ jsx(
634
- AgentActivityHighlight,
635
- {
636
- x: highlight.bounds.x,
637
- y: highlight.bounds.y,
638
- width: highlight.bounds.width,
639
- height: highlight.bounds.height,
640
- color: agent.color ?? "#a855f7",
641
- pulseKey: highlight.pulseKey
642
- }
643
- )
644
- ] })
645
- }
646
- ),
647
- showAgentPanel && /* @__PURE__ */ jsx("div", { style: { height }, children: /* @__PURE__ */ jsx(
648
- AgentPanel,
649
- {
650
- agent,
651
- activity,
652
- onSubmit: handleSubmit
653
- }
654
- ) })
655
- ]
656
- }
657
- )
658
- ] });
659
- }
660
- function resolveCenter(ref, notes, shapes) {
661
- if (typeof ref === "string") {
662
- const n = notes.find((x) => x.id === ref);
663
- if (n) return { x: n.x + n.width / 2, y: n.y + n.height / 2 };
664
- const s = shapes.find((x) => x.id === ref);
665
- if (s) return { x: s.x + s.width / 2, y: s.y + s.height / 2 };
666
- return null;
667
- }
668
- return ref;
669
- }
670
116
 
671
- export { AgentActivityHighlight, AgentCursor, AgentPanel, BridgedForm, ScreensActivityBridge, ShareControls, SharedWhiteboard };
117
+ export { BridgedForm, ScreensActivityBridge };
672
118
  //# sourceMappingURL=index.js.map
673
119
  //# sourceMappingURL=index.js.map