diff --git a/docs/technical/FRONTEND_FIRST_LOAD_PERFORMANCE_FIX_2026-04-26.md b/docs/technical/FRONTEND_FIRST_LOAD_PERFORMANCE_FIX_2026-04-26.md index 4178d9d2..04ab6cbd 100644 --- a/docs/technical/FRONTEND_FIRST_LOAD_PERFORMANCE_FIX_2026-04-26.md +++ b/docs/technical/FRONTEND_FIRST_LOAD_PERFORMANCE_FIX_2026-04-26.md @@ -4,27 +4,38 @@ ## 1. 背景 -网站启动后首次打开页面约需三分钟才出现可用界面。已确认 Vite dev server 本身可在数秒内 ready,因此本次不继续扩大 `api-server` 冷编译等待窗口,而是收口浏览器首屏可见链路。 +网站启动后首次打开页面约需三分钟才出现可用界面。已确认 Vite dev server 本身可在数秒内 ready,浏览器 Network 面板中主要等待项集中在 `.tsx` 模块请求,因此本次不继续扩大 `api-server` 冷编译等待窗口,而是收口浏览器首屏 `.tsx` 冷转译与默认路由依赖图。 ## 2. 现象与根因 -本次排查发现两个会放大首屏等待的前端问题: +本次排查发现三个会放大首屏等待的前端问题: -1. `RouteImageReadyGate` 会先挂载真实业务页面但把整页 `visibility: hidden`,扫描路由 DOM 中所有 `` 和 CSS 图片,等全部图片 settled 后才显示页面。平台首页和运行时页面会渲染作品封面、角色图、图标和生成资源,任何慢图片或后端图片代理等待都会把整页可见时间拖长。 -2. Vite dev server 监听范围过宽,日志中可见 `docs/`、`scripts/`、`server-rs/` 和测试文件变更都会触发 `page reload`。后端编译、文档更新或测试文件保存会让浏览器反复全量重载,叠加首屏图片门控后表现为“首次加载一直等”。 +1. 默认路由进入 `AuthenticatedApp -> App -> RpgRuntimeShell -> PlatformEntryFlowShellImpl`,首屏虽然只显示平台首页,但入口文件静态导入了创作中心、拼图 Agent、拼图结果页、拼图运行态等非首屏阶段组件。Vite dev 首次访问时需要逐个请求并转译这些 `.tsx`,表现为浏览器长时间卡在加载 `.tsx`。 +2. `RouteImageReadyGate` 会先挂载真实业务页面但把整页 `visibility: hidden`,扫描路由 DOM 中所有 `` 和 CSS 图片,等全部图片 settled 后才显示页面。图片不是本轮确认到的主等待项,但会放大 `.tsx` 冷转译后的可见延迟。 +3. Vite dev server 监听范围过宽,日志中可见 `docs/`、`scripts/`、`server-rs/` 和测试文件变更都会触发 `page reload`。后端编译、文档更新或测试文件保存会让浏览器反复全量重载,叠加 `.tsx` 冷转译后表现为“首次加载一直等”。 ## 3. 修复口径 -### 3.1 首屏图片门控 +### 3.1 首屏 `.tsx` 冷转译 -首屏门控从“等待所有图片加载完成”改为“短暂稳态等待后放行”: +默认首页入口先做低风险依赖图收敛: + +- `App`、运行时阶段路由、面板路由避免从 barrel 文件导入,改为直连具体实现文件或类型文件。 +- `PlatformEntryFlowShellImpl` 将拼图 Agent、拼图结果页、拼图详情页、拼图运行态、创作货架等非默认首屏组件改为 `lazy`。 +- 平台首页 Tab 保留已访问页面的挂载状态,但首访只挂载当前 Tab,避免隐藏的创作页提前触发创作中心等懒加载模块。 +- RPG 运行态画布和 overlay host 只在已经进入 RPG 世界后挂载,平台首页不再同步拉取运行态画布链路。 +- 平台首页资料服务直连 `rpgProfileClient`,避免经过 `services/rpg-entry/index.ts` 把同域其它 client 一并纳入冷转译链路。 + +### 3.2 首屏图片门控 + +图片门控从“等待所有图片加载完成”改为“短暂稳态等待后放行”: - 页面仍先真实挂载,保留极短等待窗口,避免首帧布局剧烈闪动。 - 达到最大阻塞时间后必须显示页面,慢图片由浏览器渐进加载,不再隐藏整页。 - 页面已经显示后,不再因为新增图片或图片地址变化重新隐藏页面。 - 图片预加载继续保留,用于提前触发浏览器缓存,但不得成为首屏可见的硬阻塞。 -### 3.2 Vite 监听范围 +### 3.3 Vite 监听范围 Vite dev server 只对前端真实运行入口保持热更新敏感: @@ -34,8 +45,9 @@ Vite dev server 只对前端真实运行入口保持热更新敏感: ## 4. 验收标准 -1. Vite ready 后,默认站点首屏不再等待所有图片完成才显示。 -2. 慢图片、失败图片或生成资源代理慢时,页面主体仍能先显示并保持可操作。 -3. 修改 `docs/`、`server-rs/`、`scripts/` 或测试文件时,不再触发前端页面 reload。 -4. `RouteImageReadyGate` 工具测试覆盖慢图片仍会放行首屏的行为。 -5. 修改中文文件后运行编码检查,确保没有破坏 UTF-8 文本。 +1. Vite ready 后,默认站点首屏不再一次性转译明显非首屏的拼图/玩法结果/运行态组件。 +2. 默认首页冷加载 `.tsx` 请求数量下降,创作、拼图、运行态等阶段在用户进入时再加载对应 chunk。 +3. 慢图片、失败图片或生成资源代理慢时,页面主体仍能先显示并保持可操作。 +4. 修改 `docs/`、`server-rs/`、`scripts/` 或测试文件时,不再触发前端页面 reload。 +5. `RouteImageReadyGate` 工具测试覆盖慢图片仍会放行首屏的行为。 +6. 修改中文文件后运行编码检查,确保没有破坏 UTF-8 文本。 diff --git a/src/App.tsx b/src/App.tsx index dc7ec6de..89a89463 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ -import { RpgRuntimeShell } from './components/rpg-runtime-shell'; -import { useRpgRuntimeSession } from './hooks/rpg-session'; +import { RpgRuntimeShell } from './components/rpg-runtime-shell/RpgRuntimeShell'; +import { useRpgRuntimeSession } from './hooks/rpg-session/useRpgRuntimeSession'; export default function App() { const gameShellProps = useRpgRuntimeSession(); diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 1fd9925b..1553cd4d 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -60,7 +60,7 @@ import { createMiniGameDraftGenerationState, type MiniGameDraftGenerationState, } from '../../services/miniGameDraftGenerationProgress'; -import { getPlatformProfileDashboard } from '../../services/platform-entry'; +import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient'; import { createPuzzleAgentSession, executePuzzleAgentAction, @@ -81,15 +81,12 @@ import { deletePuzzleWork, listPuzzleWorks } from '../../services/puzzle-works'; import { isSamePuzzlePublicWorkCode } from '../../services/publicWorkCode'; import { deleteRpgCreationAgentSession } from '../../services/rpg-creation'; import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter'; -import { deleteRpgEntryWorldProfile } from '../../services/rpg-entry'; -import { getRpgEntryWorldGalleryDetailByCode } from '../../services/rpg-entry/rpgEntryLibraryClient'; +import { + deleteRpgEntryWorldProfile, + getRpgEntryWorldGalleryDetailByCode, +} from '../../services/rpg-entry/rpgEntryLibraryClient'; import type { CustomWorldProfile } from '../../types'; import { useAuthUi } from '../auth/AuthUiContext'; -import { CustomWorldCreationHub } from '../custom-world-home/CustomWorldCreationHub'; -import { PuzzleAgentWorkspace } from '../puzzle-agent/PuzzleAgentWorkspace'; -import { PuzzleGalleryDetailView } from '../puzzle-gallery/PuzzleGalleryDetailView'; -import { PuzzleResultView } from '../puzzle-result/PuzzleResultView'; -import { PuzzleRuntimeShell } from '../puzzle-runtime/PuzzleRuntimeShell'; import { useRpgCreationAgentOperationPolling } from '../rpg-entry/useRpgCreationAgentOperationPolling'; import { useRpgCreationEnterWorld } from '../rpg-entry/useRpgCreationEnterWorld'; import { useRpgCreationResultAutosave } from '../rpg-entry/useRpgCreationResultAutosave'; @@ -332,6 +329,41 @@ const BigFishRuntimeShell = lazy(async () => { }; }); +const CustomWorldCreationHub = lazy(async () => { + const module = await import('../custom-world-home/CustomWorldCreationHub'); + return { + default: module.CustomWorldCreationHub, + }; +}); + +const PuzzleAgentWorkspace = lazy(async () => { + const module = await import('../puzzle-agent/PuzzleAgentWorkspace'); + return { + default: module.PuzzleAgentWorkspace, + }; +}); + +const PuzzleResultView = lazy(async () => { + const module = await import('../puzzle-result/PuzzleResultView'); + return { + default: module.PuzzleResultView, + }; +}); + +const PuzzleGalleryDetailView = lazy(async () => { + const module = await import('../puzzle-gallery/PuzzleGalleryDetailView'); + return { + default: module.PuzzleGalleryDetailView, + }; +}); + +const PuzzleRuntimeShell = lazy(async () => { + const module = await import('../puzzle-runtime/PuzzleRuntimeShell'); + return { + default: module.PuzzleRuntimeShell, + }; +}); + function LazyPanelFallback({ label }: { label: string }) { return (
@@ -1647,97 +1679,99 @@ export function PlatformEntryFlowShellImpl({ ]); const creationHubContent = ( - { - platformBootstrap.setPlatformError(null); - setBigFishError(null); - setPuzzleError(null); - void platformBootstrap.refreshCustomWorldWorks().catch((error) => { - platformBootstrap.setPlatformError( - resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'), - ); - }); - void refreshBigFishShelf(); - void refreshPuzzleShelf(); - }} - createError={ - sessionController.creationTypeError ?? bigFishError ?? puzzleError - } - createBusy={ - sessionController.isCreatingAgentSession || - isBigFishBusy || - isPuzzleBusy - } - onCreateType={handleCreationHubCreateType} - onOpenDraft={(item) => { - runProtectedAction(() => { - void detailNavigation.handleOpenCreationWork(item); - }); - }} - onEnterPublished={(profileId) => { - runProtectedAction(() => { - const matchedWork = creationHubItems.find( - (entry) => entry.profileId === profileId, - ); - if (!matchedWork) { - return; - } - void detailNavigation.handleOpenCreationWork(matchedWork); - }); - }} - onDeletePublished={(item) => { - handleDeletePublishedWork(item); - }} - deletingWorkId={deletingCreationWorkId} - onExperienceRpg={(item) => { - handleExperienceRpgWork(item); - }} - rpgLibraryEntries={platformBootstrap.savedCustomWorldEntries} - bigFishItems={bigFishWorks} - onOpenBigFishDetail={(item) => { - runProtectedAction(() => { - void openBigFishDraft(item); - }); - }} - onExperienceBigFish={(item) => { - runProtectedAction(() => { - void startBigFishRunFromWork(item); - }); - }} - onDeleteBigFish={(item) => { - handleDeleteBigFishWork(item); - }} - puzzleItems={puzzleWorks} - onOpenPuzzleDetail={(item) => { - runProtectedAction(() => { - void openPuzzleDraft(item); - }); - }} - onExperiencePuzzle={(profileId) => { - runProtectedAction(() => { - void startPuzzleRunFromProfile(profileId); - }); - }} - onDeletePuzzle={(item) => { - handleDeletePuzzleWork(item); - }} - /> + }> + { + platformBootstrap.setPlatformError(null); + setBigFishError(null); + setPuzzleError(null); + void platformBootstrap.refreshCustomWorldWorks().catch((error) => { + platformBootstrap.setPlatformError( + resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'), + ); + }); + void refreshBigFishShelf(); + void refreshPuzzleShelf(); + }} + createError={ + sessionController.creationTypeError ?? bigFishError ?? puzzleError + } + createBusy={ + sessionController.isCreatingAgentSession || + isBigFishBusy || + isPuzzleBusy + } + onCreateType={handleCreationHubCreateType} + onOpenDraft={(item) => { + runProtectedAction(() => { + void detailNavigation.handleOpenCreationWork(item); + }); + }} + onEnterPublished={(profileId) => { + runProtectedAction(() => { + const matchedWork = creationHubItems.find( + (entry) => entry.profileId === profileId, + ); + if (!matchedWork) { + return; + } + void detailNavigation.handleOpenCreationWork(matchedWork); + }); + }} + onDeletePublished={(item) => { + handleDeletePublishedWork(item); + }} + deletingWorkId={deletingCreationWorkId} + onExperienceRpg={(item) => { + handleExperienceRpgWork(item); + }} + rpgLibraryEntries={platformBootstrap.savedCustomWorldEntries} + bigFishItems={bigFishWorks} + onOpenBigFishDetail={(item) => { + runProtectedAction(() => { + void openBigFishDraft(item); + }); + }} + onExperienceBigFish={(item) => { + runProtectedAction(() => { + void startBigFishRunFromWork(item); + }); + }} + onDeleteBigFish={(item) => { + handleDeleteBigFishWork(item); + }} + puzzleItems={puzzleWorks} + onOpenPuzzleDetail={(item) => { + runProtectedAction(() => { + void openPuzzleDraft(item); + }); + }} + onExperiencePuzzle={(profileId) => { + runProtectedAction(() => { + void startPuzzleRunFromProfile(profileId); + }); + }} + onDeletePuzzle={(item) => { + handleDeletePuzzleWork(item); + }} + /> + ); return ( @@ -2074,21 +2108,23 @@ export function PlatformEntryFlowShellImpl({ exit={{ opacity: 0, y: -12 }} className="flex h-full min-h-0 flex-col" > - { - void submitPuzzleMessage(payload); - }} - onExecuteAction={(payload) => { - void executePuzzleAction(payload); - }} - /> + }> + { + void submitPuzzleMessage(payload); + }} + onExecuteAction={(payload) => { + void executePuzzleAction(payload); + }} + /> + )} @@ -2145,18 +2181,20 @@ export function PlatformEntryFlowShellImpl({ exit={{ opacity: 0, y: -12 }} className="flex h-full min-h-0 flex-col" > - { - setSelectionStage('puzzle-agent-workspace'); - }} - onExecuteAction={(payload) => { - void executePuzzleAction(payload); - }} - /> + }> + { + setSelectionStage('puzzle-agent-workspace'); + }} + onExecuteAction={(payload) => { + void executePuzzleAction(payload); + }} + /> + )} @@ -2168,31 +2206,33 @@ export function PlatformEntryFlowShellImpl({ exit={{ opacity: 0, y: -12 }} className="flex h-full min-h-0 flex-col" > - { - platformBootstrap.setPlatformTab( - puzzleDetailReturnTarget?.tab ?? 'home', - ); - setPuzzleDetailReturnTarget(null); - setSelectionStage('platform'); - }} - onEdit={ - selectedPuzzleDetail.ownerUserId === authUi?.user?.id && - Boolean(selectedPuzzleDetail.sourceSessionId?.trim()) - ? () => { - runProtectedAction(() => { - void openPuzzleDraft(selectedPuzzleDetail); - }); - } - : null - } - onStartGame={() => { - void startPuzzleRunFromProfile(selectedPuzzleDetail.profileId); - }} - /> + }> + { + platformBootstrap.setPlatformTab( + puzzleDetailReturnTarget?.tab ?? 'home', + ); + setPuzzleDetailReturnTarget(null); + setSelectionStage('platform'); + }} + onEdit={ + selectedPuzzleDetail.ownerUserId === authUi?.user?.id && + Boolean(selectedPuzzleDetail.sourceSessionId?.trim()) + ? () => { + runProtectedAction(() => { + void openPuzzleDraft(selectedPuzzleDetail); + }); + } + : null + } + onStartGame={() => { + void startPuzzleRunFromProfile(selectedPuzzleDetail.profileId); + }} + /> + )} @@ -2204,23 +2244,25 @@ export function PlatformEntryFlowShellImpl({ exit={{ opacity: 0 }} className="fixed inset-0 z-[100]" > - { - setSelectionStage('puzzle-gallery-detail'); - }} - onSwapPieces={(payload) => { - void swapPuzzlePiecesInRun(payload); - }} - onDragPiece={(payload) => { - void dragPuzzlePiece(payload); - }} - onAdvanceNextLevel={() => { - void advancePuzzleLevel(); - }} - /> + }> + { + setSelectionStage('puzzle-gallery-detail'); + }} + onSwapPieces={(payload) => { + void swapPuzzlePiecesInRun(payload); + }} + onDragPiece={(payload) => { + void dragPuzzlePiece(payload); + }} + onAdvanceNextLevel={() => { + void advancePuzzleLevel(); + }} + /> + {isPuzzleNextLevelGenerating ? (
diff --git a/src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx b/src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx index ba3da019..a606b42f 100644 --- a/src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx +++ b/src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx @@ -23,8 +23,8 @@ import { EDITOR_ITEM_CATALOG_API_PATH } from '../../editor/shared/editorApiClien import { fetchJson } from '../../editor/shared/jsonClient'; import { useCombatFlow } from '../../hooks/useCombatFlow'; import { useNpcInteractionFlow } from '../../hooks/useNpcInteractionFlow'; -import { useRpgRuntimeStory } from '../../hooks/rpg-runtime-story'; -import { useRpgSessionBootstrap } from '../../hooks/rpg-session'; +import { useRpgRuntimeStory } from '../../hooks/rpg-runtime-story/useRpgRuntimeStory'; +import { useRpgSessionBootstrap } from '../../hooks/rpg-session/useRpgSessionBootstrap'; import { buildSkillActionPrompt } from '../../prompts/customWorldEntityActionPrompts'; import type { CustomWorldSceneImageResult } from '../../services/aiTypes'; import { resolveCustomWorldCampScene } from '../../services/customWorldCamp'; diff --git a/src/components/rpg-entry/RpgEntryFlowShell.tsx b/src/components/rpg-entry/RpgEntryFlowShell.tsx index bb4b82a1..0d22f677 100644 --- a/src/components/rpg-entry/RpgEntryFlowShell.tsx +++ b/src/components/rpg-entry/RpgEntryFlowShell.tsx @@ -1,4 +1,4 @@ -import { PlatformEntryFlowShell } from '../platform-entry'; +import { PlatformEntryFlowShell } from '../platform-entry/PlatformEntryFlowShell'; import type { RpgEntryFlowShellProps } from './rpgEntryTypes'; import type { SelectionStage } from './rpgEntryTypes'; diff --git a/src/components/rpg-entry/RpgEntryHomeView.tsx b/src/components/rpg-entry/RpgEntryHomeView.tsx index b55a0e50..d8335911 100644 --- a/src/components/rpg-entry/RpgEntryHomeView.tsx +++ b/src/components/rpg-entry/RpgEntryHomeView.tsx @@ -1110,6 +1110,9 @@ export function RpgEntryHomeView({ const [selectedCategoryTag, setSelectedCategoryTag] = useState( null, ); + const [visitedTabs, setVisitedTabs] = useState>( + () => new Set([activeTab]), + ); const isAuthenticated = Boolean(authUi?.user); const isDesktopLayout = usePlatformDesktopLayout(); const featuredShelf = useMemo( @@ -1159,6 +1162,18 @@ export function RpgEntryHomeView({ } }, [activeTab, onTabChange, visibleTabs]); + useEffect(() => { + setVisitedTabs((currentTabs) => { + if (currentTabs.has(activeTab)) { + return currentTabs; + } + + const nextTabs = new Set(currentTabs); + nextTabs.add(activeTab); + return nextTabs; + }); + }, [activeTab]); + useEffect(() => { if (categoryGroups.length === 0) { setSelectedCategoryTag(null); @@ -1950,11 +1965,15 @@ export function RpgEntryHomeView({ } satisfies Record; const tabPanels = PLATFORM_HOME_TABS.filter((tab) => visibleTabs.includes(tab), - ).map((tab) => ( - - {tabContentById[tab]} - - )); + ).map((tab) => { + const shouldMountPanel = tab === activeTab || visitedTabs.has(tab); + + return ( + + {shouldMountPanel ? tabContentById[tab] : null} + + ); + }); if (!isDesktopLayout) { return ( diff --git a/src/components/rpg-runtime-panels/RpgRuntimePanelRouter.tsx b/src/components/rpg-runtime-panels/RpgRuntimePanelRouter.tsx index 451b9824..7ffb3615 100644 --- a/src/components/rpg-runtime-panels/RpgRuntimePanelRouter.tsx +++ b/src/components/rpg-runtime-panels/RpgRuntimePanelRouter.tsx @@ -1,6 +1,6 @@ import { lazy, Suspense } from 'react'; -import type { BottomTab } from '../../hooks/rpg-session'; +import type { BottomTab } from '../../hooks/rpg-session/rpgSessionTypes'; import type { BattleRewardUi, CharacterChatUi, @@ -18,10 +18,8 @@ import type { import { getNineSliceStyle, TAB_ICONS, UI_CHROME } from '../../uiAssets'; import type { GameCanvasEntitySelection } from '../GameCanvas'; import { PixelIcon } from '../PixelIcon'; -import { - PanelLoadingFallback, - type RpgAdventureStatistics, -} from '../rpg-runtime-shell'; +import { PanelLoadingFallback } from '../rpg-runtime-shell/rpgRuntimeLoaders'; +import type { RpgAdventureStatistics } from '../rpg-runtime-shell/types'; const RpgAdventurePanel = lazy(async () => { const module = await import('./RpgAdventurePanel'); diff --git a/src/components/rpg-runtime-shell/RpgRuntimeShell.tsx b/src/components/rpg-runtime-shell/RpgRuntimeShell.tsx index 283d7f8d..f391d188 100644 --- a/src/components/rpg-runtime-shell/RpgRuntimeShell.tsx +++ b/src/components/rpg-runtime-shell/RpgRuntimeShell.tsx @@ -7,11 +7,17 @@ import { } from '../../routing/appPageRoutes'; import { UI_CHROME } from '../../uiAssets'; import { useAuthUi } from '../auth/AuthUiContext'; -import { RpgRuntimeCanvasStage } from './RpgRuntimeCanvasStage'; import { RpgRuntimeStageRouter } from './RpgRuntimeStageRouter'; import type { RpgRuntimeShellProps as RpgRuntimeShellComponentProps } from './types'; import { useRpgRuntimeShellViewModel } from './useRpgRuntimeShellViewModel'; +const RpgRuntimeCanvasStage = lazy(async () => { + const module = await import('./RpgRuntimeCanvasStage'); + return { + default: module.RpgRuntimeCanvasStage, + }; +}); + const RpgRuntimeOverlayHost = lazy(async () => { const module = await import('./RpgRuntimeOverlayHost'); return { @@ -152,20 +158,22 @@ export function RpgRuntimeShell({ backgroundRepeat: isPlatformShell ? undefined : 'repeat', }} > - - - + {gameState.worldType ? ( + + + + ) : null} {visibleGameState.playerCharacter && (
- - - + {gameState.worldType ? ( + + + + ) : null}
); } diff --git a/src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx b/src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx index 94c5f659..1b580d31 100644 --- a/src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx +++ b/src/components/rpg-runtime-shell/RpgRuntimeStageRouter.tsx @@ -1,7 +1,7 @@ import { AnimatePresence, motion } from 'motion/react'; import { lazy, Suspense } from 'react'; -import type { BottomTab } from '../../hooks/rpg-session'; +import type { BottomTab } from '../../hooks/rpg-session/rpgSessionTypes'; import type { BattleRewardUi, CharacterChatUi, @@ -20,25 +20,25 @@ import type { } from '../../types'; import { UI_CHROME } from '../../uiAssets'; import type { GameCanvasEntitySelection } from '../GameCanvas'; -import type { SelectionStage } from '../platform-entry'; +import type { SelectionStage } from '../platform-entry/platformEntryTypes'; import type { RpgAdventureStatistics } from './types'; const RpgEntryCharacterSelectView = lazy(async () => { - const module = await import('../rpg-entry'); + const module = await import('../rpg-entry/RpgEntryCharacterSelectView'); return { default: module.RpgEntryCharacterSelectView, }; }); const PlatformEntryFlowShell = lazy(async () => { - const module = await import('../platform-entry'); + const module = await import('../platform-entry/PlatformEntryFlowShell'); return { default: module.PlatformEntryFlowShell, }; }); const RpgRuntimePanelRouter = lazy(async () => { - const module = await import('../rpg-runtime-panels'); + const module = await import('../rpg-runtime-panels/RpgRuntimePanelRouter'); return { default: module.RpgRuntimePanelRouter, }; diff --git a/src/components/rpg-runtime-shell/types.ts b/src/components/rpg-runtime-shell/types.ts index 58be638d..4d276ca8 100644 --- a/src/components/rpg-runtime-shell/types.ts +++ b/src/components/rpg-runtime-shell/types.ts @@ -1,4 +1,4 @@ -import type { BottomTab } from '../../hooks/rpg-session'; +import type { BottomTab } from '../../hooks/rpg-session/rpgSessionTypes'; import type { BattleRewardUi, CharacterChatUi, diff --git a/src/components/rpg-runtime-shell/useRpgRuntimeOverlayState.ts b/src/components/rpg-runtime-shell/useRpgRuntimeOverlayState.ts index 7e200792..3ad7d836 100644 --- a/src/components/rpg-runtime-shell/useRpgRuntimeOverlayState.ts +++ b/src/components/rpg-runtime-shell/useRpgRuntimeOverlayState.ts @@ -7,7 +7,7 @@ import { } from '../../routing/appPageRoutes'; import type { GameState } from '../../types'; import type { GameCanvasEntitySelection } from '../GameCanvas'; -import type { SelectionStage } from '../platform-entry'; +import type { SelectionStage } from '../platform-entry/platformEntryTypes'; type OverlayPanel = 'character' | 'inventory' | null; diff --git a/src/hooks/rpg-session/useRpgRuntimeSession.ts b/src/hooks/rpg-session/useRpgRuntimeSession.ts index 72cba22f..847e2adc 100644 --- a/src/hooks/rpg-session/useRpgRuntimeSession.ts +++ b/src/hooks/rpg-session/useRpgRuntimeSession.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { DEFAULT_MUSIC_VOLUME } from '../../../packages/shared/src/contracts/runtime'; import { useAuthUi } from '../../components/auth/AuthUiContext'; -import type { RpgRuntimeShellProps } from '../../components/rpg-runtime-shell'; +import type { RpgRuntimeShellProps } from '../../components/rpg-runtime-shell/types'; import { activateRosterCompanion, benchActiveCompanion } from '../../data/companionRoster'; import { syncGameStatePlayTime } from '../../data/runtimeStats'; import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes'; diff --git a/src/services/platform-entry/index.ts b/src/services/platform-entry/index.ts index be02d127..6283a613 100644 --- a/src/services/platform-entry/index.ts +++ b/src/services/platform-entry/index.ts @@ -1,5 +1 @@ -/** - * 平台入口服务通用封装。 - * 先复用既有资料看板读取逻辑,但对 `platform-entry` 暴露通用命名。 - */ -export { getRpgProfileDashboard as getPlatformProfileDashboard } from '../rpg-entry'; +export { getPlatformProfileDashboard } from './platformProfileClient'; diff --git a/src/services/platform-entry/platformProfileClient.ts b/src/services/platform-entry/platformProfileClient.ts new file mode 100644 index 00000000..84a83a24 --- /dev/null +++ b/src/services/platform-entry/platformProfileClient.ts @@ -0,0 +1,5 @@ +/** + * 平台首页资料读取入口。 + * 直连 RPG profile client,避免默认首页首访经过服务桶入口触发额外模块转译。 + */ +export { getRpgProfileDashboard as getPlatformProfileDashboard } from '../rpg-entry/rpgProfileClient';