Skip to content

fix(pi): restore the "More voices" door in Pi's redesigned in-chat menu (#491)#496

Merged
rosscado merged 1 commit into
mainfrom
fix/491-pi-more-voices-door
Jul 4, 2026
Merged

fix(pi): restore the "More voices" door in Pi's redesigned in-chat menu (#491)#496
rosscado merged 1 commit into
mainfrom
fix/491-pi-more-voices-door

Conversation

@rosscado

@rosscado rosscado commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Why

On live pi.ai the SayPi voice block never renders in the in-chat menu (#491) — users see only Pi 1–8. Root cause, confirmed live (Layer-4 CDP, headed real Chrome, main build): Pi fully redesigned its in-chat voice menu and SayPi's integration has been dark against it (the div.t-action-m anchor is unchanged since Feb 2024).

What Pi does now:

  • Lazily builds the menu on expand, tears it down on collapse. Collapsed, .saypi-audio-controls holds only a pill (two <button>s: Turn voice off, Toggle voice menu) — no voice list exists. On expand Pi swaps in a rounded-xl card with the voice list; on collapse it's destroyed and rebuilt next time.
  • Native voice rows are <div>s now, not <button>s (div.cursor-pointer > span "Pi 1").
  • SayPi's #saypi-voice-menu was an empty fallback <div> appended outside Pi's card, never getting .expanded.

So all three assumptions were stale: div.t-action-m is gone, the firstChild-<button> expand heuristic watched the wrong depth for a shape Pi no longer produces (never fired), and button-only adoption can't see div rows. Not a #492 regression (A/B-confirmed pre-existing).

What changed (door-first, founder-scoped)

Rather than re-inject a full inline voice block into Pi's ephemeral, arbitrary-Tailwind, div-based menu — fragile against Pi's ~yearly redesigns — restore the robust path: the "More voices" door, so Pi users reach the full catalog + ▶ previews in the extension's Voices settings.

  • Observe the audio-controls SUBTREE (Pi's pill↔card swap happens deep, not as a direct child) and, whenever the menu is open, ensure the door is the last row of Pi's live list. Idempotent; re-injects after each rebuild-on-expand.
  • Anchor on the most churn-resistant hooks: the stable Toggle voice menu aria-label → the rounded-xl card → the flex.flex-col.gap-1 list.
  • Clone a native row's class list to build the door so it renders Pi-native (Pi's own utilities are already compiled — SayPi can't author min-h-11/!bg-secondary-default itself), plus role=button + Enter/Space for keyboard a11y.

Deliberately deferred

Inline SayPi voice rows on Pi's in-chat menu are a follow-up (see the tracking issue). This keeps #483 (Pi ▶ on inline rows) deferred with them — there are no inline rows to attach a ▶ to under door-first.

Tests

  • New test/chatbots/PiVoiceMenu-more-voices-door.spec.ts (7 cases) with a fixture that faithfully reproduces Pi's current live DOM (collapsed pill vs. expanded card): door injects into the live list only when expanded, idempotent, last-child placement, native-clone styling, click→openSettings("voices"), Enter/Space a11y, re-injects after rebuild-on-expand.
  • Full gate green: npm test (tsc + Jest + Vitest, 201 files / 1775 tests) ✅

Layer-4 real-host evidence (pi.ai, headed real Chrome)

Build tag 66ecd77@fix/491-pi-more-voices-door. Expanded the voice menu: "More voices…" renders as the last row (Pi 1–8 + door, 9 rows), styled native (cursor-pointer + min-h-11, role=button), 0 console errors. Screenshot in the session.

Closes #491 (door-first). Refs #483, #492.

🤖 Generated with Claude Code

https://claude.ai/code/session_012WmjGuAUmFGQBZukitHp4L

…nu (#491)

Pi redesigned its in-chat voice menu (the `div.t-action-m` anchor has been
stale since Feb 2024) into a popover it LAZILY BUILDS on expand and tears down
on collapse: collapsed it's a pill; expanded, Pi swaps in a `rounded-xl` card
whose voice list holds `div.cursor-pointer` rows (no longer `<button>`s).
So SayPi's whole in-chat voice surface went dark on Pi — verified live
(Layer-4 CDP, 2026-07-04): the old firstChild-`<button>` expansion heuristic
watched the wrong depth (a direct-child childList of a shape Pi no longer
produces) and never fired, and the `#saypi-voice-menu` element was SayPi's
empty fallback div appended OUTSIDE Pi's card.

Door-first (founder-scoped): rather than re-inject a full inline voice block
into Pi's ephemeral, arbitrary-Tailwind, div-based menu (fragile against Pi's
~yearly redesigns), restore the robust path — the "More voices" door — so Pi
users reach the full catalog + previews in the extension's Voices settings.

- Observe the audio-controls SUBTREE (Pi's pill↔card swap happens deep, not as
  a direct child) and, whenever the menu is open, ensure the door is the last
  row of Pi's live list. Idempotent; re-injects after each rebuild-on-expand.
- Anchor on the stable "Toggle voice menu" aria-label → the `rounded-xl` card →
  the `flex.flex-col.gap-1` list (the load-bearing, most-churn-resistant hooks).
- Build the door by CLONING a native row's class list so it renders Pi-native
  (Pi's own utilities are already compiled — SayPi can't author them itself),
  plus role=button + Enter/Space for keyboard a11y.

Inline SayPi voice rows on Pi's in-chat menu are deferred (follow-up), which
keeps #483's Pi ▶ deferred with them. Fixture-backed unit tests reproduce Pi's
current DOM; verified live: door renders as the last row (Pi 1-8 + More voices),
native-styled, 0 console errors.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_012WmjGuAUmFGQBZukitHp4L
@rosscado rosscado merged commit cd89377 into main Jul 4, 2026
2 checks passed
@rosscado rosscado deleted the fix/491-pi-more-voices-door branch July 4, 2026 21:36
rosscado added a commit that referenced this pull request Jul 4, 2026
…follow-up) (#498)

The founder's #491 door-first fix (#496) restored the door in Pi's in-chat
menu; the same door was missing from Pi's own Voice settings page
(pi.ai/profile/settings). Two stale-DOM causes, both verified live (Layer-4 CDP,
2026-07-04):

1. getVoiceSettingsSelector matched 0 — Pi redesigned the page: the content
   column drifted `px-6 py-10` → `mx-auto w-full max-w-2xl px-6 py-3 pt-20` and
   the card grid `grid-cols-2` → `grid-cols-1 gap-4 sm:grid-cols-2`. Re-anchored
   to the settings content column (`max-w-2xl` keeps it off the wider /discover
   /threads surfaces) + its direct-child responsive card grid.
2. Even with a matching selector, decoration never fired: findAndDecorateVoiceSettings
   was gated behind sidebar-decoration in the same added node AND searched that
   node — but the grid is in the main content, and the settings route has no chat
   prompt, so the content-loaded chain never runs there. Un-gated it in the
   bootstrap to scan document.body once per mutation batch (idempotent via the
   getElementById guard; a no-op on ChatGPT (empty selector) and Claude (its
   stale Pi selector matches 0 there)).

Door-first: PiVoiceSettings now injects only the "More voices" door (deferring
inline SayPi voice rows, companion to the in-chat #497), cloning a native card's
class list so it renders Pi-native (cards are `<button>`s here). A shared
buildMoreVoicesDoor(template, tag) helper backs both Pi doors (in-chat `<div>`,
settings `<button>`); the door requires a real template (waits for Pi's cards via
observeSettingsGrid's MutationObserver) so it can never render unstyled.

Adversarial-reviewed (4-lens workflow); the four confirmed findings are fixed:
empty-grid race (template guard), selector over-match (tightened + live-verified
0 on /discover /threads /talk), missing selector-contract test
(Pi-VoiceSettingsSelector.spec.ts), untested observer re-injection (real
MutationObserver test). Full gate green (203 files / 1786 tests). Verified live:
door renders as the last grid card, native-styled, 0 console errors; no spurious
decoration on /discover /threads /talk; #496 in-chat door unaffected.


Claude-Session: https://claude.ai/code/session_012WmjGuAUmFGQBZukitHp4L

Co-authored-by: Claude Opus 4.8 <[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.

pi.ai: SayPi voice block never renders in the in-chat voice menu (expansion/menu detection misses Pi's current DOM)

1 participant