@weasel-js/labkit 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -2
- package/dist/_dts/{CanvasStackContext-BIRUVVGH.d.ts → CanvasStackContext-Dde8_1my.d.ts} +1 -1
- package/dist/_dts/{PrefsForm.d-CgxUequc.d.ts → PrefsForm.d-DDHFANkc.d.ts} +1 -1
- package/dist/_dts/{frac-X7mWgd7y.d.ts → frac-B5mB8LsJ.d.ts} +33 -13
- package/dist/_dts/{index-FEa1MudK.d.ts → index-DelFfeV0.d.ts} +99 -31
- package/dist/_dts/types-B9_zrHmb.d.ts +166 -0
- package/dist/_dts/{types-ChVJJHvk.d.ts → types-BP2OCcpg.d.ts} +41 -4
- package/dist/_dts/types-x92Kfeme.d.ts +62 -0
- package/dist/_dts/{useTrialState-Cdm13fvl.d.ts → useTrialState-D6Vb3T-g.d.ts} +11 -7
- package/dist/canvas/index.d.ts +9 -8
- package/dist/canvas/index.js +1 -2
- package/dist/chrome/index.d.ts +16 -9
- package/dist/chrome/index.js +5 -5
- package/dist/{chunk-MSR7EVS6.js → chunk-2RQPGOQF.js} +6 -5
- package/dist/chunk-2RQPGOQF.js.map +1 -0
- package/dist/{chunk-LJSIUFYD.js → chunk-5TROQVQ2.js} +46 -21
- package/dist/chunk-5TROQVQ2.js.map +1 -0
- package/dist/{chunk-53AZ2O2N.js → chunk-ASWKLRKJ.js} +92 -45
- package/dist/chunk-ASWKLRKJ.js.map +1 -0
- package/dist/{chunk-BMQWLKOL.js → chunk-C42T6DJT.js} +94 -83
- package/dist/chunk-C42T6DJT.js.map +1 -0
- package/dist/chunk-E44SU6XS.js +50 -0
- package/dist/chunk-E44SU6XS.js.map +1 -0
- package/dist/{chunk-OFCBEZMU.js → chunk-FQMJUVHJ.js} +3 -3
- package/dist/{chunk-OFCBEZMU.js.map → chunk-FQMJUVHJ.js.map} +1 -1
- package/dist/{chunk-PP7ZWAMV.js → chunk-I6JCVE24.js} +3 -3
- package/dist/chunk-I6JCVE24.js.map +1 -0
- package/dist/chunk-ISCOFAUO.js +170 -0
- package/dist/chunk-ISCOFAUO.js.map +1 -0
- package/dist/chunk-MHAGC6VD.js +7320 -0
- package/dist/chunk-MHAGC6VD.js.map +1 -0
- package/dist/{chunk-7TRFMIWX.js → chunk-QAGAHJKZ.js} +4 -4
- package/dist/{chunk-7TRFMIWX.js.map → chunk-QAGAHJKZ.js.map} +1 -1
- package/dist/{chunk-D7QIPGEB.js → chunk-RS3HRQWU.js} +62 -60
- package/dist/chunk-RS3HRQWU.js.map +1 -0
- package/dist/{chunk-RLOXQZW3.js → chunk-TJ7QY3OC.js} +3 -3
- package/dist/{chunk-RLOXQZW3.js.map → chunk-TJ7QY3OC.js.map} +1 -1
- package/dist/controls/index.d.ts +5 -4
- package/dist/controls/index.js +3 -3
- package/dist/dragdrop/index.d.ts +9 -8
- package/dist/dragdrop/index.js +1 -2
- package/dist/index.d.ts +152 -57
- package/dist/index.js +315 -200
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +27 -11
- package/dist/layers/index.js +2 -3
- package/dist/loupe/index.d.ts +8 -7
- package/dist/loupe/index.js +1 -2
- package/dist/passthrough/weasel-canvas.d.ts +1 -3
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-canvas.js.map +1 -1
- package/dist/passthrough/weasel-ui.d.ts +171 -22
- package/dist/passthrough/weasel-ui.js +1 -2
- package/dist/primitives/index.js +3 -4
- package/dist/state/index.d.ts +6 -3
- package/dist/state/index.js +3 -2
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +21 -4
- package/dist/surface/index.js +1 -2
- package/dist/ui/layers/index.d.ts +22 -9
- package/dist/ui/layers/index.js +1 -2
- package/dist/undo/index.d.ts +6 -6
- package/package.json +7 -7
- package/src/annotations/AnnotationTargets.tsx +6 -1
- package/src/annotations/Annotations.overlay.test.tsx +2 -0
- package/src/annotations/ExportMenu.test.tsx +16 -0
- package/src/annotations/ExportMenu.tsx +0 -5
- package/src/annotations/types.ts +4 -1
- package/src/canvas/useOrbit.test.ts +7 -3
- package/src/canvas/usePanZoom.test.ts +7 -3
- package/src/chrome/ChromeRegions.stories.tsx +42 -1
- package/src/chrome/builtins.test.ts +4 -0
- package/src/chrome/builtins.tsx +18 -0
- package/src/chrome/index.ts +1 -1
- package/src/chrome/regions/SidebarRegion.tsx +7 -6
- package/src/chrome/regions/TitleBarRegion.test.tsx +64 -0
- package/src/chrome/regions/TitleBarRegion.tsx +16 -7
- package/src/chrome/regions/ToolbarRegion.test.tsx +14 -0
- package/src/chrome/regions/ToolbarRegion.tsx +2 -2
- package/src/chrome/regions/ViewportRegion.tsx +2 -2
- package/src/chrome/regions/regions.test.tsx +45 -1
- package/src/chrome/types.ts +21 -3
- package/src/config/builder.test.ts +56 -2
- package/src/config/builder.ts +73 -13
- package/src/config/index.ts +16 -1
- package/src/config/path.test.ts +109 -0
- package/src/config/path.ts +76 -0
- package/src/config/resolve.test.ts +93 -2
- package/src/config/resolve.ts +72 -29
- package/src/config/types.ts +99 -9
- package/src/config/visible.ts +6 -4
- package/src/controls/ControlPanel.stories.tsx +38 -4
- package/src/controls/ControlPanel.test.tsx +152 -0
- package/src/controls/ControlPanel.tsx +134 -40
- package/src/dragdrop/DragDropRuntime.tsx +74 -61
- package/src/dragdrop/Palette.tsx +4 -3
- package/src/dragdrop/dragDrop.test.tsx +35 -12
- package/src/index.ts +18 -2
- package/src/instrument/serializers.ts +36 -0
- package/src/instrument/types.ts +8 -7
- package/src/lab/Lab.test.tsx +36 -1
- package/src/lab/Lab.tsx +12 -3
- package/src/lab/LabContext.ts +5 -1
- package/src/lab/LabShell.less +17 -2
- package/src/lab/LabShell.test.tsx +8 -0
- package/src/lab/LabShell.tsx +3 -1
- package/src/layers/AGENTS.md +29 -7
- package/src/layers/LayerList.less +16 -0
- package/src/layers/LayerList.stories.tsx +66 -0
- package/src/layers/LayerList.test.tsx +117 -5
- package/src/layers/LayerList.tsx +192 -53
- package/src/layers/index.ts +1 -1
- package/src/passthrough/weasel-ui.ts +5 -0
- package/src/primitives/FloatingPanel.test.tsx +8 -3
- package/src/primitives/ZoomControl.less +6 -2
- package/src/primitives/ZoomControl.tsx +1 -0
- package/src/state/helpers.ts +1 -1
- package/src/state/store.test.ts +233 -1
- package/src/state/store.ts +57 -24
- package/src/state/types.ts +44 -3
- package/src/state/useTrialState.ts +1 -1
- package/src/trial/Trial.config.test.tsx +44 -0
- package/src/trial/Trial.less +4 -0
- package/src/trial/Trial.targets.test.tsx +96 -0
- package/src/trial/Trial.test.tsx +171 -14
- package/src/trial/Trial.tsx +7 -4
- package/src/trial/TrialChrome.tsx +32 -7
- package/src/trial/TrialTitleBar.tsx +7 -3
- package/src/trial/index.ts +1 -0
- package/src/trial/trialOps.test.ts +49 -1
- package/src/trial/trialOps.ts +24 -6
- package/dist/_dts/types-lg4TSCb2.d.ts +0 -164
- package/dist/_dts/weasel-canvas-FJTFi3ZZ.d.ts +0 -5041
- package/dist/chunk-53AZ2O2N.js.map +0 -1
- package/dist/chunk-BMQWLKOL.js.map +0 -1
- package/dist/chunk-D7QIPGEB.js.map +0 -1
- package/dist/chunk-DG2IFMLA.js +0 -29970
- package/dist/chunk-DG2IFMLA.js.map +0 -1
- package/dist/chunk-J6SBFZYT.js +0 -7121
- package/dist/chunk-J6SBFZYT.js.map +0 -1
- package/dist/chunk-LJSIUFYD.js.map +0 -1
- package/dist/chunk-MIT3ISW4.js +0 -100
- package/dist/chunk-MIT3ISW4.js.map +0 -1
- package/dist/chunk-MSR7EVS6.js.map +0 -1
- package/dist/chunk-PP7ZWAMV.js.map +0 -1
package/README.md
CHANGED
|
@@ -13,10 +13,16 @@ algorithm and would rather not build the page around it.
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npm i @weasel-js/labkit
|
|
16
|
+
npm i @weasel-js/labkit @weasel-js/core
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
`@weasel-js/core` is a peer dependency, pinned to the matching version. labkit
|
|
20
|
+
does not ship its own copy: core keeps its content handlers, paint kinds, shape
|
|
21
|
+
painters and markers in module-global registries, and two copies means anything
|
|
22
|
+
registered through one is invisible to the other — a blank canvas with no error.
|
|
23
|
+
Installing it alongside is what guarantees there is exactly one.
|
|
24
|
+
|
|
25
|
+
React 19 is a peer dependency too (`react` and `react-dom`, `^19.0.0`). One
|
|
20
26
|
stylesheet import covers everything labkit draws, including the theme tokens and
|
|
21
27
|
the `@weasel-js/ui` components it passes through:
|
|
22
28
|
|
|
@@ -70,6 +76,18 @@ renders the settings panel from it. `render` returns the instrument's own DOM;
|
|
|
70
76
|
returning `null` alongside `canvas` means the canvas layers are the whole
|
|
71
77
|
picture. `storage` keeps open trials and their state across reloads.
|
|
72
78
|
|
|
79
|
+
A value is addressed by its path: `bins` above sits at `config.bins` and is
|
|
80
|
+
written as `setConfig('bins', 40)`. `f.group` nests one —
|
|
81
|
+
`paper: f.group({ width: f.number(210) })` puts the value at
|
|
82
|
+
`config.paper.width` and addresses it as `'paper.width'`, in `setConfig`, in a
|
|
83
|
+
control override, and anywhere else a path is taken. `.section('Advanced')` is
|
|
84
|
+
the other thing and only looks like it: it puts a heading over sibling rows and
|
|
85
|
+
leaves their paths alone.
|
|
86
|
+
|
|
87
|
+
A stored config missing something its schema has since gained — a whole nested
|
|
88
|
+
group included — is filled from the instrument's defaults as it loads, so
|
|
89
|
+
growing a schema never strands a saved trial or a snapshot.
|
|
90
|
+
|
|
73
91
|
## Capabilities
|
|
74
92
|
|
|
75
93
|
A capability is a field on the instrument. Declaring it is what makes the trial
|
|
@@ -120,6 +138,14 @@ changed draws dashed and reports `isStale`. `meaning.statuses` is the optional
|
|
|
120
138
|
vocabulary a mark can be labelled with — a status carries its own color, which
|
|
121
139
|
the mark on the canvas follows.
|
|
122
140
|
|
|
141
|
+
`targets` is declared once per instrument and called once per trial, with the
|
|
142
|
+
asking trial as its third argument: `targets(state, config, trial)`. `trial.id`
|
|
143
|
+
is the id `useTileId` scopes a surface tile under, so a consumer holding its own
|
|
144
|
+
per-trial refs keys them by it and hands back the ones this trial owns; a
|
|
145
|
+
module-level ref like the one above is one ref shared by every trial of the
|
|
146
|
+
instrument, which only shows once a second trial is open. `trial.view` is that
|
|
147
|
+
trial's camera.
|
|
148
|
+
|
|
123
149
|
Marks live in the trial's record and survive a reload. An instrument that would
|
|
124
150
|
rather keep them in a format it already owns declares `annotations.storage` with
|
|
125
151
|
a `load`/`save` pair, and labkit never writes its own slot.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { RefObject } from 'react';
|
|
3
|
-
import { V as ViewTransform, W as WorldFrame } from './frac-
|
|
3
|
+
import { V as ViewTransform, W as WorldFrame } from './frac-B5mB8LsJ.js';
|
|
4
4
|
|
|
5
5
|
/** One layer of a canvas stack: its id, whether it is currently shown, and how
|
|
6
6
|
* it paints itself. */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ComponentType, ReactNode, RefObject } from 'react';
|
|
2
|
-
import { R as ResolvedConfig,
|
|
3
|
-
import {
|
|
2
|
+
import { R as ResolvedConfig, a as ConfigSchema, C as ConfigPath } from './types-B9_zrHmb.js';
|
|
3
|
+
import { C as ConfigField } from './types-x92Kfeme.js';
|
|
4
|
+
import { c as SavedSnapshot, i as TrialInfo } from './types-BP2OCcpg.js';
|
|
4
5
|
import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
|
|
5
|
-
import {
|
|
6
|
+
import { Scene } from '@weasel-js/core';
|
|
6
7
|
|
|
7
8
|
/** How an instrument's world sits on the canvas, independent of the camera.
|
|
8
9
|
* The defaults reproduce the convention labkit shipped before this existed:
|
|
@@ -53,7 +54,10 @@ interface ToolbarItem {
|
|
|
53
54
|
pressed?: boolean;
|
|
54
55
|
/** Render the label beside the glyph rather than only in the tooltip. */
|
|
55
56
|
showLabel?: boolean;
|
|
56
|
-
|
|
57
|
+
/** Handed the trial's chrome context, so a contribution declared from the
|
|
58
|
+
* lab can reach `ctx.saveSnapshot()` and the rest without the `render`
|
|
59
|
+
* escape. A zero-argument handler stays valid. */
|
|
60
|
+
onActivate: (ctx: TrialChromeContext) => void;
|
|
57
61
|
}
|
|
58
62
|
/** A selectable tool in the palette region. */
|
|
59
63
|
interface ToolItem {
|
|
@@ -65,7 +69,7 @@ interface ToolItem {
|
|
|
65
69
|
/** A titled block in the sidebar. */
|
|
66
70
|
interface SidebarSection {
|
|
67
71
|
title: string;
|
|
68
|
-
/** Starts collapsed
|
|
72
|
+
/** Starts collapsed, until the trial remembers a fold of its own. */
|
|
69
73
|
defaultCollapsed?: boolean;
|
|
70
74
|
/** Offer the tear-out control. On by default; a section that only makes
|
|
71
75
|
* sense beside its trial sets this false. */
|
|
@@ -79,7 +83,8 @@ interface ViewportControl {
|
|
|
79
83
|
icon: IconComponent;
|
|
80
84
|
label: string;
|
|
81
85
|
disabled?: boolean;
|
|
82
|
-
onActivate
|
|
86
|
+
/** Handed the trial's chrome context, as `ToolbarItem.onActivate` is. */
|
|
87
|
+
onActivate: (ctx: TrialChromeContext) => void;
|
|
83
88
|
}
|
|
84
89
|
/** A readout in the status bar. */
|
|
85
90
|
interface StatusReadout {
|
|
@@ -139,6 +144,12 @@ type TrialContribution = (ContributionBase & {
|
|
|
139
144
|
interface TrialChromeContext {
|
|
140
145
|
trialId: string;
|
|
141
146
|
instrumentName: string;
|
|
147
|
+
/** What the title bar reads, which is the instrument's name until something
|
|
148
|
+
* calls `setTitle`. */
|
|
149
|
+
title: string;
|
|
150
|
+
/** Retitle this trial; `null` restores the instrument name. Persisted with
|
|
151
|
+
* the trial, so a title survives a reload. */
|
|
152
|
+
setTitle: (title: string | null) => void;
|
|
142
153
|
isLastTrial: boolean;
|
|
143
154
|
/** Null when the trial holds a view that is not the 2D one. */
|
|
144
155
|
zoom: number | null;
|
|
@@ -159,6 +170,13 @@ interface TrialChromeContext {
|
|
|
159
170
|
configFields: ConfigField[];
|
|
160
171
|
config: unknown;
|
|
161
172
|
setConfig: (key: string, value: unknown) => void;
|
|
173
|
+
/** Which of this trial's collapsible sections are folded. A sidebar
|
|
174
|
+
* section's key is its contribution id; a section *inside* a contribution —
|
|
175
|
+
* a control panel's property group — is keyed `<contribution id>/<label>`.
|
|
176
|
+
* A section absent here is at its own default. Persisted with the trial. */
|
|
177
|
+
collapsedSections: Readonly<Record<string, boolean>>;
|
|
178
|
+
/** Fold or unfold one section, keyed as `collapsedSections` is. */
|
|
179
|
+
setSectionCollapsed: (key: string, collapsed: boolean) => void;
|
|
162
180
|
/** Section ids this trial currently has torn out of its sidebar. */
|
|
163
181
|
undockedPanels: readonly string[];
|
|
164
182
|
/** Tear a sidebar section out into the workspace. */
|
|
@@ -282,12 +300,12 @@ interface RenderContext<TS = unknown, TC = unknown> {
|
|
|
282
300
|
state: TS;
|
|
283
301
|
config: TC;
|
|
284
302
|
setState: (next: TS | ((prev: TS) => TS)) => void;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
303
|
+
/** Write one config value, by dotted path — `'grid.size'` for a leaf under
|
|
304
|
+
* an `f.group`, `'cellSize'` for one at the root. */
|
|
305
|
+
setConfig: (path: ConfigPath<TC>, value: unknown) => void;
|
|
306
|
+
/** labkit persists `view` and restores it on Reset without ever reading
|
|
307
|
+
* into it. */
|
|
308
|
+
trial: TrialInfo & {
|
|
291
309
|
setView: (next: unknown) => void;
|
|
292
310
|
/** 2D convenience over `view`. Reads 1 and writes nothing when the trial holds
|
|
293
311
|
* a view that is not the 2D one. */
|
|
@@ -641,7 +659,9 @@ interface AnnotationStorage {
|
|
|
641
659
|
/** Declares that an instrument accepts marks: which regions take them,
|
|
642
660
|
* optionally what a mark is allowed to mean, and optionally where they live. */
|
|
643
661
|
interface AnnotationsCapability<TS = unknown, TC = unknown> {
|
|
644
|
-
|
|
662
|
+
/** `trial` is which trial is asking: a declaration made once per instrument
|
|
663
|
+
* is called once per trial, and its targets are that trial's own. */
|
|
664
|
+
targets: (state: TS, config: TC, trial: TrialInfo) => readonly AnnotationTarget[];
|
|
645
665
|
meaning?: AnnotationMeaning;
|
|
646
666
|
storage?: AnnotationStorage;
|
|
647
667
|
/** Fires after every finished export, labkit's own chrome included. A
|
|
@@ -1,20 +1,45 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { R as ResolvedConfig,
|
|
3
|
+
import { R as ResolvedConfig, c as ControlRenderer } from './types-B9_zrHmb.js';
|
|
4
|
+
import { C as ConfigField } from './types-x92Kfeme.js';
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* How much room a container gives its rows — gaps, padding, and field height,
|
|
8
|
+
* moved together. Set on any container in the family; it reaches every
|
|
9
|
+
* descendant, so an inner group can differ from the panel around it.
|
|
10
|
+
*/
|
|
11
|
+
type PropertyDensity = 'tight' | 'normal' | 'roomy';
|
|
12
|
+
/** Where an inline row's label and control sit on the row's cross axis. Set on
|
|
13
|
+
* a container to line a whole column of them up. */
|
|
14
|
+
type PropertyAlign = 'start' | 'center' | 'end' | 'baseline';
|
|
15
|
+
/** Metric props every container in the family takes. Both reach descendants,
|
|
16
|
+
* so the nearest container that states one wins. */
|
|
17
|
+
interface PropertyMetricProps {
|
|
18
|
+
/** Room the rows get. Unset inherits from an outer container, else `normal`. */
|
|
19
|
+
density?: PropertyDensity;
|
|
20
|
+
/**
|
|
21
|
+
* Cross-axis alignment of an inline row's label and control. `baseline` sits
|
|
22
|
+
* the control on the label's first-line baseline, which is what lines a
|
|
23
|
+
* column of swatches up against labels of different heights. Unset keeps each
|
|
24
|
+
* variant's own alignment — a color row centers its label and swatch and
|
|
25
|
+
* sinks the pair to the row's bottom edge, which keeps a paired alpha track
|
|
26
|
+
* level with its neighbour.
|
|
27
|
+
*/
|
|
28
|
+
align?: PropertyAlign;
|
|
29
|
+
}
|
|
5
30
|
/** Props for `<PropertyPanel>`. */
|
|
6
|
-
interface PropertyPanelProps {
|
|
31
|
+
interface PropertyPanelProps extends PropertyMetricProps {
|
|
7
32
|
title?: ReactNode;
|
|
8
33
|
children: ReactNode;
|
|
9
34
|
className?: string;
|
|
10
35
|
}
|
|
11
36
|
/** A titled panel holding property rows — the sidebar container the rest of
|
|
12
37
|
* this module's components fill. */
|
|
13
|
-
declare function PropertyPanel({ title, children, className }: PropertyPanelProps): react_jsx_runtime.JSX.Element;
|
|
38
|
+
declare function PropertyPanel({ title, children, className, density, align, }: PropertyPanelProps): react_jsx_runtime.JSX.Element;
|
|
14
39
|
/** How a property list packs its rows into two columns. */
|
|
15
|
-
type PropertyListPack = 'auto-color' | 'pairs';
|
|
40
|
+
type PropertyListPack = 'auto-color' | 'pairs' | 'one-up';
|
|
16
41
|
/** Props for `<PropertyList>`. */
|
|
17
|
-
interface PropertyListProps {
|
|
42
|
+
interface PropertyListProps extends PropertyMetricProps {
|
|
18
43
|
children: ReactNode;
|
|
19
44
|
className?: string;
|
|
20
45
|
/**
|
|
@@ -24,6 +49,8 @@ interface PropertyListProps {
|
|
|
24
49
|
* - `'pairs'`: every row auto-places into the 2-column grid two-per-row.
|
|
25
50
|
* Headers and subpanels still span the full width; wrap any other
|
|
26
51
|
* full-width child in `<PropertySpan>`. Right for dense effect bodies.
|
|
52
|
+
* - `'one-up'`: every row spans the full width, color rows included. Right
|
|
53
|
+
* for a palette — a column of swatches read as a group.
|
|
27
54
|
*/
|
|
28
55
|
pack?: PropertyListPack;
|
|
29
56
|
}
|
|
@@ -31,7 +58,7 @@ interface PropertyListProps {
|
|
|
31
58
|
* Grid container for PropertyRows. Use standalone for chrome-less layouts, or
|
|
32
59
|
* nest inside <PropertyPanel/> for the standard glass card.
|
|
33
60
|
*/
|
|
34
|
-
declare function PropertyList({ children, className, pack }: PropertyListProps): react_jsx_runtime.JSX.Element;
|
|
61
|
+
declare function PropertyList({ children, className, pack, density, align, }: PropertyListProps): react_jsx_runtime.JSX.Element;
|
|
35
62
|
/** Props for `<PropertySpan>`. */
|
|
36
63
|
interface PropertySpanProps {
|
|
37
64
|
children: ReactNode;
|
|
@@ -49,7 +76,7 @@ type PropertyRowVariant = 'default' | 'color' | 'checkbox';
|
|
|
49
76
|
/** Whether a row's label sits above its control or beside it. */
|
|
50
77
|
type PropertyRowLayout = 'block' | 'inline';
|
|
51
78
|
/** Props for `<PropertyRow>`. */
|
|
52
|
-
interface PropertyRowProps {
|
|
79
|
+
interface PropertyRowProps extends PropertyMetricProps {
|
|
53
80
|
label: ReactNode;
|
|
54
81
|
/** Right-aligned readout shown next to the label (e.g. current value). */
|
|
55
82
|
readout?: ReactNode;
|
|
@@ -60,9 +87,9 @@ interface PropertyRowProps {
|
|
|
60
87
|
description?: string;
|
|
61
88
|
variant?: PropertyRowVariant;
|
|
62
89
|
/**
|
|
63
|
-
* Label position relative to the control.
|
|
64
|
-
* label above control
|
|
65
|
-
*
|
|
90
|
+
* Label position relative to the control. Unset takes the variant's own
|
|
91
|
+
* orientation: `block` — label above control — for the default variant, and
|
|
92
|
+
* `inline` for the color and checkbox variants, which read as a row.
|
|
66
93
|
*/
|
|
67
94
|
layout?: PropertyRowLayout;
|
|
68
95
|
/**
|
|
@@ -77,15 +104,26 @@ interface PropertyRowProps {
|
|
|
77
104
|
}
|
|
78
105
|
/** The label-plus-control frame every typed row below is built from. Use it
|
|
79
106
|
* directly for a control this module does not cover. */
|
|
80
|
-
declare function PropertyRow({ label, readout, description, variant, layout, span, children, htmlFor, className, }: PropertyRowProps): react_jsx_runtime.JSX.Element;
|
|
107
|
+
declare function PropertyRow({ label, readout, description, variant, layout, span, children, htmlFor, className, density, align, }: PropertyRowProps): react_jsx_runtime.JSX.Element;
|
|
81
108
|
/** Props for `<SliderRow>`. */
|
|
82
|
-
interface SliderRowProps {
|
|
109
|
+
interface SliderRowProps extends PropertyMetricProps {
|
|
83
110
|
label: ReactNode;
|
|
84
111
|
value: number;
|
|
85
112
|
min: number;
|
|
86
113
|
max: number;
|
|
87
114
|
step?: number;
|
|
115
|
+
/**
|
|
116
|
+
* The committed value. Given `onInput` as well, it fires once a drag ends
|
|
117
|
+
* or a typed readout is accepted; on its own it fires on every move, which
|
|
118
|
+
* is what a row with one callback has always done.
|
|
119
|
+
*/
|
|
88
120
|
onChange: (next: number) => void;
|
|
121
|
+
/**
|
|
122
|
+
* The live value, fired continuously through a drag. Pass it alongside
|
|
123
|
+
* `onChange` when the write is expensive — cheap state here, the costly
|
|
124
|
+
* work there. Mirrors `<Slider>`'s `onInput` / `onChange` pair.
|
|
125
|
+
*/
|
|
126
|
+
onInput?: (next: number) => void;
|
|
89
127
|
/** Override how the value is rendered next to the label. Defaults to `value.toString()`. */
|
|
90
128
|
format?: (value: number) => ReactNode;
|
|
91
129
|
/**
|
|
@@ -101,9 +139,9 @@ interface SliderRowProps {
|
|
|
101
139
|
}
|
|
102
140
|
/** A bounded number edited by dragging, with a live readout whose precision
|
|
103
141
|
* follows `step`. */
|
|
104
|
-
declare function SliderRow({ label, value, min, max, step, onChange, format, unit, layout, description, span, }: SliderRowProps): react_jsx_runtime.JSX.Element;
|
|
142
|
+
declare function SliderRow({ label, value, min, max, step, onChange, onInput, format, unit, layout, description, span, density, align, }: SliderRowProps): react_jsx_runtime.JSX.Element;
|
|
105
143
|
/** Props for `<ColorRow>`. */
|
|
106
|
-
interface ColorRowProps {
|
|
144
|
+
interface ColorRowProps extends PropertyMetricProps {
|
|
107
145
|
label: ReactNode;
|
|
108
146
|
value: string;
|
|
109
147
|
onChange: (next: string) => void;
|
|
@@ -115,25 +153,29 @@ interface ColorRowProps {
|
|
|
115
153
|
* Use when the color's consumer drops alpha so the affordance reads dead.
|
|
116
154
|
*/
|
|
117
155
|
alphaDisabled?: boolean;
|
|
156
|
+
/** Label beside the swatch (default) or above it. */
|
|
157
|
+
layout?: PropertyRowLayout;
|
|
118
158
|
description?: string;
|
|
119
159
|
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
120
160
|
span?: boolean;
|
|
121
161
|
}
|
|
122
162
|
/** A color swatch, optionally with an alpha slider beneath it. */
|
|
123
|
-
declare function ColorRow({ label, value, onChange, alpha, onAlphaChange, alphaDisabled, description, span, }: ColorRowProps): react_jsx_runtime.JSX.Element;
|
|
163
|
+
declare function ColorRow({ label, value, onChange, alpha, onAlphaChange, alphaDisabled, layout, description, span, density, align, }: ColorRowProps): react_jsx_runtime.JSX.Element;
|
|
124
164
|
/** Props for `<CheckboxRow>`. */
|
|
125
|
-
interface CheckboxRowProps {
|
|
165
|
+
interface CheckboxRowProps extends PropertyMetricProps {
|
|
126
166
|
label: ReactNode;
|
|
127
167
|
value: boolean;
|
|
128
168
|
onChange: (next: boolean) => void;
|
|
169
|
+
/** Label beside the box (default) or above it. */
|
|
170
|
+
layout?: PropertyRowLayout;
|
|
129
171
|
description?: string;
|
|
130
172
|
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
131
173
|
span?: boolean;
|
|
132
174
|
}
|
|
133
175
|
/** A boolean checkbox. */
|
|
134
|
-
declare function CheckboxRow({ label, value, onChange, span, description }: CheckboxRowProps): react_jsx_runtime.JSX.Element;
|
|
176
|
+
declare function CheckboxRow({ label, value, onChange, layout, span, description, density, align, }: CheckboxRowProps): react_jsx_runtime.JSX.Element;
|
|
135
177
|
/** Props for `<TextRow>`. */
|
|
136
|
-
interface TextRowProps {
|
|
178
|
+
interface TextRowProps extends PropertyMetricProps {
|
|
137
179
|
label: ReactNode;
|
|
138
180
|
value: string;
|
|
139
181
|
onChange: (next: string) => void;
|
|
@@ -145,9 +187,9 @@ interface TextRowProps {
|
|
|
145
187
|
span?: boolean;
|
|
146
188
|
}
|
|
147
189
|
/** A single-line text input. */
|
|
148
|
-
declare function TextRow({ label, value, onChange, placeholder, maxLength, layout, description, span, }: TextRowProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
declare function TextRow({ label, value, onChange, placeholder, maxLength, layout, description, span, density, align, }: TextRowProps): react_jsx_runtime.JSX.Element;
|
|
149
191
|
/** Props for `<NumberRow>`. */
|
|
150
|
-
interface NumberRowProps {
|
|
192
|
+
interface NumberRowProps extends PropertyMetricProps {
|
|
151
193
|
label: ReactNode;
|
|
152
194
|
value: number;
|
|
153
195
|
onChange: (next: number) => void;
|
|
@@ -168,13 +210,13 @@ interface NumberRowProps {
|
|
|
168
210
|
}
|
|
169
211
|
/** A number typed directly. Reach for `<SliderRow>` when the range matters
|
|
170
212
|
* more than the exact value. */
|
|
171
|
-
declare function NumberRow({ label, value, onChange, min, max, step, placeholder, unit, layout, description, span, }: NumberRowProps): react_jsx_runtime.JSX.Element;
|
|
213
|
+
declare function NumberRow({ label, value, onChange, min, max, step, placeholder, unit, layout, description, span, density, align, }: NumberRowProps): react_jsx_runtime.JSX.Element;
|
|
172
214
|
interface PropertyOption<T extends string> {
|
|
173
215
|
value: T;
|
|
174
216
|
label: ReactNode;
|
|
175
217
|
}
|
|
176
218
|
/** Props for `<SelectRow>`. */
|
|
177
|
-
interface SelectRowProps<T extends string> {
|
|
219
|
+
interface SelectRowProps<T extends string> extends PropertyMetricProps {
|
|
178
220
|
label: ReactNode;
|
|
179
221
|
value: T;
|
|
180
222
|
options: ReadonlyArray<PropertyOption<T>>;
|
|
@@ -186,9 +228,9 @@ interface SelectRowProps<T extends string> {
|
|
|
186
228
|
}
|
|
187
229
|
/** A dropdown over a fixed set of choices. Prefer `<ToggleRow>` when there
|
|
188
230
|
* are only two or three and they should all stay visible. */
|
|
189
|
-
declare function SelectRow<T extends string>({ label, value, options, onChange, layout, description, span, }: SelectRowProps<T>): react_jsx_runtime.JSX.Element;
|
|
231
|
+
declare function SelectRow<T extends string>({ label, value, options, onChange, layout, description, span, density, align, }: SelectRowProps<T>): react_jsx_runtime.JSX.Element;
|
|
190
232
|
/** Props for `<ToggleRow>`. */
|
|
191
|
-
interface ToggleRowProps<T extends string> {
|
|
233
|
+
interface ToggleRowProps<T extends string> extends PropertyMetricProps {
|
|
192
234
|
label: ReactNode;
|
|
193
235
|
value: T;
|
|
194
236
|
options: ReadonlyArray<PropertyOption<T>>;
|
|
@@ -200,7 +242,7 @@ interface ToggleRowProps<T extends string> {
|
|
|
200
242
|
}
|
|
201
243
|
/** A segmented control: the same choice as a select, with every option
|
|
202
244
|
* visible at once. */
|
|
203
|
-
declare function ToggleRow<T extends string>({ label, value, options, onChange, layout, description, span, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
|
|
245
|
+
declare function ToggleRow<T extends string>({ label, value, options, onChange, layout, description, span, density, align, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
|
|
204
246
|
|
|
205
247
|
/** How a panel packs its rows into the two-column property grid.
|
|
206
248
|
* - `'auto'`: narrow controls (numbers, checkboxes, dropdowns, colours) sit
|
|
@@ -217,7 +259,9 @@ interface ControlPanelProps<TC extends Record<string, unknown>> {
|
|
|
217
259
|
/** @deprecated Pass `schema`. A field list is adapted into one internally. */
|
|
218
260
|
fields?: ConfigField[];
|
|
219
261
|
config: TC;
|
|
220
|
-
|
|
262
|
+
/** Writes one value. The path is dotted for a leaf inside an `f.group`, and
|
|
263
|
+
* the bare key for one at the root. */
|
|
264
|
+
setConfig: (path: string, value: unknown) => void;
|
|
221
265
|
/**
|
|
222
266
|
* Control overrides and app-defined kinds, PrefsForm-style. Keys are config
|
|
223
267
|
* paths (checked first) or leaf kinds. A renderer returning `null` collapses
|
|
@@ -226,9 +270,33 @@ interface ControlPanelProps<TC extends Record<string, unknown>> {
|
|
|
226
270
|
renderers?: Record<string, ControlRenderer>;
|
|
227
271
|
/** How rows pack into the two-column grid. Defaults to `'pairs'`. */
|
|
228
272
|
pack?: ControlPack;
|
|
229
|
-
/**
|
|
230
|
-
*
|
|
273
|
+
/**
|
|
274
|
+
* Where a row's label sits relative to its control. Unset takes each row's
|
|
275
|
+
* own orientation — `block` (label above, which is what leaves a paired row
|
|
276
|
+
* room for its value) for most, `inline` for colors and checkboxes.
|
|
277
|
+
*/
|
|
231
278
|
layout?: PropertyRowLayout;
|
|
279
|
+
/** Room the rows get. Passed straight to the property list. */
|
|
280
|
+
density?: PropertyDensity;
|
|
281
|
+
/** Cross-axis alignment of an inline row's label and control. */
|
|
282
|
+
align?: PropertyAlign;
|
|
283
|
+
/**
|
|
284
|
+
* Fold each section away behind a twisty, starting `'open'` or `'closed'`.
|
|
285
|
+
* Unset draws the heading alone, as the panel always has — unless a section
|
|
286
|
+
* in the schema declares how it opens, which makes that section foldable on
|
|
287
|
+
* its own and outranks this for that one section.
|
|
288
|
+
*/
|
|
289
|
+
collapse?: 'open' | 'closed';
|
|
290
|
+
/**
|
|
291
|
+
* Which sections are folded, keyed as `onCollapse` reports them. Given, the
|
|
292
|
+
* panel keeps no state of its own: every toggle arrives at `onCollapse`
|
|
293
|
+
* instead, which is where a lab that remembers a trial's sections writes
|
|
294
|
+
* them.
|
|
295
|
+
*/
|
|
296
|
+
collapsed?: Readonly<Record<string, boolean>>;
|
|
297
|
+
/** A fold moved. The key is a section's label — prefixed by its group's
|
|
298
|
+
* dotted path when the section sits inside one — or a group's own path. */
|
|
299
|
+
onCollapse?: (key: string, collapsed: boolean) => void;
|
|
232
300
|
/** Draw leaves marked `hidden`. */
|
|
233
301
|
showHidden?: boolean;
|
|
234
302
|
className?: string;
|
|
@@ -236,7 +304,7 @@ interface ControlPanelProps<TC extends Record<string, unknown>> {
|
|
|
236
304
|
/** Render an instrument's config schema as a stack of controls, each writing
|
|
237
305
|
* back through `setConfig`. Built on the property rows, so a lab's controls
|
|
238
306
|
* are the same aligned, themed rows the rest of the kit uses. */
|
|
239
|
-
declare function ControlPanel<TC extends Record<string, unknown>>({ schema, fields, config, setConfig, renderers, pack, layout, showHidden, className, }: ControlPanelProps<TC>): react_jsx_runtime.JSX.Element;
|
|
307
|
+
declare function ControlPanel<TC extends Record<string, unknown>>({ schema, fields, config, setConfig, renderers, pack, layout, density, align, collapse, collapsed, onCollapse, showHidden, className, }: ControlPanelProps<TC>): react_jsx_runtime.JSX.Element;
|
|
240
308
|
|
|
241
|
-
export { CheckboxRow as C, NumberRow as N,
|
|
242
|
-
export type {
|
|
309
|
+
export { CheckboxRow as C, NumberRow as N, SelectRow as S, TextRow as T, ColorRow as c, ControlPanel as e, PropertyList as g, PropertyPanel as j, PropertyRow as l, PropertySpan as p, SliderRow as s, ToggleRow as v };
|
|
310
|
+
export type { PropertyMetricProps as P, PropertyListPack as a, CheckboxRowProps as b, ColorRowProps as d, NumberRowProps as f, PropertyListProps as h, PropertyOption as i, PropertyPanelProps as k, PropertyRowLayout as m, PropertyRowProps as n, PropertyRowVariant as o, PropertySpanProps as q, SelectRowProps as r, SliderRowProps as t, TextRowProps as u, ToggleRowProps as w };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { ToolPrefLeaf, ToolPrefGroup } from '@weasel-js/core';
|
|
2
|
+
import { P as PrefRenderer } from './PrefsForm.d-DDHFANkc.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders the control cell for one config leaf. Identical to weasel-ui's
|
|
6
|
+
* `PrefRenderer` on purpose — aliased rather than redeclared so the two
|
|
7
|
+
* cannot drift.
|
|
8
|
+
*/
|
|
9
|
+
type ControlRenderer = PrefRenderer;
|
|
10
|
+
/** One labeled choice in an `enum` leaf. */
|
|
11
|
+
interface ConfigOption {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
/** Everything a leaf can carry beyond its kind and default. A flat union of
|
|
16
|
+
* every `Pref*` leaf's extras, plus labkit's own `debounceMs`. */
|
|
17
|
+
interface Annotations {
|
|
18
|
+
name?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
hidden?: boolean;
|
|
21
|
+
block?: boolean;
|
|
22
|
+
pair?: string;
|
|
23
|
+
min?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
step?: number;
|
|
26
|
+
suffix?: string;
|
|
27
|
+
control?: string;
|
|
28
|
+
options?: readonly ConfigOption[];
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
maxLength?: number;
|
|
31
|
+
/** Milliseconds to debounce a string leaf's live writes. Default 150. */
|
|
32
|
+
debounceMs?: number;
|
|
33
|
+
}
|
|
34
|
+
/** What a rule contributes. `kind` is honored only while it is still unset,
|
|
35
|
+
* which is what makes `f.value` claimable and a kinded factory final. */
|
|
36
|
+
type LeafPatch = Annotations & {
|
|
37
|
+
kind?: string;
|
|
38
|
+
};
|
|
39
|
+
/** What a rule is given for the leaf it is deciding about. */
|
|
40
|
+
interface ConfigRuleContext {
|
|
41
|
+
/** The leaf's own key — its last path segment, and what a label is titled
|
|
42
|
+
* from. */
|
|
43
|
+
key: string;
|
|
44
|
+
/** Dotted path within the schema, which is the path the value is written
|
|
45
|
+
* at. Equal to `key` for a leaf sitting at the root. */
|
|
46
|
+
path: string;
|
|
47
|
+
/** The leaf's default value. A rule may read it but never change it. */
|
|
48
|
+
default: unknown;
|
|
49
|
+
/** What earlier rules and the author's own annotations have settled. */
|
|
50
|
+
leaf: Readonly<LeafPatch>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Decides part of how a leaf is presented. Returns a patch, or null to
|
|
54
|
+
* abstain. Merging is gap-filling: a property already settled is never
|
|
55
|
+
* overwritten, so the author's annotations beat every rule and a consumer's
|
|
56
|
+
* rules beat labkit's built-ins.
|
|
57
|
+
*/
|
|
58
|
+
type ConfigRule = (ctx: ConfigRuleContext) => LeafPatch | null;
|
|
59
|
+
/** A presentational bucket of nodes, rendered under one heading. Buckets
|
|
60
|
+
* siblings: a section never nests a value, which is what separates it from
|
|
61
|
+
* `f.group`. */
|
|
62
|
+
interface SectionSpec {
|
|
63
|
+
/** Dotted path of the group whose children this buckets. `''` is the root,
|
|
64
|
+
* which is where every section of a flat schema sits. */
|
|
65
|
+
at: string;
|
|
66
|
+
label: string;
|
|
67
|
+
/** Full dotted paths, so a section under a group names its children the way
|
|
68
|
+
* everything else does. */
|
|
69
|
+
paths: readonly string[];
|
|
70
|
+
/** Whether the section opens folded. Set, the panel folds this section
|
|
71
|
+
* whether or not it was given a panel-wide `collapse`; a fold the reader
|
|
72
|
+
* has since toggled outranks it. */
|
|
73
|
+
collapsed?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/** A schema resolved against a set of rules: the vocabulary weasel-ui renders,
|
|
76
|
+
* plus the three things labkit keeps on the side because `PrefLeaf` has no
|
|
77
|
+
* field for them. */
|
|
78
|
+
interface ResolvedConfig {
|
|
79
|
+
/** The schema as a `PrefGroup` tree: an `f.group` is a nested group, so a
|
|
80
|
+
* leaf's dotted path within it is the path its value is written at. */
|
|
81
|
+
group: ToolPrefGroup;
|
|
82
|
+
sections: readonly SectionSpec[];
|
|
83
|
+
showIf: ReadonlyMap<string, (config: Record<string, unknown>) => boolean>;
|
|
84
|
+
/** Node-level `.render` overrides, keyed by path. */
|
|
85
|
+
renderers: Readonly<Record<string, ControlRenderer>>;
|
|
86
|
+
}
|
|
87
|
+
/** How a node names the section it belongs to: the heading, and how that
|
|
88
|
+
* section opens. Every node in one section repeats the heading; only one of
|
|
89
|
+
* them has to say `collapsed`. */
|
|
90
|
+
interface SectionOption {
|
|
91
|
+
label: string;
|
|
92
|
+
collapsed?: boolean;
|
|
93
|
+
}
|
|
94
|
+
/** Per-node extras that do not belong on a `PrefLeaf`. */
|
|
95
|
+
interface NodeOptions extends BranchOptions {
|
|
96
|
+
render?: ControlRenderer;
|
|
97
|
+
validate?: (leaf: ToolPrefLeaf) => string[];
|
|
98
|
+
}
|
|
99
|
+
/** What a branch can say about itself, beyond its children. */
|
|
100
|
+
interface BranchOptions {
|
|
101
|
+
section?: SectionOption;
|
|
102
|
+
/** Show this node only while the predicate holds. On a group it hides the
|
|
103
|
+
* whole subtree; the values stay in config either way. */
|
|
104
|
+
showIf?: (config: Record<string, unknown>) => boolean;
|
|
105
|
+
}
|
|
106
|
+
/** A group's own annotations: what it is called and, optionally, why. */
|
|
107
|
+
interface BranchAnnotations {
|
|
108
|
+
name?: string;
|
|
109
|
+
description?: string;
|
|
110
|
+
}
|
|
111
|
+
/** The builder's leaf: a kind (or null, to be decided by rules), a default,
|
|
112
|
+
* an annotation bag, and the extras above. */
|
|
113
|
+
interface ConfigNode<T = unknown> {
|
|
114
|
+
readonly kind: string | null;
|
|
115
|
+
readonly default: T;
|
|
116
|
+
readonly annotations: Readonly<Annotations>;
|
|
117
|
+
readonly options: Readonly<NodeOptions>;
|
|
118
|
+
}
|
|
119
|
+
/** The builder's branch: named children, nested as deeply as the schema
|
|
120
|
+
* wants. A branch nests the value too — `grid: f.group({ size })` puts the
|
|
121
|
+
* value at `grid.size`, where `.section('Grid')` would have left it at
|
|
122
|
+
* `size`. */
|
|
123
|
+
interface ConfigBranch<S extends ConfigShape = ConfigShape> {
|
|
124
|
+
readonly children: S;
|
|
125
|
+
readonly annotations: Readonly<BranchAnnotations>;
|
|
126
|
+
readonly options: Readonly<BranchOptions>;
|
|
127
|
+
}
|
|
128
|
+
/** Either half of a schema tree. */
|
|
129
|
+
type ConfigEntry = ConfigNode | ConfigBranch;
|
|
130
|
+
/** The children of a schema or a group. */
|
|
131
|
+
type ConfigShape = {
|
|
132
|
+
readonly [key: string]: ConfigEntry;
|
|
133
|
+
};
|
|
134
|
+
/** The value type a node produces. */
|
|
135
|
+
type NodeValue<N> = N extends ConfigNode<infer T> ? T : never;
|
|
136
|
+
/** The value type a schema entry produces — a leaf's own, or a branch's
|
|
137
|
+
* nested record. */
|
|
138
|
+
type EntryValue<E> = E extends ConfigBranch<infer S> ? InferConfig<S> : E extends ConfigNode<infer T> ? T : never;
|
|
139
|
+
/** The config type a builder shape produces. */
|
|
140
|
+
type InferConfig<S> = {
|
|
141
|
+
[K in keyof S]: EntryValue<S[K]>;
|
|
142
|
+
};
|
|
143
|
+
/** Whether a config value has children a path can descend into. Arrays and
|
|
144
|
+
* functions are values, not branches. */
|
|
145
|
+
type Branching<V> = V extends readonly unknown[] ? false : V extends (...args: never[]) => unknown ? false : V extends object ? true : false;
|
|
146
|
+
/**
|
|
147
|
+
* Every dotted path a config offers, a group's own path included. A config
|
|
148
|
+
* whose shape is not known — `unknown`, or a bare record — gives `string`,
|
|
149
|
+
* which is what keeps a generic instrument writable.
|
|
150
|
+
*/
|
|
151
|
+
type ConfigPath<T> = unknown extends T ? string : T extends object ? {
|
|
152
|
+
[K in keyof T & string]: Branching<T[K]> extends true ? K | `${K}.${ConfigPath<T[K]>}` : K;
|
|
153
|
+
}[keyof T & string] : never;
|
|
154
|
+
/** The type at a dotted path, or `unknown` where the path is not one the
|
|
155
|
+
* config's type spells out. */
|
|
156
|
+
type ValueAtPath<T, P extends string> = unknown extends T ? unknown : P extends `${infer Head}.${infer Rest}` ? Head extends keyof T ? ValueAtPath<T[Head], Rest> : unknown : P extends keyof T ? T[P] : unknown;
|
|
157
|
+
/** An instrument's config, declared once. */
|
|
158
|
+
interface ConfigSchema<TC> {
|
|
159
|
+
readonly nodes: ConfigShape;
|
|
160
|
+
/** The starting config — what `defaultConfig()` would have returned. */
|
|
161
|
+
defaults(): TC;
|
|
162
|
+
}
|
|
163
|
+
/** The config type behind a schema: `ConfigOf<typeof sceneConfig>`. */
|
|
164
|
+
type ConfigOf<S> = S extends ConfigSchema<infer TC> ? TC : never;
|
|
165
|
+
|
|
166
|
+
export type { Annotations as A, BranchAnnotations as B, ConfigPath as C, EntryValue as E, InferConfig as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionSpec as S, ValueAtPath as V, ConfigSchema as a, ConfigNode as b, ControlRenderer as c, ConfigOption as d, ConfigShape as e, ConfigBranch as f, BranchOptions as g, ConfigEntry as h, ConfigRule as i, ConfigRuleContext as j, ConfigOf as k, NodeValue as l };
|