0.5.2: paste images, shared import helper, clipart persistence

- Clipboard paste creates an image layer: a document-level paste
  listener detects image items in the clipboard (Strg+V after a
  screenshot, e.g.) and imports them via the shared helper. Skipped
  when focus is in a real text input / textarea / contenteditable.
- Shared importImageFile() helper in fileLoader.ts replaces the
  duplicated image-import logic in AssetSidebar and CanvasStage's
  clipart-drop path. Optional position argument for drop-at-point.
- Clipart folder persistence hardened for reloads:
  - initialize() is now safely re-runnable (no-ops only once a handle
    is actually loaded, tolerates refresh failures so the folder name
    survives even if permission isn't auto-granted)
  - App.vue's onMounted kicks off initialize() so it runs regardless
    of whether the drawer component has mounted
  - DocumentSettings re-invokes initialize() each time the modal
    opens — covers app-start races and gives the user gesture needed
    for FS Access permission prompts
  - Opening the drawer refreshes thumbnails when handle is set but
    items are empty, so the user-gesture context restores permission

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-20 14:05:48 +02:00
1 parent cbc309bc8c
commit 07b0ef2659
8 files changed
+126 -65

No files matched your search

+2 -28
View File
@@ -4,7 +4,7 @@ import type Konva from 'konva'
import { PhCornersIn, PhCrop, PhMaskHappy, PhPencilSimple } from '@phosphor-icons/vue'
import { useSceneStore } from '@/stores/scene'
import { sampleLayer } from '@/lib/interpolate'
import { isSvgFile, loadImage, parseSvgDimensions, readAsDataUrl } from '@/lib/fileLoader'
import { importImageFile, loadImage } from '@/lib/fileLoader'
import { useClipartStore } from '@/stores/clipart'
import { drawSceneFrame, loadSceneImages } from '@/lib/render'
import type { AnimatableProps, Asset, Layer, Point } from '@/types/scene'
@@ -1834,33 +1834,7 @@ function onContainerDragOver(e: DragEvent) {
}
async function importClipartAt(file: File, worldX: number, worldY: number) {
const dataUrl = await readAsDataUrl(file)
let width = 0
let height = 0
if (isSvgFile(file)) {
const text = await file.text()
const dims = parseSvgDimensions(text)
if (dims) {
width = dims.width
height = dims.height
}
}
if (!width || !height) {
const img = await loadImage(dataUrl)
width = img.naturalWidth || 300
height = img.naturalHeight || 300
}
const asset = store.addAsset({
kind: 'image',
name: file.name,
src: dataUrl,
width,
height,
})
const layer = store.addLayer(asset.id)
if (layer) {
store.upsertKeyframe(layer.id, store.playhead, { x: worldX, y: worldY })
}
await importImageFile(store, file, { position: { x: worldX, y: worldY } })
}
function onContainerDrop(e: DragEvent) {