diff --git a/frontend/src/views/ImagesView.vue b/frontend/src/views/ImagesView.vue index ca371f5..046d70a 100644 --- a/frontend/src/views/ImagesView.vue +++ b/frontend/src/views/ImagesView.vue @@ -47,6 +47,32 @@ async function copyLink(name) { flash(await copyText(absUrl(name)) ? '链接已复制' : '复制失败') } +async function copyImage(url) { + try { + const blob = await (await fetch(url)).blob() + const pngBlob = blob.type === 'image/png' + ? blob + : await new Promise((resolve, reject) => { + createImageBitmap(blob).then(async (bitmap) => { + const canvas = document.createElement('canvas') + canvas.width = bitmap.width + canvas.height = bitmap.height + const ctx = canvas.getContext('2d') + if (!ctx) { reject(new Error('no canvas ctx')); return } + ctx.drawImage(bitmap, 0, 0) + canvas.toBlob((out) => { + if (out) resolve(out) + else reject(new Error('png convert failed')) + }, 'image/png') + }).catch(reject) + }) + await navigator.clipboard.write([new ClipboardItem({ 'image/png': pngBlob })]) + flash('图片已复制') + } catch { + flash('复制失败') + } +} + async function copyPrompt(f) { if (!f.prompt) return flash(await copyText(f.prompt) ? '指令已复制' : '复制失败') @@ -168,6 +194,10 @@ onUnmounted(() => window.removeEventListener('keydown', onKey))
+ diff --git a/frontend/src/views/PlaygroundView.vue b/frontend/src/views/PlaygroundView.vue index e85c1d5..328fc5b 100644 --- a/frontend/src/views/PlaygroundView.vue +++ b/frontend/src/views/PlaygroundView.vue @@ -272,6 +272,32 @@ async function copyPrompt(item) { flash(await copyText(text) ? '指令已复制' : '复制失败') } +async function copyImage(url) { + try { + const blob = await (await fetch(url)).blob() + const pngBlob = blob.type === 'image/png' + ? blob + : await new Promise((resolve, reject) => { + createImageBitmap(blob).then((bitmap) => { + const canvas = document.createElement('canvas') + canvas.width = bitmap.width + canvas.height = bitmap.height + const ctx = canvas.getContext('2d') + if (!ctx) { reject(new Error('no canvas ctx')); return } + ctx.drawImage(bitmap, 0, 0) + canvas.toBlob((out) => { + if (out) resolve(out) + else reject(new Error('png convert failed')) + }, 'image/png') + }).catch(reject) + }) + await navigator.clipboard.write([new ClipboardItem({ 'image/png': pngBlob })]) + flash('图片已复制') + } catch { + flash('复制失败') + } +} + // ---- generate (concurrent — no lock) ---- // 生图 can request 1–4 images at once: each is an independent task/charge. const count = ref(1) @@ -686,6 +712,10 @@ onUnmounted(() => {
+ diff --git a/frontend/src/views/UserLogsView.vue b/frontend/src/views/UserLogsView.vue index 99074b6..f4a04df 100644 --- a/frontend/src/views/UserLogsView.vue +++ b/frontend/src/views/UserLogsView.vue @@ -88,6 +88,33 @@ async function copyLink(name) { setTimeout(() => (toast.value = ''), 1500) } +async function copyImage(url) { + try { + const blob = await (await fetch(url)).blob() + const pngBlob = blob.type === 'image/png' + ? blob + : await new Promise((resolve, reject) => { + createImageBitmap(blob).then((bitmap) => { + const canvas = document.createElement('canvas') + canvas.width = bitmap.width + canvas.height = bitmap.height + const ctx = canvas.getContext('2d') + if (!ctx) { reject(new Error('no canvas ctx')); return } + ctx.drawImage(bitmap, 0, 0) + canvas.toBlob((out) => { + if (out) resolve(out) + else reject(new Error('png convert failed')) + }, 'image/png') + }).catch(reject) + }) + await navigator.clipboard.write([new ClipboardItem({ 'image/png': pngBlob })]) + toast.value = '图片已复制' + } catch { + toast.value = '复制失败' + } + setTimeout(() => (toast.value = ''), 1500) +} + async function copyPrompt(e) { if (!e.prompt) return const ok = await copyText(e.prompt) @@ -199,6 +226,10 @@ onUnmounted(() => {