@fieldnotes/core 0.8.11 → 0.9.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 CHANGED
@@ -16,6 +16,9 @@ A lightweight, framework-agnostic infinite canvas SDK for the web — with first
16
16
  - **Select & multi-select** — click, drag box, move, resize (layer-aware)
17
17
  - **Undo / redo** — full history stack with configurable depth
18
18
  - **State serialization** — export/import JSON snapshots with automatic migration
19
+ - **Grids** — square and hex grid overlays for D&D maps and alignment
20
+ - **Export** — PNG export with scale, padding, background, and element filter options
21
+ - **Performance instrumentation** — `getRenderStats()` and `logPerformance()` for frame timing
19
22
  - **Touch & tablet** — Pointer Events API, pinch-to-zoom, two-finger pan, stylus pressure
20
23
  - **Zero dependencies** — vanilla TypeScript, no framework required
21
24
  - **Tree-shakeable** — ESM + CJS output
@@ -101,6 +104,56 @@ viewport.addImage('/assets/map.png', { x: 0, y: 0 }, { w: 800, h: 600 });
101
104
 
102
105
  > **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
106
 
107
+ ## Grids
108
+
109
+ Add square or hex grid overlays — useful for D&D combat maps, alignment, or graph paper backgrounds. Grids always render on top of images and other layer elements.
110
+
111
+ ```typescript
112
+ // Add a hex grid
113
+ viewport.addGrid({
114
+ gridType: 'hex',
115
+ hexOrientation: 'pointy', // 'pointy' | 'flat'
116
+ cellSize: 40,
117
+ strokeColor: '#cccccc',
118
+ strokeWidth: 1,
119
+ opacity: 0.5,
120
+ });
121
+
122
+ // Update grid properties
123
+ viewport.updateGrid({ cellSize: 50, strokeColor: '#aaaaaa' });
124
+
125
+ // Remove grid
126
+ viewport.removeGrid();
127
+ ```
128
+
129
+ ## Image Export
130
+
131
+ Export the canvas as a PNG image:
132
+
133
+ ```typescript
134
+ const blob = await viewport.exportImage({
135
+ scale: 2, // pixel density (default 2)
136
+ padding: 20, // world-space padding around content (default 0)
137
+ background: '#fff', // fill color (default '#ffffff')
138
+ filter: (el) => el.type !== 'html', // optional per-element filter
139
+ });
140
+ ```
141
+
142
+ HTML elements are excluded from image exports (DOM cannot be rasterized to canvas). Cross-origin images are handled automatically via CORS cache-busting.
143
+
144
+ ## Performance Monitoring
145
+
146
+ ```typescript
147
+ // Get a snapshot of render stats
148
+ const stats = viewport.getRenderStats();
149
+ // { fps, avgFrameMs, p95FrameMs, lastGridMs, frameCount }
150
+
151
+ // Log stats to console every 2 seconds (returns stop function)
152
+ const stop = viewport.logPerformance(2000);
153
+ // [FieldNotes] fps=60 frame=1.2ms p95=2.1ms grid=0.1ms
154
+ stop(); // stop logging
155
+ ```
156
+
104
157
  ## Camera Control
105
158
 
106
159
  ```typescript
@@ -347,15 +400,16 @@ interface BaseElement {
347
400
  }
348
401
  ```
349
402
 
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` |
403
+ | Type | Key Fields |
404
+ | -------- | -------------------------------------------------------------------------------------- |
405
+ | `stroke` | `points: StrokePoint[]`, `color`, `width`, `opacity` |
406
+ | `note` | `size`, `text`, `backgroundColor`, `textColor` |
407
+ | `arrow` | `from`, `to`, `bend`, `color`, `width`, `fromBinding`, `toBinding` |
408
+ | `image` | `size`, `src` |
409
+ | `shape` | `size`, `shape` (`rectangle` \| `ellipse`), `strokeColor`, `fillColor` |
410
+ | `text` | `size`, `text`, `fontSize`, `color`, `textAlign` |
411
+ | `grid` | `gridType` (`square` \| `hex`), `hexOrientation`, `cellSize`, `strokeColor`, `opacity` |
412
+ | `html` | `size` |
359
413
 
360
414
  ## Built-in Interactions
361
415