@weasel-js/labkit 1.6.0 → 1.7.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.
Files changed (222) hide show
  1. package/dist/_dts/{CanvasStackContext-DQgcH1ny.d.ts → CanvasStackContext-D2jnIwfk.d.ts} +1 -1
  2. package/dist/_dts/FloatingPanel-7j0IcXwz.d.ts +30 -0
  3. package/dist/_dts/{LayerList.d-2c9yup5Y.d.ts → LayerList.d-DiuEArKq.d.ts} +4 -4
  4. package/dist/_dts/PrefsRow.d-TOZ7BgD9.d.ts +79 -0
  5. package/dist/_dts/PropertyPanel.d-D-1paCbE.d.ts +141 -0
  6. package/dist/_dts/{Select.d-BchGMC2Y.d.ts → Select.d-Bensr_MT.d.ts} +15 -6
  7. package/dist/_dts/Stage-CCWfQQWM.d.ts +202 -0
  8. package/dist/_dts/index-B2Yj5aA0.d.ts +274 -0
  9. package/dist/_dts/{index-B0--8PEM.d.ts → index-CsYqUHD3.d.ts} +12 -13
  10. package/dist/_dts/{types-B7y8hX1D.d.ts → types-CRPSGFAp.d.ts} +33 -5
  11. package/dist/_dts/{frac-DTRq7Xpb.d.ts → types-CSm9jgol.d.ts} +700 -650
  12. package/dist/_dts/{useTrialState-CP4jfuKl.d.ts → useTrialState-CTZPgeZ7.d.ts} +2 -3
  13. package/dist/canvas/index.d.ts +40 -8
  14. package/dist/canvas/index.js +5 -3
  15. package/dist/chrome/index.d.ts +5 -6
  16. package/dist/chrome/index.js +9 -7
  17. package/dist/chunk-24W5VEJW.js +18 -0
  18. package/dist/chunk-24W5VEJW.js.map +1 -0
  19. package/dist/chunk-275JRRVD.js +728 -0
  20. package/dist/chunk-275JRRVD.js.map +1 -0
  21. package/dist/chunk-3RY6WL5T.js +38 -0
  22. package/dist/chunk-3RY6WL5T.js.map +1 -0
  23. package/dist/chunk-4LJSC7MB.js +143 -0
  24. package/dist/chunk-4LJSC7MB.js.map +1 -0
  25. package/dist/{chunk-3FI7Y4R4.js → chunk-7BO7HNGF.js} +5 -5
  26. package/dist/{chunk-3FI7Y4R4.js.map → chunk-7BO7HNGF.js.map} +1 -1
  27. package/dist/chunk-AWQG7FCC.js +19 -0
  28. package/dist/chunk-AWQG7FCC.js.map +1 -0
  29. package/dist/chunk-CZZPXUOU.js +3 -0
  30. package/dist/{chunk-SBXNQW4G.js.map → chunk-CZZPXUOU.js.map} +1 -1
  31. package/dist/{chunk-ZORVNOKX.js → chunk-FMK7UQ6V.js} +24 -145
  32. package/dist/chunk-FMK7UQ6V.js.map +1 -0
  33. package/dist/{chunk-BVL7Y574.js → chunk-FZ7YFQN7.js} +26 -10
  34. package/dist/chunk-FZ7YFQN7.js.map +1 -0
  35. package/dist/{chunk-USO6TERP.js → chunk-GOZLNM4L.js} +11 -9
  36. package/dist/chunk-GOZLNM4L.js.map +1 -0
  37. package/dist/{chunk-H5VIV7MQ.js → chunk-I5DUQ5HM.js} +88 -82
  38. package/dist/chunk-I5DUQ5HM.js.map +1 -0
  39. package/dist/chunk-J7OWWTCW.js +143 -0
  40. package/dist/chunk-J7OWWTCW.js.map +1 -0
  41. package/dist/{chunk-H6ZAOWNE.js → chunk-JVBYV6TL.js} +3 -3
  42. package/dist/{chunk-H6ZAOWNE.js.map → chunk-JVBYV6TL.js.map} +1 -1
  43. package/dist/{chunk-VQ47KWD5.js → chunk-KMJ7RWD3.js} +18 -7
  44. package/dist/chunk-KMJ7RWD3.js.map +1 -0
  45. package/dist/{chunk-ERFMC6WU.js → chunk-M7HP3FTK.js} +20 -11
  46. package/dist/chunk-M7HP3FTK.js.map +1 -0
  47. package/dist/chunk-MIM5R4ZT.js +14972 -0
  48. package/dist/chunk-MIM5R4ZT.js.map +1 -0
  49. package/dist/{chunk-HFRE4QQV.js → chunk-T5IBSPAM.js} +96 -32
  50. package/dist/chunk-T5IBSPAM.js.map +1 -0
  51. package/dist/chunk-UNAZSLWE.js +294 -0
  52. package/dist/chunk-UNAZSLWE.js.map +1 -0
  53. package/dist/{chunk-RS3HRQWU.js → chunk-WPLAANH4.js} +4 -3
  54. package/dist/chunk-WPLAANH4.js.map +1 -0
  55. package/dist/{chunk-AWH3SRPW.js → chunk-Y23G2VZL.js} +5 -37
  56. package/dist/chunk-Y23G2VZL.js.map +1 -0
  57. package/dist/{chunk-MOLIUNVH.js → chunk-YT75VYRD.js} +4 -4
  58. package/dist/{chunk-MOLIUNVH.js.map → chunk-YT75VYRD.js.map} +1 -1
  59. package/dist/chunk-ZIFPHIWZ.js +302 -0
  60. package/dist/chunk-ZIFPHIWZ.js.map +1 -0
  61. package/dist/config/index.d.ts +48 -40
  62. package/dist/config/index.js +4 -5
  63. package/dist/controls/index.d.ts +91 -7
  64. package/dist/controls/index.js +5 -4
  65. package/dist/dragdrop/index.d.ts +9 -7
  66. package/dist/dragdrop/index.js +3 -2
  67. package/dist/index.d.ts +294 -82
  68. package/dist/index.js +425 -402
  69. package/dist/index.js.map +1 -1
  70. package/dist/layers/index.d.ts +4 -5
  71. package/dist/layers/index.js +2 -1
  72. package/dist/loupe/index.d.ts +20 -17
  73. package/dist/loupe/index.js +5 -3
  74. package/dist/overview/index.d.ts +56 -0
  75. package/dist/overview/index.js +289 -0
  76. package/dist/overview/index.js.map +1 -0
  77. package/dist/passthrough/weasel-ui.d.ts +239 -69
  78. package/dist/passthrough/weasel-ui.js +3 -2
  79. package/dist/primitives/index.d.ts +5 -204
  80. package/dist/primitives/index.js +6 -4
  81. package/dist/state/index.d.ts +8 -8
  82. package/dist/state/index.js +4 -5
  83. package/dist/state/index.js.map +1 -1
  84. package/dist/styles.css +152 -82
  85. package/dist/undo/index.d.ts +4 -4
  86. package/package.json +17 -12
  87. package/src/annotations/AnnotationOverlay.tsx +2 -2
  88. package/src/annotations/Annotations.less +0 -10
  89. package/src/annotations/MarkList.tsx +4 -8
  90. package/src/annotations/index.ts +1 -0
  91. package/src/annotations/paint.test.ts +3 -14
  92. package/src/annotations/paint.ts +3 -19
  93. package/src/annotations/store.test.ts +26 -0
  94. package/src/annotations/store.ts +25 -6
  95. package/src/annotations/svgNodes.test.ts +6 -9
  96. package/src/annotations/svgNodes.ts +3 -3
  97. package/src/annotations/toolMap.test.ts +37 -2
  98. package/src/annotations/toolMap.ts +13 -0
  99. package/src/annotations/types.ts +27 -0
  100. package/src/canvas/AGENTS.md +9 -10
  101. package/src/canvas/CameraInput.test.tsx +136 -0
  102. package/src/canvas/CameraInput.tsx +322 -0
  103. package/src/canvas/CanvasStack.tsx +47 -58
  104. package/src/canvas/LinkedCursor.less +16 -0
  105. package/src/canvas/LinkedCursor.tsx +54 -0
  106. package/src/canvas/Stage.test.tsx +33 -4
  107. package/src/canvas/Stage.tsx +47 -37
  108. package/src/canvas/cameraRegistry.test.ts +55 -0
  109. package/src/canvas/cameraRegistry.ts +86 -0
  110. package/src/canvas/cameraView.test.ts +60 -0
  111. package/src/canvas/cameraView.ts +61 -0
  112. package/src/canvas/cameraZoom.test.ts +46 -0
  113. package/src/canvas/cameraZoom.ts +24 -0
  114. package/src/canvas/index.ts +23 -2
  115. package/src/canvas/sharedScope.test.tsx +94 -0
  116. package/src/chrome/ChromeRegions.stories.tsx +1 -0
  117. package/src/chrome/regions/PaletteRegion.less +0 -7
  118. package/src/chrome/regions/SidebarRegion.less +12 -2
  119. package/src/chrome/regions/SidebarRegion.test.tsx +9 -2
  120. package/src/config/builder.test.ts +2 -1
  121. package/src/config/builder.ts +15 -8
  122. package/src/config/entry.test.ts +3 -1
  123. package/src/config/fromConfigField.test.ts +14 -0
  124. package/src/config/fromConfigField.ts +19 -4
  125. package/src/config/index.ts +4 -1
  126. package/src/config/path.ts +2 -5
  127. package/src/config/resolve.ts +15 -1
  128. package/src/config/sectionTree.test.ts +12 -6
  129. package/src/config/sectionTree.ts +15 -5
  130. package/src/config/types.ts +34 -9
  131. package/src/controls/ControlPanel.less +1 -1
  132. package/src/controls/ControlPanel.stories.tsx +3 -2
  133. package/src/controls/ControlPanel.test.tsx +76 -13
  134. package/src/controls/ControlPanel.tsx +80 -11
  135. package/src/controls/inDialog.test.tsx +18 -0
  136. package/src/controls/inDialog.tsx +2 -10
  137. package/src/index.test.ts +1 -1
  138. package/src/index.ts +23 -5
  139. package/src/instrument/SineWave.smoke.test.tsx +2 -0
  140. package/src/instrument/types.ts +10 -1
  141. package/src/lab/Lab.nebula.test.tsx +11 -2
  142. package/src/lab/Lab.test.tsx +3 -1
  143. package/src/lab/Lab.tsx +87 -76
  144. package/src/lab/{LabFit.stories.less → LabFit.browser.test.less} +1 -1
  145. package/src/lab/{LabFit.stories.tsx → LabFit.browser.test.tsx} +41 -63
  146. package/src/lab/LabFullChrome.stories.tsx +1 -0
  147. package/src/lab/LabHeader.test.tsx +24 -14
  148. package/src/lab/LabHeader.tsx +11 -12
  149. package/src/lab/LabShell.less +8 -17
  150. package/src/lab/LabSwitcher.less +6 -6
  151. package/src/lab/LabZoom.test.tsx +223 -0
  152. package/src/lab/LabZoom.tsx +170 -0
  153. package/src/lab/index.ts +1 -0
  154. package/src/loupe/AGENTS.md +7 -6
  155. package/src/loupe/LoupeGestures.tsx +15 -10
  156. package/src/loupe/TrialLoupe.tsx +5 -5
  157. package/src/loupe/index.ts +1 -0
  158. package/src/loupe/loupeActions.ts +6 -2
  159. package/src/overview/AGENTS.md +38 -0
  160. package/src/overview/OverviewMarks.test.tsx +100 -0
  161. package/src/overview/OverviewMarks.tsx +93 -0
  162. package/src/overview/TrialOverview.less +53 -0
  163. package/src/overview/TrialOverview.stories.tsx +50 -0
  164. package/src/overview/TrialOverview.test.tsx +116 -0
  165. package/src/overview/TrialOverview.tsx +376 -0
  166. package/src/overview/index.ts +2 -0
  167. package/src/passthrough/weasel-ui.test.ts +2 -0
  168. package/src/passthrough/weasel-ui.ts +23 -4
  169. package/src/primitives/FloatingPanel.test.tsx +11 -0
  170. package/src/primitives/FloatingPanel.tsx +2 -5
  171. package/src/primitives/JobProgress.less +1 -1
  172. package/src/primitives/Legend.stories.tsx +1 -0
  173. package/src/primitives/Readout.browser.test.tsx +137 -0
  174. package/src/primitives/Readout.less +12 -0
  175. package/src/primitives/Readout.stories.tsx +58 -0
  176. package/src/primitives/Readout.test.tsx +93 -0
  177. package/src/primitives/Readout.tsx +56 -0
  178. package/src/primitives/index.ts +2 -0
  179. package/src/specimen/Specimen.stories.tsx +1 -0
  180. package/src/specimen/Specimen.tsx +21 -4
  181. package/src/state/Persistence.browser.test.tsx +47 -0
  182. package/src/state/labRecords.test.ts +4 -1
  183. package/src/state/store.test.ts +9 -5
  184. package/src/state/usePersistedState.test.tsx +1 -1
  185. package/src/state/useTrialState.test.tsx +1 -1
  186. package/src/styles.less +3 -0
  187. package/src/theme/Interstellar.stories.tsx +4 -6
  188. package/src/theme/base.less +21 -47
  189. package/src/theme/interstellar.test.ts +1 -1
  190. package/src/theme/interstellar.theme.json +10 -3
  191. package/src/tools/labTool.ts +6 -2
  192. package/src/trial/Trial.annotations.test.tsx +16 -0
  193. package/src/trial/Trial.less +0 -13
  194. package/src/trial/Trial.loupe.test.tsx +73 -1
  195. package/src/trial/Trial.stories.tsx +2 -0
  196. package/src/trial/Trial.tsx +23 -1
  197. package/src/trial/TrialChrome.tsx +21 -3
  198. package/src/trial/trialOps.test.ts +4 -4
  199. package/dist/_dts/PrefsRow.d-DFpPIeIC.d.ts +0 -39
  200. package/dist/_dts/inDialog-D5ZKGjiD.d.ts +0 -23
  201. package/dist/_dts/index-DINmCuLA.d.ts +0 -453
  202. package/dist/_dts/number.d-OeQYQoUO.d.ts +0 -28
  203. package/dist/_dts/usePanZoom-Bq73pwFe.d.ts +0 -117
  204. package/dist/chunk-2AYGEN57.js +0 -32
  205. package/dist/chunk-2AYGEN57.js.map +0 -1
  206. package/dist/chunk-AWH3SRPW.js.map +0 -1
  207. package/dist/chunk-BVL7Y574.js.map +0 -1
  208. package/dist/chunk-ERFMC6WU.js.map +0 -1
  209. package/dist/chunk-H5VIV7MQ.js.map +0 -1
  210. package/dist/chunk-HFRE4QQV.js.map +0 -1
  211. package/dist/chunk-RS3HRQWU.js.map +0 -1
  212. package/dist/chunk-SBXNQW4G.js +0 -3
  213. package/dist/chunk-TT5C5S6R.js +0 -368
  214. package/dist/chunk-TT5C5S6R.js.map +0 -1
  215. package/dist/chunk-USO6TERP.js.map +0 -1
  216. package/dist/chunk-VQ47KWD5.js.map +0 -1
  217. package/dist/chunk-WUJXJC5O.js +0 -14568
  218. package/dist/chunk-WUJXJC5O.js.map +0 -1
  219. package/dist/chunk-ZORVNOKX.js.map +0 -1
  220. package/src/canvas/usePanZoom.test.ts +0 -244
  221. package/src/canvas/usePanZoom.ts +0 -147
  222. package/src/state/Persistence.stories.tsx +0 -75
@@ -1,9 +1,8 @@
1
- export { L as LayerList, a as LayerListItem, b as LayerListProps, c as LayerMove, m as moveLayers } from '../_dts/LayerList.d-2c9yup5Y.js';
2
- export { L as LayerCapability, a as LayerDescriptor } from '../_dts/frac-DTRq7Xpb.js';
3
- import 'react/jsx-runtime';
1
+ export { L as LayerList, a as LayerListItem, b as LayerListProps, c as LayerMove, m as moveLayers } from '../_dts/LayerList.d-DiuEArKq.js';
2
+ export { L as LayerCapability, a as LayerDescriptor } from '../_dts/types-CSm9jgol.js';
4
3
  import 'react';
5
- import '../_dts/PrefsRow.d-DFpPIeIC.js';
4
+ import '../_dts/PrefsRow.d-TOZ7BgD9.js';
6
5
  import '@weasel-js/theme';
7
6
  import '@weasel-js/core';
8
- import '../_dts/types-B7y8hX1D.js';
7
+ import '../_dts/types-CRPSGFAp.js';
9
8
  import '../_dts/types-DJ79Tg5J.js';
@@ -1,4 +1,5 @@
1
1
  import '../chunk-2RVCB2PT.js';
2
- export { D as LayerList, S2 as moveLayers } from '../chunk-WUJXJC5O.js';
2
+ export { E3 as LayerList, x3 as moveLayers } from '../chunk-MIM5R4ZT.js';
3
+ import '../chunk-275JRRVD.js';
3
4
  //# sourceMappingURL=index.js.map
4
5
  //# sourceMappingURL=index.js.map
@@ -1,12 +1,14 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { aH as LoupePoint, aI as LoupeMode, m as ViewTransform, W as WorldFrame, b as WorldSpec, V as ViewportSize, ae as LoupeRenderArgs, ah as ResolvedLoupe } from '../_dts/frac-DTRq7Xpb.js';
3
- export { ab as LOUPE_DEFAULTS, ac as LoupeCapability, ad as LoupeDeclaration, aE as resolveLoupe } from '../_dts/frac-DTRq7Xpb.js';
4
- import { c as CanvasStackSurface, C as CanvasLayerDescriptor } from '../_dts/CanvasStackContext-DQgcH1ny.js';
1
+ import { ai as LoupePoint, ah as LoupeMode, aj as LoupeRenderArgs, am as ResolvedLoupe } from '../_dts/types-CSm9jgol.js';
2
+ import { WorldFrame, WorldSpec, ViewportSize } from '../canvas/index.js';
3
+ import { ViewTransform } from '../index.js';
4
+ export { ae as LOUPE_DEFAULTS, af as LoupeCapability, ag as LoupeDeclaration, aK as resolveLoupe } from '../_dts/types-CSm9jgol.js';
5
+ import * as react from 'react';
5
6
  import { RefObject, ReactNode } from 'react';
7
+ import { CanvasStackSurface, CanvasLayerDescriptor } from '../canvas/index.js';
6
8
  import { Action } from '@weasel-js/core';
7
- import '../_dts/PrefsRow.d-DFpPIeIC.js';
9
+ import '../_dts/PrefsRow.d-TOZ7BgD9.js';
8
10
  import '@weasel-js/theme';
9
- import '../_dts/types-B7y8hX1D.js';
11
+ import '../_dts/types-CRPSGFAp.js';
10
12
  import '../_dts/types-DJ79Tg5J.js';
11
13
 
12
14
  /** Props for `<CanvasLoupe>`. */
@@ -26,7 +28,7 @@ interface CanvasLoupeProps {
26
28
  * a zoomed camera — so the magnified content is as sharp as the original, at
27
29
  * any factor. `pixel` mode enlarges what the stack presented instead.
28
30
  */
29
- declare function CanvasLoupe({ aim, factor, mode, diameter, surface, view, frame, worldSpec, }: CanvasLoupeProps): react_jsx_runtime.JSX.Element;
31
+ declare function CanvasLoupe({ aim, factor, mode, diameter, surface, view, frame, worldSpec, }: CanvasLoupeProps): react.JSX.Element;
30
32
 
31
33
  /** The camera a round lens of `diameter` shows a magnified region through, and
32
34
  * the coordinate system it is read in — the instrument's own `WorldSpec`,
@@ -99,7 +101,7 @@ interface DomLoupeProps {
99
101
  * where it already was, and the stage is then slid by `diameter / 2 - aim` to
100
102
  * bring it to the middle of the lens.
101
103
  */
102
- declare function DomLoupe({ aim, factor, mode, diameter, size, view, frame, state, config, render, }: DomLoupeProps): react_jsx_runtime.JSX.Element;
104
+ declare function DomLoupe({ aim, factor, mode, diameter, size, view, frame, state, config, render, }: DomLoupeProps): react.JSX.Element;
103
105
 
104
106
  /** Props for `<LoupeBubble>`. */
105
107
  interface LoupeBubbleProps {
@@ -117,7 +119,7 @@ interface LoupeBubbleProps {
117
119
  * keep working while it is up — and it is `aria-hidden`, since it magnifies
118
120
  * content already on the page rather than adding any.
119
121
  */
120
- declare function LoupeBubble({ aim, diameter, hostRef, children }: LoupeBubbleProps): react_jsx_runtime.JSX.Element;
122
+ declare function LoupeBubble({ aim, diameter, hostRef, children }: LoupeBubbleProps): react.JSX.Element;
121
123
 
122
124
  /**
123
125
  * The loupe's two bindable interactions, as `Action` descriptors the gesture
@@ -136,8 +138,8 @@ declare const LOUPE_MAGNIFY_ID = "loupe.magnify";
136
138
  * registration. */
137
139
  interface LoupeInputApi {
138
140
  /** Whether the lens is up. The wheel belongs to the loupe only while it is;
139
- * otherwise `loupe.magnify` declines and the dispatcher leaves the event
140
- * unhandled, which is what hands it back to the lab's own pan/zoom. */
141
+ * otherwise `loupe.magnify` declines and the dispatcher falls through to
142
+ * the trial camera's zoom. */
141
143
  shown: () => boolean;
142
144
  /** Raise or drop the hold-to-peek flag. */
143
145
  setPeeking: (on: boolean) => void;
@@ -160,11 +162,12 @@ interface LoupeGesturesProps {
160
162
  peekKey: string | null;
161
163
  }
162
164
  /**
163
- * The loupe's input, routed. Render it inside a `<WeaselProvider isolate>` —
164
- * with no registry in scope it renders nothing rather than registering
165
- * actions that could never fire.
165
+ * The loupe's input, routed. Inside a camera (`<CanvasStack>`, `<Stage>`) its
166
+ * actions join the camera's dispatcher, which already listens on the host;
167
+ * elsewhere it mounts a dispatcher of its own. With no registry in scope it
168
+ * renders nothing rather than registering actions that could never fire.
166
169
  */
167
- declare function LoupeGestures(props: LoupeGesturesProps): react_jsx_runtime.JSX.Element | null;
170
+ declare function LoupeGestures(props: LoupeGesturesProps): react.JSX.Element | null;
168
171
 
169
172
  /** Props for `<TrialLoupe>`. */
170
173
  interface TrialLoupeProps {
@@ -185,7 +188,7 @@ interface TrialLoupeProps {
185
188
  * stack it is mounted inside, or the instrument's own `render` at a magnified
186
189
  * camera.
187
190
  */
188
- declare function TrialLoupe({ capability, enabled, state, config, view, worldSpec, hostRef, }: TrialLoupeProps): react_jsx_runtime.JSX.Element;
191
+ declare function TrialLoupe({ capability, enabled, state, config, view, worldSpec, hostRef, }: TrialLoupeProps): react.JSX.Element;
189
192
 
190
193
  /** Options for {@link useLoupe}. */
191
194
  interface UseLoupeOptions {
@@ -228,5 +231,5 @@ interface LoupeState {
228
231
  */
229
232
  declare function useLoupe({ capability, hostRef, enabled, sample }: UseLoupeOptions): LoupeState;
230
233
 
231
- export { CanvasLoupe, DomLoupe, LOUPE_MAGNIFY_ID, LOUPE_PEEK_ID, LoupeBubble, LoupeGestures, LoupeRenderArgs, ResolvedLoupe, TrialLoupe, createLoupeActions, drawCanvasLens, lensCamera, lensSourceRect, sampleStack, useLoupe };
234
+ export { CanvasLoupe, DomLoupe, LOUPE_MAGNIFY_ID, LOUPE_PEEK_ID, LoupeBubble, LoupeGestures, LoupeMode, LoupePoint, LoupeRenderArgs, ResolvedLoupe, TrialLoupe, createLoupeActions, drawCanvasLens, lensCamera, lensSourceRect, sampleStack, useLoupe };
232
235
  export type { CanvasLoupeProps, DomLoupeProps, LensCamera, LoupeBubbleProps, LoupeGesturesProps, LoupeInputApi, LoupeState, SourceRect, TrialLoupeProps, UseLoupeOptions };
@@ -1,6 +1,8 @@
1
- export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LOUPE_MAGNIFY_ID, LOUPE_PEEK_ID, LoupeBubble, LoupeGestures, TrialLoupe, createLoupeActions, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from '../chunk-ERFMC6WU.js';
2
- import '../chunk-H6ZAOWNE.js';
3
- import '../chunk-2AYGEN57.js';
1
+ export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LOUPE_MAGNIFY_ID, LOUPE_PEEK_ID, LoupeBubble, LoupeGestures, TrialLoupe, createLoupeActions, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from '../chunk-M7HP3FTK.js';
2
+ import '../chunk-JVBYV6TL.js';
3
+ import '../chunk-AWQG7FCC.js';
4
+ import '../chunk-UNAZSLWE.js';
5
+ import '../chunk-24W5VEJW.js';
4
6
  import '../chunk-CQLQPQ4P.js';
5
7
  //# sourceMappingURL=index.js.map
6
8
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,56 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { ViewportSize } from '../canvas/index.js';
4
+ import { FloatingPanelProps } from '../index.js';
5
+ import '@weasel-js/core';
6
+ import '../_dts/PrefsRow.d-TOZ7BgD9.js';
7
+ import '@weasel-js/theme';
8
+ import '../_dts/types-CRPSGFAp.js';
9
+ import '../_dts/types-DJ79Tg5J.js';
10
+ import 'windease';
11
+
12
+ /** The view id the overview publishes its pointer under. */
13
+ declare const OVERVIEW_VIEW_ID = "overview";
14
+ /** A rect in the instrument's world. */
15
+ interface OverviewBounds {
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ }
21
+ /** What an overview's `render` is handed. */
22
+ interface OverviewRenderArgs {
23
+ /** The content's own size in CSS pixels, at zoom 1 — lay out at this size
24
+ * and the overview scales it to fit. */
25
+ size: ViewportSize;
26
+ }
27
+ /** Props for `<TrialOverview>`. */
28
+ interface TrialOverviewProps {
29
+ /** The overview box, in CSS pixels. */
30
+ width: number;
31
+ height: number;
32
+ /**
33
+ * DOM content for the overview, laid out at the content's own size — for a
34
+ * `stage` instrument, typically a lighter copy of what its `render` draws.
35
+ * Never the instrument's own `render`: its effects would run twice. A canvas
36
+ * instrument omits it, and the overview redraws the canvas's layers.
37
+ */
38
+ render?: (args: OverviewRenderArgs) => ReactNode;
39
+ /** What to frame, in the instrument's world. Defaults to a stage's content
40
+ * rect; a canvas instrument, whose world has no edge, names one. */
41
+ bounds?: OverviewBounds;
42
+ /** Panel title. */
43
+ title?: string;
44
+ anchor?: FloatingPanelProps['anchor'];
45
+ persist?: string;
46
+ className?: string;
47
+ }
48
+ /**
49
+ * A trial's overview. Mount it from an instrument's `render` (a canvas
50
+ * instrument) or `stage.overlay` (a DOM stage) — anywhere inside the trial's
51
+ * camera.
52
+ */
53
+ declare function TrialOverview(props: TrialOverviewProps): react.JSX.Element | null;
54
+
55
+ export { OVERVIEW_VIEW_ID, TrialOverview };
56
+ export type { OverviewBounds, OverviewRenderArgs, TrialOverviewProps };
@@ -0,0 +1,289 @@
1
+ import { markSvgNodes } from '../chunk-4LJSC7MB.js';
2
+ import { useAnnotationsOptional } from '../chunk-3RY6WL5T.js';
3
+ import { FloatingPanel } from '../chunk-J7OWWTCW.js';
4
+ import '../chunk-RWNDDKOH.js';
5
+ import { CameraContext, fromCameraView, pointerElsewhere, usePublishPointer } from '../chunk-UNAZSLWE.js';
6
+ import '../chunk-24W5VEJW.js';
7
+ import { CanvasStackContext } from '../chunk-CQLQPQ4P.js';
8
+ import '../chunk-2YILQ7Y5.js';
9
+ import { serializeSvg } from '../chunk-275JRRVD.js';
10
+ import { usePointerPosition, crosshairRects, DepRegistryProvider, ActionsProvider, ActiveToolContextProvider, useActionsRegistry, useDepSource, minimapCenterAction, minimapPanAction, useGestureDispatcher, MINIMAP_CENTER, MINIMAP_PAN } from '@weasel-js/core';
11
+ import { useContext, useMemo, useRef, useLayoutEffect, useEffect, useReducer } from 'react';
12
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
+
14
+ function localRectOf(el, camera) {
15
+ const host = camera.element();
16
+ if (!host) return null;
17
+ const r = el.getBoundingClientRect();
18
+ const h = host.getBoundingClientRect();
19
+ const v = camera.view.get();
20
+ return {
21
+ x: (r.left - h.left) / v.scale.x + v.x,
22
+ y: (r.top - h.top) / v.scale.y + v.y,
23
+ w: r.width / v.scale.x,
24
+ h: r.height / v.scale.y
25
+ };
26
+ }
27
+ function elementOf(target) {
28
+ return target.ref?.current ?? null;
29
+ }
30
+ function markupOf(api, target, content) {
31
+ const painted = api.paintedMarks(target);
32
+ if (painted.length === 0) return "";
33
+ const nodes = painted.flatMap(({ mark, style }) => markSvgNodes(mark, content, style));
34
+ return serializeSvg(nodes, { viewBox: { x: 0, y: 0, width: content.w, height: content.h } });
35
+ }
36
+ function OverviewMarks({ fit, camera }) {
37
+ const api = useAnnotationsOptional();
38
+ const [, redraw] = useReducer((n) => n + 1, 0);
39
+ useEffect(() => api?.subscribe(redraw), [api]);
40
+ if (!api) return null;
41
+ const s = fit.scale.x;
42
+ return /* @__PURE__ */ jsx(Fragment, { children: api.targets().map((t) => {
43
+ const el = elementOf(t);
44
+ const box = el ? localRectOf(el, camera) : null;
45
+ const markup = box ? markupOf(api, t.id, t.content) : "";
46
+ if (!box || !markup) return null;
47
+ const place = {
48
+ ["--lk-overview-mx"]: `${(box.x - fit.x) * s}px`,
49
+ ["--lk-overview-my"]: `${(box.y - fit.y) * s}px`,
50
+ ["--lk-overview-mw"]: `${box.w * s}px`,
51
+ ["--lk-overview-mh"]: `${box.h * s}px`
52
+ };
53
+ return /* @__PURE__ */ jsx(
54
+ "div",
55
+ {
56
+ className: "lk-overview__marks",
57
+ style: place,
58
+ "data-target": t.id,
59
+ dangerouslySetInnerHTML: { __html: markup }
60
+ },
61
+ t.id
62
+ );
63
+ }) });
64
+ }
65
+ var OVERVIEW_VIEW_ID = "overview";
66
+ var PADDING = 6;
67
+ var OVERVIEW_INPUT = {
68
+ id: "overview",
69
+ eligibility: { always: true },
70
+ bindings: [
71
+ { spec: { kind: "pointerDown" }, actionId: MINIMAP_CENTER },
72
+ { spec: { kind: "drag" }, actionId: MINIMAP_PAN }
73
+ ]
74
+ };
75
+ var TOOLS_BY_ID = /* @__PURE__ */ new Map([[OVERVIEW_INPUT.id, OVERVIEW_INPUT]]);
76
+ var CHANNELS = { wheel: false, pinch: false, contextMenu: false, ingest: false };
77
+ function toLocalRect(b, frame) {
78
+ if (frame.yDir === 1) return b;
79
+ return { x: b.x, y: -(b.y + b.height), width: b.width, height: b.height };
80
+ }
81
+ function fitRect(rect, size) {
82
+ const s = Math.min(
83
+ Math.max(1, size.width - 2 * PADDING) / Math.max(rect.width, 1e-9),
84
+ Math.max(1, size.height - 2 * PADDING) / Math.max(rect.height, 1e-9)
85
+ );
86
+ return {
87
+ x: rect.x + rect.width / 2 - size.width / (2 * s),
88
+ y: rect.y + rect.height / 2 - size.height / (2 * s),
89
+ scale: { x: s, y: s }
90
+ };
91
+ }
92
+ function prepare(canvas, size) {
93
+ const ctx = canvas?.getContext("2d") ?? null;
94
+ if (!canvas || !ctx) return null;
95
+ const dpr = window.devicePixelRatio || 1;
96
+ const w = Math.round(size.width * dpr);
97
+ const h = Math.round(size.height * dpr);
98
+ if (canvas.width !== w) canvas.width = w;
99
+ if (canvas.height !== h) canvas.height = h;
100
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
101
+ ctx.clearRect(0, 0, size.width, size.height);
102
+ return ctx;
103
+ }
104
+ function cssVar(el, name, fallback) {
105
+ if (!el) return fallback;
106
+ return getComputedStyle(el).getPropertyValue(name).trim() || fallback;
107
+ }
108
+ function TrialOverview(props) {
109
+ const camera = useContext(CameraContext);
110
+ if (!camera) return null;
111
+ return /* @__PURE__ */ jsx(Overview, { ...props, camera });
112
+ }
113
+ function Overview({
114
+ width,
115
+ height,
116
+ render,
117
+ bounds,
118
+ title = "Overview",
119
+ anchor = "bottom-left",
120
+ persist,
121
+ className,
122
+ camera
123
+ }) {
124
+ const size = useMemo(() => ({ width, height }), [width, height]);
125
+ const content = bounds ? toLocalRect(bounds, camera.frame) : camera.content;
126
+ const framed = content !== null && content !== void 0;
127
+ const { x = 0, y = 0, width: w = 0, height: h = 0 } = content ?? {};
128
+ const fit = useMemo(
129
+ () => framed ? fitRect({ x, y, width: w, height: h }, size) : { x: 0, y: 0, scale: { x: 1, y: 1 } },
130
+ [framed, x, y, w, h, size]
131
+ );
132
+ const boxRef = useRef(null);
133
+ return /* @__PURE__ */ jsxs(
134
+ FloatingPanel,
135
+ {
136
+ anchor,
137
+ className: className ? `lk-overview ${className}` : "lk-overview",
138
+ ...persist ? { persist } : {},
139
+ children: [
140
+ /* @__PURE__ */ jsx("div", { className: "lk-overview__title", children: title }),
141
+ /* @__PURE__ */ jsxs("div", { ref: boxRef, className: "lk-overview__box", "data-no-drag": true, style: boxVars(size), children: [
142
+ render && content ? /* @__PURE__ */ jsx("div", { className: "lk-overview__content", style: contentVars(fit, content), children: render({ size: { width: content.width, height: content.height } }) }) : null,
143
+ render ? null : /* @__PURE__ */ jsx(LayersCanvas, { fit, size, frame: camera.frame }),
144
+ /* @__PURE__ */ jsx(OverviewMarks, { fit, camera }),
145
+ /* @__PURE__ */ jsx(ChromeCanvas, { fit, size, camera }),
146
+ /* @__PURE__ */ jsx(OverviewInput, { boxRef, fit, camera })
147
+ ] })
148
+ ]
149
+ }
150
+ );
151
+ }
152
+ function boxVars(size) {
153
+ return {
154
+ ["--lk-overview-w"]: `${size.width}px`,
155
+ ["--lk-overview-h"]: `${size.height}px`
156
+ };
157
+ }
158
+ function contentVars(fit, content) {
159
+ const s = fit.scale.x;
160
+ return {
161
+ ["--lk-overview-x"]: `${(content.x - fit.x) * s}px`,
162
+ ["--lk-overview-y"]: `${(content.y - fit.y) * s}px`,
163
+ ["--lk-overview-zoom"]: String(s),
164
+ ["--lk-overview-cw"]: `${content.width}px`,
165
+ ["--lk-overview-ch"]: `${content.height}px`
166
+ };
167
+ }
168
+ function LayersCanvas({ fit, size, frame }) {
169
+ const stack = useContext(CanvasStackContext);
170
+ const ref = useRef(null);
171
+ const layers = stack?.surface?.layers;
172
+ useLayoutEffect(() => {
173
+ const ctx = prepare(ref.current, size);
174
+ if (!ctx || !layers) return;
175
+ const local = { originPx: { x: 0, y: 0 }, yDir: frame.yDir };
176
+ const view = fromCameraView(fit, local);
177
+ for (const layer of layers) {
178
+ if (!layer.visible || layer.id.startsWith("__lk_")) continue;
179
+ ctx.save();
180
+ layer.render(ctx, view, local);
181
+ ctx.restore();
182
+ }
183
+ }, [layers, fit, size, frame.yDir]);
184
+ return /* @__PURE__ */ jsx("canvas", { ref, className: "lk-overview__canvas" });
185
+ }
186
+ function ChromeCanvas({
187
+ fit,
188
+ size,
189
+ camera
190
+ }) {
191
+ const ref = useRef(null);
192
+ const pointer = usePointerPosition();
193
+ const root = camera.view.get();
194
+ const host = camera.view.hostSize?.() ?? null;
195
+ useLayoutEffect(() => {
196
+ const ctx = prepare(ref.current, size);
197
+ if (!ctx) return;
198
+ const s = fit.scale.x;
199
+ const px = (x, y) => ({ x: (x - fit.x) * s, y: (y - fit.y) * s });
200
+ if (host) {
201
+ const a = px(root.x, root.y);
202
+ ctx.strokeStyle = cssVar(ref.current, "--wzl-fg", "#ffffff");
203
+ ctx.lineWidth = 1;
204
+ ctx.setLineDash([2, 3]);
205
+ ctx.strokeRect(
206
+ a.x + 0.5,
207
+ a.y + 0.5,
208
+ host.width / root.scale.x * s,
209
+ host.height / root.scale.y * s
210
+ );
211
+ ctx.setLineDash([]);
212
+ }
213
+ const world = pointerElsewhere(pointer, OVERVIEW_VIEW_ID, camera.frame);
214
+ if (world) {
215
+ const at = px(world.x, world.y * camera.frame.yDir);
216
+ const { halo, bars } = crosshairRects(at.x, at.y, 1);
217
+ ctx.fillStyle = "rgb(0 0 0 / 60%)";
218
+ for (const r of halo) ctx.fillRect(r.x, r.y, r.w, r.h);
219
+ ctx.fillStyle = cssVar(ref.current, "--wzl-accent", "#4c8dff");
220
+ for (const r of bars) ctx.fillRect(r.x, r.y, r.w, r.h);
221
+ }
222
+ });
223
+ return /* @__PURE__ */ jsx("canvas", { ref, className: "lk-overview__canvas lk-overview__chrome" });
224
+ }
225
+ function OverviewInput(props) {
226
+ return /* @__PURE__ */ jsx(DepRegistryProvider, { children: /* @__PURE__ */ jsx(ActionsProvider, { children: /* @__PURE__ */ jsx(ActiveToolContextProvider, { children: /* @__PURE__ */ jsx(OverviewDispatch, { ...props }) }) }) });
227
+ }
228
+ function OverviewDispatch({
229
+ boxRef,
230
+ fit,
231
+ camera
232
+ }) {
233
+ const registry = useActionsRegistry();
234
+ const fitRef = useRef(fit);
235
+ fitRef.current = fit;
236
+ const root = camera.view;
237
+ useDepSource("rootView", () => root);
238
+ useEffect(() => {
239
+ if (!registry) return;
240
+ const offs = [registry.register(minimapCenterAction()), registry.register(minimapPanAction())];
241
+ return () => {
242
+ for (const off of offs) off();
243
+ };
244
+ }, [registry]);
245
+ const clientToLocal = useMemo(
246
+ () => (cx, cy) => {
247
+ const r = boxRef.current?.getBoundingClientRect();
248
+ const f = fitRef.current;
249
+ return {
250
+ x: (cx - (r?.left ?? 0)) / f.scale.x + f.x,
251
+ y: (cy - (r?.top ?? 0)) / f.scale.y + f.y
252
+ };
253
+ },
254
+ [boxRef]
255
+ );
256
+ useGestureDispatcher({
257
+ canvasRef: boxRef,
258
+ // Always present: `OverviewInput` mounts the provider directly above.
259
+ actions: registry,
260
+ entriesById: TOOLS_BY_ID,
261
+ clientToWorld: clientToLocal,
262
+ keyboard: false,
263
+ channels: CHANNELS
264
+ });
265
+ usePublishPointer(boxRef, clientToLocal, camera.frame, OVERVIEW_VIEW_ID);
266
+ useEffect(() => {
267
+ const el = boxRef.current;
268
+ if (!el) return;
269
+ const stop = (e) => e.stopPropagation();
270
+ const kinds = [
271
+ "pointerdown",
272
+ "pointermove",
273
+ "pointerup",
274
+ "click",
275
+ "wheel",
276
+ "dblclick",
277
+ "contextmenu"
278
+ ];
279
+ for (const k of kinds) el.addEventListener(k, stop);
280
+ return () => {
281
+ for (const k of kinds) el.removeEventListener(k, stop);
282
+ };
283
+ }, [boxRef]);
284
+ return null;
285
+ }
286
+
287
+ export { OVERVIEW_VIEW_ID, TrialOverview };
288
+ //# sourceMappingURL=index.js.map
289
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/overview/OverviewMarks.tsx","../../src/overview/TrialOverview.tsx"],"names":["jsx","useEffect"],"mappings":";;;;;;;;;;;;;AAsBA,SAAS,WAAA,CAAY,IAAiB,MAAA,EAA8C;AAClF,EAAA,MAAM,IAAA,GAAO,OAAO,OAAA,EAAQ;AAC5B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,MAAM,CAAA,GAAI,GAAG,qBAAA,EAAsB;AACnC,EAAA,MAAM,CAAA,GAAI,KAAK,qBAAA,EAAsB;AACrC,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,GAAA,EAAI;AAC1B,EAAA,OAAO;AAAA,IACL,CAAA,EAAA,CAAI,EAAE,IAAA,GAAO,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,CAAE,CAAA;AAAA,IACrC,CAAA,EAAA,CAAI,EAAE,GAAA,GAAM,CAAA,CAAE,OAAO,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,CAAE,CAAA;AAAA,IACnC,CAAA,EAAG,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAA,CAAM,CAAA;AAAA,IACrB,CAAA,EAAG,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,KAAA,CAAM;AAAA,GACxB;AACF;AAIA,SAAS,UAAU,MAAA,EAAoC;AACrD,EAAA,OAAQ,MAAA,CAAmD,KAAK,OAAA,IAAW,IAAA;AAC7E;AAEA,SAAS,QAAA,CAAS,GAAA,EAAqB,MAAA,EAAgB,OAAA,EAA2C;AAChG,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA;AACvC,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,CAAC,EAAE,IAAA,EAAM,KAAA,EAAM,KAAM,YAAA,CAAa,IAAA,EAAM,OAAA,EAAS,KAAK,CAAC,CAAA;AACrF,EAAA,OAAO,aAAa,KAAA,EAAO,EAAE,OAAA,EAAS,EAAE,GAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,KAAA,EAAO,QAAQ,CAAA,EAAG,MAAA,EAAQ,OAAA,CAAQ,CAAA,IAAK,CAAA;AAC7F;AAaO,SAAS,aAAA,CAAc,EAAE,GAAA,EAAK,MAAA,EAAO,EAAuB;AACjE,EAAA,MAAM,MAAM,sBAAA,EAAuB;AACnC,EAAA,MAAM,GAAG,MAAM,CAAA,GAAI,WAAW,CAAC,CAAA,KAAc,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA;AACrD,EAAA,SAAA,CAAU,MAAM,GAAA,EAAK,SAAA,CAAU,MAAM,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AAC7C,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,MAAM,CAAA,GAAI,IAAI,KAAA,CAAM,CAAA;AACpB,EAAA,uCAEK,QAAA,EAAA,GAAA,CAAI,OAAA,EAAQ,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM;AACxB,IAAA,MAAM,EAAA,GAAK,UAAU,CAAC,CAAA;AACtB,IAAA,MAAM,GAAA,GAAM,EAAA,GAAK,WAAA,CAAY,EAAA,EAAI,MAAM,CAAA,GAAI,IAAA;AAC3C,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,GAAA,EAAK,EAAE,EAAA,EAAI,CAAA,CAAE,OAAO,CAAA,GAAI,EAAA;AACtD,IAAA,IAAI,CAAC,GAAA,IAAO,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC5B,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,CAAC,kBAA4B,GAAG,CAAA,EAAA,CAAI,IAAI,CAAA,GAAI,GAAA,CAAI,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,MACtD,CAAC,kBAA4B,GAAG,CAAA,EAAA,CAAI,IAAI,CAAA,GAAI,GAAA,CAAI,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,MACtD,CAAC,kBAA4B,GAAG,CAAA,EAAG,GAAA,CAAI,IAAI,CAAC,CAAA,EAAA,CAAA;AAAA,MAC5C,CAAC,kBAA4B,GAAG,CAAA,EAAG,GAAA,CAAI,IAAI,CAAC,CAAA,EAAA;AAAA,KAC9C;AACA,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAEC,SAAA,EAAU,oBAAA;AAAA,QACV,KAAA,EAAO,KAAA;AAAA,QACP,eAAa,CAAA,CAAE,EAAA;AAAA,QAEf,uBAAA,EAAyB,EAAE,MAAA,EAAQ,MAAA;AAAO,OAAA;AAAA,MALrC,CAAA,CAAE;AAAA,KAMT;AAAA,EAEJ,CAAC,CAAA,EACH,CAAA;AAEJ;AC3CO,IAAM,gBAAA,GAAmB;AAuChC,IAAM,OAAA,GAAU,CAAA;AAChB,IAAM,cAAA,GAAuB;AAAA,EAC3B,EAAA,EAAI,UAAA;AAAA,EACJ,WAAA,EAAa,EAAE,MAAA,EAAQ,IAAA,EAAK;AAAA,EAC5B,QAAA,EAAU;AAAA,IACR,EAAE,IAAA,EAAM,EAAE,MAAM,aAAA,EAAc,EAAG,UAAU,cAAA,EAAe;AAAA,IAC1D,EAAE,IAAA,EAAM,EAAE,MAAM,MAAA,EAAO,EAAG,UAAU,WAAA;AAAY;AAEpD,CAAA;AACA,IAAM,WAAA,uBAA6C,GAAA,CAAI,CAAC,CAAC,cAAA,CAAe,EAAA,EAAI,cAAc,CAAC,CAAC,CAAA;AAC5F,IAAM,QAAA,GAAW,EAAE,KAAA,EAAO,KAAA,EAAO,OAAO,KAAA,EAAO,WAAA,EAAa,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAM;AAGjF,SAAS,WAAA,CAAY,GAAmB,KAAA,EAAmC;AACzE,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,CAAA,EAAG,OAAO,CAAA;AAC7B,EAAA,OAAO,EAAE,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,GAAG,EAAE,CAAA,CAAE,CAAA,GAAI,CAAA,CAAE,SAAS,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,MAAA,EAAQ,EAAE,MAAA,EAAO;AAC1E;AAGA,SAAS,OAAA,CAAQ,MAAsB,IAAA,EAA0B;AAC/D,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,GAAQ,CAAA,GAAI,OAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,KAAA,EAAO,IAAI,CAAA;AAAA,IACjE,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,OAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,MAAA,EAAQ,IAAI;AAAA,GACrE;AACA,EAAA,OAAO;AAAA,IACL,CAAA,EAAG,KAAK,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAA,CAAK,SAAS,CAAA,GAAI,CAAA,CAAA;AAAA,IAC/C,CAAA,EAAG,KAAK,CAAA,GAAI,IAAA,CAAK,SAAS,CAAA,GAAI,IAAA,CAAK,UAAU,CAAA,GAAI,CAAA,CAAA;AAAA,IACjD,KAAA,EAAO,EAAE,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA;AAAE,GACtB;AACF;AAGA,SAAS,OAAA,CACP,QACA,IAAA,EACiC;AACjC,EAAA,MAAM,GAAA,GAAM,MAAA,EAAQ,UAAA,CAAW,IAAI,CAAA,IAAK,IAAA;AACxC,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,GAAA,EAAK,OAAO,IAAA;AAC5B,EAAA,MAAM,GAAA,GAAM,OAAO,gBAAA,IAAoB,CAAA;AACvC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,QAAQ,GAAG,CAAA;AACrC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAS,GAAG,CAAA;AACtC,EAAA,IAAI,MAAA,CAAO,KAAA,KAAU,CAAA,EAAG,MAAA,CAAO,KAAA,GAAQ,CAAA;AACvC,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,MAAA,CAAO,MAAA,GAAS,CAAA;AACzC,EAAA,GAAA,CAAI,aAAa,GAAA,EAAK,CAAA,EAAG,CAAA,EAAG,GAAA,EAAK,GAAG,CAAC,CAAA;AACrC,EAAA,GAAA,CAAI,UAAU,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,KAAA,EAAO,KAAK,MAAM,CAAA;AAC3C,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,MAAA,CAAO,EAAA,EAAoB,IAAA,EAAc,QAAA,EAA0B;AAC1E,EAAA,IAAI,CAAC,IAAI,OAAO,QAAA;AAChB,EAAA,OAAO,iBAAiB,EAAE,CAAA,CAAE,iBAAiB,IAAI,CAAA,CAAE,MAAK,IAAK,QAAA;AAC/D;AAOO,SAAS,cAAc,KAAA,EAA2B;AACvD,EAAA,MAAM,MAAA,GAAS,WAAW,aAAa,CAAA;AACvC,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,EAAA,uBAAOA,GAAAA,CAAC,QAAA,EAAA,EAAU,GAAG,OAAO,MAAA,EAAgB,CAAA;AAC9C;AAEA,SAAS,QAAA,CAAS;AAAA,EAChB,KAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA,GAAQ,UAAA;AAAA,EACR,MAAA,GAAS,aAAA;AAAA,EACT,OAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAwD;AACtD,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAO,EAAE,KAAA,EAAO,QAAO,CAAA,EAAI,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAC/D,EAAA,MAAM,UAAU,MAAA,GAAS,WAAA,CAAY,QAAQ,MAAA,CAAO,KAAK,IAAI,MAAA,CAAO,OAAA;AAEpE,EAAA,MAAM,MAAA,GAAS,OAAA,KAAY,IAAA,IAAQ,OAAA,KAAY,MAAA;AAC/C,EAAA,MAAM,EAAE,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,KAAA,EAAO,CAAA,GAAI,CAAA,EAAG,MAAA,EAAQ,CAAA,GAAI,CAAA,EAAE,GAAI,WAAW,EAAC;AAClE,EAAA,MAAM,GAAA,GAAM,OAAA;AAAA,IACV,MACE,MAAA,GAAS,OAAA,CAAQ,EAAE,CAAA,EAAG,GAAG,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAE,EAAG,IAAI,IAAI,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,KAAA,EAAO,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE,EAAE;AAAA,IAC9F,CAAC,MAAA,EAAQ,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,IAAI;AAAA,GAC3B;AACA,EAAA,MAAM,MAAA,GAAS,OAA8B,IAAI,CAAA;AAEjD,EAAA,uBACE,IAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,MAAA;AAAA,MACA,SAAA,EAAW,SAAA,GAAY,CAAA,YAAA,EAAe,SAAS,CAAA,CAAA,GAAK,aAAA;AAAA,MACnD,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY,EAAC;AAAA,MAE9B,QAAA,EAAA;AAAA,wBAAAA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oBAAA,EAAsB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBAC3C,IAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,SAAA,EAAU,kBAAA,EAAmB,cAAA,EAAY,IAAA,EAAC,KAAA,EAAO,OAAA,CAAQ,IAAI,CAAA,EAC5E,QAAA,EAAA;AAAA,UAAA,MAAA,IAAU,OAAA,mBACTA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAuB,KAAA,EAAO,WAAA,CAAY,GAAA,EAAK,OAAO,CAAA,EAClE,QAAA,EAAA,MAAA,CAAO,EAAE,IAAA,EAAM,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAO,EAAG,CAAA,EACpE,CAAA,GACE,IAAA;AAAA,UACH,MAAA,GAAS,uBAAOA,GAAAA,CAAC,gBAAa,GAAA,EAAU,IAAA,EAAY,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,CAAA;AAAA,0BAC1EA,GAAAA,CAAC,aAAA,EAAA,EAAc,GAAA,EAAU,MAAA,EAAgB,CAAA;AAAA,0BACzCA,GAAAA,CAAC,YAAA,EAAA,EAAa,GAAA,EAAU,MAAY,MAAA,EAAgB,CAAA;AAAA,0BACpDA,GAAAA,CAAC,aAAA,EAAA,EAAc,MAAA,EAAgB,KAAU,MAAA,EAAgB;AAAA,SAAA,EAC3D;AAAA;AAAA;AAAA,GACF;AAEJ;AAKA,SAAS,QAAQ,IAAA,EAAmC;AAClD,EAAA,OAAO;AAAA,IACL,CAAC,iBAA2B,GAAG,CAAA,EAAG,KAAK,KAAK,CAAA,EAAA,CAAA;AAAA,IAC5C,CAAC,iBAA2B,GAAG,CAAA,EAAG,KAAK,MAAM,CAAA,EAAA;AAAA,GAC/C;AACF;AAEA,SAAS,WAAA,CAAY,KAAW,OAAA,EAAwC;AACtE,EAAA,MAAM,CAAA,GAAI,IAAI,KAAA,CAAM,CAAA;AACpB,EAAA,OAAO;AAAA,IACL,CAAC,iBAA2B,GAAG,CAAA,EAAA,CAAI,QAAQ,CAAA,GAAI,GAAA,CAAI,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,IACzD,CAAC,iBAA2B,GAAG,CAAA,EAAA,CAAI,QAAQ,CAAA,GAAI,GAAA,CAAI,KAAK,CAAC,CAAA,EAAA,CAAA;AAAA,IACzD,CAAC,oBAA8B,GAAG,MAAA,CAAO,CAAC,CAAA;AAAA,IAC1C,CAAC,kBAA4B,GAAG,CAAA,EAAG,QAAQ,KAAK,CAAA,EAAA,CAAA;AAAA,IAChD,CAAC,kBAA4B,GAAG,CAAA,EAAG,QAAQ,MAAM,CAAA,EAAA;AAAA,GACnD;AACF;AAGA,SAAS,YAAA,CAAa,EAAE,GAAA,EAAK,IAAA,EAAM,OAAM,EAAyD;AAChG,EAAA,MAAM,KAAA,GAAQ,WAAW,kBAAkB,CAAA;AAC3C,EAAA,MAAM,GAAA,GAAM,OAAiC,IAAI,CAAA;AACjD,EAAA,MAAM,MAAA,GAAS,OAAO,OAAA,EAAS,MAAA;AAC/B,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAS,IAAI,CAAA;AACrC,IAAA,IAAI,CAAC,GAAA,IAAO,CAAC,MAAA,EAAQ;AACrB,IAAA,MAAM,KAAA,GAAoB,EAAE,QAAA,EAAU,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE,EAAG,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK;AACvE,IAAA,MAAM,IAAA,GAAO,cAAA,CAAe,GAAA,EAAK,KAAK,CAAA;AACtC,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,MAAM,EAAA,CAAG,UAAA,CAAW,OAAO,CAAA,EAAG;AACpD,MAAA,GAAA,CAAI,IAAA,EAAK;AACT,MAAA,KAAA,CAAM,MAAA,CAAO,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAC7B,MAAA,GAAA,CAAI,OAAA,EAAQ;AAAA,IACd;AAAA,EACF,GAAG,CAAC,MAAA,EAAQ,KAAK,IAAA,EAAM,KAAA,CAAM,IAAI,CAAC,CAAA;AAClC,EAAA,uBAAOA,GAAAA,CAAC,QAAA,EAAA,EAAO,GAAA,EAAU,WAAU,qBAAA,EAAsB,CAAA;AAC3D;AAGA,SAAS,YAAA,CAAa;AAAA,EACpB,GAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,MAAM,GAAA,GAAM,OAAiC,IAAI,CAAA;AACjD,EAAA,MAAM,UAAU,kBAAA,EAAmB;AACnC,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,GAAA,EAAI;AAC7B,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,QAAA,IAAW,IAAK,IAAA;AAGzC,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAS,IAAI,CAAA;AACrC,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,MAAM,CAAA,GAAI,IAAI,KAAA,CAAM,CAAA;AACpB,IAAA,MAAM,EAAA,GAAK,CAAC,CAAA,EAAW,CAAA,MAAe,EAAE,CAAA,EAAA,CAAI,CAAA,GAAI,GAAA,CAAI,CAAA,IAAK,CAAA,EAAG,CAAA,EAAA,CAAI,CAAA,GAAI,GAAA,CAAI,KAAK,CAAA,EAAE,CAAA;AAC/E,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,CAAA,GAAI,EAAA,CAAG,IAAA,CAAK,CAAA,EAAG,KAAK,CAAC,CAAA;AAC3B,MAAA,GAAA,CAAI,WAAA,GAAc,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,YAAY,SAAS,CAAA;AAC3D,MAAA,GAAA,CAAI,SAAA,GAAY,CAAA;AAChB,MAAA,GAAA,CAAI,WAAA,CAAY,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA;AACtB,MAAA,GAAA,CAAI,UAAA;AAAA,QACF,EAAE,CAAA,GAAI,GAAA;AAAA,QACN,EAAE,CAAA,GAAI,GAAA;AAAA,QACL,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,CAAA,GAAK,CAAA;AAAA,QAC7B,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,CAAA,GAAK;AAAA,OACjC;AACA,MAAA,GAAA,CAAI,WAAA,CAAY,EAAE,CAAA;AAAA,IACpB;AACA,IAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,OAAA,EAAS,gBAAA,EAAkB,OAAO,KAAK,CAAA;AACtE,IAAA,IAAI,KAAA,EAAO;AAET,MAAA,MAAM,EAAA,GAAK,GAAG,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,GAAI,MAAA,CAAO,MAAM,IAAI,CAAA;AAClD,MAAA,MAAM,EAAE,MAAM,IAAA,EAAK,GAAI,eAAe,EAAA,CAAG,CAAA,EAAG,EAAA,CAAG,CAAA,EAAG,CAAC,CAAA;AACnD,MAAA,GAAA,CAAI,SAAA,GAAY,kBAAA;AAChB,MAAA,KAAA,MAAW,CAAA,IAAK,IAAA,EAAM,GAAA,CAAI,QAAA,CAAS,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA;AACrD,MAAA,GAAA,CAAI,SAAA,GAAY,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,gBAAgB,SAAS,CAAA;AAC7D,MAAA,KAAA,MAAW,CAAA,IAAK,IAAA,EAAM,GAAA,CAAI,QAAA,CAAS,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA;AAAA,IACvD;AAAA,EACF,CAAC,CAAA;AACD,EAAA,uBAAOA,GAAAA,CAAC,QAAA,EAAA,EAAO,GAAA,EAAU,WAAU,yCAAA,EAA0C,CAAA;AAC/E;AAMA,SAAS,cAAc,KAAA,EAIpB;AACD,EAAA,uBACEA,GAAAA,CAAC,mBAAA,EAAA,EACC,QAAA,kBAAAA,GAAAA,CAAC,mBACC,QAAA,kBAAAA,GAAAA,CAAC,yBAAA,EAAA,EACC,QAAA,kBAAAA,IAAC,gBAAA,EAAA,EAAkB,GAAG,KAAA,EAAO,CAAA,EAC/B,GACF,CAAA,EACF,CAAA;AAEJ;AAEA,SAAS,gBAAA,CAAiB;AAAA,EACxB,MAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,MAAM,WAAW,kBAAA,EAAmB;AACpC,EAAA,MAAM,MAAA,GAAS,OAAO,GAAG,CAAA;AACzB,EAAA,MAAA,CAAO,OAAA,GAAU,GAAA;AACjB,EAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,EAAA,YAAA,CAAa,UAAA,EAAY,MAAM,IAAe,CAAA;AAE9C,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,MAAM,IAAA,GAAO,CAAC,QAAA,CAAS,QAAA,CAAS,mBAAA,EAAqB,CAAA,EAAG,QAAA,CAAS,QAAA,CAAS,gBAAA,EAAkB,CAAC,CAAA;AAC7F,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,MAAW,GAAA,IAAO,MAAM,GAAA,EAAI;AAAA,IAC9B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,aAAA,GAAgB,OAAA;AAAA,IACpB,MAAM,CAAC,EAAA,EAAY,EAAA,KAAe;AAChC,MAAA,MAAM,CAAA,GAAI,MAAA,CAAO,OAAA,EAAS,qBAAA,EAAsB;AAChD,MAAA,MAAM,IAAI,MAAA,CAAO,OAAA;AACjB,MAAA,OAAO;AAAA,QACL,CAAA,EAAA,CAAI,MAAM,CAAA,EAAG,IAAA,IAAQ,MAAM,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,CAAE,CAAA;AAAA,QACzC,CAAA,EAAA,CAAI,MAAM,CAAA,EAAG,GAAA,IAAO,MAAM,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,CAAE;AAAA,OAC1C;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,oBAAA,CAAqB;AAAA,IACnB,SAAA,EAAW,MAAA;AAAA;AAAA,IAEX,OAAA,EAAS,QAAA;AAAA,IACT,WAAA,EAAa,WAAA;AAAA,IACb,aAAA,EAAe,aAAA;AAAA,IACf,QAAA,EAAU,KAAA;AAAA,IACV,QAAA,EAAU;AAAA,GACX,CAAA;AAED,EAAA,iBAAA,CAAkB,MAAA,EAAQ,aAAA,EAAe,MAAA,CAAO,KAAA,EAAO,gBAAgB,CAAA;AAIvE,EAAAA,UAAU,MAAM;AACd,IAAA,MAAM,KAAK,MAAA,CAAO,OAAA;AAClB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAmB,CAAA,CAAE,eAAA,EAAgB;AACnD,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,aAAA;AAAA,MACA,aAAA;AAAA,MACA,WAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,KAAA,MAAW,CAAA,IAAK,KAAA,EAAO,EAAA,CAAG,gBAAA,CAAiB,GAAG,IAAI,CAAA;AAClD,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,MAAW,CAAA,IAAK,KAAA,EAAO,EAAA,CAAG,mBAAA,CAAoB,GAAG,IAAI,CAAA;AAAA,IACvD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,OAAO,IAAA;AACT","file":"index.js","sourcesContent":["/**\n * Every annotation target's marks, drawn read-only on the overview through the\n * export's own vector path: `markSvgNodes`, then `serializeSvg`.\n */\nimport type { View } from '@weasel-js/core';\nimport { serializeSvg } from '@weasel-js/svg';\nimport { type CSSProperties, type RefObject, useEffect, useReducer } from 'react';\nimport { useAnnotationsOptional } from '../annotations/AnnotationsContext';\nimport { markSvgNodes } from '../annotations/svgNodes';\nimport type { AnnotationsApi } from '../annotations/types';\nimport type { CameraContextValue } from '../canvas/CameraInput';\n\n/** A target's box, in the camera's frame-local units. */\ninterface LocalRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** Where `el` sits in the camera's frame-local units: its screen rect,\n * measured against the camera's element, through the camera. */\nfunction localRectOf(el: HTMLElement, camera: CameraContextValue): LocalRect | null {\n const host = camera.element();\n if (!host) return null;\n const r = el.getBoundingClientRect();\n const h = host.getBoundingClientRect();\n const v = camera.view.get();\n return {\n x: (r.left - h.left) / v.scale.x + v.x,\n y: (r.top - h.top) / v.scale.y + v.y,\n w: r.width / v.scale.x,\n h: r.height / v.scale.y,\n };\n}\n\n/** The element a target's marks sit on. The capability's targets carry their\n * `ref`; the store's type names only the half it reads. */\nfunction elementOf(target: object): HTMLElement | null {\n return (target as { ref?: RefObject<HTMLElement | null> }).ref?.current ?? null;\n}\n\nfunction markupOf(api: AnnotationsApi, target: string, content: { w: number; h: number }): string {\n const painted = api.paintedMarks(target);\n if (painted.length === 0) return '';\n const nodes = painted.flatMap(({ mark, style }) => markSvgNodes(mark, content, style));\n return serializeSvg(nodes, { viewBox: { x: 0, y: 0, width: content.w, height: content.h } });\n}\n\n/** Props for `<OverviewMarks>`. */\nexport interface OverviewMarksProps {\n fit: View;\n camera: CameraContextValue;\n}\n\n/**\n * The marks of every target in the annotations store in scope, placed where\n * each target's element sits on the stage. Draws nothing outside an annotating\n * trial, and takes no input.\n */\nexport function OverviewMarks({ fit, camera }: OverviewMarksProps) {\n const api = useAnnotationsOptional();\n const [, redraw] = useReducer((n: number) => n + 1, 0);\n useEffect(() => api?.subscribe(redraw), [api]);\n if (!api) return null;\n const s = fit.scale.x;\n return (\n <>\n {api.targets().map((t) => {\n const el = elementOf(t);\n const box = el ? localRectOf(el, camera) : null;\n const markup = box ? markupOf(api, t.id, t.content) : '';\n if (!box || !markup) return null;\n const place = {\n ['--lk-overview-mx' as string]: `${(box.x - fit.x) * s}px`,\n ['--lk-overview-my' as string]: `${(box.y - fit.y) * s}px`,\n ['--lk-overview-mw' as string]: `${box.w * s}px`,\n ['--lk-overview-mh' as string]: `${box.h * s}px`,\n } as CSSProperties;\n return (\n <div\n key={t.id}\n className=\"lk-overview__marks\"\n style={place}\n data-target={t.id}\n // biome-ignore lint/security/noDangerouslySetInnerHtml: the export's SVG serializer is the one vector path marks have; re-deriving React nodes would be a second.\n dangerouslySetInnerHTML={{ __html: markup }}\n />\n );\n })}\n </>\n );\n}\n","/**\n * `<TrialOverview>` — the whole of a trial's content at a glance, in a panel\n * that floats over the stage: the visible rect, a crosshair where the pointer\n * is on the stage, and a press or drag that moves the stage's camera there.\n *\n * It is the labkit form of core's minimap contribution: the same\n * `minimap.center` / `minimap.pan` actions, run on a dispatcher of its own with\n * the trial's camera as `rootView`, and the same crosshair geometry.\n */\nimport {\n ActionsProvider,\n ActiveToolContextProvider,\n crosshairRects,\n DepRegistryProvider,\n MINIMAP_CENTER,\n MINIMAP_PAN,\n minimapCenterAction,\n minimapPanAction,\n type Tool,\n useActionsRegistry,\n useDepSource,\n useGestureDispatcher,\n usePointerPosition,\n type View,\n type ViewApi,\n} from '@weasel-js/core';\nimport {\n type CSSProperties,\n type ReactNode,\n type RefObject,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n} from 'react';\nimport {\n CameraContext,\n type CameraContextValue,\n pointerElsewhere,\n usePublishPointer,\n} from '../canvas/CameraInput';\nimport { CanvasStackContext } from '../canvas/CanvasStackContext';\nimport { fromCameraView } from '../canvas/cameraView';\nimport type { ViewportSize, WorldFrame } from '../canvas/worldSpec';\nimport { FloatingPanel, type FloatingPanelProps } from '../primitives/FloatingPanel';\nimport { OverviewMarks } from './OverviewMarks';\n\n/** The view id the overview publishes its pointer under. */\nexport const OVERVIEW_VIEW_ID = 'overview';\n\n/** A rect in the instrument's world. */\nexport interface OverviewBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** What an overview's `render` is handed. */\nexport interface OverviewRenderArgs {\n /** The content's own size in CSS pixels, at zoom 1 — lay out at this size\n * and the overview scales it to fit. */\n size: ViewportSize;\n}\n\n/** Props for `<TrialOverview>`. */\nexport interface TrialOverviewProps {\n /** The overview box, in CSS pixels. */\n width: number;\n height: number;\n /**\n * DOM content for the overview, laid out at the content's own size — for a\n * `stage` instrument, typically a lighter copy of what its `render` draws.\n * Never the instrument's own `render`: its effects would run twice. A canvas\n * instrument omits it, and the overview redraws the canvas's layers.\n */\n render?: (args: OverviewRenderArgs) => ReactNode;\n /** What to frame, in the instrument's world. Defaults to a stage's content\n * rect; a canvas instrument, whose world has no edge, names one. */\n bounds?: OverviewBounds;\n /** Panel title. */\n title?: string;\n anchor?: FloatingPanelProps['anchor'];\n persist?: string;\n className?: string;\n}\n\nconst PADDING = 6;\nconst OVERVIEW_INPUT: Tool = {\n id: 'overview',\n eligibility: { always: true },\n bindings: [\n { spec: { kind: 'pointerDown' }, actionId: MINIMAP_CENTER },\n { spec: { kind: 'drag' }, actionId: MINIMAP_PAN },\n ],\n};\nconst TOOLS_BY_ID: ReadonlyMap<string, Tool> = new Map([[OVERVIEW_INPUT.id, OVERVIEW_INPUT]]);\nconst CHANNELS = { wheel: false, pinch: false, contextMenu: false, ingest: false } as const;\n\n/** A world rect as a frame-local one. */\nfunction toLocalRect(b: OverviewBounds, frame: WorldFrame): OverviewBounds {\n if (frame.yDir === 1) return b;\n return { x: b.x, y: -(b.y + b.height), width: b.width, height: b.height };\n}\n\n/** The camera that fits `rect` into `size`, `PADDING` in from every edge. */\nfunction fitRect(rect: OverviewBounds, size: ViewportSize): View {\n const s = Math.min(\n Math.max(1, size.width - 2 * PADDING) / Math.max(rect.width, 1e-9),\n Math.max(1, size.height - 2 * PADDING) / Math.max(rect.height, 1e-9),\n );\n return {\n x: rect.x + rect.width / 2 - size.width / (2 * s),\n y: rect.y + rect.height / 2 - size.height / (2 * s),\n scale: { x: s, y: s },\n };\n}\n\n/** A canvas's 2D context sized to `size` at the device pixel ratio. */\nfunction prepare(\n canvas: HTMLCanvasElement | null,\n size: ViewportSize,\n): CanvasRenderingContext2D | null {\n const ctx = canvas?.getContext('2d') ?? null;\n if (!canvas || !ctx) return null;\n const dpr = window.devicePixelRatio || 1;\n const w = Math.round(size.width * dpr);\n const h = Math.round(size.height * dpr);\n if (canvas.width !== w) canvas.width = w;\n if (canvas.height !== h) canvas.height = h;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, size.width, size.height);\n return ctx;\n}\n\nfunction cssVar(el: Element | null, name: string, fallback: string): string {\n if (!el) return fallback;\n return getComputedStyle(el).getPropertyValue(name).trim() || fallback;\n}\n\n/**\n * A trial's overview. Mount it from an instrument's `render` (a canvas\n * instrument) or `stage.overlay` (a DOM stage) — anywhere inside the trial's\n * camera.\n */\nexport function TrialOverview(props: TrialOverviewProps) {\n const camera = useContext(CameraContext);\n if (!camera) return null;\n return <Overview {...props} camera={camera} />;\n}\n\nfunction Overview({\n width,\n height,\n render,\n bounds,\n title = 'Overview',\n anchor = 'bottom-left',\n persist,\n className,\n camera,\n}: TrialOverviewProps & { camera: CameraContextValue }) {\n const size = useMemo(() => ({ width, height }), [width, height]);\n const content = bounds ? toLocalRect(bounds, camera.frame) : camera.content;\n // `content` is rebuilt every render, so the fit keys on its numbers.\n const framed = content !== null && content !== undefined;\n const { x = 0, y = 0, width: w = 0, height: h = 0 } = content ?? {};\n const fit = useMemo(\n () =>\n framed ? fitRect({ x, y, width: w, height: h }, size) : { x: 0, y: 0, scale: { x: 1, y: 1 } },\n [framed, x, y, w, h, size],\n );\n const boxRef = useRef<HTMLDivElement | null>(null);\n\n return (\n <FloatingPanel\n anchor={anchor}\n className={className ? `lk-overview ${className}` : 'lk-overview'}\n {...(persist ? { persist } : {})}\n >\n <div className=\"lk-overview__title\">{title}</div>\n <div ref={boxRef} className=\"lk-overview__box\" data-no-drag style={boxVars(size)}>\n {render && content ? (\n <div className=\"lk-overview__content\" style={contentVars(fit, content)}>\n {render({ size: { width: content.width, height: content.height } })}\n </div>\n ) : null}\n {render ? null : <LayersCanvas fit={fit} size={size} frame={camera.frame} />}\n <OverviewMarks fit={fit} camera={camera} />\n <ChromeCanvas fit={fit} size={size} camera={camera} />\n <OverviewInput boxRef={boxRef} fit={fit} camera={camera} />\n </div>\n </FloatingPanel>\n );\n}\n\n// The box's size and the content's camera change with props, and a transform\n// has nowhere to live but the element's own style: set as custom properties\n// the stylesheet reads, as `<Stage>` does.\nfunction boxVars(size: ViewportSize): CSSProperties {\n return {\n ['--lk-overview-w' as string]: `${size.width}px`,\n ['--lk-overview-h' as string]: `${size.height}px`,\n } as CSSProperties;\n}\n\nfunction contentVars(fit: View, content: OverviewBounds): CSSProperties {\n const s = fit.scale.x;\n return {\n ['--lk-overview-x' as string]: `${(content.x - fit.x) * s}px`,\n ['--lk-overview-y' as string]: `${(content.y - fit.y) * s}px`,\n ['--lk-overview-zoom' as string]: String(s),\n ['--lk-overview-cw' as string]: `${content.width}px`,\n ['--lk-overview-ch' as string]: `${content.height}px`,\n } as CSSProperties;\n}\n\n/** A canvas instrument's layers, redrawn through the fit camera. */\nfunction LayersCanvas({ fit, size, frame }: { fit: View; size: ViewportSize; frame: WorldFrame }) {\n const stack = useContext(CanvasStackContext);\n const ref = useRef<HTMLCanvasElement | null>(null);\n const layers = stack?.surface?.layers;\n useLayoutEffect(() => {\n const ctx = prepare(ref.current, size);\n if (!ctx || !layers) return;\n const local: WorldFrame = { originPx: { x: 0, y: 0 }, yDir: frame.yDir };\n const view = fromCameraView(fit, local);\n for (const layer of layers) {\n if (!layer.visible || layer.id.startsWith('__lk_')) continue;\n ctx.save();\n layer.render(ctx, view, local);\n ctx.restore();\n }\n }, [layers, fit, size, frame.yDir]);\n return <canvas ref={ref} className=\"lk-overview__canvas\" />;\n}\n\n/** The visible-rect indicator and the linked crosshair. */\nfunction ChromeCanvas({\n fit,\n size,\n camera,\n}: {\n fit: View;\n size: ViewportSize;\n camera: CameraContextValue;\n}) {\n const ref = useRef<HTMLCanvasElement | null>(null);\n const pointer = usePointerPosition();\n const root = camera.view.get();\n const host = camera.view.hostSize?.() ?? null;\n // No dependency list: this renders only when the pointer or the trial's\n // camera moved, and either one moves the chrome.\n useLayoutEffect(() => {\n const ctx = prepare(ref.current, size);\n if (!ctx) return;\n const s = fit.scale.x;\n const px = (x: number, y: number) => ({ x: (x - fit.x) * s, y: (y - fit.y) * s });\n if (host) {\n const a = px(root.x, root.y);\n ctx.strokeStyle = cssVar(ref.current, '--wzl-fg', '#ffffff');\n ctx.lineWidth = 1;\n ctx.setLineDash([2, 3]);\n ctx.strokeRect(\n a.x + 0.5,\n a.y + 0.5,\n (host.width / root.scale.x) * s,\n (host.height / root.scale.y) * s,\n );\n ctx.setLineDash([]);\n }\n const world = pointerElsewhere(pointer, OVERVIEW_VIEW_ID, camera.frame);\n if (world) {\n // Back to frame-local: the map is its own inverse.\n const at = px(world.x, world.y * camera.frame.yDir);\n const { halo, bars } = crosshairRects(at.x, at.y, 1);\n ctx.fillStyle = 'rgb(0 0 0 / 60%)';\n for (const r of halo) ctx.fillRect(r.x, r.y, r.w, r.h);\n ctx.fillStyle = cssVar(ref.current, '--wzl-accent', '#4c8dff');\n for (const r of bars) ctx.fillRect(r.x, r.y, r.w, r.h);\n }\n });\n return <canvas ref={ref} className=\"lk-overview__canvas lk-overview__chrome\" />;\n}\n\n/**\n * The overview's input, on a dispatcher of its own: its actions and deps are\n * isolated from the trial's, its pointer store is shared with it.\n */\nfunction OverviewInput(props: {\n boxRef: RefObject<HTMLDivElement | null>;\n fit: View;\n camera: CameraContextValue;\n}) {\n return (\n <DepRegistryProvider>\n <ActionsProvider>\n <ActiveToolContextProvider>\n <OverviewDispatch {...props} />\n </ActiveToolContextProvider>\n </ActionsProvider>\n </DepRegistryProvider>\n );\n}\n\nfunction OverviewDispatch({\n boxRef,\n fit,\n camera,\n}: {\n boxRef: RefObject<HTMLDivElement | null>;\n fit: View;\n camera: CameraContextValue;\n}) {\n const registry = useActionsRegistry();\n const fitRef = useRef(fit);\n fitRef.current = fit;\n const root = camera.view;\n useDepSource('rootView', () => root as ViewApi);\n\n useEffect(() => {\n if (!registry) return;\n const offs = [registry.register(minimapCenterAction()), registry.register(minimapPanAction())];\n return () => {\n for (const off of offs) off();\n };\n }, [registry]);\n\n const clientToLocal = useMemo(\n () => (cx: number, cy: number) => {\n const r = boxRef.current?.getBoundingClientRect();\n const f = fitRef.current;\n return {\n x: (cx - (r?.left ?? 0)) / f.scale.x + f.x,\n y: (cy - (r?.top ?? 0)) / f.scale.y + f.y,\n };\n },\n [boxRef],\n );\n\n useGestureDispatcher({\n canvasRef: boxRef,\n // Always present: `OverviewInput` mounts the provider directly above.\n actions: registry as NonNullable<typeof registry>,\n entriesById: TOOLS_BY_ID,\n clientToWorld: clientToLocal,\n keyboard: false,\n channels: CHANNELS,\n });\n\n usePublishPointer(boxRef, clientToLocal, camera.frame, OVERVIEW_VIEW_ID);\n\n // The box sits inside the stage's element, whose own dispatcher, loupe and\n // pointer publisher would otherwise take every event that bubbles out of it.\n useEffect(() => {\n const el = boxRef.current;\n if (!el) return;\n const stop = (e: Event): void => e.stopPropagation();\n const kinds = [\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'click',\n 'wheel',\n 'dblclick',\n 'contextmenu',\n ];\n for (const k of kinds) el.addEventListener(k, stop);\n return () => {\n for (const k of kinds) el.removeEventListener(k, stop);\n };\n }, [boxRef]);\n\n return null;\n}\n"]}