#19259: fix(ui): add legacy fallback for copy-as-markdown on non-secure contexts
app: web-ui
size: XS
Cluster:
Web UI Enhancements and Fixes
## 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
#9716: feat(ui): add GitHub-style floating copy button to code blocks
by BigUncle · 2026-02-05
78.9%
#12719: feat(ui): add copy button to logs page
by billgetman · 2026-02-09
76.2%
#7316: fix: /chat dashboard performance
by felipcsousa · 2026-02-02
74.2%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
73.8%
#15204: fix(ui): preserve angle-bracketed text in chat
by bufordtjustice2918 · 2026-02-13
72.9%
#6819: fix(tui): handle unstructured tool results and errors in tool execu...
by TreyDong · 2026-02-02
71.9%
#9220: Fix: TUI drops API responses silently when runID already finalized
by vishaltandale00 · 2026-02-05
71.8%
#7528: feat: Adds slash command suggestions to chat
by JohnnyD1776 · 2026-02-02
71.6%
#17297: fix(tui): copy-paste inserts spaces into long tokens at wrap points...
by yinghaosang · 2026-02-15
71.6%
#4109: fix(cli): bypass config guard for tui --url
by hopenjin · 2026-01-29
71.4%