@fieldnotes/core 0.6.0 → 0.6.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 CHANGED
@@ -7,12 +7,15 @@ A lightweight, framework-agnostic infinite canvas SDK for the web — with first
7
7
  - **Infinite canvas** — pan, zoom, pinch-to-zoom
8
8
  - **Freehand drawing** — pencil tool with stroke smoothing and pressure-sensitive width
9
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
10
+ - **Arrows** — curved bezier arrows with element binding
11
+ - **Shapes** — rectangles, ellipses with fill and stroke
12
+ - **Text** — standalone text elements with font size and alignment
13
+ - **Images** — drag & drop or programmatic placement (canvas-rendered for proper layer ordering)
12
14
  - **HTML embedding** — add any DOM element as a fully interactive canvas citizen
13
- - **Select & multi-select** — click, drag box, move, resize
15
+ - **Layers** — named layers with visibility, locking, and absolute ordering
16
+ - **Select & multi-select** — click, drag box, move, resize (layer-aware)
14
17
  - **Undo / redo** — full history stack with configurable depth
15
- - **State serialization** — export/import JSON snapshots
18
+ - **State serialization** — export/import JSON snapshots with automatic migration
16
19
  - **Touch & tablet** — Pointer Events API, pinch-to-zoom, two-finger pan, stylus pressure
17
20
  - **Zero dependencies** — vanilla TypeScript, no framework required
18
21
  - **Tree-shakeable** — ESM + CJS output
@@ -91,10 +94,13 @@ viewport.stopInteracting();
91
94
  ```typescript
92
95
  // Programmatic
93
96
  viewport.addImage('https://example.com/photo.jpg', { x: 0, y: 0 });
97
+ viewport.addImage('/assets/map.png', { x: 0, y: 0 }, { w: 800, h: 600 });
94
98
 
95
99
  // Drag & drop is handled automatically — drop images onto the canvas
96
100
  ```
97
101
 
102
+ > **Important: Use URLs, not base64 data URLs.** Images are stored inline in the serialized state. A single base64-encoded photo can be 2-5MB, which will blow past the `localStorage` ~5MB quota and make JSON exports impractical. Upload images to your server or CDN and use the URL. For offline/local-first apps, store blobs in IndexedDB and reference them by URL.
103
+
98
104
  ## Camera Control
99
105
 
100
106
  ```typescript
@@ -147,6 +153,45 @@ viewport.history.onChange(() => {
147
153
  });
148
154
  ```
149
155
 
156
+ ## Layers
157
+
158
+ Organize elements into named layers with visibility, lock, and ordering controls. All elements on a higher layer render above all elements on a lower layer, regardless of individual z-index.
159
+
160
+ ```typescript
161
+ const { layerManager } = viewport;
162
+
163
+ // Create layers
164
+ const background = layerManager.activeLayer; // "Layer 1" exists by default
165
+ layerManager.renameLayer(background.id, 'Map');
166
+ const tokens = layerManager.createLayer('Tokens');
167
+ const notes = layerManager.createLayer('Notes');
168
+
169
+ // Set active layer — new elements are created on the active layer
170
+ layerManager.setActiveLayer(tokens.id);
171
+
172
+ // Visibility and locking
173
+ layerManager.setLayerVisible(background.id, false); // hide
174
+ layerManager.setLayerLocked(background.id, true); // prevent selection/editing
175
+
176
+ // Move elements between layers
177
+ layerManager.moveElementToLayer(elementId, notes.id);
178
+
179
+ // Reorder layers
180
+ layerManager.reorderLayer(tokens.id, 5); // higher order = renders on top
181
+
182
+ // Query
183
+ layerManager.getLayers(); // sorted by order
184
+ layerManager.isLayerVisible(id);
185
+ layerManager.isLayerLocked(id);
186
+
187
+ // Listen for changes
188
+ layerManager.on('change', () => {
189
+ /* update UI */
190
+ });
191
+ ```
192
+
193
+ Locked layers prevent selection, erasing, and arrow binding on their elements. Hidden layers are invisible and non-interactive. The active layer cannot be hidden or locked — if you try, it automatically switches to the next available layer.
194
+
150
195
  ## State Serialization
151
196
 
152
197
  ```typescript
@@ -158,6 +203,8 @@ localStorage.setItem('canvas', json);
158
203
  viewport.loadJSON(localStorage.getItem('canvas'));
159
204
  ```
160
205
 
206
+ > **Note:** Serialized state includes all layers and element `layerId` assignments. States saved before layers were introduced are automatically migrated — elements are placed on a default "Layer 1".
207
+
161
208
  ## Tool Switching
162
209
 
163
210
  ```typescript
@@ -296,16 +343,19 @@ interface BaseElement {
296
343
  position: { x: number; y: number };
297
344
  zIndex: number;
298
345
  locked: boolean;
346
+ layerId: string;
299
347
  }
300
348
  ```
301
349
 
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` |
350
+ | Type | Key Fields |
351
+ | -------- | ---------------------------------------------------------------------- |
352
+ | `stroke` | `points: StrokePoint[]`, `color`, `width`, `opacity` |
353
+ | `note` | `size`, `text`, `backgroundColor`, `textColor` |
354
+ | `arrow` | `from`, `to`, `bend`, `color`, `width`, `fromBinding`, `toBinding` |
355
+ | `image` | `size`, `src` |
356
+ | `shape` | `size`, `shape` (`rectangle` \| `ellipse`), `strokeColor`, `fillColor` |
357
+ | `text` | `size`, `text`, `fontSize`, `color`, `textAlign` |
358
+ | `html` | `size` |
309
359
 
310
360
  ## Built-in Interactions
311
361
 
package/dist/index.cjs CHANGED
@@ -286,7 +286,11 @@ var AutoSave = class {
286
286
  if (typeof localStorage === "undefined") return;
287
287
  const layers = this.layerManager?.snapshot() ?? [];
288
288
  const state = exportState(this.store.snapshot(), this.camera, layers);
289
- localStorage.setItem(this.key, JSON.stringify(state));
289
+ try {
290
+ localStorage.setItem(this.key, JSON.stringify(state));
291
+ } catch {
292
+ console.warn("Auto-save failed: storage quota exceeded. State too large for localStorage.");
293
+ }
290
294
  }
291
295
  };
292
296
 
@@ -3436,7 +3440,7 @@ var UpdateLayerCommand = class {
3436
3440
  };
3437
3441
 
3438
3442
  // src/index.ts
3439
- var VERSION = "0.6.0";
3443
+ var VERSION = "0.6.1";
3440
3444
  // Annotate the CommonJS export names for ESM import in node:
3441
3445
  0 && (module.exports = {
3442
3446
  AddElementCommand,