True when two BadgeOutputs would put the badge in the same visible
state. Used to skip redundant re-applies within a session — most importantly,
so a repeated animate doesn't restart (and visibly reset) the ellipsis cycle.
Decides whether an output actually needs to be written to the badge, given the
badge's current on-screen text. This is what prevents the open-with-results
flicker: the toolbar badge persists across popup opens, so if it already shows
the desired text (or is already empty), re-applying would blank-then-rewrite it
(BadgeAnimator.setText clears before setting) — a visible flash.
Mount-once hook that subscribes to all search-lifecycle events plus
IDB_SEARCH_RESULTS_CLEARED, runs them through reduceBadge, and applies
the result to the badge. Call this exactly once, near the top of App, before
any code that emits search events.
Internal badge state tracked across events.
A badge-lifecycle event fed to reduceBadge.
What the controller should render on the badge after handling an event.
The initial badge state (idle, no results).
Pure reducer: given the previous BadgeState and an event, returns the next state and the resulting BadgeOutput. No side effects, so it can be unit-tested without a DOM or chrome.action mock.