@weasel-js/labkit 1.1.0 → 1.3.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 (226) hide show
  1. package/README.md +27 -0
  2. package/dist/_dts/{DrawCommand-BkZztJsW.d.ts → DrawCommand-DBB45NfN.d.ts} +244 -58
  3. package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
  4. package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
  5. package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
  6. package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
  7. package/dist/_dts/types-DJ79Tg5J.d.ts +56 -0
  8. package/dist/_dts/{useTrialState-DYe2vUwN.d.ts → useTrialState-CsGMjhu9.d.ts} +6 -92
  9. package/dist/canvas/index.d.ts +8 -3
  10. package/dist/canvas/index.js +2 -1
  11. package/dist/chrome/index.d.ts +86 -0
  12. package/dist/chrome/index.js +8 -0
  13. package/dist/chrome/index.js.map +1 -0
  14. package/dist/{chunk-3TYUJR7Z.js → chunk-4TMMVIDM.js} +9 -3
  15. package/dist/chunk-4TMMVIDM.js.map +1 -0
  16. package/dist/chunk-HFDVGF4X.js +170 -0
  17. package/dist/chunk-HFDVGF4X.js.map +1 -0
  18. package/dist/chunk-LN6JDUGB.js +106 -0
  19. package/dist/chunk-LN6JDUGB.js.map +1 -0
  20. package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
  21. package/dist/chunk-NS54R52R.js.map +1 -0
  22. package/dist/{chunk-NRKWVTVT.js → chunk-O2BULFHX.js} +5 -14
  23. package/dist/chunk-O2BULFHX.js.map +1 -0
  24. package/dist/chunk-O7NVSCLN.js +266 -0
  25. package/dist/chunk-O7NVSCLN.js.map +1 -0
  26. package/dist/chunk-PO6L3NSH.js +366 -0
  27. package/dist/chunk-PO6L3NSH.js.map +1 -0
  28. package/dist/chunk-PQJ5B2U2.js +27 -0
  29. package/dist/chunk-PQJ5B2U2.js.map +1 -0
  30. package/dist/chunk-RVNN4CHQ.js +212 -0
  31. package/dist/chunk-RVNN4CHQ.js.map +1 -0
  32. package/dist/chunk-TLGRYALP.js +6151 -0
  33. package/dist/chunk-TLGRYALP.js.map +1 -0
  34. package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
  35. package/dist/chunk-V7PRSIRQ.js.map +1 -0
  36. package/dist/{chunk-DJLDIRFN.js → chunk-Z6HJ5FEM.js} +9626 -6050
  37. package/dist/chunk-Z6HJ5FEM.js.map +1 -0
  38. package/dist/chunk-ZZAYVX4X.js +505 -0
  39. package/dist/chunk-ZZAYVX4X.js.map +1 -0
  40. package/dist/controls/index.d.ts +19 -5
  41. package/dist/controls/index.js +3 -1
  42. package/dist/dragdrop/index.d.ts +5 -2
  43. package/dist/index.d.ts +307 -107
  44. package/dist/index.js +797 -540
  45. package/dist/index.js.map +1 -1
  46. package/dist/job/index.d.ts +13 -0
  47. package/dist/job/index.js +3 -0
  48. package/dist/job/index.js.map +1 -0
  49. package/dist/layers/index.d.ts +6 -3
  50. package/dist/layers/index.js +2 -1
  51. package/dist/passthrough/weasel-canvas.d.ts +69 -13
  52. package/dist/passthrough/weasel-canvas.js +1 -1
  53. package/dist/passthrough/weasel-ui.d.ts +3804 -3661
  54. package/dist/passthrough/weasel-ui.js +2 -2
  55. package/dist/primitives/index.d.ts +102 -8
  56. package/dist/primitives/index.js +4 -1
  57. package/dist/state/index.d.ts +3 -2
  58. package/dist/state/index.js +2 -2
  59. package/dist/styles.css +543 -144
  60. package/dist/surface/index.d.ts +77 -0
  61. package/dist/surface/index.js +4 -0
  62. package/dist/surface/index.js.map +1 -0
  63. package/dist/ui/layers/index.js +4 -3
  64. package/dist/undo/index.d.ts +5 -2
  65. package/package.json +24 -7
  66. package/src/canvas/AGENTS.md +12 -0
  67. package/src/canvas/CanvasStack.tsx +6 -2
  68. package/src/canvas/useLayerScheduler.ts +22 -15
  69. package/src/canvas/useOrbit.test.ts +71 -0
  70. package/src/canvas/useOrbit.ts +149 -0
  71. package/src/canvas/usePanZoom.test.ts +80 -0
  72. package/src/canvas/usePanZoom.ts +16 -2
  73. package/src/chrome/ChromeRegions.stories.tsx +106 -0
  74. package/src/chrome/builtins.test.ts +153 -0
  75. package/src/chrome/builtins.tsx +205 -0
  76. package/src/chrome/index.ts +25 -0
  77. package/src/chrome/merge.test.ts +51 -0
  78. package/src/chrome/merge.ts +52 -0
  79. package/src/chrome/regions/PaletteRegion.less +14 -0
  80. package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
  81. package/src/chrome/regions/PaletteRegion.tsx +41 -0
  82. package/src/chrome/regions/SidebarRegion.tsx +56 -0
  83. package/src/chrome/regions/StatusRegion.tsx +31 -0
  84. package/src/chrome/regions/TitleBarRegion.tsx +41 -0
  85. package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
  86. package/src/chrome/regions/ToolbarRegion.tsx +71 -0
  87. package/src/chrome/regions/ViewportRegion.less +42 -0
  88. package/src/chrome/regions/ViewportRegion.tsx +37 -0
  89. package/src/chrome/regions/regions.test.tsx +81 -0
  90. package/src/chrome/types.ts +127 -0
  91. package/src/config/builder.test.ts +87 -0
  92. package/src/config/builder.ts +224 -0
  93. package/src/config/fromConfigField.test.ts +93 -0
  94. package/src/config/fromConfigField.ts +74 -0
  95. package/src/config/index.ts +22 -0
  96. package/src/config/resolve.test.ts +120 -0
  97. package/src/config/resolve.ts +67 -0
  98. package/src/config/rules.ts +68 -0
  99. package/src/config/types.ts +108 -0
  100. package/src/config/useConfigSchema.ts +30 -0
  101. package/src/config/visible.ts +20 -0
  102. package/src/controls/ControlPanel.less +4 -58
  103. package/src/controls/ControlPanel.stories.tsx +86 -0
  104. package/src/controls/ControlPanel.test.tsx +223 -10
  105. package/src/controls/ControlPanel.tsx +202 -203
  106. package/src/dragdrop/DragDropRuntime.tsx +36 -18
  107. package/src/dragdrop/DragGhost.less +1 -1
  108. package/src/dragdrop/Palette.less +1 -1
  109. package/src/index.test.ts +18 -0
  110. package/src/index.ts +38 -3
  111. package/src/instrument/SineWave.smoke.test.tsx +9 -1
  112. package/src/instrument/defineInstrument.test.ts +38 -1
  113. package/src/instrument/defineInstrument.ts +31 -4
  114. package/src/instrument/index.ts +1 -0
  115. package/src/instrument/types.ts +38 -3
  116. package/src/instrument/validateConfigSchema.test.ts +13 -3
  117. package/src/instrument/validateConfigSchema.ts +7 -10
  118. package/src/job/index.ts +3 -0
  119. package/src/job/types.ts +47 -0
  120. package/src/job/useJob.test.tsx +210 -0
  121. package/src/job/useJob.ts +134 -0
  122. package/src/lab/Lab.chrome.test.tsx +65 -0
  123. package/src/lab/Lab.stories.tsx +1 -2
  124. package/src/lab/Lab.tsx +58 -15
  125. package/src/lab/LabContext.ts +6 -0
  126. package/src/lab/LabFullChrome.stories.tsx +308 -0
  127. package/src/lab/LabHeader.test.tsx +42 -0
  128. package/src/lab/LabHeader.tsx +57 -0
  129. package/src/lab/LabPalette.test.tsx +35 -0
  130. package/src/lab/LabPalette.tsx +34 -0
  131. package/src/lab/LabShell.less +50 -3
  132. package/src/lab/Workspace.less +1 -30
  133. package/src/lab/Workspace.surface.test.tsx +49 -0
  134. package/src/lab/Workspace.test.tsx +20 -10
  135. package/src/lab/Workspace.tsx +19 -9
  136. package/src/lab/index.ts +2 -0
  137. package/src/layers/AGENTS.md +1 -1
  138. package/src/layers/LayerList.less +31 -7
  139. package/src/layers/LayerList.tsx +17 -4
  140. package/src/passthrough/weasel-ui.ts +23 -0
  141. package/src/primitives/DragHandleGlyph.tsx +29 -0
  142. package/src/primitives/FloatingPanel.less +28 -0
  143. package/src/primitives/FloatingPanel.stories.tsx +46 -0
  144. package/src/primitives/FloatingPanel.test.tsx +240 -0
  145. package/src/primitives/FloatingPanel.tsx +190 -0
  146. package/src/primitives/FpsMeter.test.tsx +44 -0
  147. package/src/primitives/FpsMeter.tsx +20 -9
  148. package/src/primitives/JobProgress.less +69 -0
  149. package/src/primitives/JobProgress.stories.tsx +48 -0
  150. package/src/primitives/JobProgress.tsx +65 -0
  151. package/src/primitives/Legend.less +47 -0
  152. package/src/primitives/Legend.stories.tsx +32 -0
  153. package/src/primitives/Legend.test.tsx +57 -0
  154. package/src/primitives/Legend.tsx +40 -0
  155. package/src/primitives/Sidebar.less +32 -2
  156. package/src/primitives/StatusBar.less +7 -0
  157. package/src/primitives/StatusBar.tsx +10 -3
  158. package/src/primitives/Toolbar.less +50 -3
  159. package/src/primitives/Toolbar.test.tsx +67 -1
  160. package/src/primitives/Toolbar.tsx +72 -6
  161. package/src/primitives/ZoomControl.less +31 -0
  162. package/src/primitives/ZoomControl.tsx +76 -0
  163. package/src/primitives/floating.entry.test.tsx +14 -0
  164. package/src/primitives/index.ts +10 -2
  165. package/src/primitives/useRovingTabIndex.test.ts +27 -0
  166. package/src/primitives/useRovingTabIndex.ts +79 -0
  167. package/src/state/store.ts +16 -2
  168. package/src/state/toolSlot.test.ts +36 -0
  169. package/src/state/types.ts +8 -2
  170. package/src/state/view.test.ts +127 -0
  171. package/src/state/view.ts +18 -0
  172. package/src/styles.less +6 -0
  173. package/src/surface/AGENTS.md +64 -0
  174. package/src/surface/SurfaceContext.ts +5 -0
  175. package/src/surface/composeRects.test.ts +50 -0
  176. package/src/surface/composeRects.ts +19 -0
  177. package/src/surface/deviceRect.test.ts +40 -0
  178. package/src/surface/deviceRect.ts +19 -0
  179. package/src/surface/index.ts +7 -0
  180. package/src/surface/rect.ts +16 -0
  181. package/src/surface/useSurfaceTile.test.tsx +67 -0
  182. package/src/surface/useSurfaceTile.ts +32 -0
  183. package/src/surface/useTiledSurface.test.tsx +231 -0
  184. package/src/surface/useTiledSurface.ts +161 -0
  185. package/src/test-setup.ts +26 -0
  186. package/src/theme/base.less +19 -2
  187. package/src/theme/interstellar.tokens.json +6 -2
  188. package/src/tools/types.ts +24 -0
  189. package/src/trial/Trial.config.test.tsx +113 -0
  190. package/src/trial/Trial.job.test.tsx +73 -0
  191. package/src/trial/Trial.less +121 -4
  192. package/src/trial/Trial.stories.tsx +9 -1
  193. package/src/trial/Trial.test.tsx +67 -96
  194. package/src/trial/Trial.tsx +112 -45
  195. package/src/trial/TrialChrome.tsx +110 -64
  196. package/src/trial/TrialDragContext.ts +10 -0
  197. package/src/trial/TrialTitleBar.tsx +41 -0
  198. package/src/trial/index.ts +1 -15
  199. package/src/trial/trialChrome.borders.test.ts +38 -0
  200. package/src/ui/layers/LayerStack.less +9 -9
  201. package/src/ui/layers/LayerStack.tsx +1 -13
  202. package/src/ui/properties/CurveField.less +2 -2
  203. package/src/ui/properties/CurveField.test.tsx +11 -11
  204. package/src/ui/properties/PropertyGroup.less +2 -2
  205. package/src/ui/properties/PropertyPanel.less +55 -41
  206. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
  207. package/src/ui/properties/storyLayouts.tsx +2 -2
  208. package/dist/_dts/index-iAP6XbH3.d.ts +0 -132
  209. package/dist/_dts/types-x92Kfeme.d.ts +0 -62
  210. package/dist/chunk-3TYUJR7Z.js.map +0 -1
  211. package/dist/chunk-574LJAV4.js.map +0 -1
  212. package/dist/chunk-5R2ATYPJ.js +0 -201
  213. package/dist/chunk-5R2ATYPJ.js.map +0 -1
  214. package/dist/chunk-C6GJKPUI.js +0 -5986
  215. package/dist/chunk-C6GJKPUI.js.map +0 -1
  216. package/dist/chunk-DJLDIRFN.js.map +0 -1
  217. package/dist/chunk-NRKWVTVT.js.map +0 -1
  218. package/dist/chunk-PMAU3SEE.js +0 -126
  219. package/dist/chunk-PMAU3SEE.js.map +0 -1
  220. package/dist/chunk-SFL7NFKN.js.map +0 -1
  221. package/src/instrument/capabilityDetector.test.ts +0 -64
  222. package/src/instrument/capabilityDetector.ts +0 -20
  223. package/src/trial/DefaultSidebar.tsx +0 -29
  224. package/src/trial/DefaultStatusBar.tsx +0 -19
  225. package/src/trial/DefaultToolbar.tsx +0 -92
  226. package/src/trial/slotTypes.ts +0 -53
package/dist/index.d.ts CHANGED
@@ -1,34 +1,250 @@
1
1
  export { CanvasLayerDescriptor, CanvasStack, CanvasStackContext, CanvasStackContextValue, CanvasStackProps, screenToWorld, worldToScreen } from './canvas/index.js';
2
+ import * as react from 'react';
3
+ import { WheelEvent, PointerEvent, ReactNode } from 'react';
4
+ export { PaletteRegion, PaletteRegionProps, SidebarRegion, SidebarRegionProps, StatusRegion, StatusRegionProps, TitleBarRegion, TitleBarRegionProps, ToolbarRegion, ToolbarRegionProps, ViewportRegion, ViewportRegionProps, builtinContributions, mergeContributions, suppressContributions } from './chrome/index.js';
5
+ import { I as Instrument, a as InstrumentList, T as TrialContribution, b as TrialTool, V as ViewTransform } from './_dts/index-DgcNdEdh.js';
6
+ export { C as CanvasCapability, c as CanvasLayer, D as DragDropCapability, d as DragFeedback, H as HitResult, e as IconComponent, L as LayerCapability, f as LayerDescriptor, P as PaletteItem, g as Point, R as RenderContext, S as SidebarSection, h as StatusReadout, i as SystemEvent, j as ToolCapability, k as ToolItem, l as ToolbarItem, m as TrialChromeContext, n as TrialRegion, U as UndoCapability, o as ViewportControl } from './_dts/index-DgcNdEdh.js';
7
+ import { T as ToolPrefLeaf } from './_dts/PrefsForm-BYa6cWnO.js';
8
+ import { C as ConfigNode, A as Annotations, N as NodeOptions, a as ControlRenderer, b as ConfigOption, c as ConfigSchema, I as InferConfig, d as ConfigField, R as ResolvedConfig, e as ConfigRule, L as LeafPatch, f as ConfigRuleContext } from './_dts/types-AHlQxBNN.js';
9
+ export { g as CheckboxField, h as ColorField, i as ConfigFieldBase, j as ConfigFieldType, k as ConfigOf, l as NodeValue, m as NumberField, S as SectionSpec, n as SelectField, o as SelectOption, p as SliderField, T as TextField } from './_dts/types-AHlQxBNN.js';
2
10
  export { ControlPanel } from './controls/index.js';
3
- import { C as ConfigField } from './_dts/types-x92Kfeme.js';
4
- export { a as CheckboxField, b as ColorField, c as ConfigFieldBase, d as ConfigFieldType, N as NumberField, S as SelectField, e as SelectOption, f as SliderField, T as TextField } from './_dts/types-x92Kfeme.js';
5
- import { I as Instrument, a as InstrumentList } from './_dts/index-iAP6XbH3.js';
6
- export { C as CanvasCapability, b as CanvasLayer, D as DragDropCapability, c as DragFeedback, H as HitResult, L as LayerCapability, d as LayerDescriptor, P as PaletteItem, e as Point, R as RenderContext, S as SystemEvent, U as UndoCapability, V as ViewTransform } from './_dts/index-iAP6XbH3.js';
11
+ import { J as JobHandle } from './_dts/types-DJ79Tg5J.js';
12
+ export { a as JobCapability, b as JobEvent, c as JobFailure, d as JobStatus } from './_dts/types-DJ79Tg5J.js';
13
+ export { UseJobOptions, useJob } from './job/index.js';
7
14
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
- import * as react from 'react';
9
- import { ReactNode } from 'react';
10
- import { a as StorageAdapter, w as LabMode, T as TrialRecord, h as SavedSnapshot } from './_dts/useTrialState-DYe2vUwN.js';
11
- export { C as CURRENT_DOCUMENT_VERSION, b as CreateLabStoreOptions, L as LabDocument, e as LabStoreContext, f as LabStoreProvider, g as LabStoreState, S as SerializedTrial, i as TrialIdContext, j as TrialIdProvider, k as TrialStateHandle, U as UndoStack, m as createMemoryAdapter, n as labDocumentKey, o as localStorageAdapter, p as noneAdapter, q as quarantineKey, s as sessionStorageAdapter, u as urlHashAdapter, r as useLabStore, t as useTrialId, v as useTrialState } from './_dts/useTrialState-DYe2vUwN.js';
15
+ import { a as StorageAdapter, e as LabMode, T as TrialRecord, c as SavedSnapshot } from './_dts/types-1Sdxy_Pv.js';
16
+ export { C as CreateLabStoreOptions, L as LabDocument, b as LabStoreState, S as SerializedTrial, d as TrialStateHandle, U as UndoStack } from './_dts/types-1Sdxy_Pv.js';
12
17
  export { LayerList, LayerListProps } from './layers/index.js';
13
- export { FpsMeter, ScaleIndicator, ScaleIndicatorProps, Sidebar, SidebarProps, StatusBar, StatusBarProps, Toolbar, ToolbarProps } from './primitives/index.js';
18
+ export { FloatingPanel, FloatingPanelProps, FpsMeter, JobProgress, JobProgressProps, Legend, LegendEntry, LegendMark, LegendProps, ScaleIndicator, ScaleIndicatorProps, Sidebar, SidebarProps, StatusBar, StatusBarProps, StatusBarSectionProps, Toolbar, ToolbarButtonProps, ToolbarGroupProps, ToolbarProps, ZoomControl, ZoomControlProps } from './primitives/index.js';
19
+ export { C as CURRENT_DOCUMENT_VERSION, b as LabStoreContext, c as LabStoreProvider, T as TrialIdContext, d as TrialIdProvider, f as createMemoryAdapter, l as labDocumentKey, g as localStorageAdapter, n as noneAdapter, q as quarantineKey, s as sessionStorageAdapter, u as urlHashAdapter, h as useLabStore, i as useTrialId, j as useTrialState } from './_dts/useTrialState-CsGMjhu9.js';
20
+ export { Box, Rect, SurfaceContext, SurfaceFrame, SurfaceHandle, UseTiledSurfaceOptions, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceOptional, useSurfaceTile, useTiledSurface } from './surface/index.js';
14
21
  export { LayerStack, LayerStackItem, LayerStackProps } from './ui/layers/index.js';
15
22
  export { EventBus, EventListener, clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './undo/index.js';
23
+ import 'windease';
16
24
  import 'zustand/vanilla';
17
25
 
18
- /** Which optional capabilities an instrument declared what the trial
19
- * consults to decide which chrome to show. */
20
- interface CapabilityFlags {
21
- hasCanvas: boolean;
22
- hasLayers: boolean;
23
- hasDragDrop: boolean;
24
- hasUndo: boolean;
25
- }
26
- /** Read an instrument's declared capabilities off its definition. */
27
- declare function detectCapabilities(instrument: Instrument<unknown, unknown>): CapabilityFlags;
26
+ /** A point in the space the instrument works in. */
27
+ interface Vec3 {
28
+ x: number;
29
+ y: number;
30
+ z: number;
31
+ }
32
+ /** An orbit camera as a trial view: where it looks from, and what it looks at.
33
+ * labkit does not turn this into a matrix — the host's renderer does. */
34
+ interface OrbitView {
35
+ yaw: number;
36
+ pitch: number;
37
+ distance: number;
38
+ target: Vec3;
39
+ }
40
+ /** Just short of the pole. At exactly ±PI/2 the azimuth is undefined and the
41
+ * camera rolls, which reads as the model jumping rather than as a limit. */
42
+ declare const PITCH_LIMIT: number;
43
+ declare function clampPitch(pitch: number): number;
44
+ declare function wrapYaw(yaw: number): number;
45
+ /** The view a drag of (dx, dy) from `start` produces. Absolute against the drag
46
+ * start, so it can be re-applied any number of times without compounding. */
47
+ declare function orbitAfterDrag(start: OrbitView, dx: number, dy: number): OrbitView;
48
+ /** Multiplicative, so one notch covers the same proportion of the distance
49
+ * whether the camera is near or far. */
50
+ declare function orbitAfterWheel(view: OrbitView, deltaY: number, minDistance: number, maxDistance: number): OrbitView;
51
+ interface UseOrbitOptions {
52
+ view: OrbitView;
53
+ onViewChange: (v: OrbitView) => void;
54
+ /** Restored on double-click. Defaults to the view the hook first saw. */
55
+ home?: OrbitView;
56
+ minDistance?: number;
57
+ maxDistance?: number;
58
+ }
59
+ interface OrbitHandlers {
60
+ onWheel: (e: WheelEvent<HTMLElement>) => void;
61
+ onPointerDown: (e: PointerEvent<HTMLElement>) => void;
62
+ onPointerMove: (e: PointerEvent<HTMLElement>) => void;
63
+ onPointerUp: (e: PointerEvent<HTMLElement>) => void;
64
+ onDoubleClick: () => void;
65
+ isDragging: () => boolean;
66
+ }
67
+ /** Pointer gestures over an orbit view: drag to turn, wheel or pinch to dolly,
68
+ * double-click to go home. The 3D peer of `usePanZoom`. */
69
+ declare function useOrbit({ view, onViewChange, home, minDistance, maxDistance, }: UseOrbitOptions): OrbitHandlers;
70
+
71
+ /** Shared chaining surface. Every method clones, so a node can be reused as a
72
+ * base for several leaves without one bleeding into the next. */
73
+ declare abstract class BaseNode<T> implements ConfigNode<T> {
74
+ readonly annotations: Readonly<Annotations>;
75
+ readonly options: Readonly<NodeOptions>;
76
+ abstract readonly kind: string | null;
77
+ readonly default: T;
78
+ constructor(defaultValue: T, annotations?: Readonly<Annotations>, options?: Readonly<NodeOptions>);
79
+ /** Clone into the same subclass. Subclasses with extra constructor
80
+ * arguments override this. */
81
+ protected with(annotations: Annotations, options: NodeOptions): this;
82
+ protected ann(patch: Annotations): this;
83
+ protected opt(patch: NodeOptions): this;
84
+ /** Human-readable label. Defaults to the key, title-cased. */
85
+ label(name: string): this;
86
+ /** Longer help text, surfaced as a tooltip on the row label. */
87
+ describe(description: string): this;
88
+ /** Omit from the panel unless it is asked to show hidden leaves. */
89
+ hidden(): this;
90
+ /** Pair with a sibling sharing this id, side-by-side on one row. */
91
+ pair(pair: string): this;
92
+ /** Render under a named section heading. */
93
+ section(section: string): this;
94
+ /** Show this row only while the predicate holds. Presentational — the value
95
+ * stays in config and the instrument still reads it. */
96
+ showIf(predicate: (config: Record<string, unknown>) => boolean): this;
97
+ /** Draw this one row yourself, keeping the kind, default and validation. */
98
+ render(renderer: ControlRenderer): this;
99
+ }
100
+ declare class NumberNode extends BaseNode<number> {
101
+ readonly kind = "number";
102
+ /** Bound the value. A number with both bounds renders as a slider. */
103
+ range(min: number, max: number): this;
104
+ step(step: number): this;
105
+ /** Force a slider even without both bounds. */
106
+ slider(): this;
107
+ /** Force a typed input even when both bounds are set. */
108
+ input(): this;
109
+ }
110
+ declare class BooleanNode extends BaseNode<boolean> {
111
+ readonly kind = "boolean";
112
+ /** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's
113
+ * `PrefsForm` honors the distinction. */
114
+ toggle(): this;
115
+ }
116
+ declare class StringNode extends BaseNode<string> {
117
+ readonly kind = "string";
118
+ placeholder(placeholder: string): this;
119
+ maxLength(maxLength: number): this;
120
+ /** Milliseconds to debounce live writes. 0 commits every keystroke. */
121
+ debounce(debounceMs: number): this;
122
+ }
123
+ declare class ColorNode extends BaseNode<string> {
124
+ readonly kind = "color";
125
+ }
126
+ declare class EnumNode<T extends string> extends BaseNode<T> {
127
+ readonly kind = "enum";
128
+ /** Render as a segmented control rather than a select. */
129
+ radio(): this;
130
+ /** Relabel the choices. Named `labels` rather than `options` because
131
+ * `options` is the node's own extras bag. */
132
+ labels(options: readonly ConfigOption[]): this;
133
+ }
134
+ /** A leaf whose kind the rule chain decides. */
135
+ declare class ValueNode<T> extends BaseNode<T> {
136
+ readonly kind: null;
137
+ }
138
+ /** A leaf of a kind labkit does not ship a row for. */
139
+ declare class CustomNode<T> extends BaseNode<T> {
140
+ readonly kind: string;
141
+ constructor(kind: string, defaultValue: T, annotations?: Readonly<Annotations>, options?: Readonly<NodeOptions>);
142
+ protected with(annotations: Annotations, options: NodeOptions): this;
143
+ }
144
+ /**
145
+ * Builds an instrument's config: the values, their types, and how each is
146
+ * edited, in one declaration.
147
+ *
148
+ * ```ts
149
+ * const config = f.schema({
150
+ * showGrid: f.boolean(true),
151
+ * cellSize: f.number(20).range(5, 80).step(5).label('Grid spacing'),
152
+ * })
153
+ * ```
154
+ */
155
+ declare const f: {
156
+ number: (def: number) => NumberNode;
157
+ boolean: (def: boolean) => BooleanNode;
158
+ string: (def: string) => StringNode;
159
+ color: (def: string) => ColorNode;
160
+ /** A fixed set of choices. The default's literal type flows into the config
161
+ * type, so `f.enum('fast', ['fast', 'accurate'])` gives
162
+ * `'fast' | 'accurate'` with no `as const`. */
163
+ enum: <const T extends string>(def: T, options: readonly T[] | readonly ConfigOption[]) => EnumNode<T>;
164
+ /** A leaf with no declared kind: the rule chain decides, which is how a lab
165
+ * states a convention like "every `*Color` key is a color picker". */
166
+ value: <T>(def: T) => ValueNode<T>;
167
+ /** A leaf of a kind a lab supplies the control for, through `controls`. */
168
+ custom: <T>(kind: string, def: T, validate?: (leaf: ToolPrefLeaf) => string[]) => CustomNode<T>;
169
+ /** Collect leaves into an instrument's config. */
170
+ schema<S extends Record<string, ConfigNode>>(nodes: S): ConfigSchema<InferConfig<S>>;
171
+ };
172
+
173
+ /**
174
+ * Adapt a legacy `configSchema(): ConfigField[]` into the resolved shape, so
175
+ * both instrument paths reach one renderer.
176
+ *
177
+ * Rules do not run here: a hand-written `ConfigField` already states its label
178
+ * and control, which is everything the rule chain would have inferred.
179
+ */
180
+ declare function fromConfigFields(fields: readonly ConfigField[]): ResolvedConfig;
181
+
182
+ /**
183
+ * Resolve a schema into the vocabulary weasel-ui renders, running each leaf
184
+ * through the consumer's rules and then labkit's own.
185
+ *
186
+ * The group is flat — every leaf is a direct child — so a leaf's path is its
187
+ * config key and both `ControlPanel` and `PrefsForm` address it identically.
188
+ */
189
+ declare function resolveConfigSchema<TC>(schema: ConfigSchema<TC>, rules?: readonly ConfigRule[]): ResolvedConfig;
190
+
191
+ /** `cellSize` -> `Cell size`. Splits camelCase without shattering acronyms. */
192
+ declare function titleCase(key: string): string;
193
+ /**
194
+ * labkit's own inference, expressed in the same vocabulary a consumer
195
+ * extends. Runs after the consumer's rules, so a lab always wins.
196
+ *
197
+ * If one of these ever needs something a `ConfigRule` cannot express, the
198
+ * seam is too weak — that is the point of writing them this way.
199
+ */
200
+ declare const builtinRules: readonly ConfigRule[];
201
+ /**
202
+ * Fold a rule chain into one leaf patch. Gap-filling: a property already
203
+ * settled is never overwritten, so the accumulator seeded with the author's
204
+ * annotations beats every rule, and earlier rules beat later ones.
205
+ */
206
+ declare function applyRules(seed: LeafPatch, ctx: Omit<ConfigRuleContext, 'leaf'>, rules: readonly ConfigRule[]): LeafPatch;
28
207
 
29
- /** Identity at runtime; exists so an instrument's state and config types are
30
- * inferred from the spec rather than having to be written out. */
31
- declare function defineInstrument<TS, TC>(spec: Instrument<TS, TC>): Instrument<TS, TC>;
208
+ /**
209
+ * An instrument's controls, resolved against the surrounding lab's rules.
210
+ *
211
+ * Resolution cannot happen in `defineInstrument` — rules are lab-scoped and
212
+ * that runs at module load — so it happens here, at render. Both instrument
213
+ * paths land in the same shape, so callers never branch on which was used.
214
+ */
215
+ declare function useConfigSchema(instrument: Instrument<any, any, any>): ResolvedConfig;
216
+
217
+ /**
218
+ * Whether a leaf's row should be drawn right now.
219
+ *
220
+ * `hidden` is the leaf's own static flag; `showIf` is the schema's predicate
221
+ * over the live config. Both are presentational — a hidden leaf keeps its
222
+ * value and the instrument still reads it.
223
+ */
224
+ declare function isLeafVisible(resolved: ResolvedConfig, path: string, config: Record<string, unknown>, showHidden?: boolean): boolean;
225
+
226
+ /** An instrument as it is written: either the legacy `defaultConfig` (plus an
227
+ * optional `configSchema`), or a `config` schema that supplies both. */
228
+ type InstrumentSpec<TS, TC, TItem = unknown> = Instrument<TS, TC, TItem> | (Omit<Instrument<TS, TC, TItem>, 'defaultConfig'> & {
229
+ config: ConfigSchema<TC>;
230
+ });
231
+ /**
232
+ * Normalizes an instrument spec, and infers its state and config types from
233
+ * the literal rather than making them be written out.
234
+ *
235
+ * A spec declaring `config` gets its `defaultConfig` synthesized from the
236
+ * schema, so nothing downstream branches on which way the config was
237
+ * declared. A spec written the legacy way is returned unchanged, by
238
+ * reference.
239
+ *
240
+ * Resolving the schema into controls does not happen here: rules are
241
+ * lab-scoped and this runs at module load. `useConfigSchema` does that.
242
+ *
243
+ * TypeScript infers all three type arguments or none, so a call that names
244
+ * `TS` and `TC` leaves `TItem` at `unknown` and a job's `onItem` needs a cast.
245
+ * Name the item type too, or pass no type arguments at all.
246
+ */
247
+ declare function defineInstrument<TS, TC, TItem = unknown>(spec: InstrumentSpec<TS, TC, TItem>): Instrument<TS, TC, TItem>;
32
248
 
33
249
  /** Whether a config schema is usable, and every problem found rather than
34
250
  * just the first. */
@@ -37,8 +253,12 @@ interface ValidationResult {
37
253
  errors: string[];
38
254
  }
39
255
  /** Check a config schema for the mistakes that would otherwise surface as a
40
- * silently broken control: empty or duplicate keys, unknown field types, and
41
- * fields whose own constraints do not hold. */
256
+ * silently broken control: empty or duplicate keys, and fields whose own
257
+ * constraints do not hold.
258
+ *
259
+ * An unrecognized type is not an error — a lab supplies controls for its own
260
+ * kinds through `ControlPanel`'s `renderers`, and this cannot see them. Such
261
+ * a field simply has no constraints to check here. */
42
262
  declare function validateConfigSchema(fields: ConfigField[]): ValidationResult;
43
263
 
44
264
  /** Props for `<Lab>`. */
@@ -56,11 +276,27 @@ interface LabProps {
56
276
  */
57
277
  nebula?: readonly string[];
58
278
  title?: string;
279
+ /** Rendered in the shell's footer, below the workspace. */
280
+ footer?: ReactNode;
281
+ /** Contributions added to every trial's chrome, after the instrument's own. */
282
+ chrome?: readonly TrialContribution[];
283
+ /** Built-in contribution ids to drop. Throws on an id that is not there. */
284
+ suppress?: readonly string[];
285
+ /** Tools offered lab-wide. A trial whose instrument declares none of its own
286
+ * reflects and writes this slot. */
287
+ tools?: readonly TrialTool[];
288
+ /** Rules run over every instrument's config leaves, before labkit's own
289
+ * inference — where a lab states a convention once instead of annotating
290
+ * each field. Memoize or hoist. */
291
+ configRules?: readonly ConfigRule[];
292
+ /** Control overrides and app-defined kinds for every trial's settings panel.
293
+ * Keys are config paths (checked first) or leaf kinds. Memoize or hoist. */
294
+ controls?: Record<string, ControlRenderer>;
59
295
  children?: ReactNode;
60
296
  }
61
297
  /** The lab runtime: creates the store, provides it, and renders one trial
62
298
  * per record in a grid. Each trial runs one of `instruments`. */
63
- declare function Lab({ instruments, defaultInstrument, storage, storageKey, mode, nebula, title, children, }: LabProps): react_jsx_runtime.JSX.Element;
299
+ declare function Lab({ instruments, defaultInstrument, storage, storageKey, mode, nebula, title, footer, chrome, suppress, tools, configRules, controls, children, }: LabProps): react_jsx_runtime.JSX.Element;
64
300
 
65
301
  /** Lab-wide state and commands: the available instruments, the open
66
302
  * trials and the operations over them, saved snapshots, and the color
@@ -79,12 +315,25 @@ interface LabContextValue {
79
315
  deleteSnapshot: (snapshotId: string) => void;
80
316
  mode: LabMode;
81
317
  setMode: (m: LabMode) => void;
318
+ /** Lab-wide rules run over every instrument's config leaves, before
319
+ * labkit's own inference. */
320
+ configRules?: readonly ConfigRule[];
321
+ /** Lab-wide control overrides, keyed by config path or by leaf kind. */
322
+ controls?: Record<string, ControlRenderer>;
82
323
  }
83
324
  /** Context carrying the surrounding lab. Prefer `useLabContext`. */
84
325
  declare const LabContext: react.Context<LabContextValue | null>;
85
326
  /** The surrounding lab. Throws outside a `<Lab>`. */
86
327
  declare function useLabContext(): LabContextValue;
87
328
 
329
+ /** Props for `<LabPalette>`. */
330
+ interface LabPaletteProps {
331
+ tools: readonly TrialTool[];
332
+ }
333
+ /** The lab's tool strip. Writes the lab's tool slot, which every trial whose
334
+ * instrument declares no tools of its own resolves to. */
335
+ declare function LabPalette({ tools }: LabPaletteProps): react_jsx_runtime.JSX.Element;
336
+
88
337
  /** Props for `<LabShell>`. */
89
338
  interface LabShellProps {
90
339
  title: string;
@@ -158,6 +407,14 @@ interface WorkspaceProps {
158
407
  */
159
408
  declare function Workspace({ children, ids, resizable, reorderable, onReorder, layout, onLayoutChange, gap, padding, viewport, }: WorkspaceProps): react_jsx_runtime.JSX.Element;
160
409
 
410
+ /** The 2D view labkit has always shipped, and what a trial gets when it names no
411
+ * other. Re-exported under its own name so a consumer can say which it means. */
412
+ type ViewTransform2D = ViewTransform;
413
+ declare const DEFAULT_VIEW: ViewTransform2D;
414
+ /** A trial's view is opaque to labkit, so anything that needs the 2D shape — the
415
+ * zoom chrome, `CanvasStack` — asks for it and handles not getting it. */
416
+ declare function as2DView(view: unknown): ViewTransform2D | null;
417
+
161
418
  type TokenValue = string | number | readonly (string | number)[];
162
419
  /**
163
420
  * A single design token as authored, before aliases are resolved: a DTCG-shaped
@@ -196,84 +453,18 @@ interface Theme {
196
453
  */
197
454
  declare const interstellarTheme: Theme;
198
455
 
199
- /** What a trial hands its toolbar: which instrument is running, the undo
200
- * state and commands, the zoom controls, and the snapshot commands. */
201
- interface TrialToolbarContext {
202
- trialId: string;
203
- instrumentName: string;
204
- hasUndo: boolean;
205
- canUndo: boolean;
206
- canRedo: boolean;
207
- undo: () => void;
208
- redo: () => void;
209
- zoom: number;
210
- setZoom: (z: number) => void;
211
- zoomIn: () => void;
212
- zoomOut: () => void;
213
- resetZoom: () => void;
214
- hasCanvas: boolean;
215
- savedSnapshots: SavedSnapshot[];
216
- saveSnapshot: (name?: string) => void;
217
- loadSnapshot: (snapshotId: string) => void;
218
- clone: () => void;
219
- reset: () => void;
220
- close: () => void;
221
- isLastTrial: boolean;
222
- }
223
- /** What a trial hands its sidebar: the instrument's config schema, its
224
- * current values, and the setter. */
225
- interface TrialSidebarContext {
226
- trialId: string;
227
- instrumentName: string;
228
- configFields: ConfigField[];
229
- config: unknown;
230
- setConfig: (key: string, value: unknown) => void;
231
- }
232
- /** What a trial hands its status bar. */
233
- interface TrialStatusBarContext {
234
- trialId: string;
235
- instrumentName: string;
236
- zoom: number;
237
- }
238
- /** Replaces a trial's toolbar. Receives everything the default one uses,
239
- * so a custom toolbar need not reach into the store. */
240
- type ToolbarSlot = (ctx: TrialToolbarContext) => ReactNode;
241
- /** Replaces a trial's sidebar. */
242
- type SidebarSlot = (ctx: TrialSidebarContext) => ReactNode;
243
- /** Replaces a trial's status bar. */
244
- type StatusBarSlot = (ctx: TrialStatusBarContext) => ReactNode;
245
-
246
- /** Props for `<DefaultSidebar>`. */
247
- interface DefaultSidebarProps {
248
- ctx: TrialSidebarContext;
249
- }
250
- /** The sidebar a trial renders when no `sidebar` slot is supplied: a
251
- * control panel over the instrument's config schema. */
252
- declare function DefaultSidebar({ ctx }: DefaultSidebarProps): react_jsx_runtime.JSX.Element;
253
-
254
- /** Props for `<DefaultStatusBar>`. */
255
- interface DefaultStatusBarProps {
256
- ctx: TrialStatusBarContext;
257
- }
258
- /** The status bar a trial renders when no `statusBar` slot is supplied:
259
- * the instrument's name and the current zoom. */
260
- declare function DefaultStatusBar({ ctx }: DefaultStatusBarProps): react_jsx_runtime.JSX.Element;
261
-
262
- /** Props for `<DefaultToolbar>`. */
263
- interface DefaultToolbarProps {
264
- ctx: TrialToolbarContext;
265
- }
266
- /** The toolbar a trial renders when no `toolbar` slot is supplied. Only
267
- * shows the controls the instrument's declared capabilities support. */
268
- declare function DefaultToolbar({ ctx }: DefaultToolbarProps): react_jsx_runtime.JSX.Element;
269
-
270
456
  /** Props for `<Trial>`. */
271
457
  interface TrialProps {
272
458
  id: string;
459
+ /** Contributions the lab adds to this trial's chrome, merged after the
460
+ * instrument's own. */
461
+ chrome?: readonly TrialContribution[];
462
+ /** Built-in contribution ids to drop. Throws on an id that is not there. */
463
+ suppress?: readonly string[];
273
464
  }
274
465
  /** Renders one trial from the lab store: looks up its record and
275
466
  * instrument, and mounts the instrument inside the trial chrome. */
276
- declare function Trial({ id }: TrialProps): react_jsx_runtime.JSX.Element;
467
+ declare function Trial({ id, chrome, suppress }: TrialProps): react_jsx_runtime.JSX.Element;
277
468
 
278
469
  interface UndoBindings {
279
470
  canUndo: boolean;
@@ -287,17 +478,26 @@ interface TrialChromeProps {
287
478
  record: TrialRecord;
288
479
  instrument: Instrument;
289
480
  isLastTrial: boolean;
290
- toolbar?: ToolbarSlot;
291
- sidebar?: SidebarSlot;
292
- statusBar?: StatusBarSlot;
293
481
  undoBindings?: UndoBindings;
294
- sidebarExtras?: ReactNode;
482
+ /** Supplied when the instrument declares a `job`. */
483
+ job?: JobHandle;
484
+ /** Contributions the trial runtime itself adds — the drag palette and the
485
+ * layer list, which depend on runtime state the instrument cannot reach. */
486
+ trialChrome?: readonly TrialContribution[];
487
+ /** Contributions from the lab, merged last. */
488
+ chrome?: readonly TrialContribution[];
489
+ /** Built-in contribution ids to drop. Throws on an id that is not there. */
490
+ suppress?: readonly string[];
491
+ /** The trial's resolved tool slot, and the setter that writes whichever
492
+ * slot it resolved to. `Trial` owns the resolution. */
493
+ activeToolId?: string | null;
494
+ setActiveTool?: (id: string) => void;
295
495
  children: ReactNode;
296
496
  }
297
- /** The frame around a running instrument toolbar, sidebar, status bar —
298
- * each replaceable by a slot. Assembles the slot contexts and wires the undo
299
- * keyboard shortcuts. */
300
- declare function TrialChrome({ trialId, record, instrument, isLastTrial, toolbar, sidebar, statusBar, undoBindings, sidebarExtras, children, }: TrialChromeProps): react_jsx_runtime.JSX.Element;
497
+ /** The frame around a running instrument. Builds one chrome context, assembles
498
+ * the contributions the instrument, the runtime and the lab declare, and hands
499
+ * each region its slice. */
500
+ declare function TrialChrome({ trialId, record, instrument, isLastTrial, undoBindings, job, trialChrome, chrome, suppress, activeToolId, setActiveTool, children, }: TrialChromeProps): react_jsx_runtime.JSX.Element;
301
501
 
302
502
  /** Append a new trial running `instrumentName`, at that instrument's
303
503
  * default config and initial state. */
@@ -595,5 +795,5 @@ interface ToggleRowProps<T extends string> {
595
795
  * visible at once. */
596
796
  declare function ToggleRow<T extends string>({ label, value, options, onChange, layout, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
597
797
 
598
- export { CheckboxRow, ColorRow, ConfigField, CurveField, DefaultSidebar, DefaultStatusBar, DefaultToolbar, EffectCard, EffectCardList, Instrument, InstrumentList, Lab, LabContext, LabMode, LabShell, NumberRow, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Trial, TrialChrome, TrialRecord, Workspace, addTrial, cloneTrial, closeTrial, defineInstrument, detectCapabilities, interstellarTheme, reorderTrials, resetTrial, useLabContext, validateConfigSchema };
599
- export type { CapabilityFlags, CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, DefaultSidebarProps, DefaultStatusBarProps, DefaultToolbarProps, EffectCardListItem, EffectCardListProps, EffectCardProps, LabContextValue, LabProps, LabShellProps, NumberRowProps, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SidebarSlot, SliderRowProps, StatusBarSlot, SubpanelProps, TextRowProps, ToggleRowProps, ToolbarSlot, TrialChromeProps, TrialProps, TrialSidebarContext, TrialStatusBarContext, TrialToolbarContext, ValidationResult, WorkspaceProps };
798
+ export { Annotations, CheckboxRow, ColorRow, ConfigField, ConfigNode, ConfigOption, ConfigRule, ConfigRuleContext, ConfigSchema, ControlRenderer, CurveField, DEFAULT_VIEW, EffectCard, EffectCardList, InferConfig, Instrument, InstrumentList, JobHandle, Lab, LabContext, LabMode, LabPalette, LabShell, LeafPatch, NodeOptions, NumberRow, PITCH_LIMIT, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, ResolvedConfig, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Trial, TrialChrome, TrialContribution, TrialRecord, TrialTool, ViewTransform, Workspace, addTrial, applyRules, as2DView, builtinRules, clampPitch, cloneTrial, closeTrial, defineInstrument, f, fromConfigFields, interstellarTheme, isLeafVisible, orbitAfterDrag, orbitAfterWheel, reorderTrials, resetTrial, resolveConfigSchema, titleCase, useConfigSchema, useLabContext, useOrbit, validateConfigSchema, wrapYaw };
799
+ export type { CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, EffectCardListItem, EffectCardListProps, EffectCardProps, InstrumentSpec, LabContextValue, LabPaletteProps, LabProps, LabShellProps, NumberRowProps, OrbitHandlers, OrbitView, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SliderRowProps, SubpanelProps, TextRowProps, ToggleRowProps, TrialChromeProps, TrialProps, UndoBindings, UseOrbitOptions, ValidationResult, Vec3, ViewTransform2D, WorkspaceProps };