agent-canvas 0.1.5 → 0.1.7
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/LICENSE +21 -0
- package/README.md +71 -0
- package/bin/agent-canvas.ts +26 -353
- package/bin/lib/commands/daemon.ts +51 -0
- package/bin/lib/commands/fetch.ts +20 -0
- package/bin/lib/commands/install.ts +48 -0
- package/bin/lib/commands/push.ts +66 -0
- package/bin/lib/commands/watch.ts +23 -0
- package/bin/lib/config.ts +10 -0
- package/bin/lib/daemon-lifecycle.ts +74 -0
- package/bin/lib/helpers.ts +78 -0
- package/daemon/build.ts +27 -61
- package/daemon/client/AnnotationProvider.tsx +2 -2
- package/daemon/client/AnnotationSidebar.tsx +199 -31
- package/daemon/client/App.tsx +4 -3
- package/daemon/client/FileBrowser.tsx +8 -7
- package/daemon/client/FileViewer.tsx +153 -16
- package/daemon/client/PlanRenderer.tsx +6 -3
- package/daemon/client/ResponsePreview.tsx +5 -116
- package/daemon/client/SessionSwitcher.tsx +21 -10
- package/daemon/client/compat/preact-all.ts +4 -0
- package/daemon/client/components/Markdown.tsx +44 -0
- package/daemon/client/components/ResponseNote.tsx +3 -8
- package/daemon/client/components/UserInput.tsx +2 -4
- package/daemon/client/components/index.ts +1 -0
- package/daemon/client/domDiff.ts +82 -2
- package/daemon/client/generateMarkdown.ts +46 -9
- package/daemon/client/highlightRange.ts +8 -6
- package/daemon/client/runtime.ts +2 -0
- package/daemon/client/styles.css +126 -0
- package/daemon/client/unifiedDiff.ts +155 -2
- package/daemon/client/utils.ts +10 -0
- package/daemon/dist/client.css +1 -1
- package/daemon/dist/client.js +83 -36
- package/daemon/dist/components.js +63 -5
- package/daemon/dist/index.html +5 -12
- package/daemon/dist/jsx-runtime.js +3 -0
- package/daemon/dist/preact-compat.js +1 -0
- package/daemon/src/compiler.ts +28 -2
- package/daemon/src/handlers/api.ts +150 -0
- package/daemon/src/handlers/files.ts +114 -0
- package/daemon/src/handlers/static.ts +61 -0
- package/daemon/src/handlers/utils.ts +14 -0
- package/daemon/src/router.ts +16 -0
- package/daemon/src/server.ts +28 -403
- package/daemon/src/session.ts +41 -23
- package/daemon/src/websocket.ts +83 -0
- package/package.json +3 -3
- package/skills/canvas/SKILL.md +32 -22
- package/skills/canvas/components.md +31 -0
- package/skills/canvas/flows.md +2 -2
- package/daemon/dist/jsx-dev-runtime-shim.js +0 -17
- package/daemon/dist/jsx-runtime-shim.js +0 -17
- package/daemon/dist/react-dom-client-shim.js +0 -4
- package/daemon/dist/react-dom-shim.js +0 -4
- package/daemon/dist/react-shim.js +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-canvas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Interactive visual canvas for Claude Code — rich annotatable documents in the browser",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-canvas": "bin/agent-canvas.js"
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"prepublishOnly": "bun daemon/build.ts"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"
|
|
25
|
-
"
|
|
24
|
+
"marked": "^17.0.4",
|
|
25
|
+
"preact": "^10.25.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@tailwindcss/cli": "4",
|
package/skills/canvas/SKILL.md
CHANGED
|
@@ -15,18 +15,18 @@ disable-model-invocation: true
|
|
|
15
15
|
|
|
16
16
|
Canvas opens a rich, annotatable document in the user's browser. You write JSX, the user reviews and annotates it like Google Docs, and feedback comes back as markdown. Use it whenever structured visual communication beats inline chat.
|
|
17
17
|
|
|
18
|
-
**Read the flow reference before starting**: Check
|
|
18
|
+
**Read the flow reference before starting**: Check `${CLAUDE_SKILL_DIR}/flows.md` to pick the right flow for your task. Each flow has a specific sequence of canvases with templates.
|
|
19
19
|
|
|
20
|
-
**Read the component reference as needed**: Check
|
|
20
|
+
**Read the component reference as needed**: Check `${CLAUDE_SKILL_DIR}/components.md` for the full component API.
|
|
21
21
|
|
|
22
22
|
## Core Mechanics
|
|
23
23
|
|
|
24
24
|
### Writing a Canvas
|
|
25
25
|
|
|
26
|
-
Use the **Write** tool to create a `.jsx` file in `.claude/agent-canvas/`:
|
|
26
|
+
Use the **Write** tool to create a `.jsx` file in `.claude/agent-canvas/${CLAUDE_SESSION_ID}/`:
|
|
27
27
|
|
|
28
28
|
```jsx
|
|
29
|
-
// Write to: .claude/agent-canvas/plan.jsx
|
|
29
|
+
// Write to: .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx
|
|
30
30
|
|
|
31
31
|
<Section title="Authentication Redesign">
|
|
32
32
|
A proposal to replace session-based auth with JWT tokens.
|
|
@@ -50,7 +50,7 @@ Components are auto-available — no imports needed. The file is a JSX fragment
|
|
|
50
50
|
After writing the file, push it to open the canvas in the browser:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
bunx agent-canvas push .claude/agent-canvas/plan.jsx --label "Implementation Plan"
|
|
53
|
+
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --session ${CLAUDE_SESSION_ID} --label "Implementation Plan"
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
This opens the canvas in the user's browser (first time) and exits immediately. The round label defaults to the filename if `--label` is omitted. **Always show the `browserUrl` from the push output to the user** so they can open the canvas manually if auto-open didn't work.
|
|
@@ -60,20 +60,30 @@ This opens the canvas in the user's browser (first time) and exits immediately.
|
|
|
60
60
|
After pushing, wait for the user to review and submit:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
bunx agent-canvas
|
|
63
|
+
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
This **blocks until the user submits feedback**, then prints feedback markdown to stdout. If feedback was already submitted
|
|
66
|
+
This **blocks until the user submits feedback**, then prints feedback markdown to stdout. If feedback was already submitted but not yet consumed, it returns immediately with the existing feedback.
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
**Timeout handling**: The watch command may time out if the user takes a while to review. If it times out, don't retry automatically — wait for the user to tell you they've submitted feedback, then run the watch command again (or use `fetch`) to retrieve it.
|
|
69
|
+
|
|
70
|
+
### Checking for feedback without blocking
|
|
71
|
+
|
|
72
|
+
Use `fetch` to check for feedback without waiting:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bunx agent-canvas fetch --session ${CLAUDE_SESSION_ID}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Returns immediately — prints feedback to stdout if available, otherwise produces no output. Use this to retry after a timeout or when the user tells you they've submitted.
|
|
69
79
|
|
|
70
80
|
### Iterating
|
|
71
81
|
|
|
72
82
|
Use the **Edit** tool to modify the existing JSX based on feedback — targeted edits, not full rewrites. Then push and wait again:
|
|
73
83
|
|
|
74
84
|
```bash
|
|
75
|
-
bunx agent-canvas push .claude/agent-canvas/plan.jsx --label "Implementation Plan (revised)"
|
|
76
|
-
bunx agent-canvas
|
|
85
|
+
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --session ${CLAUDE_SESSION_ID} --label "Implementation Plan (revised)"
|
|
86
|
+
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
77
87
|
```
|
|
78
88
|
|
|
79
89
|
Each push creates a new **round** — the user sees previous rounds and their feedback in the UI.
|
|
@@ -83,23 +93,23 @@ Each push creates a new **round** — the user sees previous rounds and their fe
|
|
|
83
93
|
Maintain separate files for different phases:
|
|
84
94
|
|
|
85
95
|
```bash
|
|
86
|
-
bunx agent-canvas push .claude/agent-canvas/discovery.jsx --label "Discovery"
|
|
87
|
-
bunx agent-canvas
|
|
96
|
+
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/discovery.jsx --session ${CLAUDE_SESSION_ID} --label "Discovery"
|
|
97
|
+
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
88
98
|
# ... process feedback ...
|
|
89
|
-
bunx agent-canvas push .claude/agent-canvas/requirements.jsx --label "Requirements"
|
|
90
|
-
bunx agent-canvas
|
|
99
|
+
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/requirements.jsx --session ${CLAUDE_SESSION_ID} --label "Requirements"
|
|
100
|
+
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
91
101
|
# ... process feedback ...
|
|
92
|
-
bunx agent-canvas push .claude/agent-canvas/plan.jsx --label "Implementation Plan"
|
|
93
|
-
bunx agent-canvas
|
|
102
|
+
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --session ${CLAUDE_SESSION_ID} --label "Implementation Plan"
|
|
103
|
+
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
94
104
|
```
|
|
95
105
|
|
|
96
106
|
### File Location
|
|
97
107
|
|
|
98
|
-
All canvas files go in `.claude/agent-canvas/` within the project root. Add `.claude/agent-canvas/` to `.gitignore` — these are ephemeral working files.
|
|
108
|
+
All canvas files go in `.claude/agent-canvas/${CLAUDE_SESSION_ID}/` within the project root. Add `.claude/agent-canvas/` to `.gitignore` — these are ephemeral working files.
|
|
99
109
|
|
|
100
110
|
## Choosing a Flow
|
|
101
111
|
|
|
102
|
-
Determine which flow fits before starting. See
|
|
112
|
+
Determine which flow fits before starting. See `${CLAUDE_SKILL_DIR}/flows.md` for full details with templates.
|
|
103
113
|
|
|
104
114
|
```
|
|
105
115
|
User wants something built/changed?
|
|
@@ -129,10 +139,10 @@ User wants to make a decision?
|
|
|
129
139
|
|
|
130
140
|
1. **Determine flow** from user intent
|
|
131
141
|
2. **Announce** briefly: "I'll start with discovery, then create a detailed plan."
|
|
132
|
-
3. **Write canvas JSX** with the Write tool to `.claude/agent-canvas/<name>.jsx`
|
|
133
|
-
4. **Push**: `bunx agent-canvas push .claude/agent-canvas/<name>.jsx --label "<Label>"`
|
|
142
|
+
3. **Write canvas JSX** with the Write tool to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/<name>.jsx`
|
|
143
|
+
4. **Push**: `bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/<name>.jsx --session ${CLAUDE_SESSION_ID} --label "<Label>"`
|
|
134
144
|
5. **Tell the user** the canvas is ready for review (they see it in their browser)
|
|
135
|
-
6. **Wait**: `bunx agent-canvas
|
|
145
|
+
6. **Wait**: `bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}` — this blocks until the user submits feedback
|
|
136
146
|
7. **Read feedback** — check for annotations, answers, added context files, approval
|
|
137
147
|
8. **Edit with the Edit tool and re-push + wait**, or advance to next phase
|
|
138
148
|
9. **After implementation**, push a summary canvas
|
|
@@ -165,7 +175,7 @@ Then interview deeply in each selected area across subsequent rounds. Or ask in
|
|
|
165
175
|
|
|
166
176
|
## Important Rules
|
|
167
177
|
|
|
168
|
-
- **Write** canvas files to `.claude/agent-canvas/` using the Write tool. Never use bash heredocs or cat.
|
|
178
|
+
- **Write** canvas files to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/` using the Write tool. Never use bash heredocs or cat.
|
|
169
179
|
- **Edit** canvas files using the Edit tool. Never rewrite entire files — make targeted edits.
|
|
170
180
|
- JSX can be a fragment (just tags) or a full module with `export default function Canvas()`.
|
|
171
181
|
- Every `<Item>` and interactive component needs a unique `id`.
|
|
@@ -100,6 +100,37 @@ Inline code block with syntax highlighting. For code that's part of the narrativ
|
|
|
100
100
|
**Props:**
|
|
101
101
|
- `language` (string) — syntax highlighting language
|
|
102
102
|
|
|
103
|
+
### `<Markdown>`
|
|
104
|
+
|
|
105
|
+
Renders markdown content with full GFM support (headings, lists, tables, code blocks, links, etc.). Two modes:
|
|
106
|
+
|
|
107
|
+
**Inline source** — markdown as children:
|
|
108
|
+
```jsx
|
|
109
|
+
<Markdown>{`
|
|
110
|
+
# Summary
|
|
111
|
+
|
|
112
|
+
This is **bold** and *italic*. Here's a list:
|
|
113
|
+
|
|
114
|
+
- First item
|
|
115
|
+
- Second item
|
|
116
|
+
- Nested item
|
|
117
|
+
|
|
118
|
+
\`\`\`typescript
|
|
119
|
+
const x = 42;
|
|
120
|
+
\`\`\`
|
|
121
|
+
`}</Markdown>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**File reference** — loads file content at compile time:
|
|
125
|
+
```jsx
|
|
126
|
+
<Markdown file="docs/architecture.md" />
|
|
127
|
+
<Markdown file="CHANGELOG.md" />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Props:**
|
|
131
|
+
- `file` (string) — path relative to project root. Content is resolved at compile time.
|
|
132
|
+
- Children (string) — inline markdown source. Used when `file` is not specified.
|
|
133
|
+
|
|
103
134
|
### `<ImageView src="string">`
|
|
104
135
|
|
|
105
136
|
Displays an image from the project directory.
|
package/skills/canvas/flows.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Each flow is a sequence of canvas rounds. Phases can be skipped or combined based on context.
|
|
4
4
|
|
|
5
|
-
**Tooling reminder**: Write all `.jsx` files using the **Write** tool to `.claude/agent-canvas/`. Edit them with the **Edit** tool. Push with `bunx agent-canvas push .claude/agent-canvas/<file>.jsx --label "<Label>"`, then `bunx agent-canvas
|
|
5
|
+
**Tooling reminder**: Write all `.jsx` files using the **Write** tool to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/`. Edit them with the **Edit** tool. Push with `bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/<file>.jsx --session ${CLAUDE_SESSION_ID} --label "<Label>"`, then `bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}` to block for feedback. The JSX blocks below show what to write — they are the file content, not bash commands.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ Each flow is a sequence of canvas rounds. Phases can be skipped or combined base
|
|
|
16
16
|
|
|
17
17
|
Goal: understand scope, constraints, and user expectations. Start broad, go deep.
|
|
18
18
|
|
|
19
|
-
Write to `.claude/agent-canvas/discovery.jsx`, then push and wait:
|
|
19
|
+
Write to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/discovery.jsx`, then push and wait:
|
|
20
20
|
|
|
21
21
|
```jsx
|
|
22
22
|
<Section title="Discovery: [Feature Name]">
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const R = window.React;
|
|
3
|
-
export const Fragment = R.Fragment;
|
|
4
|
-
function _jsx(type, props, key) {
|
|
5
|
-
if (props && 'children' in props) {
|
|
6
|
-
const { children, ...rest } = props;
|
|
7
|
-
if (key !== undefined && key !== null) rest.key = key;
|
|
8
|
-
return Array.isArray(children)
|
|
9
|
-
? R.createElement(type, rest, ...children)
|
|
10
|
-
: R.createElement(type, rest, children);
|
|
11
|
-
}
|
|
12
|
-
if (key !== undefined && key !== null) props = { ...props, key };
|
|
13
|
-
return R.createElement(type, props);
|
|
14
|
-
}
|
|
15
|
-
export const jsx = _jsx;
|
|
16
|
-
export const jsxs = _jsx;
|
|
17
|
-
export function jsxDEV(type, props, key) { return _jsx(type, props, key); }
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const R = window.React;
|
|
3
|
-
export const Fragment = R.Fragment;
|
|
4
|
-
function _jsx(type, props, key) {
|
|
5
|
-
if (props && 'children' in props) {
|
|
6
|
-
const { children, ...rest } = props;
|
|
7
|
-
if (key !== undefined && key !== null) rest.key = key;
|
|
8
|
-
return Array.isArray(children)
|
|
9
|
-
? R.createElement(type, rest, ...children)
|
|
10
|
-
: R.createElement(type, rest, children);
|
|
11
|
-
}
|
|
12
|
-
if (key !== undefined && key !== null) props = { ...props, key };
|
|
13
|
-
return R.createElement(type, props);
|
|
14
|
-
}
|
|
15
|
-
export const jsx = _jsx;
|
|
16
|
-
export const jsxs = _jsx;
|
|
17
|
-
export function jsxDEV(type, props, key) { return _jsx(type, props, key); }
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const R = window.React;
|
|
3
|
-
export default R;
|
|
4
|
-
export const { useState, useEffect, useRef, useCallback, useContext, useMemo, useReducer, createContext, createElement, Fragment, Children, cloneElement, isValidElement, memo, forwardRef, lazy, Suspense, startTransition, useTransition, useDeferredValue, useId, useSyncExternalStore, useInsertionEffect, useImperativeHandle, useLayoutEffect, useDebugValue, Component, PureComponent } = R;
|