@weasel-js/labkit 1.4.0-pre.0 → 1.4.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 +226 -55
- package/dist/_dts/{CanvasStackContext-DtAn_3O-.d.ts → CanvasStackContext-D6M0o8cQ.d.ts} +1 -1
- package/dist/_dts/{frac-Cp3NivlC.d.ts → frac-QS7XzvpG.d.ts} +11 -4
- package/dist/_dts/{index-BDVzvRzQ.d.ts → index-B2X21G1c.d.ts} +8 -2
- package/dist/_dts/{types-D6s4b7if.d.ts → types-C1Zw7s5P.d.ts} +1 -0
- package/dist/_dts/{types-DYMaEvM5.d.ts → types-ChVJJHvk.d.ts} +3 -0
- package/dist/_dts/{useTrialState-gmMvZqPc.d.ts → useTrialState-Cdm13fvl.d.ts} +2 -1
- package/dist/canvas/index.d.ts +75 -9
- package/dist/canvas/index.js +2 -2
- package/dist/chrome/index.d.ts +4 -4
- package/dist/chrome/index.js +5 -5
- package/dist/chunk-2YILQ7Y5.js +33 -0
- package/dist/chunk-2YILQ7Y5.js.map +1 -0
- package/dist/{chunk-3P5TTTA7.js → chunk-4AMN4MMB.js} +5 -5
- package/dist/{chunk-3P5TTTA7.js.map → chunk-4AMN4MMB.js.map} +1 -1
- package/dist/{chunk-ROQDRXKW.js → chunk-7VW3S44V.js} +621 -51
- package/dist/chunk-7VW3S44V.js.map +1 -0
- package/dist/{chunk-4EY67BKJ.js → chunk-B5VWJBWM.js} +12 -6
- package/dist/chunk-B5VWJBWM.js.map +1 -0
- package/dist/{chunk-HEORBLVT.js → chunk-L5WBGUVR.js} +1664 -1628
- package/dist/chunk-L5WBGUVR.js.map +1 -0
- package/dist/{chunk-XJ6N32QP.js → chunk-LJSIUFYD.js} +14 -30
- package/dist/chunk-LJSIUFYD.js.map +1 -0
- package/dist/{chunk-6JMU56CU.js → chunk-QR5V3AFG.js} +6 -4
- package/dist/chunk-QR5V3AFG.js.map +1 -0
- package/dist/{chunk-LITE5YFB.js → chunk-UD7TEHZT.js} +15 -15
- package/dist/chunk-UD7TEHZT.js.map +1 -0
- package/dist/{chunk-3ZELRRBV.js → chunk-VHFQKAUL.js} +86 -6
- package/dist/chunk-VHFQKAUL.js.map +1 -0
- package/dist/{chunk-YQJNT4XQ.js → chunk-W3KSUXXR.js} +16 -13
- package/dist/chunk-W3KSUXXR.js.map +1 -0
- package/dist/{chunk-JDDIZSLL.js → chunk-XCBCZEBO.js} +3 -3
- package/dist/{chunk-JDDIZSLL.js.map → chunk-XCBCZEBO.js.map} +1 -1
- package/dist/{chunk-EQIB2PGC.js → chunk-Y2HK44TG.js} +3 -3
- package/dist/{chunk-EQIB2PGC.js.map → chunk-Y2HK44TG.js.map} +1 -1
- package/dist/{chunk-5TCEVMPI.js → chunk-YQGQUBKA.js} +3 -3
- package/dist/{chunk-5TCEVMPI.js.map → chunk-YQGQUBKA.js.map} +1 -1
- package/dist/controls/index.d.ts +2 -2
- package/dist/controls/index.js +3 -3
- package/dist/dragdrop/index.d.ts +3 -3
- package/dist/dragdrop/index.js +2 -2
- package/dist/index.d.ts +57 -59
- package/dist/index.js +335 -240
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +4 -4
- package/dist/layers/index.js +3 -3
- package/dist/loupe/index.d.ts +5 -5
- package/dist/loupe/index.js +2 -2
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +336 -15
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.js +4 -4
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +4 -2
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +64 -5
- package/dist/surface/index.d.ts +17 -2
- package/dist/surface/index.js +3 -2
- package/dist/ui/layers/index.js +2 -2
- package/dist/undo/index.d.ts +3 -3
- package/package.json +8 -8
- package/src/annotations/AnnotationOverlay.tsx +10 -6
- package/src/annotations/Annotations.less +6 -0
- package/src/annotations/Annotations.overlay.test.tsx +113 -0
- package/src/annotations/MarkList.tsx +3 -2
- package/src/annotations/store.test.ts +72 -0
- package/src/annotations/store.ts +30 -0
- package/src/annotations/types.ts +9 -2
- package/src/canvas/index.ts +11 -0
- package/src/config/builder.test.ts +5 -0
- package/src/config/builder.ts +6 -0
- package/src/config/types.ts +1 -0
- package/src/controls/ControlPanel.test.tsx +23 -0
- package/src/controls/ControlPanel.tsx +3 -0
- package/src/index.test.ts +1 -0
- package/src/index.ts +13 -9
- package/src/lab/Lab.surface.test.tsx +8 -3
- package/src/lab/LabHeader.test.tsx +12 -0
- package/src/lab/LabHeader.tsx +16 -6
- package/src/passthrough/weasel-ui.test.ts +9 -0
- package/src/passthrough/weasel-ui.ts +4 -0
- package/src/primitives/ZoomControl.tsx +2 -0
- package/src/state/store.ts +10 -0
- package/src/state/types.ts +3 -0
- package/src/surface/AGENTS.md +11 -1
- package/src/surface/index.ts +7 -1
- package/src/surface/useSurfaceTile.ts +20 -2
- package/src/surface/useTiledSurface.ts +4 -1
- package/src/theme/Interstellar.stories.tsx +4 -3
- package/src/theme/base.less +39 -1
- package/src/trial/Trial.less +27 -5
- package/src/trial/TrialBody.test.tsx +67 -0
- package/src/trial/TrialBody.tsx +155 -0
- package/src/trial/TrialChrome.tsx +18 -10
- package/src/trial/index.ts +2 -0
- package/dist/chunk-3ZELRRBV.js.map +0 -1
- package/dist/chunk-4EY67BKJ.js.map +0 -1
- package/dist/chunk-6JMU56CU.js.map +0 -1
- package/dist/chunk-HEORBLVT.js.map +0 -1
- package/dist/chunk-LITE5YFB.js.map +0 -1
- package/dist/chunk-ROQDRXKW.js.map +0 -1
- package/dist/chunk-XJ6N32QP.js.map +0 -1
- package/dist/chunk-YQJNT4XQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,52 +1,185 @@
|
|
|
1
1
|
# @weasel-js/labkit
|
|
2
2
|
|
|
3
|
-
React
|
|
3
|
+
React components for building **labs**: pages where you change parameters and
|
|
4
|
+
watch something redraw. A lab hosts one or more **instruments** — self-contained
|
|
5
|
+
experiments that own their settings and their state and render a picture from
|
|
6
|
+
both. Each open instrument gets a **trial**, one tile of the lab's workspace,
|
|
7
|
+
and the trial supplies the apparatus around it: a control panel, a toolbar, a
|
|
8
|
+
sidebar, undo, snapshots, persistence and drawing tools.
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
Reach for it when you have a visualization, a simulation or a rendering
|
|
11
|
+
algorithm and would rather not build the page around it.
|
|
6
12
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
Not published to npm — for now the package is intentionally local-install-only.
|
|
10
|
-
It also depends on a sibling clone of [weasel](https://github.com/orochi235/weasel),
|
|
11
|
-
so clone both side by side:
|
|
13
|
+
## Install
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
|
|
15
|
-
git clone https://github.com/orochi235/labkit.git
|
|
16
|
-
cd weasel && npm install && npm run build
|
|
17
|
-
cd ../labkit && npm install && npm run build
|
|
16
|
+
npm i @weasel-js/labkit
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
React 19 is a peer dependency (`react` and `react-dom`, `^19.0.0`). One
|
|
20
|
+
stylesheet import covers everything labkit draws, including the theme tokens and
|
|
21
|
+
the `@weasel-js/ui` components it passes through:
|
|
21
22
|
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
"@weasel-js/labkit": "file:../labkit"
|
|
25
|
-
}
|
|
23
|
+
```ts
|
|
24
|
+
import '@weasel-js/labkit/styles.css';
|
|
26
25
|
```
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
Annotations are on the `pre` tag until the next stable release:
|
|
28
|
+
`npm i @weasel-js/labkit@pre`.
|
|
29
|
+
|
|
30
|
+
## A lab
|
|
29
31
|
|
|
30
32
|
```tsx
|
|
31
|
-
import {
|
|
33
|
+
import { type ConfigOf, defineInstrument, f, Lab, localStorageAdapter } from '@weasel-js/labkit';
|
|
32
34
|
import '@weasel-js/labkit/styles.css';
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
const config = f.schema({
|
|
37
|
+
bins: f.number(20).range(5, 100).step(1).label('Bins'),
|
|
38
|
+
showAxes: f.boolean(true),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const Histogram = defineInstrument<{ samples: number[] }, ConfigOf<typeof config>>({
|
|
42
|
+
name: 'Histogram',
|
|
43
|
+
config,
|
|
44
|
+
initialState: () => ({ samples: sample(500) }),
|
|
45
|
+
render: () => null,
|
|
46
|
+
canvas: {
|
|
47
|
+
layers: [
|
|
48
|
+
{ id: 'axes', draw: drawAxes },
|
|
49
|
+
{ id: 'bars', draw: drawBars },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
layers: { ids: ['axes', 'bars'] },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export function App() {
|
|
35
56
|
return (
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
<Lab
|
|
58
|
+
instruments={[Histogram]}
|
|
59
|
+
defaultInstrument="Histogram"
|
|
60
|
+
title="Histogram"
|
|
61
|
+
storage={localStorageAdapter}
|
|
62
|
+
storageKey="histogram-lab"
|
|
63
|
+
/>
|
|
42
64
|
);
|
|
43
65
|
}
|
|
44
66
|
```
|
|
45
67
|
|
|
68
|
+
`f.schema` states the config once — values, types and controls — and the trial
|
|
69
|
+
renders the settings panel from it. `render` returns the instrument's own DOM;
|
|
70
|
+
returning `null` alongside `canvas` means the canvas layers are the whole
|
|
71
|
+
picture. `storage` keeps open trials and their state across reloads.
|
|
72
|
+
|
|
73
|
+
## Capabilities
|
|
74
|
+
|
|
75
|
+
A capability is a field on the instrument. Declaring it is what makes the trial
|
|
76
|
+
provide the corresponding chrome; they compose freely.
|
|
77
|
+
|
|
78
|
+
| Field | What the trial then provides |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `canvas` | A stack of layered `<canvas>` elements with pan and zoom, redrawing only the layers that changed |
|
|
81
|
+
| `layers` | A sidebar list that hides and reorders those layers |
|
|
82
|
+
| `annotations` | Drawing tools, an overlay on each region you name, a Marks panel, undo and export |
|
|
83
|
+
| `loupe` | A magnifier toggle that redraws the canvas through a zoomed camera, or calls your own `render` for DOM content |
|
|
84
|
+
| `dragDrop` | A palette to drag items from, and a drop pipeline that hands you world coordinates |
|
|
85
|
+
| `undo` | Undo and redo buttons, snapshotting state on the events you name |
|
|
86
|
+
| `job` | Starts async work, aborts it on unmount and on a key change, and renders progress and a cancel control |
|
|
87
|
+
| `tools` | A tool palette and a tool slot on the trial |
|
|
88
|
+
| `chrome` | Anything else, as contributions keyed to a region |
|
|
89
|
+
|
|
90
|
+
## Annotations
|
|
91
|
+
|
|
92
|
+
An instrument names regions of itself that accept **marks** — freehand strokes,
|
|
93
|
+
lines, arrows, rectangles, ellipses and text drawn over its picture. labkit
|
|
94
|
+
supplies the tool palette, the overlay, the store, undo, persistence and export.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
const paneRef = createRef<HTMLDivElement>();
|
|
98
|
+
|
|
99
|
+
defineInstrument<State, Config>({
|
|
100
|
+
// …
|
|
101
|
+
annotations: {
|
|
102
|
+
targets: () => [
|
|
103
|
+
{ id: 'pane', ref: paneRef, content: { w: 260, h: 180 }, positionDependsOn: ['angle'] },
|
|
104
|
+
],
|
|
105
|
+
meaning: {
|
|
106
|
+
statuses: [
|
|
107
|
+
{ id: 'open', label: 'Open', color: '#e5484d' },
|
|
108
|
+
{ id: 'fixed', label: 'Fixed', color: '#30a46c' },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A mark's position is stored as fractions of its target's `content` box, so it
|
|
116
|
+
stays on the same feature when the picture is rendered at a different size.
|
|
117
|
+
`positionDependsOn` names the config keys that move the picture; labkit
|
|
118
|
+
snapshots their values beside each mark, and a mark whose values have since
|
|
119
|
+
changed draws dashed and reports `isStale`. `meaning.statuses` is the optional
|
|
120
|
+
vocabulary a mark can be labelled with — a status carries its own color, which
|
|
121
|
+
the mark on the canvas follows.
|
|
122
|
+
|
|
123
|
+
Marks live in the trial's record and survive a reload. An instrument that would
|
|
124
|
+
rather keep them in a format it already owns declares `annotations.storage` with
|
|
125
|
+
a `load`/`save` pair, and labkit never writes its own slot.
|
|
126
|
+
|
|
127
|
+
`useAnnotations()` reaches the store from the instrument's `render` or from a
|
|
128
|
+
chrome contribution, and re-renders its caller as marks change:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
const marks = useAnnotations();
|
|
132
|
+
const stale = marks.query({ target: 'pane' }).filter((m) => marks.isStale(m, config));
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`selection()` answers which marks the user has picked on the overlay, as
|
|
136
|
+
annotation ids, and `setSelection()` replaces them — the same selection
|
|
137
|
+
weasel's own click, marquee and handles drive, merged across every target.
|
|
138
|
+
A panel that opens a card for the clicked mark subscribes and re-reads it.
|
|
139
|
+
|
|
140
|
+
It also carries `get`, `hitTest`, `within`, `add`, `update`, `setMeta`,
|
|
141
|
+
`remove`, `undo`/`redo` and `capture`.
|
|
142
|
+
|
|
143
|
+
**Export.** A target declaring `base()` hands over the picture underneath its
|
|
144
|
+
marks — SVG markup, an image `src` or a canvas — and
|
|
145
|
+
`capture(target, { format, scale })` returns a Blob with the marks drawn on top,
|
|
146
|
+
as PNG or, from an SVG base, as vector SVG. Declaring `annotations` earns an
|
|
147
|
+
Export button in the toolbar; `onCapture` fires after every export, labkit's own
|
|
148
|
+
chrome included.
|
|
149
|
+
|
|
150
|
+
The pieces are exported for a host that wants to assemble them itself:
|
|
151
|
+
`createAnnotationStore`, `<AnnotationOverlay>`, `<AnnotationTargets>`,
|
|
152
|
+
`<MarkList>`, `<ExportMenu>`, `ANNOTATION_TOOLS` and `capturePlan`.
|
|
153
|
+
|
|
154
|
+
## Chrome
|
|
155
|
+
|
|
156
|
+
A trial's chrome has six named regions — `titlebar`, `toolbar`, `palette`,
|
|
157
|
+
`sidebar`, `viewport`, `status`. A `TrialContribution` names one and supplies
|
|
158
|
+
data the region lays out, or a `render` function that opts out of that layout:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<Lab
|
|
162
|
+
chrome={[{ id: 'seed', region: 'status', item: { text: `seed ${seed}` } }]}
|
|
163
|
+
suppress={['fps']}
|
|
164
|
+
// …
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Contributions passed to `<Lab>` apply to every trial; an instrument's own
|
|
169
|
+
`chrome` field applies to its trials only. `suppress` drops a built-in by id and
|
|
170
|
+
throws on an id that is not there.
|
|
171
|
+
|
|
172
|
+
A sidebar section can be torn out of the trial into the workspace — as a tile
|
|
173
|
+
beside the trials, or as a floating panel — with the trial still rendering into
|
|
174
|
+
it. `SidebarSection.undockable` and `undockAs` control the offer;
|
|
175
|
+
`undockPanel` / `dockPanel` move one.
|
|
176
|
+
|
|
46
177
|
## Theming
|
|
47
178
|
|
|
48
179
|
labkit ships one theme, `interstellar` — a cosmic dark and a warm parchment
|
|
49
|
-
light — as a value, not a stylesheet
|
|
180
|
+
light — as a value, not a stylesheet. `<Lab>` and `<LabShell>` apply it for you;
|
|
181
|
+
`mode` is `"auto"` (follow the OS), `"light"` or `"dark"`. Only `styles.css`
|
|
182
|
+
needs importing, and the token values arrive through the provider:
|
|
50
183
|
|
|
51
184
|
```tsx
|
|
52
185
|
import { interstellarTheme } from '@weasel-js/labkit';
|
|
@@ -55,9 +188,31 @@ import { ThemeProvider } from '@weasel-js/theme/react';
|
|
|
55
188
|
<ThemeProvider theme={interstellarTheme} mode="dark">…</ThemeProvider>
|
|
56
189
|
```
|
|
57
190
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
191
|
+
Style your own surfaces against the `--wzl-*` tokens so they follow the mode the
|
|
192
|
+
lab is in. Class names are `lk-*` and are not public API — every component takes
|
|
193
|
+
a `className`.
|
|
194
|
+
|
|
195
|
+
## Without the runtime
|
|
196
|
+
|
|
197
|
+
The presentational pieces work on their own. `<LabShell>` is a titled page
|
|
198
|
+
frame; `<Workspace>` is a tile grid that can be resized and reordered and takes
|
|
199
|
+
any children:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { LabShell, Workspace } from '@weasel-js/labkit';
|
|
203
|
+
|
|
204
|
+
<LabShell title="My Lab" header={<button>+ Add</button>}>
|
|
205
|
+
<Workspace ids={['a', 'b']} resizable>
|
|
206
|
+
<MyPane id="a" />
|
|
207
|
+
<MyPane id="b" />
|
|
208
|
+
</Workspace>
|
|
209
|
+
</LabShell>;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`<Toolbar>`, `<Sidebar>`, `<StatusBar>`, `<FpsMeter>`, `<ScaleIndicator>`,
|
|
213
|
+
`<Legend>`, `<FloatingPanel>`, `<ZoomControl>` and the `@weasel-js/ui` property
|
|
214
|
+
rows re-exported from the root are all usable this way. `<Trial>` is not: it
|
|
215
|
+
reads the lab store and only mounts inside a `<Lab>`.
|
|
61
216
|
|
|
62
217
|
## Driving your own renderer
|
|
63
218
|
|
|
@@ -68,40 +223,56 @@ labkit and keep the GL yourself:
|
|
|
68
223
|
import { toDeviceRect, useSurfaceTile, useTiledSurface } from '@weasel-js/labkit/surface';
|
|
69
224
|
```
|
|
70
225
|
|
|
71
|
-
A trial's `view` is opaque to labkit —
|
|
72
|
-
|
|
73
|
-
|
|
226
|
+
A trial's `view` is opaque to labkit — persisted, restored on Reset and handed
|
|
227
|
+
to the instrument without being read into — so a 3D lab stores an orbit there.
|
|
228
|
+
Both cameras ship independently of `CanvasStack`: `useOrbit` for a 3D view,
|
|
229
|
+
`usePanZoom` for a 2D one over a `ViewTransform`. See `src/surface/AGENTS.md`
|
|
230
|
+
for the contract and the traps.
|
|
74
231
|
|
|
75
|
-
|
|
232
|
+
## Subpath exports
|
|
76
233
|
|
|
77
|
-
|
|
234
|
+
The root barrel carries the common surface; a subpath reaches one slice
|
|
235
|
+
directly. Several expose more than the root does, `/state` most of all.
|
|
78
236
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
237
|
+
| Subpath | |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| `@weasel-js/labkit` | Everything |
|
|
240
|
+
| `/styles.css` | The one stylesheet |
|
|
241
|
+
| `/primitives` | Toolbar, Sidebar, StatusBar, Legend, FloatingPanel, meters |
|
|
242
|
+
| `/chrome` | Regions, contribution types, built-in contributions |
|
|
243
|
+
| `/controls` | `<ControlPanel>` and the config field types |
|
|
244
|
+
| `/canvas` | `<CanvasStack>`, coordinate helpers, `usePanZoom`, `useOrbit` |
|
|
245
|
+
| `/layers` | `<LayerList>` |
|
|
246
|
+
| `/loupe` | Magnifier components and `useLoupe` |
|
|
247
|
+
| `/surface` | Tiled surface hooks for your own renderer |
|
|
248
|
+
| `/job` | `useJob` and the job capability types |
|
|
249
|
+
| `/state` | The lab store, storage adapters, `useTrialState`, serialization helpers |
|
|
250
|
+
| `/undo` | Undo stack and event bus |
|
|
251
|
+
| `/dragdrop` | `<Palette>`, `<DragGhost>`, `useDragDrop` |
|
|
252
|
+
| `/ui/layers` | `<LayerStack>`, the expandable layer-card list |
|
|
253
|
+
| `/weasel-ui`, `/weasel-canvas` | Passthroughs to `@weasel-js/ui` and `@weasel-js/core` |
|
|
88
254
|
|
|
89
255
|
## Development
|
|
90
256
|
|
|
257
|
+
labkit is a workspace package in the [weasel](https://github.com/orochi235/weasel)
|
|
258
|
+
monorepo. From the repo root, `npm install` once, then from `packages/labkit`:
|
|
259
|
+
|
|
91
260
|
```bash
|
|
92
|
-
npm
|
|
93
|
-
npm run dev
|
|
94
|
-
npm
|
|
95
|
-
npm
|
|
96
|
-
npm run
|
|
97
|
-
npm run
|
|
261
|
+
npm run dev # Vite dev server on examples/minimal
|
|
262
|
+
npm run dev:annotate # …on examples/annotate-lab (and dev:drag, dev:weasel, dev:schema)
|
|
263
|
+
npm test # Vitest
|
|
264
|
+
npm run lint # Biome, plus the class-prefix and design-token checks
|
|
265
|
+
npm run build # dist/ for publish
|
|
266
|
+
npm run docs:dev # VitePress on docs/ (docs:build to render it)
|
|
98
267
|
```
|
|
99
268
|
|
|
269
|
+
Storybook is built at the repo root: `npm run storybook` from there covers every
|
|
270
|
+
package.
|
|
271
|
+
|
|
100
272
|
## Documentation
|
|
101
273
|
|
|
102
|
-
- [Docs site](https://orochi235.github.io/labkit/)
|
|
103
|
-
- [Recipes](https://orochi235.github.io/labkit/RECIPES) — composition patterns
|
|
104
|
-
- [Agent guide](https://orochi235.github.io/labkit/AGENTS) —
|
|
105
|
-
- [Storybook](https://orochi235.github.io/
|
|
106
|
-
- [
|
|
107
|
-
- [Design spec](https://github.com/orochi235/labkit/blob/main/docs/superpowers/specs/2026-04-26-labkit-design.md)
|
|
274
|
+
- [Docs site](https://orochi235.github.io/weasel/labkit/)
|
|
275
|
+
- [Recipes](https://orochi235.github.io/weasel/labkit/RECIPES) — composition patterns
|
|
276
|
+
- [Agent guide](https://orochi235.github.io/weasel/labkit/AGENTS) — a map of the source
|
|
277
|
+
- [Storybook](https://orochi235.github.io/weasel/docs/ui/storybook/)
|
|
278
|
+
- [Design spec](https://github.com/orochi235/weasel/blob/main/packages/labkit/docs/superpowers/specs/2026-04-26-labkit-design.md)
|
|
@@ -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-QS7XzvpG.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,6 +1,6 @@
|
|
|
1
1
|
import { ComponentType, ReactNode, RefObject } from 'react';
|
|
2
|
-
import { R as ResolvedConfig, C as ConfigField, a as ConfigSchema } from './types-
|
|
3
|
-
import { c as SavedSnapshot } from './types-
|
|
2
|
+
import { R as ResolvedConfig, C as ConfigField, a as ConfigSchema } from './types-C1Zw7s5P.js';
|
|
3
|
+
import { c as SavedSnapshot } from './types-ChVJJHvk.js';
|
|
4
4
|
import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
|
|
5
5
|
import { S as Scene } from './types-C-gh9Ap-.js';
|
|
6
6
|
|
|
@@ -671,8 +671,15 @@ interface AnnotationsApi {
|
|
|
671
671
|
within(target: string, box: FracRect): Annotation[];
|
|
672
672
|
/** Whether `a`'s position still describes the picture `config` produces. */
|
|
673
673
|
isStale(a: Annotation, config: unknown): boolean;
|
|
674
|
-
/**
|
|
675
|
-
*
|
|
674
|
+
/** The marks the user currently has selected, across every target, as
|
|
675
|
+
* annotation ids. Every one resolves through `get`. */
|
|
676
|
+
selection(): readonly string[];
|
|
677
|
+
/** Replace the selection. An id naming a target or a mark that is not
|
|
678
|
+
* there is dropped, the way `update` and `remove` ignore one. */
|
|
679
|
+
setSelection(ids: readonly string[]): void;
|
|
680
|
+
/** Fires after every mutation *and* after a selection change — weasel keeps
|
|
681
|
+
* a canvas's selection on the scene, so both already arrive on this one
|
|
682
|
+
* channel. No delta: re-query, and re-read `selection()`. */
|
|
676
683
|
subscribe(fn: () => void): () => void;
|
|
677
684
|
/** Whether the last mark change on any target can be taken back. Weasel
|
|
678
685
|
* history is the authority; this only decides *which* target's. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { R as ResolvedConfig, C as ConfigField, c as ControlRenderer } from './types-
|
|
3
|
+
import { R as ResolvedConfig, C as ConfigField, c as ControlRenderer } from './types-C1Zw7s5P.js';
|
|
4
4
|
|
|
5
5
|
/** Props for `<PropertyPanel>`. */
|
|
6
6
|
interface PropertyPanelProps {
|
|
@@ -155,6 +155,12 @@ interface NumberRowProps {
|
|
|
155
155
|
max?: number;
|
|
156
156
|
step?: number;
|
|
157
157
|
placeholder?: string;
|
|
158
|
+
/**
|
|
159
|
+
* Optional suffix rendered after the field. A string becomes a dim "word"
|
|
160
|
+
* unit (e.g. "px"); pass JSX like `<sup>°</sup>` for symbol units. Display
|
|
161
|
+
* only — it never participates in parsing, and the value stays a number.
|
|
162
|
+
*/
|
|
163
|
+
unit?: ReactNode;
|
|
158
164
|
layout?: PropertyRowLayout;
|
|
159
165
|
description?: string;
|
|
160
166
|
/** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
|
|
@@ -162,7 +168,7 @@ interface NumberRowProps {
|
|
|
162
168
|
}
|
|
163
169
|
/** A number typed directly. Reach for `<SliderRow>` when the range matters
|
|
164
170
|
* more than the exact value. */
|
|
165
|
-
declare function NumberRow({ label, value, onChange, min, max, step, placeholder, layout, description, span, }: NumberRowProps): react_jsx_runtime.JSX.Element;
|
|
171
|
+
declare function NumberRow({ label, value, onChange, min, max, step, placeholder, unit, layout, description, span, }: NumberRowProps): react_jsx_runtime.JSX.Element;
|
|
166
172
|
interface PropertyOption<T extends string> {
|
|
167
173
|
value: T;
|
|
168
174
|
label: ReactNode;
|
|
@@ -30,6 +30,9 @@ interface TrialRecord<TS = unknown, TC = unknown, TV = unknown> {
|
|
|
30
30
|
view: TV;
|
|
31
31
|
/** This trial's own tool slot. Undefined means it reads the lab's. */
|
|
32
32
|
activeToolId?: string | null;
|
|
33
|
+
/** The extent the trial's sidebar was last dragged to, in pixels. Undefined
|
|
34
|
+
* until someone moves the seam. */
|
|
35
|
+
sidebarWidth?: number;
|
|
33
36
|
/** The marks on this trial's annotation targets, as `AnnotationsApi.toJSON`
|
|
34
37
|
* wrote them. Opaque here, and absent for a trial whose instrument declares
|
|
35
38
|
* no `annotations` — or declares its own `storage`. Not in `state`, which
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as StorageAdapter, b as LabStoreState, T as TrialRecord, c as SavedSnapshot, h as LabMode, e as UndockedPanel, I as InstrumentSerializers, C as CreateLabStoreOptions, d as TrialStateHandle } from './types-
|
|
1
|
+
import { a as StorageAdapter, b as LabStoreState, T as TrialRecord, c as SavedSnapshot, h as LabMode, e as UndockedPanel, I as InstrumentSerializers, C as CreateLabStoreOptions, d as TrialStateHandle } from './types-ChVJJHvk.js';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode, ReactElement } from 'react';
|
|
4
4
|
import { StoreApi } from 'zustand/vanilla';
|
|
@@ -25,6 +25,7 @@ interface LabStoreActions {
|
|
|
25
25
|
updateTrialState: <TS>(id: string, next: TS | ((prev: TS) => TS)) => void;
|
|
26
26
|
updateTrialConfig: <TC>(id: string, key: keyof TC, value: TC[keyof TC]) => void;
|
|
27
27
|
updateTrialView: (id: string, view: unknown) => void;
|
|
28
|
+
updateTrialSidebarWidth: (id: string, width: number) => void;
|
|
28
29
|
updateTrialAnnotations: (id: string, doc: unknown) => void;
|
|
29
30
|
updateTrialUndoStack: (id: string, next: TrialRecord['undoStack'] | ((prev: TrialRecord['undoStack']) => TrialRecord['undoStack'])) => void;
|
|
30
31
|
setTrialInstrument: (id: string, instrumentName: string) => void;
|
package/dist/canvas/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { V as ViewTransform, c as WorldSpec, a2 as ViewportSize, a as Point, W as WorldFrame } from '../_dts/frac-
|
|
4
|
-
export { x as DEFAULT_FRAME, a4 as applyCamera, aa as resolveFrame } from '../_dts/frac-
|
|
5
|
-
import { C as CanvasLayerDescriptor } from '../_dts/CanvasStackContext-
|
|
6
|
-
export { a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from '../_dts/CanvasStackContext-
|
|
7
|
-
import '../_dts/types-
|
|
2
|
+
import { ReactNode, WheelEvent, PointerEvent } from 'react';
|
|
3
|
+
import { V as ViewTransform, c as WorldSpec, a2 as ViewportSize, a as Point, W as WorldFrame } from '../_dts/frac-QS7XzvpG.js';
|
|
4
|
+
export { x as DEFAULT_FRAME, a4 as applyCamera, aa as resolveFrame } from '../_dts/frac-QS7XzvpG.js';
|
|
5
|
+
import { C as CanvasLayerDescriptor } from '../_dts/CanvasStackContext-D6M0o8cQ.js';
|
|
6
|
+
export { a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from '../_dts/CanvasStackContext-D6M0o8cQ.js';
|
|
7
|
+
import '../_dts/types-C1Zw7s5P.js';
|
|
8
8
|
import '../_dts/PrefsForm-BkUJZx0A.js';
|
|
9
|
-
import '../_dts/types-
|
|
9
|
+
import '../_dts/types-ChVJJHvk.js';
|
|
10
10
|
import '../_dts/types-DJ79Tg5J.js';
|
|
11
11
|
import '../_dts/types-C-gh9Ap-.js';
|
|
12
12
|
|
|
@@ -64,5 +64,71 @@ declare function worldToScreen(world: Point, view: ViewTransform, frame?: WorldF
|
|
|
64
64
|
/** Unproject a screen point back to world coordinates under a view. */
|
|
65
65
|
declare function screenToWorld(screen: Point, view: ViewTransform, frame?: WorldFrame): Point;
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
/** A point in the space the instrument works in. */
|
|
68
|
+
interface Vec3 {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
z: number;
|
|
72
|
+
}
|
|
73
|
+
/** An orbit camera as a trial view: where it looks from, and what it looks at.
|
|
74
|
+
* labkit does not turn this into a matrix — the host's renderer does. */
|
|
75
|
+
interface OrbitView {
|
|
76
|
+
yaw: number;
|
|
77
|
+
pitch: number;
|
|
78
|
+
distance: number;
|
|
79
|
+
target: Vec3;
|
|
80
|
+
}
|
|
81
|
+
/** Just short of the pole. At exactly ±PI/2 the azimuth is undefined and the
|
|
82
|
+
* camera rolls, which reads as the model jumping rather than as a limit. */
|
|
83
|
+
declare const PITCH_LIMIT: number;
|
|
84
|
+
declare function clampPitch(pitch: number): number;
|
|
85
|
+
declare function wrapYaw(yaw: number): number;
|
|
86
|
+
/** The view a drag of (dx, dy) from `start` produces. Absolute against the drag
|
|
87
|
+
* start, so it can be re-applied any number of times without compounding. */
|
|
88
|
+
declare function orbitAfterDrag(start: OrbitView, dx: number, dy: number): OrbitView;
|
|
89
|
+
/** Multiplicative, so one notch covers the same proportion of the distance
|
|
90
|
+
* whether the camera is near or far. */
|
|
91
|
+
declare function orbitAfterWheel(view: OrbitView, deltaY: number, minDistance: number, maxDistance: number): OrbitView;
|
|
92
|
+
interface UseOrbitOptions {
|
|
93
|
+
view: OrbitView;
|
|
94
|
+
onViewChange: (v: OrbitView) => void;
|
|
95
|
+
/** Restored on double-click. Defaults to the view the hook first saw. */
|
|
96
|
+
home?: OrbitView;
|
|
97
|
+
minDistance?: number;
|
|
98
|
+
maxDistance?: number;
|
|
99
|
+
}
|
|
100
|
+
interface OrbitHandlers {
|
|
101
|
+
onWheel: (e: WheelEvent<HTMLElement>) => void;
|
|
102
|
+
onPointerDown: (e: PointerEvent<HTMLElement>) => void;
|
|
103
|
+
onPointerMove: (e: PointerEvent<HTMLElement>) => void;
|
|
104
|
+
onPointerUp: (e: PointerEvent<HTMLElement>) => void;
|
|
105
|
+
onDoubleClick: () => void;
|
|
106
|
+
isDragging: () => boolean;
|
|
107
|
+
}
|
|
108
|
+
/** Pointer gestures over an orbit view: drag to turn, wheel or pinch to dolly,
|
|
109
|
+
* double-click to go home. The 3D peer of `usePanZoom`. */
|
|
110
|
+
declare function useOrbit({ view, onViewChange, home, minDistance, maxDistance, }: UseOrbitOptions): OrbitHandlers;
|
|
111
|
+
|
|
112
|
+
interface UsePanZoomOptions {
|
|
113
|
+
view: ViewTransform;
|
|
114
|
+
onViewChange: (v: ViewTransform) => void;
|
|
115
|
+
/** The opening `view.zoom` always stays reachable, widening these past
|
|
116
|
+
* whatever is passed here if it would otherwise exclude it. */
|
|
117
|
+
minZoom?: number;
|
|
118
|
+
maxZoom?: number;
|
|
119
|
+
/** The instrument's coordinate system, resolved against the viewport. The
|
|
120
|
+
* wheel anchors in this frame; omitting it anchors at the element's
|
|
121
|
+
* top-left, which is what labkit did before a frame could be declared. */
|
|
122
|
+
frame?: WorldFrame;
|
|
123
|
+
}
|
|
124
|
+
interface PanZoomHandlers {
|
|
125
|
+
onWheel: (e: WheelEvent<HTMLElement>) => void;
|
|
126
|
+
onPointerDown: (e: PointerEvent<HTMLElement>) => void;
|
|
127
|
+
onPointerMove: (e: PointerEvent<HTMLElement>) => void;
|
|
128
|
+
onPointerUp: (e: PointerEvent<HTMLElement>) => void;
|
|
129
|
+
isDragging: () => boolean;
|
|
130
|
+
}
|
|
131
|
+
declare function usePanZoom({ view, onViewChange, minZoom, maxZoom, frame, }: UsePanZoomOptions): PanZoomHandlers;
|
|
132
|
+
|
|
133
|
+
export { CanvasLayerDescriptor, CanvasStack, PITCH_LIMIT, ViewportSize, WorldFrame, WorldSpec, centerOn, clampPitch, orbitAfterDrag, orbitAfterWheel, screenToWorld, useOrbit, usePanZoom, worldToScreen, wrapYaw, zoomAt };
|
|
134
|
+
export type { CanvasStackProps, OrbitHandlers, OrbitView, PanZoomHandlers, UseOrbitOptions, UsePanZoomOptions, Vec3, ZoomAtOptions };
|
package/dist/canvas/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { CanvasStack } from '../chunk-
|
|
1
|
+
export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from '../chunk-VHFQKAUL.js';
|
|
2
2
|
export { centerOn, zoomAt } from '../chunk-CTRKTLYZ.js';
|
|
3
3
|
export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from '../chunk-2AYGEN57.js';
|
|
4
4
|
export { CanvasStackContext } from '../chunk-CQLQPQ4P.js';
|
|
5
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-7VW3S44V.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/dist/chrome/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { c as ControlRenderer } from '../_dts/types-
|
|
2
|
-
import { I as Instrument, _ as TrialChromeContext, m as TrialContribution } from '../_dts/frac-
|
|
3
|
-
export { z as IconComponent, O as SidebarSection, Q as StatusReadout, Y as ToolItem, Z as ToolbarItem, $ as TrialRegion, a1 as ViewportControl } from '../_dts/frac-
|
|
1
|
+
import { c as ControlRenderer } from '../_dts/types-C1Zw7s5P.js';
|
|
2
|
+
import { I as Instrument, _ as TrialChromeContext, m as TrialContribution } from '../_dts/frac-QS7XzvpG.js';
|
|
3
|
+
export { z as IconComponent, O as SidebarSection, Q as StatusReadout, Y as ToolItem, Z as ToolbarItem, $ as TrialRegion, a1 as ViewportControl } from '../_dts/frac-QS7XzvpG.js';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import '../_dts/PrefsForm-BkUJZx0A.js';
|
|
6
6
|
import 'react';
|
|
7
|
-
import '../_dts/types-
|
|
7
|
+
import '../_dts/types-ChVJJHvk.js';
|
|
8
8
|
import '../_dts/types-DJ79Tg5J.js';
|
|
9
9
|
import '../_dts/types-C-gh9Ap-.js';
|
|
10
10
|
|
package/dist/chrome/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, mergeContributions, suppressContributions } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
export { PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, mergeContributions, suppressContributions } from '../chunk-UD7TEHZT.js';
|
|
2
|
+
import '../chunk-QR5V3AFG.js';
|
|
3
|
+
import '../chunk-W3KSUXXR.js';
|
|
4
4
|
import '../chunk-SBXNQW4G.js';
|
|
5
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-L5WBGUVR.js';
|
|
6
6
|
import '../chunk-CQLQPQ4P.js';
|
|
7
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-7VW3S44V.js';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { useStore } from 'zustand/react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/state/context.tsx
|
|
6
|
+
var LabStoreContext = createContext(null);
|
|
7
|
+
function LabStoreProvider({
|
|
8
|
+
store,
|
|
9
|
+
children
|
|
10
|
+
}) {
|
|
11
|
+
return /* @__PURE__ */ jsx(LabStoreContext.Provider, { value: { store }, children });
|
|
12
|
+
}
|
|
13
|
+
function useLabStore() {
|
|
14
|
+
const ctx = useContext(LabStoreContext);
|
|
15
|
+
if (!ctx) throw new Error("[labkit] useLabStore must be used inside <LabStoreProvider>");
|
|
16
|
+
return useStore(ctx.store);
|
|
17
|
+
}
|
|
18
|
+
var TrialIdContext = createContext(null);
|
|
19
|
+
function TrialIdProvider({
|
|
20
|
+
trialId,
|
|
21
|
+
children
|
|
22
|
+
}) {
|
|
23
|
+
return /* @__PURE__ */ jsx(TrialIdContext.Provider, { value: trialId, children });
|
|
24
|
+
}
|
|
25
|
+
function useTrialId() {
|
|
26
|
+
const id = useContext(TrialIdContext);
|
|
27
|
+
if (!id) throw new Error("[labkit] useTrialId must be used inside <TrialIdProvider>");
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, useLabStore, useTrialId };
|
|
32
|
+
//# sourceMappingURL=chunk-2YILQ7Y5.js.map
|
|
33
|
+
//# sourceMappingURL=chunk-2YILQ7Y5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/state/context.tsx"],"names":[],"mappings":";;;;;AASO,IAAM,eAAA,GAAkB,cAA2B,IAAI;AAGvD,SAAS,gBAAA,CAAiB;AAAA,EAC/B,KAAA;AAAA,EACA;AACF,CAAA,EAGiB;AACf,EAAA,uBAAO,GAAA,CAAC,gBAAgB,QAAA,EAAhB,EAAyB,OAAO,EAAE,KAAA,IAAU,QAAA,EAAS,CAAA;AAC/D;AAGO,SAAS,WAAA,GAAgE;AAC9E,EAAA,MAAM,GAAA,GAAM,WAAW,eAAe,CAAA;AACtC,EAAA,IAAI,CAAC,GAAA,EAAK,MAAM,IAAI,MAAM,6DAA6D,CAAA;AACvF,EAAA,OAAO,QAAA,CAAS,IAAI,KAAK,CAAA;AAC3B;AAGO,IAAM,cAAA,GAAiB,cAA6B,IAAI;AAIxD,SAAS,eAAA,CAAgB;AAAA,EAC9B,OAAA;AAAA,EACA;AACF,CAAA,EAGiB;AACf,EAAA,2BAAQ,cAAA,CAAe,QAAA,EAAf,EAAwB,KAAA,EAAO,SAAU,QAAA,EAAS,CAAA;AAC5D;AAIO,SAAS,UAAA,GAAqB;AACnC,EAAA,MAAM,EAAA,GAAK,WAAW,cAAc,CAAA;AACpC,EAAA,IAAI,CAAC,EAAA,EAAI,MAAM,IAAI,MAAM,2DAA2D,CAAA;AACpF,EAAA,OAAO,EAAA;AACT","file":"chunk-2YILQ7Y5.js","sourcesContent":["import { createContext, type ReactElement, type ReactNode, useContext } from 'react';\nimport { useStore } from 'zustand/react';\nimport type { LabStore } from './store';\nimport type { LabStoreState } from './types';\n\ntype LabStoreCtx = { store: LabStore } | null;\n\n/** Context carrying the lab store. Prefer `useLabStore`; this is exported for\n * code that needs to read the context without subscribing. */\nexport const LabStoreContext = createContext<LabStoreCtx>(null);\n\n/** Provides a lab store to its subtree. */\nexport function LabStoreProvider({\n store,\n children,\n}: {\n store: LabStore;\n children: ReactNode;\n}): ReactElement {\n return <LabStoreContext.Provider value={{ store }}>{children}</LabStoreContext.Provider>;\n}\n\n/** Subscribe to the whole lab store. Throws outside a `<LabStoreProvider>`. */\nexport function useLabStore(): LabStoreState & ReturnType<LabStore['getState']> {\n const ctx = useContext(LabStoreContext);\n if (!ctx) throw new Error('[labkit] useLabStore must be used inside <LabStoreProvider>');\n return useStore(ctx.store);\n}\n\n/** Context carrying which trial the subtree belongs to. */\nexport const TrialIdContext = createContext<string | null>(null);\n\n/** Names the trial its subtree belongs to, so an instrument's hooks can\n * find their own record in the store without being passed an id. */\nexport function TrialIdProvider({\n trialId,\n children,\n}: {\n trialId: string;\n children: ReactNode;\n}): ReactElement {\n return <TrialIdContext.Provider value={trialId}>{children}</TrialIdContext.Provider>;\n}\n\n/** The id of the trial this component is inside. Throws outside a\n * `<TrialIdProvider>`. */\nexport function useTrialId(): string {\n const id = useContext(TrialIdContext);\n if (!id) throw new Error('[labkit] useTrialId must be used inside <TrialIdProvider>');\n return id;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ce, le } from './chunk-L5WBGUVR.js';
|
|
2
2
|
import { useRef, useMemo, useState, useEffect } from 'react';
|
|
3
3
|
import { floatingStrategy, DEFAULT_INSET, DEFAULT_ANCHOR, FLOATING_DRAG_PREFIX } from 'windease';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -168,12 +168,12 @@ function JobProgress({ job, className }) {
|
|
|
168
168
|
determinate ? ` / ${total}` : ""
|
|
169
169
|
] }),
|
|
170
170
|
failures.length > 0 && /* @__PURE__ */ jsxs("span", { className: "lk-job__failures", title: failures.map((f) => f.error).join("\n"), children: [
|
|
171
|
-
/* @__PURE__ */ jsx(
|
|
171
|
+
/* @__PURE__ */ jsx(ce, { size: 14 }),
|
|
172
172
|
failures.length,
|
|
173
173
|
" failed"
|
|
174
174
|
] }),
|
|
175
175
|
error && /* @__PURE__ */ jsxs("span", { className: "lk-job__error", title: error, children: [
|
|
176
|
-
/* @__PURE__ */ jsx(
|
|
176
|
+
/* @__PURE__ */ jsx(le, { size: 14 }),
|
|
177
177
|
error
|
|
178
178
|
] }),
|
|
179
179
|
status === "running" && /* @__PURE__ */ jsx("button", { type: "button", className: "lk-job__cancel", onClick: job.cancel, children: "Cancel" })
|
|
@@ -216,5 +216,5 @@ function Sidebar({ children, title, collapsed = false, onToggle }) {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
export { FloatingPanel, JobProgress, Legend, Sidebar };
|
|
219
|
-
//# sourceMappingURL=chunk-
|
|
220
|
-
//# sourceMappingURL=chunk-
|
|
219
|
+
//# sourceMappingURL=chunk-4AMN4MMB.js.map
|
|
220
|
+
//# sourceMappingURL=chunk-4AMN4MMB.js.map
|