From fd9bd1a7557d5f2f50bd0e5b2a5813205d5891e1 Mon Sep 17 00:00:00 2001 From: Alan Trebugeais Date: Sun, 10 May 2026 00:19:03 +0200 Subject: [PATCH 1/4] fix: SliderControl is now --- src/components/video-editor/SliderControl.tsx | 83 ++++++++++++++----- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/src/components/video-editor/SliderControl.tsx b/src/components/video-editor/SliderControl.tsx index d9db8998..3de99ed7 100644 --- a/src/components/video-editor/SliderControl.tsx +++ b/src/components/video-editor/SliderControl.tsx @@ -1,5 +1,5 @@ import type { PointerEvent as ReactPointerEvent } from "react"; -import { useCallback, useRef } from "react"; +import { useCallback, useRef, memo, useEffect } from "react"; import { cn } from "@/lib/utils"; interface SliderControlProps { @@ -27,7 +27,7 @@ function quantizeToStep(value: number, min: number, step: number) { return min + Math.round((value - min) / step) * step; } -export function SliderControl({ +export const SliderControl = memo(function SliderControl({ label, value, defaultValue: _defaultValue, @@ -40,30 +40,49 @@ export function SliderControl({ accentColor = "blue", }: SliderControlProps) { const rootRef = useRef(null); + const valueTextRef = useRef(null); + const boundsRef = useRef(null); + const requestRef = useRef(null); + const pct = Math.min(100, Math.max(0, ((value - min) / (max - min || 1)) * 100)); + const dividerClass = accentColor === "purple" ? "bg-foreground/95 shadow-[0_0_10px_rgba(139,92,246,0.28)]" : "bg-foreground/95 shadow-[0_0_10px_rgba(37,99,235,0.28)]"; - const setValueFromClientX = useCallback( + // Sync initial and prop-driven changes to CSS variable + useEffect(() => { + if (rootRef.current) { + rootRef.current.style.setProperty("--slider-pct", `${pct}%`); + } + }, [pct]); + + const updateValue = useCallback( (clientX: number) => { - const root = rootRef.current; - if (!root) { + const bounds = boundsRef.current; + if (!bounds || bounds.width <= 6) { return; } - const bounds = root.getBoundingClientRect(); - if (!(bounds.width > 0)) { - return; - } - - const normalized = clamp((clientX - bounds.left) / bounds.width, 0, 1); + const normalized = clamp((clientX - (bounds.left + 3)) / (bounds.width - 6), 0, 1); const rawValue = min + normalized * (max - min); const nextValue = clamp(quantizeToStep(rawValue, min, step), min, max); - onChange(Number(nextValue.toFixed(6))); + const finalValue = Number(nextValue.toFixed(6)); + const currentPct = (normalized * 100).toFixed(4); + + // Direct DOM update for instant feedback + if (rootRef.current) { + rootRef.current.style.setProperty("--slider-pct", `${currentPct}%`); + } + if (valueTextRef.current) { + valueTextRef.current.textContent = formatValue(finalValue); + } + + // Notify parent + onChange(finalValue); }, - [max, min, onChange, step], + [max, min, onChange, step, formatValue], ); const handlePointerDown = useCallback( @@ -72,15 +91,24 @@ export function SliderControl({ const pointerId = event.pointerId; const target = event.currentTarget; + // Cache bounds to avoid layout thrashing during move + boundsRef.current = target.getBoundingClientRect(); + target.setPointerCapture(pointerId); - setValueFromClientX(event.clientX); + updateValue(event.clientX); const handlePointerMove = (moveEvent: PointerEvent) => { if (moveEvent.pointerId !== pointerId) { return; } - setValueFromClientX(moveEvent.clientX); + if (requestRef.current) { + cancelAnimationFrame(requestRef.current); + } + + requestRef.current = requestAnimationFrame(() => { + updateValue(moveEvent.clientX); + }); }; const finishPointer = (finishEvent: PointerEvent) => { @@ -88,17 +116,22 @@ export function SliderControl({ return; } + if (requestRef.current) { + cancelAnimationFrame(requestRef.current); + } + target.releasePointerCapture(pointerId); target.removeEventListener("pointermove", handlePointerMove); target.removeEventListener("pointerup", finishPointer); target.removeEventListener("pointercancel", finishPointer); + boundsRef.current = null; }; target.addEventListener("pointermove", handlePointerMove); target.addEventListener("pointerup", finishPointer); target.addEventListener("pointercancel", finishPointer); }, - [setValueFromClientX], + [updateValue], ); return ( @@ -124,11 +157,16 @@ export function SliderControl({ } }} className="relative flex h-10 w-full select-none items-center overflow-hidden rounded-xl bg-editor-bg/80 px-1.5 outline-none focus-visible:ring-1 focus-visible:ring-[#2563EB]/40" + style={ + { + "--slider-pct": `${pct}%`, + } as React.CSSProperties + } >
0 ? `max(calc(${pct}% - 6px), 2.1rem)` : 0, + width: "calc(var(--slider-pct) - (var(--slider-pct) * 6px / 100%))", }} />
{label} - + {formatValue(value)}
); -} +}); From bc674346aa07845250705e5db395e6f5b20547c1 Mon Sep 17 00:00:00 2001 From: Alan Trebugeais Date: Sun, 10 May 2026 00:31:04 +0200 Subject: [PATCH 2/4] fix problems called out by CodeRabbits --- src/components/video-editor/SliderControl.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/video-editor/SliderControl.tsx b/src/components/video-editor/SliderControl.tsx index 3de99ed7..25a1c145 100644 --- a/src/components/video-editor/SliderControl.tsx +++ b/src/components/video-editor/SliderControl.tsx @@ -69,11 +69,13 @@ export const SliderControl = memo(function SliderControl({ const rawValue = min + normalized * (max - min); const nextValue = clamp(quantizeToStep(rawValue, min, step), min, max); const finalValue = Number(nextValue.toFixed(6)); - const currentPct = (normalized * 100).toFixed(4); + const finalPct = (((finalValue - min) / (max - min || 1)) * 100).toFixed(4); // Direct DOM update for instant feedback if (rootRef.current) { - rootRef.current.style.setProperty("--slider-pct", `${currentPct}%`); + rootRef.current.style.setProperty("--slider-pct", `${finalPct}%`); + rootRef.current.setAttribute("aria-valuenow", String(finalValue)); + rootRef.current.setAttribute("aria-valuetext", formatValue(finalValue)); } if (valueTextRef.current) { valueTextRef.current.textContent = formatValue(finalValue); @@ -118,6 +120,11 @@ export const SliderControl = memo(function SliderControl({ if (requestRef.current) { cancelAnimationFrame(requestRef.current); + requestRef.current = null; + } + + if (finishEvent.type === "pointerup") { + updateValue(finishEvent.clientX); } target.releasePointerCapture(pointerId); From f092b1ede9ba7ab802d147529377c870c21aea7d Mon Sep 17 00:00:00 2001 From: Alan Trebugeais Date: Sun, 10 May 2026 00:44:23 +0200 Subject: [PATCH 3/4] fix nitpick comments --- src/components/video-editor/SliderControl.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/video-editor/SliderControl.tsx b/src/components/video-editor/SliderControl.tsx index 25a1c145..a7c41f88 100644 --- a/src/components/video-editor/SliderControl.tsx +++ b/src/components/video-editor/SliderControl.tsx @@ -54,7 +54,7 @@ export const SliderControl = memo(function SliderControl({ // Sync initial and prop-driven changes to CSS variable useEffect(() => { if (rootRef.current) { - rootRef.current.style.setProperty("--slider-pct", `${pct}%`); + rootRef.current.style.setProperty("--slider-pct", String(pct / 100)); } }, [pct]); @@ -73,7 +73,7 @@ export const SliderControl = memo(function SliderControl({ // Direct DOM update for instant feedback if (rootRef.current) { - rootRef.current.style.setProperty("--slider-pct", `${finalPct}%`); + rootRef.current.style.setProperty("--slider-pct", String(Number(finalPct) / 100)); rootRef.current.setAttribute("aria-valuenow", String(finalValue)); rootRef.current.setAttribute("aria-valuetext", formatValue(finalValue)); } @@ -166,14 +166,14 @@ export const SliderControl = memo(function SliderControl({ className="relative flex h-10 w-full select-none items-center overflow-hidden rounded-xl bg-editor-bg/80 px-1.5 outline-none focus-visible:ring-1 focus-visible:ring-[#2563EB]/40" style={ { - "--slider-pct": `${pct}%`, + "--slider-pct": String(pct / 100), } as React.CSSProperties } >
From 865a900c5a6fc4d7746d61532a2c6839660ed539 Mon Sep 17 00:00:00 2001 From: Alan Trebugeais Date: Mon, 11 May 2026 00:40:32 +0200 Subject: [PATCH 4/4] fix suggestion from web adderall --- src/components/video-editor/SliderControl.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/video-editor/SliderControl.tsx b/src/components/video-editor/SliderControl.tsx index a7c41f88..dd0aaf0d 100644 --- a/src/components/video-editor/SliderControl.tsx +++ b/src/components/video-editor/SliderControl.tsx @@ -182,7 +182,7 @@ export const SliderControl = memo(function SliderControl({ dividerClass, )} style={{ - left: "calc(2px + var(--slider-pct) * (100% - 6px))", + left: "calc(var(--slider-pct) * (100% - 6px) - 6px)", }} />