mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 04:15:25 +00:00
fix/refac: heic image handling
This commit is contained in:
parent
bb506edcc3
commit
e1efd54a70
5 changed files with 145 additions and 81 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
|
@ -43,6 +43,7 @@
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"focus-trap": "^7.6.4",
|
"focus-trap": "^7.6.4",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
|
"heic2any": "^0.0.4",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"html-entities": "^2.5.3",
|
"html-entities": "^2.5.3",
|
||||||
"html2canvas-pro": "^1.5.11",
|
"html2canvas-pro": "^1.5.11",
|
||||||
|
|
@ -7315,6 +7316,12 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/heic2any": {
|
||||||
|
"version": "0.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/heic2any/-/heic2any-0.0.4.tgz",
|
||||||
|
"integrity": "sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/heimdalljs": {
|
"node_modules/heimdalljs": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/heimdalljs/-/heimdalljs-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/heimdalljs/-/heimdalljs-0.2.6.tgz",
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"focus-trap": "^7.6.4",
|
"focus-trap": "^7.6.4",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
|
"heic2any": "^0.0.4",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"html-entities": "^2.5.3",
|
"html-entities": "^2.5.3",
|
||||||
"html2canvas-pro": "^1.5.11",
|
"html2canvas-pro": "^1.5.11",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import heic2any from 'heic2any';
|
||||||
|
|
||||||
import { tick, getContext, onMount, onDestroy } from 'svelte';
|
import { tick, getContext, onMount, onDestroy } from 'svelte';
|
||||||
|
|
||||||
|
|
@ -78,7 +79,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputFilesHandler = async (inputFiles) => {
|
const inputFilesHandler = async (inputFiles) => {
|
||||||
inputFiles.forEach((file) => {
|
inputFiles.forEach(async (file) => {
|
||||||
console.info('Processing file:', {
|
console.info('Processing file:', {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
|
|
@ -102,43 +103,50 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (file['type'].startsWith('image/')) {
|
||||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png', 'image/avif'].includes(file['type'])
|
const compressImageHandler = async (imageUrl, settings = {}, config = {}) => {
|
||||||
) {
|
// Quick shortcut so we don’t do unnecessary work.
|
||||||
|
const settingsCompression = settings?.imageCompression ?? false;
|
||||||
|
const configWidth = config?.file?.image_compression?.width ?? null;
|
||||||
|
const configHeight = config?.file?.image_compression?.height ?? null;
|
||||||
|
|
||||||
|
// If neither settings nor config wants compression, return original URL.
|
||||||
|
if (!settingsCompression && !configWidth && !configHeight) {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to null (no compression unless set)
|
||||||
|
let width = null;
|
||||||
|
let height = null;
|
||||||
|
|
||||||
|
// If user/settings want compression, pick their preferred size.
|
||||||
|
if (settingsCompression) {
|
||||||
|
width = settings?.imageCompressionSize?.width ?? null;
|
||||||
|
height = settings?.imageCompressionSize?.height ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply config limits as an upper bound if any
|
||||||
|
if (configWidth && (width === null || width > configWidth)) {
|
||||||
|
width = configWidth;
|
||||||
|
}
|
||||||
|
if (configHeight && (height === null || height > configHeight)) {
|
||||||
|
height = configHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the compression if required
|
||||||
|
if (width || height) {
|
||||||
|
return await compressImage(imageUrl, width, height);
|
||||||
|
}
|
||||||
|
return imageUrl;
|
||||||
|
};
|
||||||
|
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
let imageUrl = event.target.result;
|
let imageUrl = event.target.result;
|
||||||
|
|
||||||
if (
|
// Compress the image if settings or config require it
|
||||||
($settings?.imageCompression ?? false) ||
|
imageUrl = await compressImageHandler(imageUrl, $settings, $config);
|
||||||
($config?.file?.image_compression?.width ?? null) ||
|
|
||||||
($config?.file?.image_compression?.height ?? null)
|
|
||||||
) {
|
|
||||||
let width = null;
|
|
||||||
let height = null;
|
|
||||||
|
|
||||||
if ($settings?.imageCompression ?? false) {
|
|
||||||
width = $settings?.imageCompressionSize?.width ?? null;
|
|
||||||
height = $settings?.imageCompressionSize?.height ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($config?.file?.image_compression?.width ?? null) ||
|
|
||||||
($config?.file?.image_compression?.height ?? null)
|
|
||||||
) {
|
|
||||||
if (width > ($config?.file?.image_compression?.width ?? null)) {
|
|
||||||
width = $config?.file?.image_compression?.width ?? null;
|
|
||||||
}
|
|
||||||
if (height > ($config?.file?.image_compression?.height ?? null)) {
|
|
||||||
height = $config?.file?.image_compression?.height ?? null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width || height) {
|
|
||||||
imageUrl = await compressImage(imageUrl, width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = [
|
files = [
|
||||||
...files,
|
...files,
|
||||||
|
|
@ -149,7 +157,11 @@
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(
|
||||||
|
file['type'] === 'image/heic'
|
||||||
|
? await heic2any({ blob: file, toType: 'image/jpeg' })
|
||||||
|
: file
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
uploadFileHandler(file);
|
uploadFileHandler(file);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
|
import heic2any from 'heic2any';
|
||||||
|
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
|
|
@ -320,7 +321,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputFiles.forEach((file) => {
|
inputFiles.forEach(async (file) => {
|
||||||
console.log('Processing file:', {
|
console.log('Processing file:', {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
|
|
@ -344,46 +345,53 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (file['type'].startsWith('image/')) {
|
||||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png', 'image/avif'].includes(file['type'])
|
|
||||||
) {
|
|
||||||
if (visionCapableModels.length === 0) {
|
if (visionCapableModels.length === 0) {
|
||||||
toast.error($i18n.t('Selected model(s) do not support image inputs'));
|
toast.error($i18n.t('Selected model(s) do not support image inputs'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const compressImageHandler = async (imageUrl, settings = {}, config = {}) => {
|
||||||
|
// Quick shortcut so we don’t do unnecessary work.
|
||||||
|
const settingsCompression = settings?.imageCompression ?? false;
|
||||||
|
const configWidth = config?.file?.image_compression?.width ?? null;
|
||||||
|
const configHeight = config?.file?.image_compression?.height ?? null;
|
||||||
|
|
||||||
|
// If neither settings nor config wants compression, return original URL.
|
||||||
|
if (!settingsCompression && !configWidth && !configHeight) {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to null (no compression unless set)
|
||||||
|
let width = null;
|
||||||
|
let height = null;
|
||||||
|
|
||||||
|
// If user/settings want compression, pick their preferred size.
|
||||||
|
if (settingsCompression) {
|
||||||
|
width = settings?.imageCompressionSize?.width ?? null;
|
||||||
|
height = settings?.imageCompressionSize?.height ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply config limits as an upper bound if any
|
||||||
|
if (configWidth && (width === null || width > configWidth)) {
|
||||||
|
width = configWidth;
|
||||||
|
}
|
||||||
|
if (configHeight && (height === null || height > configHeight)) {
|
||||||
|
height = configHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the compression if required
|
||||||
|
if (width || height) {
|
||||||
|
return await compressImage(imageUrl, width, height);
|
||||||
|
}
|
||||||
|
return imageUrl;
|
||||||
|
};
|
||||||
|
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
let imageUrl = event.target.result;
|
let imageUrl = event.target.result;
|
||||||
|
|
||||||
if (
|
imageUrl = await compressImageHandler(imageUrl, $settings, $config);
|
||||||
($settings?.imageCompression ?? false) ||
|
|
||||||
($config?.file?.image_compression?.width ?? null) ||
|
|
||||||
($config?.file?.image_compression?.height ?? null)
|
|
||||||
) {
|
|
||||||
let width = null;
|
|
||||||
let height = null;
|
|
||||||
|
|
||||||
if ($settings?.imageCompression ?? false) {
|
|
||||||
width = $settings?.imageCompressionSize?.width ?? null;
|
|
||||||
height = $settings?.imageCompressionSize?.height ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($config?.file?.image_compression?.width ?? null) ||
|
|
||||||
($config?.file?.image_compression?.height ?? null)
|
|
||||||
) {
|
|
||||||
if (width > ($config?.file?.image_compression?.width ?? null)) {
|
|
||||||
width = $config?.file?.image_compression?.width ?? null;
|
|
||||||
}
|
|
||||||
if (height > ($config?.file?.image_compression?.height ?? null)) {
|
|
||||||
height = $config?.file?.image_compression?.height ?? null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width || height) {
|
|
||||||
imageUrl = await compressImage(imageUrl, width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = [
|
files = [
|
||||||
...files,
|
...files,
|
||||||
|
|
@ -393,7 +401,11 @@
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(
|
||||||
|
file['type'] === 'image/heic'
|
||||||
|
? await heic2any({ blob: file, toType: 'image/jpeg' })
|
||||||
|
: file
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
uploadFileHandler(file);
|
uploadFileHandler(file);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import heic2any from 'heic2any';
|
||||||
import fileSaver from 'file-saver';
|
import fileSaver from 'file-saver';
|
||||||
const { saveAs } = fileSaver;
|
const { saveAs } = fileSaver;
|
||||||
|
|
||||||
|
|
@ -328,7 +329,7 @@
|
||||||
|
|
||||||
const inputFilesHandler = async (inputFiles) => {
|
const inputFilesHandler = async (inputFiles) => {
|
||||||
console.log('Input files handler called with:', inputFiles);
|
console.log('Input files handler called with:', inputFiles);
|
||||||
inputFiles.forEach((file) => {
|
inputFiles.forEach(async (file) => {
|
||||||
console.log('Processing file:', {
|
console.log('Processing file:', {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
|
|
@ -352,21 +353,48 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (file['type'].startsWith('image/')) {
|
||||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png', 'image/avif'].includes(file['type'])
|
const compressImageHandler = async (imageUrl, settings = {}, config = {}) => {
|
||||||
) {
|
// Quick shortcut so we don’t do unnecessary work.
|
||||||
|
const settingsCompression = settings?.imageCompression ?? false;
|
||||||
|
const configWidth = config?.file?.image_compression?.width ?? null;
|
||||||
|
const configHeight = config?.file?.image_compression?.height ?? null;
|
||||||
|
|
||||||
|
// If neither settings nor config wants compression, return original URL.
|
||||||
|
if (!settingsCompression && !configWidth && !configHeight) {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to null (no compression unless set)
|
||||||
|
let width = null;
|
||||||
|
let height = null;
|
||||||
|
|
||||||
|
// If user/settings want compression, pick their preferred size.
|
||||||
|
if (settingsCompression) {
|
||||||
|
width = settings?.imageCompressionSize?.width ?? null;
|
||||||
|
height = settings?.imageCompressionSize?.height ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply config limits as an upper bound if any
|
||||||
|
if (configWidth && (width === null || width > configWidth)) {
|
||||||
|
width = configWidth;
|
||||||
|
}
|
||||||
|
if (configHeight && (height === null || height > configHeight)) {
|
||||||
|
height = configHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the compression if required
|
||||||
|
if (width || height) {
|
||||||
|
return await compressImage(imageUrl, width, height);
|
||||||
|
}
|
||||||
|
return imageUrl;
|
||||||
|
};
|
||||||
|
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
let imageUrl = event.target.result;
|
let imageUrl = event.target.result;
|
||||||
|
|
||||||
if ($settings?.imageCompression ?? false) {
|
imageUrl = await compressImageHandler(imageUrl, $settings, $config);
|
||||||
const width = $settings?.imageCompressionSize?.width ?? null;
|
|
||||||
const height = $settings?.imageCompressionSize?.height ?? null;
|
|
||||||
|
|
||||||
if (width || height) {
|
|
||||||
imageUrl = await compressImage(imageUrl, width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = [
|
files = [
|
||||||
...files,
|
...files,
|
||||||
|
|
@ -377,7 +405,11 @@
|
||||||
];
|
];
|
||||||
note.data.files = files;
|
note.data.files = files;
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(
|
||||||
|
file['type'] === 'image/heic'
|
||||||
|
? await heic2any({ blob: file, toType: 'image/jpeg' })
|
||||||
|
: file
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
uploadFileHandler(file);
|
uploadFileHandler(file);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue