Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2ab7121
feat(mobile): add android form vocabulary behind the platform seam
Zerlight Aug 17, 2026
0f8077d
feat(mobile): android settings, account, and terminal-appearance screens
Zerlight Aug 17, 2026
e548cc9
feat(mobile): android connect and add-host screens
Zerlight Aug 17, 2026
0171283
feat(mobile): android host switcher via shared bottom-sheet picker
Zerlight Aug 17, 2026
69572b1
feat(mobile): android threads inbox and host connection state
Zerlight Aug 17, 2026
15b4576
feat(mobile): android terminals inbox and new-terminal sheet
Zerlight Aug 17, 2026
b08fcd6
feat(mobile): android composer chips and workspace picker as bottom s…
Zerlight Aug 17, 2026
a42c76e
feat(mobile): android prompt dock and tool-detail sheet
Zerlight Aug 17, 2026
0e6c5bc
feat(mobile,ui): android tab glyphs, opaque header, and monospace fal…
Zerlight Aug 17, 2026
4b3b9a1
fix(mobile): align expo sdk packages so @expo/ui compiles on android
Zerlight Aug 17, 2026
b4aa103
fix(mobile): pin compose surfaces to the app theme
Zerlight Aug 17, 2026
ca77830
feat(mobile): android primary action as a floating action button
Zerlight Aug 18, 2026
9685fdb
feat(mobile): drive android chrome with material you dynamic colors
Zerlight Aug 18, 2026
6188a92
feat(mobile): android header menus as compose dropdowns
Zerlight Aug 18, 2026
b8da545
feat(mobile,ui): native color bridge, android search view, and add-ho…
Zerlight Aug 18, 2026
1601480
fix(mobile): fit android adaptive icon layers to the safe zone
Zerlight Aug 18, 2026
e1e0eb2
feat(mobile,client-core): reach the dev mock host through mock:// hosts
Zerlight Aug 21, 2026
c55ec2b
fix(mobile): give android prompt-dock cards a bounded compose width
Zerlight Aug 21, 2026
ae41027
fix(mobile): push the android add-host route with a real toolbar
Zerlight Aug 21, 2026
5c3426e
fix(mobile): give thread groups material chevrons and expand motion
Zerlight Aug 21, 2026
ae659ec
fix(mobile): center the android pull-to-refresh indicator
Zerlight Aug 24, 2026
12ee982
feat(mobile): structure the android selector sheet by axis
Zerlight Aug 24, 2026
26257bb
fix(mobile): reuse the refresh indicator for load and connect states
Zerlight Aug 24, 2026
071511c
feat(mobile): lead thread rows with the harness brand mark
Zerlight Aug 24, 2026
154a7dc
fix(mobile): keep in-app account deletion after the android split
Zerlight Sep 7, 2026
74f095a
fix(mobile): share platform state and repair native controls
Zerlight Sep 7, 2026
4a1f105
refactor(mobile): use native controls for app chrome
Zerlight Sep 7, 2026
49fe774
fix(mobile): use native long-press menus for Android list actions
Zerlight Sep 7, 2026
83fea76
refactor(mobile): use native controls in the chat composer
Zerlight Sep 7, 2026
922b803
refactor(mobile): render chat activity with native controls
Zerlight Sep 7, 2026
cb6a02c
refactor(mobile): unify native question response flow
Zerlight Sep 7, 2026
5a84ac2
fix(mobile): provide CocoaPods in the development environment
Zerlight Sep 7, 2026
6ef09f3
refactor(mobile): align native chat records with desktop
Zerlight Sep 7, 2026
5a4ff56
fix(mobile): preserve plan contrast in dark mode
Zerlight Sep 7, 2026
fc1c760
refactor(mobile): move Android questions into a native sheet
Zerlight Sep 8, 2026
39ae808
fix(mobile): repair native chat layout and accessibility
Zerlight Sep 8, 2026
33bd138
fix(mobile): synchronize native appearance and composer layout
Zerlight Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ apps/mobile/generated/
apps/mobile/src/runtime/config/bundled.generated.ios.ts
apps/mobile/src/runtime/config/bundled.generated.android.ts

# gradle caches inside tracked local expo modules (the app's android/ dir is already ignored)
apps/mobile/modules/*/android/.gradle/
apps/mobile/modules/*/android/build/

# standalone daemon distribution (pnpm --prod deploy output)
apps/daemon/standalone/

Expand Down
41 changes: 35 additions & 6 deletions apps/mobile/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# apps/mobile — Expo / React Native client

Expo + React Native. List/form screens render **`@expo/ui`** (real SwiftUI); **HeroUI Native**
Expo + React Native. List/form screens render **`@expo/ui`** (SwiftUI on iOS, Jetpack Compose on Android); **HeroUI Native**
covers the RN surfaces that cannot cross over ("What deliberately stays React Native" below).
Reaches the host through the `server` tunnel; business data still travels over `transport` +
`@linkcode/schema`, the same contract as every other client.
Expand Down Expand Up @@ -44,6 +44,27 @@ Native. Mobile consumes `@linkcode/ui` only through its **native** components
under Node, suspect this class first and probe the runtime rather than reasoning from the API's
documentation.

## Native UI boundaries

Platform views share `runtime/` hooks for data and actions. Keep `.ios.tsx` responsible for
SwiftUI and the Android implementation responsible for Compose; shared routes and runtime must
not import either platform's UI package. Android hosts use `ThemedHost` so Material colors follow
the app's appearance preference. Each visible list row must be a direct `LazyColumn` child.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This sentence contradicts form/list.android.tsx:7, added in the same PR: "LazyColumn maps each direct child to one lazy item, so keep children at section granularity."

The implementation follows the docstring, not this line — FormSection (form/section.android.tsx:23) wraps its children in a Column, so on every FormList-backed screen (settings, terminal appearance, threads, connect sections, account sections, terminals) the rows are grandchildren of LazyColumn, never direct children. The code is internally consistent and, I think, correct; it's this rule that's misstated. As written, the next contributor who reads AGENTS.md alone will conclude the whole PR is already in violation and try to "fix" it by hoisting rows out of their sections, which would cost the section chrome and turn every row into its own lazy item.

Suggested change
the app's appearance preference. Each visible list row must be a direct `LazyColumn` child.
the app's appearance preference. `LazyColumn` children stay at section granularity — one `FormSection`
per lazy item, with rows nested inside it.


Expo's Compose `Switch` always receives a native callback, even without a JS handler. Wire
`onCheckedChange` alongside a toggleable row; a tap on the thumb is consumed by the switch.
Single-choice rows use `selectable` and their parent uses `selectableGroup` for TalkBack.

List-item management actions use SwiftUI `SwipeActions` on iOS (horizontal row swipes) and
`RowActions` on Android (Compose long-press dropdown menus under `MaterialExpressiveTheme`).
Keep a row's normal tap for navigation; rows without a primary action may also open the menu on
tap. Destructive actions belong in these native action surfaces, not permanent trailing buttons.
Children of `RowActions` must not add their own click handler, which would consume the gesture.

Android question batches open from a compact transcript dock into a Compose modal sheet. Closing
the sheet preserves drafts; cancelling the request requires confirmation. Keep permission actions
inline with one-time approval emphasized, and allow the action row to wrap for longer labels.

## `@expo/ui` (SwiftUI) — its layout rules are not RN's

Settings, terminal appearance, and connect render a real `Form` inside a `Host` (`style={{flex:1}}`
Expand Down Expand Up @@ -80,13 +101,16 @@ only by driving the simulator:
RN→SwiftUI is the supported direction; going back needs `RNHostView`, whose bidirectional nesting is
the very thing 57.0.5 had to fix. So anything whose indispensable part is an RN view cannot cross
over: **sign-in** (`AppleAuthenticationButton` is an RN view and `@expo/ui` has no Sign in with
Apple), the **conversation surface** — timeline, composer, and the screen holding them (excluded by
the redesign decision; the composer also rides `react-native-keyboard-controller`), the **terminal
Apple), the **conversation layout** — virtualized Markdown timeline and controlled multiline input
with `react-native-keyboard-controller` (its controls, disclosures, prompts, and transcript records
use SwiftUI / Compose), the **terminal
canvas** (`expo-libghostty`), the **startup splash** (`BrandMark` is a bundled RN image), and the
**navigation header** (react-navigation). Two smaller losses are accepted rather than worked
**navigation header** (react-navigation). One smaller loss is accepted rather than worked
around: `Image` takes SF Symbols, asset-catalog names, and local file URIs but **never a remote
URL**, so the account avatar is an SF Symbol; and the agent brand marks are RN SVG components, so
thread rows and the new-thread picker name the agent in text instead.
URL**, so the account avatar is an SF Symbol. The agent brand marks (RN SVG) do cross into rows
now: the iOS thread row hosts `AgentIcon` through `RNHostView` (`matchContents`, pointer events
off), and the Android row uses vector-drawable copies of the lobe glyphs in `assets/icons/` —
but the new-thread picker and selector sheets still name the agent in text.

## Styling & version pins

Expand Down Expand Up @@ -161,6 +185,9 @@ thread rows and the new-thread picker name the agent in text instead.

## Build & run

- Expo SDK 57 requires Xcode 26.4 or newer. Older Xcode can fail while compiling
`expo-modules-jsi`'s Swift/C++ bridging annotations before app code is built.

- **Dev builds, not Expo Go.** Cloud sign-in needs the real `linkcode://` scheme: production HQ
trusts only `https://linkcode.ai,linkcode://` (`TRUSTED_ORIGINS`), and the `@better-auth/expo`
server plugin auto-trusts `exp://` only under `NODE_ENV=development`, so "Sign in" from Expo Go
Expand All @@ -182,6 +209,8 @@ thread rows and the new-thread picker name the agent in text instead.
`scripts.mobile` in `devenv.nix` unsets both groups (and sets `SENTRY_DISABLE_AUTO_UPLOAD`, since
the Sentry Xcode phase otherwise fails the build without org/project credentials). Outside devenv
the shell is already clean and `pnpm -F @linkcode/mobile ios` works as-is.
CocoaPods comes from devenv with its own Ruby runtime; a user-installed `pod` using env Ruby
breaks when this runner puts `/usr/bin` first and resolves Apple's older system Ruby.

Symptoms are misleading: the first thing to fail is usually the `[RNDeps] Replace React Native
Dependencies` script phase, which is collateral — that script runs fine on its own.
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
],
"expo-apple-authentication",
"expo-background-task",
"expo-font",
"expo-sqlite",
"expo-secure-store",
"expo-status-bar",
Expand Down
Binary file modified apps/mobile/assets/android-icon-foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/mobile/assets/android-icon-monochrome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions apps/mobile/assets/icons/add.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector>
10 changes: 10 additions & 0 deletions apps/mobile/assets/icons/agent-claude-code.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"
android:pathData="M20.998,10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6,10.949h1.488V8.102H6v2.847zm10.51,0H18V8.102h-1.49v2.847z" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This path is a pixel-art robot face, but the Claude Code mark everywhere else in the repo is the Anthropic "A" — packages/presentation/ui/src/native/agent-icon.tsx:11-12 starts M4.709 15.955l4.72-2.647…. Since thread-row.ios.tsx:53 renders that same AgentIcon, an Android user and an iOS user looking at the same claude-code thread see two different brand marks.

What makes this look like a substitution slip rather than a deliberate choice: agent-codex.xml and agent-opencode.xml both match agent-icon.tsx's GLYPH_PATHS character-for-character, so the other two were clearly derived from it and only this one wasn't. Relatedly, thread-row.tsx:13 says these are "vendored as vector drawables from @proj-airi/lobe-icons (the web glyph set)" — that's accurate for codex and opencode but not for claude-code, which has no entry in lobe-icons' icons.json at all. Worth converting the agent-icon.tsx path and fixing the comment to say where the claude-code mark actually comes from.

</vector>
10 changes: 10 additions & 0 deletions apps/mobile/assets/icons/agent-codex.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"
android:pathData="M8.086,0.457a6.105,6.105 0,0 1,3.046 -0.415c1.333,0.153 2.521,0.72 3.564,1.7a0.117,0.117 0,0 0,0.107 0.029c1.408,-0.346 2.762,-0.224 4.061,0.366l0.063,0.03 0.154,0.076c1.357,0.703 2.33,1.77 2.918,3.198c0.278,0.679 0.418,1.388 0.421,2.126a5.655,5.655 0,0 1,-0.18 1.631a0.167,0.167 0,0 0,0.04 0.155a5.982,5.982 0,0 1,1.578 2.891c0.385,1.901 -0.01,3.615 -1.183,5.14l-0.182,0.22a6.063,6.063 0,0 1,-2.934 1.851a0.162,0.162 0,0 0,-0.108 0.102c-0.255,0.736 -0.511,1.364 -0.987,1.992c-1.199,1.582 -2.962,2.462 -4.948,2.451c-1.583,-0.008 -2.986,-0.587 -4.21,-1.736a0.145,0.145 0,0 0,-0.14 -0.032c-0.518,0.167 -1.04,0.191 -1.604,0.185a5.924,5.924 0,0 1,-2.595 -0.622a6.058,6.058 0,0 1,-2.146 -1.781c-0.203,-0.269 -0.404,-0.522 -0.551,-0.821a7.74,7.74 0,0 1,-0.495 -1.283a6.11,6.11 0,0 1,-0.017 -3.064a0.166,0.166 0,0 0,0.008 -0.074a0.115,0.115 0,0 0,-0.037 -0.064a5.958,5.958 0,0 1,-1.38 -2.202a5.196,5.196 0,0 1,-0.333 -1.589a6.915,6.915 0,0 1,0.188 -2.132c0.45,-1.484 1.309,-2.648 2.577,-3.493c0.282,-0.188 0.55,-0.334 0.802,-0.438c0.286,-0.12 0.573,-0.22 0.861,-0.304a0.129,0.129 0,0 0,0.087 -0.087A6.016,6.016 0,0 1,5.635 2.31C6.315,1.464 7.132,0.846 8.086,0.457zM7.282,8.307a0.848,0.848 0,0 0,-1.473 0.842l1.694,2.965 -1.688,2.848a0.849,0.849 0,0 0,1.46 0.864l1.94,-3.272a0.849,0.849 0,0 0,0.007 -0.854l-1.94,-3.393zM12.728,14.547a0.849,0.849 0,0 0,0 1.695h4.848a0.849,0.849 0,0 0,0 -1.696h-4.848z" />
</vector>
10 changes: 10 additions & 0 deletions apps/mobile/assets/icons/agent-opencode.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"
android:pathData="M16,6H8v12h8V6zm4,16H4V2h16v20z" />
</vector>
1 change: 1 addition & 0 deletions apps/mobile/assets/icons/arrow-up.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#FF000000" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.59,5.58L20,12l-8,-8z" /></vector>
9 changes: 9 additions & 0 deletions apps/mobile/assets/icons/check.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>
1 change: 1 addition & 0 deletions apps/mobile/assets/icons/chevron-left.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#FF000000" android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z" /></vector>
1 change: 1 addition & 0 deletions apps/mobile/assets/icons/chevron-right.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#FF000000" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z" /></vector>
1 change: 1 addition & 0 deletions apps/mobile/assets/icons/close.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#FF000000" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" /></vector>
9 changes: 9 additions & 0 deletions apps/mobile/assets/icons/expand-less.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14l-6,-6z" />
</vector>
9 changes: 9 additions & 0 deletions apps/mobile/assets/icons/expand-more.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z" />
</vector>
9 changes: 9 additions & 0 deletions apps/mobile/assets/icons/more-vert.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
</vector>
9 changes: 9 additions & 0 deletions apps/mobile/assets/icons/search.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This asset has no importers anywhere under apps/mobile — the Android search affordance in use-trailing-actions.tsx uses a Lucide SearchIcon in an RN Pressable, and use-search-header-options.tsx uses Lucide's ArrowLeftIcon. The other 14 XMLs in this directory are all wired up; this one looks like it was added in anticipation and then not used. Either drop it or switch the search button over to it for consistency with the other Compose icon call sites.

android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>
1 change: 1 addition & 0 deletions apps/mobile/assets/icons/shield.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#FF000000" android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5zM19,11c0,4.52 -2.98,8.69 -7,9.93C7.98,19.69 5,15.52 5,11V6.3l7,-3.11 7,3.11z" /></vector>
1 change: 1 addition & 0 deletions apps/mobile/assets/icons/stop.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#FF000000" android:pathData="M6,6h12v12H6z" /></vector>
59 changes: 30 additions & 29 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
"dependencies": {
"@better-auth/expo": "1.7.0-rc.2",
"@expo/ui": "57.0.7",
"@expo/ui": "57.0.11",
"@gorhom/bottom-sheet": "^5.2.14",
"@linkcode/client-core": "workspace:*",
"@linkcode/common": "workspace:*",
Expand All @@ -29,51 +29,52 @@
"@react-native-community/netinfo": "12.0.1",
"@sentry/react-native": "~7.11.0",
"better-auth": "1.7.0-rc.2",
"expo": "~57.0.4",
"expo": "~57.0.14",
"expo-apple-authentication": "~57.0.1",
"expo-audio": "~57.0.0",
"expo-background-task": "~57.0.2",
"expo-camera": "~57.0.1",
"expo-clipboard": "~57.0.0",
"expo-constants": "~57.0.3",
"expo-audio": "~57.0.3",
"expo-background-task": "~57.0.11",
"expo-camera": "~57.0.3",
"expo-clipboard": "~57.0.1",
"expo-constants": "~57.0.12",
"expo-crypto": "~57.0.1",
"expo-dev-client": "~57.0.5",
"expo-device": "~57.0.0",
"expo-document-picker": "~57.0.0",
"expo-file-system": "~57.0.0",
"expo-font": "~57.0.0",
"expo-haptics": "~57.0.0",
"expo-image-picker": "~57.0.2",
"expo-dev-client": "~57.0.13",
"expo-device": "~57.0.1",
"expo-document-picker": "~57.0.1",
"expo-file-system": "~57.0.4",
"expo-font": "~57.0.1",
"expo-haptics": "~57.0.1",
"expo-image-picker": "~57.0.11",
"expo-libghostty": "^0.8.1",
"expo-linking": "~57.0.2",
"expo-local-authentication": "~57.0.0",
"expo-linking": "~57.0.6",
"expo-local-authentication": "~57.0.2",
"expo-network": "~57.0.1",
"expo-notifications": "~57.0.3",
"expo-router": "~57.0.4",
"expo-secure-store": "~57.0.0",
"expo-sharing": "~57.0.3",
"expo-sqlite": "~57.0.0",
"expo-status-bar": "~57.0.0",
"expo-task-manager": "~57.0.2",
"expo-updates": "~57.0.6",
"expo-web-browser": "~57.0.0",
"expo-notifications": "~57.0.12",
"expo-router": "~57.0.14",
"expo-secure-store": "~57.0.1",
"expo-sharing": "~57.0.13",
"expo-sqlite": "~57.0.1",
"expo-status-bar": "~57.0.1",
"expo-symbols": "~57.0.2",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing under apps/mobile/src imports from expo-symbols — the dependency is declared but unused, which widens the native build fingerprint for no benefit.

Also worth noting it cuts against the repo's own guidance: .agents/skills/expo-native-ui/SKILL.md:72 says to use "expo-image with source="sf:name" for SF Symbols, not expo-symbols or @expo/vector-icons". If this is intentional groundwork for a later change, it likely also needs an app.json plugin entry the way expo-font has one (app.json:90) — without it the package would silently not work on first use. Otherwise it should come back out.

"expo-task-manager": "~57.0.11",
"expo-updates": "~57.0.15",
"expo-web-browser": "~57.0.2",
"foxact": "^0.3.10",
"foxts": "^5.9.1",
"heroui-native": "1.0.6",
"lucide-react-native": "^1.25.0",
"posthog-react-native": "^4.60.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.86.0",
"react-native": "0.86.2",
"react-native-gesture-handler": "~2.32.0",
"react-native-keyboard-controller": "1.21.9",
"react-native-reanimated": "4.5.0",
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "4.25.2",
"react-native-screens": "4.26.2",
"react-native-svg": "15.15.4",
"react-native-web": "^0.21.2",
"react-native-webview": "13.16.1",
"react-native-worklets": "0.10.0",
"react-native-worklets": "0.10.1",
"tailwind-merge": "^3.6.0",
"tailwind-variants": "^3.3.0",
"uniwind": "1.10.0",
Expand Down
6 changes: 4 additions & 2 deletions apps/mobile/src/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ function TabsNavigator(): React.ReactNode {

return (
<NativeTabs>
{/* `md` is the Android glyph — without it the converter leaves the icon undefined and the
tab renders label-only; `sf` is never read there. */}
<NativeTabs.Trigger name="threads">
<NativeTabs.Trigger.Icon sf="bubble.left.and.text.bubble.right" />
<NativeTabs.Trigger.Icon sf="bubble.left.and.text.bubble.right" md="chat" />
<NativeTabs.Trigger.Label>{tThreads('title')}</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="terminals">
<NativeTabs.Trigger.Icon sf="apple.terminal" />
<NativeTabs.Trigger.Icon sf="apple.terminal" md="terminal" />
<NativeTabs.Trigger.Label>{tTerminals('title')}</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
{/* iOS 26's separated tab-bar slot (the `search` role) carries the focused tab's primary
Expand Down
Loading
Loading