← Back to PRs

#19259: fix(ui): add legacy fallback for copy-as-markdown on non-secure contexts

by assistent-dries-robuxio open 2026-02-17 15:48 View on GitHub →
app: web-ui size: XS
## Problem The "Copy as markdown" button in the Control UI dashboard uses `navigator.clipboard.writeText()` which requires a **secure context** (HTTPS or localhost). When the dashboard is served over plain HTTP or via tunnels (e.g. Tailscale serve, LAN access), the Clipboard API silently fails and users see "Copy failed". ## Fix Add a `document.execCommand('copy')` fallback using a hidden textarea so the copy button works in all contexts. The modern Clipboard API is still preferred when available. ## Changes - `ui/src/ui/chat/copy-as-markdown.ts`: Add legacy clipboard fallback with hidden textarea approach ## Testing - Tested on HTTP dashboard access (Tailscale serve) — copy now works - Secure context (HTTPS/localhost) still uses the modern API - Fallback handles edge cases (iOS scroll, cleanup) AI-assisted: Claude (lightly tested) <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds a `document.execCommand('copy')` fallback to the "Copy as markdown" button so it works in non-secure contexts (plain HTTP, Tailscale serve, LAN access) where the Clipboard API is unavailable. The modern `navigator.clipboard.writeText()` is still preferred when available. - The fallback approach using a hidden textarea is a well-established pattern and the implementation is straightforward. - One issue: the textarea cleanup is not exception-safe — if `execCommand` throws, the textarea remains in the DOM. Should use `try/finally` to guarantee removal. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge with a minor cleanup fix for the textarea leak on exception. - The change is small, well-scoped, and addresses a real usability issue. The only concern is a minor DOM leak in an edge case (execCommand throwing), which is unlikely to cause problems in practice but should be fixed for correctness. - `ui/src/ui/chat/copy-as-markdown.ts` — textarea cleanup in the legacy fallback path should use try/finally <sub>Last reviewed commit: 490bd97</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs