fix: rotated transform box, video texture crash, paste duplication, and loading UX

- TransformBox rotates with single-item selection (Figma-style), with
  handles and resize math projected into rotated coordinate space
- Fix VideoSprite alphaMode crash by swapping texture to EMPTY before
  destroying, preventing PixiJS render loop from reading null source
- Fix Ctrl+V double-paste: internal clipboard now always takes priority
  over system clipboard PNG, with wasRecentInternalPaste() guard
- Add asset loading progress bar and suppress "Drop images here" flash
  during initial scene load
This commit is contained in:
Hiren Kangad
2026-03-13 17:37:21 +05:30
parent e7e217d3d4
commit a901833b72
7 changed files with 243 additions and 70 deletions
+14 -8
View File
@@ -22,12 +22,19 @@ import { onArrangeAnimationDone } from './operations';
// Tracks when the last internal copy happened so paste can decide
// whether to use internal clipboard (just copied) vs system clipboard (external app).
let _lastInternalCopyTime = 0;
// Tracks when the last internal paste happened so setupPaste can skip the native event.
let _lastInternalPasteTime = 0;
/** Mark that an internal copy just happened. Called by copy/cut handlers. */
export function markInternalCopy(): void {
_lastInternalCopyTime = Date.now();
}
/** Check if an internal paste just happened (within last 500ms). Used by setupPaste to skip. */
export function wasRecentInternalPaste(): boolean {
return Date.now() - _lastInternalPasteTime < 500;
}
/** Collect selected items + their group children (for deep clone). */
function _collectGroupChildren(items: SceneItem[], scene: SceneManager): SceneItem[] {
const all: SceneItem[] = [];
@@ -494,19 +501,18 @@ export const shortcuts: ShortcutDef[] = [
category: 'editing', description: 'Paste',
preventDefault: false,
handler: async (ctx) => {
// Only handle recent internal copies here.
// For external clipboard content (images, text, HTML), do NOT call
// e.preventDefault() — let the native paste event fire through to setupPaste
// in image-drop.ts, which is the single path for all external clipboard content.
const timeSinceInternalCopy = Date.now() - _lastInternalCopyTime;
// If we have items in the internal clipboard, always prefer internal paste
// (duplicates the scene items with offset). This avoids re-uploading the
// PNG screenshot that Ctrl+C also wrote to the system clipboard.
const hasInternalItems = ctx.clipboardRef.current.length > 0;
const recentInternalCopy = hasInternalItems && timeSinceInternalCopy < 500;
if (recentInternalCopy) {
if (hasInternalItems) {
_lastInternalPasteTime = Date.now();
await _pasteInternal(ctx);
return;
}
// For external clipboard content: do nothing, let native paste event fire through to setupPaste
// No internal items — let native paste event fire through to setupPaste
// in image-drop.ts for external clipboard content (images, text, HTML).
},
},
{