Skip to content

feat(ios-keyboard): simplify keyboard playback transport and organize core ownership - #66

Merged
macmixing merged 6 commits into
mainfrom
logobar
Apr 6, 2026
Merged

feat(ios-keyboard): simplify keyboard playback transport and organize core ownership#66
macmixing merged 6 commits into
mainfrom
logobar

Conversation

@macmixing

@macmixing macmixing commented Apr 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • replace the keyboard playback waveform treatment with simple playback transport controls
  • add keyboard playback progress, pause/resume, and stop behavior across the shared TTS bridge
  • reorganize keyboard core code by domain and document the new ownership boundaries

What Changed

  • updated the keyboard logo bar to use playback transport symbols instead of playback-driven bar animation
  • added keyboard playback pause/resume on the center control and stop behavior on the cancel and speak controls
  • added the indigo playback progress ring on the keyboard logo bar and a spring return to the microphone when playback ends
  • introduced a non-visual keyboard transport state layer separate from the proprietary logo-bar rendering file
  • removed the old TTS playback metering path from the app-side playback coordinator and shared bridge
  • published keyboard playback transport state through the shared IPC bridge and updated the keyboard controllers to use it
  • reorganized keyboard Core into Dictation, Feedback, Input, Text, and Transport groups
  • moved KeyboardLayoutGeometry into Core and updated the Xcode project plus iOS architecture docs to match

Why

  • the previous playback waveform in the keyboard did not track TTS well enough to justify the complexity
  • simple transport controls are clearer, more reliable, and fit the keyboard better
  • the core-folder reorganization makes keyboard ownership easier to scan and keeps non-visual logic out of protected visual files

Summary by CodeRabbit

  • New Features

    • Pause/resume controls for TTS via the keyboard logo; cancel fully stops playback
    • Live playback progress shown as an indigo arc on the logo
  • Improvements

    • Keyboard state expanded to include a paused-speaking state for clearer UI behavior
    • Mic/cancel taps route to playback controls when relevant; smoother speak/cancel visibility and animations
  • Documentation

    • Updated architecture and directory/ownership docs to reflect transport/playback changes

- replace playback waveform behavior with logo-bar pause and play symbols
- let the keyboard cancel and speak controls stop active TTS playback while the center control pauses and resumes
- add paused playback state and pause/resume IPC publishing across the keyboard bridge
- remove unused playback metering plumbing and update the iOS codemap and engineering docs
- publish copied-text playback progress through the shared keyboard IPC bridge
- overlay an indigo transport arc on the keyboard logo bar during active playback
- keep the keyboard playback ring in sync across pause, resume, finish, failure, and replay restore states
- document the new shared playback progress key and keyboard transport ring behavior
…etary visuals

- move keyboard logo transport state, accessibility, and playback presentation inputs into KeyboardTransportDisplayState
- keep KeyboardLogoBarView focused on proprietary drawing, layout, and animation behavior only
- update the iOS codemap and engineering notes to document the visual-only licensing boundary
- add a dedicated spring return animation for the keyboard microphone icon
- trigger the transport-to-microphone spring when keyboard playback hands control back to idle
- group keyboard core files into Dictation, Feedback, Input, Text, and Transport folders
- move KeyboardLayoutGeometry into Core and keep shared state, style, and layout primitives at the core root
- update the Xcode project path exceptions plus the iOS codemap and engineering docs to match the new structure
@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds TTS pause/resume and playback-progress sharing, introduces a pausedSpeaking keyboard state, removes playback metering in favor of progress, reorganizes Core/ into domain subfolders, moves non-visual logo transport state into a new display-state extension, updates UI to render transport progress and control pause/resume, and updates IPC/App bridge contracts and docs.

Changes

Cohort / File(s) Summary
Documentation & Project File
iOS/Docs/CODEMAP.md, iOS/Docs/ENGINEERING.md, iOS/KeyVox iOS.xcodeproj/project.pbxproj
Docs updated for Core/ subfolder reorg and new ownership/flow notes; pbxproj updated to match moved/renamed source files and layout changes.
Keyboard State & ViewModel
iOS/KeyVox Keyboard/Core/KeyboardState.swift, iOS/KeyVox Keyboard/Core/Dictation/KeyboardDictationController.swift
Added pausedSpeaking state and isTTSPlaybackActive; adjusted indicator, cancel visibility, and dictation transition handling to include paused state.
View Controllers & Presentation
iOS/KeyVox Keyboard/App/KeyboardViewController.swift, iOS/KeyVox Keyboard/App/KeyboardViewController+PresentationLifecycle.swift, iOS/KeyVox Keyboard/Views/KeyboardRootView.swift
Route center-icon taps and cancel to TTS controller when playback active; presentation lifecycle now strongly captures self and updates/reset logo playback progress; cancel/speak visibility gating refined.
Logo Bar: Display vs Render
iOS/KeyVox Keyboard/Core/Transport/KeyboardTransportDisplayState.swift, iOS/KeyVox Keyboard/Views/Components/KeyboardLogoBarView.swift
New KeyboardTransportDisplayState extension for non-visual transport state and accessibility; KeyboardLogoBarView refactored to render transport symbol, playback-progress arc, dynamic center icon sizing, and expose updateLayerFrames() publicly.
IPC / App Bridge & App Service Wiring
iOS/KeyVox iOS/App/KeyVoxIPCBridge.swift, iOS/KeyVox iOS/App/KeyVoxKeyboardBridge.swift, iOS/KeyVox iOS/App/AppServiceRegistry.swift
Replaced meter persistence with ttsIsPaused/ttsPlaybackProgress; added pause/resume notification names and command callbacks; added publishing helpers for progress/paused/resumed.
Keyboard IPC Manager / TTS Controller
iOS/KeyVox Keyboard/Core/Transport/KeyboardIPCManager.swift, iOS/KeyVox Keyboard/Core/Transport/KeyboardTTSController.swift
Added onTTSPaused/onTTSResumed callbacks and send-pause/resume commands; exposed currentTTSPlaybackProgress(); adjusted shared recording/state reconciliation to account for pause flag and paused mapping.
TTS Manager / Playback Coordinator
iOS/KeyVox iOS/Core/TTS/TTSManager/*, iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/*
Removed playback-meter API and MeterPolicy; added publishing of playback progress and paused/resumed events; removed meter scheduling and sample-dependent scheduling arguments.
Minor / Formatting
iOS/KeyVox Keyboard/Core/KeyboardLayoutGeometry.swift
Whitespace addition only (blank line before imports).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant KeyboardVC as KeyboardViewController
    participant TTSCtrl as KeyboardTTSController
    participant IPCMgr as KeyboardIPCManager
    participant AppBridge as KeyVoxKeyboardBridge
    participant TTSMgr as TTSManager

    User->>KeyboardVC: Tap center icon (pause/resume)
    KeyboardVC->>TTSCtrl: handlePlaybackControlTap()

    alt was .speaking
        TTSCtrl->>IPCMgr: sendPauseTTSCommand()
        IPCMgr->>AppBridge: post pauseTTS notification
        AppBridge->>TTSMgr: handlePauseTTS()
        TTSMgr->>AppBridge: publishTTSPaused()
        AppBridge->>IPCMgr: onTTSPaused callback
        IPCMgr->>TTSCtrl: onTTSPaused -> handleTTSPaused()
        TTSCtrl->>TTSCtrl: update state -> .pausedSpeaking
    else was .pausedSpeaking
        TTSCtrl->>IPCMgr: sendResumeTTSCommand()
        IPCMgr->>AppBridge: post resumeTTS notification
        AppBridge->>TTSMgr: handleResumeTTS()
        TTSMgr->>AppBridge: publishTTSResumed()
        AppBridge->>IPCMgr: onTTSResumed callback
        IPCMgr->>TTSCtrl: onTTSResumed -> handleTTSResumed()
        TTSCtrl->>TTSCtrl: update state -> .speaking
    end
Loading
sequenceDiagram
    participant TTS as TTSManager
    participant Bridge as KeyVoxKeyboardBridge
    participant IPC as KeyboardIPCManager
    participant VC as KeyboardViewController
    participant Logo as KeyboardLogoBarView

    TTS->>Bridge: publishTTSPlaybackProgress(0.45)
    Bridge->>IPC: update shared playback progress state
    IPC->>VC: deliver progress (via currentTTSPlaybackProgress)
    VC->>Logo: applyPlaybackProgress(0.45)
    Logo->>Logo: update transportProgressLayer.strokeEnd & redraw
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hushed the meter, progress glows,

Ringing arcs where silence flows,
States now pause and softly play,
Transport hops in Core's new way,
A tiny rabbit taps—hooray! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: simplifying keyboard playback transport controls and reorganizing Core ownership into domain groups.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch logobar

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
iOS/KeyVox Keyboard/Core/Transport/KeyboardIPCManager.swift (1)

161-170: ⚠️ Potential issue | 🟠 Major

Paused transport gets evicted as stale too aggressively.

When ttsState is .playing and currentTTSIsPaused() is true, this branch still clears shared TTS state after ~5 seconds without a warm session. Since pause/progress are now persisted in KeyVoxIPCBridge, reopening the keyboard after the app suspends will lose the paused transport and the resume path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@iOS/KeyVox` Keyboard/Core/Transport/KeyboardIPCManager.swift around lines 161
- 170, The reconcileKeyboardStateIfNeeded() logic is incorrectly clearing
persisted paused playback: when KeyVoxIPCBridge.currentTTSState() == .playing
and KeyVoxIPCBridge.currentTTSIsPaused() == true you should not treat it as
stale and evict it; update the stale-clear condition (which currently checks
isSessionWarm() and KeyVoxIPCBridge.currentTTSStateAge() > 5) to first check
KeyVoxIPCBridge.currentTTSIsPaused() and skip calling
KeyVoxIPCBridge.clearTTSState() if paused, leaving the paused state to be
returned as .pausedSpeaking; ensure you only clear when not paused and age > 5
and session is not warm so paused transports survive reopen/resume paths.
iOS/KeyVox iOS/Core/TTS/TTSManager/TTSManager+State.swift (1)

19-20: ⚠️ Potential issue | 🟠 Major

Don't clear the finished transport snapshot in the same task.

finishPlayback() publishes .finished/progress = 1, but clearActiveRequest() immediately publishes progress = 0 and calls KeyVoxIPCBridge.clearTTSState(). That makes the delayed cleanup in KeyVoxKeyboardBridge.publishTTSFinished() ineffective and turns the keyboard's end-of-playback ring update into a race.

Also applies to: 78-92

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@iOS/Docs/ENGINEERING.md`:
- Around line 296-297: Documentation is inconsistent: add the new IPC keys
`pauseTTS`, `resumeTTS`, `ttsPaused`, and `ttsResumed` to the Darwin
notification list and ensure the documented runtime-structure no longer
references the removed file `TTSPlaybackCoordinator+Metering.swift`; instead
update it to reference the current TTS coordinator/meters file(s) and include
`ttsIsPaused` and `ttsPlaybackProgress` where appropriate (also apply the same
updates to the repeated section around lines 607-615) so the Darwin
notifications list and runtime-structure match the new IPC contract.

---

Outside diff comments:
In `@iOS/KeyVox` Keyboard/Core/Transport/KeyboardIPCManager.swift:
- Around line 161-170: The reconcileKeyboardStateIfNeeded() logic is incorrectly
clearing persisted paused playback: when KeyVoxIPCBridge.currentTTSState() ==
.playing and KeyVoxIPCBridge.currentTTSIsPaused() == true you should not treat
it as stale and evict it; update the stale-clear condition (which currently
checks isSessionWarm() and KeyVoxIPCBridge.currentTTSStateAge() > 5) to first
check KeyVoxIPCBridge.currentTTSIsPaused() and skip calling
KeyVoxIPCBridge.clearTTSState() if paused, leaving the paused state to be
returned as .pausedSpeaking; ensure you only clear when not paused and age > 5
and session is not warm so paused transports survive reopen/resume paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a607a8a-6939-4306-a1a5-3cfae265a288

📥 Commits

Reviewing files that changed from the base of the PR and between 91873e9 and 5831dbc.

📒 Files selected for processing (34)
  • iOS/Docs/CODEMAP.md
  • iOS/Docs/ENGINEERING.md
  • iOS/KeyVox Keyboard/App/KeyboardViewController+PresentationLifecycle.swift
  • iOS/KeyVox Keyboard/App/KeyboardViewController.swift
  • iOS/KeyVox Keyboard/Core/Dictation/AudioIndicatorDriver.swift
  • iOS/KeyVox Keyboard/Core/Dictation/KeyboardCallObserver.swift
  • iOS/KeyVox Keyboard/Core/Dictation/KeyboardDictationController.swift
  • iOS/KeyVox Keyboard/Core/Feedback/KeyboardHapticsSettingsStore.swift
  • iOS/KeyVox Keyboard/Core/Feedback/KeyboardInteractionHaptics.swift
  • iOS/KeyVox Keyboard/Core/Feedback/KeyboardKeypressHaptics.swift
  • iOS/KeyVox Keyboard/Core/Input/KeyboardCursorTrackpadSupport.swift
  • iOS/KeyVox Keyboard/Core/Input/KeyboardSpecialKeyInteractionSupport.swift
  • iOS/KeyVox Keyboard/Core/Input/KeyboardTextInputController.swift
  • iOS/KeyVox Keyboard/Core/KeyboardLayoutGeometry.swift
  • iOS/KeyVox Keyboard/Core/KeyboardState.swift
  • iOS/KeyVox Keyboard/Core/Text/KeyboardCapsLockStateStore.swift
  • iOS/KeyVox Keyboard/Core/Text/KeyboardDictionaryCasingStore.swift
  • iOS/KeyVox Keyboard/Core/Text/KeyboardInsertionCapitalizationHeuristics.swift
  • iOS/KeyVox Keyboard/Core/Text/KeyboardInsertionSpacingHeuristics.swift
  • iOS/KeyVox Keyboard/Core/Transport/KeyboardIPCManager.swift
  • iOS/KeyVox Keyboard/Core/Transport/KeyboardTTSController.swift
  • iOS/KeyVox Keyboard/Core/Transport/KeyboardTransportDisplayState.swift
  • iOS/KeyVox Keyboard/Views/Components/KeyboardLogoBarView.swift
  • iOS/KeyVox Keyboard/Views/KeyboardRootView.swift
  • iOS/KeyVox iOS.xcodeproj/project.pbxproj
  • iOS/KeyVox iOS/App/AppServiceRegistry.swift
  • iOS/KeyVox iOS/App/KeyVoxIPCBridge.swift
  • iOS/KeyVox iOS/App/KeyVoxKeyboardBridge.swift
  • iOS/KeyVox iOS/Core/TTS/TTSManager/TTSManager+State.swift
  • iOS/KeyVox iOS/Core/TTS/TTSManager/TTSManager.swift
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator+Lifecycle.swift
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator+Metering.swift
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator+Scheduling.swift
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator.swift
💤 Files with no reviewable changes (3)
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator+Metering.swift
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator.swift
  • iOS/KeyVox iOS/Core/TTS/TTSPlaybackCoordinator/TTSPlaybackCoordinator+Lifecycle.swift

Comment thread iOS/Docs/ENGINEERING.md
- keep paused TTS transport snapshots from being cleared during keyboard state reconciliation
- preserve the finished playback progress snapshot until the bridge's delayed cleanup runs
- update engineering docs for pause/resume transport notifications and the current TTS runtime structure

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
iOS/Docs/ENGINEERING.md (1)

608-609: Optional wording polish for repeated bullet openings.

Both bullets starting with “... owns ...” in sequence are correct, but rephrasing one can improve scan readability in this dense section.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@iOS/Docs/ENGINEERING.md` around lines 608 - 609, The two consecutive bullets
both start with "owns", which is repetitive; revise one of them (either the
TTSPlaybackCoordinator or TTSManager line) to use a different verb such as
"manages", "is responsible for", or "handles" to improve scannability—e.g.,
change the TTSManager bullet to "TTSManager manages request lifecycle,
playback-preparation progress, home-card replay state, replay cache persistence,
paused replay restoration, App Group TTS state publishing, and the free-speak
consumption point once a new generation has actually started."
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@iOS/Docs/ENGINEERING.md`:
- Around line 608-609: The two consecutive bullets both start with "owns", which
is repetitive; revise one of them (either the TTSPlaybackCoordinator or
TTSManager line) to use a different verb such as "manages", "is responsible
for", or "handles" to improve scannability—e.g., change the TTSManager bullet to
"TTSManager manages request lifecycle, playback-preparation progress, home-card
replay state, replay cache persistence, paused replay restoration, App Group TTS
state publishing, and the free-speak consumption point once a new generation has
actually started."

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f92c32ac-9995-4143-badf-773fdd39403f

📥 Commits

Reviewing files that changed from the base of the PR and between 5831dbc and 2059d8b.

📒 Files selected for processing (4)
  • iOS/Docs/ENGINEERING.md
  • iOS/KeyVox Keyboard/Core/KeyboardLayoutGeometry.swift
  • iOS/KeyVox Keyboard/Core/Transport/KeyboardIPCManager.swift
  • iOS/KeyVox iOS/Core/TTS/TTSManager/TTSManager+State.swift
✅ Files skipped from review due to trivial changes (1)
  • iOS/KeyVox Keyboard/Core/KeyboardLayoutGeometry.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • iOS/KeyVox Keyboard/Core/Transport/KeyboardIPCManager.swift

@macmixing
macmixing merged commit 80579fb into main Apr 6, 2026
5 checks passed
@macmixing
macmixing deleted the logobar branch April 7, 2026 01:37
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.

1 participant