Skip to content

feat(peek): in-page action feedback — visual cue when peek acts#90

Merged
harry-harish merged 11 commits into
mainfrom
feat/peek-action-feedback
Jun 16, 2026
Merged

feat(peek): in-page action feedback — visual cue when peek acts#90
harry-harish merged 11 commits into
mainfrom
feat/peek-action-feedback

Conversation

@harry-harish

@harry-harish harry-harish commented Jun 16, 2026

Copy link
Copy Markdown
Member

Summary

Closes a UX gap in peek's act write-path: when execute_action mutates a page (fill/click/etc.), there was no on-page indication — and at Level 4 (auto-allow) not even a confirm banner, so actions landed silently. This adds an automatic, transient, per-verb visual cue so a human can see where peek acted and what it did.

  • Lifecycle: scroll the target into view (only if off-screen) → act → confirm flash.
  • Per-verb effects: type/fill → green ring; click → indigo ripple; enter → amber ripple; dblclick → double ripple; scroll→element → ring + pulse; navigate/reload/back/forward → corner toast on the destination page.
  • rrweb-invisible: every cue is built inside a closed shadow root and its data-peek-fx host is in RECORDER_BLOCK_SELECTOR, so cues never appear in recordings and leave no placeholder box.
  • Never blocks the agent: fire-and-forget after the action result returns; a feedback failure can't alter the action verdict.
  • Egress-safe: geometry only — no typed/field values are ever rendered.
  • Default-on, with a Show action feedback side-panel toggle (peek:showActionFeedback); honors prefers-reduced-motion (static cue) and @media print.

No new MCP tool, no action-schema/permission change. Extension is private:true → no changeset.

How it works

dispatchInMainWorld (SW) injects dispatchAction (now with bringIntoView) into the page MAIN world, returns the result to the agent, then fires emitActionFeedback: getShowActionFeedback() gate → elementFeedbackFor/pageToastFor decision → injects showElementFeedback / (post-load) showPageToast (src/permissions/action-feedback.ts). Mirrors the existing highlight.ts / shield/view.ts closed-shadow idiom.

9 commits, built via subagent-driven TDD (each: implement → spec review → code-quality review). Design + plan: rrweb-stack-private/docs/specs/2026-06-16-peek-action-feedback-{design,plan}.md.

Test plan

  • pnpm --filter @peekdev/extension test554 tests green (incl. closed-shadow construction, per-verb nodes, egress invariant, self-removal, MAIN-world serialization boundary, scroll-in gating, decision helpers)
  • tsc --noEmit clean; wxt build + assert:recorder-iife pass
  • Manual (Task 10): load the unpacked build, drive type/click/navigate at Level 3/4 → confirm cue appears, scrolls off-screen targets into view, fades; confirm cues are absent from the recorded replay (no placeholder box); toggle off → no cue; reduced-motion → static cue.

Notes

  • Deferred (not in this PR): migrating the Level-2 suggest highlight (.__peek_highlight__, light-DOM) to a closed-shadow host. The design suggested also adding it to RECORDER_BLOCK_SELECTOR, but blocking a visible element leaves a frozen placeholder box on replay — so its proper fix (closed-shadow migration) is deferred; the new action-feedback cues are fully invisible. Also deferred: automated Playwright e2e for the visual + capture-absence, real click coordinates.
  • Branch is based on c818aab; origin/main has since advanced (Windows hardening), and background.ts changed on both sides — may need a rebase to resolve background.ts (Windows guards + feedback wiring should coexist).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Added an “Action feedback” toggle to the side panel (persisted across sessions).
    • Added in-page visual cues for element actions and corner toast notifications for page-level actions after navigation-like events.
  • Enhancements
    • Improved action visibility by scrolling off-screen targets into view before interacting.
  • Bug Fixes
    • Prevented action-feedback markers from being captured in recordings.
  • Tests
    • Added/expanded unit and end-to-end coverage for element cues, page toasts, toggle persistence, and scrolling behavior.

harry-harish and others added 9 commits June 16, 2026 10:58
…ctor

Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Before peek mutates an element (click / type / dblclick / enter), call
bringIntoView() — a nested helper inlined inside dispatchAction so it
survives MAIN-world executeScript serialisation. Only scrolls when the
target is not already fully visible in the viewport; no-ops for on-screen
elements to avoid gratuitous re-centering on every action.

Also aligns the explicit `scroll` verb to use the same
{ block: 'center', inline: 'nearest' } options for consistency.

Adds 3 new dispatcher tests (42 total); full-suite 554/554 green.

Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f2926eb-511f-40e8-a09f-93d8da1bc41d

📥 Commits

Reviewing files that changed from the base of the PR and between 4e960e4 and de59e14.

📒 Files selected for processing (1)
  • packages/peek-extension/entrypoints/background.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/peek-extension/entrypoints/background.ts

📝 Walkthrough

Walkthrough

Introduces in-page visual action feedback (element ripple/ring and page-level toast) injected via chrome.scripting.executeScript into the MAIN world after each dispatched action. Adds a persistent toggle in chrome.storage.sync, a side panel checkbox UI, a new ACTION_FEEDBACK_HOST_ATTR constant blocked from rrweb recording, scroll-into-view pre-mutation in the dispatcher, and comprehensive test coverage.

Changes

In-Page Action Feedback

Layer / File(s) Summary
Constants and storage
packages/peek-extension/src/constants.ts, packages/peek-extension/src/indicators/storage.ts
Adds ACTION_FEEDBACK_HOST_ATTR and expands RECORDER_BLOCK_SELECTOR to block the feedback host from rrweb; adds SHOW_ACTION_FEEDBACK_KEY, getShowActionFeedback, and setShowActionFeedback backed by chrome.storage.sync.
Feedback CSS, injection functions, and planning helpers
packages/peek-extension/src/permissions/action-feedback.ts
Defines FEEDBACK_CSS (ring/ripple/toast animations, reduced-motion, print gating), serializable showElementFeedback and showPageToast injection functions, and elementFeedbackFor/pageToastFor action-to-plan mapping helpers.
Scroll-into-view before mutations
packages/peek-extension/src/permissions/dispatcher.ts
Adds inlined bringIntoView helper wired into click, type, enter, dblclick paths; updates scroll action to use { block: 'center', inline: 'nearest' }.
Background orchestration
packages/peek-extension/entrypoints/background.ts
Adds imports for action-feedback decision helpers and injection targets; introduces scheduleActionToast (waits for tab navigation to complete, then injects page toast) and emitActionFeedback (reads toggle, picks element vs page cue, fires chrome.scripting.executeScript); dispatchInMainWorld triggers emitActionFeedback fire-and-forget on success.
Side panel toggle
packages/peek-extension/entrypoints/sidepanel/sections/ActionFeedbackToggle.tsx, packages/peek-extension/entrypoints/sidepanel/App.tsx
New ActionFeedbackToggle React component with optimistic state, async load, and write-failure rollback; mounted in App beneath RecordingBorderToggle.
Unit and integration tests
packages/peek-extension/src/__tests__/action-feedback.test.ts, .../__tests__/dispatcher.test.ts, .../__tests__/indicators-storage.test.ts, .../__tests__/shield-recorder-invisibility.test.ts
Full coverage for element/page feedback DOM structure, MAIN-world serialization, timer removal, planning helper mapping, dispatcher scroll behavior, storage round-trips, and updated recorder block selector assertions.
E2E browser tests
packages/peek-extension/e2e/action-feedback.spec.ts
Playwright tests in real Chromium verifying host presence, closed shadow root behavior, display:contents styling, timer self-removal, and no-op handling for missing selectors.

Sequence Diagram(s)

sequenceDiagram
  participant BackgroundWorker
  participant dispatchInMainWorld
  participant emitActionFeedback
  participant getShowActionFeedback
  participant scheduleActionToast
  participant chrome.scripting

  BackgroundWorker->>dispatchInMainWorld: execute action
  dispatchInMainWorld->>dispatchInMainWorld: await MAIN-world dispatch → finalResult
  dispatchInMainWorld->>emitActionFeedback: fire-and-forget (action, tabId)
  emitActionFeedback->>getShowActionFeedback: read chrome.storage.sync
  alt feedback disabled
    emitActionFeedback-->>emitActionFeedback: return
  else element verb (click/type/dblclick/enter/scroll)
    emitActionFeedback->>chrome.scripting: executeScript(showElementFeedback, args)
  else page verb (navigate/reload/back/forward)
    emitActionFeedback->>scheduleActionToast: schedule(tabId, plan)
    scheduleActionToast->>scheduleActionToast: wait for tab.status === complete
    scheduleActionToast->>chrome.scripting: executeScript(showPageToast, args)
  end
  dispatchInMainWorld-->>BackgroundWorker: return finalResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Cubenest/rrweb-stack#72: Directly modifies dispatcher.ts dispatchAction implementation (inlining resolveElement for MAIN-world serialization), the same function this PR patches with bringIntoView and scrollIntoView option changes.
  • Cubenest/rrweb-stack#37: Establishes the dispatchInMainWorld plumbing in background.ts that this PR extends to fire emitActionFeedback as a side effect after successful dispatch.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature: in-page visual feedback when peek executes actions. It is concise, specific, and clearly communicates the primary change across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/peek-action-feedback

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 16, 2026

@harish-captain harish-captain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving.

  • CI green: ci, ci-windows, dco, e2e-peek, e2e-wdio all pass.
  • CodeRabbit: approved, no actionable comments.
  • Full unit suite (554) + e2e green; adversarial whole-feature review found no correctness/security issues.
  • Closed-shadow cues confirmed rrweb-invisible; egress-safe (geometry only); fire-and-forget (never blocks the act).

Non-blocking: CodeRabbit docstring-coverage advisory (76.47% vs 80%). Branch is BEHIND main — Update branch before merge.

Exercises showElementFeedback/showPageToast in real Chromium (closed shadow
root, display:contents host, real-timer self-removal, missing-target no-op) —
the invariants jsdom can't observe. Converts the plan's deferred e2e item into
a CI regression guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>

@harish-captain harish-captain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-approving on 4e960e4 (added the real-browser Playwright e2e for the closed-shadow cues).

  • All CI green on this commit: ci, ci-windows, dco, e2e-peek (now runs the new action-feedback.spec.ts — closed-shadow render, display:contents host, real-timer self-removal), e2e-wdio.
  • CodeRabbit re-reviewed: no actionable comments.
  • Verified working in real Chromium before merge.

Branch is BEHIND main — Update branch before merge.

@harry-harish harry-harish merged commit eacde39 into main Jun 16, 2026
5 checks passed
@harry-harish harry-harish deleted the feat/peek-action-feedback branch June 16, 2026 09:27
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.

2 participants