summaryrefslogtreecommitdiff
path: root/apps/web
diff options
context:
space:
mode:
authorGeorge <cjooo092@gmail.com>2025-06-24 20:06:08 +0800
committerGitHub <noreply@github.com>2025-06-24 20:06:08 +0800
commit984be4f6e6d0c09499773c239e743a6e4ae90879 (patch)
treea6c8df1143c1c3297aaea3efedd81bfb8d5fd21e /apps/web
parent4941ec4798300667c2336d5c644ced6b75fad139 (diff)
parent9e761a9c0b005da948b296ac0c2054ac4e0bf574 (diff)
downloadopencut-984be4f6e6d0c09499773c239e743a6e4ae90879.tar.gz
Merge branch 'OpenCut-app:main' into feat/media-panel-filter
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/src/components/editor/timeline.tsx117
-rw-r--r--apps/web/src/lib/waitlist.ts4
2 files changed, 76 insertions, 45 deletions
diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx
index 9dec5bbc..f1dd04a7 100644
--- a/apps/web/src/components/editor/timeline.tsx
+++ b/apps/web/src/components/editor/timeline.tsx
@@ -524,17 +524,24 @@ export function Timeline() {
onClick={toggle}
className="mr-2"
>
- {isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
+ {isPlaying ? (
+ <Pause className="h-4 w-4" />
+ ) : (
+ <Play className="h-4 w-4" />
+ )}
</Button>
</TooltipTrigger>
- <TooltipContent>{isPlaying ? "Pause (Space)" : "Play (Space)"}</TooltipContent>
+ <TooltipContent>
+ {isPlaying ? "Pause (Space)" : "Play (Space)"}
+ </TooltipContent>
</Tooltip>
<div className="w-px h-6 bg-border mx-1" />
{/* Time Display */}
<div className="text-xs text-muted-foreground font-mono px-2">
- {Math.floor(currentTime * 10) / 10}s / {Math.floor(duration * 10) / 10}s
+ {Math.floor(currentTime * 10) / 10}s /{" "}
+ {Math.floor(duration * 10) / 10}s
</div>
<div className="w-px h-6 bg-border mx-1" />
@@ -606,7 +613,11 @@ export function Timeline() {
<Tooltip>
<TooltipTrigger asChild>
- <Button variant="text" size="icon" onClick={handleDuplicateSelected}>
+ <Button
+ variant="text"
+ size="icon"
+ onClick={handleDuplicateSelected}
+ >
<Copy className="h-4 w-4" />
</Button>
</TooltipTrigger>
@@ -710,17 +721,19 @@ export function Timeline() {
return (
<div
key={i}
- className={`absolute top-0 bottom-0 ${isMainMarker
- ? "border-l border-muted-foreground/40"
- : "border-l border-muted-foreground/20"
- }`}
+ className={`absolute top-0 bottom-0 ${
+ isMainMarker
+ ? "border-l border-muted-foreground/40"
+ : "border-l border-muted-foreground/20"
+ }`}
style={{ left: `${time * 50 * zoomLevel}px` }}
>
<span
- className={`absolute top-1 left-1 text-xs ${isMainMarker
- ? "text-muted-foreground font-medium"
- : "text-muted-foreground/70"
- }`}
+ className={`absolute top-1 left-1 text-xs ${
+ isMainMarker
+ ? "text-muted-foreground font-medium"
+ : "text-muted-foreground/70"
+ }`}
>
{(() => {
const formatTime = (seconds: number) => {
@@ -762,18 +775,8 @@ export function Timeline() {
{/* Tracks Area */}
<div className="flex-1 flex overflow-hidden">
{/* Track Labels */}
- <div className="w-48 flex-shrink-0 border-r bg-background overflow-y-auto">
- {tracks.length === 0 ? (
- <div className="flex flex-col items-center justify-center h-full py-8 text-center px-4">
- <div className="w-12 h-12 rounded-full bg-muted/30 flex items-center justify-center mb-3">
- <SplitSquareHorizontal className="h-6 w-6 text-muted-foreground" />
- </div>
- <p className="text-sm text-muted-foreground">No tracks</p>
- <p className="text-xs text-muted-foreground/70 mt-1">
- Drop media to create tracks
- </p>
- </div>
- ) : (
+ {tracks.length > 0 && (
+ <div className="w-48 flex-shrink-0 border-r bg-background overflow-y-auto">
<div className="flex flex-col">
{tracks.map((track) => (
<div
@@ -782,7 +785,7 @@ export function Timeline() {
onContextMenu={(e) => {
e.preventDefault();
setContextMenu({
- type: 'track',
+ type: "track",
trackId: track.id,
x: e.clientX,
y: e.clientY,
@@ -795,8 +798,8 @@ export function Timeline() {
track.type === "video"
? "bg-blue-500"
: track.type === "audio"
- ? "bg-green-500"
- : "bg-purple-500"
+ ? "bg-green-500"
+ : "bg-purple-500"
}`}
/>
<span className="ml-2 text-sm font-medium truncate">
@@ -811,12 +814,16 @@ export function Timeline() {
</div>
))}
</div>
- )}
- </div>
+ </div>
+ )}
{/* Timeline Tracks Content */}
<div className="flex-1 relative overflow-hidden">
- <div className="w-full h-[600px] overflow-hidden flex" ref={timelineRef} style={{ position: 'relative' }}>
+ <div
+ className="w-full h-[600px] overflow-hidden flex"
+ ref={timelineRef}
+ style={{ position: "relative" }}
+ >
{/* Timeline grid and clips area (with left margin for sidebar) */}
<div
className="relative flex-1"
@@ -852,7 +859,7 @@ export function Timeline() {
onContextMenu={(e) => {
e.preventDefault();
setContextMenu({
- type: 'track',
+ type: "track",
trackId: track.id,
x: e.clientX,
y: e.clientY,
@@ -893,19 +900,23 @@ export function Timeline() {
style={{ left: contextMenu.x, top: contextMenu.y }}
onContextMenu={(e) => e.preventDefault()}
>
- {contextMenu.type === 'track' ? (
+ {contextMenu.type === "track" ? (
// Track context menu
<>
<button
className="flex items-center w-full px-3 py-2 hover:bg-accent hover:text-accent-foreground transition-colors text-left"
onClick={() => {
- const track = tracks.find(t => t.id === contextMenu.trackId);
+ const track = tracks.find(
+ (t) => t.id === contextMenu.trackId
+ );
if (track) toggleTrackMute(track.id);
setContextMenu(null);
}}
>
{(() => {
- const track = tracks.find(t => t.id === contextMenu.trackId);
+ const track = tracks.find(
+ (t) => t.id === contextMenu.trackId
+ );
return track?.muted ? (
<>
<Volume2 className="h-4 w-4 mr-2" />
@@ -939,14 +950,23 @@ export function Timeline() {
className="flex items-center w-full px-3 py-2 hover:bg-accent hover:text-accent-foreground transition-colors text-left"
onClick={() => {
if (contextMenu.clipId) {
- const track = tracks.find(t => t.id === contextMenu.trackId);
- const clip = track?.clips.find(c => c.id === contextMenu.clipId);
+ const track = tracks.find(
+ (t) => t.id === contextMenu.trackId
+ );
+ const clip = track?.clips.find(
+ (c) => c.id === contextMenu.clipId
+ );
if (clip && track) {
const splitTime = currentTime;
const effectiveStart = clip.startTime;
- const effectiveEnd = clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
+ const effectiveEnd =
+ clip.startTime +
+ (clip.duration - clip.trimStart - clip.trimEnd);
- if (splitTime > effectiveStart && splitTime < effectiveEnd) {
+ if (
+ splitTime > effectiveStart &&
+ splitTime < effectiveEnd
+ ) {
updateClipTrim(
track.id,
clip.id,
@@ -958,7 +978,8 @@ export function Timeline() {
name: clip.name + " (split)",
duration: clip.duration,
startTime: splitTime,
- trimStart: clip.trimStart + (splitTime - effectiveStart),
+ trimStart:
+ clip.trimStart + (splitTime - effectiveStart),
trimEnd: clip.trimEnd,
});
toast.success("Clip split successfully");
@@ -977,14 +998,21 @@ export function Timeline() {
className="flex items-center w-full px-3 py-2 hover:bg-accent hover:text-accent-foreground transition-colors text-left"
onClick={() => {
if (contextMenu.clipId) {
- const track = tracks.find(t => t.id === contextMenu.trackId);
- const clip = track?.clips.find(c => c.id === contextMenu.clipId);
+ const track = tracks.find(
+ (t) => t.id === contextMenu.trackId
+ );
+ const clip = track?.clips.find(
+ (c) => c.id === contextMenu.clipId
+ );
if (clip && track) {
useTimelineStore.getState().addClipToTrack(track.id, {
mediaId: clip.mediaId,
name: clip.name + " (copy)",
duration: clip.duration,
- startTime: clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd) + 0.1,
+ startTime:
+ clip.startTime +
+ (clip.duration - clip.trimStart - clip.trimEnd) +
+ 0.1,
trimStart: clip.trimStart,
trimEnd: clip.trimEnd,
});
@@ -1002,7 +1030,10 @@ export function Timeline() {
className="flex items-center w-full px-3 py-2 text-destructive hover:bg-destructive/10 transition-colors text-left"
onClick={() => {
if (contextMenu.clipId) {
- removeClipFromTrack(contextMenu.trackId, contextMenu.clipId);
+ removeClipFromTrack(
+ contextMenu.trackId,
+ contextMenu.clipId
+ );
toast.success("Clip deleted");
}
setContextMenu(null);
diff --git a/apps/web/src/lib/waitlist.ts b/apps/web/src/lib/waitlist.ts
index c397109d..c01347d8 100644
--- a/apps/web/src/lib/waitlist.ts
+++ b/apps/web/src/lib/waitlist.ts
@@ -1,5 +1,5 @@
-import { db } from "@/lib/db";
-import { waitlist } from "@/lib/db/schema";
+import { db } from "@opencut/db";
+import { waitlist } from "@opencut/db/schema";
import { sql } from "drizzle-orm";
export async function getWaitlistCount() {