agentic-canvas 0.10.0 → 0.12.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 +55 -3
- package/dist/{chunk-JQCUVXXD.js → chunk-5II2WZIL.js} +2 -2
- package/dist/{chunk-NLTN7J22.js → chunk-A4YK2SWM.js} +3 -5
- package/dist/chunk-EDQNA2SU.js +290 -0
- 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 +114 -35
- package/dist/index.js +11 -11
- package/dist/live-worker.js +4 -4
- package/dist/live.js +6 -5
- package/dist/ui.css +17 -9
- package/dist/worker.js +4 -4
- package/llms.txt +1 -1
- package/package.json +4 -2
- 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++;
|
|
@@ -410,25 +477,29 @@ function BoardView({ id, client, room: roomOptions, me, backHref }) {
|
|
|
410
477
|
const empty = d.nodes.length === 0 && d.edges.length === 0;
|
|
411
478
|
return /* @__PURE__ */ jsxs3("section", { className: "board", "aria-labelledby": "board-h", children: [
|
|
412
479
|
/* @__PURE__ */ jsxs3("div", { className: "row", children: [
|
|
413
|
-
/* @__PURE__ */
|
|
480
|
+
/* @__PURE__ */ jsxs3("div", { className: "heading", children: [
|
|
481
|
+
/* @__PURE__ */ jsx3("h1", { id: "board-h", children: d.title || d.id }),
|
|
482
|
+
/* @__PURE__ */ jsx3("p", { className: "note", role: "status", children: editable ? status : "" })
|
|
483
|
+
] }),
|
|
414
484
|
/* @__PURE__ */ jsx3(PresenceStack, { entries: [{ id: "me", name: me.actor, kind: me.kind === "human" ? "person" : "agent" }, ...peers], label: live ? "In this board" : "In this board (not live)" })
|
|
415
485
|
] }),
|
|
416
|
-
/* @__PURE__ */
|
|
417
|
-
|
|
418
|
-
/* @__PURE__ */
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
486
|
+
!editable && /* @__PURE__ */ jsx3("p", { className: "note", children: host ? "This board is read only." : "This token can read the board but not change it." }),
|
|
487
|
+
/* @__PURE__ */ jsxs3("div", { className: "stage", children: [
|
|
488
|
+
/* @__PURE__ */ jsxs3("div", { className: "surface", ref: canvasBox, children: [
|
|
489
|
+
/* @__PURE__ */ jsx3("div", { className: "palette", children: /* @__PURE__ */ jsx3(Toolbar, { label: "Shape palette", children: TOOLS.map((t) => /* @__PURE__ */ jsx3(ToolbarButton, { label: t.text, icon: t.icon, disabled: !editable, onClick: () => add(t.type) }, t.type)) }) }),
|
|
490
|
+
/* @__PURE__ */ jsx3("div", { className: "zoom", children: /* @__PURE__ */ jsxs3(Toolbar, { label: "Zoom and pan", children: [
|
|
491
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Zoom out", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 10h12" })), onClick: () => zoom(1 / 1.25) }),
|
|
492
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Zoom in", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 10h12M10 4v12" })), onClick: () => zoom(1.25) }),
|
|
493
|
+
/* @__PURE__ */ jsx3(ToolbarSeparator, {}),
|
|
494
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Pan left", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M12 4l-6 6 6 6" })), onClick: () => setView((v) => ({ ...v, x: v.x + PAN_STEP })) }),
|
|
495
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Pan right", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M8 4l6 6-6 6" })), onClick: () => setView((v) => ({ ...v, x: v.x - PAN_STEP })) }),
|
|
496
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Pan up", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 12l6-6 6 6" })), onClick: () => setView((v) => ({ ...v, y: v.y + PAN_STEP })) }),
|
|
497
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Pan down", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 8l6 6 6-6" })), onClick: () => setView((v) => ({ ...v, y: v.y - PAN_STEP })) }),
|
|
498
|
+
/* @__PURE__ */ jsx3(ToolbarButton, { label: "Reset view", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 4h5M4 4v5M16 16h-5M16 16v-5" })), onClick: () => setView(HOME) })
|
|
499
|
+
] }) }),
|
|
500
|
+
/* @__PURE__ */ jsx3(Canvas, { data: d, selected, editable, view, onView: setView, onSelect: setSelected, onMove: move, onResize: resize, onDelete: remove }),
|
|
501
|
+
empty && /* @__PURE__ */ jsx3("div", { className: "empty-overlay", children: /* @__PURE__ */ jsx3(EmptyState, { title: "This board is empty", description: editable ? "Pick a shape from the palette to start." : "Nothing has been drawn yet." }) })
|
|
502
|
+
] }),
|
|
432
503
|
/* @__PURE__ */ jsx3(Inspector, { item, nodes: d.nodes, editable, onText: (text) => item && write(() => client.update(id, item.id, { text }), "Could not save the text"), onPlace: place, onDelete: remove, onConnect: (to) => item && write(() => client.addConnector(id, { from: { id: item.id }, to: { id: to } }), "Could not connect") }, item?.id ?? "none")
|
|
433
504
|
] })
|
|
434
505
|
] });
|
|
@@ -487,10 +558,18 @@ function Inspector({ item, nodes, editable, onText, onPlace, onDelete, onConnect
|
|
|
487
558
|
|
|
488
559
|
// ui/embed.tsx
|
|
489
560
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
490
|
-
function checkPrincipal(principal) {
|
|
561
|
+
function checkPrincipal(principal, options) {
|
|
491
562
|
if (!principal || !PRINCIPAL_KINDS.includes(principal.kind) || typeof principal.name !== "string" || principal.name.trim() === "") {
|
|
492
563
|
throw new TypeError(`the board needs a principal with a kind (${PRINCIPAL_KINDS.join(", ")}) and a name`);
|
|
493
564
|
}
|
|
565
|
+
if (!options) return;
|
|
566
|
+
if (options.host) {
|
|
567
|
+
checkHost(options.host);
|
|
568
|
+
if (options.client) throw new TypeError("a host board writes to its own document, not through a `client`: give one of them");
|
|
569
|
+
const stamp = principalSchema.safeParse({ kind: options.principal.kind, id: options.principal.id ?? options.principal.name });
|
|
570
|
+
if (!stamp.success) throw new TypeError(`the principal needs an \`id\` of letters, digits, ".", "_" and "-" to stamp its writes: ${stamp.error.issues[0]?.message}`);
|
|
571
|
+
if (options.room) throw new TypeError("a host board has no live room of its own: give its presence in `host.presence`, not `room`");
|
|
572
|
+
} else if (!options.client) throw new TypeError("the board needs a `client`, or a `host` board");
|
|
494
573
|
}
|
|
495
574
|
var Boundary = class extends Component {
|
|
496
575
|
state = { failed: false };
|
|
@@ -504,16 +583,16 @@ var Boundary = class extends Component {
|
|
|
504
583
|
] }) }) : this.props.children;
|
|
505
584
|
}
|
|
506
585
|
};
|
|
507
|
-
function
|
|
508
|
-
checkPrincipal(principal);
|
|
586
|
+
function Board2({ boardId, client, host, principal, readOnly, room, backHref }) {
|
|
587
|
+
checkPrincipal(principal, { boardId, client, host, principal, room });
|
|
509
588
|
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);
|
|
589
|
+
return /* @__PURE__ */ jsx4(BoardView, { id: boardId, client, host, room, backHref, me: { actor: principal.name, id: principal.id, kind: principal.kind, role } }, boardId);
|
|
511
590
|
}
|
|
512
591
|
function BoardEditor(options) {
|
|
513
|
-
return /* @__PURE__ */ jsx4("div", { className: "cv-root", children: /* @__PURE__ */ jsx4(Boundary, { children: /* @__PURE__ */ jsx4(
|
|
592
|
+
return /* @__PURE__ */ jsx4("div", { className: "cv-root", children: /* @__PURE__ */ jsx4(Boundary, { children: /* @__PURE__ */ jsx4(Board2, { ...options }) }) });
|
|
514
593
|
}
|
|
515
594
|
function mount(element, options) {
|
|
516
|
-
checkPrincipal(options.principal);
|
|
595
|
+
checkPrincipal(options.principal, options);
|
|
517
596
|
const root = createRoot(element);
|
|
518
597
|
const render = (next) => root.render(
|
|
519
598
|
/* @__PURE__ */ jsx4(TooltipProvider, { children: /* @__PURE__ */ jsx4(ToastProvider, { children: /* @__PURE__ */ jsx4(BoardEditor, { ...next }) }) })
|
|
@@ -521,7 +600,7 @@ function mount(element, options) {
|
|
|
521
600
|
render(options);
|
|
522
601
|
let open = true;
|
|
523
602
|
return {
|
|
524
|
-
update: (next) => (checkPrincipal(next.principal), open && render(next)),
|
|
603
|
+
update: (next) => (checkPrincipal(next.principal, next), open && render(next)),
|
|
525
604
|
unmount: () => {
|
|
526
605
|
if (!open) return;
|
|
527
606
|
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-A4YK2SWM.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/ui.css
CHANGED
|
@@ -5,32 +5,40 @@
|
|
|
5
5
|
.note { color: var(--agd-color-text-muted); }
|
|
6
6
|
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
7
7
|
|
|
8
|
-
.board { display: grid; gap: var(--agd-space-
|
|
9
|
-
.
|
|
8
|
+
.board { display: grid; gap: var(--agd-space-4); }
|
|
9
|
+
.heading { display: grid; gap: var(--agd-space-1); }
|
|
10
|
+
.heading h1, .heading .note { margin: 0; }
|
|
11
|
+
.heading .note { font-size: var(--agd-font-size-sm); min-height: 1.25em; }
|
|
12
|
+
.stage { display: grid; grid-template-columns: minmax(0, 1fr) 15rem; gap: var(--agd-space-4); height: 68vh; min-height: 24rem; }
|
|
13
|
+
/* The palette and the zoom controls float on the canvas, so the page keeps one calm surface. They come before the canvas, so Tab reaches them first. */
|
|
14
|
+
.surface { position: relative; min-height: 18rem; }
|
|
15
|
+
.palette, .zoom { position: absolute; z-index: 1; left: var(--agd-space-3); max-width: calc(100% - 2 * var(--agd-space-3)); }
|
|
16
|
+
.palette { top: var(--agd-space-3); }
|
|
17
|
+
.zoom { bottom: var(--agd-space-3); }
|
|
18
|
+
.palette .agd-toolbar, .zoom .agd-toolbar { flex-wrap: wrap; box-shadow: var(--agd-shadow-raised); }
|
|
19
|
+
.canvas { height: 100%; box-sizing: border-box; background-color: var(--agd-color-surface); background-image: radial-gradient(var(--agd-color-border) 1px, transparent 1px); background-size: 24px 24px; border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-lg); overflow: hidden; touch-action: none; min-height: 18rem; }
|
|
10
20
|
@media (max-width: 48rem) { .stage { grid-template-columns: 1fr; height: auto; } .canvas { height: 55vh; } }
|
|
11
|
-
.canvas { background: var(--agd-color-surface-sunken); border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-md); overflow: hidden; touch-action: none; min-height: 18rem; }
|
|
12
21
|
.canvas:focus-visible { outline: 3px solid var(--agd-color-focus); outline-offset: 2px; }
|
|
13
22
|
.canvas svg { display: block; cursor: grab; font-family: var(--agd-font-family-sans); font-size: 14px; }
|
|
14
23
|
.item { outline: none; cursor: pointer; }
|
|
15
|
-
.shape { fill: var(--agd-color-surface-raised); stroke: var(--agd-color-border-strong); stroke-width: 1
|
|
24
|
+
.shape { fill: var(--agd-color-surface-raised); stroke: var(--agd-color-border-strong); stroke-width: 1; }
|
|
16
25
|
.shape.text { fill: transparent; stroke: none; }
|
|
17
26
|
.shape.frame, .shape.image { fill: none; stroke-dasharray: 6 4; pointer-events: visiblePainted; }
|
|
18
27
|
.shape.sticky { fill: var(--agd-color-warning-subtle); stroke: var(--agd-color-warning); }
|
|
19
28
|
.label { fill: var(--agd-color-text); pointer-events: none; }
|
|
20
29
|
.sticky-label { fill: var(--agd-color-warning-text); }
|
|
21
|
-
.edge { stroke: var(--agd-color-text-muted); stroke-width:
|
|
30
|
+
.edge { stroke: var(--agd-color-text-muted); stroke-width: 1.5; fill: none; pointer-events: none; }
|
|
22
31
|
.hit { stroke: transparent; stroke-width: 14; }
|
|
23
32
|
.arrowhead { fill: var(--agd-color-text-muted); }
|
|
24
|
-
.edge-text { fill: var(--agd-color-text); paint-order: stroke; stroke: var(--agd-color-surface
|
|
33
|
+
.edge-text { fill: var(--agd-color-text); paint-order: stroke; stroke: var(--agd-color-surface); stroke-width: 4px; pointer-events: none; }
|
|
25
34
|
.ring, .ring-line { fill: none; stroke: none; stroke-width: 3; pointer-events: none; }
|
|
26
35
|
.item.selected .ring, .item.selected .ring-line, .item:focus-visible .ring, .item:focus-visible .ring-line { stroke: var(--agd-color-focus); }
|
|
27
36
|
.item:focus-visible .ring, .item:focus-visible .ring-line { stroke-width: 4; }
|
|
28
37
|
.handle { fill: var(--agd-color-focus); stroke: var(--agd-color-bg); stroke-width: 2; cursor: nwse-resize; }
|
|
29
38
|
.agent-badge rect { fill: var(--agd-color-agent-subtle); stroke: var(--agd-color-agent); }
|
|
30
39
|
.agent-badge text { fill: var(--agd-color-agent-text); font-size: 11px; font-weight: 600; pointer-events: none; }
|
|
31
|
-
.empty-overlay { position: absolute; inset: 0
|
|
32
|
-
|
|
33
|
-
.inspector { border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-md); padding: var(--agd-space-3); background: var(--agd-color-surface); display: grid; gap: var(--agd-space-3); align-content: start; overflow: auto; }
|
|
40
|
+
.empty-overlay { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; }
|
|
41
|
+
.inspector { border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-lg); padding: var(--agd-space-4); background: var(--agd-color-surface); display: grid; gap: var(--agd-space-3); align-content: start; overflow: auto; }
|
|
34
42
|
.inspector h2 { margin: 0; font-size: var(--agd-font-size-lg); }
|
|
35
43
|
.inspector form { display: grid; gap: var(--agd-space-3); }
|
|
36
44
|
.inspector .by { margin: 0; color: var(--agd-color-text-muted); font-size: var(--agd-font-size-sm); }
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "A shared whiteboard for diagrams, drawn by people and agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"whiteboard",
|
|
@@ -74,6 +74,8 @@
|
|
|
74
74
|
"typecheck": "tsc --noEmit && tsc --noEmit -p ui",
|
|
75
75
|
"test": "vitest run",
|
|
76
76
|
"clean": "rm -rf dist",
|
|
77
|
+
"dev": "npm run build && WRANGLER_SEND_METRICS=false wrangler dev --port 8793 --var CANVAS_REGION:unknown",
|
|
78
|
+
"dev:smoke": "npm run build && node scripts/dev-smoke.mjs 8793 /health --var CANVAS_REGION:unknown",
|
|
77
79
|
"ci:local": "npm run build && npm run typecheck && npm test && npm run test:e2e",
|
|
78
80
|
"deploy:demo": "wrangler deploy -c wrangler.demo.jsonc",
|
|
79
81
|
"test:e2e": "playwright test -c e2e/playwright.config.ts"
|
|
@@ -81,7 +83,7 @@
|
|
|
81
83
|
"dependencies": {
|
|
82
84
|
"@dagrejs/dagre": "^3.1.3",
|
|
83
85
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
84
|
-
"agentic-design": "^0.
|
|
86
|
+
"agentic-design": "^0.5.0",
|
|
85
87
|
"agentic-identity": "^0.8.0",
|
|
86
88
|
"agentic-realtime": "^0.4.2",
|
|
87
89
|
"yjs": "^13.6.0",
|
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
|
-
};
|