mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
Merge branch 'staging'
This commit is contained in:
@@ -36,6 +36,7 @@ Add screenshots to help explain your changes.
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have added screenshots if ui has been changed
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
"zustand": "^5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pg": "^8.15.4",
|
||||
"@types/bun": "latest",
|
||||
"@types/pg": "^8.15.4",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"cross-env": "^7.0.3",
|
||||
@@ -70,6 +70,6 @@
|
||||
"prettier": "^3.4.2",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,6 @@ export default function ProjectsPage() {
|
||||
isSelectionMode={isSelectionMode}
|
||||
isSelected={selectedProjects.has(project.id)}
|
||||
onSelect={handleSelectProject}
|
||||
searchQuery={searchQuery}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -266,7 +265,6 @@ interface ProjectCardProps {
|
||||
isSelectionMode?: boolean;
|
||||
isSelected?: boolean;
|
||||
onSelect?: (projectId: string, checked: boolean) => void;
|
||||
searchQuery?: string;
|
||||
}
|
||||
|
||||
function ProjectCard({
|
||||
@@ -274,7 +272,6 @@ function ProjectCard({
|
||||
isSelectionMode = false,
|
||||
isSelected = false,
|
||||
onSelect,
|
||||
searchQuery,
|
||||
}: ProjectCardProps) {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
@@ -363,7 +360,7 @@ function ProjectCard({
|
||||
<CardContent className="px-0 pt-5 flex flex-col gap-1">
|
||||
<div className="flex items-start justify-between">
|
||||
<h3 className="font-medium text-sm leading-snug group-hover:text-foreground/90 transition-colors line-clamp-2">
|
||||
<Highlight text={project.name} query={searchQuery} />
|
||||
{project.name}
|
||||
</h3>
|
||||
{!isSelectionMode && (
|
||||
<DropdownMenu
|
||||
@@ -470,7 +467,7 @@ function ProjectCard({
|
||||
<CardContent className="px-0 pt-5 flex flex-col gap-1">
|
||||
<div className="flex items-start justify-between">
|
||||
<h3 className="font-medium text-sm leading-snug group-hover:text-foreground/90 transition-colors line-clamp-2">
|
||||
<Highlight text={project.name} query={searchQuery} />
|
||||
{project.name}
|
||||
</h3>
|
||||
<DropdownMenu
|
||||
open={isDropdownOpen}
|
||||
@@ -546,6 +543,7 @@ function ProjectCard({
|
||||
isOpen={isDeleteDialogOpen}
|
||||
onOpenChange={setIsDeleteDialogOpen}
|
||||
onConfirm={handleDeleteProject}
|
||||
projectName={project.name}
|
||||
/>
|
||||
<RenameProjectDialog
|
||||
isOpen={isRenameDialogOpen}
|
||||
@@ -557,28 +555,6 @@ function ProjectCard({
|
||||
);
|
||||
}
|
||||
|
||||
function Highlight({ text, query }: { text: string; query?: string }) {
|
||||
if (!query || query.trim() === "") {
|
||||
return <span>{text}</span>;
|
||||
}
|
||||
|
||||
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const parts = text.split(new RegExp(`(${escapedQuery})`, "gi"));
|
||||
return (
|
||||
<span>
|
||||
{parts.map((part, i) =>
|
||||
part.toLowerCase() === query.toLowerCase() ? (
|
||||
<mark key={i} className="bg-yellow-200 text-black">
|
||||
{part}
|
||||
</mark>
|
||||
) : (
|
||||
part
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function CreateButton({ onClick }: { onClick?: () => void }) {
|
||||
return (
|
||||
<Button className="flex" onClick={onClick}>
|
||||
|
||||
@@ -7,15 +7,18 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function DeleteProjectDialog({
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
onConfirm,
|
||||
projectName,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onConfirm: () => void;
|
||||
projectName?: string;
|
||||
}) {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||
@@ -26,7 +29,19 @@ export function DeleteProjectDialog({
|
||||
}}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete Project</DialogTitle>
|
||||
<DialogTitle>
|
||||
{projectName ? (
|
||||
<>
|
||||
{"Delete '"}
|
||||
<span className="inline-block max-w-[300px] truncate align-bottom">
|
||||
{projectName}
|
||||
</span>
|
||||
{"'?"}
|
||||
</>
|
||||
) : (
|
||||
"Delete Project?"
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Are you sure you want to delete this project? This action cannot be
|
||||
undone.
|
||||
|
||||
@@ -69,7 +69,7 @@ export function TabBar() {
|
||||
/>
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="h-12 bg-panel-accent px-3 flex justify-start items-center gap-5 overflow-x-auto scrollbar-x-hidden relative"
|
||||
className="h-12 bg-panel-accent px-3 flex justify-start items-center gap-5 overflow-x-auto scrollbar-x-hidden relative w-full"
|
||||
>
|
||||
{(Object.keys(tabs) as Tab[]).map((tabKey) => {
|
||||
const tab = tabs[tabKey];
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { env } from "./env";
|
||||
import { getSessionCookie } from "better-auth/cookies";
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
const protectedPaths = ["/editor", "/projects"];
|
||||
const sessionCookie = getSessionCookie(request);
|
||||
|
||||
const canAccessProtectedPaths = (request: NextRequest) => {
|
||||
if (env.NODE_ENV === "development") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sessionCookie) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Handle fuckcapcut.com domain redirect
|
||||
if (request.headers.get("host") === "fuckcapcut.com") {
|
||||
@@ -12,7 +25,7 @@ export async function middleware(request: NextRequest) {
|
||||
|
||||
const path = request.nextUrl.pathname;
|
||||
|
||||
if (protectedPaths.includes(path) && env.NODE_ENV === "production") {
|
||||
if (protectedPaths.includes(path) && !canAccessProtectedPaths(request)) {
|
||||
const homeUrl = new URL("/", request.url);
|
||||
homeUrl.searchParams.set("redirect", request.url);
|
||||
return NextResponse.redirect(homeUrl);
|
||||
|
||||
@@ -213,7 +213,29 @@ export const useMediaStore = create<MediaStore>((set, get) => ({
|
||||
|
||||
try {
|
||||
const mediaItems = await storageService.loadAllMediaItems(projectId);
|
||||
set({ mediaItems });
|
||||
|
||||
// Regenerate thumbnails for video items
|
||||
const updatedMediaItems = await Promise.all(
|
||||
mediaItems.map(async (item) => {
|
||||
if (item.type === "video" && item.file) {
|
||||
try {
|
||||
const { thumbnailUrl, width, height } = await generateVideoThumbnail(item.file);
|
||||
return {
|
||||
...item,
|
||||
thumbnailUrl,
|
||||
width: width || item.width,
|
||||
height: height || item.height
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Failed to regenerate thumbnail for video ${item.id}:`, error);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
})
|
||||
);
|
||||
|
||||
set({ mediaItems: updatedMediaItems });
|
||||
} catch (error) {
|
||||
console.error("Failed to load media items:", error);
|
||||
} finally {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"turbo": "^2.5.4",
|
||||
"typescript": "5.8.3",
|
||||
},
|
||||
},
|
||||
"apps/web": {
|
||||
@@ -71,7 +72,7 @@
|
||||
"prettier": "^3.4.2",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "^5",
|
||||
"typescript": "^5.8.3",
|
||||
},
|
||||
},
|
||||
"packages/auth": {
|
||||
@@ -1022,19 +1023,19 @@
|
||||
|
||||
"tsx": ["tsx@4.20.3", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ=="],
|
||||
|
||||
"turbo": ["turbo@2.5.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.4", "turbo-darwin-arm64": "2.5.4", "turbo-linux-64": "2.5.4", "turbo-linux-arm64": "2.5.4", "turbo-windows-64": "2.5.4", "turbo-windows-arm64": "2.5.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA=="],
|
||||
"turbo": ["turbo@2.5.5", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.5", "turbo-darwin-arm64": "2.5.5", "turbo-linux-64": "2.5.5", "turbo-linux-arm64": "2.5.5", "turbo-windows-64": "2.5.5", "turbo-windows-arm64": "2.5.5" }, "bin": { "turbo": "bin/turbo" } }, "sha512-eZ7wI6KjtT1eBqCnh2JPXWNUAxtoxxfi6VdBdZFvil0ychCOTxbm7YLRBi1JSt7U3c+u3CLxpoPxLdvr/Npr3A=="],
|
||||
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.5.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ=="],
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.5.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-RYnTz49u4F5tDD2SUwwtlynABNBAfbyT2uU/brJcyh5k6lDLyNfYKdKmqd3K2ls4AaiALWrFKVSBsiVwhdFNzQ=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2+Nx6LAyuXw2MdXb7pxqle3MYignLvS7OwtsP9SgtSBaMlnNlxl9BovzqdYAgkUW3AsYiQMJ/wBRb7d+xemM5A=="],
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Tk+ZeSNdBobZiMw9aFypQt0DlLsWSFWu1ymqsAdJLuPoAH05qCfYtRxE1pJuYHcJB5pqI+/HOxtJoQ40726Btw=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-5May2kjWbc8w4XxswGAl74GZ5eM4Gr6IiroqdLhXeXyfvWEdm2mFYCSWOzz0/z5cAgqyGidF1jt1qzUR8hTmOA=="],
|
||||
"turbo-linux-64": ["turbo-linux-64@2.5.5", "", { "os": "linux", "cpu": "x64" }, "sha512-2/XvMGykD7VgsvWesZZYIIVXMlgBcQy+ZAryjugoTcvJv8TZzSU/B1nShcA7IAjZ0q7OsZ45uP2cOb8EgKT30w=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-/2yqFaS3TbfxV3P5yG2JUI79P7OUQKOUvAnx4MV9Bdz6jqHsHwc9WZPpO4QseQm+NvmgY6ICORnoVPODxGUiJg=="],
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.5.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-DW+8CjCjybu0d7TFm9dovTTVg1VRnlkZ1rceO4zqsaLrit3DgHnN4to4uwyuf9s2V/BwS3IYcRy+HG9BL596Iw=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.5.4", "", { "os": "win32", "cpu": "x64" }, "sha512-EQUO4SmaCDhO6zYohxIjJpOKRN3wlfU7jMAj3CgcyTPvQR/UFLEKAYHqJOnJtymbQmiiM/ihX6c6W6Uq0yC7mA=="],
|
||||
"turbo-windows-64": ["turbo-windows-64@2.5.5", "", { "os": "win32", "cpu": "x64" }, "sha512-q5p1BOy8ChtSZfULuF1BhFMYIx6bevXu4fJ+TE/hyNfyHJIfjl90Z6jWdqAlyaFLmn99X/uw+7d6T/Y/dr5JwQ=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.5.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-oQ8RrK1VS8lrxkLriotFq+PiF7iiGgkZtfLKF4DDKsmdbPo0O9R2mQxm7jHLuXraRCuIQDWMIw6dpcr7Iykf4A=="],
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.5.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-AXbF1KmpHUq3PKQwddMGoKMYhHsy5t1YBQO8HZ04HLMR0rWv9adYlQ8kaeQJTko1Ay1anOBFTqaxfVOOsu7+1Q=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
|
||||
+2
-1
@@ -2,7 +2,8 @@
|
||||
"name": "opencut",
|
||||
"packageManager": "bun@1.2.18",
|
||||
"devDependencies": {
|
||||
"turbo": "^2.5.4"
|
||||
"turbo": "^2.5.4",
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
|
||||
Reference in New Issue
Block a user