mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-08 17:06:42 +00:00
multi-file select
This commit is contained in:
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
const notification = make("div", "notification", "off-screen");
|
const notification = make("div", "notification", "off-screen");
|
||||||
|
|
||||||
notification.textContent = msg;
|
notification.textContent = msg instanceof Error ? msg.message : msg;
|
||||||
|
|
||||||
$notifications.appendChild(notification);
|
$notifications.appendChild(notification);
|
||||||
|
|
||||||
@@ -84,8 +84,6 @@
|
|||||||
notification.remove();
|
notification.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.notify = notify;
|
|
||||||
|
|
||||||
function updateTitle() {
|
function updateTitle() {
|
||||||
const title = chatTitle || (messages.length ? "New Chat" : "");
|
const title = chatTitle || (messages.length ? "New Chat" : "");
|
||||||
|
|
||||||
@@ -1112,7 +1110,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notify(err.message);
|
notify(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
titleController = null;
|
titleController = null;
|
||||||
@@ -1329,6 +1327,8 @@
|
|||||||
|
|
||||||
attachments.splice(index, 1);
|
attachments.splice(index, 1);
|
||||||
|
|
||||||
|
storeValue("attachments", attachments);
|
||||||
|
|
||||||
el.remove();
|
el.remove();
|
||||||
|
|
||||||
$attachments.classList.toggle("has-files", !!attachments.length);
|
$attachments.classList.toggle("has-files", !!attachments.length);
|
||||||
@@ -1506,34 +1506,34 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$upload.addEventListener("click", async () => {
|
$upload.addEventListener("click", async () => {
|
||||||
const file = await selectFile(
|
const files = await selectFile(
|
||||||
// the ultimate list
|
// the ultimate list
|
||||||
".adoc,.bash,.bashrc,.bat,.c,.cc,.cfg,.cjs,.cmd,.conf,.cpp,.cs,.css,.csv,.cxx,.dockerfile,.dockerignore,.editorconfig,.env,.fish,.fs,.fsx,.gitattributes,.gitignore,.go,.gradle,.groovy,.h,.hh,.hpp,.htm,.html,.ini,.ipynb,.java,.jl,.js,.json,.jsonc,.jsx,.kt,.kts,.less,.log,.lua,.m,.makefile,.markdown,.md,.mjs,.mk,.mm,.php,.phtml,.pl,.pm,.profile,.properties,.ps1,.psql,.py,.pyw,.r,.rb,.rs,.rst,.sass,.scala,.scss,.sh,.sql,.svelte,.swift,.t,.toml,.ts,.tsv,.tsx,.txt,.vb,.vue,.xhtml,.xml,.xsd,.xsl,.xslt,.yaml,.yml,.zig,.zsh",
|
".adoc,.bash,.bashrc,.bat,.c,.cc,.cfg,.cjs,.cmd,.conf,.cpp,.cs,.css,.csv,.cxx,.dockerfile,.dockerignore,.editorconfig,.env,.fish,.fs,.fsx,.gitattributes,.gitignore,.go,.gradle,.groovy,.h,.hh,.hpp,.htm,.html,.ini,.ipynb,.java,.jl,.js,.json,.jsonc,.jsx,.kt,.kts,.less,.log,.lua,.m,.makefile,.markdown,.md,.mjs,.mk,.mm,.php,.phtml,.pl,.pm,.profile,.properties,.ps1,.psql,.py,.pyw,.r,.rb,.rs,.rst,.sass,.scala,.scss,.sh,.sql,.svelte,.swift,.t,.toml,.ts,.tsv,.tsx,.txt,.vb,.vue,.xhtml,.xml,.xsd,.xsl,.xslt,.yaml,.yml,.zig,.zsh",
|
||||||
false
|
true,
|
||||||
|
file => {
|
||||||
|
if (!file.name) {
|
||||||
|
file.name = "unknown.txt";
|
||||||
|
} else if (file.name.length > 512) {
|
||||||
|
throw new Error("File name too long (max 512 characters)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof file.content !== "string") {
|
||||||
|
throw new Error("File is not a text file");
|
||||||
|
} else if (!file.content) {
|
||||||
|
throw new Error("File is empty");
|
||||||
|
} else if (file.content.length > 4 * 1024 * 1024) {
|
||||||
|
throw new Error("File is too big (max 4MB)");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notify
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!file) {
|
if (!files.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
for (const file of files) {
|
||||||
if (!file.name) {
|
|
||||||
file.name = "unknown.txt";
|
|
||||||
} else if (file.name.length > 512) {
|
|
||||||
throw new Error("File name too long (max 512 characters)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof file.content !== "string") {
|
|
||||||
throw new Error("File is not a text file");
|
|
||||||
} else if (!file.content) {
|
|
||||||
throw new Error("File is empty");
|
|
||||||
} else if (file.content.length > 4 * 1024 * 1024) {
|
|
||||||
throw new Error("File is too big (max 4MB)");
|
|
||||||
}
|
|
||||||
|
|
||||||
pushAttachment(file);
|
pushAttachment(file);
|
||||||
} catch (err) {
|
|
||||||
notify(err.message);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1579,7 +1579,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = await selectFile("application/json", true),
|
const file = await selectFile(
|
||||||
|
"application/json",
|
||||||
|
false,
|
||||||
|
file => {
|
||||||
|
file.content = JSON.parse(file.content);
|
||||||
|
},
|
||||||
|
notify
|
||||||
|
),
|
||||||
data = file?.content;
|
data = file?.content;
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@@ -156,46 +156,67 @@ function lines(text) {
|
|||||||
return count + 1;
|
return count + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFile(accept, asJson = false) {
|
function readFile(file, handler, onError = false) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = () => {
|
||||||
|
try {
|
||||||
|
const result = {
|
||||||
|
name: file.name,
|
||||||
|
content: reader.result,
|
||||||
|
};
|
||||||
|
|
||||||
|
handler(result);
|
||||||
|
|
||||||
|
resolve(result);
|
||||||
|
} catch (err) {
|
||||||
|
onError?.(`${file.name}: ${err.message}`);
|
||||||
|
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = () => resolve(false);
|
||||||
|
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFile(accept, multiple, handler, onError = false) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const input = make("input");
|
const input = make("input");
|
||||||
|
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.accept = accept;
|
input.accept = accept;
|
||||||
|
input.multiple = multiple;
|
||||||
|
|
||||||
input.onchange = () => {
|
input.onchange = async () => {
|
||||||
const file = input.files[0];
|
const files = input.files;
|
||||||
|
|
||||||
if (!file) {
|
if (!files.length) {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const results = [];
|
||||||
|
|
||||||
reader.onload = () => {
|
for (const file of files) {
|
||||||
let content = reader.result;
|
const result = await readFile(file, handler, onError);
|
||||||
|
|
||||||
if (asJson) {
|
if (result) {
|
||||||
try {
|
results.push(result);
|
||||||
content = JSON.parse(content);
|
|
||||||
} catch {
|
|
||||||
resolve(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resolve({
|
if (!results.length) {
|
||||||
name: file.name,
|
resolve(false);
|
||||||
content: content,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.onerror = () => resolve(false);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
reader.readAsText(file);
|
resolve(multiple ? results : results[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
input.click();
|
input.click();
|
||||||
|
Reference in New Issue
Block a user