diff --git a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts
index 97f9208b3..fd8563e44 100644
--- a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts
+++ b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts
@@ -2032,7 +2032,9 @@ export function registerProjectWorkbenchFoundationTests() {
expect(childVersionCard?.textContent).not.toContain('version-root');
fireEvent.click(childVersionButton);
- expect(screen.getByRole('toolbar', { name: '图片工具栏' })).not.toBeNull();
+ // 版本资源没有任何美术画布动作,选中反馈由卡片自身承担。
+ expect(childVersionButton.getAttribute('aria-pressed')).toBe('true');
+ expect(screen.queryByRole('toolbar')).toBeNull();
expect(screen.queryByText('asset:asset-removed')).toBeNull();
fireEvent.click(
within(screen.getByLabelText('资源栏目大纲')).getByRole('button', {
@@ -2057,160 +2059,21 @@ export function registerProjectWorkbenchFoundationTests() {
});
});
- it('keeps the resource toolbar and canvas interactive while a non-modal detail card is open', async () => {
- const manifest = createGameCreationAppManifest(
- 'workbench-resource-details',
- '不应显示的项目标题',
- );
- render(
- React.createElement(ProjectDevelopmentView, {
- projectName: '不应显示的项目标题',
- projectPath: '/tmp/workbench-resource-details',
- manifest,
- attachments: [],
- recentRunStatus: null,
- recentRunStopReason: null,
- agentRuntimeSummaries: [
- {
- group: 'art',
- label: '美术 Agent',
- status: 'completed',
- statusLabel: '已完成',
- currentTask: '本轮工作已完成',
- currentAction: null,
- waitingOn: null,
- completedCount: 4,
- totalCount: 4,
- },
- ],
- agentResults: [
- {
- agentId: 'design-foundation',
- runId: 'design-result-run',
- label: '玩法策划 Agent',
- title: '玩法策划 Agent 文本回执',
- content: '策划回执正文',
- updatedAt: 1,
- },
- {
- agentId: 'art-asset-plan',
- runId: 'art-result-run',
- label: '美术资源计划 Agent',
- title: '美术资源计划 Agent 文本回执',
- content:
- '# 美术计划\n\n- 仅有美术计划\n- 没有图片文件\n\n**等待实际素材生成。**',
- updatedAt: 2,
- },
- ],
- supervisor: React.createElement('div', null, '项目总控'),
- onHomeOpen: vi.fn(),
- onProjectsOpen: vi.fn(),
- }),
- );
-
- expect(screen.queryByText('不应显示的项目标题')).toBeNull();
- expect(screen.queryByRole('button', { name: '回首页' })).toBeNull();
- expect(screen.queryByRole('button', { name: '项目组' })).toBeNull();
- const resourceSortControl = screen.getByRole('group', {
- name: '资源排列方式',
- });
- expect(
- Array.from(resourceSortControl.children).map((child) => child.tagName),
- ).toEqual(['BUTTON', 'BUTTON']);
- expect(
- within(resourceSortControl)
- .getAllByRole('button')
- .map((button) => button.textContent?.trim()),
- ).toEqual(['依赖', '类型']);
- expect(screen.getByText('仅完成计划')).not.toBeNull();
- expect(
- screen.getByText('美术资源计划已完成,尚未生成或登记图片'),
- ).not.toBeNull();
- for (const label of [
- '生成视频',
- '生成音效',
- '生成背景音乐',
- '新增 UI 设计',
- ]) {
- expect(screen.queryByRole('button', { name: label })).toBeNull();
- }
-
- fireEvent.click(screen.getByRole('button', { name: '按类型' }));
- await openResourceBookCategory('设计文档');
- const searchInput = screen.getByLabelText(
- '搜索项目资源',
- ) as HTMLInputElement;
- fireEvent.change(searchInput, { target: { value: '美术资源计划' } });
- const resourceCanvas = screen.getByLabelText(
- '资源类型视图',
- ) as HTMLDivElement;
- resourceCanvas.scrollLeft = 48;
- resourceCanvas.scrollTop = 36;
-
- const artReceiptButton = screen.getByRole('button', {
- name: /选中资源:设计文档 美术资源计划 Agent 文本回执/,
- });
- const artReceiptCard = artReceiptButton.closest('.game-resource-card');
- expect(artReceiptCard).not.toBeNull();
+ it('资源卡 chrome 只保留选中按钮与当前版本边框', () => {
const styles = readFileSync(
resolve(process.cwd(), 'apps/ai-game-creator-shell/src/styles.css'),
'utf8',
);
+ // 卡片本体不再自带描边;只有被当前版本绑定的素材才有边框。
+ expect(styles).toMatch(/\.game-resource-card\s*\{[^}]*border:\s*0;/s);
+ expect(styles).toMatch(
+ /\.game-resource-card\.is-current-version\s*\{[^}]*border:\s*1px solid/s,
+ );
expect(styles).toMatch(
/\.game-resource-card-select\s*\{[^}]*cursor:\s*pointer[^}]*touch-action:\s*manipulation/s,
);
- expect(styles).toMatch(
- /\.game-resource-card-media-control\s*\{[^}]*transform:\s*scale\(var\(--genarrative-image-canvas-inverse-scale,\s*1\)\)[^}]*transform-origin:\s*bottom\s+right/s,
- );
- expect(styles).toMatch(
- /\.game-resource-canvas\s*\{[^}]*user-select:\s*none/s,
- );
- expect(styles).toMatch(/\.game-resource-card\.is-dragging/);
- expect(styles).toMatch(/\.game-resource-card\.is-current-version/);
-
- fireEvent.click(artReceiptButton);
- const workbenchStage = screen.getByLabelText('项目主视窗');
- expect(workbenchStage.getAttribute('data-resource-view-state')).toBe(
- 'resources.selected.document',
- );
- expect(screen.getByLabelText('搜索项目资源')).not.toBeNull();
- for (const label of [
- '生成视频',
- '生成音效',
- '生成背景音乐',
- '新增 UI 设计',
- ]) {
- expect(screen.queryByRole('button', { name: label })).toBeNull();
- }
- expect(screen.getByRole('button', { name: '按依赖' })).not.toBeNull();
- expect(
- screen
- .getByRole('button', { name: '按类型' })
- .getAttribute('aria-pressed'),
- ).toBe('true');
- expect(screen.getByLabelText('资源类型视图')).not.toBeNull();
- expect(screen.getByLabelText('陶泥儿 Agent 对话')).not.toBeNull();
- expect(screen.getByLabelText('子 Agent 状态栏')).not.toBeNull();
- expect(
- workbenchStage
- .querySelector('.game-workbench-toolbar')
- ?.hasAttribute('inert'),
- ).toBe(false);
- expect(screen.getByLabelText('资源类型视图').hasAttribute('inert')).toBe(
- false,
- );
- // 选中资源后浮出的是画布工具条,不再是"资源详情"面板。
- expect(screen.queryByRole('dialog')).toBeNull();
- expect(artReceiptButton.getAttribute('aria-pressed')).toBe('true');
- const receiptToolbar = screen.getByRole('toolbar', {
- name: '图片工具栏',
- });
- // 分类与标签入口已从详情面板搬到选中工具条。
- expect(
- within(receiptToolbar).getByRole('button', { name: '分类与标签' }),
- ).not.toBeNull();
+ expect(styles).not.toMatch(/\.game-resource-card-open\s*\{/);
});
-
it('loads registered documents, art media, video, and audio with safe failure states inside central focus', async () => {
const manifest = createGameCreationAppManifest(
'workbench-resource-media',
@@ -2382,9 +2245,9 @@ export function registerProjectWorkbenchFoundationTests() {
await openResourceBookCategory('设计文档');
fireEvent.click(getResourceSelectButton('blocked.md'));
- expect((await screen.findByRole('alert')).textContent).toBe(
- '当前项目策略要求先确认读取文档,确认后请关闭详情并重试',
- );
+ expect(
+ getResourceSelectButton('blocked.md').getAttribute('aria-pressed'),
+ ).toBe('true');
expect(screen.getByLabelText('陶泥儿 Agent 对话')).not.toBeNull();
expect(screen.getByLabelText('子 Agent 状态栏')).not.toBeNull();
});
@@ -3742,7 +3605,8 @@ export function registerProjectWorkbenchFoundationTests() {
);
fireEvent.click(card!);
- expect(screen.getByRole('toolbar', { name: '图片工具栏' })).not.toBeNull();
+ // 该卡片是 Agent 回执(没有正式素材身份),选中反馈由卡片自身承担。
+ expect(card?.querySelector('[aria-pressed="true"]')).not.toBeNull();
cleanup();
renderWorkbench();
@@ -3912,7 +3776,7 @@ export function registerProjectWorkbenchFoundationTests() {
fireEvent.pointerCancel(card, { pointerId: 41 });
fireEvent.click(card!);
- expect(screen.getByRole('toolbar', { name: '图片工具栏' })).not.toBeNull();
+ expect(card?.querySelector('[aria-pressed="true"]')).not.toBeNull();
});
it('maps manifest asset kinds into stable type layout ordering', async () => {
diff --git a/apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx b/apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx
index e340032f2..2031f42cb 100644
--- a/apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx
+++ b/apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx
@@ -125,7 +125,7 @@ function LiveWorkbench() {
);
}
-function DerivedWorkbench() {
+function DerivedWorkbench({ includeArt = false }: { includeArt?: boolean }) {
const initial = createGameCreationAppManifest(
'live-canvas-project',
'实时画布项目',
@@ -138,6 +138,17 @@ function DerivedWorkbench() {
localPath: 'docs/rules.md',
source: { kind: 'generated', resourceId: 'rules-resource' },
},
+ ...(includeArt
+ ? [
+ {
+ id: 'source-art',
+ kind: 'art-image',
+ mediaType: 'image/png',
+ localPath: 'assets/source-art.png',
+ source: { kind: 'generated', resourceId: 'art-resource' },
+ },
+ ]
+ : []),
];
const [manifest, setManifest] = useState(initial);
canvasFixture.manifest = manifest;
@@ -477,7 +488,7 @@ describe('project resource live canvas integration', () => {
it('derives a new art asset non-destructively from the quick edit panel and reuses the original operation identity', async () => {
const { deriveCalls } = installTauri({ failFirstDerive: true });
- render();
+ render();
fireEvent.click(screen.getByRole('button', { name: '打开美术资源' }));
fireEvent.click(await findResourceSelectButton('source-art.png'));