1
0
mirror of https://github.com/coalaura/whiskr.git synced 2025-12-02 20:22:52 +00:00

improve attaching to specific messages

This commit is contained in:
Laura
2025-11-02 23:50:43 +01:00
parent a8ed9b2b06
commit 90b1509e94
3 changed files with 101 additions and 62 deletions

View File

@@ -629,11 +629,13 @@ body:not(.loading) #loading {
transition: 150ms;
}
.message.editing .options,
.message:hover .options {
opacity: 1;
pointer-events: all;
}
.message .attach,
.message .collapse {
position: relative;
margin-right: 14px;
@@ -651,6 +653,7 @@ body:not(.loading) #loading {
transform: scaleY(-100%);
}
.message .attach::after,
.message .collapse::after {
position: absolute;
top: 4px;
@@ -659,6 +662,8 @@ body:not(.loading) #loading {
.message.errored .options .copy,
.message.errored .options .edit,
.message:not(.editing) .options .attach,
.message.editing .options .collapse,
.message.waiting .options,
.message.reasoning .options,
.message.tooling .options,
@@ -966,6 +971,7 @@ select {
gap: 4px;
}
.message .options .attach::after,
.message .options .collapse::after,
#chat .option+.option::before {
content: "";
@@ -996,6 +1002,7 @@ body.loading #version,
.message .edit,
.message .retry,
.message .delete,
.message .attach,
.pre-copy,
.tool .call .name::after,
.tool .call::before,
@@ -1044,6 +1051,10 @@ input.invalid {
background-image: url(icons/collapse.svg);
}
.message .attach {
background-image: url(icons/attach_message.svg);
}
.pre-copy,
.message .copy {
background-image: url(icons/copy.svg);
@@ -1159,6 +1170,7 @@ label[for="reasoning-tokens"] {
background-image: url(icons/attach.svg);
}
.message .attach.loading,
#upload.loading {
animation: rotating 1.2s linear infinite;
background-image: url(icons/spinner.svg);

View File

@@ -0,0 +1,11 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M20 10.9696L11.9628 18.5497C10.9782 19.4783 9.64274 20 8.25028 20C6.85782 20 5.52239 19.4783 4.53777 18.5497C3.55315 17.6211 3 16.3616 3 15.0483C3 13.7351 3.55315 12.4756 4.53777 11.547L12.575 3.96687C13.2314 3.34779 14.1217 3 15.05 3C15.9783 3 16.8686 3.34779 17.525 3.96687C18.1814 4.58595 18.5502 5.4256 18.5502 6.30111C18.5502 7.17662 18.1814 8.01628 17.525 8.63535L9.47904 16.2154C9.15083 16.525 8.70569 16.6989 8.24154 16.6989C7.77738 16.6989 7.33224 16.525 7.00403 16.2154C6.67583 15.9059 6.49144 15.4861 6.49144 15.0483C6.49144 14.6106 6.67583 14.1907 7.00403 13.8812L14.429 6.88674" stroke="#b7bdf8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -69,7 +69,6 @@
chatTitle = false;
let searchAvailable = false,
activeMessage = null,
isResizing = false,
scrollResize = false,
isUploading = false,
@@ -372,6 +371,19 @@
this.#save();
});
// attach option
if (this.#role === "user") {
const _attach = make("button", "attach");
_attach.title = "Add files to this message";
_opts.appendChild(_attach);
_attach.addEventListener("click", () => {
uploadToMessage(_attach, this);
});
}
// copy option
const _optCopy = make("button", "copy");
@@ -443,6 +455,10 @@
_opts.appendChild(_optEdit);
_optEdit.addEventListener("click", () => {
if (this.#_message.classList.contains("collapsed")) {
_optCollapse.click();
}
this.toggleEdit();
});
@@ -926,8 +942,6 @@
this.#editing = !this.#editing;
if (this.#editing) {
activeMessage = this;
this.#_edit.value = this.#text;
this.setState("editing");
@@ -936,8 +950,6 @@
this.#_edit.focus();
} else {
activeMessage = null;
this.#text = this.#_edit.value;
this.setState(false);
@@ -1656,11 +1668,11 @@
return _file;
}
function pushAttachment(file) {
function pushAttachment(file, message = false) {
file.id = uid();
if (activeMessage?.isUser()) {
activeMessage.addFile(file);
if (message) {
message.addFile(file);
return;
}
@@ -1717,6 +1729,62 @@
return message;
}
async function uploadToMessage(self, message) {
if (isUploading) {
return;
}
const files = await selectFile(
// the ultimate list
"text/*",
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 (!file.content) {
throw new Error("File is empty");
} else if (file.content.includes("\0")) {
throw new Error("File is not a text file");
} else if (file.content.length > 4 * 1024 * 1024) {
throw new Error("File is too big (max 4MB)");
}
},
notify
);
if (!files.length) {
return;
}
isUploading = true;
self.classList.add("loading");
const promises = [];
for (const file of files) {
promises.push(
resolveTokenCount(file.content).then(tokens => {
file.tokens = tokens;
})
);
}
await Promise.all(promises);
for (const file of files) {
pushAttachment(file, message);
}
self.classList.remove("loading");
isUploading = false;
}
$total.addEventListener("auxclick", event => {
if (event.button !== 1) {
return;
@@ -1869,60 +1937,8 @@
storeValue("message", $message.value);
});
$upload.addEventListener("click", async () => {
if (isUploading) {
return;
}
const files = await selectFile(
// the ultimate list
"text/*",
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 (!file.content) {
throw new Error("File is empty");
} else if (file.content.includes("\0")) {
throw new Error("File is not a text file");
} else if (file.content.length > 4 * 1024 * 1024) {
throw new Error("File is too big (max 4MB)");
}
},
notify
);
if (!files.length) {
return;
}
isUploading = true;
$upload.classList.add("loading");
const promises = [];
for (const file of files) {
promises.push(
resolveTokenCount(file.content).then(tokens => {
file.tokens = tokens;
})
);
}
await Promise.all(promises);
for (const file of files) {
pushAttachment(file);
}
$upload.classList.remove("loading");
isUploading = false;
$upload.addEventListener("click", () => {
uploadToMessage($upload, false);
});
$add.addEventListener("click", () => {