suzmii
|
f5b381dbd8
|
视图切换时只取消上一视图排队中的可见性预取
用户离开某个视图后,继续为它排队读图没有任何收益,而这些排队的预取会**排在新视图按需请求的前面**(3 槽跨视图共享),把"进入总览后等图"变成纯等待。这是"进总览要等图"的主因,单张读取本身只有 5–15ms。
- `cancelQueuedVisiblePrefetches()`:只下掉**还在队列里**的 `visible` 理由任务。语义边界刻意收窄 ——
- `detail` / `play` 理由的排队**保留**;
- **在途请求不打断**(只占 3 槽中的 1 个,打断它拿不回已花的读盘成本,且切回来要重读);
- **缓存与身份不失效**(不触发 `disposeAllCachedPreviews`),切回原视图不重读已拿到的图。
- 修法 4 一并收口:视图切换 effect 在取消后**立即按几何复核一次**(`sweepVisiblePreviews`),仍然可见的卡重新入队、不可见的自然不再请求 —— 因此不存在"取消后永不重试"的死角。
- 依赖使用**派生后的** `prefetchScopeKey`(不是可能为 `undefined` 的 `input.prefetchScopeKey`)。这修掉了本轮自查发现的一个真 bug:早期写法在调用方未传该 prop 时直接 `return`,整段取消逻辑成了**永不执行的死代码**;由下面的断言暴露。
断言(`tests/useProjectResourceCardPreviews.test.ts` 新增 2 条):
1. 「切视图只取消上一作用域排队中的可见性预取」:登记 6 张(3 在途占满槽 + 3 排队,其中 1 张为 `detail`),切视图后断言队列从 3 → 1、**留下的正是 `detail`**、且 `activeReadCount` 仍为 3(在途不被取消)、新视图 identity 立刻可用;
2. 「取消后再次可见会重新入队」:切走再切回,卡片重新注册且几何可见后,断言该卡**重新进入队列** —— 直接守住"不留永不重试死角"这条硬要求。
变异验证:把取消那一步去掉(effect 内只保留复核)→ 第 1 条断言立即失败(`expected [ …(3) ] to have a length of 1 but got 3`);恢复后 24/24 通过。
验证(同一时刻、同一命令的前后对照;当时树上有并发改动:`src-tauri/.../direct_project_history.rs`):
- 改动前(只 stash 我的两个文件):`86 files / 1236 passed / 4 skipped / 0 failed`
- 改动后:`86 files / 1238 passed / 4 skipped / 0 failed`(多出的 2 条为本提交新增用例)
- `src/components/image-editor` 1387 passed;typecheck exit 0;check:encoding 4388 文件;prettier 与 eslint 干净;`git diff --check` 干净。
|
2026-09-11 17:55:22 +08:00 |
|
suzmii
|
8edee9eef4
|
预览队列改为同级裁决:当前预取作用域优先,但不越过 play
背景:全局 3 槽跨视图共享,而 hook 的 scopeKey 只含 `projectPath + projectId`(不含视图)。用户在栏目页滚一遍会按可见性入队最多 51 个预览 job;点「回到资源总览」时它们仍排在同一个队列里,总览自己那几张「该出图」的卡只能排在后面 —— 这就是「进总览要等图」的真实来源。
- `nextPreviewJob` 改为两级裁决:**理由优先级固定 `play > detail > visible` 不变**,只在**同一理由内部**让当前预取作用域的请求优先。
- ⚠️ 早期写法用「作用域匹配则权值 +3」,会让当前视图的 `visible` 压过上一视图的 `play` —— 直接破坏 PRD §3.3.2 的固定调度优先级。这个错误是被本提交新增的契约用例抓到的,已改为同级裁决,并把该约束写进函数注释。
- 新增 `prefetchScopeKey`:优先取调用方显式传入的值;未传时退化为**被喂进 hook 的资源集合签名**,因此调用方(`index.tsx`)无需改动即可生效。
- 队列任务记录入队时的 `prefetchScopeKey`,供同级裁决使用。
- 新增只读 `previewQueueSnapshot()`:暴露「谁在排队、属于哪个预取作用域、当前活动读取数」。队列病理此前只能靠猜,有了它「当前视图的卡是否真的进了队列」可直接断言。不参与渲染、无副作用。
断言(`tests/useProjectResourceCardPreviews.test.ts`):新增「当前预取作用域越过上一作用域的排队预取」契约用例,三条:
1. 当前作用域的 `visible` 越过上一作用域排队的 `visible`;
2. **但不得越过 PRD 优先级** —— 当前作用域的 `visible` 仍排在 `play` 之后;
3. 同一作用域内仍是 `detail > visible`。
变异验证:把作用域偏好关掉(`&& false`)→ 第 1 条断言立即失败(`expected 'previous' to be 'current'`);恢复后 22/22 通过。断言不是恒真假守卫。
验证:typecheck exit 0;该测试文件 22 passed;check:encoding 4388 文件;`git diff --check` 干净。(AGC 全量与本条无关的并发改动混跑,故此处只报本文件的定向结果;全量对照见后续提交的回报。)
|
2026-09-11 17:44:21 +08:00 |
|
suzmii
|
a4bec3d018
|
记下「不再提醒」偏好保持单键、不按入口分域的理由
Project CI / Repository checks (pull_request) Successful in 2m35s
Project CI / Frontend tests (pull_request) Successful in 3m28s
Project CI / Backend tests (pull_request) Successful in 5m48s
Project CI / Native shell tests (pull_request) Successful in 18m25s
- 该键关掉的只有聊天输入区的「发送前提醒」;资源侧生成素材与快速编辑没有可关的提醒,润色是显式按钮动作
- 分域只在「每个入口都有可关的提醒」时才有意义,现在分域等于给不存在的提醒提前留位
- 只补注释,不改行为与断言
|
2026-09-11 17:06:35 +08:00 |
|
suzmii
|
c2d3292769
|
快速编辑浮层接入提示词润色,提示词变了就重铸请求身份
- 选中工具条的快速编辑面板通过共享 composer 的注入位渲染同一个 ResourcePromptPolishSlot,场景约束为「图片素材的快速编辑提示词」
- 润色回填走 applyResourceQuickEditPrompt:与共享 composer 的 resetFailedDialogStatus 同口径,提示词变了就把上一次的失败状态清掉
- 提交侧请求身份按提示词收口:同 sourceLayerId 且提示词未变时复用(失败重试仍命中同一 operation 账本),提示词变了则重铸 operationId / 幂等键——Rust 的 request_fingerprint 含 prompt,复用旧身份会被判「已绑定到不同资源编辑请求」;正规化结果与 revision 属于别的事实,换身份时原样保留
- 打开面板时的初始身份改用统一的 createResourceEditRequestIdentity(绑定面板草稿提示词)
- 测试:live integration 补两条——润色成功回填并换身份(断言 prompt 与 operationId/幂等键同时变化)、润色失败保留原文且身份不变
- 变异验证:提交侧去掉按提示词重铸(同 sourceLayerId 直接复用旧身份)→ 「润色后换身份」用例红灯(两次 operationId 相同,实测)
|
2026-09-11 17:02:17 +08:00 |
|
suzmii
|
d44e1ae8ea
|
生成素材弹窗接入提示词润色,并让请求身份绑住它铸造时的那句提示词
- 新增 ResourcePromptPolishSlot:素材侧两处入口共用的「AI 润色 / 恢复原文 / 润色中…」动作,取数走 AGC 侧 usePromptPolish,聊天框同款失败态(失败保留原文、可重试文案)
- 场景约束走 polish_local_project_prompt 的 context:「这是素材生成提示词(音效)」+「只润色这一句、不要扩写成需求/方案/验收标准、不要新增事实」(本批不动后端提示词文件),文案落在 resourceCanvasPromptPolishModel
- resourceEditModel 新增 truncateResourceEditPrompt:超上限先按类型截断(背景音乐 140 / 音效 1900 / 视频与角色动画 4000 / 其余 32000)再回填,并在状态行给出「已按长度上限截断」,不静默丢内容;后端上限未改
- resourceEditModel 新增 resolveResourceEditRequestIdentity:请求身份绑定到铸造时的 prompt(Rust request_fingerprint 含 prompt,改了提示词还复用同一 operationId 会被判「已绑定到不同资源编辑请求」);提示词没变时仍复用,失败重试照旧命中同一账本
- 生成素材弹窗用注入位渲染润色动作(共享 composer 只提供插槽,不上 Tauri 调用),提交时按同一口径解析身份;原先只在这一处使用的 createResourceCanvasGenerationRequest 随之下线
- 样式落在 resourceCanvasChrome.css(resource-prompt-polish 一族),弹窗与 composer 共用一行
- 测试:模型层补截断边界与身份复用/重铸;弹窗层补润色回填与上下文、超限截断提示、失败保留原文、失败后提示词与润色一起锁定且重试复用同一请求
- 变异验证:截断改成恒等 → 模型用例与「超限截断」用例双双红灯(实测字段长度 206 ≠ 140);身份去掉重铸 → 模型用例红灯
|
2026-09-11 16:58:49 +08:00 |
|
suzmii
|
25c55fcb7f
|
把提示词润色的状态机抽成 usePromptPolish,聊天输入区先迁过去
- 新增 features/project-workspace/usePromptPolish.ts:失败保留原文、首次成功落原文快照(反复润色只覆盖结果)、可重入保护、可注入的上下文与规范化回调,Tauri 调用固定在 AGC 侧(默认 requestChatPromptPolish)
- 聊天输入区改用它,行为逐字不变:润色按钮 aria-busy/disabled、Loader2/Sparkles、「润色中…」/失败文案、失败保留原文、恢复原文回到最初原文、草稿清空后重置
- 聊天特有的「发送前提醒」留在输入区:提醒偏好、本轮已确认草稿指纹、表单捕获拦截、面板内润色用另一句失败文案
- chatPromptPolish.ts 的 requestChatPromptPolish 及其单测未改动,继续作为这条 LLM 通道的契约测试
- 护栏:chatPromptPolish.test.tsx(16 条)与 resourceReferenceInput.test.tsx(20 条)全绿
|
2026-09-11 16:53:33 +08:00 |
|
suzmii
|
3de2ec734b
|
预览门禁去掉单点依赖:observer 必定建出,并加可见性兜底扫描
真机现场:整个「UI 交互」栏(51 项)全部只剩占位图标。根因链是**同一对文件里的两个缺陷叠加**,因此作为一个原子提交收口(拆成两半任一半都不足以独立成立:光修 observer 仍可能被回调时序漏掉,光加扫描则 observer 根本不存在时的判定精度无从恢复)。
一、确认 2a157ea6f 引入的回归:observer 可能永远不建
- 那次改动把「root 为 null」当成「不建 observer」,并指望「卡片注册」作为 root 就绪信号重建。
- 但那个 effect 每个 scope **实际只跑一次**(`requestPreview` 是稳定引用,其余依赖在 scope 内不变),一旦这一轮因 root 为 null 直接返回就再无第二次机会;
- 而「卡片注册」信号只在**后续还有新注册**时才来 —— 初次挂载时 51 张卡一次性注册完,之后再没有新注册,信号永不再来。
- 结果:observer 永不创建、所有卡永不被观察、永远停在 `idle`,整栏只剩占位图标(`categoryIcons['ui-interaction'] = LayoutGrid`)。
- 修复:**绝不拿"root 未就绪"当作"不建 observer"的理由**。拿到容器 root 最好;拿不到就退到 canvas、再退到视口(`root: null`)。视口判定严格优于"没有 observer"——屏幕上真实可见的卡一定落在视口矩形内;代价只是视口外但仍在画布内的卡晚一步由滚动触发。容器 root 迟到时再有界重试(5 次、退避到 512ms)重建一次,恢复按画本容器裁剪的精确判定。
二、系统性防线:可见性兜底扫描(去掉单点依赖)
- 门禁原先只有 `IntersectionObserver` 一条路,observer 没建出来 / 回调没送达 / 注册与创建交错,卡片就再无第二次机会。
- 新增 `sweepVisiblePreviews()`:按**与 observer 对齐的视口几何判据**(含 160px 余量)独立复核登记表,只对 `idle`(从未请求)的卡补一次 `visible`,`loading`/`queued`/`loaded`/`failed` 一律不碰。
- **不是第二条加载通路**:仍经 `requestPreview` 走同一条队列,受同一套去重、优先级、全局 3 槽与 `48` 项 / `64 MiB` 预算约束;**只补"可判定为可见"的卡,不退化成全量预读**(PRD §3.3.2 合同不变)。
- 触发点:scope 变化后 0 / 250 / 1000ms 各扫一次(覆盖晚挂载的卡)、observer 就绪后立即扫一次、每次卡片注册时扫一次、窗口 resize 与页面重新可见时各扫一次。
测试(`tests/useProjectResourceCardPreviews.test.ts`)
- 更新「等真正的 root 就绪」这条契约:它原先断言"root 为 null 时不得建 observer"——那正是本轮回归的语义,改为断言"**仍然必须建出 observer**(`root: null` 视口判定)",并要求容器 root 迟到后重建为容器 root;
- 新增「不在热预取窗口内的可见卡不得停在 idle」(20 张 / 热预取 12);
- 新增「没有任何 observer 回调时,可见卡也必须被放行」——完全不触发回调,只靠几何可见性,专门守住兜底扫描;
- 新增「视口外的已登记卡不得被请求」——守住"不退化成全量预读"。
变异验证:把兜底扫描改成直接 `return 0`(等价于去掉扫描)→ 「没有任何 observer 回调时可见卡也必须被放行」立即失败(`expected undefined to be 'loaded'`);恢复后 21/21 通过。断言不是恒真假守卫。
可观测性:`data-preview-status` / `data-preview-error` 已在位,因此"可见却 idle"这类问题以后读一个 DOM 属性即可判定,不再需要解码图片或猜调度。
验证(同一时刻、同一命令的前后对照;树上另有并发改动):改动前 `86 files / 1225 passed / 4 skipped / 0 failed`;改动后 `86 files / 1227 passed / 4 skipped / 0 failed`(多出的 2 条为本提交新增用例)。`src/components/image-editor` 1385 passed;typecheck exit 0;check:encoding 4384 文件;prettier 与 eslint 干净;`git diff --check` 干净。
|
2026-09-11 16:49:09 +08:00 |
|
suzmii
|
e088ede217
|
让「当前使用」的资源卡真的发光,并修掉版本高亮的两处既有缺陷
- .game-resource-card.is-current-version 的外向扩散层从 0 0 0 2px /14%(那是描边)改成 0 0 0 3px /32% + 0 0 20px /34% 的真发光,边框口径仍是 1px
- 新增 game-resource-card-current-version-breathe 关键帧与 ::after 光环(只呼吸 opacity,照本页 .ui-editor-status-attention 的既有写法):声明档位 38%,呼吸区间 0.58 ↔ 1,视觉上约 22% ↔ 38%
- prefers-reduced-motion 下关掉动画但保留可见档位(opacity 0.75),收敛动画不收敛可见性
- 缺陷①:.game-resource-card.is-relation-version-binding 原本只写 border-color,而卡片本体 border: 0,规则从未生效(「点击版本卡后高亮绑定资源」这条 PRD 语义一直看不见);改为真实的 1px 边框 + ring,并把该规则移到 .is-current-version 之前,让常驻光环在两者同时命中时仍然赢
- 缺陷②:当前版本规则与 hover/focus/selected 同权重且在后,导致当前版本卡几乎看不出被选中;新增 .is-current-version.is-selected / :hover / :focus-within / .is-dragging 变体,显式提权到 (0,3,0) 并带上选中态自己那层阴影
- 测试:新增 resourceCardCurrentVersionStyle.test.ts(外向扩散层档位、呼吸实现方式与关键帧区间、选中态变体权重更高、reduced-motion 下仍可见、版本聚焦规则不再是死规则)
- 测试:resourceVersionSwitch 补 data-used-by-current-version 的 DOM 断言(命中为 "true"、未命中不含该属性、切版本后跟着换手)
- 测试夹具修正:project-development.suite 的版本夹具把 slotId 从 'player' 改成恒等映射 'asset:asset-player'('historical' → 'asset:asset-removed'),否则「当前版本」判定永远不命中、照它写断言必成假守卫;并补一条自证断言钉住它真的命中
|
2026-09-11 16:42:58 +08:00 |
|
suzmii
|
082d8d62de
|
把运行页签的资源信息搬到画布选中工具条,两处共用一份只读字段
- 新增「信息」动作:排在「@引用」之后、「编辑标签」之前,点开非模态信息浮层,再点一次即收起(aria-pressed 反映开合)
- 新增只读字段组件 ResourceInfoFieldsView(ResourceInfoPanelView.tsx),运行页签的「信息展示」与画布浮层共用,字段清单只在 resolveResourceInfoFieldRows 单点定义
- 字段口径:名称 / 路径 / 类型 + 只读分类与标签(分类取画布栏目口径,标签为空时给「暂无标签」),刻意不含来源任务、资产 ID 等 manifest 内部标识
- 浮层 chrome 与快速编辑浮层同族(同一锚点、同档圆角与阴影),样式落在 resourceCanvasChrome.css;关闭入口三处:动作按钮、面板关闭按钮、画布浮层统一的 Esc / 点边界外
- 关闭判据收口:resolveResourceCanvasQuickEditDismissOpen 更名为 resolveResourceCanvasFloatingPanelDismissOpen、参数改为 isFloatingPanelOpen,宿主把「快速编辑开」与「信息开」合成一个布尔
- .game-resource-info-panel 补进 RESOURCE_CANVAS_INTERACTION_SELECTOR,否则点在只读字段值上会被判成点画布空白而清掉选中
- 换选 / 清空选中 / 切项目时信息浮层自动收起:在选中派生处按 selectedResourceId 收口,不在每个重置点各补一行
- 测试:新增 resourceCanvasInfoModel 三条与「信息面板在画布浮层与运行页签里渲染同一份只读字段」(工具条顺序、开合、关闭按钮只收面板、点画布以外收起、Esc 连工具条一起收、运行页签字段逐行相同);resourceCanvasFloatingDismiss 的判据用例与交互选择器夹具改为浮层通用口径
- resourceCanvasChrome.css 的字段排版收在浮层选择器下,不跨上下文改运行页签的字号与列宽
- decision-log 新增一条:记录这次对 Issue #309 C3「取消资源详情面板」的有意局部回退(只回退「看信息」,不恢复可编辑详情面板与全屏编辑路由),并登记需要同步的 C3 引用位置
|
2026-09-11 16:42:40 +08:00 |
|
suzmii
|
d80011656b
|
把资源卡的 @ 引用入口挪进选中资源工具条
- 删除资源卡右上角的 @ 引用圆钮,连同它的 44×44 热区伪元素与全部样式一起退役
- 选中资源的画布工具条新增「@引用」动作,排在「UI 编辑器」之后,出站负载与来源逐字未变(source: 'resource-card')
- 测试:原「卡片圆钮视觉尺寸」用例改写为「入口只留在工具条里」的源级 + 样式级断言,并补反向断言卡片组件内不再出现插入调用
- 测试:新增键盘通路用例,聚焦后回车必须真正派发 agc-resource-reference-insert,且两条通路负载逐字相同、每次激活只派发一次
- harness 注释同步:资源卡本体只剩「选中资源」与媒体播放钮
|
2026-09-11 16:28:48 +08:00 |
|
suzmii
|
ea9668e4a9
|
资源总览「所有资源」卡补上资源预览
- 现象与归因:「所有资源」卡的预览区一直是空的,原因是我在 1cc9a2381 里加的闸 `!isResourceBookAllTarget(category)`(当时为避开"同一张资源卡挂两份")让它一张卡片计划都不生成;预览卡从来只由画本场景注入,占位摞本身不含卡片
- 改为真正生成卡片计划:喂进去的仍是全量投影(与标题栏计数徽标同一份口径),但当成一摞交给栏目卡同一个选取函数 `selectResourceBookOverviewCards`——没有视图中心时按投影顺序取前 `RESOURCE_BOOK_OVERVIEW_STACK_LIMIT`(=3) 张,与栏目卡每摞上限同口径;不分类型列,避免 66 项一口气铺出十几列
- 预览用同一个 `renderCard` 与同一套 `resourceBookOverviewCardLayout` 几何渲染,唯一差别是宿主类名换成 `.game-resource-book-preview-card`:转场层按 `.game-resource-book-scene-card` 收集 FLIP 节点并以 `card:${resourceId}` 为键,同一张卡两个宿主会让两条记录互相顶掉,因此预览宿主不进收集范围(`resourceBookController` 一行未改)
- 预览宿主带 `aria-hidden` + `pointer-events: none`:无障碍树与 `getByRole` 里每张资源卡仍然只有栏目卡那一份,不会重复命中;点预览区仍走进「所有资源」页,栏目卡预览卡仍可选中
- 转场淡出集合(`resourceBookPlanKeysRef` 那段)跳过「所有资源」组,避免预览节点被当成"离开视图的卡片"按子画布坐标重新贴到画布别处
- `ResourceBookThumbnail`:该卡正文的占位摞从最多 3 摞改为对齐真实摞数的 1 摞,卡片高度回到与栏目卡同一条口径
- `styles.css`:把 `.game-resource-book-preview-card` 并进 `.game-resource-book-scene-card` 的两条既有规则(宿主几何、主态卡片圆角/阴影)与 `prefers-reduced-motion`;没有新写一套预览样式
- 断言(`project-development.suite.ts`):预览卡的 `data-resource-card-id` 序列恰等于全量投影前 3 个、条数 == `min(3, 计数徽标)`、预览里是完整卡片(视觉体 + 选中按钮)、宿主在 `aria-hidden` 子树里、宿主几何规则与栏目卡共用同一条
- 断言(新增用例):空项目下沿用既有空态「暂无资源」、预览节点 0 个、计数 `0 项`、入口行仍在,卡片不破版
- 单测(`resourceBookLayout.test.ts`):把已被新行为取代的旧断言(`all` 组不出卡片、全计划无重复卡片键)删掉,换成新合同(预览 = 全量投影前 3 张、只铺一列、各栏目摞不变),并补空投影 → 0 张
- 变异验证(两条都跑过并已还原):① 喂进去的资源改回空数组 → `AssertionError: expected +0 to be 4`;② 恢复旧闸(计数仍正确、预览为 0)→ `AssertionError: expected [] to deeply equal [ Array(3) ]`
|
2026-09-11 15:37:17 +08:00 |
|
suzmii
|
6bdc8bbd93
|
AGC 资源分类面板改为「编辑素材标签」:只编辑标签、分类不再有手动入口
- ResourceClassificationPanel.tsx:面板改为只编辑 manifest `assets[].tags`,移除分类 6 类
chip 那一排;标题与 aria-label 改「编辑素材标签」,副标题由资源路径改为素材名
(`localPath` 的 basename,仓库没有独立显示名字段)
- ResourceClassificationPanel.tsx:标签状态由「整段逗号分隔字符串」改为字符串数组,
每个已有标签渲染成自带删除按钮的胶囊 pill;保存时把未落成 pill 的输入尾巴一并切分,
归一化仍走 `normalizeGameCreationAppAssetTags`,回车 / 中英文逗号 / 顿号提交方式不变
- ResourceClassificationPanel.tsx:写入命令 `category` 是必填,读一次当前权威值并
在保存时原样回传,保证「只改标签」不漂移分类;底部按钮「保存」改「保存标签」,
删除资源入口按既有保留(截图未画,但它是唯一的资源删除入口)
- resourceClassificationTagPanel.css:新增标签 pill 样式,删除按钮嵌在 pill 内部
(视觉图标 11px、热区 32×32、负外边距抵消不撑高 pill);若第二处出现带删除按钮的
标签 pill,抽到 `packages/shared` 做 `PlatformRemovableTagPill`,不要复制这份实现
- index.tsx:资源卡工具条入口 label / title / 文案由「分类与标签」改「编辑标签」,
与面板标题一致(否则入口写着分类、打开的是纯标签面板)
- tests/resourceClassificationPanel.test.tsx:断言对齐截图文案、逐个 pill 自带删除、
点某个 × 只删对应标签、aria-label 可区分、保存 payload 的 tags 数组与 category 原样回传、
取消不写盘、入口 label 与面板标题一致、删除热区不小于 32px
- 文档:PRD §5.3「分类取值优先级」改写为当前状态(分类没有用户手动设置入口、
该面板只编辑标签),并新增 decision-log 条目记录本次产品决策与「用户手动设
category 的能力就此移除」这一事实
- 说明:截图文案(编辑素材标签 / 新增标签,多个用逗号分隔 / 保存标签)在全仓检索
无命中,属仓库之外来源,本次按用户截图对齐,不是 PRD 明文
|
2026-09-11 15:34:36 +08:00 |
|
suzmii
|
2a157ea6f8
|
预览可见性门禁等 root 就绪再建 observer,并把登记表补挂齐
真机现场:同一屏里 `idle` 与 `loaded` 交错,12 张相交卡从未入队(`idle` 的语义就是「从未请求」,不是被淘汰回退)。根因在 observer 的**创建时机**:`useProjectResourceCardPreviews` 建 IntersectionObserver 时把 `intersectionRootRef.current ?? canvasRef.current` 直接当 root 传下去,而 root 是资源画本容器、由被观察卡片所在的子树持有。创建那一刻 root 还是 `null` 时,浏览器会**退回按视口判定**,于是被画本容器裁掉的卡片永远报「不可见」,可见性门禁再也不放行它们 —— 卡面只剩占位图标,且没有任何错误提示。
- `useProjectResourceCardPreviews`:root 为 `null` 时**不建 observer**,等 root 就绪后由卡片注册触发的 `rootEpoch` 重建。判据是「卡片一定渲染在画本容器内部」,所以**有卡片注册本身就等价于 root 已就绪**,不需要新增跨组件契约、也不必改 `index.tsx`。
- 新增 `attachObservedCards()`:observer 就绪或重建后,按登记表把**每一张已注册卡片**补挂一遍。此前只在新 observer 创建时补挂一次,注册与创建分属不同 effect 存在时序窗口,错过那次补挂的卡会停在登记表里却从未被观察。
- root 未变且 observer 已在时只补挂、不重建,避免每次渲染重建观察器。
- `observePreview` 在没有 `IntersectionObserver` 的环境(如单测)保持静默,不制造多余渲染。
测试(`tests/useProjectResourceCardPreviews.test.ts` 新增 3 条契约):
- 「不在热预取窗口内的可见卡不得停在 idle」:注册 20 张、`eagerPreviewLimit: 12`,断言注册进 observer 的元素数等于注册数、回调报可见后全部落 `loaded`、且没有一张停留在未请求状态;
- 「等真正的 root 就绪后再观察并加载」:root 首次为 `null` 时**不得建 observer**(否则按视口判定),注册后必须以真正的 root 建出来并完成加载;
- 「建 observer 之前就注册的卡要被补挂」:断言 observer 就绪后登记表里的每一张都在观察集合内,且报可见后全部 `loaded`。
变异验证:把 root 守卫退回旧行为(允许 root 为 `null` 时照建 observer)后,「等真正的 root 就绪」这条立即失败(`expected [] to have a length of 0 but got 1`);恢复后 19/19 通过。断言不是恒真假守卫。
边界说明:只改预览 hook 与它的测试,未动 `index.tsx` / `styles.css` / `resourceBookLayout.ts`(均在他人手上),未放宽任何既有断言、未取消可见性门禁(仍是按需加载)。
验证:`npm run test -- apps/ai-game-creator-shell/tests` 84 files passed / 1216 passed / 4 skipped / 0 failed;`src/components/image-editor` 1385 passed;typecheck exit 0;check:encoding 4379 文件;prettier 与 eslint 干净;`git diff --check` 干净。
|
2026-09-11 15:31:29 +08:00 |
|
suzmii
|
39a96aa418
|
模型下拉菜单高度有界:健壮性补强,条目多时不再盖住消息
Project CI / Repository checks (pull_request) Successful in 2m36s
Project CI / Frontend tests (pull_request) Successful in 3m20s
Project CI / Backend tests (pull_request) Successful in 6m29s
Project CI / Native shell tests (pull_request) Successful in 18m34s
- 性质说明:本条是**健壮性补强**,不是用户本轮报的那几条之一;由上一提交(锚点修复)排查时顺手发现的未修观察收尾。
- styles.css:`.conversation-model-menu` 补 `max-height: min(240px, 40vh)` + `overflow: auto` + `overscroll-behavior: contain`。菜单此前没有高度上限,模型条目很多时会长到 composer 顶边以上、盖住消息;240px 与视口 40vh 取小者,矮窗 / 窄屏(移动端优先)更稳。`overscroll-behavior: contain` 与既有 `.resource-reference-menu` 同一口径,菜单内滚动不传染给背后的消息列表。不新增视觉:border / radius / padding 与 token 全部沿用原值。
- 语义零改动:锚点与流式归属不变(`position: absolute; right: 0; bottom: calc(100% + 8px)` 未动),打开 / 选择 / 刷新模型列表的交互不变。
- project-development.suite.ts:新增声明级守卫「bounds the model dropdown height so a long catalog cannot cover the composer」——菜单规则必须同时声明 `max-height: min(240px, 40vh)`、`overflow: auto`、`overscroll-behavior: contain`,并仍是 `position: absolute`(高度有界不改锚点)。
- 变异验证(已实测):把 `max-height` 摘掉 → 用例红灯(expected '…' to match /max-height:\s*min\(\s*240px\s*,\s*40vh\s*\)/u)。
- 门禁:typecheck exit 0;AGC 子集 1206 passed / 4 skipped / 0 failed(基线 1205 + 本次新增 1 条);src/components/image-editor 1385 passed;check:encoding 4379 文件通过;`git diff --check` 全量与本次两个文件范围均 exit 0。
|
2026-09-11 14:59:42 +08:00 |
|
suzmii
|
4f3516a5d9
|
修复分类 chip 条叠字:分段页签子项补内容宽度下限
- packages/shared/src/components/PlatformSegmentedTabs.tsx:容器补 `platform-segmented-tabs`
稳定钩子类,供共享样式表精确命中子项(不依赖 Tailwind 任意值扫描)
- packages/shared/src/components/styles.css:新增 `.platform-segmented-tabs > button`
的 `min-width: max-content`,给每个子项一个「内容宽度」下限;放不下时由容器横向滚动兜底
- apps/ai-game-creator-shell/src/view/project-development/ResourceClassificationPanel.tsx:
「资源分类与标签」的 6 类分类条由 `columns="threeToSix"` 改为 `layout="scroll"` +
`frame="bare"` + `surface="transparent"`,与 `@` 面板和资源画布的分类条同一条既有形态
- packages/shared/src/components/PlatformResourceFilterBar.test.tsx:新增两条声明级断言,
钉住「子项不换行 + 容器横向可滚 + 子项有不低于内容宽度的下限」
|
2026-09-11 14:54:27 +08:00 |
|
suzmii
|
ff287c19fc
|
模型下拉弹层改回以触发钮为锚点,输入区高度变化不再牵动它
- styles.css:direct-codex 控制排里的 .conversation-model-select 由 position: static !important 改为 position: relative !important(只改这一个词,另补一段注释说明它同时是弹层锚点)。这颗容器是 .conversation-model-menu(position: absolute; right: 0; bottom: calc(100% + 8px))与模型提示([role='alert'] / [role='status'],position: absolute; bottom: 36px)的包含块;写成 static 时包含块回落到最近的有定位祖先——也就是 position: absolute 的 composer 盒子,菜单于是出现在整个 composer 上方、与触发钮之间隔着整个输入区,输入区一变高(多行、引用 chip、AI 润色)菜单与提示跟着往上飘:这就是"编辑框把元素挤开"的形态。
- styles.css:right/bottom: auto、flex: 0 1 auto、min-width: 0、max-width: 180px 全部保持原样——relative 不会把模型选择钮移出控制排,也不改它被压缩换行的行为;交互语义零改动(打开/选择/刷新模型、发送、@ 引用、快速模式全在原位)。
- project-development.suite.ts:新增一条声明级守卫。① 弹层不参与父容器流式布局(菜单与模型提示都是 absolute,菜单 bottom: calc(100% + …)、right: 0);② 锚点必须是有定位的容器(relative 且不是 static),同时仍在原位(right/bottom auto、flex 0 1 auto、min-width 0);③ 输入区高度变化不得推动同排操作元素(composer 是底边锚定的两行网格:position: absolute + bottom: 12px + grid-template-rows: auto auto;控制排是单行 flex 且不换行,@ 与发送钮 flex: 0 0 30px 不可压缩,窄屏下被压缩的是可收缩的模型选择钮)。
- conversationModelSelect.test.tsx:新增一条 DOM 守卫——菜单必须是 .conversation-model-select 的直接子节点,且不得成为控制排容器 .project-supervisor-composer-controls-right 的流式子节点。
- 变异验证(均已实测红灯):① 锚点改回 static → "expected … to match /position:\s*relative\s*!important/";② 菜单改成 position: static(弹层回流式)→ "expected … to match /position:\s*absolute/";③ 把菜单渲染挪出触发钮容器(变成同排容器的流式子节点)→ DOM 守卫 "expected null not to be null"。
- 门禁:typecheck exit 0;AGC 子集 1205 passed / 4 skipped / 0 failed(基线 1203 + 本次新增 2 条);src/components/image-editor 1385 passed;check:encoding 4379 文件通过;本次三个文件单独 `git diff --check` exit 0(全量 `git diff --check` 目前报的是另一位 Agent 在途修改的 packages/shared/src/components/styles.css:1281 EOF 空行,与本次无关)。
|
2026-09-11 14:53:11 +08:00 |
|
suzmii
|
9c4778c881
|
把资源卡右下角的 @ 引用按钮做小:视觉缩到 0.8×,热区保持 44×44
Project CI / Repository checks (pull_request) Successful in 3m31s
Project CI / Frontend tests (pull_request) Successful in 4m24s
Project CI / Backend tests (pull_request) Successful in 6m51s
Project CI / Native shell tests (pull_request) Successful in 18m37s
- styles.css:`.game-resource-card-reference` 视觉尺寸 30×30 → 24×24(0.8×),新加 `.game-resource-card-reference svg` 把内部 `AtSign` 图标同步从 16px 收到 13px,圆角/描边/底色/阴影 token 全部沿用原值。
- styles.css:`bottom` 由 9px 调到 14px,与右侧 34px 播放钮圆心对齐(9 + 34 / 2 = 14 + 24 / 2),避免缩小后两颗圆钮错位。
- styles.css:新增 `.game-resource-card-reference::before` 伪元素撑出 44×44 热区(本仓 window chrome / 素材选择列表同口径的最小可点尺寸),只扩命中范围、不参与布局也不改变绘制;整体向左让 4px,让热区右边缘与播放钮的盒子恰好相邻,不覆盖它。
- 语义未动:仍是「插入素材引用」入口,className、`aria-label={\`引用资源 ${resource.label}\`}`、key 与 `dispatchResourceReferenceInsert` 调用一行未改,本次只改 CSS 声明。
- 测试:新增 CSS 声明级用例,断言按钮与图标确实变小(24 < 30、13 < 16)、热区不小于 44×44、热区右边缘不越过播放钮盒子,并顺带钉住 `index.tsx` 里 @ 入口的 className / aria-label / 插入动作仍在。
- 变异验证:把按钮改回 30px、热区改回 24px、图标改回 16px → 用例红灯;只把热区缩回 24px → 用例同样红灯(均已实测,随后原样还原)。
|
2026-09-11 14:00:32 +08:00 |
|
suzmii
|
29b99f213d
|
删除资源画布左侧的栏目大纲导航:切栏目改由总览卡片、所有资源入口与下一页承担
- index.tsx:整条删除 `ResourceBookOutlineNav` 组件(`.game-resource-outline` / `aria-label="资源栏目大纲"` / 栏目图标 + 栏目文字 + 未读红点)以及栏目页与「所有资源」页两处挂载点;`ResourceBookAllResourcesPage` 不再接收只服务导航的 `unreadCategories` / `onOpen`。
- index.tsx:一并清掉只服务这条导航的「未读红点」状态链(`unreadResourceCategoryState`、`resourceCategorySnapshotRef`、`resourceIdsByCategory`、`viewedResourceCategory` 与对应 effect),不留死代码、不留墓碑注释。
- styles.css:删除全部 `.game-resource-outline*` 规则(悬浮 Dock、常态/悬停态、图标列宽、`.game-resource-outline-label`、`.game-resource-outline-unread`,共 106 行)。
- 保留未动:分区栏目本身、栏目标题栏、资源卡、总览缩略卡片、第 0 张「所有资源」入口、「所有资源」汇总页、搜索、总览、快速编辑与选中工具条。
- 测试:切栏目 helper(appSurface/project-development、appSurface/home、resourceRename、resourceVersionSwitch)改为走「资源总览」缩略卡片(需要时先点「收起资源」回总览),不再点导航;栏目顺序断言改钉在总览缩略卡片的 `aria-label` 序列上(第 0 张「所有资源」+ 固定七栏目),断言内容未放宽。
- 测试:按「四不写」删除只守这条导航的断言——大纲常态可读性的 CSS 声明用例、大纲条目图标/文字的 DOM 用例(该用例保留其中与搜索浮层相关的断言并改名)、未读红点用例(该用例保留"只登记游戏代码的项目照常分页并落在待归类"的部分并改名)、空项目与「所有资源」页里的导航存在性断言。
- 测试:新增反向守卫 + 切栏目链路用例(`has no outline nav and keeps every section reachable through the remaining entries`):断言 `资源栏目大纲` 标签、`.game-resource-outline` 类名与对应 `<nav>` 都不再渲染,并逐条走通总览缩略卡片(含空栏目)→ 下一页 → 标题栏「资源总览」→ 「所有资源」入口往返。
- 测试:`preserves independent art viewports across sort and workbench mode switches` 保留全部原断言,只去掉排序模式切换后那些"点当前栏目"的空操作往返(旧导航点在同栏目上是提前返回,改 helper 后变成真实往返,会在 jsdom 里触发重新 fit)。
|
2026-09-11 13:54:12 +08:00 |
|
suzmii
|
083df0aa2c
|
类型判定改为内容证据优先:消除 kind 与内容证据同权的脆弱点
**口径更正(后加的)**:本条提交**不是**"修复真机那 8 张卡"。经用真源码复算真机 manifest 确认,当前 HEAD 上这 8 张 `.json` 本就正确落 `document`、角标「文档」,该现象不复现——用户看到的是更早构建的行为。本条的真实价值是**消除 `kind` 与内容证据同权的脆弱点,并把「派发决策由内容证据决定」钉成不变量**,见下。
- 背景:真机「待归类」栏目曾出现 8 张卡——角标写「图片」、卡面却只有占位图标。该现象在当前 HEAD 上不复现(见上),但它对应一个**真实存在且此前只是碰巧没被踩中的脆弱点**:`projectedResourceKind` 里 `kind` 与内容证据**同权**,而 `artKind` 含 `ui`;一旦界面规格类资产丢了 `.json` 扩展名,预览派发就会翻车。原先能通过**完全依赖 `.json` 恰好在那张扩展名表里**,属巧合而非设计。
- 机制(旧行为):`kind:"UI"` 被 `artKind` 命中 → 判定为 `art`;预览调度再按 `art` 走图像分支,又因 `mediaType:"application/json"` 不是图像而兜底成 `placeholder`,而 `requestPreview` 对 `placeholder` 直接返回——**一次 IPC 都不发**,卡片既不报错也没有图。
- `resourceProjectionModel.ts` 的 `projectedResourceKind` 改为两趟判定:第一趟只看 `mediaType` 与路径扩展名(文件内容的可验证证据),第二趟才看 `kind`(登记标签,可能过时或宽泛)。`kind` 不再与内容证据同权,因此 `kind:"ui"` 无法再把 `application/json` 改写成 `art`。
- 不改 `artKind` 的取值(`ui` 仍在表内),只调整判定顺序:最小改动面,`kind:"ui"` + `image/png` 这类正常资产行为不变(仍由 `mediaType` 命中)。
- 界面规格 JSON 现在落 `document` 分支,走 `read_local_project_text_preview`(Rust 侧 `.json` 与 `application/json` 本就在支持范围内且这些条目已登记),卡面渲染 JSON 文本摘要,角标随 `projectResourceTypeLabel` 变为「文档」。预览链条数、缓存、并发与门禁一律未动。
- `tests/resourceCardPreviewRealManifest.test.ts` 中记录旧行为的用例同步跟上新合同:`placeholder: 8` 改为 `document: 8`,用例名与注释改写为「52 张 PNG 走图片分支,8 条 UI 规格走文档分支,无一落占位」,并新增「角标与预览分支必须说同一件事」的逐条断言。旧断言记录的是缺陷本身,按「四不写」直接更新为当前权威行为,不做兼容保留;其余既有断言一条未放宽。
- 验证:`npm run test -- apps/ai-game-creator-shell/tests` 1202 passed / 4 skipped / 0 failed;`src/components/image-editor` 1385 passed;typecheck exit 0;check:encoding 通过;`git diff --check` 干净。
|
2026-09-11 13:30:14 +08:00 |
|
suzmii
|
5a37ccfd2b
|
资源卡暴露预览状态,并补「派发决策由内容证据决定」的契约用例
占位分支对「还没读 / 读失败 / 压根不适用」给的是同一个图标,卡面也没有任何文字提示,现场只能看到「没有图」,无法区分是调度没发请求、原生拒绝了读取,还是这类资源本来就没有预览。本轮排查「图片加载不出来」时,这个不可观测性正是反复误判的来源。
- `index.tsx` 的 `ResourceCard` 根节点新增 `data-preview-status`(`idle` / `loading` / `loaded` / `failed`)与 `data-preview-error`(仅失败态给出原因)。排障从此只需读一个 DOM 属性,而不必去猜调度层。纯新增属性,不改变任何渲染分支与调度行为。
- 新增 `tests/resourcePreviewPredicateContract.test.ts`:把「派发决策必须由可验证的内容证据(mediaType,缺失时退路径扩展名)决定,登记标签 `kind` 只能在两者都给不出结论时兜底」钉成不变量。用 7 组「mediaType + 同通道扩展名」× 20 个 `kind` 的全量笛卡尔积断言 `kind` 不能改写预览分支,并断言组合总数等于笛卡尔积(防止将来收窄枚举让断言悄悄变弱)。另附本轮缺陷形状的最小复现(`application/json` + 任意图片类 `kind` 必须落 `document` 而不是 `placeholder`)、mediaType 缺失时由扩展名决定、以及扩展名与 mediaType 冲突时按既有设计由扩展名决定这三组显式合同。
- 该用例在**变异验证**下确实会红:把 `kind` 判定移回第一趟(还原本轮的分叉)后,本文件 4 条用例失败(不变量报出 56 处违规),恢复后全绿。断言不是恒真假守卫。
- 验证:`npm run test -- apps/ai-game-creator-shell/tests` 84 files passed / 1202 passed / 4 skipped / 0 failed;`src/components/image-editor` 1385 passed;typecheck exit 0;prettier 与 eslint 干净;check:encoding 4378 文件;`git diff --check` 干净。
|
2026-09-11 13:26:17 +08:00 |
|
suzmii
|
66bb240115
|
进项目时按内存 registry 核对预览活体,陈旧记录不再冒充可运行预览
- 新增 features/app-shell/sessionPreview.ts 的 resolveSessionPreviewOnProjectOpen:进入项目时问一次 get_local_game_preview_status(内存 registry 是活体的唯一真相),确实 running 才把它作为本次会话的预览;记录写着 running 而 registry 没有活体时判为陈旧,复用现役 stop_local_game_preview 把落盘记录对齐成 stopped,并同时把会话 manifest 按真相投影成 stopped(否则运行入口会继续拿着打不开的 URL 渲染运行画面)。
- useHomeProjectCreation.ts:enterProjectDevelopment 改为 async,先做这次核对再进工作台,并按核对结果决定会话预览与 manifest 投影;三个调用点(新建项目、创建项目、打开项目)都改为 await。
- preview.rs:同一个项目重启预览时,旧 registry 身份的收尾不再覆盖刚落盘的 running——只有当旧预览属于**另一个项目**时才替它收尾。此前同项目重启会把刚写进去的 running 覆盖成 stopped,下次进项目便丢掉"预览在跑"这条事实(记录反方向失真)。
- preview.rs:stop_local_game_preview_for_root 在没有活体可停、但落盘记录仍是 running 时也把记录收尾成 stopped(stop 的语义变成"真的停干净,包括记录"),因此陈旧记录既可由进入项目触发对齐,也可由用户显式停止对齐。
- tests/project.rs:新增三条 Rust 定向用例——同项目重启后记录仍为 running 且端口是新预览的;换项目启动时旧项目记录写 stopped、新项目写 running;registry 无活体而记录写着 running 时 stop 会把记录对齐成 stopped。
- tests/sessionPreview.test.ts(新增):五条用例覆盖活体优先、陈旧记录对齐并投影、registry 不可读按没有在跑处理、记录本来不是 running 时不发多余停止命令、无 invoke 时零请求。变异验证:把活体核对去掉(直接信落盘记录)→「registry 里没有活体时不给会话预览」与「registry 不可读」两条立即红灯(已实测)。
- project-development.suite.ts:把「打开项目不查预览状态」那条断言改为「恰好核对一次预览活体」,并保留邻近的 start_local_game_preview 必须为 0 的断言——被新行为取代的是"从不核验","开项目不偷偷启动预览"这条原意一字未改。
- PRD §4.1:状态机按当前实现改写(进入项目一律落到 resource-overview;run 的自动进入只认内存 registry 的活体确认;落盘 preview 是记录而非活体,陈旧记录不触发切换也不参与渲染),并把退出收尾一并写进该节说明。
|
2026-09-11 12:27:31 +08:00 |
|
suzmii
|
3ed567d99a
|
运行视图的自动切换只认会话内确认过的预览
- index.tsx:自动切运行的那条 effect 从「合并后的预览 URL」收窄为「会话预览 URL」(resolveEmbeddedPreviewUrl(previewOverride))。manifest.preview 是"上次留下的一条落盘记录":预览服务器是进程内线程、非 Linux 端口还是随机临时端口,客户端退出后记录仍会写着 running,照着它自动切视图就是把用户丢进一个打不开的运行界面。运行入口(运行 tab、播放按钮)与 runAvailable 一行未动,PRD §4.1「运行入口不可用时仍允许点击…但不切换状态」继续成立。
- useHomeProjectCreation.ts:enterProjectDevelopment 不再把 context.manifest.preview 播种成本次会话的预览(改为 null)。会话预览此后只由活体确认(下一提交)与本次会话里 preview.start / preview.status 的返回值经 onPreviewChange 回流两处写入。
- 有意接受的代价:「回到仍在运行的预览」从自动变成用户点一下「运行」;下一提交会把"确实还在跑"这条路径按活体确认接回自动进入。
- project-development.suite.ts:新增两条用例——① 仅 manifest.preview 为 running(不传 preview)时初始仍是「资源管理」、运行 tab 仍可用、点一下仍能进运行视图;② 会话预览为 running 时自动切到运行表现层。
- 变异验证:把这条 effect 还原成读合并后的 URL(即撤掉本次收窄),用例①立即红灯(已实测)。
|
2026-09-11 12:07:44 +08:00 |
|
suzmii
|
28bb5800ea
|
资源总览去掉大标题旁的副标题
- 删除资源总览标题栏里那颗副标题的**两个分支**:`${canvasResources.length} 项正式与候选资源` 与空项目时的 `从资源栏目开始整理资源`
- 位置:`apps/ai-game-creator-shell/src/view/project-development/index.tsx` 的 `<header className="game-resource-book-main-heading">`(基线 364492de3 的 5867-5871 行那颗 `<span>`,标题栏自 5863 行起)
- 同步删除只服务于它的 `.game-resource-book-main-heading > span` 样式,模块内不再留墓碑规则
- 该文案没有承载 `aria-label` 等语义(区域语义一直在容器的 `aria-label="资源总览"` 上),因此不需要把语义迁移到容器,也没有改成"更短的说明"
- 全量计数没有丢:同一次改动后由总览第 0 张「所有资源」卡的计数徽标承载,仍是同一份画布投影
- 卡片内部的说明/装饰文案(「打开 UI 交互」「打开角色与对象」「1 个直接子版本」等)保持不动
- 断言:资源总览标题栏整段 `textContent` 恰为「资源总览」(精确相等,不放宽成 `not.toContain`),并复核总览第 0 张卡与「文档」栏目卡的内部文案仍在
- 既有用例没有任何一条断言过这段副标题文案(全仓检索只命中源文件),因此没有"按文案已删更新"或"按四不写删除"的对象;本用例是补上的正向合同断言
|
2026-09-11 11:28:59 +08:00 |
|
suzmii
|
1cc9a23815
|
资源总览新增「所有资源」汇总卡与全部资源页
- 渲染层新增 `RESOURCE_BOOK_ALL_TARGET = 'all'` 特殊项:跨端契约 `ProjectResourceCanvasCategory` 不加 `'all'`,sidecar schema 不升版,落盘布局的 `section` 也不会出现它
- 资源总览第 0 张卡即「所有资源」:排在全部栏目卡之前,复用同一套缩略图类名/圆角/底色/阴影与栏目标题栏组件,不新增卡片样式
- 该卡的计数徽标取全量资源数,口径等于各栏目计数之和(同一份画布投影,不另算一套)
- 画本场景的栏目顺序与资源分组各多一个渲染层入口;`buildResourceBookScenePlan` 对特殊项只出计数与标题栏,不铺卡片,避免同一张资源卡在总览里挂载两份(`data-resource-card-id` 查询与 FLIP 的 `card:*` 键都会撞车)
- 新增「所有资源」页:栏目大纲 + 收起资源按钮 + 按栏目分组的滚动卡片网格;卡片复用栏目页同一颗渲染器与卡宽高,容器与网格样式补齐在 styles.css
- 该页的卡片显式关掉拖拽(卡片坐标不属于任何栏目画布,写回会污染布局 sidecar),保留选中/播放/菜单交互,并把光标与 `touch-action: pan-y` 交给滚动容器
- 栏目大纲抽成 `ResourceBookOutlineNav`,栏目页与全部资源页共用同一条入口带;渲染出的 DOM 与抽取前一致
- `renderResourceBookCard` 增加可选 `draggable` 参数,`ResourceCard` 的三个移动端指针回调改为可选
- 断言:总览存在「所有资源」卡且为第 0 张、计数等于全量资源数且等于各栏目计数之和、点它进入的页面按栏目分组且每组都挂真实资源卡、场景计划里特殊项不出卡片且整份计划无重复卡片键、全部资源页分组只保留非空栏目且保持总览顺序
- 既有「N 项」徽标断言从全文档文本查找收窄到 character 栏自己的标题栏(新增第 0 张卡后同文案会撞车,收窄而非放宽)
|
2026-09-11 11:21:03 +08:00 |
|
suzmii
|
6cc929e47d
|
修复陶泥儿对话外框没把输入区包住:外框底边不再与输入区重合
- 根因:`.project-supervisor-message-list` 在文件末尾还留着一条同选择器、同权重的
`bottom: 156px`("列表在上、输入区在下"那版两盒布局的残留)。它按"后写胜出"把上一轮
bdb07c141 改的 `bottom: 0` 悄悄顶掉,外框底边落在输入区腰上、输入区下半截露在框外,
真机上看到的就是"消息区外框底边与输入区压在一起、外框没把输入区完整包住"。
- styles.css:把那条残留规则换成对话框盒子的最终几何(top/right/bottom/left 全 0,
`padding-bottom` 与 `scroll-padding-bottom` 都是 256px)。它位置在文件靠后,同权重后写
胜出,顺带把 `:has(.plan-gdd-surface, .planning-lane-runtime-strip)` 给列表留的
`padding-bottom: 12px` 压回去,滚到底时最后一条消息不会被输入区盖住。
- styles.css:输入区补上第四条边——`max-height: calc(100% - 24px)`(24px = 上下各 12px)。
左右下三边仍显式内缩 12px,上边由 max-height 兜底,于是外框完整包住输入区:四条边都不
与外框描边重合,底边留出 12px 边距(外框底边 0 / 输入区底边 12px)。
- styles.css:窄屏(≤760px)改成一个模型——外框画在列表与输入区共同的父节点
`.project-supervisor-conversation` 上,列表撤掉自己那只框(框里不套框),输入区回到
文档流,靠外框 12px 内边距四周留白、随列高增长不溢出。原来那条窄屏输入区规则权重低于
`.project-supervisor-composer.is-direct-codex` 那条,其实一直是死声明,这次才真正生效。
- tests/chatDialogFrameLayout.test.ts:新增用例,先按真实层叠(媒体查询是否命中 -> 特异性
-> 源码顺序)求生效值,再断言外框四边归 0、输入区四边内缩同一个值、底边与左右留白严格
大于 0、留白 256 = 12px 内缩 + 228px 输入区最高高度 + 16px 间距(228 由编辑器
max-height 140、输入框行距 8、操作排 28px 方钮、输入框下内边距 4、输入区上下内边距 16、
操作条 2 + 30 方钮逐项从真实声明里算出来),以及窄屏下外框/列表/输入区各自落位、输入区
仍在外框节点子树里且交互控件原位。
- tests/appSurface/project-development.suite.ts:把那条按"第一条匹配规则"取值、用 230 估
算的留白断言同步成按声明算出的 228 与实际间距 16,并注明层叠生效值的守卫在新用例里。
- 变异验证:输入区 `bottom: 12px -> 0`(还原"贴死重合")时新断言失败;外框
`bottom: 0 -> 156px`(还原上一轮残留)时"外框底边必须严格低于输入区底边"失败(-144)。
- 说明:本机无无头浏览器、vitest 未开 `css: true`,像素级观感只能真机复核;这里钉的是
层叠生效的声明级几何关系与"输入区仍在外框节点的子树里"。
|
2026-09-11 10:36:45 +08:00 |
|
suzmii
|
1da7dc62a0
|
fix(资源画布): 拖拽写入不再触发其它自动卡重派生补位
- useProjectResourceCanvasLayout 新增 AutomaticPositionPolicy(rederive / preserve),取代原先裸布尔 rederiveAutomaticPositions 传参
- 拖拽(manual 意图)写链固定用 preserve:不再丢弃其它自动坐标,其余自动卡原地不动,也不再补位到被拖走的空位
- 资源集合与拓扑身份签名变化仍按 PRD 用 rederive 重派生,关系图就绪后的深度修正不受影响
- 新增 automaticCoordinatesMatch(忽略数组顺序的坐标比较):手动写入后只在坐标真的变化或有新资源需要落位时才继续排一次资源同步,避免只由排序归一触发的第二次 CAS 写入再次把其它卡补位
- 写入后的 updated / conflict 分支统一按 writePolicy 计算 reconciledAfterWrite,并据此判定是否需要资源同步
- rebuildOptimisticLayout 改为接收策略参数,手动写入后的会话布局同样保持其它自动卡不动
- 新增用例:拖走一张后其余自动卡在所有写入里坐标逐项不变、视图里也不补位;变异验证(把拖拽路径改回 rederive)后该断言失败
- 副作用(已确认接受):把卡正好丢在另一张卡上时不再自动挪开它,直到下一次资源集合或拓扑变化触发重派生
- 门禁:AGC 1178 passed / 4 skipped / 0 failed,共享组件 1385 passed,typecheck exit 0,编码检查 4374 通过
|
2026-09-11 10:27:32 +08:00 |
|
suzmii
|
921d86ed44
|
feat(资源画布): dependency 默认铺排改为层内网格平面,不再退化成一列
- 新增 RESOURCE_CANVAS_DEPENDENCY_LAYER_MAX_COLUMNS = 6 与槽位宽高比常量,作为层内网格列数上限
- 新增 normalizedDependencyDepth:落位、层带与层内网格共用同一深度归一,避免同一深度被当成两层
- 新增 dependencyLayerColumnCount:列数 = clamp(floor(sqrt(卡数 × 槽位宽高比)), 1, 6),是卡片数量的纯函数
- 新增 dependencyLayerGrid:层内网格几何,行高统一取该层最高卡,使层高与层内顺序无关(中位数扫描重排后几何不抖)
- 用 dependencyColumnGeometryByDepth 取代 dependencyColumnXByDepth:x 仍只由 dependencyDepth 决定层带基址(depth 仍是唯一横向层级权威),层带宽度 = 列数 × 列间距;全部层为单列时与旧值逐值一致
- 层内按行优先落位,层内排序(稳定 ID 初序 + 两轮左至右/右至左中位数扫描)与深度层级语义不变;网格之外仍按既有向下找空位回避手动坐标与饱和层带
- 移除 dependencyBucketHeight,簇高、簇行数与层内 rank 改用层内网格行数
- 同步 resourceCanvasLayoutModel 测试:新增层内网格平面(12 张同深度 → 4 列 3 行)、重复计算与输入顺序稳定、层带随深度单调且不与换行层重叠、列数封顶 6、手动坐标在自动网格重派生前后逐项不变;同层 3 张卡的环连续性用例改为行优先网格口径
- 影响面:既有项目里已落盘的自动坐标(manuallyPlaced=false)会在下次协调时按新口径一次性重排成平面;manuallyPlaced=true 的历史手动坐标不删除、不迁移、不被自动排序覆盖
- 不新增持久化字段、sidecar schema 不升版,ProjectResourceCanvasSection/Position 契约与 RESOURCE_CANVAS_INITIAL_FIT_MAX_SCALE 均未改动
|
2026-09-11 10:16:01 +08:00 |
|
suzmii
|
84b8f5130b
|
修复资源总览只画当前栏目卡片的问题:拆开身份口径与热预取口径
前一次改动(60d8b8fbb)把 `useProjectResourceCardPreviews` 的 `resources` 从全量投影收窄成当前分页栏目,造成产品回归:该入参不只喂热预取,它同时决定返回的 `identityByResourceId`,而 `renderResourceBookCard` 在身份缺失时 `return null`,于是资源总览(main 态)只有当前分页栏目画出真实卡片,其余栏目只剩栏目标题栏加空的层叠占位。
- 拆成两个入参:`resources` 恢复全量资源投影语义(决定 `identityByResourceId`、缓存与卡片挂载),新增 `eagerResources` 只用于 `eagerPreviewLimit` 计数的热预取,缺省回退 `resources`。两个入参的语义写进 Hook 参数注释,调用方 `index.tsx` 在传参处注明「收窄 `resources` 会让其它栏目只剩标题栏」。
- 纠正前一次提交的说法:**identity map 必须保持全量**,不能跟着热预取一起收窄。热预取可以只喂当前栏目(上一个提交的意图保留、`A → B → 回 A` 会重读 A 可见卡的已知代价也保留),但身份 / 渲染口径收窄会让总览其它栏目不再挂载卡片——技术方案要求「主画布展示各子画布的缩略入口,内部按资源类型显示有限层叠卡片……主画布预览和子画布展开态复用同一套卡片视觉」,这是产品回归而非可接受的行为变更。
- 不使用「身份未知时用 idle 占位兜底渲染」这条备选:它会让总览卡片失去真实预览,观感更差。
- `pitfalls.md` 追加同题排障口径,含通用规则:凡 Hook 入参同时参与「身份 / 缓存 key」与「调度 / 预热」,就不能为优化调度去收窄它,应先确认它还有没有别的消费者。
- 回归用例 `appSurface > uses one full-page canvas per resource section with dependency-only guide lines` 已自行变绿(未改该断言)。验证:`npm run test -- apps/ai-game-creator-shell/tests` 1171 passed / 4 skipped / 0 failed;`appSurface.test.ts` + `useProjectResourceCardPreviews.test.ts` 415 passed;`src/components/image-editor` 1385 passed;typecheck exit 0;check:encoding 4374 文件;`git diff --check` 干净。
|
2026-09-11 01:47:04 +08:00 |
|
suzmii
|
60d8b8fbb2
|
资源卡异步解码、热预取只喂当前栏目,并删掉未接线的预览版本 map
- 资源卡 `<img>` 补 `decoding="async"`,把真机 37.2 MPx 量级的 PNG 解码移出主线程。刻意不加 `loading="lazy"`:卡片已经由资源画本的 `IntersectionObserver`(`rootMargin: 160px`,root 指向真正包含卡片的容器)精确门禁,只对进入可见区及预取边界的卡片发 IPC;再叠一层浏览器视口懒加载会让「预取边界 + 热预取 12 张」读到的视口外卡片被压住不解码,等于白预取。`decoding="async"` 不改变何时加载,与既有调度正交。
- 卡片预览的热预取改为只喂当前分页栏目:原先传的是 61 项全量投影,前 12 张可能落在别的栏目,用户看不到的卡片先占满全局 3 个物理读取槽,看得见的卡片反而要排队。
- 为此把 `projectResourcesByCategory`、`resourcePageCategoryKey`、`resourcePageCategories`、`activePageCategory`、`activePageResources` 整块上移到 `useProjectResourceCardPreviews` 之前并删除原位置定义;`projectResourcesByCategory` 只依赖 `canvasResources` 与 `categoryOrder`,其余只在既有依赖之上派生,上移不引入前向引用。`activePageResources` 合并为单一声明(原先在 `activePageLayoutReady` 之前另有一份同定义局部变量)。
- 已知副作用并接受:Hook 的 `identityByResourceId` 只覆盖当前栏目,因此「A 栏目 → B 栏目 → 回到 A」会重新读取 A 的可见卡。这正是「栏目级热预取」的定义,也符合 PRD §3.3.2「项目 / mode 切换必须清理预览缓存」的口径;代价是来回切换栏目各重读一次可见卡,单张实测 1–7 ms 且仍受全局 3 槽限制。
- 删除 `resourcePreviewVersionByResourceId` 的三处引用与 `useState` 声明。它从未被写入过:只被 `new Map()` 初始化和清空,`previewVersionByResourceId` 恒为空串,属死代码。
- 选择「删」而不是「填真值」:填真 manifest revision 会让每次 manifest 变更把全部缓存 identity 失效并触发全量重读;而现有 identity(`projectPath` / `projectId` / `id` / `category` / `path` / `mediaType`)对不可变素材已经足够——路径变了就是另一个资源。本轮不新增失效判据。
- 遗留:`appSurface > keeps the resource preview version cache wired and cleared on project switch` 断言的是刚被删除的 prop 接线,需要同步删掉该断言;该测试文件当时正被另一个 Agent 在途编辑,按分工不在本次改动内,已上报由主人处理。
|
2026-09-11 01:04:06 +08:00 |
|
suzmii
|
bdb07c141b
|
聊天输入区收回对话框内部
- 根因:`.project-supervisor-surface.is-direct-codex` 下消息列表与输入区是两只各自带边框、
各自绝对定位的盒子——列表 `bottom: 104px`、输入区 `bottom: 0; left/right: 0`,两盒上下
紧贴。输入区自己那只盒子的左右描边与列表的左右描边接成两条贯穿整栏的竖线,加上列表
只留 24px 底部内边距,最后一条消息还会被不透明的输入区盖住。观感就是"输入框紧挨着
对话框、两侧有突兀竖条"。
- styles.css:消息列表(direct-codex)改为铺满整个会话区(`bottom: 104px` → `0`),它自己
那只带描边和圆角的盒子就是"对话框";`padding-bottom` 与 `scroll-padding-bottom` 由 24px
提到 256px,给框内输入区留出位置(256 ≈ 输入区最高 230px + 12px 内缩 + 约 14px 间距)。
- styles.css:输入区(direct-codex,含同类选择器共两条绝对定位规则)由贴边改为框内内聚
区块:左右下三边各内缩 12px,圆角 14px 收到与外框同一档 12px;边框、底色、阴影继续用
既有 token(`--platform-surface-border` / `--platform-input-fill`),不新增配色。
- 不改交互语义:输入区仍是同一棵 DOM(编辑器、@ 引用、AI 润色、模型选择、提交按钮全部
原位),只改它在对话框里的落点与留白。
- project-development.suite.ts:新增守卫——对话框盒子四边归 0、输入区三边内缩同一个值、
仍是绝对定位且保留边框与底色、消息列表留白 ≥ 输入区最高高度 + 内缩且与
`scroll-padding-bottom` 同值、编辑器 min/max 高度就是留白算式的来源;并同步既有那条
钉 `bottom: 104px` 的断言为 `bottom: 0`。
- 说明:本机无无头浏览器、vitest 未开 `css: true`,像素级观感只能靠真机复核;这里钉的是
声明级几何关系与"输入区仍在同一棵渲染树里"。
|
2026-09-11 01:00:10 +08:00 |
|
suzmii
|
f7831348c6
|
搜索栏改成「临时叫出」的浮层,并撤掉为它引入的工具条带
- index.tsx:搜索框不再常驻。常态只在右下角缩放 Dock 末尾留一颗搜索按钮(复用 `game-resource-book-zoom-button`),`Ctrl/Cmd+F` 或点它把浮层叫出来;关闭走本仓既有浮层口径——直接复用美术画布的 `useImageCanvasFloatingOptionDismiss`(Esc / 点外部,Escape 在 document 上截断,不会连带触发画布清选中)。
- index.tsx:浮层打开后自动聚焦搜索框,`resourceSearchRef` 不再是死 ref(PRD「焦点落到资源搜索框」的入口接上了)。
- index.tsx:搜索语义零改动——`setSearchText`、`visibleResources` 过滤、「清除搜索并定位」原样保留;浮层收起不清条件(`type=search` 的原生 Esc 清空被 preventDefault 挡掉),条件生效时 Dock 搜索按钮带 `is-active` 高亮,用户看得出"当前有搜索"。
- index.tsx:撤掉常驻搜索条连带的那条排布带——删除 `resourceBookToolsRef`、`resourceBookToolsHeight`、`resourceCanvasSceneSize()`、ResizeObserver 测高 effect,以及缩放锚点 / 滚轮锚点里减掉工具条带的两处算式;`resourceBookSceneSize` 重新等于管理区盒子。
- index.tsx:状态提示与附件导入失败条移出排布带,落到新的浮层容器 `game-resource-book-notices`(顶部绝对定位,不占流内高度)。
- styles.css:`.game-resource-manager` 去掉 `padding-top: var(--game-resource-book-tools-height)`,`.game-resource-book-scene` 恢复 `inset: 0`,删除 `.game-resource-book-tools` 规则与窄屏 560px 那条只为常驻搜索框写的宽度规则;搜索框改为贴右下角 Dock 向上展开的浮层(`bottom: 58px` = Dock 的 14px + 34px 高 + 10px 间隙,`z-index: 40`)。
- styles.css:新增 `.game-resource-book-notices` 浮层与 `.game-resource-book-zoom-button.is-active`(复用 Dock 按钮的 hover 色,不新增配色 token)。
- tests/appSurface/harness.ts:新增 `openResourceSearch()`——按产品入口打开浮层再返回输入框,`getByLabelText` 在浮层未打开时直接抛错,堵掉"元素在但点不到"那类假绿。
- tests/appSurface/project-development.suite.ts:重写搜索可见性守卫为「浮层不落在标题栏那条带上」(管理区不再有 padding-top、场景 `inset: 0`、浮层底边 ≥ Dock 底边 + Dock 高度、浮层不带 top、提示层顶边 ≥ 标题栏高度),新增浮层开合用例(按钮 / Ctrl+F 打开、焦点落在输入框、Esc 与点外部收起都不清条件),并把既有 5 处搜索用例改为先叫出浮层。
- tests/projectResourceLiveIntegration.test.tsx:「清除搜索并定位」用例改为重新叫出浮层读条件与清空后的值。
- 验证:新增的两条断言已用变异实测不是恒真守卫——去掉自动聚焦、或让关闭顺手清条件,对应用例都会红灯。
|
2026-09-11 00:56:52 +08:00 |
|
suzmii
|
bb48345206
|
移除资源画布上的分类筛选条
- index.tsx:删掉画布侧 PlatformResourceFilterBar 的用法(「全部 / UI 交互 / 角色与对象 / 场景与环境 / 音频 / 文档 / 待归类」分类 chip 与画布标签 chip);@ 素材面板与参考图弹窗继续用同一个共享组件,共享组件本身不动。
- index.tsx:随筛选条一起摘掉只为它存在的画布筛选轴——resourceCanvasCategoryFilters / showResourceCategoryFilter / resourceCanvasActiveTags / resourceTagLibrary,并把 visibleResources 里「分类不等于 all 才过滤」「标签必须命中已选标签」两个此后恒真的条件删掉,画布可见资源只由搜索框收窄。
- index.tsx:移除只被筛选条用到的导入(PlatformResourceFilterBar、RESOURCE_REFERENCE_FILTERS、type ResourceReferenceFilter、assetTagsMatchSelection、buildGameCreationAppAssetTagLibrary)与 ResourceCanvasCategoryFilterBySortMode 类型及其默认值构造函数。
- styles.css:删除只服务画布筛选条的 .game-resource-category-filter 规则。
- 不动分区栏目:栏目大纲、栏目标题栏、categoryOrder、projectResourcesByCategory 与「资源总览」入口保持原样。
|
2026-09-11 00:38:02 +08:00 |
|
suzmii
|
2fefb373d1
|
修复 AGC 资源搜索框与栏目标题栏挤在同一水平带的重叠
- styles.css:新增 `.game-resource-book-tools` 工具条带,搜索框、筛选条与状态提示在里面按流式排布;`.game-resource-search` 去掉 `margin: 10px 12px 0`,尺寸与间距交给工具条带。
- styles.css:`.game-resource-manager` 加 `padding-top: var(--game-resource-book-tools-height, 0px)`,`.game-resource-book-scene` 的 `inset: 0` 改成同一变量让出顶边;管理区与画本场景共用一条实测高度,两者不再落在同一条排布带上(总览态标题栏与分页态栏目标题栏都整体下移)。
- styles.css:`@media (max-width: 560px)` 下搜索框独占整行,窄屏筛选条换行时工具条带变高、场景按实测高度继续下移,不靠 z-index 硬压。
- styles.css:更新工具条带注释,说明 `z-index: 40` 仍保留(高于场景的 20),作用只剩"工具条内容溢出时不被画本盖住"。
- index.tsx:把画本场景以外的流式工具条包进 `game-resource-book-tools`,搜索框挂 `resourceBookToolsRef`,用 ResizeObserver 实测 `offsetTop + offsetHeight` 写入 `--game-resource-book-tools-height`;搜索、筛选、「清除搜索并定位」与 `resourceSearchRef` 全部零改动。
- index.tsx:新增 `resourceCanvasSceneSize()`,视口居中、「适应内容」、总览入场中心、滚轮与缩放锚点都按"管理区盒子减掉工具条带"计算,不再按"场景一直顶到管理区顶端"排布;`handleResourceBookWheel` 与 `zoomResourceBookBy` 补上新增依赖。
- index.tsx:`measureResourceBookOverview()` 改用画本场景自己的 rect 作为总览栏目矩形原点,场景盒子上移后栏目矩形与世界坐标仍严格同源。
- project-development.suite.ts:在既有 CSS 声明级守卫里补断言——管理区 `padding-top` 与场景 `inset` 必须指向同一个 `--game-resource-book-tools-height` 变量,工具条带是绝对定位的独立排布行,栏目标题栏 z-index 低于工具条带;选择器加行首断言,避免误取 `.game-workbench-stage > .game-resource-manager`。
- project-development.suite.ts:在栏目大纲用例里补真实渲染的 DOM 归属断言——搜索框的父链不得命中画本场景或资源总览层,工具条带是管理区直接子节点;该断言在改动前失败、改动后通过,已用"把工具条带类名改掉"的变异验证它不是恒真假守卫。
|
2026-09-10 21:12:30 +08:00 |
|
suzmii
|
14d1635858
|
修复游戏版本时间显示 1970 年:createdAt 按 Unix 秒换算成毫秒
- 写入侧真实单位是 Unix 秒:project/manifest.rs 的 GameIterationVersion.created_at 赋值为 unix_timestamp(),该函数返回 duration.as_secs()
- formatIterationVersionLabel 原来把秒值直接交给 new Date(),被当成毫秒解释,真机因此显示 1970/1/22(1788075047 毫秒 = 1970/1/22 00:41:15,与截图逐秒吻合)
- 显示侧改为 new Date(version.createdAt * 1000),与 ResourceAssetDeleteDialog 和待确认资源编辑创建时间标签的既有秒口径一致
- 给 GameIterationVersion.createdAt 补契约注释,写明单位是 Unix 秒及换算要求,避免后续再按毫秒消费
- resourceCanvasVersionBindingModel 夹具由毫秒改为真实秒值,并用真机取值 1788075047 补断言:年月日必须是 2026/8/30 而不是 1970
- resourceVersionSwitch 夹具同步改为真实秒值,避免继续沿用误导性的毫秒夹具
|
2026-09-10 21:00:54 +08:00 |
|
suzmii
|
dde860716e
|
修复资源卡片媒体预览发不出请求:线上 category 改回原生读取分支
- 新增 projectResourceMediaPreviewCategory:从卡片预览类型派生原生侧线上 category(art / audio),与画布分区栏目轴解耦
- read_local_project_media_preview 调用点不再传 job.resource.category:该字段自 6 类资产分类轴落地后是画布栏目(unclassified / ui-interaction / …),传给原生侧会被 read_local_project_media_preview_at 以「媒体预览类别只支持 art 或 audio」直接拒绝
- 该回归导致所有走媒体分支的卡片(GIF / SVG / AVIF / BMP / MP4 / WebM / MOV 与音频)读不出预览,只显示占位图标
- previewReadErrorMessage 与 resourceReadKindLabel 改用卡片预览类型判定文案,不再依赖已换语义的 category
- 废弃 useProjectResourceCardPreviews 对 projectResourceDisplayKind 的引用,错误文案与读取路由共用同一分类口径
- 预览 Hook 用例补「扩展名图片按 art 分支发出请求」与「音频仅在播放意图后按 audio 分支发出请求」两条断言,直接钉住原生侧线上取值
- 应用层媒体用例补 icon.svg 的 category 必须是 art 的断言,并修正原先描述「媒体读取改传画布栏目」的过期注释
|
2026-09-10 20:59:22 +08:00 |
|
suzmii
|
2f8753f597
|
AGC 资源画布切换为 6 类资产分类加项目版本栏目(阶段二:投影轴与栏目表落地)
- 契约层把 ProjectResourceCanvasPosition.section 加宽为 PersistedProjectResourceCanvasSection 并删除旧 ProjectResourceCanvasSection 联合
- 七个分区类型消费点改用 ProjectResourceCanvasCategory:投影、布局模型、布局 Hook、分区高度、依赖图层与画布历史
- RESOURCE_CANVAS_SECTION_ORDER 改为 PROJECT_RESOURCE_CANVAS_SECTIONS,删除 RESOURCE_CANVAS_VISIBLE_SECTION_ORDER
- reconcileResourceCanvasLayout 接入读时归一化:旧栏目值按资源当前分区改写 section,坐标与手动标记原样保留
- 归并后的布局与原布局逐项不同,由既有协调路径判定 changed 并写回一次新分区,不引入迁移脚本
- 投影层五个资源入表点统一走 projectResourceCanvasCategory:项目版本独立成栏、Agent 回执归文档、其余按资产分类
- 新增 projectResourceDisplayKind 收敛 manifest 资产 subtype 即 kind 的显示类型口径,projectResourceTypeLabel 不再依赖旧栏目
- 卡片预览、编辑分流、媒体工具条、预览文案与画布历史快照改用显示类型与新分区,历史快照入栈时收窄到现行分区
- index.tsx 删除可见栏目导入与 visibleCategoryOrder,栏目标签由资源筛选唯一中文口径派生并补齐 7 栏
- index.tsx 补齐 7 栏默认视口与 7 个栏目图标(新增 LayoutGrid、Users、PackageOpen,移除已无用的 Code2)
- index.tsx 删除 canvasResources 的 code 过滤:只登记游戏代码的项目现在落在待归类栏目并正常显示卡片
- 新增只登记游戏代码项目的 UI 回归用例,覆盖栏目大纲、默认落地栏目与代码卡片渲染
- 测试夹具按新分区轴迁移,新增旧栏目 sidecar 读时归并用例与栏目顺序用例
- 共享测试 harness 的资源卡查询正则允许栏目标签自带空格(UI 交互)
- 同步 PRD 与共享记忆决策记录的栏目口径描述
|
2026-09-10 20:41:19 +08:00 |
|
suzmii
|
eda34187fc
|
修复 AGC 资源栏目大纲常态不可读:恢复 Dock 底板、栏目图标与完整文字
- styles.css:`.game-resource-outline` 常态从 `opacity: 0.68` + `scale(0.86)` + 透明底 + 无阴影,改回完整尺寸(`opacity: 1`、`transform: translateY(-50%)`)并给现有视觉语言的 Dock 底板(`--platform-subpanel-border` 边框 + `--platform-nav-fill` 背景 + 与右下角缩放 Dock 同款阴影 + `blur(6px)`)。
- styles.css:`.game-resource-outline button` 常态列宽由 `0 minmax(0,1fr)` 改为 `18px minmax(0,1fr)`、`gap: 6px`,栏目图标常态 width 14px / opacity 1,不再只在悬停时才出现。
- styles.css:栏目文字 11px → 12px;当前栏目(`.is-active`)常态就带 `--platform-nav-active-*` 的边框、底色和阴影,用户一眼能认出当前分区。
- styles.css:悬停/键盘聚焦保留为同一套 token 的加强(更深的边框、`--platform-nav-active-shadow`、`blur(10px)`),不再靠"常态缩到只剩文字"表达层级。
- 不改交互语义:大纲仍然是 `openResourceBookChild` 的入口,按钮 key、aria-label、aria-current 与未读小圆点全部保持原样。
- 测试:新增 CSS 声明级断言,钉住常态不降透明度、不 scale、有底板,图标常态 14px 且文字 12px(jsdom 不加载 styles.css,可见性只能钉声明)。
- 测试:新增 DOM 用例,断言每条栏目入口同时具备图标与栏目文字,且切换栏目后当前项带 `aria-current="page"`。
- 文档:同步《GameAgent资源自由画板与快速编辑》的「操作边界」与「验收」两条,把"常态缩小、只露栏目文字"改成"常态即可读可点",并同步 pitfalls 里的对应口径。
|
2026-09-10 20:11:09 +08:00 |
|
suzmii
|
9196ba3c96
|
修复 AGC 资源画本框选没有任何颜色:给画本场景根补共享画布根类
- index.tsx:`ResourceBookScene` 的场景根 `.game-resource-book-scene` 补上 `genarrative-image-canvas` 类,让共享 `SelectionOverlay` 的框选 token 有定义它的祖先。
- 根因:共享选择框的 `border: 1px solid var(--genarrative-image-canvas-selection-border)` / `background: var(--genarrative-image-canvas-selection-fill)` 没有 fallback,这两个 token 只声明在 `packages/image-canvas-react/src/styles.css` 的 `.genarrative-image-canvas` 根类里,而该根类由共享 `CanvasViewport` 渲染;AGC 只渲染 `SelectionOverlay`、从不渲染根类,声明在计算值阶段被整条丢弃,用户框选时看不到任何选择框。
- 安全性:该根类只有 3 条自定义属性声明、无布局副作用,加 className 不改布局。
- 测试:新增真实 `pointerdown` / `pointermove` 框选用例,断言选择框存在、内联几何非空,且最近的 `.genarrative-image-canvas` 祖先就是画本场景根;去掉根类后该用例失败(已实测)。
- 测试:新增 CSS 声明级断言,钉住根类的描边/底色 token 存在且非透明,消费端仍按 var() 取值(jsdom 拿不到计算色,这里只能钉声明)。
- pitfalls.md:补记"复用共享画布子组件要自己渲染根类"的排查口径。
|
2026-09-10 20:00:42 +08:00 |
|
suzmii
|
7e6f7555eb
|
修复 AGC 资源搜索框被 display: none 误关导致的搜索链路死代码
- styles.css:`.game-resource-search` 的 `display: none` 恢复成 `display: flex`(原始提交 3a8dff140 里就是 flex,PR #193 重排工作台时被改成 none),并补 `position: relative; z-index: 40`、常态 `align-self: center`、与右下角缩放 Dock 同款的浮层阴影。
- styles.css:`.game-resource-live-notice` 同样补 `position: relative; z-index: 40`;「清除搜索并定位」按钮只在这条提示条里,提示条被画本场景盖住时搜索链路依旧是断的。
- 根因(堆叠顺序):资源卡与栏目标题栏都画在 `.game-resource-book-scene`(absolute + z-index 20,子画布态还有一层不透明点阵底)里,而搜索框与提示条是 `.game-resource-book-manager` 的流式子节点;定位元素按绘制顺序整体盖在流内内容之上,所以只放开 display 等于"画了但看不见、点不到"。
- 可见性口径:AGC 的 vitest 没开 `css: true`、不加载 styles.css,`toBeVisible()` 在这里是恒真的假守卫;新增的守卫按本仓既有口径读 CSS 源文件做声明级断言(不是 display:none、z-index 高于画本场景)。
- 行为用例:总览态输入即过滤总览摞上的卡片;分页画布态同一搜索条件继续生效、清空后卡片恢复,证明这条链路确实由搜索框驱动。
- 端到端用例:搜索条件挡掉刚提交的新素材时保留条件并提示,点「清除搜索并定位」后清空搜索并定位选中新素材。
|
2026-09-10 19:59:52 +08:00 |
|
suzmii
|
6f09bf3a00
|
AGC 资源画布分区轴改为 6 类资产分类加独立项目版本栏目(阶段一:契约与读时映射)
- 契约层新增 PROJECT_RESOURCE_CANVAS_SECTIONS:6 类资产分类加末尾独立的项目版本栏目,并补齐分区类型
- 契约层新增旧四 / 五栏目取值白名单、Persisted 联合类型与两个类型守卫
- Rust 契约 ProjectResourceCanvasSection 增补 7 个现行 variant 并保留 Code / Art 旧值,新增同名分区常量
- Rust 契约不升 game-creator-resource-layout.v1 版本、不给未知 section 加兜底,损坏 payload 继续失败关闭
- 新增 resourceCanvasSectionMapping.ts:旧栏目到现行分区的映射表、回落栏目与读时归一化
- 读时归一化只改写 section,x / y / manuallyPlaced 原样保留,无法归并时沿用既有丢弃行为
- resourceProjectionModel 把扩展名分类器更名为 projectedResourceKind 并收敛为准入与显示类型
- resourceProjectionModel 新增 projectResourceCanvasCategory:项目版本独立成栏、Agent 回执归文档、其余按资产分类
- Rust resource_layout 测试夹具改用现行分区值,并新增既有旧 section sidecar 仍可读取的用例
- 新增 resourceCanvasSectionMapping 测试:旧值乘目标栏目矩阵覆盖坐标保留、section 改写、changed 判定与回落列不变量
- projectResourceProjectionModel 测试新增 7 栏分区投影、项目版本独立成栏与只登记游戏代码项目落在待归类
- 同步 PRD、GameAgent 资源自由画板技术方案、AGC 实施计划与共享记忆决策记录的分区口径
|
2026-09-10 19:57:34 +08:00 |
|
suzmii
|
cb76aadb48
|
AGC 输入区三处观感修复:AI 润色归位、@ 面板筛选条单行、引用 chip 原子化
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 3m5s
Project CI / Native shell tests (pull_request) Successful in 18m26s
- styles.css:把 direct-codex composer 下广播式的 `.project-supervisor-composer button` 规则
收窄到 `.project-supervisor-submit-button`(含 svg 那条与 media query 里那条),
并给 `.resource-reference-input-actions` 补 `grid-column: 1 / -1` 让输入区操作排独占一行
- styles.css:`.resource-reference-input-actions` / `.resource-reference-input-at` 撤掉绝对定位,
操作排回到网格文档流,与输入框右下角对齐,不再浮在文本区中间
- styles.css:`.resource-reference-input-polish` 与 `.resource-reference-input-restore` 并入
`@` 按钮同一条尺寸规则(28px 方钮、8px 圆角、同 hover/disabled 观感)
- styles.css:输入区状态行(润色中/润色失败)改为与应用排同一行,不再单独占一行顶高 22.8px
- styles.css:direct-codex 输入框 padding 收为 `12px 15px 6px`,窄屏 `10px 12px 6px`
- styles.css:`.resource-reference-chip` 的 `×` 删除按钮不再被 broadcast 规则摘出 chip
- PlatformSegmentedTabs.tsx:分段页签 item 加 `whitespace-nowrap`,中文不再逐字竖排
- packages/shared/styles.css:补 `.scrollbar-hide` 实现(该类名一直被 `layout="scroll"` 引用但仓库未定义)
- ResourceReferenceNode.tsx:`getTextContent()` 改为单个 U+FFFC 占位符并显式实现
`getTextContentSize()`,引用成为真正的原子内联节点(1 个引用 = 1 个字符)
- tests/resourceReferenceInput.test.tsx:新增「1 个引用 = 1 个字符」与「chip 自带删除、一次删净」两条断言
- tests/appSurface/project-development.suite.ts:把 composer 按钮样式断言收窄到提交按钮,
并新增一条反向断言,钉住「广播到 composer 下所有 button」的规则不再出现
|
2026-09-10 19:05:57 +08:00 |
|
suzmii
|
7d0f5c68fe
|
快速编辑浮层样式照搬美术画布:逐条搬真实规则,补回共享组件的 Tailwind 来源
- resourceCanvasChrome.css:删掉手写的近似样式,从网页端 src/index.css 逐条搬入 AGC 实际用到的 image-canvas-editor__* 规则——生成 composer 基座(grid 布局、42rem 宽、1.45rem 圆角、双阴影)、generation-prompt(--auto-grow-editor-* 变量 + 聚焦态)、composer-footer 五列网格(修掉底部一行挤在一起、对齐混乱)、option-cluster / option-popover-anchor 及其 --dimensions / --model 变体、option-popover 与它的 sections / section / title / items / items--card / items--model、option-popover-choice 及 --ratio / --model 与 aria-pressed 选中态、ratio-wireframe 六个比例线框、option-selected-check、mud-point-inline、generation-submit 三态、quick-edit-panel / --quick-edit / quick-edit-footer / quick-edit-prompt / quick-edit-submit 这一组面板专属覆盖。
- resourceCanvasChrome.css:补入提示词输入框依赖的 auto-grow-text-area 一族规则(外层尺寸变量、content 内边距、placeholder、viewport 与 OverlayScrollbars 竖条),原来只给了裸边框和 padding,所以输入框看着很素。
- resourceCanvasChrome.css:保留/新增的宿主规则只有美术画布没有的那几项——工具条与面板的层级、锚点 transform、窄屏宽度;面板的 overflow 由 auto 改成 visible,因为 AGC 的下拉弹层是就地渲染、没有 portal,滚动容器会把它裁掉。
- styles.css:新增 @source 指向 src/components/image-editor 与 src/components/common。这两个目录不在 AGC app 根下,Tailwind 自动探测扫不到,组件自带的工具类(h-3 / rounded-full / inline-flex / text-slate-700 …)在 AGC 里根本没生成,是「样式和美术画布对不齐」的另一半根因。
- 选中工具条上一轮已按真实规则改好的部分原样保留,未回退。
|
2026-09-10 18:59:33 +08:00 |
|
suzmii
|
f49ff61ddc
|
资源画布浮层接上美术画布的关闭时机:空白/Esc 清焦点,快速编辑点外部即收
- 新增 resourceCanvasFocusModel:把「这次 pointerdown 要不要清画布焦点」「快速编辑浮层是否接管点外部关闭」「宿主 Escape 是否归资源画布」抽成纯判据,逐条对齐美术画布 clearCanvasFocus() / closeTransientEditorPanels() / clearCanvasSelection()。
- index.tsx 画布空白左键 pointerdown 调 clearResourceCanvasFocus():清空选中(选中工具条随之收起)并关闭快速编辑浮层,与美术画布 useImageCanvasStageInteractions 的空白分支一致;中键/空格平移分支不清,避免平移顺手丢选中。
- 快速编辑浮层复用共享 hook useImageCanvasFloatingOptionDismiss:document 上接管 click 与 Escape,点整个资源画布以外即关,点边界内(含模型/比例下拉 .image-canvas-editor__portal-menu)保留;宿主弹窗(资源面板/生成素材/分类与标签/重命名/恢复队列)开着时不接管,避免清掉资源面板共用的多选。
- 选中工具条补 window 级 Escape:与美术画布 clearCanvasSelection() 同口径清空选中;下拉弹层打开时共享 hook 在 document 上截断,浮层优先收内层。
- 生成为中的快速编辑面板不关(status === 'generating'),保留「修改中」状态与失败重试入口。
- 新增定向测试:交互目标判据、生成中不关、宿主弹窗让位、点边界外关闭、Esc 两态关闭、只有工具条时点外部不清选中、模型/比例下拉点外部与 Esc 关闭。
|
2026-09-10 18:58:15 +08:00 |
|
suzmii
|
8f96b05f6e
|
资源画布工具条接回下载按钮:走资源面板同一条真链路并收紧放行判据
- index.tsx 把资源面板的保存流程抽成共用 saveProjectResourcesToDisk(原生保存对话框 + save_local_project_asset_file),资源面板改为委派调用,不再有第二份保存实现
- index.tsx 工具条 onDownloadLayer 由空回调改为调用该共用 handler,回执写进 resourceWorkbenchNotice;不可导出的资源仍直接返回
- resourceCanvasToolbarModel.ts 把 download 加回 supportedActions,判据是 isResourceCanvasExportable(拿不到本地文件路径就不渲染),不按媒体类型放行
- resourceCanvasToolbarModel.test.ts 期望值随判据重算为 [quick-edit, download] 或 [download],并补 download 放行与拒绝两侧用例(空 path、虚拟版本条目)
- project-development.suite.ts 音频工具条按钮集合断言更新为 [分类与标签, 重命名, 下载按钮],并新增两条真链路断言:工具条下载打到 save_local_project_asset_file、资源面板下载仍打到同一命令
- appSurface.test.ts 在入口文件 mock 原生对话框,只替代用户选择目标路径这一步;放别处注册会晚于真实模块实例化
|
2026-09-10 18:05:15 +08:00 |
|
suzmii
|
93ddb841cd
|
抽出资源落盘判据 isResourceCanvasExportable,资源面板与工具条共用一条口径
- resourceCanvasAssetTransferModel.ts 新增 isResourceCanvasExportable:只有拿得到可落盘本地文件路径的资源(version 为空且 path 非空)才允许保存
- resolveResourceCanvasPanelEntries 的 downloadable 改为调用该判据,去掉内联的同一段表达式
- 纯重构,资源面板行为不变;目的是让画布工具条的下载放行判据与资源面板使用同一个权威口径,不再写第二份
|
2026-09-10 18:05:02 +08:00 |
|
suzmii
|
7fe1fd9b64
|
Merge branch 'feat/agc-v3-canvas-generation-entry' into feat/agc-canvas-resource-workbench-v3
|
2026-09-10 17:40:49 +08:00 |
|
suzmii
|
fed17ea08c
|
工具条只渲染真实接通的动作,去掉两个点了没反应的按钮
- resolveResourceCanvasToolbarActions 不再把 redraw 与 character-animation 放进支持集合:这两个动作在宿主编排层仍是空回调,放进去会让工具条渲染出点了没反应的按钮
- 判据收敛为一条:放进来就必须真能跑通,当前真实接通的只有快速编辑(normalize → derive(editKind='image-reference'))
- 补注释写明接上真实链路后再放回来
|
2026-09-10 17:39:17 +08:00 |
|
suzmii
|
6496601d12
|
资源画布接入生成素材入口并打通无源新建链路
- index.tsx 只做导入、一个生成入口按钮、一处浮层渲染与提交回调;无 invoke 桥或项目未就绪时入口不渲染
- 提交走 derive_local_project_resource 的 create 模式:sourceResourceId 为 create:<operationId>,sourceAssetId / sourcePath / sourceSubtype / producerTaskId / sourceVersionId 全为 null
- 产出后经 pendingResourceFocusRef 定位并选中新素材卡,并写入生成资源已保存提示
- projectResourceLiveIntegration 补入口可见性、create 入参、重试复用 operationId 与新卡定位 2 条用例
- 实施计划文档补 C3 生成入口口径、图片无源生成未完成项与本轮验证数字
|
2026-09-10 16:37:52 +08:00 |
|
suzmii
|
0035d16efa
|
AGC 资源画布新增生成素材浮层与生成入口模型
- 新增 resourceCanvasGenerationModel:按本地契约锁定无源生成放行的视频 / 音效 / 背景音乐三类,提供 create:<operationId> 源标识、入口渲染门禁与请求身份工厂
- 新增 ResourceCanvasGenerationPanelView:独立浮层面板承载类型选择、草稿与失败重试,失败后锁定原请求并复用同一 operationId / idempotencyKey
- 浮层只呈现三类真实可用类型,不出现图片专属的模型 / 比例 / 尺寸 / 像素艺术与泥点价
- resourceCanvasChrome.css 补浮层宿主样式
- 新增 resourceCanvasGenerationEntry 定向测试 6 条
|
2026-09-10 16:37:39 +08:00 |
|
suzmii
|
56c6e0fecc
|
修复工具条折行与资源编辑 400 的错误盲区
- resourceCanvasChrome.css:移除我自行添加的 max-width 与 flex-wrap: wrap,按网页端美术画布原始规则改回单行不换行(容器 nowrap + 溢出横向兜底),按钮统一 2.25rem 方块与 0.45rem 圆角,文字键 padding 改回 0 0.62rem,分隔线改回 1.125rem,消除动作变多时折叠成多层与按钮高矮不齐
- resource_editor.rs:新增 editor_api_rejection_reason,在 HTTP 400 分支把服务端响应体的可读原因(兼容 error.message / error 字符串 / details.message / 顶层 message,截断 200 字符)拼进用户可见错误,替换原先只有状态码的笼统提示
|
2026-09-10 16:33:13 +08:00 |
|