@weasel-js/labkit 1.3.0-pre.0 → 1.4.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_dts/CanvasStackContext-DtAn_3O-.d.ts +42 -0
- package/dist/_dts/DrawCommand-B3bskUsC.d.ts +564 -0
- package/dist/_dts/{PrefsForm-BYa6cWnO.d.ts → PrefsForm-BkUJZx0A.d.ts} +4 -1
- package/dist/_dts/fitViewToBounds-dZ2UDB6e.d.ts +21 -0
- package/dist/_dts/frac-Cp3NivlC.d.ts +728 -0
- package/dist/_dts/index-BDVzvRzQ.d.ts +236 -0
- package/dist/_dts/shapeKinds-Cx_rxwsa.d.ts +87 -0
- package/dist/_dts/{DrawCommand-DBB45NfN.d.ts → types-C-gh9Ap-.d.ts} +1 -582
- package/dist/_dts/{types-AHlQxBNN.d.ts → types-D6s4b7if.d.ts} +2 -2
- package/dist/_dts/{types-1Sdxy_Pv.d.ts → types-DYMaEvM5.d.ts} +26 -1
- package/dist/_dts/{useTrialState-CsGMjhu9.d.ts → useTrialState-gmMvZqPc.d.ts} +7 -2
- package/dist/canvas/index.d.ts +43 -25
- package/dist/canvas/index.js +5 -3
- package/dist/chrome/index.d.ts +9 -6
- package/dist/chrome/index.js +7 -6
- package/dist/chunk-2AYGEN57.js +32 -0
- package/dist/chunk-2AYGEN57.js.map +1 -0
- package/dist/{chunk-RVNN4CHQ.js → chunk-3P5TTTA7.js} +16 -8
- package/dist/chunk-3P5TTTA7.js.map +1 -0
- package/dist/{chunk-NS54R52R.js → chunk-3ZELRRBV.js} +45 -40
- package/dist/chunk-3ZELRRBV.js.map +1 -0
- package/dist/{chunk-HFDVGF4X.js → chunk-4EY67BKJ.js} +32 -7
- package/dist/chunk-4EY67BKJ.js.map +1 -0
- package/dist/chunk-5TCEVMPI.js +140 -0
- package/dist/chunk-5TCEVMPI.js.map +1 -0
- package/dist/{chunk-O7NVSCLN.js → chunk-6JMU56CU.js} +133 -134
- package/dist/chunk-6JMU56CU.js.map +1 -0
- package/dist/{chunk-CPUJ3QXL.js → chunk-CQLQPQ4P.js} +2 -2
- package/dist/chunk-CQLQPQ4P.js.map +1 -0
- package/dist/chunk-CTRKTLYZ.js +33 -0
- package/dist/chunk-CTRKTLYZ.js.map +1 -0
- package/dist/chunk-EQIB2PGC.js +458 -0
- package/dist/chunk-EQIB2PGC.js.map +1 -0
- package/dist/chunk-HEORBLVT.js +7128 -0
- package/dist/chunk-HEORBLVT.js.map +1 -0
- package/dist/{chunk-V7PRSIRQ.js → chunk-JDDIZSLL.js} +4 -4
- package/dist/chunk-JDDIZSLL.js.map +1 -0
- package/dist/chunk-LITE5YFB.js +669 -0
- package/dist/chunk-LITE5YFB.js.map +1 -0
- package/dist/{chunk-Z6HJ5FEM.js → chunk-ROQDRXKW.js} +732 -315
- package/dist/chunk-ROQDRXKW.js.map +1 -0
- package/dist/chunk-SBXNQW4G.js +3 -0
- package/dist/chunk-SBXNQW4G.js.map +1 -0
- package/dist/{chunk-4TMMVIDM.js → chunk-XJ6N32QP.js} +50 -6
- package/dist/chunk-XJ6N32QP.js.map +1 -0
- package/dist/chunk-YQJNT4XQ.js +293 -0
- package/dist/chunk-YQJNT4XQ.js.map +1 -0
- package/dist/controls/index.d.ts +4 -28
- package/dist/controls/index.js +3 -3
- package/dist/dragdrop/index.d.ts +50 -5
- package/dist/dragdrop/index.js +3 -1
- package/dist/index.d.ts +660 -302
- package/dist/index.js +1793 -587
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +6 -5
- package/dist/layers/index.js +4 -2
- package/dist/loupe/index.d.ts +187 -0
- package/dist/loupe/index.js +7 -0
- package/dist/loupe/index.js.map +1 -0
- package/dist/passthrough/weasel-canvas.d.ts +3 -1
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +151 -91
- package/dist/passthrough/weasel-ui.js +3 -2
- package/dist/primitives/index.d.ts +3 -1
- package/dist/primitives/index.js +6 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +2 -2
- package/dist/styles.css +229 -703
- package/dist/surface/index.d.ts +18 -2
- package/dist/surface/index.js +2 -2
- package/dist/ui/layers/index.d.ts +8 -2
- package/dist/ui/layers/index.js +2 -4
- package/dist/undo/index.d.ts +5 -4
- package/package.json +14 -7
- package/src/annotations/AnnotationOverlay.tsx +205 -0
- package/src/annotations/AnnotationTargets.tsx +37 -0
- package/src/annotations/Annotations.less +129 -0
- package/src/annotations/Annotations.meaning.test.tsx +111 -0
- package/src/annotations/Annotations.overlay.test.tsx +142 -0
- package/src/annotations/AnnotationsContext.ts +49 -0
- package/src/annotations/ExportMenu.test.tsx +86 -0
- package/src/annotations/ExportMenu.tsx +182 -0
- package/src/annotations/MarkList.tsx +75 -0
- package/src/annotations/capture.test.ts +140 -0
- package/src/annotations/capture.ts +237 -0
- package/src/annotations/drawOne.test.ts +56 -0
- package/src/annotations/drawOne.ts +47 -0
- package/src/annotations/frac.test.ts +59 -0
- package/src/annotations/frac.ts +66 -0
- package/src/annotations/history.test.ts +82 -0
- package/src/annotations/history.ts +118 -0
- package/src/annotations/index.ts +37 -0
- package/src/annotations/paint.test.ts +125 -0
- package/src/annotations/paint.ts +130 -0
- package/src/annotations/staleness.test.ts +56 -0
- package/src/annotations/staleness.ts +41 -0
- package/src/annotations/store.test.ts +175 -0
- package/src/annotations/store.ts +317 -0
- package/src/annotations/svgNodes.test.ts +77 -0
- package/src/annotations/svgNodes.ts +74 -0
- package/src/annotations/toolMap.test.ts +36 -0
- package/src/annotations/toolMap.ts +54 -0
- package/src/annotations/types.ts +222 -0
- package/src/annotations/view.test.ts +37 -0
- package/src/annotations/view.ts +40 -0
- package/src/canvas/AGENTS.md +55 -5
- package/src/canvas/CanvasStack.test.tsx +27 -1
- package/src/canvas/CanvasStack.tsx +29 -4
- package/src/canvas/CanvasStackContext.ts +23 -4
- package/src/canvas/camera.test.ts +78 -0
- package/src/canvas/camera.ts +62 -0
- package/src/canvas/canvasCoords.test.ts +26 -0
- package/src/canvas/canvasCoords.ts +20 -7
- package/src/canvas/index.ts +5 -1
- package/src/canvas/useLayerScheduler.ts +7 -3
- package/src/canvas/usePanZoom.test.ts +36 -5
- package/src/canvas/usePanZoom.ts +20 -15
- package/src/canvas/worldSpec.test.ts +63 -0
- package/src/canvas/worldSpec.ts +48 -0
- package/src/chrome/builtins.test.ts +31 -1
- package/src/chrome/builtins.tsx +63 -19
- package/src/chrome/regions/PaletteRegion.test.tsx +22 -1
- package/src/chrome/regions/PaletteRegion.tsx +4 -0
- package/src/chrome/regions/SidebarRegion.tsx +37 -13
- package/src/chrome/regions/ToolbarRegion.tsx +2 -1
- package/src/chrome/regions/ViewportRegion.tsx +9 -1
- package/src/chrome/regions/regions.test.tsx +6 -1
- package/src/chrome/types.ts +20 -0
- package/src/config/builder.test.ts +7 -1
- package/src/config/builder.ts +1 -5
- package/src/config/resolve.test.ts +1 -2
- package/src/controls/ControlPanel.stories.tsx +41 -2
- package/src/controls/ControlPanel.test.tsx +69 -9
- package/src/controls/ControlPanel.tsx +106 -25
- package/src/dragdrop/DragDropRuntime.tsx +8 -2
- package/src/dragdrop/dragDrop.test.tsx +29 -2
- package/src/dragdrop/index.ts +11 -0
- package/src/index.test.ts +22 -0
- package/src/index.ts +136 -4
- package/src/instrument/SineWave.smoke.test.tsx +1 -0
- package/src/instrument/types.ts +24 -1
- package/src/lab/Lab.surface.test.tsx +162 -0
- package/src/lab/Lab.tsx +102 -17
- package/src/lab/Lab.undock.test.tsx +85 -0
- package/src/lab/LabFullChrome.stories.tsx +20 -0
- package/src/lab/LabShell.less +11 -0
- package/src/lab/Workspace.less +73 -0
- package/src/lab/Workspace.surface.test.tsx +2 -0
- package/src/lab/Workspace.tsx +97 -8
- package/src/lab/index.ts +1 -1
- package/src/lab/panelHost.ts +37 -0
- package/src/layers/AGENTS.md +1 -1
- package/src/layers/LayerList.tsx +1 -1
- package/src/loupe/AGENTS.md +49 -0
- package/src/loupe/CanvasLoupe.tsx +83 -0
- package/src/loupe/DomLoupe.tsx +62 -0
- package/src/loupe/Loupe.less +38 -0
- package/src/loupe/LoupeBubble.tsx +32 -0
- package/src/loupe/TrialLoupe.tsx +93 -0
- package/src/loupe/canvasLens.test.ts +205 -0
- package/src/loupe/canvasLens.ts +141 -0
- package/src/loupe/index.ts +19 -0
- package/src/loupe/types.test.ts +29 -0
- package/src/loupe/types.ts +90 -0
- package/src/loupe/useHostSize.ts +30 -0
- package/src/loupe/useLoupe.test.tsx +197 -0
- package/src/loupe/useLoupe.ts +189 -0
- package/src/passthrough/weasel-ui.ts +25 -17
- package/src/primitives/FloatingPanel.test.tsx +76 -1
- package/src/primitives/FloatingPanel.tsx +19 -3
- package/src/primitives/ScaleIndicator.test.tsx +7 -2
- package/src/primitives/Sidebar.less +18 -0
- package/src/primitives/Toolbar.less +8 -0
- package/src/primitives/Toolbar.tsx +5 -0
- package/src/primitives/useRovingTabIndex.test.ts +21 -0
- package/src/primitives/useRovingTabIndex.ts +26 -17
- package/src/state/document.test.ts +23 -0
- package/src/state/document.ts +14 -2
- package/src/state/index.ts +2 -0
- package/src/state/store.test.ts +24 -0
- package/src/state/store.ts +35 -1
- package/src/state/types.ts +11 -0
- package/src/state/undock.test.ts +40 -0
- package/src/state/undock.ts +39 -0
- package/src/styles.less +2 -4
- package/src/surface/SurfaceContext.ts +4 -0
- package/src/surface/index.ts +8 -3
- package/src/surface/useSurfaceTile.test.tsx +2 -0
- package/src/surface/useSurfaceTile.ts +7 -1
- package/src/surface/useTiledSurface.test.tsx +58 -0
- package/src/surface/useTiledSurface.ts +38 -3
- package/src/theme/Interstellar.stories.tsx +1 -1
- package/src/trial/Trial.annotations.persist.test.tsx +166 -0
- package/src/trial/Trial.annotations.test.tsx +121 -0
- package/src/trial/Trial.canvas.test.tsx +76 -1
- package/src/trial/Trial.config.test.tsx +3 -8
- package/src/trial/Trial.less +19 -0
- package/src/trial/Trial.loupe.test.tsx +128 -0
- package/src/trial/Trial.stories.tsx +1 -0
- package/src/trial/Trial.test.tsx +12 -0
- package/src/trial/Trial.trialId.test.tsx +36 -0
- package/src/trial/Trial.tsx +215 -40
- package/src/trial/TrialChrome.tsx +35 -0
- package/src/trial/UndockedSections.tsx +65 -0
- package/src/trial/trialOps.test.ts +21 -0
- package/src/trial/trialOps.ts +5 -3
- package/src/ui/layers/index.ts +5 -1
- package/dist/_dts/index-DgcNdEdh.d.ts +0 -314
- package/dist/chunk-4TMMVIDM.js.map +0 -1
- package/dist/chunk-CPUJ3QXL.js.map +0 -1
- package/dist/chunk-HFDVGF4X.js.map +0 -1
- package/dist/chunk-NS54R52R.js.map +0 -1
- package/dist/chunk-O2BULFHX.js +0 -152
- package/dist/chunk-O2BULFHX.js.map +0 -1
- package/dist/chunk-O7NVSCLN.js.map +0 -1
- package/dist/chunk-PO6L3NSH.js +0 -366
- package/dist/chunk-PO6L3NSH.js.map +0 -1
- package/dist/chunk-PQJ5B2U2.js +0 -27
- package/dist/chunk-PQJ5B2U2.js.map +0 -1
- package/dist/chunk-RVNN4CHQ.js.map +0 -1
- package/dist/chunk-TLGRYALP.js +0 -6151
- package/dist/chunk-TLGRYALP.js.map +0 -1
- package/dist/chunk-V7PRSIRQ.js.map +0 -1
- package/dist/chunk-Z6HJ5FEM.js.map +0 -1
- package/dist/chunk-ZZAYVX4X.js +0 -505
- package/dist/chunk-ZZAYVX4X.js.map +0 -1
- package/src/primitives/DragHandleGlyph.tsx +0 -29
- package/src/ui/format.test.ts +0 -33
- package/src/ui/format.ts +0 -30
- package/src/ui/layers/LayerStack.less +0 -143
- package/src/ui/layers/LayerStack.stories.tsx +0 -45
- package/src/ui/layers/LayerStack.test.tsx +0 -170
- package/src/ui/layers/LayerStack.tsx +0 -208
- package/src/ui/properties/CheckboxRow.stories.tsx +0 -27
- package/src/ui/properties/ColorRow.stories.tsx +0 -106
- package/src/ui/properties/CurveField.less +0 -48
- package/src/ui/properties/CurveField.stories.tsx +0 -20
- package/src/ui/properties/CurveField.test.tsx +0 -127
- package/src/ui/properties/CurveField.tsx +0 -184
- package/src/ui/properties/EffectCard.tsx +0 -324
- package/src/ui/properties/Gallery.stories.tsx +0 -178
- package/src/ui/properties/NumberRow.stories.tsx +0 -65
- package/src/ui/properties/PropertyGroup.less +0 -50
- package/src/ui/properties/PropertyGroup.stories.tsx +0 -34
- package/src/ui/properties/PropertyGroup.test.tsx +0 -33
- package/src/ui/properties/PropertyGroup.tsx +0 -45
- package/src/ui/properties/PropertyList.stories.tsx +0 -83
- package/src/ui/properties/PropertyPanel.less +0 -606
- package/src/ui/properties/PropertyPanel.stories.tsx +0 -190
- package/src/ui/properties/PropertyPanel.test.tsx +0 -193
- package/src/ui/properties/PropertyPanel.tsx +0 -469
- package/src/ui/properties/PropertyRow.stories.tsx +0 -53
- package/src/ui/properties/SelectRow.stories.tsx +0 -58
- package/src/ui/properties/SliderRow.stories.tsx +0 -74
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +0 -398
- package/src/ui/properties/TextRow.stories.tsx +0 -62
- package/src/ui/properties/ToggleRow.stories.tsx +0 -57
- package/src/ui/properties/index.ts +0 -39
- package/src/ui/properties/storyLayouts.tsx +0 -36
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import { isBuiltinToolPref } from '@weasel-js/core';
|
|
2
|
-
import type { PrefLeaf } from '@weasel-js/ui';
|
|
3
|
-
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
|
4
|
-
import { fromConfigFields } from '../config/fromConfigField';
|
|
5
|
-
import type { ControlRenderer, ResolvedConfig } from '../config/types';
|
|
6
|
-
import { isLeafVisible } from '../config/visible';
|
|
7
|
-
import { PropertyGroup } from '../ui/properties/PropertyGroup';
|
|
8
2
|
import {
|
|
9
3
|
CheckboxRow,
|
|
10
4
|
ColorRow,
|
|
11
5
|
NumberRow,
|
|
6
|
+
type PrefLeaf,
|
|
7
|
+
PropertyGroup,
|
|
12
8
|
PropertyList,
|
|
13
9
|
PropertyRow,
|
|
10
|
+
type PropertyRowLayout,
|
|
14
11
|
SelectRow,
|
|
12
|
+
SliderRow,
|
|
15
13
|
TextRow,
|
|
16
14
|
ToggleRow,
|
|
17
|
-
|
|
18
|
-
} from '
|
|
15
|
+
} from '@weasel-js/ui';
|
|
16
|
+
import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
|
17
|
+
import { fromConfigFields } from '../config/fromConfigField';
|
|
18
|
+
import type { ControlRenderer, ResolvedConfig } from '../config/types';
|
|
19
|
+
import { isLeafVisible } from '../config/visible';
|
|
19
20
|
import type { ConfigField } from './types';
|
|
20
21
|
|
|
22
|
+
/** How a panel packs its rows into the two-column property grid.
|
|
23
|
+
* - `'auto'`: narrow controls (numbers, checkboxes, dropdowns, colours) sit
|
|
24
|
+
* two per row; the ones that need the width — text, sliders, segmented
|
|
25
|
+
* toggles — span it.
|
|
26
|
+
* - `'pairs'`: every row pairs. A control that needs the full width says so
|
|
27
|
+
* itself, with `<PropertyRow span>`.
|
|
28
|
+
* - `'one-up'`: one control per row, colours excepted.
|
|
29
|
+
*/
|
|
30
|
+
export type ControlPack = 'auto' | 'pairs' | 'one-up';
|
|
31
|
+
|
|
21
32
|
export interface ControlPanelProps<TC extends Record<string, unknown>> {
|
|
22
33
|
/** The instrument's resolved config schema. */
|
|
23
34
|
schema?: ResolvedConfig;
|
|
@@ -31,6 +42,11 @@ export interface ControlPanelProps<TC extends Record<string, unknown>> {
|
|
|
31
42
|
* the row; entries override the built-in rows on collision.
|
|
32
43
|
*/
|
|
33
44
|
renderers?: Record<string, ControlRenderer>;
|
|
45
|
+
/** How rows pack into the two-column grid. Defaults to `'pairs'`. */
|
|
46
|
+
pack?: ControlPack;
|
|
47
|
+
/** Where a row's label sits relative to its control. Defaults to `'block'`
|
|
48
|
+
* — above it, which is what leaves a paired row room for its value. */
|
|
49
|
+
layout?: PropertyRowLayout;
|
|
34
50
|
/** Draw leaves marked `hidden`. */
|
|
35
51
|
showHidden?: boolean;
|
|
36
52
|
className?: string;
|
|
@@ -45,13 +61,12 @@ export function ControlPanel<TC extends Record<string, unknown>>({
|
|
|
45
61
|
config,
|
|
46
62
|
setConfig,
|
|
47
63
|
renderers,
|
|
64
|
+
pack = 'pairs',
|
|
65
|
+
layout = 'block',
|
|
48
66
|
showHidden = false,
|
|
49
67
|
className,
|
|
50
68
|
}: ControlPanelProps<TC>) {
|
|
51
|
-
const resolved = useMemo(
|
|
52
|
-
() => schema ?? fromConfigFields(fields ?? []),
|
|
53
|
-
[schema, fields],
|
|
54
|
-
);
|
|
69
|
+
const resolved = useMemo(() => schema ?? fromConfigFields(fields ?? []), [schema, fields]);
|
|
55
70
|
|
|
56
71
|
const paths = Object.keys(resolved.group.children);
|
|
57
72
|
const sectioned = new Set(resolved.sections.flatMap((s) => s.paths));
|
|
@@ -65,15 +80,21 @@ export function ControlPanel<TC extends Record<string, unknown>>({
|
|
|
65
80
|
config={config}
|
|
66
81
|
setConfig={setConfig}
|
|
67
82
|
renderers={renderers}
|
|
83
|
+
pack={pack}
|
|
84
|
+
layout={layout}
|
|
68
85
|
showHidden={showHidden}
|
|
69
86
|
/>
|
|
70
87
|
);
|
|
71
88
|
|
|
89
|
+
const gridPack = pack === 'one-up' ? 'auto-color' : 'pairs';
|
|
72
90
|
return (
|
|
73
|
-
<PropertyList
|
|
91
|
+
<PropertyList
|
|
92
|
+
pack={gridPack}
|
|
93
|
+
className={className ? `lk-control-panel ${className}` : 'lk-control-panel'}
|
|
94
|
+
>
|
|
74
95
|
{loose.map(row)}
|
|
75
96
|
{resolved.sections.map((section) => (
|
|
76
|
-
<PropertyGroup key={section.label} title={section.label}>
|
|
97
|
+
<PropertyGroup key={section.label} title={section.label} pack={gridPack}>
|
|
77
98
|
{section.paths.map(row)}
|
|
78
99
|
</PropertyGroup>
|
|
79
100
|
))}
|
|
@@ -87,6 +108,8 @@ interface ControlRowProps<TC extends Record<string, unknown>> {
|
|
|
87
108
|
config: TC;
|
|
88
109
|
setConfig: (key: keyof TC, value: unknown) => void;
|
|
89
110
|
renderers?: Record<string, ControlRenderer>;
|
|
111
|
+
pack: ControlPack;
|
|
112
|
+
layout: PropertyRowLayout;
|
|
90
113
|
showHidden: boolean;
|
|
91
114
|
}
|
|
92
115
|
|
|
@@ -102,6 +125,8 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
102
125
|
config,
|
|
103
126
|
setConfig,
|
|
104
127
|
renderers,
|
|
128
|
+
pack,
|
|
129
|
+
layout,
|
|
105
130
|
showHidden,
|
|
106
131
|
}: ControlRowProps<TC>) {
|
|
107
132
|
const leaf = resolved.group.children[path] as PrefLeaf | undefined;
|
|
@@ -114,14 +139,20 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
114
139
|
|
|
115
140
|
// Most specific wins, and within a tier the lab's entry beats the
|
|
116
141
|
// instrument's: controls[path] -> node .render -> controls[kind] -> built-in.
|
|
117
|
-
const custom =
|
|
118
|
-
|
|
142
|
+
const custom = renderers?.[path] ?? resolved.renderers[path] ?? renderers?.[leaf.kind];
|
|
143
|
+
// A custom row places itself like any other; one that needs the full width
|
|
144
|
+
// says so with `<PropertyRow span>`, which is the same opt-out a built-in has.
|
|
119
145
|
if (custom) return custom({ path, pref: leaf, value, setValue: write });
|
|
120
146
|
|
|
121
147
|
const label = leaf.name;
|
|
148
|
+
const description = leaf.description;
|
|
122
149
|
const read = <T,>(): T => value as T;
|
|
150
|
+
// `auto` gives the whole width to the controls that read badly at half of a
|
|
151
|
+
// sidebar's: free text, a slider track, a segmented toggle. `pairs` doesn't.
|
|
152
|
+
const wide = pack === 'auto';
|
|
123
153
|
|
|
124
|
-
if (!isBuiltinToolPref(leaf))
|
|
154
|
+
if (!isBuiltinToolPref(leaf))
|
|
155
|
+
return <UnwiredRow label={label} kind={leaf.kind} description={description} />;
|
|
125
156
|
|
|
126
157
|
switch (leaf.kind) {
|
|
127
158
|
case 'number': {
|
|
@@ -137,6 +168,9 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
137
168
|
max={max}
|
|
138
169
|
step={step}
|
|
139
170
|
onChange={write}
|
|
171
|
+
layout={layout}
|
|
172
|
+
span={wide}
|
|
173
|
+
description={description}
|
|
140
174
|
/>
|
|
141
175
|
);
|
|
142
176
|
}
|
|
@@ -153,28 +187,58 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
153
187
|
max={max}
|
|
154
188
|
step={step}
|
|
155
189
|
onChange={(n) => write(Math.min(hi, Math.max(lo, n)))}
|
|
190
|
+
layout={layout}
|
|
191
|
+
description={description}
|
|
156
192
|
/>
|
|
157
193
|
);
|
|
158
194
|
}
|
|
159
195
|
case 'boolean':
|
|
160
|
-
return
|
|
196
|
+
return (
|
|
197
|
+
<CheckboxRow
|
|
198
|
+
label={label}
|
|
199
|
+
value={read<boolean>()}
|
|
200
|
+
onChange={write}
|
|
201
|
+
description={description}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
161
204
|
case 'enum': {
|
|
162
205
|
const options = extra<readonly { value: string; label: string }[]>(leaf, 'options') ?? [];
|
|
163
|
-
const
|
|
206
|
+
const segmented = extra<string>(leaf, 'control') === 'radio';
|
|
207
|
+
const Row = segmented ? ToggleRow : SelectRow;
|
|
164
208
|
return (
|
|
165
|
-
<Row
|
|
209
|
+
<Row
|
|
210
|
+
label={label}
|
|
211
|
+
value={read<string>()}
|
|
212
|
+
options={options}
|
|
213
|
+
onChange={write}
|
|
214
|
+
layout={layout}
|
|
215
|
+
span={segmented && wide}
|
|
216
|
+
description={description}
|
|
217
|
+
/>
|
|
166
218
|
);
|
|
167
219
|
}
|
|
168
220
|
case 'string':
|
|
169
|
-
return
|
|
221
|
+
return (
|
|
222
|
+
<DebouncedTextRow
|
|
223
|
+
leaf={leaf}
|
|
224
|
+
label={label}
|
|
225
|
+
value={read<string>()}
|
|
226
|
+
write={write}
|
|
227
|
+
layout={layout}
|
|
228
|
+
span={wide}
|
|
229
|
+
description={description}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
170
232
|
case 'color':
|
|
171
|
-
return
|
|
233
|
+
return (
|
|
234
|
+
<ColorRow label={label} value={read<string>()} onChange={write} description={description} />
|
|
235
|
+
);
|
|
172
236
|
case 'paint':
|
|
173
237
|
case 'object':
|
|
174
238
|
// Declined: a hex swatch would write a solid over a gradient, and a flat
|
|
175
239
|
// row would write one field into a half-built object. Override with
|
|
176
240
|
// `render` to edit either.
|
|
177
|
-
return <UnwiredRow label={label} kind={leaf.kind} />;
|
|
241
|
+
return <UnwiredRow label={label} kind={leaf.kind} description={description} />;
|
|
178
242
|
default: {
|
|
179
243
|
// Not reachable while every built-in kind has an arm — and a new kind
|
|
180
244
|
// that lacks one is a compile error here, never a blank row.
|
|
@@ -188,9 +252,17 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
188
252
|
|
|
189
253
|
/** A leaf this panel has no control for is named rather than dropped: a silent
|
|
190
254
|
* gap reads as "this control does not exist". */
|
|
191
|
-
function UnwiredRow({
|
|
255
|
+
function UnwiredRow({
|
|
256
|
+
label,
|
|
257
|
+
kind,
|
|
258
|
+
description,
|
|
259
|
+
}: {
|
|
260
|
+
label: string;
|
|
261
|
+
kind: string;
|
|
262
|
+
description?: string;
|
|
263
|
+
}) {
|
|
192
264
|
return (
|
|
193
|
-
<PropertyRow label={label}>
|
|
265
|
+
<PropertyRow label={label} description={description}>
|
|
194
266
|
<span className="lk-control-panel__unknown">no control for “{kind}”</span>
|
|
195
267
|
</PropertyRow>
|
|
196
268
|
);
|
|
@@ -204,11 +276,17 @@ function DebouncedTextRow({
|
|
|
204
276
|
label,
|
|
205
277
|
value,
|
|
206
278
|
write,
|
|
279
|
+
layout,
|
|
280
|
+
span,
|
|
281
|
+
description,
|
|
207
282
|
}: {
|
|
208
283
|
leaf: PrefLeaf;
|
|
209
284
|
label: string;
|
|
210
285
|
value: string;
|
|
211
286
|
write: (value: unknown) => void;
|
|
287
|
+
layout?: PropertyRowLayout;
|
|
288
|
+
span?: boolean;
|
|
289
|
+
description?: string;
|
|
212
290
|
}) {
|
|
213
291
|
const debounceMs = extra<number>(leaf, 'debounceMs') ?? 150;
|
|
214
292
|
const [local, setLocal] = useState(value);
|
|
@@ -232,6 +310,9 @@ function DebouncedTextRow({
|
|
|
232
310
|
return (
|
|
233
311
|
<TextRow
|
|
234
312
|
label={label}
|
|
313
|
+
layout={layout}
|
|
314
|
+
span={span}
|
|
315
|
+
description={description}
|
|
235
316
|
value={local}
|
|
236
317
|
placeholder={extra<string>(leaf, 'placeholder')}
|
|
237
318
|
maxLength={extra<number>(leaf, 'maxLength')}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useVisibleRaf } from '@weasel-js/core';
|
|
2
2
|
import { type RefObject, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { screenToWorld } from '../canvas/canvasCoords';
|
|
4
|
+
import { resolveFrame, type WorldSpec } from '../canvas/worldSpec';
|
|
4
5
|
import type {
|
|
5
6
|
DragDropCapability,
|
|
6
7
|
DragFeedback,
|
|
@@ -20,6 +21,9 @@ export interface UseDragDropArgs<TS, TC> {
|
|
|
20
21
|
capability: DragDropCapability<TS, TC>;
|
|
21
22
|
canvasContainerRef: RefObject<HTMLElement | null>;
|
|
22
23
|
view: ViewTransform;
|
|
24
|
+
/** The instrument's coordinate system, resolved against the container each
|
|
25
|
+
* time a screen point is converted — the container is what knows its size. */
|
|
26
|
+
worldSpec?: WorldSpec;
|
|
23
27
|
state: TS;
|
|
24
28
|
config: TC;
|
|
25
29
|
setState: (next: TS | ((prev: TS) => TS)) => void;
|
|
@@ -39,6 +43,7 @@ export function useDragDrop<TS, TC>({
|
|
|
39
43
|
config,
|
|
40
44
|
setState,
|
|
41
45
|
emit,
|
|
46
|
+
worldSpec,
|
|
42
47
|
}: UseDragDropArgs<TS, TC>): UseDragDropResult {
|
|
43
48
|
const [drag, setDrag] = useState<DragState | null>(null);
|
|
44
49
|
const dragRef = useRef<DragState | null>(null);
|
|
@@ -67,9 +72,10 @@ export function useDragDrop<TS, TC>({
|
|
|
67
72
|
const el = canvasContainerRef.current;
|
|
68
73
|
if (!el) return null;
|
|
69
74
|
const r = el.getBoundingClientRect();
|
|
70
|
-
|
|
75
|
+
const frame = resolveFrame(worldSpec, { width: r.width, height: r.height });
|
|
76
|
+
return screenToWorld({ x: screenPos.x - r.left, y: screenPos.y - r.top }, view, frame);
|
|
71
77
|
},
|
|
72
|
-
[canvasContainerRef, view],
|
|
78
|
+
[canvasContainerRef, view, worldSpec],
|
|
73
79
|
);
|
|
74
80
|
|
|
75
81
|
const frameLoop = useVisibleRaf(() => {
|
|
@@ -41,9 +41,19 @@ function StateProbe({ onState }: { onState: (state: DropState) => void }) {
|
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const centredInstrument = defineInstrument<DropState, Record<string, never>>({
|
|
45
|
+
...testInstrument,
|
|
46
|
+
name: 'CentredDrop',
|
|
47
|
+
canvas: {
|
|
48
|
+
worldSpec: { origin: { x: 0.5, y: 0.5 }, yAxis: 'up' },
|
|
49
|
+
initialView: { zoom: 1, pan: { x: 0, y: 0 } },
|
|
50
|
+
layers: [{ id: 'main', draw: () => undefined }],
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function renderLab(probe?: (state: DropState) => void, instrument = testInstrument) {
|
|
45
55
|
return render(
|
|
46
|
-
<Lab instruments={[
|
|
56
|
+
<Lab instruments={[instrument]} defaultInstrument={instrument.name}>
|
|
47
57
|
{probe ? <StateProbe onState={probe} /> : null}
|
|
48
58
|
</Lab>,
|
|
49
59
|
);
|
|
@@ -139,4 +149,21 @@ describe('DragDrop integration', () => {
|
|
|
139
149
|
windowPointerUp(250, 250);
|
|
140
150
|
expect(document.querySelector('.lk-drag-ghost')).toBeNull();
|
|
141
151
|
});
|
|
152
|
+
|
|
153
|
+
it('drops through the instrument world spec, not the canvas top-left', () => {
|
|
154
|
+
let latest: DropState = { items: [] };
|
|
155
|
+
renderLab((s) => {
|
|
156
|
+
latest = s;
|
|
157
|
+
}, centredInstrument);
|
|
158
|
+
const palette = screen.getByRole('button', { name: 'Item A' });
|
|
159
|
+
|
|
160
|
+
act(() => {
|
|
161
|
+
pointerDown(palette, 0, 0);
|
|
162
|
+
});
|
|
163
|
+
// The host spans (100,100)-(500,500), so its centre is at (300,300).
|
|
164
|
+
// Dropping 50px right of centre and 50px above it, at zoom 1, y up.
|
|
165
|
+
windowPointerUp(350, 250);
|
|
166
|
+
|
|
167
|
+
expect(latest.items[0]).toMatchObject({ x: 50, y: 50 });
|
|
168
|
+
});
|
|
142
169
|
});
|
package/src/dragdrop/index.ts
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
1
|
export type { DragDropCapability, DragFeedback, PaletteItem } from '../instrument/types';
|
|
2
|
+
export type {
|
|
3
|
+
DragOverlayProps,
|
|
4
|
+
DragState,
|
|
5
|
+
UseDragDropArgs,
|
|
6
|
+
UseDragDropResult,
|
|
7
|
+
} from './DragDropRuntime';
|
|
8
|
+
export { DragOverlay, useDragDrop } from './DragDropRuntime';
|
|
9
|
+
export type { DragGhostProps } from './DragGhost';
|
|
10
|
+
export { DragGhost } from './DragGhost';
|
|
11
|
+
export type { PaletteProps } from './Palette';
|
|
12
|
+
export { Palette } from './Palette';
|
package/src/index.test.ts
CHANGED
|
@@ -60,11 +60,33 @@ describe('surface, job and orbit entry points', () => {
|
|
|
60
60
|
expect(typeof kit.useSurfaceOptional).toBe('function');
|
|
61
61
|
expect(typeof kit.toDeviceRect).toBe('function');
|
|
62
62
|
expect(typeof kit.composeRects).toBe('function');
|
|
63
|
+
expect(typeof kit.createAnnotationStore).toBe('function');
|
|
64
|
+
expect(typeof kit.createAnnotationScene).toBe('function');
|
|
65
|
+
expect(typeof kit.annotationsFromJSON).toBe('function');
|
|
66
|
+
expect(typeof kit.seenFrom).toBe('function');
|
|
67
|
+
expect(typeof kit.fracToWorld).toBe('function');
|
|
63
68
|
expect(typeof kit.useOrbit).toBe('function');
|
|
64
69
|
expect(typeof kit.orbitAfterDrag).toBe('function');
|
|
65
70
|
expect(typeof kit.useJob).toBe('function');
|
|
66
71
|
expect(typeof kit.as2DView).toBe('function');
|
|
67
72
|
expect(kit.SurfaceContext).toBeDefined();
|
|
73
|
+
expect(kit.SurfaceCanvasContext).toBeDefined();
|
|
74
|
+
expect(typeof kit.useSurfaceCanvas).toBe('function');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('the annotations capability', () => {
|
|
79
|
+
it('reaches a host through the package root', async () => {
|
|
80
|
+
const kit = await import('./index');
|
|
81
|
+
expect(typeof kit.useAnnotations).toBe('function');
|
|
82
|
+
expect(typeof kit.useAnnotationsOptional).toBe('function');
|
|
83
|
+
expect(typeof kit.AnnotationTargets).toBe('function');
|
|
84
|
+
expect(typeof kit.AnnotationOverlay).toBe('function');
|
|
85
|
+
expect(typeof kit.markCommands).toBe('function');
|
|
86
|
+
expect(typeof kit.annotationToolInfo).toBe('function');
|
|
87
|
+
expect(typeof kit.fitView).toBe('function');
|
|
88
|
+
expect(kit.ANNOTATION_TOOLS.map((t) => t.id)).toContain('arrow');
|
|
89
|
+
expect(typeof kit.MarkList).toBe('function');
|
|
68
90
|
expect(kit.DEFAULT_VIEW).toEqual({ zoom: 1, pan: { x: 0, y: 0 } });
|
|
69
91
|
});
|
|
70
92
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,136 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// The property-panel family and LayerStack live in `@weasel-js/ui` now; labkit
|
|
2
|
+
// re-exports them so existing `@weasel-js/labkit` imports keep resolving. Named,
|
|
3
|
+
// not `export *` — a star re-export of an external package emits no binding in
|
|
4
|
+
// the bundle.
|
|
5
|
+
export type {
|
|
6
|
+
CheckboxRowProps,
|
|
7
|
+
ColorRowProps,
|
|
8
|
+
CurveFieldProps,
|
|
9
|
+
CurveMark,
|
|
10
|
+
EffectCardListItem,
|
|
11
|
+
EffectCardListProps,
|
|
12
|
+
EffectCardProps,
|
|
13
|
+
LayerStackItem,
|
|
14
|
+
LayerStackProps,
|
|
15
|
+
NumberRowProps,
|
|
16
|
+
PropertyGroupProps,
|
|
17
|
+
PropertyListPack,
|
|
18
|
+
PropertyListProps,
|
|
19
|
+
PropertyOption,
|
|
20
|
+
PropertyPanelProps,
|
|
21
|
+
PropertyRowLayout,
|
|
22
|
+
PropertyRowProps,
|
|
23
|
+
PropertyRowVariant,
|
|
24
|
+
PropertySpanProps,
|
|
25
|
+
SelectRowProps,
|
|
26
|
+
SliderRowProps,
|
|
27
|
+
SubpanelProps,
|
|
28
|
+
TextRowProps,
|
|
29
|
+
ToggleRowProps,
|
|
30
|
+
} from '@weasel-js/ui';
|
|
31
|
+
export {
|
|
32
|
+
CheckboxRow,
|
|
33
|
+
ColorRow,
|
|
34
|
+
CurveField,
|
|
35
|
+
EffectCard,
|
|
36
|
+
EffectCardList,
|
|
37
|
+
LayerStack,
|
|
38
|
+
NumberRow,
|
|
39
|
+
PropertyGroup,
|
|
40
|
+
PropertyList,
|
|
41
|
+
PropertyPanel,
|
|
42
|
+
PropertyRow,
|
|
43
|
+
PropertySpan,
|
|
44
|
+
SelectRow,
|
|
45
|
+
SliderRow,
|
|
46
|
+
Subpanel,
|
|
47
|
+
TextRow,
|
|
48
|
+
ToggleRow,
|
|
49
|
+
} from '@weasel-js/ui';
|
|
50
|
+
export type {
|
|
51
|
+
Annotation,
|
|
52
|
+
AnnotationData,
|
|
53
|
+
AnnotationInit,
|
|
54
|
+
AnnotationKind,
|
|
55
|
+
AnnotationMeaning,
|
|
56
|
+
AnnotationPatch,
|
|
57
|
+
AnnotationQuery,
|
|
58
|
+
AnnotationStatus,
|
|
59
|
+
AnnotationStoreOptions,
|
|
60
|
+
AnnotationsApi,
|
|
61
|
+
AnnotationsCapability,
|
|
62
|
+
AnnotationTarget,
|
|
63
|
+
AnnotationTargetInfo,
|
|
64
|
+
CaptureDeps,
|
|
65
|
+
CaptureOptions,
|
|
66
|
+
CaptureResult,
|
|
67
|
+
CaptureSource,
|
|
68
|
+
FracPoint,
|
|
69
|
+
FracRect,
|
|
70
|
+
MarkScene,
|
|
71
|
+
SerializedAnnotations,
|
|
72
|
+
WorldRect,
|
|
73
|
+
} from './annotations';
|
|
74
|
+
export {
|
|
75
|
+
annotationsFromJSON,
|
|
76
|
+
createAnnotationScene,
|
|
77
|
+
createAnnotationStore,
|
|
78
|
+
fracContains,
|
|
79
|
+
fracIntersects,
|
|
80
|
+
fracToWorld,
|
|
81
|
+
isStale,
|
|
82
|
+
roundFrac,
|
|
83
|
+
seenFrom,
|
|
84
|
+
worldToFrac,
|
|
85
|
+
} from './annotations';
|
|
86
|
+
export type { AnnotationOverlayProps } from './annotations/AnnotationOverlay';
|
|
87
|
+
export { AnnotationOverlay } from './annotations/AnnotationOverlay';
|
|
88
|
+
export {
|
|
89
|
+
AnnotationsContext,
|
|
90
|
+
useAnnotations,
|
|
91
|
+
useAnnotationsOptional,
|
|
92
|
+
} from './annotations/AnnotationsContext';
|
|
93
|
+
export type { AnnotationTargetsProps } from './annotations/AnnotationTargets';
|
|
94
|
+
export { AnnotationTargets } from './annotations/AnnotationTargets';
|
|
95
|
+
export type { CaptureArgs, CapturePlan, ComposeSvgArgs } from './annotations/capture';
|
|
96
|
+
export { capturePlan, captureTarget, composeCaptureSvg } from './annotations/capture';
|
|
97
|
+
export type { MarkDrawOptions } from './annotations/drawOne';
|
|
98
|
+
export { createMarkDrawOne, resolveMarkStyle } from './annotations/drawOne';
|
|
99
|
+
export { ExportMenu } from './annotations/ExportMenu';
|
|
100
|
+
export type { MarkListProps } from './annotations/MarkList';
|
|
101
|
+
export { MarkList } from './annotations/MarkList';
|
|
102
|
+
export type { MarkStyle, PaintableMark } from './annotations/paint';
|
|
103
|
+
export { markCommands } from './annotations/paint';
|
|
104
|
+
export { markSvgNodes } from './annotations/svgNodes';
|
|
105
|
+
export type { AnnotationToolInfo } from './annotations/toolMap';
|
|
106
|
+
export {
|
|
107
|
+
ANNOTATION_TOOLS,
|
|
108
|
+
ANNOTATION_WEASEL_TOOLS,
|
|
109
|
+
annotationToolInfo,
|
|
110
|
+
} from './annotations/toolMap';
|
|
111
|
+
export type { ContentSize, PaneSize } from './annotations/view';
|
|
112
|
+
export { fitView, fromWeaselView, toWeaselView } from './annotations/view';
|
|
113
|
+
export type {
|
|
114
|
+
CanvasLayerDescriptor,
|
|
115
|
+
CanvasStackContextValue,
|
|
116
|
+
CanvasStackProps,
|
|
117
|
+
CanvasStackSurface,
|
|
118
|
+
ViewportSize,
|
|
119
|
+
WorldFrame,
|
|
120
|
+
WorldSpec,
|
|
121
|
+
ZoomAtOptions,
|
|
122
|
+
} from './canvas';
|
|
123
|
+
export {
|
|
124
|
+
applyCamera,
|
|
125
|
+
CanvasStack,
|
|
126
|
+
CanvasStackContext,
|
|
127
|
+
centerOn,
|
|
128
|
+
DEFAULT_FRAME,
|
|
129
|
+
resolveFrame,
|
|
130
|
+
screenToWorld,
|
|
131
|
+
worldToScreen,
|
|
132
|
+
zoomAt,
|
|
133
|
+
} from './canvas';
|
|
3
134
|
export type { OrbitHandlers, OrbitView, UseOrbitOptions, Vec3 } from './canvas/useOrbit';
|
|
4
135
|
export {
|
|
5
136
|
clampPitch,
|
|
@@ -69,6 +200,7 @@ export * from './job';
|
|
|
69
200
|
export * from './lab';
|
|
70
201
|
export type { LayerListProps } from './layers';
|
|
71
202
|
export { LayerList } from './layers';
|
|
203
|
+
export * from './loupe';
|
|
72
204
|
export * from './primitives';
|
|
73
205
|
export {
|
|
74
206
|
createMemoryAdapter,
|
|
@@ -98,6 +230,8 @@ export type {
|
|
|
98
230
|
TrialStateHandle,
|
|
99
231
|
UndoStack,
|
|
100
232
|
} from './state/types';
|
|
233
|
+
export type { UndockedPanel, UndockedPanels } from './state/undock';
|
|
234
|
+
export { dockPanel, panelKey, undockPanel } from './state/undock';
|
|
101
235
|
export { useTrialState } from './state/useTrialState';
|
|
102
236
|
export type { ViewTransform2D } from './state/view';
|
|
103
237
|
export { as2DView, DEFAULT_VIEW } from './state/view';
|
|
@@ -105,7 +239,5 @@ export * from './surface';
|
|
|
105
239
|
export { interstellarTheme } from './theme/interstellar';
|
|
106
240
|
export type { ToolCapability, TrialTool } from './tools/types';
|
|
107
241
|
export * from './trial';
|
|
108
|
-
export * from './ui/layers';
|
|
109
|
-
export * from './ui/properties';
|
|
110
242
|
export type { EventBus, EventListener } from './undo';
|
|
111
243
|
export { clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './undo';
|
package/src/instrument/types.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import type { AnnotationsCapability } from '../annotations/types';
|
|
3
|
+
import type { ViewportSize, WorldSpec } from '../canvas/worldSpec';
|
|
2
4
|
import type { TrialContribution } from '../chrome/types';
|
|
3
5
|
import type { ConfigSchema } from '../config/types';
|
|
4
6
|
import type { ConfigField } from '../controls/types';
|
|
5
7
|
import type { JobCapability, JobHandle } from '../job/types';
|
|
8
|
+
import type { LoupeDeclaration } from '../loupe/types';
|
|
6
9
|
import type { ToolCapability } from '../tools/types';
|
|
7
10
|
|
|
8
11
|
/** What an instrument's `render` is handed: its state and config, the setters
|
|
@@ -25,6 +28,10 @@ export interface RenderContext<TS = unknown, TC = unknown> {
|
|
|
25
28
|
/** Resolved active tool: this trial's slot, or the lab's. Null when neither
|
|
26
29
|
* holds one. */
|
|
27
30
|
activeToolId: string | null;
|
|
31
|
+
/** The canvas layers currently shown, in declaration order. labkit skips a
|
|
32
|
+
* hidden layer's `draw`, so this is the only way to know what was
|
|
33
|
+
* painted — a legend listing rows the view did not draw needs it. */
|
|
34
|
+
visibleLayers: readonly string[];
|
|
28
35
|
};
|
|
29
36
|
emit: (event: string) => void;
|
|
30
37
|
/** Present only when the instrument declares a `job`. */
|
|
@@ -45,7 +52,14 @@ export interface CanvasLayer<TS = unknown, TC = unknown> {
|
|
|
45
52
|
* view starts. */
|
|
46
53
|
export interface CanvasCapability<TS = unknown, TC = unknown> {
|
|
47
54
|
layers: CanvasLayer<TS, TC>[];
|
|
48
|
-
|
|
55
|
+
/** The coordinate system this instrument's world is in. Omitted, world (0,0)
|
|
56
|
+
* sits at the canvas top-left with y running down — what labkit assumed
|
|
57
|
+
* before an instrument could say otherwise. */
|
|
58
|
+
worldSpec?: WorldSpec;
|
|
59
|
+
/** Where the view starts. A function is called once the canvas knows its
|
|
60
|
+
* size, so an instrument can frame content it can only place in terms of
|
|
61
|
+
* the viewport; until then the trial's view is `null`. */
|
|
62
|
+
initialView?: ViewTransform | ((size: ViewportSize) => ViewTransform);
|
|
49
63
|
/** Widens `usePanZoom`'s default clamp; the opening zoom stays reachable
|
|
50
64
|
* regardless of these. */
|
|
51
65
|
minZoom?: number;
|
|
@@ -127,6 +141,15 @@ export interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
|
|
|
127
141
|
/** Tools this instrument offers. Declaring them gives the trial a palette
|
|
128
142
|
* region and its own tool slot. */
|
|
129
143
|
tools?: ToolCapability;
|
|
144
|
+
/** Regions of this instrument that accept marks, and optionally what a mark
|
|
145
|
+
* is allowed to mean. Declaring it is what makes the trial provide the
|
|
146
|
+
* annotation overlay and its chrome. */
|
|
147
|
+
annotations?: AnnotationsCapability<TS, TC>;
|
|
148
|
+
/** Magnification. `true` takes every default and re-draws the instrument's
|
|
149
|
+
* canvas layers through a zoomed camera; an instrument whose content is DOM
|
|
150
|
+
* gives a `render` that draws it again at a camera it is handed. A function
|
|
151
|
+
* is re-read as the config changes, so a setting can drive the lens. */
|
|
152
|
+
loupe?: LoupeDeclaration<TS, TC> | ((config: TC) => LoupeDeclaration<TS, TC>);
|
|
130
153
|
/** Chrome this instrument contributes beyond what its capabilities imply. */
|
|
131
154
|
chrome?: TrialContribution[];
|
|
132
155
|
/** Work too slow to do during a render. The runtime starts it, aborts it on
|