From 9bb19712e5c213262a18051bf0c5d220b2de245e Mon Sep 17 00:00:00 2001 From: yassinehaimouch Date: Tue, 24 Jun 2025 23:27:00 +0100 Subject: add: opengraph image --- apps/web/src/app/opengraph-image.jpg | Bin 0 -> 55439 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/web/src/app/opengraph-image.jpg (limited to 'apps/web/src') diff --git a/apps/web/src/app/opengraph-image.jpg b/apps/web/src/app/opengraph-image.jpg new file mode 100644 index 00000000..d45c0854 Binary files /dev/null and b/apps/web/src/app/opengraph-image.jpg differ -- cgit v1.3.1 From 274d03185b346fd715274018c6804d0229f2f74e Mon Sep 17 00:00:00 2001 From: Harshit Shukla <120409631+Harshitshukla0208@users.noreply.github.com> Date: Wed, 25 Jun 2025 04:54:56 +0530 Subject: fix(issue): Address issue #67 - [BUG] buttons jitter left and right --- apps/web/src/components/editor/timeline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apps/web/src') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 12907738..cb375034 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -221,7 +221,7 @@ export function Timeline() { const clipLeft = clip.startTime * 50 * zoomLevel; const clipTop = trackIdx * 60; const clipBottom = clipTop + 60; - const clipRight = clipLeft + clipWidth; + const clipRight = clipLeft + 60; // Set a fixed width for time display if ( bx1 < clipRight && bx2 > clipLeft && -- cgit v1.3.1 From 22ae5d0e374d9ccf7d5f5e3dce4f3df2feb6ab67 Mon Sep 17 00:00:00 2001 From: yassinehaimouch Date: Wed, 25 Jun 2025 01:16:37 +0100 Subject: fix: metadata for og image --- apps/web/public/opengraph-image.jpg | Bin 0 -> 55439 bytes apps/web/src/app/layout.tsx | 29 +++++++++++++++++++++++++++++ apps/web/src/app/opengraph-image.jpg | Bin 55439 -> 0 bytes 3 files changed, 29 insertions(+) create mode 100644 apps/web/public/opengraph-image.jpg delete mode 100644 apps/web/src/app/opengraph-image.jpg (limited to 'apps/web/src') diff --git a/apps/web/public/opengraph-image.jpg b/apps/web/public/opengraph-image.jpg new file mode 100644 index 00000000..d45c0854 Binary files /dev/null and b/apps/web/public/opengraph-image.jpg differ diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 1cd4b3b9..5d33100d 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -16,6 +16,35 @@ export const metadata: Metadata = { title: "OpenCut", description: "A simple but powerful video editor that gets the job done. In your browser.", + openGraph: { + title: "OpenCut", + description: + "A simple but powerful video editor that gets the job done. In your browser.", + url: "https://opencut.app", + siteName: "OpenCut", + locale: "en_US", + type: "website", + images: [ + { + url: "https://opencut.app/opengraph-image.jpg", + width: 1200, + height: 630, + alt: "OpenCut", + }, + ], + }, + twitter: { + card: "summary_large_image", + title: "OpenCut", + description: + "A simple but powerful video editor that gets the job done. In your browser.", + creator: "@opencutapp", + images: ["/opengraph-image.jpg"], + }, + robots: { + index: true, + follow: true, + }, }; export default function RootLayout({ diff --git a/apps/web/src/app/opengraph-image.jpg b/apps/web/src/app/opengraph-image.jpg deleted file mode 100644 index d45c0854..00000000 Binary files a/apps/web/src/app/opengraph-image.jpg and /dev/null differ -- cgit v1.3.1 From 84d206c01e1ae02d52e30e82982971436e98828c Mon Sep 17 00:00:00 2001 From: creotove Date: Wed, 25 Jun 2025 08:36:27 +0530 Subject: Fix: [BUG] buttons jitter left and right #67 --- apps/web/src/components/editor/timeline.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'apps/web/src') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 0d021604..81b99747 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -602,9 +602,10 @@ export function Timeline() {
{/* Time Display */} -
- {Math.floor(currentTime * 10) / 10}s /{" "} - {Math.floor(duration * 10) / 10}s +
+ {currentTime.toFixed(1)}s / {duration.toFixed(1)}s
-- cgit v1.3.1 From 7de09adce696442b0ba536bd3895f335246c744b Mon Sep 17 00:00:00 2001 From: creotove Date: Wed, 25 Jun 2025 08:51:17 +0530 Subject: Fix: unwanted seperator when there is a clip on track. --- apps/web/src/components/editor/timeline.tsx | 53 +++++++++++++++-------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'apps/web/src') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 81b99747..6c39c893 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -603,38 +603,39 @@ export function Timeline() { {/* Time Display */}
{currentTime.toFixed(1)}s / {duration.toFixed(1)}s
-
- {/* Test Clip Button - for debugging */} {tracks.length === 0 && ( - - - - - Add a test clip to try playback - + <> +
+ + + + + Add a test clip to try playback + + )}
-- cgit v1.3.1 From 547f566fc20466df0eac5a8ef78642d21ee5c0a9 Mon Sep 17 00:00:00 2001 From: ga1az Date: Tue, 24 Jun 2025 23:44:06 -0400 Subject: refactor(docker): update Dockerfile and docker-compose for improved build process and add health check API --- apps/web/Dockerfile | 42 ++++++++++++++++++++++++------------ apps/web/next.config.ts | 1 + apps/web/src/app/api/health/route.ts | 5 +++++ docker-compose.yaml | 2 +- 4 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 apps/web/src/app/api/health/route.ts (limited to 'apps/web/src') diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index ca1f0987..49820e78 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,16 +1,27 @@ -FROM oven/bun:latest AS base +FROM oven/bun:alpine AS base -# Install dependencies -FROM base AS deps -WORKDIR /app -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile - -# Build the application FROM base AS builder + WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . + +COPY package.json package.json +COPY bun.lock bun.lock +COPY turbo.json turbo.json + +COPY apps/web/package.json apps/web/package.json +COPY packages/db/package.json packages/db/package.json +COPY packages/auth/package.json packages/auth/package.json + +RUN bun install + +COPY apps/web/ apps/web/ +COPY packages/db/ packages/db/ +COPY packages/auth/ packages/auth/ + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +WORKDIR /app/apps/web RUN bun run build # Production image @@ -18,13 +29,16 @@ FROM base AS runner WORKDIR /app ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder /app/public ./public -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static + +RUN chown nextjs:nodejs apps USER nextjs @@ -33,4 +47,4 @@ EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" -CMD ["bun", "server.js"] \ No newline at end of file +CMD ["bun", "apps/web/server.js"] \ No newline at end of file diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index de7fefe2..319ca134 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -6,6 +6,7 @@ const nextConfig: NextConfig = { }, reactStrictMode: true, productionBrowserSourceMaps: true, + output: "standalone", }; export default nextConfig; diff --git a/apps/web/src/app/api/health/route.ts b/apps/web/src/app/api/health/route.ts new file mode 100644 index 00000000..ecb9ce04 --- /dev/null +++ b/apps/web/src/app/api/health/route.ts @@ -0,0 +1,5 @@ +import { NextRequest } from "next/server"; + +export async function GET(request: NextRequest) { + return new Response("OK", { status: 200 }); +} diff --git a/docker-compose.yaml b/docker-compose.yaml index f5973f3a..e3fc55ac 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -48,7 +48,7 @@ services: start_period: 10s web: build: - context: ./apps/web + context: . dockerfile: ./apps/web/Dockerfile restart: unless-stopped ports: -- cgit v1.3.1 From 411a9a5f1cdb4b52c65f4a1344c9be7694d130d7 Mon Sep 17 00:00:00 2001 From: Pulkit Garg Date: Wed, 25 Jun 2025 10:26:29 +0530 Subject: feature: add project name editing functionality --- apps/web/src/components/editor-header.tsx | 17 ++-- .../src/components/editor/project-name-editor.tsx | 110 +++++++++++++++++++++ apps/web/src/stores/project-store.ts | 13 +++ 3 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 apps/web/src/components/editor/project-name-editor.tsx (limited to 'apps/web/src') diff --git a/apps/web/src/components/editor-header.tsx b/apps/web/src/components/editor-header.tsx index c6cecbe8..6f9f0b2f 100644 --- a/apps/web/src/components/editor-header.tsx +++ b/apps/web/src/components/editor-header.tsx @@ -6,6 +6,7 @@ import { ChevronLeft, Download } from "lucide-react"; import { useProjectStore } from "@/stores/project-store"; import { useTimelineStore } from "@/stores/timeline-store"; import { HeaderBase } from "./header-base"; +import { ProjectNameEditor } from "./editor/project-name-editor"; export function EditorHeader() { const { activeProject } = useProjectStore(); @@ -24,13 +25,15 @@ export function EditorHeader() { }; const leftContent = ( - - - {activeProject?.name || "Loading..."} - +
+ + + + +
); const centerContent = ( diff --git a/apps/web/src/components/editor/project-name-editor.tsx b/apps/web/src/components/editor/project-name-editor.tsx new file mode 100644 index 00000000..aa0141ae --- /dev/null +++ b/apps/web/src/components/editor/project-name-editor.tsx @@ -0,0 +1,110 @@ +"use client"; + +import { useState, useRef, useEffect } from "react"; +import { Input } from "../ui/input"; +import { useProjectStore } from "@/stores/project-store"; +import { Edit2, Check, X } from "lucide-react"; +import { Button } from "../ui/button"; + +interface ProjectNameEditorProps { + className?: string; +} + +export function ProjectNameEditor({ className }: ProjectNameEditorProps) { + const { activeProject, updateProjectName } = useProjectStore(); + const [isEditing, setIsEditing] = useState(false); + const [editValue, setEditValue] = useState(""); + const inputRef = useRef(null); + + useEffect(() => { + if (activeProject) { + setEditValue(activeProject.name); + } + }, [activeProject]); + + useEffect(() => { + if (isEditing && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isEditing]); + + const handleStartEdit = () => { + if (activeProject) { + setEditValue(activeProject.name); + setIsEditing(true); + } + }; + + const handleSave = () => { + if (editValue.trim()) { + updateProjectName(editValue.trim()); + setIsEditing(false); + } + }; + + const handleCancel = () => { + if (activeProject) { + setEditValue(activeProject.name); + } + setIsEditing(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleSave(); + } else if (e.key === "Escape") { + handleCancel(); + } + }; + + if (!activeProject) { + return Loading...; + } + + if (isEditing) { + return ( +
+ setEditValue(e.target.value)} + onKeyDown={handleKeyDown} + className="h-7 text-sm px-3 py-1 min-w-[200px]" + size={1} + /> + + +
+ ); + } + + return ( +
+ {activeProject.name} + +
+ ); +} \ No newline at end of file diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index 734c07e2..0d0812cd 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -7,6 +7,7 @@ interface ProjectStore { // Actions createNewProject: (name: string) => void; closeProject: () => void; + updateProjectName: (name: string) => void; } export const useProjectStore = create((set) => ({ @@ -25,4 +26,16 @@ export const useProjectStore = create((set) => ({ closeProject: () => { set({ activeProject: null }); }, + + updateProjectName: (name: string) => { + set((state) => ({ + activeProject: state.activeProject + ? { + ...state.activeProject, + name, + updatedAt: new Date(), + } + : null, + })); + }, })); -- cgit v1.3.1 From 7444ab2894a03963c017ec4a788343e5749c405c Mon Sep 17 00:00:00 2001 From: Shubbu03 Date: Wed, 25 Jun 2025 12:13:16 +0530 Subject: UI fixes to the media-panel component --- apps/web/src/components/editor/media-panel.tsx | 72 ++++++++++++++++---------- 1 file changed, 44 insertions(+), 28 deletions(-) (limited to 'apps/web/src') diff --git a/apps/web/src/components/editor/media-panel.tsx b/apps/web/src/components/editor/media-panel.tsx index 95cfa0a8..51a92fed 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 */}
{/* Search and filter controls */} - - setSearchQuery(e.target.value)} - /> + + setSearchQuery(e.target.value)} + /> {/* Add media button */} -
+
@@ -276,7 +284,15 @@ export function MediaPanel() { {renderPreview(item)} - {item.name} + + {item.name.length > 8 + ? `${item.name.slice(0, 4)}...${item.name.slice(-3)}` + : item.name} + {/* Show remove button on hover */} -- cgit v1.3.1 From 4db515df518879ea7553db55faba5f092ca9e210 Mon Sep 17 00:00:00 2001 From: Shubbu03 Date: Wed, 25 Jun 2025 12:29:06 +0530 Subject: code rabbit suggested fix --- apps/web/src/components/editor/media-panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apps/web/src') diff --git a/apps/web/src/components/editor/media-panel.tsx b/apps/web/src/components/editor/media-panel.tsx index 51a92fed..f2b3f657 100644 --- a/apps/web/src/components/editor/media-panel.tsx +++ b/apps/web/src/components/editor/media-panel.tsx @@ -247,7 +247,7 @@ export function MediaPanel() { <> Add -- cgit v1.3.1 From 66eb6f13d52edfd7497d40315f16e490268632f4 Mon Sep 17 00:00:00 2001 From: DevloperAmanSingh Date: Wed, 25 Jun 2025 13:27:39 +0530 Subject: feat(preview-panel): add volume control and mute functionality to the video player --- apps/web/src/components/editor/preview-panel.tsx | 67 ++++++++++++++++++------ 1 file changed, 50 insertions(+), 17 deletions(-) (limited to 'apps/web/src') 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(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() {