@weasel-js/labkit 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +6 -6
  2. package/dist/_dts/{index-CFlDPeZh.d.ts → index-iAP6XbH3.d.ts} +17 -7
  3. package/dist/_dts/{useExperimentState-D7EQnnwJ.d.ts → useTrialState-DYe2vUwN.d.ts} +60 -36
  4. package/dist/canvas/index.d.ts +1 -1
  5. package/dist/chunk-3TYUJR7Z.js +488 -0
  6. package/dist/chunk-3TYUJR7Z.js.map +1 -0
  7. package/dist/{chunk-BAPZPDDA.js → chunk-C6GJKPUI.js} +365 -340
  8. package/dist/chunk-C6GJKPUI.js.map +1 -0
  9. package/dist/{chunk-DWV7SFKR.js → chunk-DJLDIRFN.js} +28 -12
  10. package/dist/chunk-DJLDIRFN.js.map +1 -0
  11. package/dist/{chunk-DEWXYFEU.js → chunk-NRKWVTVT.js} +3 -3
  12. package/dist/{chunk-DEWXYFEU.js.map → chunk-NRKWVTVT.js.map} +1 -1
  13. package/dist/dragdrop/index.d.ts +1 -1
  14. package/dist/index.d.ts +149 -149
  15. package/dist/index.js +164 -159
  16. package/dist/index.js.map +1 -1
  17. package/dist/layers/index.d.ts +2 -2
  18. package/dist/passthrough/weasel-canvas.js +1 -1
  19. package/dist/passthrough/weasel-ui.d.ts +9 -2
  20. package/dist/passthrough/weasel-ui.js +2 -2
  21. package/dist/state/index.d.ts +12 -12
  22. package/dist/state/index.js +5 -5
  23. package/dist/state/index.js.map +1 -1
  24. package/dist/styles.css +21 -13
  25. package/dist/ui/layers/index.js +3 -3
  26. package/dist/undo/index.d.ts +1 -1
  27. package/package.json +1 -1
  28. package/src/canvas/AGENTS.md +6 -2
  29. package/src/dragdrop/dragDrop.test.tsx +4 -7
  30. package/src/index.test.ts +52 -0
  31. package/src/index.ts +11 -7
  32. package/src/instrument/SineWave.smoke.test.tsx +1 -1
  33. package/src/instrument/capabilityDetector.ts +1 -1
  34. package/src/instrument/types.ts +18 -6
  35. package/src/lab/Lab.less +16 -0
  36. package/src/lab/Lab.stories.tsx +5 -5
  37. package/src/lab/Lab.test.tsx +25 -25
  38. package/src/lab/Lab.tsx +50 -50
  39. package/src/lab/LabContext.ts +12 -12
  40. package/src/lab/LabShell.tsx +1 -1
  41. package/src/lab/{WorkspaceGrid.less → Workspace.less} +2 -2
  42. package/src/lab/{WorkspaceGrid.stories.tsx → Workspace.stories.tsx} +13 -13
  43. package/src/lab/{WorkspaceGrid.test.tsx → Workspace.test.tsx} +24 -24
  44. package/src/lab/{WorkspaceGrid.tsx → Workspace.tsx} +18 -18
  45. package/src/lab/index.ts +2 -2
  46. package/src/layers/AGENTS.md +3 -3
  47. package/src/primitives/Toolbar.stories.tsx +2 -2
  48. package/src/state/SingletonExperiment.test.tsx +8 -8
  49. package/src/state/SingletonExperiment.tsx +9 -9
  50. package/src/state/context.tsx +12 -12
  51. package/src/state/document.test.ts +369 -0
  52. package/src/state/document.ts +194 -0
  53. package/src/state/helpers.test.ts +74 -46
  54. package/src/state/helpers.ts +21 -29
  55. package/src/state/index.ts +16 -8
  56. package/src/state/store.test.ts +440 -41
  57. package/src/state/store.ts +159 -102
  58. package/src/state/types.ts +29 -12
  59. package/src/state/{useExperimentState.test.tsx → useTrialState.test.tsx} +18 -18
  60. package/src/state/useTrialState.ts +29 -0
  61. package/src/styles.less +2 -2
  62. package/src/theme/base.less +1 -1
  63. package/src/{workspace → trial}/DefaultSidebar.tsx +3 -3
  64. package/src/{workspace → trial}/DefaultStatusBar.tsx +5 -4
  65. package/src/{workspace → trial}/DefaultToolbar.tsx +9 -8
  66. package/src/trial/Trial.canvas.test.tsx +80 -0
  67. package/src/{workspace/Workspace.less → trial/Trial.less} +1 -1
  68. package/src/{workspace/Workspace.stories.tsx → trial/Trial.stories.tsx} +13 -18
  69. package/src/{workspace/Workspace.test.tsx → trial/Trial.test.tsx} +26 -26
  70. package/src/{workspace/Workspace.tsx → trial/Trial.tsx} +48 -43
  71. package/src/{workspace/WorkspaceChrome.tsx → trial/TrialChrome.tsx} +42 -42
  72. package/src/{workspace → trial}/index.ts +13 -13
  73. package/src/{workspace → trial}/slotTypes.ts +16 -16
  74. package/src/{workspace/workspaceOps.test.ts → trial/trialOps.test.ts} +38 -44
  75. package/src/trial/trialOps.ts +99 -0
  76. package/src/ui/format.test.ts +33 -0
  77. package/src/ui/format.ts +11 -0
  78. package/src/ui/properties/CurveField.tsx +6 -6
  79. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +3 -3
  80. package/dist/chunk-BAPZPDDA.js.map +0 -1
  81. package/dist/chunk-DWV7SFKR.js.map +0 -1
  82. package/dist/chunk-KSTEW2AF.js +0 -367
  83. package/dist/chunk-KSTEW2AF.js.map +0 -1
  84. package/src/state/useExperimentState.ts +0 -31
  85. package/src/workspace/workspaceOps.ts +0 -102
@@ -1,4 +1,4 @@
1
- import { c, p } from './chunk-BAPZPDDA.js';
1
+ import { c, p } from './chunk-C6GJKPUI.js';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -157,5 +157,5 @@ function DragHandleGlyph() {
157
157
  }
158
158
 
159
159
  export { LayerStack };
160
- //# sourceMappingURL=chunk-DEWXYFEU.js.map
161
- //# sourceMappingURL=chunk-DEWXYFEU.js.map
160
+ //# sourceMappingURL=chunk-NRKWVTVT.js.map
161
+ //# sourceMappingURL=chunk-NRKWVTVT.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui/layers/LayerStack.tsx"],"names":[],"mappings":"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","file":"chunk-DEWXYFEU.js","sourcesContent":["import { type CSSProperties, type ReactNode, type RefCallback, useEffect, useState } from 'react';\nimport { dlog, useReorderDragList } from '../../passthrough/weasel-ui';\n\n/** One card in a layer stack: its identity, the label shown when collapsed,\n * and the optional select hoisted into its header. */\nexport interface LayerStackItem {\n /** Stable id used for keys, onRemove, onReorder. Numeric to match\n * common id-from-nextId conventions; string ids also work. */\n id: number | string;\n /** Short kind label rendered in the header when no primary select\n * is hoisted (e.g. \"shadow\", \"stroke\"). */\n kind: string;\n /** When present, hoist this select into the card header so the user\n * can switch mode/shape without expanding. */\n primaryValue?: string;\n primaryOptions?: string[];\n /** Accent CSS color used as the left border / index-badge fill. */\n accent?: string;\n /** Optional badge text rendered before the primary control\n * (e.g. tail index \"1\", \"2\", \"3\"). When omitted a drag handle\n * glyph renders in its place. */\n badge?: string;\n /** Initial expanded state. Defaults to true for newly-added items. */\n defaultExpanded?: boolean;\n}\n\n/** Props for `<LayerStack>`. */\nexport interface LayerStackProps {\n title: string;\n items: LayerStackItem[];\n /** Kinds the user can add via the header palette. */\n paletteKinds: string[];\n onAdd: (kind: string) => void;\n onRemove: (id: number | string) => void;\n onReorder: (orderedIds: Array<number | string>) => void;\n onPrimaryChange: (id: number | string, nextValue: string) => void;\n /** Render the body controls for each item. */\n renderBody: (item: LayerStackItem) => ReactNode;\n /** Hide the title + palette row (used when an outer wrap renders its\n * own head — see speech-balloons Tails panel). */\n hideHead?: boolean;\n}\n\n/** A drag-reorderable stack of expandable cards, with a palette in the header\n * for adding more. The body of each card is the caller's to render. */\nexport function LayerStack({\n title,\n items,\n paletteKinds,\n onAdd,\n onRemove,\n onReorder,\n onPrimaryChange,\n renderBody,\n hideHead,\n}: LayerStackProps) {\n const [expandedIds, setExpandedIds] = useState<Set<number | string>>(\n () => new Set(items.filter((i) => i.defaultExpanded !== false).map((i) => i.id)),\n );\n\n useEffect(() => {\n setExpandedIds((prev) => {\n let next: Set<number | string> | null = null;\n for (const item of items) {\n if (item.defaultExpanded === false) continue;\n if (prev.has(item.id)) continue;\n if (next === null) next = new Set(prev);\n next.add(item.id);\n }\n return next ?? prev;\n });\n }, [items]);\n\n const dragItems = items.map((it) => ({ id: String(it.id), label: it.kind }));\n const drag = useReorderDragList({\n items: dragItems,\n selectedIds: [],\n onReorder: (ids, targetIndex) => {\n dlog('layer-stack', 'onReorder', { ids, targetIndex });\n const orig = items.map((i) => i.id);\n const moving = new Set(ids);\n const remaining = orig.filter((id) => !moving.has(String(id)));\n const movedIds = items.map((i) => i.id).filter((id) => moving.has(String(id)));\n // weasel reports targetIndex against the original list (which includes\n // the dragged row). Each moved item that originally sat before\n // targetIndex needs to shift the insertion point left by one.\n const shift = orig.filter((id, idx) => moving.has(String(id)) && idx < targetIndex).length;\n const adjusted = targetIndex - shift;\n const out = [...remaining];\n out.splice(adjusted, 0, ...movedIds);\n onReorder(out);\n },\n });\n\n const toggleExpanded = (id: number | string) => {\n setExpandedIds((s) => {\n const n = new Set(s);\n if (n.has(id)) n.delete(id);\n else n.add(id);\n return n;\n });\n };\n\n return (\n <div className=\"lk-layer-stack\">\n {!hideHead && (\n <div className=\"lk-layer-stack__head\">\n <h2 className=\"lk-layer-stack__title\">{title}</h2>\n <div className=\"lk-layer-stack__palette\">\n {paletteKinds.map((k) => (\n <button\n key={k}\n type=\"button\"\n className=\"lk-layer-stack__add\"\n onClick={() => {\n dlog('layer-stack', 'onAdd', { kind: k });\n onAdd(k);\n }}\n aria-label={`Add ${k}`}\n >\n {k}\n </button>\n ))}\n </div>\n </div>\n )}\n <div\n className=\"lk-layer-stack__list\"\n ref={drag.containerProps.ref as RefCallback<HTMLDivElement>}\n onPointerMove={drag.containerProps.onPointerMove}\n onPointerUp={drag.containerProps.onPointerUp}\n onPointerCancel={drag.containerProps.onPointerCancel}\n >\n {items.map((item, i) => {\n const expanded = expandedIds.has(item.id);\n const draggedId = drag.state.draggedIds?.[0];\n const isDragging = draggedId === String(item.id);\n const showHintBefore = drag.state.targetIndex === i && draggedId !== String(item.id);\n const showHintAfter = drag.state.targetIndex === items.length && i === items.length - 1;\n const cardCls = [\n 'lk-layer-card',\n expanded ? 'is-expanded' : 'is-collapsed',\n isDragging ? 'is-dragging' : '',\n item.accent ? 'has-accent' : '',\n ]\n .filter(Boolean)\n .join(' ');\n const cardStyle = item.accent\n ? ({ '--lk-layer-card-accent': item.accent } as CSSProperties)\n : undefined;\n const { onPointerDown } = drag.rowProps(String(item.id), i);\n return (\n <div key={item.id} className=\"lk-layer-card-wrap\">\n {showHintBefore && <div className=\"lk-layer-stack__drop-hint\" />}\n <div className={cardCls} data-testid={`lk-layer-card-${item.id}`} style={cardStyle}>\n <div className=\"lk-layer-card__head\">\n <button\n type=\"button\"\n className=\"lk-layer-card__handle\"\n aria-label={`Drag to reorder layer ${item.id}`}\n onPointerDown={onPointerDown}\n onClick={() => toggleExpanded(item.id)}\n >\n {item.badge ?? <DragHandleGlyph />}\n </button>\n {item.primaryValue !== undefined && item.primaryOptions ? (\n <select\n className=\"lk-layer-card__primary\"\n value={item.primaryValue}\n aria-label={`Primary select for layer ${item.id}`}\n onChange={(e) => onPrimaryChange(item.id, e.target.value)}\n onClick={(e) => e.stopPropagation()}\n >\n {item.primaryOptions.map((o) => (\n <option key={o} value={o}>\n {o}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"lk-layer-card__kind\">{item.kind}</span>\n )}\n <button\n type=\"button\"\n className=\"lk-layer-card__remove\"\n aria-label=\"Remove layer\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove(item.id);\n }}\n >\n ✕\n </button>\n </div>\n {expanded && <div className=\"lk-layer-card__body\">{renderBody(item)}</div>}\n </div>\n {showHintAfter && <div className=\"lk-layer-stack__drop-hint\" />}\n </div>\n );\n })}\n {items.length === 0 && (\n <div className=\"lk-layer-stack__empty\">No layers — add one above.</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction DragHandleGlyph() {\n return (\n <svg width=\"12\" height=\"16\" viewBox=\"0 0 8 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"2\" cy=\"3\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"3\" r=\"1.1\" />\n <circle cx=\"2\" cy=\"8\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"8\" r=\"1.1\" />\n <circle cx=\"2\" cy=\"13\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"13\" r=\"1.1\" />\n </svg>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/ui/layers/LayerStack.tsx"],"names":[],"mappings":";;;;AA6CO,SAAS,UAAA,CAAW;AAAA,EACzB,KAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,eAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAAoB;AAClB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,QAAA;AAAA,IACpC,MAAM,IAAI,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,KAAoB,KAAK,EAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC;AAAA,GACjF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,cAAA,CAAe,CAAC,IAAA,KAAS;AACvB,MAAA,IAAI,IAAA,GAAoC,IAAA;AACxC,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,IAAI,IAAA,CAAK,oBAAoB,KAAA,EAAO;AACpC,QAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG;AACvB,QAAA,IAAI,IAAA,KAAS,IAAA,EAAM,IAAA,GAAO,IAAI,IAAI,IAAI,CAAA;AACtC,QAAA,IAAA,CAAK,GAAA,CAAI,KAAK,EAAE,CAAA;AAAA,MAClB;AACA,MAAA,OAAO,IAAA,IAAQ,IAAA;AAAA,IACjB,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,GAAA,CAAI,CAAC,QAAQ,EAAE,EAAA,EAAI,MAAA,CAAO,EAAA,CAAG,EAAE,CAAA,EAAG,KAAA,EAAO,EAAA,CAAG,MAAK,CAAE,CAAA;AAC3E,EAAA,MAAM,OAAO,CAAA,CAAmB;AAAA,IAC9B,KAAA,EAAO,SAAA;AAAA,IACP,aAAa,EAAC;AAAA,IACd,SAAA,EAAW,CAAC,GAAA,EAAK,WAAA,KAAgB;AAC/B,MAAA,CAAA,CAAK,aAAA,EAAe,WAAA,EAAa,EAAE,GAAA,EAAK,aAAa,CAAA;AACrD,MAAA,MAAM,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA;AAClC,MAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,GAAG,CAAA;AAC1B,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,CAAC,EAAA,KAAO,CAAC,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAC,CAAA;AAC7D,MAAA,MAAM,WAAW,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,OAAO,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAC,CAAA;AAI7E,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,MAAA,CAAO,CAAC,IAAI,GAAA,KAAQ,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA,IAAK,GAAA,GAAM,WAAW,CAAA,CAAE,MAAA;AACpF,MAAA,MAAM,WAAW,WAAA,GAAc,KAAA;AAC/B,MAAA,MAAM,GAAA,GAAM,CAAC,GAAG,SAAS,CAAA;AACzB,MAAA,GAAA,CAAI,MAAA,CAAO,QAAA,EAAU,CAAA,EAAG,GAAG,QAAQ,CAAA;AACnC,MAAA,SAAA,CAAU,GAAG,CAAA;AAAA,IACf;AAAA,GACD,CAAA;AAED,EAAA,MAAM,cAAA,GAAiB,CAAC,EAAA,KAAwB;AAC9C,IAAA,cAAA,CAAe,CAAC,CAAA,KAAM;AACpB,MAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,CAAC,CAAA;AACnB,MAAA,IAAI,EAAE,GAAA,CAAI,EAAE,CAAA,EAAG,CAAA,CAAE,OAAO,EAAE,CAAA;AAAA,WACrB,CAAA,CAAE,IAAI,EAAE,CAAA;AACb,MAAA,OAAO,CAAA;AAAA,IACT,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gBAAA,EACZ,QAAA,EAAA;AAAA,IAAA,CAAC,QAAA,oBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAA,EACb,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,0BAC5C,KAAA,EAAA,EAAI,SAAA,EAAU,2BACZ,QAAA,EAAA,YAAA,CAAa,GAAA,CAAI,CAAC,CAAA,qBACjB,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UAEC,IAAA,EAAK,QAAA;AAAA,UACL,SAAA,EAAU,qBAAA;AAAA,UACV,SAAS,MAAM;AACb,YAAA,CAAA,CAAK,aAAA,EAAe,OAAA,EAAS,EAAE,IAAA,EAAM,GAAG,CAAA;AACxC,YAAA,KAAA,CAAM,CAAC,CAAA;AAAA,UACT,CAAA;AAAA,UACA,YAAA,EAAY,OAAO,CAAC,CAAA,CAAA;AAAA,UAEnB,QAAA,EAAA;AAAA,SAAA;AAAA,QATI;AAAA,OAWR,CAAA,EACH;AAAA,KAAA,EACF,CAAA;AAAA,oBAEF,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,sBAAA;AAAA,QACV,GAAA,EAAK,KAAK,cAAA,CAAe,GAAA;AAAA,QACzB,aAAA,EAAe,KAAK,cAAA,CAAe,aAAA;AAAA,QACnC,WAAA,EAAa,KAAK,cAAA,CAAe,WAAA;AAAA,QACjC,eAAA,EAAiB,KAAK,cAAA,CAAe,eAAA;AAAA,QAEpC,QAAA,EAAA;AAAA,UAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,KAAM;AACtB,YAAA,MAAM,QAAA,GAAW,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AACxC,YAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,UAAA,GAAa,CAAC,CAAA;AAC3C,YAAA,MAAM,UAAA,GAAa,SAAA,KAAc,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AAC/C,YAAA,MAAM,cAAA,GAAiB,KAAK,KAAA,CAAM,WAAA,KAAgB,KAAK,SAAA,KAAc,MAAA,CAAO,KAAK,EAAE,CAAA;AACnF,YAAA,MAAM,aAAA,GAAgB,KAAK,KAAA,CAAM,WAAA,KAAgB,MAAM,MAAA,IAAU,CAAA,KAAM,MAAM,MAAA,GAAS,CAAA;AACtF,YAAA,MAAM,OAAA,GAAU;AAAA,cACd,eAAA;AAAA,cACA,WAAW,aAAA,GAAgB,cAAA;AAAA,cAC3B,aAAa,aAAA,GAAgB,EAAA;AAAA,cAC7B,IAAA,CAAK,SAAS,YAAA,GAAe;AAAA,aAC/B,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AACX,YAAA,MAAM,YAAY,IAAA,CAAK,MAAA,GAClB,EAAE,wBAAA,EAA0B,IAAA,CAAK,QAAO,GACzC,MAAA;AACJ,YAAA,MAAM,EAAE,eAAc,GAAI,IAAA,CAAK,SAAS,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA,EAAG,CAAC,CAAA;AAC1D,YAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAkB,SAAA,EAAU,oBAAA,EAC1B,QAAA,EAAA;AAAA,cAAA,cAAA,oBAAkB,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EAA4B,CAAA;AAAA,8BAC9D,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,EAAS,aAAA,EAAa,iBAAiB,IAAA,CAAK,EAAE,CAAA,CAAA,EAAI,KAAA,EAAO,SAAA,EACvE,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,qBAAA,EACb,QAAA,EAAA;AAAA,kCAAA,GAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,IAAA,EAAK,QAAA;AAAA,sBACL,SAAA,EAAU,uBAAA;AAAA,sBACV,YAAA,EAAY,CAAA,sBAAA,EAAyB,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,sBAC5C,aAAA;AAAA,sBACA,OAAA,EAAS,MAAM,cAAA,CAAe,IAAA,CAAK,EAAE,CAAA;AAAA,sBAEpC,QAAA,EAAA,IAAA,CAAK,KAAA,oBAAS,GAAA,CAAC,eAAA,EAAA,EAAgB;AAAA;AAAA,mBAClC;AAAA,kBACC,IAAA,CAAK,YAAA,KAAiB,MAAA,IAAa,IAAA,CAAK,cAAA,mBACvC,GAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,SAAA,EAAU,wBAAA;AAAA,sBACV,OAAO,IAAA,CAAK,YAAA;AAAA,sBACZ,YAAA,EAAY,CAAA,yBAAA,EAA4B,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,sBAC/C,QAAA,EAAU,CAAC,CAAA,KAAM,eAAA,CAAgB,KAAK,EAAA,EAAI,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,sBACxD,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,sBAEjC,QAAA,EAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,CAAC,CAAA,qBACxB,GAAA,CAAC,QAAA,EAAA,EAAe,KAAA,EAAO,CAAA,EACpB,QAAA,EAAA,CAAA,EAAA,EADU,CAEb,CACD;AAAA;AAAA,sCAGH,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qBAAA,EAAuB,eAAK,IAAA,EAAK,CAAA;AAAA,kCAEnD,GAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,IAAA,EAAK,QAAA;AAAA,sBACL,SAAA,EAAU,uBAAA;AAAA,sBACV,YAAA,EAAW,cAAA;AAAA,sBACX,OAAA,EAAS,CAAC,CAAA,KAAM;AACd,wBAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,wBAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAAA,sBAClB,CAAA;AAAA,sBACD,QAAA,EAAA;AAAA;AAAA;AAED,iBAAA,EACF,CAAA;AAAA,gBACC,4BAAY,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,qBAAA,EAAuB,QAAA,EAAA,UAAA,CAAW,IAAI,CAAA,EAAE;AAAA,eAAA,EACtE,CAAA;AAAA,cACC,aAAA,oBAAiB,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EAA4B;AAAA,aAAA,EAAA,EA5CrD,KAAK,EA6Cf,CAAA;AAAA,UAEJ,CAAC,CAAA;AAAA,UACA,MAAM,MAAA,KAAW,CAAA,wBACf,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAwB,QAAA,EAAA,iCAAA,EAA0B;AAAA;AAAA;AAAA;AAErE,GAAA,EACF,CAAA;AAEJ;AAEA,SAAS,eAAA,GAAkB;AACzB,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,IAAA,EAAK,MAAA,EAAO,IAAA,EAAK,OAAA,EAAQ,UAAA,EAAW,IAAA,EAAK,cAAA,EAAe,aAAA,EAAY,MAAA,EAC7E,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,YAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,GAAE,KAAA,EAAM,CAAA;AAAA,wBAC7B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,GAAA,EAAI,GAAE,KAAA,EAAM,CAAA;AAAA,wBAC7B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,GAAA,EAAI,GAAE,KAAA,EAAM,CAAA;AAAA,wBAC7B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,GAAA,EAAI,GAAE,KAAA,EAAM,CAAA;AAAA,wBAC7B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,GAAE,KAAA,EAAM,CAAA;AAAA,wBAC9B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,GAAE,KAAA,EAAM;AAAA,GAAA,EACjC,CAAA;AAEJ","file":"chunk-NRKWVTVT.js","sourcesContent":["import { type CSSProperties, type ReactNode, type RefCallback, useEffect, useState } from 'react';\nimport { dlog, useReorderDragList } from '../../passthrough/weasel-ui';\n\n/** One card in a layer stack: its identity, the label shown when collapsed,\n * and the optional select hoisted into its header. */\nexport interface LayerStackItem {\n /** Stable id used for keys, onRemove, onReorder. Numeric to match\n * common id-from-nextId conventions; string ids also work. */\n id: number | string;\n /** Short kind label rendered in the header when no primary select\n * is hoisted (e.g. \"shadow\", \"stroke\"). */\n kind: string;\n /** When present, hoist this select into the card header so the user\n * can switch mode/shape without expanding. */\n primaryValue?: string;\n primaryOptions?: string[];\n /** Accent CSS color used as the left border / index-badge fill. */\n accent?: string;\n /** Optional badge text rendered before the primary control\n * (e.g. tail index \"1\", \"2\", \"3\"). When omitted a drag handle\n * glyph renders in its place. */\n badge?: string;\n /** Initial expanded state. Defaults to true for newly-added items. */\n defaultExpanded?: boolean;\n}\n\n/** Props for `<LayerStack>`. */\nexport interface LayerStackProps {\n title: string;\n items: LayerStackItem[];\n /** Kinds the user can add via the header palette. */\n paletteKinds: string[];\n onAdd: (kind: string) => void;\n onRemove: (id: number | string) => void;\n onReorder: (orderedIds: Array<number | string>) => void;\n onPrimaryChange: (id: number | string, nextValue: string) => void;\n /** Render the body controls for each item. */\n renderBody: (item: LayerStackItem) => ReactNode;\n /** Hide the title + palette row (used when an outer wrap renders its\n * own head — see speech-balloons Tails panel). */\n hideHead?: boolean;\n}\n\n/** A drag-reorderable stack of expandable cards, with a palette in the header\n * for adding more. The body of each card is the caller's to render. */\nexport function LayerStack({\n title,\n items,\n paletteKinds,\n onAdd,\n onRemove,\n onReorder,\n onPrimaryChange,\n renderBody,\n hideHead,\n}: LayerStackProps) {\n const [expandedIds, setExpandedIds] = useState<Set<number | string>>(\n () => new Set(items.filter((i) => i.defaultExpanded !== false).map((i) => i.id)),\n );\n\n useEffect(() => {\n setExpandedIds((prev) => {\n let next: Set<number | string> | null = null;\n for (const item of items) {\n if (item.defaultExpanded === false) continue;\n if (prev.has(item.id)) continue;\n if (next === null) next = new Set(prev);\n next.add(item.id);\n }\n return next ?? prev;\n });\n }, [items]);\n\n const dragItems = items.map((it) => ({ id: String(it.id), label: it.kind }));\n const drag = useReorderDragList({\n items: dragItems,\n selectedIds: [],\n onReorder: (ids, targetIndex) => {\n dlog('layer-stack', 'onReorder', { ids, targetIndex });\n const orig = items.map((i) => i.id);\n const moving = new Set(ids);\n const remaining = orig.filter((id) => !moving.has(String(id)));\n const movedIds = items.map((i) => i.id).filter((id) => moving.has(String(id)));\n // weasel reports targetIndex against the original list (which includes\n // the dragged row). Each moved item that originally sat before\n // targetIndex needs to shift the insertion point left by one.\n const shift = orig.filter((id, idx) => moving.has(String(id)) && idx < targetIndex).length;\n const adjusted = targetIndex - shift;\n const out = [...remaining];\n out.splice(adjusted, 0, ...movedIds);\n onReorder(out);\n },\n });\n\n const toggleExpanded = (id: number | string) => {\n setExpandedIds((s) => {\n const n = new Set(s);\n if (n.has(id)) n.delete(id);\n else n.add(id);\n return n;\n });\n };\n\n return (\n <div className=\"lk-layer-stack\">\n {!hideHead && (\n <div className=\"lk-layer-stack__head\">\n <h2 className=\"lk-layer-stack__title\">{title}</h2>\n <div className=\"lk-layer-stack__palette\">\n {paletteKinds.map((k) => (\n <button\n key={k}\n type=\"button\"\n className=\"lk-layer-stack__add\"\n onClick={() => {\n dlog('layer-stack', 'onAdd', { kind: k });\n onAdd(k);\n }}\n aria-label={`Add ${k}`}\n >\n {k}\n </button>\n ))}\n </div>\n </div>\n )}\n <div\n className=\"lk-layer-stack__list\"\n ref={drag.containerProps.ref as RefCallback<HTMLDivElement>}\n onPointerMove={drag.containerProps.onPointerMove}\n onPointerUp={drag.containerProps.onPointerUp}\n onPointerCancel={drag.containerProps.onPointerCancel}\n >\n {items.map((item, i) => {\n const expanded = expandedIds.has(item.id);\n const draggedId = drag.state.draggedIds?.[0];\n const isDragging = draggedId === String(item.id);\n const showHintBefore = drag.state.targetIndex === i && draggedId !== String(item.id);\n const showHintAfter = drag.state.targetIndex === items.length && i === items.length - 1;\n const cardCls = [\n 'lk-layer-card',\n expanded ? 'is-expanded' : 'is-collapsed',\n isDragging ? 'is-dragging' : '',\n item.accent ? 'has-accent' : '',\n ]\n .filter(Boolean)\n .join(' ');\n const cardStyle = item.accent\n ? ({ '--lk-layer-card-accent': item.accent } as CSSProperties)\n : undefined;\n const { onPointerDown } = drag.rowProps(String(item.id), i);\n return (\n <div key={item.id} className=\"lk-layer-card-wrap\">\n {showHintBefore && <div className=\"lk-layer-stack__drop-hint\" />}\n <div className={cardCls} data-testid={`lk-layer-card-${item.id}`} style={cardStyle}>\n <div className=\"lk-layer-card__head\">\n <button\n type=\"button\"\n className=\"lk-layer-card__handle\"\n aria-label={`Drag to reorder layer ${item.id}`}\n onPointerDown={onPointerDown}\n onClick={() => toggleExpanded(item.id)}\n >\n {item.badge ?? <DragHandleGlyph />}\n </button>\n {item.primaryValue !== undefined && item.primaryOptions ? (\n <select\n className=\"lk-layer-card__primary\"\n value={item.primaryValue}\n aria-label={`Primary select for layer ${item.id}`}\n onChange={(e) => onPrimaryChange(item.id, e.target.value)}\n onClick={(e) => e.stopPropagation()}\n >\n {item.primaryOptions.map((o) => (\n <option key={o} value={o}>\n {o}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"lk-layer-card__kind\">{item.kind}</span>\n )}\n <button\n type=\"button\"\n className=\"lk-layer-card__remove\"\n aria-label=\"Remove layer\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove(item.id);\n }}\n >\n ✕\n </button>\n </div>\n {expanded && <div className=\"lk-layer-card__body\">{renderBody(item)}</div>}\n </div>\n {showHintAfter && <div className=\"lk-layer-stack__drop-hint\" />}\n </div>\n );\n })}\n {items.length === 0 && (\n <div className=\"lk-layer-stack__empty\">No layers — add one above.</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction DragHandleGlyph() {\n return (\n <svg width=\"12\" height=\"16\" viewBox=\"0 0 8 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"2\" cy=\"3\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"3\" r=\"1.1\" />\n <circle cx=\"2\" cy=\"8\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"8\" r=\"1.1\" />\n <circle cx=\"2\" cy=\"13\" r=\"1.1\" />\n <circle cx=\"6\" cy=\"13\" r=\"1.1\" />\n </svg>\n );\n}\n"]}
@@ -1,3 +1,3 @@
1
- export { D as DragDropCapability, b as DragFeedback, P as PaletteItem } from '../_dts/index-CFlDPeZh.js';
1
+ export { D as DragDropCapability, c as DragFeedback, P as PaletteItem } from '../_dts/index-iAP6XbH3.js';
2
2
  import 'react';
3
3
  import '../_dts/types-x92Kfeme.js';
package/dist/index.d.ts CHANGED
@@ -2,20 +2,20 @@ export { CanvasLayerDescriptor, CanvasStack, CanvasStackContext, CanvasStackCont
2
2
  export { ControlPanel } from './controls/index.js';
3
3
  import { C as ConfigField } from './_dts/types-x92Kfeme.js';
4
4
  export { a as CheckboxField, b as ColorField, c as ConfigFieldBase, d as ConfigFieldType, N as NumberField, S as SelectField, e as SelectOption, f as SliderField, T as TextField } from './_dts/types-x92Kfeme.js';
5
- import { I as Instrument } from './_dts/index-CFlDPeZh.js';
6
- export { C as CanvasCapability, a as CanvasLayer, D as DragDropCapability, b as DragFeedback, H as HitResult, L as LayerCapability, c as LayerDescriptor, P as PaletteItem, d as Point, R as RenderContext, S as SystemEvent, U as UndoCapability, V as ViewTransform } from './_dts/index-CFlDPeZh.js';
5
+ import { I as Instrument, a as InstrumentList } from './_dts/index-iAP6XbH3.js';
6
+ export { C as CanvasCapability, b as CanvasLayer, D as DragDropCapability, c as DragFeedback, H as HitResult, L as LayerCapability, d as LayerDescriptor, P as PaletteItem, e as Point, R as RenderContext, S as SystemEvent, U as UndoCapability, V as ViewTransform } from './_dts/index-iAP6XbH3.js';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
  import * as react from 'react';
9
9
  import { ReactNode } from 'react';
10
- import { S as StorageAdapter, o as LabMode, W as WorkspaceRecord, e as SavedSnapshot } from './_dts/useExperimentState-D7EQnnwJ.js';
11
- export { C as CreateLabStoreOptions, E as ExperimentStateHandle, b as LabStoreContext, c as LabStoreProvider, d as LabStoreState, U as UndoStack, f as WorkspaceIdContext, g as WorkspaceIdProvider, i as createMemoryAdapter, l as localStorageAdapter, n as noneAdapter, s as sessionStorageAdapter, u as urlHashAdapter, j as useExperimentState, k as useLabStore, m as useWorkspaceId } from './_dts/useExperimentState-D7EQnnwJ.js';
10
+ import { a as StorageAdapter, w as LabMode, T as TrialRecord, h as SavedSnapshot } from './_dts/useTrialState-DYe2vUwN.js';
11
+ export { C as CURRENT_DOCUMENT_VERSION, b as CreateLabStoreOptions, L as LabDocument, e as LabStoreContext, f as LabStoreProvider, g as LabStoreState, S as SerializedTrial, i as TrialIdContext, j as TrialIdProvider, k as TrialStateHandle, U as UndoStack, m as createMemoryAdapter, n as labDocumentKey, o as localStorageAdapter, p as noneAdapter, q as quarantineKey, s as sessionStorageAdapter, u as urlHashAdapter, r as useLabStore, t as useTrialId, v as useTrialState } from './_dts/useTrialState-DYe2vUwN.js';
12
12
  export { LayerList, LayerListProps } from './layers/index.js';
13
13
  export { FpsMeter, ScaleIndicator, ScaleIndicatorProps, Sidebar, SidebarProps, StatusBar, StatusBarProps, Toolbar, ToolbarProps } from './primitives/index.js';
14
14
  export { LayerStack, LayerStackItem, LayerStackProps } from './ui/layers/index.js';
15
15
  export { EventBus, EventListener, clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './undo/index.js';
16
16
  import 'zustand/vanilla';
17
17
 
18
- /** Which optional capabilities an instrument declared — what the workspace
18
+ /** Which optional capabilities an instrument declared — what the trial
19
19
  * consults to decide which chrome to show. */
20
20
  interface CapabilityFlags {
21
21
  hasCanvas: boolean;
@@ -43,7 +43,7 @@ declare function validateConfigSchema(fields: ConfigField[]): ValidationResult;
43
43
 
44
44
  /** Props for `<Lab>`. */
45
45
  interface LabProps {
46
- instruments: Instrument[];
46
+ instruments: InstrumentList;
47
47
  defaultInstrument: string;
48
48
  storage?: StorageAdapter | null;
49
49
  storageKey?: string;
@@ -58,24 +58,24 @@ interface LabProps {
58
58
  title?: string;
59
59
  children?: ReactNode;
60
60
  }
61
- /** The lab runtime: creates the store, provides it, and renders one workspace
62
- * per record in a grid. Each workspace runs one of `instruments`. */
61
+ /** The lab runtime: creates the store, provides it, and renders one trial
62
+ * per record in a grid. Each trial runs one of `instruments`. */
63
63
  declare function Lab({ instruments, defaultInstrument, storage, storageKey, mode, nebula, title, children, }: LabProps): react_jsx_runtime.JSX.Element;
64
64
 
65
65
  /** Lab-wide state and commands: the available instruments, the open
66
- * workspaces and the operations over them, saved snapshots, and the color
66
+ * trials and the operations over them, saved snapshots, and the color
67
67
  * mode. */
68
68
  interface LabContextValue {
69
- instruments: Instrument[];
70
- workspaces: WorkspaceRecord[];
71
- addWorkspace: (instrumentName: string) => void;
72
- cloneWorkspace: (id: string) => void;
73
- closeWorkspace: (id: string) => void;
74
- resetWorkspace: (id: string) => void;
75
- reorderWorkspaces: (ids: readonly string[]) => void;
69
+ instruments: InstrumentList;
70
+ trials: TrialRecord[];
71
+ addTrial: (instrumentName: string) => void;
72
+ cloneTrial: (id: string) => void;
73
+ closeTrial: (id: string) => void;
74
+ resetTrial: (id: string) => void;
75
+ reorderTrials: (ids: readonly string[]) => void;
76
76
  savedSnapshots: SavedSnapshot[];
77
- saveSnapshot: (workspaceId: string, name?: string) => void;
78
- loadSnapshot: (workspaceId: string, snapshotId: string) => void;
77
+ saveSnapshot: (trialId: string, name?: string) => void;
78
+ loadSnapshot: (trialId: string, snapshotId: string) => void;
79
79
  deleteSnapshot: (snapshotId: string) => void;
80
80
  mode: LabMode;
81
81
  setMode: (m: LabMode) => void;
@@ -98,12 +98,12 @@ interface LabShellProps {
98
98
  }
99
99
  /** Page frame for a lab: a titled header, a body, and an optional footer,
100
100
  * themed for the resolved color mode. Presentational only — use `<Lab>` when
101
- * the workspace runtime is wanted too. */
101
+ * the trial runtime is wanted too. */
102
102
  declare function LabShell({ title, children, header, footer, mode }: LabShellProps): react_jsx_runtime.JSX.Element;
103
103
 
104
104
  /** A tile's persisted extent, keyed by the id its caller gave it. Grid resizes
105
105
  * write `span`; `size` is here because a strategy swap would write that. */
106
- type WorkspaceLayout = Record<string, {
106
+ type TrialLayout = Record<string, {
107
107
  size?: {
108
108
  w?: number;
109
109
  h?: number;
@@ -113,7 +113,7 @@ type WorkspaceLayout = Record<string, {
113
113
  rows?: number;
114
114
  };
115
115
  }>;
116
- interface WorkspaceGridProps {
116
+ interface WorkspaceProps {
117
117
  children: ReactNode;
118
118
  /**
119
119
  * Stable id per child, positionally matched. Supply these whenever a tile
@@ -133,10 +133,10 @@ interface WorkspaceGridProps {
133
133
  /** The full id list a drop would produce, in its new order. */
134
134
  onReorder?: (ids: string[]) => void;
135
135
  /** Extents from a previous session, applied to tiles as they register. */
136
- layout?: WorkspaceLayout;
136
+ layout?: TrialLayout;
137
137
  /** Fires when a tile's extent changes. Persist it and hand it back as
138
138
  * `layout` to make a resize survive a reload. */
139
- onLayoutChange?: (layout: WorkspaceLayout) => void;
139
+ onLayoutChange?: (layout: TrialLayout) => void;
140
140
  gap?: number;
141
141
  padding?: number;
142
142
  /**
@@ -150,13 +150,13 @@ interface WorkspaceGridProps {
150
150
  };
151
151
  }
152
152
  /**
153
- * Auto-balanced tiling of workspaces, `ceil(sqrt(n))` columns wide.
153
+ * Auto-balanced tiling of trials, `ceil(sqrt(n))` columns wide.
154
154
  *
155
155
  * Tiles are absolutely positioned at the rects `gridStrategy` computes, not
156
156
  * laid out by CSS — `windease/styles.css` (folded into
157
157
  * `@weasel-js/labkit/styles.css`) carries the rules that positioning depends on.
158
158
  */
159
- declare function WorkspaceGrid({ children, ids, resizable, reorderable, onReorder, layout, onLayoutChange, gap, padding, viewport, }: WorkspaceGridProps): react_jsx_runtime.JSX.Element;
159
+ declare function Workspace({ children, ids, resizable, reorderable, onReorder, layout, onLayoutChange, gap, padding, viewport, }: WorkspaceProps): react_jsx_runtime.JSX.Element;
160
160
 
161
161
  type TokenValue = string | number | readonly (string | number)[];
162
162
  /**
@@ -196,6 +196,128 @@ interface Theme {
196
196
  */
197
197
  declare const interstellarTheme: Theme;
198
198
 
199
+ /** What a trial hands its toolbar: which instrument is running, the undo
200
+ * state and commands, the zoom controls, and the snapshot commands. */
201
+ interface TrialToolbarContext {
202
+ trialId: string;
203
+ instrumentName: string;
204
+ hasUndo: boolean;
205
+ canUndo: boolean;
206
+ canRedo: boolean;
207
+ undo: () => void;
208
+ redo: () => void;
209
+ zoom: number;
210
+ setZoom: (z: number) => void;
211
+ zoomIn: () => void;
212
+ zoomOut: () => void;
213
+ resetZoom: () => void;
214
+ hasCanvas: boolean;
215
+ savedSnapshots: SavedSnapshot[];
216
+ saveSnapshot: (name?: string) => void;
217
+ loadSnapshot: (snapshotId: string) => void;
218
+ clone: () => void;
219
+ reset: () => void;
220
+ close: () => void;
221
+ isLastTrial: boolean;
222
+ }
223
+ /** What a trial hands its sidebar: the instrument's config schema, its
224
+ * current values, and the setter. */
225
+ interface TrialSidebarContext {
226
+ trialId: string;
227
+ instrumentName: string;
228
+ configFields: ConfigField[];
229
+ config: unknown;
230
+ setConfig: (key: string, value: unknown) => void;
231
+ }
232
+ /** What a trial hands its status bar. */
233
+ interface TrialStatusBarContext {
234
+ trialId: string;
235
+ instrumentName: string;
236
+ zoom: number;
237
+ }
238
+ /** Replaces a trial's toolbar. Receives everything the default one uses,
239
+ * so a custom toolbar need not reach into the store. */
240
+ type ToolbarSlot = (ctx: TrialToolbarContext) => ReactNode;
241
+ /** Replaces a trial's sidebar. */
242
+ type SidebarSlot = (ctx: TrialSidebarContext) => ReactNode;
243
+ /** Replaces a trial's status bar. */
244
+ type StatusBarSlot = (ctx: TrialStatusBarContext) => ReactNode;
245
+
246
+ /** Props for `<DefaultSidebar>`. */
247
+ interface DefaultSidebarProps {
248
+ ctx: TrialSidebarContext;
249
+ }
250
+ /** The sidebar a trial renders when no `sidebar` slot is supplied: a
251
+ * control panel over the instrument's config schema. */
252
+ declare function DefaultSidebar({ ctx }: DefaultSidebarProps): react_jsx_runtime.JSX.Element;
253
+
254
+ /** Props for `<DefaultStatusBar>`. */
255
+ interface DefaultStatusBarProps {
256
+ ctx: TrialStatusBarContext;
257
+ }
258
+ /** The status bar a trial renders when no `statusBar` slot is supplied:
259
+ * the instrument's name and the current zoom. */
260
+ declare function DefaultStatusBar({ ctx }: DefaultStatusBarProps): react_jsx_runtime.JSX.Element;
261
+
262
+ /** Props for `<DefaultToolbar>`. */
263
+ interface DefaultToolbarProps {
264
+ ctx: TrialToolbarContext;
265
+ }
266
+ /** The toolbar a trial renders when no `toolbar` slot is supplied. Only
267
+ * shows the controls the instrument's declared capabilities support. */
268
+ declare function DefaultToolbar({ ctx }: DefaultToolbarProps): react_jsx_runtime.JSX.Element;
269
+
270
+ /** Props for `<Trial>`. */
271
+ interface TrialProps {
272
+ id: string;
273
+ }
274
+ /** Renders one trial from the lab store: looks up its record and
275
+ * instrument, and mounts the instrument inside the trial chrome. */
276
+ declare function Trial({ id }: TrialProps): react_jsx_runtime.JSX.Element;
277
+
278
+ interface UndoBindings {
279
+ canUndo: boolean;
280
+ canRedo: boolean;
281
+ undo: () => void;
282
+ redo: () => void;
283
+ }
284
+ /** Props for `<TrialChrome>`. */
285
+ interface TrialChromeProps {
286
+ trialId: string;
287
+ record: TrialRecord;
288
+ instrument: Instrument;
289
+ isLastTrial: boolean;
290
+ toolbar?: ToolbarSlot;
291
+ sidebar?: SidebarSlot;
292
+ statusBar?: StatusBarSlot;
293
+ undoBindings?: UndoBindings;
294
+ sidebarExtras?: ReactNode;
295
+ children: ReactNode;
296
+ }
297
+ /** The frame around a running instrument — toolbar, sidebar, status bar —
298
+ * each replaceable by a slot. Assembles the slot contexts and wires the undo
299
+ * keyboard shortcuts. */
300
+ declare function TrialChrome({ trialId, record, instrument, isLastTrial, toolbar, sidebar, statusBar, undoBindings, sidebarExtras, children, }: TrialChromeProps): react_jsx_runtime.JSX.Element;
301
+
302
+ /** Append a new trial running `instrumentName`, at that instrument's
303
+ * default config and initial state. */
304
+ declare function addTrial(trials: TrialRecord[], instruments: InstrumentList, instrumentName: string): TrialRecord[];
305
+ /** Insert a deep copy of a trial directly after it. The copy starts with
306
+ * an empty undo history — the original's is not shared. */
307
+ declare function cloneTrial(trials: TrialRecord[], id: string): TrialRecord[];
308
+ /** Remove a trial, unless it is the last one — a lab always has at least
309
+ * one. */
310
+ declare function closeTrial(trials: TrialRecord[], id: string): TrialRecord[];
311
+ /** Return a trial to its instrument's defaults, keeping its id and its
312
+ * place in the list. */
313
+ declare function resetTrial(trials: TrialRecord[], id: string, instruments: InstrumentList): TrialRecord[];
314
+ /**
315
+ * Reorder to match `ids`. Ids the list doesn't mention keep their relative
316
+ * order at the end, and ids it names that no longer exist are dropped — a
317
+ * reorder that raced a close should not resurrect the closed trial.
318
+ */
319
+ declare function reorderTrials(trials: TrialRecord[], ids: readonly string[]): TrialRecord[];
320
+
199
321
  /** An annotation drawn over a curve plot: a shaded band across a range of x,
200
322
  * or a vertical line at one x. */
201
323
  type CurveMark = {
@@ -473,127 +595,5 @@ interface ToggleRowProps<T extends string> {
473
595
  * visible at once. */
474
596
  declare function ToggleRow<T extends string>({ label, value, options, onChange, layout, }: ToggleRowProps<T>): react_jsx_runtime.JSX.Element;
475
597
 
476
- /** What a workspace hands its toolbar: which instrument is running, the undo
477
- * state and commands, the zoom controls, and the snapshot commands. */
478
- interface WorkspaceToolbarContext {
479
- workspaceId: string;
480
- instrumentName: string;
481
- hasUndo: boolean;
482
- canUndo: boolean;
483
- canRedo: boolean;
484
- undo: () => void;
485
- redo: () => void;
486
- zoom: number;
487
- setZoom: (z: number) => void;
488
- zoomIn: () => void;
489
- zoomOut: () => void;
490
- resetZoom: () => void;
491
- hasCanvas: boolean;
492
- savedSnapshots: SavedSnapshot[];
493
- saveSnapshot: (name?: string) => void;
494
- loadSnapshot: (snapshotId: string) => void;
495
- clone: () => void;
496
- reset: () => void;
497
- close: () => void;
498
- isLastWorkspace: boolean;
499
- }
500
- /** What a workspace hands its sidebar: the instrument's config schema, its
501
- * current values, and the setter. */
502
- interface WorkspaceSidebarContext {
503
- workspaceId: string;
504
- instrumentName: string;
505
- configFields: ConfigField[];
506
- config: unknown;
507
- setConfig: (key: string, value: unknown) => void;
508
- }
509
- /** What a workspace hands its status bar. */
510
- interface WorkspaceStatusBarContext {
511
- workspaceId: string;
512
- instrumentName: string;
513
- zoom: number;
514
- }
515
- /** Replaces a workspace's toolbar. Receives everything the default one uses,
516
- * so a custom toolbar need not reach into the store. */
517
- type ToolbarSlot = (ctx: WorkspaceToolbarContext) => ReactNode;
518
- /** Replaces a workspace's sidebar. */
519
- type SidebarSlot = (ctx: WorkspaceSidebarContext) => ReactNode;
520
- /** Replaces a workspace's status bar. */
521
- type StatusBarSlot = (ctx: WorkspaceStatusBarContext) => ReactNode;
522
-
523
- /** Props for `<DefaultSidebar>`. */
524
- interface DefaultSidebarProps {
525
- ctx: WorkspaceSidebarContext;
526
- }
527
- /** The sidebar a workspace renders when no `sidebar` slot is supplied: a
528
- * control panel over the instrument's config schema. */
529
- declare function DefaultSidebar({ ctx }: DefaultSidebarProps): react_jsx_runtime.JSX.Element;
530
-
531
- /** Props for `<DefaultStatusBar>`. */
532
- interface DefaultStatusBarProps {
533
- ctx: WorkspaceStatusBarContext;
534
- }
535
- /** The status bar a workspace renders when no `statusBar` slot is supplied:
536
- * the instrument's name and the current zoom. */
537
- declare function DefaultStatusBar({ ctx }: DefaultStatusBarProps): react_jsx_runtime.JSX.Element;
538
-
539
- /** Props for `<DefaultToolbar>`. */
540
- interface DefaultToolbarProps {
541
- ctx: WorkspaceToolbarContext;
542
- }
543
- /** The toolbar a workspace renders when no `toolbar` slot is supplied. Only
544
- * shows the controls the instrument's declared capabilities support. */
545
- declare function DefaultToolbar({ ctx }: DefaultToolbarProps): react_jsx_runtime.JSX.Element;
546
-
547
- /** Props for `<Workspace>`. */
548
- interface WorkspaceProps {
549
- id: string;
550
- }
551
- /** Renders one workspace from the lab store: looks up its record and
552
- * instrument, and mounts the instrument inside the workspace chrome. */
553
- declare function Workspace({ id }: WorkspaceProps): react_jsx_runtime.JSX.Element;
554
-
555
- interface UndoBindings {
556
- canUndo: boolean;
557
- canRedo: boolean;
558
- undo: () => void;
559
- redo: () => void;
560
- }
561
- /** Props for `<WorkspaceChrome>`. */
562
- interface WorkspaceChromeProps {
563
- workspaceId: string;
564
- record: WorkspaceRecord;
565
- instrument: Instrument;
566
- isLastWorkspace: boolean;
567
- toolbar?: ToolbarSlot;
568
- sidebar?: SidebarSlot;
569
- statusBar?: StatusBarSlot;
570
- undoBindings?: UndoBindings;
571
- sidebarExtras?: ReactNode;
572
- children: ReactNode;
573
- }
574
- /** The frame around a running instrument — toolbar, sidebar, status bar —
575
- * each replaceable by a slot. Assembles the slot contexts and wires the undo
576
- * keyboard shortcuts. */
577
- declare function WorkspaceChrome({ workspaceId, record, instrument, isLastWorkspace, toolbar, sidebar, statusBar, undoBindings, sidebarExtras, children, }: WorkspaceChromeProps): react_jsx_runtime.JSX.Element;
578
-
579
- /** Append a new workspace running `instrumentName`, at that instrument's
580
- * default config and initial state. */
581
- declare function addWorkspace(workspaces: WorkspaceRecord[], instruments: Instrument[], instrumentName: string): WorkspaceRecord[];
582
- /** Insert a deep copy of a workspace directly after it. The copy starts with
583
- * an empty undo history — the original's is not shared. */
584
- declare function cloneWorkspace(workspaces: WorkspaceRecord[], id: string): WorkspaceRecord[];
585
- /** Remove a workspace, unless it is the last one — a lab always has at least
586
- * one. */
587
- declare function closeWorkspace(workspaces: WorkspaceRecord[], id: string): WorkspaceRecord[];
588
- /** Return a workspace to its instrument's defaults, keeping its id and its
589
- * place in the list. */
590
- declare function resetWorkspace(workspaces: WorkspaceRecord[], id: string, instruments: Instrument[]): WorkspaceRecord[];
591
- /**
592
- * Reorder to match `ids`. Ids the list doesn't mention keep their relative
593
- * order at the end, and ids it names that no longer exist are dropped — a
594
- * reorder that raced a close should not resurrect the closed workspace.
595
- */
596
- declare function reorderWorkspaces(workspaces: WorkspaceRecord[], ids: readonly string[]): WorkspaceRecord[];
597
-
598
- export { CheckboxRow, ColorRow, ConfigField, CurveField, DefaultSidebar, DefaultStatusBar, DefaultToolbar, EffectCard, EffectCardList, Instrument, Lab, LabContext, LabMode, LabShell, NumberRow, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Workspace, WorkspaceChrome, WorkspaceGrid, WorkspaceRecord, addWorkspace, cloneWorkspace, closeWorkspace, defineInstrument, detectCapabilities, interstellarTheme, reorderWorkspaces, resetWorkspace, useLabContext, validateConfigSchema };
599
- export type { CapabilityFlags, CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, DefaultSidebarProps, DefaultStatusBarProps, DefaultToolbarProps, EffectCardListItem, EffectCardListProps, EffectCardProps, LabContextValue, LabProps, LabShellProps, NumberRowProps, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SidebarSlot, SliderRowProps, StatusBarSlot, SubpanelProps, TextRowProps, ToggleRowProps, ToolbarSlot, ValidationResult, WorkspaceChromeProps, WorkspaceGridProps, WorkspaceProps, WorkspaceSidebarContext, WorkspaceStatusBarContext, WorkspaceToolbarContext };
598
+ export { CheckboxRow, ColorRow, ConfigField, CurveField, DefaultSidebar, DefaultStatusBar, DefaultToolbar, EffectCard, EffectCardList, Instrument, InstrumentList, Lab, LabContext, LabMode, LabShell, NumberRow, PropertyGroup, PropertyList, PropertyPanel, PropertyRow, SavedSnapshot, SelectRow, SliderRow, StorageAdapter, Subpanel, TextRow, ToggleRow, Trial, TrialChrome, TrialRecord, Workspace, addTrial, cloneTrial, closeTrial, defineInstrument, detectCapabilities, interstellarTheme, reorderTrials, resetTrial, useLabContext, validateConfigSchema };
599
+ export type { CapabilityFlags, CheckboxRowProps, ColorRowProps, CurveFieldProps, CurveMark, DefaultSidebarProps, DefaultStatusBarProps, DefaultToolbarProps, EffectCardListItem, EffectCardListProps, EffectCardProps, LabContextValue, LabProps, LabShellProps, NumberRowProps, PropertyGroupProps, PropertyListPack, PropertyListProps, PropertyPanelProps, PropertyRowLayout, PropertyRowProps, PropertyRowVariant, SelectRowProps, SidebarSlot, SliderRowProps, StatusBarSlot, SubpanelProps, TextRowProps, ToggleRowProps, ToolbarSlot, TrialChromeProps, TrialProps, TrialSidebarContext, TrialStatusBarContext, TrialToolbarContext, ValidationResult, WorkspaceProps };