Skip to content

fix: copy Studio agent prompts in Safari#536

Merged
miguel-heygen merged 1 commit intonextfrom
fix/safari-copy-agent
Apr 28, 2026
Merged

fix: copy Studio agent prompts in Safari#536
miguel-heygen merged 1 commit intonextfrom
fix/safari-copy-agent

Conversation

@miguel-heygen
Copy link
Copy Markdown
Collaborator

Problem

The Design panel's Ask agent flow could fail to copy prompts in Safari. The modal looked like it completed the action from the user's point of view, but Safari's clipboard permission is more tightly coupled to the original user gesture than Chromium's.

The submit handler was doing an async project-file fetch before calling navigator.clipboard.writeText(). In Safari, that can lose the trusted click/key gesture by the time the copy runs, so the prompt never reaches the system clipboard.

What this fixes

  • Adds a shared Studio clipboard helper that handles browser-specific copy ordering.
  • Uses the synchronous selection copy path first in Safari, where preserving the user gesture matters most.
  • Keeps Chromium on navigator.clipboard.writeText() first, avoiding the automation/system-clipboard mismatch from using execCommand too eagerly there.
  • Preloads the selected element's source snippet when the Ask agent modal opens instead of waiting until submit.
  • Falls back to the selected runtime element's outerHTML if the source snippet has not loaded yet.
  • Routes the other Studio copy affordances through the shared helper so prompt, lint, timeline, and asset-path copy behavior stay consistent.
  • Shows a Studio toast if the Design panel prompt copy fails instead of closing the modal with a false success state.

Root cause

The Design panel Ask agent submit path mixed two operations with different timing requirements: it fetched the project source asynchronously, then tried to copy the generated prompt. Safari can reject clipboard writes after that async boundary because the write is no longer directly attached to the user's click or Cmd+Enter gesture.

The older fallback also only ran after navigator.clipboard.writeText() failed, which is too late for Safari's stricter gesture handling in this flow. Moving source lookup ahead of the submit action and preferring a synchronous copy path in Safari keeps the actual copy inside the trusted interaction.

Verification

Local checks

  • bun run --filter @hyperframes/studio test -- src/utils/clipboard.test.ts -> 4 tests pass
  • bun run --filter @hyperframes/studio test -> 23 files pass, 278 tests pass
  • bun run --filter @hyperframes/studio typecheck
  • bun run --filter @hyperframes/studio build
  • bunx oxlint packages/studio/src/utils/clipboard.ts packages/studio/src/utils/clipboard.test.ts packages/studio/src/App.tsx packages/studio/src/components/LintModal.tsx packages/studio/src/player/components/EditModal.tsx packages/studio/src/components/sidebar/AssetsTab.tsx -> 0 warnings, 0 errors
  • bunx oxfmt --check packages/studio/src/utils/clipboard.ts packages/studio/src/utils/clipboard.test.ts packages/studio/src/App.tsx packages/studio/src/components/LintModal.tsx packages/studio/src/player/components/EditModal.tsx packages/studio/src/components/sidebar/AssetsTab.tsx
  • git diff --check
  • Lefthook pre-commit -> lint, format, typecheck pass
  • Lefthook commit-msg -> commitlint pass

Browser verification

  • Started Studio locally from this branch against a scratch safari-copy-agent project.
  • Used agent-browser to load Studio, select a layer from the preview into the Design panel, open Ask agent, enter an instruction, and run the copy flow.
  • Recorded the agent-browser-driven flow.
  • Opened the same local Studio project in Safari.
  • Selected the layer in the Design panel, opened Ask agent, typed Make the headline larger, clicked Copy prompt, and verified the Design panel changed to Prompt copied.
  • Verified the macOS system clipboard with pbpaste; it contained the generated ## HyperFrames element edit request v1 prompt and the typed instruction.

Notes

  • Local screenshots and recordings are kept under qa-artifacts/safari-copy-agent/ and are intentionally not committed.
  • Safari WebDriver / Apple Events JavaScript execution were not enabled on this machine, so the Safari pass used macOS UI automation plus pbpaste for clipboard verification. The agent-browser recording covers the same Studio flow in the required automation path.

@miguel-heygen miguel-heygen requested review from jrusso1020 and vanceingalls and removed request for jrusso1020 April 28, 2026 19:50
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the structural fix is the actually-correct one.

Pre-loading agentPromptTagSnippet on selection (not on copy click) is the right shape — Safari's navigator.clipboard.writeText requires synchronous user activation, and the previous async fetch chain broke that even with the most permissive clipboard API. Race-safety on stale fetch via domEditSelectionRef.current !== selection guard is correct.

copyTextToClipboard has solid fallback ordering: Safari → execCommand textarea → async clipboard → execCommand. UA sniffing is fragile in principle, but the worst-case degradation is a Safari user falling back to async clipboard which usually works for direct-button-click flows. focus({ preventScroll: true }) is a nice detail. try/finally removes the textarea on error.

Test coverage in clipboard.test.ts exercises Safari path, non-Safari path, async-failure fallback, and both-paths-fail. @hyperframes/studio 278/278 pass locally.

— Review by Rames Jusso

Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Staff review: approved.

Preloading the source snippet before submit is the important structural fix because it keeps Safari's copy operation tied to the user gesture. Routing the other copy affordances through the shared helper is also a good consolidation, and the error toast avoids the previous false-success behavior. I did not find any blocking issues.

@miguel-heygen miguel-heygen force-pushed the fix/safari-copy-agent branch from d7e6a0c to 64d8b19 Compare April 28, 2026 20:46
@miguel-heygen miguel-heygen merged commit 4c0e997 into next Apr 28, 2026
9 checks passed
@miguel-heygen miguel-heygen deleted the fix/safari-copy-agent branch April 28, 2026 20:47
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.

3 participants