claude-canvas 1.0.0 → 1.0.2
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 +599 -0
- package/dist/bin/claude-canvas.js +38 -1
- package/dist/client/assets/index-DAnVr01G.js +1777 -0
- package/dist/client/assets/index-svMNxi11.css +2 -0
- package/dist/client/favicon.svg +12 -0
- package/dist/client/index.html +3 -2
- package/package.json +7 -3
- package/scripts/postinstall.js +126 -0
- package/src/skill/claude-canvas/SKILL.md +310 -0
- package/dist/client/assets/index-CotVKMmB.css +0 -2
- package/dist/client/assets/index-aLa6sjSl.js +0 -487
package/README.md
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/claude_canvas_art_2.png" alt="claude-canvas" width="600" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">claude-canvas</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>A visual canvas for Claude Code — instead of asking questions in the terminal, Claude can draw diagrams, wireframes, and mockups on a shared canvas and collect visual feedback from the user.</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/claude-canvas"><img src="https://img.shields.io/npm/v/claude-canvas?style=for-the-badge&logo=npm&logoColor=white&color=CB3837" alt="npm version" /></a>
|
|
13
|
+
<a href="https://github.com/uditalias/claude-canvas/blob/main/LICENSE"><img src="https://img.shields.io/github/license/uditalias/claude-canvas?style=for-the-badge&color=blue" alt="License" /></a>
|
|
14
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js" /></a>
|
|
15
|
+
<a href="https://www.typescriptlang.org"><img src="https://img.shields.io/badge/typescript-5.4-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" /></a>
|
|
16
|
+
<a href="https://github.com/uditalias/claude-canvas/issues"><img src="https://img.shields.io/github/issues/uditalias/claude-canvas?style=for-the-badge&logo=github&color=orange" alt="Issues" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="#installation">Installation</a> •
|
|
21
|
+
<a href="#quick-start">Quick Start</a> •
|
|
22
|
+
<a href="#cli-reference">CLI Reference</a> •
|
|
23
|
+
<a href="#visual-qa">Visual Q&A</a> •
|
|
24
|
+
<a href="#interactive-canvas">Interactive Canvas</a> •
|
|
25
|
+
<a href="#claude-code-skill">Claude Code Skill</a> •
|
|
26
|
+
<a href="#architecture">Architecture</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What is claude-canvas?
|
|
32
|
+
|
|
33
|
+
**claude-canvas** gives [Claude Code](https://claude.ai/code) a visual canvas. It runs a local server that opens a browser-based drawing surface where Claude can send draw commands (shapes, arrows, text, freehand paths) via CLI or HTTP API, and users can draw interactively too. Communication happens in real-time over WebSocket.
|
|
34
|
+
|
|
35
|
+
It also serves as a **visual Q&A tool** — Claude can send structured questions alongside canvas drawings, and users answer by picking options, typing text, or drawing directly on the canvas.
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<img src="docs/screenshots/ui-drawing.png" alt="claude-canvas UI — architecture diagram with toolbar and canvas" width="700" />
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
### Key Features
|
|
42
|
+
|
|
43
|
+
- **Sketchy hand-drawn aesthetic** — powered by [Rough.js](https://roughjs.com), all shapes render with a natural, whiteboard feel
|
|
44
|
+
- **Bidirectional drawing** — Claude draws via CLI/API, users draw interactively in the browser
|
|
45
|
+
- **Visual Q&A system** — ask structured questions with per-question canvas drawings, collect answers programmatically
|
|
46
|
+
- **Multiple fill styles** — hachure, solid, zigzag, cross-hatch, dots, dashed, and wireframe outlines
|
|
47
|
+
- **Session management** — run multiple isolated canvas sessions simultaneously
|
|
48
|
+
- **Real-time sync** — WebSocket-powered instant updates between CLI and browser
|
|
49
|
+
- **Export options** — save as PNG, SVG, or JSON
|
|
50
|
+
- **Claude Code skill** — install the skill so Claude automatically knows when and how to use the canvas
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="docs/screenshots/fill-styles.png" alt="Fill styles gallery — hachure, solid, zigzag, cross-hatch, dots" width="700" />
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
### How It Works
|
|
57
|
+
|
|
58
|
+
1. **`claude-canvas start`** launches a local Express + WebSocket server and opens a browser tab
|
|
59
|
+
2. **Claude Code** sends draw commands and questions via the CLI (which hits the HTTP API)
|
|
60
|
+
3. The **server broadcasts** commands to the browser over WebSocket in real-time
|
|
61
|
+
4. **Users interact** directly on the canvas — drawing, answering questions, or annotating Claude's work
|
|
62
|
+
5. **`claude-canvas screenshot`** captures the canvas state and returns answers to any pending questions
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
### Via npm (recommended)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g claude-canvas
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### From source
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/uditalias/claude-canvas.git
|
|
78
|
+
cd claude-canvas
|
|
79
|
+
npm install
|
|
80
|
+
npm run build
|
|
81
|
+
npm link # makes `claude-canvas` available globally
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Requirements
|
|
85
|
+
|
|
86
|
+
- **Node.js** >= 18
|
|
87
|
+
- A modern browser (Chrome, Firefox, Safari, Edge)
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Quick Start
|
|
92
|
+
|
|
93
|
+
**1. Start a canvas session:**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
claude-canvas start
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This opens a browser tab with a fresh canvas and returns session info:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{"sessionId": "a1b2c3d4", "port": 7890, "url": "http://127.0.0.1:7890", "pid": 1234}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**2. Draw something:**
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
claude-canvas draw '{"commands": [
|
|
109
|
+
{"type": "rect", "x": 50, "y": 50, "width": 200, "height": 100, "label": "Frontend"},
|
|
110
|
+
{"type": "rect", "x": 350, "y": 50, "width": 200, "height": 100, "label": "Backend"},
|
|
111
|
+
{"type": "arrow", "x1": 250, "y1": 100, "x2": 350, "y2": 100, "label": "API"}
|
|
112
|
+
]}'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**3. Take a screenshot:**
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
claude-canvas screenshot
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{"ok": true, "path": "/tmp/claude-canvas/canvas-123.png", "answers": []}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**4. Stop the session when done:**
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
claude-canvas stop --all
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## CLI Reference
|
|
134
|
+
|
|
135
|
+
All commands accept `-s, --session <id>`. You can omit it when only one session is running.
|
|
136
|
+
|
|
137
|
+
### Session Management
|
|
138
|
+
|
|
139
|
+
| Command | Description |
|
|
140
|
+
|---------|-------------|
|
|
141
|
+
| `claude-canvas start` | Start a new canvas session (opens browser) |
|
|
142
|
+
| `claude-canvas start -p 8080` | Start on a specific port |
|
|
143
|
+
| `claude-canvas stop -s <id>` | Stop a specific session |
|
|
144
|
+
| `claude-canvas stop --all` | Stop all running sessions |
|
|
145
|
+
|
|
146
|
+
### Drawing
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Send draw commands as JSON
|
|
150
|
+
claude-canvas draw '{"commands": [...]}'
|
|
151
|
+
|
|
152
|
+
# Read from stdin (useful for large payloads)
|
|
153
|
+
echo '{"commands": [...]}' | claude-canvas draw -
|
|
154
|
+
|
|
155
|
+
# Render instantly without animation
|
|
156
|
+
claude-canvas draw --no-animate '{"commands": [...]}'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Canvas Operations
|
|
160
|
+
|
|
161
|
+
| Command | Description |
|
|
162
|
+
|---------|-------------|
|
|
163
|
+
| `claude-canvas clear` | Clear all objects from the canvas |
|
|
164
|
+
| `claude-canvas clear --layer claude` | Clear only Claude's objects (keep user drawings) |
|
|
165
|
+
| `claude-canvas screenshot` | Capture canvas as PNG and collect Q&A answers |
|
|
166
|
+
| `claude-canvas export -f png` | Export as PNG |
|
|
167
|
+
| `claude-canvas export -f svg` | Export as SVG |
|
|
168
|
+
| `claude-canvas export -f json` | Export as JSON |
|
|
169
|
+
| `claude-canvas export -f png --labels` | Export with shape labels included |
|
|
170
|
+
|
|
171
|
+
<details>
|
|
172
|
+
<summary><strong>DrawCommand Types</strong> (click to expand)</summary>
|
|
173
|
+
|
|
174
|
+
#### Shapes
|
|
175
|
+
|
|
176
|
+
All support optional `label`, `color`, `opacity`, and `fillStyle`:
|
|
177
|
+
|
|
178
|
+
```jsonc
|
|
179
|
+
// Rectangle
|
|
180
|
+
{"type": "rect", "x": 50, "y": 50, "width": 200, "height": 120, "label": "Header"}
|
|
181
|
+
|
|
182
|
+
// Circle
|
|
183
|
+
{"type": "circle", "x": 200, "y": 200, "radius": 60, "label": "Node"}
|
|
184
|
+
|
|
185
|
+
// Ellipse
|
|
186
|
+
{"type": "ellipse", "x": 300, "y": 150, "width": 180, "height": 100}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Lines and Arrows
|
|
190
|
+
|
|
191
|
+
```jsonc
|
|
192
|
+
// Line
|
|
193
|
+
{"type": "line", "x1": 100, "y1": 100, "x2": 300, "y2": 100}
|
|
194
|
+
|
|
195
|
+
// Arrow (with directional head)
|
|
196
|
+
{"type": "arrow", "x1": 100, "y1": 200, "x2": 300, "y2": 200, "label": "flow"}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### Text
|
|
200
|
+
|
|
201
|
+
```jsonc
|
|
202
|
+
// textAlign: "left" | "center" | "right"
|
|
203
|
+
{"type": "text", "x": 200, "y": 50, "content": "Title", "fontSize": 24, "textAlign": "center"}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Freehand
|
|
207
|
+
|
|
208
|
+
```jsonc
|
|
209
|
+
{"type": "freehand", "points": [[10, 10], [50, 30], [90, 10], [130, 30]]}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### Groups and Connectors
|
|
213
|
+
|
|
214
|
+
For structured flowcharts:
|
|
215
|
+
|
|
216
|
+
```jsonc
|
|
217
|
+
// Group: bundle shapes under an ID for connectors
|
|
218
|
+
{"type": "group", "id": "box-a", "commands": [
|
|
219
|
+
{"type": "rect", "x": 200, "y": 30, "width": 140, "height": 60},
|
|
220
|
+
{"type": "text", "x": 270, "y": 50, "content": "Start", "textAlign": "center"}
|
|
221
|
+
]}
|
|
222
|
+
|
|
223
|
+
// Connector: auto-routes between group edges
|
|
224
|
+
{"type": "connector", "from": "box-a", "to": "box-b", "label": "next"}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
</details>
|
|
228
|
+
|
|
229
|
+
### Fill Styles
|
|
230
|
+
|
|
231
|
+
Shapes default to `"hachure"`. Set `fillStyle` on any shape:
|
|
232
|
+
|
|
233
|
+
| Style | Description |
|
|
234
|
+
|-------|-------------|
|
|
235
|
+
| `hachure` | Hand-drawn diagonal lines (default) |
|
|
236
|
+
| `solid` | Solid fill |
|
|
237
|
+
| `zigzag` | Zigzag pattern |
|
|
238
|
+
| `cross-hatch` | Cross-hatched lines |
|
|
239
|
+
| `dots` | Dotted pattern |
|
|
240
|
+
| `dashed` | Dashed lines |
|
|
241
|
+
| `zigzag-line` | Zigzag line pattern |
|
|
242
|
+
| `none` | No fill (wireframe outline only) |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Visual Q&A
|
|
247
|
+
|
|
248
|
+
The Q&A system lets Claude send structured questions with visual context. A floating panel appears in the browser where users can answer by clicking options, typing text, or drawing.
|
|
249
|
+
|
|
250
|
+
<p align="center">
|
|
251
|
+
<img src="docs/screenshots/ui-ask.png" alt="Visual Q&A — question panel with layout options" width="700" />
|
|
252
|
+
</p>
|
|
253
|
+
|
|
254
|
+
Users select answers via interactive pill buttons. Selected answers are highlighted:
|
|
255
|
+
|
|
256
|
+
<p align="center">
|
|
257
|
+
<img src="docs/screenshots/ui-ask-answered.png" alt="Visual Q&A — answer selected" width="700" />
|
|
258
|
+
</p>
|
|
259
|
+
|
|
260
|
+
### Sending Questions
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
claude-canvas ask '{"questions": [
|
|
264
|
+
{
|
|
265
|
+
"id": "q1",
|
|
266
|
+
"text": "Which layout do you prefer?",
|
|
267
|
+
"type": "single",
|
|
268
|
+
"options": ["Layout A", "Layout B", "Layout C"],
|
|
269
|
+
"commands": [
|
|
270
|
+
{"type": "rect", "x": 80, "y": 80, "width": 200, "height": 150, "label": "Layout A"},
|
|
271
|
+
{"type": "rect", "x": 350, "y": 80, "width": 200, "height": 150, "label": "Layout B"}
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"id": "q2",
|
|
276
|
+
"text": "What should the title be?",
|
|
277
|
+
"type": "text"
|
|
278
|
+
}
|
|
279
|
+
]}'
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Question Types
|
|
283
|
+
|
|
284
|
+
| Type | Description | User interaction | Answer format |
|
|
285
|
+
|------|------------|------------------|---------------|
|
|
286
|
+
| `single` | Pick one option | Radio-style pill buttons | `"value": "Option A"` |
|
|
287
|
+
| `multi` | Pick multiple options | Toggle pill buttons | `"value": ["Option A", "Option C"]` |
|
|
288
|
+
| `text` | Free text input | Text field | `"value": "user's text"` |
|
|
289
|
+
| `canvas` | Draw on canvas | Freeform drawing | `"value": "see canvas"` + snapshot PNG |
|
|
290
|
+
|
|
291
|
+
### Collecting Answers
|
|
292
|
+
|
|
293
|
+
After sending questions, call `screenshot` to retrieve answers:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
claude-canvas screenshot
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"ok": true,
|
|
302
|
+
"path": "/tmp/claude-canvas/canvas-123.png",
|
|
303
|
+
"answers": [
|
|
304
|
+
{"questionId": "q1", "value": "Layout A"},
|
|
305
|
+
{"questionId": "q2", "value": "My Custom Title"}
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
For `canvas`-type questions, Claude draws a diagram and the user responds by drawing directly on the canvas. The answer includes a snapshot of what the user drew:
|
|
311
|
+
|
|
312
|
+
<p align="center">
|
|
313
|
+
<img src="docs/screenshots/ui-canvas-answer.png" alt="Canvas Q&A — Claude draws a wireframe, user annotates with freehand drawings" width="700" />
|
|
314
|
+
</p>
|
|
315
|
+
|
|
316
|
+
```json
|
|
317
|
+
{"questionId": "q3", "value": "see canvas", "canvasSnapshot": "/tmp/claude-canvas/canvas-q3-456.png"}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Interactive Canvas
|
|
323
|
+
|
|
324
|
+
The browser canvas is a full interactive drawing surface, not just a display. Users can draw alongside Claude's shapes in real-time.
|
|
325
|
+
|
|
326
|
+
### Drawing Tools
|
|
327
|
+
|
|
328
|
+
The toolbar provides these drawing tools:
|
|
329
|
+
|
|
330
|
+
| Tool | Description |
|
|
331
|
+
|------|-------------|
|
|
332
|
+
| Rectangle | Draw rectangles with optional fill |
|
|
333
|
+
| Circle | Draw circles |
|
|
334
|
+
| Line | Draw straight lines |
|
|
335
|
+
| Arrow | Draw directional arrows |
|
|
336
|
+
| Freehand | Freeform pencil drawing |
|
|
337
|
+
| Text | Click to place text |
|
|
338
|
+
| Paint | Brush painting with adjustable size |
|
|
339
|
+
|
|
340
|
+
### Canvas Features
|
|
341
|
+
|
|
342
|
+
- **Zoom & Pan** — scroll to zoom, drag to pan the canvas
|
|
343
|
+
- **Undo/Redo** — full history support (up to 50 states)
|
|
344
|
+
- **Snap Guides** — alignment guides appear when moving objects near other objects
|
|
345
|
+
- **Context Menu** — right-click any shape to change color, fill, opacity, label, lock, or layer order
|
|
346
|
+
- **Color Palette** — soft muted color presets with custom color picker
|
|
347
|
+
- **Brush Size** — adjustable size for paint and freehand tools
|
|
348
|
+
- **Dark Mode** — respects system theme preference
|
|
349
|
+
- **Keyboard Shortcuts** — quick tool switching via keyboard
|
|
350
|
+
|
|
351
|
+
### Layer System
|
|
352
|
+
|
|
353
|
+
Objects have a layer property:
|
|
354
|
+
- **`user`** — shapes drawn interactively in the browser
|
|
355
|
+
- **`claude`** — shapes drawn via the CLI/API
|
|
356
|
+
|
|
357
|
+
Use `claude-canvas clear --layer claude` to remove Claude's drawings without affecting user drawings.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Claude Code Skill
|
|
362
|
+
|
|
363
|
+
Install the included skill so Claude Code automatically knows when and how to use the canvas.
|
|
364
|
+
|
|
365
|
+
### Installation
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
cp -r src/skill/claude-canvas ~/.claude/skills/
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Or if installed globally via npm:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
cp -r $(npm root -g)/claude-canvas/src/skill/claude-canvas ~/.claude/skills/
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### What the Skill Does
|
|
378
|
+
|
|
379
|
+
Once installed, Claude Code will automatically use the canvas when it makes sense — for example:
|
|
380
|
+
|
|
381
|
+
- Drawing architecture diagrams during system design discussions
|
|
382
|
+
- Sketching UI wireframes when discussing layouts
|
|
383
|
+
- Creating flowcharts to explain processes
|
|
384
|
+
- Presenting visual options and asking for your preference via Q&A
|
|
385
|
+
|
|
386
|
+
You don't need to explicitly tell Claude to use the canvas. The skill teaches Claude when the canvas is the right tool for the job.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Architecture
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
┌─────────────────┐
|
|
394
|
+
│ Claude Code │
|
|
395
|
+
│ (CLI / Skill) │
|
|
396
|
+
└────────┬────────┘
|
|
397
|
+
│ HTTP API
|
|
398
|
+
┌────────▼────────┐
|
|
399
|
+
│ Express Server │
|
|
400
|
+
│ + WebSocket │
|
|
401
|
+
└────────┬────────┘
|
|
402
|
+
│ WebSocket
|
|
403
|
+
┌────────▼────────┐
|
|
404
|
+
│ Browser Canvas │
|
|
405
|
+
│ React + Fabric │
|
|
406
|
+
└─────────────────┘
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
<p align="center">
|
|
410
|
+
<img src="docs/screenshots/architecture-diagram.png" alt="Architecture diagram drawn on claude-canvas" width="700" />
|
|
411
|
+
</p>
|
|
412
|
+
|
|
413
|
+
<details>
|
|
414
|
+
<summary><strong>Project Structure</strong> (click to expand)</summary>
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
src/
|
|
418
|
+
├── bin/claude-canvas.ts # CLI entry point (Commander)
|
|
419
|
+
├── server/
|
|
420
|
+
│ ├── router.ts # REST API endpoints
|
|
421
|
+
│ ├── websocket.ts # WebSocket server
|
|
422
|
+
│ ├── state.ts # In-memory state & broadcast
|
|
423
|
+
│ └── process.ts # Session management (PID/port)
|
|
424
|
+
├── client/
|
|
425
|
+
│ ├── components/
|
|
426
|
+
│ │ ├── Canvas.tsx # Main canvas view
|
|
427
|
+
│ │ ├── Toolbox.tsx # Drawing toolbar
|
|
428
|
+
│ │ ├── QuestionPanel.tsx # Q&A floating panel
|
|
429
|
+
│ │ ├── Hud.tsx # Connection status & zoom
|
|
430
|
+
│ │ └── ContextMenu.tsx # Right-click context menu
|
|
431
|
+
│ ├── hooks/
|
|
432
|
+
│ │ ├── useCanvas.ts # Fabric.js canvas + rough.js rendering
|
|
433
|
+
│ │ ├── useDrawingTools.ts# Interactive drawing tools
|
|
434
|
+
│ │ ├── useWebSocket.ts # WS connection + auto-reconnect
|
|
435
|
+
│ │ ├── useToolState.ts # Tool selection + shortcuts
|
|
436
|
+
│ │ ├── useUndoRedo.ts # Canvas history (50 states)
|
|
437
|
+
│ │ ├── useSnapGuides.ts # Alignment snap guides
|
|
438
|
+
│ │ └── useQuestionPanel.ts# Q&A state management
|
|
439
|
+
│ └── lib/
|
|
440
|
+
│ └── rough-line.ts # Custom Fabric objects for rough.js
|
|
441
|
+
├── protocol/
|
|
442
|
+
│ └── types.ts # Shared types (DrawCommand, WsMessage, etc.)
|
|
443
|
+
└── skill/
|
|
444
|
+
└── claude-canvas.md # Claude Code skill definition
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
</details>
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Development
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Clone the repository
|
|
455
|
+
git clone https://github.com/uditalias/claude-canvas.git
|
|
456
|
+
cd claude-canvas
|
|
457
|
+
npm install
|
|
458
|
+
|
|
459
|
+
# Run in development mode (server with hot reload)
|
|
460
|
+
npm run dev
|
|
461
|
+
|
|
462
|
+
# Run client only (Vite dev server on :5173, proxies to :7890)
|
|
463
|
+
npm run dev:client
|
|
464
|
+
|
|
465
|
+
# Build everything
|
|
466
|
+
npm run build
|
|
467
|
+
|
|
468
|
+
# Run unit tests
|
|
469
|
+
npm test
|
|
470
|
+
|
|
471
|
+
# Run E2E tests (requires build first)
|
|
472
|
+
npm run build && npx playwright test
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Examples
|
|
478
|
+
|
|
479
|
+
<details>
|
|
480
|
+
<summary><strong>Architecture Diagram</strong></summary>
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
claude-canvas draw '{"commands": [
|
|
484
|
+
{"type": "text", "x": 400, "y": 40, "content": "System Architecture", "fontSize": 28, "textAlign": "center"},
|
|
485
|
+
{"type": "rect", "x": 50, "y": 100, "width": 180, "height": 80, "label": "Client App", "fillStyle": "hachure"},
|
|
486
|
+
{"type": "rect", "x": 310, "y": 100, "width": 180, "height": 80, "label": "API Gateway", "fillStyle": "solid"},
|
|
487
|
+
{"type": "rect", "x": 570, "y": 100, "width": 180, "height": 80, "label": "Database", "fillStyle": "dots"},
|
|
488
|
+
{"type": "arrow", "x1": 230, "y1": 140, "x2": 310, "y2": 140, "label": "REST"},
|
|
489
|
+
{"type": "arrow", "x1": 490, "y1": 140, "x2": 570, "y2": 140, "label": "SQL"}
|
|
490
|
+
]}'
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
</details>
|
|
494
|
+
|
|
495
|
+
<details>
|
|
496
|
+
<summary><strong>Wireframe Layout</strong></summary>
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
claude-canvas draw '{"commands": [
|
|
500
|
+
{"type": "rect", "x": 50, "y": 30, "width": 500, "height": 60, "label": "Navigation", "fillStyle": "none"},
|
|
501
|
+
{"type": "rect", "x": 50, "y": 110, "width": 150, "height": 300, "label": "Sidebar", "fillStyle": "none"},
|
|
502
|
+
{"type": "rect", "x": 220, "y": 110, "width": 330, "height": 300, "label": "Main Content", "fillStyle": "none"}
|
|
503
|
+
]}'
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
</details>
|
|
507
|
+
|
|
508
|
+
<details>
|
|
509
|
+
<summary><strong>Flowchart with Connectors</strong></summary>
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
claude-canvas draw '{"commands": [
|
|
513
|
+
{"type": "group", "id": "start", "commands": [
|
|
514
|
+
{"type": "rect", "x": 200, "y": 30, "width": 140, "height": 60},
|
|
515
|
+
{"type": "text", "x": 270, "y": 50, "content": "Start", "textAlign": "center"}
|
|
516
|
+
]},
|
|
517
|
+
{"type": "group", "id": "process", "commands": [
|
|
518
|
+
{"type": "rect", "x": 200, "y": 150, "width": 140, "height": 60},
|
|
519
|
+
{"type": "text", "x": 270, "y": 170, "content": "Process", "textAlign": "center"}
|
|
520
|
+
]},
|
|
521
|
+
{"type": "group", "id": "end", "commands": [
|
|
522
|
+
{"type": "rect", "x": 200, "y": 270, "width": 140, "height": 60},
|
|
523
|
+
{"type": "text", "x": 270, "y": 290, "content": "End", "textAlign": "center"}
|
|
524
|
+
]},
|
|
525
|
+
{"type": "connector", "from": "start", "to": "process"},
|
|
526
|
+
{"type": "connector", "from": "process", "to": "end"}
|
|
527
|
+
]}'
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
</details>
|
|
531
|
+
|
|
532
|
+
<details>
|
|
533
|
+
<summary><strong>Visual Decision Making (Q&A)</strong></summary>
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
claude-canvas ask '{"questions": [
|
|
537
|
+
{
|
|
538
|
+
"id": "theme",
|
|
539
|
+
"text": "Which color theme should we use?",
|
|
540
|
+
"type": "single",
|
|
541
|
+
"options": ["Blue Ocean", "Forest Green", "Sunset Purple"],
|
|
542
|
+
"commands": [
|
|
543
|
+
{"type": "circle", "x": 150, "y": 150, "radius": 50, "label": "Blue", "fillStyle": "solid"},
|
|
544
|
+
{"type": "circle", "x": 350, "y": 150, "radius": 50, "label": "Green", "fillStyle": "solid"},
|
|
545
|
+
{"type": "circle", "x": 550, "y": 150, "radius": 50, "label": "Purple", "fillStyle": "solid"}
|
|
546
|
+
]
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"id": "name",
|
|
550
|
+
"text": "What should we name this feature?",
|
|
551
|
+
"type": "text"
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
"id": "features",
|
|
555
|
+
"text": "Which features should we include?",
|
|
556
|
+
"type": "multi",
|
|
557
|
+
"options": ["Dark mode", "Animations", "Keyboard shortcuts", "Mobile support"]
|
|
558
|
+
}
|
|
559
|
+
]}'
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
</details>
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Tips
|
|
567
|
+
|
|
568
|
+
- The visible canvas area is roughly **1200 x 800 pixels** — place shapes within this range
|
|
569
|
+
- Use `label` on shapes for clarity — labels float above shapes as text overlays
|
|
570
|
+
- Use `textAlign: "center"` with text inside groups to center text within boxes
|
|
571
|
+
- For groups, place the text `x` at the center of the rect (`rect.x + rect.width / 2`)
|
|
572
|
+
- After drawing, call `screenshot` to capture and verify what the user sees
|
|
573
|
+
- Use `clear --layer claude` to remove Claude's drawings without erasing user drawings
|
|
574
|
+
- Connectors automatically route between group edges — just specify `from` and `to` group IDs
|
|
575
|
+
- Pass `color` as a hex string for custom colors: `"color": "#D4726A"`
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Contributing
|
|
580
|
+
|
|
581
|
+
Contributions are welcome! Please feel free to submit a [Pull Request](https://github.com/uditalias/claude-canvas/pulls).
|
|
582
|
+
|
|
583
|
+
1. Fork the repository
|
|
584
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
585
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
586
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
587
|
+
5. Open a Pull Request
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## License
|
|
592
|
+
|
|
593
|
+
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
<p align="center">
|
|
598
|
+
Built with ✨ by <a href="https://github.com/uditalias">Udi Talias</a>
|
|
599
|
+
</p>
|
|
@@ -3038,6 +3038,7 @@ var {
|
|
|
3038
3038
|
|
|
3039
3039
|
// src/bin/claude-canvas.ts
|
|
3040
3040
|
var http = __toESM(require("http"));
|
|
3041
|
+
var https = __toESM(require("https"));
|
|
3041
3042
|
|
|
3042
3043
|
// src/utils/port.ts
|
|
3043
3044
|
var net = __toESM(require("net"));
|
|
@@ -3181,7 +3182,7 @@ var import_node_path = require("node:path");
|
|
|
3181
3182
|
var import_node_url = require("node:url");
|
|
3182
3183
|
var import_meta = {};
|
|
3183
3184
|
function getVersion() {
|
|
3184
|
-
if (true) return "1.0.
|
|
3185
|
+
if (true) return "1.0.2";
|
|
3185
3186
|
try {
|
|
3186
3187
|
const dir = (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
3187
3188
|
const pkg = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.resolve)(dir, "../../package.json"), "utf-8"));
|
|
@@ -3302,7 +3303,43 @@ program2.command("export").description("Export the canvas as PNG, SVG, or JSON")
|
|
|
3302
3303
|
process.exit(1);
|
|
3303
3304
|
}
|
|
3304
3305
|
});
|
|
3306
|
+
program2.command("update").description("Check for updates and install the latest version").action(async () => {
|
|
3307
|
+
const currentVersion = getVersion();
|
|
3308
|
+
console.log(`Current version: ${currentVersion}`);
|
|
3309
|
+
console.log("Checking for updates...");
|
|
3310
|
+
try {
|
|
3311
|
+
const latest = await fetchLatestVersion();
|
|
3312
|
+
if (latest === currentVersion) {
|
|
3313
|
+
console.log("You are already on the latest version.");
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
console.log(`New version available: ${latest}`);
|
|
3317
|
+
console.log("Updating...");
|
|
3318
|
+
const { execSync } = await import("child_process");
|
|
3319
|
+
execSync("npm install -g claude-canvas@latest", { stdio: "inherit" });
|
|
3320
|
+
console.log(`Successfully updated to ${latest}`);
|
|
3321
|
+
} catch (err) {
|
|
3322
|
+
console.error("Update failed:", err.message);
|
|
3323
|
+
process.exit(1);
|
|
3324
|
+
}
|
|
3325
|
+
});
|
|
3305
3326
|
program2.parse();
|
|
3327
|
+
function fetchLatestVersion() {
|
|
3328
|
+
return new Promise((resolve3, reject) => {
|
|
3329
|
+
https.get("https://registry.npmjs.org/claude-canvas/latest", (res) => {
|
|
3330
|
+
let data = "";
|
|
3331
|
+
res.on("data", (chunk) => data += chunk);
|
|
3332
|
+
res.on("end", () => {
|
|
3333
|
+
try {
|
|
3334
|
+
const pkg = JSON.parse(data);
|
|
3335
|
+
resolve3(pkg.version);
|
|
3336
|
+
} catch {
|
|
3337
|
+
reject(new Error("Failed to parse npm registry response"));
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
}).on("error", reject);
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3306
3343
|
function httpGet(url) {
|
|
3307
3344
|
return new Promise((resolve3, reject) => {
|
|
3308
3345
|
http.get(url, (res) => {
|