agentic-canvas 0.11.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 CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  A shared whiteboard for diagrams, drawn by people and agents.
9
9
 
10
- > Status: 0.11.0. The board model, the agent operations, the exports, the CLI, the
10
+ > Status: 0.12.0. The board model, the agent operations, the exports, the CLI, the
11
11
  > MCP server, a small Worker API, and live co-editing work. The CLI, the MCP server,
12
12
  > and the SDK can join a live room. A remote client calls the Worker API. A small web UI, served by the Worker, is new in 0.4.0.
13
13
  > The package exports `agentic-canvas/ui`, a board view you can mount in your own app, new in 0.5.0.
@@ -111,6 +111,24 @@ There is one method per route: `me`, `listBoards`, `createBoard`, `readBoard`, `
111
111
  and status 0. The client never retries. A 503 means "try again", and the caller decides, because a
112
112
  repeated write can change a board twice. Pass `fetch` to use your own.
113
113
 
114
+ ## Run locally
115
+
116
+ ```sh
117
+ git clone https://github.com/pooriaarab/agentic-canvas
118
+ cd agentic-canvas
119
+ npm install && npm run dev
120
+ ```
121
+
122
+ You can also use `bun install && bun run dev`. The `dev` script calls npm, so npm must be installed. The scripts need macOS, Linux, or WSL. The `dev` script builds the package, then starts `wrangler dev` on <http://localhost:8793>. Wrangler keeps a local D1 database. The `dev` script turns off Wrangler telemetry. Set `WRANGLER_SEND_METRICS=false` when you run other wrangler commands.
123
+
124
+ ```sh
125
+ curl -i http://localhost:8793/health # 200
126
+ ```
127
+
128
+ The `dev` script sets `CANVAS_REGION=unknown`, which local Wrangler needs. The API answers 401 until you sign in. For local work, see the dev fallback in "Auth".
129
+
130
+ `npm run dev:smoke` runs the same check by itself. It starts the dev server. It waits for `/health` to answer 200. It stops the server. CI runs it as the "dev smoke" step. To use another port, change `8793` in the `dev` and `dev:smoke` scripts.
131
+
114
132
  ## Live demo
115
133
 
116
134
  A demo Worker runs on workers.dev: <https://agentic-canvas-demo.hello-fd1.workers.dev>. `GET /health` answers 200
@@ -240,7 +258,7 @@ What it does:
240
258
 
241
259
  - Lists boards, makes a board, and opens one. Empty, loading, and error states have a retry.
242
260
  - Draws shapes, connectors, text, and sticky notes. Select, move, resize, connect, and delete an
243
- item. Pan by dragging the background, zoom with the wheel or the toolbar.
261
+ item. Pan by dragging the background, zoom with the wheel or the zoom controls on the canvas.
244
262
  - Marks an item an agent wrote with an "Agent" tag and in its accessible name. The side panel shows
245
263
  who made the item and who last edited it.
246
264
  - Shows who is in the board, from the live room (`agentic-realtime`). Edits go over the HTTP API.
@@ -6,7 +6,7 @@ import {
6
6
 
7
7
  // src/version.ts
8
8
  import { createRequire } from "module";
9
- var VERSION = true ? "0.11.0" : readPackageVersion();
9
+ var VERSION = true ? "0.12.0" : readPackageVersion();
10
10
 
11
11
  // src/brand.ts
12
12
  var BRAND = {
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  operatorActor,
6
6
  runMcp,
7
7
  svgToPng
8
- } from "./chunk-EE725BJ3.js";
8
+ } from "./chunk-A4YK2SWM.js";
9
9
  import {
10
10
  openLocalStore
11
11
  } from "./chunk-NRLF6MGR.js";
package/dist/embed.js CHANGED
@@ -477,25 +477,29 @@ function BoardView({ id, client: remote, host, room: roomOptions, me, backHref }
477
477
  const empty = d.nodes.length === 0 && d.edges.length === 0;
478
478
  return /* @__PURE__ */ jsxs3("section", { className: "board", "aria-labelledby": "board-h", children: [
479
479
  /* @__PURE__ */ jsxs3("div", { className: "row", children: [
480
- /* @__PURE__ */ jsx3("h1", { id: "board-h", children: d.title || d.id }),
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
+ ] }),
481
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)" })
482
485
  ] }),
483
- /* @__PURE__ */ jsxs3(Toolbar, { label: "Board tools", children: [
484
- TOOLS.map((t) => /* @__PURE__ */ jsx3(ToolbarButton, { label: t.text, icon: t.icon, disabled: !editable, onClick: () => add(t.type) }, t.type)),
485
- /* @__PURE__ */ jsx3(ToolbarSeparator, {}),
486
- /* @__PURE__ */ jsx3(ToolbarButton, { label: "Zoom out", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 10h12" })), onClick: () => zoom(1 / 1.25) }),
487
- /* @__PURE__ */ jsx3(ToolbarButton, { label: "Zoom in", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 10h12M10 4v12" })), onClick: () => zoom(1.25) }),
488
- /* @__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 })) }),
489
- /* @__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 })) }),
490
- /* @__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 })) }),
491
- /* @__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 })) }),
492
- /* @__PURE__ */ jsx3(ToolbarButton, { label: "Reset view", icon: icon(/* @__PURE__ */ jsx3("path", { d: "M4 4h5M4 4v5M16 16h-5M16 16v-5" })), onClick: () => setView(HOME) })
493
- ] }),
494
- /* @__PURE__ */ jsx3("p", { className: "note", role: "status", children: editable ? status : "" }),
495
486
  !editable && /* @__PURE__ */ jsx3("p", { className: "note", children: host ? "This board is read only." : "This token can read the board but not change it." }),
496
- /* @__PURE__ */ jsxs3("div", { className: "stage", ref: canvasBox, children: [
497
- /* @__PURE__ */ jsx3(Canvas, { data: d, selected, editable, view, onView: setView, onSelect: setSelected, onMove: move, onResize: resize, onDelete: remove }),
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
+ /* @__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
+ ] }),
499
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")
500
504
  ] })
501
505
  ] });
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  operatorActor,
7
7
  runMcp,
8
8
  svgToPng
9
- } from "./chunk-EE725BJ3.js";
9
+ } from "./chunk-A4YK2SWM.js";
10
10
  import {
11
11
  createFileStore,
12
12
  openLocalStore
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-3); }
9
- .stage { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 16rem; gap: var(--agd-space-3); height: 65vh; min-height: 22rem; }
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.5; }
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: 2; fill: none; pointer-events: none; }
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-sunken); stroke-width: 4px; pointer-events: none; }
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 16rem 0 0; display: grid; place-items: center; pointer-events: none; }
32
- @media (max-width: 48rem) { .empty-overlay { inset: 0; bottom: auto; height: 55vh; } }
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-canvas",
3
- "version": "0.11.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.4.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",