130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
import van from "vanjs-core";
|
||
import type { AppController } from "../../app/app-controller";
|
||
import "./project-panel.css";
|
||
|
||
const { button, div, form, h1, input, label, p, span } = van.tags;
|
||
|
||
export function ProjectPanel(controller: AppController): HTMLElement {
|
||
const selection = controller.selection.val;
|
||
if (selection.kind === "project-create")
|
||
return ProjectForm("新建项目", "创建项目", "", "", (name, root) =>
|
||
controller.createProject(name, root),
|
||
);
|
||
if (!("projectId" in selection)) return div();
|
||
const project = controller.projects.val.find(
|
||
(item) => item.id === selection.projectId,
|
||
);
|
||
if (!project) return div({ class: "project-panel" }, p("项目不存在"));
|
||
if (selection.kind === "project-rename")
|
||
return ProjectForm(
|
||
"重命名项目",
|
||
"保存",
|
||
project.name,
|
||
project.workspaceRoot,
|
||
(name) => controller.renameProject(project.id, name),
|
||
true,
|
||
);
|
||
if (selection.kind === "project-delete")
|
||
return div(
|
||
{ class: "project-panel" },
|
||
span({ class: "project-icon danger" }, "×"),
|
||
h1("删除项目?"),
|
||
p("将删除应用内的项目记录,以及该项目下的对话、消息和运行记录。"),
|
||
div(
|
||
{ class: "delete-summary" },
|
||
p("项目", span(project.name)),
|
||
p("项目对话", span(`${project.conversationCount} 条`)),
|
||
p("本地工作目录", span("不会删除、移动或写入")),
|
||
),
|
||
div(
|
||
{ class: "form-actions" },
|
||
button(
|
||
{
|
||
class: "secondary",
|
||
onclick: () => controller.startProjectTask(project.id),
|
||
},
|
||
"取消",
|
||
),
|
||
button(
|
||
{
|
||
class: "danger-button",
|
||
onclick: () => void controller.deleteProject(project.id),
|
||
},
|
||
"确认删除",
|
||
),
|
||
),
|
||
);
|
||
return div(
|
||
{ class: "project-welcome" },
|
||
span({ class: "project-icon" }, "▣"),
|
||
h1(project.name),
|
||
p(project.workspaceRoot),
|
||
!project.workspaceAvailable
|
||
? p(
|
||
{ class: "project-unavailable" },
|
||
"项目目录当前不可用。历史对话仍可查看,但无法开始新任务。",
|
||
)
|
||
: p("开始一段新的项目对话,第一条消息发送后才会创建会话。"),
|
||
);
|
||
}
|
||
|
||
function ProjectForm(
|
||
title: string,
|
||
submitLabel: string,
|
||
initialName: string,
|
||
initialRoot: string,
|
||
submit: (name: string, root: string) => Promise<void>,
|
||
rename = false,
|
||
): HTMLElement {
|
||
const name = van.state(initialName);
|
||
const root = van.state(initialRoot);
|
||
return form(
|
||
{
|
||
class: "project-panel",
|
||
onsubmit: (event: Event) => {
|
||
event.preventDefault();
|
||
void submit(name.val, root.val);
|
||
},
|
||
},
|
||
span({ class: "project-icon" }, "▣"),
|
||
h1(title),
|
||
p(
|
||
rename
|
||
? "修改侧栏中显示的项目名称。"
|
||
: "选择一个已存在、可读写的本地文件夹作为项目目录。",
|
||
),
|
||
label(
|
||
"项目名称",
|
||
input({
|
||
value: name,
|
||
maxlength: 80,
|
||
autofocus: true,
|
||
oninput: (event: Event) =>
|
||
(name.val = (event.target as HTMLInputElement).value),
|
||
}),
|
||
),
|
||
rename
|
||
? null
|
||
: label(
|
||
"本地目录(绝对路径)",
|
||
input({
|
||
value: root,
|
||
placeholder: "/Users/你的名字/Projects/项目名",
|
||
oninput: (event: Event) =>
|
||
(root.val = (event.target as HTMLInputElement).value),
|
||
}),
|
||
),
|
||
div(
|
||
{ class: "form-actions" },
|
||
button(
|
||
{
|
||
class: "primary",
|
||
type: "submit",
|
||
disabled: () => !name.val.trim() || (!rename && !root.val.trim()),
|
||
},
|
||
submitLabel,
|
||
),
|
||
),
|
||
);
|
||
}
|