Skip to content

fix: cursor icon never rendering in preview/export - #28

Merged
Alfredoalv13 merged 2 commits into
mainfrom
fix/cursor-icon-not-rendering
Jul 25, 2026
Merged

fix: cursor icon never rendering in preview/export#28
Alfredoalv13 merged 2 commits into
mainfrom
fix/cursor-icon-not-rendering

Conversation

@Alfredoalv13

Copy link
Copy Markdown
Owner

Summary

  • The cursor icon has silently never rendered in the editor preview or in exports (across all 5 styles). The click-highlight ripple/spotlight effect (procedural Graphics) still rendered fine, and changing the Pointer style dropdown appeared to do nothing — both are explained by the same root cause below.
  • Root cause: every cursor-texture creation path in cursorRenderer.ts called Assets.load(dataUrl) on a raw base64 PNG data URI immediately 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 data:image/png;base64,.... That rejection was caught per-asset in preloadCursorAssets()'s try/catch and only logged via console.warn (stripped from prod visibility in practice), so the whole cursor asset set silently ended up empty and getStatefulCursorAsset/getCursorStyleAsset had nothing to draw.
  • Fix: removed all 6 Assets.load(...) calls (and the now-unused Assets import). Texture.from(url) resolves textures independently of the global Assets cache, and nothing else in the codebase reads Assets.get/Assets.cache for 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 in vite.config.ts were fully reverted before this commit; git diff on that file is empty.

Test plan

  • npx tsc -p tsconfig.json --noEmit — clean
  • npx vitest --run — 834/834 passing
  • Live-verify in the installed app: cursor icon renders at multiple points along real telemetry, and changing the Pointer style dropdown visibly changes the rendered icon (in progress as part of this PR's merge process)

🤖 Generated with Claude Code

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.
@Alfredoalv13
Alfredoalv13 merged commit 484b4fd into main Jul 25, 2026
1 check passed
@Alfredoalv13
Alfredoalv13 deleted the fix/cursor-icon-not-rendering branch July 25, 2026 22:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants