diff --git a/apps/ai-game-creator-shell/src/app/appMetadata.ts b/apps/ai-game-creator-shell/src/app/appMetadata.ts new file mode 100644 index 000000000..8ba27e8e9 --- /dev/null +++ b/apps/ai-game-creator-shell/src/app/appMetadata.ts @@ -0,0 +1,5 @@ +import appPackage from '../../package.json'; + +/** Product metadata shared by the client UI and release bundle. */ +export const APP_NAME = 'Genarrative AI Game Creator'; +export const APP_VERSION = appPackage.version; diff --git a/apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx b/apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx index 320b5acc8..386c6d2cf 100644 --- a/apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx +++ b/apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx @@ -3,6 +3,7 @@ import { Cable, CheckCircle2, CircleAlert, + Info, LoaderCircle, RotateCcw, Save, @@ -13,6 +14,8 @@ import { import { type FormEvent, useEffect, useRef, useState } from 'react'; import { createGameCreationAppSeedTasks } from '../../../../../packages/shared/src/contracts/gameCreationApp'; +import BRAND_ICON from '../../../../../packages/shared/src/icons/taonier-product-ip.png'; +import { APP_NAME, APP_VERSION } from '../../app/appMetadata'; import { closeDialogOnBackdropMouseDown, closeDialogOnEscape, @@ -79,7 +82,12 @@ const defaultRuntimeConfigDraft: GameCreatorAppConfig = { }, }; -type RuntimeSettingsSection = 'general' | 'agents' | 'connections' | 'advanced'; +type RuntimeSettingsSection = + | 'general' + | 'agents' + | 'connections' + | 'advanced' + | 'about'; type RuntimeConfigToast = { tone: 'success' | 'error'; @@ -111,6 +119,12 @@ const runtimeSettingsSections = [ description: '上下文、超时与重试', icon: SlidersHorizontal, }, + { + id: 'about', + label: '关于', + description: '客户端信息', + icon: Info, + }, ] as const satisfies ReadonlyArray<{ id: RuntimeSettingsSection; label: string; @@ -1258,6 +1272,44 @@ export function RuntimeConfigDialog({ ) : null} ) : null} + {activeSection === 'about' ? ( +
+
+ +
+ + GENARRATIVE + +

{APP_NAME}

+

面向游戏创作者的智能创作客户端

+
+
+
+
+
客户端版本
+
+ v{APP_VERSION} +
+
+
+
运行环境
+
桌面客户端
+
+
+
+ ) : null} diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 1721e16b8..2f8f6e3e2 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -3212,6 +3212,101 @@ h2 { box-shadow: 0 0 0 3px var(--platform-input-focus-ring); } +.runtime-settings-about { + grid-column: 1 / -1; + display: grid; + gap: 14px; + max-width: 680px; +} + +.runtime-settings-about-hero { + display: flex; + align-items: center; + gap: 18px; + min-height: 158px; + padding: 24px; + border: 1px solid var(--platform-subpanel-border); + border-radius: 16px; + background: var(--runtime-settings-subpanel-fill); +} + +.runtime-settings-about-mark { + display: grid; + flex: 0 0 auto; + width: 72px; + height: 72px; + border: 1px solid var(--platform-nav-active-border); + border-radius: 20px; + background: var(--platform-nav-active-fill); + color: var(--platform-accent); + font-size: 32px; + font-weight: 900; + letter-spacing: -0.08em; + place-items: center; + box-shadow: var(--platform-nav-active-shadow); +} + +.runtime-settings-about-mark img { + width: 100%; + height: 100%; + object-fit: contain; + transform: scale(1.35); +} + +.runtime-settings-about-hero > div:last-child { + display: grid; + gap: 5px; + min-width: 0; +} + +.runtime-settings-about-kicker { + color: var(--platform-accent); + font-size: 10px; + font-weight: 900; + letter-spacing: 0.16em; +} + +.runtime-settings-about-hero h4 { + margin: 0; + color: var(--platform-text-strong); + font-size: 20px; + letter-spacing: -0.03em; +} + +.runtime-settings-about-hero p { + margin: 0; + color: var(--platform-text-soft); + font-size: 12px; +} + +.runtime-settings-about-meta { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 0; +} + +.runtime-settings-about-meta > div { + display: grid; + gap: 7px; + padding: 14px; + border: 1px solid var(--platform-subpanel-border); + border-radius: 12px; + background: var(--runtime-settings-subpanel-fill); +} + +.runtime-settings-about-meta dt { + color: var(--platform-text-soft); + font-size: 10px; +} + +.runtime-settings-about-meta dd { + margin: 0; + color: var(--platform-text-strong); + font-size: 14px; + font-weight: 800; +} + .runtime-agent-list { grid-column: 1 / -1; display: grid; @@ -4034,6 +4129,16 @@ iframe.preview-frame { grid-template-columns: 1fr; } + .runtime-settings-about-hero { + align-items: flex-start; + flex-direction: column; + min-height: 0; + } + + .runtime-settings-about-meta { + grid-template-columns: 1fr; + } + .runtime-agent-card-summary > span:last-child { max-width: 48%; text-align: right; 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 e38683510..da1e25334 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 内嵌预览、云同步或插件市场。 ## 技术选择