@weasel-js/labkit 1.3.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-DCGEcMO_.d.ts → types-D6s4b7if.d.ts} +1 -1
- 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 -4
- package/dist/chrome/index.d.ts +9 -6
- package/dist/chrome/index.js +6 -6
- package/dist/chunk-2AYGEN57.js +32 -0
- package/dist/chunk-2AYGEN57.js.map +1 -0
- package/dist/{chunk-QRYPXSGP.js → chunk-3P5TTTA7.js} +16 -8
- package/dist/chunk-3P5TTTA7.js.map +1 -0
- package/dist/{chunk-BDJXIBRZ.js → chunk-3ZELRRBV.js} +44 -27
- package/dist/chunk-3ZELRRBV.js.map +1 -0
- package/dist/{chunk-LGKRIUSW.js → chunk-4EY67BKJ.js} +32 -7
- package/dist/chunk-4EY67BKJ.js.map +1 -0
- package/dist/{chunk-3XXPU73K.js → chunk-5TCEVMPI.js} +9 -7
- package/dist/chunk-5TCEVMPI.js.map +1 -0
- package/dist/{chunk-JDROYQM3.js → chunk-6JMU56CU.js} +130 -131
- 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-BMW4TDP5.js → chunk-JDDIZSLL.js} +3 -3
- package/dist/{chunk-BMW4TDP5.js.map → chunk-JDDIZSLL.js.map} +1 -1
- package/dist/chunk-LITE5YFB.js +669 -0
- package/dist/chunk-LITE5YFB.js.map +1 -0
- package/dist/{chunk-QCICIHEO.js → chunk-ROQDRXKW.js} +716 -311
- package/dist/chunk-ROQDRXKW.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-WTI26YTM.js → chunk-YQJNT4XQ.js} +40 -13
- 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 +9 -5
- package/dist/dragdrop/index.js +3 -3
- package/dist/index.d.ts +518 -190
- package/dist/index.js +1753 -75
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +6 -5
- package/dist/layers/index.js +3 -3
- 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 +138 -91
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.d.ts +3 -1
- package/dist/primitives/index.js +5 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +2 -2
- package/dist/styles.css +229 -1
- package/dist/surface/index.d.ts +18 -2
- package/dist/surface/index.js +2 -2
- package/dist/ui/layers/index.js +2 -2
- 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/controls/ControlPanel.test.tsx +38 -0
- package/src/controls/ControlPanel.tsx +50 -3
- package/src/dragdrop/DragDropRuntime.tsx +8 -2
- package/src/dragdrop/dragDrop.test.tsx +29 -2
- package/src/index.test.ts +22 -0
- package/src/index.ts +87 -2
- 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/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 +3 -0
- 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 -0
- 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/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.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/dist/_dts/types-BttTIent.d.ts +0 -314
- package/dist/chunk-3XXPU73K.js.map +0 -1
- package/dist/chunk-4TMMVIDM.js.map +0 -1
- package/dist/chunk-6X5RBC6G.js +0 -366
- package/dist/chunk-6X5RBC6G.js.map +0 -1
- package/dist/chunk-BDJXIBRZ.js.map +0 -1
- package/dist/chunk-CPUJ3QXL.js.map +0 -1
- package/dist/chunk-JDROYQM3.js.map +0 -1
- package/dist/chunk-LGKRIUSW.js.map +0 -1
- package/dist/chunk-QCICIHEO.js.map +0 -1
- package/dist/chunk-QRYPXSGP.js.map +0 -1
- package/dist/chunk-RGT4EJIB.js +0 -6961
- package/dist/chunk-RGT4EJIB.js.map +0 -1
- package/dist/chunk-T66HU3HX.js +0 -17
- package/dist/chunk-T66HU3HX.js.map +0 -1
- package/dist/chunk-WTI26YTM.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ToolButton, ToolGroup } from '../../passthrough/weasel-ui';
|
|
2
|
+
import { useRovingTabIndex } from '../../primitives/useRovingTabIndex';
|
|
2
3
|
import type { TrialChromeContext, TrialContribution } from '../types';
|
|
3
4
|
|
|
4
5
|
/** Props for `<PaletteRegion>`. */
|
|
@@ -10,13 +11,16 @@ export interface PaletteRegionProps {
|
|
|
10
11
|
/** A trial's tool strip. Selection lives in the trial's or the lab's tool
|
|
11
12
|
* slot; this region only reflects it. */
|
|
12
13
|
export function PaletteRegion({ contributions, ctx }: PaletteRegionProps) {
|
|
14
|
+
const { ref, onKeyDown } = useRovingTabIndex<HTMLDivElement>('vertical');
|
|
13
15
|
if (contributions.length === 0) return null;
|
|
14
16
|
return (
|
|
15
17
|
<div
|
|
18
|
+
ref={ref}
|
|
16
19
|
className="lk-palette-region"
|
|
17
20
|
role="toolbar"
|
|
18
21
|
aria-label="Tools"
|
|
19
22
|
aria-orientation="vertical"
|
|
23
|
+
onKeyDown={onKeyDown}
|
|
20
24
|
>
|
|
21
25
|
<ToolGroup orientation="vertical">
|
|
22
26
|
{contributions.map((c) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import type { TrialChromeContext, TrialContribution } from '../types';
|
|
3
|
+
import type { SidebarSection, TrialChromeContext, TrialContribution } from '../types';
|
|
4
4
|
|
|
5
5
|
/** Props for `<SidebarRegion>`. */
|
|
6
6
|
export interface SidebarRegionProps {
|
|
@@ -11,29 +11,46 @@ export interface SidebarRegionProps {
|
|
|
11
11
|
function Section({
|
|
12
12
|
title,
|
|
13
13
|
defaultCollapsed,
|
|
14
|
+
onUndock,
|
|
14
15
|
children,
|
|
15
16
|
}: {
|
|
16
17
|
title: string;
|
|
17
18
|
defaultCollapsed: boolean;
|
|
19
|
+
onUndock?: () => void;
|
|
18
20
|
children: ReactNode;
|
|
19
21
|
}) {
|
|
20
22
|
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
21
23
|
return (
|
|
22
24
|
<section className="lk-sidebar-section">
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
<div className="lk-sidebar-section__bar">
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
className="lk-sidebar-section__head"
|
|
29
|
+
aria-expanded={!collapsed}
|
|
30
|
+
onClick={() => setCollapsed((c) => !c)}
|
|
31
|
+
>
|
|
32
|
+
{title}
|
|
33
|
+
</button>
|
|
34
|
+
{onUndock ? (
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
className="lk-sidebar-section__undock"
|
|
38
|
+
aria-label={`Undock ${title}`}
|
|
39
|
+
title={`Undock ${title}`}
|
|
40
|
+
onClick={onUndock}
|
|
41
|
+
>
|
|
42
|
+
⧉
|
|
43
|
+
</button>
|
|
44
|
+
) : null}
|
|
45
|
+
</div>
|
|
31
46
|
{collapsed ? null : <div className="lk-sidebar-section__body">{children}</div>}
|
|
32
47
|
</section>
|
|
33
48
|
);
|
|
34
49
|
}
|
|
35
50
|
|
|
36
|
-
/** Lays a trial's `sidebar` contributions out as titled, collapsible sections.
|
|
51
|
+
/** Lays a trial's `sidebar` contributions out as titled, collapsible sections.
|
|
52
|
+
* A section the trial has torn out is not rendered here — `Trial` portals its
|
|
53
|
+
* body into the workspace instead. */
|
|
37
54
|
export function SidebarRegion({ contributions, ctx }: SidebarRegionProps) {
|
|
38
55
|
if (contributions.length === 0) return null;
|
|
39
56
|
return (
|
|
@@ -41,13 +58,20 @@ export function SidebarRegion({ contributions, ctx }: SidebarRegionProps) {
|
|
|
41
58
|
{contributions.map((c) => {
|
|
42
59
|
if (c.render) return <div key={c.id}>{c.render(ctx)}</div>;
|
|
43
60
|
if (c.region !== 'sidebar' || !c.item) return null;
|
|
61
|
+
if (ctx.undockedPanels.includes(c.id)) return null;
|
|
62
|
+
const item = c.item as SidebarSection;
|
|
44
63
|
return (
|
|
45
64
|
<Section
|
|
46
65
|
key={c.id}
|
|
47
|
-
title={
|
|
48
|
-
defaultCollapsed={
|
|
66
|
+
title={item.title}
|
|
67
|
+
defaultCollapsed={item.defaultCollapsed ?? false}
|
|
68
|
+
onUndock={
|
|
69
|
+
item.undockable === false
|
|
70
|
+
? undefined
|
|
71
|
+
: () => ctx.undockPanel(c.id, item.undockAs ?? 'tile')
|
|
72
|
+
}
|
|
49
73
|
>
|
|
50
|
-
{
|
|
74
|
+
{item.body}
|
|
51
75
|
</Section>
|
|
52
76
|
);
|
|
53
77
|
})}
|
|
@@ -38,13 +38,14 @@ function groupsOf(contributions: readonly TrialContribution[]): Group[] {
|
|
|
38
38
|
function renderEntry(c: TrialContribution, ctx: TrialChromeContext): ReactNode {
|
|
39
39
|
if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;
|
|
40
40
|
if (c.region !== 'toolbar' || !c.item) return null;
|
|
41
|
-
const { icon: Icon, label, shortcut, disabled, danger, showLabel } = c.item;
|
|
41
|
+
const { icon: Icon, label, shortcut, disabled, danger, showLabel, pressed } = c.item;
|
|
42
42
|
return (
|
|
43
43
|
<Toolbar.Button
|
|
44
44
|
key={c.id}
|
|
45
45
|
iconOnly={!showLabel}
|
|
46
46
|
variant={danger ? 'danger' : 'default'}
|
|
47
47
|
disabled={disabled}
|
|
48
|
+
pressed={pressed}
|
|
48
49
|
aria-label={label}
|
|
49
50
|
title={shortcut ? `${label} (${shortcut})` : label}
|
|
50
51
|
onClick={c.item.onActivate}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useRovingTabIndex } from '../../primitives/useRovingTabIndex';
|
|
1
2
|
import type { TrialChromeContext, TrialContribution } from '../types';
|
|
2
3
|
|
|
3
4
|
/** Props for `<ViewportRegion>`. */
|
|
@@ -11,9 +12,16 @@ export interface ViewportRegionProps {
|
|
|
11
12
|
* rather than in the toolbar, which acts on the trial itself.
|
|
12
13
|
*/
|
|
13
14
|
export function ViewportRegion({ contributions, ctx }: ViewportRegionProps) {
|
|
15
|
+
const { ref, onKeyDown } = useRovingTabIndex<HTMLDivElement>();
|
|
14
16
|
if (contributions.length === 0) return null;
|
|
15
17
|
return (
|
|
16
|
-
<div
|
|
18
|
+
<div
|
|
19
|
+
ref={ref}
|
|
20
|
+
className="lk-viewport-controls"
|
|
21
|
+
role="toolbar"
|
|
22
|
+
aria-label="View"
|
|
23
|
+
onKeyDown={onKeyDown}
|
|
24
|
+
>
|
|
17
25
|
{contributions.map((c) => {
|
|
18
26
|
if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;
|
|
19
27
|
if (c.region !== 'viewport' || !c.item) return null;
|
|
@@ -6,7 +6,12 @@ import { StatusRegion } from './StatusRegion';
|
|
|
6
6
|
import { ViewportRegion } from './ViewportRegion';
|
|
7
7
|
|
|
8
8
|
const Glyph = () => <svg />;
|
|
9
|
-
const ctx = {
|
|
9
|
+
const ctx = {
|
|
10
|
+
trialId: 't1',
|
|
11
|
+
undockedPanels: [],
|
|
12
|
+
undockPanel: () => {},
|
|
13
|
+
dockPanel: () => {},
|
|
14
|
+
} as unknown as TrialChromeContext;
|
|
10
15
|
|
|
11
16
|
describe('SidebarRegion', () => {
|
|
12
17
|
it('renders nothing when empty', () => {
|
package/src/chrome/types.ts
CHANGED
|
@@ -19,6 +19,9 @@ export interface ToolbarItem {
|
|
|
19
19
|
disabled?: boolean;
|
|
20
20
|
/** Reddens on hover. For actions that discard work. */
|
|
21
21
|
danger?: boolean;
|
|
22
|
+
/** A toggle rather than a command: renders `aria-pressed` and reads as held
|
|
23
|
+
* down while true. */
|
|
24
|
+
pressed?: boolean;
|
|
22
25
|
/** Render the label beside the glyph rather than only in the tooltip. */
|
|
23
26
|
showLabel?: boolean;
|
|
24
27
|
onActivate: () => void;
|
|
@@ -37,6 +40,11 @@ export interface SidebarSection {
|
|
|
37
40
|
title: string;
|
|
38
41
|
/** Starts collapsed. The open/closed state itself is the region's. */
|
|
39
42
|
defaultCollapsed?: boolean;
|
|
43
|
+
/** Offer the tear-out control. On by default; a section that only makes
|
|
44
|
+
* sense beside its trial sets this false. */
|
|
45
|
+
undockable?: boolean;
|
|
46
|
+
/** Where the tear-out control sends it. Default `'tile'`. */
|
|
47
|
+
undockAs?: 'tile' | 'floating';
|
|
40
48
|
body: ReactNode;
|
|
41
49
|
}
|
|
42
50
|
|
|
@@ -103,6 +111,11 @@ export interface TrialChromeContext {
|
|
|
103
111
|
undo: () => void;
|
|
104
112
|
redo: () => void;
|
|
105
113
|
|
|
114
|
+
/** Whether the trial's loupe is turned on. False for an instrument that
|
|
115
|
+
* declares none, whose chrome offers no way to turn one on. */
|
|
116
|
+
loupeOn: boolean;
|
|
117
|
+
toggleLoupe: () => void;
|
|
118
|
+
|
|
106
119
|
/** The instrument's controls, resolved against the lab's rules. Always
|
|
107
120
|
* populated: a legacy `configSchema()` is adapted into the same shape. */
|
|
108
121
|
configSchema: ResolvedConfig;
|
|
@@ -112,6 +125,13 @@ export interface TrialChromeContext {
|
|
|
112
125
|
config: unknown;
|
|
113
126
|
setConfig: (key: string, value: unknown) => void;
|
|
114
127
|
|
|
128
|
+
/** Section ids this trial currently has torn out of its sidebar. */
|
|
129
|
+
undockedPanels: readonly string[];
|
|
130
|
+
/** Tear a sidebar section out into the workspace. */
|
|
131
|
+
undockPanel: (sectionId: string, as?: 'tile' | 'floating') => void;
|
|
132
|
+
/** Put a torn-out section back in the sidebar. */
|
|
133
|
+
dockPanel: (sectionId: string) => void;
|
|
134
|
+
|
|
115
135
|
savedSnapshots: SavedSnapshot[];
|
|
116
136
|
saveSnapshot: (name?: string) => void;
|
|
117
137
|
loadSnapshot: (snapshotId: string) => void;
|
|
@@ -382,3 +382,41 @@ describe('<ControlPanel> built-in kind coverage', () => {
|
|
|
382
382
|
).toThrow(/spline/);
|
|
383
383
|
});
|
|
384
384
|
});
|
|
385
|
+
|
|
386
|
+
describe('<ControlPanel> packing', () => {
|
|
387
|
+
const schema = resolveConfigSchema(
|
|
388
|
+
f.schema({ width: f.number(256), title: f.string('a'), tint: f.color('#ff0000') }),
|
|
389
|
+
[],
|
|
390
|
+
);
|
|
391
|
+
const config = { width: 256, title: 'a', tint: '#ff0000' };
|
|
392
|
+
// jsdom lays out no grid, so the class the grid rules key off is the proxy
|
|
393
|
+
// for "these rows pair" — a layout assertion is not available here.
|
|
394
|
+
const list = (container: HTMLElement) => container.querySelector('.lk-control-panel');
|
|
395
|
+
const rowOf = (name: string) => screen.getByLabelText(name).closest('label');
|
|
396
|
+
|
|
397
|
+
it('packs two rows to a line by default', () => {
|
|
398
|
+
const { container } = render(
|
|
399
|
+
<ControlPanel schema={schema} config={config} setConfig={vi.fn()} />,
|
|
400
|
+
);
|
|
401
|
+
expect(list(container)?.className).toMatch(/listPairs/);
|
|
402
|
+
expect(rowOf('Width')?.className).not.toMatch(/span/);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('gives every row its own line under one-up', () => {
|
|
406
|
+
const { container } = render(
|
|
407
|
+
<ControlPanel schema={schema} config={config} setConfig={vi.fn()} pack="one-up" />,
|
|
408
|
+
);
|
|
409
|
+
expect(list(container)?.className).not.toMatch(/listPairs/);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('spans the rows that need the width under auto, and pairs the rest', () => {
|
|
413
|
+
render(<ControlPanel schema={schema} config={config} setConfig={vi.fn()} pack="auto" />);
|
|
414
|
+
expect(rowOf('Title')?.className).toMatch(/span/);
|
|
415
|
+
expect(rowOf('Width')?.className).not.toMatch(/span/);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('passes its layout down to the rows', () => {
|
|
419
|
+
render(<ControlPanel schema={schema} config={config} setConfig={vi.fn()} layout="inline" />);
|
|
420
|
+
expect(rowOf('Width')?.className).toMatch(/rowInline/);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
PropertyGroup,
|
|
8
8
|
PropertyList,
|
|
9
9
|
PropertyRow,
|
|
10
|
+
type PropertyRowLayout,
|
|
10
11
|
SelectRow,
|
|
11
12
|
SliderRow,
|
|
12
13
|
TextRow,
|
|
@@ -18,6 +19,16 @@ import type { ControlRenderer, ResolvedConfig } from '../config/types';
|
|
|
18
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,6 +61,8 @@ 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>) {
|
|
@@ -62,15 +80,21 @@ export function ControlPanel<TC extends Record<string, unknown>>({
|
|
|
62
80
|
config={config}
|
|
63
81
|
setConfig={setConfig}
|
|
64
82
|
renderers={renderers}
|
|
83
|
+
pack={pack}
|
|
84
|
+
layout={layout}
|
|
65
85
|
showHidden={showHidden}
|
|
66
86
|
/>
|
|
67
87
|
);
|
|
68
88
|
|
|
89
|
+
const gridPack = pack === 'one-up' ? 'auto-color' : 'pairs';
|
|
69
90
|
return (
|
|
70
|
-
<PropertyList
|
|
91
|
+
<PropertyList
|
|
92
|
+
pack={gridPack}
|
|
93
|
+
className={className ? `lk-control-panel ${className}` : 'lk-control-panel'}
|
|
94
|
+
>
|
|
71
95
|
{loose.map(row)}
|
|
72
96
|
{resolved.sections.map((section) => (
|
|
73
|
-
<PropertyGroup key={section.label} title={section.label}>
|
|
97
|
+
<PropertyGroup key={section.label} title={section.label} pack={gridPack}>
|
|
74
98
|
{section.paths.map(row)}
|
|
75
99
|
</PropertyGroup>
|
|
76
100
|
))}
|
|
@@ -84,6 +108,8 @@ interface ControlRowProps<TC extends Record<string, unknown>> {
|
|
|
84
108
|
config: TC;
|
|
85
109
|
setConfig: (key: keyof TC, value: unknown) => void;
|
|
86
110
|
renderers?: Record<string, ControlRenderer>;
|
|
111
|
+
pack: ControlPack;
|
|
112
|
+
layout: PropertyRowLayout;
|
|
87
113
|
showHidden: boolean;
|
|
88
114
|
}
|
|
89
115
|
|
|
@@ -99,6 +125,8 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
99
125
|
config,
|
|
100
126
|
setConfig,
|
|
101
127
|
renderers,
|
|
128
|
+
pack,
|
|
129
|
+
layout,
|
|
102
130
|
showHidden,
|
|
103
131
|
}: ControlRowProps<TC>) {
|
|
104
132
|
const leaf = resolved.group.children[path] as PrefLeaf | undefined;
|
|
@@ -112,11 +140,16 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
112
140
|
// Most specific wins, and within a tier the lab's entry beats the
|
|
113
141
|
// instrument's: controls[path] -> node .render -> controls[kind] -> built-in.
|
|
114
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.
|
|
115
145
|
if (custom) return custom({ path, pref: leaf, value, setValue: write });
|
|
116
146
|
|
|
117
147
|
const label = leaf.name;
|
|
118
148
|
const description = leaf.description;
|
|
119
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';
|
|
120
153
|
|
|
121
154
|
if (!isBuiltinToolPref(leaf))
|
|
122
155
|
return <UnwiredRow label={label} kind={leaf.kind} description={description} />;
|
|
@@ -135,6 +168,8 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
135
168
|
max={max}
|
|
136
169
|
step={step}
|
|
137
170
|
onChange={write}
|
|
171
|
+
layout={layout}
|
|
172
|
+
span={wide}
|
|
138
173
|
description={description}
|
|
139
174
|
/>
|
|
140
175
|
);
|
|
@@ -152,6 +187,7 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
152
187
|
max={max}
|
|
153
188
|
step={step}
|
|
154
189
|
onChange={(n) => write(Math.min(hi, Math.max(lo, n)))}
|
|
190
|
+
layout={layout}
|
|
155
191
|
description={description}
|
|
156
192
|
/>
|
|
157
193
|
);
|
|
@@ -167,13 +203,16 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
167
203
|
);
|
|
168
204
|
case 'enum': {
|
|
169
205
|
const options = extra<readonly { value: string; label: string }[]>(leaf, 'options') ?? [];
|
|
170
|
-
const
|
|
206
|
+
const segmented = extra<string>(leaf, 'control') === 'radio';
|
|
207
|
+
const Row = segmented ? ToggleRow : SelectRow;
|
|
171
208
|
return (
|
|
172
209
|
<Row
|
|
173
210
|
label={label}
|
|
174
211
|
value={read<string>()}
|
|
175
212
|
options={options}
|
|
176
213
|
onChange={write}
|
|
214
|
+
layout={layout}
|
|
215
|
+
span={segmented && wide}
|
|
177
216
|
description={description}
|
|
178
217
|
/>
|
|
179
218
|
);
|
|
@@ -185,6 +224,8 @@ function ControlRow<TC extends Record<string, unknown>>({
|
|
|
185
224
|
label={label}
|
|
186
225
|
value={read<string>()}
|
|
187
226
|
write={write}
|
|
227
|
+
layout={layout}
|
|
228
|
+
span={wide}
|
|
188
229
|
description={description}
|
|
189
230
|
/>
|
|
190
231
|
);
|
|
@@ -235,12 +276,16 @@ function DebouncedTextRow({
|
|
|
235
276
|
label,
|
|
236
277
|
value,
|
|
237
278
|
write,
|
|
279
|
+
layout,
|
|
280
|
+
span,
|
|
238
281
|
description,
|
|
239
282
|
}: {
|
|
240
283
|
leaf: PrefLeaf;
|
|
241
284
|
label: string;
|
|
242
285
|
value: string;
|
|
243
286
|
write: (value: unknown) => void;
|
|
287
|
+
layout?: PropertyRowLayout;
|
|
288
|
+
span?: boolean;
|
|
244
289
|
description?: string;
|
|
245
290
|
}) {
|
|
246
291
|
const debounceMs = extra<number>(leaf, 'debounceMs') ?? 150;
|
|
@@ -265,6 +310,8 @@ function DebouncedTextRow({
|
|
|
265
310
|
return (
|
|
266
311
|
<TextRow
|
|
267
312
|
label={label}
|
|
313
|
+
layout={layout}
|
|
314
|
+
span={span}
|
|
268
315
|
description={description}
|
|
269
316
|
value={local}
|
|
270
317
|
placeholder={extra<string>(leaf, 'placeholder')}
|
|
@@ -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/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
|
@@ -47,8 +47,90 @@ export {
|
|
|
47
47
|
TextRow,
|
|
48
48
|
ToggleRow,
|
|
49
49
|
} from '@weasel-js/ui';
|
|
50
|
-
export type {
|
|
51
|
-
|
|
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';
|
|
52
134
|
export type { OrbitHandlers, OrbitView, UseOrbitOptions, Vec3 } from './canvas/useOrbit';
|
|
53
135
|
export {
|
|
54
136
|
clampPitch,
|
|
@@ -118,6 +200,7 @@ export * from './job';
|
|
|
118
200
|
export * from './lab';
|
|
119
201
|
export type { LayerListProps } from './layers';
|
|
120
202
|
export { LayerList } from './layers';
|
|
203
|
+
export * from './loupe';
|
|
121
204
|
export * from './primitives';
|
|
122
205
|
export {
|
|
123
206
|
createMemoryAdapter,
|
|
@@ -147,6 +230,8 @@ export type {
|
|
|
147
230
|
TrialStateHandle,
|
|
148
231
|
UndoStack,
|
|
149
232
|
} from './state/types';
|
|
233
|
+
export type { UndockedPanel, UndockedPanels } from './state/undock';
|
|
234
|
+
export { dockPanel, panelKey, undockPanel } from './state/undock';
|
|
150
235
|
export { useTrialState } from './state/useTrialState';
|
|
151
236
|
export type { ViewTransform2D } from './state/view';
|
|
152
237
|
export { as2DView, DEFAULT_VIEW } from './state/view';
|