From 50b74e6628f8ef8bd5ad148d8b85b61f229fc528 Mon Sep 17 00:00:00 2001 From: EP Date: Sun, 9 Mar 2025 19:04:52 -0400 Subject: [PATCH] Fix clipboard permission errors and add manual copy buttons --- css/style.css | 65 ++++++++++++++++++----------------- index.html | 12 +++++-- js/app.js | 94 +++++++++++++++++++++++++++++++++++---------------- 3 files changed, 106 insertions(+), 65 deletions(-) diff --git a/css/style.css b/css/style.css index a1ff505..e59c158 100644 --- a/css/style.css +++ b/css/style.css @@ -210,33 +210,7 @@ button { transition: all 0.3s; } -.copy-button { - position: absolute; - top: 8px; - right: 8px; - padding: 6px; - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - background: var(--button-bg); - border: 1px solid var(--input-border); - border-radius: 4px; - color: var(--text-color); - cursor: pointer; - transition: all 0.2s ease; -} - -.copy-button:hover { - background: var(--button-hover-bg); - border-color: var(--accent-color); - color: var(--accent-color); -} - -.copy-button:active { - transform: translateY(1px); -} +/* Copy button styling moved to a single definition below */ button:hover { background-color: var(--button-hover-bg); @@ -390,21 +364,46 @@ button:hover { position: absolute; top: 8px; right: 8px; - padding: 8px; + padding: 6px; background: var(--button-bg); - border: none; - border-radius: 6px; + border: 1px solid var(--input-border); + border-radius: 4px; color: var(--text-color); - opacity: 0.7; + opacity: 0.8; + cursor: pointer; transition: all 0.2s ease; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + z-index: 10; } .copy-button:hover { - opacity: 1; background: var(--button-hover-bg); - color: var(--main-bg-color); + border-color: var(--accent-color); + color: var(--accent-color); + opacity: 1; } +.copy-button:active { + transform: translateY(1px); +} + +.decoded-message { + position: relative; + padding: 12px; + background: var(--input-bg); + border-radius: 4px; + margin-top: 12px; + border: 1px solid var(--input-border); + font-family: 'Fira Code', monospace; + word-break: break-word; +} + +/* Copy button hover styling is defined above */ + .form-group label { display: flex; align-items: center; diff --git a/index.html b/index.html index 49e3772..07f6cf4 100644 --- a/index.html +++ b/index.html @@ -101,7 +101,9 @@
- +
@@ -115,7 +117,9 @@ >
{{ decodedMessage }} - +
@@ -161,7 +165,9 @@ v-model="transformOutput" aria-label="Transformed text output" > - + diff --git a/js/app.js b/js/app.js index a66788b..9e684d6 100644 --- a/js/app.js +++ b/js/app.js @@ -68,13 +68,13 @@ new Vue({ if (this.activeSteg === 'invisible') { this.encodedMessage = window.steganography.encodeInvisible(this.emojiMessage); - this.copyToClipboard(this.encodedMessage); + // Don't auto-copy to avoid clipboard permission errors } else if (this.selectedCarrier) { this.encodedMessage = window.steganography.encodeEmoji( this.selectedCarrier.emoji, this.emojiMessage ); - this.copyToClipboard(this.encodedMessage); + // Don't auto-copy to avoid clipboard permission errors } }, autoDecode() { @@ -88,7 +88,7 @@ new Vue({ if (result) { this.decodedMessage = `Decoded (${result.method}): ${result.text}`; - this.copyToClipboard(result.text); + // Don't auto-copy to avoid clipboard permission errors } else { this.decodedMessage = 'No encoded message detected'; } @@ -101,35 +101,71 @@ new Vue({ async copyToClipboard(text) { if (!text) return; - try { - await navigator.clipboard.writeText(text); - - // Show a brief notification - const notification = document.createElement('div'); - notification.className = 'copy-notification'; - notification.innerHTML = ' Copied!'; - document.body.appendChild(notification); - - // Remove after animation - setTimeout(() => { - notification.classList.add('fade-out'); - setTimeout(() => document.body.removeChild(notification), 300); - }, 1000); - } catch (err) { - console.error('Failed to copy text:', err); - // Show error notification - const notification = document.createElement('div'); - notification.className = 'copy-notification error'; - notification.innerHTML = ' Copy failed'; - document.body.appendChild(notification); - - setTimeout(() => { - notification.classList.add('fade-out'); - setTimeout(() => document.body.removeChild(notification), 300); - }, 1000); + // Don't auto-copy in preview/iframe environments to avoid permission errors + // Only copy when explicitly triggered by a button click + const isExplicitUserAction = event && event.isTrusted; + + // Only try to copy if we're not in a restricted environment or it's a direct user action + if (isExplicitUserAction) { + try { + await navigator.clipboard.writeText(text); + + // Show a success notification + this.showNotification(' Copied!', 'success'); + } catch (err) { + console.warn('Clipboard access not available:', err); + + // Try fallback method for copying (textarea method) + this.fallbackCopy(text); + } } }, + fallbackCopy(text) { + try { + // Create temporary textarea + const textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; // Avoid scrolling to bottom + document.body.appendChild(textarea); + textarea.select(); + + // Try the copy command + const successful = document.execCommand('copy'); + + // Show appropriate notification + if (successful) { + this.showNotification(' Copied!', 'success'); + } else { + this.showNotification(' Copy not supported', 'error'); + } + + // Clean up + document.body.removeChild(textarea); + } catch (err) { + console.warn('Fallback copy method failed:', err); + this.showNotification(' Copy not supported', 'error'); + } + }, + + showNotification(message, type = 'success') { + // Create notification element + const notification = document.createElement('div'); + notification.className = `copy-notification ${type}`; + notification.innerHTML = message; + document.body.appendChild(notification); + + // Remove after animation + setTimeout(() => { + notification.classList.add('fade-out'); + setTimeout(() => { + if (notification.parentNode) { + document.body.removeChild(notification); + } + }, 300); + }, 1000); + }, + // Universal Decoder - tries all decoding methods universalDecode(input) { if (!input) return '';