One chip, three modes: Dictate, Rewrite, Translate on the phones - #27
Merged
Conversation
The second stage has three possibilities and `TranscriptMode` has said so for a while: verbatim, rewrite, translate. The phones offered two of them, as a Dictate/Rewrite toggle, while a target language in Settings silently overrode it — so the chip could read Rewrite over a dictation that came back translated, and moving between them meant leaving the text field, opening the app, and coming back. `LiveMode` is what the user is choosing, with one resolver from the mode to the stage, so the three are exclusive by construction rather than by a settings flag beating a toggle. Only the phones have one: a desktop chooses by which key it is holding, so a persistent chip would answer a question the keyboard has already answered. `RewriteAvailability` grows the job it is being asked about. Rewriting and translating need exactly the same thing — a backend that takes text and gives text back — so they share the rule and differ only in the noun, because telling someone their backend "cannot rewrite text" when they asked for a translation sends them to fix the wrong setting. The rewrite sentences are byte-identical to what they were. The desktops keep the answer they had. There a target language still replaces what the second key produces, and the settings window still says so, now through `forSecondKey` rather than through `resolve` — the phones never ask it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG
Hand-ported, with the rewrite wording unchanged so the existing parity assertions still mean what they meant. Windows has no mode chip — a desktop picks the operation by which key is held — so it gets `ForSecondKey` and keeps reporting a target language beside the rewrite picker. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG
The Kotlin half of the same rule, with the parity table duplicated rather than shared: a fixture read from disk would be read by whichever platform remembered to read it. This suite also gains the two sentences the Swift and C# suites already asserted literally, which Kotlin had been missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG
Tapping the chip on the keyboard opens a flat menu of Dictate, Rewrite and Translate; the app's own screen grows the third segment. Deliberately flat: the target language stays in Settings, because a keyboard cannot type into its own popup and any list offered there would be a fixed handful quietly disagreeing with the free-text target the app already stores. A mode that cannot run is offered and disabled with the sentence saying why — Rewrite without a backend that can rewrite, Translate with no language set — rather than being absent, which is how the rewrite feature came to look as though it did not exist. A disabled menu item swallows its own click, so the keyboard answers in the status line instead. The stored mode migrates from what an older build actually did with the two settings it had: a target language means Translate, a rewrite style means Rewrite, anything else is Dictate. Verified on the Pixel: the menu opens over the keyboard with Translate greyed out, choosing Rewrite stores `liveMode=rewrite` and repaints the chip, and the app screen shows the three segments with Translate disabled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG
The chip opens a `UIMenu` of the three modes instead of toggling between two, and the app's own badge grows the third segment. The bridge carries a mode rather than a boolean, migrating an install that had the old switch. The extension cannot read the app's Keychain, so it could never say why a rewrite would not run — it just offered it. The app now publishes which case the shared rule landed on and the keyboard rebuilds the sentence from it, so the wording still lives in one place and a disabled item explains itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
The phones' mode control was a two-state Dictate/Rewrite toggle. A target
language set in Settings silently overrode it, so the chip could read
Rewriteover a dictation that came back translated, and switching between them meant
leaving the text field, opening the app, and coming back.
It is now one three-way picker: a menu on each keyboard, three segments on each
app's own screen.
TranscriptModealready had exactly these three cases;LiveModenames them for what the user is choosing, with a single resolver frommode to stage, so "both at once" is unconstructible rather than merely avoided.
The target language stays in Settings, and the picker does not offer a language
list. A keyboard cannot type into its own popup, so a list there would be a fixed
handful quietly disagreeing with the free-text target the app already stores —
and the popup's job is to answer one question.
Modes that cannot run
They are offered and disabled with the sentence saying why, which is what this
project already does for an unavailable rewrite. Rewriting and translating need
the same thing — a backend that takes text and gives text back — so they share
one rule and differ only in the noun:
SecondStageJob. Telling someone theirbackend "cannot rewrite text" when they asked for a translation sends them to fix
the wrong setting. The rewrite sentences are byte-identical to what they were,
so the existing parity assertions still mean what they meant.
Translate with no language configured is the state the old toggle could not
represent at all. It gets its own case and its own sentence.
On iOS the extension cannot read the app's Keychain, so it could never say why a
rewrite would not run — it just offered it. The app now publishes which case the
shared rule landed on and the keyboard rebuilds the sentence from it, so the
wording still lives in exactly one place.
The desktops are unchanged
macOS and Windows pick the operation by which key is held, so a persistent chip
would be a second answer to a question the keyboard has already answered. There a
target language still replaces what the second key produces, and the settings
window still says so — now through
RewriteAvailability.forSecondKeyrather thanthrough
resolve, which the phones use.Migration
Both phones read the old settings: a target language means Translate, a rewrite
style means Rewrite, anything else is Dictate. That is what an older build
actually did with those two settings. The settings-transfer document keeps its
shape, so a file written here still opens in an older client.
Verified
refused with no language set, and choosing a mode that cannot run does not
become the stored one.
the simulator.
choosing Rewrite stores
liveMode=rewriteand repaints the chip; the app screenshows three segments. Installed and left on Dictate.
🤖 Generated with Claude Code
https://claude.ai/code/session_0199EDkXZ1NZzx1dk9KYTpmG