Improved paste handling to support selection and providing undo support
This commit is contained in:
		
							parent
							
								
									767048d79b
								
							
						
					
					
						commit
						31a754d264
					
				@ -38,7 +38,6 @@
 | 
				
			|||||||
{% include "dialog_online.html" %}
 | 
					{% include "dialog_online.html" %}
 | 
				
			||||||
<script type="module">
 | 
					<script type="module">
 | 
				
			||||||
    import {app} from "/app.js";
 | 
					    import {app} from "/app.js";
 | 
				
			||||||
import { Schedule } from "/schedule.js";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // --- Cache selectors ---
 | 
					    // --- Cache selectors ---
 | 
				
			||||||
const chatInputField = document.querySelector("chat-input");
 | 
					const chatInputField = document.querySelector("chat-input");
 | 
				
			||||||
@ -99,50 +98,8 @@ setInterval(() => requestIdleCallback(updateTimes), 30000);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// --- Paste & drag/drop uploads ---
 | 
					// --- Paste & drag/drop uploads ---
 | 
				
			||||||
const textBox = chatInputField.textarea;
 | 
					const textBox = chatInputField.textarea;
 | 
				
			||||||
textBox.addEventListener("paste", async (e) => {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
        e.preventDefault();
 | 
					 | 
				
			||||||
        const uploadButton = chatInputField.fileUploadGrid;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const clipboardItems = await navigator.clipboard.read()
 | 
					function uploadDataTransfer(dt) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        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;
 | 
					 | 
				
			||||||
    if (dt.items.length > 0) {
 | 
					    if (dt.items.length > 0) {
 | 
				
			||||||
        const uploadButton = chatInputField.fileUploadGrid;
 | 
					        const uploadButton = chatInputField.fileUploadGrid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,10 +109,58 @@ chatArea.addEventListener("drop", async (e) => {
 | 
				
			|||||||
                if (file) {
 | 
					                if (file) {
 | 
				
			||||||
                   uploadButton.uploadsStarted++
 | 
					                   uploadButton.uploadsStarted++
 | 
				
			||||||
                   uploadButton.createTile(file)
 | 
					                   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 => {
 | 
					chatArea.addEventListener("dragover", e => {
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user