Conversation
… choices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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: 95daa761a3
ℹ️ 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".
| function syncDisabled(entry) { | ||
| entry.offerButton.disabled = isDisabled(entry.pair.offer); | ||
| entry.declineButton.disabled = isDisabled(entry.pair.decline); |
There was a problem hiding this comment.
Avoid observing writes to the replacement buttons
When either original control remains disabled and any observed DOM mutation invokes reconcile, syncDisabled writes disabled = true to the already-disabled replacement button. The observer covers the entire body, including that button, so the attribute write schedules another callback and can create an endless mutation cycle that starves a dynamic checkout page. Compare the current state before writing or exclude the generated panel from observation.
Useful? React with 👍 / 👎.
| return; | ||
| } | ||
| unmark(pair); | ||
| entry.pair = replacement; |
There was a problem hiding this comment.
Refresh displayed labels after a control rerender
When a checkout rerenders the same-named controls after changing the price, replacement contains the new price but assigning it here never updates offerButton.textContent, declineButton.textContent, or the title initialized in apply. The panel can therefore continue displaying the old price while clicking the newly rendered control at a different price; recompute the generated labels whenever the pair is replaced.
Useful? React with 👍 / 👎.
| return; | ||
| } | ||
| entry.observer.disconnect(); | ||
| const replacement = findPairs().find((p) => p.offerName === pair.offerName && p.declineName === pair.declineName); |
There was a problem hiding this comment.
Match rerendered controls within their original scope
When a page contains multiple add-ons with identical generic labels, such as several Add/No thanks pairs, rerendering a later applied pair makes this global first-match select an earlier still-live pair. The extension then hides and forwards clicks to the wrong add-on while leaving the actual replacement untouched; constrain reconciliation using the original container or another stable positional identity rather than names alone.
Useful? React with 👍 / 👎.
Why
Checkout pages often put a large filled "Upgrade" button next to a small grey "Continue" link, so declining the upsell takes more effort than accepting it. Clean Choice finds those pairs and, when asked, shows both options as equal buttons.
Summary
clean-choice/package: a Manifest V3 extension (activeTab,scripting,storage) that runs only when its toolbar button is clicked.click(). Undo and Off for this site restore the page.Test plan
bun testinclean-choice/: 13 pass, 0 failtsc --noEmitis clean🤖 Generated with Claude Code