From 4dcc6d1a7fb7121f51d98059c909a08f41245de0 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Sun, 26 Apr 2026 19:32:54 +0700 Subject: [PATCH 1/4] feat(editor): add audio region volume control --- src/components/video-editor/SettingsPanel.tsx | 49 +++++++++++++++++-- src/components/video-editor/VideoEditor.tsx | 22 +++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 5d016b238..aa7d2bfd0 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -12,6 +12,7 @@ import { } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { useTheme } from "@/contexts/ThemeContext"; import { getAssetPath, getRenderableAssetUrl, getWallpaperThumbnailUrl } from "@/lib/assetPath"; import type { ExtensionSettingField } from "@/lib/extensions"; import { extensionHost, type FrameInstance } from "@/lib/extensions"; @@ -27,7 +28,6 @@ import minimalCursorUrl from "../../../Minimal Cursor.svg"; import { useI18n, useScopedT } from "../../contexts/I18nContext"; import type { AppLocale } from "../../i18n/config"; import { SUPPORTED_LOCALES } from "../../i18n/config"; -import { useTheme } from "@/contexts/ThemeContext"; import { AnnotationSettingsPanel } from "./AnnotationSettingsPanel"; import { loadEditorPreferences, saveEditorPreferences } from "./editorPreferences"; import { SliderControl } from "./SliderControl"; @@ -50,9 +50,6 @@ import type { ZoomMode, ZoomTransitionEasing, } from "./types"; -import { - isZeroPadding, -} from "./videoPlayback/layoutUtils"; import { DEFAULT_AUTO_CAPTION_SETTINGS, DEFAULT_CROP_REGION, @@ -76,6 +73,7 @@ import { SPEED_OPTIONS, } from "./types"; import { fromCursorSwaySliderValue, toCursorSwaySliderValue } from "./videoPlayback/cursorSway"; +import { isZeroPadding } from "./videoPlayback/layoutUtils"; import { cursorSetAssets, getCursorStyleSizeMultiplier, @@ -348,6 +346,10 @@ interface SettingsPanelProps { onClipSpeedChange?: (speed: number) => void; onClipMutedChange?: (muted: boolean) => void; onClipDelete?: (id: string) => void; + selectedAudioId?: string | null; + selectedAudioVolume?: number | null; + onAudioVolumeChange?: (volume: number) => void; + onAudioDelete?: (id: string) => void; shadowIntensity?: number; onShadowChange?: (intensity: number) => void; backgroundBlur?: number; @@ -721,6 +723,10 @@ export function SettingsPanel({ onClipSpeedChange, onClipMutedChange, onClipDelete, + selectedAudioId, + selectedAudioVolume, + onAudioVolumeChange, + onAudioDelete, shadowIntensity = 0.67, onShadowChange, backgroundBlur = 0, @@ -3031,7 +3037,7 @@ export function SettingsPanel({
{selectedTrimId && ( @@ -3094,6 +3100,39 @@ export function SettingsPanel({
)} + + {selectedAudioId && ( +
+
+ + {tSettings("audio.volumeTitle", "Audio Volume")} + + + {Math.round((selectedAudioVolume ?? 1) * 100)}% + +
+ onAudioVolumeChange?.(v)} + formatValue={(v) => `${Math.round(v * 100)}%`} + parseInput={(text) => parseFloat(text.replace(/%$/, "")) / 100} + /> + +
+ )} ); diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index bc2c01ef0..5fd491a53 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -3257,6 +3257,19 @@ export default function VideoEditor() { ); }, []); + const handleAudioVolumeChange = useCallback((volume: number) => { + if (!selectedAudioId) { + return; + } + + const nextVolume = Number.isFinite(volume) ? Math.max(0, Math.min(1, volume)) : 1; + setAudioRegions((prev) => + prev.map((region) => + region.id === selectedAudioId ? { ...region, volume: nextVolume } : region, + ), + ); + }, [selectedAudioId]); + const handleAudioDelete = useCallback( (id: string) => { setAudioRegions((prev) => prev.filter((region) => region.id !== id)); @@ -5188,6 +5201,15 @@ export default function VideoEditor() { selectedClipId && handleClipMutedChange(muted) } onClipDelete={handleClipDelete} + selectedAudioId={selectedAudioId} + selectedAudioVolume={ + selectedAudioId + ? (audioRegions.find((r) => r.id === selectedAudioId) + ?.volume ?? null) + : null + } + onAudioVolumeChange={handleAudioVolumeChange} + onAudioDelete={handleAudioDelete} shadowIntensity={shadowIntensity} onShadowChange={setShadowIntensity} backgroundBlur={backgroundBlur} From d93b455be279f318a9ffe19d9ce54c80cb25e895 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Sun, 26 Apr 2026 22:27:08 +0700 Subject: [PATCH 2/4] fix(editor): include smoke export config deps --- src/components/video-editor/VideoEditor.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 5fd491a53..9eb0afb2c 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -4409,6 +4409,8 @@ export default function VideoEditor() { effectiveSpeedRegions, frame, smokeExportConfig.encodingMode, + smokeExportConfig.fps, + smokeExportConfig.quality, ], ); From bd12ab2b1a735346b4a20a04d83a6cbfe33be54b Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Mon, 27 Apr 2026 00:05:07 +0700 Subject: [PATCH 3/4] fix(editor): keep timeline row hit targets visible --- src/components/video-editor/timeline/Row.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/video-editor/timeline/Row.tsx b/src/components/video-editor/timeline/Row.tsx index f2aba7238..f54e5a9cb 100644 --- a/src/components/video-editor/timeline/Row.tsx +++ b/src/components/video-editor/timeline/Row.tsx @@ -30,7 +30,11 @@ export default function Row({ id, children, label, hint, isEmpty, labelColor = " {hint} )} -
+
{children}
From d67c153166a6637ca61c89afb5b506b1d51512a7 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Mon, 27 Apr 2026 00:12:17 +0700 Subject: [PATCH 4/4] fix(editor): ignore invalid audio volume input --- src/components/video-editor/VideoEditor.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 9eb0afb2c..580627ac4 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -3262,7 +3262,11 @@ export default function VideoEditor() { return; } - const nextVolume = Number.isFinite(volume) ? Math.max(0, Math.min(1, volume)) : 1; + if (!Number.isFinite(volume)) { + return; + } + + const nextVolume = Math.max(0, Math.min(1, volume)); setAudioRegions((prev) => prev.map((region) => region.id === selectedAudioId ? { ...region, volume: nextVolume } : region,