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