From 5b0bf63749ca1ac1f4329bfa07442e6998fd384b Mon Sep 17 00:00:00 2001 From: webadderall <131426131+webadderall@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:42:50 +1100 Subject: [PATCH] refactor(projects): simplify browser modal and thumbnail capture --- .../video-editor/ProjectBrowserDialog.tsx | 91 ++----------------- src/components/video-editor/VideoEditor.tsx | 66 +++----------- 2 files changed, 24 insertions(+), 133 deletions(-) diff --git a/src/components/video-editor/ProjectBrowserDialog.tsx b/src/components/video-editor/ProjectBrowserDialog.tsx index 37a82d45..ff3109ee 100644 --- a/src/components/video-editor/ProjectBrowserDialog.tsx +++ b/src/components/video-editor/ProjectBrowserDialog.tsx @@ -1,6 +1,4 @@ -import { FolderOpen, Save } from "lucide-react"; import { useMemo } from "react"; -import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, @@ -23,11 +21,7 @@ type ProjectBrowserDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; entries: ProjectLibraryEntry[]; - projectsDirectoryPath: string | null; onOpenProject: (projectPath: string) => void; - onBrowseProjectFiles: () => void; - onOpenProjectsFolder: () => void; - onSaveProjectAs: () => void; }; function formatUpdatedAt(updatedAt: number) { @@ -47,65 +41,22 @@ export default function ProjectBrowserDialog({ open, onOpenChange, entries, - projectsDirectoryPath, onOpenProject, - onBrowseProjectFiles, - onOpenProjectsFolder, - onSaveProjectAs, }: ProjectBrowserDialogProps) { - const visibleEntries = useMemo(() => entries.slice(0, 16), [entries]); + const visibleEntries = useMemo(() => entries.slice(0, 24), [entries]); return ( - + Projects - Open recent Recordly projects from one place. New projects are saved to the dedicated - projects folder by default. + Open recent Recordly projects from one place. -
-
-
- Projects Folder -
-
- {projectsDirectoryPath ?? "Loading projects folder..."} -
-
-
- - - -
-
{visibleEntries.length > 0 ? ( -
+
{visibleEntries.map((entry) => { const thumbnailSrc = entry.thumbnailPath ? toFileUrl(entry.thumbnailPath) : null; return ( @@ -113,7 +64,7 @@ export default function ProjectBrowserDialog({ key={entry.path} type="button" onClick={() => onOpenProject(entry.path)} - className="group flex flex-col overflow-hidden rounded-2xl border border-white/10 bg-white/[0.03] text-left transition hover:border-[#2563EB]/60 hover:bg-white/[0.05]" + className="group flex flex-col overflow-hidden rounded-xl border border-white/10 bg-white/[0.03] text-left transition hover:border-[#2563EB]/60 hover:bg-white/[0.05]" >
{thumbnailSrc ? ( @@ -139,12 +90,10 @@ export default function ProjectBrowserDialog({ ) : null}
-
-
- {entry.name} -
-
{entry.path}
-
+
+
{entry.name}
+
{entry.path}
+
Updated {formatUpdatedAt(entry.updatedAt)}
@@ -156,27 +105,7 @@ export default function ProjectBrowserDialog({
No saved projects yet
- Save a project and Recordly will keep it in the projects folder with a preview - thumbnail so it is easy to reopen later. -
-
- - + Save a project and it will appear here with a preview thumbnail.
)} diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index c0cbe77a..0de8be18 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -305,7 +305,6 @@ export default function VideoEditor() { const [videoSourcePath, setVideoSourcePath] = useState(null); const [currentProjectPath, setCurrentProjectPath] = useState(null); const [projectLibraryEntries, setProjectLibraryEntries] = useState([]); - const [projectsDirectoryPath, setProjectsDirectoryPath] = useState(null); const [projectBrowserOpen, setProjectBrowserOpen] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -460,7 +459,6 @@ export default function VideoEditor() { throw new Error(result.error || "Failed to load project library"); } - setProjectsDirectoryPath(result.projectsDir ?? null); setProjectLibraryEntries(result.entries); } catch (projectLibraryError) { console.warn("Unable to refresh project library:", projectLibraryError); @@ -469,12 +467,13 @@ export default function VideoEditor() { const captureProjectThumbnail = useCallback(async () => { const previewHandle = videoPlaybackRef.current; - const previewCanvas = previewHandle?.app?.canvas ?? null; const previewVideo = previewHandle?.video ?? null; + const previewCanvas = previewHandle?.app?.canvas ?? null; if (previewHandle && previewVideo && previewVideo.paused) { try { await previewHandle.refreshFrame(); + await new Promise((resolve) => requestAnimationFrame(() => resolve(undefined))); } catch (thumbnailRefreshError) { console.warn( "Unable to refresh preview frame before thumbnail capture:", @@ -484,8 +483,8 @@ export default function VideoEditor() { } const canvas = document.createElement("canvas"); - const targetWidth = 1280; - const targetHeight = 720; + const targetWidth = 360; + const targetHeight = 203; canvas.width = targetWidth; canvas.height = targetHeight; @@ -493,15 +492,17 @@ export default function VideoEditor() { if (!context) { return null; } + context.imageSmoothingEnabled = true; + context.imageSmoothingQuality = "high"; context.fillStyle = "#111113"; context.fillRect(0, 0, targetWidth, targetHeight); const drawableSource = - previewCanvas && previewCanvas.width > 0 && previewCanvas.height > 0 - ? previewCanvas - : previewVideo && previewVideo.videoWidth > 0 && previewVideo.videoHeight > 0 - ? previewVideo + previewVideo && previewVideo.videoWidth > 0 && previewVideo.videoHeight > 0 + ? previewVideo + : previewCanvas && previewCanvas.width > 0 && previewCanvas.height > 0 + ? previewCanvas : null; if (!drawableSource) { @@ -1559,30 +1560,6 @@ export default function VideoEditor() { } }, [saveProject]); - const handleLoadProject = useCallback(async () => { - const result = await window.electronAPI.loadProjectFile(); - - if (result.canceled) { - return; - } - - if (!result.success) { - toast.error(result.message || "Failed to load project"); - return; - } - - const restored = await applyLoadedProject(result.project, result.path ?? null); - if (!restored) { - toast.error("Invalid project file format"); - return; - } - - setProjectBrowserOpen(false); - await refreshProjectLibrary(); - - toast.success(`Project loaded from ${result.path}`); - }, [applyLoadedProject, refreshProjectLibrary]); - const handleOpenProjectFromLibrary = useCallback( async (projectPath: string) => { const result = await window.electronAPI.openProjectFileAtPath(projectPath); @@ -1609,20 +1586,15 @@ export default function VideoEditor() { [applyLoadedProject, refreshProjectLibrary], ); - const handleOpenProjectsFolder = useCallback(async () => { - const result = await window.electronAPI.openProjectsDirectory(); - if (!result.success) { - toast.error(result.message || result.error || "Failed to open projects folder"); - } - }, []); - const handleOpenProjectBrowser = useCallback(async () => { await refreshProjectLibrary(); setProjectBrowserOpen(true); }, [refreshProjectLibrary]); useEffect(() => { - const removeLoadListener = window.electronAPI.onMenuLoadProject(handleLoadProject); + const removeLoadListener = window.electronAPI.onMenuLoadProject(() => { + void handleOpenProjectBrowser(); + }); const removeSaveListener = window.electronAPI.onMenuSaveProject(handleSaveProject); const removeSaveAsListener = window.electronAPI.onMenuSaveProjectAs(handleSaveProjectAs); @@ -1631,7 +1603,7 @@ export default function VideoEditor() { removeSaveListener?.(); removeSaveAsListener?.(); }; - }, [handleLoadProject, handleSaveProject, handleSaveProjectAs]); + }, [handleOpenProjectBrowser, handleSaveProject, handleSaveProjectAs]); useEffect(() => { let mounted = true; @@ -3418,19 +3390,9 @@ export default function VideoEditor() { open={projectBrowserOpen} onOpenChange={setProjectBrowserOpen} entries={projectLibraryEntries} - projectsDirectoryPath={projectsDirectoryPath} onOpenProject={(projectPath) => { void handleOpenProjectFromLibrary(projectPath); }} - onBrowseProjectFiles={() => { - void handleLoadProject(); - }} - onOpenProjectsFolder={() => { - void handleOpenProjectsFolder(); - }} - onSaveProjectAs={() => { - void handleSaveProjectAs(); - }} />