补充本地化文案聊天入口
新增 /localization 本地化与文案检查摘要 补充 /next 与 /help 的本地化入口覆盖 验证 /localization 不触发文件读取、运行、预览、导出或写入 同步 AI 游戏创作 App 技术方案和决策记录
This commit is contained in:
@@ -1878,6 +1878,7 @@ const chatCommandHelp = [
|
||||
'/mobile:查看移动试玩检查',
|
||||
'/compatibility:准备兼容性说明',
|
||||
'/accessibility:查看可读性与无障碍检查',
|
||||
'/localization:查看本地化与文案检查',
|
||||
'/performance:查看性能与加载检查',
|
||||
'/polish:查看试玩前打磨清单',
|
||||
'/risks:查看当前项目风险',
|
||||
@@ -2846,6 +2847,106 @@ function summarizeProjectAccessibilityGuide(
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeProjectLocalizationChecklist(
|
||||
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 goal =
|
||||
nextManifest.goal?.trim() ||
|
||||
trace?.goal?.trim() ||
|
||||
trace?.taskGraph.goal?.trim() ||
|
||||
'暂无';
|
||||
const manifestTasks = taskRowsFromManifest(nextManifest);
|
||||
const traceTasks = trace?.taskGraph.tasks ?? [];
|
||||
const tasks = manifestTasks.map(
|
||||
(task) => traceTasks.find((traceTask) => traceTask.id === task.id) ?? task,
|
||||
);
|
||||
const relevantTasks = [
|
||||
'design-foundation',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'publish-package',
|
||||
]
|
||||
.map((taskId) => tasks.find((task) => task.id === taskId))
|
||||
.filter((task): task is NonNullable<typeof task> => Boolean(task));
|
||||
const taskLines = relevantTasks.map(
|
||||
(task) =>
|
||||
`- ${task.id}:${taskGroupLabels[task.group]} / ${task.role} ${task.title} · ${taskStatusLabels[task.status]}`,
|
||||
);
|
||||
const hasPublishArtifact =
|
||||
trace?.artifacts.some((artifact) => artifact.path === 'exports/README.md') ??
|
||||
false;
|
||||
const latestCopyStep =
|
||||
trace?.steps
|
||||
.filter(
|
||||
(step) =>
|
||||
step.group === 'design' ||
|
||||
step.group === 'code' ||
|
||||
step.group === 'publishing' ||
|
||||
step.phase === 'evaluation' ||
|
||||
step.taskId === 'design-foundation' ||
|
||||
step.taskId === 'quality-review' ||
|
||||
step.taskId === 'publish-package',
|
||||
)
|
||||
.slice(-1)[0] ?? null;
|
||||
|
||||
let draftCommand = '/next';
|
||||
let draftCommandLabel = '查看下一步';
|
||||
if (trace && !tracePassed) {
|
||||
draftCommand = '/review';
|
||||
draftCommandLabel = '查看评审';
|
||||
} else if (hasPublishArtifact) {
|
||||
draftCommand = '/read exports/README.md';
|
||||
draftCommandLabel = '读发布说明';
|
||||
} else if (tracePassed) {
|
||||
draftCommand =
|
||||
'/agent-resume 本地化与文案:统一标题、按钮、状态提示、失败胜利文案、发布简介';
|
||||
draftCommandLabel = '补充文案';
|
||||
}
|
||||
|
||||
return {
|
||||
text: [
|
||||
'本地化与文案:',
|
||||
`- 项目:${nextManifest.name}`,
|
||||
`- 目标:${goal}`,
|
||||
'- 默认语言:简体中文;首版不承诺多语言',
|
||||
'- 文案范围:标题;目标提示;操作按钮;状态反馈;失败/胜利;重开;发布简介',
|
||||
`- 当前证据:${
|
||||
tracePassed
|
||||
? `最近 run 已通过${trace?.runId ? ` ${trace.runId}` : ''}`
|
||||
: trace
|
||||
? `最近 run 未通过 ${trace.status} / ${trace.stopReason}`
|
||||
: '暂无最近 run'
|
||||
};预览 ${previewSummary};发布说明 ${
|
||||
hasPublishArtifact ? '已生成' : '未见 trace 产物'
|
||||
}`,
|
||||
taskLines.length > 0
|
||||
? `- 关联任务:\n${taskLines.join('\n')}`
|
||||
: '- 关联任务:暂无',
|
||||
'- 检查口径:短句优先;动词一致;玩家术语统一;错误提示可复现;UI 文案避免开发解释',
|
||||
'- 暂不做:英日韩等多语言包;自动翻译;地区化素材;语音本地化;商店长文案 A/B',
|
||||
`- 最近文案相关步骤:${
|
||||
latestCopyStep
|
||||
? `${latestCopyStep.agent} #${latestCopyStep.pass} · ${latestCopyStep.status} · ${latestCopyStep.summary}`
|
||||
: '暂无'
|
||||
}`,
|
||||
'- 参考:/rules;/tutorial;/listing;/faq;/known-issues',
|
||||
'- 边界:只整理本地化与文案检查;不读取文件;不启动或打开预览;不导出试玩包;不上传云端;不发布作品;不写项目',
|
||||
`- 建议:${draftCommand}`,
|
||||
].join('\n'),
|
||||
draftCommand,
|
||||
draftCommandLabel,
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeProjectPerformanceCheck(
|
||||
nextManifest: GameCreationAppManifest,
|
||||
trace: GameCreationAgentRunTrace | null,
|
||||
@@ -6067,6 +6168,7 @@ function summarizeNextProjectActions(
|
||||
addSuggestion('查看移动试玩检查', '/mobile');
|
||||
addSuggestion('准备兼容性说明', '/compatibility');
|
||||
addSuggestion('查看可读性与无障碍检查', '/accessibility');
|
||||
addSuggestion('查看本地化与文案检查', '/localization');
|
||||
addSuggestion('查看性能与加载检查', '/performance');
|
||||
addSuggestion('查看试玩前打磨清单', '/polish');
|
||||
addSuggestion('查看数值与难度口径', '/balance');
|
||||
@@ -9952,6 +10054,26 @@ export function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prompt === '/localization') {
|
||||
if (!requireChatProjectForUserAction()) {
|
||||
return;
|
||||
}
|
||||
const summary = summarizeProjectLocalizationChecklist(
|
||||
manifest,
|
||||
agentRunTrace,
|
||||
);
|
||||
setMessages((current) => [
|
||||
...current,
|
||||
{
|
||||
role: 'assistant',
|
||||
text: summary.text,
|
||||
draftCommand: summary.draftCommand,
|
||||
draftCommandLabel: summary.draftCommandLabel,
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prompt === '/performance') {
|
||||
if (!requireChatProjectForUserAction()) {
|
||||
return;
|
||||
|
||||
@@ -3196,6 +3196,139 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
invoke.mock.calls.filter(([command]) => command === 'control_agent_run'),
|
||||
).toHaveLength(commandCountsBeforeAccessibility.controlAgentRun);
|
||||
|
||||
const commandCountsBeforeLocalization = {
|
||||
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('/localization');
|
||||
const localizationMessages =
|
||||
await screen.findAllByText(/本地化与文案:/);
|
||||
const localizationMessage =
|
||||
localizationMessages[localizationMessages.length - 1];
|
||||
expect(localizationMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'目标:做一个厨房弹幕游戏',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'默认语言:简体中文;首版不承诺多语言',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'文案范围:标题;目标提示;操作按钮;状态反馈;失败/胜利;重开;发布简介',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'当前证据:最近 run 已通过 run-main-shortcut-trace;预览 未启动;发布说明 已生成',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'design-foundation:策划组 / Gameplay 确定玩法规格 · 待处理',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'code-prototype:程序组 / Code 生成可运行原型 · 待处理',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'quality-review:程序组 / Review 执行质量评审 · 待处理',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'publish-package:运营组 / Publish 整理发布包装 · 待处理',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'检查口径:短句优先;动词一致;玩家术语统一;错误提示可复现;UI 文案避免开发解释',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'暂不做:英日韩等多语言包;自动翻译;地区化素材;语音本地化;商店长文案 A/B',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'最近文案相关步骤:Evaluator #1 · passed · 质量评审通过',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'参考:/rules;/tutorial;/listing;/faq;/known-issues',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'边界:只整理本地化与文案检查;不读取文件;不启动或打开预览;不导出试玩包;不上传云端;不发布作品;不写项目',
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
'建议:/read exports/README.md',
|
||||
);
|
||||
const localizationMessageList = document.querySelector('.message-list');
|
||||
expect(localizationMessageList).not.toBeNull();
|
||||
const localizationDraftButtons = within(
|
||||
localizationMessageList as HTMLElement,
|
||||
).getAllByRole('button', { name: '读发布说明' });
|
||||
fireEvent.click(
|
||||
localizationDraftButtons[localizationDraftButtons.length - 1],
|
||||
);
|
||||
expect(screen.getByLabelText('创作想法')).toHaveProperty(
|
||||
'value',
|
||||
'/read exports/README.md',
|
||||
);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'get_local_game_manifest',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.manifestRead);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'read_local_project_file',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.fileRead);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'list_local_project_files',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.fileList);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'run_limited_local_command',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.runLocal);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_local_game_preview',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.startPreview);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'open_local_game_preview',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.openPreview);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'export_local_project_package',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.exportPackage);
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'list_local_project_export_packages',
|
||||
),
|
||||
).toHaveLength(commandCountsBeforeLocalization.exportList);
|
||||
expect(
|
||||
invoke.mock.calls.filter(([command]) => command === 'control_agent_run'),
|
||||
).toHaveLength(commandCountsBeforeLocalization.controlAgentRun);
|
||||
|
||||
const commandCountsBeforePerformance = {
|
||||
manifestRead: invoke.mock.calls.filter(
|
||||
([command]) => command === 'get_local_game_manifest',
|
||||
@@ -4847,6 +4980,9 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
expect(
|
||||
screen.getByText(/查看可读性与无障碍检查:\/accessibility/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/查看本地化与文案检查:\/localization/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/查看性能与加载检查:\/performance/),
|
||||
).not.toBeNull();
|
||||
@@ -12588,6 +12724,9 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
expect(
|
||||
screen.getByText(/\/accessibility:查看可读性与无障碍检查/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/\/localization:查看本地化与文案检查/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/\/performance:查看性能与加载检查/),
|
||||
).not.toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user