From 2ee0a44735d2b930348e9774909638b2c1909738 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 29 Apr 2026 01:28:50 +0200 Subject: refactor: unify element params --- apps/web/src/timeline/components/audio-tab.tsx | 151 --------------------- .../src/timeline/components/audio-volume-line.tsx | 13 +- .../src/timeline/components/timeline-element.tsx | 8 +- 3 files changed, 10 insertions(+), 162 deletions(-) delete mode 100644 apps/web/src/timeline/components/audio-tab.tsx (limited to 'apps/web/src/timeline/components') diff --git a/apps/web/src/timeline/components/audio-tab.tsx b/apps/web/src/timeline/components/audio-tab.tsx deleted file mode 100644 index 1e47dcc8..00000000 --- a/apps/web/src/timeline/components/audio-tab.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { NumberField } from "@/components/ui/number-field"; -import { VOLUME_DB_MAX, VOLUME_DB_MIN } from "@/timeline/audio-constants"; -import { isSourceAudioSeparated } from "@/timeline/audio-separation"; -import { DEFAULTS } from "@/timeline/defaults"; -import { - clamp, - formatNumberForDisplay, - getFractionDigitsForStep, - isNearlyEqual, - snapToStep, -} from "@/utils/math"; -import type { AudioElement, VideoElement } from "@/timeline"; -import { resolveNumberAtTime } from "@/animation/values"; -import { useEditor } from "@/editor/use-editor"; -import { useElementPlayhead } from "@/components/editor/panels/properties/hooks/use-element-playhead"; -import { useKeyframedNumberProperty } from "@/components/editor/panels/properties/hooks/use-keyframed-number-property"; -import { KeyframeToggle } from "@/components/editor/panels/properties/components/keyframe-toggle"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { VolumeHighIcon } from "@hugeicons/core-free-icons"; -import { - Section, - SectionContent, - SectionField, - SectionFields, - SectionHeader, - SectionTitle, -} from "@/components/section"; - -const VOLUME_STEP = 0.1; -const VOLUME_FRACTION_DIGITS = getFractionDigitsForStep({ step: VOLUME_STEP }); - -export function AudioTab({ - element, - trackId, -}: { - element: AudioElement | VideoElement; - trackId: string; -}) { - const editor = useEditor(); - const { localTime, isPlayheadWithinElementRange } = useElementPlayhead({ - startTime: element.startTime, - duration: element.duration, - }); - const resolvedVolume = resolveNumberAtTime({ - baseValue: element.volume ?? DEFAULTS.element.volume, - animations: element.animations, - propertyPath: "volume", - localTime, - }); - const volume = useKeyframedNumberProperty({ - trackId, - elementId: element.id, - animations: element.animations, - propertyPath: "volume", - localTime, - isPlayheadWithinElementRange, - displayValue: formatNumberForDisplay({ - value: resolvedVolume, - fractionDigits: VOLUME_FRACTION_DIGITS, - }), - parse: (input) => { - const parsed = parseFloat(input); - if (Number.isNaN(parsed)) { - return null; - } - - return clamp({ - value: snapToStep({ value: parsed, step: VOLUME_STEP }), - min: VOLUME_DB_MIN, - max: VOLUME_DB_MAX, - }); - }, - valueAtPlayhead: resolvedVolume, - step: VOLUME_STEP, - buildBaseUpdates: ({ value }) => ({ - volume: value, - }), - }); - const isDefault = - volume.hasAnimatedKeyframes && isPlayheadWithinElementRange - ? isNearlyEqual({ - leftValue: resolvedVolume, - rightValue: DEFAULTS.element.volume, - }) - : (element.volume ?? DEFAULTS.element.volume) === DEFAULTS.element.volume; - const isSeparated = - element.type === "video" && isSourceAudioSeparated({ element }); - - return ( - <> - {isSeparated && ( -
-

Audio has been separated.

- -
- )} -
- - Audio - - - - - } - > - } - value={volume.displayValue} - onFocus={volume.onFocus} - onChange={volume.onChange} - onBlur={volume.onBlur} - dragSensitivity="slow" - scrubClamp={{ min: VOLUME_DB_MIN, max: VOLUME_DB_MAX }} - onScrub={volume.scrubTo} - onScrubEnd={volume.commitScrub} - onReset={() => - volume.commitValue({ - value: DEFAULTS.element.volume, - }) - } - isDefault={isDefault} - suffix="dB" - /> - - - -
- - ); -} diff --git a/apps/web/src/timeline/components/audio-volume-line.tsx b/apps/web/src/timeline/components/audio-volume-line.tsx index ac2fd32a..659bc5dc 100644 --- a/apps/web/src/timeline/components/audio-volume-line.tsx +++ b/apps/web/src/timeline/components/audio-volume-line.tsx @@ -3,13 +3,12 @@ import { useCallback, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useEditor } from "@/editor/use-editor"; -import { DEFAULTS } from "@/timeline/defaults"; import { getDbFromLinePos, getLinePosFromDb, } from "@/timeline/audio-display"; import { VOLUME_DB_MAX, VOLUME_DB_MIN } from "@/timeline/audio-constants"; -import { hasAnimatedVolume } from "@/timeline/audio-state"; +import { getElementVolume, hasAnimatedVolume } from "@/timeline/audio-state"; import type { AudioElement } from "@/timeline/types"; import { clamp, @@ -60,10 +59,8 @@ export function AudioVolumeLine({ const editor = useEditor(); const surfaceRef = useRef(null); const activePointerIdRef = useRef(null); - const startVolumeRef = useRef(element.volume ?? DEFAULTS.element.volume); - const lastPreviewVolumeRef = useRef( - element.volume ?? DEFAULTS.element.volume, - ); + const startVolumeRef = useRef(getElementVolume({ element })); + const lastPreviewVolumeRef = useRef(getElementVolume({ element })); const hasChangedRef = useRef(false); const [isDragging, setIsDragging] = useState(false); const [tooltipClientPos, setTooltipClientPos] = useState<{ @@ -72,7 +69,7 @@ export function AudioVolumeLine({ } | null>(null); const hasAnimatedEnvelope = hasAnimatedVolume({ element }); - const currentVolume = element.volume ?? DEFAULTS.element.volume; + const currentVolume = getElementVolume({ element }); const lineTop = `${getLinePosFromDb({ db: currentVolume })}%`; const volumeLabel = `${formatNumberForDisplay({ @@ -96,7 +93,7 @@ export function AudioVolumeLine({ { trackId, elementId: element.id, - updates: { volume: nextVolume }, + updates: { params: { volume: nextVolume } }, }, ], }); diff --git a/apps/web/src/timeline/components/timeline-element.tsx b/apps/web/src/timeline/components/timeline-element.tsx index be8409f3..74f026dc 100644 --- a/apps/web/src/timeline/components/timeline-element.tsx +++ b/apps/web/src/timeline/components/timeline-element.tsx @@ -47,7 +47,7 @@ import { getSourceAudioActionLabel, isSourceAudioSeparated, } from "@/timeline/audio-separation"; -import { buildWaveformGainSamples } from "@/timeline/audio-state"; +import { buildWaveformGainSamples, isElementMuted } from "@/timeline/audio-state"; import { getTimelinePixelsPerSecond } from "@/timeline"; import { buildWaveformSourceKey } from "@/media/waveform-summary"; import { addMediaTime, type MediaTime, TICKS_PER_SECOND } from "@/wasm"; @@ -335,7 +335,7 @@ export function TimelineElement({ } }; - const isMuted = canElementHaveAudio(element) && element.muted === true; + const isMuted = canElementHaveAudio(element) && isElementMuted({ element }); const canToggleCurrentSourceAudio = selectedElements.length === 1 && isCurrentElementSelected && @@ -908,7 +908,9 @@ function TextElementContent({ }) { return (
- {element.content} + + {typeof element.params.content === "string" ? element.params.content : ""} +
); } -- cgit v1.3.1