@weasel-js/labkit 1.7.3 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/README.md +24 -0
  2. package/dist/_dts/{CanvasStackContext-Cjy6I-Iv.d.ts → CanvasStackContext-B05ZM04l.d.ts} +3 -1
  3. package/dist/_dts/{Stage-D6t7o0pF.d.ts → Stage-CIJlbjrE.d.ts} +5 -3
  4. package/dist/_dts/{index-DCMA4Fy1.d.ts → index-8geQNQet.d.ts} +84 -12
  5. package/dist/_dts/{index-BTmaRy6a.d.ts → index-DRi3V5sN.d.ts} +6 -4
  6. package/dist/_dts/{types-BUBSIZuV.d.ts → types-DUfQ0isU.d.ts} +5 -1
  7. package/dist/_dts/{useTrialState-Bh8bzFgE.d.ts → useTrialState-Ar0AFpCe.d.ts} +94 -87
  8. package/dist/canvas/index.d.ts +5 -5
  9. package/dist/canvas/index.js +2 -2
  10. package/dist/chrome/index.d.ts +3 -3
  11. package/dist/chrome/index.js +7 -7
  12. package/dist/{chunk-HBGH344L.js → chunk-4RCVWX4M.js} +55 -18
  13. package/dist/chunk-4RCVWX4M.js.map +1 -0
  14. package/dist/{chunk-WIG6XHJ7.js → chunk-6IIKLXBZ.js} +13 -4
  15. package/dist/chunk-6IIKLXBZ.js.map +1 -0
  16. package/dist/{chunk-F4ACIK7Z.js → chunk-6Q6ACJ3H.js} +3 -3
  17. package/dist/{chunk-F4ACIK7Z.js.map → chunk-6Q6ACJ3H.js.map} +1 -1
  18. package/dist/{chunk-PMLZP7RT.js → chunk-7ALJOVGP.js} +5 -9
  19. package/dist/chunk-7ALJOVGP.js.map +1 -0
  20. package/dist/{chunk-XE5HJOPL.js → chunk-7FMZT2WL.js} +3 -3
  21. package/dist/{chunk-XE5HJOPL.js.map → chunk-7FMZT2WL.js.map} +1 -1
  22. package/dist/{chunk-XVUKDSYV.js → chunk-AVOILDN2.js} +2 -2
  23. package/dist/{chunk-XVUKDSYV.js.map → chunk-AVOILDN2.js.map} +1 -1
  24. package/dist/{chunk-2N7UBSS2.js → chunk-J5MDJEOZ.js} +3 -3
  25. package/dist/{chunk-2N7UBSS2.js.map → chunk-J5MDJEOZ.js.map} +1 -1
  26. package/dist/{chunk-LPGBC63T.js → chunk-KHB3KRRQ.js} +7 -7
  27. package/dist/chunk-KHB3KRRQ.js.map +1 -0
  28. package/dist/{chunk-IBVRMHW5.js → chunk-NHIO2O3O.js} +31 -30
  29. package/dist/chunk-NHIO2O3O.js.map +1 -0
  30. package/dist/{chunk-EESJUSYR.js → chunk-QMAY74TP.js} +56 -5
  31. package/dist/chunk-QMAY74TP.js.map +1 -0
  32. package/dist/{chunk-4X44DMRS.js → chunk-RN7T7OBO.js} +46 -24
  33. package/dist/chunk-RN7T7OBO.js.map +1 -0
  34. package/dist/{chunk-4TJ34FXA.js → chunk-RXO5UEGT.js} +5 -12
  35. package/dist/chunk-RXO5UEGT.js.map +1 -0
  36. package/dist/{chunk-E6NARGU7.js → chunk-SQX35X5V.js} +3 -3
  37. package/dist/{chunk-E6NARGU7.js.map → chunk-SQX35X5V.js.map} +1 -1
  38. package/dist/{chunk-YCFKW5RX.js → chunk-T5OEUNCB.js} +16 -2
  39. package/dist/chunk-T5OEUNCB.js.map +1 -0
  40. package/dist/{chunk-RL2LOLNI.js → chunk-UX66USTN.js} +2 -2
  41. package/dist/chunk-UX66USTN.js.map +1 -0
  42. package/dist/{chunk-IRMPZIYU.js → chunk-V4SMGEYY.js} +6732 -6507
  43. package/dist/chunk-V4SMGEYY.js.map +1 -0
  44. package/dist/{chunk-E23Q6A3Y.js → chunk-VCK5T6EP.js} +3 -3
  45. package/dist/{chunk-E23Q6A3Y.js.map → chunk-VCK5T6EP.js.map} +1 -1
  46. package/dist/{chunk-T6DLGEJB.js → chunk-XWR2P6MR.js} +23 -6
  47. package/dist/chunk-XWR2P6MR.js.map +1 -0
  48. package/dist/{chunk-XSJJTXNA.js → chunk-YZA73U4A.js} +19 -19
  49. package/dist/chunk-YZA73U4A.js.map +1 -0
  50. package/dist/config/index.d.ts +12 -5
  51. package/dist/config/index.js +2 -2
  52. package/dist/controls/index.d.ts +2 -2
  53. package/dist/controls/index.js +5 -5
  54. package/dist/dragdrop/index.d.ts +2 -2
  55. package/dist/dragdrop/index.js +2 -2
  56. package/dist/index.d.ts +158 -16
  57. package/dist/index.js +1036 -415
  58. package/dist/index.js.map +1 -1
  59. package/dist/layers/index.d.ts +2 -2
  60. package/dist/layers/index.js +3 -3
  61. package/dist/loupe/index.d.ts +176 -28
  62. package/dist/loupe/index.js +547 -141
  63. package/dist/loupe/index.js.map +1 -1
  64. package/dist/overview/index.d.ts +1 -1
  65. package/dist/overview/index.js +5 -5
  66. package/dist/passthrough/weasel-ui.js +3 -3
  67. package/dist/primitives/index.d.ts +4 -19
  68. package/dist/primitives/index.js +7 -7
  69. package/dist/state/index.d.ts +6 -8
  70. package/dist/state/index.js +5 -5
  71. package/dist/styles.css +181 -44
  72. package/dist/surface/index.d.ts +20 -1
  73. package/dist/surface/index.js +2 -2
  74. package/dist/undo/index.d.ts +19 -8
  75. package/dist/undo/index.js +1 -1
  76. package/package.json +11 -11
  77. package/src/annotations/AnnotationOverlay.abandon.test.tsx +4 -0
  78. package/src/annotations/AnnotationOverlay.tsx +10 -1
  79. package/src/annotations/Annotations.less +2 -0
  80. package/src/annotations/Annotations.overlay.test.tsx +29 -0
  81. package/src/canvas/CanvasStack.test.tsx +35 -0
  82. package/src/canvas/CanvasStack.tsx +12 -1
  83. package/src/canvas/useLayerScheduler.ts +14 -0
  84. package/src/chrome/builtins.test.ts +14 -0
  85. package/src/chrome/builtins.tsx +17 -2
  86. package/src/chrome/regions/StatusRegion.tsx +20 -14
  87. package/src/chrome/regions/regions.test.tsx +28 -0
  88. package/src/chrome/types.ts +8 -0
  89. package/src/clock/clock.browser.test.less +5 -0
  90. package/src/clock/clock.browser.test.tsx +77 -0
  91. package/src/clock/clock.integration.test.tsx +95 -0
  92. package/src/clock/clockRegistry.test.ts +33 -0
  93. package/src/clock/clockRegistry.ts +46 -0
  94. package/src/clock/hooks.ts +44 -0
  95. package/src/clock/index.ts +2 -0
  96. package/src/clock/trialClock.test.ts +265 -0
  97. package/src/clock/trialClock.ts +245 -0
  98. package/src/clock/useClockLoop.ts +45 -0
  99. package/src/config/builder.ts +11 -1
  100. package/src/config/resolve.test.ts +12 -0
  101. package/src/config/resolve.ts +5 -0
  102. package/src/config/types.ts +5 -1
  103. package/src/controls/ControlMatrix.test.tsx +10 -0
  104. package/src/controls/ControlMatrix.tsx +110 -58
  105. package/src/controls/ControlPanel.test.tsx +30 -0
  106. package/src/index.ts +3 -2
  107. package/src/instrument/types.ts +21 -1
  108. package/src/lab/Lab.chrome.test.tsx +8 -1
  109. package/src/lab/Lab.presentation.test.tsx +113 -0
  110. package/src/lab/Lab.surface.test.tsx +20 -9
  111. package/src/lab/Lab.tsx +132 -257
  112. package/src/lab/LabLightbox.browser.test.less +17 -0
  113. package/src/lab/LabLightbox.browser.test.tsx +216 -0
  114. package/src/lab/LabShell.less +10 -3
  115. package/src/lab/LabSurface.tsx +129 -0
  116. package/src/lab/Workspace.stories.tsx +18 -0
  117. package/src/lab/Workspace.surface.test.tsx +4 -0
  118. package/src/lab/Workspace.test.tsx +37 -1
  119. package/src/lab/Workspace.tsx +34 -6
  120. package/src/lab/index.ts +2 -0
  121. package/src/lab/nebula.ts +30 -0
  122. package/src/lab/openLab.test.ts +81 -0
  123. package/src/lab/openLab.ts +119 -0
  124. package/src/lab/presentation.browser.test.less +5 -0
  125. package/src/lab/presentation.browser.test.tsx +119 -0
  126. package/src/lab/presentation.less +64 -0
  127. package/src/lab/presentation.tsx +47 -0
  128. package/src/lab/useFocusPick.ts +43 -0
  129. package/src/lightbox/Lightbox.browser.test.less +14 -0
  130. package/src/lightbox/Lightbox.browser.test.tsx +113 -0
  131. package/src/lightbox/Lightbox.less +128 -0
  132. package/src/lightbox/Lightbox.test.tsx +195 -0
  133. package/src/lightbox/Lightbox.tsx +205 -0
  134. package/src/lightbox/LightboxContext.ts +38 -0
  135. package/src/lightbox/LightboxLayers.tsx +40 -0
  136. package/src/lightbox/index.ts +8 -0
  137. package/src/lightbox/modal.ts +94 -0
  138. package/src/loupe/AGENTS.md +40 -2
  139. package/src/loupe/CanvasLoupe.tsx +5 -4
  140. package/src/loupe/DomLoupe.tsx +7 -5
  141. package/src/loupe/Loupe.less +15 -0
  142. package/src/loupe/LoupeBubble.tsx +35 -13
  143. package/src/loupe/SourceLoupe.browser.test.tsx +204 -0
  144. package/src/loupe/SourceLoupe.tsx +60 -0
  145. package/src/loupe/TrialLoupe.browser.test.tsx +342 -0
  146. package/src/loupe/TrialLoupe.tsx +126 -28
  147. package/src/loupe/canvasLens.ts +20 -17
  148. package/src/loupe/index.ts +26 -2
  149. package/src/loupe/sourceLens.test.ts +65 -0
  150. package/src/loupe/sourceLens.ts +97 -0
  151. package/src/loupe/types.test.ts +6 -0
  152. package/src/loupe/types.ts +109 -7
  153. package/src/loupe/useLoupe.test.tsx +188 -3
  154. package/src/loupe/useLoupe.ts +106 -11
  155. package/src/primitives/index.ts +2 -2
  156. package/src/specimen/Specimen.tsx +0 -7
  157. package/src/state/helpers.test.ts +4 -11
  158. package/src/state/helpers.ts +2 -8
  159. package/src/state/index.ts +0 -2
  160. package/src/state/openLabStore.test.ts +10 -8
  161. package/src/state/openLabStore.ts +1 -1
  162. package/src/state/store.test.ts +2 -2
  163. package/src/state/store.ts +15 -16
  164. package/src/state/types.ts +9 -9
  165. package/src/styles.less +2 -1
  166. package/src/surface/AGENTS.md +13 -0
  167. package/src/surface/index.ts +1 -0
  168. package/src/surface/useSurfaceTile.test.tsx +4 -0
  169. package/src/surface/useSurfaceTile.ts +17 -1
  170. package/src/surface/useTiledSurface.test.tsx +65 -0
  171. package/src/surface/useTiledSurface.ts +68 -1
  172. package/src/theme/interstellar.theme.json +0 -14
  173. package/src/trial/Trial.stories.tsx +0 -1
  174. package/src/trial/Trial.test.tsx +40 -3
  175. package/src/trial/Trial.tsx +87 -43
  176. package/src/trial/TrialChrome.tsx +20 -2
  177. package/src/trial/trialOps.test.ts +1 -2
  178. package/src/trial/trialOps.ts +2 -3
  179. package/src/undo/stateOp.ts +21 -0
  180. package/src/undo/undoStack.ts +18 -6
  181. package/dist/chunk-4TJ34FXA.js.map +0 -1
  182. package/dist/chunk-4X44DMRS.js.map +0 -1
  183. package/dist/chunk-EESJUSYR.js.map +0 -1
  184. package/dist/chunk-HBGH344L.js.map +0 -1
  185. package/dist/chunk-IBVRMHW5.js.map +0 -1
  186. package/dist/chunk-IRMPZIYU.js.map +0 -1
  187. package/dist/chunk-LPGBC63T.js.map +0 -1
  188. package/dist/chunk-PMLZP7RT.js.map +0 -1
  189. package/dist/chunk-RL2LOLNI.js.map +0 -1
  190. package/dist/chunk-T6DLGEJB.js.map +0 -1
  191. package/dist/chunk-WIG6XHJ7.js.map +0 -1
  192. package/dist/chunk-XSJJTXNA.js.map +0 -1
  193. package/dist/chunk-YCFKW5RX.js.map +0 -1
  194. package/src/primitives/StatusBar.less +0 -34
  195. package/src/primitives/StatusBar.stories.tsx +0 -24
  196. package/src/primitives/StatusBar.test.tsx +0 -25
  197. package/src/primitives/StatusBar.tsx +0 -28
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/primitives/JobProgress.tsx","../src/primitives/Legend.tsx","../src/primitives/Readout.tsx","../src/primitives/Split.tsx"],"names":["jsx","jsxs","c"],"mappings":"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","file":"chunk-LPGBC63T.js","sourcesContent":["import { ErrorIcon, WarningIcon } from '@weasel-js/ui';\nimport type { JobHandle } from '../job/types';\n\n/** Props for `<JobProgress>`. */\nexport interface JobProgressProps {\n job: JobHandle;\n className?: string;\n}\n\n/** A running job's progress, failures and outcome. Determinate once the job\n * reports a total; a job that cannot count up front never sends one, so the\n * bar stays indeterminate rather than inventing a denominator. */\nexport function JobProgress({ job, className }: JobProgressProps) {\n const { status, done, total, failures, error } = job;\n if (status === 'idle') return null;\n\n const determinate = total !== null && total > 0;\n const fraction = determinate ? Math.min(1, done / total) : 0;\n const cls = ['lk-job', `lk-job--${status}`, className].filter(Boolean).join(' ');\n\n return (\n <div className={cls}>\n <div\n className={`lk-job__track${determinate ? '' : ' lk-job__track--indeterminate'}`}\n role=\"progressbar\"\n aria-label=\"Job progress\"\n aria-valuenow={determinate ? done : undefined}\n aria-valuemin={determinate ? 0 : undefined}\n aria-valuemax={determinate ? total : undefined}\n >\n {/* Scaling a full-width bar keeps the fill off the layout path, so\n progress updates never reflow the status bar. */}\n <div\n className=\"lk-job__fill\"\n style={{ transform: `scaleX(${determinate ? fraction : 1})` }}\n />\n </div>\n\n <span className=\"lk-job__count\">\n {done}\n {determinate ? ` / ${total}` : ''}\n </span>\n\n {failures.length > 0 && (\n <span className=\"lk-job__failures\" title={failures.map((f) => f.error).join('\\n')}>\n <WarningIcon size={14} />\n {failures.length} failed\n </span>\n )}\n\n {error && (\n <span className=\"lk-job__error\" title={error}>\n <ErrorIcon size={14} />\n {error}\n </span>\n )}\n\n {status === 'running' && (\n <button type=\"button\" className=\"lk-job__cancel\" onClick={job.cancel}>\n Cancel\n </button>\n )}\n </div>\n );\n}\n","import type { CSSProperties } from 'react';\n\n/** How a legend entry's swatch is drawn, matching how the thing looks on canvas. */\nexport type LegendMark = 'line' | 'dash' | 'dot' | 'band';\n\n/** One row of a legend: a swatch and what it means. */\nexport interface LegendEntry {\n key: string;\n label: string;\n color: string;\n /** Defaults to `'line'`. */\n mark?: LegendMark;\n}\n\n/** Props for `<Legend>`. */\nexport interface LegendProps {\n entries: readonly LegendEntry[];\n className?: string;\n}\n\n/** A color key. Presentational only — no handlers, no state, no hover behavior. */\nexport function Legend({ entries, className }: LegendProps) {\n return (\n <ul className={className ? `lk-legend ${className}` : 'lk-legend'}>\n {entries.map((entry) => {\n const mark = entry.mark ?? 'line';\n return (\n <li className=\"lk-legend__row\" key={entry.key}>\n <span\n aria-hidden=\"true\"\n className={`lk-legend__swatch lk-legend__swatch--${mark}`}\n style={{ '--lk-legend-ink': entry.color } as CSSProperties}\n />\n <span className=\"lk-legend__label\">{entry.label}</span>\n </li>\n );\n })}\n </ul>\n );\n}\n","import { DetailList, type DetailListValues, DetailRow, type DetailRowStatus } from '@weasel-js/ui';\nimport type { ReactNode } from 'react';\n\n/** One measurement in a {@link Readout}. */\nexport interface ReadoutRow {\n /** React key. Defaults to `label` when that is a string, else the row's index. */\n key?: string;\n label: ReactNode;\n /** `null` or `undefined` keeps the row in place showing an en dash, so the\n * readout holds its height while a measurement comes and goes. */\n value?: ReactNode;\n /** A dot in the status color before the value. Color is never the only\n * signal: the value has to say the same thing in words. */\n status?: DetailRowStatus;\n /** `text` sets this row's value as wrapping prose instead of a figure. */\n values?: DetailListValues;\n}\n\n/** Props for {@link Readout}. */\nexport interface ReadoutProps {\n rows: readonly ReadoutRow[];\n /** Heading above the rows, which also names the list. */\n title?: ReactNode;\n /** Drawn below the rows — a small table, a note. */\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * Values a lab only reads — measurements shown beside its picture, such as\n * \"Lock margin +47.9°\". A {@link DetailList} set as figures: labels take the\n * params label recipe and `--wzl-params-label-width` rail, so a readout lines\n * up with a `ControlPanel` beside it, and values sit right-aligned in the mono\n * face with spaces kept, so figure-space padding aligns a column on its\n * decimal point. Rows compose by hand as `DetailRow`s inside a `DetailList`\n * with `values=\"figures\"`.\n */\nexport function Readout({ rows, title, children, className }: ReadoutProps) {\n return (\n <div className={className ? `lk-readout ${className}` : 'lk-readout'}>\n <DetailList title={title} values=\"figures\">\n {rows.map((row, i) => (\n <DetailRow\n key={row.key ?? (typeof row.label === 'string' ? row.label : i)}\n label={row.label}\n status={row.status}\n values={row.values}\n >\n {row.value}\n </DetailRow>\n ))}\n </DetailList>\n {children != null && <div className=\"lk-readout__footer\">{children}</div>}\n </div>\n );\n}\n","import { useLatest } from '@weasel-js/core';\nimport { type ReactNode, useEffect, useMemo, useRef } from 'react';\nimport { asNodeId, createNode, Store, stripStrategy } from 'windease';\nimport { type ChromeMap, Container, Provider, StrategyRegistryProvider } from 'windease/react';\n\nconst ZONE_ID = asNodeId('lk-split');\nconst SIDEBAR_ID = asNodeId('lk-split-sidebar');\nconst CONTENT_ID = asNodeId('lk-split-content');\nconst STRATEGIES = { strip: stripStrategy as never };\n\n/** Props for `<Split>`. */\nexport interface SplitProps {\n /** The sidebar pane's contents. */\n sidebar: ReactNode;\n /** The edge the sidebar sits on. Read once, when the strip mounts. Default `'start'`. */\n side?: 'start' | 'end';\n /** The content pane's contents. */\n children: ReactNode;\n /** Names the sidebar pane, and through it the seam. Default `'Sidebar'`. */\n label?: string;\n /** Extra classes on the strip, which always has `lk-split`. */\n className?: string;\n /** The class on the sidebar pane. */\n sidebarClassName?: string;\n /** The class on the content pane. */\n contentClassName?: string;\n /** The sidebar's current extent in pixels. Omit to leave it unpersisted: the\n * seam still works, its result just does not outlive the mount. */\n width?: number;\n /** Where the sidebar starts before anyone drags it. */\n defaultWidth?: number;\n minWidth?: number;\n maxWidth?: number;\n /** How little the content pane may be squeezed to. Reported as the seam's\n * reachable maximum, so the drag stops there rather than clamping silently. */\n contentMinWidth?: number;\n /** Fires through the drag, not on release: the pane is already there. */\n onWidthChange?: (width: number) => void;\n /**\n * Fixed extent. Omit in an app — the strip measures its own box. Supply it\n * where nothing measures, notably jsdom, or both panes lay out at zero.\n */\n viewport?: { w: number; h: number };\n}\n\ninterface SplitInit {\n side: 'start' | 'end';\n label: string;\n sidebarWidth: number;\n minWidth: number;\n maxWidth: number;\n contentMinWidth: number;\n}\n\n/** A strip holding the sidebar and the content pane, sized as the first render asks. */\nfunction createSplitStore({\n side,\n label,\n sidebarWidth,\n minWidth,\n maxWidth,\n contentMinWidth,\n}: SplitInit): Store {\n const store = new Store();\n store.registerNode(\n createNode({\n kind: 'zone',\n id: ZONE_ID,\n // A strip's seam resizes the pane before it, which on the end side is the\n // content; 'neighbor' has the seam size the sidebar after it too.\n container: {\n strategyId: 'strip',\n config: {\n axis: 'x',\n resizable: true,\n ...(side === 'end' ? { resizeMode: 'neighbor' } : {}),\n },\n },\n }),\n );\n const sidebarNode = createNode({\n kind: 'sidebar',\n id: SIDEBAR_ID,\n parentId: ZONE_ID,\n meta: { title: label },\n placement: { size: { w: sidebarWidth } },\n hints: { minSize: { w: minWidth, h: 0 }, maxSize: { w: maxWidth, h: 0 } },\n });\n const contentNode = createNode({\n kind: 'content',\n id: CONTENT_ID,\n parentId: ZONE_ID,\n meta: { title: 'Content' },\n hints: { minSize: { w: contentMinWidth, h: 0 } },\n });\n // A strip lays its children out in registration order.\n for (const node of side === 'end' ? [contentNode, sidebarNode] : [sidebarNode, contentNode]) {\n store.registerNode(node);\n }\n store.showNode(SIDEBAR_ID);\n store.showNode(CONTENT_ID);\n return store;\n}\n\n/**\n * A sidebar and a content pane as a two-pane strip with a draggable seam.\n *\n * The seam is `stripStrategy`'s own resize affordance, so it arrives as a\n * `role=\"separator\"` carrying the reachable range, operable by pointer and by\n * arrows / Home / End, and clamped against the content pane's floor rather\n * than against the sidebar's alone.\n */\nexport function Split({\n sidebar,\n children,\n side = 'start',\n label = 'Sidebar',\n className,\n sidebarClassName,\n contentClassName,\n width,\n defaultWidth = 320,\n minWidth = 140,\n maxWidth = 720,\n contentMinWidth = 160,\n onWidthChange,\n viewport,\n}: SplitProps) {\n // The cross-axis half of every size hint below is inert: a horizontal strip\n // stretches its panes to the container's height and never reads `h`.\n const storeRef = useRef<Store | null>(null);\n if (storeRef.current === null) {\n storeRef.current = createSplitStore({\n side,\n label,\n sidebarWidth: width ?? defaultWidth,\n minWidth,\n maxWidth,\n contentMinWidth,\n });\n }\n const store = storeRef.current;\n\n // What the store last held, so the placement event can tell a drag from the\n // echo of our own write and a controlled `width` does not fight the seam.\n const widthRef = useRef(width ?? defaultWidth);\n\n useEffect(() => {\n store.setHints(SIDEBAR_ID, {\n minSize: { w: minWidth, h: 0 },\n maxSize: { w: maxWidth, h: 0 },\n });\n store.setHints(CONTENT_ID, { minSize: { w: contentMinWidth, h: 0 } });\n }, [store, minWidth, maxWidth, contentMinWidth]);\n\n useEffect(() => {\n store.setMeta(SIDEBAR_ID, { title: label });\n }, [store, label]);\n\n useEffect(() => {\n if (width === undefined || width === widthRef.current) return;\n widthRef.current = width;\n store.patchPlacement(SIDEBAR_ID, { size: { w: width } });\n }, [store, width]);\n\n const onWidthChangeRef = useLatest(onWidthChange);\n useEffect(\n () =>\n store.events.on('node.placementChanged', () => {\n // A neighbor drag pins the content's width too; unpinned, it goes on filling the strip.\n if ((store.getPlacement(CONTENT_ID)?.size as { w?: number } | undefined)?.w !== undefined) {\n store.patchPlacement(CONTENT_ID, { size: {} });\n }\n const next = (store.getPlacement(SIDEBAR_ID)?.size as { w?: number } | undefined)?.w;\n if (typeof next !== 'number' || next === widthRef.current) return;\n widthRef.current = next;\n onWidthChangeRef.current?.(next);\n }),\n [onWidthChangeRef, store],\n );\n\n const chrome = useMemo<ChromeMap>(\n () => ({\n sidebar: () => <div className={sidebarClassName}>{sidebar}</div>,\n content: () => <div className={contentClassName}>{children}</div>,\n }),\n [sidebar, children, sidebarClassName, contentClassName],\n );\n\n return (\n <Provider store={store}>\n <StrategyRegistryProvider strategies={STRATEGIES}>\n <Container\n parentId={ZONE_ID}\n chrome={chrome}\n className={className ? `lk-split windease-zone ${className}` : 'lk-split windease-zone'}\n affordances\n viewport={viewport}\n />\n </StrategyRegistryProvider>\n </Provider>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/state/helpers.ts","../src/state/adapters.ts","../src/state/labRecords.ts","../src/state/records.ts","../src/state/useOpenOnce.ts","../src/state/Persistence.tsx","../src/state/usePersistedState.ts"],"names":["useState"],"mappings":";;;;;AAIO,SAAS,aAAA,CACd,YACA,MAAA,EACQ;AACR,EAAA,OAAO,CAAA,GAAA,EAAM,UAAU,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AACnC;AAGO,SAAS,cAAc,KAAA,EAAuB;AACnD,EAAA,OAAO,IAAA,CAAK,kBAAA,CAAmB,KAAK,CAAC,CAAA;AACvC;AAIO,SAAS,cAAc,IAAA,EAA6B;AACzD,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,IAAI;AACF,IAAA,OAAO,kBAAA,CAAmB,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EACtC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAWA,IAAI,SAAA,GAAY,CAAA;AACT,SAAS,KAAA,GAAgB;AAC9B,EAAA,MAAM,IAAK,UAAA,CAA0D,MAAA;AACrE,EAAA,IAAI,KAAK,OAAO,CAAA,CAAE,eAAe,UAAA,EAAY,OAAO,EAAE,UAAA,EAAW;AACjE,EAAA,OAAO,CAAA,GAAA,EAAM,KAAK,GAAA,EAAI,CAAE,SAAS,EAAE,CAAC,IAAI,SAAA,EAAW,CAAA,CAAA;AACrD;AAGO,SAAS,cAAA,GAA4B;AAC1C,EAAA,OAAO,EAAE,IAAA,EAAM,EAAC,EAAG,MAAA,EAAQ,EAAC,EAAE;AAChC;AAKO,SAAS,eAAA,CACd,QACA,WAAA,EACmB;AACnB,EAAA,OAAO,MAAA,CAAO,IAAI,CAAC,EAAE,WAAW,KAAA,EAAO,GAAG,GAAE,KAAM;AAChD,IAAA,MAAM,CAAA,GAAI,WAAA,CAAY,CAAA,CAAE,cAAc,CAAA;AACtC,IAAA,OAAO,EAAE,GAAG,CAAA,EAAG,KAAA,EAAO,CAAA,EAAG,SAAA,GAAY,CAAA,CAAE,SAAA,CAAU,CAAA,CAAE,KAAK,CAAA,GAAI,CAAA,CAAE,KAAA,EAAM;AAAA,EACtE,CAAC,CAAA;AACH;AAKO,SAAS,iBAAA,CACd,SACA,aAAA,EACe;AACf,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC3B,IAAA,OAAA,CAAQ,KAAK,gEAAgE,CAAA;AAC7E,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM;AACxB,IAAA,MAAM,CAAA,GAAI,aAAA,CAAc,CAAA,CAAE,cAAc,CAAA;AACxC,IAAA,OAAO;AAAA,MACL,GAAG,CAAA;AAAA,MACH,KAAA,EAAO,CAAA,EAAG,WAAA,GAAc,CAAA,CAAE,WAAA,CAAY,EAAE,KAAA,EAAO,CAAA,CAAE,MAAM,CAAA,GAAI,CAAA,CAAE,KAAA;AAAA,MAC7D,WAAW,cAAA;AAAe,KAC5B;AAAA,EACF,CAAC,CAAA;AACH;;;AC1EA,SAAS,MAAM,GAAA,EAAsB;AACnC,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,GAAA;AAAA,EACT;AACF;AAKA,SAAS,UAAA,CAAW,KAAA,EAAgB,IAAA,mBAAoB,IAAI,KAAI,EAAY;AAC1E,EAAA,IAAI,KAAA,KAAU,MAAM,OAAO,IAAA;AAC3B,EAAA,QAAQ,OAAO,KAAA;AAAO,IACpB,KAAK,QAAA;AAAA,IACL,KAAK,SAAA;AACH,MAAA,OAAO,IAAA;AAAA,IACT,KAAK,QAAA;AACH,MAAA,OAAO,MAAA,CAAO,SAAS,KAAK,CAAA;AAAA,IAC9B,KAAK,QAAA,EAAU;AACb,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,KAAA;AAC5B,MAAA,IAAA,CAAK,IAAI,KAAK,CAAA;AACd,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,cAAA,CAAe,KAAK,CAAA;AACzC,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAC1B,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,EAAG,IAAI,CAAC,CAAA,GAAA,CACrC,KAAA,KAAU,MAAA,CAAO,SAAA,IAAa,KAAA,KAAU,IAAA,KACzC,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,CAAA,KAAM,MAAA,IAAa,UAAA,CAAW,CAAA,EAAG,IAAI,CAAC,CAAA;AAC5E,MAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AACjB,MAAA,OAAO,EAAA;AAAA,IACT;AAAA,IACA;AACE,MAAA,OAAO,KAAA;AAAA;AAEb;AAEA,SAAS,iBAAA,CAAkB,IAAA,EAAqB,IAAA,EAAc,QAAA,EAAmC;AAC/F,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAC/B,EAAA,MAAM,OAAA,GAA0B;AAAA,IAC9B,GAAA,EAAK,OAAO,GAAA,KAAQ;AAClB,MAAA,MAAM,GAAA,GAAM,IAAA,EAAK,CAAE,OAAA,CAAQ,GAAG,CAAA;AAC9B,MAAA,OAAO,GAAA,KAAQ,IAAA,GAAO,MAAA,GAAY,KAAA,CAAM,GAAG,CAAA;AAAA,IAC7C,CAAA;AAAA,IACA,IAAA,EAAM,OAAO,MAAA,KAAW;AACtB,MAAA,MAAM,QAAQ,IAAA,EAAK;AACnB,MAAA,MAAM,MAA2B,EAAC;AAClC,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,QAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA;AACvB,QAAA,IAAI,QAAQ,IAAA,IAAQ,CAAC,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC7C,QAAA,MAAM,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AAC7B,QAAA,IAAI,GAAA,KAAQ,MAAM,GAAA,CAAI,IAAA,CAAK,CAAC,GAAA,EAAK,KAAA,CAAM,GAAG,CAAC,CAAC,CAAA;AAAA,MAC9C;AACA,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA,GAAA,EAAK,OAAO,GAAA,EAAK,KAAA,KAAU;AACzB,MAAA,IAAI,CAAC,OAAO,GAAA,CAAI,GAAG,KAAK,CAAC,UAAA,CAAW,KAAK,CAAA,EAAG;AAC1C,QAAA,MAAA,CAAO,IAAI,GAAG,CAAA;AACd,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,UAAA,EAAa,GAAG,CAAA,0CAAA,EAA6C,IAAI,CAAA,oEAAA;AAAA,SACnE;AAAA,MACF;AACA,MAAA,IAAA,GAAO,OAAA,CAAQ,GAAA,EAAK,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,IAC3C,CAAA;AAAA,IACA,MAAA,EAAQ,OAAO,GAAA,KAAQ;AACrB,MAAA,IAAA,EAAK,CAAE,WAAW,GAAG,CAAA;AAAA,IACvB;AAAA,GACF;AACA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAA,CAAQ,SAAA,GAAY,CAAC,MAAA,EAAQ,EAAA,KAAO;AAClC,MAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,MAAM;AAAA,MAAC,CAAA;AACjD,MAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAA0B;AACzC,QAAA,IAAI,CAAA,CAAE,QAAQ,IAAA,IAAQ,CAAC,EAAE,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AACjD,QAAA,IAAI,CAAA,CAAE,WAAA,KAAgB,IAAA,EAAK,EAAG;AAC9B,QAAA,EAAA,CAAG,CAAC,CAAC,CAAA,CAAE,GAAA,EAAK,CAAA,CAAE,QAAA,KAAa,IAAA,GAAO,MAAA,GAAY,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;AAAA,MACnE,CAAA;AACA,MAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,OAAO,CAAA;AAC1C,MAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,IAC5D,CAAA;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAIO,IAAM,mBAAA,GAAsC,iBAAA;AAAA,EACjD,MAAM,YAAA;AAAA,EACN,cAAA;AAAA,EACA;AACF;AAGO,IAAM,qBAAA,GAAwC,iBAAA;AAAA,EACnD,MAAM,cAAA;AAAA,EACN,gBAAA;AAAA,EACA;AACF;AAEA,IAAM,cAAA,GAAiB,OAAO,MAAA,KAAW,WAAA;AAEzC,SAAS,WAAA,GAAsC;AAC7C,EAAA,IAAI,CAAC,cAAA,EAAgB,OAAO,EAAC;AAC7B,EAAA,MAAM,GAAA,GAAM,cAAc,MAAA,CAAO,QAAA,CAAS,KAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAC,CAAA;AAChE,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,EAAC;AAClB,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAIA,IAAM,eAAA,uBAAsB,GAAA,EAAsC;AAElE,SAAS,aAAa,GAAA,EAAmC;AACvD,EAAA,IAAI,CAAC,cAAA,EAAgB;AACrB,EAAA,MAAM,OAAA,GAAU,aAAA,CAAc,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA;AACjD,EAAA,MAAA,CAAO,QAAQ,YAAA,CAAa,IAAA,EAAM,EAAA,EAAI,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,CAAA;AACnD,EAAA,KAAA,MAAW,OAAO,eAAA,EAAiB,GAAA,CAAI,IAAA,GAAO,EAAE,GAAG,GAAA,EAAI;AACzD;AAKO,IAAM,cAAA,GAAiC;AAAA,EAC5C,GAAA,EAAK,OAAO,GAAA,KAAQ;AAClB,IAAA,MAAM,GAAA,GAAM,WAAA,EAAY,CAAE,GAAG,CAAA;AAC7B,IAAA,OAAO,GAAA,KAAQ,MAAA,GAAY,MAAA,GAAY,KAAA,CAAM,GAAG,CAAA;AAAA,EAClD,CAAA;AAAA,EACA,IAAA,EAAM,OAAO,MAAA,KACX,MAAA,CAAO,OAAA,CAAQ,WAAA,EAAa,CAAA,CACzB,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,GAAA,CAAI,UAAA,CAAW,MAAM,CAAC,CAAA,CACxC,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,GAAG,CAAA,KAAyB,CAAC,GAAA,EAAK,KAAA,CAAM,GAAG,CAAC,CAAC,CAAA;AAAA,EAC7D,GAAA,EAAK,OAAO,GAAA,EAAK,KAAA,KAAU;AACzB,IAAA,MAAM,MAAM,WAAA,EAAY;AACxB,IAAA,GAAA,CAAI,GAAG,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAC/B,IAAA,YAAA,CAAa,GAAG,CAAA;AAAA,EAClB,CAAA;AAAA,EACA,MAAA,EAAQ,OAAO,GAAA,KAAQ;AACrB,IAAA,MAAM,MAAM,WAAA,EAAY;AACxB,IAAA,OAAO,IAAI,GAAG,CAAA;AACd,IAAA,YAAA,CAAa,GAAG,CAAA;AAAA,EAClB,CAAA;AAAA,EACA,SAAA,EAAW,CAAC,MAAA,EAAQ,EAAA,KAAO;AACzB,IAAA,IAAI,CAAC,cAAA,EAAgB,OAAO,MAAM;AAAA,IAAC,CAAA;AACnC,IAAA,MAAM,GAAA,GAAM,EAAE,IAAA,EAAM,WAAA,EAAY,EAAE;AAClC,IAAA,eAAA,CAAgB,IAAI,GAAG,CAAA;AACvB,IAAA,MAAM,UAAU,MAAY;AAC1B,MAAA,MAAM,OAAO,WAAA,EAAY;AACzB,MAAA,MAAM,UAA2B,EAAC;AAClC,MAAA,KAAA,MAAW,uBAAO,IAAI,GAAA,CAAI,CAAC,GAAG,OAAO,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG,GAAG,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAC,CAAA,EAAG;AAC3E,QAAA,IAAI,CAAC,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,IAAK,GAAA,CAAI,IAAA,CAAK,GAAG,CAAA,KAAM,IAAA,CAAK,GAAG,CAAA,EAAG;AAC5D,QAAA,MAAM,GAAA,GAAM,KAAK,GAAG,CAAA;AACpB,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,GAAA,EAAK,GAAA,KAAQ,SAAY,MAAA,GAAY,KAAA,CAAM,GAAG,CAAC,CAAC,CAAA;AAAA,MAChE;AACA,MAAA,GAAA,CAAI,IAAA,GAAO,IAAA;AACX,MAAA,IAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG,EAAA,CAAG,OAAO,CAAA;AAAA,IACpC,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,cAAc,OAAO,CAAA;AAC7C,IAAA,OAAO,MAAM;AACX,MAAA,eAAA,CAAgB,OAAO,GAAG,CAAA;AAC1B,MAAA,MAAA,CAAO,mBAAA,CAAoB,cAAc,OAAO,CAAA;AAAA,IAClD,CAAA;AAAA,EACF;AACF;AAQA,IAAM,iBAAA,uBAAwB,OAAA,EAAqD;AAK5E,SAAS,mBAAA,CAAoB,OAAA,mBAAgC,IAAI,GAAA,EAAI,EAAmB;AAC7F,EAAA,IAAI,WAAA,GAAc,iBAAA,CAAkB,GAAA,CAAI,OAAO,CAAA;AAC/C,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,WAAA,uBAAkB,GAAA,EAAI;AACtB,IAAA,iBAAA,CAAkB,GAAA,CAAI,SAAS,WAAW,CAAA;AAAA,EAC5C;AACA,EAAA,MAAM,KAAA,GAAQ,WAAA;AACd,EAAA,MAAM,MAAA,GAAS,CAAC,GAAA,EAAa,KAAA,KAAyB;AACpD,IAAA,KAAA,MAAW,OAAO,KAAA,EAAO;AACvB,MAAA,IAAI,GAAA,CAAI,UAAU,OAAA,IAAW,CAAC,IAAI,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA,EAAG;AAC1D,MAAA,MAAM,IAAA,GAAO,KAAA,KAAU,MAAA,GAAY,MAAA,GAAY,gBAAgB,KAAK,CAAA;AACpE,MAAA,cAAA,CAAe,MAAM,IAAI,EAAA,CAAG,CAAC,CAAC,GAAA,EAAK,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,IAC5C;AAAA,EACF,CAAA;AACA,EAAA,MAAM,OAAA,GAA0B;AAAA,IAC9B,GAAA,EAAK,OAAO,GAAA,KAAS,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,GAAI,eAAA,CAAgB,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAC,CAAA,GAAI,MAAA;AAAA,IAC5E,IAAA,EAAM,OAAO,MAAA,KACX,CAAC,GAAG,OAAO,CAAA,CACR,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,GAAA,CAAI,UAAA,CAAW,MAAM,CAAC,CAAA,CACxC,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAyB,CAAC,GAAA,EAAK,eAAA,CAAgB,KAAK,CAAC,CAAC,CAAA;AAAA,IAC3E,GAAA,EAAK,OAAO,GAAA,EAAK,KAAA,KAAU;AACzB,MAAA,MAAM,IAAA,GAAO,gBAAgB,KAAK,CAAA;AAClC,MAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,IAAI,CAAA;AACrB,MAAA,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,EAAQ,OAAO,GAAA,KAAQ;AACrB,MAAA,OAAA,CAAQ,OAAO,GAAG,CAAA;AAClB,MAAA,MAAA,CAAO,KAAK,MAAS,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,MAAA,EAAQ,EAAA,KAAO;AACzB,MAAA,MAAM,GAAA,GAAM,EAAE,KAAA,EAAO,OAAA,EAAS,QAAQ,EAAA,EAAG;AACzC,MAAA,KAAA,CAAM,IAAI,GAAG,CAAA;AACb,MAAA,OAAO,MAAM;AACX,QAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACA,EAAA,OAAO,OAAA;AACT;AAGO,IAAM,WAAA,GAA8B;AAAA,EACzC,KAAK,YAAY,MAAA;AAAA,EACjB,IAAA,EAAM,YAAY,EAAC;AAAA,EACnB,KAAK,YAAY;AAAA,EAAC,CAAA;AAAA,EAClB,QAAQ,YAAY;AAAA,EAAC;AACvB;AAkBO,SAAS,sBAAA,CAAuB;AAAA,EACrC,QAAA,GAAW,QAAA;AAAA,EACX,KAAA,GAAQ;AACV,CAAA,GAA6B,EAAC,EAAmB;AAC/C,EAAA,IAAI,MAAA,GAAsC,IAAA;AAC1C,EAAA,MAAM,OAAO,MAA4B;AACvC,IAAA,MAAA,KAAW,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AAC1C,MAAA,IAAI,OAAO,cAAc,WAAA,EAAa;AACpC,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,0CAA0C,CAAC,CAAA;AAC5D,QAAA;AAAA,MACF;AACA,MAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,CAAK,QAAA,EAAU,CAAC,CAAA;AAC1C,MAAA,OAAA,CAAQ,kBAAkB,MAAM;AAC9B,QAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,CAAO,gBAAA,CAAiB,QAAA,CAAS,KAAK,CAAA,EAAG;AACpD,UAAA,OAAA,CAAQ,MAAA,CAAO,kBAAkB,KAAK,CAAA;AAAA,QACxC;AAAA,MACF,CAAA;AACA,MAAA,OAAA,CAAQ,YAAY,MAAM;AACxB,QAAA,MAAM,KAAK,OAAA,CAAQ,MAAA;AACnB,QAAA,IAAI,CAAC,EAAA,CAAG,gBAAA,CAAiB,QAAA,CAAS,KAAK,CAAA,EAAG;AACxC,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,QAAQ,CAAA,gBAAA,EAAmB,KAAK,GAAG,CAAC,CAAA;AACrF,UAAA;AAAA,QACF;AACA,QAAA,OAAA,CAAQ,EAAE,CAAA;AAAA,MACZ,CAAA;AACA,MAAA,OAAA,CAAQ,OAAA,GAAU,MAAM,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AAC5C,MAAA,OAAA,CAAQ,SAAA,GAAY,MAAM,MAAA,CAAO,IAAI,MAAM,CAAA,oBAAA,EAAuB,QAAQ,cAAc,CAAC,CAAA;AAAA,IAC3F,CAAC,CAAA;AACD,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,GAAA,GAAM,CAAI,IAAA,EAA0B,IAAA,KACxC,MAAK,CAAE,IAAA;AAAA,IACL,CAAC,EAAA,KACC,IAAI,OAAA,CAAW,CAAC,SAAS,MAAA,KAAW;AAClC,MAAA,MAAM,EAAA,GAAK,EAAA,CAAG,WAAA,CAAY,KAAA,EAAO,IAAI,CAAA;AACrC,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,KAAK,CAAC,CAAA;AACzC,MAAA,EAAA,CAAG,UAAA,GAAa,MAAM,OAAA,CAAQ,MAAA,EAAQ,CAAA;AACtC,MAAA,EAAA,CAAG,OAAA,GAAU,MAAM,MAAA,CAAO,EAAA,CAAG,KAAK,CAAA;AAClC,MAAA,EAAA,CAAG,OAAA,GAAU,MAAM,MAAA,CAAO,EAAA,CAAG,KAAK,CAAA;AAAA,IACpC,CAAC;AAAA,GACL;AAEF,EAAA,IAAI,OAAA,GAA8B,IAAA;AAClC,EAAA,MAAM,aAAa,MAA0B;AAC3C,IAAA,IAAI,OAAO,gBAAA,KAAqB,WAAA,EAAa,OAAO,IAAA;AACpD,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAA,GAAU,IAAI,gBAAA,CAAiB,CAAA,OAAA,EAAU,QAAQ,CAAA,CAAA,EAAI,KAAK,CAAA,CAAE,CAAA;AAE5D,MAAA,OAAA,CAAQ,KAAA,IAAQ;AAAA,IAClB;AACA,IAAA,OAAO,OAAA;AAAA,EACT,CAAA;AACA,EAAA,MAAM,QAAA,GAAW,CAAC,OAAA,KAAmC;AACnD,IAAA,UAAA,EAAW,EAAG,YAAY,OAAO,CAAA;AAAA,EACnC,CAAA;AAEA,EAAA,MAAM,OAAA,GAA0B;AAAA,IAC9B,KAAK,CAAC,GAAA,KACJ,GAAA,CAAI,UAAA,EAAY,CAAC,CAAA,KAAM;AACrB,MAAA,MAAM,OAAA,GAAU,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA;AACzB,MAAA,OAAO,MAAM,OAAA,CAAQ,MAAA;AAAA,IACvB,CAAC,CAAA;AAAA,IACH,MAAM,CAAC,MAAA,KACL,GAAA,CAAI,UAAA,EAAY,CAAC,CAAA,KAAM;AACrB,MAAA,MAAM,QAAQ,WAAA,CAAY,KAAA,CAAM,MAAA,EAAQ,CAAA,EAAG,MAAM,CAAA,MAAA,CAAG,CAAA;AACpD,MAAA,MAAM,IAAA,GAAO,CAAA,CAAE,UAAA,CAAW,KAAK,CAAA;AAC/B,MAAA,MAAM,MAAA,GAAS,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AAC7B,MAAA,OAAO,MACL,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,KAAyB,CAAC,MAAA,CAAO,GAAG,CAAA,EAAG,MAAA,CAAO,MAAA,CAAO,CAAC,CAAC,CAAC,CAAA;AAAA,IAClF,CAAC,CAAA;AAAA,IACH,GAAA,EAAK,OAAO,GAAA,EAAK,KAAA,KAAU;AACzB,MAAA,MAAM,GAAA,CAAI,WAAA,EAAa,CAAC,CAAA,KAAM;AAC5B,QAAA,CAAA,CAAE,GAAA,CAAI,OAAO,GAAG,CAAA;AAChB,QAAA,OAAO,MAAM,MAAA;AAAA,MACf,CAAC,CAAA;AACD,MAAA,QAAA,CAAS,CAAC,CAAC,GAAA,EAAK,KAAK,CAAC,CAAC,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,MAAA,EAAQ,OAAO,GAAA,KAAQ;AACrB,MAAA,MAAM,GAAA,CAAI,WAAA,EAAa,CAAC,CAAA,KAAM;AAC5B,QAAA,CAAA,CAAE,OAAO,GAAG,CAAA;AACZ,QAAA,OAAO,MAAM,MAAA;AAAA,MACf,CAAC,CAAA;AACD,MAAA,QAAA,CAAS,CAAC,CAAC,GAAA,EAAK,MAAS,CAAC,CAAC,CAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,IAAI,OAAO,qBAAqB,WAAA,EAAa;AAC3C,IAAA,OAAA,CAAQ,SAAA,GAAY,CAAC,MAAA,EAAQ,EAAA,KAAO;AAClC,MAAA,MAAM,KAAK,UAAA,EAAW;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI,OAAO,MAAM;AAAA,MAAC,CAAA;AACvB,MAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAA2C;AAC1D,QAAA,MAAM,IAAA,GAAO,CAAA,CAAE,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,GAAA,CAAI,UAAA,CAAW,MAAM,CAAC,CAAA;AAC5D,QAAA,IAAI,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG,EAAA,CAAG,IAAI,CAAA;AAAA,MAC9B,CAAA;AACA,MAAA,EAAA,CAAG,gBAAA,CAAiB,WAAW,OAAO,CAAA;AACtC,MAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,IACxD,CAAA;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAGO,IAAM,mBAAmC,sBAAA;AAEhD,IAAI,eAAA,GAAkD,IAAA;AAI/C,SAAS,cAAA,GAA0C;AACxD,EAAA,eAAA,KAAoB,gBAAA,CAAiB,IAAA,CAAK,IAAG,CAAA,CAAE,IAAA;AAAA,IAC7C,MAAM,gBAAA;AAAA,IACN,CAAC,KAAA,KAAU;AACT,MAAA,OAAA,CAAQ,IAAA,CAAK,yEAAyE,KAAK,CAAA;AAC3F,MAAA,OAAO,mBAAA;AAAA,IACT;AAAA,GACF;AACA,EAAA,OAAO,eAAA;AACT;;;ACtWO,SAAS,UAAU,UAAA,EAA4B;AACpD,EAAA,OAAO,CAAA,GAAA,EAAM,kBAAA,CAAmB,UAAU,CAAC,CAAA,CAAA,CAAA;AAC7C;AAEO,IAAM,WAAA,GAAc;AACpB,IAAM,aAAA,GAAgB;AACtB,IAAM,aAAA,GAAgB;AAEtB,SAAS,YAAY,EAAA,EAAoB;AAC9C,EAAA,OAAO,CAAA,MAAA,EAAS,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAA;AACxC;AAEO,SAAS,WAAW,EAAA,EAAoB;AAC7C,EAAA,OAAO,CAAA,KAAA,EAAQ,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAA;AACvC;AAGO,SAAS,kBAAkB,OAAA,EAAyB;AACzD,EAAA,OAAO,CAAA,YAAA,EAAe,kBAAA,CAAmB,OAAO,CAAC,CAAA,CAAA,CAAA;AACnD;AAIO,SAAS,WAAA,CAAY,SAAwB,IAAA,EAAsB;AACxE,EAAA,MAAM,OAAA,GAAU,mBAAmB,IAAI,CAAA;AACvC,EAAA,OAAO,OAAA,KAAY,IAAA,GAAO,CAAA,UAAA,EAAa,OAAO,CAAA,CAAA,GAAK,GAAG,iBAAA,CAAkB,OAAO,CAAC,CAAA,EAAG,OAAO,CAAA,CAAA;AAC5F;AAOO,SAAS,gBAAgB,IAAA,EAAiC;AAC/D,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,IAAI;AACF,IAAA,QAAQ,KAAA,CAAM,CAAC,CAAA;AAAG,MAChB,KAAK,MAAA;AAAA,MACL,KAAK,QAAA;AAAA,MACL,KAAK,QAAA;AACH,QAAA,OAAO,KAAA,CAAM,WAAW,CAAA,GAAI,EAAE,MAAM,KAAA,CAAM,CAAC,GAAE,GAAI,IAAA;AAAA,MACnD,KAAK,OAAA;AAAA,MACL,KAAK,MAAA;AACH,QAAA,OAAO,MAAM,MAAA,KAAW,CAAA,IAAK,KAAA,CAAM,CAAC,IAChC,EAAE,IAAA,EAAM,KAAA,CAAM,CAAC,GAAG,EAAA,EAAI,kBAAA,CAAmB,MAAM,CAAC,CAAC,GAAE,GACnD,IAAA;AAAA,MACN,KAAK,OAAA;AACH,QAAA,OAAQ,MAAM,CAAC,CAAA,KAAM,KAAA,IAAS,KAAA,CAAM,WAAW,CAAA,IAC5C,KAAA,CAAM,CAAC,CAAA,KAAM,WAAW,KAAA,CAAM,MAAA,KAAW,IACxC,EAAE,IAAA,EAAM,SAAQ,GAChB,IAAA;AAAA,MACN;AACE,QAAA,OAAO,IAAA;AAAA;AACX,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAQA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,CAAC,CAAC,KAAA,IAAS,OAAO,UAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AACrE;AAKO,SAAS,kBAAkB,GAAA,EAAuC;AACvE,EAAA,OAAO;AAAA,IACL,GAAG,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,GAAG,KAAA,KAA6B,CAAC,WAAA,CAAY,CAAA,CAAE,EAAE,CAAA,EAAG,EAAE,GAAG,CAAA,EAAG,KAAA,EAAO,CAAC,CAAA;AAAA,IACvF,GAAG,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAyB,CAAC,UAAA,CAAW,CAAA,CAAE,EAAE,CAAA,EAAG,CAAC,CAAC,CAAA;AAAA,IAChE,CAAC,aAAA,EAAe,GAAA,CAAI,MAAM,CAAA;AAAA,IAC1B,CAAC,aAAA,EAAe,GAAA,CAAI,cAAc,CAAA;AAAA,IAClC,CAAC,aAAa,EAAE,OAAA,EAAS,IAAI,OAAA,EAAS,IAAA,EAAM,GAAA,CAAI,IAAA,EAAwB;AAAA,GAC1E;AACF;AAKO,SAAS,kBACd,OAAA,EACsE;AACtE,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,MAAA;AACJ,EAAA,MAAM,SAAsD,EAAC;AAC7D,EAAA,MAAM,QAAyB,EAAC;AAEhC,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,CAAA,IAAK,OAAA,EAAS;AACnC,IAAA,MAAM,MAAA,GAAS,gBAAgB,IAAI,CAAA;AACnC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,QAAQ,OAAO,IAAA;AAAM,MACnB,KAAK,MAAA;AACH,QAAA,IAAA,GAAO,KAAA;AACP,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,MAAA,GAAS,KAAA;AACT,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,MAAA,GAAS,KAAA;AACT,QAAA;AAAA,MACF,KAAK,OAAA;AACH,QAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG;AACnB,UAAA,MAAM,EAAE,KAAA,EAAO,GAAG,IAAA,EAAK,GAAI,KAAA;AAC3B,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,OAAO,EAAE,GAAG,IAAA,EAAM,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,YAChC,KAAA,EAAO,OAAO,KAAA,KAAU,QAAA,IAAY,OAAO,QAAA,CAAS,KAAK,IAAI,KAAA,GAAQ;AAAA,WACtE,CAAA;AAAA,QACH;AACA,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,EAAE,GAAG,KAAA,EAAO,EAAA,EAAI,MAAA,CAAO,EAAA,EAAqB,CAAA;AAC5E,QAAA;AAAA;AACJ,EACF;AACA,EAAA,IAAI,CAAC,QAAA,CAAS,IAAI,CAAA,EAAG,OAAO,IAAA;AAE5B,EAAA,MAAA,CAAO,KAAK,iBAAiB,CAAA;AAC7B,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AACvC,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,MAAW,EAAE,KAAA,EAAO,KAAA,EAAM,IAAK,MAAA,EAAQ;AACrC,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,QAAA,CAAS,KAAK,IAAI,KAAA,GAAQ,IAAA;AAC5C,IAAA,MAAA,CAAO,GAAA,CAAI,KAAA,CAAM,EAAA,EAAI,EAAE,CAAA;AACvB,IAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAA,GAAK,CAAC,CAAA;AAAA,EAC9B;AACA,EAAA,KAAA,CAAM,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,OAAA,GAAU,EAAE,OAAO,CAAA;AAE1C,EAAA,OAAO;AAAA,IACL,GAAA,EAAK;AAAA,MACH,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,QAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AAAA,MACjC,KAAA;AAAA,MACA,MAAA;AAAA,MACA,cAAA,EAAgB;AAAA,KAClB;AAAA,IACA;AAAA,GACF;AACF;AAIO,SAAS,iBAAA,CACd,GACA,CAAA,EACQ;AACR,EAAA,IAAI,CAAA,CAAE,UAAU,CAAA,CAAE,KAAA,SAAc,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAA,GAAQ,EAAA,GAAK,CAAA;AACzD,EAAA,OAAO,CAAA,CAAE,KAAA,CAAM,EAAA,GAAK,CAAA,CAAE,KAAA,CAAM,EAAA,GAAK,EAAA,GAAK,CAAA,CAAE,KAAA,CAAM,EAAA,GAAK,CAAA,CAAE,KAAA,CAAM,KAAK,CAAA,GAAI,CAAA;AACtE;;;ACzGA,IAAM,OAAA,0BAAiB,SAAS,CAAA;AAGzB,SAAS,kBACd,OAAA,EACkB;AAClB,EAAA,MAAM,EAAE,OAAA,EAAS,MAAA,EAAO,GAAI,OAAA;AAC5B,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,GAAA;AACzC,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,GAAA;AACvC,EAAA,MAAM,SAAS,IAAI,GAAA,CAAqB,OAAA,CAAQ,OAAA,IAAW,EAAE,CAAA;AAC7D,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAqB;AACxC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAuC;AAC7D,EAAA,IAAI,QAAA,GAAW,QAAQ,QAAA,IAAY,IAAA;AACnC,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,IAAI,KAAA,GAA8C,IAAA;AAClD,EAAA,IAAI,aAAA,GAA+B,IAAA;AAEnC,EAAA,MAAM,IAAA,GAAO,CAAC,OAAA,KAAkC;AAC9C,IAAA,KAAA,MAAW,YAAY,CAAC,GAAG,SAAS,CAAA,WAAY,OAAO,CAAA;AAAA,EACzD,CAAA;AAEA,EAAA,MAAM,WAAW,MAAY;AAC3B,IAAA,IAAI,CAAC,YAAY,MAAA,EAAQ;AACzB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,aAAA,KAAkB,GAAA;AAClB,IAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAC7B,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAI,UAAA,EAAY,aAAA,GAAgB,SAAA,GAAY,GAAG,CAAC,CAAA;AAC9E,IAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,MAAA,KAAA,GAAQ,IAAA;AACR,MAAA,KAAK,KAAA,EAAM;AAAA,IACb,GAAG,IAAI,CAAA;AAAA,EACT,CAAA;AAEA,EAAA,eAAe,KAAA,GAAuB;AACpC,IAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAC7B,IAAA,KAAA,GAAQ,IAAA;AACR,IAAA,aAAA,GAAgB,IAAA;AAChB,IAAA,IAAI,CAAC,QAAA,IAAY,MAAA,CAAO,IAAA,KAAS,CAAA,EAAG;AACpC,IAAA,MAAM,KAAA,GAAQ,CAAC,GAAG,MAAM,CAAA;AACxB,IAAA,MAAA,CAAO,KAAA,EAAM;AACb,IAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,MAAM,GAAA,CAAI,OAAO,CAAC,IAAA,EAAM,KAAK,CAAA,KAAM;AACjC,QAAA,MAAM,MAAM,MAAA,GAAS,IAAA;AACrB,QAAA,IAAI;AACF,UAAA,IAAI,KAAA,KAAU,OAAA,EAAS,MAAM,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,eAC1C,MAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,QACnC,SAAS,KAAA,EAAO;AACd,UAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,0BAAA,EAA6B,GAAG,CAAA,uBAAA,CAAA,EAA2B,KAAK,CAAA;AAAA,QAC/E;AAAA,MACF,CAAC;AAAA,KACH;AAAA,EACF;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,OAAA,KAAmC;AACtD,IAAA,MAAM,UAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,OAAA,EAAS;AAClC,MAAA,IAAI,CAAC,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC7B,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA;AACpC,MAAA,IAAI,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA,EAAG;AACtB,MAAA,IAAI,UAAU,MAAA,EAAW;AACvB,QAAA,IAAI,CAAC,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA,EAAG;AACvB,QAAA,MAAA,CAAO,OAAO,IAAI,CAAA;AAAA,MACpB,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,MACxB;AACA,MAAA,OAAA,CAAQ,KAAK,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,UAAU,CAAA;AAAA,IAChD;AACA,IAAA,IAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA;AAAA,EACtC,CAAA;AAEA,EAAA,MAAM,cAAc,QAAA,GAAW,OAAA,CAAQ,SAAA,GAAY,MAAA,EAAQ,WAAW,CAAA,GAAI,MAAA;AAE1E,EAAA,MAAM,aAAa,MAAY;AAC7B,IAAA,KAAK,KAAA,EAAM;AAAA,EACb,CAAA;AACA,EAAA,MAAM,eAAe,MAAY;AAC/B,IAAA,IAAI,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU,KAAK,KAAA,EAAM;AAAA,EACxD,CAAA;AACA,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,UAAU,CAAA;AAC9C,IAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,YAAY,CAAA;AAAA,EAC5D;AAEA,EAAA,MAAM,KAAA,GAAgE;AAAA,IACpE,MAAA;AAAA,IACA,IAAI,QAAA,GAAW;AACb,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IACA,GAAA,EAAK,CAAC,IAAA,KAAS,MAAA,CAAO,IAAI,IAAI,CAAA;AAAA,IAC9B,GAAA,EAAK,CAAC,IAAA,KAAS,MAAA,CAAO,IAAI,IAAI,CAAA;AAAA,IAC9B,SAAS,CAAC,UAAA,GAAa,EAAA,KAAO,CAAC,GAAG,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,IAAI,CAAA,KAAM,IAAA,CAAK,UAAA,CAAW,UAAU,CAAC,CAAA;AAAA,IACxF,GAAA,EAAK,CAAC,IAAA,EAAM,KAAA,KAAU;AACpB,MAAA,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AACtB,MAAA,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AACtB,MAAA,IAAA,CAAK,CAAC,EAAE,IAAA,EAAM,OAAO,MAAA,EAAQ,OAAA,EAAS,CAAC,CAAA;AACvC,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAAA,IACA,MAAA,EAAQ,CAAC,IAAA,KAAS;AAChB,MAAA,IAAI,CAAC,OAAO,GAAA,CAAI,IAAI,KAAK,CAAC,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA,EAAG;AAC5C,MAAA,MAAA,CAAO,OAAO,IAAI,CAAA;AAClB,MAAA,MAAA,CAAO,GAAA,CAAI,MAAM,OAAO,CAAA;AACxB,MAAA,IAAA,CAAK,CAAC,EAAE,IAAA,EAAM,KAAA,EAAO,QAAW,MAAA,EAAQ,OAAA,EAAS,CAAC,CAAA;AAClD,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,QAAA,KAAa;AACvB,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,MAAA,OAAO,MAAM;AACX,QAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,MAC3B,CAAA;AAAA,IACF,CAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAO,YAAY;AACjB,MAAA,IAAI,MAAA,EAAQ;AACZ,MAAA,MAAM,KAAA,EAAM;AACZ,MAAA,MAAA,GAAS,IAAA;AACT,MAAA,WAAA,IAAc;AACd,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,mBAAA,CAAoB,YAAY,UAAU,CAAA;AACjD,QAAA,QAAA,CAAS,mBAAA,CAAoB,oBAAoB,YAAY,CAAA;AAAA,MAC/D;AAAA,IACF,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,OAAA,KAAY;AACjB,MAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,SAAS,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IAC7D,CAAA;AAAA,IACA,aAAa,MAAM;AACjB,MAAA,QAAA,GAAW,KAAA;AACX,MAAA,MAAA,CAAO,KAAA,EAAM;AACb,MAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAC7B,MAAA,KAAA,GAAQ,IAAA;AACR,MAAA,WAAA,IAAc;AAAA,IAChB,CAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO,KAAA;AACT;AAKA,eAAsB,YAAY,OAAA,EAAwD;AAExF,EAAA,MAAM,QAAyB,EAAC;AAChC,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,SAAA,GAAY,OAAA,CAAQ,MAAA,EAAQ,CAAC,CAAA,KAAM,KAAA,CAAM,IAAA,CAAK,GAAG,CAAC,CAAC,CAAA;AACrF,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA,EACpD,SAAS,KAAA,EAAO;AACd,IAAA,SAAA,IAAY;AACZ,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,CAAA,yBAAA,EAA4B,QAAQ,MAAM,CAAA,mCAAA,CAAA;AAAA,MAC1C;AAAA,KACF;AACA,IAAA,OAAO,kBAAkB,EAAE,GAAG,OAAA,EAAS,QAAA,EAAU,OAAO,CAAA;AAAA,EAC1D;AACA,EAAA,MAAM,QAAQ,iBAAA,CAAkB;AAAA,IAC9B,GAAG,OAAA;AAAA,IACH,SAAS,MAAA,CAAO,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAyB;AAAA,MACvD,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA;AAAA,MAC/B;AAAA,KACD;AAAA,GACF,CAAA;AACD,EAAA,SAAA,IAAY;AACZ,EAAC,KAAA,CAAmE,YAAY,KAAK,CAAA;AACrF,EAAA,OAAO,KAAA;AACT;AC9MA,SAAS,UAAa,IAAA,EAAsC;AAC1D,EAAA,MAAM,SAAS,IAAA,EAAK;AACpB,EAAA,OAAO,kBAAkB,OAAA,GACrB,EAAE,KAAA,EAAO,IAAA,EAAM,SAAS,MAAA,EAAQ,MAAA,EAAQ,CAAA,EAAE,GAC1C,EAAE,KAAA,EAAO,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,QAAQ,CAAA,EAAE;AAChD;AAQO,SAAS,YAAgC,IAAA,EAAsC;AACpF,EAAA,MAAM,KAAA,GAAQ,OAAwB,IAAI,CAAA;AAC1C,EAAA,IAAI,MAAM,OAAA,KAAY,IAAA,EAAM,KAAA,CAAM,OAAA,GAAU,UAAU,IAAI,CAAA;AAC1D,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,IAAI,QAAA,CAAmB,KAAA,CAAM,QAAQ,KAAK,CAAA;AAEhE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA;AAChB,IAAA,CAAA,CAAE,MAAA,IAAU,CAAA;AACZ,IAAA,IAAI,IAAA,GAAO,IAAA;AACX,IAAA,KAAK,CAAA,CAAE,OAAA,EAAS,IAAA,CAAK,CAAC,MAAA,KAAW;AAC/B,MAAA,CAAA,CAAE,KAAA,GAAQ,MAAA;AACV,MAAA,IAAI,IAAA,WAAe,MAAM,CAAA;AAAA,IAC3B,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,GAAO,KAAA;AACP,MAAA,CAAA,CAAE,MAAA,IAAU,CAAA;AACZ,MAAA,cAAA,CAAe,MAAM;AACnB,QAAA,IAAI,CAAA,CAAE,SAAS,CAAA,EAAG;AAClB,QAAA,IAAI,CAAA,CAAE,KAAA,EAAO,KAAK,CAAA,CAAE,MAAM,KAAA,EAAM;AAAA;AAE9B,UAAA,KAAK,CAAA,CAAE,OAAA,EAAS,IAAA,CAAK,CAAC,MAAA,KAAW;AAC/B,YAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,EAAG,KAAK,OAAO,KAAA,EAAM;AAAA,UACxC,CAAC,CAAA;AAAA,MACL,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,oBAAA,CAAqB,WAAmB,KAAA,EAAsC;AAC5F,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,OAAO,KAAK,CAAA;AAE3B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,IAAgB,OAAO,OAAA,EAAS;AAC7D,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,EAAG;AACpC,MAAA,IAAI,MAAA,CAAO,GAAG,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG,KAAA,CAAM,GAAG,CAAC,CAAA,EAAG;AAC/C,MAAA,MAAA,CAAO,OAAA,GAAU,IAAA;AACjB,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA,SAAA,EAAY,SAAS,CAAA,SAAA,EAAY,GAAG,CAAA,4CAAA;AAAA,OACtC;AACA,MAAA;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AACH;AC9DO,IAAM,kBAAA,GAAqB,cAAkC,IAAI;AAgBjE,SAAS,YAAY,EAAE,UAAA,EAAY,SAAS,QAAA,GAAW,IAAA,EAAM,UAAS,EAAqB;AAChG,EAAA,oBAAA,CAAqB,eAAA,EAAiB,EAAE,UAAA,EAAY,OAAA,EAAS,CAAA;AAC7D,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IAAY,YAC1B,WAAA,CAAY,EAAE,OAAA,EAAS,OAAA,IAAY,MAAM,cAAA,EAAe,EAAI,MAAA,EAAQ,SAAA,CAAU,UAAU,CAAA,EAAG;AAAA,GAC7F;AACA,EAAA,IAAI,CAAC,OAAA,EAAS,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,QAAA,EAAS,CAAA;AACjC,EAAA,2BAAQ,kBAAA,CAAmB,QAAA,EAAnB,EAA4B,KAAA,EAAO,SAAU,QAAA,EAAS,CAAA;AAChE;ACFO,SAAS,iBAAA,CACd,IAAA,EACA,OAAA,EACA,OAAA,GAAiC,EAAC,EACA;AAClC,EAAA,MAAM,OAAA,GAAU,WAAW,kBAAkB,CAAA;AAC7C,EAAA,MAAM,OAAA,GAAU,WAAW,cAAc,CAAA;AACzC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAS,OAAO,CAAA;AAE1C,EAAA,MAAM,MAAA,GACJ,OAAA,IAAW,IAAA,IAAQ,IAAA,GAAO,WAAA,CAAY,OAAA,CAAQ,KAAA,KAAU,KAAA,GAAQ,IAAA,GAAO,OAAA,EAAS,IAAI,CAAA,GAAI,IAAA;AAE1F,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,MAAA,KACC,OAAA,IAAW,SACP,OAAA,CAAQ,SAAA,CAAU,CAAC,OAAA,KAAY;AAC7B,MAAA,IAAI,OAAA,CAAQ,KAAK,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,MAAM,GAAG,MAAA,EAAO;AAAA,IACrD,CAAC,IACD,MAAM;AAAA,IAAC,CAAA;AAAA,IACb,CAAC,SAAS,MAAM;AAAA,GAClB;AACA,EAAA,MAAM,IAAA,GAAO,MACX,OAAA,IAAW,MAAA,IAAU,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA,GAAK,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA,GAAU,KAAA;AAC1E,EAAA,MAAM,SAAA,GAAY,oBAAA,CAAqB,SAAA,EAAW,IAAA,EAAM,IAAI,CAAA;AAE5D,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,CAAC,IAAA,KAAS;AACR,MAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AACzB,MAAA,MAAM,IAAA,GAAO,QAAQ,GAAA,CAAI,MAAM,IAAK,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA,GAAU,KAAA;AAChE,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,OAAO,IAAA,KAAS,aAAc,IAAA,CAAqB,IAAI,IAAI,IAAI,CAAA;AAAA,IACrF,CAAA;AAAA,IACA,CAAC,OAAA,EAAS,MAAA,EAAQ,KAAK;AAAA,GACzB;AAEA,EAAA,OAAO,OAAA,IAAW,SAAS,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,CAAC,OAAO,QAAQ,CAAA;AACzE","file":"chunk-PMLZP7RT.js","sourcesContent":["import type { InstrumentSerializers, SerializedTrial, TrialRecord, UndoStack } from './types';\n\n/** The storage key a lab writes one of its buckets under. Namespaced by\n * `storageKey` so two labs sharing an origin do not collide. */\nexport function labStorageKey(\n storageKey: string,\n bucket: 'workspaces' | 'saves' | 'theme' | 'layout',\n): string {\n return `lk:${storageKey}:${bucket}`;\n}\n\n/** Encode a string for the URL fragment. */\nexport function encodeUrlHash(value: string): string {\n return btoa(encodeURIComponent(value));\n}\n\n/** Decode a URL fragment written by `encodeUrlHash`, or `null` if it is\n * malformed. */\nexport function decodeUrlHash(hash: string): string | null {\n if (!hash) return null;\n try {\n return decodeURIComponent(atob(hash));\n } catch {\n return null;\n }\n}\n\n/**\n * An id for a trial or a snapshot.\n *\n * `crypto.randomUUID` exists only in a secure context, and a LAN address is\n * not one — so a lab opened on a phone or a tablet by IP throws on its first\n * render and shows a blank page with nothing in reach to say why. The counter\n * is per page load, which is all these ids have to survive: they name records\n * inside one session's store, and a saved snapshot carries its own name.\n */\nlet idCounter = 0;\nexport function newId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c && typeof c.randomUUID === 'function') return c.randomUUID();\n return `lk-${Date.now().toString(36)}-${idCounter++}`;\n}\n\n/** A fresh, empty undo history. */\nexport function emptyUndoStack(): UndoStack {\n return { past: [], future: [] };\n}\n\n/** Serialize trials for storage, running each instrument's own serializer\n * over its state. Undo history is deliberately dropped — it does not survive\n * a reload. */\nexport function serializeTrials(\n trials: TrialRecord[],\n serializers: InstrumentSerializers,\n): SerializedTrial[] {\n return trials.map(({ undoStack: _undo, ...w }) => {\n const s = serializers[w.instrumentName];\n return { ...w, state: s?.serialize ? s.serialize(w.state) : w.state };\n });\n}\n\n/** Rebuild trials from storage, running each instrument's deserializer\n * over its state and starting each with an empty undo history. Returns an\n * empty list rather than throwing on malformed input. */\nexport function deserializeTrials(\n records: SerializedTrial[],\n deserializers: InstrumentSerializers,\n): TrialRecord[] {\n if (!Array.isArray(records)) {\n console.warn('[labkit] deserializeTrials: not an array, returning empty list');\n return [];\n }\n return records.map((r) => {\n const d = deserializers[r.instrumentName];\n return {\n ...r,\n state: d?.deserialize ? d.deserialize(r.state, r.config) : r.state,\n undoStack: emptyUndoStack(),\n };\n });\n}\n","import { decodeUrlHash, encodeUrlHash } from './helpers';\nimport type { StorageAdapter, StorageChange } from './types';\n\n/** JSON-backed substrates held raw strings before records existed — a theme\n * bucket stored `dark`, not `\"dark\"` — so an unparseable value comes back as\n * the string it is. */\nfunction parse(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** Whether `JSON.stringify` would keep `value` exactly. An object key holding\n * `undefined` counts as kept: JSON drops the key, and reading back an absent\n * key yields the same `undefined`. */\nfunction isJsonSafe(value: unknown, seen: Set<object> = new Set()): boolean {\n if (value === null) return true;\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return true;\n case 'number':\n return Number.isFinite(value);\n case 'object': {\n if (seen.has(value)) return false;\n seen.add(value);\n const proto = Object.getPrototypeOf(value);\n const ok = Array.isArray(value)\n ? value.every((v) => isJsonSafe(v, seen))\n : (proto === Object.prototype || proto === null) &&\n Object.values(value).every((v) => v === undefined || isJsonSafe(v, seen));\n seen.delete(value);\n return ok;\n }\n default:\n return false;\n }\n}\n\nfunction webStorageAdapter(area: () => Storage, name: string, crossTab: boolean): StorageAdapter {\n const warned = new Set<string>();\n const adapter: StorageAdapter = {\n get: async (key) => {\n const raw = area().getItem(key);\n return raw === null ? undefined : parse(raw);\n },\n list: async (prefix) => {\n const store = area();\n const out: [string, unknown][] = [];\n for (let i = 0; i < store.length; i++) {\n const key = store.key(i);\n if (key === null || !key.startsWith(prefix)) continue;\n const raw = store.getItem(key);\n if (raw !== null) out.push([key, parse(raw)]);\n }\n return out;\n },\n set: async (key, value) => {\n if (!warned.has(key) && !isJsonSafe(value)) {\n warned.add(key);\n console.warn(\n `[labkit] \"${key}\" holds a value JSON cannot keep exactly; ${name} stores what JSON keeps. Use IndexedDB for binary or non-JSON state.`,\n );\n }\n area().setItem(key, JSON.stringify(value));\n },\n delete: async (key) => {\n area().removeItem(key);\n },\n };\n if (crossTab) {\n adapter.subscribe = (prefix, on) => {\n if (typeof window === 'undefined') return () => {};\n const handler = (e: StorageEvent): void => {\n if (e.key === null || !e.key.startsWith(prefix)) return;\n if (e.storageArea !== area()) return;\n on([[e.key, e.newValue === null ? undefined : parse(e.newValue)]]);\n };\n window.addEventListener('storage', handler);\n return () => window.removeEventListener('storage', handler);\n };\n }\n return adapter;\n}\n\n/** Persist to `localStorage`, one JSON value per record. Survives a reload and\n * reaches other tabs through the `storage` event. */\nexport const localStorageAdapter: StorageAdapter = webStorageAdapter(\n () => localStorage,\n 'localStorage',\n true,\n);\n\n/** Persist to `sessionStorage` — survives a reload but not a new tab. */\nexport const sessionStorageAdapter: StorageAdapter = webStorageAdapter(\n () => sessionStorage,\n 'sessionStorage',\n false,\n);\n\nconst URL_HASH_GUARD = typeof window !== 'undefined';\n\nfunction readHashMap(): Record<string, string> {\n if (!URL_HASH_GUARD) return {};\n const raw = decodeUrlHash(window.location.hash.replace(/^#/, ''));\n if (!raw) return {};\n try {\n return JSON.parse(raw) as Record<string, string>;\n } catch {\n return {};\n }\n}\n\n/** What each live subscription last saw, refreshed on this page's own writes\n * so `hashchange` reports only somebody else's. */\nconst hashSubscribers = new Set<{ last: Record<string, string> }>();\n\nfunction writeHashMap(map: Record<string, string>): void {\n if (!URL_HASH_GUARD) return;\n const encoded = encodeUrlHash(JSON.stringify(map));\n window.history.replaceState(null, '', `#${encoded}`);\n for (const sub of hashSubscribers) sub.last = { ...map };\n}\n\n/** Persist into the URL fragment, so the page's link carries its state and can\n * be shared or bookmarked. Changes to the fragment made elsewhere — a pasted\n * link, back and forward — arrive through `hashchange`. */\nexport const urlHashAdapter: StorageAdapter = {\n get: async (key) => {\n const raw = readHashMap()[key];\n return raw === undefined ? undefined : parse(raw);\n },\n list: async (prefix) =>\n Object.entries(readHashMap())\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, raw]): [string, unknown] => [key, parse(raw)]),\n set: async (key, value) => {\n const map = readHashMap();\n map[key] = JSON.stringify(value);\n writeHashMap(map);\n },\n delete: async (key) => {\n const map = readHashMap();\n delete map[key];\n writeHashMap(map);\n },\n subscribe: (prefix, on) => {\n if (!URL_HASH_GUARD) return () => {};\n const sub = { last: readHashMap() };\n hashSubscribers.add(sub);\n const handler = (): void => {\n const next = readHashMap();\n const changes: StorageChange[] = [];\n for (const key of new Set([...Object.keys(sub.last), ...Object.keys(next)])) {\n if (!key.startsWith(prefix) || sub.last[key] === next[key]) continue;\n const raw = next[key];\n changes.push([key, raw === undefined ? undefined : parse(raw)]);\n }\n sub.last = next;\n if (changes.length > 0) on(changes);\n };\n window.addEventListener('hashchange', handler);\n return () => {\n hashSubscribers.delete(sub);\n window.removeEventListener('hashchange', handler);\n };\n },\n};\n\ninterface MemorySubscriber {\n owner: StorageAdapter;\n prefix: string;\n on: (changes: StorageChange[]) => void;\n}\n\nconst memorySubscribers = new WeakMap<Map<string, unknown>, Set<MemorySubscriber>>();\n\n/** An in-memory store, discarded on reload. Values are copied in and out, as a\n * real substrate would. Adapters built over one shared `backing` hear each\n * other's writes — two tabs, for a test. */\nexport function createMemoryAdapter(backing: Map<string, unknown> = new Map()): StorageAdapter {\n let subscribers = memorySubscribers.get(backing);\n if (!subscribers) {\n subscribers = new Set();\n memorySubscribers.set(backing, subscribers);\n }\n const peers = subscribers;\n const notify = (key: string, value: unknown): void => {\n for (const sub of peers) {\n if (sub.owner === adapter || !key.startsWith(sub.prefix)) continue;\n const copy = value === undefined ? undefined : structuredClone(value);\n queueMicrotask(() => sub.on([[key, copy]]));\n }\n };\n const adapter: StorageAdapter = {\n get: async (key) => (backing.has(key) ? structuredClone(backing.get(key)) : undefined),\n list: async (prefix) =>\n [...backing]\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, value]): [string, unknown] => [key, structuredClone(value)]),\n set: async (key, value) => {\n const copy = structuredClone(value);\n backing.set(key, copy);\n notify(key, copy);\n },\n delete: async (key) => {\n backing.delete(key);\n notify(key, undefined);\n },\n subscribe: (prefix, on) => {\n const sub = { owner: adapter, prefix, on };\n peers.add(sub);\n return () => {\n peers.delete(sub);\n };\n },\n };\n return adapter;\n}\n\n/** Persists nothing and reads back nothing. */\nexport const noneAdapter: StorageAdapter = {\n get: async () => undefined,\n list: async () => [],\n set: async () => {},\n delete: async () => {},\n};\n\n/** Options for `createIndexedDbAdapter`. */\nexport interface IndexedDbAdapterOptions {\n /** Default `'labkit'`. */\n database?: string;\n /** Default `'records'`. One object store per database: a store added to a\n * database that already exists is not created. */\n store?: string;\n}\n\ninterface NodeChannel extends BroadcastChannel {\n unref?: () => void;\n}\n\n/** Persist to IndexedDB, which stores structured-clone values natively — typed\n * arrays, `Map`s, `Blob`s — and holds far more than localStorage. Other tabs\n * hear writes through a `BroadcastChannel`. The database opens on first use. */\nexport function createIndexedDbAdapter({\n database = 'labkit',\n store = 'records',\n}: IndexedDbAdapterOptions = {}): StorageAdapter {\n let opened: Promise<IDBDatabase> | null = null;\n const open = (): Promise<IDBDatabase> => {\n opened ??= new Promise((resolve, reject) => {\n if (typeof indexedDB === 'undefined') {\n reject(new Error('[labkit] IndexedDB is not available here'));\n return;\n }\n const request = indexedDB.open(database, 1);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(store)) {\n request.result.createObjectStore(store);\n }\n };\n request.onsuccess = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(store)) {\n reject(new Error(`[labkit] IndexedDB database \"${database}\" has no store \"${store}\"`));\n return;\n }\n resolve(db);\n };\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(`[labkit] IndexedDB \"${database}\" is blocked`));\n });\n return opened;\n };\n\n const run = <T>(mode: IDBTransactionMode, body: (s: IDBObjectStore) => () => T): Promise<T> =>\n open().then(\n (db) =>\n new Promise<T>((resolve, reject) => {\n const tx = db.transaction(store, mode);\n const result = body(tx.objectStore(store));\n tx.oncomplete = () => resolve(result());\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error);\n }),\n );\n\n let channel: NodeChannel | null = null;\n const getChannel = (): NodeChannel | null => {\n if (typeof BroadcastChannel === 'undefined') return null;\n if (!channel) {\n channel = new BroadcastChannel(`labkit:${database}:${store}`) as NodeChannel;\n // Node keeps its event loop alive for an open channel; a browser has no unref.\n channel.unref?.();\n }\n return channel;\n };\n const announce = (changes: StorageChange[]): void => {\n getChannel()?.postMessage(changes);\n };\n\n const adapter: StorageAdapter = {\n get: (key) =>\n run('readonly', (s) => {\n const request = s.get(key);\n return () => request.result as unknown;\n }),\n list: (prefix) =>\n run('readonly', (s) => {\n const range = IDBKeyRange.bound(prefix, `${prefix}￿`);\n const keys = s.getAllKeys(range);\n const values = s.getAll(range);\n return () =>\n keys.result.map((key, i): [string, unknown] => [String(key), values.result[i]]);\n }),\n set: async (key, value) => {\n await run('readwrite', (s) => {\n s.put(value, key);\n return () => undefined;\n });\n announce([[key, value]]);\n },\n delete: async (key) => {\n await run('readwrite', (s) => {\n s.delete(key);\n return () => undefined;\n });\n announce([[key, undefined]]);\n },\n };\n if (typeof BroadcastChannel !== 'undefined') {\n adapter.subscribe = (prefix, on) => {\n const ch = getChannel();\n if (!ch) return () => {};\n const handler = (e: MessageEvent<StorageChange[]>): void => {\n const mine = e.data.filter(([key]) => key.startsWith(prefix));\n if (mine.length > 0) on(mine);\n };\n ch.addEventListener('message', handler);\n return () => ch.removeEventListener('message', handler);\n };\n }\n return adapter;\n}\n\n/** IndexedDB under the default database and store. */\nexport const indexedDbAdapter: StorageAdapter = createIndexedDbAdapter();\n\nlet resolvedDefault: Promise<StorageAdapter> | null = null;\n\n/** What a lab given only a `storageKey` persists to: IndexedDB, or\n * localStorage — with a warning — where IndexedDB will not open. */\nexport function defaultStorage(): Promise<StorageAdapter> {\n resolvedDefault ??= indexedDbAdapter.list('\u0000').then(\n () => indexedDbAdapter,\n (error) => {\n console.warn('[labkit] IndexedDB would not open; persisting to localStorage instead', error);\n return localStorageAdapter;\n },\n );\n return resolvedDefault;\n}\n\n/** Forget which default was chosen. Tests only. */\nexport function resetDefaultStorage(): void {\n resolvedDefault = null;\n}\n","import type { LabDocument, SavedSnapshot, SerializedTrial } from './types';\n\n/** Every record of the lab stored under `storageKey` starts with this. The key\n * is URI-encoded, so it holds no `:` and one lab's prefix can never reach into\n * another lab's records. */\nexport function labPrefix(storageKey: string): string {\n return `lk:${encodeURIComponent(storageKey)}:`;\n}\n\nexport const META_RECORD = 'meta';\nexport const LAYOUT_RECORD = 'layout';\nexport const UNDOCK_RECORD = 'undock';\n\nexport function trialRecord(id: string): string {\n return `trial:${encodeURIComponent(id)}`;\n}\n\nexport function saveRecord(id: string): string {\n return `save:${encodeURIComponent(id)}`;\n}\n\n/** The prefix every value scoped to one trial shares. */\nexport function trialValuesPrefix(trialId: string): string {\n return `value:trial:${encodeURIComponent(trialId)}:`;\n}\n\n/** Where a `usePersistedState` value lives: under its trial, or — with a null\n * trial — under the lab. */\nexport function valueRecord(trialId: string | null, name: string): string {\n const encoded = encodeURIComponent(name);\n return trialId === null ? `value:lab:${encoded}` : `${trialValuesPrefix(trialId)}${encoded}`;\n}\n\n/** What a record name addresses, or null for a name no lab writes. */\nexport type RecordName =\n | { kind: 'meta' | 'layout' | 'undock' | 'value' }\n | { kind: 'trial' | 'save'; id: string };\n\nexport function parseRecordName(name: string): RecordName | null {\n const parts = name.split(':');\n try {\n switch (parts[0]) {\n case 'meta':\n case 'layout':\n case 'undock':\n return parts.length === 1 ? { kind: parts[0] } : null;\n case 'trial':\n case 'save':\n return parts.length === 2 && parts[1]\n ? { kind: parts[0], id: decodeURIComponent(parts[1]) }\n : null;\n case 'value':\n return (parts[1] === 'lab' && parts.length === 3) ||\n (parts[1] === 'trial' && parts.length === 4)\n ? { kind: 'value' }\n : null;\n default:\n return null;\n }\n } catch {\n return null;\n }\n}\n\n/** The fields of the meta record. */\nexport interface LabMeta {\n version: number;\n mode: unknown;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** A document as the records a lab stores it in, `meta` last — so a write in\n * this order that stops partway leaves no `meta`, and the next open folds the\n * old storage again rather than trusting a partial set. */\nexport function recordsOfDocument(doc: LabDocument): [string, unknown][] {\n return [\n ...doc.trials.map((t, order): [string, unknown] => [trialRecord(t.id), { ...t, order }]),\n ...doc.saves.map((s): [string, unknown] => [saveRecord(s.id), s]),\n [LAYOUT_RECORD, doc.layout],\n [UNDOCK_RECORD, doc.undockedPanels],\n [META_RECORD, { version: doc.version, mode: doc.mode } satisfies LabMeta],\n ];\n}\n\n/** Records joined back into an unmigrated document, with each trial's stored\n * order. Null when there is no `meta` record — no lab has been stored here in\n * this form. */\nexport function documentOfRecords(\n entries: Iterable<[string, unknown]>,\n): { doc: Record<string, unknown>; orders: Map<string, number> } | null {\n let meta: unknown;\n let layout: unknown;\n let undock: unknown;\n const trials: { trial: SerializedTrial; order: number }[] = [];\n const saves: SavedSnapshot[] = [];\n\n for (const [name, value] of entries) {\n const parsed = parseRecordName(name);\n if (!parsed) continue;\n switch (parsed.kind) {\n case 'meta':\n meta = value;\n break;\n case 'layout':\n layout = value;\n break;\n case 'undock':\n undock = value;\n break;\n case 'trial':\n if (isObject(value)) {\n const { order, ...rest } = value;\n trials.push({\n trial: { ...rest, id: parsed.id } as SerializedTrial,\n order: typeof order === 'number' && Number.isFinite(order) ? order : Infinity,\n });\n }\n break;\n case 'save':\n if (isObject(value)) saves.push({ ...value, id: parsed.id } as SavedSnapshot);\n break;\n }\n }\n if (!isObject(meta)) return null;\n\n trials.sort(compareTrialOrder);\n const orders = new Map<string, number>();\n let next = 0;\n for (const { trial, order } of trials) {\n const at = Number.isFinite(order) ? order : next;\n orders.set(trial.id, at);\n next = Math.max(next, at + 1);\n }\n saves.sort((a, b) => a.savedAt - b.savedAt);\n\n return {\n doc: {\n version: meta.version,\n mode: meta.mode,\n trials: trials.map((t) => t.trial),\n saves,\n layout,\n undockedPanels: undock,\n },\n orders,\n };\n}\n\n/** Stored order first; two trials added at once in two tabs can share an\n * order, so the id breaks the tie the same way everywhere. */\nexport function compareTrialOrder(\n a: { trial: { id: string }; order: number },\n b: { trial: { id: string }; order: number },\n): number {\n if (a.order !== b.order) return a.order < b.order ? -1 : 1;\n return a.trial.id < b.trial.id ? -1 : a.trial.id > b.trial.id ? 1 : 0;\n}\n","import type { StorageAdapter, StorageChange } from './types';\n\n/** One record changing, as a cache listener hears it. `value` is `undefined`\n * for a delete. `local` changes were made through this cache; `remote` ones\n * came from another writer. */\nexport interface RecordChange {\n name: string;\n value: unknown;\n origin: 'local' | 'remote';\n}\n\n/**\n * Every record under one prefix, held in memory: the only thing that talks to\n * a `StorageAdapter`. Names are keys with the prefix removed.\n *\n * Writes land in memory at once and reach storage on a debounce. The newest\n * write to a record wins: another writer's change to a record with a write\n * still queued here is ignored, since the queued write lands after it.\n */\nexport interface RecordCache {\n readonly prefix: string;\n /** False once writing is off — the records could not be read, or were\n * written by a newer labkit — so nothing here can overwrite them. */\n readonly writable: boolean;\n has(name: string): boolean;\n get(name: string): unknown;\n /** Every record whose name starts with `namePrefix`. */\n entries(namePrefix?: string): [string, unknown][];\n set(name: string, value: unknown): void;\n delete(name: string): void;\n subscribe(listener: (changes: RecordChange[]) => void): () => void;\n /** Send queued writes now. */\n flush(): Promise<void>;\n /** Send queued writes and stop hearing other writers. */\n close(): Promise<void>;\n}\n\n/** The parts of a cache only its opener uses. */\nexport interface OwnedRecordCache extends RecordCache {\n /** Hold these records without writing them back. */\n seed(entries: Iterable<[string, unknown]>): void;\n stopWriting(): void;\n}\n\n/** Options for `createRecordCache` and `openRecords`. */\nexport interface RecordCacheOptions {\n storage: StorageAdapter;\n prefix: string;\n /** Trailing debounce on writes. Default 300 ms. */\n debounceMs?: number;\n /** The longest a write waits under continuous change. Default 1000 ms. */\n maxWaitMs?: number;\n}\n\nconst DELETED = Symbol('deleted');\n\n/** A cache holding `initial`, with nothing read from storage. */\nexport function createRecordCache(\n options: RecordCacheOptions & { initial?: Iterable<[string, unknown]>; writable?: boolean },\n): OwnedRecordCache {\n const { storage, prefix } = options;\n const debounceMs = options.debounceMs ?? 300;\n const maxWaitMs = options.maxWaitMs ?? 1000;\n const values = new Map<string, unknown>(options.initial ?? []);\n const queued = new Map<string, unknown>();\n const listeners = new Set<(changes: RecordChange[]) => void>();\n let writable = options.writable ?? true;\n let closed = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let firstQueuedAt: number | null = null;\n\n const emit = (changes: RecordChange[]): void => {\n for (const listener of [...listeners]) listener(changes);\n };\n\n const schedule = (): void => {\n if (!writable || closed) return;\n const now = Date.now();\n firstQueuedAt ??= now;\n if (timer) clearTimeout(timer);\n const wait = Math.max(0, Math.min(debounceMs, firstQueuedAt + maxWaitMs - now));\n timer = setTimeout(() => {\n timer = null;\n void flush();\n }, wait);\n };\n\n async function flush(): Promise<void> {\n if (timer) clearTimeout(timer);\n timer = null;\n firstQueuedAt = null;\n if (!writable || queued.size === 0) return;\n const batch = [...queued];\n queued.clear();\n await Promise.all(\n batch.map(async ([name, value]) => {\n const key = prefix + name;\n try {\n if (value === DELETED) await storage.delete(key);\n else await storage.set(key, value);\n } catch (error) {\n console.warn(`[labkit] could not write \"${key}\"; keeping it in memory`, error);\n }\n }),\n );\n }\n\n const applyRemote = (changes: StorageChange[]): void => {\n const applied: RecordChange[] = [];\n for (const [key, value] of changes) {\n if (!key.startsWith(prefix)) continue;\n const name = key.slice(prefix.length);\n if (queued.has(name)) continue;\n if (value === undefined) {\n if (!values.has(name)) continue;\n values.delete(name);\n } else {\n values.set(name, value);\n }\n applied.push({ name, value, origin: 'remote' });\n }\n if (applied.length > 0) emit(applied);\n };\n\n const unsubscribe = writable ? storage.subscribe?.(prefix, applyRemote) : undefined;\n\n const onPageHide = (): void => {\n void flush();\n };\n const onVisibility = (): void => {\n if (document.visibilityState === 'hidden') void flush();\n };\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', onPageHide);\n document.addEventListener('visibilitychange', onVisibility);\n }\n\n const cache: OwnedRecordCache & { applyRemote: typeof applyRemote } = {\n prefix,\n get writable() {\n return writable;\n },\n has: (name) => values.has(name),\n get: (name) => values.get(name),\n entries: (namePrefix = '') => [...values].filter(([name]) => name.startsWith(namePrefix)),\n set: (name, value) => {\n values.set(name, value);\n queued.set(name, value);\n emit([{ name, value, origin: 'local' }]);\n schedule();\n },\n delete: (name) => {\n if (!values.has(name) && !queued.has(name)) return;\n values.delete(name);\n queued.set(name, DELETED);\n emit([{ name, value: undefined, origin: 'local' }]);\n schedule();\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n flush,\n close: async () => {\n if (closed) return;\n await flush();\n closed = true;\n unsubscribe?.();\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', onPageHide);\n document.removeEventListener('visibilitychange', onVisibility);\n }\n },\n seed: (entries) => {\n for (const [name, value] of entries) values.set(name, value);\n },\n stopWriting: () => {\n writable = false;\n queued.clear();\n if (timer) clearTimeout(timer);\n timer = null;\n unsubscribe?.();\n },\n applyRemote,\n };\n return cache;\n}\n\n/** Read every record under `prefix` and hold them. A failed read opens the\n * cache empty with writing off, so it never overwrites what it could not\n * read. */\nexport async function openRecords(options: RecordCacheOptions): Promise<OwnedRecordCache> {\n // Listen before listing, so a change landing between the two is not lost.\n const early: StorageChange[] = [];\n const stopEarly = options.storage.subscribe?.(options.prefix, (c) => early.push(...c));\n let listed: [string, unknown][];\n try {\n listed = await options.storage.list(options.prefix);\n } catch (error) {\n stopEarly?.();\n console.warn(\n `[labkit] could not read \"${options.prefix}\"; opening empty and not persisting`,\n error,\n );\n return createRecordCache({ ...options, writable: false });\n }\n const cache = createRecordCache({\n ...options,\n initial: listed.map(([key, value]): [string, unknown] => [\n key.slice(options.prefix.length),\n value,\n ]),\n });\n stopEarly?.();\n (cache as unknown as { applyRemote: (c: StorageChange[]) => void }).applyRemote(early);\n return cache;\n}\n","import { useEffect, useRef, useState } from 'react';\n\ninterface Closable {\n close(): Promise<void> | void;\n}\n\ninterface Entry<T> {\n value: T | null;\n pending: Promise<T> | null;\n mounts: number;\n}\n\nfunction openEntry<T>(open: () => T | Promise<T>): Entry<T> {\n const result = open();\n return result instanceof Promise\n ? { value: null, pending: result, mounts: 0 }\n : { value: result, pending: null, mounts: 0 };\n}\n\n/**\n * Open something once for a component's lifetime: at once when `open` returns\n * a value, after it settles when it returns a promise. Closed on unmount — but\n * the close waits a microtask, and the remount React StrictMode performs right\n * after its rehearsal unmount cancels it, so StrictMode opens it once.\n */\nexport function useOpenOnce<T extends Closable>(open: () => T | Promise<T>): T | null {\n const entry = useRef<Entry<T> | null>(null);\n if (entry.current === null) entry.current = openEntry(open);\n const [value, setValue] = useState<T | null>(entry.current.value);\n\n useEffect(() => {\n const e = entry.current as Entry<T>;\n e.mounts += 1;\n let live = true;\n void e.pending?.then((opened) => {\n e.value = opened;\n if (live) setValue(opened);\n });\n return () => {\n live = false;\n e.mounts -= 1;\n queueMicrotask(() => {\n if (e.mounts > 0) return;\n if (e.value) void e.value.close();\n else\n void e.pending?.then((opened) => {\n if (e.mounts === 0) void opened.close();\n });\n });\n };\n }, []);\n\n return value;\n}\n\n/** Warn once, in development, when a prop read only at mount changes after it. */\nexport function useWarnIgnoredChange(component: string, props: Record<string, unknown>): void {\n const first = useRef(props);\n const warned = useRef(false);\n // In an effect, so only props that committed are compared.\n useEffect(() => {\n if (process.env.NODE_ENV === 'production' || warned.current) return;\n for (const key of Object.keys(props)) {\n if (Object.is(first.current[key], props[key])) continue;\n warned.current = true;\n console.warn(\n `[labkit] ${component} reads \\`${key}\\` once, at mount; a later change is ignored`,\n );\n return;\n }\n });\n}\n","import { createContext, type ReactNode } from 'react';\nimport { defaultStorage } from './adapters';\nimport { labPrefix } from './labRecords';\nimport { openRecords, type RecordCache } from './records';\nimport type { StorageAdapter } from './types';\nimport { useOpenOnce, useWarnIgnoredChange } from './useOpenOnce';\n\n/** The records `usePersistedState` reads and writes. `<Lab>`,\n * `SingletonExperimentProvider` and `<Persistence>` provide one. */\nexport const PersistenceContext = createContext<RecordCache | null>(null);\n\n/** Props for `<Persistence>`. */\nexport interface PersistenceProps {\n /** Names the records, as `<Lab storageKey>` does. A lab and a\n * `<Persistence>` given the same key share lab-scoped values. */\n storageKey: string;\n /** Default: IndexedDB, or localStorage where IndexedDB will not open. */\n storage?: StorageAdapter;\n /** Rendered until the records have loaded. Default: nothing. */\n fallback?: ReactNode;\n children: ReactNode;\n}\n\n/** Persisted values for a page with no lab: `usePersistedState` below it keeps\n * what it holds across a reload. Both props are read once, at mount. */\nexport function Persistence({ storageKey, storage, fallback = null, children }: PersistenceProps) {\n useWarnIgnoredChange('<Persistence>', { storageKey, storage });\n const records = useOpenOnce(async () =>\n openRecords({ storage: storage ?? (await defaultStorage()), prefix: labPrefix(storageKey) }),\n );\n if (!records) return <>{fallback}</>;\n return <PersistenceContext.Provider value={records}>{children}</PersistenceContext.Provider>;\n}\n","import {\n type Dispatch,\n type SetStateAction,\n useCallback,\n useContext,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { TrialIdContext } from './context';\nimport { valueRecord } from './labRecords';\nimport { PersistenceContext } from './Persistence';\n\n/** Options for `usePersistedState`. */\nexport interface PersistedStateOptions {\n /** `'trial'` — the default inside a trial — keeps a value per trial, and\n * closing the trial deletes it. `'lab'` shares one value across the lab. */\n scope?: 'trial' | 'lab';\n}\n\n/**\n * `useState` whose value survives a reload: a widget's open tab, a panel's\n * position. Inside a `<Lab>`, `SingletonExperimentProvider` or `<Persistence>`\n * the value is a record, loaded before first render and re-rendered when\n * another tab changes it. With no provider above it, or a null `name`, it is\n * plain `useState` — so a component can offer optional persistence and still\n * mount anywhere.\n *\n * Values must be structured-clone values; the JSON-backed adapters warn about\n * one JSON would change. Snapshots do not include them.\n */\nexport function usePersistedState<T>(\n name: string | null | undefined,\n initial: T | (() => T),\n options: PersistedStateOptions = {},\n): [T, Dispatch<SetStateAction<T>>] {\n const records = useContext(PersistenceContext);\n const trialId = useContext(TrialIdContext);\n const [local, setLocal] = useState(initial);\n\n const record =\n records && name != null ? valueRecord(options.scope === 'lab' ? null : trialId, name) : null;\n\n const subscribe = useCallback(\n (notify: () => void) =>\n records && record\n ? records.subscribe((changes) => {\n if (changes.some((c) => c.name === record)) notify();\n })\n : () => {},\n [records, record],\n );\n const read = (): T =>\n records && record && records.has(record) ? (records.get(record) as T) : local;\n const persisted = useSyncExternalStore(subscribe, read, read);\n\n const setPersisted = useCallback<Dispatch<SetStateAction<T>>>(\n (next) => {\n if (!records || !record) return;\n const prev = records.has(record) ? (records.get(record) as T) : local;\n records.set(record, typeof next === 'function' ? (next as (p: T) => T)(prev) : next);\n },\n [records, record, local],\n );\n\n return records && record ? [persisted, setPersisted] : [local, setLocal];\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/undo/eventBus.ts","../src/undo/undoStack.ts"],"names":[],"mappings":";AAYO,SAAS,cAAA,GAA2B;AACzC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgC;AACtD,EAAA,OAAO;AAAA,IACL,EAAA,CAAG,OAAO,QAAA,EAAU;AAClB,MAAA,IAAI,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AAC7B,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,GAAA,uBAAU,GAAA,EAAI;AACd,QAAA,SAAA,CAAU,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA,MAC1B;AACA,MAAA,GAAA,CAAI,IAAI,QAAQ,CAAA;AAChB,MAAA,OAAO,MAAM;AACX,QAAA,GAAA,EAAK,OAAO,QAAQ,CAAA;AAAA,MACtB,CAAA;AAAA,IACF,CAAA;AAAA,IACA,KAAK,KAAA,EAAO;AACV,MAAA,MAAM,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AAC/B,MAAA,IAAI,CAAC,GAAA,EAAK;AACV,MAAA,KAAA,MAAW,EAAA,IAAM,CAAC,GAAG,GAAG,GAAG,EAAA,EAAG;AAAA,IAChC,CAAA;AAAA,IACA,KAAA,GAAQ;AACN,MAAA,SAAA,CAAU,KAAA,EAAM;AAAA,IAClB;AAAA,GACF;AACF;;;AC3BO,SAAS,UAAA,GAAwB;AACtC,EAAA,OAAO,EAAE,IAAA,EAAM,EAAC,EAAG,MAAA,EAAQ,EAAC,EAAE;AAChC;AAIO,SAAS,YAAA,CAAa,KAAA,EAAkB,QAAA,EAAmB,QAAA,EAA6B;AAC7F,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,MAAA,IAAU,QAAA,GAAW,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,KAAA,EAAM;AACpF,EAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,EAAC,EAAE;AAC5B;AAKO,SAAS,IAAA,CACd,OACA,OAAA,EACgD;AAChD,EAAA,IAAI,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACpC,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAM;AAC9B,EAAA,MAAM,QAAA,GAAW,KAAK,GAAA,EAAI;AAC1B,EAAA,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,CAAC,GAAG,KAAA,CAAM,MAAA,EAAQ,OAAO,CAAA,EAAE,EAAG,QAAA,EAAS;AACzE;AAIO,SAAS,IAAA,CACd,OACA,OAAA,EACgD;AAChD,EAAA,IAAI,KAAA,CAAM,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACtC,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,MAAA,CAAO,KAAA,EAAM;AAClC,EAAA,MAAM,QAAA,GAAW,OAAO,GAAA,EAAI;AAC5B,EAAA,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,CAAC,GAAG,KAAA,CAAM,IAAA,EAAM,OAAO,CAAA,EAAG,MAAA,EAAO,EAAG,QAAA,EAAS;AACvE;AAGO,SAAS,UAAU,MAAA,EAA8B;AACtD,EAAA,OAAO,UAAA,EAAW;AACpB","file":"chunk-RL2LOLNI.js","sourcesContent":["/** A subscriber to a named event. */\nexport type EventListener = () => void;\n\n/** Named-event pub/sub, the channel an instrument's `emit` writes to and undo\n * snapshotting listens on. */\nexport interface EventBus {\n on(event: string, listener: EventListener): () => void;\n emit(event: string): void;\n clear(): void;\n}\n\n/** Build an empty event bus. */\nexport function createEventBus(): EventBus {\n const listeners = new Map<string, Set<EventListener>>();\n return {\n on(event, listener) {\n let set = listeners.get(event);\n if (!set) {\n set = new Set();\n listeners.set(event, set);\n }\n set.add(listener);\n return () => {\n set?.delete(listener);\n };\n },\n emit(event) {\n const set = listeners.get(event);\n if (!set) return;\n for (const fn of [...set]) fn();\n },\n clear() {\n listeners.clear();\n },\n };\n}\n","/** Undo history as two stacks of state snapshots: what has been done, and\n * what has been undone. The present state is held elsewhere, not here. */\nexport interface UndoStack {\n past: unknown[];\n future: unknown[];\n}\n\n/** A fresh, empty history. */\nexport function emptyStack(): UndoStack {\n return { past: [], future: [] };\n}\n\n/** Record a new snapshot, discarding the redo branch and the oldest entry\n * once `maxDepth` is reached. */\nexport function pushSnapshot(stack: UndoStack, snapshot: unknown, maxDepth: number): UndoStack {\n const past = stack.past.length >= maxDepth ? stack.past.slice(1) : stack.past.slice();\n past.push(snapshot);\n return { past, future: [] };\n}\n\n/** Step back one snapshot, returning the new history and the state to restore.\n * `null` when there is nothing to undo. The caller supplies `current` so it\n * can be redone. */\nexport function undo(\n stack: UndoStack,\n current: unknown,\n): { stack: UndoStack; snapshot: unknown } | null {\n if (stack.past.length === 0) return null;\n const past = stack.past.slice();\n const snapshot = past.pop();\n return { stack: { past, future: [...stack.future, current] }, snapshot };\n}\n\n/** Step forward one snapshot, returning the new history and the state to\n * restore. `null` when there is nothing to redo. */\nexport function redo(\n stack: UndoStack,\n current: unknown,\n): { stack: UndoStack; snapshot: unknown } | null {\n if (stack.future.length === 0) return null;\n const future = stack.future.slice();\n const snapshot = future.pop();\n return { stack: { past: [...stack.past, current], future }, snapshot };\n}\n\n/** Discard the whole history. */\nexport function clearUndo(_stack: UndoStack): UndoStack {\n return emptyStack();\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/canvas/LinkedCursor.tsx","../src/canvas/useLayerScheduler.ts","../src/canvas/CanvasStack.tsx","../src/canvas/Stage.tsx"],"names":["useRef","useEffect","jsx","jsxs","useMemo","useLatest"],"mappings":"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","file":"chunk-T6DLGEJB.js","sourcesContent":["import { crosshairRects, usePointerPosition } from '@weasel-js/core';\nimport { useContext } from 'react';\nimport { CameraContext, pointerElsewhere, STAGE_VIEW_ID } from './CameraInput';\n\n/** `crosshairRects` returns its four arms in this order. */\nconst ARMS = ['left', 'right', 'up', 'down'] as const;\n\n/**\n * The other side of a linked cursor: a crosshair on the camera's own element\n * wherever the shared pointer is over some other view — an overview, say. The\n * same crosshair core paints on a weasel canvas.\n */\nexport function LinkedCursor() {\n const camera = useContext(CameraContext);\n const pointer = usePointerPosition();\n if (!camera) return null;\n const local = pointerElsewhere(pointer, STAGE_VIEW_ID, camera.frame);\n if (!local) return null;\n // `pointerElsewhere` hands back the instrument's world; the camera works\n // frame-local, and the map is its own inverse.\n const v = camera.view.get();\n const x = (local.x - v.x) * v.scale.x;\n const y = (local.y * camera.frame.yDir - v.y) * v.scale.y;\n const { halo, bars } = crosshairRects(x, y, 1);\n const arms = ARMS.map((name, i) => ({ name, halo: halo[i], bar: bars[i] }));\n return (\n <svg className=\"lk-linked-cursor\" aria-hidden=\"true\">\n {arms.map(({ name, halo: r }) =>\n r ? (\n <rect\n key={`halo-${name}`}\n className=\"lk-linked-cursor__halo\"\n x={r.x}\n y={r.y}\n width={r.w}\n height={r.h}\n />\n ) : null,\n )}\n {arms.map(({ name, bar: r }) =>\n r ? (\n <rect\n key={`bar-${name}`}\n className=\"lk-linked-cursor__bar\"\n x={r.x}\n y={r.y}\n width={r.w}\n height={r.h}\n />\n ) : null,\n )}\n </svg>\n );\n}\n","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 type { Yoke } from '@weasel-js/core';\nimport { useLatest } from '@weasel-js/core';\nimport { type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport type { Point, ViewTransform } from '../instrument/types';\nimport { normalize2DView } from '../state/view';\nimport { CameraContext, CameraInput, CameraScope, useCameraView } from './CameraInput';\nimport { CanvasStackContext } from './CanvasStackContext';\nimport { LinkedCursor } from './LinkedCursor';\nimport { type CanvasLayerDescriptor, useLayerScheduler } from './useLayerScheduler';\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 /** A yoke to join, from `useYoke()`: cameras on one yoke share their tool\n * and gesture in flight. Omitted, this one keeps its own. */\n yoke?: Yoke;\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 and device pixel ratio; pan and zoom route through weasel's\n * gesture dispatcher (`<CameraInput>`). */\nexport function CanvasStack({\n layers,\n view: viewProp,\n onViewChange,\n worldSpec,\n minZoom,\n maxZoom,\n width = '100%',\n height = '100%',\n className,\n onResize,\n onHitTest,\n children,\n yoke,\n}: CanvasStackProps) {\n const view = normalize2DView(viewProp);\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 = useLatest(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, onResizeRef]);\n\n const frame = useMemo(() => resolveFrame(worldSpec, size), [worldSpec, size]);\n\n const camera = useCameraView({\n view: viewProp,\n onViewChange,\n frame,\n hostRef: containerRef,\n minZoom,\n maxZoom,\n });\n const cameraCtx = useMemo(\n () => ({ view: camera, frame, element: () => containerRef.current }),\n [camera, frame],\n );\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, worldSpec },\n }),\n [view, frame, size, layers, worldSpec],\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 <CameraScope yoke={yoke}>\n <CameraContext.Provider value={cameraCtx}>\n <CanvasStackContext.Provider value={ctxValue}>\n <CameraInput hostRef={containerRef} camera={camera} frame={frame} onTap={onHitTest} />\n <div\n ref={containerRef}\n className={className ? `lk-canvas-stack ${className}` : 'lk-canvas-stack'}\n style={containerStyle}\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 <LinkedCursor />\n <div className=\"lk-canvas-stack__overlay\">{children}</div>\n </div>\n </CanvasStackContext.Provider>\n </CameraContext.Provider>\n </CameraScope>\n );\n}\n","import type { Yoke } from '@weasel-js/core';\nimport { useLatest } from '@weasel-js/core';\nimport { type CSSProperties, type ReactNode, useEffect, useMemo, useRef } from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { normalize2DView } from '../state/view';\nimport { useSurfaceOptional } from '../surface/useSurfaceTile';\nimport { CameraContext, CameraInput, CameraScope, useCameraView } from './CameraInput';\nimport { LinkedCursor } from './LinkedCursor';\nimport { DEFAULT_FRAME, type ViewportSize } from './worldSpec';\n\n/** Props for `<Stage>`. */\nexport interface StageProps {\n /** The content's own size in CSS pixels, at zoom 1. */\n size: ViewportSize;\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n minZoom?: number;\n maxZoom?: number;\n /** Fired whenever the stage's viewport is measured at a non-zero size. */\n onResize?: (viewport: ViewportSize) => void;\n /** Handed the viewport element, for a consumer tracking the pointer over it. */\n hostRef?: { current: HTMLDivElement | null };\n /** Drawn over the content in viewport pixels, outside the camera. */\n overlay?: ReactNode;\n children?: ReactNode;\n /** A yoke to join, from `useYoke()`: cameras on one yoke share their tool\n * and gesture in flight. Omitted, this one keeps its own. */\n yoke?: Yoke;\n}\n\n/**\n * The camera a stage opens at: the content centered in its viewport, shrunk to\n * fit if it has to be and never enlarged past its own size.\n */\nexport function fitStage(size: ViewportSize, viewport: ViewportSize): ViewTransform {\n if (!(size.width > 0 && size.height > 0 && viewport.width > 0 && viewport.height > 0)) {\n return { zoom: 1, pan: { x: 0, y: 0 } };\n }\n const zoom = Math.min(1, viewport.width / size.width, viewport.height / size.height);\n return {\n zoom,\n pan: {\n x: (viewport.width - size.width * zoom) / 2,\n y: (viewport.height - size.height * zoom) / 2,\n },\n };\n}\n\n/**\n * DOM content of a fixed size, panned and zoomed by a camera — what\n * `<CanvasStack>` is for layers drawn to a canvas. Pan and zoom route through\n * weasel's gesture dispatcher (`<CameraInput>`). The content is laid out at\n * its own size and transformed, so everything inside keeps its layout and a\n * measurement of any element in it reports where it is actually drawn.\n */\nexport function Stage({\n size,\n view: viewProp,\n onViewChange,\n minZoom,\n maxZoom,\n onResize,\n hostRef,\n overlay,\n children,\n yoke,\n}: StageProps) {\n const view = normalize2DView(viewProp);\n const host = useRef<HTMLDivElement | null>(null);\n const surface = useSurfaceOptional();\n const camera = useCameraView({\n view: viewProp,\n onViewChange,\n frame: DEFAULT_FRAME,\n hostRef: host,\n minZoom,\n maxZoom,\n });\n const cameraCtx = useMemo(\n () => ({\n view: camera,\n frame: DEFAULT_FRAME,\n element: () => host.current,\n content: { x: 0, y: 0, width: size.width, height: size.height },\n }),\n [camera, size.width, size.height],\n );\n\n const onResizeRef = useLatest(onResize);\n useEffect(() => {\n const el = host.current;\n if (!el) return;\n const measure = () => {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n onResizeRef.current?.({ width: rect.width, height: rect.height });\n }\n };\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(measure);\n ro.observe(el);\n return () => ro.disconnect();\n }, [onResizeRef]);\n\n // A transform moves every tile inside the content without resizing anything,\n // and a ResizeObserver reports only resizes.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the camera is the trigger, not an input — the effect re-measures whenever it moves.\n useEffect(() => {\n surface?.invalidateRects();\n }, [surface, view.zoom, view.pan.x, view.pan.y]);\n\n // The camera changes every frame of a pan, and a transform has nowhere to\n // live but the element's own style: set as custom properties the stylesheet\n // reads.\n const cameraVars = {\n ['--lk-stage-x' as string]: `${view.pan.x}px`,\n ['--lk-stage-y' as string]: `${view.pan.y}px`,\n ['--lk-stage-zoom' as string]: String(view.zoom),\n ['--lk-stage-w' as string]: `${size.width}px`,\n ['--lk-stage-h' as string]: `${size.height}px`,\n } as CSSProperties;\n\n return (\n <CameraScope yoke={yoke}>\n <CameraContext.Provider value={cameraCtx}>\n <CameraInput hostRef={host} camera={camera} frame={DEFAULT_FRAME} />\n <div\n ref={(el) => {\n host.current = el;\n if (hostRef) hostRef.current = el;\n }}\n className=\"lk-stage\"\n >\n <div className=\"lk-stage__content\" style={cameraVars}>\n {children}\n </div>\n <LinkedCursor />\n {overlay}\n </div>\n </CameraContext.Provider>\n </CameraScope>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/surface/SurfaceContext.ts","../src/surface/useSurfaceTile.ts"],"names":[],"mappings":";;;AAIO,IAAM,cAAA,GAAiB,cAAoC,IAAI;AAwBtE,IAAM,WAAA,GAA+B,EAAE,IAAA,EAAM,IAAA,EAAM,OAAO,IAAA,EAAK;AAGxD,IAAM,oBAAA,GAAuB,cAA+B,WAAW;ACvBvE,SAAS,gBAAA,CAAiB,QAAsB,MAAA,EAAkC;AACvF,EAAA,OAAO,UAAA,CAAW,oBAAoB,CAAA,CAAE,KAAK,CAAA;AAC/C;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","file":"chunk-WIG6XHJ7.js","sourcesContent":["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/**\n * Which of the two stacked buffers a tenant paints into.\n *\n * `'over'` sits above the trial DOM: right for marks that annotate an\n * instrument, which is what the surface was built for and so the default.\n * `'under'` sits below it, which is what an opaque renderer needs — a tile that\n * fills its pane hides anything the pane's own DOM draws, so a 3D viewport on\n * the over-buffer has to paint its own chrome into the backend rather than\n * putting a label or a button in the pane.\n *\n * Both exist for the lifetime of the surface and share one set of tile rects,\n * so a lab can hold an annotated 2D trial and an opaque 3D one at once.\n */\nexport type SurfaceLayer = 'over' | 'under';\n\n/** The two buffers a surface owner publishes. A member is null until the\n * owner's canvas mounts, and for an owner that keeps its surface unpainted. */\nexport interface SurfaceCanvases {\n over: HTMLCanvasElement | null;\n under: HTMLCanvasElement | null;\n}\n\nconst NO_CANVASES: SurfaceCanvases = { over: null, under: null };\n\n/** The buffers the surface's tiles paint into. */\nexport const SurfaceCanvasContext = createContext<SurfaceCanvases>(NO_CANVASES);\n","import { useCallback, useContext } from 'react';\nimport { TrialIdContext } from '../state/context';\nimport { SurfaceCanvasContext, SurfaceContext, type SurfaceLayer } from './SurfaceContext';\nimport type { SurfaceHandle } from './useTiledSurface';\n\n/** One of the surface's two shared buffers, or null where nothing paints one.\n * What a tile hands to `paintInto`. See {@link SurfaceLayer} for which to ask\n * for; the default is the one over the trial DOM. */\nexport function useSurfaceCanvas(layer: SurfaceLayer = 'over'): HTMLCanvasElement | null {\n return useContext(SurfaceCanvasContext)[layer];\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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/controls/inDialog.tsx","../src/controls/ControlPanel.tsx"],"names":["auto","c","jsx","Fragment","b","ae","value","l","h","L","fe"],"mappings":"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","file":"chunk-XSJJTXNA.js","sourcesContent":["import { DialogRow } from '@weasel-js/ui';\nimport type { ControlRenderer, InDialogOptions } from '../config/types';\n\nexport type { InDialogOptions };\n\n/** The one-line summary a dialog row shows when it is given none. */\nexport function summarizeValue(value: unknown): string {\n if (Array.isArray(value)) return value.length === 0 ? 'None' : value.join(', ');\n if (value === undefined || value === null || value === '') return 'None';\n return typeof value === 'object' ? JSON.stringify(value) : String(value);\n}\n\n/**\n * Moves a control into a modal: the row becomes a button showing a summary of\n * the value, and the button opens a dialog whose body is `body`, handed the\n * same context a row renderer gets. For a leaf whose editor will never fit a\n * sidebar row.\n */\nexport function inDialog(body: ControlRenderer, opts: InDialogOptions = {}): ControlRenderer {\n const summary = opts.summary ?? summarizeValue;\n return (ctx) => {\n const { pref, value, auto, setAuto } = ctx;\n const manual = (pref as { manual?: boolean }).manual === true;\n return (\n <DialogRow\n label={pref.name}\n description={pref.description}\n summary={summary(value)}\n title={opts.title}\n readout={auto ? 'auto' : undefined}\n auto={auto}\n onAutoChange={manual ? undefined : setAuto}\n >\n {() => body(ctx)}\n </DialogRow>\n );\n };\n}\n","import { getAlpha01, isBuiltinToolPref, toHex8, withAlpha01 } from '@weasel-js/core';\nimport {\n Button,\n DialogRow,\n isPrefLeaf,\n ListEditor,\n type PrefGroup,\n type PrefLeaf,\n type PropertyAlign,\n PropertyControl,\n type PropertyControlProps,\n type PropertyDensity,\n PropertyField,\n PropertyGroup,\n PropertyList,\n type PropertyListPack,\n PropertyPanel,\n PropertyRow,\n type PropertyRowLayout,\n prefFieldProps,\n ResetIcon,\n type StanceProps,\n} from '@weasel-js/ui';\nimport { Fragment, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport { auto as autoValue } from '../config/auto';\nimport { resolveAutoConfig } from '../config/autoConfig';\nimport { fromConfigFields } from '../config/fromConfigField';\nimport { schemaNodeAtPath, valueAtPath } from '../config/path';\nimport type { ControlRenderer, ResolvedConfig, SectionSpec } from '../config/types';\nimport { isLeafVisible } from '../config/visible';\nimport { inDialog, summarizeValue } from './inDialog';\nimport type { ConfigField } from './types';\n\n/** How a panel packs its rows into the two-column property grid.\n * - `'auto'`: narrow controls (numbers, checkboxes, dropdowns, colors) sit\n * two per row; the ones that need the width — text, sliders, segmented\n * toggles — span it.\n * - `'pairs'`: every row pairs. A control that needs the full width says so\n * itself, with `<PropertyRow span>`.\n * - `'one-up'`: one control per row, colors excepted.\n */\nexport type ControlPack = 'auto' | 'pairs' | 'one-up';\n\n/** How the rows under one heading are laid out: the panel's, or a section's own. */\ninterface Rows {\n pack: ControlPack;\n layout: PropertyRowLayout | undefined;\n grid: PropertyListPack;\n}\n\nconst gridOf = (pack: ControlPack): PropertyListPack =>\n pack === 'one-up' ? 'auto-color' : 'pairs';\n\nfunction sectionRows(section: SectionSpec, rows: Rows): Rows {\n return {\n pack: section.pack ?? rows.pack,\n layout: section.layout ?? rows.layout,\n grid: section.pack ? gridOf(section.pack) : rows.grid,\n };\n}\n\nexport interface ControlPanelProps<TC extends Record<string, unknown>> extends StanceProps {\n /** The instrument's resolved config schema. */\n schema?: ResolvedConfig;\n /** @deprecated Pass `schema`. A field list is adapted into one internally. */\n fields?: ConfigField[];\n config: TC;\n /** Writes one value. The path is dotted for a leaf inside an `f.group`, and\n * the bare key for one at the root. */\n setConfig: (path: string, value: unknown) => void;\n /**\n * Control overrides and app-defined kinds, PrefsForm-style. Keys are config\n * paths (checked first) or leaf kinds. A renderer returning `null` collapses\n * the row; entries override the built-in rows on collision.\n */\n renderers?: Record<string, ControlRenderer>;\n /** How rows pack into the two-column grid. Defaults to `'pairs'`. */\n pack?: ControlPack;\n /**\n * Where a row's label sits relative to its control. Unset takes each row's\n * own orientation — `block` (label above, which is what leaves a paired row\n * room for its value) for most, `inline` for colors and checkboxes.\n */\n layout?: PropertyRowLayout;\n /** Room the rows get. Passed straight to the property list. */\n density?: PropertyDensity;\n /** Cross-axis alignment of an inline row's label and control. */\n align?: PropertyAlign;\n /**\n * Fold each section away behind a twisty, starting `'open'` or `'closed'`.\n * Unset draws the heading alone, as the panel always has — unless a section\n * in the schema declares how it opens, which makes that section foldable on\n * its own and outranks this for that one section.\n */\n collapse?: 'open' | 'closed';\n /**\n * Which sections are folded, keyed as `onCollapse` reports them. Given, the\n * panel keeps no state of its own: every toggle arrives at `onCollapse`\n * instead, which is where a lab that remembers a trial's sections writes\n * them.\n */\n collapsed?: Readonly<Record<string, boolean>>;\n /** A fold moved. The key is a section's label — prefixed by its group's\n * dotted path when the section sits inside one — or a group's own path. */\n onCollapse?: (key: string, collapsed: boolean) => void;\n /** Dotted paths currently unpinned. A row in this set hides its control and\n * reads out `auto`, and clicking its label writes the sentinel back through\n * `setConfig`. Omitted, the panel keeps the set itself: the labels still\n * toggle and the sentinel never reaches `setConfig`, which would otherwise\n * store it as the row's value. */\n auto?: ReadonlySet<string>;\n /** Draw leaves marked `hidden`. */\n showHidden?: boolean;\n /** Heads the panel. With `title`, `stance` or `tone` given, the rows sit in a\n * `<PropertyPanel>` carrying them; with none, they are the bare list. */\n title?: ReactNode;\n className?: string;\n}\n\nconst NO_AUTO: ReadonlySet<string> = new Set();\n\n/** Every leaf beneath a group, as its dotted path and its default. */\nfunction leavesUnder(group: PrefGroup, at: string): [string, unknown][] {\n return Object.entries(group.children).flatMap(([key, child]) => {\n const path = at === '' ? key : `${at}.${key}`;\n return isPrefLeaf(child)\n ? [[path, child.default] as [string, unknown]]\n : leavesUnder(child, path);\n });\n}\n\n/** The heading button of a `.resettable()` group: writes each changed leaf's\n * default, and is disabled while nothing has changed. */\nfunction ResetGroup({\n group,\n path,\n config,\n setConfig,\n}: {\n group: PrefGroup;\n path: string;\n config: Record<string, unknown>;\n setConfig: (path: string, value: unknown) => void;\n}) {\n const changed = leavesUnder(group, path).filter(\n ([p, d]) => !Object.is(valueAtPath(config, p), d),\n );\n return (\n <Button\n iconOnly\n variant=\"ghost\"\n size=\"sm\"\n ariaLabel={`Reset ${group.name}`}\n tooltip={`Reset ${group.name} to defaults`}\n disabled={changed.length === 0}\n onClick={() => {\n for (const [p, d] of changed) setConfig(p, d);\n }}\n >\n <ResetIcon />\n </Button>\n );\n}\n\n/** Render an instrument's config schema as a stack of controls, each writing\n * back through `setConfig`. Built on the property rows, so a lab's controls\n * are the same aligned, themed rows the rest of the kit uses. */\nexport function ControlPanel<TC extends Record<string, unknown>>({\n schema,\n fields,\n config,\n setConfig,\n renderers,\n pack = 'pairs',\n layout,\n density,\n align,\n collapse,\n collapsed,\n onCollapse,\n auto: given,\n showHidden = false,\n title,\n stance,\n tone,\n className,\n}: ControlPanelProps<TC>) {\n const resolved = useMemo(() => schema ?? fromConfigFields(fields ?? []), [schema, fields]);\n\n // An owner that tracks the set decides what a dot means; one that does not\n // gets a panel that decides for itself, because the alternative is handing it\n // a sentinel it would write into the config as though it were a value.\n const [ownAuto, setOwnAuto] = useState<ReadonlySet<string>>(NO_AUTO);\n const auto = given ?? ownAuto;\n const setRowAuto = (path: string, next: boolean, value: unknown): void => {\n if (given) {\n setConfig(path, next ? autoValue : value);\n return;\n }\n setOwnAuto((prev) => {\n const now = new Set(prev);\n if (next) now.add(path);\n else now.delete(path);\n return now;\n });\n // Pinning keeps whatever the row was showing, the same value the controlled\n // path writes back.\n if (!next) setConfig(path, value);\n };\n\n // What the instrument is reading. A row draws and reports from this rather\n // than calling its own resolver against the raw config, which would disagree\n // with the instrument whenever a resolver depends on another auto path.\n const shown = useMemo(\n () => (auto && auto.size > 0 ? resolveAutoConfig(resolved, config, auto) : config),\n [resolved, config, auto],\n );\n\n const gridPack = gridOf(pack);\n // A section that declares how it opens is foldable whether or not the lab\n // asked for folds — there is nothing else for the declaration to mean.\n const folds =\n collapse !== undefined ||\n collapsed !== undefined ||\n onCollapse !== undefined ||\n resolved.sections.some((s) => s.collapsed !== undefined);\n const startsFolded = collapse === 'closed';\n\n const fold = (key: string, declared?: boolean, grid: PropertyListPack = gridPack) => ({\n pack: grid,\n collapsible: folds,\n defaultCollapsed: declared ?? startsFolded,\n collapsed: collapsed ? (collapsed[key] ?? declared ?? startsFolded) : undefined,\n onCollapsedChange: onCollapse ? (next: boolean) => onCollapse(key, next) : undefined,\n });\n\n /** One node, which is either a group to recurse into or a row to draw. */\n const node = (path: string, rows: Rows): ReactNode => {\n const found = schemaNodeAtPath(resolved.group, path);\n if (!found) return null;\n if (!isLeafVisible(resolved, path, config as Record<string, unknown>, showHidden)) return null;\n if (isPrefLeaf(found)) {\n return (\n <ControlRow\n key={path}\n path={path}\n leaf={found}\n resolved={resolved}\n config={config}\n shown={shown}\n setConfig={setConfig}\n renderers={renderers}\n pack={rows.pack}\n layout={rows.layout}\n auto={auto}\n setRowAuto={setRowAuto}\n />\n );\n }\n // A group with no name organizes without heading it — core's rule for an\n // empty `PrefGroup.name` — so it contributes its rows and no chrome.\n if (found.name === '') return <Fragment key={path}>{body(found, path, rows)}</Fragment>;\n return (\n <PropertyGroup\n key={path}\n title={found.name}\n description={found.description}\n // A nested group is a block of rows, not a control: half of a\n // two-column grid leaves it laying its own rows out inside a cell,\n // which overlaps them.\n span\n actions={\n resolved.resettable?.has(path) ? (\n <ResetGroup\n group={found}\n path={path}\n config={config as Record<string, unknown>}\n setConfig={setConfig}\n />\n ) : undefined\n }\n {...fold(path, undefined, rows.grid)}\n >\n {body(found, path, rows)}\n </PropertyGroup>\n );\n };\n\n /**\n * The leaf at `path` if it may share a row with its neighbours: `pair` is\n * presentational, so a leaf whose control the lab draws itself, or one whose\n * control needs the whole row, keeps its own row instead.\n */\n const pairable = (path: string): { leaf: PrefLeaf; pair: string } | undefined => {\n const found = schemaNodeAtPath(resolved.group, path);\n if (!found || !isPrefLeaf(found)) return undefined;\n const pair = extra<string>(found, 'pair');\n if (pair === undefined) return undefined;\n if (!isLeafVisible(resolved, path, config as Record<string, unknown>, showHidden))\n return undefined;\n if (\n renderers?.[path] ??\n resolved.renderers[path] ??\n resolved.dialogs[path] ??\n renderers?.[found.kind]\n )\n return undefined;\n if (!isBuiltinToolPref(found)) return undefined;\n if (found.kind === 'paint' || found.kind === 'object' || isSliderLeaf(found)) return undefined;\n return { leaf: found, pair };\n };\n\n /** A run of sibling paths as rows, merging each run of adjacent leaves\n * sharing a `pair` id into one row named by the pair. */\n const rowsFor = (paths: readonly string[], rows: Rows): ReactNode[] => {\n const out: ReactNode[] = [];\n for (let i = 0; i < paths.length; ) {\n const head = pairable(paths[i]);\n if (!head) {\n out.push(node(paths[i], rows));\n i += 1;\n continue;\n }\n const { pair } = head;\n const cells = [{ path: paths[i], leaf: head.leaf }];\n let j = i + 1;\n for (; j < paths.length; j += 1) {\n const next = pairable(paths[j]);\n if (!next || next.pair !== pair) break;\n cells.push({ path: paths[j], leaf: next.leaf });\n }\n out.push(\n <PairedRow\n key={paths[i]}\n label={pair}\n cells={cells}\n shown={shown}\n setConfig={setConfig}\n layout={rows.layout}\n // A segmented cell carries every choice at once, so a pair holding\n // one needs the whole row the way a segmented row of its own does.\n span={cells.some(({ leaf }) => extra<string>(leaf, 'control') === 'radio')}\n />,\n );\n i = j;\n }\n return out;\n };\n\n /** One group's children: its loose nodes, then its sections. */\n const body = (group: PrefGroup, at: string, rows: Rows): ReactNode => {\n const sections = resolved.sections.filter((s) => s.at === at);\n const sectioned = new Set(sections.flatMap((s) => s.paths));\n const paths = Object.keys(group.children).map((key) => (at === '' ? key : `${at}.${key}`));\n return (\n <>\n {rowsFor(\n paths.filter((p) => !sectioned.has(p)),\n rows,\n )}\n {sections.map((section) => {\n const inner = sectionRows(section, rows);\n return (\n <PropertyGroup\n key={sectionKey(section)}\n title={section.label}\n {...fold(sectionKey(section), section.collapsed, inner.grid)}\n >\n {rowsFor(section.paths, inner)}\n </PropertyGroup>\n );\n })}\n </>\n );\n };\n\n const list = (\n <PropertyList\n pack={gridPack}\n density={density}\n align={align}\n className={className ? `lk-control-panel ${className}` : 'lk-control-panel'}\n >\n {body(resolved.group, '', { pack, layout, grid: gridPack })}\n </PropertyList>\n );\n if (title === undefined && stance === undefined && tone === undefined) return list;\n return (\n <PropertyPanel title={title} stance={stance} tone={tone} density={density}>\n {list}\n </PropertyPanel>\n );\n}\n\n/** How a section is keyed for folding: its label at the root, and its group's\n * path in front of it anywhere else, so two groups may both have an\n * `Advanced`. */\nfunction sectionKey(section: SectionSpec): string {\n return section.at === '' ? section.label : `${section.at}.${section.label}`;\n}\n\nexport interface ControlRowProps<TC extends Record<string, unknown>> {\n path: string;\n leaf: PrefLeaf;\n resolved: ResolvedConfig;\n config: TC;\n /** `config` with every auto path resolved -- what the instrument reads. */\n shown: TC;\n setConfig: (path: string, value: unknown) => void;\n renderers?: Record<string, ControlRenderer>;\n pack: ControlPack;\n layout?: PropertyRowLayout;\n auto?: ReadonlySet<string>;\n setRowAuto: (path: string, next: boolean, value: unknown) => void;\n}\n\n/** Whether this leaf draws as a slider, mirroring the condition the `number`\n * arm below branches on. A slider is the one control whose value cannot be\n * read off the control itself. */\nfunction isSliderLeaf(leaf: PrefLeaf): boolean {\n return (\n leaf.kind === 'number' &&\n extra<string>(leaf, 'control') === 'slider' &&\n extra<number>(leaf, 'min') !== undefined &&\n extra<number>(leaf, 'max') !== undefined\n );\n}\n\n/** Reads a labkit-only extra off a leaf. `PrefLeaf` has no field for these,\n * and extra keys survive the resolve pass at runtime. */\nfunction extra<T>(leaf: PrefLeaf, key: string): T | undefined {\n return (leaf as unknown as Record<string, T | undefined>)[key];\n}\n\n/** One leaf's row, as the panel draws it. Shared with `ControlMatrix`, whose\n * popover edits one cell with exactly this row. */\nexport function ControlRow<TC extends Record<string, unknown>>({\n path,\n leaf,\n resolved,\n config,\n shown,\n setConfig,\n renderers,\n pack,\n layout,\n auto,\n setRowAuto,\n}: ControlRowProps<TC>) {\n const write = (value: unknown): void => setConfig(path, value);\n const fallback = extra<unknown>(leaf, 'default');\n const pinned = valueAtPath(config, path) ?? fallback;\n\n const isAutoRow = auto?.has(path) ?? false;\n const canAuto = !extra<boolean>(leaf, 'manual');\n // Read out of the resolved config rather than re-running this leaf's own\n // resolver, so the row reports the value the instrument actually got — the\n // two diverge as soon as a resolver reads another auto path.\n const resolvedValue = isAutoRow ? valueAtPath(shown, path) : undefined;\n // An auto row draws what the resolver decided, not the value underneath it —\n // a handle sitting at the pinned number while the readout says something else\n // reads as a rendering bug. Pinning then keeps what you were looking at.\n const value = resolvedValue ?? pinned;\n const setAuto = (next: boolean): void => setRowAuto(path, next, value);\n const onAutoChange = canAuto ? setAuto : undefined;\n // An auto row hides its control, so the word is all the row has to say.\n const autoReadout = isAutoRow ? 'auto' : undefined;\n const autoProps = { auto: isAutoRow, onAutoChange };\n\n // Most specific wins, and within a tier the lab's entry beats the\n // instrument's: controls[path] -> node .render/.dialog -> controls[kind] -> built-in.\n const dialog = resolved.dialogs[path];\n const custom =\n renderers?.[path] ??\n resolved.renderers[path] ??\n (dialog ? inDialog(dialog.body, dialog) : undefined) ??\n renderers?.[leaf.kind];\n // A custom row places itself like any other; one that needs the full width\n // says so with `<PropertyRow span>`, which is the same opt-out a built-in has.\n if (custom) return custom({ path, pref: leaf, value, setValue: write, auto: isAutoRow, setAuto });\n\n const label = leaf.name;\n const description = leaf.description;\n // `auto` gives the whole width to the controls that read badly at half of a\n // sidebar's: free text, a slider track, a segmented toggle. `pairs` doesn't.\n const wide = pack === 'auto';\n const row = { label, description, layout, readout: autoReadout, ...autoProps };\n\n if (leaf.kind === 'list') {\n const entries = Array.isArray(value) ? (value as string[]) : [];\n return (\n <DialogRow\n label={label}\n summary={summarizeValue(entries)}\n readout={autoReadout}\n layout={layout}\n description={description}\n {...autoProps}\n >\n {() => (\n <ListEditor\n aria-label={label}\n value={entries}\n onChange={write}\n placeholder={extra<string>(leaf, 'placeholder')}\n />\n )}\n </DialogRow>\n );\n }\n\n const field = labField(leaf, value, write);\n if (field === null)\n return <UnwiredRow label={label} kind={leaf.kind} description={description} />;\n switch (field.kind) {\n case 'string':\n return <DebouncedTextRow leaf={leaf} field={field} span={wide} {...row} />;\n case 'number':\n return <PropertyField {...field} {...row} span={field.control === 'slider' && wide} />;\n case 'enum':\n return <PropertyField {...field} {...row} span={field.control === 'toggle' && wide} />;\n default:\n return <PropertyField {...field} {...row} />;\n }\n}\n\n/**\n * A leaf as the field it draws, with the labkit-only extras `prefFieldProps`\n * knows nothing of. `null` for a kind the panel declines: a paint, which a\n * hex swatch would flatten to a solid, and an object, which a flat row would\n * write one field of. Override with `render` to edit either.\n */\nfunction labField(\n leaf: PrefLeaf,\n value: unknown,\n write: (value: unknown) => void,\n): PropertyControlProps | null {\n if (!isBuiltinToolPref(leaf) || leaf.kind === 'paint' || leaf.kind === 'object') return null;\n const field = prefFieldProps(leaf, { value, setValue: write });\n if (field === null) return null;\n switch (field.kind) {\n case 'number':\n // `prefFieldProps` clamps what it stores, so an instrument is never\n // handed a value outside the range it asked for.\n return {\n ...field,\n control: isSliderLeaf(leaf) ? 'slider' : 'input',\n unit: extra<string>(leaf, 'suffix') ?? field.unit,\n };\n case 'boolean':\n return {\n ...field,\n control: extra<string>(leaf, 'control') === 'switch' ? 'switch' : 'checkbox',\n };\n case 'enum':\n // `.radio()` asks for every option at once, as segments.\n return {\n ...field,\n control: extra<string>(leaf, 'control') === 'radio' ? 'toggle' : 'select',\n };\n case 'string':\n return {\n ...field,\n placeholder: extra<string>(leaf, 'placeholder'),\n maxLength: extra<number>(leaf, 'maxLength'),\n };\n default:\n return field;\n }\n}\n\n/** A leaf this panel has no control for is named rather than dropped: a silent\n * gap reads as \"this control does not exist\". */\nfunction UnwiredRow({\n label,\n kind,\n description,\n}: {\n label: string;\n kind: string;\n description?: string;\n}) {\n return (\n <PropertyRow label={label} description={description}>\n <span className=\"lk-control-panel__unknown\">no control for “{kind}”</span>\n </PropertyRow>\n );\n}\n\n/** A text row whose writes are debounced — see {@link useDebouncedText}. */\nfunction DebouncedTextRow({\n leaf,\n field,\n ...row\n}: {\n leaf: PrefLeaf;\n field: PropertyControlProps & { kind: 'string' };\n label: string;\n layout?: PropertyRowLayout;\n span?: boolean;\n description?: string;\n readout?: ReactNode;\n auto?: boolean;\n onAutoChange?: (next: boolean) => void;\n}) {\n const text = useDebouncedText(\n field.value ?? '',\n field.onChange,\n extra<number>(leaf, 'debounceMs') ?? 150,\n );\n return <PropertyField {...field} {...row} value={text.local} onChange={text.type} />;\n}\n\n/** One cell of a paired row, with its path and the leaf it draws. */\ninterface PairCellSpec {\n path: string;\n leaf: PrefLeaf;\n}\n\n/**\n * Leaves sharing a `pair` id, side by side on one row the pair names — the\n * compact `X` / `Y` idiom `SelectionPanel` draws for the same annotation.\n *\n * The cells are bare controls: a row holds one label column, so each leaf's\n * own name stays with its control as the accessible name and its description\n * as a title. For the same reason the row's label does not toggle auto — it\n * names one path, and this row has several.\n */\nfunction PairedRow<TC extends Record<string, unknown>>({\n label,\n cells,\n shown,\n setConfig,\n layout,\n span,\n}: {\n label: string;\n cells: readonly PairCellSpec[];\n shown: TC;\n setConfig: (path: string, value: unknown) => void;\n layout?: PropertyRowLayout;\n span?: boolean;\n}) {\n return (\n <PropertyRow group span={span} label={label} layout={layout}>\n {cells.map(({ path, leaf }) => (\n // The row is named by the pair, so a cell that shares it says which\n // knob it is — without a caption a paired switch reads only to a\n // screen reader. A cell whose leaf is deliberately unnamed keeps none.\n <span key={path} className=\"lk-pair-cell\" title={leaf.description || undefined}>\n {leaf.name ? <span className=\"lk-pair-label\">{leaf.name}</span> : null}\n <PairCell\n leaf={leaf}\n value={valueAtPath(shown, path) ?? extra<unknown>(leaf, 'default')}\n write={(value) => setConfig(path, value)}\n />\n </span>\n ))}\n </PropertyRow>\n );\n}\n\n/** The control a paired cell holds, without the row chrome a whole row of its\n * own would bring. */\nfunction PairCell({\n leaf,\n value,\n write,\n}: {\n leaf: PrefLeaf;\n value: unknown;\n write: (value: unknown) => void;\n}) {\n const text = useDebouncedText(\n typeof value === 'string' ? value : '',\n write,\n extra<number>(leaf, 'debounceMs') ?? 150,\n );\n const field = labField(leaf, value, write);\n // Unreachable for null: `pairable` admits only kinds with a field.\n if (field === null) return null;\n switch (field.kind) {\n case 'string':\n return (\n <PropertyControl {...field} name={leaf.name} value={text.local} onChange={text.type} />\n );\n case 'number':\n // The pair names the row, so a cell has no room for a unit.\n return <PropertyControl {...field} name={leaf.name} control=\"input\" unit={undefined} />;\n case 'color': {\n // The alpha track needs a row of its own to sit under, so a paired\n // swatch edits the color and carries the stored alpha through untouched.\n const stored = typeof value === 'string' ? toHex8(value) : '#000000';\n return (\n <PropertyControl\n {...field}\n name={leaf.name}\n alpha={undefined}\n onChange={(rgb: string) =>\n write(field.alpha ? withAlpha01(rgb, getAlpha01(stored)) : rgb)\n }\n />\n );\n }\n default:\n return <PropertyControl {...field} name={leaf.name} />;\n }\n}\n\n/**\n * Live text held locally between debounced commits, so typing does not re-run\n * the instrument on every keystroke. Locally controlled between commits, which\n * means it has to notice the value changing underneath it.\n */\nfunction useDebouncedText(\n value: string,\n write: (value: string) => void,\n debounceMs: number,\n): { local: string; type: (next: string) => void } {\n const [local, setLocal] = useState(value);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastExternal = useRef(value);\n\n useEffect(() => {\n if (value !== lastExternal.current) {\n lastExternal.current = value;\n setLocal(value);\n }\n }, [value]);\n\n useEffect(\n () => () => {\n if (timer.current) clearTimeout(timer.current);\n },\n [],\n );\n\n return {\n local,\n type: (next) => {\n setLocal(next);\n if (timer.current) clearTimeout(timer.current);\n const commit = () => {\n lastExternal.current = next;\n write(next);\n };\n if (debounceMs === 0) commit();\n else timer.current = setTimeout(commit, debounceMs);\n },\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/builder.ts","../src/config/rules.ts","../src/config/resolve.ts","../src/config/useResolvedConfig.ts"],"names":[],"mappings":"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","file":"chunk-YCFKW5RX.js","sourcesContent":["import type { Display } from '@weasel-js/quantity';\nimport type { PrefLeaf, PrefNumberUnit } from '@weasel-js/ui';\nimport { type Auto, isAuto } from './auto';\nimport type {\n Annotations,\n BranchAnnotations,\n BranchOptions,\n ConfigBranch,\n ConfigEntry,\n ConfigNode,\n ConfigOption,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n InDialogOptions,\n InferConfig,\n NodeOptions,\n SectionOption,\n} from './types';\n\n/** Whether a schema entry is a branch rather than a leaf. */\nexport function isConfigBranch(entry: ConfigEntry): entry is ConfigBranch {\n return 'children' in entry;\n}\n\n/** Shared chaining surface. Every method clones, so a node can be reused as a\n * base for several leaves without one bleeding into the next. */\nexport abstract class BaseNode<T> implements ConfigNode<T> {\n abstract readonly kind: string | null;\n\n // `default` is a reserved word in a parameter position, so the field is\n // declared rather than taken as a parameter property.\n readonly default: T;\n\n constructor(\n defaultValue: T,\n readonly annotations: Readonly<Annotations> = {},\n readonly options: Readonly<NodeOptions> = {},\n ) {\n this.default = defaultValue;\n }\n\n /** Clone into the same subclass. Subclasses with extra constructor\n * arguments override this. */\n protected with(annotations: Annotations, options: NodeOptions): this {\n const Ctor = this.constructor as new (d: T, a: Annotations, o: NodeOptions) => this;\n return new Ctor(this.default, annotations, options);\n }\n\n protected ann(patch: Annotations): this {\n return this.with({ ...this.annotations, ...patch }, this.options);\n }\n\n protected opt(patch: NodeOptions): this {\n return this.with(this.annotations, { ...this.options, ...patch });\n }\n\n /** Human-readable label. Defaults to the key, title-cased. */\n label(name: string): this {\n return this.ann({ name });\n }\n\n /** Longer help text, surfaced as a tooltip on the row label. */\n describe(description: string): this {\n return this.ann({ description });\n }\n\n /** Omit from the panel unless it is asked to show hidden leaves. */\n hidden(): this {\n return this.ann({ hidden: true });\n }\n\n /** Pair with a sibling sharing this id, side-by-side on one row. */\n pair(pair: string): this {\n return this.ann({ pair });\n }\n\n /** Render under a named section heading. `collapsed` opens the section\n * folded, and `layout` and `pack` lay out its rows — say each on any one of\n * the section's leaves. */\n section(label: string, opts: Omit<SectionOption, 'label'> = {}): this {\n return this.opt({ section: { label, ...opts } });\n }\n\n /** Show this row only while the predicate holds. Presentational — the value\n * stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): this {\n return this.opt({ showIf: predicate });\n }\n\n /** Draw this one row yourself, keeping the kind, default and validation. */\n render(renderer: ControlRenderer): this {\n return this.opt({ render: renderer, dialog: undefined });\n }\n\n /** Draw this row as a button that opens a dialog, with `body` as the\n * dialog's content — for an editor too big for the row. */\n dialog(body: ControlRenderer, opts?: InDialogOptions): this {\n return this.opt({ dialog: { ...opts, body }, render: undefined });\n }\n\n /**\n * Compute this leaf's value while it is auto, instead of leaving it\n * `undefined`. The resolver is given the config with every other auto path\n * already resolved.\n */\n auto(resolve: (config: Record<string, unknown>) => T): this {\n return this.opt({ autoResolve: resolve as (c: Record<string, unknown>) => unknown });\n }\n\n /**\n * Start this leaf auto rather than pinned at its default. Takes `auto` and\n * nothing else — the constructor argument already declares the value, and a\n * second way to say it would fight with the first.\n */\n initial(value: Auto): this {\n if (!isAuto(value)) throw new Error('[labkit] .initial() takes `auto` and nothing else');\n return this.opt({ unpinned: true });\n }\n\n /** Never auto. The row's label does not toggle. */\n manual(): this {\n return this.opt({ manual: true });\n }\n}\n\n/** A numeric leaf, built by `f.number`. */\nexport class NumberNode extends BaseNode<number> {\n readonly kind = 'number';\n\n /** Bound the value. A number with both bounds renders as a slider. */\n range(min: number, max: number): this {\n return this.ann({ min, max });\n }\n\n step(step: number): this {\n return this.ann({ step });\n }\n\n /** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:\n * it is never parsed, and the stored value stays a plain number. */\n suffix(suffix: string): this {\n return this.ann({ suffix });\n }\n\n /** Show, speak and read the value through a display — `compact()` reads\n * `2.00M`. Presentation only: the stored value stays a plain number. */\n display(display: Display): this {\n return this.ann({ display });\n }\n\n /**\n * Store the value in one unit and edit it in another — radians stored,\n * degrees typed. `min`, `max` and `step` are declared in the stored unit\n * alongside the value, and convert with it. `prefUnit` builds one from a\n * `UnitSystem`.\n */\n unit(unit: PrefNumberUnit): this {\n return this.ann({ unit });\n }\n\n /** Force a slider even without both bounds. */\n slider(): this {\n return this.ann({ control: 'slider' });\n }\n\n /** Force a typed input even when both bounds are set. */\n input(): this {\n return this.ann({ control: 'input' });\n }\n}\n\n/** A true/false leaf, built by `f.boolean`; a checkbox unless `.toggle()`. */\nexport class BooleanNode extends BaseNode<boolean> {\n readonly kind = 'boolean';\n\n /** Ask for a switch rather than a checkbox. */\n toggle(): this {\n return this.ann({ control: 'switch' });\n }\n}\n\n/** A text leaf, built by `f.string`. */\nexport class StringNode extends BaseNode<string> {\n readonly kind = 'string';\n\n placeholder(placeholder: string): this {\n return this.ann({ placeholder });\n }\n\n maxLength(maxLength: number): this {\n return this.ann({ maxLength });\n }\n\n /** Milliseconds to debounce live writes. 0 commits every keystroke. */\n debounce(debounceMs: number): this {\n return this.ann({ debounceMs });\n }\n}\n\n/** A string-list leaf, built by `f.list`. */\nexport class ListNode extends BaseNode<string[]> {\n readonly kind = 'list';\n\n /** Shown in an empty entry. */\n placeholder(placeholder: string): this {\n return this.ann({ placeholder });\n }\n}\n\n/** A color leaf holding a hex string, built by `f.color`. */\nexport class ColorNode extends BaseNode<string> {\n readonly kind = 'color';\n\n /** The value carries alpha as `#rrggbbaa`, and the row gets an opacity\n * track beside the swatch. */\n alpha(): this {\n return this.ann({ alpha: true });\n }\n}\n\n/** A fixed-choice leaf, built by `f.enum`; a select unless `.radio()`. */\nexport class EnumNode<T extends string> extends BaseNode<T> {\n readonly kind = 'enum';\n\n /** Render as a segmented control rather than a select. */\n radio(): this {\n return this.ann({ control: 'radio' });\n }\n\n /** Relabel the choices. Named `labels` rather than `options` because\n * `options` is the node's own extras bag. */\n labels(options: readonly ConfigOption[]): this {\n return this.ann({ options });\n }\n}\n\n/** A leaf whose kind the rule chain decides. */\nexport class ValueNode<T> extends BaseNode<T> {\n readonly kind = null;\n}\n\n/** A leaf of a kind labkit does not ship a row for. */\nexport class CustomNode<T> extends BaseNode<T> {\n constructor(\n readonly kind: string,\n defaultValue: T,\n annotations: Readonly<Annotations> = {},\n options: Readonly<NodeOptions> = {},\n ) {\n super(defaultValue, annotations, options);\n }\n\n protected override with(annotations: Annotations, options: NodeOptions): this {\n return new CustomNode(this.kind, this.default, annotations, options) as this;\n }\n}\n\n/**\n * A branch of the schema: its children nest under its key, so a leaf inside\n * one is read and written at a dotted path. Distinct from `.section()`, which\n * puts a heading over sibling leaves and leaves their paths alone.\n */\nexport class GroupNode<S extends ConfigShape> implements ConfigBranch<S> {\n constructor(\n readonly children: S,\n readonly annotations: Readonly<BranchAnnotations> = {},\n readonly options: Readonly<BranchOptions> = {},\n ) {}\n\n private with(annotations: BranchAnnotations, options: BranchOptions): GroupNode<S> {\n return new GroupNode(this.children, annotations, options);\n }\n\n /** Heading for the group's rows. Defaults to the key, title-cased. */\n label(name: string): GroupNode<S> {\n return this.with({ ...this.annotations, name }, this.options);\n }\n\n /** Longer help text. Carried on the resolved `PrefGroup`; both weasel-ui's\n * `PrefsForm` and `ControlPanel` draw it under the heading. */\n describe(description: string): GroupNode<S> {\n return this.with({ ...this.annotations, description }, this.options);\n }\n\n /** Render this whole group under a named section heading. */\n section(label: string, opts: Omit<SectionOption, 'label'> = {}): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, section: { label, ...opts } });\n }\n\n /** Show this group only while the predicate holds. Presentational — every\n * value beneath it stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, showIf: predicate });\n }\n\n /** Put a button on the group's heading that returns every value beneath it\n * to its default, leaving the rest of the config alone. */\n resettable(): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, resettable: true });\n }\n}\n\n/** Every default in a shape, nested the way the shape is. */\nfunction shapeDefaults(shape: ConfigShape): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(shape)) {\n out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;\n }\n return out;\n}\n\nconst expand = <T extends string>(\n options: readonly T[] | readonly ConfigOption[],\n): readonly ConfigOption[] =>\n options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o));\n\n/**\n * Builds an instrument's config: the values, their types, and how each is\n * edited, in one declaration.\n *\n * ```ts\n * const config = f.schema({\n * showGrid: f.boolean(true),\n * cellSize: f.number(20).range(5, 80).step(5).label('Grid spacing'),\n * // Nested: read and written at `grid.color`.\n * grid: f.group({ color: f.color('#ffffff') }),\n * })\n * ```\n */\nexport const f = {\n number: (def: number): NumberNode => new NumberNode(def),\n boolean: (def: boolean): BooleanNode => new BooleanNode(def),\n string: (def: string): StringNode => new StringNode(def),\n color: (def: string): ColorNode => new ColorNode(def),\n /** A list of strings. Its row summarizes the list, and opens a dialog to\n * edit it one entry per field. */\n list: (def: readonly string[]): ListNode => new ListNode([...def]),\n\n /** A fixed set of choices. The default's literal type flows into the config\n * type, so `f.enum('fast', ['fast', 'accurate'])` gives\n * `'fast' | 'accurate'` with no `as const`. */\n enum: <const T extends string>(\n def: T,\n options: readonly T[] | readonly ConfigOption[],\n ): EnumNode<T> => new EnumNode<T>(def, { options: expand(options) }),\n\n /** A leaf with no declared kind: the rule chain decides, which is how a lab\n * states a convention like \"every `*Color` key is a color picker\". */\n value: <T>(def: T): ValueNode<T> => new ValueNode(def),\n\n /** A branch: its children nest under this key, so `grid: f.group({ size })`\n * puts the value at `config.grid.size` and addresses it as `'grid.size'`.\n * Groups nest as deeply as the schema wants. */\n group: <const S extends ConfigShape>(children: S): GroupNode<S> => new GroupNode(children),\n\n /** A leaf of a kind a lab supplies the control for, through `controls`. */\n custom: <T>(\n kind: string,\n def: T,\n validate?: (leaf: PrefLeaf, config: Record<string, unknown>) => string[],\n ): CustomNode<T> => new CustomNode(kind, def, {}, validate ? { validate } : {}),\n\n /** Collect leaves and groups into an instrument's config. */\n schema<S extends ConfigShape>(nodes: S): ConfigSchema<InferConfig<S>> {\n return { nodes, defaults: () => shapeDefaults(nodes) as InferConfig<S> };\n },\n};\n","import type { ConfigRule, ConfigRuleContext, LeafPatch } from './types';\n\n/** `cellSize` -> `Cell size`. Splits camelCase without shattering acronyms. */\nexport function titleCase(key: string): string {\n const words = key\n .replace(/[_-]+/g, ' ')\n .split(/(?<=[a-z0-9])(?=[A-Z])/)\n .join(' ')\n .trim()\n .toLowerCase();\n return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);\n}\n\n/** An `f.value` leaf takes its kind from the shape of its default. */\nconst kindFromValue: ConfigRule = (ctx) => {\n const t = typeof ctx.default;\n if (t === 'boolean' || t === 'number' || t === 'string') return { kind: t };\n const d = ctx.default;\n if (Array.isArray(d) && d.every((v) => typeof v === 'string')) return { kind: 'list' };\n return null;\n};\n\nconst labelFromKey: ConfigRule = (ctx) => ({ name: titleCase(ctx.key) });\n\n/** A number bounded on both sides is a slider; one bound or none is an input. */\nconst sliderWhenBounded: ConfigRule = (ctx) => {\n if (ctx.leaf.kind !== 'number') return null;\n const bounded = ctx.leaf.min !== undefined && ctx.leaf.max !== undefined;\n return bounded ? { control: 'slider' } : null;\n};\n\n/** `PrefBase.description` is required, so every leaf must end up with one. */\nconst descriptionDefault: ConfigRule = () => ({ description: '' });\n\n/**\n * labkit's own inference, expressed in the same vocabulary a consumer\n * extends. Runs after the consumer's rules, so a lab always wins.\n *\n * If one of these ever needs something a `ConfigRule` cannot express, the\n * seam is too weak — that is the point of writing them this way.\n */\nexport const builtinRules: readonly ConfigRule[] = [\n kindFromValue,\n labelFromKey,\n sliderWhenBounded,\n descriptionDefault,\n];\n\n/**\n * Fold a rule chain into one leaf patch. Gap-filling: a property already\n * settled is never overwritten, so the accumulator seeded with the author's\n * annotations beats every rule, and earlier rules beat later ones.\n */\nexport function applyRules(\n seed: LeafPatch,\n ctx: Omit<ConfigRuleContext, 'leaf'>,\n rules: readonly ConfigRule[],\n): LeafPatch {\n const acc: LeafPatch = { ...seed };\n for (const rule of rules) {\n const patch = rule({ ...ctx, leaf: acc });\n if (patch === null) continue;\n for (const [k, v] of Object.entries(patch)) {\n if (v !== undefined && acc[k as keyof LeafPatch] === undefined) {\n (acc as Record<string, unknown>)[k] = v;\n }\n }\n }\n return acc;\n}\n","import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\nimport { isConfigBranch } from './builder';\nimport { applyRules, builtinRules, titleCase } from './rules';\nimport type {\n ConfigEntry,\n ConfigRule,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n DialogSpec,\n LeafPatch,\n ResolvedConfig,\n SectionSpec,\n} from './types';\n\n/** Drop keys whose value is undefined, so an unset annotation stays a gap the\n * rule chain can fill rather than a settled `undefined`. */\nfunction defined(annotations: Record<string, unknown>): LeafPatch {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(annotations)) if (v !== undefined) out[k] = v;\n return out as LeafPatch;\n}\n\n/** What the walk collects on the side of the `PrefGroup` tree. */\ninterface Sink {\n sections: SectionSpec[];\n showIf: Map<string, (config: Record<string, unknown>) => boolean>;\n renderers: Record<string, ControlRenderer>;\n dialogs: Record<string, DialogSpec>;\n resettable: Set<string>;\n chain: readonly ConfigRule[];\n}\n\n/**\n * Resolve a schema into the vocabulary weasel-ui renders, running each leaf\n * through the consumer's rules and then labkit's own.\n *\n * The group mirrors the schema's own nesting: an `f.group` becomes a nested\n * `PrefGroup`, so a leaf's dotted path within the tree is the path its value\n * is written at, and both `ControlPanel` and `PrefsForm` address it\n * identically.\n */\nexport function resolveConfigSchema<TC>(\n schema: ConfigSchema<TC>,\n rules: readonly ConfigRule[] = [],\n): ResolvedConfig {\n const sink: Sink = {\n sections: [],\n showIf: new Map(),\n renderers: {},\n dialogs: {},\n resettable: new Set(),\n chain: [...rules, ...builtinRules],\n };\n const group = resolveShape(schema.nodes, '', '', sink);\n return {\n group,\n sections: sink.sections,\n showIf: sink.showIf,\n renderers: sink.renderers,\n dialogs: sink.dialogs,\n resettable: sink.resettable,\n };\n}\n\n/** A section while it is still being filled. */\ntype OpenSection = Omit<SectionSpec, 'paths'> & { paths: string[] };\n\nfunction resolveShape(shape: ConfigShape, at: string, name: string, sink: Sink): PrefGroup {\n const children: Record<string, PrefLeaf | PrefGroup> = {};\n const sections = new Map<string, OpenSection>();\n\n for (const [key, entry] of Object.entries(shape)) {\n const path = at === '' ? key : `${at}.${key}`;\n children[key] = resolveEntry(entry, key, path, sink);\n\n const { section, showIf: predicate } = entry.options;\n if (section !== undefined) {\n let spec = sections.get(section.label);\n if (!spec) {\n spec = { at, label: section.label, paths: [] };\n sections.set(section.label, spec);\n sink.sections.push(spec);\n }\n spec.paths.push(path);\n // One node saying `collapsed` settles the section, so a schema does not\n // have to repeat it on every node under the heading.\n if (section.collapsed !== undefined) {\n spec.collapsed = (spec.collapsed ?? false) || section.collapsed;\n }\n if (section.layout !== undefined && spec.layout === undefined) spec.layout = section.layout;\n if (section.pack !== undefined && spec.pack === undefined) spec.pack = section.pack;\n }\n if (predicate) sink.showIf.set(path, predicate);\n if (isConfigBranch(entry) && entry.options.resettable) sink.resettable.add(path);\n if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;\n if (!isConfigBranch(entry) && entry.options.dialog) sink.dialogs[path] = entry.options.dialog;\n }\n\n return { name, children };\n}\n\nfunction resolveEntry(\n entry: ConfigEntry,\n key: string,\n path: string,\n sink: Sink,\n): PrefLeaf | PrefGroup {\n if (isConfigBranch(entry)) {\n const group = resolveShape(\n entry.children,\n path,\n entry.annotations.name ?? titleCase(key),\n sink,\n );\n return entry.annotations.description === undefined\n ? group\n : { ...group, description: entry.annotations.description };\n }\n if (entry.options.manual && entry.options.unpinned) {\n throw new Error(\n `[labkit] \"${path}\" is both .manual() and .initial(auto) — a field that can never be auto cannot start auto`,\n );\n }\n const seed: LeafPatch = defined({\n ...entry.annotations,\n ...(entry.kind === null ? {} : { kind: entry.kind }),\n });\n const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);\n const { autoResolve, unpinned, manual } = entry.options;\n return {\n ...patch,\n default: entry.default,\n // Extra keys survive onto the leaf at runtime; `ControlPanel` reads them\n // back with its `extra<T>` helper, the same as `min`, `step` and the rest.\n ...(autoResolve === undefined ? {} : { autoResolve }),\n ...(unpinned === undefined ? {} : { unpinned }),\n ...(manual === undefined ? {} : { manual }),\n } as PrefLeaf;\n}\n","import { useMemo } from 'react';\nimport { resolveAutoConfig } from './autoConfig';\nimport type { ResolvedConfig } from './types';\n\nconst NONE: ReadonlySet<string> = new Set();\n\n/**\n * The config an instrument reads: the stored one with every unpinned path\n * resolved. The single place resolution happens, so `Trial` and\n * `useTrialState` cannot disagree about what an instrument is looking at.\n */\nexport function useResolvedConfig<TC>(\n schema: ResolvedConfig | undefined,\n config: TC,\n autoPaths: readonly string[] | undefined,\n): { config: TC; auto: ReadonlySet<string> } {\n const auto = useMemo(\n () => (autoPaths && autoPaths.length > 0 ? new Set(autoPaths) : NONE),\n [autoPaths],\n );\n const resolvedConfig = useMemo(\n () => (schema ? resolveAutoConfig(schema, config, auto) : config),\n [schema, config, auto],\n );\n return { config: resolvedConfig, auto };\n}\n"]}
@@ -1,34 +0,0 @@
1
- .lk-status-bar {
2
- .numeric();
3
- display: flex;
4
- align-items: center;
5
- /* One line, always. A bar that grows a second line takes it from the canvas
6
- above it, and a readout that changes width would do that on and off as it
7
- ticks. Too narrow for its contents clips them instead. */
8
- overflow: hidden;
9
- gap: var(--wzl-space-md);
10
- padding: var(--wzl-space-xs) var(--wzl-space-sm);
11
- background: var(--wzl-surface-raised);
12
- border-top: 1px solid var(--wzl-border);
13
- font-size: var(--wzl-font-size-sm);
14
- color: var(--wzl-fg-muted);
15
- }
16
-
17
- .lk-status-bar-section {
18
- display: inline-flex;
19
- align-items: center;
20
- white-space: nowrap;
21
- min-width: 0;
22
- gap: var(--wzl-space-xs);
23
- padding: 0 var(--wzl-space-xs);
24
- border-right: 1px solid var(--wzl-line-subtle);
25
-
26
- &:last-child { border-right: none; }
27
-
28
- &--end {
29
- margin-left: auto;
30
- }
31
-
32
- // A section pushed to the end already reads as separated by the gap.
33
- &:has(+ &--end) { border-right: none; }
34
- }
@@ -1,24 +0,0 @@
1
- import type { Meta, StoryObj } from '@weasel-js/forge';
2
- import { StatusBar } from './StatusBar';
3
-
4
- const meta: Meta<typeof StatusBar> = {
5
- title: 'labkit/Primitives/StatusBar',
6
- component: StatusBar,
7
- };
8
- export default meta;
9
-
10
- type Story = StoryObj<typeof StatusBar>;
11
-
12
- export const Plain: Story = {
13
- render: () => <StatusBar>Ready</StatusBar>,
14
- };
15
-
16
- export const Sections: Story = {
17
- render: () => (
18
- <StatusBar>
19
- <StatusBar.Section>Items: 12</StatusBar.Section>
20
- <StatusBar.Section>Zoom: 100%</StatusBar.Section>
21
- <StatusBar.Section>FPS: 60</StatusBar.Section>
22
- </StatusBar>
23
- ),
24
- };
@@ -1,25 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { describe, expect, test } from 'vitest';
3
- import { StatusBar } from './StatusBar';
4
-
5
- describe('StatusBar', () => {
6
- test('renders children', () => {
7
- render(<StatusBar>3 items</StatusBar>);
8
- expect(screen.getByText('3 items')).toBeInTheDocument();
9
- });
10
-
11
- test('uses lk-status-bar class', () => {
12
- const { container } = render(<StatusBar>x</StatusBar>);
13
- expect((container.firstChild as HTMLElement).className).toBe('lk-status-bar');
14
- });
15
-
16
- test('renders multiple section children separated', () => {
17
- const { container } = render(
18
- <StatusBar>
19
- <StatusBar.Section>left</StatusBar.Section>
20
- <StatusBar.Section>right</StatusBar.Section>
21
- </StatusBar>,
22
- );
23
- expect(container.querySelectorAll('.lk-status-bar-section').length).toBe(2);
24
- });
25
- });