fix: scene clear race, path editor anchors, avatar cover, team color crash
Clear scene now patches traps/tokens/npcTokens atomically and updateScene merges against a fresh scene snapshot. Align NPC path-editor previews on token center, default new paths to fixed facing, size avatars by natural aspect for cover, and read team color synchronously from the picker. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -34,13 +34,15 @@
|
||||
box-shadow: 0 0 0 3px var(--accent-fill-soft, rgba(167, 139, 250, 0.28));
|
||||
}
|
||||
|
||||
/* Width/height set in JS (syncAvatarCoverSize) for aspect-preserving cover.
|
||||
Do not force a square img box — that cropped tall/wide photos before pan/zoom. */
|
||||
.avatar {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 140%;
|
||||
height: 140%;
|
||||
object-fit: cover;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
object-fit: fill;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,17 @@ function writeAvatarTransform(
|
||||
img.style.transform = `translate(-50%, -50%) translate(${String(offset.x * 100)}%, ${String(offset.y * 100)}%) scale(${String(scale)})`;
|
||||
}
|
||||
|
||||
/** Size the <img> so it covers the circle while keeping natural aspect (no square pre-crop). */
|
||||
function syncAvatarCoverSize(img: HTMLImageElement | null, circlePx: number) {
|
||||
if (!img || circlePx <= 0) return;
|
||||
const nw = img.naturalWidth;
|
||||
const nh = img.naturalHeight;
|
||||
if (nw <= 0 || nh <= 0) return;
|
||||
const cover = Math.max(circlePx / nw, circlePx / nh);
|
||||
img.style.width = `${String(nw * cover)}px`;
|
||||
img.style.height = `${String(nh * cover)}px`;
|
||||
}
|
||||
|
||||
export type PlayerTokenViewProps = {
|
||||
name: string;
|
||||
imageUrl: string | null;
|
||||
@@ -134,6 +145,7 @@ export function PlayerTokenView({
|
||||
}, [scale]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
syncAvatarCoverSize(imgRef.current, sizePx);
|
||||
writeAvatarTransform(imgRef.current, visualOffsetRef.current, visualScaleRef.current);
|
||||
});
|
||||
|
||||
@@ -259,6 +271,10 @@ export function PlayerTokenView({
|
||||
alt=""
|
||||
draggable={false}
|
||||
style={inactive ? { filter: 'grayscale(1)', opacity: 0.85 } : undefined}
|
||||
onLoad={() => {
|
||||
syncAvatarCoverSize(imgRef.current, sizePx);
|
||||
writeAvatarTransform(imgRef.current, visualOffsetRef.current, visualScaleRef.current);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={[styles.avatarEmpty, emptyContent ? styles.avatarEmptySolid : ''].filter(Boolean).join(' ')}>
|
||||
|
||||
Reference in New Issue
Block a user