@weasel-js/labkit 1.6.1 → 1.7.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.
Files changed (256) hide show
  1. package/README.md +3 -3
  2. package/dist/_dts/{CanvasStackContext-Wo6OwIVM.d.ts → CanvasStackContext-Cjy6I-Iv.d.ts} +4 -1
  3. package/dist/_dts/{Stage-DWBBi8IU.d.ts → Stage-D6t7o0pF.d.ts} +59 -18
  4. package/dist/_dts/{index-CKyX0ME_.d.ts → index-BTmaRy6a.d.ts} +10 -6
  5. package/dist/_dts/{types-B7yBTTF9.d.ts → index-DCMA4Fy1.d.ts} +59 -113
  6. package/dist/_dts/{types-DTuViBvc.d.ts → types-BUBSIZuV.d.ts} +18 -8
  7. package/dist/_dts/{useTrialState-CMaV6UD4.d.ts → useTrialState-Bh8bzFgE.d.ts} +1 -1
  8. package/dist/canvas/index.d.ts +8 -6
  9. package/dist/canvas/index.js +6 -6
  10. package/dist/chrome/index.d.ts +6 -4
  11. package/dist/chrome/index.js +11 -10
  12. package/dist/chunk-2ES4W47N.js +8 -0
  13. package/dist/chunk-2ES4W47N.js.map +1 -0
  14. package/dist/{chunk-WPLAANH4.js → chunk-2N7UBSS2.js} +12 -12
  15. package/dist/chunk-2N7UBSS2.js.map +1 -0
  16. package/dist/chunk-4KSC7NXL.js +15491 -0
  17. package/dist/chunk-4KSC7NXL.js.map +1 -0
  18. package/dist/{chunk-SLAQPUI4.js → chunk-CEGFXTBD.js} +10 -63
  19. package/dist/chunk-CEGFXTBD.js.map +1 -0
  20. package/dist/chunk-CZZPXUOU.js +3 -0
  21. package/dist/{chunk-SBXNQW4G.js.map → chunk-CZZPXUOU.js.map} +1 -1
  22. package/dist/{chunk-QGMOA2YE.js → chunk-E23Q6A3Y.js} +296 -104
  23. package/dist/chunk-E23Q6A3Y.js.map +1 -0
  24. package/dist/{chunk-GHGO52SO.js → chunk-E6NARGU7.js} +5 -5
  25. package/dist/{chunk-GHGO52SO.js.map → chunk-E6NARGU7.js.map} +1 -1
  26. package/dist/{chunk-IJJUK4FU.js → chunk-EESJUSYR.js} +5 -6
  27. package/dist/chunk-EESJUSYR.js.map +1 -0
  28. package/dist/{chunk-HXXTULDN.js → chunk-EFXRM6XQ.js} +8 -11
  29. package/dist/chunk-EFXRM6XQ.js.map +1 -0
  30. package/dist/chunk-F4ACIK7Z.js +210 -0
  31. package/dist/chunk-F4ACIK7Z.js.map +1 -0
  32. package/dist/{chunk-6FLXL3I7.js → chunk-FB4MIZOO.js} +77 -45
  33. package/dist/chunk-FB4MIZOO.js.map +1 -0
  34. package/dist/{chunk-AWQG7FCC.js → chunk-FG3EXZDV.js} +3 -3
  35. package/dist/{chunk-AWQG7FCC.js.map → chunk-FG3EXZDV.js.map} +1 -1
  36. package/dist/{chunk-4TT3PNPR.js → chunk-GAHELLJT.js} +95 -95
  37. package/dist/chunk-GAHELLJT.js.map +1 -0
  38. package/dist/{chunk-HKV7LRHY.js → chunk-IBVRMHW5.js} +5 -5
  39. package/dist/{chunk-HKV7LRHY.js.map → chunk-IBVRMHW5.js.map} +1 -1
  40. package/dist/{chunk-ASOK76ZH.js → chunk-JFOJESJF.js} +72 -29
  41. package/dist/chunk-JFOJESJF.js.map +1 -0
  42. package/dist/{chunk-RWNDDKOH.js → chunk-PMLZP7RT.js} +18 -15
  43. package/dist/chunk-PMLZP7RT.js.map +1 -0
  44. package/dist/{chunk-TJ77A4A3.js → chunk-READTDQX.js} +5 -9
  45. package/dist/chunk-READTDQX.js.map +1 -0
  46. package/dist/{chunk-JVBYV6TL.js → chunk-ROGJWDQA.js} +3 -3
  47. package/dist/{chunk-JVBYV6TL.js.map → chunk-ROGJWDQA.js.map} +1 -1
  48. package/dist/{chunk-CGHF6RZV.js → chunk-T6DLGEJB.js} +18 -18
  49. package/dist/chunk-T6DLGEJB.js.map +1 -0
  50. package/dist/{chunk-24W5VEJW.js → chunk-ULYYNWGP.js} +2 -2
  51. package/dist/{chunk-24W5VEJW.js.map → chunk-ULYYNWGP.js.map} +1 -1
  52. package/dist/{chunk-DWMSB4CA.js → chunk-VYVGVBGY.js} +95 -32
  53. package/dist/chunk-VYVGVBGY.js.map +1 -0
  54. package/dist/{chunk-IRTGUQ5O.js → chunk-WLMDTI2Q.js} +16 -6
  55. package/dist/chunk-WLMDTI2Q.js.map +1 -0
  56. package/dist/{chunk-NEXR37YJ.js → chunk-XE5HJOPL.js} +6 -8
  57. package/dist/chunk-XE5HJOPL.js.map +1 -0
  58. package/dist/chunk-XVUKDSYV.js +38 -0
  59. package/dist/chunk-XVUKDSYV.js.map +1 -0
  60. package/dist/{chunk-O6FFFOHS.js → chunk-YCFKW5RX.js} +16 -8
  61. package/dist/chunk-YCFKW5RX.js.map +1 -0
  62. package/dist/{chunk-CQLQPQ4P.js → chunk-ZQIHJXP4.js} +2 -2
  63. package/dist/chunk-ZQIHJXP4.js.map +1 -0
  64. package/dist/config/index.d.ts +28 -13
  65. package/dist/config/index.js +4 -4
  66. package/dist/controls/index.d.ts +4 -7
  67. package/dist/controls/index.js +6 -5
  68. package/dist/dragdrop/index.d.ts +23 -3
  69. package/dist/dragdrop/index.js +4 -3
  70. package/dist/fonts/oswald-tabular-digits.woff2 +0 -0
  71. package/dist/index.d.ts +72 -487
  72. package/dist/index.js +603 -441
  73. package/dist/index.js.map +1 -1
  74. package/dist/job/index.d.ts +8 -0
  75. package/dist/job/index.js +1 -1
  76. package/dist/layers/index.d.ts +6 -5
  77. package/dist/layers/index.js +3 -2
  78. package/dist/loupe/index.d.ts +100 -30
  79. package/dist/loupe/index.js +585 -6
  80. package/dist/loupe/index.js.map +1 -1
  81. package/dist/overview/index.d.ts +4 -2
  82. package/dist/overview/index.js +20 -18
  83. package/dist/overview/index.js.map +1 -1
  84. package/dist/passthrough/weasel-canvas.d.ts +2 -0
  85. package/dist/passthrough/weasel-ui.d.ts +2 -2701
  86. package/dist/passthrough/weasel-ui.js +4 -3
  87. package/dist/primitives/index.d.ts +37 -2
  88. package/dist/primitives/index.js +9 -8
  89. package/dist/state/index.d.ts +7 -5
  90. package/dist/state/index.js +6 -6
  91. package/dist/styles.css +150 -126
  92. package/dist/surface/index.d.ts +10 -2
  93. package/dist/surface/index.js +1 -1
  94. package/dist/undo/index.d.ts +5 -3
  95. package/package.json +14 -13
  96. package/src/annotations/AnnotationOverlay.abandon.test.tsx +143 -0
  97. package/src/annotations/AnnotationOverlay.tsx +52 -33
  98. package/src/annotations/Annotations.less +0 -10
  99. package/src/annotations/Annotations.overlay.test.tsx +31 -0
  100. package/src/annotations/MarkList.tsx +4 -8
  101. package/src/annotations/capture.browser.test.ts +41 -0
  102. package/src/annotations/capture.ts +14 -7
  103. package/src/annotations/drawOne.ts +2 -2
  104. package/src/annotations/frac.test.ts +44 -1
  105. package/src/annotations/frac.ts +46 -18
  106. package/src/annotations/index.ts +1 -0
  107. package/src/annotations/paint.test.ts +76 -46
  108. package/src/annotations/paint.ts +65 -43
  109. package/src/annotations/store.test.ts +212 -2
  110. package/src/annotations/store.ts +130 -27
  111. package/src/annotations/svgNodes.test.ts +28 -19
  112. package/src/annotations/svgNodes.ts +6 -6
  113. package/src/annotations/types.ts +29 -10
  114. package/src/canvas/AGENTS.md +1 -0
  115. package/src/canvas/CameraInput.test.tsx +30 -0
  116. package/src/canvas/CameraInput.tsx +60 -48
  117. package/src/canvas/CanvasStack.tsx +11 -6
  118. package/src/canvas/CanvasStackContext.ts +4 -1
  119. package/src/canvas/LinkedCursor.less +1 -1
  120. package/src/canvas/Stage.test.tsx +33 -4
  121. package/src/canvas/Stage.tsx +9 -4
  122. package/src/canvas/cameraRegistry.test.ts +55 -0
  123. package/src/canvas/cameraRegistry.ts +87 -0
  124. package/src/canvas/cameraZoom.test.ts +46 -0
  125. package/src/canvas/cameraZoom.ts +24 -0
  126. package/src/canvas/index.ts +14 -2
  127. package/src/canvas/sharedScope.test.tsx +94 -0
  128. package/src/canvas/worldSpec.ts +3 -1
  129. package/src/chrome/ChromeRegions.stories.tsx +1 -0
  130. package/src/chrome/builtins.test.ts +7 -6
  131. package/src/chrome/builtins.tsx +5 -1
  132. package/src/chrome/index.ts +1 -1
  133. package/src/chrome/merge.test.ts +5 -0
  134. package/src/chrome/merge.ts +4 -2
  135. package/src/chrome/regions/PaletteRegion.less +0 -7
  136. package/src/chrome/regions/PaletteRegion.test.tsx +2 -3
  137. package/src/chrome/regions/PaletteRegion.tsx +3 -4
  138. package/src/chrome/regions/SidebarRegion.less +12 -2
  139. package/src/chrome/regions/ViewportRegion.browser.test.less +6 -0
  140. package/src/chrome/regions/ViewportRegion.browser.test.tsx +38 -0
  141. package/src/chrome/regions/ViewportRegion.less +6 -0
  142. package/src/chrome/regions/ViewportRegion.tsx +10 -4
  143. package/src/chrome/types.ts +4 -2
  144. package/src/config/auto.ts +1 -0
  145. package/src/config/builder.test.ts +2 -1
  146. package/src/config/builder.ts +18 -5
  147. package/src/config/fromConfigField.test.ts +14 -0
  148. package/src/config/fromConfigField.ts +18 -4
  149. package/src/config/path.ts +2 -5
  150. package/src/config/resolve.ts +4 -0
  151. package/src/config/types.ts +14 -9
  152. package/src/controls/ControlPanel.less +1 -1
  153. package/src/controls/ControlPanel.stories.tsx +3 -2
  154. package/src/controls/ControlPanel.test.tsx +98 -11
  155. package/src/controls/ControlPanel.tsx +61 -2
  156. package/src/dragdrop/DragDropRuntime.tsx +22 -13
  157. package/src/dragdrop/DragGhost.tsx +3 -0
  158. package/src/dragdrop/Palette.tsx +2 -0
  159. package/src/index.test.ts +5 -0
  160. package/src/index.ts +16 -2
  161. package/src/instrument/types.ts +0 -6
  162. package/src/job/useJob.ts +14 -9
  163. package/src/lab/Lab.tsx +88 -73
  164. package/src/lab/{LabFit.stories.less → LabFit.browser.test.less} +1 -1
  165. package/src/lab/{LabFit.stories.tsx → LabFit.browser.test.tsx} +41 -63
  166. package/src/lab/LabFullChrome.stories.tsx +1 -0
  167. package/src/lab/LabHeader.test.tsx +24 -14
  168. package/src/lab/LabHeader.tsx +11 -12
  169. package/src/lab/LabShell.less +9 -17
  170. package/src/lab/LabSwitcher.browser.test.tsx +27 -0
  171. package/src/lab/LabSwitcher.less +8 -6
  172. package/src/lab/LabZoom.test.tsx +223 -0
  173. package/src/lab/LabZoom.tsx +170 -0
  174. package/src/lab/Workspace.tsx +48 -52
  175. package/src/lab/index.ts +1 -0
  176. package/src/loupe/AGENTS.md +28 -15
  177. package/src/loupe/CanvasLoupe.tsx +2 -3
  178. package/src/loupe/DomLoupe.tsx +1 -7
  179. package/src/loupe/Loupe.less +16 -0
  180. package/src/loupe/LoupeGestures.tsx +1 -1
  181. package/src/loupe/TrialLoupe.tsx +120 -57
  182. package/src/loupe/index.ts +1 -6
  183. package/src/loupe/types.test.ts +3 -3
  184. package/src/loupe/types.ts +24 -27
  185. package/src/loupe/useLoupe.test.tsx +29 -4
  186. package/src/loupe/useLoupe.ts +20 -17
  187. package/src/overview/OverviewMarks.tsx +12 -4
  188. package/src/overview/TrialOverview.tsx +10 -8
  189. package/src/passthrough/weasel-ui.test.ts +2 -0
  190. package/src/passthrough/weasel-ui.ts +17 -5
  191. package/src/primitives/FloatingPanel.test.tsx +11 -0
  192. package/src/primitives/FloatingPanel.tsx +8 -7
  193. package/src/primitives/JobProgress.less +2 -1
  194. package/src/primitives/Legend.stories.tsx +1 -0
  195. package/src/primitives/Readout.browser.test.tsx +137 -0
  196. package/src/primitives/Readout.less +12 -0
  197. package/src/primitives/Readout.stories.tsx +58 -0
  198. package/src/primitives/Readout.test.tsx +93 -0
  199. package/src/primitives/Readout.tsx +56 -0
  200. package/src/primitives/Split.tsx +69 -41
  201. package/src/primitives/Toolbar.tsx +3 -3
  202. package/src/primitives/ZoomControl.less +1 -2
  203. package/src/primitives/index.ts +2 -0
  204. package/src/specimen/Specimen.stories.tsx +1 -0
  205. package/src/specimen/Specimen.tsx +2 -3
  206. package/src/state/Persistence.browser.test.tsx +47 -0
  207. package/src/state/store.test.ts +5 -1
  208. package/src/state/undock.ts +3 -0
  209. package/src/state/useOpenOnce.ts +20 -16
  210. package/src/state/usePersistedState.test.tsx +1 -1
  211. package/src/state/view.ts +1 -0
  212. package/src/styles.less +1 -0
  213. package/src/surface/index.ts +1 -1
  214. package/src/surface/useTiledSurface.test.tsx +14 -0
  215. package/src/surface/useTiledSurface.ts +11 -3
  216. package/src/theme/Interstellar.stories.tsx +3 -1
  217. package/src/theme/base.less +21 -47
  218. package/src/theme/interstellar.test.ts +1 -1
  219. package/src/theme/interstellar.theme.json +10 -3
  220. package/src/trial/Trial.annotations.test.tsx +73 -0
  221. package/src/trial/Trial.less +0 -29
  222. package/src/trial/Trial.loupe.test.tsx +28 -11
  223. package/src/trial/Trial.tsx +85 -67
  224. package/src/trial/TrialChrome.tsx +27 -6
  225. package/src/trial/index.ts +2 -0
  226. package/src/trial/loupeSwitch.ts +15 -0
  227. package/dist/_dts/LayerList.d-BoELAMsM.d.ts +0 -83
  228. package/dist/_dts/PrefsRow.d-DFpPIeIC.d.ts +0 -39
  229. package/dist/_dts/PropertyField.d-D0ndfIIt.d.ts +0 -404
  230. package/dist/_dts/Select.d-Cr8-zoup.d.ts +0 -569
  231. package/dist/chunk-4TT3PNPR.js.map +0 -1
  232. package/dist/chunk-6FLXL3I7.js.map +0 -1
  233. package/dist/chunk-ASOK76ZH.js.map +0 -1
  234. package/dist/chunk-CGHF6RZV.js.map +0 -1
  235. package/dist/chunk-CQLQPQ4P.js.map +0 -1
  236. package/dist/chunk-DWMSB4CA.js.map +0 -1
  237. package/dist/chunk-HXXTULDN.js.map +0 -1
  238. package/dist/chunk-IJJUK4FU.js.map +0 -1
  239. package/dist/chunk-IRTGUQ5O.js.map +0 -1
  240. package/dist/chunk-KHK5FGER.js +0 -145
  241. package/dist/chunk-KHK5FGER.js.map +0 -1
  242. package/dist/chunk-NEXR37YJ.js.map +0 -1
  243. package/dist/chunk-O6FFFOHS.js.map +0 -1
  244. package/dist/chunk-QGMOA2YE.js.map +0 -1
  245. package/dist/chunk-RWNDDKOH.js.map +0 -1
  246. package/dist/chunk-S4NCI32V.js +0 -549
  247. package/dist/chunk-S4NCI32V.js.map +0 -1
  248. package/dist/chunk-SBXNQW4G.js +0 -3
  249. package/dist/chunk-SLAQPUI4.js.map +0 -1
  250. package/dist/chunk-TJ77A4A3.js.map +0 -1
  251. package/dist/chunk-TYN6MLXX.js +0 -13864
  252. package/dist/chunk-TYN6MLXX.js.map +0 -1
  253. package/dist/chunk-WPLAANH4.js.map +0 -1
  254. package/src/primitives/useRovingTabIndex.test.ts +0 -48
  255. package/src/primitives/useRovingTabIndex.ts +0 -88
  256. package/src/state/Persistence.stories.tsx +0 -75
package/README.md CHANGED
@@ -143,7 +143,6 @@ provide the corresponding chrome; they compose freely.
143
143
  | `canvas` | A stack of layered `<canvas>` elements with pan and zoom, redrawing only the layers that changed |
144
144
  | `layers` | A sidebar list that hides and reorders those layers |
145
145
  | `annotations` | Drawing tools, an overlay on each region you name, a Marks panel, undo and export |
146
- | `loupe` | A magnifier toggle that redraws the canvas through a zoomed camera, or calls your own `render` for DOM content |
147
146
  | `dragDrop` | A palette to drag items from, and a drop pipeline that hands you world coordinates |
148
147
  | `undo` | Undo and redo buttons, snapshotting state on the events you name |
149
148
  | `job` | Starts async work, aborts it on unmount and on a key change, and renders progress and a cancel control |
@@ -307,14 +306,15 @@ directly. Several expose more than the root does, `/state` most of all.
307
306
 
308
307
  | Subpath | |
309
308
  | --- | --- |
310
- | `@weasel-js/labkit` | Everything |
309
+ | `@weasel-js/labkit` | Everything but `/loupe` and `/overview` |
311
310
  | `/styles.css` | The one stylesheet |
312
311
  | `/primitives` | Toolbar, Sidebar, StatusBar, Legend, FloatingPanel, meters |
313
312
  | `/chrome` | Regions, contribution types, built-in contributions |
314
313
  | `/controls` | `<ControlPanel>` and the config field types |
315
314
  | `/canvas` | `<CanvasStack>`, coordinate helpers, `usePanZoom`, `useOrbit` |
316
315
  | `/layers` | `<LayerList>` from `@weasel-js/ui`, and the layers capability types |
317
- | `/loupe` | Magnifier components and `useLoupe` |
316
+ | `/loupe` | `<TrialLoupe>`, the magnifier an instrument mounts in its content; the trial offers a Loupe toggle while one is mounted. Not in the root barrel |
317
+ | `/overview` | `<TrialOverview>`, a floating panel showing a trial's whole content. Not in the root barrel |
318
318
  | `/surface` | Tiled surface hooks for your own renderer |
319
319
  | `/job` | `useJob` and the job capability types |
320
320
  | `/state` | The lab store, storage adapters, `useTrialState`, serialization helpers |
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject } from 'react';
3
- import { b as ViewTransform, W as WorldFrame } from './types-B7yBTTF9.js';
3
+ import { a as ViewTransform, W as WorldFrame, b as WorldSpec } from './index-DCMA4Fy1.js';
4
4
 
5
5
  /** One layer of a canvas stack: its id, whether it is currently shown, and how
6
6
  * it paints itself. */
@@ -25,6 +25,9 @@ interface CanvasStackSurface {
25
25
  canvases: RefObject<Map<string, HTMLCanvasElement>>;
26
26
  /** The layers as the stack is drawing them, bottom first. */
27
27
  layers: readonly CanvasLayerDescriptor[];
28
+ /** The coordinate system the stack was given, which `frame` resolves at
29
+ * its size — for a re-draw at another size. */
30
+ worldSpec?: WorldSpec;
28
31
  }
29
32
  /** What a canvas stack publishes to its children: the view, and the resolved
30
33
  * coordinate system it is read in, so DOM overlays can place themselves in the
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject, ReactNode } from 'react';
3
- import { ViewApi } from '@weasel-js/core';
4
- import { W as WorldFrame, P as Point, b as ViewTransform, l as WorldSpec, V as ViewportSize } from './types-B7yBTTF9.js';
5
- import { C as CanvasLayerDescriptor } from './CanvasStackContext-Wo6OwIVM.js';
3
+ import { ViewApi, Yoke } from '@weasel-js/core';
4
+ import { W as WorldFrame, P as Point, a as ViewTransform, b as WorldSpec, V as ViewportSize } from './index-DCMA4Fy1.js';
5
+ import { C as CanvasLayerDescriptor } from './CanvasStackContext-Cjy6I-Iv.js';
6
6
 
7
7
  /** The view id the stage publishes its pointer under. */
8
8
  declare const STAGE_VIEW_ID = "stage";
@@ -27,17 +27,16 @@ interface CameraContextValue {
27
27
  }
28
28
  /** The camera around the caller, or `null` outside one. */
29
29
  declare const CameraContext: react.Context<CameraContextValue | null>;
30
- /** Set by a host that already provides one input scope for everything inside
31
- * it — a trial — so the camera, the loupe and an overview share it. */
32
- declare const CameraScopeContext: react.Context<boolean>;
33
30
  /**
34
- * The actions and deps a camera routes through: the ones a trial provides, or
35
- * an isolated set of its own. Isolated, because an actions registry holds one
36
- * dispatcher, and a camera must not take input from a canvas beside it. The
37
- * pointer store is the exception — one already in scope is kept, since sharing
38
- * it is the point.
31
+ * The input scope a camera routes through. Inside another camera — a loupe or
32
+ * an overlay laid over a canvas stack — it joins that camera's scope, so its
33
+ * actions reach the dispatcher already listening there. Anywhere else it
34
+ * mounts a scope of its own, which keeps its own tool unless it joins `yoke`:
35
+ * a camera beside another never takes that one's gestures. The pointer store is the exception — one
36
+ * already in scope is kept, since sharing it is the point.
39
37
  */
40
- declare function CameraScope({ children }: {
38
+ declare function CameraScope({ yoke, children }: {
39
+ yoke?: Yoke;
41
40
  children: ReactNode;
42
41
  }): react.JSX.Element;
43
42
  /** Options for {@link useCameraView}. */
@@ -49,16 +48,27 @@ interface CameraViewOptions {
49
48
  minZoom?: number;
50
49
  maxZoom?: number;
51
50
  }
51
+ /** A camera's zoom limits, after widening to admit its opening zoom. */
52
+ interface ZoomRange {
53
+ min: number;
54
+ max: number;
55
+ }
56
+ /** A labkit camera: a weasel `ViewApi` that also says how far it zooms. */
57
+ interface CameraView extends ViewApi {
58
+ zoomRange(): ZoomRange;
59
+ }
52
60
  /**
53
61
  * A labkit camera as a stable `ViewApi`. Its zoom range is widened to keep the
54
62
  * opening zoom reachable — captured once, so a zoom-out cannot shrink the
55
63
  * range and strand the opening view behind it.
56
64
  */
57
- declare function useCameraView({ view, onViewChange, frame, hostRef, minZoom, maxZoom, }: CameraViewOptions): ViewApi;
65
+ declare function useCameraView({ view, onViewChange, frame, hostRef, minZoom, maxZoom, }: CameraViewOptions): CameraView;
58
66
  /** Props for `<CameraInput>`. */
59
67
  interface CameraInputProps {
60
68
  hostRef: RefObject<HTMLElement | null>;
61
- camera: ViewApi;
69
+ /** Published to the host's camera registry while its scope is the active
70
+ * one, so chrome acting on "the camera" acts on the one last used. */
71
+ camera: CameraView;
62
72
  frame: WorldFrame;
63
73
  /** A primary-button press released without crossing the drag threshold, at
64
74
  * the world point it landed on. */
@@ -89,12 +99,15 @@ interface CanvasStackProps {
89
99
  onResize?: (size: ViewportSize) => void;
90
100
  onHitTest?: (worldPos: Point) => void;
91
101
  children?: ReactNode;
102
+ /** A yoke to join, from `useYoke()`: cameras on one yoke share their tool
103
+ * and gesture in flight. Omitted, this one keeps its own. */
104
+ yoke?: Yoke;
92
105
  }
93
106
  /** Stacks one `<canvas>` per layer and drives them from a shared view, so a
94
107
  * layer that changes rarely is not redrawn with one that changes every frame.
95
108
  * Handles sizing and device pixel ratio; pan and zoom route through weasel's
96
109
  * gesture dispatcher (`<CameraInput>`). */
97
- declare function CanvasStack({ layers, view: viewProp, onViewChange, worldSpec, minZoom, maxZoom, width, height, className, onResize, onHitTest, children, }: CanvasStackProps): react.JSX.Element;
110
+ declare function CanvasStack({ layers, view: viewProp, onViewChange, worldSpec, minZoom, maxZoom, width, height, className, onResize, onHitTest, children, yoke, }: CanvasStackProps): react.JSX.Element;
98
111
 
99
112
  /** Bounds and coordinate system a {@link zoomAt} works in. */
100
113
  interface ZoomAtOptions {
@@ -117,6 +130,31 @@ declare function zoomAt(view: ViewTransform, factor: number, at: Point, { frame,
117
130
  * `zoom`. What a lens showing a region around one point renders through. */
118
131
  declare function centerOn(world: Point, zoom: number, size: ViewportSize, frame?: WorldFrame): ViewTransform;
119
132
 
133
+ /**
134
+ * Cameras by key, for chrome that acts on a camera it does not contain — the
135
+ * lab's zoom controls, which drive whichever trial has the focus.
136
+ */
137
+ interface CameraRegistry {
138
+ /** Publish `camera` under `key` and return its release. Registrants for one
139
+ * key stack, newest live. */
140
+ register(key: string, camera: CameraView): () => void;
141
+ get(key: string): CameraView | null;
142
+ /** Fires after any register or release. Shaped for `useSyncExternalStore`. */
143
+ subscribe(listener: () => void): () => void;
144
+ }
145
+ /** An empty in-memory `CameraRegistry`. */
146
+ declare function createCameraRegistry(): CameraRegistry;
147
+ /** The registry a lab keeps its trials' cameras in. */
148
+ declare const CameraRegistryContext: react.Context<CameraRegistry | null>;
149
+ /** Where a camera publishes itself. The host binds the key — a trial provides
150
+ * one that registers under its own id — so the camera needs no name. */
151
+ declare const CameraPublishContext: react.Context<((camera: CameraView) => () => void) | null>;
152
+ /** Publish `camera` to the host in scope for as long as the caller is mounted,
153
+ * and again each time its input scope becomes the active one — a registry
154
+ * key stacks newest-live, so the camera last used is the one chrome reaches.
155
+ * Call it inside the camera's scope. */
156
+ declare function usePublishCamera(camera: CameraView): void;
157
+
120
158
  /** Project a world point into screen coordinates under a view.
121
159
  *
122
160
  * `frame` is the instrument's declared coordinate system, resolved against the
@@ -143,6 +181,9 @@ interface StageProps {
143
181
  /** Drawn over the content in viewport pixels, outside the camera. */
144
182
  overlay?: ReactNode;
145
183
  children?: ReactNode;
184
+ /** A yoke to join, from `useYoke()`: cameras on one yoke share their tool
185
+ * and gesture in flight. Omitted, this one keeps its own. */
186
+ yoke?: Yoke;
146
187
  }
147
188
  /**
148
189
  * The camera a stage opens at: the content centered in its viewport, shrunk to
@@ -156,7 +197,7 @@ declare function fitStage(size: ViewportSize, viewport: ViewportSize): ViewTrans
156
197
  * its own size and transformed, so everything inside keeps its layout and a
157
198
  * measurement of any element in it reports where it is actually drawn.
158
199
  */
159
- declare function Stage({ size, view: viewProp, onViewChange, minZoom, maxZoom, onResize, hostRef, overlay, children, }: StageProps): react.JSX.Element;
200
+ declare function Stage({ size, view: viewProp, onViewChange, minZoom, maxZoom, onResize, hostRef, overlay, children, yoke, }: StageProps): react.JSX.Element;
160
201
 
161
- export { CameraContext as C, STAGE_VIEW_ID as S, CameraInput as b, CameraScope as d, CameraScopeContext as e, CanvasStack as g, Stage as i, centerOn as k, fitStage as l, screenToWorld as s, useCameraView as u, worldToScreen as w, zoomAt as z };
162
- export type { ZoomAtOptions as Z, CameraContextValue as a, CameraInputProps as c, CameraViewOptions as f, CanvasStackProps as h, StageProps as j };
202
+ export { CameraContext as C, STAGE_VIEW_ID as S, CameraInput as b, CameraPublishContext as d, CameraRegistryContext as f, CameraScope as g, CanvasStack as j, Stage as l, centerOn as o, createCameraRegistry as p, fitStage as q, usePublishCamera as r, screenToWorld as s, useCameraView as u, worldToScreen as w, zoomAt as z };
203
+ export type { ZoomAtOptions as Z, CameraContextValue as a, CameraInputProps as c, CameraRegistry as e, CameraView as h, CameraViewOptions as i, CanvasStackProps as k, StageProps as m, ZoomRange as n };
@@ -1,8 +1,11 @@
1
- import { d as ControlRenderer, c as ConfigRule } from './types-DTuViBvc.js';
2
- import { R as Instrument, ax as TrialChromeContext, Y as TrialContribution, T as TrialRecord, t as InstrumentList, f as SavedSnapshot, s as LabMode, av as ToolSlotContext, a9 as ContributionBase, aw as ToolbarItem, au as ToolItem, ao as SidebarSection, ar as StatusReadout, O as TrialTool, ak as RegionContribution, ap as SidebarSlotContext } from './types-B7yBTTF9.js';
1
+ import { c as ControlRenderer, b as ConfigRule } from './types-BUBSIZuV.js';
2
+ import { O as Instrument, ar as TrialChromeContext, R as TrialContribution, T as TrialRecord, q as InstrumentList, e as SavedSnapshot, o as LabMode, ap as ToolSlotContext, a8 as ContributionBase, aq as ToolbarItem, ao as ToolItem, ai as SidebarSection, al as StatusReadout, K as TrialTool, af as RegionContribution, aj as SidebarSlotContext } from './index-DCMA4Fy1.js';
3
3
  import * as react from 'react';
4
4
  import { ReactNode } from 'react';
5
5
 
6
+ /** Built-in ids contributed only while runtime state says so — the loupe
7
+ * toggle appears once a lens mounts, after the trial's first render. */
8
+ declare const TRANSIENT_BUILTINS: ReadonlySet<string>;
6
9
  /**
7
10
  * The contributions a trial gets from what its instrument declared. This is
8
11
  * the whole "declaring a capability provides the chrome" rule — it replaces
@@ -165,11 +168,12 @@ declare function mergeContributions<T extends {
165
168
  /**
166
169
  * Drop contributions by id. Throws when an id is not present: a typo that
167
170
  * silently suppresses nothing is the same class of bug as a duplicate id
168
- * silently winning.
171
+ * silently winning. `transient` names ids that are legitimately absent at
172
+ * times — contributed only while some runtime state holds.
169
173
  */
170
174
  declare function suppressContributions<T extends {
171
175
  id: string;
172
- }>(bundle: readonly T[], ids: readonly string[]): T[];
176
+ }>(bundle: readonly T[], ids: readonly string[], transient?: ReadonlySet<string>): T[];
173
177
 
174
178
  /** Props for `<PaletteRegion>`. */
175
179
  interface PaletteRegionProps<TCtx extends ToolSlotContext = TrialChromeContext> {
@@ -244,5 +248,5 @@ interface ViewportRegionProps {
244
248
  */
245
249
  declare function ViewportRegion({ contributions, ctx }: ViewportRegionProps): react.JSX.Element | null;
246
250
 
247
- export { resetTrial as B, suppressContributions as C, toolContributions as D, useLabChromeContext as E, useLabContext as F, PaletteRegion as P, SidebarRegion as S, ViewportRegion as V, LabAsideRegion as a, LabContext as c, LabFooterRegion as e, LabHeaderRegion as f, LabSidebarRegion as i, StatusRegion as l, TitleBarRegion as n, ToolbarRegion as p, addTrial as s, builtinContributions as t, cloneTrial as u, closeTrial as v, contributionsIn as w, labContributions as x, mergeContributions as y, reorderTrials as z };
248
- export type { AddTrialOptions as A, LabContribution as L, TitleBarPlacement as T, LabChromeContext as b, LabContextValue as d, LabRegion as g, LabRegionProps as h, PaletteRegionProps as j, SidebarRegionProps as k, StatusRegionProps as m, TitleBarRegionProps as o, ToolbarRegionProps as q, ViewportRegionProps as r };
251
+ export { reorderTrials as B, resetTrial as C, suppressContributions as D, toolContributions as E, useLabChromeContext as F, useLabContext as G, PaletteRegion as P, SidebarRegion as S, TRANSIENT_BUILTINS as T, ViewportRegion as V, LabAsideRegion as a, LabContext as c, LabFooterRegion as e, LabHeaderRegion as f, LabSidebarRegion as i, StatusRegion as l, TitleBarRegion as o, ToolbarRegion as q, addTrial as t, builtinContributions as u, cloneTrial as v, closeTrial as w, contributionsIn as x, labContributions as y, mergeContributions as z };
252
+ export type { AddTrialOptions as A, LabContribution as L, LabChromeContext as b, LabContextValue as d, LabRegion as g, LabRegionProps as h, PaletteRegionProps as j, SidebarRegionProps as k, StatusRegionProps as m, TitleBarPlacement as n, TitleBarRegionProps as p, ToolbarRegionProps as r, ViewportRegionProps as s };
@@ -1,8 +1,9 @@
1
- import { DrawCommand, Scene, PointerContextValue } from '@weasel-js/core';
1
+ import { StanceProps } from '@weasel-js/ui';
2
+ import { View, DrawCommand, Scene, PointerContextValue } from '@weasel-js/core';
2
3
  import { RefObject, ComponentType, ReactNode } from 'react';
3
- import { S as StanceProps } from './PrefsRow.d-DFpPIeIC.js';
4
- import { R as ResolvedConfig, C as ConfigPath, V as ValueAtPath, a as ConfigField, b as ConfigSchema } from './types-DTuViBvc.js';
5
4
  import { ColorModePreference } from '@weasel-js/theme';
5
+ import { R as ResolvedConfig, n as ConfigPath, V as ValueAtPath, a as ConfigField, C as ConfigSchema } from './types-BUBSIZuV.js';
6
+ import { Rect } from '@weasel-js/geom';
6
7
  import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
7
8
 
8
9
  /**
@@ -15,6 +16,7 @@ import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
15
16
  declare const auto: unique symbol;
16
17
  /** The type of the `auto` sentinel, for widening a value parameter. */
17
18
  type Auto = typeof auto;
19
+ /** Whether `value` is the `auto` sentinel. */
18
20
  declare function isAuto(value: unknown): value is Auto;
19
21
 
20
22
  /** A sidebar section torn out of its trial, and where it went. */
@@ -25,7 +27,10 @@ interface UndockedPanel {
25
27
  }
26
28
  /** Undocked panels, keyed by {@link panelKey}. */
27
29
  type UndockedPanels = Record<string, UndockedPanel>;
30
+ /** The `UndockedPanels` key for one trial's section. */
28
31
  declare function panelKey(trialId: string, sectionId: string): string;
32
+ /** `panels` with the section marked undocked `as` a tile or a floating panel,
33
+ * replacing any earlier entry for it. Returns a new record. */
29
34
  declare function undockPanel(panels: UndockedPanels, trialId: string, sectionId: string, as?: UndockedPanel['as']): UndockedPanels;
30
35
  /** Dock one section back, or — with no `sectionId` — every panel the trial
31
36
  * owns, which is what closing a trial wants. */
@@ -218,12 +223,7 @@ type Migration = (doc: Record<string, unknown>) => Record<string, unknown>;
218
223
 
219
224
  /** A mark's box in its target's world: CSS pixels at zoom 1. Matches weasel's
220
225
  * default `RectPose`, which is what the scene stores. */
221
- interface WorldRect {
222
- x: number;
223
- y: number;
224
- width: number;
225
- height: number;
226
- }
226
+ type WorldRect = Rect;
227
227
  /** Where a fraction lands on a content box of this size. */
228
228
  declare function fracToWorld(f: FracRect, content: {
229
229
  w: number;
@@ -267,14 +267,15 @@ interface MarkStyle {
267
267
  /**
268
268
  * What one mark draws, in its target's world.
269
269
  *
270
- * Pure: a node and the target's content box in, draw commands out. Geometry
271
- * that a bounding box cannot describe — a line's ends, a stroke's path — comes
272
- * from `data.points`, which is in fractions like the bounds.
270
+ * Pure: a node in, draw commands out, in its target's world. Geometry that a
271
+ * bounding box cannot describe — a line's ends, a stroke's path — comes from
272
+ * `data.shape`, placed in the pose.
273
+ *
274
+ * `scale` is the view's, world to screen. Only a point reads it — its ring is
275
+ * sized in screen pixels — and the default draws that ring as though a world
276
+ * unit were a pixel.
273
277
  */
274
- declare function markCommands(m: PaintableMark, content: {
275
- w: number;
276
- h: number;
277
- }, style?: MarkStyle): DrawCommand[];
278
+ declare function markCommands(m: PaintableMark, style?: MarkStyle, scale?: View['scale']): DrawCommand[];
278
279
 
279
280
  /** A mark's box in its target's world. Matches weasel's default `RectPose`. */
280
281
  type MarkPose = WorldRect;
@@ -282,6 +283,7 @@ type MarkPose = WorldRect;
282
283
  type MarkScene = Scene<AnnotationData, 'marks', MarkPose>;
283
284
  /** A mark scene: one layer, because marks do not stack in tiers. */
284
285
  declare function createAnnotationScene(): MarkScene;
286
+ /** Inputs to `createAnnotationStore`. */
285
287
  interface AnnotationStoreOptions {
286
288
  /** Re-read on every call, so a target resizing or gaining a dependency takes
287
289
  * effect without rebuilding the store. */
@@ -297,6 +299,10 @@ interface AnnotationStoreOptions {
297
299
  config?: () => unknown;
298
300
  /** Notified after every finished export. */
299
301
  onCapture?: (result: CaptureResult) => void;
302
+ /** Whether a selection can stand in several targets at once. `'exclusive'`
303
+ * (the default) clears every other target when a mark is selected in one.
304
+ * A thunk is read at each selection change. */
305
+ selection?: AnnotationSelectionMode | (() => AnnotationSelectionMode | undefined);
300
306
  }
301
307
  /** Everything a capture needs that is not the store's own state. Split out so
302
308
  * `capture.ts` takes data rather than reaching back into the store. */
@@ -338,8 +344,8 @@ interface FracRect {
338
344
  h: number;
339
345
  }
340
346
  /** What shape a mark is. The kinds map onto weasel's own tools; an arrow is a
341
- * line carrying an end marker, not a separate geometry. A point holds one
342
- * entry in `points` and zero-size bounds at that position. */
347
+ * line carrying an end marker, not a separate geometry. A point is zero-size
348
+ * bounds at its position. */
343
349
  type AnnotationKind = 'stroke' | 'line' | 'arrow' | 'rect' | 'ellipse' | 'text' | 'point';
344
350
  /** One selectable status in an instrument's meaning tier. */
345
351
  interface AnnotationStatus {
@@ -360,8 +366,11 @@ interface AnnotationData {
360
366
  target: string;
361
367
  kind: AnnotationKind;
362
368
  /** Vertices for the kinds a bounding box cannot describe — a line's two
363
- * ends, a stroke's path. In fractions, like the bounds. */
364
- points?: readonly FracPoint[];
369
+ * ends, a stroke's path — in fractions of the mark's own bounds, so moving
370
+ * or resizing the bounds carries them along. A point stores none: it is its
371
+ * bounds' origin. `Annotation.points` is this restated in the target's
372
+ * content box. */
373
+ shape?: readonly FracPoint[];
365
374
  title?: string;
366
375
  status?: string;
367
376
  tags?: readonly string[];
@@ -373,16 +382,21 @@ interface AnnotationData {
373
382
  }
374
383
  /** A mark, as the store reports it. `id` is `<target>/<node>`: one scene per
375
384
  * target means a node id is only unique within one. */
376
- interface Annotation extends AnnotationData {
385
+ interface Annotation extends Omit<AnnotationData, 'shape'> {
377
386
  id: string;
378
387
  /** Bounds in fractions of the target's content box. */
379
388
  frac: FracRect;
389
+ /** Vertices in fractions of the target's content box, wherever the bounds
390
+ * now are. A point's one entry is its bounds' origin. */
391
+ points?: readonly FracPoint[];
380
392
  }
381
393
  /** A new mark, before the store gives it an id and dates it. */
382
394
  interface AnnotationInit {
383
395
  target: string;
384
396
  kind: AnnotationKind;
385
397
  frac: FracRect;
398
+ /** In the target's content box. Stored relative to `frac`, so they should
399
+ * lie inside it; a point takes its position from `frac` alone. */
386
400
  points?: readonly FracPoint[];
387
401
  title?: string;
388
402
  status?: string;
@@ -398,7 +412,8 @@ interface AnnotationQuery {
398
412
  where?: (a: Annotation) => boolean;
399
413
  }
400
414
  /** What a mark's meaning can be patched to. Geometry moves through `frac` /
401
- * `points`; everything else is the meaning tier. */
415
+ * `points`; everything else is the meaning tier. `frac` alone carries the
416
+ * vertices with it; `points` alone moves the bounds to enclose them. */
402
417
  type AnnotationPatch = Partial<Pick<Annotation, 'frac' | 'points' | 'title' | 'status' | 'tags' | 'meta'>>;
403
418
  /** A target's own picture, handed over for an export to draw marks on top of.
404
419
  * labkit cannot rasterize it: it is the consumer's DOM. `svg` is the one that
@@ -468,10 +483,14 @@ interface AnnotationStorage {
468
483
  load: () => Promise<SerializedAnnotations | null | undefined>;
469
484
  save: (doc: SerializedAnnotations) => void;
470
485
  }
471
- /** Declares that an instrument accepts marks: which regions take them,
472
- * optionally what a mark is allowed to mean, and optionally where they live. */
473
486
  /** The annotation tools a lab's rail can carry. */
474
487
  type AnnotationToolId = 'pointer' | 'select' | 'stroke' | 'line' | 'arrow' | 'rect' | 'ellipse' | 'text';
488
+ /** Whether a selection can stand in several of a trial's targets at once.
489
+ * `'exclusive'` clears every other target when a mark is selected in one;
490
+ * `'per-target'` lets each target keep its own. */
491
+ type AnnotationSelectionMode = 'exclusive' | 'per-target';
492
+ /** Declares that an instrument accepts marks: which regions take them,
493
+ * optionally what a mark is allowed to mean, and optionally where they live. */
475
494
  interface AnnotationsCapability<TS = unknown, TC = unknown> {
476
495
  /** `trial` is which trial is asking: a declaration made once per instrument
477
496
  * is called once per trial, and its targets are that trial's own. */
@@ -488,6 +507,9 @@ interface AnnotationsCapability<TS = unknown, TC = unknown> {
488
507
  * notification, not an interception: a host wanting its own flow calls
489
508
  * `capture()` from its own UI, which is the surface the chrome uses. */
490
509
  onCapture?: (result: CaptureResult) => void;
510
+ /** Defaults to `'exclusive'`: selecting a mark in one target clears the
511
+ * selection in every other. */
512
+ selection?: AnnotationSelectionMode;
491
513
  }
492
514
  /** A persisted mark set. Versioned by this arc rather than by labkit's
493
515
  * document migrations, which only ever reach top-level document sections and
@@ -526,11 +548,13 @@ interface AnnotationsApi {
526
548
  * annotation ids. Every one resolves through `get`. */
527
549
  selection(): readonly string[];
528
550
  /** Replace the selection. An id naming a target or a mark that is not
529
- * there is dropped, the way `update` and `remove` ignore one. */
551
+ * there is dropped, the way `update` and `remove` ignore one. Under an
552
+ * `'exclusive'` selection, so is every id outside the first id's target. */
530
553
  setSelection(ids: readonly string[]): void;
531
554
  /** Fires after every mutation *and* after a selection change — weasel keeps
532
555
  * a canvas's selection on the scene, so both already arrive on this one
533
- * channel. No delta: re-query, and re-read `selection()`. */
556
+ * channel. A selection change touching several targets is one event. No
557
+ * delta: re-query, and re-read `selection()`. */
534
558
  subscribe(fn: () => void): () => void;
535
559
  /** Whether the last mark change on any target can be taken back. Weasel
536
560
  * history is the authority; this only decides *which* target's. */
@@ -559,7 +583,7 @@ interface AnnotationsApi {
559
583
  * world (0,0) at the element's top-left, y growing downward. */
560
584
  interface WorldSpec {
561
585
  /** Where world (0,0) sits at `pan` zero, as a fraction of the viewport.
562
- * `{x:0,y:0}` top-left (default); `{x:0.5,y:0.5}` centre. */
586
+ * `{x:0,y:0}` top-left (default); `{x:0.5,y:0.5}` center. */
563
587
  origin?: Point;
564
588
  /** Which way the world y axis runs on screen. Default `'down'`. */
565
589
  yAxis?: 'down' | 'up';
@@ -576,7 +600,9 @@ interface WorldFrame {
576
600
  originPx: Point;
577
601
  yDir: 1 | -1;
578
602
  }
603
+ /** The frame an absent `WorldSpec` resolves to: origin top-left, y down. */
579
604
  declare const DEFAULT_FRAME: WorldFrame;
605
+ /** Resolve `spec` against a viewport of `size`; `undefined` gives `DEFAULT_FRAME`. */
580
606
  declare function resolveFrame(spec: WorldSpec | undefined, size: ViewportSize): WorldFrame;
581
607
  /** Put a 2D context into the instrument's world coordinates, so a layer's
582
608
  * `draw` works in them. Must stay the exact inverse of `screenToWorld`. */
@@ -744,8 +770,10 @@ interface TrialChromeContext extends ToolSlotContext, SidebarSlotContext {
744
770
  canRedo: boolean;
745
771
  undo: () => void;
746
772
  redo: () => void;
747
- /** Whether the trial's loupe is turned on. False for an instrument that
748
- * declares none, whose chrome offers no way to turn one on. */
773
+ /** Whether a `<TrialLoupe>` following the trial's toggle is mounted in it.
774
+ * The loupe toggle is offered only while one is. */
775
+ hasLoupe: boolean;
776
+ /** Whether the trial's loupe is turned on. */
749
777
  loupeOn: boolean;
750
778
  toggleLoupe: () => void;
751
779
  /** The instrument's controls, resolved against the lab's rules. Always
@@ -780,83 +808,6 @@ interface TrialChromeContext extends ToolSlotContext, SidebarSlotContext {
780
808
  close: () => void;
781
809
  }
782
810
 
783
- /** A point on the magnified surface, in its own CSS pixels. */
784
- interface LoupePoint {
785
- x: number;
786
- y: number;
787
- }
788
-
789
- /** How a loupe magnifies. `'vector'` re-renders the source through a zoomed-in
790
- * view, so content stays sharp at any factor; `'pixel'` blows up the actual
791
- * pixels the surface presented. */
792
- type LoupeMode = 'vector' | 'pixel';
793
-
794
- /** What a DOM instrument's loupe `render` is handed: the same data its own
795
- * `render` gets, and the camera to draw it through. */
796
- interface LoupeRenderArgs<TS = unknown, TC = unknown> {
797
- state: TS;
798
- config: TC;
799
- /** The trial's own camera composed with the magnification, about the aimed
800
- * point — so drawing the same content through it magnifies in place. */
801
- view: ViewTransform;
802
- /** The magnification on its own, for whatever must not scale with the
803
- * camera. */
804
- factor: number;
805
- mode: LoupeMode;
806
- /** The size of the viewport `view` is written for: the trial's content well,
807
- * not the lens. The lens shows a circle cut out of it. */
808
- size: ViewportSize;
809
- }
810
- /**
811
- * Declares that an instrument can be magnified.
812
- *
813
- * With no `render`, the loupe re-draws the instrument's canvas layers through a
814
- * zoomed camera, so it stays sharp at any factor. An instrument whose content is
815
- * DOM supplies `render` instead: given a camera, draw me again.
816
- */
817
- interface LoupeCapability<TS = unknown, TC = unknown> {
818
- render?: (args: LoupeRenderArgs<TS, TC>) => ReactNode;
819
- /** Opening magnification, clamped to the bounds below. Default 6. */
820
- factor?: number;
821
- /** What the wheel clamps to. Defaults 2 and 32. */
822
- minFactor?: number;
823
- maxFactor?: number;
824
- /** `'vector'` (default) re-renders the content magnified; `'pixel'` blows up
825
- * the pixels the instrument presented. A `render` loupe is always vector —
826
- * DOM has no framebuffer to enlarge. */
827
- mode?: LoupeMode;
828
- /** Lens diameter in CSS px. Default 200. */
829
- diameter?: number;
830
- /** Held for a momentary peek while the loupe is off. Default `'Alt'`; `null`
831
- * turns hold-to-peek off. Matched against `KeyboardEvent.key`. */
832
- peekKey?: string | null;
833
- /** Called with the color under the aim, wherever the surface can say. The
834
- * canvas painter reads it back; a DOM loupe has no pixels to sample. */
835
- onColorChange?: (hex: string) => void;
836
- }
837
- /** An instrument's loupe declaration. `true` takes every default. */
838
- type LoupeDeclaration<TS = unknown, TC = unknown> = true | LoupeCapability<TS, TC>;
839
- /** A {@link LoupeCapability} with every default filled in. */
840
- interface ResolvedLoupe<TS = unknown, TC = unknown> {
841
- render?: (args: LoupeRenderArgs<TS, TC>) => ReactNode;
842
- onColorChange?: (hex: string) => void;
843
- factor: number;
844
- minFactor: number;
845
- maxFactor: number;
846
- mode: LoupeMode;
847
- diameter: number;
848
- peekKey: string | null;
849
- }
850
- declare const LOUPE_DEFAULTS: {
851
- readonly factor: 6;
852
- readonly minFactor: 2;
853
- readonly maxFactor: 32;
854
- readonly mode: "vector";
855
- readonly diameter: 200;
856
- readonly peekKey: "Alt";
857
- };
858
- declare function resolveLoupe<TS, TC>(declared: LoupeDeclaration<TS, TC>): ResolvedLoupe<TS, TC>;
859
-
860
811
  /**
861
812
  * A tool a trial can be in. labkit's own — core's `ToolsApi` carries hotkey
862
813
  * slots, ambient tools, eligibility tiers and canvas overlay layers, all bound
@@ -1070,11 +1021,6 @@ interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
1070
1021
  * is allowed to mean. Declaring it is what makes the trial provide the
1071
1022
  * annotation overlay and its chrome. */
1072
1023
  annotations?: AnnotationsCapability<TS, TC>;
1073
- /** Magnification. `true` takes every default and re-draws the instrument's
1074
- * canvas layers through a zoomed camera; an instrument whose content is DOM
1075
- * gives a `render` that draws it again at a camera it is handed. A function
1076
- * is re-read as the config changes, so a setting can drive the lens. */
1077
- loupe?: LoupeDeclaration<TS, TC> | ((config: TC) => LoupeDeclaration<TS, TC>);
1078
1024
  /** Chrome this instrument contributes beyond what its capabilities imply. */
1079
1025
  chrome?: TrialContribution[];
1080
1026
  /** Work too slow to do during a render. The runtime starts it, aborts it on
@@ -1086,5 +1032,5 @@ interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
1086
1032
  * an `Instrument<unknown, unknown>[]`; it is contained to this alias. */
1087
1033
  type InstrumentList = readonly Instrument<any, any, any>[];
1088
1034
 
1089
- export { DEFAULT_FRAME as D, annotationsFromJSON as aB, auto as aC, createAnnotationScene as aD, createAnnotationStore as aE, fracContains as aF, fracEncloses as aG, fracToWorld as aH, isAuto as aI, markCommands as aJ, resolveLoupe as aK, roundFrac as aL, worldToFrac as aM, LOUPE_DEFAULTS as ae, dockPanel as k, applyCamera as m, panelKey as p, resolveFrame as r, undockPanel as u };
1090
- export type { AnnotationPatch as $, AnnotationMeaning as A, CaptureSource as B, CreateLabStoreOptions as C, CaptureOptions as E, CaptureResult as F, PaintableMark as G, AnnotationTarget as H, InstrumentSerializers as I, AnnotationsApi as J, AnnotationsCapability as K, LayerCapability as L, Migration as M, TrialInfo as N, TrialTool as O, Point as P, AnnotationKind as Q, Instrument as R, SerializedTrial as S, TrialRecord as T, UndoStack as U, ViewportSize as V, WorldFrame as W, LabDensity as X, TrialContribution as Y, Annotation as Z, AnnotationInit as _, LayerDescriptor as a, AnnotationQuery as a0, AnnotationStatus as a1, AnnotationStoreOptions as a2, AnnotationTargetInfo as a3, AnnotationToolId as a4, Auto as a5, CanvasCapability as a6, CanvasLayer as a7, CaptureDeps as a8, ContributionBase as a9, ViewportControl as aA, FracPoint as aa, FracRect as ab, HitResult as ac, IconComponent as ad, LoupeCapability as af, LoupeDeclaration as ag, LoupeMode as ah, LoupePoint as ai, LoupeRenderArgs as aj, RegionContribution as ak, RenderContext as al, ResolvedLoupe as am, SerializedAnnotations as an, SidebarSection as ao, SidebarSlotContext as ap, StageCapability as aq, StatusReadout as ar, SystemEvent as as, ToolCapability as at, ToolItem as au, ToolSlotContext as av, ToolbarItem as aw, TrialChromeContext as ax, TrialRegion as ay, UndoCapability as az, ViewTransform as b, StorageAdapter as c, LabDocument as d, LabStoreState as e, SavedSnapshot as f, StorageChange as g, TrialStateHandle as h, UndockedPanel as i, UndockedPanels as j, WorldSpec as l, PaletteItem as n, DragFeedback as o, DragDropCapability as q, LabMode as s, InstrumentList as t, InstrumentHooks as v, AnnotationData as w, WorldRect as x, MarkStyle as y, MarkScene as z };
1035
+ export { DEFAULT_FRAME as D, fracEncloses as aA, fracToWorld as aB, isAuto as aC, markCommands as aD, roundFrac as aE, worldToFrac as aF, annotationsFromJSON as av, auto as aw, createAnnotationScene as ax, createAnnotationStore as ay, fracContains as az, dockPanel as j, applyCamera as k, panelKey as p, resolveFrame as r, undockPanel as u };
1036
+ export type { AnnotationSelectionMode as $, AnnotationMeaning as A, CaptureResult as B, CreateLabStoreOptions as C, PaintableMark as E, AnnotationTarget as F, AnnotationsApi as G, AnnotationsCapability as H, InstrumentSerializers as I, TrialInfo as J, TrialTool as K, LabDocument as L, Migration as M, AnnotationKind as N, Instrument as O, Point as P, LabDensity as Q, TrialContribution as R, SerializedTrial as S, TrialRecord as T, UndoStack as U, ViewportSize as V, WorldFrame as W, Annotation as X, AnnotationInit as Y, AnnotationPatch as Z, AnnotationQuery as _, ViewTransform as a, AnnotationStatus as a0, AnnotationStoreOptions as a1, AnnotationTargetInfo as a2, AnnotationToolId as a3, Auto as a4, CanvasCapability as a5, CanvasLayer as a6, CaptureDeps as a7, ContributionBase as a8, FracPoint as a9, FracRect as aa, HitResult as ab, IconComponent as ac, LayerCapability as ad, LayerDescriptor as ae, RegionContribution as af, RenderContext as ag, SerializedAnnotations as ah, SidebarSection as ai, SidebarSlotContext as aj, StageCapability as ak, StatusReadout as al, SystemEvent as am, ToolCapability as an, ToolItem as ao, ToolSlotContext as ap, ToolbarItem as aq, TrialChromeContext as ar, TrialRegion as as, UndoCapability as at, ViewportControl as au, WorldSpec as b, StorageAdapter as c, LabStoreState as d, SavedSnapshot as e, StorageChange as f, TrialStateHandle as g, UndockedPanel as h, UndockedPanels as i, PaletteItem as l, DragFeedback as m, DragDropCapability as n, LabMode as o, InstrumentList as q, InstrumentHooks as s, AnnotationData as t, WorldRect as v, MarkStyle as w, MarkScene as x, CaptureSource as y, CaptureOptions as z };
@@ -1,6 +1,6 @@
1
+ import { Display } from '@weasel-js/quantity';
2
+ import { PrefNumberUnit, PrefRenderer, PrefLeaf, PrefGroup } from '@weasel-js/ui';
1
3
  import { ReactNode } from 'react';
2
- import { ToolPrefNumberFormat, ToolPrefNumberUnit, ToolPrefLeaf, ToolPrefGroup } from '@weasel-js/core';
3
- import { P as PrefRenderer } from './PrefsRow.d-DFpPIeIC.js';
4
4
 
5
5
  /**
6
6
  * Renders the control cell for one config leaf. Identical to weasel-ui's
@@ -12,6 +12,10 @@ type ControlRenderer = PrefRenderer;
12
12
  interface ConfigOption {
13
13
  value: string;
14
14
  label: string;
15
+ /** A glyph in weasel-ui's icon set (`IconName`). A segmented row draws it in
16
+ * place of `label`, which stays the accessible name and becomes the tooltip;
17
+ * a select draws it beside `label`. */
18
+ icon?: string;
15
19
  }
16
20
  /** Everything a leaf can carry beyond its kind and default. A flat union of
17
21
  * every `Pref*` leaf's extras, plus labkit's own `debounceMs`. */
@@ -25,11 +29,11 @@ interface Annotations {
25
29
  max?: number;
26
30
  step?: number;
27
31
  suffix?: string;
28
- /** How a number's value is shown. `compact` abbreviates from a thousand up. */
29
- format?: ToolPrefNumberFormat;
32
+ /** How a number's value shows, speaks and reads back — `compact()` reads `2.00M`. */
33
+ display?: Display;
30
34
  /** Display-unit conversion for a number stored in a canonical unit — the
31
35
  * value, its bounds and its step all convert at the control's edge. */
32
- unit?: ToolPrefNumberUnit;
36
+ unit?: PrefNumberUnit;
33
37
  /** A color leaf's value carries alpha (`#rrggbbaa`) and its row edits it. */
34
38
  alpha?: boolean;
35
39
  control?: string;
@@ -90,13 +94,16 @@ interface SectionSpec {
90
94
  interface ResolvedConfig {
91
95
  /** The schema as a `PrefGroup` tree: an `f.group` is a nested group, so a
92
96
  * leaf's dotted path within it is the path its value is written at. */
93
- group: ToolPrefGroup;
97
+ group: PrefGroup;
94
98
  sections: readonly SectionSpec[];
95
99
  showIf: ReadonlyMap<string, (config: Record<string, unknown>) => boolean>;
96
100
  /** Node-level `.render` overrides, keyed by path. */
97
101
  renderers: Readonly<Record<string, ControlRenderer>>;
98
102
  /** Node-level `.dialog` rows, keyed by path. */
99
103
  dialogs: Readonly<Record<string, DialogSpec>>;
104
+ /** Paths of the groups marked `.resettable()`, whose heading carries a
105
+ * button that puts every value beneath it back to its default. */
106
+ resettable?: ReadonlySet<string>;
100
107
  }
101
108
  /** Options for a row drawn as a button that opens a dialog. */
102
109
  interface InDialogOptions {
@@ -129,7 +136,7 @@ interface NodeOptions extends BranchOptions {
129
136
  * config entry records it, and `ControlPanel` draws it. */
130
137
  dialog?: DialogSpec;
131
138
  /** Errors for this leaf under `config`, the instrument's whole config. */
132
- validate?: (leaf: ToolPrefLeaf, config: Record<string, unknown>) => string[];
139
+ validate?: (leaf: PrefLeaf, config: Record<string, unknown>) => string[];
133
140
  /** Computes this leaf's value while it is auto. Attaching one is what gives
134
141
  * the row a real value to pin back to and keeps the instrument off
135
142
  * `?? compute()`. */
@@ -147,6 +154,9 @@ interface BranchOptions {
147
154
  /** Show this node only while the predicate holds. On a group it hides the
148
155
  * whole subtree; the values stay in config either way. */
149
156
  showIf?: (config: Record<string, unknown>) => boolean;
157
+ /** Put a button on this group's heading that returns every value beneath it
158
+ * to its default. Groups only. */
159
+ resettable?: boolean;
150
160
  }
151
161
  /** A group's own annotations: what it is called and, optionally, why. */
152
162
  interface BranchAnnotations {
@@ -269,4 +279,4 @@ interface ColorField extends ConfigFieldBase {
269
279
  * panel renders, and what `validateConfigSchema` checks. */
270
280
  type ConfigField = SliderField | CheckboxField | SelectField | NumberField | TextField | ColorField;
271
281
 
272
- export type { Annotations as A, BranchAnnotations as B, ConfigPath as C, DialogSpec as D, EntryValue as E, InDialogOptions as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionOption as S, TextField as T, ValueAtPath as V, ConfigField as a, ConfigSchema as b, ConfigRule as c, ControlRenderer as d, BranchOptions as e, CheckboxField as f, ColorField as g, ConfigBranch as h, ConfigEntry as i, ConfigFieldBase as j, ConfigFieldType as k, ConfigNode as l, ConfigOf as m, ConfigOption as n, ConfigRuleContext as o, ConfigShape as p, InferConfig as q, NodeValue as r, NumberField as s, SectionSpec as t, SelectField as u, SelectOption as v, SliderField as w };
282
+ export type { Annotations as A, BranchAnnotations as B, ConfigSchema as C, DialogSpec as D, EntryValue as E, InDialogOptions as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionOption as S, TextField as T, ValueAtPath as V, ConfigField as a, ConfigRule as b, ControlRenderer as c, BranchOptions as d, CheckboxField as e, ColorField as f, ConfigBranch as g, ConfigEntry as h, ConfigFieldBase as i, ConfigFieldType as j, ConfigNode as k, ConfigOf as l, ConfigOption as m, ConfigPath as n, ConfigRuleContext as o, ConfigShape as p, InferConfig as q, NodeValue as r, NumberField as s, SectionSpec as t, SelectField as u, SelectOption as v, SliderField as w };
@@ -1,4 +1,4 @@
1
- import { c as StorageAdapter, e as LabStoreState, T as TrialRecord, f as SavedSnapshot, s as LabMode, i as UndockedPanel, t as InstrumentList, v as InstrumentHooks, C as CreateLabStoreOptions, h as TrialStateHandle } from './types-B7yBTTF9.js';
1
+ import { c as StorageAdapter, d as LabStoreState, T as TrialRecord, e as SavedSnapshot, o as LabMode, h as UndockedPanel, q as InstrumentList, s as InstrumentHooks, C as CreateLabStoreOptions, g as TrialStateHandle } from './index-DCMA4Fy1.js';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, ReactElement, Dispatch, SetStateAction } from 'react';
4
4
  import { StoreApi } from 'zustand/vanilla';