Create standalone browser port of LTX Athena with authenticated native API transport
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { ArrowLeft, Sparkles, Film } from 'lucide-react'
|
||||
import { useProjects } from '../contexts/ProjectContext'
|
||||
import { useView } from '../contexts/ViewContext'
|
||||
import { LtxLogo } from '../components/LtxLogo'
|
||||
import { Button } from '../components/ui/button'
|
||||
import { GenSpace } from './GenSpace'
|
||||
import { VideoEditor } from './VideoEditor'
|
||||
import type { ProjectTab } from '../types/project-model'
|
||||
import {
|
||||
hasVisualAssetMetadataForMigration,
|
||||
runVisualAssetMetadataMigration,
|
||||
} from '../lib/project-asset-metadata-migration'
|
||||
|
||||
export function Project() {
|
||||
const {
|
||||
activeProject,
|
||||
currentTab,
|
||||
setProject,
|
||||
setCurrentTab,
|
||||
updateAsset,
|
||||
pendingRetakeUpdate,
|
||||
setPendingRetakeUpdate,
|
||||
pendingIcLoraUpdate,
|
||||
setPendingIcLoraUpdate,
|
||||
} = useProjects()
|
||||
const { goHome } = useView()
|
||||
const [assetMetadataMigrationProgress, setAssetMetadataMigrationProgress] = useState({ running: false, total: 0, completed: 0 })
|
||||
const [upgradePassProjectId, setUpgradePassProjectId] = useState<string | null>(null)
|
||||
const activeProjectId = activeProject?.id ?? null
|
||||
const activeProjectAssets = activeProject?.assets ?? null
|
||||
const needsAssetMetadataMigration = activeProjectAssets
|
||||
? hasVisualAssetMetadataForMigration(activeProjectAssets)
|
||||
: false
|
||||
|
||||
const handleSaveActiveProject = useCallback((project: typeof activeProject extends null ? never : NonNullable<typeof activeProject>) => {
|
||||
if (!activeProjectId) return
|
||||
setProject(activeProjectId, project)
|
||||
}, [activeProjectId, setProject])
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeProjectId || !activeProjectAssets || !needsAssetMetadataMigration) return
|
||||
|
||||
let cancelled = false
|
||||
|
||||
const runAssetMetadataMigration = async () => {
|
||||
for await (const event of runVisualAssetMetadataMigration(activeProjectAssets, window.electronAPI)) {
|
||||
if (cancelled) return
|
||||
|
||||
if (event.kind === 'progress') {
|
||||
setAssetMetadataMigrationProgress({ running: true, total: event.total, completed: event.completed })
|
||||
continue
|
||||
}
|
||||
|
||||
for (const update of event.updates) {
|
||||
updateAsset(activeProjectId, update.assetId, update.updates)
|
||||
}
|
||||
|
||||
setAssetMetadataMigrationProgress({ running: false, total: 0, completed: 0 })
|
||||
setUpgradePassProjectId(activeProjectId)
|
||||
}
|
||||
}
|
||||
|
||||
void runAssetMetadataMigration()
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [activeProjectAssets, activeProjectId, needsAssetMetadataMigration, updateAsset])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentTab !== 'video-editor') return
|
||||
if (pendingRetakeUpdate) setPendingRetakeUpdate(null)
|
||||
if (pendingIcLoraUpdate) setPendingIcLoraUpdate(null)
|
||||
}, [
|
||||
currentTab,
|
||||
pendingRetakeUpdate,
|
||||
setPendingRetakeUpdate,
|
||||
pendingIcLoraUpdate,
|
||||
setPendingIcLoraUpdate,
|
||||
])
|
||||
|
||||
if (!activeProject) {
|
||||
return (
|
||||
<div className="h-screen bg-background flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<p className="text-zinc-400 mb-4">Project not found</p>
|
||||
<Button onClick={goHome}>Go Home</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const tabs: { id: ProjectTab; label: string; icon: React.ReactNode }[] = [
|
||||
{ id: 'gen-space', label: 'Gen Space', icon: <Sparkles className="h-4 w-4" /> },
|
||||
{ id: 'video-editor', label: 'Video Editor', icon: <Film className="h-4 w-4" /> },
|
||||
]
|
||||
const shouldShowAssetMetadataMigrationProgressScreen = assetMetadataMigrationProgress.running
|
||||
|| (upgradePassProjectId !== activeProjectId && needsAssetMetadataMigration)
|
||||
|
||||
if (shouldShowAssetMetadataMigrationProgressScreen) {
|
||||
const progressPct = assetMetadataMigrationProgress.total > 0
|
||||
? (assetMetadataMigrationProgress.completed / assetMetadataMigrationProgress.total) * 100
|
||||
: 0
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-background flex items-center justify-center">
|
||||
<div className="w-[360px]">
|
||||
<p className="text-center text-sm text-zinc-300 mb-4">
|
||||
Preparing your project assets...
|
||||
</p>
|
||||
<div className="h-2 w-full rounded-full bg-zinc-800 overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-blue-500 transition-all duration-150"
|
||||
style={{ width: `${Math.max(0, Math.min(100, progressPct))}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-background flex flex-col">
|
||||
{/* Header */}
|
||||
<header className="flex items-center px-4 py-3 border-b border-zinc-800">
|
||||
<div className="flex-1 flex items-center gap-4">
|
||||
{/* Back button and logo */}
|
||||
<button
|
||||
onClick={goHome}
|
||||
className="p-2 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5 text-zinc-400" />
|
||||
</button>
|
||||
|
||||
<LtxLogo className="h-5 w-auto text-white" />
|
||||
|
||||
{/* Project name */}
|
||||
<span className="text-white font-medium">{activeProject.name}</span>
|
||||
</div>
|
||||
|
||||
{/* Center - Tabs */}
|
||||
<div className="flex items-center gap-1 bg-zinc-900 rounded-lg p-1">
|
||||
{tabs.map(tab => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setCurrentTab(tab.id)}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
currentTab === tab.id
|
||||
? 'bg-zinc-800 text-white'
|
||||
: 'text-zinc-400 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{tab.icon}
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right spacer - equal to left to keep tabs centered */}
|
||||
<div className="flex-1" />
|
||||
</header>
|
||||
|
||||
<main className="flex-1 overflow-hidden relative">
|
||||
{currentTab === 'gen-space' ? (
|
||||
<GenSpace />
|
||||
) : (
|
||||
<VideoEditor
|
||||
key={activeProject.id}
|
||||
currentProject={activeProject}
|
||||
saveProject={handleSaveActiveProject}
|
||||
pendingRetakeUpdate={pendingRetakeUpdate}
|
||||
pendingIcLoraUpdate={pendingIcLoraUpdate}
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user