补充宣传截图聊天入口
新增 /screenshots 宣传截图清单摘要 补充 /next 与 /help 的宣传截图入口覆盖 验证 /screenshots 不触发文件读取、运行、预览、导出或 agent 控制 同步 AI 游戏创作 App 技术方案和决策记录
This commit is contained in:
@@ -1889,6 +1889,7 @@ const chatCommandHelp = [
|
||||
'/invite:准备试玩邀请文案',
|
||||
'/bug-report:准备缺陷复现记录',
|
||||
'/survey:准备试玩问卷问题',
|
||||
'/screenshots:准备宣传截图清单',
|
||||
'/criteria:查看当前任务验收标准',
|
||||
'/groups:查看专业组进度',
|
||||
'/balance:查看数值与难度口径',
|
||||
@@ -3914,6 +3915,67 @@ function summarizeProjectPlaytestSurvey(
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeProjectScreenshotChecklist(
|
||||
nextManifest: GameCreationAppManifest,
|
||||
trace: GameCreationAgentRunTrace | null,
|
||||
) {
|
||||
const preview = nextManifest.preview;
|
||||
const previewRunning = preview?.status === 'running' && preview.url;
|
||||
const previewSummary = previewRunning
|
||||
? `运行中 ${preview.url}`
|
||||
: preview
|
||||
? previewStatusLabels[preview.status]
|
||||
: '未启动';
|
||||
const tracePassed = isAgentRunTracePassed(trace);
|
||||
const blockedTrace =
|
||||
trace?.lifecycleStatus === 'killed' ||
|
||||
trace?.status === 'failed' ||
|
||||
trace?.status === 'needs-revision' ||
|
||||
trace?.stopReason === 'max-passes-exhausted';
|
||||
const visualAssetCount = nextManifest.assets.filter(isProjectVisualAsset)
|
||||
.length;
|
||||
|
||||
let draftCommand = '/listing';
|
||||
let draftCommandLabel = '准备作品页';
|
||||
if (!trace) {
|
||||
draftCommand = '/next';
|
||||
draftCommandLabel = '查看下一步';
|
||||
} else if (blockedTrace) {
|
||||
draftCommand = '/review';
|
||||
draftCommandLabel = '查看评审';
|
||||
} else if (!tracePassed) {
|
||||
draftCommand = '/trace';
|
||||
draftCommandLabel = '查看 trace';
|
||||
} else if (!previewRunning) {
|
||||
draftCommand = '/run';
|
||||
draftCommandLabel = '启动试玩';
|
||||
}
|
||||
|
||||
return {
|
||||
text: [
|
||||
'宣传截图:',
|
||||
`- 项目:${nextManifest.name}`,
|
||||
`- 当前状态:${
|
||||
tracePassed
|
||||
? `最近 run 已通过${trace?.runId ? ` ${trace.runId}` : ''}`
|
||||
: trace
|
||||
? `最近 run 未通过 ${trace.status} / ${trace.stopReason}`
|
||||
: '暂无最近 run'
|
||||
};预览 ${previewSummary}`,
|
||||
`- 可用素材:视觉素材 ${visualAssetCount} 个;总资产 ${nextManifest.assets.length} 个`,
|
||||
'- 截图目标:封面一张;核心操作一张;胜负/重开一张;移动或窄屏一张;异常/空状态不作为首批宣传图',
|
||||
'- 拍摄顺序:先确认 /run 可试玩;进入第一局 10-30 秒;截核心交互;再截结算或失败反馈',
|
||||
'- 命名建议:exports/screenshots/cover.png;gameplay.png;result.png;mobile.png',
|
||||
'- 文案搭配:每张图只配一句卖点;作品页标题和标签继续走 /listing',
|
||||
'- 参考:/listing;/publish;/share;/credits',
|
||||
'- 边界:只准备截图清单;不截屏;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
|
||||
`- 建议:${draftCommand}`,
|
||||
].join('\n'),
|
||||
draftCommand,
|
||||
draftCommandLabel,
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeProjectAcceptanceCriteria(
|
||||
nextManifest: GameCreationAppManifest,
|
||||
trace: GameCreationAgentRunTrace | null,
|
||||
@@ -5352,6 +5414,7 @@ function summarizeNextProjectActions(
|
||||
addSuggestion('准备试玩邀请文案', '/invite');
|
||||
addSuggestion('准备缺陷复现记录', '/bug-report');
|
||||
addSuggestion('准备试玩问卷问题', '/survey');
|
||||
addSuggestion('准备宣传截图清单', '/screenshots');
|
||||
|
||||
const readyTasks = selectGameCreationAppReadyTasks({
|
||||
tasks: taskRowsFromManifest(nextManifest),
|
||||
@@ -9408,6 +9471,26 @@ export function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prompt === '/screenshots') {
|
||||
if (!requireChatProjectForUserAction()) {
|
||||
return;
|
||||
}
|
||||
const summary = summarizeProjectScreenshotChecklist(
|
||||
manifest,
|
||||
agentRunTrace,
|
||||
);
|
||||
setMessages((current) => [
|
||||
...current,
|
||||
{
|
||||
role: 'assistant',
|
||||
text: summary.text,
|
||||
draftCommand: summary.draftCommand,
|
||||
draftCommandLabel: summary.draftCommandLabel,
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prompt === '/criteria') {
|
||||
if (!requireChatProjectForUserAction()) {
|
||||
return;
|
||||
|
||||
@@ -4749,6 +4749,7 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
expect(screen.getByText(/准备试玩邀请文案:\/invite/)).not.toBeNull();
|
||||
expect(screen.getByText(/准备缺陷复现记录:\/bug-report/)).not.toBeNull();
|
||||
expect(screen.getByText(/准备试玩问卷问题:\/survey/)).not.toBeNull();
|
||||
expect(screen.getByText(/准备宣传截图清单:\/screenshots/)).not.toBeNull();
|
||||
expect(screen.getByText(/查看素材署名与来源:\/credits/)).not.toBeNull();
|
||||
expect(screen.getByText(/查看美术素材:\/art/)).not.toBeNull();
|
||||
expect(screen.getByText(/查看发布准备清单:\/publish/)).not.toBeNull();
|
||||
@@ -5525,6 +5526,118 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
invoke.mock.calls.filter(([command]) => command === 'control_agent_run'),
|
||||
).toHaveLength(commandCountsBeforeSurvey.controlAgentRun);
|
||||
|
||||
const commandCountsBeforeScreenshots = {
|
||||
manifestRead: invoke.mock.calls.filter(
|
||||
([command]) => command === 'get_local_game_manifest',
|
||||
).length,
|
||||
fileRead: invoke.mock.calls.filter(
|
||||
([command]) => command === 'read_local_project_file',
|
||||
).length,
|
||||
fileList: invoke.mock.calls.filter(
|
||||
([command]) => command === 'list_local_project_files',
|
||||
).length,
|
||||
runLocal: invoke.mock.calls.filter(
|
||||
([command]) => command === 'run_limited_local_command',
|
||||
).length,
|
||||
startPreview: invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_local_game_preview',
|
||||
).length,
|
||||
openPreview: invoke.mock.calls.filter(
|
||||
([command]) => command === 'open_local_game_preview',
|
||||
).length,
|
||||
exportPackage: invoke.mock.calls.filter(
|
||||
([command]) => command === 'export_local_project_package',
|
||||
).length,
|
||||
exportList: invoke.mock.calls.filter(
|
||||
([command]) => command === 'list_local_project_export_packages',
|
||||
).length,
|
||||
controlAgentRun: invoke.mock.calls.filter(
|
||||
([command]) => command === 'control_agent_run',
|
||||
).length,
|
||||
};
|
||||
submitChat('/screenshots');
|
||||
const screenshotMessages = await screen.findAllByText(/宣传截图:/);
|
||||
const screenshotMessage =
|
||||
screenshotMessages[screenshotMessages.length - 1];
|
||||
expect(screenshotMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'当前状态:最近 run 已通过 run-main-shortcut-trace;预览 未启动',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'可用素材:视觉素材 1 个;总资产 2 个',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'截图目标:封面一张;核心操作一张;胜负/重开一张;移动或窄屏一张;异常/空状态不作为首批宣传图',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'拍摄顺序:先确认 /run 可试玩;进入第一局 10-30 秒;截核心交互;再截结算或失败反馈',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'命名建议:exports/screenshots/cover.png;gameplay.png;result.png;mobile.png',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'文案搭配:每张图只配一句卖点;作品页标题和标签继续走 /listing',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'参考:/listing;/publish;/share;/credits',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'边界:只准备截图清单;不截屏;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain('建议:/run');
|
||||
const screenshotMessageList = document.querySelector('.message-list');
|
||||
expect(screenshotMessageList).not.toBeNull();
|
||||
const screenshotDraftButtons = within(
|
||||
screenshotMessageList as HTMLElement,
|
||||
).getAllByRole('button', { name: '启动试玩' });
|
||||
fireEvent.click(
|
||||
screenshotDraftButtons[screenshotDraftButtons.length - 1],
|
||||
);
|
||||
expect(screen.getByLabelText('创作想法')).toHaveProperty('value', '/run');
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'get_local_game_manifest',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.manifestRead);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'read_local_project_file',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.fileRead);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'list_local_project_files',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.fileList);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'run_limited_local_command',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.runLocal);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_local_game_preview',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.startPreview);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'open_local_game_preview',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.openPreview);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'export_local_project_package',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.exportPackage);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'list_local_project_export_packages',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.exportList);
|
||||
expect(
|
||||
invoke.mock.calls.filter(([command]) => command === 'control_agent_run'),
|
||||
).toHaveLength(commandCountsBeforeScreenshots.controlAgentRun);
|
||||
|
||||
const commandCountsBeforeFeedback = {
|
||||
manifestRead: invoke.mock.calls.filter(
|
||||
([command]) => command === 'get_local_game_manifest',
|
||||
@@ -11496,6 +11609,7 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
screen.getByText(/\/bug-report:准备缺陷复现记录/),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/\/survey:准备试玩问卷问题/)).not.toBeNull();
|
||||
expect(screen.getByText(/\/screenshots:准备宣传截图清单/)).not.toBeNull();
|
||||
expect(screen.getByText(/\/criteria:查看当前任务验收标准/)).not.toBeNull();
|
||||
expect(screen.getByText(/\/groups:查看专业组进度/)).not.toBeNull();
|
||||
expect(screen.getByText(/\/balance:查看数值与难度口径/)).not.toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user