diff --git a/apps/ai-game-creator-shell/package.json b/apps/ai-game-creator-shell/package.json index 74f901d6f..4d7563c1a 100644 --- a/apps/ai-game-creator-shell/package.json +++ b/apps/ai-game-creator-shell/package.json @@ -18,12 +18,15 @@ "lucide-react": "^0.546.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "vite": "^6.2.0" + "vite": "^6.2.0", + "zustand": "^5.0.14" }, "devDependencies": { + "@tailwindcss/vite": "^4.1.14", "@tauri-apps/cli": "^2.11.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", + "tailwindcss": "^4.1.14", "typescript": "~5.8.2" } } diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index e42f5e595..e3a6650d1 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -23,14 +23,21 @@ import { Image, LogOut, MessageSquare, - Plus, Settings, - Sparkles, - Upload, User, - X, Zap, } from 'lucide-react'; +import HomeView, { + type HomeDraft, + type HomeAgentMode, + type HomeAgentModeItem, + type HomeAttachmentDraft, +} from './view/home'; +import { useLauncherHomeDraftStore } from './stores/useHomeDraftStore'; +import { + getClientProfileDashboard, + getClientProfileWalletLedger, +} from './services/clientApi'; import type { AuthEntryResponse, @@ -49,7 +56,6 @@ import { import type { ProfileDashboardSummary, ProfileWalletLedgerEntry, - ProfileWalletLedgerResponse, } from '../../../packages/shared/src/contracts/runtime'; import { createGameCreationAppManifest, @@ -105,13 +111,6 @@ type LauncherView = | 'contact' | 'news' | 'project-development'; -type HomeAgentMode = 'game' | 'art' | 'doc'; - -type HomeAttachmentDraft = { - id: string; - file: File; -}; - type LauncherImportedAttachment = { fileName: string; mediaType: string; @@ -149,26 +148,6 @@ type PendingNonEmptyProject = projectPath: string; }; -type EditorShowcaseResource = { - resourceId: string; - showcaseId?: string | null; - label?: string | null; - imageSrc: string; - width?: number; - height?: number; - prompt?: string | null; - actualPrompt?: string | null; - assetKind?: string | null; - authorDisplayName?: string | null; - authorPublicUserCode?: string | null; - likeCount?: number | null; -}; - -type EditorShowcaseResourceListResponse = { - resources: EditorShowcaseResource[]; - nextCursor?: string | null; -}; - interface InitLocalProjectResult { projectPath: string; manifestPath: string; @@ -1242,38 +1221,7 @@ async function logoutClientAuthSession() { } } -function getClientProfileDashboard() { - return requestAuthJson( - '/api/profile/dashboard', - { method: 'GET' }, - '读取泥点余额失败', - ); -} - -function getClientProfileWalletLedger() { - return requestAuthJson( - '/api/profile/wallet-ledger', - { method: 'GET' }, - '读取泥点明细失败', - ); -} - -function listClientShowcaseResources() { - return requestAuthJson( - '/api/editor/showcase/resources', - { method: 'GET' }, - '读取灵感推荐失败', - { skipAuth: true }, - ); -} - -const homeAgentModeItems: Array<{ - mode: HomeAgentMode; - label: string; - placeholder: string; - emptyPrompt: string; - icon: typeof Gamepad2; -}> = [ +const homeAgentModeItems: HomeAgentModeItem[] = [ { mode: 'game', label: '做游戏', @@ -1331,10 +1279,6 @@ function formatLocalDateTime(value: string | number | null | undefined) { }); } -function createHomeAttachmentId(file: File, index: number) { - return `${file.name}:${file.size}:${file.lastModified}:${index}:${Date.now()}`; -} - function buildHomeConversationContent( mode: HomeAgentMode, prompt: string, @@ -2164,12 +2108,6 @@ export function WorkspaceLauncher({ const [projectPath, setProjectPath] = useState(defaultProjectPath); const [status, setStatus] = useState('请选择项目'); const [launcherView, setLauncherView] = useState(initialView); - const [homeAgentMode, setHomeAgentMode] = useState('game'); - const [homePrompt, setHomePrompt] = useState(''); - const [homeAttachments, setHomeAttachments] = useState( - [], - ); - const [homeCreationBusy, setHomeCreationBusy] = useState(false); const [currentProjectContext, setCurrentProjectContext] = useState(null); const [recentWorkspaces, setRecentWorkspaces] = @@ -2197,13 +2135,11 @@ export function WorkspaceLauncher({ title: string; message: string; } | null>(null); - const [showcaseResources, setShowcaseResources] = useState< - EditorShowcaseResource[] - >([]); - const [showcaseStatus, setShowcaseStatus] = useState('正在读取灵感'); - const homeAttachmentInputRef = useRef(null); const accountMenuRef = useRef(null); const helpMenuRef = useRef(null); + const resetLauncherHomeDraft = useLauncherHomeDraftStore( + (state) => state.reset, + ); const launcherAgentChatAgents = deriveAgentStatusCards(seedManifest, null); const [agentChatProjectPath, setAgentChatProjectPath] = useState(defaultProjectPath); @@ -2269,23 +2205,8 @@ export function WorkspaceLauncher({ return; } setProfileDashboard(null); - setWalletLedgerStatus(error instanceof Error ? error.message : '泥点读取失败'); - }); - void listClientShowcaseResources() - .then((response) => { - if (disposed) { - return; - } - setShowcaseResources(response.resources.slice(0, 6)); - setShowcaseStatus(response.resources.length > 0 ? '已读取灵感' : '暂无灵感'); - }) - .catch((error) => { - if (disposed) { - return; - } - setShowcaseResources([]); - setShowcaseStatus( - error instanceof Error ? error.message : '灵感读取失败', + setWalletLedgerStatus( + error instanceof Error ? error.message : '泥点读取失败', ); }); return () => { @@ -2444,14 +2365,13 @@ export function WorkspaceLauncher({ ) { const trimmedProjectPath = validateProjectPath(nextProjectPath); if (!trimmedProjectPath) { - return; + return '项目目录无效'; } const invoke = resolveTauriInvoke(); if (!invoke) { setStatus('需要在 Tauri App 内运行'); - return; + return '需要在 Tauri App 内运行'; } - setHomeCreationBusy(true); setStatus('正在创建项目'); try { if (!skipNonEmptyCheck) { @@ -2468,7 +2388,7 @@ export function WorkspaceLauncher({ attachments, }); setStatus('目标文件夹不是空的'); - return; + return '目标文件夹不是空的,请确认是否继续新建'; } } const result = await invoke( @@ -2514,12 +2434,12 @@ export function WorkspaceLauncher({ recentRunStopReason: null, createdAt: Date.now(), }); - setHomePrompt(''); - setHomeAttachments([]); + resetLauncherHomeDraft(); + return '已创建项目并进入项目开发'; } catch (error) { - setStatus(error instanceof Error ? error.message : String(error)); - } finally { - setHomeCreationBusy(false); + const message = error instanceof Error ? error.message : String(error); + setStatus(message); + throw error; } } @@ -2632,17 +2552,21 @@ export function WorkspaceLauncher({ } const pendingProject = pendingNonEmptyProject; setPendingNonEmptyProject(null); - if (pendingProject.kind === 'home-create') { - await createHomeProjectFromDirectory( - pendingProject.projectPath, - pendingProject.mode, - pendingProject.prompt, - pendingProject.attachments, - true, - ); - return; + try { + if (pendingProject.kind === 'home-create') { + await createHomeProjectFromDirectory( + pendingProject.projectPath, + pendingProject.mode, + pendingProject.prompt, + pendingProject.attachments, + true, + ); + return; + } + await createProjectFromProjectPage(pendingProject.projectPath, true); + } catch (error) { + setStatus(error instanceof Error ? error.message : String(error)); } - await createProjectFromProjectPage(pendingProject.projectPath, true); } function cancelCreateInNonEmptyFolder() { @@ -2660,43 +2584,51 @@ export function WorkspaceLauncher({ void openProject(projectPath, 'open'); } - async function handleHomeSubmit(event: FormEvent) { - event.preventDefault(); - const activeMode = homeAgentModeItems.find( - (item) => item.mode === homeAgentMode, - ); - if (!homePrompt.trim() && homeAttachments.length === 0) { - setStatus(activeMode?.emptyPrompt ?? '请输入需求或上传附件'); - return; - } + async function createHomeDraft(draft: HomeDraft) { const invoke = resolveTauriInvoke(); if (!invoke) { - setStatus('需要在 Tauri App 内运行'); - return; + throw new Error('需要在 Tauri App 内运行'); } - setHomeCreationBusy(true); - setStatus('请选择项目目录'); + const selectedPath = await invoke( + 'pick_local_project_directory', + ); + if (!selectedPath) { + return '已取消'; + } + return createHomeProjectFromDirectory( + selectedPath, + draft.mode, + draft.prompt, + draft.attachments, + ); + } + + async function loadWalletLedger() { + setWalletLedgerStatus('正在读取明细'); try { - const selectedPath = await invoke( - 'pick_local_project_directory', - ); - if (!selectedPath) { - setStatus('已取消'); - return; - } - await createHomeProjectFromDirectory( - selectedPath, - homeAgentMode, - homePrompt, - homeAttachments, + const response = await getClientProfileWalletLedger(); + setWalletLedger(response.entries.slice(0, 5)); + setWalletLedgerStatus( + response.entries.length > 0 ? '已读取明细' : '暂无泥点明细', ); } catch (error) { - setStatus(error instanceof Error ? error.message : String(error)); - } finally { - setHomeCreationBusy(false); + setWalletLedger([]); + setWalletLedgerStatus( + error instanceof Error ? error.message : '明细读取失败', + ); } } + function showLauncherNotice(title: string) { + setAccountMenuOpen(false); + setHelpMenuOpen(false); + setWalletPanelOpen(false); + setLauncherNotice({ + title, + message: `${title}正在接入中,当前版本会先保留入口。`, + }); + } + async function handlePickProjectDirectory() { const invoke = resolveTauriInvoke(); if (!invoke) { @@ -2744,53 +2676,6 @@ export function WorkspaceLauncher({ } } - function handleHomeAttachmentSelection(event: ChangeEvent) { - const files = Array.from(event.currentTarget.files ?? []); - event.currentTarget.value = ''; - if (files.length === 0) { - return; - } - setHomeAttachments((current) => [ - ...current, - ...files.map((file, index) => ({ - id: createHomeAttachmentId(file, index), - file, - })), - ]); - } - - function removeHomeAttachment(attachmentId: string) { - setHomeAttachments((current) => - current.filter((attachment) => attachment.id !== attachmentId), - ); - } - - async function loadWalletLedger() { - setWalletLedgerStatus('正在读取明细'); - try { - const response = await getClientProfileWalletLedger(); - setWalletLedger(response.entries.slice(0, 5)); - setWalletLedgerStatus( - response.entries.length > 0 ? '已读取明细' : '暂无泥点明细', - ); - } catch (error) { - setWalletLedger([]); - setWalletLedgerStatus( - error instanceof Error ? error.message : '明细读取失败', - ); - } - } - - function showLauncherNotice(title: string) { - setAccountMenuOpen(false); - setHelpMenuOpen(false); - setWalletPanelOpen(false); - setLauncherNotice({ - title, - message: `${title}正在接入中,当前版本会先保留入口。`, - }); - } - function handleRecentWorkspaceRefresh() { if (recentWorkspaces.length === 0 || recentWorkspaceRefreshing) { return; @@ -3189,10 +3074,6 @@ export function WorkspaceLauncher({ const recentProjectRows = projectRows .filter((project) => project.canOpen) .slice(0, 3); - const activeHomeMode = - homeAgentModeItems.find((item) => item.mode === homeAgentMode) ?? - homeAgentModeItems[0]!; - const ActiveHomeModeIcon = activeHomeMode.icon; const currentAgentChatAgent = selectedLauncherAgentChatAgent(); const currentAgentChatLlmWarning = getCurrentAgentChatLlmWarning( currentAgentChatAgent, @@ -3256,7 +3137,9 @@ export function WorkspaceLauncher({
{currentUser.displayName || '陶泥儿用户'} - {currentUser.phoneNumberMasked || currentUser.publicUserCode} + {currentUser.phoneNumberMasked || + currentUser.publicUserCode || + '账号信息读取中'} - @@ -3407,171 +3297,19 @@ export function WorkspaceLauncher({
{launcherView === 'home' ? ( - <> -
-
- - -
-

陶泥儿 GameAgent

-

{activeHomeMode.placeholder}

-
-
-
- {homeAgentModeItems.map((item) => { - const ModeIcon = item.icon; - return ( - - ); - })} -
-
-