From 1eef11acbe0db63ab1189ce7ba674dc179303929 Mon Sep 17 00:00:00 2001 From: b1rdmania <102524336+b1rdmania@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:53:55 +0000 Subject: [PATCH] Fix iOS clipboard with contentEditable approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Try modern clipboard API first - For iOS: use contentEditable + special range selection - Set font-size 16px to prevent zoom on iOS - Properly throw error on failure for user feedback 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/main.ts | 57 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/main.ts b/src/main.ts index a7fbbc0..fb1f8ba 100644 --- a/src/main.ts +++ b/src/main.ts @@ -679,40 +679,61 @@ class MotifApp { } private async copyToClipboard(text: string): Promise { - // iOS Safari needs the textarea fallback - clipboard API is unreliable - // Try textarea approach first for better iOS compatibility + // Try modern clipboard API first + if (navigator.clipboard?.writeText) { + try { + await navigator.clipboard.writeText(text); + return; + } catch { + // Fall through to textarea method + } + } + + // Fallback for iOS Safari and older browsers const ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; - ta.style.left = '0'; ta.style.top = '0'; - ta.style.opacity = '0'; - ta.style.pointerEvents = 'none'; - ta.setAttribute('readonly', ''); // Prevent keyboard on iOS + ta.style.left = '0'; + ta.style.width = '2em'; + ta.style.height = '2em'; + ta.style.padding = '0'; + ta.style.border = 'none'; + ta.style.outline = 'none'; + ta.style.boxShadow = 'none'; + ta.style.background = 'transparent'; + ta.style.fontSize = '16px'; // Prevent zoom on iOS + ta.setAttribute('readonly', ''); document.body.appendChild(ta); - // iOS specific selection - const range = document.createRange(); - range.selectNodeContents(ta); - const selection = window.getSelection(); - if (selection) { - selection.removeAllRanges(); - selection.addRange(range); + // iOS needs special handling + const isiOS = /ipad|iphone|ipod/i.test(navigator.userAgent); + if (isiOS) { + ta.contentEditable = 'true'; + ta.readOnly = false; + const range = document.createRange(); + range.selectNodeContents(ta); + const sel = window.getSelection(); + if (sel) { + sel.removeAllRanges(); + sel.addRange(range); + } + ta.setSelectionRange(0, 999999); + } else { + ta.select(); } - ta.setSelectionRange(0, text.length); // iOS needs this let success = false; try { success = document.execCommand('copy'); } catch { - success = false; + // ignore } document.body.removeChild(ta); - // Fallback to modern API if execCommand failed - if (!success && navigator.clipboard?.writeText) { - await navigator.clipboard.writeText(text); + if (!success) { + throw new Error('Copy failed'); } }