From 4c2dab541e87330b572da194e4740f5ba7c34924 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Thu, 19 Mar 2026 20:58:33 +0800 Subject: [PATCH] fix: use ClipboardEvent paste for Twitter text input (#93) Draft.js corrupts editor state when execCommand('insertText') encounters paragraph breaks (\n\n), causing content loss. ClipboardEvent paste triggers Draft.js's robust handlePastedText path instead. --- src/clis/twitter/post.ts | 9 +++++++-- src/clis/twitter/reply.ts | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/clis/twitter/post.ts b/src/clis/twitter/post.ts index cee2203..87dcd34 100644 --- a/src/clis/twitter/post.ts +++ b/src/clis/twitter/post.ts @@ -26,8 +26,13 @@ cli({ const box = document.querySelector('[data-testid="tweetTextarea_0"]'); if (box) { box.focus(); - // insertText is the most reliable way to trigger React's onChange events - document.execCommand('insertText', false, ${JSON.stringify(kwargs.text)}); + // Use ClipboardEvent paste: Draft.js handles paste robustly, + // while execCommand('insertText') corrupts state on multi-paragraph text (\n\n) + const dt = new DataTransfer(); + dt.setData('text/plain', ${JSON.stringify(kwargs.text)}); + box.dispatchEvent(new ClipboardEvent('paste', { + bubbles: true, cancelable: true, clipboardData: dt + })); } else { return { ok: false, message: 'Could not find the tweet composer text area.' }; } diff --git a/src/clis/twitter/reply.ts b/src/clis/twitter/reply.ts index 73925e0..e14530e 100644 --- a/src/clis/twitter/reply.ts +++ b/src/clis/twitter/reply.ts @@ -28,7 +28,13 @@ cli({ const box = document.querySelector('[data-testid="tweetTextarea_0"]'); if (box) { box.focus(); - document.execCommand('insertText', false, ${JSON.stringify(kwargs.text)}); + // Use ClipboardEvent paste: Draft.js handles paste robustly, + // while execCommand('insertText') corrupts state on multi-paragraph text (\n\n) + const dt = new DataTransfer(); + dt.setData('text/plain', ${JSON.stringify(kwargs.text)}); + box.dispatchEvent(new ClipboardEvent('paste', { + bubbles: true, cancelable: true, clipboardData: dt + })); } else { return { ok: false, message: 'Could not find the reply text area. Are you logged in?' }; }