- Text tool: click-to-place immediately opens inline editor, auto-switches
back to select tool. Empty text cleanup on save.
- Crop: select image + press C (or right-click > Crop) to enter crop mode.
8 drag handles with rule-of-thirds grid, dimmed outside area.
Enter confirms, Escape cancels. Non-destructive (stored as normalized rect).
- Text format toolbar: appears when text items selected, with font size +/-,
font family dropdown, and color picker with presets.
- Double-click image: zoom-to-fit (PureRef-style focus)
- Entrance animation: replaced bounce with simple fade-in (no delay before
items become interactive)
- TextEditor: allow empty text (caller handles cleanup)
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.
PixiJS v8 crashes with 'alphaMode of null' even when Sprite.visible
is false — it still traverses the display object. Fix: create the
Sprite lazily in loadTexture() only after a real texture is ready.
On unload, remove and destroy the Sprite entirely.
PixiJS v8 crashes with 'Cannot read alphaMode of null' when rendering
a Sprite with Texture.EMPTY. Fix: set sprite.visible = false until a
real texture is loaded, re-hide on unload.
Images no longer load textures eagerly on creation. Instead, the
culling ticker checks viewport proximity every 200ms:
- Load margin: 1x screen size beyond viewport (preload before visible)
- Unload margin: 2x screen size (hysteresis prevents thrash during pan)
ImageSprite changes:
- Constructor no longer calls loadTexture() — deferred to culling system
- New unloadTexture() restores placeholder and frees GPU memory
- Exposed texture getter for clipboard resolution detection
- loaded field now public for culling system to check state
This should allow hundreds of images without GPU OOM, since only
nearby textures consume GPU memory at any time.
DropShadowFilter applied to all ImageSprites for photos-on-a-desk feel.
SelectionManager now handles object dragging with shadow lift/drop and
spring-animated scale (1.0 <-> 1.03). Replaced incompatible @pixi/filter-drop-shadow
v5 with pixi-filters v6 for PixiJS v8 compatibility.
Introduces ImageSprite (extends Sprite) that manages per-image LOD
texture swapping via TextureManager. Shows a placeholder rect until
the first texture loads, then upgrades/downgrades tiers based on zoom.