@fieldnotes/core 0.1.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 ADDED
@@ -0,0 +1,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 pressure support (Apple Pencil, Surface Pen)
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 while remaining fully interactive.
78
+
79
+ ## Adding Images
80
+
81
+ ```typescript
82
+ // Programmatic
83
+ viewport.addImage('https://example.com/photo.jpg', { x: 0, y: 0 });
84
+
85
+ // Drag & drop is handled automatically — drop images onto the canvas
86
+ ```
87
+
88
+ ## Camera Control
89
+
90
+ ```typescript
91
+ const { camera } = viewport;
92
+
93
+ camera.pan(100, 50); // pan by offset
94
+ camera.moveTo(0, 0); // jump to position
95
+ camera.setZoom(2); // set zoom level
96
+ camera.zoomAt(1.5, { x: 400, y: 300 }); // zoom toward screen point
97
+
98
+ const world = camera.screenToWorld({ x: e.clientX, y: e.clientY });
99
+ const screen = camera.worldToScreen({ x: 0, y: 0 });
100
+
101
+ camera.onChange(() => {
102
+ /* camera moved */
103
+ });
104
+ ```
105
+
106
+ ## Element Store
107
+
108
+ Direct access to canvas elements:
109
+
110
+ ```typescript
111
+ const { store } = viewport;
112
+
113
+ const all = store.getAll(); // sorted by zIndex
114
+ const el = store.getById('some-id');
115
+ const strokes = store.getElementsByType('stroke');
116
+
117
+ store.update('some-id', { locked: true });
118
+ store.remove('some-id');
119
+
120
+ store.on('add', (el) => console.log('added', el));
121
+ store.on('remove', (el) => console.log('removed', el));
122
+ store.on('update', ({ previous, current }) => {
123
+ /* ... */
124
+ });
125
+ ```
126
+
127
+ ## Undo / Redo
128
+
129
+ ```typescript
130
+ viewport.undo();
131
+ viewport.redo();
132
+
133
+ viewport.history.canUndo; // boolean
134
+ viewport.history.canRedo; // boolean
135
+ viewport.history.onChange(() => {
136
+ /* update UI */
137
+ });
138
+ ```
139
+
140
+ ## State Serialization
141
+
142
+ ```typescript
143
+ // Save
144
+ const json = viewport.exportJSON();
145
+ localStorage.setItem('canvas', json);
146
+
147
+ // Load
148
+ viewport.loadJSON(localStorage.getItem('canvas'));
149
+ ```
150
+
151
+ ## Tool Switching
152
+
153
+ ```typescript
154
+ viewport.toolManager.setTool('pencil', viewport.toolContext);
155
+ viewport.toolManager.setTool('hand', viewport.toolContext);
156
+
157
+ viewport.toolManager.onChange((toolName) => {
158
+ console.log('switched to', toolName);
159
+ });
160
+ ```
161
+
162
+ ## Custom Tools
163
+
164
+ Implement the `Tool` interface to create your own tools:
165
+
166
+ ```typescript
167
+ import type { Tool, ToolContext, PointerState } from '@fieldnotes/core';
168
+
169
+ const myTool: Tool = {
170
+ name: 'my-tool',
171
+
172
+ onPointerDown(state: PointerState, ctx: ToolContext) {
173
+ const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
174
+ // state.pressure is available for stylus input (0-1)
175
+ },
176
+
177
+ onPointerMove(state: PointerState, ctx: ToolContext) {
178
+ // called during drag
179
+ },
180
+
181
+ onPointerUp(state: PointerState, ctx: ToolContext) {
182
+ // finalize action
183
+ ctx.store.add(myElement);
184
+ ctx.requestRender();
185
+ },
186
+
187
+ // Optional
188
+ onActivate(ctx) {
189
+ ctx.setCursor?.('crosshair');
190
+ },
191
+ onDeactivate(ctx) {
192
+ ctx.setCursor?.('default');
193
+ },
194
+ renderOverlay(canvasCtx) {
195
+ /* draw preview on canvas */
196
+ },
197
+ };
198
+
199
+ viewport.toolManager.register(myTool);
200
+ viewport.toolManager.setTool('my-tool', viewport.toolContext);
201
+ ```
202
+
203
+ ## Configuration
204
+
205
+ ### Viewport Options
206
+
207
+ ```typescript
208
+ new Viewport(container, {
209
+ camera: {
210
+ minZoom: 0.1, // default: 0.1
211
+ maxZoom: 10, // default: 10
212
+ },
213
+ background: {
214
+ pattern: 'dots', // 'dots' | 'grid' | 'none' (default: 'dots')
215
+ spacing: 24, // grid spacing in px (default: 24)
216
+ color: '#d0d0d0', // dot/line color (default: '#d0d0d0')
217
+ },
218
+ });
219
+ ```
220
+
221
+ ### Tool Options
222
+
223
+ ```typescript
224
+ new PencilTool({ color: '#ff0000', width: 3 });
225
+ new EraserTool({ radius: 30 });
226
+ new ArrowTool({ color: '#333', width: 2 });
227
+ new NoteTool({ backgroundColor: '#fff9c4', size: { w: 200, h: 150 } });
228
+ new ImageTool({ size: { w: 400, h: 300 } });
229
+ ```
230
+
231
+ ## Element Types
232
+
233
+ All elements share a base shape:
234
+
235
+ ```typescript
236
+ interface BaseElement {
237
+ id: string;
238
+ type: string;
239
+ position: { x: number; y: number };
240
+ zIndex: number;
241
+ locked: boolean;
242
+ }
243
+ ```
244
+
245
+ | Type | Key Fields |
246
+ | -------- | -------------------------------------- |
247
+ | `stroke` | `points`, `color`, `width`, `opacity` |
248
+ | `note` | `size`, `text`, `backgroundColor` |
249
+ | `arrow` | `from`, `to`, `bend`, `color`, `width` |
250
+ | `image` | `size`, `src` |
251
+ | `html` | `size` |
252
+
253
+ ## Built-in Interactions
254
+
255
+ | Input | Action |
256
+ | -------------------- | --------------- |
257
+ | Scroll wheel | Zoom |
258
+ | Middle-click drag | Pan |
259
+ | Space + drag | Pan |
260
+ | Two-finger pinch | Zoom |
261
+ | Two-finger drag | Pan |
262
+ | Delete / Backspace | Remove selected |
263
+ | Ctrl+Z / Cmd+Z | Undo |
264
+ | Ctrl+Shift+Z / Cmd+Y | Redo |
265
+ | Double-click note | Edit text |
266
+
267
+ ## Browser Support
268
+
269
+ Works in all modern browsers supporting Pointer Events API and HTML5 Canvas.
270
+
271
+ ## License
272
+
273
+ MIT