@fieldnotes/core 0.25.0 → 0.27.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
@@ -279,7 +279,8 @@ viewport.toolManager.onChange((toolName) => {
279
279
 
280
280
  Defaults (remappable): `Delete`/`Backspace` delete · `Escape` deselect · `mod+Z` undo ·
281
281
  `mod+Y`/`mod+Shift+Z` redo · `mod+A` select all · `mod+C/V/D` copy/paste/duplicate ·
282
- `[`/`]` z-order (with `mod` = to back/front) · `Shift+1` zoom-to-fit · arrows nudge
282
+ `[`/`]` z-order (with `mod` = to back/front) · `Shift+1` zoom-to-fit · `mod+=` zoom in ·
283
+ `mod+-` zoom out · `mod+0` reset zoom to 100% · arrows nudge
283
284
  (`Shift` = one grid cell) · tool keys `V` select, `H` hand, `P` pencil, `E` eraser,
284
285
  `A` arrow, `N` note, `T` text, `S` shape, `M` measure, `G` template.
285
286
 
@@ -470,6 +471,78 @@ interface BaseElement {
470
471
  | `grid` | `gridType` (`square` \| `hex`), `hexOrientation`, `cellSize`, `strokeColor`, `opacity` |
471
472
  | `html` | `size` |
472
473
 
474
+ ## Styling the Selection
475
+
476
+ A normalized `ElementStyle` interface lets you read and apply visual properties across all element types through a single, consistent shape. The `SelectTool` emits a selection-change event; `Viewport` exposes four methods that together cover reactive UIs.
477
+
478
+ ### `ElementStyle` interface
479
+
480
+ ```typescript
481
+ interface ElementStyle {
482
+ color?: string; // stroke color / text color
483
+ fillColor?: string; // fill / background color
484
+ strokeWidth?: number; // line width in world-space units
485
+ opacity?: number; // 0–1
486
+ fontSize?: number; // px
487
+ }
488
+ ```
489
+
490
+ #### Mapping across element types
491
+
492
+ | `ElementStyle` field | `stroke` | `arrow` | `shape` | `note` | `text` |
493
+ | -------------------- | --------- | ------- | ------------- | ----------------- | ---------- |
494
+ | `color` | `color` | `color` | `strokeColor` | `textColor` | `color` |
495
+ | `fillColor` | — | — | `fillColor` | `backgroundColor` | — |
496
+ | `strokeWidth` | `width` | `width` | `strokeWidth` | — | — |
497
+ | `opacity` | `opacity` | — | — | — | — |
498
+ | `fontSize` | — | — | — | (via toolbar) | `fontSize` |
499
+
500
+ ### Conversion helpers
501
+
502
+ ```typescript
503
+ import { styleToPatch, getElementStyle } from '@fieldnotes/core';
504
+
505
+ // ElementStyle → element-specific patch object
506
+ const patch = styleToPatch(element, { color: '#e00', strokeWidth: 3 });
507
+ store.update(element.id, patch);
508
+
509
+ // element → normalized ElementStyle
510
+ const style = getElementStyle(element);
511
+ ```
512
+
513
+ ### Viewport methods
514
+
515
+ - **`viewport.getSelectedIds()`** — returns the current selection as a referentially-stable array (the same array reference is reused across calls when the selection has not changed — safe for `useSyncExternalStore` equality checks).
516
+ - **`viewport.onSelectionChange(listener)`** — subscribes to selection changes; returns an unsubscribe function. The listener receives the new stable id array.
517
+ - **`viewport.getSelectionStyle()`** — returns an `ElementStyle` containing only the properties that are identical across every selected element. Properties that differ are omitted.
518
+ - **`viewport.applyStyleToSelection(style)`** — applies the given `ElementStyle` to all selected elements in a single undo step.
519
+
520
+ ### `SelectTool.onSelectionChange`
521
+
522
+ ```typescript
523
+ const selectTool = viewport.toolManager.getTool<SelectTool>('select');
524
+ selectTool?.onSelectionChange((ids) => {
525
+ console.log('selected:', ids);
526
+ });
527
+ ```
528
+
529
+ ### Example
530
+
531
+ ```typescript
532
+ // Apply a red stroke to everything currently selected — one undo step
533
+ viewport.applyStyleToSelection({ color: '#ff0000' });
534
+
535
+ // Read back the shared style for a UI color picker
536
+ const style = viewport.getSelectionStyle();
537
+ // style.color is defined only if all selected elements share the same color
538
+
539
+ // React to selection changes
540
+ const unsub = viewport.onSelectionChange((ids) => {
541
+ setSelectedIds(ids); // ids is referentially stable — safe for deps arrays
542
+ });
543
+ // call unsub() to unsubscribe
544
+ ```
545
+
473
546
  ## Built-in Interactions
474
547
 
475
548
  | Input | Action |