@weasel-js/labkit 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -7
- package/dist/_dts/{CanvasStackContext-CJNMS6mz.d.ts → CanvasStackContext-DQgcH1ny.d.ts} +1 -1
- package/dist/_dts/LayerList.d-2c9yup5Y.d.ts +83 -0
- package/dist/_dts/{PrefsForm.d-DPy9I8It.d.ts → PrefsRow.d-DFpPIeIC.d.ts} +16 -1
- package/dist/_dts/{Select.d-Gwr8fLpK.d.ts → Select.d-BchGMC2Y.d.ts} +129 -12
- package/dist/_dts/{frac-B4okC2lX.d.ts → frac-DTRq7Xpb.d.ts} +31 -13
- package/dist/_dts/inDialog-D5ZKGjiD.d.ts +23 -0
- package/dist/_dts/{index-CSJoj4LC.d.ts → index-B0--8PEM.d.ts} +5 -4
- package/dist/_dts/index-DINmCuLA.d.ts +453 -0
- package/dist/_dts/number.d-OeQYQoUO.d.ts +28 -0
- package/dist/_dts/{types-DzoNY1pO.d.ts → types-B7y8hX1D.d.ts} +6 -6
- package/dist/_dts/{usePanZoom-Pzf9f2jh.d.ts → usePanZoom-Bq73pwFe.d.ts} +4 -47
- package/dist/_dts/{useTrialState-uLlmaccn.d.ts → useTrialState-CP4jfuKl.d.ts} +1 -1
- package/dist/canvas/index.d.ts +6 -5
- package/dist/canvas/index.js +1 -1
- package/dist/chrome/index.d.ts +5 -4
- package/dist/chrome/index.js +7 -7
- package/dist/chunk-2RVCB2PT.js +3 -0
- package/dist/chunk-2RVCB2PT.js.map +1 -0
- package/dist/{chunk-UN4U4P63.js → chunk-3FI7Y4R4.js} +9 -9
- package/dist/chunk-3FI7Y4R4.js.map +1 -0
- package/dist/{chunk-7PFH3CNM.js → chunk-AWH3SRPW.js} +31 -5
- package/dist/chunk-AWH3SRPW.js.map +1 -0
- package/dist/{chunk-ZDHY47EV.js → chunk-BVL7Y574.js} +25 -9
- package/dist/chunk-BVL7Y574.js.map +1 -0
- package/dist/{chunk-IHUVQ3TX.js → chunk-H5VIV7MQ.js} +31 -27
- package/dist/chunk-H5VIV7MQ.js.map +1 -0
- package/dist/chunk-HFRE4QQV.js +400 -0
- package/dist/chunk-HFRE4QQV.js.map +1 -0
- package/dist/{chunk-IXTQI7JX.js → chunk-MOLIUNVH.js} +4 -4
- package/dist/{chunk-IXTQI7JX.js.map → chunk-MOLIUNVH.js.map} +1 -1
- package/dist/{chunk-CEIUBPPW.js → chunk-TT5C5S6R.js} +3 -86
- package/dist/chunk-TT5C5S6R.js.map +1 -0
- package/dist/chunk-USO6TERP.js +90 -0
- package/dist/chunk-USO6TERP.js.map +1 -0
- package/dist/{chunk-PHK4W562.js → chunk-VQ47KWD5.js} +3 -3
- package/dist/{chunk-PHK4W562.js.map → chunk-VQ47KWD5.js.map} +1 -1
- package/dist/chunk-WUJXJC5O.js +14568 -0
- package/dist/chunk-WUJXJC5O.js.map +1 -0
- package/dist/{chunk-EHGVROVV.js → chunk-ZORVNOKX.js} +6 -25
- package/dist/chunk-ZORVNOKX.js.map +1 -0
- package/dist/config/index.d.ts +53 -11
- package/dist/config/index.js +5 -5
- package/dist/controls/index.d.ts +6 -3
- package/dist/controls/index.js +4 -4
- package/dist/dragdrop/index.d.ts +4 -3
- package/dist/index.d.ts +139 -114
- package/dist/index.js +470 -479
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +6 -35
- package/dist/layers/index.js +2 -3
- package/dist/loupe/index.d.ts +6 -5
- package/dist/passthrough/weasel-ui.d.ts +421 -215
- package/dist/passthrough/weasel-ui.js +1 -1
- package/dist/primitives/index.d.ts +2 -13
- package/dist/primitives/index.js +4 -4
- package/dist/state/index.d.ts +6 -5
- package/dist/state/index.js +5 -5
- package/dist/styles.css +410 -190
- package/dist/undo/index.d.ts +4 -3
- package/package.json +9 -13
- package/src/annotations/frac.test.ts +7 -0
- package/src/annotations/paint.test.ts +21 -0
- package/src/annotations/paint.ts +12 -0
- package/src/annotations/store.test.ts +31 -0
- package/src/annotations/types.ts +3 -2
- package/src/canvas/Stage.test.tsx +18 -0
- package/src/canvas/index.ts +0 -9
- package/src/chrome/labTypes.ts +1 -1
- package/src/chrome/regions/PaletteRegion.test.tsx +18 -0
- package/src/chrome/regions/PaletteRegion.tsx +5 -4
- package/src/chrome/regions/SidebarRegion.less +197 -0
- package/src/chrome/regions/SidebarRegion.test.tsx +20 -0
- package/src/chrome/regions/SidebarRegion.tsx +7 -2
- package/src/chrome/regions/TitleBarRegion.tsx +1 -1
- package/src/chrome/types.ts +13 -4
- package/src/config/autoConfig.ts +2 -2
- package/src/config/builder.ts +23 -5
- package/src/config/entry.test.ts +8 -0
- package/src/config/index.ts +2 -0
- package/src/config/rules.ts +2 -0
- package/src/config/sectionTree.test.ts +146 -0
- package/src/config/sectionTree.ts +124 -0
- package/src/config/types.ts +4 -4
- package/src/controls/AutoControls.stories.tsx +6 -6
- package/src/controls/ControlPanel.less +27 -0
- package/src/controls/ControlPanel.stories.tsx +95 -1
- package/src/controls/ControlPanel.test.tsx +82 -112
- package/src/controls/ControlPanel.tsx +159 -396
- package/src/controls/inDialog.test.tsx +82 -0
- package/src/controls/inDialog.tsx +46 -0
- package/src/controls/index.ts +1 -0
- package/src/index.test.ts +0 -2
- package/src/index.ts +14 -35
- package/src/instrument/types.ts +5 -2
- package/src/lab/Lab.nebula.test.tsx +31 -0
- package/src/lab/Lab.tsx +57 -18
- package/src/lab/LabBoundary.test.tsx +66 -0
- package/src/lab/LabBoundary.tsx +56 -0
- package/src/lab/LabHeader.tsx +4 -23
- package/src/lab/LabRoot.tsx +2 -3
- package/src/lab/LabShell.less +0 -1
- package/src/lab/LabShell.stories.tsx +15 -0
- package/src/lab/LabShell.test.tsx +18 -0
- package/src/lab/LabShell.tsx +14 -1
- package/src/lab/LabSwitcher.less +1 -1
- package/src/lab/LabSwitcher.test.tsx +41 -0
- package/src/lab/LabSwitcher.tsx +30 -8
- package/src/lab/index.ts +1 -0
- package/src/layers/AGENTS.md +18 -74
- package/src/layers/index.ts +4 -2
- package/src/passthrough/weasel-ui.ts +25 -10
- package/src/primitives/FpsMeter.less +6 -1
- package/src/primitives/FpsMeter.test.tsx +12 -4
- package/src/primitives/FpsMeter.tsx +6 -1
- package/src/primitives/JobProgress.stories.tsx +1 -1
- package/src/primitives/StatusBar.less +6 -0
- package/src/primitives/index.ts +0 -2
- package/src/specimen/Specimen.tsx +42 -56
- package/src/state/types.ts +8 -2
- package/src/state/view.test.ts +4 -4
- package/src/styles.less +4 -2
- package/src/theme/Interstellar.stories.tsx +8 -9
- package/src/theme/base.less +3 -20
- package/src/theme/interstellar.test.ts +6 -3
- package/src/theme/interstellar.theme.json +0 -56
- package/src/theme/interstellar.ts +2 -2
- package/src/trial/Trial.canvas.test.tsx +9 -5
- package/src/trial/Trial.config.test.tsx +1 -1
- package/src/trial/Trial.tsx +30 -8
- package/dist/_dts/index-BNdz-tNd.d.ts +0 -423
- package/dist/chunk-7PFH3CNM.js.map +0 -1
- package/dist/chunk-CEIUBPPW.js.map +0 -1
- package/dist/chunk-EH7UEQV7.js +0 -590
- package/dist/chunk-EH7UEQV7.js.map +0 -1
- package/dist/chunk-EHGVROVV.js.map +0 -1
- package/dist/chunk-IHUVQ3TX.js.map +0 -1
- package/dist/chunk-MDVTK5U3.js +0 -170
- package/dist/chunk-MDVTK5U3.js.map +0 -1
- package/dist/chunk-PJ7WEQBJ.js +0 -18
- package/dist/chunk-PJ7WEQBJ.js.map +0 -1
- package/dist/chunk-T3EC2DXD.js +0 -12628
- package/dist/chunk-T3EC2DXD.js.map +0 -1
- package/dist/chunk-UN4U4P63.js.map +0 -1
- package/dist/chunk-ZDHY47EV.js.map +0 -1
- package/dist/ui/layers/index.d.ts +0 -66
- package/dist/ui/layers/index.js +0 -3
- package/dist/ui/layers/index.js.map +0 -1
- package/src/canvas/useOrbit.test.ts +0 -167
- package/src/canvas/useOrbit.ts +0 -156
- package/src/lab/useSystemMode.ts +0 -30
- package/src/layers/LayerList.less +0 -93
- package/src/layers/LayerList.stories.tsx +0 -66
- package/src/layers/LayerList.test.tsx +0 -232
- package/src/layers/LayerList.tsx +0 -268
- package/src/primitives/Sidebar.less +0 -112
- package/src/primitives/Sidebar.stories.tsx +0 -38
- package/src/primitives/Sidebar.test.tsx +0 -74
- package/src/primitives/Sidebar.tsx +0 -35
- package/src/ui/layers/index.ts +0 -5
|
@@ -218,7 +218,7 @@ describe('a config written as auto', () => {
|
|
|
218
218
|
const { container } = render(<Lab instruments={[instrument]} defaultInstrument="Ghosted" />);
|
|
219
219
|
expect(screen.getByRole('button', { name: /Gap/ })).toHaveAttribute('aria-pressed', 'false');
|
|
220
220
|
expect(screen.getByRole('button', { name: /Width/ })).toHaveAttribute('aria-pressed', 'true');
|
|
221
|
-
// 432 / 24.
|
|
221
|
+
// 432 / 24. An auto row holds what the resolver decided rather than
|
|
222
222
|
// the pinned 12 underneath it, and un-pinning writes back what it drew.
|
|
223
223
|
expect((container.querySelector('input[type=range]') as HTMLInputElement).value).toBe('18');
|
|
224
224
|
});
|
package/src/trial/Trial.tsx
CHANGED
|
@@ -24,9 +24,9 @@ import type {
|
|
|
24
24
|
} from '../instrument/types';
|
|
25
25
|
import { useJob } from '../job/useJob';
|
|
26
26
|
import { useLabContext } from '../lab/LabContext';
|
|
27
|
-
import { LayerList } from '../layers/LayerList';
|
|
28
27
|
import { TrialLoupe } from '../loupe/TrialLoupe';
|
|
29
28
|
import { resolveLoupe } from '../loupe/types';
|
|
29
|
+
import { LayerList, type LayerListItem, moveLayers } from '../passthrough/weasel-ui';
|
|
30
30
|
import { LabStoreContext, TrialIdProvider } from '../state/context';
|
|
31
31
|
import type { LabStore } from '../state/store';
|
|
32
32
|
import type { TrialRecord } from '../state/types';
|
|
@@ -401,6 +401,24 @@ function TrialRuntime({
|
|
|
401
401
|
return instrument.layers.ids.map((l) => (typeof l === 'string' ? { id: l, label: l } : l));
|
|
402
402
|
}, [instrument.layers]);
|
|
403
403
|
|
|
404
|
+
// In the order the canvas draws them, with the always-on layers pinned
|
|
405
|
+
// below the rest, where a drag cannot reach past them.
|
|
406
|
+
const layerItems: LayerListItem[] = useMemo(() => {
|
|
407
|
+
const ordered = layerOrder
|
|
408
|
+
? [...layerDescriptors].sort((a, b) => layerOrder.indexOf(a.id) - layerOrder.indexOf(b.id))
|
|
409
|
+
: layerDescriptors;
|
|
410
|
+
const pinnedLast = [
|
|
411
|
+
...ordered.filter((l) => !l.alwaysOn),
|
|
412
|
+
...ordered.filter((l) => l.alwaysOn),
|
|
413
|
+
];
|
|
414
|
+
return pinnedLast.map((l) => ({
|
|
415
|
+
id: l.id,
|
|
416
|
+
label: l.label,
|
|
417
|
+
locked: l.alwaysOn === true,
|
|
418
|
+
visible: l.alwaysOn === true || layerVisibility[l.id] !== false,
|
|
419
|
+
}));
|
|
420
|
+
}, [layerDescriptors, layerOrder, layerVisibility]);
|
|
421
|
+
|
|
404
422
|
const dragDropResult = useDragDrop({
|
|
405
423
|
capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i, s) => s },
|
|
406
424
|
canvasContainerRef,
|
|
@@ -492,7 +510,12 @@ function TrialRuntime({
|
|
|
492
510
|
minZoom={stage.minZoom}
|
|
493
511
|
maxZoom={stage.maxZoom}
|
|
494
512
|
hostRef={loupeHostRef}
|
|
495
|
-
overlay={
|
|
513
|
+
overlay={
|
|
514
|
+
<>
|
|
515
|
+
{stage.overlay?.(renderCtx)}
|
|
516
|
+
{lens}
|
|
517
|
+
</>
|
|
518
|
+
}
|
|
496
519
|
>
|
|
497
520
|
{instrument.render(renderCtx)}
|
|
498
521
|
</Stage>
|
|
@@ -519,19 +542,18 @@ function TrialRuntime({
|
|
|
519
542
|
() =>
|
|
520
543
|
instrument.layers && layerDescriptors.length > 0 ? (
|
|
521
544
|
<LayerList
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
setLayerOrder(next.map((l) => l.id));
|
|
545
|
+
items={layerItems}
|
|
546
|
+
onReorder={(move) => {
|
|
547
|
+
setLayerOrder(moveLayers(layerItems, move).map((l) => l.id));
|
|
526
548
|
bus.emit('layers.reorder');
|
|
527
549
|
}}
|
|
528
|
-
|
|
550
|
+
onVisibilityChange={(lid, visible) => {
|
|
529
551
|
setLayerVisibility((prev) => ({ ...prev, [lid]: visible }));
|
|
530
552
|
bus.emit('layers.toggle');
|
|
531
553
|
}}
|
|
532
554
|
/>
|
|
533
555
|
) : null,
|
|
534
|
-
[instrument.layers, layerDescriptors,
|
|
556
|
+
[instrument.layers, layerDescriptors, layerItems, bus],
|
|
535
557
|
);
|
|
536
558
|
|
|
537
559
|
const extraChrome = useMemo<TrialContribution[]>(() => {
|
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { ToolPrefNumberFormat } from '@weasel-js/core';
|
|
4
|
-
import { R as ResolvedConfig, a as ConfigField, d as ControlRenderer } from './types-DzoNY1pO.js';
|
|
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
|
-
}
|
|
30
|
-
/** Props for `<PropertyPanel>`. */
|
|
31
|
-
interface PropertyPanelProps extends PropertyMetricProps {
|
|
32
|
-
title?: ReactNode;
|
|
33
|
-
children: ReactNode;
|
|
34
|
-
className?: string;
|
|
35
|
-
}
|
|
36
|
-
/** A titled panel holding property rows — the sidebar container the rest of
|
|
37
|
-
* this module's components fill. */
|
|
38
|
-
declare function PropertyPanel({ title, children, className, density, align, }: PropertyPanelProps): react_jsx_runtime.JSX.Element;
|
|
39
|
-
/** How a property list packs its rows into two columns. */
|
|
40
|
-
type PropertyListPack = 'auto-color' | 'pairs' | 'one-up';
|
|
41
|
-
/** Props for `<PropertyList>`. */
|
|
42
|
-
interface PropertyListProps extends PropertyMetricProps {
|
|
43
|
-
children: ReactNode;
|
|
44
|
-
className?: string;
|
|
45
|
-
/**
|
|
46
|
-
* How rows pack into the 2-column grid.
|
|
47
|
-
* - `'auto-color'` (default): only color rows pair side-by-side; everything
|
|
48
|
-
* else spans the full width. Right for sparse top-level panels.
|
|
49
|
-
* - `'pairs'`: every row auto-places into the 2-column grid two-per-row.
|
|
50
|
-
* Headers and subpanels still span the full width; wrap any other
|
|
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.
|
|
54
|
-
*/
|
|
55
|
-
pack?: PropertyListPack;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Grid container for PropertyRows. Use standalone for chrome-less layouts, or
|
|
59
|
-
* nest inside <PropertyPanel/> for the standard glass card.
|
|
60
|
-
*/
|
|
61
|
-
declare function PropertyList({ children, className, pack, density, align, }: PropertyListProps): react_jsx_runtime.JSX.Element;
|
|
62
|
-
/** Props for `<PropertySpan>`. */
|
|
63
|
-
interface PropertySpanProps {
|
|
64
|
-
children: ReactNode;
|
|
65
|
-
className?: string;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Wrapper that makes an arbitrary child span both columns of a
|
|
69
|
-
* `<PropertyList pack="pairs">`, a `<PropertyGroup pack="pairs">` body, or a
|
|
70
|
-
* `<Subpanel>`. `<PropertyRow span>` covers the row case; this covers
|
|
71
|
-
* everything else a consumer puts in the grid.
|
|
72
|
-
*/
|
|
73
|
-
declare function PropertySpan({ children, className }: PropertySpanProps): react_jsx_runtime.JSX.Element;
|
|
74
|
-
/** Which control shape a row holds, which decides its intrinsic layout. */
|
|
75
|
-
type PropertyRowVariant = 'default' | 'color' | 'checkbox';
|
|
76
|
-
/** Whether a row's label sits above its control or beside it. */
|
|
77
|
-
type PropertyRowLayout = 'block' | 'inline';
|
|
78
|
-
/** Props for `<PropertyRow>`. */
|
|
79
|
-
interface PropertyRowProps extends PropertyMetricProps {
|
|
80
|
-
label: ReactNode;
|
|
81
|
-
/** Right-aligned readout shown next to the label (e.g. current value). */
|
|
82
|
-
readout?: ReactNode;
|
|
83
|
-
/**
|
|
84
|
-
* Help text for the row, shown in a tooltip off an ⓘ affordance beside the
|
|
85
|
-
* label. Empty or absent renders no affordance.
|
|
86
|
-
*/
|
|
87
|
-
description?: string;
|
|
88
|
-
variant?: PropertyRowVariant;
|
|
89
|
-
/**
|
|
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.
|
|
93
|
-
*/
|
|
94
|
-
layout?: PropertyRowLayout;
|
|
95
|
-
/**
|
|
96
|
-
* Take the full width of the enclosing grid. Only has an effect inside a
|
|
97
|
-
* `<PropertyList pack="pairs">` or a `<Subpanel>`; elsewhere rows are already
|
|
98
|
-
* full width.
|
|
99
|
-
*/
|
|
100
|
-
span?: boolean;
|
|
101
|
-
children: ReactNode;
|
|
102
|
-
htmlFor?: string;
|
|
103
|
-
/** The control is a group of several (a segmented toggle), not one element.
|
|
104
|
-
* The row then renders as a `<div>`: a `<label>` hands a click on its text to
|
|
105
|
-
* its first control, which in a group means selecting the first option. Each
|
|
106
|
-
* member has to carry its own name. */
|
|
107
|
-
group?: boolean;
|
|
108
|
-
className?: string;
|
|
109
|
-
/** The row is auto: its value is not pinned and the owner computes it. Draws
|
|
110
|
-
* the control ghosted and colorless. */
|
|
111
|
-
auto?: boolean;
|
|
112
|
-
/** Given, the row carries a pin dot that toggles `auto`. Omitted, the row can
|
|
113
|
-
* show an auto state but not change it. */
|
|
114
|
-
onAutoChange?: (next: boolean) => void;
|
|
115
|
-
/** Marks the row's element for a panel-level gesture handler. `PropertyRow`
|
|
116
|
-
* does not spread unknown props, so this is declared rather than inherited. */
|
|
117
|
-
'data-auto-path'?: string;
|
|
118
|
-
}
|
|
119
|
-
/** The label-plus-control frame every typed row below is built from. Use it
|
|
120
|
-
* directly for a control this module does not cover. */
|
|
121
|
-
declare function PropertyRow({ label, readout, description, variant, layout, span, children, htmlFor, group, className, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: PropertyRowProps): react_jsx_runtime.JSX.Element;
|
|
122
|
-
/** Props for `<SliderRow>`. */
|
|
123
|
-
interface SliderRowProps extends PropertyMetricProps {
|
|
124
|
-
label: ReactNode;
|
|
125
|
-
value: number;
|
|
126
|
-
min: number;
|
|
127
|
-
max: number;
|
|
128
|
-
step?: number;
|
|
129
|
-
/**
|
|
130
|
-
* The committed value. Given `onInput` as well, it fires once a drag ends
|
|
131
|
-
* or a typed readout is accepted; on its own it fires on every move, which
|
|
132
|
-
* is what a row with one callback has always done.
|
|
133
|
-
*/
|
|
134
|
-
onChange: (next: number) => void;
|
|
135
|
-
/**
|
|
136
|
-
* The live value, fired continuously through a drag. Pass it alongside
|
|
137
|
-
* `onChange` when the write is expensive — cheap state here, the costly
|
|
138
|
-
* work there. Mirrors `<Slider>`'s `onInput` / `onChange` pair.
|
|
139
|
-
*/
|
|
140
|
-
onInput?: (next: number) => void;
|
|
141
|
-
/** Override how the value is rendered next to the label. Defaults to `value.toString()`. */
|
|
142
|
-
format?: (value: number) => ReactNode;
|
|
143
|
-
/**
|
|
144
|
-
* Static text in place of the editable readout. Use it when the row is not
|
|
145
|
-
* showing a number a caller could type back — an auto row's `auto · 18`, say,
|
|
146
|
-
* which an editable field would seed a draft with and then fail to parse.
|
|
147
|
-
*/
|
|
148
|
-
readout?: ReactNode;
|
|
149
|
-
/** A named display for the value: `compact` reads `2.0M`. `format` wins when both are given. */
|
|
150
|
-
notation?: ToolPrefNumberFormat;
|
|
151
|
-
/**
|
|
152
|
-
* Optional suffix rendered next to the readout. A string becomes a
|
|
153
|
-
* baseline-aligned dim "word" unit (e.g. "px"); pass JSX like `<sup>°</sup>`
|
|
154
|
-
* to get the browser's native super positioning for symbol units.
|
|
155
|
-
*/
|
|
156
|
-
unit?: ReactNode;
|
|
157
|
-
layout?: PropertyRowLayout;
|
|
158
|
-
description?: string;
|
|
159
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
160
|
-
span?: boolean;
|
|
161
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
162
|
-
auto?: boolean;
|
|
163
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
164
|
-
onAutoChange?: (next: boolean) => void;
|
|
165
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
166
|
-
'data-auto-path'?: string;
|
|
167
|
-
}
|
|
168
|
-
/** A bounded number edited by dragging, with a live readout whose precision
|
|
169
|
-
* follows `step`. */
|
|
170
|
-
declare function SliderRow({ label, value, min, max, step, onChange, onInput, format, readout, notation, unit, layout, description, span, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: SliderRowProps): react_jsx_runtime.JSX.Element;
|
|
171
|
-
/** Props for `<ColorRow>`. */
|
|
172
|
-
interface ColorRowProps extends PropertyMetricProps {
|
|
173
|
-
label: ReactNode;
|
|
174
|
-
value: string;
|
|
175
|
-
/**
|
|
176
|
-
* The committed color. Given `onInput` as well, it fires once the picker
|
|
177
|
-
* closes; on its own it fires on every move inside it, which is what a row
|
|
178
|
-
* with one callback has always done.
|
|
179
|
-
*/
|
|
180
|
-
onChange: (next: string) => void;
|
|
181
|
-
/**
|
|
182
|
-
* The live color, fired continuously while the picker is open. Pass it
|
|
183
|
-
* alongside `onChange` when the write is expensive — cheap state here, the
|
|
184
|
-
* costly work there. Mirrors `<Slider>`'s `onInput` / `onChange` pair.
|
|
185
|
-
*/
|
|
186
|
-
onInput?: (next: string) => void;
|
|
187
|
-
/** 0..1 alpha. When provided, a translucent slider renders beneath the swatch. */
|
|
188
|
-
alpha?: number;
|
|
189
|
-
/** The committed alpha, on the same terms as `onChange`. */
|
|
190
|
-
onAlphaChange?: (next: number) => void;
|
|
191
|
-
/** The live alpha, on the same terms as `onInput`. */
|
|
192
|
-
onAlphaInput?: (next: number) => void;
|
|
193
|
-
/**
|
|
194
|
-
* Render the alpha track as inert (dimmed, no thumb, not-allowed cursor).
|
|
195
|
-
* Use when the color's consumer drops alpha so the affordance reads dead.
|
|
196
|
-
*/
|
|
197
|
-
alphaDisabled?: boolean;
|
|
198
|
-
/** Label beside the swatch (default) or above it. */
|
|
199
|
-
layout?: PropertyRowLayout;
|
|
200
|
-
description?: string;
|
|
201
|
-
/** Right-aligned readout shown next to the label — see `<PropertyRow readout>`. */
|
|
202
|
-
readout?: ReactNode;
|
|
203
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
204
|
-
span?: boolean;
|
|
205
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
206
|
-
auto?: boolean;
|
|
207
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
208
|
-
onAutoChange?: (next: boolean) => void;
|
|
209
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
210
|
-
'data-auto-path'?: string;
|
|
211
|
-
}
|
|
212
|
-
/** A color swatch, optionally with an alpha slider beneath it. */
|
|
213
|
-
declare function ColorRow({ label, readout, value, onChange, onInput, alpha, onAlphaChange, onAlphaInput, alphaDisabled, layout, description, span, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: ColorRowProps): react_jsx_runtime.JSX.Element;
|
|
214
|
-
/** Props for `<CheckboxRow>`. */
|
|
215
|
-
interface CheckboxRowProps extends PropertyMetricProps {
|
|
216
|
-
label: ReactNode;
|
|
217
|
-
/** Absent reads as unchecked. */
|
|
218
|
-
value: boolean | undefined;
|
|
219
|
-
onChange: (next: boolean) => void;
|
|
220
|
-
/** Label beside the box (default) or above it. */
|
|
221
|
-
layout?: PropertyRowLayout;
|
|
222
|
-
description?: string;
|
|
223
|
-
/** Right-aligned readout shown next to the label — see `<PropertyRow readout>`. */
|
|
224
|
-
readout?: ReactNode;
|
|
225
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
226
|
-
span?: boolean;
|
|
227
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
228
|
-
auto?: boolean;
|
|
229
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
230
|
-
onAutoChange?: (next: boolean) => void;
|
|
231
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
232
|
-
'data-auto-path'?: string;
|
|
233
|
-
}
|
|
234
|
-
/** A boolean checkbox. */
|
|
235
|
-
declare function CheckboxRow({ label, readout, value, onChange, layout, span, description, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: CheckboxRowProps): react_jsx_runtime.JSX.Element;
|
|
236
|
-
/** Props for `<TextRow>`. */
|
|
237
|
-
interface TextRowProps extends PropertyMetricProps {
|
|
238
|
-
label: ReactNode;
|
|
239
|
-
/** Absent reads as an empty field. */
|
|
240
|
-
value: string | null | undefined;
|
|
241
|
-
onChange: (next: string) => void;
|
|
242
|
-
placeholder?: string;
|
|
243
|
-
maxLength?: number;
|
|
244
|
-
layout?: PropertyRowLayout;
|
|
245
|
-
description?: string;
|
|
246
|
-
/** Right-aligned readout shown next to the label — see `<PropertyRow readout>`. */
|
|
247
|
-
readout?: ReactNode;
|
|
248
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
249
|
-
span?: boolean;
|
|
250
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
251
|
-
auto?: boolean;
|
|
252
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
253
|
-
onAutoChange?: (next: boolean) => void;
|
|
254
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
255
|
-
'data-auto-path'?: string;
|
|
256
|
-
}
|
|
257
|
-
/** A single-line text input. */
|
|
258
|
-
declare function TextRow({ label, readout, value, onChange, placeholder, maxLength, layout, description, span, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: TextRowProps): react_jsx_runtime.JSX.Element;
|
|
259
|
-
/** Props for `<NumberRow>`. */
|
|
260
|
-
interface NumberRowProps extends PropertyMetricProps {
|
|
261
|
-
label: ReactNode;
|
|
262
|
-
/** Absent reads as an empty field. */
|
|
263
|
-
value: number | null | undefined;
|
|
264
|
-
/**
|
|
265
|
-
* The committed value. Given `onInput` as well, it fires on blur or Enter;
|
|
266
|
-
* on its own it fires on every keystroke, which is what a row with one
|
|
267
|
-
* callback has always done.
|
|
268
|
-
*/
|
|
269
|
-
onChange: (next: number) => void;
|
|
270
|
-
/**
|
|
271
|
-
* The live value, fired per keystroke. Pass it alongside `onChange` when the
|
|
272
|
-
* write is expensive — cheap state here, the costly work there. Mirrors
|
|
273
|
-
* `<Slider>`'s `onInput` / `onChange` pair.
|
|
274
|
-
*/
|
|
275
|
-
onInput?: (next: number) => void;
|
|
276
|
-
min?: number;
|
|
277
|
-
max?: number;
|
|
278
|
-
step?: number;
|
|
279
|
-
placeholder?: string;
|
|
280
|
-
/**
|
|
281
|
-
* Optional suffix rendered after the field. A string becomes a dim "word"
|
|
282
|
-
* unit (e.g. "px"); pass JSX like `<sup>°</sup>` for symbol units. Display
|
|
283
|
-
* only — it never participates in parsing, and the value stays a number.
|
|
284
|
-
*/
|
|
285
|
-
unit?: ReactNode;
|
|
286
|
-
layout?: PropertyRowLayout;
|
|
287
|
-
description?: string;
|
|
288
|
-
/** Right-aligned readout shown next to the label — see `<PropertyRow readout>`. */
|
|
289
|
-
readout?: ReactNode;
|
|
290
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
291
|
-
span?: boolean;
|
|
292
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
293
|
-
auto?: boolean;
|
|
294
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
295
|
-
onAutoChange?: (next: boolean) => void;
|
|
296
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
297
|
-
'data-auto-path'?: string;
|
|
298
|
-
}
|
|
299
|
-
/** A number typed directly. Reach for `<SliderRow>` when the range matters
|
|
300
|
-
* more than the exact value. */
|
|
301
|
-
declare function NumberRow({ label, readout, value, onChange, onInput, min, max, step, placeholder, unit, layout, description, span, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: NumberRowProps): react_jsx_runtime.JSX.Element;
|
|
302
|
-
interface PropertyOption<T extends string> {
|
|
303
|
-
value: T;
|
|
304
|
-
label: ReactNode;
|
|
305
|
-
}
|
|
306
|
-
/** Props for `<SelectRow>`. */
|
|
307
|
-
interface SelectRowProps<T extends string> extends PropertyMetricProps {
|
|
308
|
-
label: ReactNode;
|
|
309
|
-
/** Absent, or not one of `options`, shows a placeholder rather than the first option. */
|
|
310
|
-
value: T | undefined;
|
|
311
|
-
options: ReadonlyArray<PropertyOption<T>>;
|
|
312
|
-
onChange: (next: T) => void;
|
|
313
|
-
/** Shown while no option is chosen. Defaults to "Choose option…". */
|
|
314
|
-
placeholder?: string;
|
|
315
|
-
layout?: PropertyRowLayout;
|
|
316
|
-
description?: string;
|
|
317
|
-
/** Right-aligned readout shown next to the label — see `<PropertyRow readout>`. */
|
|
318
|
-
readout?: ReactNode;
|
|
319
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
320
|
-
span?: boolean;
|
|
321
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
322
|
-
auto?: boolean;
|
|
323
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
324
|
-
onAutoChange?: (next: boolean) => void;
|
|
325
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
326
|
-
'data-auto-path'?: string;
|
|
327
|
-
}
|
|
328
|
-
/** A dropdown over a fixed set of choices. Prefer `<ToggleRow>` when there
|
|
329
|
-
* are only two or three and they should all stay visible. */
|
|
330
|
-
declare function SelectRow<T extends string>({ label, readout, value, options, onChange, placeholder, layout, description, span, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: SelectRowProps<T>): react_jsx_runtime.JSX.Element;
|
|
331
|
-
/** Props for `<ToggleRow>`. */
|
|
332
|
-
interface ToggleRowProps<T extends string> extends PropertyMetricProps {
|
|
333
|
-
label: ReactNode;
|
|
334
|
-
value: T;
|
|
335
|
-
options: ReadonlyArray<PropertyOption<T>>;
|
|
336
|
-
onChange: (next: T) => void;
|
|
337
|
-
layout?: PropertyRowLayout;
|
|
338
|
-
description?: string;
|
|
339
|
-
/** Right-aligned readout shown next to the label — see `<PropertyRow readout>`. */
|
|
340
|
-
readout?: ReactNode;
|
|
341
|
-
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
342
|
-
span?: boolean;
|
|
343
|
-
/** The row is auto — see `<PropertyRow auto>`. */
|
|
344
|
-
auto?: boolean;
|
|
345
|
-
/** Toggles `auto` from a pin dot — see `<PropertyRow onAutoChange>`. */
|
|
346
|
-
onAutoChange?: (next: boolean) => void;
|
|
347
|
-
/** Marks the row for a panel-level gesture handler — see `<PropertyRow data-auto-path>`. */
|
|
348
|
-
'data-auto-path'?: string;
|
|
349
|
-
}
|
|
350
|
-
/** A segmented control: the same choice as a select, with every option
|
|
351
|
-
* visible at once. */
|
|
352
|
-
declare function ToggleRow<T extends string>({ label, readout, value, options, onChange, layout, description, span, density, align, auto, onAutoChange, 'data-auto-path': autoPath, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
|
|
353
|
-
|
|
354
|
-
/** How a panel packs its rows into the two-column property grid.
|
|
355
|
-
* - `'auto'`: narrow controls (numbers, checkboxes, dropdowns, colors) sit
|
|
356
|
-
* two per row; the ones that need the width — text, sliders, segmented
|
|
357
|
-
* toggles — span it.
|
|
358
|
-
* - `'pairs'`: every row pairs. A control that needs the full width says so
|
|
359
|
-
* itself, with `<PropertyRow span>`.
|
|
360
|
-
* - `'one-up'`: one control per row, colors excepted.
|
|
361
|
-
*/
|
|
362
|
-
type ControlPack = 'auto' | 'pairs' | 'one-up';
|
|
363
|
-
interface ControlPanelProps<TC extends Record<string, unknown>> {
|
|
364
|
-
/** The instrument's resolved config schema. */
|
|
365
|
-
schema?: ResolvedConfig;
|
|
366
|
-
/** @deprecated Pass `schema`. A field list is adapted into one internally. */
|
|
367
|
-
fields?: ConfigField[];
|
|
368
|
-
config: TC;
|
|
369
|
-
/** Writes one value. The path is dotted for a leaf inside an `f.group`, and
|
|
370
|
-
* the bare key for one at the root. */
|
|
371
|
-
setConfig: (path: string, value: unknown) => void;
|
|
372
|
-
/**
|
|
373
|
-
* Control overrides and app-defined kinds, PrefsForm-style. Keys are config
|
|
374
|
-
* paths (checked first) or leaf kinds. A renderer returning `null` collapses
|
|
375
|
-
* the row; entries override the built-in rows on collision.
|
|
376
|
-
*/
|
|
377
|
-
renderers?: Record<string, ControlRenderer>;
|
|
378
|
-
/** How rows pack into the two-column grid. Defaults to `'pairs'`. */
|
|
379
|
-
pack?: ControlPack;
|
|
380
|
-
/**
|
|
381
|
-
* Where a row's label sits relative to its control. Unset takes each row's
|
|
382
|
-
* own orientation — `block` (label above, which is what leaves a paired row
|
|
383
|
-
* room for its value) for most, `inline` for colors and checkboxes.
|
|
384
|
-
*/
|
|
385
|
-
layout?: PropertyRowLayout;
|
|
386
|
-
/** Room the rows get. Passed straight to the property list. */
|
|
387
|
-
density?: PropertyDensity;
|
|
388
|
-
/** Cross-axis alignment of an inline row's label and control. */
|
|
389
|
-
align?: PropertyAlign;
|
|
390
|
-
/**
|
|
391
|
-
* Fold each section away behind a twisty, starting `'open'` or `'closed'`.
|
|
392
|
-
* Unset draws the heading alone, as the panel always has — unless a section
|
|
393
|
-
* in the schema declares how it opens, which makes that section foldable on
|
|
394
|
-
* its own and outranks this for that one section.
|
|
395
|
-
*/
|
|
396
|
-
collapse?: 'open' | 'closed';
|
|
397
|
-
/**
|
|
398
|
-
* Which sections are folded, keyed as `onCollapse` reports them. Given, the
|
|
399
|
-
* panel keeps no state of its own: every toggle arrives at `onCollapse`
|
|
400
|
-
* instead, which is where a lab that remembers a trial's sections writes
|
|
401
|
-
* them.
|
|
402
|
-
*/
|
|
403
|
-
collapsed?: Readonly<Record<string, boolean>>;
|
|
404
|
-
/** A fold moved. The key is a section's label — prefixed by its group's
|
|
405
|
-
* dotted path when the section sits inside one — or a group's own path. */
|
|
406
|
-
onCollapse?: (key: string, collapsed: boolean) => void;
|
|
407
|
-
/** Dotted paths currently unpinned. A row in this set draws ghosted and its
|
|
408
|
-
* dot reads as auto, and the dot writes the sentinel back through
|
|
409
|
-
* `setConfig`. Omitted, the panel keeps the set itself: the dots still work
|
|
410
|
-
* and the sentinel never reaches `setConfig`, which would otherwise store it
|
|
411
|
-
* as the row's value. */
|
|
412
|
-
auto?: ReadonlySet<string>;
|
|
413
|
-
/** Draw leaves marked `hidden`. */
|
|
414
|
-
showHidden?: boolean;
|
|
415
|
-
className?: string;
|
|
416
|
-
}
|
|
417
|
-
/** Render an instrument's config schema as a stack of controls, each writing
|
|
418
|
-
* back through `setConfig`. Built on the property rows, so a lab's controls
|
|
419
|
-
* are the same aligned, themed rows the rest of the kit uses. */
|
|
420
|
-
declare function ControlPanel<TC extends Record<string, unknown>>({ schema, fields, config, setConfig, renderers, pack, layout, density, align, collapse, collapsed, onCollapse, auto: given, showHidden, className, }: ControlPanelProps<TC>): react_jsx_runtime.JSX.Element;
|
|
421
|
-
|
|
422
|
-
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 };
|
|
423
|
-
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 };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/auto.ts","../src/config/path.ts","../src/config/autoConfig.ts"],"names":["n"],"mappings":"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","file":"chunk-7PFH3CNM.js","sourcesContent":["/**\n * Written at a config path to mean \"do not pin this — let the instrument\n * decide\". Accepted by `setConfig`, by a trial's `configSeed`, and by\n * `.initial()` in a schema. It is normalized into the trial's set of unpinned\n * paths on the way into the store and is never itself stored, so a serialized\n * trial contains no sentinel and the last pinned value survives.\n */\nexport const auto: unique symbol = Symbol('weasel.auto');\n\n/** The type of the `auto` sentinel, for widening a value parameter. */\nexport type Auto = typeof auto;\n\nexport function isAuto(value: unknown): value is Auto {\n return value === auto;\n}\n","import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\n\n/**\n * Reading and writing a config tree by dotted path. A schema's leaves address\n * their values this way, so a leaf nested under `f.group` and a flat one are\n * reached by exactly the same call.\n */\n\n/** A plain object a config tree can be walked into. An array, a `Date`, a\n * `Map` or a class instance is a leaf value: a walk that descended into one\n * would rebuild it as a bare `{}`. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/** The value at a dotted path. `undefined` when a segment is missing or the\n * walk hits something that is not a record. */\nexport function valueAtPath(config: unknown, path: string): unknown {\n let cur = config;\n for (const segment of path.split('.')) {\n if (!isRecord(cur)) return undefined;\n cur = cur[segment];\n }\n return cur;\n}\n\n/** Whether a dotted path is actually present, which a `undefined` value is\n * not enough to tell you. */\nexport function hasConfigPath(config: unknown, path: string): boolean {\n let cur = config;\n for (const segment of path.split('.')) {\n if (!isRecord(cur) || !(segment in cur)) return false;\n cur = cur[segment];\n }\n return true;\n}\n\nfunction writeIn(node: unknown, segments: readonly string[], value: unknown): unknown {\n const [head, ...rest] = segments;\n if (head === undefined) return value;\n const base = isRecord(node) ? node : {};\n return { ...base, [head]: rest.length === 0 ? value : writeIn(base[head], rest, value) };\n}\n\n/** A copy of `config` with `value` written at a dotted path. Every record on\n * the way down is copied and the input is left alone; a missing — or\n * non-record — intermediate is replaced with the branch the path implies. */\nexport function withValueAtPath<T>(config: T, path: string, value: unknown): T {\n return writeIn(config, path.split('.'), value) as T;\n}\n\n/**\n * `stored` with every gap filled from `defaults`, down the whole tree.\n *\n * This is how a config written before its schema grew a branch still loads:\n * the branch arrives at its defaults instead of `undefined`, and a key the\n * defaults no longer mention is kept rather than dropped, so nothing is lost\n * to a schema change the author has not finished making.\n */\nexport function fillConfigDefaults<T>(stored: unknown, defaults: T): T {\n if (!isRecord(defaults)) return (stored === undefined ? defaults : stored) as T;\n const base = isRecord(stored) ? stored : {};\n const out: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(defaults)) {\n out[key] = fillConfigDefaults(base[key], value);\n }\n return out as T;\n}\n\n/** A node in a resolved schema tree, by dotted path. Structural rather than\n * `isPrefLeaf` so this stays free of a runtime import. */\nexport function schemaNodeAtPath(group: PrefGroup, path: string): PrefLeaf | PrefGroup | undefined {\n let node: PrefLeaf | PrefGroup | undefined = group;\n for (const segment of path.split('.')) {\n if (node === undefined || !('children' in node)) return undefined;\n node = node.children[segment];\n }\n return node;\n}\n","import { isPrefLeaf, type PrefGroup, type PrefLeaf } from '@weasel-js/ui';\nimport { isAuto } from './auto';\nimport { isRecord, withValueAtPath } from './path';\nimport type { ResolvedConfig } from './types';\n\ntype Resolver = (config: Record<string, unknown>) => unknown;\n\n/** Reads a labkit-only extra off a resolved leaf. `PrefLeaf` has no field for\n * these; they ride as extra keys and survive the resolve walk. */\nfunction extra<T>(leaf: PrefLeaf, key: string): T | undefined {\n return (leaf as unknown as Record<string, T | undefined>)[key];\n}\n\n/** Every leaf in a resolved schema, in schema order, with its dotted path. */\nfunction* leaves(group: PrefGroup, at = ''): Generator<[string, PrefLeaf]> {\n for (const [key, child] of Object.entries(group.children)) {\n const path = at === '' ? key : `${at}.${key}`;\n if (isPrefLeaf(child)) yield [path, child];\n else yield* leaves(child, path);\n }\n}\n\n/** The dotted paths a schema declares as starting auto — every leaf that said\n * `.initial(auto)`. This is what seeds a new trial's unpinned set. */\nexport function autoPathsOf(resolved: ResolvedConfig): string[] {\n const out: string[] = [];\n for (const [path, leaf] of leaves(resolved.group)) {\n if (extra<boolean>(leaf, 'unpinned')) out.push(path);\n }\n return out;\n}\n\n/**\n * The config an instrument reads: the stored one, with every unpinned path\n * either computed by its resolver or removed.\n *\n * Resolution is demand-driven — a resolver reading another unpinned path\n * forces that one first, whatever order the schema declared them in — so a\n * cycle is a real cycle and not an ordering accident.\n */\nexport function resolveAutoConfig<TC>(\n resolved: ResolvedConfig,\n config: TC,\n autoPaths: ReadonlySet<string>,\n): TC {\n if (autoPaths.size === 0) return config;\n\n const resolvers = new Map<string, Resolver | undefined>();\n for (const [path, leaf] of leaves(resolved.group)) {\n if (autoPaths.has(path)) resolvers.set(path, extra<Resolver>(leaf, 'autoResolve'));\n }\n if (resolvers.size === 0) return config;\n\n // Drop first, so a resolver reading a still-unresolved auto path sees\n // `undefined` rather than a stale pinned value it would silently believe.\n let out = config as unknown as Record<string, unknown>;\n for (const path of resolvers.keys()) out = dropAtPath(out, path);\n\n const inFlight = new Set<string>();\n const done = new Set<string>();\n\n const need = (path: string): void => {\n if (done.has(path)) return;\n if (inFlight.has(path)) {\n throw new Error(`[labkit] auto resolver cycle at \"${path}\"`);\n }\n const resolve = resolvers.get(path);\n if (!resolve) {\n done.add(path);\n return;\n }\n inFlight.add(path);\n try {\n // Read `out` only after the resolver has run: it forces its own\n // dependencies, and each of those replaced `out`.\n const value = resolve(demand(() => out, resolvers, need));\n out = withValueAtPath(out, path, value);\n } finally {\n inFlight.delete(path);\n }\n done.add(path);\n };\n\n for (const path of resolvers.keys()) need(path);\n return out as unknown as TC;\n}\n\n/**\n * The config a resolver reads. A top-level key that is itself unpinned is\n * forced before it is handed back, which is what makes declaration order\n * irrelevant and turns a genuine loop into a named error.\n *\n * Only top-level keys are trapped: a resolver reaching `c.grid.size` gets the\n * already-dropped branch, and a nested auto path it depends on resolves by\n * schema order alone.\n */\nfunction demand(\n read: () => Record<string, unknown>,\n resolvers: ReadonlyMap<string, Resolver | undefined>,\n need: (path: string) => void,\n): Record<string, unknown> {\n return new Proxy(read(), {\n get(_target, key) {\n if (typeof key === 'string' && resolvers.has(key)) need(key);\n return Reflect.get(read(), key);\n },\n has(_target, key) {\n return Reflect.has(read(), key);\n },\n ownKeys() {\n return Reflect.ownKeys(read());\n },\n getOwnPropertyDescriptor(_target, key) {\n return Reflect.getOwnPropertyDescriptor(read(), key);\n },\n });\n}\n\n/** A copy of `config` with the dotted path removed, copying every record on\n * the way down. A path that is not there comes back unchanged. */\nfunction dropAtPath(config: Record<string, unknown>, path: string): Record<string, unknown> {\n const [head, ...rest] = path.split('.');\n if (head === undefined || !(head in config)) return config;\n if (rest.length === 0) {\n const { [head]: _dropped, ...kept } = config;\n return kept;\n }\n const child = config[head];\n if (!isRecord(child)) return config;\n return { ...config, [head]: dropAtPath(child, rest.join('.')) };\n}\n\n/**\n * What writing `value` at `path` makes of a trial's raw config and its set of\n * unpinned paths. `auto` pins nothing and joins the set; any other value is\n * written and leaves it. Both the store and the chrome go through this, so\n * they cannot disagree about what a write meant.\n */\nexport function applyConfigWrite<TC>(\n config: TC,\n autoPaths: readonly string[],\n path: string,\n value: unknown,\n): { config: TC; autoPaths: readonly string[] } {\n if (isAuto(value)) {\n if (autoPaths.includes(path)) return { config, autoPaths };\n return { config, autoPaths: [...autoPaths, path] };\n }\n return {\n config: withValueAtPath(config, path, value),\n autoPaths: autoPaths.includes(path) ? autoPaths.filter((p) => p !== path) : autoPaths,\n };\n}\n"]}
|