fix: fall back to execCommand and report failure in copy.js
commit
aa01840fix: fall back to execCommand and report failure in copy.js
navigator.clipboard.writeText was called unconditionally with no .catch(), so a rejected promise silently did nothing, and navigator.clipboard is undefined outside a secure context (plain HTTP), throwing instead of copying at all. copyText now falls back to a hidden-textarea execCommand("copy") when the Clipboard API is unavailable, and both button handlers show Copy failed on rejection instead of leaving the click looking like it did nothing.
Assisted-by: Claude:claude-sonnet-5
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
crates/git-ents-server/src/web/copy.js
@@ -1,9 +1,41 @@
+function copyText(text) {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ return navigator.clipboard.writeText(text);
+ }
+ return new Promise((resolve, reject) => {
+ const textarea = document.createElement('textarea');
+ textarea.value = text;
+ textarea.style.position = 'fixed';
+ textarea.style.opacity = '0';
+ document.body.appendChild(textarea);
+ textarea.focus();
+ textarea.select();
+ try {
+ const ok = document.execCommand('copy');
+ document.body.removeChild(textarea);
+ if (ok) {
+ resolve();
+ } else {
+ reject(new Error('execCommand copy failed'));
+ }
+ } catch (err) {
+ document.body.removeChild(textarea);
+ reject(err);
+ }
+ });
+}
+
document.querySelectorAll('[data-copy]').forEach((btn) => {
btn.addEventListener('click', () => {
- navigator.clipboard.writeText(btn.dataset.copy).then(() => {
- const label = btn.textContent;
- btn.textContent = 'Copied';
- setTimeout(() => { btn.textContent = label; }, 1200);
- });
+ const label = btn.textContent;
+ copyText(btn.dataset.copy)
+ .then(() => {
+ btn.textContent = 'Copied';
+ setTimeout(() => { btn.textContent = label; }, 1200);
+ })
+ .catch(() => {
+ btn.textContent = 'Copy failed';
+ setTimeout(() => { btn.textContent = label; }, 1200);
+ });
});
});