@weasel-js/labkit 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_dts/{DrawCommand-CitWPxMY.d.ts → DrawCommand-uKHt4Vul.d.ts} +56 -0
- package/dist/_dts/{index-C6Yze7sQ.d.ts → index-CFlDPeZh.d.ts} +30 -1
- package/dist/_dts/{types-Si4Fw-1F.d.ts → types-x92Kfeme.d.ts} +14 -0
- package/dist/_dts/{useExperimentState-CJn2hHzd.d.ts → useExperimentState-vrttakTt.d.ts} +43 -0
- package/dist/canvas/index.d.ts +13 -2
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-3P56ZCCJ.js → chunk-3WPOGKUP.js} +93 -62
- package/dist/chunk-3WPOGKUP.js.map +1 -0
- package/dist/{chunk-54IQ2DX7.js → chunk-574LJAV4.js} +3 -3
- package/dist/chunk-574LJAV4.js.map +1 -0
- package/dist/{chunk-VLAHRJOC.js → chunk-5R2ATYPJ.js} +2 -2
- package/dist/{chunk-VLAHRJOC.js.map → chunk-5R2ATYPJ.js.map} +1 -1
- package/dist/{chunk-2ZRE7WGQ.js → chunk-73PXCRCR.js} +3 -3
- package/dist/chunk-73PXCRCR.js.map +1 -0
- package/dist/{chunk-PWC7AQZM.js → chunk-CPUJ3QXL.js} +2 -2
- package/dist/chunk-CPUJ3QXL.js.map +1 -0
- package/dist/{chunk-R4TACNW7.js → chunk-N5KTQKQA.js} +11 -11
- package/dist/chunk-N5KTQKQA.js.map +1 -0
- package/dist/{chunk-HXZHVU4G.js → chunk-PMAU3SEE.js} +3 -3
- package/dist/chunk-PMAU3SEE.js.map +1 -0
- package/dist/{chunk-7BKDG73Z.js → chunk-RL2LOLNI.js} +2 -2
- package/dist/chunk-RL2LOLNI.js.map +1 -0
- package/dist/{chunk-2QNYYL3V.js → chunk-SFL7NFKN.js} +2 -2
- package/dist/chunk-SFL7NFKN.js.map +1 -0
- package/dist/{chunk-53XSBIUK.js → chunk-T7OKNJTY.js} +2 -2
- package/dist/chunk-T7OKNJTY.js.map +1 -0
- package/dist/controls/index.d.ts +4 -2
- package/dist/controls/index.js +1 -1
- package/dist/dragdrop/index.d.ts +2 -2
- package/dist/index.d.ts +122 -6
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +6 -3
- package/dist/layers/index.js +1 -1
- package/dist/passthrough/weasel-canvas.d.ts +5 -1
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +444 -7
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.d.ts +13 -0
- package/dist/primitives/index.js +2 -2
- package/dist/state/index.d.ts +15 -2
- package/dist/state/index.js +2 -2
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/ui/layers/index.d.ts +5 -0
- package/dist/ui/layers/index.js +3 -3
- package/dist/undo/index.d.ts +17 -2
- package/dist/undo/index.js +1 -1
- package/package.json +1 -1
- package/src/canvas/CanvasStack.tsx +4 -0
- package/src/canvas/CanvasStackContext.ts +3 -0
- package/src/canvas/canvasCoords.ts +2 -0
- package/src/canvas/useLayerScheduler.ts +2 -0
- package/src/controls/ControlPanel.tsx +2 -0
- package/src/controls/types.ts +14 -0
- package/src/instrument/capabilityDetector.ts +3 -0
- package/src/instrument/defineInstrument.ts +2 -0
- package/src/instrument/types.ts +29 -0
- package/src/instrument/validateConfigSchema.ts +5 -0
- package/src/lab/Lab.tsx +3 -0
- package/src/lab/LabContext.ts +5 -0
- package/src/lab/LabShell.tsx +4 -0
- package/src/lab/WorkspaceGrid.tsx +2 -0
- package/src/lab/gridDims.ts +2 -0
- package/src/layers/LayerList.tsx +3 -0
- package/src/primitives/FpsMeter.tsx +1 -0
- package/src/primitives/ScaleIndicator.tsx +4 -0
- package/src/primitives/Sidebar.tsx +3 -0
- package/src/primitives/StatusBar.tsx +3 -0
- package/src/primitives/Toolbar.tsx +6 -0
- package/src/state/SingletonExperiment.tsx +1 -0
- package/src/state/adapters.ts +8 -0
- package/src/state/context.tsx +9 -0
- package/src/state/helpers.ts +12 -0
- package/src/state/store.ts +6 -0
- package/src/state/types.ts +17 -0
- package/src/state/useExperimentState.ts +3 -0
- package/src/ui/layers/LayerStack.tsx +5 -0
- package/src/ui/properties/CurveField.tsx +4 -1
- package/src/ui/properties/EffectCard.tsx +12 -0
- package/src/ui/properties/PropertyGroup.tsx +1 -0
- package/src/ui/properties/PropertyPanel.tsx +28 -0
- package/src/undo/eventBus.ts +4 -0
- package/src/undo/undoStack.ts +11 -0
- package/src/workspace/DefaultSidebar.tsx +3 -0
- package/src/workspace/DefaultStatusBar.tsx +3 -0
- package/src/workspace/DefaultToolbar.tsx +3 -0
- package/src/workspace/Workspace.tsx +3 -0
- package/src/workspace/WorkspaceChrome.tsx +4 -0
- package/src/workspace/slotTypes.ts +9 -0
- package/src/workspace/workspaceOps.ts +8 -0
- package/dist/chunk-2QNYYL3V.js.map +0 -1
- package/dist/chunk-2ZRE7WGQ.js.map +0 -1
- package/dist/chunk-3P56ZCCJ.js.map +0 -1
- package/dist/chunk-53XSBIUK.js.map +0 -1
- package/dist/chunk-54IQ2DX7.js.map +0 -1
- package/dist/chunk-7BKDG73Z.js.map +0 -1
- package/dist/chunk-HXZHVU4G.js.map +0 -1
- package/dist/chunk-PWC7AQZM.js.map +0 -1
- package/dist/chunk-R4TACNW7.js.map +0 -1
|
@@ -32,10 +32,30 @@ interface Op {
|
|
|
32
32
|
args?: unknown;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** Options for `history.beginJournal()`. */
|
|
35
36
|
interface BeginJournalOptions {
|
|
37
|
+
/** Label for the single parent-history entry the journal flushes on commit. */
|
|
36
38
|
label: string;
|
|
39
|
+
/** Caller-supplied tag naming what this journal is scoped to — typically the
|
|
40
|
+
* id of the node being edited. The history layer only carries it; callers
|
|
41
|
+
* read it back off the journal to decide whether a suspended journal
|
|
42
|
+
* matches what they are about to edit. */
|
|
37
43
|
targetId?: string;
|
|
38
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* A scoped sub-history forked from a `History`, opened by
|
|
47
|
+
* `history.beginJournal()`. Applies, undoes and redoes against the same
|
|
48
|
+
* adapter as its parent, but keeps its entries to itself: `commit` flushes the
|
|
49
|
+
* journal's net forward ops to the parent as one entry, `cancel` rewinds them
|
|
50
|
+
* and contributes nothing. Use it when a self-contained editing session (a
|
|
51
|
+
* text edit, a modal drag) should collapse to a single step in the parent's
|
|
52
|
+
* undo stack while still offering undo *within* the session.
|
|
53
|
+
*
|
|
54
|
+
* A journal is active, suspended or closed. `commit` and `cancel` are
|
|
55
|
+
* terminal; `suspend` lets the parent be used again and can be reversed with
|
|
56
|
+
* `history.resumeJournal()`. Every mutating method throws when the journal is
|
|
57
|
+
* not active.
|
|
58
|
+
*/
|
|
39
59
|
interface Journal {
|
|
40
60
|
readonly targetId: string | undefined;
|
|
41
61
|
readonly forkedAtEntryId: number;
|
|
@@ -277,9 +297,13 @@ interface NodeBase<TData, TLayer extends string, TPose> {
|
|
|
277
297
|
data: TData;
|
|
278
298
|
parent: NodeId | null;
|
|
279
299
|
}
|
|
300
|
+
/** A node with no children — a shape, a label, an image. */
|
|
280
301
|
interface LeafNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
|
|
281
302
|
kind: 'leaf';
|
|
282
303
|
}
|
|
304
|
+
/** A node with an ordered list of children. This is the real group: what
|
|
305
|
+
* Cmd+G creates, what SVG `<g>` round-trips to. A container has its own pose,
|
|
306
|
+
* which its children's poses are relative to, and may optionally clip them. */
|
|
283
307
|
interface ContainerNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
|
|
284
308
|
kind: 'container';
|
|
285
309
|
children: NodeId[];
|
|
@@ -290,20 +314,30 @@ interface ContainerNode<TData, TLayer extends string, TPose = RectPose> extends
|
|
|
290
314
|
* paints descendants only where it covers. */
|
|
291
315
|
clipFromPose?: (pose: TPose) => Path | null;
|
|
292
316
|
}
|
|
317
|
+
/** A node in the scene tree: either a leaf or a container. Re-exported
|
|
318
|
+
* publicly as `SceneNode`, to avoid colliding with the DOM's `Node`. */
|
|
293
319
|
type Node<TData, TLayer extends string, TPose = RectPose> = LeafNode<TData, TLayer, TPose> | ContainerNode<TData, TLayer, TPose>;
|
|
294
320
|
interface LayerRecordBase<TLayer extends string> {
|
|
295
321
|
id: TLayer;
|
|
296
322
|
visible: boolean;
|
|
297
323
|
locked: boolean;
|
|
298
324
|
}
|
|
325
|
+
/** A layer declared when the scene was created. Fixed set, no display name —
|
|
326
|
+
* these are the kit's own render bands, not something a user manages. */
|
|
299
327
|
interface SystemLayerRecord<TLayer extends string> extends LayerRecordBase<TLayer> {
|
|
300
328
|
kind: 'system';
|
|
301
329
|
}
|
|
330
|
+
/** A layer the user created and can rename, reorder or delete. */
|
|
302
331
|
interface UserLayerRecord<TLayer extends string> extends LayerRecordBase<TLayer> {
|
|
303
332
|
kind: 'user';
|
|
304
333
|
name: string;
|
|
305
334
|
}
|
|
335
|
+
/** Per-layer metadata held by the scene: whether it is visible and locked,
|
|
336
|
+
* and where it sits in the render stack. Distinct from a node's `layer` tag,
|
|
337
|
+
* which merely names one of these. */
|
|
306
338
|
type LayerRecord<TLayer extends string> = SystemLayerRecord<TLayer> | UserLayerRecord<TLayer>;
|
|
339
|
+
/** What `Scene.add` needs to mint a node. Everything except the id is
|
|
340
|
+
* required; the id is generated unless one is supplied. */
|
|
307
341
|
interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
|
|
308
342
|
kind: 'leaf' | 'container';
|
|
309
343
|
layer: TLayer;
|
|
@@ -317,10 +351,13 @@ interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
|
|
|
317
351
|
* to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */
|
|
318
352
|
clipFromPose?: (pose: TPose) => Path | null;
|
|
319
353
|
}
|
|
354
|
+
/** A custom scene mutation registered with `Scene.registerOp`: how to apply
|
|
355
|
+
* it and how to undo it. The pair is what makes it participate in history. */
|
|
320
356
|
interface RegisteredOp<P> {
|
|
321
357
|
apply: (payload: P) => void;
|
|
322
358
|
revert: (payload: P) => void;
|
|
323
359
|
}
|
|
360
|
+
/** One of the layers a scene is created with. */
|
|
324
361
|
interface SystemLayerSpec<TLayer extends string> {
|
|
325
362
|
id: TLayer;
|
|
326
363
|
visible?: boolean;
|
|
@@ -361,6 +398,18 @@ interface SerializedNode<TData, TLayer extends string, TPose> {
|
|
|
361
398
|
* Containers only; omitted when the container has no clip. */
|
|
362
399
|
clipFromPoseKey?: string;
|
|
363
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* The kit-owned scene tree: nodes, layers, and the undo history over both.
|
|
403
|
+
*
|
|
404
|
+
* A scene is logical, not visual — it says what exists and where, and nothing
|
|
405
|
+
* about how it is painted. Every mutating method is undoable, and reads are
|
|
406
|
+
* snapshots rather than live views. Nodes are addressed by `NodeId`; hold ids
|
|
407
|
+
* across mutations, not node objects.
|
|
408
|
+
*
|
|
409
|
+
* Three type parameters keep it domain-agnostic: `TData` is the app's payload,
|
|
410
|
+
* which the kit never inspects; `TPose` is the transform shape, `RectPose` by
|
|
411
|
+
* default; `TLayer` is the union of layer names.
|
|
412
|
+
*/
|
|
364
413
|
interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
365
414
|
readonly nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;
|
|
366
415
|
readonly roots: readonly NodeId[];
|
|
@@ -865,6 +914,8 @@ interface TextStyle {
|
|
|
865
914
|
* `runs/rangeStyle.ts` for why the model collapses the tri-state.
|
|
866
915
|
*/
|
|
867
916
|
|
|
917
|
+
/** A run with every style resolved against the node's text style — no
|
|
918
|
+
* optional inheritance left. This is what layout and painting consume. */
|
|
868
919
|
interface ResolvedRun {
|
|
869
920
|
text: string;
|
|
870
921
|
fontFamily: string;
|
|
@@ -926,6 +977,8 @@ type ShaderUniform = number | [number, number] | [number, number, number] | [num
|
|
|
926
977
|
|
|
927
978
|
/** DrawCommand variants implemented through step 6. */
|
|
928
979
|
type DrawCommand = PathDrawCommand | GroupDrawCommand | TextDrawCommand | ImageDrawCommand | ShaderDrawCommand;
|
|
980
|
+
/** Draw a path, filled and/or stroked. The workhorse command: every shape the
|
|
981
|
+
* kit draws that is not text, an image, or a custom shader is one of these. */
|
|
929
982
|
interface PathDrawCommand {
|
|
930
983
|
kind: 'path';
|
|
931
984
|
path: Path;
|
|
@@ -948,6 +1001,9 @@ interface PathDrawCommand {
|
|
|
948
1001
|
*/
|
|
949
1002
|
vertexColors?: number[];
|
|
950
1003
|
}
|
|
1004
|
+
/** Draw a list of commands under a shared transform, opacity, color matrix
|
|
1005
|
+
* and clip. Groups nest, and their effects accumulate down the stack — this
|
|
1006
|
+
* is how a container node's transform reaches its descendants. */
|
|
951
1007
|
interface GroupDrawCommand {
|
|
952
1008
|
kind: 'group';
|
|
953
1009
|
transform?: Mat3;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { C as ConfigField } from './types-
|
|
2
|
+
import { C as ConfigField } from './types-x92Kfeme.js';
|
|
3
3
|
|
|
4
|
+
/** What an instrument's `render` is handed: its state and config, the setters
|
|
5
|
+
* for both, the workspace it is mounted in, and a way to emit named events. */
|
|
4
6
|
interface RenderContext<TS = unknown, TC = unknown> {
|
|
5
7
|
state: TS;
|
|
6
8
|
config: TC;
|
|
@@ -13,6 +15,7 @@ interface RenderContext<TS = unknown, TC = unknown> {
|
|
|
13
15
|
};
|
|
14
16
|
emit: (event: string) => void;
|
|
15
17
|
}
|
|
18
|
+
/** One 2D canvas layer of an instrument, drawn in declaration order. */
|
|
16
19
|
interface CanvasLayer<TS = unknown, TC = unknown> {
|
|
17
20
|
id: string;
|
|
18
21
|
draw: (ctx: CanvasRenderingContext2D, args: {
|
|
@@ -21,6 +24,8 @@ interface CanvasLayer<TS = unknown, TC = unknown> {
|
|
|
21
24
|
zoom: number;
|
|
22
25
|
}) => void;
|
|
23
26
|
}
|
|
27
|
+
/** Declares that an instrument draws to a canvas: its layers, and where the
|
|
28
|
+
* view starts. */
|
|
24
29
|
interface CanvasCapability<TS = unknown, TC = unknown> {
|
|
25
30
|
layers: CanvasLayer<TS, TC>[];
|
|
26
31
|
initialView?: {
|
|
@@ -31,9 +36,14 @@ interface CanvasCapability<TS = unknown, TC = unknown> {
|
|
|
31
36
|
};
|
|
32
37
|
};
|
|
33
38
|
}
|
|
39
|
+
/** Declares which of an instrument's layers the workspace should offer
|
|
40
|
+
* show/hide controls for. */
|
|
34
41
|
interface LayerCapability {
|
|
35
42
|
ids: string[];
|
|
36
43
|
}
|
|
44
|
+
/** Declares that an instrument accepts items dragged from a palette: what the
|
|
45
|
+
* palette offers, what a drop does to the state, and — optionally — live
|
|
46
|
+
* feedback during the drag and the ability to drag existing items back out. */
|
|
37
47
|
interface DragDropCapability<TS = unknown, TC = unknown> {
|
|
38
48
|
palette: PaletteItem[] | ((state: TS, config: TC) => PaletteItem[]);
|
|
39
49
|
onDrop: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => TS;
|
|
@@ -43,38 +53,57 @@ interface DragDropCapability<TS = unknown, TC = unknown> {
|
|
|
43
53
|
state: TS;
|
|
44
54
|
} | null;
|
|
45
55
|
}
|
|
56
|
+
/** Declares that an instrument's state is undoable: which emitted events
|
|
57
|
+
* snapshot it, and how many snapshots to keep. */
|
|
46
58
|
interface UndoCapability {
|
|
47
59
|
snapshotOn?: string[];
|
|
48
60
|
maxDepth?: number;
|
|
49
61
|
}
|
|
62
|
+
/** The name of an event an instrument emits through `RenderContext.emit`. */
|
|
50
63
|
type SystemEvent = string;
|
|
64
|
+
/** A point in world coordinates. */
|
|
51
65
|
type Point = {
|
|
52
66
|
x: number;
|
|
53
67
|
y: number;
|
|
54
68
|
};
|
|
69
|
+
/** What a hit-test found, and where. */
|
|
55
70
|
type HitResult = {
|
|
56
71
|
hit: boolean;
|
|
57
72
|
layerId?: string;
|
|
58
73
|
pointId?: string;
|
|
59
74
|
};
|
|
75
|
+
/** A workspace's camera. */
|
|
60
76
|
type ViewTransform = {
|
|
61
77
|
zoom: number;
|
|
62
78
|
pan: Point;
|
|
63
79
|
};
|
|
80
|
+
/** A layer as the layer list shows it. `alwaysOn` layers cannot be hidden. */
|
|
64
81
|
type LayerDescriptor = {
|
|
65
82
|
id: string;
|
|
66
83
|
label: string;
|
|
67
84
|
alwaysOn?: boolean;
|
|
68
85
|
};
|
|
86
|
+
/** One draggable entry in an instrument's palette. */
|
|
69
87
|
type PaletteItem = {
|
|
70
88
|
id: string;
|
|
71
89
|
label: string;
|
|
72
90
|
data?: unknown;
|
|
73
91
|
};
|
|
92
|
+
/** Whether a drop would be accepted at the current position, and why not if
|
|
93
|
+
* it would not. */
|
|
74
94
|
type DragFeedback = {
|
|
75
95
|
ok: boolean;
|
|
76
96
|
reason?: string;
|
|
77
97
|
};
|
|
98
|
+
/**
|
|
99
|
+
* An instrument: one self-contained interactive experiment a lab can host.
|
|
100
|
+
*
|
|
101
|
+
* It owns two pieces of data — `config`, the settings the control panel edits,
|
|
102
|
+
* and `state`, what the experiment is currently doing — and renders from both.
|
|
103
|
+
* The optional capability fields declare what else it wants from the runtime:
|
|
104
|
+
* a canvas, a layer list, palette drag-and-drop, undo. Declaring a capability
|
|
105
|
+
* is what makes the workspace provide the corresponding chrome.
|
|
106
|
+
*/
|
|
78
107
|
interface Instrument<TS = unknown, TC = unknown> {
|
|
79
108
|
name: string;
|
|
80
109
|
defaultConfig: () => TC;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
/** Which control a config field is edited with. */
|
|
1
2
|
type ConfigFieldType = 'slider' | 'checkbox' | 'select' | 'number' | 'text' | 'color';
|
|
3
|
+
/** What every config field carries: the config key it writes, the label shown
|
|
4
|
+
* beside it, and which control renders it. */
|
|
2
5
|
interface ConfigFieldBase {
|
|
3
6
|
key: string;
|
|
4
7
|
label: string;
|
|
5
8
|
type: ConfigFieldType;
|
|
6
9
|
}
|
|
10
|
+
/** A bounded number edited by dragging. */
|
|
7
11
|
interface SliderField extends ConfigFieldBase {
|
|
8
12
|
type: 'slider';
|
|
9
13
|
default: number;
|
|
@@ -11,19 +15,24 @@ interface SliderField extends ConfigFieldBase {
|
|
|
11
15
|
max: number;
|
|
12
16
|
step?: number;
|
|
13
17
|
}
|
|
18
|
+
/** A boolean. */
|
|
14
19
|
interface CheckboxField extends ConfigFieldBase {
|
|
15
20
|
type: 'checkbox';
|
|
16
21
|
default: boolean;
|
|
17
22
|
}
|
|
23
|
+
/** One choice in a select field. */
|
|
18
24
|
interface SelectOption {
|
|
19
25
|
value: string;
|
|
20
26
|
label: string;
|
|
21
27
|
}
|
|
28
|
+
/** A fixed set of labeled choices. */
|
|
22
29
|
interface SelectField extends ConfigFieldBase {
|
|
23
30
|
type: 'select';
|
|
24
31
|
default: string;
|
|
25
32
|
options: SelectOption[];
|
|
26
33
|
}
|
|
34
|
+
/** A number typed directly, optionally bounded. Use a slider field instead
|
|
35
|
+
* when the range matters more than the exact value. */
|
|
27
36
|
interface NumberField extends ConfigFieldBase {
|
|
28
37
|
type: 'number';
|
|
29
38
|
default: number;
|
|
@@ -31,6 +40,8 @@ interface NumberField extends ConfigFieldBase {
|
|
|
31
40
|
max?: number;
|
|
32
41
|
step?: number;
|
|
33
42
|
}
|
|
43
|
+
/** A free-text string. Writes are debounced so typing does not re-run the
|
|
44
|
+
* instrument on every keystroke. */
|
|
34
45
|
interface TextField extends ConfigFieldBase {
|
|
35
46
|
type: 'text';
|
|
36
47
|
default: string;
|
|
@@ -39,10 +50,13 @@ interface TextField extends ConfigFieldBase {
|
|
|
39
50
|
/** Milliseconds to debounce live setConfig calls. Default 150 ms. Set to 0 to disable. */
|
|
40
51
|
debounceMs?: number;
|
|
41
52
|
}
|
|
53
|
+
/** A color, as a CSS color string. */
|
|
42
54
|
interface ColorField extends ConfigFieldBase {
|
|
43
55
|
type: 'color';
|
|
44
56
|
default: string;
|
|
45
57
|
}
|
|
58
|
+
/** One field of an instrument's config schema. The schema is what the control
|
|
59
|
+
* panel renders, and what `validateConfigSchema` checks. */
|
|
46
60
|
type ConfigField = SliderField | CheckboxField | SelectField | NumberField | TextField | ColorField;
|
|
47
61
|
|
|
48
62
|
export type { ConfigField as C, NumberField as N, SelectField as S, TextField as T, CheckboxField as a, ColorField as b, ConfigFieldBase as c, ConfigFieldType as d, SelectOption as e, SliderField as f };
|
|
@@ -2,10 +2,14 @@ import * as react from 'react';
|
|
|
2
2
|
import { ReactNode, ReactElement } from 'react';
|
|
3
3
|
import { StoreApi } from 'zustand/vanilla';
|
|
4
4
|
|
|
5
|
+
/** A workspace's undo history, as snapshots of its state either side of the
|
|
6
|
+
* present. */
|
|
5
7
|
interface UndoStack {
|
|
6
8
|
past: unknown[];
|
|
7
9
|
future: unknown[];
|
|
8
10
|
}
|
|
11
|
+
/** One workspace as the store holds it: which instrument it runs, that
|
|
12
|
+
* instrument's config and state, the camera, and the undo history. */
|
|
9
13
|
interface WorkspaceRecord<TS = unknown, TC = unknown> {
|
|
10
14
|
id: string;
|
|
11
15
|
instrumentName: string;
|
|
@@ -20,6 +24,8 @@ interface WorkspaceRecord<TS = unknown, TC = unknown> {
|
|
|
20
24
|
};
|
|
21
25
|
undoStack: UndoStack;
|
|
22
26
|
}
|
|
27
|
+
/** A named, saved copy of a workspace's config and state, restorable into any
|
|
28
|
+
* workspace running the same instrument. */
|
|
23
29
|
interface SavedSnapshot {
|
|
24
30
|
id: string;
|
|
25
31
|
name: string;
|
|
@@ -31,38 +37,59 @@ interface SavedSnapshot {
|
|
|
31
37
|
}
|
|
32
38
|
/** `auto` follows the OS; the other two are an explicit choice. */
|
|
33
39
|
type LabMode = 'auto' | 'light' | 'dark';
|
|
40
|
+
/** Everything a lab persists: its workspaces, its saved snapshots, and the
|
|
41
|
+
* chosen color mode. */
|
|
34
42
|
interface LabStoreState {
|
|
35
43
|
workspaces: WorkspaceRecord[];
|
|
36
44
|
savedSnapshots: SavedSnapshot[];
|
|
37
45
|
mode: LabMode;
|
|
38
46
|
}
|
|
47
|
+
/** Where a lab persists itself. Implementations are keyed string storage and
|
|
48
|
+
* nothing more, so the same store works against localStorage, the URL hash,
|
|
49
|
+
* or memory. */
|
|
39
50
|
interface StorageAdapter {
|
|
40
51
|
read(key: string): string | null;
|
|
41
52
|
write(key: string, value: string): void;
|
|
42
53
|
delete?(key: string): void;
|
|
43
54
|
}
|
|
55
|
+
/** What `useExperimentState` hands an instrument: its state and config, with
|
|
56
|
+
* a setter for each. */
|
|
44
57
|
interface ExperimentStateHandle<TS, TC> {
|
|
45
58
|
state: TS;
|
|
46
59
|
setState: (next: TS | ((prev: TS) => TS)) => void;
|
|
47
60
|
config: TC;
|
|
48
61
|
setConfig: (key: keyof TC, value: TC[keyof TC]) => void;
|
|
49
62
|
}
|
|
63
|
+
/** Options for `createLabStore`. `storageKey` namespaces the keys written, so
|
|
64
|
+
* two labs on one origin do not collide. */
|
|
50
65
|
interface CreateLabStoreOptions {
|
|
51
66
|
storageKey: string;
|
|
52
67
|
storage: StorageAdapter;
|
|
53
68
|
initialMode?: LabMode;
|
|
54
69
|
}
|
|
70
|
+
/** Per-instrument serialize/deserialize hooks, keyed by instrument name. An
|
|
71
|
+
* instrument whose state is already JSON-safe needs no entry. */
|
|
55
72
|
type InstrumentSerializers = Record<string, {
|
|
56
73
|
serialize?: (state: unknown) => unknown;
|
|
57
74
|
deserialize?: (data: unknown) => unknown;
|
|
58
75
|
} | undefined>;
|
|
59
76
|
|
|
77
|
+
/** Persist to `localStorage` — state survives a reload and a new tab. Falls
|
|
78
|
+
* back to no-ops when storage is unavailable. */
|
|
60
79
|
declare const localStorageAdapter: StorageAdapter;
|
|
80
|
+
/** Persist to `sessionStorage` — state survives a reload but not a new tab. */
|
|
61
81
|
declare const sessionStorageAdapter: StorageAdapter;
|
|
82
|
+
/** Persist into the URL fragment, so the page's link carries its state and
|
|
83
|
+
* can be shared or bookmarked. */
|
|
62
84
|
declare const urlHashAdapter: StorageAdapter;
|
|
85
|
+
/** An in-memory store, discarded on reload. For tests, and for labs that
|
|
86
|
+
* should start fresh every time. */
|
|
63
87
|
declare function createMemoryAdapter(): StorageAdapter;
|
|
88
|
+
/** Persists nothing and reads back nothing. */
|
|
64
89
|
declare const noneAdapter: StorageAdapter;
|
|
65
90
|
|
|
91
|
+
/** Every mutation a lab store supports: managing workspaces, saving and
|
|
92
|
+
* restoring snapshots, and setting the color mode. */
|
|
66
93
|
interface LabStoreActions {
|
|
67
94
|
addWorkspace: (record: Omit<WorkspaceRecord, 'undoStack'>) => void;
|
|
68
95
|
removeWorkspace: (id: string) => void;
|
|
@@ -77,27 +104,43 @@ interface LabStoreActions {
|
|
|
77
104
|
listSnapshots: (workspaceId?: string) => SavedSnapshot[];
|
|
78
105
|
setMode: (mode: LabMode) => void;
|
|
79
106
|
}
|
|
107
|
+
/** A lab's store: its state and actions, plus the hook instruments use to
|
|
108
|
+
* register how their state is serialized. */
|
|
80
109
|
type LabStore = StoreApi<LabStoreState & LabStoreActions> & {
|
|
81
110
|
registerSerializers: (s: InstrumentSerializers) => void;
|
|
82
111
|
};
|
|
112
|
+
/** Build a lab store, hydrating from storage if anything was saved under the
|
|
113
|
+
* same key. Writes back are debounced. */
|
|
83
114
|
declare function createLabStore(options: CreateLabStoreOptions): LabStore;
|
|
84
115
|
|
|
85
116
|
type LabStoreCtx = {
|
|
86
117
|
store: LabStore;
|
|
87
118
|
} | null;
|
|
119
|
+
/** Context carrying the lab store. Prefer `useLabStore`; this is exported for
|
|
120
|
+
* code that needs to read the context without subscribing. */
|
|
88
121
|
declare const LabStoreContext: react.Context<LabStoreCtx>;
|
|
122
|
+
/** Provides a lab store to its subtree. */
|
|
89
123
|
declare function LabStoreProvider({ store, children, }: {
|
|
90
124
|
store: LabStore;
|
|
91
125
|
children: ReactNode;
|
|
92
126
|
}): ReactElement;
|
|
127
|
+
/** Subscribe to the whole lab store. Throws outside a `<LabStoreProvider>`. */
|
|
93
128
|
declare function useLabStore(): LabStoreState & ReturnType<LabStore['getState']>;
|
|
129
|
+
/** Context carrying which workspace the subtree belongs to. */
|
|
94
130
|
declare const WorkspaceIdContext: react.Context<string | null>;
|
|
131
|
+
/** Names the workspace its subtree belongs to, so an instrument's hooks can
|
|
132
|
+
* find their own record in the store without being passed an id. */
|
|
95
133
|
declare function WorkspaceIdProvider({ workspaceId, children, }: {
|
|
96
134
|
workspaceId: string;
|
|
97
135
|
children: ReactNode;
|
|
98
136
|
}): ReactElement;
|
|
137
|
+
/** The id of the workspace this component is inside. Throws outside a
|
|
138
|
+
* `<WorkspaceIdProvider>`. */
|
|
99
139
|
declare function useWorkspaceId(): string;
|
|
100
140
|
|
|
141
|
+
/** An instrument's own state and config, plus setters. Reads the surrounding
|
|
142
|
+
* workspace id, so an instrument never has to know which workspace it is
|
|
143
|
+
* running in. Throws outside a lab store and workspace. */
|
|
101
144
|
declare function useExperimentState<TS = unknown, TC = unknown>(): ExperimentStateHandle<TS, TC>;
|
|
102
145
|
|
|
103
146
|
export { LabStoreContext as b, LabStoreProvider as c, WorkspaceIdContext as f, WorkspaceIdProvider as g, createLabStore as h, createMemoryAdapter as i, useExperimentState as j, useLabStore as k, localStorageAdapter as l, useWorkspaceId as m, noneAdapter as n, sessionStorageAdapter as s, urlHashAdapter as u };
|
package/dist/canvas/index.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { V as ViewTransform, d as Point } from '../_dts/index-
|
|
5
|
-
import '../_dts/types-
|
|
4
|
+
import { V as ViewTransform, d as Point } from '../_dts/index-CFlDPeZh.js';
|
|
5
|
+
import '../_dts/types-x92Kfeme.js';
|
|
6
6
|
|
|
7
|
+
/** One layer of a canvas stack: its id, whether it is currently shown, and how
|
|
8
|
+
* it paints itself. */
|
|
7
9
|
interface CanvasLayerDescriptor {
|
|
8
10
|
id: string;
|
|
9
11
|
visible: boolean;
|
|
10
12
|
render: (ctx: CanvasRenderingContext2D, view: ViewTransform) => void;
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
/** Props for `<CanvasStack>`. */
|
|
13
16
|
interface CanvasStackProps {
|
|
14
17
|
layers: CanvasLayerDescriptor[];
|
|
15
18
|
view: ViewTransform;
|
|
@@ -20,14 +23,22 @@ interface CanvasStackProps {
|
|
|
20
23
|
onHitTest?: (worldPos: Point) => void;
|
|
21
24
|
children?: ReactNode;
|
|
22
25
|
}
|
|
26
|
+
/** Stacks one `<canvas>` per layer and drives them from a shared view, so a
|
|
27
|
+
* layer that changes rarely is not redrawn with one that changes every frame.
|
|
28
|
+
* Handles sizing, device pixel ratio, and pan/zoom. */
|
|
23
29
|
declare function CanvasStack({ layers, view, onViewChange, width, height, className, onHitTest, children, }: CanvasStackProps): react_jsx_runtime.JSX.Element;
|
|
24
30
|
|
|
31
|
+
/** What a canvas stack publishes to its children — currently the view, so
|
|
32
|
+
* DOM overlays can position themselves in the same coordinates. */
|
|
25
33
|
interface CanvasStackContextValue {
|
|
26
34
|
view: ViewTransform;
|
|
27
35
|
}
|
|
36
|
+
/** Context carrying the surrounding canvas stack's view. */
|
|
28
37
|
declare const CanvasStackContext: react.Context<CanvasStackContextValue | null>;
|
|
29
38
|
|
|
39
|
+
/** Project a world point into screen coordinates under a view. */
|
|
30
40
|
declare function worldToScreen(world: Point, view: ViewTransform): Point;
|
|
41
|
+
/** Unproject a screen point back to world coordinates under a view. */
|
|
31
42
|
declare function screenToWorld(screen: Point, view: ViewTransform): Point;
|
|
32
43
|
|
|
33
44
|
export { CanvasStack, CanvasStackContext, screenToWorld, worldToScreen };
|
package/dist/canvas/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CanvasStack, screenToWorld, worldToScreen } from '../chunk-
|
|
2
|
-
export { CanvasStackContext } from '../chunk-
|
|
1
|
+
export { CanvasStack, screenToWorld, worldToScreen } from '../chunk-574LJAV4.js';
|
|
2
|
+
export { CanvasStackContext } from '../chunk-CPUJ3QXL.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
4
4
|
//# sourceMappingURL=index.js.map
|