@fieldnotes/core 0.63.0 → 0.65.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/dist/index.d.cts CHANGED
@@ -241,7 +241,25 @@ interface Tool {
241
241
  onPointerDown(state: PointerState, ctx: ToolContext): void;
242
242
  onPointerMove(state: PointerState, ctx: ToolContext): void;
243
243
  onPointerUp(state: PointerState, ctx: ToolContext): void;
244
+ /**
245
+ * The gesture was taken over (a second pointer started navigation) or the
246
+ * platform cancelled the pointer. Optional: tools without it receive
247
+ * `onPointerUp` instead, exactly as before. Implement it when "up" and
248
+ * "abandon" must differ (a multi-step tool must not treat a pinch as input).
249
+ * The gesture's history transaction is still committed afterwards (an
250
+ * in-progress store mutation must never be left open); a tool that wants
251
+ * abandon semantics must revert its own store mutations before returning.
252
+ */
253
+ onPointerCancel?(state: PointerState, ctx: ToolContext): void;
244
254
  onHover?(state: PointerState, ctx: ToolContext): void;
255
+ /**
256
+ * Offered every keydown that reaches the canvas (never from editable
257
+ * targets, and only within the viewport's keyboard scope) BEFORE the
258
+ * shortcut map. Return `true` to consume it (default prevented, no shortcut
259
+ * runs). Listeners are owned by the viewport, so a tool holds no DOM
260
+ * subscriptions and deactivation cannot leak.
261
+ */
262
+ onKeyDown?(event: KeyboardEvent, ctx: ToolContext): boolean;
245
263
  onActivate?(ctx: ToolContext): void;
246
264
  onDeactivate?(ctx: ToolContext): void;
247
265
  renderOverlay?(ctx: CanvasRenderingContext2D): void;
@@ -249,11 +267,70 @@ interface Tool {
249
267
  setOptions?(options: object): void;
250
268
  onOptionsChange?(listener: () => void): () => void;
251
269
  }
252
- type ToolName = 'hand' | 'select' | 'pencil' | 'eraser' | 'arrow' | 'note' | 'image' | 'text' | 'shape' | 'measure' | 'template' | 'laser' | 'ping';
270
+ type ToolName = 'hand' | 'select' | 'pencil' | 'eraser' | 'arrow' | 'note' | 'image' | 'text' | 'shape' | 'measure' | 'path' | 'template' | 'laser' | 'ping';
253
271
 
254
272
  declare function snapPoint(point: Point, gridSize: number): Point;
255
273
  declare function snapToHexCenter(point: Point, cellSize: number, orientation: HexOrientation): Point;
256
274
  declare function smartSnap(point: Point, ctx: ToolContext): Point;
275
+ /** Cell footprint of a snapped thing: a scalar N means N×N cells. */
276
+ type Footprint = number | {
277
+ w: number;
278
+ h: number;
279
+ };
280
+ /**
281
+ * The cell footprint an element of `size` occupies on a square grid: each axis
282
+ * rounds to the NEAREST whole cell (a 1.25-cell token is one cell wide), never
283
+ * below one. Without a usable grid size the footprint is a single cell. Pair it
284
+ * with `snapFootprintCenter` so an odd-cell element lands on a cell centre and
285
+ * an even-cell one on an intersection.
286
+ */
287
+ declare function footprintFromSize(size: {
288
+ w: number;
289
+ h: number;
290
+ }, gridSize: number): Footprint;
291
+ /**
292
+ * Snaps a CENTRE point so a footprint of `w`×`h` cells fills whole cells on a
293
+ * square grid: an odd axis lands on a cell centre, an even axis on an
294
+ * intersection. Each axis is independent (a 1×2 footprint centres on X and
295
+ * sits on an intersection on Y). Compare `snapPoint`, which snaps to
296
+ * intersections only.
297
+ */
298
+ declare function snapToCellCenter(point: Point, gridSize: number, footprint?: Footprint): Point;
299
+ /**
300
+ * `smartSnap` for centres: identity when snapping is off, hex centres on hex
301
+ * grids, footprint-aware cell centres otherwise.
302
+ */
303
+ declare function snapFootprintCenter(point: Point, footprint: Footprint, ctx: ToolContext): Point;
304
+
305
+ /**
306
+ * How diagonal steps are costed on a SQUARE grid, in cells.
307
+ * - `euclidean` — straight-line distance over the cell size (the ruler's behaviour; default).
308
+ * - `chebyshev` — a diagonal step costs one cell.
309
+ * - `alternate` — diagonals alternate 1, 2, 1, 2 … cells ("5-10-5"), counted over the WHOLE path.
310
+ * - `manhattan` — a diagonal step costs two cells.
311
+ * Ignored on hex grids (hex distance is the cube metric).
312
+ */
313
+ type DiagonalRule = 'euclidean' | 'chebyshev' | 'alternate' | 'manhattan';
314
+ interface GridMetric {
315
+ gridSize: number;
316
+ gridType?: 'square' | 'hex';
317
+ hexOrientation?: HexOrientation;
318
+ diagonalRule?: DiagonalRule;
319
+ }
320
+ interface PathDistance {
321
+ /** Cost of the whole path in cells. */
322
+ total: number;
323
+ /** Cost after each waypoint; `cumulative[0] === 0`, one entry per point. */
324
+ cumulative: number[];
325
+ }
326
+ /**
327
+ * Distance of a polyline in grid cells. Stateful rules (`alternate`) accumulate
328
+ * over the whole path, so the unit of computation is the path, not a segment;
329
+ * a segment's own cost is `cumulative[i] - cumulative[i - 1]`.
330
+ */
331
+ declare function pathDistanceCells(points: readonly Point[], grid: GridMetric): PathDistance;
332
+ /** Two-point convenience over `pathDistanceCells`. */
333
+ declare function gridDistanceCells(a: Point, b: Point, grid: GridMetric): number;
257
334
 
258
335
  interface Layer {
259
336
  id: string;
@@ -419,6 +496,8 @@ declare class ToolManager {
419
496
  handlePointerDown(state: PointerState, ctx: ToolContext): void;
420
497
  handlePointerMove(state: PointerState, ctx: ToolContext): void;
421
498
  handlePointerUp(state: PointerState, ctx: ToolContext): void;
499
+ /** Cancels the active gesture; falls back to `onPointerUp` for tools without `onPointerCancel`. */
500
+ handlePointerCancel(state: PointerState, ctx: ToolContext): void;
422
501
  onChange(listener: (name: string) => void): () => void;
423
502
  onRegister(listener: (tool: Tool) => void): () => void;
424
503
  }
@@ -1568,17 +1647,13 @@ interface RemoteMeasureOverlayOptions {
1568
1647
  * updated for `maxAgeMs` is expired by a timer — an idle map never renders,
1569
1648
  * so expiry cannot ride on the draw path. The overlay never touches elements,
1570
1649
  * history, or persisted state, and never moves the viewer's camera.
1650
+ *
1651
+ * The per-sender lifetime is `LingerOverlay`; this class only validates
1652
+ * payloads and draws one measurement.
1571
1653
  */
1572
1654
  declare class RemoteMeasureOverlay {
1573
- private readonly host;
1574
1655
  private readonly color;
1575
- private readonly holdMs;
1576
- private readonly fadeMs;
1577
- private readonly maxAgeMs;
1578
- private readonly measurements;
1579
- private unregister;
1580
- private rafId;
1581
- private disposed;
1656
+ private readonly overlay;
1582
1657
  constructor(host: RemoteMeasureOverlayHost, options?: RemoteMeasureOverlayOptions);
1583
1658
  private now;
1584
1659
  /**
@@ -1595,10 +1670,265 @@ declare class RemoteMeasureOverlay {
1595
1670
  get activeSenderCount(): number;
1596
1671
  /** Unregisters the overlay, cancels timers, stops animating. Idempotent. */
1597
1672
  dispose(): void;
1598
- private beginLinger;
1599
- private ensureAnimating;
1600
- private tick;
1601
- private renderMeasurements;
1673
+ }
1674
+
1675
+ /** A running-total threshold: segments up to `feet` are drawn in `color`. */
1676
+ interface PathRangeBand {
1677
+ readonly feet: number;
1678
+ readonly color: string;
1679
+ }
1680
+ /**
1681
+ * Where a path starts. A host resolves the pointer to the thing being moved
1682
+ * (a token, a marker) so the path anchors on it instead of the raw cursor;
1683
+ * `anchorKey` is opaque to the tool and echoed back on every emission so the
1684
+ * host can apply the move it asked for.
1685
+ */
1686
+ interface PathAnchor {
1687
+ origin: Point;
1688
+ footprint?: Footprint;
1689
+ anchorKey?: string;
1690
+ }
1691
+ interface PathToolOptions {
1692
+ feetPerCell?: number;
1693
+ color?: string;
1694
+ diagonalRule?: DiagonalRule;
1695
+ footprint?: Footprint;
1696
+ rangeBands?: readonly PathRangeBand[];
1697
+ /**
1698
+ * Screen-space radius, in CSS pixels, around the last waypoint inside which a
1699
+ * tap finishes the path instead of adding a corner. Default `12`. Screen
1700
+ * space so the target keeps its physical size at any zoom; `0` restores
1701
+ * exact-match-only commits (a fingertip can never hit an exact world point on
1702
+ * a gridless canvas, so a touch commit needs the tolerance). Ignored while
1703
+ * this path has an active snapping grid (square, or hex with an
1704
+ * orientation) — there the snapped tap already lands exactly on the last
1705
+ * waypoint, and a screen-space radius could otherwise reach a neighbouring
1706
+ * cell centre at a zoomed-out camera.
1707
+ */
1708
+ commitTapRadiusPx?: number;
1709
+ /** Returning `null` vetoes the gesture — no path opens and nothing is emitted. */
1710
+ resolveStart?: (world: Point, ctx: ToolContext) => PathAnchor | null;
1711
+ }
1712
+ interface PathSegment {
1713
+ readonly cells: number;
1714
+ readonly feet: number;
1715
+ }
1716
+ /**
1717
+ * A raf-coalesced snapshot of the in-progress path. `waypoints` are the
1718
+ * committed corners; `cursor` is the rubber-banding end (null once the path
1719
+ * closes) and is counted in the totals without being a waypoint. Ephemeral by
1720
+ * contract: presence only — never elements, history, or persisted state.
1721
+ */
1722
+ interface PathEmission {
1723
+ readonly anchorKey?: string;
1724
+ readonly waypoints: readonly Point[];
1725
+ readonly cursor: Point | null;
1726
+ readonly segments: readonly PathSegment[];
1727
+ readonly totalCells: number;
1728
+ readonly totalFeet: number;
1729
+ readonly color: string;
1730
+ readonly rangeBands: readonly PathRangeBand[];
1731
+ }
1732
+ /**
1733
+ * Multi-waypoint movement measuring: tap to anchor, drag or click to add
1734
+ * corners, tap on or near the last waypoint (or press Enter) to finish, Escape
1735
+ * or a pointer takeover to abandon.
1736
+ *
1737
+ * Store-free by contract: the tool never creates, moves, or deletes elements
1738
+ * and never opens a history transaction — it only measures. A completed path
1739
+ * is handed to `onCommit` listeners and the HOST applies the move (inside its
1740
+ * own transaction) if it wants one. Path snapshots are ephemeral in the same
1741
+ * sense as `MeasureTool`'s: presence only — never elements, history, or
1742
+ * persisted state.
1743
+ */
1744
+ declare class PathTool implements Tool {
1745
+ readonly name = "path";
1746
+ private feetPerCell;
1747
+ private color;
1748
+ private diagonalRule;
1749
+ private footprintOption;
1750
+ private rangeBands;
1751
+ private commitTapRadiusPx;
1752
+ private resolveStart;
1753
+ private waypoints;
1754
+ private cursor;
1755
+ private anchorKey;
1756
+ private footprint;
1757
+ private pointerDown;
1758
+ private commitOnUp;
1759
+ private gridSize;
1760
+ private gridType;
1761
+ private hexOrientation;
1762
+ private snapEnabled;
1763
+ private optionListeners;
1764
+ private pathListeners;
1765
+ private commitListeners;
1766
+ private emissionRafId;
1767
+ constructor(options?: PathToolOptions);
1768
+ getOptions(): PathToolOptions;
1769
+ setOptions(options: PathToolOptions): void;
1770
+ onOptionsChange(listener: () => void): () => void;
1771
+ /**
1772
+ * Subscribes to raf-coalesced path snapshots. While a path is open,
1773
+ * listeners receive at most one snapshot per animation frame carrying the
1774
+ * latest state; `null` is delivered synchronously when the path closes
1775
+ * (commit, cancel, or deactivate).
1776
+ */
1777
+ onPath(listener: (emission: PathEmission | null) => void): () => void;
1778
+ /**
1779
+ * Subscribes to finished paths. The emission carries `cursor: null` and the
1780
+ * final waypoints; applying the move (and recording history for it) is the
1781
+ * host's job — the tool has already forgotten the path by then.
1782
+ */
1783
+ onCommit(listener: (emission: PathEmission) => void): () => void;
1784
+ get isOpen(): boolean;
1785
+ /** Synchronous snapshot of the open path; `null` when no path is open. */
1786
+ getEmission(): PathEmission | null;
1787
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
1788
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
1789
+ onPointerUp(_state: PointerState, ctx: ToolContext): void;
1790
+ onHover(state: PointerState, ctx: ToolContext): void;
1791
+ /** A takeover (second pointer, platform cancel) abandons the path outright. */
1792
+ onPointerCancel(_state: PointerState, ctx: ToolContext): void;
1793
+ onDeactivate(ctx: ToolContext): void;
1794
+ onKeyDown(event: KeyboardEvent, ctx: ToolContext): boolean;
1795
+ renderOverlay(ctx: CanvasRenderingContext2D): void;
1796
+ private lastWaypoint;
1797
+ /**
1798
+ * Is `point` close enough to `last` to mean "finish here"? EXACT match always
1799
+ * qualifies. The screen-space tolerance is added ONLY when this path has no
1800
+ * active snapping grid: on a snapping grid (`gridType === 'square'`, or
1801
+ * `'hex'` with a captured orientation, and `gridSize > 0`) the adjacent
1802
+ * cell/hex centre can be as little as one grid cell away, and a radius
1803
+ * computed from CSS pixels has no relationship to that distance — at a
1804
+ * sufficiently zoomed-out camera the radius would swallow a whole neighbour
1805
+ * cell and turn a deliberate next-waypoint tap into an unwanted commit.
1806
+ * Snapping already makes a fingertip tap land exactly on the last waypoint,
1807
+ * so the tolerance is unnecessary there; it exists for the gridless and
1808
+ * `snapPoint`/identity paths, where a fingertip can never land on an exact
1809
+ * world point.
1810
+ */
1811
+ private withinCommitRadius;
1812
+ /**
1813
+ * True when this path's captured grid state snaps every waypoint onto a
1814
+ * cell/hex centre: a `square` grid, or a `hex` grid with a captured
1815
+ * orientation, both with a usable `gridSize`. Mirrors the unconditional
1816
+ * branches of `snap` below — NOT the `snapToGrid`-gated fallback, which
1817
+ * leaves waypoints unsnapped when the user turns snapping off.
1818
+ */
1819
+ private hasSnappingGrid;
1820
+ /**
1821
+ * Snapping to cell or hex centres is UNCONDITIONAL for a `square` grid, or a
1822
+ * `hex` grid WITH a captured orientation: a movement path measures in cells,
1823
+ * so an unsnapped waypoint would report a distance the grid does not agree
1824
+ * with. A `hex` grid WITHOUT an orientation falls through to the
1825
+ * `snapToGrid`-gated `snapPoint` (intersection) branch below, same as the
1826
+ * gridType-less case — unlike `smartSnap`, which is off entirely when the
1827
+ * user turns snapping off.
1828
+ */
1829
+ private snap;
1830
+ /** The measured polyline: waypoints, plus the cursor when it adds a leg. */
1831
+ private measure;
1832
+ private buildEmission;
1833
+ private commit;
1834
+ private cancel;
1835
+ private reset;
1836
+ private notifyOptionsChange;
1837
+ private scheduleEmission;
1838
+ private emitClear;
1839
+ private emitPath;
1840
+ private emitCommit;
1841
+ }
1842
+
1843
+ /**
1844
+ * The wire shape of a movement-path presence payload. Presence data is untyped
1845
+ * on the wire, so hosts discriminate on `kind`; `isPathPresence` validates a
1846
+ * received payload before it reaches the overlay. Distance is
1847
+ * sender-authoritative: receivers render the payload's `feet` and pre-resolved
1848
+ * `segmentColors` and never recompute from their own grid or range bands.
1849
+ * Paths are ephemeral by contract: presence only — never elements, undo
1850
+ * history, persisted canvas state, or durable operations. The emission's
1851
+ * `anchorKey` is host-private and never travels on the wire.
1852
+ */
1853
+ type PathPresence = {
1854
+ readonly kind: 'path';
1855
+ readonly points: readonly Point[];
1856
+ /** One entry per segment: `points.length - 1` colours. */
1857
+ readonly segmentColors: readonly string[];
1858
+ readonly feet: number;
1859
+ readonly color?: string;
1860
+ } | {
1861
+ readonly kind: 'path';
1862
+ readonly cleared: true;
1863
+ };
1864
+ declare const PATH_PRESENCE_KIND = "path";
1865
+ /** Point cap for a received path; longer payloads are rejected outright. */
1866
+ declare const PATH_PRESENCE_MAX_POINTS = 256;
1867
+ declare function isPathPresence(data: unknown): data is PathPresence;
1868
+ /**
1869
+ * Builds the presence payload for one local `PathTool` emission. Range bands
1870
+ * are resolved to per-segment colours here, so a receiver renders the sender's
1871
+ * bands without knowing them. `anchorKey` is deliberately dropped: it names a
1872
+ * local element the receiver cannot resolve.
1873
+ *
1874
+ * The sender imposes NO waypoint cap: a path longer than
1875
+ * `PATH_PRESENCE_MAX_POINTS` produces a payload that every receiver rejects, so
1876
+ * a host that allows very long paths should cap waypoints itself or expect
1877
+ * non-delivery beyond the limit.
1878
+ */
1879
+ declare function toPathPresence(emission: PathEmission | null): PathPresence;
1880
+ /**
1881
+ * The two viewport capabilities the overlay needs; `Viewport` satisfies it.
1882
+ * Structurally identical to the internal `LingerOverlayHost` and to
1883
+ * `RemoteMeasureOverlayHost`; the members are spelled out rather than inherited
1884
+ * so the public shape never depends on an unexported internal interface.
1885
+ */
1886
+ interface RemotePathOverlayHost {
1887
+ registerOverlay(draw: OverlayRenderer): () => void;
1888
+ requestRender(): void;
1889
+ }
1890
+ interface RemotePathOverlayOptions {
1891
+ /** Style fallback when a payload omits `color`. Default `'#FF5722'`. */
1892
+ color?: string;
1893
+ /** Full-opacity hold after a cleared payload. Default `1500`. */
1894
+ holdMs?: number;
1895
+ /** Linear fade to 0 after the hold. Default `400`. */
1896
+ fadeMs?: number;
1897
+ /** Stale active entries are treated as cleared after this. Default `30000`. */
1898
+ maxAgeMs?: number;
1899
+ }
1900
+ /**
1901
+ * Renders remote movement paths through the viewport overlay registration,
1902
+ * independent of the viewer's active tool. Entries are stamped with local
1903
+ * receive time (remote clocks are never trusted). A cleared payload holds the
1904
+ * final path for `holdMs`, fades over `fadeMs`, and deletes; presence-leave
1905
+ * (`remove`) deletes immediately. An active entry not updated for `maxAgeMs`
1906
+ * is expired by a timer — an idle map never renders, so expiry cannot ride on
1907
+ * the draw path. The overlay never touches elements, history, or persisted
1908
+ * state, and never moves the viewer's camera.
1909
+ *
1910
+ * The per-sender lifetime is `LingerOverlay`; this class only validates
1911
+ * payloads and draws one path.
1912
+ */
1913
+ declare class RemotePathOverlay {
1914
+ private readonly color;
1915
+ private readonly overlay;
1916
+ constructor(host: RemotePathOverlayHost, options?: RemotePathOverlayOptions);
1917
+ private now;
1918
+ /**
1919
+ * Applies a presence payload from `sender` (any opaque per-sender key, e.g.
1920
+ * the envelope `from`). Non-path or malformed payloads are ignored and
1921
+ * reported as `false`, so hosts can feed every presence frame through.
1922
+ */
1923
+ apply(sender: string, data: unknown): boolean;
1924
+ /** Removes a sender's path immediately (presence-leave/disconnect). */
1925
+ remove(sender: string): void;
1926
+ /** Removes every path immediately. */
1927
+ clear(): void;
1928
+ /** Number of senders with a visible (active or lingering) path. */
1929
+ get activeSenderCount(): number;
1930
+ /** Unregisters the overlay, cancels timers, stops animating. Idempotent. */
1931
+ dispose(): void;
1602
1932
  }
1603
1933
 
1604
1934
  /**
@@ -1875,6 +2205,434 @@ declare class RemoteFocusReceiver {
1875
2205
  dispose(): void;
1876
2206
  }
1877
2207
 
2208
+ /**
2209
+ * Sender identity carried on every awareness frame. `id` is the app's stable
2210
+ * peer id (a character id, a user id); `name`, `color`, and `role` are
2211
+ * self-asserted display data. Receivers MUST treat `name` as untrusted text
2212
+ * (render through canvas text or `textContent`, never as HTML) and MUST NOT
2213
+ * gate anything security-relevant on any of these fields — the relay does not
2214
+ * authenticate presence payloads.
2215
+ */
2216
+ interface AwarenessIdentity {
2217
+ readonly id: string;
2218
+ readonly name?: string;
2219
+ readonly color?: string;
2220
+ readonly role?: string;
2221
+ }
2222
+ /**
2223
+ * The wire shape of one awareness frame. Every frame is the sender's COMPLETE
2224
+ * state (identity + whatever it publishes), so any coalescer that keeps only
2225
+ * the newest frame — the relay's per-kind throttle lane, a slow consumer — is
2226
+ * correct by construction: latest wins. Absent fields mean "none / not
2227
+ * published"; there is no delta encoding. A `cleared` frame says the sender is
2228
+ * going away (receivers drop it now instead of waiting for stale expiry).
2229
+ * Presence only: never elements, undo history, persisted canvas state, or
2230
+ * durable operations.
2231
+ */
2232
+ interface AwarenessPresence extends AwarenessIdentity {
2233
+ readonly kind: 'awareness';
2234
+ /** World-space pointer position; absent = no cursor to show. */
2235
+ readonly cursor?: Point;
2236
+ /** Selected element ids the sender chose to publish; absent = none. */
2237
+ readonly selection?: readonly string[];
2238
+ /** Active tool name; absent = not published. */
2239
+ readonly tool?: string;
2240
+ readonly cleared?: true;
2241
+ }
2242
+ declare const AWARENESS_PRESENCE_KIND = "awareness";
2243
+ /** Cap on `selection` entries; longer payloads are rejected outright. */
2244
+ declare const AWARENESS_MAX_SELECTION = 256;
2245
+ /**
2246
+ * Wire-boundary guard, fail closed: any violated cap rejects the whole frame
2247
+ * (nothing is clamped or sanitised). Unknown extra fields are tolerated so a
2248
+ * newer sender can add fields without breaking older receivers. `cleared`,
2249
+ * when present, must be exactly `true`; `cleared: false` rejects the whole
2250
+ * frame.
2251
+ */
2252
+ declare function isAwarenessPresence(data: unknown): data is AwarenessPresence;
2253
+
2254
+ /** One remote peer as last seen. Liveness timestamps are deliberately not exposed. */
2255
+ interface Peer extends AwarenessIdentity {
2256
+ /** The relay's server-owned per-socket sender key (the envelope `from`). */
2257
+ readonly from: string;
2258
+ readonly cursor: Point | null;
2259
+ readonly selection: readonly string[];
2260
+ readonly tool: string | null;
2261
+ }
2262
+ type PeerLeaveReason = 'left' | 'cleared' | 'stale';
2263
+ interface PeerRosterOptions {
2264
+ /**
2265
+ * A sender with no valid frame for this long is dropped (row and discovery
2266
+ * entry) with reason `'stale'`. Default `45000` (3× the publisher heartbeat);
2267
+ * `0` disables expiry.
2268
+ */
2269
+ staleMs?: number;
2270
+ /** Clock seam for tests; default `Date.now`. */
2271
+ now?: () => number;
2272
+ }
2273
+ /**
2274
+ * Last-known awareness state per remote sender, keyed by the relay's
2275
+ * server-owned `from`. Two books are kept deliberately separate:
2276
+ *
2277
+ * - **Rows** are visible membership: created by any valid non-cleared frame,
2278
+ * removed by a `cleared` frame, by `remove()` (the server-authored
2279
+ * presence-leave), or by stale expiry.
2280
+ * - **Discovery entries** are the re-announce budget: touched by EVERY valid
2281
+ * frame (cleared included) and dropped ONLY by `remove()` or by `staleMs`
2282
+ * without any valid frame. `onDiscover` fires when a valid frame arrives from
2283
+ * a sender with no entry — at most once per socket lifetime, so a client
2284
+ * cycling full → cleared → full cannot make every peer re-announce.
2285
+ *
2286
+ * `getPeers()` returns the same array until a visible field or membership
2287
+ * changes (a heartbeat carrying identical state is silent), which makes it a
2288
+ * valid `useSyncExternalStore` snapshot. Remote clocks are never trusted; all
2289
+ * timing is local.
2290
+ */
2291
+ declare class PeerRoster {
2292
+ private readonly staleMs;
2293
+ private readonly now;
2294
+ private readonly rows;
2295
+ private readonly discovered;
2296
+ private readonly changeListeners;
2297
+ private readonly discoverListeners;
2298
+ private readonly leaveListeners;
2299
+ private snapshot;
2300
+ private snapshotDirty;
2301
+ private staleTimer;
2302
+ private isDisposed;
2303
+ constructor(options?: PeerRosterOptions);
2304
+ get disposed(): boolean;
2305
+ /**
2306
+ * Applies a presence payload from `from`. Non-awareness or malformed payloads
2307
+ * return `false` untouched, so hosts can feed every presence frame through.
2308
+ */
2309
+ apply(from: string, data: unknown): boolean;
2310
+ /** Server-authored presence-leave: drops the row AND the discovery entry. */
2311
+ remove(from: string): void;
2312
+ getPeers(): readonly Peer[];
2313
+ getPeer(from: string): Peer | undefined;
2314
+ /** Fires only when `getPeers()` would return a new reference. */
2315
+ onChange(listener: () => void): () => void;
2316
+ /** First valid frame from a sender since its discovery entry was last dropped. */
2317
+ onDiscover(listener: (from: string) => void): () => void;
2318
+ onLeave(listener: (peer: Peer, reason: PeerLeaveReason) => void): () => void;
2319
+ dispose(): void;
2320
+ private dropRow;
2321
+ private changed;
2322
+ private emit;
2323
+ private armStaleTimer;
2324
+ private expireStale;
2325
+ }
2326
+
2327
+ /** The viewport capabilities the publisher reads; `Viewport` satisfies it. */
2328
+ interface LocalAwarenessHost {
2329
+ readonly camera: {
2330
+ screenToWorld(screen: Point): Point;
2331
+ };
2332
+ /** The pointer listener attaches to `domLayer.parentElement` (the wrapper). */
2333
+ readonly domLayer: HTMLElement;
2334
+ onSelectionChange(listener: () => void): () => void;
2335
+ getSelectedIds(): string[];
2336
+ readonly toolManager: {
2337
+ onChange(listener: (name: string) => void): () => void;
2338
+ readonly activeTool: {
2339
+ readonly name: string;
2340
+ } | null;
2341
+ };
2342
+ }
2343
+ interface AwarenessFields {
2344
+ cursor?: boolean;
2345
+ /** Off by default: selection ids are a disclosure the moment they are sent. */
2346
+ selection?: boolean;
2347
+ tool?: boolean;
2348
+ }
2349
+ interface LocalAwarenessOptions {
2350
+ identity: AwarenessIdentity;
2351
+ /** Default `{ cursor: true, selection: false, tool: true }`. */
2352
+ fields?: AwarenessFields;
2353
+ /**
2354
+ * Host projection applied to selected ids before they leave the client.
2355
+ * Fails closed: if it throws or returns anything but an array of strings,
2356
+ * frames carry NO selection (never the unfiltered ids) until it next
2357
+ * succeeds, and the error goes to `onError`.
2358
+ */
2359
+ selectionFilter?: (ids: readonly string[]) => readonly string[];
2360
+ onError?: (error: unknown) => void;
2361
+ /** Minimum spacing between frames. Default `50` (the relay lane throttle). */
2362
+ intervalMs?: number;
2363
+ /** Full-state re-send while idle, measured from the last send. Default `15000`; `0` disables. */
2364
+ heartbeatMs?: number;
2365
+ /** Pointer listener target override; default `host.domLayer.parentElement`. */
2366
+ element?: HTMLElement;
2367
+ send: (data: AwarenessPresence) => void;
2368
+ }
2369
+ /**
2370
+ * Publishes this client's awareness state (identity, pointer in world space,
2371
+ * selection, active tool) as full-snapshot frames through `send`. Sources are
2372
+ * a passive primary-pointer listener on the viewport wrapper, the viewport's
2373
+ * selection-change event, and the tool manager's change event. Any change
2374
+ * marks the state dirty; a leading-edge frame goes out at once when idle,
2375
+ * otherwise one trailing frame per `intervalMs` carries every change made in
2376
+ * the window. A heartbeat re-sends the state while idle. `dispose` sends one
2377
+ * `cleared` frame. Nothing here touches elements, history, or the camera.
2378
+ *
2379
+ * Frames are valid by construction: identity is truncated to the wire caps
2380
+ * (`name`/`color` to 64 characters, `role` to 32) so a sender can never
2381
+ * publish a frame the wire guard would reject outright; truncation is
2382
+ * reported through `onError` (a `RangeError`) rather than silent. An
2383
+ * invalid `id` (empty or over 128 characters) throws instead of truncating.
2384
+ * `tool` is never truncated: a tool name over 64 characters is omitted from
2385
+ * the frame rather than corrupted. An over-long or empty selection id fails
2386
+ * the selection closed (like a non-string filter entry) rather than being
2387
+ * sent. `intervalMs` and `heartbeatMs` are normalised so a non-finite or
2388
+ * out-of-range value (e.g. `Infinity`) can never arm a near-zero busy-loop
2389
+ * timer. The selection filter is re-applied before every frame that carries
2390
+ * a selection, so a policy change takes effect on the next frame without a
2391
+ * selection event.
2392
+ */
2393
+ declare class LocalAwareness {
2394
+ private readonly host;
2395
+ private readonly element;
2396
+ private readonly send;
2397
+ private readonly selectionFilter;
2398
+ private readonly onError;
2399
+ private readonly intervalMs;
2400
+ private readonly heartbeatMs;
2401
+ private identity;
2402
+ private fields;
2403
+ private lastPointer;
2404
+ private selection;
2405
+ private selectionFailed;
2406
+ private tool;
2407
+ private dirty;
2408
+ private lastSentAt;
2409
+ private throttleTimer;
2410
+ private heartbeatTimer;
2411
+ private readonly unsubscribers;
2412
+ private isDisposed;
2413
+ private readonly handlePointerMove;
2414
+ private readonly handlePointerEnd;
2415
+ constructor(host: LocalAwarenessHost, options: LocalAwarenessOptions);
2416
+ get disposed(): boolean;
2417
+ getFields(): Readonly<Required<AwarenessFields>>;
2418
+ setIdentity(identity: AwarenessIdentity): void;
2419
+ /** Merges the given flags into the current policy; `undefined` keys are ignored. */
2420
+ setFields(fields: AwarenessFields): void;
2421
+ /**
2422
+ * Requests a full frame: immediate when idle, otherwise folded into the
2423
+ * pending trailing frame (so N simultaneous requests cost one frame). Hosts
2424
+ * call it when the connection becomes live or reconnects.
2425
+ */
2426
+ announce(): void;
2427
+ /**
2428
+ * The complete state a frame carries right now. Side-effecting when
2429
+ * selection publishing is on: re-reads `getSelectedIds()`, re-runs
2430
+ * `selectionFilter`, updates the fail-closed selection state, and may call
2431
+ * `onError`. A no-op with respect to selection while publishing is off.
2432
+ */
2433
+ getState(): AwarenessPresence;
2434
+ dispose(): void;
2435
+ private now;
2436
+ private onPointerMove;
2437
+ private onPointerEnd;
2438
+ private refreshSelection;
2439
+ private schedule;
2440
+ private flush;
2441
+ private armHeartbeat;
2442
+ private safeSend;
2443
+ private report;
2444
+ /**
2445
+ * Truncates identity strings to the wire caps and rejects an invalid id, so a
2446
+ * sender can never publish a frame that the wire guard would drop outright.
2447
+ * A truncated field is reported through `onError` (a `RangeError`) rather
2448
+ * than silently shortened, so a caller passing an over-long name finds out.
2449
+ */
2450
+ private normalizeIdentity;
2451
+ }
2452
+
2453
+ /** The viewport capabilities the overlay needs; `Viewport` satisfies it. */
2454
+ interface RemoteCursorOverlayHost {
2455
+ registerOverlay(draw: OverlayRenderer): () => void;
2456
+ requestRender(): void;
2457
+ /** Read at draw time so glyph and label keep a constant screen size. */
2458
+ readonly camera: {
2459
+ readonly zoom: number;
2460
+ };
2461
+ }
2462
+ interface RemoteCursorOverlayOptions {
2463
+ /**
2464
+ * Host colour resolver consulted first (e.g. a campaign's per-player colour
2465
+ * table keyed by `peer.id`). Return `undefined` to fall through to the wire
2466
+ * `color`, then to `defaultPeerColor(peer.id)`.
2467
+ */
2468
+ colorFor?: (peer: Peer) => string | undefined;
2469
+ /** Draw the name chip next to the arrow. Default `true`. */
2470
+ showLabels?: boolean;
2471
+ /** Default `'12px sans-serif'`. */
2472
+ labelFont?: string;
2473
+ }
2474
+ /** Twelve well-separated hues so peers stay distinguishable without a colour on the wire. */
2475
+ declare const PEER_COLORS: readonly string[];
2476
+ /**
2477
+ * Deterministic palette colour for a stable peer id (FNV-1a over UTF-16 code
2478
+ * units), so every client shows the same peer in the same colour without any
2479
+ * colour travelling on the wire.
2480
+ */
2481
+ declare function defaultPeerColor(seed: string): string;
2482
+ /**
2483
+ * Renders every roster peer that has a cursor as an arrow glyph plus a name
2484
+ * chip, in world space but at constant screen size (scaled by `1 / zoom`).
2485
+ * Names are drawn as canvas text, so untrusted display text is inert. Reads
2486
+ * the roster only; never re-parses payloads, never moves the camera, never
2487
+ * touches the store.
2488
+ */
2489
+ declare class RemoteCursorOverlay {
2490
+ private readonly host;
2491
+ private readonly roster;
2492
+ private readonly colorFor;
2493
+ private readonly showLabels;
2494
+ private readonly labelFont;
2495
+ private readonly labelWidths;
2496
+ private unregister;
2497
+ private unsubscribe;
2498
+ private isDisposed;
2499
+ constructor(host: RemoteCursorOverlayHost, roster: PeerRoster, options?: RemoteCursorOverlayOptions);
2500
+ get disposed(): boolean;
2501
+ resolveColor(peer: Peer): string;
2502
+ dispose(): void;
2503
+ private render;
2504
+ private drawLabel;
2505
+ }
2506
+
2507
+ /** The viewport capabilities the overlay needs; `Viewport` satisfies it. */
2508
+ interface RemoteSelectionOverlayHost {
2509
+ registerOverlay(draw: OverlayRenderer): () => void;
2510
+ requestRender(): void;
2511
+ readonly camera: {
2512
+ readonly zoom: number;
2513
+ };
2514
+ readonly store: ElementStore;
2515
+ readonly layerManager: LayerManager;
2516
+ }
2517
+ interface RemoteSelectionOverlayOptions {
2518
+ /** Same precedence as `RemoteCursorOverlay`: resolver → wire colour → `defaultPeerColor`. */
2519
+ colorFor?: (peer: Peer) => string | undefined;
2520
+ /** Outline opacity. Default `0.6`. */
2521
+ alpha?: number;
2522
+ /** Outline width in screen pixels. Default `2`. */
2523
+ lineWidthPx?: number;
2524
+ }
2525
+ /**
2526
+ * Outlines the elements other peers have selected. Only ids that exist in the
2527
+ * LOCAL store AND sit on a locally visible layer are drawn; this is a
2528
+ * rendering courtesy on top of the sender-side opt-in (`fields.selection`) and
2529
+ * `selectionFilter` — privacy is decided at publish time, not here. The store
2530
+ * is rescanned only when some peer's selection reference or resolved colour
2531
+ * changed, or when the store or layer visibility changed; cursor-only roster
2532
+ * updates never scan. When two peers select the same element, the peer
2533
+ * earlier in `roster.getPeers()` order (insertion order: whoever the roster
2534
+ * saw first) supplies the outline colour for it.
2535
+ */
2536
+ declare class RemoteSelectionOverlay {
2537
+ private readonly host;
2538
+ private readonly roster;
2539
+ private readonly colorFor;
2540
+ private readonly alpha;
2541
+ private readonly lineWidthPx;
2542
+ private signatures;
2543
+ private outlines;
2544
+ private storeDirty;
2545
+ private unregister;
2546
+ private readonly unsubscribers;
2547
+ private isDisposed;
2548
+ constructor(host: RemoteSelectionOverlayHost, roster: PeerRoster, options?: RemoteSelectionOverlayOptions);
2549
+ get disposed(): boolean;
2550
+ dispose(): void;
2551
+ private resolveColor;
2552
+ /** Recomputes outlines only when the selection signature or the store/layers changed. */
2553
+ private rebuild;
2554
+ private render;
2555
+ }
2556
+
2557
+ /**
2558
+ * The three presence primitives every Field Notes transport exposes. The raw
2559
+ * `SyncClient`, `ManagedSyncConnection`, and host wrappers all satisfy it
2560
+ * structurally, so core never depends on `@fieldnotes/sync`. `from` is the
2561
+ * relay's opaque per-sender key.
2562
+ */
2563
+ interface PresenceChannel {
2564
+ sendPresence(data: unknown): void;
2565
+ onPresence(handler: (from: string, data: unknown) => void): () => void;
2566
+ onPresenceLeave(handler: (from: string) => void): () => void;
2567
+ }
2568
+ /**
2569
+ * What `attachAwareness` needs from a viewport; `Viewport` satisfies it.
2570
+ *
2571
+ * This does not `extends LocalAwarenessHost, RemoteCursorOverlayHost,
2572
+ * RemoteSelectionOverlayHost`: those three declare `camera` with different
2573
+ * member sets (`screenToWorld` vs `zoom`), and TypeScript rejects an
2574
+ * interface that extends multiple parents whose same-named property types
2575
+ * are not identical or mutually assignable. Listing the members directly
2576
+ * (with `camera` widened to carry both) keeps `AwarenessViewport`
2577
+ * structurally assignable to all three host types.
2578
+ */
2579
+ interface AwarenessViewport {
2580
+ readonly camera: {
2581
+ readonly zoom: number;
2582
+ screenToWorld(screen: Point): Point;
2583
+ };
2584
+ /** The pointer listener attaches to `domLayer.parentElement` (the wrapper). */
2585
+ readonly domLayer: HTMLElement;
2586
+ readonly store: ElementStore;
2587
+ readonly layerManager: LayerManager;
2588
+ onSelectionChange(listener: () => void): () => void;
2589
+ getSelectedIds(): string[];
2590
+ readonly toolManager: {
2591
+ onChange(listener: (name: string) => void): () => void;
2592
+ readonly activeTool: {
2593
+ readonly name: string;
2594
+ } | null;
2595
+ };
2596
+ registerOverlay(draw: OverlayRenderer): () => void;
2597
+ requestRender(): void;
2598
+ }
2599
+ interface AttachAwarenessOptions extends Omit<LocalAwarenessOptions, 'send'> {
2600
+ roster?: PeerRosterOptions;
2601
+ /** Named cursor overlay options, or `false` to render no cursors. Default on. */
2602
+ cursors?: RemoteCursorOverlayOptions | false;
2603
+ /** Selection outline overlay: options or `true` to enable. Default off. */
2604
+ selections?: RemoteSelectionOverlayOptions | boolean;
2605
+ /**
2606
+ * `false` builds a receive-only attachment (no `LocalAwareness`). Default
2607
+ * `true`. When `false`, `identity` is ignored: there is no publisher to
2608
+ * carry it.
2609
+ */
2610
+ publish?: boolean;
2611
+ }
2612
+ interface AwarenessHandle {
2613
+ readonly roster: PeerRoster;
2614
+ /** `null` when `publish: false`. */
2615
+ readonly local: LocalAwareness | null;
2616
+ readonly cursors: RemoteCursorOverlay | null;
2617
+ readonly selections: RemoteSelectionOverlay | null;
2618
+ /** Call when the connection becomes live or reconnects. No-op when receive-only. */
2619
+ announce(): void;
2620
+ /** Forwards to `LocalAwareness.setFields` (merge). No-op when receive-only. */
2621
+ setFields(fields: AwarenessFields): void;
2622
+ dispose(): void;
2623
+ }
2624
+ /**
2625
+ * Binds the awareness lifecycle to a presence channel: incoming frames feed
2626
+ * the roster, presence-leave drops rows and discovery budget, and each newly
2627
+ * discovered sender makes this client re-announce once, coalesced by the
2628
+ * publisher's interval: from a cold start the first discovery goes out on
2629
+ * the leading edge and later ones fold into a single trailing frame, so N
2630
+ * simultaneous joiners cost at most two frames; while already active, one.
2631
+ * Dispose order: publisher (sends `cleared`) → overlays → roster → channel
2632
+ * unsubscribes.
2633
+ */
2634
+ declare function attachAwareness(viewport: AwarenessViewport, channel: PresenceChannel, options: AttachAwarenessOptions): AwarenessHandle;
2635
+
1878
2636
  interface ActiveFormats {
1879
2637
  bold: boolean;
1880
2638
  italic: boolean;
@@ -2232,8 +2990,8 @@ declare class ShapeTool implements Tool {
2232
2990
  private computeRect;
2233
2991
  private notifyOptionsChange;
2234
2992
  private snap;
2235
- private onKeyDown;
2236
- private onKeyUp;
2993
+ private trackShiftKeyDown;
2994
+ private trackShiftKeyUp;
2237
2995
  }
2238
2996
 
2239
2997
  interface TemplateToolOptions {
@@ -2281,6 +3039,6 @@ declare class TemplateTool implements Tool {
2281
3039
  private notifyOptionsChange;
2282
3040
  }
2283
3041
 
2284
- declare const VERSION = "0.63.0";
3042
+ declare const VERSION = "0.65.0";
2285
3043
 
2286
- export { type ActivationOptions, type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraAnimationEndReason, CameraAnimator, type CameraAnimatorOptions, type CameraChangeInfo, type CameraOptions, type CameraView, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementActivationEvent, type ElementChangeMeta, type ElementRect, type ElementRectMatch, type ElementRectMatchError, ElementRectTracker, type ElementRectTrackerOptions, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportAssetError, type ExportAssetErrorReason, type ExportImageOptions, type ExportResourceOptions, type ExportSvgOptions, FOCUS_PRESENCE_KIND, type FocusAudience, type FocusPresence, type FocusRole, type FontSizePreset, type FrameScheduler, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HitTestOptions, type HtmlElement, type HtmlExportError, type HtmlExportErrorReason, type HtmlExportOptions, type HtmlExportRenderer, type HtmlPaintContext, type HtmlPaintDiagnostic, type HtmlPainter, HtmlPainterMissingError, HtmlPainterRegistry, type HtmlRenderTarget, type HtmlRouting, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LASER_TRAIL_PRESENCE_KIND, LaserTool, type LaserToolOptions, type LaserTrailEmission, type LaserTrailPresence, type Layer, LayerManager, LocalStorageAdapter, MEASURE_PRESENCE_KIND, type MeasureEmission, type MeasurePresence, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, MinimapController, type MinimapControllerOptions, type NoteElement, NoteTool, type NoteToolOptions, type OverlayRenderer, PING_PRESENCE_KIND, PencilTool, type PencilToolOptions, type PingEmission, PingInput, type PingInputHost, type PingInputOptions, type PingPresence, PingTool, type PingToolOptions, type Point, type PointerState, type RectTrackerHost, RemoteFocusReceiver, type RemoteFocusReceiverHost, type RemoteFocusReceiverOptions, RemoteLaserOverlay, type RemoteLaserOverlayHost, type RemoteLaserOverlayOptions, RemoteMeasureOverlay, type RemoteMeasureOverlayHost, type RemoteMeasureOverlayOptions, RemotePingOverlay, type RemotePingOverlayHost, type RemotePingOverlayOptions, type RenderStatsSnapshot, type RotateDirection, SelectTool, type SelectionStyleDetails, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, applyCameraView, boundsIntersect, cameraOriginForView, captureCameraView, computeElementRects, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, elementRectsEqual, exportImage, exportSvg, fitZoomForView, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isFocusPresence, isLaserTrailPresence, isMeasurePresence, isNearBezier, isPingPresence, resolveHtmlRouting, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toFocusPresence, toLaserTrailPresence, toMeasurePresence, toPingPresence, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
3044
+ export { AWARENESS_MAX_SELECTION, AWARENESS_PRESENCE_KIND, type ActivationOptions, type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, type AttachAwarenessOptions, AutoSave, type AutoSaveOptions, type AwarenessFields, type AwarenessHandle, type AwarenessIdentity, type AwarenessPresence, type AwarenessViewport, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraAnimationEndReason, CameraAnimator, type CameraAnimatorOptions, type CameraChangeInfo, type CameraOptions, type CameraView, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DiagonalRule, type DistributeAxis, type ElementActivationEvent, type ElementChangeMeta, type ElementRect, type ElementRectMatch, type ElementRectMatchError, ElementRectTracker, type ElementRectTrackerOptions, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportAssetError, type ExportAssetErrorReason, type ExportImageOptions, type ExportResourceOptions, type ExportSvgOptions, FOCUS_PRESENCE_KIND, type FocusAudience, type FocusPresence, type FocusRole, type FontSizePreset, type Footprint, type FrameScheduler, type GridElement, type GridInfo, type GridMetric, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HitTestOptions, type HtmlElement, type HtmlExportError, type HtmlExportErrorReason, type HtmlExportOptions, type HtmlExportRenderer, type HtmlPaintContext, type HtmlPaintDiagnostic, type HtmlPainter, HtmlPainterMissingError, HtmlPainterRegistry, type HtmlRenderTarget, type HtmlRouting, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LASER_TRAIL_PRESENCE_KIND, LaserTool, type LaserToolOptions, type LaserTrailEmission, type LaserTrailPresence, type Layer, LayerManager, LocalAwareness, type LocalAwarenessHost, type LocalAwarenessOptions, LocalStorageAdapter, MEASURE_PRESENCE_KIND, type MeasureEmission, type MeasurePresence, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, MinimapController, type MinimapControllerOptions, type NoteElement, NoteTool, type NoteToolOptions, type OverlayRenderer, PATH_PRESENCE_KIND, PATH_PRESENCE_MAX_POINTS, PEER_COLORS, PING_PRESENCE_KIND, type PathAnchor, type PathDistance, type PathEmission, type PathPresence, type PathRangeBand, type PathSegment, PathTool, type PathToolOptions, type Peer, type PeerLeaveReason, PeerRoster, type PeerRosterOptions, PencilTool, type PencilToolOptions, type PingEmission, PingInput, type PingInputHost, type PingInputOptions, type PingPresence, PingTool, type PingToolOptions, type Point, type PointerState, type PresenceChannel, type RectTrackerHost, RemoteCursorOverlay, type RemoteCursorOverlayHost, type RemoteCursorOverlayOptions, RemoteFocusReceiver, type RemoteFocusReceiverHost, type RemoteFocusReceiverOptions, RemoteLaserOverlay, type RemoteLaserOverlayHost, type RemoteLaserOverlayOptions, RemoteMeasureOverlay, type RemoteMeasureOverlayHost, type RemoteMeasureOverlayOptions, RemotePathOverlay, type RemotePathOverlayHost, type RemotePathOverlayOptions, RemotePingOverlay, type RemotePingOverlayHost, type RemotePingOverlayOptions, RemoteSelectionOverlay, type RemoteSelectionOverlayHost, type RemoteSelectionOverlayOptions, type RenderStatsSnapshot, type RotateDirection, SelectTool, type SelectionStyleDetails, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, applyCameraView, attachAwareness, boundsIntersect, cameraOriginForView, captureCameraView, computeElementRects, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, defaultPeerColor, drawHexPath, elementRectsEqual, exportImage, exportSvg, fitZoomForView, footprintFromSize, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, gridDistanceCells, isAwarenessPresence, isFocusPresence, isLaserTrailPresence, isMeasurePresence, isNearBezier, isPathPresence, isPingPresence, pathDistanceCells, resolveHtmlRouting, setFontSize, smartSnap, snapFootprintCenter, snapPoint, snapToCellCenter, snapToHexCenter, styleToPatch, toFocusPresence, toLaserTrailPresence, toMeasurePresence, toPathPresence, toPingPresence, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };