@weasel-js/labkit 1.3.0 → 1.4.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/_dts/CanvasStackContext-DtAn_3O-.d.ts +42 -0
  2. package/dist/_dts/DrawCommand-B3bskUsC.d.ts +564 -0
  3. package/dist/_dts/{PrefsForm-BYa6cWnO.d.ts → PrefsForm-BkUJZx0A.d.ts} +4 -1
  4. package/dist/_dts/fitViewToBounds-dZ2UDB6e.d.ts +21 -0
  5. package/dist/_dts/frac-Cp3NivlC.d.ts +728 -0
  6. package/dist/_dts/index-BDVzvRzQ.d.ts +236 -0
  7. package/dist/_dts/shapeKinds-Cx_rxwsa.d.ts +87 -0
  8. package/dist/_dts/{DrawCommand-DBB45NfN.d.ts → types-C-gh9Ap-.d.ts} +1 -582
  9. package/dist/_dts/{types-DCGEcMO_.d.ts → types-D6s4b7if.d.ts} +1 -1
  10. package/dist/_dts/{types-1Sdxy_Pv.d.ts → types-DYMaEvM5.d.ts} +26 -1
  11. package/dist/_dts/{useTrialState-CsGMjhu9.d.ts → useTrialState-gmMvZqPc.d.ts} +7 -2
  12. package/dist/canvas/index.d.ts +43 -25
  13. package/dist/canvas/index.js +5 -4
  14. package/dist/chrome/index.d.ts +9 -6
  15. package/dist/chrome/index.js +6 -6
  16. package/dist/chunk-2AYGEN57.js +32 -0
  17. package/dist/chunk-2AYGEN57.js.map +1 -0
  18. package/dist/{chunk-QRYPXSGP.js → chunk-3P5TTTA7.js} +16 -8
  19. package/dist/chunk-3P5TTTA7.js.map +1 -0
  20. package/dist/{chunk-BDJXIBRZ.js → chunk-3ZELRRBV.js} +44 -27
  21. package/dist/chunk-3ZELRRBV.js.map +1 -0
  22. package/dist/{chunk-LGKRIUSW.js → chunk-4EY67BKJ.js} +32 -7
  23. package/dist/chunk-4EY67BKJ.js.map +1 -0
  24. package/dist/{chunk-3XXPU73K.js → chunk-5TCEVMPI.js} +9 -7
  25. package/dist/chunk-5TCEVMPI.js.map +1 -0
  26. package/dist/{chunk-JDROYQM3.js → chunk-6JMU56CU.js} +130 -131
  27. package/dist/chunk-6JMU56CU.js.map +1 -0
  28. package/dist/{chunk-CPUJ3QXL.js → chunk-CQLQPQ4P.js} +2 -2
  29. package/dist/chunk-CQLQPQ4P.js.map +1 -0
  30. package/dist/chunk-CTRKTLYZ.js +33 -0
  31. package/dist/chunk-CTRKTLYZ.js.map +1 -0
  32. package/dist/chunk-EQIB2PGC.js +458 -0
  33. package/dist/chunk-EQIB2PGC.js.map +1 -0
  34. package/dist/chunk-HEORBLVT.js +7128 -0
  35. package/dist/chunk-HEORBLVT.js.map +1 -0
  36. package/dist/{chunk-BMW4TDP5.js → chunk-JDDIZSLL.js} +3 -3
  37. package/dist/{chunk-BMW4TDP5.js.map → chunk-JDDIZSLL.js.map} +1 -1
  38. package/dist/chunk-LITE5YFB.js +669 -0
  39. package/dist/chunk-LITE5YFB.js.map +1 -0
  40. package/dist/{chunk-QCICIHEO.js → chunk-ROQDRXKW.js} +716 -311
  41. package/dist/chunk-ROQDRXKW.js.map +1 -0
  42. package/dist/{chunk-4TMMVIDM.js → chunk-XJ6N32QP.js} +50 -6
  43. package/dist/chunk-XJ6N32QP.js.map +1 -0
  44. package/dist/{chunk-WTI26YTM.js → chunk-YQJNT4XQ.js} +40 -13
  45. package/dist/chunk-YQJNT4XQ.js.map +1 -0
  46. package/dist/controls/index.d.ts +4 -28
  47. package/dist/controls/index.js +3 -3
  48. package/dist/dragdrop/index.d.ts +9 -5
  49. package/dist/dragdrop/index.js +3 -3
  50. package/dist/index.d.ts +518 -190
  51. package/dist/index.js +1753 -75
  52. package/dist/index.js.map +1 -1
  53. package/dist/layers/index.d.ts +6 -5
  54. package/dist/layers/index.js +3 -3
  55. package/dist/loupe/index.d.ts +187 -0
  56. package/dist/loupe/index.js +7 -0
  57. package/dist/loupe/index.js.map +1 -0
  58. package/dist/passthrough/weasel-canvas.d.ts +3 -1
  59. package/dist/passthrough/weasel-canvas.js +1 -1
  60. package/dist/passthrough/weasel-ui.d.ts +138 -91
  61. package/dist/passthrough/weasel-ui.js +2 -2
  62. package/dist/primitives/index.d.ts +3 -1
  63. package/dist/primitives/index.js +5 -5
  64. package/dist/state/index.d.ts +3 -3
  65. package/dist/state/index.js +2 -2
  66. package/dist/styles.css +229 -1
  67. package/dist/surface/index.d.ts +18 -2
  68. package/dist/surface/index.js +2 -2
  69. package/dist/ui/layers/index.js +2 -2
  70. package/dist/undo/index.d.ts +5 -4
  71. package/package.json +14 -7
  72. package/src/annotations/AnnotationOverlay.tsx +205 -0
  73. package/src/annotations/AnnotationTargets.tsx +37 -0
  74. package/src/annotations/Annotations.less +129 -0
  75. package/src/annotations/Annotations.meaning.test.tsx +111 -0
  76. package/src/annotations/Annotations.overlay.test.tsx +142 -0
  77. package/src/annotations/AnnotationsContext.ts +49 -0
  78. package/src/annotations/ExportMenu.test.tsx +86 -0
  79. package/src/annotations/ExportMenu.tsx +182 -0
  80. package/src/annotations/MarkList.tsx +75 -0
  81. package/src/annotations/capture.test.ts +140 -0
  82. package/src/annotations/capture.ts +237 -0
  83. package/src/annotations/drawOne.test.ts +56 -0
  84. package/src/annotations/drawOne.ts +47 -0
  85. package/src/annotations/frac.test.ts +59 -0
  86. package/src/annotations/frac.ts +66 -0
  87. package/src/annotations/history.test.ts +82 -0
  88. package/src/annotations/history.ts +118 -0
  89. package/src/annotations/index.ts +37 -0
  90. package/src/annotations/paint.test.ts +125 -0
  91. package/src/annotations/paint.ts +130 -0
  92. package/src/annotations/staleness.test.ts +56 -0
  93. package/src/annotations/staleness.ts +41 -0
  94. package/src/annotations/store.test.ts +175 -0
  95. package/src/annotations/store.ts +317 -0
  96. package/src/annotations/svgNodes.test.ts +77 -0
  97. package/src/annotations/svgNodes.ts +74 -0
  98. package/src/annotations/toolMap.test.ts +36 -0
  99. package/src/annotations/toolMap.ts +54 -0
  100. package/src/annotations/types.ts +222 -0
  101. package/src/annotations/view.test.ts +37 -0
  102. package/src/annotations/view.ts +40 -0
  103. package/src/canvas/AGENTS.md +55 -5
  104. package/src/canvas/CanvasStack.test.tsx +27 -1
  105. package/src/canvas/CanvasStack.tsx +29 -4
  106. package/src/canvas/CanvasStackContext.ts +23 -4
  107. package/src/canvas/camera.test.ts +78 -0
  108. package/src/canvas/camera.ts +62 -0
  109. package/src/canvas/canvasCoords.test.ts +26 -0
  110. package/src/canvas/canvasCoords.ts +20 -7
  111. package/src/canvas/index.ts +5 -1
  112. package/src/canvas/useLayerScheduler.ts +7 -3
  113. package/src/canvas/usePanZoom.test.ts +36 -5
  114. package/src/canvas/usePanZoom.ts +20 -15
  115. package/src/canvas/worldSpec.test.ts +63 -0
  116. package/src/canvas/worldSpec.ts +48 -0
  117. package/src/chrome/builtins.test.ts +31 -1
  118. package/src/chrome/builtins.tsx +63 -19
  119. package/src/chrome/regions/PaletteRegion.test.tsx +22 -1
  120. package/src/chrome/regions/PaletteRegion.tsx +4 -0
  121. package/src/chrome/regions/SidebarRegion.tsx +37 -13
  122. package/src/chrome/regions/ToolbarRegion.tsx +2 -1
  123. package/src/chrome/regions/ViewportRegion.tsx +9 -1
  124. package/src/chrome/regions/regions.test.tsx +6 -1
  125. package/src/chrome/types.ts +20 -0
  126. package/src/controls/ControlPanel.test.tsx +38 -0
  127. package/src/controls/ControlPanel.tsx +50 -3
  128. package/src/dragdrop/DragDropRuntime.tsx +8 -2
  129. package/src/dragdrop/dragDrop.test.tsx +29 -2
  130. package/src/index.test.ts +22 -0
  131. package/src/index.ts +87 -2
  132. package/src/instrument/SineWave.smoke.test.tsx +1 -0
  133. package/src/instrument/types.ts +24 -1
  134. package/src/lab/Lab.surface.test.tsx +162 -0
  135. package/src/lab/Lab.tsx +102 -17
  136. package/src/lab/Lab.undock.test.tsx +85 -0
  137. package/src/lab/LabFullChrome.stories.tsx +20 -0
  138. package/src/lab/LabShell.less +11 -0
  139. package/src/lab/Workspace.less +73 -0
  140. package/src/lab/Workspace.surface.test.tsx +2 -0
  141. package/src/lab/Workspace.tsx +97 -8
  142. package/src/lab/index.ts +1 -1
  143. package/src/lab/panelHost.ts +37 -0
  144. package/src/loupe/AGENTS.md +49 -0
  145. package/src/loupe/CanvasLoupe.tsx +83 -0
  146. package/src/loupe/DomLoupe.tsx +62 -0
  147. package/src/loupe/Loupe.less +38 -0
  148. package/src/loupe/LoupeBubble.tsx +32 -0
  149. package/src/loupe/TrialLoupe.tsx +93 -0
  150. package/src/loupe/canvasLens.test.ts +205 -0
  151. package/src/loupe/canvasLens.ts +141 -0
  152. package/src/loupe/index.ts +19 -0
  153. package/src/loupe/types.test.ts +29 -0
  154. package/src/loupe/types.ts +90 -0
  155. package/src/loupe/useHostSize.ts +30 -0
  156. package/src/loupe/useLoupe.test.tsx +197 -0
  157. package/src/loupe/useLoupe.ts +189 -0
  158. package/src/passthrough/weasel-ui.ts +3 -0
  159. package/src/primitives/FloatingPanel.test.tsx +76 -1
  160. package/src/primitives/FloatingPanel.tsx +19 -3
  161. package/src/primitives/ScaleIndicator.test.tsx +7 -2
  162. package/src/primitives/Sidebar.less +18 -0
  163. package/src/primitives/Toolbar.less +8 -0
  164. package/src/primitives/Toolbar.tsx +5 -0
  165. package/src/primitives/useRovingTabIndex.test.ts +21 -0
  166. package/src/primitives/useRovingTabIndex.ts +26 -17
  167. package/src/state/document.test.ts +23 -0
  168. package/src/state/document.ts +14 -2
  169. package/src/state/index.ts +2 -0
  170. package/src/state/store.test.ts +24 -0
  171. package/src/state/store.ts +35 -1
  172. package/src/state/types.ts +11 -0
  173. package/src/state/undock.test.ts +40 -0
  174. package/src/state/undock.ts +39 -0
  175. package/src/styles.less +2 -0
  176. package/src/surface/SurfaceContext.ts +4 -0
  177. package/src/surface/index.ts +8 -3
  178. package/src/surface/useSurfaceTile.test.tsx +2 -0
  179. package/src/surface/useSurfaceTile.ts +7 -1
  180. package/src/surface/useTiledSurface.test.tsx +58 -0
  181. package/src/surface/useTiledSurface.ts +38 -3
  182. package/src/trial/Trial.annotations.persist.test.tsx +166 -0
  183. package/src/trial/Trial.annotations.test.tsx +121 -0
  184. package/src/trial/Trial.canvas.test.tsx +76 -1
  185. package/src/trial/Trial.less +19 -0
  186. package/src/trial/Trial.loupe.test.tsx +128 -0
  187. package/src/trial/Trial.stories.tsx +1 -0
  188. package/src/trial/Trial.test.tsx +12 -0
  189. package/src/trial/Trial.trialId.test.tsx +36 -0
  190. package/src/trial/Trial.tsx +215 -40
  191. package/src/trial/TrialChrome.tsx +35 -0
  192. package/src/trial/UndockedSections.tsx +65 -0
  193. package/src/trial/trialOps.test.ts +21 -0
  194. package/src/trial/trialOps.ts +5 -3
  195. package/dist/_dts/types-BttTIent.d.ts +0 -314
  196. package/dist/chunk-3XXPU73K.js.map +0 -1
  197. package/dist/chunk-4TMMVIDM.js.map +0 -1
  198. package/dist/chunk-6X5RBC6G.js +0 -366
  199. package/dist/chunk-6X5RBC6G.js.map +0 -1
  200. package/dist/chunk-BDJXIBRZ.js.map +0 -1
  201. package/dist/chunk-CPUJ3QXL.js.map +0 -1
  202. package/dist/chunk-JDROYQM3.js.map +0 -1
  203. package/dist/chunk-LGKRIUSW.js.map +0 -1
  204. package/dist/chunk-QCICIHEO.js.map +0 -1
  205. package/dist/chunk-QRYPXSGP.js.map +0 -1
  206. package/dist/chunk-RGT4EJIB.js +0 -6961
  207. package/dist/chunk-RGT4EJIB.js.map +0 -1
  208. package/dist/chunk-T66HU3HX.js +0 -17
  209. package/dist/chunk-T66HU3HX.js.map +0 -1
  210. package/dist/chunk-WTI26YTM.js.map +0 -1
@@ -0,0 +1,728 @@
1
+ import { ComponentType, ReactNode, RefObject } from 'react';
2
+ import { R as ResolvedConfig, C as ConfigField, a as ConfigSchema } from './types-D6s4b7if.js';
3
+ import { c as SavedSnapshot } from './types-DYMaEvM5.js';
4
+ import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
5
+ import { S as Scene } from './types-C-gh9Ap-.js';
6
+
7
+ /** How an instrument's world sits on the canvas, independent of the camera.
8
+ * The defaults reproduce the convention labkit shipped before this existed:
9
+ * world (0,0) at the element's top-left, y growing downward. */
10
+ interface WorldSpec {
11
+ /** Where world (0,0) sits at `pan` zero, as a fraction of the viewport.
12
+ * `{x:0,y:0}` top-left (default); `{x:0.5,y:0.5}` centre. */
13
+ origin?: Point;
14
+ /** Which way the world y axis runs on screen. Default `'down'`. */
15
+ yAxis?: 'down' | 'up';
16
+ }
17
+ /** A viewport's CSS-pixel size. */
18
+ interface ViewportSize {
19
+ width: number;
20
+ height: number;
21
+ }
22
+ /** A `WorldSpec` resolved against a viewport: what the coordinate helpers,
23
+ * the camera and the wheel all read. */
24
+ interface WorldFrame {
25
+ /** Screen position of world (0,0) at `pan` zero. */
26
+ originPx: Point;
27
+ yDir: 1 | -1;
28
+ }
29
+ declare const DEFAULT_FRAME: WorldFrame;
30
+ declare function resolveFrame(spec: WorldSpec | undefined, size: ViewportSize): WorldFrame;
31
+ /** Put a 2D context into the instrument's world coordinates, so a layer's
32
+ * `draw` works in them. Must stay the exact inverse of `screenToWorld`. */
33
+ declare function applyCamera(ctx: CanvasRenderingContext2D, view: ViewTransform, frame?: WorldFrame): void;
34
+
35
+ /** A named position in a trial's chrome. Content is not a region — that is
36
+ * the instrument. */
37
+ type TrialRegion = 'titlebar' | 'toolbar' | 'palette' | 'sidebar' | 'viewport' | 'status';
38
+ /** An icon component taking a pixel size, as `@weasel-js/ui` glyphs do. */
39
+ type IconComponent = ComponentType<{
40
+ size?: number;
41
+ }>;
42
+ /** A button in the trial toolbar. */
43
+ interface ToolbarItem {
44
+ icon: IconComponent;
45
+ label: string;
46
+ /** Shown in the tooltip. Not bound here — the trial owns its keymap. */
47
+ shortcut?: string;
48
+ disabled?: boolean;
49
+ /** Reddens on hover. For actions that discard work. */
50
+ danger?: boolean;
51
+ /** A toggle rather than a command: renders `aria-pressed` and reads as held
52
+ * down while true. */
53
+ pressed?: boolean;
54
+ /** Render the label beside the glyph rather than only in the tooltip. */
55
+ showLabel?: boolean;
56
+ onActivate: () => void;
57
+ }
58
+ /** A selectable tool in the palette region. */
59
+ interface ToolItem {
60
+ icon: IconComponent;
61
+ label: string;
62
+ shortcut?: string;
63
+ disabled?: boolean;
64
+ }
65
+ /** A titled block in the sidebar. */
66
+ interface SidebarSection {
67
+ title: string;
68
+ /** Starts collapsed. The open/closed state itself is the region's. */
69
+ defaultCollapsed?: boolean;
70
+ /** Offer the tear-out control. On by default; a section that only makes
71
+ * sense beside its trial sets this false. */
72
+ undockable?: boolean;
73
+ /** Where the tear-out control sends it. Default `'tile'`. */
74
+ undockAs?: 'tile' | 'floating';
75
+ body: ReactNode;
76
+ }
77
+ /** A control acting on the view of the trial, not on the trial. */
78
+ interface ViewportControl {
79
+ icon: IconComponent;
80
+ label: string;
81
+ disabled?: boolean;
82
+ onActivate: () => void;
83
+ }
84
+ /** A readout in the status bar. */
85
+ interface StatusReadout {
86
+ /** Short enough for a status bar. Rendered as text. */
87
+ text: string;
88
+ /** Tooltip. */
89
+ title?: string;
90
+ }
91
+ /** What every contribution shares. */
92
+ interface ContributionBase {
93
+ id: string;
94
+ /** Groups sort by first appearance; items sort within a group by
95
+ * declaration order. Contributions with no group sort after grouped ones. */
96
+ group?: string;
97
+ /** Pushes this contribution, and its group, to the far end of the region. */
98
+ end?: boolean;
99
+ }
100
+ /**
101
+ * A contribution is data the chrome renders, keyed to a region. Supplying
102
+ * `render` instead of `item` opts out of the chrome's layout — deliberate,
103
+ * and visible in the declaration.
104
+ */
105
+ type TrialContribution = (ContributionBase & {
106
+ region: 'titlebar';
107
+ item: ToolbarItem;
108
+ render?: never;
109
+ }) | (ContributionBase & {
110
+ region: 'toolbar';
111
+ item: ToolbarItem;
112
+ render?: never;
113
+ }) | (ContributionBase & {
114
+ region: 'palette';
115
+ item: ToolItem;
116
+ render?: never;
117
+ }) | (ContributionBase & {
118
+ region: 'sidebar';
119
+ item: SidebarSection;
120
+ render?: never;
121
+ }) | (ContributionBase & {
122
+ region: 'viewport';
123
+ item: ViewportControl;
124
+ render?: never;
125
+ }) | (ContributionBase & {
126
+ region: 'status';
127
+ item: StatusReadout;
128
+ render?: never;
129
+ }) | (ContributionBase & {
130
+ region: TrialRegion;
131
+ item?: never;
132
+ render: (ctx: TrialChromeContext) => ReactNode;
133
+ });
134
+ /**
135
+ * Everything a contribution can read about the trial it is being rendered
136
+ * into. Replaces the three separate slot contexts, which each carried a
137
+ * hand-picked subset.
138
+ */
139
+ interface TrialChromeContext {
140
+ trialId: string;
141
+ instrumentName: string;
142
+ isLastTrial: boolean;
143
+ /** Null when the trial holds a view that is not the 2D one. */
144
+ zoom: number | null;
145
+ setZoom: (z: number) => void;
146
+ canUndo: boolean;
147
+ canRedo: boolean;
148
+ undo: () => void;
149
+ redo: () => void;
150
+ /** Whether the trial's loupe is turned on. False for an instrument that
151
+ * declares none, whose chrome offers no way to turn one on. */
152
+ loupeOn: boolean;
153
+ toggleLoupe: () => void;
154
+ /** The instrument's controls, resolved against the lab's rules. Always
155
+ * populated: a legacy `configSchema()` is adapted into the same shape. */
156
+ configSchema: ResolvedConfig;
157
+ /** @deprecated Read `configSchema`. Empty for an instrument declaring
158
+ * `config`, since a builder schema has no `ConfigField[]` form. */
159
+ configFields: ConfigField[];
160
+ config: unknown;
161
+ setConfig: (key: string, value: unknown) => void;
162
+ /** Section ids this trial currently has torn out of its sidebar. */
163
+ undockedPanels: readonly string[];
164
+ /** Tear a sidebar section out into the workspace. */
165
+ undockPanel: (sectionId: string, as?: 'tile' | 'floating') => void;
166
+ /** Put a torn-out section back in the sidebar. */
167
+ dockPanel: (sectionId: string) => void;
168
+ savedSnapshots: SavedSnapshot[];
169
+ saveSnapshot: (name?: string) => void;
170
+ loadSnapshot: (snapshotId: string) => void;
171
+ clone: () => void;
172
+ reset: () => void;
173
+ close: () => void;
174
+ /** Resolved active tool: the trial's slot, or the lab's when the trial has
175
+ * none. Null when neither holds one. */
176
+ activeToolId: string | null;
177
+ setActiveTool: (id: string) => void;
178
+ }
179
+
180
+ /** How a loupe magnifies. `'vector'` re-renders the source through a zoomed-in
181
+ * view, so content stays sharp at any factor; `'pixel'` blows up the actual
182
+ * pixels the surface presented. */
183
+ type LoupeMode = 'vector' | 'pixel';
184
+
185
+ /** What a DOM instrument's loupe `render` is handed: the same data its own
186
+ * `render` gets, and the camera to draw it through. */
187
+ interface LoupeRenderArgs<TS = unknown, TC = unknown> {
188
+ state: TS;
189
+ config: TC;
190
+ /** The trial's own camera composed with the magnification, about the aimed
191
+ * point — so drawing the same content through it magnifies in place. */
192
+ view: ViewTransform;
193
+ /** The magnification on its own, for whatever must not scale with the
194
+ * camera. */
195
+ factor: number;
196
+ mode: LoupeMode;
197
+ /** The size of the viewport `view` is written for: the trial's content well,
198
+ * not the lens. The lens shows a circle cut out of it. */
199
+ size: ViewportSize;
200
+ }
201
+ /**
202
+ * Declares that an instrument can be magnified.
203
+ *
204
+ * With no `render`, the loupe re-draws the instrument's canvas layers through a
205
+ * zoomed camera, so it stays sharp at any factor. An instrument whose content is
206
+ * DOM supplies `render` instead: given a camera, draw me again.
207
+ */
208
+ interface LoupeCapability<TS = unknown, TC = unknown> {
209
+ render?: (args: LoupeRenderArgs<TS, TC>) => ReactNode;
210
+ /** Opening magnification, clamped to the bounds below. Default 6. */
211
+ factor?: number;
212
+ /** What the wheel clamps to. Defaults 2 and 32. */
213
+ minFactor?: number;
214
+ maxFactor?: number;
215
+ /** `'vector'` (default) re-renders the content magnified; `'pixel'` blows up
216
+ * the pixels the instrument presented. A `render` loupe is always vector —
217
+ * DOM has no framebuffer to enlarge. */
218
+ mode?: LoupeMode;
219
+ /** Lens diameter in CSS px. Default 200. */
220
+ diameter?: number;
221
+ /** Held for a momentary peek while the loupe is off. Default `'Alt'`; `null`
222
+ * turns hold-to-peek off. Matched against `KeyboardEvent.key`. */
223
+ peekKey?: string | null;
224
+ /** Called with the colour under the aim, wherever the surface can say. The
225
+ * canvas painter reads it back; a DOM loupe has no pixels to sample. */
226
+ onColorChange?: (hex: string) => void;
227
+ }
228
+ /** An instrument's loupe declaration. `true` takes every default. */
229
+ type LoupeDeclaration<TS = unknown, TC = unknown> = true | LoupeCapability<TS, TC>;
230
+ /** A {@link LoupeCapability} with every default filled in. */
231
+ interface ResolvedLoupe<TS = unknown, TC = unknown> {
232
+ render?: (args: LoupeRenderArgs<TS, TC>) => ReactNode;
233
+ onColorChange?: (hex: string) => void;
234
+ factor: number;
235
+ minFactor: number;
236
+ maxFactor: number;
237
+ mode: LoupeMode;
238
+ diameter: number;
239
+ peekKey: string | null;
240
+ }
241
+ declare const LOUPE_DEFAULTS: {
242
+ readonly factor: 6;
243
+ readonly minFactor: 2;
244
+ readonly maxFactor: 32;
245
+ readonly mode: "vector";
246
+ readonly diameter: 200;
247
+ readonly peekKey: "Alt";
248
+ };
249
+ declare function resolveLoupe<TS, TC>(declared: LoupeDeclaration<TS, TC>): ResolvedLoupe<TS, TC>;
250
+
251
+ /**
252
+ * A tool a trial can be in. labkit's own — core's `ToolsApi` carries hotkey
253
+ * slots, ambient tools, eligibility tiers and canvas overlay layers, all bound
254
+ * to the gesture dispatcher, and a labkit instrument is an arbitrary canvas or
255
+ * DOM tree rather than a weasel scene.
256
+ */
257
+ interface TrialTool {
258
+ id: string;
259
+ label: string;
260
+ icon: IconComponent;
261
+ /** Shown in the tooltip. Not bound here — the instrument owns its keymap. */
262
+ shortcut?: string;
263
+ /** Presentation grouping in the palette. Ungrouped tools sort after grouped. */
264
+ group?: string;
265
+ }
266
+ /** What an instrument declares to get a palette region. */
267
+ interface ToolCapability {
268
+ tools: TrialTool[];
269
+ /** Which tool a fresh trial starts in. Defaults to the first. */
270
+ initial?: string;
271
+ }
272
+
273
+ /** What an instrument's `render` is handed: its state and config, the setters
274
+ * for both, the trial it is mounted in, and a way to emit named events. */
275
+ interface RenderContext<TS = unknown, TC = unknown> {
276
+ state: TS;
277
+ config: TC;
278
+ setState: (next: TS | ((prev: TS) => TS)) => void;
279
+ setConfig: (key: keyof TC, value: unknown) => void;
280
+ trial: {
281
+ id: string;
282
+ /** The trial's view, in whatever shape this instrument chose. labkit persists
283
+ * it and restores it on Reset without ever reading into it. */
284
+ view: unknown;
285
+ setView: (next: unknown) => void;
286
+ /** 2D convenience over `view`. Reads 1 and writes nothing when the trial holds
287
+ * a view that is not the 2D one. */
288
+ zoom: number;
289
+ setZoom: (z: number) => void;
290
+ /** Resolved active tool: this trial's slot, or the lab's. Null when neither
291
+ * holds one. */
292
+ activeToolId: string | null;
293
+ /** The canvas layers currently shown, in declaration order. labkit skips a
294
+ * hidden layer's `draw`, so this is the only way to know what was
295
+ * painted — a legend listing rows the view did not draw needs it. */
296
+ visibleLayers: readonly string[];
297
+ };
298
+ emit: (event: string) => void;
299
+ /** Present only when the instrument declares a `job`. */
300
+ job?: JobHandle;
301
+ }
302
+ /** One 2D canvas layer of an instrument, drawn in declaration order.
303
+ *
304
+ * `draw` is called with the camera already applied, so it works in world
305
+ * coordinates. `zoom` is passed for the things that must not scale with it —
306
+ * set `ctx.lineWidth = 1 / zoom` to keep a hairline hairline. */
307
+ interface CanvasLayer<TS = unknown, TC = unknown> {
308
+ id: string;
309
+ draw: (ctx: CanvasRenderingContext2D, args: {
310
+ state: TS;
311
+ config: TC;
312
+ zoom: number;
313
+ }) => void;
314
+ }
315
+ /** Declares that an instrument draws to a canvas: its layers, and where the
316
+ * view starts. */
317
+ interface CanvasCapability<TS = unknown, TC = unknown> {
318
+ layers: CanvasLayer<TS, TC>[];
319
+ /** The coordinate system this instrument's world is in. Omitted, world (0,0)
320
+ * sits at the canvas top-left with y running down — what labkit assumed
321
+ * before an instrument could say otherwise. */
322
+ worldSpec?: WorldSpec;
323
+ /** Where the view starts. A function is called once the canvas knows its
324
+ * size, so an instrument can frame content it can only place in terms of
325
+ * the viewport; until then the trial's view is `null`. */
326
+ initialView?: ViewTransform | ((size: ViewportSize) => ViewTransform);
327
+ /** Widens `usePanZoom`'s default clamp; the opening zoom stays reachable
328
+ * regardless of these. */
329
+ minZoom?: number;
330
+ maxZoom?: number;
331
+ }
332
+ /** Declares which of an instrument's layers the trial should offer
333
+ * show/hide controls for. */
334
+ interface LayerCapability {
335
+ /** In list order. A bare string is a layer id that doubles as its own label;
336
+ * give a descriptor instead to label a layer or mark it `alwaysOn`. */
337
+ ids: readonly (string | LayerDescriptor)[];
338
+ }
339
+ /** Declares that an instrument accepts items dragged from a palette: what the
340
+ * palette offers, what a drop does to the state, and — optionally — live
341
+ * feedback during the drag and the ability to drag existing items back out. */
342
+ interface DragDropCapability<TS = unknown, TC = unknown> {
343
+ palette: PaletteItem[] | ((state: TS, config: TC) => PaletteItem[]);
344
+ onDrop: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => TS;
345
+ onDragOver?: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => DragFeedback | null;
346
+ pickUp?: (hit: HitResult, state: TS, config: TC) => {
347
+ item: PaletteItem;
348
+ state: TS;
349
+ } | null;
350
+ }
351
+ /** Declares that an instrument's state is undoable: which emitted events
352
+ * snapshot it, and how many snapshots to keep. */
353
+ interface UndoCapability {
354
+ snapshotOn?: string[];
355
+ maxDepth?: number;
356
+ }
357
+ /** The name of an event an instrument emits through `RenderContext.emit`. */
358
+ type SystemEvent = string;
359
+ /** A point in world coordinates. */
360
+ type Point = {
361
+ x: number;
362
+ y: number;
363
+ };
364
+ /** What a hit-test found, and where. */
365
+ type HitResult = {
366
+ hit: boolean;
367
+ layerId?: string;
368
+ pointId?: string;
369
+ };
370
+ /** A trial's camera. */
371
+ type ViewTransform = {
372
+ zoom: number;
373
+ pan: Point;
374
+ };
375
+ /** A layer as the layer list shows it. `alwaysOn` layers cannot be hidden. */
376
+ type LayerDescriptor = {
377
+ id: string;
378
+ label: string;
379
+ alwaysOn?: boolean;
380
+ };
381
+ /** One draggable entry in an instrument's palette. */
382
+ type PaletteItem = {
383
+ id: string;
384
+ label: string;
385
+ data?: unknown;
386
+ };
387
+ /** Whether a drop would be accepted at the current position, and why not if
388
+ * it would not. */
389
+ type DragFeedback = {
390
+ ok: boolean;
391
+ reason?: string;
392
+ };
393
+ /**
394
+ * An instrument: one self-contained interactive experiment a lab can host.
395
+ *
396
+ * It owns two pieces of data — `config`, the settings the control panel edits,
397
+ * and `state`, what the experiment is currently doing — and renders from both.
398
+ * The optional capability fields declare what else it wants from the runtime:
399
+ * a canvas, a layer list, palette drag-and-drop, undo. Declaring a capability
400
+ * is what makes the trial provide the corresponding chrome.
401
+ */
402
+ interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
403
+ name: string;
404
+ defaultConfig: () => TC;
405
+ initialState: (config: TC) => TS;
406
+ /** The instrument's config, declared once: values, types and controls.
407
+ * Supplying this makes `defaultConfig` optional — `defineInstrument`
408
+ * synthesizes it. Prefer it over `defaultConfig` + `configSchema`. */
409
+ config?: ConfigSchema<TC>;
410
+ /** @deprecated Declare `config` instead; this repeats what `TC` already
411
+ * says and nothing holds the two to one answer. */
412
+ configSchema?: () => ConfigField[];
413
+ /** The instrument's DOM. With `canvas`, this renders as an overlay above the
414
+ * layers rather than instead of them; return `null` for canvas only. */
415
+ render: (ctx: RenderContext<TS, TC>) => ReactNode;
416
+ onConfigChange?: (config: TC, prev: TC, state: TS) => TS;
417
+ serialize?: (state: TS) => unknown;
418
+ deserialize?: (data: unknown, config: TC) => TS;
419
+ canvas?: CanvasCapability<TS, TC>;
420
+ layers?: LayerCapability;
421
+ dragDrop?: DragDropCapability<TS, TC>;
422
+ undo?: UndoCapability;
423
+ /** Tools this instrument offers. Declaring them gives the trial a palette
424
+ * region and its own tool slot. */
425
+ tools?: ToolCapability;
426
+ /** Regions of this instrument that accept marks, and optionally what a mark
427
+ * is allowed to mean. Declaring it is what makes the trial provide the
428
+ * annotation overlay and its chrome. */
429
+ annotations?: AnnotationsCapability<TS, TC>;
430
+ /** Magnification. `true` takes every default and re-draws the instrument's
431
+ * canvas layers through a zoomed camera; an instrument whose content is DOM
432
+ * gives a `render` that draws it again at a camera it is handed. A function
433
+ * is re-read as the config changes, so a setting can drive the lens. */
434
+ loupe?: LoupeDeclaration<TS, TC> | ((config: TC) => LoupeDeclaration<TS, TC>);
435
+ /** Chrome this instrument contributes beyond what its capabilities imply. */
436
+ chrome?: TrialContribution[];
437
+ /** Work too slow to do during a render. The runtime starts it, aborts it on
438
+ * unmount and on a `key` change, and renders progress into the trial. */
439
+ job?: JobCapability<TS, TC, TItem>;
440
+ }
441
+ /** Instruments as a lab receives them. `any` rather than `unknown` because
442
+ * parameter contravariance keeps a `defineInstrument<TS, TC>` result out of
443
+ * an `Instrument<unknown, unknown>[]`; it is contained to this alias. */
444
+ type InstrumentList = readonly Instrument<any, any, any>[];
445
+
446
+ /** A mark's box in its target's world. Matches weasel's default `RectPose`. */
447
+ type MarkPose = WorldRect;
448
+ /** The scene one target's marks live in. */
449
+ type MarkScene = Scene<AnnotationData, 'marks', MarkPose>;
450
+ /** A mark scene: one layer, because marks do not stack in tiers. */
451
+ declare function createAnnotationScene(): MarkScene;
452
+ interface AnnotationStoreOptions {
453
+ /** Re-read on every call, so a target resizing or gaining a dependency takes
454
+ * effect without rebuilding the store. */
455
+ targets: () => readonly AnnotationTargetInfo[];
456
+ /** Serialized scenes from a previous `toJSON`, keyed by target. */
457
+ restore?: Readonly<Record<string, unknown>>;
458
+ /** The instrument's vocabulary, so an export draws a mark in the colour its
459
+ * status gives it. */
460
+ meaning?: AnnotationMeaning;
461
+ /** The trial's live config. A getter for the same reason `targets` is: the
462
+ * store is built once and the config changes under it. */
463
+ config?: () => unknown;
464
+ /** Notified after every finished export. */
465
+ onCapture?: (result: CaptureResult) => void;
466
+ }
467
+ /** Everything a capture needs that is not the store's own state. Split out so
468
+ * `capture.ts` takes data rather than reaching back into the store. */
469
+ interface CaptureDeps {
470
+ scene: MarkScene;
471
+ target: AnnotationTargetInfo;
472
+ meaning?: AnnotationMeaning;
473
+ config: unknown;
474
+ }
475
+ /**
476
+ * A facade over one weasel scene per target: the scenes are the truth, this
477
+ * answers questions about them. Everything crossing this boundary is in
478
+ * fractions of a target's content box; the scenes hold world units.
479
+ *
480
+ * A scene per target rather than one scene with a `target` field, because a
481
+ * pane's hit-test, marquee and paint all walk the whole scene it is given and
482
+ * take no filter — one shared scene puts every other pane's marks under the
483
+ * pointer.
484
+ */
485
+ declare function createAnnotationStore(opts: AnnotationStoreOptions): AnnotationsApi;
486
+ /** Rebuild a store from what `toJSON` wrote. Unknown or future versions give
487
+ * an empty store rather than throwing: a lab that cannot read its marks
488
+ * should still open. */
489
+ declare function annotationsFromJSON(raw: unknown, targets: () => readonly AnnotationTargetInfo[], rest?: Omit<AnnotationStoreOptions, 'targets' | 'restore'>): AnnotationsApi;
490
+
491
+ /** A point in fractions of a target's content box. */
492
+ interface FracPoint {
493
+ x: number;
494
+ y: number;
495
+ }
496
+ /** A rectangle in fractions of a target's content box.
497
+ *
498
+ * Fractions rather than pixels because a mark must survive a change of render
499
+ * resolution: the picture gets bigger, the mark stays on the same feature. */
500
+ interface FracRect {
501
+ x: number;
502
+ y: number;
503
+ w: number;
504
+ h: number;
505
+ }
506
+ /** What shape a mark is. The kinds map onto weasel's own tools; an arrow is a
507
+ * line carrying an end marker, not a separate geometry. */
508
+ type AnnotationKind = 'stroke' | 'line' | 'arrow' | 'rect' | 'ellipse' | 'text';
509
+ /** One selectable status in an instrument's meaning tier. */
510
+ interface AnnotationStatus {
511
+ id: string;
512
+ label: string;
513
+ /** What a mark in this status is drawn in. Omitted, it takes the default
514
+ * mark colour — a status is allowed to be a label and nothing more. */
515
+ color?: string;
516
+ }
517
+ /** The optional meaning tier: what a mark means, as opposed to where it is.
518
+ * An instrument declaring it gets labkit's own chrome for the vocabulary;
519
+ * one that omits it keeps `meta` and owns meaning entirely. */
520
+ interface AnnotationMeaning {
521
+ statuses?: readonly AnnotationStatus[];
522
+ }
523
+ /** What labkit keeps in a mark's scene-node data. */
524
+ interface AnnotationData {
525
+ target: string;
526
+ kind: AnnotationKind;
527
+ /** Vertices for the kinds a bounding box cannot describe — a line's two
528
+ * ends, a stroke's path. In fractions, like the bounds. */
529
+ points?: readonly FracPoint[];
530
+ title?: string;
531
+ status?: string;
532
+ tags?: readonly string[];
533
+ meta?: unknown;
534
+ /** The target's `positionDependsOn` values when the mark was made. Compared
535
+ * against the live config to answer whether the mark still describes the
536
+ * same picture. */
537
+ seen?: Readonly<Record<string, unknown>>;
538
+ }
539
+ /** A mark, as the store reports it. `id` is `<target>/<node>`: one scene per
540
+ * target means a node id is only unique within one. */
541
+ interface Annotation extends AnnotationData {
542
+ id: string;
543
+ /** Bounds in fractions of the target's content box. */
544
+ frac: FracRect;
545
+ }
546
+ /** A new mark, before the store gives it an id and dates it. */
547
+ interface AnnotationInit {
548
+ target: string;
549
+ kind: AnnotationKind;
550
+ frac: FracRect;
551
+ points?: readonly FracPoint[];
552
+ title?: string;
553
+ status?: string;
554
+ tags?: readonly string[];
555
+ meta?: unknown;
556
+ }
557
+ /** Filters, ANDed. A mark matches `tags` when it carries every tag listed. */
558
+ interface AnnotationQuery {
559
+ target?: string;
560
+ kind?: AnnotationKind;
561
+ status?: string;
562
+ tags?: readonly string[];
563
+ where?: (a: Annotation) => boolean;
564
+ }
565
+ /** What a mark's meaning can be patched to. Geometry moves through `frac` /
566
+ * `points`; everything else is the meaning tier. */
567
+ type AnnotationPatch = Partial<Pick<Annotation, 'frac' | 'points' | 'title' | 'status' | 'tags' | 'meta'>>;
568
+ /** A target's own picture, handed over for an export to draw marks on top of.
569
+ * labkit cannot rasterize it: it is the consumer's DOM. `svg` is the one that
570
+ * keeps the export vector all the way through. */
571
+ type CaptureSource = {
572
+ kind: 'svg';
573
+ markup: string;
574
+ } | {
575
+ kind: 'image';
576
+ src: string;
577
+ } | {
578
+ kind: 'canvas';
579
+ canvas: HTMLCanvasElement;
580
+ };
581
+ /** What an export produces, and at what resolution. */
582
+ interface CaptureOptions {
583
+ /** `png` rasterizes; `svg` stays vector, which needs the base to be one too
584
+ * — a raster base embeds as an `<image>`. Default `png`. */
585
+ format?: 'png' | 'svg';
586
+ /** Output pixels per unit of the target's content box. Default 2. */
587
+ scale?: number;
588
+ }
589
+ /** A finished export. `width`/`height` are the output's, not the content
590
+ * box's — `content × scale`. */
591
+ interface CaptureResult {
592
+ target: string;
593
+ blob: Blob;
594
+ format: 'png' | 'svg';
595
+ width: number;
596
+ height: number;
597
+ }
598
+ /** What the store needs to know about a target: enough to convert a position
599
+ * and to date a mark. `AnnotationTarget` adds what only the overlay reads. */
600
+ interface AnnotationTargetInfo {
601
+ id: string;
602
+ /** Intrinsic content size in CSS pixels at zoom 1 — the box fractions are
603
+ * fractions *of*, and the target's world. */
604
+ content: {
605
+ w: number;
606
+ h: number;
607
+ };
608
+ /** Config keys whose change means a stored position no longer refers to the
609
+ * same picture. labkit snapshots and compares them without knowing what any
610
+ * of them mean. */
611
+ positionDependsOn?: readonly string[];
612
+ /** The target's own picture, for an export to draw marks over. A target
613
+ * declaring none exports its marks on transparency, which fails visibly
614
+ * rather than producing a blank brick.
615
+ *
616
+ * Here rather than on `AnnotationTarget` because the store is what calls
617
+ * it: `ref` and `view` are the React-shaped half only the overlay reads. */
618
+ base?: () => CaptureSource | Promise<CaptureSource>;
619
+ }
620
+ /** One region of an instrument that accepts marks. */
621
+ interface AnnotationTarget extends AnnotationTargetInfo {
622
+ /** The element the overlay tracks and takes input from. */
623
+ ref: RefObject<HTMLElement | null>;
624
+ /** The pane's camera, mirrored so marks pan and zoom with what they mark. */
625
+ view?: ViewTransform;
626
+ }
627
+ /** Where an instrument keeps its own marks. Declaring this means labkit never
628
+ * writes its trial slot — for an instrument whose marks belong in a format it
629
+ * already owns. Both halves are called outside React; `save` is already
630
+ * debounced by the time it arrives. */
631
+ interface AnnotationStorage {
632
+ load: () => SerializedAnnotations | null | undefined;
633
+ save: (doc: SerializedAnnotations) => void;
634
+ }
635
+ /** Declares that an instrument accepts marks: which regions take them,
636
+ * optionally what a mark is allowed to mean, and optionally where they live. */
637
+ interface AnnotationsCapability<TS = unknown, TC = unknown> {
638
+ targets: (state: TS, config: TC) => readonly AnnotationTarget[];
639
+ meaning?: AnnotationMeaning;
640
+ storage?: AnnotationStorage;
641
+ /** Fires after every finished export, labkit's own chrome included. A
642
+ * notification, not an interception: a host wanting its own flow calls
643
+ * `capture()` from its own UI, which is the surface the chrome uses. */
644
+ onCapture?: (result: CaptureResult) => void;
645
+ }
646
+ /** A persisted mark set. Versioned by this arc rather than by labkit's
647
+ * document migrations, which only ever reach top-level document sections and
648
+ * never into a trial's state. */
649
+ interface SerializedAnnotations {
650
+ version: 1;
651
+ /** One serialized scene per target that has ever held a mark. */
652
+ scenes: Record<string, unknown>;
653
+ }
654
+ /** Everything a host can ask or tell labkit about the marks on its targets. */
655
+ interface AnnotationsApi {
656
+ /** The scene a target's marks live in, created on first ask. One per target
657
+ * because a pane's hit-test, marquee and paint walk the whole scene they
658
+ * are given: a shared one would put a neighbour's marks under the pointer. */
659
+ sceneFor(target: string): MarkScene;
660
+ /** The targets the instrument declares, in declaration order. Chrome needs
661
+ * the list and cannot get it from the capability, which wants instrument
662
+ * state the chrome context does not carry. */
663
+ targets(): readonly AnnotationTargetInfo[];
664
+ get(id: string): Annotation | undefined;
665
+ /** Every mark matching the filters, in scene order. Omit `q` for all. */
666
+ query(q?: AnnotationQuery): Annotation[];
667
+ /** Marks whose bounds contain `pt`, topmost first. `tol` widens the hit in
668
+ * fractions, so a hairline mark stays reachable. */
669
+ hitTest(target: string, pt: FracPoint, tol?: number): Annotation[];
670
+ /** Marks wholly inside `box` — a marquee, not a brush. */
671
+ within(target: string, box: FracRect): Annotation[];
672
+ /** Whether `a`'s position still describes the picture `config` produces. */
673
+ isStale(a: Annotation, config: unknown): boolean;
674
+ /** Fires after every mutation. No delta: the scene does not diff, so
675
+ * re-query rather than expecting a change payload. */
676
+ subscribe(fn: () => void): () => void;
677
+ /** Whether the last mark change on any target can be taken back. Weasel
678
+ * history is the authority; this only decides *which* target's. */
679
+ canUndo(): boolean;
680
+ canRedo(): boolean;
681
+ /** Take back the most recent mark change, wherever it was made. */
682
+ undo(): boolean;
683
+ redo(): boolean;
684
+ /** Export a target's picture with its marks on it. Rejects on an id the
685
+ * instrument does not declare. */
686
+ capture(target: string, opts?: CaptureOptions): Promise<CaptureResult>;
687
+ add(init: AnnotationInit, config?: unknown): string;
688
+ update(id: string, patch: AnnotationPatch): void;
689
+ setMeta(id: string, meta: unknown): void;
690
+ remove(id: string): void;
691
+ /** A JSON-safe snapshot for `record.state`. */
692
+ toJSON(): SerializedAnnotations;
693
+ }
694
+
695
+ /** A mark's box in its target's world: CSS pixels at zoom 1. Matches weasel's
696
+ * default `RectPose`, which is what the scene stores. */
697
+ interface WorldRect {
698
+ x: number;
699
+ y: number;
700
+ width: number;
701
+ height: number;
702
+ }
703
+ /** Where a fraction lands on a content box of this size. */
704
+ declare function fracToWorld(f: FracRect, content: {
705
+ w: number;
706
+ h: number;
707
+ }): WorldRect;
708
+ /** What fraction of the content box a world rect covers.
709
+ *
710
+ * A pane measured before layout is 0×0, and dividing by that would seed every
711
+ * stored position with `NaN` — which compares false against everything and so
712
+ * fails silently rather than loudly. Zero sides give zeros. */
713
+ declare function worldToFrac(r: WorldRect, content: {
714
+ w: number;
715
+ h: number;
716
+ }): FracRect;
717
+ /** Snap to 4dp. Positions are diffed by humans in stored documents, so the
718
+ * last digits of a float are noise that hides the change that matters. */
719
+ declare function roundFrac(f: FracRect): FracRect;
720
+ /** Whether `pt` falls in `box`, widened by `tol` on every side so a hairline
721
+ * mark stays reachable. */
722
+ declare function fracContains(box: FracRect, pt: FracPoint, tol?: number): boolean;
723
+ /** Whether `outer` wholly encloses `inner`. A marquee takes what it encloses,
724
+ * not what it grazes — brushing selection is a different gesture. */
725
+ declare function fracIntersects(outer: FracRect, inner: FracRect): boolean;
726
+
727
+ export { LOUPE_DEFAULTS as L, annotationsFromJSON as a3, applyCamera as a4, createAnnotationScene as a5, createAnnotationStore as a6, fracContains as a7, fracIntersects as a8, fracToWorld as a9, resolveFrame as aa, resolveLoupe as ab, roundFrac as ac, worldToFrac as ad, DEFAULT_FRAME as x };
728
+ export type { TrialRegion as $, AnnotationData as A, LayerCapability as B, CaptureSource as C, DragFeedback as D, LayerDescriptor as E, FracPoint as F, LoupeCapability as G, HitResult as H, Instrument as I, LoupeDeclaration as J, LoupeRenderArgs as K, MarkScene as M, ResolvedLoupe as N, SidebarSection as O, PaletteItem as P, StatusReadout as Q, RenderContext as R, SerializedAnnotations as S, TrialTool as T, SystemEvent as U, ViewTransform as V, WorldFrame as W, ToolCapability as X, ToolItem as Y, ToolbarItem as Z, TrialChromeContext as _, Point as a, UndoCapability as a0, ViewportControl as a1, ViewportSize as a2, LoupeMode as ae, DragDropCapability as b, WorldSpec as c, WorldRect as d, AnnotationMeaning as e, CaptureOptions as f, CaptureResult as g, AnnotationTarget as h, AnnotationsApi as i, AnnotationsCapability as j, AnnotationKind as k, InstrumentList as l, TrialContribution as m, Annotation as n, AnnotationInit as o, AnnotationPatch as p, AnnotationQuery as q, AnnotationStatus as r, AnnotationStoreOptions as s, AnnotationTargetInfo as t, CanvasCapability as u, CanvasLayer as v, CaptureDeps as w, FracRect as y, IconComponent as z };