feat: add Electron support for headless exe service
- Add folder selection button in UI - Add Electron main process with IPC handlers - Support --port and --docs command line arguments - Support --headless mode for headless service - Add portable exe build configuration
This commit is contained in:
99
src/App.tsx
99
src/App.tsx
@@ -1,44 +1,87 @@
|
||||
import { useState } from 'react';
|
||||
import { FileText, Box } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { FileText, Box, FolderOpen } from 'lucide-react';
|
||||
import { ApiDocViewer } from './components/ApiDocViewer';
|
||||
import BlueprintPage from './components/blueprint/BlueprintPage';
|
||||
import { config } from './config';
|
||||
|
||||
type Page = 'docs' | 'blueprint';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI?: {
|
||||
selectFolder: () => Promise<string | null>;
|
||||
getDocsPath: () => Promise<string>;
|
||||
onDocsPathChanged: (callback: (path: string) => void) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [currentPage, setCurrentPage] = useState<Page>('docs');
|
||||
const [docsPath, setDocsPath] = useState<string>('');
|
||||
|
||||
document.title = config.projectName;
|
||||
useEffect(() => {
|
||||
document.title = config.projectName;
|
||||
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI.getDocsPath().then(setDocsPath);
|
||||
window.electronAPI.onDocsPathChanged(setDocsPath);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSelectFolder = async () => {
|
||||
if (window.electronAPI) {
|
||||
const path = await window.electronAPI.selectFolder();
|
||||
if (path) {
|
||||
setDocsPath(path);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen flex flex-col">
|
||||
<header className="h-12 bg-zinc-900 border-b border-zinc-800 flex items-center px-4 justify-between">
|
||||
<h1 className="text-sm font-medium text-white">{config.projectName}</h1>
|
||||
<nav className="flex gap-1">
|
||||
<button
|
||||
onClick={() => setCurrentPage('docs')}
|
||||
className={`flex items-center gap-2 px-3 py-1.5 rounded text-sm ${
|
||||
currentPage === 'docs'
|
||||
? 'bg-zinc-800 text-white'
|
||||
: 'text-zinc-400 hover:text-white hover:bg-zinc-800/50'
|
||||
}`}
|
||||
>
|
||||
<FileText size={16} />
|
||||
API 文档
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage('blueprint')}
|
||||
className={`flex items-center gap-2 px-3 py-1.5 rounded text-sm ${
|
||||
currentPage === 'blueprint'
|
||||
? 'bg-zinc-800 text-white'
|
||||
: 'text-zinc-400 hover:text-white hover:bg-zinc-800/50'
|
||||
}`}
|
||||
>
|
||||
<Box size={16} />
|
||||
3D 蓝图
|
||||
</button>
|
||||
</nav>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-sm font-medium text-white">{config.projectName}</h1>
|
||||
{docsPath && (
|
||||
<span className="text-xs text-zinc-500">📁 {docsPath}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{window.electronAPI && (
|
||||
<button
|
||||
onClick={handleSelectFolder}
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded text-sm text-zinc-400 hover:text-white hover:bg-zinc-800/50"
|
||||
title="选择文档文件夹"
|
||||
>
|
||||
<FolderOpen size={16} />
|
||||
</button>
|
||||
)}
|
||||
<nav className="flex gap-1">
|
||||
<button
|
||||
onClick={() => setCurrentPage('docs')}
|
||||
className={`flex items-center gap-2 px-3 py-1.5 rounded text-sm ${
|
||||
currentPage === 'docs'
|
||||
? 'bg-zinc-800 text-white'
|
||||
: 'text-zinc-400 hover:text-white hover:bg-zinc-800/50'
|
||||
}`}
|
||||
>
|
||||
<FileText size={16} />
|
||||
API 文档
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage('blueprint')}
|
||||
className={`flex items-center gap-2 px-3 py-1.5 rounded text-sm ${
|
||||
currentPage === 'blueprint'
|
||||
? 'bg-zinc-800 text-white'
|
||||
: 'text-zinc-400 hover:text-white hover:bg-zinc-800/50'
|
||||
}`}
|
||||
>
|
||||
<Box size={16} />
|
||||
3D 蓝图
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex-1 overflow-hidden">
|
||||
{currentPage === 'docs' ? <ApiDocViewer /> : <BlueprintPage />}
|
||||
|
||||
Reference in New Issue
Block a user