1 Commits

Author SHA1 Message Date
de37809eec fix: Copy share URL over HTTP via execCommand fallback
Some checks failed
Deploy / deploy (push) Failing after 44s
navigator.clipboard is only available in a secure context (HTTPS or
localhost), so the Copy URL button threw over plain HTTP. Fall back to
the deprecated document.execCommand("copy") when the Clipboard API is
unavailable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 01:41:17 +00:00
2 changed files with 25 additions and 10 deletions

View File

@@ -64,16 +64,8 @@ jobs:
- name: Copy backend binary + unit file
run: |
# Copy to a temp path then atomically rename into place. You can't
# overwrite a running executable in place (ETXTBSY / "Text file busy"),
# but rename(2) just swaps the directory entry while the old inode keeps
# serving the running process until the restart step picks up the new
# binary. chmod first so the exec bit survives the rename (scp doesn't
# preserve mode without -p).
scp -i ~/.ssh/deploy_key server/share-svc \
"$DEPLOY_USER@$DEPLOY_HOST:/usr/local/bin/share-svc.new"
ssh -i ~/.ssh/deploy_key "$DEPLOY_USER@$DEPLOY_HOST" \
'chmod 0755 /usr/local/bin/share-svc.new && mv -f /usr/local/bin/share-svc.new /usr/local/bin/share-svc'
"$DEPLOY_USER@$DEPLOY_HOST:/usr/local/bin/share-svc"
scp -i ~/.ssh/deploy_key server/share-svc.service \
"$DEPLOY_USER@$DEPLOY_HOST:/etc/systemd/system/share-svc.service"

View File

@@ -192,6 +192,29 @@ function apiURL(path: string) {
return new URL(path, window.location.href).toString();
}
// Copy text to the clipboard. navigator.clipboard is only available in a
// secure context (HTTPS or localhost); over plain HTTP it's undefined, so we
// fall back to the deprecated execCommand("copy"), which still works there.
// Must be called from within a user gesture (e.g. a click handler).
async function copyToClipboard(text: string) {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
return;
}
const ta = document.createElement("textarea");
ta.value = text;
ta.style.position = "fixed";
ta.style.opacity = "0";
document.body.appendChild(ta);
ta.focus();
ta.select();
try {
document.execCommand("copy");
} finally {
document.body.removeChild(ta);
}
}
export default function CharacterSheet() {
const [activeTab, setActiveTab] = useState("main");
const [urlMode, setUrlMode] = useState(false);
@@ -516,7 +539,7 @@ export default function CharacterSheet() {
// Backend unreachable: fall back to a self-contained inline ?c= link.
shareURL = base + "?c=" + (await compressToBase64(json));
}
await navigator.clipboard.writeText(shareURL);
await copyToClipboard(shareURL);
setCopyStatus(true);
setTimeout(() => setCopyStatus(false), 2000);
}, [collectData]);