feat: drag file to chatbot to upload 拖动以上传文件 (#1396)
* feat: 拖动以上传文件 * 上传文件过程中转圈圈 * fix: 解决仅在第一次上传时才有上传动画的问题 --------- Co-authored-by: 505030475 <qingxu.fu@outlook.com>
This commit is contained in:
parent
f7588d4776
commit
2b90302851
2
main.py
2
main.py
@ -292,7 +292,9 @@ def main():
|
|||||||
cancel_handles.append(click_handle)
|
cancel_handles.append(click_handle)
|
||||||
# 文件上传区,接收文件后与chatbot的互动
|
# 文件上传区,接收文件后与chatbot的互动
|
||||||
file_upload.upload(on_file_uploaded, [file_upload, chatbot, txt, txt2, checkboxes, cookies], [chatbot, txt, txt2, cookies])
|
file_upload.upload(on_file_uploaded, [file_upload, chatbot, txt, txt2, checkboxes, cookies], [chatbot, txt, txt2, cookies])
|
||||||
|
file_upload.upload(None, None, None, _js=r"()=>{toast_push('上传完毕, 请等待文件清单展现后继续操作 ...'); cancel_loading_status();}")
|
||||||
file_upload_2.upload(on_file_uploaded, [file_upload_2, chatbot, txt, txt2, checkboxes, cookies], [chatbot, txt, txt2, cookies])
|
file_upload_2.upload(on_file_uploaded, [file_upload_2, chatbot, txt, txt2, checkboxes, cookies], [chatbot, txt, txt2, cookies])
|
||||||
|
file_upload_2.upload(None, None, None, _js=r"()=>{toast_push('上传完毕, 请等待文件清单展现后继续操作 ...'); cancel_loading_status();}")
|
||||||
# 函数插件-固定按钮区
|
# 函数插件-固定按钮区
|
||||||
for k in plugins:
|
for k in plugins:
|
||||||
if not plugins[k].get("AsButton", True): continue
|
if not plugins[k].get("AsButton", True): continue
|
||||||
|
191
themes/common.js
191
themes/common.js
@ -3,7 +3,7 @@ function gradioApp() {
|
|||||||
const elems = document.getElementsByTagName('gradio-app');
|
const elems = document.getElementsByTagName('gradio-app');
|
||||||
const elem = elems.length == 0 ? document : elems[0];
|
const elem = elems.length == 0 ? document : elems[0];
|
||||||
if (elem !== document) {
|
if (elem !== document) {
|
||||||
elem.getElementById = function(id) {
|
elem.getElementById = function (id) {
|
||||||
return document.getElementById(id);
|
return document.getElementById(id);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -12,31 +12,31 @@ function gradioApp() {
|
|||||||
|
|
||||||
function setCookie(name, value, days) {
|
function setCookie(name, value, days) {
|
||||||
var expires = "";
|
var expires = "";
|
||||||
|
|
||||||
if (days) {
|
if (days) {
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
expires = "; expires=" + date.toUTCString();
|
expires = "; expires=" + date.toUTCString();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.cookie = name + "=" + value + expires + "; path=/";
|
document.cookie = name + "=" + value + expires + "; path=/";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
var decodedCookie = decodeURIComponent(document.cookie);
|
var decodedCookie = decodeURIComponent(document.cookie);
|
||||||
var cookies = decodedCookie.split(';');
|
var cookies = decodedCookie.split(';');
|
||||||
|
|
||||||
for (var i = 0; i < cookies.length; i++) {
|
for (var i = 0; i < cookies.length; i++) {
|
||||||
var cookie = cookies[i].trim();
|
var cookie = cookies[i].trim();
|
||||||
|
|
||||||
if (cookie.indexOf(name + "=") === 0) {
|
if (cookie.indexOf(name + "=") === 0) {
|
||||||
return cookie.substring(name.length + 1, cookie.length);
|
return cookie.substring(name.length + 1, cookie.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCopyButton(botElement) {
|
function addCopyButton(botElement) {
|
||||||
// https://github.com/GaiZhenbiao/ChuanhuChatGPT/tree/main/web_assets/javascript
|
// https://github.com/GaiZhenbiao/ChuanhuChatGPT/tree/main/web_assets/javascript
|
||||||
// Copy bot button
|
// Copy bot button
|
||||||
@ -49,7 +49,7 @@ function addCopyButton(botElement) {
|
|||||||
// messageBtnColumnElement.remove();
|
// messageBtnColumnElement.remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var copyButton = document.createElement('button');
|
var copyButton = document.createElement('button');
|
||||||
copyButton.classList.add('copy-bot-btn');
|
copyButton.classList.add('copy-bot-btn');
|
||||||
copyButton.setAttribute('aria-label', 'Copy');
|
copyButton.setAttribute('aria-label', 'Copy');
|
||||||
@ -98,40 +98,38 @@ function chatbotContentChanged(attempt = 1, force = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function chatbotAutoHeight(){
|
function chatbotAutoHeight() {
|
||||||
// 自动调整高度
|
// 自动调整高度
|
||||||
function update_height(){
|
function update_height() {
|
||||||
var { panel_height_target, chatbot_height, chatbot } = get_elements(true);
|
var { panel_height_target, chatbot_height, chatbot } = get_elements(true);
|
||||||
if (panel_height_target!=chatbot_height)
|
if (panel_height_target != chatbot_height) {
|
||||||
{
|
|
||||||
var pixelString = panel_height_target.toString() + 'px';
|
var pixelString = panel_height_target.toString() + 'px';
|
||||||
chatbot.style.maxHeight = pixelString; chatbot.style.height = pixelString;
|
chatbot.style.maxHeight = pixelString; chatbot.style.height = pixelString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_height_slow(){
|
function update_height_slow() {
|
||||||
var { panel_height_target, chatbot_height, chatbot } = get_elements();
|
var { panel_height_target, chatbot_height, chatbot } = get_elements();
|
||||||
if (panel_height_target!=chatbot_height)
|
if (panel_height_target != chatbot_height) {
|
||||||
{
|
new_panel_height = (panel_height_target - chatbot_height) * 0.5 + chatbot_height;
|
||||||
new_panel_height = (panel_height_target - chatbot_height)*0.5 + chatbot_height;
|
if (Math.abs(new_panel_height - panel_height_target) < 10) {
|
||||||
if (Math.abs(new_panel_height - panel_height_target) < 10){
|
|
||||||
new_panel_height = panel_height_target;
|
new_panel_height = panel_height_target;
|
||||||
}
|
}
|
||||||
// console.log(chatbot_height, panel_height_target, new_panel_height);
|
// console.log(chatbot_height, panel_height_target, new_panel_height);
|
||||||
var pixelString = new_panel_height.toString() + 'px';
|
var pixelString = new_panel_height.toString() + 'px';
|
||||||
chatbot.style.maxHeight = pixelString; chatbot.style.height = pixelString;
|
chatbot.style.maxHeight = pixelString; chatbot.style.height = pixelString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
monitoring_input_box()
|
monitoring_input_box()
|
||||||
update_height();
|
update_height();
|
||||||
setInterval(function() {
|
setInterval(function () {
|
||||||
update_height_slow()
|
update_height_slow()
|
||||||
}, 50); // 每100毫秒执行一次
|
}, 50); // 每100毫秒执行一次
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function get_elements(consider_state_panel=false) {
|
function get_elements(consider_state_panel = false) {
|
||||||
var chatbot = document.querySelector('#gpt-chatbot > div.wrap.svelte-18telvq');
|
var chatbot = document.querySelector('#gpt-chatbot > div.wrap.svelte-18telvq');
|
||||||
if (!chatbot) {
|
if (!chatbot) {
|
||||||
chatbot = document.querySelector('#gpt-chatbot');
|
chatbot = document.querySelector('#gpt-chatbot');
|
||||||
@ -142,13 +140,13 @@ function get_elements(consider_state_panel=false) {
|
|||||||
// const panel4 = document.querySelector('#interact-panel').getBoundingClientRect();
|
// const panel4 = document.querySelector('#interact-panel').getBoundingClientRect();
|
||||||
const panel5 = document.querySelector('#input-panel2').getBoundingClientRect();
|
const panel5 = document.querySelector('#input-panel2').getBoundingClientRect();
|
||||||
const panel_active = document.querySelector('#state-panel').getBoundingClientRect();
|
const panel_active = document.querySelector('#state-panel').getBoundingClientRect();
|
||||||
if (consider_state_panel || panel_active.height < 25){
|
if (consider_state_panel || panel_active.height < 25) {
|
||||||
document.state_panel_height = panel_active.height;
|
document.state_panel_height = panel_active.height;
|
||||||
}
|
}
|
||||||
// 25 是chatbot的label高度, 16 是右侧的gap
|
// 25 是chatbot的label高度, 16 是右侧的gap
|
||||||
var panel_height_target = panel1.height + panel2.height + panel3.height + 0 + 0 - 25 + 16*2;
|
var panel_height_target = panel1.height + panel2.height + panel3.height + 0 + 0 - 25 + 16 * 2;
|
||||||
// 禁止动态的state-panel高度影响
|
// 禁止动态的state-panel高度影响
|
||||||
panel_height_target = panel_height_target + (document.state_panel_height-panel_active.height)
|
panel_height_target = panel_height_target + (document.state_panel_height - panel_active.height)
|
||||||
var panel_height_target = parseInt(panel_height_target);
|
var panel_height_target = parseInt(panel_height_target);
|
||||||
var chatbot_height = chatbot.style.height;
|
var chatbot_height = chatbot.style.height;
|
||||||
var chatbot_height = parseInt(chatbot_height);
|
var chatbot_height = parseInt(chatbot_height);
|
||||||
@ -173,7 +171,7 @@ function add_func_paste(input) {
|
|||||||
}
|
}
|
||||||
if (paste_files.length > 0) {
|
if (paste_files.length > 0) {
|
||||||
// 按照文件列表执行批量上传逻辑
|
// 按照文件列表执行批量上传逻辑
|
||||||
await paste_upload_files(paste_files);
|
await upload_files(paste_files);
|
||||||
paste_files = []
|
paste_files = []
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -182,8 +180,42 @@ function add_func_paste(input) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function add_func_drag(elem) {
|
||||||
|
if (elem) {
|
||||||
|
const dragEvents = ["dragover", "dragenter"];
|
||||||
|
const leaveEvents = ["dragleave", "dragend", "drop"];
|
||||||
|
|
||||||
async function paste_upload_files(files) {
|
const onDrag = function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (elem_upload_float.querySelector("input[type=file]")) {
|
||||||
|
toast_push('释放以上传文件', 50)
|
||||||
|
} else {
|
||||||
|
toast_push('⚠️请先删除上传区中的历史文件,再尝试上传。', 50)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLeave = function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
dragEvents.forEach(event => {
|
||||||
|
elem.addEventListener(event, onDrag);
|
||||||
|
});
|
||||||
|
|
||||||
|
leaveEvents.forEach(event => {
|
||||||
|
elem.addEventListener(event, onLeave);
|
||||||
|
});
|
||||||
|
|
||||||
|
elem.addEventListener("drop", async function (e) {
|
||||||
|
const files = e.dataTransfer.files;
|
||||||
|
await upload_files(files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upload_files(files) {
|
||||||
const uploadInputElement = elem_upload_float.querySelector("input[type=file]");
|
const uploadInputElement = elem_upload_float.querySelector("input[type=file]");
|
||||||
let totalSizeMb = 0
|
let totalSizeMb = 0
|
||||||
if (files && files.length > 0) {
|
if (files && files.length > 0) {
|
||||||
@ -195,19 +227,20 @@ async function paste_upload_files(files) {
|
|||||||
}
|
}
|
||||||
// 检查文件总大小是否超过20MB
|
// 检查文件总大小是否超过20MB
|
||||||
if (totalSizeMb > 20) {
|
if (totalSizeMb > 20) {
|
||||||
toast_push('⚠️文件夹大于20MB 🚀上传文件中', 2000)
|
toast_push('⚠️文件夹大于 20MB 🚀上传文件中', 3000)
|
||||||
// return; // 如果超过了指定大小, 可以不进行后续上传操作
|
// return; // 如果超过了指定大小, 可以不进行后续上传操作
|
||||||
}
|
}
|
||||||
// 监听change事件, 原生Gradio可以实现
|
// 监听change事件, 原生Gradio可以实现
|
||||||
// uploadInputElement.addEventListener('change', function(){replace_input_string()});
|
// uploadInputElement.addEventListener('change', function(){replace_input_string()});
|
||||||
let event = new Event("change");
|
let event = new Event("change");
|
||||||
Object.defineProperty(event, "target", {value: uploadInputElement, enumerable: true});
|
Object.defineProperty(event, "target", { value: uploadInputElement, enumerable: true });
|
||||||
Object.defineProperty(event, "currentTarget", {value: uploadInputElement, enumerable: true});
|
Object.defineProperty(event, "currentTarget", { value: uploadInputElement, enumerable: true });
|
||||||
Object.defineProperty(uploadInputElement, "files", {value: files, enumerable: true});
|
Object.defineProperty(uploadInputElement, "files", { value: files, enumerable: true });
|
||||||
uploadInputElement.dispatchEvent(event);
|
uploadInputElement.dispatchEvent(event);
|
||||||
|
|
||||||
// toast_push('🎉上传文件成功', 2000)
|
// toast_push('🎉上传文件成功', 2000)
|
||||||
} else {
|
} else {
|
||||||
toast_push('⚠️请先删除上传区中的历史文件,再尝试粘贴。', 2000)
|
toast_push('⚠️请先删除上传区中的历史文件,再尝试上传。', 3000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,23 +264,85 @@ var elem_upload = null;
|
|||||||
var elem_upload_float = null;
|
var elem_upload_float = null;
|
||||||
var elem_input_main = null;
|
var elem_input_main = null;
|
||||||
var elem_input_float = null;
|
var elem_input_float = null;
|
||||||
|
var gptChatbot = null;
|
||||||
|
|
||||||
|
|
||||||
|
function begin_loading_status() {
|
||||||
|
// Create the loader div and add styling
|
||||||
|
var loader = document.createElement('div');
|
||||||
|
loader.id = 'Js_File_Loading';
|
||||||
|
loader.style.position = "absolute";
|
||||||
|
loader.style.top = "50%";
|
||||||
|
loader.style.left = "50%";
|
||||||
|
loader.style.width = "60px";
|
||||||
|
loader.style.height = "60px";
|
||||||
|
loader.style.border = "16px solid #f3f3f3";
|
||||||
|
loader.style.borderTop = "16px solid #3498db";
|
||||||
|
loader.style.borderRadius = "50%";
|
||||||
|
loader.style.animation = "spin 2s linear infinite";
|
||||||
|
loader.style.transform = "translate(-50%, -50%)";
|
||||||
|
document.body.appendChild(loader); // Add the loader to the body
|
||||||
|
// Set the CSS animation keyframes
|
||||||
|
var styleSheet = document.createElement('style');
|
||||||
|
// styleSheet.type = 'text/css';
|
||||||
|
styleSheet.id = 'Js_File_Loading_Style'
|
||||||
|
styleSheet.innerText = `
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}`;
|
||||||
|
document.head.appendChild(styleSheet);
|
||||||
|
}
|
||||||
|
function cancel_loading_status() {
|
||||||
|
var loadingElement = document.getElementById('Js_File_Loading');
|
||||||
|
if (loadingElement) {
|
||||||
|
document.body.removeChild(loadingElement); // remove the loader from the body
|
||||||
|
}
|
||||||
|
var loadingStyle = document.getElementById('Js_File_Loading_Style');
|
||||||
|
if (loadingStyle) {
|
||||||
|
document.head.removeChild(loadingStyle);
|
||||||
|
}
|
||||||
|
let clearButton = document.querySelectorAll('div[id*="elem_upload"] button[aria-label="Clear"]');
|
||||||
|
for (let button of clearButton) {
|
||||||
|
button.addEventListener('click', function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
register_upload_event();
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function register_upload_event() {
|
||||||
|
elem_upload_float = document.getElementById('elem_upload_float')
|
||||||
|
const upload_component = elem_upload_float.querySelector("input[type=file]");
|
||||||
|
if (upload_component) {
|
||||||
|
upload_component.addEventListener('change', function (event) {
|
||||||
|
toast_push('正在上传中,请稍等。', 2000);
|
||||||
|
begin_loading_status();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
function monitoring_input_box() {
|
function monitoring_input_box() {
|
||||||
|
register_upload_event();
|
||||||
|
|
||||||
elem_upload = document.getElementById('elem_upload')
|
elem_upload = document.getElementById('elem_upload')
|
||||||
elem_upload_float = document.getElementById('elem_upload_float')
|
elem_upload_float = document.getElementById('elem_upload_float')
|
||||||
elem_input_main = document.getElementById('user_input_main')
|
elem_input_main = document.getElementById('user_input_main')
|
||||||
elem_input_float = document.getElementById('user_input_float')
|
elem_input_float = document.getElementById('user_input_float')
|
||||||
|
|
||||||
if (elem_input_main) {
|
if (elem_input_main) {
|
||||||
if (elem_input_main.querySelector("textarea")) {
|
if (elem_input_main.querySelector("textarea")) {
|
||||||
add_func_paste(elem_input_main.querySelector("textarea"))
|
add_func_paste(elem_input_main.querySelector("textarea"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (elem_input_float) {
|
if (elem_input_float) {
|
||||||
if (elem_input_float.querySelector("textarea")){
|
if (elem_input_float.querySelector("textarea")) {
|
||||||
add_func_paste(elem_input_float.querySelector("textarea"))
|
add_func_paste(elem_input_float.querySelector("textarea"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
gptChatbot = document.getElementById('gpt-chatbot')
|
||||||
|
if (gptChatbot) {
|
||||||
|
add_func_drag(gptChatbot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -259,13 +354,13 @@ window.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
function audio_fn_init() {
|
function audio_fn_init() {
|
||||||
let audio_component = document.getElementById('elem_audio');
|
let audio_component = document.getElementById('elem_audio');
|
||||||
if (audio_component){
|
if (audio_component) {
|
||||||
let buttonElement = audio_component.querySelector('button');
|
let buttonElement = audio_component.querySelector('button');
|
||||||
let specificElement = audio_component.querySelector('.hide.sr-only');
|
let specificElement = audio_component.querySelector('.hide.sr-only');
|
||||||
specificElement.remove();
|
specificElement.remove();
|
||||||
|
|
||||||
buttonElement.childNodes[1].nodeValue = '启动麦克风';
|
buttonElement.childNodes[1].nodeValue = '启动麦克风';
|
||||||
buttonElement.addEventListener('click', function(event) {
|
buttonElement.addEventListener('click', function (event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
toast_push('您启动了麦克风!下一步请点击“实时语音对话”启动语音对话。');
|
toast_push('您启动了麦克风!下一步请点击“实时语音对话”启动语音对话。');
|
||||||
});
|
});
|
||||||
@ -273,14 +368,14 @@ function audio_fn_init() {
|
|||||||
// 查找语音插件按钮
|
// 查找语音插件按钮
|
||||||
let buttons = document.querySelectorAll('button');
|
let buttons = document.querySelectorAll('button');
|
||||||
let audio_button = null;
|
let audio_button = null;
|
||||||
for(let button of buttons){
|
for (let button of buttons) {
|
||||||
if (button.textContent.includes('语音')){
|
if (button.textContent.includes('语音')) {
|
||||||
audio_button = button;
|
audio_button = button;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (audio_button){
|
if (audio_button) {
|
||||||
audio_button.addEventListener('click', function() {
|
audio_button.addEventListener('click', function () {
|
||||||
toast_push('您点击了“实时语音对话”启动语音对话。');
|
toast_push('您点击了“实时语音对话”启动语音对话。');
|
||||||
});
|
});
|
||||||
let parent_element = audio_component.parentElement; // 将buttonElement移动到audio_button的内部
|
let parent_element = audio_component.parentElement; // 将buttonElement移动到audio_button的内部
|
||||||
@ -300,5 +395,5 @@ function GptAcademicJavaScriptInit(LAYOUT = "LEFT-RIGHT") {
|
|||||||
chatbotContentChanged(1);
|
chatbotContentChanged(1);
|
||||||
});
|
});
|
||||||
chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
|
chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
|
||||||
if (LAYOUT === "LEFT-RIGHT") {chatbotAutoHeight();}
|
if (LAYOUT === "LEFT-RIGHT") { chatbotAutoHeight(); }
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user