@weasel-js/ui 1.4.4 → 1.5.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 (161) hide show
  1. package/dist/chunks/{BandEditor-CUlbOlJi.js → BandEditor-BeWO56_w.js} +1 -1
  2. package/dist/chunks/{BandEditor-CUlbOlJi.js.map → BandEditor-BeWO56_w.js.map} +1 -1
  3. package/dist/chunks/{Callout-Dvt6lqVn.js → Callout-BPIA2is-.js} +1 -1
  4. package/dist/chunks/{Callout-Dvt6lqVn.js.map → Callout-BPIA2is-.js.map} +1 -1
  5. package/dist/chunks/ComboBox-CPES6Y1i.js +138 -0
  6. package/dist/chunks/ComboBox-CPES6Y1i.js.map +1 -0
  7. package/dist/chunks/CurveEditor-Cj0lLiyR.js +533 -0
  8. package/dist/chunks/CurveEditor-Cj0lLiyR.js.map +1 -0
  9. package/dist/chunks/{DetentSlider-BpD7bgjN.js → DetentSlider-CYicfnKr.js} +2 -2
  10. package/dist/chunks/{DetentSlider-BpD7bgjN.js.map → DetentSlider-CYicfnKr.js.map} +1 -1
  11. package/dist/chunks/{GradientEditor-BLpAtMDK.js → GradientEditor-dBprHbS0.js} +2 -2
  12. package/dist/chunks/{GradientEditor-BLpAtMDK.js.map → GradientEditor-dBprHbS0.js.map} +1 -1
  13. package/dist/chunks/MenuButton-Do8fFUx1.js +60 -0
  14. package/dist/chunks/MenuButton-Do8fFUx1.js.map +1 -0
  15. package/dist/chunks/NumberField-BWzhqS19.js +112 -0
  16. package/dist/chunks/NumberField-BWzhqS19.js.map +1 -0
  17. package/dist/chunks/{PaintInput-B7AScmHN.js → PaintInput-DAevU7fd.js} +2 -2
  18. package/dist/chunks/{PaintInput-B7AScmHN.js.map → PaintInput-DAevU7fd.js.map} +1 -1
  19. package/dist/chunks/Plot2D-CVZwQpWJ.js +292 -0
  20. package/dist/chunks/Plot2D-CVZwQpWJ.js.map +1 -0
  21. package/dist/chunks/{PointPlotter-CbDKVggs.js → PointPlotter-anTHwz8f.js} +2 -2
  22. package/dist/chunks/PointPlotter-anTHwz8f.js.map +1 -0
  23. package/dist/chunks/Prefs-CzIn-hlq.js +302 -0
  24. package/dist/chunks/Prefs-CzIn-hlq.js.map +1 -0
  25. package/dist/chunks/Properties-DJaDEJkT.js +765 -0
  26. package/dist/chunks/Properties-DJaDEJkT.js.map +1 -0
  27. package/dist/chunks/{ResizeHandle-BrCO0JrH.js → ResizeHandle-CiExUWP-.js} +1 -1
  28. package/dist/chunks/{ResizeHandle-BrCO0JrH.js.map → ResizeHandle-CiExUWP-.js.map} +1 -1
  29. package/dist/chunks/{SelectionPanel-C59wavdA.js → SelectionPanel-CbyUT3Dh.js} +204 -194
  30. package/dist/chunks/SelectionPanel-CbyUT3Dh.js.map +1 -0
  31. package/dist/chunks/{Sidebar-BvwjZiFm.js → Sidebar-Cqcfx3_s.js} +1 -1
  32. package/dist/chunks/{Sidebar-BvwjZiFm.js.map → Sidebar-Cqcfx3_s.js.map} +1 -1
  33. package/dist/chunks/{SidebarPanel-D5Ua2vyP.js → SidebarPanel-BLzBo0S2.js} +1 -1
  34. package/dist/chunks/{SidebarPanel-D5Ua2vyP.js.map → SidebarPanel-BLzBo0S2.js.map} +1 -1
  35. package/dist/chunks/{Slider-Bs-KSYgQ.js → Slider--DFRC_hc.js} +2 -2
  36. package/dist/chunks/{Slider-Bs-KSYgQ.js.map → Slider--DFRC_hc.js.map} +1 -1
  37. package/dist/chunks/{StatusBar-Bqv4D0dW.js → StatusBar-B9NQ8atD.js} +1 -1
  38. package/dist/chunks/{StatusBar-Bqv4D0dW.js.map → StatusBar-B9NQ8atD.js.map} +1 -1
  39. package/dist/chunks/{Tabs-BT3CDkoQ.js → Tabs-9sODd2Ut.js} +1 -1
  40. package/dist/chunks/{Tabs-BT3CDkoQ.js.map → Tabs-9sODd2Ut.js.map} +1 -1
  41. package/dist/chunks/Timeline-DOZgb1CG.js +915 -0
  42. package/dist/chunks/Timeline-DOZgb1CG.js.map +1 -0
  43. package/dist/chunks/{Toast-B7ylq7fz.js → Toast-LpN1WK8E.js} +1 -1
  44. package/dist/chunks/{Toast-B7ylq7fz.js.map → Toast-LpN1WK8E.js.map} +1 -1
  45. package/dist/chunks/{ToolButton-CtDkqUIc.js → ToolButton-B81YjdwO.js} +1 -1
  46. package/dist/chunks/{ToolButton-CtDkqUIc.js.map → ToolButton-B81YjdwO.js.map} +1 -1
  47. package/dist/chunks/{ToolGroup-zDtqt0QJ.js → ToolGroup-D7juDOC_.js} +1 -1
  48. package/dist/chunks/{ToolGroup-zDtqt0QJ.js.map → ToolGroup-D7juDOC_.js.map} +1 -1
  49. package/dist/chunks/{ToolPalette-BbrMHfQ9.js → ToolPalette-BSzmRJS7.js} +3 -3
  50. package/dist/chunks/{ToolPalette-BbrMHfQ9.js.map → ToolPalette-BSzmRJS7.js.map} +1 -1
  51. package/dist/chunks/createKeyframeLayer-AL4jP0EX.js +694 -0
  52. package/dist/chunks/createKeyframeLayer-AL4jP0EX.js.map +1 -0
  53. package/dist/chunks/number-f6PydOZK.js +52 -0
  54. package/dist/chunks/number-f6PydOZK.js.map +1 -0
  55. package/dist/chunks/{schema-CtWwGbqJ.js → schema-CPdFNeP6.js} +15 -9
  56. package/dist/chunks/schema-CPdFNeP6.js.map +1 -0
  57. package/dist/components/BandEditor/index.js +1 -1
  58. package/dist/components/Callout/index.js +1 -1
  59. package/dist/components/ComboBox/ComboBox.d.ts +36 -0
  60. package/dist/components/ComboBox/ComboBox.d.ts.map +1 -1
  61. package/dist/components/ComboBox/index.d.ts +1 -1
  62. package/dist/components/ComboBox/index.d.ts.map +1 -1
  63. package/dist/components/ComboBox/index.js +1 -1
  64. package/dist/components/CurveEditor/CurveEditor.d.ts +4 -2
  65. package/dist/components/CurveEditor/CurveEditor.d.ts.map +1 -1
  66. package/dist/components/CurveEditor/LayeredCurveEditor.d.ts +8 -1
  67. package/dist/components/CurveEditor/LayeredCurveEditor.d.ts.map +1 -1
  68. package/dist/components/CurveEditor/createFunctionLayer.d.ts +2 -0
  69. package/dist/components/CurveEditor/createFunctionLayer.d.ts.map +1 -1
  70. package/dist/components/CurveEditor/createKeyframeLayer.d.ts +70 -0
  71. package/dist/components/CurveEditor/createKeyframeLayer.d.ts.map +1 -0
  72. package/dist/components/CurveEditor/index.d.ts +3 -1
  73. package/dist/components/CurveEditor/index.d.ts.map +1 -1
  74. package/dist/components/CurveEditor/index.js +3 -2
  75. package/dist/components/CurveEditor/layerTypes.d.ts +8 -3
  76. package/dist/components/CurveEditor/layerTypes.d.ts.map +1 -1
  77. package/dist/components/CurveEditor/snap.d.ts +3 -0
  78. package/dist/components/CurveEditor/snap.d.ts.map +1 -0
  79. package/dist/components/DetentSlider/index.js +1 -1
  80. package/dist/components/GradientEditor/index.js +1 -1
  81. package/dist/components/MenuButton/MenuButton.d.ts +29 -0
  82. package/dist/components/MenuButton/MenuButton.d.ts.map +1 -0
  83. package/dist/components/MenuButton/index.d.ts +3 -0
  84. package/dist/components/MenuButton/index.d.ts.map +1 -0
  85. package/dist/components/MenuButton/index.js +2 -0
  86. package/dist/components/NumberField/UnitField.d.ts +30 -0
  87. package/dist/components/NumberField/UnitField.d.ts.map +1 -0
  88. package/dist/components/NumberField/index.d.ts +2 -0
  89. package/dist/components/NumberField/index.d.ts.map +1 -1
  90. package/dist/components/NumberField/index.js +2 -2
  91. package/dist/components/PaintInput/index.js +1 -1
  92. package/dist/components/Plot2D/Plot2D.d.ts +38 -2
  93. package/dist/components/Plot2D/Plot2D.d.ts.map +1 -1
  94. package/dist/components/Plot2D/index.d.ts +2 -1
  95. package/dist/components/Plot2D/index.d.ts.map +1 -1
  96. package/dist/components/Plot2D/index.js +2 -2
  97. package/dist/components/Plot2D/ticks.d.ts +43 -0
  98. package/dist/components/Plot2D/ticks.d.ts.map +1 -0
  99. package/dist/components/PointPlotter/PointPlotter.d.ts +3 -1
  100. package/dist/components/PointPlotter/PointPlotter.d.ts.map +1 -1
  101. package/dist/components/PointPlotter/index.js +1 -1
  102. package/dist/components/Prefs/PrefsForm.d.ts.map +1 -1
  103. package/dist/components/Prefs/index.d.ts +1 -1
  104. package/dist/components/Prefs/index.d.ts.map +1 -1
  105. package/dist/components/Prefs/index.js +3 -3
  106. package/dist/components/Prefs/schema.d.ts +5 -2
  107. package/dist/components/Prefs/schema.d.ts.map +1 -1
  108. package/dist/components/Properties/PropertyPanel.d.ts +15 -6
  109. package/dist/components/Properties/PropertyPanel.d.ts.map +1 -1
  110. package/dist/components/Properties/index.js +1 -1
  111. package/dist/components/ResizeHandle/index.js +1 -1
  112. package/dist/components/SelectionPanel/index.js +1 -1
  113. package/dist/components/Sidebar/index.js +1 -1
  114. package/dist/components/SidebarPanel/index.js +1 -1
  115. package/dist/components/Slider/index.js +1 -1
  116. package/dist/components/StatusBar/index.js +1 -1
  117. package/dist/components/Tabs/index.js +1 -1
  118. package/dist/components/Timeline/Lane.d.ts +9 -7
  119. package/dist/components/Timeline/Lane.d.ts.map +1 -1
  120. package/dist/components/Timeline/LaneGraph.d.ts +24 -0
  121. package/dist/components/Timeline/LaneGraph.d.ts.map +1 -0
  122. package/dist/components/Timeline/Ruler.d.ts.map +1 -1
  123. package/dist/components/Timeline/Timeline.d.ts.map +1 -1
  124. package/dist/components/Timeline/index.js +1 -1
  125. package/dist/components/Timeline/keys.d.ts +4 -3
  126. package/dist/components/Timeline/keys.d.ts.map +1 -1
  127. package/dist/components/Timeline/timeScale.d.ts +2 -0
  128. package/dist/components/Timeline/timeScale.d.ts.map +1 -1
  129. package/dist/components/Toast/index.js +1 -1
  130. package/dist/components/ToolButton/index.js +1 -1
  131. package/dist/components/ToolGroup/index.js +1 -1
  132. package/dist/components/ToolPalette/index.js +1 -1
  133. package/dist/format/number.d.ts +16 -0
  134. package/dist/format/number.d.ts.map +1 -1
  135. package/dist/index.d.ts +4 -1
  136. package/dist/index.d.ts.map +1 -1
  137. package/dist/index.js +99 -64
  138. package/dist/index.js.map +1 -1
  139. package/dist/style.css +1 -1
  140. package/dist/useAsyncOptions.d.ts +38 -0
  141. package/dist/useAsyncOptions.d.ts.map +1 -0
  142. package/package.json +7 -6
  143. package/dist/chunks/ComboBox-BBRRWvdc.js +0 -107
  144. package/dist/chunks/ComboBox-BBRRWvdc.js.map +0 -1
  145. package/dist/chunks/CurveEditor-DtjdpJVB.js +0 -784
  146. package/dist/chunks/CurveEditor-DtjdpJVB.js.map +0 -1
  147. package/dist/chunks/NumberField-DBowmOfe.js +0 -62
  148. package/dist/chunks/NumberField-DBowmOfe.js.map +0 -1
  149. package/dist/chunks/Plot2D-C97pO13t.js +0 -134
  150. package/dist/chunks/Plot2D-C97pO13t.js.map +0 -1
  151. package/dist/chunks/PointPlotter-CbDKVggs.js.map +0 -1
  152. package/dist/chunks/Prefs-4JSEtuU-.js +0 -294
  153. package/dist/chunks/Prefs-4JSEtuU-.js.map +0 -1
  154. package/dist/chunks/Properties-BpUYKi7-.js +0 -758
  155. package/dist/chunks/Properties-BpUYKi7-.js.map +0 -1
  156. package/dist/chunks/SelectionPanel-C59wavdA.js.map +0 -1
  157. package/dist/chunks/Timeline-Bz806JbE.js +0 -908
  158. package/dist/chunks/Timeline-Bz806JbE.js.map +0 -1
  159. package/dist/chunks/number-Bv1nbhOJ.js +0 -18
  160. package/dist/chunks/number-Bv1nbhOJ.js.map +0 -1
  161. package/dist/chunks/schema-CtWwGbqJ.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createKeyframeLayer-AL4jP0EX.js","names":[],"sources":["../../src/components/CurveEditor/hitTest.ts","../../src/components/CurveEditor/CurveEditor.module.css","../../src/components/CurveEditor/LayeredCurveEditor.tsx","../../src/components/CurveEditor/snap.ts","../../src/components/CurveEditor/createKeyframeLayer.tsx"],"sourcesContent":["/**\n * Curve-specific hit tests. Generic coord transforms live in\n * `../Plot2D/geometry`; what's here is curve-only (anchor / polyline\n * proximity).\n */\nimport type { Point } from '../Plot2D/geometry';\n\n/**\n * Nearest-anchor hit test. Anchors are already in plot/screen coords.\n * Returns the index of the nearest anchor whose squared distance to\n * the pointer is ≤ radius². Null if no anchor is within radius.\n */\nexport function hitTestAnchor(\n anchors: readonly Point[],\n pointer: Point,\n radius: number,\n): { index: number } | null {\n const r2 = radius * radius;\n let best: { index: number; d2: number } | null = null;\n for (let i = 0; i < anchors.length; i++) {\n const a = anchors[i];\n const dx = a.x - pointer.x;\n const dy = a.y - pointer.y;\n const d2 = dx * dx + dy * dy;\n if (d2 > r2) continue;\n if (best === null || d2 < best.d2) best = { index: i, d2 };\n }\n return best ? { index: best.index } : null;\n}\n\n/**\n * Hit test against a per-segment sampled polyline. Each entry in\n * `segments` is the sample list for one segment (inclusive of\n * endpoints). Returns segment index + t (∈ [0, 1]) of the closest\n * point on the polyline within radius. Null if nothing is in range.\n *\n * Distance is measured to the line-segment chords between adjacent\n * samples, not just to the samples themselves — so sparse sampling\n * still produces accurate hits along the chord.\n */\nexport function hitTestCurve(\n segments: readonly (readonly Point[])[],\n pointer: Point,\n radius: number,\n): { segIdx: number; t: number } | null {\n const r2 = radius * radius;\n let best: { segIdx: number; t: number; d2: number } | null = null;\n for (let segIdx = 0; segIdx < segments.length; segIdx++) {\n const samples = segments[segIdx];\n if (samples.length < 2) continue;\n const last = samples.length - 1;\n for (let i = 0; i < last; i++) {\n const a = samples[i];\n const b = samples[i + 1];\n const abx = b.x - a.x;\n const aby = b.y - a.y;\n const apx = pointer.x - a.x;\n const apy = pointer.y - a.y;\n const ab2 = abx * abx + aby * aby;\n let u = ab2 > 0 ? (apx * abx + apy * aby) / ab2 : 0;\n if (u < 0) u = 0;\n else if (u > 1) u = 1;\n const cx = a.x + u * abx;\n const cy = a.y + u * aby;\n const dx = cx - pointer.x;\n const dy = cy - pointer.y;\n const d2 = dx * dx + dy * dy;\n if (d2 > r2) continue;\n const t = (i + u) / last;\n if (best === null || d2 < best.d2) best = { segIdx, t, d2 };\n }\n }\n return best ? { segIdx: best.segIdx, t: best.t } : null;\n}\n",".root {\n /* Component-local vars layered over the theme tokens. Each var resolves\n * to a semantic token from @weasel-js/theme; consumers override\n * either the curve-* var (component scope) or the wzl-* token (app\n * scope) without touching this file.\n *\n * Background / grid / axis tokens live on Plot2D now (this component\n * composes Plot2D, which paints the background and chrome). */\n --curve-line: var(--wzl-curve-color);\n --curve-line-width: var(--wzl-curve-width);\n --curve-fill: color-mix(in srgb, var(--curve-line) 30%, transparent);\n --curve-anchor-fill: var(--wzl-surface);\n --curve-anchor-stroke: var(--curve-line);\n --curve-anchor-radius: 4;\n --curve-anchor-active-fill: var(--curve-line);\n --curve-pinned-fill: var(--wzl-fg-subtle);\n}\n\n.fill {\n fill: var(--curve-fill);\n stroke: none;\n pointer-events: none;\n}\n\n.curve {\n stroke: var(--curve-line);\n stroke-width: var(--curve-line-width);\n fill: none;\n pointer-events: stroke;\n}\n\n.anchor {\n fill: var(--curve-anchor-fill);\n stroke: var(--curve-anchor-stroke);\n stroke-width: 1.5;\n cursor: grab;\n}\n\n.anchor:hover {\n fill: var(--curve-anchor-active-fill);\n}\n\n.anchor.active {\n fill: var(--curve-anchor-active-fill);\n cursor: grabbing;\n}\n\n.anchor.pinned {\n fill: var(--curve-pinned-fill);\n cursor: default;\n}\n\n/* Caller-set locked anchors share the gray pinned fill but signal\n * \"not interactive at all\" via the not-allowed cursor. */\n.anchor.locked {\n cursor: not-allowed;\n}\n\n/* Endpoint vertices render as diamonds (rotated squares) instead of\n * circles, so they read as a different category of handle. The shape\n * is enforced inline via SVG <rect transform>; this class only adds\n * the hover/active styling. */\n.endpoint {\n /* No fill / stroke overrides — endpoint visuals reuse .anchor /\n * .anchor.pinned / .anchor.active styling. The class exists as a\n * marker for future per-endpoint styling needs. */\n}\n\n/* When the consumer opts into hiding non-interactive handles, pinned\n * endpoints disappear entirely. */\n.hidden {\n display: none;\n}\n\n/* Focus is drawn on the mark itself: an outline on an SVG shape rings its\n bounding box, which the nearest scroll container clips. */\n.anchor:focus-visible {\n outline: none;\n stroke: var(--wzl-focus-ring);\n stroke-width: 2;\n}\n\n/* ── keyframe layer ─────────────────────────────────────────────────── */\n\n.keyframe {\n fill: var(--curve-key-fill, var(--wzl-fg-muted));\n cursor: grab;\n}\n\n.keyframe[aria-current='true'] { fill: var(--curve-selected, var(--wzl-accent-strong)); }\n.keyframe:focus-visible,\n.handle:focus-visible {\n outline: none;\n stroke: var(--wzl-focus-ring);\n stroke-width: 2;\n}\n\n.segmentTarget:focus-visible {\n outline: none;\n stroke: var(--wzl-focus-ring);\n stroke-opacity: 0.5;\n}\n\n/* The committed key holds while its ghost tracks the pointer, so a drag\n reads as a move from somewhere to somewhere. */\n.keyframe[data-dragging='true'] { opacity: 0.4; }\n\n.keyframeGhost {\n fill: var(--curve-selected-soft, var(--wzl-accent-soft));\n stroke: var(--curve-selected, var(--wzl-accent-strong));\n stroke-width: 1;\n stroke-dasharray: 2 2;\n pointer-events: none;\n}\n\n.segmentBand {\n fill: var(--curve-selected-soft, var(--wzl-accent-soft));\n pointer-events: none;\n}\n\n/* Carries focus and a name for each segment; hit testing is geometric. */\n.segmentTarget {\n fill: none;\n stroke: transparent;\n stroke-width: 8;\n pointer-events: none;\n}\n\n.handle {\n fill: var(--wzl-surface);\n stroke: var(--curve-selected, var(--wzl-accent-strong));\n stroke-width: 1;\n cursor: grab;\n}\n\n.handleStem {\n stroke: var(--curve-selected, var(--wzl-accent-strong));\n stroke-width: 1;\n pointer-events: none;\n}\n","import {\n useCallback, useEffect, useMemo, useRef, useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from 'react';\nimport { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport {\n Plot2D,\n type Plot2DHandle,\n type GridSettings,\n type AxesSettings,\n type TickSettings,\n} from '../Plot2D';\nimport {\n modelToPlot, plotToModel,\n type ModelRange,\n} from '../Plot2D/geometry';\nimport { dlog } from '../../dlog';\nimport s from './CurveEditor.module.css';\nimport type {\n CurveLayer, LayerCtx, LayerGesture, LayerModifiers, ModelPoint, PlotPoint,\n} from './layerTypes';\n\n/**\n * A layer paired with the state the consumer holds for it. The editor never\n * stores layer state itself.\n */\nexport interface LayerBinding<S = unknown> {\n layer: CurveLayer<S>;\n state: S;\n}\n\n/** Props for {@link LayeredCurveEditor}. */\nexport interface LayeredCurveEditorProps {\n /** Bottom-to-top render order: `layers[0]` paints first, `layers[N-1]`\n * paints on top. Hit-testing runs in reverse (topmost claim wins). */\n layers: ReadonlyArray<LayerBinding<unknown>>;\n\n /** Fires every frame during a gesture with the live in-flight state\n * for the layer that owns the gesture. */\n onLayerChange(id: string, next: unknown): void;\n\n /** Fires once per discrete user action (drag-end, one-shot insert,\n * delete) for the layer that produced the change. `prev` is the\n * layer's state at gesture start. */\n onLayerCommit?(id: string, next: unknown, prev: unknown): void;\n\n width: number;\n height: number;\n /** Model-space x range. Default [0, 1]. */\n xRange?: readonly [number, number];\n /** Model-space y range. Default [0, 1]. */\n yRange?: readonly [number, number];\n grid?: GridSettings | false | null;\n axes?: AxesSettings | false | null;\n /** Ticks at x values. See `Plot2D.xTicks`. */\n xTicks?: TickSettings | false | null;\n /** Ticks at y values. See `Plot2D.yTicks`. */\n yTicks?: TickSettings | false | null;\n\n /** Built-in undo/redo via Cmd/Ctrl+Z (Shift+Z or Y for redo) on the\n * focused SVG. Snapshots every layer's state on each commit; undo\n * fires `onLayerChange(id, prevState)` for each layer whose state\n * changed. Default `true`. */\n history?: boolean;\n\n /** Accessible name for the plot, which is exposed as a group so the\n * focusable marks layers render inside it stay reachable. */\n 'aria-label'?: string;\n className?: string;\n style?: CSSProperties;\n\n /** Extra SVG content rendered behind all layers (under the lowest\n * layer but above grid/axes). Use this for static chrome that isn't\n * worth modeling as a layer. */\n children?: ReactNode;\n}\n\ninterface ActiveGesture {\n layerId: string;\n gesture: LayerGesture<unknown>;\n startStates: Map<string, unknown>;\n startSelfState: unknown;\n}\n\n/** A cancel carries no event, so a layer's `onCancel` sees no modifiers held. */\nconst NO_MODIFIERS: LayerModifiers = { shift: false, alt: false, meta: false, ctrl: false };\n\nfunction readModifiers(e: PointerEvent | ReactPointerEvent | KeyboardEvent | ReactKeyboardEvent): LayerModifiers {\n return {\n shift: e.shiftKey,\n alt: e.altKey,\n meta: e.metaKey,\n ctrl: e.ctrlKey,\n };\n}\n\n/**\n * A `Plot2D` that hosts a stack of composable curve layers. It owns the\n * pointer gestures and the coordinate mapping; each layer supplies its own\n * drawing, hit testing and gesture behavior.\n *\n * Layer state stays with the consumer: the editor reports changes through\n * `onLayerChange` during a gesture and `onLayerCommit` at its end, and reads\n * the state back from props each frame. That is what lets one layer's drag\n * update another layer's state mid-gesture.\n */\nexport function LayeredCurveEditor(props: LayeredCurveEditorProps) {\n const {\n layers, onLayerChange, onLayerCommit,\n width, height,\n xRange = [0, 1], yRange = [0, 1],\n grid, axes, xTicks, yTicks,\n className, style, children,\n } = props;\n\n const historyEnabled = props.history !== false;\n\n const modelRange: ModelRange = useMemo(\n () => ({ xMin: xRange[0], xMax: xRange[1], yMin: yRange[0], yMax: yRange[1] }),\n [xRange, yRange],\n );\n const plotSize = useMemo(() => ({ width, height }), [width, height]);\n\n // Refs that always hold the latest props — gesture handlers and\n // window listeners close over these refs, never over render-time\n // values, so mid-drag re-renders (including cross-layer state\n // updates) are visible to the next pointermove tick.\n const layersRef = useRef(layers);\n layersRef.current = layers;\n const modelRangeRef = useRef(modelRange);\n modelRangeRef.current = modelRange;\n const plotSizeRef = useRef(plotSize);\n plotSizeRef.current = plotSize;\n\n const plotRef = useRef<Plot2DHandle | null>(null);\n\n // ── Coord helpers ──────────────────────────────────────────────────\n const makeCtx = useCallback((modifiers: LayerModifiers): LayerCtx => {\n const mr = modelRangeRef.current;\n const ps = plotSizeRef.current;\n return {\n modelRange: mr,\n plotSize: ps,\n toPlot: (p) => modelToPlot(p, mr, ps),\n toModel: (p) => plotToModel(p, mr, ps),\n modifiers,\n };\n }, []);\n\n // ── Gesture routing ────────────────────────────────────────────────\n const activeRef = useRef(null as ActiveGesture | null);\n const [activeLayerId, setActiveLayerId] = useState<string | null>(null);\n\n // ── History (whole-state snapshots) ────────────────────────────────\n const pastRef = useRef<Map<string, unknown>[]>([]);\n const futureRef = useRef<Map<string, unknown>[]>([]);\n\n const snapshot = useCallback((): Map<string, unknown> => {\n const m = new Map<string, unknown>();\n for (const b of layersRef.current) m.set(b.layer.id, b.state);\n return m;\n }, []);\n\n const restore = useCallback((snap: Map<string, unknown>) => {\n const current = layersRef.current;\n for (const b of current) {\n const prev = snap.get(b.layer.id);\n if (prev !== undefined && prev !== b.state) {\n onLayerChange(b.layer.id, prev);\n }\n }\n }, [onLayerChange]);\n\n const undo = useCallback(() => {\n if (!historyEnabled) return;\n const past = pastRef.current;\n if (past.length === 0) return;\n const prev = past.pop()!;\n futureRef.current.push(snapshot());\n restore(prev);\n }, [historyEnabled, snapshot, restore]);\n\n const redo = useCallback(() => {\n if (!historyEnabled) return;\n const future = futureRef.current;\n if (future.length === 0) return;\n const next = future.pop()!;\n pastRef.current.push(snapshot());\n restore(next);\n }, [historyEnabled, snapshot, restore]);\n\n // ── Pointer routing during an active gesture ───────────────────────\n // The session's callbacks are fixed at gesture start, so they delegate\n // through refs holding the latest per-render impl — a mid-drag re-render\n // must reach the next pointermove tick.\n const onGestureMoveRef = useRef<(e: PointerEvent) => void>(() => {});\n const onGestureEndRef = useRef<(e: PointerEvent) => void>(() => {});\n const onGestureCancelRef = useRef<() => void>(() => {});\n\n const sessionRef = useRef<PointerSession | null>(null);\n\n const cleanupGesture = useCallback(() => {\n sessionRef.current = null;\n activeRef.current = null;\n setActiveLayerId(null);\n }, []);\n\n const findLayerState = useCallback((id: string): unknown | undefined => {\n for (const b of layersRef.current) if (b.layer.id === id) return b.state;\n return undefined;\n }, []);\n\n onGestureMoveRef.current = (e: PointerEvent) => {\n const a = activeRef.current;\n if (!a) return;\n const h = plotRef.current;\n if (!h) return;\n const model = h.clientToModel(e);\n const current = findLayerState(a.layerId);\n if (current === undefined) return;\n const ctx = makeCtx(readModifiers(e));\n const next = a.gesture.onMove(current, model, e, ctx);\n if (next !== current) onLayerChange(a.layerId, next);\n };\n\n onGestureEndRef.current = (e: PointerEvent) => {\n const a = activeRef.current;\n if (!a) return;\n const current = findLayerState(a.layerId);\n if (current === undefined) { cleanupGesture(); return; }\n const ctx = makeCtx(readModifiers(e));\n const finalState = a.gesture.onCommit ? a.gesture.onCommit(current, ctx) : current;\n if (finalState !== current) onLayerChange(a.layerId, finalState);\n if (historyEnabled) {\n pastRef.current.push(a.startStates);\n futureRef.current = [];\n }\n if (onLayerCommit) onLayerCommit(a.layerId, finalState, a.startSelfState);\n cleanupGesture();\n };\n\n onGestureCancelRef.current = () => {\n const a = activeRef.current;\n if (!a) return;\n const current = findLayerState(a.layerId);\n if (current === undefined) { cleanupGesture(); return; }\n const ctx = makeCtx(NO_MODIFIERS);\n const restored = a.gesture.onCancel ? a.gesture.onCancel(current, ctx) : a.startSelfState;\n if (restored !== current) onLayerChange(a.layerId, restored);\n cleanupGesture();\n };\n\n // No pointer capture: consumers render their own SVG chrome into the plot\n // through `children`, and capture would retarget pointerup away from it.\n const installGesture = useCallback((\n layerId: string,\n down: ReactPointerEvent<SVGSVGElement>,\n gesture: LayerGesture<unknown>,\n ) => {\n const startStates = snapshot();\n const startSelfState = startStates.get(layerId);\n activeRef.current = {\n layerId, gesture,\n startStates,\n startSelfState,\n };\n setActiveLayerId(layerId);\n sessionRef.current = openPointerSession(down.currentTarget, down, {\n onMove: (e) => onGestureMoveRef.current(e),\n onEnd: (e) => onGestureEndRef.current(e),\n onCancel: () => onGestureCancelRef.current(),\n }, { capture: false });\n }, [snapshot]);\n\n // ── pointerdown dispatch ───────────────────────────────────────────\n const onSvgPointerDown = useCallback((\n e: ReactPointerEvent<SVGSVGElement>,\n coords: { plot: PlotPoint; model: ModelPoint },\n ) => {\n if (activeRef.current) return; // ignore secondary touches mid-gesture\n const modifiers = readModifiers(e);\n const ctx = makeCtx(modifiers);\n const ls = layersRef.current;\n const nativeEvent = e.nativeEvent;\n\n dlog('layered-curve-editor', 'pointerdown', { plot: coords.plot, model: coords.model, layers: ls.length });\n\n // Capture the pre-event snapshot once; reuse for history if any\n // path produces a commit (gesture-start commit or one-shot).\n const preEventSnapshot = snapshot();\n\n // Top-to-bottom hit test.\n for (let i = ls.length - 1; i >= 0; i--) {\n const b = ls[i];\n const hit = b.layer.hitTest?.(b.state, coords.plot, ctx);\n if (!hit) continue;\n // Found a claim. onPointerDown may start a gesture, absorb the\n // click, or commit a one-shot edit via extra.commit.\n const prevState = b.state;\n let committed: unknown = undefined;\n const gesture = b.layer.onPointerDown?.(\n prevState, hit, nativeEvent, ctx,\n {\n plot: coords.plot,\n model: coords.model,\n commit(next) { committed = next; onLayerChange(b.layer.id, next); },\n },\n );\n e.stopPropagation();\n if (gesture) {\n installGesture(b.layer.id, e, gesture);\n return;\n }\n if (committed !== undefined) {\n if (historyEnabled) {\n pastRef.current.push(preEventSnapshot);\n futureRef.current = [];\n }\n onLayerCommit?.(b.layer.id, committed, prevState);\n }\n return;\n }\n\n // No hit — empty space dispatch, top-to-bottom.\n let pendingCommit: { id: string; next: unknown; prev: unknown } | null = null;\n for (let i = ls.length - 1; i >= 0; i--) {\n const b = ls[i];\n if (!b.layer.onEmptyPointerDown) continue;\n const prevState = b.state;\n let committed: unknown = undefined;\n const gesture = b.layer.onEmptyPointerDown(\n prevState, coords.model, nativeEvent, ctx,\n {\n plot: coords.plot,\n model: coords.model,\n commit(next) {\n committed = next;\n onLayerChange(b.layer.id, next);\n },\n },\n );\n if (gesture) {\n // If onEmptyPointerDown also published a commit (e.g. click-curve\n // insert publishes the new point then drags it), the gesture's\n // startStates already capture the post-insert state; the commit\n // for history is the *pre*-event snapshot, written when the\n // gesture eventually finishes via the gesture-commit path. We\n // override the gesture's startStates so undo restores correctly.\n installGesture(b.layer.id, e, gesture);\n if (committed !== undefined && activeRef.current !== null) {\n (activeRef.current as ActiveGesture).startStates = preEventSnapshot;\n (activeRef.current as ActiveGesture).startSelfState = preEventSnapshot.get(b.layer.id);\n }\n return;\n }\n if (committed !== undefined) {\n pendingCommit = { id: b.layer.id, next: committed, prev: prevState };\n break;\n }\n }\n if (pendingCommit) {\n if (historyEnabled) {\n pastRef.current.push(preEventSnapshot);\n futureRef.current = [];\n }\n onLayerCommit?.(pendingCommit.id, pendingCommit.next, pendingCommit.prev);\n }\n }, [makeCtx, installGesture, onLayerChange, onLayerCommit, historyEnabled, snapshot]);\n\n // ── keyboard ───────────────────────────────────────────────────────\n const onSvgKeyDown = useCallback((e: ReactKeyboardEvent<SVGSVGElement>) => {\n const modifiers = readModifiers(e);\n const ctx = makeCtx(modifiers);\n const ls = layersRef.current;\n // History first.\n if (historyEnabled && (e.metaKey || e.ctrlKey)) {\n const k = e.key.toLowerCase();\n if (k === 'z') {\n if (e.shiftKey) redo();\n else undo();\n e.preventDefault();\n return;\n }\n if (k === 'y') { redo(); e.preventDefault(); return; }\n }\n // Then layers (top-to-bottom).\n for (let i = ls.length - 1; i >= 0; i--) {\n const b = ls[i];\n if (!b.layer.onKeyDown) continue;\n const preEventSnapshot = snapshot();\n let committed: unknown = undefined;\n b.layer.onKeyDown(b.state, e.nativeEvent, ctx, {\n commit(next) { committed = next; onLayerChange(b.layer.id, next); },\n });\n if (committed !== undefined) {\n if (historyEnabled) {\n pastRef.current.push(preEventSnapshot);\n futureRef.current = [];\n }\n onLayerCommit?.(b.layer.id, committed, b.state);\n }\n // The layer prevents default on the native event, which the synthetic\n // event's `isDefaultPrevented` does not observe.\n if (e.nativeEvent.defaultPrevented) return;\n }\n }, [historyEnabled, undo, redo, makeCtx, snapshot, onLayerChange, onLayerCommit]);\n\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n // ── render ─────────────────────────────────────────────────────────\n const renderCtxBase: Omit<LayerCtx, 'modifiers'> = useMemo(() => ({\n modelRange,\n plotSize,\n toPlot: (p) => modelToPlot(p, modelRange, plotSize),\n toModel: (p) => plotToModel(p, modelRange, plotSize),\n }), [modelRange, plotSize]);\n\n const cls = [s.root, className].filter(Boolean).join(' ');\n\n return (\n <Plot2D\n ref={plotRef}\n className={cls}\n style={style}\n width={width}\n height={height}\n xRange={xRange}\n yRange={yRange}\n grid={grid}\n axes={axes}\n xTicks={xTicks}\n yTicks={yTicks}\n tabIndex={historyEnabled ? 0 : undefined}\n role=\"group\"\n aria-label={props['aria-label']}\n onPointerDown={onSvgPointerDown}\n onKeyDown={onSvgKeyDown}\n >\n {children}\n {layers.map((b) => {\n const ctx = {\n ...renderCtxBase,\n modifiers: { shift: false, alt: false, meta: false, ctrl: false },\n isActive: activeLayerId === b.layer.id,\n };\n return (\n <g key={b.layer.id} data-layer-id={b.layer.id}>\n {b.layer.render(b.state, ctx)}\n </g>\n );\n })}\n </Plot2D>\n );\n}\n","/** Snap `value` to the nearest candidate within `tolerance`, else return it. */\nexport function snapToNearest(value: number, candidates: readonly number[], tolerance: number): number {\n let best = value;\n let bestDist = tolerance;\n for (const c of candidates) {\n const d = Math.abs(c - value);\n if (d <= bestDist) {\n best = c;\n bestDist = d;\n }\n }\n return best;\n}\n","/**\n * Keyframe layer for LayeredCurveEditor: a value-over-time curve whose shape\n * between two keys is the easing on the later one, with draggable keys and\n * draggable cubic-bezier handles on the selected segment.\n *\n * Distinct from `createFunctionLayer`, which interpolates a spline through\n * its anchors. Here the keys are exact and the easing approximates between\n * them, so the two do not share curve maths.\n */\nimport type { ReactNode } from 'react';\nimport { cubicBezierEasing, resolveEasing, type EasingFn, type Keyframe } from '@weasel-js/core';\nimport { hitTestAnchor, hitTestCurve } from './hitTest';\nimport { snapToNearest } from './snap';\nimport s from './CurveEditor.module.css';\nimport type {\n CurveLayer, EmptyDownArgs, KeyDownArgs, LayerCtx, LayerGesture, LayerHit, LayerRenderCtx, PlotPoint,\n} from './layerTypes';\n\ntype Bezier = readonly [number, number, number, number];\n\n/** A gesture in flight. The committed keys hold still underneath it. */\nexport type KeyframeDrag =\n | { kind: 'key'; index: number; t: number; value: number }\n | { kind: 'handle'; segment: number; bezier: Bezier };\n\n/**\n * The state a keyframe layer owns, held by the consumer and round-tripped\n * through `LayeredCurveEditor`'s `onLayerChange`.\n */\nexport interface KeyframeLayerState {\n /** Sorted ascending by `t`. */\n keys: readonly Keyframe<number>[];\n selectedKey: number | null;\n /** Index of the key the selected segment runs into. Its bezier handles show. */\n selectedSegment: number | null;\n drag: KeyframeDrag | null;\n}\n\n/** Configuration for {@link createKeyframeLayer}. */\nexport interface KeyframeLayerConfig {\n /** Layer id; default `'keyframes'`. */\n id?: string;\n /** Names the curve in each mark's accessible name. Default `'Curve'`. */\n label?: string;\n /** Formats a key time for accessible names. Default: rounded number. */\n formatX?: (x: number) => string;\n /** Times a dragged key snaps to. Holding alt defeats the snap. */\n snapX?: readonly number[];\n /** Snap reach, in plot pixels. Default 6. */\n snapRadiusPx?: number;\n /** Bounds for a key's time. Default: unbounded. */\n xClamp?: readonly [number, number];\n /** Bounds for a key's value. Default: unbounded. */\n yClamp?: readonly [number, number];\n /** One arrow-key step in model units; shift takes ten. Default: 1% of the\n * plot's range on that axis. */\n step?: { x?: number; y?: number };\n}\n\nconst SAMPLES_PER_SEGMENT = 16;\nconst KEY_HIT_PX = 8;\nconst HANDLE_HIT_PX = 8;\nconst CURVE_HIT_PX = 6;\nconst DEFAULT_SNAP_PX = 6;\nconst KEY_HALF = 4.5;\nconst HANDLE_RADIUS = 3.5;\nconst SHIFT_MULTIPLIER = 10;\nconst HANDLE_STEP = 0.01;\n\n/** A keyframe layer state with nothing selected and no gesture in flight. */\nexport function keyframeLayerState(keys: readonly Keyframe<number>[]): KeyframeLayerState {\n return { keys, selectedKey: null, selectedSegment: null, drag: null };\n}\n\nfunction clampTo(v: number, range?: readonly [number, number]): number {\n return range ? Math.min(range[1], Math.max(range[0], v)) : v;\n}\n\nfunction bezierOf(key: Keyframe<number> | undefined): Bezier | null {\n const e = key?.easing;\n return typeof e === 'object' && e !== null && 'bezier' in e ? e.bezier : null;\n}\n\nfunction moveKeyTo(\n keys: readonly Keyframe<number>[], index: number, t: number, value: number,\n): { keys: Keyframe<number>[]; selectedKey: number } {\n const moved = { ...keys[index], t, value };\n const rest = keys.filter((_, i) => i !== index);\n const at = rest.findIndex((k) => k.t > t);\n const selectedKey = at === -1 ? rest.length : at;\n rest.splice(selectedKey, 0, moved);\n return { keys: rest, selectedKey };\n}\n\n/**\n * The keys a state's drag would leave behind: a dragged key re-sorted into\n * place, or a dragged handle's control points written onto the key its\n * segment runs into. `selectedKey` follows a moved key to its new index; a\n * key dropped where it started returns the same `keys` array.\n */\nexport function applyKeyframeDrag(\n state: KeyframeLayerState,\n): { keys: readonly Keyframe<number>[]; selectedKey: number | null } {\n const { drag, keys } = state;\n if (!drag) return { keys, selectedKey: state.selectedKey };\n if (drag.kind === 'key') {\n const k = keys[drag.index];\n if (k && k.t === drag.t && k.value === drag.value) return { keys, selectedKey: drag.index };\n return moveKeyTo(keys, drag.index, drag.t, drag.value);\n }\n const next = keys.slice();\n next[drag.segment] = { ...keys[drag.segment], easing: { bezier: drag.bezier } };\n return { keys: next, selectedKey: state.selectedKey };\n}\n\n/**\n * Builds a keyframe layer for `LayeredCurveEditor`. Model x is time and model\n * y is value; each segment is drawn through the easing on the key it runs\n * into. Every key, segment and handle is focusable and named, and the arrow\n * keys move whichever one has focus.\n */\nexport function createKeyframeLayer(cfg: KeyframeLayerConfig = {}): CurveLayer<KeyframeLayerState> {\n const id = cfg.id ?? 'keyframes';\n const label = cfg.label ?? 'Curve';\n const formatX = cfg.formatX ?? ((x: number) => String(Math.round(x)));\n const snapRadiusPx = cfg.snapRadiusPx ?? DEFAULT_SNAP_PX;\n\n // A live handle drag is previewed straight from its control points: a\n // fresh spec per pointermove through `resolveEasing` would leave one\n // permanent cache entry per pixel of drag.\n function easingInto(state: KeyframeLayerState, keys: readonly Keyframe<number>[], seg: number): EasingFn {\n const d = state.drag;\n if (d?.kind === 'handle' && d.segment === seg) return cubicBezierEasing(...d.bezier);\n return resolveEasing(keys[seg].easing);\n }\n\n function segmentSamples(state: KeyframeLayerState, keys: readonly Keyframe<number>[], ctx: LayerCtx): PlotPoint[][] {\n const out: PlotPoint[][] = [];\n for (let i = 1; i < keys.length; i++) {\n const a = keys[i - 1];\n const b = keys[i];\n const ease = easingInto(state, keys, i);\n const pts: PlotPoint[] = [];\n for (let j = 0; j <= SAMPLES_PER_SEGMENT; j++) {\n const u = j / SAMPLES_PER_SEGMENT;\n pts.push(ctx.toPlot({ x: a.t + (b.t - a.t) * u, y: a.value + (b.value - a.value) * ease(u) }));\n }\n out.push(pts);\n }\n return out;\n }\n\n function activeBezier(state: KeyframeLayerState): Bezier | null {\n const seg = state.selectedSegment;\n if (seg === null || seg < 1 || seg >= state.keys.length) return null;\n const d = state.drag;\n if (d?.kind === 'handle' && d.segment === seg) return d.bezier;\n return bezierOf(state.keys[seg]);\n }\n\n function handlePoints(state: KeyframeLayerState, bezier: Bezier, ctx: LayerCtx): [PlotPoint, PlotPoint] {\n const a = state.keys[state.selectedSegment! - 1];\n const b = state.keys[state.selectedSegment!];\n const at = (xf: number, yf: number) =>\n ctx.toPlot({ x: a.t + xf * (b.t - a.t), y: a.value + yf * (b.value - a.value) });\n return [at(bezier[0], bezier[1]), at(bezier[2], bezier[3])];\n }\n\n const path = (pts: readonly PlotPoint[]): string =>\n pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(2)},${p.y.toFixed(2)}`).join('');\n\n // ── render ──────────────────────────────────────────────────────────\n function render(state: KeyframeLayerState, ctx: LayerRenderCtx): ReactNode {\n const shown = state.drag?.kind === 'key' ? applyKeyframeDrag(state).keys : state.keys;\n const segments = segmentSamples(state, shown, ctx);\n const curve = segments.flatMap((pts, i) => (i === 0 ? pts : pts.slice(1)));\n const seg = state.selectedSegment;\n const bezier = activeBezier(state);\n const dragIndex = state.drag?.kind === 'key' ? state.drag.index : null;\n\n return (\n <>\n {seg !== null && seg >= 1 && seg < shown.length ? (() => {\n const x0 = ctx.toPlot({ x: shown[seg - 1].t, y: 0 }).x;\n const x1 = ctx.toPlot({ x: shown[seg].t, y: 0 }).x;\n return (\n <rect\n className={s.segmentBand}\n x={Math.min(x0, x1)}\n y={0}\n width={Math.abs(x1 - x0)}\n height={ctx.plotSize.height}\n />\n );\n })() : null}\n {curve.length > 1 ? <path data-curve-element=\"curve\" className={s.curve} d={path(curve)} /> : null}\n {segments.map((pts, i) => (\n <path\n key={`seg-${i + 1}`}\n className={s.segmentTarget}\n d={path(pts)}\n role=\"button\"\n tabIndex={0}\n aria-label={`${label} segment into ${formatX(shown[i + 1].t)}`}\n aria-current={seg === i + 1 ? 'true' : undefined}\n data-segment-index={i + 1}\n />\n ))}\n {bezier ? (() => {\n const [h1, h2] = handlePoints(state, bezier, ctx);\n const a = ctx.toPlot({ x: state.keys[seg! - 1].t, y: state.keys[seg! - 1].value });\n const b = ctx.toPlot({ x: state.keys[seg!].t, y: state.keys[seg!].value });\n return (\n <>\n <line className={s.handleStem} x1={a.x} y1={a.y} x2={h1.x} y2={h1.y} />\n <line className={s.handleStem} x1={b.x} y1={b.y} x2={h2.x} y2={h2.y} />\n {[h1, h2].map((h, i) => (\n <circle\n key={`handle-${i}`}\n className={s.handle}\n cx={h.x}\n cy={h.y}\n r={HANDLE_RADIUS}\n role=\"button\"\n tabIndex={0}\n aria-label={`${label} bezier handle ${i + 1}`}\n data-handle-index={i}\n />\n ))}\n </>\n );\n })() : null}\n {state.keys.map((k, i) => {\n const p = ctx.toPlot({ x: k.t, y: k.value });\n return (\n <rect\n key={`key-${i}`}\n className={s.keyframe}\n x={p.x - KEY_HALF}\n y={p.y - KEY_HALF}\n width={KEY_HALF * 2}\n height={KEY_HALF * 2}\n transform={`rotate(45 ${p.x} ${p.y})`}\n role=\"button\"\n tabIndex={0}\n aria-label={`${label} key at ${formatX(k.t)}`}\n aria-current={state.selectedKey === i ? 'true' : undefined}\n data-keyframe-index={i}\n data-dragging={dragIndex === i ? 'true' : undefined}\n />\n );\n })}\n {state.drag?.kind === 'key' ? (() => {\n const p = ctx.toPlot({ x: state.drag.t, y: state.drag.value });\n return (\n <rect\n aria-hidden=\"true\"\n className={s.keyframeGhost}\n x={p.x - KEY_HALF}\n y={p.y - KEY_HALF}\n width={KEY_HALF * 2}\n height={KEY_HALF * 2}\n transform={`rotate(45 ${p.x} ${p.y})`}\n data-keyframe-ghost=\"\"\n />\n );\n })() : null}\n </>\n );\n }\n\n // ── hit test ────────────────────────────────────────────────────────\n function hitTest(state: KeyframeLayerState, plot: PlotPoint, ctx: LayerCtx): LayerHit | null {\n const bezier = activeBezier(state);\n if (bezier) {\n const h = hitTestAnchor(handlePoints(state, bezier, ctx), plot, HANDLE_HIT_PX);\n if (h) return { kind: 'handle', payload: { handle: h.index } };\n }\n const k = hitTestAnchor(state.keys.map((key) => ctx.toPlot({ x: key.t, y: key.value })), plot, KEY_HIT_PX);\n if (k) return { kind: 'key', payload: { index: k.index } };\n const c = hitTestCurve(segmentSamples(state, state.keys, ctx), plot, CURVE_HIT_PX);\n if (c) return { kind: 'segment', payload: { segment: c.segIdx + 1 } };\n return null;\n }\n\n // ── gestures ────────────────────────────────────────────────────────\n function keyGesture(index: number): LayerGesture<KeyframeLayerState> {\n return {\n onMove(state, model, e, ctx) {\n let t = clampTo(model.x, cfg.xClamp);\n if (cfg.snapX && !e.altKey) {\n const perPx = (ctx.modelRange.xMax - ctx.modelRange.xMin) / ctx.plotSize.width;\n t = clampTo(snapToNearest(t, cfg.snapX, Math.abs(perPx) * snapRadiusPx), cfg.xClamp);\n }\n return { ...state, drag: { kind: 'key', index, t, value: clampTo(model.y, cfg.yClamp) } };\n },\n onCommit(state) { return { ...state, ...applyKeyframeDrag(state), drag: null }; },\n onCancel(state) { return { ...state, drag: null }; },\n };\n }\n\n function handleGesture(segment: number, handle: 0 | 1): LayerGesture<KeyframeLayerState> {\n return {\n onMove(state, model) {\n const a = state.keys[segment - 1];\n const b = state.keys[segment];\n const d = state.drag;\n if (!a || !b || d?.kind !== 'handle') return state;\n const xf = b.t === a.t ? 0 : Math.min(1, Math.max(0, (model.x - a.t) / (b.t - a.t)));\n const yf = b.value === a.value ? 0 : (model.y - a.value) / (b.value - a.value);\n const bezier = [...d.bezier] as [number, number, number, number];\n bezier[handle * 2] = xf;\n bezier[handle * 2 + 1] = yf;\n return { ...state, drag: { kind: 'handle', segment, bezier } };\n },\n onCommit(state) { return { ...state, ...applyKeyframeDrag(state), drag: null }; },\n onCancel(state) { return { ...state, drag: null }; },\n };\n }\n\n function onPointerDown(\n state: KeyframeLayerState,\n hit: LayerHit,\n e: PointerEvent,\n _ctx: LayerCtx,\n extra: EmptyDownArgs<KeyframeLayerState>,\n ): LayerGesture<KeyframeLayerState> | void {\n if (e.button !== 0) return;\n if (hit.kind === 'handle') {\n const bezier = activeBezier(state);\n const segment = state.selectedSegment;\n if (!bezier || segment === null) return;\n const { handle } = hit.payload as { handle: 0 | 1 };\n extra.commit({ ...state, drag: { kind: 'handle', segment, bezier } });\n return handleGesture(segment, handle);\n }\n if (hit.kind === 'key') {\n const { index } = hit.payload as { index: number };\n const k = state.keys[index];\n extra.commit({ ...state, selectedKey: index, drag: { kind: 'key', index, t: k.t, value: k.value } });\n return keyGesture(index);\n }\n if (hit.kind === 'segment') {\n const { segment } = hit.payload as { segment: number };\n extra.commit({ ...state, selectedSegment: segment });\n }\n }\n\n // ── keyboard ────────────────────────────────────────────────────────\n function onKeyDown(\n state: KeyframeLayerState,\n e: KeyboardEvent,\n ctx: LayerCtx,\n extra: KeyDownArgs<KeyframeLayerState>,\n ): void {\n const target = e.target as Element | null;\n if (target?.closest?.('[data-layer-id]')?.getAttribute('data-layer-id') !== id) return;\n const activate = e.key === 'Enter' || e.key === ' ';\n const dx = e.key === 'ArrowRight' ? 1 : e.key === 'ArrowLeft' ? -1 : 0;\n const dy = e.key === 'ArrowUp' ? 1 : e.key === 'ArrowDown' ? -1 : 0;\n const mult = e.shiftKey ? SHIFT_MULTIPLIER : 1;\n\n const keyEl = target.closest('[data-keyframe-index]');\n if (keyEl) {\n const index = Number(keyEl.getAttribute('data-keyframe-index'));\n const k = state.keys[index];\n if (!k) return;\n if (activate) {\n e.preventDefault();\n extra.commit({ ...state, selectedKey: index });\n return;\n }\n if (dx === 0 && dy === 0) return;\n e.preventDefault();\n const m = ctx.modelRange;\n const stepX = (cfg.step?.x ?? Math.abs(m.xMax - m.xMin) / 100) * mult;\n const stepY = (cfg.step?.y ?? Math.abs(m.yMax - m.yMin) / 100) * mult;\n const t = clampTo(k.t + dx * stepX, cfg.xClamp);\n const value = clampTo(k.value + dy * stepY, cfg.yClamp);\n if (t === k.t && value === k.value) return;\n extra.commit({ ...state, ...moveKeyTo(state.keys, index, t, value), drag: null });\n return;\n }\n\n const segEl = target.closest('[data-segment-index]');\n if (segEl && activate) {\n e.preventDefault();\n extra.commit({ ...state, selectedSegment: Number(segEl.getAttribute('data-segment-index')) });\n return;\n }\n\n const handleEl = target.closest('[data-handle-index]');\n const bezier = activeBezier(state);\n if (handleEl && bezier && state.selectedSegment !== null && (dx !== 0 || dy !== 0)) {\n e.preventDefault();\n const h = Number(handleEl.getAttribute('data-handle-index'));\n const next = [...bezier] as [number, number, number, number];\n next[h * 2] = Math.min(1, Math.max(0, next[h * 2] + dx * HANDLE_STEP * mult));\n next[h * 2 + 1] += dy * HANDLE_STEP * mult;\n const drag: KeyframeDrag = { kind: 'handle', segment: state.selectedSegment, bezier: next };\n extra.commit({ ...state, ...applyKeyframeDrag({ ...state, drag }), drag: null });\n }\n }\n\n return { id, render, hitTest, onPointerDown, onKeyDown };\n}\n"],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ //#region src/format/number.ts
2
+ var e = "−";
3
+ function t(e, t) {
4
+ return (Number.isFinite(e) ? e.toLocaleString(void 0, t) : String(e)).replace(/^-/, "−");
5
+ }
6
+ function n(e) {
7
+ return Number(e.replace("−", "-"));
8
+ }
9
+ var r = {
10
+ k: 3,
11
+ m: 6,
12
+ b: 9,
13
+ t: 12
14
+ };
15
+ function i(e, n = 0) {
16
+ if (!Number.isFinite(e)) return t(e);
17
+ let r = Math.abs(e) < 1e3 ? {
18
+ useGrouping: !1,
19
+ minimumFractionDigits: n,
20
+ maximumFractionDigits: n
21
+ } : {
22
+ notation: "compact",
23
+ minimumFractionDigits: 1,
24
+ maximumFractionDigits: 1
25
+ };
26
+ return e.toLocaleString("en-US", r).replace(/^-/, "−");
27
+ }
28
+ function a(e, t) {
29
+ if (t) {
30
+ let n = e.trim(), r = o(n, t);
31
+ if (r !== void 0) {
32
+ let e = a(n.slice(0, -r.length)), i = t[r];
33
+ return i < 1 ? e / (1 / i) : e * i;
34
+ }
35
+ }
36
+ let n = e.trim().replace("−", "-"), i = r[n.slice(-1).toLowerCase()];
37
+ return i !== void 0 && (n = n.slice(0, -1).trimEnd()), /^[-+]?\d{1,3}(,\d{3})+(\.\d*)?$/.test(n) && (n = n.replace(/,/g, "")), /\d/.test(n) ? Number(i === void 0 ? n : `${n}e${i}`) : NaN;
38
+ }
39
+ function o(e, t) {
40
+ let n = Object.keys(t).filter((e) => e !== "").sort((e, t) => t.length - e.length), r = e.toLowerCase();
41
+ return n.find((t) => e.endsWith(t)) ?? n.find((e) => r.endsWith(e.toLowerCase()));
42
+ }
43
+ function s(e) {
44
+ return String(e).replace(/^-/, "−");
45
+ }
46
+ function c(e) {
47
+ return Number.isFinite(e) ? e <= 2 ? `${s(Math.round(e * 100))}%` : e >= 100 ? `${Math.round(e).toLocaleString("en-US")}x` : `${s(Math.round(e * 10) / 10)}x` : String(e);
48
+ }
49
+ //#endregion
50
+ export { a, c as i, i as n, n as o, t as r, e as t };
51
+
52
+ //# sourceMappingURL=number-f6PydOZK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number-f6PydOZK.js","names":[],"sources":["../../src/format/number.ts"],"sourcesContent":["/**\n * Display-formatter for numbers. Use this anywhere a number is shown to\n * a user. The whole point: negative values get prefixed with the real\n * MINUS SIGN (U+2212) instead of the ASCII HYPHEN-MINUS (U+002D) that\n * `toLocaleString` and template literals produce by default.\n *\n * U+2212 is the same visual width as `+` and reads as a sign rather\n * than a hyphen — columns of signed numbers align cleanly and the\n * glyph doesn't get confused with a bullet or list dash.\n */\nexport const MINUS_SIGN = '−';\n\n/**\n * Formats a number for display, substituting {@link MINUS_SIGN} for the ASCII\n * hyphen `toLocaleString` emits. Non-finite values stringify as-is.\n */\nexport function formatNumber(value: number, options?: Intl.NumberFormatOptions): string {\n const formatted = Number.isFinite(value)\n ? value.toLocaleString(undefined, options)\n : String(value);\n return formatted.replace(/^-/, MINUS_SIGN);\n}\n\n/**\n * Parses a string that may carry {@link MINUS_SIGN} in place of the ASCII\n * hyphen. The inverse of {@link formatNumber} for any editing surface that\n * renders its value through it and reads the edited text back.\n */\nexport function parseSignedNumber(text: string): number {\n return Number(text.replace(MINUS_SIGN, '-'));\n}\n\n/** Powers of ten a typed magnitude suffix stands for. */\nconst EXPONENTS: Record<string, number> = { k: 3, m: 6, b: 9, t: 12 };\n\n/**\n * Formats a number the way a `compact` readout shows it: below 1,000 at\n * `decimals` places, from 1,000 up at one decimal with a magnitude suffix\n * (`40.0K`, `2.0M`). Always `en-US`, so {@link parseNumber} reads it back.\n */\nexport function formatCompact(value: number, decimals = 0): string {\n if (!Number.isFinite(value)) return formatNumber(value);\n const options: Intl.NumberFormatOptions =\n Math.abs(value) < 1000\n ? { useGrouping: false, minimumFractionDigits: decimals, maximumFractionDigits: decimals }\n : { notation: 'compact', minimumFractionDigits: 1, maximumFractionDigits: 1 };\n return value.toLocaleString('en-US', options).replace(/^-/, MINUS_SIGN);\n}\n\n/**\n * Reads a typed number: anything {@link parseSignedNumber} reads, plus\n * thousands commas in the `40,000` shape and a `k`/`m`/`b`/`t` suffix in either\n * case (`2.5m` is 2,500,000). Empty text is NaN rather than zero.\n *\n * Given `units` — a suffix mapped to the factor it scales by — a trailing unit\n * name is read first: the longest match wins, exact case before any case, and\n * a unit beats a magnitude suffix, so with `m` accepted `2m` is `2 * units.m`.\n */\nexport function parseNumber(text: string, units?: Readonly<Record<string, number>>): number {\n if (units) {\n const trimmed = text.trim();\n const name = unitSuffixOf(trimmed, units);\n if (name !== undefined) {\n const n = parseNumber(trimmed.slice(0, -name.length));\n const factor = units[name]!;\n // Divided by the reciprocal below 1: 12 * 0.1 is 1.2000000000000002, 12 / 10 is 1.2.\n return factor < 1 ? n / (1 / factor) : n * factor;\n }\n }\n let t = text.trim().replace(MINUS_SIGN, '-');\n const exponent = EXPONENTS[t.slice(-1).toLowerCase()];\n if (exponent !== undefined) t = t.slice(0, -1).trimEnd();\n if (/^[-+]?\\d{1,3}(,\\d{3})+(\\.\\d*)?$/.test(t)) t = t.replace(/,/g, '');\n if (!/\\d/.test(t)) return Number.NaN;\n // Scaled through the exponent rather than by multiplying: 1.1 * 1000 is 1100.0000000000002.\n return Number(exponent === undefined ? t : `${t}e${exponent}`);\n}\n\n/** The longest unit name `text` ends with — exact case first, then any case. */\nfunction unitSuffixOf(text: string, units: Readonly<Record<string, number>>): string | undefined {\n const names = Object.keys(units).filter((n) => n !== '').sort((a, b) => b.length - a.length);\n const lower = text.toLowerCase();\n return names.find((n) => text.endsWith(n)) ?? names.find((n) => lower.endsWith(n.toLowerCase()));\n}\n\n/** {@link String} with the leading ASCII hyphen swapped for {@link MINUS_SIGN}.\n * Locale-independent, unlike `toLocaleString`. */\nfunction signedString(value: number): string {\n return String(value).replace(/^-/, MINUS_SIGN);\n}\n\n/**\n * Formats a zoom factor for display. Below 2x a percentage reads naturally;\n * past it the numbers get long and a multiplier is what people say out loud,\n * so 250% shows as `2.5x`. Past 100x a tenth is noise, so the decimal is\n * dropped and thousands are grouped: `1009.74` reads as `1,010x`.\n */\nexport function formatZoom(zoom: number): string {\n if (!Number.isFinite(zoom)) return String(zoom);\n if (zoom <= 2) return `${signedString(Math.round(zoom * 100))}%`;\n if (zoom >= 100) return `${Math.round(zoom).toLocaleString('en-US')}x`;\n return `${signedString(Math.round(zoom * 10) / 10)}x`;\n}\n"],"mappings":";AAUA,IAAa,IAAa;AAM1B,SAAgB,EAAa,GAAe,GAA4C;AAItF,SAHkB,OAAO,SAAS,EAAM,GACpC,EAAM,eAAe,KAAA,GAAW,EAAQ,GACxC,OAAO,EAAM,EACA,QAAQ,MAAA,IAAiB;;AAQ5C,SAAgB,EAAkB,GAAsB;AACtD,QAAO,OAAO,EAAK,QAAA,KAAoB,IAAI,CAAC;;AAI9C,IAAM,IAAoC;CAAE,GAAG;CAAG,GAAG;CAAG,GAAG;CAAG,GAAG;CAAI;AAOrE,SAAgB,EAAc,GAAe,IAAW,GAAW;AACjE,KAAI,CAAC,OAAO,SAAS,EAAM,CAAE,QAAO,EAAa,EAAM;CACvD,IAAM,IACJ,KAAK,IAAI,EAAM,GAAG,MACd;EAAE,aAAa;EAAO,uBAAuB;EAAU,uBAAuB;EAAU,GACxF;EAAE,UAAU;EAAW,uBAAuB;EAAG,uBAAuB;EAAG;AACjF,QAAO,EAAM,eAAe,SAAS,EAAQ,CAAC,QAAQ,MAAA,IAAiB;;AAYzE,SAAgB,EAAY,GAAc,GAAkD;AAC1F,KAAI,GAAO;EACT,IAAM,IAAU,EAAK,MAAM,EACrB,IAAO,EAAa,GAAS,EAAM;AACzC,MAAI,MAAS,KAAA,GAAW;GACtB,IAAM,IAAI,EAAY,EAAQ,MAAM,GAAG,CAAC,EAAK,OAAO,CAAC,EAC/C,IAAS,EAAM;AAErB,UAAO,IAAS,IAAI,KAAK,IAAI,KAAU,IAAI;;;CAG/C,IAAI,IAAI,EAAK,MAAM,CAAC,QAAA,KAAoB,IAAI,EACtC,IAAW,EAAU,EAAE,MAAM,GAAG,CAAC,aAAa;AAKpD,QAJI,MAAa,KAAA,MAAW,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,SAAS,GACpD,kCAAkC,KAAK,EAAE,KAAE,IAAI,EAAE,QAAQ,MAAM,GAAG,GACjE,KAAK,KAAK,EAAE,GAEV,OAAO,MAAa,KAAA,IAAY,IAAI,GAAG,EAAE,GAAG,IAAW,GAFpC;;AAM5B,SAAS,EAAa,GAAc,GAA6D;CAC/F,IAAM,IAAQ,OAAO,KAAK,EAAM,CAAC,QAAQ,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO,EACtF,IAAQ,EAAK,aAAa;AAChC,QAAO,EAAM,MAAM,MAAM,EAAK,SAAS,EAAE,CAAC,IAAI,EAAM,MAAM,MAAM,EAAM,SAAS,EAAE,aAAa,CAAC,CAAC;;AAKlG,SAAS,EAAa,GAAuB;AAC3C,QAAO,OAAO,EAAM,CAAC,QAAQ,MAAA,IAAiB;;AAShD,SAAgB,EAAW,GAAsB;AAI/C,QAHK,OAAO,SAAS,EAAK,GACtB,KAAQ,IAAU,GAAG,EAAa,KAAK,MAAM,IAAO,IAAI,CAAC,CAAC,KAC1D,KAAQ,MAAY,GAAG,KAAK,MAAM,EAAK,CAAC,eAAe,QAAQ,CAAC,KAC7D,GAAG,EAAa,KAAK,MAAM,IAAO,GAAG,GAAG,GAAG,CAAC,KAHhB,OAAO,EAAK"}
@@ -13,9 +13,15 @@ function e(e) {
13
13
  };
14
14
  }
15
15
  function t(e) {
16
+ return e.suffix === void 0 ? { ...e.accepts } : {
17
+ [e.suffix]: 1,
18
+ ...e.accepts
19
+ };
20
+ }
21
+ function n(e) {
16
22
  return "kind" in e;
17
23
  }
18
- function n(e, t) {
24
+ function r(e, t) {
19
25
  let n = e;
20
26
  for (let e of t.split(".")) {
21
27
  if (typeof n != "object" || !n) return;
@@ -23,12 +29,12 @@ function n(e, t) {
23
29
  }
24
30
  return n;
25
31
  }
26
- function r(e, n) {
27
- if (t(e)) return e.hidden && !n ? null : e;
28
- let i = e, a = {};
29
- for (let [e, t] of Object.entries(i.children)) {
30
- let i = r(t, n);
31
- i && (a[e] = i);
32
+ function i(e, t) {
33
+ if (n(e)) return e.hidden && !t ? null : e;
34
+ let r = e, a = {};
35
+ for (let [e, n] of Object.entries(r.children)) {
36
+ let r = i(n, t);
37
+ r && (a[e] = r);
32
38
  }
33
39
  return Object.keys(a).length === 0 ? null : {
34
40
  ...e,
@@ -36,6 +42,6 @@ function r(e, n) {
36
42
  };
37
43
  }
38
44
  //#endregion
39
- export { r as i, e as n, n as r, t };
45
+ export { i as a, r as i, e as n, t as r, n as t };
40
46
 
41
- //# sourceMappingURL=schema-CtWwGbqJ.js.map
47
+ //# sourceMappingURL=schema-CPdFNeP6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-CPdFNeP6.js","names":[],"sources":["../../src/components/Prefs/schema.ts"],"sourcesContent":["// Preference-schema vocabulary for PrefsForm / PrefsDialog / SelectionPanel:\n// core's `ToolPref*` family under this package's names. Never re-declare them\n// here — this was a hand-kept structural copy until it drifted, and the\n// independence from `@weasel-js/core` it bought is long gone anyway.\n\nexport type {\n ToolPref as BuiltinPref,\n ToolPrefBoolean as PrefBoolean,\n ToolPrefBooleanControl as PrefBooleanControl,\n ToolPrefColor as PrefColor,\n ToolPrefCustom as PrefCustom,\n ToolPrefEnum as PrefEnum,\n ToolPrefEnumControl as PrefEnumControl,\n ToolPrefEnumEncoding as PrefEnumEncoding,\n ToolPrefGroup as PrefGroup,\n ToolPrefKind as PrefKind,\n ToolPrefLeaf as PrefLeaf,\n ToolPrefNumber as PrefNumber,\n ToolPrefNumberControl as PrefNumberControl,\n ToolPrefNumberFormat as PrefNumberFormat,\n ToolPrefNumberUnit as PrefNumberUnit,\n ToolPrefObject as PrefObject,\n ToolPrefPaint as PrefPaint,\n ToolPrefString as PrefString,\n ToolPrefStringControl as PrefStringControl,\n} from '@weasel-js/core';\n\nimport type { ToolPrefGroup, ToolPrefLeaf, ToolPrefNumber, ToolPrefNumberUnit as PrefNumberUnitType } from '@weasel-js/core';\n\n/**\n * A number leaf's bounds in the unit it is displayed in.\n *\n * Only what the leaf declares converts: an omitted bound has no stored\n * counterpart to put through the conversion, and its fallback — 0..100 for a\n * slider's track, a step of 1 — is a display-space number already. `min` and\n * `max` are points, so they convert the way the value does; `step` is a\n * distance, and a unit with an offset maps zero somewhere else, so converting\n * it as a point would scale it wrong. A decreasing conversion swaps which end\n * is the lower one.\n */\nexport function prefDisplayBounds(\n p: ToolPrefNumber,\n): { min?: number; max?: number; step: number } {\n if (!p.unit) return { min: p.min, max: p.max, step: p.step ?? 1 };\n const { toDisplay } = p.unit;\n const lo = p.min === undefined ? undefined : toDisplay(p.min);\n const hi = p.max === undefined ? undefined : toDisplay(p.max);\n const flipped = lo !== undefined && hi !== undefined && lo > hi;\n const step = p.step === undefined\n ? 1\n : Math.abs(toDisplay(p.step) - toDisplay(0)) || p.step;\n return { min: flipped ? hi : lo, max: flipped ? lo : hi, step };\n}\n\n/** What a unit leaf's field reads as typed text: its `accepts` table, and its\n * `suffix` as the display unit itself. */\nexport function prefUnitAccepts(unit: PrefNumberUnitType): Readonly<Record<string, number>> {\n return unit.suffix === undefined ? { ...unit.accepts } : { [unit.suffix]: 1, ...unit.accepts };\n}\n\n/** Distinguishes a leaf from a group while walking a schema tree. */\nexport function isPrefLeaf(node: ToolPrefLeaf | ToolPrefGroup): node is ToolPrefLeaf {\n return 'kind' in node;\n}\n\n/** Get the value at a dotted path inside a nested value tree. Returns\n * `undefined` when a segment is missing or hits a non-object. */\nexport function prefValueAtPath(values: unknown, path: string): unknown {\n let cur: unknown = values;\n for (const seg of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Recursively drop `hidden` leaves (unless `showHidden`), pruning groups\n * that end up empty. Returns null when the entire subtree is hidden.\n */\nexport function visiblePrefSubtree<T extends ToolPrefLeaf | ToolPrefGroup>(\n node: T,\n showHidden: boolean,\n): T | null {\n if (isPrefLeaf(node)) return node.hidden && !showHidden ? null : node;\n const group = node as ToolPrefGroup;\n const children: Record<string, ToolPrefLeaf | ToolPrefGroup> = {};\n for (const [key, child] of Object.entries(group.children)) {\n const kept = visiblePrefSubtree(child, showHidden);\n if (kept) children[key] = kept;\n }\n if (Object.keys(children).length === 0) return null;\n return { ...node, children };\n}\n"],"mappings":";AAwCA,SAAgB,EACd,GAC8C;AAC9C,KAAI,CAAC,EAAE,KAAM,QAAO;EAAE,KAAK,EAAE;EAAK,KAAK,EAAE;EAAK,MAAM,EAAE,QAAQ;EAAG;CACjE,IAAM,EAAE,iBAAc,EAAE,MAClB,IAAK,EAAE,QAAQ,KAAA,IAAY,KAAA,IAAY,EAAU,EAAE,IAAI,EACvD,IAAK,EAAE,QAAQ,KAAA,IAAY,KAAA,IAAY,EAAU,EAAE,IAAI,EACvD,IAAU,MAAO,KAAA,KAAa,MAAO,KAAA,KAAa,IAAK,GACvD,IAAO,EAAE,SAAS,KAAA,IACpB,IACA,KAAK,IAAI,EAAU,EAAE,KAAK,GAAG,EAAU,EAAE,CAAC,IAAI,EAAE;AACpD,QAAO;EAAE,KAAK,IAAU,IAAK;EAAI,KAAK,IAAU,IAAK;EAAI;EAAM;;AAKjE,SAAgB,EAAgB,GAA4D;AAC1F,QAAO,EAAK,WAAW,KAAA,IAAY,EAAE,GAAG,EAAK,SAAS,GAAG;GAAG,EAAK,SAAS;EAAG,GAAG,EAAK;EAAS;;AAIhG,SAAgB,EAAW,GAA0D;AACnF,QAAO,UAAU;;AAKnB,SAAgB,EAAgB,GAAiB,GAAuB;CACtE,IAAI,IAAe;AACnB,MAAK,IAAM,KAAO,EAAK,MAAM,IAAI,EAAE;AACjC,MAAmB,OAAO,KAAQ,aAA9B,EAAwC;AAC5C,MAAO,EAAgC;;AAEzC,QAAO;;AAOT,SAAgB,EACd,GACA,GACU;AACV,KAAI,EAAW,EAAK,CAAE,QAAO,EAAK,UAAU,CAAC,IAAa,OAAO;CACjE,IAAM,IAAQ,GACR,IAAyD,EAAE;AACjE,MAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAM,SAAS,EAAE;EACzD,IAAM,IAAO,EAAmB,GAAO,EAAW;AAClD,EAAI,MAAM,EAAS,KAAO;;AAG5B,QADI,OAAO,KAAK,EAAS,CAAC,WAAW,IAAU,OACxC;EAAE,GAAG;EAAM;EAAU"}
@@ -1,2 +1,2 @@
1
- import { n as e, r as t, t as n } from "../../chunks/BandEditor-CUlbOlJi.js";
1
+ import { n as e, r as t, t as n } from "../../chunks/BandEditor-BeWO56_w.js";
2
2
  export { n as BandEditor, e as linearScale, t as logScale };
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "../../chunks/Callout-Dvt6lqVn.js";
1
+ import { n as e, t } from "../../chunks/Callout-BPIA2is-.js";
2
2
  export { t as Callout, e as CalloutTrigger };
@@ -13,6 +13,23 @@ export type ComboBoxOption = {
13
13
  isDisabled?: boolean;
14
14
  };
15
15
  type Key = string | number;
16
+ /**
17
+ * How the typed text narrows the options.
18
+ *
19
+ * `'contains'` is a locale-aware substring match over each option's text.
20
+ * `'none'` shows every option given — what a list a server already filtered
21
+ * and ranked needs, since a second pass would drop rows that do not contain
22
+ * the query and reorder whatever survived.
23
+ */
24
+ export type ComboBoxFilter = 'contains' | 'none' | ((textValue: string, inputValue: string) => boolean);
25
+ /** What the user committed: an option they picked, or text they typed. */
26
+ export type ComboBoxCommit<T extends Key = string> = {
27
+ source: 'option';
28
+ key: T;
29
+ } | {
30
+ source: 'text';
31
+ text: string;
32
+ };
16
33
  /**
17
34
  * Props for {@link ComboBox}, on top of React Aria's `ComboBox` props, with
18
35
  * the selection key narrowed to the option value type.
@@ -30,6 +47,25 @@ export type ComboBoxProps<T extends Key = string> = Omit<RACComboBoxProps<object
30
47
  defaultSelectedKey?: T;
31
48
  onSelectionChange?: (key: T | null) => void;
32
49
  emptyLabel?: ReactNode;
50
+ /** Shown in place of `emptyLabel` while `loadError` is set. */
51
+ errorLabel?: ReactNode;
52
+ /**
53
+ * How typed text narrows `options`. Defaults to `'contains'`. `'none'` also
54
+ * keeps the popover open on an empty collection: a list the kit does not
55
+ * filter can arrive empty from a server mid-query, and without that the
56
+ * popover closes and `emptyLabel` is never seen.
57
+ */
58
+ filter?: ComboBoxFilter;
59
+ /** Marks the options as out of date while the next set is being fetched. */
60
+ isLoading?: boolean;
61
+ /** A failed load, shown as `errorLabel` rather than as an empty corpus. */
62
+ loadError?: unknown | null;
63
+ /**
64
+ * Fires when the user commits — Enter on the active option, a click on one,
65
+ * or Enter on text matching none of them when `allowsCustomValue` is set.
66
+ * Empty text commits nothing.
67
+ */
68
+ onCommit?: (commit: ComboBoxCommit<T>) => void;
33
69
  /**
34
70
  * `'fill'` (the default) takes the width of whatever row the combo box sits
35
71
  * in. `'fit'` sizes the input to its widest option, so it neither swallows a
@@ -1 +1 @@
1
- {"version":3,"file":"ComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/ComboBox/ComboBox.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAUL,KAAK,aAAa,IAAI,gBAAgB,EACtC,KAAK,gBAAgB,IAAI,mBAAmB,EAC5C,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGtF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,GAAG,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,oBAAoB,GAAG,mBAAmB,CAAC,GAAG;IAC1K,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC,CAAC;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC,cAAc,GAAG;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,kBAAkB,CAAC;AAEvB;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,2CAmEvE;AAgBD,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,GAAG;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,2CAErE"}
1
+ {"version":3,"file":"ComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/ComboBox/ComboBox.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjG,OAAO,EAWL,KAAK,aAAa,IAAI,gBAAgB,EACtC,KAAK,gBAAgB,IAAI,mBAAmB,EAC5C,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGtF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,GAAG,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3B;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AAExG,0EAA0E;AAC1E,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,GAAG,GAAG,MAAM,IAC7C;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,CAAC,CAAA;CAAE,GAC5B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAIrC;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,oBAAoB,GAAG,mBAAmB,CAAC,GAAG;IAC1K,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC,CAAC;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC,cAAc,GAAG;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,kBAAkB,CAAC;AAEvB;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,2CAsFvE;AA4DD,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,GAAG;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,2CAErE"}
@@ -1,3 +1,3 @@
1
1
  export { ComboBox, ComboBoxItem } from './ComboBox';
2
- export type { ComboBoxProps, ComboBoxItemProps, ComboBoxOption } from './ComboBox';
2
+ export type { ComboBoxProps, ComboBoxItemProps, ComboBoxOption, ComboBoxCommit, ComboBoxFilter, } from './ComboBox';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ComboBox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ComboBox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC"}
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "../../chunks/ComboBox-BBRRWvdc.js";
1
+ import { n as e, t } from "../../chunks/ComboBox-CPES6Y1i.js";
2
2
  export { t as ComboBox, e as ComboBoxItem };
@@ -5,10 +5,10 @@
5
5
  * directly.
6
6
  */
7
7
  import { type CSSProperties, type ReactNode } from 'react';
8
- import { type GridSettings, type AxesSettings } from '../Plot2D';
8
+ import { type GridSettings, type AxesSettings, type TickSettings } from '../Plot2D';
9
9
  import type { InterpolationMode } from './interpolation';
10
10
  import { type ControlPoint, type CurveDomain, type EndpointMode, type AddPointMode, type CurveSettings, type FillSettings, type AnchorRenderProps } from './createFunctionLayer';
11
- export type { GridSettings, AxesSettings };
11
+ export type { GridSettings, AxesSettings, TickSettings };
12
12
  export type { ControlPoint, CurveDomain, EndpointMode, AddPointMode, CurveSettings, FillSettings, AnchorRenderProps, };
13
13
  /**
14
14
  * Props for {@link CurveEditor}. `onInput` fires throughout a gesture and
@@ -27,6 +27,8 @@ export interface CurveEditorProps {
27
27
  height: number;
28
28
  grid?: GridSettings | false | null;
29
29
  axes?: AxesSettings | false | null;
30
+ xTicks?: TickSettings | false | null;
31
+ yTicks?: TickSettings | false | null;
30
32
  curve?: CurveSettings | false | null;
31
33
  fill?: FillSettings | false | null;
32
34
  hideNonInteractive?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"CurveEditor.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/CurveEditor.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAG/B,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AAC3C,YAAY,EACV,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EACrD,aAAa,EAAE,YAAY,EAAE,iBAAiB,GAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,KAAK,IAAI,CAAC;IACzE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACtD,+DAA+D;IAC/D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CA4GlD"}
1
+ {"version":3,"file":"CurveEditor.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/CurveEditor.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAG/B,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AACzD,YAAY,EACV,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EACrD,aAAa,EAAE,YAAY,EAAE,iBAAiB,GAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,KAAK,IAAI,CAAC;IACzE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACtD,+DAA+D;IAC/D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CA8GlD"}
@@ -1,5 +1,5 @@
1
1
  import { type CSSProperties, type ReactNode } from 'react';
2
- import { type GridSettings, type AxesSettings } from '../Plot2D';
2
+ import { type GridSettings, type AxesSettings, type TickSettings } from '../Plot2D';
3
3
  import type { CurveLayer } from './layerTypes';
4
4
  /**
5
5
  * A layer paired with the state the consumer holds for it. The editor never
@@ -29,11 +29,18 @@ export interface LayeredCurveEditorProps {
29
29
  yRange?: readonly [number, number];
30
30
  grid?: GridSettings | false | null;
31
31
  axes?: AxesSettings | false | null;
32
+ /** Ticks at x values. See `Plot2D.xTicks`. */
33
+ xTicks?: TickSettings | false | null;
34
+ /** Ticks at y values. See `Plot2D.yTicks`. */
35
+ yTicks?: TickSettings | false | null;
32
36
  /** Built-in undo/redo via Cmd/Ctrl+Z (Shift+Z or Y for redo) on the
33
37
  * focused SVG. Snapshots every layer's state on each commit; undo
34
38
  * fires `onLayerChange(id, prevState)` for each layer whose state
35
39
  * changed. Default `true`. */
36
40
  history?: boolean;
41
+ /** Accessible name for the plot, which is exposed as a group so the
42
+ * focusable marks layers render inside it stay reachable. */
43
+ 'aria-label'?: string;
37
44
  className?: string;
38
45
  style?: CSSProperties;
39
46
  /** Extra SVG content rendered behind all layers (under the lowest
@@ -1 +1 @@
1
- {"version":3,"file":"LayeredCurveEditor.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/LayeredCurveEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAGlB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAOnB,OAAO,KAAK,EACV,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACrB,KAAK,EAAE,CAAC,CAAC;CACV;AAED,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC;2EACuE;IACvE,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAE7C;+CAC2C;IAC3C,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAE/C;;0CAEsC;IACtC,aAAa,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAE/D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IAEnC;;;mCAG+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;qCAEiC;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CAyUhE"}
1
+ {"version":3,"file":"LayeredCurveEditor.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/LayeredCurveEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAGlB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAOnB,OAAO,KAAK,EACV,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACrB,KAAK,EAAE,CAAC,CAAC;CACV;AAED,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC;2EACuE;IACvE,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAE7C;+CAC2C;IAC3C,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAE/C;;0CAEsC;IACtC,aAAa,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAE/D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,8CAA8C;IAC9C,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,8CAA8C;IAC9C,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IAErC;;;mCAG+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;kEAC8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;qCAEiC;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CA2VhE"}
@@ -82,6 +82,8 @@ export interface FunctionLayerConfig {
82
82
  * sub-range of the plot (e.g. a bevel curve occupying x ∈ [0, b]
83
83
  * while the editor's xRange is [0, halfWidth]). */
84
84
  xClamp?: readonly [number, number];
85
+ /** Names the curve in each anchor's accessible name. Default `'Point'`. */
86
+ label?: string;
85
87
  }
86
88
  /**
87
89
  * The state a function layer owns. Treat it as opaque and round-trip it
@@ -1 +1 @@
1
- {"version":3,"file":"createFunctionLayer.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/createFunctionLayer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAyB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAIhF,OAAO,KAAK,EACV,UAAU,EAEX,MAAM,cAAc,CAAC;AAItB,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV;iEAC6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC;AAC/D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG,OAAO,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC,oEAAoE;AACpE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qDAAqD;AACrD,MAAM,WAAW,mBAAmB;IAClC;kCAC8B;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACtD;;qEAEiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;wDAGoD;IACpD,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC;;sDAEkD;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAsDD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,mBAAwB,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAkQjG;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,kBAAkB,CAEtF"}
1
+ {"version":3,"file":"createFunctionLayer.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/createFunctionLayer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAyB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAIhF,OAAO,KAAK,EACV,UAAU,EAEX,MAAM,cAAc,CAAC;AAItB,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV;iEAC6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC;AAC/D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG,OAAO,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC,oEAAoE;AACpE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qDAAqD;AACrD,MAAM,WAAW,mBAAmB;IAClC;kCAC8B;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;IACrC,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACtD;;qEAEiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;wDAGoD;IACpD,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC;;sDAEkD;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA2DD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,mBAAwB,GAAG,UAAU,CAAC,kBAAkB,CAAC,CA+SjG;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,kBAAkB,CAEtF"}
@@ -0,0 +1,70 @@
1
+ import { type Keyframe } from '@weasel-js/core';
2
+ import type { CurveLayer } from './layerTypes';
3
+ type Bezier = readonly [number, number, number, number];
4
+ /** A gesture in flight. The committed keys hold still underneath it. */
5
+ export type KeyframeDrag = {
6
+ kind: 'key';
7
+ index: number;
8
+ t: number;
9
+ value: number;
10
+ } | {
11
+ kind: 'handle';
12
+ segment: number;
13
+ bezier: Bezier;
14
+ };
15
+ /**
16
+ * The state a keyframe layer owns, held by the consumer and round-tripped
17
+ * through `LayeredCurveEditor`'s `onLayerChange`.
18
+ */
19
+ export interface KeyframeLayerState {
20
+ /** Sorted ascending by `t`. */
21
+ keys: readonly Keyframe<number>[];
22
+ selectedKey: number | null;
23
+ /** Index of the key the selected segment runs into. Its bezier handles show. */
24
+ selectedSegment: number | null;
25
+ drag: KeyframeDrag | null;
26
+ }
27
+ /** Configuration for {@link createKeyframeLayer}. */
28
+ export interface KeyframeLayerConfig {
29
+ /** Layer id; default `'keyframes'`. */
30
+ id?: string;
31
+ /** Names the curve in each mark's accessible name. Default `'Curve'`. */
32
+ label?: string;
33
+ /** Formats a key time for accessible names. Default: rounded number. */
34
+ formatX?: (x: number) => string;
35
+ /** Times a dragged key snaps to. Holding alt defeats the snap. */
36
+ snapX?: readonly number[];
37
+ /** Snap reach, in plot pixels. Default 6. */
38
+ snapRadiusPx?: number;
39
+ /** Bounds for a key's time. Default: unbounded. */
40
+ xClamp?: readonly [number, number];
41
+ /** Bounds for a key's value. Default: unbounded. */
42
+ yClamp?: readonly [number, number];
43
+ /** One arrow-key step in model units; shift takes ten. Default: 1% of the
44
+ * plot's range on that axis. */
45
+ step?: {
46
+ x?: number;
47
+ y?: number;
48
+ };
49
+ }
50
+ /** A keyframe layer state with nothing selected and no gesture in flight. */
51
+ export declare function keyframeLayerState(keys: readonly Keyframe<number>[]): KeyframeLayerState;
52
+ /**
53
+ * The keys a state's drag would leave behind: a dragged key re-sorted into
54
+ * place, or a dragged handle's control points written onto the key its
55
+ * segment runs into. `selectedKey` follows a moved key to its new index; a
56
+ * key dropped where it started returns the same `keys` array.
57
+ */
58
+ export declare function applyKeyframeDrag(state: KeyframeLayerState): {
59
+ keys: readonly Keyframe<number>[];
60
+ selectedKey: number | null;
61
+ };
62
+ /**
63
+ * Builds a keyframe layer for `LayeredCurveEditor`. Model x is time and model
64
+ * y is value; each segment is drawn through the easing on the key it runs
65
+ * into. Every key, segment and handle is focusable and named, and the arrow
66
+ * keys move whichever one has focus.
67
+ */
68
+ export declare function createKeyframeLayer(cfg?: KeyframeLayerConfig): CurveLayer<KeyframeLayerState>;
69
+ export {};
70
+ //# sourceMappingURL=createKeyframeLayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createKeyframeLayer.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/createKeyframeLayer.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAmD,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIjG,OAAO,KAAK,EACV,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB,KAAK,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAExD,wEAAwE;AACxE,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,IAAI,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gFAAgF;IAChF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CAC3B;AAED,qDAAqD;AACrD,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,kEAAkE;IAClE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,oDAAoD;IACpD,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC;qCACiC;IACjC,IAAI,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAYD,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,kBAAkB,CAExF;AAsBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,kBAAkB,GACxB;IAAE,IAAI,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWnE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,mBAAwB,GAAG,UAAU,CAAC,kBAAkB,CAAC,CA4RjG"}
@@ -1,7 +1,9 @@
1
1
  export { CurveEditor, type CurveEditorProps, } from './CurveEditor';
2
2
  export { type ControlPoint, type CurveDomain, type EndpointMode, type AddPointMode, type FillSettings, type CurveSettings, type AnchorRenderProps, createFunctionLayer, functionLayerState, type FunctionLayerConfig, type FunctionLayerState, } from './createFunctionLayer';
3
+ export { createKeyframeLayer, keyframeLayerState, applyKeyframeDrag, type KeyframeDrag, type KeyframeLayerConfig, type KeyframeLayerState, } from './createKeyframeLayer';
4
+ export { snapToNearest } from './snap';
3
5
  export { LayeredCurveEditor, type LayeredCurveEditorProps, type LayerBinding, } from './LayeredCurveEditor';
4
- export type { CurveLayer, LayerCtx, LayerRenderCtx, LayerHit, LayerGesture, LayerModifiers, EmptyDownArgs, ModelPoint, PlotPoint, } from './layerTypes';
6
+ export type { CurveLayer, LayerCtx, LayerRenderCtx, LayerHit, LayerGesture, LayerModifiers, EmptyDownArgs, KeyDownArgs, ModelPoint, PlotPoint, } from './layerTypes';
5
7
  export { createSetCurveOp, type SetCurveAdapter, type CreateSetCurveOpArgs, } from './setCurveOp';
6
8
  export { sampleByInterpolation, type InterpolationMode, } from './interpolation';
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,UAAU,EACV,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,aAAa,EACb,UAAU,EACV,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,UAAU,EACV,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC"}
@@ -1,2 +1,3 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "../../chunks/CurveEditor-DtjdpJVB.js";
2
- export { n as CurveEditor, i as LayeredCurveEditor, t as createFunctionLayer, a as createSetCurveOp, e as functionLayerState, r as sampleByInterpolation };
1
+ import { a as e, i as t, n, r, t as i } from "../../chunks/CurveEditor-Cj0lLiyR.js";
2
+ import { a, i as o, n as s, r as c, t as l } from "../../chunks/createKeyframeLayer-AL4jP0EX.js";
3
+ export { n as CurveEditor, a as LayeredCurveEditor, l as applyKeyframeDrag, r as createFunctionLayer, s as createKeyframeLayer, i as createSetCurveOp, t as functionLayerState, c as keyframeLayerState, e as sampleByInterpolation, o as snapToNearest };
@@ -88,9 +88,14 @@ export interface CurveLayer<S> {
88
88
  * gesture or commit a one-shot edit (return void after publishing
89
89
  * state via the editor — see `EmptyDownArgs.commit`). */
90
90
  onEmptyPointerDown?(state: S, model: ModelPoint, e: PointerEvent, ctx: LayerCtx, extra: EmptyDownArgs<S>): LayerGesture<S> | void;
91
- /** Keyboard while the editor has focus. Top-to-bottom; call
92
- * `e.preventDefault()` to stop propagation. */
93
- onKeyDown?(state: S, e: KeyboardEvent, ctx: LayerCtx): void;
91
+ /** Keyboard while focus is on the editor or on anything a layer rendered
92
+ * into it; `e.target` says which. Top-to-bottom; `e.preventDefault()` keeps
93
+ * the key from the layers below. Publish an edit with `extra.commit`. */
94
+ onKeyDown?(state: S, e: KeyboardEvent, ctx: LayerCtx, extra: KeyDownArgs<S>): void;
95
+ }
96
+ /** Handed to `onKeyDown` so a key press can publish a committed edit. */
97
+ export interface KeyDownArgs<S> {
98
+ commit(next: S): void;
94
99
  }
95
100
  /** Helpers handed to `onPointerDown` and `onEmptyPointerDown` so a
96
101
  * layer can publish a one-shot edit (shift-click delete, empty-click
@@ -1 +1 @@
1
- {"version":3,"file":"layerTypes.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/layerTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"layerTypes.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/layerTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** Snap `value` to the nearest candidate within `tolerance`, else return it. */
2
+ export declare function snapToNearest(value: number, candidates: readonly number[], tolerance: number): number;
3
+ //# sourceMappingURL=snap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snap.d.ts","sourceRoot":"","sources":["../../../src/components/CurveEditor/snap.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAWrG"}
@@ -1,2 +1,2 @@
1
- import { t as e } from "../../chunks/DetentSlider-BpD7bgjN.js";
1
+ import { t as e } from "../../chunks/DetentSlider-CYicfnKr.js";
2
2
  export { e as DetentSlider };