fix(space): 배경 asset fallback 경로와 scene alias 해석 보강

맥락:
- /space 에서 forest 배경이 remote manifest asset 대신 기본 이미지로 조용히 fallback 될 수 있었다.
- scene key alias 와 manifest 실패 상태가 코드상 드러나지 않아 원인 추적이 어려웠다.

변경사항:
- media scene asset key 를 alias-aware 하게 정규화하고 asset source(fallback|remote) 메타를 추가했다.
- useMediaCatalog 가 remote manifest 실패와 fallback 사용 여부를 노출하도록 보강했다.
- SpaceWorkspaceWidget 에서 manifest 실패와 scene fallback 사용을 진단 로그/상태 메시지로 남기도록 정리했다.
- docs/work.md, docs/90_current_state.md, docs/session_brief.md 를 이번 작업 기준으로 갱신했다.

검증:
- npx tsc --noEmit

세션-상태: /space 배경 asset lookup 과 manifest fallback 진단을 보강했다.
세션-다음: forest/green-forest manifest 변형을 실제 브라우저에서 QA 한다.
세션-리스크: alias 목록 밖의 legacy scene id 는 추가 정규화가 필요할 수 있다.
This commit is contained in:
2026-03-11 13:35:44 +09:00
parent 9811134d8a
commit 4717bb3a1a
7 changed files with 163 additions and 123 deletions

View File

@@ -208,8 +208,16 @@ export const SpaceWorkspaceWidget = () => {
});
const queuedFocusStatusMessageRef = useRef<string | null>(null);
const lastSoundPlaybackErrorRef = useRef<string | null>(null);
const lastMediaManifestErrorRef = useRef<string | null>(null);
const lastFallbackSceneDiagnosticRef = useRef<string | null>(null);
const didHydrateServerPreferencesRef = useRef(false);
const { sceneAssetMap, soundAssetMap } = useMediaCatalog();
const {
sceneAssetMap,
soundAssetMap,
error: mediaCatalogError,
usedFallbackManifest,
hasResolvedManifest,
} = useMediaCatalog();
const {
selectedPresetId,
@@ -778,6 +786,49 @@ export const SpaceWorkspaceWidget = () => {
});
}, [pushStatusLine, soundPlaybackError]);
useEffect(() => {
if (!mediaCatalogError) {
lastMediaManifestErrorRef.current = null;
return;
}
if (mediaCatalogError === lastMediaManifestErrorRef.current) {
return;
}
lastMediaManifestErrorRef.current = mediaCatalogError;
console.error('[media] Failed to load remote media manifest.', {
error: mediaCatalogError,
sceneId: selectedScene.id,
});
pushStatusLine({
message: mediaCatalogError,
});
}, [mediaCatalogError, pushStatusLine, selectedScene.id]);
useEffect(() => {
if (!hasResolvedManifest || usedFallbackManifest) {
return;
}
const isUsingFallbackSceneAsset = !selectedSceneAsset || selectedSceneAsset.source === 'fallback';
if (!isUsingFallbackSceneAsset) {
lastFallbackSceneDiagnosticRef.current = null;
return;
}
if (lastFallbackSceneDiagnosticRef.current === selectedScene.id) {
return;
}
lastFallbackSceneDiagnosticRef.current = selectedScene.id;
console.warn('[space] Selected scene is using fallback asset data.', {
sceneId: selectedScene.id,
asset: selectedSceneAsset ?? null,
});
}, [hasResolvedManifest, selectedScene.id, selectedSceneAsset, usedFallbackManifest]);
return (
<div className="relative h-dvh overflow-hidden text-white">
<div