From 5a76196221b079ee2a6fb6de2a66ee452d77acbb Mon Sep 17 00:00:00 2001 From: enkeii64 Date: Wed, 16 Jul 2025 22:23:21 +1000 Subject: [PATCH 01/13] feat: add search and sort functionality to projects page --- apps/web/src/app/projects/page.tsx | 122 +++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 25 deletions(-) diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index 499b999a..4ebecafb 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -5,6 +5,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; +import { Input } from "@/components/ui/input"; import { ChevronLeft, Plus, @@ -28,6 +29,13 @@ import { useProjectStore } from "@/stores/project-store"; import { useRouter } from "next/navigation"; import { DeleteProjectDialog } from "@/components/delete-project-dialog"; import { RenameProjectDialog } from "@/components/rename-project-dialog"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; export default function ProjectsPage() { const { @@ -43,6 +51,8 @@ export default function ProjectsPage() { new Set() ); const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const [sortOption, setSortOption] = useState("createdAt-desc"); const handleCreateProject = async () => { const projectId = await createNewProject("New Project"); @@ -62,7 +72,7 @@ export default function ProjectsPage() { const handleSelectAll = (checked: boolean) => { if (checked) { - setSelectedProjects(new Set(savedProjects.map((p) => p.id))); + setSelectedProjects(new Set(filteredProjects.map((p) => p.id))); } else { setSelectedProjects(new Set()); } @@ -82,10 +92,34 @@ export default function ProjectsPage() { setIsBulkDeleteDialogOpen(false); }; + const filteredProjects = savedProjects.filter((project) => + project.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + const sortedProjects = [...filteredProjects].sort((a, b) => { + const [key, order] = sortOption.split("-"); + const aValue = a[key as keyof TProject]; + const bValue = b[key as keyof TProject]; + + if (aValue === undefined || bValue === undefined) return 0; + + if (order === "asc") { + if (aValue < bValue) return -1; + if (aValue > bValue) return 1; + return 0; + } else { + if (aValue > bValue) return -1; + if (aValue < bValue) return 1; + return 0; + } + }); + const allSelected = - savedProjects.length > 0 && selectedProjects.size === savedProjects.length; + sortedProjects.length > 0 && + selectedProjects.size === sortedProjects.length; const someSelected = - selectedProjects.size > 0 && selectedProjects.size < savedProjects.length; + selectedProjects.size > 0 && + selectedProjects.size < sortedProjects.length; return (
@@ -172,25 +206,40 @@ export default function ProjectsPage() {
- {isSelectionMode && savedProjects.length > 0 && ( -
handleSelectAll(!allSelected)} - > +
+
+ setSearchQuery(e.target.value)} + /> +
+ +
+ + {isSelectionMode && sortedProjects.length > 0 && ( +
e.stopPropagation()} + checked={someSelected ? "indeterminate" : allSelected} + onCheckedChange={(value) => handleSelectAll(!!value)} /> -
- - {allSelected ? "Deselect All" : "Select All"} - - - ({selectedProjects.size} of {savedProjects.length} selected) - -
+ + {allSelected ? "Deselect All" : "Select All"} + + + ({selectedProjects.size} of {sortedProjects.length} selected) +
)} @@ -199,17 +248,18 @@ export default function ProjectsPage() {
- ) : savedProjects.length === 0 ? ( + ) : sortedProjects.length === 0 ? ( ) : (
- {savedProjects.map((project) => ( + {sortedProjects.map((project) => ( ))}
@@ -230,6 +280,7 @@ interface ProjectCardProps { isSelectionMode?: boolean; isSelected?: boolean; onSelect?: (projectId: string, checked: boolean) => void; + searchQuery?: string; } function ProjectCard({ @@ -237,6 +288,7 @@ function ProjectCard({ isSelectionMode = false, isSelected = false, onSelect, + searchQuery, }: ProjectCardProps) { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); @@ -292,7 +344,7 @@ function ProjectCard({ {/* Selection checkbox */} {isSelectionMode && (
-
+
@@ -322,10 +374,10 @@ function ProjectCard({
- +

- {project.name} +

{!isSelectionMode && (

- {project.name} +

{text}; + } + const parts = text.split(new RegExp(`(${query})`, "gi")); + return ( + + {parts.map((part, i) => + part.toLowerCase() === query.toLowerCase() ? ( + + {part} + + ) : ( + part + ) + )} + + ); +} + function CreateButton({ onClick }: { onClick?: () => void }) { return ( +
+ ); +} From f83f647299fe4e650052371e0c5efd1aa9801be9 Mon Sep 17 00:00:00 2001 From: enkeii64 Date: Wed, 16 Jul 2025 22:43:01 +1000 Subject: [PATCH 03/13] refactor: improve type safety and security in projects page --- apps/web/src/app/projects/page.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index 16722815..e68c70c4 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -99,8 +99,14 @@ export default function ProjectsPage() { const sortedProjects = [...filteredProjects].sort((a, b) => { const [key, order] = sortOption.split("-"); - const aValue = a[key as keyof TProject]; - const bValue = b[key as keyof TProject]; + + if (key !== "createdAt" && key !== "name") { + console.warn(`Invalid sort key: ${key}`); + return 0; + } + + const aValue = a[key]; + const bValue = b[key]; if (aValue === undefined || bValue === undefined) return 0; @@ -578,10 +584,12 @@ function ProjectCard({ } function Highlight({ text, query }: { text: string; query?: string }) { - if (!query) { + if (!query || query.trim() === "") { return {text}; } - const parts = text.split(new RegExp(`(${query})`, "gi")); + + const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const parts = text.split(new RegExp(`(${escapedQuery})`, "gi")); return ( {parts.map((part, i) => From e0a5d4860a8b5c6f4be70414a83613b6509b8a78 Mon Sep 17 00:00:00 2001 From: enkeii64 Date: Thu, 17 Jul 2025 00:06:22 +1000 Subject: [PATCH 04/13] fix(ui): add tooltipText prop to PlusButton --- apps/web/src/components/ui/draggable-item.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/ui/draggable-item.tsx b/apps/web/src/components/ui/draggable-item.tsx index c1f81e16..6e1e4990 100644 --- a/apps/web/src/components/ui/draggable-item.tsx +++ b/apps/web/src/components/ui/draggable-item.tsx @@ -149,7 +149,15 @@ export function DraggableMediaItem({ ); } -function PlusButton({ className, onClick }: { className?: string; onClick?: () => void }) { +function PlusButton({ + className, + onClick, + tooltipText, +}: { + className?: string; + onClick?: () => void; + tooltipText?: string; +}) { return ( From 7cbbbe1630db0e4edeeb11a8af42b4012b766379 Mon Sep 17 00:00:00 2001 From: enkeii64 Date: Thu, 17 Jul 2025 00:07:39 +1000 Subject: [PATCH 05/13] fix: resolve merge conflict in draggable-item.tsx --- apps/web/src/components/ui/draggable-item.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/ui/draggable-item.tsx b/apps/web/src/components/ui/draggable-item.tsx index 6e1e4990..2af7932a 100644 --- a/apps/web/src/components/ui/draggable-item.tsx +++ b/apps/web/src/components/ui/draggable-item.tsx @@ -158,7 +158,7 @@ function PlusButton({ onClick?: () => void; tooltipText?: string; }) { - return ( + const button = ( ); + + return button; } From 9d5707d5fdd0e6a47622df6d9dda6db2e88f266f Mon Sep 17 00:00:00 2001 From: enkeii64 Date: Thu, 17 Jul 2025 00:12:41 +1000 Subject: [PATCH 06/13] fix: resolve merge conflict in draggable-item.tsx --- apps/web/src/components/ui/draggable-item.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/web/src/components/ui/draggable-item.tsx b/apps/web/src/components/ui/draggable-item.tsx index 2af7932a..3245c7ba 100644 --- a/apps/web/src/components/ui/draggable-item.tsx +++ b/apps/web/src/components/ui/draggable-item.tsx @@ -2,6 +2,11 @@ import { AspectRatio } from "@/components/ui/aspect-ratio"; import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { ReactNode, useState, useRef, useEffect } from "react"; import { createPortal } from "react-dom"; import { Plus } from "lucide-react"; @@ -173,5 +178,16 @@ function PlusButton({ ); + if (tooltipText) { + return ( + + {button} + +

{tooltipText}

+
+
+ ); + } + return button; } From b18a48b58675c0b9b7542ee045215b235517bed2 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Thu, 17 Jul 2025 13:45:12 +0200 Subject: [PATCH 07/13] refactor: move sorting logic to project store --- apps/web/src/app/projects/page.tsx | 34 +++-------------------- apps/web/src/stores/project-store.ts | 41 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index e68c70c4..04270aee 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -45,6 +45,7 @@ export default function ProjectsPage() { isLoading, isInitialized, deleteProject, + getFilteredAndSortedProjects, } = useProjectStore(); const router = useRouter(); const [isSelectionMode, setIsSelectionMode] = useState(false); @@ -73,7 +74,7 @@ export default function ProjectsPage() { const handleSelectAll = (checked: boolean) => { if (checked) { - setSelectedProjects(new Set(filteredProjects.map((p) => p.id))); + setSelectedProjects(new Set(sortedProjects.map((p) => p.id))); } else { setSelectedProjects(new Set()); } @@ -93,40 +94,13 @@ export default function ProjectsPage() { setIsBulkDeleteDialogOpen(false); }; - const filteredProjects = savedProjects.filter((project) => - project.name.toLowerCase().includes(searchQuery.toLowerCase()) - ); - - const sortedProjects = [...filteredProjects].sort((a, b) => { - const [key, order] = sortOption.split("-"); - - if (key !== "createdAt" && key !== "name") { - console.warn(`Invalid sort key: ${key}`); - return 0; - } - - const aValue = a[key]; - const bValue = b[key]; - - if (aValue === undefined || bValue === undefined) return 0; - - if (order === "asc") { - if (aValue < bValue) return -1; - if (aValue > bValue) return 1; - return 0; - } else { - if (aValue > bValue) return -1; - if (aValue < bValue) return 1; - return 0; - } - }); + const sortedProjects = getFilteredAndSortedProjects(searchQuery, sortOption); const allSelected = sortedProjects.length > 0 && selectedProjects.size === sortedProjects.length; const someSelected = - selectedProjects.size > 0 && - selectedProjects.size < sortedProjects.length; + selectedProjects.size > 0 && selectedProjects.size < sortedProjects.length; return (
diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index efb9cc71..a8833982 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -27,6 +27,11 @@ interface ProjectStore { options?: { backgroundColor?: string; blurIntensity?: number } ) => Promise; updateProjectFps: (fps: number) => Promise; + + getFilteredAndSortedProjects: ( + searchQuery: string, + sortOption: string + ) => TProject[]; } export const useProjectStore = create((set, get) => ({ @@ -317,4 +322,40 @@ export const useProjectStore = create((set, get) => ({ }); } }, + + getFilteredAndSortedProjects: (searchQuery: string, sortOption: string) => { + const { savedProjects } = get(); + + // Filter projects by search query + const filteredProjects = savedProjects.filter((project) => + project.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + // Sort filtered projects + const sortedProjects = [...filteredProjects].sort((a, b) => { + const [key, order] = sortOption.split("-"); + + if (key !== "createdAt" && key !== "name") { + console.warn(`Invalid sort key: ${key}`); + return 0; + } + + const aValue = a[key]; + const bValue = b[key]; + + if (aValue === undefined || bValue === undefined) return 0; + + if (order === "asc") { + if (aValue < bValue) return -1; + if (aValue > bValue) return 1; + return 0; + } else { + if (aValue > bValue) return -1; + if (aValue < bValue) return 1; + return 0; + } + }); + + return sortedProjects; + }, })); From f94b1a86d656ab7cef4e12daf0fb07039f958d4a Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Thu, 17 Jul 2025 14:17:47 +0200 Subject: [PATCH 08/13] fuck yan --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4869b9bd..38711b81 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,6 @@ # debug /apps/web/npm-debug.log* -/apps/web/yarn-debug.log* -/apps/web/yarn-error.log* # env files (can opt-in for committing if needed) /apps/web/.env* @@ -18,7 +16,6 @@ # typescript /apps/web/next-env.d.ts -/apps/web/yarn.lock # asdf version management .tool-versions From f21b704323b8905f8a64c08feef27820898efc3c Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Wed, 16 Jul 2025 04:34:25 +0600 Subject: [PATCH 09/13] docs: update README for clarity on setup instructions --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ad9a6d38..2f613c89 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,15 @@ Before you begin, ensure you have the following installed on your system: - [Bun](https://bun.sh/docs/installation) -- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) - [Node.js](https://nodejs.org/en/) (for `npm` alternative) -### Setup +**Additional requirements for full-stack development:** + +- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) (only needed for database/auth features) + +### Frontend-only Development (Recommended for UI work) + +If you're working on the UI/frontend and don't need authentication or database features: 1. Fork the repository 2. Clone your fork locally @@ -51,15 +56,13 @@ Before you begin, ensure you have the following installed on your system: 4. Install dependencies: `bun install` 5. Start the development server: `bun dev` -## Development Setup +The application will be available at [http://localhost:3000](http://localhost:3000). -### Prerequisites +**Note:** This setup runs without Docker and is perfect for frontend development, UI improvements, and most feature work. -- Node.js 18+ -- Bun (latest version) -- Docker (for local database) +### Full Development Setup (Database + Auth) -### Local Development +Only follow this if you need to work with authentication, user accounts, or database features. 1. Start the database and Redis services: From 6691e74db916f0869496853f1aa0cec8498aae7f Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Wed, 16 Jul 2025 04:34:31 +0600 Subject: [PATCH 10/13] docs: update contributing guidelines for clarity on setup instructions --- .github/CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 77d7a6b6..4db3ad02 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -18,6 +18,7 @@ Thank you for your interest in contributing to OpenCut! This document provides g ## What to Focus On **🎯 Good Areas to Contribute:** + - Timeline functionality and UI improvements - Project management features - Performance optimizations @@ -26,6 +27,7 @@ Thank you for your interest in contributing to OpenCut! This document provides g - Documentation and testing **⚠️ Areas to Avoid:** + - Preview panel enhancements (text fonts, stickers, effects) - Export functionality improvements - Preview rendering optimizations From f1fed515029ebdc7e29a976fd84b4e84cb0b5f03 Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Wed, 16 Jul 2025 04:41:31 +0600 Subject: [PATCH 11/13] docs: update setup instructions to include environment file copying steps --- .github/CONTRIBUTING.md | 18 ++++++++++++++++-- README.md | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4db3ad02..cf46f6a5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -7,8 +7,22 @@ Thank you for your interest in contributing to OpenCut! This document provides g 1. Fork the repository 2. Clone your fork locally 3. Navigate to the web app directory: `cd apps/web` -4. Install dependencies: `bun install` -5. Start the development server: `bun run dev` +4. Copy `.env.example` to `.env.local`: + + ```bash + # Unix/Linux/Mac + cp .env.example .env.local + + # Windows Command Prompt + copy .env.example .env.local + + # Windows PowerShell + Copy-Item .env.example .env.local + ``` + +5. Install dependencies: `bun install` + +6. Start the development server: `bun run dev` > **Note:** If you see an error like `Unsupported URL Type "workspace:*"` when running `npm install`, you have two options: > diff --git a/README.md b/README.md index 2f613c89..c352bd91 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,22 @@ If you're working on the UI/frontend and don't need authentication or database f 1. Fork the repository 2. Clone your fork locally 3. Navigate to the web app directory: `cd apps/web` -4. Install dependencies: `bun install` -5. Start the development server: `bun dev` +4. Copy `.env.example` to `.env.local`: + + ```bash + # Unix/Linux/Mac + cp .env.example .env.local + + # Windows Command Prompt + copy .env.example .env.local + + # Windows PowerShell + Copy-Item .env.example .env.local + ``` + +5. Install dependencies: `bun install` + +6. Start the development server: `bun dev` The application will be available at [http://localhost:3000](http://localhost:3000). From 4d028487e695baf9693ca74916193dd66c49524e Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Thu, 17 Jul 2025 20:02:46 +0600 Subject: [PATCH 12/13] docs: clarify Docker setup note in contributing guidelines --- .github/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cf46f6a5..d6ce85ab 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -21,7 +21,6 @@ Thank you for your interest in contributing to OpenCut! This document provides g ``` 5. Install dependencies: `bun install` - 6. Start the development server: `bun run dev` > **Note:** If you see an error like `Unsupported URL Type "workspace:*"` when running `npm install`, you have two options: @@ -60,6 +59,8 @@ If you're unsure whether your idea falls into the preview category, feel free to - Bun (latest version) - Docker (for local database) +> **Note:** Docker is optional, but it's essential for running the local database and Redis services. If you're planning to contribute to frontend features, you can skip the Docker setup. If you have followed the steps above in [Getting Started](#getting-started), you're all set to go! + ### Local Development 1. Start the database and Redis services: From f3307a64d1b17978cc0e129dbec645adaf34e3d3 Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Thu, 17 Jul 2025 20:12:34 +0600 Subject: [PATCH 13/13] docs: reorganize prerequisites and setup instructions for clarity --- .github/CONTRIBUTING.md | 19 +++++++++++-------- README.md | 21 +++++++-------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d6ce85ab..75c4120b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,6 +4,17 @@ Thank you for your interest in contributing to OpenCut! This document provides g ## Getting Started +### Prerequisites + +- [Node.js](https://nodejs.org/en/) (v18 or later) +- [Bun](https://bun.sh/docs/installation) + (for `npm` alternative) +- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) + +> **Note:** Docker is optional, but it's essential for running the local database and Redis services. If you're planning to contribute to frontend features, you can skip the Docker setup. If you have followed the steps below in [Setup](#setup), you're all set to go! + +### Setup + 1. Fork the repository 2. Clone your fork locally 3. Navigate to the web app directory: `cd apps/web` @@ -53,14 +64,6 @@ If you're unsure whether your idea falls into the preview category, feel free to ## Development Setup -### Prerequisites - -- Node.js 18+ -- Bun (latest version) -- Docker (for local database) - -> **Note:** Docker is optional, but it's essential for running the local database and Redis services. If you're planning to contribute to frontend features, you can skip the Docker setup. If you have followed the steps above in [Getting Started](#getting-started), you're all set to go! - ### Local Development 1. Start the database and Redis services: diff --git a/README.md b/README.md index c352bd91..1378b15a 100644 --- a/README.md +++ b/README.md @@ -39,16 +39,14 @@ Before you begin, ensure you have the following installed on your system: +- [Node.js](https://nodejs.org/en/) (v18 or later) - [Bun](https://bun.sh/docs/installation) -- [Node.js](https://nodejs.org/en/) (for `npm` alternative) + (for `npm` alternative) +- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) -**Additional requirements for full-stack development:** +> **Note:** Docker is optional, but it's essential for running the local database and Redis services. If you're planning to run the frontend or want to contribute to frontend features, you can skip the Docker setup. If you have followed the steps below in [Setup](#setup), you're all set to go! -- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) (only needed for database/auth features) - -### Frontend-only Development (Recommended for UI work) - -If you're working on the UI/frontend and don't need authentication or database features: +### Setup 1. Fork the repository 2. Clone your fork locally @@ -67,16 +65,11 @@ If you're working on the UI/frontend and don't need authentication or database f ``` 5. Install dependencies: `bun install` - 6. Start the development server: `bun dev` -The application will be available at [http://localhost:3000](http://localhost:3000). +## Development Setup -**Note:** This setup runs without Docker and is perfect for frontend development, UI improvements, and most feature work. - -### Full Development Setup (Database + Auth) - -Only follow this if you need to work with authentication, user accounts, or database features. +### Local Development 1. Start the database and Redis services: