@weasel-js/labkit 1.7.0 → 1.7.2
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 +3 -3
- package/dist/_dts/{CanvasStackContext-D2jnIwfk.d.ts → CanvasStackContext-Cjy6I-Iv.d.ts} +4 -1
- package/dist/_dts/{Stage-CCWfQQWM.d.ts → Stage-D6t7o0pF.d.ts} +3 -2
- package/dist/_dts/{index-CsYqUHD3.d.ts → index-BTmaRy6a.d.ts} +10 -6
- package/dist/_dts/{types-CSm9jgol.d.ts → index-DCMA4Fy1.d.ts} +59 -113
- package/dist/_dts/{types-CRPSGFAp.d.ts → types-BUBSIZuV.d.ts} +8 -7
- package/dist/_dts/{useTrialState-CTZPgeZ7.d.ts → useTrialState-Bh8bzFgE.d.ts} +1 -1
- package/dist/canvas/index.d.ts +8 -6
- package/dist/canvas/index.js +6 -6
- package/dist/chrome/index.d.ts +7 -5
- package/dist/chrome/index.js +10 -9
- package/dist/chunk-2ES4W47N.js +8 -0
- package/dist/chunk-2ES4W47N.js.map +1 -0
- package/dist/{chunk-WPLAANH4.js → chunk-2N7UBSS2.js} +12 -12
- package/dist/chunk-2N7UBSS2.js.map +1 -0
- package/dist/{chunk-275JRRVD.js → chunk-E23Q6A3Y.js} +232 -37
- package/dist/chunk-E23Q6A3Y.js.map +1 -0
- package/dist/{chunk-GOZLNM4L.js → chunk-E6NARGU7.js} +5 -5
- package/dist/{chunk-GOZLNM4L.js.map → chunk-E6NARGU7.js.map} +1 -1
- package/dist/{chunk-IJJUK4FU.js → chunk-EESJUSYR.js} +5 -6
- package/dist/chunk-EESJUSYR.js.map +1 -0
- package/dist/{chunk-HXXTULDN.js → chunk-EFXRM6XQ.js} +8 -11
- package/dist/chunk-EFXRM6XQ.js.map +1 -0
- package/dist/chunk-F4ACIK7Z.js +210 -0
- package/dist/chunk-F4ACIK7Z.js.map +1 -0
- package/dist/{chunk-AWQG7FCC.js → chunk-FG3EXZDV.js} +3 -3
- package/dist/{chunk-AWQG7FCC.js.map → chunk-FG3EXZDV.js.map} +1 -1
- package/dist/{chunk-YT75VYRD.js → chunk-IBVRMHW5.js} +5 -5
- package/dist/{chunk-YT75VYRD.js.map → chunk-IBVRMHW5.js.map} +1 -1
- package/dist/{chunk-RWNDDKOH.js → chunk-PMLZP7RT.js} +18 -15
- package/dist/chunk-PMLZP7RT.js.map +1 -0
- package/dist/{chunk-Y23G2VZL.js → chunk-READTDQX.js} +2 -2
- package/dist/chunk-READTDQX.js.map +1 -0
- package/dist/{chunk-I5DUQ5HM.js → chunk-RODIY7HI.js} +75 -72
- package/dist/chunk-RODIY7HI.js.map +1 -0
- package/dist/{chunk-JVBYV6TL.js → chunk-ROGJWDQA.js} +3 -3
- package/dist/{chunk-JVBYV6TL.js.map → chunk-ROGJWDQA.js.map} +1 -1
- package/dist/{chunk-ZIFPHIWZ.js → chunk-T6DLGEJB.js} +12 -14
- package/dist/chunk-T6DLGEJB.js.map +1 -0
- package/dist/chunk-U6UQVGFP.js +15583 -0
- package/dist/chunk-U6UQVGFP.js.map +1 -0
- package/dist/{chunk-24W5VEJW.js → chunk-ULYYNWGP.js} +2 -2
- package/dist/{chunk-24W5VEJW.js.map → chunk-ULYYNWGP.js.map} +1 -1
- package/dist/{chunk-UNAZSLWE.js → chunk-VYVGVBGY.js} +17 -20
- package/dist/chunk-VYVGVBGY.js.map +1 -0
- package/dist/chunk-WFALF2X2.js +302 -0
- package/dist/chunk-WFALF2X2.js.map +1 -0
- package/dist/{chunk-KMJ7RWD3.js → chunk-WLMDTI2Q.js} +3 -3
- package/dist/{chunk-KMJ7RWD3.js.map → chunk-WLMDTI2Q.js.map} +1 -1
- package/dist/{chunk-T5IBSPAM.js → chunk-WWJDPNZZ.js} +43 -33
- package/dist/chunk-WWJDPNZZ.js.map +1 -0
- package/dist/{chunk-J7OWWTCW.js → chunk-XE5HJOPL.js} +5 -6
- package/dist/chunk-XE5HJOPL.js.map +1 -0
- package/dist/{chunk-7BO7HNGF.js → chunk-XRX5S5NT.js} +8 -61
- package/dist/chunk-XRX5S5NT.js.map +1 -0
- package/dist/chunk-XVUKDSYV.js +38 -0
- package/dist/chunk-XVUKDSYV.js.map +1 -0
- package/dist/{chunk-FMK7UQ6V.js → chunk-XZDCEMEL.js} +64 -47
- package/dist/chunk-XZDCEMEL.js.map +1 -0
- package/dist/{chunk-FZ7YFQN7.js → chunk-YCFKW5RX.js} +3 -3
- package/dist/chunk-YCFKW5RX.js.map +1 -0
- package/dist/{chunk-CQLQPQ4P.js → chunk-ZQIHJXP4.js} +2 -2
- package/dist/chunk-ZQIHJXP4.js.map +1 -0
- package/dist/config/index.d.ts +18 -10
- package/dist/config/index.js +4 -4
- package/dist/controls/index.d.ts +58 -9
- package/dist/controls/index.js +7 -5
- package/dist/dragdrop/index.d.ts +23 -3
- package/dist/dragdrop/index.js +4 -3
- package/dist/fonts/oswald-tabular-digits.woff2 +0 -0
- package/dist/index.d.ts +61 -715
- package/dist/index.js +456 -432
- package/dist/index.js.map +1 -1
- package/dist/job/index.d.ts +8 -0
- package/dist/job/index.js +1 -1
- package/dist/layers/index.d.ts +6 -5
- package/dist/layers/index.js +3 -2
- package/dist/loupe/index.d.ts +100 -30
- package/dist/loupe/index.js +585 -6
- package/dist/loupe/index.js.map +1 -1
- package/dist/overview/index.d.ts +4 -2
- package/dist/overview/index.js +15 -14
- package/dist/overview/index.js.map +1 -1
- package/dist/passthrough/weasel-canvas.d.ts +2 -0
- package/dist/passthrough/weasel-ui.d.ts +2 -2871
- package/dist/passthrough/weasel-ui.js +3 -2
- package/dist/primitives/index.d.ts +213 -3
- package/dist/primitives/index.js +8 -7
- package/dist/state/index.d.ts +7 -5
- package/dist/state/index.js +6 -6
- package/dist/styles.css +284 -51
- package/dist/surface/index.d.ts +10 -2
- package/dist/surface/index.js +1 -1
- package/dist/undo/index.d.ts +5 -3
- package/package.json +11 -11
- package/src/annotations/AnnotationOverlay.abandon.test.tsx +143 -0
- package/src/annotations/AnnotationOverlay.tsx +50 -31
- package/src/annotations/Annotations.overlay.test.tsx +31 -0
- package/src/annotations/capture.browser.test.ts +47 -0
- package/src/annotations/capture.ts +14 -7
- package/src/annotations/drawOne.ts +2 -2
- package/src/annotations/frac.test.ts +44 -1
- package/src/annotations/frac.ts +46 -18
- package/src/annotations/index.ts +1 -0
- package/src/annotations/paint.test.ts +74 -33
- package/src/annotations/paint.ts +70 -32
- package/src/annotations/store.test.ts +212 -2
- package/src/annotations/store.ts +130 -27
- package/src/annotations/svgNodes.test.ts +22 -10
- package/src/annotations/svgNodes.ts +3 -3
- package/src/annotations/types.ts +29 -10
- package/src/canvas/CameraInput.test.tsx +30 -0
- package/src/canvas/CameraInput.tsx +16 -15
- package/src/canvas/CanvasStack.tsx +5 -5
- package/src/canvas/CanvasStackContext.ts +4 -1
- package/src/canvas/Stage.test.tsx +34 -25
- package/src/canvas/Stage.tsx +3 -3
- package/src/canvas/cameraRegistry.ts +1 -0
- package/src/canvas/worldSpec.ts +3 -1
- package/src/chrome/builtins.test.ts +7 -6
- package/src/chrome/builtins.tsx +5 -1
- package/src/chrome/index.ts +1 -1
- package/src/chrome/labRegions.test.tsx +37 -26
- package/src/chrome/merge.test.ts +5 -0
- package/src/chrome/merge.ts +4 -2
- package/src/chrome/regions/PaletteRegion.test.tsx +4 -5
- package/src/chrome/regions/PaletteRegion.tsx +3 -4
- package/src/chrome/regions/ViewportRegion.browser.test.less +6 -0
- package/src/chrome/regions/ViewportRegion.browser.test.tsx +38 -0
- package/src/chrome/regions/ViewportRegion.less +6 -0
- package/src/chrome/regions/ViewportRegion.tsx +10 -4
- package/src/chrome/types.ts +4 -2
- package/src/config/auto.ts +1 -0
- package/src/config/builder.ts +6 -0
- package/src/config/types.ts +2 -1
- package/src/controls/ControlMatrix.less +245 -0
- package/src/controls/ControlMatrix.stories.less +13 -0
- package/src/controls/ControlMatrix.stories.tsx +135 -0
- package/src/controls/ControlMatrix.test.tsx +206 -0
- package/src/controls/ControlMatrix.tsx +446 -0
- package/src/controls/ControlPanel.test.tsx +37 -3
- package/src/controls/ControlPanel.tsx +16 -5
- package/src/controls/index.ts +6 -0
- package/src/dragdrop/DragDropRuntime.tsx +22 -13
- package/src/dragdrop/DragGhost.tsx +3 -0
- package/src/dragdrop/Palette.tsx +2 -0
- package/src/dragdrop/dragDrop.test.tsx +34 -33
- package/src/index.test.ts +5 -0
- package/src/index.ts +7 -1
- package/src/instrument/types.ts +0 -6
- package/src/job/useJob.ts +14 -9
- package/src/lab/Lab.chrome.test.tsx +15 -10
- package/src/lab/Lab.nebula.test.tsx +5 -5
- package/src/lab/Lab.surface.test.tsx +30 -21
- package/src/lab/Lab.test.tsx +75 -74
- package/src/lab/Lab.tsx +5 -1
- package/src/lab/Lab.undock.test.tsx +15 -10
- package/src/lab/LabHeader.test.tsx +15 -14
- package/src/lab/LabPalette.test.tsx +12 -7
- package/src/lab/LabShell.less +1 -1
- package/src/lab/LabSwitcher.browser.test.tsx +27 -0
- package/src/lab/LabSwitcher.less +2 -0
- package/src/lab/LabZoom.test.tsx +55 -44
- package/src/lab/LabZoom.tsx +4 -4
- package/src/lab/Workspace.surface.test.tsx +16 -13
- package/src/lab/Workspace.test.tsx +32 -27
- package/src/lab/Workspace.tsx +48 -52
- package/src/loupe/AGENTS.md +28 -15
- package/src/loupe/CanvasLoupe.tsx +2 -3
- package/src/loupe/DomLoupe.tsx +1 -7
- package/src/loupe/Loupe.less +16 -0
- package/src/loupe/TrialLoupe.tsx +120 -57
- package/src/loupe/index.ts +1 -6
- package/src/loupe/types.test.ts +3 -3
- package/src/loupe/types.ts +24 -27
- package/src/loupe/useLoupe.test.tsx +29 -4
- package/src/loupe/useLoupe.ts +20 -17
- package/src/overview/OverviewMarks.test.tsx +9 -8
- package/src/overview/OverviewMarks.tsx +12 -4
- package/src/overview/TrialOverview.test.tsx +18 -17
- package/src/overview/TrialOverview.tsx +3 -3
- package/src/passthrough/weasel-ui.test.ts +1 -1
- package/src/passthrough/weasel-ui.ts +1 -1
- package/src/primitives/FloatingPanel.tsx +7 -3
- package/src/primitives/FpsMeter.less +1 -2
- package/src/primitives/JobProgress.less +1 -1
- package/src/primitives/Readout.test.tsx +2 -2
- package/src/primitives/ScaleIndicator.less +1 -1
- package/src/primitives/Split.tsx +69 -41
- package/src/primitives/StatusBar.less +1 -1
- package/src/primitives/Toolbar.tsx +3 -3
- package/src/primitives/ZoomControl.less +1 -2
- package/src/specimen/Specimen.less +1 -0
- package/src/specimen/Specimen.tsx +0 -1
- package/src/state/undock.ts +3 -0
- package/src/state/useOpenOnce.ts +20 -16
- package/src/state/view.ts +1 -0
- package/src/styles.less +3 -0
- package/src/surface/index.ts +1 -1
- package/src/surface/useTiledSurface.test.tsx +14 -0
- package/src/surface/useTiledSurface.ts +11 -3
- package/src/trial/Trial.annotations.persist.test.tsx +20 -9
- package/src/trial/Trial.annotations.test.tsx +105 -27
- package/src/trial/Trial.canvas.test.tsx +21 -17
- package/src/trial/Trial.config.test.tsx +34 -27
- package/src/trial/Trial.less +0 -16
- package/src/trial/Trial.loupe.test.tsx +51 -25
- package/src/trial/Trial.targets.test.tsx +9 -8
- package/src/trial/Trial.test.tsx +51 -46
- package/src/trial/Trial.trialId.test.tsx +7 -6
- package/src/trial/Trial.tsx +74 -62
- package/src/trial/TrialChrome.tsx +6 -3
- package/src/trial/index.ts +2 -0
- package/src/trial/loupeSwitch.ts +15 -0
- package/dist/_dts/LayerList.d-DiuEArKq.d.ts +0 -83
- package/dist/_dts/PrefsRow.d-TOZ7BgD9.d.ts +0 -79
- package/dist/_dts/PropertyPanel.d-D-1paCbE.d.ts +0 -141
- package/dist/_dts/Select.d-Bensr_MT.d.ts +0 -579
- package/dist/_dts/index-B2Yj5aA0.d.ts +0 -274
- package/dist/chunk-275JRRVD.js.map +0 -1
- package/dist/chunk-4LJSC7MB.js +0 -143
- package/dist/chunk-4LJSC7MB.js.map +0 -1
- package/dist/chunk-7BO7HNGF.js.map +0 -1
- package/dist/chunk-CQLQPQ4P.js.map +0 -1
- package/dist/chunk-FMK7UQ6V.js.map +0 -1
- package/dist/chunk-FZ7YFQN7.js.map +0 -1
- package/dist/chunk-HXXTULDN.js.map +0 -1
- package/dist/chunk-I5DUQ5HM.js.map +0 -1
- package/dist/chunk-IJJUK4FU.js.map +0 -1
- package/dist/chunk-J7OWWTCW.js.map +0 -1
- package/dist/chunk-M7HP3FTK.js +0 -549
- package/dist/chunk-M7HP3FTK.js.map +0 -1
- package/dist/chunk-MIM5R4ZT.js +0 -14972
- package/dist/chunk-MIM5R4ZT.js.map +0 -1
- package/dist/chunk-RWNDDKOH.js.map +0 -1
- package/dist/chunk-T5IBSPAM.js.map +0 -1
- package/dist/chunk-UNAZSLWE.js.map +0 -1
- package/dist/chunk-WPLAANH4.js.map +0 -1
- package/dist/chunk-Y23G2VZL.js.map +0 -1
- package/dist/chunk-ZIFPHIWZ.js.map +0 -1
- package/src/primitives/useRovingTabIndex.test.ts +0 -48
- package/src/primitives/useRovingTabIndex.ts +0 -88
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useSurfaceOptional } from './chunk-WIG6XHJ7.js';
|
|
2
|
-
import { CameraContext, pointerElsewhere, STAGE_VIEW_ID, normalize2DView, useCameraView, CameraScope, CameraInput } from './chunk-
|
|
3
|
-
import { resolveFrame, DEFAULT_FRAME } from './chunk-
|
|
4
|
-
import { CanvasStackContext } from './chunk-
|
|
2
|
+
import { CameraContext, pointerElsewhere, STAGE_VIEW_ID, normalize2DView, useCameraView, CameraScope, CameraInput } from './chunk-VYVGVBGY.js';
|
|
3
|
+
import { resolveFrame, DEFAULT_FRAME } from './chunk-ULYYNWGP.js';
|
|
4
|
+
import { CanvasStackContext } from './chunk-ZQIHJXP4.js';
|
|
5
|
+
import { usePointerPosition, crosshairRects, useLatest, useVisibleRaf } from '@weasel-js/core';
|
|
5
6
|
import { useContext, useRef, useState, useEffect, useMemo } from 'react';
|
|
6
|
-
import { usePointerPosition, crosshairRects, useVisibleRaf } from '@weasel-js/core';
|
|
7
7
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
8
8
|
|
|
9
9
|
var ARMS = ["left", "right", "up", "down"];
|
|
@@ -141,12 +141,11 @@ function CanvasStack({
|
|
|
141
141
|
ro.observe(el);
|
|
142
142
|
return () => ro.disconnect();
|
|
143
143
|
}, []);
|
|
144
|
-
const onResizeRef =
|
|
145
|
-
onResizeRef.current = onResize;
|
|
144
|
+
const onResizeRef = useLatest(onResize);
|
|
146
145
|
useEffect(() => {
|
|
147
146
|
if (size.width === 0 && size.height === 0) return;
|
|
148
147
|
onResizeRef.current?.({ width: size.width, height: size.height });
|
|
149
|
-
}, [size.width, size.height]);
|
|
148
|
+
}, [size.width, size.height, onResizeRef]);
|
|
150
149
|
const frame = useMemo(() => resolveFrame(worldSpec, size), [worldSpec, size]);
|
|
151
150
|
const camera = useCameraView({
|
|
152
151
|
view: viewProp,
|
|
@@ -165,9 +164,9 @@ function CanvasStack({
|
|
|
165
164
|
() => ({
|
|
166
165
|
view,
|
|
167
166
|
frame,
|
|
168
|
-
surface: { element: containerRef, size, canvases: canvasMap, layers }
|
|
167
|
+
surface: { element: containerRef, size, canvases: canvasMap, layers, worldSpec }
|
|
169
168
|
}),
|
|
170
|
-
[view, frame, size, layers]
|
|
169
|
+
[view, frame, size, layers, worldSpec]
|
|
171
170
|
);
|
|
172
171
|
const containerStyle = { width, height };
|
|
173
172
|
const canvasPx = {
|
|
@@ -250,8 +249,7 @@ function Stage({
|
|
|
250
249
|
}),
|
|
251
250
|
[camera, size.width, size.height]
|
|
252
251
|
);
|
|
253
|
-
const onResizeRef =
|
|
254
|
-
onResizeRef.current = onResize;
|
|
252
|
+
const onResizeRef = useLatest(onResize);
|
|
255
253
|
useEffect(() => {
|
|
256
254
|
const el = host.current;
|
|
257
255
|
if (!el) return;
|
|
@@ -266,7 +264,7 @@ function Stage({
|
|
|
266
264
|
const ro = new ResizeObserver(measure);
|
|
267
265
|
ro.observe(el);
|
|
268
266
|
return () => ro.disconnect();
|
|
269
|
-
}, []);
|
|
267
|
+
}, [onResizeRef]);
|
|
270
268
|
useEffect(() => {
|
|
271
269
|
surface?.invalidateRects();
|
|
272
270
|
}, [surface, view.zoom, view.pan.x, view.pan.y]);
|
|
@@ -298,5 +296,5 @@ function Stage({
|
|
|
298
296
|
}
|
|
299
297
|
|
|
300
298
|
export { CanvasStack, LinkedCursor, Stage, fitStage };
|
|
301
|
-
//# sourceMappingURL=chunk-
|
|
302
|
-
//# sourceMappingURL=chunk-
|
|
299
|
+
//# sourceMappingURL=chunk-T6DLGEJB.js.map
|
|
300
|
+
//# sourceMappingURL=chunk-T6DLGEJB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/canvas/LinkedCursor.tsx","../src/canvas/useLayerScheduler.ts","../src/canvas/CanvasStack.tsx","../src/canvas/Stage.tsx"],"names":["useRef","useEffect","jsx","jsxs","useMemo","useLatest"],"mappings":"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","file":"chunk-T6DLGEJB.js","sourcesContent":["import { crosshairRects, usePointerPosition } from '@weasel-js/core';\nimport { useContext } from 'react';\nimport { CameraContext, pointerElsewhere, STAGE_VIEW_ID } from './CameraInput';\n\n/** `crosshairRects` returns its four arms in this order. */\nconst ARMS = ['left', 'right', 'up', 'down'] as const;\n\n/**\n * The other side of a linked cursor: a crosshair on the camera's own element\n * wherever the shared pointer is over some other view — an overview, say. The\n * same crosshair core paints on a weasel canvas.\n */\nexport function LinkedCursor() {\n const camera = useContext(CameraContext);\n const pointer = usePointerPosition();\n if (!camera) return null;\n const local = pointerElsewhere(pointer, STAGE_VIEW_ID, camera.frame);\n if (!local) return null;\n // `pointerElsewhere` hands back the instrument's world; the camera works\n // frame-local, and the map is its own inverse.\n const v = camera.view.get();\n const x = (local.x - v.x) * v.scale.x;\n const y = (local.y * camera.frame.yDir - v.y) * v.scale.y;\n const { halo, bars } = crosshairRects(x, y, 1);\n const arms = ARMS.map((name, i) => ({ name, halo: halo[i], bar: bars[i] }));\n return (\n <svg className=\"lk-linked-cursor\" aria-hidden=\"true\">\n {arms.map(({ name, halo: r }) =>\n r ? (\n <rect\n key={`halo-${name}`}\n className=\"lk-linked-cursor__halo\"\n x={r.x}\n y={r.y}\n width={r.w}\n height={r.h}\n />\n ) : null,\n )}\n {arms.map(({ name, bar: r }) =>\n r ? (\n <rect\n key={`bar-${name}`}\n className=\"lk-linked-cursor__bar\"\n x={r.x}\n y={r.y}\n width={r.w}\n height={r.h}\n />\n ) : null,\n )}\n </svg>\n );\n}\n","import { useVisibleRaf } from '@weasel-js/core';\nimport { type RefObject, useEffect, useRef } from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { DEFAULT_FRAME, type WorldFrame } from './worldSpec';\n\n/** One layer of a canvas stack: its id, whether it is currently shown, and how\n * it paints itself. */\nexport interface CanvasLayerDescriptor {\n id: string;\n visible: boolean;\n render: (ctx: CanvasRenderingContext2D, view: ViewTransform, frame: WorldFrame) => void;\n}\n\ninterface SchedulerOptions {\n layers: CanvasLayerDescriptor[];\n view: ViewTransform;\n /** The instrument's coordinate system, resolved against `size`. */\n frame?: WorldFrame;\n canvasRefs: RefObject<Map<string, HTMLCanvasElement>>;\n size: { width: number; height: number; dpr: number };\n /** The element the stack occupies. Given one, the scheduler also stops while\n * the stack sits outside the viewport. */\n host?: RefObject<Element | null>;\n}\n\nexport function useLayerScheduler({\n layers,\n view,\n frame = DEFAULT_FRAME,\n canvasRefs,\n size,\n host,\n}: SchedulerOptions): void {\n const dirty = useRef<Set<string>>(new Set());\n const lastRenderRef = useRef<Map<string, CanvasLayerDescriptor['render']>>(new Map());\n\n useEffect(() => {\n for (const layer of layers) {\n const prev = lastRenderRef.current.get(layer.id);\n if (prev !== layer.render) dirty.current.add(layer.id);\n lastRenderRef.current.set(layer.id, layer.render);\n }\n for (const id of [...lastRenderRef.current.keys()]) {\n if (!layers.find((l) => l.id === id)) lastRenderRef.current.delete(id);\n }\n }, [layers]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-mark dirty when view or size changes\n useEffect(() => {\n for (const layer of layers) dirty.current.add(layer.id);\n }, [view, frame, size, layers]);\n\n // Painting only what is dirty is not the same as doing nothing: a hidden tab\n // still commits React updates, and the effect above marks every layer dirty\n // on any view or size change. The gate is what actually stops the work.\n const frameLoop = useVisibleRaf(\n () => {\n if (dirty.current.size === 0) return;\n const map = canvasRefs.current;\n if (!map) {\n dirty.current.clear();\n return;\n }\n for (const layer of layers) {\n if (!dirty.current.has(layer.id)) continue;\n if (!layer.visible) continue;\n const canvas = map.get(layer.id);\n if (!canvas) continue;\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n ctx.save();\n ctx.setTransform(size.dpr, 0, 0, size.dpr, 0, 0);\n ctx.clearRect(0, 0, size.width, size.height);\n layer.render(ctx, view, frame);\n ctx.restore();\n }\n dirty.current.clear();\n },\n { target: host },\n );\n\n useEffect(() => {\n if (dirty.current.size > 0) frameLoop.request();\n });\n}\n","import type { Yoke } from '@weasel-js/core';\nimport { useLatest } from '@weasel-js/core';\nimport { type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport type { Point, ViewTransform } from '../instrument/types';\nimport { normalize2DView } from '../state/view';\nimport { CameraContext, CameraInput, CameraScope, useCameraView } from './CameraInput';\nimport { CanvasStackContext } from './CanvasStackContext';\nimport { LinkedCursor } from './LinkedCursor';\nimport { type CanvasLayerDescriptor, useLayerScheduler } from './useLayerScheduler';\nimport { resolveFrame, type ViewportSize, type WorldSpec } from './worldSpec';\n\n/** Props for `<CanvasStack>`. */\nexport interface CanvasStackProps {\n layers: CanvasLayerDescriptor[];\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n /** The instrument's coordinate system. Omitted, world (0,0) sits at the\n * element's top-left with y running down. */\n worldSpec?: WorldSpec;\n minZoom?: number;\n maxZoom?: number;\n width?: number | string;\n height?: number | string;\n className?: string;\n /** Fired whenever the stack is measured, so a consumer can place a view that\n * only makes sense in terms of the viewport. */\n onResize?: (size: ViewportSize) => void;\n onHitTest?: (worldPos: Point) => void;\n children?: ReactNode;\n /** A yoke to join, from `useYoke()`: cameras on one yoke share their tool\n * and gesture in flight. Omitted, this one keeps its own. */\n yoke?: Yoke;\n}\n\n/** Stacks one `<canvas>` per layer and drives them from a shared view, so a\n * layer that changes rarely is not redrawn with one that changes every frame.\n * Handles sizing and device pixel ratio; pan and zoom route through weasel's\n * gesture dispatcher (`<CameraInput>`). */\nexport function CanvasStack({\n layers,\n view: viewProp,\n onViewChange,\n worldSpec,\n minZoom,\n maxZoom,\n width = '100%',\n height = '100%',\n className,\n onResize,\n onHitTest,\n children,\n yoke,\n}: CanvasStackProps) {\n const view = normalize2DView(viewProp);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasMap = useRef<Map<string, HTMLCanvasElement>>(new Map());\n const [size, setSize] = useState({ width: 0, height: 0, dpr: 1 });\n\n const setCanvasRef = (id: string, el: HTMLCanvasElement | null): void => {\n if (el) canvasMap.current.set(id, el);\n else canvasMap.current.delete(id);\n };\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const update = () => {\n const rect = el.getBoundingClientRect();\n const dpr = window.devicePixelRatio ?? 1;\n setSize((prev) => {\n if (prev.width === rect.width && prev.height === rect.height && prev.dpr === dpr) {\n return prev;\n }\n return { width: rect.width, height: rect.height, dpr };\n });\n };\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const onResizeRef = useLatest(onResize);\n useEffect(() => {\n if (size.width === 0 && size.height === 0) return;\n onResizeRef.current?.({ width: size.width, height: size.height });\n }, [size.width, size.height, onResizeRef]);\n\n const frame = useMemo(() => resolveFrame(worldSpec, size), [worldSpec, size]);\n\n const camera = useCameraView({\n view: viewProp,\n onViewChange,\n frame,\n hostRef: containerRef,\n minZoom,\n maxZoom,\n });\n const cameraCtx = useMemo(\n () => ({ view: camera, frame, element: () => containerRef.current }),\n [camera, frame],\n );\n useLayerScheduler({ layers, view, frame, canvasRefs: canvasMap, size, host: containerRef });\n\n const ctxValue = useMemo(\n () => ({\n view,\n frame,\n surface: { element: containerRef, size, canvases: canvasMap, layers, worldSpec },\n }),\n [view, frame, size, layers, worldSpec],\n );\n\n const containerStyle: CSSProperties = { width, height };\n const canvasPx = {\n width: Math.max(0, Math.round(size.width * size.dpr)),\n height: Math.max(0, Math.round(size.height * size.dpr)),\n };\n const canvasCss: CSSProperties = {\n width: size.width,\n height: size.height,\n };\n\n return (\n <CameraScope yoke={yoke}>\n <CameraContext.Provider value={cameraCtx}>\n <CanvasStackContext.Provider value={ctxValue}>\n <CameraInput hostRef={containerRef} camera={camera} frame={frame} onTap={onHitTest} />\n <div\n ref={containerRef}\n className={className ? `lk-canvas-stack ${className}` : 'lk-canvas-stack'}\n style={containerStyle}\n >\n {layers.map((layer) => (\n <canvas\n key={layer.id}\n ref={(el) => setCanvasRef(layer.id, el)}\n className=\"lk-canvas-stack__canvas\"\n width={canvasPx.width}\n height={canvasPx.height}\n style={{ ...canvasCss, display: layer.visible ? 'block' : 'none' }}\n />\n ))}\n <LinkedCursor />\n <div className=\"lk-canvas-stack__overlay\">{children}</div>\n </div>\n </CanvasStackContext.Provider>\n </CameraContext.Provider>\n </CameraScope>\n );\n}\n","import type { Yoke } from '@weasel-js/core';\nimport { useLatest } from '@weasel-js/core';\nimport { type CSSProperties, type ReactNode, useEffect, useMemo, useRef } from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { normalize2DView } from '../state/view';\nimport { useSurfaceOptional } from '../surface/useSurfaceTile';\nimport { CameraContext, CameraInput, CameraScope, useCameraView } from './CameraInput';\nimport { LinkedCursor } from './LinkedCursor';\nimport { DEFAULT_FRAME, type ViewportSize } from './worldSpec';\n\n/** Props for `<Stage>`. */\nexport interface StageProps {\n /** The content's own size in CSS pixels, at zoom 1. */\n size: ViewportSize;\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n minZoom?: number;\n maxZoom?: number;\n /** Fired whenever the stage's viewport is measured at a non-zero size. */\n onResize?: (viewport: ViewportSize) => void;\n /** Handed the viewport element, for a consumer tracking the pointer over it. */\n hostRef?: { current: HTMLDivElement | null };\n /** Drawn over the content in viewport pixels, outside the camera. */\n overlay?: ReactNode;\n children?: ReactNode;\n /** A yoke to join, from `useYoke()`: cameras on one yoke share their tool\n * and gesture in flight. Omitted, this one keeps its own. */\n yoke?: Yoke;\n}\n\n/**\n * The camera a stage opens at: the content centered in its viewport, shrunk to\n * fit if it has to be and never enlarged past its own size.\n */\nexport function fitStage(size: ViewportSize, viewport: ViewportSize): ViewTransform {\n if (!(size.width > 0 && size.height > 0 && viewport.width > 0 && viewport.height > 0)) {\n return { zoom: 1, pan: { x: 0, y: 0 } };\n }\n const zoom = Math.min(1, viewport.width / size.width, viewport.height / size.height);\n return {\n zoom,\n pan: {\n x: (viewport.width - size.width * zoom) / 2,\n y: (viewport.height - size.height * zoom) / 2,\n },\n };\n}\n\n/**\n * DOM content of a fixed size, panned and zoomed by a camera — what\n * `<CanvasStack>` is for layers drawn to a canvas. Pan and zoom route through\n * weasel's gesture dispatcher (`<CameraInput>`). The content is laid out at\n * its own size and transformed, so everything inside keeps its layout and a\n * measurement of any element in it reports where it is actually drawn.\n */\nexport function Stage({\n size,\n view: viewProp,\n onViewChange,\n minZoom,\n maxZoom,\n onResize,\n hostRef,\n overlay,\n children,\n yoke,\n}: StageProps) {\n const view = normalize2DView(viewProp);\n const host = useRef<HTMLDivElement | null>(null);\n const surface = useSurfaceOptional();\n const camera = useCameraView({\n view: viewProp,\n onViewChange,\n frame: DEFAULT_FRAME,\n hostRef: host,\n minZoom,\n maxZoom,\n });\n const cameraCtx = useMemo(\n () => ({\n view: camera,\n frame: DEFAULT_FRAME,\n element: () => host.current,\n content: { x: 0, y: 0, width: size.width, height: size.height },\n }),\n [camera, size.width, size.height],\n );\n\n const onResizeRef = useLatest(onResize);\n useEffect(() => {\n const el = host.current;\n if (!el) return;\n const measure = () => {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n onResizeRef.current?.({ width: rect.width, height: rect.height });\n }\n };\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(measure);\n ro.observe(el);\n return () => ro.disconnect();\n }, [onResizeRef]);\n\n // A transform moves every tile inside the content without resizing anything,\n // and a ResizeObserver reports only resizes.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the camera is the trigger, not an input — the effect re-measures whenever it moves.\n useEffect(() => {\n surface?.invalidateRects();\n }, [surface, view.zoom, view.pan.x, view.pan.y]);\n\n // The camera changes every frame of a pan, and a transform has nowhere to\n // live but the element's own style: set as custom properties the stylesheet\n // reads.\n const cameraVars = {\n ['--lk-stage-x' as string]: `${view.pan.x}px`,\n ['--lk-stage-y' as string]: `${view.pan.y}px`,\n ['--lk-stage-zoom' as string]: String(view.zoom),\n ['--lk-stage-w' as string]: `${size.width}px`,\n ['--lk-stage-h' as string]: `${size.height}px`,\n } as CSSProperties;\n\n return (\n <CameraScope yoke={yoke}>\n <CameraContext.Provider value={cameraCtx}>\n <CameraInput hostRef={host} camera={camera} frame={DEFAULT_FRAME} />\n <div\n ref={(el) => {\n host.current = el;\n if (hostRef) hostRef.current = el;\n }}\n className=\"lk-stage\"\n >\n <div className=\"lk-stage__content\" style={cameraVars}>\n {children}\n </div>\n <LinkedCursor />\n {overlay}\n </div>\n </CameraContext.Provider>\n </CameraScope>\n );\n}\n"]}
|