修复 DirectProject 历史分页失败状态并清理漂移绑定
首页历史读取失败时保留显示更早按钮状态并补充回归用例 恢复 DirectCodexUser 生成绑定契约,移除多余 envelope 与无引用 UI 绑定 同步 ts-rs 生成物排障共享记忆
This commit is contained in:
@@ -5,8 +5,8 @@ mod validation;
|
||||
mod wire;
|
||||
|
||||
pub(crate) use model::{
|
||||
DirectCodexUserContentPart, DirectCodexUserItem, DirectCodexUserMessageEnvelope,
|
||||
DirectCodexUserMessageItem, DirectCodexUserRole, DirectCodexUserRuntimeRegionPart,
|
||||
DirectCodexUserContentPart, DirectCodexUserItem, DirectCodexUserMessageItem,
|
||||
DirectCodexUserRole, DirectCodexUserRuntimeRegionPart,
|
||||
};
|
||||
pub(crate) use validation::validate_direct_codex_user_item;
|
||||
pub(crate) use wire::{
|
||||
|
||||
@@ -61,13 +61,6 @@ pub(crate) struct DirectCodexUserRuntimeRegionPart {
|
||||
pub(crate) resource_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/features/project-workspace/generated/"))]
|
||||
pub(crate) struct DirectCodexUserMessageEnvelope {
|
||||
pub(crate) item: DirectCodexUserItem,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -11902,7 +11902,11 @@ export function App({
|
||||
setDirectThreadChat((state) =>
|
||||
mergeDirectHistoryItems(state, pages.items),
|
||||
);
|
||||
setDirectHistoryHasMore(pages.hasMore);
|
||||
// 首页或中途读取失败时保留旧值:失败页没有可靠的 hasMore,不能因为
|
||||
// 一次瞬时 IO 抖动把「显示更早」按钮永久收掉。
|
||||
if (!pages.error) {
|
||||
setDirectHistoryHasMore(pages.hasMore);
|
||||
}
|
||||
directHistoryOldestItemIdRef.current =
|
||||
pages.firstItemId ?? directHistoryOldestItemIdRef.current;
|
||||
if (pages.error) {
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
import type { DirectCodexUserRuntimeRegionPart } from './DirectCodexUserRuntimeRegionPart';
|
||||
|
||||
export type DirectCodexUserContentPart =
|
||||
|
||||
+3
-7
@@ -1,9 +1,5 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
import type { DirectCodexUserMessageItem } from './DirectCodexUserMessageItem';
|
||||
|
||||
/**
|
||||
* DirectProject 本轮 user input 的唯一结构化入口。
|
||||
*/
|
||||
export type DirectCodexUserItem = {
|
||||
type: 'message';
|
||||
} & DirectCodexUserMessageItem;
|
||||
export type DirectCodexUserItem = DirectCodexUserMessageItem;
|
||||
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectCodexUserItem } from './DirectCodexUserItem';
|
||||
|
||||
export type DirectCodexUserMessageEnvelope = { item: DirectCodexUserItem };
|
||||
+4
-2
@@ -1,9 +1,11 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
import type { DirectCodexUserContentPart } from './DirectCodexUserContentPart';
|
||||
import type { DirectCodexUserRole } from './DirectCodexUserRole';
|
||||
|
||||
export type DirectCodexUserMessageItem = {
|
||||
type: 'message';
|
||||
role: DirectCodexUserRole;
|
||||
content: Array<DirectCodexUserContentPart>;
|
||||
content: DirectCodexUserContentPart[];
|
||||
id: string;
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type DirectCodexUserRole = 'user';
|
||||
|
||||
+9
-9
@@ -1,13 +1,13 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type DirectCodexUserRuntimeRegionPart = {
|
||||
label: string;
|
||||
runId: string | null;
|
||||
versionId: string | null;
|
||||
elementTag: string | null;
|
||||
elementRole: string | null;
|
||||
text: string | null;
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
resourceIds: Array<string>;
|
||||
runId?: string;
|
||||
versionId?: string;
|
||||
elementTag?: string;
|
||||
elementRole?: string;
|
||||
text?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
resourceIds: string[];
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import type {
|
||||
DirectCodexUserContentPart,
|
||||
DirectCodexUserItem,
|
||||
DirectCodexUserMessageItem,
|
||||
} from './generated';
|
||||
|
||||
export type ResourceReferenceSource =
|
||||
@@ -99,17 +100,16 @@ export function chatReferenceToContentPart(
|
||||
if (reference.type === 'resource') {
|
||||
return { type: 'agc_resource_reference', resourceId: reference.resourceId };
|
||||
}
|
||||
// 生成绑定里可选字段是 `T | null`(Rust `Option<T>` 会显式序列化成 null,不是省略键)。
|
||||
return {
|
||||
type: 'agc_runtime_region_reference',
|
||||
label: reference.label,
|
||||
runId: reference.runId ?? null,
|
||||
versionId: reference.versionId ?? null,
|
||||
elementTag: reference.elementTag ?? null,
|
||||
elementRole: reference.elementRole ?? null,
|
||||
text: reference.text ?? null,
|
||||
width: reference.width ?? null,
|
||||
height: reference.height ?? null,
|
||||
runId: reference.runId,
|
||||
versionId: reference.versionId,
|
||||
elementTag: reference.elementTag,
|
||||
elementRole: reference.elementRole,
|
||||
text: reference.text,
|
||||
width: reference.width,
|
||||
height: reference.height,
|
||||
resourceIds: reference.resourceIds,
|
||||
};
|
||||
}
|
||||
@@ -135,7 +135,7 @@ export function chatComposerDraftToDirectCodexUserItem(
|
||||
role: 'user',
|
||||
content,
|
||||
id,
|
||||
} satisfies DirectCodexUserItem;
|
||||
} satisfies DirectCodexUserMessageItem;
|
||||
}
|
||||
|
||||
export function resourceDisplayName(asset: GameCreationAppAssetManifestEntry) {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { NodeComponent } from "./NodeComponent";
|
||||
import type { NodeId } from "./NodeId";
|
||||
import type { StageStatus } from "./StageStatus";
|
||||
|
||||
export type BindingChange = { node_id: NodeId, component: NodeComponent, component_status: StageStatus, };
|
||||
@@ -1,4 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { BindingChange } from "./BindingChange";
|
||||
|
||||
export type BindingDTO = { changes: Array<BindingChange>, };
|
||||
@@ -231,4 +231,23 @@ describe('DirectProject 历史分页连拉', () => {
|
||||
expect(result.hasMore).toBe(true);
|
||||
expect(result.error).toBe(failure);
|
||||
});
|
||||
|
||||
it('首页读取失败时保留首屏锚点并返回错误', async () => {
|
||||
const failure = new Error('首页读取失败');
|
||||
const readSlice = vi.fn(async (_beforeItemId: string | null) => {
|
||||
throw failure;
|
||||
});
|
||||
|
||||
const result = await readDirectHistoryPages({
|
||||
existingEntries: [],
|
||||
beforeItemId: null,
|
||||
readSlice,
|
||||
});
|
||||
|
||||
expect(readSlice).toHaveBeenCalledTimes(1);
|
||||
expect(result.items).toEqual([]);
|
||||
expect(result.firstItemId).toBeNull();
|
||||
expect(result.hasMore).toBe(false);
|
||||
expect(result.error).toBe(failure);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5691,8 +5691,8 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
|
||||
- **现象**:在 `apps/ai-game-creator-shell/src-tauri` 跑过 `cargo test` 之后,`npm run ai-game-creator-shell:typecheck` 报 10 条类型错(`src/features/project-workspace/resourceReferences.ts:106-112` 的 `string | undefined` 不能赋给 `string | null`;同文件 134 行的对象字面量带 `type: 'message'`,而 `DirectCodexUserMessageItem` 里没有该字段),`npm run ai-game-creator-shell:build` 也死在 `beforeBuildCommand` 的同一条 typecheck 上。
|
||||
- **原因**:crate 里的 ts-rs 导出会按**本机依赖版本**重写 `src/features/project-workspace/generated/DirectCodexUser*.ts`:注释头变成 "This file was generated…"、字符串改双引号、`DirectCodexUserMessageItem` 丢掉 `type: 'message'` 判别字段、并多出一个 `DirectCodexUserMessageEnvelope.ts`。仓库里提交的那份是前端真正依赖的形状(前端按带 `type` 的判别联合写),重写后两边就对不上——错在生成器版本漂移,不在前端。
|
||||
- **处理(现行口径)**:不要把重写结果当改动提交。跑过 `cargo test` 或构建后先 `git checkout -- apps/ai-game-creator-shell/src/features/project-workspace/generated`,再删掉多出来的 `DirectCodexUserMessageEnvelope.ts`,然后才做 typecheck / 打包;绑定与前端形状冲突时以**已提交的绑定 + 前端**为基准排查。
|
||||
- **验证**:恢复提交版本后 `npm run ai-game-creator-shell:typecheck` exit 0(`[skill-pack] OK`);保留重写结果时同一条命令 exit 2。release 构建本身还会在 `src/features/ui-editor/types/` 落下 `BindingChange.ts` / `BindingDTO.ts` 两个无人引用的未跟踪文件,属同类生成产物。
|
||||
- **处理(现行口径)**:不要把重写结果当改动提交。跑过 `cargo test` 或构建后只恢复 `apps/ai-game-creator-shell/src/features/project-workspace/generated/DirectCodexUser*.ts` 的仓库版本,再删掉多出来的 `DirectCodexUserMessageEnvelope.ts`;不要恢复整个 `generated/` 目录,以免误删 DirectThread 的现役绑定。绑定与前端形状冲突时以**已提交的 DirectCodexUser 绑定 + 前端**为基准排查。
|
||||
- **验证**:恢复仓库版本后 `npm run ai-game-creator-shell:typecheck` exit 0(`[skill-pack] OK`);保留重写结果时同一条命令 exit 2。release 构建本身还会在 `src/features/ui-editor/types/` 落下 `BindingChange.ts` / `BindingDTO.ts` 两个无人引用的生成产物;它们不属于前端契约,发现后直接删除,不提交。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/`(ts-rs 导出源)、`apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts`、`apps/ai-game-creator-shell/scripts/build-release.mjs`(`beforeBuildCommand`)。
|
||||
|
||||
## 2026-09-16 Node 26 下 vitest 的 jsdom 用例拿不到 window.localStorage
|
||||
|
||||
Reference in New Issue
Block a user