diff --git a/.github/workflows/bun-ci.yml b/.github/workflows/bun-ci.yml new file mode 100644 index 0000000..91bf125 --- /dev/null +++ b/.github/workflows/bun-ci.yml @@ -0,0 +1,50 @@ +name: Bun CI + +concurrency: + group: bun-ci-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [main] + paths-ignore: + - "*.md" + pull_request: + branches: [main] + paths-ignore: + - "*.md" + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 + with: + bun-version: 1.2.2 + + - name: Cache Bun modules + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('apps/web/bun.lock') }} + + - name: Install dependencies + working-directory: apps/web + run: bun install + + - name: Build + working-directory: apps/web + run: bun run build + + - name: Run tests + working-directory: apps/web + run: echo "No tests implemented yet" + continue-on-error: true diff --git a/apps/web/src/components/editor/media-panel.tsx b/apps/web/src/components/editor/media-panel.tsx index 0d6a1f1..95cfa0a 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(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() {
{/* Button to add/upload media */} - +
+ {/* Search and filter controls */} + + setSearchQuery(e.target.value)} + /> + + {/* Add media button */} + +
{/* Show message if no media, otherwise show media grid */} - {mediaItems.length === 0 ? ( + {filteredMediaItems.length === 0 ? (
@@ -225,7 +267,7 @@ export function MediaPanel() { ) : (
{/* Render each media item as a draggable button */} - {mediaItems.map((item) => ( + {filteredMediaItems.map((item) => (