Improved paste handling to support selection and providing undo support #74
@ -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();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user