继续扩展共享组件并收口入口页面

新增 PlatformBackActionButton 共享返回动作与行为测试

迁移登录、绑定手机号和世界实体目录的共享组件直引

扩展示展示页示例并同步组件库文档与决策记录
This commit is contained in:
2026-08-31 11:53:04 +08:00
parent aadb09a9ea
commit 7e7ef50e40
10 changed files with 152 additions and 103 deletions
@@ -1,4 +1,5 @@
# 决策记录
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
> 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。
@@ -36,6 +37,7 @@
- 决策:三个工具调用的 arguments 统一限制为 `1 MiB`,先解析通用 JSON 并迭代检查,再进入递归业务类型。结构识别按每棵树独立限制 `512` 个 LLM 节点 / `32` 层,不跨树求和且不计 Rust 页面根;语义建议限制 `4` 节点 / `4` 层;合并计划限制 `512` 节点 / `32` 层。超限整次拒绝,不截断或交付部分结果,日志不记录 arguments 正文。
- 输入边界:`merge_ui` 继续直接接收 `State`,不修改 Tauri/frontend IPC 参数;进入 Rust 后、发起 LLM 前按每棵源树独立限制 `512` 节点 / `32` 层,不跨树求和,并限制 `2 MiB` 序列化投影。UI 设计参考图只设单张 `5 MiB` 上限,不设批次合计或像素数上限;元数据检查、有限读取和 base64 编码进入 blocking worker,不新增命令超时。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
## 2026-08-19 M1 审查后四项可靠性修复
- **审批 stale 收口**`status``phase``needs-reconciliation` 的策划根不再满足审批所需的 active 身份;审批命令返回 `PLAN_STALE_APPROVAL`,并且不得创建 approval receipt。
@@ -80,6 +82,7 @@
- **验证**`appSurface.test.ts` **381 passed / 0 failed**378 既有 + 3 新增);`agentTraceSummary``rememberCommand`(另两个 import `src/App` 的用例文件)13 passed`agc:typecheck` 通过;6 个改动/新增文件 ESLint `--max-warnings 0` 通过;`check:encoding` 5409 文件通过。不改 Rust——三条全在前端,后端语义已经正确。
- **对既有记录的更正**M1D-1 与 M1D-2 两条记录分别称「Shell TypeScript typecheck 仍被仓库既有依赖缺失阻断」「appSurface UI suite 受仓库现有缺失 Tauri plugin 依赖阻断,未把该基线失败归因于本包」,在原分支主工作树上都不成立:`agc:typecheck` 干净退出,appSurface 378 条全绿;两道门分别位于 CI 的 `check:native-shells`(且 typecheck 排在 cargo test 之前)与 Frontend tests 内,一直是活的。实际情况与记录相反——`bf2185fba` 改名 `taskGroupLabels.design` 后,appSurface 有 8 个用例文件的断言变红,随后由 `c6a08ef98` 修复;把该套件记为「基线阻断、不归因本包」正是让这条自带回归合入的原因。**隔离工作树的依赖缺失不能作为跳过门禁的依据,须回原工作树复跑后再下结论。**
- **未修的审查发现(本次不并入,单列后续)**:① hydrate 在校验 GDD/session 的 projectId 与 manifest 一致之前,已执行 `reconcile_plan_gdd_approval_projections_at`、session previous 提升与 index 重建等落盘修复,违反第 18.3 节固定顺序,其中 `session.previous.json` 的提升+删除不可逆(触发需外部篡改 `.agent/`,App 自身流程造不出该分歧);② 项目写锁竞争时 `acquire_project_write_lock` 的错误原文内嵌项目绝对路径,被原样回传前端,违反第 18.3 节「返回值不包含绝对路径」,常态可达;③ design 组展示名只改了 `taskGroupLabels` 一本字典,`agentPresentation.ts``groupConfigs``view/project-development/index.tsx``summarizeAgent` 仍硬编码「策划 Agent」,与新阶段「立项策划」同屏共存,违反第 18.2 节;④ 阶段进度「轮次 X/3」直接透传 0-indexed 的 `clarificationRound` 未 +1(后端自己用的是 `current_round + 1`),最后一轮显示「轮次 2/3」,字面暗示还剩一轮。
## 2026-08-18 M1E 隔离工作树:Fast GDD submit 有界拒绝与覆盖审计
- **范围与结论**:在 `codex/genarrative-isolated` 上按 M1E 只接受具备完整触发链的缺陷。确认 Provider 连续输出不合法 `plan.submit_gdd` 时,Runtime 原有「rejected observation → 同 child run 续跑」链没有次数上限,模型可反复请求 tool-plan 并累积历史 observation;这是可达的 prompt 膨胀与资源消耗链。
@@ -216,7 +219,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 不做:改 walker、透明补边、裁切、横纵不同倍率、Lanczos / bilinear、另存逻辑图、前端框缩放、失败路径、新测试。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md``docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md``docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md``docs/【编辑器】图片画布结构化持久化与迁移回滚方案-2026-07-19.md``docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
- 背景:真实 `gpt-5.6-sol / max` 验收中,`art-director` 失败后已形成 `ready + needs-repair` delivery,但认领、合同读取、claim observation 和完成 blocker 均硬编码为 Supervisor-only;实际直属父 Run `code-prototype` 无法消费回执,随后又发起 29 次 Provider 请求。
- 验证方式:覆盖合法认领与合同精确读取、错误 Agent/Run/delegation 拒绝、delivery 身份篡改阻断、唯一安全默认返工、普通失败零后续 Provider lifecycle,以及新的真实 Provider 空项目轮次。顺带收紧 `validate_executable_inline_javascript_syntax`:正文游离 `<` 不再吞掉后续 `<script>`,未闭合或非标签状 `<` 继续扫描,避免后置脚本被静默跳过语法校验。
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md``docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
@@ -294,7 +296,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
**以下三条是经复核后有意保留的取舍,不是待办。后续 PR 不得在未重新裁决的情况下「顺手修掉」。**
1. **`$isolatedAgentTemplates` 仍会向 plan 根 run 列出全部专业角色名。** `art-director` / `design-foundation` / `art-asset-plan` / `code-prototype` 等名字来自 `$base``RUNTIME_PROMPT_RUNTIME_COMPOSITION``$isolatedAgentTemplates` 段,由 `GAME_CREATOR_AGENT_GROUP_DEFINITIONS` 运行时渲染),用途是广告 `agent.spawn_isolated` 的合法模板 id,**不在本次裁掉的两段之内**。裁掉它要动 `$base` 与隔离模板目录,波及全部 Agent。保留的依据是:`A2` 已对 plan 根 run 硬拒 `spawn_isolated`,该目录对 plan 根 run 是**死文本**,不构成可利用面。**结论:上下文层的收窄边界到此为止;「plan 根 run 的上下文里不出现其它 Agent 名」这一目标 M1 不成立,不要据此写验收句。**
3. **上下文层回归是弱断言。** `plan_root_supervisor_prompt_drops_intro_and_visual_contract_sections` 用「不含某几条独有短句」断言,不是对照组那种字节级 `assert_eq`。已实测非永真。已知漏报场景:若将来 `$visualContract` / `supervisorIntro` 被替换成措辞不同但仍暗示专业组扇出的新文本,这条不会报警。**执行层的 `A1`/`A2` 是该场景的唯一保障**——这也是本包把硬门排在裁段之前的原因。
2. **上下文层回归是弱断言。** `plan_root_supervisor_prompt_drops_intro_and_visual_contract_sections` 用「不含某几条独有短句」断言,不是对照组那种字节级 `assert_eq`。已实测非永真。已知漏报场景:若将来 `$visualContract` / `supervisorIntro` 被替换成措辞不同但仍暗示专业组扇出的新文本,这条不会报警。**执行层的 `A1`/`A2` 是该场景的唯一保障**——这也是本包把硬门排在裁段之前的原因。
- 关联文档:`docs/technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md` 第 4.3、22、23.8、24 节。
## 2026-08-14 M1B-1planning storage 基础与只挡写隔离完成(待合入)
@@ -442,7 +445,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 决策(D10,问询机制):策划节点不得直接调用 `user.input_request`,改走 **Runtime 直投**——策划节点提交结构化决策字段,Runtime 创建 **owner 为 Project Supervisor** 的 pending,用户在 Supervisor 对话里回答,Supervisor 的 Provider 全程不参与提问。这不是新造机制:`AgentRuntimeUserInputRecord` 已经是唯一事实源并两路投影(`user_input.rs:494-530` 投影成会话消息写进 pending owner 的会话文件,`:595-623` 投影成结构化 observation`:803-807` 有「observation 重算冲突」校验防漂移),直投只是让两路分别落到 Supervisor 与策划节点。
- 依据的产品约束:一、用户侧只有一个对话对象;二、所有呈现给用户的对话内容必须**物理存在于** Supervisor 的会话文件中,不得由前端把多个 Agent 的会话拼成单一视图。第二条不是靠约定满足,而是靠「会话文件按 `agentId` 分目录(`conversation.rs:42`)、消息归属完全由 pending owner 决定」这个物理事实。
- 决策卡冻结范围收窄:冻结三个固定选项及顺序(要确定性映射到 decision state 与 answer source)与 question ID 规则(`decisionId` 由它推导);**问题正文措辞不冻结**,作者是策划节点。保真由「Runtime 不经过任何 Provider 搬运」保证,与模板无关。
- Goal Contract 处置:原四方案 A/B/C/D 随之作废(共同前提是策划 run 自己就是那个 root)。但入口门 `validate_root_goal_contract_control_plan_at` 无 profile 判断,standard root Supervisor 仍受约束,故替换为三条实测约束:Supervisor 第一轮必须且只能提交 `agent.goal_contract`;验收 `requiredEvidence` 锚定 `game/fast_gdd.md``tool:file.read`**不得指向 `.agent/planning/**`**`reject_agent_runtime_private_control_path` 不区分读写,加进去会把 `file.read` 一并挡死、出口门永久 blocked,planning 的写保护须用只挡写的独立判据);Supervisor 取证必须在 GDD 落盘之后。
- Goal Contract 处置:原四方案 A/B/C/D 随之作废(共同前提是策划 run 自己就是那个 root)。但入口门 `validate_root_goal_contract_control_plan_at` 无 profile 判断,standard root Supervisor 仍受约束,故替换为三条实测约束:Supervisor 第一轮必须且只能提交 `agent.goal_contract`;验收 `requiredEvidence` 锚定 `game/fast_gdd.md``tool:file.read`**不得指向 `.agent/planning/**`**`reject_agent_runtime_private_control_path`不区分读写,加进去会把`file.read` 一并挡死、出口门永久 blocked,planning 的写保护须用只挡写的独立判据);Supervisor 取证必须在 GDD 落盘之后。
- M0 影响判定:三个代码工作包(`M0A-2` / `M0B-1` / `M0B-2`)**全部不受影响、零回退**——无一行按 D6 编写,全仓库检索 `fast_gdd` / `.agent/planning` / `project-supervisor-plan-chat` / `is_exact_supervisor_plan_run_at` 均零命中。只有 `M0A-1` 交付的文档基线失效,以工作包 `M0A-3` 修订,修订完成前不得声称「M0 全部完成」。**注意 `M0A-2` 虽不受影响,其实现也不可复用**`autonomous_owner_artifact_validation_available_for_run_at``autonomous_completion.rs:416-441`)四重绑死 owner 白名单、profile、source 且要求 `parent_agent_id` 为 Project Supervisorstandard 路径必须另建物理独立实现。
- 保留待裁决:策划节点 `agentId` / source 命名(是 `M0A-3` 批二的共同阻塞点,身份常量不定则第 3、8、9、12、13 节无法落笔,第 9.1 节 golden vector 的 SHA-256 必然重算);Supervisor 侧新可信 source 是沿用旧字符串还是取新名;checkpoint handoff 私有持久化(原有项,不受影响);plan run 是否允许 steer,以及 Supervisor 被 steer 时下游策划节点如何收束。
@@ -455,17 +458,14 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- M2 裁决二:planning baseline 不是「一次冻结永久有效」。steer 替换协议会终止旧根树并另起 replacement root run,该 run 必须在自己的锁/CAS 边界内重新冻结与被替换根**完全相同**的 `approvedGddRef`,即使期间已有更新的 approved 版本也不换稿;无法证明同一 ref 时失败关闭,不得降级为 `mode=direct-build`
- M2 附带证据:Goal Contract 落在 `.agent/runtime/goal-contracts/{rootAgent}/{rootRun}.json`,按 root run 分文件并绑定 binding fingerprint 与 source SHA-256。这是「自带 root/run 身份」的正面先例,D4 关于 `approvedGddRef` 是否进 manifest 的决策应参照此形态,而非项目级单例。
- 背景:M0B-2 遗留一条挂起项——manifest 没有 root/run 绑定,不能安全决定 cached failed manifest 是否应覆盖活跃 main 状态。本条对该项作出处置,使 M0-4 可以收口。
- 事实基线:`GameCreationAppManifest` / `GameCreationAppTaskState`(TS 与 Rust 双侧)不含任何 run/root/agent 身份字段;manifest 是项目级单例文件、跨轮复用;前端唯一读取入口按 `task.id === 'code-prototype'` 做纯字符串过滤。因此"这份 manifest 属于哪一轮"无法从数据本身判定,这是结构性质而非实现疏漏。
- 本阶段不加绑定:不给 manifest 或其投影新增 `statusRunId` / `statusSource` 等身份字段,维持 M0B-2「不修改后端 DTO/schema/delivery/route」的范围声明。补字段的方案必须同时覆盖 `update_manifest_task_status_at``set_task_status` 两条写入路径,否则会制造"校验通过"的假象(详见 pitfalls 2026-08-12 条)。
- 残余风险(明示保留,不视为回归):当前 main 到达 Runtime 终态 `completed`、而全局 manifest state 尚未被本轮 `manifestInvalidated` 事件刷新时,跨轮残留的 `failed` 仍会被当作本轮结论显示。该窗口实际宽度未量化;三条候选机制(新鲜度门控、root-scoped 永久缓存、manifest 补身份字段)经审查均不可安全落地,故本阶段只记录不实现。
- 归档边界:`【Supervisor 阶段记录】` 只有在 root、唯一 main、当前动态美术 children 与 manifest `code-prototype` 全部终态且无冲突/reconciliation 时才可写入。等待期间按完整 `agent/session/run` 身份保存 root-scoped Runtime 快照,避免 current-by-agent map 被新 root 覆盖后把新旧证据串线;消息 ID 固定绑定 root run,重载与 hydration 幂等。
- 范围:M0B-2 只改前端纯投影、接线、文案与回归,不修改后端 DTO/schema/delivery/route,不迁移历史 manifest,也不提前实现 M1M3。
- 结构性依据:delivery 的 delegationId 由父动作 ID 派生,且 targetRunId 绑定原 child run;通用 retry 铸造的 `retry:{旧run}:{新run}:{纳秒}` 身份在结构上不可能匹配任何现有 delivery。即使只圈住 retry 的写边界,其产出也没有合法消费者。让 retry 继承 lineage 必须引入可变 delivery、换绑或放宽 exact binding,与不可变事实和失败关闭方向冲突,明确不采纳。
- 既有语义确认:同一 main run 内“每个审计缺口最多委派一次”(不含 `Suppressed`、包含终态失败或取消 delivery)是 2026-08-08 单主编排重构的 master 既有防抖语义,本裁决有意保留,不为失败 child 开豁免。同 run 重来与通用 retry 一样被拒绝,恢复只走下一轮 main 重新审计;这与禁止 retry 继承 lineage 是同一设计哲学。
- 实现边界:入口守卫落在 `runtime_driver/lifecycle_control.rs``retry_game_creator_agent_runtime_task_at`,必须使用不要求 child 仍为 running 的结构身份分类;`resolve_game_creator_agent_runtime_retry_configuration_at` 保持不变。纵深防御落在 `runtime_tools/file_ops.rs` 的动态美术分类入口;严格 lineage/Canvas 授权 predicate 本身不放宽。除这两处与对应测试外,不扩展 M0B-1 生产改动面。
@@ -493,8 +493,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证方式:M0 验证 tracked 技术方案、索引、注册表、golden 指纹、提交/恢复合同和决策记录自包含一致;M1~M3 分别按关联技术方案的阶段门禁执行,不能以文档合入冒充功能完成。
- 关联文档:`docs/technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md``docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`
## 2026-08-10 资源管理评审阻塞项按第二轮正式合同修复
- 背景:资源管理第一轮实现后,人工验证继续暴露 WebView 默认缩放、预览队列饥饿、过滤后媒体残留播放、外层滚动串 scope、超深依赖坐标越过 Rust 上限和暂时错误无法重试等问题。部分 PRD / 技术方案仍描述第一轮的中央媒体预览、单全局 Overlay 和统一 section scope,已经与第二轮代码及验收结论冲突。
@@ -625,7 +623,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证方式:按权威专题的 28 项矩阵覆盖 Web/Tauri 共用源码、新增/精修、生成响应丢失、重复提交、两窗口并发、事务各崩溃点、草稿恢复、切项目/切状态/改选择/改筛选迟到结果和不刷新即时投影。
- 关联文档:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md``docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md``docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
- 验证方式:覆盖路由后只启动 `code-prototype`、既有素材零美术委派、精确缺口才允许单个对应 child、child 的 `game/**`、memory 和 manifest 写入拒绝而 `assets/**` 写入允许、回执恢复同一主 Run,以及主 Agent 的接入、静态 smoke 与双视口试玩;追加真实 scheduler 对旧 canonical task 的同 Run 恢复、旧固定美术 child 及其历史 isolated 后代对 Canvas/memory/manifest/project scope 零 mutation、伪只读 `agent.run_status` 阻断、嵌套美术 child 硬截止对账回执;另跑完整 DAG 非回归。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
@@ -713,8 +710,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
---
- 背景:ready scheduler 已为当前根 Run 持久化并启动 `code-prototype`,但并发 hydration 持有的旧 manifest 快照随后把该节点从 running 覆盖回 pending。父 Run 只读取 manifest 时会在 child 仍运行的情况下误判固定 Graph 已停滞并先行失败;child 完成门又因 pending 连续拒绝真实交付,最终耗尽 loop。
- 失败关闭:GUI/CLI、旧父 Run child、终态 child、错误/伪造绑定、非确定性 runId、WaitingForConfirmation、WaitingForUserInput 和 needs-reconciliation 都不得借用该容忍;创建更新的根 Run 后,旧 child 立即失去父 DAG 活性与 pending 完成资格。
- 玩法连续性:普通美术措辞不再触发历史玩法类型回溯;只有正式 failed continuation 继承原完成合同,纯“继续”沿用现有 continuation 识别边界。
@@ -724,7 +719,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
## 2026-08-05 增量接入既有美术时复用已验收 Graph 节点
- 禁止替代方案:不得增加 loop 次数掩盖冲突,不得递增虚构的产物版本号,不得覆盖 art manifest 扩展字段,也不得重放历史图片生成 action。
- 验证方式:回归必须证明明确复用时两项美术节点保持 completed、父完成门不再报告 art manifest baseline 未变化、俄罗斯方块场景保持 `tetris-v1`;删除任一切片后豁免立即失效。另以普通新目标和“全新美术”请求证明旧美术不能被认领。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/pitfalls.md`
@@ -943,7 +937,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
- 背景:External Runner 和 Tauri 客户端各自拥有进程内 `PreviewRegistry`。Runner 完成 `preview.validate` 后,其 server 与 running 状态不会出现在 Tauri registry,导致已有可玩版本时用户预览不自动出现;后续 revision 即使验证成功,既有 iframe 也可能继续显示 WebView 缓存中的旧资源。顶部只写“未启动”还会让用户无法判断是 Runner、Runtime 还是预览未启动。
- 验证方式:以当前 run 成功 `preview.validate` revision N 后断言 iframe 自动出现且 server 归 Tauri registry;再完成 revision N+1,断言 server 进程和 loopback origin 不变、iframe 重新加载新内容且所有响应为 `no-store`。Runner registry 单独 running 不得让页面显示预览;相同 / 更低 revision 不得刷新;停止预览后顶部必须显示“预览未启动”;构建产物和安装信息必须为 `0.1.1`
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
@@ -963,7 +956,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证方式:Rust mock 503 覆盖等待、恢复与耗尽,断言 exact HTTP status、attempt、sidecar 清理和正文零泄漏;前端模型覆盖状态卡优先级、严格字段解析、字段不一致与尾随正文失败关闭;conversation 测试覆盖 Provider URL/query、API Key、绝对路径、fingerprint、chars 和 redaction marker 均不可见。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
- 背景:独立包原先在 Tauri 最终退出时仅调用一次 `runner.shutdown_if_idle`;若 Runner 正忙便返回 busy 且没有稍后关闭闩锁,Runner 和后台命令会永久残留。`command.exec / project.verify` 只有进程组 flagSTDIO MCP、Git 和清理命令也缺少 `CREATE_NO_WINDOW`,因此 Windows release 会连续弹出多个控制台窗口。
- 验证方式:定向测试覆盖专用 RPC 的 draining / shutdown 与普通 idle 语义不变;正式安装包在活跃任务期间确认无后台控制台窗口,关闭主窗口后核对 Runner、MCP、command、ConPTY 和孙进程全部退出,再启动确认 durable 状态正确 reconciliation。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
@@ -974,16 +966,13 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证方式:Windows 定向测试覆盖 endpoint 原子写入与 TokenUser 复核、new/stale lock owner 修复、活锁不截断、hardlink / reparse 不触碰目标、project-owner 诊断 TokenUser 复核、错误码精确分类,以及子进程退出在 2 秒内返回;正式包在现场 TokenOwner 为 Administrators 的机器上必须依次出现 `startup.runner.start.complete`,创建项目任务时 execution-owner 诊断也必须成功。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
- 背景:Windows 安装包首次创建 AppData 时若沿用继承 owner,目录 owner 可能是 Administrators 而非当前登录用户;后续严格 owner 校验会让客户端在 `.setup()` 阶段退出,且无控制台 release 缺少可见诊断。直接修改 foreign-owner 旧目录的 ACL 还可能覆盖其它主体持有的数据或跟随 reparse 路径。
- 决策:新建客户端 AppData 时以进程 `TokenUser` SID 显式设置 owner 和当前用户私有 DACL,不使用 `TokenOwner` 代表用户。历史 foreign-owner 真实目录先原子重命名为同级唯一 `.owner-mismatch-backup-*`,再重建并回读验证安全目录;任何 reparse / junction / symlink、备份冲突或迁移失败都失败关闭,不在旧目录上放宽权限。独立 release 的 `startup.log` 和记录 Runner stdout / stderr 摘要的 `agent-runner.log` 均采用 256 KiB 上限、仅一份 previous 和脱敏写入;AppData 日志不可写时 `startup.log` 回退系统 TEMPTauri URL、AppData、Runner、`.setup()``.build()` 初始化失败时在 Windows 显示包含诊断日志位置的错误对话框。
- 验证方式:Windows 定向测试覆盖 TokenUser owner、私有 DACL、foreign-owner 同级备份不覆盖和 reparse 拒绝;诊断测试覆盖 256 KiB 单 previous 轮转、凭据与绝对路径脱敏、AppData 不可写时 TEMP 回退和初始化失败对话框。安装包 smoke 后保留旧备份证据并确认新 AppData 可写、Runner 可启动。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
## 2026-07-28 AI 游戏临时页面启动消息采用 URL 消费加页面闩锁
@@ -6656,7 +6645,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 历史边界:成功加入画布时写一条 `perfect-pixel` 历史,中文标签为“完美像素”,并纳入新增结果保护;撤销不得让派生 PNG 消失。像素处理失败或 completion 因占位删除未落画布时不写该历史。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md``docs/【图片画布】撤销范围与操作提示方案-2026-07-17.md``docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md``docs/【编辑器】图片画布结构化持久化与迁移回滚方案-2026-07-19.md`
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md`
## 2026-07-31 autonomous 单一任务图、预览 fail-fast 与逐 Agent 推理默认
@@ -6704,7 +6692,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证:顺序断言新增 `tokio::time::timeout_at(` 与超时文案;参照 `937378ab9` 的做法用 `assert_function_occurrence_count``resolve_editor_pixel_art_source_for_owner` 内的 `.list_editor_projects(``.get_editor_asset_library(` 各钉为 1 次,并用 `assert_function_not_contains` 禁止该函数重新调用取数包装。后者断言的是调用形式 `resolve_editor_reference_object_key_for_owner(state` 而非裸函数名,否则会命中生产代码里说明「老包装保持不动」的注释——该陷阱在编写时即由测试抓出。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`
- 预算:父 Run 接受请求后以 `240` 秒作为首版软预算;父 Run、所有 child Run、等待、回收和确定性验收共享 `300` 秒累计硬上限。硬上限是从 root `bound_at` 计算的绝对 deadline,必须包住 Provider、图片生成、文件写入、静态检查、`preview.validate` 和 final-reply 的在途等待;超时先强制持久化 `failed` 终态,再清理 pending action、Provider batch、confirmation、recovery 和进程会话,不能留下 `needs-reconciliation` 悬空态。软预算后不再扩展 Provider 规划,只能运行受控 fallback、`game.static_smoke``preview.validate`;硬上限未形成当前 revision 的通过证据时必须失败关闭,单轮确定性收束也必须复核累计时间,不能在上限后补写 completed。
- Provider:首版最多一次 Provider 规划 / 写入请求,禁止同一首版自动传输重试、第二次 tool-plan 或无限 repair。Provider 结束后由 Runtime 按当前 revision 依次执行确定性静态 smoke 与浏览器试玩。
- 兜底:fallback HTML 必须自包含、无远程运行依赖,从 `ready` 开始并真实绘制 Canvas,持续更新 `playable-web-game-state.v1`,提供键盘 / 触控、start / primary-action / restart 和胜负状态;primary-action 后可保持 `playing`restart 后可稳定恢复 `ready | playing`,不得开始前固定 `lost` 或用固定失败充当完成。fallback 只有在 `assets/art-spec.png` 已有效登记并真实存在时才能生成,而且必须把该平台图片显著绘制为主要背景、玩家和目标;不得以纯 Canvas 视觉绕过平台图片硬门。
@@ -6747,13 +6734,9 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证:`dropDeadInlineGenerationPlaceholders` 四条单测覆盖「剥离已死 inline 占位」「保留队列型占位(缺字段与显式 false 两种)」「保留已终态的 inline 占位与普通图层」「标记经 hydrate 与序列化往返不丢失」——最后一条钉住白名单式 hydrate 漏字段会让标记在一次「加载→保存」后消失。工作流测试新增 `live-session-dialogs` 探针,正向断言完美像素占位置位、反向断言去除背景占位不置位。`vitest src/components/image-editor` 893 通过 / 72 文件,typecheck、eslint、check:encoding 通过。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`
- 显式协作合同:autonomous 的旧 `code-prototype + quality-review` 首批合同退出。显式 project collaboration policy 或持久 batch 恢复若进入首批 `agent.delegate` 路径,只允许且要求三个 Director 各一次;策划与程序 Director 是只读规划且 `expectedArtifacts=[]`,美术 Director 是非只读规范图任务且必须交付 `assets/art-spec.png`。任何非 repair 底层委派与 isolated child 都在首批失败关闭;默认 manifest DAG 仍是唯一自动首轮执行链,不额外复制三个 Director 委派。
- 输出决策:保留未提交 `streaming / ready` 的当前 revision 门;已提交的专业 Agent final reply 继续使用既有 durable response-stream 身份,后续项目 revision 变化不再隐藏早期阶段回复。
- 图集事务退役补充:九路径快照在创建和恢复读取时都使用跨平台不跟随符号链接 / reparse point 的文件句柄核算 64 MiB 总预算,marker、journal 与快照均通过有界双次读取和句柄元数据复核拒绝同长度并发改写;实际读取仍受剩余预算限制,稀疏或并发增长文件不能触发无界分配。恢复开始时锚定可信事务目录句柄,每次读取控制文件前后都复核目录身份,拒绝 rename、junction 或替换目录。恢复必须先把全部 journal 条目和九路径快照完成结构、大小与摘要校验并形成内存计划,随后缓存全部 canonical 路径的恢复前状态;每项落盘前再次校验目标与父目录,后续项失败时按逆序回滚本轮已应用项,但回滚前必须 CAS 证明目标仍等于本轮安装结果,外部修改不得被覆盖并进入 reconciliation。末尾路径竞态或快照损坏不得留下静默的新旧混合合同。`committed` 持久化后先删除并同步 `prepared`,再清理 `.previous / .replacement`、同步 canonical 合同并最后删除事务目录;递归删除中断后最多留下只有 `committed` 的可清理事务,不能重新落入 rollback 分支。
- 关联:`apps/ai-game-creator-shell/scripts/start-tauri-dev.mjs``start-dev-stack.mjs``src-tauri/src/agent/runtime_protocol/autonomous_completion.rs``response_stream.rs``docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
@@ -7169,13 +7152,11 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 验证:模型测试覆盖允许与拒绝类型,工具栏和两类右键菜单覆盖视频、单个拆分图标、角色动作及音频不展示,打开与提交工作流覆盖视频等不支持类型绕过入口时仍拒绝;后端表驱动测试覆盖全部现役图片素材 / 媒体类型与未知类型,锁定图片编辑端点失败关闭。
- 关联:`src/components/image-editor/ImageCanvasGenerationModel.ts``ImageCanvasSelectedLayerToolbarView.tsx``ImageCanvasContextMenusView.tsx``useImageCanvasGenerationWorkflow.ts``useImageCanvasGenerationSubmissionWorkflow.ts`
- Canvas 目的区域证明以 Oxc symbol、调用实参、计数循环和所属 Canvas 身份为权威。大 classic script 的调用图保持整轮 visited;函数内 alias 重绑定必须按作用域和写入位置解析。格子坐标无法进一步化简时只可在 `COLS × ROWS × CELL` 唯一合同下建模为完整棋盘轴区间,已知调用参数或循环边界优先,越界调用继续失败关闭。
- 动态坐标只新增一种受限可见性证明:未遮蔽的全局 `Math.min(currentCanvas.width|height - size, Math.max(0, dynamic))`。尺寸成员必须属于创建当前绘图 context 的 Canvas;其它 Canvas、被遮蔽的 `Math`、缺少上下界或普通未知动态坐标均不得作证。
- autonomous parent wake 的瞬态重试预算耗尽后必须形成 durable reconciliation。lane 忙时先写 deferred recovery signal;获得同一 execution lane 与项目写锁后,重新读取原始 Runtime state、最新 task、cancel tombstone 和 DAG 进展。只有仍指向同一非终态根 Run 时才能以 CAS 追加 reconciliation task 并原子替换 statemanifest 已损坏时也不能让普通 hydrated writer 先阻断对账证据。
- autonomous 测试夹具必须先建立带完整 parent/delegation identity 的 linked Pending child,再由正式启动路径写第一条 Running;禁止先启动无父身份再补 journal,也禁止把 terminal runId 复活成 Running。Completed-only 深验按任务逐项执行:Pending 任务不深验,但同一 manifest 中已 Completed 的美术任务仍必须验证其切片合同。
- code-prototype 只有在本人当前 Run 已有 `status=ok` 的真实 mutation action、对应 mutation revision 已通过 `game.static_smoke`,且完整 `runtime.autonomous_completion` 完成门无阻塞时,才允许快车道返回确定性交付或把结构化计划全部标为 completed。verification gate 的 mutation revision 可能因保守失效策略在失败 patch 前推进,不能单独证明文件已修改。若 static smoke 已过但完成门仍报告素材、正式产物或其它诊断,计划尚有未完成步骤时用单一 in-progress 修复步骤替换首个非终态步骤,并把其余非终态步骤保持 pending;计划已全 completed 且仍有容量时才追加修复步骤。这样既保留 completed 单调历史,也不会因 steer 合并后超过 8 步而永久卡在 `runtime.plan_update`;不得重复返回同一交付计划直至耗尽 loop budget。
- mutation ownership 以当前 run 的最后一条同工具调用和 Agent DB receipt 为联合权威;pending action 的 `plannedSteerCursor` 必须在 recent tool-call 与 receipt 中使用同一 fingerprint。结构化计划含 failed 步骤时 code-prototype 立即失败关闭;8 个 completed 步骤仍有 blocker 时不追加第 9 步,改走只允许读取、真实 mutation 与重新验证的外部 repair lane。
- 当前根 Run 有 durable active child 时,即使 manifest 快照把全部 seed task 写成 CompletedDAG 仍保持 in-progress;任一 seed task 为 Failed 时继续立即失败关闭。所有项目修改在取得项目写锁后再次核对 ready child 的确定性 runId、父绑定、durable Running 状态和当前活跃根 Run;新根 Run 建立后旧 child 不得推进 revision 或修改文件。
@@ -7183,7 +7164,6 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 根 Supervisor 的 manifest completion gaps 只对 status 已为 Completed 的 seed task执行正式产物与 Canvas 深验;pending/running/failed 本身已经构成完成阻塞,禁止提前扫描后续波次。自动唤醒 200 次瞬态重试预算耗尽后必须写入 `needs-reconciliation`,不能静默返回并留下假运行状态。
- parent wake 的 terminal reconciliation task 是 durable commit markerstate / queue / event / Agent DB audit 是可幂等重建投影;非瞬态 task journal 读取错误直接失败关闭。restart 只在 raw state 具有完整 Agent/task/Session/run/source/profile/binding/task 身份时修复其当前 run;state 缺失、损坏、空对象或关键身份为空时只取 journal 最后 logical run,完整有效的新 Run 阻止历史 marker 覆盖。event/audit 必须完整 payload 唯一匹配,同键冲突或重复失败关闭;旧 task 终态、Runtime 非 waiting 或新 Run 接管时,durable deferred signal 追加 resolved/superseded 后才返回 obsolete。
- 升级恢复兼容 v1 决策,但不沿用旧责任链:读取时严格复核 v1 fingerprint,从根完成合同的有效任务恢复 `intentSummary`,并保留旧 fingerprint 只用于核对已有 route 身份。旧 `code-director` coverage/route 对当前单主完成门表现为 migration pending;当前 `code-prototype` 必须重新 `asset.list`,再原位写入自己的 coverage/route。这样同一根 Run 可以继续,又不会把旧 Director 审计冒充成主 Agent 本人的完成证据。
## 2026-08-04 静态视觉状态流与可见证据收口
@@ -7671,6 +7651,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 失败结算的 `expectedDraftRevision` CAS、结算意图和公开投影必须在同一草稿锁边界内串行化;显式失败结算和生成流程内部错误都先持久化 `failure-settlement-pending``reconciliation-settlement-pending`,不能直接跨文件发布终态。
- pending 恢复不依赖平台登录或 External API Key。恢复按当前权威草稿幂等补齐 generation 投影和 staging revision,再把私有 ledger 发布为 `failed``reconciliation-required`;公开投影已经存在时只完成账本,不重复增加草稿 revision。
- 回归必须覆盖 pending ledger 写入后、公开草稿写入前,公开草稿写入后、staging revision 写入前,以及 staging revision 写入后、终态 ledger 写入前三种重启切点。
## 2026-08-23 AGC 本地资源与 External Editor 账号绑定分离
- 权威边界:本地项目 ID、manifest asset、正式本地文件和内容摘要属于设备上的本地项目;`canvasProjectId / resourceId / assetObjectId / objectKey` 属于具体 External Editor 服务 principal。manifest 中现有远端字段继续保留生成来源,不再承担“当前账号可编辑句柄”,本轮不修改共享 manifest schema。
@@ -7709,6 +7690,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 严格图集崩溃收口:workflow 在严格图集调用前先持久化 `strictSpritesheetPending` 并冻结底层严格事务覆盖的九项旧合同身份;旧路径可精确冻结为缺失。Provider 完成结果先绑定原 retained stage ledger。恢复在同一项目锁内对账严格事务;只有新九项合同、规范图/背景图替换锚点与 retained spritesheet result 三者一致才补写 `completed`,旧九项合同才允许补偿。旧合同判定、写 `compensating`、恢复两项素材与登记、回读和清锚点必须在同一项目锁内,重启已有 `compensating` 也重新判定;第三种混合、漂移或 foreign result 状态进入 reconciliation。不能在主图集与四切片已整体提交后仍按两文件 rollback 制造混合包;若中断前阶段告警尚未进入 durable completed result,恢复结果追加“原阶段告警无法完整重放”的明确 warning,不静默清空。
- Direct 对话恢复从新到旧扫描全部合法 User 回合,遇到较新已回答回合继续向前,不得丢失更早未回答回合。成功返回时 Rust 已先持久化 assistant,前端冗余 append 失败也不得重跑 Provider;普通错误终态的显式 append 失败后,恢复 claim 必须保持到 React fallback writer 对同一稳定 assistant messageId 的写入明确成功或失败,不能在 writer 尚在途时按旧 `/history` 快照重跑。fallback 成功后释放 claimfallback 失败时跳过该 writer 的无界迟到重试并释放 claim,后续显式 `/history` 才可复用原稳定 `clientTurnId`。终态收敛后删除 claim,避免长会话无界增长。
- 正式资源提交结算遵守同一顺序:阶段三 commit 成功后先持久化 `asset-commit-settlement-pending`,恢复器幂等补齐 `asset-durable-committed` 公开投影与 staging revision,再发布私有终态;公开投影已经存在时不得重复增加草稿 revision。恢复必须把私有回执与阶段三 commit ledger、transaction journal、manifest 资产和事件 payload 的完整身份绑定,任一错配都保留 pending 并失败关闭。回归同时覆盖三个 durable write cut,以及私有回执、commit ledger、journal 错配。
## 2026-08-24 AGC Direct 抠图语义工具
- 决策:将 External v1 `/api/external/v1/editor/images/background-removals` 通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。
@@ -7772,3 +7754,9 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 决策:新增 `packages/shared/src/components/PlatformToggleRow.tsx`,统一承接白底整行 checkbox / status 开关的语义、状态胶囊和禁用态;`src/components/common/PlatformToggleRow.tsx` 仅保留兼容导出,展示页改为直接从共享 barrel 引入。
- 迁移:`Match3DResultView``PuzzleResultView``VisualNovelResultView``SquareHoleResultView``RpgCreationResultViewImpl``RpgCreationResultActionBar``RpgCreationAssetDebugPanel``CustomWorldCreationHub``BabyObjectMatchWorkspace``AccountModal``CreationAgentWorkspace` 直接消费共享 `Platform*` chrome,玩法专属资源 / 媒体 / 上传 / 弹窗组件继续留在业务层。
- 验证:共享 PlatformToggleRow 定向测试、相关前端类型检查、编码检查和 `git diff --check` 通过;未改变业务行为或后端契约。
## 2026-08-31 第二批共享组件直引
- 决策:新增 `PlatformBackActionButton` canonical 返回动作组件,统一 compact / regular 尺寸、返回图标和 platform / editorDark surface`src/components/common/PlatformBackActionButton.tsx` 仅保留兼容出口。
- 迁移:`LoginScreen``BindPhoneScreen``CustomWorldEntityCatalog` 将已有共享 `Platform*` chrome 直接从 `@genarrative/shared/components` 引入;展示页新增返回动作示例并保留整行开关示例。
- 边界:媒体、上传、资源换签、业务弹窗等带副作用组件继续留在网站业务层。
@@ -30,6 +30,7 @@
- `PlatformAsyncStatePanel``PlatformFilterToolbar``PlatformIconBadge``PlatformRuntimeStatusToast`
- `PlatformInfoBlock``PlatformNavigableListItem``PlatformStatGrid`
- `PlatformToggleRow`(整行 checkbox / 状态开关)
- `PlatformBackActionButton`(白底结果页返回动作)
迁移约定:`Button``Modal``SegmentedTabs``Switch``Input``Textarea``Badge``Card` 的 canonical source 位于 `packages/shared/src/components/ui`,旧的 `@genarrative/shared/components` API 作为兼容适配层继续保留。`Button``Input``Badge` 使用 CVADialog/Tabs/Switch 使用按需 Radix primitive;原生 `SelectField` 暂不迁移,避免破坏现有 `<option>` 与表单事件合同。后续组件按使用面逐个迁移。
@@ -37,6 +38,8 @@
2026-08-31 继续迁移:`PlatformToggleRow` 已收口到共享包,保留 `src/components/common/PlatformToggleRow.tsx` 兼容出口;`Match3DResultView``PuzzleResultView``VisualNovelResultView``SquareHoleResultView``RpgCreationResultViewImpl``RpgCreationResultActionBar``RpgCreationAssetDebugPanel``CustomWorldCreationHub``BabyObjectMatchWorkspace``AccountModal``CreationAgentWorkspace` 已将共享 chrome 组件改为直接从 `@genarrative/shared/components` 引入。玩法专属的媒体、上传、编辑器、弹窗和资源状态组件仍保留在业务 common 层。
2026-08-31 第二批:新增 `PlatformBackActionButton` canonical 实现及兼容出口,展示页加入 compact / regular 两种返回动作示例;`LoginScreen``BindPhoneScreen``CustomWorldEntityCatalog` 继续将共享 chrome 直引到 `@genarrative/shared/components`。媒体、上传、资源换签与业务弹窗仍不迁移。
同时提供 `Platform*` 别名,便于从现有平台组件命名迁移;别名不携带平台业务语义。
Web 宿主需要显式启用 Tailwind 4,并引入 `@genarrative/shared/styles.css``@genarrative/shared/theme.css`Tauri WebView 与网站共用这套源码,移动端 React Native 不消费 DOM/CSS 组件。筛选工具栏所需的 `platform-category-*` chrome 样式也已放入共享样式文件,独立宿主无需依赖网站 `src/index.css`。组件库不提供页面壳或业务流程。`components.json` 只用于 shadcn CLI 定位源码,不允许覆盖现有业务组件。
@@ -0,0 +1,36 @@
/* @vitest-environment jsdom */
import { render, screen } from '@testing-library/react';
import { expect, test } from 'vitest';
import { PlatformBackActionButton } from './index';
test('renders compact back action button by default', () => {
render(<PlatformBackActionButton />);
const button = screen.getByRole('button', { name: '返回' });
expect(button.className).toContain('platform-button--ghost');
expect(button.className).toContain('min-h-0');
expect(button.className).toContain('text-[11px]');
expect(button.className).toContain('gap-1.5');
expect(button.querySelector('svg')?.className.baseVal).toContain('h-3.5');
expect(button.getAttribute('type')).toBe('button');
});
test('supports regular variant and editor dark surface', () => {
render(
<PlatformBackActionButton
label="返回编辑"
variant="regular"
surface="editorDark"
/>,
);
const button = screen.getByRole('button', { name: '返回编辑' });
expect(button.className).toContain('platform-action-button--editor-dark');
expect(button.className).toContain('text-sm');
expect(button.className).toContain('gap-2');
expect(button.querySelector('svg')?.className.baseVal).toContain('h-4');
});
@@ -0,0 +1,53 @@
import { ArrowLeft } from 'lucide-react';
import type { ButtonHTMLAttributes } from 'react';
import { PlatformActionButton } from './PlatformActionButton';
import type { PlatformActionButtonSurface } from './platformActionButtonModel';
export type PlatformBackActionButtonVariant = 'compact' | 'regular';
export type PlatformBackActionButtonProps = Omit<
ButtonHTMLAttributes<HTMLButtonElement>,
'children'
> & {
label?: string;
variant?: PlatformBackActionButtonVariant;
surface?: PlatformActionButtonSurface;
};
const VARIANT_CLASS: Record<PlatformBackActionButtonVariant, string> = {
compact: 'gap-1.5 py-1.5 text-[11px]',
regular: 'gap-2 py-2 text-sm',
};
const ICON_CLASS: Record<PlatformBackActionButtonVariant, string> = {
compact: 'h-3.5 w-3.5',
regular: 'h-4 w-4',
};
/**
* 平台轻量返回动作按钮。
* 统一结果页、工作台等白底场景里的“左箭头 + 返回文案”按钮骨架。
*/
export function PlatformBackActionButton({
label = '返回',
variant = 'compact',
surface = 'platform',
className,
...buttonProps
}: PlatformBackActionButtonProps) {
return (
<PlatformActionButton
{...buttonProps}
surface={surface}
tone="ghost"
size="xs"
className={['min-h-0 self-start', VARIANT_CLASS[variant], className]
.filter(Boolean)
.join(' ')}
>
<ArrowLeft className={ICON_CLASS[variant]} />
{label}
</PlatformActionButton>
);
}
+5
View File
@@ -10,6 +10,11 @@ export { PlatformActionButton } from './PlatformActionButton';
export { getPlatformActionButtonClassName } from './platformActionButtonModel';
export type { PlatformAsyncStatePanelProps } from './PlatformAsyncStatePanel';
export { PlatformAsyncStatePanel } from './PlatformAsyncStatePanel';
export type {
PlatformBackActionButtonProps,
PlatformBackActionButtonVariant,
} from './PlatformBackActionButton';
export { PlatformBackActionButton } from './PlatformBackActionButton';
export type { PlatformBadgeProps, PlatformBadgeTone } from './PlatformBadge';
export { PlatformBadge } from './PlatformBadge';
export type {
+11 -9
View File
@@ -1,4 +1,14 @@
import { PlatformStatGrid } from '@genarrative/shared/components';
import {
PlatformActionButton,
PlatformEmptyState,
PlatformPillBadge,
PlatformProgressBar,
PlatformSegmentedTabs,
PlatformStatGrid,
PlatformStatusMessage,
PlatformSubpanel,
PlatformTextField,
} from '@genarrative/shared/components';
import {
type ReactNode,
useDeferredValue,
@@ -25,16 +35,8 @@ import {
} from '../types';
import { CharacterAnimator } from './CharacterAnimator';
import { PlatformAcknowledgeStatusDialog } from './common/PlatformAcknowledgeStatusDialog';
import { PlatformActionButton } from './common/PlatformActionButton';
import { PlatformDangerConfirmDialog } from './common/PlatformDangerConfirmDialog';
import { PlatformEmptyState } from './common/PlatformEmptyState';
import { PlatformMediaFrame } from './common/PlatformMediaFrame';
import { PlatformPillBadge } from './common/PlatformPillBadge';
import { PlatformProgressBar } from './common/PlatformProgressBar';
import { PlatformSegmentedTabs } from './common/PlatformSegmentedTabs';
import { PlatformStatusMessage } from './common/PlatformStatusMessage';
import { PlatformSubpanel } from './common/PlatformSubpanel';
import { PlatformTextField } from './common/PlatformTextField';
import { CustomWorldNpcPortrait } from './CustomWorldNpcVisualEditor';
import { ResolvedAssetImage } from './ResolvedAssetImage';
import { ResolvedAssetVideo } from './ResolvedAssetVideo';
+7 -5
View File
@@ -1,3 +1,10 @@
import {
PlatformActionButton,
PlatformFieldLabel,
PlatformStatusMessage,
PlatformSubpanel,
PlatformTextField,
} from '@genarrative/shared/components';
import { useEffect, useState } from 'react';
import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
@@ -5,12 +12,7 @@ import type {
AuthCaptchaChallenge,
AuthUser,
} from '../../services/authService';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PLATFORM_BRAND_ASSETS } from '../common/platformBrandAssets';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
import { PlatformTextField } from '../common/PlatformTextField';
import { CaptchaChallengeField } from './CaptchaChallengeField';
type BindPhoneScreenProps = {
+8 -6
View File
@@ -1,3 +1,11 @@
import {
PlatformActionButton,
PlatformEmptyState,
PlatformFieldLabel,
PlatformSegmentedTabs,
PlatformStatusMessage,
PlatformTextField,
} from '@genarrative/shared/components';
import { Check } from 'lucide-react';
import { type ReactNode, useEffect, useState } from 'react';
@@ -15,12 +23,6 @@ import {
persistLegalConsent,
readStoredLegalConsent,
} from '../common/legalDocuments';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformEmptyState } from '../common/PlatformEmptyState';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformSegmentedTabs } from '../common/PlatformSegmentedTabs';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformTextField } from '../common/PlatformTextField';
import { CaptchaChallengeField } from './CaptchaChallengeField';
import { PlatformAuthModalShell } from './PlatformAuthModalShell';
@@ -1,57 +1,5 @@
import { ArrowLeft } from 'lucide-react';
import type { ButtonHTMLAttributes } from 'react';
import { PlatformActionButton } from './PlatformActionButton';
import type { PlatformActionButtonSurface } from './platformActionButtonModel';
type PlatformBackActionButtonVariant = 'compact' | 'regular';
type PlatformBackActionButtonProps = Omit<
ButtonHTMLAttributes<HTMLButtonElement>,
'children'
> & {
label?: string;
variant?: PlatformBackActionButtonVariant;
surface?: PlatformActionButtonSurface;
};
const VARIANT_CLASS: Record<PlatformBackActionButtonVariant, string> = {
compact: 'gap-1.5 py-1.5 text-[11px]',
regular: 'gap-2 py-2 text-sm',
};
const ICON_CLASS: Record<PlatformBackActionButtonVariant, string> = {
compact: 'h-3.5 w-3.5',
regular: 'h-4 w-4',
};
/**
* 平台轻量返回动作按钮。
* 统一结果页、工作台等白底场景里的“左箭头 + 返回文案”按钮骨架。
*/
export function PlatformBackActionButton({
label = '返回',
variant = 'compact',
surface = 'platform',
className,
...buttonProps
}: PlatformBackActionButtonProps) {
return (
<PlatformActionButton
{...buttonProps}
surface={surface}
tone="ghost"
size="xs"
className={[
'min-h-0 self-start',
VARIANT_CLASS[variant],
className,
]
.filter(Boolean)
.join(' ')}
>
<ArrowLeft className={ICON_CLASS[variant]} />
{label}
</PlatformActionButton>
);
}
export type {
PlatformBackActionButtonProps,
PlatformBackActionButtonVariant,
} from '@genarrative/shared/components';
export { PlatformBackActionButton } from '@genarrative/shared/components';
@@ -11,6 +11,7 @@ import {
Label,
Modal,
PlatformAsyncStatePanel,
PlatformBackActionButton,
PlatformEmptyState,
PlatformFilterToolbar,
PlatformInfoBlock,
@@ -562,6 +563,15 @@ function PlatformComponentsReference({
icon={<Sparkles size={15} />}
/>
</div>
<div className="mt-3 flex flex-wrap items-center gap-2">
<PlatformBackActionButton onClick={() => onFeedback('返回动作')} />
<PlatformBackActionButton
label="返回编辑"
variant="regular"
surface="editorDark"
onClick={() => onFeedback('返回编辑')}
/>
</div>
</ShowcaseSection>
<ShowcaseSection title="媒体 / 上传入口">