diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx
index 39214bb6..d1946fdf 100644
--- a/src/components/video-editor/SettingsPanel.tsx
+++ b/src/components/video-editor/SettingsPanel.tsx
@@ -2,7 +2,7 @@ import { cn } from "@/lib/utils";
import { useEffect, useRef } from "react";
import { getAssetPath, getRenderableAssetUrl } from "@/lib/assetPath";
import { BUILT_IN_WALLPAPERS, WALLPAPER_PATHS, WALLPAPER_RELATIVE_PATHS } from "@/lib/wallpapers";
-import { Slider } from "@/components/ui/slider";
+import { SliderControl } from "./SliderControl";
import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
@@ -12,7 +12,7 @@ import { Trash2, Download, Crop, X, Bug, Upload, Star, Film, Image, Sparkles, Pa
import { toast } from "sonner";
import { useI18n, useScopedT } from "../../contexts/I18nContext";
import type { ZoomDepth, CropRegion, AnnotationRegion, AnnotationType, PlaybackSpeed } from "./types";
-import { SPEED_OPTIONS } from "./types";
+import { SPEED_OPTIONS, DEFAULT_CURSOR_SIZE, DEFAULT_CURSOR_SMOOTHING, DEFAULT_CURSOR_MOTION_BLUR, DEFAULT_CURSOR_CLICK_BOUNCE, DEFAULT_ZOOM_MOTION_BLUR } from "./types";
import { CropControl } from "./CropControl";
import { KeyboardShortcutsHelp } from "./KeyboardShortcutsHelp";
import { AnnotationSettingsPanel } from "./AnnotationSettingsPanel";
@@ -457,34 +457,32 @@ export function SettingsPanel({
/>
-
-
{tSettings('effects.backgroundBlur')}
-
{backgroundBlur.toFixed(1)}px
-
-
onBackgroundBlurChange?.(values[0])}
+ onBackgroundBlurChange?.(v)}
+ formatValue={(v) => `${v.toFixed(1)}px`}
+ parseInput={(t) => parseFloat(t.replace(/px$/, ''))}
/>
-
-
{tSettings('effects.zoomMotionBlur')}
-
{zoomMotionBlur.toFixed(2)}×
-
-
onZoomMotionBlurChange?.(values[0])}
+ onZoomMotionBlurChange?.(v)}
+ formatValue={(v) => `${v.toFixed(2)}×`}
+ parseInput={(t) => parseFloat(t.replace(/×$/, ''))}
/>
@@ -500,107 +498,100 @@ export function SettingsPanel({
-
-
{tSettings('effects.cursorSize')}
-
{cursorSize.toFixed(2)}×
-
-
onCursorSizeChange?.(values[0])}
+ onCursorSizeChange?.(v)}
+ formatValue={(v) => `${v.toFixed(2)}×`}
+ parseInput={(t) => parseFloat(t.replace(/×$/, ''))}
/>
-
-
{tSettings('effects.cursorSmoothing')}
-
{cursorSmoothing <= 0 ? tSettings('effects.off') : cursorSmoothing.toFixed(2)}
-
-
onCursorSmoothingChange?.(values[0])}
+ onCursorSmoothingChange?.(v)}
+ formatValue={(v) => v <= 0 ? 'Off' : v.toFixed(2)}
+ parseInput={(t) => parseFloat(t)}
/>
-
-
{tSettings('effects.cursorMotionBlur')}
-
{cursorMotionBlur.toFixed(2)}×
-
-
onCursorMotionBlurChange?.(values[0])}
+ onCursorMotionBlurChange?.(v)}
+ formatValue={(v) => `${v.toFixed(2)}×`}
+ parseInput={(t) => parseFloat(t.replace(/×$/, ''))}
/>
-
-
{tSettings('effects.cursorClickBounce')}
-
{cursorClickBounce.toFixed(2)}×
-
-
onCursorClickBounceChange?.(values[0])}
+ onCursorClickBounceChange?.(v)}
+ formatValue={(v) => `${v.toFixed(2)}×`}
+ parseInput={(t) => parseFloat(t.replace(/×$/, ''))}
/>
-
-
{tSettings('effects.shadow')}
-
{Math.round(shadowIntensity * 100)}%
-
-
onShadowChange?.(values[0])}
+ onShadowChange?.(v)}
+ formatValue={(v) => `${Math.round(v * 100)}%`}
+ parseInput={(t) => parseFloat(t.replace(/%$/, '')) / 100}
/>
-
-
{tSettings('effects.roundness')}
-
{borderRadius}px
-
-
onBorderRadiusChange?.(values[0])}
+ onBorderRadiusChange?.(v)}
+ formatValue={(v) => `${v}px`}
+ parseInput={(t) => parseFloat(t.replace(/px$/, ''))}
/>
-
-
{tSettings('effects.padding')}
-
{padding}%
-
-
onPaddingChange?.(values[0])}
+ onPaddingChange?.(v)}
+ formatValue={(v) => `${v}%`}
+ parseInput={(t) => parseFloat(t.replace(/%$/, ''))}
/>
diff --git a/src/components/video-editor/SliderControl.tsx b/src/components/video-editor/SliderControl.tsx
new file mode 100644
index 00000000..7025bc65
--- /dev/null
+++ b/src/components/video-editor/SliderControl.tsx
@@ -0,0 +1,109 @@
+import { useState, useRef, useEffect } from "react";
+import { Slider } from "@/components/ui/slider";
+import { RotateCcw } from "lucide-react";
+
+interface SliderControlProps {
+ label: string;
+ value: number;
+ defaultValue: number;
+ min: number;
+ max: number;
+ step: number;
+ onChange: (value: number) => void;
+ formatValue: (value: number) => string;
+ parseInput: (text: string) => number | null;
+ accentColor?: "purple" | "blue";
+}
+
+export function SliderControl({
+ label,
+ value,
+ defaultValue,
+ min,
+ max,
+ step,
+ onChange,
+ formatValue,
+ parseInput,
+ accentColor = "blue",
+}: SliderControlProps) {
+ const [editing, setEditing] = useState(false);
+ const [editText, setEditText] = useState("");
+ const inputRef = useRef
(null);
+ const isModified = value !== defaultValue;
+
+ useEffect(() => {
+ if (editing && inputRef.current) {
+ inputRef.current.focus();
+ inputRef.current.select();
+ }
+ }, [editing]);
+
+ const commitEdit = () => {
+ const parsed = parseInput(editText);
+ if (parsed != null && !isNaN(parsed)) {
+ onChange(Math.min(max, Math.max(min, parsed)));
+ }
+ setEditing(false);
+ };
+
+ const cancelEdit = () => {
+ setEditing(false);
+ };
+
+ return (
+ <>
+
+
+
{label}
+ {isModified && (
+
+ )}
+
+ {editing ? (
+
setEditText(e.target.value)}
+ onBlur={commitEdit}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") commitEdit();
+ if (e.key === "Escape") cancelEdit();
+ }}
+ className="w-14 text-[10px] text-right font-mono bg-white/10 border border-white/20 rounded px-1 py-0 text-slate-200 outline-none focus:border-white/40"
+ />
+ ) : (
+
{
+ setEditText(formatValue(value));
+ setEditing(true);
+ }}
+ >
+ {formatValue(value)}
+
+ )}
+
+ onChange(values[0])}
+ min={min}
+ max={max}
+ step={step}
+ className={
+ accentColor === "purple"
+ ? "w-full [&_[role=slider]]:bg-[#8b5cf6] [&_[role=slider]]:border-[#8b5cf6] [&_[role=slider]]:h-3 [&_[role=slider]]:w-3"
+ : "w-full [&_[role=slider]]:bg-[#2563EB] [&_[role=slider]]:border-[#2563EB] [&_[role=slider]]:h-3 [&_[role=slider]]:w-3"
+ }
+ />
+ >
+ );
+}