@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
@@ -0,0 +1,289 @@
1
+ import { buildShareUrl, buildShareConfig } from './chunk-LVQXIUJH.js';
2
+ import { useRef, useEffect, useState } from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
6
+ const scrollRef = useRef(null);
7
+ const inputRef = useRef(null);
8
+ useEffect(() => {
9
+ const el = scrollRef.current;
10
+ if (!el) return;
11
+ el.scrollTop = el.scrollHeight;
12
+ }, [activity.length]);
13
+ const handleSubmit = (e) => {
14
+ e.preventDefault();
15
+ const value = inputRef.current?.value.trim();
16
+ if (!value || !onSubmit) return;
17
+ onSubmit(value);
18
+ if (inputRef.current) inputRef.current.value = "";
19
+ };
20
+ const color = agent?.color ?? "#a855f7";
21
+ const name = agent?.name ?? "Agent";
22
+ return /* @__PURE__ */ jsxs("div", { className: ["fai-panel", className ?? ""].filter(Boolean).join(" "), style, children: [
23
+ /* @__PURE__ */ jsxs("header", { className: "fai-panel__header", children: [
24
+ /* @__PURE__ */ jsx(
25
+ "div",
26
+ {
27
+ className: "fai-panel__avatar",
28
+ style: { background: color },
29
+ "aria-hidden": true,
30
+ children: name.slice(0, 1)
31
+ }
32
+ ),
33
+ /* @__PURE__ */ jsxs("div", { className: "fai-panel__title", children: [
34
+ /* @__PURE__ */ jsx("strong", { children: name }),
35
+ /* @__PURE__ */ jsx("span", { className: "fai-panel__subtitle", children: busy ? "Working\u2026" : `${activity.length} event${activity.length === 1 ? "" : "s"}` })
36
+ ] }),
37
+ actions && /* @__PURE__ */ jsx("div", { className: "fai-panel__actions", children: actions })
38
+ ] }),
39
+ /* @__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)) }),
40
+ onSubmit && /* @__PURE__ */ jsxs("form", { className: "fai-panel__composer", onSubmit: handleSubmit, children: [
41
+ /* @__PURE__ */ jsx(
42
+ "textarea",
43
+ {
44
+ ref: inputRef,
45
+ className: "fai-panel__input",
46
+ placeholder: busy ? "Working\u2026" : "Ask the agent\u2026",
47
+ disabled: busy,
48
+ rows: 2,
49
+ onKeyDown: (e) => {
50
+ if (e.key === "Enter" && !e.shiftKey) {
51
+ e.preventDefault();
52
+ handleSubmit(e);
53
+ }
54
+ }
55
+ }
56
+ ),
57
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "fai-panel__send", disabled: busy, children: "Send" })
58
+ ] })
59
+ ] });
60
+ }
61
+ function ActivityRow({ item }) {
62
+ const time = formatTime(item.at);
63
+ return /* @__PURE__ */ jsxs("div", { className: `fai-row fai-row--${item.kind}`, children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "fai-row__meta", children: [
65
+ /* @__PURE__ */ jsx("span", { className: "fai-row__source", children: item.source }),
66
+ /* @__PURE__ */ jsx("span", { className: "fai-row__time", children: time })
67
+ ] }),
68
+ /* @__PURE__ */ jsx("div", { className: "fai-row__text", children: item.text }),
69
+ item.detail !== void 0 && /* @__PURE__ */ jsxs("details", { className: "fai-row__detail", children: [
70
+ /* @__PURE__ */ jsx("summary", { children: "details" }),
71
+ /* @__PURE__ */ jsx("pre", { children: safeJson(item.detail) })
72
+ ] })
73
+ ] });
74
+ }
75
+ function formatTime(at) {
76
+ const d = new Date(at);
77
+ const hh = d.getHours().toString().padStart(2, "0");
78
+ const mm = d.getMinutes().toString().padStart(2, "0");
79
+ const ss = d.getSeconds().toString().padStart(2, "0");
80
+ return `${hh}:${mm}:${ss}`;
81
+ }
82
+ function safeJson(v) {
83
+ try {
84
+ return JSON.stringify(v, null, 2);
85
+ } catch {
86
+ return String(v);
87
+ }
88
+ }
89
+ function AgentCursor({ x, y, name, color = "#a855f7", status, className, style }) {
90
+ return /* @__PURE__ */ jsxs(
91
+ "div",
92
+ {
93
+ className: ["fai-cursor", className ?? ""].filter(Boolean).join(" "),
94
+ style: {
95
+ position: "absolute",
96
+ left: x,
97
+ top: y,
98
+ pointerEvents: "none",
99
+ transform: "translate(-2px, -2px)",
100
+ ...style
101
+ },
102
+ children: [
103
+ /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", "aria-hidden": true, children: /* @__PURE__ */ jsx(
104
+ "path",
105
+ {
106
+ d: "M2 2 L2 17 L7 13 L10 19 L12 18 L9 12 L15 12 Z",
107
+ fill: color,
108
+ stroke: "white",
109
+ strokeWidth: "1.2"
110
+ }
111
+ ) }),
112
+ name && /* @__PURE__ */ jsxs(
113
+ "span",
114
+ {
115
+ className: "fai-cursor__tag",
116
+ style: { background: color },
117
+ children: [
118
+ name,
119
+ status ? /* @__PURE__ */ jsxs("em", { className: "fai-cursor__status", children: [
120
+ " \xB7 ",
121
+ status
122
+ ] }) : null
123
+ ]
124
+ }
125
+ )
126
+ ]
127
+ }
128
+ );
129
+ }
130
+ function AgentActivityHighlight({
131
+ x,
132
+ y,
133
+ width,
134
+ height,
135
+ pulseKey,
136
+ color = "#a855f7",
137
+ duration = 1200,
138
+ className,
139
+ style
140
+ }) {
141
+ const [visible, setVisible] = useState(false);
142
+ useEffect(() => {
143
+ if (pulseKey === void 0) return;
144
+ setVisible(true);
145
+ const t = setTimeout(() => setVisible(false), duration);
146
+ return () => clearTimeout(t);
147
+ }, [pulseKey, duration]);
148
+ if (!visible) return null;
149
+ return /* @__PURE__ */ jsx(
150
+ "div",
151
+ {
152
+ className: ["fai-highlight", className ?? ""].filter(Boolean).join(" "),
153
+ style: {
154
+ position: "absolute",
155
+ left: x - 4,
156
+ top: y - 4,
157
+ width: width + 8,
158
+ height: height + 8,
159
+ borderRadius: 8,
160
+ boxShadow: `0 0 0 2px ${color}, 0 0 16px ${color}66`,
161
+ pointerEvents: "none",
162
+ animation: `fai-pulse ${duration}ms ease-out forwards`,
163
+ ...style
164
+ }
165
+ }
166
+ );
167
+ }
168
+ function ShareControls({
169
+ session,
170
+ onStart,
171
+ onStop,
172
+ status,
173
+ shareBaseUrl,
174
+ className,
175
+ style
176
+ }) {
177
+ const [tab, setTab] = useState("url");
178
+ if (!session) {
179
+ return /* @__PURE__ */ jsxs("div", { className: ["fai-share fai-share--idle", className ?? ""].filter(Boolean).join(" "), style, children: [
180
+ /* @__PURE__ */ jsx("button", { type: "button", className: "fai-share__start", onClick: onStart, children: "Start shared session" }),
181
+ /* @__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." })
182
+ ] });
183
+ }
184
+ const url = buildShareUrl(session, shareBaseUrl);
185
+ const config = buildShareConfig(session);
186
+ const curl = buildCurlRecipe(session);
187
+ return /* @__PURE__ */ jsxs("div", { className: ["fai-share fai-share--active", className ?? ""].filter(Boolean).join(" "), style, children: [
188
+ /* @__PURE__ */ jsxs("div", { className: "fai-share__header", children: [
189
+ /* @__PURE__ */ jsxs("div", { children: [
190
+ /* @__PURE__ */ jsx("strong", { children: "Sharing" }),
191
+ /* @__PURE__ */ jsxs("span", { className: "fai-share__id", children: [
192
+ "session ",
193
+ /* @__PURE__ */ jsx("code", { children: session.id }),
194
+ " \xB7 token ",
195
+ /* @__PURE__ */ jsxs("code", { children: [
196
+ session.display,
197
+ "\u2026"
198
+ ] })
199
+ ] })
200
+ ] }),
201
+ /* @__PURE__ */ jsxs("div", { className: "fai-share__header-actions", children: [
202
+ status && /* @__PURE__ */ jsx("span", { className: "fai-share__status", children: status }),
203
+ /* @__PURE__ */ jsx("button", { type: "button", className: "fai-share__stop", onClick: onStop, children: "Stop" })
204
+ ] })
205
+ ] }),
206
+ /* @__PURE__ */ jsxs("div", { className: "fai-share__tabs", role: "tablist", children: [
207
+ /* @__PURE__ */ jsx(TabButton, { tab: "url", active: tab, setTab, children: "URL" }),
208
+ /* @__PURE__ */ jsx(TabButton, { tab: "json", active: tab, setTab, children: "JSON" }),
209
+ /* @__PURE__ */ jsx(TabButton, { tab: "curl", active: tab, setTab, children: "cURL recipe" })
210
+ ] }),
211
+ /* @__PURE__ */ jsxs("div", { className: "fai-share__panel", children: [
212
+ tab === "url" && /* @__PURE__ */ jsx(CopyBox, { label: "Open this URL in another tab to join the session", value: url }),
213
+ tab === "json" && /* @__PURE__ */ jsx(
214
+ CopyBox,
215
+ {
216
+ label: "Paste into Claude Desktop / Cline MCP server config",
217
+ value: JSON.stringify(config, null, 2)
218
+ }
219
+ ),
220
+ tab === "curl" && /* @__PURE__ */ jsx(
221
+ CopyBox,
222
+ {
223
+ label: "Connect from a terminal (verifies the relay is reachable)",
224
+ value: curl,
225
+ multiline: true
226
+ }
227
+ )
228
+ ] })
229
+ ] });
230
+ }
231
+ function TabButton({ tab, active, setTab, children }) {
232
+ return /* @__PURE__ */ jsx(
233
+ "button",
234
+ {
235
+ type: "button",
236
+ role: "tab",
237
+ "aria-selected": tab === active,
238
+ className: `fai-share__tab${tab === active ? " is-active" : ""}`,
239
+ onClick: () => setTab(tab),
240
+ children
241
+ }
242
+ );
243
+ }
244
+ function CopyBox({ label, value, multiline }) {
245
+ const [copied, setCopied] = useState(false);
246
+ const copy = async () => {
247
+ try {
248
+ await navigator.clipboard.writeText(value);
249
+ setCopied(true);
250
+ setTimeout(() => setCopied(false), 1200);
251
+ } catch {
252
+ }
253
+ };
254
+ return /* @__PURE__ */ jsxs("div", { children: [
255
+ /* @__PURE__ */ jsx("div", { className: "fai-share__panel-label", children: label }),
256
+ /* @__PURE__ */ jsxs("div", { className: "fai-share__copy", children: [
257
+ /* @__PURE__ */ jsx("pre", { className: `fai-share__pre${multiline ? " is-multi" : ""}`, children: value }),
258
+ /* @__PURE__ */ jsx("button", { type: "button", className: "fai-share__copy-btn", onClick: copy, children: copied ? "Copied" : "Copy" })
259
+ ] })
260
+ ] });
261
+ }
262
+ function buildCurlRecipe(session) {
263
+ const base = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.host}` : "http://localhost";
264
+ const inbox = `${base}/whiteboard-share/${session.id}/inbox?token=${session.token}`;
265
+ const events = `${base}/whiteboard-share/${session.id}/events?token=${session.token}`;
266
+ return [
267
+ `# 1) In one terminal, subscribe to server-pushed frames (SSE)`,
268
+ `curl -N "${events}"`,
269
+ ``,
270
+ `# 2) In another terminal, send an initialize handshake`,
271
+ `curl -X POST "${inbox}" \\`,
272
+ ` -H 'content-type: application/json' \\`,
273
+ ` -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'`,
274
+ ``,
275
+ `# 3) List the tools the bridge exposes`,
276
+ `curl -X POST "${inbox}" \\`,
277
+ ` -H 'content-type: application/json' \\`,
278
+ ` -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'`,
279
+ ``,
280
+ `# 4) Add a sticky note`,
281
+ `curl -X POST "${inbox}" \\`,
282
+ ` -H 'content-type: application/json' \\`,
283
+ ` -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"whiteboard_add_sticky","arguments":{"x":300,"y":300,"text":"hello from curl"}}}'`
284
+ ].join("\n");
285
+ }
286
+
287
+ export { AgentActivityHighlight, AgentCursor, AgentPanel, ShareControls };
288
+ //# sourceMappingURL=chunk-ZHAK2DQR.js.map
289
+ //# sourceMappingURL=chunk-ZHAK2DQR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/AgentPanel/AgentPanel.tsx","../src/components/AgentCursor/AgentCursor.tsx","../src/components/AgentActivityHighlight/AgentActivityHighlight.tsx","../src/components/ShareControls/ShareControls.tsx"],"names":["jsxs","jsx","useEffect","useState"],"mappings":"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","file":"chunk-ZHAK2DQR.js","sourcesContent":["import { type CSSProperties, type ReactNode, useEffect, useRef } from \"react\";\n\nexport type AgentActivity = {\n id: string;\n /** Wall-clock timestamp; component formats it. */\n at: number;\n /** \"tool\" for MCP tool invocations, \"message\" for chat, \"info\" for status. */\n kind: \"tool\" | \"message\" | \"info\" | \"error\";\n /** Short label, e.g. \"whiteboard_add_sticky\" or \"Agent\". */\n source: string;\n /** Body text. */\n text: string;\n /** Optional structured payload, rendered as collapsed JSON. */\n detail?: unknown;\n};\n\nexport type AgentPanelProps = {\n /** The agent's identity (name + color appears in the header). */\n agent?: { name?: string; color?: string };\n /** Activity stream. Most recent at the end. */\n activity: AgentActivity[];\n /** Optional chat composer — pass an onSubmit to enable. */\n onSubmit?: (message: string) => void;\n /** Disabled while a request is in flight. */\n busy?: boolean;\n /** Right-rail header actions. */\n actions?: ReactNode;\n className?: string;\n style?: CSSProperties;\n};\n\n/**\n * AgentPanel — sidebar showing the agent's identity, a tool-and-chat log,\n * and an optional input composer. Pure presentational: hosts feed it the\n * activity stream from their own state (typically the MCP transport log).\n */\nexport function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }: AgentPanelProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n el.scrollTop = el.scrollHeight;\n }, [activity.length]);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const value = inputRef.current?.value.trim();\n if (!value || !onSubmit) return;\n onSubmit(value);\n if (inputRef.current) inputRef.current.value = \"\";\n };\n\n const color = agent?.color ?? \"#a855f7\";\n const name = agent?.name ?? \"Agent\";\n\n return (\n <div className={[\"fai-panel\", className ?? \"\"].filter(Boolean).join(\" \")} style={style}>\n <header className=\"fai-panel__header\">\n <div\n className=\"fai-panel__avatar\"\n style={{ background: color }}\n aria-hidden\n >\n {name.slice(0, 1)}\n </div>\n <div className=\"fai-panel__title\">\n <strong>{name}</strong>\n <span className=\"fai-panel__subtitle\">\n {busy ? \"Working…\" : `${activity.length} event${activity.length === 1 ? \"\" : \"s\"}`}\n </span>\n </div>\n {actions && <div className=\"fai-panel__actions\">{actions}</div>}\n </header>\n\n <div ref={scrollRef} className=\"fai-panel__stream\">\n {activity.length === 0 ? (\n <p className=\"fai-panel__empty\">No activity yet.</p>\n ) : (\n activity.map((a) => <ActivityRow key={a.id} item={a} />)\n )}\n </div>\n\n {onSubmit && (\n <form className=\"fai-panel__composer\" onSubmit={handleSubmit}>\n <textarea\n ref={inputRef}\n className=\"fai-panel__input\"\n placeholder={busy ? \"Working…\" : \"Ask the agent…\"}\n disabled={busy}\n rows={2}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit(e as unknown as React.FormEvent);\n }\n }}\n />\n <button type=\"submit\" className=\"fai-panel__send\" disabled={busy}>\n Send\n </button>\n </form>\n )}\n </div>\n );\n}\n\nfunction ActivityRow({ item }: { item: AgentActivity }) {\n const time = formatTime(item.at);\n return (\n <div className={`fai-row fai-row--${item.kind}`}>\n <div className=\"fai-row__meta\">\n <span className=\"fai-row__source\">{item.source}</span>\n <span className=\"fai-row__time\">{time}</span>\n </div>\n <div className=\"fai-row__text\">{item.text}</div>\n {item.detail !== undefined && (\n <details className=\"fai-row__detail\">\n <summary>details</summary>\n <pre>{safeJson(item.detail)}</pre>\n </details>\n )}\n </div>\n );\n}\n\nfunction formatTime(at: number): string {\n const d = new Date(at);\n const hh = d.getHours().toString().padStart(2, \"0\");\n const mm = d.getMinutes().toString().padStart(2, \"0\");\n const ss = d.getSeconds().toString().padStart(2, \"0\");\n return `${hh}:${mm}:${ss}`;\n}\n\nfunction safeJson(v: unknown): string {\n try {\n return JSON.stringify(v, null, 2);\n } catch {\n return String(v);\n }\n}\n","import type { CSSProperties } from \"react\";\n\nexport type AgentCursorProps = {\n x: number;\n y: number;\n name?: string;\n color?: string;\n /** Optional caption shown under the name (e.g. current tool). */\n status?: string;\n className?: string;\n style?: CSSProperties;\n};\n\n/**\n * AgentCursor — on-canvas presence marker for the agent. Drop it inside\n * (or alongside) a fancy-whiteboard <Board> at screen coords matching\n * the agent's reported position.\n */\nexport function AgentCursor({ x, y, name, color = \"#a855f7\", status, className, style }: AgentCursorProps) {\n return (\n <div\n className={[\"fai-cursor\", className ?? \"\"].filter(Boolean).join(\" \")}\n style={{\n position: \"absolute\",\n left: x,\n top: y,\n pointerEvents: \"none\",\n transform: \"translate(-2px, -2px)\",\n ...style,\n }}\n >\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 22 22\" aria-hidden>\n <path\n d=\"M2 2 L2 17 L7 13 L10 19 L12 18 L9 12 L15 12 Z\"\n fill={color}\n stroke=\"white\"\n strokeWidth=\"1.2\"\n />\n </svg>\n {name && (\n <span\n className=\"fai-cursor__tag\"\n style={{ background: color }}\n >\n {name}\n {status ? <em className=\"fai-cursor__status\"> · {status}</em> : null}\n </span>\n )}\n </div>\n );\n}\n","import { type CSSProperties, useEffect, useState } from \"react\";\n\nexport type AgentActivityHighlightProps = {\n /** Bounds of the highlighted item in the parent's coord system. */\n x: number;\n y: number;\n width: number;\n height: number;\n /** Trigger token — change it (e.g. set to Date.now()) to re-fire the pulse. */\n pulseKey?: string | number;\n /** Highlight tint. */\n color?: string;\n /** Pulse duration in ms. Defaults 1200. */\n duration?: number;\n className?: string;\n style?: CSSProperties;\n};\n\n/**\n * AgentActivityHighlight — short pulsing outline that flashes around an\n * item the agent just touched. Position the parent so this can be placed\n * absolutely matching the item's bounds.\n */\nexport function AgentActivityHighlight({\n x,\n y,\n width,\n height,\n pulseKey,\n color = \"#a855f7\",\n duration = 1200,\n className,\n style,\n}: AgentActivityHighlightProps) {\n const [visible, setVisible] = useState(false);\n\n useEffect(() => {\n if (pulseKey === undefined) return;\n setVisible(true);\n const t = setTimeout(() => setVisible(false), duration);\n return () => clearTimeout(t);\n }, [pulseKey, duration]);\n\n if (!visible) return null;\n\n return (\n <div\n className={[\"fai-highlight\", className ?? \"\"].filter(Boolean).join(\" \")}\n style={{\n position: \"absolute\",\n left: x - 4,\n top: y - 4,\n width: width + 8,\n height: height + 8,\n borderRadius: 8,\n boxShadow: `0 0 0 2px ${color}, 0 0 16px ${color}66`,\n pointerEvents: \"none\",\n animation: `fai-pulse ${duration}ms ease-out forwards`,\n ...style,\n }}\n />\n );\n}\n","import { type CSSProperties, useState } from \"react\";\nimport type { SessionDescriptor } from \"../../sharing/token\";\nimport { buildShareConfig, buildShareUrl } from \"../../sharing/token\";\n\nexport type ShareControlsProps = {\n /** The active session, or null when not sharing yet. */\n session: SessionDescriptor | null;\n onStart: () => void;\n onStop: () => void;\n /** Optional connection-state badge text. */\n status?: string;\n /** Override the URL base used in the share URL. */\n shareBaseUrl?: string;\n className?: string;\n style?: CSSProperties;\n};\n\ntype Tab = \"url\" | \"json\" | \"curl\";\n\n/**\n * ShareControls — the host-facing UI for turning sharing on/off and\n * surfacing the resulting connection details (URL / JSON / cURL).\n */\nexport function ShareControls({\n session,\n onStart,\n onStop,\n status,\n shareBaseUrl,\n className,\n style,\n}: ShareControlsProps) {\n const [tab, setTab] = useState<Tab>(\"url\");\n\n if (!session) {\n return (\n <div className={[\"fai-share fai-share--idle\", className ?? \"\"].filter(Boolean).join(\" \")} style={style}>\n <button type=\"button\" className=\"fai-share__start\" onClick={onStart}>\n Start shared session\n </button>\n <p className=\"fai-share__hint\">\n Generates a session id + secret token. Share the URL with humans, or hand the JSON config to an MCP-capable agent.\n </p>\n </div>\n );\n }\n\n const url = buildShareUrl(session, shareBaseUrl);\n const config = buildShareConfig(session);\n const curl = buildCurlRecipe(session);\n\n return (\n <div className={[\"fai-share fai-share--active\", className ?? \"\"].filter(Boolean).join(\" \")} style={style}>\n <div className=\"fai-share__header\">\n <div>\n <strong>Sharing</strong>\n <span className=\"fai-share__id\">\n session <code>{session.id}</code> · token <code>{session.display}…</code>\n </span>\n </div>\n <div className=\"fai-share__header-actions\">\n {status && <span className=\"fai-share__status\">{status}</span>}\n <button type=\"button\" className=\"fai-share__stop\" onClick={onStop}>\n Stop\n </button>\n </div>\n </div>\n\n <div className=\"fai-share__tabs\" role=\"tablist\">\n <TabButton tab=\"url\" active={tab} setTab={setTab}>URL</TabButton>\n <TabButton tab=\"json\" active={tab} setTab={setTab}>JSON</TabButton>\n <TabButton tab=\"curl\" active={tab} setTab={setTab}>cURL recipe</TabButton>\n </div>\n\n <div className=\"fai-share__panel\">\n {tab === \"url\" && <CopyBox label=\"Open this URL in another tab to join the session\" value={url} />}\n {tab === \"json\" && (\n <CopyBox\n label=\"Paste into Claude Desktop / Cline MCP server config\"\n value={JSON.stringify(config, null, 2)}\n />\n )}\n {tab === \"curl\" && (\n <CopyBox\n label=\"Connect from a terminal (verifies the relay is reachable)\"\n value={curl}\n multiline\n />\n )}\n </div>\n </div>\n );\n}\n\nfunction TabButton({ tab, active, setTab, children }: { tab: Tab; active: Tab; setTab: (t: Tab) => void; children: React.ReactNode }) {\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={tab === active}\n className={`fai-share__tab${tab === active ? \" is-active\" : \"\"}`}\n onClick={() => setTab(tab)}\n >\n {children}\n </button>\n );\n}\n\nfunction CopyBox({ label, value, multiline }: { label: string; value: string; multiline?: boolean }) {\n const [copied, setCopied] = useState(false);\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n } catch {\n // ignore\n }\n };\n return (\n <div>\n <div className=\"fai-share__panel-label\">{label}</div>\n <div className=\"fai-share__copy\">\n <pre className={`fai-share__pre${multiline ? \" is-multi\" : \"\"}`}>{value}</pre>\n <button type=\"button\" className=\"fai-share__copy-btn\" onClick={copy}>\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </div>\n </div>\n );\n}\n\n/** Build a copy-paste cURL recipe for connecting an external MCP client. */\nfunction buildCurlRecipe(session: SessionDescriptor): string {\n const base =\n typeof window !== \"undefined\"\n ? `${window.location.protocol}//${window.location.host}`\n : \"http://localhost\";\n const inbox = `${base}/whiteboard-share/${session.id}/inbox?token=${session.token}`;\n const events = `${base}/whiteboard-share/${session.id}/events?token=${session.token}`;\n return [\n `# 1) In one terminal, subscribe to server-pushed frames (SSE)`,\n `curl -N \"${events}\"`,\n ``,\n `# 2) In another terminal, send an initialize handshake`,\n `curl -X POST \"${inbox}\" \\\\`,\n ` -H 'content-type: application/json' \\\\`,\n ` -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}'`,\n ``,\n `# 3) List the tools the bridge exposes`,\n `curl -X POST \"${inbox}\" \\\\`,\n ` -H 'content-type: application/json' \\\\`,\n ` -d '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\"}'`,\n ``,\n `# 4) Add a sticky note`,\n `curl -X POST \"${inbox}\" \\\\`,\n ` -H 'content-type: application/json' \\\\`,\n ` -d '{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"whiteboard_add_sticky\",\"arguments\":{\"x\":300,\"y\":300,\"text\":\"hello from curl\"}}}'`,\n ].join(\"\\n\");\n}\n"]}
@@ -0,0 +1,55 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+ import { StickyNoteItem, ShapeItem, ConnectorItem, Stroke, Viewport } from '@particle-academy/fancy-whiteboard';
4
+ import { S as SessionDescriptor } from '../../token-CrJF76oH.cjs';
5
+
6
+ type SharedWhiteboardProps = {
7
+ /** Initial board contents. */
8
+ initialNotes?: StickyNoteItem[];
9
+ initialShapes?: ShapeItem[];
10
+ initialConnectors?: ConnectorItem[];
11
+ initialStrokes?: Stroke[];
12
+ initialViewport?: Viewport;
13
+ /** Agent identity displayed in the panel + cursor. */
14
+ agent?: {
15
+ id: string;
16
+ name?: string;
17
+ color?: string;
18
+ };
19
+ /**
20
+ * Where the relay HTTP endpoints live. The host app implements these (see
21
+ * docs/relay-protocol.md). Pass `null` to disable sharing — the board
22
+ * still works locally with the in-process MCP server.
23
+ */
24
+ shareBaseUrl?: string | null;
25
+ /**
26
+ * Optional callback to register a new session token with the host's
27
+ * relay broker. Receives `{ session, token }` and should return after
28
+ * registration. Defaults to POSTing JSON to `${shareBaseUrl}/register`.
29
+ */
30
+ onRegisterSession?: (descriptor: SessionDescriptor) => Promise<void>;
31
+ /** Show the agent panel. Default true. */
32
+ showAgentPanel?: boolean;
33
+ /** Show share controls. Default true. */
34
+ showShareControls?: boolean;
35
+ /** Auto-broadcast local edits as `notifications/state_update`. Default true. */
36
+ broadcastEdits?: boolean;
37
+ /** Pixel height of the board area. Default 640. */
38
+ height?: number;
39
+ /** Header content rendered above the board. */
40
+ header?: ReactNode;
41
+ className?: string;
42
+ style?: CSSProperties;
43
+ };
44
+ /**
45
+ * SharedWhiteboard — drop-in component that bundles every piece of the
46
+ * "agent-collaborative whiteboard" UX: board with all primitives, in-page
47
+ * MCP server, share controls, agent panel, presence cursor, activity
48
+ * highlight, and outbound state broadcast.
49
+ *
50
+ * Most apps only need this one component. For deeper customization, swap
51
+ * it for the lower-level primitives (Board, MicroMcpServer, ShareControls).
52
+ */
53
+ declare function SharedWhiteboard({ initialNotes, initialShapes, initialConnectors, initialStrokes, initialViewport, agent, shareBaseUrl, onRegisterSession, showAgentPanel, showShareControls, broadcastEdits, height, header, className, style, }: SharedWhiteboardProps): react_jsx_runtime.JSX.Element;
54
+
55
+ export { SharedWhiteboard, type SharedWhiteboardProps };
@@ -0,0 +1,55 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+ import { StickyNoteItem, ShapeItem, ConnectorItem, Stroke, Viewport } from '@particle-academy/fancy-whiteboard';
4
+ import { S as SessionDescriptor } from '../../token-CrJF76oH.js';
5
+
6
+ type SharedWhiteboardProps = {
7
+ /** Initial board contents. */
8
+ initialNotes?: StickyNoteItem[];
9
+ initialShapes?: ShapeItem[];
10
+ initialConnectors?: ConnectorItem[];
11
+ initialStrokes?: Stroke[];
12
+ initialViewport?: Viewport;
13
+ /** Agent identity displayed in the panel + cursor. */
14
+ agent?: {
15
+ id: string;
16
+ name?: string;
17
+ color?: string;
18
+ };
19
+ /**
20
+ * Where the relay HTTP endpoints live. The host app implements these (see
21
+ * docs/relay-protocol.md). Pass `null` to disable sharing — the board
22
+ * still works locally with the in-process MCP server.
23
+ */
24
+ shareBaseUrl?: string | null;
25
+ /**
26
+ * Optional callback to register a new session token with the host's
27
+ * relay broker. Receives `{ session, token }` and should return after
28
+ * registration. Defaults to POSTing JSON to `${shareBaseUrl}/register`.
29
+ */
30
+ onRegisterSession?: (descriptor: SessionDescriptor) => Promise<void>;
31
+ /** Show the agent panel. Default true. */
32
+ showAgentPanel?: boolean;
33
+ /** Show share controls. Default true. */
34
+ showShareControls?: boolean;
35
+ /** Auto-broadcast local edits as `notifications/state_update`. Default true. */
36
+ broadcastEdits?: boolean;
37
+ /** Pixel height of the board area. Default 640. */
38
+ height?: number;
39
+ /** Header content rendered above the board. */
40
+ header?: ReactNode;
41
+ className?: string;
42
+ style?: CSSProperties;
43
+ };
44
+ /**
45
+ * SharedWhiteboard — drop-in component that bundles every piece of the
46
+ * "agent-collaborative whiteboard" UX: board with all primitives, in-page
47
+ * MCP server, share controls, agent panel, presence cursor, activity
48
+ * highlight, and outbound state broadcast.
49
+ *
50
+ * Most apps only need this one component. For deeper customization, swap
51
+ * it for the lower-level primitives (Board, MicroMcpServer, ShareControls).
52
+ */
53
+ declare function SharedWhiteboard({ initialNotes, initialShapes, initialConnectors, initialStrokes, initialViewport, agent, shareBaseUrl, onRegisterSession, showAgentPanel, showShareControls, broadcastEdits, height, header, className, style, }: SharedWhiteboardProps): react_jsx_runtime.JSX.Element;
54
+
55
+ export { SharedWhiteboard, type SharedWhiteboardProps };