@weasel-js/ui 1.4.4 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{ActionBar-DxGp3WuZ.js → ActionBar-GZdcfdQL.js} +1 -2
- package/dist/chunks/{ActionBar-DxGp3WuZ.js.map → ActionBar-GZdcfdQL.js.map} +1 -1
- package/dist/chunks/{ActionsBar-D1VFreoj.js → ActionsBar-Fhx_40iP.js} +2 -2
- package/dist/chunks/{ActionsBar-D1VFreoj.js.map → ActionsBar-Fhx_40iP.js.map} +1 -1
- package/dist/chunks/{BandEditor-CUlbOlJi.js → BandEditor-D8I5Uun-.js} +93 -98
- package/dist/chunks/BandEditor-D8I5Uun-.js.map +1 -0
- package/dist/chunks/{Callout-Dvt6lqVn.js → Callout-cx5_CWcW.js} +1 -1
- package/dist/chunks/{Callout-Dvt6lqVn.js.map → Callout-cx5_CWcW.js.map} +1 -1
- package/dist/chunks/{Checkbox-D-pVpobU.js → Checkbox-BZZsPaVF.js} +1 -1
- package/dist/chunks/{Checkbox-D-pVpobU.js.map → Checkbox-BZZsPaVF.js.map} +1 -1
- package/dist/chunks/{ColorField-C20aRvCg.js → ColorField-YVf8irfr.js} +2 -2
- package/dist/chunks/{ColorField-C20aRvCg.js.map → ColorField-YVf8irfr.js.map} +1 -1
- package/dist/chunks/ComboBox-D57tNYDD.js +138 -0
- package/dist/chunks/ComboBox-D57tNYDD.js.map +1 -0
- package/dist/chunks/CurveEditor-BXiMoN9d.js +533 -0
- package/dist/chunks/CurveEditor-BXiMoN9d.js.map +1 -0
- package/dist/chunks/DetentSlider-BWNSv07Z.js +72 -0
- package/dist/chunks/DetentSlider-BWNSv07Z.js.map +1 -0
- package/dist/chunks/{Dialog-CYBLjwbd.js → Dialog-DuOzsR2g.js} +1 -1
- package/dist/chunks/{Dialog-CYBLjwbd.js.map → Dialog-DuOzsR2g.js.map} +1 -1
- package/dist/chunks/{Disclosure-DiA6UKdM.js → Disclosure-9UgDzc7Z.js} +6 -6
- package/dist/chunks/{Disclosure-DiA6UKdM.js.map → Disclosure-9UgDzc7Z.js.map} +1 -1
- package/dist/chunks/{Field-dzlxOGxT.js → Field-BNqvUJfg.js} +1 -1
- package/dist/chunks/{Field-dzlxOGxT.js.map → Field-BNqvUJfg.js.map} +1 -1
- package/dist/chunks/{FontFamilySelect-DIJ4phXz.js → FontFamilySelect-CO19kyOO.js} +2 -2
- package/dist/chunks/{FontFamilySelect-DIJ4phXz.js.map → FontFamilySelect-CO19kyOO.js.map} +1 -1
- package/dist/chunks/{GradientEditor-BLpAtMDK.js → GradientEditor-B_ymIr4Q.js} +74 -51
- package/dist/chunks/GradientEditor-B_ymIr4Q.js.map +1 -0
- package/dist/chunks/Icon-QhLhaGFe.js +498 -0
- package/dist/chunks/Icon-QhLhaGFe.js.map +1 -0
- package/dist/chunks/{InlineRange-CcfzY80y.js → InlineRange-DdIlfGqG.js} +2 -2
- package/dist/chunks/{InlineRange-CcfzY80y.js.map → InlineRange-DdIlfGqG.js.map} +1 -1
- package/dist/chunks/{Input-CoKOtyD0.js → Input-B9Vh7ASP.js} +2 -2
- package/dist/chunks/{Input-CoKOtyD0.js.map → Input-B9Vh7ASP.js.map} +1 -1
- package/dist/chunks/Jog-BYrxbAQU.js +81 -0
- package/dist/chunks/Jog-BYrxbAQU.js.map +1 -0
- package/dist/chunks/{Keycaps-DSeq6QWH.js → Keycaps-CGNQV2Pz.js} +5 -5
- package/dist/chunks/Keycaps-CGNQV2Pz.js.map +1 -0
- package/dist/chunks/MenuButton-DGE-0DP0.js +60 -0
- package/dist/chunks/MenuButton-DGE-0DP0.js.map +1 -0
- package/dist/chunks/MeshEditor-CHoGQ9K8.js +123 -0
- package/dist/chunks/MeshEditor-CHoGQ9K8.js.map +1 -0
- package/dist/chunks/NumberField-CQzS6TjF.js +112 -0
- package/dist/chunks/NumberField-CQzS6TjF.js.map +1 -0
- package/dist/chunks/{OptionsBar-D_Cd3ZYs.js → OptionsBar-BWaYG_OH.js} +2 -2
- package/dist/chunks/{OptionsBar-D_Cd3ZYs.js.map → OptionsBar-BWaYG_OH.js.map} +1 -1
- package/dist/chunks/PaintField-BVYMu3OI.js +62 -0
- package/dist/chunks/PaintField-BVYMu3OI.js.map +1 -0
- package/dist/chunks/PaintInput-BzAHCKro.js +131 -0
- package/dist/chunks/PaintInput-BzAHCKro.js.map +1 -0
- package/dist/chunks/{PatternPicker-Ba22Bf4L.js → PatternPicker-C4OryrYi.js} +2 -2
- package/dist/chunks/{PatternPicker-Ba22Bf4L.js.map → PatternPicker-C4OryrYi.js.map} +1 -1
- package/dist/chunks/Plot2D-Czvyt_vG.js +292 -0
- package/dist/chunks/Plot2D-Czvyt_vG.js.map +1 -0
- package/dist/chunks/{PointPlotter-CbDKVggs.js → PointPlotter-Xvb98GwB.js} +2 -2
- package/dist/chunks/PointPlotter-Xvb98GwB.js.map +1 -0
- package/dist/chunks/{Powerline-BaHDJlcO.js → Powerline-B_EH-7Z-.js} +1 -1
- package/dist/chunks/{Powerline-BaHDJlcO.js.map → Powerline-B_EH-7Z-.js.map} +1 -1
- package/dist/chunks/Prefs-FuaJBs3t.js +308 -0
- package/dist/chunks/Prefs-FuaJBs3t.js.map +1 -0
- package/dist/chunks/Properties-B3KU69NI.js +874 -0
- package/dist/chunks/Properties-B3KU69NI.js.map +1 -0
- package/dist/chunks/{RadioGroup-B2_LUgDp.js → RadioGroup-C6xpdQNg.js} +2 -2
- package/dist/chunks/{RadioGroup-B2_LUgDp.js.map → RadioGroup-C6xpdQNg.js.map} +1 -1
- package/dist/chunks/{RangeSlider-S2R3zA8N.js → RangeSlider-CzgeGLVS.js} +1 -1
- package/dist/chunks/{RangeSlider-S2R3zA8N.js.map → RangeSlider-CzgeGLVS.js.map} +1 -1
- package/dist/chunks/{ResizeHandle-BrCO0JrH.js → ResizeHandle-1Ey8AOij.js} +1 -1
- package/dist/chunks/{ResizeHandle-BrCO0JrH.js.map → ResizeHandle-1Ey8AOij.js.map} +1 -1
- package/dist/chunks/{Select-CacMfs4v.js → Select-BMYVh10x.js} +20 -17
- package/dist/chunks/Select-BMYVh10x.js.map +1 -0
- package/dist/chunks/{SelectionPanel-C59wavdA.js → SelectionPanel-DTpcQidl.js} +211 -201
- package/dist/chunks/SelectionPanel-DTpcQidl.js.map +1 -0
- package/dist/chunks/{Sidebar-BvwjZiFm.js → Sidebar-BlUjn8lD.js} +1 -1
- package/dist/chunks/{Sidebar-BvwjZiFm.js.map → Sidebar-BlUjn8lD.js.map} +1 -1
- package/dist/chunks/{SidebarPanel-D5Ua2vyP.js → SidebarPanel-BqHfz6VK.js} +2 -2
- package/dist/chunks/{SidebarPanel-D5Ua2vyP.js.map → SidebarPanel-BqHfz6VK.js.map} +1 -1
- package/dist/chunks/Slider-B2ywUZH6.js +381 -0
- package/dist/chunks/Slider-B2ywUZH6.js.map +1 -0
- package/dist/chunks/{StatusBar-Bqv4D0dW.js → StatusBar-BFt7Yw5O.js} +1 -1
- package/dist/chunks/{StatusBar-Bqv4D0dW.js.map → StatusBar-BFt7Yw5O.js.map} +1 -1
- package/dist/chunks/{Switch-C4NU-me1.js → Switch-Om3t3ASc.js} +1 -1
- package/dist/chunks/{Switch-C4NU-me1.js.map → Switch-Om3t3ASc.js.map} +1 -1
- package/dist/chunks/{Tabs-BT3CDkoQ.js → Tabs-EAvYywil.js} +1 -1
- package/dist/chunks/{Tabs-BT3CDkoQ.js.map → Tabs-EAvYywil.js.map} +1 -1
- package/dist/chunks/Timeline-BEyKaBXl.js +916 -0
- package/dist/chunks/Timeline-BEyKaBXl.js.map +1 -0
- package/dist/chunks/{Toast-B7ylq7fz.js → Toast-iqLDD_XZ.js} +1 -1
- package/dist/chunks/{Toast-B7ylq7fz.js.map → Toast-iqLDD_XZ.js.map} +1 -1
- package/dist/chunks/{ToggleBar-Bxwv6xvj.js → ToggleBar--9As2PWG.js} +8 -8
- package/dist/chunks/ToggleBar--9As2PWG.js.map +1 -0
- package/dist/chunks/TokenPanel-BL9hlq7x.js +374 -0
- package/dist/chunks/TokenPanel-BL9hlq7x.js.map +1 -0
- package/dist/chunks/{ToolButton-CtDkqUIc.js → ToolButton-Dd-1T5aX.js} +1 -1
- package/dist/chunks/{ToolButton-CtDkqUIc.js.map → ToolButton-Dd-1T5aX.js.map} +1 -1
- package/dist/chunks/{ToolGroup-zDtqt0QJ.js → ToolGroup-BBI-W-Gm.js} +1 -1
- package/dist/chunks/{ToolGroup-zDtqt0QJ.js.map → ToolGroup-BBI-W-Gm.js.map} +1 -1
- package/dist/chunks/{ToolPalette-BbrMHfQ9.js → ToolPalette-BmTDDpmz.js} +3 -3
- package/dist/chunks/{ToolPalette-BbrMHfQ9.js.map → ToolPalette-BmTDDpmz.js.map} +1 -1
- package/dist/chunks/createKeyframeLayer-7j7ukrSx.js +699 -0
- package/dist/chunks/createKeyframeLayer-7j7ukrSx.js.map +1 -0
- package/dist/chunks/{icons-BV27dhKB.js → icons-Btrs-JD7.js} +35 -32
- package/dist/chunks/icons-Btrs-JD7.js.map +1 -0
- package/dist/chunks/number-f6PydOZK.js +52 -0
- package/dist/chunks/number-f6PydOZK.js.map +1 -0
- package/dist/chunks/range.module-DSsOIq1R.js +8 -0
- package/dist/chunks/range.module-DSsOIq1R.js.map +1 -0
- package/dist/chunks/{schema-CtWwGbqJ.js → schema-MCVT1MLv.js} +15 -9
- package/dist/chunks/schema-MCVT1MLv.js.map +1 -0
- package/dist/chunks/segmentedControl.module-DmeUMYe3.js +11 -0
- package/dist/chunks/segmentedControl.module-DmeUMYe3.js.map +1 -0
- package/dist/chunks/snap-B0WQwetQ.js +13 -0
- package/dist/chunks/snap-B0WQwetQ.js.map +1 -0
- package/dist/components/ActionBar/ActionBar.d.ts.map +1 -1
- package/dist/components/ActionBar/index.js +1 -1
- package/dist/components/ActionsBar/index.js +1 -1
- package/dist/components/BandEditor/BandEditor.d.ts.map +1 -1
- package/dist/components/BandEditor/index.js +1 -1
- package/dist/components/BandEditor/scale.d.ts +1 -8
- package/dist/components/BandEditor/scale.d.ts.map +1 -1
- package/dist/components/Callout/index.js +1 -1
- package/dist/components/Checkbox/index.js +1 -1
- package/dist/components/ColorField/index.js +1 -1
- package/dist/components/ComboBox/ComboBox.d.ts +36 -0
- package/dist/components/ComboBox/ComboBox.d.ts.map +1 -1
- package/dist/components/ComboBox/index.d.ts +1 -1
- package/dist/components/ComboBox/index.d.ts.map +1 -1
- package/dist/components/ComboBox/index.js +1 -1
- package/dist/components/CurveEditor/CurveEditor.d.ts +4 -2
- package/dist/components/CurveEditor/CurveEditor.d.ts.map +1 -1
- package/dist/components/CurveEditor/LayeredCurveEditor.d.ts +8 -1
- package/dist/components/CurveEditor/LayeredCurveEditor.d.ts.map +1 -1
- package/dist/components/CurveEditor/createFunctionLayer.d.ts +2 -0
- package/dist/components/CurveEditor/createFunctionLayer.d.ts.map +1 -1
- package/dist/components/CurveEditor/createKeyframeLayer.d.ts +70 -0
- package/dist/components/CurveEditor/createKeyframeLayer.d.ts.map +1 -0
- package/dist/components/CurveEditor/index.d.ts +3 -1
- package/dist/components/CurveEditor/index.d.ts.map +1 -1
- package/dist/components/CurveEditor/index.js +4 -2
- package/dist/components/CurveEditor/layerTypes.d.ts +8 -3
- package/dist/components/CurveEditor/layerTypes.d.ts.map +1 -1
- package/dist/components/DetentSlider/DetentSlider.d.ts +9 -10
- package/dist/components/DetentSlider/DetentSlider.d.ts.map +1 -1
- package/dist/components/DetentSlider/index.js +1 -1
- package/dist/components/Dialog/index.js +1 -1
- package/dist/components/Disclosure/index.js +1 -1
- package/dist/components/Field/index.js +1 -1
- package/dist/components/FontFamilySelect/index.js +1 -1
- package/dist/components/GradientEditor/GradientEditor.d.ts +3 -0
- package/dist/components/GradientEditor/GradientEditor.d.ts.map +1 -1
- package/dist/components/GradientEditor/index.js +1 -1
- package/dist/components/InlineRange/index.js +1 -1
- package/dist/components/Input/index.js +1 -1
- package/dist/components/Jog/Jog.d.ts +28 -0
- package/dist/components/Jog/Jog.d.ts.map +1 -0
- package/dist/components/Jog/index.d.ts +3 -0
- package/dist/components/Jog/index.d.ts.map +1 -0
- package/dist/components/Jog/index.js +2 -0
- package/dist/components/Keycaps/index.js +1 -1
- package/dist/components/MenuButton/MenuButton.d.ts +29 -0
- package/dist/components/MenuButton/MenuButton.d.ts.map +1 -0
- package/dist/components/MenuButton/index.d.ts +3 -0
- package/dist/components/MenuButton/index.d.ts.map +1 -0
- package/dist/components/MenuButton/index.js +2 -0
- package/dist/components/MeshEditor/MeshEditor.d.ts +30 -0
- package/dist/components/MeshEditor/MeshEditor.d.ts.map +1 -0
- package/dist/components/MeshEditor/index.d.ts +3 -0
- package/dist/components/MeshEditor/index.d.ts.map +1 -0
- package/dist/components/MeshEditor/index.js +2 -0
- package/dist/components/NumberField/UnitField.d.ts +30 -0
- package/dist/components/NumberField/UnitField.d.ts.map +1 -0
- package/dist/components/NumberField/index.d.ts +2 -0
- package/dist/components/NumberField/index.d.ts.map +1 -1
- package/dist/components/NumberField/index.js +2 -2
- package/dist/components/OptionsBar/index.js +1 -1
- package/dist/components/PaintField/PaintField.d.ts +36 -0
- package/dist/components/PaintField/PaintField.d.ts.map +1 -0
- package/dist/components/PaintField/index.d.ts +3 -0
- package/dist/components/PaintField/index.d.ts.map +1 -0
- package/dist/components/PaintField/index.js +2 -0
- package/dist/components/PaintInput/PaintInput.d.ts.map +1 -1
- package/dist/components/PaintInput/index.js +1 -1
- package/dist/components/PatternPicker/index.js +1 -1
- package/dist/components/Plot2D/Plot2D.d.ts +38 -2
- package/dist/components/Plot2D/Plot2D.d.ts.map +1 -1
- package/dist/components/Plot2D/index.d.ts +2 -1
- package/dist/components/Plot2D/index.d.ts.map +1 -1
- package/dist/components/Plot2D/index.js +2 -2
- package/dist/components/Plot2D/ticks.d.ts +43 -0
- package/dist/components/Plot2D/ticks.d.ts.map +1 -0
- package/dist/components/PointPlotter/PointPlotter.d.ts +3 -1
- package/dist/components/PointPlotter/PointPlotter.d.ts.map +1 -1
- package/dist/components/PointPlotter/index.js +1 -1
- package/dist/components/Powerline/index.js +1 -1
- package/dist/components/Prefs/PrefsForm.d.ts +4 -0
- package/dist/components/Prefs/PrefsForm.d.ts.map +1 -1
- package/dist/components/Prefs/index.d.ts +1 -1
- package/dist/components/Prefs/index.d.ts.map +1 -1
- package/dist/components/Prefs/index.js +3 -3
- package/dist/components/Prefs/schema.d.ts +5 -2
- package/dist/components/Prefs/schema.d.ts.map +1 -1
- package/dist/components/Properties/PinDot.d.ts +18 -0
- package/dist/components/Properties/PinDot.d.ts.map +1 -0
- package/dist/components/Properties/PropertyPanel.d.ts +106 -12
- package/dist/components/Properties/PropertyPanel.d.ts.map +1 -1
- package/dist/components/Properties/index.d.ts +4 -2
- package/dist/components/Properties/index.d.ts.map +1 -1
- package/dist/components/Properties/index.js +2 -2
- package/dist/components/Properties/storyLayouts.d.ts +1 -1
- package/dist/components/RadioGroup/index.js +1 -1
- package/dist/components/RangeSlider/index.js +1 -1
- package/dist/components/ResizeHandle/index.js +1 -1
- package/dist/components/Select/Select.d.ts +8 -0
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/index.js +1 -1
- package/dist/components/SelectionPanel/index.js +1 -1
- package/dist/components/Sidebar/index.js +1 -1
- package/dist/components/SidebarPanel/index.js +1 -1
- package/dist/components/Slider/Slider.d.ts +19 -1
- package/dist/components/Slider/Slider.d.ts.map +1 -1
- package/dist/components/Slider/index.d.ts +1 -1
- package/dist/components/Slider/index.d.ts.map +1 -1
- package/dist/components/Slider/index.js +1 -1
- package/dist/components/StatusBar/index.js +1 -1
- package/dist/components/Switch/index.js +1 -1
- package/dist/components/Tabs/index.js +1 -1
- package/dist/components/Timeline/Lane.d.ts +9 -7
- package/dist/components/Timeline/Lane.d.ts.map +1 -1
- package/dist/components/Timeline/LaneGraph.d.ts +24 -0
- package/dist/components/Timeline/LaneGraph.d.ts.map +1 -0
- package/dist/components/Timeline/Ruler.d.ts.map +1 -1
- package/dist/components/Timeline/Timeline.d.ts.map +1 -1
- package/dist/components/Timeline/index.js +2 -2
- package/dist/components/Timeline/keys.d.ts +4 -3
- package/dist/components/Timeline/keys.d.ts.map +1 -1
- package/dist/components/Timeline/timeScale.d.ts +2 -0
- package/dist/components/Timeline/timeScale.d.ts.map +1 -1
- package/dist/components/Toast/index.js +1 -1
- package/dist/components/ToggleBar/index.js +1 -1
- package/dist/components/TokenPanel/TokenPanel.d.ts +33 -0
- package/dist/components/TokenPanel/TokenPanel.d.ts.map +1 -0
- package/dist/components/TokenPanel/color.d.ts +7 -0
- package/dist/components/TokenPanel/color.d.ts.map +1 -0
- package/dist/components/TokenPanel/index.d.ts +5 -0
- package/dist/components/TokenPanel/index.d.ts.map +1 -0
- package/dist/components/TokenPanel/index.js +2 -0
- package/dist/components/TokenPanel/tokenTypes.d.ts +31 -0
- package/dist/components/TokenPanel/tokenTypes.d.ts.map +1 -0
- package/dist/components/ToolButton/index.js +1 -1
- package/dist/components/ToolGroup/index.js +1 -1
- package/dist/components/ToolPalette/index.js +1 -1
- package/dist/components/paintValue.d.ts +8 -0
- package/dist/components/paintValue.d.ts.map +1 -1
- package/dist/format/number.d.ts +16 -0
- package/dist/format/number.d.ts.map +1 -1
- package/dist/handles.d.ts +20 -0
- package/dist/handles.d.ts.map +1 -0
- package/dist/icons/Icon.d.ts +10 -2
- package/dist/icons/Icon.d.ts.map +1 -1
- package/dist/icons/index.d.ts +6 -3
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/paths.d.ts +270 -16
- package/dist/icons/paths.d.ts.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -65
- package/dist/index.js.map +1 -1
- package/dist/paintPreview.d.ts +13 -0
- package/dist/paintPreview.d.ts.map +1 -0
- package/dist/snap.d.ts +5 -0
- package/dist/snap.d.ts.map +1 -0
- package/dist/style.css +1 -1
- package/dist/useAsyncOptions.d.ts +38 -0
- package/dist/useAsyncOptions.d.ts.map +1 -0
- package/package.json +8 -6
- package/dist/chunks/BandEditor-CUlbOlJi.js.map +0 -1
- package/dist/chunks/ComboBox-BBRRWvdc.js +0 -107
- package/dist/chunks/ComboBox-BBRRWvdc.js.map +0 -1
- package/dist/chunks/CurveEditor-DtjdpJVB.js +0 -784
- package/dist/chunks/CurveEditor-DtjdpJVB.js.map +0 -1
- package/dist/chunks/DetentSlider-BpD7bgjN.js +0 -83
- package/dist/chunks/DetentSlider-BpD7bgjN.js.map +0 -1
- package/dist/chunks/GradientEditor-BLpAtMDK.js.map +0 -1
- package/dist/chunks/Icon-DBXtBB1f.js +0 -101
- package/dist/chunks/Icon-DBXtBB1f.js.map +0 -1
- package/dist/chunks/Keycaps-DSeq6QWH.js.map +0 -1
- package/dist/chunks/NumberField-DBowmOfe.js +0 -62
- package/dist/chunks/NumberField-DBowmOfe.js.map +0 -1
- package/dist/chunks/PaintInput-B7AScmHN.js +0 -118
- package/dist/chunks/PaintInput-B7AScmHN.js.map +0 -1
- package/dist/chunks/Plot2D-C97pO13t.js +0 -134
- package/dist/chunks/Plot2D-C97pO13t.js.map +0 -1
- package/dist/chunks/PointPlotter-CbDKVggs.js.map +0 -1
- package/dist/chunks/Prefs-4JSEtuU-.js +0 -294
- package/dist/chunks/Prefs-4JSEtuU-.js.map +0 -1
- package/dist/chunks/Properties-BpUYKi7-.js +0 -758
- package/dist/chunks/Properties-BpUYKi7-.js.map +0 -1
- package/dist/chunks/Select-CacMfs4v.js.map +0 -1
- package/dist/chunks/SelectionPanel-C59wavdA.js.map +0 -1
- package/dist/chunks/Slider-Bs-KSYgQ.js +0 -303
- package/dist/chunks/Slider-Bs-KSYgQ.js.map +0 -1
- package/dist/chunks/Timeline-Bz806JbE.js +0 -908
- package/dist/chunks/Timeline-Bz806JbE.js.map +0 -1
- package/dist/chunks/ToggleBar-Bxwv6xvj.js.map +0 -1
- package/dist/chunks/icons-BV27dhKB.js.map +0 -1
- package/dist/chunks/number-Bv1nbhOJ.js +0 -18
- package/dist/chunks/number-Bv1nbhOJ.js.map +0 -1
- package/dist/chunks/range.module-CvSfABmu.js +0 -8
- package/dist/chunks/range.module-CvSfABmu.js.map +0 -1
- package/dist/chunks/schema-CtWwGbqJ.js.map +0 -1
- package/dist/chunks/segmentedControl.module-BgvAPkSZ.js +0 -11
- package/dist/chunks/segmentedControl.module-BgvAPkSZ.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Timeline-Bz806JbE.js","names":[],"sources":["../../src/components/Timeline/Timeline.module.css","../../src/components/Timeline/easingSpec.ts","../../src/components/Timeline/EasingPicker.tsx","../../src/components/Timeline/timeScale.ts","../../src/components/Timeline/lanes.ts","../../src/components/Timeline/keys.ts","../../src/components/Timeline/Lane.tsx","../../src/components/Timeline/Ruler.tsx","../../src/components/Timeline/Transport.tsx","../../src/components/Timeline/Timeline.tsx","../../src/components/Timeline/AnimatedTimeline.tsx"],"sourcesContent":[".ruler {\n position: relative;\n height: var(--wzl-control-h, 24px);\n border-bottom: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n cursor: ew-resize;\n user-select: none;\n overflow: hidden;\n}\n\n.tick {\n position: absolute;\n top: 0;\n transform: translateX(2px);\n color: var(--wzl-fg-muted);\n pointer-events: none;\n white-space: nowrap;\n}\n\n.tick::before {\n content: '';\n position: absolute;\n left: -2px;\n top: 0;\n bottom: 0;\n border-left: 1px solid var(--wzl-border);\n}\n\n.playhead {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--wzl-accent-strong);\n pointer-events: none;\n}\n\n.lane {\n display: flex;\n align-items: stretch;\n min-height: 20px;\n border-bottom: 1px solid var(--wzl-line-subtle);\n}\n\n.laneLabel {\n flex: 0 0 var(--wzl-timeline-label-w, 120px);\n display: flex;\n align-items: center;\n gap: 4px;\n padding-inline: 6px;\n padding-inline-start: calc(6px + var(--depth, 0) * 12px);\n font-size: var(--wzl-font-size);\n color: var(--wzl-fg);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.lane[data-depth='1'] .laneLabel { --depth: 1; }\n.lane[data-depth='2'] .laneLabel { --depth: 2; }\n.lane[data-depth='3'] .laneLabel { --depth: 3; }\n\n.disclosure { display: inline-flex; flex: none; cursor: pointer; color: var(--wzl-fg-muted); }\n.disclosureIcon { transition: transform var(--wzl-motion-fast) ease; }\n.disclosureCollapsed { transform: rotate(-90deg); }\n\n.laneTrack {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n background: var(--wzl-surface);\n}\n\n.key, .eventMark {\n position: absolute;\n top: 50%;\n width: 9px;\n height: 9px;\n margin-left: -4.5px;\n background: var(--wzl-fg-muted);\n cursor: ew-resize;\n}\n\n.key { transform: translateY(-50%) rotate(45deg); }\n.eventMark { transform: translateY(-50%); border-radius: 50%; }\n\n.key[aria-current='true'], .eventMark[aria-current='true'] { background: var(--wzl-accent-strong); }\n.key:focus-visible, .eventMark:focus-visible { outline: 2px solid var(--wzl-focus-ring); outline-offset: 1px; }\n\n/* The key stays drawn where it was committed while its ghost tracks the\n pointer, so the drag reads as a move from somewhere to somewhere. */\n.key[data-dragging='true'], .eventMark[data-dragging='true'] { opacity: 0.4; }\n\n.keyGhost, .eventGhost {\n position: absolute;\n top: 50%;\n width: 9px;\n height: 9px;\n margin-left: -4.5px;\n background: var(--wzl-accent-soft);\n border: 1px dashed var(--wzl-accent-strong);\n pointer-events: none;\n}\n\n.keyGhost { transform: translateY(-50%) rotate(45deg); }\n.eventGhost { transform: translateY(-50%); border-radius: 50%; }\n.lane[data-mode='graph'] .keyGhost { top: auto; transform: translateY(50%) rotate(45deg); }\n\n.curve {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n fill: none;\n stroke: var(--wzl-accent-strong);\n stroke-width: 1;\n}\n\n.lane[data-mode='graph'] { min-height: 72px; }\n.lane[data-mode='graph'] .key { top: auto; transform: translateY(50%) rotate(45deg); }\n\n.nestedBar {\n position: absolute;\n top: 4px;\n bottom: 4px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.transport {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 6px;\n border-bottom: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n}\n\n.transportButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n padding: 2px 6px;\n /* Sits on `--wzl-surface-raised`, so a transparent fill and a border that\n close in value leave it reading as text rather than as a control. */\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n color: var(--wzl-fg);\n cursor: pointer;\n}\n\n.transportButton:hover { border-color: var(--wzl-fg-muted); }\n\n.transportButton[aria-checked='true'] {\n background: var(--wzl-accent-soft);\n border-color: var(--wzl-accent-strong);\n color: var(--wzl-accent-strong);\n}\n\n/* The bar's chrome lives on the row, so controls beside `<Transport>` sit\n inside it rather than hanging off the panel's corner. `.transport` keeps its\n own chrome for standalone use. */\n.transportRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding-inline-end: 6px;\n border-bottom: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n}\n\n.transportRow > .transport {\n flex: 1 1 auto;\n border-bottom: 0;\n background: transparent;\n}\n\n.time { font-variant-numeric: tabular-nums; color: var(--wzl-fg-muted); }\n.rate { display: inline-flex; align-items: center; gap: 6px; color: var(--wzl-fg-muted); }\n.rateSlider { width: 88px; }\n\n/* The detents carry no labels in a bar this dense, so the chosen rate is said\n once, here, in a fixed-width cell so the controls beside it never shift. */\n.rateReadout {\n min-width: 3ch;\n font-variant-numeric: tabular-nums;\n color: var(--wzl-fg);\n}\n\n.root {\n display: flex;\n flex-direction: column;\n min-height: 0;\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n color: var(--wzl-fg);\n /* `--wzl-font-ui` is condensed, so it reads a size smaller than it measures.\n Everything below inherits this; override per rule only to go up. */\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-sm);\n line-height: var(--wzl-leading-snug);\n}\n\n.label { padding: 4px 6px; color: var(--wzl-fg-muted); }\n\n.body { display: flex; align-items: stretch; }\n.gutter { flex: 0 0 var(--wzl-timeline-label-w, 120px); border-right: 1px solid var(--wzl-border); }\n.rulerWrap { flex: 1 1 auto; min-width: 0; }\n\n.lanes {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.inspector {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 6px;\n border-top: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n}\n\n.segment {\n position: absolute;\n top: 0;\n bottom: 0;\n cursor: pointer;\n}\n\n.segment[aria-current='true'] { background: var(--wzl-accent-soft); }\n.segment:focus-visible { outline: 2px solid var(--wzl-focus-ring); outline-offset: -2px; }\n\n.easingPicker { display: inline-flex; align-items: center; gap: 6px; }\n.easingLabel { font-variant-numeric: tabular-nums; color: var(--wzl-fg-muted); }\n\n.easingPreview {\n width: 32px;\n height: 32px;\n fill: none;\n stroke: var(--wzl-accent-strong);\n stroke-width: 1;\n border: 1px solid var(--wzl-border);\n}\n\n.bezierHandle {\n position: absolute;\n width: 7px;\n height: 7px;\n margin: -3.5px 0 0 -3.5px;\n border-radius: 50%;\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-accent-strong);\n cursor: grab;\n}\n","import { EASINGS, resolveEasing, type EasingName, type EasingSpec } from '@weasel-js/core';\n\nfunction bezierOf(spec: EasingSpec): readonly [number, number, number, number] | null {\n if (typeof spec === 'object' && spec !== null && 'bezier' in spec) return spec.bezier;\n return null;\n}\n\n/** Control points, for the graph view's draggable handles. `null` means this\n * spec has no handles to drag — the view offers to convert it to a bezier. */\nexport function easingBezier(spec?: EasingSpec): readonly [number, number, number, number] | null {\n if (spec === undefined) return null;\n return bezierOf(spec);\n}\n\n/** What the picker shows for a spec.\n *\n * A function is matched against the built-ins by reference so a consumer that\n * passed `easeOutBack` directly still reads back as `'easeOutBack'`. A wrapped\n * or hand-written function cannot be named and reads as `'custom'` — which is\n * why an editor writes names and control points rather than functions. */\nexport function easingLabel(spec?: EasingSpec): string {\n if (spec === undefined) return 'linear';\n if (typeof spec === 'string') return spec;\n const b = bezierOf(spec);\n if (b) return `cubic-bezier(${b.join(', ')})`;\n for (const [name, fn] of Object.entries(EASINGS)) {\n if (fn === spec) return name;\n }\n return 'custom';\n}\n\n/** `count` evenly spaced samples of the curve over 0..1, for drawing it. */\nexport function sampleEasing(spec: EasingSpec | undefined, count: number): number[] {\n const fn = resolveEasing(spec);\n const out = new Array<number>(count);\n const last = count - 1;\n for (let i = 0; i < count; i++) out[i] = fn(last === 0 ? 0 : i / last);\n return out;\n}\n\n/** Every built-in name, for the picker's list. */\nexport const EASING_NAMES = Object.keys(EASINGS) as EasingName[];\n","import type { ReactElement } from 'react';\nimport type { EasingSpec } from '@weasel-js/core';\nimport s from './Timeline.module.css';\nimport { EASING_NAMES, easingBezier, easingLabel, sampleEasing } from './easingSpec';\n\n/** Samples drawn in the inline curve preview. */\nconst PREVIEW_SAMPLES = 24;\n\n/** Control points for the named curves the picker can convert. A name the table\n * does not cover converts to the CSS default rather than refusing. */\nconst BEZIER_OF: Partial<Record<string, readonly [number, number, number, number]>> = {\n linear: [0, 0, 1, 1],\n easeInQuad: [0.11, 0, 0.5, 0],\n easeOutQuad: [0.5, 1, 0.89, 1],\n easeInOutQuad: [0.45, 0, 0.55, 1],\n easeInCubic: [0.32, 0, 0.67, 0],\n easeOutCubic: [0.33, 1, 0.68, 1],\n easeInOutCubic: [0.65, 0, 0.35, 1],\n easeInSine: [0.12, 0, 0.39, 0],\n easeOutSine: [0.61, 1, 0.88, 1],\n easeInOutSine: [0.37, 0, 0.63, 1],\n};\n\nconst DEFAULT_BEZIER: readonly [number, number, number, number] = [0.25, 0.1, 0.25, 1];\n\nexport interface EasingPickerProps {\n value: EasingSpec | undefined;\n onChange: (next: EasingSpec | undefined) => void;\n}\n\nexport function EasingPicker(props: EasingPickerProps): ReactElement {\n const { value, onChange } = props;\n const bezier = easingBezier(value);\n const label = easingLabel(value);\n const named = bezier === null && (EASING_NAMES as string[]).includes(label);\n const selectValue = bezier !== null ? 'custom' : named ? label : 'linear';\n\n const points = sampleEasing(value, PREVIEW_SAMPLES)\n .map((v, i) => `${(i / (PREVIEW_SAMPLES - 1)) * 100},${100 - v * 100}`)\n .join(' ');\n\n return (\n <div className={s.easingPicker}>\n <label>\n Easing\n <select\n value={selectValue}\n onChange={(e) => onChange(e.target.value === 'linear' ? undefined : e.target.value as EasingSpec)}\n >\n <option value=\"linear\">linear</option>\n {EASING_NAMES.filter((n) => n !== 'linear').map((n) => <option key={n} value={n}>{n}</option>)}\n {bezier !== null ? <option value=\"custom\">custom</option> : null}\n </select>\n </label>\n\n {bezier !== null ? <span className={s.easingLabel}>{label}</span> : (\n <button\n type=\"button\"\n className={s.transportButton}\n onClick={() => onChange({ bezier: BEZIER_OF[selectValue] ?? DEFAULT_BEZIER })}\n >\n Convert to bezier\n </button>\n )}\n\n <svg className={s.easingPreview} data-testid=\"easing-preview\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\">\n <polyline points={points} vectorEffect=\"non-scaling-stroke\" />\n </svg>\n </div>\n );\n}\n","/** A visible span of timeline time, in ms. */\nexport interface TimeWindow {\n from: number;\n to: number;\n}\n\nexport interface TimeScale {\n window: TimeWindow;\n widthPx: number;\n toPx(ms: number): number;\n toMs(px: number): number;\n}\n\n/** Narrowest window the user can zoom to, in ms. Below this the playhead and\n * the keys it sits between stop being separable at any sane track width. */\nconst MIN_SPAN_MS = 1;\n\nexport function createTimeScale(window: TimeWindow, widthPx: number): TimeScale {\n const span = window.to - window.from;\n const perMs = span === 0 ? 0 : widthPx / span;\n const perPx = widthPx === 0 ? 0 : span / widthPx;\n return {\n window,\n widthPx,\n toPx: (ms) => (ms - window.from) * perMs,\n toMs: (px) => window.from + px * perPx,\n };\n}\n\n/** Where `ms` sits in the window, as a fraction. The width-free half of\n * `createTimeScale` — a CSS `left: %` needs no pixel width, and asking for one\n * is what drove three copies of this arithmetic into the lane and the ruler. */\nexport function toFraction(window: TimeWindow, ms: number): number {\n const span = window.to - window.from;\n return span === 0 ? 0 : (ms - window.from) / span;\n}\n\n/** `toFraction` as a CSS percentage. */\nexport function toPercent(window: TimeWindow, ms: number): string {\n return `${toFraction(window, ms) * 100}%`;\n}\n\n/** A duration as a fraction of the window. Distinct from `toFraction`, which\n * measures a position and so subtracts `window.from`. */\nexport function spanFraction(window: TimeWindow, ms: number): number {\n const span = window.to - window.from;\n return span === 0 ? 0 : ms / span;\n}\n\n/** A duration as a CSS percentage, for a width. */\nexport function spanPercent(window: TimeWindow, ms: number): string {\n return `${spanFraction(window, ms) * 100}%`;\n}\n\nfunction clampWindow(w: TimeWindow, bounds: TimeWindow): TimeWindow {\n const span = Math.min(w.to - w.from, bounds.to - bounds.from);\n let from = w.from;\n if (from < bounds.from) from = bounds.from;\n if (from + span > bounds.to) from = bounds.to - span;\n return { from, to: from + span };\n}\n\n/** Scale the window by `factor` about `atMs`, which stays at the same fraction\n * of the track. `factor < 1` zooms in. */\nexport function zoomWindow(\n w: TimeWindow, atMs: number, factor: number, bounds: TimeWindow,\n): TimeWindow {\n const span = w.to - w.from;\n const maxSpan = bounds.to - bounds.from;\n const nextSpan = Math.min(maxSpan, Math.max(MIN_SPAN_MS, span * factor));\n const frac = span === 0 ? 0.5 : (atMs - w.from) / span;\n return clampWindow({ from: atMs - frac * nextSpan, to: atMs + (1 - frac) * nextSpan }, bounds);\n}\n\n/** Shift the window by `byMs`, preserving its span. */\nexport function panWindow(w: TimeWindow, byMs: number, bounds: TimeWindow): TimeWindow {\n return clampWindow({ from: w.from + byMs, to: w.to + byMs }, bounds);\n}\n\n/** 1, 2, 5, 10, 20, 50, … — the tick steps that read as round numbers. */\nfunction niceStep(roughMs: number): number {\n const mag = Math.pow(10, Math.floor(Math.log10(roughMs)));\n const norm = roughMs / mag;\n const step = norm <= 1 ? 1 : norm <= 2 ? 2 : norm <= 5 ? 5 : 10;\n return step * mag;\n}\n\n/** Ruler tick times inside `w`, spaced at least `minSpacingPx` apart. */\nexport function tickTimes(w: TimeWindow, widthPx: number, minSpacingPx: number): number[] {\n const span = w.to - w.from;\n if (widthPx <= 0 || span <= 0) return [];\n const step = niceStep((minSpacingPx / widthPx) * span);\n const out: number[] = [];\n for (let t = Math.ceil(w.from / step) * step; t <= w.to + 1e-9; t += step) {\n out.push(Math.round(t / step) * step);\n }\n return out;\n}\n","import type { SampledTrack, Track } from '@weasel-js/core';\n\nexport interface LaneRow {\n /** Stable across re-renders and edits: the dotted index path. */\n key: string;\n /** Index path into the track tree, outermost first. */\n path: number[];\n track: Track;\n label: string;\n /** Nesting depth; 0 for a top-level track. */\n depth: number;\n /** Accumulated `at` from every nested parent, in ms. A key drawn on this row\n * sits at `offset + key.t` on the ruler. */\n offset: number;\n kind: Track['kind'];\n /** True only for a sampled track whose every key value is a number — the\n * sole case where a value axis has an honest meaning. */\n numeric: boolean;\n}\n\nfunction isNumeric(track: Track): boolean {\n if (track.kind !== 'sampled') return false;\n const keys = (track as SampledTrack<unknown>).keys;\n return keys.length > 0 && keys.every((k) => typeof k.value === 'number');\n}\n\n/** Flatten the track tree to rows, descending only into expanded nested\n * timelines. `expanded` holds the `key` of each open nested row. */\nexport function buildLanes(\n tracks: readonly Track[],\n expanded: ReadonlySet<string>,\n): LaneRow[] {\n const out: LaneRow[] = [];\n const walk = (list: readonly Track[], path: number[], depth: number, offset: number): void => {\n list.forEach((track, i) => {\n const nextPath = [...path, i];\n const key = nextPath.join('.');\n out.push({\n key,\n path: nextPath,\n track,\n label: track.label ?? `${track.kind} ${i}`,\n depth,\n offset,\n kind: track.kind,\n numeric: isNumeric(track),\n });\n if (track.kind === 'timeline' && expanded.has(key)) {\n walk(track.timeline.tracks, nextPath, depth + 1, offset + track.at);\n }\n });\n };\n walk(tracks, [], 0, 0);\n return out;\n}\n\n/** The track at an index path, or `undefined` when the path leaves the tree. */\nexport function trackAtPath(tracks: readonly Track[], path: readonly number[]): Track | undefined {\n let list: readonly Track[] = tracks;\n let found: Track | undefined;\n for (const i of path) {\n found = list[i];\n if (!found) return undefined;\n list = found.kind === 'timeline' ? found.timeline.tracks : [];\n }\n return found;\n}\n","import { sampleTrack, type EasingSpec, type EventTrack, type Keyframe, type SampledTrack, type TimelineTrack, type Track } from '@weasel-js/core';\nimport { trackAtPath } from './lanes';\n\nexport interface KeySelection {\n /** Index path into the track tree, outermost first — a `LaneRow.path`. A\n * top-level track is `[i]`; a track inside the nested timeline at `i` is\n * `[i, j]`. */\n trackPath: readonly number[];\n keyIndex: number;\n}\n\nexport interface KeyEdit {\n tracks: Track[];\n selection: KeySelection | null;\n}\n\n/** True when two index paths address the same track. */\nexport function samePath(a: readonly number[], b: readonly number[]): boolean {\n return a.length === b.length && a.every((n, i) => n === b[i]);\n}\n\n/** Shallow-clone one track, replacing only its key list. `onTick`, `fire`,\n * `interpolate` and `interpolator` survive as references, which is what lets a\n * running timeline take an edited track without rewiring. */\nfunction withKeys(track: Track, keys: Keyframe<unknown>[]): Track {\n return { ...(track as SampledTrack<unknown>), keys } as Track;\n}\n\nfunction withEvents(track: Track, events: EventTrack['events']): Track {\n return { ...(track as EventTrack), events } as Track;\n}\n\n/** Replace the track at `path`, cloning the timeline tracks along the way so\n * the caller's tree is untouched at every level, not just the top. */\nfunction replaceAtPath(tracks: readonly Track[], path: readonly number[], next: Track): Track[] {\n const out = tracks.slice();\n const [i, ...rest] = path;\n if (rest.length === 0) {\n out[i] = next;\n return out;\n }\n const parent = out[i];\n if (parent?.kind !== 'timeline') return out;\n const tt = parent as TimelineTrack;\n out[i] = {\n ...tt,\n timeline: { ...tt.timeline, tracks: replaceAtPath(tt.timeline.tracks, rest, next) },\n };\n return out;\n}\n\n/** Move a key or an event crossing to `toMs`, clamped at zero. `toMs` is in the\n * addressed track's own time, not the ruler's — a nested track's keys are\n * measured from its parent's `at`.\n *\n * Re-sorts, and reports the moved entry's new index. `sampleTrack` binary-\n * searches without sorting first, so a drag past a neighbour that left the list\n * unsorted would sample the wrong segment and raise nothing. */\nexport function moveKey(\n tracks: readonly Track[], sel: KeySelection, toMs: number,\n): KeyEdit {\n const track = trackAtPath(tracks, sel.trackPath);\n const t = Math.max(0, toMs);\n\n if (track?.kind === 'sampled') {\n const st = track as SampledTrack<unknown>;\n const moved = { ...st.keys[sel.keyIndex], t };\n const keys = st.keys.filter((_, i) => i !== sel.keyIndex);\n const at = keys.findIndex((k) => k.t > t);\n const keyIndex = at === -1 ? keys.length : at;\n keys.splice(keyIndex, 0, moved);\n return {\n tracks: replaceAtPath(tracks, sel.trackPath, withKeys(track, keys)),\n selection: { trackPath: sel.trackPath, keyIndex },\n };\n }\n\n if (track?.kind === 'event') {\n const et = track as EventTrack;\n const moved = { ...et.events[sel.keyIndex], t };\n const events = et.events.filter((_, i) => i !== sel.keyIndex);\n const at = events.findIndex((e) => e.t > t);\n const keyIndex = at === -1 ? events.length : at;\n events.splice(keyIndex, 0, moved);\n return {\n tracks: replaceAtPath(tracks, sel.trackPath, withEvents(track, events)),\n selection: { trackPath: sel.trackPath, keyIndex },\n };\n }\n\n return { tracks: tracks.slice(), selection: sel };\n}\n\n/** Insert a key at `atMs`, seeded with whatever the track already reads there\n * so inserting alone never changes the motion. */\nexport function insertKey(\n tracks: readonly Track[], trackPath: readonly number[], atMs: number,\n): KeyEdit {\n const track = trackAtPath(tracks, trackPath);\n if (track?.kind !== 'sampled') return { tracks: tracks.slice(), selection: null };\n\n const st = track as SampledTrack<unknown>;\n const t = Math.max(0, atMs);\n const value = st.keys.length === 0 ? 0 : sampleTrack(st, t);\n const keys = st.keys.slice();\n const at = keys.findIndex((k) => k.t > t);\n const keyIndex = at === -1 ? keys.length : at;\n keys.splice(keyIndex, 0, { t, value });\n return {\n tracks: replaceAtPath(tracks, trackPath, withKeys(track, keys)),\n selection: { trackPath, keyIndex },\n };\n}\n\n/** Remove a key, selecting the one before it. */\nexport function deleteKey(tracks: readonly Track[], sel: KeySelection): KeyEdit {\n const track = trackAtPath(tracks, sel.trackPath);\n if (track?.kind !== 'sampled') return { tracks: tracks.slice(), selection: sel };\n\n const st = track as SampledTrack<unknown>;\n const keys = st.keys.filter((_, i) => i !== sel.keyIndex);\n return {\n tracks: replaceAtPath(tracks, sel.trackPath, withKeys(track, keys)),\n selection: keys.length === 0\n ? null\n : { trackPath: sel.trackPath, keyIndex: Math.max(0, sel.keyIndex - 1) },\n };\n}\n\n/** Set the easing shaping the approach INTO the selected key. */\nexport function setKeyEasing(\n tracks: readonly Track[], sel: KeySelection, easing: EasingSpec | undefined,\n): Track[] {\n const track = trackAtPath(tracks, sel.trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n\n const st = track as SampledTrack<unknown>;\n const keys = st.keys.slice();\n const { easing: _drop, ...rest } = keys[sel.keyIndex];\n keys[sel.keyIndex] = easing === undefined ? rest : { ...rest, easing };\n return replaceAtPath(tracks, sel.trackPath, withKeys(track, keys));\n}\n\n/** Set the selected key's value, leaving its time alone. */\nexport function setKeyValue(\n tracks: readonly Track[], sel: KeySelection, value: unknown,\n): Track[] {\n const track = trackAtPath(tracks, sel.trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n\n const st = track as SampledTrack<unknown>;\n const keys = st.keys.slice();\n keys[sel.keyIndex] = { ...keys[sel.keyIndex], value };\n return replaceAtPath(tracks, sel.trackPath, withKeys(track, keys));\n}\n\n/** Snap `ms` to the nearest candidate within `toleranceMs`, else return it. */\nexport function snapTime(ms: number, candidates: readonly number[], toleranceMs: number): number {\n let best = ms;\n let bestDist = toleranceMs;\n for (const c of candidates) {\n const d = Math.abs(c - ms);\n if (d <= bestDist) {\n best = c;\n bestDist = d;\n }\n }\n return best;\n}\n","import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react';\nimport { cubicBezierEasing, openPointerSession, type EasingSpec, type EventTrack, type PointerSession, type SampledTrack, type TimelineTrack } from '@weasel-js/core';\nimport { ChevronIcon } from '../../icons';\nimport s from './Timeline.module.css';\nimport { createTimeScale, spanPercent, toFraction, toPercent, type TimeWindow } from './timeScale';\nimport { snapTime } from './keys';\nimport { easingBezier, sampleEasing } from './easingSpec';\nimport type { LaneRow } from './lanes';\n\n/** Keeps the extreme keys inside the lane: at 0% a marker centres on the lane's\n * border and hangs half into its neighbour. Symmetric, so the midpoint holds. */\nconst V_INSET_PCT = 8;\n\n/** Snap radius, in track pixels. */\nconst SNAP_PX = 6;\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\n\n/** Samples per segment when drawing an eased curve in graph mode. */\nconst CURVE_SAMPLES = 16;\n\nexport interface LaneProps {\n row: LaneRow;\n window: TimeWindow;\n mode: 'dope' | 'graph';\n /** Index of the selected key on THIS row, or null. */\n selection: number | null;\n onSelect: (keyIndex: number) => void;\n /** Live during a drag. `value` is set only in graph mode on a numeric row. */\n onKeyInput: (keyIndex: number, toMs: number, value?: number) => void;\n /** Once, at the end of a gesture. `value` is set only in graph mode on a numeric row. */\n onKeyCommit: (keyIndex: number, toMs: number, value?: number) => void;\n onInsert: (atMs: number) => void;\n onToggleExpand: () => void;\n expanded: boolean;\n /** Times a dragged key snaps to. */\n snapTimes: readonly number[];\n /** Index of the key a selected segment runs INTO, or null. */\n selectedSegment?: number | null;\n onSelectSegment?: (keyIndex: number) => void;\n /** Once, at the end of a bezier-handle drag. */\n onEasingCommit?: (keyIndex: number, easing: EasingSpec) => void;\n}\n\n/** `count` evenly spaced samples of a bezier curve, straight from its control\n * points — not through `resolveEasing`. A drag writes a fresh set of control\n * points on every pointermove, and `resolveEasing`'s cache is keyed by them:\n * routing a live preview through it would leave one permanent cache entry per\n * pixel of drag. Only the value committed on pointerup ever reaches it. */\nfunction sampleBezierDirect(points: readonly [number, number, number, number], count: number): number[] {\n const fn = cubicBezierEasing(...points);\n const out = new Array<number>(count);\n const last = count - 1;\n for (let i = 0; i < count; i++) out[i] = fn(last === 0 ? 0 : i / last);\n return out;\n}\n\nfunction entryTimes(row: LaneRow): number[] {\n if (row.kind === 'sampled') return (row.track as SampledTrack<unknown>).keys.map((k) => k.t);\n if (row.kind === 'event') return (row.track as EventTrack).events.map((e) => e.t);\n return [];\n}\n\nexport function Lane(props: LaneProps): ReactElement {\n const {\n row, window: win, mode, selection, onSelect, onKeyInput, onKeyCommit, onInsert, onToggleExpand, expanded, snapTimes,\n selectedSegment = null, onSelectSegment, onEasingCommit,\n } = props;\n const trackRef = useRef<HTMLDivElement | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n // Live position of a bezier handle being dragged; null once the drag ends.\n // Distinct from the committed spec on the key so a preview never writes it.\n const [dragBezier, setDragBezier] = useState<{ keyIndex: number; points: readonly [number, number, number, number] } | null>(null);\n\n // Where the dragged key would land. Owned here rather than derived from the\n // tracks prop, so a drag previews whether or not the consumer wires\n // `onKeyInput` — the committed key stays put underneath as the origin.\n const [drag, setDrag] = useState<{ keyIndex: number; t: number; value?: number } | null>(null);\n\n const pct = (ms: number): string => toPercent(win, ms + row.offset);\n\n const msAt = (clientX: number): number => {\n const rect = trackRef.current?.getBoundingClientRect();\n if (!rect) return win.from;\n return createTimeScale(win, rect.width).toMs(clientX - rect.left) - row.offset;\n };\n\n const snapPxToMs = (): number => {\n const width = trackRef.current?.getBoundingClientRect().width ?? 0;\n const scale = createTimeScale(win, width);\n return scale.toMs(SNAP_PX) - scale.toMs(0);\n };\n\n // Value axis: only a numeric sampled row in graph mode has an honest one.\n const graph = mode === 'graph' && row.numeric;\n const sampledKeys = row.kind === 'sampled' ? (row.track as SampledTrack<number>).keys : [];\n const values = sampledKeys.map((k) => k.value);\n const lo = graph ? Math.min(...values) : 0;\n const hi = graph ? Math.max(...values) : 1;\n const vSpan = hi - lo || 1;\n const vPct = (v: number): number => V_INSET_PCT + ((v - lo) / vSpan) * (100 - 2 * V_INSET_PCT);\n\n const valueAt = (clientY: number): number => {\n const rect = trackRef.current?.getBoundingClientRect();\n if (!rect || rect.height === 0) return lo;\n const frac = 1 - (clientY - rect.top) / rect.height;\n const norm = (frac * 100 - V_INSET_PCT) / (100 - 2 * V_INSET_PCT);\n return lo + Math.min(1, Math.max(0, norm)) * vSpan;\n };\n\n const curvePoints = (): string => {\n const pts: string[] = [];\n for (let i = 1; i < sampledKeys.length; i++) {\n const a = sampledKeys[i - 1];\n const b = sampledKeys[i];\n const eased = dragBezier && dragBezier.keyIndex === i\n ? sampleBezierDirect(dragBezier.points, CURVE_SAMPLES)\n : sampleEasing(b.easing, CURVE_SAMPLES);\n for (let j = 0; j < eased.length; j++) {\n const t = a.t + ((b.t - a.t) * j) / (eased.length - 1);\n const v = a.value + (b.value - a.value) * eased[j];\n pts.push(`${toFraction(win, t + row.offset) * 100},${100 - vPct(v)}`);\n }\n }\n return pts.join(' ');\n };\n\n // The segment a bezier handle drags: the key it runs into and the one before it.\n const committedBezier = selectedSegment != null && row.kind === 'sampled'\n ? easingBezier(sampledKeys[selectedSegment]?.easing)\n : null;\n const activeBezier = dragBezier && dragBezier.keyIndex === selectedSegment ? dragBezier.points : committedBezier;\n\n const bezierHandlePos = (segIndex: number, xFrac: number, yFrac: number): { left: string; bottom: string } => {\n const a = sampledKeys[segIndex - 1];\n const b = sampledKeys[segIndex];\n const t = a.t + xFrac * (b.t - a.t);\n const v = a.value + yFrac * (b.value - a.value);\n return { left: pct(t), bottom: `${vPct(v)}%` };\n };\n\n // No pointer capture — see `Ruler`, same idiom across the timeline.\n const beginDrag = (\n down: ReactPointerEvent<HTMLDivElement>,\n cb: { onMove: (ev: PointerEvent) => void; onEnd: (ev: PointerEvent) => void; onCancel: () => void },\n ): void => {\n sessionRef.current?.cancel();\n sessionRef.current = openPointerSession(down.currentTarget, down, cb, { capture: false });\n };\n\n const onHandlePointerDown = (segIndex: number, h: 0 | 1) => (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0 || !committedBezier) return;\n e.stopPropagation();\n const a = sampledKeys[segIndex - 1];\n const b = sampledKeys[segIndex];\n let points: [number, number, number, number] = [...committedBezier];\n\n const at = (ev: { clientX: number; clientY: number }): [number, number, number, number] => {\n const ms = msAt(ev.clientX);\n const xFrac = b.t === a.t ? 0 : Math.min(1, Math.max(0, (ms - a.t) / (b.t - a.t)));\n const v = valueAt(ev.clientY);\n const yFrac = b.value === a.value ? 0 : (v - a.value) / (b.value - a.value);\n const next = [...points] as [number, number, number, number];\n next[h * 2] = xFrac;\n next[h * 2 + 1] = yFrac;\n return next;\n };\n\n const end = (): void => {\n setDragBezier(null);\n sessionRef.current = null;\n };\n beginDrag(e, {\n onMove: (ev) => {\n points = at(ev);\n setDragBezier({ keyIndex: segIndex, points });\n },\n onEnd: (ev) => {\n points = at(ev);\n onEasingCommit?.(segIndex, { bezier: points });\n end();\n },\n onCancel: end,\n });\n };\n\n const onKeyPointerDown = (i: number) => (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0) return;\n e.stopPropagation();\n onSelect(i);\n\n // `snapTimes` are ruler times and `msAt` is track-local, so a nested row\n // has to cross into ruler space to snap and back out to commit.\n const at = (ev: { clientX: number; altKey: boolean }): number => {\n const raw = Math.max(0, msAt(ev.clientX));\n return ev.altKey ? raw : snapTime(raw + row.offset, snapTimes, snapPxToMs()) - row.offset;\n };\n const valueOf = (ev: { clientY: number }): number | undefined =>\n graph ? valueAt(ev.clientY) : undefined;\n\n setDrag({ keyIndex: i, t: times[i], value: values[i] });\n\n const end = (): void => {\n setDrag(null);\n sessionRef.current = null;\n };\n beginDrag(e, {\n onMove: (ev) => {\n const v = valueOf(ev);\n setDrag({ keyIndex: i, t: at(ev), value: v ?? values[i] });\n if (v === undefined) onKeyInput(i, at(ev)); else onKeyInput(i, at(ev), v);\n },\n onEnd: (ev) => {\n const v = valueOf(ev);\n if (v === undefined) onKeyCommit(i, at(ev)); else onKeyCommit(i, at(ev), v);\n end();\n },\n onCancel: end,\n });\n };\n\n const onKeyDown = (i: number, t: number) => (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n const step = KEY_STEP_MS * (e.shiftKey ? 10 : 1);\n if (e.key === 'ArrowRight') { e.preventDefault(); onKeyCommit(i, t + step); }\n if (e.key === 'ArrowLeft') { e.preventDefault(); onKeyCommit(i, Math.max(0, t - step)); }\n };\n\n const times = entryTimes(row);\n\n return (\n <div className={s.lane} data-depth={row.depth} data-mode={mode}>\n <div className={s.laneLabel}>\n {row.kind === 'timeline' ? (\n <span\n role=\"button\"\n tabIndex={0}\n aria-expanded={expanded}\n data-testid=\"timeline-disclosure\"\n className={s.disclosure}\n onClick={onToggleExpand}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggleExpand(); } }}\n >\n <ChevronIcon\n size={14}\n className={[s.disclosureIcon, !expanded && s.disclosureCollapsed].filter(Boolean).join(' ')}\n />\n </span>\n ) : null}\n {row.label}\n </div>\n <div\n className={s.laneTrack}\n ref={trackRef}\n data-testid=\"timeline-lane-track\"\n onDoubleClick={(e) => { if (row.kind === 'sampled') onInsert(Math.max(0, msAt(e.clientX))); }}\n >\n {row.kind === 'timeline' ? (\n <div\n className={s.nestedBar}\n data-testid=\"timeline-nested\"\n style={{ left: pct(0), width: spanPercent(win, (row.track as TimelineTrack).timeline.duration ?? 0) }}\n />\n ) : null}\n {row.kind === 'sampled' ? sampledKeys.slice(1).map((k, idx) => {\n const i = idx + 1;\n return (\n <div\n key={`seg-${i}`}\n role=\"button\"\n tabIndex={0}\n aria-label={`${row.label} segment into ${Math.round(k.t)} ms`}\n aria-current={selectedSegment === i ? 'true' : undefined}\n data-testid=\"timeline-segment\"\n className={s.segment}\n style={{ left: pct(sampledKeys[i - 1].t), width: spanPercent(win, k.t - sampledKeys[i - 1].t) }}\n onClick={() => onSelectSegment?.(i)}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onSelectSegment?.(i); } }}\n />\n );\n }) : null}\n {graph && sampledKeys.length > 1 ? (\n <svg className={s.curve} data-testid=\"timeline-curve\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\">\n <polyline points={curvePoints()} vectorEffect=\"non-scaling-stroke\" />\n </svg>\n ) : null}\n {times.map((t, i) => (\n <div\n key={i}\n role=\"button\"\n tabIndex={0}\n aria-label={`${row.label} key at ${Math.round(t)} ms`}\n aria-current={selection === i ? 'true' : undefined}\n data-testid={row.kind === 'event' ? 'timeline-event' : 'timeline-key'}\n data-dragging={drag?.keyIndex === i ? 'true' : undefined}\n className={row.kind === 'event' ? s.eventMark : s.key}\n style={graph ? { left: pct(t), bottom: `${vPct(values[i])}%` } : { left: pct(t) }}\n onPointerDown={onKeyPointerDown(i)}\n onKeyDown={onKeyDown(i, t)}\n />\n ))}\n {drag ? (\n <div\n aria-hidden=\"true\"\n data-testid=\"timeline-key-ghost\"\n className={row.kind === 'event' ? s.eventGhost : s.keyGhost}\n style={graph && drag.value !== undefined\n ? { left: pct(drag.t), bottom: `${vPct(drag.value)}%` }\n : { left: pct(drag.t) }}\n />\n ) : null}\n {graph && activeBezier && selectedSegment != null ? (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n aria-label={`${row.label} bezier handle 1`}\n data-testid=\"timeline-bezier-handle\"\n className={s.bezierHandle}\n style={bezierHandlePos(selectedSegment, activeBezier[0], activeBezier[1])}\n onPointerDown={onHandlePointerDown(selectedSegment, 0)}\n />\n <div\n role=\"button\"\n tabIndex={0}\n aria-label={`${row.label} bezier handle 2`}\n data-testid=\"timeline-bezier-handle\"\n className={s.bezierHandle}\n style={bezierHandlePos(selectedSegment, activeBezier[2], activeBezier[3])}\n onPointerDown={onHandlePointerDown(selectedSegment, 1)}\n />\n </>\n ) : null}\n </div>\n </div>\n );\n}\n","import { useEffect, useLayoutEffect, useRef, useState, type PointerEvent as ReactPointerEvent, type ReactElement, type WheelEvent as ReactWheelEvent } from 'react';\nimport { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport s from './Timeline.module.css';\nimport { createTimeScale, panWindow, tickTimes, toPercent, zoomWindow, type TimeWindow } from './timeScale';\n\n/** Minimum gap between ruler ticks, in px. */\nconst TICK_SPACING_PX = 64;\n\n/** One wheel notch's zoom factor. */\nconst ZOOM_STEP = 0.0015;\n\nexport interface RulerProps {\n window: TimeWindow;\n /** The full extent the window may cover — normally `{ from: 0, to: duration }`. */\n bounds: TimeWindow;\n playhead: number;\n onScrub: (t: number) => void;\n onWindowChange: (w: TimeWindow) => void;\n}\n\nfunction formatMs(ms: number): string {\n if (ms >= 1000) return `${(ms / 1000).toFixed(ms % 1000 === 0 ? 0 : 2)}s`;\n return `${Math.round(ms)}ms`;\n}\n\nexport function Ruler(props: RulerProps): ReactElement {\n const { window: win, bounds, playhead, onScrub, onWindowChange } = props;\n const trackRef = useRef<HTMLDivElement | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n // Re-render once the track is mounted so ticks lay out against its real\n // width — `trackRef.current` is null on the render that first attaches it.\n const [width, setWidth] = useState(0);\n useLayoutEffect(() => {\n const el = trackRef.current;\n if (!el) return;\n const update = (): void => setWidth(el.getBoundingClientRect().width);\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const widthOf = (): number => trackRef.current?.getBoundingClientRect().width ?? 0;\n const msAt = (clientX: number): number => {\n const rect = trackRef.current?.getBoundingClientRect();\n if (!rect) return win.from;\n const scale = createTimeScale(win, rect.width);\n const raw = scale.toMs(clientX - rect.left);\n return Math.min(win.to, Math.max(win.from, raw));\n };\n\n // `capture: false`: the session tracks the pointer off the ruler on its own,\n // and capture would retarget pointerup and kill the click on non-native\n // children of the scrubbed track.\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0) return;\n onScrub(msAt(e.clientX));\n\n sessionRef.current?.cancel();\n const end = (): void => { sessionRef.current = null; };\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => { onScrub(msAt(ev.clientX)); },\n onEnd: end,\n onCancel: end,\n }, { capture: false });\n };\n\n const onWheel = (e: ReactWheelEvent<HTMLDivElement>): void => {\n if (e.ctrlKey) {\n onWindowChange(zoomWindow(win, msAt(e.clientX), Math.exp(e.deltaY * ZOOM_STEP), bounds));\n } else {\n const width = widthOf();\n const perPx = width === 0 ? 0 : (win.to - win.from) / width;\n onWindowChange(panWindow(win, e.deltaY * perPx, bounds));\n }\n };\n\n const ticks = tickTimes(win, width, TICK_SPACING_PX);\n const pct = (ms: number): string => toPercent(win, ms);\n\n return (\n <div\n className={s.ruler}\n ref={trackRef}\n data-testid=\"timeline-ruler\"\n onPointerDown={onPointerDown}\n onWheel={onWheel}\n >\n {ticks.map((t) => (\n <span\n key={t}\n className={s.tick}\n data-testid=\"timeline-tick\"\n style={{ left: pct(t) }}\n >\n {formatMs(t)}\n </span>\n ))}\n <div\n className={s.playhead}\n data-testid=\"timeline-playhead\"\n style={{ left: pct(playhead) }}\n />\n </div>\n );\n}\n","import type { ReactElement } from 'react';\nimport { DetentSlider } from '../DetentSlider';\nimport { PauseIcon, PlayIcon } from '../../icons';\nimport s from './Timeline.module.css';\n\n/** Playback rates the transport offers. */\nconst RATES = [0.25, 0.5, 1, 2, 4] as const;\n\nexport interface TransportProps {\n paused: boolean;\n loop: boolean | number;\n rate: number;\n playhead: number;\n duration: number;\n onPlay: () => void;\n onPause: () => void;\n onLoopChange: (loop: boolean | number) => void;\n onRateChange: (rate: number) => void;\n}\n\nconst seconds = (ms: number): string => `${(ms / 1000).toFixed(2)}s`;\n\nexport function Transport(props: TransportProps): ReactElement {\n const { paused, loop, rate, playhead, duration, onPlay, onPause, onLoopChange, onRateChange } = props;\n const looping = loop !== false && loop !== 0;\n // The handle's scale is whatever anything holding it set, so an off-list rate\n // gets its own detent — `DetentSlider` would otherwise round it to the\n // nearest and label the thumb with a rate the timeline is not running at.\n const rates = RATES.includes(rate as typeof RATES[number])\n ? (RATES as readonly number[])\n : [...RATES, rate].sort((a, b) => a - b);\n\n return (\n <div className={s.transport}>\n <button\n type=\"button\"\n className={s.transportButton}\n aria-label={paused ? 'Play' : 'Pause'}\n onClick={paused ? onPlay : onPause}\n >\n {paused ? <PlayIcon size={14} /> : <PauseIcon size={14} />}\n </button>\n\n <span className={s.time} data-testid=\"timeline-time\">\n {seconds(playhead)} / {seconds(duration)}\n </span>\n\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={looping}\n aria-label=\"Loop\"\n className={s.transportButton}\n onClick={() => onLoopChange(!looping)}\n >\n ⟲\n </button>\n\n <div className={s.rate}>\n Rate\n <DetentSlider\n ariaLabel=\"Rate\"\n items={rates}\n value={rate}\n onChange={onRateChange}\n formatLabel={(r) => `${r}x`}\n labels=\"none\"\n className={s.rateSlider}\n />\n <span className={s.rateReadout}>{rate}x</span>\n </div>\n </div>\n );\n}\n","import { useMemo, useState, type KeyboardEvent as ReactKeyboardEvent, type ReactElement, type ReactNode } from 'react';\nimport type { EasingSpec, Keyframe, SampledTrack, Track } from '@weasel-js/core';\nimport s from './Timeline.module.css';\nimport { EasingPicker } from './EasingPicker';\nimport { Lane } from './Lane';\nimport { Ruler } from './Ruler';\nimport { Transport, type TransportProps } from './Transport';\nimport { buildLanes, trackAtPath } from './lanes';\nimport { deleteKey, insertKey, moveKey, samePath, setKeyEasing, setKeyValue, type KeySelection } from './keys';\nimport type { TimeWindow } from './timeScale';\n\nexport type { KeySelection } from './keys';\nexport type { TimeWindow } from './timeScale';\n\nexport interface KeyEditorCtx<T = unknown> {\n key: Keyframe<T>;\n track: SampledTrack<T>;\n selection: KeySelection;\n /** Replace the selected key; routed through this component's `onChange`. */\n commit: (next: Keyframe<T>) => void;\n /** Set the easing shaping the approach into this key. */\n setEasing: (easing: EasingSpec | undefined) => void;\n}\n\nexport interface TimelineProps {\n tracks: readonly Track[];\n duration: number;\n playhead: number;\n\n mode?: 'dope' | 'graph';\n onModeChange?: (mode: 'dope' | 'graph') => void;\n\n /** Live during a drag — wire for preview, do not write to history. */\n onInput?: (next: Track[]) => void;\n /** Committed at gesture end: one call per gesture. */\n onChange: (next: Track[]) => void;\n onScrub: (t: number) => void;\n\n /** `false` hides the transport. */\n transport?: Omit<TransportProps, 'playhead' | 'duration'> | false;\n selection?: KeySelection | null;\n onSelect?: (sel: KeySelection | null) => void;\n renderKeyEditor?: (ctx: KeyEditorCtx) => ReactNode;\n\n window?: TimeWindow;\n onWindowChange?: (w: TimeWindow) => void;\n\n label?: ReactNode;\n className?: string;\n}\n\nexport function Timeline(props: TimelineProps): ReactElement {\n const {\n tracks, duration, playhead,\n onInput, onChange, onScrub,\n renderKeyEditor, label, className,\n } = props;\n\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(() => new Set());\n const [ownWindow, setOwnWindow] = useState<TimeWindow>({ from: 0, to: duration });\n const [ownSelection, setOwnSelection] = useState<KeySelection | null>(null);\n const [segmentSelection, setSegmentSelection] = useState<KeySelection | null>(null);\n\n const win = props.window ?? ownWindow;\n const setWindow = props.onWindowChange ?? setOwnWindow;\n const selection = props.selection !== undefined ? props.selection : ownSelection;\n const setSelection = props.onSelect ?? setOwnSelection;\n\n const bounds: TimeWindow = { from: 0, to: duration };\n const rows = useMemo(() => buildLanes(tracks, expanded), [tracks, expanded]);\n\n /** Every key time on every row — what a dragged key snaps to. */\n const snapTimes = useMemo(() => {\n const out = new Set<number>([0, duration]);\n for (const row of rows) {\n if (row.kind === 'sampled') for (const k of (row.track as SampledTrack<unknown>).keys) out.add(k.t + row.offset);\n }\n return [...out];\n }, [rows, duration]);\n\n const toggle = (key: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key); else next.add(key);\n return next;\n });\n };\n\n const onRootKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n if ((e.key === 'Delete' || e.key === 'Backspace') && selection) {\n e.preventDefault();\n const r = deleteKey(tracks, selection);\n onChange(r.tracks);\n setSelection(r.selection);\n }\n };\n\n const selectedTrack = selection ? trackAtPath(tracks, selection.trackPath) : undefined;\n const selectedKey = selectedTrack?.kind === 'sampled'\n ? (selectedTrack as SampledTrack<unknown>).keys[selection!.keyIndex]\n : undefined;\n\n const segmentTrack = segmentSelection ? trackAtPath(tracks, segmentSelection.trackPath) : undefined;\n const segmentKey = segmentTrack?.kind === 'sampled'\n ? (segmentTrack as SampledTrack<unknown>).keys[segmentSelection!.keyIndex]\n : undefined;\n\n return (\n <div\n className={[s.root, className].filter(Boolean).join(' ')}\n data-testid=\"timeline-root\"\n tabIndex={-1}\n onKeyDown={onRootKeyDown}\n >\n {label ? <div className={s.label}>{label}</div> : null}\n\n {props.transport === false ? null : (\n <div className={s.transportRow}>\n <Transport\n {...(props.transport ?? {\n paused: true, loop: false, rate: 1,\n onPlay: () => {}, onPause: () => {},\n onLoopChange: () => {}, onRateChange: () => {},\n })}\n playhead={playhead}\n duration={duration}\n />\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={(props.mode ?? 'dope') === 'graph'}\n aria-label=\"Graph mode\"\n className={s.transportButton}\n onClick={() => props.onModeChange?.((props.mode ?? 'dope') === 'graph' ? 'dope' : 'graph')}\n >\n Graph\n </button>\n </div>\n )}\n\n <div className={s.body}>\n <div className={s.gutter} />\n <div className={s.rulerWrap}>\n <Ruler\n window={win}\n bounds={bounds}\n playhead={playhead}\n onScrub={onScrub}\n onWindowChange={setWindow}\n />\n </div>\n </div>\n\n <div className={s.lanes}>\n {rows.map((row) => {\n const trackPath = row.path;\n return (\n <Lane\n key={row.key}\n row={row}\n window={win}\n mode={props.mode ?? 'dope'}\n selection={selection && samePath(selection.trackPath, trackPath) ? selection.keyIndex : null}\n expanded={expanded.has(row.key)}\n snapTimes={snapTimes}\n onToggleExpand={() => toggle(row.key)}\n onSelect={(keyIndex) => setSelection({ trackPath, keyIndex })}\n onKeyInput={(keyIndex, toMs, value) => {\n if (!onInput) return;\n const r = moveKey(tracks, { trackPath, keyIndex }, toMs);\n onInput(value === undefined ? r.tracks : setKeyValue(r.tracks, r.selection!, value));\n }}\n onKeyCommit={(keyIndex, toMs, value) => {\n const r = moveKey(tracks, { trackPath, keyIndex }, toMs);\n onChange(value === undefined ? r.tracks : setKeyValue(r.tracks, r.selection!, value));\n setSelection(r.selection);\n }}\n onInsert={(atMs) => {\n const r = insertKey(tracks, trackPath, atMs);\n onChange(r.tracks);\n setSelection(r.selection);\n }}\n selectedSegment={segmentSelection && samePath(segmentSelection.trackPath, trackPath) ? segmentSelection.keyIndex : null}\n onSelectSegment={(keyIndex) => setSegmentSelection({ trackPath, keyIndex })}\n onEasingCommit={(keyIndex, easing) => {\n onChange(setKeyEasing(tracks, { trackPath, keyIndex }, easing));\n }}\n />\n );\n })}\n </div>\n\n {renderKeyEditor && selection && selectedKey && selectedTrack?.kind === 'sampled' ? (\n <div className={s.inspector}>\n {renderKeyEditor({\n key: selectedKey,\n track: selectedTrack as SampledTrack<unknown>,\n selection,\n commit: (next) => onChange(setKeyValue(tracks, selection, next.value)),\n setEasing: (easing) => onChange(setKeyEasing(tracks, selection, easing)),\n })}\n </div>\n ) : null}\n\n {segmentSelection && segmentKey ? (\n <div className={s.inspector}>\n <EasingPicker\n value={segmentKey.easing}\n onChange={(next) => onChange(setKeyEasing(tracks, segmentSelection, next))}\n />\n </div>\n ) : null}\n </div>\n );\n}\n","import { useCallback, useEffect, useReducer, useRef, type ReactElement } from 'react';\nimport { useVisibleRaf, type TimelineHandle, type Track } from '@weasel-js/core';\nimport { Timeline, type TimelineProps } from './Timeline';\n\nexport interface AnimatedTimelineProps\n extends Omit<TimelineProps, 'tracks' | 'duration' | 'playhead' | 'onChange' | 'onScrub' | 'transport'> {\n handle: TimelineHandle;\n /** `false` hides the transport, as on `<Timeline>`. */\n transport?: false;\n}\n\n/** Binds `<Timeline>` to a live handle.\n *\n * `handle.tracks()` returns the timeline's own array, not a copy, so an edit\n * splices it in place inside `edit()`. Assigning a replacement array would\n * update nothing and raise nothing. */\nexport function AnimatedTimeline(props: AnimatedTimelineProps): ReactElement {\n const { handle, ...rest } = props;\n const [, bump] = useReducer((n: number) => n + 1, 0);\n\n // `subscribe` fires on `edit` only, so the playhead comes from the frame loop.\n useEffect(() => handle.subscribe(bump), [handle]);\n\n // `useVisibleRaf` returns a controller; a continuous loop re-requests from\n // inside its own frame, and nothing runs until something calls `request`.\n // No `onResume` here: this loop measures no elapsed time of its own, it only\n // reads the playhead the animator already advanced.\n const rafRef = useRef<{ request(): void } | null>(null);\n const raf = useVisibleRaf(() => {\n bump();\n if (!handle.isPaused()) rafRef.current?.request();\n });\n rafRef.current = raf;\n useEffect(() => {\n if (!handle.isPaused()) raf.request();\n return () => raf.cancel();\n }, [handle, raf]);\n\n const onChange = useCallback((next: Track[]) => {\n handle.edit(() => {\n const live = handle.tracks() as Track[];\n live.splice(0, live.length, ...next);\n });\n }, [handle]);\n\n const playhead = handle.time();\n const duration = handle.duration();\n\n return (\n <Timeline\n {...rest}\n tracks={handle.tracks()}\n duration={duration}\n playhead={playhead}\n onChange={onChange}\n onScrub={handle.seek}\n transport={props.transport === false ? false : {\n paused: handle.isPaused(),\n loop: handle.loop(),\n rate: handle.timeScale(),\n onPlay: () => {\n // `rearm` declines to revive a timeline at its duration, so resume\n // alone would do nothing. Play-at-end rewinds first.\n if (handle.time() >= duration) handle.seek(0);\n handle.resume();\n raf.request();\n bump();\n },\n onPause: () => { handle.pause(); bump(); },\n onLoopChange: (next) => { handle.setLoop(next); bump(); },\n onRateChange: (next) => { handle.setTimeScale(next); bump(); },\n }}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ToggleBar-Bxwv6xvj.js","names":[],"sources":["../../src/components/ToggleBar/ToggleBar.module.css","../../src/components/ToggleBar/ToggleBar.tsx"],"sourcesContent":["/* Aqua-glass segmented control. The track is a pill-shaped well; the\n selected segment becomes a tinted glass cylinder that reads as the same\n \"lozenge\" family as the standalone Button. Visual params come from the\n `--btn-*` custom properties (see Button.module.css) so the Button Lab\n can tune both surfaces with one set of sliders. */\n.root {\n display: inline-flex;\n height: var(--tb-h, var(--wzl-control-h, 24px));\n padding: 2px;\n background: linear-gradient(\n 180deg,\n rgba(0, 0, 0, 0.18) 0%,\n rgba(0, 0, 0, 0.05) 100%\n );\n border-radius: var(--wzl-radius-pill);\n /* Clip segments to the track outline so interior corners read crisp and\n the selected segment's glass treatment doesn't paint over the rounded\n outer pixels. */\n overflow: hidden;\n box-shadow:\n inset 0 1px 2px rgba(0, 0, 0, 0.4),\n 0 1px 0 rgba(255, 255, 255, 0.06);\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n}\n\n.segment {\n position: relative;\n flex: 1 1 0;\n min-width: 0;\n padding: 0 12px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n margin: 0;\n font-family: var(--wzl-font-ui);\n font-weight: var(--wzl-font-weight-normal);\n font-size: var(--btn-fontsize, var(--wzl-font-size));\n line-height: 1;\n color: var(--wzl-fg-muted);\n cursor: pointer;\n /* Only round the outer corners of the bar — middle segments stay square\n so the seams between them don't render as facing parentheses. */\n border-radius: 0;\n isolation: isolate;\n overflow: hidden;\n transition: color var(--wzl-motion-fast);\n}\n\n/* A glyph label is a fixed box, not text that can reflow: without this it is\n the only thing in the segment flex can take width from, and three bars in\n one property row squeeze it to nothing. The side padding drops with it —\n 8px around a 14px glyph is most of a segment. */\n.segment > svg {\n flex: none;\n}\n\n.segment:has(> svg) {\n padding: 0 5px;\n}\n\n.segment:first-child {\n border-radius: var(--wzl-radius-pill) 0 0 var(--wzl-radius-pill);\n}\n\n.segment:last-child {\n border-radius: 0 var(--wzl-radius-pill) var(--wzl-radius-pill) 0;\n}\n\n.segment:only-child {\n border-radius: var(--wzl-radius-pill);\n}\n\n.segment:focus-visible {\n outline: 2px solid var(--wzl-accent);\n outline-offset: -2px;\n}\n\n.segment:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.segment:hover:not(:disabled):not(.segmentSelected) {\n color: var(--wzl-fg);\n}\n\n/* Selected segment — full Aqua glass cylinder. Mirrors Button.variant_primary. */\n.segmentSelected {\n background: linear-gradient(\n 180deg,\n color-mix(in srgb, var(--wzl-accent) var(--btn-top-alpha, 45%), transparent) 0%,\n color-mix(in srgb, var(--wzl-accent) var(--btn-upper-alpha, 70%), transparent) calc(var(--btn-equator, 52%) - var(--btn-equator-spread, 12%)),\n color-mix(in srgb, var(--wzl-accent) var(--btn-equator-tint, 95%), white) var(--btn-equator, 52%),\n var(--wzl-accent) calc(var(--btn-equator, 52%) + var(--btn-equator-spread, 12%) * 1.6),\n color-mix(in srgb, var(--wzl-accent) var(--btn-base-tint, 70%), black) 100%\n );\n backdrop-filter: blur(var(--btn-blur, 6px)) saturate(var(--btn-saturate, 1.3));\n -webkit-backdrop-filter: blur(var(--btn-blur, 6px)) saturate(var(--btn-saturate, 1.3));\n color: var(--wzl-fg-on-accent);\n text-shadow:\n 0 1px 0 rgba(0, 0, 0, var(--btn-text-shadow, 0.35)),\n var(--btn-text-inset-x, 0px) var(--btn-text-inset-y, 1px) 0 rgba(0, 0, 0, var(--btn-text-inset, 0.55)),\n 0 0 6px rgba(0, 0, 0, calc(var(--btn-text-shadow, 0.35) * 0.5));\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, var(--btn-inset-hi, 0.42)),\n inset 0 -1px 2px rgba(0, 0, 0, var(--btn-inset-lo, 0.3)),\n 0 0 0 1px rgba(0, 0, 0, var(--btn-rim, 0.22)),\n 0 1px 2px rgba(0, 0, 0, var(--btn-drop-near, 0.3)),\n 0 2px 4px rgba(0, 0, 0, calc(var(--btn-drop-far, 0.18) * 0.8));\n}\n\n/* Specular gloss on the selected segment. Top corners inherit the\n segment's radius so they hug the upper edge cleanly; bottom corners\n are flattened so the gloss ends in a straight line at the equator\n instead of two little curves that read as `)(` brackets. */\n.segmentSelected::before {\n content: '';\n position: absolute;\n inset: 1px 1px var(--btn-gloss-bottom, 50%) 1px;\n border-radius: inherit;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, var(--btn-gloss-top, 0.42)) 0%,\n rgba(255, 255, 255, var(--btn-gloss-mid, 0.12)) 60%,\n rgba(255, 255, 255, 0) 100%\n );\n pointer-events: none;\n z-index: 1;\n}\n\n.segmentSelected > * {\n position: relative;\n z-index: 2;\n}\n\n/* Mixed segment (`aria-pressed=\"mixed\"`) — the sources disagree. Reads as a\n deliberate third state rather than a dimmed \"on\": a flat accent wash with\n none of the selected segment's gloss/cylinder treatment, so it can't be\n mistaken for the glass lozenge at a glance. */\n.segmentMixed {\n background: color-mix(in srgb, var(--wzl-accent) 22%, transparent);\n color: var(--wzl-fg);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--wzl-accent) 45%, transparent);\n}\n\n.segmentMixed:hover:not(:disabled) {\n background: color-mix(in srgb, var(--wzl-accent) 32%, transparent);\n}\n\n.variant_minimal .segmentMixed {\n background: color-mix(in srgb, var(--wzl-accent) 18%, transparent);\n box-shadow: none;\n color: var(--wzl-fg);\n}\n\n/* Small variant — ~60% of the default height, with proportionally reduced\n padding and font. Sized for dense surfaces (lab control panels, inline\n filters, etc.). */\n.size_sm {\n height: 17px;\n padding: 1px;\n}\n\n.size_sm .segment {\n padding: 0 8px;\n font-size: var(--wzl-font-size-sm);\n}\n\n/* `.size_sm .segment` outranks the glyph rule above on specificity, so a\n small bar's glyph segments need the tight padding restated to stay\n near-square rather than reading as wide plates. */\n.size_sm .segment:has(> svg) {\n padding: 0 3px;\n}\n\n/* Minimal variant — strips the pill track, glass selected state, inset\n shadows, and outer padding. Used in dense diagnostic surfaces (dispatch\n trace, lab inspectors) where the bar must read as plain text controls. */\n.variant_minimal {\n height: auto;\n padding: 0;\n background: none;\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n box-shadow: none;\n gap: 0;\n}\n\n.variant_minimal .segment {\n padding: 0 6px;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-medium);\n line-height: 1.4;\n border-radius: 0;\n color: var(--wzl-fg-muted);\n transition: none;\n}\n\n/* Dividers between segments make the mutual-exclusion grouping visible\n without a heavy track. Only the inner edges get a rule — the outer\n border on `.variant_minimal` handles the perimeter. */\n.variant_minimal .segment + .segment {\n border-left: 1px solid var(--wzl-border);\n}\n\n.variant_minimal .segment:first-child,\n.variant_minimal .segment:last-child,\n.variant_minimal .segment:only-child {\n border-radius: 0;\n}\n\n.variant_minimal .segment:hover:not(:disabled):not(.segmentSelected) {\n color: var(--wzl-fg);\n background: var(--wzl-surface-hover);\n}\n\n.variant_minimal .segmentSelected {\n background: none;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n color: var(--wzl-accent-fg, var(--wzl-accent));\n text-shadow: none;\n box-shadow: none;\n font-weight: var(--wzl-font-weight-medium);\n}\n\n.variant_minimal .segmentSelected::before {\n content: none;\n}\n\n/* Flat variant — no track, no glass, no pill: each segment is its own\n outlined cell. For property panels, where a bar sits in a row beside\n inputs and the glass cylinder makes it the loudest thing on the surface. */\n.variant_flat {\n padding: 0;\n background: none;\n border-radius: var(--wzl-radius-sm);\n box-shadow: none;\n overflow: visible;\n}\n\n.variant_flat .segment {\n border: 1px solid var(--wzl-border-strong);\n border-radius: 0;\n /* Sunken, so an unselected cell sits below the panel and the selected one\n reads as raised without needing the glass treatment back. */\n background: var(--wzl-surface-sunken);\n color: var(--wzl-fg-muted);\n}\n\n/* Adjacent cells share one rule rather than stacking two. */\n.variant_flat .segment + .segment {\n margin-inline-start: -1px;\n}\n\n.variant_flat .segment:first-child {\n border-start-start-radius: var(--wzl-radius-sm);\n border-end-start-radius: var(--wzl-radius-sm);\n}\n\n.variant_flat .segment:last-child {\n border-start-end-radius: var(--wzl-radius-sm);\n border-end-end-radius: var(--wzl-radius-sm);\n}\n\n.variant_flat .segment:hover:not(:disabled):not(.segmentSelected) {\n background: var(--wzl-surface-hover);\n color: var(--wzl-fg);\n}\n\n.variant_flat .segmentSelected {\n background: var(--wzl-accent);\n border-color: var(--wzl-accent);\n color: var(--wzl-fg-on-accent);\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n text-shadow: none;\n box-shadow: none;\n /* Its own border rather than the neighbour's overlapping edge. */\n z-index: 1;\n}\n\n.variant_flat .segmentSelected::before {\n content: none;\n}\n","import { type CSSProperties, type ReactElement, type ReactNode } from 'react';\nimport s from './ToggleBar.module.css';\nimport { useRovingTabIndex } from '../../useRovingTabIndex';\n\n/** One segment of a {@link ToggleBar}, identified by its `value`. */\nexport type ToggleBarItem<V extends string | number = string> = {\n value: V;\n label?: ReactNode;\n ariaLabel?: string;\n disabled?: boolean;\n};\n\n/** Segment height and type scale for a {@link ToggleBar}. */\nexport type ToggleBarSize = 'sm' | 'md';\n/** Visual treatment of a {@link ToggleBar}. */\nexport type ToggleBarVariant = 'default' | 'minimal' | 'flat';\n\ntype CommonProps = {\n ariaLabel?: string;\n className?: string;\n height?: number;\n /** Size variant. `sm` is ~60% of the default height with reduced padding\n * and font size — sized for dense surfaces like lab control panels. */\n size?: ToggleBarSize;\n /** Visual variant. `minimal` strips the pill track and glass treatment;\n * selection becomes a flat accent. For dense diagnostic surfaces. */\n variant?: ToggleBarVariant;\n};\n\n/**\n * Props for {@link ToggleBar}, discriminated on `mode`. Single mode is\n * controlled by one value or `null`; multiple mode by an array, and only it\n * accepts `mixedValues`.\n */\nexport type ToggleBarProps<V extends string | number = string> =\n | (CommonProps & {\n mode?: 'single';\n items: readonly ToggleBarItem<V>[];\n value: V | null;\n onChange: (next: V | null) => void;\n allowDeselect?: boolean;\n })\n | (CommonProps & {\n mode: 'multiple';\n items: readonly ToggleBarItem<V>[];\n value: readonly V[];\n /**\n * Values that are neither on nor off — the sources this bar\n * aggregates disagree (a text range that is bold in part of it, a\n * multi-selection whose nodes differ). Rendered `aria-pressed=\"mixed\"`,\n * the ARIA tri-state a toggle button actually has, rather than\n * `SelectionPanel`'s reduced-opacity-plus-`title` workaround for\n * `Switch`, which has no indeterminate state to render.\n *\n * `value` wins where the two lists overlap, so a caller that can't\n * cheaply keep them disjoint doesn't get an ambiguous segment.\n *\n * Clicking a mixed segment turns it fully **on**, matching the\n * everywhere-else convention (and `toggleFlagInRange`'s rule for a\n * partially-styled text range) that a mixed toggle resolves toward\n * the affirmative rather than clearing.\n */\n mixedValues?: readonly V[];\n onChange: (next: V[]) => void;\n });\n\n/**\n * Segmented control for choosing among a fixed set of values — one of them\n * (`mode: 'single'`, the default) or any number (`mode: 'multiple'`).\n *\n * Single mode ignores a click on the already-selected segment unless\n * `allowDeselect` is set. Arrow keys move focus without changing the value;\n * Space and Enter commit.\n */\nexport function ToggleBar<V extends string | number = string>(props: ToggleBarProps<V>): ReactElement {\n const { items, ariaLabel, className, height, size, variant } = props;\n const mode = props.mode ?? 'single';\n\n const isSelected = (value: V): boolean => {\n if (mode === 'multiple') return (props.value as readonly V[]).includes(value);\n return (props.value as V | null) === value;\n };\n\n /** Mixed only in multiple mode, and only where `value` doesn't already\n * claim the segment. Single mode has nothing to be mixed about — its\n * aggregate is one value or none. */\n const isMixed = (value: V): boolean => {\n if (mode !== 'multiple') return false;\n const mixed = (props as { mixedValues?: readonly V[] }).mixedValues;\n return mixed !== undefined && mixed.includes(value) && !isSelected(value);\n };\n\n const handleClick = (index: number) => () => {\n const item = items[index];\n if (item.disabled) return;\n if (mode === 'multiple') {\n const current = props.value as readonly V[];\n const next = current.includes(item.value)\n ? current.filter(v => v !== item.value)\n : [...current, item.value];\n (props.onChange as (n: V[]) => void)(next);\n } else {\n const current = props.value as V | null;\n if (current === item.value) {\n if ((props as { allowDeselect?: boolean }).allowDeselect) {\n (props.onChange as (n: V | null) => void)(null);\n }\n return;\n }\n (props.onChange as (n: V | null) => void)(item.value);\n }\n };\n\n const selectedIndex = mode === 'single'\n ? items.findIndex(it => it.value === (props.value as V | null))\n : -1;\n\n const roving = useRovingTabIndex({\n items,\n itemClassName: s.segment,\n // Single mode is a radiogroup: the tab stop is the current value, and\n // arrow keys move the selection with the focus. Multiple mode is a set of\n // independent toggles — focus alone moves, and Space/Enter flips.\n tabStopIndex: selectedIndex >= 0 && !items[selectedIndex].disabled ? selectedIndex : undefined,\n onNavigate: mode === 'single'\n ? (index) => (props.onChange as (n: V | null) => void)(items[index].value)\n : undefined,\n onActivate: mode === 'multiple' ? (index) => handleClick(index)() : undefined,\n });\n\n const style: CSSProperties | undefined = height !== undefined\n ? ({ ['--tb-h' as string]: `${height}px` } as CSSProperties)\n : undefined;\n\n const rootCls = [\n s.root,\n size && s[`size_${size}`],\n variant && s[`variant_${variant}`],\n className,\n ].filter(Boolean).join(' ');\n\n return (\n <div\n ref={roving.rootRef}\n className={rootCls}\n role={mode === 'multiple' ? 'group' : 'radiogroup'}\n aria-label={ariaLabel}\n style={style}\n >\n {items.map((item, i) => {\n const selected = isSelected(item.value);\n const mixed = isMixed(item.value);\n const cls = [s.segment, selected && s.segmentSelected, mixed && s.segmentMixed]\n .filter(Boolean)\n .join(' ');\n return (\n <button\n key={item.value}\n type=\"button\"\n role={mode === 'multiple' ? undefined : 'radio'}\n aria-checked={mode === 'multiple' ? undefined : selected}\n aria-pressed={mode === 'multiple' ? (mixed ? 'mixed' : selected) : undefined}\n aria-label={item.ariaLabel}\n disabled={item.disabled}\n tabIndex={roving.tabIndexFor(i)}\n className={cls}\n onClick={handleClick(i)}\n onKeyDown={roving.onKeyDown(i)}\n >\n {item.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AC0EA,SAAgB,EAA8C,GAAwC;CACpG,IAAM,EAAE,UAAO,cAAW,cAAW,WAAQ,SAAM,eAAY,GACzD,IAAO,EAAM,QAAQ,UAErB,KAAc,MACd,MAAS,aAAoB,EAAM,MAAuB,SAAS,EAAM,GACrE,EAAM,UAAuB,GAMjC,KAAW,MAAsB;AACrC,MAAI,MAAS,WAAY,QAAO;EAChC,IAAM,IAAS,EAAyC;AACxD,SAAO,MAAU,KAAA,KAAa,EAAM,SAAS,EAAM,IAAI,CAAC,EAAW,EAAM;IAGrE,KAAe,YAAwB;EAC3C,IAAM,IAAO,EAAM;AACf,SAAK,SACT,KAAI,MAAS,YAAY;GACvB,IAAM,IAAU,EAAM,OAChB,IAAO,EAAQ,SAAS,EAAK,MAAM,GACrC,EAAQ,QAAO,MAAK,MAAM,EAAK,MAAM,GACrC,CAAC,GAAG,GAAS,EAAK,MAAM;AAC3B,KAAM,SAA8B,EAAK;SACrC;AAEL,OADgB,EAAM,UACN,EAAK,OAAO;AAC1B,IAAK,EAAsC,iBACxC,EAAM,SAAmC,KAAK;AAEjD;;AAED,KAAM,SAAmC,EAAK,MAAM;;IAInD,IAAgB,MAAS,WAC3B,EAAM,WAAU,MAAM,EAAG,UAAW,EAAM,MAAmB,GAC7D,IAEE,IAAS,EAAkB;EAC/B;EACA,eAAe,EAAE;EAIjB,cAAc,KAAiB,KAAK,CAAC,EAAM,GAAe,WAAW,IAAgB,KAAA;EACrF,YAAY,MAAS,YAChB,MAAW,EAAM,SAAmC,EAAM,GAAO,MAAM,GACxE,KAAA;EACJ,YAAY,MAAS,cAAc,MAAU,EAAY,EAAM,EAAE,GAAG,KAAA;EACrE,CAAC,EAEI,IAAmC,MAAW,KAAA,IAEhD,KAAA,IADC,EAAG,UAAqB,GAAG,EAAO,KAAK,EAGtC,IAAU;EACd,EAAE;EACF,KAAQ,EAAE,QAAQ;EAClB,KAAW,EAAE,WAAW;EACxB;EACD,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;AAE3B,QACE,kBAAC,OAAD;EACE,KAAK,EAAO;EACZ,WAAW;EACX,MAAM,MAAS,aAAa,UAAU;EACtC,cAAY;EACL;YAEN,EAAM,KAAK,GAAM,MAAM;GACtB,IAAM,IAAW,EAAW,EAAK,MAAM,EACjC,IAAQ,EAAQ,EAAK,MAAM,EAC3B,IAAM;IAAC,EAAE;IAAS,KAAY,EAAE;IAAiB,KAAS,EAAE;IAAa,CAC5E,OAAO,QAAQ,CACf,KAAK,IAAI;AACZ,UACE,kBAAC,UAAD;IAEE,MAAK;IACL,MAAM,MAAS,aAAa,KAAA,IAAY;IACxC,gBAAc,MAAS,aAAa,KAAA,IAAY;IAChD,gBAAc,MAAS,aAAc,IAAQ,UAAU,IAAY,KAAA;IACnE,cAAY,EAAK;IACjB,UAAU,EAAK;IACf,UAAU,EAAO,YAAY,EAAE;IAC/B,WAAW;IACX,SAAS,EAAY,EAAE;IACvB,WAAW,EAAO,UAAU,EAAE;cAE7B,EAAK;IACC,EAbF,EAAK,MAaH;IAEX;EACE,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"icons-BV27dhKB.js","names":[],"sources":["../../src/icons/index.tsx"],"sourcesContent":["import { Icon, type IconProps } from './Icon';\n\nexport { Icon } from './Icon';\nexport type { IconProps } from './Icon';\nexport { ICON_PATHS } from './paths';\nexport type { IconName } from './paths';\n\n// Tool glyphs live in @weasel-js/core because core needs them for\n// `Tool.presentation.icon` defaults and cannot depend on this package.\n// Re-exported here so consumers have one import site for the whole set.\nexport {\n SelectIcon, LassoIcon, RectIcon, EllipseIcon, ImageIcon, EyedropperIcon,\n LineIcon, ArrowIcon, PolygonIcon, StarIcon, PencilIcon, TextIcon, PenIcon, HandIcon,\n UnknownIcon,\n} from '@weasel-js/core';\n\nexport const CloneIcon = (p: IconProps) => <Icon name=\"clone\" {...p} />;\nexport const ResetIcon = (p: IconProps) => <Icon name=\"reset\" {...p} />;\nexport const CloseIcon = (p: IconProps) => <Icon name=\"close\" {...p} />;\nexport const ExportIcon = (p: IconProps) => <Icon name=\"export\" {...p} />;\nexport const ZoomInIcon = (p: IconProps) => <Icon name=\"zoomIn\" {...p} />;\nexport const PanIcon = (p: IconProps) => <Icon name=\"pan\" {...p} />;\nexport const AddIcon = (p: IconProps) => <Icon name=\"add\" {...p} />;\nexport const ModeLightIcon = (p: IconProps) => <Icon name=\"modeLight\" {...p} />;\nexport const ModeDarkIcon = (p: IconProps) => <Icon name=\"modeDark\" {...p} />;\nexport const ModeAutoIcon = (p: IconProps) => <Icon name=\"modeAuto\" {...p} />;\nexport const RemoveIcon = (p: IconProps) => <Icon name=\"remove\" {...p} />;\nexport const DeleteIcon = (p: IconProps) => <Icon name=\"delete\" {...p} />;\nexport const SortIcon = (p: IconProps) => <Icon name=\"sort\" {...p} />;\nexport const UndoIcon = (p: IconProps) => <Icon name=\"undo\" {...p} />;\nexport const RedoIcon = (p: IconProps) => <Icon name=\"redo\" {...p} />;\nexport const ZoomOutIcon = (p: IconProps) => <Icon name=\"zoomOut\" {...p} />;\nexport const FitIcon = (p: IconProps) => <Icon name=\"fit\" {...p} />;\nexport const SnapshotIcon = (p: IconProps) => <Icon name=\"snapshot\" {...p} />;\nexport const PlayIcon = (p: IconProps) => <Icon name=\"play\" {...p} />;\nexport const PauseIcon = (p: IconProps) => <Icon name=\"pause\" {...p} />;\nexport const StopIcon = (p: IconProps) => <Icon name=\"stop\" {...p} />;\nexport const StepIcon = (p: IconProps) => <Icon name=\"step\" {...p} />;\nexport const CrosshairIcon = (p: IconProps) => <Icon name=\"crosshair\" {...p} />;\nexport const FullscreenIcon = (p: IconProps) => <Icon name=\"fullscreen\" {...p} />;\nexport const CompareIcon = (p: IconProps) => <Icon name=\"compare\" {...p} />;\nexport const FilterIcon = (p: IconProps) => <Icon name=\"filter\" {...p} />;\nexport const SearchIcon = (p: IconProps) => <Icon name=\"search\" {...p} />;\nexport const LoupeIcon = (p: IconProps) => <Icon name=\"loupe\" {...p} />;\nexport const LayersIcon = (p: IconProps) => <Icon name=\"layers\" {...p} />;\nexport const LockIcon = (p: IconProps) => <Icon name=\"lock\" {...p} />;\nexport const UnlockIcon = (p: IconProps) => <Icon name=\"unlock\" {...p} />;\nexport const VisibleIcon = (p: IconProps) => <Icon name=\"visible\" {...p} />;\nexport const HiddenIcon = (p: IconProps) => <Icon name=\"hidden\" {...p} />;\nexport const PinIcon = (p: IconProps) => <Icon name=\"pin\" {...p} />;\nexport const LinkIcon = (p: IconProps) => <Icon name=\"link\" {...p} />;\nexport const CollapseIcon = (p: IconProps) => <Icon name=\"collapse\" {...p} />;\nexport const ExpandIcon = (p: IconProps) => <Icon name=\"expand\" {...p} />;\nexport const ChevronIcon = (p: IconProps) => <Icon name=\"chevron\" {...p} />;\nexport const TuneIcon = (p: IconProps) => <Icon name=\"tune\" {...p} />;\nexport const GridIcon = (p: IconProps) => <Icon name=\"grid\" {...p} />;\nexport const SnapIcon = (p: IconProps) => <Icon name=\"snap\" {...p} />;\nexport const MeasureIcon = (p: IconProps) => <Icon name=\"measure\" {...p} />;\nexport const RandomizeIcon = (p: IconProps) => <Icon name=\"randomize\" {...p} />;\nexport const RefreshIcon = (p: IconProps) => <Icon name=\"refresh\" {...p} />;\nexport const InfoIcon = (p: IconProps) => <Icon name=\"info\" {...p} />;\nexport const WarningIcon = (p: IconProps) => <Icon name=\"warning\" {...p} />;\nexport const ErrorIcon = (p: IconProps) => <Icon name=\"error\" {...p} />;\nexport const BusyIcon = (p: IconProps) => <Icon name=\"busy\" {...p} />;\nexport const LayoutRowsIcon = (p: IconProps) => <Icon name=\"layoutRows\" {...p} />;\nexport const LayoutColumnsIcon = (p: IconProps) => <Icon name=\"layoutColumns\" {...p} />;\nexport const LayoutGridIcon = (p: IconProps) => <Icon name=\"layoutGrid\" {...p} />;\n"],"mappings":"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"}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
function r(e) {
|
|
10
|
-
return String(e).replace(/^-/, "−");
|
|
11
|
-
}
|
|
12
|
-
function i(e) {
|
|
13
|
-
return Number.isFinite(e) ? e <= 2 ? `${r(Math.round(e * 100))}%` : e >= 100 ? `${Math.round(e).toLocaleString("en-US")}x` : `${r(Math.round(e * 10) / 10)}x` : String(e);
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
16
|
-
export { n as i, t as n, i as r, e as t };
|
|
17
|
-
|
|
18
|
-
//# sourceMappingURL=number-Bv1nbhOJ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"number-Bv1nbhOJ.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/** {@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;;AAK9C,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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"range.module-CvSfABmu.js","names":[],"sources":["../../src/components/range.module.css"],"sourcesContent":["/* The one skin for a native <input type=\"range\">. Imported by InlineRange and\n by the property rows, which render the same DOM and used to carry three\n independently authored copies of these rules.\n\n The track is painted here rather than left to `accent-color`: a UA fills the\n left of the track with the accent but draws the remainder near-white on a\n dark surface whatever `color-scheme` says, and overriding the track's\n background removes the accent fill instead of recolouring the remainder. */\n.range {\n appearance: none;\n -webkit-appearance: none;\n width: 100%;\n min-inline-size: 48px;\n /* The box is taller than the track so the thumb has room to spill; a surface\n where the element itself IS the track overrides this. */\n height: var(--wzl-range-box-h, 12px);\n margin: 0;\n padding: 0;\n /* Transparent so the track pseudo-element shows; a surface that paints the element itself overrides it. */\n background: var(--wzl-range-bg, transparent);\n cursor: pointer;\n}\n\n.range::-webkit-slider-runnable-track {\n height: var(--wzl-slider-track-h, 4px);\n border-radius: var(--wzl-radius-sm);\n background: var(\n --wzl-range-track-bg,\n color-mix(in srgb, var(--wzl-accent) var(--wzl-slider-track-mix), transparent)\n );\n}\n\n.range::-moz-range-track {\n height: var(--wzl-slider-track-h, 4px);\n border-radius: var(--wzl-radius-sm);\n background: var(\n --wzl-range-track-bg,\n color-mix(in srgb, var(--wzl-accent) var(--wzl-slider-track-mix), transparent)\n );\n}\n\n.range::-webkit-slider-thumb {\n appearance: none;\n -webkit-appearance: none;\n width: var(--wzl-slider-thumb-size, 8px);\n height: var(--wzl-slider-thumb-size, 8px);\n border: none;\n border-radius: 50%;\n background: color-mix(in srgb, var(--wzl-accent) var(--wzl-slider-thumb-mix), transparent);\n /* Centres an 8px thumb on a 4px track. Webkit does not centre it for you. */\n margin-top: calc((var(--wzl-slider-track-h, 4px) - var(--wzl-slider-thumb-size, 8px)) / 2);\n}\n\n.range::-moz-range-thumb {\n width: var(--wzl-slider-thumb-size, 8px);\n height: var(--wzl-slider-thumb-size, 8px);\n border: none;\n border-radius: 50%;\n background: color-mix(in srgb, var(--wzl-accent) var(--wzl-slider-thumb-mix), transparent);\n}\n\n.range:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 2px;\n}\n\n.range:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n/* Alpha variant: a fainter track, used where the row already carries a colour\n swatch and the slider must not compete with it. */\n.alpha::-webkit-slider-runnable-track {\n background: color-mix(in srgb, var(--wzl-accent) 10%, transparent);\n}\n\n.alpha::-moz-range-track {\n background: color-mix(in srgb, var(--wzl-accent) 10%, transparent);\n}\n\n/* A dead alpha track keeps its groove but loses its thumb, so the affordance\n reads inert rather than merely dimmed. Keyed off `:disabled`, which ColorRow\n already sets on the input — not off a class on the row. */\n.alpha:disabled::-webkit-slider-runnable-track,\n.alpha:disabled::-moz-range-track {\n background: color-mix(in srgb, var(--wzl-fg) 5%, transparent);\n}\n\n.alpha:disabled::-webkit-slider-thumb,\n.alpha:disabled::-moz-range-thumb {\n background: transparent;\n}\n"],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schema-CtWwGbqJ.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 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 } 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/** 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":";AAuCA,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;;AAIjE,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,11 +0,0 @@
|
|
|
1
|
-
var e = {
|
|
2
|
-
root: "_root_gmx9t_10",
|
|
3
|
-
segment: "_segment_gmx9t_32",
|
|
4
|
-
segmentSelected: "_segmentSelected_gmx9t_79",
|
|
5
|
-
size_sm: "_size_sm_gmx9t_137",
|
|
6
|
-
variant_minimal: "_variant_minimal_gmx9t_150"
|
|
7
|
-
};
|
|
8
|
-
//#endregion
|
|
9
|
-
export { e as t };
|
|
10
|
-
|
|
11
|
-
//# sourceMappingURL=segmentedControl.module-BgvAPkSZ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"segmentedControl.module-BgvAPkSZ.js","names":[],"sources":["../../src/components/segmentedControl.module.css"],"sourcesContent":["/* Shared by ActionsBar and OptionsBar, which look identical and differ only\n in what a segment does. Lives here rather than in either component so\n neither owns the other's styling.\n\n Aqua-glass segmented control. The track is a pill-shaped well; the\n selected segment becomes a tinted glass cylinder that reads as the same\n \"lozenge\" family as the standalone Button. Visual params come from the\n `--btn-*` custom properties (see Button.module.css) so the Button Lab\n can tune both surfaces with one set of sliders. */\n.root {\n display: inline-flex;\n height: var(--tb-h, var(--wzl-control-h, 24px));\n padding: 2px;\n background: linear-gradient(\n 180deg,\n rgba(0, 0, 0, 0.18) 0%,\n rgba(0, 0, 0, 0.05) 100%\n );\n border-radius: var(--wzl-radius-pill);\n /* Clip segments to the track outline so interior corners read crisp and\n the selected segment's glass treatment doesn't paint over the rounded\n outer pixels. */\n overflow: hidden;\n box-shadow:\n inset 0 1px 2px rgba(0, 0, 0, 0.4),\n 0 1px 0 rgba(255, 255, 255, 0.06);\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n}\n\n.segment {\n position: relative;\n flex: 1 1 0;\n min-width: 0;\n padding: 0 12px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n margin: 0;\n font-family: var(--wzl-font-ui);\n font-weight: var(--wzl-font-weight-normal);\n font-size: var(--btn-fontsize, var(--wzl-font-size));\n line-height: 1;\n color: var(--wzl-fg-muted);\n cursor: pointer;\n /* Only round the outer corners of the bar — middle segments stay square\n so the seams between them don't render as facing parentheses. */\n border-radius: 0;\n isolation: isolate;\n overflow: hidden;\n transition: color var(--wzl-motion-fast);\n}\n\n.segment:first-child {\n border-radius: var(--wzl-radius-pill) 0 0 var(--wzl-radius-pill);\n}\n\n.segment:last-child {\n border-radius: 0 var(--wzl-radius-pill) var(--wzl-radius-pill) 0;\n}\n\n.segment:only-child {\n border-radius: var(--wzl-radius-pill);\n}\n\n.segment:focus-visible {\n outline: 2px solid var(--wzl-accent);\n outline-offset: -2px;\n}\n\n.segment:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.segment:hover:not(:disabled):not(.segmentSelected) {\n color: var(--wzl-fg);\n}\n\n/* Selected segment — full Aqua glass cylinder. Mirrors Button.variant_primary. */\n.segmentSelected {\n background: linear-gradient(\n 180deg,\n color-mix(in srgb, var(--wzl-accent) var(--btn-top-alpha, 45%), transparent) 0%,\n color-mix(in srgb, var(--wzl-accent) var(--btn-upper-alpha, 70%), transparent) calc(var(--btn-equator, 52%) - var(--btn-equator-spread, 12%)),\n color-mix(in srgb, var(--wzl-accent) var(--btn-equator-tint, 95%), white) var(--btn-equator, 52%),\n var(--wzl-accent) calc(var(--btn-equator, 52%) + var(--btn-equator-spread, 12%) * 1.6),\n color-mix(in srgb, var(--wzl-accent) var(--btn-base-tint, 70%), black) 100%\n );\n backdrop-filter: blur(var(--btn-blur, 6px)) saturate(var(--btn-saturate, 1.3));\n -webkit-backdrop-filter: blur(var(--btn-blur, 6px)) saturate(var(--btn-saturate, 1.3));\n color: var(--wzl-fg-on-accent);\n text-shadow:\n 0 1px 0 rgba(0, 0, 0, var(--btn-text-shadow, 0.35)),\n var(--btn-text-inset-x, 0px) var(--btn-text-inset-y, 1px) 0 rgba(0, 0, 0, var(--btn-text-inset, 0.55)),\n 0 0 6px rgba(0, 0, 0, calc(var(--btn-text-shadow, 0.35) * 0.5));\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, var(--btn-inset-hi, 0.42)),\n inset 0 -1px 2px rgba(0, 0, 0, var(--btn-inset-lo, 0.3)),\n 0 0 0 1px rgba(0, 0, 0, var(--btn-rim, 0.22)),\n 0 1px 2px rgba(0, 0, 0, var(--btn-drop-near, 0.3)),\n 0 2px 4px rgba(0, 0, 0, calc(var(--btn-drop-far, 0.18) * 0.8));\n}\n\n/* Specular gloss on the selected segment. Top corners inherit the\n segment's radius so they hug the upper edge cleanly; bottom corners\n are flattened so the gloss ends in a straight line at the equator\n instead of two little curves that read as `)(` brackets. */\n.segmentSelected::before {\n content: '';\n position: absolute;\n inset: 1px 1px var(--btn-gloss-bottom, 50%) 1px;\n border-radius: inherit;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, var(--btn-gloss-top, 0.42)) 0%,\n rgba(255, 255, 255, var(--btn-gloss-mid, 0.12)) 60%,\n rgba(255, 255, 255, 0) 100%\n );\n pointer-events: none;\n z-index: 1;\n}\n\n.segmentSelected > * {\n position: relative;\n z-index: 2;\n}\n\n/* Small variant — ~60% of the default height, with proportionally reduced\n padding and font. Sized for dense surfaces (lab control panels, inline\n filters, etc.). */\n.size_sm {\n height: 17px;\n padding: 1px;\n}\n\n.size_sm .segment {\n padding: 0 8px;\n font-size: var(--wzl-font-size-sm);\n}\n\n/* Minimal variant — strips the pill track, glass selected state, inset\n shadows, and outer padding. Used in dense diagnostic surfaces (dispatch\n trace, lab inspectors) where the bar must read as plain text controls. */\n.variant_minimal {\n height: auto;\n padding: 0;\n background: none;\n border-radius: 0;\n box-shadow: none;\n gap: 2px;\n}\n\n.variant_minimal .segment {\n padding: 0 4px;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-medium);\n line-height: 1.4;\n border-radius: var(--wzl-radius-sm);\n color: var(--wzl-fg-muted);\n transition: none;\n}\n\n.variant_minimal .segment:first-child,\n.variant_minimal .segment:last-child,\n.variant_minimal .segment:only-child {\n border-radius: var(--wzl-radius-sm);\n}\n\n.variant_minimal .segment:hover:not(:disabled):not(.segmentSelected) {\n color: var(--wzl-fg);\n background: var(--wzl-surface-hover);\n}\n\n.variant_minimal .segmentSelected {\n background: none;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n color: var(--wzl-accent-fg, var(--wzl-accent));\n text-shadow: none;\n box-shadow: none;\n font-weight: var(--wzl-font-weight-medium);\n}\n\n.variant_minimal .segmentSelected::before {\n content: none;\n}\n"],"mappings":""}
|