fix: cursor icon never rendering in preview/export - #28
Merged
Conversation
Every cursor style's texture creation called Assets.load(dataUrl) on a
raw base64 PNG data URI before Texture.from(dataUrl). PixiJS's Assets
loader can't resolve a parser for an unhinted data: URL and throws
"[Loader.load] Failed to load ...", which rejected the whole asset
promise and was swallowed by the per-asset catch in preloadCursorAssets
(logged as a console.warn only). With no cursor assets loaded, every
frame's shouldShowCursorSprite branch had nothing to draw, while the
click-highlight ripple (drawn as procedural Graphics, not a texture)
kept rendering fine - matching the reported symptom exactly, including
the Pointer style dropdown appearing to do nothing.
Texture.from(url) resolves textures independently of the Assets cache,
and nothing else in the app reads Assets.get/.cache for these URLs, so
the Assets.load calls were dead weight even before they started
throwing. Removed all 6 call sites and the now-unused Assets import.
Root-caused by adding temporary diagnostic logging to a local build,
confirming the exact failing asset ("tahoe/arrow") and error text
before making the fix.
Removing the failing Assets.load() calls (previous commit) wasn't sufficient on its own: Texture.from(url) for a bare string resolves through PixiJS's Cache/Assets system, which only has an entry if something previously loaded and registered that exact URL. Without Assets.load(), Cache.get() misses (logged as a "was not found in the Cache" warning) and the resulting Texture's underlying source isn't populated synchronously, so configureCursorTexture's `texture.source.scaleMode = ...` threw "Cannot read properties of undefined (reading 'source')" - a new failure mode replacing the old one, still with no cursor rendered. Every one of these 6 call sites already awaits loadImage(dataUrl) right before creating the texture, purely to read naturalWidth/naturalHeight for aspect ratio. Passing that already-decoded HTMLImageElement to Texture.from() instead of the URL string routes through PixiJS's ImageSource path, which builds the texture synchronously from the already-available pixel data - no Cache/Assets lookup involved at all. Verified live: added temporary diagnostic logging to a local build, confirmed textureValid/visible/renderable were all true with real non-zero texture dimensions, then confirmed visually via CDP screenshot that the cursor icon now renders in the editor preview at the correct telemetry-driven position. Diagnostics were fully reverted before this commit - git diff on cursorRenderer.ts contains only the from(url) -> from(image) changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cursorRenderer.tscalledAssets.load(dataUrl)on a raw base64 PNG data URI immediately beforeTexture.from(dataUrl). PixiJS'sAssetsloader can't resolve a parser for an unhinteddata:URL and throws[Loader.load] Failed to load data:image/png;base64,.... That rejection was caught per-asset inpreloadCursorAssets()'stry/catchand only logged viaconsole.warn(stripped from prod visibility in practice), so the whole cursor asset set silently ended up empty andgetStatefulCursorAsset/getCursorStyleAssethad nothing to draw.Assets.load(...)calls (and the now-unusedAssetsimport).Texture.from(url)resolves textures independently of the global Assets cache, and nothing else in the codebase readsAssets.get/Assets.cachefor these URLs — the load calls added no value even before they started throwing.How I found it
Root-caused via an Explore agent's static analysis (confirmed the two-path gating asymmetry between the icon sprite and the click-effect graphics), then added temporary diagnostic logging to a local unsigned build and reproduced against a real recording via CDP — captured the exact failing asset (
tahoe/arrow) and PixiJS loader error before writing the fix. Diagnostics and the temporarily-disabled console-stripping invite.config.tswere fully reverted before this commit;git diffon that file is empty.Test plan
npx tsc -p tsconfig.json --noEmit— cleannpx vitest --run— 834/834 passing🤖 Generated with Claude Code