移除资源画布顶部手动生成入口
Project CI / Repository checks (push) Successful in 6m9s
Project CI / Frontend tests (push) Successful in 6m19s
Project CI / Backend tests (push) Successful in 7m23s
Project CI / Native shell tests (push) Successful in 15m50s

删除资源画布顶部的生成视频、生成音效、生成背景音乐和新增 UI 设计按钮
移除仅服务于这些入口的前端 handler 与入口配置
保留 Agent 语义生成、已有资源编辑、生成动画和未完成任务恢复能力
补充顶部入口消失与画布级操作保留的定向回归测试
同步更新 AGC 配置白名单、技术方案与项目决策记录
This commit is contained in:
2026-08-24 18:29:43 +08:00
parent 15a524e6e0
commit b00a3f8057
9 changed files with 59 additions and 146 deletions
@@ -127,6 +127,7 @@ const rustSharedContractSource = fs.readFileSync(
const allowedUncalledTauriCommands = [
'chat_with_game_creator_agent',
'check_ui_editor_font_glyph_coverage',
'create_ui_design_resource',
'open_game_creator_launcher_window',
'open_game_creator_workspace_window',
];
@@ -1547,13 +1548,11 @@ if (!viteConfigSource.includes('allow: [repoRoot]')) {
);
}
if (
!(
tauriConfig.build?.beforeDevCommand?.includes(
'run ai-game-creator-shell:dev-server',
) || tauriConfig.build?.beforeDevCommand?.includes('run agc:serve')
)
) {
if (!(
tauriConfig.build?.beforeDevCommand?.includes(
'run ai-game-creator-shell:dev-server',
) || tauriConfig.build?.beforeDevCommand?.includes('run agc:serve')
)) {
throw new Error(
'AI game creator shell beforeDevCommand must start the selected Vite dev server',
);
@@ -106,8 +106,6 @@ import {
type ProjectResourceEditCapability,
resolveProjectCharacterAnimationCapability,
resolveProjectResourceEditCapability,
type ResourceGenerationKind,
resourceGenerationOptions,
} from './resourceEditModel';
import {
type ResourceEditSubmitInput,
@@ -213,12 +211,6 @@ type UiEditorRoute = {
resourceLabel: string;
};
type CreateUiDesignResourceResult = {
asset: { id: string };
manifest: GameCreationAppManifest;
committedProjectRevision: number;
};
type DeriveLocalProjectResourceResult = {
operationId: string;
sourceResourceId: string;
@@ -1606,8 +1598,7 @@ export default function ProjectDevelopmentView({
}
const viewport = resolveViewport(event);
const category = viewport?.dataset.resourceSectionScroll as
| ResourceCategory
| undefined;
ResourceCategory | undefined;
if (!viewport || !category) {
return;
}
@@ -1626,8 +1617,7 @@ export default function ProjectDevelopmentView({
const event = rawEvent as WebKitGestureEvent;
const viewport = resolveViewport(event);
const category = viewport?.dataset.resourceSectionScroll as
| ResourceCategory
| undefined;
ResourceCategory | undefined;
if (!viewport || !category) {
return;
}
@@ -1739,8 +1729,7 @@ export default function ProjectDevelopmentView({
.querySelectorAll<HTMLElement>('[data-resource-section-scroll]')
.forEach((viewport) => {
const category = viewport.dataset.resourceSectionScroll as
| ResourceCategory
| undefined;
ResourceCategory | undefined;
if (!category) {
return;
}
@@ -1760,8 +1749,7 @@ export default function ProjectDevelopmentView({
?.querySelectorAll<HTMLElement>('[data-resource-section-scroll]')
.forEach((viewport) => {
const category = viewport.dataset.resourceSectionScroll as
| ResourceCategory
| undefined;
ResourceCategory | undefined;
if (!category) {
return;
}
@@ -2258,34 +2246,6 @@ export default function ProjectDevelopmentView({
],
);
const createUiDesignResource = useCallback(async () => {
const invoke = window.__TAURI__?.core?.invoke;
if (!invoke) {
setAssetCanvasNotice('UI 设计资源需要在客户端内创建');
return;
}
setAssetCanvasNotice('正在创建 UI 设计…');
try {
const result = await invoke<CreateUiDesignResourceResult>(
'create_ui_design_resource',
{ projectPath, expectedProjectId: manifest.projectId },
);
if (result.manifest.projectId !== manifest.projectId) {
throw new Error('UI 设计资源登记结果与当前项目不一致');
}
onManifestChange?.(projectPath, result.manifest, {
projectId: result.manifest.projectId,
revision: result.committedProjectRevision,
source: 'asset-command',
});
setAssetCanvasNotice('UI 设计已创建,正在同步资源与布局…');
} catch (error) {
setAssetCanvasNotice(
error instanceof Error ? error.message : String(error),
);
}
}, [manifest.projectId, onManifestChange, projectPath]);
const resetResourceCanvasViewport = useCallback(() => {
const canvasSize = resourceCanvasElementSize(resourceCanvasRef.current);
setConstrainedResourceCanvasViewport(
@@ -2635,39 +2595,6 @@ export default function ProjectDevelopmentView({
[advanceFocusGeneration],
);
const openCreateResource = useCallback(
(kind: ResourceGenerationKind) => {
canvasOpenEpochRef.current += 1;
advanceFocusGeneration();
const flowId = crypto.randomUUID();
activeFocusFlowIdRef.current = flowId;
pendingResourceFocusRef.current = null;
setAssetCanvasRoute(null);
setAssetCanvasNotice('');
const sourceMediaType = kind === 'video' ? 'video/mp4' : 'audio/mpeg';
setResourceEditorRoute({
flowId,
operationId: crypto.randomUUID(),
idempotencyKey: crypto.randomUUID(),
resource: null,
capability: {
route: 'derive',
editKind: kind,
sourceMediaType,
semanticNotice:
kind === 'background-music'
? '将生成全新的背景音乐。'
: kind === 'sound-effect'
? '将生成全新的音效。'
: '将生成全新的视频。',
generationMode: 'create',
},
generationMode: 'create',
});
},
[advanceFocusGeneration],
);
const cancelResourceEditor = useCallback(() => {
if (resourceEditorRoute) {
resourceEditorRevisionRef.current.delete(resourceEditorRoute.operationId);
@@ -3277,16 +3204,16 @@ export default function ProjectDevelopmentView({
dependencyLayoutSettled: dependencyLayout.settled,
dependencyPositioned: Boolean(
intent.resourceId &&
dependencyLayout.layout.positions.some(
(position) => position.resourceId === intent.resourceId,
),
dependencyLayout.layout.positions.some(
(position) => position.resourceId === intent.resourceId,
),
),
typeLayoutSettled: typeLayout.settled,
typePositioned: Boolean(
intent.resourceId &&
typeLayout.layout.positions.some(
(position) => position.resourceId === intent.resourceId,
),
typeLayout.layout.positions.some(
(position) => position.resourceId === intent.resourceId,
),
),
visible: targetVisible,
domRendered: Boolean(card),
@@ -3457,23 +3384,6 @@ export default function ProjectDevelopmentView({
: `管理未完成编辑 (${pendingResourceEdits.length})`}
</button>
) : null}
{resourceGenerationOptions.map((option) => (
<button
key={option.kind}
type="button"
onClick={() => openCreateResource(option.kind)}
>
<Sparkles size={15} aria-hidden="true" />
{option.label}
</button>
))}
<button
type="button"
onClick={() => void createUiDesignResource()}
>
<Sparkles size={15} aria-hidden="true" />
新增 UI 设计
</button>
<button
type="button"
className={sortMode === 'dependency' ? 'is-active' : ''}
@@ -32,20 +32,6 @@ export type ProjectResourceEditCapability =
generationMode?: 'create' | 'derive';
};
export type ResourceGenerationKind = Exclude<
LocalProjectResourceEditKind,
'image-reference' | 'svg' | 'text' | 'agent-result' | 'version'
>;
export const resourceGenerationOptions: Array<{
kind: ResourceGenerationKind;
label: string;
}> = [
{ kind: 'video', label: '生成视频' },
{ kind: 'sound-effect', label: '生成音效' },
{ kind: 'background-music', label: '生成背景音乐' },
];
const extensionMediaTypes: Record<string, string> = {
png: 'image/png',
jpg: 'image/jpeg',
@@ -1800,6 +1800,14 @@ export function registerProjectWorkbenchFoundationTests() {
expect(
screen.getByText('美术资源计划已完成,尚未生成或登记图片'),
).not.toBeNull();
for (const label of [
'生成视频',
'生成音效',
'生成背景音乐',
'新增 UI 设计',
]) {
expect(screen.queryByRole('button', { name: label })).toBeNull();
}
fireEvent.click(screen.getByRole('button', { name: '按类型' }));
const searchInput = screen.getByLabelText(
@@ -1855,7 +1863,14 @@ export function registerProjectWorkbenchFoundationTests() {
'resources.focused.document',
);
expect(screen.getByLabelText('搜索项目资源')).not.toBeNull();
expect(screen.getByRole('button', { name: '生成视频' })).not.toBeNull();
for (const label of [
'生成视频',
'生成音效',
'生成背景音乐',
'新增 UI 设计',
]) {
expect(screen.queryByRole('button', { name: label })).toBeNull();
}
expect(screen.getByRole('button', { name: '按依赖' })).not.toBeNull();
expect(
screen
@@ -9409,8 +9424,7 @@ export function registerProjectSupervisorSurfaceTests() {
projectPath,
});
let directTurnUpdateHandler:
| ((event: { payload: Record<string, unknown> }) => void)
| null = null;
((event: { payload: Record<string, unknown> }) => void) | null = null;
const listen = vi.fn(
async (
eventName: string,
@@ -10368,8 +10382,7 @@ export function registerProjectAgentStatusTests() {
},
);
let runtimeUpdateHandler:
| ((event: { payload: Record<string, unknown> }) => void)
| null = null;
((event: { payload: Record<string, unknown> }) => void) | null = null;
const listen = vi.fn(
async (
eventName: string,
@@ -700,15 +700,14 @@ describe('project resource live canvas integration', () => {
const sourceCard = await screen.findByRole('button', {
name: /source-art\.png/,
});
expect(
(
screen.getByRole('button', {
name: '生成视频',
}) as HTMLButtonElement
).disabled,
).toBe(false);
expect(screen.getByRole('button', { name: '生成音效' })).not.toBeNull();
expect(screen.getByRole('button', { name: '生成背景音乐' })).not.toBeNull();
for (const label of [
'生成视频',
'生成音效',
'生成背景音乐',
'新增 UI 设计',
]) {
expect(screen.queryByRole('button', { name: label })).toBeNull();
}
fireEvent.click(sourceCard);
const sourceDetails = screen.getByRole('dialog', {
name: /source-art\.png/,
@@ -718,7 +717,14 @@ describe('project resource live canvas integration', () => {
);
expect(sourceDetails.hasAttribute('aria-modal')).toBe(false);
expect(document.querySelector('.game-resource-focus-backdrop')).toBeNull();
expect(screen.getByRole('button', { name: '生成视频' })).not.toBeNull();
for (const label of [
'生成视频',
'生成音效',
'生成背景音乐',
'新增 UI 设计',
]) {
expect(screen.queryByRole('button', { name: label })).toBeNull();
}
expect(
screen
.getByRole('button', { name: '按依赖' })
@@ -5,7 +5,6 @@ import {
resolveProjectCharacterAnimationCapability,
resolveProjectResourceEditCapability,
resourceEditPromptMaxLength,
resourceGenerationOptions,
} from '../src/view/project-development/resourceEditModel';
import type { ProjectResource } from '../src/view/project-development/resourceProjectionModel';
@@ -241,12 +240,4 @@ describe('全类型资源编辑能力模型', () => {
),
).toBeNull();
});
it('资源页提供新建视频、音效和背景音乐入口', () => {
expect(resourceGenerationOptions).toEqual([
{ kind: 'video', label: '生成视频' },
{ kind: 'sound-effect', label: '生成音效' },
{ kind: 'background-music', label: '生成背景音乐' },
]);
});
});