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:
1 parent
cbc309bc8c
commit
07b0ef2659
8 files changed
+126
-65
No files matched your search
@@ -51,3 +51,52 @@ export function parseSvgDimensions(svgText: string): Dimensions | null {
|
||||
export function isSvgFile(file: File): boolean {
|
||||
return file.type === 'image/svg+xml' || file.name.toLowerCase().endsWith('.svg')
|
||||
}
|
||||
|
||||
interface ImageImportStore {
|
||||
addAsset: (a: {
|
||||
kind: 'image'
|
||||
name: string
|
||||
src: string
|
||||
width: number
|
||||
height: number
|
||||
}) => { id: string }
|
||||
addLayer: (assetId: string, name?: string) => { id: string } | null
|
||||
upsertKeyframe?: (layerId: string, time: number, props: { x: number; y: number }) => void
|
||||
playhead?: number
|
||||
}
|
||||
|
||||
export async function importImageFile(
|
||||
store: ImageImportStore,
|
||||
file: File,
|
||||
options?: { position?: { x: number; y: number } },
|
||||
): Promise<{ assetId: string; layerId: string } | null> {
|
||||
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 || 'image',
|
||||
src: dataUrl,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
const layer = store.addLayer(asset.id)
|
||||
if (!layer) return null
|
||||
if (options?.position && store.upsertKeyframe) {
|
||||
store.upsertKeyframe(layer.id, store.playhead ?? 0, options.position)
|
||||
}
|
||||
return { assetId: asset.id, layerId: layer.id }
|
||||
}
|
||||
Reference in new issue
Block a user