summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-08-13 00:52:19 +0200
committerMaze Winther <mazewinther@gmail.com>2025-08-13 00:52:19 +0200
commit71b6718337ec24ce8791ef039c7dd08ced81e974 (patch)
treeded067364f276746633da5adb4071e77230e398a /apps/web/src/components/editor
parenta97eb26fa4552df520f26bbacfaa44d1f8faff3d (diff)
downloadopencut-71b6718337ec24ce8791ef039c7dd08ced81e974.tar.gz
feat: implement mute video tracks
Diffstat (limited to 'apps/web/src/components/editor')
-rw-r--r--apps/web/src/components/editor/preview-panel.tsx12
-rw-r--r--apps/web/src/components/editor/timeline/timeline-element.tsx25
2 files changed, 24 insertions, 13 deletions
diff --git a/apps/web/src/components/editor/preview-panel.tsx b/apps/web/src/components/editor/preview-panel.tsx
index 785162a6..3873ebca 100644
--- a/apps/web/src/components/editor/preview-panel.tsx
+++ b/apps/web/src/components/editor/preview-panel.tsx
@@ -235,7 +235,7 @@ export function PreviewPanel() {
const activeElements: ActiveElement[] = [];
// Iterate tracks from bottom to top so topmost track renders last (on top)
- ;[...tracks].reverse().forEach((track) => {
+ [...tracks].reverse().forEach((track) => {
track.elements.forEach((element) => {
if (element.hidden) return;
const elementStart = element.startTime;
@@ -436,7 +436,7 @@ export function PreviewPanel() {
trimStart={element.trimStart}
trimEnd={element.trimEnd}
clipDuration={element.duration}
- trackMuted={elementData.track.muted}
+ trackMuted={element.muted || elementData.track.muted}
/>
</div>
);
@@ -462,14 +462,18 @@ export function PreviewPanel() {
// Audio elements (no visual representation)
if (mediaItem.type === "audio") {
return (
- <div key={element.id} className="absolute inset-0" style={{ pointerEvents: "none" }}>
+ <div
+ key={element.id}
+ className="absolute inset-0"
+ style={{ pointerEvents: "none" }}
+ >
<AudioPlayer
src={mediaItem.url!}
clipStartTime={element.startTime}
trimStart={element.trimStart}
trimEnd={element.trimEnd}
clipDuration={element.duration}
- trackMuted={elementData.track.muted}
+ trackMuted={element.muted || elementData.track.muted}
/>
</div>
);
diff --git a/apps/web/src/components/editor/timeline/timeline-element.tsx b/apps/web/src/components/editor/timeline/timeline-element.tsx
index b26a14bb..f2623161 100644
--- a/apps/web/src/components/editor/timeline/timeline-element.tsx
+++ b/apps/web/src/components/editor/timeline/timeline-element.tsx
@@ -50,6 +50,7 @@ export function TimelineElement({
replaceElementMedia,
rippleEditingEnabled,
toggleElementHidden,
+ toggleElementMuted,
} = useTimelineStore();
const { currentTime } = usePlaybackStore();
@@ -57,7 +58,7 @@ export function TimelineElement({
element.type === "media"
? mediaItems.find((item) => item.id === element.mediaId)
: null;
- const isAudio = mediaItem?.type === "audio";
+ const hasAudio = mediaItem?.type === "audio" || mediaItem?.type === "video";
const { resizing, handleResizeStart, handleResizeMove, handleResizeEnd } =
useTimelineElementResize({
@@ -124,8 +125,12 @@ export function TimelineElement({
}
};
- const handleToggleElementHidden = (e: React.MouseEvent) => {
+ const handleToggleElementContext = (e: React.MouseEvent) => {
e.stopPropagation();
+ if (hasAudio && element.type === "media") {
+ toggleElementMuted(track.id, element.id);
+ return;
+ }
toggleElementHidden(track.id, element.id);
};
@@ -246,6 +251,8 @@ export function TimelineElement({
}
};
+ const isMuted = element.type === "media" ? element.muted === true : false;
+
return (
<ContextMenu>
<ContextMenuTrigger asChild>
@@ -279,9 +286,9 @@ export function TimelineElement({
{renderElementContent()}
</div>
- {element.hidden && (
+ {(hasAudio ? isMuted : element.hidden) && (
<div className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center pointer-events-none">
- {isAudio ? (
+ {hasAudio ? (
<VolumeX className="h-6 w-6 text-white" />
) : (
<EyeOff className="h-6 w-6 text-white" />
@@ -309,9 +316,9 @@ export function TimelineElement({
<Scissors className="h-4 w-4 mr-2" />
Split at playhead
</ContextMenuItem>
- <ContextMenuItem onClick={handleToggleElementHidden}>
- {isAudio ? (
- element.hidden ? (
+ <ContextMenuItem onClick={handleToggleElementContext}>
+ {hasAudio ? (
+ isMuted ? (
<Volume2 className="h-4 w-4 mr-2" />
) : (
<VolumeX className="h-4 w-4 mr-2" />
@@ -322,8 +329,8 @@ export function TimelineElement({
<EyeOff className="h-4 w-4 mr-2" />
)}
<span>
- {isAudio
- ? element.hidden
+ {hasAudio
+ ? isMuted
? "Unmute"
: "Mute"
: element.hidden