@weasel-js/labkit 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/_dts/{CanvasStackContext-Dde8_1my.d.ts → CanvasStackContext-CqkxkFJW.d.ts} +1 -1
- package/dist/_dts/ToggleBar.d-DbHoWKIA.d.ts +116 -0
- package/dist/_dts/{frac-B5mB8LsJ.d.ts → frac-8R6UmlvN.d.ts} +256 -33
- package/dist/_dts/index-B8uu2ba7.d.ts +237 -0
- package/dist/_dts/{index-DelFfeV0.d.ts → index-retnAut7.d.ts} +45 -12
- package/dist/_dts/{types-B9_zrHmb.d.ts → types-Ca2LCnPq.d.ts} +65 -2
- package/dist/_dts/usePanZoom-nS798rOF.d.ts +160 -0
- package/dist/_dts/useTrialState-jcx_TdU6.d.ts +212 -0
- package/dist/canvas/index.d.ts +17 -128
- package/dist/canvas/index.js +4 -2
- package/dist/chrome/index.d.ts +4 -92
- package/dist/chrome/index.js +9 -5
- package/dist/chunk-67SJLMC7.js +704 -0
- package/dist/chunk-67SJLMC7.js.map +1 -0
- package/dist/chunk-6ZDGOZQV.js +310 -0
- package/dist/chunk-6ZDGOZQV.js.map +1 -0
- package/dist/{chunk-QAGAHJKZ.js → chunk-EKIICY6X.js} +111 -25
- package/dist/chunk-EKIICY6X.js.map +1 -0
- package/dist/{chunk-CTRKTLYZ.js → chunk-H6ZAOWNE.js} +11 -11
- package/dist/chunk-H6ZAOWNE.js.map +1 -0
- package/dist/{chunk-2RQPGOQF.js → chunk-KJALCDWE.js} +9 -7
- package/dist/chunk-KJALCDWE.js.map +1 -0
- package/dist/{chunk-ISCOFAUO.js → chunk-MUKOW3TC.js} +3 -3
- package/dist/{chunk-ISCOFAUO.js.map → chunk-MUKOW3TC.js.map} +1 -1
- package/dist/chunk-NDRYLVOW.js +15 -0
- package/dist/chunk-NDRYLVOW.js.map +1 -0
- package/dist/{chunk-C42T6DJT.js → chunk-NS64DXMN.js} +226 -159
- package/dist/chunk-NS64DXMN.js.map +1 -0
- package/dist/{chunk-I6JCVE24.js → chunk-PGETSEDK.js} +135 -13
- package/dist/chunk-PGETSEDK.js.map +1 -0
- package/dist/{chunk-FQMJUVHJ.js → chunk-QLSV2N3G.js} +32 -37
- package/dist/chunk-QLSV2N3G.js.map +1 -0
- package/dist/chunk-RWNDDKOH.js +682 -0
- package/dist/chunk-RWNDDKOH.js.map +1 -0
- package/dist/chunk-S2SHZRD7.js +7681 -0
- package/dist/chunk-S2SHZRD7.js.map +1 -0
- package/dist/{chunk-ASWKLRKJ.js → chunk-TQBBYTAF.js} +19 -82
- package/dist/chunk-TQBBYTAF.js.map +1 -0
- package/dist/chunk-UU3NJO6N.js +72 -0
- package/dist/chunk-UU3NJO6N.js.map +1 -0
- package/dist/chunk-WIG6XHJ7.js +35 -0
- package/dist/chunk-WIG6XHJ7.js.map +1 -0
- package/dist/{chunk-TJ7QY3OC.js → chunk-ZPH5WTWF.js} +21 -15
- package/dist/chunk-ZPH5WTWF.js.map +1 -0
- package/dist/config/index.d.ts +232 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controls/index.d.ts +2 -3
- package/dist/controls/index.js +3 -2
- package/dist/dragdrop/index.d.ts +2 -4
- package/dist/index.d.ts +123 -348
- package/dist/index.js +794 -837
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +3 -5
- package/dist/layers/index.js +2 -2
- package/dist/loupe/index.d.ts +6 -8
- package/dist/loupe/index.js +2 -2
- package/dist/passthrough/weasel-ui.d.ts +169 -117
- package/dist/passthrough/weasel-ui.js +1 -1
- package/dist/primitives/index.d.ts +53 -6
- package/dist/primitives/index.js +6 -4
- package/dist/state/index.d.ts +12 -7
- package/dist/state/index.js +16 -13
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +163 -62
- package/dist/surface/index.d.ts +44 -7
- package/dist/surface/index.js +2 -1
- package/dist/ui/layers/index.js +1 -1
- package/dist/undo/index.d.ts +2 -4
- package/package.json +15 -8
- package/src/annotations/AnnotationOverlay.tsx +53 -5
- package/src/annotations/Annotations.overlay.test.tsx +85 -6
- package/src/annotations/drawOne.test.ts +1 -1
- package/src/annotations/drawOne.ts +2 -2
- package/src/annotations/frac.test.ts +4 -4
- package/src/annotations/frac.ts +3 -2
- package/src/annotations/index.ts +1 -1
- package/src/annotations/paint.test.ts +2 -2
- package/src/annotations/paint.ts +1 -1
- package/src/annotations/preload.ts +6 -0
- package/src/annotations/store.test.ts +18 -0
- package/src/annotations/store.ts +7 -6
- package/src/annotations/svgNodes.test.ts +2 -2
- package/src/annotations/svgNodes.ts +1 -2
- package/src/annotations/toolMap.ts +3 -3
- package/src/annotations/types.ts +6 -3
- package/src/canvas/CameraWheelContext.ts +12 -0
- package/src/canvas/CanvasStack.test.tsx +25 -0
- package/src/canvas/CanvasStack.tsx +31 -4
- package/src/canvas/Stage.less +18 -0
- package/src/canvas/Stage.test.tsx +186 -0
- package/src/canvas/Stage.tsx +134 -0
- package/src/canvas/camera.test.ts +17 -2
- package/src/canvas/camera.ts +9 -9
- package/src/canvas/index.ts +4 -0
- package/src/canvas/usePanZoom.ts +10 -5
- package/src/chrome/ChromeRegions.stories.tsx +0 -3
- package/src/chrome/LabChrome.tsx +114 -0
- package/src/chrome/builtins.tsx +20 -31
- package/src/chrome/index.ts +15 -0
- package/src/chrome/labRegions.test.tsx +208 -0
- package/src/chrome/labTypes.ts +50 -0
- package/src/chrome/merge.ts +7 -9
- package/src/chrome/regions/PaletteRegion.test.tsx +6 -4
- package/src/chrome/regions/PaletteRegion.tsx +21 -9
- package/src/chrome/regions/SidebarRegion.tsx +25 -13
- package/src/chrome/regions/StatusRegion.tsx +15 -8
- package/src/chrome/regions/ToolbarRegion.tsx +30 -16
- package/src/chrome/types.ts +47 -20
- package/src/config/builder.test.ts +4 -0
- package/src/config/builder.ts +16 -10
- package/src/config/declarationEmit.fixture.ts +11 -0
- package/src/config/declarationEmit.test.ts +51 -0
- package/src/config/entry.test.ts +46 -0
- package/src/config/index.ts +13 -1
- package/src/config/nodeClasses.test.ts +18 -0
- package/src/config/types.ts +3 -1
- package/src/controls/ControlPanel.test.tsx +16 -0
- package/src/controls/ControlPanel.tsx +5 -2
- package/src/fake-indexeddb-auto.d.ts +5 -0
- package/src/index.test.ts +7 -0
- package/src/index.ts +39 -6
- package/src/instrument/serializers.test.ts +22 -0
- package/src/instrument/serializers.ts +11 -0
- package/src/instrument/types.ts +25 -1
- package/src/lab/Lab.less +4 -13
- package/src/lab/Lab.persist.test.tsx +147 -0
- package/src/lab/Lab.stories.tsx +0 -2
- package/src/lab/Lab.surface.test.tsx +66 -3
- package/src/lab/Lab.test.tsx +91 -17
- package/src/lab/Lab.tsx +337 -124
- package/src/lab/LabFit.stories.less +18 -0
- package/src/lab/LabFit.stories.tsx +219 -0
- package/src/lab/LabFullChrome.stories.tsx +0 -3
- package/src/lab/LabHeader.test.tsx +40 -9
- package/src/lab/LabHeader.tsx +7 -13
- package/src/lab/LabPalette.tsx +13 -26
- package/src/lab/LabShell.less +49 -12
- package/src/lab/LabShell.tsx +14 -2
- package/src/lab/LabSwitcher.less +92 -0
- package/src/lab/LabSwitcher.test.tsx +96 -0
- package/src/lab/LabSwitcher.tsx +112 -0
- package/src/lab/Workspace.surface.test.tsx +2 -1
- package/src/lab/fitCheck.test.ts +57 -0
- package/src/lab/fitCheck.ts +125 -0
- package/src/lab/index.ts +2 -0
- package/src/loupe/AGENTS.md +1 -1
- package/src/loupe/Loupe.less +1 -1
- package/src/loupe/canvasLens.ts +1 -1
- package/src/loupe/types.ts +1 -1
- package/src/loupe/useLoupe.test.tsx +1 -1
- package/src/loupe/useLoupe.ts +1 -1
- package/src/passthrough/weasel-ui.ts +9 -0
- package/src/primitives/FloatingPanel.stories.tsx +13 -1
- package/src/primitives/FloatingPanel.test.tsx +47 -16
- package/src/primitives/FloatingPanel.tsx +11 -25
- package/src/primitives/Split.less +14 -0
- package/src/primitives/Split.test.tsx +108 -0
- package/src/primitives/Split.tsx +159 -0
- package/src/primitives/ZoomControl.tsx +5 -2
- package/src/primitives/index.ts +2 -0
- package/src/state/Persistence.stories.tsx +75 -0
- package/src/state/Persistence.tsx +33 -0
- package/src/state/SingletonExperiment.test.tsx +47 -53
- package/src/state/SingletonExperiment.tsx +24 -13
- package/src/state/adapterContract.ts +116 -0
- package/src/state/adapters.test.ts +113 -61
- package/src/state/adapters.ts +328 -69
- package/src/state/document.test.ts +71 -96
- package/src/state/document.ts +66 -52
- package/src/state/helpers.test.ts +20 -0
- package/src/state/helpers.ts +16 -0
- package/src/state/index.ts +8 -0
- package/src/state/labRecords.test.ts +105 -0
- package/src/state/labRecords.ts +160 -0
- package/src/state/openLabStore.test.ts +658 -0
- package/src/state/openLabStore.ts +403 -0
- package/src/state/records.test.ts +234 -0
- package/src/state/records.ts +219 -0
- package/src/state/store.test.ts +104 -635
- package/src/state/store.ts +117 -158
- package/src/state/toolSlot.test.ts +1 -1
- package/src/state/types.ts +39 -15
- package/src/state/useOpenOnce.ts +68 -0
- package/src/state/usePersistedState.test.tsx +125 -0
- package/src/state/usePersistedState.ts +66 -0
- package/src/state/useTrialState.test.tsx +2 -3
- package/src/state/view.test.ts +49 -15
- package/src/state/view.ts +18 -1
- package/src/styles.less +3 -0
- package/src/surface/AGENTS.md +39 -7
- package/src/surface/SurfaceContext.ts +26 -3
- package/src/surface/index.ts +2 -0
- package/src/surface/useSurfaceTile.test.tsx +2 -1
- package/src/surface/useSurfaceTile.ts +6 -5
- package/src/surface/useTiledSurface.test.tsx +175 -0
- package/src/surface/useTiledSurface.ts +54 -5
- package/src/theme/Interstellar.stories.tsx +7 -7
- package/src/theme/base.less +4 -4
- package/src/theme/interstellar.test.ts +1 -1
- package/src/theme/interstellar.tokens.json +2 -2
- package/src/tools/labTool.ts +11 -0
- package/src/trial/Trial.annotations.persist.test.tsx +100 -58
- package/src/trial/Trial.annotations.test.tsx +47 -4
- package/src/trial/Trial.job.test.tsx +3 -3
- package/src/trial/Trial.less +1 -26
- package/src/trial/Trial.stories.tsx +1 -2
- package/src/trial/Trial.test.tsx +28 -4
- package/src/trial/Trial.trialId.test.tsx +23 -1
- package/src/trial/Trial.tsx +118 -38
- package/src/trial/TrialBody.tsx +17 -141
- package/src/trial/TrialChrome.tsx +7 -4
- package/src/trial/trialOps.ts +7 -4
- package/dist/_dts/types-BP2OCcpg.d.ts +0 -155
- package/dist/_dts/types-x92Kfeme.d.ts +0 -62
- package/dist/_dts/useTrialState-D6Vb3T-g.d.ts +0 -99
- package/dist/chunk-2RQPGOQF.js.map +0 -1
- package/dist/chunk-5TROQVQ2.js +0 -547
- package/dist/chunk-5TROQVQ2.js.map +0 -1
- package/dist/chunk-ASWKLRKJ.js.map +0 -1
- package/dist/chunk-C42T6DJT.js.map +0 -1
- package/dist/chunk-CTRKTLYZ.js.map +0 -1
- package/dist/chunk-FQMJUVHJ.js.map +0 -1
- package/dist/chunk-I6JCVE24.js.map +0 -1
- package/dist/chunk-MHAGC6VD.js +0 -7320
- package/dist/chunk-MHAGC6VD.js.map +0 -1
- package/dist/chunk-QAGAHJKZ.js.map +0 -1
- package/dist/chunk-TJ7QY3OC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,54 +1,62 @@
|
|
|
1
|
+
import { resolveLoupe, TrialLoupe } from './chunk-ZPH5WTWF.js';
|
|
2
|
+
export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-ZPH5WTWF.js';
|
|
1
3
|
import { createEventBus, pushSnapshot, redo, undo } from './chunk-RL2LOLNI.js';
|
|
2
4
|
export { clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './chunk-RL2LOLNI.js';
|
|
3
5
|
import { useDragDrop, DragOverlay, Palette } from './chunk-RS3HRQWU.js';
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
6
|
+
import { useTiledSurface } from './chunk-QLSV2N3G.js';
|
|
7
|
+
export { composeRects, rectsEqual, toDeviceRect, useTiledSurface } from './chunk-QLSV2N3G.js';
|
|
6
8
|
import { useJob } from './chunk-HXXTULDN.js';
|
|
7
9
|
export { useJob } from './chunk-HXXTULDN.js';
|
|
8
|
-
import {
|
|
9
|
-
export {
|
|
10
|
-
import { JobProgress } from './chunk-
|
|
11
|
-
export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-
|
|
12
|
-
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-
|
|
13
|
-
import { createLabStore,
|
|
14
|
-
export { CURRENT_DOCUMENT_VERSION,
|
|
15
|
-
import {
|
|
16
|
-
export {
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
import { suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, ViewportRegion, SidebarRegion, StatusRegion, resolveLabTool, AnnotationsContext, useLabChromeContext, labContributions, LabHeaderRegion, LabFooterRegion, LabSidebarRegion } from './chunk-NS64DXMN.js';
|
|
11
|
+
export { AnnotationsContext, ExportMenu, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, suppressContributions, toolContributions, useAnnotations, useAnnotationsOptional, useLabChromeContext } from './chunk-NS64DXMN.js';
|
|
12
|
+
import { Split, JobProgress } from './chunk-EKIICY6X.js';
|
|
13
|
+
export { FloatingPanel, JobProgress, Legend, Sidebar, Split } from './chunk-EKIICY6X.js';
|
|
14
|
+
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-KJALCDWE.js';
|
|
15
|
+
import { createLabStore, openLabStore, panelKey } from './chunk-67SJLMC7.js';
|
|
16
|
+
export { CURRENT_DOCUMENT_VERSION, dockPanel, labDocumentKey, openLabStore, panelKey, quarantineKey, undockPanel, useTrialState } from './chunk-67SJLMC7.js';
|
|
17
|
+
import { newId, useWarnIgnoredChange, useOpenOnce, PersistenceContext, createRecordCache, noneAdapter, defaultStorage, usePersistedState } from './chunk-RWNDDKOH.js';
|
|
18
|
+
export { Persistence, createIndexedDbAdapter, createMemoryAdapter, indexedDbAdapter, localStorageAdapter, noneAdapter, sessionStorageAdapter, urlHashAdapter, usePersistedState } from './chunk-RWNDDKOH.js';
|
|
19
|
+
export { ControlPanel } from './chunk-TQBBYTAF.js';
|
|
20
|
+
import { useConfigSchema } from './chunk-6ZDGOZQV.js';
|
|
21
|
+
export { BaseNode, BooleanNode, ColorNode, CustomNode, EnumNode, GroupNode, NumberNode, StringNode, ValueNode, applyRules, builtinRules, f, isConfigBranch, resolveConfigSchema, titleCase, useConfigSchema } from './chunk-6ZDGOZQV.js';
|
|
22
|
+
import { useLabContext, LabContext } from './chunk-NDRYLVOW.js';
|
|
23
|
+
export { LabContext, useLabContext } from './chunk-NDRYLVOW.js';
|
|
24
|
+
export { fromConfigFields, isLeafVisible } from './chunk-UU3NJO6N.js';
|
|
19
25
|
import { hasConfigPath, schemaNodeAtPath, withValueAtPath, fillConfigDefaults } from './chunk-E44SU6XS.js';
|
|
20
26
|
export { fillConfigDefaults, hasConfigPath, schemaNodeAtPath, valueAtPath, withValueAtPath } from './chunk-E44SU6XS.js';
|
|
21
|
-
import { CanvasStack } from './chunk-
|
|
22
|
-
export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-
|
|
23
|
-
|
|
24
|
-
export { LayerList } from './chunk-ISCOFAUO.js';
|
|
25
|
-
import './chunk-SBXNQW4G.js';
|
|
26
|
-
import { E, w, T, C, g, r6 } from './chunk-MHAGC6VD.js';
|
|
27
|
-
export { M2 as CheckboxRow, j2 as ColorRow, y3 as CurveField, R as EffectCard, B as EffectCardList, c3 as LayerStack, P2 as NumberRow, V as PropertyGroup, T2 as PropertyList, w2 as PropertyPanel, D as PropertyRow, E2 as PropertySpan, F as SelectRow, k as SliderRow, L2 as Subpanel, N2 as TextRow, I as ToggleRow } from './chunk-MHAGC6VD.js';
|
|
28
|
-
import { resolveLoupe, TrialLoupe } from './chunk-TJ7QY3OC.js';
|
|
29
|
-
export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-TJ7QY3OC.js';
|
|
30
|
-
export { centerOn, zoomAt } from './chunk-CTRKTLYZ.js';
|
|
27
|
+
import { CameraWheelContext, as2DView, DEFAULT_VIEW, CanvasStack, Stage, withZoom, fitStage } from './chunk-PGETSEDK.js';
|
|
28
|
+
export { CanvasStack, DEFAULT_VIEW, PITCH_LIMIT, Stage, as2DView, clampPitch, fitStage, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-PGETSEDK.js';
|
|
29
|
+
export { centerOn, zoomAt } from './chunk-H6ZAOWNE.js';
|
|
31
30
|
import { applyCamera } from './chunk-2AYGEN57.js';
|
|
32
31
|
export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from './chunk-2AYGEN57.js';
|
|
32
|
+
import { useSurfaceOptional, useSurfaceCanvas, useTileId, SurfaceContext, SurfaceCanvasContext } from './chunk-WIG6XHJ7.js';
|
|
33
|
+
export { SurfaceCanvasContext, SurfaceContext, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTileId } from './chunk-WIG6XHJ7.js';
|
|
33
34
|
export { CanvasStackContext } from './chunk-CQLQPQ4P.js';
|
|
34
|
-
import {
|
|
35
|
+
import { LabStoreContext, TrialIdProvider } from './chunk-2YILQ7Y5.js';
|
|
36
|
+
export { LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, useLabStore, useTrialId } from './chunk-2YILQ7Y5.js';
|
|
37
|
+
import { LayerList } from './chunk-MUKOW3TC.js';
|
|
38
|
+
export { LayerList } from './chunk-MUKOW3TC.js';
|
|
39
|
+
import './chunk-SBXNQW4G.js';
|
|
40
|
+
import { E, w, T, p, u, c, n, f, r2, h, C, l5, r6 } from './chunk-S2SHZRD7.js';
|
|
41
|
+
export { a3 as Button, P3 as CheckboxRow, N2 as ColorRow, b3 as CurveField, B as EffectCard, H as EffectCardList, c3 as LayerStack, I as NumberRow, U2 as PropertyGroup, E2 as PropertyList, T2 as PropertyPanel, O as PropertyRow, D as PropertySpan, L as SelectRow, j2 as SliderRow, z as Subpanel, F as TextRow, r6 as ToggleBar, R as ToggleRow } from './chunk-S2SHZRD7.js';
|
|
42
|
+
import { textCommand, linePath, markerDrawCommands, ellipsePath, rectPath, createScene, asNodeId as asNodeId$1, WeaselProvider, SceneCanvas, sceneFromJSON, PathBuilder, renderSceneToPixels, useActiveToolContext, getMarker, getPaintKind, boundsOfPath, PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, resolveAlign, SCRIPT_METRICS } from '@weasel-js/core';
|
|
35
43
|
import { tileGeometry } from '@weasel-js/core/patterns-builtin';
|
|
36
|
-
import { createContext, useState, useRef, useEffect, useContext, useMemo, Children, useLayoutEffect,
|
|
37
|
-
import { createPortal } from 'react-dom';
|
|
44
|
+
import { createContext, useState, useRef, useEffect, useCallback, useContext, useMemo, useId, Children, useLayoutEffect, useSyncExternalStore } from 'react';
|
|
45
|
+
import { flushSync, createPortal } from 'react-dom';
|
|
38
46
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
39
47
|
import { useStore } from 'zustand/react';
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
48
|
+
import { Provider, StrategyRegistryProvider, DragProvider, Container, useDragHandle } from 'windease/react';
|
|
49
|
+
import { asNodeId, Store, createNode, floatingStrategy, gridStrategy } from 'windease';
|
|
42
50
|
|
|
43
51
|
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
|
44
|
-
function multiply(
|
|
52
|
+
function multiply(a2, b2) {
|
|
45
53
|
return [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
a2[0] * b2[0] + a2[2] * b2[1],
|
|
55
|
+
a2[1] * b2[0] + a2[3] * b2[1],
|
|
56
|
+
a2[0] * b2[2] + a2[2] * b2[3],
|
|
57
|
+
a2[1] * b2[2] + a2[3] * b2[3],
|
|
58
|
+
a2[0] * b2[4] + a2[2] * b2[5] + a2[4],
|
|
59
|
+
a2[1] * b2[4] + a2[3] * b2[5] + a2[5]
|
|
52
60
|
];
|
|
53
61
|
}
|
|
54
62
|
function isIdentity(m, eps = 1e-9) {
|
|
@@ -56,13 +64,17 @@ function isIdentity(m, eps = 1e-9) {
|
|
|
56
64
|
}
|
|
57
65
|
function formatMatrix(m) {
|
|
58
66
|
if (isIdentity(m)) return null;
|
|
59
|
-
return `matrix(${m.map((
|
|
67
|
+
return `matrix(${m.map((n2) => trimNumber(n2)).join(" ")})`;
|
|
68
|
+
}
|
|
69
|
+
function trimNumber(n2) {
|
|
70
|
+
if (!Number.isFinite(n2)) return "0";
|
|
71
|
+
const r3 = Math.round(n2 * 1e6) / 1e6;
|
|
72
|
+
if (Number.isInteger(r3)) return String(r3);
|
|
73
|
+
return String(r3);
|
|
60
74
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (Number.isInteger(r2)) return String(r2);
|
|
65
|
-
return String(r2);
|
|
75
|
+
function anchorOffset(style, width) {
|
|
76
|
+
const edge = resolveAlign(style?.align ?? "left", style?.direction ?? "ltr");
|
|
77
|
+
return edge === "center" ? width / 2 : edge === "right" ? width : 0;
|
|
66
78
|
}
|
|
67
79
|
function patternSpecOf(paint) {
|
|
68
80
|
return "tile" in paint.pattern ? paint.pattern : null;
|
|
@@ -110,32 +122,32 @@ function escapeAttr(s) {
|
|
|
110
122
|
}
|
|
111
123
|
function serializePathD(path) {
|
|
112
124
|
if (path.kind === "rect") {
|
|
113
|
-
const { x, y
|
|
114
|
-
return `M${trimNumber(x)} ${trimNumber(
|
|
125
|
+
const { x, y, width, height } = path;
|
|
126
|
+
return `M${trimNumber(x)} ${trimNumber(y)}h${trimNumber(width)}v${trimNumber(height)}h${trimNumber(-width)}Z`;
|
|
115
127
|
}
|
|
116
128
|
const cmds = path.commands;
|
|
117
129
|
const xs = path.coords;
|
|
118
130
|
const out = [];
|
|
119
131
|
let ci = 0;
|
|
120
132
|
for (let i = 0; i < cmds.length; i++) {
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
133
|
+
const c3 = cmds[i];
|
|
134
|
+
if (c3 === PATH_M) {
|
|
123
135
|
out.push(`M${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])}`);
|
|
124
136
|
ci += 2;
|
|
125
|
-
} else if (
|
|
137
|
+
} else if (c3 === PATH_L) {
|
|
126
138
|
out.push(`L${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])}`);
|
|
127
139
|
ci += 2;
|
|
128
|
-
} else if (
|
|
140
|
+
} else if (c3 === PATH_C) {
|
|
129
141
|
out.push(
|
|
130
142
|
`C${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])} ${trimNumber(xs[ci + 2])} ${trimNumber(xs[ci + 3])} ${trimNumber(xs[ci + 4])} ${trimNumber(xs[ci + 5])}`
|
|
131
143
|
);
|
|
132
144
|
ci += 6;
|
|
133
|
-
} else if (
|
|
145
|
+
} else if (c3 === PATH_Q) {
|
|
134
146
|
out.push(
|
|
135
147
|
`Q${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])} ${trimNumber(xs[ci + 2])} ${trimNumber(xs[ci + 3])}`
|
|
136
148
|
);
|
|
137
149
|
ci += 4;
|
|
138
|
-
} else if (
|
|
150
|
+
} else if (c3 === PATH_Z) {
|
|
139
151
|
out.push("Z");
|
|
140
152
|
}
|
|
141
153
|
}
|
|
@@ -259,7 +271,7 @@ function serializeSvg(nodes, opts = {}) {
|
|
|
259
271
|
}
|
|
260
272
|
}
|
|
261
273
|
const defsXml = registry.toDefsXml(opts.onWarn);
|
|
262
|
-
const bodyXml = nodes.map((
|
|
274
|
+
const bodyXml = nodes.map((n2) => nodeXml(n2, registry, namespaces)).join("");
|
|
263
275
|
const titleXml = opts.title && opts.title.length > 0 ? `<title>${escapeText(opts.title)}</title>` : "";
|
|
264
276
|
return `<svg ${rootAttrs.join(" ")}>${titleXml}${defsXml}${docMetaXml}${bodyXml}</svg>`;
|
|
265
277
|
}
|
|
@@ -304,18 +316,18 @@ function metaElementsXml(meta, namespaces) {
|
|
|
304
316
|
return out;
|
|
305
317
|
}
|
|
306
318
|
function registerPaintServers(nodes, registry) {
|
|
307
|
-
for (const
|
|
308
|
-
if (
|
|
309
|
-
registerPaintServers(
|
|
310
|
-
} else if (
|
|
311
|
-
if (
|
|
312
|
-
if (
|
|
313
|
-
registerMarkers(
|
|
314
|
-
} else if (
|
|
315
|
-
registerTextPaint(
|
|
316
|
-
registerTextPaint(
|
|
317
|
-
registerMarkers(
|
|
318
|
-
for (const run of
|
|
319
|
+
for (const n2 of nodes) {
|
|
320
|
+
if (n2.kind === "group") {
|
|
321
|
+
registerPaintServers(n2.children, registry);
|
|
322
|
+
} else if (n2.kind === "path") {
|
|
323
|
+
if (n2.fill.kind === "gradient") registry.register(n2.fill.paint);
|
|
324
|
+
if (n2.stroke && n2.stroke.paint.kind === "gradient") registry.register(n2.stroke.paint.paint);
|
|
325
|
+
registerMarkers(n2.stroke, registry);
|
|
326
|
+
} else if (n2.kind === "text") {
|
|
327
|
+
registerTextPaint(n2.fill, registry);
|
|
328
|
+
registerTextPaint(n2.stroke?.paint, registry);
|
|
329
|
+
registerMarkers(n2.stroke, registry);
|
|
330
|
+
for (const run of n2.runs ?? []) {
|
|
319
331
|
registerTextPaint(run.fill, registry);
|
|
320
332
|
registerTextPaint(run.stroke?.paint, registry);
|
|
321
333
|
}
|
|
@@ -377,19 +389,19 @@ function groupXml(node, registry, namespaces) {
|
|
|
377
389
|
}
|
|
378
390
|
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
379
391
|
const head = attrs.length > 0 ? `<g ${attrs.join(" ")}${metaAttrs}>` : `<g${metaAttrs}>`;
|
|
380
|
-
const body = node.children.map((
|
|
392
|
+
const body = node.children.map((c3) => nodeXml(c3, registry, namespaces)).join("");
|
|
381
393
|
return `${head}${body}${metaElementsXml(node.meta, namespaces)}</g>`;
|
|
382
394
|
}
|
|
383
395
|
function pathXml(node, registry, namespaces) {
|
|
384
396
|
const attrs = [`d="${serializePathD(node.path)}"`];
|
|
385
397
|
const fillAttrs = paintAttrs(node.fill, "fill", registry);
|
|
386
|
-
for (const
|
|
398
|
+
for (const a2 of fillAttrs) attrs.push(a2);
|
|
387
399
|
if (node.path.kind === "polygon" && node.path.fillRule === "evenodd") {
|
|
388
400
|
attrs.push(`fill-rule="evenodd"`);
|
|
389
401
|
}
|
|
390
402
|
if (node.stroke) {
|
|
391
403
|
const strokeAttrs = strokeAttrsFor(node.stroke, registry);
|
|
392
|
-
for (const
|
|
404
|
+
for (const a2 of strokeAttrs) attrs.push(a2);
|
|
393
405
|
} else {
|
|
394
406
|
attrs.push('stroke="none"');
|
|
395
407
|
}
|
|
@@ -397,9 +409,9 @@ function pathXml(node, registry, namespaces) {
|
|
|
397
409
|
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
398
410
|
}
|
|
399
411
|
if (node.rotation != null && node.rotation !== 0) {
|
|
400
|
-
const
|
|
401
|
-
const cx =
|
|
402
|
-
const cy =
|
|
412
|
+
const b2 = pathBounds(node.path);
|
|
413
|
+
const cx = b2.minX + (b2.maxX - b2.minX) / 2;
|
|
414
|
+
const cy = b2.minY + (b2.maxY - b2.minY) / 2;
|
|
403
415
|
const deg = node.rotation * 180 / Math.PI;
|
|
404
416
|
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
405
417
|
}
|
|
@@ -422,11 +434,15 @@ function paintAttrs(paint, name, registry, includeOpacity = true) {
|
|
|
422
434
|
const id = registry.register(paint.paint);
|
|
423
435
|
return [`${name}="url(#${id})"`];
|
|
424
436
|
}
|
|
437
|
+
function strokeWidthAttrs(width) {
|
|
438
|
+
if (typeof width === "number") return [`stroke-width="${trimNumber(width)}"`];
|
|
439
|
+
return [`stroke-width="${trimNumber(width.px)}"`, 'vector-effect="non-scaling-stroke"'];
|
|
440
|
+
}
|
|
425
441
|
function coreStrokeAttrs(stroke, registry) {
|
|
426
442
|
const paint = stroke?.paint;
|
|
427
443
|
if (!stroke || !paint) return [];
|
|
428
|
-
const width =
|
|
429
|
-
if (!(width > 0)) return [];
|
|
444
|
+
const width = stroke.width ?? 1;
|
|
445
|
+
if (!((typeof width === "object" ? width.px : width) > 0)) return [];
|
|
430
446
|
const attrs = [];
|
|
431
447
|
if ("color" in paint) {
|
|
432
448
|
attrs.push(`stroke="${paint.color}"`);
|
|
@@ -436,7 +452,7 @@ function coreStrokeAttrs(stroke, registry) {
|
|
|
436
452
|
} else {
|
|
437
453
|
attrs.push(`stroke="url(#${registry.register(paint)})"`);
|
|
438
454
|
}
|
|
439
|
-
attrs.push(
|
|
455
|
+
attrs.push(...strokeWidthAttrs(width));
|
|
440
456
|
if (stroke.cap) attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
441
457
|
if (stroke.join) attrs.push(`stroke-linejoin="${stroke.join}"`);
|
|
442
458
|
if (stroke.dash && stroke.dash.length > 0) {
|
|
@@ -459,7 +475,7 @@ function coreStrokeAttrs(stroke, registry) {
|
|
|
459
475
|
}
|
|
460
476
|
function strokeAttrsFor(stroke, registry) {
|
|
461
477
|
const attrs = paintAttrs(stroke.paint, "stroke", registry, false);
|
|
462
|
-
attrs.push(
|
|
478
|
+
attrs.push(...strokeWidthAttrs(stroke.width));
|
|
463
479
|
const opacity = stroke.opacity ?? (stroke.paint.kind === "solid" ? stroke.paint.opacity : void 0);
|
|
464
480
|
if (opacity != null && opacity !== 1) {
|
|
465
481
|
attrs.push(`stroke-opacity="${trimNumber(opacity)}"`);
|
|
@@ -491,42 +507,42 @@ function strokeAttrsFor(stroke, registry) {
|
|
|
491
507
|
}
|
|
492
508
|
function computeBounds(nodes) {
|
|
493
509
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
494
|
-
const visit = (
|
|
495
|
-
if (
|
|
496
|
-
const childCtm =
|
|
497
|
-
|
|
510
|
+
const visit = (n2, ctm) => {
|
|
511
|
+
if (n2.kind === "group") {
|
|
512
|
+
const childCtm = n2.transform ? multiply(ctm, n2.transform) : ctm;
|
|
513
|
+
n2.children.forEach((c3) => visit(c3, childCtm));
|
|
498
514
|
return;
|
|
499
515
|
}
|
|
500
|
-
const
|
|
501
|
-
const local =
|
|
502
|
-
for (const [x,
|
|
503
|
-
const px = local[0] * x + local[2] *
|
|
504
|
-
const py = local[1] * x + local[3] *
|
|
516
|
+
const b2 = n2.kind === "text" || n2.kind === "image" ? { minX: n2.x, minY: n2.y, maxX: n2.x + n2.width, maxY: n2.y + n2.height } : pathBounds(n2.path);
|
|
517
|
+
const local = n2.rotation ? rotateAboutCenter(ctm, b2, n2.rotation) : ctm;
|
|
518
|
+
for (const [x, y] of corners(b2)) {
|
|
519
|
+
const px = local[0] * x + local[2] * y + local[4];
|
|
520
|
+
const py = local[1] * x + local[3] * y + local[5];
|
|
505
521
|
if (px < minX) minX = px;
|
|
506
522
|
if (py < minY) minY = py;
|
|
507
523
|
if (px > maxX) maxX = px;
|
|
508
524
|
if (py > maxY) maxY = py;
|
|
509
525
|
}
|
|
510
526
|
};
|
|
511
|
-
nodes.forEach((
|
|
527
|
+
nodes.forEach((n2) => visit(n2, IDENTITY_MATRIX));
|
|
512
528
|
if (!Number.isFinite(minX)) {
|
|
513
529
|
return { x: 0, y: 0, width: 0, height: 0 };
|
|
514
530
|
}
|
|
515
531
|
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
516
532
|
}
|
|
517
|
-
function corners(
|
|
518
|
-
return [[
|
|
533
|
+
function corners(b2) {
|
|
534
|
+
return [[b2.minX, b2.minY], [b2.maxX, b2.minY], [b2.maxX, b2.maxY], [b2.minX, b2.maxY]];
|
|
519
535
|
}
|
|
520
|
-
function rotateAboutCenter(ctm,
|
|
521
|
-
const cx = (
|
|
522
|
-
const cy = (
|
|
523
|
-
const
|
|
536
|
+
function rotateAboutCenter(ctm, b2, angle) {
|
|
537
|
+
const cx = (b2.minX + b2.maxX) / 2;
|
|
538
|
+
const cy = (b2.minY + b2.maxY) / 2;
|
|
539
|
+
const c3 = Math.cos(angle);
|
|
524
540
|
const s = Math.sin(angle);
|
|
525
|
-
return multiply(ctm, [
|
|
541
|
+
return multiply(ctm, [c3, s, -s, c3, cx - cx * c3 + cy * s, cy - cx * s - cy * c3]);
|
|
526
542
|
}
|
|
527
543
|
function textXml(node, registry, namespaces) {
|
|
528
544
|
const attrs = [
|
|
529
|
-
`x="${trimNumber(node.x)}"`,
|
|
545
|
+
`x="${trimNumber(node.x + anchorOffset(node.style, node.width))}"`,
|
|
530
546
|
`y="${trimNumber(node.y)}"`,
|
|
531
547
|
`dominant-baseline="text-before-edge"`,
|
|
532
548
|
// The runs model stores real line breaks; SVG's default whitespace
|
|
@@ -552,6 +568,7 @@ function textXml(node, registry, namespaces) {
|
|
|
552
568
|
}
|
|
553
569
|
const decoration = textDecorationValue(style?.underline, style?.strikethrough, style?.overline);
|
|
554
570
|
if (decoration) attrs.push(`text-decoration="${decoration}"`);
|
|
571
|
+
if (style?.wrap) attrs.push('data-weasel-wrap="true"');
|
|
555
572
|
if (node.fill === null) {
|
|
556
573
|
attrs.push('fill="none"');
|
|
557
574
|
} else if (node.fill) {
|
|
@@ -565,7 +582,7 @@ function textXml(node, registry, namespaces) {
|
|
|
565
582
|
attrs.push(`fill="url(#${id})"`);
|
|
566
583
|
}
|
|
567
584
|
}
|
|
568
|
-
for (const
|
|
585
|
+
for (const a2 of coreStrokeAttrs(node.stroke, registry)) attrs.push(a2);
|
|
569
586
|
if (node.opacity != null && node.opacity !== 1) {
|
|
570
587
|
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
571
588
|
}
|
|
@@ -575,7 +592,7 @@ function textXml(node, registry, namespaces) {
|
|
|
575
592
|
const deg = node.rotation * 180 / Math.PI;
|
|
576
593
|
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
577
594
|
}
|
|
578
|
-
const body = node.runs && node.runs.length > 0 ? node.runs.map((
|
|
595
|
+
const body = node.runs && node.runs.length > 0 ? node.runs.map((r3) => runXml(r3, registry)).join("") : escapeText(node.text);
|
|
579
596
|
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
580
597
|
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
581
598
|
return `<text ${attrs.join(" ")}${metaAttrs}>${body}${metaEls}</text>`;
|
|
@@ -607,7 +624,7 @@ function runXml(run, registry) {
|
|
|
607
624
|
attrs.push(`fill="url(#${id})"`);
|
|
608
625
|
}
|
|
609
626
|
}
|
|
610
|
-
for (const
|
|
627
|
+
for (const a2 of coreStrokeAttrs(run.stroke, registry)) attrs.push(a2);
|
|
611
628
|
const head = attrs.length > 0 ? `<tspan ${attrs.join(" ")}>` : "<tspan>";
|
|
612
629
|
return `${head}${escapeText(run.text)}</tspan>`;
|
|
613
630
|
}
|
|
@@ -633,32 +650,32 @@ function pathBounds(path) {
|
|
|
633
650
|
maxY: path.y + path.height
|
|
634
651
|
};
|
|
635
652
|
}
|
|
636
|
-
const
|
|
637
|
-
return { minX:
|
|
653
|
+
const b2 = boundsOfPath(path);
|
|
654
|
+
return { minX: b2.x, minY: b2.y, maxX: b2.x + b2.width, maxY: b2.y + b2.height };
|
|
638
655
|
}
|
|
639
656
|
var MARK_COLOR = "#e5484d";
|
|
640
657
|
var MARK_WIDTH = 2;
|
|
641
658
|
var TEXT_SIZE = 14;
|
|
642
|
-
var toWorld = (
|
|
643
|
-
x:
|
|
644
|
-
y:
|
|
659
|
+
var toWorld = (p2, content) => ({
|
|
660
|
+
x: p2.x * content.w,
|
|
661
|
+
y: p2.y * content.h
|
|
645
662
|
});
|
|
646
663
|
function vertices(m, content) {
|
|
647
664
|
const stored = m.data.points;
|
|
648
|
-
if (stored && stored.length >= 2) return stored.map((
|
|
649
|
-
const { x, y
|
|
665
|
+
if (stored && stored.length >= 2) return stored.map((p2) => toWorld(p2, content));
|
|
666
|
+
const { x, y, width, height } = m.pose;
|
|
650
667
|
return [
|
|
651
|
-
{ x, y
|
|
652
|
-
{ x: x + width, y:
|
|
668
|
+
{ x, y },
|
|
669
|
+
{ x: x + width, y: y + height }
|
|
653
670
|
];
|
|
654
671
|
}
|
|
655
672
|
function polyline(points) {
|
|
656
|
-
const
|
|
673
|
+
const b2 = new PathBuilder();
|
|
657
674
|
const first = points[0];
|
|
658
675
|
if (!first) return rectPath(0, 0, 0, 0);
|
|
659
|
-
|
|
660
|
-
for (const
|
|
661
|
-
return
|
|
676
|
+
b2.moveTo(first.x, first.y);
|
|
677
|
+
for (const p2 of points.slice(1)) b2.lineTo(p2.x, p2.y);
|
|
678
|
+
return b2.build();
|
|
662
679
|
}
|
|
663
680
|
function markCommands(m, content, style = {}) {
|
|
664
681
|
const color = style.color ?? MARK_COLOR;
|
|
@@ -677,13 +694,13 @@ function markCommands(m, content, style = {}) {
|
|
|
677
694
|
case "ellipse":
|
|
678
695
|
return [{ kind: "path", path: ellipsePath(m.pose), stroke }];
|
|
679
696
|
case "line": {
|
|
680
|
-
const [
|
|
681
|
-
return [{ kind: "path", path: linePath(
|
|
697
|
+
const [a2, b2] = vertices(m, content);
|
|
698
|
+
return [{ kind: "path", path: linePath(a2, b2), stroke }];
|
|
682
699
|
}
|
|
683
700
|
case "arrow": {
|
|
684
|
-
const [
|
|
701
|
+
const [a2, b2] = vertices(m, content);
|
|
685
702
|
const marked = { ...stroke, markerEnd: "arrow" };
|
|
686
|
-
const path = linePath(
|
|
703
|
+
const path = linePath(a2, b2);
|
|
687
704
|
return [
|
|
688
705
|
{ kind: "path", path, stroke: marked },
|
|
689
706
|
...markerDrawCommands(path, marked, MARK_WIDTH, void 0)
|
|
@@ -717,8 +734,8 @@ function seenFrom(config, keys) {
|
|
|
717
734
|
const out = {};
|
|
718
735
|
if (config === null || typeof config !== "object") return out;
|
|
719
736
|
const src = config;
|
|
720
|
-
for (const
|
|
721
|
-
if (
|
|
737
|
+
for (const k of keys) {
|
|
738
|
+
if (k in src) out[k] = src[k];
|
|
722
739
|
}
|
|
723
740
|
return out;
|
|
724
741
|
}
|
|
@@ -726,9 +743,9 @@ function isStale(seen, config, keys) {
|
|
|
726
743
|
if (!seen || keys.length === 0) return false;
|
|
727
744
|
if (config === null || typeof config !== "object") return false;
|
|
728
745
|
const src = config;
|
|
729
|
-
for (const
|
|
730
|
-
if (!(
|
|
731
|
-
if (!Object.is(seen[
|
|
746
|
+
for (const k of keys) {
|
|
747
|
+
if (!(k in seen)) continue;
|
|
748
|
+
if (!Object.is(seen[k], src[k])) return true;
|
|
732
749
|
}
|
|
733
750
|
return false;
|
|
734
751
|
}
|
|
@@ -745,6 +762,8 @@ function resolveMarkStyle(data, opts) {
|
|
|
745
762
|
stale: isStale(data.seen, opts.config, opts.positionDependsOn ?? [])
|
|
746
763
|
};
|
|
747
764
|
}
|
|
765
|
+
|
|
766
|
+
// src/annotations/svgNodes.ts
|
|
748
767
|
var NO_PAINT = { kind: "none" };
|
|
749
768
|
function toSvgPaint(paint) {
|
|
750
769
|
if (!paint) return NO_PAINT;
|
|
@@ -760,7 +779,7 @@ function toSvgPaint(paint) {
|
|
|
760
779
|
function toSvgStroke(stroke) {
|
|
761
780
|
return {
|
|
762
781
|
paint: toSvgPaint(stroke.paint),
|
|
763
|
-
width:
|
|
782
|
+
width: stroke.width ?? 1,
|
|
764
783
|
...stroke.cap ? { cap: stroke.cap } : {},
|
|
765
784
|
...stroke.join ? { join: stroke.join } : {},
|
|
766
785
|
...stroke.dash ? { dash: [...stroke.dash] } : {},
|
|
@@ -800,7 +819,7 @@ function capturePlan(base, format) {
|
|
|
800
819
|
if (format === "svg") return "svg-document";
|
|
801
820
|
return base?.kind === "svg" ? "svg-document" : "raster-stack";
|
|
802
821
|
}
|
|
803
|
-
function nestBaseSvg(markup,
|
|
822
|
+
function nestBaseSvg(markup, w2, h2) {
|
|
804
823
|
const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
|
|
805
824
|
const root = doc.documentElement;
|
|
806
825
|
if (doc.getElementsByTagName("parsererror").length > 0 || root.localName !== "svg") {
|
|
@@ -808,17 +827,17 @@ function nestBaseSvg(markup, w3, h) {
|
|
|
808
827
|
}
|
|
809
828
|
root.setAttribute("x", "0");
|
|
810
829
|
root.setAttribute("y", "0");
|
|
811
|
-
root.setAttribute("width", String(
|
|
812
|
-
root.setAttribute("height", String(
|
|
830
|
+
root.setAttribute("width", String(w2));
|
|
831
|
+
root.setAttribute("height", String(h2));
|
|
813
832
|
return new XMLSerializer().serializeToString(root);
|
|
814
833
|
}
|
|
815
|
-
function embedRasterBase(base,
|
|
834
|
+
function embedRasterBase(base, w2, h2, onWarn) {
|
|
816
835
|
const href = base.kind === "canvas" ? base.canvas.toDataURL() : base.kind === "image" ? base.src : "";
|
|
817
836
|
if (base.kind === "image" && !href.startsWith("data:")) {
|
|
818
837
|
onWarn?.(`base image '${href}' is a reference, so this SVG is not self-contained`);
|
|
819
838
|
}
|
|
820
|
-
return serializeSvg([{ kind: "image", href, x: 0, y: 0, width:
|
|
821
|
-
viewBox: { x: 0, y: 0, width:
|
|
839
|
+
return serializeSvg([{ kind: "image", href, x: 0, y: 0, width: w2, height: h2 }], {
|
|
840
|
+
viewBox: { x: 0, y: 0, width: w2, height: h2 }
|
|
822
841
|
});
|
|
823
842
|
}
|
|
824
843
|
function markNodesOf(scene, draw) {
|
|
@@ -834,15 +853,15 @@ function markNodesOf(scene, draw) {
|
|
|
834
853
|
}
|
|
835
854
|
function composeCaptureSvg(args) {
|
|
836
855
|
const { base, scene, draw, scale, onWarn } = args;
|
|
837
|
-
const { w:
|
|
838
|
-
const baseXml = base === void 0 ? "" : base.kind === "svg" ? nestBaseSvg(base.markup,
|
|
856
|
+
const { w: w2, h: h2 } = draw.content;
|
|
857
|
+
const baseXml = base === void 0 ? "" : base.kind === "svg" ? nestBaseSvg(base.markup, w2, h2) : embedRasterBase(base, w2, h2, onWarn);
|
|
839
858
|
const marksXml = serializeSvg(markNodesOf(scene, draw), {
|
|
840
|
-
viewBox: { x: 0, y: 0, width:
|
|
859
|
+
viewBox: { x: 0, y: 0, width: w2, height: h2 },
|
|
841
860
|
onWarn
|
|
842
861
|
});
|
|
843
|
-
const outW = Math.max(1, Math.round(
|
|
844
|
-
const outH = Math.max(1, Math.round(
|
|
845
|
-
return `<svg xmlns="${SVG_NS}" width="${outW}" height="${outH}" viewBox="0 0 ${
|
|
862
|
+
const outW = Math.max(1, Math.round(w2 * scale));
|
|
863
|
+
const outH = Math.max(1, Math.round(h2 * scale));
|
|
864
|
+
return `<svg xmlns="${SVG_NS}" width="${outW}" height="${outH}" viewBox="0 0 ${w2} ${h2}">${baseXml}${marksXml}</svg>`;
|
|
846
865
|
}
|
|
847
866
|
function context2d(width, height) {
|
|
848
867
|
const canvas = document.createElement("canvas");
|
|
@@ -936,32 +955,32 @@ async function captureTarget(args, opts = {}) {
|
|
|
936
955
|
}
|
|
937
956
|
|
|
938
957
|
// src/annotations/frac.ts
|
|
939
|
-
function fracToWorld(
|
|
958
|
+
function fracToWorld(f3, content) {
|
|
940
959
|
return {
|
|
941
|
-
x:
|
|
942
|
-
y:
|
|
943
|
-
width:
|
|
944
|
-
height:
|
|
960
|
+
x: f3.x * content.w,
|
|
961
|
+
y: f3.y * content.h,
|
|
962
|
+
width: f3.w * content.w,
|
|
963
|
+
height: f3.h * content.h
|
|
945
964
|
};
|
|
946
965
|
}
|
|
947
|
-
function worldToFrac(
|
|
966
|
+
function worldToFrac(r3, content) {
|
|
948
967
|
const sx = content.w === 0 ? 0 : 1 / content.w;
|
|
949
968
|
const sy = content.h === 0 ? 0 : 1 / content.h;
|
|
950
|
-
return { x:
|
|
969
|
+
return { x: r3.x * sx, y: r3.y * sy, w: r3.width * sx, h: r3.height * sy };
|
|
951
970
|
}
|
|
952
971
|
var DP = 1e4;
|
|
953
|
-
function roundFrac(
|
|
972
|
+
function roundFrac(f3) {
|
|
954
973
|
return {
|
|
955
|
-
x: Math.round(
|
|
956
|
-
y: Math.round(
|
|
957
|
-
w: Math.round(
|
|
958
|
-
h: Math.round(
|
|
974
|
+
x: Math.round(f3.x * DP) / DP,
|
|
975
|
+
y: Math.round(f3.y * DP) / DP,
|
|
976
|
+
w: Math.round(f3.w * DP) / DP,
|
|
977
|
+
h: Math.round(f3.h * DP) / DP
|
|
959
978
|
};
|
|
960
979
|
}
|
|
961
980
|
function fracContains(box, pt, tol = 0) {
|
|
962
981
|
return pt.x >= box.x - tol && pt.x <= box.x + box.w + tol && pt.y >= box.y - tol && pt.y <= box.y + box.h + tol;
|
|
963
982
|
}
|
|
964
|
-
function
|
|
983
|
+
function fracEncloses(outer, inner) {
|
|
965
984
|
return inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.w <= outer.x + outer.w && inner.y + inner.h <= outer.y + outer.h;
|
|
966
985
|
}
|
|
967
986
|
|
|
@@ -1098,21 +1117,21 @@ function createAnnotationStore(opts) {
|
|
|
1098
1117
|
if (!scene) return [];
|
|
1099
1118
|
const out = [];
|
|
1100
1119
|
for (const id of scene.renderOrder()) {
|
|
1101
|
-
const
|
|
1102
|
-
if (
|
|
1120
|
+
const a2 = project(target, String(id));
|
|
1121
|
+
if (a2) out.push(a2);
|
|
1103
1122
|
}
|
|
1104
1123
|
return out;
|
|
1105
1124
|
};
|
|
1106
1125
|
const all = () => liveTargets().flatMap(marksOn);
|
|
1107
|
-
const matches = (
|
|
1108
|
-
if (q.target !== void 0 &&
|
|
1109
|
-
if (q.kind !== void 0 &&
|
|
1110
|
-
if (q.status !== void 0 &&
|
|
1126
|
+
const matches = (a2, q) => {
|
|
1127
|
+
if (q.target !== void 0 && a2.target !== q.target) return false;
|
|
1128
|
+
if (q.kind !== void 0 && a2.kind !== q.kind) return false;
|
|
1129
|
+
if (q.status !== void 0 && a2.status !== q.status) return false;
|
|
1111
1130
|
if (q.tags !== void 0) {
|
|
1112
|
-
const have =
|
|
1131
|
+
const have = a2.tags ?? [];
|
|
1113
1132
|
for (const t of q.tags) if (!have.includes(t)) return false;
|
|
1114
1133
|
}
|
|
1115
|
-
if (q.where && !q.where(
|
|
1134
|
+
if (q.where && !q.where(a2)) return false;
|
|
1116
1135
|
return true;
|
|
1117
1136
|
};
|
|
1118
1137
|
const nodeOf = (id) => {
|
|
@@ -1130,17 +1149,17 @@ function createAnnotationStore(opts) {
|
|
|
1130
1149
|
return parts ? project(parts.target, parts.node) : void 0;
|
|
1131
1150
|
},
|
|
1132
1151
|
query(q) {
|
|
1133
|
-
return q ? all().filter((
|
|
1152
|
+
return q ? all().filter((a2) => matches(a2, q)) : all();
|
|
1134
1153
|
},
|
|
1135
1154
|
hitTest(target, pt, tol = 0) {
|
|
1136
|
-
return marksOn(target).filter((
|
|
1155
|
+
return marksOn(target).filter((a2) => fracContains(a2.frac, pt, tol)).reverse();
|
|
1137
1156
|
},
|
|
1138
1157
|
within(target, box) {
|
|
1139
|
-
return marksOn(target).filter((
|
|
1158
|
+
return marksOn(target).filter((a2) => fracEncloses(box, a2.frac));
|
|
1140
1159
|
},
|
|
1141
|
-
isStale(
|
|
1142
|
-
const keys = targetOf(
|
|
1143
|
-
return isStale(
|
|
1160
|
+
isStale(a2, config) {
|
|
1161
|
+
const keys = targetOf(a2.target)?.positionDependsOn ?? [];
|
|
1162
|
+
return isStale(a2.seen, config, keys);
|
|
1144
1163
|
},
|
|
1145
1164
|
canUndo() {
|
|
1146
1165
|
return history.canUndo(historySceneAt);
|
|
@@ -1178,7 +1197,7 @@ function createAnnotationStore(opts) {
|
|
|
1178
1197
|
for (const [target, scene] of scenes) {
|
|
1179
1198
|
const next = wanted.get(target) ?? [];
|
|
1180
1199
|
const now = scene.getSelection();
|
|
1181
|
-
if (now.length === next.length && next.every((
|
|
1200
|
+
if (now.length === next.length && next.every((n2, i) => String(now[i]) === n2)) continue;
|
|
1182
1201
|
scene.setSelection(next.map(asNodeId$1));
|
|
1183
1202
|
}
|
|
1184
1203
|
},
|
|
@@ -1240,7 +1259,7 @@ function createAnnotationStore(opts) {
|
|
|
1240
1259
|
content: info.content,
|
|
1241
1260
|
positionDependsOn: info.positionDependsOn,
|
|
1242
1261
|
config: opts.config?.(),
|
|
1243
|
-
meaning: opts.meaning
|
|
1262
|
+
meaning: typeof opts.meaning === "function" ? opts.meaning() : opts.meaning
|
|
1244
1263
|
},
|
|
1245
1264
|
base: info.base,
|
|
1246
1265
|
onWarn: (message) => console.warn(`[labkit] capture: ${message}`)
|
|
@@ -1263,6 +1282,32 @@ function annotationsFromJSON(raw, targets, rest = {}) {
|
|
|
1263
1282
|
return createAnnotationStore({ targets, restore: doc.scenes, ...rest });
|
|
1264
1283
|
}
|
|
1265
1284
|
|
|
1285
|
+
// src/annotations/toolMap.ts
|
|
1286
|
+
var TOOLS = {
|
|
1287
|
+
select: { weaselTool: "select" },
|
|
1288
|
+
stroke: { weaselTool: "pencil", kind: "stroke" },
|
|
1289
|
+
line: { weaselTool: "line", kind: "line" },
|
|
1290
|
+
arrow: { weaselTool: "line", kind: "arrow" },
|
|
1291
|
+
rect: { weaselTool: "rect", kind: "rect" },
|
|
1292
|
+
ellipse: { weaselTool: "ellipse", kind: "ellipse" },
|
|
1293
|
+
text: { weaselTool: "text", kind: "text" }
|
|
1294
|
+
};
|
|
1295
|
+
var ANNOTATION_TOOLS = [
|
|
1296
|
+
{ id: "select", label: "Select", icon: p, group: "annotate" },
|
|
1297
|
+
{ id: "stroke", label: "Freehand", icon: u, group: "annotate" },
|
|
1298
|
+
{ id: "line", label: "Line", icon: c, group: "annotate" },
|
|
1299
|
+
{ id: "arrow", label: "Arrow", icon: n, group: "annotate" },
|
|
1300
|
+
{ id: "rect", label: "Rectangle", icon: f, group: "annotate" },
|
|
1301
|
+
{ id: "ellipse", label: "Ellipse", icon: r2, group: "annotate" },
|
|
1302
|
+
{ id: "text", label: "Text", icon: h, group: "annotate" }
|
|
1303
|
+
];
|
|
1304
|
+
function annotationToolInfo(id) {
|
|
1305
|
+
return id === null ? void 0 : TOOLS[id];
|
|
1306
|
+
}
|
|
1307
|
+
var ANNOTATION_WEASEL_TOOLS = [
|
|
1308
|
+
...new Set(Object.values(TOOLS).map((t) => t.weaselTool))
|
|
1309
|
+
];
|
|
1310
|
+
|
|
1266
1311
|
// src/annotations/view.ts
|
|
1267
1312
|
function toWeaselView(v) {
|
|
1268
1313
|
return { x: v.pan.x, y: v.pan.y, scale: { x: v.zoom, y: v.zoom } };
|
|
@@ -1274,7 +1319,7 @@ function fitView(content, pane) {
|
|
|
1274
1319
|
const zoom = content.w > 0 && content.h > 0 ? Math.min(pane.w / content.w, pane.h / content.h) : 1;
|
|
1275
1320
|
return { zoom: Number.isFinite(zoom) && zoom > 0 ? zoom : 1, pan: { x: 0, y: 0 } };
|
|
1276
1321
|
}
|
|
1277
|
-
var sameRect = (
|
|
1322
|
+
var sameRect = (a2, b2) => a2 !== null && a2.x === b2.x && a2.y === b2.y && a2.w === b2.w && a2.h === b2.h;
|
|
1278
1323
|
function ToolBridge({ toolId }) {
|
|
1279
1324
|
const tools = useActiveToolContext();
|
|
1280
1325
|
const setActive = tools.setActive;
|
|
@@ -1284,9 +1329,9 @@ function ToolBridge({ toolId }) {
|
|
|
1284
1329
|
return null;
|
|
1285
1330
|
}
|
|
1286
1331
|
function pointsOf(extras, content) {
|
|
1287
|
-
const toFrac = (
|
|
1288
|
-
x: content.w === 0 ? 0 :
|
|
1289
|
-
y: content.h === 0 ? 0 :
|
|
1332
|
+
const toFrac = (p2) => ({
|
|
1333
|
+
x: content.w === 0 ? 0 : p2.x / content.w,
|
|
1334
|
+
y: content.h === 0 ? 0 : p2.y / content.h
|
|
1290
1335
|
});
|
|
1291
1336
|
if (extras.kind === "line") {
|
|
1292
1337
|
return [toFrac(extras.a), toFrac(extras.b)];
|
|
@@ -1328,9 +1373,40 @@ function AnnotationOverlay({
|
|
|
1328
1373
|
},
|
|
1329
1374
|
[surface, tileId]
|
|
1330
1375
|
);
|
|
1376
|
+
const painted = useRef(false);
|
|
1377
|
+
const canvasRef = useRef(canvas);
|
|
1378
|
+
if (canvasRef.current !== canvas) painted.current = false;
|
|
1379
|
+
canvasRef.current = canvas;
|
|
1380
|
+
const unsubscribeFrame = useRef(null);
|
|
1381
|
+
const attachSceneCanvas = useCallback((api) => {
|
|
1382
|
+
sceneCanvas.current = api;
|
|
1383
|
+
unsubscribeFrame.current?.();
|
|
1384
|
+
unsubscribeFrame.current = api ? api.subscribeFrame(() => {
|
|
1385
|
+
painted.current = true;
|
|
1386
|
+
}) : null;
|
|
1387
|
+
}, []);
|
|
1388
|
+
useEffect(() => {
|
|
1389
|
+
if (!surface) return;
|
|
1390
|
+
return surface.registerClear(tileId, (size, dpr) => {
|
|
1391
|
+
if (!painted.current) return;
|
|
1392
|
+
const gl = canvasRef.current?.getContext("webgl2");
|
|
1393
|
+
if (!gl) return;
|
|
1394
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
1395
|
+
gl.viewport(0, 0, Math.round(size.width * dpr), Math.round(size.height * dpr));
|
|
1396
|
+
gl.clearColor(0, 0, 0, 0);
|
|
1397
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
1398
|
+
});
|
|
1399
|
+
}, [surface, tileId]);
|
|
1400
|
+
const wheelSlot = useContext(CameraWheelContext);
|
|
1401
|
+
useEffect(() => {
|
|
1402
|
+
if (!input || !wheelSlot) return;
|
|
1403
|
+
const wheel = (e) => wheelSlot.current?.(e);
|
|
1404
|
+
input.addEventListener("wheel", wheel, { passive: false });
|
|
1405
|
+
return () => input.removeEventListener("wheel", wheel);
|
|
1406
|
+
}, [input, wheelSlot]);
|
|
1331
1407
|
useEffect(
|
|
1332
1408
|
() => surface?.registerPainter(tileId, (next) => {
|
|
1333
|
-
setRect((prev) => sameRect(prev, next) ? prev : next);
|
|
1409
|
+
flushSync(() => setRect((prev) => sameRect(prev, next) ? prev : next));
|
|
1334
1410
|
sceneCanvas.current?.requestRedraw();
|
|
1335
1411
|
}),
|
|
1336
1412
|
[surface, tileId]
|
|
@@ -1388,7 +1464,7 @@ function AnnotationOverlay({
|
|
|
1388
1464
|
/* @__PURE__ */ jsx(
|
|
1389
1465
|
SceneCanvas,
|
|
1390
1466
|
{
|
|
1391
|
-
ref:
|
|
1467
|
+
ref: attachSceneCanvas,
|
|
1392
1468
|
scene,
|
|
1393
1469
|
width: rect.w,
|
|
1394
1470
|
height: rect.h,
|
|
@@ -1428,314 +1504,6 @@ function AnnotationTargets({
|
|
|
1428
1504
|
)) });
|
|
1429
1505
|
}
|
|
1430
1506
|
|
|
1431
|
-
// src/config/builder.ts
|
|
1432
|
-
function isConfigBranch(entry) {
|
|
1433
|
-
return "children" in entry;
|
|
1434
|
-
}
|
|
1435
|
-
var BaseNode = class {
|
|
1436
|
-
constructor(defaultValue, annotations = {}, options = {}) {
|
|
1437
|
-
this.annotations = annotations;
|
|
1438
|
-
this.options = options;
|
|
1439
|
-
this.default = defaultValue;
|
|
1440
|
-
}
|
|
1441
|
-
annotations;
|
|
1442
|
-
options;
|
|
1443
|
-
// `default` is a reserved word in a parameter position, so the field is
|
|
1444
|
-
// declared rather than taken as a parameter property.
|
|
1445
|
-
default;
|
|
1446
|
-
/** Clone into the same subclass. Subclasses with extra constructor
|
|
1447
|
-
* arguments override this. */
|
|
1448
|
-
with(annotations, options) {
|
|
1449
|
-
const Ctor = this.constructor;
|
|
1450
|
-
return new Ctor(this.default, annotations, options);
|
|
1451
|
-
}
|
|
1452
|
-
ann(patch) {
|
|
1453
|
-
return this.with({ ...this.annotations, ...patch }, this.options);
|
|
1454
|
-
}
|
|
1455
|
-
opt(patch) {
|
|
1456
|
-
return this.with(this.annotations, { ...this.options, ...patch });
|
|
1457
|
-
}
|
|
1458
|
-
/** Human-readable label. Defaults to the key, title-cased. */
|
|
1459
|
-
label(name) {
|
|
1460
|
-
return this.ann({ name });
|
|
1461
|
-
}
|
|
1462
|
-
/** Longer help text, surfaced as a tooltip on the row label. */
|
|
1463
|
-
describe(description) {
|
|
1464
|
-
return this.ann({ description });
|
|
1465
|
-
}
|
|
1466
|
-
/** Omit from the panel unless it is asked to show hidden leaves. */
|
|
1467
|
-
hidden() {
|
|
1468
|
-
return this.ann({ hidden: true });
|
|
1469
|
-
}
|
|
1470
|
-
/** Pair with a sibling sharing this id, side-by-side on one row. */
|
|
1471
|
-
pair(pair) {
|
|
1472
|
-
return this.ann({ pair });
|
|
1473
|
-
}
|
|
1474
|
-
/** Render under a named section heading. `collapsed` opens the section
|
|
1475
|
-
* folded — say it on any one of the section's leaves. */
|
|
1476
|
-
section(label, opts = {}) {
|
|
1477
|
-
return this.opt({ section: { label, ...opts } });
|
|
1478
|
-
}
|
|
1479
|
-
/** Show this row only while the predicate holds. Presentational — the value
|
|
1480
|
-
* stays in config and the instrument still reads it. */
|
|
1481
|
-
showIf(predicate) {
|
|
1482
|
-
return this.opt({ showIf: predicate });
|
|
1483
|
-
}
|
|
1484
|
-
/** Draw this one row yourself, keeping the kind, default and validation. */
|
|
1485
|
-
render(renderer) {
|
|
1486
|
-
return this.opt({ render: renderer });
|
|
1487
|
-
}
|
|
1488
|
-
};
|
|
1489
|
-
var NumberNode = class extends BaseNode {
|
|
1490
|
-
kind = "number";
|
|
1491
|
-
/** Bound the value. A number with both bounds renders as a slider. */
|
|
1492
|
-
range(min, max) {
|
|
1493
|
-
return this.ann({ min, max });
|
|
1494
|
-
}
|
|
1495
|
-
step(step) {
|
|
1496
|
-
return this.ann({ step });
|
|
1497
|
-
}
|
|
1498
|
-
/** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:
|
|
1499
|
-
* it is never parsed, and the stored value stays a plain number. */
|
|
1500
|
-
suffix(suffix) {
|
|
1501
|
-
return this.ann({ suffix });
|
|
1502
|
-
}
|
|
1503
|
-
/** Force a slider even without both bounds. */
|
|
1504
|
-
slider() {
|
|
1505
|
-
return this.ann({ control: "slider" });
|
|
1506
|
-
}
|
|
1507
|
-
/** Force a typed input even when both bounds are set. */
|
|
1508
|
-
input() {
|
|
1509
|
-
return this.ann({ control: "input" });
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
var BooleanNode = class extends BaseNode {
|
|
1513
|
-
kind = "boolean";
|
|
1514
|
-
/** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's
|
|
1515
|
-
* `PrefsForm` honors the distinction. */
|
|
1516
|
-
toggle() {
|
|
1517
|
-
return this.ann({ control: "switch" });
|
|
1518
|
-
}
|
|
1519
|
-
};
|
|
1520
|
-
var StringNode = class extends BaseNode {
|
|
1521
|
-
kind = "string";
|
|
1522
|
-
placeholder(placeholder) {
|
|
1523
|
-
return this.ann({ placeholder });
|
|
1524
|
-
}
|
|
1525
|
-
maxLength(maxLength) {
|
|
1526
|
-
return this.ann({ maxLength });
|
|
1527
|
-
}
|
|
1528
|
-
/** Milliseconds to debounce live writes. 0 commits every keystroke. */
|
|
1529
|
-
debounce(debounceMs) {
|
|
1530
|
-
return this.ann({ debounceMs });
|
|
1531
|
-
}
|
|
1532
|
-
};
|
|
1533
|
-
var ColorNode = class extends BaseNode {
|
|
1534
|
-
kind = "color";
|
|
1535
|
-
};
|
|
1536
|
-
var EnumNode = class extends BaseNode {
|
|
1537
|
-
kind = "enum";
|
|
1538
|
-
/** Render as a segmented control rather than a select. */
|
|
1539
|
-
radio() {
|
|
1540
|
-
return this.ann({ control: "radio" });
|
|
1541
|
-
}
|
|
1542
|
-
/** Relabel the choices. Named `labels` rather than `options` because
|
|
1543
|
-
* `options` is the node's own extras bag. */
|
|
1544
|
-
labels(options) {
|
|
1545
|
-
return this.ann({ options });
|
|
1546
|
-
}
|
|
1547
|
-
};
|
|
1548
|
-
var ValueNode = class extends BaseNode {
|
|
1549
|
-
kind = null;
|
|
1550
|
-
};
|
|
1551
|
-
var CustomNode = class _CustomNode extends BaseNode {
|
|
1552
|
-
constructor(kind, defaultValue, annotations = {}, options = {}) {
|
|
1553
|
-
super(defaultValue, annotations, options);
|
|
1554
|
-
this.kind = kind;
|
|
1555
|
-
}
|
|
1556
|
-
kind;
|
|
1557
|
-
with(annotations, options) {
|
|
1558
|
-
return new _CustomNode(this.kind, this.default, annotations, options);
|
|
1559
|
-
}
|
|
1560
|
-
};
|
|
1561
|
-
var GroupNode = class _GroupNode {
|
|
1562
|
-
constructor(children, annotations = {}, options = {}) {
|
|
1563
|
-
this.children = children;
|
|
1564
|
-
this.annotations = annotations;
|
|
1565
|
-
this.options = options;
|
|
1566
|
-
}
|
|
1567
|
-
children;
|
|
1568
|
-
annotations;
|
|
1569
|
-
options;
|
|
1570
|
-
with(annotations, options) {
|
|
1571
|
-
return new _GroupNode(this.children, annotations, options);
|
|
1572
|
-
}
|
|
1573
|
-
/** Heading for the group's rows. Defaults to the key, title-cased. */
|
|
1574
|
-
label(name) {
|
|
1575
|
-
return this.with({ ...this.annotations, name }, this.options);
|
|
1576
|
-
}
|
|
1577
|
-
/** Longer help text. Carried on the resolved `PrefGroup`, where weasel-ui's
|
|
1578
|
-
* `PrefsForm` draws it under the heading; `ControlPanel` has no place for
|
|
1579
|
-
* it yet and shows the heading alone. */
|
|
1580
|
-
describe(description) {
|
|
1581
|
-
return this.with({ ...this.annotations, description }, this.options);
|
|
1582
|
-
}
|
|
1583
|
-
/** Render this whole group under a named section heading. */
|
|
1584
|
-
section(label, opts = {}) {
|
|
1585
|
-
return this.with(this.annotations, { ...this.options, section: { label, ...opts } });
|
|
1586
|
-
}
|
|
1587
|
-
/** Show this group only while the predicate holds. Presentational — every
|
|
1588
|
-
* value beneath it stays in config and the instrument still reads it. */
|
|
1589
|
-
showIf(predicate) {
|
|
1590
|
-
return this.with(this.annotations, { ...this.options, showIf: predicate });
|
|
1591
|
-
}
|
|
1592
|
-
};
|
|
1593
|
-
function shapeDefaults(shape) {
|
|
1594
|
-
const out = {};
|
|
1595
|
-
for (const [key, entry] of Object.entries(shape)) {
|
|
1596
|
-
out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;
|
|
1597
|
-
}
|
|
1598
|
-
return out;
|
|
1599
|
-
}
|
|
1600
|
-
var expand = (options) => options.map((o) => typeof o === "string" ? { value: o, label: o } : o);
|
|
1601
|
-
var f = {
|
|
1602
|
-
number: (def) => new NumberNode(def),
|
|
1603
|
-
boolean: (def) => new BooleanNode(def),
|
|
1604
|
-
string: (def) => new StringNode(def),
|
|
1605
|
-
color: (def) => new ColorNode(def),
|
|
1606
|
-
/** A fixed set of choices. The default's literal type flows into the config
|
|
1607
|
-
* type, so `f.enum('fast', ['fast', 'accurate'])` gives
|
|
1608
|
-
* `'fast' | 'accurate'` with no `as const`. */
|
|
1609
|
-
enum: (def, options) => new EnumNode(def, { options: expand(options) }),
|
|
1610
|
-
/** A leaf with no declared kind: the rule chain decides, which is how a lab
|
|
1611
|
-
* states a convention like "every `*Color` key is a color picker". */
|
|
1612
|
-
value: (def) => new ValueNode(def),
|
|
1613
|
-
/** A branch: its children nest under this key, so `grid: f.group({ size })`
|
|
1614
|
-
* puts the value at `config.grid.size` and addresses it as `'grid.size'`.
|
|
1615
|
-
* Groups nest as deeply as the schema wants. */
|
|
1616
|
-
group: (children) => new GroupNode(children),
|
|
1617
|
-
/** A leaf of a kind a lab supplies the control for, through `controls`. */
|
|
1618
|
-
custom: (kind, def, validate) => new CustomNode(kind, def, {}, validate ? { validate } : {}),
|
|
1619
|
-
/** Collect leaves and groups into an instrument's config. */
|
|
1620
|
-
schema(nodes) {
|
|
1621
|
-
return { nodes, defaults: () => shapeDefaults(nodes) };
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
|
|
1625
|
-
// src/config/rules.ts
|
|
1626
|
-
function titleCase(key) {
|
|
1627
|
-
const words = key.replace(/[_-]+/g, " ").split(/(?<=[a-z0-9])(?=[A-Z])/).join(" ").trim().toLowerCase();
|
|
1628
|
-
return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);
|
|
1629
|
-
}
|
|
1630
|
-
var kindFromValue = (ctx) => {
|
|
1631
|
-
const t = typeof ctx.default;
|
|
1632
|
-
if (t === "boolean" || t === "number" || t === "string") return { kind: t };
|
|
1633
|
-
return null;
|
|
1634
|
-
};
|
|
1635
|
-
var labelFromKey = (ctx) => ({ name: titleCase(ctx.key) });
|
|
1636
|
-
var sliderWhenBounded = (ctx) => {
|
|
1637
|
-
if (ctx.leaf.kind !== "number") return null;
|
|
1638
|
-
const bounded = ctx.leaf.min !== void 0 && ctx.leaf.max !== void 0;
|
|
1639
|
-
return bounded ? { control: "slider" } : null;
|
|
1640
|
-
};
|
|
1641
|
-
var descriptionDefault = () => ({ description: "" });
|
|
1642
|
-
var builtinRules = [
|
|
1643
|
-
kindFromValue,
|
|
1644
|
-
labelFromKey,
|
|
1645
|
-
sliderWhenBounded,
|
|
1646
|
-
descriptionDefault
|
|
1647
|
-
];
|
|
1648
|
-
function applyRules(seed, ctx, rules) {
|
|
1649
|
-
const acc = { ...seed };
|
|
1650
|
-
for (const rule of rules) {
|
|
1651
|
-
const patch = rule({ ...ctx, leaf: acc });
|
|
1652
|
-
if (patch === null) continue;
|
|
1653
|
-
for (const [k2, v] of Object.entries(patch)) {
|
|
1654
|
-
if (v !== void 0 && acc[k2] === void 0) {
|
|
1655
|
-
acc[k2] = v;
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
return acc;
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
// src/config/resolve.ts
|
|
1663
|
-
function defined(annotations) {
|
|
1664
|
-
const out = {};
|
|
1665
|
-
for (const [k2, v] of Object.entries(annotations)) if (v !== void 0) out[k2] = v;
|
|
1666
|
-
return out;
|
|
1667
|
-
}
|
|
1668
|
-
function resolveConfigSchema(schema, rules = []) {
|
|
1669
|
-
const sink = {
|
|
1670
|
-
sections: [],
|
|
1671
|
-
showIf: /* @__PURE__ */ new Map(),
|
|
1672
|
-
renderers: {},
|
|
1673
|
-
chain: [...rules, ...builtinRules]
|
|
1674
|
-
};
|
|
1675
|
-
const group = resolveShape(schema.nodes, "", "", sink);
|
|
1676
|
-
return { group, sections: sink.sections, showIf: sink.showIf, renderers: sink.renderers };
|
|
1677
|
-
}
|
|
1678
|
-
function resolveShape(shape, at, name, sink) {
|
|
1679
|
-
const children = {};
|
|
1680
|
-
const sections = /* @__PURE__ */ new Map();
|
|
1681
|
-
for (const [key, entry] of Object.entries(shape)) {
|
|
1682
|
-
const path = at === "" ? key : `${at}.${key}`;
|
|
1683
|
-
children[key] = resolveEntry(entry, key, path, sink);
|
|
1684
|
-
const { section, showIf: predicate } = entry.options;
|
|
1685
|
-
if (section !== void 0) {
|
|
1686
|
-
let spec = sections.get(section.label);
|
|
1687
|
-
if (!spec) {
|
|
1688
|
-
spec = { at, label: section.label, paths: [] };
|
|
1689
|
-
sections.set(section.label, spec);
|
|
1690
|
-
sink.sections.push(spec);
|
|
1691
|
-
}
|
|
1692
|
-
spec.paths.push(path);
|
|
1693
|
-
if (section.collapsed !== void 0) {
|
|
1694
|
-
spec.collapsed = (spec.collapsed ?? false) || section.collapsed;
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
if (predicate) sink.showIf.set(path, predicate);
|
|
1698
|
-
if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;
|
|
1699
|
-
}
|
|
1700
|
-
return { name, children };
|
|
1701
|
-
}
|
|
1702
|
-
function resolveEntry(entry, key, path, sink) {
|
|
1703
|
-
if (isConfigBranch(entry)) {
|
|
1704
|
-
const group = resolveShape(
|
|
1705
|
-
entry.children,
|
|
1706
|
-
path,
|
|
1707
|
-
entry.annotations.name ?? titleCase(key),
|
|
1708
|
-
sink
|
|
1709
|
-
);
|
|
1710
|
-
return entry.annotations.description === void 0 ? group : { ...group, description: entry.annotations.description };
|
|
1711
|
-
}
|
|
1712
|
-
const seed = defined({
|
|
1713
|
-
...entry.annotations,
|
|
1714
|
-
...entry.kind === null ? {} : { kind: entry.kind }
|
|
1715
|
-
});
|
|
1716
|
-
const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);
|
|
1717
|
-
return { ...patch, default: entry.default };
|
|
1718
|
-
}
|
|
1719
|
-
var LabContext = createContext(null);
|
|
1720
|
-
function useLabContext() {
|
|
1721
|
-
const ctx = useContext(LabContext);
|
|
1722
|
-
if (ctx === null) {
|
|
1723
|
-
throw new Error("useLabContext must be used inside <Lab>");
|
|
1724
|
-
}
|
|
1725
|
-
return ctx;
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
// src/config/useConfigSchema.ts
|
|
1729
|
-
var NO_RULES = [];
|
|
1730
|
-
function useConfigSchema(instrument) {
|
|
1731
|
-
const lab = useContext(LabContext);
|
|
1732
|
-
const rules = lab?.configRules ?? NO_RULES;
|
|
1733
|
-
return useMemo(
|
|
1734
|
-
() => instrument.config ? resolveConfigSchema(instrument.config, rules) : fromConfigFields(instrument.configSchema?.() ?? []),
|
|
1735
|
-
[instrument, rules]
|
|
1736
|
-
);
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
1507
|
// src/instrument/defineInstrument.ts
|
|
1740
1508
|
function defineInstrument(spec) {
|
|
1741
1509
|
if (spec.config && !("defaultConfig" in spec && spec.defaultConfig)) {
|
|
@@ -1794,7 +1562,7 @@ function validateConfigSchema(fields) {
|
|
|
1794
1562
|
return { valid: errors.length === 0, errors };
|
|
1795
1563
|
}
|
|
1796
1564
|
|
|
1797
|
-
// ../theme/dist/chunk-
|
|
1565
|
+
// ../theme/dist/chunk-VKZ4JIVX.js
|
|
1798
1566
|
var THEME_SOURCES = {
|
|
1799
1567
|
"weasel": {
|
|
1800
1568
|
"primitives": {
|
|
@@ -1931,46 +1699,46 @@ var THEME_SOURCES = {
|
|
|
1931
1699
|
"alpha": 0.18,
|
|
1932
1700
|
"description": "Pressed wash over a raised surface."
|
|
1933
1701
|
},
|
|
1934
|
-
"swatch-
|
|
1702
|
+
"swatch-fuchsia": {
|
|
1935
1703
|
"type": "color",
|
|
1936
|
-
"value": "#
|
|
1937
|
-
"description": "First of ten categorical spot colors for plotting, status and data viz.
|
|
1704
|
+
"value": "#f641f7",
|
|
1705
|
+
"description": "First of ten categorical spot colors for plotting, status and data viz. Take them in sequence: the order is farthest-point over hue, so the first N are as separated as N colors can be (first three 87\xB0 apart, all ten 32\xB0). Mode-invariant on purpose \u2014 a series keeps its color when the mode flips, which is why they are tuned for a dark surface and clear only 1.5:1 on a light one."
|
|
1938
1706
|
},
|
|
1939
|
-
"swatch-
|
|
1707
|
+
"swatch-green": {
|
|
1940
1708
|
"type": "color",
|
|
1941
|
-
"value": "#
|
|
1709
|
+
"value": "#48e628"
|
|
1942
1710
|
},
|
|
1943
|
-
"swatch-
|
|
1711
|
+
"swatch-sky": {
|
|
1944
1712
|
"type": "color",
|
|
1945
|
-
"value": "#
|
|
1713
|
+
"value": "#36bff2"
|
|
1946
1714
|
},
|
|
1947
|
-
"swatch-
|
|
1715
|
+
"swatch-amber": {
|
|
1948
1716
|
"type": "color",
|
|
1949
|
-
"value": "#
|
|
1717
|
+
"value": "#e69628"
|
|
1950
1718
|
},
|
|
1951
|
-
"swatch-
|
|
1719
|
+
"swatch-teal": {
|
|
1952
1720
|
"type": "color",
|
|
1953
|
-
"value": "#
|
|
1721
|
+
"value": "#3ee1cb"
|
|
1954
1722
|
},
|
|
1955
|
-
"swatch-
|
|
1723
|
+
"swatch-red": {
|
|
1956
1724
|
"type": "color",
|
|
1957
|
-
"value": "#
|
|
1725
|
+
"value": "#f75034"
|
|
1958
1726
|
},
|
|
1959
|
-
"swatch-
|
|
1727
|
+
"swatch-blue": {
|
|
1960
1728
|
"type": "color",
|
|
1961
|
-
"value": "#
|
|
1729
|
+
"value": "#407ef7"
|
|
1962
1730
|
},
|
|
1963
|
-
"swatch-
|
|
1731
|
+
"swatch-citron": {
|
|
1964
1732
|
"type": "color",
|
|
1965
|
-
"value": "#
|
|
1733
|
+
"value": "#d5d23a"
|
|
1966
1734
|
},
|
|
1967
|
-
"swatch-
|
|
1735
|
+
"swatch-rose": {
|
|
1968
1736
|
"type": "color",
|
|
1969
|
-
"value": "#
|
|
1737
|
+
"value": "#fa4590"
|
|
1970
1738
|
},
|
|
1971
|
-
"swatch-
|
|
1739
|
+
"swatch-violet": {
|
|
1972
1740
|
"type": "color",
|
|
1973
|
-
"value": "#
|
|
1741
|
+
"value": "#955cf9"
|
|
1974
1742
|
},
|
|
1975
1743
|
"radius-sm": {
|
|
1976
1744
|
"type": "dimension",
|
|
@@ -2023,15 +1791,15 @@ var THEME_SOURCES = {
|
|
|
2023
1791
|
"value": "8px",
|
|
2024
1792
|
"description": "Diameter of a linear range thumb."
|
|
2025
1793
|
},
|
|
2026
|
-
"slider-track-
|
|
1794
|
+
"slider-track-mix": {
|
|
2027
1795
|
"type": "dimension",
|
|
2028
1796
|
"value": "18%",
|
|
2029
|
-
"description": "Accent proportion in a range track. Mixed into a real property, never into another custom property."
|
|
1797
|
+
"description": "Accent proportion in a range track, as a color-mix percentage \u2014 not a color. Mixed into a real property, never into another custom property."
|
|
2030
1798
|
},
|
|
2031
|
-
"slider-thumb-
|
|
1799
|
+
"slider-thumb-mix": {
|
|
2032
1800
|
"type": "dimension",
|
|
2033
1801
|
"value": "70%",
|
|
2034
|
-
"description": "Accent proportion in a range thumb."
|
|
1802
|
+
"description": "Accent proportion in a range thumb, as a color-mix percentage \u2014 not a color."
|
|
2035
1803
|
},
|
|
2036
1804
|
"field-pad-x": {
|
|
2037
1805
|
"type": "dimension",
|
|
@@ -2271,7 +2039,8 @@ var THEME_SOURCES = {
|
|
|
2271
2039
|
},
|
|
2272
2040
|
"border-strong": {
|
|
2273
2041
|
"type": "color",
|
|
2274
|
-
"value": "{color.gray-
|
|
2042
|
+
"value": "{color.gray-400}",
|
|
2043
|
+
"description": "The line that has to be seen: a control's edge, a raised surface's outline. Clears WCAG 1.4.11's 3:1 against every surface in both modes."
|
|
2275
2044
|
},
|
|
2276
2045
|
"accent-fg": {
|
|
2277
2046
|
"type": "color",
|
|
@@ -2287,11 +2056,6 @@ var THEME_SOURCES = {
|
|
|
2287
2056
|
"type": "color",
|
|
2288
2057
|
"value": "rgba(0, 0, 0, 0.6)",
|
|
2289
2058
|
"description": "Elevation. Always darker than any surface \u2014 never derived from fg, which is near-white on dark."
|
|
2290
|
-
},
|
|
2291
|
-
"border-raised": {
|
|
2292
|
-
"type": "color",
|
|
2293
|
-
"value": "{color.gray-400}",
|
|
2294
|
-
"description": "The line around a raised surface. Clears 3:1 against surface and surface-raised in either mode, which the general-purpose border does not; over surface-sunken it does not, so a panel on a sunken field needs its own answer."
|
|
2295
2059
|
}
|
|
2296
2060
|
},
|
|
2297
2061
|
"light": {
|
|
@@ -2325,7 +2089,7 @@ var THEME_SOURCES = {
|
|
|
2325
2089
|
},
|
|
2326
2090
|
"border-strong": {
|
|
2327
2091
|
"type": "color",
|
|
2328
|
-
"value": "{color.gray-
|
|
2092
|
+
"value": "{color.gray-500}"
|
|
2329
2093
|
},
|
|
2330
2094
|
"accent-fg": {
|
|
2331
2095
|
"type": "color",
|
|
@@ -2340,10 +2104,6 @@ var THEME_SOURCES = {
|
|
|
2340
2104
|
"shadow": {
|
|
2341
2105
|
"type": "color",
|
|
2342
2106
|
"value": "rgba(0, 0, 0, 0.18)"
|
|
2343
|
-
},
|
|
2344
|
-
"border-raised": {
|
|
2345
|
-
"type": "color",
|
|
2346
|
-
"value": "{color.gray-400}"
|
|
2347
2107
|
}
|
|
2348
2108
|
}
|
|
2349
2109
|
}
|
|
@@ -2360,11 +2120,11 @@ function hexToRgba(hex, alpha) {
|
|
|
2360
2120
|
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
|
|
2361
2121
|
if (!m) throw new Error(`Expected a hex color, got "${hex}"`);
|
|
2362
2122
|
const body = m[1];
|
|
2363
|
-
const full = body.length === 3 ? body.replace(/./g, (
|
|
2364
|
-
const
|
|
2365
|
-
const
|
|
2366
|
-
const
|
|
2367
|
-
return `rgba(${
|
|
2123
|
+
const full = body.length === 3 ? body.replace(/./g, (c3) => c3 + c3) : body;
|
|
2124
|
+
const r3 = Number.parseInt(full.slice(0, 2), 16);
|
|
2125
|
+
const g = Number.parseInt(full.slice(2, 4), 16);
|
|
2126
|
+
const b2 = Number.parseInt(full.slice(4, 6), 16);
|
|
2127
|
+
return `rgba(${r3}, ${g}, ${b2}, ${alpha})`;
|
|
2368
2128
|
}
|
|
2369
2129
|
var REF = /^\{([^}]+)\}$/;
|
|
2370
2130
|
function refTarget(value) {
|
|
@@ -2376,7 +2136,7 @@ function refTarget(value) {
|
|
|
2376
2136
|
return dot === -1 ? path : path.slice(dot + 1);
|
|
2377
2137
|
}
|
|
2378
2138
|
function fontStack(value) {
|
|
2379
|
-
return value.map((
|
|
2139
|
+
return value.map((f3) => typeof f3 === "string" && /\s/.test(f3) ? `'${f3}'` : String(f3)).join(", ");
|
|
2380
2140
|
}
|
|
2381
2141
|
function serialize(type, value) {
|
|
2382
2142
|
if (Array.isArray(value)) {
|
|
@@ -2501,25 +2261,7 @@ function ThemeProvider({
|
|
|
2501
2261
|
function useThemeOptional() {
|
|
2502
2262
|
return useContext(ThemeContext);
|
|
2503
2263
|
}
|
|
2504
|
-
|
|
2505
|
-
// src/instrument/serializers.ts
|
|
2506
|
-
function configDefaultsOf(instruments) {
|
|
2507
|
-
const out = {};
|
|
2508
|
-
for (const instrument of instruments) out[instrument.name] = () => instrument.defaultConfig();
|
|
2509
|
-
return out;
|
|
2510
|
-
}
|
|
2511
|
-
function serializersOf(instruments) {
|
|
2512
|
-
const out = {};
|
|
2513
|
-
for (const instrument of instruments) {
|
|
2514
|
-
const { name, serialize: serialize2, deserialize } = instrument;
|
|
2515
|
-
if (!serialize2 && !deserialize) continue;
|
|
2516
|
-
out[name] = {
|
|
2517
|
-
...serialize2 ? { serialize: (state) => serialize2(state) } : {},
|
|
2518
|
-
...deserialize ? { deserialize: (data, config) => deserialize(data, config) } : {}
|
|
2519
|
-
};
|
|
2520
|
-
}
|
|
2521
|
-
return out;
|
|
2522
|
-
}
|
|
2264
|
+
var AnnotationPreloadContext = createContext(null);
|
|
2523
2265
|
|
|
2524
2266
|
// ../theme/dist/index.js
|
|
2525
2267
|
var ALPHA_EXT = "com.weasel.alpha";
|
|
@@ -2529,9 +2271,9 @@ function flattenTokens(doc) {
|
|
|
2529
2271
|
for (const [groupName, group] of Object.entries(doc)) {
|
|
2530
2272
|
if (groupName.startsWith("$")) continue;
|
|
2531
2273
|
if (typeof group !== "object" || group === null) continue;
|
|
2532
|
-
const
|
|
2533
|
-
const groupType =
|
|
2534
|
-
for (const [leaf, node] of Object.entries(
|
|
2274
|
+
const g = group;
|
|
2275
|
+
const groupType = g.$type;
|
|
2276
|
+
for (const [leaf, node] of Object.entries(g)) {
|
|
2535
2277
|
if (leaf.startsWith("$")) continue;
|
|
2536
2278
|
if (!isToken(node)) continue;
|
|
2537
2279
|
const type = node.$type ?? groupType;
|
|
@@ -2604,7 +2346,7 @@ var interstellar_tokens_default = {
|
|
|
2604
2346
|
"accent-hover": { $value: "#c6a3ee" },
|
|
2605
2347
|
"focus-ring": { $value: "rgba(176, 138, 219, 0.45)" },
|
|
2606
2348
|
shadow: { $value: "rgba(0, 0, 0, 0.7)" },
|
|
2607
|
-
"border-
|
|
2349
|
+
"border-strong": { $value: "rgba(255, 255, 255, 0.36)" }
|
|
2608
2350
|
},
|
|
2609
2351
|
gradient: {
|
|
2610
2352
|
$type: "gradient",
|
|
@@ -2628,7 +2370,7 @@ var interstellar_tokens_default = {
|
|
|
2628
2370
|
"accent-hover": { $value: "#c2854f" },
|
|
2629
2371
|
"focus-ring": { $value: "rgba(168, 111, 60, 0.35)" },
|
|
2630
2372
|
shadow: { $value: "rgba(80, 60, 40, 0.18)" },
|
|
2631
|
-
"border-
|
|
2373
|
+
"border-strong": { $value: "#9e8b71" }
|
|
2632
2374
|
}
|
|
2633
2375
|
}
|
|
2634
2376
|
}
|
|
@@ -2636,114 +2378,18 @@ var interstellar_tokens_default = {
|
|
|
2636
2378
|
|
|
2637
2379
|
// src/theme/interstellar.ts
|
|
2638
2380
|
var interstellarTheme = loadDTCG(interstellar_tokens_default);
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
function as2DView(view) {
|
|
2643
|
-
if (typeof view !== "object" || view === null) return null;
|
|
2644
|
-
const v = view;
|
|
2645
|
-
if (typeof v.zoom !== "number") return null;
|
|
2646
|
-
if (typeof v.pan !== "object" || v.pan === null) return null;
|
|
2647
|
-
if (typeof v.pan.x !== "number" || typeof v.pan.y !== "number") return null;
|
|
2648
|
-
return { zoom: v.zoom, pan: { x: v.pan.x, y: v.pan.y } };
|
|
2649
|
-
}
|
|
2650
|
-
var ZONE_ID = asNodeId("lk-trial-body");
|
|
2651
|
-
var SIDEBAR_ID = asNodeId("lk-trial-sidebar");
|
|
2652
|
-
var CONTENT_ID = asNodeId("lk-trial-content");
|
|
2653
|
-
var STRATEGIES = { strip: stripStrategy };
|
|
2654
|
-
function TrialBody({
|
|
2655
|
-
sidebar,
|
|
2656
|
-
children,
|
|
2657
|
-
contentClassName,
|
|
2658
|
-
width,
|
|
2659
|
-
defaultWidth = 320,
|
|
2660
|
-
minWidth = 140,
|
|
2661
|
-
maxWidth = 720,
|
|
2662
|
-
contentMinWidth = 160,
|
|
2663
|
-
onWidthChange,
|
|
2664
|
-
viewport
|
|
2665
|
-
}) {
|
|
2666
|
-
const storeRef = useRef(null);
|
|
2667
|
-
if (storeRef.current === null) {
|
|
2668
|
-
const store2 = new Store();
|
|
2669
|
-
store2.registerNode(
|
|
2670
|
-
createNode({
|
|
2671
|
-
kind: "zone",
|
|
2672
|
-
id: ZONE_ID,
|
|
2673
|
-
container: { strategyId: "strip", config: { axis: "x", resizable: true } }
|
|
2674
|
-
})
|
|
2675
|
-
);
|
|
2676
|
-
store2.registerNode(
|
|
2677
|
-
createNode({
|
|
2678
|
-
kind: "sidebar",
|
|
2679
|
-
id: SIDEBAR_ID,
|
|
2680
|
-
parentId: ZONE_ID,
|
|
2681
|
-
meta: { title: "Sidebar" },
|
|
2682
|
-
placement: { size: { w: width ?? defaultWidth } },
|
|
2683
|
-
hints: { minSize: { w: minWidth, h: 0 }, maxSize: { w: maxWidth, h: 0 } }
|
|
2684
|
-
})
|
|
2685
|
-
);
|
|
2686
|
-
store2.registerNode(
|
|
2687
|
-
createNode({
|
|
2688
|
-
kind: "content",
|
|
2689
|
-
id: CONTENT_ID,
|
|
2690
|
-
parentId: ZONE_ID,
|
|
2691
|
-
meta: { title: "Content" },
|
|
2692
|
-
hints: { minSize: { w: contentMinWidth, h: 0 } }
|
|
2693
|
-
})
|
|
2694
|
-
);
|
|
2695
|
-
store2.showNode(SIDEBAR_ID);
|
|
2696
|
-
store2.showNode(CONTENT_ID);
|
|
2697
|
-
storeRef.current = store2;
|
|
2698
|
-
}
|
|
2699
|
-
const store = storeRef.current;
|
|
2700
|
-
const widthRef = useRef(width ?? defaultWidth);
|
|
2701
|
-
useEffect(() => {
|
|
2702
|
-
store.setHints(SIDEBAR_ID, {
|
|
2703
|
-
minSize: { w: minWidth, h: 0 },
|
|
2704
|
-
maxSize: { w: maxWidth, h: 0 }
|
|
2705
|
-
});
|
|
2706
|
-
store.setHints(CONTENT_ID, { minSize: { w: contentMinWidth, h: 0 } });
|
|
2707
|
-
}, [store, minWidth, maxWidth, contentMinWidth]);
|
|
2708
|
-
useEffect(() => {
|
|
2709
|
-
if (width === void 0 || width === widthRef.current) return;
|
|
2710
|
-
widthRef.current = width;
|
|
2711
|
-
store.patchPlacement(SIDEBAR_ID, { size: { w: width } });
|
|
2712
|
-
}, [store, width]);
|
|
2713
|
-
const onWidthChangeRef = useRef(onWidthChange);
|
|
2714
|
-
onWidthChangeRef.current = onWidthChange;
|
|
2715
|
-
useEffect(
|
|
2716
|
-
() => store.events.on("node.placementChanged", () => {
|
|
2717
|
-
const next = store.getPlacement(SIDEBAR_ID)?.size?.w;
|
|
2718
|
-
if (typeof next !== "number" || next === widthRef.current) return;
|
|
2719
|
-
widthRef.current = next;
|
|
2720
|
-
onWidthChangeRef.current?.(next);
|
|
2721
|
-
}),
|
|
2722
|
-
[store]
|
|
2723
|
-
);
|
|
2724
|
-
const chrome = useMemo(
|
|
2725
|
-
() => ({
|
|
2726
|
-
sidebar: () => /* @__PURE__ */ jsx("div", { className: "lk-trial__sidebar", children: sidebar }),
|
|
2727
|
-
content: () => /* @__PURE__ */ jsx(
|
|
2728
|
-
"div",
|
|
2729
|
-
{
|
|
2730
|
-
className: contentClassName ? `lk-trial__content ${contentClassName}` : "lk-trial__content",
|
|
2731
|
-
children
|
|
2732
|
-
}
|
|
2733
|
-
)
|
|
2734
|
-
}),
|
|
2735
|
-
[sidebar, children, contentClassName]
|
|
2736
|
-
);
|
|
2737
|
-
return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES, children: /* @__PURE__ */ jsx(
|
|
2738
|
-
Container,
|
|
2381
|
+
function TrialBody({ sidebar, children, contentClassName, ...rest }) {
|
|
2382
|
+
return /* @__PURE__ */ jsx(
|
|
2383
|
+
Split,
|
|
2739
2384
|
{
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
className: "lk-trial__panes
|
|
2743
|
-
|
|
2744
|
-
|
|
2385
|
+
...rest,
|
|
2386
|
+
sidebar,
|
|
2387
|
+
className: "lk-trial__panes",
|
|
2388
|
+
sidebarClassName: "lk-trial__sidebar",
|
|
2389
|
+
contentClassName: contentClassName ? `lk-trial__content ${contentClassName}` : "lk-trial__content",
|
|
2390
|
+
children
|
|
2745
2391
|
}
|
|
2746
|
-
)
|
|
2392
|
+
);
|
|
2747
2393
|
}
|
|
2748
2394
|
var TrialDragContext = createContext(null);
|
|
2749
2395
|
function useTrialDrag() {
|
|
@@ -2828,7 +2474,7 @@ function Portalled({
|
|
|
2828
2474
|
}
|
|
2829
2475
|
function UndockedSections({ trialId, sections, onDock }) {
|
|
2830
2476
|
if (sections.length === 0) return null;
|
|
2831
|
-
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((
|
|
2477
|
+
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((c3) => /* @__PURE__ */ jsx(Portalled, { trialId, contribution: c3, onDock }, c3.id)) });
|
|
2832
2478
|
}
|
|
2833
2479
|
var NO_OP = () => {
|
|
2834
2480
|
};
|
|
@@ -2861,17 +2507,17 @@ function TrialChrome({
|
|
|
2861
2507
|
const dockPanelAction = useStore(storeCtx.store, (s) => s.dockPanel);
|
|
2862
2508
|
const undockedPanels = useStore(storeCtx.store, (s) => s.undockedPanels);
|
|
2863
2509
|
const undockedIds = useMemo(
|
|
2864
|
-
() => Object.values(undockedPanels).filter((
|
|
2510
|
+
() => Object.values(undockedPanels).filter((p2) => p2.trialId === trialId).map((p2) => p2.sectionId),
|
|
2865
2511
|
[undockedPanels, trialId]
|
|
2866
2512
|
);
|
|
2867
2513
|
const view2d = as2DView(record.view);
|
|
2868
2514
|
const configSchema = useConfigSchema(instrument);
|
|
2869
|
-
const title = record.title ??
|
|
2515
|
+
const title = record.title ?? instrument.title ?? instrument.name;
|
|
2870
2516
|
const collapsedSections = record.collapsedSections ?? NO_SECTIONS;
|
|
2871
2517
|
const ctx = useMemo(() => {
|
|
2872
|
-
const setZoom = (
|
|
2518
|
+
const setZoom = (z2) => {
|
|
2873
2519
|
if (!view2d) return;
|
|
2874
|
-
updateTrialView(trialId,
|
|
2520
|
+
updateTrialView(trialId, withZoom(view2d, z2));
|
|
2875
2521
|
};
|
|
2876
2522
|
return {
|
|
2877
2523
|
trialId,
|
|
@@ -2953,7 +2599,7 @@ function TrialChrome({
|
|
|
2953
2599
|
),
|
|
2954
2600
|
[instrument, ctx, trialChrome, chrome, suppress, lab.controls]
|
|
2955
2601
|
);
|
|
2956
|
-
const inRegion = (region) => contributions.filter((
|
|
2602
|
+
const inRegion = (region) => contributions.filter((c3) => c3.region === region);
|
|
2957
2603
|
const handleKeyDown = (e) => {
|
|
2958
2604
|
const mod = e.metaKey || e.ctrlKey;
|
|
2959
2605
|
if (!mod) return;
|
|
@@ -2970,6 +2616,7 @@ function TrialChrome({
|
|
|
2970
2616
|
"section",
|
|
2971
2617
|
{
|
|
2972
2618
|
className: "lk-trial",
|
|
2619
|
+
"data-trial-id": trialId,
|
|
2973
2620
|
"aria-label": `Trial ${title}`,
|
|
2974
2621
|
tabIndex: -1,
|
|
2975
2622
|
onKeyDown: handleKeyDown,
|
|
@@ -2989,15 +2636,15 @@ function TrialChrome({
|
|
|
2989
2636
|
TrialBody,
|
|
2990
2637
|
{
|
|
2991
2638
|
width: record.sidebarWidth,
|
|
2992
|
-
onWidthChange: (
|
|
2993
|
-
contentClassName: instrument.canvas ? "lk-trial__content--flush" : void 0,
|
|
2639
|
+
onWidthChange: (w2) => updateTrialSidebarWidth(trialId, w2),
|
|
2640
|
+
contentClassName: instrument.canvas || instrument.stage ? "lk-trial__content--flush" : void 0,
|
|
2994
2641
|
sidebar: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2995
2642
|
/* @__PURE__ */ jsx(SidebarRegion, { contributions: inRegion("sidebar"), ctx }),
|
|
2996
2643
|
/* @__PURE__ */ jsx(
|
|
2997
2644
|
UndockedSections,
|
|
2998
2645
|
{
|
|
2999
2646
|
trialId,
|
|
3000
|
-
sections: inRegion("sidebar").filter((
|
|
2647
|
+
sections: inRegion("sidebar").filter((c3) => undockedIds.includes(c3.id)),
|
|
3001
2648
|
onDock: ctx.dockPanel
|
|
3002
2649
|
}
|
|
3003
2650
|
)
|
|
@@ -3021,25 +2668,30 @@ function Trial({ id, chrome, suppress }) {
|
|
|
3021
2668
|
const lab = useLabContext();
|
|
3022
2669
|
const storeCtx = useContext(LabStoreContext);
|
|
3023
2670
|
if (!storeCtx) throw new Error("[labkit] <Trial> requires <LabStoreProvider>");
|
|
3024
|
-
const
|
|
2671
|
+
const instruments = useStore(storeCtx.store, (s) => s.instruments) ?? lab.instruments;
|
|
2672
|
+
const record = useStore(storeCtx.store, (s) => s.trials.find((w2) => w2.id === id));
|
|
2673
|
+
const instrument = record ? instruments.find((i) => i.name === record.instrumentName) : void 0;
|
|
2674
|
+
const kept = useKeptMarks(instrument?.annotations?.storage, id);
|
|
3025
2675
|
if (!record) {
|
|
3026
2676
|
return /* @__PURE__ */ jsxs("div", { className: "lk-trial lk-trial--unknown", children: [
|
|
3027
2677
|
"Trial not found: ",
|
|
3028
2678
|
id
|
|
3029
2679
|
] });
|
|
3030
2680
|
}
|
|
3031
|
-
const instrument = lab.instruments.find((i) => i.name === record.instrumentName);
|
|
3032
2681
|
if (!instrument) {
|
|
3033
2682
|
return /* @__PURE__ */ jsxs("div", { className: "lk-trial lk-trial--unknown", children: [
|
|
3034
2683
|
"Unknown instrument: ",
|
|
3035
2684
|
record.instrumentName
|
|
3036
2685
|
] });
|
|
3037
2686
|
}
|
|
2687
|
+
if (!kept.ready) return /* @__PURE__ */ jsx("div", { className: "lk-trial lk-trial--loading" });
|
|
3038
2688
|
return /* @__PURE__ */ jsx(
|
|
3039
2689
|
TrialRuntime,
|
|
3040
2690
|
{
|
|
2691
|
+
keptMarks: kept.value,
|
|
3041
2692
|
record,
|
|
3042
2693
|
instrument,
|
|
2694
|
+
instruments,
|
|
3043
2695
|
store: storeCtx.store,
|
|
3044
2696
|
isLast: lab.trials.length <= 1,
|
|
3045
2697
|
chrome,
|
|
@@ -3047,7 +2699,41 @@ function Trial({ id, chrome, suppress }) {
|
|
|
3047
2699
|
}
|
|
3048
2700
|
);
|
|
3049
2701
|
}
|
|
3050
|
-
function
|
|
2702
|
+
function useKeptMarks(storage, trialId) {
|
|
2703
|
+
const preload = useContext(AnnotationPreloadContext);
|
|
2704
|
+
const [loaded, setLoaded] = useState(
|
|
2705
|
+
() => storage && preload?.has(trialId) ? { value: preload.get(trialId) } : null
|
|
2706
|
+
);
|
|
2707
|
+
useEffect(() => {
|
|
2708
|
+
if (!storage || loaded) return;
|
|
2709
|
+
let live = true;
|
|
2710
|
+
storage.load().then(
|
|
2711
|
+
(value) => {
|
|
2712
|
+
if (live) setLoaded({ value });
|
|
2713
|
+
},
|
|
2714
|
+
(error) => {
|
|
2715
|
+
console.warn(`[labkit] could not load the marks of trial "${trialId}"`, error);
|
|
2716
|
+
if (live) setLoaded({ value: null });
|
|
2717
|
+
}
|
|
2718
|
+
);
|
|
2719
|
+
return () => {
|
|
2720
|
+
live = false;
|
|
2721
|
+
};
|
|
2722
|
+
}, [storage, loaded, trialId]);
|
|
2723
|
+
if (!storage) return { ready: true, value: void 0 };
|
|
2724
|
+
return loaded ? { ready: true, value: loaded.value } : { ready: false, value: void 0 };
|
|
2725
|
+
}
|
|
2726
|
+
function TrialRuntime({
|
|
2727
|
+
keptMarks,
|
|
2728
|
+
record,
|
|
2729
|
+
instrument,
|
|
2730
|
+
instruments,
|
|
2731
|
+
store,
|
|
2732
|
+
isLast,
|
|
2733
|
+
chrome,
|
|
2734
|
+
suppress
|
|
2735
|
+
}) {
|
|
2736
|
+
const wheelSlot = useRef(null);
|
|
3051
2737
|
const canvasContainerRef = useRef(null);
|
|
3052
2738
|
const loupeHostRef = useRef(null);
|
|
3053
2739
|
const updateTrialState = useStore(store, (s) => s.updateTrialState);
|
|
@@ -3065,7 +2751,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3065
2751
|
const [layerOrder, setLayerOrder] = useState(null);
|
|
3066
2752
|
const [loupeOn, setLoupeOn] = useState(false);
|
|
3067
2753
|
const visibleLayers = useMemo(
|
|
3068
|
-
() => (instrument.canvas?.layers ?? []).filter((
|
|
2754
|
+
() => (instrument.canvas?.layers ?? []).filter((l2) => layerVisibility[l2.id] !== false).map((l2) => l2.id),
|
|
3069
2755
|
[instrument.canvas, layerVisibility]
|
|
3070
2756
|
);
|
|
3071
2757
|
const undoCap = instrument.undo;
|
|
@@ -3073,14 +2759,15 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3073
2759
|
const maxDepth = undoCap?.maxDepth ?? 50;
|
|
3074
2760
|
const snapshotIfNeeded = (event) => {
|
|
3075
2761
|
if (!undoCap || !undoEvents.has(event)) return;
|
|
3076
|
-
const current = store.getState().trials.find((
|
|
2762
|
+
const current = store.getState().trials.find((w2) => w2.id === record.id);
|
|
3077
2763
|
if (!current) return;
|
|
3078
2764
|
const snap = structuredClone(current.state);
|
|
3079
2765
|
updateTrialUndoStack(record.id, (prev) => pushSnapshot(prev, snap, maxDepth));
|
|
3080
2766
|
};
|
|
3081
2767
|
const setView = (v) => updateTrialView(record.id, v);
|
|
2768
|
+
const stage = instrument.canvas ? void 0 : instrument.stage;
|
|
3082
2769
|
const placeView = (size) => {
|
|
3083
|
-
const declared = instrument.canvas?.initialView;
|
|
2770
|
+
const declared = instrument.canvas?.initialView ?? (stage ? stage.initialView ?? ((vp) => fitStage(stage.size, vp)) : void 0);
|
|
3084
2771
|
if (typeof declared !== "function") return;
|
|
3085
2772
|
if (record.view != null) return;
|
|
3086
2773
|
setView(declared(size));
|
|
@@ -3091,12 +2778,14 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3091
2778
|
const annotationsRef = useRef(null);
|
|
3092
2779
|
const configRef = useRef(record.config);
|
|
3093
2780
|
configRef.current = record.config;
|
|
2781
|
+
const capRef = useRef(annotationsCap);
|
|
2782
|
+
capRef.current = annotationsCap;
|
|
3094
2783
|
if (annotationsRef.current === null) {
|
|
3095
|
-
const kept = annotationsCap?.storage ?
|
|
2784
|
+
const kept = annotationsCap?.storage ? keptMarks : record.annotations;
|
|
3096
2785
|
annotationsRef.current = annotationsFromJSON(kept, () => targetsRef.current(), {
|
|
3097
|
-
meaning:
|
|
2786
|
+
meaning: () => capRef.current?.meaning,
|
|
3098
2787
|
config: () => configRef.current,
|
|
3099
|
-
onCapture: (result) =>
|
|
2788
|
+
onCapture: (result) => capRef.current?.onCapture?.(result)
|
|
3100
2789
|
});
|
|
3101
2790
|
}
|
|
3102
2791
|
const annotations = annotationsRef.current;
|
|
@@ -3140,8 +2829,9 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3140
2829
|
flush();
|
|
3141
2830
|
};
|
|
3142
2831
|
}, [annotations, annotationsCap]);
|
|
3143
|
-
const declaresTools = instrument.tools != null
|
|
3144
|
-
const
|
|
2832
|
+
const declaresTools = instrument.tools != null;
|
|
2833
|
+
const labTool = resolveLabTool(labToolId, instruments);
|
|
2834
|
+
const resolvedToolId = declaresTools ? record.activeToolId ?? instrument.tools?.initial ?? instrument.tools?.tools[0]?.id ?? null : labTool;
|
|
3145
2835
|
const setActiveTool = (id) => {
|
|
3146
2836
|
if (declaresTools) setTrialTool(record.id, id);
|
|
3147
2837
|
else setLabTool(id);
|
|
@@ -3176,9 +2866,9 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3176
2866
|
view: record.view,
|
|
3177
2867
|
setView,
|
|
3178
2868
|
zoom: view2d?.zoom ?? 1,
|
|
3179
|
-
setZoom: (
|
|
2869
|
+
setZoom: (z2) => {
|
|
3180
2870
|
if (!view2d) return;
|
|
3181
|
-
updateTrialView(record.id,
|
|
2871
|
+
updateTrialView(record.id, withZoom(view2d, z2));
|
|
3182
2872
|
},
|
|
3183
2873
|
activeToolId: resolvedToolId,
|
|
3184
2874
|
visibleLayers
|
|
@@ -3222,7 +2912,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3222
2912
|
const canvasLayers = useMemo(() => {
|
|
3223
2913
|
if (!instrument.canvas) return [];
|
|
3224
2914
|
const baseLayers = instrument.canvas.layers;
|
|
3225
|
-
const ordered = layerOrder ? [...baseLayers].sort((
|
|
2915
|
+
const ordered = layerOrder ? [...baseLayers].sort((a2, b2) => layerOrder.indexOf(a2.id) - layerOrder.indexOf(b2.id)) : baseLayers;
|
|
3226
2916
|
return ordered.map((layer) => ({
|
|
3227
2917
|
id: layer.id,
|
|
3228
2918
|
visible: layerVisibility[layer.id] !== false,
|
|
@@ -3234,7 +2924,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3234
2924
|
}, [instrument.canvas, record.state, record.config, layerVisibility, layerOrder]);
|
|
3235
2925
|
const layerDescriptors = useMemo(() => {
|
|
3236
2926
|
if (!instrument.layers) return [];
|
|
3237
|
-
return instrument.layers.ids.map((
|
|
2927
|
+
return instrument.layers.ids.map((l2) => typeof l2 === "string" ? { id: l2, label: l2 } : l2);
|
|
3238
2928
|
}, [instrument.layers]);
|
|
3239
2929
|
const dragDropResult = useDragDrop({
|
|
3240
2930
|
capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i, s) => s },
|
|
@@ -3254,8 +2944,8 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3254
2944
|
});
|
|
3255
2945
|
const paletteItems = useMemo(() => {
|
|
3256
2946
|
if (!instrument.dragDrop) return [];
|
|
3257
|
-
const
|
|
3258
|
-
return typeof
|
|
2947
|
+
const p2 = instrument.dragDrop.palette;
|
|
2948
|
+
return typeof p2 === "function" ? p2(record.state, record.config) : p2;
|
|
3259
2949
|
}, [instrument.dragDrop, record.state, record.config]);
|
|
3260
2950
|
const layersWithFeedback = useMemo(() => {
|
|
3261
2951
|
if (!dragDropResult.drag?.feedback) return canvasLayers;
|
|
@@ -3269,12 +2959,12 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3269
2959
|
render: (ctx) => {
|
|
3270
2960
|
const el = canvasContainerRef.current;
|
|
3271
2961
|
if (!el) return;
|
|
3272
|
-
const
|
|
2962
|
+
const r3 = el.getBoundingClientRect();
|
|
3273
2963
|
ctx.save();
|
|
3274
2964
|
ctx.strokeStyle = fb.ok ? "#3a7" : "#c44";
|
|
3275
2965
|
ctx.lineWidth = 2;
|
|
3276
2966
|
ctx.beginPath();
|
|
3277
|
-
ctx.arc(screen.x -
|
|
2967
|
+
ctx.arc(screen.x - r3.left, screen.y - r3.top, 16, 0, Math.PI * 2);
|
|
3278
2968
|
ctx.stroke();
|
|
3279
2969
|
ctx.restore();
|
|
3280
2970
|
}
|
|
@@ -3314,6 +3004,21 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3314
3004
|
),
|
|
3315
3005
|
/* @__PURE__ */ jsx(DragOverlay, { drag: dragDropResult.drag })
|
|
3316
3006
|
] });
|
|
3007
|
+
} else if (stage) {
|
|
3008
|
+
body = /* @__PURE__ */ jsx(
|
|
3009
|
+
Stage,
|
|
3010
|
+
{
|
|
3011
|
+
size: stage.size,
|
|
3012
|
+
view: view2d ?? DEFAULT_VIEW,
|
|
3013
|
+
onViewChange: setView,
|
|
3014
|
+
onResize: placeView,
|
|
3015
|
+
minZoom: stage.minZoom,
|
|
3016
|
+
maxZoom: stage.maxZoom,
|
|
3017
|
+
hostRef: loupeHostRef,
|
|
3018
|
+
overlay: lens,
|
|
3019
|
+
children: instrument.render(renderCtx)
|
|
3020
|
+
}
|
|
3021
|
+
);
|
|
3317
3022
|
} else if (lens) {
|
|
3318
3023
|
body = /* @__PURE__ */ jsxs("div", { ref: loupeHostRef, className: "lk-trial__loupe-host", children: [
|
|
3319
3024
|
instrument.render(renderCtx),
|
|
@@ -3334,7 +3039,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3334
3039
|
layers: layerDescriptors,
|
|
3335
3040
|
visibility: layerVisibility,
|
|
3336
3041
|
onReorder: (next) => {
|
|
3337
|
-
setLayerOrder(next.map((
|
|
3042
|
+
setLayerOrder(next.map((l2) => l2.id));
|
|
3338
3043
|
bus.emit("layers.reorder");
|
|
3339
3044
|
},
|
|
3340
3045
|
onToggle: (lid, visible) => {
|
|
@@ -3371,10 +3076,10 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3371
3076
|
config: record.config,
|
|
3372
3077
|
trial: { id: record.id, view: record.view },
|
|
3373
3078
|
annotations,
|
|
3374
|
-
activeToolId:
|
|
3079
|
+
activeToolId: labTool
|
|
3375
3080
|
}
|
|
3376
3081
|
) : null;
|
|
3377
|
-
return /* @__PURE__ */ jsx(TrialIdProvider, { trialId: record.id, children: /* @__PURE__ */ jsx(AnnotationsContext.Provider, { value: annotationsCap ? annotations : null, children: /* @__PURE__ */ jsxs(
|
|
3082
|
+
return /* @__PURE__ */ jsx(TrialIdProvider, { trialId: record.id, children: /* @__PURE__ */ jsx(AnnotationsContext.Provider, { value: annotationsCap ? annotations : null, children: /* @__PURE__ */ jsx(CameraWheelContext.Provider, { value: wheelSlot, children: /* @__PURE__ */ jsxs(
|
|
3378
3083
|
TrialChrome,
|
|
3379
3084
|
{
|
|
3380
3085
|
job: jobCap ? job : void 0,
|
|
@@ -3394,7 +3099,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3394
3099
|
annotationOverlays
|
|
3395
3100
|
]
|
|
3396
3101
|
}
|
|
3397
|
-
) }) });
|
|
3102
|
+
) }) }) });
|
|
3398
3103
|
}
|
|
3399
3104
|
|
|
3400
3105
|
// src/trial/trialOps.ts
|
|
@@ -3407,8 +3112,9 @@ function findInstrument(instruments, name) {
|
|
|
3407
3112
|
return found;
|
|
3408
3113
|
}
|
|
3409
3114
|
function initialView(instrument) {
|
|
3410
|
-
const
|
|
3411
|
-
|
|
3115
|
+
const stage = instrument.canvas ? void 0 : instrument.stage;
|
|
3116
|
+
const declared = instrument.canvas?.initialView ?? stage?.initialView;
|
|
3117
|
+
if (typeof declared === "function" || stage && declared === void 0) return null;
|
|
3412
3118
|
return declared ? structuredClone(declared) : { ...DEFAULT_VIEW2, pan: { ...DEFAULT_VIEW2.pan } };
|
|
3413
3119
|
}
|
|
3414
3120
|
function seedConfig(defaults, seed) {
|
|
@@ -3419,7 +3125,7 @@ function addTrial(trials, instruments, instrumentName, options = {}) {
|
|
|
3419
3125
|
const config = seedConfig(instrument.defaultConfig(), options.config);
|
|
3420
3126
|
const state = instrument.initialState(config);
|
|
3421
3127
|
const record = {
|
|
3422
|
-
id:
|
|
3128
|
+
id: newId(),
|
|
3423
3129
|
instrumentName,
|
|
3424
3130
|
config,
|
|
3425
3131
|
state,
|
|
@@ -3430,12 +3136,12 @@ function addTrial(trials, instruments, instrumentName, options = {}) {
|
|
|
3430
3136
|
return [...trials, record];
|
|
3431
3137
|
}
|
|
3432
3138
|
function cloneTrial(trials, id) {
|
|
3433
|
-
const sourceIdx = trials.findIndex((
|
|
3139
|
+
const sourceIdx = trials.findIndex((w2) => w2.id === id);
|
|
3434
3140
|
const source = trials[sourceIdx];
|
|
3435
3141
|
if (!source) return trials;
|
|
3436
3142
|
const clone = {
|
|
3437
3143
|
...source,
|
|
3438
|
-
id:
|
|
3144
|
+
id: newId(),
|
|
3439
3145
|
config: structuredClone(source.config),
|
|
3440
3146
|
state: structuredClone(source.state),
|
|
3441
3147
|
view: structuredClone(source.view),
|
|
@@ -3445,11 +3151,11 @@ function cloneTrial(trials, id) {
|
|
|
3445
3151
|
}
|
|
3446
3152
|
function closeTrial(trials, id) {
|
|
3447
3153
|
if (trials.length <= 1) return trials;
|
|
3448
|
-
const next = trials.filter((
|
|
3154
|
+
const next = trials.filter((w2) => w2.id !== id);
|
|
3449
3155
|
return next.length === trials.length ? trials : next;
|
|
3450
3156
|
}
|
|
3451
3157
|
function resetTrial(trials, id, instruments) {
|
|
3452
|
-
const idx = trials.findIndex((
|
|
3158
|
+
const idx = trials.findIndex((w2) => w2.id === id);
|
|
3453
3159
|
const current = trials[idx];
|
|
3454
3160
|
if (!current) return trials;
|
|
3455
3161
|
const instrument = findInstrument(instruments, current.instrumentName);
|
|
@@ -3464,12 +3170,92 @@ function resetTrial(trials, id, instruments) {
|
|
|
3464
3170
|
return [...trials.slice(0, idx), reset, ...trials.slice(idx + 1)];
|
|
3465
3171
|
}
|
|
3466
3172
|
function reorderTrials(trials, ids) {
|
|
3467
|
-
const byId = new Map(trials.map((
|
|
3468
|
-
const named = ids.map((id) => byId.get(id)).filter((
|
|
3469
|
-
const seen = new Set(named.map((
|
|
3470
|
-
const rest = trials.filter((
|
|
3173
|
+
const byId = new Map(trials.map((w2) => [w2.id, w2]));
|
|
3174
|
+
const named = ids.map((id) => byId.get(id)).filter((w2) => w2 !== void 0);
|
|
3175
|
+
const seen = new Set(named.map((w2) => w2.id));
|
|
3176
|
+
const rest = trials.filter((w2) => !seen.has(w2.id));
|
|
3471
3177
|
return [...named, ...rest];
|
|
3472
3178
|
}
|
|
3179
|
+
function findOvershoot(bound, items, tolerance = 1) {
|
|
3180
|
+
let worst = null;
|
|
3181
|
+
for (const { item, edges } of items) {
|
|
3182
|
+
const y = edges.bottom - bound.bottom;
|
|
3183
|
+
const x = edges.right - bound.right;
|
|
3184
|
+
const by = Math.max(x, y);
|
|
3185
|
+
if (by <= tolerance) continue;
|
|
3186
|
+
if (!worst || by > worst.by) worst = { item, by, axis: y >= x ? "y" : "x" };
|
|
3187
|
+
}
|
|
3188
|
+
return worst;
|
|
3189
|
+
}
|
|
3190
|
+
function tagAndClasses(el) {
|
|
3191
|
+
return el.tagName.toLowerCase() + [...el.classList].map((c3) => `.${c3}`).join("");
|
|
3192
|
+
}
|
|
3193
|
+
function describeElement(el) {
|
|
3194
|
+
if (el.classList.length > 0) return tagAndClasses(el);
|
|
3195
|
+
let up = el.parentElement;
|
|
3196
|
+
while (up && up.classList.length === 0) up = up.parentElement;
|
|
3197
|
+
return up ? `${tagAndClasses(el)} in ${tagAndClasses(up)}` : tagAndClasses(el);
|
|
3198
|
+
}
|
|
3199
|
+
function edgesOf(el) {
|
|
3200
|
+
const r3 = el.getBoundingClientRect();
|
|
3201
|
+
return { top: r3.top, left: r3.left, bottom: r3.bottom, right: r3.right };
|
|
3202
|
+
}
|
|
3203
|
+
function* candidates(root) {
|
|
3204
|
+
yield { item: root, edges: edgesOf(root) };
|
|
3205
|
+
for (const el of root.querySelectorAll("*")) yield { item: el, edges: edgesOf(el) };
|
|
3206
|
+
}
|
|
3207
|
+
function overflows(el) {
|
|
3208
|
+
return el.scrollHeight > el.clientHeight + 1 || el.scrollWidth > el.clientWidth + 1;
|
|
3209
|
+
}
|
|
3210
|
+
function explainLabScroll(lab, shellBody) {
|
|
3211
|
+
if (overflows(shellBody)) {
|
|
3212
|
+
const r4 = shellBody.getBoundingClientRect();
|
|
3213
|
+
const bound2 = {
|
|
3214
|
+
top: r4.top,
|
|
3215
|
+
left: r4.left,
|
|
3216
|
+
bottom: r4.top + shellBody.clientHeight,
|
|
3217
|
+
right: r4.left + shellBody.clientWidth
|
|
3218
|
+
};
|
|
3219
|
+
const hit2 = findOvershoot(bound2, candidates(shellBody));
|
|
3220
|
+
return hit2 ? `.lk-shell-body scrolls: ${describeElement(hit2.item)} reaches ${Math.round(hit2.by)}px past it (${hit2.axis})` : ".lk-shell-body scrolls";
|
|
3221
|
+
}
|
|
3222
|
+
const doc = document.scrollingElement ?? document.documentElement;
|
|
3223
|
+
if (!overflows(doc)) return null;
|
|
3224
|
+
const r3 = lab.getBoundingClientRect();
|
|
3225
|
+
const bound = {
|
|
3226
|
+
top: r3.top,
|
|
3227
|
+
left: r3.left,
|
|
3228
|
+
bottom: r3.top + Math.min(r3.height, doc.clientHeight),
|
|
3229
|
+
right: r3.left + Math.min(r3.width, doc.clientWidth)
|
|
3230
|
+
};
|
|
3231
|
+
const hit = findOvershoot(bound, candidates(lab));
|
|
3232
|
+
return hit ? `the page scrolls: ${describeElement(hit.item)} reaches ${Math.round(hit.by)}px past the lab's box (${hit.axis})` : null;
|
|
3233
|
+
}
|
|
3234
|
+
var SETTLE_MS = 300;
|
|
3235
|
+
function useLabFitWarning(labBody) {
|
|
3236
|
+
useEffect(() => {
|
|
3237
|
+
if (process.env.NODE_ENV === "production") return;
|
|
3238
|
+
if (!labBody || typeof ResizeObserver === "undefined") return;
|
|
3239
|
+
const lab = labBody.closest(".lk-lab");
|
|
3240
|
+
const shellBody = labBody.closest(".lk-shell-body");
|
|
3241
|
+
if (!lab || !shellBody) return;
|
|
3242
|
+
let timer;
|
|
3243
|
+
const observer = new ResizeObserver(() => {
|
|
3244
|
+
clearTimeout(timer);
|
|
3245
|
+
timer = setTimeout(() => {
|
|
3246
|
+
const why = explainLabScroll(lab, shellBody);
|
|
3247
|
+
if (!why) return;
|
|
3248
|
+
observer.disconnect();
|
|
3249
|
+
console.warn(`[labkit] <Lab> should fit its container without scrolling, but ${why}.`);
|
|
3250
|
+
}, SETTLE_MS);
|
|
3251
|
+
});
|
|
3252
|
+
for (const el of [document.documentElement, lab, shellBody, labBody]) observer.observe(el);
|
|
3253
|
+
return () => {
|
|
3254
|
+
clearTimeout(timer);
|
|
3255
|
+
observer.disconnect();
|
|
3256
|
+
};
|
|
3257
|
+
}, [labBody]);
|
|
3258
|
+
}
|
|
3473
3259
|
var MODES = [
|
|
3474
3260
|
{ value: "auto", label: "Auto", glyph: /* @__PURE__ */ jsx(E, { size: 14 }) },
|
|
3475
3261
|
{ value: "light", label: "Light", glyph: /* @__PURE__ */ jsx(w, { size: 14 }) },
|
|
@@ -3480,11 +3266,11 @@ var MODE_ITEMS = MODES.map(({ value, label, glyph }) => ({
|
|
|
3480
3266
|
label: glyph,
|
|
3481
3267
|
ariaLabel: label
|
|
3482
3268
|
}));
|
|
3483
|
-
function LabHeader() {
|
|
3269
|
+
function LabHeader({ addTrial: addTrial2 = true }) {
|
|
3484
3270
|
const lab = useLabContext();
|
|
3485
3271
|
const only = lab.instruments.length === 1 ? lab.instruments[0] : null;
|
|
3486
3272
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3487
|
-
only ? /* @__PURE__ */ jsxs(
|
|
3273
|
+
!addTrial2 ? null : only ? /* @__PURE__ */ jsxs(
|
|
3488
3274
|
"button",
|
|
3489
3275
|
{
|
|
3490
3276
|
type: "button",
|
|
@@ -3495,22 +3281,14 @@ function LabHeader() {
|
|
|
3495
3281
|
/* @__PURE__ */ jsx("span", { children: "Add trial" })
|
|
3496
3282
|
]
|
|
3497
3283
|
}
|
|
3498
|
-
) : (
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
{
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
placeholder: "Add trial\u2026",
|
|
3507
|
-
selectedKey: null,
|
|
3508
|
-
options: lab.instruments.map((i) => ({ value: i.name, label: i.name })),
|
|
3509
|
-
onSelectionChange: (name) => {
|
|
3510
|
-
if (name != null) lab.addTrial(String(name));
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
)
|
|
3284
|
+
) : /* @__PURE__ */ jsx(
|
|
3285
|
+
l5,
|
|
3286
|
+
{
|
|
3287
|
+
"aria-label": "Add trial",
|
|
3288
|
+
label: "Add trial\u2026",
|
|
3289
|
+
items: lab.instruments.map((i) => ({ value: i.name, label: i.title ?? i.name })),
|
|
3290
|
+
onAction: (name) => lab.addTrial(name)
|
|
3291
|
+
}
|
|
3514
3292
|
),
|
|
3515
3293
|
/* @__PURE__ */ jsx(
|
|
3516
3294
|
r6,
|
|
@@ -3528,22 +3306,71 @@ function LabHeader() {
|
|
|
3528
3306
|
)
|
|
3529
3307
|
] });
|
|
3530
3308
|
}
|
|
3531
|
-
function LabPalette({ tools }) {
|
|
3532
|
-
const
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
const
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3309
|
+
function LabPalette({ tools, contributions }) {
|
|
3310
|
+
const ctx = useLabChromeContext();
|
|
3311
|
+
const all = labContributions(tools, contributions);
|
|
3312
|
+
return /* @__PURE__ */ jsx(PaletteRegion, { contributions: all.filter((c3) => c3.region === "palette"), ctx });
|
|
3313
|
+
}
|
|
3314
|
+
function currentPage(path, pages) {
|
|
3315
|
+
const here = path.split("?")[0].split("#")[0].replace(/\/$/, "").replace(/\.html$/, "");
|
|
3316
|
+
return pages.findIndex((p2) => here.endsWith(p2.href.replace(/\.html$/, "")));
|
|
3317
|
+
}
|
|
3318
|
+
function LabSwitcher({ title, pages, path, className }) {
|
|
3319
|
+
const [open, setOpen] = useState(false);
|
|
3320
|
+
const root = useRef(null);
|
|
3321
|
+
const menuId = useId();
|
|
3322
|
+
const here = currentPage(path ?? (typeof window === "undefined" ? "" : window.location.pathname), pages);
|
|
3323
|
+
const close = useCallback(() => setOpen(false), []);
|
|
3324
|
+
useEffect(() => {
|
|
3325
|
+
if (!open) return;
|
|
3326
|
+
const onKey = (e) => {
|
|
3327
|
+
if (e.key === "Escape") {
|
|
3328
|
+
close();
|
|
3329
|
+
root.current?.querySelector("button")?.focus();
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
const onDown = (e) => {
|
|
3333
|
+
if (!root.current?.contains(e.target)) close();
|
|
3334
|
+
};
|
|
3335
|
+
document.addEventListener("keydown", onKey);
|
|
3336
|
+
document.addEventListener("pointerdown", onDown);
|
|
3337
|
+
return () => {
|
|
3338
|
+
document.removeEventListener("keydown", onKey);
|
|
3339
|
+
document.removeEventListener("pointerdown", onDown);
|
|
3340
|
+
};
|
|
3341
|
+
}, [open, close]);
|
|
3342
|
+
if (pages.length < 2) {
|
|
3343
|
+
return /* @__PURE__ */ jsx("h1", { className: `lk-switcher-title${className ? ` ${className}` : ""}`, children: title });
|
|
3344
|
+
}
|
|
3345
|
+
return /* @__PURE__ */ jsxs("div", { className: `lk-switcher${className ? ` ${className}` : ""}`, ref: root, children: [
|
|
3346
|
+
/* @__PURE__ */ jsx("h1", { className: "lk-switcher-title", children: /* @__PURE__ */ jsxs(
|
|
3347
|
+
"button",
|
|
3348
|
+
{
|
|
3349
|
+
type: "button",
|
|
3350
|
+
className: "lk-switcher-trigger",
|
|
3351
|
+
"aria-expanded": open,
|
|
3352
|
+
"aria-haspopup": "menu",
|
|
3353
|
+
"aria-controls": open ? menuId : void 0,
|
|
3354
|
+
onClick: () => setOpen((was) => !was),
|
|
3355
|
+
children: [
|
|
3356
|
+
title,
|
|
3357
|
+
/* @__PURE__ */ jsx("span", { className: "lk-switcher-caret", "aria-hidden": "true" })
|
|
3358
|
+
]
|
|
3359
|
+
}
|
|
3360
|
+
) }),
|
|
3361
|
+
open && /* @__PURE__ */ jsx("div", { className: "lk-switcher-menu", id: menuId, role: "menu", children: pages.map((page, i) => /* @__PURE__ */ jsx(
|
|
3362
|
+
"a",
|
|
3363
|
+
{
|
|
3364
|
+
href: page.href,
|
|
3365
|
+
role: "menuitem",
|
|
3366
|
+
className: "lk-switcher-item",
|
|
3367
|
+
"aria-current": i === here ? "page" : void 0,
|
|
3368
|
+
onClick: close,
|
|
3369
|
+
children: page.label
|
|
3370
|
+
},
|
|
3371
|
+
page.href
|
|
3372
|
+
)) })
|
|
3373
|
+
] });
|
|
3547
3374
|
}
|
|
3548
3375
|
function query() {
|
|
3549
3376
|
if (typeof window === "undefined" || !window.matchMedia) return null;
|
|
@@ -3563,7 +3390,15 @@ function useResolvedMode(mode) {
|
|
|
3563
3390
|
}, [mode]);
|
|
3564
3391
|
return mode === "auto" ? system : mode;
|
|
3565
3392
|
}
|
|
3566
|
-
function LabShell({
|
|
3393
|
+
function LabShell({
|
|
3394
|
+
title,
|
|
3395
|
+
children,
|
|
3396
|
+
header,
|
|
3397
|
+
footer,
|
|
3398
|
+
mode = "auto",
|
|
3399
|
+
pages,
|
|
3400
|
+
path
|
|
3401
|
+
}) {
|
|
3567
3402
|
const resolved = useResolvedMode(mode);
|
|
3568
3403
|
const outer = useThemeOptional();
|
|
3569
3404
|
const shell = (
|
|
@@ -3571,7 +3406,15 @@ function LabShell({ title, children, header, footer, mode = "auto" }) {
|
|
|
3571
3406
|
// carries the element defaults a lab's bare markup is styled by.
|
|
3572
3407
|
/* @__PURE__ */ jsxs("div", { className: "lk-root lk-shell", "data-wzl-portal-host": "", children: [
|
|
3573
3408
|
/* @__PURE__ */ jsxs("header", { className: "lk-shell-header", children: [
|
|
3574
|
-
/* @__PURE__ */ jsx(
|
|
3409
|
+
pages && pages.length > 0 ? /* @__PURE__ */ jsx(
|
|
3410
|
+
LabSwitcher,
|
|
3411
|
+
{
|
|
3412
|
+
title,
|
|
3413
|
+
pages,
|
|
3414
|
+
path,
|
|
3415
|
+
className: "lk-shell-title"
|
|
3416
|
+
}
|
|
3417
|
+
) : /* @__PURE__ */ jsx("h1", { className: "lk-shell-title", children: title }),
|
|
3575
3418
|
header && /* @__PURE__ */ jsx("div", { className: "lk-shell-header-actions", children: header })
|
|
3576
3419
|
] }),
|
|
3577
3420
|
/* @__PURE__ */ jsx("main", { className: "lk-shell-body", children }),
|
|
@@ -3580,17 +3423,17 @@ function LabShell({ title, children, header, footer, mode = "auto" }) {
|
|
|
3580
3423
|
);
|
|
3581
3424
|
return outer ? shell : /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, mode: resolved, children: shell });
|
|
3582
3425
|
}
|
|
3583
|
-
var
|
|
3584
|
-
var
|
|
3426
|
+
var ZONE_ID = asNodeId("lk-workspace");
|
|
3427
|
+
var STRATEGIES = { grid: gridStrategy, floating: floatingStrategy };
|
|
3585
3428
|
var FLOAT_ZONE_ID = asNodeId("lk-workspace-floating");
|
|
3586
3429
|
var PANEL_KIND = "panel";
|
|
3587
3430
|
var KIND = "trial";
|
|
3588
3431
|
function extentOf(store, id) {
|
|
3589
|
-
const
|
|
3590
|
-
if (!
|
|
3432
|
+
const p2 = store.getNode(id)?.membership?.placement;
|
|
3433
|
+
if (!p2) return null;
|
|
3591
3434
|
const out = {};
|
|
3592
|
-
if (
|
|
3593
|
-
if (
|
|
3435
|
+
if (p2.size) out.size = p2.size;
|
|
3436
|
+
if (p2.span) out.span = p2.span;
|
|
3594
3437
|
return Object.keys(out).length > 0 ? out : null;
|
|
3595
3438
|
}
|
|
3596
3439
|
function PanelFrame({ panel, floating = false }) {
|
|
@@ -3611,11 +3454,11 @@ function Workspace({
|
|
|
3611
3454
|
viewport
|
|
3612
3455
|
}) {
|
|
3613
3456
|
const items = Children.toArray(children);
|
|
3614
|
-
const tilePanels = useMemo(() => (panels ?? []).filter((
|
|
3615
|
-
const floatPanels = useMemo(() => (panels ?? []).filter((
|
|
3457
|
+
const tilePanels = useMemo(() => (panels ?? []).filter((p2) => p2.as === "tile"), [panels]);
|
|
3458
|
+
const floatPanels = useMemo(() => (panels ?? []).filter((p2) => p2.as === "floating"), [panels]);
|
|
3616
3459
|
const wantedIds = [
|
|
3617
3460
|
...items.map((_, i) => ids?.[i] ?? `lk-ws-${i}`),
|
|
3618
|
-
...tilePanels.map((
|
|
3461
|
+
...tilePanels.map((p2) => `lk-panel-${p2.key}`)
|
|
3619
3462
|
];
|
|
3620
3463
|
const idKey = wantedIds.join(",");
|
|
3621
3464
|
const idKeyRef = useRef(null);
|
|
@@ -3635,7 +3478,7 @@ function Workspace({
|
|
|
3635
3478
|
store2.registerNode(
|
|
3636
3479
|
createNode({
|
|
3637
3480
|
kind: "zone",
|
|
3638
|
-
id:
|
|
3481
|
+
id: ZONE_ID,
|
|
3639
3482
|
container: { strategyId: "grid", config: { resizable, gap, padding } }
|
|
3640
3483
|
})
|
|
3641
3484
|
);
|
|
@@ -3651,10 +3494,10 @@ function Workspace({
|
|
|
3651
3494
|
const store = storeRef.current;
|
|
3652
3495
|
const surface = useSurfaceOptional();
|
|
3653
3496
|
useLayoutEffect(() => {
|
|
3654
|
-
store.updateContainerConfig(
|
|
3497
|
+
store.updateContainerConfig(ZONE_ID, { resizable, gap, padding });
|
|
3655
3498
|
}, [store, resizable, gap, padding]);
|
|
3656
3499
|
useLayoutEffect(() => {
|
|
3657
|
-
const present = new Set(store.getContainerView(
|
|
3500
|
+
const present = new Set(store.getContainerView(ZONE_ID)?.childOrder ?? []);
|
|
3658
3501
|
const wanted = new Set(nodeIds);
|
|
3659
3502
|
for (const id of present) {
|
|
3660
3503
|
if (!wanted.has(id)) store.unregisterNode(id);
|
|
@@ -3662,12 +3505,12 @@ function Workspace({
|
|
|
3662
3505
|
for (const id of nodeIds) {
|
|
3663
3506
|
if (present.has(id)) continue;
|
|
3664
3507
|
const kind = String(id).startsWith("lk-panel-") ? PANEL_KIND : KIND;
|
|
3665
|
-
store.registerNode(createNode({ kind, id, parentId:
|
|
3508
|
+
store.registerNode(createNode({ kind, id, parentId: ZONE_ID, focus: true }));
|
|
3666
3509
|
store.showNode(id);
|
|
3667
3510
|
const saved = layoutRef.current?.[id];
|
|
3668
3511
|
if (saved) store.patchPlacement(id, saved);
|
|
3669
3512
|
}
|
|
3670
|
-
store.setChildOrder(
|
|
3513
|
+
store.setChildOrder(ZONE_ID, [...nodeIds]);
|
|
3671
3514
|
}, [store, nodeIds]);
|
|
3672
3515
|
useEffect(() => {
|
|
3673
3516
|
if (!surface) return;
|
|
@@ -3678,7 +3521,7 @@ function Workspace({
|
|
|
3678
3521
|
if (!onLayoutChange) return;
|
|
3679
3522
|
return store.events.on("node.placementChanged", () => {
|
|
3680
3523
|
const next = {};
|
|
3681
|
-
for (const child of store.getChildren(
|
|
3524
|
+
for (const child of store.getChildren(ZONE_ID)) {
|
|
3682
3525
|
const extent = extentOf(store, child.id);
|
|
3683
3526
|
if (extent) next[child.id] = extent;
|
|
3684
3527
|
}
|
|
@@ -3692,7 +3535,7 @@ function Workspace({
|
|
|
3692
3535
|
const chrome = useMemo(() => {
|
|
3693
3536
|
const byId = new Map(nodeIds.map((id, i) => [id, items[i]]));
|
|
3694
3537
|
const panelByNode = new Map(
|
|
3695
|
-
tilePanels.map((
|
|
3538
|
+
tilePanels.map((p2) => [`lk-panel-${p2.key}`, p2])
|
|
3696
3539
|
);
|
|
3697
3540
|
return {
|
|
3698
3541
|
[PANEL_KIND]: ({ node }) => {
|
|
@@ -3710,11 +3553,11 @@ function Workspace({
|
|
|
3710
3553
|
}
|
|
3711
3554
|
};
|
|
3712
3555
|
}, [nodeIds, items, reorderable, tilePanels]);
|
|
3713
|
-
const floatingLayer = floatPanels.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "lk-workspace__floating", children: floatPanels.map((
|
|
3556
|
+
const floatingLayer = floatPanels.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "lk-workspace__floating", children: floatPanels.map((p2) => /* @__PURE__ */ jsx(PanelFrame, { panel: p2, floating: true }, p2.key)) });
|
|
3714
3557
|
const grid = /* @__PURE__ */ jsx(
|
|
3715
3558
|
Container,
|
|
3716
3559
|
{
|
|
3717
|
-
parentId:
|
|
3560
|
+
parentId: ZONE_ID,
|
|
3718
3561
|
chrome,
|
|
3719
3562
|
className: "lk-workspace windease-zone",
|
|
3720
3563
|
affordances: resizable,
|
|
@@ -3722,7 +3565,7 @@ function Workspace({
|
|
|
3722
3565
|
onChildOrderChange: reorderable ? commitOrder : void 0
|
|
3723
3566
|
}
|
|
3724
3567
|
);
|
|
3725
|
-
return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies:
|
|
3568
|
+
return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES, children: floatingLayer ? (
|
|
3726
3569
|
// The floating layer insets against the workspace, so it needs a
|
|
3727
3570
|
// positioned box around both. Without one it insets against the page
|
|
3728
3571
|
// and lands over the lab header.
|
|
@@ -3732,23 +3575,59 @@ function Workspace({
|
|
|
3732
3575
|
] })
|
|
3733
3576
|
) : reorderable ? /* @__PURE__ */ jsx(DragProvider, { children: grid }) : grid }) });
|
|
3734
3577
|
}
|
|
3735
|
-
function
|
|
3736
|
-
|
|
3578
|
+
function seedDefaultTrial(store, instruments, defaultInstrument) {
|
|
3579
|
+
if (store.getState().trials.length > 0) return;
|
|
3580
|
+
const record = addTrial([], instruments, defaultInstrument)[0];
|
|
3581
|
+
if (!record) return;
|
|
3582
|
+
const { undoStack: _undoStack, ...rest } = record;
|
|
3583
|
+
store.getState().addTrial(rest);
|
|
3584
|
+
}
|
|
3585
|
+
function openUnstoredLab({ instruments, defaultInstrument, mode }) {
|
|
3586
|
+
const store = createLabStore({ initialMode: mode ?? "auto", instruments });
|
|
3587
|
+
seedDefaultTrial(store, instruments, defaultInstrument);
|
|
3588
|
+
const records = createRecordCache({ storage: noneAdapter, prefix: "", writable: false });
|
|
3589
|
+
return { store, records, close: () => records.close(), marks: /* @__PURE__ */ new Map() };
|
|
3590
|
+
}
|
|
3591
|
+
async function openStoredLab({ instruments, defaultInstrument, mode }, storageKey, storage) {
|
|
3592
|
+
const opened = await openLabStore({
|
|
3737
3593
|
storageKey,
|
|
3738
|
-
storage,
|
|
3739
|
-
initialMode,
|
|
3740
|
-
|
|
3741
|
-
configDefaults: configDefaultsOf(instruments)
|
|
3594
|
+
storage: storage ?? await defaultStorage(),
|
|
3595
|
+
initialMode: mode ?? "auto",
|
|
3596
|
+
instruments
|
|
3742
3597
|
});
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
const
|
|
3748
|
-
|
|
3598
|
+
seedDefaultTrial(opened.store, instruments, defaultInstrument);
|
|
3599
|
+
const marks = /* @__PURE__ */ new Map();
|
|
3600
|
+
await Promise.all(
|
|
3601
|
+
opened.store.getState().trials.map(async (trial) => {
|
|
3602
|
+
const kept = instruments.find((i) => i.name === trial.instrumentName)?.annotations?.storage;
|
|
3603
|
+
if (!kept) return;
|
|
3604
|
+
try {
|
|
3605
|
+
marks.set(trial.id, await kept.load());
|
|
3606
|
+
} catch (error) {
|
|
3607
|
+
console.warn(`[labkit] could not load the marks of trial "${trial.id}"`, error);
|
|
3608
|
+
marks.set(trial.id, null);
|
|
3609
|
+
}
|
|
3610
|
+
})
|
|
3611
|
+
);
|
|
3612
|
+
return { ...opened, marks };
|
|
3613
|
+
}
|
|
3614
|
+
function LabFallback({
|
|
3615
|
+
title,
|
|
3616
|
+
mode,
|
|
3617
|
+
pages,
|
|
3618
|
+
path
|
|
3619
|
+
}) {
|
|
3620
|
+
const resolvedMode = useResolvedMode(mode ?? "auto");
|
|
3621
|
+
return /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, mode: resolvedMode, className: "lk-lab", children: /* @__PURE__ */ jsx(
|
|
3622
|
+
LabShell,
|
|
3623
|
+
{
|
|
3624
|
+
title: title ?? "Labkit",
|
|
3625
|
+
mode,
|
|
3626
|
+
...pages ? { pages } : {},
|
|
3627
|
+
...path !== void 0 ? { path } : {},
|
|
3628
|
+
children: null
|
|
3749
3629
|
}
|
|
3750
|
-
}
|
|
3751
|
-
return store;
|
|
3630
|
+
) });
|
|
3752
3631
|
}
|
|
3753
3632
|
var NEBULA_SLOTS = [
|
|
3754
3633
|
{ cx: "18%", cy: "30%", sx: "60%", sy: "80%", stop: "70%" },
|
|
@@ -3758,43 +3637,77 @@ var NEBULA_SLOTS = [
|
|
|
3758
3637
|
{ cx: "85%", cy: "18%", sx: "55%", sy: "80%", stop: "70%" }
|
|
3759
3638
|
];
|
|
3760
3639
|
function buildNebula(colors) {
|
|
3761
|
-
const blobs = colors.map((
|
|
3762
|
-
const
|
|
3763
|
-
return `radial-gradient(ellipse ${
|
|
3640
|
+
const blobs = colors.map((c3, i) => {
|
|
3641
|
+
const p2 = NEBULA_SLOTS[i % NEBULA_SLOTS.length];
|
|
3642
|
+
return `radial-gradient(ellipse ${p2.sx} ${p2.sy} at ${p2.cx} ${p2.cy}, color-mix(in srgb, ${c3} 22%, transparent), transparent ${p2.stop})`;
|
|
3764
3643
|
});
|
|
3765
3644
|
blobs.push("radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)");
|
|
3766
3645
|
return blobs.join(", ");
|
|
3767
3646
|
}
|
|
3768
|
-
function
|
|
3647
|
+
function LabPanes({
|
|
3648
|
+
contributions,
|
|
3649
|
+
children
|
|
3650
|
+
}) {
|
|
3651
|
+
const surface = useSurfaceOptional();
|
|
3652
|
+
const [sidebarWidth, setSidebarWidth] = usePersistedState(
|
|
3653
|
+
"lk-lab-sidebar-width",
|
|
3654
|
+
void 0,
|
|
3655
|
+
{ scope: "lab" }
|
|
3656
|
+
);
|
|
3657
|
+
return /* @__PURE__ */ jsx(
|
|
3658
|
+
Split,
|
|
3659
|
+
{
|
|
3660
|
+
className: "lk-lab__panes",
|
|
3661
|
+
sidebarClassName: "lk-lab__sidebar",
|
|
3662
|
+
contentClassName: "lk-lab__main",
|
|
3663
|
+
label: "Lab sidebar",
|
|
3664
|
+
defaultWidth: 260,
|
|
3665
|
+
width: sidebarWidth,
|
|
3666
|
+
onWidthChange: (w2) => {
|
|
3667
|
+
setSidebarWidth(w2);
|
|
3668
|
+
surface?.invalidateRects();
|
|
3669
|
+
},
|
|
3670
|
+
sidebar: /* @__PURE__ */ jsx(LabSidebarRegion, { contributions }),
|
|
3671
|
+
children
|
|
3672
|
+
}
|
|
3673
|
+
);
|
|
3674
|
+
}
|
|
3675
|
+
function Lab(props) {
|
|
3676
|
+
if (process.env.NODE_ENV !== "production" && props.instruments.length === 0) {
|
|
3677
|
+
throw new Error("[labkit] <Lab> requires a non-empty `instruments` array");
|
|
3678
|
+
}
|
|
3679
|
+
const { storageKey, storage } = props;
|
|
3680
|
+
useWarnIgnoredChange("<Lab>", { storageKey, storage });
|
|
3681
|
+
const opened = useOpenOnce(
|
|
3682
|
+
() => storageKey === void 0 ? openUnstoredLab(props) : openStoredLab(props, storageKey, storage)
|
|
3683
|
+
);
|
|
3684
|
+
if (!opened) {
|
|
3685
|
+
return props.fallback !== void 0 ? /* @__PURE__ */ jsx(Fragment, { children: props.fallback }) : /* @__PURE__ */ jsx(LabFallback, { title: props.title, mode: props.mode, pages: props.pages, path: props.path });
|
|
3686
|
+
}
|
|
3687
|
+
return /* @__PURE__ */ jsx(LabRuntime, { ...props, opened });
|
|
3688
|
+
}
|
|
3689
|
+
function LabRuntime({
|
|
3769
3690
|
instruments,
|
|
3770
|
-
defaultInstrument,
|
|
3771
|
-
storage,
|
|
3772
|
-
storageKey,
|
|
3773
3691
|
mode,
|
|
3774
3692
|
nebula,
|
|
3775
3693
|
title,
|
|
3694
|
+
pages,
|
|
3695
|
+
path,
|
|
3776
3696
|
footer,
|
|
3777
3697
|
chrome,
|
|
3698
|
+
labChrome,
|
|
3778
3699
|
suppress,
|
|
3700
|
+
addTrial: addTrial2,
|
|
3779
3701
|
tools,
|
|
3780
3702
|
configRules,
|
|
3781
3703
|
controls,
|
|
3782
|
-
children
|
|
3704
|
+
children,
|
|
3705
|
+
opened
|
|
3783
3706
|
}) {
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
if (storeRef.current === null) {
|
|
3789
|
-
storeRef.current = buildStore(
|
|
3790
|
-
instruments,
|
|
3791
|
-
defaultInstrument,
|
|
3792
|
-
storage ?? noneAdapter,
|
|
3793
|
-
storageKey ?? "labkit",
|
|
3794
|
-
mode ?? "auto"
|
|
3795
|
-
);
|
|
3796
|
-
}
|
|
3797
|
-
const store = storeRef.current;
|
|
3707
|
+
const { store } = opened;
|
|
3708
|
+
useLayoutEffect(() => {
|
|
3709
|
+
store.getState().setInstruments(instruments);
|
|
3710
|
+
}, [instruments, store]);
|
|
3798
3711
|
const trials = useStore(store, (s) => s.trials);
|
|
3799
3712
|
const savedSnapshots = useStore(store, (s) => s.savedSnapshots);
|
|
3800
3713
|
const modeValue = useStore(store, (s) => s.mode);
|
|
@@ -3803,28 +3716,48 @@ function Lab({
|
|
|
3803
3716
|
const panelHostsRef = useRef(null);
|
|
3804
3717
|
if (panelHostsRef.current === null) panelHostsRef.current = createPanelHostRegistry();
|
|
3805
3718
|
const outerSurface = useSurfaceOptional();
|
|
3806
|
-
const
|
|
3807
|
-
const
|
|
3808
|
-
const
|
|
3719
|
+
const outerOver = useSurfaceCanvas("over");
|
|
3720
|
+
const outerUnder = useSurfaceCanvas("under");
|
|
3721
|
+
const [ownOver, setOwnOver] = useState(null);
|
|
3722
|
+
const [ownUnder, setOwnUnder] = useState(null);
|
|
3723
|
+
const ownOverRef = useRef(null);
|
|
3724
|
+
const ownUnderRef = useRef(null);
|
|
3809
3725
|
const bufferRef = useRef({ w: 0, h: 0 });
|
|
3810
3726
|
const surfaceRef = useRef(null);
|
|
3811
3727
|
const onFrame = useCallback((frame) => {
|
|
3812
|
-
const
|
|
3813
|
-
if (
|
|
3814
|
-
const
|
|
3815
|
-
const
|
|
3816
|
-
if (bufferRef.current.w
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3728
|
+
const canvases = [ownUnderRef.current, ownOverRef.current].filter((c3) => c3 !== null);
|
|
3729
|
+
if (canvases.length === 0) return;
|
|
3730
|
+
const w2 = Math.round(frame.size.width * frame.dpr);
|
|
3731
|
+
const h2 = Math.round(frame.size.height * frame.dpr);
|
|
3732
|
+
if (bufferRef.current.w !== w2 || bufferRef.current.h !== h2) {
|
|
3733
|
+
bufferRef.current = { w: w2, h: h2 };
|
|
3734
|
+
for (const c3 of canvases) {
|
|
3735
|
+
c3.width = w2;
|
|
3736
|
+
c3.height = h2;
|
|
3737
|
+
c3.style.width = `${frame.size.width}px`;
|
|
3738
|
+
c3.style.height = `${frame.size.height}px`;
|
|
3739
|
+
}
|
|
3740
|
+
surfaceRef.current?.invalidateAll();
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3823
3743
|
}, []);
|
|
3824
3744
|
const ownSurface = useTiledSurface({ onFrame });
|
|
3825
3745
|
surfaceRef.current = ownSurface;
|
|
3746
|
+
const [labBody, setLabBody] = useState(null);
|
|
3747
|
+
const attachOwnSurface = ownSurface.containerRef;
|
|
3748
|
+
const labBodyRef = useCallback(
|
|
3749
|
+
(el) => {
|
|
3750
|
+
setLabBody(el);
|
|
3751
|
+
if (!outerSurface) attachOwnSurface(el);
|
|
3752
|
+
},
|
|
3753
|
+
[outerSurface, attachOwnSurface]
|
|
3754
|
+
);
|
|
3755
|
+
useLabFitWarning(labBody);
|
|
3826
3756
|
const surface = outerSurface ?? ownSurface;
|
|
3827
|
-
const
|
|
3757
|
+
const surfaceCanvases = useMemo(
|
|
3758
|
+
() => outerSurface ? { over: outerOver, under: outerUnder } : { over: ownOver, under: ownUnder },
|
|
3759
|
+
[outerSurface, outerOver, outerUnder, ownOver, ownUnder]
|
|
3760
|
+
);
|
|
3828
3761
|
const workspacePanels = useMemo(
|
|
3829
3762
|
() => Object.entries(undockedPanels).map(([key, panel]) => ({
|
|
3830
3763
|
key,
|
|
@@ -3834,6 +3767,13 @@ function Lab({
|
|
|
3834
3767
|
[undockedPanels]
|
|
3835
3768
|
);
|
|
3836
3769
|
const resolvedMode = useResolvedMode(modeValue);
|
|
3770
|
+
const annotates = instruments.some((i) => i.annotations != null);
|
|
3771
|
+
const labChromeAll = useMemo(
|
|
3772
|
+
() => labContributions([...annotates ? ANNOTATION_TOOLS : [], ...tools ?? []], labChrome),
|
|
3773
|
+
[annotates, tools, labChrome]
|
|
3774
|
+
);
|
|
3775
|
+
const hasFooterChrome = labChromeAll.some((c3) => c3.region === "footer");
|
|
3776
|
+
const hasSidebarChrome = labChromeAll.some((c3) => c3.region === "sidebar");
|
|
3837
3777
|
useEffect(() => {
|
|
3838
3778
|
if (mode && mode !== store.getState().mode) {
|
|
3839
3779
|
store.getState().setMode(mode);
|
|
@@ -3841,10 +3781,9 @@ function Lab({
|
|
|
3841
3781
|
}, [mode, store]);
|
|
3842
3782
|
const contextValue = useMemo(() => {
|
|
3843
3783
|
const replaceTrials = (next) => {
|
|
3844
|
-
const currentById = new Map(store.getState().trials.map((
|
|
3845
|
-
const merged = next.map((
|
|
3784
|
+
const currentById = new Map(store.getState().trials.map((w2) => [w2.id, w2]));
|
|
3785
|
+
const merged = next.map((w2) => currentById.get(w2.id) ?? w2);
|
|
3846
3786
|
store.setState({ trials: merged });
|
|
3847
|
-
store.getState().setMode(store.getState().mode);
|
|
3848
3787
|
};
|
|
3849
3788
|
return {
|
|
3850
3789
|
instruments,
|
|
@@ -3866,14 +3805,13 @@ function Lab({
|
|
|
3866
3805
|
},
|
|
3867
3806
|
resetTrial: (id) => {
|
|
3868
3807
|
const next = resetTrial(store.getState().trials, id, instruments);
|
|
3869
|
-
const record = next.find((
|
|
3808
|
+
const record = next.find((w2) => w2.id === id);
|
|
3870
3809
|
if (!record) return;
|
|
3871
3810
|
store.setState((s) => ({
|
|
3872
3811
|
trials: s.trials.map(
|
|
3873
|
-
(
|
|
3812
|
+
(w2) => w2.id === id ? { ...w2, config: record.config, state: record.state, view: record.view } : w2
|
|
3874
3813
|
)
|
|
3875
3814
|
}));
|
|
3876
|
-
store.getState().updateTrialView(id, record.view);
|
|
3877
3815
|
},
|
|
3878
3816
|
savedSnapshots,
|
|
3879
3817
|
saveSnapshot: (trialId, name) => {
|
|
@@ -3895,7 +3833,20 @@ function Lab({
|
|
|
3895
3833
|
};
|
|
3896
3834
|
}, [instruments, trials, savedSnapshots, modeValue, store, configRules, controls]);
|
|
3897
3835
|
const backdropStyle = resolvedMode === "dark" && nebula && nebula.length > 0 ? { ["--wzl-backdrop"]: buildNebula(nebula) } : void 0;
|
|
3898
|
-
|
|
3836
|
+
const workspace = /* @__PURE__ */ jsx(
|
|
3837
|
+
Workspace,
|
|
3838
|
+
{
|
|
3839
|
+
panels: workspacePanels,
|
|
3840
|
+
ids: trials.map((w2) => w2.id),
|
|
3841
|
+
resizable: true,
|
|
3842
|
+
reorderable: true,
|
|
3843
|
+
onReorder: (ids) => contextValue.reorderTrials(ids),
|
|
3844
|
+
layout,
|
|
3845
|
+
onLayoutChange: (next) => store.getState().setLayout(next),
|
|
3846
|
+
children: trials.map((w2) => /* @__PURE__ */ jsx(Trial, { id: w2.id, chrome, suppress }, w2.id))
|
|
3847
|
+
}
|
|
3848
|
+
);
|
|
3849
|
+
return /* @__PURE__ */ jsx(LabStoreContext.Provider, { value: { store }, children: /* @__PURE__ */ jsx(PersistenceContext.Provider, { value: opened.records, children: /* @__PURE__ */ jsx(AnnotationPreloadContext.Provider, { value: opened.marks, children: /* @__PURE__ */ jsx(LabContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
3899
3850
|
ThemeProvider,
|
|
3900
3851
|
{
|
|
3901
3852
|
theme: interstellarTheme,
|
|
@@ -3907,55 +3858,61 @@ function Lab({
|
|
|
3907
3858
|
{
|
|
3908
3859
|
title: title ?? "Labkit",
|
|
3909
3860
|
mode: modeValue,
|
|
3910
|
-
|
|
3861
|
+
...pages ? { pages } : {},
|
|
3862
|
+
...path !== void 0 ? { path } : {},
|
|
3863
|
+
footer: hasFooterChrome ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3864
|
+
footer,
|
|
3865
|
+
/* @__PURE__ */ jsx(LabFooterRegion, { contributions: labChromeAll })
|
|
3866
|
+
] }) : footer,
|
|
3911
3867
|
header: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3912
|
-
/* @__PURE__ */ jsx(LabHeader, {}),
|
|
3913
|
-
children
|
|
3868
|
+
/* @__PURE__ */ jsx(LabHeader, { ...addTrial2 !== void 0 ? { addTrial: addTrial2 } : {} }),
|
|
3869
|
+
children,
|
|
3870
|
+
/* @__PURE__ */ jsx(LabHeaderRegion, { contributions: labChromeAll })
|
|
3914
3871
|
] }),
|
|
3915
|
-
children: /* @__PURE__ */ jsx(PanelHostContext.Provider, { value: panelHostsRef.current, children: /* @__PURE__ */ jsx(SurfaceContext.Provider, { value: surface, children: /* @__PURE__ */ jsx(SurfaceCanvasContext.Provider, { value:
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
"
|
|
3927
|
-
{
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
ownCanvasRef.current = el;
|
|
3931
|
-
setOwnCanvas(el);
|
|
3932
|
-
}
|
|
3872
|
+
children: /* @__PURE__ */ jsx(PanelHostContext.Provider, { value: panelHostsRef.current, children: /* @__PURE__ */ jsx(SurfaceContext.Provider, { value: surface, children: /* @__PURE__ */ jsx(SurfaceCanvasContext.Provider, { value: surfaceCanvases, children: /* @__PURE__ */ jsxs("div", { className: "lk-lab__body", ref: labBodyRef, children: [
|
|
3873
|
+
outerSurface ? null : (
|
|
3874
|
+
// Two buffers stacked around the trials, both inert —
|
|
3875
|
+
// each tile takes input from its own box. A tile's marks
|
|
3876
|
+
// annotate the instrument's DOM from over it; an opaque
|
|
3877
|
+
// renderer sits under it, so the pane can still hold a
|
|
3878
|
+
// label. The under one is first so it paints first.
|
|
3879
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3880
|
+
/* @__PURE__ */ jsx(
|
|
3881
|
+
"canvas",
|
|
3882
|
+
{
|
|
3883
|
+
className: "lk-lab__surface lk-lab__surface--under",
|
|
3884
|
+
ref: (el) => {
|
|
3885
|
+
ownUnderRef.current = el;
|
|
3886
|
+
setOwnUnder(el);
|
|
3933
3887
|
}
|
|
3934
|
-
|
|
3888
|
+
}
|
|
3935
3889
|
),
|
|
3936
|
-
tools ? /* @__PURE__ */ jsx(LabPalette, { tools }) : null,
|
|
3937
3890
|
/* @__PURE__ */ jsx(
|
|
3938
|
-
|
|
3891
|
+
"canvas",
|
|
3939
3892
|
{
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
layout,
|
|
3946
|
-
onLayoutChange: (next) => store.getState().setLayout(next),
|
|
3947
|
-
children: trials.map((w3) => /* @__PURE__ */ jsx(Trial, { id: w3.id, chrome, suppress }, w3.id))
|
|
3893
|
+
className: "lk-lab__surface lk-lab__surface--over",
|
|
3894
|
+
ref: (el) => {
|
|
3895
|
+
ownOverRef.current = el;
|
|
3896
|
+
setOwnOver(el);
|
|
3897
|
+
}
|
|
3948
3898
|
}
|
|
3949
3899
|
)
|
|
3950
|
-
]
|
|
3951
|
-
|
|
3952
|
-
|
|
3900
|
+
] })
|
|
3901
|
+
),
|
|
3902
|
+
hasSidebarChrome ? /* @__PURE__ */ jsxs(LabPanes, { contributions: labChromeAll, children: [
|
|
3903
|
+
/* @__PURE__ */ jsx(LabPalette, { contributions: labChromeAll }),
|
|
3904
|
+
workspace
|
|
3905
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3906
|
+
/* @__PURE__ */ jsx(LabPalette, { contributions: labChromeAll }),
|
|
3907
|
+
workspace
|
|
3908
|
+
] })
|
|
3909
|
+
] }) }) }) })
|
|
3953
3910
|
}
|
|
3954
3911
|
)
|
|
3955
3912
|
}
|
|
3956
|
-
) }) });
|
|
3913
|
+
) }) }) }) });
|
|
3957
3914
|
}
|
|
3958
3915
|
|
|
3959
|
-
export {
|
|
3916
|
+
export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationOverlay, AnnotationTargets, Lab, LabPalette, LabShell, LabSwitcher, Trial, TrialBody, TrialChrome, Workspace, addTrial, annotationToolInfo, annotationsFromJSON, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createAnnotationScene, createAnnotationStore, createMarkDrawOne, currentPage, defineInstrument, fitView, fracContains, fracEncloses, fracToWorld, fromWeaselView, interstellarTheme, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveMarkStyle, roundFrac, seenFrom, toWeaselView, validateConfigSchema, worldToFrac };
|
|
3960
3917
|
//# sourceMappingURL=index.js.map
|
|
3961
3918
|
//# sourceMappingURL=index.js.map
|