mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
[FEATURE_REQUEST] Sending PDF/HTML files? #1414
This commit is contained in:
@@ -8,14 +8,33 @@ import {
|
||||
eventSource,
|
||||
event_types,
|
||||
getCurrentChatId,
|
||||
getRequestHeaders,
|
||||
hideSwipeButtons,
|
||||
name2,
|
||||
saveChatDebounced,
|
||||
showSwipeButtons,
|
||||
} from "../script.js";
|
||||
import { getBase64Async, humanFileSize, saveBase64AsFile } from "./utils.js";
|
||||
import {
|
||||
extractTextFromHTML,
|
||||
extractTextFromMarkdown,
|
||||
extractTextFromPDF,
|
||||
getBase64Async,
|
||||
getStringHash,
|
||||
humanFileSize,
|
||||
saveBase64AsFile,
|
||||
} from "./utils.js";
|
||||
|
||||
const fileSizeLimit = 1024 * 1024 * 1; // 1 MB
|
||||
const fileSizeLimit = 1024 * 1024 * 10; // 10 MB
|
||||
|
||||
const converters = {
|
||||
'application/pdf': extractTextFromPDF,
|
||||
'text/html': extractTextFromHTML,
|
||||
'text/markdown': extractTextFromMarkdown,
|
||||
}
|
||||
|
||||
function isConvertible(type) {
|
||||
return Object.keys(converters).includes(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark message as hidden (system message).
|
||||
@@ -70,7 +89,7 @@ export async function unhideChatMessage(messageId, messageBlock) {
|
||||
/**
|
||||
* Adds a file attachment to the message.
|
||||
* @param {object} message Message object
|
||||
* @returns {Promise<void>}
|
||||
* @returns {Promise<void>} A promise that resolves when file is uploaded.
|
||||
*/
|
||||
export async function populateFileAttachment(message, inputId = 'file_form_input') {
|
||||
try {
|
||||
@@ -81,18 +100,38 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
|
||||
const file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const fileBase64 = await getBase64Async(file);
|
||||
let base64Data = fileBase64.split(',')[1];
|
||||
|
||||
// If file is image
|
||||
if (file.type.startsWith('image/')) {
|
||||
const base64Img = await getBase64Async(file);
|
||||
const base64ImgData = base64Img.split(',')[1];
|
||||
const extension = file.type.split('/')[1];
|
||||
const imageUrl = await saveBase64AsFile(base64ImgData, name2, file.name, extension);
|
||||
const imageUrl = await saveBase64AsFile(base64Data, name2, file.name, extension);
|
||||
message.extra.image = imageUrl;
|
||||
message.extra.inline_image = true;
|
||||
} else {
|
||||
const fileText = await file.text();
|
||||
const slug = getStringHash(file.name);
|
||||
const uniqueFileName = `${Date.now()}_${slug}.txt`;
|
||||
|
||||
if (isConvertible(file.type)) {
|
||||
try {
|
||||
const converter = converters[file.type];
|
||||
const fileText = await converter(file);
|
||||
base64Data = window.btoa(unescape(encodeURIComponent(fileText)));
|
||||
} catch (error) {
|
||||
toastr.error(error, 'Could not convert file');
|
||||
console.error('Could not convert file', error);
|
||||
}
|
||||
}
|
||||
|
||||
const fileUrl = await uploadFileAttachment(uniqueFileName, base64Data);
|
||||
|
||||
if (!fileUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
message.extra.file = {
|
||||
text: fileText,
|
||||
url: fileUrl,
|
||||
size: file.size,
|
||||
name: file.name,
|
||||
};
|
||||
@@ -105,6 +144,62 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads file to the server.
|
||||
* @param {string} fileName
|
||||
* @param {string} base64Data
|
||||
* @returns {Promise<string>} File URL
|
||||
*/
|
||||
export async function uploadFileAttachment(fileName, base64Data) {
|
||||
try {
|
||||
const result = await fetch('/api/file/upload', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({
|
||||
name: fileName,
|
||||
data: base64Data,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
const error = await result.text();
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const responseData = await result.json();
|
||||
return responseData.path.replace(/\\/g, '/');
|
||||
} catch (error) {
|
||||
toastr.error(error, 'Could not upload file');
|
||||
console.error('Could not upload file', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads file from the server.
|
||||
* @param {string} url File URL
|
||||
* @returns {Promise<string>} File text
|
||||
*/
|
||||
export async function getFileAttachment(url) {
|
||||
try {
|
||||
const result = await fetch(url, {
|
||||
method: 'GET',
|
||||
cache: 'force-cache',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
const error = await result.text();
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const text = await result.text();
|
||||
return text;
|
||||
} catch (error) {
|
||||
toastr.error(error, 'Could not download file');
|
||||
console.error('Could not download file', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates file to make sure it is not binary or not image.
|
||||
* @param {File} file File object
|
||||
@@ -121,7 +216,7 @@ async function validateFile(file) {
|
||||
}
|
||||
|
||||
// If file is binary
|
||||
if (isBinary && !isImage) {
|
||||
if (isBinary && !isImage && !isConvertible(file.type)) {
|
||||
toastr.error('Binary files are not supported. Select a text file or image.');
|
||||
return false;
|
||||
}
|
||||
@@ -193,22 +288,23 @@ async function deleteMessageFile(messageId) {
|
||||
* @param {number} messageId Message ID
|
||||
*/
|
||||
async function viewMessageFile(messageId) {
|
||||
const messageText = chat[messageId]?.extra?.file?.text;
|
||||
const messageFile = chat[messageId]?.extra?.file;
|
||||
|
||||
if (!messageText) {
|
||||
if (!messageFile) {
|
||||
console.debug('Message has no file or it is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileText = messageFile.text || (await getFileAttachment(messageFile.url));
|
||||
|
||||
const modalTemplate = $('<div><pre><code></code></pre></div>');
|
||||
modalTemplate.find('code').addClass('txt').text(messageText);
|
||||
modalTemplate.find('code').addClass('txt').text(fileText);
|
||||
modalTemplate.addClass('file_modal');
|
||||
addCopyToCodeBlocks(modalTemplate);
|
||||
|
||||
callPopup(modalTemplate, 'text');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a file embed into the message.
|
||||
* @param {number} messageId
|
||||
|
Reference in New Issue
Block a user