@weasel-js/labkit 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/_dts/{DrawCommand-uKHt4Vul.d.ts → DrawCommand-BkZztJsW.d.ts} +3 -1
- package/dist/_dts/{index-CFlDPeZh.d.ts → index-iAP6XbH3.d.ts} +17 -7
- package/dist/_dts/{useExperimentState-vrttakTt.d.ts → useTrialState-DYe2vUwN.d.ts} +62 -34
- package/dist/canvas/index.d.ts +1 -1
- package/dist/chunk-3TYUJR7Z.js +488 -0
- package/dist/chunk-3TYUJR7Z.js.map +1 -0
- package/dist/{chunk-N5KTQKQA.js → chunk-C6GJKPUI.js} +835 -770
- package/dist/chunk-C6GJKPUI.js.map +1 -0
- package/dist/{chunk-3WPOGKUP.js → chunk-DJLDIRFN.js} +543 -226
- package/dist/chunk-DJLDIRFN.js.map +1 -0
- package/dist/{chunk-73PXCRCR.js → chunk-NRKWVTVT.js} +6 -6
- package/dist/chunk-NRKWVTVT.js.map +1 -0
- package/dist/dragdrop/index.d.ts +1 -1
- package/dist/index.d.ts +197 -148
- package/dist/index.js +275 -161
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +2 -2
- package/dist/passthrough/weasel-canvas.d.ts +1 -1
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +10 -3
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/state/index.d.ts +14 -14
- package/dist/state/index.js +5 -5
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +179 -14
- package/dist/ui/layers/index.js +3 -3
- package/dist/undo/index.d.ts +1 -1
- package/package.json +2 -1
- package/src/canvas/AGENTS.md +6 -2
- package/src/dragdrop/dragDrop.test.tsx +4 -7
- package/src/index.test.ts +52 -0
- package/src/index.ts +11 -7
- package/src/instrument/SineWave.smoke.test.tsx +1 -1
- package/src/instrument/capabilityDetector.ts +1 -1
- package/src/instrument/types.ts +18 -6
- package/src/lab/Lab.less +16 -0
- package/src/lab/Lab.stories.tsx +5 -5
- package/src/lab/Lab.test.tsx +25 -25
- package/src/lab/Lab.tsx +56 -44
- package/src/lab/LabContext.ts +12 -11
- package/src/lab/LabShell.tsx +1 -1
- package/src/lab/Workspace.less +40 -0
- package/src/lab/{WorkspaceGrid.stories.tsx → Workspace.stories.tsx} +27 -11
- package/src/lab/Workspace.test.tsx +114 -0
- package/src/lab/Workspace.tsx +196 -0
- package/src/lab/index.ts +2 -4
- package/src/layers/AGENTS.md +3 -3
- package/src/primitives/Toolbar.stories.tsx +2 -2
- package/src/state/SingletonExperiment.test.tsx +8 -8
- package/src/state/SingletonExperiment.tsx +9 -9
- package/src/state/context.tsx +12 -12
- package/src/state/document.test.ts +369 -0
- package/src/state/document.ts +194 -0
- package/src/state/helpers.test.ts +74 -46
- package/src/state/helpers.ts +23 -31
- package/src/state/index.ts +16 -8
- package/src/state/store.test.ts +440 -40
- package/src/state/store.ts +166 -90
- package/src/state/types.ts +30 -10
- package/src/state/{useExperimentState.test.tsx → useTrialState.test.tsx} +18 -18
- package/src/state/useTrialState.ts +29 -0
- package/src/styles.less +2 -2
- package/src/test-setup.ts +19 -0
- package/src/theme/base.less +1 -1
- package/src/{workspace → trial}/DefaultSidebar.tsx +3 -3
- package/src/{workspace → trial}/DefaultStatusBar.tsx +5 -4
- package/src/{workspace → trial}/DefaultToolbar.tsx +9 -8
- package/src/trial/Trial.canvas.test.tsx +80 -0
- package/src/{workspace/Workspace.less → trial/Trial.less} +1 -1
- package/src/{workspace/Workspace.stories.tsx → trial/Trial.stories.tsx} +13 -17
- package/src/{workspace/Workspace.test.tsx → trial/Trial.test.tsx} +26 -25
- package/src/{workspace/Workspace.tsx → trial/Trial.tsx} +48 -43
- package/src/{workspace/WorkspaceChrome.tsx → trial/TrialChrome.tsx} +42 -42
- package/src/{workspace → trial}/index.ts +13 -12
- package/src/{workspace → trial}/slotTypes.ts +16 -16
- package/src/{workspace/workspaceOps.test.ts → trial/trialOps.test.ts} +60 -33
- package/src/trial/trialOps.ts +99 -0
- package/src/ui/format.test.ts +33 -0
- package/src/ui/format.ts +11 -0
- package/src/ui/properties/CurveField.tsx +6 -6
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +3 -3
- package/dist/chunk-3WPOGKUP.js.map +0 -1
- package/dist/chunk-73PXCRCR.js.map +0 -1
- package/dist/chunk-N5KTQKQA.js.map +0 -1
- package/dist/chunk-T7OKNJTY.js +0 -351
- package/dist/chunk-T7OKNJTY.js.map +0 -1
- package/src/lab/WorkspaceGrid.less +0 -8
- package/src/lab/WorkspaceGrid.test.tsx +0 -40
- package/src/lab/WorkspaceGrid.tsx +0 -22
- package/src/lab/gridDims.test.ts +0 -35
- package/src/lab/gridDims.ts +0 -13
- package/src/state/useExperimentState.ts +0 -31
- package/src/workspace/workspaceOps.ts +0 -86
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import {
|
|
3
3
|
decodeUrlHash,
|
|
4
|
-
|
|
4
|
+
deserializeTrials,
|
|
5
5
|
emptyUndoStack,
|
|
6
6
|
encodeUrlHash,
|
|
7
7
|
labStorageKey,
|
|
8
|
-
|
|
8
|
+
serializeTrials,
|
|
9
9
|
} from './helpers';
|
|
10
|
-
import type { WorkspaceRecord } from './types';
|
|
11
10
|
|
|
12
11
|
describe('labStorageKey', () => {
|
|
13
12
|
it('produces namespaced keys', () => {
|
|
@@ -19,7 +18,7 @@ describe('labStorageKey', () => {
|
|
|
19
18
|
|
|
20
19
|
describe('encodeUrlHash / decodeUrlHash', () => {
|
|
21
20
|
it('round-trips a string', () => {
|
|
22
|
-
const original = JSON.stringify({
|
|
21
|
+
const original = JSON.stringify({ trials: '[]', saves: '[]' });
|
|
23
22
|
expect(decodeUrlHash(encodeUrlHash(original))).toBe(original);
|
|
24
23
|
});
|
|
25
24
|
|
|
@@ -35,55 +34,84 @@ describe('emptyUndoStack', () => {
|
|
|
35
34
|
});
|
|
36
35
|
});
|
|
37
36
|
|
|
38
|
-
describe('
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
37
|
+
describe('serializeTrials', () => {
|
|
38
|
+
it('returns records with the undo stack dropped', () => {
|
|
39
|
+
const records = serializeTrials(
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
id: 'w1',
|
|
43
|
+
instrumentName: 'Test',
|
|
44
|
+
config: { a: 1 },
|
|
45
|
+
state: { b: 2 },
|
|
46
|
+
view: { zoom: 1, pan: { x: 0, y: 0 } },
|
|
47
|
+
undoStack: { past: [{ b: 1 }], future: [] },
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
{},
|
|
51
|
+
);
|
|
52
|
+
expect(records).toEqual([
|
|
53
|
+
{
|
|
54
|
+
id: 'w1',
|
|
55
|
+
instrumentName: 'Test',
|
|
56
|
+
config: { a: 1 },
|
|
57
|
+
state: { b: 2 },
|
|
58
|
+
view: { zoom: 1, pan: { x: 0, y: 0 } },
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
59
61
|
});
|
|
60
62
|
|
|
61
|
-
it('
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
it('runs the instrument serializer over the state', () => {
|
|
64
|
+
const records = serializeTrials(
|
|
65
|
+
[
|
|
66
|
+
{
|
|
67
|
+
id: 'w1',
|
|
68
|
+
instrumentName: 'Test',
|
|
69
|
+
config: {},
|
|
70
|
+
state: { n: 2 },
|
|
71
|
+
view: { zoom: 1, pan: { x: 0, y: 0 } },
|
|
72
|
+
undoStack: { past: [], future: [] },
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
{ Test: { serialize: (s) => ({ doubled: (s as { n: number }).n * 2 }) } },
|
|
76
|
+
);
|
|
77
|
+
expect(records[0].state).toEqual({ doubled: 4 });
|
|
65
78
|
});
|
|
79
|
+
});
|
|
66
80
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
describe('deserializeTrials', () => {
|
|
82
|
+
it('rebuilds records with an empty undo stack', () => {
|
|
83
|
+
const out = deserializeTrials(
|
|
84
|
+
[
|
|
85
|
+
{
|
|
86
|
+
id: 'w1',
|
|
87
|
+
instrumentName: 'Test',
|
|
88
|
+
config: {},
|
|
89
|
+
state: { n: 1 },
|
|
90
|
+
view: { zoom: 1, pan: { x: 0, y: 0 } },
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
{},
|
|
94
|
+
);
|
|
95
|
+
expect(out[0].undoStack).toEqual({ past: [], future: [] });
|
|
77
96
|
});
|
|
78
97
|
|
|
79
|
-
it('
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
98
|
+
it('runs the instrument deserializer over the state', () => {
|
|
99
|
+
const out = deserializeTrials(
|
|
100
|
+
[
|
|
101
|
+
{
|
|
102
|
+
id: 'w1',
|
|
103
|
+
instrumentName: 'Test',
|
|
104
|
+
config: {},
|
|
105
|
+
state: { doubled: 4 },
|
|
106
|
+
view: { zoom: 1, pan: { x: 0, y: 0 } },
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
{ Test: { deserialize: (d) => ({ n: (d as { doubled: number }).doubled / 2 }) } },
|
|
110
|
+
);
|
|
111
|
+
expect(out[0].state).toEqual({ n: 2 });
|
|
83
112
|
});
|
|
84
113
|
|
|
85
|
-
it('returns empty
|
|
86
|
-
|
|
87
|
-
expect(result).toEqual([]);
|
|
114
|
+
it('returns an empty list when given something that is not an array', () => {
|
|
115
|
+
expect(deserializeTrials(undefined as never, {})).toEqual([]);
|
|
88
116
|
});
|
|
89
117
|
});
|
package/src/state/helpers.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { InstrumentSerializers,
|
|
1
|
+
import type { InstrumentSerializers, SerializedTrial, TrialRecord, UndoStack } from './types';
|
|
2
2
|
|
|
3
|
-
/** The storage key a lab writes one of its
|
|
3
|
+
/** The storage key a lab writes one of its buckets under. Namespaced by
|
|
4
4
|
* `storageKey` so two labs sharing an origin do not collide. */
|
|
5
5
|
export function labStorageKey(
|
|
6
6
|
storageKey: string,
|
|
7
|
-
bucket: 'workspaces' | 'saves' | 'theme',
|
|
7
|
+
bucket: 'workspaces' | 'saves' | 'theme' | 'layout',
|
|
8
8
|
): string {
|
|
9
9
|
return `lk:${storageKey}:${bucket}`;
|
|
10
10
|
}
|
|
@@ -30,44 +30,36 @@ export function emptyUndoStack(): UndoStack {
|
|
|
30
30
|
return { past: [], future: [] };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/** Serialize workspaces for storage, running each instrument's own serializer
|
|
33
|
+
/** Serialize trials for storage, running each instrument's own serializer
|
|
36
34
|
* over its state. Undo history is deliberately dropped — it does not survive
|
|
37
35
|
* a reload. */
|
|
38
|
-
export function
|
|
39
|
-
|
|
36
|
+
export function serializeTrials(
|
|
37
|
+
trials: TrialRecord[],
|
|
40
38
|
serializers: InstrumentSerializers,
|
|
41
|
-
):
|
|
42
|
-
|
|
39
|
+
): SerializedTrial[] {
|
|
40
|
+
return trials.map(({ undoStack: _undo, ...w }) => {
|
|
43
41
|
const s = serializers[w.instrumentName];
|
|
44
|
-
return {
|
|
45
|
-
...w,
|
|
46
|
-
state: s?.serialize ? s.serialize(w.state) : w.state,
|
|
47
|
-
};
|
|
42
|
+
return { ...w, state: s?.serialize ? s.serialize(w.state) : w.state };
|
|
48
43
|
});
|
|
49
|
-
return JSON.stringify(records);
|
|
50
44
|
}
|
|
51
45
|
|
|
52
|
-
/** Rebuild
|
|
46
|
+
/** Rebuild trials from storage, running each instrument's deserializer
|
|
53
47
|
* over its state and starting each with an empty undo history. Returns an
|
|
54
48
|
* empty list rather than throwing on malformed input. */
|
|
55
|
-
export function
|
|
56
|
-
|
|
49
|
+
export function deserializeTrials(
|
|
50
|
+
records: SerializedTrial[],
|
|
57
51
|
deserializers: InstrumentSerializers,
|
|
58
|
-
):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return records.map((r) => {
|
|
62
|
-
const d = deserializers[r.instrumentName];
|
|
63
|
-
return {
|
|
64
|
-
...r,
|
|
65
|
-
state: d?.deserialize ? d.deserialize(r.state) : r.state,
|
|
66
|
-
undoStack: emptyUndoStack(),
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
} catch {
|
|
70
|
-
console.warn('[labkit] deserializeWorkspaces: failed to parse, returning empty list');
|
|
52
|
+
): TrialRecord[] {
|
|
53
|
+
if (!Array.isArray(records)) {
|
|
54
|
+
console.warn('[labkit] deserializeTrials: not an array, returning empty list');
|
|
71
55
|
return [];
|
|
72
56
|
}
|
|
57
|
+
return records.map((r) => {
|
|
58
|
+
const d = deserializers[r.instrumentName];
|
|
59
|
+
return {
|
|
60
|
+
...r,
|
|
61
|
+
state: d?.deserialize ? d.deserialize(r.state) : r.state,
|
|
62
|
+
undoStack: emptyUndoStack(),
|
|
63
|
+
};
|
|
64
|
+
});
|
|
73
65
|
}
|
package/src/state/index.ts
CHANGED
|
@@ -8,18 +8,23 @@ export {
|
|
|
8
8
|
export {
|
|
9
9
|
LabStoreContext,
|
|
10
10
|
LabStoreProvider,
|
|
11
|
+
TrialIdContext,
|
|
12
|
+
TrialIdProvider,
|
|
11
13
|
useLabStore,
|
|
12
|
-
|
|
13
|
-
WorkspaceIdContext,
|
|
14
|
-
WorkspaceIdProvider,
|
|
14
|
+
useTrialId,
|
|
15
15
|
} from './context';
|
|
16
|
+
export {
|
|
17
|
+
CURRENT_DOCUMENT_VERSION,
|
|
18
|
+
labDocumentKey,
|
|
19
|
+
quarantineKey,
|
|
20
|
+
} from './document';
|
|
16
21
|
export {
|
|
17
22
|
decodeUrlHash,
|
|
18
|
-
|
|
23
|
+
deserializeTrials,
|
|
19
24
|
emptyUndoStack,
|
|
20
25
|
encodeUrlHash,
|
|
21
26
|
labStorageKey,
|
|
22
|
-
|
|
27
|
+
serializeTrials,
|
|
23
28
|
} from './helpers';
|
|
24
29
|
export {
|
|
25
30
|
SingletonExperimentProvider,
|
|
@@ -29,12 +34,15 @@ export type { LabStore, LabStoreActions } from './store';
|
|
|
29
34
|
export { createLabStore } from './store';
|
|
30
35
|
export type {
|
|
31
36
|
CreateLabStoreOptions,
|
|
32
|
-
ExperimentStateHandle,
|
|
33
37
|
InstrumentSerializers,
|
|
38
|
+
LabDocument,
|
|
34
39
|
LabStoreState,
|
|
40
|
+
Migration,
|
|
35
41
|
SavedSnapshot,
|
|
42
|
+
SerializedTrial,
|
|
36
43
|
StorageAdapter,
|
|
44
|
+
TrialRecord,
|
|
45
|
+
TrialStateHandle,
|
|
37
46
|
UndoStack,
|
|
38
|
-
WorkspaceRecord,
|
|
39
47
|
} from './types';
|
|
40
|
-
export {
|
|
48
|
+
export { useTrialState } from './useTrialState';
|