improved chapters/markers
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useEffect, useState, useMemo, useCallback, useRef } from 'react';
|
||||
import { useEditorStore } from './store/editorStore';
|
||||
import VideoPlayer from './components/VideoPlayer';
|
||||
import TranscriptEditor from './components/TranscriptEditor';
|
||||
@ -68,6 +68,54 @@ export default function App() {
|
||||
|
||||
const [activePanel, setActivePanel] = useState<Panel>(null);
|
||||
const [projectName, setProjectName] = useState<string | null>(null);
|
||||
const [splitRatio, setSplitRatio] = useState(() => {
|
||||
try { return Number(localStorage.getItem('talkedit:splitRatio')) || 0.5; } catch { return 0.5; }
|
||||
});
|
||||
const splitRef = useRef<HTMLDivElement>(null);
|
||||
const isDraggingSplit = useRef(false);
|
||||
|
||||
const startSplitDrag = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
isDraggingSplit.current = true;
|
||||
const container = splitRef.current?.parentElement;
|
||||
if (!container) return;
|
||||
const rect = container.getBoundingClientRect();
|
||||
const onMove = (me: MouseEvent) => {
|
||||
if (!isDraggingSplit.current) return;
|
||||
const pct = (me.clientX - rect.left) / rect.width;
|
||||
const clamped = Math.max(0.15, Math.min(0.85, pct));
|
||||
setSplitRatio(clamped);
|
||||
localStorage.setItem('talkedit:splitRatio', String(clamped));
|
||||
};
|
||||
const onUp = () => { isDraggingSplit.current = false; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
|
||||
window.addEventListener('mousemove', onMove);
|
||||
window.addEventListener('mouseup', onUp);
|
||||
}, []);
|
||||
|
||||
// Draggable right sidebar
|
||||
const [sidebarWidth, setSidebarWidth] = useState(() => {
|
||||
try { return Number(localStorage.getItem('talkedit:sidebarWidth')) || 320; } catch { return 320; }
|
||||
});
|
||||
const isDraggingSidebar = useRef(false);
|
||||
|
||||
const startSidebarDrag = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
isDraggingSidebar.current = true;
|
||||
const container = document.querySelector('.main-content') as HTMLElement;
|
||||
if (!container) return;
|
||||
const rect = container.getBoundingClientRect();
|
||||
const onMove = (me: MouseEvent) => {
|
||||
if (!isDraggingSidebar.current) return;
|
||||
const w = rect.right - me.clientX;
|
||||
const clamped = Math.max(180, Math.min(600, w));
|
||||
setSidebarWidth(clamped);
|
||||
localStorage.setItem('talkedit:sidebarWidth', String(clamped));
|
||||
};
|
||||
const onUp = () => { isDraggingSidebar.current = false; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
|
||||
window.addEventListener('mousemove', onMove);
|
||||
window.addEventListener('mouseup', onUp);
|
||||
}, []);
|
||||
|
||||
const [whisperModel, setWhisperModel] = useState('base');
|
||||
useEffect(() => { if (transcriptionModel) setWhisperModel(transcriptionModel); }, [transcriptionModel]);
|
||||
const [cutMode, setCutMode] = useState(false);
|
||||
@ -666,17 +714,24 @@ export default function App() {
|
||||
</header>
|
||||
|
||||
{/* Main content */}
|
||||
<div className="flex-1 flex overflow-hidden">
|
||||
<div className="main-content flex-1 flex overflow-hidden">
|
||||
{/* Left: video + transcript */}
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
<div className="flex-1 flex min-h-0">
|
||||
<div ref={splitRef} className="flex-1 flex min-h-0" style={{ position: 'relative' }}>
|
||||
{/* Video player */}
|
||||
<div className="w-1/2 p-3 flex items-center justify-center bg-black/20">
|
||||
<div className="p-3 flex items-center justify-center bg-black/20 overflow-hidden" style={{ width: `${splitRatio * 100}%`, minWidth: 0 }}>
|
||||
<VideoPlayer />
|
||||
</div>
|
||||
|
||||
{/* Draggable divider */}
|
||||
<div
|
||||
className="w-1 shrink-0 bg-editor-border cursor-col-resize hover:bg-editor-accent/50 active:bg-editor-accent transition-colors relative z-10"
|
||||
style={{ cursor: isDraggingSplit.current ? 'col-resize' : 'col-resize' }}
|
||||
onMouseDown={startSplitDrag}
|
||||
/>
|
||||
|
||||
{/* Transcript */}
|
||||
<div className="w-1/2 border-l border-editor-border flex flex-col min-h-0">
|
||||
<div className="border-l border-editor-border flex flex-col min-h-0" style={{ width: `${(1 - splitRatio) * 100}%`, minWidth: 0 }}>
|
||||
{videoPath && (
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 border-b border-editor-border shrink-0 bg-editor-surface/50">
|
||||
{projectName && (
|
||||
@ -745,7 +800,13 @@ export default function App() {
|
||||
|
||||
{/* Right panel (AI / Export / Settings) */}
|
||||
{activePanel && (
|
||||
<div className="w-80 border-l border-editor-border overflow-y-auto shrink-0">
|
||||
<div className="flex shrink-0">
|
||||
{/* Draggable sidebar divider */}
|
||||
<div
|
||||
className="w-1 shrink-0 bg-editor-border cursor-col-resize hover:bg-editor-accent/50 active:bg-editor-accent transition-colors relative z-10"
|
||||
onMouseDown={startSidebarDrag}
|
||||
/>
|
||||
<div className="overflow-y-auto" style={{ width: sidebarWidth }}>
|
||||
{activePanel === 'zones' && (
|
||||
<ZoneEditor />
|
||||
)}
|
||||
@ -755,7 +816,8 @@ export default function App() {
|
||||
{activePanel === 'export' && <ExportDialog />}
|
||||
{activePanel === 'settings' && <SettingsPanel />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{import.meta.env.DEV && <DevPanel />}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user