diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css
index 8017ff63c..7ea1cc2ab 100644
--- a/apps/ai-game-creator-shell/src/styles.css
+++ b/apps/ai-game-creator-shell/src/styles.css
@@ -5634,6 +5634,21 @@ iframe.preview-frame {
pointer-events: none;
}
+.game-resource-book-loading {
+ position: absolute;
+ inset: 0;
+ z-index: 40;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ background: rgb(255 253 250 / 78%);
+ color: #a08073;
+ font-size: 13px;
+ pointer-events: none;
+}
+
.game-resource-book-manager[data-book-motion='running']
.game-resource-book-scene-world {
opacity: 0;
diff --git a/apps/ai-game-creator-shell/src/view/project-development/index.tsx b/apps/ai-game-creator-shell/src/view/project-development/index.tsx
index ed658b681..d24b0fc2b 100644
--- a/apps/ai-game-creator-shell/src/view/project-development/index.tsx
+++ b/apps/ai-game-creator-shell/src/view/project-development/index.tsx
@@ -10,6 +10,7 @@ import {
Image,
Info,
ListFilter,
+ Loader2,
Maximize2,
Minus,
Music2,
@@ -98,6 +99,7 @@ import {
} from './resourceBookLayout';
import {
initialResourceBookState,
+ resourceBookIsLoading,
resourceBookReducer,
resourceBookSceneCategory,
resourceBookShowsChildCards,
@@ -2189,6 +2191,36 @@ export default function ProjectDevelopmentView({
sortMode,
visibleCategoryOrder,
]);
+ const resourceBookOverviewReady = useMemo(
+ () =>
+ visibleCategoryOrder.every((category) => {
+ const rect = resourceBookOverviewRects.get(category);
+ return Boolean(rect && rect.width > 0 && rect.height > 0);
+ }),
+ [resourceBookOverviewRects, visibleCategoryOrder],
+ );
+ const resourceBookSettledPreviewCount = useMemo(() => {
+ let settled = 0;
+ for (const resource of visibleResources) {
+ const identity = resourceCardPreviews.identityByResourceId.get(
+ resource.id,
+ );
+ if (!identity) continue;
+ const status = resourceCardPreviews.previews.get(identity)?.status;
+ if (status === 'loaded' || status === 'failed') settled += 1;
+ }
+ return settled;
+ }, [
+ resourceCardPreviews.identityByResourceId,
+ resourceCardPreviews.previews,
+ visibleResources,
+ ]);
+ const resourceBookLoading = resourceBookIsLoading({
+ resourceCount: visibleResources.length,
+ overviewReady: resourceBookOverviewReady,
+ cardsReady: sortMode !== 'dependency' || resourceGraphInitializationReady,
+ settledPreviewCount: resourceBookSettledPreviewCount,
+ });
const setResourceCanvasViewport = useCallback(
(
category: ResourceCategory,
@@ -5590,6 +5622,20 @@ export default function ProjectDevelopmentView({
onWheel={handleResourceBookWheel}
/>