@weasel-js/labkit 1.2.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 (196) hide show
  1. package/dist/_dts/{DrawCommand-C_XboUpz.d.ts → DrawCommand-DBB45NfN.d.ts} +196 -52
  2. package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
  3. package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
  4. package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
  5. package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
  6. package/dist/_dts/{useTrialState-BMNIx3Cy.d.ts → useTrialState-CsGMjhu9.d.ts} +5 -87
  7. package/dist/canvas/index.d.ts +7 -3
  8. package/dist/canvas/index.js +2 -1
  9. package/dist/chrome/index.d.ts +86 -0
  10. package/dist/chrome/index.js +8 -0
  11. package/dist/chrome/index.js.map +1 -0
  12. package/dist/{chunk-VUU5UXHE.js → chunk-4TMMVIDM.js} +8 -5
  13. package/dist/chunk-4TMMVIDM.js.map +1 -0
  14. package/dist/{chunk-THBG7FQZ.js → chunk-HFDVGF4X.js} +15 -12
  15. package/dist/chunk-HFDVGF4X.js.map +1 -0
  16. package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
  17. package/dist/chunk-NS54R52R.js.map +1 -0
  18. package/dist/{chunk-G5TJVQQT.js → chunk-O2BULFHX.js} +5 -14
  19. package/dist/chunk-O2BULFHX.js.map +1 -0
  20. package/dist/chunk-O7NVSCLN.js +266 -0
  21. package/dist/chunk-O7NVSCLN.js.map +1 -0
  22. package/dist/chunk-PO6L3NSH.js +366 -0
  23. package/dist/chunk-PO6L3NSH.js.map +1 -0
  24. package/dist/chunk-PQJ5B2U2.js +27 -0
  25. package/dist/chunk-PQJ5B2U2.js.map +1 -0
  26. package/dist/chunk-RVNN4CHQ.js +212 -0
  27. package/dist/chunk-RVNN4CHQ.js.map +1 -0
  28. package/dist/chunk-TLGRYALP.js +6151 -0
  29. package/dist/chunk-TLGRYALP.js.map +1 -0
  30. package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
  31. package/dist/chunk-V7PRSIRQ.js.map +1 -0
  32. package/dist/{chunk-BOHF3PQO.js → chunk-Z6HJ5FEM.js} +8111 -5190
  33. package/dist/chunk-Z6HJ5FEM.js.map +1 -0
  34. package/dist/chunk-ZZAYVX4X.js +505 -0
  35. package/dist/chunk-ZZAYVX4X.js.map +1 -0
  36. package/dist/controls/index.d.ts +19 -5
  37. package/dist/controls/index.js +3 -1
  38. package/dist/dragdrop/index.d.ts +4 -2
  39. package/dist/index.d.ts +246 -106
  40. package/dist/index.js +673 -559
  41. package/dist/index.js.map +1 -1
  42. package/dist/layers/index.d.ts +5 -3
  43. package/dist/layers/index.js +2 -1
  44. package/dist/passthrough/weasel-canvas.d.ts +69 -13
  45. package/dist/passthrough/weasel-canvas.js +1 -1
  46. package/dist/passthrough/weasel-ui.d.ts +3799 -3672
  47. package/dist/passthrough/weasel-ui.js +2 -2
  48. package/dist/primitives/index.d.ts +102 -8
  49. package/dist/primitives/index.js +4 -1
  50. package/dist/state/index.d.ts +3 -2
  51. package/dist/state/index.js +2 -2
  52. package/dist/styles.css +542 -153
  53. package/dist/surface/index.js +2 -1
  54. package/dist/ui/layers/index.js +4 -3
  55. package/dist/undo/index.d.ts +4 -2
  56. package/package.json +16 -7
  57. package/src/canvas/AGENTS.md +4 -0
  58. package/src/canvas/CanvasStack.tsx +6 -2
  59. package/src/canvas/useLayerScheduler.ts +22 -15
  60. package/src/canvas/usePanZoom.test.ts +80 -0
  61. package/src/canvas/usePanZoom.ts +16 -2
  62. package/src/chrome/ChromeRegions.stories.tsx +106 -0
  63. package/src/chrome/builtins.test.ts +153 -0
  64. package/src/chrome/builtins.tsx +205 -0
  65. package/src/chrome/index.ts +25 -0
  66. package/src/chrome/merge.test.ts +51 -0
  67. package/src/chrome/merge.ts +52 -0
  68. package/src/chrome/regions/PaletteRegion.less +14 -0
  69. package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
  70. package/src/chrome/regions/PaletteRegion.tsx +41 -0
  71. package/src/chrome/regions/SidebarRegion.tsx +56 -0
  72. package/src/chrome/regions/StatusRegion.tsx +31 -0
  73. package/src/chrome/regions/TitleBarRegion.tsx +41 -0
  74. package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
  75. package/src/chrome/regions/ToolbarRegion.tsx +71 -0
  76. package/src/chrome/regions/ViewportRegion.less +42 -0
  77. package/src/chrome/regions/ViewportRegion.tsx +37 -0
  78. package/src/chrome/regions/regions.test.tsx +81 -0
  79. package/src/chrome/types.ts +127 -0
  80. package/src/config/builder.test.ts +87 -0
  81. package/src/config/builder.ts +224 -0
  82. package/src/config/fromConfigField.test.ts +93 -0
  83. package/src/config/fromConfigField.ts +74 -0
  84. package/src/config/index.ts +22 -0
  85. package/src/config/resolve.test.ts +120 -0
  86. package/src/config/resolve.ts +67 -0
  87. package/src/config/rules.ts +68 -0
  88. package/src/config/types.ts +108 -0
  89. package/src/config/useConfigSchema.ts +30 -0
  90. package/src/config/visible.ts +20 -0
  91. package/src/controls/ControlPanel.less +4 -58
  92. package/src/controls/ControlPanel.stories.tsx +86 -0
  93. package/src/controls/ControlPanel.test.tsx +223 -10
  94. package/src/controls/ControlPanel.tsx +202 -203
  95. package/src/dragdrop/DragDropRuntime.tsx +36 -18
  96. package/src/dragdrop/DragGhost.less +1 -1
  97. package/src/dragdrop/Palette.less +1 -1
  98. package/src/index.ts +25 -3
  99. package/src/instrument/SineWave.smoke.test.tsx +8 -1
  100. package/src/instrument/defineInstrument.test.ts +38 -1
  101. package/src/instrument/defineInstrument.ts +31 -4
  102. package/src/instrument/index.ts +1 -0
  103. package/src/instrument/types.ts +27 -4
  104. package/src/instrument/validateConfigSchema.test.ts +13 -3
  105. package/src/instrument/validateConfigSchema.ts +7 -10
  106. package/src/lab/Lab.chrome.test.tsx +65 -0
  107. package/src/lab/Lab.tsx +58 -15
  108. package/src/lab/LabContext.ts +6 -0
  109. package/src/lab/LabFullChrome.stories.tsx +308 -0
  110. package/src/lab/LabHeader.test.tsx +42 -0
  111. package/src/lab/LabHeader.tsx +57 -0
  112. package/src/lab/LabPalette.test.tsx +35 -0
  113. package/src/lab/LabPalette.tsx +34 -0
  114. package/src/lab/LabShell.less +50 -3
  115. package/src/lab/Workspace.less +1 -30
  116. package/src/lab/Workspace.test.tsx +20 -10
  117. package/src/lab/Workspace.tsx +5 -8
  118. package/src/lab/index.ts +2 -0
  119. package/src/layers/AGENTS.md +1 -1
  120. package/src/layers/LayerList.less +31 -7
  121. package/src/layers/LayerList.tsx +17 -4
  122. package/src/passthrough/weasel-ui.ts +23 -0
  123. package/src/primitives/DragHandleGlyph.tsx +29 -0
  124. package/src/primitives/FloatingPanel.less +28 -0
  125. package/src/primitives/FloatingPanel.stories.tsx +46 -0
  126. package/src/primitives/FloatingPanel.test.tsx +240 -0
  127. package/src/primitives/FloatingPanel.tsx +190 -0
  128. package/src/primitives/FpsMeter.test.tsx +44 -0
  129. package/src/primitives/FpsMeter.tsx +20 -9
  130. package/src/primitives/JobProgress.less +69 -0
  131. package/src/primitives/JobProgress.stories.tsx +48 -0
  132. package/src/primitives/JobProgress.tsx +65 -0
  133. package/src/primitives/Legend.less +47 -0
  134. package/src/primitives/Legend.stories.tsx +32 -0
  135. package/src/primitives/Legend.test.tsx +57 -0
  136. package/src/primitives/Legend.tsx +40 -0
  137. package/src/primitives/Sidebar.less +32 -2
  138. package/src/primitives/StatusBar.less +7 -0
  139. package/src/primitives/StatusBar.tsx +10 -3
  140. package/src/primitives/Toolbar.less +50 -3
  141. package/src/primitives/Toolbar.test.tsx +67 -1
  142. package/src/primitives/Toolbar.tsx +72 -6
  143. package/src/primitives/ZoomControl.less +31 -0
  144. package/src/primitives/ZoomControl.tsx +76 -0
  145. package/src/primitives/floating.entry.test.tsx +14 -0
  146. package/src/primitives/index.ts +10 -2
  147. package/src/primitives/useRovingTabIndex.test.ts +27 -0
  148. package/src/primitives/useRovingTabIndex.ts +79 -0
  149. package/src/state/store.ts +11 -3
  150. package/src/state/toolSlot.test.ts +36 -0
  151. package/src/state/types.ts +4 -0
  152. package/src/styles.less +6 -0
  153. package/src/surface/useTiledSurface.ts +14 -10
  154. package/src/test-setup.ts +26 -0
  155. package/src/theme/base.less +19 -2
  156. package/src/theme/interstellar.tokens.json +6 -2
  157. package/src/tools/types.ts +24 -0
  158. package/src/trial/Trial.config.test.tsx +113 -0
  159. package/src/trial/Trial.job.test.tsx +13 -15
  160. package/src/trial/Trial.less +115 -10
  161. package/src/trial/Trial.stories.tsx +1 -0
  162. package/src/trial/Trial.test.tsx +67 -96
  163. package/src/trial/Trial.tsx +80 -39
  164. package/src/trial/TrialChrome.tsx +99 -80
  165. package/src/trial/TrialDragContext.ts +10 -0
  166. package/src/trial/TrialTitleBar.tsx +41 -0
  167. package/src/trial/index.ts +1 -15
  168. package/src/trial/trialChrome.borders.test.ts +38 -0
  169. package/src/ui/layers/LayerStack.less +9 -9
  170. package/src/ui/layers/LayerStack.tsx +1 -13
  171. package/src/ui/properties/CurveField.less +2 -2
  172. package/src/ui/properties/CurveField.test.tsx +11 -11
  173. package/src/ui/properties/PropertyGroup.less +2 -2
  174. package/src/ui/properties/PropertyPanel.less +55 -41
  175. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
  176. package/src/ui/properties/storyLayouts.tsx +2 -2
  177. package/dist/_dts/index-JFAYj5Tv.d.ts +0 -144
  178. package/dist/_dts/types-x92Kfeme.d.ts +0 -62
  179. package/dist/chunk-574LJAV4.js.map +0 -1
  180. package/dist/chunk-5R2ATYPJ.js +0 -201
  181. package/dist/chunk-5R2ATYPJ.js.map +0 -1
  182. package/dist/chunk-73KA7WBO.js +0 -5992
  183. package/dist/chunk-73KA7WBO.js.map +0 -1
  184. package/dist/chunk-BOHF3PQO.js.map +0 -1
  185. package/dist/chunk-G5TJVQQT.js.map +0 -1
  186. package/dist/chunk-PMAU3SEE.js +0 -126
  187. package/dist/chunk-PMAU3SEE.js.map +0 -1
  188. package/dist/chunk-SFL7NFKN.js.map +0 -1
  189. package/dist/chunk-THBG7FQZ.js.map +0 -1
  190. package/dist/chunk-VUU5UXHE.js.map +0 -1
  191. package/src/instrument/capabilityDetector.test.ts +0 -64
  192. package/src/instrument/capabilityDetector.ts +0 -20
  193. package/src/trial/DefaultSidebar.tsx +0 -29
  194. package/src/trial/DefaultStatusBar.tsx +0 -19
  195. package/src/trial/DefaultToolbar.tsx +0 -92
  196. package/src/trial/slotTypes.ts +0 -54
package/dist/index.d.ts CHANGED
@@ -1,22 +1,26 @@
1
1
  export { CanvasLayerDescriptor, CanvasStack, CanvasStackContext, CanvasStackContextValue, CanvasStackProps, screenToWorld, worldToScreen } from './canvas/index.js';
2
2
  import * as react from 'react';
3
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';
4
10
  export { ControlPanel } from './controls/index.js';
5
- import { C as ConfigField } from './_dts/types-x92Kfeme.js';
6
- 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';
7
- import { I as Instrument, a as InstrumentList, V as ViewTransform } from './_dts/index-JFAYj5Tv.js';
8
- 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 } from './_dts/index-JFAYj5Tv.js';
9
11
  import { J as JobHandle } from './_dts/types-DJ79Tg5J.js';
10
12
  export { a as JobCapability, b as JobEvent, c as JobFailure, d as JobStatus } from './_dts/types-DJ79Tg5J.js';
11
13
  export { UseJobOptions, useJob } from './job/index.js';
12
14
  import * as react_jsx_runtime from 'react/jsx-runtime';
13
- import { a as StorageAdapter, w as LabMode, T as TrialRecord, h as SavedSnapshot } from './_dts/useTrialState-BMNIx3Cy.js';
14
- 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-BMNIx3Cy.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';
15
17
  export { LayerList, LayerListProps } from './layers/index.js';
16
- 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';
17
20
  export { Box, Rect, SurfaceContext, SurfaceFrame, SurfaceHandle, UseTiledSurfaceOptions, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceOptional, useSurfaceTile, useTiledSurface } from './surface/index.js';
18
21
  export { LayerStack, LayerStackItem, LayerStackProps } from './ui/layers/index.js';
19
22
  export { EventBus, EventListener, clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './undo/index.js';
23
+ import 'windease';
20
24
  import 'zustand/vanilla';
21
25
 
22
26
  /** A point in the space the instrument works in. */
@@ -64,20 +68,183 @@ interface OrbitHandlers {
64
68
  * double-click to go home. The 3D peer of `usePanZoom`. */
65
69
  declare function useOrbit({ view, onViewChange, home, minDistance, maxDistance, }: UseOrbitOptions): OrbitHandlers;
66
70
 
67
- /** Which optional capabilities an instrument declared what the trial
68
- * consults to decide which chrome to show. */
69
- interface CapabilityFlags {
70
- hasCanvas: boolean;
71
- hasLayers: boolean;
72
- hasDragDrop: boolean;
73
- hasUndo: boolean;
74
- }
75
- /** Read an instrument's declared capabilities off its definition. */
76
- declare function detectCapabilities(instrument: Instrument<unknown, unknown>): CapabilityFlags;
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;
207
+
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;
77
225
 
78
- /** Identity at runtime; exists so an instrument's state and config types are
79
- * inferred from the spec rather than having to be written out. */
80
- declare function defineInstrument<TS, TC>(spec: Instrument<TS, TC>): Instrument<TS, TC>;
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>;
81
248
 
82
249
  /** Whether a config schema is usable, and every problem found rather than
83
250
  * just the first. */
@@ -86,8 +253,12 @@ interface ValidationResult {
86
253
  errors: string[];
87
254
  }
88
255
  /** Check a config schema for the mistakes that would otherwise surface as a
89
- * silently broken control: empty or duplicate keys, unknown field types, and
90
- * 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. */
91
262
  declare function validateConfigSchema(fields: ConfigField[]): ValidationResult;
92
263
 
93
264
  /** Props for `<Lab>`. */
@@ -105,11 +276,27 @@ interface LabProps {
105
276
  */
106
277
  nebula?: readonly string[];
107
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>;
108
295
  children?: ReactNode;
109
296
  }
110
297
  /** The lab runtime: creates the store, provides it, and renders one trial
111
298
  * per record in a grid. Each trial runs one of `instruments`. */
112
- 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;
113
300
 
114
301
  /** Lab-wide state and commands: the available instruments, the open
115
302
  * trials and the operations over them, saved snapshots, and the color
@@ -128,12 +315,25 @@ interface LabContextValue {
128
315
  deleteSnapshot: (snapshotId: string) => void;
129
316
  mode: LabMode;
130
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>;
131
323
  }
132
324
  /** Context carrying the surrounding lab. Prefer `useLabContext`. */
133
325
  declare const LabContext: react.Context<LabContextValue | null>;
134
326
  /** The surrounding lab. Throws outside a `<Lab>`. */
135
327
  declare function useLabContext(): LabContextValue;
136
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
+
137
337
  /** Props for `<LabShell>`. */
138
338
  interface LabShellProps {
139
339
  title: string;
@@ -253,85 +453,18 @@ interface Theme {
253
453
  */
254
454
  declare const interstellarTheme: Theme;
255
455
 
256
- /** What a trial hands its toolbar: which instrument is running, the undo
257
- * state and commands, the zoom controls, and the snapshot commands. */
258
- interface TrialToolbarContext {
259
- trialId: string;
260
- instrumentName: string;
261
- hasUndo: boolean;
262
- canUndo: boolean;
263
- canRedo: boolean;
264
- undo: () => void;
265
- redo: () => void;
266
- zoom: number;
267
- setZoom: (z: number) => void;
268
- zoomIn: () => void;
269
- zoomOut: () => void;
270
- resetZoom: () => void;
271
- hasCanvas: boolean;
272
- savedSnapshots: SavedSnapshot[];
273
- saveSnapshot: (name?: string) => void;
274
- loadSnapshot: (snapshotId: string) => void;
275
- clone: () => void;
276
- reset: () => void;
277
- close: () => void;
278
- isLastTrial: boolean;
279
- }
280
- /** What a trial hands its sidebar: the instrument's config schema, its
281
- * current values, and the setter. */
282
- interface TrialSidebarContext {
283
- trialId: string;
284
- instrumentName: string;
285
- configFields: ConfigField[];
286
- config: unknown;
287
- setConfig: (key: string, value: unknown) => void;
288
- }
289
- /** What a trial hands its status bar. */
290
- interface TrialStatusBarContext {
291
- trialId: string;
292
- instrumentName: string;
293
- /** Null when the trial holds a view that is not the 2D one. */
294
- zoom: number | null;
295
- }
296
- /** Replaces a trial's toolbar. Receives everything the default one uses,
297
- * so a custom toolbar need not reach into the store. */
298
- type ToolbarSlot = (ctx: TrialToolbarContext) => ReactNode;
299
- /** Replaces a trial's sidebar. */
300
- type SidebarSlot = (ctx: TrialSidebarContext) => ReactNode;
301
- /** Replaces a trial's status bar. */
302
- type StatusBarSlot = (ctx: TrialStatusBarContext) => ReactNode;
303
-
304
- /** Props for `<DefaultSidebar>`. */
305
- interface DefaultSidebarProps {
306
- ctx: TrialSidebarContext;
307
- }
308
- /** The sidebar a trial renders when no `sidebar` slot is supplied: a
309
- * control panel over the instrument's config schema. */
310
- declare function DefaultSidebar({ ctx }: DefaultSidebarProps): react_jsx_runtime.JSX.Element;
311
-
312
- /** Props for `<DefaultStatusBar>`. */
313
- interface DefaultStatusBarProps {
314
- ctx: TrialStatusBarContext;
315
- }
316
- /** The status bar a trial renders when no `statusBar` slot is supplied:
317
- * the instrument's name, and the zoom when the trial's view has one. */
318
- declare function DefaultStatusBar({ ctx }: DefaultStatusBarProps): react_jsx_runtime.JSX.Element;
319
-
320
- /** Props for `<DefaultToolbar>`. */
321
- interface DefaultToolbarProps {
322
- ctx: TrialToolbarContext;
323
- }
324
- /** The toolbar a trial renders when no `toolbar` slot is supplied. Only
325
- * shows the controls the instrument's declared capabilities support. */
326
- declare function DefaultToolbar({ ctx }: DefaultToolbarProps): react_jsx_runtime.JSX.Element;
327
-
328
456
  /** Props for `<Trial>`. */
329
457
  interface TrialProps {
330
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[];
331
464
  }
332
465
  /** Renders one trial from the lab store: looks up its record and
333
466
  * instrument, and mounts the instrument inside the trial chrome. */
334
- declare function Trial({ id }: TrialProps): react_jsx_runtime.JSX.Element;
467
+ declare function Trial({ id, chrome, suppress }: TrialProps): react_jsx_runtime.JSX.Element;
335
468
 
336
469
  interface UndoBindings {
337
470
  canUndo: boolean;
@@ -345,19 +478,26 @@ interface TrialChromeProps {
345
478
  record: TrialRecord;
346
479
  instrument: Instrument;
347
480
  isLastTrial: boolean;
348
- toolbar?: ToolbarSlot;
349
- sidebar?: SidebarSlot;
350
- statusBar?: StatusBarSlot;
351
481
  undoBindings?: UndoBindings;
352
482
  /** Supplied when the instrument declares a `job`. */
353
483
  job?: JobHandle;
354
- sidebarExtras?: ReactNode;
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;
355
495
  children: ReactNode;
356
496
  }
357
- /** The frame around a running instrument toolbar, sidebar, status bar —
358
- * each replaceable by a slot. Assembles the slot contexts and wires the undo
359
- * keyboard shortcuts. */
360
- declare function TrialChrome({ trialId, record, instrument, isLastTrial, toolbar, sidebar, statusBar, undoBindings, job, 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;
361
501
 
362
502
  /** Append a new trial running `instrumentName`, at that instrument's
363
503
  * default config and initial state. */
@@ -655,5 +795,5 @@ interface ToggleRowProps<T extends string> {
655
795
  * visible at once. */
656
796
  declare function ToggleRow<T extends string>({ label, value, options, onChange, layout, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
657
797
 
658
- export { CheckboxRow, ColorRow, ConfigField, CurveField, DEFAULT_VIEW, DefaultSidebar, DefaultStatusBar, DefaultToolbar, EffectCard, EffectCardList, Instrument, InstrumentList, JobHandle, Lab, LabContext, LabMode, LabShell, NumberRow, PITCH_LIMIT, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Trial, TrialChrome, TrialRecord, ViewTransform, Workspace, addTrial, as2DView, clampPitch, cloneTrial, closeTrial, defineInstrument, detectCapabilities, interstellarTheme, orbitAfterDrag, orbitAfterWheel, reorderTrials, resetTrial, useLabContext, useOrbit, validateConfigSchema, wrapYaw };
659
- export type { CapabilityFlags, CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, DefaultSidebarProps, DefaultStatusBarProps, DefaultToolbarProps, EffectCardListItem, EffectCardListProps, EffectCardProps, LabContextValue, LabProps, LabShellProps, NumberRowProps, OrbitHandlers, OrbitView, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SidebarSlot, SliderRowProps, StatusBarSlot, SubpanelProps, TextRowProps, ToggleRowProps, ToolbarSlot, TrialChromeProps, TrialProps, TrialSidebarContext, TrialStatusBarContext, TrialToolbarContext, UseOrbitOptions, ValidationResult, Vec3, ViewTransform2D, 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 };