Skip to content

feat(mobile): native chat header with a top-to-bottom message list - #2344

Merged
DoodleBears merged 13 commits into
mainfrom
claude/project-thread-07shzq-native-chat
Oct 19, 2026
Merged

DoodleBears merged 13 commits into
mainfrom
claude/project-thread-07shzq-native-chat

Conversation

@DoodleBears

@DoodleBears DoodleBears commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Vain · project thread

Why the change

Before: the chat page hides the system header and draws its own frosted header over an inverted list. On iOS 26, a transparent native header dimmed the whole inverted list, so the page could not use the system's soft scroll edge.

After: chat uses the native navigation header.

  • On iOS 26 it is a transparent Liquid Glass bar: the native back button, the Bot's avatar and name as a separate capsule, and the computer as a native SF Symbol button.
  • On Android it is an opaque Material top app bar.
  • The message list runs top to bottom, so UIKit's own scroll edge effect softens messages under the header.

Vain chose the native header ("现在开原型") and approved it from the screenshots ("原生顶栏效果很不错"). The reference is Grok's chat header. Refs #2333.

Special things to note

  • Merge risk: the list is no longer inverted. This touches follow-latest, older-history paging, focus jumps and the markdown viewport. It is JS only, and one revert undoes it.
  • Verification:
    • Locally on 3e5fc42: pnpm lint, mobile tsc and oxfmt pass, and vitest apps/mobile/test reports 872 passed, 9 skipped.
    • iOS 26 Simulator (screenshots below, not a physical device):
      • Opening a DM lands at the newest message.
      • The header, edge, keyboard, drag-to-dismiss and status-bar scroll-to-top pass in light and dark, including image and theme backgrounds.
      • At AX5 the chip truncates to "Desig…" without overflowing.
      • With Reduce Transparency, light passes. Dark failed on ec1f976: the bar items turned light grey while the chip text stayed white. dcdf2c4 fixes this: the computer button and the first-screen back are native SF Symbol bar buttons, and the chip text uses the system label colour. The re-shoot on 3e5fc42 passes: "Design Room" and the computer icon are dark on the light items. The DM header shows the computer icon alone, with no text label.
    • Not tested: a Bot-to-Bot DM header, because the QA Host can't create one without a model turn.
    • Android is unverified on a device. It is on the deferred Android list.
  • CI test plan: affected (mobile).

Change outline

  1. conversation-screen.tsx
    • iOS:
      • The header is transparent, with no title.
      • Leading items: back, a zero-width spacing item, then the title chip as a custom item.
      • Trailing item: a native macwindow button.
      • When the chat is the first screen, the back is a native chevron.left button.
      • Before iOS 26, the bar uses headerBlurEffect: systemChromeMaterial.
    • Android: a left-aligned title and the computer action.
    • The background view is passed only for image backgrounds, so the soft edge reaches the list.
    • Android dismisses the keyboard on drag.
  2. follow-latest.ts
    • Rewritten for a top-to-bottom list: it pins growth to the end at most once per frame, only while following.
    • onStartReached and maintainVisibleContentPosition handle older history.
  3. markdown-viewport.ts: the inverted formula is removed.
  4. Keyboard frame: keyboardVerticalOffset no longer counts the home-indicator padding twice.
  5. List bottom inset: useListBottomSafeArea cancels UIKit's extra bottom inset, so the end of the list has no 34pt dead zone.
  6. Removed: the custom ChatHeader and the frosted pills.
  7. chat-title.tsx / channel-title.tsx: these are plain bar items, with system label text on iOS.
  8. Docs: the DESIGN.md chat header and list bullets, and the ios-hig skill references.

iOS 26 Simulator screenshots

Screen Light Dark
DM opened at the newest message
Mid-scroll, default background
Mid-scroll, image background
Mid-scroll, theme background
Keyboard open
Status bar tap to top
Group header
Group, largest text (ec1f976)
Reduce Transparency (light on ec1f976, dark before the fix)
Reduce Transparency dark, after the fix; DM header (3e5fc42)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 13 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8cb928a0-40f1-4d90-bd41-ee280bf21890
📥 Commits

Reviewing files that changed from the base of the PR and between 86a55c6 and 287c111.

⛔ Files ignored due to path filters: 26
  • .github/pr-screenshots/2344-native-chat/1-2-dm-open-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/1-2-dm-open-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/1-dm-open-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/1-dm-open-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3-mid-scroll-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3-mid-scroll-image-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3-mid-scroll-image-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3-mid-scroll-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3-mid-scroll-theme-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3-mid-scroll-theme-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3e5f-dm-header-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/3e5f-group-reduce-transparency-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/4-keyboard-open-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/4-keyboard-open-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/5-after-hard-drag-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/6-status-bar-top-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/6-status-bar-top-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/6b-after-drag-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/6b-after-slow-drag-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/7-group-header-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/7-group-header-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/ec1f-1-group-ax5-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/ec1f-1-group-ax5-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/ec1f-2-group-reduce-transparency-dark.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/ec1f-2-group-reduce-transparency-light.png is excluded by !**/*.png
  • .github/pr-screenshots/2344-native-chat/ec1f-3-dm-rest-light.png is excluded by !**/*.png
📒 Files selected for processing: 22
  • .agents/skills/ios-hig/SKILL.md
  • .agents/skills/ios-hig/references/components.md
  • apps/mobile/DESIGN.md
  • apps/mobile/src/ui/chat/bot-tone.ts
  • apps/mobile/src/ui/chat/channel-title.tsx
  • apps/mobile/src/ui/chat/chat-title.tsx
  • apps/mobile/src/ui/chat/conversation-screen.tsx
  • apps/mobile/src/ui/chat/follow-latest.ts
  • apps/mobile/src/ui/chat/header-chrome.tsx
  • apps/mobile/src/ui/chat/native-header.ts
  • apps/mobile/src/ui/markdown-viewport.ts
  • apps/mobile/src/ui/open-full-client.tsx
  • apps/mobile/src/ui/primitives/keyboard-frame.android.tsx
  • apps/mobile/src/ui/primitives/keyboard-frame.tsx
  • apps/mobile/test/conversation-navigation.test.tsx
  • apps/mobile/test/conversation-streaming-rows.test.tsx
  • apps/mobile/test/follow-latest-gestures.test.tsx
  • apps/mobile/test/follow-latest.test.tsx
  • apps/mobile/test/keyboard-frame.test.tsx
  • apps/mobile/test/markdown-viewport.test.tsx
  • apps/mobile/test/reanimated-shim.ts
  • changes/2344-native-chat-header.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

claude and others added 5 commits October 19, 2026 20:51
Convert the chat FlatList from inverted to oldest-first on iOS and Android.
useFollowLatest now measures the distance from the end, pins streaming
growth with one non-animated scroll per frame, keeps
maintainVisibleContentPosition on for prepends, and reveals the list only
after it has settled at the end. Older history loads from onStartReached,
the markdown viewport measures a non-inverted content view, and the
working status and history footer swap ends.

The chat uses the system stack header: transparent with the automatic
scroll edge effect on iOS (no header background colour, no glass on bar
items), an opaque Material top app bar on Android.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
Update DESIGN.md's Chat header, list and code-rule bullets and the ios-hig
skill's chat edge-effect note, and add the change fragment (placeholder
PR number 0000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
…yboard gap

- Header: on iOS the title chip (avatar + name, no state dot) is a native
  leading bar item beside Back, separated by a zero-width spacing item so
  UIKit draws a round Back and its own glass capsule; the computer button
  is a native trailing item and headerTitle is empty. Android keeps a
  Material top app bar with the chip at the start.
- Follow latest: only a drag, the status-bar scroll to top or an explicit
  hold leave the newest message. Layout-driven scroll events (the automatic
  inset adjustment, MVCP corrections, batch growth) no longer release
  following, which left the chat opened at the oldest message.
- iOS keyboard frame: the keyboard already covers the home indicator, so
  take the composer's safe-area padding back out of the keyboard offset.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@DoodleBears
DoodleBears force-pushed the claude/project-thread-07shzq-native-chat branch from 010444d to 685b454 Compare October 19, 2026 21:03
DoodleBears and others added 3 commits October 20, 2026 06:13
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… drag to dismiss

- The default and colour chat backgrounds are the frame's backgroundColor
  instead of a view in front of the list, so the message list is in the
  screen's first-subview chain, where UIKit and React Native Screens look
  for the content scroll view of the header's soft scroll edge.
- Cancel UIKit's automatic home-indicator bottom inset on the list while
  the keyboard is closed; the composer height already carries it.
- Dismiss the keyboard by dragging the list: interactive on iOS, on-drag
  on Android.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
… to dismiss

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
@DoodleBears
DoodleBears marked this pull request as ready for review October 19, 2026 21:43
DoodleBears and others added 3 commits October 20, 2026 06:55
…DM rest

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…at header

The computer button and the first-screen back control are native bar
buttons with SF Symbols, and the title chip uses the system label colour,
so dark mode with Reduce Transparency keeps them legible.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
@DoodleBears DoodleBears changed the title feat(mobile): native chat header prototype with a top-to-bottom message list feat(mobile): native chat header with a top-to-bottom message list Oct 19, 2026
… and DM header

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@DoodleBears
DoodleBears merged commit b39972c into main Oct 19, 2026
11 checks passed
DoodleBears pushed a commit that referenced this pull request Oct 20, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfa9gqyZUnwgh2fwSFVQQ1
DoodleBears added a commit that referenced this pull request Oct 20, 2026
Captured on the Xiaomi phone (Android 16, HyperOS 3, zh-CN) running a Release build of
main d220792, light and dark: the Material chat composer with the keyboard open, the
chat header, the Unpair confirmation dialog (cancelled, nothing unpaired) and the
message actions menu. The same set is filed under each PR. Status bar, computer
names and the keyboard suggestion strip are redacted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor Author

Android phone result: PASS (Xiaomi, Android 16 / HyperOS 3, isolated QA Host, deepseek-v4.1-flash), release build of main d220792

Screenshots: 2344-android-hig/phone (commit 73da8de): composer-*, chat-header-*, dialog-*, menu-*, light and dark. Material composer, top app bar, dialogs and menus render as expected on Android.


Generated by Claude Code

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.

2 participants