Merge pull request 'Improved paste handling to support selection and providing undo support' (#74) from BordedDev/snek:feature/even-better-paste-handling into main

Reviewed-on: #74
This commit is contained in:
retoor 2025-10-05 22:07:18 +02:00
commit 87e19e3d02

View File

@ -37,10 +37,9 @@
{% include "dialog_help.html" %}
{% include "dialog_online.html" %}
<script type="module">
import { app } from "/app.js";
import { Schedule } from "/schedule.js";
import {app} from "/app.js";
// --- Cache selectors ---
// --- Cache selectors ---
const chatInputField = document.querySelector("chat-input");
const messagesContainer = document.querySelector(".chat-messages");
const chatArea = document.querySelector(".chat-area");
@ -99,50 +98,8 @@ setInterval(() => requestIdleCallback(updateTimes), 30000);
// --- Paste & drag/drop uploads ---
const textBox = chatInputField.textarea;
textBox.addEventListener("paste", async (e) => {
try {
e.preventDefault();
const uploadButton = chatInputField.fileUploadGrid;
const clipboardItems = await navigator.clipboard.read()
for (const item of clipboardItems) {
if (item.types.every(v => v === "text/plain")) {
const text = await (await item.getType("text/plain")).text();
chatInputField.value += text;
continue
}
if (item.types.every(t => t.startsWith('text/'))) {
console.log("All types are text:", item.types);
const codeType = item.types.find(t => !t.startsWith('text/plain') && !t.startsWith('text/html'));
let code = await(await item.getType(codeType ?? 'text/plain')).text();
const minIndentDepth = code.split('\n').reduce((acc, line) => {
if (!line.trim()) return acc;
const match = line.match(/^(\s*)/);
return match ? Math.min(acc, match[1].length) : acc;
}, 9000)
code = code.split('\n').map(line => line.slice(minIndentDepth)).join('\n')
chatInputField.value += `\`\`\`${codeType?.split('/')?.[1] ?? ''}\n${code}\n\`\`\`\n`;
} else {
for (const type of item.types.filter(t => !t.startsWith('text/'))) {
const blob = await item.getType(type);
const name = type.replace('/', '.')
uploadButton.uploadsStarted++
uploadButton.createTile(new File([blob], name, {type}))
}
}
}
} catch (error) {
console.error("Failed to read clipboard contents: ", error);
}
});
chatArea.addEventListener("drop", async (e) => {
e.preventDefault();
const dt = e.dataTransfer;
function uploadDataTransfer(dt) {
if (dt.items.length > 0) {
const uploadButton = chatInputField.fileUploadGrid;
@ -152,10 +109,58 @@ chatArea.addEventListener("drop", async (e) => {
if (file) {
uploadButton.uploadsStarted++
uploadButton.createTile(file)
} else {
console.error("Failed to get file from DataTransferItem");
}
}
}
}
}
textBox.addEventListener("paste", async (e) => {
try {
console.log("Pasted data:", e.clipboardData);
if (e.clipboardData.types.every(v => v.startsWith("text/"))) {
const codeType = e.clipboardData.types.find(t => !t.startsWith('text/plain') && !t.startsWith('text/html') && !t.startsWith('text/rtf'));
const probablyCode = codeType ||e.clipboardData.types.some(t => !t.startsWith('text/plain'));
for (const item of e.clipboardData.items) {
if (item.kind === "string" && item.type === "text/plain") {
e.preventDefault();
item.getAsString(text => {
const value = chatInputField.value;
if (probablyCode) {
let code = text;
const minIndentDepth = code.split('\n').reduce((acc, line) => {
if (!line.trim()) return acc;
const match = line.match(/^(\s*)/);
return match ? Math.min(acc, match[1].length) : acc;
}, 9000);
code = code.split('\n').map(line => line.slice(minIndentDepth)).join('\n')
text = `\`\`\`${codeType?.split('/')?.[1] ?? ''}\n${code}\n\`\`\`\n`;
}
const area = chatInputField.textarea
if(area){
const start = area.selectionStart
if ("\n" !== value[start - 1]) {
text = `\n${text}`;
}
document.execCommand('insertText', false, text);
}
});
break;
}
}
} else {
uploadDataTransfer(e.clipboardData);
}
} catch (error) {
console.error("Failed to read clipboard contents: ", error);
}
});
chatArea.addEventListener("drop", async (e) => {
e.preventDefault();
uploadDataTransfer(e.dataTransfer);
});
chatArea.addEventListener("dragover", e => {
e.preventDefault();