Files
XCDesktop/src/pages/PopoutPage.tsx
ssdfasd aa5895873b feat: transfer tab state to popout window and close tab in main window
- Restore transfer-tab-data IPC for transferring tab state
- Create usePopOutTab hook to receive tab data in new window
- Update handlePopOut to transfer data and close tab in main window
- Add PopOutTabData interface for type safety
2026-03-21 23:52:02 +08:00

142 lines
4.7 KiB
TypeScript

import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import type { FileItemDTO } from '@shared/types/file'
import { matchModule } from '@/lib/module-registry'
import { MarkdownTabPage } from '@/components/tabs/MarkdownTabPage'
import { RemoteTabPage } from '@/modules/remote/RemoteTabPage'
import { FileTransferPage } from '@/modules/remote/components/file-transfer/FileTransferPage'
import { Minus, Square, X, Maximize2 } from 'lucide-react'
import { usePopOutTab } from '@/hooks/domain/usePopOutTab'
export const PopoutPage = () => {
const [searchParams] = useSearchParams()
const [file, setFile] = useState<FileItemDTO | null>(null)
const [error, setError] = useState<string | null>(null)
const [isMaximized, setIsMaximized] = useState(false)
usePopOutTab()
useEffect(() => {
const path = searchParams.get('path')
const name = searchParams.get('name')
if (!path || !name) {
setError('Missing path or name parameter')
return
}
setFile({
name: decodeURIComponent(name),
path: decodeURIComponent(path),
type: 'file',
size: 0,
modified: new Date().toISOString(),
})
window.electronAPI?.windowIsMaximized().then((result) => {
if (result.success) {
setIsMaximized(result.isMaximized)
}
})
}, [searchParams])
const handleMinimize = () => {
window.electronAPI?.windowMinimize()
}
const handleMaximize = async () => {
const result = await window.electronAPI?.windowMaximize()
if (result?.success && result.isMaximized !== undefined) {
setIsMaximized(result.isMaximized)
}
}
const handleClose = () => {
window.electronAPI?.windowClose()
}
if (error) {
return (
<div className="flex items-center justify-center h-screen bg-white dark:bg-gray-900">
<div className="text-red-500">{error}</div>
</div>
)
}
if (!file) {
return (
<div className="flex items-center justify-center h-screen bg-white dark:bg-gray-900">
<div className="text-gray-500">Loading...</div>
</div>
)
}
const renderContent = () => {
if (file.path.startsWith('file-transfer-panel')) {
const queryString = file.path.includes('?') ? file.path.split('?')[1] : ''
const urlParams = new URLSearchParams(queryString)
const serverHost = urlParams.get('host') || ''
const port = parseInt(urlParams.get('port') || '3000', 10)
const password = urlParams.get('password') || undefined
return (
<FileTransferPage
serverHost={serverHost}
port={port}
password={password}
onClose={() => window.close()}
/>
)
}
if (file.path.startsWith('remote-desktop://') || file.path.startsWith('remote-git://')) {
const urlParams = new URLSearchParams(file.path.split('?')[1])
const url = urlParams.get('url') || 'https://www.baidu.com'
const deviceName = urlParams.get('device') || ''
return <RemoteTabPage url={url} title={file.name} deviceName={deviceName} />
}
const module = matchModule(file)
if (module) {
const Component = module.component
return <Component />
}
return <MarkdownTabPage file={file} onTocUpdated={() => {}} />
}
return (
<div className="h-screen w-screen flex flex-col bg-white dark:bg-gray-900">
<div className="titlebar-drag-region h-8 flex items-center justify-between px-3 bg-gray-100 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shrink-0">
<span className="text-sm text-gray-700 dark:text-gray-200 truncate">{file.name}</span>
<div className="flex items-center gap-1 titlebar-no-drag">
<button
onClick={handleMinimize}
className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors"
>
<Minus size={14} className="text-gray-600 dark:text-gray-300" />
</button>
<button
onClick={handleMaximize}
className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors"
>
{isMaximized ? (
<Square size={12} className="text-gray-600 dark:text-gray-300" />
) : (
<Maximize2 size={12} className="text-gray-600 dark:text-gray-300" />
)}
</button>
<button
onClick={handleClose}
className="p-1.5 hover:bg-red-500 hover:text-white rounded transition-colors"
>
<X size={14} className="text-gray-600 dark:text-gray-300" />
</button>
</div>
</div>
<div className="flex-1 min-h-0 overflow-hidden">
{renderContent()}
</div>
</div>
)
}