@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.
Files changed (94) hide show
  1. package/README.md +6 -6
  2. package/dist/_dts/{DrawCommand-uKHt4Vul.d.ts → DrawCommand-BkZztJsW.d.ts} +3 -1
  3. package/dist/_dts/{index-CFlDPeZh.d.ts → index-iAP6XbH3.d.ts} +17 -7
  4. package/dist/_dts/{useExperimentState-vrttakTt.d.ts → useTrialState-DYe2vUwN.d.ts} +62 -34
  5. package/dist/canvas/index.d.ts +1 -1
  6. package/dist/chunk-3TYUJR7Z.js +488 -0
  7. package/dist/chunk-3TYUJR7Z.js.map +1 -0
  8. package/dist/{chunk-N5KTQKQA.js → chunk-C6GJKPUI.js} +835 -770
  9. package/dist/chunk-C6GJKPUI.js.map +1 -0
  10. package/dist/{chunk-3WPOGKUP.js → chunk-DJLDIRFN.js} +543 -226
  11. package/dist/chunk-DJLDIRFN.js.map +1 -0
  12. package/dist/{chunk-73PXCRCR.js → chunk-NRKWVTVT.js} +6 -6
  13. package/dist/chunk-NRKWVTVT.js.map +1 -0
  14. package/dist/dragdrop/index.d.ts +1 -1
  15. package/dist/index.d.ts +197 -148
  16. package/dist/index.js +275 -161
  17. package/dist/index.js.map +1 -1
  18. package/dist/layers/index.d.ts +2 -2
  19. package/dist/passthrough/weasel-canvas.d.ts +1 -1
  20. package/dist/passthrough/weasel-canvas.js +1 -1
  21. package/dist/passthrough/weasel-ui.d.ts +10 -3
  22. package/dist/passthrough/weasel-ui.js +2 -2
  23. package/dist/state/index.d.ts +14 -14
  24. package/dist/state/index.js +5 -5
  25. package/dist/state/index.js.map +1 -1
  26. package/dist/styles.css +179 -14
  27. package/dist/ui/layers/index.js +3 -3
  28. package/dist/undo/index.d.ts +1 -1
  29. package/package.json +2 -1
  30. package/src/canvas/AGENTS.md +6 -2
  31. package/src/dragdrop/dragDrop.test.tsx +4 -7
  32. package/src/index.test.ts +52 -0
  33. package/src/index.ts +11 -7
  34. package/src/instrument/SineWave.smoke.test.tsx +1 -1
  35. package/src/instrument/capabilityDetector.ts +1 -1
  36. package/src/instrument/types.ts +18 -6
  37. package/src/lab/Lab.less +16 -0
  38. package/src/lab/Lab.stories.tsx +5 -5
  39. package/src/lab/Lab.test.tsx +25 -25
  40. package/src/lab/Lab.tsx +56 -44
  41. package/src/lab/LabContext.ts +12 -11
  42. package/src/lab/LabShell.tsx +1 -1
  43. package/src/lab/Workspace.less +40 -0
  44. package/src/lab/{WorkspaceGrid.stories.tsx → Workspace.stories.tsx} +27 -11
  45. package/src/lab/Workspace.test.tsx +114 -0
  46. package/src/lab/Workspace.tsx +196 -0
  47. package/src/lab/index.ts +2 -4
  48. package/src/layers/AGENTS.md +3 -3
  49. package/src/primitives/Toolbar.stories.tsx +2 -2
  50. package/src/state/SingletonExperiment.test.tsx +8 -8
  51. package/src/state/SingletonExperiment.tsx +9 -9
  52. package/src/state/context.tsx +12 -12
  53. package/src/state/document.test.ts +369 -0
  54. package/src/state/document.ts +194 -0
  55. package/src/state/helpers.test.ts +74 -46
  56. package/src/state/helpers.ts +23 -31
  57. package/src/state/index.ts +16 -8
  58. package/src/state/store.test.ts +440 -40
  59. package/src/state/store.ts +166 -90
  60. package/src/state/types.ts +30 -10
  61. package/src/state/{useExperimentState.test.tsx → useTrialState.test.tsx} +18 -18
  62. package/src/state/useTrialState.ts +29 -0
  63. package/src/styles.less +2 -2
  64. package/src/test-setup.ts +19 -0
  65. package/src/theme/base.less +1 -1
  66. package/src/{workspace → trial}/DefaultSidebar.tsx +3 -3
  67. package/src/{workspace → trial}/DefaultStatusBar.tsx +5 -4
  68. package/src/{workspace → trial}/DefaultToolbar.tsx +9 -8
  69. package/src/trial/Trial.canvas.test.tsx +80 -0
  70. package/src/{workspace/Workspace.less → trial/Trial.less} +1 -1
  71. package/src/{workspace/Workspace.stories.tsx → trial/Trial.stories.tsx} +13 -17
  72. package/src/{workspace/Workspace.test.tsx → trial/Trial.test.tsx} +26 -25
  73. package/src/{workspace/Workspace.tsx → trial/Trial.tsx} +48 -43
  74. package/src/{workspace/WorkspaceChrome.tsx → trial/TrialChrome.tsx} +42 -42
  75. package/src/{workspace → trial}/index.ts +13 -12
  76. package/src/{workspace → trial}/slotTypes.ts +16 -16
  77. package/src/{workspace/workspaceOps.test.ts → trial/trialOps.test.ts} +60 -33
  78. package/src/trial/trialOps.ts +99 -0
  79. package/src/ui/format.test.ts +33 -0
  80. package/src/ui/format.ts +11 -0
  81. package/src/ui/properties/CurveField.tsx +6 -6
  82. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +3 -3
  83. package/dist/chunk-3WPOGKUP.js.map +0 -1
  84. package/dist/chunk-73PXCRCR.js.map +0 -1
  85. package/dist/chunk-N5KTQKQA.js.map +0 -1
  86. package/dist/chunk-T7OKNJTY.js +0 -351
  87. package/dist/chunk-T7OKNJTY.js.map +0 -1
  88. package/src/lab/WorkspaceGrid.less +0 -8
  89. package/src/lab/WorkspaceGrid.test.tsx +0 -40
  90. package/src/lab/WorkspaceGrid.tsx +0 -22
  91. package/src/lab/gridDims.test.ts +0 -35
  92. package/src/lab/gridDims.ts +0 -13
  93. package/src/state/useExperimentState.ts +0 -31
  94. package/src/workspace/workspaceOps.ts +0 -86
@@ -1,4 +1,4 @@
1
- import { s, p } from './chunk-N5KTQKQA.js';
1
+ import { c, p } from './chunk-C6GJKPUI.js';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -29,7 +29,7 @@ function LayerStack({
29
29
  });
30
30
  }, [items]);
31
31
  const dragItems = items.map((it) => ({ id: String(it.id), label: it.kind }));
32
- const drag = s({
32
+ const drag = c({
33
33
  items: dragItems,
34
34
  selectedIds: [],
35
35
  onReorder: (ids, targetIndex) => {
@@ -46,8 +46,8 @@ function LayerStack({
46
46
  }
47
47
  });
48
48
  const toggleExpanded = (id) => {
49
- setExpandedIds((s2) => {
50
- const n = new Set(s2);
49
+ setExpandedIds((s) => {
50
+ const n = new Set(s);
51
51
  if (n.has(id)) n.delete(id);
52
52
  else n.add(id);
53
53
  return n;
@@ -157,5 +157,5 @@ function DragHandleGlyph() {
157
157
  }
158
158
 
159
159
  export { LayerStack };
160
- //# sourceMappingURL=chunk-73PXCRCR.js.map
161
- //# sourceMappingURL=chunk-73PXCRCR.js.map
160
+ //# sourceMappingURL=chunk-NRKWVTVT.js.map
161
+ //# sourceMappingURL=chunk-NRKWVTVT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/layers/LayerStack.tsx"],"names":[],"mappings":"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","file":"chunk-NRKWVTVT.js","sourcesContent":["import { type CSSProperties, type ReactNode, type RefCallback, useEffect, useState } from 'react';\nimport { dlog, useReorderDragList } from '../../passthrough/weasel-ui';\n\n/** One card in a layer stack: its identity, the label shown when collapsed,\n * and the optional select hoisted into its header. */\nexport interface LayerStackItem {\n /** Stable id used for keys, onRemove, onReorder. Numeric to match\n * common id-from-nextId conventions; string ids also work. */\n id: number | string;\n /** Short kind label rendered in the header when no primary select\n * is hoisted (e.g. \"shadow\", \"stroke\"). */\n kind: string;\n /** When present, hoist this select into the card header so the user\n * can switch mode/shape without expanding. */\n primaryValue?: string;\n primaryOptions?: string[];\n /** Accent CSS color used as the left border / index-badge fill. */\n accent?: string;\n /** Optional badge text rendered before the primary control\n * (e.g. tail index \"1\", \"2\", \"3\"). When omitted a drag handle\n * glyph renders in its place. */\n badge?: string;\n /** Initial expanded state. Defaults to true for newly-added items. */\n defaultExpanded?: boolean;\n}\n\n/** Props for `<LayerStack>`. */\nexport interface LayerStackProps {\n title: string;\n items: LayerStackItem[];\n /** Kinds the user can add via the header palette. */\n paletteKinds: string[];\n onAdd: (kind: string) => void;\n onRemove: (id: number | string) => void;\n onReorder: (orderedIds: Array<number | string>) => void;\n onPrimaryChange: (id: number | string, nextValue: string) => void;\n /** Render the body controls for each item. */\n renderBody: (item: LayerStackItem) => ReactNode;\n /** Hide the title + palette row (used when an outer wrap renders its\n * own head — see speech-balloons Tails panel). */\n hideHead?: boolean;\n}\n\n/** A drag-reorderable stack of expandable cards, with a palette in the header\n * for adding more. The body of each card is the caller's to render. */\nexport function LayerStack({\n title,\n items,\n paletteKinds,\n onAdd,\n onRemove,\n onReorder,\n onPrimaryChange,\n renderBody,\n hideHead,\n}: LayerStackProps) {\n const [expandedIds, setExpandedIds] = useState<Set<number | string>>(\n () => new Set(items.filter((i) => i.defaultExpanded !== false).map((i) => i.id)),\n );\n\n useEffect(() => {\n setExpandedIds((prev) => {\n let next: Set<number | string> | null = null;\n for (const item of items) {\n if (item.defaultExpanded === false) continue;\n if (prev.has(item.id)) continue;\n if (next === null) next = new Set(prev);\n next.add(item.id);\n }\n return next ?? prev;\n });\n }, [items]);\n\n const dragItems = items.map((it) => ({ id: String(it.id), label: it.kind }));\n const drag = useReorderDragList({\n items: dragItems,\n selectedIds: [],\n onReorder: (ids, targetIndex) => {\n dlog('layer-stack', 'onReorder', { ids, targetIndex });\n const orig = items.map((i) => i.id);\n const moving = new Set(ids);\n const remaining = orig.filter((id) => !moving.has(String(id)));\n const movedIds = items.map((i) => i.id).filter((id) => moving.has(String(id)));\n // weasel reports targetIndex against the original list (which includes\n // the dragged row). Each moved item that originally sat before\n // targetIndex needs to shift the insertion point left by one.\n const shift = orig.filter((id, idx) => moving.has(String(id)) && idx < targetIndex).length;\n const adjusted = targetIndex - shift;\n const out = [...remaining];\n out.splice(adjusted, 0, ...movedIds);\n onReorder(out);\n },\n });\n\n const toggleExpanded = (id: number | string) => {\n setExpandedIds((s) => {\n const n = new Set(s);\n if (n.has(id)) n.delete(id);\n else n.add(id);\n return n;\n });\n };\n\n return (\n <div className=\"lk-layer-stack\">\n {!hideHead && (\n <div className=\"lk-layer-stack__head\">\n <h2 className=\"lk-layer-stack__title\">{title}</h2>\n <div className=\"lk-layer-stack__palette\">\n {paletteKinds.map((k) => (\n <button\n key={k}\n type=\"button\"\n className=\"lk-layer-stack__add\"\n onClick={() => {\n dlog('layer-stack', 'onAdd', { kind: k });\n onAdd(k);\n }}\n aria-label={`Add ${k}`}\n >\n {k}\n </button>\n ))}\n </div>\n </div>\n )}\n <div\n className=\"lk-layer-stack__list\"\n ref={drag.containerProps.ref as RefCallback<HTMLDivElement>}\n onPointerMove={drag.containerProps.onPointerMove}\n onPointerUp={drag.containerProps.onPointerUp}\n onPointerCancel={drag.containerProps.onPointerCancel}\n >\n {items.map((item, i) => {\n const expanded = expandedIds.has(item.id);\n const draggedId = drag.state.draggedIds?.[0];\n const isDragging = draggedId === String(item.id);\n const showHintBefore = drag.state.targetIndex === i && draggedId !== String(item.id);\n const showHintAfter = drag.state.targetIndex === items.length && i === items.length - 1;\n const cardCls = [\n 'lk-layer-card',\n expanded ? 'is-expanded' : 'is-collapsed',\n isDragging ? 'is-dragging' : '',\n item.accent ? 'has-accent' : '',\n ]\n .filter(Boolean)\n .join(' ');\n const cardStyle = item.accent\n ? ({ '--lk-layer-card-accent': item.accent } as CSSProperties)\n : undefined;\n const { onPointerDown } = drag.rowProps(String(item.id), i);\n return (\n <div key={item.id} className=\"lk-layer-card-wrap\">\n {showHintBefore && <div className=\"lk-layer-stack__drop-hint\" />}\n <div className={cardCls} data-testid={`lk-layer-card-${item.id}`} style={cardStyle}>\n <div className=\"lk-layer-card__head\">\n <button\n type=\"button\"\n className=\"lk-layer-card__handle\"\n aria-label={`Drag to reorder layer ${item.id}`}\n onPointerDown={onPointerDown}\n onClick={() => toggleExpanded(item.id)}\n >\n {item.badge ?? <DragHandleGlyph />}\n </button>\n {item.primaryValue !== undefined && item.primaryOptions ? (\n <select\n className=\"lk-layer-card__primary\"\n value={item.primaryValue}\n aria-label={`Primary select for layer ${item.id}`}\n onChange={(e) => onPrimaryChange(item.id, e.target.value)}\n onClick={(e) => e.stopPropagation()}\n >\n {item.primaryOptions.map((o) => (\n <option key={o} value={o}>\n {o}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"lk-layer-card__kind\">{item.kind}</span>\n )}\n <button\n type=\"button\"\n className=\"lk-layer-card__remove\"\n aria-label=\"Remove layer\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove(item.id);\n }}\n >\n ✕\n </button>\n </div>\n {expanded && <div className=\"lk-layer-card__body\">{renderBody(item)}</div>}\n </div>\n {showHintAfter && <div className=\"lk-layer-stack__drop-hint\" />}\n </div>\n );\n })}\n {items.length === 0 && (\n <div className=\"lk-layer-stack__empty\">No layers — add one above.</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction DragHandleGlyph() {\n return (\n <svg width=\"12\" height=\"16\" viewBox=\"0 0 8 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"2\" cy=\"3\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"3\" r=\"1.1\" />\n <circle cx=\"2\" cy=\"8\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"8\" r=\"1.1\" />\n <circle cx=\"2\" cy=\"13\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"13\" r=\"1.1\" />\n </svg>\n );\n}\n"]}
@@ -1,3 +1,3 @@
1
- export { D as DragDropCapability, b as DragFeedback, P as PaletteItem } from '../_dts/index-CFlDPeZh.js';
1
+ export { D as DragDropCapability, c as DragFeedback, P as PaletteItem } from '../_dts/index-iAP6XbH3.js';
2
2
  import 'react';
3
3
  import '../_dts/types-x92Kfeme.js';
package/dist/index.d.ts CHANGED
@@ -2,20 +2,20 @@ export { CanvasLayerDescriptor, CanvasStack, CanvasStackContext, CanvasStackCont
2
2
  export { ControlPanel } from './controls/index.js';
3
3
  import { C as ConfigField } from './_dts/types-x92Kfeme.js';
4
4
  export { a as CheckboxField, b as ColorField, c as ConfigFieldBase, d as ConfigFieldType, N as NumberField, S as SelectField, e as SelectOption, f as SliderField, T as TextField } from './_dts/types-x92Kfeme.js';
5
- import { I as Instrument } from './_dts/index-CFlDPeZh.js';
6
- export { C as CanvasCapability, a as CanvasLayer, D as DragDropCapability, b as DragFeedback, H as HitResult, L as LayerCapability, c as LayerDescriptor, P as PaletteItem, d as Point, R as RenderContext, S as SystemEvent, U as UndoCapability, V as ViewTransform } from './_dts/index-CFlDPeZh.js';
5
+ import { I as Instrument, a as InstrumentList } from './_dts/index-iAP6XbH3.js';
6
+ export { C as CanvasCapability, b as CanvasLayer, D as DragDropCapability, c as DragFeedback, H as HitResult, L as LayerCapability, d as LayerDescriptor, P as PaletteItem, e as Point, R as RenderContext, S as SystemEvent, U as UndoCapability, V as ViewTransform } from './_dts/index-iAP6XbH3.js';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
  import * as react from 'react';
9
9
  import { ReactNode } from 'react';
10
- import { S as StorageAdapter, o as LabMode, W as WorkspaceRecord, e as SavedSnapshot } from './_dts/useExperimentState-vrttakTt.js';
11
- export { C as CreateLabStoreOptions, E as ExperimentStateHandle, b as LabStoreContext, c as LabStoreProvider, d as LabStoreState, U as UndoStack, f as WorkspaceIdContext, g as WorkspaceIdProvider, i as createMemoryAdapter, l as localStorageAdapter, n as noneAdapter, s as sessionStorageAdapter, u as urlHashAdapter, j as useExperimentState, k as useLabStore, m as useWorkspaceId } from './_dts/useExperimentState-vrttakTt.js';
10
+ import { a as StorageAdapter, w as LabMode, T as TrialRecord, h as SavedSnapshot } from './_dts/useTrialState-DYe2vUwN.js';
11
+ export { C as CURRENT_DOCUMENT_VERSION, b as CreateLabStoreOptions, L as LabDocument, e as LabStoreContext, f as LabStoreProvider, g as LabStoreState, S as SerializedTrial, i as TrialIdContext, j as TrialIdProvider, k as TrialStateHandle, U as UndoStack, m as createMemoryAdapter, n as labDocumentKey, o as localStorageAdapter, p as noneAdapter, q as quarantineKey, s as sessionStorageAdapter, u as urlHashAdapter, r as useLabStore, t as useTrialId, v as useTrialState } from './_dts/useTrialState-DYe2vUwN.js';
12
12
  export { LayerList, LayerListProps } from './layers/index.js';
13
13
  export { FpsMeter, ScaleIndicator, ScaleIndicatorProps, Sidebar, SidebarProps, StatusBar, StatusBarProps, Toolbar, ToolbarProps } from './primitives/index.js';
14
14
  export { LayerStack, LayerStackItem, LayerStackProps } from './ui/layers/index.js';
15
15
  export { EventBus, EventListener, clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './undo/index.js';
16
16
  import 'zustand/vanilla';
17
17
 
18
- /** Which optional capabilities an instrument declared — what the workspace
18
+ /** Which optional capabilities an instrument declared — what the trial
19
19
  * consults to decide which chrome to show. */
20
20
  interface CapabilityFlags {
21
21
  hasCanvas: boolean;
@@ -41,17 +41,9 @@ interface ValidationResult {
41
41
  * fields whose own constraints do not hold. */
42
42
  declare function validateConfigSchema(fields: ConfigField[]): ValidationResult;
43
43
 
44
- /** A grid's column and row counts. */
45
- interface GridDims {
46
- cols: number;
47
- rows: number;
48
- }
49
- /** The most nearly square grid that fits `count` items. */
50
- declare function gridDims(count: number): GridDims;
51
-
52
44
  /** Props for `<Lab>`. */
53
45
  interface LabProps {
54
- instruments: Instrument[];
46
+ instruments: InstrumentList;
55
47
  defaultInstrument: string;
56
48
  storage?: StorageAdapter | null;
57
49
  storageKey?: string;
@@ -66,23 +58,24 @@ interface LabProps {
66
58
  title?: string;
67
59
  children?: ReactNode;
68
60
  }
69
- /** The lab runtime: creates the store, provides it, and renders one workspace
70
- * per record in a grid. Each workspace runs one of `instruments`. */
61
+ /** The lab runtime: creates the store, provides it, and renders one trial
62
+ * per record in a grid. Each trial runs one of `instruments`. */
71
63
  declare function Lab({ instruments, defaultInstrument, storage, storageKey, mode, nebula, title, children, }: LabProps): react_jsx_runtime.JSX.Element;
72
64
 
73
65
  /** Lab-wide state and commands: the available instruments, the open
74
- * workspaces and the operations over them, saved snapshots, and the color
66
+ * trials and the operations over them, saved snapshots, and the color
75
67
  * mode. */
76
68
  interface LabContextValue {
77
- instruments: Instrument[];
78
- workspaces: WorkspaceRecord[];
79
- addWorkspace: (instrumentName: string) => void;
80
- cloneWorkspace: (id: string) => void;
81
- closeWorkspace: (id: string) => void;
82
- resetWorkspace: (id: string) => void;
69
+ instruments: InstrumentList;
70
+ trials: TrialRecord[];
71
+ addTrial: (instrumentName: string) => void;
72
+ cloneTrial: (id: string) => void;
73
+ closeTrial: (id: string) => void;
74
+ resetTrial: (id: string) => void;
75
+ reorderTrials: (ids: readonly string[]) => void;
83
76
  savedSnapshots: SavedSnapshot[];
84
- saveSnapshot: (workspaceId: string, name?: string) => void;
85
- loadSnapshot: (workspaceId: string, snapshotId: string) => void;
77
+ saveSnapshot: (trialId: string, name?: string) => void;
78
+ loadSnapshot: (trialId: string, snapshotId: string) => void;
86
79
  deleteSnapshot: (snapshotId: string) => void;
87
80
  mode: LabMode;
88
81
  setMode: (m: LabMode) => void;
@@ -105,15 +98,65 @@ interface LabShellProps {
105
98
  }
106
99
  /** Page frame for a lab: a titled header, a body, and an optional footer,
107
100
  * themed for the resolved color mode. Presentational only — use `<Lab>` when
108
- * the workspace runtime is wanted too. */
101
+ * the trial runtime is wanted too. */
109
102
  declare function LabShell({ title, children, header, footer, mode }: LabShellProps): react_jsx_runtime.JSX.Element;
110
103
 
111
- /** Props for `<WorkspaceGrid>`. */
112
- interface WorkspaceGridProps {
104
+ /** A tile's persisted extent, keyed by the id its caller gave it. Grid resizes
105
+ * write `span`; `size` is here because a strategy swap would write that. */
106
+ type TrialLayout = Record<string, {
107
+ size?: {
108
+ w?: number;
109
+ h?: number;
110
+ };
111
+ span?: {
112
+ cols?: number;
113
+ rows?: number;
114
+ };
115
+ }>;
116
+ interface WorkspaceProps {
113
117
  children: ReactNode;
118
+ /**
119
+ * Stable id per child, positionally matched. Supply these whenever a tile
120
+ * can be closed from the middle: without them a tile is identified by its
121
+ * position, so closing one shifts every id after it and the panes inherit
122
+ * each other's dragged extents. `layout` and `reorderable` both key off
123
+ * these, so neither means much without them.
124
+ */
125
+ ids?: readonly string[];
126
+ /** Draggable seams between tiles. Off by default — an even tiling is the
127
+ * behavior every existing caller has. */
128
+ resizable?: boolean;
129
+ /** Let a tile be dragged to a new position. Off by default. The grid never
130
+ * reorders `children` itself: it reports the order a drop would produce and
131
+ * the caller commits it. */
132
+ reorderable?: boolean;
133
+ /** The full id list a drop would produce, in its new order. */
134
+ onReorder?: (ids: string[]) => void;
135
+ /** Extents from a previous session, applied to tiles as they register. */
136
+ layout?: TrialLayout;
137
+ /** Fires when a tile's extent changes. Persist it and hand it back as
138
+ * `layout` to make a resize survive a reload. */
139
+ onLayoutChange?: (layout: TrialLayout) => void;
140
+ gap?: number;
141
+ padding?: number;
142
+ /**
143
+ * Fixed tiling extent. Omit in an app — the grid measures its own box. Supply
144
+ * it where nothing measures, notably jsdom: at a zero measurement the grid
145
+ * renders no tiles at all.
146
+ */
147
+ viewport?: {
148
+ w: number;
149
+ h: number;
150
+ };
114
151
  }
115
- /** Lays its children out in the most nearly square grid that fits them. */
116
- declare function WorkspaceGrid({ children }: WorkspaceGridProps): react_jsx_runtime.JSX.Element;
152
+ /**
153
+ * Auto-balanced tiling of trials, `ceil(sqrt(n))` columns wide.
154
+ *
155
+ * Tiles are absolutely positioned at the rects `gridStrategy` computes, not
156
+ * laid out by CSS — `windease/styles.css` (folded into
157
+ * `@weasel-js/labkit/styles.css`) carries the rules that positioning depends on.
158
+ */
159
+ declare function Workspace({ children, ids, resizable, reorderable, onReorder, layout, onLayoutChange, gap, padding, viewport, }: WorkspaceProps): react_jsx_runtime.JSX.Element;
117
160
 
118
161
  type TokenValue = string | number | readonly (string | number)[];
119
162
  /**
@@ -153,6 +196,128 @@ interface Theme {
153
196
  */
154
197
  declare const interstellarTheme: Theme;
155
198
 
199
+ /** What a trial hands its toolbar: which instrument is running, the undo
200
+ * state and commands, the zoom controls, and the snapshot commands. */
201
+ interface TrialToolbarContext {
202
+ trialId: string;
203
+ instrumentName: string;
204
+ hasUndo: boolean;
205
+ canUndo: boolean;
206
+ canRedo: boolean;
207
+ undo: () => void;
208
+ redo: () => void;
209
+ zoom: number;
210
+ setZoom: (z: number) => void;
211
+ zoomIn: () => void;
212
+ zoomOut: () => void;
213
+ resetZoom: () => void;
214
+ hasCanvas: boolean;
215
+ savedSnapshots: SavedSnapshot[];
216
+ saveSnapshot: (name?: string) => void;
217
+ loadSnapshot: (snapshotId: string) => void;
218
+ clone: () => void;
219
+ reset: () => void;
220
+ close: () => void;
221
+ isLastTrial: boolean;
222
+ }
223
+ /** What a trial hands its sidebar: the instrument's config schema, its
224
+ * current values, and the setter. */
225
+ interface TrialSidebarContext {
226
+ trialId: string;
227
+ instrumentName: string;
228
+ configFields: ConfigField[];
229
+ config: unknown;
230
+ setConfig: (key: string, value: unknown) => void;
231
+ }
232
+ /** What a trial hands its status bar. */
233
+ interface TrialStatusBarContext {
234
+ trialId: string;
235
+ instrumentName: string;
236
+ zoom: number;
237
+ }
238
+ /** Replaces a trial's toolbar. Receives everything the default one uses,
239
+ * so a custom toolbar need not reach into the store. */
240
+ type ToolbarSlot = (ctx: TrialToolbarContext) => ReactNode;
241
+ /** Replaces a trial's sidebar. */
242
+ type SidebarSlot = (ctx: TrialSidebarContext) => ReactNode;
243
+ /** Replaces a trial's status bar. */
244
+ type StatusBarSlot = (ctx: TrialStatusBarContext) => ReactNode;
245
+
246
+ /** Props for `<DefaultSidebar>`. */
247
+ interface DefaultSidebarProps {
248
+ ctx: TrialSidebarContext;
249
+ }
250
+ /** The sidebar a trial renders when no `sidebar` slot is supplied: a
251
+ * control panel over the instrument's config schema. */
252
+ declare function DefaultSidebar({ ctx }: DefaultSidebarProps): react_jsx_runtime.JSX.Element;
253
+
254
+ /** Props for `<DefaultStatusBar>`. */
255
+ interface DefaultStatusBarProps {
256
+ ctx: TrialStatusBarContext;
257
+ }
258
+ /** The status bar a trial renders when no `statusBar` slot is supplied:
259
+ * the instrument's name and the current zoom. */
260
+ declare function DefaultStatusBar({ ctx }: DefaultStatusBarProps): react_jsx_runtime.JSX.Element;
261
+
262
+ /** Props for `<DefaultToolbar>`. */
263
+ interface DefaultToolbarProps {
264
+ ctx: TrialToolbarContext;
265
+ }
266
+ /** The toolbar a trial renders when no `toolbar` slot is supplied. Only
267
+ * shows the controls the instrument's declared capabilities support. */
268
+ declare function DefaultToolbar({ ctx }: DefaultToolbarProps): react_jsx_runtime.JSX.Element;
269
+
270
+ /** Props for `<Trial>`. */
271
+ interface TrialProps {
272
+ id: string;
273
+ }
274
+ /** Renders one trial from the lab store: looks up its record and
275
+ * instrument, and mounts the instrument inside the trial chrome. */
276
+ declare function Trial({ id }: TrialProps): react_jsx_runtime.JSX.Element;
277
+
278
+ interface UndoBindings {
279
+ canUndo: boolean;
280
+ canRedo: boolean;
281
+ undo: () => void;
282
+ redo: () => void;
283
+ }
284
+ /** Props for `<TrialChrome>`. */
285
+ interface TrialChromeProps {
286
+ trialId: string;
287
+ record: TrialRecord;
288
+ instrument: Instrument;
289
+ isLastTrial: boolean;
290
+ toolbar?: ToolbarSlot;
291
+ sidebar?: SidebarSlot;
292
+ statusBar?: StatusBarSlot;
293
+ undoBindings?: UndoBindings;
294
+ sidebarExtras?: ReactNode;
295
+ children: ReactNode;
296
+ }
297
+ /** The frame around a running instrument — toolbar, sidebar, status bar —
298
+ * each replaceable by a slot. Assembles the slot contexts and wires the undo
299
+ * keyboard shortcuts. */
300
+ declare function TrialChrome({ trialId, record, instrument, isLastTrial, toolbar, sidebar, statusBar, undoBindings, sidebarExtras, children, }: TrialChromeProps): react_jsx_runtime.JSX.Element;
301
+
302
+ /** Append a new trial running `instrumentName`, at that instrument's
303
+ * default config and initial state. */
304
+ declare function addTrial(trials: TrialRecord[], instruments: InstrumentList, instrumentName: string): TrialRecord[];
305
+ /** Insert a deep copy of a trial directly after it. The copy starts with
306
+ * an empty undo history — the original's is not shared. */
307
+ declare function cloneTrial(trials: TrialRecord[], id: string): TrialRecord[];
308
+ /** Remove a trial, unless it is the last one — a lab always has at least
309
+ * one. */
310
+ declare function closeTrial(trials: TrialRecord[], id: string): TrialRecord[];
311
+ /** Return a trial to its instrument's defaults, keeping its id and its
312
+ * place in the list. */
313
+ declare function resetTrial(trials: TrialRecord[], id: string, instruments: InstrumentList): TrialRecord[];
314
+ /**
315
+ * Reorder to match `ids`. Ids the list doesn't mention keep their relative
316
+ * order at the end, and ids it names that no longer exist are dropped — a
317
+ * reorder that raced a close should not resurrect the closed trial.
318
+ */
319
+ declare function reorderTrials(trials: TrialRecord[], ids: readonly string[]): TrialRecord[];
320
+
156
321
  /** An annotation drawn over a curve plot: a shaded band across a range of x,
157
322
  * or a vertical line at one x. */
158
323
  type CurveMark = {
@@ -430,121 +595,5 @@ interface ToggleRowProps<T extends string> {
430
595
  * visible at once. */
431
596
  declare function ToggleRow<T extends string>({ label, value, options, onChange, layout, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
432
597
 
433
- /** What a workspace hands its toolbar: which instrument is running, the undo
434
- * state and commands, the zoom controls, and the snapshot commands. */
435
- interface WorkspaceToolbarContext {
436
- workspaceId: string;
437
- instrumentName: string;
438
- hasUndo: boolean;
439
- canUndo: boolean;
440
- canRedo: boolean;
441
- undo: () => void;
442
- redo: () => void;
443
- zoom: number;
444
- setZoom: (z: number) => void;
445
- zoomIn: () => void;
446
- zoomOut: () => void;
447
- resetZoom: () => void;
448
- hasCanvas: boolean;
449
- savedSnapshots: SavedSnapshot[];
450
- saveSnapshot: (name?: string) => void;
451
- loadSnapshot: (snapshotId: string) => void;
452
- clone: () => void;
453
- reset: () => void;
454
- close: () => void;
455
- isLastWorkspace: boolean;
456
- }
457
- /** What a workspace hands its sidebar: the instrument's config schema, its
458
- * current values, and the setter. */
459
- interface WorkspaceSidebarContext {
460
- workspaceId: string;
461
- instrumentName: string;
462
- configFields: ConfigField[];
463
- config: unknown;
464
- setConfig: (key: string, value: unknown) => void;
465
- }
466
- /** What a workspace hands its status bar. */
467
- interface WorkspaceStatusBarContext {
468
- workspaceId: string;
469
- instrumentName: string;
470
- zoom: number;
471
- }
472
- /** Replaces a workspace's toolbar. Receives everything the default one uses,
473
- * so a custom toolbar need not reach into the store. */
474
- type ToolbarSlot = (ctx: WorkspaceToolbarContext) => ReactNode;
475
- /** Replaces a workspace's sidebar. */
476
- type SidebarSlot = (ctx: WorkspaceSidebarContext) => ReactNode;
477
- /** Replaces a workspace's status bar. */
478
- type StatusBarSlot = (ctx: WorkspaceStatusBarContext) => ReactNode;
479
-
480
- /** Props for `<DefaultSidebar>`. */
481
- interface DefaultSidebarProps {
482
- ctx: WorkspaceSidebarContext;
483
- }
484
- /** The sidebar a workspace renders when no `sidebar` slot is supplied: a
485
- * control panel over the instrument's config schema. */
486
- declare function DefaultSidebar({ ctx }: DefaultSidebarProps): react_jsx_runtime.JSX.Element;
487
-
488
- /** Props for `<DefaultStatusBar>`. */
489
- interface DefaultStatusBarProps {
490
- ctx: WorkspaceStatusBarContext;
491
- }
492
- /** The status bar a workspace renders when no `statusBar` slot is supplied:
493
- * the instrument's name and the current zoom. */
494
- declare function DefaultStatusBar({ ctx }: DefaultStatusBarProps): react_jsx_runtime.JSX.Element;
495
-
496
- /** Props for `<DefaultToolbar>`. */
497
- interface DefaultToolbarProps {
498
- ctx: WorkspaceToolbarContext;
499
- }
500
- /** The toolbar a workspace renders when no `toolbar` slot is supplied. Only
501
- * shows the controls the instrument's declared capabilities support. */
502
- declare function DefaultToolbar({ ctx }: DefaultToolbarProps): react_jsx_runtime.JSX.Element;
503
-
504
- /** Props for `<Workspace>`. */
505
- interface WorkspaceProps {
506
- id: string;
507
- }
508
- /** Renders one workspace from the lab store: looks up its record and
509
- * instrument, and mounts the instrument inside the workspace chrome. */
510
- declare function Workspace({ id }: WorkspaceProps): react_jsx_runtime.JSX.Element;
511
-
512
- interface UndoBindings {
513
- canUndo: boolean;
514
- canRedo: boolean;
515
- undo: () => void;
516
- redo: () => void;
517
- }
518
- /** Props for `<WorkspaceChrome>`. */
519
- interface WorkspaceChromeProps {
520
- workspaceId: string;
521
- record: WorkspaceRecord;
522
- instrument: Instrument;
523
- isLastWorkspace: boolean;
524
- toolbar?: ToolbarSlot;
525
- sidebar?: SidebarSlot;
526
- statusBar?: StatusBarSlot;
527
- undoBindings?: UndoBindings;
528
- sidebarExtras?: ReactNode;
529
- children: ReactNode;
530
- }
531
- /** The frame around a running instrument — toolbar, sidebar, status bar —
532
- * each replaceable by a slot. Assembles the slot contexts and wires the undo
533
- * keyboard shortcuts. */
534
- declare function WorkspaceChrome({ workspaceId, record, instrument, isLastWorkspace, toolbar, sidebar, statusBar, undoBindings, sidebarExtras, children, }: WorkspaceChromeProps): react_jsx_runtime.JSX.Element;
535
-
536
- /** Append a new workspace running `instrumentName`, at that instrument's
537
- * default config and initial state. */
538
- declare function addWorkspace(workspaces: WorkspaceRecord[], instruments: Instrument[], instrumentName: string): WorkspaceRecord[];
539
- /** Insert a deep copy of a workspace directly after it. The copy starts with
540
- * an empty undo history — the original's is not shared. */
541
- declare function cloneWorkspace(workspaces: WorkspaceRecord[], id: string): WorkspaceRecord[];
542
- /** Remove a workspace, unless it is the last one — a lab always has at least
543
- * one. */
544
- declare function closeWorkspace(workspaces: WorkspaceRecord[], id: string): WorkspaceRecord[];
545
- /** Return a workspace to its instrument's defaults, keeping its id and its
546
- * place in the list. */
547
- declare function resetWorkspace(workspaces: WorkspaceRecord[], id: string, instruments: Instrument[]): WorkspaceRecord[];
548
-
549
- export { CheckboxRow, ColorRow, ConfigField, CurveField, DefaultSidebar, DefaultStatusBar, DefaultToolbar, EffectCard, EffectCardList, Instrument, Lab, LabContext, LabMode, LabShell, NumberRow, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Workspace, WorkspaceChrome, WorkspaceGrid, WorkspaceRecord, addWorkspace, cloneWorkspace, closeWorkspace, defineInstrument, detectCapabilities, gridDims, interstellarTheme, resetWorkspace, useLabContext, validateConfigSchema };
550
- export type { CapabilityFlags, CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, DefaultSidebarProps, DefaultStatusBarProps, DefaultToolbarProps, EffectCardListItem, EffectCardListProps, EffectCardProps, GridDims, LabContextValue, LabProps, LabShellProps, NumberRowProps, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SidebarSlot, SliderRowProps, StatusBarSlot, SubpanelProps, TextRowProps, ToggleRowProps, ToolbarSlot, ValidationResult, WorkspaceChromeProps, WorkspaceGridProps, WorkspaceProps, WorkspaceSidebarContext, WorkspaceStatusBarContext, WorkspaceToolbarContext };
598
+ export { CheckboxRow, ColorRow, ConfigField, CurveField, DefaultSidebar, DefaultStatusBar, DefaultToolbar, EffectCard, EffectCardList, Instrument, InstrumentList, Lab, LabContext, LabMode, LabShell, NumberRow, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Trial, TrialChrome, TrialRecord, Workspace, addTrial, cloneTrial, closeTrial, defineInstrument, detectCapabilities, interstellarTheme, reorderTrials, resetTrial, useLabContext, validateConfigSchema };
599
+ export type { CapabilityFlags, CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, DefaultSidebarProps, DefaultStatusBarProps, DefaultToolbarProps, EffectCardListItem, EffectCardListProps, EffectCardProps, LabContextValue, LabProps, LabShellProps, NumberRowProps, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SidebarSlot, SliderRowProps, StatusBarSlot, SubpanelProps, TextRowProps, ToggleRowProps, ToolbarSlot, TrialChromeProps, TrialProps, TrialSidebarContext, TrialStatusBarContext, TrialToolbarContext, ValidationResult, WorkspaceProps };