2f8753f597
- 契约层把 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 与共享记忆决策记录的栏目口径描述
216 lines
6.3 KiB
TypeScript
216 lines
6.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
|
||
import { createGameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
|
||
import { resolveResourceCanvasToolbarActions } from '../src/features/resource-canvas/resourceCanvasToolbarModel';
|
||
import type {
|
||
ProjectResource,
|
||
ProjectVersionResourceSummary,
|
||
} from '../src/view/project-development/resourceProjectionModel';
|
||
|
||
function createResource(
|
||
overrides: Partial<ProjectResource> = {},
|
||
): ProjectResource {
|
||
return {
|
||
id: 'asset:hero',
|
||
category: 'ui-interaction',
|
||
subtype: 'icon',
|
||
label: '主角立绘',
|
||
path: 'assets/hero.png',
|
||
mediaType: 'image/png',
|
||
sourceLabel: '生成',
|
||
taskTitle: null,
|
||
manifestAssetId: 'hero',
|
||
producerTaskId: null,
|
||
externalResourceId: null,
|
||
referenceResourceIds: [],
|
||
dependencies: [],
|
||
dependencyDepth: 0,
|
||
...overrides,
|
||
};
|
||
}
|
||
|
||
/** 虚拟版本条目:只有版本摘要,没有对应文件。 */
|
||
function createVersionSummary(): ProjectVersionResourceSummary {
|
||
return {
|
||
versionId: 'version-1',
|
||
parentVersionId: null,
|
||
projectRevision: 3,
|
||
resourceBindings: [],
|
||
createdReason: 'initial',
|
||
createdAt: 1,
|
||
label: '版本 1',
|
||
childVersionIds: [],
|
||
};
|
||
}
|
||
|
||
function createManifest() {
|
||
return createGameCreationAppManifest(
|
||
'project-toolbar-actions',
|
||
'工具条动作测试',
|
||
);
|
||
}
|
||
|
||
/** 把种子任务的第一个任务改成已完成并登记指定产物,用于覆盖任务产物正规化门禁。 */
|
||
function createManifestWithProducingTask(
|
||
status: 'completed' | 'running',
|
||
artifactPath: string,
|
||
) {
|
||
const manifest = createManifest();
|
||
manifest.tasks = manifest.tasks.map((task, index) =>
|
||
index === 0 ? { ...task, status, artifacts: [artifactPath] } : task,
|
||
);
|
||
return manifest;
|
||
}
|
||
|
||
function toolbarActions(
|
||
manifest: ReturnType<typeof createManifest>,
|
||
resource: ProjectResource,
|
||
) {
|
||
return [...resolveResourceCanvasToolbarActions(manifest, resource)];
|
||
}
|
||
|
||
/**
|
||
* AGC 资源画布工具条的放行判据只有一条:**进集合的动作必须真能跑通**。
|
||
*
|
||
* `ImageCanvasSelectedLayerToolbarView` 收到 `supportedActions` 后完全以集合为准,
|
||
* 宿主编排层里还是空回调的动作一旦进集合就会渲染成"点了没反应"的按钮,因此这里
|
||
* 逐条钉住当前真正接通的动作集合(见
|
||
* `docs/project-memory/shared-memory/pitfalls.md` 的「资源画布工具条的『改造』
|
||
* 『角色动画』按钮点了没反应」)。
|
||
*
|
||
* 当前真实接通的是「快速编辑」(normalize → derive(editKind='image-reference'))
|
||
* 与「下载」(资源面板同一条原生保存对话框 + `save_local_project_asset_file` 链路)。
|
||
*/
|
||
describe('resource canvas toolbar actions', () => {
|
||
it('已登记 manifest 资产的栅格图片放行快速编辑与下载', () => {
|
||
expect(toolbarActions(createManifest(), createResource())).toEqual([
|
||
'quick-edit',
|
||
'download',
|
||
]);
|
||
});
|
||
|
||
it('音频与视频资源不再放行空回调的改造动作,但仍放行下载', () => {
|
||
const manifest = createManifest();
|
||
expect(
|
||
toolbarActions(
|
||
manifest,
|
||
createResource({
|
||
id: 'asset:bgm',
|
||
category: 'audio',
|
||
subtype: 'background-music',
|
||
label: '背景音乐',
|
||
path: 'assets/bgm.mp3',
|
||
mediaType: 'audio/mpeg',
|
||
manifestAssetId: 'bgm',
|
||
}),
|
||
),
|
||
).toEqual(['download']);
|
||
expect(
|
||
toolbarActions(
|
||
manifest,
|
||
createResource({
|
||
id: 'asset:intro',
|
||
subtype: 'video',
|
||
label: '开场动画',
|
||
path: 'assets/intro.mp4',
|
||
mediaType: 'video/mp4',
|
||
manifestAssetId: 'intro',
|
||
}),
|
||
),
|
||
).toEqual(['download']);
|
||
});
|
||
|
||
it('角色资源只放行快速编辑与下载,不再放行空回调的角色动画动作', () => {
|
||
expect(
|
||
toolbarActions(
|
||
createManifest(),
|
||
createResource({ id: 'asset:hero-action', subtype: 'character' }),
|
||
),
|
||
).toEqual(['quick-edit', 'download']);
|
||
});
|
||
|
||
it('未登记 manifest 资产且没有已完成任务产物背书的资源只放行下载', () => {
|
||
expect(
|
||
toolbarActions(
|
||
createManifest(),
|
||
createResource({ manifestAssetId: null, producerTaskId: 'task-art' }),
|
||
),
|
||
).toEqual(['download']);
|
||
});
|
||
|
||
it('只认已完成任务的栅格产物,未完成任务的产物不放行快速编辑但可下载', () => {
|
||
const artifactPath = 'assets/hero.png';
|
||
const completedManifest = createManifestWithProducingTask(
|
||
'completed',
|
||
artifactPath,
|
||
);
|
||
expect(
|
||
toolbarActions(
|
||
completedManifest,
|
||
createResource({
|
||
manifestAssetId: null,
|
||
producerTaskId: completedManifest.tasks[0].id,
|
||
}),
|
||
),
|
||
).toEqual(['quick-edit', 'download']);
|
||
|
||
const runningManifest = createManifestWithProducingTask(
|
||
'running',
|
||
artifactPath,
|
||
);
|
||
expect(
|
||
toolbarActions(
|
||
runningManifest,
|
||
createResource({
|
||
manifestAssetId: null,
|
||
producerTaskId: runningManifest.tasks[0].id,
|
||
}),
|
||
),
|
||
).toEqual(['download']);
|
||
});
|
||
|
||
it('非栅格图片不进快速编辑的派生门禁,但仍可下载', () => {
|
||
expect(
|
||
toolbarActions(
|
||
createManifest(),
|
||
createResource({
|
||
id: 'asset:icon',
|
||
path: 'assets/icon.svg',
|
||
mediaType: 'image/svg+xml',
|
||
}),
|
||
),
|
||
).toEqual(['download']);
|
||
});
|
||
|
||
it('拿得到本地文件路径的资源才放行下载', () => {
|
||
expect(toolbarActions(createManifest(), createResource())).toContain(
|
||
'download',
|
||
);
|
||
});
|
||
|
||
it('拿不到本地文件路径的资源不放行下载,避免渲染出点了报错的按钮', () => {
|
||
const manifest = createManifest();
|
||
expect(
|
||
toolbarActions(
|
||
manifest,
|
||
createResource({ path: '', manifestAssetId: 'virtual' }),
|
||
),
|
||
).not.toContain('download');
|
||
expect(
|
||
toolbarActions(
|
||
manifest,
|
||
createResource({
|
||
id: 'version:1',
|
||
category: 'version',
|
||
subtype: 'version',
|
||
label: '版本 1',
|
||
path: '',
|
||
mediaType: 'application/vnd.genarrative.project-version+json',
|
||
manifestAssetId: null,
|
||
version: createVersionSummary(),
|
||
}),
|
||
),
|
||
).not.toContain('download');
|
||
});
|
||
});
|