@weasel-js/labkit 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -4
- package/dist/_dts/{CanvasStackContext-CqkxkFJW.d.ts → CanvasStackContext-CJNMS6mz.d.ts} +1 -1
- package/dist/_dts/{PrefsForm.d-DDHFANkc.d.ts → PrefsForm.d-DPy9I8It.d.ts} +4 -0
- package/dist/_dts/Select.d-Gwr8fLpK.d.ts +453 -0
- package/dist/_dts/{frac-8R6UmlvN.d.ts → frac-B4okC2lX.d.ts} +42 -4
- package/dist/_dts/{index-retnAut7.d.ts → index-BNdz-tNd.d.ts} +90 -10
- package/dist/_dts/{index-B8uu2ba7.d.ts → index-CSJoj4LC.d.ts} +17 -6
- package/dist/_dts/{types-Ca2LCnPq.d.ts → types-DzoNY1pO.d.ts} +28 -4
- package/dist/_dts/{usePanZoom-nS798rOF.d.ts → usePanZoom-Pzf9f2jh.d.ts} +2 -2
- package/dist/_dts/{useTrialState-jcx_TdU6.d.ts → useTrialState-uLlmaccn.d.ts} +1 -1
- package/dist/canvas/index.d.ts +5 -5
- package/dist/canvas/index.js +1 -1
- package/dist/chrome/index.d.ts +4 -4
- package/dist/chrome/index.js +7 -7
- package/dist/chunk-7PFH3CNM.js +150 -0
- package/dist/chunk-7PFH3CNM.js.map +1 -0
- package/dist/{chunk-PGETSEDK.js → chunk-CEIUBPPW.js} +10 -6
- package/dist/chunk-CEIUBPPW.js.map +1 -0
- package/dist/chunk-EH7UEQV7.js +590 -0
- package/dist/chunk-EH7UEQV7.js.map +1 -0
- package/dist/{chunk-EKIICY6X.js → chunk-EHGVROVV.js} +37 -25
- package/dist/chunk-EHGVROVV.js.map +1 -0
- package/dist/{chunk-ZPH5WTWF.js → chunk-ERFMC6WU.js} +142 -66
- package/dist/chunk-ERFMC6WU.js.map +1 -0
- package/dist/{chunk-NS64DXMN.js → chunk-IHUVQ3TX.js} +39 -16
- package/dist/chunk-IHUVQ3TX.js.map +1 -0
- package/dist/{chunk-QLSV2N3G.js → chunk-IJJUK4FU.js} +25 -13
- package/dist/chunk-IJJUK4FU.js.map +1 -0
- package/dist/{chunk-67SJLMC7.js → chunk-IXTQI7JX.js} +45 -12
- package/dist/chunk-IXTQI7JX.js.map +1 -0
- package/dist/{chunk-MUKOW3TC.js → chunk-MDVTK5U3.js} +4 -4
- package/dist/{chunk-MUKOW3TC.js.map → chunk-MDVTK5U3.js.map} +1 -1
- package/dist/{chunk-UU3NJO6N.js → chunk-PHK4W562.js} +3 -3
- package/dist/{chunk-UU3NJO6N.js.map → chunk-PHK4W562.js.map} +1 -1
- package/dist/chunk-PJ7WEQBJ.js +18 -0
- package/dist/chunk-PJ7WEQBJ.js.map +1 -0
- package/dist/chunk-T3EC2DXD.js +12628 -0
- package/dist/chunk-T3EC2DXD.js.map +1 -0
- package/dist/{chunk-KJALCDWE.js → chunk-UN4U4P63.js} +4 -4
- package/dist/{chunk-KJALCDWE.js.map → chunk-UN4U4P63.js.map} +1 -1
- package/dist/{chunk-NDRYLVOW.js → chunk-Z76KEZAX.js} +2 -2
- package/dist/chunk-Z76KEZAX.js.map +1 -0
- package/dist/{chunk-6ZDGOZQV.js → chunk-ZDHY47EV.js} +70 -17
- package/dist/chunk-ZDHY47EV.js.map +1 -0
- package/dist/config/index.d.ts +60 -16
- package/dist/config/index.js +6 -4
- package/dist/controls/index.d.ts +3 -3
- package/dist/controls/index.js +4 -4
- package/dist/dragdrop/index.d.ts +3 -3
- package/dist/index.d.ts +177 -53
- package/dist/index.js +1333 -1683
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +4 -4
- package/dist/layers/index.js +2 -2
- package/dist/loupe/index.d.ts +67 -16
- package/dist/loupe/index.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +26 -202
- package/dist/passthrough/weasel-ui.js +1 -1
- package/dist/primitives/index.d.ts +3 -1
- package/dist/primitives/index.js +3 -3
- package/dist/state/index.d.ts +5 -5
- package/dist/state/index.js +5 -3
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +153 -40
- package/dist/surface/index.js +1 -1
- package/dist/ui/layers/index.js +1 -1
- package/dist/undo/index.d.ts +3 -3
- package/package.json +9 -9
- package/src/annotations/AnnotationOverlay.tsx +3 -0
- package/src/annotations/Annotations.less +4 -0
- package/src/annotations/Annotations.overlay.test.tsx +20 -0
- package/src/annotations/svgNodes.ts +1 -0
- package/src/annotations/toolMap.test.ts +9 -5
- package/src/annotations/toolMap.ts +3 -0
- package/src/canvas/CanvasStack.stories.tsx +1 -1
- package/src/canvas/usePanZoom.test.ts +28 -0
- package/src/canvas/usePanZoom.ts +11 -4
- package/src/chrome/ChromeRegions.stories.tsx +1 -1
- package/src/chrome/LabChrome.tsx +21 -5
- package/src/chrome/builtins.test.ts +1 -0
- package/src/chrome/builtins.tsx +1 -0
- package/src/chrome/index.ts +1 -0
- package/src/chrome/labTypes.ts +2 -2
- package/src/chrome/regions/PaletteRegion.tsx +1 -6
- package/src/chrome/regions/SidebarRegion.stories.tsx +44 -0
- package/src/chrome/regions/ToolbarRegion.tsx +11 -7
- package/src/chrome/types.ts +3 -0
- package/src/config/auto.test.ts +12 -0
- package/src/config/auto.ts +15 -0
- package/src/config/autoConfig.test.ts +107 -0
- package/src/config/autoConfig.ts +153 -0
- package/src/config/builder.test.ts +38 -0
- package/src/config/builder.ts +53 -8
- package/src/config/index.ts +3 -0
- package/src/config/path.ts +7 -2
- package/src/config/resolve.test.ts +25 -0
- package/src/config/resolve.ts +17 -1
- package/src/config/types.ts +32 -2
- package/src/config/useResolvedConfig.ts +26 -0
- package/src/controls/AutoControls.stories.tsx +88 -0
- package/src/controls/ControlPanel.stories.tsx +42 -1
- package/src/controls/ControlPanel.test.tsx +514 -4
- package/src/controls/ControlPanel.tsx +557 -80
- package/src/index.ts +29 -1
- package/src/instrument/serializers.ts +29 -1
- package/src/lab/Lab.chrome.test.tsx +16 -0
- package/src/lab/Lab.stories.tsx +1 -1
- package/src/lab/Lab.surface.test.tsx +7 -2
- package/src/lab/Lab.test.tsx +79 -1
- package/src/lab/Lab.tsx +105 -13
- package/src/lab/LabContext.ts +8 -0
- package/src/lab/LabFit.stories.tsx +4 -4
- package/src/lab/LabFullChrome.stories.tsx +1 -1
- package/src/lab/LabHeader.tsx +0 -1
- package/src/lab/LabRoot.test.tsx +56 -0
- package/src/lab/LabRoot.tsx +47 -0
- package/src/lab/LabShell.less +24 -3
- package/src/lab/LabShell.stories.tsx +50 -1
- package/src/lab/LabShell.tsx +18 -27
- package/src/lab/LabSwitcher.less +10 -10
- package/src/lab/LabSwitcher.test.tsx +14 -9
- package/src/lab/LabSwitcher.tsx +8 -4
- package/src/lab/Workspace.less +1 -1
- package/src/lab/Workspace.stories.tsx +1 -1
- package/src/lab/Workspace.surface.test.tsx +1 -1
- package/src/lab/index.ts +2 -0
- package/src/layers/LayerList.stories.tsx +1 -1
- package/src/loupe/AGENTS.md +16 -4
- package/src/loupe/LoupeGestures.tsx +67 -0
- package/src/loupe/TrialLoupe.tsx +43 -28
- package/src/loupe/index.ts +4 -0
- package/src/loupe/loupeActions.ts +98 -0
- package/src/loupe/useLoupe.test.tsx +59 -2
- package/src/loupe/useLoupe.ts +28 -48
- package/src/passthrough/weasel-ui.ts +3 -3
- package/src/primitives/FloatingPanel.stories.tsx +1 -1
- package/src/primitives/FpsMeter.stories.tsx +1 -1
- package/src/primitives/JobProgress.stories.tsx +1 -1
- package/src/primitives/Legend.stories.tsx +1 -1
- package/src/primitives/ScaleIndicator.stories.tsx +1 -1
- package/src/primitives/Sidebar.less +7 -0
- package/src/primitives/Sidebar.stories.tsx +1 -1
- package/src/primitives/Split.test.tsx +25 -0
- package/src/primitives/Split.tsx +36 -20
- package/src/primitives/StatusBar.stories.tsx +1 -1
- package/src/primitives/Toolbar.stories.tsx +1 -1
- package/src/specimen/Specimen.less +95 -0
- package/src/specimen/Specimen.stories.tsx +13 -0
- package/src/specimen/Specimen.test.tsx +13 -0
- package/src/specimen/Specimen.tsx +888 -0
- package/src/state/Persistence.stories.tsx +4 -4
- package/src/state/store.test.ts +65 -0
- package/src/state/store.ts +32 -8
- package/src/state/types.ts +26 -2
- package/src/state/useTrialState.test.tsx +85 -0
- package/src/state/useTrialState.ts +9 -1
- package/src/styles.less +1 -0
- package/src/surface/index.ts +1 -1
- package/src/surface/useSurfaceTile.test.tsx +1 -1
- package/src/surface/useTiledSurface.test.tsx +65 -10
- package/src/surface/useTiledSurface.ts +23 -11
- package/src/theme/Interstellar.stories.tsx +2 -2
- package/src/theme/interstellar.test.ts +16 -5
- package/src/theme/interstellar.theme.json +180 -0
- package/src/theme/interstellar.ts +8 -5
- package/src/tools/labTool.ts +3 -3
- package/src/trial/Trial.annotations.test.tsx +13 -4
- package/src/trial/Trial.canvas.test.tsx +32 -0
- package/src/trial/Trial.config.test.tsx +75 -2
- package/src/trial/Trial.less +4 -0
- package/src/trial/Trial.stories.tsx +4 -1
- package/src/trial/Trial.test.tsx +3 -0
- package/src/trial/Trial.tsx +21 -14
- package/src/trial/TrialChrome.tsx +17 -3
- package/src/trial/trialOps.test.ts +101 -1
- package/src/trial/trialOps.ts +62 -4
- package/dist/_dts/ToggleBar.d-DbHoWKIA.d.ts +0 -116
- package/dist/chunk-67SJLMC7.js.map +0 -1
- package/dist/chunk-6ZDGOZQV.js.map +0 -1
- package/dist/chunk-E44SU6XS.js +0 -50
- package/dist/chunk-E44SU6XS.js.map +0 -1
- package/dist/chunk-EKIICY6X.js.map +0 -1
- package/dist/chunk-NDRYLVOW.js.map +0 -1
- package/dist/chunk-NS64DXMN.js.map +0 -1
- package/dist/chunk-PGETSEDK.js.map +0 -1
- package/dist/chunk-QLSV2N3G.js.map +0 -1
- package/dist/chunk-S2SHZRD7.js +0 -7681
- package/dist/chunk-S2SHZRD7.js.map +0 -1
- package/dist/chunk-TQBBYTAF.js +0 -280
- package/dist/chunk-TQBBYTAF.js.map +0 -1
- package/dist/chunk-ZPH5WTWF.js.map +0 -1
- package/src/theme/interstellar.tokens.json +0 -61
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { auto } from '../config/auto';
|
|
3
|
+
import { f } from '../config/builder';
|
|
4
|
+
import { defineInstrument } from '../instrument/defineInstrument';
|
|
2
5
|
import type { Instrument } from '../instrument/types';
|
|
3
6
|
import type { TrialRecord } from '../state/types';
|
|
4
|
-
import { addTrial, cloneTrial, closeTrial, reorderTrials, resetTrial } from './trialOps';
|
|
7
|
+
import { addTrial, cloneTrial, closeTrial, reorderTrials, resetTrial, swapTrial } from './trialOps';
|
|
5
8
|
|
|
6
9
|
interface CounterState {
|
|
7
10
|
count: number;
|
|
@@ -38,10 +41,22 @@ const subjectInstrument: Instrument<{ label: string }, SubjectConfig> = {
|
|
|
38
41
|
render: () => null,
|
|
39
42
|
};
|
|
40
43
|
|
|
44
|
+
const schemaInstrument = defineInstrument({
|
|
45
|
+
name: 'Schemed',
|
|
46
|
+
config: f.schema({
|
|
47
|
+
cols: f.number(3).initial(auto),
|
|
48
|
+
gap: f.number(12),
|
|
49
|
+
grid: f.group({ size: f.number(4) }),
|
|
50
|
+
}),
|
|
51
|
+
initialState: () => ({}),
|
|
52
|
+
render: () => null,
|
|
53
|
+
});
|
|
54
|
+
|
|
41
55
|
const instruments: Instrument[] = [
|
|
42
56
|
counter as Instrument,
|
|
43
57
|
counterWithCanvas as Instrument,
|
|
44
58
|
subjectInstrument as Instrument,
|
|
59
|
+
schemaInstrument as Instrument,
|
|
45
60
|
];
|
|
46
61
|
|
|
47
62
|
function head<T>(arr: T[]): T {
|
|
@@ -107,6 +122,38 @@ describe('addTrial', () => {
|
|
|
107
122
|
const out = addTrial([], instruments, 'Subject', { config: { subject: undefined } });
|
|
108
123
|
expect(out[0]?.config).toEqual({ subject: 'none', zoom: 1 });
|
|
109
124
|
});
|
|
125
|
+
|
|
126
|
+
it('opens a trial with the schema-declared auto paths already unpinned', () => {
|
|
127
|
+
const out = addTrial([], instruments, 'Schemed');
|
|
128
|
+
expect(out[0]?.auto).toEqual(['cols']);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('honors auto in a seed config and keeps the sentinel out of the record', () => {
|
|
132
|
+
const out = addTrial([], instruments, 'Schemed', { config: { gap: auto } });
|
|
133
|
+
expect(out[0]?.auto).toContain('gap');
|
|
134
|
+
expect(out[0]?.autoSeed).toEqual(['gap']);
|
|
135
|
+
expect((out[0]?.config as { gap: number }).gap).toBe(12);
|
|
136
|
+
expect(out[0]?.configSeed).not.toHaveProperty('gap');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('keeps a seed value that is an object but not a config branch', () => {
|
|
140
|
+
const when = new Date('2020-01-01T00:00:00Z');
|
|
141
|
+
const out = addTrial([], instruments, 'Counter', {
|
|
142
|
+
config: { at: when } as unknown as Partial<CounterConfig>,
|
|
143
|
+
});
|
|
144
|
+
expect((out[0]?.config as { at: unknown }).at).toBe(when);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('honors auto at a nested seed path', () => {
|
|
148
|
+
const out = addTrial([], instruments, 'Schemed', { config: { grid: { size: auto } } });
|
|
149
|
+
expect(out[0]?.auto).toContain('grid.size');
|
|
150
|
+
expect((out[0]?.config as { grid: { size: number } }).grid.size).toBe(4);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('leaves a trial with no auto field when nothing is unpinned', () => {
|
|
154
|
+
const out = addTrial([], instruments, 'Counter');
|
|
155
|
+
expect(out[0]?.auto).toBeUndefined();
|
|
156
|
+
});
|
|
110
157
|
});
|
|
111
158
|
|
|
112
159
|
describe('cloneTrial', () => {
|
|
@@ -187,6 +234,20 @@ describe('resetTrial', () => {
|
|
|
187
234
|
expect(reset[0]?.state).toEqual({ label: 'ONE' });
|
|
188
235
|
expect(reset[1]?.config).toEqual({ subject: 'two', zoom: 1 });
|
|
189
236
|
});
|
|
237
|
+
|
|
238
|
+
it('un-pins the paths the trial opened auto again, from schema and from seed', () => {
|
|
239
|
+
const arr = addTrial([], instruments, 'Schemed', { config: { gap: auto } });
|
|
240
|
+
head(arr).auto = [];
|
|
241
|
+
const reset = resetTrial(arr, head(arr).id, instruments);
|
|
242
|
+
expect([...(reset[0]?.auto ?? [])].sort()).toEqual(['cols', 'gap']);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('pins a path the user un-pinned that the trial did not open on', () => {
|
|
246
|
+
const arr = addTrial([], instruments, 'Schemed');
|
|
247
|
+
head(arr).auto = ['cols', 'grid.size'];
|
|
248
|
+
const reset = resetTrial(arr, head(arr).id, instruments);
|
|
249
|
+
expect(reset[0]?.auto).toEqual(['cols']);
|
|
250
|
+
});
|
|
190
251
|
});
|
|
191
252
|
|
|
192
253
|
describe('reorderTrials', () => {
|
|
@@ -236,3 +297,42 @@ describe('a size-aware initial view', () => {
|
|
|
236
297
|
expect(trial?.view).toBeNull();
|
|
237
298
|
});
|
|
238
299
|
});
|
|
300
|
+
|
|
301
|
+
describe('swapTrial', () => {
|
|
302
|
+
const instruments = [counter, subjectInstrument];
|
|
303
|
+
const three = (): TrialRecord[] =>
|
|
304
|
+
addTrial(
|
|
305
|
+
addTrial(addTrial([], instruments, 'Counter'), instruments, 'Counter'),
|
|
306
|
+
instruments,
|
|
307
|
+
'Counter',
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
it('puts a fresh trial of the other instrument in the same place', () => {
|
|
311
|
+
const trials = three();
|
|
312
|
+
const target = trials[1] as TrialRecord;
|
|
313
|
+
const next = swapTrial(trials, instruments, target.id, 'Subject', {
|
|
314
|
+
config: { subject: 'moon' },
|
|
315
|
+
});
|
|
316
|
+
expect(next).toHaveLength(3);
|
|
317
|
+
expect(next[0]).toBe(trials[0]);
|
|
318
|
+
expect(next[2]).toBe(trials[2]);
|
|
319
|
+
const swapped = next[1] as TrialRecord;
|
|
320
|
+
expect(swapped.id).not.toBe(target.id);
|
|
321
|
+
expect(swapped.instrumentName).toBe('Subject');
|
|
322
|
+
expect(swapped.config).toEqual({ subject: 'moon', zoom: 1 });
|
|
323
|
+
expect(swapped.state).toEqual({ label: 'MOON' });
|
|
324
|
+
expect(swapped.configSeed).toEqual({ subject: 'moon' });
|
|
325
|
+
expect(swapped.undoStack).toEqual({ past: [], future: [] });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('keeps the width the trial’s sidebar was dragged to', () => {
|
|
329
|
+
const trials = three().map((t, i) => (i === 1 ? { ...t, sidebarWidth: 312 } : t));
|
|
330
|
+
const next = swapTrial(trials, instruments, (trials[1] as TrialRecord).id, 'Subject');
|
|
331
|
+
expect(next[1]?.sidebarWidth).toBe(312);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('returns the list untouched for an id it does not hold', () => {
|
|
335
|
+
const trials = three();
|
|
336
|
+
expect(swapTrial(trials, instruments, 'nope', 'Subject')).toBe(trials);
|
|
337
|
+
});
|
|
338
|
+
});
|
package/src/trial/trialOps.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAuto } from '../config/auto';
|
|
2
|
+
import { autoPathsOf } from '../config/autoConfig';
|
|
3
|
+
import { fillConfigDefaults, isRecord } from '../config/path';
|
|
4
|
+
import { unruledConfigSchema } from '../instrument/serializers';
|
|
2
5
|
import type { Instrument, InstrumentList } from '../instrument/types';
|
|
3
6
|
import { newId } from '../state/helpers';
|
|
4
7
|
import type { TrialRecord } from '../state/types';
|
|
@@ -37,6 +40,35 @@ function seedConfig<TC>(defaults: TC, seed: Partial<TC> | undefined): TC {
|
|
|
37
40
|
return seed ? fillConfigDefaults(seed, defaults) : defaults;
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
/** Splits a seed config into its ordinary values and the dotted paths it wrote
|
|
44
|
+
* as `auto`, so the sentinel never reaches the record. */
|
|
45
|
+
function splitAutoSeed(
|
|
46
|
+
seed: Record<string, unknown> | undefined,
|
|
47
|
+
at = '',
|
|
48
|
+
): { config: Record<string, unknown>; autoPaths: string[] } {
|
|
49
|
+
const config: Record<string, unknown> = {};
|
|
50
|
+
const autoPaths: string[] = [];
|
|
51
|
+
for (const [key, value] of Object.entries(seed ?? {})) {
|
|
52
|
+
const path = at === '' ? key : `${at}.${key}`;
|
|
53
|
+
if (isAuto(value)) {
|
|
54
|
+
autoPaths.push(path);
|
|
55
|
+
} else if (isRecord(value)) {
|
|
56
|
+
const inner = splitAutoSeed(value as Record<string, unknown>, path);
|
|
57
|
+
config[key] = inner.config;
|
|
58
|
+
autoPaths.push(...inner.autoPaths);
|
|
59
|
+
} else {
|
|
60
|
+
config[key] = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { config, autoPaths };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The paths an instrument's own schema declares as starting auto. */
|
|
67
|
+
function schemaAutoPaths(instrument: Instrument): string[] {
|
|
68
|
+
const schema = unruledConfigSchema(instrument);
|
|
69
|
+
return schema ? autoPathsOf(schema) : [];
|
|
70
|
+
}
|
|
71
|
+
|
|
40
72
|
/** Append a new trial running `instrumentName`, at that instrument's default
|
|
41
73
|
* config — with `options.config` written over it — and the initial state
|
|
42
74
|
* that config produces. */
|
|
@@ -47,7 +79,9 @@ export function addTrial(
|
|
|
47
79
|
options: AddTrialOptions = {},
|
|
48
80
|
): TrialRecord[] {
|
|
49
81
|
const instrument = findInstrument(instruments, instrumentName);
|
|
50
|
-
const
|
|
82
|
+
const seeded = splitAutoSeed(options.config as Record<string, unknown> | undefined);
|
|
83
|
+
const autoPaths = [...new Set([...schemaAutoPaths(instrument), ...seeded.autoPaths])];
|
|
84
|
+
const config = seedConfig(instrument.defaultConfig(), seeded.config);
|
|
51
85
|
const state = instrument.initialState(config);
|
|
52
86
|
const record: TrialRecord = {
|
|
53
87
|
id: newId(),
|
|
@@ -57,7 +91,9 @@ export function addTrial(
|
|
|
57
91
|
view: initialView(instrument),
|
|
58
92
|
undoStack: { past: [], future: [] },
|
|
59
93
|
};
|
|
60
|
-
if (options.config) record.configSeed =
|
|
94
|
+
if (options.config) record.configSeed = seeded.config;
|
|
95
|
+
if (seeded.autoPaths.length > 0) record.autoSeed = seeded.autoPaths;
|
|
96
|
+
if (autoPaths.length > 0) record.auto = autoPaths;
|
|
61
97
|
return [...trials, record];
|
|
62
98
|
}
|
|
63
99
|
|
|
@@ -99,15 +135,37 @@ export function resetTrial(
|
|
|
99
135
|
const instrument = findInstrument(instruments, current.instrumentName);
|
|
100
136
|
const config = seedConfig(instrument.defaultConfig(), current.configSeed);
|
|
101
137
|
const state = instrument.initialState(config);
|
|
138
|
+
const autoPaths = [...new Set([...schemaAutoPaths(instrument), ...(current.autoSeed ?? [])])];
|
|
139
|
+
const { auto: _unpinnedNow, ...kept } = current;
|
|
102
140
|
const reset: TrialRecord = {
|
|
103
|
-
...
|
|
141
|
+
...kept,
|
|
104
142
|
config,
|
|
105
143
|
state,
|
|
106
144
|
view: initialView(instrument),
|
|
107
145
|
};
|
|
146
|
+
if (autoPaths.length > 0) reset.auto = autoPaths;
|
|
108
147
|
return [...trials.slice(0, idx), reset, ...trials.slice(idx + 1)];
|
|
109
148
|
}
|
|
110
149
|
|
|
150
|
+
/** Put a fresh trial running `instrumentName` where trial `id` is. It gets a
|
|
151
|
+
* new id, since the history, snapshots and marks keyed by the old one belong
|
|
152
|
+
* to what that trial was running, but keeps the width its sidebar was
|
|
153
|
+
* dragged to. */
|
|
154
|
+
export function swapTrial(
|
|
155
|
+
trials: TrialRecord[],
|
|
156
|
+
instruments: InstrumentList,
|
|
157
|
+
id: string,
|
|
158
|
+
instrumentName: string,
|
|
159
|
+
options: AddTrialOptions = {},
|
|
160
|
+
): TrialRecord[] {
|
|
161
|
+
const idx = trials.findIndex((w) => w.id === id);
|
|
162
|
+
const current = trials[idx];
|
|
163
|
+
if (!current) return trials;
|
|
164
|
+
const [record] = addTrial([], instruments, instrumentName, options) as [TrialRecord];
|
|
165
|
+
if (current.sidebarWidth !== undefined) record.sidebarWidth = current.sidebarWidth;
|
|
166
|
+
return [...trials.slice(0, idx), record, ...trials.slice(idx + 1)];
|
|
167
|
+
}
|
|
168
|
+
|
|
111
169
|
/**
|
|
112
170
|
* Reorder to match `ids`. Ids the list doesn't mention keep their relative
|
|
113
171
|
* order at the end, and ids it names that no longer exist are dropped — a
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ButtonHTMLAttributes, ReactElement } from 'react';
|
|
3
|
-
|
|
4
|
-
/** Visual weight of a button. Defaults to `secondary`. */
|
|
5
|
-
type ButtonVariant = 'primary' | 'secondary' | 'ghost';
|
|
6
|
-
/** Button height and type scale. */
|
|
7
|
-
type ButtonSize = 'sm' | 'md';
|
|
8
|
-
type ButtonBase = {
|
|
9
|
-
variant?: ButtonVariant;
|
|
10
|
-
size?: ButtonSize;
|
|
11
|
-
/** A button that stays down: renders `aria-pressed` and holds the active
|
|
12
|
-
* treatment while it is on. For a control that reports a state rather than
|
|
13
|
-
* firing an action -- a panel toggle, a mode switch. Left off, the button
|
|
14
|
-
* announces no pressed state at all, which is what an action button wants. */
|
|
15
|
-
pressed?: boolean;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
loading?: boolean;
|
|
18
|
-
leadingIcon?: ReactNode;
|
|
19
|
-
trailingIcon?: ReactNode;
|
|
20
|
-
fullWidth?: boolean;
|
|
21
|
-
type?: 'button' | 'submit' | 'reset';
|
|
22
|
-
className?: string;
|
|
23
|
-
style?: ButtonHTMLAttributes<HTMLButtonElement>['style'];
|
|
24
|
-
children?: ReactNode;
|
|
25
|
-
onClick?: ButtonHTMLAttributes<HTMLButtonElement>['onClick'];
|
|
26
|
-
};
|
|
27
|
-
type ButtonRegular = ButtonBase & {
|
|
28
|
-
iconOnly?: false;
|
|
29
|
-
ariaLabel?: string;
|
|
30
|
-
};
|
|
31
|
-
type ButtonIconOnly = ButtonBase & {
|
|
32
|
-
iconOnly: true;
|
|
33
|
-
ariaLabel: string;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Props for {@link Button}. Setting `iconOnly` makes `ariaLabel` required,
|
|
37
|
-
* since an icon-only button has no text for a screen reader to announce.
|
|
38
|
-
*/
|
|
39
|
-
type ButtonProps = ButtonRegular | ButtonIconOnly;
|
|
40
|
-
/**
|
|
41
|
-
* Standard button. `loading` swaps the leading icon for a spinner, marks the
|
|
42
|
-
* button `aria-busy`, and leaves the label in place; it does not disable the
|
|
43
|
-
* button, so pass `disabled` too if the click should be blocked.
|
|
44
|
-
*
|
|
45
|
-
* `ref` forwards to the underlying `<button>`.
|
|
46
|
-
*/
|
|
47
|
-
declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
48
|
-
|
|
49
|
-
/** One segment of a {@link ToggleBar}, identified by its `value`. */
|
|
50
|
-
type ToggleBarItem<V extends string | number = string> = {
|
|
51
|
-
value: V;
|
|
52
|
-
label?: ReactNode;
|
|
53
|
-
ariaLabel?: string;
|
|
54
|
-
disabled?: boolean;
|
|
55
|
-
};
|
|
56
|
-
/** Segment height and type scale for a {@link ToggleBar}. */
|
|
57
|
-
type ToggleBarSize = 'sm' | 'md';
|
|
58
|
-
/** Visual treatment of a {@link ToggleBar}. */
|
|
59
|
-
type ToggleBarVariant = 'default' | 'minimal' | 'flat';
|
|
60
|
-
type CommonProps = {
|
|
61
|
-
ariaLabel?: string;
|
|
62
|
-
className?: string;
|
|
63
|
-
height?: number;
|
|
64
|
-
/** Size variant. `sm` is ~60% of the default height with reduced padding
|
|
65
|
-
* and font size — sized for dense surfaces like lab control panels. */
|
|
66
|
-
size?: ToggleBarSize;
|
|
67
|
-
/** Visual variant. `minimal` strips the pill track and glass treatment;
|
|
68
|
-
* selection becomes a flat accent. For dense diagnostic surfaces. */
|
|
69
|
-
variant?: ToggleBarVariant;
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Props for {@link ToggleBar}, discriminated on `mode`. Single mode is
|
|
73
|
-
* controlled by one value or `null`; multiple mode by an array, and only it
|
|
74
|
-
* accepts `mixedValues`.
|
|
75
|
-
*/
|
|
76
|
-
type ToggleBarProps<V extends string | number = string> = (CommonProps & {
|
|
77
|
-
mode?: 'single';
|
|
78
|
-
items: readonly ToggleBarItem<V>[];
|
|
79
|
-
value: V | null;
|
|
80
|
-
onChange: (next: V | null) => void;
|
|
81
|
-
allowDeselect?: boolean;
|
|
82
|
-
}) | (CommonProps & {
|
|
83
|
-
mode: 'multiple';
|
|
84
|
-
items: readonly ToggleBarItem<V>[];
|
|
85
|
-
value: readonly V[];
|
|
86
|
-
/**
|
|
87
|
-
* Values that are neither on nor off — the sources this bar
|
|
88
|
-
* aggregates disagree (a text range that is bold in part of it, a
|
|
89
|
-
* multi-selection whose nodes differ). Rendered `aria-pressed="mixed"`,
|
|
90
|
-
* the ARIA tri-state a toggle button actually has, rather than
|
|
91
|
-
* `SelectionPanel`'s reduced-opacity-plus-`title` workaround for
|
|
92
|
-
* `Switch`, which has no indeterminate state to render.
|
|
93
|
-
*
|
|
94
|
-
* `value` wins where the two lists overlap, so a caller that can't
|
|
95
|
-
* cheaply keep them disjoint doesn't get an ambiguous segment.
|
|
96
|
-
*
|
|
97
|
-
* Clicking a mixed segment turns it fully **on**, matching the
|
|
98
|
-
* everywhere-else convention (and `toggleFlagInRange`'s rule for a
|
|
99
|
-
* partially-styled text range) that a mixed toggle resolves toward
|
|
100
|
-
* the affirmative rather than clearing.
|
|
101
|
-
*/
|
|
102
|
-
mixedValues?: readonly V[];
|
|
103
|
-
onChange: (next: V[]) => void;
|
|
104
|
-
});
|
|
105
|
-
/**
|
|
106
|
-
* Segmented control for choosing among a fixed set of values — one of them
|
|
107
|
-
* (`mode: 'single'`, the default) or any number (`mode: 'multiple'`).
|
|
108
|
-
*
|
|
109
|
-
* Single mode ignores a click on the already-selected segment unless
|
|
110
|
-
* `allowDeselect` is set. Arrow keys move focus without changing the value;
|
|
111
|
-
* Space and Enter commit.
|
|
112
|
-
*/
|
|
113
|
-
declare function ToggleBar<V extends string | number = string>(props: ToggleBarProps<V>): ReactElement;
|
|
114
|
-
|
|
115
|
-
export { Button as B, ToggleBar as T };
|
|
116
|
-
export type { ButtonProps as a, ButtonSize as b, ButtonVariant as c, ToggleBarItem as d, ToggleBarProps as e, ToggleBarSize as f, ToggleBarVariant as g };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/state/document.ts","../src/state/undock.ts","../src/instrument/serializers.ts","../src/state/store.ts","../src/state/openLabStore.ts","../src/state/useTrialState.ts"],"names":[],"mappings":"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","file":"chunk-67SJLMC7.js","sourcesContent":["import { labStorageKey } from './helpers';\nimport type {\n LabDocument,\n LabMode,\n Migration,\n SavedSnapshot,\n SerializedTrial,\n StorageAdapter,\n} from './types';\nimport type { UndockedPanels } from './undock';\n\n/** Bumped whenever the persisted shape changes; every bump needs a migration. */\nexport const CURRENT_DOCUMENT_VERSION = 4;\n\n/** Where a version-3 lab kept its one document. Read once, to fold it into\n * records, then deleted. */\nexport function labDocumentKey(storageKey: string): string {\n return `lk:${storageKey}:doc`;\n}\n\n/** Where a document that failed to migrate is set aside, so a bad migration\n * loses state loudly rather than silently. */\nexport function quarantineKey(storageKey: string): string {\n return `lk:${storageKey}:quarantine`;\n}\n\n/** A fresh document at the current version. */\nexport function emptyDocument(mode: LabMode): LabDocument {\n return {\n version: CURRENT_DOCUMENT_VERSION,\n undockedPanels: {},\n trials: [],\n saves: [],\n layout: {},\n mode,\n };\n}\n\n/** What a migration run produced: the migrated document, or why it refused. */\nexport type MigrationOutcome =\n | { ok: true; doc: Record<string, unknown>; migrated: boolean }\n | { ok: false; reason: 'future' | 'failed'; error?: unknown };\n\n/** Walk a document forward to `target`, one migration at a time. A version\n * above `target` is refused rather than parsed under a shape it may not\n * have. */\nexport function runMigrations(\n raw: Record<string, unknown>,\n migrations: Migration[],\n target: number,\n): MigrationOutcome {\n const from = typeof raw.version === 'number' ? raw.version : 0;\n if (from > target) return { ok: false, reason: 'future' };\n if (from === target) return { ok: true, doc: raw, migrated: false };\n\n let doc = raw;\n try {\n for (let v = from; v < target; v++) {\n const migration = migrations[v];\n if (!migration) throw new Error(`[labkit] no migration from version ${v}`);\n doc = migration(doc);\n }\n } catch (error) {\n return { ok: false, reason: 'failed', error };\n }\n return { ok: true, doc, migrated: true };\n}\n\nconst LEGACY_BUCKETS = ['workspaces', 'saves', 'layout', 'theme'] as const;\n\n/** The four keys a lab used before it was one document. */\nexport function legacyKeys(storageKey: string): string[] {\n return LEGACY_BUCKETS.map((bucket) => labStorageKey(storageKey, bucket));\n}\n\n/** A value some substrate returned for an old key: already parsed, or a JSON\n * string from a store that does not parse. `undefined` when it is neither. */\nexport function parseStored(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nfunction bucketOr<T>(value: unknown, fallback: T, bucket: string, shape: 'array' | 'object'): T {\n if (value === undefined) return fallback;\n const parsed = parseStored(value);\n const fits = shape === 'array' ? Array.isArray(parsed) : !!parsed && typeof parsed === 'object';\n if (fits) return parsed as T;\n console.warn(`[labkit] unparseable legacy bucket \"${bucket}\", dropping it`);\n return fallback;\n}\n\n/** Assemble a version-0 document out of the four pre-document keys, or null\n * if none of them is present. `fallbackMode` is used when the theme key is\n * absent or holds something unrecognized; `interstellar` passes through\n * untouched for `migrateV0toV1` to coerce. */\nexport async function readLegacyDocument(\n read: (key: string) => Promise<unknown>,\n storageKey: string,\n fallbackMode: LabMode,\n): Promise<Record<string, unknown> | null> {\n const [workspaces, saves, layout, theme] = await Promise.all(\n LEGACY_BUCKETS.map((bucket) => read(labStorageKey(storageKey, bucket))),\n );\n if ([workspaces, saves, layout, theme].every((v) => v === undefined)) return null;\n\n const mode =\n theme === 'light' || theme === 'dark' || theme === 'auto' || theme === 'interstellar'\n ? theme\n : fallbackMode;\n\n return {\n version: 0,\n workspaces: bucketOr(workspaces, [], 'workspaces', 'array'),\n saves: bucketOr(saves, [], 'saves', 'array'),\n layout: bucketOr(layout, {}, 'layout', 'object'),\n mode,\n };\n}\n\n/** Whether two stored values hold the same data. Stored values are\n * structured-clone copies, so identity says nothing. */\nexport function sameStored(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/** Delete `keys` and read each back, so an adapter whose delete silently did\n * nothing is caught. `true` only once all of them are gone. */\nexport async function deleteConfirmed(storage: StorageAdapter, keys: string[]): Promise<boolean> {\n try {\n await Promise.all(keys.map((key) => storage.delete(key)));\n const left = await Promise.all(keys.map((key) => storage.get(key)));\n return left.every((v) => v === undefined);\n } catch {\n return false;\n }\n}\n\n/** Set `value` aside under the quarantine key, reading it back to confirm it\n * landed. Returns `false` when it did not — a full disk is exactly when a\n * document goes unreadable — so the caller can leave the original alone\n * rather than overwrite the only copy of it. */\nexport async function quarantineDocument(\n storage: StorageAdapter,\n storageKey: string,\n value: unknown,\n): Promise<boolean> {\n try {\n await storage.set(quarantineKey(storageKey), value);\n return sameStored(await storage.get(quarantineKey(storageKey)), value);\n } catch {\n return false;\n }\n}\n\n/** Fill in whatever a document is missing or holds the wrong shape of, so a\n * hydrated store never sees `undefined` where a section should be. Applied to\n * every document, migrated or already current. */\nexport function normalizeDocument(\n doc: Record<string, unknown>,\n fallbackMode: LabMode,\n): LabDocument {\n const mode =\n doc.mode === 'light' || doc.mode === 'dark' || doc.mode === 'auto' ? doc.mode : fallbackMode;\n return {\n version: CURRENT_DOCUMENT_VERSION,\n trials: Array.isArray(doc.trials) ? (doc.trials as SerializedTrial[]) : [],\n saves: Array.isArray(doc.saves) ? (doc.saves as SavedSnapshot[]) : [],\n layout:\n doc.layout && typeof doc.layout === 'object' ? (doc.layout as Record<string, unknown>) : {},\n undockedPanels:\n doc.undockedPanels && typeof doc.undockedPanels === 'object'\n ? (doc.undockedPanels as UndockedPanels)\n : {},\n mode,\n };\n}\n\n/** Version 0 (four loose keys) to version 1 (one document). Normalizes the\n * mode, including `interstellar` — the dark mode's name back when it was a\n * theme. Coerces against version 1's own field names rather than reusing\n * `normalizeDocument`, which produces the current shape and would drop\n * `workspaces` from a document it labels version 1. */\nexport function migrateV0toV1(doc: Record<string, unknown>): Record<string, unknown> {\n const raw = doc.mode === 'interstellar' ? 'dark' : doc.mode;\n const mode = raw === 'light' || raw === 'dark' || raw === 'auto' ? raw : 'auto';\n return {\n version: 1,\n workspaces: Array.isArray(doc.workspaces) ? doc.workspaces : [],\n saves: Array.isArray(doc.saves) ? doc.saves : [],\n layout: doc.layout && typeof doc.layout === 'object' ? doc.layout : {},\n mode,\n };\n}\n\n/** Version 1 to version 2: the vocabulary refresh renamed a tile from\n * workspace to trial. Only the records field moves; a record's own fields are\n * unchanged. */\nexport function migrateV1toV2(doc: Record<string, unknown>): Record<string, unknown> {\n const { workspaces, ...rest } = doc;\n return { ...rest, trials: Array.isArray(workspaces) ? workspaces : [], version: 2 };\n}\n\n/** Version 2 to version 3: undocked sidebar panels became persisted state.\n * Nothing existing moves; the field simply starts empty. */\nexport function migrateV2toV3(doc: Record<string, unknown>): Record<string, unknown> {\n return { ...doc, undockedPanels: {}, version: 3 };\n}\n\n/** Version 3 to version 4: a lab is stored as records under one prefix\n * instead of one document. The joined shape does not change. */\nexport function migrateV3toV4(doc: Record<string, unknown>): Record<string, unknown> {\n return { ...doc, version: 4 };\n}\n\n/** Index `i` migrates a version-`i` document to version `i + 1`. */\nexport const MIGRATIONS: Migration[] = [migrateV0toV1, migrateV1toV2, migrateV2toV3, migrateV3toV4];\n","/** A sidebar section torn out of its trial, and where it went. */\nexport interface UndockedPanel {\n trialId: string;\n sectionId: string;\n as: 'tile' | 'floating';\n}\n\n/** Undocked panels, keyed by {@link panelKey}. */\nexport type UndockedPanels = Record<string, UndockedPanel>;\n\nexport function panelKey(trialId: string, sectionId: string): string {\n return `${trialId}::${sectionId}`;\n}\n\nexport function undockPanel(\n panels: UndockedPanels,\n trialId: string,\n sectionId: string,\n as: UndockedPanel['as'] = 'tile',\n): UndockedPanels {\n return { ...panels, [panelKey(trialId, sectionId)]: { trialId, sectionId, as } };\n}\n\n/** Dock one section back, or — with no `sectionId` — every panel the trial\n * owns, which is what closing a trial wants. */\nexport function dockPanel(\n panels: UndockedPanels,\n trialId: string,\n sectionId?: string,\n): UndockedPanels {\n const out: UndockedPanels = {};\n for (const [key, panel] of Object.entries(panels)) {\n if (panel.trialId === trialId && (sectionId === undefined || panel.sectionId === sectionId)) {\n continue;\n }\n out[key] = panel;\n }\n return out;\n}\n","import type { InstrumentSerializers } from '../state/types';\nimport type { InstrumentList } from './types';\n\n/**\n * Each instrument's default config, keyed by name — what `createLabStore`\n * fills a stored config's gaps from, so one saved before its schema grew a\n * branch still loads.\n */\nexport function configDefaultsOf(instruments: InstrumentList): Record<string, () => unknown> {\n const out: Record<string, () => unknown> = {};\n for (const instrument of instruments) out[instrument.name] = () => instrument.defaultConfig();\n return out;\n}\n\n/** Each instrument's `migrateConfig`, keyed by name, for those that declare one. */\nexport function configMigrationsOf(\n instruments: InstrumentList,\n): Record<string, (stored: unknown) => unknown> {\n const out: Record<string, (stored: unknown) => unknown> = {};\n for (const { name, migrateConfig } of instruments) {\n if (migrateConfig) out[name] = (stored) => migrateConfig(stored);\n }\n return out;\n}\n\n/**\n * The `serialize` / `deserialize` an instrument list declares, keyed by\n * instrument name — what `createLabStore` needs to rebuild a trial's state\n * from storage.\n *\n * An instrument declaring neither gets no entry, which is what leaves a\n * JSON-safe state to pass through untouched.\n */\nexport function serializersOf(instruments: InstrumentList): InstrumentSerializers {\n const out: InstrumentSerializers = {};\n for (const instrument of instruments) {\n const { name, serialize, deserialize } = instrument;\n if (!serialize && !deserialize) continue;\n out[name] = {\n ...(serialize ? { serialize: (state: unknown) => serialize(state) } : {}),\n ...(deserialize\n ? { deserialize: (data: unknown, config: unknown) => deserialize(data, config) }\n : {}),\n };\n }\n return out;\n}\n","import { createStore, type StoreApi } from 'zustand/vanilla';\nimport { fillConfigDefaults, withValueAtPath } from '../config/path';\nimport { configDefaultsOf, configMigrationsOf, serializersOf } from '../instrument/serializers';\nimport type { InstrumentList } from '../instrument/types';\nimport { emptyDocument } from './document';\nimport { deserializeTrials, emptyUndoStack, newId } from './helpers';\nimport type {\n CreateLabStoreOptions,\n InstrumentHooks,\n InstrumentSerializers,\n LabMode,\n LabStoreState,\n SavedSnapshot,\n SerializedTrial,\n TrialRecord,\n} from './types';\nimport {\n dockPanel as dockPanelIn,\n type UndockedPanel,\n undockPanel as undockPanelIn,\n} from './undock';\n\n/** Every mutation a lab store supports: managing trials, saving and\n * restoring snapshots, and setting the color mode. */\nexport interface LabStoreActions {\n addTrial: (record: Omit<TrialRecord, 'undoStack'>) => void;\n removeTrial: (id: string) => void;\n updateTrialState: <TS>(id: string, next: TS | ((prev: TS) => TS)) => void;\n /** Write one config value. `path` is dotted for a value nested under an\n * `f.group`, and the bare key for one at the root. */\n updateTrialConfig: (id: string, path: string, value: unknown) => void;\n updateTrialView: (id: string, view: unknown) => void;\n updateTrialSidebarWidth: (id: string, width: number) => void;\n /** Retitle a trial. `null` returns it to its instrument's name. */\n setTrialTitle: (id: string, title: string | null) => void;\n /** Fold or unfold one of a trial's sections. */\n setTrialSectionCollapsed: (id: string, key: string, collapsed: boolean) => void;\n updateTrialAnnotations: (id: string, doc: unknown) => void;\n updateTrialUndoStack: (\n id: string,\n next: TrialRecord['undoStack'] | ((prev: TrialRecord['undoStack']) => TrialRecord['undoStack']),\n ) => void;\n setTrialInstrument: (id: string, instrumentName: string) => void;\n saveSnapshot: (trialId: string, name: string) => void;\n loadSnapshot: (snapshotId: string, trialId: string) => void;\n deleteSnapshot: (snapshotId: string) => void;\n listSnapshots: (trialId?: string) => SavedSnapshot[];\n setMode: (mode: LabMode) => void;\n setLabTool: (id: string | null) => void;\n setTrialTool: (trialId: string, id: string | null) => void;\n setLayout: (layout: Record<string, unknown>) => void;\n /** Tear a sidebar section out of its trial. Default target is a workspace tile. */\n undockPanel: (trialId: string, sectionId: string, as?: UndockedPanel['as']) => void;\n /** Put one section back, or — with no `sectionId` — every panel the trial owns. */\n dockPanel: (trialId: string, sectionId?: string) => void;\n /** Replace the lab's instruments. Trials and saves of an instrument that is\n * new or a different object are brought up to date against it in the same\n * write; every other trial keeps its object. */\n setInstruments: (instruments: InstrumentList) => void;\n /** The per-instrument hooks in force now. Read them at use; never keep them. */\n instrumentHooks: () => InstrumentHooks;\n}\n\n/** A lab's store: its state and its actions. How each instrument's state is\n * serialized comes in through `CreateLabStoreOptions.instruments`. */\nexport type LabStore = StoreApi<LabStoreState & LabStoreActions>;\n\n/** Build a lab store holding `initial`, or an empty lab. It knows nothing\n * about storage; `openLabStore` reads a stored lab and binds one of these to\n * it. */\nexport function createLabStore(options: CreateLabStoreOptions = {}): LabStore {\n let hooks: InstrumentHooks = options.instruments\n ? hooksOf(options.instruments)\n : {\n serializers: options.serializers ?? {},\n configDefaults: options.configDefaults ?? {},\n configMigrations: options.configMigrations ?? {},\n };\n const initial = options.initial ?? emptyDocument(options.initialMode ?? 'auto');\n\n const store = createStore<LabStoreState & LabStoreActions>()((set, get) => ({\n trials: hydrateTrials(initial.trials, hooks.serializers, hooks),\n savedSnapshots: hydrateSnapshots(initial.saves, hooks),\n mode: initial.mode,\n layout: initial.layout,\n undockedPanels: initial.undockedPanels,\n activeToolId: null,\n instruments: options.instruments ?? null,\n\n addTrial: (record) => {\n set((s) => ({\n trials: [...s.trials, { ...record, undoStack: emptyUndoStack() }],\n }));\n },\n\n removeTrial: (id) => {\n set((s) => ({\n trials: s.trials.filter((w) => w.id !== id),\n undockedPanels: dockPanelIn(s.undockedPanels, id),\n }));\n },\n\n updateTrialState: (id, next) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n const nextState =\n typeof next === 'function' ? (next as (prev: unknown) => unknown)(w.state) : next;\n // An updater that returns its input means \"nothing changed\", and must not\n // cost a new record: the trial re-renders on record identity, so\n // allocating here turns the standard React bail-out into a render loop.\n if (Object.is(nextState, w.state)) return w;\n return { ...w, state: nextState };\n }),\n }));\n },\n\n updateTrialConfig: (id, path, value) => {\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === id ? { ...w, config: withValueAtPath(w.config, path, value) } : w,\n ),\n }));\n },\n\n updateTrialView: (id, view) => {\n set((s) => ({\n trials: s.trials.map((w) => (w.id === id && !Object.is(view, w.view) ? { ...w, view } : w)),\n }));\n },\n\n updateTrialSidebarWidth: (id, width) => {\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === id && w.sidebarWidth !== width ? { ...w, sidebarWidth: width } : w,\n ),\n }));\n },\n\n setTrialTitle: (id, title) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n const next = title === null ? undefined : title;\n if ((w.title ?? undefined) === next) return w;\n return { ...w, title: next };\n }),\n }));\n },\n\n setTrialSectionCollapsed: (id, key, collapsed) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n if (w.collapsedSections?.[key] === collapsed) return w;\n return { ...w, collapsedSections: { ...w.collapsedSections, [key]: collapsed } };\n }),\n }));\n },\n\n updateTrialAnnotations: (id, doc) => {\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === id && !Object.is(doc, w.annotations) ? { ...w, annotations: doc } : w,\n ),\n }));\n },\n\n updateTrialUndoStack: (id, next) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n const undoStack =\n typeof next === 'function'\n ? (next as (prev: TrialRecord['undoStack']) => TrialRecord['undoStack'])(w.undoStack)\n : next;\n return { ...w, undoStack };\n }),\n }));\n },\n\n setTrialInstrument: (id, instrumentName) => {\n set((s) => ({\n trials: s.trials.map((w) => (w.id === id ? { ...w, instrumentName } : w)),\n }));\n },\n\n saveSnapshot: (trialId, name) => {\n const trial = get().trials.find((w) => w.id === trialId);\n if (!trial) return;\n const reg = hooks.serializers[trial.instrumentName];\n const serializedState = reg?.serialize\n ? reg.serialize(trial.state)\n : structuredClone(trial.state);\n const clonedConfig = structuredClone(trial.config);\n const lastAt = get().savedSnapshots.reduce((m, sn) => (sn.savedAt > m ? sn.savedAt : m), 0);\n const savedAt = Math.max(Date.now(), lastAt + 1);\n const snapshot: SavedSnapshot = {\n id: newId(),\n name,\n trialId,\n instrumentName: trial.instrumentName,\n config: clonedConfig,\n state: serializedState,\n savedAt,\n };\n set((s) => ({ savedSnapshots: [...s.savedSnapshots, snapshot] }));\n },\n\n loadSnapshot: (snapshotId, trialId) => {\n const snapshot = get().savedSnapshots.find((sn) => sn.id === snapshotId);\n if (!snapshot) return;\n const trial = get().trials.find((w) => w.id === trialId);\n if (!trial) return;\n if (snapshot.instrumentName !== trial.instrumentName) {\n console.warn(\n `[labkit] loadSnapshot: instrument mismatch (snapshot=${snapshot.instrumentName}, trial=${trial.instrumentName}); refusing to load`,\n );\n return;\n }\n const reg = hooks.serializers[snapshot.instrumentName];\n const restoredState = reg?.deserialize\n ? reg.deserialize(snapshot.state, snapshot.config)\n : snapshot.state;\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === trialId ? { ...w, state: restoredState, config: snapshot.config } : w,\n ),\n }));\n },\n\n deleteSnapshot: (snapshotId) => {\n set((s) => ({\n savedSnapshots: s.savedSnapshots.filter((sn) => sn.id !== snapshotId),\n }));\n },\n\n listSnapshots: (trialId) => {\n const all = get().savedSnapshots;\n const filtered = trialId ? all.filter((sn) => sn.trialId === trialId) : all;\n return [...filtered].sort((a, b) => b.savedAt - a.savedAt);\n },\n\n setMode: (mode) => {\n set({ mode });\n },\n\n setLabTool: (id) => set({ activeToolId: id }),\n\n setTrialTool: (trialId, id) =>\n set((s) => ({\n trials: s.trials.map((t) => (t.id === trialId ? { ...t, activeToolId: id } : t)),\n })),\n\n undockPanel: (trialId, sectionId, as) => {\n set((s) => ({ undockedPanels: undockPanelIn(s.undockedPanels, trialId, sectionId, as) }));\n },\n\n dockPanel: (trialId, sectionId) => {\n set((s) => ({ undockedPanels: dockPanelIn(s.undockedPanels, trialId, sectionId) }));\n },\n\n setLayout: (layout) => {\n set({ layout });\n },\n\n setInstruments: (instruments) => {\n const before = get().instruments;\n if (before === instruments) return;\n const next = mergedHooks(hooks, instruments);\n const previous = new Map((before ?? []).map((i) => [i.name, i]));\n const changed = new Set(\n instruments.filter((i) => previous.get(i.name) !== i).map((i) => i.name),\n );\n if (changed.size === 0) {\n hooks = next;\n set({ instruments });\n return;\n }\n const s = get();\n const trials = s.trials.map((w) =>\n changed.has(w.instrumentName)\n ? { ...w, config: hydratedConfig(next, w.instrumentName, w.config) }\n : w,\n );\n const savedSnapshots = s.savedSnapshots.map((sn) =>\n changed.has(sn.instrumentName)\n ? { ...sn, config: hydratedConfig(next, sn.instrumentName, sn.config) }\n : sn,\n );\n hooks = next;\n set({ instruments, trials, savedSnapshots });\n },\n\n instrumentHooks: () => hooks,\n }));\n\n return store;\n}\n\nfunction hooksOf(instruments: InstrumentList): InstrumentHooks {\n return {\n serializers: serializersOf(instruments),\n configDefaults: configDefaultsOf(instruments),\n configMigrations: configMigrationsOf(instruments),\n };\n}\n\n/** Hooks for `instruments`, keeping those of any instrument no longer listed:\n * its trials stay open, and their state still has to round-trip. A listed\n * name takes its hooks wholly from the new list. */\nfunction mergedHooks(old: InstrumentHooks, instruments: InstrumentList): InstrumentHooks {\n const listed = new Set(instruments.map((i) => i.name));\n const fresh = hooksOf(instruments);\n const unlisted = <T>(entries: Record<string, T>): Record<string, T> =>\n Object.fromEntries(Object.entries(entries).filter(([name]) => !listed.has(name)));\n return {\n serializers: { ...unlisted(old.serializers), ...fresh.serializers },\n configDefaults: { ...unlisted(old.configDefaults), ...fresh.configDefaults },\n configMigrations: { ...unlisted(old.configMigrations), ...fresh.configMigrations },\n };\n}\n\n/** How a stored config is brought up to date on the way in. */\nexport type ConfigHydration = Pick<CreateLabStoreOptions, 'configDefaults' | 'configMigrations'>;\n\nfunction hydratedConfig(\n hydration: ConfigHydration,\n instrumentName: string,\n config: unknown,\n): unknown {\n const migrate = hydration.configMigrations?.[instrumentName];\n const moved = migrate ? migrate(config) : config;\n const defaults = hydration.configDefaults?.[instrumentName];\n return defaults ? fillConfigDefaults(moved, defaults()) : moved;\n}\n\n/** Rebuild stored trials: migrate each config, fill its gaps from its\n * instrument's defaults, then run the deserializer against the result. */\nexport function hydrateTrials(\n trials: SerializedTrial[],\n serializers: InstrumentSerializers,\n hydration: ConfigHydration = {},\n): TrialRecord[] {\n return deserializeTrials(\n trials.map((t) => ({ ...t, config: hydratedConfig(hydration, t.instrumentName, t.config) })),\n serializers,\n );\n}\n\n/** Bring each stored snapshot's config up to date the way `hydrateTrials` does a trial's. */\nexport function hydrateSnapshots(\n saves: SavedSnapshot[],\n hydration: ConfigHydration = {},\n): SavedSnapshot[] {\n return saves.map((sn) => ({\n ...sn,\n config: hydratedConfig(hydration, sn.instrumentName, sn.config),\n }));\n}\n","import {\n CURRENT_DOCUMENT_VERSION,\n deleteConfirmed,\n emptyDocument,\n labDocumentKey,\n legacyKeys,\n MIGRATIONS,\n normalizeDocument,\n parseStored,\n quarantineDocument,\n readLegacyDocument,\n runMigrations,\n sameStored,\n} from './document';\nimport { serializeTrials } from './helpers';\nimport {\n compareTrialOrder,\n documentOfRecords,\n LAYOUT_RECORD,\n type LabMeta,\n labPrefix,\n META_RECORD,\n parseRecordName,\n recordsOfDocument,\n saveRecord,\n trialRecord,\n trialValuesPrefix,\n UNDOCK_RECORD,\n} from './labRecords';\nimport { type OwnedRecordCache, openRecords, type RecordCache } from './records';\nimport { createLabStore, hydrateSnapshots, hydrateTrials, type LabStore } from './store';\nimport type {\n CreateLabStoreOptions,\n LabDocument,\n LabMode,\n SavedSnapshot,\n SerializedTrial,\n StorageAdapter,\n TrialRecord,\n} from './types';\n\n/** Options for `openLabStore`. `storageKey` names the lab, so two labs on one\n * origin keep separate records. */\nexport interface OpenLabStoreOptions extends Omit<CreateLabStoreOptions, 'initial'> {\n storageKey: string;\n storage: StorageAdapter;\n}\n\n/** A lab store bound to its records. */\nexport interface OpenedLabStore {\n store: LabStore;\n /** Every record of the lab, `usePersistedState` values included. */\n records: RecordCache;\n /** Send queued writes and stop hearing other writers. */\n close(): Promise<void>;\n}\n\n/**\n * Read a stored lab and return a store bound to it: every change to the store\n * is written as the records it touched, and another writer's records are\n * applied back into the store. A lab stored before records existed is folded\n * forward on the way in.\n */\nexport async function openLabStore(options: OpenLabStoreOptions): Promise<OpenedLabStore> {\n const records = await openRecords({\n storage: options.storage,\n prefix: labPrefix(options.storageKey),\n });\n const { doc, orders } = await readLab(options, records);\n const store = createLabStore({ ...options, initial: doc });\n const unbind = bindLabStore(store, records, { orders });\n return {\n store,\n records,\n close: async () => {\n unbind();\n await records.close();\n },\n };\n}\n\ninterface ReadLab {\n doc: LabDocument;\n orders: Map<string, number>;\n}\n\nfunction emptyLab(mode: LabMode): ReadLab {\n return { doc: emptyDocument(mode), orders: new Map() };\n}\n\nfunction ordersOf(doc: LabDocument): Map<string, number> {\n return new Map(doc.trials.map((t, i) => [t.id, i]));\n}\n\nasync function readLab(options: OpenLabStoreOptions, records: OwnedRecordCache): Promise<ReadLab> {\n const mode = options.initialMode ?? 'auto';\n if (!records.writable) return emptyLab(mode);\n\n const joined = documentOfRecords(records.entries());\n if (!joined) return foldOldStorage(options, records);\n\n const outcome = runMigrations(joined.doc, MIGRATIONS, CURRENT_DOCUMENT_VERSION);\n if (outcome.ok) {\n return { doc: normalizeDocument(outcome.doc, mode), orders: joined.orders };\n }\n const why =\n outcome.reason === 'future' ? 'are from a newer version of labkit' : 'failed to migrate';\n console.warn(`[labkit] lab records ${why}; starting empty and leaving them alone`, outcome.error);\n records.stopWriting();\n return emptyLab(mode);\n}\n\n/** Fold a version-3 document, or the four keys before it, into records. The\n * old keys are deleted only once every record has been read back. */\nasync function foldOldStorage(\n options: OpenLabStoreOptions,\n records: OwnedRecordCache,\n): Promise<ReadLab> {\n const { storage, storageKey } = options;\n const mode = options.initialMode ?? 'auto';\n\n let raw: unknown;\n let oldKeys: string[];\n try {\n const stored = await storage.get(labDocumentKey(storageKey));\n if (stored !== undefined) {\n raw = stored;\n oldKeys = [labDocumentKey(storageKey)];\n } else {\n raw = await readLegacyDocument((key) => storage.get(key), storageKey, mode);\n oldKeys = legacyKeys(storageKey);\n if (raw === null) return emptyLab(mode);\n }\n } catch (error) {\n console.warn(\n `[labkit] could not read the old storage for \"${storageKey}\"; not persisting`,\n error,\n );\n records.stopWriting();\n return emptyLab(mode);\n }\n\n const setAside = async (value: unknown, why: string, error?: unknown): Promise<ReadLab> => {\n if (await quarantineDocument(storage, storageKey, value)) {\n console.warn(`[labkit] ${why}; quarantined it and starting empty`, error);\n await deleteConfirmed(storage, oldKeys);\n records.set(META_RECORD, { version: CURRENT_DOCUMENT_VERSION, mode } satisfies LabMeta);\n } else {\n console.warn(\n `[labkit] ${why} and could not be quarantined; leaving it in place and not persisting`,\n error,\n );\n records.stopWriting();\n }\n return emptyLab(mode);\n };\n\n const parsed = parseStored(raw);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return setAside(raw, 'lab document is unparseable');\n }\n const outcome = runMigrations(\n parsed as Record<string, unknown>,\n MIGRATIONS,\n CURRENT_DOCUMENT_VERSION,\n );\n if (!outcome.ok) {\n if (outcome.reason === 'future') {\n console.warn(\n '[labkit] lab document is from a newer version of labkit; starting empty and leaving it alone',\n );\n records.stopWriting();\n return emptyLab(mode);\n }\n return setAside(parsed, 'lab document failed to migrate', outcome.error);\n }\n\n const doc = normalizeDocument(outcome.doc, mode);\n const entries = recordsOfDocument(doc);\n records.seed(entries);\n if (await writeConfirmed(storage, records.prefix, entries)) {\n await deleteConfirmed(storage, oldKeys);\n } else {\n console.warn(\n `[labkit] keeping the old storage for \"${storageKey}\": could not confirm its records were written`,\n );\n }\n return { doc, orders: ordersOf(doc) };\n}\n\n/** Write `entries` in order and read every one back. */\nasync function writeConfirmed(\n storage: StorageAdapter,\n prefix: string,\n entries: [string, unknown][],\n): Promise<boolean> {\n try {\n for (const [name, value] of entries) await storage.set(prefix + name, value);\n const back = new Map(await storage.list(prefix));\n return entries.every(([name, value]) => sameStored(back.get(prefix + name), value));\n } catch {\n return false;\n }\n}\n\ninterface BindOptions {\n orders: Map<string, number>;\n}\n\nfunction persistedFieldsDiffer(a: TrialRecord, b: TrialRecord): boolean {\n if (a === b) return false;\n const left = a as unknown as Record<string, unknown>;\n const right = b as unknown as Record<string, unknown>;\n for (const key of new Set([...Object.keys(left), ...Object.keys(right)])) {\n if (key !== 'undoStack' && !Object.is(left[key], right[key])) return true;\n }\n return false;\n}\n\n/** Keep `store` and `records` in step, both ways. Returns the unbinding. */\nfunction bindLabStore(store: LabStore, records: RecordCache, options: BindOptions): () => void {\n const orderOf = new Map(options.orders);\n let applyingRemote = false;\n\n const nextOrder = (): number => {\n let max = -1;\n for (const order of orderOf.values()) max = Math.max(max, order);\n return max + 1;\n };\n\n const writeTrial = (trial: TrialRecord): void => {\n const [serialized] = serializeTrials([trial], store.getState().instrumentHooks().serializers);\n records.set(trialRecord(trial.id), { ...serialized, order: orderOf.get(trial.id) });\n };\n\n const inStoredOrder = (trials: TrialRecord[]): boolean => {\n for (let i = 1; i < trials.length; i++) {\n const a = trials[i - 1] as TrialRecord;\n const b = trials[i] as TrialRecord;\n const at = { trial: a, order: orderOf.get(a.id) ?? 0 };\n const bt = { trial: b, order: orderOf.get(b.id) ?? 0 };\n if (compareTrialOrder(at, bt) >= 0) return false;\n }\n return true;\n };\n\n const offStore = store.subscribe((state, prev) => {\n if (applyingRemote) return;\n let wrote = false;\n\n if (state.trials !== prev.trials) {\n const before = new Map(prev.trials.map((t) => [t.id, t]));\n const now = new Set(state.trials.map((t) => t.id));\n for (const t of prev.trials) {\n if (now.has(t.id)) continue;\n records.delete(trialRecord(t.id));\n for (const [name] of records.entries(trialValuesPrefix(t.id))) records.delete(name);\n orderOf.delete(t.id);\n wrote = true;\n }\n const added = state.trials.filter((t) => !before.has(t.id));\n for (const t of added) orderOf.set(t.id, nextOrder());\n if (inStoredOrder(state.trials)) {\n for (const t of state.trials) {\n const old = before.get(t.id);\n if (old && !persistedFieldsDiffer(old, t)) continue;\n writeTrial(t);\n wrote = true;\n }\n } else {\n // A reorder, or a trial inserted mid-list: renumber them all.\n for (const [i, t] of state.trials.entries()) orderOf.set(t.id, i);\n for (const t of state.trials) writeTrial(t);\n wrote = true;\n }\n }\n\n if (state.savedSnapshots !== prev.savedSnapshots) {\n const before = new Map(prev.savedSnapshots.map((s) => [s.id, s]));\n const now = new Set(state.savedSnapshots.map((s) => s.id));\n for (const s of state.savedSnapshots) {\n if (before.get(s.id) === s) continue;\n records.set(saveRecord(s.id), s);\n wrote = true;\n }\n for (const s of prev.savedSnapshots) {\n if (now.has(s.id)) continue;\n records.delete(saveRecord(s.id));\n wrote = true;\n }\n }\n\n if (state.layout !== prev.layout) {\n records.set(LAYOUT_RECORD, state.layout);\n wrote = true;\n }\n if (state.undockedPanels !== prev.undockedPanels) {\n records.set(UNDOCK_RECORD, state.undockedPanels);\n wrote = true;\n }\n if (state.mode !== prev.mode || (wrote && !records.has(META_RECORD))) {\n records.set(META_RECORD, {\n version: CURRENT_DOCUMENT_VERSION,\n mode: state.mode,\n } satisfies LabMeta);\n }\n });\n\n const offRecords = records.subscribe((changes) => {\n const remote = changes.filter((c) => c.origin === 'remote');\n if (remote.length === 0) return;\n\n const current = store.getState();\n let trials = current.trials;\n let saves = current.savedSnapshots;\n const patch: Partial<Pick<typeof current, 'layout' | 'undockedPanels' | 'mode'>> = {};\n\n for (const { name, value } of remote) {\n const parsed = parseRecordName(name);\n if (!parsed) continue;\n switch (parsed.kind) {\n case 'trial': {\n const id = parsed.id;\n if (value === undefined || !value || typeof value !== 'object') {\n trials = trials.filter((t) => t.id !== id);\n orderOf.delete(id);\n break;\n }\n const { order, ...rest } = value as SerializedTrial & { order?: unknown };\n orderOf.set(\n id,\n typeof order === 'number' && Number.isFinite(order) ? order : nextOrder(),\n );\n // Hydrating starts an empty undo history: the old one describes a\n // state that no longer exists.\n const hooks = store.getState().instrumentHooks();\n const [replacement] = hydrateTrials([{ ...rest, id }], hooks.serializers, hooks);\n if (!replacement) break;\n trials = trials.some((t) => t.id === id)\n ? trials.map((t) => (t.id === id ? replacement : t))\n : [...trials, replacement];\n break;\n }\n case 'save': {\n const id = parsed.id;\n if (value === undefined || !value || typeof value !== 'object') {\n saves = saves.filter((s) => s.id !== id);\n break;\n }\n const [snapshot] = hydrateSnapshots(\n [{ ...(value as SavedSnapshot), id }],\n store.getState().instrumentHooks(),\n );\n if (!snapshot) break;\n saves = saves.some((s) => s.id === id)\n ? saves.map((s) => (s.id === id ? snapshot : s))\n : [...saves, snapshot];\n break;\n }\n case 'layout':\n patch.layout = (value as Record<string, unknown> | undefined) ?? {};\n break;\n case 'undock':\n patch.undockedPanels = (value as typeof current.undockedPanels | undefined) ?? {};\n break;\n case 'meta': {\n const meta = value as Partial<LabMeta> | undefined;\n if (typeof meta?.version === 'number' && meta.version > CURRENT_DOCUMENT_VERSION) {\n console.warn(\n '[labkit] another tab wrote this lab with a newer labkit; no longer persisting',\n );\n if ('stopWriting' in records) (records as OwnedRecordCache).stopWriting();\n break;\n }\n if (meta?.mode === 'light' || meta?.mode === 'dark' || meta?.mode === 'auto') {\n patch.mode = meta.mode;\n }\n break;\n }\n }\n }\n\n if (trials !== current.trials) {\n trials = [...trials].sort((a, b) =>\n compareTrialOrder(\n { trial: a, order: orderOf.get(a.id) ?? 0 },\n { trial: b, order: orderOf.get(b.id) ?? 0 },\n ),\n );\n }\n applyingRemote = true;\n try {\n store.setState({ ...patch, trials, savedSnapshots: saves });\n } finally {\n applyingRemote = false;\n }\n });\n\n return () => {\n offStore();\n offRecords();\n };\n}\n","import { useContext } from 'react';\nimport { useStore } from 'zustand/react';\nimport { LabStoreContext, TrialIdContext } from './context';\nimport type { TrialStateHandle } from './types';\n\n/** An instrument's own state and config, plus setters. Reads the surrounding\n * trial id, so an instrument never has to know which trial it is\n * running in. Throws outside a lab store and trial. */\nexport function useTrialState<TS = unknown, TC = unknown>(): TrialStateHandle<TS, TC> {\n const ctx = useContext(LabStoreContext);\n if (!ctx) throw new Error('[labkit] useTrialState must be used inside <LabStoreProvider>');\n\n const trialId = useContext(TrialIdContext);\n if (!trialId) throw new Error('[labkit] useTrialState must be used inside <TrialIdProvider>');\n\n const record = useStore(ctx.store, (s) => s.trials.find((w) => w.id === trialId));\n\n if (!record) throw new Error(`[labkit] No trial found with id \"${trialId}\"`);\n\n const updateTrialState = useStore(ctx.store, (s) => s.updateTrialState);\n const updateTrialConfig = useStore(ctx.store, (s) => s.updateTrialConfig);\n\n return {\n state: record.state as TS,\n config: record.config as TC,\n setState: (next) => updateTrialState(trialId, next as Parameters<typeof updateTrialState>[1]),\n setConfig: (path, value) => updateTrialConfig(trialId, path, value),\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/builder.ts","../src/config/rules.ts","../src/config/resolve.ts","../src/config/useConfigSchema.ts"],"names":[],"mappings":";;;;;AAiBO,SAAS,eAAe,KAAA,EAA2C;AACxE,EAAA,OAAO,UAAA,IAAc,KAAA;AACvB;AAIO,IAAe,WAAf,MAAoD;AAAA,EAOzD,YACE,YAAA,EACS,WAAA,GAAqC,EAAC,EACtC,OAAA,GAAiC,EAAC,EAC3C;AAFS,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAET,IAAA,IAAA,CAAK,OAAA,GAAU,YAAA;AAAA,EACjB;AAAA,EAJW,WAAA;AAAA,EACA,OAAA;AAAA;AAAA;AAAA,EALF,OAAA;AAAA;AAAA;AAAA,EAYC,IAAA,CAAK,aAA0B,OAAA,EAA4B;AACnE,IAAA,MAAM,OAAO,IAAA,CAAK,WAAA;AAClB,IAAA,OAAO,IAAI,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,aAAa,OAAO,CAAA;AAAA,EACpD;AAAA,EAEU,IAAI,KAAA,EAA0B;AACtC,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,GAAG,IAAA,CAAK,aAAa,GAAG,KAAA,EAAM,EAAG,IAAA,CAAK,OAAO,CAAA;AAAA,EAClE;AAAA,EAEU,IAAI,KAAA,EAA0B;AACtC,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,WAAA,EAAa,EAAE,GAAG,IAAA,CAAK,OAAA,EAAS,GAAG,KAAA,EAAO,CAAA;AAAA,EAClE;AAAA;AAAA,EAGA,MAAM,IAAA,EAAoB;AACxB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,IAAA,EAAM,CAAA;AAAA,EAC1B;AAAA;AAAA,EAGA,SAAS,WAAA,EAA2B;AAClC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,WAAA,EAAa,CAAA;AAAA,EACjC;AAAA;AAAA,EAGA,MAAA,GAAe;AACb,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,MAAA,EAAQ,MAAM,CAAA;AAAA,EAClC;AAAA;AAAA,EAGA,KAAK,IAAA,EAAoB;AACvB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,IAAA,EAAM,CAAA;AAAA,EAC1B;AAAA;AAAA;AAAA,EAIA,OAAA,CAAQ,KAAA,EAAe,IAAA,GAAgC,EAAC,EAAS;AAC/D,IAAA,OAAO,IAAA,CAAK,IAAI,EAAE,OAAA,EAAS,EAAE,KAAA,EAAO,GAAG,IAAA,EAAK,EAAG,CAAA;AAAA,EACjD;AAAA;AAAA;AAAA,EAIA,OAAO,SAAA,EAA+D;AACpE,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,MAAA,EAAQ,WAAW,CAAA;AAAA,EACvC;AAAA;AAAA,EAGA,OAAO,QAAA,EAAiC;AACtC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,MAAA,EAAQ,UAAU,CAAA;AAAA,EACtC;AACF;AAEO,IAAM,UAAA,GAAN,cAAyB,QAAA,CAAiB;AAAA,EACtC,IAAA,GAAO,QAAA;AAAA;AAAA,EAGhB,KAAA,CAAM,KAAa,GAAA,EAAmB;AACpC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,GAAA,EAAK,KAAK,CAAA;AAAA,EAC9B;AAAA,EAEA,KAAK,IAAA,EAAoB;AACvB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,IAAA,EAAM,CAAA;AAAA,EAC1B;AAAA;AAAA;AAAA,EAIA,OAAO,MAAA,EAAsB;AAC3B,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,MAAA,EAAQ,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA,EAIA,OAAO,MAAA,EAAgC;AACrC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,MAAA,EAAQ,CAAA;AAAA,EAC5B;AAAA;AAAA,EAGA,MAAA,GAAe;AACb,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,OAAA,EAAS,UAAU,CAAA;AAAA,EACvC;AAAA;AAAA,EAGA,KAAA,GAAc;AACZ,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,OAAA,EAAS,SAAS,CAAA;AAAA,EACtC;AACF;AAEO,IAAM,WAAA,GAAN,cAA0B,QAAA,CAAkB;AAAA,EACxC,IAAA,GAAO,SAAA;AAAA;AAAA;AAAA,EAIhB,MAAA,GAAe;AACb,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,OAAA,EAAS,UAAU,CAAA;AAAA,EACvC;AACF;AAEO,IAAM,UAAA,GAAN,cAAyB,QAAA,CAAiB;AAAA,EACtC,IAAA,GAAO,QAAA;AAAA,EAEhB,YAAY,WAAA,EAA2B;AACrC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,WAAA,EAAa,CAAA;AAAA,EACjC;AAAA,EAEA,UAAU,SAAA,EAAyB;AACjC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,SAAA,EAAW,CAAA;AAAA,EAC/B;AAAA;AAAA,EAGA,SAAS,UAAA,EAA0B;AACjC,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,UAAA,EAAY,CAAA;AAAA,EAChC;AACF;AAEO,IAAM,SAAA,GAAN,cAAwB,QAAA,CAAiB;AAAA,EACrC,IAAA,GAAO,OAAA;AAClB;AAEO,IAAM,QAAA,GAAN,cAAyC,QAAA,CAAY;AAAA,EACjD,IAAA,GAAO,MAAA;AAAA;AAAA,EAGhB,KAAA,GAAc;AACZ,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,OAAA,EAAS,SAAS,CAAA;AAAA,EACtC;AAAA;AAAA;AAAA,EAIA,OAAO,OAAA,EAAwC;AAC7C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,OAAA,EAAS,CAAA;AAAA,EAC7B;AACF;AAGO,IAAM,SAAA,GAAN,cAA2B,QAAA,CAAY;AAAA,EACnC,IAAA,GAAO,IAAA;AAClB;AAGO,IAAM,UAAA,GAAN,MAAM,WAAA,SAAsB,QAAA,CAAY;AAAA,EAC7C,WAAA,CACW,MACT,YAAA,EACA,WAAA,GAAqC,EAAC,EACtC,OAAA,GAAiC,EAAC,EAClC;AACA,IAAA,KAAA,CAAM,YAAA,EAAc,aAAa,OAAO,CAAA;AAL/B,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAMX;AAAA,EANW,IAAA;AAAA,EAQQ,IAAA,CAAK,aAA0B,OAAA,EAA4B;AAC5E,IAAA,OAAO,IAAI,WAAA,CAAW,IAAA,CAAK,MAAM,IAAA,CAAK,OAAA,EAAS,aAAa,OAAO,CAAA;AAAA,EACrE;AACF;AAOO,IAAM,SAAA,GAAN,MAAM,UAAA,CAA4D;AAAA,EACvE,YACW,QAAA,EACA,WAAA,GAA2C,EAAC,EAC5C,OAAA,GAAmC,EAAC,EAC7C;AAHS,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EACR;AAAA,EAHQ,QAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EAGH,IAAA,CAAK,aAAgC,OAAA,EAAsC;AACjF,IAAA,OAAO,IAAI,UAAA,CAAU,IAAA,CAAK,QAAA,EAAU,aAAa,OAAO,CAAA;AAAA,EAC1D;AAAA;AAAA,EAGA,MAAM,IAAA,EAA4B;AAChC,IAAA,OAAO,IAAA,CAAK,KAAK,EAAE,GAAG,KAAK,WAAA,EAAa,IAAA,EAAK,EAAG,IAAA,CAAK,OAAO,CAAA;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,WAAA,EAAmC;AAC1C,IAAA,OAAO,IAAA,CAAK,KAAK,EAAE,GAAG,KAAK,WAAA,EAAa,WAAA,EAAY,EAAG,IAAA,CAAK,OAAO,CAAA;AAAA,EACrE;AAAA;AAAA,EAGA,OAAA,CAAQ,KAAA,EAAe,IAAA,GAAgC,EAAC,EAAiB;AACvE,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,WAAA,EAAa,EAAE,GAAG,IAAA,CAAK,OAAA,EAAS,OAAA,EAAS,EAAE,KAAA,EAAO,GAAG,IAAA,IAAQ,CAAA;AAAA,EACrF;AAAA;AAAA;AAAA,EAIA,OAAO,SAAA,EAAuE;AAC5E,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,WAAA,EAAa,EAAE,GAAG,IAAA,CAAK,OAAA,EAAS,MAAA,EAAQ,SAAA,EAAW,CAAA;AAAA,EAC3E;AACF;AAGA,SAAS,cAAc,KAAA,EAA6C;AAClE,EAAA,MAAM,MAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAChD,IAAA,GAAA,CAAI,GAAG,IAAI,cAAA,CAAe,KAAK,IAAI,aAAA,CAAc,KAAA,CAAM,QAAQ,CAAA,GAAI,KAAA,CAAM,OAAA;AAAA,EAC3E;AACA,EAAA,OAAO,GAAA;AACT;AAEA,IAAM,SAAS,CACb,OAAA,KAEA,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAO,OAAO,CAAA,KAAM,QAAA,GAAW,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,CAAA,KAAM,CAAE,CAAA;AAelE,IAAM,CAAA,GAAI;AAAA,EACf,MAAA,EAAQ,CAAC,GAAA,KAA4B,IAAI,WAAW,GAAG,CAAA;AAAA,EACvD,OAAA,EAAS,CAAC,GAAA,KAA8B,IAAI,YAAY,GAAG,CAAA;AAAA,EAC3D,MAAA,EAAQ,CAAC,GAAA,KAA4B,IAAI,WAAW,GAAG,CAAA;AAAA,EACvD,KAAA,EAAO,CAAC,GAAA,KAA2B,IAAI,UAAU,GAAG,CAAA;AAAA;AAAA;AAAA;AAAA,EAKpD,IAAA,EAAM,CACJ,GAAA,EACA,OAAA,KACgB,IAAI,QAAA,CAAY,GAAA,EAAK,EAAE,OAAA,EAAS,MAAA,CAAO,OAAO,CAAA,EAAG,CAAA;AAAA;AAAA;AAAA,EAInE,KAAA,EAAO,CAAI,GAAA,KAAyB,IAAI,UAAU,GAAG,CAAA;AAAA;AAAA;AAAA;AAAA,EAKrD,KAAA,EAAO,CAA8B,QAAA,KAA8B,IAAI,UAAU,QAAQ,CAAA;AAAA;AAAA,EAGzF,QAAQ,CAAI,IAAA,EAAc,GAAA,EAAQ,QAAA,KAChC,IAAI,UAAA,CAAW,IAAA,EAAM,GAAA,EAAK,IAAI,QAAA,GAAW,EAAE,QAAA,EAAS,GAAI,EAAE,CAAA;AAAA;AAAA,EAG5D,OAA8B,KAAA,EAAwC;AACpE,IAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,MAAM,aAAA,CAAc,KAAK,CAAA,EAAoB;AAAA,EACzE;AACF;;;AChSO,SAAS,UAAU,GAAA,EAAqB;AAC7C,EAAA,MAAM,KAAA,GAAQ,GAAA,CACX,OAAA,CAAQ,QAAA,EAAU,GAAG,CAAA,CACrB,KAAA,CAAM,wBAAwB,CAAA,CAC9B,IAAA,CAAK,GAAG,CAAA,CACR,IAAA,GACA,WAAA,EAAY;AACf,EAAA,OAAO,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,KAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,CAAE,WAAA,EAAY,GAAI,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA;AAC5E;AAGA,IAAM,aAAA,GAA4B,CAAC,GAAA,KAAQ;AACzC,EAAA,MAAM,CAAA,GAAI,OAAO,GAAA,CAAI,OAAA;AACrB,EAAA,IAAI,CAAA,KAAM,aAAa,CAAA,KAAM,QAAA,IAAY,MAAM,QAAA,EAAU,OAAO,EAAE,IAAA,EAAM,CAAA,EAAE;AAC1E,EAAA,OAAO,IAAA;AACT,CAAA;AAEA,IAAM,YAAA,GAA2B,CAAC,GAAA,MAAS,EAAE,MAAM,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA,EAAE,CAAA;AAGtE,IAAM,iBAAA,GAAgC,CAAC,GAAA,KAAQ;AAC7C,EAAA,IAAI,GAAA,CAAI,IAAA,CAAK,IAAA,KAAS,QAAA,EAAU,OAAO,IAAA;AACvC,EAAA,MAAM,UAAU,GAAA,CAAI,IAAA,CAAK,QAAQ,MAAA,IAAa,GAAA,CAAI,KAAK,GAAA,KAAQ,MAAA;AAC/D,EAAA,OAAO,OAAA,GAAU,EAAE,OAAA,EAAS,QAAA,EAAS,GAAI,IAAA;AAC3C,CAAA;AAGA,IAAM,kBAAA,GAAiC,OAAO,EAAE,WAAA,EAAa,EAAA,EAAG,CAAA;AASzD,IAAM,YAAA,GAAsC;AAAA,EACjD,aAAA;AAAA,EACA,YAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF;AAOO,SAAS,UAAA,CACd,IAAA,EACA,GAAA,EACA,KAAA,EACW;AACX,EAAA,MAAM,GAAA,GAAiB,EAAE,GAAG,IAAA,EAAK;AACjC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,QAAQ,IAAA,CAAK,EAAE,GAAG,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AACxC,IAAA,IAAI,UAAU,IAAA,EAAM;AACpB,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC1C,MAAA,IAAI,CAAA,KAAM,MAAA,IAAa,GAAA,CAAI,CAAoB,MAAM,MAAA,EAAW;AAC9D,QAAC,GAAA,CAAgC,CAAC,CAAA,GAAI,CAAA;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;;;ACnDA,SAAS,QAAQ,WAAA,EAAiD;AAChE,EAAA,MAAM,MAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,EAAG,IAAI,CAAA,KAAM,MAAA,EAAW,GAAA,CAAI,CAAC,CAAA,GAAI,CAAA;AAChF,EAAA,OAAO,GAAA;AACT;AAmBO,SAAS,mBAAA,CACd,MAAA,EACA,KAAA,GAA+B,EAAC,EAChB;AAChB,EAAA,MAAM,IAAA,GAAa;AAAA,IACjB,UAAU,EAAC;AAAA,IACX,MAAA,sBAAY,GAAA,EAAI;AAAA,IAChB,WAAW,EAAC;AAAA,IACZ,KAAA,EAAO,CAAC,GAAG,KAAA,EAAO,GAAG,YAAY;AAAA,GACnC;AACA,EAAA,MAAM,QAAQ,YAAA,CAAa,MAAA,CAAO,KAAA,EAAO,EAAA,EAAI,IAAI,IAAI,CAAA;AACrD,EAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,QAAQ,IAAA,CAAK,MAAA,EAAQ,SAAA,EAAW,IAAA,CAAK,SAAA,EAAU;AAC1F;AAKA,SAAS,YAAA,CAAa,KAAA,EAAoB,EAAA,EAAY,IAAA,EAAc,IAAA,EAAuB;AACzF,EAAA,MAAM,WAAiD,EAAC;AACxD,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAyB;AAE9C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAChD,IAAA,MAAM,OAAO,EAAA,KAAO,EAAA,GAAK,MAAM,CAAA,EAAG,EAAE,IAAI,GAAG,CAAA,CAAA;AAC3C,IAAA,QAAA,CAAS,GAAG,CAAA,GAAI,YAAA,CAAa,KAAA,EAAO,GAAA,EAAK,MAAM,IAAI,CAAA;AAEnD,IAAA,MAAM,EAAE,OAAA,EAAS,MAAA,EAAQ,SAAA,KAAc,KAAA,CAAM,OAAA;AAC7C,IAAA,IAAI,YAAY,MAAA,EAAW;AACzB,MAAA,IAAI,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA;AACrC,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,IAAA,GAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,KAAA,EAAO,KAAA,EAAO,EAAC,EAAE;AAC7C,QAAA,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,IAAI,CAAA;AAChC,QAAA,IAAA,CAAK,QAAA,CAAS,KAAK,IAAI,CAAA;AAAA,MACzB;AACA,MAAA,IAAA,CAAK,KAAA,CAAM,KAAK,IAAI,CAAA;AAGpB,MAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,QAAA,IAAA,CAAK,SAAA,GAAA,CAAa,IAAA,CAAK,SAAA,IAAa,KAAA,KAAU,OAAA,CAAQ,SAAA;AAAA,MACxD;AAAA,IACF;AACA,IAAA,IAAI,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,MAAM,SAAS,CAAA;AAC9C,IAAA,IAAI,CAAC,cAAA,CAAe,KAAK,CAAA,IAAK,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,MAAA;AAAA,EAC3F;AAEA,EAAA,OAAO,EAAE,MAAM,QAAA,EAAS;AAC1B;AAEA,SAAS,YAAA,CACP,KAAA,EACA,GAAA,EACA,IAAA,EACA,IAAA,EACsB;AACtB,EAAA,IAAI,cAAA,CAAe,KAAK,CAAA,EAAG;AACzB,IAAA,MAAM,KAAA,GAAQ,YAAA;AAAA,MACZ,KAAA,CAAM,QAAA;AAAA,MACN,IAAA;AAAA,MACA,KAAA,CAAM,WAAA,CAAY,IAAA,IAAQ,SAAA,CAAU,GAAG,CAAA;AAAA,MACvC;AAAA,KACF;AACA,IAAA,OAAO,KAAA,CAAM,WAAA,CAAY,WAAA,KAAgB,MAAA,GACrC,KAAA,GACA,EAAE,GAAG,KAAA,EAAO,WAAA,EAAa,KAAA,CAAM,WAAA,CAAY,WAAA,EAAY;AAAA,EAC7D;AACA,EAAA,MAAM,OAAkB,OAAA,CAAQ;AAAA,IAC9B,GAAG,KAAA,CAAM,WAAA;AAAA,IACT,GAAI,MAAM,IAAA,KAAS,IAAA,GAAO,EAAC,GAAI,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA;AAAK,GACnD,CAAA;AACD,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,EAAE,GAAA,EAAK,IAAA,EAAM,OAAA,EAAS,KAAA,CAAM,OAAA,EAAQ,EAAG,IAAA,CAAK,KAAK,CAAA;AAChF,EAAA,OAAO,EAAE,GAAG,KAAA,EAAO,OAAA,EAAS,MAAM,OAAA,EAAQ;AAC5C;ACtGA,IAAM,WAAwB,EAAC;AASxB,SAAS,gBAEd,UAAA,EACgB;AAChB,EAAA,MAAM,GAAA,GAAM,WAAW,UAAU,CAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,KAAK,WAAA,IAAe,QAAA;AAClC,EAAA,OAAO,OAAA;AAAA,IACL,MACE,UAAA,CAAW,MAAA,GACP,mBAAA,CAAoB,UAAA,CAAW,MAAA,EAAQ,KAAK,CAAA,GAC5C,gBAAA,CAAiB,UAAA,CAAW,YAAA,IAAe,IAAK,EAAE,CAAA;AAAA,IACxD,CAAC,YAAY,KAAK;AAAA,GACpB;AACF","file":"chunk-6ZDGOZQV.js","sourcesContent":["import type { PrefLeaf, PrefNumberFormat } from '@weasel-js/ui';\nimport type {\n Annotations,\n BranchAnnotations,\n BranchOptions,\n ConfigBranch,\n ConfigEntry,\n ConfigNode,\n ConfigOption,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n InferConfig,\n NodeOptions,\n} from './types';\n\n/** Whether a schema entry is a branch rather than a leaf. */\nexport function isConfigBranch(entry: ConfigEntry): entry is ConfigBranch {\n return 'children' in entry;\n}\n\n/** Shared chaining surface. Every method clones, so a node can be reused as a\n * base for several leaves without one bleeding into the next. */\nexport abstract class BaseNode<T> implements ConfigNode<T> {\n abstract readonly kind: string | null;\n\n // `default` is a reserved word in a parameter position, so the field is\n // declared rather than taken as a parameter property.\n readonly default: T;\n\n constructor(\n defaultValue: T,\n readonly annotations: Readonly<Annotations> = {},\n readonly options: Readonly<NodeOptions> = {},\n ) {\n this.default = defaultValue;\n }\n\n /** Clone into the same subclass. Subclasses with extra constructor\n * arguments override this. */\n protected with(annotations: Annotations, options: NodeOptions): this {\n const Ctor = this.constructor as new (d: T, a: Annotations, o: NodeOptions) => this;\n return new Ctor(this.default, annotations, options);\n }\n\n protected ann(patch: Annotations): this {\n return this.with({ ...this.annotations, ...patch }, this.options);\n }\n\n protected opt(patch: NodeOptions): this {\n return this.with(this.annotations, { ...this.options, ...patch });\n }\n\n /** Human-readable label. Defaults to the key, title-cased. */\n label(name: string): this {\n return this.ann({ name });\n }\n\n /** Longer help text, surfaced as a tooltip on the row label. */\n describe(description: string): this {\n return this.ann({ description });\n }\n\n /** Omit from the panel unless it is asked to show hidden leaves. */\n hidden(): this {\n return this.ann({ hidden: true });\n }\n\n /** Pair with a sibling sharing this id, side-by-side on one row. */\n pair(pair: string): this {\n return this.ann({ pair });\n }\n\n /** Render under a named section heading. `collapsed` opens the section\n * folded — say it on any one of the section's leaves. */\n section(label: string, opts: { collapsed?: boolean } = {}): this {\n return this.opt({ section: { label, ...opts } });\n }\n\n /** Show this row only while the predicate holds. Presentational — the value\n * stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): this {\n return this.opt({ showIf: predicate });\n }\n\n /** Draw this one row yourself, keeping the kind, default and validation. */\n render(renderer: ControlRenderer): this {\n return this.opt({ render: renderer });\n }\n}\n\nexport class NumberNode extends BaseNode<number> {\n readonly kind = 'number';\n\n /** Bound the value. A number with both bounds renders as a slider. */\n range(min: number, max: number): this {\n return this.ann({ min, max });\n }\n\n step(step: number): this {\n return this.ann({ step });\n }\n\n /** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:\n * it is never parsed, and the stored value stays a plain number. */\n suffix(suffix: string): this {\n return this.ann({ suffix });\n }\n\n /** Show the value in a named format — `'compact'` reads `2.0M`. Presentation\n * only: the stored value stays a plain number, and a typed `2.5m` reads back. */\n format(format: PrefNumberFormat): this {\n return this.ann({ format });\n }\n\n /** Force a slider even without both bounds. */\n slider(): this {\n return this.ann({ control: 'slider' });\n }\n\n /** Force a typed input even when both bounds are set. */\n input(): this {\n return this.ann({ control: 'input' });\n }\n}\n\nexport class BooleanNode extends BaseNode<boolean> {\n readonly kind = 'boolean';\n\n /** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's\n * `PrefsForm` honors the distinction. */\n toggle(): this {\n return this.ann({ control: 'switch' });\n }\n}\n\nexport class StringNode extends BaseNode<string> {\n readonly kind = 'string';\n\n placeholder(placeholder: string): this {\n return this.ann({ placeholder });\n }\n\n maxLength(maxLength: number): this {\n return this.ann({ maxLength });\n }\n\n /** Milliseconds to debounce live writes. 0 commits every keystroke. */\n debounce(debounceMs: number): this {\n return this.ann({ debounceMs });\n }\n}\n\nexport class ColorNode extends BaseNode<string> {\n readonly kind = 'color';\n}\n\nexport class EnumNode<T extends string> extends BaseNode<T> {\n readonly kind = 'enum';\n\n /** Render as a segmented control rather than a select. */\n radio(): this {\n return this.ann({ control: 'radio' });\n }\n\n /** Relabel the choices. Named `labels` rather than `options` because\n * `options` is the node's own extras bag. */\n labels(options: readonly ConfigOption[]): this {\n return this.ann({ options });\n }\n}\n\n/** A leaf whose kind the rule chain decides. */\nexport class ValueNode<T> extends BaseNode<T> {\n readonly kind = null;\n}\n\n/** A leaf of a kind labkit does not ship a row for. */\nexport class CustomNode<T> extends BaseNode<T> {\n constructor(\n readonly kind: string,\n defaultValue: T,\n annotations: Readonly<Annotations> = {},\n options: Readonly<NodeOptions> = {},\n ) {\n super(defaultValue, annotations, options);\n }\n\n protected override with(annotations: Annotations, options: NodeOptions): this {\n return new CustomNode(this.kind, this.default, annotations, options) as this;\n }\n}\n\n/**\n * A branch of the schema: its children nest under its key, so a leaf inside\n * one is read and written at a dotted path. Distinct from `.section()`, which\n * puts a heading over sibling leaves and leaves their paths alone.\n */\nexport class GroupNode<S extends ConfigShape> implements ConfigBranch<S> {\n constructor(\n readonly children: S,\n readonly annotations: Readonly<BranchAnnotations> = {},\n readonly options: Readonly<BranchOptions> = {},\n ) {}\n\n private with(annotations: BranchAnnotations, options: BranchOptions): GroupNode<S> {\n return new GroupNode(this.children, annotations, options);\n }\n\n /** Heading for the group's rows. Defaults to the key, title-cased. */\n label(name: string): GroupNode<S> {\n return this.with({ ...this.annotations, name }, this.options);\n }\n\n /** Longer help text. Carried on the resolved `PrefGroup`, where weasel-ui's\n * `PrefsForm` draws it under the heading; `ControlPanel` has no place for\n * it yet and shows the heading alone. */\n describe(description: string): GroupNode<S> {\n return this.with({ ...this.annotations, description }, this.options);\n }\n\n /** Render this whole group under a named section heading. */\n section(label: string, opts: { collapsed?: boolean } = {}): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, section: { label, ...opts } });\n }\n\n /** Show this group only while the predicate holds. Presentational — every\n * value beneath it stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, showIf: predicate });\n }\n}\n\n/** Every default in a shape, nested the way the shape is. */\nfunction shapeDefaults(shape: ConfigShape): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(shape)) {\n out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;\n }\n return out;\n}\n\nconst expand = <T extends string>(\n options: readonly T[] | readonly ConfigOption[],\n): readonly ConfigOption[] =>\n options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o));\n\n/**\n * Builds an instrument's config: the values, their types, and how each is\n * edited, in one declaration.\n *\n * ```ts\n * const config = f.schema({\n * showGrid: f.boolean(true),\n * cellSize: f.number(20).range(5, 80).step(5).label('Grid spacing'),\n * // Nested: read and written at `grid.color`.\n * grid: f.group({ color: f.color('#ffffff') }),\n * })\n * ```\n */\nexport const f = {\n number: (def: number): NumberNode => new NumberNode(def),\n boolean: (def: boolean): BooleanNode => new BooleanNode(def),\n string: (def: string): StringNode => new StringNode(def),\n color: (def: string): ColorNode => new ColorNode(def),\n\n /** A fixed set of choices. The default's literal type flows into the config\n * type, so `f.enum('fast', ['fast', 'accurate'])` gives\n * `'fast' | 'accurate'` with no `as const`. */\n enum: <const T extends string>(\n def: T,\n options: readonly T[] | readonly ConfigOption[],\n ): EnumNode<T> => new EnumNode<T>(def, { options: expand(options) }),\n\n /** A leaf with no declared kind: the rule chain decides, which is how a lab\n * states a convention like \"every `*Color` key is a color picker\". */\n value: <T>(def: T): ValueNode<T> => new ValueNode(def),\n\n /** A branch: its children nest under this key, so `grid: f.group({ size })`\n * puts the value at `config.grid.size` and addresses it as `'grid.size'`.\n * Groups nest as deeply as the schema wants. */\n group: <const S extends ConfigShape>(children: S): GroupNode<S> => new GroupNode(children),\n\n /** A leaf of a kind a lab supplies the control for, through `controls`. */\n custom: <T>(kind: string, def: T, validate?: (leaf: PrefLeaf) => string[]): CustomNode<T> =>\n new CustomNode(kind, def, {}, validate ? { validate } : {}),\n\n /** Collect leaves and groups into an instrument's config. */\n schema<S extends ConfigShape>(nodes: S): ConfigSchema<InferConfig<S>> {\n return { nodes, defaults: () => shapeDefaults(nodes) as InferConfig<S> };\n },\n};\n","import type { ConfigRule, ConfigRuleContext, LeafPatch } from './types';\n\n/** `cellSize` -> `Cell size`. Splits camelCase without shattering acronyms. */\nexport function titleCase(key: string): string {\n const words = key\n .replace(/[_-]+/g, ' ')\n .split(/(?<=[a-z0-9])(?=[A-Z])/)\n .join(' ')\n .trim()\n .toLowerCase();\n return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);\n}\n\n/** An `f.value` leaf takes its kind from the shape of its default. */\nconst kindFromValue: ConfigRule = (ctx) => {\n const t = typeof ctx.default;\n if (t === 'boolean' || t === 'number' || t === 'string') return { kind: t };\n return null;\n};\n\nconst labelFromKey: ConfigRule = (ctx) => ({ name: titleCase(ctx.key) });\n\n/** A number bounded on both sides is a slider; one bound or none is an input. */\nconst sliderWhenBounded: ConfigRule = (ctx) => {\n if (ctx.leaf.kind !== 'number') return null;\n const bounded = ctx.leaf.min !== undefined && ctx.leaf.max !== undefined;\n return bounded ? { control: 'slider' } : null;\n};\n\n/** `PrefBase.description` is required, so every leaf must end up with one. */\nconst descriptionDefault: ConfigRule = () => ({ description: '' });\n\n/**\n * labkit's own inference, expressed in the same vocabulary a consumer\n * extends. Runs after the consumer's rules, so a lab always wins.\n *\n * If one of these ever needs something a `ConfigRule` cannot express, the\n * seam is too weak — that is the point of writing them this way.\n */\nexport const builtinRules: readonly ConfigRule[] = [\n kindFromValue,\n labelFromKey,\n sliderWhenBounded,\n descriptionDefault,\n];\n\n/**\n * Fold a rule chain into one leaf patch. Gap-filling: a property already\n * settled is never overwritten, so the accumulator seeded with the author's\n * annotations beats every rule, and earlier rules beat later ones.\n */\nexport function applyRules(\n seed: LeafPatch,\n ctx: Omit<ConfigRuleContext, 'leaf'>,\n rules: readonly ConfigRule[],\n): LeafPatch {\n const acc: LeafPatch = { ...seed };\n for (const rule of rules) {\n const patch = rule({ ...ctx, leaf: acc });\n if (patch === null) continue;\n for (const [k, v] of Object.entries(patch)) {\n if (v !== undefined && acc[k as keyof LeafPatch] === undefined) {\n (acc as Record<string, unknown>)[k] = v;\n }\n }\n }\n return acc;\n}\n","import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\nimport { isConfigBranch } from './builder';\nimport { applyRules, builtinRules, titleCase } from './rules';\nimport type {\n ConfigEntry,\n ConfigRule,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n LeafPatch,\n ResolvedConfig,\n SectionSpec,\n} from './types';\n\n/** Drop keys whose value is undefined, so an unset annotation stays a gap the\n * rule chain can fill rather than a settled `undefined`. */\nfunction defined(annotations: Record<string, unknown>): LeafPatch {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(annotations)) if (v !== undefined) out[k] = v;\n return out as LeafPatch;\n}\n\n/** What the walk collects on the side of the `PrefGroup` tree. */\ninterface Sink {\n sections: SectionSpec[];\n showIf: Map<string, (config: Record<string, unknown>) => boolean>;\n renderers: Record<string, ControlRenderer>;\n chain: readonly ConfigRule[];\n}\n\n/**\n * Resolve a schema into the vocabulary weasel-ui renders, running each leaf\n * through the consumer's rules and then labkit's own.\n *\n * The group mirrors the schema's own nesting: an `f.group` becomes a nested\n * `PrefGroup`, so a leaf's dotted path within the tree is the path its value\n * is written at, and both `ControlPanel` and `PrefsForm` address it\n * identically.\n */\nexport function resolveConfigSchema<TC>(\n schema: ConfigSchema<TC>,\n rules: readonly ConfigRule[] = [],\n): ResolvedConfig {\n const sink: Sink = {\n sections: [],\n showIf: new Map(),\n renderers: {},\n chain: [...rules, ...builtinRules],\n };\n const group = resolveShape(schema.nodes, '', '', sink);\n return { group, sections: sink.sections, showIf: sink.showIf, renderers: sink.renderers };\n}\n\n/** A section while it is still being filled. */\ntype OpenSection = Omit<SectionSpec, 'paths'> & { paths: string[] };\n\nfunction resolveShape(shape: ConfigShape, at: string, name: string, sink: Sink): PrefGroup {\n const children: Record<string, PrefLeaf | PrefGroup> = {};\n const sections = new Map<string, OpenSection>();\n\n for (const [key, entry] of Object.entries(shape)) {\n const path = at === '' ? key : `${at}.${key}`;\n children[key] = resolveEntry(entry, key, path, sink);\n\n const { section, showIf: predicate } = entry.options;\n if (section !== undefined) {\n let spec = sections.get(section.label);\n if (!spec) {\n spec = { at, label: section.label, paths: [] };\n sections.set(section.label, spec);\n sink.sections.push(spec);\n }\n spec.paths.push(path);\n // One node saying `collapsed` settles the section, so a schema does not\n // have to repeat it on every node under the heading.\n if (section.collapsed !== undefined) {\n spec.collapsed = (spec.collapsed ?? false) || section.collapsed;\n }\n }\n if (predicate) sink.showIf.set(path, predicate);\n if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;\n }\n\n return { name, children };\n}\n\nfunction resolveEntry(\n entry: ConfigEntry,\n key: string,\n path: string,\n sink: Sink,\n): PrefLeaf | PrefGroup {\n if (isConfigBranch(entry)) {\n const group = resolveShape(\n entry.children,\n path,\n entry.annotations.name ?? titleCase(key),\n sink,\n );\n return entry.annotations.description === undefined\n ? group\n : { ...group, description: entry.annotations.description };\n }\n const seed: LeafPatch = defined({\n ...entry.annotations,\n ...(entry.kind === null ? {} : { kind: entry.kind }),\n });\n const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);\n return { ...patch, default: entry.default } as PrefLeaf;\n}\n","import { useContext, useMemo } from 'react';\nimport type { Instrument } from '../instrument/types';\nimport { LabContext } from '../lab/LabContext';\nimport { fromConfigFields } from './fromConfigField';\nimport { resolveConfigSchema } from './resolve';\nimport type { ResolvedConfig } from './types';\n\nconst NO_RULES: readonly [] = [];\n\n/**\n * An instrument's controls, resolved against the surrounding lab's rules.\n *\n * Resolution cannot happen in `defineInstrument` — rules are lab-scoped and\n * that runs at module load — so it happens here, at render. Both instrument\n * paths land in the same shape, so callers never branch on which was used.\n */\nexport function useConfigSchema(\n // biome-ignore lint/suspicious/noExplicitAny: instruments are stored contravariantly; see InstrumentList\n instrument: Instrument<any, any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n): ResolvedConfig {\n const lab = useContext(LabContext);\n const rules = lab?.configRules ?? NO_RULES;\n return useMemo(\n () =>\n instrument.config\n ? resolveConfigSchema(instrument.config, rules)\n : fromConfigFields(instrument.configSchema?.() ?? []),\n [instrument, rules],\n );\n}\n"]}
|
package/dist/chunk-E44SU6XS.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// src/config/path.ts
|
|
2
|
-
function isRecord(value) {
|
|
3
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
function valueAtPath(config, path) {
|
|
6
|
-
let cur = config;
|
|
7
|
-
for (const segment of path.split(".")) {
|
|
8
|
-
if (!isRecord(cur)) return void 0;
|
|
9
|
-
cur = cur[segment];
|
|
10
|
-
}
|
|
11
|
-
return cur;
|
|
12
|
-
}
|
|
13
|
-
function hasConfigPath(config, path) {
|
|
14
|
-
let cur = config;
|
|
15
|
-
for (const segment of path.split(".")) {
|
|
16
|
-
if (!isRecord(cur) || !(segment in cur)) return false;
|
|
17
|
-
cur = cur[segment];
|
|
18
|
-
}
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
function writeIn(node, segments, value) {
|
|
22
|
-
const [head, ...rest] = segments;
|
|
23
|
-
if (head === void 0) return value;
|
|
24
|
-
const base = isRecord(node) ? node : {};
|
|
25
|
-
return { ...base, [head]: rest.length === 0 ? value : writeIn(base[head], rest, value) };
|
|
26
|
-
}
|
|
27
|
-
function withValueAtPath(config, path, value) {
|
|
28
|
-
return writeIn(config, path.split("."), value);
|
|
29
|
-
}
|
|
30
|
-
function fillConfigDefaults(stored, defaults) {
|
|
31
|
-
if (!isRecord(defaults)) return stored === void 0 ? defaults : stored;
|
|
32
|
-
const base = isRecord(stored) ? stored : {};
|
|
33
|
-
const out = { ...base };
|
|
34
|
-
for (const [key, value] of Object.entries(defaults)) {
|
|
35
|
-
out[key] = fillConfigDefaults(base[key], value);
|
|
36
|
-
}
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
function schemaNodeAtPath(group, path) {
|
|
40
|
-
let node = group;
|
|
41
|
-
for (const segment of path.split(".")) {
|
|
42
|
-
if (node === void 0 || !("children" in node)) return void 0;
|
|
43
|
-
node = node.children[segment];
|
|
44
|
-
}
|
|
45
|
-
return node;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export { fillConfigDefaults, hasConfigPath, schemaNodeAtPath, valueAtPath, withValueAtPath };
|
|
49
|
-
//# sourceMappingURL=chunk-E44SU6XS.js.map
|
|
50
|
-
//# sourceMappingURL=chunk-E44SU6XS.js.map
|