[feature] the card is for voice typing, and the record button listens - #397
Merged
Merged
Conversation
Two changes that landed together because they are the same correction from the same device session: the Live Activity was doing too much, and the thing the user actually looks at while dictating was faking it. **The meeting card is gone.** It shipped in 1.15 and was removed after use. The founder was told twice that the Dynamic Island and the lock-screen card are two presentations of one Live Activity and cannot be separated — so dropping the island for meetings drops the lock-screen card that motivated the feature — and reaffirmed. The island's constant presence turned out to cost more than the lock-screen card was worth for a recording he is sitting in front of. So `MicActivityState` is down to two modes, `MeetingRecorder` stops driving the card, and `MeetingControlChannel` and the stop intent go with it. The meeting haptics added in #395 stay: they were never part of the card. **The dictation card loses ⏹ and ✕.** Deciding what happens to a transcript is not something to do on a surface you glance at with the phone face-up on a table. A dictation card is read, not operated; tapping it opens Parley, where both decisions are one tap away. **Standby keeps its one button**, which is a different kind of decision rather than a surviving exception. ⏹ and ✕ settled the fate of words the card deliberately does not show. Ending standby settles nothing about content — standby *is* the state where nothing is recorded — it closes an open microphone, and every surface that announces a window has always also been a way to end one. This is the fourth, and the only one reachable from a locked screen, which is where someone who has just noticed an unexplained orange dot is looking. **The record button now listens.** It pulsed on a fixed 1.25× loop that had nothing to do with what was being said — the canned visualiser this project's visual rules exist to prevent, and the founder spotted it immediately. The keyboard could not have done better: no amplitude crossed the App Group. So there is now a seventh mailbox carrying one normalised level at 12 Hz (the rate the measurement already arrives at), reusing the RMS the in-app meter computes and the `reportsLevel` gate that already keeps an idle window from spending battery on a number nobody reads. Silence is a value the app writes rather than a value the keyboard infers, a stale reading reads as silence, and the ripple is a lagged copy of the smoothed level drawn further out — so a syllable pushes one ring then the next, and in silence there is no ripple at all. **And a haptic for the microphone being taken.** Tapping iOS's own dictation key in the strip below the keyboard opens a recording session in another process and Parley stops hearing anything. That key cannot be hidden — it is the system's, and there is no public API — so the least Parley can do is tell the hand, at the one moment the user is most likely to keep talking into nothing. A flat pair rather than a rise or a fall: every other beat answers something the user did, and this one answers something done to them. Nothing here is verified on hardware. The swell depth, the ripple reading as a travelling wave, the smoothing constants and the new beat's distinguishability are arguments, not observations.
✅ SonarQube Quality Gate passed — pathorsAI_parley0 open issues on this PR. |
Lanznx
pushed a commit
that referenced
this pull request
Sep 19, 2026
The 1.15 What's New was written before #397 and described the meeting Live Activity as its headline — a card on the Lock Screen with the elapsed time and a Stop button. That feature was removed after a device session. Submitting copy that describes something a reviewer can go and fail to find is worse than submitting no copy, and it would have been the first paragraph. Rewritten around what actually ships: the card is voice typing's, the record button follows the voice instead of pulsing on a loop, and the haptics include the two new endings and the microphone being taken by the system. Both locales, zh-Hant as a peer rather than a translation. Verified against `asc_submit`'s parser at version 1.15 — both sections resolve, stop at the 1.14 heading, and mention no meeting card. Build goes to 30 in `project.yml` rather than being passed as a workflow input. 27 shipped nothing and 28 and 29 were both uploaded, so every tag push since has begun with a run that fails at the version check before anyone remembers to dispatch with an override. The repo should say what TestFlight already holds. Co-authored-by: YJack0000 <jack@pathors.com>
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.
Two changes that landed together because they are the same correction from the same device session: the Live Activity was doing too much, and the thing the user actually looks at while dictating was faking it.
They are one PR rather than two because they were developed in one working tree and interlock — the removal and the additions touch overlapping surfaces, and an intermediate state would not have built.
The meeting card is gone
It shipped in 1.15 and was removed after use. The founder was told twice that the Dynamic Island and the lock-screen card are two presentations of one Live Activity and cannot be separated — so dropping the island for meetings also drops the lock-screen card that motivated the whole feature — and reaffirmed. The island's constant presence cost more than the lock-screen card was worth for a recording he is sitting in front of.
MicActivityState.Modeis down to.dictationand.standby;titlewent with the meeting name.MeetingRecorderstops driving the card. Itsstart(token:app:),weak var host, and the Darwin observer — all added by [feature] the microphone says so on the lock screen, and in your hand #393 purely for the card's Stop button — are reverted to their pre-[feature] the microphone says so on the lock screen, and in your hand #393 form.MeetingControlChannel,StopMeetingRecordingIntentand their catalog strings are removed.Starting a meeting now makes a live card disappear rather than turn red. That is the behaviour, not a bug:
yieldMicrophone()ends the dictation and closes the window, so both ofderive's inputs go nil. The answer is written atderive, because that is where somebody chasing the disappearance will land.The dictation card loses ⏹ and ✕
Deciding what happens to a transcript is not a choice to make on a surface you are glancing at with the phone face-up on a table. A dictation card is read, not operated;
.widgetURLstill opens Parley, where both decisions are one tap away.Standby keeps its one button
A first pass removed this too, as a literal reading of "no buttons". That was wrong and it is restored. ⏹ and ✕ settled the fate of words the card deliberately does not show. Ending standby settles nothing about content — standby is precisely the state in which nothing is being recorded. It closes a microphone that is open.
And it is the surface that most needs to offer it: every place that announces a window has always also been a way to end one (the Settings picker, the Record tab's bar, the keyboard's chip), and this is the fourth and the only one reachable from a locked screen — exactly where someone who has just noticed an unexplained orange dot is looking.
The record button now listens
KeyboardRootViewpulsed the record button on a fixedscaleEffect(animating ? 1.25 : 1)loop that had nothing to do with what was being said. That is the canned visualiser this project's visual rules exist to prevent — driven by real amplitude, flat in silence — and the founder spotted it on the device immediately.The keyboard could not have done better: no amplitude crossed the App Group. So:
dictation-level.json) carrying one normalised level plus a stamp. Not a field onDownlink: that mailbox's staleness is the liveness watchdog's only input, and a value that moves whether or not a word does would refresh it forever — which does not weaken the watchdog, it switches it off.AudioCapturetaps 4096 frames ≈ 85 ms), so a faster mailbox would mostly republish unchanged readings into a backgrounded process.reportsLevelgate, which already exists to stop an idle window spending battery on a number nobody reads.A haptic for the microphone being taken
Tapping iOS's own dictation key in the strip below the keyboard opens a recording session in another process, and Parley stops hearing anything. That key cannot be hidden — it is system-drawn over third-party keyboards from iPhone X onward and there is no public API to suppress it; the only switch is the user's global Settings › General › Keyboard › Enable Dictation.
So the least Parley can do is tell the hand, at the one moment the user is most likely to keep talking into nothing. A flat pair rather than a rise or a fall: every other beat in the file answers something the user did, and direction is how they say what. This one answers something done to them, so it has no direction to borrow.
Deliberately not done
No waveform on the card. It cannot be driven by real audio — that would need
activity.update()landing several times a second from a backgrounded app, which is this feature's one unverified assumption and a battery cost even if it worked. What is left is a loop that moves whether or not anyone is speaking: a picture of listening rather than evidence of it, on the surface whose entire job is to be believable. The reasoning is written where someone would otherwise add one.Verification
swift test --package-path ios/ParleyKit→ 421 tests, 0 failures (1 pre-existing skip). Six new wire-format tests for the level mailbox, including staleness, the unstamped file, NaN/clamping, and one pinning the level off the downlink.xcodegen generate+xcodebuild -scheme Parley -configuration **Release** -destination generic/platform=iOS→ BUILD SUCCEEDED.🤖 Generated with Claude Code