summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor
diff options
context:
space:
mode:
authorZaid-maker <53424436+Zaid-maker@users.noreply.github.com>2025-06-25 14:12:45 +0500
committerZaid-maker <53424436+Zaid-maker@users.noreply.github.com>2025-06-25 14:12:45 +0500
commite2a0f745c8584eeabe274cb05cb7dae79b877731 (patch)
tree34ed85028f24eb98e8d865c329bc8c55e4b3bdbc /apps/web/src/components/editor
parent1722055a721eae513750d54feab7018d1dc650f1 (diff)
parentc18b173bc918c7f1688a30586b75792938e57690 (diff)
downloadopencut-e2a0f745c8584eeabe274cb05cb7dae79b877731.tar.gz
Merge branch 'main' into bump-bun-version
Diffstat (limited to 'apps/web/src/components/editor')
-rw-r--r--apps/web/src/components/editor/media-panel.tsx72
-rw-r--r--apps/web/src/components/editor/preview-panel.tsx67
2 files changed, 94 insertions, 45 deletions
diff --git a/apps/web/src/components/editor/media-panel.tsx b/apps/web/src/components/editor/media-panel.tsx
index 95cfa0a8..f2b3f657 100644
--- a/apps/web/src/components/editor/media-panel.tsx
+++ b/apps/web/src/components/editor/media-panel.tsx
@@ -84,17 +84,20 @@ export function MediaPanel() {
useEffect(() => {
const filtered = mediaItems.filter((item) => {
- if (mediaFilter && mediaFilter !== 'all' && item.type !== mediaFilter) {
+ if (mediaFilter && mediaFilter !== "all" && item.type !== mediaFilter) {
return false;
}
-
- if (searchQuery && !item.name.toLowerCase().includes(searchQuery.toLowerCase())) {
+
+ if (
+ searchQuery &&
+ !item.name.toLowerCase().includes(searchQuery.toLowerCase())
+ ) {
return false;
}
-
+
return true;
});
-
+
setFilteredMediaItems(filtered);
}, [mediaItems, mediaFilter, searchQuery]);
@@ -209,23 +212,23 @@ export function MediaPanel() {
{/* Button to add/upload media */}
<div className="flex gap-2">
{/* Search and filter controls */}
- <select
- value={mediaFilter}
- onChange={(e) => setMediaFilter(e.target.value)}
- className="px-2 py-1 text-xs border rounded bg-background"
- >
- <option value="all">All</option>
- <option value="video">Video</option>
- <option value="audio">Audio</option>
- <option value="image">Image</option>
- </select>
- <input
- type="text"
- placeholder="Search media..."
- className="min-w-[60px] flex-1 px-2 py-1 text-xs border rounded bg-background"
- value={searchQuery}
- onChange={(e) => setSearchQuery(e.target.value)}
- />
+ <select
+ value={mediaFilter}
+ onChange={(e) => setMediaFilter(e.target.value)}
+ className="px-2 py-1 text-xs border rounded bg-background"
+ >
+ <option value="all">All</option>
+ <option value="video">Video</option>
+ <option value="audio">Audio</option>
+ <option value="image">Image</option>
+ </select>
+ <input
+ type="text"
+ placeholder="Search media..."
+ className="min-w-[60px] flex-1 px-2 py-1 text-xs border rounded bg-background"
+ value={searchQuery}
+ onChange={(e) => setSearchQuery(e.target.value)}
+ />
{/* Add media button */}
<Button
@@ -233,21 +236,26 @@ export function MediaPanel() {
size="sm"
onClick={handleFileSelect}
disabled={isProcessing}
- className="flex-none min-w-[80px] whitespace-nowrap"
+ className="flex-none min-w-[30px] whitespace-nowrap overflow-hidden px-2 justify-center items-center"
>
{isProcessing ? (
<>
- <Upload className="h-4 w-4 mr-2 animate-spin" />
- Processing...
+ <Upload className="h-4 w-4 animate-spin" />
+ <span className="hidden md:inline ml-2">Processing...</span>
</>
) : (
<>
<Plus className="h-4 w-4" />
- Add
+ <span
+ className="hidden sm:inline ml-2"
+ aria-label="Add file"
+ >
+ Add
+ </span>
</>
)}
</Button>
- </div>
+ </div>
</div>
<div className="flex-1 overflow-y-auto p-2">
@@ -276,7 +284,15 @@ export function MediaPanel() {
<AspectRatio ratio={item.aspectRatio}>
{renderPreview(item)}
</AspectRatio>
- <span className="text-xs truncate px-1">{item.name}</span>
+ <span
+ className="text-xs truncate px-1 max-w-full"
+ aria-label={item.name}
+ title={item.name}
+ >
+ {item.name.length > 8
+ ? `${item.name.slice(0, 4)}...${item.name.slice(-3)}`
+ : item.name}
+ </span>
</Button>
{/* Show remove button on hover */}
diff --git a/apps/web/src/components/editor/preview-panel.tsx b/apps/web/src/components/editor/preview-panel.tsx
index 624a549e..33bfe688 100644
--- a/apps/web/src/components/editor/preview-panel.tsx
+++ b/apps/web/src/components/editor/preview-panel.tsx
@@ -5,16 +5,17 @@ import { useMediaStore } from "@/stores/media-store";
import { usePlaybackStore } from "@/stores/playback-store";
import { VideoPlayer } from "@/components/ui/video-player";
import { Button } from "@/components/ui/button";
-import { Play, Pause } from "lucide-react";
+import { Play, Pause, Volume2, VolumeX } from "lucide-react";
import { useState, useRef } from "react";
// Debug flag - set to false to hide active clips info
-const SHOW_DEBUG_INFO = process.env.NODE_ENV === 'development';
+const SHOW_DEBUG_INFO = process.env.NODE_ENV === "development";
export function PreviewPanel() {
const { tracks } = useTimelineStore();
const { mediaItems } = useMediaStore();
- const { isPlaying, toggle, currentTime } = usePlaybackStore();
+ const { isPlaying, toggle, currentTime, muted, toggleMute, volume } =
+ usePlaybackStore();
const [canvasSize, setCanvasSize] = useState({ width: 1920, height: 1080 });
const [showDebug, setShowDebug] = useState(SHOW_DEBUG_INFO);
const previewRef = useRef<HTMLDivElement>(null);
@@ -30,12 +31,14 @@ export function PreviewPanel() {
tracks.forEach((track) => {
track.clips.forEach((clip) => {
const clipStart = clip.startTime;
- const clipEnd = clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
+ const clipEnd =
+ clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
if (currentTime >= clipStart && currentTime < clipEnd) {
- const mediaItem = clip.mediaId === "test"
- ? { type: "test", name: clip.name, url: "", thumbnailUrl: "" }
- : mediaItems.find((item) => item.id === clip.mediaId);
+ const mediaItem =
+ clip.mediaId === "test"
+ ? { type: "test", name: clip.name, url: "", thumbnailUrl: "" }
+ : mediaItems.find((item) => item.id === clip.mediaId);
if (mediaItem || clip.mediaId === "test") {
activeClips.push({ clip, track, mediaItem });
@@ -134,13 +137,19 @@ export function PreviewPanel() {
<select
value={`${canvasSize.width}x${canvasSize.height}`}
onChange={(e) => {
- const preset = canvasPresets.find(p => `${p.width}x${p.height}` === e.target.value);
- if (preset) setCanvasSize({ width: preset.width, height: preset.height });
+ const preset = canvasPresets.find(
+ (p) => `${p.width}x${p.height}` === e.target.value
+ );
+ if (preset)
+ setCanvasSize({ width: preset.width, height: preset.height });
}}
className="bg-background border rounded px-2 py-1 text-xs"
>
- {canvasPresets.map(preset => (
- <option key={preset.name} value={`${preset.width}x${preset.height}`}>
+ {canvasPresets.map((preset) => (
+ <option
+ key={preset.name}
+ value={`${preset.width}x${preset.height}`}
+ >
{preset.name} ({preset.width}×{preset.height})
</option>
))}
@@ -154,12 +163,30 @@ export function PreviewPanel() {
onClick={() => setShowDebug(!showDebug)}
className="text-xs"
>
- Debug {showDebug ? 'ON' : 'OFF'}
+ Debug {showDebug ? "ON" : "OFF"}
</Button>
)}
- <Button variant="outline" size="sm" onClick={toggle} className="ml-auto">
- {isPlaying ? <Pause className="h-3 w-3 mr-1" /> : <Play className="h-3 w-3 mr-1" />}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={toggleMute}
+ className="ml-auto"
+ >
+ {muted || volume === 0 ? (
+ <VolumeX className="h-3 w-3 mr-1" />
+ ) : (
+ <Volume2 className="h-3 w-3 mr-1" />
+ )}
+ {muted || volume === 0 ? "Unmute" : "Mute"}
+ </Button>
+
+ <Button variant="outline" size="sm" onClick={toggle}>
+ {isPlaying ? (
+ <Pause className="h-3 w-3 mr-1" />
+ ) : (
+ <Play className="h-3 w-3 mr-1" />
+ )}
{isPlaying ? "Pause" : "Play"}
</Button>
</div>
@@ -177,7 +204,9 @@ export function PreviewPanel() {
>
{activeClips.length === 0 ? (
<div className="absolute inset-0 flex items-center justify-center text-white/50">
- {tracks.length === 0 ? "Drop media to start editing" : "No clips at current time"}
+ {tracks.length === 0
+ ? "Drop media to start editing"
+ : "No clips at current time"}
</div>
) : (
activeClips.map((clipData, index) => renderClip(clipData, index))
@@ -188,7 +217,9 @@ export function PreviewPanel() {
{/* Debug Info Panel - Conditionally rendered */}
{showDebug && (
<div className="border-t bg-background p-2 flex-shrink-0">
- <div className="text-xs font-medium mb-1">Debug: Active Clips ({activeClips.length})</div>
+ <div className="text-xs font-medium mb-1">
+ Debug: Active Clips ({activeClips.length})
+ </div>
<div className="flex gap-2 overflow-x-auto">
{activeClips.map((clipData, index) => (
<div
@@ -199,7 +230,9 @@ export function PreviewPanel() {
{index + 1}
</span>
<span>{clipData.clip.name}</span>
- <span className="text-muted-foreground">({clipData.mediaItem?.type || 'test'})</span>
+ <span className="text-muted-foreground">
+ ({clipData.mediaItem?.type || "test"})
+ </span>
</div>
))}
{activeClips.length === 0 && (