diff --git a/app/static/public/js/imagine_workbench.js b/app/static/public/js/imagine_workbench.js index 0e50cb8..c465ce3 100644 --- a/app/static/public/js/imagine_workbench.js +++ b/app/static/public/js/imagine_workbench.js @@ -969,22 +969,55 @@ renderReferenceStrip(); } + function inferImageMimeFromName(name) { + const lower = String(name || '').trim().toLowerCase(); + if (lower.endsWith('.png')) return 'image/png'; + if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg'; + if (lower.endsWith('.webp')) return 'image/webp'; + if (lower.endsWith('.gif')) return 'image/gif'; + if (lower.endsWith('.bmp')) return 'image/bmp'; + if (lower.endsWith('.svg')) return 'image/svg+xml'; + if (lower.endsWith('.avif')) return 'image/avif'; + if (lower.endsWith('.heic')) return 'image/heic'; + if (lower.endsWith('.heif')) return 'image/heif'; + if (lower.endsWith('.tif') || lower.endsWith('.tiff')) return 'image/tiff'; + if (lower.endsWith('.ico')) return 'image/x-icon'; + return ''; + } + + function normalizeImageFile(file, fallbackMime = '') { + if (!(file instanceof Blob)) return null; + const detectedMime = String(file.type || fallbackMime || inferImageMimeFromName(file.name) || '').trim(); + if (!detectedMime.startsWith('image/')) return null; + if (String(file.type || '').trim()) { + return file; + } + const safeName = String(file.name || '').trim() || `clipboard.${detectedMime.split('/')[1] || 'png'}`; + try { + return new File([file], safeName, { type: detectedMime }); + } catch (e) { + const blob = file.slice(0, file.size, detectedMime); + blob.name = safeName; + return blob; + } + } + function pickImageFilesFromDataTransfer(dataTransfer) { if (!dataTransfer) return []; const files = []; - const pushIfImage = (file) => { - if (!file) return; - if (!String(file.type || '').startsWith('image/')) return; - files.push(file); + const pushIfImage = (file, fallbackMime = '') => { + const normalized = normalizeImageFile(file, fallbackMime); + if (!normalized) return; + files.push(normalized); }; if (dataTransfer.files && dataTransfer.files.length) { - Array.from(dataTransfer.files).forEach(pushIfImage); + Array.from(dataTransfer.files).forEach((file) => pushIfImage(file)); } - if (!files.length && dataTransfer.items && dataTransfer.items.length) { + if (dataTransfer.items && dataTransfer.items.length) { Array.from(dataTransfer.items).forEach((item) => { if (!item || item.kind !== 'file') return; const file = item.getAsFile ? item.getAsFile() : null; - pushIfImage(file); + pushIfImage(file, String(item.type || '').trim()); }); } return files; @@ -1007,14 +1040,14 @@ let added = 0; for (const file of targets) { - const mimeType = String(file.type || ''); - if (mimeType && !mimeType.startsWith('image/')) continue; - const dataUrl = await readFileAsDataUrl(file); + const normalizedFile = normalizeImageFile(file); + if (!normalizedFile) continue; + const dataUrl = await readFileAsDataUrl(normalizedFile); if (!dataUrl.startsWith('data:image/')) continue; state.referenceImages.push({ id: buildRefId(), - name: file.name || source || '未命名图片', - mime: file.type || '', + name: normalizedFile.name || source || '未命名图片', + mime: normalizedFile.type || '', data: dataUrl, source: source || 'upload', isPrimary: false, diff --git a/app/static/public/js/video.js b/app/static/public/js/video.js index 992e7f4..aabee5b 100644 --- a/app/static/public/js/video.js +++ b/app/static/public/js/video.js @@ -1256,6 +1256,39 @@ }); } + function inferImageMimeFromName(name) { + const lower = String(name || '').trim().toLowerCase(); + if (lower.endsWith('.png')) return 'image/png'; + if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg'; + if (lower.endsWith('.webp')) return 'image/webp'; + if (lower.endsWith('.gif')) return 'image/gif'; + if (lower.endsWith('.bmp')) return 'image/bmp'; + if (lower.endsWith('.svg')) return 'image/svg+xml'; + if (lower.endsWith('.avif')) return 'image/avif'; + if (lower.endsWith('.heic')) return 'image/heic'; + if (lower.endsWith('.heif')) return 'image/heif'; + if (lower.endsWith('.tif') || lower.endsWith('.tiff')) return 'image/tiff'; + if (lower.endsWith('.ico')) return 'image/x-icon'; + return ''; + } + + function normalizeImageFile(file, fallbackMime = '') { + if (!(file instanceof Blob)) return null; + const detectedMime = String(file.type || fallbackMime || inferImageMimeFromName(file.name) || '').trim(); + if (!detectedMime.startsWith('image/')) return null; + if (String(file.type || '').trim()) { + return file; + } + const safeName = String(file.name || '').trim() || `clipboard.${detectedMime.split('/')[1] || 'png'}`; + try { + return new File([file], safeName, { type: detectedMime }); + } catch (e) { + const blob = file.slice(0, file.size, detectedMime); + blob.name = safeName; + return blob; + } + } + function hasFiles(dataTransfer) { if (!dataTransfer) return false; if (dataTransfer.files && dataTransfer.files.length > 0) return true; @@ -1267,19 +1300,19 @@ function pickImageFilesFromDataTransfer(dataTransfer) { if (!dataTransfer) return []; const files = []; - const pushIfImage = (file) => { - if (!file) return; - if (!String(file.type || '').startsWith('image/')) return; - files.push(file); + const pushIfImage = (file, fallbackMime = '') => { + const normalized = normalizeImageFile(file, fallbackMime); + if (!normalized) return; + files.push(normalized); }; if (dataTransfer.files && dataTransfer.files.length) { - Array.from(dataTransfer.files).forEach(pushIfImage); + Array.from(dataTransfer.files).forEach((file) => pushIfImage(file)); } - if (!files.length && dataTransfer.items && dataTransfer.items.length) { + if (dataTransfer.items && dataTransfer.items.length) { Array.from(dataTransfer.items).forEach((item) => { if (!item || item.kind !== 'file') return; const file = item.getAsFile ? item.getAsFile() : null; - pushIfImage(file); + pushIfImage(file, String(item.type || '').trim()); }); } return files; @@ -1301,11 +1334,9 @@ const accepted = targets.slice(0, slotsLeft); let added = 0; for (const file of accepted) { - const mimeType = String(file.type || ''); - if (mimeType && !mimeType.startsWith('image/')) { - continue; - } - const dataUrl = await readFileAsDataUrl(file); + const normalizedFile = normalizeImageFile(file); + if (!normalizedFile) continue; + const dataUrl = await readFileAsDataUrl(normalizedFile); if (!dataUrl.startsWith('data:image/')) { continue; } @@ -1315,7 +1346,7 @@ sourceUrl: dataUrl, url: dataUrl, parentPostId: '', - name: file.name || sourceLabel || '已选择图片', + name: normalizedFile.name || sourceLabel || '已选择图片', mentionLabel: getReferenceMentionLabel(referenceImages.length) }); added += 1;