diff --git a/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue b/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue index c2971c60d..6def771a0 100644 --- a/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue +++ b/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue @@ -171,10 +171,13 @@ const onPaste = e => { const files = e.clipboardData?.files; if (!files?.length) return; - Array.from(files).forEach(file => { - const { name, type, size } = file; - onFileUpload({ file, name, type, size }); - }); + // Filter valid files (non-zero size) + Array.from(files) + .filter(file => file.size > 0) + .forEach(file => { + const { name, type, size } = file; + onFileUpload({ file, name, type, size }); + }); }; useEventListener(document, 'paste', onPaste); diff --git a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue index 7a2a489ac..4b83556de 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue @@ -676,11 +676,18 @@ function createEditorView() { typingIndicator.stop(); emit('blur'); }, - paste: (_view, event) => { + paste: (view, event) => { if (props.disabled) return; - const data = event.clipboardData.files; - if (data.length > 0) { - event.preventDefault(); + const { files } = event.clipboardData; + if (!files.length) return; + event.preventDefault(); + // Paste text content alongside files (e.g., spreadsheet data from Numbers app) + // Numbers app includes invalid 0-byte attachments with text, so we paste the text here + // while ReplyBox filters and handles valid file attachments + const text = event.clipboardData.getData('text/plain'); + if (text) { + view.dispatch(view.state.tr.insertText(text)); + emitOnChange(); } }, }, diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue index 40127aca3..aa67cda0b 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue @@ -692,20 +692,20 @@ export default { }, onPaste(e) { // Don't handle paste if compose new conversation modal is open - if (this.newConversationModalActive) { - return; - } + if (this.newConversationModalActive) return; + const data = e.clipboardData.files; if (!this.showRichContentEditor && data.length !== 0) { this.$refs.messageInput?.$el?.blur(); } - if (!data.length || !data[0]) { - return; - } - data.forEach(file => { - const { name, type, size } = file; - this.onFileUpload({ name, type, size, file: file }); - }); + + // Filter valid files (non-zero size) + Array.from(e.clipboardData.files) + .filter(file => file.size > 0) + .forEach(file => { + const { name, type, size } = file; + this.onFileUpload({ name, type, size, file }); + }); }, toggleUserMention(currentMentionState) { this.showUserMentions = currentMentionState;