移除首页与工作台素材生成入口
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 4m58s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 4m35s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m31s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 4m10s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 4m29s
Project CI / AI game creator shell Rust crates (push) Successful in 2m9s
Project CI / Frontend tests (push) Successful in 4m5s
Project CI / Repository checks (push) Successful in 3m20s
Project CI / Native shell tests (push) Successful in 6m24s
Project CI / Backend tests (push) Successful in 7m40s
Project CI / AI game creator shell web tests (push) Successful in 3m18s

首页只保留做游戏和做方案入口

移除项目工作台顶部生成素材按钮

同步相关测试与产品文档
This commit is contained in:
Git Hooks Test
2026-09-16 02:38:32 +08:00
parent bca198a17e
commit a4a030dfb5
7 changed files with 23 additions and 98 deletions
@@ -8,9 +8,9 @@ import { subscribeTauriEvent } from '../services/tauriEventSubscription';
import { AppUpdateNotice } from './AppUpdateNotice';
import {
WINDOW_CHROME_DEFAULT_TITLE,
type WindowChromeActiveProjectRuns,
WindowChromeContext,
type WindowChromeContextValue,
type WindowChromeActiveProjectRuns,
} from './windowChromeContext';
type WindowChromeProps = {
@@ -4,7 +4,6 @@ import {
FolderKanban,
FolderOpen,
Gamepad2,
Image,
Loader2,
type LucideIcon,
Sparkles,
@@ -50,13 +49,6 @@ const HOME_CREATION_TYPE_ITEMS: readonly HomeCreationTypeItem[] = [
emptyPrompt: '请输入游戏灵感或上传参考素材',
icon: Gamepad2,
},
{
creationType: 'art',
label: '做素材',
placeholder: '今天想做什么样的美术素材',
emptyPrompt: '请输入素材需求或上传参考图',
icon: Image,
},
{
creationType: 'doc',
label: '做方案',
@@ -66,7 +58,7 @@ const HOME_CREATION_TYPE_ITEMS: readonly HomeCreationTypeItem[] = [
},
];
// 首页只做游戏与做方案之间分叉:做素材已被 master 并进直连构建,与做游戏同链,
// 首页只保留做游戏与做方案两个入口;素材生成在项目内按实际工作流触发。
export type HomeProjectRow = {
path: string;
name: string;
@@ -149,13 +141,15 @@ export default function HomeView({
const [planningCompletionEnabled, setPlanningCompletionEnabled] =
useState(false);
const homeCreationBusyRef = useRef(false);
const effectiveCreationType =
homeCreationType === 'art' ? 'game' : homeCreationType;
const activeCreationType =
HOME_CREATION_TYPE_ITEMS.find(
(item) => item.creationType === homeCreationType,
(item) => item.creationType === effectiveCreationType,
) ?? HOME_CREATION_TYPE_ITEMS[0]!;
// 做方案始终走立项策划链路;做游戏勾选“策划补全”时复用该链路,做素材保持直接开建
// 做方案始终走立项策划链路;做游戏勾选“策划补全”时复用该链路。
const startMode = resolveHomeStartMode(
homeCreationType,
effectiveCreationType,
planningCompletionEnabled,
);
@@ -166,7 +160,7 @@ export default function HomeView({
const referencedAttachments = richTextToAttachments(homeRichText);
const prompt = richTextToPrompt(homeRichText);
if (!prompt && referencedAttachments.length === 0) {
if (homeCreationType !== 'doc') {
if (effectiveCreationType !== 'doc') {
onStatusChange(activeCreationType.emptyPrompt);
}
return;
@@ -177,7 +171,7 @@ export default function HomeView({
onStatusChange(
await onCreateDraftAutomatically(
{
creationType: homeCreationType,
creationType: effectiveCreationType,
prompt,
attachments: referencedAttachments,
},
@@ -274,7 +268,7 @@ export default function HomeView({
<div className="grid grid-cols-[1fr_auto] items-center gap-2.5 text-[12px] text-(--platform-text-soft)">
<div className="flex min-w-0 items-center gap-3">
<UploadButton />
{homeCreationType === 'game' ? (
{effectiveCreationType === 'game' ? (
<label className="inline-flex cursor-pointer items-center gap-1.5 whitespace-nowrap">
<input
type="checkbox"
@@ -156,7 +156,6 @@ import {
resolveResourceCanvasFocusEscapeActive,
} from '../../features/resource-canvas/resourceCanvasFocusModel';
import {
isResourceCanvasGenerationAvailable,
type ResourceCanvasGenerationKind,
resourceCanvasGenerationOption,
resourceCanvasGenerationSourceId,
@@ -7441,12 +7440,6 @@ export default function ProjectDevelopmentView({
RESOURCE_CHARACTER_ANIMATION_RESOLUTION,
RESOURCE_CHARACTER_ANIMATION_DURATION_SECONDS,
);
// 没有客户端 invoke 桥或项目还没就绪时不渲染生成入口,避免留下点了没反应的按钮。
const resourceGenerationAvailable = isResourceCanvasGenerationAvailable({
hasRuntimeInvoke: Boolean(window.__TAURI__?.core?.invoke),
projectPath,
projectId: manifest.projectId,
});
/**
* `child`
*
@@ -7680,27 +7673,9 @@ export default function ProjectDevelopmentView({
<FolderTree size={15} aria-hidden="true" />
</button>
{resourceGenerationAvailable ? (
<button
type="button"
className="game-workbench-resource-panel-button"
aria-haspopup="dialog"
aria-expanded={resourceGenerationOpen}
onClick={() =>
setResourceGenerationDraft({
initialKind: 'video',
kinds: ['video'],
})
}
>
<Sparkles size={15} aria-hidden="true" />
</button>
) : null}
{/*
{/*
`game-workbench-view-actions`
+ secondary pill
CSS****
@@ -1498,14 +1498,11 @@ export function registerHomeProjectCreationTests() {
const gameType = within(creationTypes).getByRole('button', {
name: '做游戏',
});
const artType = within(creationTypes).getByRole('button', {
name: '做素材',
});
const documentType = within(creationTypes).getByRole('button', {
name: '做方案',
});
expect(gameType.getAttribute('aria-pressed')).toBe('true');
expect(artType.getAttribute('aria-pressed')).toBe('false');
expect(within(creationTypes).queryByRole('button', { name: '做素材' })).toBeNull();
expect(documentType.getAttribute('aria-pressed')).toBe('false');
expect(screen.getByText('你的游戏创作管家')).not.toBeNull();
expect(screen.getAllByText('今天想把什么灵感做成游戏')).toHaveLength(1);
@@ -1519,11 +1516,10 @@ export function registerHomeProjectCreationTests() {
expect(invoke).not.toHaveBeenCalledWith(
'create_automatic_local_game_project',
);
fireEvent.click(artType);
expect(gameType.getAttribute('aria-pressed')).toBe('false');
expect(artType.getAttribute('aria-pressed')).toBe('true');
fireEvent.click(gameType);
expect(gameType.getAttribute('aria-pressed')).toBe('true');
expect(screen.getByText('你的游戏创作管家')).not.toBeNull();
expect(screen.getAllByText('今天想什么样的美术素材')).toHaveLength(1);
expect(screen.getAllByText('今天想什么灵感做成游戏')).toHaveLength(1);
const promptInput = screen.getByLabelText('创作想法');
nativeClipboardMock.text = '你好,今天多少号';
@@ -1562,7 +1558,7 @@ export function registerHomeProjectCreationTests() {
expect(invoke).toHaveBeenCalledWith('chat_with_game_creator_direct_codex', {
projectPath: automaticProjectPath,
prompt: '你好,今天多少号',
creationType: 'art',
creationType: 'game',
clientTurnId: expect.any(String),
userItem: {
id: expect.stringMatching(/^direct-codex:[A-Za-z0-9-]+:user$/),
@@ -1109,49 +1109,11 @@ describe('project resource live canvas integration', () => {
expect(readViewport()).not.toBe(viewportBefore);
});
it('creates a brand new media asset from the canvas generation entry with a create-mode derive request', async () => {
const { deriveCalls } = installTauri({ failFirstDerive: true });
it('does not expose a top-level canvas generation entry', async () => {
installTauri();
render(<DerivedWorkbench />);
fireEvent.click(await screen.findByRole('button', { name: '生成素材' }));
// 「生成素材」入口只保留视频:音频入口已由栏目画布底部工具栏承载。
const panel = await screen.findByRole('dialog', { name: '生成视频' });
fireEvent.change(within(panel).getByLabelText('生成提示词'), {
target: { value: '一段片头动画,镜头缓慢推进' },
});
fireEvent.click(within(panel).getByRole('button', { name: '生成视频' }));
expect(await within(panel).findByRole('alert')).not.toBeNull();
fireEvent.click(
within(panel).getByRole('button', { name: '使用原请求重试' }),
);
await waitFor(() => expect(deriveCalls).toHaveLength(2));
const operationId = String(deriveCalls[0]?.operationId);
expect(deriveCalls[0]).toMatchObject({
projectPath,
expectedProjectId: 'live-canvas-project',
editKind: 'video',
generationMode: 'create',
sourceResourceId: `create:${operationId}`,
sourceAssetId: null,
sourcePath: null,
sourceMediaType: 'video/mp4',
sourceSubtype: null,
producerTaskId: null,
sourceVersionId: null,
prompt: '一段片头动画,镜头缓慢推进',
assetName: '新视频',
});
expect(deriveCalls[0]).not.toHaveProperty('accessToken');
expect(deriveCalls[1]?.operationId).toBe(operationId);
expect(deriveCalls[1]?.idempotencyKey).toBe(deriveCalls[0]?.idempotencyKey);
expect(screen.queryByRole('dialog', { name: '生成视频' })).toBeNull();
// 产出物是新素材:画布定位并选中新卡片。
expect(
(await findResourceSelectButton(`${operationId}-rules.md`)).getAttribute(
'aria-pressed',
),
).toBe('true');
expect(screen.queryByRole('button', { name: '生成素材' })).toBeNull();
});
it('keeps the search condition, offers an explicit clear-and-locate, then locates the new asset', async () => {
@@ -790,8 +790,7 @@ describe('资源画布手动重排口径', () => {
const actionsRow = rederiveButton.closest('.game-workbench-view-actions');
expect(actionsRow).not.toBeNull();
// 位置:不再是这一行的最后一个按钮(行尾会被读成"针对整个工具条"的动作),
// 紧跟「生成素材」,并且在排序组左侧。
// 位置:整理画布仍在排序组左侧,不会被读成排列方式的一部分。
const rowButtons = Array.from(actionsRow!.querySelectorAll('button'));
expect(rowButtons.at(-1)).not.toBe(rederiveButton);
const rederiveIndex = rowButtons.indexOf(rederiveButton);
@@ -801,9 +800,6 @@ describe('资源画布手动重排口径', () => {
expect(rederiveIndex).toBeGreaterThanOrEqual(0);
expect(sortGroupIndex).toBeGreaterThanOrEqual(0);
expect(rederiveIndex).toBeLessThan(sortGroupIndex);
expect(rederiveButton.previousElementSibling).toBe(
screen.getByRole('button', { name: '生成素材' }),
);
// 语义没变:可点性只跟布局就绪绑定,布局读完后它就是可点的。
await waitFor(() =>