@weasel-js/labkit 1.2.0 → 1.3.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_dts/{DrawCommand-C_XboUpz.d.ts → DrawCommand-DBB45NfN.d.ts} +196 -52
- package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
- package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
- package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
- package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
- package/dist/_dts/{useTrialState-BMNIx3Cy.d.ts → useTrialState-CsGMjhu9.d.ts} +5 -87
- package/dist/canvas/index.d.ts +7 -3
- package/dist/canvas/index.js +2 -1
- package/dist/chrome/index.d.ts +86 -0
- package/dist/chrome/index.js +8 -0
- package/dist/chrome/index.js.map +1 -0
- package/dist/{chunk-VUU5UXHE.js → chunk-4TMMVIDM.js} +8 -5
- package/dist/chunk-4TMMVIDM.js.map +1 -0
- package/dist/{chunk-THBG7FQZ.js → chunk-HFDVGF4X.js} +15 -12
- package/dist/chunk-HFDVGF4X.js.map +1 -0
- package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
- package/dist/chunk-NS54R52R.js.map +1 -0
- package/dist/{chunk-G5TJVQQT.js → chunk-O2BULFHX.js} +5 -14
- package/dist/chunk-O2BULFHX.js.map +1 -0
- package/dist/chunk-O7NVSCLN.js +266 -0
- package/dist/chunk-O7NVSCLN.js.map +1 -0
- package/dist/chunk-PO6L3NSH.js +366 -0
- package/dist/chunk-PO6L3NSH.js.map +1 -0
- package/dist/chunk-PQJ5B2U2.js +27 -0
- package/dist/chunk-PQJ5B2U2.js.map +1 -0
- package/dist/chunk-RVNN4CHQ.js +212 -0
- package/dist/chunk-RVNN4CHQ.js.map +1 -0
- package/dist/chunk-TLGRYALP.js +6151 -0
- package/dist/chunk-TLGRYALP.js.map +1 -0
- package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
- package/dist/chunk-V7PRSIRQ.js.map +1 -0
- package/dist/{chunk-BOHF3PQO.js → chunk-Z6HJ5FEM.js} +8111 -5190
- package/dist/chunk-Z6HJ5FEM.js.map +1 -0
- package/dist/chunk-ZZAYVX4X.js +505 -0
- package/dist/chunk-ZZAYVX4X.js.map +1 -0
- package/dist/controls/index.d.ts +19 -5
- package/dist/controls/index.js +3 -1
- package/dist/dragdrop/index.d.ts +4 -2
- package/dist/index.d.ts +246 -106
- package/dist/index.js +673 -559
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +5 -3
- package/dist/layers/index.js +2 -1
- package/dist/passthrough/weasel-canvas.d.ts +69 -13
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +3799 -3672
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.d.ts +102 -8
- package/dist/primitives/index.js +4 -1
- package/dist/state/index.d.ts +3 -2
- package/dist/state/index.js +2 -2
- package/dist/styles.css +542 -153
- package/dist/surface/index.js +2 -1
- package/dist/ui/layers/index.js +4 -3
- package/dist/undo/index.d.ts +4 -2
- package/package.json +16 -7
- package/src/canvas/AGENTS.md +4 -0
- package/src/canvas/CanvasStack.tsx +6 -2
- package/src/canvas/useLayerScheduler.ts +22 -15
- package/src/canvas/usePanZoom.test.ts +80 -0
- package/src/canvas/usePanZoom.ts +16 -2
- package/src/chrome/ChromeRegions.stories.tsx +106 -0
- package/src/chrome/builtins.test.ts +153 -0
- package/src/chrome/builtins.tsx +205 -0
- package/src/chrome/index.ts +25 -0
- package/src/chrome/merge.test.ts +51 -0
- package/src/chrome/merge.ts +52 -0
- package/src/chrome/regions/PaletteRegion.less +14 -0
- package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
- package/src/chrome/regions/PaletteRegion.tsx +41 -0
- package/src/chrome/regions/SidebarRegion.tsx +56 -0
- package/src/chrome/regions/StatusRegion.tsx +31 -0
- package/src/chrome/regions/TitleBarRegion.tsx +41 -0
- package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
- package/src/chrome/regions/ToolbarRegion.tsx +71 -0
- package/src/chrome/regions/ViewportRegion.less +42 -0
- package/src/chrome/regions/ViewportRegion.tsx +37 -0
- package/src/chrome/regions/regions.test.tsx +81 -0
- package/src/chrome/types.ts +127 -0
- package/src/config/builder.test.ts +87 -0
- package/src/config/builder.ts +224 -0
- package/src/config/fromConfigField.test.ts +93 -0
- package/src/config/fromConfigField.ts +74 -0
- package/src/config/index.ts +22 -0
- package/src/config/resolve.test.ts +120 -0
- package/src/config/resolve.ts +67 -0
- package/src/config/rules.ts +68 -0
- package/src/config/types.ts +108 -0
- package/src/config/useConfigSchema.ts +30 -0
- package/src/config/visible.ts +20 -0
- package/src/controls/ControlPanel.less +4 -58
- package/src/controls/ControlPanel.stories.tsx +86 -0
- package/src/controls/ControlPanel.test.tsx +223 -10
- package/src/controls/ControlPanel.tsx +202 -203
- package/src/dragdrop/DragDropRuntime.tsx +36 -18
- package/src/dragdrop/DragGhost.less +1 -1
- package/src/dragdrop/Palette.less +1 -1
- package/src/index.ts +25 -3
- package/src/instrument/SineWave.smoke.test.tsx +8 -1
- package/src/instrument/defineInstrument.test.ts +38 -1
- package/src/instrument/defineInstrument.ts +31 -4
- package/src/instrument/index.ts +1 -0
- package/src/instrument/types.ts +27 -4
- package/src/instrument/validateConfigSchema.test.ts +13 -3
- package/src/instrument/validateConfigSchema.ts +7 -10
- package/src/lab/Lab.chrome.test.tsx +65 -0
- package/src/lab/Lab.tsx +58 -15
- package/src/lab/LabContext.ts +6 -0
- package/src/lab/LabFullChrome.stories.tsx +308 -0
- package/src/lab/LabHeader.test.tsx +42 -0
- package/src/lab/LabHeader.tsx +57 -0
- package/src/lab/LabPalette.test.tsx +35 -0
- package/src/lab/LabPalette.tsx +34 -0
- package/src/lab/LabShell.less +50 -3
- package/src/lab/Workspace.less +1 -30
- package/src/lab/Workspace.test.tsx +20 -10
- package/src/lab/Workspace.tsx +5 -8
- package/src/lab/index.ts +2 -0
- package/src/layers/AGENTS.md +1 -1
- package/src/layers/LayerList.less +31 -7
- package/src/layers/LayerList.tsx +17 -4
- package/src/passthrough/weasel-ui.ts +23 -0
- package/src/primitives/DragHandleGlyph.tsx +29 -0
- package/src/primitives/FloatingPanel.less +28 -0
- package/src/primitives/FloatingPanel.stories.tsx +46 -0
- package/src/primitives/FloatingPanel.test.tsx +240 -0
- package/src/primitives/FloatingPanel.tsx +190 -0
- package/src/primitives/FpsMeter.test.tsx +44 -0
- package/src/primitives/FpsMeter.tsx +20 -9
- package/src/primitives/JobProgress.less +69 -0
- package/src/primitives/JobProgress.stories.tsx +48 -0
- package/src/primitives/JobProgress.tsx +65 -0
- package/src/primitives/Legend.less +47 -0
- package/src/primitives/Legend.stories.tsx +32 -0
- package/src/primitives/Legend.test.tsx +57 -0
- package/src/primitives/Legend.tsx +40 -0
- package/src/primitives/Sidebar.less +32 -2
- package/src/primitives/StatusBar.less +7 -0
- package/src/primitives/StatusBar.tsx +10 -3
- package/src/primitives/Toolbar.less +50 -3
- package/src/primitives/Toolbar.test.tsx +67 -1
- package/src/primitives/Toolbar.tsx +72 -6
- package/src/primitives/ZoomControl.less +31 -0
- package/src/primitives/ZoomControl.tsx +76 -0
- package/src/primitives/floating.entry.test.tsx +14 -0
- package/src/primitives/index.ts +10 -2
- package/src/primitives/useRovingTabIndex.test.ts +27 -0
- package/src/primitives/useRovingTabIndex.ts +79 -0
- package/src/state/store.ts +11 -3
- package/src/state/toolSlot.test.ts +36 -0
- package/src/state/types.ts +4 -0
- package/src/styles.less +6 -0
- package/src/surface/useTiledSurface.ts +14 -10
- package/src/test-setup.ts +26 -0
- package/src/theme/base.less +19 -2
- package/src/theme/interstellar.tokens.json +6 -2
- package/src/tools/types.ts +24 -0
- package/src/trial/Trial.config.test.tsx +113 -0
- package/src/trial/Trial.job.test.tsx +13 -15
- package/src/trial/Trial.less +115 -10
- package/src/trial/Trial.stories.tsx +1 -0
- package/src/trial/Trial.test.tsx +67 -96
- package/src/trial/Trial.tsx +80 -39
- package/src/trial/TrialChrome.tsx +99 -80
- package/src/trial/TrialDragContext.ts +10 -0
- package/src/trial/TrialTitleBar.tsx +41 -0
- package/src/trial/index.ts +1 -15
- package/src/trial/trialChrome.borders.test.ts +38 -0
- package/src/ui/layers/LayerStack.less +9 -9
- package/src/ui/layers/LayerStack.tsx +1 -13
- package/src/ui/properties/CurveField.less +2 -2
- package/src/ui/properties/CurveField.test.tsx +11 -11
- package/src/ui/properties/PropertyGroup.less +2 -2
- package/src/ui/properties/PropertyPanel.less +55 -41
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
- package/src/ui/properties/storyLayouts.tsx +2 -2
- package/dist/_dts/index-JFAYj5Tv.d.ts +0 -144
- package/dist/_dts/types-x92Kfeme.d.ts +0 -62
- package/dist/chunk-574LJAV4.js.map +0 -1
- package/dist/chunk-5R2ATYPJ.js +0 -201
- package/dist/chunk-5R2ATYPJ.js.map +0 -1
- package/dist/chunk-73KA7WBO.js +0 -5992
- package/dist/chunk-73KA7WBO.js.map +0 -1
- package/dist/chunk-BOHF3PQO.js.map +0 -1
- package/dist/chunk-G5TJVQQT.js.map +0 -1
- package/dist/chunk-PMAU3SEE.js +0 -126
- package/dist/chunk-PMAU3SEE.js.map +0 -1
- package/dist/chunk-SFL7NFKN.js.map +0 -1
- package/dist/chunk-THBG7FQZ.js.map +0 -1
- package/dist/chunk-VUU5UXHE.js.map +0 -1
- package/src/instrument/capabilityDetector.test.ts +0 -64
- package/src/instrument/capabilityDetector.ts +0 -20
- package/src/trial/DefaultSidebar.tsx +0 -29
- package/src/trial/DefaultStatusBar.tsx +0 -19
- package/src/trial/DefaultToolbar.tsx +0 -92
- package/src/trial/slotTypes.ts +0 -54
package/dist/index.js
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import { useSurfaceOptional } from './chunk-
|
|
2
|
-
export { SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceOptional, useSurfaceTile, useTiledSurface } from './chunk-
|
|
1
|
+
import { useSurfaceOptional } from './chunk-HFDVGF4X.js';
|
|
2
|
+
export { SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceOptional, useSurfaceTile, useTiledSurface } from './chunk-HFDVGF4X.js';
|
|
3
3
|
import { useJob } from './chunk-LN6JDUGB.js';
|
|
4
4
|
export { useJob } from './chunk-LN6JDUGB.js';
|
|
5
|
-
export { LayerStack } from './chunk-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
export { LayerStack } from './chunk-O2BULFHX.js';
|
|
6
|
+
import { suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, SidebarRegion, ViewportRegion, StatusRegion } from './chunk-PO6L3NSH.js';
|
|
7
|
+
export { PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, mergeContributions, suppressContributions } from './chunk-PO6L3NSH.js';
|
|
8
|
+
import { JobProgress } from './chunk-RVNN4CHQ.js';
|
|
9
|
+
export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-RVNN4CHQ.js';
|
|
10
|
+
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-O7NVSCLN.js';
|
|
11
|
+
import { LabStoreContext, createLabStore, noneAdapter } from './chunk-4TMMVIDM.js';
|
|
12
|
+
export { CURRENT_DOCUMENT_VERSION, LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, createMemoryAdapter, labDocumentKey, localStorageAdapter, noneAdapter, quarantineKey, sessionStorageAdapter, urlHashAdapter, useLabStore, useTrialId, useTrialState } from './chunk-4TMMVIDM.js';
|
|
13
|
+
import { fromConfigFields } from './chunk-ZZAYVX4X.js';
|
|
14
|
+
export { CheckboxRow, ColorRow, ControlPanel, NumberRow, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SelectRow, SliderRow, TextRow, ToggleRow, fromConfigFields, isLeafVisible } from './chunk-ZZAYVX4X.js';
|
|
15
|
+
import { p, F, vt, p2, r } from './chunk-TLGRYALP.js';
|
|
16
|
+
import { CanvasStack, screenToWorld } from './chunk-NS54R52R.js';
|
|
17
|
+
export { CanvasStack, screenToWorld, worldToScreen } from './chunk-NS54R52R.js';
|
|
16
18
|
export { CanvasStackContext } from './chunk-CPUJ3QXL.js';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
+
import { useVisibleRaf } from './chunk-Z6HJ5FEM.js';
|
|
20
|
+
import { LayerList } from './chunk-V7PRSIRQ.js';
|
|
21
|
+
export { LayerList } from './chunk-V7PRSIRQ.js';
|
|
22
|
+
import './chunk-PQJ5B2U2.js';
|
|
19
23
|
import { createEventBus, redo, undo, pushSnapshot } from './chunk-RL2LOLNI.js';
|
|
20
24
|
export { clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './chunk-RL2LOLNI.js';
|
|
21
25
|
import { createContext, useRef, useCallback, useContext, useMemo, useState, Children, useLayoutEffect, useEffect } from 'react';
|
|
22
|
-
import {
|
|
26
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
23
27
|
import { useStore } from 'zustand/react';
|
|
24
28
|
import { createPortal } from 'react-dom';
|
|
29
|
+
import { Provider, StrategyRegistryProvider, DragProvider, Container, useDragHandle } from 'windease/react';
|
|
25
30
|
import { asNodeId, Store, createNode, gridStrategy } from 'windease';
|
|
26
|
-
import { DragHandle, Provider, StrategyRegistryProvider, DragProvider, Container } from 'windease/react';
|
|
27
31
|
|
|
28
32
|
var PITCH_LIMIT = Math.PI / 2 - 0.01;
|
|
29
33
|
var YAW_PER_PX = 8e-3;
|
|
@@ -106,23 +110,260 @@ function useOrbit({
|
|
|
106
110
|
return { onWheel, onPointerDown, onPointerMove, onPointerUp, onDoubleClick, isDragging };
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
// src/
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
// src/config/builder.ts
|
|
114
|
+
var BaseNode = class {
|
|
115
|
+
constructor(defaultValue, annotations = {}, options = {}) {
|
|
116
|
+
this.annotations = annotations;
|
|
117
|
+
this.options = options;
|
|
118
|
+
this.default = defaultValue;
|
|
119
|
+
}
|
|
120
|
+
annotations;
|
|
121
|
+
options;
|
|
122
|
+
// `default` is a reserved word in a parameter position, so the field is
|
|
123
|
+
// declared rather than taken as a parameter property.
|
|
124
|
+
default;
|
|
125
|
+
/** Clone into the same subclass. Subclasses with extra constructor
|
|
126
|
+
* arguments override this. */
|
|
127
|
+
with(annotations, options) {
|
|
128
|
+
const Ctor = this.constructor;
|
|
129
|
+
return new Ctor(this.default, annotations, options);
|
|
130
|
+
}
|
|
131
|
+
ann(patch) {
|
|
132
|
+
return this.with({ ...this.annotations, ...patch }, this.options);
|
|
133
|
+
}
|
|
134
|
+
opt(patch) {
|
|
135
|
+
return this.with(this.annotations, { ...this.options, ...patch });
|
|
136
|
+
}
|
|
137
|
+
/** Human-readable label. Defaults to the key, title-cased. */
|
|
138
|
+
label(name) {
|
|
139
|
+
return this.ann({ name });
|
|
140
|
+
}
|
|
141
|
+
/** Longer help text, surfaced as a tooltip on the row label. */
|
|
142
|
+
describe(description) {
|
|
143
|
+
return this.ann({ description });
|
|
144
|
+
}
|
|
145
|
+
/** Omit from the panel unless it is asked to show hidden leaves. */
|
|
146
|
+
hidden() {
|
|
147
|
+
return this.ann({ hidden: true });
|
|
148
|
+
}
|
|
149
|
+
/** Pair with a sibling sharing this id, side-by-side on one row. */
|
|
150
|
+
pair(pair) {
|
|
151
|
+
return this.ann({ pair });
|
|
152
|
+
}
|
|
153
|
+
/** Render under a named section heading. */
|
|
154
|
+
section(section) {
|
|
155
|
+
return this.opt({ section });
|
|
156
|
+
}
|
|
157
|
+
/** Show this row only while the predicate holds. Presentational — the value
|
|
158
|
+
* stays in config and the instrument still reads it. */
|
|
159
|
+
showIf(predicate) {
|
|
160
|
+
return this.opt({ showIf: predicate });
|
|
161
|
+
}
|
|
162
|
+
/** Draw this one row yourself, keeping the kind, default and validation. */
|
|
163
|
+
render(renderer) {
|
|
164
|
+
return this.opt({ render: renderer });
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var NumberNode = class extends BaseNode {
|
|
168
|
+
kind = "number";
|
|
169
|
+
/** Bound the value. A number with both bounds renders as a slider. */
|
|
170
|
+
range(min, max) {
|
|
171
|
+
return this.ann({ min, max });
|
|
172
|
+
}
|
|
173
|
+
step(step) {
|
|
174
|
+
return this.ann({ step });
|
|
175
|
+
}
|
|
176
|
+
/** Force a slider even without both bounds. */
|
|
177
|
+
slider() {
|
|
178
|
+
return this.ann({ control: "slider" });
|
|
179
|
+
}
|
|
180
|
+
/** Force a typed input even when both bounds are set. */
|
|
181
|
+
input() {
|
|
182
|
+
return this.ann({ control: "input" });
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var BooleanNode = class extends BaseNode {
|
|
186
|
+
kind = "boolean";
|
|
187
|
+
/** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's
|
|
188
|
+
* `PrefsForm` honors the distinction. */
|
|
189
|
+
toggle() {
|
|
190
|
+
return this.ann({ control: "switch" });
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var StringNode = class extends BaseNode {
|
|
194
|
+
kind = "string";
|
|
195
|
+
placeholder(placeholder) {
|
|
196
|
+
return this.ann({ placeholder });
|
|
197
|
+
}
|
|
198
|
+
maxLength(maxLength) {
|
|
199
|
+
return this.ann({ maxLength });
|
|
200
|
+
}
|
|
201
|
+
/** Milliseconds to debounce live writes. 0 commits every keystroke. */
|
|
202
|
+
debounce(debounceMs) {
|
|
203
|
+
return this.ann({ debounceMs });
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
var ColorNode = class extends BaseNode {
|
|
207
|
+
kind = "color";
|
|
208
|
+
};
|
|
209
|
+
var EnumNode = class extends BaseNode {
|
|
210
|
+
kind = "enum";
|
|
211
|
+
/** Render as a segmented control rather than a select. */
|
|
212
|
+
radio() {
|
|
213
|
+
return this.ann({ control: "radio" });
|
|
214
|
+
}
|
|
215
|
+
/** Relabel the choices. Named `labels` rather than `options` because
|
|
216
|
+
* `options` is the node's own extras bag. */
|
|
217
|
+
labels(options) {
|
|
218
|
+
return this.ann({ options });
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var ValueNode = class extends BaseNode {
|
|
222
|
+
kind = null;
|
|
223
|
+
};
|
|
224
|
+
var CustomNode = class _CustomNode extends BaseNode {
|
|
225
|
+
constructor(kind, defaultValue, annotations = {}, options = {}) {
|
|
226
|
+
super(defaultValue, annotations, options);
|
|
227
|
+
this.kind = kind;
|
|
228
|
+
}
|
|
229
|
+
kind;
|
|
230
|
+
with(annotations, options) {
|
|
231
|
+
return new _CustomNode(this.kind, this.default, annotations, options);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
var expand = (options) => options.map((o) => typeof o === "string" ? { value: o, label: o } : o);
|
|
235
|
+
var f = {
|
|
236
|
+
number: (def) => new NumberNode(def),
|
|
237
|
+
boolean: (def) => new BooleanNode(def),
|
|
238
|
+
string: (def) => new StringNode(def),
|
|
239
|
+
color: (def) => new ColorNode(def),
|
|
240
|
+
/** A fixed set of choices. The default's literal type flows into the config
|
|
241
|
+
* type, so `f.enum('fast', ['fast', 'accurate'])` gives
|
|
242
|
+
* `'fast' | 'accurate'` with no `as const`. */
|
|
243
|
+
enum: (def, options) => new EnumNode(def, { options: expand(options) }),
|
|
244
|
+
/** A leaf with no declared kind: the rule chain decides, which is how a lab
|
|
245
|
+
* states a convention like "every `*Color` key is a color picker". */
|
|
246
|
+
value: (def) => new ValueNode(def),
|
|
247
|
+
/** A leaf of a kind a lab supplies the control for, through `controls`. */
|
|
248
|
+
custom: (kind, def, validate) => new CustomNode(kind, def, {}, validate ? { validate } : {}),
|
|
249
|
+
/** Collect leaves into an instrument's config. */
|
|
250
|
+
schema(nodes) {
|
|
251
|
+
return {
|
|
252
|
+
nodes,
|
|
253
|
+
defaults: () => {
|
|
254
|
+
const out = {};
|
|
255
|
+
for (const [key, node] of Object.entries(nodes)) out[key] = node.default;
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// src/config/rules.ts
|
|
263
|
+
function titleCase(key) {
|
|
264
|
+
const words = key.replace(/[_-]+/g, " ").split(/(?<=[a-z0-9])(?=[A-Z])/).join(" ").trim().toLowerCase();
|
|
265
|
+
return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);
|
|
266
|
+
}
|
|
267
|
+
var kindFromValue = (ctx) => {
|
|
268
|
+
const t = typeof ctx.default;
|
|
269
|
+
if (t === "boolean" || t === "number" || t === "string") return { kind: t };
|
|
270
|
+
return null;
|
|
271
|
+
};
|
|
272
|
+
var labelFromKey = (ctx) => ({ name: titleCase(ctx.key) });
|
|
273
|
+
var sliderWhenBounded = (ctx) => {
|
|
274
|
+
if (ctx.leaf.kind !== "number") return null;
|
|
275
|
+
const bounded = ctx.leaf.min !== void 0 && ctx.leaf.max !== void 0;
|
|
276
|
+
return bounded ? { control: "slider" } : null;
|
|
277
|
+
};
|
|
278
|
+
var descriptionDefault = () => ({ description: "" });
|
|
279
|
+
var builtinRules = [
|
|
280
|
+
kindFromValue,
|
|
281
|
+
labelFromKey,
|
|
282
|
+
sliderWhenBounded,
|
|
283
|
+
descriptionDefault
|
|
284
|
+
];
|
|
285
|
+
function applyRules(seed, ctx, rules) {
|
|
286
|
+
const acc = { ...seed };
|
|
287
|
+
for (const rule of rules) {
|
|
288
|
+
const patch = rule({ ...ctx, leaf: acc });
|
|
289
|
+
if (patch === null) continue;
|
|
290
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
291
|
+
if (v !== void 0 && acc[k] === void 0) {
|
|
292
|
+
acc[k] = v;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return acc;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/config/resolve.ts
|
|
300
|
+
function defined(annotations) {
|
|
301
|
+
const out = {};
|
|
302
|
+
for (const [k, v] of Object.entries(annotations)) if (v !== void 0) out[k] = v;
|
|
303
|
+
return out;
|
|
304
|
+
}
|
|
305
|
+
function resolveConfigSchema(schema, rules = []) {
|
|
306
|
+
const children = {};
|
|
307
|
+
const sectionOrder = [];
|
|
308
|
+
const sectionPaths = /* @__PURE__ */ new Map();
|
|
309
|
+
const showIf = /* @__PURE__ */ new Map();
|
|
310
|
+
const renderers = {};
|
|
311
|
+
const chain2 = [...rules, ...builtinRules];
|
|
312
|
+
for (const [key, node] of Object.entries(schema.nodes)) {
|
|
313
|
+
const seed = defined({
|
|
314
|
+
...node.annotations,
|
|
315
|
+
...node.kind === null ? {} : { kind: node.kind }
|
|
316
|
+
});
|
|
317
|
+
const patch = applyRules(seed, { key, path: key, default: node.default }, chain2);
|
|
318
|
+
children[key] = { ...patch, default: node.default };
|
|
319
|
+
const { section, showIf: predicate, render } = node.options;
|
|
320
|
+
if (section !== void 0) {
|
|
321
|
+
if (!sectionPaths.has(section)) {
|
|
322
|
+
sectionOrder.push(section);
|
|
323
|
+
sectionPaths.set(section, []);
|
|
324
|
+
}
|
|
325
|
+
sectionPaths.get(section)?.push(key);
|
|
326
|
+
}
|
|
327
|
+
if (predicate) showIf.set(key, predicate);
|
|
328
|
+
if (render) renderers[key] = render;
|
|
329
|
+
}
|
|
330
|
+
const group = { name: "", children };
|
|
331
|
+
const sections = sectionOrder.map((label) => ({
|
|
332
|
+
label,
|
|
333
|
+
paths: sectionPaths.get(label) ?? []
|
|
334
|
+
}));
|
|
335
|
+
return { group, sections, showIf, renderers };
|
|
336
|
+
}
|
|
337
|
+
var LabContext = createContext(null);
|
|
338
|
+
function useLabContext() {
|
|
339
|
+
const ctx = useContext(LabContext);
|
|
340
|
+
if (ctx === null) {
|
|
341
|
+
throw new Error("useLabContext must be used inside <Lab>");
|
|
342
|
+
}
|
|
343
|
+
return ctx;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/config/useConfigSchema.ts
|
|
347
|
+
var NO_RULES = [];
|
|
348
|
+
function useConfigSchema(instrument) {
|
|
349
|
+
const lab = useContext(LabContext);
|
|
350
|
+
const rules = lab?.configRules ?? NO_RULES;
|
|
351
|
+
return useMemo(
|
|
352
|
+
() => instrument.config ? resolveConfigSchema(instrument.config, rules) : fromConfigFields(instrument.configSchema?.() ?? []),
|
|
353
|
+
[instrument, rules]
|
|
354
|
+
);
|
|
117
355
|
}
|
|
118
356
|
|
|
119
357
|
// src/instrument/defineInstrument.ts
|
|
120
358
|
function defineInstrument(spec) {
|
|
359
|
+
if (spec.config && !("defaultConfig" in spec && spec.defaultConfig)) {
|
|
360
|
+
const schema = spec.config;
|
|
361
|
+
return { ...spec, defaultConfig: () => schema.defaults() };
|
|
362
|
+
}
|
|
121
363
|
return spec;
|
|
122
364
|
}
|
|
123
365
|
|
|
124
366
|
// src/instrument/validateConfigSchema.ts
|
|
125
|
-
var KNOWN_TYPES = ["slider", "checkbox", "select", "number", "text", "color"];
|
|
126
367
|
function validateConfigSchema(fields) {
|
|
127
368
|
const errors = [];
|
|
128
369
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
@@ -137,10 +378,6 @@ function validateConfigSchema(fields) {
|
|
|
137
378
|
if (field.label === "") {
|
|
138
379
|
errors.push(`Field "${field.key}" has empty label`);
|
|
139
380
|
}
|
|
140
|
-
if (!KNOWN_TYPES.includes(field.type)) {
|
|
141
|
-
errors.push(`Unknown field type: "${field.type}" on key "${field.key}"`);
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
381
|
if (field.type === "slider") {
|
|
145
382
|
if (!Number.isFinite(field.default)) {
|
|
146
383
|
errors.push(`Field "${field.key}": default must be a finite number`);
|
|
@@ -175,7 +412,7 @@ function validateConfigSchema(fields) {
|
|
|
175
412
|
return { valid: errors.length === 0, errors };
|
|
176
413
|
}
|
|
177
414
|
|
|
178
|
-
// ../theme/dist/chunk-
|
|
415
|
+
// ../theme/dist/chunk-O32P3G22.js
|
|
179
416
|
var THEME_SOURCES = {
|
|
180
417
|
"weasel": {
|
|
181
418
|
"primitives": {
|
|
@@ -361,6 +598,15 @@ var THEME_SOURCES = {
|
|
|
361
598
|
"type": "dimension",
|
|
362
599
|
"value": "5px"
|
|
363
600
|
},
|
|
601
|
+
"radius-lg": {
|
|
602
|
+
"type": "dimension",
|
|
603
|
+
"value": "14px"
|
|
604
|
+
},
|
|
605
|
+
"radius-pill": {
|
|
606
|
+
"type": "dimension",
|
|
607
|
+
"value": "999px",
|
|
608
|
+
"description": "Fully rounded ends \u2014 segmented controls, progress tracks."
|
|
609
|
+
},
|
|
364
610
|
"border-w": {
|
|
365
611
|
"type": "dimension",
|
|
366
612
|
"value": "1px"
|
|
@@ -378,29 +624,47 @@ var THEME_SOURCES = {
|
|
|
378
624
|
"tb-height": {
|
|
379
625
|
"type": "dimension",
|
|
380
626
|
"value": "28px",
|
|
381
|
-
"description": "Height of a horizontal toolbar strip (ActionsBar, OptionsBar,
|
|
382
|
-
},
|
|
383
|
-
"radius-lg": {
|
|
384
|
-
"type": "dimension",
|
|
385
|
-
"value": "14px"
|
|
627
|
+
"description": "Height of a horizontal toolbar strip (ActionsBar, OptionsBar, ToolOptionsBar). Taller than control-h so the controls inside a strip are not flush with its edges."
|
|
386
628
|
},
|
|
387
629
|
"control-h": {
|
|
388
630
|
"type": "dimension",
|
|
389
|
-
"value": "
|
|
390
|
-
"description": "Height of an interactive control \u2014 button, input, select. Distinct from tb-height, which sizes the strip a row of them sits in."
|
|
631
|
+
"value": "24px",
|
|
632
|
+
"description": "Height of an interactive control \u2014 button, input, select, segmented control. Distinct from tb-height, which sizes the strip a row of them sits in."
|
|
391
633
|
},
|
|
392
634
|
"glass-blur": {
|
|
393
635
|
"type": "dimension",
|
|
394
636
|
"value": "3px",
|
|
395
637
|
"description": "Backdrop blur radius for frosted surfaces. Pairs with glass-tint."
|
|
396
638
|
},
|
|
397
|
-
"font-size": {
|
|
639
|
+
"font-size-2xs": {
|
|
640
|
+
"type": "dimension",
|
|
641
|
+
"value": "9px",
|
|
642
|
+
"description": "Shortcut keys and index badges. The floor for chrome text."
|
|
643
|
+
},
|
|
644
|
+
"font-size-xs": {
|
|
398
645
|
"type": "dimension",
|
|
399
|
-
"value": "
|
|
646
|
+
"value": "10px",
|
|
647
|
+
"description": "Tool-button labels under an icon."
|
|
400
648
|
},
|
|
401
649
|
"font-size-sm": {
|
|
402
650
|
"type": "dimension",
|
|
403
|
-
"value": "11px"
|
|
651
|
+
"value": "11px",
|
|
652
|
+
"description": "Chrome labels, status readouts, section headings."
|
|
653
|
+
},
|
|
654
|
+
"font-size": {
|
|
655
|
+
"type": "dimension",
|
|
656
|
+
"value": "13px",
|
|
657
|
+
"description": "Controls and body text."
|
|
658
|
+
},
|
|
659
|
+
"font-size-lg": {
|
|
660
|
+
"type": "dimension",
|
|
661
|
+
"value": "16px",
|
|
662
|
+
"description": "Panel and dialog titles."
|
|
663
|
+
},
|
|
664
|
+
"font-size-xl": {
|
|
665
|
+
"type": "dimension",
|
|
666
|
+
"value": "20px",
|
|
667
|
+
"description": "The largest chrome text \u2014 a lab or app title."
|
|
404
668
|
},
|
|
405
669
|
"space-xs": {
|
|
406
670
|
"type": "dimension",
|
|
@@ -418,6 +682,20 @@ var THEME_SOURCES = {
|
|
|
418
682
|
"type": "dimension",
|
|
419
683
|
"value": "16px"
|
|
420
684
|
},
|
|
685
|
+
"tracking-none": {
|
|
686
|
+
"type": "dimension",
|
|
687
|
+
"value": "0"
|
|
688
|
+
},
|
|
689
|
+
"tracking-wide": {
|
|
690
|
+
"type": "dimension",
|
|
691
|
+
"value": "0.06em",
|
|
692
|
+
"description": "Uppercase section headings."
|
|
693
|
+
},
|
|
694
|
+
"tracking-wider": {
|
|
695
|
+
"type": "dimension",
|
|
696
|
+
"value": "0.08em",
|
|
697
|
+
"description": "Uppercase at 11px and below."
|
|
698
|
+
},
|
|
421
699
|
"z-toolbar": {
|
|
422
700
|
"type": "number",
|
|
423
701
|
"value": 10
|
|
@@ -430,6 +708,21 @@ var THEME_SOURCES = {
|
|
|
430
708
|
"type": "number",
|
|
431
709
|
"value": 30
|
|
432
710
|
},
|
|
711
|
+
"leading-tight": {
|
|
712
|
+
"type": "number",
|
|
713
|
+
"value": 1,
|
|
714
|
+
"description": "Single-line controls; the box sets the height."
|
|
715
|
+
},
|
|
716
|
+
"leading-snug": {
|
|
717
|
+
"type": "number",
|
|
718
|
+
"value": 1.2,
|
|
719
|
+
"description": "Headings and two-line labels."
|
|
720
|
+
},
|
|
721
|
+
"leading": {
|
|
722
|
+
"type": "number",
|
|
723
|
+
"value": 1.4,
|
|
724
|
+
"description": "Body and anything that wraps."
|
|
725
|
+
},
|
|
433
726
|
"backdrop": {
|
|
434
727
|
"type": "gradient",
|
|
435
728
|
"value": "none",
|
|
@@ -582,6 +875,16 @@ var THEME_SOURCES = {
|
|
|
582
875
|
"type": "color",
|
|
583
876
|
"value": "{color.gray-900}",
|
|
584
877
|
"description": "Text drawn on a filled foreground-colored element (e.g. a slider thumb). Flips with the mode, so it is a semantic, not an alias to a primitive."
|
|
878
|
+
},
|
|
879
|
+
"shadow": {
|
|
880
|
+
"type": "color",
|
|
881
|
+
"value": "rgba(0, 0, 0, 0.6)",
|
|
882
|
+
"description": "Elevation. Always darker than any surface \u2014 never derived from fg, which is near-white on dark."
|
|
883
|
+
},
|
|
884
|
+
"border-raised": {
|
|
885
|
+
"type": "color",
|
|
886
|
+
"value": "{color.gray-400}",
|
|
887
|
+
"description": "The line around a raised surface. Clears 3:1 against surface and surface-raised in either mode, which the general-purpose border does not; over surface-sunken it does not, so a panel on a sunken field needs its own answer."
|
|
585
888
|
}
|
|
586
889
|
},
|
|
587
890
|
"light": {
|
|
@@ -626,6 +929,14 @@ var THEME_SOURCES = {
|
|
|
626
929
|
"type": "color",
|
|
627
930
|
"value": "{color.gray-50}",
|
|
628
931
|
"description": "Flips with the mode \u2014 see the dark layer."
|
|
932
|
+
},
|
|
933
|
+
"shadow": {
|
|
934
|
+
"type": "color",
|
|
935
|
+
"value": "rgba(0, 0, 0, 0.18)"
|
|
936
|
+
},
|
|
937
|
+
"border-raised": {
|
|
938
|
+
"type": "color",
|
|
939
|
+
"value": "{color.gray-400}"
|
|
629
940
|
}
|
|
630
941
|
}
|
|
631
942
|
}
|
|
@@ -643,10 +954,10 @@ function hexToRgba(hex, alpha) {
|
|
|
643
954
|
if (!m) throw new Error(`Expected a hex color, got "${hex}"`);
|
|
644
955
|
const body = m[1];
|
|
645
956
|
const full = body.length === 3 ? body.replace(/./g, (c) => c + c) : body;
|
|
646
|
-
const
|
|
957
|
+
const r2 = Number.parseInt(full.slice(0, 2), 16);
|
|
647
958
|
const g = Number.parseInt(full.slice(2, 4), 16);
|
|
648
959
|
const b = Number.parseInt(full.slice(4, 6), 16);
|
|
649
|
-
return `rgba(${
|
|
960
|
+
return `rgba(${r2}, ${g}, ${b}, ${alpha})`;
|
|
650
961
|
}
|
|
651
962
|
var REF = /^\{([^}]+)\}$/;
|
|
652
963
|
function refTarget(value) {
|
|
@@ -658,7 +969,7 @@ function refTarget(value) {
|
|
|
658
969
|
return dot === -1 ? path : path.slice(dot + 1);
|
|
659
970
|
}
|
|
660
971
|
function fontStack(value) {
|
|
661
|
-
return value.map((
|
|
972
|
+
return value.map((f2) => typeof f2 === "string" && /\s/.test(f2) ? `'${f2}'` : String(f2)).join(", ");
|
|
662
973
|
}
|
|
663
974
|
function serialize(type, value) {
|
|
664
975
|
if (Array.isArray(value)) {
|
|
@@ -865,7 +1176,9 @@ var interstellar_tokens_default = {
|
|
|
865
1176
|
"fg-subtle": { $value: "#5a5a78" },
|
|
866
1177
|
accent: { $value: "#b08adb" },
|
|
867
1178
|
"accent-hover": { $value: "#c6a3ee" },
|
|
868
|
-
"focus-ring": { $value: "rgba(176, 138, 219, 0.45)" }
|
|
1179
|
+
"focus-ring": { $value: "rgba(176, 138, 219, 0.45)" },
|
|
1180
|
+
shadow: { $value: "rgba(0, 0, 0, 0.7)" },
|
|
1181
|
+
"border-raised": { $value: "rgba(255, 255, 255, 0.36)" }
|
|
869
1182
|
},
|
|
870
1183
|
gradient: {
|
|
871
1184
|
$type: "gradient",
|
|
@@ -887,7 +1200,9 @@ var interstellar_tokens_default = {
|
|
|
887
1200
|
"fg-subtle": { $value: "#a89b85" },
|
|
888
1201
|
accent: { $value: "#a86f3c" },
|
|
889
1202
|
"accent-hover": { $value: "#c2854f" },
|
|
890
|
-
"focus-ring": { $value: "rgba(168, 111, 60, 0.35)" }
|
|
1203
|
+
"focus-ring": { $value: "rgba(168, 111, 60, 0.35)" },
|
|
1204
|
+
shadow: { $value: "rgba(80, 60, 40, 0.18)" },
|
|
1205
|
+
"border-raised": { $value: "#9e8b71" }
|
|
891
1206
|
}
|
|
892
1207
|
}
|
|
893
1208
|
}
|
|
@@ -920,13 +1235,13 @@ function useDragDrop({
|
|
|
920
1235
|
const [drag, setDrag] = useState(null);
|
|
921
1236
|
const dragRef = useRef(null);
|
|
922
1237
|
dragRef.current = drag;
|
|
923
|
-
const
|
|
1238
|
+
const pendingPos = useRef(null);
|
|
924
1239
|
const isOverCanvas = useCallback(
|
|
925
1240
|
(screenPos) => {
|
|
926
1241
|
const el = canvasContainerRef.current;
|
|
927
1242
|
if (!el) return false;
|
|
928
|
-
const
|
|
929
|
-
return screenPos.x >=
|
|
1243
|
+
const r2 = el.getBoundingClientRect();
|
|
1244
|
+
return screenPos.x >= r2.left && screenPos.x <= r2.right && screenPos.y >= r2.top && screenPos.y <= r2.bottom;
|
|
930
1245
|
},
|
|
931
1246
|
[canvasContainerRef]
|
|
932
1247
|
);
|
|
@@ -934,42 +1249,44 @@ function useDragDrop({
|
|
|
934
1249
|
(screenPos) => {
|
|
935
1250
|
const el = canvasContainerRef.current;
|
|
936
1251
|
if (!el) return null;
|
|
937
|
-
const
|
|
938
|
-
return screenToWorld({ x: screenPos.x -
|
|
1252
|
+
const r2 = el.getBoundingClientRect();
|
|
1253
|
+
return screenToWorld({ x: screenPos.x - r2.left, y: screenPos.y - r2.top }, view);
|
|
939
1254
|
},
|
|
940
1255
|
[canvasContainerRef, view]
|
|
941
1256
|
);
|
|
1257
|
+
const frameLoop = useVisibleRaf(() => {
|
|
1258
|
+
const screenPos = pendingPos.current;
|
|
1259
|
+
pendingPos.current = null;
|
|
1260
|
+
const active = dragRef.current;
|
|
1261
|
+
if (!active || !screenPos) return;
|
|
1262
|
+
let feedback = null;
|
|
1263
|
+
if (capability.onDragOver && isOverCanvas(screenPos)) {
|
|
1264
|
+
const world = screenToWorldFromContainer(screenPos);
|
|
1265
|
+
if (world) feedback = capability.onDragOver(world, active.item, state, config);
|
|
1266
|
+
}
|
|
1267
|
+
setDrag({ ...active, screenPos, feedback });
|
|
1268
|
+
});
|
|
942
1269
|
useEffect(() => {
|
|
943
1270
|
if (!drag) return;
|
|
944
1271
|
const handleMove = (e) => {
|
|
945
1272
|
const screenPos = { x: e.clientX, y: e.clientY };
|
|
946
1273
|
const current = dragRef.current;
|
|
947
1274
|
if (!current) return;
|
|
948
|
-
if (
|
|
1275
|
+
if (pendingPos.current !== null) {
|
|
1276
|
+
pendingPos.current = screenPos;
|
|
949
1277
|
dragRef.current = { ...current, screenPos };
|
|
950
1278
|
setDrag(dragRef.current);
|
|
951
1279
|
return;
|
|
952
1280
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const active = dragRef.current;
|
|
956
|
-
if (!active) return;
|
|
957
|
-
let feedback = null;
|
|
958
|
-
if (capability.onDragOver && isOverCanvas(screenPos)) {
|
|
959
|
-
const world = screenToWorldFromContainer(screenPos);
|
|
960
|
-
if (world) feedback = capability.onDragOver(world, active.item, state, config);
|
|
961
|
-
}
|
|
962
|
-
setDrag({ ...active, screenPos, feedback });
|
|
963
|
-
});
|
|
1281
|
+
pendingPos.current = screenPos;
|
|
1282
|
+
frameLoop.request();
|
|
964
1283
|
};
|
|
965
1284
|
const handleUp = (e) => {
|
|
966
1285
|
const screenPos = { x: e.clientX, y: e.clientY };
|
|
967
1286
|
const active = dragRef.current;
|
|
968
1287
|
if (!active) return;
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
rafThrottle.current = null;
|
|
972
|
-
}
|
|
1288
|
+
pendingPos.current = null;
|
|
1289
|
+
frameLoop.cancel();
|
|
973
1290
|
if (isOverCanvas(screenPos)) {
|
|
974
1291
|
const world = screenToWorldFromContainer(screenPos);
|
|
975
1292
|
if (world) {
|
|
@@ -986,7 +1303,17 @@ function useDragDrop({
|
|
|
986
1303
|
window.removeEventListener("pointermove", handleMove);
|
|
987
1304
|
window.removeEventListener("pointerup", handleUp);
|
|
988
1305
|
};
|
|
989
|
-
}, [
|
|
1306
|
+
}, [
|
|
1307
|
+
drag,
|
|
1308
|
+
capability,
|
|
1309
|
+
state,
|
|
1310
|
+
config,
|
|
1311
|
+
setState,
|
|
1312
|
+
emit,
|
|
1313
|
+
frameLoop,
|
|
1314
|
+
isOverCanvas,
|
|
1315
|
+
screenToWorldFromContainer
|
|
1316
|
+
]);
|
|
990
1317
|
const startDrag = useCallback((item, originScreenPos) => {
|
|
991
1318
|
setDrag({ item, screenPos: originScreenPos, feedback: null });
|
|
992
1319
|
}, []);
|
|
@@ -1011,14 +1338,6 @@ function Palette({ items, onDragStart, className }) {
|
|
|
1011
1338
|
item.id
|
|
1012
1339
|
)) });
|
|
1013
1340
|
}
|
|
1014
|
-
var LabContext = createContext(null);
|
|
1015
|
-
function useLabContext() {
|
|
1016
|
-
const ctx = useContext(LabContext);
|
|
1017
|
-
if (ctx === null) {
|
|
1018
|
-
throw new Error("useLabContext must be used inside <Lab>");
|
|
1019
|
-
}
|
|
1020
|
-
return ctx;
|
|
1021
|
-
}
|
|
1022
1341
|
|
|
1023
1342
|
// src/state/view.ts
|
|
1024
1343
|
var DEFAULT_VIEW = { zoom: 1, pan: { x: 0, y: 0 } };
|
|
@@ -1030,112 +1349,40 @@ function as2DView(view) {
|
|
|
1030
1349
|
if (typeof v.pan.x !== "number" || typeof v.pan.y !== "number") return null;
|
|
1031
1350
|
return { zoom: v.zoom, pan: { x: v.pan.x, y: v.pan.y } };
|
|
1032
1351
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
ctx.instrumentName,
|
|
1037
|
-
" has no config fields."
|
|
1038
|
-
] }) });
|
|
1039
|
-
}
|
|
1040
|
-
return /* @__PURE__ */ jsx(Sidebar, { title: ctx.instrumentName, children: /* @__PURE__ */ jsx(
|
|
1041
|
-
ControlPanel,
|
|
1042
|
-
{
|
|
1043
|
-
fields: ctx.configFields,
|
|
1044
|
-
config: ctx.config,
|
|
1045
|
-
setConfig: (key, value) => ctx.setConfig(key, value)
|
|
1046
|
-
}
|
|
1047
|
-
) });
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
// src/ui/format.ts
|
|
1051
|
-
var MINUS = "\u2212";
|
|
1052
|
-
function formatNumber(n, fractionDigits) {
|
|
1053
|
-
if (!Number.isFinite(n)) return String(n);
|
|
1054
|
-
const s = fractionDigits == null ? String(n) : n.toFixed(fractionDigits);
|
|
1055
|
-
return s.startsWith("-") ? MINUS + s.slice(1) : s;
|
|
1352
|
+
var TrialDragContext = createContext(null);
|
|
1353
|
+
function useTrialDrag() {
|
|
1354
|
+
return useContext(TrialDragContext);
|
|
1056
1355
|
}
|
|
1057
|
-
function
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
if (zoom <= 2) return `${formatNumber(Math.round(zoom * 100))}%`;
|
|
1063
|
-
if (zoom >= 100) return `${Math.round(zoom).toLocaleString("en-US")}x`;
|
|
1064
|
-
return `${formatNumber(Math.round(zoom * 10) / 10)}x`;
|
|
1065
|
-
}
|
|
1066
|
-
function DefaultStatusBar({ ctx }) {
|
|
1067
|
-
return /* @__PURE__ */ jsxs(StatusBar, { children: [
|
|
1068
|
-
/* @__PURE__ */ jsx(StatusBar.Section, { children: ctx.instrumentName }),
|
|
1069
|
-
ctx.zoom === null ? null : /* @__PURE__ */ jsx(StatusBar.Section, { children: formatZoom(ctx.zoom) })
|
|
1356
|
+
function Draggable({ nodeId, title, children }) {
|
|
1357
|
+
const handlers = useDragHandle(nodeId);
|
|
1358
|
+
return /* @__PURE__ */ jsxs("div", { className: "lk-trial__titlebar lk-trial__titlebar--draggable", ...handlers, children: [
|
|
1359
|
+
/* @__PURE__ */ jsx("span", { className: "lk-trial__title", children: title }),
|
|
1360
|
+
children
|
|
1070
1361
|
] });
|
|
1071
1362
|
}
|
|
1072
|
-
function
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
onClick: ctx.redo,
|
|
1080
|
-
disabled: !ctx.canRedo,
|
|
1081
|
-
title: "Redo (Cmd/Ctrl+Shift+Z)",
|
|
1082
|
-
children: "Redo"
|
|
1083
|
-
}
|
|
1084
|
-
),
|
|
1085
|
-
/* @__PURE__ */ jsx(Toolbar.Spacer, {})
|
|
1086
|
-
] }),
|
|
1087
|
-
ctx.hasCanvas && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1088
|
-
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: ctx.zoomOut, title: "Zoom out", children: "\u2212" }),
|
|
1089
|
-
/* @__PURE__ */ jsx("span", { className: "lk-toolbar__zoom-label", children: formatZoom(ctx.zoom) }),
|
|
1090
|
-
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: ctx.zoomIn, title: "Zoom in", children: "+" }),
|
|
1091
|
-
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: ctx.resetZoom, title: "Reset zoom", children: "1:1" }),
|
|
1092
|
-
/* @__PURE__ */ jsx(Toolbar.Spacer, {})
|
|
1093
|
-
] }),
|
|
1094
|
-
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: () => ctx.saveSnapshot(), title: "Save snapshot (Cmd/Ctrl+S)", children: "Save" }),
|
|
1095
|
-
ctx.savedSnapshots.length > 0 && /* @__PURE__ */ jsxs(
|
|
1096
|
-
"select",
|
|
1097
|
-
{
|
|
1098
|
-
className: "lk-toolbar__load-select",
|
|
1099
|
-
"aria-label": "Load snapshot",
|
|
1100
|
-
defaultValue: "",
|
|
1101
|
-
onChange: (e) => {
|
|
1102
|
-
const id = e.target.value;
|
|
1103
|
-
if (id) {
|
|
1104
|
-
ctx.loadSnapshot(id);
|
|
1105
|
-
e.target.value = "";
|
|
1106
|
-
}
|
|
1107
|
-
},
|
|
1108
|
-
children: [
|
|
1109
|
-
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Load\u2026" }),
|
|
1110
|
-
ctx.savedSnapshots.map((sn) => /* @__PURE__ */ jsx("option", { value: sn.id, children: sn.name }, sn.id))
|
|
1111
|
-
]
|
|
1112
|
-
}
|
|
1113
|
-
),
|
|
1114
|
-
/* @__PURE__ */ jsx(Toolbar.Spacer, {}),
|
|
1115
|
-
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: ctx.clone, title: "Clone trial", children: "Clone" }),
|
|
1116
|
-
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: ctx.reset, title: "Reset trial", children: "Reset" }),
|
|
1117
|
-
/* @__PURE__ */ jsx(
|
|
1118
|
-
Toolbar.Button,
|
|
1119
|
-
{
|
|
1120
|
-
onClick: ctx.close,
|
|
1121
|
-
disabled: ctx.isLastTrial,
|
|
1122
|
-
title: ctx.isLastTrial ? "Cannot close the last trial" : "Close trial",
|
|
1123
|
-
children: "Close"
|
|
1124
|
-
}
|
|
1125
|
-
)
|
|
1363
|
+
function TrialTitleBar({ title, children }) {
|
|
1364
|
+
const drag = useTrialDrag();
|
|
1365
|
+
if (drag)
|
|
1366
|
+
return /* @__PURE__ */ jsx(Draggable, { nodeId: drag.nodeId, title, children });
|
|
1367
|
+
return /* @__PURE__ */ jsxs("div", { className: "lk-trial__titlebar", children: [
|
|
1368
|
+
/* @__PURE__ */ jsx("span", { className: "lk-trial__title", children: title }),
|
|
1369
|
+
children
|
|
1126
1370
|
] });
|
|
1127
1371
|
}
|
|
1372
|
+
var NO_OP = () => {
|
|
1373
|
+
};
|
|
1128
1374
|
function TrialChrome({
|
|
1129
1375
|
trialId,
|
|
1130
1376
|
record,
|
|
1131
1377
|
instrument,
|
|
1132
1378
|
isLastTrial,
|
|
1133
|
-
toolbar,
|
|
1134
|
-
sidebar,
|
|
1135
|
-
statusBar,
|
|
1136
1379
|
undoBindings,
|
|
1137
1380
|
job,
|
|
1138
|
-
|
|
1381
|
+
trialChrome,
|
|
1382
|
+
chrome,
|
|
1383
|
+
suppress,
|
|
1384
|
+
activeToolId = null,
|
|
1385
|
+
setActiveTool = NO_OP,
|
|
1139
1386
|
children
|
|
1140
1387
|
}) {
|
|
1141
1388
|
const lab = useLabContext();
|
|
@@ -1145,7 +1392,8 @@ function TrialChrome({
|
|
|
1145
1392
|
const updateTrialConfig = useStore(storeCtx.store, (s) => s.updateTrialConfig);
|
|
1146
1393
|
const updateTrialState = useStore(storeCtx.store, (s) => s.updateTrialState);
|
|
1147
1394
|
const view2d = as2DView(record.view);
|
|
1148
|
-
const
|
|
1395
|
+
const configSchema = useConfigSchema(instrument);
|
|
1396
|
+
const ctx = useMemo(() => {
|
|
1149
1397
|
const setZoom = (z) => {
|
|
1150
1398
|
if (!view2d) return;
|
|
1151
1399
|
updateTrialView(trialId, { ...view2d, zoom: z });
|
|
@@ -1153,33 +1401,15 @@ function TrialChrome({
|
|
|
1153
1401
|
return {
|
|
1154
1402
|
trialId,
|
|
1155
1403
|
instrumentName: record.instrumentName,
|
|
1156
|
-
|
|
1404
|
+
isLastTrial,
|
|
1405
|
+
zoom: view2d ? view2d.zoom : null,
|
|
1406
|
+
setZoom,
|
|
1157
1407
|
canUndo: undoBindings?.canUndo ?? false,
|
|
1158
1408
|
canRedo: undoBindings?.canRedo ?? false,
|
|
1159
|
-
undo: undoBindings?.undo ??
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
zoom: view2d?.zoom ?? 1,
|
|
1164
|
-
setZoom,
|
|
1165
|
-
zoomIn: () => setZoom((view2d?.zoom ?? 1) * 1.25),
|
|
1166
|
-
zoomOut: () => setZoom((view2d?.zoom ?? 1) * 0.8),
|
|
1167
|
-
resetZoom: () => setZoom(1),
|
|
1168
|
-
hasCanvas: instrument.canvas != null,
|
|
1169
|
-
savedSnapshots: lab.savedSnapshots.filter((s) => s.trialId === trialId),
|
|
1170
|
-
saveSnapshot: (name) => lab.saveSnapshot(trialId, name),
|
|
1171
|
-
loadSnapshot: (snapshotId) => lab.loadSnapshot(trialId, snapshotId),
|
|
1172
|
-
clone: () => lab.cloneTrial(trialId),
|
|
1173
|
-
reset: () => lab.resetTrial(trialId),
|
|
1174
|
-
close: () => lab.closeTrial(trialId),
|
|
1175
|
-
isLastTrial
|
|
1176
|
-
};
|
|
1177
|
-
}, [trialId, record, instrument, lab, isLastTrial, updateTrialView, undoBindings, view2d]);
|
|
1178
|
-
const sidebarCtx = useMemo(
|
|
1179
|
-
() => ({
|
|
1180
|
-
trialId,
|
|
1181
|
-
instrumentName: record.instrumentName,
|
|
1182
|
-
configFields: instrument.configSchema?.() ?? [],
|
|
1409
|
+
undo: undoBindings?.undo ?? NO_OP,
|
|
1410
|
+
redo: undoBindings?.redo ?? NO_OP,
|
|
1411
|
+
configSchema,
|
|
1412
|
+
configFields: instrument.config ? [] : instrument.configSchema?.() ?? [],
|
|
1183
1413
|
config: record.config,
|
|
1184
1414
|
setConfig: (key, value) => {
|
|
1185
1415
|
const prevConfig = record.config;
|
|
@@ -1194,25 +1424,54 @@ function TrialChrome({
|
|
|
1194
1424
|
const nextState = instrument.onConfigChange(nextConfig, prevConfig, record.state);
|
|
1195
1425
|
updateTrialState(trialId, nextState);
|
|
1196
1426
|
}
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1427
|
+
},
|
|
1428
|
+
savedSnapshots: lab.savedSnapshots.filter((s) => s.trialId === trialId),
|
|
1429
|
+
saveSnapshot: (name) => lab.saveSnapshot(trialId, name),
|
|
1430
|
+
loadSnapshot: (snapshotId) => lab.loadSnapshot(trialId, snapshotId),
|
|
1431
|
+
clone: () => lab.cloneTrial(trialId),
|
|
1432
|
+
reset: () => lab.resetTrial(trialId),
|
|
1433
|
+
close: () => lab.closeTrial(trialId),
|
|
1434
|
+
activeToolId,
|
|
1435
|
+
setActiveTool
|
|
1436
|
+
};
|
|
1437
|
+
}, [
|
|
1202
1438
|
trialId,
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1439
|
+
record,
|
|
1440
|
+
instrument,
|
|
1441
|
+
lab,
|
|
1442
|
+
isLastTrial,
|
|
1443
|
+
updateTrialView,
|
|
1444
|
+
updateTrialConfig,
|
|
1445
|
+
updateTrialState,
|
|
1446
|
+
undoBindings,
|
|
1447
|
+
view2d,
|
|
1448
|
+
activeToolId,
|
|
1449
|
+
setActiveTool,
|
|
1450
|
+
configSchema
|
|
1451
|
+
]);
|
|
1452
|
+
const contributions = useMemo(
|
|
1453
|
+
() => suppressContributions(
|
|
1454
|
+
mergeContributions(
|
|
1455
|
+
builtinContributions(instrument, ctx, lab.controls),
|
|
1456
|
+
[...instrument.chrome ?? []],
|
|
1457
|
+
[...trialChrome ?? []],
|
|
1458
|
+
[...chrome ?? []]
|
|
1459
|
+
),
|
|
1460
|
+
suppress ?? []
|
|
1461
|
+
),
|
|
1462
|
+
[instrument, ctx, trialChrome, chrome, suppress, lab.controls]
|
|
1463
|
+
);
|
|
1464
|
+
const inRegion = (region) => contributions.filter((c) => c.region === region);
|
|
1206
1465
|
const handleKeyDown = (e) => {
|
|
1207
1466
|
const mod = e.metaKey || e.ctrlKey;
|
|
1208
1467
|
if (!mod) return;
|
|
1209
1468
|
if (e.key === "z" || e.key === "Z") {
|
|
1210
1469
|
e.preventDefault();
|
|
1211
|
-
if (e.shiftKey)
|
|
1212
|
-
else
|
|
1470
|
+
if (e.shiftKey) ctx.redo();
|
|
1471
|
+
else ctx.undo();
|
|
1213
1472
|
} else if (e.key === "s" || e.key === "S") {
|
|
1214
1473
|
e.preventDefault();
|
|
1215
|
-
|
|
1474
|
+
ctx.saveSnapshot();
|
|
1216
1475
|
}
|
|
1217
1476
|
};
|
|
1218
1477
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1220,36 +1479,28 @@ function TrialChrome({
|
|
|
1220
1479
|
{
|
|
1221
1480
|
className: "lk-trial",
|
|
1222
1481
|
"aria-label": `Trial ${record.instrumentName}`,
|
|
1482
|
+
tabIndex: -1,
|
|
1223
1483
|
onKeyDown: handleKeyDown,
|
|
1224
1484
|
children: [
|
|
1225
|
-
/* @__PURE__ */ jsx(
|
|
1485
|
+
/* @__PURE__ */ jsx(TrialTitleBar, { title: record.instrumentName, children: /* @__PURE__ */ jsx(TitleBarRegion, { contributions: inRegion("titlebar"), ctx }) }),
|
|
1486
|
+
/* @__PURE__ */ jsx("div", { className: "lk-trial__toolbar", children: /* @__PURE__ */ jsx(ToolbarRegion, { contributions: inRegion("toolbar"), ctx }) }),
|
|
1226
1487
|
/* @__PURE__ */ jsxs("div", { className: "lk-trial__body", children: [
|
|
1227
|
-
/* @__PURE__ */
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1488
|
+
/* @__PURE__ */ jsx(PaletteRegion, { contributions: inRegion("palette"), ctx }),
|
|
1489
|
+
/* @__PURE__ */ jsx("div", { className: "lk-trial__sidebar", children: /* @__PURE__ */ jsx(SidebarRegion, { contributions: inRegion("sidebar"), ctx }) }),
|
|
1490
|
+
/* @__PURE__ */ jsxs("div", { className: `lk-trial__content${instrument.canvas ? " lk-trial__content--flush" : ""}`, children: [
|
|
1491
|
+
children,
|
|
1492
|
+
/* @__PURE__ */ jsx(ViewportRegion, { contributions: inRegion("viewport"), ctx })
|
|
1493
|
+
] })
|
|
1232
1494
|
] }),
|
|
1233
1495
|
/* @__PURE__ */ jsxs("div", { className: "lk-trial__status", children: [
|
|
1234
|
-
job ? /* @__PURE__ */
|
|
1235
|
-
|
|
1236
|
-
job.done,
|
|
1237
|
-
job.total === null ? "" : ` / ${job.total}`
|
|
1238
|
-
] }),
|
|
1239
|
-
job.failures.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "lk-trial__job-failures", children: [
|
|
1240
|
-
job.failures.length,
|
|
1241
|
-
" failed"
|
|
1242
|
-
] }) : null,
|
|
1243
|
-
job.error ? /* @__PURE__ */ jsx("span", { className: "lk-trial__job-error", children: job.error }) : null,
|
|
1244
|
-
job.status === "running" ? /* @__PURE__ */ jsx("button", { type: "button", className: "lk-trial__job-cancel", onClick: job.cancel, children: "Cancel" }) : null
|
|
1245
|
-
] }) : null,
|
|
1246
|
-
statusBar ? statusBar(statusCtx) : /* @__PURE__ */ jsx(DefaultStatusBar, { ctx: statusCtx })
|
|
1496
|
+
job ? /* @__PURE__ */ jsx(JobProgress, { job }) : null,
|
|
1497
|
+
/* @__PURE__ */ jsx(StatusRegion, { contributions: inRegion("status"), ctx })
|
|
1247
1498
|
] })
|
|
1248
1499
|
]
|
|
1249
1500
|
}
|
|
1250
1501
|
);
|
|
1251
1502
|
}
|
|
1252
|
-
function Trial({ id }) {
|
|
1503
|
+
function Trial({ id, chrome, suppress }) {
|
|
1253
1504
|
const lab = useLabContext();
|
|
1254
1505
|
const storeCtx = useContext(LabStoreContext);
|
|
1255
1506
|
if (!storeCtx) throw new Error("[labkit] <Trial> requires <LabStoreProvider>");
|
|
@@ -1273,16 +1524,21 @@ function Trial({ id }) {
|
|
|
1273
1524
|
record,
|
|
1274
1525
|
instrument,
|
|
1275
1526
|
store: storeCtx.store,
|
|
1276
|
-
isLast: lab.trials.length <= 1
|
|
1527
|
+
isLast: lab.trials.length <= 1,
|
|
1528
|
+
chrome,
|
|
1529
|
+
suppress
|
|
1277
1530
|
}
|
|
1278
1531
|
);
|
|
1279
1532
|
}
|
|
1280
|
-
function TrialRuntime({ record, instrument, store, isLast }) {
|
|
1533
|
+
function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
1281
1534
|
const canvasContainerRef = useRef(null);
|
|
1282
1535
|
const updateTrialState = useStore(store, (s) => s.updateTrialState);
|
|
1283
1536
|
const updateTrialConfig = useStore(store, (s) => s.updateTrialConfig);
|
|
1284
1537
|
const updateTrialView = useStore(store, (s) => s.updateTrialView);
|
|
1285
1538
|
const updateTrialUndoStack = useStore(store, (s) => s.updateTrialUndoStack);
|
|
1539
|
+
const labToolId = useStore(store, (s) => s.activeToolId);
|
|
1540
|
+
const setLabTool = useStore(store, (s) => s.setLabTool);
|
|
1541
|
+
const setTrialTool = useStore(store, (s) => s.setTrialTool);
|
|
1286
1542
|
const busRef = useRef(null);
|
|
1287
1543
|
if (busRef.current === null) busRef.current = createEventBus();
|
|
1288
1544
|
const bus = busRef.current;
|
|
@@ -1299,6 +1555,12 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1299
1555
|
updateTrialUndoStack(record.id, (prev) => pushSnapshot(prev, snap, maxDepth));
|
|
1300
1556
|
};
|
|
1301
1557
|
const setView = (v) => updateTrialView(record.id, v);
|
|
1558
|
+
const declaresTools = instrument.tools != null;
|
|
1559
|
+
const resolvedToolId = declaresTools ? record.activeToolId ?? instrument.tools?.initial ?? instrument.tools?.tools[0]?.id ?? null : labToolId;
|
|
1560
|
+
const setActiveTool = (id) => {
|
|
1561
|
+
if (declaresTools) setTrialTool(record.id, id);
|
|
1562
|
+
else setLabTool(id);
|
|
1563
|
+
};
|
|
1302
1564
|
const view2d = as2DView(record.view);
|
|
1303
1565
|
const jobCap = instrument.job;
|
|
1304
1566
|
const job = useJob({
|
|
@@ -1332,7 +1594,8 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1332
1594
|
setZoom: (z) => {
|
|
1333
1595
|
if (!view2d) return;
|
|
1334
1596
|
updateTrialView(record.id, { ...view2d, zoom: z });
|
|
1335
|
-
}
|
|
1597
|
+
},
|
|
1598
|
+
activeToolId: resolvedToolId
|
|
1336
1599
|
},
|
|
1337
1600
|
emit: (event) => {
|
|
1338
1601
|
snapshotIfNeeded(event);
|
|
@@ -1372,7 +1635,7 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1372
1635
|
}, [instrument.canvas, record.state, record.config, layerVisibility, layerOrder]);
|
|
1373
1636
|
const layerDescriptors = useMemo(() => {
|
|
1374
1637
|
if (!instrument.layers) return [];
|
|
1375
|
-
return instrument.layers.ids.map((
|
|
1638
|
+
return instrument.layers.ids.map((l) => typeof l === "string" ? { id: l, label: l } : l);
|
|
1376
1639
|
}, [instrument.layers]);
|
|
1377
1640
|
const dragDropResult = useDragDrop({
|
|
1378
1641
|
capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i, s) => s },
|
|
@@ -1391,8 +1654,8 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1391
1654
|
});
|
|
1392
1655
|
const paletteItems = useMemo(() => {
|
|
1393
1656
|
if (!instrument.dragDrop) return [];
|
|
1394
|
-
const
|
|
1395
|
-
return typeof
|
|
1657
|
+
const p3 = instrument.dragDrop.palette;
|
|
1658
|
+
return typeof p3 === "function" ? p3(record.state, record.config) : p3;
|
|
1396
1659
|
}, [instrument.dragDrop, record.state, record.config]);
|
|
1397
1660
|
const layersWithFeedback = useMemo(() => {
|
|
1398
1661
|
if (!dragDropResult.drag?.feedback) return canvasLayers;
|
|
@@ -1406,12 +1669,12 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1406
1669
|
render: (ctx) => {
|
|
1407
1670
|
const el = canvasContainerRef.current;
|
|
1408
1671
|
if (!el) return;
|
|
1409
|
-
const
|
|
1672
|
+
const r2 = el.getBoundingClientRect();
|
|
1410
1673
|
ctx.save();
|
|
1411
1674
|
ctx.strokeStyle = fb.ok ? "#3a7" : "#c44";
|
|
1412
1675
|
ctx.lineWidth = 2;
|
|
1413
1676
|
ctx.beginPath();
|
|
1414
|
-
ctx.arc(screen.x -
|
|
1677
|
+
ctx.arc(screen.x - r2.left, screen.y - r2.top, 16, 0, Math.PI * 2);
|
|
1415
1678
|
ctx.stroke();
|
|
1416
1679
|
ctx.restore();
|
|
1417
1680
|
}
|
|
@@ -1420,45 +1683,64 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1420
1683
|
}, [canvasLayers, dragDropResult.drag]);
|
|
1421
1684
|
let body;
|
|
1422
1685
|
if (instrument.canvas) {
|
|
1423
|
-
body = /* @__PURE__ */ jsxs(
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
}
|
|
1438
|
-
),
|
|
1439
|
-
/* @__PURE__ */ jsx(DragOverlay, { drag: dragDropResult.drag })
|
|
1440
|
-
]
|
|
1441
|
-
}
|
|
1442
|
-
);
|
|
1686
|
+
body = /* @__PURE__ */ jsxs("div", { ref: canvasContainerRef, className: "lk-trial__canvas-host", children: [
|
|
1687
|
+
/* @__PURE__ */ jsx(
|
|
1688
|
+
CanvasStack,
|
|
1689
|
+
{
|
|
1690
|
+
layers: layersWithFeedback,
|
|
1691
|
+
view: view2d ?? DEFAULT_VIEW,
|
|
1692
|
+
onViewChange: setView,
|
|
1693
|
+
minZoom: instrument.canvas.minZoom,
|
|
1694
|
+
maxZoom: instrument.canvas.maxZoom,
|
|
1695
|
+
children: instrument.render(renderCtx)
|
|
1696
|
+
}
|
|
1697
|
+
),
|
|
1698
|
+
/* @__PURE__ */ jsx(DragOverlay, { drag: dragDropResult.drag })
|
|
1699
|
+
] });
|
|
1443
1700
|
} else {
|
|
1444
1701
|
body = instrument.render(renderCtx);
|
|
1445
1702
|
}
|
|
1446
|
-
const
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1703
|
+
const startDrag = dragDropResult.startDrag;
|
|
1704
|
+
const paletteNode = useMemo(
|
|
1705
|
+
() => paletteItems.length > 0 ? /* @__PURE__ */ jsx(Palette, { items: paletteItems, onDragStart: startDrag }) : null,
|
|
1706
|
+
[paletteItems, startDrag]
|
|
1707
|
+
);
|
|
1708
|
+
const layerListNode = useMemo(
|
|
1709
|
+
() => instrument.layers && layerDescriptors.length > 0 ? /* @__PURE__ */ jsx(
|
|
1710
|
+
LayerList,
|
|
1711
|
+
{
|
|
1712
|
+
layers: layerDescriptors,
|
|
1713
|
+
visibility: layerVisibility,
|
|
1714
|
+
onReorder: (next) => {
|
|
1715
|
+
setLayerOrder(next.map((l) => l.id));
|
|
1716
|
+
bus.emit("layers.reorder");
|
|
1717
|
+
},
|
|
1718
|
+
onToggle: (lid, visible) => {
|
|
1719
|
+
setLayerVisibility((prev) => ({ ...prev, [lid]: visible }));
|
|
1720
|
+
bus.emit("layers.toggle");
|
|
1721
|
+
}
|
|
1459
1722
|
}
|
|
1723
|
+
) : null,
|
|
1724
|
+
[instrument.layers, layerDescriptors, layerVisibility, bus]
|
|
1725
|
+
);
|
|
1726
|
+
const extraChrome = useMemo(() => {
|
|
1727
|
+
const out = [];
|
|
1728
|
+
if (paletteNode) {
|
|
1729
|
+
out.push({
|
|
1730
|
+
id: "dragdrop-palette",
|
|
1731
|
+
region: "sidebar",
|
|
1732
|
+
item: { title: "Parts", body: paletteNode }
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
if (layerListNode) {
|
|
1736
|
+
out.push({
|
|
1737
|
+
id: "layer-list",
|
|
1738
|
+
region: "sidebar",
|
|
1739
|
+
item: { title: "Layers", body: layerListNode }
|
|
1740
|
+
});
|
|
1460
1741
|
}
|
|
1461
|
-
|
|
1742
|
+
return out;
|
|
1743
|
+
}, [paletteNode, layerListNode]);
|
|
1462
1744
|
return /* @__PURE__ */ jsx(
|
|
1463
1745
|
TrialChrome,
|
|
1464
1746
|
{
|
|
@@ -1468,10 +1750,11 @@ function TrialRuntime({ record, instrument, store, isLast }) {
|
|
|
1468
1750
|
instrument,
|
|
1469
1751
|
isLastTrial: isLast,
|
|
1470
1752
|
undoBindings,
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1753
|
+
trialChrome: extraChrome,
|
|
1754
|
+
chrome,
|
|
1755
|
+
suppress,
|
|
1756
|
+
activeToolId: resolvedToolId,
|
|
1757
|
+
setActiveTool,
|
|
1475
1758
|
children: body
|
|
1476
1759
|
}
|
|
1477
1760
|
);
|
|
@@ -1544,6 +1827,74 @@ function reorderTrials(trials, ids) {
|
|
|
1544
1827
|
const rest = trials.filter((w) => !seen.has(w.id));
|
|
1545
1828
|
return [...named, ...rest];
|
|
1546
1829
|
}
|
|
1830
|
+
var MODES = [
|
|
1831
|
+
{ value: "auto", label: "Auto" },
|
|
1832
|
+
{ value: "light", label: "Light" },
|
|
1833
|
+
{ value: "dark", label: "Dark" }
|
|
1834
|
+
];
|
|
1835
|
+
function LabHeader() {
|
|
1836
|
+
const lab = useLabContext();
|
|
1837
|
+
const only = lab.instruments.length === 1 ? lab.instruments[0] : null;
|
|
1838
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1839
|
+
only ? /* @__PURE__ */ jsxs(
|
|
1840
|
+
"button",
|
|
1841
|
+
{
|
|
1842
|
+
type: "button",
|
|
1843
|
+
className: "lk-lab-header__add",
|
|
1844
|
+
onClick: () => lab.addTrial(only.name),
|
|
1845
|
+
children: [
|
|
1846
|
+
/* @__PURE__ */ jsx(vt, { size: 16 }),
|
|
1847
|
+
/* @__PURE__ */ jsx("span", { children: "Add trial" })
|
|
1848
|
+
]
|
|
1849
|
+
}
|
|
1850
|
+
) : (
|
|
1851
|
+
// Held at null so the control stays an "add one" action rather than
|
|
1852
|
+
// reading as the current instrument.
|
|
1853
|
+
/* @__PURE__ */ jsx(
|
|
1854
|
+
p2,
|
|
1855
|
+
{
|
|
1856
|
+
className: "lk-lab-header__add-select",
|
|
1857
|
+
"aria-label": "Add trial",
|
|
1858
|
+
placeholder: "Add trial\u2026",
|
|
1859
|
+
selectedKey: null,
|
|
1860
|
+
options: lab.instruments.map((i) => ({ value: i.name, label: i.name })),
|
|
1861
|
+
onSelectionChange: (name) => {
|
|
1862
|
+
if (name != null) lab.addTrial(String(name));
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
)
|
|
1866
|
+
),
|
|
1867
|
+
/* @__PURE__ */ jsx(
|
|
1868
|
+
r,
|
|
1869
|
+
{
|
|
1870
|
+
className: "lk-lab-header__mode",
|
|
1871
|
+
ariaLabel: "Color mode",
|
|
1872
|
+
items: MODES,
|
|
1873
|
+
value: lab.mode,
|
|
1874
|
+
onChange: (next) => {
|
|
1875
|
+
if (next) lab.setMode(next);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
)
|
|
1879
|
+
] });
|
|
1880
|
+
}
|
|
1881
|
+
function LabPalette({ tools }) {
|
|
1882
|
+
const storeCtx = useContext(LabStoreContext);
|
|
1883
|
+
if (!storeCtx) throw new Error("[labkit] LabPalette requires <LabStoreProvider>");
|
|
1884
|
+
const activeToolId = useStore(storeCtx.store, (s) => s.activeToolId);
|
|
1885
|
+
const setLabTool = useStore(storeCtx.store, (s) => s.setLabTool);
|
|
1886
|
+
const contributions = tools.map((t) => ({
|
|
1887
|
+
id: t.id,
|
|
1888
|
+
region: "palette",
|
|
1889
|
+
group: t.group,
|
|
1890
|
+
item: { icon: t.icon, label: t.label, shortcut: t.shortcut }
|
|
1891
|
+
}));
|
|
1892
|
+
const ctx = {
|
|
1893
|
+
activeToolId,
|
|
1894
|
+
setActiveTool: setLabTool
|
|
1895
|
+
};
|
|
1896
|
+
return /* @__PURE__ */ jsx(PaletteRegion, { contributions, ctx });
|
|
1897
|
+
}
|
|
1547
1898
|
function query() {
|
|
1548
1899
|
if (typeof window === "undefined" || !window.matchMedia) return null;
|
|
1549
1900
|
return window.matchMedia("(prefers-color-scheme: light)");
|
|
@@ -1579,11 +1930,11 @@ var ZONE_ID = asNodeId("lk-workspace");
|
|
|
1579
1930
|
var STRATEGIES = { grid: gridStrategy };
|
|
1580
1931
|
var KIND = "trial";
|
|
1581
1932
|
function extentOf(store, id) {
|
|
1582
|
-
const
|
|
1583
|
-
if (!
|
|
1933
|
+
const p3 = store.getNode(id)?.membership?.placement;
|
|
1934
|
+
if (!p3) return null;
|
|
1584
1935
|
const out = {};
|
|
1585
|
-
if (
|
|
1586
|
-
if (
|
|
1936
|
+
if (p3.size) out.size = p3.size;
|
|
1937
|
+
if (p3.span) out.span = p3.span;
|
|
1587
1938
|
return Object.keys(out).length > 0 ? out : null;
|
|
1588
1939
|
}
|
|
1589
1940
|
function Workspace({
|
|
@@ -1663,10 +2014,10 @@ function Workspace({
|
|
|
1663
2014
|
[KIND]: ({ node }) => {
|
|
1664
2015
|
const content = byId.get(node.id) ?? null;
|
|
1665
2016
|
if (!reorderable) return content;
|
|
1666
|
-
return
|
|
1667
|
-
|
|
1668
|
-
/* @__PURE__ */ jsx("div", { className: "lk-trial-
|
|
1669
|
-
|
|
2017
|
+
return (
|
|
2018
|
+
// No grip strip: the trial's own title bar is the drag surface.
|
|
2019
|
+
/* @__PURE__ */ jsx(TrialDragContext.Provider, { value: { nodeId: node.id }, children: /* @__PURE__ */ jsx("div", { className: "lk-trial-tile", children: content }) })
|
|
2020
|
+
);
|
|
1670
2021
|
}
|
|
1671
2022
|
};
|
|
1672
2023
|
}, [nodeIds, items, reorderable]);
|
|
@@ -1704,8 +2055,8 @@ var NEBULA_SLOTS = [
|
|
|
1704
2055
|
];
|
|
1705
2056
|
function buildNebula(colors) {
|
|
1706
2057
|
const blobs = colors.map((c, i) => {
|
|
1707
|
-
const
|
|
1708
|
-
return `radial-gradient(ellipse ${
|
|
2058
|
+
const p3 = NEBULA_SLOTS[i % NEBULA_SLOTS.length];
|
|
2059
|
+
return `radial-gradient(ellipse ${p3.sx} ${p3.sy} at ${p3.cx} ${p3.cy}, color-mix(in srgb, ${c} 22%, transparent), transparent ${p3.stop})`;
|
|
1709
2060
|
});
|
|
1710
2061
|
blobs.push("radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)");
|
|
1711
2062
|
return blobs.join(", ");
|
|
@@ -1718,6 +2069,12 @@ function Lab({
|
|
|
1718
2069
|
mode,
|
|
1719
2070
|
nebula,
|
|
1720
2071
|
title,
|
|
2072
|
+
footer,
|
|
2073
|
+
chrome,
|
|
2074
|
+
suppress,
|
|
2075
|
+
tools,
|
|
2076
|
+
configRules,
|
|
2077
|
+
controls,
|
|
1721
2078
|
children
|
|
1722
2079
|
}) {
|
|
1723
2080
|
if (process.env.NODE_ENV !== "production" && instruments.length === 0) {
|
|
@@ -1782,7 +2139,8 @@ function Lab({
|
|
|
1782
2139
|
},
|
|
1783
2140
|
savedSnapshots,
|
|
1784
2141
|
saveSnapshot: (trialId, name) => {
|
|
1785
|
-
store.getState().
|
|
2142
|
+
const taken = store.getState().savedSnapshots.filter((s) => s.trialId === trialId).length;
|
|
2143
|
+
store.getState().saveSnapshot(trialId, name ?? `Snapshot ${taken + 1}`);
|
|
1786
2144
|
},
|
|
1787
2145
|
loadSnapshot: (trialId, snapshotId) => {
|
|
1788
2146
|
store.getState().loadSnapshot(snapshotId, trialId);
|
|
@@ -1793,9 +2151,11 @@ function Lab({
|
|
|
1793
2151
|
mode: modeValue,
|
|
1794
2152
|
setMode: (m) => {
|
|
1795
2153
|
store.getState().setMode(m);
|
|
1796
|
-
}
|
|
2154
|
+
},
|
|
2155
|
+
configRules,
|
|
2156
|
+
controls
|
|
1797
2157
|
};
|
|
1798
|
-
}, [instruments, trials, savedSnapshots, modeValue, store]);
|
|
2158
|
+
}, [instruments, trials, savedSnapshots, modeValue, store, configRules, controls]);
|
|
1799
2159
|
const backdropStyle = resolvedMode === "dark" && nebula && nebula.length > 0 ? { ["--wzl-backdrop"]: buildNebula(nebula) } : void 0;
|
|
1800
2160
|
return /* @__PURE__ */ jsx(LabStoreContext.Provider, { value: { store }, children: /* @__PURE__ */ jsx(LabContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
1801
2161
|
ThemeProvider,
|
|
@@ -1804,18 +2164,33 @@ function Lab({
|
|
|
1804
2164
|
mode: resolvedMode,
|
|
1805
2165
|
className: "lk-lab",
|
|
1806
2166
|
style: backdropStyle,
|
|
1807
|
-
children: /* @__PURE__ */ jsx(
|
|
1808
|
-
|
|
2167
|
+
children: /* @__PURE__ */ jsx(
|
|
2168
|
+
LabShell,
|
|
1809
2169
|
{
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
2170
|
+
title: title ?? "Labkit",
|
|
2171
|
+
mode: modeValue,
|
|
2172
|
+
footer,
|
|
2173
|
+
header: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2174
|
+
/* @__PURE__ */ jsx(LabHeader, {}),
|
|
2175
|
+
children
|
|
2176
|
+
] }),
|
|
2177
|
+
children: /* @__PURE__ */ jsxs("div", { className: "lk-lab__body", children: [
|
|
2178
|
+
tools ? /* @__PURE__ */ jsx(LabPalette, { tools }) : null,
|
|
2179
|
+
/* @__PURE__ */ jsx(
|
|
2180
|
+
Workspace,
|
|
2181
|
+
{
|
|
2182
|
+
ids: trials.map((w) => w.id),
|
|
2183
|
+
resizable: true,
|
|
2184
|
+
reorderable: true,
|
|
2185
|
+
onReorder: (ids) => contextValue.reorderTrials(ids),
|
|
2186
|
+
layout,
|
|
2187
|
+
onLayoutChange: (next) => store.getState().setLayout(next),
|
|
2188
|
+
children: trials.map((w) => /* @__PURE__ */ jsx(Trial, { id: w.id, chrome, suppress }, w.id))
|
|
2189
|
+
}
|
|
2190
|
+
)
|
|
2191
|
+
] })
|
|
1817
2192
|
}
|
|
1818
|
-
)
|
|
2193
|
+
)
|
|
1819
2194
|
}
|
|
1820
2195
|
) }) });
|
|
1821
2196
|
}
|
|
@@ -1860,7 +2235,7 @@ function CurveField({
|
|
|
1860
2235
|
}
|
|
1861
2236
|
flipped.sort((a, b) => a.x - b.x);
|
|
1862
2237
|
const out = [];
|
|
1863
|
-
for (const
|
|
2238
|
+
for (const p3 of flipped) out.push(p3.x, p3.y);
|
|
1864
2239
|
onChange(out);
|
|
1865
2240
|
}, [values, onChange]);
|
|
1866
2241
|
const handleFlipVertical = useCallback(() => {
|
|
@@ -2164,268 +2539,7 @@ function EffectCardList({
|
|
|
2164
2539
|
] }, String(item.id));
|
|
2165
2540
|
}) });
|
|
2166
2541
|
}
|
|
2167
|
-
function PropertyGroup({
|
|
2168
|
-
title,
|
|
2169
|
-
hidden,
|
|
2170
|
-
children,
|
|
2171
|
-
className,
|
|
2172
|
-
pack = "auto-color"
|
|
2173
|
-
}) {
|
|
2174
|
-
if (hidden) return null;
|
|
2175
|
-
const packClass = pack === "pairs" ? " lk-property-group--pairs" : "";
|
|
2176
|
-
const cls = `lk-property-group${packClass}${className ? ` ${className}` : ""}`;
|
|
2177
|
-
return /* @__PURE__ */ jsxs("div", { className: cls, children: [
|
|
2178
|
-
/* @__PURE__ */ jsxs("h3", { className: "lk-property-group__title", children: [
|
|
2179
|
-
/* @__PURE__ */ jsx("hr", {}),
|
|
2180
|
-
/* @__PURE__ */ jsx("span", { children: title }),
|
|
2181
|
-
/* @__PURE__ */ jsx("hr", {})
|
|
2182
|
-
] }),
|
|
2183
|
-
/* @__PURE__ */ jsx("div", { className: "lk-property-group__body", children })
|
|
2184
|
-
] });
|
|
2185
|
-
}
|
|
2186
|
-
function PropertyPanel({ title, children, className }) {
|
|
2187
|
-
const cls = className ? `lk-property-panel ${className}` : "lk-property-panel";
|
|
2188
|
-
return /* @__PURE__ */ jsxs("div", { className: cls, children: [
|
|
2189
|
-
title != null && /* @__PURE__ */ jsx("h2", { className: "lk-property-panel__title", children: title }),
|
|
2190
|
-
children
|
|
2191
|
-
] });
|
|
2192
|
-
}
|
|
2193
|
-
function PropertyList({ children, className, pack = "auto-color" }) {
|
|
2194
|
-
const packClass = pack === "pairs" ? " lk-property-list--pairs" : "";
|
|
2195
|
-
const cls = `lk-property-list${packClass}${className ? ` ${className}` : ""}`;
|
|
2196
|
-
return /* @__PURE__ */ jsx("div", { className: cls, children });
|
|
2197
|
-
}
|
|
2198
|
-
function PropertyRow({
|
|
2199
|
-
label,
|
|
2200
|
-
readout,
|
|
2201
|
-
variant = "default",
|
|
2202
|
-
layout = "block",
|
|
2203
|
-
children,
|
|
2204
|
-
htmlFor,
|
|
2205
|
-
className
|
|
2206
|
-
}) {
|
|
2207
|
-
const variantClass = variant === "default" ? "" : ` lk-property-row--${variant}`;
|
|
2208
|
-
const layoutClass = variant === "default" && layout === "inline" ? " lk-property-row--inline" : "";
|
|
2209
|
-
const cls = `lk-property-row${variantClass}${layoutClass}${className ? ` ${className}` : ""}`;
|
|
2210
|
-
return /* @__PURE__ */ jsxs("label", { className: cls, htmlFor, children: [
|
|
2211
|
-
/* @__PURE__ */ jsxs("span", { className: "lk-property-row__label", children: [
|
|
2212
|
-
label,
|
|
2213
|
-
readout != null && /* @__PURE__ */ jsx("em", { className: "lk-property-row__readout", children: readout })
|
|
2214
|
-
] }),
|
|
2215
|
-
children
|
|
2216
|
-
] });
|
|
2217
|
-
}
|
|
2218
|
-
function SliderRow({
|
|
2219
|
-
label,
|
|
2220
|
-
value,
|
|
2221
|
-
min,
|
|
2222
|
-
max,
|
|
2223
|
-
step = 1,
|
|
2224
|
-
onChange,
|
|
2225
|
-
format,
|
|
2226
|
-
unit,
|
|
2227
|
-
layout
|
|
2228
|
-
}) {
|
|
2229
|
-
const decimals = step >= 1 ? 0 : Math.min(6, Math.max(0, Math.ceil(-Math.log10(step))));
|
|
2230
|
-
const effectiveFormat = format ?? ((n) => formatNumber(n, decimals));
|
|
2231
|
-
return /* @__PURE__ */ jsx(
|
|
2232
|
-
PropertyRow,
|
|
2233
|
-
{
|
|
2234
|
-
label,
|
|
2235
|
-
readout: /* @__PURE__ */ jsx(
|
|
2236
|
-
EditableReadout,
|
|
2237
|
-
{
|
|
2238
|
-
value,
|
|
2239
|
-
min,
|
|
2240
|
-
max,
|
|
2241
|
-
format: effectiveFormat,
|
|
2242
|
-
unit,
|
|
2243
|
-
onCommit: onChange
|
|
2244
|
-
}
|
|
2245
|
-
),
|
|
2246
|
-
layout,
|
|
2247
|
-
children: /* @__PURE__ */ jsx(
|
|
2248
|
-
"input",
|
|
2249
|
-
{
|
|
2250
|
-
type: "range",
|
|
2251
|
-
tabIndex: -1,
|
|
2252
|
-
min,
|
|
2253
|
-
max,
|
|
2254
|
-
step,
|
|
2255
|
-
value,
|
|
2256
|
-
onChange: (e) => {
|
|
2257
|
-
const v = Number(e.target.value);
|
|
2258
|
-
p("property-panel", "slider", { label, value: v });
|
|
2259
|
-
onChange(v);
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
)
|
|
2263
|
-
}
|
|
2264
|
-
);
|
|
2265
|
-
}
|
|
2266
|
-
function EditableReadout({ value, min, max, format, unit, onCommit }) {
|
|
2267
|
-
const [draft, setDraft] = useState(null);
|
|
2268
|
-
const fmt = (n) => format ? format(n) : formatNumber(n);
|
|
2269
|
-
const displayValue = draft !== null ? draft : (() => {
|
|
2270
|
-
const formatted = fmt(value);
|
|
2271
|
-
return typeof formatted === "string" ? formatted : String(formatted);
|
|
2272
|
-
})();
|
|
2273
|
-
const suffix = unit == null ? null : typeof unit === "string" ? /* @__PURE__ */ jsx("span", { className: "lk-property-row__readout-unit", children: unit }) : unit;
|
|
2274
|
-
const commit = () => {
|
|
2275
|
-
if (draft !== null) {
|
|
2276
|
-
const n = parseSignedNumber(draft);
|
|
2277
|
-
if (Number.isFinite(n)) onCommit(Math.min(max, Math.max(min, n)));
|
|
2278
|
-
}
|
|
2279
|
-
setDraft(null);
|
|
2280
|
-
};
|
|
2281
|
-
return /* @__PURE__ */ jsxs("span", { className: "lk-property-row__readout-group", children: [
|
|
2282
|
-
/* @__PURE__ */ jsx(
|
|
2283
|
-
"input",
|
|
2284
|
-
{
|
|
2285
|
-
type: "text",
|
|
2286
|
-
inputMode: "decimal",
|
|
2287
|
-
className: "lk-property-row__readout-input",
|
|
2288
|
-
value: displayValue,
|
|
2289
|
-
onFocus: (e) => {
|
|
2290
|
-
const formatted = fmt(value);
|
|
2291
|
-
setDraft(typeof formatted === "string" ? formatted : String(formatted));
|
|
2292
|
-
e.currentTarget.select();
|
|
2293
|
-
},
|
|
2294
|
-
onChange: (e) => setDraft(e.target.value.replace(/-/g, "\u2212")),
|
|
2295
|
-
onBlur: commit,
|
|
2296
|
-
onClick: (e) => {
|
|
2297
|
-
e.preventDefault();
|
|
2298
|
-
e.stopPropagation();
|
|
2299
|
-
e.currentTarget.focus();
|
|
2300
|
-
},
|
|
2301
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
2302
|
-
onKeyDown: (e) => {
|
|
2303
|
-
if (e.key === "Enter") {
|
|
2304
|
-
commit();
|
|
2305
|
-
e.currentTarget.blur();
|
|
2306
|
-
} else if (e.key === "Escape") {
|
|
2307
|
-
setDraft(null);
|
|
2308
|
-
e.currentTarget.blur();
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
),
|
|
2313
|
-
suffix
|
|
2314
|
-
] });
|
|
2315
|
-
}
|
|
2316
|
-
function ColorRow({
|
|
2317
|
-
label,
|
|
2318
|
-
value,
|
|
2319
|
-
onChange,
|
|
2320
|
-
alpha,
|
|
2321
|
-
onAlphaChange,
|
|
2322
|
-
alphaDisabled
|
|
2323
|
-
}) {
|
|
2324
|
-
const showAlpha = alpha != null;
|
|
2325
|
-
const className = alphaDisabled ? "lk-property-row--alpha-disabled" : void 0;
|
|
2326
|
-
return /* @__PURE__ */ jsxs(PropertyRow, { label, variant: "color", className, children: [
|
|
2327
|
-
/* @__PURE__ */ jsx("input", { type: "color", value, onChange: (e) => onChange(e.target.value) }),
|
|
2328
|
-
showAlpha && /* @__PURE__ */ jsx(
|
|
2329
|
-
"input",
|
|
2330
|
-
{
|
|
2331
|
-
type: "range",
|
|
2332
|
-
className: "lk-property-row__alpha",
|
|
2333
|
-
min: 0,
|
|
2334
|
-
max: 1,
|
|
2335
|
-
step: 0.01,
|
|
2336
|
-
value: alpha,
|
|
2337
|
-
disabled: alphaDisabled,
|
|
2338
|
-
onChange: (e) => onAlphaChange?.(Number(e.target.value))
|
|
2339
|
-
}
|
|
2340
|
-
)
|
|
2341
|
-
] });
|
|
2342
|
-
}
|
|
2343
|
-
function CheckboxRow({ label, value, onChange }) {
|
|
2344
|
-
return /* @__PURE__ */ jsx(PropertyRow, { label, variant: "checkbox", children: /* @__PURE__ */ jsx("input", { type: "checkbox", checked: value, onChange: (e) => onChange(e.target.checked) }) });
|
|
2345
|
-
}
|
|
2346
|
-
function TextRow({ label, value, onChange, placeholder, maxLength, layout }) {
|
|
2347
|
-
return /* @__PURE__ */ jsx(PropertyRow, { label, layout, children: /* @__PURE__ */ jsx(
|
|
2348
|
-
"input",
|
|
2349
|
-
{
|
|
2350
|
-
type: "text",
|
|
2351
|
-
value,
|
|
2352
|
-
placeholder,
|
|
2353
|
-
maxLength,
|
|
2354
|
-
onChange: (e) => onChange(e.target.value)
|
|
2355
|
-
}
|
|
2356
|
-
) });
|
|
2357
|
-
}
|
|
2358
|
-
function NumberRow({
|
|
2359
|
-
label,
|
|
2360
|
-
value,
|
|
2361
|
-
onChange,
|
|
2362
|
-
min,
|
|
2363
|
-
max,
|
|
2364
|
-
step,
|
|
2365
|
-
placeholder,
|
|
2366
|
-
layout
|
|
2367
|
-
}) {
|
|
2368
|
-
return /* @__PURE__ */ jsx(PropertyRow, { label, layout, children: /* @__PURE__ */ jsx(
|
|
2369
|
-
"input",
|
|
2370
|
-
{
|
|
2371
|
-
type: "number",
|
|
2372
|
-
value,
|
|
2373
|
-
min,
|
|
2374
|
-
max,
|
|
2375
|
-
step,
|
|
2376
|
-
placeholder,
|
|
2377
|
-
onChange: (e) => {
|
|
2378
|
-
const raw = e.target.value;
|
|
2379
|
-
if (raw === "") return;
|
|
2380
|
-
const n = Number(raw);
|
|
2381
|
-
if (Number.isFinite(n)) onChange(n);
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
) });
|
|
2385
|
-
}
|
|
2386
|
-
function SelectRow({
|
|
2387
|
-
label,
|
|
2388
|
-
value,
|
|
2389
|
-
options,
|
|
2390
|
-
onChange,
|
|
2391
|
-
layout
|
|
2392
|
-
}) {
|
|
2393
|
-
return /* @__PURE__ */ jsx(PropertyRow, { label, layout, children: /* @__PURE__ */ jsx(
|
|
2394
|
-
"select",
|
|
2395
|
-
{
|
|
2396
|
-
value,
|
|
2397
|
-
onChange: (e) => {
|
|
2398
|
-
const v = e.target.value;
|
|
2399
|
-
p("property-panel", "select", { label, value: v });
|
|
2400
|
-
onChange(v);
|
|
2401
|
-
},
|
|
2402
|
-
children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
2403
|
-
}
|
|
2404
|
-
) });
|
|
2405
|
-
}
|
|
2406
|
-
function ToggleRow({
|
|
2407
|
-
label,
|
|
2408
|
-
value,
|
|
2409
|
-
options,
|
|
2410
|
-
onChange,
|
|
2411
|
-
layout
|
|
2412
|
-
}) {
|
|
2413
|
-
return /* @__PURE__ */ jsx(PropertyRow, { label, layout, children: /* @__PURE__ */ jsx("div", { className: "lk-property-row__toggle", children: options.map((opt) => {
|
|
2414
|
-
const selected = opt.value === value;
|
|
2415
|
-
return /* @__PURE__ */ jsx(
|
|
2416
|
-
"button",
|
|
2417
|
-
{
|
|
2418
|
-
type: "button",
|
|
2419
|
-
"aria-pressed": selected,
|
|
2420
|
-
className: selected ? "lk-property-row__toggle-button lk-property-row__toggle-button--selected" : "lk-property-row__toggle-button",
|
|
2421
|
-
onClick: () => onChange(opt.value),
|
|
2422
|
-
children: opt.label
|
|
2423
|
-
},
|
|
2424
|
-
opt.value
|
|
2425
|
-
);
|
|
2426
|
-
}) }) });
|
|
2427
|
-
}
|
|
2428
2542
|
|
|
2429
|
-
export {
|
|
2543
|
+
export { CurveField, DEFAULT_VIEW, EffectCard, EffectCardList, Lab, LabContext, LabPalette, LabShell, PITCH_LIMIT, Subpanel, Trial, TrialChrome, Workspace, addTrial, applyRules, as2DView, builtinRules, clampPitch, cloneTrial, closeTrial, defineInstrument, f, interstellarTheme, orbitAfterDrag, orbitAfterWheel, reorderTrials, resetTrial, resolveConfigSchema, titleCase, useConfigSchema, useLabContext, useOrbit, validateConfigSchema, wrapYaw };
|
|
2430
2544
|
//# sourceMappingURL=index.js.map
|
|
2431
2545
|
//# sourceMappingURL=index.js.map
|