Conversation
Implements plan Phase 1: repeatable --point SELECTOR LABEL flags draw numbered markers with auto-placed labels and outlines via pre-capture DOM overlay; --highlight outlines elements; --dim dims the rest of the page. Element clips grow to include nearby markers. MCP capture gains matching annotations/highlight/dim fields; JSON reports add an annotations count (omitted when zero).
Implements plan Phase 2: repeatable --click/--fill/--hover/--press flags (--open aliases --click; text=... targets the deepest visible element containing the text) run as ordered in-page steps after the readiness gate, with per-step budgets and step-indexed errors. MCP capture gains an ordered steps array for exact mixed ordering. Adds the interact.html fixture and a browser interaction contract test.
Implements plan Phase 3: repeatable --mask covers every match with ink (or --mask-blur blur); opt-in --mask-patterns email,phone,ssn, account covers conservative pattern matches in text nodes and form values. Masks run before overlays; JSON reports count boxes (masked). MCP capture gains redact/mask_blur_px/redact_patterns. Adds the private.html fixture and a browser masking contract test.
Implements plan Phase 4: --color-scheme light|dark|system is the source of truth with --light/--dark shorthands (clap conflicts keep them exclusive); system applies no override, preserving historical output. The engine forces the scheme through the existing emulated media path; JSON reports record the effective scheme. MCP capture gains color_scheme with dark:true back-compat. Adds a browser color-scheme contract test.
Implements plan Phase 5: iris login --session NAME URL opens a visible browser for one manual sign-in and keeps the profile under the OS data dir; --session NAME captures reuse it (erroring with a login hint when unknown). Session names are validated against path escapes. MCP capture gains session with a per-session cache. Adds the session.html fixture and a browser session contract test proving persistence across launches and isolation from temp profiles.
Implements plan Phase 6: --workflow FILE (repeatable, .yaml/.yml/ .json) carries url, viewport, steps, redactions, annotations, selector, session, timeout, and output per file. Recipes convert into capture requests so files, flags, and MCP calls validate through one prepare path (proven by a full-field equivalence test). --dry-run prints the resolved plan without launching Chrome; unknown fields fail fast naming the field. YAML uses serde-saphyr. Adds the workflow.yaml fixture, a no-browser dry-run CLI test, and a README workflows section with a CI example.
Audit follow-up on annotations: --point now connects each numbered marker to its label with an orange leader line painted beneath both, so markers genuinely point at elements. Element clips already grow over overlay boxes, which now include the arrow span.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8888857eba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| element.dispatchEvent(new PointerEvent("pointerover", at)); | ||
| element.dispatchEvent(new MouseEvent("mouseover", at)); | ||
| element.dispatchEvent(new MouseEvent("mouseenter", at)); |
There was a problem hiding this comment.
Drive a real pointer for hover steps
When a target reveals content through CSS :hover, these synthetic events do not change Chrome's pointer state, so the hover rule never matches and the screenshot is taken without the menu or tooltip. The comment immediately above acknowledges this limitation; move the pointer through the CDP input domain instead of merely dispatching DOM events.
Useful? React with 👍 / 👎.
| const target = document.activeElement || document.body; | ||
| for (const type of ["keydown", "keypress", "keyup"]) {{ | ||
| target.dispatchEvent(new KeyboardEvent(type, {{ key, bubbles: true, cancelable: true, view: window }})); | ||
| }} |
There was a problem hiding this comment.
Send key presses through the browser input domain
When press is expected to submit a form with Enter, dismiss browser UI with Escape, or invoke another native keyboard action, dispatchEvent creates untrusted events and does not perform the browser's default behavior. A normal fill step also does not focus its input, so a following press commonly targets body; the step can therefore report success while leaving the page unchanged. Use real CDP keyboard input and ensure the intended control is focused.
Useful? React with 👍 / 👎.
| veil.style.cssText = css; | ||
| document.body.appendChild(veil); |
There was a problem hiding this comment.
Keep masks above top-layer elements
When the selected private content is inside an open native <dialog> or popover, appending the mask to document.body places it below the browser's top layer regardless of its maximum z-index. The dialog content therefore remains visible even though the capture reports it as masked; masks need to be rendered within the relevant top-layer container or applied outside normal document stacking.
Useful? React with 👍 / 👎.
| if (blurPx === null) css += "background:#111310;"; | ||
| else css += "background:rgba(17,19,16,.15);backdrop-filter:blur(" + blurPx + "px);" | ||
| + "-webkit-backdrop-filter:blur(" + blurPx + "px);"; |
There was a problem hiding this comment.
When callers pass --mask-blur 0 or mask_blur_px: 0, the value is accepted and this branch draws only a 15%-opaque tint with blur(0px), leaving the supposedly redacted text readable while still incrementing the masked count. Reject zero or fall back to the opaque mask so a valid redaction request cannot silently expose its target.
Useful? React with 👍 / 👎.
| /// One pre-capture interaction. Array order is the execution order. | ||
| #[derive(Clone, Debug, Deserialize, schemars::JsonSchema)] | ||
| #[serde(untagged)] | ||
| pub enum StepRequest { |
There was a problem hiding this comment.
Reject extra keys in workflow steps
When a workflow step contains multiple action keys or an extra misspelled key, these untagged struct variants ignore the unknown fields and deserialize as the first matching action. For example, {click: "#open", wait_for: "#ready"} silently executes only the click despite the documented strict workflow validation, producing a partial capture instead of failing fast. Define deny-unknown-fields step shapes before applying the untagged enum.
Useful? React with 👍 / 👎.
Summary
Implements the full six-phase capture feature plan for iris:
--point SELECTOR LABELdraws numbered markers with auto-placed labels and leader arrows;--highlightoutlines elements;--dimdims the rest of the page. Element clips grow to cover overlay boxes.--click/--fill/--hover/--press/--waitsteps (--openaliases--click;text=...targets the deepest visible match) run after the readiness gate with per-step budgets and step-indexed errors.--maskinks out matches (or--mask-blurblurs); opt-in--mask-patterns email,phone,ssn,accountcovers conservative sensitive-data patterns in text nodes and form values. Masks run before overlays.--color-scheme light|dark|systemwith--light/--darkshorthands;systempreserves historical output. JSON reports record the effective scheme.iris login --session NAME URLopens a visible browser for one manual sign-in and persists the profile under the OS data dir;--session NAMEreuses it with a login hint when unknown. Session names are validated against path escapes.--workflow FILE(.yaml/.yml/.json) carries url, viewport, steps, redactions, annotations, selector, session, timeout, and output;--dry-runprints the resolved plan without launching Chrome; unknown fields fail fast. Recipes convert into capture requests so files, flags, and MCP calls validate through one prepare path.annotations/highlight/dim, orderedsteps,redact/mask_blur_px/redact_patterns,color_scheme(withdark:trueback-compat), andsessionwith a per-session cache.Test plan
--dry-runresolves the workflow planinteract.html,private.html,session.html,workflow.yamliris login --session demo <url>theniris --session demo <url>Generated with Devin