be89296492
拆分 App 认证、壳层、运行配置与项目摘要模块 拆分 Tauri 项目能力与 Rust 测试领域模块 拆分界面测试与 Agent Runtime 真实 E2E 套件 补充源码扫描和客户端模块化文档约定
204 lines
5.9 KiB
TypeScript
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>
|
|
);
|
|
}
|