@fieldnotes/react 0.14.0 → 0.14.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/dist/index.js CHANGED
@@ -158,12 +158,11 @@ function CanvasElement({ position, size, children }) {
158
158
  };
159
159
  elementIdRef.current = register();
160
160
  setPortalTarget(container);
161
- let disposed = false;
162
161
  const handleStoreReplacement = () => {
163
162
  const clearedId = elementIdRef.current;
164
163
  if (clearedId === null) return;
165
164
  queueMicrotask(() => {
166
- if (disposed || elementIdRef.current !== clearedId) return;
165
+ if (elementIdRef.current !== clearedId) return;
167
166
  if (viewport.store.getById(clearedId)?.type === "html") {
168
167
  viewport.updateHtmlElement(clearedId, container);
169
168
  } else {
@@ -175,7 +174,6 @@ function CanvasElement({ position, size, children }) {
175
174
  const unsubscribeClear = viewport.store.on("clear", handleStoreReplacement);
176
175
  const unsubscribeBatch = viewport.store.on("batch", handleStoreReplacement);
177
176
  return () => {
178
- disposed = true;
179
177
  unsubscribeClear();
180
178
  unsubscribeBatch();
181
179
  if (elementIdRef.current) {
@@ -199,67 +197,42 @@ function CanvasElement({ position, size, children }) {
199
197
  }
200
198
 
201
199
  // src/hooks/use-active-tool.ts
202
- import { useCallback, useSyncExternalStore } from "react";
200
+ import { useCallback, useMemo, useSyncExternalStore } from "react";
201
+ import { createActiveToolSource } from "@fieldnotes/core";
203
202
  function useActiveTool() {
204
203
  const viewport = useViewport();
205
- const subscribe = useCallback(
206
- (onStoreChange) => viewport.toolManager.onChange(onStoreChange),
207
- [viewport]
208
- );
209
- const getSnapshot = useCallback(() => viewport.toolManager.activeTool?.name ?? "", [viewport]);
210
- const toolName = useSyncExternalStore(subscribe, getSnapshot);
204
+ const source = useMemo(() => createActiveToolSource(viewport), [viewport]);
205
+ const toolName = useSyncExternalStore(source.subscribe, source.getSnapshot);
211
206
  const setTool = useCallback((name) => viewport.setTool(name), [viewport]);
212
207
  return [toolName, setTool];
213
208
  }
214
209
 
215
210
  // src/hooks/use-camera.ts
216
- import { useCallback as useCallback2, useRef as useRef3, useSyncExternalStore as useSyncExternalStore2 } from "react";
211
+ import { useMemo as useMemo2, useSyncExternalStore as useSyncExternalStore2 } from "react";
212
+ import { createCameraSource } from "@fieldnotes/core";
217
213
  function useCamera() {
218
214
  const viewport = useViewport();
219
- const cachedRef = useRef3({ x: 0, y: 0, zoom: 1 });
220
- const subscribe = useCallback2(
221
- (onStoreChange) => viewport.camera.onChange(onStoreChange),
222
- [viewport]
223
- );
224
- const getSnapshot = useCallback2(() => {
225
- const { position, zoom } = viewport.camera;
226
- const cached = cachedRef.current;
227
- if (cached.x === position.x && cached.y === position.y && cached.zoom === zoom) {
228
- return cached;
229
- }
230
- const next = { x: position.x, y: position.y, zoom };
231
- cachedRef.current = next;
232
- return next;
233
- }, [viewport]);
234
- return useSyncExternalStore2(subscribe, getSnapshot);
215
+ const source = useMemo2(() => createCameraSource(viewport), [viewport]);
216
+ return useSyncExternalStore2(source.subscribe, source.getSnapshot);
235
217
  }
236
218
 
237
219
  // src/hooks/use-tool-options.ts
238
- import { useCallback as useCallback3, useRef as useRef4, useSyncExternalStore as useSyncExternalStore3 } from "react";
239
- function noop() {
240
- }
220
+ import { useCallback as useCallback2, useMemo as useMemo3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore3 } from "react";
221
+ import { createToolOptionsSource } from "@fieldnotes/core";
241
222
  function useToolOptions(toolName) {
242
223
  const viewport = useViewport();
243
- const cachedRef = useRef4(null);
244
- const subscribe = useCallback3(
245
- (onStoreChange) => {
246
- const tool = viewport.toolManager.getTool(toolName);
247
- if (!tool?.onOptionsChange) return noop;
248
- return tool.onOptionsChange(onStoreChange);
249
- },
250
- [viewport, toolName]
224
+ const nameRef = useRef3(toolName);
225
+ nameRef.current = toolName;
226
+ const source = useMemo3(
227
+ () => createToolOptionsSource(viewport, () => nameRef.current),
228
+ [viewport]
251
229
  );
252
- const getSnapshot = useCallback3(() => {
253
- const tool = viewport.toolManager.getTool(toolName);
254
- if (!tool?.getOptions) return null;
255
- const next = tool.getOptions();
256
- const cached = cachedRef.current;
257
- if (cached && shallowEqual(cached, next)) return cached;
258
- cachedRef.current = next;
259
- return next;
260
- }, [viewport, toolName]);
261
- const options = useSyncExternalStore3(subscribe, getSnapshot);
262
- const setOptions = useCallback3(
230
+ const subscribe = useCallback2(
231
+ (onStoreChange) => source.subscribe(onStoreChange),
232
+ [source, toolName]
233
+ );
234
+ const options = useSyncExternalStore3(subscribe, source.getSnapshot);
235
+ const setOptions = useCallback2(
263
236
  (partial) => {
264
237
  const tool = viewport.toolManager.getTool(toolName);
265
238
  tool?.setOptions?.(partial);
@@ -268,84 +241,47 @@ function useToolOptions(toolName) {
268
241
  );
269
242
  return [options, setOptions];
270
243
  }
271
- function shallowEqual(a, b) {
272
- const keysA = Object.keys(a);
273
- const keysB = Object.keys(b);
274
- if (keysA.length !== keysB.length) return false;
275
- for (const key of keysA) {
276
- const valA = a[key];
277
- const valB = b[key];
278
- if (valA === valB) continue;
279
- if (typeof valA === "object" && valA !== null && typeof valB === "object" && valB !== null && !Array.isArray(valA) && !Array.isArray(valB)) {
280
- const objA = valA;
281
- const objB = valB;
282
- const innerKeys = Object.keys(objA);
283
- if (innerKeys.length === Object.keys(objB).length && innerKeys.every((k) => objA[k] === objB[k])) {
284
- continue;
285
- }
286
- }
287
- return false;
288
- }
289
- return true;
290
- }
291
244
 
292
245
  // src/hooks/use-layers.ts
293
- import { useCallback as useCallback4, useRef as useRef5, useSyncExternalStore as useSyncExternalStore4 } from "react";
246
+ import { useCallback as useCallback3, useMemo as useMemo4, useSyncExternalStore as useSyncExternalStore4 } from "react";
247
+ import { createLayersSource } from "@fieldnotes/core";
294
248
  function useLayers() {
295
249
  const viewport = useViewport();
296
- const cachedRef = useRef5({ layers: [], activeLayerId: "" });
297
- const subscribe = useCallback4(
298
- (onStoreChange) => viewport.layerManager.on("change", onStoreChange),
299
- [viewport]
300
- );
301
- const getSnapshot = useCallback4(() => {
302
- const layers = viewport.layerManager.getLayers();
303
- const activeLayerId = viewport.layerManager.activeLayerId;
304
- const cached = cachedRef.current;
305
- if (cached.activeLayerId === activeLayerId && cached.layers.length === layers.length && cached.layers.every((l, i) => {
306
- const next2 = layers[i];
307
- return next2 !== void 0 && l.id === next2.id && l.name === next2.name && l.visible === next2.visible && l.locked === next2.locked && l.order === next2.order && l.opacity === next2.opacity;
308
- })) {
309
- return cached;
310
- }
311
- const next = { layers, activeLayerId };
312
- cachedRef.current = next;
313
- return next;
314
- }, [viewport]);
315
- const snapshot = useSyncExternalStore4(subscribe, getSnapshot);
316
- const createLayer = useCallback4(
250
+ const source = useMemo4(() => createLayersSource(viewport), [viewport]);
251
+ const snapshot = useSyncExternalStore4(source.subscribe, source.getSnapshot);
252
+ const createLayer = useCallback3(
317
253
  (name) => viewport.layerManager.createLayer(name),
318
254
  [viewport]
319
255
  );
320
- const removeLayer = useCallback4(
256
+ const removeLayer = useCallback3(
321
257
  (id) => viewport.layerManager.removeLayer(id),
322
258
  [viewport]
323
259
  );
324
- const renameLayer = useCallback4(
260
+ const renameLayer = useCallback3(
325
261
  (id, name) => viewport.layerManager.renameLayer(id, name),
326
262
  [viewport]
327
263
  );
328
- const reorderLayer = useCallback4(
264
+ const reorderLayer = useCallback3(
329
265
  (id, newOrder) => viewport.layerManager.reorderLayer(id, newOrder),
330
266
  [viewport]
331
267
  );
332
- const setVisible = useCallback4(
268
+ const setVisible = useCallback3(
333
269
  (id, visible) => viewport.layerManager.setLayerVisible(id, visible),
334
270
  [viewport]
335
271
  );
336
- const setLocked = useCallback4(
272
+ const setLocked = useCallback3(
337
273
  (id, locked) => viewport.layerManager.setLayerLocked(id, locked),
338
274
  [viewport]
339
275
  );
340
- const setOpacity = useCallback4(
276
+ const setOpacity = useCallback3(
341
277
  (id, opacity) => viewport.layerManager.setLayerOpacity(id, opacity),
342
278
  [viewport]
343
279
  );
344
- const setActiveLayer = useCallback4(
280
+ const setActiveLayer = useCallback3(
345
281
  (id) => viewport.layerManager.setActiveLayer(id),
346
282
  [viewport]
347
283
  );
348
- const moveElement = useCallback4(
284
+ const moveElement = useCallback3(
349
285
  (elementId, layerId) => viewport.layerManager.moveElementToLayer(elementId, layerId),
350
286
  [viewport]
351
287
  );
@@ -365,28 +301,14 @@ function useLayers() {
365
301
  }
366
302
 
367
303
  // src/hooks/use-history.ts
368
- import { useCallback as useCallback5, useRef as useRef6, useSyncExternalStore as useSyncExternalStore5 } from "react";
304
+ import { useCallback as useCallback4, useMemo as useMemo5, useSyncExternalStore as useSyncExternalStore5 } from "react";
305
+ import { createHistorySource } from "@fieldnotes/core";
369
306
  function useHistory() {
370
307
  const viewport = useViewport();
371
- const cachedRef = useRef6({ canUndo: false, canRedo: false });
372
- const subscribe = useCallback5(
373
- (onStoreChange) => viewport.history.onChange(onStoreChange),
374
- [viewport]
375
- );
376
- const getSnapshot = useCallback5(() => {
377
- const canUndo = viewport.history.canUndo;
378
- const canRedo = viewport.history.canRedo;
379
- const cached = cachedRef.current;
380
- if (cached.canUndo === canUndo && cached.canRedo === canRedo) {
381
- return cached;
382
- }
383
- const next = { canUndo, canRedo };
384
- cachedRef.current = next;
385
- return next;
386
- }, [viewport]);
387
- const snapshot = useSyncExternalStore5(subscribe, getSnapshot);
388
- const undo = useCallback5(() => viewport.undo(), [viewport]);
389
- const redo = useCallback5(() => viewport.redo(), [viewport]);
308
+ const source = useMemo5(() => createHistorySource(viewport), [viewport]);
309
+ const snapshot = useSyncExternalStore5(source.subscribe, source.getSnapshot);
310
+ const undo = useCallback4(() => viewport.undo(), [viewport]);
311
+ const redo = useCallback4(() => viewport.redo(), [viewport]);
390
312
  return {
391
313
  canUndo: snapshot.canUndo,
392
314
  canRedo: snapshot.canRedo,
@@ -396,197 +318,80 @@ function useHistory() {
396
318
  }
397
319
 
398
320
  // src/hooks/use-elements.ts
399
- import { useCallback as useCallback6, useRef as useRef7, useSyncExternalStore as useSyncExternalStore6 } from "react";
400
- function shallowEqual2(a, b) {
401
- if (Object.is(a, b)) return true;
402
- if (Array.isArray(a) && Array.isArray(b)) {
403
- return a.length === b.length && a.every((v, i) => Object.is(v, b[i]));
404
- }
405
- if (typeof a === "object" && a !== null && typeof b === "object" && b !== null && !Array.isArray(a) && !Array.isArray(b)) {
406
- const ka = Object.keys(a);
407
- const kb = Object.keys(b);
408
- return ka.length === kb.length && ka.every(
409
- (k) => k in b && Object.is(a[k], b[k])
410
- );
411
- }
412
- return false;
413
- }
321
+ import { useMemo as useMemo6, useRef as useRef4, useSyncExternalStore as useSyncExternalStore6 } from "react";
322
+ import { createElementsSelectorSource, createElementsSource } from "@fieldnotes/core";
414
323
  function useElements(arg, isEqual) {
415
324
  const viewport = useViewport();
416
- const cachedRef = useRef7([]);
417
- const selectedRef = useRef7(null);
418
- const subscribe = useCallback6(
419
- (onStoreChange) => viewport.store.onChange(onStoreChange),
325
+ const latest = useRef4({ arg, isEqual });
326
+ latest.current = { arg, isEqual };
327
+ const elementsSource = useMemo6(
328
+ () => createElementsSource(viewport, () => {
329
+ const a = latest.current.arg;
330
+ return typeof a === "function" ? void 0 : a;
331
+ }),
420
332
  [viewport]
421
333
  );
422
- const getSnapshot = useCallback6(() => {
423
- if (typeof arg === "function") {
424
- const next2 = arg(viewport.store.getAll());
425
- const prev = selectedRef.current;
426
- const equal = isEqual ?? shallowEqual2;
427
- if (prev !== null && equal(prev.value, next2)) {
428
- return prev.value;
429
- }
430
- selectedRef.current = { value: next2 };
431
- return next2;
432
- }
433
- const next = arg ? viewport.store.getElementsByType(arg) : viewport.store.getAll();
434
- const cached = cachedRef.current;
435
- if (cached.length === next.length && cached.every((el, i) => el === next[i])) {
436
- return cached;
437
- }
438
- cachedRef.current = next;
439
- return next;
440
- }, [viewport, arg, isEqual]);
441
- return useSyncExternalStore6(subscribe, getSnapshot);
334
+ const selectorSource = useMemo6(
335
+ () => createElementsSelectorSource(
336
+ viewport,
337
+ // Only read while `arg` is a selector: the source is picked below by `typeof arg`.
338
+ () => latest.current.arg,
339
+ () => latest.current.isEqual
340
+ ),
341
+ [viewport]
342
+ );
343
+ const source = typeof arg === "function" ? selectorSource : elementsSource;
344
+ return useSyncExternalStore6(source.subscribe, source.getSnapshot);
442
345
  }
443
346
 
444
347
  // src/hooks/use-selection.ts
445
- import { useCallback as useCallback7, useSyncExternalStore as useSyncExternalStore7 } from "react";
348
+ import { useMemo as useMemo7, useSyncExternalStore as useSyncExternalStore7 } from "react";
349
+ import { createSelectionSource } from "@fieldnotes/core";
446
350
  function useSelection() {
447
351
  const viewport = useViewport();
448
- const subscribe = useCallback7(
449
- (onChange) => viewport.onSelectionChange(onChange),
450
- [viewport]
451
- );
452
- const getSnapshot = useCallback7(() => viewport.getSelectedIds(), [viewport]);
453
- return useSyncExternalStore7(subscribe, getSnapshot, getSnapshot);
352
+ const source = useMemo7(() => createSelectionSource(viewport), [viewport]);
353
+ return useSyncExternalStore7(source.subscribe, source.getSnapshot, source.getSnapshot);
454
354
  }
455
355
 
456
356
  // src/hooks/use-selection-style.ts
457
- import { useCallback as useCallback8, useRef as useRef8, useSyncExternalStore as useSyncExternalStore8 } from "react";
458
-
459
- // src/hooks/style-keys.ts
460
- var STYLE_KEYS = [
461
- "color",
462
- "fillColor",
463
- "strokeWidth",
464
- "opacity",
465
- "fontSize",
466
- "strokeStyle"
467
- ];
468
-
469
- // src/hooks/use-selection-style.ts
470
- function styleEqual(a, b) {
471
- if (a === b) return true;
472
- if (a === null || b === null) return false;
473
- return STYLE_KEYS.every((k) => a[k] === b[k]);
474
- }
357
+ import { useCallback as useCallback5, useMemo as useMemo8, useSyncExternalStore as useSyncExternalStore8 } from "react";
358
+ import { createSelectionStyleSource } from "@fieldnotes/core";
475
359
  function useSelectionStyle() {
476
360
  const viewport = useViewport();
477
- const cacheRef = useRef8(null);
478
- const subscribe = useCallback8(
479
- (onChange) => {
480
- const offSel = viewport.onSelectionChange(onChange);
481
- const offStore = viewport.store.onChange(onChange);
482
- return () => {
483
- offSel();
484
- offStore();
485
- };
486
- },
487
- [viewport]
488
- );
489
- const getSnapshot = useCallback8(() => {
490
- const next = viewport.getSelectionStyle();
491
- if (styleEqual(cacheRef.current, next)) return cacheRef.current;
492
- cacheRef.current = next;
493
- return next;
494
- }, [viewport]);
495
- const style = useSyncExternalStore8(subscribe, getSnapshot, getSnapshot);
496
- const apply = useCallback8((s) => viewport.applyStyleToSelection(s), [viewport]);
361
+ const source = useMemo8(() => createSelectionStyleSource(viewport), [viewport]);
362
+ const style = useSyncExternalStore8(source.subscribe, source.getSnapshot, source.getSnapshot);
363
+ const apply = useCallback5((s) => viewport.applyStyleToSelection(s), [viewport]);
497
364
  return [style, apply];
498
365
  }
499
366
 
500
367
  // src/hooks/use-selection-style-details.ts
501
- import { useCallback as useCallback9, useRef as useRef9, useSyncExternalStore as useSyncExternalStore9 } from "react";
502
- function keysEqual(a, b) {
503
- return a.length === b.length && a.every((k, i) => k === b[i]);
504
- }
505
- function detailsEqual(a, b) {
506
- if (a === b) return true;
507
- if (a === null || b === null) return false;
508
- return keysEqual(a.applicable, b.applicable) && keysEqual(a.mixed, b.mixed) && STYLE_KEYS.every((k) => a.common[k] === b.common[k]);
509
- }
368
+ import { useCallback as useCallback6, useMemo as useMemo9, useSyncExternalStore as useSyncExternalStore9 } from "react";
369
+ import { createSelectionStyleDetailsSource } from "@fieldnotes/core";
510
370
  function useSelectionStyleDetails() {
511
371
  const viewport = useViewport();
512
- const cacheRef = useRef9(null);
513
- const subscribe = useCallback9(
514
- (onChange) => {
515
- const offSel = viewport.onSelectionChange(onChange);
516
- const offStore = viewport.store.onChange(onChange);
517
- return () => {
518
- offSel();
519
- offStore();
520
- };
521
- },
522
- [viewport]
523
- );
524
- const getSnapshot = useCallback9(() => {
525
- const next = viewport.getSelectionStyleDetails();
526
- if (detailsEqual(cacheRef.current, next)) return cacheRef.current;
527
- cacheRef.current = next;
528
- return next;
529
- }, [viewport]);
530
- const details = useSyncExternalStore9(subscribe, getSnapshot, getSnapshot);
531
- const apply = useCallback9((s) => viewport.applyStyleToSelection(s), [viewport]);
372
+ const source = useMemo9(() => createSelectionStyleDetailsSource(viewport), [viewport]);
373
+ const details = useSyncExternalStore9(source.subscribe, source.getSnapshot, source.getSnapshot);
374
+ const apply = useCallback6((s) => viewport.applyStyleToSelection(s), [viewport]);
532
375
  return [details, apply];
533
376
  }
534
377
 
535
378
  // src/hooks/use-selection-ops.ts
536
- import { useCallback as useCallback10, useRef as useRef10, useSyncExternalStore as useSyncExternalStore10 } from "react";
537
- function snapshotEqual(a, b) {
538
- return a.selectedIds === b.selectedIds && a.canGroup === b.canGroup && a.canUngroup === b.canUngroup && a.canAlign === b.canAlign && a.canDistribute === b.canDistribute && a.isLocked === b.isLocked;
539
- }
379
+ import { useCallback as useCallback7, useMemo as useMemo10, useSyncExternalStore as useSyncExternalStore10 } from "react";
380
+ import { createSelectionOpsSource } from "@fieldnotes/core";
540
381
  function useSelectionOps() {
541
382
  const viewport = useViewport();
542
- const cacheRef = useRef10(null);
543
- const subscribe = useCallback10(
544
- (onChange) => {
545
- const offSel = viewport.onSelectionChange(onChange);
546
- const offStore = viewport.store.onChange(onChange);
547
- return () => {
548
- offSel();
549
- offStore();
550
- };
551
- },
552
- [viewport]
553
- );
554
- const getSnapshot = useCallback10(() => {
555
- const ids = viewport.getSelectedIds();
556
- const n = ids.length;
557
- let canUngroup = false;
558
- let lockedCount = 0;
559
- for (const id of ids) {
560
- const el = viewport.store.getById(id);
561
- if (!el) continue;
562
- if (el.groupId !== void 0) canUngroup = true;
563
- if (el.locked) lockedCount++;
564
- }
565
- const isLocked = n === 0 ? null : lockedCount === n ? true : lockedCount === 0 ? false : null;
566
- const next = {
567
- selectedIds: ids,
568
- canGroup: n >= 2,
569
- canUngroup,
570
- canAlign: n >= 2,
571
- canDistribute: n >= 3,
572
- isLocked
573
- };
574
- const prev = cacheRef.current;
575
- if (prev && snapshotEqual(prev, next)) return prev;
576
- cacheRef.current = next;
577
- return next;
578
- }, [viewport]);
579
- const snap = useSyncExternalStore10(subscribe, getSnapshot, getSnapshot);
580
- const group = useCallback10(() => viewport.groupSelection(), [viewport]);
581
- const ungroup = useCallback10(() => viewport.ungroupSelection(), [viewport]);
582
- const toggleLock = useCallback10(() => viewport.toggleLockSelection(), [viewport]);
583
- const align = useCallback10((edge) => viewport.alignSelection(edge), [viewport]);
584
- const distribute = useCallback10(
383
+ const source = useMemo10(() => createSelectionOpsSource(viewport), [viewport]);
384
+ const snap = useSyncExternalStore10(source.subscribe, source.getSnapshot, source.getSnapshot);
385
+ const group = useCallback7(() => viewport.groupSelection(), [viewport]);
386
+ const ungroup = useCallback7(() => viewport.ungroupSelection(), [viewport]);
387
+ const toggleLock = useCallback7(() => viewport.toggleLockSelection(), [viewport]);
388
+ const align = useCallback7((edge) => viewport.alignSelection(edge), [viewport]);
389
+ const distribute = useCallback7(
585
390
  (axis) => viewport.distributeSelection(axis),
586
391
  [viewport]
587
392
  );
588
- const rotateCW = useCallback10(() => viewport.rotateSelection("cw"), [viewport]);
589
- const rotateCCW = useCallback10(() => viewport.rotateSelection("ccw"), [viewport]);
393
+ const rotateCW = useCallback7(() => viewport.rotateSelection("cw"), [viewport]);
394
+ const rotateCCW = useCallback7(() => viewport.rotateSelection("ccw"), [viewport]);
590
395
  return {
591
396
  selectedIds: snap.selectedIds,
592
397
  selectedCount: snap.selectedIds.length,
@@ -606,16 +411,16 @@ function useSelectionOps() {
606
411
  }
607
412
 
608
413
  // src/hooks/use-element-rects.ts
609
- import { useCallback as useCallback11, useEffect as useEffect3, useRef as useRef11, useSyncExternalStore as useSyncExternalStore11 } from "react";
414
+ import { useCallback as useCallback8, useEffect as useEffect3, useRef as useRef5, useSyncExternalStore as useSyncExternalStore11 } from "react";
610
415
  import { ElementRectTracker, computeElementRects, elementRectsEqual } from "@fieldnotes/core";
611
416
  function useElementRects(match) {
612
417
  const viewport = useViewport();
613
- const matchRef = useRef11(match);
418
+ const matchRef = useRef5(match);
614
419
  matchRef.current = match;
615
- const stableMatch = useCallback11((el) => matchRef.current(el), []);
616
- const cacheRef = useRef11(null);
617
- const trackerRef = useRef11(null);
618
- const subscribe = useCallback11(
420
+ const stableMatch = useCallback8((el) => matchRef.current(el), []);
421
+ const cacheRef = useRef5(null);
422
+ const trackerRef = useRef5(null);
423
+ const subscribe = useCallback8(
619
424
  (onStoreChange) => {
620
425
  const tracker = new ElementRectTracker(viewport, { match: stableMatch });
621
426
  trackerRef.current = tracker;
@@ -637,7 +442,7 @@ function useElementRects(match) {
637
442
  },
638
443
  [viewport, stableMatch]
639
444
  );
640
- const getSnapshot = useCallback11(() => {
445
+ const getSnapshot = useCallback8(() => {
641
446
  if (cacheRef.current === null) {
642
447
  cacheRef.current = computeElementRects(viewport.store, stableMatch);
643
448
  }
@@ -651,7 +456,7 @@ function useElementRects(match) {
651
456
  }
652
457
 
653
458
  // src/minimap.tsx
654
- import { useEffect as useEffect4, useRef as useRef12, useState as useState3 } from "react";
459
+ import { useEffect as useEffect4, useRef as useRef6, useState as useState3 } from "react";
655
460
  import { MinimapController } from "@fieldnotes/core";
656
461
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
657
462
  var toggleStyle = {
@@ -679,7 +484,7 @@ function Minimap({
679
484
  }) {
680
485
  const viewport = useViewport();
681
486
  const [collapsed, setCollapsed] = useState3(defaultCollapsed);
682
- const canvasRef = useRef12(null);
487
+ const canvasRef = useRef6(null);
683
488
  useEffect4(() => {
684
489
  if (collapsed) return;
685
490
  const canvas = canvasRef.current;