@weasel-js/labkit 1.4.3 → 1.4.4

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.
Files changed (57) hide show
  1. package/dist/_dts/{CanvasStackContext-Dde8_1my.d.ts → CanvasStackContext-kTEZvEgE.d.ts} +1 -1
  2. package/dist/_dts/{frac-B5mB8LsJ.d.ts → frac-z7ker2Vx.d.ts} +4 -3
  3. package/dist/_dts/{index-DelFfeV0.d.ts → index-Blv9uQzu.d.ts} +27 -2
  4. package/dist/canvas/index.d.ts +4 -4
  5. package/dist/chrome/index.d.ts +2 -2
  6. package/dist/chrome/index.js +4 -4
  7. package/dist/{chunk-5TROQVQ2.js → chunk-2P6PP5N4.js} +10 -4
  8. package/dist/chunk-2P6PP5N4.js.map +1 -0
  9. package/dist/{chunk-C42T6DJT.js → chunk-64ZCN3DA.js} +5 -5
  10. package/dist/{chunk-C42T6DJT.js.map → chunk-64ZCN3DA.js.map} +1 -1
  11. package/dist/{chunk-ISCOFAUO.js → chunk-BKFVHKJH.js} +3 -3
  12. package/dist/{chunk-ISCOFAUO.js.map → chunk-BKFVHKJH.js.map} +1 -1
  13. package/dist/{chunk-QAGAHJKZ.js → chunk-MOM3GOVY.js} +3 -3
  14. package/dist/{chunk-QAGAHJKZ.js.map → chunk-MOM3GOVY.js.map} +1 -1
  15. package/dist/{chunk-2RQPGOQF.js → chunk-SMHP6XZ4.js} +3 -3
  16. package/dist/{chunk-2RQPGOQF.js.map → chunk-SMHP6XZ4.js.map} +1 -1
  17. package/dist/{chunk-ASWKLRKJ.js → chunk-U3IYHIAE.js} +11 -11
  18. package/dist/chunk-U3IYHIAE.js.map +1 -0
  19. package/dist/{chunk-MHAGC6VD.js → chunk-W3ECWC2K.js} +199 -184
  20. package/dist/chunk-W3ECWC2K.js.map +1 -0
  21. package/dist/controls/index.d.ts +1 -1
  22. package/dist/controls/index.js +2 -2
  23. package/dist/dragdrop/index.d.ts +1 -1
  24. package/dist/index.d.ts +55 -9
  25. package/dist/index.js +128 -45
  26. package/dist/index.js.map +1 -1
  27. package/dist/layers/index.d.ts +2 -2
  28. package/dist/layers/index.js +2 -2
  29. package/dist/loupe/index.d.ts +3 -3
  30. package/dist/passthrough/weasel-ui.d.ts +10 -0
  31. package/dist/passthrough/weasel-ui.js +1 -1
  32. package/dist/primitives/index.js +3 -3
  33. package/dist/state/index.js +2 -2
  34. package/dist/styles.css +81 -14
  35. package/dist/ui/layers/index.js +1 -1
  36. package/dist/undo/index.d.ts +1 -1
  37. package/package.json +7 -7
  38. package/src/annotations/frac.test.ts +4 -4
  39. package/src/annotations/frac.ts +3 -2
  40. package/src/annotations/index.ts +1 -1
  41. package/src/annotations/store.ts +2 -2
  42. package/src/annotations/svgNodes.ts +1 -2
  43. package/src/index.ts +1 -1
  44. package/src/lab/LabShell.tsx +14 -2
  45. package/src/lab/LabSwitcher.less +85 -0
  46. package/src/lab/LabSwitcher.test.tsx +96 -0
  47. package/src/lab/LabSwitcher.tsx +112 -0
  48. package/src/lab/index.ts +2 -0
  49. package/src/state/helpers.test.ts +20 -0
  50. package/src/state/helpers.ts +16 -0
  51. package/src/state/store.ts +2 -2
  52. package/src/styles.less +1 -0
  53. package/src/theme/base.less +4 -4
  54. package/src/trial/trialOps.ts +3 -2
  55. package/dist/chunk-5TROQVQ2.js.map +0 -1
  56. package/dist/chunk-ASWKLRKJ.js.map +0 -1
  57. package/dist/chunk-MHAGC6VD.js.map +0 -1
@@ -1,4 +1,4 @@
1
- export { e as ControlPanel } from '../_dts/index-DelFfeV0.js';
1
+ export { e as ControlPanel } from '../_dts/index-Blv9uQzu.js';
2
2
  export { a as CheckboxField, b as ColorField, C as ConfigField, d as ConfigFieldType, N as NumberField, S as SelectField, e as SelectOption, f as SliderField, T as TextField } from '../_dts/types-x92Kfeme.js';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
@@ -1,5 +1,5 @@
1
- export { ControlPanel } from '../chunk-ASWKLRKJ.js';
1
+ export { ControlPanel } from '../chunk-U3IYHIAE.js';
2
2
  import '../chunk-E44SU6XS.js';
3
- import '../chunk-MHAGC6VD.js';
3
+ import '../chunk-W3ECWC2K.js';
4
4
  //# sourceMappingURL=index.js.map
5
5
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import { P as PaletteItem, a as Point, D as DragFeedback, b as DragDropCapability, V as ViewTransform, c as WorldSpec } from '../_dts/frac-B5mB8LsJ.js';
1
+ import { P as PaletteItem, a as Point, D as DragFeedback, b as DragDropCapability, V as ViewTransform, c as WorldSpec } from '../_dts/frac-z7ker2Vx.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { RefObject, PointerEvent, ReactPortal } from 'react';
4
4
  import '../_dts/types-B9_zrHmb.js';
package/dist/index.d.ts CHANGED
@@ -2,15 +2,15 @@ export { LayerStack, LayerStackItem, LayerStackProps } from './ui/layers/index.j
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import * as react from 'react';
4
4
  import { ReactNode } from 'react';
5
- import { P as PropertyMetricProps, a as PropertyListPack } from './_dts/index-DelFfeV0.js';
6
- export { C as CheckboxRow, b as CheckboxRowProps, c as ColorRow, d as ColorRowProps, e as ControlPanel, N as NumberRow, f as NumberRowProps, g as PropertyList, h as PropertyListProps, i as PropertyOption, j as PropertyPanel, k as PropertyPanelProps, l as PropertyRow, m as PropertyRowLayout, n as PropertyRowProps, o as PropertyRowVariant, p as PropertySpan, q as PropertySpanProps, S as SelectRow, r as SelectRowProps, s as SliderRow, t as SliderRowProps, T as TextRow, u as TextRowProps, v as ToggleRow, w as ToggleRowProps } from './_dts/index-DelFfeV0.js';
5
+ import { P as PropertyMetricProps, a as PropertyListPack } from './_dts/index-Blv9uQzu.js';
6
+ export { C as CheckboxRow, b as CheckboxRowProps, c as ColorRow, d as ColorRowProps, e as ControlPanel, N as NumberRow, f as NumberRowProps, g as PropertyList, h as PropertyListProps, i as PropertyOption, j as PropertyPanel, k as PropertyPanelProps, l as PropertyRow, m as PropertyRowLayout, n as PropertyRowProps, o as PropertyRowVariant, p as PropertySpan, q as PropertySpanProps, S as SelectRow, r as SelectRowProps, s as SliderRow, t as SliderRowProps, T as TextRow, u as TextRowProps, v as ToggleRow, w as ToggleRowProps } from './_dts/index-Blv9uQzu.js';
7
7
  import { DrawCommand, SceneNode, View, Path, FillStyle, StyledRun, TextStyle, Stroke, BuiltinToolId, ToolPrefLeaf, ToolPrefGroup } from '@weasel-js/core';
8
- import { d as WorldRect, A as AnnotationData, e as AnnotationMeaning, M as MarkScene, C as CaptureSource, f as CaptureOptions, g as CaptureResult, h as AnnotationTarget, i as AnnotationsApi, j as AnnotationsCapability, T as TrialTool, k as AnnotationKind, V as ViewTransform, I as Instrument, l as InstrumentList, m as TrialContribution } from './_dts/frac-B5mB8LsJ.js';
9
- export { n as Annotation, o as AnnotationInit, p as AnnotationPatch, q as AnnotationQuery, r as AnnotationStatus, s as AnnotationStoreOptions, t as AnnotationTargetInfo, u as CanvasCapability, v as CanvasLayer, w as CaptureDeps, x as DEFAULT_FRAME, b as DragDropCapability, D as DragFeedback, F as FracPoint, y as FracRect, H as HitResult, z as IconComponent, L as LOUPE_DEFAULTS, B as LayerCapability, E as LayerDescriptor, G as LoupeCapability, J as LoupeDeclaration, K as LoupeRenderArgs, P as PaletteItem, a as Point, R as RenderContext, N as ResolvedLoupe, S as SerializedAnnotations, O as SidebarSection, Q as StatusReadout, U as SystemEvent, X as ToolCapability, Y as ToolItem, Z as ToolbarItem, _ as TrialChromeContext, $ as TrialRegion, a0 as UndoCapability, a1 as ViewportControl, a2 as ViewportSize, W as WorldFrame, c as WorldSpec, a3 as annotationsFromJSON, a4 as applyCamera, a5 as createAnnotationScene, a6 as createAnnotationStore, a7 as fracContains, a8 as fracIntersects, a9 as fracToWorld, aa as resolveFrame, ab as resolveLoupe, ac as roundFrac, ad as worldToFrac } from './_dts/frac-B5mB8LsJ.js';
8
+ import { d as WorldRect, A as AnnotationData, e as AnnotationMeaning, M as MarkScene, C as CaptureSource, f as CaptureOptions, g as CaptureResult, h as AnnotationTarget, i as AnnotationsApi, j as AnnotationsCapability, T as TrialTool, k as AnnotationKind, V as ViewTransform, I as Instrument, l as InstrumentList, m as TrialContribution } from './_dts/frac-z7ker2Vx.js';
9
+ export { n as Annotation, o as AnnotationInit, p as AnnotationPatch, q as AnnotationQuery, r as AnnotationStatus, s as AnnotationStoreOptions, t as AnnotationTargetInfo, u as CanvasCapability, v as CanvasLayer, w as CaptureDeps, x as DEFAULT_FRAME, b as DragDropCapability, D as DragFeedback, F as FracPoint, y as FracRect, H as HitResult, z as IconComponent, L as LOUPE_DEFAULTS, B as LayerCapability, E as LayerDescriptor, G as LoupeCapability, J as LoupeDeclaration, K as LoupeRenderArgs, P as PaletteItem, a as Point, R as RenderContext, N as ResolvedLoupe, S as SerializedAnnotations, O as SidebarSection, Q as StatusReadout, U as SystemEvent, X as ToolCapability, Y as ToolItem, Z as ToolbarItem, _ as TrialChromeContext, $ as TrialRegion, a0 as UndoCapability, a1 as ViewportControl, a2 as ViewportSize, W as WorldFrame, c as WorldSpec, a3 as annotationsFromJSON, a4 as applyCamera, a5 as createAnnotationScene, a6 as createAnnotationStore, a7 as fracContains, a8 as fracEncloses, a9 as fracToWorld, aa as resolveFrame, ab as resolveLoupe, ac as roundFrac, ad as worldToFrac } from './_dts/frac-z7ker2Vx.js';
10
10
  import { i as TrialInfo, a as StorageAdapter, h as LabMode, T as TrialRecord, c as SavedSnapshot } from './_dts/types-BP2OCcpg.js';
11
11
  export { C as CreateLabStoreOptions, L as LabDocument, b as LabStoreState, S as SerializedTrial, d as TrialStateHandle, U as UndoStack, e as UndockedPanel, f as UndockedPanels, g as dockPanel, p as panelKey, u as undockPanel } from './_dts/types-BP2OCcpg.js';
12
12
  export { CanvasStack, CanvasStackProps, OrbitHandlers, OrbitView, PITCH_LIMIT, PanZoomHandlers, UseOrbitOptions, UsePanZoomOptions, Vec3, ZoomAtOptions, centerOn, clampPitch, orbitAfterDrag, orbitAfterWheel, screenToWorld, useOrbit, usePanZoom, worldToScreen, wrapYaw, zoomAt } from './canvas/index.js';
13
- export { C as CanvasLayerDescriptor, a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from './_dts/CanvasStackContext-Dde8_1my.js';
13
+ export { C as CanvasLayerDescriptor, a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from './_dts/CanvasStackContext-kTEZvEgE.js';
14
14
  export { PaletteRegion, PaletteRegionProps, SidebarRegion, SidebarRegionProps, StatusRegion, StatusRegionProps, TitleBarPlacement, TitleBarRegion, TitleBarRegionProps, ToolbarRegion, ToolbarRegionProps, ViewportRegion, ViewportRegionProps, builtinContributions, mergeContributions, suppressContributions } from './chrome/index.js';
15
15
  import { b as ConfigNode, A as Annotations, N as NodeOptions, c as ControlRenderer, d as ConfigOption, e as ConfigShape, f as ConfigBranch, B as BranchAnnotations, g as BranchOptions, a as ConfigSchema, I as InferConfig, h as ConfigEntry, R as ResolvedConfig, i as ConfigRule, L as LeafPatch, j as ConfigRuleContext } from './_dts/types-B9_zrHmb.js';
16
16
  export { k as ConfigOf, C as ConfigPath, E as EntryValue, l as NodeValue, S as SectionSpec, V as ValueAtPath } from './_dts/types-B9_zrHmb.js';
@@ -377,7 +377,12 @@ type SvgPaint = {
377
377
  /** Stroke description: a paint plus structural line parameters. */
378
378
  interface SvgStroke {
379
379
  paint: SvgPaint;
380
- width: number;
380
+ /** World units, or `{ px }` for a width that holds its rendered thickness
381
+ * however the document is scaled — SVG's `vector-effect="non-scaling-stroke"`,
382
+ * and the same unit system as the kit's `Stroke.width`. */
383
+ width: number | {
384
+ px: number;
385
+ };
381
386
  opacity?: number;
382
387
  /** `stroke-linecap`. Default per SVG spec is `'butt'`. */
383
388
  cap?: 'butt' | 'round' | 'square';
@@ -966,6 +971,41 @@ interface LabPaletteProps {
966
971
  * instrument declares no tools of its own resolves to. */
967
972
  declare function LabPalette({ tools }: LabPaletteProps): react_jsx_runtime.JSX.Element;
968
973
 
974
+ /** One page in a project's set of labs. */
975
+ interface LabPage {
976
+ /** The page's own URL, and how the switcher knows which page is open. */
977
+ href: string;
978
+ label: string;
979
+ }
980
+ /** Props for `<LabSwitcher>`. */
981
+ interface LabSwitcherProps {
982
+ /** The open page's title, shown as the control's own label. */
983
+ title: string;
984
+ pages: readonly LabPage[];
985
+ /** Defaults to the current location. Pass it in tests, or to render a
986
+ * switcher for a page other than the one being viewed. */
987
+ path?: string;
988
+ className?: string;
989
+ }
990
+ /** Which of `pages` a path is on, or -1.
991
+ *
992
+ * Matched on the end of the path so a query string, a trailing slash or a
993
+ * leftover `.html` cannot lose it: a project whose dev server maps `/stats`
994
+ * to `stats.html` serves both spellings, and a bookmark from before the URLs
995
+ * lost their extension still resolves to the same page. */
996
+ declare function currentPage(path: string, pages: readonly LabPage[]): number;
997
+ /** A project's lab title, doubling as the way to reach its other labs.
998
+ *
999
+ * The menu holds real anchors, not a `<select>` and not buttons. These are
1000
+ * separate documents, so an `href` is what gets middle-click, cmd-click and
1001
+ * the back button for free; a select fires a change event and can do none of
1002
+ * the three. The trigger is a button because it opens something rather than
1003
+ * going anywhere.
1004
+ *
1005
+ * Pages accumulate -- a project grows a wall, a dashboard, an ingest page, a
1006
+ * bench -- and a tab strip is the layout that stops working first. */
1007
+ declare function LabSwitcher({ title, pages, path, className }: LabSwitcherProps): react_jsx_runtime.JSX.Element;
1008
+
969
1009
  /** Props for `<LabShell>`. */
970
1010
  interface LabShellProps {
971
1011
  title: string;
@@ -976,11 +1016,17 @@ interface LabShellProps {
976
1016
  footer?: ReactNode;
977
1017
  /** Color mode. "auto" (default) follows prefers-color-scheme. */
978
1018
  mode?: LabMode;
1019
+ /** The project's other labs. Given two or more, the title becomes the way
1020
+ * to reach them; given none, it stays a plain heading. */
1021
+ pages?: readonly LabPage[];
1022
+ /** The path the switcher reads to mark the open page. Defaults to the
1023
+ * current location. */
1024
+ path?: string;
979
1025
  }
980
1026
  /** Page frame for a lab: a titled header, a body, and an optional footer,
981
1027
  * themed for the resolved color mode. Presentational only — use `<Lab>` when
982
1028
  * the trial runtime is wanted too. */
983
- declare function LabShell({ title, children, header, footer, mode }: LabShellProps): react_jsx_runtime.JSX.Element;
1029
+ declare function LabShell({ title, children, header, footer, mode, pages, path }: LabShellProps): react_jsx_runtime.JSX.Element;
984
1030
 
985
1031
  /** A tile's persisted extent, keyed by the id its caller gave it. Grid resizes
986
1032
  * write `span`; `size` is here because a strategy swap would write that. */
@@ -1183,5 +1229,5 @@ interface TrialChromeProps {
1183
1229
  * each region its slice. */
1184
1230
  declare function TrialChrome({ trialId, record, instrument, isLastTrial, undoBindings, job, loupe, trialChrome, chrome, suppress, activeToolId, setActiveTool, children, }: TrialChromeProps): react_jsx_runtime.JSX.Element;
1185
1231
 
1186
- export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationData, AnnotationKind, AnnotationMeaning, AnnotationOverlay, AnnotationTarget, AnnotationTargets, Annotations, AnnotationsApi, AnnotationsCapability, AnnotationsContext, BranchAnnotations, BranchOptions, CaptureOptions, CaptureResult, CaptureSource, ConfigBranch, ConfigEntry, ConfigField, ConfigNode, ConfigOption, ConfigRule, ConfigRuleContext, ConfigSchema, ConfigShape, ControlRenderer, CurveField, DEFAULT_VIEW, EffectCard, EffectCardList, ExportMenu, InferConfig, Instrument, InstrumentList, JobHandle, Lab, LabContext, LabMode, LabPalette, LabShell, LeafPatch, MarkList, MarkScene, NodeOptions, PropertyGroup, PropertyListPack, ResolvedConfig, SavedSnapshot, StorageAdapter, Subpanel, Trial, TrialBody, TrialChrome, TrialContribution, TrialInfo, TrialRecord, TrialTool, ViewTransform, Workspace, WorldRect, addTrial, annotationToolInfo, applyRules, as2DView, builtinRules, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createMarkDrawOne, defineInstrument, f, fillConfigDefaults, fitView, fromConfigFields, fromWeaselView, hasConfigPath, interstellarTheme, isConfigBranch, isLeafVisible, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, schemaNodeAtPath, seenFrom, titleCase, toWeaselView, useAnnotations, useAnnotationsOptional, useConfigSchema, useLabContext, validateConfigSchema, valueAtPath, withValueAtPath };
1187
- export type { AddTrialOptions, AnnotationOverlayProps, AnnotationTargetsProps, AnnotationToolInfo, CaptureArgs, CapturePlan, ComposeSvgArgs, ContentSize, CurveFieldProps, CurveMark, EffectCardListItem, EffectCardListProps, EffectCardProps, InstrumentSpec, LabContextValue, LabPaletteProps, LabProps, LabShellProps, MarkDrawOptions, MarkListProps, MarkStyle, PaintableMark, PaneSize, PanelDescriptor, PropertyGroupProps, SubpanelProps, TrialBodyProps, TrialChromeProps, TrialLayout, TrialProps, UndoBindings, ValidationResult, ViewTransform2D, WorkspaceProps };
1232
+ export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationData, AnnotationKind, AnnotationMeaning, AnnotationOverlay, AnnotationTarget, AnnotationTargets, Annotations, AnnotationsApi, AnnotationsCapability, AnnotationsContext, BranchAnnotations, BranchOptions, CaptureOptions, CaptureResult, CaptureSource, ConfigBranch, ConfigEntry, ConfigField, ConfigNode, ConfigOption, ConfigRule, ConfigRuleContext, ConfigSchema, ConfigShape, ControlRenderer, CurveField, DEFAULT_VIEW, EffectCard, EffectCardList, ExportMenu, InferConfig, Instrument, InstrumentList, JobHandle, Lab, LabContext, LabMode, LabPalette, LabShell, LabSwitcher, LeafPatch, MarkList, MarkScene, NodeOptions, PropertyGroup, PropertyListPack, ResolvedConfig, SavedSnapshot, StorageAdapter, Subpanel, Trial, TrialBody, TrialChrome, TrialContribution, TrialInfo, TrialRecord, TrialTool, ViewTransform, Workspace, WorldRect, addTrial, annotationToolInfo, applyRules, as2DView, builtinRules, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createMarkDrawOne, currentPage, defineInstrument, f, fillConfigDefaults, fitView, fromConfigFields, fromWeaselView, hasConfigPath, interstellarTheme, isConfigBranch, isLeafVisible, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, schemaNodeAtPath, seenFrom, titleCase, toWeaselView, useAnnotations, useAnnotationsOptional, useConfigSchema, useLabContext, validateConfigSchema, valueAtPath, withValueAtPath };
1233
+ export type { AddTrialOptions, AnnotationOverlayProps, AnnotationTargetsProps, AnnotationToolInfo, CaptureArgs, CapturePlan, ComposeSvgArgs, ContentSize, CurveFieldProps, CurveMark, EffectCardListItem, EffectCardListProps, EffectCardProps, InstrumentSpec, LabContextValue, LabPage, LabPaletteProps, LabProps, LabShellProps, LabSwitcherProps, MarkDrawOptions, MarkListProps, MarkStyle, PaintableMark, PaneSize, PanelDescriptor, PropertyGroupProps, SubpanelProps, TrialBodyProps, TrialChromeProps, TrialLayout, TrialProps, UndoBindings, ValidationResult, ViewTransform2D, WorkspaceProps };
package/dist/index.js CHANGED
@@ -5,35 +5,35 @@ import { useSurfaceOptional, useSurfaceCanvas, useTileId, useTiledSurface, Surfa
5
5
  export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTileId, useTiledSurface } from './chunk-FQMJUVHJ.js';
6
6
  import { useJob } from './chunk-HXXTULDN.js';
7
7
  export { useJob } from './chunk-HXXTULDN.js';
8
- import { ANNOTATION_WEASEL_TOOLS, annotationToolInfo, suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, ViewportRegion, SidebarRegion, StatusRegion, AnnotationsContext } from './chunk-C42T6DJT.js';
9
- export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationsContext, ExportMenu, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, annotationToolInfo, builtinContributions, mergeContributions, suppressContributions, useAnnotations, useAnnotationsOptional } from './chunk-C42T6DJT.js';
10
- import { JobProgress } from './chunk-QAGAHJKZ.js';
11
- export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-QAGAHJKZ.js';
12
- export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-2RQPGOQF.js';
13
- import { createLabStore, panelKey, noneAdapter } from './chunk-5TROQVQ2.js';
14
- export { CURRENT_DOCUMENT_VERSION, createMemoryAdapter, dockPanel, labDocumentKey, localStorageAdapter, noneAdapter, panelKey, quarantineKey, sessionStorageAdapter, undockPanel, urlHashAdapter, useTrialState } from './chunk-5TROQVQ2.js';
8
+ import { ANNOTATION_WEASEL_TOOLS, annotationToolInfo, suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, ViewportRegion, SidebarRegion, StatusRegion, AnnotationsContext } from './chunk-64ZCN3DA.js';
9
+ export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationsContext, ExportMenu, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, annotationToolInfo, builtinContributions, mergeContributions, suppressContributions, useAnnotations, useAnnotationsOptional } from './chunk-64ZCN3DA.js';
10
+ import { JobProgress } from './chunk-MOM3GOVY.js';
11
+ export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-MOM3GOVY.js';
12
+ export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-SMHP6XZ4.js';
13
+ import { newId, createLabStore, panelKey, noneAdapter } from './chunk-2P6PP5N4.js';
14
+ export { CURRENT_DOCUMENT_VERSION, createMemoryAdapter, dockPanel, labDocumentKey, localStorageAdapter, noneAdapter, panelKey, quarantineKey, sessionStorageAdapter, undockPanel, urlHashAdapter, useTrialState } from './chunk-2P6PP5N4.js';
15
15
  import { LabStoreContext, TrialIdProvider } from './chunk-2YILQ7Y5.js';
16
16
  export { LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, useLabStore, useTrialId } from './chunk-2YILQ7Y5.js';
17
- import { fromConfigFields } from './chunk-ASWKLRKJ.js';
18
- export { ControlPanel, fromConfigFields, isLeafVisible } from './chunk-ASWKLRKJ.js';
17
+ import { fromConfigFields } from './chunk-U3IYHIAE.js';
18
+ export { ControlPanel, fromConfigFields, isLeafVisible } from './chunk-U3IYHIAE.js';
19
19
  import { hasConfigPath, schemaNodeAtPath, withValueAtPath, fillConfigDefaults } from './chunk-E44SU6XS.js';
20
20
  export { fillConfigDefaults, hasConfigPath, schemaNodeAtPath, valueAtPath, withValueAtPath } from './chunk-E44SU6XS.js';
21
21
  import { CanvasStack } from './chunk-I6JCVE24.js';
22
22
  export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-I6JCVE24.js';
23
- import { LayerList } from './chunk-ISCOFAUO.js';
24
- export { LayerList } from './chunk-ISCOFAUO.js';
23
+ import { LayerList } from './chunk-BKFVHKJH.js';
24
+ export { LayerList } from './chunk-BKFVHKJH.js';
25
25
  import './chunk-SBXNQW4G.js';
26
- import { E, w, T, C, g, r6 } from './chunk-MHAGC6VD.js';
27
- export { M2 as CheckboxRow, j2 as ColorRow, y3 as CurveField, R as EffectCard, B as EffectCardList, c3 as LayerStack, P2 as NumberRow, V as PropertyGroup, T2 as PropertyList, w2 as PropertyPanel, D as PropertyRow, E2 as PropertySpan, F as SelectRow, k as SliderRow, L2 as Subpanel, N2 as TextRow, I as ToggleRow } from './chunk-MHAGC6VD.js';
26
+ import { E, w, T, C, g, r6 } from './chunk-W3ECWC2K.js';
27
+ export { N2 as CheckboxRow, M2 as ColorRow, y3 as CurveField, z as EffectCard, V as EffectCardList, c3 as LayerStack, F as NumberRow, H as PropertyGroup, T2 as PropertyList, w2 as PropertyPanel, D as PropertyRow, E2 as PropertySpan, I as SelectRow, A2 as SliderRow, R as Subpanel, P2 as TextRow, L2 as ToggleRow } from './chunk-W3ECWC2K.js';
28
28
  import { resolveLoupe, TrialLoupe } from './chunk-TJ7QY3OC.js';
29
29
  export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-TJ7QY3OC.js';
30
30
  export { centerOn, zoomAt } from './chunk-CTRKTLYZ.js';
31
31
  import { applyCamera } from './chunk-2AYGEN57.js';
32
32
  export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from './chunk-2AYGEN57.js';
33
33
  export { CanvasStackContext } from './chunk-CQLQPQ4P.js';
34
- import { textCommand, linePath, markerDrawCommands, ellipsePath, rectPath, createScene, asNodeId as asNodeId$1, WeaselProvider, SceneCanvas, sceneFromJSON, PathBuilder, renderSceneToPixels, useActiveToolContext, resolveStrokeWidth, getMarker, getPaintKind, boundsOfPath, PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, SCRIPT_METRICS } from '@weasel-js/core';
34
+ import { textCommand, linePath, markerDrawCommands, ellipsePath, rectPath, createScene, asNodeId as asNodeId$1, WeaselProvider, SceneCanvas, sceneFromJSON, PathBuilder, renderSceneToPixels, useActiveToolContext, getMarker, getPaintKind, boundsOfPath, PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, SCRIPT_METRICS } from '@weasel-js/core';
35
35
  import { tileGeometry } from '@weasel-js/core/patterns-builtin';
36
- import { createContext, useState, useRef, useEffect, useContext, useMemo, Children, useLayoutEffect, useCallback, useSyncExternalStore } from 'react';
36
+ import { createContext, useState, useRef, useEffect, useContext, useMemo, useId, useCallback, Children, useLayoutEffect, useSyncExternalStore } from 'react';
37
37
  import { createPortal } from 'react-dom';
38
38
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
39
39
  import { useStore } from 'zustand/react';
@@ -422,11 +422,15 @@ function paintAttrs(paint, name, registry, includeOpacity = true) {
422
422
  const id = registry.register(paint.paint);
423
423
  return [`${name}="url(#${id})"`];
424
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
+ }
425
429
  function coreStrokeAttrs(stroke, registry) {
426
430
  const paint = stroke?.paint;
427
431
  if (!stroke || !paint) return [];
428
- const width = resolveStrokeWidth(stroke.width ?? 1, 1);
429
- if (!(width > 0)) return [];
432
+ const width = stroke.width ?? 1;
433
+ if (!((typeof width === "object" ? width.px : width) > 0)) return [];
430
434
  const attrs = [];
431
435
  if ("color" in paint) {
432
436
  attrs.push(`stroke="${paint.color}"`);
@@ -436,7 +440,7 @@ function coreStrokeAttrs(stroke, registry) {
436
440
  } else {
437
441
  attrs.push(`stroke="url(#${registry.register(paint)})"`);
438
442
  }
439
- attrs.push(`stroke-width="${trimNumber(width)}"`);
443
+ attrs.push(...strokeWidthAttrs(width));
440
444
  if (stroke.cap) attrs.push(`stroke-linecap="${stroke.cap}"`);
441
445
  if (stroke.join) attrs.push(`stroke-linejoin="${stroke.join}"`);
442
446
  if (stroke.dash && stroke.dash.length > 0) {
@@ -459,7 +463,7 @@ function coreStrokeAttrs(stroke, registry) {
459
463
  }
460
464
  function strokeAttrsFor(stroke, registry) {
461
465
  const attrs = paintAttrs(stroke.paint, "stroke", registry, false);
462
- attrs.push(`stroke-width="${trimNumber(stroke.width)}"`);
466
+ attrs.push(...strokeWidthAttrs(stroke.width));
463
467
  const opacity = stroke.opacity ?? (stroke.paint.kind === "solid" ? stroke.paint.opacity : void 0);
464
468
  if (opacity != null && opacity !== 1) {
465
469
  attrs.push(`stroke-opacity="${trimNumber(opacity)}"`);
@@ -717,8 +721,8 @@ function seenFrom(config, keys) {
717
721
  const out = {};
718
722
  if (config === null || typeof config !== "object") return out;
719
723
  const src = config;
720
- for (const k2 of keys) {
721
- if (k2 in src) out[k2] = src[k2];
724
+ for (const k of keys) {
725
+ if (k in src) out[k] = src[k];
722
726
  }
723
727
  return out;
724
728
  }
@@ -726,9 +730,9 @@ function isStale(seen, config, keys) {
726
730
  if (!seen || keys.length === 0) return false;
727
731
  if (config === null || typeof config !== "object") return false;
728
732
  const src = config;
729
- for (const k2 of keys) {
730
- if (!(k2 in seen)) continue;
731
- if (!Object.is(seen[k2], src[k2])) return true;
733
+ for (const k of keys) {
734
+ if (!(k in seen)) continue;
735
+ if (!Object.is(seen[k], src[k])) return true;
732
736
  }
733
737
  return false;
734
738
  }
@@ -745,6 +749,8 @@ function resolveMarkStyle(data, opts) {
745
749
  stale: isStale(data.seen, opts.config, opts.positionDependsOn ?? [])
746
750
  };
747
751
  }
752
+
753
+ // src/annotations/svgNodes.ts
748
754
  var NO_PAINT = { kind: "none" };
749
755
  function toSvgPaint(paint) {
750
756
  if (!paint) return NO_PAINT;
@@ -760,7 +766,7 @@ function toSvgPaint(paint) {
760
766
  function toSvgStroke(stroke) {
761
767
  return {
762
768
  paint: toSvgPaint(stroke.paint),
763
- width: resolveStrokeWidth(stroke.width ?? 1, 1),
769
+ width: stroke.width ?? 1,
764
770
  ...stroke.cap ? { cap: stroke.cap } : {},
765
771
  ...stroke.join ? { join: stroke.join } : {},
766
772
  ...stroke.dash ? { dash: [...stroke.dash] } : {},
@@ -961,7 +967,7 @@ function roundFrac(f2) {
961
967
  function fracContains(box, pt, tol = 0) {
962
968
  return pt.x >= box.x - tol && pt.x <= box.x + box.w + tol && pt.y >= box.y - tol && pt.y <= box.y + box.h + tol;
963
969
  }
964
- function fracIntersects(outer, inner) {
970
+ function fracEncloses(outer, inner) {
965
971
  return inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.w <= outer.x + outer.w && inner.y + inner.h <= outer.y + outer.h;
966
972
  }
967
973
 
@@ -1136,7 +1142,7 @@ function createAnnotationStore(opts) {
1136
1142
  return marksOn(target).filter((a) => fracContains(a.frac, pt, tol)).reverse();
1137
1143
  },
1138
1144
  within(target, box) {
1139
- return marksOn(target).filter((a) => fracIntersects(box, a.frac));
1145
+ return marksOn(target).filter((a) => fracEncloses(box, a.frac));
1140
1146
  },
1141
1147
  isStale(a, config) {
1142
1148
  const keys = targetOf(a.target)?.positionDependsOn ?? [];
@@ -1650,9 +1656,9 @@ function applyRules(seed, ctx, rules) {
1650
1656
  for (const rule of rules) {
1651
1657
  const patch = rule({ ...ctx, leaf: acc });
1652
1658
  if (patch === null) continue;
1653
- for (const [k2, v] of Object.entries(patch)) {
1654
- if (v !== void 0 && acc[k2] === void 0) {
1655
- acc[k2] = v;
1659
+ for (const [k, v] of Object.entries(patch)) {
1660
+ if (v !== void 0 && acc[k] === void 0) {
1661
+ acc[k] = v;
1656
1662
  }
1657
1663
  }
1658
1664
  }
@@ -1662,7 +1668,7 @@ function applyRules(seed, ctx, rules) {
1662
1668
  // src/config/resolve.ts
1663
1669
  function defined(annotations) {
1664
1670
  const out = {};
1665
- for (const [k2, v] of Object.entries(annotations)) if (v !== void 0) out[k2] = v;
1671
+ for (const [k, v] of Object.entries(annotations)) if (v !== void 0) out[k] = v;
1666
1672
  return out;
1667
1673
  }
1668
1674
  function resolveConfigSchema(schema, rules = []) {
@@ -1794,7 +1800,7 @@ function validateConfigSchema(fields) {
1794
1800
  return { valid: errors.length === 0, errors };
1795
1801
  }
1796
1802
 
1797
- // ../theme/dist/chunk-CXVFAOTM.js
1803
+ // ../theme/dist/chunk-D6AFL2JW.js
1798
1804
  var THEME_SOURCES = {
1799
1805
  "weasel": {
1800
1806
  "primitives": {
@@ -2023,15 +2029,15 @@ var THEME_SOURCES = {
2023
2029
  "value": "8px",
2024
2030
  "description": "Diameter of a linear range thumb."
2025
2031
  },
2026
- "slider-track-tint": {
2032
+ "slider-track-mix": {
2027
2033
  "type": "dimension",
2028
2034
  "value": "18%",
2029
- "description": "Accent proportion in a range track. Mixed into a real property, never into another custom property."
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."
2030
2036
  },
2031
- "slider-thumb-tint": {
2037
+ "slider-thumb-mix": {
2032
2038
  "type": "dimension",
2033
2039
  "value": "70%",
2034
- "description": "Accent proportion in a range thumb."
2040
+ "description": "Accent proportion in a range thumb, as a color-mix percentage \u2014 not a color."
2035
2041
  },
2036
2042
  "field-pad-x": {
2037
2043
  "type": "dimension",
@@ -2869,9 +2875,9 @@ function TrialChrome({
2869
2875
  const title = record.title ?? record.instrumentName;
2870
2876
  const collapsedSections = record.collapsedSections ?? NO_SECTIONS;
2871
2877
  const ctx = useMemo(() => {
2872
- const setZoom = (z) => {
2878
+ const setZoom = (z2) => {
2873
2879
  if (!view2d) return;
2874
- updateTrialView(trialId, { ...view2d, zoom: z });
2880
+ updateTrialView(trialId, { ...view2d, zoom: z2 });
2875
2881
  };
2876
2882
  return {
2877
2883
  trialId,
@@ -3176,9 +3182,9 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
3176
3182
  view: record.view,
3177
3183
  setView,
3178
3184
  zoom: view2d?.zoom ?? 1,
3179
- setZoom: (z) => {
3185
+ setZoom: (z2) => {
3180
3186
  if (!view2d) return;
3181
- updateTrialView(record.id, { ...view2d, zoom: z });
3187
+ updateTrialView(record.id, { ...view2d, zoom: z2 });
3182
3188
  },
3183
3189
  activeToolId: resolvedToolId,
3184
3190
  visibleLayers
@@ -3419,7 +3425,7 @@ function addTrial(trials, instruments, instrumentName, options = {}) {
3419
3425
  const config = seedConfig(instrument.defaultConfig(), options.config);
3420
3426
  const state = instrument.initialState(config);
3421
3427
  const record = {
3422
- id: crypto.randomUUID(),
3428
+ id: newId(),
3423
3429
  instrumentName,
3424
3430
  config,
3425
3431
  state,
@@ -3435,7 +3441,7 @@ function cloneTrial(trials, id) {
3435
3441
  if (!source) return trials;
3436
3442
  const clone = {
3437
3443
  ...source,
3438
- id: crypto.randomUUID(),
3444
+ id: newId(),
3439
3445
  config: structuredClone(source.config),
3440
3446
  state: structuredClone(source.state),
3441
3447
  view: structuredClone(source.view),
@@ -3545,6 +3551,67 @@ function LabPalette({ tools }) {
3545
3551
  };
3546
3552
  return /* @__PURE__ */ jsx(PaletteRegion, { contributions, ctx });
3547
3553
  }
3554
+ function currentPage(path, pages) {
3555
+ const here = path.split("?")[0].split("#")[0].replace(/\/$/, "").replace(/\.html$/, "");
3556
+ return pages.findIndex((p) => here.endsWith(p.href.replace(/\.html$/, "")));
3557
+ }
3558
+ function LabSwitcher({ title, pages, path, className }) {
3559
+ const [open, setOpen] = useState(false);
3560
+ const root = useRef(null);
3561
+ const menuId = useId();
3562
+ const here = currentPage(path ?? (typeof window === "undefined" ? "" : window.location.pathname), pages);
3563
+ const close = useCallback(() => setOpen(false), []);
3564
+ useEffect(() => {
3565
+ if (!open) return;
3566
+ const onKey = (e) => {
3567
+ if (e.key === "Escape") {
3568
+ close();
3569
+ root.current?.querySelector("button")?.focus();
3570
+ }
3571
+ };
3572
+ const onDown = (e) => {
3573
+ if (!root.current?.contains(e.target)) close();
3574
+ };
3575
+ document.addEventListener("keydown", onKey);
3576
+ document.addEventListener("pointerdown", onDown);
3577
+ return () => {
3578
+ document.removeEventListener("keydown", onKey);
3579
+ document.removeEventListener("pointerdown", onDown);
3580
+ };
3581
+ }, [open, close]);
3582
+ if (pages.length < 2) {
3583
+ return /* @__PURE__ */ jsx("h1", { className: `lk-switcher-title${className ? ` ${className}` : ""}`, children: title });
3584
+ }
3585
+ return /* @__PURE__ */ jsxs("div", { className: `lk-switcher${className ? ` ${className}` : ""}`, ref: root, children: [
3586
+ /* @__PURE__ */ jsx("h1", { className: "lk-switcher-title", children: /* @__PURE__ */ jsxs(
3587
+ "button",
3588
+ {
3589
+ type: "button",
3590
+ className: "lk-switcher-trigger",
3591
+ "aria-expanded": open,
3592
+ "aria-haspopup": "menu",
3593
+ "aria-controls": open ? menuId : void 0,
3594
+ onClick: () => setOpen((was) => !was),
3595
+ children: [
3596
+ title,
3597
+ /* @__PURE__ */ jsx("span", { className: "lk-switcher-caret", "aria-hidden": "true" })
3598
+ ]
3599
+ }
3600
+ ) }),
3601
+ open && /* @__PURE__ */ jsx("div", { className: "lk-switcher-menu", id: menuId, role: "menu", children: pages.map((page, i) => /* @__PURE__ */ jsx(
3602
+ "a",
3603
+ {
3604
+ href: page.href,
3605
+ role: "menuitem",
3606
+ className: "lk-switcher-item",
3607
+ "aria-current": i === here ? "page" : void 0,
3608
+ onClick: close,
3609
+ children: page.label
3610
+ },
3611
+ page.href
3612
+ )) })
3613
+ ] });
3614
+ }
3548
3615
  function query() {
3549
3616
  if (typeof window === "undefined" || !window.matchMedia) return null;
3550
3617
  return window.matchMedia("(prefers-color-scheme: light)");
@@ -3563,7 +3630,15 @@ function useResolvedMode(mode) {
3563
3630
  }, [mode]);
3564
3631
  return mode === "auto" ? system : mode;
3565
3632
  }
3566
- function LabShell({ title, children, header, footer, mode = "auto" }) {
3633
+ function LabShell({
3634
+ title,
3635
+ children,
3636
+ header,
3637
+ footer,
3638
+ mode = "auto",
3639
+ pages,
3640
+ path
3641
+ }) {
3567
3642
  const resolved = useResolvedMode(mode);
3568
3643
  const outer = useThemeOptional();
3569
3644
  const shell = (
@@ -3571,7 +3646,15 @@ function LabShell({ title, children, header, footer, mode = "auto" }) {
3571
3646
  // carries the element defaults a lab's bare markup is styled by.
3572
3647
  /* @__PURE__ */ jsxs("div", { className: "lk-root lk-shell", "data-wzl-portal-host": "", children: [
3573
3648
  /* @__PURE__ */ jsxs("header", { className: "lk-shell-header", children: [
3574
- /* @__PURE__ */ jsx("h1", { className: "lk-shell-title", children: title }),
3649
+ pages && pages.length > 0 ? /* @__PURE__ */ jsx(
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 }),
3575
3658
  header && /* @__PURE__ */ jsx("div", { className: "lk-shell-header-actions", children: header })
3576
3659
  ] }),
3577
3660
  /* @__PURE__ */ jsx("main", { className: "lk-shell-body", children }),
@@ -3956,6 +4039,6 @@ function Lab({
3956
4039
  ) }) });
3957
4040
  }
3958
4041
 
3959
- export { AnnotationOverlay, AnnotationTargets, DEFAULT_VIEW, Lab, LabContext, LabPalette, LabShell, Trial, TrialBody, TrialChrome, Workspace, addTrial, annotationsFromJSON, applyRules, as2DView, builtinRules, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createAnnotationScene, createAnnotationStore, createMarkDrawOne, defineInstrument, f, fitView, fracContains, fracIntersects, fracToWorld, fromWeaselView, interstellarTheme, isConfigBranch, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, roundFrac, seenFrom, titleCase, toWeaselView, useConfigSchema, useLabContext, validateConfigSchema, worldToFrac };
4042
+ export { AnnotationOverlay, AnnotationTargets, DEFAULT_VIEW, Lab, LabContext, LabPalette, LabShell, LabSwitcher, Trial, TrialBody, TrialChrome, Workspace, addTrial, annotationsFromJSON, applyRules, as2DView, builtinRules, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createAnnotationScene, createAnnotationStore, createMarkDrawOne, currentPage, defineInstrument, f, fitView, fracContains, fracEncloses, fracToWorld, fromWeaselView, interstellarTheme, isConfigBranch, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, roundFrac, seenFrom, titleCase, toWeaselView, useConfigSchema, useLabContext, validateConfigSchema, worldToFrac };
3960
4043
  //# sourceMappingURL=index.js.map
3961
4044
  //# sourceMappingURL=index.js.map