@weasel-js/ui 1.7.3 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{Callout-BUn0dP_S.js → Callout-DpTO3fkb.js} +10 -10
- package/dist/chunks/Callout-DpTO3fkb.js.map +1 -0
- package/dist/chunks/{Checkbox-DFn-x0JO.js → Checkbox-d4IfhiXh.js} +2 -2
- package/dist/chunks/{Checkbox-DFn-x0JO.js.map → Checkbox-d4IfhiXh.js.map} +1 -1
- package/dist/chunks/{CloseButton-BmNmRVtb.js → CloseButton-wb7Or5Eg.js} +2 -2
- package/dist/chunks/{CloseButton-BmNmRVtb.js.map → CloseButton-wb7Or5Eg.js.map} +1 -1
- package/dist/chunks/{ColorModeControl-DaVJ-SyX.js → ColorModeControl-DET6Itwq.js} +3 -3
- package/dist/chunks/{ColorModeControl-DaVJ-SyX.js.map → ColorModeControl-DET6Itwq.js.map} +1 -1
- package/dist/chunks/{DataGrid-CmHJe57v.js → DataGrid-B6IS9vUk.js} +2 -2
- package/dist/chunks/{DataGrid-CmHJe57v.js.map → DataGrid-B6IS9vUk.js.map} +1 -1
- package/dist/chunks/{DetentSlider-DNTAk8xP.js → DetentSlider-D40V8Oma.js} +2 -2
- package/dist/chunks/{DetentSlider-DNTAk8xP.js.map → DetentSlider-D40V8Oma.js.map} +1 -1
- package/dist/chunks/{Dialog-DKfsk5VN.js → Dialog-h-5MeYpu.js} +9 -9
- package/dist/chunks/{Dialog-DKfsk5VN.js.map → Dialog-h-5MeYpu.js.map} +1 -1
- package/dist/chunks/{Disclosure-Ch2KxYuB.js → Disclosure-CpWt87nO.js} +7 -7
- package/dist/chunks/{Disclosure-Ch2KxYuB.js.map → Disclosure-CpWt87nO.js.map} +1 -1
- package/dist/chunks/FitLabel-BzZGkWfL.js +76 -0
- package/dist/chunks/FitLabel-BzZGkWfL.js.map +1 -0
- package/dist/chunks/{GradientEditor-DD2OjQtn.js → GradientEditor-vz771M-N.js} +4 -4
- package/dist/chunks/{GradientEditor-DD2OjQtn.js.map → GradientEditor-vz771M-N.js.map} +1 -1
- package/dist/chunks/{Icon--B1ME-6c.js → Icon-DPRUIo3G.js} +4 -1
- package/dist/chunks/Icon-DPRUIo3G.js.map +1 -0
- package/dist/chunks/{Jog-BrkzdGV2.js → Jog-TE6QI9i1.js} +3 -3
- package/dist/chunks/{Jog-BrkzdGV2.js.map → Jog-TE6QI9i1.js.map} +1 -1
- package/dist/chunks/{LayerList-BqeWFuGc.js → LayerList-BXTAjK-4.js} +6 -6
- package/dist/chunks/{LayerList-BqeWFuGc.js.map → LayerList-BXTAjK-4.js.map} +1 -1
- package/dist/chunks/{ListEditor-OQDzG4A5.js → ListEditor-DGtwj99m.js} +2 -2
- package/dist/chunks/{ListEditor-OQDzG4A5.js.map → ListEditor-DGtwj99m.js.map} +1 -1
- package/dist/chunks/{MeshEditor-DWpvg8H6.js → MeshEditor-8CRsu-Td.js} +2 -2
- package/dist/chunks/{MeshEditor-DWpvg8H6.js.map → MeshEditor-8CRsu-Td.js.map} +1 -1
- package/dist/chunks/{NumberField-DS0WSiin.js → NumberField-BXFrOi5M.js} +8 -5
- package/dist/chunks/{NumberField-DS0WSiin.js.map → NumberField-BXFrOi5M.js.map} +1 -1
- package/dist/chunks/{PaintField-BdmQM53v.js → PaintField-DX4uYlF9.js} +2 -2
- package/dist/chunks/{PaintField-BdmQM53v.js.map → PaintField-DX4uYlF9.js.map} +1 -1
- package/dist/chunks/{PaintInput-DaQMeBsI.js → PaintInput-CWyOJXUM.js} +5 -5
- package/dist/chunks/{PaintInput-DaQMeBsI.js.map → PaintInput-CWyOJXUM.js.map} +1 -1
- package/dist/chunks/{PatternPicker-B3HcMZWg.js → PatternPicker-0j51aDJ3.js} +9 -9
- package/dist/chunks/PatternPicker-0j51aDJ3.js.map +1 -0
- package/dist/chunks/{Prefs-BnVEyfWO.js → Prefs-gbjYqvSD.js} +41 -41
- package/dist/chunks/Prefs-gbjYqvSD.js.map +1 -0
- package/dist/chunks/{Properties-BoG2MMHR.js → Properties-BbNuMiic.js} +4 -4
- package/dist/chunks/{Properties-BoG2MMHR.js.map → Properties-BbNuMiic.js.map} +1 -1
- package/dist/chunks/PropertyField-CWUtNK1w.js +751 -0
- package/dist/chunks/PropertyField-CWUtNK1w.js.map +1 -0
- package/dist/chunks/{SelectionPanel-COZ6z9_P.js → SelectionPanel-BpUqP75b.js} +2 -2
- package/dist/chunks/{SelectionPanel-COZ6z9_P.js.map → SelectionPanel-BpUqP75b.js.map} +1 -1
- package/dist/chunks/{SidebarPanel-AXQ0jRDx.js → SidebarPanel--dJ-k9-V.js} +3 -3
- package/dist/chunks/{SidebarPanel-AXQ0jRDx.js.map → SidebarPanel--dJ-k9-V.js.map} +1 -1
- package/dist/chunks/Slider-CHBQ8uUC.js +439 -0
- package/dist/chunks/Slider-CHBQ8uUC.js.map +1 -0
- package/dist/chunks/{SwatchGrid-9ZPiDmxd.js → SwatchGrid-DxK9e_IV.js} +3 -3
- package/dist/chunks/SwatchGrid-DxK9e_IV.js.map +1 -0
- package/dist/chunks/{SwatchStrip-Bi9iPLoh.js → SwatchStrip-BY6A511W.js} +2 -2
- package/dist/chunks/{SwatchStrip-Bi9iPLoh.js.map → SwatchStrip-BY6A511W.js.map} +1 -1
- package/dist/chunks/{ThemeSwitcher-DiBmMh_B.js → ThemeSwitcher-tnATkIo1.js} +2 -2
- package/dist/chunks/{ThemeSwitcher-DiBmMh_B.js.map → ThemeSwitcher-tnATkIo1.js.map} +1 -1
- package/dist/chunks/{Timeline-sMCH2fah.js → Timeline-FlLUm8nd.js} +4 -4
- package/dist/chunks/{Timeline-sMCH2fah.js.map → Timeline-FlLUm8nd.js.map} +1 -1
- package/dist/chunks/{Toast-C3ozq11V.js → Toast-CuHkT0x0.js} +2 -2
- package/dist/chunks/{Toast-C3ozq11V.js.map → Toast-CuHkT0x0.js.map} +1 -1
- package/dist/chunks/ToggleBar-BZ4Gk1IU.js +84 -0
- package/dist/chunks/ToggleBar-BZ4Gk1IU.js.map +1 -0
- package/dist/chunks/{TokenPanel-WtKGY2Db.js → TokenPanel-DongdiZM.js} +23 -23
- package/dist/chunks/{TokenPanel-WtKGY2Db.js.map → TokenPanel-DongdiZM.js.map} +1 -1
- package/dist/chunks/ToolOptionsBar-wTPJFfOn.js +94 -0
- package/dist/chunks/ToolOptionsBar-wTPJFfOn.js.map +1 -0
- package/dist/chunks/{Tree-Cc8lIzgM.js → Tree-hzIbj2q1.js} +2 -2
- package/dist/chunks/{Tree-Cc8lIzgM.js.map → Tree-hzIbj2q1.js.map} +1 -1
- package/dist/chunks/checkbox.module-Dcx8ILgR.js +5 -0
- package/dist/chunks/checkbox.module-Dcx8ILgR.js.map +1 -0
- package/dist/chunks/{icons-Dr2-r30x.js → icons-CofIOTiI.js} +2 -2
- package/dist/chunks/{icons-Dr2-r30x.js.map → icons-CofIOTiI.js.map} +1 -1
- package/dist/chunks/{model-B6zuQg6b.js → model-qQj8Ny9w.js} +134 -133
- package/dist/chunks/model-qQj8Ny9w.js.map +1 -0
- package/dist/chunks/{prefField-BE_Ytr2H.js → prefField-CCm_nzlM.js} +81 -75
- package/dist/chunks/prefField-CCm_nzlM.js.map +1 -0
- package/dist/components/Callout/index.js +1 -1
- package/dist/components/Checkbox/index.js +1 -1
- package/dist/components/CloseButton/index.js +1 -1
- package/dist/components/ColorModeControl/index.js +1 -1
- package/dist/components/DataGrid/index.js +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/FitLabel/FitLabel.d.ts +41 -0
- package/dist/components/FitLabel/FitLabel.d.ts.map +1 -0
- package/dist/components/FitLabel/index.d.ts +3 -0
- package/dist/components/FitLabel/index.d.ts.map +1 -0
- package/dist/components/FitLabel/index.js +2 -0
- package/dist/components/GradientEditor/index.js +1 -1
- package/dist/components/InlineRange/InlineRange.d.ts +1 -1
- package/dist/components/Jog/index.js +1 -1
- package/dist/components/LayerList/index.js +1 -1
- package/dist/components/ListEditor/index.js +1 -1
- package/dist/components/MeshEditor/index.js +1 -1
- package/dist/components/NumberField/UnitField.d.ts.map +1 -1
- package/dist/components/NumberField/index.js +1 -1
- package/dist/components/PaintField/index.js +1 -1
- package/dist/components/PaintInput/index.js +1 -1
- package/dist/components/PatternPicker/index.js +1 -1
- package/dist/components/Prefs/index.js +2 -2
- package/dist/components/Prefs/prefField.d.ts.map +1 -1
- package/dist/components/Properties/NumberControls.d.ts +16 -0
- package/dist/components/Properties/NumberControls.d.ts.map +1 -0
- package/dist/components/Properties/PropertyField.d.ts +18 -8
- package/dist/components/Properties/PropertyField.d.ts.map +1 -1
- package/dist/components/Properties/index.js +2 -2
- package/dist/components/SelectionPanel/index.js +2 -2
- package/dist/components/SelectionPanel/renderLeaf.d.ts.map +1 -1
- package/dist/components/SidebarPanel/index.js +1 -1
- package/dist/components/Slider/Slider.d.ts +6 -0
- package/dist/components/Slider/Slider.d.ts.map +1 -1
- package/dist/components/Slider/index.js +1 -1
- package/dist/components/SwatchGrid/index.js +1 -1
- package/dist/components/SwatchStrip/index.js +1 -1
- package/dist/components/ThemeSwitcher/index.js +1 -1
- package/dist/components/Timeline/index.js +1 -1
- package/dist/components/Toast/index.js +1 -1
- package/dist/components/ToggleBar/ToggleBar.d.ts.map +1 -1
- package/dist/components/ToggleBar/index.js +1 -1
- package/dist/components/TokenPanel/index.js +1 -1
- package/dist/components/ToolOptionsBar/ToolOptionsBar.d.ts +1 -1
- package/dist/components/ToolOptionsBar/ToolOptionsBar.d.ts.map +1 -1
- package/dist/components/ToolOptionsBar/index.js +1 -1
- package/dist/components/Tree/index.js +1 -1
- package/dist/components/stanceCss.d.ts +6 -0
- package/dist/components/stanceCss.d.ts.map +1 -1
- package/dist/components/stanceSurfaces.d.ts.map +1 -1
- package/dist/endless.d.ts +7 -0
- package/dist/endless.d.ts.map +1 -0
- package/dist/icons/paths.d.ts +2 -0
- package/dist/icons/paths.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -70
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +9 -9
- package/dist/chunks/Callout-BUn0dP_S.js.map +0 -1
- package/dist/chunks/Icon--B1ME-6c.js.map +0 -1
- package/dist/chunks/PatternPicker-B3HcMZWg.js.map +0 -1
- package/dist/chunks/Prefs-BnVEyfWO.js.map +0 -1
- package/dist/chunks/PropertyField-PMrqBA0n.js +0 -743
- package/dist/chunks/PropertyField-PMrqBA0n.js.map +0 -1
- package/dist/chunks/Slider-DSrlMEq8.js +0 -389
- package/dist/chunks/Slider-DSrlMEq8.js.map +0 -1
- package/dist/chunks/SwatchGrid-9ZPiDmxd.js.map +0 -1
- package/dist/chunks/ToggleBar-Z2jZiguF.js +0 -80
- package/dist/chunks/ToggleBar-Z2jZiguF.js.map +0 -1
- package/dist/chunks/ToolOptionsBar-BJKwnemU.js +0 -88
- package/dist/chunks/ToolOptionsBar-BJKwnemU.js.map +0 -1
- package/dist/chunks/checkbox.module-DLQV9tMX.js +0 -5
- package/dist/chunks/checkbox.module-DLQV9tMX.js.map +0 -1
- package/dist/chunks/model-B6zuQg6b.js.map +0 -1
- package/dist/chunks/prefField-BE_Ytr2H.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwatchStrip-Bi9iPLoh.js","names":["_Fragment","s","useRecentColors","normalizeRecentColor","useRecentColorsStore","RecentColorsProvider","Select","SwatchGrid","STANDARD_PALETTE","BASIC_PALETTE","BUILTIN_PALETTES","SwatchStrip_module_default","describeColor","c","color","alpha","getAlpha01","same","a","b","SwatchStrip","props","palettes","columns","ctxStore","store","recent","recentHeading","useId","ownPalette","setOwnPalette","useState","paletteId","shown","p","choosePalette","id","recentOptions","currentIn","value","options","o","grids","Fragment","className"],"sources":["../../src/components/SwatchStrip/palettes.ts","../../src/components/SwatchStrip/SwatchStrip.module.css","../../src/components/SwatchStrip/SwatchStrip.tsx"],"sourcesContent":["import type { SwatchGridOption } from '../SwatchGrid';\n\n/** A named set of swatches offered by {@link SwatchStrip}. */\nexport interface SwatchPalette {\n /** Stable key, for the palette choice and for React. */\n id: string;\n /** What the palette picker shows. */\n name: string;\n /** `value: null` is the \"no paint\" swatch. Each `label` is the swatch's\n * accessible name, so it should say the color. */\n colors: readonly SwatchGridOption[];\n /** Swatches per row. Default: the strip's `columns`. */\n columns?: number;\n}\n\n/** No paint, the named primaries and secondaries, a gray ramp, and eight hue\n * ramps of ten shades each (50 → 900). */\nexport const STANDARD_PALETTE: SwatchPalette = {\n id: 'standard',\n name: 'Standard',\n columns: 10,\n colors: [\n { value: null, label: 'None' },\n { value: '#ffffffff', label: 'White' },\n { value: '#000000ff', label: 'Black' },\n { value: '#ff0000ff', label: 'Red' },\n { value: '#00ff00ff', label: 'Green' },\n { value: '#0000ffff', label: 'Blue' },\n { value: '#00ffffff', label: 'Cyan' },\n { value: '#ff00ffff', label: 'Magenta' },\n { value: '#ffff00ff', label: 'Yellow' },\n { value: '#ff8800ff', label: 'Orange' },\n { value: '#fafafaff', label: 'Gray 50' },\n { value: '#f5f5f5ff', label: 'Gray 100' },\n { value: '#e5e5e5ff', label: 'Gray 200' },\n { value: '#d4d4d4ff', label: 'Gray 300' },\n { value: '#a3a3a3ff', label: 'Gray 400' },\n { value: '#737373ff', label: 'Gray 500' },\n { value: '#525252ff', label: 'Gray 600' },\n { value: '#404040ff', label: 'Gray 700' },\n { value: '#262626ff', label: 'Gray 800' },\n { value: '#171717ff', label: 'Gray 900' },\n { value: '#fef2f2ff', label: 'Red 50' },\n { value: '#fee2e2ff', label: 'Red 100' },\n { value: '#fecacaff', label: 'Red 200' },\n { value: '#fca5a5ff', label: 'Red 300' },\n { value: '#f87171ff', label: 'Red 400' },\n { value: '#ef4444ff', label: 'Red 500' },\n { value: '#dc2626ff', label: 'Red 600' },\n { value: '#b91c1cff', label: 'Red 700' },\n { value: '#991b1bff', label: 'Red 800' },\n { value: '#7f1d1dff', label: 'Red 900' },\n { value: '#fff7edff', label: 'Orange 50' },\n { value: '#ffedd5ff', label: 'Orange 100' },\n { value: '#fed7aaff', label: 'Orange 200' },\n { value: '#fdba74ff', label: 'Orange 300' },\n { value: '#fb923cff', label: 'Orange 400' },\n { value: '#f97316ff', label: 'Orange 500' },\n { value: '#ea580cff', label: 'Orange 600' },\n { value: '#c2410cff', label: 'Orange 700' },\n { value: '#9a3412ff', label: 'Orange 800' },\n { value: '#7c2d12ff', label: 'Orange 900' },\n { value: '#fefce8ff', label: 'Yellow 50' },\n { value: '#fef9c3ff', label: 'Yellow 100' },\n { value: '#fef08aff', label: 'Yellow 200' },\n { value: '#fde047ff', label: 'Yellow 300' },\n { value: '#facc15ff', label: 'Yellow 400' },\n { value: '#eab308ff', label: 'Yellow 500' },\n { value: '#ca8a04ff', label: 'Yellow 600' },\n { value: '#a16207ff', label: 'Yellow 700' },\n { value: '#854d0eff', label: 'Yellow 800' },\n { value: '#713f12ff', label: 'Yellow 900' },\n { value: '#f0fdf4ff', label: 'Green 50' },\n { value: '#dcfce7ff', label: 'Green 100' },\n { value: '#bbf7d0ff', label: 'Green 200' },\n { value: '#86efacff', label: 'Green 300' },\n { value: '#4ade80ff', label: 'Green 400' },\n { value: '#22c55eff', label: 'Green 500' },\n { value: '#16a34aff', label: 'Green 600' },\n { value: '#15803dff', label: 'Green 700' },\n { value: '#166534ff', label: 'Green 800' },\n { value: '#14532dff', label: 'Green 900' },\n { value: '#ecfeffff', label: 'Cyan 50' },\n { value: '#cffafeff', label: 'Cyan 100' },\n { value: '#a5f3fcff', label: 'Cyan 200' },\n { value: '#67e8f9ff', label: 'Cyan 300' },\n { value: '#22d3eeff', label: 'Cyan 400' },\n { value: '#06b6d4ff', label: 'Cyan 500' },\n { value: '#0891b2ff', label: 'Cyan 600' },\n { value: '#0e7490ff', label: 'Cyan 700' },\n { value: '#155e75ff', label: 'Cyan 800' },\n { value: '#164e63ff', label: 'Cyan 900' },\n { value: '#eff6ffff', label: 'Blue 50' },\n { value: '#dbeafeff', label: 'Blue 100' },\n { value: '#bfdbfeff', label: 'Blue 200' },\n { value: '#93c5fdff', label: 'Blue 300' },\n { value: '#60a5faff', label: 'Blue 400' },\n { value: '#3b82f6ff', label: 'Blue 500' },\n { value: '#2563ebff', label: 'Blue 600' },\n { value: '#1d4ed8ff', label: 'Blue 700' },\n { value: '#1e40afff', label: 'Blue 800' },\n { value: '#1e3a8aff', label: 'Blue 900' },\n { value: '#faf5ffff', label: 'Purple 50' },\n { value: '#f3e8ffff', label: 'Purple 100' },\n { value: '#e9d5ffff', label: 'Purple 200' },\n { value: '#d8b4feff', label: 'Purple 300' },\n { value: '#c084fcff', label: 'Purple 400' },\n { value: '#a855f7ff', label: 'Purple 500' },\n { value: '#9333eaff', label: 'Purple 600' },\n { value: '#7e22ceff', label: 'Purple 700' },\n { value: '#6b21a8ff', label: 'Purple 800' },\n { value: '#581c87ff', label: 'Purple 900' },\n { value: '#fdf2f8ff', label: 'Pink 50' },\n { value: '#fce7f3ff', label: 'Pink 100' },\n { value: '#fbcfe8ff', label: 'Pink 200' },\n { value: '#f9a8d4ff', label: 'Pink 300' },\n { value: '#f472b6ff', label: 'Pink 400' },\n { value: '#ec4899ff', label: 'Pink 500' },\n { value: '#db2777ff', label: 'Pink 600' },\n { value: '#be185dff', label: 'Pink 700' },\n { value: '#9d174dff', label: 'Pink 800' },\n { value: '#831843ff', label: 'Pink 900' },\n ],\n};\n\n/** No paint and CSS's basic named colors, with pink and brown. */\nexport const BASIC_PALETTE: SwatchPalette = {\n id: 'basic',\n name: 'Basic',\n columns: 10,\n colors: [\n { value: null, label: 'None' },\n { value: '#000000ff', label: 'Black' },\n { value: '#ffffffff', label: 'White' },\n { value: '#808080ff', label: 'Gray' },\n { value: '#c0c0c0ff', label: 'Silver' },\n { value: '#800000ff', label: 'Maroon' },\n { value: '#ff0000ff', label: 'Red' },\n { value: '#ffa500ff', label: 'Orange' },\n { value: '#ffff00ff', label: 'Yellow' },\n { value: '#808000ff', label: 'Olive' },\n { value: '#00ff00ff', label: 'Lime' },\n { value: '#008000ff', label: 'Green' },\n { value: '#008080ff', label: 'Teal' },\n { value: '#00ffffff', label: 'Cyan' },\n { value: '#000080ff', label: 'Navy' },\n { value: '#0000ffff', label: 'Blue' },\n { value: '#800080ff', label: 'Purple' },\n { value: '#ff00ffff', label: 'Magenta' },\n { value: '#ffc0cbff', label: 'Pink' },\n { value: '#a52a2aff', label: 'Brown' },\n ],\n};\n\n/** What {@link SwatchStrip} offers when given no `palettes`. */\nexport const BUILTIN_PALETTES: readonly SwatchPalette[] = [STANDARD_PALETTE, BASIC_PALETTE];\n",".root {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-3);\n min-inline-size: 0;\n}\n\n.section {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-1);\n min-inline-size: 0;\n}\n\n.heading {\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-xs);\n font-weight: var(--wzl-font-weight-medium);\n text-transform: var(--wzl-params-label-case, uppercase);\n letter-spacing: var(--wzl-params-label-tracking, var(--wzl-tracking-wide));\n color: var(--wzl-fg-muted);\n}\n\n.empty {\n margin: 0;\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-muted);\n}\n","import { useId, useState, type ReactElement } from 'react';\nimport { getAlpha01 } from '@weasel-js/core';\nimport { SwatchGrid, type SwatchGridOption } from '../SwatchGrid';\nimport { Select } from '../Select';\nimport {\n RecentColorsProvider,\n normalizeRecentColor,\n useRecentColors,\n useRecentColorsStore,\n type RecentColorsStore,\n} from '../RecentColors';\nimport { BUILTIN_PALETTES, type SwatchPalette } from './palettes';\nimport s from './SwatchStrip.module.css';\n\n/** Props for {@link SwatchStrip}. */\nexport interface SwatchStripProps {\n /** The color to mark current, in either row. `null` marks \"no paint\". */\n value?: string | null;\n /** A swatch applied: click, Enter or Space. */\n onChange: (value: string | null) => void;\n /** The alternate target, as on {@link SwatchGrid}: shift-click,\n * right-click, Shift+Enter. */\n onAltChange?: (value: string | null) => void;\n /** The preset palettes on offer. Default {@link BUILTIN_PALETTES}. More\n * than one adds a picker. */\n palettes?: readonly SwatchPalette[];\n /** The shown palette's id, controlled. */\n palette?: string;\n /** The shown palette's id to start on, uncontrolled. Default: the first. */\n defaultPalette?: string;\n onPaletteChange?: (id: string) => void;\n /** Where the recent row reads from and applies record into. Default: the\n * nearest {@link RecentColorsProvider}'s. With neither there is no recent\n * row. */\n store?: RecentColorsStore;\n /** Swatches per row, for the recent row and any palette that sets none.\n * Default 10. */\n columns?: number;\n 'aria-label'?: string;\n className?: string;\n}\n\n/** A color as a screen reader should hear it: the hex, and the opacity\n * when it is not opaque. Any other CSS color reads as written. */\nexport function describeColor(color: string): string {\n const c = normalizeRecentColor(color) ?? color;\n if (!c.startsWith('#') || c.length !== 9) return c;\n const alpha = Math.round(getAlpha01(c) * 100);\n return alpha === 100 ? c.slice(0, 7) : `${c.slice(0, 7)}, ${alpha}% opacity`;\n}\n\nfunction same(a: string | null, b: string | null): boolean {\n if (a === null || b === null) return a === b;\n return normalizeRecentColor(a) === normalizeRecentColor(b);\n}\n\n/**\n * The swatches a color picker sits beside: the recently used colors, most\n * recent first, above a preset palette. Applying any swatch records it, so\n * it moves to the head of the recent row — as does a color committed\n * through any other picker under the same {@link RecentColorsProvider}.\n *\n * Like {@link SwatchGrid} it applies nothing itself; `onChange` and\n * `onAltChange` hand the color to the app.\n */\nexport function SwatchStrip(props: SwatchStripProps): ReactElement {\n const { value, onChange, onAltChange, className } = props;\n const palettes = props.palettes ?? BUILTIN_PALETTES;\n const columns = props.columns ?? 10;\n const ctxStore = useRecentColorsStore();\n const store = props.store ?? ctxStore;\n const recent = useRecentColors(store);\n const recentHeading = useId();\n\n const [ownPalette, setOwnPalette] = useState(props.defaultPalette);\n const paletteId = props.palette ?? ownPalette;\n const shown = palettes.find((p) => p.id === paletteId) ?? palettes[0];\n const choosePalette = (id: string): void => {\n setOwnPalette(id);\n props.onPaletteChange?.(id);\n };\n\n const recentOptions: SwatchGridOption[] = recent.map((c) => ({ value: c, label: describeColor(c) }));\n // Match on the color, not its spelling, so `#F00` lights `#ff0000ff`.\n const currentIn = (options: readonly SwatchGridOption[]): string | null | undefined =>\n value === undefined ? undefined : options.find((o) => same(o.value, value))?.value;\n\n const grids = (\n <>\n {store && (\n <section className={s.section} aria-labelledby={recentHeading}>\n <div id={recentHeading} className={s.heading}>Recent</div>\n {recentOptions.length > 0 ? (\n <SwatchGrid\n aria-label=\"Recent colors\"\n options={recentOptions}\n value={currentIn(recentOptions)}\n columns={columns}\n onChange={onChange}\n onAltChange={onAltChange}\n />\n ) : (\n <p className={s.empty}>No recent colors</p>\n )}\n </section>\n )}\n {shown && (\n <section className={s.section}>\n {palettes.length > 1 ? (\n <Select<string>\n label=\"Palette\"\n orientation=\"row\"\n options={palettes.map((p) => ({ value: p.id, label: p.name }))}\n selectedKey={shown.id}\n onSelectionChange={choosePalette}\n />\n ) : (\n <div className={s.heading}>{shown.name}</div>\n )}\n <SwatchGrid\n key={shown.id}\n aria-label={shown.name}\n options={shown.colors}\n value={currentIn(shown.colors)}\n columns={shown.columns ?? columns}\n onChange={onChange}\n onAltChange={onAltChange}\n />\n </section>\n )}\n </>\n );\n\n return (\n <div\n className={[s.root, className].filter(Boolean).join(' ')}\n role=\"group\"\n aria-label={props['aria-label'] ?? 'Swatches'}\n >\n {/* A store given by prop is where applies here record too. */}\n {props.store && props.store !== ctxStore\n ? <RecentColorsProvider store={props.store}>{grids}</RecentColorsProvider>\n : grids}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SwatchStrip-BY6A511W.js","names":["_Fragment","s","useRecentColors","normalizeRecentColor","useRecentColorsStore","RecentColorsProvider","Select","SwatchGrid","STANDARD_PALETTE","BASIC_PALETTE","BUILTIN_PALETTES","SwatchStrip_module_default","describeColor","c","color","alpha","getAlpha01","same","a","b","SwatchStrip","props","palettes","columns","ctxStore","store","recent","recentHeading","useId","ownPalette","setOwnPalette","useState","paletteId","shown","p","choosePalette","id","recentOptions","currentIn","value","options","o","grids","Fragment","className"],"sources":["../../src/components/SwatchStrip/palettes.ts","../../src/components/SwatchStrip/SwatchStrip.module.css","../../src/components/SwatchStrip/SwatchStrip.tsx"],"sourcesContent":["import type { SwatchGridOption } from '../SwatchGrid';\n\n/** A named set of swatches offered by {@link SwatchStrip}. */\nexport interface SwatchPalette {\n /** Stable key, for the palette choice and for React. */\n id: string;\n /** What the palette picker shows. */\n name: string;\n /** `value: null` is the \"no paint\" swatch. Each `label` is the swatch's\n * accessible name, so it should say the color. */\n colors: readonly SwatchGridOption[];\n /** Swatches per row. Default: the strip's `columns`. */\n columns?: number;\n}\n\n/** No paint, the named primaries and secondaries, a gray ramp, and eight hue\n * ramps of ten shades each (50 → 900). */\nexport const STANDARD_PALETTE: SwatchPalette = {\n id: 'standard',\n name: 'Standard',\n columns: 10,\n colors: [\n { value: null, label: 'None' },\n { value: '#ffffffff', label: 'White' },\n { value: '#000000ff', label: 'Black' },\n { value: '#ff0000ff', label: 'Red' },\n { value: '#00ff00ff', label: 'Green' },\n { value: '#0000ffff', label: 'Blue' },\n { value: '#00ffffff', label: 'Cyan' },\n { value: '#ff00ffff', label: 'Magenta' },\n { value: '#ffff00ff', label: 'Yellow' },\n { value: '#ff8800ff', label: 'Orange' },\n { value: '#fafafaff', label: 'Gray 50' },\n { value: '#f5f5f5ff', label: 'Gray 100' },\n { value: '#e5e5e5ff', label: 'Gray 200' },\n { value: '#d4d4d4ff', label: 'Gray 300' },\n { value: '#a3a3a3ff', label: 'Gray 400' },\n { value: '#737373ff', label: 'Gray 500' },\n { value: '#525252ff', label: 'Gray 600' },\n { value: '#404040ff', label: 'Gray 700' },\n { value: '#262626ff', label: 'Gray 800' },\n { value: '#171717ff', label: 'Gray 900' },\n { value: '#fef2f2ff', label: 'Red 50' },\n { value: '#fee2e2ff', label: 'Red 100' },\n { value: '#fecacaff', label: 'Red 200' },\n { value: '#fca5a5ff', label: 'Red 300' },\n { value: '#f87171ff', label: 'Red 400' },\n { value: '#ef4444ff', label: 'Red 500' },\n { value: '#dc2626ff', label: 'Red 600' },\n { value: '#b91c1cff', label: 'Red 700' },\n { value: '#991b1bff', label: 'Red 800' },\n { value: '#7f1d1dff', label: 'Red 900' },\n { value: '#fff7edff', label: 'Orange 50' },\n { value: '#ffedd5ff', label: 'Orange 100' },\n { value: '#fed7aaff', label: 'Orange 200' },\n { value: '#fdba74ff', label: 'Orange 300' },\n { value: '#fb923cff', label: 'Orange 400' },\n { value: '#f97316ff', label: 'Orange 500' },\n { value: '#ea580cff', label: 'Orange 600' },\n { value: '#c2410cff', label: 'Orange 700' },\n { value: '#9a3412ff', label: 'Orange 800' },\n { value: '#7c2d12ff', label: 'Orange 900' },\n { value: '#fefce8ff', label: 'Yellow 50' },\n { value: '#fef9c3ff', label: 'Yellow 100' },\n { value: '#fef08aff', label: 'Yellow 200' },\n { value: '#fde047ff', label: 'Yellow 300' },\n { value: '#facc15ff', label: 'Yellow 400' },\n { value: '#eab308ff', label: 'Yellow 500' },\n { value: '#ca8a04ff', label: 'Yellow 600' },\n { value: '#a16207ff', label: 'Yellow 700' },\n { value: '#854d0eff', label: 'Yellow 800' },\n { value: '#713f12ff', label: 'Yellow 900' },\n { value: '#f0fdf4ff', label: 'Green 50' },\n { value: '#dcfce7ff', label: 'Green 100' },\n { value: '#bbf7d0ff', label: 'Green 200' },\n { value: '#86efacff', label: 'Green 300' },\n { value: '#4ade80ff', label: 'Green 400' },\n { value: '#22c55eff', label: 'Green 500' },\n { value: '#16a34aff', label: 'Green 600' },\n { value: '#15803dff', label: 'Green 700' },\n { value: '#166534ff', label: 'Green 800' },\n { value: '#14532dff', label: 'Green 900' },\n { value: '#ecfeffff', label: 'Cyan 50' },\n { value: '#cffafeff', label: 'Cyan 100' },\n { value: '#a5f3fcff', label: 'Cyan 200' },\n { value: '#67e8f9ff', label: 'Cyan 300' },\n { value: '#22d3eeff', label: 'Cyan 400' },\n { value: '#06b6d4ff', label: 'Cyan 500' },\n { value: '#0891b2ff', label: 'Cyan 600' },\n { value: '#0e7490ff', label: 'Cyan 700' },\n { value: '#155e75ff', label: 'Cyan 800' },\n { value: '#164e63ff', label: 'Cyan 900' },\n { value: '#eff6ffff', label: 'Blue 50' },\n { value: '#dbeafeff', label: 'Blue 100' },\n { value: '#bfdbfeff', label: 'Blue 200' },\n { value: '#93c5fdff', label: 'Blue 300' },\n { value: '#60a5faff', label: 'Blue 400' },\n { value: '#3b82f6ff', label: 'Blue 500' },\n { value: '#2563ebff', label: 'Blue 600' },\n { value: '#1d4ed8ff', label: 'Blue 700' },\n { value: '#1e40afff', label: 'Blue 800' },\n { value: '#1e3a8aff', label: 'Blue 900' },\n { value: '#faf5ffff', label: 'Purple 50' },\n { value: '#f3e8ffff', label: 'Purple 100' },\n { value: '#e9d5ffff', label: 'Purple 200' },\n { value: '#d8b4feff', label: 'Purple 300' },\n { value: '#c084fcff', label: 'Purple 400' },\n { value: '#a855f7ff', label: 'Purple 500' },\n { value: '#9333eaff', label: 'Purple 600' },\n { value: '#7e22ceff', label: 'Purple 700' },\n { value: '#6b21a8ff', label: 'Purple 800' },\n { value: '#581c87ff', label: 'Purple 900' },\n { value: '#fdf2f8ff', label: 'Pink 50' },\n { value: '#fce7f3ff', label: 'Pink 100' },\n { value: '#fbcfe8ff', label: 'Pink 200' },\n { value: '#f9a8d4ff', label: 'Pink 300' },\n { value: '#f472b6ff', label: 'Pink 400' },\n { value: '#ec4899ff', label: 'Pink 500' },\n { value: '#db2777ff', label: 'Pink 600' },\n { value: '#be185dff', label: 'Pink 700' },\n { value: '#9d174dff', label: 'Pink 800' },\n { value: '#831843ff', label: 'Pink 900' },\n ],\n};\n\n/** No paint and CSS's basic named colors, with pink and brown. */\nexport const BASIC_PALETTE: SwatchPalette = {\n id: 'basic',\n name: 'Basic',\n columns: 10,\n colors: [\n { value: null, label: 'None' },\n { value: '#000000ff', label: 'Black' },\n { value: '#ffffffff', label: 'White' },\n { value: '#808080ff', label: 'Gray' },\n { value: '#c0c0c0ff', label: 'Silver' },\n { value: '#800000ff', label: 'Maroon' },\n { value: '#ff0000ff', label: 'Red' },\n { value: '#ffa500ff', label: 'Orange' },\n { value: '#ffff00ff', label: 'Yellow' },\n { value: '#808000ff', label: 'Olive' },\n { value: '#00ff00ff', label: 'Lime' },\n { value: '#008000ff', label: 'Green' },\n { value: '#008080ff', label: 'Teal' },\n { value: '#00ffffff', label: 'Cyan' },\n { value: '#000080ff', label: 'Navy' },\n { value: '#0000ffff', label: 'Blue' },\n { value: '#800080ff', label: 'Purple' },\n { value: '#ff00ffff', label: 'Magenta' },\n { value: '#ffc0cbff', label: 'Pink' },\n { value: '#a52a2aff', label: 'Brown' },\n ],\n};\n\n/** What {@link SwatchStrip} offers when given no `palettes`. */\nexport const BUILTIN_PALETTES: readonly SwatchPalette[] = [STANDARD_PALETTE, BASIC_PALETTE];\n",".root {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-3);\n min-inline-size: 0;\n}\n\n.section {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-1);\n min-inline-size: 0;\n}\n\n.heading {\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-xs);\n font-weight: var(--wzl-font-weight-medium);\n text-transform: var(--wzl-params-label-case, uppercase);\n letter-spacing: var(--wzl-params-label-tracking, var(--wzl-tracking-wide));\n color: var(--wzl-fg-muted);\n}\n\n.empty {\n margin: 0;\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-muted);\n}\n","import { useId, useState, type ReactElement } from 'react';\nimport { getAlpha01 } from '@weasel-js/core';\nimport { SwatchGrid, type SwatchGridOption } from '../SwatchGrid';\nimport { Select } from '../Select';\nimport {\n RecentColorsProvider,\n normalizeRecentColor,\n useRecentColors,\n useRecentColorsStore,\n type RecentColorsStore,\n} from '../RecentColors';\nimport { BUILTIN_PALETTES, type SwatchPalette } from './palettes';\nimport s from './SwatchStrip.module.css';\n\n/** Props for {@link SwatchStrip}. */\nexport interface SwatchStripProps {\n /** The color to mark current, in either row. `null` marks \"no paint\". */\n value?: string | null;\n /** A swatch applied: click, Enter or Space. */\n onChange: (value: string | null) => void;\n /** The alternate target, as on {@link SwatchGrid}: shift-click,\n * right-click, Shift+Enter. */\n onAltChange?: (value: string | null) => void;\n /** The preset palettes on offer. Default {@link BUILTIN_PALETTES}. More\n * than one adds a picker. */\n palettes?: readonly SwatchPalette[];\n /** The shown palette's id, controlled. */\n palette?: string;\n /** The shown palette's id to start on, uncontrolled. Default: the first. */\n defaultPalette?: string;\n onPaletteChange?: (id: string) => void;\n /** Where the recent row reads from and applies record into. Default: the\n * nearest {@link RecentColorsProvider}'s. With neither there is no recent\n * row. */\n store?: RecentColorsStore;\n /** Swatches per row, for the recent row and any palette that sets none.\n * Default 10. */\n columns?: number;\n 'aria-label'?: string;\n className?: string;\n}\n\n/** A color as a screen reader should hear it: the hex, and the opacity\n * when it is not opaque. Any other CSS color reads as written. */\nexport function describeColor(color: string): string {\n const c = normalizeRecentColor(color) ?? color;\n if (!c.startsWith('#') || c.length !== 9) return c;\n const alpha = Math.round(getAlpha01(c) * 100);\n return alpha === 100 ? c.slice(0, 7) : `${c.slice(0, 7)}, ${alpha}% opacity`;\n}\n\nfunction same(a: string | null, b: string | null): boolean {\n if (a === null || b === null) return a === b;\n return normalizeRecentColor(a) === normalizeRecentColor(b);\n}\n\n/**\n * The swatches a color picker sits beside: the recently used colors, most\n * recent first, above a preset palette. Applying any swatch records it, so\n * it moves to the head of the recent row — as does a color committed\n * through any other picker under the same {@link RecentColorsProvider}.\n *\n * Like {@link SwatchGrid} it applies nothing itself; `onChange` and\n * `onAltChange` hand the color to the app.\n */\nexport function SwatchStrip(props: SwatchStripProps): ReactElement {\n const { value, onChange, onAltChange, className } = props;\n const palettes = props.palettes ?? BUILTIN_PALETTES;\n const columns = props.columns ?? 10;\n const ctxStore = useRecentColorsStore();\n const store = props.store ?? ctxStore;\n const recent = useRecentColors(store);\n const recentHeading = useId();\n\n const [ownPalette, setOwnPalette] = useState(props.defaultPalette);\n const paletteId = props.palette ?? ownPalette;\n const shown = palettes.find((p) => p.id === paletteId) ?? palettes[0];\n const choosePalette = (id: string): void => {\n setOwnPalette(id);\n props.onPaletteChange?.(id);\n };\n\n const recentOptions: SwatchGridOption[] = recent.map((c) => ({ value: c, label: describeColor(c) }));\n // Match on the color, not its spelling, so `#F00` lights `#ff0000ff`.\n const currentIn = (options: readonly SwatchGridOption[]): string | null | undefined =>\n value === undefined ? undefined : options.find((o) => same(o.value, value))?.value;\n\n const grids = (\n <>\n {store && (\n <section className={s.section} aria-labelledby={recentHeading}>\n <div id={recentHeading} className={s.heading}>Recent</div>\n {recentOptions.length > 0 ? (\n <SwatchGrid\n aria-label=\"Recent colors\"\n options={recentOptions}\n value={currentIn(recentOptions)}\n columns={columns}\n onChange={onChange}\n onAltChange={onAltChange}\n />\n ) : (\n <p className={s.empty}>No recent colors</p>\n )}\n </section>\n )}\n {shown && (\n <section className={s.section}>\n {palettes.length > 1 ? (\n <Select<string>\n label=\"Palette\"\n orientation=\"row\"\n options={palettes.map((p) => ({ value: p.id, label: p.name }))}\n selectedKey={shown.id}\n onSelectionChange={choosePalette}\n />\n ) : (\n <div className={s.heading}>{shown.name}</div>\n )}\n <SwatchGrid\n key={shown.id}\n aria-label={shown.name}\n options={shown.colors}\n value={currentIn(shown.colors)}\n columns={shown.columns ?? columns}\n onChange={onChange}\n onAltChange={onAltChange}\n />\n </section>\n )}\n </>\n );\n\n return (\n <div\n className={[s.root, className].filter(Boolean).join(' ')}\n role=\"group\"\n aria-label={props['aria-label'] ?? 'Swatches'}\n >\n {/* A store given by prop is where applies here record too. */}\n {props.store && props.store !== ctxStore\n ? <RecentColorsProvider store={props.store}>{grids}</RecentColorsProvider>\n : grids}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as e, Q as t, Z as n } from "./icons-
|
|
1
|
+
import { $ as e, Q as t, Z as n } from "./icons-CofIOTiI.js";
|
|
2
2
|
import { t as r } from "./Button-XB-T75YL.js";
|
|
3
3
|
import { jsx as i } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/ThemeSwitcher/ThemeSwitcher.tsx
|
|
@@ -35,4 +35,4 @@ function o({ value: e, onChange: t, options: n = a, ariaLabel: o = "Theme", size
|
|
|
35
35
|
//#endregion
|
|
36
36
|
export { o as n, a as t };
|
|
37
37
|
|
|
38
|
-
//# sourceMappingURL=ThemeSwitcher-
|
|
38
|
+
//# sourceMappingURL=ThemeSwitcher-tnATkIo1.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeSwitcher-
|
|
1
|
+
{"version":3,"file":"ThemeSwitcher-tnATkIo1.js","names":["ModeLightIcon","ModeDarkIcon","ModeAutoIcon","Button","COLOR_MODE_OPTIONS","ThemeSwitcher","count","options","index","o","value","current","next","previous","name","ariaLabel","onChange","event"],"sources":["../../src/components/ThemeSwitcher/ThemeSwitcher.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from 'react';\nimport type { ColorModePreference } from '@weasel-js/theme';\nimport { ModeAutoIcon, ModeDarkIcon, ModeLightIcon } from '../../icons';\nimport { Button, type ButtonSize, type ButtonVariant } from '../Button/Button';\n\n/** One stop in a {@link ThemeSwitcher}'s rotation. */\nexport interface ThemeSwitcherOption<T extends string = string> {\n value: T;\n /** Drawn in the button while this option is current. */\n icon: ReactNode;\n /** The option's name, read in the accessible name and tooltip. */\n label: string;\n}\n\n/** The color modes, in rotation order, as {@link ThemeSwitcher} cycles them by default. */\nexport const COLOR_MODE_OPTIONS: readonly ThemeSwitcherOption<ColorModePreference>[] = [\n { value: 'auto', icon: <ModeAutoIcon size={16} />, label: 'Auto' },\n { value: 'light', icon: <ModeLightIcon size={16} />, label: 'Light' },\n { value: 'dark', icon: <ModeDarkIcon size={16} />, label: 'Dark' },\n];\n\n/** Props for {@link ThemeSwitcher}. */\nexport interface ThemeSwitcherProps<T extends string = ColorModePreference> {\n value: T;\n onChange: (next: T) => void;\n /** The rotation, in order. Defaults to {@link COLOR_MODE_OPTIONS}. */\n options?: readonly ThemeSwitcherOption<T>[];\n /** What is being switched, leading the accessible name and tooltip\n * (`Theme: Auto — click for Light`). Defaults to `'Theme'`. */\n ariaLabel?: string;\n /** Defaults to `sm`, the size of a header's icon buttons. */\n size?: ButtonSize;\n /** Defaults to `ghost`. */\n variant?: ButtonVariant;\n className?: string;\n}\n\n/**\n * One icon button that steps through a list of options: a click moves to the\n * next, a shift-click to the previous, both wrapping. It shows the current\n * option's icon, and its name and tooltip say what is current and what a\n * click chooses. The compact alternative to `ColorModeControl`'s three\n * segments. Controlled: pair the default color modes with\n * `useColorModePreference` from `@weasel-js/theme/react`.\n */\nexport function ThemeSwitcher<T extends string = ColorModePreference>({\n value,\n onChange,\n options = COLOR_MODE_OPTIONS as unknown as readonly ThemeSwitcherOption<T>[],\n ariaLabel = 'Theme',\n size = 'sm',\n variant = 'ghost',\n className,\n}: ThemeSwitcherProps<T>): ReactElement {\n const count = options.length;\n const index = Math.max(0, options.findIndex((o) => o.value === value));\n const current = options[index];\n const next = options[(index + 1) % count];\n const previous = options[(index - 1 + count) % count];\n const name = `${ariaLabel}: ${current.label} — click for ${next.label}`;\n\n return (\n <Button\n variant={variant}\n size={size}\n iconOnly\n ariaLabel={name}\n tooltip={name}\n className={className}\n onClick={(event) => onChange((event.shiftKey ? previous : next).value)}\n >\n {current.icon}\n </Button>\n );\n}\n"],"mappings":";;;;AAeA,IAAaI,IAA0E;CACrF;EAAE,OAAO;EAAQ,MAAM,kBAACF,GAAD,EAAc,MAAM,GAAK,CAAA;EAAG,OAAO;CAAO;CACjE;EAAE,OAAO;EAAS,MAAM,kBAACF,GAAD,EAAe,MAAM,GAAK,CAAA;EAAG,OAAO;CAAQ;CACpE;EAAE,OAAO;EAAQ,MAAM,kBAACC,GAAD,EAAc,MAAM,GAAK,CAAA;EAAG,OAAO;CAAO;AACnE;AA0BA,SAAgBI,EAAsD,EACpE,UACA,aACA,aAAUD,GACV,eAAY,SACZ,UAAO,MACP,aAAU,SACV,gBACsC;CACtC,IAAME,IAAQC,EAAQ,QAChBC,IAAQ,KAAK,IAAI,GAAGD,EAAQ,WAAW,MAAME,EAAE,UAAUC,CAAK,CAAC,GAC/DC,IAAUJ,EAAQC,IAClBI,IAAOL,GAASC,IAAQ,KAAKF,IAC7BO,IAAWN,GAASC,IAAQ,IAAIF,KAASA,IACzCQ,IAAO,GAAGC,EAAU,IAAIJ,EAAQ,MAAM,eAAeC,EAAK;CAEhE,OACE,kBAACT,GAAD;EACW;EACH;EACN,UAAA;EACA,WAAWW;EACX,SAASA;EACE;EACX,UAAU,MAAUE,GAAUC,EAAM,WAAWJ,IAAWD,EAAA,CAAM,KAAK;EAEpE,UAAAD,EAAQ;CACH,CAAA;AAEZ"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { rt as e, st as t } from "./icons-
|
|
2
|
-
import { n } from "./Disclosure-
|
|
1
|
+
import { rt as e, st as t } from "./icons-CofIOTiI.js";
|
|
2
|
+
import { n } from "./Disclosure-CpWt87nO.js";
|
|
3
3
|
import { r } from "./Plot2D-BrvQS0dh.js";
|
|
4
4
|
import { i, n as a, t as o } from "./createKeyframeLayer-DSVf13Su.js";
|
|
5
5
|
import { t as s } from "./snap-DRjbOiG9.js";
|
|
6
|
-
import { t as c } from "./DetentSlider-
|
|
6
|
+
import { t as c } from "./DetentSlider-D40V8Oma.js";
|
|
7
7
|
import { EASINGS as l, openPointerSession as u, resolveEasing as d, sampleTrack as f, useLatest as p, useVisibleRaf as m } from "@weasel-js/core";
|
|
8
8
|
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
9
9
|
import { useCallback as v, useEffect as y, useLayoutEffect as b, useMemo as x, useReducer as S, useRef as C, useState as w } from "react";
|
|
@@ -926,4 +926,4 @@ function Se(e) {
|
|
|
926
926
|
//#endregion
|
|
927
927
|
export { I as a, A as c, q as i, j as l, xe as n, M as o, Y as r, k as s, Se as t };
|
|
928
928
|
|
|
929
|
-
//# sourceMappingURL=Timeline-
|
|
929
|
+
//# sourceMappingURL=Timeline-FlLUm8nd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timeline-sMCH2fah.js","names":["easingLabel","s","KEY_STEP_MS","s","s","_Fragment","s","s","st","s","PauseIcon","PlayIcon","createKeyframeLayer","applyKeyframeDrag","snapToNearest","DetentSlider","Timeline_module_default","bezierOf","spec","easingBezier","easingLabel","b","name","fn","EASINGS","sampleEasing","resolveEasing","out","count","last","i","EASING_NAMES","PREVIEW_SAMPLES","BEZIER_OF","DEFAULT_BEZIER","EasingPicker","props","bezier","value","label","named","selectValue","points","v","onChange","n","MIN_SPAN_MS","createTimeScale","span","window","perMs","widthPx","perPx","ms","px","toFraction","toPercent","spanFraction","spanPercent","clampWindow","w","bounds","from","zoomWindow","maxSpan","nextSpan","factor","frac","atMs","panWindow","byMs","tickTimes","step","niceStep","minSpacingPx","V_INSET_PCT","KEY_STEP_MS$1","valueRange","values","keys","k","lo","pad","LaneGraph","win","hostRef","useRef","size","setSize","useState","el","update","r","prev","ro","layer","useMemo","t","snapTimes","offset","committed","selection","selectedSegment","live","setLive","state","stateRef","useLatest","layers","onLayerChange","next","raw","onSelect","onSelectSegment","applied","onKeysInput","onLayerCommit","rawPrev","onKeysCommit","yLo","yHi","ticks","LayeredCurveEditor","KEY_STEP_MS","entryTimes","row","Lane","trackRef","sessionRef","drag","setDrag","pct","msAt","rect","clientX","snapPxToMs","width","scale","graph","mode","sampledKeys","onKeyPointerDown","e","at","ev","times","end","openPointerSession","onKeyInput","onKeyCommit","onKeyDown","expanded","onToggleExpand","DisclosureMark","onInsert","Fragment","idx","ZOOM_STEP","formatMs","Ruler","setWidth","widthOf","onPointerDown","onScrub","onWheel","playhead","RATE","multiplier","RATES","seconds","rateText","qty","Transport","looping","loop","rates","rate","a","paused","onPlay","onPause","jsx","onLoopChange","onRateChange","duration","isNumeric","track","buildLanes","walk","nextPath","path","key","depth","tracks","trackAtPath","list","found","samePath","withKeys","withEvents","replaceAtPath","rest","parent","tt","moveKey","sel","toMs","st","moved","keyIndex","et","events","insertKey","trackPath","sampleTrack","deleteKey","setKeyEasing","_drop","easing","setKeys","setKeyValue","Timeline","setExpanded","ownWindow","setOwnWindow","ownSelection","setOwnSelection","segmentSelection","setSegmentSelection","setWindow","setSelection","rows","toggle","onRootKeyDown","selectedTrack","selectedKey","segmentTrack","segmentKey","className","onInput","renderKeyEditor","AnimatedTimeline","bump","useReducer","handle","raf","useVisibleRaf","rafRef","useCallback"],"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/LaneGraph.tsx","../../src/components/Timeline/Lane.tsx","../../src/components/Timeline/Ruler.tsx","../../src/components/Timeline/Transport.tsx","../../src/components/Timeline/lanes.ts","../../src/components/Timeline/keys.ts","../../src/components/Timeline/Timeline.tsx","../../src/components/Timeline/AnimatedTimeline.tsx"],"sourcesContent":[".ruler {\n position: relative;\n height: var(--wzl-control-h);\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 composes: numeric from '@weasel-js/theme/numeric.module.css';\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: var(--wzl-control-h-sm);\n border-bottom: 1px solid var(--wzl-line-subtle);\n}\n\n.laneLabel {\n /* Must match `.gutter`'s width exactly, or every key sits off the ruler. */\n box-sizing: border-box;\n flex: 0 0 var(--wzl-timeline-label-w, 120px);\n display: flex;\n align-items: center;\n gap: var(--wzl-space-2);\n padding-inline: var(--wzl-space-3);\n padding-inline-start: calc(var(--wzl-space-3) + var(--depth, 0) * var(--wzl-space-6));\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; }\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: var(--wzl-handle-size);\n height: var(--wzl-handle-size);\n margin-left: calc(var(--wzl-handle-size) / -2);\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: var(--wzl-handle-size);\n height: var(--wzl-handle-size);\n margin-left: calc(var(--wzl-handle-size) / -2);\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\n.lane[data-mode='graph'] { min-height: 72px; }\n\n/* A numeric lane in graph mode is a curve editor filling the track. */\n.graphHost {\n position: absolute;\n inset: 0;\n}\n\n/* Out-specifies the editor's own defaults: the lane keeps the track's ground\n and the timeline's accent line rather than a plot's. */\n.graphHost .graph {\n --plot-bg: transparent;\n --curve-line: var(--wzl-accent-strong);\n --curve-line-width: 1px;\n}\n\n/* A graph lane's value labels hang off the track's start edge into the label\n column, so the lane's own label ellipsizes before it reaches them. */\n.lane[data-graph='true'] .laneLabel {\n padding-inline-end: calc(var(--wzl-space-3) + var(--wzl-timeline-value-axis-w, 32px));\n}\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: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-3);\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: var(--wzl-icon-button-size);\n padding: var(--wzl-space-1) var(--wzl-space-3);\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 font: inherit;\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-fg);\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: var(--wzl-space-4);\n padding-inline-end: var(--wzl-space-3);\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/* The playhead rewrites this every frame and gains a digit at 10s. Its sizer\n holds the cell at the duration's width, so a transport sized to its content\n does not widen; last in the row, it also takes whatever slack the row has. */\n.time {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: inline-grid;\n margin-inline-start: auto;\n text-align: end;\n color: var(--wzl-fg-muted);\n}\n.rate { display: inline-flex; align-items: center; gap: var(--wzl-space-3); 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 cell as wide as the widest rate on offer. */\n.rateReadout {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: inline-grid;\n text-align: end;\n color: var(--wzl-fg);\n}\n\n.time > *,\n.rateReadout > * {\n grid-area: 1 / 1;\n}\n\n.sizer {\n composes: sizer from '../sizer.module.css';\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: var(--wzl-space-2) var(--wzl-space-3); color: var(--wzl-fg-muted); }\n\n.body { display: flex; align-items: stretch; }\n.gutter { box-sizing: border-box; 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: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-3);\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: var(--wzl-space-3); }\n.easingLabel { composes: numeric from '@weasel-js/theme/numeric.module.css'; color: var(--wzl-fg-muted); }\n\n.easingPreview {\n width: 32px;\n height: 32px; /* not-a-control */\n fill: none;\n stroke: var(--wzl-accent-strong);\n stroke-width: 1;\n border: 1px solid var(--wzl-border);\n}\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\n/** Props for {@link EasingPicker}. `undefined` is linear. */\nexport interface EasingPickerProps {\n value: EasingSpec | undefined;\n onChange: (next: EasingSpec | undefined) => void;\n}\n\n/** A key's easing: a menu of the named curves, a button converting a named\n * curve to its cubic-bezier control points, and a plot of the curve. */\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","import { niceStep } from '../Plot2D/ticks';\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/** Minimum gap between time ticks, in px — the ruler's and the graph grid's. */\nexport const TICK_SPACING_PX = 64;\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 { useLayoutEffect, useMemo, useRef, useState, type ReactElement } from 'react';\nimport { useLatest, type Keyframe } from '@weasel-js/core';\nimport { LayeredCurveEditor } from '../CurveEditor/LayeredCurveEditor';\nimport {\n applyKeyframeDrag, createKeyframeLayer, type KeyframeLayerState,\n} from '../CurveEditor/createKeyframeLayer';\nimport s from './Timeline.module.css';\nimport { TICK_SPACING_PX, tickTimes, type TimeWindow } from './timeScale';\n\n/** Keeps the extreme keys inside the lane: at the very edge a key centers on\n * the lane's border and hangs half into its neighbor. As a percentage of\n * the lane's height, applied top and bottom. */\nconst V_INSET_PCT = 8;\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\n\nexport interface LaneGraphProps {\n keys: readonly Keyframe<number>[];\n label: string;\n window: TimeWindow;\n /** Ruler time of this track's zero. */\n offset: number;\n /** Ruler times a dragged key snaps to. */\n snapTimes: readonly number[];\n selection: number | null;\n selectedSegment: number | null;\n onSelect: (keyIndex: number) => void;\n onSelectSegment?: (keyIndex: number) => void;\n /** Live during a key drag. */\n onKeysInput?: (keys: readonly Keyframe<number>[]) => void;\n /** Once per edit: a drag's end, a handle's release, a key press. */\n onKeysCommit: (keys: readonly Keyframe<number>[]) => void;\n}\n\n/** The value range a lane plots: its keys' extent, padded so no key sits on\n * the border. A flat track gets a unit span so it still has a middle. */\nfunction valueRange(keys: readonly Keyframe<number>[]): [number, number] {\n const values = keys.map((k) => k.value);\n const lo = Math.min(...values);\n const hi = Math.max(...values);\n const span = hi - lo || 1;\n const pad = (span * V_INSET_PCT) / (100 - 2 * V_INSET_PCT);\n return [lo - pad, lo + span + pad];\n}\n\n/** A numeric lane's graph: one keyframe layer on a curve editor, sized to the\n * lane and windowed to the ruler. */\nexport function LaneGraph(props: LaneGraphProps): ReactElement {\n const {\n keys, label, window: win, offset, snapTimes, selection, selectedSegment,\n onSelect, onSelectSegment, onKeysInput, onKeysCommit,\n } = props;\n\n const hostRef = useRef<HTMLDivElement | null>(null);\n const [size, setSize] = useState({ width: 0, height: 0 });\n useLayoutEffect(() => {\n const el = hostRef.current;\n if (!el) return;\n const update = (): void => {\n const r = el.getBoundingClientRect();\n setSize((prev) => (prev.width === r.width && prev.height === r.height ? prev : { width: r.width, height: r.height }));\n };\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const layer = useMemo(() => createKeyframeLayer({\n id: 'keys',\n label,\n formatX: (t) => `${Math.round(t)} ms`,\n // The layer works in track time; the snap candidates are ruler times.\n snapX: snapTimes.map((t) => t - offset),\n xClamp: [0, Infinity],\n step: { x: KEY_STEP_MS },\n }), [label, snapTimes, offset]);\n\n const committed = useMemo<KeyframeLayerState>(\n () => ({ keys, selectedKey: selection, selectedSegment, drag: null }),\n [keys, selection, selectedSegment],\n );\n // Held only while a gesture is in flight, so a consumer echoing live input\n // back through `keys` cannot move the keys out from under the drag.\n const [live, setLive] = useState<KeyframeLayerState | null>(null);\n const state = live ?? committed;\n const stateRef = useLatest(state);\n\n const layers = useMemo(() => [{ layer, state: state as unknown }], [layer, state]);\n\n const onLayerChange = (_id: string, raw: unknown): void => {\n const next = raw as KeyframeLayerState;\n const prev = stateRef.current;\n if (next.selectedKey !== null && next.selectedKey !== prev.selectedKey) onSelect(next.selectedKey);\n if (next.selectedSegment !== null && next.selectedSegment !== prev.selectedSegment) onSelectSegment?.(next.selectedSegment);\n if (!next.drag) { setLive(null); return; }\n setLive(next);\n // A handle drag writes a fresh easing spec per move; publishing those\n // would grow the consumer's easing cache by one entry per pixel.\n if (next.drag.kind !== 'key') return;\n const applied = applyKeyframeDrag(next).keys;\n if (applied !== next.keys) onKeysInput?.(applied);\n };\n\n const onLayerCommit = (_id: string, raw: unknown, rawPrev: unknown): void => {\n setLive(null);\n const next = raw as KeyframeLayerState;\n if (next.keys !== (rawPrev as KeyframeLayerState).keys) onKeysCommit(next.keys);\n };\n\n const [yLo, yHi] = valueRange(state.keys);\n const ticks = size.width > 0 ? tickTimes(win, size.width, TICK_SPACING_PX) : [];\n\n return (\n <div ref={hostRef} className={s.graphHost}>\n {size.width > 0 && size.height > 0 ? (\n <LayeredCurveEditor\n className={s.graph}\n aria-label={`${label} graph`}\n layers={layers}\n onLayerChange={onLayerChange}\n onLayerCommit={onLayerCommit}\n width={size.width}\n height={size.height}\n xRange={[win.from - offset, win.to - offset]}\n yRange={[yLo, yHi]}\n grid={false}\n axes={false}\n // The ruler's ticks, carried into track time.\n xTicks={{ values: ticks.map((t) => t - offset), labels: false }}\n // Clears a key at the track's start edge, which hangs into the gutter.\n yTicks={{ labels: 'outside', gap: 10 }}\n history={false}\n />\n ) : null}\n </div>\n );\n}\n","import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react';\nimport { openPointerSession, type EventTrack, type Keyframe, type PointerSession, type SampledTrack, type TimelineTrack } from '@weasel-js/core';\nimport { DisclosureMark } from '../Disclosure';\nimport s from './Timeline.module.css';\nimport { createTimeScale, spanPercent, toPercent, type TimeWindow } from './timeScale';\nimport { SNAP_RADIUS_PX, snapToNearest } from '../../snap';\nimport { LaneGraph } from './LaneGraph';\nimport type { LaneRow } from './lanes';\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\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 dope-sheet drag. */\n onKeyInput: (keyIndex: number, toMs: number) => void;\n /** Once, at the end of a dope-sheet gesture. */\n onKeyCommit: (keyIndex: number, toMs: number) => void;\n /** Live during a graph-mode key drag: the row's whole key list. */\n onKeysInput?: (keys: readonly Keyframe<number>[]) => void;\n /** Once per graph-mode edit: the row's whole key list. */\n onKeysCommit?: (keys: readonly Keyframe<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}\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, onKeysInput, onKeysCommit,\n } = props;\n const trackRef = useRef<HTMLDivElement | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\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 } | 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_RADIUS_PX) - scale.toMs(0);\n };\n\n // Only a numeric sampled row has an honest value axis to graph.\n const graph = mode === 'graph' && row.numeric;\n const sampledKeys = row.kind === 'sampled' ? (row.track as SampledTrack<number>).keys : [];\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 : snapToNearest(raw + row.offset, snapTimes, snapPxToMs()) - row.offset;\n };\n\n setDrag({ keyIndex: i, t: times[i] });\n\n const end = (): void => {\n setDrag(null);\n sessionRef.current = null;\n };\n // No pointer capture — see `Ruler`, same idiom across the timeline.\n sessionRef.current?.cancel();\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n setDrag({ keyIndex: i, t: at(ev) });\n onKeyInput(i, at(ev));\n },\n onEnd: (ev) => {\n onKeyCommit(i, at(ev));\n end();\n },\n onCancel: end,\n }, { capture: false });\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} data-graph={graph ? 'true' : undefined}>\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 <DisclosureMark open={expanded} />\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 {graph ? (\n <LaneGraph\n keys={sampledKeys}\n label={row.label}\n window={win}\n offset={row.offset}\n snapTimes={snapTimes}\n selection={selection}\n selectedSegment={selectedSegment}\n onSelect={onSelect}\n onSelectSegment={onSelectSegment}\n onKeysInput={onKeysInput}\n onKeysCommit={(keys) => onKeysCommit?.(keys)}\n />\n ) : (\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 {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={{ 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={{ left: pct(drag.t) }}\n />\n ) : null}\n </>\n )}\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, TICK_SPACING_PX, tickTimes, toPercent, zoomWindow, type TimeWindow } from './timeScale';\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 { multiplier, qty } from '@weasel-js/quantity';\nimport { DetentSlider } from '../DetentSlider';\nimport { PauseIcon, PlayIcon } from '../../icons';\nimport s from './Timeline.module.css';\n\nconst RATE = multiplier({ symbol: 'x' });\n\n/** Playback rates the transport offers. */\nconst RATES = [0.25, 0.5, 1, 2, 4] as const;\n\n/** Props for {@link Transport}. Times are in ms. */\nexport interface TransportProps {\n paused: boolean;\n /** A `TimelineHandle`'s loop policy. The switch shows any value but\n * `false` or `0` as on, and toggles between `true` and `false`. */\n loop: boolean | number;\n /** Playback speed multiplier. A rate off the offered detents gets one of its own. */\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`;\nconst rateText = (r: number): string => qty(r, RATE).text;\n\n/** Play/pause, a loop switch, a rate slider, and the playhead over the\n * duration in seconds. Holds no state: every control reports through a callback. */\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 <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.map((r) => ({ value: r, ariaLabel: qty(r, RATE).spoken }))}\n value={rate}\n onChange={onRateChange}\n formatLabel={rateText}\n labels=\"none\"\n className={s.rateSlider}\n />\n <span className={s.rateReadout}>\n <span>{rateText(rate)}</span>\n <span className={s.sizer} aria-hidden=\"true\">\n {rates.map((r) => <span key={r}>{rateText(r)}</span>)}\n </span>\n </span>\n </div>\n\n <span className={s.time}>\n <span data-testid=\"timeline-time\">\n {seconds(Math.min(Math.max(playhead, 0), duration))} / {seconds(duration)}\n </span>\n <span className={s.sizer} aria-hidden=\"true\">\n <span>{seconds(duration)} / {seconds(duration)}</span>\n </span>\n </span>\n </div>\n );\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\n/** One keyframe, addressed by its track's place in the tree and its index in that track's `keys`. */\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/** Replace a sampled track's whole key list, keeping its callbacks. */\nexport function setKeys(\n tracks: readonly Track[], trackPath: readonly number[], keys: readonly Keyframe<unknown>[],\n): Track[] {\n const track = trackAtPath(tracks, trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n return replaceAtPath(tracks, trackPath, withKeys(track, keys.slice()));\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\nexport { snapToNearest as snapTime } from '../../snap';\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, setKeys, setKeyValue, type KeySelection } from './keys';\nimport type { TimeWindow } from './timeScale';\n\nexport type { KeySelection } from './keys';\nexport type { TimeWindow } from './timeScale';\n\n/** What `renderKeyEditor` is handed for the selected keyframe. */\nexport interface KeyEditorCtx<T = unknown> {\n key: Keyframe<T>;\n track: SampledTrack<T>;\n selection: KeySelection;\n /** Replace the selected key's value — `next`'s time and easing are ignored.\n * 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\n/** Props for {@link Timeline}. Times are in ms. `selection` and `window` are\n * controlled when given and held internally otherwise. */\nexport interface TimelineProps {\n tracks: readonly Track[];\n duration: number;\n playhead: number;\n\n /** Keys as marks on a row (`'dope'`, the default), or plotted as a value\n * curve (`'graph'`) on rows whose values are numbers. */\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\n/** A keyframe editor for a set of tracks: a ruler to scrub and zoom on, a\n * lane per track, and a transport. Every edit comes back through `onChange`\n * as a new track array; Delete or Backspace removes the selected key. */\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) => {\n onInput?.(moveKey(tracks, { trackPath, keyIndex }, toMs).tracks);\n }}\n onKeyCommit={(keyIndex, toMs) => {\n const r = moveKey(tracks, { trackPath, keyIndex }, toMs);\n onChange(r.tracks);\n setSelection(r.selection);\n }}\n onKeysInput={(keys) => onInput?.(setKeys(tracks, trackPath, keys))}\n onKeysCommit={(keys) => onChange(setKeys(tracks, trackPath, keys))}\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 />\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, type ReactElement } from 'react';\nimport { useLatest, useVisibleRaf, type TimelineHandle, type Track } from '@weasel-js/core';\nimport { Timeline, type TimelineProps } from './Timeline';\n\n/** Props for {@link AnimatedTimeline}: `<Timeline>`'s, less what it reads off `handle`. */\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 raf = useVisibleRaf(() => {\n bump();\n if (!handle.isPaused()) rafRef.current.request();\n });\n const rafRef = useLatest(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
|
+
{"version":3,"file":"Timeline-FlLUm8nd.js","names":["easingLabel","s","KEY_STEP_MS","s","s","_Fragment","s","s","st","s","PauseIcon","PlayIcon","createKeyframeLayer","applyKeyframeDrag","snapToNearest","DetentSlider","Timeline_module_default","bezierOf","spec","easingBezier","easingLabel","b","name","fn","EASINGS","sampleEasing","resolveEasing","out","count","last","i","EASING_NAMES","PREVIEW_SAMPLES","BEZIER_OF","DEFAULT_BEZIER","EasingPicker","props","bezier","value","label","named","selectValue","points","v","onChange","n","MIN_SPAN_MS","createTimeScale","span","window","perMs","widthPx","perPx","ms","px","toFraction","toPercent","spanFraction","spanPercent","clampWindow","w","bounds","from","zoomWindow","maxSpan","nextSpan","factor","frac","atMs","panWindow","byMs","tickTimes","step","niceStep","minSpacingPx","V_INSET_PCT","KEY_STEP_MS$1","valueRange","values","keys","k","lo","pad","LaneGraph","win","hostRef","useRef","size","setSize","useState","el","update","r","prev","ro","layer","useMemo","t","snapTimes","offset","committed","selection","selectedSegment","live","setLive","state","stateRef","useLatest","layers","onLayerChange","next","raw","onSelect","onSelectSegment","applied","onKeysInput","onLayerCommit","rawPrev","onKeysCommit","yLo","yHi","ticks","LayeredCurveEditor","KEY_STEP_MS","entryTimes","row","Lane","trackRef","sessionRef","drag","setDrag","pct","msAt","rect","clientX","snapPxToMs","width","scale","graph","mode","sampledKeys","onKeyPointerDown","e","at","ev","times","end","openPointerSession","onKeyInput","onKeyCommit","onKeyDown","expanded","onToggleExpand","DisclosureMark","onInsert","Fragment","idx","ZOOM_STEP","formatMs","Ruler","setWidth","widthOf","onPointerDown","onScrub","onWheel","playhead","RATE","multiplier","RATES","seconds","rateText","qty","Transport","looping","loop","rates","rate","a","paused","onPlay","onPause","jsx","onLoopChange","onRateChange","duration","isNumeric","track","buildLanes","walk","nextPath","path","key","depth","tracks","trackAtPath","list","found","samePath","withKeys","withEvents","replaceAtPath","rest","parent","tt","moveKey","sel","toMs","st","moved","keyIndex","et","events","insertKey","trackPath","sampleTrack","deleteKey","setKeyEasing","_drop","easing","setKeys","setKeyValue","Timeline","setExpanded","ownWindow","setOwnWindow","ownSelection","setOwnSelection","segmentSelection","setSegmentSelection","setWindow","setSelection","rows","toggle","onRootKeyDown","selectedTrack","selectedKey","segmentTrack","segmentKey","className","onInput","renderKeyEditor","AnimatedTimeline","bump","useReducer","handle","raf","useVisibleRaf","rafRef","useCallback"],"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/LaneGraph.tsx","../../src/components/Timeline/Lane.tsx","../../src/components/Timeline/Ruler.tsx","../../src/components/Timeline/Transport.tsx","../../src/components/Timeline/lanes.ts","../../src/components/Timeline/keys.ts","../../src/components/Timeline/Timeline.tsx","../../src/components/Timeline/AnimatedTimeline.tsx"],"sourcesContent":[".ruler {\n position: relative;\n height: var(--wzl-control-h);\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 composes: numeric from '@weasel-js/theme/numeric.module.css';\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: var(--wzl-control-h-sm);\n border-bottom: 1px solid var(--wzl-line-subtle);\n}\n\n.laneLabel {\n /* Must match `.gutter`'s width exactly, or every key sits off the ruler. */\n box-sizing: border-box;\n flex: 0 0 var(--wzl-timeline-label-w, 120px);\n display: flex;\n align-items: center;\n gap: var(--wzl-space-2);\n padding-inline: var(--wzl-space-3);\n padding-inline-start: calc(var(--wzl-space-3) + var(--depth, 0) * var(--wzl-space-6));\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; }\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: var(--wzl-handle-size);\n height: var(--wzl-handle-size);\n margin-left: calc(var(--wzl-handle-size) / -2);\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: var(--wzl-handle-size);\n height: var(--wzl-handle-size);\n margin-left: calc(var(--wzl-handle-size) / -2);\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\n.lane[data-mode='graph'] { min-height: 72px; }\n\n/* A numeric lane in graph mode is a curve editor filling the track. */\n.graphHost {\n position: absolute;\n inset: 0;\n}\n\n/* Out-specifies the editor's own defaults: the lane keeps the track's ground\n and the timeline's accent line rather than a plot's. */\n.graphHost .graph {\n --plot-bg: transparent;\n --curve-line: var(--wzl-accent-strong);\n --curve-line-width: 1px;\n}\n\n/* A graph lane's value labels hang off the track's start edge into the label\n column, so the lane's own label ellipsizes before it reaches them. */\n.lane[data-graph='true'] .laneLabel {\n padding-inline-end: calc(var(--wzl-space-3) + var(--wzl-timeline-value-axis-w, 32px));\n}\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: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-3);\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: var(--wzl-icon-button-size);\n padding: var(--wzl-space-1) var(--wzl-space-3);\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 font: inherit;\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-fg);\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: var(--wzl-space-4);\n padding-inline-end: var(--wzl-space-3);\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/* The playhead rewrites this every frame and gains a digit at 10s. Its sizer\n holds the cell at the duration's width, so a transport sized to its content\n does not widen; last in the row, it also takes whatever slack the row has. */\n.time {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: inline-grid;\n margin-inline-start: auto;\n text-align: end;\n color: var(--wzl-fg-muted);\n}\n.rate { display: inline-flex; align-items: center; gap: var(--wzl-space-3); 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 cell as wide as the widest rate on offer. */\n.rateReadout {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: inline-grid;\n text-align: end;\n color: var(--wzl-fg);\n}\n\n.time > *,\n.rateReadout > * {\n grid-area: 1 / 1;\n}\n\n.sizer {\n composes: sizer from '../sizer.module.css';\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: var(--wzl-space-2) var(--wzl-space-3); color: var(--wzl-fg-muted); }\n\n.body { display: flex; align-items: stretch; }\n.gutter { box-sizing: border-box; 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: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-3);\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: var(--wzl-space-3); }\n.easingLabel { composes: numeric from '@weasel-js/theme/numeric.module.css'; color: var(--wzl-fg-muted); }\n\n.easingPreview {\n width: 32px;\n height: 32px; /* not-a-control */\n fill: none;\n stroke: var(--wzl-accent-strong);\n stroke-width: 1;\n border: 1px solid var(--wzl-border);\n}\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\n/** Props for {@link EasingPicker}. `undefined` is linear. */\nexport interface EasingPickerProps {\n value: EasingSpec | undefined;\n onChange: (next: EasingSpec | undefined) => void;\n}\n\n/** A key's easing: a menu of the named curves, a button converting a named\n * curve to its cubic-bezier control points, and a plot of the curve. */\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","import { niceStep } from '../Plot2D/ticks';\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/** Minimum gap between time ticks, in px — the ruler's and the graph grid's. */\nexport const TICK_SPACING_PX = 64;\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 { useLayoutEffect, useMemo, useRef, useState, type ReactElement } from 'react';\nimport { useLatest, type Keyframe } from '@weasel-js/core';\nimport { LayeredCurveEditor } from '../CurveEditor/LayeredCurveEditor';\nimport {\n applyKeyframeDrag, createKeyframeLayer, type KeyframeLayerState,\n} from '../CurveEditor/createKeyframeLayer';\nimport s from './Timeline.module.css';\nimport { TICK_SPACING_PX, tickTimes, type TimeWindow } from './timeScale';\n\n/** Keeps the extreme keys inside the lane: at the very edge a key centers on\n * the lane's border and hangs half into its neighbor. As a percentage of\n * the lane's height, applied top and bottom. */\nconst V_INSET_PCT = 8;\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\n\nexport interface LaneGraphProps {\n keys: readonly Keyframe<number>[];\n label: string;\n window: TimeWindow;\n /** Ruler time of this track's zero. */\n offset: number;\n /** Ruler times a dragged key snaps to. */\n snapTimes: readonly number[];\n selection: number | null;\n selectedSegment: number | null;\n onSelect: (keyIndex: number) => void;\n onSelectSegment?: (keyIndex: number) => void;\n /** Live during a key drag. */\n onKeysInput?: (keys: readonly Keyframe<number>[]) => void;\n /** Once per edit: a drag's end, a handle's release, a key press. */\n onKeysCommit: (keys: readonly Keyframe<number>[]) => void;\n}\n\n/** The value range a lane plots: its keys' extent, padded so no key sits on\n * the border. A flat track gets a unit span so it still has a middle. */\nfunction valueRange(keys: readonly Keyframe<number>[]): [number, number] {\n const values = keys.map((k) => k.value);\n const lo = Math.min(...values);\n const hi = Math.max(...values);\n const span = hi - lo || 1;\n const pad = (span * V_INSET_PCT) / (100 - 2 * V_INSET_PCT);\n return [lo - pad, lo + span + pad];\n}\n\n/** A numeric lane's graph: one keyframe layer on a curve editor, sized to the\n * lane and windowed to the ruler. */\nexport function LaneGraph(props: LaneGraphProps): ReactElement {\n const {\n keys, label, window: win, offset, snapTimes, selection, selectedSegment,\n onSelect, onSelectSegment, onKeysInput, onKeysCommit,\n } = props;\n\n const hostRef = useRef<HTMLDivElement | null>(null);\n const [size, setSize] = useState({ width: 0, height: 0 });\n useLayoutEffect(() => {\n const el = hostRef.current;\n if (!el) return;\n const update = (): void => {\n const r = el.getBoundingClientRect();\n setSize((prev) => (prev.width === r.width && prev.height === r.height ? prev : { width: r.width, height: r.height }));\n };\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const layer = useMemo(() => createKeyframeLayer({\n id: 'keys',\n label,\n formatX: (t) => `${Math.round(t)} ms`,\n // The layer works in track time; the snap candidates are ruler times.\n snapX: snapTimes.map((t) => t - offset),\n xClamp: [0, Infinity],\n step: { x: KEY_STEP_MS },\n }), [label, snapTimes, offset]);\n\n const committed = useMemo<KeyframeLayerState>(\n () => ({ keys, selectedKey: selection, selectedSegment, drag: null }),\n [keys, selection, selectedSegment],\n );\n // Held only while a gesture is in flight, so a consumer echoing live input\n // back through `keys` cannot move the keys out from under the drag.\n const [live, setLive] = useState<KeyframeLayerState | null>(null);\n const state = live ?? committed;\n const stateRef = useLatest(state);\n\n const layers = useMemo(() => [{ layer, state: state as unknown }], [layer, state]);\n\n const onLayerChange = (_id: string, raw: unknown): void => {\n const next = raw as KeyframeLayerState;\n const prev = stateRef.current;\n if (next.selectedKey !== null && next.selectedKey !== prev.selectedKey) onSelect(next.selectedKey);\n if (next.selectedSegment !== null && next.selectedSegment !== prev.selectedSegment) onSelectSegment?.(next.selectedSegment);\n if (!next.drag) { setLive(null); return; }\n setLive(next);\n // A handle drag writes a fresh easing spec per move; publishing those\n // would grow the consumer's easing cache by one entry per pixel.\n if (next.drag.kind !== 'key') return;\n const applied = applyKeyframeDrag(next).keys;\n if (applied !== next.keys) onKeysInput?.(applied);\n };\n\n const onLayerCommit = (_id: string, raw: unknown, rawPrev: unknown): void => {\n setLive(null);\n const next = raw as KeyframeLayerState;\n if (next.keys !== (rawPrev as KeyframeLayerState).keys) onKeysCommit(next.keys);\n };\n\n const [yLo, yHi] = valueRange(state.keys);\n const ticks = size.width > 0 ? tickTimes(win, size.width, TICK_SPACING_PX) : [];\n\n return (\n <div ref={hostRef} className={s.graphHost}>\n {size.width > 0 && size.height > 0 ? (\n <LayeredCurveEditor\n className={s.graph}\n aria-label={`${label} graph`}\n layers={layers}\n onLayerChange={onLayerChange}\n onLayerCommit={onLayerCommit}\n width={size.width}\n height={size.height}\n xRange={[win.from - offset, win.to - offset]}\n yRange={[yLo, yHi]}\n grid={false}\n axes={false}\n // The ruler's ticks, carried into track time.\n xTicks={{ values: ticks.map((t) => t - offset), labels: false }}\n // Clears a key at the track's start edge, which hangs into the gutter.\n yTicks={{ labels: 'outside', gap: 10 }}\n history={false}\n />\n ) : null}\n </div>\n );\n}\n","import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react';\nimport { openPointerSession, type EventTrack, type Keyframe, type PointerSession, type SampledTrack, type TimelineTrack } from '@weasel-js/core';\nimport { DisclosureMark } from '../Disclosure';\nimport s from './Timeline.module.css';\nimport { createTimeScale, spanPercent, toPercent, type TimeWindow } from './timeScale';\nimport { SNAP_RADIUS_PX, snapToNearest } from '../../snap';\nimport { LaneGraph } from './LaneGraph';\nimport type { LaneRow } from './lanes';\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\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 dope-sheet drag. */\n onKeyInput: (keyIndex: number, toMs: number) => void;\n /** Once, at the end of a dope-sheet gesture. */\n onKeyCommit: (keyIndex: number, toMs: number) => void;\n /** Live during a graph-mode key drag: the row's whole key list. */\n onKeysInput?: (keys: readonly Keyframe<number>[]) => void;\n /** Once per graph-mode edit: the row's whole key list. */\n onKeysCommit?: (keys: readonly Keyframe<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}\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, onKeysInput, onKeysCommit,\n } = props;\n const trackRef = useRef<HTMLDivElement | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\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 } | 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_RADIUS_PX) - scale.toMs(0);\n };\n\n // Only a numeric sampled row has an honest value axis to graph.\n const graph = mode === 'graph' && row.numeric;\n const sampledKeys = row.kind === 'sampled' ? (row.track as SampledTrack<number>).keys : [];\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 : snapToNearest(raw + row.offset, snapTimes, snapPxToMs()) - row.offset;\n };\n\n setDrag({ keyIndex: i, t: times[i] });\n\n const end = (): void => {\n setDrag(null);\n sessionRef.current = null;\n };\n // No pointer capture — see `Ruler`, same idiom across the timeline.\n sessionRef.current?.cancel();\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n setDrag({ keyIndex: i, t: at(ev) });\n onKeyInput(i, at(ev));\n },\n onEnd: (ev) => {\n onKeyCommit(i, at(ev));\n end();\n },\n onCancel: end,\n }, { capture: false });\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} data-graph={graph ? 'true' : undefined}>\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 <DisclosureMark open={expanded} />\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 {graph ? (\n <LaneGraph\n keys={sampledKeys}\n label={row.label}\n window={win}\n offset={row.offset}\n snapTimes={snapTimes}\n selection={selection}\n selectedSegment={selectedSegment}\n onSelect={onSelect}\n onSelectSegment={onSelectSegment}\n onKeysInput={onKeysInput}\n onKeysCommit={(keys) => onKeysCommit?.(keys)}\n />\n ) : (\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 {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={{ 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={{ left: pct(drag.t) }}\n />\n ) : null}\n </>\n )}\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, TICK_SPACING_PX, tickTimes, toPercent, zoomWindow, type TimeWindow } from './timeScale';\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 { multiplier, qty } from '@weasel-js/quantity';\nimport { DetentSlider } from '../DetentSlider';\nimport { PauseIcon, PlayIcon } from '../../icons';\nimport s from './Timeline.module.css';\n\nconst RATE = multiplier({ symbol: 'x' });\n\n/** Playback rates the transport offers. */\nconst RATES = [0.25, 0.5, 1, 2, 4] as const;\n\n/** Props for {@link Transport}. Times are in ms. */\nexport interface TransportProps {\n paused: boolean;\n /** A `TimelineHandle`'s loop policy. The switch shows any value but\n * `false` or `0` as on, and toggles between `true` and `false`. */\n loop: boolean | number;\n /** Playback speed multiplier. A rate off the offered detents gets one of its own. */\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`;\nconst rateText = (r: number): string => qty(r, RATE).text;\n\n/** Play/pause, a loop switch, a rate slider, and the playhead over the\n * duration in seconds. Holds no state: every control reports through a callback. */\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 <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.map((r) => ({ value: r, ariaLabel: qty(r, RATE).spoken }))}\n value={rate}\n onChange={onRateChange}\n formatLabel={rateText}\n labels=\"none\"\n className={s.rateSlider}\n />\n <span className={s.rateReadout}>\n <span>{rateText(rate)}</span>\n <span className={s.sizer} aria-hidden=\"true\">\n {rates.map((r) => <span key={r}>{rateText(r)}</span>)}\n </span>\n </span>\n </div>\n\n <span className={s.time}>\n <span data-testid=\"timeline-time\">\n {seconds(Math.min(Math.max(playhead, 0), duration))} / {seconds(duration)}\n </span>\n <span className={s.sizer} aria-hidden=\"true\">\n <span>{seconds(duration)} / {seconds(duration)}</span>\n </span>\n </span>\n </div>\n );\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\n/** One keyframe, addressed by its track's place in the tree and its index in that track's `keys`. */\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/** Replace a sampled track's whole key list, keeping its callbacks. */\nexport function setKeys(\n tracks: readonly Track[], trackPath: readonly number[], keys: readonly Keyframe<unknown>[],\n): Track[] {\n const track = trackAtPath(tracks, trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n return replaceAtPath(tracks, trackPath, withKeys(track, keys.slice()));\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\nexport { snapToNearest as snapTime } from '../../snap';\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, setKeys, setKeyValue, type KeySelection } from './keys';\nimport type { TimeWindow } from './timeScale';\n\nexport type { KeySelection } from './keys';\nexport type { TimeWindow } from './timeScale';\n\n/** What `renderKeyEditor` is handed for the selected keyframe. */\nexport interface KeyEditorCtx<T = unknown> {\n key: Keyframe<T>;\n track: SampledTrack<T>;\n selection: KeySelection;\n /** Replace the selected key's value — `next`'s time and easing are ignored.\n * 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\n/** Props for {@link Timeline}. Times are in ms. `selection` and `window` are\n * controlled when given and held internally otherwise. */\nexport interface TimelineProps {\n tracks: readonly Track[];\n duration: number;\n playhead: number;\n\n /** Keys as marks on a row (`'dope'`, the default), or plotted as a value\n * curve (`'graph'`) on rows whose values are numbers. */\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\n/** A keyframe editor for a set of tracks: a ruler to scrub and zoom on, a\n * lane per track, and a transport. Every edit comes back through `onChange`\n * as a new track array; Delete or Backspace removes the selected key. */\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) => {\n onInput?.(moveKey(tracks, { trackPath, keyIndex }, toMs).tracks);\n }}\n onKeyCommit={(keyIndex, toMs) => {\n const r = moveKey(tracks, { trackPath, keyIndex }, toMs);\n onChange(r.tracks);\n setSelection(r.selection);\n }}\n onKeysInput={(keys) => onInput?.(setKeys(tracks, trackPath, keys))}\n onKeysCommit={(keys) => onChange(setKeys(tracks, trackPath, keys))}\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 />\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, type ReactElement } from 'react';\nimport { useLatest, useVisibleRaf, type TimelineHandle, type Track } from '@weasel-js/core';\nimport { Timeline, type TimelineProps } from './Timeline';\n\n/** Props for {@link AnimatedTimeline}: `<Timeline>`'s, less what it reads off `handle`. */\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 raf = useVisibleRaf(() => {\n bump();\n if (!handle.isPaused()) rafRef.current.request();\n });\n const rafRef = useLatest(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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"}
|