@weasel-js/labkit 1.4.4 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -6
- package/dist/_dts/{CanvasStackContext-kTEZvEgE.d.ts → CanvasStackContext-CJNMS6mz.d.ts} +1 -1
- package/dist/_dts/{PrefsForm.d-DDHFANkc.d.ts → PrefsForm.d-DPy9I8It.d.ts} +4 -0
- package/dist/_dts/Select.d-Gwr8fLpK.d.ts +453 -0
- package/dist/_dts/{frac-z7ker2Vx.d.ts → frac-B4okC2lX.d.ts} +291 -31
- package/dist/_dts/{index-Blv9uQzu.d.ts → index-BNdz-tNd.d.ts} +105 -17
- package/dist/_dts/index-CSJoj4LC.d.ts +248 -0
- package/dist/_dts/{types-B9_zrHmb.d.ts → types-DzoNY1pO.d.ts} +91 -4
- package/dist/_dts/usePanZoom-Pzf9f2jh.d.ts +160 -0
- package/dist/_dts/useTrialState-uLlmaccn.d.ts +212 -0
- package/dist/canvas/index.d.ts +18 -129
- package/dist/canvas/index.js +4 -2
- package/dist/chrome/index.d.ts +5 -93
- package/dist/chrome/index.js +10 -6
- package/dist/chunk-7PFH3CNM.js +150 -0
- package/dist/chunk-7PFH3CNM.js.map +1 -0
- package/dist/{chunk-I6JCVE24.js → chunk-CEIUBPPW.js} +143 -17
- package/dist/chunk-CEIUBPPW.js.map +1 -0
- package/dist/chunk-EH7UEQV7.js +590 -0
- package/dist/chunk-EH7UEQV7.js.map +1 -0
- package/dist/{chunk-MOM3GOVY.js → chunk-EHGVROVV.js} +125 -27
- package/dist/chunk-EHGVROVV.js.map +1 -0
- package/dist/{chunk-TJ7QY3OC.js → chunk-ERFMC6WU.js} +160 -78
- package/dist/chunk-ERFMC6WU.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-64ZCN3DA.js → chunk-IHUVQ3TX.js} +250 -160
- package/dist/chunk-IHUVQ3TX.js.map +1 -0
- package/dist/{chunk-FQMJUVHJ.js → chunk-IJJUK4FU.js} +53 -46
- package/dist/chunk-IJJUK4FU.js.map +1 -0
- package/dist/chunk-IXTQI7JX.js +737 -0
- package/dist/chunk-IXTQI7JX.js.map +1 -0
- package/dist/{chunk-BKFVHKJH.js → chunk-MDVTK5U3.js} +4 -4
- package/dist/{chunk-BKFVHKJH.js.map → chunk-MDVTK5U3.js.map} +1 -1
- package/dist/chunk-PHK4W562.js +72 -0
- package/dist/chunk-PHK4W562.js.map +1 -0
- package/dist/chunk-PJ7WEQBJ.js +18 -0
- package/dist/chunk-PJ7WEQBJ.js.map +1 -0
- package/dist/chunk-RWNDDKOH.js +682 -0
- package/dist/chunk-RWNDDKOH.js.map +1 -0
- package/dist/chunk-T3EC2DXD.js +12628 -0
- package/dist/chunk-T3EC2DXD.js.map +1 -0
- package/dist/{chunk-SMHP6XZ4.js → chunk-UN4U4P63.js} +10 -8
- package/dist/chunk-UN4U4P63.js.map +1 -0
- package/dist/chunk-WIG6XHJ7.js +35 -0
- package/dist/chunk-WIG6XHJ7.js.map +1 -0
- package/dist/chunk-Z76KEZAX.js +15 -0
- package/dist/chunk-Z76KEZAX.js.map +1 -0
- package/dist/chunk-ZDHY47EV.js +363 -0
- package/dist/chunk-ZDHY47EV.js.map +1 -0
- package/dist/config/index.d.ts +276 -0
- package/dist/config/index.js +8 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controls/index.d.ts +3 -4
- package/dist/controls/index.js +4 -3
- package/dist/dragdrop/index.d.ts +3 -5
- package/dist/index.d.ts +266 -413
- package/dist/index.js +1775 -2251
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +4 -6
- package/dist/layers/index.js +2 -2
- package/dist/loupe/index.d.ts +69 -20
- package/dist/loupe/index.js +2 -2
- package/dist/passthrough/weasel-ui.d.ts +175 -309
- package/dist/passthrough/weasel-ui.js +1 -1
- package/dist/primitives/index.d.ts +55 -6
- package/dist/primitives/index.js +6 -4
- package/dist/state/index.d.ts +13 -8
- package/dist/state/index.js +18 -13
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +223 -76
- 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 +3 -5
- package/package.json +15 -8
- package/src/annotations/AnnotationOverlay.tsx +56 -5
- package/src/annotations/Annotations.less +4 -0
- package/src/annotations/Annotations.overlay.test.tsx +105 -6
- package/src/annotations/drawOne.test.ts +1 -1
- package/src/annotations/drawOne.ts +2 -2
- 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 +5 -4
- package/src/annotations/svgNodes.test.ts +2 -2
- package/src/annotations/svgNodes.ts +1 -0
- package/src/annotations/toolMap.test.ts +9 -5
- package/src/annotations/toolMap.ts +6 -3
- package/src/annotations/types.ts +6 -3
- package/src/canvas/CameraWheelContext.ts +12 -0
- package/src/canvas/CanvasStack.stories.tsx +1 -1
- 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.test.ts +28 -0
- package/src/canvas/usePanZoom.ts +21 -9
- package/src/chrome/ChromeRegions.stories.tsx +1 -4
- package/src/chrome/LabChrome.tsx +130 -0
- package/src/chrome/builtins.test.ts +1 -0
- package/src/chrome/builtins.tsx +21 -31
- package/src/chrome/index.ts +16 -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 +16 -9
- package/src/chrome/regions/SidebarRegion.stories.tsx +44 -0
- package/src/chrome/regions/SidebarRegion.tsx +25 -13
- package/src/chrome/regions/StatusRegion.tsx +15 -8
- package/src/chrome/regions/ToolbarRegion.tsx +34 -16
- package/src/chrome/types.ts +50 -20
- package/src/config/auto.test.ts +12 -0
- package/src/config/auto.ts +15 -0
- package/src/config/autoConfig.test.ts +107 -0
- package/src/config/autoConfig.ts +153 -0
- package/src/config/builder.test.ts +42 -0
- package/src/config/builder.ts +68 -17
- 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 +16 -1
- package/src/config/nodeClasses.test.ts +18 -0
- package/src/config/path.ts +7 -2
- package/src/config/resolve.test.ts +25 -0
- package/src/config/resolve.ts +17 -1
- package/src/config/types.ts +34 -2
- package/src/config/useResolvedConfig.ts +26 -0
- package/src/controls/AutoControls.stories.tsx +88 -0
- package/src/controls/ControlPanel.stories.tsx +42 -1
- package/src/controls/ControlPanel.test.tsx +530 -4
- package/src/controls/ControlPanel.tsx +562 -82
- package/src/fake-indexeddb-auto.d.ts +5 -0
- package/src/index.test.ts +7 -0
- package/src/index.ts +66 -5
- package/src/instrument/serializers.test.ts +22 -0
- package/src/instrument/serializers.ts +40 -1
- package/src/instrument/types.ts +25 -1
- package/src/lab/Lab.chrome.test.tsx +16 -0
- package/src/lab/Lab.less +4 -13
- package/src/lab/Lab.persist.test.tsx +147 -0
- package/src/lab/Lab.stories.tsx +1 -3
- package/src/lab/Lab.surface.test.tsx +71 -3
- package/src/lab/Lab.test.tsx +168 -16
- package/src/lab/Lab.tsx +434 -129
- package/src/lab/LabContext.ts +8 -0
- package/src/lab/LabFit.stories.less +18 -0
- package/src/lab/LabFit.stories.tsx +219 -0
- package/src/lab/LabFullChrome.stories.tsx +1 -4
- package/src/lab/LabHeader.test.tsx +40 -9
- package/src/lab/LabHeader.tsx +7 -14
- package/src/lab/LabPalette.tsx +13 -26
- package/src/lab/LabRoot.test.tsx +56 -0
- package/src/lab/LabRoot.tsx +47 -0
- package/src/lab/LabShell.less +73 -15
- package/src/lab/LabShell.stories.tsx +50 -1
- package/src/lab/LabShell.tsx +18 -27
- package/src/lab/LabSwitcher.less +18 -11
- package/src/lab/LabSwitcher.test.tsx +14 -9
- package/src/lab/LabSwitcher.tsx +8 -4
- package/src/lab/Workspace.less +1 -1
- package/src/lab/Workspace.stories.tsx +1 -1
- package/src/lab/Workspace.surface.test.tsx +1 -0
- package/src/lab/fitCheck.test.ts +57 -0
- package/src/lab/fitCheck.ts +125 -0
- package/src/lab/index.ts +2 -0
- package/src/layers/LayerList.stories.tsx +1 -1
- package/src/loupe/AGENTS.md +17 -5
- package/src/loupe/Loupe.less +1 -1
- package/src/loupe/LoupeGestures.tsx +67 -0
- package/src/loupe/TrialLoupe.tsx +43 -28
- package/src/loupe/canvasLens.ts +1 -1
- package/src/loupe/index.ts +4 -0
- package/src/loupe/loupeActions.ts +98 -0
- package/src/loupe/types.ts +1 -1
- package/src/loupe/useLoupe.test.tsx +60 -3
- package/src/loupe/useLoupe.ts +29 -49
- package/src/passthrough/weasel-ui.ts +9 -0
- package/src/primitives/FloatingPanel.stories.tsx +14 -2
- package/src/primitives/FloatingPanel.test.tsx +47 -16
- package/src/primitives/FloatingPanel.tsx +11 -25
- package/src/primitives/FpsMeter.stories.tsx +1 -1
- package/src/primitives/JobProgress.stories.tsx +1 -1
- package/src/primitives/Legend.stories.tsx +1 -1
- package/src/primitives/ScaleIndicator.stories.tsx +1 -1
- package/src/primitives/Sidebar.less +7 -0
- package/src/primitives/Sidebar.stories.tsx +1 -1
- package/src/primitives/Split.less +14 -0
- package/src/primitives/Split.test.tsx +133 -0
- package/src/primitives/Split.tsx +175 -0
- package/src/primitives/StatusBar.stories.tsx +1 -1
- package/src/primitives/Toolbar.stories.tsx +1 -1
- package/src/primitives/ZoomControl.tsx +5 -2
- package/src/primitives/index.ts +2 -0
- package/src/specimen/Specimen.less +95 -0
- package/src/specimen/Specimen.stories.tsx +13 -0
- package/src/specimen/Specimen.test.tsx +13 -0
- package/src/specimen/Specimen.tsx +888 -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 +1 -1
- 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 +169 -635
- package/src/state/store.ts +146 -163
- package/src/state/toolSlot.test.ts +1 -1
- package/src/state/types.ts +65 -17
- 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 +87 -3
- package/src/state/useTrialState.ts +9 -1
- 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 +1 -0
- package/src/surface/useSurfaceTile.ts +6 -5
- package/src/surface/useTiledSurface.test.tsx +231 -1
- package/src/surface/useTiledSurface.ts +76 -15
- package/src/theme/Interstellar.stories.tsx +9 -9
- package/src/theme/interstellar.test.ts +17 -6
- package/src/theme/interstellar.theme.json +180 -0
- package/src/theme/interstellar.ts +8 -5
- package/src/tools/labTool.ts +11 -0
- package/src/trial/Trial.annotations.persist.test.tsx +100 -58
- package/src/trial/Trial.annotations.test.tsx +59 -7
- package/src/trial/Trial.canvas.test.tsx +32 -0
- package/src/trial/Trial.config.test.tsx +75 -2
- package/src/trial/Trial.job.test.tsx +3 -3
- package/src/trial/Trial.less +5 -26
- package/src/trial/Trial.stories.tsx +5 -3
- package/src/trial/Trial.test.tsx +31 -4
- package/src/trial/Trial.trialId.test.tsx +23 -1
- package/src/trial/Trial.tsx +139 -52
- package/src/trial/TrialBody.tsx +17 -141
- package/src/trial/TrialChrome.tsx +24 -7
- package/src/trial/trialOps.test.ts +101 -1
- package/src/trial/trialOps.ts +67 -7
- 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-2P6PP5N4.js +0 -553
- package/dist/chunk-2P6PP5N4.js.map +0 -1
- package/dist/chunk-64ZCN3DA.js.map +0 -1
- package/dist/chunk-CTRKTLYZ.js.map +0 -1
- package/dist/chunk-E44SU6XS.js +0 -50
- package/dist/chunk-E44SU6XS.js.map +0 -1
- package/dist/chunk-FQMJUVHJ.js.map +0 -1
- package/dist/chunk-I6JCVE24.js.map +0 -1
- package/dist/chunk-MOM3GOVY.js.map +0 -1
- package/dist/chunk-SMHP6XZ4.js.map +0 -1
- package/dist/chunk-TJ7QY3OC.js.map +0 -1
- package/dist/chunk-U3IYHIAE.js +0 -343
- package/dist/chunk-U3IYHIAE.js.map +0 -1
- package/dist/chunk-W3ECWC2K.js +0 -7335
- package/dist/chunk-W3ECWC2K.js.map +0 -1
- package/src/theme/interstellar.tokens.json +0 -61
package/dist/index.js
CHANGED
|
@@ -1,670 +1,83 @@
|
|
|
1
|
+
import { resolveLoupe, TrialLoupe } from './chunk-ERFMC6WU.js';
|
|
2
|
+
export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LOUPE_MAGNIFY_ID, LOUPE_PEEK_ID, LoupeBubble, LoupeGestures, TrialLoupe, createLoupeActions, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-ERFMC6WU.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-IJJUK4FU.js';
|
|
7
|
+
export { composeRects, rectsEqual, toDeviceRect, useTiledSurface } from './chunk-IJJUK4FU.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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export { ControlPanel
|
|
19
|
-
import {
|
|
20
|
-
export {
|
|
21
|
-
import {
|
|
22
|
-
export {
|
|
23
|
-
import {
|
|
24
|
-
export {
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
export {
|
|
28
|
-
import {
|
|
29
|
-
export {
|
|
30
|
-
export { centerOn, zoomAt } from './chunk-
|
|
10
|
+
import { suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, ViewportRegion, SidebarRegion, StatusRegion, resolveLabTool, AnnotationsContext, useLabChromeContext, labContributions, LabHeaderRegion, LabFooterRegion, LabAsideRegion, LabSidebarRegion } from './chunk-IHUVQ3TX.js';
|
|
11
|
+
export { AnnotationsContext, ExportMenu, LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, suppressContributions, toolContributions, useAnnotations, useAnnotationsOptional, useLabChromeContext } from './chunk-IHUVQ3TX.js';
|
|
12
|
+
import { Split, JobProgress, Sidebar, Legend, FloatingPanel } from './chunk-EHGVROVV.js';
|
|
13
|
+
export { FloatingPanel, JobProgress, Legend, Sidebar, Split } from './chunk-EHGVROVV.js';
|
|
14
|
+
import { Toolbar, StatusBar, ScaleIndicator, ZoomControl } from './chunk-UN4U4P63.js';
|
|
15
|
+
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-UN4U4P63.js';
|
|
16
|
+
import { unruledConfigSchema, createLabStore, openLabStore, panelKey } from './chunk-IXTQI7JX.js';
|
|
17
|
+
export { CURRENT_DOCUMENT_VERSION, dockPanel, labDocumentKey, openLabStore, panelKey, quarantineKey, undockPanel, useTrialState } from './chunk-IXTQI7JX.js';
|
|
18
|
+
import { newId, useWarnIgnoredChange, useOpenOnce, PersistenceContext, createRecordCache, noneAdapter, defaultStorage, usePersistedState } from './chunk-RWNDDKOH.js';
|
|
19
|
+
export { Persistence, createIndexedDbAdapter, createMemoryAdapter, indexedDbAdapter, localStorageAdapter, noneAdapter, sessionStorageAdapter, urlHashAdapter, usePersistedState } from './chunk-RWNDDKOH.js';
|
|
20
|
+
export { ControlPanel } from './chunk-EH7UEQV7.js';
|
|
21
|
+
import { useConfigSchema } from './chunk-PJ7WEQBJ.js';
|
|
22
|
+
export { useConfigSchema } from './chunk-PJ7WEQBJ.js';
|
|
23
|
+
import { useLabContext, LabContext } from './chunk-Z76KEZAX.js';
|
|
24
|
+
export { LabContext, useLabContext } from './chunk-Z76KEZAX.js';
|
|
25
|
+
import { useResolvedConfig } from './chunk-ZDHY47EV.js';
|
|
26
|
+
export { BaseNode, BooleanNode, ColorNode, CustomNode, EnumNode, GroupNode, NumberNode, StringNode, ValueNode, applyRules, builtinRules, f, isConfigBranch, resolveConfigSchema, titleCase, useResolvedConfig } from './chunk-ZDHY47EV.js';
|
|
27
|
+
export { fromConfigFields, isLeafVisible } from './chunk-PHK4W562.js';
|
|
28
|
+
import { hasConfigPath, schemaNodeAtPath, applyConfigWrite, resolveAutoConfig, isAuto, isRecord, autoPathsOf, fillConfigDefaults } from './chunk-7PFH3CNM.js';
|
|
29
|
+
export { auto, autoPathsOf, fillConfigDefaults, hasConfigPath, isAuto, resolveAutoConfig, schemaNodeAtPath, valueAtPath, withValueAtPath } from './chunk-7PFH3CNM.js';
|
|
30
|
+
import { CameraWheelContext, as2DView, DEFAULT_VIEW, CanvasStack, Stage, withZoom, fitStage } from './chunk-CEIUBPPW.js';
|
|
31
|
+
export { CanvasStack, DEFAULT_VIEW, PITCH_LIMIT, Stage, as2DView, clampPitch, fitStage, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-CEIUBPPW.js';
|
|
32
|
+
export { centerOn, zoomAt } from './chunk-H6ZAOWNE.js';
|
|
31
33
|
import { applyCamera } from './chunk-2AYGEN57.js';
|
|
32
34
|
export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from './chunk-2AYGEN57.js';
|
|
35
|
+
import { useSurfaceOptional, useSurfaceCanvas, useTileId, SurfaceContext, SurfaceCanvasContext } from './chunk-WIG6XHJ7.js';
|
|
36
|
+
export { SurfaceCanvasContext, SurfaceContext, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTileId } from './chunk-WIG6XHJ7.js';
|
|
33
37
|
export { CanvasStackContext } from './chunk-CQLQPQ4P.js';
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
+
import { LabStoreContext, TrialIdProvider } from './chunk-2YILQ7Y5.js';
|
|
39
|
+
export { LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, useLabStore, useTrialId } from './chunk-2YILQ7Y5.js';
|
|
40
|
+
import { LayerList } from './chunk-MDVTK5U3.js';
|
|
41
|
+
export { LayerList } from './chunk-MDVTK5U3.js';
|
|
42
|
+
import './chunk-SBXNQW4G.js';
|
|
43
|
+
import { E, w, T, a2, p, u, c, n, f, r, h, serializeSvg, C, l7, r3, a5, Lt, o2, a4, l3, u3, r7, r8, r6, o5, s5, c6, l6, c3, s3, u2, g2, g, y2, l4, b2, c7, r2, l2, C2, S, O, k, F2, L3, V, H, U, B, R2, J, A2, c4, n5, s4, m2, c2, l, g3, l5, S4, r10, n6, r11, i5, i4, s2, n7, o4, r9, c5, w2, L2, n8, k2, $2 } from './chunk-T3EC2DXD.js';
|
|
44
|
+
export { a5 as Button, R2 as CheckboxRow, y as CloseIcon, $ as CollapseIcon, L3 as ColorRow, S2 as CurveField, G2 as EffectCard, q as EffectCardList, te as ExpandIcon, o as Icon, u2 as Input, l9 as Jog, c4 as LayerStack, V as NumberRow, I as PauseIcon, F as PlayIcon, J as PropertyGroup, k as PropertyList, O as PropertyPanel, j2 as PropertyRow, A3 as PropertySpan, v as ResetIcon, g as Select, H as SelectRow, F2 as SliderRow, R as StepBackIcon, z as StepForwardIcon, L as StopIcon, W as Subpanel, B as TextRow, r3 as ToggleBar, U as ToggleRow, q2 as TokenPanel, I2 as inferTokenType } from './chunk-T3EC2DXD.js';
|
|
45
|
+
import { textCommand, linePath, markerDrawCommands, ellipsePath, rectPath, createScene, asNodeId as asNodeId$1, WeaselProvider, SceneCanvas, sceneFromJSON, PathBuilder, renderSceneToPixels, useActiveToolContext } from '@weasel-js/core';
|
|
46
|
+
import { createContext, useState, useRef, useEffect, useCallback, useContext, useMemo, useId, Children, useLayoutEffect, useSyncExternalStore } from 'react';
|
|
47
|
+
import { flushSync, createPortal } from 'react-dom';
|
|
38
48
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
49
|
+
import { useThemeOptional, ThemeProvider } from '@weasel-js/theme/react';
|
|
39
50
|
import { useStore } from 'zustand/react';
|
|
40
|
-
import {
|
|
41
|
-
import { Provider, StrategyRegistryProvider,
|
|
51
|
+
import { defineTheme, weaselTheme } from '@weasel-js/theme';
|
|
52
|
+
import { Provider, StrategyRegistryProvider, DragProvider, Container, useDragHandle } from 'windease/react';
|
|
53
|
+
import { asNodeId, Store, createNode, floatingStrategy, gridStrategy } from 'windease';
|
|
54
|
+
import { Focusable } from 'react-aria-components';
|
|
42
55
|
|
|
43
|
-
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
|
44
|
-
function multiply(a, b) {
|
|
45
|
-
return [
|
|
46
|
-
a[0] * b[0] + a[2] * b[1],
|
|
47
|
-
a[1] * b[0] + a[3] * b[1],
|
|
48
|
-
a[0] * b[2] + a[2] * b[3],
|
|
49
|
-
a[1] * b[2] + a[3] * b[3],
|
|
50
|
-
a[0] * b[4] + a[2] * b[5] + a[4],
|
|
51
|
-
a[1] * b[4] + a[3] * b[5] + a[5]
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
|
-
function isIdentity(m, eps = 1e-9) {
|
|
55
|
-
return Math.abs(m[0] - 1) < eps && Math.abs(m[1]) < eps && Math.abs(m[2]) < eps && Math.abs(m[3] - 1) < eps && Math.abs(m[4]) < eps && Math.abs(m[5]) < eps;
|
|
56
|
-
}
|
|
57
|
-
function formatMatrix(m) {
|
|
58
|
-
if (isIdentity(m)) return null;
|
|
59
|
-
return `matrix(${m.map((n) => trimNumber(n)).join(" ")})`;
|
|
60
|
-
}
|
|
61
|
-
function trimNumber(n) {
|
|
62
|
-
if (!Number.isFinite(n)) return "0";
|
|
63
|
-
const r2 = Math.round(n * 1e6) / 1e6;
|
|
64
|
-
if (Number.isInteger(r2)) return String(r2);
|
|
65
|
-
return String(r2);
|
|
66
|
-
}
|
|
67
|
-
function patternSpecOf(paint) {
|
|
68
|
-
return "tile" in paint.pattern ? paint.pattern : null;
|
|
69
|
-
}
|
|
70
|
-
function patternXml(id, paint, onWarn) {
|
|
71
|
-
const spec = patternSpecOf(paint);
|
|
72
|
-
if (!spec) {
|
|
73
|
-
onWarn?.("pattern fill carries a TextureHandle, which has no vector form \u2014 omitted from <defs>");
|
|
74
|
-
return "";
|
|
75
|
-
}
|
|
76
|
-
const geometry = tileGeometry(spec);
|
|
77
|
-
const body = geometry.shapes.map(shapeXml).join("");
|
|
78
|
-
const origin = paint.origin ?? { x: 0, y: 0 };
|
|
79
|
-
const attrs = [
|
|
80
|
-
`id="${id}"`,
|
|
81
|
-
'patternUnits="userSpaceOnUse"',
|
|
82
|
-
`width="${trimNumber(geometry.size)}"`,
|
|
83
|
-
`height="${trimNumber(geometry.size)}"`
|
|
84
|
-
];
|
|
85
|
-
if (origin.x !== 0 || origin.y !== 0) {
|
|
86
|
-
attrs.push(`x="${trimNumber(origin.x)}"`, `y="${trimNumber(origin.y)}"`);
|
|
87
|
-
}
|
|
88
|
-
attrs.push(`data-weasel-tile="${escapeAttr(JSON.stringify(spec))}"`);
|
|
89
|
-
return `<pattern ${attrs.join(" ")}>${body}</pattern>`;
|
|
90
|
-
}
|
|
91
|
-
function shapeXml(shape) {
|
|
92
|
-
switch (shape.kind) {
|
|
93
|
-
case "line":
|
|
94
|
-
return `<line x1="${trimNumber(shape.x1)}" y1="${trimNumber(shape.y1)}" x2="${trimNumber(shape.x2)}" y2="${trimNumber(shape.y2)}" stroke="${shape.color}" stroke-width="${trimNumber(shape.width)}"/>`;
|
|
95
|
-
case "circle":
|
|
96
|
-
return `<circle cx="${trimNumber(shape.cx)}" cy="${trimNumber(shape.cy)}" r="${trimNumber(shape.r)}" fill="${shape.color}"/>`;
|
|
97
|
-
case "ellipse": {
|
|
98
|
-
const deg = shape.rotation * 180 / Math.PI;
|
|
99
|
-
const transform = deg === 0 ? "" : ` transform="rotate(${trimNumber(deg)} ${trimNumber(shape.cx)} ${trimNumber(shape.cy)})"`;
|
|
100
|
-
return `<ellipse cx="${trimNumber(shape.cx)}" cy="${trimNumber(shape.cy)}" rx="${trimNumber(shape.rx)}" ry="${trimNumber(shape.ry)}" fill="${shape.color}"${transform}/>`;
|
|
101
|
-
}
|
|
102
|
-
case "rect":
|
|
103
|
-
return `<rect x="${trimNumber(shape.x)}" y="${trimNumber(shape.y)}" width="${trimNumber(shape.width)}" height="${trimNumber(shape.height)}" fill="${shape.color}"/>`;
|
|
104
|
-
default:
|
|
105
|
-
return "";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function escapeAttr(s) {
|
|
109
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
110
|
-
}
|
|
111
|
-
function serializePathD(path) {
|
|
112
|
-
if (path.kind === "rect") {
|
|
113
|
-
const { x, y: y2, width, height } = path;
|
|
114
|
-
return `M${trimNumber(x)} ${trimNumber(y2)}h${trimNumber(width)}v${trimNumber(height)}h${trimNumber(-width)}Z`;
|
|
115
|
-
}
|
|
116
|
-
const cmds = path.commands;
|
|
117
|
-
const xs = path.coords;
|
|
118
|
-
const out = [];
|
|
119
|
-
let ci = 0;
|
|
120
|
-
for (let i = 0; i < cmds.length; i++) {
|
|
121
|
-
const c2 = cmds[i];
|
|
122
|
-
if (c2 === PATH_M) {
|
|
123
|
-
out.push(`M${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])}`);
|
|
124
|
-
ci += 2;
|
|
125
|
-
} else if (c2 === PATH_L) {
|
|
126
|
-
out.push(`L${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])}`);
|
|
127
|
-
ci += 2;
|
|
128
|
-
} else if (c2 === PATH_C) {
|
|
129
|
-
out.push(
|
|
130
|
-
`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
|
-
);
|
|
132
|
-
ci += 6;
|
|
133
|
-
} else if (c2 === PATH_Q) {
|
|
134
|
-
out.push(
|
|
135
|
-
`Q${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])} ${trimNumber(xs[ci + 2])} ${trimNumber(xs[ci + 3])}`
|
|
136
|
-
);
|
|
137
|
-
ci += 4;
|
|
138
|
-
} else if (c2 === PATH_Z) {
|
|
139
|
-
out.push("Z");
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return out.join("");
|
|
143
|
-
}
|
|
144
|
-
var PaintServerRegistry = class {
|
|
145
|
-
byPaint = /* @__PURE__ */ new Map();
|
|
146
|
-
order = [];
|
|
147
|
-
counter = 0;
|
|
148
|
-
// Marker keys referenced by any stroke, in first-use order. The `<defs>`
|
|
149
|
-
// id is the marker key itself, not a minted counter id: `parseSvg` reads a
|
|
150
|
-
// `marker-end="url(#id)"` fragment back as the marker key directly (it
|
|
151
|
-
// never inspects the `<marker>` element), so a synthetic id would come
|
|
152
|
-
// back unresolvable and warn on round-trip.
|
|
153
|
-
markerKeys = [];
|
|
154
|
-
markerKeySet = /* @__PURE__ */ new Set();
|
|
155
|
-
register(paint) {
|
|
156
|
-
const existing = this.byPaint.get(paint);
|
|
157
|
-
if (existing) return existing;
|
|
158
|
-
const id = paint.fill === "pattern" ? `pat${this.counter++}` : `grad${this.counter++}`;
|
|
159
|
-
this.byPaint.set(paint, id);
|
|
160
|
-
this.order.push(paint);
|
|
161
|
-
return id;
|
|
162
|
-
}
|
|
163
|
-
/** The `<defs>` id for a marker key — the key itself, minting nothing.
|
|
164
|
-
* `undefined` when nothing is registered under `key`, so a caller emits no
|
|
165
|
-
* attribute at all rather than a `url(#…)` pointing at a def that will
|
|
166
|
-
* never be written. */
|
|
167
|
-
markerId(key) {
|
|
168
|
-
if (getMarker(key) === void 0) return void 0;
|
|
169
|
-
if (!this.markerKeySet.has(key)) {
|
|
170
|
-
this.markerKeySet.add(key);
|
|
171
|
-
this.markerKeys.push(key);
|
|
172
|
-
}
|
|
173
|
-
return key;
|
|
174
|
-
}
|
|
175
|
-
/** Emit `<defs>...</defs>` XML for every registered paint server and marker. */
|
|
176
|
-
toDefsXml(onWarn) {
|
|
177
|
-
if (this.order.length === 0 && this.markerKeys.length === 0) return "";
|
|
178
|
-
const parts = ["<defs>"];
|
|
179
|
-
for (const paint of this.order) {
|
|
180
|
-
parts.push(paintServerXml(this.byPaint.get(paint), paint, onWarn));
|
|
181
|
-
}
|
|
182
|
-
for (const key of this.markerKeys) {
|
|
183
|
-
const entry = getMarker(key);
|
|
184
|
-
if (!entry) continue;
|
|
185
|
-
parts.push(entry.toSvg ? entry.toSvg(key, entry) : defaultMarkerXml(key, entry));
|
|
186
|
-
}
|
|
187
|
-
parts.push("</defs>");
|
|
188
|
-
return parts.join("");
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
function defaultMarkerXml(id, entry) {
|
|
192
|
-
const path = entry.path({ size: 1, stroke: { paint: { fill: "solid", color: "#000" } } });
|
|
193
|
-
const d = serializePathD(path);
|
|
194
|
-
const fill = entry.fill === "none" ? "none" : "context-stroke";
|
|
195
|
-
const outline = entry.outline ? ` stroke="context-stroke" stroke-width="${entry.outline.width}" stroke-linecap="round" stroke-linejoin="round"` : "";
|
|
196
|
-
return `<marker id="${id}" markerUnits="strokeWidth" markerWidth="8" markerHeight="8" refX="0" refY="0" orient="auto" overflow="visible"><path d="${d}" fill="${fill}"${outline}/></marker>`;
|
|
197
|
-
}
|
|
198
|
-
function paintServerXml(id, paint, onWarn) {
|
|
199
|
-
if (paint.fill === "pattern") return patternXml(id, paint, onWarn);
|
|
200
|
-
const builtin = gradientXml(id, paint);
|
|
201
|
-
if (builtin) return builtin;
|
|
202
|
-
const custom = getPaintKind(paint.fill)?.toSvg?.(id, paint);
|
|
203
|
-
if (custom) return custom;
|
|
204
|
-
onWarn?.(`${paint.fill} fill has no vector form \u2014 omitted from <defs>`);
|
|
205
|
-
return "";
|
|
206
|
-
}
|
|
207
|
-
function gradientUnitsAttr(units) {
|
|
208
|
-
return units === "bounds" ? "objectBoundingBox" : "userSpaceOnUse";
|
|
209
|
-
}
|
|
210
|
-
function gradientXml(id, paint) {
|
|
211
|
-
if (paint.fill === "linear-gradient") {
|
|
212
|
-
const stops = paint.stops.map(stopXml).join("");
|
|
213
|
-
return `<linearGradient id="${id}" gradientUnits="${gradientUnitsAttr(paint.units)}" x1="${trimNumber(paint.from.x)}" y1="${trimNumber(paint.from.y)}" x2="${trimNumber(paint.to.x)}" y2="${trimNumber(paint.to.y)}">${stops}</linearGradient>`;
|
|
214
|
-
}
|
|
215
|
-
if (paint.fill === "radial-gradient") {
|
|
216
|
-
const stops = paint.stops.map(stopXml).join("");
|
|
217
|
-
return `<radialGradient id="${id}" gradientUnits="${gradientUnitsAttr(paint.units)}" cx="${trimNumber(paint.center.x)}" cy="${trimNumber(paint.center.y)}" r="${trimNumber(paint.radius)}">${stops}</radialGradient>`;
|
|
218
|
-
}
|
|
219
|
-
return "";
|
|
220
|
-
}
|
|
221
|
-
function stopXml(s) {
|
|
222
|
-
const m = /^#([0-9a-f]{6})([0-9a-f]{2})$/i.exec(s.color);
|
|
223
|
-
if (m) {
|
|
224
|
-
const alpha = parseInt(m[2], 16) / 255;
|
|
225
|
-
return `<stop offset="${trimNumber(s.offset)}" stop-color="#${m[1].toLowerCase()}" stop-opacity="${trimNumber(alpha)}"/>`;
|
|
226
|
-
}
|
|
227
|
-
return `<stop offset="${trimNumber(s.offset)}" stop-color="${s.color}"/>`;
|
|
228
|
-
}
|
|
229
|
-
function serializeSvg(nodes, opts = {}) {
|
|
230
|
-
const registry = new PaintServerRegistry();
|
|
231
|
-
registerPaintServers(nodes, registry);
|
|
232
|
-
const bounds = opts.viewBox ?? computeBounds(nodes);
|
|
233
|
-
const vb = `${trimNumber(bounds.x)} ${trimNumber(bounds.y)} ${trimNumber(bounds.width)} ${trimNumber(bounds.height)}`;
|
|
234
|
-
const namespaces = opts.namespaces ?? {};
|
|
235
|
-
const rootAttrs = [`xmlns="http://www.w3.org/2000/svg"`];
|
|
236
|
-
for (const [prefix, uri] of Object.entries(namespaces)) {
|
|
237
|
-
rootAttrs.push(`xmlns:${prefix}="${escapeAttr2(uri)}"`);
|
|
238
|
-
}
|
|
239
|
-
rootAttrs.push(`viewBox="${vb}"`);
|
|
240
|
-
if (opts.width != null) rootAttrs.push(`width="${trimNumber(opts.width)}"`);
|
|
241
|
-
if (opts.height != null) rootAttrs.push(`height="${trimNumber(opts.height)}"`);
|
|
242
|
-
if (opts.documentMeta) {
|
|
243
|
-
for (const prefix of Object.keys(namespaces)) {
|
|
244
|
-
const bucket = opts.documentMeta[prefix];
|
|
245
|
-
if (!bucket?.attrs) continue;
|
|
246
|
-
for (const [name, value] of Object.entries(bucket.attrs)) {
|
|
247
|
-
rootAttrs.push(`${prefix}:${name}="${escapeAttr2(value)}"`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
let docMetaXml = "";
|
|
252
|
-
if (opts.documentMeta) {
|
|
253
|
-
for (const prefix of Object.keys(namespaces)) {
|
|
254
|
-
const bucket = opts.documentMeta[prefix];
|
|
255
|
-
if (!bucket?.elements) continue;
|
|
256
|
-
for (const [localName, list] of Object.entries(bucket.elements)) {
|
|
257
|
-
for (const el of list) docMetaXml += namespacedElementXml(prefix, localName, el);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
const defsXml = registry.toDefsXml(opts.onWarn);
|
|
262
|
-
const bodyXml = nodes.map((n) => nodeXml(n, registry, namespaces)).join("");
|
|
263
|
-
const titleXml = opts.title && opts.title.length > 0 ? `<title>${escapeText(opts.title)}</title>` : "";
|
|
264
|
-
return `<svg ${rootAttrs.join(" ")}>${titleXml}${defsXml}${docMetaXml}${bodyXml}</svg>`;
|
|
265
|
-
}
|
|
266
|
-
function namespacedElementXml(prefix, localName, el) {
|
|
267
|
-
const attrs = [];
|
|
268
|
-
for (const [name, value] of Object.entries(el.attrs)) {
|
|
269
|
-
attrs.push(`${name}="${escapeAttr2(value)}"`);
|
|
270
|
-
}
|
|
271
|
-
const head = attrs.length > 0 ? `<${prefix}:${localName} ${attrs.join(" ")}>` : `<${prefix}:${localName}>`;
|
|
272
|
-
let body = "";
|
|
273
|
-
if (el.children) {
|
|
274
|
-
for (const [childName, list] of Object.entries(el.children)) {
|
|
275
|
-
for (const child of list) body += namespacedElementXml(prefix, childName, child);
|
|
276
|
-
}
|
|
277
|
-
} else if (el.text != null) {
|
|
278
|
-
body = escapeText(el.text);
|
|
279
|
-
}
|
|
280
|
-
return `${head}${body}</${prefix}:${localName}>`;
|
|
281
|
-
}
|
|
282
|
-
function metaAttrsXml(meta, namespaces) {
|
|
283
|
-
if (!meta) return "";
|
|
284
|
-
const parts = [];
|
|
285
|
-
for (const prefix of Object.keys(namespaces)) {
|
|
286
|
-
const bucket = meta[prefix];
|
|
287
|
-
if (!bucket?.attrs) continue;
|
|
288
|
-
for (const [name, value] of Object.entries(bucket.attrs)) {
|
|
289
|
-
parts.push(`${prefix}:${name}="${escapeAttr2(value)}"`);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
293
|
-
}
|
|
294
|
-
function metaElementsXml(meta, namespaces) {
|
|
295
|
-
if (!meta) return "";
|
|
296
|
-
let out = "";
|
|
297
|
-
for (const prefix of Object.keys(namespaces)) {
|
|
298
|
-
const bucket = meta[prefix];
|
|
299
|
-
if (!bucket?.elements) continue;
|
|
300
|
-
for (const [localName, list] of Object.entries(bucket.elements)) {
|
|
301
|
-
for (const el of list) out += namespacedElementXml(prefix, localName, el);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return out;
|
|
305
|
-
}
|
|
306
|
-
function registerPaintServers(nodes, registry) {
|
|
307
|
-
for (const n of nodes) {
|
|
308
|
-
if (n.kind === "group") {
|
|
309
|
-
registerPaintServers(n.children, registry);
|
|
310
|
-
} else if (n.kind === "path") {
|
|
311
|
-
if (n.fill.kind === "gradient") registry.register(n.fill.paint);
|
|
312
|
-
if (n.stroke && n.stroke.paint.kind === "gradient") registry.register(n.stroke.paint.paint);
|
|
313
|
-
registerMarkers(n.stroke, registry);
|
|
314
|
-
} else if (n.kind === "text") {
|
|
315
|
-
registerTextPaint(n.fill, registry);
|
|
316
|
-
registerTextPaint(n.stroke?.paint, registry);
|
|
317
|
-
registerMarkers(n.stroke, registry);
|
|
318
|
-
for (const run of n.runs ?? []) {
|
|
319
|
-
registerTextPaint(run.fill, registry);
|
|
320
|
-
registerTextPaint(run.stroke?.paint, registry);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
function registerTextPaint(paint, registry) {
|
|
326
|
-
if (paint && !("color" in paint)) registry.register(paint);
|
|
327
|
-
}
|
|
328
|
-
function markerKeyOfRef(ref) {
|
|
329
|
-
if (typeof ref === "string") return ref;
|
|
330
|
-
if (ref && typeof ref === "object" && "key" in ref) return String(ref.key);
|
|
331
|
-
return void 0;
|
|
332
|
-
}
|
|
333
|
-
function registerMarkers(stroke, registry) {
|
|
334
|
-
if (!stroke) return;
|
|
335
|
-
for (const ref of [stroke.markerStart, stroke.markerMid, stroke.markerEnd]) {
|
|
336
|
-
const key = markerKeyOfRef(ref);
|
|
337
|
-
if (key) registry.markerId(key);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
function nodeXml(node, registry, namespaces) {
|
|
341
|
-
if (node.kind === "group") return groupXml(node, registry, namespaces);
|
|
342
|
-
if (node.kind === "text") return textXml(node, registry, namespaces);
|
|
343
|
-
if (node.kind === "image") return imageXml(node, namespaces);
|
|
344
|
-
return pathXml(node, registry, namespaces);
|
|
345
|
-
}
|
|
346
|
-
function imageXml(node, namespaces) {
|
|
347
|
-
const attrs = [
|
|
348
|
-
`href="${escapeAttr2(node.href)}"`,
|
|
349
|
-
`x="${trimNumber(node.x)}"`,
|
|
350
|
-
`y="${trimNumber(node.y)}"`,
|
|
351
|
-
`width="${trimNumber(node.width)}"`,
|
|
352
|
-
`height="${trimNumber(node.height)}"`,
|
|
353
|
-
`preserveAspectRatio="none"`
|
|
354
|
-
];
|
|
355
|
-
if (node.opacity != null && node.opacity !== 1) {
|
|
356
|
-
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
357
|
-
}
|
|
358
|
-
if (node.rotation != null && node.rotation !== 0) {
|
|
359
|
-
const cx = node.x + node.width / 2;
|
|
360
|
-
const cy = node.y + node.height / 2;
|
|
361
|
-
const deg = node.rotation * 180 / Math.PI;
|
|
362
|
-
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
363
|
-
}
|
|
364
|
-
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
365
|
-
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
366
|
-
if (metaEls) return `<image ${attrs.join(" ")}${metaAttrs}>${metaEls}</image>`;
|
|
367
|
-
return `<image ${attrs.join(" ")}${metaAttrs}/>`;
|
|
368
|
-
}
|
|
369
|
-
function groupXml(node, registry, namespaces) {
|
|
370
|
-
const attrs = [];
|
|
371
|
-
if (node.transform) {
|
|
372
|
-
const m = formatMatrix(node.transform);
|
|
373
|
-
if (m) attrs.push(`transform="${m}"`);
|
|
374
|
-
}
|
|
375
|
-
if (node.opacity != null && node.opacity !== 1) {
|
|
376
|
-
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
377
|
-
}
|
|
378
|
-
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
379
|
-
const head = attrs.length > 0 ? `<g ${attrs.join(" ")}${metaAttrs}>` : `<g${metaAttrs}>`;
|
|
380
|
-
const body = node.children.map((c2) => nodeXml(c2, registry, namespaces)).join("");
|
|
381
|
-
return `${head}${body}${metaElementsXml(node.meta, namespaces)}</g>`;
|
|
382
|
-
}
|
|
383
|
-
function pathXml(node, registry, namespaces) {
|
|
384
|
-
const attrs = [`d="${serializePathD(node.path)}"`];
|
|
385
|
-
const fillAttrs = paintAttrs(node.fill, "fill", registry);
|
|
386
|
-
for (const a of fillAttrs) attrs.push(a);
|
|
387
|
-
if (node.path.kind === "polygon" && node.path.fillRule === "evenodd") {
|
|
388
|
-
attrs.push(`fill-rule="evenodd"`);
|
|
389
|
-
}
|
|
390
|
-
if (node.stroke) {
|
|
391
|
-
const strokeAttrs = strokeAttrsFor(node.stroke, registry);
|
|
392
|
-
for (const a of strokeAttrs) attrs.push(a);
|
|
393
|
-
} else {
|
|
394
|
-
attrs.push('stroke="none"');
|
|
395
|
-
}
|
|
396
|
-
if (node.opacity != null && node.opacity !== 1) {
|
|
397
|
-
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
398
|
-
}
|
|
399
|
-
if (node.rotation != null && node.rotation !== 0) {
|
|
400
|
-
const b = pathBounds(node.path);
|
|
401
|
-
const cx = b.minX + (b.maxX - b.minX) / 2;
|
|
402
|
-
const cy = b.minY + (b.maxY - b.minY) / 2;
|
|
403
|
-
const deg = node.rotation * 180 / Math.PI;
|
|
404
|
-
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
405
|
-
}
|
|
406
|
-
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
407
|
-
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
408
|
-
if (metaEls) {
|
|
409
|
-
return `<path ${attrs.join(" ")}${metaAttrs}>${metaEls}</path>`;
|
|
410
|
-
}
|
|
411
|
-
return `<path ${attrs.join(" ")}${metaAttrs}/>`;
|
|
412
|
-
}
|
|
413
|
-
function paintAttrs(paint, name, registry, includeOpacity = true) {
|
|
414
|
-
if (paint.kind === "none") return [`${name}="none"`];
|
|
415
|
-
if (paint.kind === "solid") {
|
|
416
|
-
const out = [`${name}="${paint.color}"`];
|
|
417
|
-
if (includeOpacity && paint.opacity != null && paint.opacity !== 1) {
|
|
418
|
-
out.push(`${name}-opacity="${trimNumber(paint.opacity)}"`);
|
|
419
|
-
}
|
|
420
|
-
return out;
|
|
421
|
-
}
|
|
422
|
-
const id = registry.register(paint.paint);
|
|
423
|
-
return [`${name}="url(#${id})"`];
|
|
424
|
-
}
|
|
425
|
-
function strokeWidthAttrs(width) {
|
|
426
|
-
if (typeof width === "number") return [`stroke-width="${trimNumber(width)}"`];
|
|
427
|
-
return [`stroke-width="${trimNumber(width.px)}"`, 'vector-effect="non-scaling-stroke"'];
|
|
428
|
-
}
|
|
429
|
-
function coreStrokeAttrs(stroke, registry) {
|
|
430
|
-
const paint = stroke?.paint;
|
|
431
|
-
if (!stroke || !paint) return [];
|
|
432
|
-
const width = stroke.width ?? 1;
|
|
433
|
-
if (!((typeof width === "object" ? width.px : width) > 0)) return [];
|
|
434
|
-
const attrs = [];
|
|
435
|
-
if ("color" in paint) {
|
|
436
|
-
attrs.push(`stroke="${paint.color}"`);
|
|
437
|
-
if (paint.opacity != null && paint.opacity !== 1) {
|
|
438
|
-
attrs.push(`stroke-opacity="${trimNumber(paint.opacity)}"`);
|
|
439
|
-
}
|
|
440
|
-
} else {
|
|
441
|
-
attrs.push(`stroke="url(#${registry.register(paint)})"`);
|
|
442
|
-
}
|
|
443
|
-
attrs.push(...strokeWidthAttrs(width));
|
|
444
|
-
if (stroke.cap) attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
445
|
-
if (stroke.join) attrs.push(`stroke-linejoin="${stroke.join}"`);
|
|
446
|
-
if (stroke.dash && stroke.dash.length > 0) {
|
|
447
|
-
attrs.push(`stroke-dasharray="${stroke.dash.map(trimNumber).join(" ")}"`);
|
|
448
|
-
}
|
|
449
|
-
if (stroke.miterLimit != null) attrs.push(`stroke-miterlimit="${trimNumber(stroke.miterLimit)}"`);
|
|
450
|
-
for (const [field, attr] of [
|
|
451
|
-
["markerStart", "marker-start"],
|
|
452
|
-
["markerMid", "marker-mid"],
|
|
453
|
-
["markerEnd", "marker-end"]
|
|
454
|
-
]) {
|
|
455
|
-
const ref = stroke[field];
|
|
456
|
-
if (ref === void 0) continue;
|
|
457
|
-
const key = typeof ref === "string" ? ref : ref.key;
|
|
458
|
-
const id = registry.markerId(key);
|
|
459
|
-
if (id === void 0) continue;
|
|
460
|
-
attrs.push(`${attr}="url(#${id})"`);
|
|
461
|
-
}
|
|
462
|
-
return attrs;
|
|
463
|
-
}
|
|
464
|
-
function strokeAttrsFor(stroke, registry) {
|
|
465
|
-
const attrs = paintAttrs(stroke.paint, "stroke", registry, false);
|
|
466
|
-
attrs.push(...strokeWidthAttrs(stroke.width));
|
|
467
|
-
const opacity = stroke.opacity ?? (stroke.paint.kind === "solid" ? stroke.paint.opacity : void 0);
|
|
468
|
-
if (opacity != null && opacity !== 1) {
|
|
469
|
-
attrs.push(`stroke-opacity="${trimNumber(opacity)}"`);
|
|
470
|
-
}
|
|
471
|
-
if (stroke.cap) {
|
|
472
|
-
attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
473
|
-
}
|
|
474
|
-
if (stroke.join) {
|
|
475
|
-
attrs.push(`stroke-linejoin="${stroke.join}"`);
|
|
476
|
-
}
|
|
477
|
-
if (stroke.dash && stroke.dash.length > 0) {
|
|
478
|
-
attrs.push(`stroke-dasharray="${stroke.dash.map(trimNumber).join(" ")}"`);
|
|
479
|
-
}
|
|
480
|
-
if (stroke.miterLimit != null) {
|
|
481
|
-
attrs.push(`stroke-miterlimit="${trimNumber(stroke.miterLimit)}"`);
|
|
482
|
-
}
|
|
483
|
-
for (const [field, attr] of [
|
|
484
|
-
["markerStart", "marker-start"],
|
|
485
|
-
["markerMid", "marker-mid"],
|
|
486
|
-
["markerEnd", "marker-end"]
|
|
487
|
-
]) {
|
|
488
|
-
const ref = stroke[field];
|
|
489
|
-
if (ref === void 0) continue;
|
|
490
|
-
const id = registry.markerId(ref);
|
|
491
|
-
if (id === void 0) continue;
|
|
492
|
-
attrs.push(`${attr}="url(#${id})"`);
|
|
493
|
-
}
|
|
494
|
-
return attrs;
|
|
495
|
-
}
|
|
496
|
-
function computeBounds(nodes) {
|
|
497
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
498
|
-
const visit = (n, ctm) => {
|
|
499
|
-
if (n.kind === "group") {
|
|
500
|
-
const childCtm = n.transform ? multiply(ctm, n.transform) : ctm;
|
|
501
|
-
n.children.forEach((c2) => visit(c2, childCtm));
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
const b = n.kind === "text" || n.kind === "image" ? { minX: n.x, minY: n.y, maxX: n.x + n.width, maxY: n.y + n.height } : pathBounds(n.path);
|
|
505
|
-
const local = n.rotation ? rotateAboutCenter(ctm, b, n.rotation) : ctm;
|
|
506
|
-
for (const [x, y2] of corners(b)) {
|
|
507
|
-
const px = local[0] * x + local[2] * y2 + local[4];
|
|
508
|
-
const py = local[1] * x + local[3] * y2 + local[5];
|
|
509
|
-
if (px < minX) minX = px;
|
|
510
|
-
if (py < minY) minY = py;
|
|
511
|
-
if (px > maxX) maxX = px;
|
|
512
|
-
if (py > maxY) maxY = py;
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
nodes.forEach((n) => visit(n, IDENTITY_MATRIX));
|
|
516
|
-
if (!Number.isFinite(minX)) {
|
|
517
|
-
return { x: 0, y: 0, width: 0, height: 0 };
|
|
518
|
-
}
|
|
519
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
520
|
-
}
|
|
521
|
-
function corners(b) {
|
|
522
|
-
return [[b.minX, b.minY], [b.maxX, b.minY], [b.maxX, b.maxY], [b.minX, b.maxY]];
|
|
523
|
-
}
|
|
524
|
-
function rotateAboutCenter(ctm, b, angle) {
|
|
525
|
-
const cx = (b.minX + b.maxX) / 2;
|
|
526
|
-
const cy = (b.minY + b.maxY) / 2;
|
|
527
|
-
const c2 = Math.cos(angle);
|
|
528
|
-
const s = Math.sin(angle);
|
|
529
|
-
return multiply(ctm, [c2, s, -s, c2, cx - cx * c2 + cy * s, cy - cx * s - cy * c2]);
|
|
530
|
-
}
|
|
531
|
-
function textXml(node, registry, namespaces) {
|
|
532
|
-
const attrs = [
|
|
533
|
-
`x="${trimNumber(node.x)}"`,
|
|
534
|
-
`y="${trimNumber(node.y)}"`,
|
|
535
|
-
`dominant-baseline="text-before-edge"`,
|
|
536
|
-
// The runs model stores real line breaks; SVG's default whitespace
|
|
537
|
-
// handling would collapse them (and any indentation) away on re-import.
|
|
538
|
-
`xml:space="preserve"`,
|
|
539
|
-
`data-weasel-width="${trimNumber(node.width)}"`,
|
|
540
|
-
`data-weasel-height="${trimNumber(node.height)}"`
|
|
541
|
-
];
|
|
542
|
-
const style = node.style;
|
|
543
|
-
if (style?.fontSize != null) attrs.push(`font-size="${trimNumber(style.fontSize)}"`);
|
|
544
|
-
if (style?.fontFamily) attrs.push(`font-family="${escapeAttr2(style.fontFamily)}"`);
|
|
545
|
-
if (style?.fontWeight != null) attrs.push(`font-weight="${String(style.fontWeight)}"`);
|
|
546
|
-
if (style?.fontStyle && style.fontStyle !== "normal") attrs.push(`font-style="${style.fontStyle}"`);
|
|
547
|
-
const direction = style?.direction ?? "ltr";
|
|
548
|
-
if (direction === "rtl") attrs.push('direction="rtl"');
|
|
549
|
-
if (style?.align != null || direction === "rtl") {
|
|
550
|
-
const align = style?.align ?? "left";
|
|
551
|
-
const anchor = align === "center" ? "middle" : align === "start" ? "start" : align === "end" ? "end" : align === "left" === (direction === "ltr") ? "start" : "end";
|
|
552
|
-
if (anchor !== "start") attrs.push(`text-anchor="${anchor}"`);
|
|
553
|
-
}
|
|
554
|
-
if (style?.letterSpacing != null && style.letterSpacing !== 0) {
|
|
555
|
-
attrs.push(`letter-spacing="${trimNumber(style.letterSpacing)}"`);
|
|
556
|
-
}
|
|
557
|
-
const decoration = textDecorationValue(style?.underline, style?.strikethrough, style?.overline);
|
|
558
|
-
if (decoration) attrs.push(`text-decoration="${decoration}"`);
|
|
559
|
-
if (node.fill === null) {
|
|
560
|
-
attrs.push('fill="none"');
|
|
561
|
-
} else if (node.fill) {
|
|
562
|
-
if ("color" in node.fill) {
|
|
563
|
-
attrs.push(`fill="${node.fill.color}"`);
|
|
564
|
-
if (node.fill.opacity != null && node.fill.opacity !== 1) {
|
|
565
|
-
attrs.push(`fill-opacity="${trimNumber(node.fill.opacity)}"`);
|
|
566
|
-
}
|
|
567
|
-
} else {
|
|
568
|
-
const id = registry.register(node.fill);
|
|
569
|
-
attrs.push(`fill="url(#${id})"`);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
for (const a of coreStrokeAttrs(node.stroke, registry)) attrs.push(a);
|
|
573
|
-
if (node.opacity != null && node.opacity !== 1) {
|
|
574
|
-
attrs.push(`opacity="${trimNumber(node.opacity)}"`);
|
|
575
|
-
}
|
|
576
|
-
if (node.rotation != null && node.rotation !== 0) {
|
|
577
|
-
const cx = node.x + node.width / 2;
|
|
578
|
-
const cy = node.y + node.height / 2;
|
|
579
|
-
const deg = node.rotation * 180 / Math.PI;
|
|
580
|
-
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
581
|
-
}
|
|
582
|
-
const body = node.runs && node.runs.length > 0 ? node.runs.map((r2) => runXml(r2, registry)).join("") : escapeText(node.text);
|
|
583
|
-
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
584
|
-
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
585
|
-
return `<text ${attrs.join(" ")}${metaAttrs}>${body}${metaEls}</text>`;
|
|
586
|
-
}
|
|
587
|
-
function runXml(run, registry) {
|
|
588
|
-
const attrs = [];
|
|
589
|
-
if (run.bold) attrs.push(`font-weight="700"`);
|
|
590
|
-
if (run.italic) attrs.push(`font-style="italic"`);
|
|
591
|
-
if (run.fontFamily) attrs.push(`font-family="${escapeAttr2(run.fontFamily)}"`);
|
|
592
|
-
const presetScale = run.script != null && run.baselineShift != null ? SCRIPT_METRICS[run.script].size : void 0;
|
|
593
|
-
const scale = run.fontScale ?? presetScale;
|
|
594
|
-
if (run.fontSize != null) attrs.push(`font-size="${trimNumber(run.fontSize)}"`);
|
|
595
|
-
else if (scale != null) attrs.push(`font-size="${trimNumber(scale * 100)}%"`);
|
|
596
|
-
if (run.baselineShift != null) {
|
|
597
|
-
attrs.push(`baseline-shift="${trimNumber(run.baselineShift * 100)}%"`);
|
|
598
|
-
} else if (run.script) {
|
|
599
|
-
attrs.push(`baseline-shift="${run.script}"`);
|
|
600
|
-
}
|
|
601
|
-
if (run.letterSpacing != null) {
|
|
602
|
-
attrs.push(`letter-spacing="${trimNumber(run.letterSpacing)}"`);
|
|
603
|
-
}
|
|
604
|
-
const runDecoration = textDecorationValue(run.underline, run.strikethrough, run.overline);
|
|
605
|
-
if (runDecoration) attrs.push(`text-decoration="${runDecoration}"`);
|
|
606
|
-
if (run.fill) {
|
|
607
|
-
if ("color" in run.fill) {
|
|
608
|
-
attrs.push(`fill="${run.fill.color}"`);
|
|
609
|
-
} else {
|
|
610
|
-
const id = registry.register(run.fill);
|
|
611
|
-
attrs.push(`fill="url(#${id})"`);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
for (const a of coreStrokeAttrs(run.stroke, registry)) attrs.push(a);
|
|
615
|
-
const head = attrs.length > 0 ? `<tspan ${attrs.join(" ")}>` : "<tspan>";
|
|
616
|
-
return `${head}${escapeText(run.text)}</tspan>`;
|
|
617
|
-
}
|
|
618
|
-
function textDecorationValue(underline, strikethrough, overline) {
|
|
619
|
-
const tokens = [];
|
|
620
|
-
if (underline) tokens.push("underline");
|
|
621
|
-
if (strikethrough) tokens.push("line-through");
|
|
622
|
-
if (overline) tokens.push("overline");
|
|
623
|
-
return tokens.length > 0 ? tokens.join(" ") : null;
|
|
624
|
-
}
|
|
625
|
-
function escapeAttr2(s) {
|
|
626
|
-
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
627
|
-
}
|
|
628
|
-
function escapeText(s) {
|
|
629
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
630
|
-
}
|
|
631
|
-
function pathBounds(path) {
|
|
632
|
-
if (path.kind === "rect") {
|
|
633
|
-
return {
|
|
634
|
-
minX: path.x,
|
|
635
|
-
minY: path.y,
|
|
636
|
-
maxX: path.x + path.width,
|
|
637
|
-
maxY: path.y + path.height
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
const b = boundsOfPath(path);
|
|
641
|
-
return { minX: b.x, minY: b.y, maxX: b.x + b.width, maxY: b.y + b.height };
|
|
642
|
-
}
|
|
643
56
|
var MARK_COLOR = "#e5484d";
|
|
644
57
|
var MARK_WIDTH = 2;
|
|
645
58
|
var TEXT_SIZE = 14;
|
|
646
|
-
var toWorld = (
|
|
647
|
-
x:
|
|
648
|
-
y:
|
|
59
|
+
var toWorld = (p2, content) => ({
|
|
60
|
+
x: p2.x * content.w,
|
|
61
|
+
y: p2.y * content.h
|
|
649
62
|
});
|
|
650
|
-
function vertices(
|
|
651
|
-
const stored =
|
|
652
|
-
if (stored && stored.length >= 2) return stored.map((
|
|
653
|
-
const { x, y:
|
|
63
|
+
function vertices(m2, content) {
|
|
64
|
+
const stored = m2.data.points;
|
|
65
|
+
if (stored && stored.length >= 2) return stored.map((p2) => toWorld(p2, content));
|
|
66
|
+
const { x, y: y3, width, height } = m2.pose;
|
|
654
67
|
return [
|
|
655
|
-
{ x, y:
|
|
656
|
-
{ x: x + width, y:
|
|
68
|
+
{ x, y: y3 },
|
|
69
|
+
{ x: x + width, y: y3 + height }
|
|
657
70
|
];
|
|
658
71
|
}
|
|
659
72
|
function polyline(points) {
|
|
660
|
-
const
|
|
73
|
+
const b2 = new PathBuilder();
|
|
661
74
|
const first = points[0];
|
|
662
75
|
if (!first) return rectPath(0, 0, 0, 0);
|
|
663
|
-
|
|
664
|
-
for (const
|
|
665
|
-
return
|
|
76
|
+
b2.moveTo(first.x, first.y);
|
|
77
|
+
for (const p2 of points.slice(1)) b2.lineTo(p2.x, p2.y);
|
|
78
|
+
return b2.build();
|
|
666
79
|
}
|
|
667
|
-
function markCommands(
|
|
80
|
+
function markCommands(m2, content, style = {}) {
|
|
668
81
|
const color = style.color ?? MARK_COLOR;
|
|
669
82
|
const stroke = {
|
|
670
83
|
paint: { color },
|
|
@@ -673,35 +86,35 @@ function markCommands(m, content, style = {}) {
|
|
|
673
86
|
join: "round",
|
|
674
87
|
...style.stale ? { dash: [6, 4] } : {}
|
|
675
88
|
};
|
|
676
|
-
switch (
|
|
89
|
+
switch (m2.data.kind) {
|
|
677
90
|
case "rect":
|
|
678
91
|
return [
|
|
679
|
-
{ kind: "path", path: rectPath(
|
|
92
|
+
{ kind: "path", path: rectPath(m2.pose.x, m2.pose.y, m2.pose.width, m2.pose.height), stroke }
|
|
680
93
|
];
|
|
681
94
|
case "ellipse":
|
|
682
|
-
return [{ kind: "path", path: ellipsePath(
|
|
95
|
+
return [{ kind: "path", path: ellipsePath(m2.pose), stroke }];
|
|
683
96
|
case "line": {
|
|
684
|
-
const [
|
|
685
|
-
return [{ kind: "path", path: linePath(
|
|
97
|
+
const [a4, b2] = vertices(m2, content);
|
|
98
|
+
return [{ kind: "path", path: linePath(a4, b2), stroke }];
|
|
686
99
|
}
|
|
687
100
|
case "arrow": {
|
|
688
|
-
const [
|
|
101
|
+
const [a4, b2] = vertices(m2, content);
|
|
689
102
|
const marked = { ...stroke, markerEnd: "arrow" };
|
|
690
|
-
const path = linePath(
|
|
103
|
+
const path = linePath(a4, b2);
|
|
691
104
|
return [
|
|
692
105
|
{ kind: "path", path, stroke: marked },
|
|
693
106
|
...markerDrawCommands(path, marked, MARK_WIDTH, void 0)
|
|
694
107
|
];
|
|
695
108
|
}
|
|
696
109
|
case "stroke":
|
|
697
|
-
return [{ kind: "path", path: polyline(vertices(
|
|
110
|
+
return [{ kind: "path", path: polyline(vertices(m2, content)), stroke }];
|
|
698
111
|
case "text": {
|
|
699
|
-
const text =
|
|
112
|
+
const text = m2.data.title;
|
|
700
113
|
if (!text) return [];
|
|
701
114
|
return [
|
|
702
115
|
textCommand(
|
|
703
|
-
|
|
704
|
-
|
|
116
|
+
m2.pose.x,
|
|
117
|
+
m2.pose.y,
|
|
705
118
|
text,
|
|
706
119
|
{ fontSize: TEXT_SIZE },
|
|
707
120
|
void 0,
|
|
@@ -721,8 +134,8 @@ function seenFrom(config, keys) {
|
|
|
721
134
|
const out = {};
|
|
722
135
|
if (config === null || typeof config !== "object") return out;
|
|
723
136
|
const src = config;
|
|
724
|
-
for (const
|
|
725
|
-
if (
|
|
137
|
+
for (const k3 of keys) {
|
|
138
|
+
if (k3 in src) out[k3] = src[k3];
|
|
726
139
|
}
|
|
727
140
|
return out;
|
|
728
141
|
}
|
|
@@ -730,9 +143,9 @@ function isStale(seen, config, keys) {
|
|
|
730
143
|
if (!seen || keys.length === 0) return false;
|
|
731
144
|
if (config === null || typeof config !== "object") return false;
|
|
732
145
|
const src = config;
|
|
733
|
-
for (const
|
|
734
|
-
if (!(
|
|
735
|
-
if (!Object.is(seen[
|
|
146
|
+
for (const k3 of keys) {
|
|
147
|
+
if (!(k3 in seen)) continue;
|
|
148
|
+
if (!Object.is(seen[k3], src[k3])) return true;
|
|
736
149
|
}
|
|
737
150
|
return false;
|
|
738
151
|
}
|
|
@@ -743,7 +156,7 @@ function createMarkDrawOne(opts) {
|
|
|
743
156
|
}
|
|
744
157
|
function resolveMarkStyle(data, opts) {
|
|
745
158
|
return {
|
|
746
|
-
color: opts.meaning?.statuses?.find((
|
|
159
|
+
color: opts.meaning?.statuses?.find((s5) => s5.id === data.status)?.color,
|
|
747
160
|
// Read off the node rather than through the store's `isStale`, which
|
|
748
161
|
// wants a projected Annotation this path does not have.
|
|
749
162
|
stale: isStale(data.seen, opts.config, opts.positionDependsOn ?? [])
|
|
@@ -770,10 +183,11 @@ function toSvgStroke(stroke) {
|
|
|
770
183
|
...stroke.cap ? { cap: stroke.cap } : {},
|
|
771
184
|
...stroke.join ? { join: stroke.join } : {},
|
|
772
185
|
...stroke.dash ? { dash: [...stroke.dash] } : {},
|
|
773
|
-
...stroke.miterLimit != null ? { miterLimit: stroke.miterLimit } : {}
|
|
186
|
+
...stroke.miterLimit != null ? { miterLimit: stroke.miterLimit } : {},
|
|
187
|
+
...stroke.align ? { align: stroke.align } : {}
|
|
774
188
|
};
|
|
775
189
|
}
|
|
776
|
-
function toSvgNode(cmd,
|
|
190
|
+
function toSvgNode(cmd, m2) {
|
|
777
191
|
if (cmd.kind === "path") {
|
|
778
192
|
return {
|
|
779
193
|
kind: "path",
|
|
@@ -787,8 +201,8 @@ function toSvgNode(cmd, m) {
|
|
|
787
201
|
kind: "text",
|
|
788
202
|
x: cmd.x,
|
|
789
203
|
y: cmd.y,
|
|
790
|
-
width:
|
|
791
|
-
height:
|
|
204
|
+
width: m2.pose.width,
|
|
205
|
+
height: m2.pose.height,
|
|
792
206
|
text: cmd.runs.map((run) => run.text).join(""),
|
|
793
207
|
style: cmd.style,
|
|
794
208
|
...cmd.runs[0]?.fill ? { fill: cmd.runs[0].fill } : {}
|
|
@@ -796,8 +210,8 @@ function toSvgNode(cmd, m) {
|
|
|
796
210
|
}
|
|
797
211
|
throw new Error(`[labkit] a mark drew a '${cmd.kind}' command, which has no vector form`);
|
|
798
212
|
}
|
|
799
|
-
function markSvgNodes(
|
|
800
|
-
return markCommands(
|
|
213
|
+
function markSvgNodes(m2, content, style = {}) {
|
|
214
|
+
return markCommands(m2, content, style).map((cmd) => toSvgNode(cmd, m2));
|
|
801
215
|
}
|
|
802
216
|
|
|
803
217
|
// src/annotations/capture.ts
|
|
@@ -806,7 +220,7 @@ function capturePlan(base, format) {
|
|
|
806
220
|
if (format === "svg") return "svg-document";
|
|
807
221
|
return base?.kind === "svg" ? "svg-document" : "raster-stack";
|
|
808
222
|
}
|
|
809
|
-
function nestBaseSvg(markup, w3,
|
|
223
|
+
function nestBaseSvg(markup, w3, h2) {
|
|
810
224
|
const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
|
|
811
225
|
const root = doc.documentElement;
|
|
812
226
|
if (doc.getElementsByTagName("parsererror").length > 0 || root.localName !== "svg") {
|
|
@@ -815,16 +229,16 @@ function nestBaseSvg(markup, w3, h) {
|
|
|
815
229
|
root.setAttribute("x", "0");
|
|
816
230
|
root.setAttribute("y", "0");
|
|
817
231
|
root.setAttribute("width", String(w3));
|
|
818
|
-
root.setAttribute("height", String(
|
|
232
|
+
root.setAttribute("height", String(h2));
|
|
819
233
|
return new XMLSerializer().serializeToString(root);
|
|
820
234
|
}
|
|
821
|
-
function embedRasterBase(base, w3,
|
|
235
|
+
function embedRasterBase(base, w3, h2, onWarn) {
|
|
822
236
|
const href = base.kind === "canvas" ? base.canvas.toDataURL() : base.kind === "image" ? base.src : "";
|
|
823
237
|
if (base.kind === "image" && !href.startsWith("data:")) {
|
|
824
238
|
onWarn?.(`base image '${href}' is a reference, so this SVG is not self-contained`);
|
|
825
239
|
}
|
|
826
|
-
return serializeSvg([{ kind: "image", href, x: 0, y: 0, width: w3, height:
|
|
827
|
-
viewBox: { x: 0, y: 0, width: w3, height:
|
|
240
|
+
return serializeSvg([{ kind: "image", href, x: 0, y: 0, width: w3, height: h2 }], {
|
|
241
|
+
viewBox: { x: 0, y: 0, width: w3, height: h2 }
|
|
828
242
|
});
|
|
829
243
|
}
|
|
830
244
|
function markNodesOf(scene, draw) {
|
|
@@ -840,15 +254,15 @@ function markNodesOf(scene, draw) {
|
|
|
840
254
|
}
|
|
841
255
|
function composeCaptureSvg(args) {
|
|
842
256
|
const { base, scene, draw, scale, onWarn } = args;
|
|
843
|
-
const { w: w3, h } = draw.content;
|
|
844
|
-
const baseXml = base === void 0 ? "" : base.kind === "svg" ? nestBaseSvg(base.markup, w3,
|
|
257
|
+
const { w: w3, h: h2 } = draw.content;
|
|
258
|
+
const baseXml = base === void 0 ? "" : base.kind === "svg" ? nestBaseSvg(base.markup, w3, h2) : embedRasterBase(base, w3, h2, onWarn);
|
|
845
259
|
const marksXml = serializeSvg(markNodesOf(scene, draw), {
|
|
846
|
-
viewBox: { x: 0, y: 0, width: w3, height:
|
|
260
|
+
viewBox: { x: 0, y: 0, width: w3, height: h2 },
|
|
847
261
|
onWarn
|
|
848
262
|
});
|
|
849
263
|
const outW = Math.max(1, Math.round(w3 * scale));
|
|
850
|
-
const outH = Math.max(1, Math.round(
|
|
851
|
-
return `<svg xmlns="${SVG_NS}" width="${outW}" height="${outH}" viewBox="0 0 ${w3} ${
|
|
264
|
+
const outH = Math.max(1, Math.round(h2 * scale));
|
|
265
|
+
return `<svg xmlns="${SVG_NS}" width="${outW}" height="${outH}" viewBox="0 0 ${w3} ${h2}">${baseXml}${marksXml}</svg>`;
|
|
852
266
|
}
|
|
853
267
|
function context2d(width, height) {
|
|
854
268
|
const canvas = document.createElement("canvas");
|
|
@@ -942,26 +356,26 @@ async function captureTarget(args, opts = {}) {
|
|
|
942
356
|
}
|
|
943
357
|
|
|
944
358
|
// src/annotations/frac.ts
|
|
945
|
-
function fracToWorld(
|
|
359
|
+
function fracToWorld(f3, content) {
|
|
946
360
|
return {
|
|
947
|
-
x:
|
|
948
|
-
y:
|
|
949
|
-
width:
|
|
950
|
-
height:
|
|
361
|
+
x: f3.x * content.w,
|
|
362
|
+
y: f3.y * content.h,
|
|
363
|
+
width: f3.w * content.w,
|
|
364
|
+
height: f3.h * content.h
|
|
951
365
|
};
|
|
952
366
|
}
|
|
953
|
-
function worldToFrac(
|
|
367
|
+
function worldToFrac(r10, content) {
|
|
954
368
|
const sx = content.w === 0 ? 0 : 1 / content.w;
|
|
955
369
|
const sy = content.h === 0 ? 0 : 1 / content.h;
|
|
956
|
-
return { x:
|
|
370
|
+
return { x: r10.x * sx, y: r10.y * sy, w: r10.width * sx, h: r10.height * sy };
|
|
957
371
|
}
|
|
958
372
|
var DP = 1e4;
|
|
959
|
-
function roundFrac(
|
|
373
|
+
function roundFrac(f3) {
|
|
960
374
|
return {
|
|
961
|
-
x: Math.round(
|
|
962
|
-
y: Math.round(
|
|
963
|
-
w: Math.round(
|
|
964
|
-
h: Math.round(
|
|
375
|
+
x: Math.round(f3.x * DP) / DP,
|
|
376
|
+
y: Math.round(f3.y * DP) / DP,
|
|
377
|
+
w: Math.round(f3.w * DP) / DP,
|
|
378
|
+
h: Math.round(f3.h * DP) / DP
|
|
965
379
|
};
|
|
966
380
|
}
|
|
967
381
|
function fracContains(box, pt, tol = 0) {
|
|
@@ -1003,8 +417,8 @@ var MarkHistory = class {
|
|
|
1003
417
|
/** The target `undo` would act on, skipping any whose scene has nothing
|
|
1004
418
|
* left — a scene can drop entries this ordering still lists. */
|
|
1005
419
|
next(stack, can, at) {
|
|
1006
|
-
for (let
|
|
1007
|
-
const target = stack[
|
|
420
|
+
for (let i3 = stack.length - 1; i3 >= 0; i3--) {
|
|
421
|
+
const target = stack[i3];
|
|
1008
422
|
if (target === void 0) continue;
|
|
1009
423
|
const scene = at(target);
|
|
1010
424
|
if (scene && can(scene)) return target;
|
|
@@ -1012,13 +426,13 @@ var MarkHistory = class {
|
|
|
1012
426
|
return null;
|
|
1013
427
|
}
|
|
1014
428
|
canUndo(at) {
|
|
1015
|
-
return this.next(this.undoable, (
|
|
429
|
+
return this.next(this.undoable, (s5) => s5.canUndo(), at) !== null;
|
|
1016
430
|
}
|
|
1017
431
|
canRedo(at) {
|
|
1018
|
-
return this.next(this.redoable, (
|
|
432
|
+
return this.next(this.redoable, (s5) => s5.canRedo(), at) !== null;
|
|
1019
433
|
}
|
|
1020
434
|
undo(at) {
|
|
1021
|
-
const target = this.next(this.undoable, (
|
|
435
|
+
const target = this.next(this.undoable, (s5) => s5.canUndo(), at);
|
|
1022
436
|
const scene = target === null ? void 0 : at(target);
|
|
1023
437
|
if (!scene) return false;
|
|
1024
438
|
this.replaying = "undo";
|
|
@@ -1029,7 +443,7 @@ var MarkHistory = class {
|
|
|
1029
443
|
}
|
|
1030
444
|
}
|
|
1031
445
|
redo(at) {
|
|
1032
|
-
const target = this.next(this.redoable, (
|
|
446
|
+
const target = this.next(this.redoable, (s5) => s5.canRedo(), at);
|
|
1033
447
|
const scene = target === null ? void 0 : at(target);
|
|
1034
448
|
if (!scene) return false;
|
|
1035
449
|
this.replaying = "redo";
|
|
@@ -1041,9 +455,9 @@ var MarkHistory = class {
|
|
|
1041
455
|
}
|
|
1042
456
|
};
|
|
1043
457
|
function drop(stack, value) {
|
|
1044
|
-
for (let
|
|
1045
|
-
if (stack[
|
|
1046
|
-
stack.splice(
|
|
458
|
+
for (let i3 = stack.length - 1; i3 >= 0; i3--) {
|
|
459
|
+
if (stack[i3] === value) {
|
|
460
|
+
stack.splice(i3, 1);
|
|
1047
461
|
return;
|
|
1048
462
|
}
|
|
1049
463
|
}
|
|
@@ -1104,21 +518,21 @@ function createAnnotationStore(opts) {
|
|
|
1104
518
|
if (!scene) return [];
|
|
1105
519
|
const out = [];
|
|
1106
520
|
for (const id of scene.renderOrder()) {
|
|
1107
|
-
const
|
|
1108
|
-
if (
|
|
521
|
+
const a4 = project(target, String(id));
|
|
522
|
+
if (a4) out.push(a4);
|
|
1109
523
|
}
|
|
1110
524
|
return out;
|
|
1111
525
|
};
|
|
1112
526
|
const all = () => liveTargets().flatMap(marksOn);
|
|
1113
|
-
const matches = (
|
|
1114
|
-
if (
|
|
1115
|
-
if (
|
|
1116
|
-
if (
|
|
1117
|
-
if (
|
|
1118
|
-
const have =
|
|
1119
|
-
for (const t of
|
|
527
|
+
const matches = (a4, q3) => {
|
|
528
|
+
if (q3.target !== void 0 && a4.target !== q3.target) return false;
|
|
529
|
+
if (q3.kind !== void 0 && a4.kind !== q3.kind) return false;
|
|
530
|
+
if (q3.status !== void 0 && a4.status !== q3.status) return false;
|
|
531
|
+
if (q3.tags !== void 0) {
|
|
532
|
+
const have = a4.tags ?? [];
|
|
533
|
+
for (const t of q3.tags) if (!have.includes(t)) return false;
|
|
1120
534
|
}
|
|
1121
|
-
if (
|
|
535
|
+
if (q3.where && !q3.where(a4)) return false;
|
|
1122
536
|
return true;
|
|
1123
537
|
};
|
|
1124
538
|
const nodeOf = (id) => {
|
|
@@ -1135,18 +549,18 @@ function createAnnotationStore(opts) {
|
|
|
1135
549
|
const parts = splitId(id);
|
|
1136
550
|
return parts ? project(parts.target, parts.node) : void 0;
|
|
1137
551
|
},
|
|
1138
|
-
query(
|
|
1139
|
-
return
|
|
552
|
+
query(q3) {
|
|
553
|
+
return q3 ? all().filter((a4) => matches(a4, q3)) : all();
|
|
1140
554
|
},
|
|
1141
555
|
hitTest(target, pt, tol = 0) {
|
|
1142
|
-
return marksOn(target).filter((
|
|
556
|
+
return marksOn(target).filter((a4) => fracContains(a4.frac, pt, tol)).reverse();
|
|
1143
557
|
},
|
|
1144
558
|
within(target, box) {
|
|
1145
|
-
return marksOn(target).filter((
|
|
559
|
+
return marksOn(target).filter((a4) => fracEncloses(box, a4.frac));
|
|
1146
560
|
},
|
|
1147
|
-
isStale(
|
|
1148
|
-
const keys = targetOf(
|
|
1149
|
-
return isStale(
|
|
561
|
+
isStale(a4, config) {
|
|
562
|
+
const keys = targetOf(a4.target)?.positionDependsOn ?? [];
|
|
563
|
+
return isStale(a4.seen, config, keys);
|
|
1150
564
|
},
|
|
1151
565
|
canUndo() {
|
|
1152
566
|
return history.canUndo(historySceneAt);
|
|
@@ -1184,7 +598,7 @@ function createAnnotationStore(opts) {
|
|
|
1184
598
|
for (const [target, scene] of scenes) {
|
|
1185
599
|
const next = wanted.get(target) ?? [];
|
|
1186
600
|
const now = scene.getSelection();
|
|
1187
|
-
if (now.length === next.length && next.every((
|
|
601
|
+
if (now.length === next.length && next.every((n6, i3) => String(now[i3]) === n6)) continue;
|
|
1188
602
|
scene.setSelection(next.map(asNodeId$1));
|
|
1189
603
|
}
|
|
1190
604
|
},
|
|
@@ -1246,7 +660,7 @@ function createAnnotationStore(opts) {
|
|
|
1246
660
|
content: info.content,
|
|
1247
661
|
positionDependsOn: info.positionDependsOn,
|
|
1248
662
|
config: opts.config?.(),
|
|
1249
|
-
meaning: opts.meaning
|
|
663
|
+
meaning: typeof opts.meaning === "function" ? opts.meaning() : opts.meaning
|
|
1250
664
|
},
|
|
1251
665
|
base: info.base,
|
|
1252
666
|
onWarn: (message) => console.warn(`[labkit] capture: ${message}`)
|
|
@@ -1269,18 +683,46 @@ function annotationsFromJSON(raw, targets, rest = {}) {
|
|
|
1269
683
|
return createAnnotationStore({ targets, restore: doc.scenes, ...rest });
|
|
1270
684
|
}
|
|
1271
685
|
|
|
686
|
+
// src/annotations/toolMap.ts
|
|
687
|
+
var TOOLS = {
|
|
688
|
+
select: { weaselTool: "select" },
|
|
689
|
+
stroke: { weaselTool: "pencil", kind: "stroke" },
|
|
690
|
+
line: { weaselTool: "line", kind: "line" },
|
|
691
|
+
arrow: { weaselTool: "line", kind: "arrow" },
|
|
692
|
+
rect: { weaselTool: "rect", kind: "rect" },
|
|
693
|
+
ellipse: { weaselTool: "ellipse", kind: "ellipse" },
|
|
694
|
+
text: { weaselTool: "text", kind: "text" }
|
|
695
|
+
};
|
|
696
|
+
var ANNOTATION_TOOLS = [
|
|
697
|
+
// Not in `TOOLS`: an id the map does not carry is what leaves the overlay idle.
|
|
698
|
+
{ id: "pointer", label: "Interact", icon: a2, group: "annotate" },
|
|
699
|
+
{ id: "select", label: "Select", icon: p, group: "annotate" },
|
|
700
|
+
{ id: "stroke", label: "Freehand", icon: u, group: "annotate" },
|
|
701
|
+
{ id: "line", label: "Line", icon: c, group: "annotate" },
|
|
702
|
+
{ id: "arrow", label: "Arrow", icon: n, group: "annotate" },
|
|
703
|
+
{ id: "rect", label: "Rectangle", icon: f, group: "annotate" },
|
|
704
|
+
{ id: "ellipse", label: "Ellipse", icon: r, group: "annotate" },
|
|
705
|
+
{ id: "text", label: "Text", icon: h, group: "annotate" }
|
|
706
|
+
];
|
|
707
|
+
function annotationToolInfo(id) {
|
|
708
|
+
return id === null ? void 0 : TOOLS[id];
|
|
709
|
+
}
|
|
710
|
+
var ANNOTATION_WEASEL_TOOLS = [
|
|
711
|
+
...new Set(Object.values(TOOLS).map((t) => t.weaselTool))
|
|
712
|
+
];
|
|
713
|
+
|
|
1272
714
|
// src/annotations/view.ts
|
|
1273
|
-
function toWeaselView(
|
|
1274
|
-
return { x:
|
|
715
|
+
function toWeaselView(v2) {
|
|
716
|
+
return { x: v2.pan.x, y: v2.pan.y, scale: { x: v2.zoom, y: v2.zoom } };
|
|
1275
717
|
}
|
|
1276
|
-
function fromWeaselView(
|
|
1277
|
-
return { zoom:
|
|
718
|
+
function fromWeaselView(v2) {
|
|
719
|
+
return { zoom: v2.scale.x, pan: { x: v2.x, y: v2.y } };
|
|
1278
720
|
}
|
|
1279
721
|
function fitView(content, pane) {
|
|
1280
722
|
const zoom = content.w > 0 && content.h > 0 ? Math.min(pane.w / content.w, pane.h / content.h) : 1;
|
|
1281
723
|
return { zoom: Number.isFinite(zoom) && zoom > 0 ? zoom : 1, pan: { x: 0, y: 0 } };
|
|
1282
724
|
}
|
|
1283
|
-
var sameRect = (
|
|
725
|
+
var sameRect = (a4, b2) => a4 !== null && a4.x === b2.x && a4.y === b2.y && a4.w === b2.w && a4.h === b2.h;
|
|
1284
726
|
function ToolBridge({ toolId }) {
|
|
1285
727
|
const tools = useActiveToolContext();
|
|
1286
728
|
const setActive = tools.setActive;
|
|
@@ -1290,9 +732,9 @@ function ToolBridge({ toolId }) {
|
|
|
1290
732
|
return null;
|
|
1291
733
|
}
|
|
1292
734
|
function pointsOf(extras, content) {
|
|
1293
|
-
const toFrac = (
|
|
1294
|
-
x: content.w === 0 ? 0 :
|
|
1295
|
-
y: content.h === 0 ? 0 :
|
|
735
|
+
const toFrac = (p2) => ({
|
|
736
|
+
x: content.w === 0 ? 0 : p2.x / content.w,
|
|
737
|
+
y: content.h === 0 ? 0 : p2.y / content.h
|
|
1296
738
|
});
|
|
1297
739
|
if (extras.kind === "line") {
|
|
1298
740
|
return [toFrac(extras.a), toFrac(extras.b)];
|
|
@@ -1334,9 +776,40 @@ function AnnotationOverlay({
|
|
|
1334
776
|
},
|
|
1335
777
|
[surface, tileId]
|
|
1336
778
|
);
|
|
779
|
+
const painted = useRef(false);
|
|
780
|
+
const canvasRef = useRef(canvas);
|
|
781
|
+
if (canvasRef.current !== canvas) painted.current = false;
|
|
782
|
+
canvasRef.current = canvas;
|
|
783
|
+
const unsubscribeFrame = useRef(null);
|
|
784
|
+
const attachSceneCanvas = useCallback((api) => {
|
|
785
|
+
sceneCanvas.current = api;
|
|
786
|
+
unsubscribeFrame.current?.();
|
|
787
|
+
unsubscribeFrame.current = api ? api.subscribeFrame(() => {
|
|
788
|
+
painted.current = true;
|
|
789
|
+
}) : null;
|
|
790
|
+
}, []);
|
|
791
|
+
useEffect(() => {
|
|
792
|
+
if (!surface) return;
|
|
793
|
+
return surface.registerClear(tileId, (size, dpr) => {
|
|
794
|
+
if (!painted.current) return;
|
|
795
|
+
const gl = canvasRef.current?.getContext("webgl2");
|
|
796
|
+
if (!gl) return;
|
|
797
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
798
|
+
gl.viewport(0, 0, Math.round(size.width * dpr), Math.round(size.height * dpr));
|
|
799
|
+
gl.clearColor(0, 0, 0, 0);
|
|
800
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
801
|
+
});
|
|
802
|
+
}, [surface, tileId]);
|
|
803
|
+
const wheelSlot = useContext(CameraWheelContext);
|
|
804
|
+
useEffect(() => {
|
|
805
|
+
if (!input || !wheelSlot) return;
|
|
806
|
+
const wheel = (e) => wheelSlot.current?.(e);
|
|
807
|
+
input.addEventListener("wheel", wheel, { passive: false });
|
|
808
|
+
return () => input.removeEventListener("wheel", wheel);
|
|
809
|
+
}, [input, wheelSlot]);
|
|
1337
810
|
useEffect(
|
|
1338
811
|
() => surface?.registerPainter(tileId, (next) => {
|
|
1339
|
-
setRect((prev) => sameRect(prev, next) ? prev : next);
|
|
812
|
+
flushSync(() => setRect((prev) => sameRect(prev, next) ? prev : next));
|
|
1340
813
|
sceneCanvas.current?.requestRedraw();
|
|
1341
814
|
}),
|
|
1342
815
|
[surface, tileId]
|
|
@@ -1380,6 +853,7 @@ function AnnotationOverlay({
|
|
|
1380
853
|
ref: setInput,
|
|
1381
854
|
className: "lk-annotate__input",
|
|
1382
855
|
"data-annotation-target": id,
|
|
856
|
+
"data-idle": annotationToolInfo(activeToolId) ? void 0 : "",
|
|
1383
857
|
role: "application",
|
|
1384
858
|
"aria-label": `Annotations on ${id}`,
|
|
1385
859
|
tabIndex: 0,
|
|
@@ -1394,7 +868,7 @@ function AnnotationOverlay({
|
|
|
1394
868
|
/* @__PURE__ */ jsx(
|
|
1395
869
|
SceneCanvas,
|
|
1396
870
|
{
|
|
1397
|
-
ref:
|
|
871
|
+
ref: attachSceneCanvas,
|
|
1398
872
|
scene,
|
|
1399
873
|
width: rect.w,
|
|
1400
874
|
height: rect.h,
|
|
@@ -1434,314 +908,6 @@ function AnnotationTargets({
|
|
|
1434
908
|
)) });
|
|
1435
909
|
}
|
|
1436
910
|
|
|
1437
|
-
// src/config/builder.ts
|
|
1438
|
-
function isConfigBranch(entry) {
|
|
1439
|
-
return "children" in entry;
|
|
1440
|
-
}
|
|
1441
|
-
var BaseNode = class {
|
|
1442
|
-
constructor(defaultValue, annotations = {}, options = {}) {
|
|
1443
|
-
this.annotations = annotations;
|
|
1444
|
-
this.options = options;
|
|
1445
|
-
this.default = defaultValue;
|
|
1446
|
-
}
|
|
1447
|
-
annotations;
|
|
1448
|
-
options;
|
|
1449
|
-
// `default` is a reserved word in a parameter position, so the field is
|
|
1450
|
-
// declared rather than taken as a parameter property.
|
|
1451
|
-
default;
|
|
1452
|
-
/** Clone into the same subclass. Subclasses with extra constructor
|
|
1453
|
-
* arguments override this. */
|
|
1454
|
-
with(annotations, options) {
|
|
1455
|
-
const Ctor = this.constructor;
|
|
1456
|
-
return new Ctor(this.default, annotations, options);
|
|
1457
|
-
}
|
|
1458
|
-
ann(patch) {
|
|
1459
|
-
return this.with({ ...this.annotations, ...patch }, this.options);
|
|
1460
|
-
}
|
|
1461
|
-
opt(patch) {
|
|
1462
|
-
return this.with(this.annotations, { ...this.options, ...patch });
|
|
1463
|
-
}
|
|
1464
|
-
/** Human-readable label. Defaults to the key, title-cased. */
|
|
1465
|
-
label(name) {
|
|
1466
|
-
return this.ann({ name });
|
|
1467
|
-
}
|
|
1468
|
-
/** Longer help text, surfaced as a tooltip on the row label. */
|
|
1469
|
-
describe(description) {
|
|
1470
|
-
return this.ann({ description });
|
|
1471
|
-
}
|
|
1472
|
-
/** Omit from the panel unless it is asked to show hidden leaves. */
|
|
1473
|
-
hidden() {
|
|
1474
|
-
return this.ann({ hidden: true });
|
|
1475
|
-
}
|
|
1476
|
-
/** Pair with a sibling sharing this id, side-by-side on one row. */
|
|
1477
|
-
pair(pair) {
|
|
1478
|
-
return this.ann({ pair });
|
|
1479
|
-
}
|
|
1480
|
-
/** Render under a named section heading. `collapsed` opens the section
|
|
1481
|
-
* folded — say it on any one of the section's leaves. */
|
|
1482
|
-
section(label, opts = {}) {
|
|
1483
|
-
return this.opt({ section: { label, ...opts } });
|
|
1484
|
-
}
|
|
1485
|
-
/** Show this row only while the predicate holds. Presentational — the value
|
|
1486
|
-
* stays in config and the instrument still reads it. */
|
|
1487
|
-
showIf(predicate) {
|
|
1488
|
-
return this.opt({ showIf: predicate });
|
|
1489
|
-
}
|
|
1490
|
-
/** Draw this one row yourself, keeping the kind, default and validation. */
|
|
1491
|
-
render(renderer) {
|
|
1492
|
-
return this.opt({ render: renderer });
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
var NumberNode = class extends BaseNode {
|
|
1496
|
-
kind = "number";
|
|
1497
|
-
/** Bound the value. A number with both bounds renders as a slider. */
|
|
1498
|
-
range(min, max) {
|
|
1499
|
-
return this.ann({ min, max });
|
|
1500
|
-
}
|
|
1501
|
-
step(step) {
|
|
1502
|
-
return this.ann({ step });
|
|
1503
|
-
}
|
|
1504
|
-
/** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:
|
|
1505
|
-
* it is never parsed, and the stored value stays a plain number. */
|
|
1506
|
-
suffix(suffix) {
|
|
1507
|
-
return this.ann({ suffix });
|
|
1508
|
-
}
|
|
1509
|
-
/** Force a slider even without both bounds. */
|
|
1510
|
-
slider() {
|
|
1511
|
-
return this.ann({ control: "slider" });
|
|
1512
|
-
}
|
|
1513
|
-
/** Force a typed input even when both bounds are set. */
|
|
1514
|
-
input() {
|
|
1515
|
-
return this.ann({ control: "input" });
|
|
1516
|
-
}
|
|
1517
|
-
};
|
|
1518
|
-
var BooleanNode = class extends BaseNode {
|
|
1519
|
-
kind = "boolean";
|
|
1520
|
-
/** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's
|
|
1521
|
-
* `PrefsForm` honors the distinction. */
|
|
1522
|
-
toggle() {
|
|
1523
|
-
return this.ann({ control: "switch" });
|
|
1524
|
-
}
|
|
1525
|
-
};
|
|
1526
|
-
var StringNode = class extends BaseNode {
|
|
1527
|
-
kind = "string";
|
|
1528
|
-
placeholder(placeholder) {
|
|
1529
|
-
return this.ann({ placeholder });
|
|
1530
|
-
}
|
|
1531
|
-
maxLength(maxLength) {
|
|
1532
|
-
return this.ann({ maxLength });
|
|
1533
|
-
}
|
|
1534
|
-
/** Milliseconds to debounce live writes. 0 commits every keystroke. */
|
|
1535
|
-
debounce(debounceMs) {
|
|
1536
|
-
return this.ann({ debounceMs });
|
|
1537
|
-
}
|
|
1538
|
-
};
|
|
1539
|
-
var ColorNode = class extends BaseNode {
|
|
1540
|
-
kind = "color";
|
|
1541
|
-
};
|
|
1542
|
-
var EnumNode = class extends BaseNode {
|
|
1543
|
-
kind = "enum";
|
|
1544
|
-
/** Render as a segmented control rather than a select. */
|
|
1545
|
-
radio() {
|
|
1546
|
-
return this.ann({ control: "radio" });
|
|
1547
|
-
}
|
|
1548
|
-
/** Relabel the choices. Named `labels` rather than `options` because
|
|
1549
|
-
* `options` is the node's own extras bag. */
|
|
1550
|
-
labels(options) {
|
|
1551
|
-
return this.ann({ options });
|
|
1552
|
-
}
|
|
1553
|
-
};
|
|
1554
|
-
var ValueNode = class extends BaseNode {
|
|
1555
|
-
kind = null;
|
|
1556
|
-
};
|
|
1557
|
-
var CustomNode = class _CustomNode extends BaseNode {
|
|
1558
|
-
constructor(kind, defaultValue, annotations = {}, options = {}) {
|
|
1559
|
-
super(defaultValue, annotations, options);
|
|
1560
|
-
this.kind = kind;
|
|
1561
|
-
}
|
|
1562
|
-
kind;
|
|
1563
|
-
with(annotations, options) {
|
|
1564
|
-
return new _CustomNode(this.kind, this.default, annotations, options);
|
|
1565
|
-
}
|
|
1566
|
-
};
|
|
1567
|
-
var GroupNode = class _GroupNode {
|
|
1568
|
-
constructor(children, annotations = {}, options = {}) {
|
|
1569
|
-
this.children = children;
|
|
1570
|
-
this.annotations = annotations;
|
|
1571
|
-
this.options = options;
|
|
1572
|
-
}
|
|
1573
|
-
children;
|
|
1574
|
-
annotations;
|
|
1575
|
-
options;
|
|
1576
|
-
with(annotations, options) {
|
|
1577
|
-
return new _GroupNode(this.children, annotations, options);
|
|
1578
|
-
}
|
|
1579
|
-
/** Heading for the group's rows. Defaults to the key, title-cased. */
|
|
1580
|
-
label(name) {
|
|
1581
|
-
return this.with({ ...this.annotations, name }, this.options);
|
|
1582
|
-
}
|
|
1583
|
-
/** Longer help text. Carried on the resolved `PrefGroup`, where weasel-ui's
|
|
1584
|
-
* `PrefsForm` draws it under the heading; `ControlPanel` has no place for
|
|
1585
|
-
* it yet and shows the heading alone. */
|
|
1586
|
-
describe(description) {
|
|
1587
|
-
return this.with({ ...this.annotations, description }, this.options);
|
|
1588
|
-
}
|
|
1589
|
-
/** Render this whole group under a named section heading. */
|
|
1590
|
-
section(label, opts = {}) {
|
|
1591
|
-
return this.with(this.annotations, { ...this.options, section: { label, ...opts } });
|
|
1592
|
-
}
|
|
1593
|
-
/** Show this group only while the predicate holds. Presentational — every
|
|
1594
|
-
* value beneath it stays in config and the instrument still reads it. */
|
|
1595
|
-
showIf(predicate) {
|
|
1596
|
-
return this.with(this.annotations, { ...this.options, showIf: predicate });
|
|
1597
|
-
}
|
|
1598
|
-
};
|
|
1599
|
-
function shapeDefaults(shape) {
|
|
1600
|
-
const out = {};
|
|
1601
|
-
for (const [key, entry] of Object.entries(shape)) {
|
|
1602
|
-
out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;
|
|
1603
|
-
}
|
|
1604
|
-
return out;
|
|
1605
|
-
}
|
|
1606
|
-
var expand = (options) => options.map((o) => typeof o === "string" ? { value: o, label: o } : o);
|
|
1607
|
-
var f = {
|
|
1608
|
-
number: (def) => new NumberNode(def),
|
|
1609
|
-
boolean: (def) => new BooleanNode(def),
|
|
1610
|
-
string: (def) => new StringNode(def),
|
|
1611
|
-
color: (def) => new ColorNode(def),
|
|
1612
|
-
/** A fixed set of choices. The default's literal type flows into the config
|
|
1613
|
-
* type, so `f.enum('fast', ['fast', 'accurate'])` gives
|
|
1614
|
-
* `'fast' | 'accurate'` with no `as const`. */
|
|
1615
|
-
enum: (def, options) => new EnumNode(def, { options: expand(options) }),
|
|
1616
|
-
/** A leaf with no declared kind: the rule chain decides, which is how a lab
|
|
1617
|
-
* states a convention like "every `*Color` key is a color picker". */
|
|
1618
|
-
value: (def) => new ValueNode(def),
|
|
1619
|
-
/** A branch: its children nest under this key, so `grid: f.group({ size })`
|
|
1620
|
-
* puts the value at `config.grid.size` and addresses it as `'grid.size'`.
|
|
1621
|
-
* Groups nest as deeply as the schema wants. */
|
|
1622
|
-
group: (children) => new GroupNode(children),
|
|
1623
|
-
/** A leaf of a kind a lab supplies the control for, through `controls`. */
|
|
1624
|
-
custom: (kind, def, validate) => new CustomNode(kind, def, {}, validate ? { validate } : {}),
|
|
1625
|
-
/** Collect leaves and groups into an instrument's config. */
|
|
1626
|
-
schema(nodes) {
|
|
1627
|
-
return { nodes, defaults: () => shapeDefaults(nodes) };
|
|
1628
|
-
}
|
|
1629
|
-
};
|
|
1630
|
-
|
|
1631
|
-
// src/config/rules.ts
|
|
1632
|
-
function titleCase(key) {
|
|
1633
|
-
const words = key.replace(/[_-]+/g, " ").split(/(?<=[a-z0-9])(?=[A-Z])/).join(" ").trim().toLowerCase();
|
|
1634
|
-
return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);
|
|
1635
|
-
}
|
|
1636
|
-
var kindFromValue = (ctx) => {
|
|
1637
|
-
const t = typeof ctx.default;
|
|
1638
|
-
if (t === "boolean" || t === "number" || t === "string") return { kind: t };
|
|
1639
|
-
return null;
|
|
1640
|
-
};
|
|
1641
|
-
var labelFromKey = (ctx) => ({ name: titleCase(ctx.key) });
|
|
1642
|
-
var sliderWhenBounded = (ctx) => {
|
|
1643
|
-
if (ctx.leaf.kind !== "number") return null;
|
|
1644
|
-
const bounded = ctx.leaf.min !== void 0 && ctx.leaf.max !== void 0;
|
|
1645
|
-
return bounded ? { control: "slider" } : null;
|
|
1646
|
-
};
|
|
1647
|
-
var descriptionDefault = () => ({ description: "" });
|
|
1648
|
-
var builtinRules = [
|
|
1649
|
-
kindFromValue,
|
|
1650
|
-
labelFromKey,
|
|
1651
|
-
sliderWhenBounded,
|
|
1652
|
-
descriptionDefault
|
|
1653
|
-
];
|
|
1654
|
-
function applyRules(seed, ctx, rules) {
|
|
1655
|
-
const acc = { ...seed };
|
|
1656
|
-
for (const rule of rules) {
|
|
1657
|
-
const patch = rule({ ...ctx, leaf: acc });
|
|
1658
|
-
if (patch === null) continue;
|
|
1659
|
-
for (const [k, v] of Object.entries(patch)) {
|
|
1660
|
-
if (v !== void 0 && acc[k] === void 0) {
|
|
1661
|
-
acc[k] = v;
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
return acc;
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
// src/config/resolve.ts
|
|
1669
|
-
function defined(annotations) {
|
|
1670
|
-
const out = {};
|
|
1671
|
-
for (const [k, v] of Object.entries(annotations)) if (v !== void 0) out[k] = v;
|
|
1672
|
-
return out;
|
|
1673
|
-
}
|
|
1674
|
-
function resolveConfigSchema(schema, rules = []) {
|
|
1675
|
-
const sink = {
|
|
1676
|
-
sections: [],
|
|
1677
|
-
showIf: /* @__PURE__ */ new Map(),
|
|
1678
|
-
renderers: {},
|
|
1679
|
-
chain: [...rules, ...builtinRules]
|
|
1680
|
-
};
|
|
1681
|
-
const group = resolveShape(schema.nodes, "", "", sink);
|
|
1682
|
-
return { group, sections: sink.sections, showIf: sink.showIf, renderers: sink.renderers };
|
|
1683
|
-
}
|
|
1684
|
-
function resolveShape(shape, at, name, sink) {
|
|
1685
|
-
const children = {};
|
|
1686
|
-
const sections = /* @__PURE__ */ new Map();
|
|
1687
|
-
for (const [key, entry] of Object.entries(shape)) {
|
|
1688
|
-
const path = at === "" ? key : `${at}.${key}`;
|
|
1689
|
-
children[key] = resolveEntry(entry, key, path, sink);
|
|
1690
|
-
const { section, showIf: predicate } = entry.options;
|
|
1691
|
-
if (section !== void 0) {
|
|
1692
|
-
let spec = sections.get(section.label);
|
|
1693
|
-
if (!spec) {
|
|
1694
|
-
spec = { at, label: section.label, paths: [] };
|
|
1695
|
-
sections.set(section.label, spec);
|
|
1696
|
-
sink.sections.push(spec);
|
|
1697
|
-
}
|
|
1698
|
-
spec.paths.push(path);
|
|
1699
|
-
if (section.collapsed !== void 0) {
|
|
1700
|
-
spec.collapsed = (spec.collapsed ?? false) || section.collapsed;
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
if (predicate) sink.showIf.set(path, predicate);
|
|
1704
|
-
if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;
|
|
1705
|
-
}
|
|
1706
|
-
return { name, children };
|
|
1707
|
-
}
|
|
1708
|
-
function resolveEntry(entry, key, path, sink) {
|
|
1709
|
-
if (isConfigBranch(entry)) {
|
|
1710
|
-
const group = resolveShape(
|
|
1711
|
-
entry.children,
|
|
1712
|
-
path,
|
|
1713
|
-
entry.annotations.name ?? titleCase(key),
|
|
1714
|
-
sink
|
|
1715
|
-
);
|
|
1716
|
-
return entry.annotations.description === void 0 ? group : { ...group, description: entry.annotations.description };
|
|
1717
|
-
}
|
|
1718
|
-
const seed = defined({
|
|
1719
|
-
...entry.annotations,
|
|
1720
|
-
...entry.kind === null ? {} : { kind: entry.kind }
|
|
1721
|
-
});
|
|
1722
|
-
const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);
|
|
1723
|
-
return { ...patch, default: entry.default };
|
|
1724
|
-
}
|
|
1725
|
-
var LabContext = createContext(null);
|
|
1726
|
-
function useLabContext() {
|
|
1727
|
-
const ctx = useContext(LabContext);
|
|
1728
|
-
if (ctx === null) {
|
|
1729
|
-
throw new Error("useLabContext must be used inside <Lab>");
|
|
1730
|
-
}
|
|
1731
|
-
return ctx;
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
// src/config/useConfigSchema.ts
|
|
1735
|
-
var NO_RULES = [];
|
|
1736
|
-
function useConfigSchema(instrument) {
|
|
1737
|
-
const lab = useContext(LabContext);
|
|
1738
|
-
const rules = lab?.configRules ?? NO_RULES;
|
|
1739
|
-
return useMemo(
|
|
1740
|
-
() => instrument.config ? resolveConfigSchema(instrument.config, rules) : fromConfigFields(instrument.configSchema?.() ?? []),
|
|
1741
|
-
[instrument, rules]
|
|
1742
|
-
);
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
911
|
// src/instrument/defineInstrument.ts
|
|
1746
912
|
function defineInstrument(spec) {
|
|
1747
913
|
if (spec.config && !("defaultConfig" in spec && spec.defaultConfig)) {
|
|
@@ -1799,957 +965,207 @@ function validateConfigSchema(fields) {
|
|
|
1799
965
|
}
|
|
1800
966
|
return { valid: errors.length === 0, errors };
|
|
1801
967
|
}
|
|
968
|
+
var AnnotationPreloadContext = createContext(null);
|
|
1802
969
|
|
|
1803
|
-
//
|
|
1804
|
-
var
|
|
1805
|
-
"
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
"value": "#25272c"
|
|
1838
|
-
},
|
|
1839
|
-
"gray-800": {
|
|
1840
|
-
"type": "color",
|
|
1841
|
-
"value": "#181a1e"
|
|
1842
|
-
},
|
|
1843
|
-
"gray-900": {
|
|
1844
|
-
"type": "color",
|
|
1845
|
-
"value": "#0e0f12"
|
|
1846
|
-
},
|
|
1847
|
-
"accent-soft": {
|
|
1848
|
-
"type": "color",
|
|
1849
|
-
"value": "#1d1454"
|
|
1850
|
-
},
|
|
1851
|
-
"accent-base": {
|
|
1852
|
-
"type": "color",
|
|
1853
|
-
"value": "#2e1f7a",
|
|
1854
|
-
"description": "Midnight blue-violet. Deep and saturated; anchors primary actions on light and dark surfaces alike."
|
|
1855
|
-
},
|
|
1856
|
-
"accent-strong": {
|
|
1857
|
-
"type": "color",
|
|
1858
|
-
"value": "#5841b8"
|
|
1859
|
-
},
|
|
1860
|
-
"danger-base": {
|
|
1861
|
-
"type": "color",
|
|
1862
|
-
"value": "#d94a3f"
|
|
1863
|
-
},
|
|
1864
|
-
"warning-base": {
|
|
1865
|
-
"type": "color",
|
|
1866
|
-
"value": "#d99a3f"
|
|
1867
|
-
},
|
|
1868
|
-
"success-base": {
|
|
1869
|
-
"type": "color",
|
|
1870
|
-
"value": "#2ec27e"
|
|
1871
|
-
},
|
|
1872
|
-
"fg-on-accent": {
|
|
1873
|
-
"type": "color",
|
|
1874
|
-
"value": "{color.gray-50}"
|
|
1875
|
-
},
|
|
1876
|
-
"line-subtle": {
|
|
1877
|
-
"type": "color",
|
|
1878
|
-
"value": "{color.fg}",
|
|
1879
|
-
"alpha": 0.1,
|
|
1880
|
-
"description": "Barely-there separators (table rows)."
|
|
1881
|
-
},
|
|
1882
|
-
"line": {
|
|
1883
|
-
"type": "color",
|
|
1884
|
-
"value": "{color.fg}",
|
|
1885
|
-
"alpha": 0.2,
|
|
1886
|
-
"description": "Primary gridlines on sunken surfaces."
|
|
1887
|
-
},
|
|
1888
|
-
"line-strong": {
|
|
1889
|
-
"type": "color",
|
|
1890
|
-
"value": "{color.fg}",
|
|
1891
|
-
"alpha": 0.4,
|
|
1892
|
-
"description": "Axes and emphasized dividers."
|
|
1893
|
-
},
|
|
1894
|
-
"curve-color": {
|
|
1895
|
-
"type": "color",
|
|
1896
|
-
"value": "{color.accent-strong}",
|
|
1897
|
-
"description": "Drawn data curves. Routes through the bright accent because accent-base is too dim on sunken surfaces."
|
|
1898
|
-
},
|
|
1899
|
-
"accent": {
|
|
1900
|
-
"type": "color",
|
|
1901
|
-
"value": "{color.accent-base}"
|
|
1902
|
-
},
|
|
1903
|
-
"accent-hover": {
|
|
1904
|
-
"type": "color",
|
|
1905
|
-
"value": "{color.accent-strong}"
|
|
1906
|
-
},
|
|
1907
|
-
"danger": {
|
|
1908
|
-
"type": "color",
|
|
1909
|
-
"value": "{color.danger-base}"
|
|
1910
|
-
},
|
|
1911
|
-
"warning": {
|
|
1912
|
-
"type": "color",
|
|
1913
|
-
"value": "{color.warning-base}"
|
|
1914
|
-
},
|
|
1915
|
-
"success": {
|
|
1916
|
-
"type": "color",
|
|
1917
|
-
"value": "{color.success-base}"
|
|
1918
|
-
},
|
|
1919
|
-
"focus-ring": {
|
|
1920
|
-
"type": "color",
|
|
1921
|
-
"value": "{color.accent-strong}"
|
|
1922
|
-
},
|
|
1923
|
-
"glass-tint": {
|
|
1924
|
-
"type": "color",
|
|
1925
|
-
"value": "{color.accent}",
|
|
1926
|
-
"description": "Frosted-glass tint. Components compose it as color-mix(in srgb, var(--wzl-glass-tint) 78%, transparent) with backdrop-filter: blur(3px)."
|
|
1927
|
-
},
|
|
1928
|
-
"surface-hover": {
|
|
1929
|
-
"type": "color",
|
|
1930
|
-
"value": "{color.fg}",
|
|
1931
|
-
"alpha": 0.1,
|
|
1932
|
-
"description": "Hover wash over a raised surface. Same formula as line-subtle, different job \u2014 do not collapse them."
|
|
1933
|
-
},
|
|
1934
|
-
"surface-pressed": {
|
|
1935
|
-
"type": "color",
|
|
1936
|
-
"value": "{color.fg}",
|
|
1937
|
-
"alpha": 0.18,
|
|
1938
|
-
"description": "Pressed wash over a raised surface."
|
|
1939
|
-
},
|
|
1940
|
-
"swatch-green": {
|
|
1941
|
-
"type": "color",
|
|
1942
|
-
"value": "#2fdd18",
|
|
1943
|
-
"description": "First of ten categorical spot colors for plotting, status and data viz. Ordered \u2014 take them in sequence. Mode-invariant on purpose: a series keeps its color when the mode flips."
|
|
1944
|
-
},
|
|
1945
|
-
"swatch-pink": {
|
|
1946
|
-
"type": "color",
|
|
1947
|
-
"value": "#ff5885"
|
|
1948
|
-
},
|
|
1949
|
-
"swatch-cyan": {
|
|
1950
|
-
"type": "color",
|
|
1951
|
-
"value": "#00dfff"
|
|
1952
|
-
},
|
|
1953
|
-
"swatch-gold": {
|
|
1954
|
-
"type": "color",
|
|
1955
|
-
"value": "#dcb700"
|
|
1956
|
-
},
|
|
1957
|
-
"swatch-amber": {
|
|
1958
|
-
"type": "color",
|
|
1959
|
-
"value": "#ff8c00"
|
|
1960
|
-
},
|
|
1961
|
-
"swatch-violet": {
|
|
1962
|
-
"type": "color",
|
|
1963
|
-
"value": "#a497ff"
|
|
1964
|
-
},
|
|
1965
|
-
"swatch-mint": {
|
|
1966
|
-
"type": "color",
|
|
1967
|
-
"value": "#00e7af"
|
|
1968
|
-
},
|
|
1969
|
-
"swatch-sky": {
|
|
1970
|
-
"type": "color",
|
|
1971
|
-
"value": "#00b8ff"
|
|
1972
|
-
},
|
|
1973
|
-
"swatch-orange": {
|
|
1974
|
-
"type": "color",
|
|
1975
|
-
"value": "#ff6b00"
|
|
1976
|
-
},
|
|
1977
|
-
"swatch-magenta": {
|
|
1978
|
-
"type": "color",
|
|
1979
|
-
"value": "#ff6eff"
|
|
1980
|
-
},
|
|
1981
|
-
"radius-sm": {
|
|
1982
|
-
"type": "dimension",
|
|
1983
|
-
"value": "3px"
|
|
1984
|
-
},
|
|
1985
|
-
"radius-md": {
|
|
1986
|
-
"type": "dimension",
|
|
1987
|
-
"value": "5px"
|
|
1988
|
-
},
|
|
1989
|
-
"radius-lg": {
|
|
1990
|
-
"type": "dimension",
|
|
1991
|
-
"value": "14px"
|
|
1992
|
-
},
|
|
1993
|
-
"radius-pill": {
|
|
1994
|
-
"type": "dimension",
|
|
1995
|
-
"value": "999px",
|
|
1996
|
-
"description": "Fully rounded ends \u2014 segmented controls, progress tracks."
|
|
1997
|
-
},
|
|
1998
|
-
"border-w": {
|
|
1999
|
-
"type": "dimension",
|
|
2000
|
-
"value": "1px"
|
|
2001
|
-
},
|
|
2002
|
-
"line-width": {
|
|
2003
|
-
"type": "dimension",
|
|
2004
|
-
"value": "2px",
|
|
2005
|
-
"description": "Structural lines \u2014 gridlines, dividers, axes."
|
|
2006
|
-
},
|
|
2007
|
-
"curve-width": {
|
|
2008
|
-
"type": "dimension",
|
|
2009
|
-
"value": "3px",
|
|
2010
|
-
"description": "Drawn data curves. Heavier than structural lines so data reads as primary content."
|
|
2011
|
-
},
|
|
2012
|
-
"tb-height": {
|
|
2013
|
-
"type": "dimension",
|
|
2014
|
-
"value": "28px",
|
|
2015
|
-
"description": "Height of a horizontal toolbar strip (ActionsBar, OptionsBar, ToolOptionsBar). Taller than control-h so the controls inside a strip are not flush with its edges."
|
|
2016
|
-
},
|
|
2017
|
-
"control-h": {
|
|
2018
|
-
"type": "dimension",
|
|
2019
|
-
"value": "24px",
|
|
2020
|
-
"description": "Height of an interactive control \u2014 button, input, select, segmented control. Distinct from tb-height, which sizes the strip a row of them sits in."
|
|
2021
|
-
},
|
|
2022
|
-
"slider-track-h": {
|
|
2023
|
-
"type": "dimension",
|
|
2024
|
-
"value": "4px",
|
|
2025
|
-
"description": "Height of a linear range track. The kit's single-thumb ranges all wear this; Slider's default 24px canvas track does not."
|
|
2026
|
-
},
|
|
2027
|
-
"slider-thumb-size": {
|
|
2028
|
-
"type": "dimension",
|
|
2029
|
-
"value": "8px",
|
|
2030
|
-
"description": "Diameter of a linear range thumb."
|
|
2031
|
-
},
|
|
2032
|
-
"slider-track-mix": {
|
|
2033
|
-
"type": "dimension",
|
|
2034
|
-
"value": "18%",
|
|
2035
|
-
"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."
|
|
2036
|
-
},
|
|
2037
|
-
"slider-thumb-mix": {
|
|
2038
|
-
"type": "dimension",
|
|
2039
|
-
"value": "70%",
|
|
2040
|
-
"description": "Accent proportion in a range thumb, as a color-mix percentage \u2014 not a color."
|
|
2041
|
-
},
|
|
2042
|
-
"field-pad-x": {
|
|
2043
|
-
"type": "dimension",
|
|
2044
|
-
"value": "8px",
|
|
2045
|
-
"description": "Horizontal padding inside a boxed field. Pairs with --wzl-field-h, which is a container override and has no :root default."
|
|
2046
|
-
},
|
|
2047
|
-
"glass-blur": {
|
|
2048
|
-
"type": "dimension",
|
|
2049
|
-
"value": "3px",
|
|
2050
|
-
"description": "Backdrop blur radius for frosted surfaces. Pairs with glass-tint."
|
|
2051
|
-
},
|
|
2052
|
-
"font-size-2xs": {
|
|
2053
|
-
"type": "dimension",
|
|
2054
|
-
"value": "9px",
|
|
2055
|
-
"description": "Shortcut keys and index badges. The floor for chrome text."
|
|
2056
|
-
},
|
|
2057
|
-
"font-size-xs": {
|
|
2058
|
-
"type": "dimension",
|
|
2059
|
-
"value": "10px",
|
|
2060
|
-
"description": "Tool-button labels under an icon."
|
|
2061
|
-
},
|
|
2062
|
-
"font-size-sm": {
|
|
2063
|
-
"type": "dimension",
|
|
2064
|
-
"value": "11px",
|
|
2065
|
-
"description": "Chrome labels, status readouts, section headings."
|
|
2066
|
-
},
|
|
2067
|
-
"font-size": {
|
|
2068
|
-
"type": "dimension",
|
|
2069
|
-
"value": "13px",
|
|
2070
|
-
"description": "Controls and body text."
|
|
2071
|
-
},
|
|
2072
|
-
"font-size-lg": {
|
|
2073
|
-
"type": "dimension",
|
|
2074
|
-
"value": "16px",
|
|
2075
|
-
"description": "Panel and dialog titles."
|
|
2076
|
-
},
|
|
2077
|
-
"font-size-xl": {
|
|
2078
|
-
"type": "dimension",
|
|
2079
|
-
"value": "20px",
|
|
2080
|
-
"description": "The largest chrome text \u2014 a lab or app title."
|
|
2081
|
-
},
|
|
2082
|
-
"space-xs": {
|
|
2083
|
-
"type": "dimension",
|
|
2084
|
-
"value": "4px"
|
|
2085
|
-
},
|
|
2086
|
-
"space-sm": {
|
|
2087
|
-
"type": "dimension",
|
|
2088
|
-
"value": "8px"
|
|
2089
|
-
},
|
|
2090
|
-
"space-md": {
|
|
2091
|
-
"type": "dimension",
|
|
2092
|
-
"value": "12px"
|
|
2093
|
-
},
|
|
2094
|
-
"space-lg": {
|
|
2095
|
-
"type": "dimension",
|
|
2096
|
-
"value": "16px"
|
|
2097
|
-
},
|
|
2098
|
-
"tracking-none": {
|
|
2099
|
-
"type": "dimension",
|
|
2100
|
-
"value": "0"
|
|
2101
|
-
},
|
|
2102
|
-
"tracking-wide": {
|
|
2103
|
-
"type": "dimension",
|
|
2104
|
-
"value": "0.06em",
|
|
2105
|
-
"description": "Uppercase section headings."
|
|
2106
|
-
},
|
|
2107
|
-
"tracking-wider": {
|
|
2108
|
-
"type": "dimension",
|
|
2109
|
-
"value": "0.08em",
|
|
2110
|
-
"description": "Uppercase at 11px and below."
|
|
2111
|
-
},
|
|
2112
|
-
"z-toolbar": {
|
|
2113
|
-
"type": "number",
|
|
2114
|
-
"value": 10
|
|
2115
|
-
},
|
|
2116
|
-
"z-overlay": {
|
|
2117
|
-
"type": "number",
|
|
2118
|
-
"value": 20
|
|
2119
|
-
},
|
|
2120
|
-
"z-modal": {
|
|
2121
|
-
"type": "number",
|
|
2122
|
-
"value": 30
|
|
2123
|
-
},
|
|
2124
|
-
"leading-tight": {
|
|
2125
|
-
"type": "number",
|
|
2126
|
-
"value": 1,
|
|
2127
|
-
"description": "Single-line controls; the box sets the height."
|
|
2128
|
-
},
|
|
2129
|
-
"leading-snug": {
|
|
2130
|
-
"type": "number",
|
|
2131
|
-
"value": 1.2,
|
|
2132
|
-
"description": "Headings and two-line labels."
|
|
2133
|
-
},
|
|
2134
|
-
"leading": {
|
|
2135
|
-
"type": "number",
|
|
2136
|
-
"value": 1.4,
|
|
2137
|
-
"description": "Body and anything that wraps."
|
|
2138
|
-
},
|
|
2139
|
-
"backdrop": {
|
|
2140
|
-
"type": "gradient",
|
|
2141
|
-
"value": "none",
|
|
2142
|
-
"description": "Layered backdrop fill for a page or lab root. `none` here; a theme with a signature background sets it."
|
|
2143
|
-
},
|
|
2144
|
-
"motion-fast": {
|
|
2145
|
-
"type": "duration",
|
|
2146
|
-
"value": "120ms",
|
|
2147
|
-
"description": "Micro-interactions \u2014 hover, focus, pressed."
|
|
2148
|
-
},
|
|
2149
|
-
"motion-medium": {
|
|
2150
|
-
"type": "duration",
|
|
2151
|
-
"value": "240ms",
|
|
2152
|
-
"description": "Entrances and exits \u2014 toasts, popovers, panels."
|
|
2153
|
-
},
|
|
2154
|
-
"ease-in-cubic": {
|
|
2155
|
-
"type": "cubicBezier",
|
|
2156
|
-
"value": [
|
|
2157
|
-
0.32,
|
|
2158
|
-
0,
|
|
2159
|
-
0.67,
|
|
2160
|
-
0
|
|
2161
|
-
]
|
|
2162
|
-
},
|
|
2163
|
-
"ease-out-cubic": {
|
|
2164
|
-
"type": "cubicBezier",
|
|
2165
|
-
"value": [
|
|
2166
|
-
0.33,
|
|
2167
|
-
1,
|
|
2168
|
-
0.68,
|
|
2169
|
-
1
|
|
2170
|
-
]
|
|
2171
|
-
},
|
|
2172
|
-
"ease-in-out-cubic": {
|
|
2173
|
-
"type": "cubicBezier",
|
|
2174
|
-
"value": [
|
|
2175
|
-
0.65,
|
|
2176
|
-
0,
|
|
2177
|
-
0.35,
|
|
2178
|
-
1
|
|
2179
|
-
]
|
|
2180
|
-
},
|
|
2181
|
-
"ease-out-back": {
|
|
2182
|
-
"type": "cubicBezier",
|
|
2183
|
-
"value": [
|
|
2184
|
-
0.34,
|
|
2185
|
-
1.56,
|
|
2186
|
-
0.64,
|
|
2187
|
-
1
|
|
2188
|
-
]
|
|
2189
|
-
},
|
|
2190
|
-
"font-ui": {
|
|
2191
|
-
"type": "fontFamily",
|
|
2192
|
-
"value": [
|
|
2193
|
-
"Oswald",
|
|
2194
|
-
"Helvetica Neue Condensed",
|
|
2195
|
-
"Arial Narrow",
|
|
2196
|
-
"system-ui",
|
|
2197
|
-
"sans-serif"
|
|
2198
|
-
],
|
|
2199
|
-
"description": "Condensed UI/display face. The heavier cuts get blocky at any size, so the weight tokens stay in the 200-400 range."
|
|
2200
|
-
},
|
|
2201
|
-
"font-display": {
|
|
2202
|
-
"type": "fontFamily",
|
|
2203
|
-
"value": [
|
|
2204
|
-
"Oswald",
|
|
2205
|
-
"Helvetica Neue Condensed",
|
|
2206
|
-
"Arial Narrow",
|
|
2207
|
-
"system-ui",
|
|
2208
|
-
"sans-serif"
|
|
2209
|
-
]
|
|
2210
|
-
},
|
|
2211
|
-
"font-body": {
|
|
2212
|
-
"type": "fontFamily",
|
|
2213
|
-
"value": [
|
|
2214
|
-
"Inter",
|
|
2215
|
-
"system-ui",
|
|
2216
|
-
"-apple-system",
|
|
2217
|
-
"Segoe UI",
|
|
2218
|
-
"sans-serif"
|
|
2219
|
-
]
|
|
2220
|
-
},
|
|
2221
|
-
"font-mono": {
|
|
2222
|
-
"type": "fontFamily",
|
|
2223
|
-
"value": [
|
|
2224
|
-
"ui-monospace",
|
|
2225
|
-
"SFMono-Regular",
|
|
2226
|
-
"Menlo",
|
|
2227
|
-
"Consolas",
|
|
2228
|
-
"monospace"
|
|
2229
|
-
]
|
|
2230
|
-
},
|
|
2231
|
-
"font-weight-light": {
|
|
2232
|
-
"type": "fontWeight",
|
|
2233
|
-
"value": 200
|
|
2234
|
-
},
|
|
2235
|
-
"font-weight-normal": {
|
|
2236
|
-
"type": "fontWeight",
|
|
2237
|
-
"value": 300
|
|
2238
|
-
},
|
|
2239
|
-
"font-weight-medium": {
|
|
2240
|
-
"type": "fontWeight",
|
|
2241
|
-
"value": 350
|
|
2242
|
-
},
|
|
2243
|
-
"font-weight-bold": {
|
|
2244
|
-
"type": "fontWeight",
|
|
2245
|
-
"value": 400
|
|
970
|
+
// src/theme/interstellar.theme.json
|
|
971
|
+
var interstellar_theme_default = {
|
|
972
|
+
name: "interstellar",
|
|
973
|
+
extends: "weasel",
|
|
974
|
+
pins: {
|
|
975
|
+
"radius-md": {
|
|
976
|
+
value: "6px",
|
|
977
|
+
type: "dimension"
|
|
978
|
+
},
|
|
979
|
+
"glass-blur": {
|
|
980
|
+
value: "12px",
|
|
981
|
+
type: "dimension"
|
|
982
|
+
},
|
|
983
|
+
"font-weight-light": {
|
|
984
|
+
value: 300,
|
|
985
|
+
type: "fontWeight"
|
|
986
|
+
},
|
|
987
|
+
"font-weight-medium": {
|
|
988
|
+
value: 500,
|
|
989
|
+
type: "fontWeight"
|
|
990
|
+
},
|
|
991
|
+
"font-weight-bold": {
|
|
992
|
+
value: 700,
|
|
993
|
+
type: "fontWeight"
|
|
994
|
+
},
|
|
995
|
+
surface: {
|
|
996
|
+
by: "mode",
|
|
997
|
+
dark: {
|
|
998
|
+
value: "#0a0a14",
|
|
999
|
+
type: "color"
|
|
1000
|
+
},
|
|
1001
|
+
light: {
|
|
1002
|
+
value: "#fafaf7",
|
|
1003
|
+
type: "color"
|
|
2246
1004
|
}
|
|
2247
1005
|
},
|
|
2248
|
-
"
|
|
2249
|
-
"
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
},
|
|
2258
|
-
"surface-sunken": {
|
|
2259
|
-
"type": "color",
|
|
2260
|
-
"value": "{color.gray-900}"
|
|
2261
|
-
},
|
|
2262
|
-
"fg": {
|
|
2263
|
-
"type": "color",
|
|
2264
|
-
"value": "{color.gray-100}"
|
|
2265
|
-
},
|
|
2266
|
-
"fg-muted": {
|
|
2267
|
-
"type": "color",
|
|
2268
|
-
"value": "{color.gray-300}"
|
|
2269
|
-
},
|
|
2270
|
-
"fg-subtle": {
|
|
2271
|
-
"type": "color",
|
|
2272
|
-
"value": "{color.gray-400}"
|
|
2273
|
-
},
|
|
2274
|
-
"border": {
|
|
2275
|
-
"type": "color",
|
|
2276
|
-
"value": "{color.gray-700}"
|
|
2277
|
-
},
|
|
2278
|
-
"border-strong": {
|
|
2279
|
-
"type": "color",
|
|
2280
|
-
"value": "{color.gray-600}"
|
|
2281
|
-
},
|
|
2282
|
-
"accent-fg": {
|
|
2283
|
-
"type": "color",
|
|
2284
|
-
"value": "{color.accent-strong}",
|
|
2285
|
-
"description": "Accent for foreground use \u2014 text, icons, selected labels drawn on a surface. accent is a fill; the midnight base is too dim to read as text on dark surfaces."
|
|
2286
|
-
},
|
|
2287
|
-
"fg-inverse": {
|
|
2288
|
-
"type": "color",
|
|
2289
|
-
"value": "{color.gray-900}",
|
|
2290
|
-
"description": "Text drawn on a filled foreground-colored element (e.g. a slider thumb). Flips with the mode, so it is a semantic, not an alias to a primitive."
|
|
2291
|
-
},
|
|
2292
|
-
"shadow": {
|
|
2293
|
-
"type": "color",
|
|
2294
|
-
"value": "rgba(0, 0, 0, 0.6)",
|
|
2295
|
-
"description": "Elevation. Always darker than any surface \u2014 never derived from fg, which is near-white on dark."
|
|
2296
|
-
},
|
|
2297
|
-
"border-raised": {
|
|
2298
|
-
"type": "color",
|
|
2299
|
-
"value": "{color.gray-400}",
|
|
2300
|
-
"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."
|
|
2301
|
-
}
|
|
2302
|
-
},
|
|
2303
|
-
"light": {
|
|
2304
|
-
"surface": {
|
|
2305
|
-
"type": "color",
|
|
2306
|
-
"value": "{color.gray-50}"
|
|
2307
|
-
},
|
|
2308
|
-
"surface-raised": {
|
|
2309
|
-
"type": "color",
|
|
2310
|
-
"value": "{color.gray-100}"
|
|
2311
|
-
},
|
|
2312
|
-
"surface-sunken": {
|
|
2313
|
-
"type": "color",
|
|
2314
|
-
"value": "{color.gray-200}"
|
|
2315
|
-
},
|
|
2316
|
-
"fg": {
|
|
2317
|
-
"type": "color",
|
|
2318
|
-
"value": "{color.gray-900}"
|
|
2319
|
-
},
|
|
2320
|
-
"fg-muted": {
|
|
2321
|
-
"type": "color",
|
|
2322
|
-
"value": "{color.gray-600}"
|
|
2323
|
-
},
|
|
2324
|
-
"fg-subtle": {
|
|
2325
|
-
"type": "color",
|
|
2326
|
-
"value": "{color.gray-500}"
|
|
2327
|
-
},
|
|
2328
|
-
"border": {
|
|
2329
|
-
"type": "color",
|
|
2330
|
-
"value": "{color.gray-200}"
|
|
2331
|
-
},
|
|
2332
|
-
"border-strong": {
|
|
2333
|
-
"type": "color",
|
|
2334
|
-
"value": "{color.gray-300}"
|
|
2335
|
-
},
|
|
2336
|
-
"accent-fg": {
|
|
2337
|
-
"type": "color",
|
|
2338
|
-
"value": "{color.accent-base}",
|
|
2339
|
-
"description": "Flips to the deep base in light mode, where it is the legible one."
|
|
2340
|
-
},
|
|
2341
|
-
"fg-inverse": {
|
|
2342
|
-
"type": "color",
|
|
2343
|
-
"value": "{color.gray-50}",
|
|
2344
|
-
"description": "Flips with the mode \u2014 see the dark layer."
|
|
2345
|
-
},
|
|
2346
|
-
"shadow": {
|
|
2347
|
-
"type": "color",
|
|
2348
|
-
"value": "rgba(0, 0, 0, 0.18)"
|
|
2349
|
-
},
|
|
2350
|
-
"border-raised": {
|
|
2351
|
-
"type": "color",
|
|
2352
|
-
"value": "{color.gray-400}"
|
|
2353
|
-
}
|
|
1006
|
+
"surface-raised": {
|
|
1007
|
+
by: "mode",
|
|
1008
|
+
dark: {
|
|
1009
|
+
value: "rgba(20, 20, 36, 0.55)",
|
|
1010
|
+
type: "color"
|
|
1011
|
+
},
|
|
1012
|
+
light: {
|
|
1013
|
+
value: "#ffffff",
|
|
1014
|
+
type: "color"
|
|
2354
1015
|
}
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
function hexToRgba(hex, alpha) {
|
|
2366
|
-
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
|
|
2367
|
-
if (!m) throw new Error(`Expected a hex color, got "${hex}"`);
|
|
2368
|
-
const body = m[1];
|
|
2369
|
-
const full = body.length === 3 ? body.replace(/./g, (c2) => c2 + c2) : body;
|
|
2370
|
-
const r2 = Number.parseInt(full.slice(0, 2), 16);
|
|
2371
|
-
const g2 = Number.parseInt(full.slice(2, 4), 16);
|
|
2372
|
-
const b = Number.parseInt(full.slice(4, 6), 16);
|
|
2373
|
-
return `rgba(${r2}, ${g2}, ${b}, ${alpha})`;
|
|
2374
|
-
}
|
|
2375
|
-
var REF = /^\{([^}]+)\}$/;
|
|
2376
|
-
function refTarget(value) {
|
|
2377
|
-
if (typeof value !== "string") return null;
|
|
2378
|
-
const m = REF.exec(value.trim());
|
|
2379
|
-
if (!m) return null;
|
|
2380
|
-
const path = m[1];
|
|
2381
|
-
const dot = path.indexOf(".");
|
|
2382
|
-
return dot === -1 ? path : path.slice(dot + 1);
|
|
2383
|
-
}
|
|
2384
|
-
function fontStack(value) {
|
|
2385
|
-
return value.map((f2) => typeof f2 === "string" && /\s/.test(f2) ? `'${f2}'` : String(f2)).join(", ");
|
|
2386
|
-
}
|
|
2387
|
-
function serialize(type, value) {
|
|
2388
|
-
if (Array.isArray(value)) {
|
|
2389
|
-
if (type === "cubicBezier") return `cubic-bezier(${value.join(", ")})`;
|
|
2390
|
-
if (type === "fontFamily") return fontStack(value);
|
|
2391
|
-
return value.join(", ");
|
|
2392
|
-
}
|
|
2393
|
-
return String(value);
|
|
2394
|
-
}
|
|
2395
|
-
function resolveTokens(tokens) {
|
|
2396
|
-
const out = {};
|
|
2397
|
-
const inProgress = /* @__PURE__ */ new Set();
|
|
2398
|
-
const resolveOne = (name) => {
|
|
2399
|
-
const cached = out[name];
|
|
2400
|
-
if (cached !== void 0) return cached;
|
|
2401
|
-
const token = tokens[name];
|
|
2402
|
-
if (!token) throw new Error(`Unknown token "${name}"`);
|
|
2403
|
-
if (inProgress.has(name)) {
|
|
2404
|
-
throw new Error(`Reference cycle at token "${name}" (${[...inProgress].join(" \u2192 ")})`);
|
|
2405
|
-
}
|
|
2406
|
-
inProgress.add(name);
|
|
2407
|
-
const target = refTarget(token.value);
|
|
2408
|
-
let value;
|
|
2409
|
-
if (target === null) {
|
|
2410
|
-
value = serialize(token.type, token.value);
|
|
2411
|
-
} else if (!tokens[target]) {
|
|
2412
|
-
throw new Error(`Token "${name}" references "${target}", which is not defined`);
|
|
2413
|
-
} else {
|
|
2414
|
-
value = resolveOne(target);
|
|
2415
|
-
}
|
|
2416
|
-
if (token.alpha !== void 0) value = hexToRgba(value, token.alpha);
|
|
2417
|
-
inProgress.delete(name);
|
|
2418
|
-
out[name] = value;
|
|
2419
|
-
return value;
|
|
2420
|
-
};
|
|
2421
|
-
for (const name of Object.keys(tokens)) resolveOne(name);
|
|
2422
|
-
return out;
|
|
2423
|
-
}
|
|
2424
|
-
function chain(theme) {
|
|
2425
|
-
const out = [];
|
|
2426
|
-
for (let t = theme; t; t = t.extends) out.unshift(t);
|
|
2427
|
-
return out;
|
|
2428
|
-
}
|
|
2429
|
-
function resolveTheme(theme, mode) {
|
|
2430
|
-
const themes = chain(theme);
|
|
2431
|
-
const effectiveMode = themes.some((t) => mode in t.modes) ? mode : theme.defaultMode;
|
|
2432
|
-
let merged = {};
|
|
2433
|
-
for (const t of themes) {
|
|
2434
|
-
merged = { ...merged, ...t.tokens, ...t.modes[effectiveMode] ?? {} };
|
|
2435
|
-
}
|
|
2436
|
-
const resolved = resolveTokens(merged);
|
|
2437
|
-
const out = {};
|
|
2438
|
-
for (const [name, value] of Object.entries(resolved)) out[`--wzl-${name}`] = value;
|
|
2439
|
-
return out;
|
|
2440
|
-
}
|
|
2441
|
-
var STYLE_ID = "wzl-themes";
|
|
2442
|
-
var emitted = /* @__PURE__ */ new Map();
|
|
2443
|
-
var sheet = null;
|
|
2444
|
-
var styleEl = null;
|
|
2445
|
-
function canAdopt() {
|
|
2446
|
-
return typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype && Array.isArray(document.adoptedStyleSheets);
|
|
2447
|
-
}
|
|
2448
|
-
function flushRules() {
|
|
2449
|
-
const css = [...emitted.values()].join("\n");
|
|
2450
|
-
if (canAdopt()) {
|
|
2451
|
-
if (!sheet) {
|
|
2452
|
-
sheet = new CSSStyleSheet();
|
|
2453
|
-
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
2454
|
-
}
|
|
2455
|
-
sheet.replaceSync(css);
|
|
2456
|
-
return;
|
|
2457
|
-
}
|
|
2458
|
-
if (!styleEl) {
|
|
2459
|
-
styleEl = document.createElement("style");
|
|
2460
|
-
styleEl.id = STYLE_ID;
|
|
2461
|
-
document.head.appendChild(styleEl);
|
|
2462
|
-
}
|
|
2463
|
-
styleEl.textContent = css;
|
|
2464
|
-
}
|
|
2465
|
-
function applyTheme(el, theme, mode) {
|
|
2466
|
-
if (typeof document === "undefined") return;
|
|
2467
|
-
const key = `${theme.name}::${mode}`;
|
|
2468
|
-
const resolved = resolveTheme(theme, mode);
|
|
2469
|
-
const body = Object.entries(resolved).map(([name, value]) => `${name}: ${value};`).join(" ");
|
|
2470
|
-
const rule = `[data-wzl-theme='${theme.name}'][data-wzl-mode='${mode}'] { ${body} }`;
|
|
2471
|
-
if (emitted.get(key) !== rule) {
|
|
2472
|
-
emitted.set(key, rule);
|
|
2473
|
-
flushRules();
|
|
2474
|
-
}
|
|
2475
|
-
el.setAttribute("data-wzl-theme", theme.name);
|
|
2476
|
-
el.setAttribute("data-wzl-mode", mode);
|
|
2477
|
-
}
|
|
2478
|
-
var ThemeContext = createContext(null);
|
|
2479
|
-
function ThemeProvider({
|
|
2480
|
-
theme = weaselTheme,
|
|
2481
|
-
mode,
|
|
2482
|
-
className,
|
|
2483
|
-
style,
|
|
2484
|
-
children
|
|
2485
|
-
}) {
|
|
2486
|
-
const ref = useRef(null);
|
|
2487
|
-
const effectiveMode = mode ?? theme.defaultMode;
|
|
2488
|
-
const value = useMemo(
|
|
2489
|
-
() => ({ theme, mode: effectiveMode, resolved: resolveTheme(theme, effectiveMode) }),
|
|
2490
|
-
[theme, effectiveMode]
|
|
2491
|
-
);
|
|
2492
|
-
useLayoutEffect(() => {
|
|
2493
|
-
if (ref.current) applyTheme(ref.current, theme, effectiveMode);
|
|
2494
|
-
}, [theme, effectiveMode]);
|
|
2495
|
-
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children: /* @__PURE__ */ jsx(
|
|
2496
|
-
"div",
|
|
2497
|
-
{
|
|
2498
|
-
ref,
|
|
2499
|
-
className,
|
|
2500
|
-
style,
|
|
2501
|
-
"data-wzl-theme": theme.name,
|
|
2502
|
-
"data-wzl-mode": effectiveMode,
|
|
2503
|
-
children
|
|
2504
|
-
}
|
|
2505
|
-
) });
|
|
2506
|
-
}
|
|
2507
|
-
function useThemeOptional() {
|
|
2508
|
-
return useContext(ThemeContext);
|
|
2509
|
-
}
|
|
2510
|
-
|
|
2511
|
-
// src/instrument/serializers.ts
|
|
2512
|
-
function configDefaultsOf(instruments) {
|
|
2513
|
-
const out = {};
|
|
2514
|
-
for (const instrument of instruments) out[instrument.name] = () => instrument.defaultConfig();
|
|
2515
|
-
return out;
|
|
2516
|
-
}
|
|
2517
|
-
function serializersOf(instruments) {
|
|
2518
|
-
const out = {};
|
|
2519
|
-
for (const instrument of instruments) {
|
|
2520
|
-
const { name, serialize: serialize2, deserialize } = instrument;
|
|
2521
|
-
if (!serialize2 && !deserialize) continue;
|
|
2522
|
-
out[name] = {
|
|
2523
|
-
...serialize2 ? { serialize: (state) => serialize2(state) } : {},
|
|
2524
|
-
...deserialize ? { deserialize: (data, config) => deserialize(data, config) } : {}
|
|
2525
|
-
};
|
|
2526
|
-
}
|
|
2527
|
-
return out;
|
|
2528
|
-
}
|
|
2529
|
-
|
|
2530
|
-
// ../theme/dist/index.js
|
|
2531
|
-
var ALPHA_EXT = "com.weasel.alpha";
|
|
2532
|
-
var isToken = (v) => typeof v === "object" && v !== null && "$value" in v;
|
|
2533
|
-
function flattenTokens(doc) {
|
|
2534
|
-
const out = {};
|
|
2535
|
-
for (const [groupName, group] of Object.entries(doc)) {
|
|
2536
|
-
if (groupName.startsWith("$")) continue;
|
|
2537
|
-
if (typeof group !== "object" || group === null) continue;
|
|
2538
|
-
const g2 = group;
|
|
2539
|
-
const groupType = g2.$type;
|
|
2540
|
-
for (const [leaf, node] of Object.entries(g2)) {
|
|
2541
|
-
if (leaf.startsWith("$")) continue;
|
|
2542
|
-
if (!isToken(node)) continue;
|
|
2543
|
-
const type = node.$type ?? groupType;
|
|
2544
|
-
if (!type) {
|
|
2545
|
-
throw new Error(`Token "${leaf}" has no $type and its group "${groupName}" declares none`);
|
|
1016
|
+
},
|
|
1017
|
+
"surface-sunken": {
|
|
1018
|
+
by: "mode",
|
|
1019
|
+
dark: {
|
|
1020
|
+
value: "#02020a",
|
|
1021
|
+
type: "color"
|
|
1022
|
+
},
|
|
1023
|
+
light: {
|
|
1024
|
+
value: "#f0ebe2",
|
|
1025
|
+
type: "color"
|
|
2546
1026
|
}
|
|
2547
|
-
|
|
2548
|
-
|
|
1027
|
+
},
|
|
1028
|
+
border: {
|
|
1029
|
+
by: "mode",
|
|
1030
|
+
dark: {
|
|
1031
|
+
value: "rgba(255, 255, 255, 0.14)",
|
|
1032
|
+
type: "color"
|
|
1033
|
+
},
|
|
1034
|
+
light: {
|
|
1035
|
+
value: "#c9bba5",
|
|
1036
|
+
type: "color"
|
|
2549
1037
|
}
|
|
2550
|
-
const alphaRaw = node.$extensions?.[ALPHA_EXT];
|
|
2551
|
-
const token = {
|
|
2552
|
-
type,
|
|
2553
|
-
value: node.$value,
|
|
2554
|
-
alpha: typeof alphaRaw === "number" ? alphaRaw : void 0,
|
|
2555
|
-
description: node.$description
|
|
2556
|
-
};
|
|
2557
|
-
out[leaf] = token;
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
return out;
|
|
2561
|
-
}
|
|
2562
|
-
function loadDTCG(doc) {
|
|
2563
|
-
if (typeof doc.name !== "string" || doc.name === "") {
|
|
2564
|
-
throw new Error('DTCG document needs a string "name"');
|
|
2565
|
-
}
|
|
2566
|
-
const modes = {};
|
|
2567
|
-
for (const [mode, body] of Object.entries(doc.modes ?? {})) {
|
|
2568
|
-
modes[mode] = flattenTokens(body);
|
|
2569
|
-
}
|
|
2570
|
-
const base = doc.extends === void 0 ? weaselTheme : doc.extends;
|
|
2571
|
-
return {
|
|
2572
|
-
name: doc.name,
|
|
2573
|
-
extends: base,
|
|
2574
|
-
defaultMode: typeof doc.defaultMode === "string" ? doc.defaultMode : base?.defaultMode ?? "dark",
|
|
2575
|
-
tokens: flattenTokens(doc.primitives ?? {}),
|
|
2576
|
-
modes
|
|
2577
|
-
};
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
// src/theme/interstellar.tokens.json
|
|
2581
|
-
var interstellar_tokens_default = {
|
|
2582
|
-
name: "interstellar",
|
|
2583
|
-
defaultMode: "dark",
|
|
2584
|
-
primitives: {
|
|
2585
|
-
dimension: {
|
|
2586
|
-
$type: "dimension",
|
|
2587
|
-
"radius-md": { $value: "6px" },
|
|
2588
|
-
"glass-blur": { $value: "12px" }
|
|
2589
1038
|
},
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
1039
|
+
"line-subtle": {
|
|
1040
|
+
by: "mode",
|
|
1041
|
+
dark: {
|
|
1042
|
+
value: "rgba(255, 255, 255, 0.08)",
|
|
1043
|
+
type: "color"
|
|
1044
|
+
},
|
|
1045
|
+
light: {
|
|
1046
|
+
value: "rgba(0, 0, 0, 0.08)",
|
|
1047
|
+
type: "color"
|
|
1048
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
fg: {
|
|
1051
|
+
by: "mode",
|
|
1052
|
+
dark: {
|
|
1053
|
+
value: "#e8e8f0",
|
|
1054
|
+
type: "color"
|
|
1055
|
+
},
|
|
1056
|
+
light: {
|
|
1057
|
+
value: "#2a2018",
|
|
1058
|
+
type: "color"
|
|
1059
|
+
}
|
|
1060
|
+
},
|
|
1061
|
+
"fg-muted": {
|
|
1062
|
+
by: "mode",
|
|
1063
|
+
dark: {
|
|
1064
|
+
value: "#a8a8c0",
|
|
1065
|
+
type: "color"
|
|
1066
|
+
},
|
|
1067
|
+
light: {
|
|
1068
|
+
value: "#6b5c4a",
|
|
1069
|
+
type: "color"
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
"fg-subtle": {
|
|
1073
|
+
by: "mode",
|
|
1074
|
+
dark: {
|
|
1075
|
+
value: "#5a5a78",
|
|
1076
|
+
type: "color"
|
|
1077
|
+
},
|
|
1078
|
+
light: {
|
|
1079
|
+
value: "#a89b85",
|
|
1080
|
+
type: "color"
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
accent: {
|
|
1084
|
+
by: "mode",
|
|
1085
|
+
dark: {
|
|
1086
|
+
value: "#b08adb",
|
|
1087
|
+
type: "color"
|
|
1088
|
+
},
|
|
1089
|
+
light: {
|
|
1090
|
+
value: "#a86f3c",
|
|
1091
|
+
type: "color"
|
|
1092
|
+
}
|
|
1093
|
+
},
|
|
1094
|
+
"accent-hover": {
|
|
1095
|
+
by: "mode",
|
|
1096
|
+
dark: {
|
|
1097
|
+
value: "#c6a3ee",
|
|
1098
|
+
type: "color"
|
|
1099
|
+
},
|
|
1100
|
+
light: {
|
|
1101
|
+
value: "#c2854f",
|
|
1102
|
+
type: "color"
|
|
2620
1103
|
}
|
|
2621
1104
|
},
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
1105
|
+
"focus-ring": {
|
|
1106
|
+
by: "mode",
|
|
1107
|
+
dark: {
|
|
1108
|
+
value: "rgba(176, 138, 219, 0.45)",
|
|
1109
|
+
type: "color"
|
|
1110
|
+
},
|
|
1111
|
+
light: {
|
|
1112
|
+
value: "rgba(168, 111, 60, 0.35)",
|
|
1113
|
+
type: "color"
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
shadow: {
|
|
1117
|
+
by: "mode",
|
|
1118
|
+
dark: {
|
|
1119
|
+
value: "rgba(0, 0, 0, 0.7)",
|
|
1120
|
+
type: "color"
|
|
1121
|
+
},
|
|
1122
|
+
light: {
|
|
1123
|
+
value: "rgba(80, 60, 40, 0.18)",
|
|
1124
|
+
type: "color"
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
"border-strong": {
|
|
1128
|
+
by: "mode",
|
|
1129
|
+
dark: {
|
|
1130
|
+
value: "rgba(255, 255, 255, 0.36)",
|
|
1131
|
+
type: "color"
|
|
1132
|
+
},
|
|
1133
|
+
light: {
|
|
1134
|
+
value: "#9e8b71",
|
|
1135
|
+
type: "color"
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
backdrop: {
|
|
1139
|
+
by: "mode",
|
|
1140
|
+
dark: {
|
|
1141
|
+
value: "radial-gradient(ellipse 60% 80% at 18% 30%, rgba(150, 80, 200, 0.22), transparent 70%), radial-gradient(ellipse 55% 90% at 78% 70%, rgba(220, 100, 140, 0.18), transparent 65%), radial-gradient(ellipse 50% 70% at 50% 50%, rgba(60, 110, 200, 0.18), transparent 75%), radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)",
|
|
1142
|
+
type: "gradient"
|
|
1143
|
+
},
|
|
1144
|
+
light: {
|
|
1145
|
+
value: "none",
|
|
1146
|
+
type: "gradient"
|
|
2638
1147
|
}
|
|
2639
1148
|
}
|
|
2640
1149
|
}
|
|
2641
1150
|
};
|
|
2642
1151
|
|
|
2643
1152
|
// src/theme/interstellar.ts
|
|
2644
|
-
var interstellarTheme =
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
function
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
if (typeof v.zoom !== "number") return null;
|
|
2652
|
-
if (typeof v.pan !== "object" || v.pan === null) return null;
|
|
2653
|
-
if (typeof v.pan.x !== "number" || typeof v.pan.y !== "number") return null;
|
|
2654
|
-
return { zoom: v.zoom, pan: { x: v.pan.x, y: v.pan.y } };
|
|
2655
|
-
}
|
|
2656
|
-
var ZONE_ID = asNodeId("lk-trial-body");
|
|
2657
|
-
var SIDEBAR_ID = asNodeId("lk-trial-sidebar");
|
|
2658
|
-
var CONTENT_ID = asNodeId("lk-trial-content");
|
|
2659
|
-
var STRATEGIES = { strip: stripStrategy };
|
|
2660
|
-
function TrialBody({
|
|
2661
|
-
sidebar,
|
|
2662
|
-
children,
|
|
2663
|
-
contentClassName,
|
|
2664
|
-
width,
|
|
2665
|
-
defaultWidth = 320,
|
|
2666
|
-
minWidth = 140,
|
|
2667
|
-
maxWidth = 720,
|
|
2668
|
-
contentMinWidth = 160,
|
|
2669
|
-
onWidthChange,
|
|
2670
|
-
viewport
|
|
2671
|
-
}) {
|
|
2672
|
-
const storeRef = useRef(null);
|
|
2673
|
-
if (storeRef.current === null) {
|
|
2674
|
-
const store2 = new Store();
|
|
2675
|
-
store2.registerNode(
|
|
2676
|
-
createNode({
|
|
2677
|
-
kind: "zone",
|
|
2678
|
-
id: ZONE_ID,
|
|
2679
|
-
container: { strategyId: "strip", config: { axis: "x", resizable: true } }
|
|
2680
|
-
})
|
|
2681
|
-
);
|
|
2682
|
-
store2.registerNode(
|
|
2683
|
-
createNode({
|
|
2684
|
-
kind: "sidebar",
|
|
2685
|
-
id: SIDEBAR_ID,
|
|
2686
|
-
parentId: ZONE_ID,
|
|
2687
|
-
meta: { title: "Sidebar" },
|
|
2688
|
-
placement: { size: { w: width ?? defaultWidth } },
|
|
2689
|
-
hints: { minSize: { w: minWidth, h: 0 }, maxSize: { w: maxWidth, h: 0 } }
|
|
2690
|
-
})
|
|
2691
|
-
);
|
|
2692
|
-
store2.registerNode(
|
|
2693
|
-
createNode({
|
|
2694
|
-
kind: "content",
|
|
2695
|
-
id: CONTENT_ID,
|
|
2696
|
-
parentId: ZONE_ID,
|
|
2697
|
-
meta: { title: "Content" },
|
|
2698
|
-
hints: { minSize: { w: contentMinWidth, h: 0 } }
|
|
2699
|
-
})
|
|
2700
|
-
);
|
|
2701
|
-
store2.showNode(SIDEBAR_ID);
|
|
2702
|
-
store2.showNode(CONTENT_ID);
|
|
2703
|
-
storeRef.current = store2;
|
|
2704
|
-
}
|
|
2705
|
-
const store = storeRef.current;
|
|
2706
|
-
const widthRef = useRef(width ?? defaultWidth);
|
|
2707
|
-
useEffect(() => {
|
|
2708
|
-
store.setHints(SIDEBAR_ID, {
|
|
2709
|
-
minSize: { w: minWidth, h: 0 },
|
|
2710
|
-
maxSize: { w: maxWidth, h: 0 }
|
|
2711
|
-
});
|
|
2712
|
-
store.setHints(CONTENT_ID, { minSize: { w: contentMinWidth, h: 0 } });
|
|
2713
|
-
}, [store, minWidth, maxWidth, contentMinWidth]);
|
|
2714
|
-
useEffect(() => {
|
|
2715
|
-
if (width === void 0 || width === widthRef.current) return;
|
|
2716
|
-
widthRef.current = width;
|
|
2717
|
-
store.patchPlacement(SIDEBAR_ID, { size: { w: width } });
|
|
2718
|
-
}, [store, width]);
|
|
2719
|
-
const onWidthChangeRef = useRef(onWidthChange);
|
|
2720
|
-
onWidthChangeRef.current = onWidthChange;
|
|
2721
|
-
useEffect(
|
|
2722
|
-
() => store.events.on("node.placementChanged", () => {
|
|
2723
|
-
const next = store.getPlacement(SIDEBAR_ID)?.size?.w;
|
|
2724
|
-
if (typeof next !== "number" || next === widthRef.current) return;
|
|
2725
|
-
widthRef.current = next;
|
|
2726
|
-
onWidthChangeRef.current?.(next);
|
|
2727
|
-
}),
|
|
2728
|
-
[store]
|
|
2729
|
-
);
|
|
2730
|
-
const chrome = useMemo(
|
|
2731
|
-
() => ({
|
|
2732
|
-
sidebar: () => /* @__PURE__ */ jsx("div", { className: "lk-trial__sidebar", children: sidebar }),
|
|
2733
|
-
content: () => /* @__PURE__ */ jsx(
|
|
2734
|
-
"div",
|
|
2735
|
-
{
|
|
2736
|
-
className: contentClassName ? `lk-trial__content ${contentClassName}` : "lk-trial__content",
|
|
2737
|
-
children
|
|
2738
|
-
}
|
|
2739
|
-
)
|
|
2740
|
-
}),
|
|
2741
|
-
[sidebar, children, contentClassName]
|
|
2742
|
-
);
|
|
2743
|
-
return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES, children: /* @__PURE__ */ jsx(
|
|
2744
|
-
Container,
|
|
1153
|
+
var interstellarTheme = defineTheme({
|
|
1154
|
+
...interstellar_theme_default,
|
|
1155
|
+
extends: weaselTheme
|
|
1156
|
+
});
|
|
1157
|
+
function TrialBody({ sidebar, children, contentClassName, ...rest }) {
|
|
1158
|
+
return /* @__PURE__ */ jsx(
|
|
1159
|
+
Split,
|
|
2745
1160
|
{
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
className: "lk-trial__panes
|
|
2749
|
-
|
|
2750
|
-
|
|
1161
|
+
...rest,
|
|
1162
|
+
sidebar,
|
|
1163
|
+
className: "lk-trial__panes",
|
|
1164
|
+
sidebarClassName: "lk-trial__sidebar",
|
|
1165
|
+
contentClassName: contentClassName ? `lk-trial__content ${contentClassName}` : "lk-trial__content",
|
|
1166
|
+
children
|
|
2751
1167
|
}
|
|
2752
|
-
)
|
|
1168
|
+
);
|
|
2753
1169
|
}
|
|
2754
1170
|
var TrialDragContext = createContext(null);
|
|
2755
1171
|
function useTrialDrag() {
|
|
@@ -2834,7 +1250,7 @@ function Portalled({
|
|
|
2834
1250
|
}
|
|
2835
1251
|
function UndockedSections({ trialId, sections, onDock }) {
|
|
2836
1252
|
if (sections.length === 0) return null;
|
|
2837
|
-
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((
|
|
1253
|
+
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((c8) => /* @__PURE__ */ jsx(Portalled, { trialId, contribution: c8, onDock }, c8.id)) });
|
|
2838
1254
|
}
|
|
2839
1255
|
var NO_OP = () => {
|
|
2840
1256
|
};
|
|
@@ -2857,27 +1273,32 @@ function TrialChrome({
|
|
|
2857
1273
|
const lab = useLabContext();
|
|
2858
1274
|
const storeCtx = useContext(LabStoreContext);
|
|
2859
1275
|
if (!storeCtx) throw new Error("[labkit] TrialChrome requires <LabStoreProvider>");
|
|
2860
|
-
const updateTrialView = useStore(storeCtx.store, (
|
|
2861
|
-
const updateTrialSidebarWidth = useStore(storeCtx.store, (
|
|
2862
|
-
const setTrialTitle = useStore(storeCtx.store, (
|
|
2863
|
-
const setTrialSectionCollapsed = useStore(storeCtx.store, (
|
|
2864
|
-
const updateTrialConfig = useStore(storeCtx.store, (
|
|
2865
|
-
const updateTrialState = useStore(storeCtx.store, (
|
|
2866
|
-
const undockPanelAction = useStore(storeCtx.store, (
|
|
2867
|
-
const dockPanelAction = useStore(storeCtx.store, (
|
|
2868
|
-
const undockedPanels = useStore(storeCtx.store, (
|
|
1276
|
+
const updateTrialView = useStore(storeCtx.store, (s5) => s5.updateTrialView);
|
|
1277
|
+
const updateTrialSidebarWidth = useStore(storeCtx.store, (s5) => s5.updateTrialSidebarWidth);
|
|
1278
|
+
const setTrialTitle = useStore(storeCtx.store, (s5) => s5.setTrialTitle);
|
|
1279
|
+
const setTrialSectionCollapsed = useStore(storeCtx.store, (s5) => s5.setTrialSectionCollapsed);
|
|
1280
|
+
const updateTrialConfig = useStore(storeCtx.store, (s5) => s5.updateTrialConfig);
|
|
1281
|
+
const updateTrialState = useStore(storeCtx.store, (s5) => s5.updateTrialState);
|
|
1282
|
+
const undockPanelAction = useStore(storeCtx.store, (s5) => s5.undockPanel);
|
|
1283
|
+
const dockPanelAction = useStore(storeCtx.store, (s5) => s5.dockPanel);
|
|
1284
|
+
const undockedPanels = useStore(storeCtx.store, (s5) => s5.undockedPanels);
|
|
2869
1285
|
const undockedIds = useMemo(
|
|
2870
|
-
() => Object.values(undockedPanels).filter((
|
|
1286
|
+
() => Object.values(undockedPanels).filter((p2) => p2.trialId === trialId).map((p2) => p2.sectionId),
|
|
2871
1287
|
[undockedPanels, trialId]
|
|
2872
1288
|
);
|
|
2873
1289
|
const view2d = as2DView(record.view);
|
|
2874
1290
|
const configSchema = useConfigSchema(instrument);
|
|
2875
|
-
const
|
|
1291
|
+
const { config: resolvedConfig, auto: auto2 } = useResolvedConfig(
|
|
1292
|
+
configSchema,
|
|
1293
|
+
record.config,
|
|
1294
|
+
record.auto
|
|
1295
|
+
);
|
|
1296
|
+
const title = record.title ?? instrument.title ?? instrument.name;
|
|
2876
1297
|
const collapsedSections = record.collapsedSections ?? NO_SECTIONS;
|
|
2877
1298
|
const ctx = useMemo(() => {
|
|
2878
1299
|
const setZoom = (z2) => {
|
|
2879
1300
|
if (!view2d) return;
|
|
2880
|
-
updateTrialView(trialId,
|
|
1301
|
+
updateTrialView(trialId, withZoom(view2d, z2));
|
|
2881
1302
|
};
|
|
2882
1303
|
return {
|
|
2883
1304
|
trialId,
|
|
@@ -2896,6 +1317,7 @@ function TrialChrome({
|
|
|
2896
1317
|
configSchema,
|
|
2897
1318
|
configFields: instrument.config ? [] : instrument.configSchema?.() ?? [],
|
|
2898
1319
|
config: record.config,
|
|
1320
|
+
auto: auto2,
|
|
2899
1321
|
setConfig: (path, value) => {
|
|
2900
1322
|
const prevConfig = record.config;
|
|
2901
1323
|
if (process.env.NODE_ENV !== "production" && !hasConfigPath(prevConfig, path) && schemaNodeAtPath(configSchema.group, path) === void 0) {
|
|
@@ -2905,8 +1327,9 @@ function TrialChrome({
|
|
|
2905
1327
|
}
|
|
2906
1328
|
updateTrialConfig(trialId, path, value);
|
|
2907
1329
|
if (instrument.onConfigChange) {
|
|
2908
|
-
const
|
|
2909
|
-
const
|
|
1330
|
+
const next = applyConfigWrite(prevConfig, record.auto ?? [], path, value);
|
|
1331
|
+
const nextConfig = resolveAutoConfig(configSchema, next.config, new Set(next.autoPaths));
|
|
1332
|
+
const nextState = instrument.onConfigChange(nextConfig, resolvedConfig, record.state);
|
|
2910
1333
|
updateTrialState(trialId, nextState);
|
|
2911
1334
|
}
|
|
2912
1335
|
},
|
|
@@ -2915,7 +1338,7 @@ function TrialChrome({
|
|
|
2915
1338
|
undockedPanels: undockedIds,
|
|
2916
1339
|
undockPanel: (sectionId, as) => undockPanelAction(trialId, sectionId, as),
|
|
2917
1340
|
dockPanel: (sectionId) => dockPanelAction(trialId, sectionId),
|
|
2918
|
-
savedSnapshots: lab.savedSnapshots.filter((
|
|
1341
|
+
savedSnapshots: lab.savedSnapshots.filter((s5) => s5.trialId === trialId),
|
|
2919
1342
|
saveSnapshot: (name) => lab.saveSnapshot(trialId, name),
|
|
2920
1343
|
loadSnapshot: (snapshotId) => lab.loadSnapshot(trialId, snapshotId),
|
|
2921
1344
|
clone: () => lab.cloneTrial(trialId),
|
|
@@ -2940,6 +1363,8 @@ function TrialChrome({
|
|
|
2940
1363
|
activeToolId,
|
|
2941
1364
|
setActiveTool,
|
|
2942
1365
|
configSchema,
|
|
1366
|
+
resolvedConfig,
|
|
1367
|
+
auto2,
|
|
2943
1368
|
undockedIds,
|
|
2944
1369
|
undockPanelAction,
|
|
2945
1370
|
dockPanelAction,
|
|
@@ -2959,7 +1384,7 @@ function TrialChrome({
|
|
|
2959
1384
|
),
|
|
2960
1385
|
[instrument, ctx, trialChrome, chrome, suppress, lab.controls]
|
|
2961
1386
|
);
|
|
2962
|
-
const inRegion = (region) => contributions.filter((
|
|
1387
|
+
const inRegion = (region) => contributions.filter((c8) => c8.region === region);
|
|
2963
1388
|
const handleKeyDown = (e) => {
|
|
2964
1389
|
const mod = e.metaKey || e.ctrlKey;
|
|
2965
1390
|
if (!mod) return;
|
|
@@ -2976,6 +1401,8 @@ function TrialChrome({
|
|
|
2976
1401
|
"section",
|
|
2977
1402
|
{
|
|
2978
1403
|
className: "lk-trial",
|
|
1404
|
+
"data-trial-id": trialId,
|
|
1405
|
+
"data-focused": lab.trials.length > 1 && lab.focusedTrialId === trialId ? "true" : void 0,
|
|
2979
1406
|
"aria-label": `Trial ${title}`,
|
|
2980
1407
|
tabIndex: -1,
|
|
2981
1408
|
onKeyDown: handleKeyDown,
|
|
@@ -2996,14 +1423,14 @@ function TrialChrome({
|
|
|
2996
1423
|
{
|
|
2997
1424
|
width: record.sidebarWidth,
|
|
2998
1425
|
onWidthChange: (w3) => updateTrialSidebarWidth(trialId, w3),
|
|
2999
|
-
contentClassName: instrument.canvas ? "lk-trial__content--flush" : void 0,
|
|
1426
|
+
contentClassName: instrument.canvas || instrument.stage ? "lk-trial__content--flush" : void 0,
|
|
3000
1427
|
sidebar: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3001
1428
|
/* @__PURE__ */ jsx(SidebarRegion, { contributions: inRegion("sidebar"), ctx }),
|
|
3002
1429
|
/* @__PURE__ */ jsx(
|
|
3003
1430
|
UndockedSections,
|
|
3004
1431
|
{
|
|
3005
1432
|
trialId,
|
|
3006
|
-
sections: inRegion("sidebar").filter((
|
|
1433
|
+
sections: inRegion("sidebar").filter((c8) => undockedIds.includes(c8.id)),
|
|
3007
1434
|
onDock: ctx.dockPanel
|
|
3008
1435
|
}
|
|
3009
1436
|
)
|
|
@@ -3027,25 +1454,30 @@ function Trial({ id, chrome, suppress }) {
|
|
|
3027
1454
|
const lab = useLabContext();
|
|
3028
1455
|
const storeCtx = useContext(LabStoreContext);
|
|
3029
1456
|
if (!storeCtx) throw new Error("[labkit] <Trial> requires <LabStoreProvider>");
|
|
3030
|
-
const
|
|
1457
|
+
const instruments = useStore(storeCtx.store, (s5) => s5.instruments) ?? lab.instruments;
|
|
1458
|
+
const record = useStore(storeCtx.store, (s5) => s5.trials.find((w3) => w3.id === id));
|
|
1459
|
+
const instrument = record ? instruments.find((i3) => i3.name === record.instrumentName) : void 0;
|
|
1460
|
+
const kept = useKeptMarks(instrument?.annotations?.storage, id);
|
|
3031
1461
|
if (!record) {
|
|
3032
1462
|
return /* @__PURE__ */ jsxs("div", { className: "lk-trial lk-trial--unknown", children: [
|
|
3033
1463
|
"Trial not found: ",
|
|
3034
1464
|
id
|
|
3035
1465
|
] });
|
|
3036
1466
|
}
|
|
3037
|
-
const instrument = lab.instruments.find((i) => i.name === record.instrumentName);
|
|
3038
1467
|
if (!instrument) {
|
|
3039
1468
|
return /* @__PURE__ */ jsxs("div", { className: "lk-trial lk-trial--unknown", children: [
|
|
3040
1469
|
"Unknown instrument: ",
|
|
3041
1470
|
record.instrumentName
|
|
3042
1471
|
] });
|
|
3043
1472
|
}
|
|
1473
|
+
if (!kept.ready) return /* @__PURE__ */ jsx("div", { className: "lk-trial lk-trial--loading" });
|
|
3044
1474
|
return /* @__PURE__ */ jsx(
|
|
3045
1475
|
TrialRuntime,
|
|
3046
1476
|
{
|
|
1477
|
+
keptMarks: kept.value,
|
|
3047
1478
|
record,
|
|
3048
1479
|
instrument,
|
|
1480
|
+
instruments,
|
|
3049
1481
|
store: storeCtx.store,
|
|
3050
1482
|
isLast: lab.trials.length <= 1,
|
|
3051
1483
|
chrome,
|
|
@@ -3053,17 +1485,53 @@ function Trial({ id, chrome, suppress }) {
|
|
|
3053
1485
|
}
|
|
3054
1486
|
);
|
|
3055
1487
|
}
|
|
3056
|
-
function
|
|
1488
|
+
function useKeptMarks(storage, trialId) {
|
|
1489
|
+
const preload = useContext(AnnotationPreloadContext);
|
|
1490
|
+
const [loaded, setLoaded] = useState(
|
|
1491
|
+
() => storage && preload?.has(trialId) ? { value: preload.get(trialId) } : null
|
|
1492
|
+
);
|
|
1493
|
+
useEffect(() => {
|
|
1494
|
+
if (!storage || loaded) return;
|
|
1495
|
+
let live = true;
|
|
1496
|
+
storage.load().then(
|
|
1497
|
+
(value) => {
|
|
1498
|
+
if (live) setLoaded({ value });
|
|
1499
|
+
},
|
|
1500
|
+
(error) => {
|
|
1501
|
+
console.warn(`[labkit] could not load the marks of trial "${trialId}"`, error);
|
|
1502
|
+
if (live) setLoaded({ value: null });
|
|
1503
|
+
}
|
|
1504
|
+
);
|
|
1505
|
+
return () => {
|
|
1506
|
+
live = false;
|
|
1507
|
+
};
|
|
1508
|
+
}, [storage, loaded, trialId]);
|
|
1509
|
+
if (!storage) return { ready: true, value: void 0 };
|
|
1510
|
+
return loaded ? { ready: true, value: loaded.value } : { ready: false, value: void 0 };
|
|
1511
|
+
}
|
|
1512
|
+
function TrialRuntime({
|
|
1513
|
+
keptMarks,
|
|
1514
|
+
record,
|
|
1515
|
+
instrument,
|
|
1516
|
+
instruments,
|
|
1517
|
+
store,
|
|
1518
|
+
isLast,
|
|
1519
|
+
chrome,
|
|
1520
|
+
suppress
|
|
1521
|
+
}) {
|
|
1522
|
+
const wheelSlot = useRef(null);
|
|
3057
1523
|
const canvasContainerRef = useRef(null);
|
|
3058
1524
|
const loupeHostRef = useRef(null);
|
|
3059
|
-
const updateTrialState = useStore(store, (
|
|
3060
|
-
const updateTrialConfig = useStore(store, (
|
|
3061
|
-
const updateTrialView = useStore(store, (
|
|
3062
|
-
const updateTrialUndoStack = useStore(store, (
|
|
3063
|
-
const updateTrialAnnotations = useStore(store, (
|
|
3064
|
-
const labToolId = useStore(store, (
|
|
3065
|
-
const setLabTool = useStore(store, (
|
|
3066
|
-
const setTrialTool = useStore(store, (
|
|
1525
|
+
const updateTrialState = useStore(store, (s5) => s5.updateTrialState);
|
|
1526
|
+
const updateTrialConfig = useStore(store, (s5) => s5.updateTrialConfig);
|
|
1527
|
+
const updateTrialView = useStore(store, (s5) => s5.updateTrialView);
|
|
1528
|
+
const updateTrialUndoStack = useStore(store, (s5) => s5.updateTrialUndoStack);
|
|
1529
|
+
const updateTrialAnnotations = useStore(store, (s5) => s5.updateTrialAnnotations);
|
|
1530
|
+
const labToolId = useStore(store, (s5) => s5.activeToolId);
|
|
1531
|
+
const setLabTool = useStore(store, (s5) => s5.setLabTool);
|
|
1532
|
+
const setTrialTool = useStore(store, (s5) => s5.setTrialTool);
|
|
1533
|
+
const schema = useConfigSchema(instrument);
|
|
1534
|
+
const { config } = useResolvedConfig(schema, record.config, record.auto);
|
|
3067
1535
|
const busRef = useRef(null);
|
|
3068
1536
|
if (busRef.current === null) busRef.current = createEventBus();
|
|
3069
1537
|
const bus = busRef.current;
|
|
@@ -3071,7 +1539,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3071
1539
|
const [layerOrder, setLayerOrder] = useState(null);
|
|
3072
1540
|
const [loupeOn, setLoupeOn] = useState(false);
|
|
3073
1541
|
const visibleLayers = useMemo(
|
|
3074
|
-
() => (instrument.canvas?.layers ?? []).filter((
|
|
1542
|
+
() => (instrument.canvas?.layers ?? []).filter((l9) => layerVisibility[l9.id] !== false).map((l9) => l9.id),
|
|
3075
1543
|
[instrument.canvas, layerVisibility]
|
|
3076
1544
|
);
|
|
3077
1545
|
const undoCap = instrument.undo;
|
|
@@ -3084,25 +1552,28 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3084
1552
|
const snap = structuredClone(current.state);
|
|
3085
1553
|
updateTrialUndoStack(record.id, (prev) => pushSnapshot(prev, snap, maxDepth));
|
|
3086
1554
|
};
|
|
3087
|
-
const setView = (
|
|
1555
|
+
const setView = (v2) => updateTrialView(record.id, v2);
|
|
1556
|
+
const stage = instrument.canvas ? void 0 : instrument.stage;
|
|
3088
1557
|
const placeView = (size) => {
|
|
3089
|
-
const declared = instrument.canvas?.initialView;
|
|
1558
|
+
const declared = instrument.canvas?.initialView ?? (stage ? stage.initialView ?? ((vp) => fitStage(stage.size, vp)) : void 0);
|
|
3090
1559
|
if (typeof declared !== "function") return;
|
|
3091
1560
|
if (record.view != null) return;
|
|
3092
1561
|
setView(declared(size));
|
|
3093
1562
|
};
|
|
3094
1563
|
const annotationsCap = instrument.annotations;
|
|
3095
1564
|
const targetsRef = useRef(() => []);
|
|
3096
|
-
targetsRef.current = () => annotationsCap ? annotationsCap.targets(record.state,
|
|
1565
|
+
targetsRef.current = () => annotationsCap ? annotationsCap.targets(record.state, config, { id: record.id, view: record.view }) : [];
|
|
3097
1566
|
const annotationsRef = useRef(null);
|
|
3098
|
-
const configRef = useRef(
|
|
3099
|
-
configRef.current =
|
|
1567
|
+
const configRef = useRef(config);
|
|
1568
|
+
configRef.current = config;
|
|
1569
|
+
const capRef = useRef(annotationsCap);
|
|
1570
|
+
capRef.current = annotationsCap;
|
|
3100
1571
|
if (annotationsRef.current === null) {
|
|
3101
|
-
const kept = annotationsCap?.storage ?
|
|
1572
|
+
const kept = annotationsCap?.storage ? keptMarks : record.annotations;
|
|
3102
1573
|
annotationsRef.current = annotationsFromJSON(kept, () => targetsRef.current(), {
|
|
3103
|
-
meaning:
|
|
1574
|
+
meaning: () => capRef.current?.meaning,
|
|
3104
1575
|
config: () => configRef.current,
|
|
3105
|
-
onCapture: (result) =>
|
|
1576
|
+
onCapture: (result) => capRef.current?.onCapture?.(result)
|
|
3106
1577
|
});
|
|
3107
1578
|
}
|
|
3108
1579
|
const annotations = annotationsRef.current;
|
|
@@ -3146,8 +1617,9 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3146
1617
|
flush();
|
|
3147
1618
|
};
|
|
3148
1619
|
}, [annotations, annotationsCap]);
|
|
3149
|
-
const declaresTools = instrument.tools != null
|
|
3150
|
-
const
|
|
1620
|
+
const declaresTools = instrument.tools != null;
|
|
1621
|
+
const labTool = resolveLabTool(labToolId, instruments);
|
|
1622
|
+
const resolvedToolId = declaresTools ? record.activeToolId ?? instrument.tools?.initial ?? instrument.tools?.tools[0]?.id ?? null : labTool;
|
|
3151
1623
|
const setActiveTool = (id) => {
|
|
3152
1624
|
if (declaresTools) setTrialTool(record.id, id);
|
|
3153
1625
|
else setLabTool(id);
|
|
@@ -3157,13 +1629,13 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3157
1629
|
const job = useJob({
|
|
3158
1630
|
capability: jobCap ?? { run: async function* () {
|
|
3159
1631
|
}, onItem: (_i, st) => st },
|
|
3160
|
-
config
|
|
1632
|
+
config,
|
|
3161
1633
|
state: record.state,
|
|
3162
1634
|
setState: (next) => updateTrialState(record.id, next)
|
|
3163
1635
|
});
|
|
3164
1636
|
const renderCtx = {
|
|
3165
1637
|
state: record.state,
|
|
3166
|
-
config
|
|
1638
|
+
config,
|
|
3167
1639
|
setState: (next) => {
|
|
3168
1640
|
snapshotIfNeeded("state.change");
|
|
3169
1641
|
updateTrialState(record.id, next);
|
|
@@ -3184,7 +1656,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3184
1656
|
zoom: view2d?.zoom ?? 1,
|
|
3185
1657
|
setZoom: (z2) => {
|
|
3186
1658
|
if (!view2d) return;
|
|
3187
|
-
updateTrialView(record.id,
|
|
1659
|
+
updateTrialView(record.id, withZoom(view2d, z2));
|
|
3188
1660
|
},
|
|
3189
1661
|
activeToolId: resolvedToolId,
|
|
3190
1662
|
visibleLayers
|
|
@@ -3222,33 +1694,33 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3222
1694
|
const loupeCap = useMemo(() => {
|
|
3223
1695
|
const declared = instrument.loupe;
|
|
3224
1696
|
if (declared == null) return null;
|
|
3225
|
-
return resolveLoupe(typeof declared === "function" ? declared(
|
|
3226
|
-
}, [instrument.loupe,
|
|
3227
|
-
const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((
|
|
1697
|
+
return resolveLoupe(typeof declared === "function" ? declared(config) : declared);
|
|
1698
|
+
}, [instrument.loupe, config]);
|
|
1699
|
+
const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v2) => !v2) } : void 0;
|
|
3228
1700
|
const canvasLayers = useMemo(() => {
|
|
3229
1701
|
if (!instrument.canvas) return [];
|
|
3230
1702
|
const baseLayers = instrument.canvas.layers;
|
|
3231
|
-
const ordered = layerOrder ? [...baseLayers].sort((
|
|
1703
|
+
const ordered = layerOrder ? [...baseLayers].sort((a4, b2) => layerOrder.indexOf(a4.id) - layerOrder.indexOf(b2.id)) : baseLayers;
|
|
3232
1704
|
return ordered.map((layer) => ({
|
|
3233
1705
|
id: layer.id,
|
|
3234
1706
|
visible: layerVisibility[layer.id] !== false,
|
|
3235
1707
|
render: (ctx, view, frame) => {
|
|
3236
1708
|
applyCamera(ctx, view, frame);
|
|
3237
|
-
layer.draw(ctx, { state: record.state, config
|
|
1709
|
+
layer.draw(ctx, { state: record.state, config, zoom: view.zoom });
|
|
3238
1710
|
}
|
|
3239
1711
|
}));
|
|
3240
|
-
}, [instrument.canvas, record.state,
|
|
1712
|
+
}, [instrument.canvas, record.state, config, layerVisibility, layerOrder]);
|
|
3241
1713
|
const layerDescriptors = useMemo(() => {
|
|
3242
1714
|
if (!instrument.layers) return [];
|
|
3243
|
-
return instrument.layers.ids.map((
|
|
1715
|
+
return instrument.layers.ids.map((l9) => typeof l9 === "string" ? { id: l9, label: l9 } : l9);
|
|
3244
1716
|
}, [instrument.layers]);
|
|
3245
1717
|
const dragDropResult = useDragDrop({
|
|
3246
|
-
capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i,
|
|
1718
|
+
capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i, s5) => s5 },
|
|
3247
1719
|
canvasContainerRef,
|
|
3248
1720
|
view: view2d ?? DEFAULT_VIEW,
|
|
3249
1721
|
worldSpec: instrument.canvas?.worldSpec,
|
|
3250
1722
|
state: record.state,
|
|
3251
|
-
config
|
|
1723
|
+
config,
|
|
3252
1724
|
setState: (next) => {
|
|
3253
1725
|
snapshotIfNeeded("canvas.itemAdded");
|
|
3254
1726
|
updateTrialState(record.id, next);
|
|
@@ -3260,9 +1732,9 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3260
1732
|
});
|
|
3261
1733
|
const paletteItems = useMemo(() => {
|
|
3262
1734
|
if (!instrument.dragDrop) return [];
|
|
3263
|
-
const
|
|
3264
|
-
return typeof
|
|
3265
|
-
}, [instrument.dragDrop, record.state,
|
|
1735
|
+
const p2 = instrument.dragDrop.palette;
|
|
1736
|
+
return typeof p2 === "function" ? p2(record.state, config) : p2;
|
|
1737
|
+
}, [instrument.dragDrop, record.state, config]);
|
|
3266
1738
|
const layersWithFeedback = useMemo(() => {
|
|
3267
1739
|
if (!dragDropResult.drag?.feedback) return canvasLayers;
|
|
3268
1740
|
const fb = dragDropResult.drag.feedback;
|
|
@@ -3275,12 +1747,12 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3275
1747
|
render: (ctx) => {
|
|
3276
1748
|
const el = canvasContainerRef.current;
|
|
3277
1749
|
if (!el) return;
|
|
3278
|
-
const
|
|
1750
|
+
const r10 = el.getBoundingClientRect();
|
|
3279
1751
|
ctx.save();
|
|
3280
1752
|
ctx.strokeStyle = fb.ok ? "#3a7" : "#c44";
|
|
3281
1753
|
ctx.lineWidth = 2;
|
|
3282
1754
|
ctx.beginPath();
|
|
3283
|
-
ctx.arc(screen.x -
|
|
1755
|
+
ctx.arc(screen.x - r10.left, screen.y - r10.top, 16, 0, Math.PI * 2);
|
|
3284
1756
|
ctx.stroke();
|
|
3285
1757
|
ctx.restore();
|
|
3286
1758
|
}
|
|
@@ -3293,7 +1765,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3293
1765
|
capability: loupeCap,
|
|
3294
1766
|
enabled: loupeOn,
|
|
3295
1767
|
state: record.state,
|
|
3296
|
-
config
|
|
1768
|
+
config,
|
|
3297
1769
|
view: view2d ?? DEFAULT_VIEW,
|
|
3298
1770
|
worldSpec: instrument.canvas?.worldSpec,
|
|
3299
1771
|
hostRef: loupeHostRef
|
|
@@ -3320,6 +1792,21 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3320
1792
|
),
|
|
3321
1793
|
/* @__PURE__ */ jsx(DragOverlay, { drag: dragDropResult.drag })
|
|
3322
1794
|
] });
|
|
1795
|
+
} else if (stage) {
|
|
1796
|
+
body = /* @__PURE__ */ jsx(
|
|
1797
|
+
Stage,
|
|
1798
|
+
{
|
|
1799
|
+
size: stage.size,
|
|
1800
|
+
view: view2d ?? DEFAULT_VIEW,
|
|
1801
|
+
onViewChange: setView,
|
|
1802
|
+
onResize: placeView,
|
|
1803
|
+
minZoom: stage.minZoom,
|
|
1804
|
+
maxZoom: stage.maxZoom,
|
|
1805
|
+
hostRef: loupeHostRef,
|
|
1806
|
+
overlay: lens,
|
|
1807
|
+
children: instrument.render(renderCtx)
|
|
1808
|
+
}
|
|
1809
|
+
);
|
|
3323
1810
|
} else if (lens) {
|
|
3324
1811
|
body = /* @__PURE__ */ jsxs("div", { ref: loupeHostRef, className: "lk-trial__loupe-host", children: [
|
|
3325
1812
|
instrument.render(renderCtx),
|
|
@@ -3340,7 +1827,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3340
1827
|
layers: layerDescriptors,
|
|
3341
1828
|
visibility: layerVisibility,
|
|
3342
1829
|
onReorder: (next) => {
|
|
3343
|
-
setLayerOrder(next.map((
|
|
1830
|
+
setLayerOrder(next.map((l9) => l9.id));
|
|
3344
1831
|
bus.emit("layers.reorder");
|
|
3345
1832
|
},
|
|
3346
1833
|
onToggle: (lid, visible) => {
|
|
@@ -3374,13 +1861,13 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3374
1861
|
{
|
|
3375
1862
|
capability: annotationsCap,
|
|
3376
1863
|
state: record.state,
|
|
3377
|
-
config
|
|
1864
|
+
config,
|
|
3378
1865
|
trial: { id: record.id, view: record.view },
|
|
3379
1866
|
annotations,
|
|
3380
|
-
activeToolId:
|
|
1867
|
+
activeToolId: labTool
|
|
3381
1868
|
}
|
|
3382
1869
|
) : null;
|
|
3383
|
-
return /* @__PURE__ */ jsx(TrialIdProvider, { trialId: record.id, children: /* @__PURE__ */ jsx(AnnotationsContext.Provider, { value: annotationsCap ? annotations : null, children: /* @__PURE__ */ jsxs(
|
|
1870
|
+
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(
|
|
3384
1871
|
TrialChrome,
|
|
3385
1872
|
{
|
|
3386
1873
|
job: jobCap ? job : void 0,
|
|
@@ -3400,29 +1887,53 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
|
|
|
3400
1887
|
annotationOverlays
|
|
3401
1888
|
]
|
|
3402
1889
|
}
|
|
3403
|
-
) }) });
|
|
1890
|
+
) }) }) });
|
|
3404
1891
|
}
|
|
3405
1892
|
|
|
3406
1893
|
// src/trial/trialOps.ts
|
|
3407
1894
|
var DEFAULT_VIEW2 = { zoom: 1, pan: { x: 0, y: 0 } };
|
|
3408
1895
|
function findInstrument(instruments, name) {
|
|
3409
|
-
const found = instruments.find((
|
|
1896
|
+
const found = instruments.find((i3) => i3.name === name);
|
|
3410
1897
|
if (!found) {
|
|
3411
1898
|
throw new Error(`[labkit] Unknown instrument: "${name}"`);
|
|
3412
1899
|
}
|
|
3413
1900
|
return found;
|
|
3414
1901
|
}
|
|
3415
1902
|
function initialView(instrument) {
|
|
3416
|
-
const
|
|
3417
|
-
|
|
1903
|
+
const stage = instrument.canvas ? void 0 : instrument.stage;
|
|
1904
|
+
const declared = instrument.canvas?.initialView ?? stage?.initialView;
|
|
1905
|
+
if (typeof declared === "function" || stage && declared === void 0) return null;
|
|
3418
1906
|
return declared ? structuredClone(declared) : { ...DEFAULT_VIEW2, pan: { ...DEFAULT_VIEW2.pan } };
|
|
3419
1907
|
}
|
|
3420
1908
|
function seedConfig(defaults, seed) {
|
|
3421
1909
|
return seed ? fillConfigDefaults(seed, defaults) : defaults;
|
|
3422
1910
|
}
|
|
1911
|
+
function splitAutoSeed(seed, at = "") {
|
|
1912
|
+
const config = {};
|
|
1913
|
+
const autoPaths = [];
|
|
1914
|
+
for (const [key, value] of Object.entries(seed ?? {})) {
|
|
1915
|
+
const path = at === "" ? key : `${at}.${key}`;
|
|
1916
|
+
if (isAuto(value)) {
|
|
1917
|
+
autoPaths.push(path);
|
|
1918
|
+
} else if (isRecord(value)) {
|
|
1919
|
+
const inner = splitAutoSeed(value, path);
|
|
1920
|
+
config[key] = inner.config;
|
|
1921
|
+
autoPaths.push(...inner.autoPaths);
|
|
1922
|
+
} else {
|
|
1923
|
+
config[key] = value;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
return { config, autoPaths };
|
|
1927
|
+
}
|
|
1928
|
+
function schemaAutoPaths(instrument) {
|
|
1929
|
+
const schema = unruledConfigSchema(instrument);
|
|
1930
|
+
return schema ? autoPathsOf(schema) : [];
|
|
1931
|
+
}
|
|
3423
1932
|
function addTrial(trials, instruments, instrumentName, options = {}) {
|
|
3424
1933
|
const instrument = findInstrument(instruments, instrumentName);
|
|
3425
|
-
const
|
|
1934
|
+
const seeded = splitAutoSeed(options.config);
|
|
1935
|
+
const autoPaths = [.../* @__PURE__ */ new Set([...schemaAutoPaths(instrument), ...seeded.autoPaths])];
|
|
1936
|
+
const config = seedConfig(instrument.defaultConfig(), seeded.config);
|
|
3426
1937
|
const state = instrument.initialState(config);
|
|
3427
1938
|
const record = {
|
|
3428
1939
|
id: newId(),
|
|
@@ -3432,7 +1943,9 @@ function addTrial(trials, instruments, instrumentName, options = {}) {
|
|
|
3432
1943
|
view: initialView(instrument),
|
|
3433
1944
|
undoStack: { past: [], future: [] }
|
|
3434
1945
|
};
|
|
3435
|
-
if (options.config) record.configSeed =
|
|
1946
|
+
if (options.config) record.configSeed = seeded.config;
|
|
1947
|
+
if (seeded.autoPaths.length > 0) record.autoSeed = seeded.autoPaths;
|
|
1948
|
+
if (autoPaths.length > 0) record.auto = autoPaths;
|
|
3436
1949
|
return [...trials, record];
|
|
3437
1950
|
}
|
|
3438
1951
|
function cloneTrial(trials, id) {
|
|
@@ -3461,14 +1974,25 @@ function resetTrial(trials, id, instruments) {
|
|
|
3461
1974
|
const instrument = findInstrument(instruments, current.instrumentName);
|
|
3462
1975
|
const config = seedConfig(instrument.defaultConfig(), current.configSeed);
|
|
3463
1976
|
const state = instrument.initialState(config);
|
|
1977
|
+
const autoPaths = [.../* @__PURE__ */ new Set([...schemaAutoPaths(instrument), ...current.autoSeed ?? []])];
|
|
1978
|
+
const { auto: _unpinnedNow, ...kept } = current;
|
|
3464
1979
|
const reset = {
|
|
3465
|
-
...
|
|
1980
|
+
...kept,
|
|
3466
1981
|
config,
|
|
3467
1982
|
state,
|
|
3468
1983
|
view: initialView(instrument)
|
|
3469
1984
|
};
|
|
1985
|
+
if (autoPaths.length > 0) reset.auto = autoPaths;
|
|
3470
1986
|
return [...trials.slice(0, idx), reset, ...trials.slice(idx + 1)];
|
|
3471
1987
|
}
|
|
1988
|
+
function swapTrial(trials, instruments, id, instrumentName, options = {}) {
|
|
1989
|
+
const idx = trials.findIndex((w3) => w3.id === id);
|
|
1990
|
+
const current = trials[idx];
|
|
1991
|
+
if (!current) return trials;
|
|
1992
|
+
const [record] = addTrial([], instruments, instrumentName, options);
|
|
1993
|
+
if (current.sidebarWidth !== void 0) record.sidebarWidth = current.sidebarWidth;
|
|
1994
|
+
return [...trials.slice(0, idx), record, ...trials.slice(idx + 1)];
|
|
1995
|
+
}
|
|
3472
1996
|
function reorderTrials(trials, ids) {
|
|
3473
1997
|
const byId = new Map(trials.map((w3) => [w3.id, w3]));
|
|
3474
1998
|
const named = ids.map((id) => byId.get(id)).filter((w3) => w3 !== void 0);
|
|
@@ -3476,6 +2000,86 @@ function reorderTrials(trials, ids) {
|
|
|
3476
2000
|
const rest = trials.filter((w3) => !seen.has(w3.id));
|
|
3477
2001
|
return [...named, ...rest];
|
|
3478
2002
|
}
|
|
2003
|
+
function findOvershoot(bound, items, tolerance = 1) {
|
|
2004
|
+
let worst = null;
|
|
2005
|
+
for (const { item, edges } of items) {
|
|
2006
|
+
const y3 = edges.bottom - bound.bottom;
|
|
2007
|
+
const x = edges.right - bound.right;
|
|
2008
|
+
const by = Math.max(x, y3);
|
|
2009
|
+
if (by <= tolerance) continue;
|
|
2010
|
+
if (!worst || by > worst.by) worst = { item, by, axis: y3 >= x ? "y" : "x" };
|
|
2011
|
+
}
|
|
2012
|
+
return worst;
|
|
2013
|
+
}
|
|
2014
|
+
function tagAndClasses(el) {
|
|
2015
|
+
return el.tagName.toLowerCase() + [...el.classList].map((c8) => `.${c8}`).join("");
|
|
2016
|
+
}
|
|
2017
|
+
function describeElement(el) {
|
|
2018
|
+
if (el.classList.length > 0) return tagAndClasses(el);
|
|
2019
|
+
let up = el.parentElement;
|
|
2020
|
+
while (up && up.classList.length === 0) up = up.parentElement;
|
|
2021
|
+
return up ? `${tagAndClasses(el)} in ${tagAndClasses(up)}` : tagAndClasses(el);
|
|
2022
|
+
}
|
|
2023
|
+
function edgesOf(el) {
|
|
2024
|
+
const r10 = el.getBoundingClientRect();
|
|
2025
|
+
return { top: r10.top, left: r10.left, bottom: r10.bottom, right: r10.right };
|
|
2026
|
+
}
|
|
2027
|
+
function* candidates(root) {
|
|
2028
|
+
yield { item: root, edges: edgesOf(root) };
|
|
2029
|
+
for (const el of root.querySelectorAll("*")) yield { item: el, edges: edgesOf(el) };
|
|
2030
|
+
}
|
|
2031
|
+
function overflows(el) {
|
|
2032
|
+
return el.scrollHeight > el.clientHeight + 1 || el.scrollWidth > el.clientWidth + 1;
|
|
2033
|
+
}
|
|
2034
|
+
function explainLabScroll(lab, shellBody) {
|
|
2035
|
+
if (overflows(shellBody)) {
|
|
2036
|
+
const r11 = shellBody.getBoundingClientRect();
|
|
2037
|
+
const bound2 = {
|
|
2038
|
+
top: r11.top,
|
|
2039
|
+
left: r11.left,
|
|
2040
|
+
bottom: r11.top + shellBody.clientHeight,
|
|
2041
|
+
right: r11.left + shellBody.clientWidth
|
|
2042
|
+
};
|
|
2043
|
+
const hit2 = findOvershoot(bound2, candidates(shellBody));
|
|
2044
|
+
return hit2 ? `.lk-shell-body scrolls: ${describeElement(hit2.item)} reaches ${Math.round(hit2.by)}px past it (${hit2.axis})` : ".lk-shell-body scrolls";
|
|
2045
|
+
}
|
|
2046
|
+
const doc = document.scrollingElement ?? document.documentElement;
|
|
2047
|
+
if (!overflows(doc)) return null;
|
|
2048
|
+
const r10 = lab.getBoundingClientRect();
|
|
2049
|
+
const bound = {
|
|
2050
|
+
top: r10.top,
|
|
2051
|
+
left: r10.left,
|
|
2052
|
+
bottom: r10.top + Math.min(r10.height, doc.clientHeight),
|
|
2053
|
+
right: r10.left + Math.min(r10.width, doc.clientWidth)
|
|
2054
|
+
};
|
|
2055
|
+
const hit = findOvershoot(bound, candidates(lab));
|
|
2056
|
+
return hit ? `the page scrolls: ${describeElement(hit.item)} reaches ${Math.round(hit.by)}px past the lab's box (${hit.axis})` : null;
|
|
2057
|
+
}
|
|
2058
|
+
var SETTLE_MS = 300;
|
|
2059
|
+
function useLabFitWarning(labBody) {
|
|
2060
|
+
useEffect(() => {
|
|
2061
|
+
if (process.env.NODE_ENV === "production") return;
|
|
2062
|
+
if (!labBody || typeof ResizeObserver === "undefined") return;
|
|
2063
|
+
const lab = labBody.closest(".lk-lab");
|
|
2064
|
+
const shellBody = labBody.closest(".lk-shell-body");
|
|
2065
|
+
if (!lab || !shellBody) return;
|
|
2066
|
+
let timer;
|
|
2067
|
+
const observer = new ResizeObserver(() => {
|
|
2068
|
+
clearTimeout(timer);
|
|
2069
|
+
timer = setTimeout(() => {
|
|
2070
|
+
const why = explainLabScroll(lab, shellBody);
|
|
2071
|
+
if (!why) return;
|
|
2072
|
+
observer.disconnect();
|
|
2073
|
+
console.warn(`[labkit] <Lab> should fit its container without scrolling, but ${why}.`);
|
|
2074
|
+
}, SETTLE_MS);
|
|
2075
|
+
});
|
|
2076
|
+
for (const el of [document.documentElement, lab, shellBody, labBody]) observer.observe(el);
|
|
2077
|
+
return () => {
|
|
2078
|
+
clearTimeout(timer);
|
|
2079
|
+
observer.disconnect();
|
|
2080
|
+
};
|
|
2081
|
+
}, [labBody]);
|
|
2082
|
+
}
|
|
3479
2083
|
var MODES = [
|
|
3480
2084
|
{ value: "auto", label: "Auto", glyph: /* @__PURE__ */ jsx(E, { size: 14 }) },
|
|
3481
2085
|
{ value: "light", label: "Light", glyph: /* @__PURE__ */ jsx(w, { size: 14 }) },
|
|
@@ -3486,11 +2090,11 @@ var MODE_ITEMS = MODES.map(({ value, label, glyph }) => ({
|
|
|
3486
2090
|
label: glyph,
|
|
3487
2091
|
ariaLabel: label
|
|
3488
2092
|
}));
|
|
3489
|
-
function LabHeader() {
|
|
2093
|
+
function LabHeader({ addTrial: addTrial2 = true }) {
|
|
3490
2094
|
const lab = useLabContext();
|
|
3491
2095
|
const only = lab.instruments.length === 1 ? lab.instruments[0] : null;
|
|
3492
2096
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3493
|
-
only ? /* @__PURE__ */ jsxs(
|
|
2097
|
+
!addTrial2 ? null : only ? /* @__PURE__ */ jsxs(
|
|
3494
2098
|
"button",
|
|
3495
2099
|
{
|
|
3496
2100
|
type: "button",
|
|
@@ -3501,29 +2105,20 @@ function LabHeader() {
|
|
|
3501
2105
|
/* @__PURE__ */ jsx("span", { children: "Add trial" })
|
|
3502
2106
|
]
|
|
3503
2107
|
}
|
|
3504
|
-
) : (
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
{
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
placeholder: "Add trial\u2026",
|
|
3513
|
-
selectedKey: null,
|
|
3514
|
-
options: lab.instruments.map((i) => ({ value: i.name, label: i.name })),
|
|
3515
|
-
onSelectionChange: (name) => {
|
|
3516
|
-
if (name != null) lab.addTrial(String(name));
|
|
3517
|
-
}
|
|
3518
|
-
}
|
|
3519
|
-
)
|
|
2108
|
+
) : /* @__PURE__ */ jsx(
|
|
2109
|
+
l7,
|
|
2110
|
+
{
|
|
2111
|
+
"aria-label": "Add trial",
|
|
2112
|
+
label: "Add trial\u2026",
|
|
2113
|
+
items: lab.instruments.map((i3) => ({ value: i3.name, label: i3.title ?? i3.name })),
|
|
2114
|
+
onAction: (name) => lab.addTrial(name)
|
|
2115
|
+
}
|
|
3520
2116
|
),
|
|
3521
2117
|
/* @__PURE__ */ jsx(
|
|
3522
|
-
|
|
2118
|
+
r3,
|
|
3523
2119
|
{
|
|
3524
2120
|
className: "lk-lab-header__mode",
|
|
3525
2121
|
ariaLabel: "Color mode",
|
|
3526
|
-
size: "sm",
|
|
3527
2122
|
variant: "flat",
|
|
3528
2123
|
items: MODE_ITEMS,
|
|
3529
2124
|
value: lab.mode,
|
|
@@ -3534,32 +2129,47 @@ function LabHeader() {
|
|
|
3534
2129
|
)
|
|
3535
2130
|
] });
|
|
3536
2131
|
}
|
|
3537
|
-
function LabPalette({ tools }) {
|
|
3538
|
-
const
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
const setLabTool = useStore(storeCtx.store, (s) => s.setLabTool);
|
|
3542
|
-
const contributions = tools.map((t) => ({
|
|
3543
|
-
id: t.id,
|
|
3544
|
-
region: "palette",
|
|
3545
|
-
group: t.group,
|
|
3546
|
-
item: { icon: t.icon, label: t.label, shortcut: t.shortcut }
|
|
3547
|
-
}));
|
|
3548
|
-
const ctx = {
|
|
3549
|
-
activeToolId,
|
|
3550
|
-
setActiveTool: setLabTool
|
|
3551
|
-
};
|
|
3552
|
-
return /* @__PURE__ */ jsx(PaletteRegion, { contributions, ctx });
|
|
2132
|
+
function LabPalette({ tools, contributions }) {
|
|
2133
|
+
const ctx = useLabChromeContext();
|
|
2134
|
+
const all = labContributions(tools, contributions);
|
|
2135
|
+
return /* @__PURE__ */ jsx(PaletteRegion, { contributions: all.filter((c8) => c8.region === "palette"), ctx });
|
|
3553
2136
|
}
|
|
3554
|
-
function
|
|
3555
|
-
|
|
3556
|
-
return
|
|
2137
|
+
function query() {
|
|
2138
|
+
if (typeof window === "undefined" || !window.matchMedia) return null;
|
|
2139
|
+
return window.matchMedia("(prefers-color-scheme: light)");
|
|
3557
2140
|
}
|
|
3558
|
-
function
|
|
2141
|
+
function useResolvedMode(mode) {
|
|
2142
|
+
const [system, setSystem] = useState(
|
|
2143
|
+
() => query()?.matches ? "light" : "dark"
|
|
2144
|
+
);
|
|
2145
|
+
useEffect(() => {
|
|
2146
|
+
if (mode !== "auto") return;
|
|
2147
|
+
const mq = query();
|
|
2148
|
+
if (!mq) return;
|
|
2149
|
+
const onChange = (e) => setSystem(e.matches ? "light" : "dark");
|
|
2150
|
+
mq.addEventListener("change", onChange);
|
|
2151
|
+
return () => mq.removeEventListener("change", onChange);
|
|
2152
|
+
}, [mode]);
|
|
2153
|
+
return mode === "auto" ? system : mode;
|
|
2154
|
+
}
|
|
2155
|
+
function LabRoot({ children, mode = "auto", className }) {
|
|
2156
|
+
const resolved = useResolvedMode(mode);
|
|
2157
|
+
const outer = useThemeOptional();
|
|
2158
|
+
const root = /* @__PURE__ */ jsx("div", { className: className ? `lk-root ${className}` : "lk-root", "data-wzl-portal-host": "", children });
|
|
2159
|
+
return outer ? root : /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, selection: { mode: resolved }, children: root });
|
|
2160
|
+
}
|
|
2161
|
+
function currentPage(path, pages) {
|
|
2162
|
+
const here = path.replace(/[?#].*$/, "").replace(/\/$/, "").replace(/\.html$/, "");
|
|
2163
|
+
return pages.findIndex((p2) => here.endsWith(p2.href.replace(/\.html$/, "")));
|
|
2164
|
+
}
|
|
2165
|
+
function LabSwitcher({ title, pages, path, className }) {
|
|
3559
2166
|
const [open, setOpen] = useState(false);
|
|
3560
2167
|
const root = useRef(null);
|
|
3561
2168
|
const menuId = useId();
|
|
3562
|
-
const here = currentPage(
|
|
2169
|
+
const here = currentPage(
|
|
2170
|
+
path ?? (typeof window === "undefined" ? "" : window.location.pathname),
|
|
2171
|
+
pages
|
|
2172
|
+
);
|
|
3563
2173
|
const close = useCallback(() => setOpen(false), []);
|
|
3564
2174
|
useEffect(() => {
|
|
3565
2175
|
if (!open) return;
|
|
@@ -3598,13 +2208,13 @@ function LabSwitcher({ title, pages, path, className }) {
|
|
|
3598
2208
|
]
|
|
3599
2209
|
}
|
|
3600
2210
|
) }),
|
|
3601
|
-
open && /* @__PURE__ */ jsx("div", { className: "lk-switcher-menu", id: menuId, role: "menu", children: pages.map((page,
|
|
2211
|
+
open && /* @__PURE__ */ jsx("div", { className: "lk-switcher-menu", id: menuId, role: "menu", children: pages.map((page, i3) => /* @__PURE__ */ jsx(
|
|
3602
2212
|
"a",
|
|
3603
2213
|
{
|
|
3604
2214
|
href: page.href,
|
|
3605
2215
|
role: "menuitem",
|
|
3606
2216
|
className: "lk-switcher-item",
|
|
3607
|
-
"aria-current":
|
|
2217
|
+
"aria-current": i3 === here ? "page" : void 0,
|
|
3608
2218
|
onClick: close,
|
|
3609
2219
|
children: page.label
|
|
3610
2220
|
},
|
|
@@ -3612,24 +2222,6 @@ function LabSwitcher({ title, pages, path, className }) {
|
|
|
3612
2222
|
)) })
|
|
3613
2223
|
] });
|
|
3614
2224
|
}
|
|
3615
|
-
function query() {
|
|
3616
|
-
if (typeof window === "undefined" || !window.matchMedia) return null;
|
|
3617
|
-
return window.matchMedia("(prefers-color-scheme: light)");
|
|
3618
|
-
}
|
|
3619
|
-
function useResolvedMode(mode) {
|
|
3620
|
-
const [system, setSystem] = useState(
|
|
3621
|
-
() => query()?.matches ? "light" : "dark"
|
|
3622
|
-
);
|
|
3623
|
-
useEffect(() => {
|
|
3624
|
-
if (mode !== "auto") return;
|
|
3625
|
-
const mq = query();
|
|
3626
|
-
if (!mq) return;
|
|
3627
|
-
const onChange = (e) => setSystem(e.matches ? "light" : "dark");
|
|
3628
|
-
mq.addEventListener("change", onChange);
|
|
3629
|
-
return () => mq.removeEventListener("change", onChange);
|
|
3630
|
-
}, [mode]);
|
|
3631
|
-
return mode === "auto" ? system : mode;
|
|
3632
|
-
}
|
|
3633
2225
|
function LabShell({
|
|
3634
2226
|
title,
|
|
3635
2227
|
children,
|
|
@@ -3639,41 +2231,26 @@ function LabShell({
|
|
|
3639
2231
|
pages,
|
|
3640
2232
|
path
|
|
3641
2233
|
}) {
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
/* @__PURE__ */
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
LabSwitcher,
|
|
3651
|
-
{
|
|
3652
|
-
title,
|
|
3653
|
-
pages,
|
|
3654
|
-
path,
|
|
3655
|
-
className: "lk-shell-title"
|
|
3656
|
-
}
|
|
3657
|
-
) : /* @__PURE__ */ jsx("h1", { className: "lk-shell-title", children: title }),
|
|
3658
|
-
header && /* @__PURE__ */ jsx("div", { className: "lk-shell-header-actions", children: header })
|
|
3659
|
-
] }),
|
|
3660
|
-
/* @__PURE__ */ jsx("main", { className: "lk-shell-body", children }),
|
|
3661
|
-
footer && /* @__PURE__ */ jsx("footer", { className: "lk-shell-footer", children: footer })
|
|
3662
|
-
] })
|
|
3663
|
-
);
|
|
3664
|
-
return outer ? shell : /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, mode: resolved, children: shell });
|
|
2234
|
+
return /* @__PURE__ */ jsxs(LabRoot, { mode, className: "lk-shell", children: [
|
|
2235
|
+
/* @__PURE__ */ jsxs("header", { className: "lk-shell-header", children: [
|
|
2236
|
+
pages && pages.length > 0 ? /* @__PURE__ */ jsx(LabSwitcher, { title, pages, path, className: "lk-shell-title" }) : /* @__PURE__ */ jsx("h1", { className: "lk-shell-title", children: title }),
|
|
2237
|
+
header && /* @__PURE__ */ jsx("div", { className: "lk-shell-header-actions", children: header })
|
|
2238
|
+
] }),
|
|
2239
|
+
/* @__PURE__ */ jsx("main", { className: "lk-shell-body", children }),
|
|
2240
|
+
footer && /* @__PURE__ */ jsx("footer", { className: "lk-shell-footer", children: footer })
|
|
2241
|
+
] });
|
|
3665
2242
|
}
|
|
3666
|
-
var
|
|
3667
|
-
var
|
|
2243
|
+
var ZONE_ID = asNodeId("lk-workspace");
|
|
2244
|
+
var STRATEGIES = { grid: gridStrategy, floating: floatingStrategy };
|
|
3668
2245
|
var FLOAT_ZONE_ID = asNodeId("lk-workspace-floating");
|
|
3669
2246
|
var PANEL_KIND = "panel";
|
|
3670
2247
|
var KIND = "trial";
|
|
3671
2248
|
function extentOf(store, id) {
|
|
3672
|
-
const
|
|
3673
|
-
if (!
|
|
2249
|
+
const p2 = store.getNode(id)?.membership?.placement;
|
|
2250
|
+
if (!p2) return null;
|
|
3674
2251
|
const out = {};
|
|
3675
|
-
if (
|
|
3676
|
-
if (
|
|
2252
|
+
if (p2.size) out.size = p2.size;
|
|
2253
|
+
if (p2.span) out.span = p2.span;
|
|
3677
2254
|
return Object.keys(out).length > 0 ? out : null;
|
|
3678
2255
|
}
|
|
3679
2256
|
function PanelFrame({ panel, floating = false }) {
|
|
@@ -3694,11 +2271,11 @@ function Workspace({
|
|
|
3694
2271
|
viewport
|
|
3695
2272
|
}) {
|
|
3696
2273
|
const items = Children.toArray(children);
|
|
3697
|
-
const tilePanels = useMemo(() => (panels ?? []).filter((
|
|
3698
|
-
const floatPanels = useMemo(() => (panels ?? []).filter((
|
|
2274
|
+
const tilePanels = useMemo(() => (panels ?? []).filter((p2) => p2.as === "tile"), [panels]);
|
|
2275
|
+
const floatPanels = useMemo(() => (panels ?? []).filter((p2) => p2.as === "floating"), [panels]);
|
|
3699
2276
|
const wantedIds = [
|
|
3700
|
-
...items.map((_,
|
|
3701
|
-
...tilePanels.map((
|
|
2277
|
+
...items.map((_, i3) => ids?.[i3] ?? `lk-ws-${i3}`),
|
|
2278
|
+
...tilePanels.map((p2) => `lk-panel-${p2.key}`)
|
|
3702
2279
|
];
|
|
3703
2280
|
const idKey = wantedIds.join(",");
|
|
3704
2281
|
const idKeyRef = useRef(null);
|
|
@@ -3718,7 +2295,7 @@ function Workspace({
|
|
|
3718
2295
|
store2.registerNode(
|
|
3719
2296
|
createNode({
|
|
3720
2297
|
kind: "zone",
|
|
3721
|
-
id:
|
|
2298
|
+
id: ZONE_ID,
|
|
3722
2299
|
container: { strategyId: "grid", config: { resizable, gap, padding } }
|
|
3723
2300
|
})
|
|
3724
2301
|
);
|
|
@@ -3734,10 +2311,10 @@ function Workspace({
|
|
|
3734
2311
|
const store = storeRef.current;
|
|
3735
2312
|
const surface = useSurfaceOptional();
|
|
3736
2313
|
useLayoutEffect(() => {
|
|
3737
|
-
store.updateContainerConfig(
|
|
2314
|
+
store.updateContainerConfig(ZONE_ID, { resizable, gap, padding });
|
|
3738
2315
|
}, [store, resizable, gap, padding]);
|
|
3739
2316
|
useLayoutEffect(() => {
|
|
3740
|
-
const present = new Set(store.getContainerView(
|
|
2317
|
+
const present = new Set(store.getContainerView(ZONE_ID)?.childOrder ?? []);
|
|
3741
2318
|
const wanted = new Set(nodeIds);
|
|
3742
2319
|
for (const id of present) {
|
|
3743
2320
|
if (!wanted.has(id)) store.unregisterNode(id);
|
|
@@ -3745,12 +2322,12 @@ function Workspace({
|
|
|
3745
2322
|
for (const id of nodeIds) {
|
|
3746
2323
|
if (present.has(id)) continue;
|
|
3747
2324
|
const kind = String(id).startsWith("lk-panel-") ? PANEL_KIND : KIND;
|
|
3748
|
-
store.registerNode(createNode({ kind, id, parentId:
|
|
2325
|
+
store.registerNode(createNode({ kind, id, parentId: ZONE_ID, focus: true }));
|
|
3749
2326
|
store.showNode(id);
|
|
3750
2327
|
const saved = layoutRef.current?.[id];
|
|
3751
2328
|
if (saved) store.patchPlacement(id, saved);
|
|
3752
2329
|
}
|
|
3753
|
-
store.setChildOrder(
|
|
2330
|
+
store.setChildOrder(ZONE_ID, [...nodeIds]);
|
|
3754
2331
|
}, [store, nodeIds]);
|
|
3755
2332
|
useEffect(() => {
|
|
3756
2333
|
if (!surface) return;
|
|
@@ -3761,7 +2338,7 @@ function Workspace({
|
|
|
3761
2338
|
if (!onLayoutChange) return;
|
|
3762
2339
|
return store.events.on("node.placementChanged", () => {
|
|
3763
2340
|
const next = {};
|
|
3764
|
-
for (const child of store.getChildren(
|
|
2341
|
+
for (const child of store.getChildren(ZONE_ID)) {
|
|
3765
2342
|
const extent = extentOf(store, child.id);
|
|
3766
2343
|
if (extent) next[child.id] = extent;
|
|
3767
2344
|
}
|
|
@@ -3773,9 +2350,9 @@ function Workspace({
|
|
|
3773
2350
|
[onReorder]
|
|
3774
2351
|
);
|
|
3775
2352
|
const chrome = useMemo(() => {
|
|
3776
|
-
const byId = new Map(nodeIds.map((id,
|
|
2353
|
+
const byId = new Map(nodeIds.map((id, i3) => [id, items[i3]]));
|
|
3777
2354
|
const panelByNode = new Map(
|
|
3778
|
-
tilePanels.map((
|
|
2355
|
+
tilePanels.map((p2) => [`lk-panel-${p2.key}`, p2])
|
|
3779
2356
|
);
|
|
3780
2357
|
return {
|
|
3781
2358
|
[PANEL_KIND]: ({ node }) => {
|
|
@@ -3793,11 +2370,11 @@ function Workspace({
|
|
|
3793
2370
|
}
|
|
3794
2371
|
};
|
|
3795
2372
|
}, [nodeIds, items, reorderable, tilePanels]);
|
|
3796
|
-
const floatingLayer = floatPanels.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "lk-workspace__floating", children: floatPanels.map((
|
|
2373
|
+
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)) });
|
|
3797
2374
|
const grid = /* @__PURE__ */ jsx(
|
|
3798
2375
|
Container,
|
|
3799
2376
|
{
|
|
3800
|
-
parentId:
|
|
2377
|
+
parentId: ZONE_ID,
|
|
3801
2378
|
chrome,
|
|
3802
2379
|
className: "lk-workspace windease-zone",
|
|
3803
2380
|
affordances: resizable,
|
|
@@ -3805,7 +2382,7 @@ function Workspace({
|
|
|
3805
2382
|
onChildOrderChange: reorderable ? commitOrder : void 0
|
|
3806
2383
|
}
|
|
3807
2384
|
);
|
|
3808
|
-
return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies:
|
|
2385
|
+
return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES, children: floatingLayer ? (
|
|
3809
2386
|
// The floating layer insets against the workspace, so it needs a
|
|
3810
2387
|
// positioned box around both. Without one it insets against the page
|
|
3811
2388
|
// and lands over the lab header.
|
|
@@ -3815,23 +2392,59 @@ function Workspace({
|
|
|
3815
2392
|
] })
|
|
3816
2393
|
) : reorderable ? /* @__PURE__ */ jsx(DragProvider, { children: grid }) : grid }) });
|
|
3817
2394
|
}
|
|
3818
|
-
function
|
|
3819
|
-
|
|
2395
|
+
function seedDefaultTrial(store, instruments, defaultInstrument) {
|
|
2396
|
+
if (store.getState().trials.length > 0) return;
|
|
2397
|
+
const record = addTrial([], instruments, defaultInstrument)[0];
|
|
2398
|
+
if (!record) return;
|
|
2399
|
+
const { undoStack: _undoStack, ...rest } = record;
|
|
2400
|
+
store.getState().addTrial(rest);
|
|
2401
|
+
}
|
|
2402
|
+
function openUnstoredLab({ instruments, defaultInstrument, mode }) {
|
|
2403
|
+
const store = createLabStore({ initialMode: mode ?? "auto", instruments });
|
|
2404
|
+
seedDefaultTrial(store, instruments, defaultInstrument);
|
|
2405
|
+
const records = createRecordCache({ storage: noneAdapter, prefix: "", writable: false });
|
|
2406
|
+
return { store, records, close: () => records.close(), marks: /* @__PURE__ */ new Map() };
|
|
2407
|
+
}
|
|
2408
|
+
async function openStoredLab({ instruments, defaultInstrument, mode }, storageKey, storage) {
|
|
2409
|
+
const opened = await openLabStore({
|
|
3820
2410
|
storageKey,
|
|
3821
|
-
storage,
|
|
3822
|
-
initialMode,
|
|
3823
|
-
|
|
3824
|
-
configDefaults: configDefaultsOf(instruments)
|
|
2411
|
+
storage: storage ?? await defaultStorage(),
|
|
2412
|
+
initialMode: mode ?? "auto",
|
|
2413
|
+
instruments
|
|
3825
2414
|
});
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
const
|
|
3831
|
-
|
|
2415
|
+
seedDefaultTrial(opened.store, instruments, defaultInstrument);
|
|
2416
|
+
const marks = /* @__PURE__ */ new Map();
|
|
2417
|
+
await Promise.all(
|
|
2418
|
+
opened.store.getState().trials.map(async (trial) => {
|
|
2419
|
+
const kept = instruments.find((i3) => i3.name === trial.instrumentName)?.annotations?.storage;
|
|
2420
|
+
if (!kept) return;
|
|
2421
|
+
try {
|
|
2422
|
+
marks.set(trial.id, await kept.load());
|
|
2423
|
+
} catch (error) {
|
|
2424
|
+
console.warn(`[labkit] could not load the marks of trial "${trial.id}"`, error);
|
|
2425
|
+
marks.set(trial.id, null);
|
|
2426
|
+
}
|
|
2427
|
+
})
|
|
2428
|
+
);
|
|
2429
|
+
return { ...opened, marks };
|
|
2430
|
+
}
|
|
2431
|
+
function LabFallback({
|
|
2432
|
+
title,
|
|
2433
|
+
mode,
|
|
2434
|
+
pages,
|
|
2435
|
+
path
|
|
2436
|
+
}) {
|
|
2437
|
+
const resolvedMode = useResolvedMode(mode ?? "auto");
|
|
2438
|
+
return /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, selection: { mode: resolvedMode }, className: "lk-lab", children: /* @__PURE__ */ jsx(
|
|
2439
|
+
LabShell,
|
|
2440
|
+
{
|
|
2441
|
+
title: title ?? "Labkit",
|
|
2442
|
+
mode,
|
|
2443
|
+
...pages ? { pages } : {},
|
|
2444
|
+
...path !== void 0 ? { path } : {},
|
|
2445
|
+
children: null
|
|
3832
2446
|
}
|
|
3833
|
-
}
|
|
3834
|
-
return store;
|
|
2447
|
+
) });
|
|
3835
2448
|
}
|
|
3836
2449
|
var NEBULA_SLOTS = [
|
|
3837
2450
|
{ cx: "18%", cy: "30%", sx: "60%", sy: "80%", stop: "70%" },
|
|
@@ -3841,73 +2454,181 @@ var NEBULA_SLOTS = [
|
|
|
3841
2454
|
{ cx: "85%", cy: "18%", sx: "55%", sy: "80%", stop: "70%" }
|
|
3842
2455
|
];
|
|
3843
2456
|
function buildNebula(colors) {
|
|
3844
|
-
const blobs = colors.map((
|
|
3845
|
-
const
|
|
3846
|
-
return `radial-gradient(ellipse ${
|
|
2457
|
+
const blobs = colors.map((c8, i3) => {
|
|
2458
|
+
const p2 = NEBULA_SLOTS[i3 % NEBULA_SLOTS.length];
|
|
2459
|
+
return `radial-gradient(ellipse ${p2.sx} ${p2.sy} at ${p2.cx} ${p2.cy}, color-mix(in srgb, ${c8} 22%, transparent), transparent ${p2.stop})`;
|
|
3847
2460
|
});
|
|
3848
2461
|
blobs.push("radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)");
|
|
3849
2462
|
return blobs.join(", ");
|
|
3850
2463
|
}
|
|
3851
|
-
function
|
|
2464
|
+
function LabPanes({
|
|
2465
|
+
contributions,
|
|
2466
|
+
children
|
|
2467
|
+
}) {
|
|
2468
|
+
const surface = useSurfaceOptional();
|
|
2469
|
+
const [sidebarWidth, setSidebarWidth] = usePersistedState(
|
|
2470
|
+
"lk-lab-sidebar-width",
|
|
2471
|
+
void 0,
|
|
2472
|
+
{ scope: "lab" }
|
|
2473
|
+
);
|
|
2474
|
+
const [asideWidth, setAsideWidth] = usePersistedState(
|
|
2475
|
+
"lk-lab-aside-width",
|
|
2476
|
+
void 0,
|
|
2477
|
+
{ scope: "lab" }
|
|
2478
|
+
);
|
|
2479
|
+
const main = contributions.some((c8) => c8.region === "aside") ? /* @__PURE__ */ jsx(
|
|
2480
|
+
Split,
|
|
2481
|
+
{
|
|
2482
|
+
className: "lk-lab__panes",
|
|
2483
|
+
side: "end",
|
|
2484
|
+
sidebarClassName: "lk-lab__aside",
|
|
2485
|
+
contentClassName: "lk-lab__main",
|
|
2486
|
+
label: "Lab aside",
|
|
2487
|
+
defaultWidth: 320,
|
|
2488
|
+
width: asideWidth,
|
|
2489
|
+
onWidthChange: (w3) => {
|
|
2490
|
+
setAsideWidth(w3);
|
|
2491
|
+
surface?.invalidateRects();
|
|
2492
|
+
},
|
|
2493
|
+
sidebar: /* @__PURE__ */ jsx(LabAsideRegion, { contributions }),
|
|
2494
|
+
children
|
|
2495
|
+
}
|
|
2496
|
+
) : children;
|
|
2497
|
+
if (!contributions.some((c8) => c8.region === "sidebar")) return main;
|
|
2498
|
+
return /* @__PURE__ */ jsx(
|
|
2499
|
+
Split,
|
|
2500
|
+
{
|
|
2501
|
+
className: "lk-lab__panes",
|
|
2502
|
+
sidebarClassName: "lk-lab__sidebar",
|
|
2503
|
+
contentClassName: "lk-lab__main",
|
|
2504
|
+
label: "Lab sidebar",
|
|
2505
|
+
defaultWidth: 260,
|
|
2506
|
+
width: sidebarWidth,
|
|
2507
|
+
onWidthChange: (w3) => {
|
|
2508
|
+
setSidebarWidth(w3);
|
|
2509
|
+
surface?.invalidateRects();
|
|
2510
|
+
},
|
|
2511
|
+
sidebar: /* @__PURE__ */ jsx(LabSidebarRegion, { contributions }),
|
|
2512
|
+
children: main
|
|
2513
|
+
}
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
function Lab(props) {
|
|
2517
|
+
if (process.env.NODE_ENV !== "production" && props.instruments.length === 0) {
|
|
2518
|
+
throw new Error("[labkit] <Lab> requires a non-empty `instruments` array");
|
|
2519
|
+
}
|
|
2520
|
+
const { storageKey, storage } = props;
|
|
2521
|
+
useWarnIgnoredChange("<Lab>", { storageKey, storage });
|
|
2522
|
+
const opened = useOpenOnce(
|
|
2523
|
+
() => storageKey === void 0 ? openUnstoredLab(props) : openStoredLab(props, storageKey, storage)
|
|
2524
|
+
);
|
|
2525
|
+
if (!opened) {
|
|
2526
|
+
if (props.fallback !== void 0) return props.fallback;
|
|
2527
|
+
return /* @__PURE__ */ jsx(LabFallback, { title: props.title, mode: props.mode, pages: props.pages, path: props.path });
|
|
2528
|
+
}
|
|
2529
|
+
return /* @__PURE__ */ jsx(LabRuntime, { ...props, opened });
|
|
2530
|
+
}
|
|
2531
|
+
function LabRuntime({
|
|
3852
2532
|
instruments,
|
|
3853
|
-
defaultInstrument,
|
|
3854
|
-
storage,
|
|
3855
|
-
storageKey,
|
|
3856
2533
|
mode,
|
|
3857
2534
|
nebula,
|
|
3858
2535
|
title,
|
|
2536
|
+
pages,
|
|
2537
|
+
path,
|
|
3859
2538
|
footer,
|
|
3860
2539
|
chrome,
|
|
2540
|
+
labChrome,
|
|
3861
2541
|
suppress,
|
|
2542
|
+
addTrial: addTrial2,
|
|
3862
2543
|
tools,
|
|
3863
2544
|
configRules,
|
|
3864
2545
|
controls,
|
|
3865
|
-
children
|
|
2546
|
+
children,
|
|
2547
|
+
opened
|
|
3866
2548
|
}) {
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
);
|
|
3879
|
-
}
|
|
3880
|
-
const store = storeRef.current;
|
|
3881
|
-
const trials = useStore(store, (s) => s.trials);
|
|
3882
|
-
const savedSnapshots = useStore(store, (s) => s.savedSnapshots);
|
|
3883
|
-
const modeValue = useStore(store, (s) => s.mode);
|
|
3884
|
-
const layout = useStore(store, (s) => s.layout);
|
|
3885
|
-
const undockedPanels = useStore(store, (s) => s.undockedPanels);
|
|
2549
|
+
const { store } = opened;
|
|
2550
|
+
useLayoutEffect(() => {
|
|
2551
|
+
store.getState().setInstruments(instruments);
|
|
2552
|
+
}, [instruments, store]);
|
|
2553
|
+
const trials = useStore(store, (s5) => s5.trials);
|
|
2554
|
+
const [focusPick, setFocusPick] = useState(null);
|
|
2555
|
+
const focusedTrialId = trials.some((t) => t.id === focusPick) ? focusPick : trials[0]?.id ?? null;
|
|
2556
|
+
const savedSnapshots = useStore(store, (s5) => s5.savedSnapshots);
|
|
2557
|
+
const modeValue = useStore(store, (s5) => s5.mode);
|
|
2558
|
+
const layout = useStore(store, (s5) => s5.layout);
|
|
2559
|
+
const undockedPanels = useStore(store, (s5) => s5.undockedPanels);
|
|
3886
2560
|
const panelHostsRef = useRef(null);
|
|
3887
2561
|
if (panelHostsRef.current === null) panelHostsRef.current = createPanelHostRegistry();
|
|
3888
2562
|
const outerSurface = useSurfaceOptional();
|
|
3889
|
-
const
|
|
3890
|
-
const
|
|
3891
|
-
const
|
|
2563
|
+
const outerOver = useSurfaceCanvas("over");
|
|
2564
|
+
const outerUnder = useSurfaceCanvas("under");
|
|
2565
|
+
const [ownOver, setOwnOver] = useState(null);
|
|
2566
|
+
const [ownUnder, setOwnUnder] = useState(null);
|
|
2567
|
+
const ownOverRef = useRef(null);
|
|
2568
|
+
const ownUnderRef = useRef(null);
|
|
3892
2569
|
const bufferRef = useRef({ w: 0, h: 0 });
|
|
3893
2570
|
const surfaceRef = useRef(null);
|
|
3894
2571
|
const onFrame = useCallback((frame) => {
|
|
3895
|
-
const
|
|
3896
|
-
if (
|
|
2572
|
+
const canvases = [ownUnderRef.current, ownOverRef.current].filter((c8) => c8 !== null);
|
|
2573
|
+
if (canvases.length === 0) return;
|
|
3897
2574
|
const w3 = Math.round(frame.size.width * frame.dpr);
|
|
3898
|
-
const
|
|
3899
|
-
if (bufferRef.current.w
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
2575
|
+
const h2 = Math.round(frame.size.height * frame.dpr);
|
|
2576
|
+
if (bufferRef.current.w !== w3 || bufferRef.current.h !== h2) {
|
|
2577
|
+
bufferRef.current = { w: w3, h: h2 };
|
|
2578
|
+
for (const c8 of canvases) {
|
|
2579
|
+
c8.width = w3;
|
|
2580
|
+
c8.height = h2;
|
|
2581
|
+
c8.style.width = `${frame.size.width}px`;
|
|
2582
|
+
c8.style.height = `${frame.size.height}px`;
|
|
2583
|
+
}
|
|
2584
|
+
surfaceRef.current?.invalidateAll();
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
3906
2587
|
}, []);
|
|
3907
2588
|
const ownSurface = useTiledSurface({ onFrame });
|
|
3908
2589
|
surfaceRef.current = ownSurface;
|
|
2590
|
+
const [labBody, setLabBody] = useState(null);
|
|
2591
|
+
const attachOwnSurface = ownSurface.containerRef;
|
|
2592
|
+
const labBodyRef = useCallback(
|
|
2593
|
+
(el) => {
|
|
2594
|
+
setLabBody(el);
|
|
2595
|
+
if (!outerSurface) attachOwnSurface(el);
|
|
2596
|
+
},
|
|
2597
|
+
[outerSurface, attachOwnSurface]
|
|
2598
|
+
);
|
|
2599
|
+
useLabFitWarning(labBody);
|
|
2600
|
+
useEffect(() => {
|
|
2601
|
+
if (!labBody) return;
|
|
2602
|
+
const pick = (node) => {
|
|
2603
|
+
const owned = new Set(store.getState().trials.map((t) => t.id));
|
|
2604
|
+
const nearest = (from) => from?.closest(".lk-trial[data-trial-id]") ?? null;
|
|
2605
|
+
let trial = typeof node?.closest === "function" ? nearest(node) : null;
|
|
2606
|
+
while (trial && !owned.has(trial.dataset.trialId ?? "")) trial = nearest(trial.parentElement);
|
|
2607
|
+
if (trial?.dataset.trialId) setFocusPick(trial.dataset.trialId);
|
|
2608
|
+
};
|
|
2609
|
+
const onInput = (event) => pick(event.target);
|
|
2610
|
+
let pending;
|
|
2611
|
+
const doc = labBody.ownerDocument;
|
|
2612
|
+
const win = doc.defaultView;
|
|
2613
|
+
const onBlur = () => {
|
|
2614
|
+
clearTimeout(pending);
|
|
2615
|
+
pending = setTimeout(() => pick(doc.activeElement), 0);
|
|
2616
|
+
};
|
|
2617
|
+
labBody.addEventListener("pointerdown", onInput, true);
|
|
2618
|
+
labBody.addEventListener("focusin", onInput, true);
|
|
2619
|
+
win?.addEventListener("blur", onBlur);
|
|
2620
|
+
return () => {
|
|
2621
|
+
clearTimeout(pending);
|
|
2622
|
+
labBody.removeEventListener("pointerdown", onInput, true);
|
|
2623
|
+
labBody.removeEventListener("focusin", onInput, true);
|
|
2624
|
+
win?.removeEventListener("blur", onBlur);
|
|
2625
|
+
};
|
|
2626
|
+
}, [labBody, store]);
|
|
3909
2627
|
const surface = outerSurface ?? ownSurface;
|
|
3910
|
-
const
|
|
2628
|
+
const surfaceCanvases = useMemo(
|
|
2629
|
+
() => outerSurface ? { over: outerOver, under: outerUnder } : { over: ownOver, under: ownUnder },
|
|
2630
|
+
[outerSurface, outerOver, outerUnder, ownOver, ownUnder]
|
|
2631
|
+
);
|
|
3911
2632
|
const workspacePanels = useMemo(
|
|
3912
2633
|
() => Object.entries(undockedPanels).map(([key, panel]) => ({
|
|
3913
2634
|
key,
|
|
@@ -3917,6 +2638,13 @@ function Lab({
|
|
|
3917
2638
|
[undockedPanels]
|
|
3918
2639
|
);
|
|
3919
2640
|
const resolvedMode = useResolvedMode(modeValue);
|
|
2641
|
+
const annotates = instruments.some((i3) => i3.annotations != null);
|
|
2642
|
+
const labChromeAll = useMemo(
|
|
2643
|
+
() => labContributions([...annotates ? ANNOTATION_TOOLS : [], ...tools ?? []], labChrome),
|
|
2644
|
+
[annotates, tools, labChrome]
|
|
2645
|
+
);
|
|
2646
|
+
const hasFooterChrome = labChromeAll.some((c8) => c8.region === "footer");
|
|
2647
|
+
const hasPaneChrome = labChromeAll.some((c8) => c8.region === "sidebar" || c8.region === "aside");
|
|
3920
2648
|
useEffect(() => {
|
|
3921
2649
|
if (mode && mode !== store.getState().mode) {
|
|
3922
2650
|
store.getState().setMode(mode);
|
|
@@ -3927,18 +2655,23 @@ function Lab({
|
|
|
3927
2655
|
const currentById = new Map(store.getState().trials.map((w3) => [w3.id, w3]));
|
|
3928
2656
|
const merged = next.map((w3) => currentById.get(w3.id) ?? w3);
|
|
3929
2657
|
store.setState({ trials: merged });
|
|
3930
|
-
|
|
2658
|
+
};
|
|
2659
|
+
const replaceAndFocusAdded = (next) => {
|
|
2660
|
+
const before = new Set(store.getState().trials.map((w3) => w3.id));
|
|
2661
|
+
replaceTrials(next);
|
|
2662
|
+
const added = next.find((w3) => !before.has(w3.id));
|
|
2663
|
+
if (added) setFocusPick(added.id);
|
|
3931
2664
|
};
|
|
3932
2665
|
return {
|
|
3933
2666
|
instruments,
|
|
3934
2667
|
trials,
|
|
3935
2668
|
addTrial: (instrumentName, options) => {
|
|
3936
|
-
|
|
3937
|
-
|
|
2669
|
+
replaceAndFocusAdded(
|
|
2670
|
+
addTrial(store.getState().trials, instruments, instrumentName, options)
|
|
2671
|
+
);
|
|
3938
2672
|
},
|
|
3939
2673
|
cloneTrial: (id) => {
|
|
3940
|
-
|
|
3941
|
-
replaceTrials(next);
|
|
2674
|
+
replaceAndFocusAdded(cloneTrial(store.getState().trials, id));
|
|
3942
2675
|
},
|
|
3943
2676
|
closeTrial: (id) => {
|
|
3944
2677
|
const next = closeTrial(store.getState().trials, id);
|
|
@@ -3947,20 +2680,31 @@ function Lab({
|
|
|
3947
2680
|
reorderTrials: (ids) => {
|
|
3948
2681
|
replaceTrials(reorderTrials(store.getState().trials, ids));
|
|
3949
2682
|
},
|
|
2683
|
+
swapTrial: (id, instrumentName, options) => {
|
|
2684
|
+
const current = store.getState();
|
|
2685
|
+
const next = swapTrial(current.trials, instruments, id, instrumentName, options);
|
|
2686
|
+
if (next === current.trials) return;
|
|
2687
|
+
const record = next[current.trials.findIndex((w3) => w3.id === id)];
|
|
2688
|
+
const { [id]: extent, ...layoutRest } = current.layout;
|
|
2689
|
+
if (extent) current.setLayout({ ...layoutRest, [record.id]: extent });
|
|
2690
|
+
replaceTrials(next);
|
|
2691
|
+
if (focusedTrialId === id) setFocusPick(record.id);
|
|
2692
|
+
},
|
|
2693
|
+
focusedTrialId,
|
|
2694
|
+
focusTrial: (id) => setFocusPick(id),
|
|
3950
2695
|
resetTrial: (id) => {
|
|
3951
2696
|
const next = resetTrial(store.getState().trials, id, instruments);
|
|
3952
2697
|
const record = next.find((w3) => w3.id === id);
|
|
3953
2698
|
if (!record) return;
|
|
3954
|
-
store.setState((
|
|
3955
|
-
trials:
|
|
2699
|
+
store.setState((s5) => ({
|
|
2700
|
+
trials: s5.trials.map(
|
|
3956
2701
|
(w3) => w3.id === id ? { ...w3, config: record.config, state: record.state, view: record.view } : w3
|
|
3957
2702
|
)
|
|
3958
2703
|
}));
|
|
3959
|
-
store.getState().updateTrialView(id, record.view);
|
|
3960
2704
|
},
|
|
3961
2705
|
savedSnapshots,
|
|
3962
2706
|
saveSnapshot: (trialId, name) => {
|
|
3963
|
-
const taken = store.getState().savedSnapshots.filter((
|
|
2707
|
+
const taken = store.getState().savedSnapshots.filter((s5) => s5.trialId === trialId).length;
|
|
3964
2708
|
store.getState().saveSnapshot(trialId, name ?? `Snapshot ${taken + 1}`);
|
|
3965
2709
|
},
|
|
3966
2710
|
loadSnapshot: (trialId, snapshotId) => {
|
|
@@ -3970,19 +2714,41 @@ function Lab({
|
|
|
3970
2714
|
store.getState().deleteSnapshot(snapshotId);
|
|
3971
2715
|
},
|
|
3972
2716
|
mode: modeValue,
|
|
3973
|
-
setMode: (
|
|
3974
|
-
store.getState().setMode(
|
|
2717
|
+
setMode: (m2) => {
|
|
2718
|
+
store.getState().setMode(m2);
|
|
3975
2719
|
},
|
|
3976
2720
|
configRules,
|
|
3977
2721
|
controls
|
|
3978
2722
|
};
|
|
3979
|
-
}, [
|
|
2723
|
+
}, [
|
|
2724
|
+
instruments,
|
|
2725
|
+
trials,
|
|
2726
|
+
focusedTrialId,
|
|
2727
|
+
savedSnapshots,
|
|
2728
|
+
modeValue,
|
|
2729
|
+
store,
|
|
2730
|
+
configRules,
|
|
2731
|
+
controls
|
|
2732
|
+
]);
|
|
3980
2733
|
const backdropStyle = resolvedMode === "dark" && nebula && nebula.length > 0 ? { ["--wzl-backdrop"]: buildNebula(nebula) } : void 0;
|
|
3981
|
-
|
|
2734
|
+
const workspace = /* @__PURE__ */ jsx(
|
|
2735
|
+
Workspace,
|
|
2736
|
+
{
|
|
2737
|
+
panels: workspacePanels,
|
|
2738
|
+
ids: trials.map((w3) => w3.id),
|
|
2739
|
+
resizable: true,
|
|
2740
|
+
reorderable: true,
|
|
2741
|
+
onReorder: (ids) => contextValue.reorderTrials(ids),
|
|
2742
|
+
layout,
|
|
2743
|
+
onLayoutChange: (next) => store.getState().setLayout(next),
|
|
2744
|
+
children: trials.map((w3) => /* @__PURE__ */ jsx(Trial, { id: w3.id, chrome, suppress }, w3.id))
|
|
2745
|
+
}
|
|
2746
|
+
);
|
|
2747
|
+
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(
|
|
3982
2748
|
ThemeProvider,
|
|
3983
2749
|
{
|
|
3984
2750
|
theme: interstellarTheme,
|
|
3985
|
-
mode: resolvedMode,
|
|
2751
|
+
selection: { mode: resolvedMode },
|
|
3986
2752
|
className: "lk-lab",
|
|
3987
2753
|
style: backdropStyle,
|
|
3988
2754
|
children: /* @__PURE__ */ jsx(
|
|
@@ -3990,55 +2756,813 @@ function Lab({
|
|
|
3990
2756
|
{
|
|
3991
2757
|
title: title ?? "Labkit",
|
|
3992
2758
|
mode: modeValue,
|
|
3993
|
-
|
|
2759
|
+
...pages ? { pages } : {},
|
|
2760
|
+
...path !== void 0 ? { path } : {},
|
|
2761
|
+
footer: hasFooterChrome ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2762
|
+
footer,
|
|
2763
|
+
/* @__PURE__ */ jsx(LabFooterRegion, { contributions: labChromeAll })
|
|
2764
|
+
] }) : footer,
|
|
3994
2765
|
header: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3995
|
-
/* @__PURE__ */ jsx(LabHeader, {}),
|
|
3996
|
-
children
|
|
2766
|
+
/* @__PURE__ */ jsx(LabHeader, { ...addTrial2 !== void 0 ? { addTrial: addTrial2 } : {} }),
|
|
2767
|
+
children,
|
|
2768
|
+
/* @__PURE__ */ jsx(LabHeaderRegion, { contributions: labChromeAll })
|
|
3997
2769
|
] }),
|
|
3998
|
-
children: /* @__PURE__ */ jsx(PanelHostContext.Provider, { value: panelHostsRef.current, children: /* @__PURE__ */ jsx(SurfaceContext.Provider, { value: surface, children: /* @__PURE__ */ jsx(SurfaceCanvasContext.Provider, { value:
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
"
|
|
4010
|
-
{
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
ownCanvasRef.current = el;
|
|
4014
|
-
setOwnCanvas(el);
|
|
4015
|
-
}
|
|
2770
|
+
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: [
|
|
2771
|
+
outerSurface ? null : (
|
|
2772
|
+
// Two buffers stacked around the trials, both inert —
|
|
2773
|
+
// each tile takes input from its own box. A tile's marks
|
|
2774
|
+
// annotate the instrument's DOM from over it; an opaque
|
|
2775
|
+
// renderer sits under it, so the pane can still hold a
|
|
2776
|
+
// label. The under one is first so it paints first.
|
|
2777
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2778
|
+
/* @__PURE__ */ jsx(
|
|
2779
|
+
"canvas",
|
|
2780
|
+
{
|
|
2781
|
+
className: "lk-lab__surface lk-lab__surface--under",
|
|
2782
|
+
ref: (el) => {
|
|
2783
|
+
ownUnderRef.current = el;
|
|
2784
|
+
setOwnUnder(el);
|
|
4016
2785
|
}
|
|
4017
|
-
|
|
2786
|
+
}
|
|
4018
2787
|
),
|
|
4019
|
-
tools ? /* @__PURE__ */ jsx(LabPalette, { tools }) : null,
|
|
4020
2788
|
/* @__PURE__ */ jsx(
|
|
4021
|
-
|
|
2789
|
+
"canvas",
|
|
4022
2790
|
{
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
layout,
|
|
4029
|
-
onLayoutChange: (next) => store.getState().setLayout(next),
|
|
4030
|
-
children: trials.map((w3) => /* @__PURE__ */ jsx(Trial, { id: w3.id, chrome, suppress }, w3.id))
|
|
2791
|
+
className: "lk-lab__surface lk-lab__surface--over",
|
|
2792
|
+
ref: (el) => {
|
|
2793
|
+
ownOverRef.current = el;
|
|
2794
|
+
setOwnOver(el);
|
|
2795
|
+
}
|
|
4031
2796
|
}
|
|
4032
2797
|
)
|
|
4033
|
-
]
|
|
4034
|
-
|
|
4035
|
-
|
|
2798
|
+
] })
|
|
2799
|
+
),
|
|
2800
|
+
hasPaneChrome ? /* @__PURE__ */ jsxs(LabPanes, { contributions: labChromeAll, children: [
|
|
2801
|
+
/* @__PURE__ */ jsx(LabPalette, { contributions: labChromeAll }),
|
|
2802
|
+
workspace
|
|
2803
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2804
|
+
/* @__PURE__ */ jsx(LabPalette, { contributions: labChromeAll }),
|
|
2805
|
+
workspace
|
|
2806
|
+
] })
|
|
2807
|
+
] }) }) }) })
|
|
4036
2808
|
}
|
|
4037
2809
|
)
|
|
4038
2810
|
}
|
|
4039
|
-
) }) });
|
|
2811
|
+
) }) }) }) });
|
|
2812
|
+
}
|
|
2813
|
+
var SPECIMEN_SECTIONS = [
|
|
2814
|
+
"Buttons & toggles",
|
|
2815
|
+
"Fields & pickers",
|
|
2816
|
+
"Panels & property rows",
|
|
2817
|
+
"Overlays & feedback",
|
|
2818
|
+
"Lists & data",
|
|
2819
|
+
"Tool chrome",
|
|
2820
|
+
"Editors & plots",
|
|
2821
|
+
"Lab chrome"
|
|
2822
|
+
];
|
|
2823
|
+
function Section({ title, children }) {
|
|
2824
|
+
const id = useId();
|
|
2825
|
+
return /* @__PURE__ */ jsxs("section", { className: "lk-specimen__section", "aria-labelledby": id, children: [
|
|
2826
|
+
/* @__PURE__ */ jsx("h2", { id, className: "lk-specimen__heading", children: title }),
|
|
2827
|
+
/* @__PURE__ */ jsx("div", { className: "lk-specimen__grid", children })
|
|
2828
|
+
] });
|
|
2829
|
+
}
|
|
2830
|
+
function Cell({ label, wide, children }) {
|
|
2831
|
+
return /* @__PURE__ */ jsxs("div", { className: wide ? "lk-specimen__cell lk-specimen__cell--wide" : "lk-specimen__cell", children: [
|
|
2832
|
+
/* @__PURE__ */ jsx("span", { className: "lk-specimen__label", children: label }),
|
|
2833
|
+
children
|
|
2834
|
+
] });
|
|
2835
|
+
}
|
|
2836
|
+
function Dot() {
|
|
2837
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": "true", children: /* @__PURE__ */ jsx("circle", { cx: 8, cy: 8, r: 4, fill: "currentColor" }) });
|
|
2838
|
+
}
|
|
2839
|
+
var LEGEND = [
|
|
2840
|
+
{ key: "contour", label: "contour", color: "var(--wzl-fg-muted)" },
|
|
2841
|
+
{ key: "floor", label: "floor", color: "var(--wzl-fg-subtle)", mark: "dash" },
|
|
2842
|
+
{ key: "authored", label: "authored", color: "var(--wzl-success)", mark: "dot" },
|
|
2843
|
+
{ key: "replaced", label: "replaced", color: "var(--wzl-danger)", mark: "band" }
|
|
2844
|
+
];
|
|
2845
|
+
var PREFS = {
|
|
2846
|
+
name: "Preferences",
|
|
2847
|
+
children: {
|
|
2848
|
+
canvas: {
|
|
2849
|
+
name: "Canvas",
|
|
2850
|
+
children: {
|
|
2851
|
+
showGrid: {
|
|
2852
|
+
kind: "boolean",
|
|
2853
|
+
name: "Show grid",
|
|
2854
|
+
description: "Draw a grid behind the scene.",
|
|
2855
|
+
default: true
|
|
2856
|
+
},
|
|
2857
|
+
zoomStep: {
|
|
2858
|
+
kind: "number",
|
|
2859
|
+
name: "Zoom step",
|
|
2860
|
+
description: "Percent the zoom controls move per click.",
|
|
2861
|
+
default: 10,
|
|
2862
|
+
min: 1,
|
|
2863
|
+
max: 50
|
|
2864
|
+
},
|
|
2865
|
+
format: {
|
|
2866
|
+
kind: "enum",
|
|
2867
|
+
name: "Format",
|
|
2868
|
+
description: "File type for exported images.",
|
|
2869
|
+
default: "svg",
|
|
2870
|
+
options: [
|
|
2871
|
+
{ value: "svg", label: "SVG" },
|
|
2872
|
+
{ value: "png", label: "PNG" }
|
|
2873
|
+
]
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
};
|
|
2879
|
+
var JOB = {
|
|
2880
|
+
status: "running",
|
|
2881
|
+
done: 34,
|
|
2882
|
+
total: 120,
|
|
2883
|
+
failures: [],
|
|
2884
|
+
error: null,
|
|
2885
|
+
start: () => {
|
|
2886
|
+
},
|
|
2887
|
+
cancel: () => {
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
function setAt(values, path, value) {
|
|
2891
|
+
const [head, ...rest] = path.split(".");
|
|
2892
|
+
if (head === void 0) return values;
|
|
2893
|
+
if (rest.length === 0) return { ...values, [head]: value };
|
|
2894
|
+
const inner = values[head] ?? {};
|
|
2895
|
+
return { ...values, [head]: setAt(inner, rest.join("."), value) };
|
|
2896
|
+
}
|
|
2897
|
+
function ButtonsAndToggles() {
|
|
2898
|
+
const [snap, setSnap] = useState(true);
|
|
2899
|
+
const [wifi, setWifi] = useState(false);
|
|
2900
|
+
const [align, setAlign] = useState("center");
|
|
2901
|
+
const [bold, setBold] = useState(true);
|
|
2902
|
+
const [italic, setItalic] = useState(false);
|
|
2903
|
+
const [open, setOpen] = useState(true);
|
|
2904
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Buttons & toggles", children: [
|
|
2905
|
+
/* @__PURE__ */ jsx(Cell, { label: "Button", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
2906
|
+
/* @__PURE__ */ jsx(a5, { variant: "primary", children: "Save" }),
|
|
2907
|
+
/* @__PURE__ */ jsx(a5, { variant: "secondary", children: "Cancel" }),
|
|
2908
|
+
/* @__PURE__ */ jsx(a5, { variant: "ghost", children: "Ghost" }),
|
|
2909
|
+
/* @__PURE__ */ jsx(a5, { variant: "secondary", disabled: true, children: "Disabled" }),
|
|
2910
|
+
/* @__PURE__ */ jsx(a5, { variant: "secondary", iconOnly: true, ariaLabel: "Add", children: /* @__PURE__ */ jsx(Dot, {}) })
|
|
2911
|
+
] }) }),
|
|
2912
|
+
/* @__PURE__ */ jsx(Cell, { label: "Badge", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
2913
|
+
/* @__PURE__ */ jsx(Lt, { shape: "pill", tone: "accent", variant: "outline", size: "sm", children: "ACCENT" }),
|
|
2914
|
+
/* @__PURE__ */ jsx(Lt, { tone: "info", variant: "subtle", children: "INFO" }),
|
|
2915
|
+
/* @__PURE__ */ jsx(Lt, { tone: "warn", variant: "solid", children: "WARN" }),
|
|
2916
|
+
/* @__PURE__ */ jsx(Lt, { tone: "danger", variant: "solid", children: "DANGER" }),
|
|
2917
|
+
/* @__PURE__ */ jsx(Lt, { tone: "muted", children: "MUTED" })
|
|
2918
|
+
] }) }),
|
|
2919
|
+
/* @__PURE__ */ jsx(Cell, { label: "Checkbox, Switch", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
2920
|
+
/* @__PURE__ */ jsx(o2, { isSelected: snap, onChange: setSnap, children: "Snap to grid" }),
|
|
2921
|
+
/* @__PURE__ */ jsx(o2, { isIndeterminate: true, children: "Mixed" }),
|
|
2922
|
+
/* @__PURE__ */ jsx(a4, { isSelected: wifi, onChange: setWifi, children: "Wifi" })
|
|
2923
|
+
] }) }),
|
|
2924
|
+
/* @__PURE__ */ jsx(Cell, { label: "RadioGroup", children: /* @__PURE__ */ jsxs(l3, { label: "Snap mode", orientation: "horizontal", defaultValue: "grid", children: [
|
|
2925
|
+
/* @__PURE__ */ jsx(u3, { value: "off", children: "Off" }),
|
|
2926
|
+
/* @__PURE__ */ jsx(u3, { value: "grid", children: "Grid" }),
|
|
2927
|
+
/* @__PURE__ */ jsx(u3, { value: "pixel", children: "Pixel" })
|
|
2928
|
+
] }) }),
|
|
2929
|
+
/* @__PURE__ */ jsxs(Cell, { label: "ToggleBar", children: [
|
|
2930
|
+
/* @__PURE__ */ jsx(
|
|
2931
|
+
r3,
|
|
2932
|
+
{
|
|
2933
|
+
ariaLabel: "Align",
|
|
2934
|
+
value: align,
|
|
2935
|
+
onChange: setAlign,
|
|
2936
|
+
items: [
|
|
2937
|
+
{ value: "left", label: "Left" },
|
|
2938
|
+
{ value: "center", label: "Center" },
|
|
2939
|
+
{ value: "right", label: "Right" }
|
|
2940
|
+
]
|
|
2941
|
+
}
|
|
2942
|
+
),
|
|
2943
|
+
/* @__PURE__ */ jsx(
|
|
2944
|
+
r3,
|
|
2945
|
+
{
|
|
2946
|
+
ariaLabel: "Align, flat",
|
|
2947
|
+
variant: "flat",
|
|
2948
|
+
value: align,
|
|
2949
|
+
onChange: setAlign,
|
|
2950
|
+
items: [
|
|
2951
|
+
{ value: "left", label: "Left" },
|
|
2952
|
+
{ value: "center", label: "Center" },
|
|
2953
|
+
{ value: "right", label: "Right" }
|
|
2954
|
+
]
|
|
2955
|
+
}
|
|
2956
|
+
)
|
|
2957
|
+
] }),
|
|
2958
|
+
/* @__PURE__ */ jsx(Cell, { label: "OptionsBar, ActionsBar", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
2959
|
+
/* @__PURE__ */ jsx(
|
|
2960
|
+
r7,
|
|
2961
|
+
{
|
|
2962
|
+
ariaLabel: "Text style",
|
|
2963
|
+
items: [
|
|
2964
|
+
{ value: "b", label: "B", selected: bold, onChange: setBold },
|
|
2965
|
+
{ value: "i", label: "I", selected: italic, onChange: setItalic }
|
|
2966
|
+
]
|
|
2967
|
+
}
|
|
2968
|
+
),
|
|
2969
|
+
/* @__PURE__ */ jsx(
|
|
2970
|
+
r8,
|
|
2971
|
+
{
|
|
2972
|
+
items: [
|
|
2973
|
+
{ value: "undo", label: "Undo", onAction: () => {
|
|
2974
|
+
} },
|
|
2975
|
+
{ value: "redo", label: "Redo", onAction: () => {
|
|
2976
|
+
} }
|
|
2977
|
+
]
|
|
2978
|
+
}
|
|
2979
|
+
)
|
|
2980
|
+
] }) }),
|
|
2981
|
+
/* @__PURE__ */ jsx(Cell, { label: "MenuButton, Disclosure", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
2982
|
+
/* @__PURE__ */ jsx(
|
|
2983
|
+
l7,
|
|
2984
|
+
{
|
|
2985
|
+
label: "Add trial\u2026",
|
|
2986
|
+
onAction: () => {
|
|
2987
|
+
},
|
|
2988
|
+
items: [
|
|
2989
|
+
{ value: "sine", label: "Sine wave" },
|
|
2990
|
+
{ value: "annotate", label: "Annotate" }
|
|
2991
|
+
]
|
|
2992
|
+
}
|
|
2993
|
+
),
|
|
2994
|
+
/* @__PURE__ */ jsx(r6, { open, onToggle: () => setOpen((o5) => !o5), label: "Shapes" }),
|
|
2995
|
+
/* @__PURE__ */ jsx(r6, { open: false, onToggle: () => {
|
|
2996
|
+
}, label: "Down", direction: "down" })
|
|
2997
|
+
] }) }),
|
|
2998
|
+
/* @__PURE__ */ jsx(Cell, { label: "Tabs", children: /* @__PURE__ */ jsxs(o5, { children: [
|
|
2999
|
+
/* @__PURE__ */ jsxs(s5, { "aria-label": "Sections", children: [
|
|
3000
|
+
/* @__PURE__ */ jsx(c6, { id: "props", children: "Props" }),
|
|
3001
|
+
/* @__PURE__ */ jsx(c6, { id: "events", children: "Events" }),
|
|
3002
|
+
/* @__PURE__ */ jsx(c6, { id: "snapshot", children: "Snapshot" })
|
|
3003
|
+
] }),
|
|
3004
|
+
/* @__PURE__ */ jsx(l6, { id: "props", children: "Props panel." }),
|
|
3005
|
+
/* @__PURE__ */ jsx(l6, { id: "events", children: "Events panel." }),
|
|
3006
|
+
/* @__PURE__ */ jsx(l6, { id: "snapshot", children: "Snapshot panel." })
|
|
3007
|
+
] }) }),
|
|
3008
|
+
/* @__PURE__ */ jsx(Cell, { label: "Keycaps", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
3009
|
+
/* @__PURE__ */ jsx(c3, { keys: [{ label: "\u2318" }, { label: "\u21E7" }, { label: "K" }], separator: "+" }),
|
|
3010
|
+
/* @__PURE__ */ jsx(s3, { label: "K" }),
|
|
3011
|
+
/* @__PURE__ */ jsx(s3, { label: "\u2325", variant: "minimal" })
|
|
3012
|
+
] }) })
|
|
3013
|
+
] });
|
|
3014
|
+
}
|
|
3015
|
+
function FieldsAndPickers() {
|
|
3016
|
+
const [thumbs, setThumbs] = useState([{ value: 0.25 }, { value: 0.75 }]);
|
|
3017
|
+
const [rate, setRate] = useState(1);
|
|
3018
|
+
const [width, setWidth] = useState(6);
|
|
3019
|
+
const [color, setColor] = useState("#b08adbff");
|
|
3020
|
+
const [gradient, setGradient] = useState({
|
|
3021
|
+
fill: "linear-gradient",
|
|
3022
|
+
from: { x: 0.1, y: 0.1 },
|
|
3023
|
+
to: { x: 0.9, y: 0.9 },
|
|
3024
|
+
stops: [
|
|
3025
|
+
{ offset: 0, color: "#0fb5a8" },
|
|
3026
|
+
{ offset: 1, color: "#f4c43c" }
|
|
3027
|
+
]
|
|
3028
|
+
});
|
|
3029
|
+
const [paint, setPaint] = useState({ fill: "solid", color: "#b08adb" });
|
|
3030
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Fields & pickers", children: [
|
|
3031
|
+
/* @__PURE__ */ jsxs(Cell, { label: "Input", children: [
|
|
3032
|
+
/* @__PURE__ */ jsx(u2, { label: "Name", placeholder: "e.g. Pico" }),
|
|
3033
|
+
/* @__PURE__ */ jsx(
|
|
3034
|
+
u2,
|
|
3035
|
+
{
|
|
3036
|
+
label: "Size",
|
|
3037
|
+
leadingAdornment: /* @__PURE__ */ jsx("span", { children: "W" }),
|
|
3038
|
+
trailingAdornment: /* @__PURE__ */ jsx("span", { children: "px" }),
|
|
3039
|
+
defaultValue: "120"
|
|
3040
|
+
}
|
|
3041
|
+
),
|
|
3042
|
+
/* @__PURE__ */ jsx(u2, { label: "Required", isInvalid: true, errorMessage: "Name is required." })
|
|
3043
|
+
] }),
|
|
3044
|
+
/* @__PURE__ */ jsxs(Cell, { label: "NumberField, Select, ComboBox", children: [
|
|
3045
|
+
/* @__PURE__ */ jsx(g2, { label: "Opacity", minValue: 0, maxValue: 100, step: 5, defaultValue: 80 }),
|
|
3046
|
+
/* @__PURE__ */ jsx(
|
|
3047
|
+
g,
|
|
3048
|
+
{
|
|
3049
|
+
label: "Color",
|
|
3050
|
+
defaultSelectedKey: "g",
|
|
3051
|
+
options: [
|
|
3052
|
+
{ value: "r", label: "Red" },
|
|
3053
|
+
{ value: "g", label: "Green" },
|
|
3054
|
+
{ value: "b", label: "Blue" }
|
|
3055
|
+
]
|
|
3056
|
+
}
|
|
3057
|
+
),
|
|
3058
|
+
/* @__PURE__ */ jsx(
|
|
3059
|
+
y2,
|
|
3060
|
+
{
|
|
3061
|
+
label: "Filter",
|
|
3062
|
+
placeholder: "Type to filter\u2026",
|
|
3063
|
+
options: [
|
|
3064
|
+
{ value: "r", label: "Red" },
|
|
3065
|
+
{ value: "g", label: "Green" }
|
|
3066
|
+
]
|
|
3067
|
+
}
|
|
3068
|
+
)
|
|
3069
|
+
] }),
|
|
3070
|
+
/* @__PURE__ */ jsxs(Cell, { label: "RangeSlider, Slider", children: [
|
|
3071
|
+
/* @__PURE__ */ jsx(l4, { label: "Range", defaultValue: [20, 80], minValue: 0, maxValue: 100 }),
|
|
3072
|
+
/* @__PURE__ */ jsx(
|
|
3073
|
+
l4,
|
|
3074
|
+
{
|
|
3075
|
+
label: "Opacity",
|
|
3076
|
+
defaultValue: 75,
|
|
3077
|
+
minValue: 0,
|
|
3078
|
+
maxValue: 100,
|
|
3079
|
+
formatOutput: (v2) => `${v2}%`
|
|
3080
|
+
}
|
|
3081
|
+
),
|
|
3082
|
+
/* @__PURE__ */ jsx(
|
|
3083
|
+
b2,
|
|
3084
|
+
{
|
|
3085
|
+
ariaLabel: "Pair",
|
|
3086
|
+
min: 0,
|
|
3087
|
+
max: 1,
|
|
3088
|
+
step: 0.01,
|
|
3089
|
+
constraint: "ordered",
|
|
3090
|
+
thumbs,
|
|
3091
|
+
onInput: setThumbs,
|
|
3092
|
+
readoutPlacement: "inline-after"
|
|
3093
|
+
}
|
|
3094
|
+
)
|
|
3095
|
+
] }),
|
|
3096
|
+
/* @__PURE__ */ jsxs(Cell, { label: "DetentSlider, InlineRange", children: [
|
|
3097
|
+
/* @__PURE__ */ jsx(
|
|
3098
|
+
c7,
|
|
3099
|
+
{
|
|
3100
|
+
items: [0.25, 0.5, 1, 2, 4],
|
|
3101
|
+
value: rate,
|
|
3102
|
+
onChange: setRate,
|
|
3103
|
+
formatLabel: (r10) => `${r10}\xD7`,
|
|
3104
|
+
labels: "all",
|
|
3105
|
+
ariaLabel: "Playback rate"
|
|
3106
|
+
}
|
|
3107
|
+
),
|
|
3108
|
+
/* @__PURE__ */ jsx(
|
|
3109
|
+
r2,
|
|
3110
|
+
{
|
|
3111
|
+
"aria-label": "Width",
|
|
3112
|
+
min: 0,
|
|
3113
|
+
max: 20,
|
|
3114
|
+
value: width,
|
|
3115
|
+
onChange: (event) => setWidth(Number(event.target.value))
|
|
3116
|
+
}
|
|
3117
|
+
)
|
|
3118
|
+
] }),
|
|
3119
|
+
/* @__PURE__ */ jsxs(Cell, { label: "ColorField, GradientEditor", children: [
|
|
3120
|
+
/* @__PURE__ */ jsx(l2, { value: color, alpha: true, onInput: setColor, onChange: setColor, "aria-label": "Color" }),
|
|
3121
|
+
/* @__PURE__ */ jsx(C2, { value: gradient, onInput: setGradient, onChange: setGradient, kindSwitch: true })
|
|
3122
|
+
] }),
|
|
3123
|
+
/* @__PURE__ */ jsx(Cell, { label: "PaintInput", children: /* @__PURE__ */ jsx(
|
|
3124
|
+
S,
|
|
3125
|
+
{
|
|
3126
|
+
value: paint,
|
|
3127
|
+
onInput: setPaint,
|
|
3128
|
+
onChange: setPaint,
|
|
3129
|
+
allowNone: true,
|
|
3130
|
+
"aria-label": "Fill"
|
|
3131
|
+
}
|
|
3132
|
+
) })
|
|
3133
|
+
] });
|
|
3134
|
+
}
|
|
3135
|
+
function PanelsAndRows() {
|
|
3136
|
+
const [opacity, setOpacity] = useState(0.8);
|
|
3137
|
+
const [radius, setRadius] = useState(12);
|
|
3138
|
+
const [fill, setFill] = useState("#5841b8");
|
|
3139
|
+
const [alpha, setAlpha] = useState(1);
|
|
3140
|
+
const [stroke, setStroke] = useState("#2ec27e");
|
|
3141
|
+
const [count, setCount] = useState(8);
|
|
3142
|
+
const [cap, setCap] = useState("round");
|
|
3143
|
+
const [side, setSide] = useState("center");
|
|
3144
|
+
const [label, setLabel] = useState("Badge");
|
|
3145
|
+
const [visible, setVisible] = useState(true);
|
|
3146
|
+
const [prefs, setPrefs] = useState({});
|
|
3147
|
+
const [layers, setLayers] = useState([
|
|
3148
|
+
{ id: "base", label: "Base coat" },
|
|
3149
|
+
{ id: "wash", label: "Wash" }
|
|
3150
|
+
]);
|
|
3151
|
+
const [panelCollapsed, setPanelCollapsed] = useState(false);
|
|
3152
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Panels & property rows", children: [
|
|
3153
|
+
/* @__PURE__ */ jsx(Cell, { label: "PropertyPanel", children: /* @__PURE__ */ jsx(O, { title: "Shape", children: /* @__PURE__ */ jsxs(k, { children: [
|
|
3154
|
+
/* @__PURE__ */ jsx(
|
|
3155
|
+
F2,
|
|
3156
|
+
{
|
|
3157
|
+
label: "Opacity",
|
|
3158
|
+
value: opacity,
|
|
3159
|
+
min: 0,
|
|
3160
|
+
max: 1,
|
|
3161
|
+
step: 0.01,
|
|
3162
|
+
onChange: setOpacity
|
|
3163
|
+
}
|
|
3164
|
+
),
|
|
3165
|
+
/* @__PURE__ */ jsx(
|
|
3166
|
+
F2,
|
|
3167
|
+
{
|
|
3168
|
+
label: "Radius",
|
|
3169
|
+
value: radius,
|
|
3170
|
+
min: 0,
|
|
3171
|
+
max: 64,
|
|
3172
|
+
unit: "px",
|
|
3173
|
+
onChange: setRadius
|
|
3174
|
+
}
|
|
3175
|
+
),
|
|
3176
|
+
/* @__PURE__ */ jsx(
|
|
3177
|
+
L3,
|
|
3178
|
+
{
|
|
3179
|
+
label: "Fill",
|
|
3180
|
+
value: fill,
|
|
3181
|
+
onChange: setFill,
|
|
3182
|
+
alpha,
|
|
3183
|
+
onAlphaChange: setAlpha
|
|
3184
|
+
}
|
|
3185
|
+
),
|
|
3186
|
+
/* @__PURE__ */ jsx(L3, { label: "Stroke", value: stroke, onChange: setStroke }),
|
|
3187
|
+
/* @__PURE__ */ jsx(V, { label: "Count", value: count, min: 0, max: 100, step: 1, onChange: setCount }),
|
|
3188
|
+
/* @__PURE__ */ jsx(
|
|
3189
|
+
H,
|
|
3190
|
+
{
|
|
3191
|
+
label: "Line cap",
|
|
3192
|
+
value: cap,
|
|
3193
|
+
onChange: setCap,
|
|
3194
|
+
options: [
|
|
3195
|
+
{ value: "butt", label: "Butt" },
|
|
3196
|
+
{ value: "round", label: "Round" }
|
|
3197
|
+
]
|
|
3198
|
+
}
|
|
3199
|
+
),
|
|
3200
|
+
/* @__PURE__ */ jsx(
|
|
3201
|
+
U,
|
|
3202
|
+
{
|
|
3203
|
+
label: "Align",
|
|
3204
|
+
value: side,
|
|
3205
|
+
onChange: setSide,
|
|
3206
|
+
options: [
|
|
3207
|
+
{ value: "left", label: "L" },
|
|
3208
|
+
{ value: "center", label: "C" },
|
|
3209
|
+
{ value: "right", label: "R" }
|
|
3210
|
+
]
|
|
3211
|
+
}
|
|
3212
|
+
),
|
|
3213
|
+
/* @__PURE__ */ jsx(B, { label: "Label", value: label, onChange: setLabel }),
|
|
3214
|
+
/* @__PURE__ */ jsx(R2, { label: "Visible", value: visible, onChange: setVisible }),
|
|
3215
|
+
/* @__PURE__ */ jsx(J, { title: "Bevel", defaultCollapsed: true, children: /* @__PURE__ */ jsx(F2, { label: "Rings", value: 32, min: 4, max: 96, step: 1, onChange: () => {
|
|
3216
|
+
} }) })
|
|
3217
|
+
] }) }) }),
|
|
3218
|
+
/* @__PURE__ */ jsx(Cell, { label: "PrefsForm", children: /* @__PURE__ */ jsx(
|
|
3219
|
+
A2,
|
|
3220
|
+
{
|
|
3221
|
+
schema: PREFS,
|
|
3222
|
+
values: prefs,
|
|
3223
|
+
onChange: (path, value) => setPrefs((prev) => setAt(prev, path, value))
|
|
3224
|
+
}
|
|
3225
|
+
) }),
|
|
3226
|
+
/* @__PURE__ */ jsx(Cell, { label: "LayerStack", children: /* @__PURE__ */ jsx(
|
|
3227
|
+
c4,
|
|
3228
|
+
{
|
|
3229
|
+
items: layers,
|
|
3230
|
+
onReorder: (ids) => setLayers(ids.flatMap((id) => layers.filter((item) => item.id === id))),
|
|
3231
|
+
renderBody: (item) => /* @__PURE__ */ jsxs("div", { children: [
|
|
3232
|
+
"settings for ",
|
|
3233
|
+
item.label
|
|
3234
|
+
] })
|
|
3235
|
+
}
|
|
3236
|
+
) }),
|
|
3237
|
+
/* @__PURE__ */ jsx(Cell, { label: "Sidebar, SidebarPanel", children: /* @__PURE__ */ jsx("div", { className: "lk-specimen__box", children: /* @__PURE__ */ jsx(n5, { side: "right", ariaLabel: "Specimen sidebar", children: /* @__PURE__ */ jsx(
|
|
3238
|
+
s4,
|
|
3239
|
+
{
|
|
3240
|
+
title: "Selection",
|
|
3241
|
+
collapsed: panelCollapsed,
|
|
3242
|
+
onToggleCollapse: () => setPanelCollapsed((v2) => !v2),
|
|
3243
|
+
onHide: () => {
|
|
3244
|
+
},
|
|
3245
|
+
children: /* @__PURE__ */ jsx("div", { className: "lk-specimen__pad", children: "2 items selected" })
|
|
3246
|
+
}
|
|
3247
|
+
) }) }) })
|
|
3248
|
+
] });
|
|
3249
|
+
}
|
|
3250
|
+
function OverlaysAndFeedback() {
|
|
3251
|
+
const calloutAnchor = useRef(null);
|
|
3252
|
+
const [calloutOpen, setCalloutOpen] = useState(false);
|
|
3253
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
3254
|
+
const toasts = useMemo(() => m2(), []);
|
|
3255
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Overlays & feedback", children: [
|
|
3256
|
+
/* @__PURE__ */ jsx(Cell, { label: "Tooltip, Callout, Dialog, Toast", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__row", children: [
|
|
3257
|
+
/* @__PURE__ */ jsxs(c2, { children: [
|
|
3258
|
+
/* @__PURE__ */ jsx(Focusable, { children: /* @__PURE__ */ jsx("button", { type: "button", children: "Hover me" }) }),
|
|
3259
|
+
/* @__PURE__ */ jsx(l, { placement: "bottom", children: "Duplicates the selected layer" })
|
|
3260
|
+
] }),
|
|
3261
|
+
/* @__PURE__ */ jsx("button", { type: "button", ref: calloutAnchor, onClick: () => setCalloutOpen(true), children: "Callout" }),
|
|
3262
|
+
/* @__PURE__ */ jsx(
|
|
3263
|
+
g3,
|
|
3264
|
+
{
|
|
3265
|
+
triggerRef: calloutAnchor,
|
|
3266
|
+
isOpen: calloutOpen,
|
|
3267
|
+
onOpenChange: setCalloutOpen,
|
|
3268
|
+
tone: "info",
|
|
3269
|
+
title: "Info",
|
|
3270
|
+
children: "Neutral guidance."
|
|
3271
|
+
}
|
|
3272
|
+
),
|
|
3273
|
+
/* @__PURE__ */ jsx(a5, { onClick: () => setDialogOpen(true), children: "Dialog" }),
|
|
3274
|
+
/* @__PURE__ */ jsx(
|
|
3275
|
+
l5,
|
|
3276
|
+
{
|
|
3277
|
+
isOpen: dialogOpen,
|
|
3278
|
+
onOpenChange: setDialogOpen,
|
|
3279
|
+
title: "Preferences",
|
|
3280
|
+
footer: /* @__PURE__ */ jsx(a5, { variant: "primary", onClick: () => setDialogOpen(false), children: "OK" }),
|
|
3281
|
+
children: /* @__PURE__ */ jsx("p", { children: "Dialog body." })
|
|
3282
|
+
}
|
|
3283
|
+
),
|
|
3284
|
+
/* @__PURE__ */ jsx(
|
|
3285
|
+
a5,
|
|
3286
|
+
{
|
|
3287
|
+
onClick: () => toasts.add("success", "Saved", { description: "All changes stored.", ttlMs: null }),
|
|
3288
|
+
children: "Toast"
|
|
3289
|
+
}
|
|
3290
|
+
),
|
|
3291
|
+
/* @__PURE__ */ jsx(S4, { queue: toasts, placement: "bottom-right" })
|
|
3292
|
+
] }) }),
|
|
3293
|
+
/* @__PURE__ */ jsx(Cell, { label: "Powerline", children: /* @__PURE__ */ jsx(
|
|
3294
|
+
r10,
|
|
3295
|
+
{
|
|
3296
|
+
variant: "solid",
|
|
3297
|
+
size: "sm",
|
|
3298
|
+
segments: [
|
|
3299
|
+
{ text: "main", tone: "accent", endCap: "chevron" },
|
|
3300
|
+
{ text: "\u2713 12", tone: "info", endCap: "slant" },
|
|
3301
|
+
{ text: "~/proj", tone: "muted" }
|
|
3302
|
+
]
|
|
3303
|
+
}
|
|
3304
|
+
) }),
|
|
3305
|
+
/* @__PURE__ */ jsx(Cell, { label: "StatusBar", children: /* @__PURE__ */ jsxs(n6, { ariaLabel: "Editor status", children: [
|
|
3306
|
+
/* @__PURE__ */ jsx(r11, { children: "tool: select" }),
|
|
3307
|
+
/* @__PURE__ */ jsx(r11, { children: "sel: 3" }),
|
|
3308
|
+
/* @__PURE__ */ jsx(i5, {}),
|
|
3309
|
+
/* @__PURE__ */ jsx(r11, { muted: true, children: "0.7.0" })
|
|
3310
|
+
] }) })
|
|
3311
|
+
] });
|
|
3312
|
+
}
|
|
3313
|
+
function ListsAndData() {
|
|
3314
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Lists & data", children: [
|
|
3315
|
+
/* @__PURE__ */ jsx(Cell, { label: "ItemList", children: /* @__PURE__ */ jsx(
|
|
3316
|
+
i4,
|
|
3317
|
+
{
|
|
3318
|
+
rows: [
|
|
3319
|
+
{ id: "a", label: "Draw rect" },
|
|
3320
|
+
{ id: "b", label: "Fill", selected: true },
|
|
3321
|
+
{ id: "c", label: "Move", muted: true }
|
|
3322
|
+
],
|
|
3323
|
+
empty: "Nothing"
|
|
3324
|
+
}
|
|
3325
|
+
) }),
|
|
3326
|
+
/* @__PURE__ */ jsx(Cell, { label: "DataGrid", children: /* @__PURE__ */ jsx(
|
|
3327
|
+
s2,
|
|
3328
|
+
{
|
|
3329
|
+
rows: [
|
|
3330
|
+
{ id: "1", name: "accent", count: 3 },
|
|
3331
|
+
{ id: "2", name: "info", count: 1 },
|
|
3332
|
+
{ id: "3", name: "danger", count: 2 }
|
|
3333
|
+
],
|
|
3334
|
+
columns: [
|
|
3335
|
+
{ id: "name", header: "name", accessor: (r10) => r10.name, sortable: true },
|
|
3336
|
+
{ id: "count", header: "count", accessor: (r10) => r10.count, sortable: true }
|
|
3337
|
+
],
|
|
3338
|
+
defaultSort: { columnId: "name", direction: "asc" }
|
|
3339
|
+
}
|
|
3340
|
+
) })
|
|
3341
|
+
] });
|
|
3342
|
+
}
|
|
3343
|
+
function ToolChrome() {
|
|
3344
|
+
const [tool, setTool] = useState("select");
|
|
3345
|
+
const [paneWidth, setPaneWidth] = useState(160);
|
|
3346
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Tool chrome", children: [
|
|
3347
|
+
/* @__PURE__ */ jsx(Cell, { label: "ToolGroup, ToolButton", children: /* @__PURE__ */ jsx(n7, { orientation: "horizontal", ariaLabel: "Tools", children: ["select", "rect", "ellipse"].map((id) => /* @__PURE__ */ jsx(
|
|
3348
|
+
o4,
|
|
3349
|
+
{
|
|
3350
|
+
icon: /* @__PURE__ */ jsx(Dot, {}),
|
|
3351
|
+
label: id,
|
|
3352
|
+
shortcut: id.slice(0, 1).toUpperCase(),
|
|
3353
|
+
active: tool === id,
|
|
3354
|
+
tabbable: tool === id,
|
|
3355
|
+
onClick: () => setTool(id)
|
|
3356
|
+
},
|
|
3357
|
+
id
|
|
3358
|
+
)) }) }),
|
|
3359
|
+
/* @__PURE__ */ jsx(Cell, { label: "ToolOptionsBar", children: /* @__PURE__ */ jsxs(r9, { label: "Text", children: [
|
|
3360
|
+
/* @__PURE__ */ jsx(a5, { size: "sm", variant: "ghost", iconOnly: true, ariaLabel: "Bold", children: "B" }),
|
|
3361
|
+
/* @__PURE__ */ jsx(a5, { size: "sm", variant: "ghost", iconOnly: true, ariaLabel: "Italic", children: "I" })
|
|
3362
|
+
] }) }),
|
|
3363
|
+
/* @__PURE__ */ jsx(Cell, { label: "ResizeHandle", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__box", children: [
|
|
3364
|
+
/* @__PURE__ */ jsx("div", { className: "lk-specimen__box-main", children: "content" }),
|
|
3365
|
+
/* @__PURE__ */ jsx(
|
|
3366
|
+
c5,
|
|
3367
|
+
{
|
|
3368
|
+
value: paneWidth,
|
|
3369
|
+
min: 100,
|
|
3370
|
+
max: 220,
|
|
3371
|
+
invert: true,
|
|
3372
|
+
onInput: setPaneWidth,
|
|
3373
|
+
ariaLabel: "Resize"
|
|
3374
|
+
}
|
|
3375
|
+
),
|
|
3376
|
+
/* @__PURE__ */ jsxs(
|
|
3377
|
+
"div",
|
|
3378
|
+
{
|
|
3379
|
+
className: "lk-specimen__pane",
|
|
3380
|
+
style: { "--lk-specimen-pane-w": `${paneWidth}px` },
|
|
3381
|
+
children: [
|
|
3382
|
+
paneWidth,
|
|
3383
|
+
"px"
|
|
3384
|
+
]
|
|
3385
|
+
}
|
|
3386
|
+
)
|
|
3387
|
+
] }) })
|
|
3388
|
+
] });
|
|
3389
|
+
}
|
|
3390
|
+
function EditorsAndPlots() {
|
|
3391
|
+
const [curve, setCurve] = useState([
|
|
3392
|
+
{ x: 0, y: 0 },
|
|
3393
|
+
{ x: 0.3, y: 0.1 },
|
|
3394
|
+
{ x: 0.7, y: 0.9 },
|
|
3395
|
+
{ x: 1, y: 1 }
|
|
3396
|
+
]);
|
|
3397
|
+
const [points, setPoints] = useState([
|
|
3398
|
+
{ x: 0.2, y: 0.3 },
|
|
3399
|
+
{ x: 0.5, y: 0.6 },
|
|
3400
|
+
{ x: 0.8, y: 0.2 }
|
|
3401
|
+
]);
|
|
3402
|
+
const [bands, setBands] = useState([
|
|
3403
|
+
{ from: 1 / 64, data: { name: "Radial" } },
|
|
3404
|
+
{ from: 1 / 12, data: { name: "Name plate" } }
|
|
3405
|
+
]);
|
|
3406
|
+
const [band, setBand] = useState(null);
|
|
3407
|
+
const [tracks, setTracks] = useState(
|
|
3408
|
+
() => [
|
|
3409
|
+
{
|
|
3410
|
+
kind: "sampled",
|
|
3411
|
+
label: "x",
|
|
3412
|
+
keys: [
|
|
3413
|
+
{ t: 0, value: 0 },
|
|
3414
|
+
{ t: 800, value: 120, easing: "easeOutCubic" },
|
|
3415
|
+
{ t: 1600, value: 40 }
|
|
3416
|
+
],
|
|
3417
|
+
onTick: () => {
|
|
3418
|
+
}
|
|
3419
|
+
},
|
|
3420
|
+
{
|
|
3421
|
+
kind: "event",
|
|
3422
|
+
label: "footstep",
|
|
3423
|
+
events: [
|
|
3424
|
+
{ t: 300, fire: () => {
|
|
3425
|
+
} },
|
|
3426
|
+
{ t: 900, fire: () => {
|
|
3427
|
+
} }
|
|
3428
|
+
]
|
|
3429
|
+
}
|
|
3430
|
+
]
|
|
3431
|
+
);
|
|
3432
|
+
const [playhead, setPlayhead] = useState(0);
|
|
3433
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Editors & plots", children: [
|
|
3434
|
+
/* @__PURE__ */ jsx(Cell, { label: "Plot2D", children: /* @__PURE__ */ jsx(
|
|
3435
|
+
w2,
|
|
3436
|
+
{
|
|
3437
|
+
width: 260,
|
|
3438
|
+
height: 130,
|
|
3439
|
+
grid: { divisions: 3 },
|
|
3440
|
+
axes: {},
|
|
3441
|
+
xRange: [0, 2e3],
|
|
3442
|
+
yRange: [-0.35, 1.2]
|
|
3443
|
+
}
|
|
3444
|
+
) }),
|
|
3445
|
+
/* @__PURE__ */ jsx(Cell, { label: "CurveEditor", children: /* @__PURE__ */ jsx(
|
|
3446
|
+
L2,
|
|
3447
|
+
{
|
|
3448
|
+
value: curve,
|
|
3449
|
+
onInput: setCurve,
|
|
3450
|
+
domain: "1d",
|
|
3451
|
+
interpolation: "catmull-rom",
|
|
3452
|
+
endpoints: "pinned-both",
|
|
3453
|
+
addPointMode: "click-curve",
|
|
3454
|
+
axes: {},
|
|
3455
|
+
fill: { side: "below" },
|
|
3456
|
+
width: 260,
|
|
3457
|
+
height: 130
|
|
3458
|
+
}
|
|
3459
|
+
) }),
|
|
3460
|
+
/* @__PURE__ */ jsx(Cell, { label: "PointPlotter", children: /* @__PURE__ */ jsx(
|
|
3461
|
+
n8,
|
|
3462
|
+
{
|
|
3463
|
+
value: points,
|
|
3464
|
+
onInput: setPoints,
|
|
3465
|
+
xRange: [0, 1],
|
|
3466
|
+
yRange: [0, 1],
|
|
3467
|
+
grid: { divisions: 3 },
|
|
3468
|
+
axes: {},
|
|
3469
|
+
addPointMode: "click-empty",
|
|
3470
|
+
width: 260,
|
|
3471
|
+
height: 130
|
|
3472
|
+
}
|
|
3473
|
+
) }),
|
|
3474
|
+
/* @__PURE__ */ jsx(Cell, { label: "BandEditor", wide: true, children: /* @__PURE__ */ jsx(
|
|
3475
|
+
k2,
|
|
3476
|
+
{
|
|
3477
|
+
min: 1 / 64,
|
|
3478
|
+
max: 1 / 2,
|
|
3479
|
+
scale: "log",
|
|
3480
|
+
snap: true,
|
|
3481
|
+
value: bands,
|
|
3482
|
+
onChange: setBands,
|
|
3483
|
+
selectedIndex: band,
|
|
3484
|
+
onSelect: setBand,
|
|
3485
|
+
renderBand: (b2) => b2.data.name,
|
|
3486
|
+
ticks: [1 / 24, 1 / 12, 1 / 6].map((at) => ({ at, label: `1/${Math.round(1 / at)}` }))
|
|
3487
|
+
}
|
|
3488
|
+
) }),
|
|
3489
|
+
/* @__PURE__ */ jsx(Cell, { label: "Timeline", wide: true, children: /* @__PURE__ */ jsx("div", { className: "lk-specimen__timeline", children: /* @__PURE__ */ jsx(
|
|
3490
|
+
$2,
|
|
3491
|
+
{
|
|
3492
|
+
tracks,
|
|
3493
|
+
duration: 2e3,
|
|
3494
|
+
playhead,
|
|
3495
|
+
onChange: setTracks,
|
|
3496
|
+
onScrub: setPlayhead
|
|
3497
|
+
}
|
|
3498
|
+
) }) })
|
|
3499
|
+
] });
|
|
3500
|
+
}
|
|
3501
|
+
function LabChrome() {
|
|
3502
|
+
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
3503
|
+
const [zoom, setZoom] = useState(1);
|
|
3504
|
+
return /* @__PURE__ */ jsxs(Section, { title: "Lab chrome", children: [
|
|
3505
|
+
/* @__PURE__ */ jsx(Cell, { label: "Toolbar", wide: true, children: /* @__PURE__ */ jsxs(Toolbar, { "aria-label": "Trial actions", children: [
|
|
3506
|
+
/* @__PURE__ */ jsx(Toolbar.Title, { children: "My Trial" }),
|
|
3507
|
+
/* @__PURE__ */ jsxs(Toolbar.Group, { children: [
|
|
3508
|
+
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: () => {
|
|
3509
|
+
}, children: "Undo" }),
|
|
3510
|
+
/* @__PURE__ */ jsx(Toolbar.Button, { onClick: () => {
|
|
3511
|
+
}, disabled: true, children: "Redo" })
|
|
3512
|
+
] }),
|
|
3513
|
+
/* @__PURE__ */ jsx(Toolbar.Group, { end: true, children: /* @__PURE__ */ jsx(Toolbar.Button, { onClick: () => {
|
|
3514
|
+
}, children: "Save" }) })
|
|
3515
|
+
] }) }),
|
|
3516
|
+
/* @__PURE__ */ jsx(Cell, { label: "StatusBar", children: /* @__PURE__ */ jsxs(StatusBar, { children: [
|
|
3517
|
+
/* @__PURE__ */ jsx(StatusBar.Section, { children: "Items: 12" }),
|
|
3518
|
+
/* @__PURE__ */ jsx(StatusBar.Section, { children: "Zoom: 100%" })
|
|
3519
|
+
] }) }),
|
|
3520
|
+
/* @__PURE__ */ jsx(Cell, { label: "Sidebar", children: /* @__PURE__ */ jsxs("div", { className: "lk-specimen__box", children: [
|
|
3521
|
+
/* @__PURE__ */ jsx("div", { className: "lk-specimen__box-main", children: "main" }),
|
|
3522
|
+
/* @__PURE__ */ jsx(
|
|
3523
|
+
Sidebar,
|
|
3524
|
+
{
|
|
3525
|
+
title: "Controls",
|
|
3526
|
+
collapsed: sidebarCollapsed,
|
|
3527
|
+
onToggle: () => setSidebarCollapsed((v2) => !v2),
|
|
3528
|
+
children: /* @__PURE__ */ jsx("p", { children: "One slider" })
|
|
3529
|
+
}
|
|
3530
|
+
)
|
|
3531
|
+
] }) }),
|
|
3532
|
+
/* @__PURE__ */ jsxs(Cell, { label: "Legend, JobProgress", children: [
|
|
3533
|
+
/* @__PURE__ */ jsx(Legend, { entries: LEGEND }),
|
|
3534
|
+
/* @__PURE__ */ jsx(JobProgress, { job: JOB })
|
|
3535
|
+
] }),
|
|
3536
|
+
/* @__PURE__ */ jsxs(Cell, { label: "ScaleIndicator, ZoomControl", children: [
|
|
3537
|
+
/* @__PURE__ */ jsx(ScaleIndicator, { zoom, unit: "ft", pixelsPerUnit: 50 }),
|
|
3538
|
+
/* @__PURE__ */ jsx(ZoomControl, { zoom, onZoomChange: setZoom, min: 0.1, max: 8 })
|
|
3539
|
+
] }),
|
|
3540
|
+
/* @__PURE__ */ jsx(Cell, { label: "FloatingPanel", children: /* @__PURE__ */ jsx("div", { className: "lk-specimen__floating-host", children: /* @__PURE__ */ jsx(FloatingPanel, { anchor: "top-right", children: /* @__PURE__ */ jsx(Legend, { entries: LEGEND.slice(0, 2) }) }) }) }),
|
|
3541
|
+
/* @__PURE__ */ jsx(Cell, { label: "Split", wide: true, children: /* @__PURE__ */ jsx("div", { className: "lk-specimen__box", children: /* @__PURE__ */ jsx(
|
|
3542
|
+
Split,
|
|
3543
|
+
{
|
|
3544
|
+
sidebar: /* @__PURE__ */ jsx("div", { className: "lk-specimen__pad", children: "sidebar" }),
|
|
3545
|
+
defaultWidth: 160,
|
|
3546
|
+
minWidth: 100,
|
|
3547
|
+
maxWidth: 260,
|
|
3548
|
+
children: /* @__PURE__ */ jsx("div", { className: "lk-specimen__pad", children: "content" })
|
|
3549
|
+
}
|
|
3550
|
+
) }) })
|
|
3551
|
+
] });
|
|
3552
|
+
}
|
|
3553
|
+
function Specimen({ className }) {
|
|
3554
|
+
return /* @__PURE__ */ jsxs("div", { className: className ? `lk-specimen ${className}` : "lk-specimen", "data-wzl-portal-host": "", children: [
|
|
3555
|
+
/* @__PURE__ */ jsx(ButtonsAndToggles, {}),
|
|
3556
|
+
/* @__PURE__ */ jsx(FieldsAndPickers, {}),
|
|
3557
|
+
/* @__PURE__ */ jsx(PanelsAndRows, {}),
|
|
3558
|
+
/* @__PURE__ */ jsx(OverlaysAndFeedback, {}),
|
|
3559
|
+
/* @__PURE__ */ jsx(ListsAndData, {}),
|
|
3560
|
+
/* @__PURE__ */ jsx(ToolChrome, {}),
|
|
3561
|
+
/* @__PURE__ */ jsx(EditorsAndPlots, {}),
|
|
3562
|
+
/* @__PURE__ */ jsx(LabChrome, {})
|
|
3563
|
+
] });
|
|
4040
3564
|
}
|
|
4041
3565
|
|
|
4042
|
-
export {
|
|
3566
|
+
export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationOverlay, AnnotationTargets, Lab, LabPalette, LabRoot, LabShell, LabSwitcher, SPECIMEN_SECTIONS, Specimen, 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 };
|
|
4043
3567
|
//# sourceMappingURL=index.js.map
|
|
4044
3568
|
//# sourceMappingURL=index.js.map
|