diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css
index e13a7e671..4d5bb02ae 100644
--- a/apps/ai-game-creator-shell/src/styles.css
+++ b/apps/ai-game-creator-shell/src/styles.css
@@ -5192,22 +5192,23 @@ iframe.preview-frame {
transition: none;
}
-.game-resource-book-scene--entering .game-resource-book-scene-world {
- transition: transform 420ms cubic-bezier(0.2, 0.78, 0.2, 1);
+.game-resource-book-transition-layer {
+ position: absolute;
+ inset: 0;
+ z-index: 35;
+ overflow: hidden;
+ pointer-events: none;
}
-.game-resource-book-scene--returning-main .game-resource-book-scene-world {
- transition: none;
+.game-resource-book-manager[data-book-motion='running']
+ .game-resource-book-scene-world {
+ opacity: 0;
}
.game-resource-book-scene--child {
pointer-events: auto;
}
-.game-resource-book-scene--child.is-measuring {
- pointer-events: none;
-}
-
.game-resource-book-zoom {
position: absolute;
right: 14px;
@@ -5262,8 +5263,8 @@ iframe.preview-frame {
margin-left: 2px;
}
-.game-resource-book-scene.is-measuring {
- opacity: 0;
+.game-resource-book-scene--main.is-measuring {
+ visibility: hidden;
}
.game-resource-book-scene::before {
@@ -5276,12 +5277,10 @@ iframe.preview-frame {
content: '';
opacity: 0;
pointer-events: none;
- transition: opacity 180ms ease;
+ transition: none;
}
-.game-resource-book-scene--child::before,
-.game-resource-book-scene--returning::before,
-.game-resource-book-scene--entering::before {
+.game-resource-book-scene--child::before {
opacity: 1;
}
@@ -5295,13 +5294,7 @@ iframe.preview-frame {
box-shadow: 0 8px 18px rgb(112 70 52 / 8%);
pointer-events: none;
transform: translateY(0);
- transition:
- left 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- top 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- width 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- transform 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- translate 180ms ease,
- opacity 180ms ease;
+ transition: none;
}
.game-resource-book-scene-titlebar.is-active {
@@ -5309,13 +5302,6 @@ iframe.preview-frame {
pointer-events: auto;
}
-/* 子画布中的标题栏是固定 UI。拖动画布时 viewport 会逐帧更新,不能让
- * 标题栏的逆变换继续走转场 transition,否则它会滞后跟随画布。 */
-.game-resource-book-scene--child.game-resource-book-scene--idle
- .game-resource-book-scene-titlebar.is-active {
- transition: none;
-}
-
.game-resource-book-scene-titlebar .game-resource-book-titlebar {
min-height: 42px;
border-bottom: 0;
@@ -5327,86 +5313,30 @@ iframe.preview-frame {
z-index: 25;
pointer-events: none;
translate: 0 0;
- transition:
- opacity 180ms ease,
- transform 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- translate 180ms ease;
+ transition: none;
}
-.game-resource-book-manager:has(
+.game-resource-book-manager--main:has(
.game-resource-book-thumbnail[data-resource-book-category='code']:hover,
.game-resource-book-thumbnail[data-resource-book-category='code']:focus-visible
)
.game-resource-book-scene-titlebar[data-resource-book-category='code'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='code']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='code']:focus-visible
- )
- .game-resource-book-scene-card[data-resource-book-category='code'],
-.game-resource-book-manager:has(
+.game-resource-book-manager--main:has(
.game-resource-book-thumbnail[data-resource-book-category='document']:hover,
.game-resource-book-thumbnail[data-resource-book-category='document']:focus-visible
)
.game-resource-book-scene-titlebar[data-resource-book-category='document'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='document']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='document']:focus-visible
- )
- .game-resource-book-scene-card[data-resource-book-category='document'],
-.game-resource-book-manager:has(
+.game-resource-book-manager--main:has(
.game-resource-book-thumbnail[data-resource-book-category='version']:hover,
.game-resource-book-thumbnail[data-resource-book-category='version']:focus-visible
)
.game-resource-book-scene-titlebar[data-resource-book-category='version'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='version']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='version']:focus-visible
- )
- .game-resource-book-scene-card[data-resource-book-category='version'],
-.game-resource-book-manager:has(
+.game-resource-book-manager--main:has(
.game-resource-book-thumbnail[data-resource-book-category='art']:hover,
.game-resource-book-thumbnail[data-resource-book-category='art']:focus-visible
)
.game-resource-book-scene-titlebar[data-resource-book-category='art'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='art']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='art']:focus-visible
- )
- .game-resource-book-scene-card[data-resource-book-category='art'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='audio']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='audio']:focus-visible
- )
- .game-resource-book-scene-titlebar[data-resource-book-category='audio'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='audio']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='audio']:focus-visible
- )
- .game-resource-book-scene-card[data-resource-book-category='audio'] {
- translate: 0 -3px;
-}
-
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='code']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='code']:focus-visible
- )
- .game-resource-book-scene-titlebar[data-resource-book-category='code'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='document']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='document']:focus-visible
- )
- .game-resource-book-scene-titlebar[data-resource-book-category='document'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='version']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='version']:focus-visible
- )
- .game-resource-book-scene-titlebar[data-resource-book-category='version'],
-.game-resource-book-manager:has(
- .game-resource-book-thumbnail[data-resource-book-category='art']:hover,
- .game-resource-book-thumbnail[data-resource-book-category='art']:focus-visible
- )
- .game-resource-book-scene-titlebar[data-resource-book-category='art'],
-.game-resource-book-manager:has(
+.game-resource-book-manager--main:has(
.game-resource-book-thumbnail[data-resource-book-category='audio']:hover,
.game-resource-book-thumbnail[data-resource-book-category='audio']:focus-visible
)
@@ -5418,16 +5348,6 @@ iframe.preview-frame {
}
.game-resource-book-scene-card .game-resource-card {
- transition:
- width 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- height 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- transform 420ms cubic-bezier(0.2, 0.78, 0.2, 1),
- box-shadow 180ms ease;
-}
-
-.game-resource-book-scene--idle
- .game-resource-book-scene-card
- .game-resource-card {
transition: box-shadow 180ms ease;
}
@@ -5537,7 +5457,7 @@ iframe.preview-frame {
border-color: #cc8060;
outline: 0;
box-shadow: 0 16px 34px rgb(158 87 57 / 18%);
- transform: translateY(-3px);
+ transform: none;
}
.game-resource-book-thumbnail-titlebar {
diff --git a/apps/ai-game-creator-shell/src/view/project-development/ResourceBookTransitionLayer.tsx b/apps/ai-game-creator-shell/src/view/project-development/ResourceBookTransitionLayer.tsx
new file mode 100644
index 000000000..f5ebb6efc
--- /dev/null
+++ b/apps/ai-game-creator-shell/src/view/project-development/ResourceBookTransitionLayer.tsx
@@ -0,0 +1,9 @@
+export function ResourceBookTransitionLayer() {
+ return (
+
+ );
+}
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 448ffeb79..419954f08 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
@@ -96,13 +96,13 @@ import {
} from './resourceBookLayout';
import {
initialResourceBookState,
- resourceBookIsReturningCategory,
resourceBookReducer,
resourceBookSceneCategory,
resourceBookShowsChildCards,
resourceBookShowsOverviewCards,
type ResourceBookState,
} from './resourceBookModel';
+import { ResourceBookTransitionLayer } from './ResourceBookTransitionLayer';
import {
DEFAULT_RESOURCE_BOOK_VIEWPORT,
keepResourceBookViewportAtReadableScale,
@@ -1080,13 +1080,8 @@ function ResourceBookScene({
const rect = overviewRects.get(category);
const categoryResources = resourcesByCategory.get(category) ?? [];
const stacks = groupResourceBookStacks(categoryResources);
- const returning = resourceBookIsReturningCategory(
- resourceBookState,
- category,
- );
- const active =
- showChildCards && sceneCategory === category && !returning;
- const renderTransitionCards = showOverviewCards || returning;
+ const active = showChildCards && sceneCategory === category;
+ const renderTransitionCards = showOverviewCards;
const renderCards = cardsReady && (renderTransitionCards || active);
const titleLayout = active
? { left: 0, top: 0, width: '100%', opacity: 1 }
@@ -1870,11 +1865,17 @@ export default function ProjectDevelopmentView({
const next = new Map();
resourceBookThumbnailElementsRef.current.forEach((element, category) => {
const rect = element.getBoundingClientRect();
+ // Read layout coordinates, not the translated/scaled main canvas.
+ const main = element.closest('.game-resource-book-main');
+ const transformed = !main?.classList.contains('is-background');
+ const viewport = transformed
+ ? resourceBookMainViewportRef.current
+ : DEFAULT_RESOURCE_BOOK_VIEWPORT;
next.set(category, {
- left: rect.left - managerRect.left,
- top: rect.top - managerRect.top,
- width: rect.width,
- height: rect.height,
+ left: (rect.left - managerRect.left - viewport.x) / viewport.scale,
+ top: (rect.top - managerRect.top - viewport.y) / viewport.scale,
+ width: rect.width / viewport.scale,
+ height: rect.height / viewport.scale,
});
});
setResourceBookOverviewRects((current) => {
@@ -2403,27 +2404,76 @@ export default function ProjectDevelopmentView({
};
}, []);
- const cancelResourceBookTransition = useCallback(() => {
- resourceBookTransitionControllerRef.current.invalidate();
- }, []);
+ useLayoutEffect(() => {
+ const manager = resourceBookManagerRef.current;
+ if (resourceBookState.phase === 'idle') return;
+ if (!manager || mode !== 'resources') {
+ resourceBookTransitionControllerRef.current.settle();
+ dispatchResourceBook({
+ type: 'finish-transition',
+ token: resourceBookState.token,
+ });
+ return;
+ }
+ resourceBookTransitionControllerRef.current.play(
+ manager,
+ resourceBookState.token,
+ () => {
+ dispatchResourceBook({
+ type: 'finish-transition',
+ token: resourceBookState.token,
+ });
+ },
+ );
+ }, [resourceBookState.token, resourceBookState.phase, mode]);
- const scheduleResourceBookTransition = useCallback(
- (callback: (token: number) => void, delayMs: number, token: number) => {
- resourceBookTransitionControllerRef.current.schedule(
- callback,
- delayMs,
- token,
- );
- },
- [],
- );
+ const motionLayoutRef = useRef({
+ token: resourceBookState.token,
+ viewport: activeResourceCanvasViewport,
+ overview: resourceBookOverviewRects,
+ positions: resourcePositionById,
+ sizes: resourceCardSizeByResourceId,
+ resources: canvasResources,
+ sortMode,
+ });
+ useLayoutEffect(() => {
+ const current = {
+ token: resourceBookState.token,
+ viewport: activeResourceCanvasViewport,
+ overview: resourceBookOverviewRects,
+ positions: resourcePositionById,
+ sizes: resourceCardSizeByResourceId,
+ resources: canvasResources,
+ sortMode,
+ };
+ const previous = motionLayoutRef.current;
+ motionLayoutRef.current = current;
+ if (
+ previous.token === current.token &&
+ (previous.viewport !== current.viewport ||
+ previous.overview !== current.overview ||
+ previous.positions !== current.positions ||
+ previous.sizes !== current.sizes ||
+ previous.resources !== current.resources ||
+ previous.sortMode !== current.sortMode)
+ ) {
+ resourceBookTransitionControllerRef.current.settle();
+ }
+ }, [
+ resourceBookState.token,
+ activeResourceCanvasViewport,
+ resourceBookOverviewRects,
+ resourcePositionById,
+ resourceCardSizeByResourceId,
+ canvasResources,
+ sortMode,
+ ]);
const openResourceBookChild = useCallback(
- (category: ResourceCategory) => {
- cancelResourceBookTransition();
+ (category: ResourceCategory, fromWheel = false) => {
cancelResourceCardDrag();
cancelResourceCanvasPan();
- cancelQueuedResourceCanvasPageWheel();
+ if (!fromWheel) cancelQueuedResourceCanvasPageWheel();
if (
resourceBookState.view === 'child' &&
@@ -2433,8 +2483,9 @@ export default function ProjectDevelopmentView({
return;
}
- const transitionToken =
- resourceBookTransitionControllerRef.current.begin();
+ const transitionToken = resourceBookTransitionControllerRef.current.begin(
+ resourceBookManagerRef.current,
+ );
// Keep the paged-canvas projection's logical target synchronous with the
// reducer action. React may commit the visible page later, but wheel and
// focus handlers arriving in the same turn must calculate from the new
@@ -2482,71 +2533,18 @@ export default function ProjectDevelopmentView({
// sort/category pair. Mark that fit as settled so the paged-canvas
// layout effect does not immediately recalculate it from a shared DOM
// measurement and overwrite the stored per-category viewport.
- resourceCanvasFitKeysRef.current.add(categoryFitKey);
-
- if (resourceBookState.view === 'child') {
- // Keep the paged canvas and its outline in sync with the requested
- // destination while the shared scene is running the return leg. The
- // scene itself remains on the current child until the 320ms handoff.
- resourceCanvasPageWheelRef.current.activeCategory = category;
- setActiveResourceCategory(category);
- // The reducer keeps the current child visible during the return leg.
- scheduleResourceBookTransition(
- (transitionToken) => {
- setActiveResourceCategory(category);
- dispatchResourceBook({
- type: 'commit-return',
- token: transitionToken,
- });
- resourceBookTransitionControllerRef.current.requestFrame(() => {
- setResourceCanvasViewport(category, categoryViewport);
- dispatchResourceBook({
- type: 'commit-enter',
- token: transitionToken,
- });
- scheduleResourceBookTransition(
- (finishToken) => {
- dispatchResourceBook({
- type: 'finish-transition',
- token: finishToken,
- });
- },
- 520,
- transitionToken,
- );
- }, transitionToken);
- },
- 320,
- transitionToken,
- );
- return;
+ if (resourceBookSceneSize.width > 0 && resourceBookSceneSize.height > 0) {
+ resourceCanvasFitKeysRef.current.add(categoryFitKey);
}
setResourceCanvasViewport(category, categoryViewport);
resourceCanvasPageWheelRef.current.activeCategory = category;
setActiveResourceCategory(category);
- scheduleResourceBookTransition(
- (finishToken) => {
- dispatchResourceBook({
- type: 'finish-transition',
- token: finishToken,
- });
- },
- 520,
- transitionToken,
- );
- resourceBookTransitionControllerRef.current.requestFrame(() => {
- dispatchResourceBook({
- type: 'commit-enter',
- token: transitionToken,
- });
- }, transitionToken);
},
[
cancelQueuedResourceCanvasPageWheel,
cancelResourceCanvasPan,
cancelResourceCardDrag,
- cancelResourceBookTransition,
manifest.projectId,
projectPath,
resourceBookState,
@@ -2554,7 +2552,6 @@ export default function ProjectDevelopmentView({
resourceCardSizeByResourceId,
resourcePositionsByCategory,
setResourceCanvasViewport,
- scheduleResourceBookTransition,
sortMode,
],
);
@@ -2563,61 +2560,32 @@ export default function ProjectDevelopmentView({
if (resourceBookState.view !== 'child') {
return;
}
- cancelResourceBookTransition();
cancelResourceCardDrag();
cancelResourceCanvasPan();
cancelQueuedResourceCanvasPageWheel();
- const transitionToken = resourceBookTransitionControllerRef.current.begin();
- dispatchResourceBook({ type: 'return-to-main', token: transitionToken });
- scheduleResourceBookTransition(
- (finishToken) => {
- dispatchResourceBook({ type: 'commit-return', token: finishToken });
- scheduleResourceBookTransition(
- (idleToken) => {
- dispatchResourceBook({
- type: 'finish-transition',
- token: idleToken,
- });
- },
- 520,
- finishToken,
- );
- },
- 320,
- transitionToken,
+ const transitionToken = resourceBookTransitionControllerRef.current.begin(
+ resourceBookManagerRef.current,
);
+ dispatchResourceBook({ type: 'return-to-main', token: transitionToken });
}, [
cancelQueuedResourceCanvasPageWheel,
cancelResourceCanvasPan,
cancelResourceCardDrag,
- cancelResourceBookTransition,
resourceBookState,
- scheduleResourceBookTransition,
]);
const selectResourceCanvasPage = useCallback(
(category: ResourceCategory) => {
- if (resourceBookView === 'main') {
- openResourceBookChild(category);
- return;
- }
- cancelResourceBookTransition();
- cancelResourceCardDrag();
- cancelResourceCanvasPan();
- cancelQueuedResourceCanvasPageWheel();
- resourceCanvasPageWheelRef.current.activeCategory = category;
- setActiveResourceCategory(category);
+ openResourceBookChild(category);
},
- [
- cancelQueuedResourceCanvasPageWheel,
- cancelResourceBookTransition,
- cancelResourceCanvasPan,
- cancelResourceCardDrag,
- openResourceBookChild,
- resourceBookView,
- ],
+ [openResourceBookChild],
);
+ const openResourceBookChildRef = useRef(openResourceBookChild);
+ useLayoutEffect(() => {
+ openResourceBookChildRef.current = openResourceBookChild;
+ }, [openResourceBookChild]);
+
const advanceResourceCanvasPage = useCallback(
(stepOffset: number) => {
const currentCategory = resourceCanvasPageWheelRef.current.activeCategory;
@@ -2639,39 +2607,9 @@ export default function ProjectDevelopmentView({
if (!nextCategory) {
return;
}
- if (resourceBookState.phase === 'returning') {
- if (!resourceBookState.pendingCategory) {
- // A return-to-main transition has no destination category. Ignore
- // any wheel step that was already queued before the collapse click;
- // otherwise it can resurrect a child-enter transition and append a
- // second viewport movement after the return leg.
- return;
- }
- // A wheel gesture can arrive while a click-driven book transition is
- // still in flight. Retarget the transition through the same tokenized
- // path so the old timer cannot restore a stale category.
- openResourceBookChild(nextCategory);
- return;
- }
- cancelResourceCardDrag();
- cancelResourceCanvasPan();
- resourceCanvasPageWheelRef.current.activeCategory = nextCategory;
- startTransition(() => {
- setActiveResourceCategory((currentCategory) =>
- resourceCanvasPageWheelRef.current.activeCategory === nextCategory
- ? nextCategory
- : currentCategory,
- );
- });
+ openResourceBookChildRef.current(nextCategory, true);
},
- [
- cancelResourceCanvasPan,
- cancelResourceCardDrag,
- openResourceBookChild,
- resourceBookState.pendingCategory,
- resourceBookState.phase,
- resourcePageCategories,
- ],
+ [resourcePageCategories],
);
useEffect(() => {
@@ -2735,6 +2673,7 @@ export default function ProjectDevelopmentView({
if (resourcePageCategories.length > 0) {
event.preventDefault();
if (event.ctrlKey || event.metaKey) {
+ resourceBookTransitionControllerRef.current.settle();
cancelQueuedResourceCanvasPageWheel();
const viewport = resolveViewport(event);
const category = viewport?.dataset.resourceSectionScroll as
@@ -2794,6 +2733,7 @@ export default function ProjectDevelopmentView({
};
resourceCanvasWheelHandlerRef.current = wheel;
const gestureStart = (rawEvent: Event) => {
+ resourceBookTransitionControllerRef.current.settle();
const event = rawEvent as WebKitGestureEvent;
cancelQueuedResourceCanvasPageWheel();
const viewport = resolveViewport(event);
@@ -3516,6 +3456,7 @@ export default function ProjectDevelopmentView({
);
const resetResourceCanvasViewport = useCallback(() => {
+ resourceBookTransitionControllerRef.current.settle();
if (!activePageCategory) {
return;
}
@@ -3532,6 +3473,7 @@ export default function ProjectDevelopmentView({
}, [activePageCategory, resourceCanvasFitBounds, setResourceCanvasViewport]);
const fitResourceBook = useCallback(() => {
+ resourceBookTransitionControllerRef.current.settle();
if (resourceBookView === 'main') {
const resetViewport = DEFAULT_RESOURCE_BOOK_VIEWPORT;
resourceBookMainViewportRef.current = resetViewport;
@@ -3560,6 +3502,8 @@ export default function ProjectDevelopmentView({
const zoomResourceBookBy = useCallback(
(factor: number) => {
+ resourceBookTransitionControllerRef.current.settle();
+ cancelQueuedResourceCanvasPageWheel();
const sceneElement = resourceBookManagerRef.current;
const rect = sceneElement?.getBoundingClientRect();
const anchorX = rect ? rect.width / 2 : 0;
@@ -3587,7 +3531,12 @@ export default function ProjectDevelopmentView({
if (next.scale === viewport.scale) return;
setResourceCanvasViewport(category, next);
},
- [activePageCategory, resourceBookView, setResourceCanvasViewport],
+ [
+ activePageCategory,
+ resourceBookView,
+ setResourceCanvasViewport,
+ cancelQueuedResourceCanvasPageWheel,
+ ],
);
const handleResourceBookWheel = useCallback(
@@ -3599,6 +3548,8 @@ export default function ProjectDevelopmentView({
}
return;
}
+ resourceBookTransitionControllerRef.current.settle();
+ cancelQueuedResourceCanvasPageWheel();
const sceneElement = resourceBookManagerRef.current;
if (!sceneElement) {
return;
@@ -3639,7 +3590,12 @@ export default function ProjectDevelopmentView({
});
setResourceCanvasViewport(category, result.viewport, true);
},
- [activePageCategory, resourceBookView, setResourceCanvasViewport],
+ [
+ activePageCategory,
+ resourceBookView,
+ setResourceCanvasViewport,
+ cancelQueuedResourceCanvasPageWheel,
+ ],
);
const handleResourceBookMainPointerDown = useCallback(
@@ -3656,6 +3612,7 @@ export default function ProjectDevelopmentView({
return;
}
event.currentTarget.setPointerCapture(event.pointerId);
+ resourceBookTransitionControllerRef.current.settle();
resourceBookMainPanRef.current = {
pointerId: event.pointerId,
startClientX: event.clientX,
@@ -3842,6 +3799,7 @@ export default function ProjectDevelopmentView({
}
cancelQueuedResourceCanvasPageWheel();
event.currentTarget.setPointerCapture(event.pointerId);
+ resourceBookTransitionControllerRef.current.settle();
resourceCanvasPanRef.current = {
pointerId: event.pointerId,
category: activePageCategory,
@@ -3888,6 +3846,7 @@ export default function ProjectDevelopmentView({
if (nextMode === sortMode) {
return;
}
+ resourceBookTransitionControllerRef.current.settle();
advanceFocusGeneration();
cancelResourceCardDrag();
cancelResourceCanvasPan();
@@ -5617,6 +5576,16 @@ export default function ProjectDevelopmentView({
className={`game-resource-manager game-resource-book-manager game-resource-book-manager--${resourceBookState.view} game-resource-book-manager--${resourceBookState.phase}`}
data-resource-book-view={resourceBookState.view}
data-resource-book-transition={resourceBookState.phase}
+ onPointerDownCapture={(event) => {
+ if (
+ event.currentTarget.hasAttribute('data-book-motion') &&
+ (event.target as Element).closest('.game-resource-card')
+ ) {
+ event.preventDefault();
+ event.stopPropagation();
+ resourceBookTransitionControllerRef.current.settle();
+ }
+ }}
>
+