@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.
- package/dist/_dts/{DrawCommand-C_XboUpz.d.ts → DrawCommand-DBB45NfN.d.ts} +196 -52
- package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
- package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
- package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
- package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
- package/dist/_dts/{useTrialState-BMNIx3Cy.d.ts → useTrialState-CsGMjhu9.d.ts} +5 -87
- package/dist/canvas/index.d.ts +7 -3
- package/dist/canvas/index.js +2 -1
- package/dist/chrome/index.d.ts +86 -0
- package/dist/chrome/index.js +8 -0
- package/dist/chrome/index.js.map +1 -0
- package/dist/{chunk-VUU5UXHE.js → chunk-4TMMVIDM.js} +8 -5
- package/dist/chunk-4TMMVIDM.js.map +1 -0
- package/dist/{chunk-THBG7FQZ.js → chunk-HFDVGF4X.js} +15 -12
- package/dist/chunk-HFDVGF4X.js.map +1 -0
- package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
- package/dist/chunk-NS54R52R.js.map +1 -0
- package/dist/{chunk-G5TJVQQT.js → chunk-O2BULFHX.js} +5 -14
- package/dist/chunk-O2BULFHX.js.map +1 -0
- package/dist/chunk-O7NVSCLN.js +266 -0
- package/dist/chunk-O7NVSCLN.js.map +1 -0
- package/dist/chunk-PO6L3NSH.js +366 -0
- package/dist/chunk-PO6L3NSH.js.map +1 -0
- package/dist/chunk-PQJ5B2U2.js +27 -0
- package/dist/chunk-PQJ5B2U2.js.map +1 -0
- package/dist/chunk-RVNN4CHQ.js +212 -0
- package/dist/chunk-RVNN4CHQ.js.map +1 -0
- package/dist/chunk-TLGRYALP.js +6151 -0
- package/dist/chunk-TLGRYALP.js.map +1 -0
- package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
- package/dist/chunk-V7PRSIRQ.js.map +1 -0
- package/dist/{chunk-BOHF3PQO.js → chunk-Z6HJ5FEM.js} +8111 -5190
- package/dist/chunk-Z6HJ5FEM.js.map +1 -0
- package/dist/chunk-ZZAYVX4X.js +505 -0
- package/dist/chunk-ZZAYVX4X.js.map +1 -0
- package/dist/controls/index.d.ts +19 -5
- package/dist/controls/index.js +3 -1
- package/dist/dragdrop/index.d.ts +4 -2
- package/dist/index.d.ts +246 -106
- package/dist/index.js +673 -559
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +5 -3
- package/dist/layers/index.js +2 -1
- package/dist/passthrough/weasel-canvas.d.ts +69 -13
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +3799 -3672
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.d.ts +102 -8
- package/dist/primitives/index.js +4 -1
- package/dist/state/index.d.ts +3 -2
- package/dist/state/index.js +2 -2
- package/dist/styles.css +542 -153
- package/dist/surface/index.js +2 -1
- package/dist/ui/layers/index.js +4 -3
- package/dist/undo/index.d.ts +4 -2
- package/package.json +16 -7
- package/src/canvas/AGENTS.md +4 -0
- package/src/canvas/CanvasStack.tsx +6 -2
- package/src/canvas/useLayerScheduler.ts +22 -15
- package/src/canvas/usePanZoom.test.ts +80 -0
- package/src/canvas/usePanZoom.ts +16 -2
- package/src/chrome/ChromeRegions.stories.tsx +106 -0
- package/src/chrome/builtins.test.ts +153 -0
- package/src/chrome/builtins.tsx +205 -0
- package/src/chrome/index.ts +25 -0
- package/src/chrome/merge.test.ts +51 -0
- package/src/chrome/merge.ts +52 -0
- package/src/chrome/regions/PaletteRegion.less +14 -0
- package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
- package/src/chrome/regions/PaletteRegion.tsx +41 -0
- package/src/chrome/regions/SidebarRegion.tsx +56 -0
- package/src/chrome/regions/StatusRegion.tsx +31 -0
- package/src/chrome/regions/TitleBarRegion.tsx +41 -0
- package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
- package/src/chrome/regions/ToolbarRegion.tsx +71 -0
- package/src/chrome/regions/ViewportRegion.less +42 -0
- package/src/chrome/regions/ViewportRegion.tsx +37 -0
- package/src/chrome/regions/regions.test.tsx +81 -0
- package/src/chrome/types.ts +127 -0
- package/src/config/builder.test.ts +87 -0
- package/src/config/builder.ts +224 -0
- package/src/config/fromConfigField.test.ts +93 -0
- package/src/config/fromConfigField.ts +74 -0
- package/src/config/index.ts +22 -0
- package/src/config/resolve.test.ts +120 -0
- package/src/config/resolve.ts +67 -0
- package/src/config/rules.ts +68 -0
- package/src/config/types.ts +108 -0
- package/src/config/useConfigSchema.ts +30 -0
- package/src/config/visible.ts +20 -0
- package/src/controls/ControlPanel.less +4 -58
- package/src/controls/ControlPanel.stories.tsx +86 -0
- package/src/controls/ControlPanel.test.tsx +223 -10
- package/src/controls/ControlPanel.tsx +202 -203
- package/src/dragdrop/DragDropRuntime.tsx +36 -18
- package/src/dragdrop/DragGhost.less +1 -1
- package/src/dragdrop/Palette.less +1 -1
- package/src/index.ts +25 -3
- package/src/instrument/SineWave.smoke.test.tsx +8 -1
- package/src/instrument/defineInstrument.test.ts +38 -1
- package/src/instrument/defineInstrument.ts +31 -4
- package/src/instrument/index.ts +1 -0
- package/src/instrument/types.ts +27 -4
- package/src/instrument/validateConfigSchema.test.ts +13 -3
- package/src/instrument/validateConfigSchema.ts +7 -10
- package/src/lab/Lab.chrome.test.tsx +65 -0
- package/src/lab/Lab.tsx +58 -15
- package/src/lab/LabContext.ts +6 -0
- package/src/lab/LabFullChrome.stories.tsx +308 -0
- package/src/lab/LabHeader.test.tsx +42 -0
- package/src/lab/LabHeader.tsx +57 -0
- package/src/lab/LabPalette.test.tsx +35 -0
- package/src/lab/LabPalette.tsx +34 -0
- package/src/lab/LabShell.less +50 -3
- package/src/lab/Workspace.less +1 -30
- package/src/lab/Workspace.test.tsx +20 -10
- package/src/lab/Workspace.tsx +5 -8
- package/src/lab/index.ts +2 -0
- package/src/layers/AGENTS.md +1 -1
- package/src/layers/LayerList.less +31 -7
- package/src/layers/LayerList.tsx +17 -4
- package/src/passthrough/weasel-ui.ts +23 -0
- package/src/primitives/DragHandleGlyph.tsx +29 -0
- package/src/primitives/FloatingPanel.less +28 -0
- package/src/primitives/FloatingPanel.stories.tsx +46 -0
- package/src/primitives/FloatingPanel.test.tsx +240 -0
- package/src/primitives/FloatingPanel.tsx +190 -0
- package/src/primitives/FpsMeter.test.tsx +44 -0
- package/src/primitives/FpsMeter.tsx +20 -9
- package/src/primitives/JobProgress.less +69 -0
- package/src/primitives/JobProgress.stories.tsx +48 -0
- package/src/primitives/JobProgress.tsx +65 -0
- package/src/primitives/Legend.less +47 -0
- package/src/primitives/Legend.stories.tsx +32 -0
- package/src/primitives/Legend.test.tsx +57 -0
- package/src/primitives/Legend.tsx +40 -0
- package/src/primitives/Sidebar.less +32 -2
- package/src/primitives/StatusBar.less +7 -0
- package/src/primitives/StatusBar.tsx +10 -3
- package/src/primitives/Toolbar.less +50 -3
- package/src/primitives/Toolbar.test.tsx +67 -1
- package/src/primitives/Toolbar.tsx +72 -6
- package/src/primitives/ZoomControl.less +31 -0
- package/src/primitives/ZoomControl.tsx +76 -0
- package/src/primitives/floating.entry.test.tsx +14 -0
- package/src/primitives/index.ts +10 -2
- package/src/primitives/useRovingTabIndex.test.ts +27 -0
- package/src/primitives/useRovingTabIndex.ts +79 -0
- package/src/state/store.ts +11 -3
- package/src/state/toolSlot.test.ts +36 -0
- package/src/state/types.ts +4 -0
- package/src/styles.less +6 -0
- package/src/surface/useTiledSurface.ts +14 -10
- package/src/test-setup.ts +26 -0
- package/src/theme/base.less +19 -2
- package/src/theme/interstellar.tokens.json +6 -2
- package/src/tools/types.ts +24 -0
- package/src/trial/Trial.config.test.tsx +113 -0
- package/src/trial/Trial.job.test.tsx +13 -15
- package/src/trial/Trial.less +115 -10
- package/src/trial/Trial.stories.tsx +1 -0
- package/src/trial/Trial.test.tsx +67 -96
- package/src/trial/Trial.tsx +80 -39
- package/src/trial/TrialChrome.tsx +99 -80
- package/src/trial/TrialDragContext.ts +10 -0
- package/src/trial/TrialTitleBar.tsx +41 -0
- package/src/trial/index.ts +1 -15
- package/src/trial/trialChrome.borders.test.ts +38 -0
- package/src/ui/layers/LayerStack.less +9 -9
- package/src/ui/layers/LayerStack.tsx +1 -13
- package/src/ui/properties/CurveField.less +2 -2
- package/src/ui/properties/CurveField.test.tsx +11 -11
- package/src/ui/properties/PropertyGroup.less +2 -2
- package/src/ui/properties/PropertyPanel.less +55 -41
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
- package/src/ui/properties/storyLayouts.tsx +2 -2
- package/dist/_dts/index-JFAYj5Tv.d.ts +0 -144
- package/dist/_dts/types-x92Kfeme.d.ts +0 -62
- package/dist/chunk-574LJAV4.js.map +0 -1
- package/dist/chunk-5R2ATYPJ.js +0 -201
- package/dist/chunk-5R2ATYPJ.js.map +0 -1
- package/dist/chunk-73KA7WBO.js +0 -5992
- package/dist/chunk-73KA7WBO.js.map +0 -1
- package/dist/chunk-BOHF3PQO.js.map +0 -1
- package/dist/chunk-G5TJVQQT.js.map +0 -1
- package/dist/chunk-PMAU3SEE.js +0 -126
- package/dist/chunk-PMAU3SEE.js.map +0 -1
- package/dist/chunk-SFL7NFKN.js.map +0 -1
- package/dist/chunk-THBG7FQZ.js.map +0 -1
- package/dist/chunk-VUU5UXHE.js.map +0 -1
- package/src/instrument/capabilityDetector.test.ts +0 -64
- package/src/instrument/capabilityDetector.ts +0 -20
- package/src/trial/DefaultSidebar.tsx +0 -29
- package/src/trial/DefaultStatusBar.tsx +0 -19
- package/src/trial/DefaultToolbar.tsx +0 -92
- package/src/trial/slotTypes.ts +0 -54
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import { R as ResolvedConfig, d as ConfigField, c as ConfigSchema } from './types-AHlQxBNN.js';
|
|
3
|
+
import { c as SavedSnapshot } from './types-1Sdxy_Pv.js';
|
|
4
|
+
import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
|
|
5
|
+
|
|
6
|
+
/** A named position in a trial's chrome. Content is not a region — that is
|
|
7
|
+
* the instrument. */
|
|
8
|
+
type TrialRegion = 'titlebar' | 'toolbar' | 'palette' | 'sidebar' | 'viewport' | 'status';
|
|
9
|
+
/** An icon component taking a pixel size, as `@weasel-js/ui` glyphs do. */
|
|
10
|
+
type IconComponent = ComponentType<{
|
|
11
|
+
size?: number;
|
|
12
|
+
}>;
|
|
13
|
+
/** A button in the trial toolbar. */
|
|
14
|
+
interface ToolbarItem {
|
|
15
|
+
icon: IconComponent;
|
|
16
|
+
label: string;
|
|
17
|
+
/** Shown in the tooltip. Not bound here — the trial owns its keymap. */
|
|
18
|
+
shortcut?: string;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/** Reddens on hover. For actions that discard work. */
|
|
21
|
+
danger?: boolean;
|
|
22
|
+
/** Render the label beside the glyph rather than only in the tooltip. */
|
|
23
|
+
showLabel?: boolean;
|
|
24
|
+
onActivate: () => void;
|
|
25
|
+
}
|
|
26
|
+
/** A selectable tool in the palette region. */
|
|
27
|
+
interface ToolItem {
|
|
28
|
+
icon: IconComponent;
|
|
29
|
+
label: string;
|
|
30
|
+
shortcut?: string;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** A titled block in the sidebar. */
|
|
34
|
+
interface SidebarSection {
|
|
35
|
+
title: string;
|
|
36
|
+
/** Starts collapsed. The open/closed state itself is the region's. */
|
|
37
|
+
defaultCollapsed?: boolean;
|
|
38
|
+
body: ReactNode;
|
|
39
|
+
}
|
|
40
|
+
/** A control acting on the view of the trial, not on the trial. */
|
|
41
|
+
interface ViewportControl {
|
|
42
|
+
icon: IconComponent;
|
|
43
|
+
label: string;
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
onActivate: () => void;
|
|
46
|
+
}
|
|
47
|
+
/** A readout in the status bar. */
|
|
48
|
+
interface StatusReadout {
|
|
49
|
+
/** Short enough for a status bar. Rendered as text. */
|
|
50
|
+
text: string;
|
|
51
|
+
/** Tooltip. */
|
|
52
|
+
title?: string;
|
|
53
|
+
}
|
|
54
|
+
/** What every contribution shares. */
|
|
55
|
+
interface ContributionBase {
|
|
56
|
+
id: string;
|
|
57
|
+
/** Groups sort by first appearance; items sort within a group by
|
|
58
|
+
* declaration order. Contributions with no group sort after grouped ones. */
|
|
59
|
+
group?: string;
|
|
60
|
+
/** Pushes this contribution, and its group, to the far end of the region. */
|
|
61
|
+
end?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A contribution is data the chrome renders, keyed to a region. Supplying
|
|
65
|
+
* `render` instead of `item` opts out of the chrome's layout — deliberate,
|
|
66
|
+
* and visible in the declaration.
|
|
67
|
+
*/
|
|
68
|
+
type TrialContribution = (ContributionBase & {
|
|
69
|
+
region: 'titlebar';
|
|
70
|
+
item: ToolbarItem;
|
|
71
|
+
render?: never;
|
|
72
|
+
}) | (ContributionBase & {
|
|
73
|
+
region: 'toolbar';
|
|
74
|
+
item: ToolbarItem;
|
|
75
|
+
render?: never;
|
|
76
|
+
}) | (ContributionBase & {
|
|
77
|
+
region: 'palette';
|
|
78
|
+
item: ToolItem;
|
|
79
|
+
render?: never;
|
|
80
|
+
}) | (ContributionBase & {
|
|
81
|
+
region: 'sidebar';
|
|
82
|
+
item: SidebarSection;
|
|
83
|
+
render?: never;
|
|
84
|
+
}) | (ContributionBase & {
|
|
85
|
+
region: 'viewport';
|
|
86
|
+
item: ViewportControl;
|
|
87
|
+
render?: never;
|
|
88
|
+
}) | (ContributionBase & {
|
|
89
|
+
region: 'status';
|
|
90
|
+
item: StatusReadout;
|
|
91
|
+
render?: never;
|
|
92
|
+
}) | (ContributionBase & {
|
|
93
|
+
region: TrialRegion;
|
|
94
|
+
item?: never;
|
|
95
|
+
render: (ctx: TrialChromeContext) => ReactNode;
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* Everything a contribution can read about the trial it is being rendered
|
|
99
|
+
* into. Replaces the three separate slot contexts, which each carried a
|
|
100
|
+
* hand-picked subset.
|
|
101
|
+
*/
|
|
102
|
+
interface TrialChromeContext {
|
|
103
|
+
trialId: string;
|
|
104
|
+
instrumentName: string;
|
|
105
|
+
isLastTrial: boolean;
|
|
106
|
+
/** Null when the trial holds a view that is not the 2D one. */
|
|
107
|
+
zoom: number | null;
|
|
108
|
+
setZoom: (z: number) => void;
|
|
109
|
+
canUndo: boolean;
|
|
110
|
+
canRedo: boolean;
|
|
111
|
+
undo: () => void;
|
|
112
|
+
redo: () => void;
|
|
113
|
+
/** The instrument's controls, resolved against the lab's rules. Always
|
|
114
|
+
* populated: a legacy `configSchema()` is adapted into the same shape. */
|
|
115
|
+
configSchema: ResolvedConfig;
|
|
116
|
+
/** @deprecated Read `configSchema`. Empty for an instrument declaring
|
|
117
|
+
* `config`, since a builder schema has no `ConfigField[]` form. */
|
|
118
|
+
configFields: ConfigField[];
|
|
119
|
+
config: unknown;
|
|
120
|
+
setConfig: (key: string, value: unknown) => void;
|
|
121
|
+
savedSnapshots: SavedSnapshot[];
|
|
122
|
+
saveSnapshot: (name?: string) => void;
|
|
123
|
+
loadSnapshot: (snapshotId: string) => void;
|
|
124
|
+
clone: () => void;
|
|
125
|
+
reset: () => void;
|
|
126
|
+
close: () => void;
|
|
127
|
+
/** Resolved active tool: the trial's slot, or the lab's when the trial has
|
|
128
|
+
* none. Null when neither holds one. */
|
|
129
|
+
activeToolId: string | null;
|
|
130
|
+
setActiveTool: (id: string) => void;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A tool a trial can be in. labkit's own — core's `ToolsApi` carries hotkey
|
|
135
|
+
* slots, ambient tools, eligibility tiers and canvas overlay layers, all bound
|
|
136
|
+
* to the gesture dispatcher, and a labkit instrument is an arbitrary canvas or
|
|
137
|
+
* DOM tree rather than a weasel scene.
|
|
138
|
+
*/
|
|
139
|
+
interface TrialTool {
|
|
140
|
+
id: string;
|
|
141
|
+
label: string;
|
|
142
|
+
icon: IconComponent;
|
|
143
|
+
/** Shown in the tooltip. Not bound here — the instrument owns its keymap. */
|
|
144
|
+
shortcut?: string;
|
|
145
|
+
/** Presentation grouping in the palette. Ungrouped tools sort after grouped. */
|
|
146
|
+
group?: string;
|
|
147
|
+
}
|
|
148
|
+
/** What an instrument declares to get a palette region. */
|
|
149
|
+
interface ToolCapability {
|
|
150
|
+
tools: TrialTool[];
|
|
151
|
+
/** Which tool a fresh trial starts in. Defaults to the first. */
|
|
152
|
+
initial?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** What an instrument's `render` is handed: its state and config, the setters
|
|
156
|
+
* for both, the trial it is mounted in, and a way to emit named events. */
|
|
157
|
+
interface RenderContext<TS = unknown, TC = unknown> {
|
|
158
|
+
state: TS;
|
|
159
|
+
config: TC;
|
|
160
|
+
setState: (next: TS | ((prev: TS) => TS)) => void;
|
|
161
|
+
setConfig: (key: keyof TC, value: unknown) => void;
|
|
162
|
+
trial: {
|
|
163
|
+
id: string;
|
|
164
|
+
/** The trial's view, in whatever shape this instrument chose. labkit persists
|
|
165
|
+
* it and restores it on Reset without ever reading into it. */
|
|
166
|
+
view: unknown;
|
|
167
|
+
setView: (next: unknown) => void;
|
|
168
|
+
/** 2D convenience over `view`. Reads 1 and writes nothing when the trial holds
|
|
169
|
+
* a view that is not the 2D one. */
|
|
170
|
+
zoom: number;
|
|
171
|
+
setZoom: (z: number) => void;
|
|
172
|
+
/** Resolved active tool: this trial's slot, or the lab's. Null when neither
|
|
173
|
+
* holds one. */
|
|
174
|
+
activeToolId: string | null;
|
|
175
|
+
};
|
|
176
|
+
emit: (event: string) => void;
|
|
177
|
+
/** Present only when the instrument declares a `job`. */
|
|
178
|
+
job?: JobHandle;
|
|
179
|
+
}
|
|
180
|
+
/** One 2D canvas layer of an instrument, drawn in declaration order.
|
|
181
|
+
*
|
|
182
|
+
* `draw` is called with the camera already applied, so it works in world
|
|
183
|
+
* coordinates. `zoom` is passed for the things that must not scale with it —
|
|
184
|
+
* set `ctx.lineWidth = 1 / zoom` to keep a hairline hairline. */
|
|
185
|
+
interface CanvasLayer<TS = unknown, TC = unknown> {
|
|
186
|
+
id: string;
|
|
187
|
+
draw: (ctx: CanvasRenderingContext2D, args: {
|
|
188
|
+
state: TS;
|
|
189
|
+
config: TC;
|
|
190
|
+
zoom: number;
|
|
191
|
+
}) => void;
|
|
192
|
+
}
|
|
193
|
+
/** Declares that an instrument draws to a canvas: its layers, and where the
|
|
194
|
+
* view starts. */
|
|
195
|
+
interface CanvasCapability<TS = unknown, TC = unknown> {
|
|
196
|
+
layers: CanvasLayer<TS, TC>[];
|
|
197
|
+
initialView?: {
|
|
198
|
+
zoom: number;
|
|
199
|
+
pan: {
|
|
200
|
+
x: number;
|
|
201
|
+
y: number;
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/** Widens `usePanZoom`'s default clamp; the opening zoom stays reachable
|
|
205
|
+
* regardless of these. */
|
|
206
|
+
minZoom?: number;
|
|
207
|
+
maxZoom?: number;
|
|
208
|
+
}
|
|
209
|
+
/** Declares which of an instrument's layers the trial should offer
|
|
210
|
+
* show/hide controls for. */
|
|
211
|
+
interface LayerCapability {
|
|
212
|
+
/** In list order. A bare string is a layer id that doubles as its own label;
|
|
213
|
+
* give a descriptor instead to label a layer or mark it `alwaysOn`. */
|
|
214
|
+
ids: readonly (string | LayerDescriptor)[];
|
|
215
|
+
}
|
|
216
|
+
/** Declares that an instrument accepts items dragged from a palette: what the
|
|
217
|
+
* palette offers, what a drop does to the state, and — optionally — live
|
|
218
|
+
* feedback during the drag and the ability to drag existing items back out. */
|
|
219
|
+
interface DragDropCapability<TS = unknown, TC = unknown> {
|
|
220
|
+
palette: PaletteItem[] | ((state: TS, config: TC) => PaletteItem[]);
|
|
221
|
+
onDrop: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => TS;
|
|
222
|
+
onDragOver?: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => DragFeedback | null;
|
|
223
|
+
pickUp?: (hit: HitResult, state: TS, config: TC) => {
|
|
224
|
+
item: PaletteItem;
|
|
225
|
+
state: TS;
|
|
226
|
+
} | null;
|
|
227
|
+
}
|
|
228
|
+
/** Declares that an instrument's state is undoable: which emitted events
|
|
229
|
+
* snapshot it, and how many snapshots to keep. */
|
|
230
|
+
interface UndoCapability {
|
|
231
|
+
snapshotOn?: string[];
|
|
232
|
+
maxDepth?: number;
|
|
233
|
+
}
|
|
234
|
+
/** The name of an event an instrument emits through `RenderContext.emit`. */
|
|
235
|
+
type SystemEvent = string;
|
|
236
|
+
/** A point in world coordinates. */
|
|
237
|
+
type Point = {
|
|
238
|
+
x: number;
|
|
239
|
+
y: number;
|
|
240
|
+
};
|
|
241
|
+
/** What a hit-test found, and where. */
|
|
242
|
+
type HitResult = {
|
|
243
|
+
hit: boolean;
|
|
244
|
+
layerId?: string;
|
|
245
|
+
pointId?: string;
|
|
246
|
+
};
|
|
247
|
+
/** A trial's camera. */
|
|
248
|
+
type ViewTransform = {
|
|
249
|
+
zoom: number;
|
|
250
|
+
pan: Point;
|
|
251
|
+
};
|
|
252
|
+
/** A layer as the layer list shows it. `alwaysOn` layers cannot be hidden. */
|
|
253
|
+
type LayerDescriptor = {
|
|
254
|
+
id: string;
|
|
255
|
+
label: string;
|
|
256
|
+
alwaysOn?: boolean;
|
|
257
|
+
};
|
|
258
|
+
/** One draggable entry in an instrument's palette. */
|
|
259
|
+
type PaletteItem = {
|
|
260
|
+
id: string;
|
|
261
|
+
label: string;
|
|
262
|
+
data?: unknown;
|
|
263
|
+
};
|
|
264
|
+
/** Whether a drop would be accepted at the current position, and why not if
|
|
265
|
+
* it would not. */
|
|
266
|
+
type DragFeedback = {
|
|
267
|
+
ok: boolean;
|
|
268
|
+
reason?: string;
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* An instrument: one self-contained interactive experiment a lab can host.
|
|
272
|
+
*
|
|
273
|
+
* It owns two pieces of data — `config`, the settings the control panel edits,
|
|
274
|
+
* and `state`, what the experiment is currently doing — and renders from both.
|
|
275
|
+
* The optional capability fields declare what else it wants from the runtime:
|
|
276
|
+
* a canvas, a layer list, palette drag-and-drop, undo. Declaring a capability
|
|
277
|
+
* is what makes the trial provide the corresponding chrome.
|
|
278
|
+
*/
|
|
279
|
+
interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
|
|
280
|
+
name: string;
|
|
281
|
+
defaultConfig: () => TC;
|
|
282
|
+
initialState: (config: TC) => TS;
|
|
283
|
+
/** The instrument's config, declared once: values, types and controls.
|
|
284
|
+
* Supplying this makes `defaultConfig` optional — `defineInstrument`
|
|
285
|
+
* synthesizes it. Prefer it over `defaultConfig` + `configSchema`. */
|
|
286
|
+
config?: ConfigSchema<TC>;
|
|
287
|
+
/** @deprecated Declare `config` instead; this repeats what `TC` already
|
|
288
|
+
* says and nothing holds the two to one answer. */
|
|
289
|
+
configSchema?: () => ConfigField[];
|
|
290
|
+
/** The instrument's DOM. With `canvas`, this renders as an overlay above the
|
|
291
|
+
* layers rather than instead of them; return `null` for canvas only. */
|
|
292
|
+
render: (ctx: RenderContext<TS, TC>) => ReactNode;
|
|
293
|
+
onConfigChange?: (config: TC, prev: TC, state: TS) => TS;
|
|
294
|
+
serialize?: (state: TS) => unknown;
|
|
295
|
+
deserialize?: (data: unknown, config: TC) => TS;
|
|
296
|
+
canvas?: CanvasCapability<TS, TC>;
|
|
297
|
+
layers?: LayerCapability;
|
|
298
|
+
dragDrop?: DragDropCapability<TS, TC>;
|
|
299
|
+
undo?: UndoCapability;
|
|
300
|
+
/** Tools this instrument offers. Declaring them gives the trial a palette
|
|
301
|
+
* region and its own tool slot. */
|
|
302
|
+
tools?: ToolCapability;
|
|
303
|
+
/** Chrome this instrument contributes beyond what its capabilities imply. */
|
|
304
|
+
chrome?: TrialContribution[];
|
|
305
|
+
/** Work too slow to do during a render. The runtime starts it, aborts it on
|
|
306
|
+
* unmount and on a `key` change, and renders progress into the trial. */
|
|
307
|
+
job?: JobCapability<TS, TC, TItem>;
|
|
308
|
+
}
|
|
309
|
+
/** Instruments as a lab receives them. `any` rather than `unknown` because
|
|
310
|
+
* parameter contravariance keeps a `defineInstrument<TS, TC>` result out of
|
|
311
|
+
* an `Instrument<unknown, unknown>[]`; it is contained to this alias. */
|
|
312
|
+
type InstrumentList = readonly Instrument<any, any, any>[];
|
|
313
|
+
|
|
314
|
+
export type { CanvasCapability as C, DragDropCapability as D, HitResult as H, Instrument as I, LayerCapability as L, PaletteItem as P, RenderContext as R, SidebarSection as S, TrialContribution as T, UndoCapability as U, ViewTransform as V, InstrumentList as a, TrialTool as b, CanvasLayer as c, DragFeedback as d, IconComponent as e, LayerDescriptor as f, Point as g, StatusReadout as h, SystemEvent as i, ToolCapability as j, ToolItem as k, ToolbarItem as l, TrialChromeContext as m, TrialRegion as n, ViewportControl as o };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** A trial's undo history, as snapshots of its state either side of the
|
|
2
|
+
* present. */
|
|
3
|
+
interface UndoStack {
|
|
4
|
+
past: unknown[];
|
|
5
|
+
future: unknown[];
|
|
6
|
+
}
|
|
7
|
+
/** One trial as the store holds it: which instrument it runs, that
|
|
8
|
+
* instrument's config and state, the camera, and the undo history. */
|
|
9
|
+
interface TrialRecord<TS = unknown, TC = unknown, TV = unknown> {
|
|
10
|
+
id: string;
|
|
11
|
+
instrumentName: string;
|
|
12
|
+
config: TC;
|
|
13
|
+
state: TS;
|
|
14
|
+
/** Opaque to labkit: persisted, restored on Reset and handed to the instrument,
|
|
15
|
+
* but never read into. A 3D lab puts an orbit here and keeps all three. */
|
|
16
|
+
view: TV;
|
|
17
|
+
/** This trial's own tool slot. Undefined means it reads the lab's. */
|
|
18
|
+
activeToolId?: string | null;
|
|
19
|
+
undoStack: UndoStack;
|
|
20
|
+
}
|
|
21
|
+
/** A named, saved copy of a trial's config and state, restorable into any
|
|
22
|
+
* trial running the same instrument. */
|
|
23
|
+
interface SavedSnapshot {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
trialId: string;
|
|
27
|
+
instrumentName: string;
|
|
28
|
+
config: unknown;
|
|
29
|
+
state: unknown;
|
|
30
|
+
savedAt: number;
|
|
31
|
+
}
|
|
32
|
+
/** `auto` follows the OS; the other two are an explicit choice. */
|
|
33
|
+
type LabMode = 'auto' | 'light' | 'dark';
|
|
34
|
+
/** Everything a lab persists: its trials, its saved snapshots, and the
|
|
35
|
+
* chosen color mode. */
|
|
36
|
+
interface LabStoreState {
|
|
37
|
+
trials: TrialRecord[];
|
|
38
|
+
savedSnapshots: SavedSnapshot[];
|
|
39
|
+
mode: LabMode;
|
|
40
|
+
/** The lab's tool slot — what a trial with no slot of its own resolves to. */
|
|
41
|
+
activeToolId: string | null;
|
|
42
|
+
/** Per-trial tile extents, keyed by trial id. Opaque here — the
|
|
43
|
+
* shape belongs to whatever lays the trials out. */
|
|
44
|
+
layout: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
/** Where a lab persists itself. Implementations are keyed string storage and
|
|
47
|
+
* nothing more, so the same store works against localStorage, the URL hash,
|
|
48
|
+
* or memory. */
|
|
49
|
+
interface StorageAdapter {
|
|
50
|
+
read(key: string): string | null;
|
|
51
|
+
write(key: string, value: string): void;
|
|
52
|
+
delete?(key: string): void;
|
|
53
|
+
}
|
|
54
|
+
/** What `useTrialState` hands an instrument: its state and config, with
|
|
55
|
+
* a setter for each. */
|
|
56
|
+
interface TrialStateHandle<TS, TC> {
|
|
57
|
+
state: TS;
|
|
58
|
+
setState: (next: TS | ((prev: TS) => TS)) => void;
|
|
59
|
+
config: TC;
|
|
60
|
+
setConfig: (key: keyof TC, value: TC[keyof TC]) => void;
|
|
61
|
+
}
|
|
62
|
+
/** Options for `createLabStore`. `storageKey` namespaces the keys written, so
|
|
63
|
+
* two labs on one origin do not collide. */
|
|
64
|
+
interface CreateLabStoreOptions {
|
|
65
|
+
storageKey: string;
|
|
66
|
+
storage: StorageAdapter;
|
|
67
|
+
initialMode?: LabMode;
|
|
68
|
+
}
|
|
69
|
+
/** Per-instrument serialize/deserialize hooks, keyed by instrument name. An
|
|
70
|
+
* instrument whose state is already JSON-safe needs no entry. */
|
|
71
|
+
type InstrumentSerializers = Record<string, {
|
|
72
|
+
serialize?: (state: unknown) => unknown;
|
|
73
|
+
deserialize?: (data: unknown) => unknown;
|
|
74
|
+
} | undefined>;
|
|
75
|
+
/** A trial as it is persisted: everything but the undo history, which is
|
|
76
|
+
* session-only. */
|
|
77
|
+
type SerializedTrial = Omit<TrialRecord, 'undoStack'>;
|
|
78
|
+
/** Everything a lab persists, under one key, at a known version. */
|
|
79
|
+
interface LabDocument {
|
|
80
|
+
version: number;
|
|
81
|
+
trials: SerializedTrial[];
|
|
82
|
+
saves: SavedSnapshot[];
|
|
83
|
+
layout: Record<string, unknown>;
|
|
84
|
+
mode: LabMode;
|
|
85
|
+
}
|
|
86
|
+
/** Migrates a document one version forward. Index `i` in the chain takes a
|
|
87
|
+
* version-`i` document to version `i + 1`. */
|
|
88
|
+
type Migration = (doc: Record<string, unknown>) => Record<string, unknown>;
|
|
89
|
+
|
|
90
|
+
export type { CreateLabStoreOptions as C, InstrumentSerializers as I, LabDocument as L, Migration as M, SerializedTrial as S, TrialRecord as T, UndoStack as U, StorageAdapter as a, LabStoreState as b, SavedSnapshot as c, TrialStateHandle as d, LabMode as e };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { P as PrefRenderer, T as ToolPrefLeaf, a as ToolPrefGroup } from './PrefsForm-BYa6cWnO.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Renders the control cell for one config leaf. Identical to weasel-ui's
|
|
5
|
+
* `PrefRenderer` on purpose — aliased rather than redeclared so the two
|
|
6
|
+
* cannot drift.
|
|
7
|
+
*/
|
|
8
|
+
type ControlRenderer = PrefRenderer;
|
|
9
|
+
/** One labeled choice in an `enum` leaf. */
|
|
10
|
+
interface ConfigOption {
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
/** Everything a leaf can carry beyond its kind and default. A flat union of
|
|
15
|
+
* every `Pref*` leaf's extras, plus labkit's own `debounceMs`. */
|
|
16
|
+
interface Annotations {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
hidden?: boolean;
|
|
20
|
+
block?: boolean;
|
|
21
|
+
pair?: string;
|
|
22
|
+
min?: number;
|
|
23
|
+
max?: number;
|
|
24
|
+
step?: number;
|
|
25
|
+
control?: string;
|
|
26
|
+
options?: readonly ConfigOption[];
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
maxLength?: number;
|
|
29
|
+
/** Milliseconds to debounce a string leaf's live writes. Default 150. */
|
|
30
|
+
debounceMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/** What a rule contributes. `kind` is honored only while it is still unset,
|
|
33
|
+
* which is what makes `f.value` claimable and a kinded factory final. */
|
|
34
|
+
type LeafPatch = Annotations & {
|
|
35
|
+
kind?: string;
|
|
36
|
+
};
|
|
37
|
+
/** What a rule is given for the leaf it is deciding about. */
|
|
38
|
+
interface ConfigRuleContext {
|
|
39
|
+
/** The config key this leaf writes. */
|
|
40
|
+
key: string;
|
|
41
|
+
/** Dotted path within the schema. Equal to `key` while schemas are flat. */
|
|
42
|
+
path: string;
|
|
43
|
+
/** The leaf's default value. A rule may read it but never change it. */
|
|
44
|
+
default: unknown;
|
|
45
|
+
/** What earlier rules and the author's own annotations have settled. */
|
|
46
|
+
leaf: Readonly<LeafPatch>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Decides part of how a leaf is presented. Returns a patch, or null to
|
|
50
|
+
* abstain. Merging is gap-filling: a property already settled is never
|
|
51
|
+
* overwritten, so the author's annotations beat every rule and a consumer's
|
|
52
|
+
* rules beat labkit's built-ins.
|
|
53
|
+
*/
|
|
54
|
+
type ConfigRule = (ctx: ConfigRuleContext) => LeafPatch | null;
|
|
55
|
+
/** A presentational bucket of leaves, rendered under one heading. */
|
|
56
|
+
interface SectionSpec {
|
|
57
|
+
label: string;
|
|
58
|
+
paths: readonly string[];
|
|
59
|
+
}
|
|
60
|
+
/** A schema resolved against a set of rules: the vocabulary weasel-ui renders,
|
|
61
|
+
* plus the three things labkit keeps on the side because `PrefLeaf` has no
|
|
62
|
+
* field for them. */
|
|
63
|
+
interface ResolvedConfig {
|
|
64
|
+
/** Flat: every leaf is a direct child, so a leaf's path is its config key. */
|
|
65
|
+
group: ToolPrefGroup;
|
|
66
|
+
sections: readonly SectionSpec[];
|
|
67
|
+
showIf: ReadonlyMap<string, (config: Record<string, unknown>) => boolean>;
|
|
68
|
+
/** Node-level `.render` overrides, keyed by path. */
|
|
69
|
+
renderers: Readonly<Record<string, ControlRenderer>>;
|
|
70
|
+
}
|
|
71
|
+
/** Per-node extras that do not belong on a `PrefLeaf`. */
|
|
72
|
+
interface NodeOptions {
|
|
73
|
+
section?: string;
|
|
74
|
+
showIf?: (config: Record<string, unknown>) => boolean;
|
|
75
|
+
render?: ControlRenderer;
|
|
76
|
+
validate?: (leaf: ToolPrefLeaf) => string[];
|
|
77
|
+
}
|
|
78
|
+
/** The builder's leaf: a kind (or null, to be decided by rules), a default,
|
|
79
|
+
* an annotation bag, and the extras above. */
|
|
80
|
+
interface ConfigNode<T = unknown> {
|
|
81
|
+
readonly kind: string | null;
|
|
82
|
+
readonly default: T;
|
|
83
|
+
readonly annotations: Readonly<Annotations>;
|
|
84
|
+
readonly options: Readonly<NodeOptions>;
|
|
85
|
+
}
|
|
86
|
+
/** The value type a node produces. */
|
|
87
|
+
type NodeValue<N> = N extends ConfigNode<infer T> ? T : never;
|
|
88
|
+
/** The config type a builder shape produces. */
|
|
89
|
+
type InferConfig<S> = {
|
|
90
|
+
[K in keyof S]: NodeValue<S[K]>;
|
|
91
|
+
};
|
|
92
|
+
/** An instrument's config, declared once. */
|
|
93
|
+
interface ConfigSchema<TC> {
|
|
94
|
+
readonly nodes: Readonly<Record<string, ConfigNode>>;
|
|
95
|
+
/** The starting config — what `defaultConfig()` would have returned. */
|
|
96
|
+
defaults(): TC;
|
|
97
|
+
}
|
|
98
|
+
/** The config type behind a schema: `ConfigOf<typeof sceneConfig>`. */
|
|
99
|
+
type ConfigOf<S> = S extends ConfigSchema<infer TC> ? TC : never;
|
|
100
|
+
|
|
101
|
+
/** Which control a config field is edited with. */
|
|
102
|
+
type ConfigFieldType = 'slider' | 'checkbox' | 'select' | 'number' | 'text' | 'color';
|
|
103
|
+
/** What every config field carries: the config key it writes, the label shown
|
|
104
|
+
* beside it, and which control renders it. */
|
|
105
|
+
interface ConfigFieldBase {
|
|
106
|
+
key: string;
|
|
107
|
+
label: string;
|
|
108
|
+
type: ConfigFieldType;
|
|
109
|
+
}
|
|
110
|
+
/** A bounded number edited by dragging. */
|
|
111
|
+
interface SliderField extends ConfigFieldBase {
|
|
112
|
+
type: 'slider';
|
|
113
|
+
default: number;
|
|
114
|
+
min: number;
|
|
115
|
+
max: number;
|
|
116
|
+
step?: number;
|
|
117
|
+
}
|
|
118
|
+
/** A boolean. */
|
|
119
|
+
interface CheckboxField extends ConfigFieldBase {
|
|
120
|
+
type: 'checkbox';
|
|
121
|
+
default: boolean;
|
|
122
|
+
}
|
|
123
|
+
/** One choice in a select field. */
|
|
124
|
+
interface SelectOption {
|
|
125
|
+
value: string;
|
|
126
|
+
label: string;
|
|
127
|
+
}
|
|
128
|
+
/** A fixed set of labeled choices. */
|
|
129
|
+
interface SelectField extends ConfigFieldBase {
|
|
130
|
+
type: 'select';
|
|
131
|
+
default: string;
|
|
132
|
+
options: SelectOption[];
|
|
133
|
+
}
|
|
134
|
+
/** A number typed directly, optionally bounded. Use a slider field instead
|
|
135
|
+
* when the range matters more than the exact value. */
|
|
136
|
+
interface NumberField extends ConfigFieldBase {
|
|
137
|
+
type: 'number';
|
|
138
|
+
default: number;
|
|
139
|
+
min?: number;
|
|
140
|
+
max?: number;
|
|
141
|
+
step?: number;
|
|
142
|
+
}
|
|
143
|
+
/** A free-text string. Writes are debounced so typing does not re-run the
|
|
144
|
+
* instrument on every keystroke. */
|
|
145
|
+
interface TextField extends ConfigFieldBase {
|
|
146
|
+
type: 'text';
|
|
147
|
+
default: string;
|
|
148
|
+
placeholder?: string;
|
|
149
|
+
maxLength?: number;
|
|
150
|
+
/** Milliseconds to debounce live setConfig calls. Default 150 ms. Set to 0 to disable. */
|
|
151
|
+
debounceMs?: number;
|
|
152
|
+
}
|
|
153
|
+
/** A color, as a CSS color string. */
|
|
154
|
+
interface ColorField extends ConfigFieldBase {
|
|
155
|
+
type: 'color';
|
|
156
|
+
default: string;
|
|
157
|
+
}
|
|
158
|
+
/** One field of an instrument's config schema. The schema is what the control
|
|
159
|
+
* panel renders, and what `validateConfigSchema` checks. */
|
|
160
|
+
type ConfigField = SliderField | CheckboxField | SelectField | NumberField | TextField | ColorField;
|
|
161
|
+
|
|
162
|
+
export type { Annotations as A, ConfigNode as C, InferConfig as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionSpec as S, TextField as T, ControlRenderer as a, ConfigOption as b, ConfigSchema as c, ConfigField as d, ConfigRule as e, ConfigRuleContext as f, CheckboxField as g, ColorField as h, ConfigFieldBase as i, ConfigFieldType as j, ConfigOf as k, NodeValue as l, NumberField as m, SelectField as n, SelectOption as o, SliderField as p };
|
|
@@ -1,92 +1,8 @@
|
|
|
1
|
+
import { a as StorageAdapter, b as LabStoreState, T as TrialRecord, c as SavedSnapshot, e as LabMode, I as InstrumentSerializers, C as CreateLabStoreOptions, d as TrialStateHandle } from './types-1Sdxy_Pv.js';
|
|
1
2
|
import * as react from 'react';
|
|
2
3
|
import { ReactNode, ReactElement } from 'react';
|
|
3
4
|
import { StoreApi } from 'zustand/vanilla';
|
|
4
5
|
|
|
5
|
-
/** A trial's undo history, as snapshots of its state either side of the
|
|
6
|
-
* present. */
|
|
7
|
-
interface UndoStack {
|
|
8
|
-
past: unknown[];
|
|
9
|
-
future: unknown[];
|
|
10
|
-
}
|
|
11
|
-
/** One trial as the store holds it: which instrument it runs, that
|
|
12
|
-
* instrument's config and state, the camera, and the undo history. */
|
|
13
|
-
interface TrialRecord<TS = unknown, TC = unknown, TV = unknown> {
|
|
14
|
-
id: string;
|
|
15
|
-
instrumentName: string;
|
|
16
|
-
config: TC;
|
|
17
|
-
state: TS;
|
|
18
|
-
/** Opaque to labkit: persisted, restored on Reset and handed to the instrument,
|
|
19
|
-
* but never read into. A 3D lab puts an orbit here and keeps all three. */
|
|
20
|
-
view: TV;
|
|
21
|
-
undoStack: UndoStack;
|
|
22
|
-
}
|
|
23
|
-
/** A named, saved copy of a trial's config and state, restorable into any
|
|
24
|
-
* trial running the same instrument. */
|
|
25
|
-
interface SavedSnapshot {
|
|
26
|
-
id: string;
|
|
27
|
-
name: string;
|
|
28
|
-
trialId: string;
|
|
29
|
-
instrumentName: string;
|
|
30
|
-
config: unknown;
|
|
31
|
-
state: unknown;
|
|
32
|
-
savedAt: number;
|
|
33
|
-
}
|
|
34
|
-
/** `auto` follows the OS; the other two are an explicit choice. */
|
|
35
|
-
type LabMode = 'auto' | 'light' | 'dark';
|
|
36
|
-
/** Everything a lab persists: its trials, its saved snapshots, and the
|
|
37
|
-
* chosen color mode. */
|
|
38
|
-
interface LabStoreState {
|
|
39
|
-
trials: TrialRecord[];
|
|
40
|
-
savedSnapshots: SavedSnapshot[];
|
|
41
|
-
mode: LabMode;
|
|
42
|
-
/** Per-trial tile extents, keyed by trial id. Opaque here — the
|
|
43
|
-
* shape belongs to whatever lays the trials out. */
|
|
44
|
-
layout: Record<string, unknown>;
|
|
45
|
-
}
|
|
46
|
-
/** Where a lab persists itself. Implementations are keyed string storage and
|
|
47
|
-
* nothing more, so the same store works against localStorage, the URL hash,
|
|
48
|
-
* or memory. */
|
|
49
|
-
interface StorageAdapter {
|
|
50
|
-
read(key: string): string | null;
|
|
51
|
-
write(key: string, value: string): void;
|
|
52
|
-
delete?(key: string): void;
|
|
53
|
-
}
|
|
54
|
-
/** What `useTrialState` hands an instrument: its state and config, with
|
|
55
|
-
* a setter for each. */
|
|
56
|
-
interface TrialStateHandle<TS, TC> {
|
|
57
|
-
state: TS;
|
|
58
|
-
setState: (next: TS | ((prev: TS) => TS)) => void;
|
|
59
|
-
config: TC;
|
|
60
|
-
setConfig: (key: keyof TC, value: TC[keyof TC]) => void;
|
|
61
|
-
}
|
|
62
|
-
/** Options for `createLabStore`. `storageKey` namespaces the keys written, so
|
|
63
|
-
* two labs on one origin do not collide. */
|
|
64
|
-
interface CreateLabStoreOptions {
|
|
65
|
-
storageKey: string;
|
|
66
|
-
storage: StorageAdapter;
|
|
67
|
-
initialMode?: LabMode;
|
|
68
|
-
}
|
|
69
|
-
/** Per-instrument serialize/deserialize hooks, keyed by instrument name. An
|
|
70
|
-
* instrument whose state is already JSON-safe needs no entry. */
|
|
71
|
-
type InstrumentSerializers = Record<string, {
|
|
72
|
-
serialize?: (state: unknown) => unknown;
|
|
73
|
-
deserialize?: (data: unknown) => unknown;
|
|
74
|
-
} | undefined>;
|
|
75
|
-
/** A trial as it is persisted: everything but the undo history, which is
|
|
76
|
-
* session-only. */
|
|
77
|
-
type SerializedTrial = Omit<TrialRecord, 'undoStack'>;
|
|
78
|
-
/** Everything a lab persists, under one key, at a known version. */
|
|
79
|
-
interface LabDocument {
|
|
80
|
-
version: number;
|
|
81
|
-
trials: SerializedTrial[];
|
|
82
|
-
saves: SavedSnapshot[];
|
|
83
|
-
layout: Record<string, unknown>;
|
|
84
|
-
mode: LabMode;
|
|
85
|
-
}
|
|
86
|
-
/** Migrates a document one version forward. Index `i` in the chain takes a
|
|
87
|
-
* version-`i` document to version `i + 1`. */
|
|
88
|
-
type Migration = (doc: Record<string, unknown>) => Record<string, unknown>;
|
|
89
|
-
|
|
90
6
|
/** Persist to `localStorage` — state survives a reload and a new tab. Falls
|
|
91
7
|
* back to no-ops when storage is unavailable. */
|
|
92
8
|
declare const localStorageAdapter: StorageAdapter;
|
|
@@ -116,6 +32,8 @@ interface LabStoreActions {
|
|
|
116
32
|
deleteSnapshot: (snapshotId: string) => void;
|
|
117
33
|
listSnapshots: (trialId?: string) => SavedSnapshot[];
|
|
118
34
|
setMode: (mode: LabMode) => void;
|
|
35
|
+
setLabTool: (id: string | null) => void;
|
|
36
|
+
setTrialTool: (trialId: string, id: string | null) => void;
|
|
119
37
|
setLayout: (layout: Record<string, unknown>) => void;
|
|
120
38
|
}
|
|
121
39
|
/** A lab's store: its state and actions, plus the hook instruments use to
|
|
@@ -167,5 +85,5 @@ declare function quarantineKey(storageKey: string): string;
|
|
|
167
85
|
* running in. Throws outside a lab store and trial. */
|
|
168
86
|
declare function useTrialState<TS = unknown, TC = unknown>(): TrialStateHandle<TS, TC>;
|
|
169
87
|
|
|
170
|
-
export { CURRENT_DOCUMENT_VERSION as C,
|
|
171
|
-
export type {
|
|
88
|
+
export { CURRENT_DOCUMENT_VERSION as C, TrialIdContext as T, LabStoreContext as b, LabStoreProvider as c, TrialIdProvider as d, createLabStore as e, createMemoryAdapter as f, localStorageAdapter as g, useLabStore as h, useTrialId as i, useTrialState as j, labDocumentKey as l, noneAdapter as n, quarantineKey as q, sessionStorageAdapter as s, urlHashAdapter as u };
|
|
89
|
+
export type { LabStore as L, LabStoreActions as a };
|