feat(onboard/parity): UI 一键接入面板——先计划预览、人确认后接入,四个入口接线(P8g) #41

Merged
lanrtop merged 3 commits from feat/agent-client-parity-p8g into master 2026-10-04 02:24:49 +08:00
5 changed files with 10 additions and 4 deletions
Showing only changes of commit 9d85609287 - Show all commits

View File

@ -338,6 +338,9 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
- depends: P8e
- acceptance: "agent_verify: [pnpm typecheck && pnpm test] 视图纯函数:blocked 非空时确认按钮禁用并列出文件和原因;not_installed 和 not_applicable 显示为灰色说明、不显示为红色失败;bypassPermissions 和 Codex 信任单独列出;确认时携带 expected_digest。human_verify: ① 用临时 git 仓库新建项目,弹出接入预览;② 确认后 30 秒内看到分步结果、L1 矩阵和「待你提交的 N 个文件」;③ 另试一次在预览里取消,目录零改动;④ 在该目录新开 Claude Code、Codex、DSH 桌面 GUI 会话调 LJ_PROBE_NONEXISTENT,得「开发需求未登记」。"
- 说明:新建 / 模板创建成功后弹出「接入预览」,create_project 本身保持无副作用;批量添加只提供批量入口、不自动接入;BlueprintModal 的「应用接入包」改为先 plan 再 apply。
- 实现(2026-10-04,分支 feat/agent-client-parity-p8g,统筹会话主线程直做——两次委派卡死于一次性生成大文件):onboardView.ts 视图纯函数(计划预览、确认请求、报告、L1 矩阵;判定全取后端字段);OnboardPanel.tsx 全局挂载于 App、经 useUIStore.openOnboard 打开(新建弹窗保存即卸载,面板须独立存在),状态机 planning → preview → applying → report,过期响应按请求序号丢弃、接入中不可关闭,refused 附新计划可回预览重新确认;plan.rs 的 BlockedItem 序列化附派生字段 skippable(可跳过判定只在本体,反序列化忽略、不进 digest),commands.ts 同步;入口:ProjectModal 新建且按 platform 的有效根目录非空才弹(概念占位不弹),NewProjectModal 模板创建成功自动弹、完成页可再开,BatchAddModal 添加后列出有目录的新项目逐个「一键接入」,BlueprintModal「施加 Buff」改为同一面板(buff 已激活时另给「重新检查接入」),关闭后按报告刷新 buff 与 init prompt;project-context.yaml 同步 skippable 与 UI 投影。
- 有意偏差:"① 呈现块拆到 OnboardSections.tsx(files 外新增,控制单文件规模)。② Toast 层级 z-50 → z-[70](审查 ②,否则接入提示被面板遮住)。③ 本地 confirm 函数改名 applyConfirmed:confirmUsage.test.ts 扫描 confirm( 防同步确认框,属正确的守卫。"
- review: "2026-10-04 独立只读审查 5b6c197,结论 merge,5 条:① [低] 新建入口按任一路径非空判是否弹面板,与后端按 platform 取有效根目录不同口径 → 已修。② [低] Toast z-50 被面板 z-[60] 遮住 → 已修。③ [低] 已有 permissions 又取消 bypass 时文案称会写 default → 已改文案(如实说明两种情况)。④ [信息] L1 格子着色在前端写死 codex_project_untrusted=待你操作,轻度复制判定 → 转后续小修(L1 格子带 pending_user 字段)。⑤ [信息] onboard_project 抛错后「取消」提示仍称不写入 → 已修(错误阶段换提示)。审查核实:确认带 plan.digest 与生成计划时的选项;refused 回预览时 planned 与新计划同一组选项(apply.rs 用请求选项重算);四条 agent_verify 有实断言。"
### ✅ P9 — DSH 桌面 GUI 按工作区连项目地址(会话级插件)
- status: done

View File

@ -165,7 +165,7 @@ function PanelFooter({
onClick={onCancel}
disabled={phase.kind === "applying"}
className={`${btnCls} border-gray-200 text-gray-600 hover:bg-gray-50`}
title="取消不写入任何文件"
title={phase.kind === "error" ? "关闭面板;若错误发生在确认接入之后,可能已有部分写入,请重新计划核对" : "取消不写入任何文件"}
>
取消
</button>

View File

@ -95,7 +95,9 @@ const pickFolder = async (field: "wsl_path" | "win_path") => {
qc.invalidateQueries({ queryKey: ["tagMaps"] });
closeEdit();
// create_project 本身无副作用;选了目录的新项目弹出接入预览(P8g),由人确认后才写入。不选目录的概念占位不接入
if (isNew && (form.win_path?.trim() || form.wsl_path?.trim())) {
// 与后端登记同口径:按 platform 只看有效根目录那一列(另一列残留不算)
const effectiveRoot = form.platform === "wsl" ? form.wsl_path : form.win_path;
if (isNew && effectiveRoot?.trim()) {
openOnboard({ projectId: form.id!, projectName: form.name?.trim() });
}
} catch (e) {

View File

@ -279,7 +279,8 @@ function bypassView(plan: OnboardingPlan, planned: OnboardSelection, selection:
return {
...base,
label: "已按项目取消 bypassPermissions",
detail: "settings.json 缺 permissions 时写显式 defaultMode=default 记住选择,之后的重注入不会补回。",
detail:
"不写入 bypassPermissions。settings.json 缺 permissions 时会写显式 defaultMode=default 记住选择(之后的重注入不会补回);已有 permissions 则原样不动。",
tone: "muted",
toggleable: true,
};

View File

@ -7,7 +7,7 @@ export function Toast() {
const isError = toast.startsWith("❌");
return (
<div className={`fixed bottom-10 left-1/2 -translate-x-1/2 z-50 text-sm px-4 py-2 rounded-lg shadow-lg animate-fade-in flex items-center gap-2 max-w-[80vw] ${
<div className={`fixed bottom-10 left-1/2 -translate-x-1/2 z-[70] text-sm px-4 py-2 rounded-lg shadow-lg animate-fade-in flex items-center gap-2 max-w-[80vw] ${
isError ? "bg-red-900 text-white pointer-events-auto" : "bg-gray-900 text-white pointer-events-none"
}`}>
<span className="break-all">{toast}</span>