@weasel-js/labkit 1.6.0 → 1.6.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 (162) hide show
  1. package/dist/_dts/{CanvasStackContext-DQgcH1ny.d.ts → CanvasStackContext-Wo6OwIVM.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-BoELAMsM.d.ts} +2 -2
  4. package/dist/_dts/{index-DINmCuLA.d.ts → PropertyField.d-D0ndfIIt.d.ts} +34 -83
  5. package/dist/_dts/{Select.d-BchGMC2Y.d.ts → Select.d-Cr8-zoup.d.ts} +4 -5
  6. package/dist/_dts/{usePanZoom-Bq73pwFe.d.ts → Stage-DWBBi8IU.d.ts} +80 -35
  7. package/dist/_dts/{index-B0--8PEM.d.ts → index-CKyX0ME_.d.ts} +12 -13
  8. package/dist/_dts/{frac-DTRq7Xpb.d.ts → types-B7yBTTF9.d.ts} +699 -649
  9. package/dist/_dts/{types-B7y8hX1D.d.ts → types-DTuViBvc.d.ts} +20 -1
  10. package/dist/_dts/{useTrialState-CP4jfuKl.d.ts → useTrialState-CMaV6UD4.d.ts} +2 -3
  11. package/dist/canvas/index.d.ts +39 -7
  12. package/dist/canvas/index.js +5 -3
  13. package/dist/chrome/index.d.ts +4 -5
  14. package/dist/chrome/index.js +8 -6
  15. package/dist/chunk-24W5VEJW.js +18 -0
  16. package/dist/chunk-24W5VEJW.js.map +1 -0
  17. package/dist/chunk-3RY6WL5T.js +38 -0
  18. package/dist/chunk-3RY6WL5T.js.map +1 -0
  19. package/dist/{chunk-H5VIV7MQ.js → chunk-4TT3PNPR.js} +60 -51
  20. package/dist/chunk-4TT3PNPR.js.map +1 -0
  21. package/dist/{chunk-ZORVNOKX.js → chunk-6FLXL3I7.js} +9 -145
  22. package/dist/chunk-6FLXL3I7.js.map +1 -0
  23. package/dist/{chunk-HFRE4QQV.js → chunk-ASOK76ZH.js} +53 -22
  24. package/dist/chunk-ASOK76ZH.js.map +1 -0
  25. package/dist/chunk-AWQG7FCC.js +19 -0
  26. package/dist/chunk-AWQG7FCC.js.map +1 -0
  27. package/dist/chunk-CGHF6RZV.js +300 -0
  28. package/dist/chunk-CGHF6RZV.js.map +1 -0
  29. package/dist/chunk-DWMSB4CA.js +228 -0
  30. package/dist/chunk-DWMSB4CA.js.map +1 -0
  31. package/dist/{chunk-USO6TERP.js → chunk-GHGO52SO.js} +11 -9
  32. package/dist/chunk-GHGO52SO.js.map +1 -0
  33. package/dist/{chunk-MOLIUNVH.js → chunk-HKV7LRHY.js} +4 -4
  34. package/dist/{chunk-MOLIUNVH.js.map → chunk-HKV7LRHY.js.map} +1 -1
  35. package/dist/{chunk-VQ47KWD5.js → chunk-IRTGUQ5O.js} +5 -4
  36. package/dist/chunk-IRTGUQ5O.js.map +1 -0
  37. package/dist/{chunk-H6ZAOWNE.js → chunk-JVBYV6TL.js} +3 -3
  38. package/dist/{chunk-H6ZAOWNE.js.map → chunk-JVBYV6TL.js.map} +1 -1
  39. package/dist/chunk-KHK5FGER.js +145 -0
  40. package/dist/chunk-KHK5FGER.js.map +1 -0
  41. package/dist/chunk-NEXR37YJ.js +144 -0
  42. package/dist/chunk-NEXR37YJ.js.map +1 -0
  43. package/dist/{chunk-BVL7Y574.js → chunk-O6FFFOHS.js} +14 -6
  44. package/dist/chunk-O6FFFOHS.js.map +1 -0
  45. package/dist/chunk-QGMOA2YE.js +731 -0
  46. package/dist/chunk-QGMOA2YE.js.map +1 -0
  47. package/dist/{chunk-ERFMC6WU.js → chunk-S4NCI32V.js} +19 -10
  48. package/dist/chunk-S4NCI32V.js.map +1 -0
  49. package/dist/{chunk-3FI7Y4R4.js → chunk-SLAQPUI4.js} +4 -4
  50. package/dist/{chunk-3FI7Y4R4.js.map → chunk-SLAQPUI4.js.map} +1 -1
  51. package/dist/{chunk-AWH3SRPW.js → chunk-TJ77A4A3.js} +3 -31
  52. package/dist/chunk-TJ77A4A3.js.map +1 -0
  53. package/dist/{chunk-WUJXJC5O.js → chunk-TYN6MLXX.js} +691 -1395
  54. package/dist/chunk-TYN6MLXX.js.map +1 -0
  55. package/dist/{chunk-RS3HRQWU.js → chunk-WPLAANH4.js} +4 -3
  56. package/dist/chunk-WPLAANH4.js.map +1 -0
  57. package/dist/config/index.d.ts +36 -35
  58. package/dist/config/index.js +4 -5
  59. package/dist/controls/index.d.ts +91 -7
  60. package/dist/controls/index.js +5 -4
  61. package/dist/dragdrop/index.d.ts +8 -6
  62. package/dist/dragdrop/index.js +3 -2
  63. package/dist/index.d.ts +50 -77
  64. package/dist/index.js +138 -254
  65. package/dist/index.js.map +1 -1
  66. package/dist/layers/index.d.ts +3 -4
  67. package/dist/layers/index.js +2 -1
  68. package/dist/loupe/index.d.ts +19 -16
  69. package/dist/loupe/index.js +5 -3
  70. package/dist/overview/index.d.ts +56 -0
  71. package/dist/overview/index.js +288 -0
  72. package/dist/overview/index.js.map +1 -0
  73. package/dist/passthrough/weasel-ui.d.ts +38 -38
  74. package/dist/passthrough/weasel-ui.js +2 -1
  75. package/dist/primitives/index.d.ts +19 -43
  76. package/dist/primitives/index.js +5 -3
  77. package/dist/state/index.d.ts +7 -7
  78. package/dist/state/index.js +4 -5
  79. package/dist/state/index.js.map +1 -1
  80. package/dist/styles.css +63 -1
  81. package/dist/undo/index.d.ts +3 -3
  82. package/package.json +13 -9
  83. package/src/annotations/index.ts +1 -0
  84. package/src/annotations/store.test.ts +26 -0
  85. package/src/annotations/store.ts +25 -6
  86. package/src/annotations/toolMap.test.ts +37 -2
  87. package/src/annotations/toolMap.ts +13 -0
  88. package/src/annotations/types.ts +27 -0
  89. package/src/canvas/AGENTS.md +8 -10
  90. package/src/canvas/CameraInput.test.tsx +136 -0
  91. package/src/canvas/CameraInput.tsx +311 -0
  92. package/src/canvas/CanvasStack.tsx +42 -58
  93. package/src/canvas/LinkedCursor.less +16 -0
  94. package/src/canvas/LinkedCursor.tsx +54 -0
  95. package/src/canvas/Stage.tsx +42 -37
  96. package/src/canvas/cameraView.test.ts +60 -0
  97. package/src/canvas/cameraView.ts +61 -0
  98. package/src/canvas/index.ts +11 -2
  99. package/src/chrome/regions/SidebarRegion.test.tsx +9 -2
  100. package/src/config/builder.ts +3 -3
  101. package/src/config/entry.test.ts +3 -1
  102. package/src/config/fromConfigField.ts +1 -0
  103. package/src/config/index.ts +4 -1
  104. package/src/config/resolve.ts +11 -1
  105. package/src/config/sectionTree.test.ts +12 -6
  106. package/src/config/sectionTree.ts +15 -5
  107. package/src/config/types.ts +21 -0
  108. package/src/controls/ControlPanel.test.tsx +12 -2
  109. package/src/controls/ControlPanel.tsx +28 -9
  110. package/src/controls/inDialog.test.tsx +18 -0
  111. package/src/controls/inDialog.tsx +2 -10
  112. package/src/index.test.ts +1 -1
  113. package/src/index.ts +8 -4
  114. package/src/instrument/SineWave.smoke.test.tsx +2 -0
  115. package/src/instrument/types.ts +10 -1
  116. package/src/lab/Lab.nebula.test.tsx +11 -2
  117. package/src/lab/Lab.test.tsx +3 -1
  118. package/src/lab/Lab.tsx +4 -4
  119. package/src/loupe/AGENTS.md +7 -6
  120. package/src/loupe/LoupeGestures.tsx +14 -9
  121. package/src/loupe/TrialLoupe.tsx +5 -5
  122. package/src/loupe/index.ts +1 -0
  123. package/src/loupe/loupeActions.ts +6 -2
  124. package/src/overview/AGENTS.md +38 -0
  125. package/src/overview/OverviewMarks.test.tsx +100 -0
  126. package/src/overview/OverviewMarks.tsx +93 -0
  127. package/src/overview/TrialOverview.less +53 -0
  128. package/src/overview/TrialOverview.stories.tsx +50 -0
  129. package/src/overview/TrialOverview.test.tsx +116 -0
  130. package/src/overview/TrialOverview.tsx +374 -0
  131. package/src/overview/index.ts +2 -0
  132. package/src/passthrough/weasel-ui.ts +7 -0
  133. package/src/specimen/Specimen.tsx +19 -2
  134. package/src/state/labRecords.test.ts +4 -1
  135. package/src/state/store.test.ts +4 -4
  136. package/src/state/useTrialState.test.tsx +1 -1
  137. package/src/styles.less +2 -0
  138. package/src/theme/Interstellar.stories.tsx +1 -5
  139. package/src/tools/labTool.ts +6 -2
  140. package/src/trial/Trial.annotations.test.tsx +16 -0
  141. package/src/trial/Trial.loupe.test.tsx +73 -1
  142. package/src/trial/Trial.stories.tsx +2 -0
  143. package/src/trial/Trial.tsx +17 -1
  144. package/src/trial/trialOps.test.ts +4 -4
  145. package/dist/_dts/inDialog-D5ZKGjiD.d.ts +0 -23
  146. package/dist/_dts/number.d-OeQYQoUO.d.ts +0 -28
  147. package/dist/chunk-2AYGEN57.js +0 -32
  148. package/dist/chunk-2AYGEN57.js.map +0 -1
  149. package/dist/chunk-AWH3SRPW.js.map +0 -1
  150. package/dist/chunk-BVL7Y574.js.map +0 -1
  151. package/dist/chunk-ERFMC6WU.js.map +0 -1
  152. package/dist/chunk-H5VIV7MQ.js.map +0 -1
  153. package/dist/chunk-HFRE4QQV.js.map +0 -1
  154. package/dist/chunk-RS3HRQWU.js.map +0 -1
  155. package/dist/chunk-TT5C5S6R.js +0 -368
  156. package/dist/chunk-TT5C5S6R.js.map +0 -1
  157. package/dist/chunk-USO6TERP.js.map +0 -1
  158. package/dist/chunk-VQ47KWD5.js.map +0 -1
  159. package/dist/chunk-WUJXJC5O.js.map +0 -1
  160. package/dist/chunk-ZORVNOKX.js.map +0 -1
  161. package/src/canvas/usePanZoom.test.ts +0 -244
  162. package/src/canvas/usePanZoom.ts +0 -147
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  import { ToolPrefNumberFormat, ToolPrefNumberUnit, ToolPrefLeaf, ToolPrefGroup } from '@weasel-js/core';
2
3
  import { P as PrefRenderer } from './PrefsRow.d-DFpPIeIC.js';
3
4
 
@@ -94,6 +95,21 @@ interface ResolvedConfig {
94
95
  showIf: ReadonlyMap<string, (config: Record<string, unknown>) => boolean>;
95
96
  /** Node-level `.render` overrides, keyed by path. */
96
97
  renderers: Readonly<Record<string, ControlRenderer>>;
98
+ /** Node-level `.dialog` rows, keyed by path. */
99
+ dialogs: Readonly<Record<string, DialogSpec>>;
100
+ }
101
+ /** Options for a row drawn as a button that opens a dialog. */
102
+ interface InDialogOptions {
103
+ /** What the row's button shows of the value. Defaults to a list joined with
104
+ * commas, or the value itself. */
105
+ summary?: (value: unknown) => ReactNode;
106
+ /** The dialog's heading. Defaults to the row's label. */
107
+ title?: ReactNode;
108
+ }
109
+ /** A `.dialog` row: `body` is the dialog's content, handed the context a row
110
+ * renderer gets. */
111
+ interface DialogSpec extends InDialogOptions {
112
+ body: ControlRenderer;
97
113
  }
98
114
  /** How a node names the section it belongs to: the heading, and how that
99
115
  * section opens. Every node in one section repeats the heading; only one of
@@ -109,6 +125,9 @@ interface SectionOption {
109
125
  /** Per-node extras that do not belong on a `PrefLeaf`. */
110
126
  interface NodeOptions extends BranchOptions {
111
127
  render?: ControlRenderer;
128
+ /** Draw the row as a button that opens a dialog. Set by `.dialog`; the
129
+ * config entry records it, and `ControlPanel` draws it. */
130
+ dialog?: DialogSpec;
112
131
  /** Errors for this leaf under `config`, the instrument's whole config. */
113
132
  validate?: (leaf: ToolPrefLeaf, config: Record<string, unknown>) => string[];
114
133
  /** Computes this leaf's value while it is auto. Attaching one is what gives
@@ -250,4 +269,4 @@ interface ColorField extends ConfigFieldBase {
250
269
  * panel renders, and what `validateConfigSchema` checks. */
251
270
  type ConfigField = SliderField | CheckboxField | SelectField | NumberField | TextField | ColorField;
252
271
 
253
- export type { Annotations as A, BranchAnnotations as B, ControlRenderer as C, EntryValue as E, InferConfig as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionSpec as S, TextField as T, ValueAtPath as V, ConfigPath as a, ConfigField as b, ConfigSchema as c, ConfigRule 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, NodeValue as q, NumberField as r, SelectField as s, SelectOption as t, SliderField as u, SectionOption as v };
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 };
@@ -1,8 +1,7 @@
1
- import { d as StorageAdapter, f as LabStoreState, T as TrialRecord, g as SavedSnapshot, s as LabMode, j as UndockedPanel, t as InstrumentList, v as InstrumentHooks, C as CreateLabStoreOptions, i as TrialStateHandle } from './frac-DTRq7Xpb.js';
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';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, ReactElement, Dispatch, SetStateAction } from 'react';
4
4
  import { StoreApi } from 'zustand/vanilla';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
6
5
 
7
6
  /** Persist to `localStorage`, one JSON value per record. Survives a reload and
8
7
  * reaches other tabs through the `storage` event. */
@@ -182,7 +181,7 @@ interface PersistenceProps {
182
181
  }
183
182
  /** Persisted values for a page with no lab: `usePersistedState` below it keeps
184
183
  * what it holds across a reload. Both props are read once, at mount. */
185
- declare function Persistence({ storageKey, storage, fallback, children }: PersistenceProps): react_jsx_runtime.JSX.Element;
184
+ declare function Persistence({ storageKey, storage, fallback, children }: PersistenceProps): react.JSX.Element;
186
185
 
187
186
  /** Options for `usePersistedState`. */
188
187
  interface PersistedStateOptions {
@@ -1,12 +1,13 @@
1
+ export { C as CameraContext, a as CameraContextValue, b as CameraInput, c as CameraInputProps, d as CameraScope, e as CameraScopeContext, f as CameraViewOptions, g as CanvasStack, h as CanvasStackProps, S as STAGE_VIEW_ID, i as Stage, j as StageProps, Z as ZoomAtOptions, k as centerOn, l as fitStage, s as screenToWorld, u as useCameraView, w as worldToScreen, z as zoomAt } from '../_dts/Stage-DWBBi8IU.js';
1
2
  import * as react from 'react';
2
- export { C as CanvasStack, a as CanvasStackProps, P as PanZoomHandlers, S as Stage, b as StageProps, U as UsePanZoomOptions, Z as ZoomAtOptions, c as centerOn, f as fitStage, s as screenToWorld, u as usePanZoom, w as worldToScreen, z as zoomAt } from '../_dts/usePanZoom-Bq73pwFe.js';
3
- export { C as CanvasLayerDescriptor, a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from '../_dts/CanvasStackContext-DQgcH1ny.js';
4
- export { D as DEFAULT_FRAME, V as ViewportSize, W as WorldFrame, b as WorldSpec, c as applyCamera, r as resolveFrame } from '../_dts/frac-DTRq7Xpb.js';
5
- import 'react/jsx-runtime';
3
+ export { C as CanvasLayerDescriptor, a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from '../_dts/CanvasStackContext-Wo6OwIVM.js';
4
+ import { View } from '@weasel-js/core';
5
+ import { W as WorldFrame } from '../_dts/types-B7yBTTF9.js';
6
+ import { Point, ViewTransform } from '../index.js';
7
+ export { D as DEFAULT_FRAME, V as ViewportSize, l as WorldSpec, m as applyCamera, r as resolveFrame } from '../_dts/types-B7yBTTF9.js';
6
8
  import '../_dts/PrefsRow.d-DFpPIeIC.js';
7
9
  import '@weasel-js/theme';
8
- import '@weasel-js/core';
9
- import '../_dts/types-B7y8hX1D.js';
10
+ import '../_dts/types-DTuViBvc.js';
10
11
  import '../_dts/types-DJ79Tg5J.js';
11
12
 
12
13
  /** Where a trial's camera takes a wheel event from outside its own element. */
@@ -21,5 +22,36 @@ type CameraWheelSlot = {
21
22
  */
22
23
  declare const CameraWheelContext: react.Context<CameraWheelSlot | null>;
23
24
 
24
- export { CameraWheelContext };
25
+ /**
26
+ * labkit's camera as a weasel `View`, so core's viewport actions drive it.
27
+ *
28
+ * labkit places a world point on screen at
29
+ * `originPx + pan + zoom * (x, yDir * y)`. A weasel `View` places one at
30
+ * `(p - (x, y)) * scale`. Over *frame-local* coordinates — the world with `y`
31
+ * multiplied by `yDir` — the two are the same affine map, which is all a pan
32
+ * or a zoom about a screen point needs.
33
+ */
34
+
35
+ /** The weasel `View` equal to `view` under `frame`. */
36
+ declare function toCameraView(view: ViewTransform, frame?: WorldFrame): View;
37
+ /** The labkit camera equal to weasel `view` under `frame`. */
38
+ declare function fromCameraView(view: View, frame?: WorldFrame): ViewTransform;
39
+ /** A frame-local point in the instrument's world, and back — the map is its
40
+ * own inverse. */
41
+ declare function frameLocalToWorld(p: Point, frame?: WorldFrame): Point;
42
+ /**
43
+ * `next` with its zoom held to [`min`, `max`], about the screen point the step
44
+ * from `prev` was anchored on, so a zoom that hits a bound stays anchored where
45
+ * the pointer is. A pan (equal scales) passes through.
46
+ */
47
+ declare function clampZoomAbout(prev: View, next: View, min: number, max: number): View;
48
+
49
+ /**
50
+ * The other side of a linked cursor: a crosshair on the camera's own element
51
+ * wherever the shared pointer is over some other view — an overview, say. The
52
+ * same crosshair core paints on a weasel canvas.
53
+ */
54
+ declare function LinkedCursor(): react.JSX.Element | null;
55
+
56
+ export { CameraWheelContext, LinkedCursor, WorldFrame, clampZoomAbout, frameLocalToWorld, fromCameraView, toCameraView };
25
57
  export type { CameraWheelSlot };
@@ -1,7 +1,9 @@
1
- export { CameraWheelContext, CanvasStack, Stage, fitStage, usePanZoom } from '../chunk-TT5C5S6R.js';
2
- export { centerOn, zoomAt } from '../chunk-H6ZAOWNE.js';
3
- export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from '../chunk-2AYGEN57.js';
1
+ export { CanvasStack, LinkedCursor, Stage, fitStage } from '../chunk-CGHF6RZV.js';
2
+ export { centerOn, zoomAt } from '../chunk-JVBYV6TL.js';
3
+ export { screenToWorld, worldToScreen } from '../chunk-AWQG7FCC.js';
4
4
  import '../chunk-WIG6XHJ7.js';
5
+ export { CameraContext, CameraInput, CameraScope, CameraScopeContext, CameraWheelContext, STAGE_VIEW_ID, clampZoomAbout, frameLocalToWorld, fromCameraView, toCameraView, useCameraView } from '../chunk-DWMSB4CA.js';
6
+ export { DEFAULT_FRAME, applyCamera, resolveFrame } from '../chunk-24W5VEJW.js';
5
7
  export { CanvasStackContext } from '../chunk-CQLQPQ4P.js';
6
8
  import '../chunk-2YILQ7Y5.js';
7
9
  //# sourceMappingURL=index.js.map
@@ -1,9 +1,8 @@
1
- export { a as LabAsideRegion, b as LabChromeContext, L as LabContribution, e as LabFooterRegion, f as LabHeaderRegion, g as LabRegion, h as LabRegionProps, i as LabSidebarRegion, P as PaletteRegion, j as PaletteRegionProps, S as SidebarRegion, k as SidebarRegionProps, l as StatusRegion, m as StatusRegionProps, T as TitleBarPlacement, n as TitleBarRegion, o as TitleBarRegionProps, p as ToolbarRegion, q as ToolbarRegionProps, V as ViewportRegion, r as ViewportRegionProps, t as builtinContributions, w as contributionsIn, x as labContributions, y as mergeContributions, C as suppressContributions, D as toolContributions, E as useLabChromeContext } from '../_dts/index-B0--8PEM.js';
2
- export { a6 as ContributionBase, aa as IconComponent, af as RegionContribution, aj as SidebarSection, ak as SidebarSlotContext, am as StatusReadout, ap as ToolItem, aq as ToolSlotContext, ar as ToolbarItem, as as TrialChromeContext, R as TrialContribution, at as TrialRegion, av as ViewportControl } from '../_dts/frac-DTRq7Xpb.js';
3
- import '../_dts/types-B7y8hX1D.js';
1
+ export { a as LabAsideRegion, b as LabChromeContext, L as LabContribution, e as LabFooterRegion, f as LabHeaderRegion, g as LabRegion, h as LabRegionProps, i as LabSidebarRegion, P as PaletteRegion, j as PaletteRegionProps, S as SidebarRegion, k as SidebarRegionProps, l as StatusRegion, m as StatusRegionProps, T as TitleBarPlacement, n as TitleBarRegion, o as TitleBarRegionProps, p as ToolbarRegion, q as ToolbarRegionProps, V as ViewportRegion, r as ViewportRegionProps, t as builtinContributions, w as contributionsIn, x as labContributions, y as mergeContributions, C as suppressContributions, D as toolContributions, E as useLabChromeContext } from '../_dts/index-CKyX0ME_.js';
2
+ export { a9 as ContributionBase, ad as IconComponent, ak as RegionContribution, ao as SidebarSection, ap as SidebarSlotContext, ar as StatusReadout, au as ToolItem, av as ToolSlotContext, aw as ToolbarItem, ax as TrialChromeContext, Y as TrialContribution, ay as TrialRegion, aA as ViewportControl } from '../_dts/types-B7yBTTF9.js';
3
+ import '../_dts/types-DTuViBvc.js';
4
+ import 'react';
4
5
  import '@weasel-js/core';
5
6
  import '../_dts/PrefsRow.d-DFpPIeIC.js';
6
7
  import '@weasel-js/theme';
7
- import 'react';
8
- import 'react/jsx-runtime';
9
8
  import '../_dts/types-DJ79Tg5J.js';
@@ -1,13 +1,15 @@
1
- export { LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, suppressContributions, toolContributions, useLabChromeContext } from '../chunk-H5VIV7MQ.js';
2
- import '../chunk-3FI7Y4R4.js';
1
+ export { LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, suppressContributions, toolContributions, useLabChromeContext } from '../chunk-4TT3PNPR.js';
2
+ import '../chunk-3RY6WL5T.js';
3
+ import '../chunk-SLAQPUI4.js';
3
4
  import '../chunk-SBXNQW4G.js';
4
5
  import '../chunk-RWNDDKOH.js';
5
- import '../chunk-HFRE4QQV.js';
6
+ import '../chunk-ASOK76ZH.js';
6
7
  import '../chunk-Z76KEZAX.js';
7
- import '../chunk-VQ47KWD5.js';
8
- import '../chunk-AWH3SRPW.js';
8
+ import '../chunk-IRTGUQ5O.js';
9
+ import '../chunk-TJ77A4A3.js';
9
10
  import '../chunk-CQLQPQ4P.js';
10
11
  import '../chunk-2YILQ7Y5.js';
11
- import '../chunk-WUJXJC5O.js';
12
+ import '../chunk-TYN6MLXX.js';
13
+ import '../chunk-QGMOA2YE.js';
12
14
  //# sourceMappingURL=index.js.map
13
15
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ // src/canvas/worldSpec.ts
2
+ var DEFAULT_FRAME = { originPx: { x: 0, y: 0 }, yDir: 1 };
3
+ function resolveFrame(spec, size) {
4
+ if (!spec) return DEFAULT_FRAME;
5
+ const origin = spec.origin ?? { x: 0, y: 0 };
6
+ return {
7
+ originPx: { x: origin.x * size.width, y: origin.y * size.height },
8
+ yDir: spec.yAxis === "up" ? -1 : 1
9
+ };
10
+ }
11
+ function applyCamera(ctx, view, frame = DEFAULT_FRAME) {
12
+ ctx.translate(frame.originPx.x + view.pan.x, frame.originPx.y + view.pan.y);
13
+ ctx.scale(view.zoom, view.zoom * frame.yDir);
14
+ }
15
+
16
+ export { DEFAULT_FRAME, applyCamera, resolveFrame };
17
+ //# sourceMappingURL=chunk-24W5VEJW.js.map
18
+ //# sourceMappingURL=chunk-24W5VEJW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/canvas/worldSpec.ts"],"names":[],"mappings":";AA2BO,IAAM,aAAA,GAA4B,EAAE,QAAA,EAAU,EAAE,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAE,EAAG,IAAA,EAAM,CAAA;AAEpE,SAAS,YAAA,CAAa,MAA6B,IAAA,EAAgC;AACxF,EAAA,IAAI,CAAC,MAAM,OAAO,aAAA;AAClB,EAAA,MAAM,SAAS,IAAA,CAAK,MAAA,IAAU,EAAE,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAE;AAC3C,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,EAAE,CAAA,EAAG,MAAA,CAAO,CAAA,GAAI,IAAA,CAAK,KAAA,EAAO,CAAA,EAAG,MAAA,CAAO,CAAA,GAAI,IAAA,CAAK,MAAA,EAAO;AAAA,IAChE,IAAA,EAAM,IAAA,CAAK,KAAA,KAAU,IAAA,GAAO,EAAA,GAAK;AAAA,GACnC;AACF;AAIO,SAAS,WAAA,CACd,GAAA,EACA,IAAA,EACA,KAAA,GAAoB,aAAA,EACd;AACN,EAAA,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,QAAA,CAAS,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,CAAM,QAAA,CAAS,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AAC1E,EAAA,GAAA,CAAI,MAAM,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,IAAA,GAAO,MAAM,IAAI,CAAA;AAC7C","file":"chunk-24W5VEJW.js","sourcesContent":["import type { Point, ViewTransform } from '../instrument/types';\n\n/** How an instrument's world sits on the canvas, independent of the camera.\n * The defaults reproduce the convention labkit shipped before this existed:\n * world (0,0) at the element's top-left, y growing downward. */\nexport interface WorldSpec {\n /** Where world (0,0) sits at `pan` zero, as a fraction of the viewport.\n * `{x:0,y:0}` top-left (default); `{x:0.5,y:0.5}` centre. */\n origin?: Point;\n /** Which way the world y axis runs on screen. Default `'down'`. */\n yAxis?: 'down' | 'up';\n}\n\n/** A viewport's CSS-pixel size. */\nexport interface ViewportSize {\n width: number;\n height: number;\n}\n\n/** A `WorldSpec` resolved against a viewport: what the coordinate helpers,\n * the camera and the wheel all read. */\nexport interface WorldFrame {\n /** Screen position of world (0,0) at `pan` zero. */\n originPx: Point;\n yDir: 1 | -1;\n}\n\nexport const DEFAULT_FRAME: WorldFrame = { originPx: { x: 0, y: 0 }, yDir: 1 };\n\nexport function resolveFrame(spec: WorldSpec | undefined, size: ViewportSize): WorldFrame {\n if (!spec) return DEFAULT_FRAME;\n const origin = spec.origin ?? { x: 0, y: 0 };\n return {\n originPx: { x: origin.x * size.width, y: origin.y * size.height },\n yDir: spec.yAxis === 'up' ? -1 : 1,\n };\n}\n\n/** Put a 2D context into the instrument's world coordinates, so a layer's\n * `draw` works in them. Must stay the exact inverse of `screenToWorld`. */\nexport function applyCamera(\n ctx: CanvasRenderingContext2D,\n view: ViewTransform,\n frame: WorldFrame = DEFAULT_FRAME,\n): void {\n ctx.translate(frame.originPx.x + view.pan.x, frame.originPx.y + view.pan.y);\n ctx.scale(view.zoom, view.zoom * frame.yDir);\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { createContext, useContext, useRef, useSyncExternalStore, useCallback } from 'react';
2
+
3
+ // src/annotations/AnnotationsContext.ts
4
+ var AnnotationsContext = createContext(null);
5
+ function useMarkVersion(api) {
6
+ const version = useRef(0);
7
+ useSyncExternalStore(
8
+ useCallback(
9
+ (fn) => api?.subscribe(() => {
10
+ version.current += 1;
11
+ fn();
12
+ }) ?? (() => {
13
+ }),
14
+ [api]
15
+ ),
16
+ useCallback(() => version.current, []),
17
+ () => 0
18
+ );
19
+ }
20
+ function useAnnotations() {
21
+ const api = useContext(AnnotationsContext);
22
+ useMarkVersion(api);
23
+ if (!api) {
24
+ throw new Error(
25
+ "[labkit] useAnnotations requires an instrument that declares the `annotations` capability"
26
+ );
27
+ }
28
+ return api;
29
+ }
30
+ function useAnnotationsOptional() {
31
+ const api = useContext(AnnotationsContext);
32
+ useMarkVersion(api);
33
+ return api;
34
+ }
35
+
36
+ export { AnnotationsContext, useAnnotations, useAnnotationsOptional };
37
+ //# sourceMappingURL=chunk-3RY6WL5T.js.map
38
+ //# sourceMappingURL=chunk-3RY6WL5T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/annotations/AnnotationsContext.ts"],"names":[],"mappings":";;;AAIO,IAAM,kBAAA,GAAqB,cAAqC,IAAI;AAO3E,SAAS,eAAe,GAAA,EAAkC;AACxD,EAAA,MAAM,OAAA,GAAU,OAAO,CAAC,CAAA;AACxB,EAAA,oBAAA;AAAA,IACE,WAAA;AAAA,MACE,CAAC,EAAA,KACC,GAAA,EAAK,SAAA,CAAU,MAAM;AACnB,QAAA,OAAA,CAAQ,OAAA,IAAW,CAAA;AACnB,QAAA,EAAA,EAAG;AAAA,MACL,CAAC,MAAM,MAAM;AAAA,MAAC,CAAA,CAAA;AAAA,MAChB,CAAC,GAAG;AAAA,KACN;AAAA,IACA,WAAA,CAAY,MAAM,OAAA,CAAQ,OAAA,EAAS,EAAE,CAAA;AAAA,IACrC,MAAM;AAAA,GACR;AACF;AAMO,SAAS,cAAA,GAAiC;AAC/C,EAAA,MAAM,GAAA,GAAM,WAAW,kBAAkB,CAAA;AACzC,EAAA,cAAA,CAAe,GAAG,CAAA;AAClB,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAIO,SAAS,sBAAA,GAAgD;AAC9D,EAAA,MAAM,GAAA,GAAM,WAAW,kBAAkB,CAAA;AACzC,EAAA,cAAA,CAAe,GAAG,CAAA;AAClB,EAAA,OAAO,GAAA;AACT","file":"chunk-3RY6WL5T.js","sourcesContent":["import { createContext, useCallback, useContext, useRef, useSyncExternalStore } from 'react';\nimport type { AnnotationsApi } from './types';\n\n/** Null in a trial whose instrument declares no `annotations`. */\nexport const AnnotationsContext = createContext<AnnotationsApi | null>(null);\n\n/** Re-render the caller whenever any of the store's scenes changes.\n *\n * The API is a stable facade over mutable scenes, so nothing about its\n * identity says a mark was added — a component reading `query()` without this\n * renders one answer and never revises it. */\nfunction useMarkVersion(api: AnnotationsApi | null): void {\n const version = useRef(0);\n useSyncExternalStore(\n useCallback(\n (fn: () => void) =>\n api?.subscribe(() => {\n version.current += 1;\n fn();\n }) ?? (() => {}),\n [api],\n ),\n useCallback(() => version.current, []),\n () => 0,\n );\n}\n\n/** The marks on this trial's targets. Reached through a hook rather than the\n * chrome context, which carries no instrument state — a sidebar panel listing\n * marks pulls it the way `useTrialState()` does. Throws outside a trial whose\n * instrument declares the capability. */\nexport function useAnnotations(): AnnotationsApi {\n const api = useContext(AnnotationsContext);\n useMarkVersion(api);\n if (!api) {\n throw new Error(\n '[labkit] useAnnotations requires an instrument that declares the `annotations` capability',\n );\n }\n return api;\n}\n\n/** The marks on this trial's targets, or null. For chrome that renders in\n * every trial and does something else where there are none. */\nexport function useAnnotationsOptional(): AnnotationsApi | null {\n const api = useContext(AnnotationsContext);\n useMarkVersion(api);\n return api;\n}\n"]}
@@ -1,44 +1,14 @@
1
- import { Toolbar, ZoomControl, ScaleIndicator, FpsMeter, StatusBar, useRovingTabIndex } from './chunk-3FI7Y4R4.js';
1
+ import { useAnnotationsOptional, useAnnotations } from './chunk-3RY6WL5T.js';
2
+ import { Toolbar, ZoomControl, ScaleIndicator, FpsMeter, StatusBar, useRovingTabIndex } from './chunk-SLAQPUI4.js';
2
3
  import { usePersistedState } from './chunk-RWNDDKOH.js';
3
- import { ControlPanel } from './chunk-HFRE4QQV.js';
4
+ import { ControlPanel } from './chunk-ASOK76ZH.js';
4
5
  import { useLabContext } from './chunk-Z76KEZAX.js';
5
6
  import { LabStoreContext } from './chunk-2YILQ7Y5.js';
6
- import { Q, g2, u4, U, F, Te, fe, ie, pe, d2, X, Z, me, re, n8, o3, n3 } from './chunk-WUJXJC5O.js';
7
- import { createContext, useContext, useState, useId, useMemo, useRef, useSyncExternalStore, useCallback } from 'react';
7
+ import { b, A, E, C, o, O, h, P, H, g2, u4, F, k, fe, X, U, Z, d2, z, B, Q, V, n8, o4, n3 } from './chunk-TYN6MLXX.js';
8
+ import { useState, useId, useContext, useMemo } from 'react';
8
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
10
  import { useStore } from 'zustand/react';
10
11
 
11
- var AnnotationsContext = createContext(null);
12
- function useMarkVersion(api) {
13
- const version = useRef(0);
14
- useSyncExternalStore(
15
- useCallback(
16
- (fn) => api?.subscribe(() => {
17
- version.current += 1;
18
- fn();
19
- }) ?? (() => {
20
- }),
21
- [api]
22
- ),
23
- useCallback(() => version.current, []),
24
- () => 0
25
- );
26
- }
27
- function useAnnotations() {
28
- const api = useContext(AnnotationsContext);
29
- useMarkVersion(api);
30
- if (!api) {
31
- throw new Error(
32
- "[labkit] useAnnotations requires an instrument that declares the `annotations` capability"
33
- );
34
- }
35
- return api;
36
- }
37
- function useAnnotationsOptional() {
38
- const api = useContext(AnnotationsContext);
39
- useMarkVersion(api);
40
- return api;
41
- }
42
12
  var SCALES = [1, 2, 4];
43
13
  var FORMATS = [
44
14
  { id: "png", label: "PNG" },
@@ -97,7 +67,7 @@ function ExportMenu() {
97
67
  "aria-label": "Export",
98
68
  title: "Export",
99
69
  onClick: () => setOpen((was) => !was),
100
- children: /* @__PURE__ */ jsx(Q, { size: 16 })
70
+ children: /* @__PURE__ */ jsx(H, { size: 16 })
101
71
  }
102
72
  ),
103
73
  /* @__PURE__ */ jsx(
@@ -237,7 +207,7 @@ function builtinContributions(instrument, ctx, controls) {
237
207
  region: "toolbar",
238
208
  group: "history",
239
209
  item: {
240
- icon: U,
210
+ icon: F,
241
211
  label: "Undo",
242
212
  shortcut: "Mod+Z",
243
213
  disabled: !ctx.canUndo,
@@ -249,7 +219,7 @@ function builtinContributions(instrument, ctx, controls) {
249
219
  region: "toolbar",
250
220
  group: "history",
251
221
  item: {
252
- icon: F,
222
+ icon: k,
253
223
  label: "Redo",
254
224
  shortcut: "Mod+Shift+Z",
255
225
  disabled: !ctx.canRedo,
@@ -271,7 +241,7 @@ function builtinContributions(instrument, ctx, controls) {
271
241
  region: "toolbar",
272
242
  group: "view",
273
243
  item: {
274
- icon: Te,
244
+ icon: fe,
275
245
  label: "Loupe",
276
246
  pressed: ctx.loupeOn,
277
247
  onActivate: ctx.toggleLoupe
@@ -284,7 +254,7 @@ function builtinContributions(instrument, ctx, controls) {
284
254
  region: "viewport",
285
255
  group: "zoom",
286
256
  item: {
287
- icon: fe,
257
+ icon: X,
288
258
  label: "Zoom out",
289
259
  onActivate: () => ctx.setZoom(zoom / ZOOM_STEP)
290
260
  }
@@ -294,7 +264,7 @@ function builtinContributions(instrument, ctx, controls) {
294
264
  region: "viewport",
295
265
  group: "zoom",
296
266
  item: {
297
- icon: ie,
267
+ icon: U,
298
268
  label: "Zoom in",
299
269
  onActivate: () => ctx.setZoom(zoom * ZOOM_STEP)
300
270
  }
@@ -303,7 +273,7 @@ function builtinContributions(instrument, ctx, controls) {
303
273
  id: "actual-size",
304
274
  region: "viewport",
305
275
  group: "zoom",
306
- item: { icon: pe, label: "Actual size", onActivate: () => ctx.setZoom(1) }
276
+ item: { icon: Z, label: "Actual size", onActivate: () => ctx.setZoom(1) }
307
277
  });
308
278
  out.push({
309
279
  id: "zoom-control",
@@ -386,20 +356,20 @@ function builtinContributions(instrument, ctx, controls) {
386
356
  id: "clone",
387
357
  region: "titlebar",
388
358
  end: true,
389
- item: { icon: X, label: "Clone trial", onActivate: ctx.clone }
359
+ item: { icon: z, label: "Clone trial", onActivate: ctx.clone }
390
360
  });
391
361
  out.push({
392
362
  id: "reset",
393
363
  region: "titlebar",
394
364
  end: true,
395
- item: { icon: Z, label: "Reset trial", onActivate: ctx.reset }
365
+ item: { icon: B, label: "Reset trial", onActivate: ctx.reset }
396
366
  });
397
367
  out.push({
398
368
  id: "snapshot",
399
369
  region: "titlebar",
400
370
  end: true,
401
371
  item: {
402
- icon: me,
372
+ icon: Q,
403
373
  label: "Save snapshot",
404
374
  shortcut: "Mod+S",
405
375
  onActivate: () => ctx.saveSnapshot()
@@ -410,7 +380,7 @@ function builtinContributions(instrument, ctx, controls) {
410
380
  region: "titlebar",
411
381
  end: true,
412
382
  item: {
413
- icon: re,
383
+ icon: V,
414
384
  label: ctx.isLastTrial ? "Cannot close the last trial" : "Close trial",
415
385
  danger: true,
416
386
  disabled: ctx.isLastTrial,
@@ -420,10 +390,49 @@ function builtinContributions(instrument, ctx, controls) {
420
390
  return out;
421
391
  }
422
392
 
393
+ // src/annotations/toolMap.ts
394
+ var TOOLS = {
395
+ select: { weaselTool: "select" },
396
+ stroke: { weaselTool: "pencil", kind: "stroke" },
397
+ line: { weaselTool: "line", kind: "line" },
398
+ arrow: { weaselTool: "line", kind: "arrow" },
399
+ rect: { weaselTool: "rect", kind: "rect" },
400
+ ellipse: { weaselTool: "ellipse", kind: "ellipse" },
401
+ text: { weaselTool: "text", kind: "text" }
402
+ };
403
+ var ANNOTATION_TOOLS = [
404
+ // Not in `TOOLS`: an id the map does not carry is what leaves the overlay idle.
405
+ { id: "pointer", label: "Interact", icon: b, group: "annotate" },
406
+ { id: "select", label: "Select", icon: A, group: "annotate" },
407
+ { id: "stroke", label: "Freehand", icon: E, group: "annotate" },
408
+ { id: "line", label: "Line", icon: C, group: "annotate" },
409
+ { id: "arrow", label: "Arrow", icon: o, group: "annotate" },
410
+ { id: "rect", label: "Rectangle", icon: O, group: "annotate" },
411
+ { id: "ellipse", label: "Ellipse", icon: h, group: "annotate" },
412
+ { id: "text", label: "Text", icon: P, group: "annotate" }
413
+ ];
414
+ function labAnnotationTools(instruments) {
415
+ const wanted = /* @__PURE__ */ new Set();
416
+ for (const i of instruments) {
417
+ if (i.annotations == null) continue;
418
+ if (i.annotations.tools == null) return ANNOTATION_TOOLS;
419
+ for (const id of i.annotations.tools) wanted.add(id);
420
+ }
421
+ return ANNOTATION_TOOLS.filter((t) => wanted.has(t.id));
422
+ }
423
+ function annotationToolInfo(id) {
424
+ return id === null ? void 0 : TOOLS[id];
425
+ }
426
+ var ANNOTATION_WEASEL_TOOLS = [
427
+ ...new Set(Object.values(TOOLS).map((t) => t.weaselTool))
428
+ ];
429
+
423
430
  // src/tools/labTool.ts
424
431
  function resolveLabTool(slot, instruments) {
425
432
  if (slot !== null) return slot;
426
- return instruments.some((i) => i.annotations != null) ? "pointer" : null;
433
+ const tools = labAnnotationTools(instruments);
434
+ if (tools.some((t) => t.id === "pointer")) return "pointer";
435
+ return tools[0]?.id ?? null;
427
436
  }
428
437
 
429
438
  // src/chrome/merge.ts
@@ -695,7 +704,7 @@ function PaletteRegion({
695
704
  if (c.region !== region || !c.item) return null;
696
705
  const { icon: Icon, label, shortcut, disabled, onActivate } = c.item;
697
706
  return /* @__PURE__ */ jsx(
698
- o3,
707
+ o4,
699
708
  {
700
709
  icon: /* @__PURE__ */ jsx(Icon, { size: 16 }),
701
710
  label,
@@ -769,6 +778,6 @@ function ViewportRegion({ contributions, ctx }) {
769
778
  );
770
779
  }
771
780
 
772
- export { AnnotationsContext, ExportMenu, LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, resolveLabTool, suppressContributions, toolContributions, useAnnotations, useAnnotationsOptional, useLabChromeContext };
773
- //# sourceMappingURL=chunk-H5VIV7MQ.js.map
774
- //# sourceMappingURL=chunk-H5VIV7MQ.js.map
781
+ export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, ExportMenu, LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, annotationToolInfo, builtinContributions, contributionsIn, labAnnotationTools, labContributions, mergeContributions, resolveLabTool, suppressContributions, toolContributions, useLabChromeContext };
782
+ //# sourceMappingURL=chunk-4TT3PNPR.js.map
783
+ //# sourceMappingURL=chunk-4TT3PNPR.js.map