agentic-canvas 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -2
- package/dist/{chunk-JQCUVXXD.js → chunk-5II2WZIL.js} +2 -2
- package/dist/chunk-EDQNA2SU.js +290 -0
- package/dist/{chunk-NLTN7J22.js → chunk-EE725BJ3.js} +3 -5
- package/dist/{chunk-ZSPZMGHG.js → chunk-MVWUIBGV.js} +50 -320
- package/dist/{chunk-M74ENUWR.js → chunk-Z6KLGAFP.js} +6 -6
- package/dist/cli.js +4 -4
- package/dist/embed.d.ts +29 -4
- package/dist/embed.js +94 -19
- package/dist/index.js +11 -11
- package/dist/live-worker.js +4 -4
- package/dist/live.js +6 -5
- package/dist/worker.js +4 -4
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/chunk-JOBTIAFJ.js +0 -19
package/dist/embed.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
Board,
|
|
3
|
+
PRINCIPAL_KINDS,
|
|
4
|
+
principalSchema,
|
|
5
|
+
readBoard
|
|
6
|
+
} from "./chunk-MVWUIBGV.js";
|
|
4
7
|
|
|
5
8
|
// ui/embed.tsx
|
|
6
9
|
import { ToastProvider, TooltipProvider } from "agentic-design/react";
|
|
@@ -9,7 +12,7 @@ import { createRoot } from "react-dom/client";
|
|
|
9
12
|
|
|
10
13
|
// ui/BoardView.tsx
|
|
11
14
|
import { Button as Button2, EmptyState, Input, PresenceChip, PresenceStack, Select, Textarea, Toolbar, ToolbarButton, ToolbarSeparator, useToast } from "agentic-design/react";
|
|
12
|
-
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
15
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
|
|
13
16
|
|
|
14
17
|
// ui/errors.ts
|
|
15
18
|
var isApiError = (error) => typeof error === "object" && error !== null && error.name === "CanvasApiError" && typeof error.code === "string" && typeof error.status === "number";
|
|
@@ -226,6 +229,29 @@ function Failure({ message, retry }) {
|
|
|
226
229
|
] });
|
|
227
230
|
}
|
|
228
231
|
|
|
232
|
+
// ui/host.ts
|
|
233
|
+
import * as Y from "yjs";
|
|
234
|
+
function checkHost(host) {
|
|
235
|
+
if (!host || !(host.doc instanceof Y.Doc)) throw new TypeError("a host board needs a Yjs document in `doc`");
|
|
236
|
+
const presence = host.presence;
|
|
237
|
+
if (presence !== void 0 && (!Array.isArray(presence?.peers) || typeof presence.onPeers !== "function")) {
|
|
238
|
+
throw new TypeError("a host presence needs peers and onPeers, as a SyncClient has");
|
|
239
|
+
}
|
|
240
|
+
if (host.status !== void 0 && typeof host.status !== "function" || host.onStatus !== void 0 && typeof host.onStatus !== "function") {
|
|
241
|
+
throw new TypeError("a host status and onStatus must be functions");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function boardCalls(board, who) {
|
|
245
|
+
return {
|
|
246
|
+
readBoard: async (id) => ({ id, ...readBoard(board) }),
|
|
247
|
+
addShape: async (_id, shape) => board.addShape(shape, who()),
|
|
248
|
+
addConnector: async (_id, connector) => board.addConnector(connector, who()),
|
|
249
|
+
update: async (_id, element, patch) => board.update(element, patch, who()),
|
|
250
|
+
move: async (_id, element, dx, dy) => ({ moved: board.move(element, dx, dy, who()) }),
|
|
251
|
+
remove: async (_id, element) => ({ removed: board.remove(element) })
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
229
255
|
// ui/presence.ts
|
|
230
256
|
import { SyncClient } from "agentic-realtime/client";
|
|
231
257
|
var WS_PROTOCOL = "agentic-canvas";
|
|
@@ -288,7 +314,7 @@ var TOOLS = [
|
|
|
288
314
|
var POLL_MS = 5e3;
|
|
289
315
|
var DEFAULT_SIZE = { rect: [140, 70], ellipse: [140, 70], diamond: [140, 90], text: [140, 40], sticky: [150, 120] };
|
|
290
316
|
var DEFAULT_TEXT = { rect: "Rectangle", ellipse: "Ellipse", diamond: "Decision", text: "Text", sticky: "Note" };
|
|
291
|
-
function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
317
|
+
function BoardView({ id, client: remote, host, room: roomOptions, me, backHref }) {
|
|
292
318
|
const roomUrl2 = roomOptions?.baseUrl;
|
|
293
319
|
const roomToken = roomOptions?.token;
|
|
294
320
|
const toast = useToast();
|
|
@@ -302,6 +328,17 @@ function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
|
302
328
|
const queue = useRef2(Promise.resolve());
|
|
303
329
|
const canvasBox = useRef2(null);
|
|
304
330
|
const editable = me.role === "editor";
|
|
331
|
+
const hostDoc = host?.doc;
|
|
332
|
+
const hostPresence = host?.presence;
|
|
333
|
+
const hostRef = useRef2(host);
|
|
334
|
+
hostRef.current = host;
|
|
335
|
+
const who = useRef2({ kind: me.kind, id: me.id ?? me.actor });
|
|
336
|
+
who.current = { kind: me.kind, id: me.id ?? me.actor };
|
|
337
|
+
const client = useMemo(() => {
|
|
338
|
+
if (hostDoc) return boardCalls(new Board(hostDoc), () => who.current);
|
|
339
|
+
if (!remote) throw new TypeError("the board needs a client or a host board");
|
|
340
|
+
return remote;
|
|
341
|
+
}, [remote, hostDoc]);
|
|
305
342
|
const [saving, setSaving] = useState2(0);
|
|
306
343
|
const pending = useRef2(0);
|
|
307
344
|
const generation = useRef2(0);
|
|
@@ -319,7 +356,7 @@ function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
|
319
356
|
}, [client, id]);
|
|
320
357
|
const load = useCallback(async () => {
|
|
321
358
|
if (pending.current > 0) return;
|
|
322
|
-
const wait = lastRead.current + 1e3 - Date.now();
|
|
359
|
+
const wait = hostDoc ? 0 : lastRead.current + 1e3 - Date.now();
|
|
323
360
|
if (wait > 0) {
|
|
324
361
|
clearTimeout(timer.current);
|
|
325
362
|
timer.current = setTimeout(() => void load(), wait);
|
|
@@ -327,7 +364,7 @@ function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
|
327
364
|
}
|
|
328
365
|
lastRead.current = Date.now();
|
|
329
366
|
await read();
|
|
330
|
-
}, [read]);
|
|
367
|
+
}, [read, hostDoc]);
|
|
331
368
|
useEffect2(() => {
|
|
332
369
|
setBoard({ state: "loading" });
|
|
333
370
|
lastRead.current = 0;
|
|
@@ -341,15 +378,45 @@ function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
|
341
378
|
return () => window.removeEventListener("beforeunload", guard);
|
|
342
379
|
}, [saving]);
|
|
343
380
|
useEffect2(() => {
|
|
344
|
-
if (
|
|
345
|
-
|
|
381
|
+
if (!hostDoc) return;
|
|
382
|
+
let queued = false;
|
|
383
|
+
const changed = () => {
|
|
384
|
+
if (queued) return;
|
|
385
|
+
queued = true;
|
|
386
|
+
queueMicrotask(() => (queued = false, void load()));
|
|
387
|
+
};
|
|
388
|
+
const elements = hostDoc.getMap("elements");
|
|
389
|
+
const meta = hostDoc.getMap("meta");
|
|
390
|
+
elements.observeDeep(changed);
|
|
391
|
+
meta.observe(changed);
|
|
392
|
+
return () => (elements.unobserveDeep(changed), meta.unobserve(changed));
|
|
393
|
+
}, [hostDoc, load]);
|
|
394
|
+
useEffect2(() => {
|
|
395
|
+
if (!hostPresence) return;
|
|
396
|
+
setPeers(hostPresence.peers.map(toEntry));
|
|
397
|
+
const off = hostPresence.onPeers((list) => setPeers(list.map(toEntry)));
|
|
398
|
+
return () => (off(), setPeers([]));
|
|
399
|
+
}, [hostPresence]);
|
|
400
|
+
useEffect2(() => {
|
|
401
|
+
if (!hostDoc) return;
|
|
402
|
+
const reset = () => setLive(false);
|
|
403
|
+
const { status: ask, onStatus: listen } = hostRef.current ?? {};
|
|
404
|
+
setLive(ask ? ask() === "connected" : !listen);
|
|
405
|
+
const stop = listen?.((next) => setLive(next === "connected"));
|
|
406
|
+
return () => (stop?.(), reset());
|
|
407
|
+
}, [hostDoc]);
|
|
408
|
+
useEffect2(() => {
|
|
409
|
+
if (hostDoc || roomUrl2 === void 0 || roomToken === void 0) return;
|
|
410
|
+
let left = false;
|
|
411
|
+
const joined = joinRoom({ board: id, baseUrl: roomUrl2, token: roomToken, name: me.actor, onPeers: (list) => !left && setPeers(list), onChange: () => !left && void load(), onStatus: (up) => !left && setLive(up) });
|
|
346
412
|
room.current = joined;
|
|
347
|
-
return () => (joined.leave(), room.current = void 0);
|
|
348
|
-
}, [id, roomUrl2, roomToken, me.actor, load]);
|
|
413
|
+
return () => (left = true, joined.leave(), setPeers([]), room.current = void 0);
|
|
414
|
+
}, [id, hostDoc, roomUrl2, roomToken, me.actor, load]);
|
|
349
415
|
useEffect2(() => {
|
|
416
|
+
if (hostDoc) return;
|
|
350
417
|
const timer2 = setInterval(() => void load(), POLL_MS);
|
|
351
418
|
return () => clearInterval(timer2);
|
|
352
|
-
}, [load]);
|
|
419
|
+
}, [load, hostDoc]);
|
|
353
420
|
const write = useCallback(
|
|
354
421
|
(change, failure, select) => {
|
|
355
422
|
pending.current++;
|
|
@@ -425,7 +492,7 @@ function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
|
425
492
|
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Reset view", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 4h5M4 4v5M16 16h-5M16 16v-5" })), onClick: () => setView(HOME) })
|
|
426
493
|
] }),
|
|
427
494
|
/* @__PURE__ */ jsx3("p", { className: "note", role: "status", children: editable ? status : "" }),
|
|
428
|
-
!editable && /* @__PURE__ */ jsx3("p", { className: "note", children: "This token can read the board but not change it." }),
|
|
495
|
+
!editable && /* @__PURE__ */ jsx3("p", { className: "note", children: host ? "This board is read only." : "This token can read the board but not change it." }),
|
|
429
496
|
/* @__PURE__ */ jsxs3("div", { className: "stage", ref: canvasBox, children: [
|
|
430
497
|
/* @__PURE__ */ jsx3(Canvas, { data: d, selected, editable, view, onView: setView, onSelect: setSelected, onMove: move, onResize: resize, onDelete: remove }),
|
|
431
498
|
empty && /* @__PURE__ */ jsx3("div", { className: "empty-overlay", children: /* @__PURE__ */ jsx3(EmptyState, { title: "This board is empty", description: editable ? "Add a shape or a note with the tools above." : "Nothing has been drawn yet." }) }),
|
|
@@ -487,10 +554,18 @@ function Inspector({ item, nodes, editable, onText, onPlace, onDelete, onConnect
|
|
|
487
554
|
|
|
488
555
|
// ui/embed.tsx
|
|
489
556
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
490
|
-
function checkPrincipal(principal) {
|
|
557
|
+
function checkPrincipal(principal, options) {
|
|
491
558
|
if (!principal || !PRINCIPAL_KINDS.includes(principal.kind) || typeof principal.name !== "string" || principal.name.trim() === "") {
|
|
492
559
|
throw new TypeError(`the board needs a principal with a kind (${PRINCIPAL_KINDS.join(", ")}) and a name`);
|
|
493
560
|
}
|
|
561
|
+
if (!options) return;
|
|
562
|
+
if (options.host) {
|
|
563
|
+
checkHost(options.host);
|
|
564
|
+
if (options.client) throw new TypeError("a host board writes to its own document, not through a `client`: give one of them");
|
|
565
|
+
const stamp = principalSchema.safeParse({ kind: options.principal.kind, id: options.principal.id ?? options.principal.name });
|
|
566
|
+
if (!stamp.success) throw new TypeError(`the principal needs an \`id\` of letters, digits, ".", "_" and "-" to stamp its writes: ${stamp.error.issues[0]?.message}`);
|
|
567
|
+
if (options.room) throw new TypeError("a host board has no live room of its own: give its presence in `host.presence`, not `room`");
|
|
568
|
+
} else if (!options.client) throw new TypeError("the board needs a `client`, or a `host` board");
|
|
494
569
|
}
|
|
495
570
|
var Boundary = class extends Component {
|
|
496
571
|
state = { failed: false };
|
|
@@ -504,16 +579,16 @@ var Boundary = class extends Component {
|
|
|
504
579
|
] }) }) : this.props.children;
|
|
505
580
|
}
|
|
506
581
|
};
|
|
507
|
-
function
|
|
508
|
-
checkPrincipal(principal);
|
|
582
|
+
function Board2({ boardId, client, host, principal, readOnly, room, backHref }) {
|
|
583
|
+
checkPrincipal(principal, { boardId, client, host, principal, room });
|
|
509
584
|
const role = principal.role === "editor" && !readOnly ? "editor" : "viewer";
|
|
510
|
-
return /* @__PURE__ */ jsx4(BoardView, { id: boardId, client, room, backHref, me: { actor: principal.name, kind: principal.kind, role } }, boardId);
|
|
585
|
+
return /* @__PURE__ */ jsx4(BoardView, { id: boardId, client, host, room, backHref, me: { actor: principal.name, id: principal.id, kind: principal.kind, role } }, boardId);
|
|
511
586
|
}
|
|
512
587
|
function BoardEditor(options) {
|
|
513
|
-
return /* @__PURE__ */ jsx4("div", { className: "cv-root", children: /* @__PURE__ */ jsx4(Boundary, { children: /* @__PURE__ */ jsx4(
|
|
588
|
+
return /* @__PURE__ */ jsx4("div", { className: "cv-root", children: /* @__PURE__ */ jsx4(Boundary, { children: /* @__PURE__ */ jsx4(Board2, { ...options }) }) });
|
|
514
589
|
}
|
|
515
590
|
function mount(element, options) {
|
|
516
|
-
checkPrincipal(options.principal);
|
|
591
|
+
checkPrincipal(options.principal, options);
|
|
517
592
|
const root = createRoot(element);
|
|
518
593
|
const render = (next) => root.render(
|
|
519
594
|
/* @__PURE__ */ jsx4(TooltipProvider, { children: /* @__PURE__ */ jsx4(ToastProvider, { children: /* @__PURE__ */ jsx4(BoardEditor, { ...next }) }) })
|
|
@@ -521,7 +596,7 @@ function mount(element, options) {
|
|
|
521
596
|
render(options);
|
|
522
597
|
let open = true;
|
|
523
598
|
return {
|
|
524
|
-
update: (next) => (checkPrincipal(next.principal), open && render(next)),
|
|
599
|
+
update: (next) => (checkPrincipal(next.principal, next), open && render(next)),
|
|
525
600
|
unmount: () => {
|
|
526
601
|
if (!open) return;
|
|
527
602
|
open = false;
|
package/dist/index.js
CHANGED
|
@@ -6,34 +6,34 @@ import {
|
|
|
6
6
|
operatorActor,
|
|
7
7
|
runMcp,
|
|
8
8
|
svgToPng
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-EE725BJ3.js";
|
|
10
10
|
import {
|
|
11
11
|
createFileStore,
|
|
12
12
|
openLocalStore
|
|
13
13
|
} from "./chunk-NRLF6MGR.js";
|
|
14
14
|
import {
|
|
15
15
|
CanvasService
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-Z6KLGAFP.js";
|
|
17
17
|
import "./chunk-NWQED42L.js";
|
|
18
18
|
import {
|
|
19
|
-
Board,
|
|
20
|
-
CanvasError,
|
|
21
|
-
MAX_ELEMENTS,
|
|
22
19
|
MAX_FLOW_EDGES,
|
|
23
20
|
MAX_FLOW_NODES,
|
|
24
|
-
MAX_TEXT,
|
|
25
|
-
SHAPE_TYPES,
|
|
26
21
|
drawFlow,
|
|
27
22
|
layoutFlow,
|
|
28
23
|
parseFlow,
|
|
29
|
-
readBoard,
|
|
30
24
|
toSvg
|
|
31
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-EDQNA2SU.js";
|
|
32
26
|
import {
|
|
27
|
+
Board,
|
|
28
|
+
CanvasError,
|
|
29
|
+
MAX_ELEMENTS,
|
|
30
|
+
MAX_TEXT,
|
|
33
31
|
PRINCIPAL_KINDS,
|
|
32
|
+
SHAPE_TYPES,
|
|
34
33
|
isMachineMade,
|
|
35
|
-
principalSchema
|
|
36
|
-
|
|
34
|
+
principalSchema,
|
|
35
|
+
readBoard
|
|
36
|
+
} from "./chunk-MVWUIBGV.js";
|
|
37
37
|
export {
|
|
38
38
|
ActorConfigError,
|
|
39
39
|
BRAND,
|
package/dist/live-worker.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createApp
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-5II2WZIL.js";
|
|
4
|
+
import "./chunk-Z6KLGAFP.js";
|
|
5
5
|
import "./chunk-NWQED42L.js";
|
|
6
|
+
import "./chunk-EDQNA2SU.js";
|
|
6
7
|
import {
|
|
7
8
|
CanvasError
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-JOBTIAFJ.js";
|
|
9
|
+
} from "./chunk-MVWUIBGV.js";
|
|
10
10
|
|
|
11
11
|
// src/live-worker.ts
|
|
12
12
|
import { parseRegion } from "agentic-identity";
|
package/dist/live.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Board,
|
|
3
|
-
CanvasError,
|
|
4
2
|
drawFlow,
|
|
5
|
-
readBoard,
|
|
6
3
|
toSvg
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import
|
|
4
|
+
} from "./chunk-EDQNA2SU.js";
|
|
5
|
+
import {
|
|
6
|
+
Board,
|
|
7
|
+
CanvasError,
|
|
8
|
+
readBoard
|
|
9
|
+
} from "./chunk-MVWUIBGV.js";
|
|
9
10
|
|
|
10
11
|
// src/live.ts
|
|
11
12
|
import { parsePrincipal } from "agentic-identity";
|
package/dist/worker.js
CHANGED
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
identify,
|
|
6
6
|
sniffImage,
|
|
7
7
|
worker_default
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-5II2WZIL.js";
|
|
9
|
+
import "./chunk-Z6KLGAFP.js";
|
|
10
10
|
import "./chunk-NWQED42L.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-EDQNA2SU.js";
|
|
12
|
+
import "./chunk-MVWUIBGV.js";
|
|
13
13
|
export {
|
|
14
14
|
WS_PROTOCOL,
|
|
15
15
|
authenticate,
|
package/llms.txt
CHANGED
|
@@ -11,7 +11,7 @@ Keywords: whiteboard,canvas,diagrams,drawing,collaboration,agents,mcp,cli.
|
|
|
11
11
|
- Live: set CANVAS_SERVER and CANVAS_TOKEN, and the CLI and `agentic-canvas mcp` edit the board in its live room.
|
|
12
12
|
- SDK: import { LiveBoard, LiveService } from "agentic-canvas/live".
|
|
13
13
|
- SDK: import { createClient, CanvasApiError } from "agentic-canvas/client". It calls the Worker API with fetch. Methods: me, listBoards, createBoard, readBoard, deleteBoard, exportSvg, addShape, addConnector, drawFlow, update, move, remove, access, grant, revoke, invite, uploadImage, getImage, state, checkpoint. A failure throws CanvasApiError with `code` and `status`. No retries.
|
|
14
|
-
- UI: import { mount } from "agentic-canvas/ui"; import "agentic-canvas/ui.css" (with agentic-design/styles.css). mount(element, { boardId, client, principal: { kind, name, role }, readOnly?, room?: { baseUrl, token } }) renders the board and returns { update(options), unmount() }. BoardEditor is the same as a React component. React 19 is a peer dependency.
|
|
14
|
+
- UI: import { mount } from "agentic-canvas/ui"; import "agentic-canvas/ui.css" (with agentic-design/styles.css). mount(element, { boardId, client, principal: { kind, name, role, id? }, readOnly?, room?: { baseUrl, token } }) (or host: { doc, presence?, status?, onStatus? } instead of client and room; in host mode id (default name) must be letters, digits, . _ -, to edit the app's own Yjs document with no HTTP call) renders the board and returns { update(options), unmount() }. BoardEditor is the same as a React component. React 19 is a peer dependency.
|
|
15
15
|
|
|
16
16
|
## Docs
|
|
17
17
|
|
package/package.json
CHANGED
package/dist/chunk-JOBTIAFJ.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// src/core/principal.ts
|
|
2
|
-
import { PRINCIPAL_KINDS, isMachineMade, parsePrincipal } from "agentic-identity";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
var principalSchema = z.object({ kind: z.string(), id: z.string() }).strict().transform((value, ctx) => {
|
|
5
|
-
try {
|
|
6
|
-
return parsePrincipal(value);
|
|
7
|
-
} catch (error) {
|
|
8
|
-
ctx.addIssue({ code: "custom", message: error instanceof Error ? error.message : "invalid principal" });
|
|
9
|
-
return z.NEVER;
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
var storedPrincipalSchema = z.object({ kind: z.enum(PRINCIPAL_KINDS), id: z.string().min(1).max(200) }).strict();
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
PRINCIPAL_KINDS,
|
|
16
|
-
isMachineMade,
|
|
17
|
-
principalSchema,
|
|
18
|
-
storedPrincipalSchema
|
|
19
|
-
};
|