@weasel-js/labkit 1.5.2 → 1.6.1
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-CMMo180l.d.ts → CanvasStackContext-Wo6OwIVM.d.ts} +1 -1
- package/dist/_dts/FloatingPanel-7j0IcXwz.d.ts +30 -0
- package/dist/_dts/LayerList.d-BoELAMsM.d.ts +83 -0
- package/dist/_dts/{PrefsForm.d-DPy9I8It.d.ts → PrefsRow.d-DFpPIeIC.d.ts} +16 -1
- package/dist/_dts/PropertyField.d-D0ndfIIt.d.ts +404 -0
- package/dist/_dts/{Select.d-ZTiwzYyT.d.ts → Select.d-Cr8-zoup.d.ts} +105 -15
- package/dist/_dts/{usePanZoom-SSTZe410.d.ts → Stage-DWBBi8IU.d.ts} +80 -35
- package/dist/_dts/{index-DlKolEIZ.d.ts → index-CKyX0ME_.d.ts} +12 -13
- package/dist/_dts/{frac-B4hFJMiN.d.ts → types-B7yBTTF9.d.ts} +709 -651
- package/dist/_dts/{types-Db-3NNk1.d.ts → types-DTuViBvc.d.ts} +21 -2
- package/dist/_dts/{useTrialState-pnAE-UBD.d.ts → useTrialState-CMaV6UD4.d.ts} +2 -3
- package/dist/canvas/index.d.ts +41 -8
- package/dist/canvas/index.js +5 -3
- package/dist/chrome/index.d.ts +6 -6
- package/dist/chrome/index.js +9 -7
- package/dist/chunk-24W5VEJW.js +18 -0
- package/dist/chunk-24W5VEJW.js.map +1 -0
- package/dist/chunk-2RVCB2PT.js +3 -0
- package/dist/chunk-2RVCB2PT.js.map +1 -0
- package/dist/chunk-3RY6WL5T.js +38 -0
- package/dist/chunk-3RY6WL5T.js.map +1 -0
- package/dist/{chunk-CKUKF72R.js → chunk-4TT3PNPR.js} +68 -55
- package/dist/chunk-4TT3PNPR.js.map +1 -0
- package/dist/{chunk-KJCUERQF.js → chunk-6FLXL3I7.js} +9 -164
- package/dist/chunk-6FLXL3I7.js.map +1 -0
- package/dist/chunk-ASOK76ZH.js +431 -0
- package/dist/chunk-ASOK76ZH.js.map +1 -0
- package/dist/chunk-AWQG7FCC.js +19 -0
- package/dist/chunk-AWQG7FCC.js.map +1 -0
- package/dist/chunk-CGHF6RZV.js +300 -0
- package/dist/chunk-CGHF6RZV.js.map +1 -0
- package/dist/chunk-DWMSB4CA.js +228 -0
- package/dist/chunk-DWMSB4CA.js.map +1 -0
- package/dist/chunk-GHGO52SO.js +92 -0
- package/dist/chunk-GHGO52SO.js.map +1 -0
- package/dist/{chunk-LRI3MBGR.js → chunk-HKV7LRHY.js} +4 -4
- package/dist/{chunk-LRI3MBGR.js.map → chunk-HKV7LRHY.js.map} +1 -1
- package/dist/{chunk-EMXAIRUJ.js → chunk-IRTGUQ5O.js} +5 -4
- package/dist/chunk-IRTGUQ5O.js.map +1 -0
- package/dist/{chunk-H6ZAOWNE.js → chunk-JVBYV6TL.js} +3 -3
- package/dist/{chunk-H6ZAOWNE.js.map → chunk-JVBYV6TL.js.map} +1 -1
- package/dist/chunk-KHK5FGER.js +145 -0
- package/dist/chunk-KHK5FGER.js.map +1 -0
- package/dist/chunk-NEXR37YJ.js +144 -0
- package/dist/chunk-NEXR37YJ.js.map +1 -0
- package/dist/{chunk-A2ACAGW6.js → chunk-O6FFFOHS.js} +32 -7
- package/dist/chunk-O6FFFOHS.js.map +1 -0
- package/dist/chunk-QGMOA2YE.js +731 -0
- package/dist/chunk-QGMOA2YE.js.map +1 -0
- package/dist/{chunk-ERFMC6WU.js → chunk-S4NCI32V.js} +19 -10
- package/dist/chunk-S4NCI32V.js.map +1 -0
- package/dist/{chunk-753Y6HE6.js → chunk-SLAQPUI4.js} +4 -4
- package/dist/chunk-SLAQPUI4.js.map +1 -0
- package/dist/{chunk-REOBGYMC.js → chunk-TJ77A4A3.js} +3 -5
- package/dist/chunk-TJ77A4A3.js.map +1 -0
- package/dist/chunk-TYN6MLXX.js +13864 -0
- package/dist/chunk-TYN6MLXX.js.map +1 -0
- package/dist/{chunk-RS3HRQWU.js → chunk-WPLAANH4.js} +4 -3
- package/dist/chunk-WPLAANH4.js.map +1 -0
- package/dist/config/index.d.ts +51 -7
- package/dist/config/index.js +4 -5
- package/dist/controls/index.d.ts +92 -5
- package/dist/controls/index.js +5 -4
- package/dist/dragdrop/index.d.ts +9 -6
- package/dist/dragdrop/index.js +3 -2
- package/dist/index.d.ts +108 -154
- package/dist/index.js +456 -600
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +5 -35
- package/dist/layers/index.js +3 -3
- package/dist/loupe/index.d.ts +21 -17
- package/dist/loupe/index.js +5 -3
- package/dist/overview/index.d.ts +56 -0
- package/dist/overview/index.js +288 -0
- package/dist/overview/index.js.map +1 -0
- package/dist/passthrough/weasel-ui.d.ts +434 -231
- package/dist/passthrough/weasel-ui.js +2 -1
- package/dist/primitives/index.d.ts +19 -54
- package/dist/primitives/index.js +6 -4
- package/dist/state/index.d.ts +8 -7
- package/dist/state/index.js +4 -5
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +360 -174
- package/dist/undo/index.d.ts +5 -4
- package/package.json +13 -13
- package/src/annotations/frac.test.ts +7 -0
- package/src/annotations/index.ts +1 -0
- package/src/annotations/paint.test.ts +21 -0
- package/src/annotations/paint.ts +12 -0
- package/src/annotations/store.test.ts +57 -0
- package/src/annotations/store.ts +25 -6
- package/src/annotations/toolMap.test.ts +37 -2
- package/src/annotations/toolMap.ts +13 -0
- package/src/annotations/types.ts +30 -2
- package/src/canvas/AGENTS.md +8 -10
- package/src/canvas/CameraInput.test.tsx +136 -0
- package/src/canvas/CameraInput.tsx +311 -0
- package/src/canvas/CanvasStack.tsx +42 -58
- package/src/canvas/LinkedCursor.less +16 -0
- package/src/canvas/LinkedCursor.tsx +54 -0
- package/src/canvas/Stage.test.tsx +18 -0
- package/src/canvas/Stage.tsx +42 -37
- package/src/canvas/cameraView.test.ts +60 -0
- package/src/canvas/cameraView.ts +61 -0
- package/src/canvas/index.ts +11 -2
- package/src/chrome/regions/SidebarRegion.less +197 -0
- package/src/chrome/regions/SidebarRegion.test.tsx +27 -0
- package/src/chrome/regions/SidebarRegion.tsx +7 -2
- package/src/chrome/types.ts +5 -2
- package/src/config/autoConfig.ts +2 -2
- package/src/config/builder.ts +21 -2
- package/src/config/entry.test.ts +10 -0
- package/src/config/fromConfigField.ts +1 -0
- package/src/config/index.ts +5 -0
- package/src/config/resolve.ts +11 -1
- package/src/config/rules.ts +2 -0
- package/src/config/sectionTree.test.ts +152 -0
- package/src/config/sectionTree.ts +134 -0
- package/src/config/types.ts +21 -0
- package/src/controls/ControlPanel.less +4 -0
- package/src/controls/ControlPanel.stories.tsx +55 -2
- package/src/controls/ControlPanel.test.tsx +40 -3
- package/src/controls/ControlPanel.tsx +134 -336
- package/src/controls/inDialog.test.tsx +100 -0
- package/src/controls/inDialog.tsx +38 -0
- package/src/controls/index.ts +1 -0
- package/src/index.test.ts +1 -1
- package/src/index.ts +18 -29
- package/src/instrument/SineWave.smoke.test.tsx +2 -0
- package/src/instrument/types.ts +15 -3
- package/src/lab/Lab.nebula.test.tsx +40 -0
- package/src/lab/Lab.test.tsx +3 -1
- package/src/lab/Lab.tsx +42 -19
- 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/loupe/AGENTS.md +7 -6
- package/src/loupe/LoupeGestures.tsx +14 -9
- package/src/loupe/TrialLoupe.tsx +5 -5
- package/src/loupe/index.ts +1 -0
- package/src/loupe/loupeActions.ts +6 -2
- package/src/overview/AGENTS.md +38 -0
- package/src/overview/OverviewMarks.test.tsx +100 -0
- package/src/overview/OverviewMarks.tsx +93 -0
- package/src/overview/TrialOverview.less +53 -0
- package/src/overview/TrialOverview.stories.tsx +50 -0
- package/src/overview/TrialOverview.test.tsx +116 -0
- package/src/overview/TrialOverview.tsx +374 -0
- package/src/overview/index.ts +2 -0
- package/src/passthrough/weasel-ui.ts +32 -10
- package/src/primitives/index.ts +0 -2
- package/src/specimen/Specimen.tsx +59 -56
- package/src/state/labRecords.test.ts +4 -1
- package/src/state/store.test.ts +4 -4
- package/src/state/types.ts +2 -1
- package/src/state/useTrialState.test.tsx +1 -1
- package/src/styles.less +6 -2
- package/src/theme/Interstellar.stories.tsx +8 -13
- 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/tools/labTool.ts +6 -2
- package/src/trial/Trial.annotations.test.tsx +16 -0
- package/src/trial/Trial.canvas.test.tsx +1 -1
- package/src/trial/Trial.loupe.test.tsx +73 -1
- package/src/trial/Trial.stories.tsx +2 -0
- package/src/trial/Trial.tsx +47 -9
- package/src/trial/trialOps.test.ts +4 -4
- package/dist/_dts/index-8LiKe3A-.d.ts +0 -410
- package/dist/chunk-2AYGEN57.js +0 -32
- package/dist/chunk-2AYGEN57.js.map +0 -1
- package/dist/chunk-6BUQ77ZV.js +0 -18
- package/dist/chunk-6BUQ77ZV.js.map +0 -1
- package/dist/chunk-753Y6HE6.js.map +0 -1
- package/dist/chunk-A2ACAGW6.js.map +0 -1
- package/dist/chunk-CKUKF72R.js.map +0 -1
- package/dist/chunk-D35PFTS7.js +0 -170
- package/dist/chunk-D35PFTS7.js.map +0 -1
- package/dist/chunk-EMXAIRUJ.js.map +0 -1
- package/dist/chunk-ERFMC6WU.js.map +0 -1
- package/dist/chunk-FKLT3ITC.js +0 -590
- package/dist/chunk-FKLT3ITC.js.map +0 -1
- package/dist/chunk-KJCUERQF.js.map +0 -1
- package/dist/chunk-LASCBWW7.js +0 -13456
- package/dist/chunk-LASCBWW7.js.map +0 -1
- package/dist/chunk-REOBGYMC.js.map +0 -1
- package/dist/chunk-RS3HRQWU.js.map +0 -1
- package/dist/chunk-TT5C5S6R.js +0 -368
- package/dist/chunk-TT5C5S6R.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/usePanZoom.test.ts +0 -244
- package/src/canvas/usePanZoom.ts +0 -147
- 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
|
@@ -69,6 +69,22 @@ describe('an instrument that declares annotations', () => {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it('gets only the tools it names', () => {
|
|
73
|
+
const narrow = defineInstrument<Record<string, never>, Record<string, never>>({
|
|
74
|
+
...annotating,
|
|
75
|
+
name: 'Narrow',
|
|
76
|
+
annotations: {
|
|
77
|
+
targets: () => [{ id: 'pane', ref: { current: null }, content: { w: 200, h: 100 } }],
|
|
78
|
+
tools: ['pointer', 'select'],
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
render(<Lab instruments={[narrow]} defaultInstrument="Narrow" />);
|
|
82
|
+
expect(screen.getByRole('button', { name: 'Interact' })).toBeTruthy();
|
|
83
|
+
expect(screen.getByRole('button', { name: 'Select' })).toBeTruthy();
|
|
84
|
+
expect(screen.queryByRole('button', { name: 'Freehand' })).toBeNull();
|
|
85
|
+
expect(screen.queryByRole('button', { name: 'Rectangle' })).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
72
88
|
it('puts the store in reach of its own render', () => {
|
|
73
89
|
render(<Lab instruments={[annotating]} defaultInstrument="Annotating" />);
|
|
74
90
|
expect(screen.getByTestId('pane').dataset.marks).toBe('0');
|
|
@@ -185,7 +185,7 @@ describe('LabProps.instruments', () => {
|
|
|
185
185
|
render(<Lab instruments={[instrument]} defaultInstrument="Probe" />);
|
|
186
186
|
expect([...seen]).toEqual(['grid', 'glyph']);
|
|
187
187
|
|
|
188
|
-
await userEvent.click(screen.getByRole('checkbox', { name: '
|
|
188
|
+
await userEvent.click(screen.getByRole('checkbox', { name: 'Show grid' }));
|
|
189
189
|
expect([...seen]).toEqual(['glyph']);
|
|
190
190
|
});
|
|
191
191
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from '@testing-library/react';
|
|
1
|
+
import { act, fireEvent, render, screen } from '@testing-library/react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import { defineInstrument } from '../instrument/defineInstrument';
|
|
@@ -126,3 +126,75 @@ describe('the loupe capability', () => {
|
|
|
126
126
|
expect(lens(container)).toBeNull();
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
|
+
|
|
130
|
+
describe('the loupe and the trial camera share one dispatcher', () => {
|
|
131
|
+
const frame = () => new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
132
|
+
|
|
133
|
+
function mountDrawn() {
|
|
134
|
+
const zooms: number[] = [];
|
|
135
|
+
const drawn = defineInstrument<Record<string, never>, Record<string, never>>({
|
|
136
|
+
name: 'Zoomed',
|
|
137
|
+
defaultConfig: () => ({}),
|
|
138
|
+
initialState: () => ({}),
|
|
139
|
+
render: () => null,
|
|
140
|
+
canvas: {
|
|
141
|
+
layers: [
|
|
142
|
+
{
|
|
143
|
+
id: 'main',
|
|
144
|
+
draw: (_ctx, { zoom }) => {
|
|
145
|
+
zooms.push(zoom);
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
loupe: true,
|
|
151
|
+
});
|
|
152
|
+
const r = render(<Lab instruments={[drawn]} defaultInstrument="Zoomed" />);
|
|
153
|
+
const stack = r.container.querySelector('.lk-canvas-stack');
|
|
154
|
+
if (!stack) throw new Error('no canvas stack');
|
|
155
|
+
return { ...r, stack, zooms };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const wheel = (el: Element) => {
|
|
159
|
+
const e = new WheelEvent('wheel', {
|
|
160
|
+
deltaY: -300,
|
|
161
|
+
clientX: 50,
|
|
162
|
+
clientY: 50,
|
|
163
|
+
bubbles: true,
|
|
164
|
+
cancelable: true,
|
|
165
|
+
});
|
|
166
|
+
el.dispatchEvent(e);
|
|
167
|
+
return e;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
it('zooms the camera while the lens is down', async () => {
|
|
171
|
+
const h = mountDrawn();
|
|
172
|
+
await act(async () => {
|
|
173
|
+
await frame();
|
|
174
|
+
});
|
|
175
|
+
await act(async () => {
|
|
176
|
+
wheel(h.stack);
|
|
177
|
+
await frame();
|
|
178
|
+
});
|
|
179
|
+
expect(h.zooms.at(-1)).toBeGreaterThan(1);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('gives the wheel to the lens while it is up, and leaves the camera alone', async () => {
|
|
183
|
+
const user = userEvent.setup();
|
|
184
|
+
const h = mountDrawn();
|
|
185
|
+
await user.click(screen.getByRole('button', { name: 'Loupe' }));
|
|
186
|
+
pointAt(h.stack, 60, 40);
|
|
187
|
+
expect(lens(h.container)).not.toBeNull();
|
|
188
|
+
await act(async () => {
|
|
189
|
+
await frame();
|
|
190
|
+
});
|
|
191
|
+
const before = h.zooms.at(-1);
|
|
192
|
+
let e!: WheelEvent;
|
|
193
|
+
await act(async () => {
|
|
194
|
+
e = wheel(h.stack);
|
|
195
|
+
await frame();
|
|
196
|
+
});
|
|
197
|
+
expect(e.defaultPrevented).toBe(true);
|
|
198
|
+
expect(h.zooms.at(-1)).toBe(before);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createPointerStore } from '@weasel-js/core';
|
|
1
2
|
import type { Meta, StoryObj } from '@weasel-js/forge';
|
|
2
3
|
import type { Instrument } from '../instrument/types';
|
|
3
4
|
import { LabContext, type LabContextValue } from '../lab/LabContext';
|
|
@@ -63,6 +64,7 @@ function Harness() {
|
|
|
63
64
|
setZoom: () => {},
|
|
64
65
|
activeToolId: null,
|
|
65
66
|
visibleLayers: [],
|
|
67
|
+
pointer: createPointerStore(),
|
|
66
68
|
},
|
|
67
69
|
emit: () => {},
|
|
68
70
|
})}
|
package/src/trial/Trial.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createPointerStore, PointerContextProvider, WeaselProvider } from '@weasel-js/core';
|
|
1
2
|
import { type ReactNode, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
3
|
import { useStore } from 'zustand/react';
|
|
3
4
|
import { AnnotationsContext } from '../annotations/AnnotationsContext';
|
|
@@ -5,6 +6,7 @@ import { AnnotationTargets } from '../annotations/AnnotationTargets';
|
|
|
5
6
|
import { AnnotationPreloadContext } from '../annotations/preload';
|
|
6
7
|
import { annotationsFromJSON } from '../annotations/store';
|
|
7
8
|
import type { AnnotationStorage, AnnotationTargetInfo } from '../annotations/types';
|
|
9
|
+
import { CameraScopeContext } from '../canvas/CameraInput';
|
|
8
10
|
import { CameraWheelContext, type CameraWheelSlot } from '../canvas/CameraWheelContext';
|
|
9
11
|
import { CanvasStack } from '../canvas/CanvasStack';
|
|
10
12
|
import { fitStage, Stage } from '../canvas/Stage';
|
|
@@ -24,9 +26,9 @@ import type {
|
|
|
24
26
|
} from '../instrument/types';
|
|
25
27
|
import { useJob } from '../job/useJob';
|
|
26
28
|
import { useLabContext } from '../lab/LabContext';
|
|
27
|
-
import { LayerList } from '../layers/LayerList';
|
|
28
29
|
import { TrialLoupe } from '../loupe/TrialLoupe';
|
|
29
30
|
import { resolveLoupe } from '../loupe/types';
|
|
31
|
+
import { LayerList, type LayerListItem, moveLayers } from '../passthrough/weasel-ui';
|
|
30
32
|
import { LabStoreContext, TrialIdProvider } from '../state/context';
|
|
31
33
|
import type { LabStore } from '../state/store';
|
|
32
34
|
import type { TrialRecord } from '../state/types';
|
|
@@ -134,6 +136,7 @@ function TrialRuntime({
|
|
|
134
136
|
suppress,
|
|
135
137
|
}: TrialRuntimeProps) {
|
|
136
138
|
const wheelSlot = useRef<CameraWheelSlot['current']>(null);
|
|
139
|
+
const [pointer] = useState(createPointerStore);
|
|
137
140
|
const canvasContainerRef = useRef<HTMLDivElement | null>(null);
|
|
138
141
|
const loupeHostRef = useRef<HTMLDivElement | null>(null);
|
|
139
142
|
const updateTrialState = useStore(store, (s) => s.updateTrialState);
|
|
@@ -328,6 +331,7 @@ function TrialRuntime({
|
|
|
328
331
|
},
|
|
329
332
|
activeToolId: resolvedToolId,
|
|
330
333
|
visibleLayers,
|
|
334
|
+
pointer,
|
|
331
335
|
},
|
|
332
336
|
emit: (event) => {
|
|
333
337
|
snapshotIfNeeded(event);
|
|
@@ -401,6 +405,24 @@ function TrialRuntime({
|
|
|
401
405
|
return instrument.layers.ids.map((l) => (typeof l === 'string' ? { id: l, label: l } : l));
|
|
402
406
|
}, [instrument.layers]);
|
|
403
407
|
|
|
408
|
+
// In the order the canvas draws them, with the always-on layers pinned
|
|
409
|
+
// below the rest, where a drag cannot reach past them.
|
|
410
|
+
const layerItems: LayerListItem[] = useMemo(() => {
|
|
411
|
+
const ordered = layerOrder
|
|
412
|
+
? [...layerDescriptors].sort((a, b) => layerOrder.indexOf(a.id) - layerOrder.indexOf(b.id))
|
|
413
|
+
: layerDescriptors;
|
|
414
|
+
const pinnedLast = [
|
|
415
|
+
...ordered.filter((l) => !l.alwaysOn),
|
|
416
|
+
...ordered.filter((l) => l.alwaysOn),
|
|
417
|
+
];
|
|
418
|
+
return pinnedLast.map((l) => ({
|
|
419
|
+
id: l.id,
|
|
420
|
+
label: l.label,
|
|
421
|
+
locked: l.alwaysOn === true,
|
|
422
|
+
visible: l.alwaysOn === true || layerVisibility[l.id] !== false,
|
|
423
|
+
}));
|
|
424
|
+
}, [layerDescriptors, layerOrder, layerVisibility]);
|
|
425
|
+
|
|
404
426
|
const dragDropResult = useDragDrop({
|
|
405
427
|
capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i, s) => s },
|
|
406
428
|
canvasContainerRef,
|
|
@@ -492,7 +514,12 @@ function TrialRuntime({
|
|
|
492
514
|
minZoom={stage.minZoom}
|
|
493
515
|
maxZoom={stage.maxZoom}
|
|
494
516
|
hostRef={loupeHostRef}
|
|
495
|
-
overlay={
|
|
517
|
+
overlay={
|
|
518
|
+
<>
|
|
519
|
+
{stage.overlay?.(renderCtx)}
|
|
520
|
+
{lens}
|
|
521
|
+
</>
|
|
522
|
+
}
|
|
496
523
|
>
|
|
497
524
|
{instrument.render(renderCtx)}
|
|
498
525
|
</Stage>
|
|
@@ -519,19 +546,18 @@ function TrialRuntime({
|
|
|
519
546
|
() =>
|
|
520
547
|
instrument.layers && layerDescriptors.length > 0 ? (
|
|
521
548
|
<LayerList
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
setLayerOrder(next.map((l) => l.id));
|
|
549
|
+
items={layerItems}
|
|
550
|
+
onReorder={(move) => {
|
|
551
|
+
setLayerOrder(moveLayers(layerItems, move).map((l) => l.id));
|
|
526
552
|
bus.emit('layers.reorder');
|
|
527
553
|
}}
|
|
528
|
-
|
|
554
|
+
onVisibilityChange={(lid, visible) => {
|
|
529
555
|
setLayerVisibility((prev) => ({ ...prev, [lid]: visible }));
|
|
530
556
|
bus.emit('layers.toggle');
|
|
531
557
|
}}
|
|
532
558
|
/>
|
|
533
559
|
) : null,
|
|
534
|
-
[instrument.layers, layerDescriptors,
|
|
560
|
+
[instrument.layers, layerDescriptors, layerItems, bus],
|
|
535
561
|
);
|
|
536
562
|
|
|
537
563
|
const extraChrome = useMemo<TrialContribution[]>(() => {
|
|
@@ -567,6 +593,18 @@ function TrialRuntime({
|
|
|
567
593
|
/>
|
|
568
594
|
) : null;
|
|
569
595
|
|
|
596
|
+
// One input scope for the trial's body: the camera, the loupe and an
|
|
597
|
+
// overview share its actions, its deps and its pointer. Isolated, because an
|
|
598
|
+
// actions registry holds one dispatcher and the trial beside this one has
|
|
599
|
+
// its own.
|
|
600
|
+
const scopedBody = (
|
|
601
|
+
<WeaselProvider isolate>
|
|
602
|
+
<PointerContextProvider store={pointer}>
|
|
603
|
+
<CameraScopeContext.Provider value={true}>{body}</CameraScopeContext.Provider>
|
|
604
|
+
</PointerContextProvider>
|
|
605
|
+
</WeaselProvider>
|
|
606
|
+
);
|
|
607
|
+
|
|
570
608
|
return (
|
|
571
609
|
<TrialIdProvider trialId={record.id}>
|
|
572
610
|
<AnnotationsContext.Provider value={annotationsCap ? annotations : null}>
|
|
@@ -585,7 +623,7 @@ function TrialRuntime({
|
|
|
585
623
|
activeToolId={resolvedToolId}
|
|
586
624
|
setActiveTool={setActiveTool}
|
|
587
625
|
>
|
|
588
|
-
{
|
|
626
|
+
{scopedBody}
|
|
589
627
|
{annotationOverlays}
|
|
590
628
|
</TrialChrome>
|
|
591
629
|
</CameraWheelContext.Provider>
|
|
@@ -132,7 +132,7 @@ describe('addTrial', () => {
|
|
|
132
132
|
const out = addTrial([], instruments, 'Schemed', { config: { gap: auto } });
|
|
133
133
|
expect(out[0]?.auto).toContain('gap');
|
|
134
134
|
expect(out[0]?.autoSeed).toEqual(['gap']);
|
|
135
|
-
expect((out[0]?.config as { gap: number })
|
|
135
|
+
expect((out[0]?.config as { gap: number } | undefined)?.gap).toBe(12);
|
|
136
136
|
expect(out[0]?.configSeed).not.toHaveProperty('gap');
|
|
137
137
|
});
|
|
138
138
|
|
|
@@ -141,13 +141,13 @@ describe('addTrial', () => {
|
|
|
141
141
|
const out = addTrial([], instruments, 'Counter', {
|
|
142
142
|
config: { at: when } as unknown as Partial<CounterConfig>,
|
|
143
143
|
});
|
|
144
|
-
expect((out[0]?.config as { at: unknown })
|
|
144
|
+
expect((out[0]?.config as { at: unknown } | undefined)?.at).toBe(when);
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
it('honors auto at a nested seed path', () => {
|
|
148
148
|
const out = addTrial([], instruments, 'Schemed', { config: { grid: { size: auto } } });
|
|
149
149
|
expect(out[0]?.auto).toContain('grid.size');
|
|
150
|
-
expect((out[0]?.config as { grid: { size: number } })
|
|
150
|
+
expect((out[0]?.config as { grid: { size: number } } | undefined)?.grid.size).toBe(4);
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it('leaves a trial with no auto field when nothing is unpinned', () => {
|
|
@@ -222,7 +222,7 @@ describe('resetTrial', () => {
|
|
|
222
222
|
const arr = addTrial([], instruments, 'Counter');
|
|
223
223
|
head(arr).config = { step: 99 } as CounterConfig;
|
|
224
224
|
resetTrial(arr, head(arr).id, instruments);
|
|
225
|
-
expect((arr[0]?.config as CounterConfig)
|
|
225
|
+
expect((arr[0]?.config as CounterConfig | undefined)?.step).toBe(99);
|
|
226
226
|
});
|
|
227
227
|
|
|
228
228
|
it('restores the seed the trial was opened with, not the bare defaults', () => {
|