draggable UI resizer
This commit is contained in:
@@ -2,8 +2,8 @@ import { screen, BrowserWindow, ipcMain, desktopCapturer, shell, app, dialog, na
|
||||
import { fileURLToPath } from "node:url";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs/promises";
|
||||
const __dirname$2 = path.dirname(fileURLToPath(import.meta.url));
|
||||
const APP_ROOT = path.join(__dirname$2, "..");
|
||||
const __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
||||
const APP_ROOT = path.join(__dirname$1, "..");
|
||||
const VITE_DEV_SERVER_URL$1 = process.env["VITE_DEV_SERVER_URL"];
|
||||
const RENDERER_DIST$1 = path.join(APP_ROOT, "dist");
|
||||
function createHudOverlayWindow() {
|
||||
@@ -29,7 +29,7 @@ function createHudOverlayWindow() {
|
||||
skipTaskbar: true,
|
||||
hasShadow: false,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname$2, "preload.mjs"),
|
||||
preload: path.join(__dirname$1, "preload.mjs"),
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
backgroundThrottling: false
|
||||
@@ -62,7 +62,7 @@ function createEditorWindow() {
|
||||
title: "OpenScreen",
|
||||
backgroundColor: "#000000",
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname$2, "preload.mjs"),
|
||||
preload: path.join(__dirname$1, "preload.mjs"),
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: false,
|
||||
@@ -98,7 +98,7 @@ function createSourceSelectorWindow() {
|
||||
transparent: true,
|
||||
backgroundColor: "#00000000",
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname$2, "preload.mjs"),
|
||||
preload: path.join(__dirname$1, "preload.mjs"),
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true
|
||||
}
|
||||
@@ -282,7 +282,7 @@ function registerIpcHandlers(createEditorWindow2, createSourceSelectorWindow2, g
|
||||
return { success: true };
|
||||
});
|
||||
}
|
||||
const __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const RECORDINGS_DIR = path.join(app.getPath("userData"), "recordings");
|
||||
async function ensureRecordingsDir() {
|
||||
try {
|
||||
@@ -293,7 +293,7 @@ async function ensureRecordingsDir() {
|
||||
console.error("Failed to create recordings directory:", error);
|
||||
}
|
||||
}
|
||||
process.env.APP_ROOT = path.join(__dirname$1, "..");
|
||||
process.env.APP_ROOT = path.join(__dirname, "..");
|
||||
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
|
||||
const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron");
|
||||
const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist");
|
||||
|
||||
Generated
+31
-6
@@ -28,9 +28,11 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-resizable-panels": "^3.0.6",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.64",
|
||||
@@ -7674,6 +7676,16 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/icon-gen/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-corefoundation": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
|
||||
@@ -10576,6 +10588,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-resizable-panels": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.6.tgz",
|
||||
"integrity": "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||
@@ -12491,13 +12513,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-resizable-panels": "^3.0.6",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function PlaybackControls({
|
||||
const progress = duration > 0 ? (currentTime / duration) * 100 : 0;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 px-4 py-2 rounded-full bg-black/60 backdrop-blur-md border border-white/10 shadow-xl transition-all duration-300 hover:bg-black/70 hover:border-white/20">
|
||||
<div className="flex items-center gap-2 px-1 py-0.5 rounded-full bg-black/60 backdrop-blur-md border border-white/10 shadow-xl transition-all duration-300 hover:bg-black/70 hover:border-white/20">
|
||||
<Button
|
||||
onClick={onTogglePlayPause}
|
||||
size="icon"
|
||||
@@ -50,7 +50,7 @@ export default function PlaybackControls({
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<span className="text-[10px] font-medium text-slate-300 tabular-nums w-[35px] text-right">
|
||||
<span className="text-[9px] font-medium text-slate-300 tabular-nums w-[30px] text-right">
|
||||
{formatTime(currentTime)}
|
||||
</span>
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function PlaybackControls({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span className="text-[10px] font-medium text-slate-500 tabular-nums w-[35px]">
|
||||
<span className="text-[9px] font-medium text-slate-500 tabular-nums w-[30px]">
|
||||
{formatTime(duration)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { toast } from "sonner";
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
||||
|
||||
import VideoPlayback, { VideoPlaybackRef } from "./VideoPlayback";
|
||||
import PlaybackControls from "./PlaybackControls";
|
||||
@@ -311,62 +312,72 @@ export default function VideoEditor() {
|
||||
<div className="flex-1" />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 p-4 gap-4 flex min-h-0 relative">
|
||||
<div className="flex-1 p-5 gap-4 flex min-h-0 relative">
|
||||
{/* Left Column - Video & Timeline */}
|
||||
<div className="flex-[7] flex flex-col gap-3 min-w-0 h-full">
|
||||
{/* Top section: video preview and controls */}
|
||||
<div className="w-full flex flex-col items-center justify-center bg-black/40 rounded-2xl border border-white/5 shadow-2xl overflow-hidden" style={{ height: '80%' }}>
|
||||
{/* Video preview */}
|
||||
<div className="w-full flex justify-center items-center" style={{ flex: '1 1 auto', padding: '24px 0' }}>
|
||||
<div className="relative" style={{ width: '100%', maxWidth: '1000px', aspectRatio: '16/9', boxSizing: 'border-box', overflow: 'hidden' }}>
|
||||
<VideoPlayback
|
||||
ref={videoPlaybackRef}
|
||||
videoPath={videoPath || ''}
|
||||
onDurationChange={setDuration}
|
||||
onTimeUpdate={setCurrentTime}
|
||||
onPlayStateChange={setIsPlaying}
|
||||
onError={setError}
|
||||
wallpaper={wallpaper}
|
||||
<PanelGroup direction="vertical" className="gap-3">
|
||||
{/* Top section: video preview and controls */}
|
||||
<Panel defaultSize={45} minSize={40}>
|
||||
<div className="w-full h-full flex flex-col items-center justify-center bg-black/40 rounded-2xl border border-white/5 shadow-2xl overflow-hidden">
|
||||
{/* Video preview */}
|
||||
<div className="w-full flex justify-center items-center" style={{ flex: '1 1 auto', margin: '6px 0 0' }}>
|
||||
<div className="relative" style={{ width: 'auto', height: '100%', aspectRatio: '16/9', maxWidth: '100%', margin: '0 auto', boxSizing: 'border-box' }}>
|
||||
<VideoPlayback
|
||||
ref={videoPlaybackRef}
|
||||
videoPath={videoPath || ''}
|
||||
onDurationChange={setDuration}
|
||||
onTimeUpdate={setCurrentTime}
|
||||
onPlayStateChange={setIsPlaying}
|
||||
onError={setError}
|
||||
wallpaper={wallpaper}
|
||||
zoomRegions={zoomRegions}
|
||||
selectedZoomId={selectedZoomId}
|
||||
onSelectZoom={handleSelectZoom}
|
||||
onZoomFocusChange={handleZoomFocusChange}
|
||||
isPlaying={isPlaying}
|
||||
showShadow={shadowIntensity > 0}
|
||||
shadowIntensity={shadowIntensity}
|
||||
showBlur={showBlur}
|
||||
cropRegion={cropRegion}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* Playback controls */}
|
||||
<div className="w-full flex justify-center items-center" style={{ height: '48px', flexShrink: 0, padding: '6px 12px', margin: '6px 0 6px 0' }}>
|
||||
<div style={{ width: '100%', maxWidth: '700px' }}>
|
||||
<PlaybackControls
|
||||
isPlaying={isPlaying}
|
||||
currentTime={currentTime}
|
||||
duration={duration}
|
||||
onTogglePlayPause={togglePlayPause}
|
||||
onSeek={handleSeek}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<PanelResizeHandle className="h-3 bg-[#09090b]/80 hover:bg-[#09090b] transition-colors rounded-full mx-4 flex items-center justify-center">
|
||||
<div className="w-8 h-1 bg-white/20 rounded-full"></div>
|
||||
</PanelResizeHandle>
|
||||
|
||||
{/* Timeline section */}
|
||||
<Panel defaultSize={55} minSize={20}>
|
||||
<div className="h-full bg-[#09090b] rounded-2xl border border-white/5 shadow-lg overflow-hidden flex flex-col">
|
||||
<TimelineEditor
|
||||
videoDuration={duration}
|
||||
currentTime={currentTime}
|
||||
onSeek={handleSeek}
|
||||
zoomRegions={zoomRegions}
|
||||
onZoomAdded={handleZoomAdded}
|
||||
onZoomSpanChange={handleZoomSpanChange}
|
||||
onZoomDelete={handleZoomDelete}
|
||||
selectedZoomId={selectedZoomId}
|
||||
onSelectZoom={handleSelectZoom}
|
||||
onZoomFocusChange={handleZoomFocusChange}
|
||||
isPlaying={isPlaying}
|
||||
showShadow={shadowIntensity > 0}
|
||||
shadowIntensity={shadowIntensity}
|
||||
showBlur={showBlur}
|
||||
cropRegion={cropRegion}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* Playback controls */}
|
||||
<div className="w-full flex justify-center items-center" style={{ padding: '0 0 24px 0' }}>
|
||||
<div style={{ maxWidth: '700px', width: '80%' }}>
|
||||
<PlaybackControls
|
||||
isPlaying={isPlaying}
|
||||
currentTime={currentTime}
|
||||
duration={duration}
|
||||
onTogglePlayPause={togglePlayPause}
|
||||
onSeek={handleSeek}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline section */}
|
||||
<div className="flex-1 min-h-[180px] bg-[#09090b] rounded-2xl border border-white/5 shadow-lg overflow-hidden flex flex-col">
|
||||
<TimelineEditor
|
||||
videoDuration={duration}
|
||||
currentTime={currentTime}
|
||||
onSeek={handleSeek}
|
||||
zoomRegions={zoomRegions}
|
||||
onZoomAdded={handleZoomAdded}
|
||||
onZoomSpanChange={handleZoomSpanChange}
|
||||
onZoomDelete={handleZoomDelete}
|
||||
selectedZoomId={selectedZoomId}
|
||||
onSelectZoom={handleSelectZoom}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
</PanelGroup>
|
||||
</div>
|
||||
|
||||
{/* Right section: settings panel */}
|
||||
|
||||
Reference in New Issue
Block a user