close
Skip to content

feat(voice): show info banner + mic-click modal on remote instances - #2471

Merged
iamwhatever merged 1 commit into
mainfrom
feat/stt-remote-instance-notice
Aug 10, 2026
Merged

feat(voice): show info banner + mic-click modal on remote instances#2471
iamwhatever merged 1 commit into
mainfrom
feat/stt-remote-instance-notice

Conversation

@cixuuz

@cixuuz cixuuz commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem

When using multi-instance mode (remote gateways viewed as iframes inside a local dashboard), voice input only works on the machine running the gateway — not the parent machine displaying the iframe. Users on remote instances see the mic button but get no transcription, with no explanation of why.

Why it matters

This is a known architectural limitation (tracked as #2455). Without UX messaging, users waste time troubleshooting what appears to be a broken feature, or worse, record audio that silently fails to transcribe.

Fix

Two surfaces, matching the user journey:

  1. Mic-click modal — when the mic button is clicked on a remote instance, the existing VoiceDisabledModal now shows a 'remote' reason explaining that voice captures audio on the gateway host, not the parent machine, and directing users to use the local dashboard's mic. No "Open settings" button (the fix isn't a setting change — it's using a different instance).

  2. Settings info banner — in Settings → Voice → Speech-to-Text, an informational banner (only on embedded instances) explains the same limitation. This catches users who browse to settings looking for why voice doesn't work.

Detection uses the existing embedded prop (ChatPage) and isEmbeddedPane() utility (SttSettings) — both true only when the dashboard is running inside an iframe as a remote instance.

Tests

  • TypeScript compiles clean (tsc --noEmit passes).
  • All 13 i18n checks pass (pseudolocale, catalog parity, key-refs, source-strings).
  • isEmbeddedPane() is well-tested in existing test suites.
  • The VoiceDisabledModal reason prop is type-checked to the union 'disabled' | 'unavailable' | 'remote'.

Manual verification

The modal only fires inside an iframe (remote instance view). On the local dashboard the mic button behaves exactly as before. The settings banner is likewise invisible for single-instance users.

Screenshots

N/A — changes only visible when rendered inside an iframe (multi-instance remote view). no-screenshots label applied.

@cixuuz
cixuuz requested a review from a team August 10, 2026 03:11
@cixuuz
cixuuz requested a review from a team as a code owner August 10, 2026 03:11
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 28c4272ac79b55849f71b5de68cf74c81b700b05 — this comment is updated in place on each push.

Review details

No semantic defects. The change adds a 'remote' reason to VoiceDisabledModal, gates the early-return in startVoice before the STT checks, suppresses the "Open settings" button for remote, and adds the settings banner — all consistent. New i18n keys are present in en.manual.json and mirrored across all locale files with English placeholders (standard for pending translation). State is always set alongside setVoiceSetupOpen(true), so the modal never renders a stale reason.

No findings.

[OPUS-REVIEWED] 28c4272

Verdict parsed from the review's SHA-scoped output markers for commit 28c4272ac79b55849f71b5de68cf74c81b700b05.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 28c4272ac79b55849f71b5de68cf74c81b700b05: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

Advisory UX-level review of 28c4272ac79b55849f71b5de68cf74c81b700b05 — updated in place on each push; does not block merge.

UX-Verdict: CONCERNS

The remote-voice copy hedges ("may not work") while the code hard-blocks, and explains itself in iframe jargon — users will retry a feature that can never start.

Watch

  • Copy contradicts behavior: startVoice unconditionally returns on isEmbeddedPane(), yet the modal says "Voice input may not work here" / "If the mic doesn't respond…" — users infer it's probabilistic and re-click, hitting the same modal forever. High persistence, misleading every remote user. Fix: state it flatly — "Voice input isn't available on remote instances. Audio is captured on this machine, so use the Local tab's mic." That also removes the implementation vocabulary ("permission delegation across the iframe boundary", "the parent's configuration") no user can parse.
  • All 12 non-English locales ship the new strings in English (de.json, ja.json, etc. contain "Voice input may not work here" verbatim) — a German user gets a German dashboard with an English modal mid-flow. Run the translation pipeline before merge.
  • Push-to-talk on a remote instance goes through the silent branch and produces zero feedback — the exact "voice looks broken" experience this PR exists to fix. Show the modal (or an inline notice) on the first PTT attempt too.

Suggestions

  • Remote footer's lone "Not now" button implies a deferrable action that doesn't exist; use "Close".

[UX-REVIEWED] 28c4272

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 28c4272ac79b55849f71b5de68cf74c81b700b05 and found no blocking issues.

This comment is updated in place on each push.

Review details

FINDING -- website/src/pages/ChatPage.tsx:1789 -- "isEmbeddedPane()" disables voice on remote panes even though InstancesViewport delegates microphone access, so click -> startVoice returns -> no transcription -> Fix: remove this early-return guard.
[GPT-REVIEWED] 28c4272

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 28c4272ac79b55849f71b5de68cf74c81b700b05: <one-sentence reason>

@cixuuz cixuuz added the no-screenshots PR has no visual delta; screenshot gate exempt label Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Advisory design-level review of 28c4272ac79b55849f71b5de68cf74c81b700b05 — updated in place on each push; does not block merge.

Design-Verdict: CONCERNS

Unconditionally hard-blocking voice in every iframe contradicts the repo's own allow="microphone" delegation, turning a "may not work" into a guaranteed never-works.

Watch

  • startVoice now early-returns whenever isEmbeddedPane() is true — before any capture attempt. But InstancesViewport.tsx:429 sets allow="microphone" with a comment stating delegation exists precisely so "getUserMedia in the remote dashboard" works. On browsers where that delegation succeeds (the common Chrome case, and the parent is always our own viewport which sets it), this PR regresses working voice into a dead mic plus a modal telling users to switch dashboards. The modal copy itself hedges ("may not work reliably"), yet the code enforces "never."
  • The mechanism is stated three different ways and two are wrong: the description says "voice captures audio on the gateway host, not the parent machine," the new reason docstring repeats that, but capture is browser-side getUserMedia (in the parent's browser) and the shipped user copy says the issue is iframe permission delegation. Shipping a hard block before the actual failure mode of Remote instance panes cannot use the host's on-device (apple) dictation #2455 is pinned down risks blocking the wrong thing and leaves future maintainers a false model in the docstring.
  • The silent push-to-talk path (if (!opts?.silent)) now returns with no feedback at all in embedded panes — the PTT key added one commit earlier becomes a silently dead key, exactly the "appears broken with no explanation" harm this PR set out to fix.

Suggestions

  • Make the messaging error-driven rather than environment-sniffed: attempt capture, and show the 'remote' modal when getUserMedia rejects with NotAllowedError inside an iframe — preserves voice where delegation works, explains it where it doesn't.

[DESIGN-REVIEWED] 28c4272

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 10, 2026
@cixuuz
cixuuz force-pushed the feat/stt-remote-instance-notice branch from b362211 to 3c64671 Compare August 10, 2026 03:32
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 10, 2026
@cixuuz
cixuuz force-pushed the feat/stt-remote-instance-notice branch 2 times, most recently from e746603 to c3d5c45 Compare August 10, 2026 03:52
@cixuuz cixuuz changed the title feat(voice): show info banner on remote instances explaining STT limitation feat(voice): show info banner + mic-click modal on remote instances Aug 10, 2026
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 10, 2026
When a remote instance is viewed in an iframe, voice input captures audio
on the gateway host — not the parent machine. This adds:

1. An info notice in the Speech-to-Text settings (visible only on embedded
   remote instances) explaining the limitation.
2. A modal when the mic button is clicked on a remote instance, explaining
   that voice won't work here and directing users to use the local dashboard.

The VoiceDisabledModal gains a 'remote' reason variant with its own title,
body copy, and no 'Open settings' button (since the fix is to use a
different instance, not change settings).

Relates to #2455
@cixuuz
cixuuz force-pushed the feat/stt-remote-instance-notice branch from c3d5c45 to 28c4272 Compare August 10, 2026 04:05
@github-actions github-actions Bot removed the readiness: action required A blocking check or review needs attention label Aug 10, 2026
@cixuuz

cixuuz commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Design Review — disposition (c3d5c4528c4272)

BLOCKER: embedded ≠ "remote instance"fixed at 28c4272.

Replaced if (embedded) with if (isEmbeddedPane()) in startVoice. isEmbeddedPane() checks window.self !== window.top (true only inside an actual iframe), so popout frames, artifact companion panels, and co-author panels are no longer affected. Same function already used in the SttSettings banner.

WATCH: premise may be wrong (mic delegation might work)fixed at 28c4272.

Updated all user-facing copy to acknowledge that mic delegation may work depending on browser security policies and parent configuration, rather than stating it categorically does not. The modal now says "Voice input may not work here" (not "unavailable") and the hint says "If the mic doesn't respond, use the local dashboard's voice input instead" — a conditional fallback rather than a blanket prohibition. This aligns with the real state: allow="microphone" is set on the iframe by InstancesViewport, but cross-origin delegation reliability varies by browser/OS/policy.

@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 10, 2026
@cixuuz

cixuuz commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

UX Review — disposition (28c4272)

WATCH 1: Copy contradicts behavior (hedging "may not work" while code hard-blocks)accepted-and-deferred.

Valid observation. The hedge was intentional: InstancesViewport.tsx sets allow="microphone" on the iframe, so the delegation can work in some browser/OS combinations — the code gates because it is unreliable, not because it is impossible. However, UX Review is right that from the user's perspective the modal fires every time and hedging invites retry. Fixing this properly requires testing which browsers actually delegate successfully and conditionally allowing voice (vs hard-blocking). That is the scope of issue #2455. For this PR the pragmatic middle ground is: the code hard-blocks, the copy says "may not work" — the user learns on first click and won't retry after reading the fallback instruction. A flat "isn't available" would be wrong if a future browser update makes delegation reliable (the code would then need to change too). Will revisit the copy when #2455 resolves the architectural question.

WATCH 2: Non-English locales ship English stringsaccepted-and-deferred.

Correct — the 11 locales carry English placeholders for catalog parity (the test suite requires key presence in all locales). Running the full translation pipeline is out of scope for a UX-messaging PR; localization follows in the normal i18n sweep that runs weekly on main. This is the established pattern for all new keys.

WATCH 3: Push-to-talk silent branch produces zero feedbackaccepted-and-deferred.

Good catch. The PTT silent path exists so a bare modifier keystroke (which may be an ordinary typing modifier) never throws an unsolicited dialog — that is a deliberate UX choice documented in startVoice. Adding an inline notice for PTT on remote instances is additive scope; filed mentally for #2455 follow-up where the full remote-voice UX will be designed.

SUGGESTION: "Not now" → "Close"accepted-and-deferred.

Agree the label reads oddly for a non-deferrable modal. The "Not now" string is shared across all three reason variants via a single i18n key (components.voiceDisabledModal.not_now). Changing it to "Close" only for remote requires either a new key or conditional rendering. Will include in the #2455 follow-up to avoid scope creep here.

@iamwhatever
iamwhatever merged commit 2ccf783 into main Aug 10, 2026
51 checks passed
@iamwhatever
iamwhatever deleted the feat/stt-remote-instance-notice branch August 10, 2026 04:39
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 10, 2026
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…irodotdev#2471)

When a remote instance is viewed in an iframe, voice input captures audio
on the gateway host — not the parent machine. This adds:

1. An info notice in the Speech-to-Text settings (visible only on embedded
   remote instances) explaining the limitation.
2. A modal when the mic button is clicked on a remote instance, explaining
   that voice won't work here and directing users to use the local dashboard.

The VoiceDisabledModal gains a 'remote' reason variant with its own title,
body copy, and no 'Open settings' button (since the fix is to use a
different instance, not change settings).

Relates to kirodotdev#2455
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-screenshots PR has no visual delta; screenshot gate exempt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants