@weasel-js/labkit 1.4.2 → 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 (117) hide show
  1. package/README.md +20 -2
  2. package/dist/_dts/{CanvasStackContext-kjILVnPj.d.ts → CanvasStackContext-kTEZvEgE.d.ts} +1 -1
  3. package/dist/_dts/{PrefsForm.d-CgxUequc.d.ts → PrefsForm.d-DDHFANkc.d.ts} +1 -1
  4. package/dist/_dts/{frac-A4R6v4ld.d.ts → frac-z7ker2Vx.d.ts} +11 -7
  5. package/dist/_dts/{index-CsU9WhjM.d.ts → index-Blv9uQzu.d.ts} +42 -9
  6. package/dist/_dts/types-B9_zrHmb.d.ts +166 -0
  7. package/dist/_dts/{types-BbXpvQa8.d.ts → types-BP2OCcpg.d.ts} +21 -3
  8. package/dist/_dts/types-x92Kfeme.d.ts +62 -0
  9. package/dist/_dts/{useTrialState-DKjqpv20.d.ts → useTrialState-D6Vb3T-g.d.ts} +7 -7
  10. package/dist/canvas/index.d.ts +9 -8
  11. package/dist/canvas/index.js +1 -2
  12. package/dist/chrome/index.d.ts +7 -6
  13. package/dist/chrome/index.js +5 -5
  14. package/dist/{chunk-ULDW42CR.js → chunk-2P6PP5N4.js} +33 -23
  15. package/dist/chunk-2P6PP5N4.js.map +1 -0
  16. package/dist/{chunk-W2FJR5FF.js → chunk-64ZCN3DA.js} +74 -76
  17. package/dist/chunk-64ZCN3DA.js.map +1 -0
  18. package/dist/{chunk-NSOVI3AZ.js → chunk-BKFVHKJH.js} +6 -6
  19. package/dist/{chunk-NSOVI3AZ.js.map → chunk-BKFVHKJH.js.map} +1 -1
  20. package/dist/chunk-E44SU6XS.js +50 -0
  21. package/dist/chunk-E44SU6XS.js.map +1 -0
  22. package/dist/{chunk-WS6ZRV75.js → chunk-FQMJUVHJ.js} +3 -3
  23. package/dist/{chunk-WS6ZRV75.js.map → chunk-FQMJUVHJ.js.map} +1 -1
  24. package/dist/{chunk-FJG4PHTL.js → chunk-I6JCVE24.js} +3 -3
  25. package/dist/chunk-I6JCVE24.js.map +1 -0
  26. package/dist/{chunk-UDXOYZEC.js → chunk-MOM3GOVY.js} +4 -4
  27. package/dist/{chunk-UDXOYZEC.js.map → chunk-MOM3GOVY.js.map} +1 -1
  28. package/dist/{chunk-TN7YSJVU.js → chunk-RS3HRQWU.js} +3 -3
  29. package/dist/{chunk-TN7YSJVU.js.map → chunk-RS3HRQWU.js.map} +1 -1
  30. package/dist/{chunk-UTOEDPNU.js → chunk-SMHP6XZ4.js} +7 -6
  31. package/dist/chunk-SMHP6XZ4.js.map +1 -0
  32. package/dist/{chunk-D5KQ5OY6.js → chunk-TJ7QY3OC.js} +3 -3
  33. package/dist/{chunk-D5KQ5OY6.js.map → chunk-TJ7QY3OC.js.map} +1 -1
  34. package/dist/{chunk-XKENZTNE.js → chunk-U3IYHIAE.js} +71 -55
  35. package/dist/chunk-U3IYHIAE.js.map +1 -0
  36. package/dist/chunk-W3ECWC2K.js +7335 -0
  37. package/dist/chunk-W3ECWC2K.js.map +1 -0
  38. package/dist/controls/index.d.ts +5 -4
  39. package/dist/controls/index.js +3 -3
  40. package/dist/dragdrop/index.d.ts +6 -5
  41. package/dist/dragdrop/index.js +1 -2
  42. package/dist/index.d.ts +139 -26
  43. package/dist/index.js +309 -144
  44. package/dist/index.js.map +1 -1
  45. package/dist/layers/index.d.ts +7 -6
  46. package/dist/layers/index.js +2 -3
  47. package/dist/loupe/index.d.ts +8 -7
  48. package/dist/loupe/index.js +1 -2
  49. package/dist/passthrough/weasel-canvas.d.ts +1 -3
  50. package/dist/passthrough/weasel-canvas.js +1 -1
  51. package/dist/passthrough/weasel-canvas.js.map +1 -1
  52. package/dist/passthrough/weasel-ui.d.ts +98 -20
  53. package/dist/passthrough/weasel-ui.js +1 -2
  54. package/dist/primitives/index.js +3 -4
  55. package/dist/state/index.d.ts +6 -3
  56. package/dist/state/index.js +3 -2
  57. package/dist/state/index.js.map +1 -1
  58. package/dist/styles.css +82 -15
  59. package/dist/surface/index.js +1 -2
  60. package/dist/ui/layers/index.js +1 -2
  61. package/dist/undo/index.d.ts +6 -6
  62. package/package.json +7 -7
  63. package/src/annotations/ExportMenu.test.tsx +16 -0
  64. package/src/annotations/ExportMenu.tsx +0 -5
  65. package/src/annotations/frac.test.ts +4 -4
  66. package/src/annotations/frac.ts +3 -2
  67. package/src/annotations/index.ts +1 -1
  68. package/src/annotations/store.ts +2 -2
  69. package/src/annotations/svgNodes.ts +1 -2
  70. package/src/config/builder.test.ts +56 -2
  71. package/src/config/builder.ts +73 -13
  72. package/src/config/index.ts +16 -1
  73. package/src/config/path.test.ts +109 -0
  74. package/src/config/path.ts +76 -0
  75. package/src/config/resolve.test.ts +93 -2
  76. package/src/config/resolve.ts +72 -29
  77. package/src/config/types.ts +99 -9
  78. package/src/config/visible.ts +6 -4
  79. package/src/controls/ControlPanel.stories.tsx +38 -4
  80. package/src/controls/ControlPanel.test.tsx +114 -0
  81. package/src/controls/ControlPanel.tsx +99 -50
  82. package/src/index.ts +17 -2
  83. package/src/instrument/serializers.ts +36 -0
  84. package/src/instrument/types.ts +4 -2
  85. package/src/lab/Lab.test.tsx +29 -1
  86. package/src/lab/Lab.tsx +10 -1
  87. package/src/lab/LabShell.test.tsx +8 -0
  88. package/src/lab/LabShell.tsx +17 -3
  89. package/src/lab/LabSwitcher.less +85 -0
  90. package/src/lab/LabSwitcher.test.tsx +96 -0
  91. package/src/lab/LabSwitcher.tsx +112 -0
  92. package/src/lab/index.ts +2 -0
  93. package/src/primitives/ZoomControl.less +6 -2
  94. package/src/primitives/ZoomControl.tsx +1 -0
  95. package/src/state/helpers.test.ts +20 -0
  96. package/src/state/helpers.ts +17 -1
  97. package/src/state/store.test.ts +155 -1
  98. package/src/state/store.ts +32 -26
  99. package/src/state/types.ts +24 -3
  100. package/src/state/useTrialState.ts +1 -1
  101. package/src/styles.less +1 -0
  102. package/src/theme/base.less +4 -4
  103. package/src/trial/Trial.config.test.tsx +44 -0
  104. package/src/trial/Trial.tsx +3 -3
  105. package/src/trial/TrialChrome.tsx +10 -5
  106. package/src/trial/trialOps.ts +7 -10
  107. package/dist/_dts/types-lg4TSCb2.d.ts +0 -164
  108. package/dist/_dts/weasel-canvas-FJTFi3ZZ.d.ts +0 -5041
  109. package/dist/chunk-54ZWZ5FQ.js +0 -29997
  110. package/dist/chunk-54ZWZ5FQ.js.map +0 -1
  111. package/dist/chunk-FJG4PHTL.js.map +0 -1
  112. package/dist/chunk-ISSVF5PT.js +0 -7263
  113. package/dist/chunk-ISSVF5PT.js.map +0 -1
  114. package/dist/chunk-ULDW42CR.js.map +0 -1
  115. package/dist/chunk-UTOEDPNU.js.map +0 -1
  116. package/dist/chunk-W2FJR5FF.js.map +0 -1
  117. package/dist/chunk-XKENZTNE.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/annotations/AnnotationsContext.ts","../src/annotations/ExportMenu.tsx","../src/annotations/MarkList.tsx","../src/annotations/toolMap.ts","../src/chrome/builtins.tsx","../src/chrome/merge.ts","../src/chrome/regions/PaletteRegion.tsx","../src/chrome/regions/SidebarRegion.tsx","../src/chrome/regions/StatusRegion.tsx","../src/chrome/regions/TitleBarRegion.tsx","../src/chrome/regions/ToolbarRegion.tsx","../src/chrome/regions/ViewportRegion.tsx"],"names":["g","f","jsx","jsxs","u","r","c","n","o","renderEntry"],"mappings":"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","file":"chunk-64ZCN3DA.js","sourcesContent":["import { createContext, useCallback, useContext, useRef, useSyncExternalStore } from 'react';\nimport type { AnnotationsApi } from './types';\n\n/** Null in a trial whose instrument declares no `annotations`. */\nexport const AnnotationsContext = createContext<AnnotationsApi | null>(null);\n\n/** Re-render the caller whenever any of the store's scenes changes.\n *\n * The API is a stable facade over mutable scenes, so nothing about its\n * identity says a mark was added — a component reading `query()` without this\n * renders one answer and never revises it. */\nfunction useMarkVersion(api: AnnotationsApi | null): void {\n const version = useRef(0);\n useSyncExternalStore(\n useCallback(\n (fn: () => void) =>\n api?.subscribe(() => {\n version.current += 1;\n fn();\n }) ?? (() => {}),\n [api],\n ),\n useCallback(() => version.current, []),\n () => 0,\n );\n}\n\n/** The marks on this trial's targets. Reached through a hook rather than the\n * chrome context, which carries no instrument state — a sidebar panel listing\n * marks pulls it the way `useTrialState()` does. Throws outside a trial whose\n * instrument declares the capability. */\nexport function useAnnotations(): AnnotationsApi {\n const api = useContext(AnnotationsContext);\n useMarkVersion(api);\n if (!api) {\n throw new Error(\n '[labkit] useAnnotations requires an instrument that declares the `annotations` capability',\n );\n }\n return api;\n}\n\n/** The marks on this trial's targets, or null. For chrome that renders in\n * every trial and does something else where there are none. */\nexport function useAnnotationsOptional(): AnnotationsApi | null {\n const api = useContext(AnnotationsContext);\n useMarkVersion(api);\n return api;\n}\n","import { ExportIcon } from '@weasel-js/ui';\nimport { useId, useState } from 'react';\nimport { Callout } from '../passthrough/weasel-ui';\nimport { Toolbar } from '../primitives/Toolbar';\nimport { useAnnotationsOptional } from './AnnotationsContext';\nimport type { CaptureResult } from './types';\n\nconst SCALES = [1, 2, 4] as const;\nconst FORMATS = [\n { id: 'png', label: 'PNG' },\n { id: 'svg', label: 'SVG' },\n] as const;\n\ntype Format = (typeof FORMATS)[number]['id'];\n\nfunction fileName(result: CaptureResult): string {\n return `${result.target.replace(/[^a-z0-9._-]+/gi, '-')}.${result.format}`;\n}\n\n/** A blob the person asked for, handed to the browser's own save flow. The\n * object URL is revoked on the next turn — revoking it synchronously races\n * the navigation the click starts. */\nfunction download(result: CaptureResult): void {\n const url = URL.createObjectURL(result.blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = fileName(result);\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 0);\n}\n\n/** PNG goes to the clipboard as an image. SVG goes as text: `image/svg+xml`\n * needs Chromium's `web ` prefix and nothing else reads it, while the markup\n * is what a person pasting a vector export actually wants. */\nasync function copy(result: CaptureResult): Promise<void> {\n if (result.format === 'svg') {\n await navigator.clipboard.writeText(await result.blob.text());\n return;\n }\n await navigator.clipboard.write([new ClipboardItem({ 'image/png': result.blob })]);\n}\n\n/**\n * Export a target's picture with its marks on it.\n *\n * A panel rather than a button because a mark belongs to a target: with more\n * than one pane, \"export\" on its own does not say what.\n *\n * Reads the store through the hook the way `<MarkList>` does — the chrome\n * context carries no instrument state and is not the route to it.\n */\nexport function ExportMenu() {\n const marks = useAnnotationsOptional();\n const [anchor, setAnchor] = useState<HTMLSpanElement | null>(null);\n const [open, setOpen] = useState(false);\n const [target, setTarget] = useState<string | null>(null);\n const [format, setFormat] = useState<Format>('png');\n const [scale, setScale] = useState<number>(2);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n if (!marks) return null;\n const targets = marks.targets();\n const chosen = target ?? targets[0]?.id ?? null;\n\n const run = async (then: (r: CaptureResult) => void | Promise<void>): Promise<void> => {\n if (!chosen) return;\n setBusy(true);\n setError(null);\n try {\n await then(await marks.capture(chosen, { format, scale }));\n setOpen(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <span className=\"lk-export\" ref={setAnchor}>\n <Toolbar.Button\n iconOnly\n pressed={open}\n aria-label=\"Export\"\n title=\"Export\"\n onClick={() => setOpen((was) => !was)}\n >\n <ExportIcon size={16} />\n </Toolbar.Button>\n <Callout\n triggerRef={{ current: anchor }}\n isOpen={open}\n onOpenChange={setOpen}\n onDismiss={() => setOpen(false)}\n placement=\"bottom end\"\n aria-labelledby={titleId}\n >\n <div className=\"lk-export__panel\">\n <span className=\"lk-export__title\" id={titleId}>\n Export\n </span>\n\n <label className=\"lk-export__row\">\n <span>Target</span>\n {/* Native controls: role, name and keyboard operability for free,\n and a five-control panel is not the place to rebuild them. */}\n <select\n aria-label=\"Target\"\n value={chosen ?? ''}\n onChange={(e) => setTarget(e.target.value)}\n >\n {targets.map((t) => (\n <option key={t.id} value={t.id}>\n {t.id}\n </option>\n ))}\n </select>\n </label>\n\n {/* A <fieldset>/<legend> cannot do this: a legend is outside its\n fieldset's grid formatting context, so it will not take a grid\n column and every choice row drops below its own label. */}\n <div className=\"lk-export__row\">\n <span>Format</span>\n <span className=\"lk-export__choices\" role=\"radiogroup\" aria-label=\"Format\">\n {FORMATS.map((f) => (\n <label key={f.id}>\n <input\n type=\"radio\"\n name=\"lk-export-format\"\n checked={format === f.id}\n onChange={() => setFormat(f.id)}\n />\n {f.label}\n </label>\n ))}\n </span>\n </div>\n\n <div className=\"lk-export__row\">\n <span>Scale</span>\n <span className=\"lk-export__choices\" role=\"radiogroup\" aria-label=\"Scale\">\n {SCALES.map((s) => (\n <label key={s}>\n <input\n type=\"radio\"\n name=\"lk-export-scale\"\n checked={scale === s}\n onChange={() => setScale(s)}\n />\n {s}×\n </label>\n ))}\n </span>\n </div>\n\n <div className=\"lk-export__actions\">\n <button type=\"button\" disabled={busy || !chosen} onClick={() => run(download)}>\n Download\n </button>\n <button type=\"button\" disabled={busy || !chosen} onClick={() => run(copy)}>\n Copy\n </button>\n </div>\n\n {error ? (\n <p className=\"lk-export__error\" role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n </Callout>\n </span>\n );\n}\n","import { Input } from '../passthrough/weasel-ui';\nimport { useAnnotations } from './AnnotationsContext';\nimport type { AnnotationMeaning } from './types';\n\n/** Props for `<MarkList>`. */\nexport interface MarkListProps {\n /** The instrument's vocabulary. Omitted, rows carry no status control —\n * a host that declared none owns what a mark means. */\n meaning?: AnnotationMeaning;\n /** The live config, for answering staleness. */\n config: unknown;\n}\n\n/** Every mark on this trial's targets: what it says, and whether it still\n * describes the picture under it.\n *\n * Reads the store through the hook, which re-renders on every mutation — so a\n * mark drawn on a pane appears here without anything wiring the two. */\nexport function MarkList({ meaning, config }: MarkListProps) {\n const marks = useAnnotations();\n const all = marks.query();\n const statuses = meaning?.statuses ?? [];\n\n if (all.length === 0) {\n return <div className=\"lk-mark-list__empty\">No marks yet</div>;\n }\n\n return (\n <ul className=\"lk-mark-list\">\n {all.map((a) => {\n const stale = marks.isStale(a, config);\n return (\n <li className=\"lk-mark-list__row\" key={a.id}>\n <div className=\"lk-mark-list__head\">\n <span className=\"lk-mark-list__kind\">{a.kind}</span>\n <span className=\"lk-mark-list__target\">{a.target}</span>\n {stale ? <span className=\"lk-mark-list__stale\">stale</span> : null}\n <button\n type=\"button\"\n className=\"lk-mark-list__drop\"\n aria-label={`Remove ${a.kind} on ${a.target}`}\n onClick={() => marks.remove(a.id)}\n >\n ×\n </button>\n </div>\n <Input\n className=\"lk-mark-list__title\"\n aria-label={`Title of ${a.kind} on ${a.target}`}\n value={a.title ?? ''}\n placeholder=\"Untitled\"\n onChange={(next) => marks.update(a.id, { title: next })}\n />\n {statuses.length > 0 ? (\n // A native select: it carries role, name and keyboard operability\n // for free, and a sidebar row is not the place to rebuild them.\n <select\n className=\"lk-mark-list__status\"\n aria-label={`Status of ${a.kind} on ${a.target}`}\n value={a.status ?? ''}\n onChange={(e) => marks.update(a.id, { status: e.target.value })}\n >\n <option value=\"\">—</option>\n {statuses.map((s) => (\n <option key={s.id} value={s.id}>\n {s.label}\n </option>\n ))}\n </select>\n ) : null}\n </li>\n );\n })}\n </ul>\n );\n}\n","import type { BuiltinToolId } from '@weasel-js/core';\nimport {\n ArrowIcon,\n EllipseIcon,\n LineIcon,\n PencilIcon,\n RectIcon,\n SelectIcon,\n TextIcon,\n} from '@weasel-js/ui';\nimport type { TrialTool } from '../tools/types';\nimport type { AnnotationKind } from './types';\n\n/** Which weasel tool an annotation tool drives, and what kind of mark that\n * makes. `select` makes none. */\nexport interface AnnotationToolInfo {\n weaselTool: BuiltinToolId;\n kind?: AnnotationKind;\n}\n\n// `arrow` and `stroke` have no weasel tool of their own, so the kind cannot be\n// read back from the insert — the overlay takes it from the tool id it holds.\nconst TOOLS: Record<string, AnnotationToolInfo> = {\n select: { weaselTool: 'select' },\n stroke: { weaselTool: 'pencil', kind: 'stroke' },\n line: { weaselTool: 'line', kind: 'line' },\n arrow: { weaselTool: 'line', kind: 'arrow' },\n rect: { weaselTool: 'rect', kind: 'rect' },\n ellipse: { weaselTool: 'ellipse', kind: 'ellipse' },\n text: { weaselTool: 'text', kind: 'text' },\n};\n\n/** The palette an instrument gets for declaring `annotations`. Ids share the\n * trial's contribution namespace with `instrument.tools`, and one tool slot\n * holds whichever is active. */\nexport const ANNOTATION_TOOLS: readonly TrialTool[] = [\n { id: 'select', label: 'Select', icon: SelectIcon, group: 'annotate' },\n { id: 'stroke', label: 'Freehand', icon: PencilIcon, group: 'annotate' },\n { id: 'line', label: 'Line', icon: LineIcon, group: 'annotate' },\n { id: 'arrow', label: 'Arrow', icon: ArrowIcon, group: 'annotate' },\n { id: 'rect', label: 'Rectangle', icon: RectIcon, group: 'annotate' },\n { id: 'ellipse', label: 'Ellipse', icon: EllipseIcon, group: 'annotate' },\n { id: 'text', label: 'Text', icon: TextIcon, group: 'annotate' },\n];\n\n/** What `id` drives, or undefined for a tool that is not one of these. */\nexport function annotationToolInfo(id: string | null): AnnotationToolInfo | undefined {\n return id === null ? undefined : TOOLS[id];\n}\n\n/** Every weasel tool the overlay has to mount for this palette to work. */\nexport const ANNOTATION_WEASEL_TOOLS: readonly BuiltinToolId[] = [\n ...new Set(Object.values(TOOLS).map((t) => t.weaselTool)),\n];\n","import {\n CloneIcon,\n CloseIcon,\n FitIcon,\n LoupeIcon,\n RedoIcon,\n ResetIcon,\n SnapshotIcon,\n UndoIcon,\n ZoomInIcon,\n ZoomOutIcon,\n} from '@weasel-js/ui';\nimport { ExportMenu } from '../annotations/ExportMenu';\nimport { MarkList } from '../annotations/MarkList';\nimport { ANNOTATION_TOOLS } from '../annotations/toolMap';\nimport type { ControlRenderer } from '../config/types';\nimport { ControlPanel } from '../controls/ControlPanel';\nimport type { Instrument } from '../instrument/types';\nimport { Select } from '../passthrough/weasel-ui';\nimport { FpsMeter } from '../primitives/FpsMeter';\nimport { ScaleIndicator } from '../primitives/ScaleIndicator';\nimport { ZoomControl } from '../primitives/ZoomControl';\nimport type { TrialChromeContext, TrialContribution } from './types';\n\nconst ZOOM_STEP = 1.25;\n\n/** The property groups inside a contribution are folded under keys prefixed\n * with that contribution's id, so a group called `marks` cannot fold the\n * sidebar section of the same name. `ControlPanel` keys by bare label, so the\n * prefix goes on here and comes off again. */\nfunction groupFolds(\n collapsedSections: Readonly<Record<string, boolean>>,\n id: string,\n): Record<string, boolean> {\n const prefix = `${id}/`;\n const out: Record<string, boolean> = {};\n for (const [key, value] of Object.entries(collapsedSections)) {\n if (key.startsWith(prefix)) out[key.slice(prefix.length)] = value;\n }\n return out;\n}\n\n/**\n * The contributions a trial gets from what its instrument declared. This is\n * the whole \"declaring a capability provides the chrome\" rule — it replaces\n * the presence checks that used to be spread across the runtime.\n */\nexport function builtinContributions(\n instrument: Instrument,\n ctx: TrialChromeContext,\n controls?: Record<string, ControlRenderer>,\n): TrialContribution[] {\n const out: TrialContribution[] = [];\n const zoom = ctx.zoom;\n\n // Weasel history is the undo authority for marks, so declaring `annotations`\n // gets the buttons whether or not the instrument also declares `undo`.\n if (instrument.undo != null || instrument.annotations != null) {\n out.push({\n id: 'undo',\n region: 'toolbar',\n group: 'history',\n item: {\n icon: UndoIcon,\n label: 'Undo',\n shortcut: 'Mod+Z',\n disabled: !ctx.canUndo,\n onActivate: ctx.undo,\n },\n });\n out.push({\n id: 'redo',\n region: 'toolbar',\n group: 'history',\n item: {\n icon: RedoIcon,\n label: 'Redo',\n shortcut: 'Mod+Shift+Z',\n disabled: !ctx.canRedo,\n onActivate: ctx.redo,\n },\n });\n }\n\n // Declaring `annotations` provides the drawing palette, the way declaring\n // `tools` provides the instrument's own; both write the same tool slot.\n // A tool id shares the contribution namespace, so a tool called `close`\n // collides with the built-in close button and throws — and so do two tools\n // of the same name from these two sources.\n const tools = [\n ...(instrument.annotations ? ANNOTATION_TOOLS : []),\n ...(instrument.tools?.tools ?? []),\n ];\n for (const t of tools) {\n out.push({\n id: t.id,\n region: 'palette',\n group: t.group,\n item: { icon: t.icon, label: t.label, shortcut: t.shortcut },\n });\n }\n\n if (instrument.loupe != null) {\n out.push({\n id: 'loupe',\n region: 'toolbar',\n group: 'view',\n item: {\n icon: LoupeIcon,\n label: 'Loupe',\n pressed: ctx.loupeOn,\n onActivate: ctx.toggleLoupe,\n },\n });\n }\n\n // Zoom acts on the view of the trial, so it is a viewport control. A trial\n // holding a non-2D view reports zoom as null and gets none of this.\n if (instrument.canvas != null && zoom !== null) {\n out.push({\n id: 'zoom-out',\n region: 'viewport',\n group: 'zoom',\n item: {\n icon: ZoomOutIcon,\n label: 'Zoom out',\n onActivate: () => ctx.setZoom(zoom / ZOOM_STEP),\n },\n });\n out.push({\n id: 'zoom-in',\n region: 'viewport',\n group: 'zoom',\n item: {\n icon: ZoomInIcon,\n label: 'Zoom in',\n onActivate: () => ctx.setZoom(zoom * ZOOM_STEP),\n },\n });\n out.push({\n id: 'actual-size',\n region: 'viewport',\n group: 'zoom',\n item: { icon: FitIcon, label: 'Actual size', onActivate: () => ctx.setZoom(1) },\n });\n out.push({\n id: 'zoom-control',\n region: 'viewport',\n group: 'zoom',\n render: (c) => <ZoomControl zoom={c.zoom ?? 1} onZoomChange={c.setZoom} />,\n });\n out.push({\n id: 'scale',\n region: 'status',\n group: 'view',\n render: () => <ScaleIndicator />,\n });\n out.push({\n id: 'fps',\n region: 'status',\n group: 'view',\n end: true,\n render: () => <FpsMeter />,\n });\n }\n\n if (instrument.annotations != null) {\n // A popover, so it takes the `render` escape: the panel picks a target,\n // and an icon button cannot ask which one.\n out.push({\n id: 'export',\n region: 'toolbar',\n group: 'trial',\n end: true,\n render: () => <ExportMenu />,\n });\n out.push({\n id: 'marks',\n region: 'sidebar',\n item: {\n title: 'Marks',\n body: <MarkList meaning={instrument.annotations.meaning} config={ctx.config} />,\n },\n });\n }\n\n if (Object.keys(ctx.configSchema.group.children).length > 0) {\n out.push({\n id: 'settings',\n region: 'sidebar',\n item: {\n title: 'Settings',\n body: (\n <ControlPanel\n schema={ctx.configSchema}\n config={ctx.config as Record<string, unknown>}\n setConfig={(key, value) => ctx.setConfig(String(key), value)}\n renderers={controls}\n collapsed={groupFolds(ctx.collapsedSections, 'settings')}\n onCollapse={(label, next) => ctx.setSectionCollapsed(`settings/${label}`, next)}\n />\n ),\n },\n });\n }\n\n if (ctx.savedSnapshots.length > 0) {\n // A picker is not an icon button, so it takes the render escape. Held at\n // `selectedKey={null}` so it stays a \"load one\" action rather than\n // drifting into a display of what was loaded last.\n out.push({\n id: 'snapshot-load',\n region: 'toolbar',\n group: 'trial',\n end: true,\n render: (c) => (\n <Select\n className=\"lk-toolbar__load-select\"\n aria-label=\"Load snapshot\"\n placeholder=\"Load…\"\n selectedKey={null}\n options={c.savedSnapshots.map((sn) => ({ value: sn.id, label: sn.name }))}\n onSelectionChange={(id) => {\n if (id != null) c.loadSnapshot(String(id));\n }}\n />\n ),\n });\n }\n out.push({\n id: 'clone',\n region: 'titlebar',\n end: true,\n item: { icon: CloneIcon, label: 'Clone trial', onActivate: ctx.clone },\n });\n out.push({\n id: 'reset',\n region: 'titlebar',\n end: true,\n item: { icon: ResetIcon, label: 'Reset trial', onActivate: ctx.reset },\n });\n out.push({\n id: 'snapshot',\n region: 'titlebar',\n end: true,\n item: {\n icon: SnapshotIcon,\n label: 'Save snapshot',\n shortcut: 'Mod+S',\n onActivate: () => ctx.saveSnapshot(),\n },\n });\n out.push({\n id: 'close',\n region: 'titlebar',\n end: true,\n item: {\n icon: CloseIcon,\n label: ctx.isLastTrial ? 'Cannot close the last trial' : 'Close trial',\n danger: true,\n disabled: ctx.isLastTrial,\n onActivate: ctx.close,\n },\n });\n\n return out;\n}\n","import type { TrialContribution } from './types';\n\n/**\n * Concatenate contribution bundles into one list, preserving order — order\n * decides how a region lays its contributions out, so it is part of the\n * result, not an accident of it.\n *\n * Throws on a duplicate id rather than dropping one: a contribution silently\n * losing to a later bundle is the failure a registry exists to prevent.\n */\nexport function mergeContributions(\n ...bundles: readonly TrialContribution[][]\n): TrialContribution[] {\n const out: TrialContribution[] = [];\n const seen = new Set<string>();\n for (const bundle of bundles) {\n for (const entry of bundle) {\n if (seen.has(entry.id)) {\n throw new Error(\n `[labkit] mergeContributions: duplicate contribution id \"${entry.id}\". ` +\n 'Two bundles registered the same id; rename one, or suppress the ' +\n 'built-in before adding yours.',\n );\n }\n seen.add(entry.id);\n out.push(entry);\n }\n }\n return out;\n}\n\n/**\n * Drop contributions by id. Throws when an id is not present: a typo that\n * silently suppresses nothing is the same class of bug as a duplicate id\n * silently winning.\n */\nexport function suppressContributions(\n bundle: readonly TrialContribution[],\n ids: readonly string[],\n): TrialContribution[] {\n const present = new Set(bundle.map((c) => c.id));\n for (const id of ids) {\n if (!present.has(id)) {\n throw new Error(\n `[labkit] cannot suppress \"${id}\": no contribution with that id. ` +\n `Present ids: ${[...present].join(', ')}`,\n );\n }\n }\n const drop = new Set(ids);\n return bundle.filter((c) => !drop.has(c.id));\n}\n","import { ToolButton, ToolGroup } from '../../passthrough/weasel-ui';\nimport { useRovingTabIndex } from '../../primitives/useRovingTabIndex';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Props for `<PaletteRegion>`. */\nexport interface PaletteRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n}\n\n/** A trial's tool strip. Selection lives in the trial's or the lab's tool\n * slot; this region only reflects it. */\nexport function PaletteRegion({ contributions, ctx }: PaletteRegionProps) {\n const { ref, onKeyDown } = useRovingTabIndex<HTMLDivElement>('vertical');\n if (contributions.length === 0) return null;\n return (\n <div\n ref={ref}\n className=\"lk-palette-region\"\n role=\"toolbar\"\n aria-label=\"Tools\"\n aria-orientation=\"vertical\"\n onKeyDown={onKeyDown}\n >\n <ToolGroup orientation=\"vertical\">\n {contributions.map((c) => {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== 'palette' || !c.item) return null;\n const { icon: Icon, label, shortcut, disabled } = c.item;\n return (\n <ToolButton\n key={c.id}\n icon={<Icon size={16} />}\n label={label}\n shortcut={shortcut}\n active={ctx.activeToolId === c.id}\n disabled={disabled}\n onClick={() => ctx.setActiveTool(c.id)}\n />\n );\n })}\n </ToolGroup>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { SidebarSection, TrialChromeContext, TrialContribution } from '../types';\n\n/** Props for `<SidebarRegion>`. */\nexport interface SidebarRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n}\n\nfunction Section({\n title,\n collapsed,\n onCollapsedChange,\n onUndock,\n children,\n}: {\n title: string;\n collapsed: boolean;\n onCollapsedChange: (collapsed: boolean) => void;\n onUndock?: () => void;\n children: ReactNode;\n}) {\n return (\n <section className=\"lk-sidebar-section\">\n <div className=\"lk-sidebar-section__bar\">\n <button\n type=\"button\"\n className=\"lk-sidebar-section__head\"\n aria-expanded={!collapsed}\n onClick={() => onCollapsedChange(!collapsed)}\n >\n {title}\n </button>\n {onUndock ? (\n <button\n type=\"button\"\n className=\"lk-sidebar-section__undock\"\n aria-label={`Undock ${title}`}\n title={`Undock ${title}`}\n onClick={onUndock}\n >\n ⧉\n </button>\n ) : null}\n </div>\n {collapsed ? null : <div className=\"lk-sidebar-section__body\">{children}</div>}\n </section>\n );\n}\n\n/** Lays a trial's `sidebar` contributions out as titled, collapsible sections.\n * A section the trial has torn out is not rendered here — `Trial` portals its\n * body into the workspace instead. */\nexport function SidebarRegion({ contributions, ctx }: SidebarRegionProps) {\n if (contributions.length === 0) return null;\n return (\n <>\n {contributions.map((c) => {\n if (c.render) return <div key={c.id}>{c.render(ctx)}</div>;\n if (c.region !== 'sidebar' || !c.item) return null;\n if (ctx.undockedPanels.includes(c.id)) return null;\n const item = c.item as SidebarSection;\n return (\n <Section\n key={c.id}\n title={item.title}\n collapsed={ctx.collapsedSections[c.id] ?? item.defaultCollapsed ?? false}\n onCollapsedChange={(next) => ctx.setSectionCollapsed(c.id, next)}\n onUndock={\n item.undockable === false\n ? undefined\n : () => ctx.undockPanel(c.id, item.undockAs ?? 'tile')\n }\n >\n {item.body}\n </Section>\n );\n })}\n </>\n );\n}\n","import { StatusBar } from '../../primitives/StatusBar';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Props for `<StatusRegion>`. */\nexport interface StatusRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n}\n\n/** Lays a trial's `status` contributions out as readouts. */\nexport function StatusRegion({ contributions, ctx }: StatusRegionProps) {\n if (contributions.length === 0) return null;\n return (\n <StatusBar>\n {contributions.map((c) => {\n if (c.render)\n return (\n <StatusBar.Section key={c.id} end={c.end}>\n {c.render(ctx)}\n </StatusBar.Section>\n );\n if (c.region !== 'status' || !c.item) return null;\n return (\n <StatusBar.Section key={c.id} end={c.end}>\n <span title={c.item.title}>{c.item.text}</span>\n </StatusBar.Section>\n );\n })}\n </StatusBar>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Which end of the title bar a `<TitleBarRegion>` lays out: `'lead'` renders\n * the contributions that leave `end` unset, before the title; `'actions'`\n * renders the ones that set it, after everything else. */\nexport type TitleBarPlacement = 'lead' | 'actions';\n\n/** Props for `<TitleBarRegion>`. */\nexport interface TitleBarRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n /** Defaults to `'actions'`, where the built-ins live. */\n placement?: TitleBarPlacement;\n}\n\nfunction renderEntry(c: TrialContribution, ctx: TrialChromeContext): ReactNode {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== 'titlebar' || !c.item) return null;\n const { icon: Icon, label, shortcut, disabled, danger, onActivate } = c.item;\n return (\n <button\n key={c.id}\n type=\"button\"\n className={`lk-titlebar-button${danger ? ' lk-titlebar-button--danger' : ''}`}\n disabled={disabled}\n aria-label={label}\n title={shortcut ? `${label} (${shortcut})` : label}\n onClick={() => onActivate(ctx)}\n // The bar behind these buttons is the window drag surface, so a press\n // that lands here must not also start a drag.\n onPointerDown={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n >\n <Icon size={14} />\n </button>\n );\n}\n\n/** Lays a trial's `titlebar` contributions out at one end of the bar. */\nexport function TitleBarRegion({ contributions, ctx, placement = 'actions' }: TitleBarRegionProps) {\n const wanted = placement === 'actions';\n const mine = contributions.filter((c) => (c.end ?? false) === wanted);\n if (mine.length === 0) return null;\n return (\n <span className={wanted ? 'lk-trial__titlebar-actions' : 'lk-trial__titlebar-lead'}>\n {mine.map((c) => renderEntry(c, ctx))}\n </span>\n );\n}\n","import type { ReactNode } from 'react';\nimport { Toolbar } from '../../primitives/Toolbar';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Props for `<ToolbarRegion>`. */\nexport interface ToolbarRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n}\n\ninterface Group {\n key: string;\n end: boolean;\n entries: TrialContribution[];\n}\n\n/** Bucket by group, preserving first-appearance order. Ungrouped\n * contributions each become their own bucket so they stay in place. */\nfunction groupsOf(contributions: readonly TrialContribution[]): Group[] {\n const groups: Group[] = [];\n const byKey = new Map<string, Group>();\n for (const c of contributions) {\n if (c.group == null) {\n groups.push({ key: c.id, end: c.end ?? false, entries: [c] });\n continue;\n }\n let g = byKey.get(c.group);\n if (!g) {\n g = { key: c.group, end: c.end ?? false, entries: [] };\n byKey.set(c.group, g);\n groups.push(g);\n }\n g.entries.push(c);\n }\n return groups;\n}\n\nfunction renderEntry(c: TrialContribution, ctx: TrialChromeContext): ReactNode {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== 'toolbar' || !c.item) return null;\n const { icon: Icon, label, shortcut, disabled, danger, showLabel, pressed, onActivate } = c.item;\n return (\n <Toolbar.Button\n key={c.id}\n iconOnly={!showLabel}\n variant={danger ? 'danger' : 'default'}\n disabled={disabled}\n pressed={pressed}\n aria-label={label}\n title={shortcut ? `${label} (${shortcut})` : label}\n onClick={() => onActivate(ctx)}\n >\n <Icon size={16} />\n {showLabel ? <span>{label}</span> : null}\n </Toolbar.Button>\n );\n}\n\n/** Lays a trial's `toolbar` contributions out, grouped by their `group`. */\nexport function ToolbarRegion({ contributions, ctx }: ToolbarRegionProps) {\n if (contributions.length === 0) return null;\n const groups = groupsOf(contributions);\n return (\n <Toolbar aria-label=\"Trial actions\">\n {groups.map((g) => (\n <Toolbar.Group key={g.key} end={g.end} aria-label={g.key}>\n {g.entries.map((c) => renderEntry(c, ctx))}\n </Toolbar.Group>\n ))}\n </Toolbar>\n );\n}\n","import { useRovingTabIndex } from '../../primitives/useRovingTabIndex';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Props for `<ViewportRegion>`. */\nexport interface ViewportRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n}\n\n/**\n * Controls acting on the view of a trial. Anchored inside the content well\n * rather than in the toolbar, which acts on the trial itself.\n */\nexport function ViewportRegion({ contributions, ctx }: ViewportRegionProps) {\n const { ref, onKeyDown } = useRovingTabIndex<HTMLDivElement>();\n if (contributions.length === 0) return null;\n return (\n <div\n ref={ref}\n className=\"lk-viewport-controls\"\n role=\"toolbar\"\n aria-label=\"View\"\n onKeyDown={onKeyDown}\n >\n {contributions.map((c) => {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== 'viewport' || !c.item) return null;\n const { icon: Icon, label, disabled, onActivate } = c.item;\n return (\n <button\n key={c.id}\n type=\"button\"\n className=\"lk-viewport-controls__button\"\n aria-label={label}\n title={label}\n disabled={disabled}\n onClick={() => onActivate(ctx)}\n >\n <Icon size={16} />\n </button>\n );\n })}\n </div>\n );\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { r3, n2 } from './chunk-ISSVF5PT.js';
2
- import { openPointerSession } from './chunk-54ZWZ5FQ.js';
1
+ import { r5, n4 } from './chunk-W3ECWC2K.js';
2
+ import { openPointerSession } from '@weasel-js/core';
3
3
  import { useState, useRef, useId, useEffect, Fragment as Fragment$1 } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -119,7 +119,7 @@ function LayerList({
119
119
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
120
120
  /* @__PURE__ */ jsxs("div", { className: cls.join(" "), children: [
121
121
  nested && (kids.length > 0 ? /* @__PURE__ */ jsx(
122
- r3,
122
+ r5,
123
123
  {
124
124
  open,
125
125
  onToggle: () => toggleCollapsed(layer.id),
@@ -135,7 +135,7 @@ function LayerList({
135
135
  className: "lk-layer-list__handle",
136
136
  "aria-label": `Reorder ${layer.label}`,
137
137
  onPointerDown: (e) => startDrag(e, parentId, index),
138
- children: /* @__PURE__ */ jsx(n2, { size: 13 })
138
+ children: /* @__PURE__ */ jsx(n4, { size: 13 })
139
139
  }
140
140
  ),
141
141
  /* @__PURE__ */ jsx(
@@ -166,5 +166,5 @@ function LayerList({
166
166
  }
167
167
 
168
168
  export { LayerList };
169
- //# sourceMappingURL=chunk-NSOVI3AZ.js.map
170
- //# sourceMappingURL=chunk-NSOVI3AZ.js.map
169
+ //# sourceMappingURL=chunk-BKFVHKJH.js.map
170
+ //# sourceMappingURL=chunk-BKFVHKJH.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/layers/LayerList.tsx"],"names":["n","Fragment","r"],"mappings":"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","file":"chunk-NSOVI3AZ.js","sourcesContent":["import { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport { Disclosure } from '@weasel-js/ui';\nimport {\n Fragment,\n type ReactNode,\n type PointerEvent as ReactPointerEvent,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport type { LayerDescriptor } from '../instrument/types';\nimport { DragHandleGlyph } from '../passthrough/weasel-ui';\n\n/** A layer that may itself contain layers. A plain `LayerDescriptor` is one\n * of these with no children, so a flat list needs no change. */\nexport interface LayerTreeNode extends LayerDescriptor {\n children?: LayerTreeNode[];\n /** Render this node's children hidden on first paint. Ignored once\n * `collapsedIds` is supplied. */\n defaultCollapsed?: boolean;\n}\n\n/** Props for `<LayerList>`. */\nexport interface LayerListProps {\n layers: LayerTreeNode[];\n visibility: Record<string, boolean>;\n /** The whole tree, with the dragged node moved. Reordering is scoped to a\n * node's siblings: a drag never changes which parent a layer sits under. */\n onReorder: (newOrder: LayerTreeNode[]) => void;\n onToggle: (id: string, visible: boolean) => void;\n /** Ids whose children are hidden. Supplying it makes collapse controlled;\n * without it the list keeps its own, seeded from `defaultCollapsed`. */\n collapsedIds?: readonly string[];\n onCollapsedChange?: (ids: string[]) => void;\n className?: string;\n}\n\ninterface DragState {\n /** Id of the dragged row's parent, or null at the top level. */\n parentId: string | null;\n fromIndex: number;\n toIndex: number;\n startY: number;\n /** Row height plus row gap, measured when the drag starts. */\n pitch: number;\n}\n\n/** Which row the drag currently marks. */\ninterface DragMark {\n parentId: string | null;\n index: number;\n}\n\nfunction findNode(nodes: LayerTreeNode[], id: string): LayerTreeNode | null {\n for (const node of nodes) {\n if (node.id === id) return node;\n const hit = node.children ? findNode(node.children, id) : null;\n if (hit) return hit;\n }\n return null;\n}\n\nfunction siblingsOf(layers: LayerTreeNode[], parentId: string | null): LayerTreeNode[] {\n if (parentId === null) return layers;\n return findNode(layers, parentId)?.children ?? [];\n}\n\nfunction withChildren(\n nodes: LayerTreeNode[],\n parentId: string | null,\n next: LayerTreeNode[],\n): LayerTreeNode[] {\n if (parentId === null) return next;\n return nodes.map((node) => {\n if (node.id === parentId) return { ...node, children: next };\n if (node.children) return { ...node, children: withChildren(node.children, parentId, next) };\n return node;\n });\n}\n\nfunction collectCollapsed(nodes: LayerTreeNode[], into: Set<string>): Set<string> {\n for (const node of nodes) {\n if (node.defaultCollapsed) into.add(node.id);\n if (node.children) collectCollapsed(node.children, into);\n }\n return into;\n}\n\nfunction anyNested(nodes: LayerTreeNode[]): boolean {\n return nodes.some((n) => (n.children?.length ?? 0) > 0);\n}\n\n/** A reorderable list of layers with per-layer visibility toggles. Layers\n * marked `alwaysOn` are pinned and cannot be reordered or hidden. Layers with\n * `children` render as an expandable subtree. */\nexport function LayerList({\n layers,\n visibility,\n onReorder,\n onToggle,\n collapsedIds,\n onCollapsedChange,\n className,\n}: LayerListProps) {\n const [dragMark, setDragMark] = useState<DragMark | null>(null);\n const [ownCollapsed, setOwnCollapsed] = useState<Set<string>>(() =>\n collectCollapsed(layers, new Set()),\n );\n const dragRef = useRef<DragState | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n const uid = useId();\n // The session's callbacks outlive the render that opened them, so the commit\n // reads its inputs here rather than from that render's closure.\n const commitRef = useRef({ layers, onReorder });\n commitRef.current = { layers, onReorder };\n\n useEffect(() => () => sessionRef.current?.cancel(), []);\n\n const collapsed = collapsedIds ? new Set(collapsedIds) : ownCollapsed;\n\n const toggleCollapsed = (id: string) => {\n const next = new Set(collapsed);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n if (!collapsedIds) setOwnCollapsed(next);\n onCollapsedChange?.([...next]);\n };\n\n if (layers.length === 0) {\n return (\n <div className={className ? `lk-layer-list ${className}` : 'lk-layer-list'}>\n <div className=\"lk-layer-list__empty\">No layers</div>\n </div>\n );\n }\n\n const clearDrag = () => {\n dragRef.current = null;\n sessionRef.current = null;\n setDragMark(null);\n };\n\n const startDrag = (e: ReactPointerEvent<HTMLElement>, parentId: string | null, index: number) => {\n // Measured at grab time rather than hardcoded: a restyle that changes row\n // height or gap would otherwise silently skew every drag distance.\n const row = e.currentTarget.closest('.lk-layer-list__row');\n const list = row?.parentElement;\n const gap = list ? Number.parseFloat(getComputedStyle(list).rowGap) || 0 : 0;\n const pitch = row ? row.getBoundingClientRect().height + gap : 0;\n const drag: DragState = {\n parentId,\n fromIndex: index,\n toIndex: index,\n startY: e.clientY,\n pitch: pitch > 0 ? pitch : 1,\n };\n dragRef.current = drag;\n setDragMark({ parentId, index });\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n const delta = Math.round((ev.clientY - drag.startY) / drag.pitch);\n const siblings = siblingsOf(commitRef.current.layers, parentId);\n const last = siblings.filter((l) => !l.alwaysOn).length - 1;\n drag.toIndex = Math.min(last, Math.max(0, drag.fromIndex + delta));\n setDragMark({ parentId, index: drag.toIndex });\n },\n onEnd: () => {\n const { layers: tree, onReorder: commit } = commitRef.current;\n const { fromIndex, toIndex } = drag;\n clearDrag();\n if (toIndex === fromIndex) return;\n const siblings = siblingsOf(tree, parentId);\n const rows = siblings.filter((l) => !l.alwaysOn);\n const locked = siblings.filter((l) => l.alwaysOn);\n const next = [...rows];\n const [moved] = next.splice(fromIndex, 1);\n if (moved) next.splice(toIndex, 0, moved);\n commit(withChildren(tree, parentId, [...next, ...locked]));\n },\n // An interrupted drag never named a destination, so it reorders nothing.\n onCancel: clearDrag,\n });\n };\n\n // A twisty column on every row keeps labels aligned across depths, but a\n // flat list would only be paying 20px for it.\n const nested = anyNested(layers);\n\n function renderRow(\n layer: LayerTreeNode,\n parentId: string | null,\n index: number,\n path: string,\n pinned: boolean,\n ): ReactNode {\n const kids = layer.children ?? [];\n const open = kids.length > 0 && !collapsed.has(layer.id);\n const bodyId = `${uid}${path}`;\n const dragging = !pinned && dragMark?.parentId === parentId && dragMark.index === index;\n const cls = ['lk-layer-list__row'];\n if (dragging) cls.push('lk-layer-list__row--dragging');\n if (pinned) cls.push('lk-layer-list__row--pinned');\n return (\n <Fragment key={layer.id}>\n <div className={cls.join(' ')}>\n {nested &&\n (kids.length > 0 ? (\n <Disclosure\n open={open}\n onToggle={() => toggleCollapsed(layer.id)}\n label={`${layer.label} sublayers`}\n controls={bodyId}\n />\n ) : (\n <span className=\"lk-layer-list__twisty-gap\" />\n ))}\n {pinned ? (\n <span className=\"lk-layer-list__lock\" role=\"img\" aria-label=\"Always on\">\n 🔒\n </span>\n ) : (\n <>\n <button\n type=\"button\"\n className=\"lk-layer-list__handle\"\n aria-label={`Reorder ${layer.label}`}\n onPointerDown={(e) => startDrag(e, parentId, index)}\n >\n <DragHandleGlyph size={13} />\n </button>\n <input\n className=\"lk-layer-list__check\"\n type=\"checkbox\"\n checked={visibility[layer.id] !== false}\n onChange={(e) => onToggle(layer.id, e.target.checked)}\n aria-label={`Toggle ${layer.label}`}\n />\n </>\n )}\n <span className=\"lk-layer-list__label\">{layer.label}</span>\n </div>\n {open && (\n <div id={bodyId} className=\"lk-layer-list__children\">\n {renderLevel(kids, layer.id, path)}\n </div>\n )}\n </Fragment>\n );\n }\n\n function renderLevel(nodes: LayerTreeNode[], parentId: string | null, path: string): ReactNode {\n const reorderable = nodes.filter((l) => !l.alwaysOn);\n const pinned = nodes.filter((l) => l.alwaysOn);\n return (\n <>\n {reorderable.map((layer, i) => renderRow(layer, parentId, i, `${path}-${i}`, false))}\n {pinned.map((layer, i) => renderRow(layer, parentId, i, `${path}-p${i}`, true))}\n </>\n );\n }\n\n return (\n <div className={className ? `lk-layer-list ${className}` : 'lk-layer-list'}>\n {renderLevel(layers, null, '')}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/layers/LayerList.tsx"],"names":["n","Fragment","r"],"mappings":"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","file":"chunk-BKFVHKJH.js","sourcesContent":["import { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport { Disclosure } from '@weasel-js/ui';\nimport {\n Fragment,\n type ReactNode,\n type PointerEvent as ReactPointerEvent,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport type { LayerDescriptor } from '../instrument/types';\nimport { DragHandleGlyph } from '../passthrough/weasel-ui';\n\n/** A layer that may itself contain layers. A plain `LayerDescriptor` is one\n * of these with no children, so a flat list needs no change. */\nexport interface LayerTreeNode extends LayerDescriptor {\n children?: LayerTreeNode[];\n /** Render this node's children hidden on first paint. Ignored once\n * `collapsedIds` is supplied. */\n defaultCollapsed?: boolean;\n}\n\n/** Props for `<LayerList>`. */\nexport interface LayerListProps {\n layers: LayerTreeNode[];\n visibility: Record<string, boolean>;\n /** The whole tree, with the dragged node moved. Reordering is scoped to a\n * node's siblings: a drag never changes which parent a layer sits under. */\n onReorder: (newOrder: LayerTreeNode[]) => void;\n onToggle: (id: string, visible: boolean) => void;\n /** Ids whose children are hidden. Supplying it makes collapse controlled;\n * without it the list keeps its own, seeded from `defaultCollapsed`. */\n collapsedIds?: readonly string[];\n onCollapsedChange?: (ids: string[]) => void;\n className?: string;\n}\n\ninterface DragState {\n /** Id of the dragged row's parent, or null at the top level. */\n parentId: string | null;\n fromIndex: number;\n toIndex: number;\n startY: number;\n /** Row height plus row gap, measured when the drag starts. */\n pitch: number;\n}\n\n/** Which row the drag currently marks. */\ninterface DragMark {\n parentId: string | null;\n index: number;\n}\n\nfunction findNode(nodes: LayerTreeNode[], id: string): LayerTreeNode | null {\n for (const node of nodes) {\n if (node.id === id) return node;\n const hit = node.children ? findNode(node.children, id) : null;\n if (hit) return hit;\n }\n return null;\n}\n\nfunction siblingsOf(layers: LayerTreeNode[], parentId: string | null): LayerTreeNode[] {\n if (parentId === null) return layers;\n return findNode(layers, parentId)?.children ?? [];\n}\n\nfunction withChildren(\n nodes: LayerTreeNode[],\n parentId: string | null,\n next: LayerTreeNode[],\n): LayerTreeNode[] {\n if (parentId === null) return next;\n return nodes.map((node) => {\n if (node.id === parentId) return { ...node, children: next };\n if (node.children) return { ...node, children: withChildren(node.children, parentId, next) };\n return node;\n });\n}\n\nfunction collectCollapsed(nodes: LayerTreeNode[], into: Set<string>): Set<string> {\n for (const node of nodes) {\n if (node.defaultCollapsed) into.add(node.id);\n if (node.children) collectCollapsed(node.children, into);\n }\n return into;\n}\n\nfunction anyNested(nodes: LayerTreeNode[]): boolean {\n return nodes.some((n) => (n.children?.length ?? 0) > 0);\n}\n\n/** A reorderable list of layers with per-layer visibility toggles. Layers\n * marked `alwaysOn` are pinned and cannot be reordered or hidden. Layers with\n * `children` render as an expandable subtree. */\nexport function LayerList({\n layers,\n visibility,\n onReorder,\n onToggle,\n collapsedIds,\n onCollapsedChange,\n className,\n}: LayerListProps) {\n const [dragMark, setDragMark] = useState<DragMark | null>(null);\n const [ownCollapsed, setOwnCollapsed] = useState<Set<string>>(() =>\n collectCollapsed(layers, new Set()),\n );\n const dragRef = useRef<DragState | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n const uid = useId();\n // The session's callbacks outlive the render that opened them, so the commit\n // reads its inputs here rather than from that render's closure.\n const commitRef = useRef({ layers, onReorder });\n commitRef.current = { layers, onReorder };\n\n useEffect(() => () => sessionRef.current?.cancel(), []);\n\n const collapsed = collapsedIds ? new Set(collapsedIds) : ownCollapsed;\n\n const toggleCollapsed = (id: string) => {\n const next = new Set(collapsed);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n if (!collapsedIds) setOwnCollapsed(next);\n onCollapsedChange?.([...next]);\n };\n\n if (layers.length === 0) {\n return (\n <div className={className ? `lk-layer-list ${className}` : 'lk-layer-list'}>\n <div className=\"lk-layer-list__empty\">No layers</div>\n </div>\n );\n }\n\n const clearDrag = () => {\n dragRef.current = null;\n sessionRef.current = null;\n setDragMark(null);\n };\n\n const startDrag = (e: ReactPointerEvent<HTMLElement>, parentId: string | null, index: number) => {\n // Measured at grab time rather than hardcoded: a restyle that changes row\n // height or gap would otherwise silently skew every drag distance.\n const row = e.currentTarget.closest('.lk-layer-list__row');\n const list = row?.parentElement;\n const gap = list ? Number.parseFloat(getComputedStyle(list).rowGap) || 0 : 0;\n const pitch = row ? row.getBoundingClientRect().height + gap : 0;\n const drag: DragState = {\n parentId,\n fromIndex: index,\n toIndex: index,\n startY: e.clientY,\n pitch: pitch > 0 ? pitch : 1,\n };\n dragRef.current = drag;\n setDragMark({ parentId, index });\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n const delta = Math.round((ev.clientY - drag.startY) / drag.pitch);\n const siblings = siblingsOf(commitRef.current.layers, parentId);\n const last = siblings.filter((l) => !l.alwaysOn).length - 1;\n drag.toIndex = Math.min(last, Math.max(0, drag.fromIndex + delta));\n setDragMark({ parentId, index: drag.toIndex });\n },\n onEnd: () => {\n const { layers: tree, onReorder: commit } = commitRef.current;\n const { fromIndex, toIndex } = drag;\n clearDrag();\n if (toIndex === fromIndex) return;\n const siblings = siblingsOf(tree, parentId);\n const rows = siblings.filter((l) => !l.alwaysOn);\n const locked = siblings.filter((l) => l.alwaysOn);\n const next = [...rows];\n const [moved] = next.splice(fromIndex, 1);\n if (moved) next.splice(toIndex, 0, moved);\n commit(withChildren(tree, parentId, [...next, ...locked]));\n },\n // An interrupted drag never named a destination, so it reorders nothing.\n onCancel: clearDrag,\n });\n };\n\n // A twisty column on every row keeps labels aligned across depths, but a\n // flat list would only be paying 20px for it.\n const nested = anyNested(layers);\n\n function renderRow(\n layer: LayerTreeNode,\n parentId: string | null,\n index: number,\n path: string,\n pinned: boolean,\n ): ReactNode {\n const kids = layer.children ?? [];\n const open = kids.length > 0 && !collapsed.has(layer.id);\n const bodyId = `${uid}${path}`;\n const dragging = !pinned && dragMark?.parentId === parentId && dragMark.index === index;\n const cls = ['lk-layer-list__row'];\n if (dragging) cls.push('lk-layer-list__row--dragging');\n if (pinned) cls.push('lk-layer-list__row--pinned');\n return (\n <Fragment key={layer.id}>\n <div className={cls.join(' ')}>\n {nested &&\n (kids.length > 0 ? (\n <Disclosure\n open={open}\n onToggle={() => toggleCollapsed(layer.id)}\n label={`${layer.label} sublayers`}\n controls={bodyId}\n />\n ) : (\n <span className=\"lk-layer-list__twisty-gap\" />\n ))}\n {pinned ? (\n <span className=\"lk-layer-list__lock\" role=\"img\" aria-label=\"Always on\">\n 🔒\n </span>\n ) : (\n <>\n <button\n type=\"button\"\n className=\"lk-layer-list__handle\"\n aria-label={`Reorder ${layer.label}`}\n onPointerDown={(e) => startDrag(e, parentId, index)}\n >\n <DragHandleGlyph size={13} />\n </button>\n <input\n className=\"lk-layer-list__check\"\n type=\"checkbox\"\n checked={visibility[layer.id] !== false}\n onChange={(e) => onToggle(layer.id, e.target.checked)}\n aria-label={`Toggle ${layer.label}`}\n />\n </>\n )}\n <span className=\"lk-layer-list__label\">{layer.label}</span>\n </div>\n {open && (\n <div id={bodyId} className=\"lk-layer-list__children\">\n {renderLevel(kids, layer.id, path)}\n </div>\n )}\n </Fragment>\n );\n }\n\n function renderLevel(nodes: LayerTreeNode[], parentId: string | null, path: string): ReactNode {\n const reorderable = nodes.filter((l) => !l.alwaysOn);\n const pinned = nodes.filter((l) => l.alwaysOn);\n return (\n <>\n {reorderable.map((layer, i) => renderRow(layer, parentId, i, `${path}-${i}`, false))}\n {pinned.map((layer, i) => renderRow(layer, parentId, i, `${path}-p${i}`, true))}\n </>\n );\n }\n\n return (\n <div className={className ? `lk-layer-list ${className}` : 'lk-layer-list'}>\n {renderLevel(layers, null, '')}\n </div>\n );\n}\n"]}
@@ -0,0 +1,50 @@
1
+ // src/config/path.ts
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4
+ }
5
+ function valueAtPath(config, path) {
6
+ let cur = config;
7
+ for (const segment of path.split(".")) {
8
+ if (!isRecord(cur)) return void 0;
9
+ cur = cur[segment];
10
+ }
11
+ return cur;
12
+ }
13
+ function hasConfigPath(config, path) {
14
+ let cur = config;
15
+ for (const segment of path.split(".")) {
16
+ if (!isRecord(cur) || !(segment in cur)) return false;
17
+ cur = cur[segment];
18
+ }
19
+ return true;
20
+ }
21
+ function writeIn(node, segments, value) {
22
+ const [head, ...rest] = segments;
23
+ if (head === void 0) return value;
24
+ const base = isRecord(node) ? node : {};
25
+ return { ...base, [head]: rest.length === 0 ? value : writeIn(base[head], rest, value) };
26
+ }
27
+ function withValueAtPath(config, path, value) {
28
+ return writeIn(config, path.split("."), value);
29
+ }
30
+ function fillConfigDefaults(stored, defaults) {
31
+ if (!isRecord(defaults)) return stored === void 0 ? defaults : stored;
32
+ const base = isRecord(stored) ? stored : {};
33
+ const out = { ...base };
34
+ for (const [key, value] of Object.entries(defaults)) {
35
+ out[key] = fillConfigDefaults(base[key], value);
36
+ }
37
+ return out;
38
+ }
39
+ function schemaNodeAtPath(group, path) {
40
+ let node = group;
41
+ for (const segment of path.split(".")) {
42
+ if (node === void 0 || !("children" in node)) return void 0;
43
+ node = node.children[segment];
44
+ }
45
+ return node;
46
+ }
47
+
48
+ export { fillConfigDefaults, hasConfigPath, schemaNodeAtPath, valueAtPath, withValueAtPath };
49
+ //# sourceMappingURL=chunk-E44SU6XS.js.map
50
+ //# sourceMappingURL=chunk-E44SU6XS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/path.ts"],"names":[],"mappings":";AAQA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAIO,SAAS,WAAA,CAAY,QAAiB,IAAA,EAAuB;AAClE,EAAA,IAAI,GAAA,GAAM,MAAA;AACV,EAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,EAAG;AACrC,IAAA,IAAI,CAAC,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,MAAA;AAC3B,IAAA,GAAA,GAAM,IAAI,OAAO,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,GAAA;AACT;AAIO,SAAS,aAAA,CAAc,QAAiB,IAAA,EAAuB;AACpE,EAAA,IAAI,GAAA,GAAM,MAAA;AACV,EAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,EAAG;AACrC,IAAA,IAAI,CAAC,QAAA,CAAS,GAAG,KAAK,EAAE,OAAA,IAAW,MAAM,OAAO,KAAA;AAChD,IAAA,GAAA,GAAM,IAAI,OAAO,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,OAAA,CAAQ,IAAA,EAAe,QAAA,EAA6B,KAAA,EAAyB;AACpF,EAAA,MAAM,CAAC,IAAA,EAAM,GAAG,IAAI,CAAA,GAAI,QAAA;AACxB,EAAA,IAAI,IAAA,KAAS,QAAW,OAAO,KAAA;AAC/B,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAI,CAAA,GAAI,OAAO,EAAC;AACtC,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,CAAC,IAAI,GAAG,IAAA,CAAK,MAAA,KAAW,CAAA,GAAI,KAAA,GAAQ,QAAQ,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,EAAM,KAAK,CAAA,EAAE;AACzF;AAKO,SAAS,eAAA,CAAmB,MAAA,EAAW,IAAA,EAAc,KAAA,EAAmB;AAC7E,EAAA,OAAO,QAAQ,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,GAAG,KAAK,CAAA;AAC/C;AAUO,SAAS,kBAAA,CAAsB,QAAiB,QAAA,EAAgB;AACrE,EAAA,IAAI,CAAC,QAAA,CAAS,QAAQ,GAAG,OAAQ,MAAA,KAAW,SAAY,QAAA,GAAW,MAAA;AACnE,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,MAAM,CAAA,GAAI,SAAS,EAAC;AAC1C,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,IAAA,EAAK;AAC/C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACnD,IAAA,GAAA,CAAI,GAAG,CAAA,GAAI,kBAAA,CAAmB,IAAA,CAAK,GAAG,GAAG,KAAK,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,GAAA;AACT;AAIO,SAAS,gBAAA,CAAiB,OAAkB,IAAA,EAAgD;AACjG,EAAA,IAAI,IAAA,GAAyC,KAAA;AAC7C,EAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,EAAG;AACrC,IAAA,IAAI,IAAA,KAAS,MAAA,IAAa,EAAE,UAAA,IAAc,OAAO,OAAO,MAAA;AACxD,IAAA,IAAA,GAAO,IAAA,CAAK,SAAS,OAAO,CAAA;AAAA,EAC9B;AACA,EAAA,OAAO,IAAA;AACT","file":"chunk-E44SU6XS.js","sourcesContent":["import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\n\n/**\n * Reading and writing a config tree by dotted path. A schema's leaves address\n * their values this way, so a leaf nested under `f.group` and a flat one are\n * reached by exactly the same call.\n */\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** The value at a dotted path. `undefined` when a segment is missing or the\n * walk hits something that is not a record. */\nexport function valueAtPath(config: unknown, path: string): unknown {\n let cur = config;\n for (const segment of path.split('.')) {\n if (!isRecord(cur)) return undefined;\n cur = cur[segment];\n }\n return cur;\n}\n\n/** Whether a dotted path is actually present, which a `undefined` value is\n * not enough to tell you. */\nexport function hasConfigPath(config: unknown, path: string): boolean {\n let cur = config;\n for (const segment of path.split('.')) {\n if (!isRecord(cur) || !(segment in cur)) return false;\n cur = cur[segment];\n }\n return true;\n}\n\nfunction writeIn(node: unknown, segments: readonly string[], value: unknown): unknown {\n const [head, ...rest] = segments;\n if (head === undefined) return value;\n const base = isRecord(node) ? node : {};\n return { ...base, [head]: rest.length === 0 ? value : writeIn(base[head], rest, value) };\n}\n\n/** A copy of `config` with `value` written at a dotted path. Every record on\n * the way down is copied and the input is left alone; a missing — or\n * non-record — intermediate is replaced with the branch the path implies. */\nexport function withValueAtPath<T>(config: T, path: string, value: unknown): T {\n return writeIn(config, path.split('.'), value) as T;\n}\n\n/**\n * `stored` with every gap filled from `defaults`, down the whole tree.\n *\n * This is how a config written before its schema grew a branch still loads:\n * the branch arrives at its defaults instead of `undefined`, and a key the\n * defaults no longer mention is kept rather than dropped, so nothing is lost\n * to a schema change the author has not finished making.\n */\nexport function fillConfigDefaults<T>(stored: unknown, defaults: T): T {\n if (!isRecord(defaults)) return (stored === undefined ? defaults : stored) as T;\n const base = isRecord(stored) ? stored : {};\n const out: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(defaults)) {\n out[key] = fillConfigDefaults(base[key], value);\n }\n return out as T;\n}\n\n/** A node in a resolved schema tree, by dotted path. Structural rather than\n * `isPrefLeaf` so this stays free of a runtime import. */\nexport function schemaNodeAtPath(group: PrefGroup, path: string): PrefLeaf | PrefGroup | undefined {\n let node: PrefLeaf | PrefGroup | undefined = group;\n for (const segment of path.split('.')) {\n if (node === undefined || !('children' in node)) return undefined;\n node = node.children[segment];\n }\n return node;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { TrialIdContext } from './chunk-2YILQ7Y5.js';
2
- import { useVisibleRaf } from './chunk-54ZWZ5FQ.js';
3
2
  import { createContext, useContext, useCallback, useRef, useEffect } from 'react';
3
+ import { useVisibleRaf } from '@weasel-js/core';
4
4
 
5
5
  // src/surface/composeRects.ts
6
6
  function composeRects(container, tiles) {
@@ -197,5 +197,5 @@ function useTiledSurface({ onFrame }) {
197
197
  }
198
198
 
199
199
  export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTileId, useTiledSurface };
200
- //# sourceMappingURL=chunk-WS6ZRV75.js.map
201
- //# sourceMappingURL=chunk-WS6ZRV75.js.map
200
+ //# sourceMappingURL=chunk-FQMJUVHJ.js.map
201
+ //# sourceMappingURL=chunk-FQMJUVHJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/surface/composeRects.ts","../src/surface/deviceRect.ts","../src/surface/SurfaceContext.ts","../src/surface/useSurfaceTile.ts","../src/surface/useTiledSurface.ts"],"names":["useCallback"],"mappings":"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","file":"chunk-WS6ZRV75.js","sourcesContent":["import type { Box, Rect } from './rect';\n\n/** Every tile's box expressed against the container's origin. */\nexport function composeRects(container: Box, tiles: ReadonlyMap<string, Box>): Map<string, Rect> {\n const out = new Map<string, Rect>();\n for (const [id, tile] of tiles) {\n out.set(id, {\n x: tile.left - container.left,\n y: tile.top - container.top,\n w: tile.width,\n h: tile.height,\n });\n }\n return out;\n}\n\nexport function rectsEqual(a: Rect | undefined, b: Rect): boolean {\n return a !== undefined && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;\n}\n","import type { Rect } from './rect';\n\n/**\n * A DOM rect as a GL viewport rect: origin at the bottom-left, every edge snapped\n * to the device-pixel grid. Still CSS pixels, because three.js applies its own\n * pixel ratio — snapping here is what stops a tile and its neighbour rounding\n * apart and leaving a hairline column between them.\n */\nexport function toDeviceRect(rect: Rect, surfaceHeight: number, dpr: number): Rect {\n const snap = (v: number) => Math.round(v * dpr) / dpr;\n const x = snap(rect.x);\n const y = snap(surfaceHeight - rect.y - rect.h);\n return {\n x,\n y,\n w: snap(rect.x + rect.w) - x,\n h: snap(surfaceHeight - rect.y) - y,\n };\n}\n","import { createContext } from 'react';\nimport type { SurfaceHandle } from './useTiledSurface';\n\n/** Null when no surface owner is above — a lab with no shared surface at all. */\nexport const SurfaceContext = createContext<SurfaceHandle | null>(null);\n\n/** The buffer the surface's tiles paint into. Null until the owner's canvas is\n * mounted, and for an owner that keeps its surface unpainted. */\nexport const SurfaceCanvasContext = createContext<HTMLCanvasElement | null>(null);\n","import { useCallback, useContext } from 'react';\nimport { TrialIdContext } from '../state/context';\nimport { SurfaceCanvasContext, SurfaceContext } from './SurfaceContext';\nimport type { SurfaceHandle } from './useTiledSurface';\n\n/** The surface's shared buffer, or null where nothing paints one. What a tile\n * hands to `paintInto`. */\nexport function useSurfaceCanvas(): HTMLCanvasElement | null {\n return useContext(SurfaceCanvasContext);\n}\n\n/** The surface above, or null. Use this where a surface is genuinely optional. */\nexport function useSurfaceOptional(): SurfaceHandle | null {\n return useContext(SurfaceContext);\n}\n\n/** The surface above. Throws where a caller cannot work without one. */\nexport function useSurface(): SurfaceHandle {\n const surface = useContext(SurfaceContext);\n if (!surface) throw new Error('[labkit] useSurface requires a surface owner above it');\n return surface;\n}\n\n/**\n * The key `id` is registered under, and so the key it comes back on in a\n * frame's `rects`: `<trial>/<id>` inside a trial, and `id` alone outside one.\n *\n * A surface's tile namespace is flat and shared by the whole lab, but an\n * instrument names its regions once and every trial of it declares those same\n * names. Unscoped, the second trial to mount takes the first one's rect and its\n * painter, and the first is never told it moved again.\n */\nexport function useTileId(id: string): string {\n const trial = useContext(TrialIdContext);\n return trial ? `${trial}/${id}` : id;\n}\n\n/**\n * A ref callback that publishes this element's rect to the surface under `id`.\n *\n * Attach it to whichever element the surface should draw into — that is not\n * necessarily the trial's own element, since a trial may hold a drawn pane beside\n * an undrawn one, or none at all.\n *\n * The frame reports it under `useTileId(id)`, not under `id`.\n */\nexport function useSurfaceTile(id: string): (el: HTMLElement | null) => void {\n const surface = useSurfaceOptional();\n const tileId = useTileId(id);\n return useCallback(\n (el: HTMLElement | null) => {\n surface?.registerTile(tileId, el);\n },\n [surface, tileId],\n );\n}\n","import { useVisibleRaf } from '@weasel-js/core';\nimport { useCallback, useEffect, useRef } from 'react';\nimport { composeRects, rectsEqual } from './composeRects';\nimport type { Box, Rect } from './rect';\n\n/** What a surface owner is handed once per animation frame. `rects` carries every\n * tile, not only the dirty ones: a scissored draw has to know where it is drawing\n * relative to a surface that may have resized under it.\n *\n * Its keys are what `useTileId` returns — `<trial>/<id>` for a tile registered\n * inside a trial — not the bare id the tile was named with. */\nexport interface SurfaceFrame {\n dirty: ReadonlySet<string>;\n rects: ReadonlyMap<string, Rect>;\n dpr: number;\n size: { width: number; height: number };\n}\n\n/** The invalidators and the two ref callbacks that publish geometry. */\nexport interface SurfaceHandle {\n /** Mark one tile for redraw. */\n invalidate: (id: string) => void;\n /** Mark every tile — what a resize or a tile-set change means. */\n invalidateAll: () => void;\n /** Re-measure before the next frame. The escape hatch for a host that knows it\n * moved something a ResizeObserver cannot see. */\n invalidateRects: () => void;\n registerTile: (id: string, el: HTMLElement | null) => void;\n /** Subscribe a tile to the frames it is dirty on. A tile paints on its own\n * loop, so this is how the surface wakes one: a resize of the shared buffer\n * clears every tile, not only the one that moved. */\n registerPainter: (id: string, paint: TilePainter) => () => void;\n containerRef: (el: HTMLElement | null) => void;\n /** The element tile rects are measured against, and so the one a tile's own\n * chrome positions itself inside. Null before the owner attaches it. */\n getContainer: () => HTMLElement | null;\n}\n\n/** What a tile's painter is handed: where it sits on the surface now, and the\n * frame that dirtied it. */\nexport type TilePainter = (rect: Rect, frame: SurfaceFrame) => void;\n\nexport interface UseTiledSurfaceOptions {\n onFrame: (frame: SurfaceFrame) => void;\n}\n\nexport function useTiledSurface({ onFrame }: UseTiledSurfaceOptions): SurfaceHandle {\n const container = useRef<HTMLElement | null>(null);\n const tiles = useRef(new Map<string, HTMLElement>());\n const rects = useRef(new Map<string, Rect>());\n const dirty = useRef(new Set<string>());\n const painters = useRef(new Map<string, TilePainter>());\n const needsMeasure = useRef(true);\n const observer = useRef<ResizeObserver | null>(null);\n const lastDpr = useRef(0);\n\n // Held in a ref so a caller passing an inline closure does not re-create every\n // callback below on each render.\n const onFrameRef = useRef(onFrame);\n onFrameRef.current = onFrame;\n\n const measure = useCallback((): boolean => {\n const el = container.current;\n if (!el) return false;\n const boxes = new Map<string, Box>();\n for (const [id, tile] of tiles.current) boxes.set(id, tile.getBoundingClientRect());\n const next = composeRects(el.getBoundingClientRect(), boxes);\n let changed = next.size !== rects.current.size;\n for (const [id, rect] of next) {\n if (!rectsEqual(rects.current.get(id), rect)) changed = true;\n }\n rects.current = next;\n return changed;\n }, []);\n\n const frameLoop = useVisibleRaf(\n () => {\n const el = container.current;\n if (!el) {\n dirty.current.clear();\n return;\n }\n if (needsMeasure.current) {\n needsMeasure.current = false;\n if (measure()) for (const id of rects.current.keys()) dirty.current.add(id);\n }\n const dpr = globalThis.devicePixelRatio ?? 1;\n if (dpr !== lastDpr.current) {\n lastDpr.current = dpr;\n for (const id of rects.current.keys()) dirty.current.add(id);\n }\n if (dirty.current.size === 0) return;\n const box = el.getBoundingClientRect();\n const frame: SurfaceFrame = {\n dirty: new Set(dirty.current),\n rects: new Map(rects.current),\n dpr,\n size: { width: box.width, height: box.height },\n };\n onFrameRef.current(frame);\n // After the owner, which is what resized the buffer this frame.\n for (const id of frame.dirty) {\n const rect = frame.rects.get(id);\n if (rect) painters.current.get(id)?.(rect, frame);\n }\n dirty.current.clear();\n },\n // The host attaches its container through `containerRef`, which may land\n // well after this hook's first effect; the gate re-resolves it per request.\n { target: () => container.current },\n );\n\n const schedule = useCallback(() => {\n frameLoop.request();\n }, [frameLoop]);\n\n const invalidate = useCallback(\n (id: string) => {\n dirty.current.add(id);\n schedule();\n },\n [schedule],\n );\n\n const invalidateAll = useCallback(() => {\n for (const id of tiles.current.keys()) dirty.current.add(id);\n schedule();\n }, [schedule]);\n\n const invalidateRects = useCallback(() => {\n needsMeasure.current = true;\n schedule();\n }, [schedule]);\n\n const registerTile = useCallback(\n (id: string, el: HTMLElement | null) => {\n const known = tiles.current.get(id);\n if (known === el) return;\n if (known) observer.current?.unobserve(known);\n if (el) {\n tiles.current.set(id, el);\n observer.current?.observe(el);\n } else {\n tiles.current.delete(id);\n rects.current.delete(id);\n dirty.current.delete(id);\n }\n needsMeasure.current = true;\n schedule();\n },\n [schedule],\n );\n\n const registerPainter = useCallback((id: string, paint: TilePainter) => {\n painters.current.set(id, paint);\n return () => {\n if (painters.current.get(id) === paint) painters.current.delete(id);\n };\n }, []);\n\n const getContainer = useCallback(() => container.current, []);\n\n const containerRef = useCallback(\n (el: HTMLElement | null) => {\n if (container.current === el) return;\n if (container.current) observer.current?.unobserve(container.current);\n container.current = el;\n if (el) observer.current?.observe(el);\n needsMeasure.current = true;\n schedule();\n },\n [schedule],\n );\n\n useEffect(() => {\n const ro = new ResizeObserver(() => {\n needsMeasure.current = true;\n schedule();\n });\n observer.current = ro;\n if (container.current) ro.observe(container.current);\n for (const el of tiles.current.values()) ro.observe(el);\n return () => {\n ro.disconnect();\n observer.current = null;\n frameLoop.cancel();\n };\n }, [schedule, frameLoop]);\n\n return {\n invalidate,\n invalidateAll,\n invalidateRects,\n registerTile,\n registerPainter,\n containerRef,\n getContainer,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/surface/composeRects.ts","../src/surface/deviceRect.ts","../src/surface/SurfaceContext.ts","../src/surface/useSurfaceTile.ts","../src/surface/useTiledSurface.ts"],"names":["useCallback"],"mappings":";;;;;AAGO,SAAS,YAAA,CAAa,WAAgB,KAAA,EAAoD;AAC/F,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAkB;AAClC,EAAA,KAAA,MAAW,CAAC,EAAA,EAAI,IAAI,CAAA,IAAK,KAAA,EAAO;AAC9B,IAAA,GAAA,CAAI,IAAI,EAAA,EAAI;AAAA,MACV,CAAA,EAAG,IAAA,CAAK,IAAA,GAAO,SAAA,CAAU,IAAA;AAAA,MACzB,CAAA,EAAG,IAAA,CAAK,GAAA,GAAM,SAAA,CAAU,GAAA;AAAA,MACxB,GAAG,IAAA,CAAK,KAAA;AAAA,MACR,GAAG,IAAA,CAAK;AAAA,KACT,CAAA;AAAA,EACH;AACA,EAAA,OAAO,GAAA;AACT;AAEO,SAAS,UAAA,CAAW,GAAqB,CAAA,EAAkB;AAChE,EAAA,OAAO,MAAM,MAAA,IAAa,CAAA,CAAE,CAAA,KAAM,CAAA,CAAE,KAAK,CAAA,CAAE,CAAA,KAAM,CAAA,CAAE,CAAA,IAAK,EAAE,CAAA,KAAM,CAAA,CAAE,CAAA,IAAK,CAAA,CAAE,MAAM,CAAA,CAAE,CAAA;AACnF;;;ACVO,SAAS,YAAA,CAAa,IAAA,EAAY,aAAA,EAAuB,GAAA,EAAmB;AACjF,EAAA,MAAM,OAAO,CAAC,CAAA,KAAc,KAAK,KAAA,CAAM,CAAA,GAAI,GAAG,CAAA,GAAI,GAAA;AAClD,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AACrB,EAAA,MAAM,IAAI,IAAA,CAAK,aAAA,GAAgB,IAAA,CAAK,CAAA,GAAI,KAAK,CAAC,CAAA;AAC9C,EAAA,OAAO;AAAA,IACL,CAAA;AAAA,IACA,CAAA;AAAA,IACA,GAAG,IAAA,CAAK,IAAA,CAAK,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA,GAAI,CAAA;AAAA,IAC3B,CAAA,EAAG,IAAA,CAAK,aAAA,GAAgB,IAAA,CAAK,CAAC,CAAA,GAAI;AAAA,GACpC;AACF;ACdO,IAAM,cAAA,GAAiB,cAAoC,IAAI;AAI/D,IAAM,oBAAA,GAAuB,cAAwC,IAAI;ACDzE,SAAS,gBAAA,GAA6C;AAC3D,EAAA,OAAO,WAAW,oBAAoB,CAAA;AACxC;AAGO,SAAS,kBAAA,GAA2C;AACzD,EAAA,OAAO,WAAW,cAAc,CAAA;AAClC;AAGO,SAAS,UAAA,GAA4B;AAC1C,EAAA,MAAM,OAAA,GAAU,WAAW,cAAc,CAAA;AACzC,EAAA,IAAI,CAAC,OAAA,EAAS,MAAM,IAAI,MAAM,uDAAuD,CAAA;AACrF,EAAA,OAAO,OAAA;AACT;AAWO,SAAS,UAAU,EAAA,EAAoB;AAC5C,EAAA,MAAM,KAAA,GAAQ,WAAW,cAAc,CAAA;AACvC,EAAA,OAAO,KAAA,GAAQ,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAA;AACpC;AAWO,SAAS,eAAe,EAAA,EAA8C;AAC3E,EAAA,MAAM,UAAU,kBAAA,EAAmB;AACnC,EAAA,MAAM,MAAA,GAAS,UAAU,EAAE,CAAA;AAC3B,EAAA,OAAO,WAAA;AAAA,IACL,CAAC,EAAA,KAA2B;AAC1B,MAAA,OAAA,EAAS,YAAA,CAAa,QAAQ,EAAE,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,CAAC,SAAS,MAAM;AAAA,GAClB;AACF;ACTO,SAAS,eAAA,CAAgB,EAAE,OAAA,EAAQ,EAA0C;AAClF,EAAA,MAAM,SAAA,GAAY,OAA2B,IAAI,CAAA;AACjD,EAAA,MAAM,KAAA,GAAQ,MAAA,iBAAO,IAAI,GAAA,EAA0B,CAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,MAAA,iBAAO,IAAI,GAAA,EAAmB,CAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,MAAA,iBAAO,IAAI,GAAA,EAAa,CAAA;AACtC,EAAA,MAAM,QAAA,GAAW,MAAA,iBAAO,IAAI,GAAA,EAA0B,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,OAAO,IAAI,CAAA;AAChC,EAAA,MAAM,QAAA,GAAW,OAA8B,IAAI,CAAA;AACnD,EAAA,MAAM,OAAA,GAAU,OAAO,CAAC,CAAA;AAIxB,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,EAAA,MAAM,OAAA,GAAUA,YAAY,MAAe;AACzC,IAAA,MAAM,KAAK,SAAA,CAAU,OAAA;AACrB,IAAA,IAAI,CAAC,IAAI,OAAO,KAAA;AAChB,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAiB;AACnC,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,IAAI,CAAA,IAAK,KAAA,CAAM,OAAA,EAAS,KAAA,CAAM,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,qBAAA,EAAuB,CAAA;AAClF,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,EAAA,CAAG,qBAAA,IAAyB,KAAK,CAAA;AAC3D,IAAA,IAAI,OAAA,GAAU,IAAA,CAAK,IAAA,KAAS,KAAA,CAAM,OAAA,CAAQ,IAAA;AAC1C,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,IAAI,CAAA,IAAK,IAAA,EAAM;AAC7B,MAAA,IAAI,CAAC,WAAW,KAAA,CAAM,OAAA,CAAQ,IAAI,EAAE,CAAA,EAAG,IAAI,CAAA,EAAG,OAAA,GAAU,IAAA;AAAA,IAC1D;AACA,IAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,IAAA,OAAO,OAAA;AAAA,EACT,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,SAAA,GAAY,aAAA;AAAA,IAChB,MAAM;AACJ,MAAA,MAAM,KAAK,SAAA,CAAU,OAAA;AACrB,MAAA,IAAI,CAAC,EAAA,EAAI;AACP,QAAA,KAAA,CAAM,QAAQ,KAAA,EAAM;AACpB,QAAA;AAAA,MACF;AACA,MAAA,IAAI,aAAa,OAAA,EAAS;AACxB,QAAA,YAAA,CAAa,OAAA,GAAU,KAAA;AACvB,QAAA,IAAI,OAAA,EAAQ,EAAG,KAAA,MAAW,EAAA,IAAM,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAK,EAAG,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAAA,MAC5E;AACA,MAAA,MAAM,GAAA,GAAM,WAAW,gBAAA,IAAoB,CAAA;AAC3C,MAAA,IAAI,GAAA,KAAQ,QAAQ,OAAA,EAAS;AAC3B,QAAA,OAAA,CAAQ,OAAA,GAAU,GAAA;AAClB,QAAA,KAAA,MAAW,EAAA,IAAM,MAAM,OAAA,CAAQ,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,EAAE,CAAA;AAAA,MAC7D;AACA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,CAAA,EAAG;AAC9B,MAAA,MAAM,GAAA,GAAM,GAAG,qBAAA,EAAsB;AACrC,MAAA,MAAM,KAAA,GAAsB;AAAA,QAC1B,KAAA,EAAO,IAAI,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA;AAAA,QAC5B,KAAA,EAAO,IAAI,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA;AAAA,QAC5B,GAAA;AAAA,QACA,MAAM,EAAE,KAAA,EAAO,IAAI,KAAA,EAAO,MAAA,EAAQ,IAAI,MAAA;AAAO,OAC/C;AACA,MAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAExB,MAAA,KAAA,MAAW,EAAA,IAAM,MAAM,KAAA,EAAO;AAC5B,QAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA;AAC/B,QAAA,IAAI,MAAM,QAAA,CAAS,OAAA,CAAQ,IAAI,EAAE,CAAA,GAAI,MAAM,KAAK,CAAA;AAAA,MAClD;AACA,MAAA,KAAA,CAAM,QAAQ,KAAA,EAAM;AAAA,IACtB,CAAA;AAAA;AAAA;AAAA,IAGA,EAAE,MAAA,EAAQ,MAAM,SAAA,CAAU,OAAA;AAAQ,GACpC;AAEA,EAAA,MAAM,QAAA,GAAWA,YAAY,MAAM;AACjC,IAAA,SAAA,CAAU,OAAA,EAAQ;AAAA,EACpB,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,UAAA,GAAaA,WAAAA;AAAA,IACjB,CAAC,EAAA,KAAe;AACd,MAAA,KAAA,CAAM,OAAA,CAAQ,IAAI,EAAE,CAAA;AACpB,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAA,MAAM,aAAA,GAAgBA,YAAY,MAAM;AACtC,IAAA,KAAA,MAAW,EAAA,IAAM,MAAM,OAAA,CAAQ,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,EAAE,CAAA;AAC3D,IAAA,QAAA,EAAS;AAAA,EACX,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,eAAA,GAAkBA,YAAY,MAAM;AACxC,IAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,IAAA,QAAA,EAAS;AAAA,EACX,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,YAAA,GAAeA,WAAAA;AAAA,IACnB,CAAC,IAAY,EAAA,KAA2B;AACtC,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAClC,MAAA,IAAI,UAAU,EAAA,EAAI;AAClB,MAAA,IAAI,KAAA,EAAO,QAAA,CAAS,OAAA,EAAS,SAAA,CAAU,KAAK,CAAA;AAC5C,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,EAAE,CAAA;AACxB,QAAA,QAAA,CAAS,OAAA,EAAS,QAAQ,EAAE,CAAA;AAAA,MAC9B,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAO,EAAE,CAAA;AACvB,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAO,EAAE,CAAA;AACvB,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAO,EAAE,CAAA;AAAA,MACzB;AACA,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAA,MAAM,eAAA,GAAkBA,WAAAA,CAAY,CAAC,EAAA,EAAY,KAAA,KAAuB;AACtE,IAAA,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,KAAK,CAAA;AAC9B,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,QAAA,CAAS,QAAQ,GAAA,CAAI,EAAE,MAAM,KAAA,EAAO,QAAA,CAAS,OAAA,CAAQ,MAAA,CAAO,EAAE,CAAA;AAAA,IACpE,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,eAAeA,WAAAA,CAAY,MAAM,SAAA,CAAU,OAAA,EAAS,EAAE,CAAA;AAE5D,EAAA,MAAM,YAAA,GAAeA,WAAAA;AAAA,IACnB,CAAC,EAAA,KAA2B;AAC1B,MAAA,IAAI,SAAA,CAAU,YAAY,EAAA,EAAI;AAC9B,MAAA,IAAI,UAAU,OAAA,EAAS,QAAA,CAAS,OAAA,EAAS,SAAA,CAAU,UAAU,OAAO,CAAA;AACpE,MAAA,SAAA,CAAU,OAAA,GAAU,EAAA;AACpB,MAAA,IAAI,EAAA,EAAI,QAAA,CAAS,OAAA,EAAS,OAAA,CAAQ,EAAE,CAAA;AACpC,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,IAAI,cAAA,CAAe,MAAM;AAClC,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,QAAA,EAAS;AAAA,IACX,CAAC,CAAA;AACD,IAAA,QAAA,CAAS,OAAA,GAAU,EAAA;AACnB,IAAA,IAAI,SAAA,CAAU,OAAA,EAAS,EAAA,CAAG,OAAA,CAAQ,UAAU,OAAO,CAAA;AACnD,IAAA,KAAA,MAAW,MAAM,KAAA,CAAM,OAAA,CAAQ,QAAO,EAAG,EAAA,CAAG,QAAQ,EAAE,CAAA;AACtD,IAAA,OAAO,MAAM;AACX,MAAA,EAAA,CAAG,UAAA,EAAW;AACd,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,MAAA,SAAA,CAAU,MAAA,EAAO;AAAA,IACnB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAA,EAAU,SAAS,CAAC,CAAA;AAExB,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,aAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF","file":"chunk-FQMJUVHJ.js","sourcesContent":["import type { Box, Rect } from './rect';\n\n/** Every tile's box expressed against the container's origin. */\nexport function composeRects(container: Box, tiles: ReadonlyMap<string, Box>): Map<string, Rect> {\n const out = new Map<string, Rect>();\n for (const [id, tile] of tiles) {\n out.set(id, {\n x: tile.left - container.left,\n y: tile.top - container.top,\n w: tile.width,\n h: tile.height,\n });\n }\n return out;\n}\n\nexport function rectsEqual(a: Rect | undefined, b: Rect): boolean {\n return a !== undefined && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;\n}\n","import type { Rect } from './rect';\n\n/**\n * A DOM rect as a GL viewport rect: origin at the bottom-left, every edge snapped\n * to the device-pixel grid. Still CSS pixels, because three.js applies its own\n * pixel ratio — snapping here is what stops a tile and its neighbour rounding\n * apart and leaving a hairline column between them.\n */\nexport function toDeviceRect(rect: Rect, surfaceHeight: number, dpr: number): Rect {\n const snap = (v: number) => Math.round(v * dpr) / dpr;\n const x = snap(rect.x);\n const y = snap(surfaceHeight - rect.y - rect.h);\n return {\n x,\n y,\n w: snap(rect.x + rect.w) - x,\n h: snap(surfaceHeight - rect.y) - y,\n };\n}\n","import { createContext } from 'react';\nimport type { SurfaceHandle } from './useTiledSurface';\n\n/** Null when no surface owner is above — a lab with no shared surface at all. */\nexport const SurfaceContext = createContext<SurfaceHandle | null>(null);\n\n/** The buffer the surface's tiles paint into. Null until the owner's canvas is\n * mounted, and for an owner that keeps its surface unpainted. */\nexport const SurfaceCanvasContext = createContext<HTMLCanvasElement | null>(null);\n","import { useCallback, useContext } from 'react';\nimport { TrialIdContext } from '../state/context';\nimport { SurfaceCanvasContext, SurfaceContext } from './SurfaceContext';\nimport type { SurfaceHandle } from './useTiledSurface';\n\n/** The surface's shared buffer, or null where nothing paints one. What a tile\n * hands to `paintInto`. */\nexport function useSurfaceCanvas(): HTMLCanvasElement | null {\n return useContext(SurfaceCanvasContext);\n}\n\n/** The surface above, or null. Use this where a surface is genuinely optional. */\nexport function useSurfaceOptional(): SurfaceHandle | null {\n return useContext(SurfaceContext);\n}\n\n/** The surface above. Throws where a caller cannot work without one. */\nexport function useSurface(): SurfaceHandle {\n const surface = useContext(SurfaceContext);\n if (!surface) throw new Error('[labkit] useSurface requires a surface owner above it');\n return surface;\n}\n\n/**\n * The key `id` is registered under, and so the key it comes back on in a\n * frame's `rects`: `<trial>/<id>` inside a trial, and `id` alone outside one.\n *\n * A surface's tile namespace is flat and shared by the whole lab, but an\n * instrument names its regions once and every trial of it declares those same\n * names. Unscoped, the second trial to mount takes the first one's rect and its\n * painter, and the first is never told it moved again.\n */\nexport function useTileId(id: string): string {\n const trial = useContext(TrialIdContext);\n return trial ? `${trial}/${id}` : id;\n}\n\n/**\n * A ref callback that publishes this element's rect to the surface under `id`.\n *\n * Attach it to whichever element the surface should draw into — that is not\n * necessarily the trial's own element, since a trial may hold a drawn pane beside\n * an undrawn one, or none at all.\n *\n * The frame reports it under `useTileId(id)`, not under `id`.\n */\nexport function useSurfaceTile(id: string): (el: HTMLElement | null) => void {\n const surface = useSurfaceOptional();\n const tileId = useTileId(id);\n return useCallback(\n (el: HTMLElement | null) => {\n surface?.registerTile(tileId, el);\n },\n [surface, tileId],\n );\n}\n","import { useVisibleRaf } from '@weasel-js/core';\nimport { useCallback, useEffect, useRef } from 'react';\nimport { composeRects, rectsEqual } from './composeRects';\nimport type { Box, Rect } from './rect';\n\n/** What a surface owner is handed once per animation frame. `rects` carries every\n * tile, not only the dirty ones: a scissored draw has to know where it is drawing\n * relative to a surface that may have resized under it.\n *\n * Its keys are what `useTileId` returns — `<trial>/<id>` for a tile registered\n * inside a trial — not the bare id the tile was named with. */\nexport interface SurfaceFrame {\n dirty: ReadonlySet<string>;\n rects: ReadonlyMap<string, Rect>;\n dpr: number;\n size: { width: number; height: number };\n}\n\n/** The invalidators and the two ref callbacks that publish geometry. */\nexport interface SurfaceHandle {\n /** Mark one tile for redraw. */\n invalidate: (id: string) => void;\n /** Mark every tile — what a resize or a tile-set change means. */\n invalidateAll: () => void;\n /** Re-measure before the next frame. The escape hatch for a host that knows it\n * moved something a ResizeObserver cannot see. */\n invalidateRects: () => void;\n registerTile: (id: string, el: HTMLElement | null) => void;\n /** Subscribe a tile to the frames it is dirty on. A tile paints on its own\n * loop, so this is how the surface wakes one: a resize of the shared buffer\n * clears every tile, not only the one that moved. */\n registerPainter: (id: string, paint: TilePainter) => () => void;\n containerRef: (el: HTMLElement | null) => void;\n /** The element tile rects are measured against, and so the one a tile's own\n * chrome positions itself inside. Null before the owner attaches it. */\n getContainer: () => HTMLElement | null;\n}\n\n/** What a tile's painter is handed: where it sits on the surface now, and the\n * frame that dirtied it. */\nexport type TilePainter = (rect: Rect, frame: SurfaceFrame) => void;\n\nexport interface UseTiledSurfaceOptions {\n onFrame: (frame: SurfaceFrame) => void;\n}\n\nexport function useTiledSurface({ onFrame }: UseTiledSurfaceOptions): SurfaceHandle {\n const container = useRef<HTMLElement | null>(null);\n const tiles = useRef(new Map<string, HTMLElement>());\n const rects = useRef(new Map<string, Rect>());\n const dirty = useRef(new Set<string>());\n const painters = useRef(new Map<string, TilePainter>());\n const needsMeasure = useRef(true);\n const observer = useRef<ResizeObserver | null>(null);\n const lastDpr = useRef(0);\n\n // Held in a ref so a caller passing an inline closure does not re-create every\n // callback below on each render.\n const onFrameRef = useRef(onFrame);\n onFrameRef.current = onFrame;\n\n const measure = useCallback((): boolean => {\n const el = container.current;\n if (!el) return false;\n const boxes = new Map<string, Box>();\n for (const [id, tile] of tiles.current) boxes.set(id, tile.getBoundingClientRect());\n const next = composeRects(el.getBoundingClientRect(), boxes);\n let changed = next.size !== rects.current.size;\n for (const [id, rect] of next) {\n if (!rectsEqual(rects.current.get(id), rect)) changed = true;\n }\n rects.current = next;\n return changed;\n }, []);\n\n const frameLoop = useVisibleRaf(\n () => {\n const el = container.current;\n if (!el) {\n dirty.current.clear();\n return;\n }\n if (needsMeasure.current) {\n needsMeasure.current = false;\n if (measure()) for (const id of rects.current.keys()) dirty.current.add(id);\n }\n const dpr = globalThis.devicePixelRatio ?? 1;\n if (dpr !== lastDpr.current) {\n lastDpr.current = dpr;\n for (const id of rects.current.keys()) dirty.current.add(id);\n }\n if (dirty.current.size === 0) return;\n const box = el.getBoundingClientRect();\n const frame: SurfaceFrame = {\n dirty: new Set(dirty.current),\n rects: new Map(rects.current),\n dpr,\n size: { width: box.width, height: box.height },\n };\n onFrameRef.current(frame);\n // After the owner, which is what resized the buffer this frame.\n for (const id of frame.dirty) {\n const rect = frame.rects.get(id);\n if (rect) painters.current.get(id)?.(rect, frame);\n }\n dirty.current.clear();\n },\n // The host attaches its container through `containerRef`, which may land\n // well after this hook's first effect; the gate re-resolves it per request.\n { target: () => container.current },\n );\n\n const schedule = useCallback(() => {\n frameLoop.request();\n }, [frameLoop]);\n\n const invalidate = useCallback(\n (id: string) => {\n dirty.current.add(id);\n schedule();\n },\n [schedule],\n );\n\n const invalidateAll = useCallback(() => {\n for (const id of tiles.current.keys()) dirty.current.add(id);\n schedule();\n }, [schedule]);\n\n const invalidateRects = useCallback(() => {\n needsMeasure.current = true;\n schedule();\n }, [schedule]);\n\n const registerTile = useCallback(\n (id: string, el: HTMLElement | null) => {\n const known = tiles.current.get(id);\n if (known === el) return;\n if (known) observer.current?.unobserve(known);\n if (el) {\n tiles.current.set(id, el);\n observer.current?.observe(el);\n } else {\n tiles.current.delete(id);\n rects.current.delete(id);\n dirty.current.delete(id);\n }\n needsMeasure.current = true;\n schedule();\n },\n [schedule],\n );\n\n const registerPainter = useCallback((id: string, paint: TilePainter) => {\n painters.current.set(id, paint);\n return () => {\n if (painters.current.get(id) === paint) painters.current.delete(id);\n };\n }, []);\n\n const getContainer = useCallback(() => container.current, []);\n\n const containerRef = useCallback(\n (el: HTMLElement | null) => {\n if (container.current === el) return;\n if (container.current) observer.current?.unobserve(container.current);\n container.current = el;\n if (el) observer.current?.observe(el);\n needsMeasure.current = true;\n schedule();\n },\n [schedule],\n );\n\n useEffect(() => {\n const ro = new ResizeObserver(() => {\n needsMeasure.current = true;\n schedule();\n });\n observer.current = ro;\n if (container.current) ro.observe(container.current);\n for (const el of tiles.current.values()) ro.observe(el);\n return () => {\n ro.disconnect();\n observer.current = null;\n frameLoop.cancel();\n };\n }, [schedule, frameLoop]);\n\n return {\n invalidate,\n invalidateAll,\n invalidateRects,\n registerTile,\n registerPainter,\n containerRef,\n getContainer,\n };\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  import { zoomAt } from './chunk-CTRKTLYZ.js';
2
2
  import { DEFAULT_FRAME, resolveFrame, screenToWorld } from './chunk-2AYGEN57.js';
3
3
  import { CanvasStackContext } from './chunk-CQLQPQ4P.js';
4
- import { openPointerSession, useVisibleRaf } from './chunk-54ZWZ5FQ.js';
5
4
  import { useRef, useCallback, useEffect, useState, useMemo } from 'react';
5
+ import { openPointerSession, useVisibleRaf } from '@weasel-js/core';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
7
7
 
8
8
  function useLayerScheduler({
@@ -321,5 +321,5 @@ function useOrbit({
321
321
  }
322
322
 
323
323
  export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw };
324
- //# sourceMappingURL=chunk-FJG4PHTL.js.map
325
- //# sourceMappingURL=chunk-FJG4PHTL.js.map
324
+ //# sourceMappingURL=chunk-I6JCVE24.js.map
325
+ //# sourceMappingURL=chunk-I6JCVE24.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/canvas/useLayerScheduler.ts","../src/canvas/usePanZoom.ts","../src/canvas/CanvasStack.tsx","../src/canvas/useOrbit.ts"],"names":["useRef","useEffect","DRAG_THRESHOLD","useCallback","openPointerSession"],"mappings":"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","file":"chunk-I6JCVE24.js","sourcesContent":["import { useVisibleRaf } from '@weasel-js/core';\nimport { type RefObject, useEffect, useRef } from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { DEFAULT_FRAME, type WorldFrame } from './worldSpec';\n\n/** One layer of a canvas stack: its id, whether it is currently shown, and how\n * it paints itself. */\nexport interface CanvasLayerDescriptor {\n id: string;\n visible: boolean;\n render: (ctx: CanvasRenderingContext2D, view: ViewTransform, frame: WorldFrame) => void;\n}\n\ninterface SchedulerOptions {\n layers: CanvasLayerDescriptor[];\n view: ViewTransform;\n /** The instrument's coordinate system, resolved against `size`. */\n frame?: WorldFrame;\n canvasRefs: RefObject<Map<string, HTMLCanvasElement>>;\n size: { width: number; height: number; dpr: number };\n /** The element the stack occupies. Given one, the scheduler also stops while\n * the stack sits outside the viewport. */\n host?: RefObject<Element | null>;\n}\n\nexport function useLayerScheduler({\n layers,\n view,\n frame = DEFAULT_FRAME,\n canvasRefs,\n size,\n host,\n}: SchedulerOptions): void {\n const dirty = useRef<Set<string>>(new Set());\n const lastRenderRef = useRef<Map<string, CanvasLayerDescriptor['render']>>(new Map());\n\n useEffect(() => {\n for (const layer of layers) {\n const prev = lastRenderRef.current.get(layer.id);\n if (prev !== layer.render) dirty.current.add(layer.id);\n lastRenderRef.current.set(layer.id, layer.render);\n }\n for (const id of [...lastRenderRef.current.keys()]) {\n if (!layers.find((l) => l.id === id)) lastRenderRef.current.delete(id);\n }\n }, [layers]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-mark dirty when view or size changes\n useEffect(() => {\n for (const layer of layers) dirty.current.add(layer.id);\n }, [view, frame, size, layers]);\n\n // Painting only what is dirty is not the same as doing nothing: a hidden tab\n // still commits React updates, and the effect above marks every layer dirty\n // on any view or size change. The gate is what actually stops the work.\n const frameLoop = useVisibleRaf(\n () => {\n if (dirty.current.size === 0) return;\n const map = canvasRefs.current;\n if (!map) {\n dirty.current.clear();\n return;\n }\n for (const layer of layers) {\n if (!dirty.current.has(layer.id)) continue;\n if (!layer.visible) continue;\n const canvas = map.get(layer.id);\n if (!canvas) continue;\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n ctx.save();\n ctx.setTransform(size.dpr, 0, 0, size.dpr, 0, 0);\n ctx.clearRect(0, 0, size.width, size.height);\n layer.render(ctx, view, frame);\n ctx.restore();\n }\n dirty.current.clear();\n },\n { target: host },\n );\n\n useEffect(() => {\n if (dirty.current.size > 0) frameLoop.request();\n });\n}\n","import { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport {\n type PointerEvent as ReactPointerEvent,\n useCallback,\n useEffect,\n useRef,\n type WheelEvent,\n} from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { zoomAt } from './camera';\nimport { DEFAULT_FRAME, type WorldFrame } from './worldSpec';\n\nexport interface UsePanZoomOptions {\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n /** The opening `view.zoom` always stays reachable, widening these past\n * whatever is passed here if it would otherwise exclude it. */\n minZoom?: number;\n maxZoom?: number;\n /** The instrument's coordinate system, resolved against the viewport. The\n * wheel anchors in this frame; omitting it anchors at the element's\n * top-left, which is what labkit did before a frame could be declared. */\n frame?: WorldFrame;\n /** A primary-button press released without ever crossing the drag\n * threshold. The pointer session owns the release, so the gesture's end is\n * reported here rather than left for a consumer's own `onPointerUp`. */\n onTap?: (e: PointerEvent) => void;\n}\n\nexport interface PanZoomHandlers {\n onWheel: (e: WheelEvent<HTMLElement>) => void;\n onPointerDown: (e: ReactPointerEvent<HTMLElement>) => void;\n isDragging: () => boolean;\n}\n\ninterface DragState {\n startScreenX: number;\n startScreenY: number;\n startPan: { x: number; y: number };\n moved: boolean;\n}\n\nconst DRAG_THRESHOLD = 3;\n\nfunction isPositiveFinite(n: number): boolean {\n return Number.isFinite(n) && n > 0;\n}\n\nexport function usePanZoom({\n view,\n onViewChange,\n minZoom = 0.1,\n maxZoom = 32,\n frame = DEFAULT_FRAME,\n onTap,\n}: UsePanZoomOptions): PanZoomHandlers {\n const dragRef = useRef<DragState | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n const viewRef = useRef(view);\n viewRef.current = view;\n const onViewChangeRef = useRef(onViewChange);\n onViewChangeRef.current = onViewChange;\n const onTapRef = useRef(onTap);\n onTapRef.current = onTap;\n // Captured once, from the view the canvas opened at — not the current view,\n // which would let a prior zoom-out shrink the range and trap the opening\n // view unreachable behind it.\n const initialZoomRef = useRef(isPositiveFinite(view.zoom) ? view.zoom : null);\n\n const onWheel = useCallback(\n (e: WheelEvent<HTMLElement>) => {\n e.preventDefault();\n const rect = e.currentTarget.getBoundingClientRect();\n const initialZoom = initialZoomRef.current;\n onViewChange(\n zoomAt(\n viewRef.current,\n Math.exp(-e.deltaY * 0.001),\n { x: e.clientX - rect.left, y: e.clientY - rect.top },\n {\n frame,\n min: initialZoom == null ? minZoom : Math.min(minZoom, initialZoom),\n max: initialZoom == null ? maxZoom : Math.max(maxZoom, initialZoom),\n },\n ),\n );\n },\n [onViewChange, minZoom, maxZoom, frame],\n );\n\n const clearDrag = useCallback(() => {\n dragRef.current = null;\n sessionRef.current = null;\n }, []);\n\n const onPointerDown = useCallback(\n (e: ReactPointerEvent<HTMLElement>) => {\n if (e.button !== 0) return;\n const drag: DragState = {\n startScreenX: e.clientX,\n startScreenY: e.clientY,\n startPan: { ...viewRef.current.pan },\n moved: false,\n };\n dragRef.current = drag;\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n const dx = ev.clientX - drag.startScreenX;\n const dy = ev.clientY - drag.startScreenY;\n if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n drag.moved = true;\n onViewChangeRef.current({\n zoom: viewRef.current.zoom,\n pan: { x: drag.startPan.x + dx, y: drag.startPan.y + dy },\n });\n },\n onEnd: (ev) => {\n const tapped = !drag.moved;\n clearDrag();\n if (tapped) onTapRef.current?.(ev);\n },\n // A cancelled pan keeps whatever it had already applied: every move\n // was committed as it arrived, so there is nothing left to undo.\n onCancel: clearDrag,\n });\n },\n [clearDrag],\n );\n\n useEffect(() => () => sessionRef.current?.cancel(), []);\n\n const isDragging = useCallback(() => dragRef.current?.moved === true, []);\n\n return { onWheel, onPointerDown, isDragging };\n}\n","import { type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport type { Point, ViewTransform } from '../instrument/types';\nimport { CanvasStackContext } from './CanvasStackContext';\nimport { screenToWorld } from './canvasCoords';\nimport { type CanvasLayerDescriptor, useLayerScheduler } from './useLayerScheduler';\nimport { usePanZoom } from './usePanZoom';\nimport { resolveFrame, type ViewportSize, type WorldSpec } from './worldSpec';\n\n/** Props for `<CanvasStack>`. */\nexport interface CanvasStackProps {\n layers: CanvasLayerDescriptor[];\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n /** The instrument's coordinate system. Omitted, world (0,0) sits at the\n * element's top-left with y running down. */\n worldSpec?: WorldSpec;\n minZoom?: number;\n maxZoom?: number;\n width?: number | string;\n height?: number | string;\n className?: string;\n /** Fired whenever the stack is measured, so a consumer can place a view that\n * only makes sense in terms of the viewport. */\n onResize?: (size: ViewportSize) => void;\n onHitTest?: (worldPos: Point) => void;\n children?: ReactNode;\n}\n\n/** Stacks one `<canvas>` per layer and drives them from a shared view, so a\n * layer that changes rarely is not redrawn with one that changes every frame.\n * Handles sizing, device pixel ratio, and pan/zoom. */\nexport function CanvasStack({\n layers,\n view,\n onViewChange,\n worldSpec,\n minZoom,\n maxZoom,\n width = '100%',\n height = '100%',\n className,\n onResize,\n onHitTest,\n children,\n}: CanvasStackProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasMap = useRef<Map<string, HTMLCanvasElement>>(new Map());\n const [size, setSize] = useState({ width: 0, height: 0, dpr: 1 });\n\n const setCanvasRef = (id: string, el: HTMLCanvasElement | null): void => {\n if (el) canvasMap.current.set(id, el);\n else canvasMap.current.delete(id);\n };\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const update = () => {\n const rect = el.getBoundingClientRect();\n const dpr = window.devicePixelRatio ?? 1;\n setSize((prev) => {\n if (prev.width === rect.width && prev.height === rect.height && prev.dpr === dpr) {\n return prev;\n }\n return { width: rect.width, height: rect.height, dpr };\n });\n };\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const onResizeRef = useRef(onResize);\n onResizeRef.current = onResize;\n useEffect(() => {\n if (size.width === 0 && size.height === 0) return;\n onResizeRef.current?.({ width: size.width, height: size.height });\n }, [size.width, size.height]);\n\n const frame = useMemo(() => resolveFrame(worldSpec, size), [worldSpec, size]);\n\n const onTap = (e: PointerEvent) => {\n const el = containerRef.current;\n if (!onHitTest || !el) return;\n const rect = el.getBoundingClientRect();\n onHitTest(screenToWorld({ x: e.clientX - rect.left, y: e.clientY - rect.top }, view, frame));\n };\n\n const handlers = usePanZoom({ view, onViewChange, minZoom, maxZoom, frame, onTap });\n useLayerScheduler({ layers, view, frame, canvasRefs: canvasMap, size, host: containerRef });\n\n const ctxValue = useMemo(\n () => ({\n view,\n frame,\n surface: { element: containerRef, size, canvases: canvasMap, layers },\n }),\n [view, frame, size, layers],\n );\n\n const containerStyle: CSSProperties = { width, height };\n const canvasPx = {\n width: Math.max(0, Math.round(size.width * size.dpr)),\n height: Math.max(0, Math.round(size.height * size.dpr)),\n };\n const canvasCss: CSSProperties = {\n width: size.width,\n height: size.height,\n };\n\n return (\n <CanvasStackContext.Provider value={ctxValue}>\n <div\n ref={containerRef}\n className={className ? `lk-canvas-stack ${className}` : 'lk-canvas-stack'}\n style={containerStyle}\n onWheel={handlers.onWheel}\n onPointerDown={handlers.onPointerDown}\n >\n {layers.map((layer) => (\n <canvas\n key={layer.id}\n ref={(el) => setCanvasRef(layer.id, el)}\n className=\"lk-canvas-stack__canvas\"\n width={canvasPx.width}\n height={canvasPx.height}\n style={{ ...canvasCss, display: layer.visible ? 'block' : 'none' }}\n />\n ))}\n <div className=\"lk-canvas-stack__overlay\">{children}</div>\n </div>\n </CanvasStackContext.Provider>\n );\n}\n","import { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport {\n type PointerEvent as ReactPointerEvent,\n useCallback,\n useEffect,\n useRef,\n type WheelEvent,\n} from 'react';\n\n/** A point in the space the instrument works in. */\nexport interface Vec3 {\n x: number;\n y: number;\n z: number;\n}\n\n/** An orbit camera as a trial view: where it looks from, and what it looks at.\n * labkit does not turn this into a matrix — the host's renderer does. */\nexport interface OrbitView {\n yaw: number;\n pitch: number;\n distance: number;\n target: Vec3;\n}\n\n/** Just short of the pole. At exactly ±PI/2 the azimuth is undefined and the\n * camera rolls, which reads as the model jumping rather than as a limit. */\nexport const PITCH_LIMIT = Math.PI / 2 - 0.01;\n\nconst YAW_PER_PX = 0.008;\nconst PITCH_PER_PX = 0.008;\nconst DISTANCE_PER_NOTCH = 0.0015;\nconst DRAG_THRESHOLD = 3;\n\nexport function clampPitch(pitch: number): number {\n return Math.min(PITCH_LIMIT, Math.max(-PITCH_LIMIT, pitch));\n}\n\nexport function wrapYaw(yaw: number): number {\n const wrapped = ((((yaw + Math.PI) % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI)) - Math.PI;\n return wrapped === -Math.PI ? Math.PI : wrapped;\n}\n\n/** The view a drag of (dx, dy) from `start` produces. Absolute against the drag\n * start, so it can be re-applied any number of times without compounding. */\nexport function orbitAfterDrag(start: OrbitView, dx: number, dy: number): OrbitView {\n return {\n ...start,\n yaw: wrapYaw(start.yaw + dx * YAW_PER_PX),\n pitch: clampPitch(start.pitch + dy * PITCH_PER_PX),\n };\n}\n\n/** Multiplicative, so one notch covers the same proportion of the distance\n * whether the camera is near or far. */\nexport function orbitAfterWheel(\n view: OrbitView,\n deltaY: number,\n minDistance: number,\n maxDistance: number,\n): OrbitView {\n const factor = Math.exp(deltaY * DISTANCE_PER_NOTCH);\n return {\n ...view,\n distance: Math.min(maxDistance, Math.max(minDistance, view.distance * factor)),\n };\n}\n\nexport interface UseOrbitOptions {\n view: OrbitView;\n onViewChange: (v: OrbitView) => void;\n /** Restored on double-click. Defaults to the view the hook first saw. */\n home?: OrbitView;\n minDistance?: number;\n maxDistance?: number;\n}\n\nexport interface OrbitHandlers {\n onWheel: (e: WheelEvent<HTMLElement>) => void;\n onPointerDown: (e: ReactPointerEvent<HTMLElement>) => void;\n onDoubleClick: () => void;\n isDragging: () => boolean;\n}\n\ninterface DragState {\n startX: number;\n startY: number;\n startView: OrbitView;\n moved: boolean;\n}\n\n/** Pointer gestures over an orbit view: drag to turn, wheel or pinch to dolly,\n * double-click to go home. The 3D peer of `usePanZoom`. */\nexport function useOrbit({\n view,\n onViewChange,\n home,\n minDistance = 0.1,\n maxDistance = 1000,\n}: UseOrbitOptions): OrbitHandlers {\n const dragRef = useRef<DragState | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n const viewRef = useRef(view);\n viewRef.current = view;\n const onViewChangeRef = useRef(onViewChange);\n onViewChangeRef.current = onViewChange;\n const homeRef = useRef(home ?? view);\n if (home) homeRef.current = home;\n\n const onWheel = useCallback(\n (e: WheelEvent<HTMLElement>) => {\n e.preventDefault();\n onViewChange(orbitAfterWheel(viewRef.current, e.deltaY, minDistance, maxDistance));\n },\n [onViewChange, minDistance, maxDistance],\n );\n\n const clearDrag = useCallback(() => {\n dragRef.current = null;\n sessionRef.current = null;\n }, []);\n\n const onPointerDown = useCallback(\n (e: ReactPointerEvent<HTMLElement>) => {\n if (e.button !== 0) return;\n const drag: DragState = {\n startX: e.clientX,\n startY: e.clientY,\n startView: viewRef.current,\n moved: false,\n };\n dragRef.current = drag;\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n const dx = ev.clientX - drag.startX;\n const dy = ev.clientY - drag.startY;\n if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n drag.moved = true;\n onViewChangeRef.current(orbitAfterDrag(drag.startView, dx, dy));\n },\n onEnd: clearDrag,\n // A cancelled turn keeps where it got to: every move was committed as\n // it arrived, so there is nothing left to undo.\n onCancel: clearDrag,\n });\n },\n [clearDrag],\n );\n\n useEffect(() => () => sessionRef.current?.cancel(), []);\n\n const onDoubleClick = useCallback(() => onViewChange(homeRef.current), [onViewChange]);\n const isDragging = useCallback(() => dragRef.current?.moved === true, []);\n\n return { onWheel, onPointerDown, onDoubleClick, isDragging };\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { ce, le } from './chunk-ISSVF5PT.js';
2
- import { openPointerSession } from './chunk-54ZWZ5FQ.js';
1
+ import { ce, le } from './chunk-W3ECWC2K.js';
2
+ import { openPointerSession } from '@weasel-js/core';
3
3
  import { useRef, useMemo, useState, useEffect } from 'react';
4
4
  import { floatingStrategy, DEFAULT_INSET, DEFAULT_ANCHOR, FLOATING_DRAG_PREFIX } from 'windease';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -230,5 +230,5 @@ function Sidebar({ children, title, collapsed = false, onToggle }) {
230
230
  }
231
231
 
232
232
  export { FloatingPanel, JobProgress, Legend, Sidebar };
233
- //# sourceMappingURL=chunk-UDXOYZEC.js.map
234
- //# sourceMappingURL=chunk-UDXOYZEC.js.map
233
+ //# sourceMappingURL=chunk-MOM3GOVY.js.map
234
+ //# sourceMappingURL=chunk-MOM3GOVY.js.map