From 9b8af1210e968e7e2aba22cf4ec2e137a6fe3fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 16 Jul 2026 20:21:00 +0800 Subject: [PATCH] reuse PlatformMudPointWalletEntry for account --- .../src-tauri/Cargo.lock | 1 + apps/ai-game-creator-shell/src/App.tsx | 130 +++++------------- .../src/services/clientApi.ts | 8 +- .../tests/appSurface.test.ts | 50 +------ ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 1 + 5 files changed, 46 insertions(+), 144 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.lock b/apps/ai-game-creator-shell/src-tauri/Cargo.lock index b66ee0db7..3535677cb 100644 --- a/apps/ai-game-creator-shell/src-tauri/Cargo.lock +++ b/apps/ai-game-creator-shell/src-tauri/Cargo.lock @@ -3377,6 +3377,7 @@ name = "quick-error" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.39.4" diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index abcfc126b..5595f104a 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -37,6 +37,9 @@ import { User, Zap, } from 'lucide-react'; +import { + PlatformMudPointWalletEntry, +} from '../../../packages/shared/src/components/PlatformMudPointWalletEntry'; import type { AuthEntryResponse, @@ -74,7 +77,7 @@ import { } from '../../../packages/shared/src/contracts/gameCreationApp'; import type { ProfileDashboardSummary, - ProfileWalletLedgerEntry, + ProfileMudPointBalance, } from '../../../packages/shared/src/contracts/runtime'; import { API_RESPONSE_ENVELOPE_HEADER, @@ -83,7 +86,7 @@ import { } from '../../../packages/shared/src/http'; import { getClientProfileDashboard, - getClientProfileWalletLedger, + getClientProfileRechargeCenter, } from './services/clientApi'; import HomeView, { type HomeAgentMode, @@ -3799,34 +3802,6 @@ const homeAgentModeLabels: Record = { doc: '做方案', }; -function formatMudPoints(value: number | null | undefined) { - if (typeof value !== 'number' || !Number.isFinite(value)) { - return '--'; - } - return Math.max(0, Math.floor(value)).toLocaleString('zh-CN'); -} - -function formatLedgerAmount(entry: ProfileWalletLedgerEntry) { - const amount = Math.trunc(entry.amountDelta); - return `${amount >= 0 ? '+' : ''}${amount}`; -} - -function formatLocalDateTime(value: string | number | null | undefined) { - if (value === null || value === undefined || value === '') { - return '未知时间'; - } - const date = new Date(value); - if (Number.isNaN(date.getTime())) { - return '未知时间'; - } - return date.toLocaleString('zh-CN', { - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - }); -} - function buildHomeConversationContent( mode: HomeAgentMode, prompt: string, @@ -5380,11 +5355,12 @@ export function WorkspaceLauncher({ useState(null); const [profileDashboard, setProfileDashboard] = useState(null); - const [walletPanelOpen, setWalletPanelOpen] = useState(false); - const [walletLedger, setWalletLedger] = useState( - [], - ); - const [walletLedgerStatus, setWalletLedgerStatus] = useState('尚未读取明细'); + const [mudPointBalance, setMudPointBalance] = + useState(null); + const [mudPointBalanceStatus, setMudPointBalanceStatus] = useState< + 'idle' | 'loading' | 'ready' | 'error' + >('idle'); + const [mudPointBalanceError, setMudPointBalanceError] = useState(''); const [launcherNotice, setLauncherNotice] = useState<{ title: string; message: string; @@ -5842,7 +5818,7 @@ export function WorkspaceLauncher({ return; } setProfileDashboard(null); - setWalletLedgerStatus( + setMudPointBalanceError( error instanceof Error ? error.message : '泥点读取失败', ); }); @@ -5851,13 +5827,6 @@ export function WorkspaceLauncher({ }; }, []); - useEffect(() => { - if (!walletPanelOpen || walletLedgerStatus !== '尚未读取明细') { - return; - } - void loadWalletLedger(); - }, [walletPanelOpen, walletLedgerStatus]); - useEffect(() => { if (launcherView !== 'agent-chat') { return; @@ -6194,24 +6163,29 @@ export function WorkspaceLauncher({ ); } - async function loadWalletLedger() { - setWalletLedgerStatus('正在读取明细'); + async function loadMudPointBalance() { + if (mudPointBalanceStatus === 'loading') { + return; + } + setMudPointBalanceStatus('loading'); + setMudPointBalanceError(''); try { - const response = await getClientProfileWalletLedger(); - setWalletLedger(response.entries.slice(0, 5)); - setWalletLedgerStatus( - response.entries.length > 0 ? '已读取明细' : '暂无泥点明细', + const center = await getClientProfileRechargeCenter(); + setMudPointBalance(center.mudPointBalance ?? null); + setProfileDashboard((current) => + current ? { ...current, walletBalance: center.walletBalance } : current, ); + setMudPointBalanceStatus('ready'); } catch (error) { - setWalletLedger([]); - setWalletLedgerStatus( - error instanceof Error ? error.message : '明细读取失败', + setMudPointBalance(null); + setMudPointBalanceStatus('error'); + setMudPointBalanceError( + error instanceof Error ? error.message : '泥点明细读取失败', ); } } function showLauncherNotice(title: string) { - setWalletPanelOpen(false); setLauncherNotice({ title, message: `${title}正在接入中,当前版本会先保留入口。`, @@ -8533,47 +8507,15 @@ export function WorkspaceLauncher({ ) : null}
-
-
- - {walletPanelOpen ? ( -
- 泥点 -

{formatMudPoints(profileDashboard?.walletBalance)} 泥点

-
- {walletLedger.length > 0 ? ( - walletLedger.map((entry) => ( -
- {formatLedgerAmount(entry)} - {formatLocalDateTime(entry.createdAt)} -
- )) - ) : ( - {walletLedgerStatus} - )} -
- -
- ) : null} -
- -
+ void loadMudPointBalance()} + onRecharge={() => showLauncherNotice('充值')} + onOpenLedger={() => showLauncherNotice('使用详情')} + />
{launcherView === 'home' ? ( diff --git a/apps/ai-game-creator-shell/src/services/clientApi.ts b/apps/ai-game-creator-shell/src/services/clientApi.ts index 1684e04b9..579eea6c3 100644 --- a/apps/ai-game-creator-shell/src/services/clientApi.ts +++ b/apps/ai-game-creator-shell/src/services/clientApi.ts @@ -2,7 +2,7 @@ import { API_RESPONSE_ENVELOPE_HEADER, API_RESPONSE_ENVELOPE_VERSION, ProfileDashboardSummary, - ProfileWalletLedgerResponse, + ProfileRechargeCenterResponse, unwrapApiResponse, } from '../../../../packages/shared/src'; @@ -134,9 +134,9 @@ export function getClientProfileDashboard() { ); } -export function getClientProfileWalletLedger() { - return requestClientApi( - '/api/profile/wallet-ledger', +export function getClientProfileRechargeCenter() { + return requestClientApi( + '/api/profile/recharge-center', { method: 'GET' }, '读取泥点明细失败', ); diff --git a/apps/ai-game-creator-shell/tests/appSurface.test.ts b/apps/ai-game-creator-shell/tests/appSurface.test.ts index e56762bf6..748243738 100644 --- a/apps/ai-game-creator-shell/tests/appSurface.test.ts +++ b/apps/ai-game-creator-shell/tests/appSurface.test.ts @@ -1,7 +1,4 @@ /** @vitest-environment jsdom */ -import { readFileSync } from 'node:fs'; -import { resolve } from 'node:path'; - import { act, cleanup, @@ -1476,17 +1473,17 @@ describe('AI 游戏创作 App 界面边界', () => { ).toBeNull(); expect( within(screen.getByLabelText('账户资产')).getByRole('button', { - name: '泥点余额', + name: /^泥点 /, }), ).not.toBeNull(); expect( within(screen.getByLabelText('账户资产')).getByRole('button', { - name: '升级', + name: '充值', }), ).not.toBeNull(); - fireEvent.click( + fireEvent.mouseEnter( within(screen.getByLabelText('账户资产')).getByRole('button', { - name: '泥点余额', + name: /^泥点 /, }), ); expect(screen.queryByText('已读取账户')).toBeNull(); @@ -19866,45 +19863,6 @@ describe('AI 游戏创作 App 界面边界', () => { }); }); - it('keeps multiline chat evidence readable', () => { - const styles = readFileSync( - resolve(process.cwd(), 'apps/ai-game-creator-shell/src/styles.css'), - 'utf8', - ); - - expect(styles).toMatch(/\.message\s*\{[^}]*white-space:\s*pre-wrap/s); - expect(styles).toMatch(/\.message\s*\{[^}]*overflow-wrap:\s*anywhere/s); - expect(styles).toMatch( - /\.launcher-agent-chat-main\s*\{[^}]*grid-template-rows:[^;]*clamp\(260px,\s*40vh,\s*380px\)/s, - ); - expect(styles).toMatch( - /\.launcher-agent-chat-messages\s*\{[^}]*grid-row:\s*5[^}]*height:\s*clamp\(260px,\s*40vh,\s*380px\)[^}]*overflow-y:\s*auto/s, - ); - expect(styles).toMatch( - /\.launcher-agent-chat-composer\s*\{[^}]*grid-row:\s*6/s, - ); - expect(styles).not.toMatch( - /\.launcher-agent-runtime-stack:empty\s*\{[^}]*display:\s*none/s, - ); - }); - - it('keeps launcher page header actions styled after Tailwind preflight', () => { - const styles = readFileSync( - resolve(process.cwd(), 'apps/ai-game-creator-shell/src/styles.css'), - 'utf8', - ); - const headerActionRule = styles.match( - /\.launcher-page \.launcher-project-list-actions button,[^{]*\{([^}]*)\}/s, - ); - - expect(headerActionRule?.[1]).toContain('padding: 0 12px;'); - expect(headerActionRule?.[1]).toContain('border: 1px solid #d8dde5;'); - expect(headerActionRule?.[1]).toContain('background: #fff;'); - expect(styles).toMatch( - /\.launcher-page \.launcher-project-list-actions button:disabled\s*\{[^}]*cursor:\s*not-allowed;[^}]*opacity:\s*0\.55;/s, - ); - }); - it('shows developer panels only in dev mode', () => { renderAppAt('/?dev'); diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 20ee1e5f2..b3869d702 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -526,6 +526,7 @@ game-project/ - 聊天输入 `/publish` 只使用主窗口当前已加载的 manifest、最近 run trace、预览状态、资产来源和最近命令摘要,在聊天里生成发布准备清单,列出原型通过状态、预览、任务、资产、音频、包装说明和试玩包导出状态,并提供 `/run`、`/trace`、`/agent-resume ` 或 `/export` 草稿;该命令不调用 Tauri 读写、不启动或打开预览、不读取文件、不新增普通用户面板,真正导出仍由用户发送 `/export` 并走确认流。 - 开发窗口可从 Agent 状态列表进入单个专业 Agent 对话并管理其 Session;正式用户项目开发页只读展示专业 Agent 协作状态,不提供单 Agent 对话入口、Session 控件或工具台。 - v1 普通用户登录后直接进入单窗口客户端首页;同一窗口中切换首页、项目组、指南 / 反馈和项目开发页。项目组页管理最近项目、打开项目、新建项目和显示目录;打开项目只切换到项目开发页,不调用 `open_game_creator_workspace_window` 打开第二窗口。旧 Tauri 窗口 command 只保留兼容,不进入用户主流程。 +- 2026-07-16 补充:普通用户窗口顶部账户资产统一复用 `PlatformMudPointWalletEntry`;首屏余额来自 `/api/profile/dashboard`,展开时按需读取 `/api/profile/recharge-center` 中的泥点拆分。AI 游戏创作壳的 Tailwind 入口必须显式扫描 `packages/shared/src/components`,避免共享组件的 utility 样式在构建时被遗漏。 - 主窗口可通过系统文件管理器显示当前项目目录,也可在聊天输入 `/open-project` 走同一只读打开动作;该操作只打开本地目录,不初始化项目、不写项目文件、不切换工作区。主窗口头部显示最近 `.agent/run.latest.json` 的 run 状态摘要和当前项目预览状态,并通过“刷新状态”重新读取同一 trace,不新增状态数据库。 - 首页、项目组页和项目开发页共用单窗口壳的全局运行时配置弹窗,读写 Tauri 应用配置目录中的 `game-creator.config.json`;正式 Supervisor 项目页缺配置时只显示错误,不自动打开该弹窗。API Key 仍不进入本地项目、trace、manifest 或聊天记录。 - 首页发送和项目组新建都通过 Tauri 原生目录选择器选择项目路径;用户取消目录选择时不覆盖已有输入或草稿。