Fix clipboard permission errors and add manual copy buttons

This commit is contained in:
EP
2025-03-09 19:04:52 -04:00
parent e4edc506c5
commit 50b74e6628
3 changed files with 106 additions and 65 deletions
+32 -33
View File
@@ -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;
+9 -3
View File
@@ -101,7 +101,9 @@
<div class="output-section" v-if="encodedMessage">
<div class="output-container">
<textarea readonly v-model="encodedMessage"></textarea>
<button class="copy-button" @click="copyToClipboard(encodedMessage)" title="Copy to clipboard">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
@@ -115,7 +117,9 @@
></textarea>
<div class="decoded-message" v-if="decodedMessage">
{{ decodedMessage }}
<button class="copy-button" @click="copyToClipboard(decodedMessage.substring(decodedMessage.indexOf(': ') + 2))" title="Copy decoded text">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
@@ -161,7 +165,9 @@
v-model="transformOutput"
aria-label="Transformed text output"
></textarea>
<button class="copy-button" @click="copyToClipboard(transformOutput)" title="Copy to clipboard">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
+65 -29
View File
@@ -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 = '<i class="fas fa-check"></i> 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 = '<i class="fas fa-times"></i> 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('<i class="fas fa-check"></i> 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('<i class="fas fa-check"></i> Copied!', 'success');
} else {
this.showNotification('<i class="fas fa-exclamation-triangle"></i> Copy not supported', 'error');
}
// Clean up
document.body.removeChild(textarea);
} catch (err) {
console.warn('Fallback copy method failed:', err);
this.showNotification('<i class="fas fa-exclamation-triangle"></i> 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 '';