diff --git a/src/frontend/app.js b/src/frontend/app.js
index 6fd757a..7c061f3 100644
--- a/src/frontend/app.js
+++ b/src/frontend/app.js
@@ -122,6 +122,43 @@ function showToast(msg) {
setTimeout(() => el.classList.remove('show'), 2500);
}
+function fallbackCopyText(text) {
+ try {
+ var ta = document.createElement('textarea');
+ ta.value = text;
+ ta.setAttribute('readonly', '');
+ ta.style.position = 'fixed';
+ ta.style.top = '-9999px';
+ ta.style.left = '-9999px';
+ document.body.appendChild(ta);
+ ta.focus();
+ ta.select();
+ var ok = document.execCommand('copy');
+ document.body.removeChild(ta);
+ return ok;
+ } catch (e) {
+ return false;
+ }
+}
+
+function copyText(text, successMsg) {
+ var done = function() {
+ showToast(successMsg || ('Copied: ' + text));
+ return true;
+ };
+ var fail = function() {
+ if (fallbackCopyText(text)) return done();
+ prompt('Copy this command:', text);
+ showToast(window.isSecureContext ? 'Clipboard copy failed' : 'Clipboard unavailable on non-secure origin');
+ return false;
+ };
+
+ if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
+ return navigator.clipboard.writeText(text).then(done).catch(fail);
+ }
+ return Promise.resolve(fail());
+}
+
function formatBytes(bytes) {
if (!bytes || bytes < 1024) return (bytes || 0) + ' B';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
@@ -1722,7 +1759,7 @@ function installAgent(agent) {
var overlay = document.getElementById('confirmOverlay');
document.getElementById('confirmTitle').textContent = 'Install ' + info.name;
- var html = '' + escHtml(info.cmd) + '';
+ var html = '' + escHtml(info.cmd) + '';
if (info.alt) {
html += 'or: ' + escHtml(info.alt) + '
';
}
@@ -1732,9 +1769,7 @@ function installAgent(agent) {
document.getElementById('confirmAction').textContent = 'Copy Install Command';
document.getElementById('confirmAction').className = 'launch-btn btn-primary';
document.getElementById('confirmAction').onclick = function() {
- navigator.clipboard.writeText(info.cmd).then(function() {
- showToast('Copied: ' + info.cmd);
- });
+ copyText(info.cmd, 'Copied: ' + info.cmd);
closeConfirm();
};
if (overlay) overlay.style.display = 'flex';
@@ -1756,9 +1791,7 @@ function showExportDialog() {
document.getElementById('confirmAction').textContent = 'Copy Export Command';
document.getElementById('confirmAction').className = 'launch-btn btn-primary';
document.getElementById('confirmAction').onclick = function() {
- navigator.clipboard.writeText('codedash export').then(function() {
- showToast('Copied: codedash export');
- });
+ copyText('codedash export', 'Copied: codedash export');
closeConfirm();
};
if (overlay) overlay.style.display = 'flex';
@@ -1789,9 +1822,7 @@ async function checkForUpdates() {
badge.classList.add('update-available');
badge.title = 'Click to copy update command';
badge.onclick = function() {
- navigator.clipboard.writeText('npm i -g codedash-app@latest').then(function() {
- showToast('Copied: npm i -g codedash-app@latest');
- });
+ copyText('npm i -g codedash-app@latest', 'Copied: npm i -g codedash-app@latest');
};
}
var banner = document.getElementById('updateBanner');
@@ -1807,9 +1838,7 @@ async function checkForUpdates() {
function copyUpdate() {
var cmd = 'codedash update && codedash restart';
- navigator.clipboard.writeText(cmd).then(function() {
- showToast('Copied: ' + cmd + ' (run in terminal)');
- });
+ copyText(cmd, 'Copied: ' + cmd + ' (run in terminal)');
}
function dismissUpdate() {
diff --git a/src/frontend/detail.js b/src/frontend/detail.js
index 1f71257..fd69d23 100644
--- a/src/frontend/detail.js
+++ b/src/frontend/detail.js
@@ -284,12 +284,7 @@ function copyResume(sessionId, tool) {
} else {
cmd = 'claude --resume ' + sessionId;
}
- navigator.clipboard.writeText(cmd).then(function() {
- showToast('Copied: ' + cmd);
- }).catch(function() {
- // Fallback
- prompt('Copy this command:', cmd);
- });
+ copyText(cmd, 'Copied: ' + cmd);
}
function exportMd(sessionId, project) {
diff --git a/src/frontend/leaderboard.js b/src/frontend/leaderboard.js
index b8efda5..cba0673 100644
--- a/src/frontend/leaderboard.js
+++ b/src/frontend/leaderboard.js
@@ -138,8 +138,7 @@ async function githubConnect() {
document.getElementById('githubAuthCode').textContent = data.user_code;
document.getElementById('githubAuthLink').href = data.verification_uri;
- // Copy code to clipboard
- try { navigator.clipboard.writeText(data.user_code); } catch {}
+ copyText(data.user_code, 'Copied GitHub code');
// Poll for token
var interval = (data.interval || 5) * 1000;