Files
Genarrative/apps/ai-game-creator-shell/src/features/app-shell/ProjectCreation.tsx
T
AIGameCreator App be89296492 拆分 AI 游戏创作客户端大型模块
拆分 App 认证、壳层、运行配置与项目摘要模块
拆分 Tauri 项目能力与 Rust 测试领域模块
拆分界面测试与 Agent Runtime 真实 E2E 套件
补充源码扫描和客户端模块化文档约定
2026-07-21 22:53:29 +08:00

204 lines
5.9 KiB
TypeScript

import { FolderKanban } from 'lucide-react';
import { closeDialogOnEscape } from '../../app/dialogs';
import type { HomeProjectCreationController } from './useHomeProjectCreation';
import type { RecentProjectsController } from './useRecentProjects';
export function ProjectsPage({
status,
homeProject,
recentProjects,
}: {
status: string;
homeProject: HomeProjectCreationController;
recentProjects: RecentProjectsController;
}) {
return (
<section className="launcher-page launcher-projects-page">
<header>
<div>
<h1>项目组</h1>
<p>
{recentProjects.recentWorkspaceRefreshing
? '正在检查项目状态'
: status}
</p>
</div>
<div className="launcher-project-list-actions">
<button
type="button"
disabled={recentProjects.recentWorkspaceRefreshing}
onClick={recentProjects.handleRecentWorkspaceRefresh}
>
{recentProjects.recentWorkspaceRefreshing ? '刷新中' : '刷新'}
</button>
<button
type="button"
onClick={recentProjects.handleRecentWorkspaceClear}
>
清空
</button>
</div>
</header>
<form
className="launcher-project-form"
onSubmit={homeProject.handleSubmit}
>
<label>
项目目录
<input
aria-label="项目目录"
value={homeProject.projectPath}
onChange={(event) =>
homeProject.setProjectPath(event.currentTarget.value)
}
/>
</label>
<div className="launcher-page-actions">
<button
type="button"
onClick={() => {
void homeProject.handlePickProjectDirectory();
}}
>
选择
</button>
<button type="submit">打开</button>
<button
type="button"
onClick={() => {
void homeProject.openProject(homeProject.projectPath, 'create');
}}
>
新建项目
</button>
<button
type="button"
onClick={() => {
void recentProjects.handleRevealProjectDirectory(
homeProject.projectPath,
);
}}
>
显示目录
</button>
</div>
</form>
<div className="launcher-project-table">
{recentProjects.projectRows.length > 0 ? (
recentProjects.projectRows.map((project) => (
<article key={project.path}>
<button
type="button"
className="launcher-project-row-main"
disabled={!project.canOpen}
onClick={() => {
homeProject.setProjectPath(project.path);
void homeProject.openProject(project.path, 'open');
}}
>
<strong>{project.name}</strong>
<small>{project.path}</small>
</button>
<span>{project.status}</span>
<button
type="button"
disabled={!project.canOpen}
onClick={() => {
homeProject.setProjectPath(project.path);
void homeProject.openProject(project.path, 'open');
}}
>
打开
</button>
<button
type="button"
aria-label={`显示 ${project.path}`}
disabled={!project.canReveal}
onClick={() => {
void recentProjects.handleRevealProjectDirectory(
project.path,
);
}}
>
显示
</button>
<button
type="button"
aria-label={`移除 ${project.path}`}
onClick={() =>
recentProjects.handleRecentWorkspaceRemove(project.path)
}
>
移除
</button>
</article>
))
) : (
<div className="launcher-empty-projects">
<FolderKanban size={28} aria-hidden="true" />
<strong>暂无项目</strong>
<button
type="button"
onClick={() => {
void homeProject.handlePickProjectDirectory();
}}
>
选择本地项目目录
</button>
</div>
)}
</div>
</section>
);
}
export function NonEmptyProjectDialog({
controller,
}: {
controller: HomeProjectCreationController;
}) {
const pendingProject = controller.pendingNonEmptyProject;
if (!pendingProject) {
return null;
}
return (
<div
className="launcher-dialog-backdrop"
role="presentation"
onMouseDown={(event) => {
if (event.target === event.currentTarget) {
controller.cancelCreateInNonEmptyFolder();
}
}}
>
<section
aria-labelledby="launcher-non-empty-title"
aria-modal="true"
className="launcher-dialog"
role="dialog"
onKeyDown={(event) =>
closeDialogOnEscape(event, controller.cancelCreateInNonEmptyFolder)
}
>
<h2 id="launcher-non-empty-title">文件夹不是空的</h2>
<p>{pendingProject.projectPath}</p>
<div className="launcher-dialog-actions">
<button
type="button"
onClick={controller.cancelCreateInNonEmptyFolder}
>
取消
</button>
<button
type="button"
onClick={() => void controller.confirmCreateInNonEmptyFolder()}
>
继续新建
</button>
</div>
</section>
</div>
);
}