From 51c80c78a4a2b8df76eabf5ad93f8fa43d96b0e0 Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Mon, 30 Mar 2026 15:52:29 +0200 Subject: [PATCH] feat(messages): paste images into chat + responsive image display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Paste handler: Ctrl+V image → uploads to /api/messages/upload-image → inserts - Fallback to base64 if upload fails - Enter sends messages with images (even without text) - CSS: responsive images in messages (max 300px height, rounded) - CSS: images in Quill editor resizable Co-Authored-By: Claude Opus 4.6 (1M context) --- static/js/conversations.js | 43 +++++++++++++++++++++++++-- templates/messages/conversations.html | 17 +++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/static/js/conversations.js b/static/js/conversations.js index 223bf98..23946cd 100644 --- a/static/js/conversations.js +++ b/static/js/conversations.js @@ -1244,9 +1244,10 @@ handler: function() { var html = state.quill.root.innerHTML; var text = state.quill.getText().trim(); - if (text) { + var hasContent = text || html.indexOf(' + state.quill.root.addEventListener('paste', function(e) { + var clipboardData = e.clipboardData || window.clipboardData; + if (!clipboardData || !clipboardData.items) return; + for (var i = 0; i < clipboardData.items.length; i++) { + var item = clipboardData.items[i]; + if (item.type.indexOf('image') !== -1) { + e.preventDefault(); + e.stopPropagation(); + var file = item.getAsFile(); + if (file) { + // Upload image as attachment + var fd = new FormData(); + fd.append('image', file, 'pasted-image.png'); + api('/api/messages/upload-image', 'POST', fd) + .then(function(result) { + if (result && result.url) { + var range = state.quill.getSelection(true); + state.quill.insertEmbed(range.index, 'image', result.url); + state.quill.setSelection(range.index + 1); + } + }) + .catch(function(err) { + // Fallback: insert as base64 + var reader = new FileReader(); + reader.onload = function(ev) { + var range = state.quill.getSelection(true); + state.quill.insertEmbed(range.index, 'image', ev.target.result); + state.quill.setSelection(range.index + 1); + }; + reader.readAsDataURL(file); + }); + } + return; + } + } + }); + // Typing indicator state.quill.on('text-change', function () { Composer.sendTyping(); diff --git a/templates/messages/conversations.html b/templates/messages/conversations.html index f515826..5baa853 100644 --- a/templates/messages/conversations.html +++ b/templates/messages/conversations.html @@ -10,6 +10,23 @@