summaryrefslogtreecommitdiff
path: root/apps/web/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/components')
-rw-r--r--apps/web/src/components/editor/media-panel.tsx86
-rw-r--r--apps/web/src/components/editor/timeline.tsx2
-rw-r--r--apps/web/src/components/header.tsx18
-rw-r--r--apps/web/src/components/icons.tsx17
-rw-r--r--apps/web/src/components/landing/hero.tsx4
5 files changed, 92 insertions, 35 deletions
diff --git a/apps/web/src/components/editor/media-panel.tsx b/apps/web/src/components/editor/media-panel.tsx
index 0d6a1f1c..95cfa0a8 100644
--- a/apps/web/src/components/editor/media-panel.tsx
+++ b/apps/web/src/components/editor/media-panel.tsx
@@ -7,7 +7,7 @@ import { useMediaStore } from "@/stores/media-store";
import { processMediaFiles } from "@/lib/media-processing";
import { Plus, Image, Video, Music, Trash2, Upload } from "lucide-react";
import { useDragDrop } from "@/hooks/use-drag-drop";
-import { useRef, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import { toast } from "sonner";
// MediaPanel lets users add, view, and drag media (images, videos, audio) into the project.
@@ -17,6 +17,8 @@ export function MediaPanel() {
const { mediaItems, addMediaItem, removeMediaItem } = useMediaStore();
const fileInputRef = useRef<HTMLInputElement>(null);
const [isProcessing, setIsProcessing] = useState(false);
+ const [searchQuery, setSearchQuery] = useState("");
+ const [mediaFilter, setMediaFilter] = useState("all");
const processFiles = async (files: FileList | File[]) => {
// If no files, do nothing
@@ -78,6 +80,24 @@ export function MediaPanel() {
e.dataTransfer.effectAllowed = "copy";
};
+ const [filteredMediaItems, setFilteredMediaItems] = useState(mediaItems);
+
+ useEffect(() => {
+ const filtered = mediaItems.filter((item) => {
+ if (mediaFilter && mediaFilter !== 'all' && item.type !== mediaFilter) {
+ return false;
+ }
+
+ if (searchQuery && !item.name.toLowerCase().includes(searchQuery.toLowerCase())) {
+ return false;
+ }
+
+ return true;
+ });
+
+ setFilteredMediaItems(filtered);
+ }, [mediaItems, mediaFilter, searchQuery]);
+
const renderPreview = (item: any) => {
// Render a preview for each media type (image, video, audio, unknown)
// Each preview is draggable to the timeline
@@ -187,30 +207,52 @@ export function MediaPanel() {
<div className="p-2 border-b">
{/* Button to add/upload media */}
- <Button
- variant="outline"
- size="sm"
- className="w-full"
- onClick={handleFileSelect}
- disabled={isProcessing}
- >
- {isProcessing ? (
- <>
- <Upload className="h-4 w-4 mr-2 animate-spin" />
- Processing...
- </>
- ) : (
- <>
- <Plus className="h-4 w-4 mr-2" />
- Add Media
- </>
- )}
- </Button>
+ <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)}
+ />
+
+ {/* Add media button */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleFileSelect}
+ disabled={isProcessing}
+ className="flex-none min-w-[80px] whitespace-nowrap"
+ >
+ {isProcessing ? (
+ <>
+ <Upload className="h-4 w-4 mr-2 animate-spin" />
+ Processing...
+ </>
+ ) : (
+ <>
+ <Plus className="h-4 w-4" />
+ Add
+ </>
+ )}
+ </Button>
+ </div>
</div>
<div className="flex-1 overflow-y-auto p-2">
{/* Show message if no media, otherwise show media grid */}
- {mediaItems.length === 0 ? (
+ {filteredMediaItems.length === 0 ? (
<div className="flex flex-col items-center justify-center py-8 text-center h-full">
<div className="w-16 h-16 rounded-full bg-muted/30 flex items-center justify-center mb-4">
<Image className="h-8 w-8 text-muted-foreground" />
@@ -225,7 +267,7 @@ export function MediaPanel() {
) : (
<div className="grid grid-cols-2 gap-2">
{/* Render each media item as a draggable button */}
- {mediaItems.map((item) => (
+ {filteredMediaItems.map((item) => (
<div key={item.id} className="relative group">
<Button
variant="outline"
diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx
index c397dd1a..0d021604 100644
--- a/apps/web/src/components/editor/timeline.tsx
+++ b/apps/web/src/components/editor/timeline.tsx
@@ -1711,7 +1711,7 @@ function TimelineTrackContent({
return (
<div
key={clip.id}
- className={`timeline-clip absolute h-full rounded-sm border transition-all duration-200 ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden group hover:shadow-lg ${isSelected ? "ring-2 ring-blue-500 z-10" : ""}`}
+ className={`timeline-clip absolute h-full border transition-all duration-200 ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden group hover:shadow-lg ${isSelected ? "ring-2 ring-blue-500 z-10" : ""}`}
style={{ width: `${clipWidth}px`, left: `${clipLeft}px` }}
onClick={(e) => {
e.stopPropagation();
diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx
index 8c3ce610..812b0afc 100644
--- a/apps/web/src/components/header.tsx
+++ b/apps/web/src/components/header.tsx
@@ -36,19 +36,17 @@ export function Header() {
const rightContent = (
<nav className="flex items-center">
+ <Link href="/contributors">
+ <Button variant="text" className="text-sm">
+ Contributors
+ </Button>
+ </Link>
<Link href="https://github.com/OpenCut-app/OpenCut" target="_blank">
- <Button
- variant="text"
- className="flex items-center text-sm text-muted-foreground hover:text-foreground"
- >
- <span className="hidden sm:inline">GitHub</span>
- <span className="text-foreground flex items-center">
- {star}+
- <Star className="w-4 h-4 ml-1" />
- </span>
+ <Button variant="text" className="text-sm">
+ GitHub
</Button>
</Link>
- <Link href={session ? "/editor" : "/login"}>
+ <Link href={session ? "/editor" : "/auth/login"}>
<Button size="sm" className="text-sm ml-4">
Start editing
<ArrowRight className="h-4 w-4" />
diff --git a/apps/web/src/components/icons.tsx b/apps/web/src/components/icons.tsx
index 5cc24157..e63b6c79 100644
--- a/apps/web/src/components/icons.tsx
+++ b/apps/web/src/components/icons.tsx
@@ -20,3 +20,20 @@ export function GoogleIcon({ className }: { className?: string }) {
</svg>
);
}
+
+export function GithubIcon({ className }: { className?: string }) {
+ return (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ className={className}
+ viewBox="0 -3.5 256 256"
+ preserveAspectRatio="xMinYMin meet"
+ >
+ <g fill="#161614">
+ <path d="M127.505 0C57.095 0 0 57.085 0 127.505c0 56.336 36.534 104.13 87.196 120.99 6.372 1.18 8.712-2.766 8.712-6.134 0-3.04-.119-13.085-.173-23.739-35.473 7.713-42.958-15.044-42.958-15.044-5.8-14.738-14.157-18.656-14.157-18.656-11.568-7.914.872-7.752.872-7.752 12.804.9 19.546 13.14 19.546 13.14 11.372 19.493 29.828 13.857 37.104 10.6 1.144-8.242 4.449-13.866 8.095-17.05-28.32-3.225-58.092-14.158-58.092-63.014 0-13.92 4.981-25.295 13.138-34.224-1.324-3.212-5.688-16.18 1.235-33.743 0 0 10.707-3.427 35.073 13.07 10.17-2.826 21.078-4.242 31.914-4.29 10.836.048 21.752 1.464 31.942 4.29 24.337-16.497 35.029-13.07 35.029-13.07 6.94 17.563 2.574 30.531 1.25 33.743 8.175 8.929 13.122 20.303 13.122 34.224 0 48.972-29.828 59.756-58.22 62.912 4.573 3.957 8.648 11.717 8.648 23.612 0 17.06-.148 30.791-.148 34.991 0 3.393 2.295 7.369 8.759 6.117 50.634-16.879 87.122-64.656 87.122-120.973C255.009 57.085 197.922 0 127.505 0" />
+
+ <path d="M47.755 181.634c-.28.633-1.278.823-2.185.389-.925-.416-1.445-1.28-1.145-1.916.275-.652 1.273-.834 2.196-.396.927.415 1.455 1.287 1.134 1.923M54.027 187.23c-.608.564-1.797.302-2.604-.589-.834-.889-.99-2.077-.373-2.65.627-.563 1.78-.3 2.616.59.834.899.996 2.08.36 2.65M58.33 194.39c-.782.543-2.06.034-2.849-1.1-.781-1.133-.781-2.493.017-3.038.792-.545 2.05-.055 2.85 1.07.78 1.153.78 2.513-.019 3.069M65.606 202.683c-.699.77-2.187.564-3.277-.488-1.114-1.028-1.425-2.487-.724-3.258.707-.772 2.204-.555 3.302.488 1.107 1.026 1.445 2.496.7 3.258M75.01 205.483c-.307.998-1.741 1.452-3.185 1.028-1.442-.437-2.386-1.607-2.095-2.616.3-1.005 1.74-1.478 3.195-1.024 1.44.435 2.386 1.596 2.086 2.612M85.714 206.67c.036 1.052-1.189 1.924-2.705 1.943-1.525.033-2.758-.818-2.774-1.852 0-1.062 1.197-1.926 2.721-1.951 1.516-.03 2.758.815 2.758 1.86M96.228 206.267c.182 1.026-.872 2.08-2.377 2.36-1.48.27-2.85-.363-3.039-1.38-.184-1.052.89-2.105 2.367-2.378 1.508-.262 2.857.355 3.049 1.398" />
+ </g>
+ </svg>
+ );
+}
diff --git a/apps/web/src/components/landing/hero.tsx b/apps/web/src/components/landing/hero.tsx
index 7b2586c3..2830097d 100644
--- a/apps/web/src/components/landing/hero.tsx
+++ b/apps/web/src/components/landing/hero.tsx
@@ -109,8 +109,8 @@ export function Hero({ signupCount }: HeroProps) {
animate={{ opacity: 1 }}
transition={{ delay: 0.4, duration: 0.8 }}
>
- A simple but powerful video editor that gets the job done. In your
- browser.
+ A simple but powerful video editor that gets the job done. Works on
+ any platform.
</motion.p>
<motion.div