From cfafc22c6263fe87843efd3295f084cec60ba58a Mon Sep 17 00:00:00 2001 From: Dorian Date: Thu, 30 Jul 2026 21:46:17 -0400 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20clipboard=20polyfill=20for=20H?= =?UTF-8?q?TTP=20(non-secure)=20contexts=20=E2=80=94=20copy=20buttons=20th?= =?UTF-8?q?rew=20writeText-of-undefined=20on=20nodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- frontend/src/main.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 945fe06..210b949 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -3,6 +3,26 @@ import { router } from './router' import App from './App.vue' import './style.css' +// Clipboard polyfill: on nodes the app is served over plain HTTP (non-secure +// context), where navigator.clipboard does not exist — every copy button would +// throw "Cannot read properties of undefined (reading 'writeText')". +if (!navigator.clipboard) { + Object.defineProperty(navigator, 'clipboard', { + value: { + async writeText(text: string) { + const ta = document.createElement('textarea') + ta.value = text + ta.style.cssText = 'position:fixed;opacity:0' + document.body.appendChild(ta) + ta.select() + document.execCommand('copy') + document.body.removeChild(ta) + }, + async readText() { return '' }, + }, + }) +} + const app = createApp(App) app.use(router) app.mount('#app')