@fieldnotes/core 0.1.2 → 0.2.1
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 +332 -273
- package/dist/index.cjs +249 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -34
- package/dist/index.d.ts +70 -34
- package/dist/index.js +248 -48
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,273 +1,332 @@
|
|
|
1
|
-
# @fieldnotes/core
|
|
2
|
-
|
|
3
|
-
A lightweight, framework-agnostic infinite canvas SDK for the web — with first-class support for embedding interactive HTML elements.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Infinite canvas** — pan, zoom, pinch-to-zoom
|
|
8
|
-
- **Freehand drawing** — pencil tool with
|
|
9
|
-
- **Sticky notes** — editable text notes with customizable colors
|
|
10
|
-
- **Arrows** — curved bezier arrows with draggable control points
|
|
11
|
-
- **Images** — drag & drop or programmatic placement
|
|
12
|
-
- **HTML embedding** — add any DOM element as a fully interactive canvas citizen
|
|
13
|
-
- **Select & multi-select** — click, drag box, move, resize
|
|
14
|
-
- **Undo / redo** — full history stack with configurable depth
|
|
15
|
-
- **State serialization** — export/import JSON snapshots
|
|
16
|
-
- **Touch & tablet** — Pointer Events API, pinch-to-zoom, two-finger pan, stylus pressure
|
|
17
|
-
- **Zero dependencies** — vanilla TypeScript, no framework required
|
|
18
|
-
- **Tree-shakeable** — ESM + CJS output
|
|
19
|
-
|
|
20
|
-
## Install
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npm install @fieldnotes/core
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Quick Start
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import {
|
|
30
|
-
Viewport,
|
|
31
|
-
HandTool,
|
|
32
|
-
SelectTool,
|
|
33
|
-
PencilTool,
|
|
34
|
-
EraserTool,
|
|
35
|
-
ArrowTool,
|
|
36
|
-
NoteTool,
|
|
37
|
-
} from '@fieldnotes/core';
|
|
38
|
-
|
|
39
|
-
// Mount on any container element
|
|
40
|
-
const viewport = new Viewport(document.getElementById('canvas'), {
|
|
41
|
-
background: { pattern: 'dots', spacing: 24 },
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Register tools
|
|
45
|
-
viewport.toolManager.register(new HandTool());
|
|
46
|
-
viewport.toolManager.register(new SelectTool());
|
|
47
|
-
viewport.toolManager.register(new PencilTool({ color: '#1a1a1a', width: 2 }));
|
|
48
|
-
viewport.toolManager.register(new EraserTool());
|
|
49
|
-
viewport.toolManager.register(new ArrowTool({ color: '#1a1a1a', width: 2 }));
|
|
50
|
-
viewport.toolManager.register(new NoteTool());
|
|
51
|
-
|
|
52
|
-
// Activate a tool
|
|
53
|
-
viewport.toolManager.setTool('select', viewport.toolContext);
|
|
54
|
-
|
|
55
|
-
// Clean up when done
|
|
56
|
-
viewport.destroy();
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Your container element needs a defined size (width/height). The canvas fills its container.
|
|
60
|
-
|
|
61
|
-
## Embedding HTML Elements
|
|
62
|
-
|
|
63
|
-
The main differentiator — embed any DOM node as a fully interactive canvas element:
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
const card = document.createElement('div');
|
|
67
|
-
card.innerHTML = '<h3>My Card</h3><button>Click me</button>';
|
|
68
|
-
|
|
69
|
-
// Buttons, inputs, links — everything works
|
|
70
|
-
card.querySelector('button').addEventListener('click', () => {
|
|
71
|
-
console.log('Clicked inside the canvas!');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const elementId = viewport.addHtmlElement(card, { x: 100, y: 200 }, { w: 250, h: 150 });
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
HTML elements pan, zoom, and resize with the canvas
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
camera
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
store
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
viewport.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
###
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
1
|
+
# @fieldnotes/core
|
|
2
|
+
|
|
3
|
+
A lightweight, framework-agnostic infinite canvas SDK for the web — with first-class support for embedding interactive HTML elements.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Infinite canvas** — pan, zoom, pinch-to-zoom
|
|
8
|
+
- **Freehand drawing** — pencil tool with stroke smoothing and pressure-sensitive width
|
|
9
|
+
- **Sticky notes** — editable text notes with customizable colors
|
|
10
|
+
- **Arrows** — curved bezier arrows with draggable control points
|
|
11
|
+
- **Images** — drag & drop or programmatic placement
|
|
12
|
+
- **HTML embedding** — add any DOM element as a fully interactive canvas citizen
|
|
13
|
+
- **Select & multi-select** — click, drag box, move, resize
|
|
14
|
+
- **Undo / redo** — full history stack with configurable depth
|
|
15
|
+
- **State serialization** — export/import JSON snapshots
|
|
16
|
+
- **Touch & tablet** — Pointer Events API, pinch-to-zoom, two-finger pan, stylus pressure
|
|
17
|
+
- **Zero dependencies** — vanilla TypeScript, no framework required
|
|
18
|
+
- **Tree-shakeable** — ESM + CJS output
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @fieldnotes/core
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import {
|
|
30
|
+
Viewport,
|
|
31
|
+
HandTool,
|
|
32
|
+
SelectTool,
|
|
33
|
+
PencilTool,
|
|
34
|
+
EraserTool,
|
|
35
|
+
ArrowTool,
|
|
36
|
+
NoteTool,
|
|
37
|
+
} from '@fieldnotes/core';
|
|
38
|
+
|
|
39
|
+
// Mount on any container element
|
|
40
|
+
const viewport = new Viewport(document.getElementById('canvas'), {
|
|
41
|
+
background: { pattern: 'dots', spacing: 24 },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Register tools
|
|
45
|
+
viewport.toolManager.register(new HandTool());
|
|
46
|
+
viewport.toolManager.register(new SelectTool());
|
|
47
|
+
viewport.toolManager.register(new PencilTool({ color: '#1a1a1a', width: 2 }));
|
|
48
|
+
viewport.toolManager.register(new EraserTool());
|
|
49
|
+
viewport.toolManager.register(new ArrowTool({ color: '#1a1a1a', width: 2 }));
|
|
50
|
+
viewport.toolManager.register(new NoteTool());
|
|
51
|
+
|
|
52
|
+
// Activate a tool
|
|
53
|
+
viewport.toolManager.setTool('select', viewport.toolContext);
|
|
54
|
+
|
|
55
|
+
// Clean up when done
|
|
56
|
+
viewport.destroy();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Your container element needs a defined size (width/height). The canvas fills its container.
|
|
60
|
+
|
|
61
|
+
## Embedding HTML Elements
|
|
62
|
+
|
|
63
|
+
The main differentiator — embed any DOM node as a fully interactive canvas element:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const card = document.createElement('div');
|
|
67
|
+
card.innerHTML = '<h3>My Card</h3><button>Click me</button>';
|
|
68
|
+
|
|
69
|
+
// Buttons, inputs, links — everything works
|
|
70
|
+
card.querySelector('button').addEventListener('click', () => {
|
|
71
|
+
console.log('Clicked inside the canvas!');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const elementId = viewport.addHtmlElement(card, { x: 100, y: 200 }, { w: 250, h: 150 });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
HTML elements pan, zoom, and resize with the canvas. They use a **two-mode interaction model**:
|
|
78
|
+
|
|
79
|
+
- **Default** — the element can be selected, dragged, and resized like any other element
|
|
80
|
+
- **Double-click** — enters interact mode, making buttons, inputs, and links work
|
|
81
|
+
- **Escape** or **click outside** — exits interact mode
|
|
82
|
+
|
|
83
|
+
You can also exit interact mode programmatically:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
viewport.stopInteracting();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Adding Images
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Programmatic
|
|
93
|
+
viewport.addImage('https://example.com/photo.jpg', { x: 0, y: 0 });
|
|
94
|
+
|
|
95
|
+
// Drag & drop is handled automatically — drop images onto the canvas
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Camera Control
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const { camera } = viewport;
|
|
102
|
+
|
|
103
|
+
camera.pan(100, 50); // pan by offset
|
|
104
|
+
camera.moveTo(0, 0); // jump to position
|
|
105
|
+
camera.setZoom(2); // set zoom level
|
|
106
|
+
camera.zoomAt(1.5, { x: 400, y: 300 }); // zoom toward screen point
|
|
107
|
+
|
|
108
|
+
const world = camera.screenToWorld({ x: e.clientX, y: e.clientY });
|
|
109
|
+
const screen = camera.worldToScreen({ x: 0, y: 0 });
|
|
110
|
+
|
|
111
|
+
camera.onChange(() => {
|
|
112
|
+
/* camera moved */
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Element Store
|
|
117
|
+
|
|
118
|
+
Direct access to canvas elements:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const { store } = viewport;
|
|
122
|
+
|
|
123
|
+
const all = store.getAll(); // sorted by zIndex
|
|
124
|
+
const el = store.getById('some-id');
|
|
125
|
+
const strokes = store.getElementsByType('stroke');
|
|
126
|
+
|
|
127
|
+
store.update('some-id', { locked: true });
|
|
128
|
+
store.remove('some-id');
|
|
129
|
+
|
|
130
|
+
store.on('add', (el) => console.log('added', el));
|
|
131
|
+
store.on('remove', (el) => console.log('removed', el));
|
|
132
|
+
store.on('update', ({ previous, current }) => {
|
|
133
|
+
/* ... */
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Undo / Redo
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
viewport.undo();
|
|
141
|
+
viewport.redo();
|
|
142
|
+
|
|
143
|
+
viewport.history.canUndo; // boolean
|
|
144
|
+
viewport.history.canRedo; // boolean
|
|
145
|
+
viewport.history.onChange(() => {
|
|
146
|
+
/* update UI */
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## State Serialization
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// Save
|
|
154
|
+
const json = viewport.exportJSON();
|
|
155
|
+
localStorage.setItem('canvas', json);
|
|
156
|
+
|
|
157
|
+
// Load
|
|
158
|
+
viewport.loadJSON(localStorage.getItem('canvas'));
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Tool Switching
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
viewport.toolManager.setTool('pencil', viewport.toolContext);
|
|
165
|
+
viewport.toolManager.setTool('hand', viewport.toolContext);
|
|
166
|
+
|
|
167
|
+
viewport.toolManager.onChange((toolName) => {
|
|
168
|
+
console.log('switched to', toolName);
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Changing Tool Options at Runtime
|
|
173
|
+
|
|
174
|
+
All drawing tools support `setOptions()` for changing color, width, and other settings without re-creating the tool:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Get a tool by name (type-safe with generics)
|
|
178
|
+
const pencil = viewport.toolManager.getTool<PencilTool>('pencil');
|
|
179
|
+
const arrow = viewport.toolManager.getTool<ArrowTool>('arrow');
|
|
180
|
+
const note = viewport.toolManager.getTool<NoteTool>('note');
|
|
181
|
+
|
|
182
|
+
// Change colors
|
|
183
|
+
pencil?.setOptions({ color: '#ff0000' });
|
|
184
|
+
arrow?.setOptions({ color: '#ff0000' });
|
|
185
|
+
note?.setOptions({ backgroundColor: '#e8f5e9' });
|
|
186
|
+
|
|
187
|
+
// Change stroke width
|
|
188
|
+
pencil?.setOptions({ width: 5 });
|
|
189
|
+
arrow?.setOptions({ width: 3 });
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Stroke Smoothing
|
|
193
|
+
|
|
194
|
+
The pencil tool automatically smooths freehand strokes using Ramer-Douglas-Peucker point simplification and Catmull-Rom curve fitting. You can control the smoothing tolerance:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
new PencilTool({
|
|
198
|
+
smoothing: 1.5, // default — higher = smoother, lower = more detail
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Or at runtime
|
|
202
|
+
pencil?.setOptions({ smoothing: 3 });
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Pressure-Sensitive Width
|
|
206
|
+
|
|
207
|
+
When using a stylus (Apple Pencil, Surface Pen), stroke width varies based on pressure automatically. The `width` option sets the **maximum** width at full pressure. Mouse input uses a default pressure of 0.5 for consistent-width strokes.
|
|
208
|
+
|
|
209
|
+
Stroke points include pressure data in the `StrokePoint` type:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
interface StrokePoint {
|
|
213
|
+
x: number;
|
|
214
|
+
y: number;
|
|
215
|
+
pressure: number; // 0-1
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Custom Tools
|
|
220
|
+
|
|
221
|
+
Implement the `Tool` interface to create your own tools:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import type { Tool, ToolContext, PointerState } from '@fieldnotes/core';
|
|
225
|
+
|
|
226
|
+
const myTool: Tool = {
|
|
227
|
+
name: 'my-tool',
|
|
228
|
+
|
|
229
|
+
onPointerDown(state: PointerState, ctx: ToolContext) {
|
|
230
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
231
|
+
// state.pressure is available for stylus input (0-1)
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
onPointerMove(state: PointerState, ctx: ToolContext) {
|
|
235
|
+
// called during drag
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
onPointerUp(state: PointerState, ctx: ToolContext) {
|
|
239
|
+
// finalize action
|
|
240
|
+
ctx.store.add(myElement);
|
|
241
|
+
ctx.requestRender();
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// Optional
|
|
245
|
+
onActivate(ctx) {
|
|
246
|
+
ctx.setCursor?.('crosshair');
|
|
247
|
+
},
|
|
248
|
+
onDeactivate(ctx) {
|
|
249
|
+
ctx.setCursor?.('default');
|
|
250
|
+
},
|
|
251
|
+
renderOverlay(canvasCtx) {
|
|
252
|
+
/* draw preview on canvas */
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
viewport.toolManager.register(myTool);
|
|
257
|
+
viewport.toolManager.setTool('my-tool', viewport.toolContext);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Configuration
|
|
261
|
+
|
|
262
|
+
### Viewport Options
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
new Viewport(container, {
|
|
266
|
+
camera: {
|
|
267
|
+
minZoom: 0.1, // default: 0.1
|
|
268
|
+
maxZoom: 10, // default: 10
|
|
269
|
+
},
|
|
270
|
+
background: {
|
|
271
|
+
pattern: 'dots', // 'dots' | 'grid' | 'none' (default: 'dots')
|
|
272
|
+
spacing: 24, // grid spacing in px (default: 24)
|
|
273
|
+
color: '#d0d0d0', // dot/line color (default: '#d0d0d0')
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Tool Options
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
new PencilTool({ color: '#ff0000', width: 3, smoothing: 1.5 });
|
|
282
|
+
new EraserTool({ radius: 30 });
|
|
283
|
+
new ArrowTool({ color: '#333', width: 2 });
|
|
284
|
+
new NoteTool({ backgroundColor: '#fff9c4', size: { w: 200, h: 150 } });
|
|
285
|
+
new ImageTool({ size: { w: 400, h: 300 } });
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Element Types
|
|
289
|
+
|
|
290
|
+
All elements share a base shape:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
interface BaseElement {
|
|
294
|
+
id: string;
|
|
295
|
+
type: string;
|
|
296
|
+
position: { x: number; y: number };
|
|
297
|
+
zIndex: number;
|
|
298
|
+
locked: boolean;
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
| Type | Key Fields |
|
|
303
|
+
| -------- | ---------------------------------------------------- |
|
|
304
|
+
| `stroke` | `points: StrokePoint[]`, `color`, `width`, `opacity` |
|
|
305
|
+
| `note` | `size`, `text`, `backgroundColor` |
|
|
306
|
+
| `arrow` | `from`, `to`, `bend`, `color`, `width` |
|
|
307
|
+
| `image` | `size`, `src` |
|
|
308
|
+
| `html` | `size` |
|
|
309
|
+
|
|
310
|
+
## Built-in Interactions
|
|
311
|
+
|
|
312
|
+
| Input | Action |
|
|
313
|
+
| -------------------- | ------------------- |
|
|
314
|
+
| Scroll wheel | Zoom |
|
|
315
|
+
| Middle-click drag | Pan |
|
|
316
|
+
| Space + drag | Pan |
|
|
317
|
+
| Two-finger pinch | Zoom |
|
|
318
|
+
| Two-finger drag | Pan |
|
|
319
|
+
| Delete / Backspace | Remove selected |
|
|
320
|
+
| Ctrl+Z / Cmd+Z | Undo |
|
|
321
|
+
| Ctrl+Shift+Z / Cmd+Y | Redo |
|
|
322
|
+
| Double-click note | Edit text |
|
|
323
|
+
| Double-click HTML | Enter interact mode |
|
|
324
|
+
| Escape | Exit interact mode |
|
|
325
|
+
|
|
326
|
+
## Browser Support
|
|
327
|
+
|
|
328
|
+
Works in all modern browsers supporting Pointer Events API and HTML5 Canvas.
|
|
329
|
+
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
MIT
|