1. Move GIF loading behind TextureManager.loadGif/releaseGif with
ref-counting (fixes shared-source invalidation on duplicate GIFs)
2. Strip query strings/fragments before .gif extension check
3. GIFs now load paused — separate MAX_PLAYING_GIFS=8 budget in
culling system, only nearest N animate (others show static frame)
When duplicated images share the same assetKey, Assets.unload()
was destroying the shared texture source — crashing all sprites
using it (null addressModeU/alphaMode in WebGL renderer).
TextureManager now tracks refCount per asset. Only calls
Assets.unload() when the last reference is released. ImageSprite
calls release() instead of unload() on texture teardown.
LRU eviction was destroying texture sources while sprites still
referenced them, causing PixiJS v8 'alphaMode of null' crash.
The viewport culling system (MAX_LOADED_TEXTURES=60) already controls
what's loaded/unloaded, so TextureManager doesn't need its own eviction.
Simplified to a pure load/unload cache with no budget tracking.
- Skip thumbnail generation when scene has >50 items (extract.canvas
on full viewport with many textures causes GPU OOM and context loss)
- Add WebGL context lost/restored handlers — auto-reload scene on recovery
- Reduce texture memory budget from 512MB to 256MB for GPU headroom
Remove server-side Sharp resize and texture dimension cap — PixiJS
handles large textures natively. Keep the proper Assets.unload() fix
to prevent "TextureSource destroyed instead of unloaded" warnings.
- TransformBox: use item.data (world-space) instead of getBounds() (screen-space)
for correct handle positioning regardless of viewport pan/zoom
- TransformBox: scale drag sensitivity relative to object size, not fixed 500px
- TransformBox: compensate drag deltas for viewport zoom level
- TransformBox: fix corner/edge handles to keep opposite edge fixed during resize
- SelectionManager: use world-space hit testing instead of screen-space getBounds()
- SelectionManager: fix rubber band selection to use world-space item bounds
- TextureManager: detect legacy asset keys (with file extension) vs new LOD keys
and load original file directly for pre-migration images (GPU handles scaling)
GPU texture cache keyed by asset:tier that loads textures on demand
via PixiJS Assets.load(), estimates RGBA memory usage, and evicts
least-recently-used entries when the budget is exceeded.