2026-08-14 14:34:27 +08:00

130 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
),
),
);
}