+
);
diff --git a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts
index e6ccb2fd3..6be9b73a8 100644
--- a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts
+++ b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts
@@ -28,12 +28,25 @@ import {
} from './harness';
export function registerClientHomeTests() {
- it('keeps the empty inspiration module without requesting the retired feed', async () => {
+ it('shows the built-in inspiration masonry gallery and opens a dismissible preview without requesting the retired feed', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch');
renderLauncherAt('/?launcher');
- expect(screen.getByLabelText('灵感推荐')).not.toBeNull();
+ const inspiration = screen.getByLabelText('灵感推荐');
+ const inspirationImages = within(inspiration).getAllByRole('button', {
+ name: /查看灵感图片/,
+ });
+ expect(inspirationImages).toHaveLength(23);
+ expect(inspiration.closest('.overflow-y-auto')).not.toBeNull();
expect(screen.queryByText('暂无灵感')).toBeNull();
+
+ fireEvent.click(inspirationImages[0]!);
+ const preview = screen.getByRole('dialog', { name: '查看灵感图片' });
+ fireEvent.click(screen.getByAltText('放大的灵感图片'));
+ expect(screen.getByRole('dialog', { name: '查看灵感图片' })).not.toBeNull();
+ fireEvent.click(preview);
+ expect(screen.queryByRole('dialog', { name: '查看灵感图片' })).toBeNull();
+
await act(async () => {
await Promise.resolve();
});
diff --git a/apps/ai-game-creator-shell/tests/appSurface/runtime-settings.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/runtime-settings.suite.ts
index e013037c3..9916904e2 100644
--- a/apps/ai-game-creator-shell/tests/appSurface/runtime-settings.suite.ts
+++ b/apps/ai-game-creator-shell/tests/appSurface/runtime-settings.suite.ts
@@ -1,3 +1,4 @@
+import { APP_VERSION } from '../../src/app/appMetadata';
import {
act,
createGameCreationAppManifest,
@@ -234,6 +235,58 @@ export function registerAgentStatusDerivationTests() {
}
export function registerRuntimeSettingsTests() {
+ it('shows the client version on the About settings page', async () => {
+ const invoke = vi.fn(async (command: string) => {
+ if (command === 'read_game_creator_app_config') {
+ return {
+ path: '/home/test/AppData/game-creator.config.json',
+ config: {
+ agentMode: 'codex_app_server',
+ llm: {
+ apiKey: '',
+ baseUrl: 'https://llm.example.test/v1',
+ model: 'gpt-about',
+ apiKind: 'openai_responses',
+ reasoningEffort: 'high',
+ stream: true,
+ webSearchEnabled: false,
+ contextWindowTokens: 128000,
+ autoCompactTokenLimit: 64000,
+ toolOutputTokenLimit: 12000,
+ requestTimeoutMs: 180000,
+ maxRetries: 2,
+ retryBackoffMs: 500,
+ },
+ agentLlm: {},
+ editorApi: {
+ baseUrl: 'http://127.0.0.1:8082',
+ apiKey: '',
+ },
+ },
+ };
+ }
+ throw new Error(`unexpected invoke ${command}`);
+ });
+ window.__TAURI__ = { core: { invoke } };
+ renderLauncherAt('/?launcher');
+
+ fireEvent.click(screen.getByRole('button', { name: '配置' }));
+ expect(
+ await screen.findByRole('dialog', { name: '运行时配置' }),
+ ).not.toBeNull();
+
+ fireEvent.click(screen.getByRole('button', { name: /关于/ }));
+
+ expect(screen.getByText('Genarrative AI Game Creator')).not.toBeNull();
+ expect(
+ screen.getByTestId('runtime-settings-app-logo').getAttribute('src'),
+ ).toContain('taonier-product-ip.png');
+ expect(screen.getByTestId('runtime-settings-app-version').textContent).toBe(
+ `v${APP_VERSION}`,
+ );
+ expect(screen.getByText('桌面客户端')).not.toBeNull();
+ });
+
it('keeps the Agent mode fixed to app-server and preserves its LLM config', async () => {
const invoke = vi.fn(
async (command: string, args?: Record
) => {
diff --git a/apps/ai-game-creator-shell/tsconfig.json b/apps/ai-game-creator-shell/tsconfig.json
index b0cd676a5..894821755 100644
--- a/apps/ai-game-creator-shell/tsconfig.json
+++ b/apps/ai-game-creator-shell/tsconfig.json
@@ -6,6 +6,7 @@
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
+ "resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": false,
diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md
index 8beef2c9a..f6ef11e15 100644
--- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md
+++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md
@@ -137,7 +137,7 @@ Supervisor 认领该回执后,由父 run 自己为每个原 delivery 逐一创
## 目标
-在 Genarrative 内建设独立桌面 App:普通用户通过项目开发工作台中的陶泥儿对话、资源画布、运行状态和确认操作,让平台生成保存在本地的可运行 Web 游戏原型,并通过本地 HTTP server 预览;主窗口提供运行时配置入口,用于保存发布版 AppData / Tauri 配置目录里的 LLM 配置及受控开发者 External Editor 配置。普通客户素材画布使用平台登录态调用内部编辑器 API,不展示或要求填写画板 Base URL / API Key。任务明细、原始文件、命令日志和专业 Agent 调试控制只通过显式开发调试入口查看,不随普通客户端启动额外打开窗口。v1 的生成闭环仍以 Web 小游戏为主,同时允许用户打开已有 Godot 项目:用户选择的目录始终作为工作区根,`.agent/`、Session、Runtime、文件工具和外围资料都留在该根;客户端检查根目录及一层直接子目录中的普通文件 `project.godot`,将唯一命中的实际目录以工作区相对 `godotProjectRoot` 记录到 manifest。Agent 使用标准运行档继续修改,不创建 `game/`、`assets/`、`memory/`、`exports/` 平行目录;本期不扩展 Unity、Godot 内嵌预览、云同步或插件市场。
+在 Genarrative 内建设独立桌面 App:普通用户通过项目开发工作台中的陶泥儿对话、资源画布、运行状态和确认操作,让平台生成保存在本地的可运行 Web 游戏原型,并通过本地 HTTP server 预览;主窗口提供运行时配置入口,用于保存发布版 AppData / Tauri 配置目录里的 LLM 配置及受控开发者 External Editor 配置,设置弹窗同时提供独立“关于”页并显示从客户端构建版本注入的版本号。普通客户素材画布使用平台登录态调用内部编辑器 API,不展示或要求填写画板 Base URL / API Key。任务明细、原始文件、命令日志和专业 Agent 调试控制只通过显式开发调试入口查看,不随普通客户端启动额外打开窗口。v1 的生成闭环仍以 Web 小游戏为主,同时允许用户打开已有 Godot 项目:用户选择的目录始终作为工作区根,`.agent/`、Session、Runtime、文件工具和外围资料都留在该根;客户端检查根目录及一层直接子目录中的普通文件 `project.godot`,将唯一命中的实际目录以工作区相对 `godotProjectRoot` 记录到 manifest。Agent 使用标准运行档继续修改,不创建 `game/`、`assets/`、`memory/`、`exports/` 平行目录;本期不扩展 Unity、Godot 内嵌预览、云同步或插件市场。
## 技术选择
@@ -683,7 +683,7 @@ game-project/
- 结构化对话记录按授权本地项目路径追加 JSONL;正式聊天读取 Supervisor active Session 与只读 legacy 项目历史,开发单 Agent 对话读取对应 Agent Session。开发入口已支持本地 Session 新建、切换、归档和分叉,但不提供云端同步。
- Agent 状态列表从 `.agent/manifest.json` 的任务 / 角色清单、`.agent/run.latest.json` / `.agent/runs/.json` 的 step、taskGraph、passPlans、lifecycleStatus,以及 `read_game_creator_agent_runtimes` 批量读取的 `.agent/runtime/agents/.json` 和最近任务派生;v1 不新增独立状态数据库,也不承诺完整后台 runner。
- 首页发送、项目组目录选择和本地文件选择必须使用 Tauri 非阻塞原生 picker,并把选择器绑定到当前 `client` 窗口;禁止在同步 command 中调用 `blocking_pick_folder` / `blocking_pick_file` 阻塞 WebView 事件循环。选择器打开期间保留首页草稿可编辑,取消后恢复“开启创作”按钮并回显“已取消”。
-- 当前尚未定义 GameAgent 独立的灵感数据源;首页保留“灵感推荐”模块及空白内容容器,但不显示无数据文案,也不得展示或请求主站 `/creation` 的陶泥儿精选 `/api/editor/showcase/resources`。后续接入前必须先明确独立数据契约和交互验收。
+- GameAgent 首页“灵感推荐”当前使用客户端随包发布的内置图片素材,不请求主站 `/creation` 的陶泥儿精选 `/api/editor/showcase/resources`,也不引入远端灵感数据源。图片按原始宽高比组成响应式瀑布流,首页内容区域允许纵向滚动;点击图片只打开全屏原图预览,点击图片外遮罩或按 Escape 关闭,不展示标题或其它业务动作。后续改接远端数据前仍须先明确独立数据契约和交互验收。
- 首页正式桌面版以约 `814px` 的居中内容栏组织品牌区、创作类型、输入框、最近项目与灵感推荐;品牌区和类型按钮在内容栏左缘对齐,输入框和下方模块仍保持整体居中。标题使用深色暖橙层级,固定副标题为“你的游戏创作管家”;选中的创作类型使用实心暖橙按钮,未选中项使用浅色描边,输入框使用更舒展的单行创作起始比例。
- 最近项目固定展示至多三个横向信息卡:左侧为本地渐变封面占位,右侧只投影项目名称、项目类型、目录真实修改时间和已有最近运行状态。目录检查通过 `modifiedAt` 提供修改时间;没有封面资产时不得假装读取了项目封面,也不新增封面持久化字段。
- debug 构建启动后在用户 `client` 窗口之外额外打开 `developer` 窗口;该窗口用于开发者单独选择 Agent、管理对应 active/archived Session 并读取历史,用户消息和真实 Agent 回复只持久化到 `.agent/conversations/agents//` 下的规范 Session。普通用户窗口不得出现 `Agent 聊天` 导航、picker 或工具台入口。