added licensing stuff and free trial timer
This commit is contained in:
@ -12,7 +12,9 @@ import SilenceTrimmerPanel from './components/SilenceTrimmerPanel';
|
||||
import ZoneEditor from './components/ZoneEditor';
|
||||
import BackgroundMusicPanel from './components/BackgroundMusicPanel';
|
||||
import AppendClipPanel from './components/AppendClipPanel';
|
||||
import LicenseDialog from './components/LicenseDialog';
|
||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts';
|
||||
import { useLicenseStore } from './store/licenseStore';
|
||||
import {
|
||||
Film,
|
||||
FolderOpen,
|
||||
@ -132,6 +134,8 @@ export default function App() {
|
||||
const [showUnsavedPrompt, setShowUnsavedPrompt] = useState(false);
|
||||
const [pendingProceedAction, setPendingProceedAction] = useState<(() => Promise<void>) | null>(null);
|
||||
const [lastSavedSignature, setLastSavedSignature] = useState<string | null>(null);
|
||||
const [showFileMenu, setShowFileMenu] = useState(false);
|
||||
const canEdit = useLicenseStore((s) => s.canEdit);
|
||||
|
||||
const projectSignature = useMemo(() => {
|
||||
if (!videoPath) return null;
|
||||
@ -196,7 +200,11 @@ export default function App() {
|
||||
|
||||
useKeyboardShortcuts();
|
||||
|
||||
// Handle Escape key to exit timeline zone modes
|
||||
useEffect(() => {
|
||||
useLicenseStore.getState().checkStatus();
|
||||
}, []);
|
||||
|
||||
// Handle Escape key to exit timeline zone modes and close menus
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
@ -204,9 +212,9 @@ export default function App() {
|
||||
setMuteMode(false);
|
||||
setGainMode(false);
|
||||
setSpeedMode(false);
|
||||
setShowFileMenu(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
@ -287,7 +295,9 @@ export default function App() {
|
||||
setProjectFilePath(null);
|
||||
setProjectName(null);
|
||||
loadVideo(path);
|
||||
await transcribeVideo(path);
|
||||
if (canEdit) {
|
||||
await transcribeVideo(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -562,44 +572,40 @@ export default function App() {
|
||||
{/* Top bar */}
|
||||
<header className="h-12 flex items-center px-4 border-b border-editor-border shrink-0">
|
||||
<div className="flex items-center gap-0.5">
|
||||
<ToolbarButton
|
||||
icon={<FilePlus2 className="w-4 h-4" />}
|
||||
label="New"
|
||||
onClick={handleNewProject}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<FolderOpen className="w-4 h-4" />}
|
||||
label="Open"
|
||||
onClick={handleOpenFile}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<Save className="w-4 h-4" />}
|
||||
label="Save"
|
||||
onClick={handleSaveProject}
|
||||
disabled={words.length === 0}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<Save className="w-4 h-4" />}
|
||||
label="Save As"
|
||||
onClick={handleSaveProjectAs}
|
||||
disabled={words.length === 0}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<FileInput className="w-4 h-4" />}
|
||||
label="Load"
|
||||
onClick={handleLoadProject}
|
||||
/>
|
||||
<div className="relative">
|
||||
<ToolbarButton
|
||||
icon={<FolderOpen className="w-4 h-4" />}
|
||||
label="File"
|
||||
onClick={() => setShowFileMenu((p) => !p)}
|
||||
active={showFileMenu}
|
||||
/>
|
||||
{showFileMenu && (
|
||||
<>
|
||||
<div className="fixed inset-0 z-40" onClick={() => setShowFileMenu(false)} />
|
||||
<div className="absolute left-0 top-full mt-1 z-50 w-44 rounded-lg border border-editor-border bg-editor-surface shadow-xl py-1">
|
||||
<DropdownItem icon={<FilePlus2 className="w-4 h-4" />} label="New Project" onClick={() => { setShowFileMenu(false); handleNewProject(); }} />
|
||||
<DropdownItem icon={<FolderOpen className="w-4 h-4" />} label="Open File" onClick={() => { setShowFileMenu(false); handleOpenFile(); }} />
|
||||
<DropdownItem icon={<FileInput className="w-4 h-4" />} label="Load Project" onClick={() => { setShowFileMenu(false); handleLoadProject(); }} />
|
||||
<div className="border-t border-editor-border my-1" />
|
||||
<DropdownItem icon={<Save className="w-4 h-4" />} label="Save" onClick={() => { setShowFileMenu(false); handleSaveProject(); }} disabled={words.length === 0} />
|
||||
<DropdownItem icon={<Save className="w-4 h-4" />} label="Save As" onClick={() => { setShowFileMenu(false); handleSaveProjectAs(); }} disabled={words.length === 0} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<ToolbarButton
|
||||
icon={<Scissors className="w-4 h-4" />}
|
||||
label="Cut"
|
||||
onClick={handleCut}
|
||||
active={cutMode}
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<VolumeX className="w-4 h-4" />}
|
||||
label="Mute"
|
||||
onClick={handleMute}
|
||||
active={muteMode}
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<ToolbarButton
|
||||
@ -607,6 +613,7 @@ export default function App() {
|
||||
label="Gain Zone"
|
||||
onClick={handleGain}
|
||||
active={gainMode}
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
@ -617,6 +624,7 @@ export default function App() {
|
||||
onChange={(e) => setGainModeDb(Math.max(-24, Math.min(24, Number(e.target.value) || 0)))}
|
||||
className="w-16 px-1.5 py-1 text-xs bg-editor-surface border border-editor-border rounded text-editor-text focus:outline-none focus:border-editor-accent"
|
||||
title="Gain dB for new gain zones"
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
@ -625,6 +633,7 @@ export default function App() {
|
||||
label="Speed Zone"
|
||||
onClick={handleSpeed}
|
||||
active={speedMode}
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
@ -635,6 +644,7 @@ export default function App() {
|
||||
onChange={(e) => setSpeedModeValue(Math.max(0.25, Math.min(4, Number(e.target.value) || 1)))}
|
||||
className="w-16 px-1.5 py-1 text-xs bg-editor-surface border border-editor-border rounded text-editor-text focus:outline-none focus:border-editor-accent"
|
||||
title="Playback rate for new speed zones"
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
</div>
|
||||
<ToolbarButton
|
||||
@ -642,35 +652,35 @@ export default function App() {
|
||||
label="Zones"
|
||||
active={activePanel === 'zones'}
|
||||
onClick={() => togglePanel('zones')}
|
||||
disabled={!videoPath}
|
||||
disabled={!videoPath || !canEdit}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<span className="text-[10px] font-semibold">PA</span>}
|
||||
label="Pause Trim"
|
||||
active={activePanel === 'silence'}
|
||||
onClick={() => togglePanel('silence')}
|
||||
disabled={!videoPath}
|
||||
disabled={!videoPath || !canEdit}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<MapPin className="w-4 h-4" />}
|
||||
label="Markers"
|
||||
active={activePanel === 'markers'}
|
||||
onClick={() => togglePanel('markers')}
|
||||
disabled={!videoPath}
|
||||
disabled={!videoPath || !canEdit}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<Music className="w-4 h-4" />}
|
||||
label="Music"
|
||||
active={activePanel === 'music'}
|
||||
onClick={() => togglePanel('music')}
|
||||
disabled={!videoPath}
|
||||
disabled={!videoPath || !canEdit}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<ListVideo className="w-4 h-4" />}
|
||||
label="Append"
|
||||
active={activePanel === 'append'}
|
||||
onClick={() => togglePanel('append')}
|
||||
disabled={!videoPath}
|
||||
disabled={!videoPath || !canEdit}
|
||||
/>
|
||||
<div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-editor-surface border border-editor-border">
|
||||
<select
|
||||
@ -700,7 +710,7 @@ export default function App() {
|
||||
</select>
|
||||
<button
|
||||
onClick={handleReprocessProject}
|
||||
disabled={isTranscribing || !videoPath}
|
||||
disabled={isTranscribing || !videoPath || !canEdit}
|
||||
title="Reprocess transcript with selected model"
|
||||
className="flex items-center gap-1 px-2 py-1 rounded text-xs text-editor-text hover:bg-editor-bg disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
@ -713,7 +723,7 @@ export default function App() {
|
||||
label="AI"
|
||||
active={activePanel === 'ai'}
|
||||
onClick={() => togglePanel('ai')}
|
||||
disabled={words.length === 0}
|
||||
disabled={words.length === 0 || !canEdit}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={<Download className="w-4 h-4" />}
|
||||
@ -841,6 +851,8 @@ export default function App() {
|
||||
</div>
|
||||
{import.meta.env.DEV && <DevPanel />}
|
||||
|
||||
<LicenseDialog />
|
||||
|
||||
{showReprocessConfirm && (
|
||||
<div
|
||||
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/60 px-4"
|
||||
@ -941,3 +953,30 @@ function ToolbarButton({
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownItem({
|
||||
icon,
|
||||
label,
|
||||
onClick,
|
||||
disabled,
|
||||
}: {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={`w-full flex items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors ${
|
||||
disabled
|
||||
? 'opacity-40 cursor-not-allowed'
|
||||
: 'text-editor-text-muted hover:text-editor-text hover:bg-editor-bg'
|
||||
}`}
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user