Skip to content

Add mobile Safari (iPadOS) version of the browser feedback extension#785

Open
selfcontained wants to merge 3 commits into
mainfrom
agt_ea6386815302/agent-815302
Open

Add mobile Safari (iPadOS) version of the browser feedback extension#785
selfcontained wants to merge 3 commits into
mainfrom
agt_ea6386815302/agent-815302

Conversation

@selfcontained

Copy link
Copy Markdown
Owner

Summary

Ports the Dispatch browser feedback extension (#783) to mobile Safari so page feedback can be sent to agents while testing on an iPad. Same server-side pairing/token/submission system — zero server changes — with a mobile-first client flow.

How it works on iPad

  1. Tap the Dispatch extension in Safari's toolbar → popup handles connect/pairing (first run) or a single Select element button.
  2. Everything else happens in a transient in-page overlay: tap an element (page clicks are blocked while aiming, scrolling still works), refine with ‹ Parent / Child › toolbar buttons, confirm, then a bottom-sheet card (agent picker + comment + Send) that releases page input. On send/cancel every injected node is removed.
  3. Pairing survives the popup being dismissed: the background script owns the poll loop and persists the pending pairing, so reopening the popup after approving in Dispatch completes the connection.

Implementation

  • Shared core extraction: worker-core.ts (fetch/auth/pairing/submission handling) pulled out of the Chrome service worker, which is now a thin shell — Chrome behavior unchanged. extension-api.ts (browser ?? chrome) is the entire Safari compat layer.
  • Safari background (src/safari/): pairing-session.ts (persisted, resumable poll; cancel-after-late-approval disconnects), overlay-session.ts (activeTab-based injection with ready-probe retries, tab lifecycle cleanup).
  • Overlay (src/safari/overlay/): pointer-event tap detection tuned for touch, pure refine.ts parent/child trail, shadow-DOM card, visualViewport math for pinch-zoom/keyboard, idempotent retry via stable clientSubmissionId.
  • Build: pnpm --filter @dispatch/browser-extension build:safaridist/safari/unpacked (popup, classic-script background, overlay IIFE, Safari manifest). Chrome zip pipeline untouched.
  • Xcode project checked in at apps/browser-extension/safari/ (converter shell, references the dist directly); TestFlight/simulator steps in the README. MARKETING_VERSION sync is test-enforced.
  • Web settings: Chrome / "Safari on iPad" toggle in the install guide.
  • Drive-by: Chrome manifest.json version was stale (0.28.4 vs package 0.29.0) — fixed; the sync test now passes again.

Testing

  • 97 unit tests (extension) incl. new pairing-session (fake timers), popup reducer, refine, viewport, manifest/pbxproj version sync.
  • New Playwright harness (e2e/browser-extension-overlay.spec.ts) drives the built overlay bundle headlessly with a touch context and stubbed extension bridge: tap → refine → card → failed send → retried send with the same clientSubmissionId → teardown.
  • Full suite: pnpm run check, pnpm run test, pnpm run finalize:web, pnpm run test:e2e (184 passed after fixing the pairing-copy assertion).
  • iPad Pro 11" simulator: app builds, installs, launches; Safari manifest + all bundle files verified inside the built .appex.

Needs a human (can't be automated)

  • Set your Team in Xcode signing, archive, upload to App Store Connect, install via TestFlight.
  • On-device validation of touch picking, keyboard/pinch-zoom behavior, and the per-site access grant flow (top open risk: whether permissions.request prompts for the Dispatch origin on iPadOS — the fallback guidance paths are built in).

🤖 Generated with Claude Code

Ports the Dispatch browser feedback extension to Safari Web Extensions with
a mobile-first flow: the popup handles connect + arming, and element picking
(tap + parent/child refine), the comment card, and Send all happen in a
transient in-page overlay that removes itself when done.

- Extract shared worker core from the Chrome service worker (behavior
  unchanged); share element-context, dispatch-url, feedback-form, types
- Safari background owns pairing polling (popup dies when the verification
  tab opens) with a persisted pending record that resumes across worker death
- Tap + refine overlay: capture-phase aiming that blocks clicks but keeps
  scrolling, shadow-DOM comment card with visualViewport keyboard handling,
  idempotent submission retry via stable clientSubmissionId
- Checked-in Xcode project (safari-web-extension-converter shell) that
  references dist/safari/unpacked; TestFlight steps documented in README
- Web settings: Chrome / Safari on iPad install guide toggle
- Playwright harness drives the built overlay headlessly with a stubbed
  extension bridge; unit tests for refine/popup/pairing/viewport/manifests
- Fix stale Chrome manifest version (0.28.4 -> 0.29.0)

Co-Authored-By: Claude Fable 5 <[email protected]>
@selfcontained

Copy link
Copy Markdown
Owner Author

Full end-to-end validation on iPad Pro 13" simulator (driven via AXe HID automation):

  1. ✅ Extension enabled in Settings → Apps → Safari → Extensions; All Websites → Allow
  2. ✅ Toolbar button opens the popup; connect form → insecure-HTTP acknowledgement gate → "Connect anyway"
  3. ✅ Verification tab opened (popup destroyed, as expected); approval card showed code in the web UI
  4. Background pairing resume proven: Safari killed the background worker after the popup closed, so the poll died mid-pairing — reopening the popup completed the exchange in one call ("Browser connected to Dispatch.")
  5. ✅ Paired browser listed as "Safari on iPadOS · 304A" (device-name heuristic correct in a worker context)
  6. ✅ Select element → popup self-closes → in-page hint → tap highlights element → ‹ Parent / Child › refinement → Use ✓
  7. ✅ Comment card loaded the real agent list (seeded theme polish agent) via the authenticated background fetch, typed a comment with the on-screen keyboard flow, Send
  8. ✅ Submission persisted in browser_feedback_submissions with full comment + element context; card surfaced Prompt delivery failed. with the comment preserved for retry (expected — the seeded demo agent has no real process behind it)

Remaining human steps are unchanged: TestFlight signing/upload and real-device touch/pinch-zoom feel.

selfcontained and others added 2 commits July 16, 2026 21:48
Sets DEVELOPMENT_TEAM (ML8BQ6D727, same team as the Mac release signing)
on all Safari app/extension configs so automatic signing works out of the
box, and updates the TestFlight README steps accordingly. Device archive
verified headlessly; App Store export still needs an ASC API key or the
Xcode GUI to mint distribution profiles.

Co-Authored-By: Claude Fable 5 <[email protected]>
Matches the Mac binary signing convention (dev.bradharris.dispatch):
app is dev.bradharris.dispatch.feedback, extension appends .extension.
Device archive re-verified with automatic signing.

Co-Authored-By: Claude Fable 5 <[email protected]>
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