Repository navigation
release: 2.1.9 — typing freeze, full history, model picker, layout, settings - #404
Merged
Merged
Conversation
The composer draft was a property on ConversationScreenModel, bound with $bindableScreenModel.composerInputText inside ConversationDestinationScreen's body. Every keystroke re-ran that body, rebuilt ConversationView with new closures, and re-rendered the transcript. Measured over 104 keystrokes after a finished Claude turn in the simulator: ConversationView.body 81 -> 1 MessageList.body 268 -> 4 BottomChrome.body 81 -> 1 pinned-diff rebuild 81 -> 1 The cost grows with the transcript, which is why typing froze once a turn had added tool output and markdown. - Move the draft text and attachments into ConversationComposerDraft, an observable passed down by reference. Only ComposerDraftBinder, a leaf in the bottom chrome, builds the bindings, so keystrokes invalidate the composer alone. - ConversationPinnedContextStrip built the combined diff in init on every parent re-render although SwiftUI keeps only the first @State initial value; build it in onChange(initial: true) instead. - Add live UI tests against a kittylitter host (skipped unless env is set): typing after a turn settles, a screenshot tour, and open-session timing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude sessions whose first user entry is a <local-command-caveat> or a slash-command wrapper showed that raw markup as the session title and preview. Clean titles and previews once in the shared ThreadInfo conversion (and for home-cache entries written by older builds), so both platforms get displayable text without parsing tags themselves. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One Kittylitter host serves codex next to bridged runtimes (claude, pi, opencode, amp, ...) whose thread/turns/list behave differently, but the paging capability was a single per-server flag, so one runtime's behaviour changed how every other runtime loaded history. The Claude and Pi bridges also cut a page to 'limit' without returning a cursor, so the app showed only the newest 5 (Android) or 20 (iOS) turns with no way to load earlier ones. Record the capability per (server, runtime). When a first page is full, has no cursor, and the runtime has never returned one, fetch the list once without a limit; if it is longer, keep the full history and mark that runtime as non-paging. Threads a runtime cannot replay (Pi refuses sessions whose cwd was deleted) now open with metadata and a 'History unavailable' row instead of failing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… separate
The composer picker showed one flat list (capped at 80 rows) of a
1,800-entry catalog with provider pills, Amp modes mixed in as models,
and effort chips plus Plan/Fast/Access toggles jammed underneath.
Rust now classifies every catalog entry when it is fetched:
ModelInfo.entry_kind (Model / Mode / PluginMode, from the agent's
visible_modes allowlist), picker_name (mode name, or the model name
without its provider prefix) and provider_label ("OpenAI", "xAI").
Both platforms read those fields instead of re-deriving them per row.
iOS (ModelPickerView) and Android (ModelSelectorPanel) now share one
layout: current selection and recents on top, one row per harness with
counts, a harness page with modes / plugin modes / models grouped by
provider (large catalogs fold, selected provider open), whole-catalog
token search capped at 150 shown rows, and an Options section with
reasoning, fast, plan and full access as list rows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A non-paging bridge can take minutes to return a large transcript (a 100 MB Claude session takes 4-5 minutes on the host). Apply the first page as soon as it arrives, then merge the full list when it lands, so the conversation is readable while the rest of the history loads. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Scroll (and tap Load more) until the requested row appears, and allow a longer hold via LITTER_E2E_HOLD_SECS for transcripts that take minutes to arrive. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On a phone the open conversation drifted left by the page margin once it scrolled to the bottom: prose, bullets and code ran into the left edge while the right side kept a 40pt gap. The transcript stack was inset with horizontal padding and marked as the scroll target layout, and `ScrollPosition.scrollTo(edge: .bottom)` aligns that layout's leading edge, so following the bottom set a 20pt horizontal content offset on a vertical-only scroll view. The scroll target layout now spans the full viewport and every row sits in a centered column of an exact width (screen minus margins, capped at the 760pt reading width). A row with a wider ideal size (long code line, table) can no longer widen the column; it scrolls inside its own box. Adds a rich transcript fixture (code block, table, diff, command, reasoning) to the multi-turn harness and a UI test that checks both gutters at the bottom and after scrolling, plus a settings screenshot test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Settings had nine categories, and four of them (Appearance, Pets, Experimental, Support) opened a page holding a single link to the real page, so most options sat three levels deep. iOS root is now Connections (Computers with a count, Harnesses, Account with the signed-in state), Interface (Appearance, Conversation) and More (Advanced, Tip the Kitty), with the app version underneath. Every row opens its settings directly. Rarely used switches live on one Advanced page: experimental features, debug mode, Wake Pet and the Local Studio link. The font family picker moved into Appearance next to text size. Rows share one shape (icon, title, trailing value) and sentence-case section headers; computer rows and the editor use LitterFont tokens instead of fixed system sizes. Android gets the same grouping: computers and connections first, font family inside Appearance, and an Advanced screen with experimental features, Wake Pet, Saved Apps, Local Studio and a Debug mode switch (debug mode was previously not reachable from Android settings). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The sessions screen carried four toolbar buttons, which squeezed the computer name to "macboo…". Refresh is now pull-to-refresh (and in the filter menu), and server info moved into the same menu, leaving filter and new session. The search field gains a magnifying glass and a clear button. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With LITTER_E2E_ATTACH the test activates an app already started by 'simctl launch --console-pty', so the Rust stderr log of the run can be captured alongside the screenshots. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The sync is removed before release: iCloud KVS is not entitled in the shipping app yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 1, 2026
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.
Purpose
Ship the fixes for the reported problems: typing froze after a reply; history only showed for Local Studio; the model picker was a hard-to-read wall of 1,800+ entries; conversations sat off-center with code running off the screen; Settings pages were complicated. Replaces #403 (freeze fix) and #401 (version bump).
Key changes
ConversationComposerDraft, bound only in a leaf (b898c33). Android was checked and was not affected.<local-command-caveat>, command tags, system reminders) is stripped from titles in shared Rust.scrollTo(edge: .bottom)slid the inset transcript stack 20pt sideways on phones. Rows now sit in a centered fixed column, and code and tables scroll inside their own box.Verification
cargo test -p codex-mobile-client --lib: 863 passed.make ios-sim-fast: BUILD SUCCEEDED.Not verified locally
testConversationDisplayCollapsedModeKeepsCompletedDetailsCollapsedalready fails on main.Follow-ups (alleycat repo)
nextCursorwhen they truncate.isMetaentries.🤖 Generated with Claude Code