Skip to content

fix: make ody-copy-button work in cross-origin iframes - #12

Merged
oskarbruening merged 1 commit into
mainfrom
fix/copy-button-iframe-fallback
Jul 10, 2026
Merged

fix: make ody-copy-button work in cross-origin iframes#12
oskarbruening merged 1 commit into
mainfrom
fix/copy-button-iframe-fallback

Conversation

@oskarbruening

Copy link
Copy Markdown
Collaborator

Summary

<ody-copy-button> relied solely on navigator.clipboard.writeText, which is blocked by default in cross-origin iframes (Chrome, no allow="clipboard-write"). The promise rejected, the button flipped to its red error state, and nothing was copied — with no fallback.

This makes a synchronous document.execCommand('copy') the source of truth, so the copy runs inside the user-gesture window and works in cross-origin iframes and legacy contexts. The async Clipboard API becomes a best-effort enhancement, attempted only when the sync path can't run; its late result never touches the UI (it settles after the gesture window closes).

Changes

  • src/ui/components/copy-button.ts: add #execCopy(value) (off-screen <textarea> + execCommand('copy'), guarded for empty value and SSR/no-DOM); rewrite #onClick to copy synchronously and reflect the result immediately, falling back to best-effort async only when sync can't run.
  • test/ui/batch3b.test.ts: rewrite the copy-button suite for the sync-first behavior — sync success, sync-fail → best-effort async (async resolve and reject; late result never flips the UI), both paths unavailable, empty value short-circuit, throwing execCommand, timer-clear, disconnect-during-revert.

Unchanged contracts

  • copy CustomEvent detail { value, ok }, bubbles: true.
  • #feedback, #state, success-duration, auto-reset timer, icons, appearance classes.
  • No new attributes or public API. Behavior-only fix; no version bump.

Verification

  • tsc --noEmit: clean
  • eslint .: clean
  • vitest run --coverage: 617 passed, 95% gate green (copy-button.ts 100% stmts/funcs/lines)
  • Manual (not automated): render inside a cross-origin iframe without allow="clipboard-write" in Chrome and confirm the green check.

Once this ships, the downstream n8n connector's navigator.clipboard.writeText monkeypatch workaround can be deleted.

🤖 Generated with Claude Code

Use a synchronous execCommand('copy') as the source of truth so copying
succeeds inside cross-origin iframes (where the async Clipboard API is
blocked by default) and legacy contexts. The async
navigator.clipboard.writeText becomes a best-effort enhancement attempted
only when the sync path can't run; its late result never touches the UI,
since it settles after the user-gesture window.

The copy CustomEvent shape ({ value, ok }, bubbles) and all state/feedback
behavior are unchanged. Behavior-only fix, no public API change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@oskarbruening
oskarbruening merged commit a6a7590 into main Jul 10, 2026
2 checks passed
@oskarbruening
oskarbruening deleted the fix/copy-button-iframe-fallback branch July 10, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant