diff --git a/public/wallpapers/wallpaper13.jpg b/public/wallpapers/wallpaper13.jpg new file mode 100644 index 0000000..052f0ff Binary files /dev/null and b/public/wallpapers/wallpaper13.jpg differ diff --git a/public/wallpapers/wallpaper14.jpg b/public/wallpapers/wallpaper14.jpg new file mode 100644 index 0000000..c1762f7 Binary files /dev/null and b/public/wallpapers/wallpaper14.jpg differ diff --git a/public/wallpapers/wallpaper15.jpg b/public/wallpapers/wallpaper15.jpg new file mode 100644 index 0000000..d3c1957 Binary files /dev/null and b/public/wallpapers/wallpaper15.jpg differ diff --git a/public/wallpapers/wallpaper16.jpg b/public/wallpapers/wallpaper16.jpg new file mode 100644 index 0000000..9038dde Binary files /dev/null and b/public/wallpapers/wallpaper16.jpg differ diff --git a/public/wallpapers/wallpaper17.jpg b/public/wallpapers/wallpaper17.jpg new file mode 100644 index 0000000..4f653c0 Binary files /dev/null and b/public/wallpapers/wallpaper17.jpg differ diff --git a/public/wallpapers/wallpaper18.jpg b/public/wallpapers/wallpaper18.jpg new file mode 100644 index 0000000..5742aab Binary files /dev/null and b/public/wallpapers/wallpaper18.jpg differ diff --git a/public/wallpapers/wallpaper19.jpg b/public/wallpapers/wallpaper19.jpg new file mode 100644 index 0000000..2270347 Binary files /dev/null and b/public/wallpapers/wallpaper19.jpg differ diff --git a/public/wallpapers/wallpaper20.jpg b/public/wallpapers/wallpaper20.jpg new file mode 100644 index 0000000..1f9efce Binary files /dev/null and b/public/wallpapers/wallpaper20.jpg differ diff --git a/public/wallpapers/wallpaper21.jpg b/public/wallpapers/wallpaper21.jpg new file mode 100644 index 0000000..d7188a4 Binary files /dev/null and b/public/wallpapers/wallpaper21.jpg differ diff --git a/public/wallpapers/wallpaper22.jpg b/public/wallpapers/wallpaper22.jpg new file mode 100644 index 0000000..c20d458 Binary files /dev/null and b/public/wallpapers/wallpaper22.jpg differ diff --git a/public/wallpapers/wallpaper23.jpg b/public/wallpapers/wallpaper23.jpg new file mode 100644 index 0000000..9976c8c Binary files /dev/null and b/public/wallpapers/wallpaper23.jpg differ diff --git a/src/components/video-editor/CropControl.tsx b/src/components/video-editor/CropControl.tsx index 2dfb235..036116c 100644 --- a/src/components/video-editor/CropControl.tsx +++ b/src/components/video-editor/CropControl.tsx @@ -158,7 +158,7 @@ export function CropControl({ videoElement, cropRegion, onCropChange }: CropCont
-
-
-
+
+
+
{showSuccess ? ( <> -
- +
+ +
+
+ Export Complete + Your video is ready
- Export Complete! ) : ( <> {isExporting ? ( - +
+ +
) : ( - +
+ +
)} - - {error ? 'Export Failed' : isExporting ? 'Exporting Video' : 'Export Video'} - +
+ + {error ? 'Export Failed' : isExporting ? 'Exporting Video' : 'Export Video'} + + + {error ? 'Please try again' : isExporting ? 'This may take a moment...' : 'Ready to start'} + +
)}
@@ -69,7 +81,7 @@ export function ExportDialog({ variant="ghost" size="icon" onClick={onClose} - className="hover:bg-[#34B27B]/20 text-slate-200" + className="hover:bg-white/10 text-slate-400 hover:text-white rounded-full" > @@ -77,40 +89,53 @@ export function ExportDialog({
{error && ( -
-
-

{error}

+
+
+
+ +
+

{error}

)} {isExporting && progress && ( -
+
-
+
Progress - {progress.percentage.toFixed(1)}% + {progress.percentage.toFixed(0)}%
-
+
-
-
Frame
-
- {progress.currentFrame} / {progress.totalFrames} +
+
+
Current Frame
+
+ {progress.currentFrame} / {progress.totalFrames} +
+
+
+
Status
+
+ + Processing +
{onCancel && ( -
+
@@ -120,14 +145,8 @@ export function ExportDialog({ )} {showSuccess && ( -
-

Video saved successfully!

-
- )} - - {!isExporting && !error && !showSuccess && ( -
-

Ready to export your video

+
+

Video saved successfully!

)}
diff --git a/src/components/video-editor/PlaybackControls.tsx b/src/components/video-editor/PlaybackControls.tsx index 24370a1..f567235 100644 --- a/src/components/video-editor/PlaybackControls.tsx +++ b/src/components/video-editor/PlaybackControls.tsx @@ -1,5 +1,6 @@ import { Button } from "../ui/button"; -import { MdPlayArrow, MdPause } from "react-icons/md"; +import { Play, Pause } from "lucide-react"; +import { cn } from "@/lib/utils"; interface PlaybackControlsProps { isPlaying: boolean; @@ -27,36 +28,63 @@ export default function PlaybackControls({ onSeek(parseFloat(e.target.value)); } + const progress = duration > 0 ? (currentTime / duration) * 100 : 0; + return ( -
+
- + + {formatTime(currentTime)} - - + +
+ {/* Custom Track Background */} +
+
+
+ + {/* Interactive Input */} + + + {/* Custom Thumb (visual only, follows progress) */} +
+
+ + {formatTime(duration)}
diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 53e1dd0..9bdf7c8 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -11,7 +11,7 @@ import { Trash2, Download, Crop, X, Bug } from "lucide-react"; import type { ZoomDepth, CropRegion } from "./types"; import { CropControl } from "./CropControl"; -const WALLPAPER_COUNT = 12; +const WALLPAPER_COUNT = 23; const WALLPAPER_RELATIVE = Array.from({ length: WALLPAPER_COUNT }, (_, i) => `wallpapers/wallpaper${i + 1}.jpg`); const GRADIENTS = [ "linear-gradient( 111.6deg, rgba(114,167,232,1) 9.4%, rgba(253,129,82,1) 43.9%, rgba(253,129,82,1) 54.8%, rgba(249,202,86,1) 86.3% )", @@ -26,6 +26,19 @@ const GRADIENTS = [ "linear-gradient(109.6deg, #F635A6, #36D860)", "linear-gradient(90deg, #FF0101, #4DFF01)", "linear-gradient(315deg, #EC0101, #5044A9)", + // New Gradients + "linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%)", + "linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%)", + "linear-gradient(to right, #ff8177 0%, #ff867a 0%, #ff8c7f 21%, #f99185 52%, #cf556c 78%, #b12a5b 100%)", + "linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%)", + "linear-gradient(to right, #4facfe 0%, #00f2fe 100%)", + "linear-gradient(to top, #fcc5e4 0%, #fda34b 15%, #ff7882 35%, #c8699e 52%, #7046aa 71%, #0c1db8 87%, #020f75 100%)", + "linear-gradient(to right, #fa709a 0%, #fee140 100%)", + "linear-gradient(to top, #30cfd0 0%, #330867 100%)", + "linear-gradient(to top, #c471f5 0%, #fa71cd 100%)", + "linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%)", + "linear-gradient(to top, #48c6ef 0%, #6f86d6 100%)", + "linear-gradient(to right, #0acffe 0%, #495aff 100%)", ]; interface SettingsPanelProps { @@ -83,13 +96,13 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, }; return ( -
-
-
- Zoom Level +
+
+
+ Zoom Level {zoomEnabled && selectedZoomDepth && ( - - Active · {ZOOM_DEPTH_OPTIONS.find(o => o.depth === selectedZoomDepth)?.label} + + {ZOOM_DEPTH_OPTIONS.find(o => o.depth === selectedZoomDepth)?.label} Active )}
@@ -103,74 +116,75 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, disabled={!zoomEnabled} onClick={() => onZoomDepthChange?.(option.depth)} className={cn( - "h-auto w-full rounded-xl border px-2 py-3 text-center shadow-lg transition-all flex flex-col items-center justify-center gap-1", - "duration-150 ease-in-out", - zoomEnabled ? "opacity-100" : "opacity-60", + "h-auto w-full rounded-xl border px-1 py-3 text-center shadow-sm transition-all flex flex-col items-center justify-center gap-1.5", + "duration-200 ease-out", + zoomEnabled ? "opacity-100 cursor-pointer" : "opacity-40 cursor-not-allowed", isActive - ? "border-[#34B27B] bg-white text-black shadow-[#34B27B]/20 scale-105" - : "border-[#23232a] bg-[#23232a] text-slate-200 hover:border-[#34B27B] hover:scale-105" + ? "border-[#34B27B] bg-[#34B27B] text-white shadow-[#34B27B]/20 scale-105 ring-2 ring-[#34B27B]/20" + : "border-white/5 bg-white/5 text-slate-400 hover:bg-white/10 hover:border-white/10 hover:text-slate-200" )} - style={isActive ? { background: '#fff', color: '#111' } : undefined} > - {option.label} + {option.label} ); })}
{!zoomEnabled && ( -

Select a zoom item in the timeline to adjust its depth.

+

Select a zoom region in the timeline to adjust depth.

)} {zoomEnabled && ( )}
-
-
-
- -
Shadow
-
-
- -
Blur Background
-
+ +
+
+
Drop Shadow
+ +
+
+
Blur Background
+
-
+ +
-

- If the preview looks weirdly positioned at any time, try force reloading +

+ If the preview looks weirdly positioned at any time, try force reloading (you will lose all your progress)

{showCropDropdown && cropRegion && onCropChange && ( <>
setShowCropDropdown(false)} /> -
+
Crop Video @@ -180,7 +194,7 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, variant="ghost" size="icon" onClick={() => setShowCropDropdown(false)} - className="hover:bg-[#34B27B]/20 text-slate-200" + className="hover:bg-white/10 text-slate-400 hover:text-white" > @@ -194,6 +208,7 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, @@ -201,100 +216,107 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth,
)} - - - Image - Color - Gradient + + + + Image + Color + Gradient - -
- {(wallpaperPaths.length > 0 ? wallpaperPaths : WALLPAPER_RELATIVE.map(p => `/${p}`)).map((path, idx) => { - const isSelected = (() => { - if (!selected) return false; - - if (selected === path) return true; - try { - const clean = (s: string) => s.replace(/^file:\/\//, '').replace(/^\//, '') - if (clean(selected).endsWith(clean(path))) return true; - if (clean(path).endsWith(clean(selected))) return true; - } catch { - } - return false; - })(); +
+ +
+ {(wallpaperPaths.length > 0 ? wallpaperPaths : WALLPAPER_RELATIVE.map(p => `/${p}`)).map((path, idx) => { + const isSelected = (() => { + if (!selected) return false; + + if (selected === path) return true; + try { + const clean = (s: string) => s.replace(/^file:\/\//, '').replace(/^\//, '') + if (clean(selected).endsWith(clean(path))) return true; + if (clean(path).endsWith(clean(selected))) return true; + } catch { + } + return false; + })(); - return ( + return ( +
onWallpaperChange(path)} + role="button" + /> + ) + })} +
+ + + +
+ { + setHsva(color.hsva); + onWallpaperChange(hsvaToHex(color.hsva)); + }} + style={{ width: '100%', borderRadius: '12px' }} + /> +
+
+ + +
+ {GRADIENTS.map((g, idx) => (
onWallpaperChange(path)} + style={{ background: g }} + aria-label={`Gradient ${idx + 1}`} + onClick={() => { setGradient(g); onWallpaperChange(g); }} role="button" /> - ) - })} -
- - - -
- { - setHsva(color.hsva); - onWallpaperChange(hsvaToHex(color.hsva)); - }} - /> -
-
- - -
- {GRADIENTS.map((g, idx) => ( -
{ setGradient(g); onWallpaperChange(g); }} - role="button" - /> - ))} -
- - -
- - + ))} +
+
+ + +
+ + +
); } diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 3c7e3e8..58b6911 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -1,6 +1,6 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { Toaster } from "@/components/ui/sonner"; import { toast } from "sonner"; @@ -22,7 +22,7 @@ import { } from "./types"; import { VideoExporter, type ExportProgress } from "@/lib/exporter"; -const WALLPAPER_COUNT = 12; +const WALLPAPER_COUNT = 23; const WALLPAPER_PATHS = Array.from({ length: WALLPAPER_COUNT }, (_, i) => `/wallpapers/wallpaper${i + 1}.jpg`); export default function VideoEditor() { @@ -153,10 +153,7 @@ export default function VideoEditor() { } }, [selectedZoomId]); - const selectedZoom = useMemo(() => { - if (!selectedZoomId) return null; - return zoomRegions.find((region) => region.id === selectedZoomId) ?? null; - }, [selectedZoomId, zoomRegions]); + useEffect(() => { if (selectedZoomId && !zoomRegions.some((region) => region.id === selectedZoomId)) { @@ -271,35 +268,26 @@ export default function VideoEditor() { const isMac = navigator.userAgent.includes('Mac'); return ( -
+
{/* Drag region for window - more padding on macOS for traffic lights */}
-
-
- -
- setShowExportDialog(false)} - progress={exportProgress} - isExporting={isExporting} - error={exportError} - onCancel={handleCancelExport} - /> -
-
- {videoPath && ( - <> -
+ +
+ {/* Left Column - Video & Timeline */} +
+ {/* Video Preview Area */} +
+
+
- - - )} + + {/* Floating Playback Controls */} +
+
+ +
+
+
+
+ + {/* Timeline Area */} +
+ +
- z.id === selectedZoomId)?.depth : null} + onZoomDepthChange={(depth) => selectedZoomId && handleZoomDepthChange(depth)} selectedZoomId={selectedZoomId} - onSelectZoom={handleSelectZoom} + onZoomDelete={handleZoomDelete} + showShadow={showShadow} + onShadowChange={setShowShadow} + showBlur={showBlur} + onBlurChange={setShowBlur} + cropRegion={cropRegion} + onCropChange={setCropRegion} + videoElement={videoPlaybackRef.current?.video || null} + onExport={handleExport} />
- + + setShowExportDialog(false)} + progress={exportProgress} + isExporting={isExporting} + error={exportError} + onCancel={handleCancelExport} /> -
); } \ No newline at end of file diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index 4396086..8c43ca3 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -741,7 +741,7 @@ const VideoPlayback = forwardRef(({ >
diff --git a/src/components/video-editor/timeline/TimelineEditor.tsx b/src/components/video-editor/timeline/TimelineEditor.tsx index 150061d..190546b 100644 --- a/src/components/video-editor/timeline/TimelineEditor.tsx +++ b/src/components/video-editor/timeline/TimelineEditor.tsx @@ -3,6 +3,7 @@ import { useTimelineContext } from "dnd-timeline"; import { Button } from "@/components/ui/button"; import { Plus } from "lucide-react"; import { toast } from "sonner"; +import { cn } from "@/lib/utils"; import TimelineWrapper from "./TimelineWrapper"; import Row from "./Row"; import Item from "./Item"; @@ -151,41 +152,21 @@ function PlaybackCursor({
-
- -
- +
-
); @@ -241,7 +222,7 @@ function TimelineAxis({ return (
-
-
+
+
{marker.label} @@ -298,7 +263,7 @@ function Timeline({ items, videoDurationMs, intervalMs, - currentTimeMs, + currentTimeMs, onSeek, onSelectZoom, selectedZoomId, @@ -333,10 +298,11 @@ function Timeline({
- +
+ {items.map((item) => ( @@ -468,33 +434,37 @@ export default function TimelineEditor({ if (!videoDuration || videoDuration === 0) { return ( -
- Load a video to see timeline +
+ Load a video to see timeline
); } return ( -
-
-
-
- - Command + Shift + Scroll +
+ + ⇧ + ⌘ + Scroll Pan - - - Command + Scroll + + ⌘ + Scroll Zoom
-
+