diff --git a/apps/ai-game-creator-shell/package.json b/apps/ai-game-creator-shell/package.json index 78f73ea34..f2a16f265 100644 --- a/apps/ai-game-creator-shell/package.json +++ b/apps/ai-game-creator-shell/package.json @@ -24,12 +24,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 c038dcdb5..960f3f863 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -32,14 +32,22 @@ import { Plus, Send, Settings, - Sparkles, Target, Trash2, - 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, @@ -58,7 +66,6 @@ import { import type { ProfileDashboardSummary, ProfileWalletLedgerEntry, - ProfileWalletLedgerResponse, } from '../../../packages/shared/src/contracts/runtime'; import { createGameCreationAppManifest, @@ -116,7 +123,6 @@ type LauncherView = | 'contact' | 'news' | 'project-development'; -type HomeAgentMode = 'game' | 'art' | 'doc'; type AgentChatInteractionMode = 'run' | 'chat' | 'goal'; type AgentBackgroundSubmitMode = 'steer' | 'queue'; type AgentChatReplyPhase = @@ -146,12 +152,6 @@ type AgentGoalDialogState = { constraintsText: string; verificationText: string; }; - -type HomeAttachmentDraft = { - id: string; - file: File; -}; - type LauncherImportedAttachment = { fileName: string; mediaType: string; @@ -190,26 +190,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; @@ -3796,38 +3776,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: '做游戏', @@ -3885,10 +3834,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, @@ -5427,12 +5372,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] = @@ -5459,13 +5398,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); @@ -5920,25 +5857,6 @@ export function WorkspaceLauncher({ 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 : '灵感读取失败', - ); - }); return () => { disposed = true; }; @@ -6066,14 +5984,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) { @@ -6090,7 +6007,7 @@ export function WorkspaceLauncher({ attachments, }); setStatus('目标文件夹不是空的'); - return; + return '目标文件夹不是空的,请确认是否继续新建'; } } const result = await invoke( @@ -6145,12 +6062,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; } } @@ -6275,17 +6192,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() { @@ -6303,43 +6224,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) { @@ -6387,53 +6316,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; @@ -8578,10 +8460,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 currentAgentChatSession = selectedLauncherAgentChatSession(); const currentAgentChatSessionArchived = @@ -8716,7 +8594,9 @@ export function WorkspaceLauncher({
{currentUser.displayName || '陶泥儿用户'} - {currentUser.phoneNumberMasked || currentUser.publicUserCode} + {currentUser.phoneNumberMasked || + currentUser.publicUserCode || + '账号信息读取中'} - @@ -8867,177 +8754,19 @@ export function WorkspaceLauncher({
{launcherView === 'home' ? ( - <> -
-
- - -
-

陶泥儿 GameAgent

-

{activeHomeMode.placeholder}

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