Librechat-Mobile / app
CLAUDE.md149 starsChanged 7 days agoApache-2.0View on GitHub
Single Activity; MainActivity is the sole entry point. Depends on :shared, all :core:* and :feature:*.
In this repository13 other files it gives its agents.
- build-logic/CLAUDE.mdCLAUDE.md
- CLAUDE.mdCLAUDE.md
- fastlane/CLAUDE.mdCLAUDE.md
- .github/CLAUDE.mdCLAUDE.md
- shared/CLAUDE.mdCLAUDE.md
- audit-depsSkill
- audit-i18nSkill
- audit-paritySkill
- marketing-refreshSkill
- release-highlightsSkill
- small-screensSkill
- sync-upstreamSkill
- update-web-assetsSkill
The file
Files
App Module
Single Activity; MainActivity is the sole entry point. Depends on :shared, all :core:* and :feature:*.
Navigation
LibreChatNavHost.kt (this module) wraps the shared LibreChatNavHost via a content lambda, adding deep
links, share-intent routing and phone/tablet branching. The shared module owns the rest (Navigator,
NavHostViewModel, MainNavDisplay, PhoneLayout, TabletLayout, drawer, all entry providers): see
shared/CLAUDE.md. Feature modules get onNavigate/onBack lambdas, never NavBackStack.
Layout modes
- Tablet layout at 680dp+ wide and 480dp+ tall (320dp sidebar + 360dp pane), else the phone drawer. Android
picks here, iOS in the shared host, both via
usesSidebarLayout(TabletLayout.kt): change the breakpoint there, not at a call site. - iOS switches layouts in place, so both layouts must place the one
rememberMovableNavDisplayinstance (aMainNavDisplayleaving composition clears every entry's ViewModels). Keep the layout choice in the host's own composition, not a subcomposition likeBoxWithConstraints— only that case is covered (MovableNavDisplayTest).
Deep links
- Scheme
librechat://; hostsconversation/{id},artifact/{uuid},oauth. - Single source of truth:
DeepLinks.resolve()(:shared/commonMain) →DeepLinkResolution(Route(target, requiresAuth)/Consumed/None). Add a link as one branch there; do NOT reintroduce a per-host allowlist here. MainActivity.handleDeepLink()(andonNewIntent) resolves to accept/drop (DeepLinkUriAdapter.kt), thenLibreChatNavHostresolves again to place the target.requiresAuthlinks go to login when signed out; artifact links open signed out.oauthisConsumed(sign-in is in-app WebView); keep the mapping so links from older builds still bring the app forward.
Share intents
MainActivity.handleShareIntent()only stages onShareIntentConsumer.LibreChatNavHostreadsnavigator.currentRouteand callsdispatchTo(conversationId)(null=NewChatlanding), navigating to a chat first if none is on screen.- A share must be addressed, never broadcast: several
ChatViewModels are alive (the landing sits beneath an openChat), and an unaddressed share goes to whichever collects first. Each screen usessharesFor(conversationId); addressing lives in the nav host. - Drive delivery off
ShareIntentConsumer.undelivered, not an activity counter: the launch intent is processed only on a fresh start, so a recreation during the theme/locale warm-up would strand the share.
Server banners
GET /api/bannerreturns one object or a 200 with empty body and noContent-Type, never an array.BannerApireads the raw body; anything else still throws, so a proxy answer can't read as "no banner".NavHostViewModelfetches viaBannerRepositoryon init, account switch and auth-complete; no timer.- The server applies
displayFrom/displayToandtype: 'banner'; don't re-check client-side (clock skew). - Dismissal lives in
BannerStateHolder: in-memory (serverId, bannerId) key. Both halves are needed: by banner alone, a bannerId shared across servers arrives pre-dismissed; cleared on switch, A's banner returns.clearForAccountChange()drops the banner but must not touch dismissals. BannerDisplaytakes a nullable banner and decides visibility itself; don't hoist the null check (kills the enter/exit animation).- One treatment,
secondaryContainer/onSecondaryContainer. Don't re-introduce atype-keyed palette or hardcoded hex (server only sendstype: "banner"; fixed colours ignore the accent seed and dark mode). persistablehides dismiss; a banner with nobannerIdis not rendered.
Other
- Session (shared
NavHostViewModel): start atNewChatif signed in, elseServerUrl;sessionExpiredclears the whole back stack into auth; drawer gestures are off during auth. - Top-level routes:
NewChat,Conversations(drawer body),AgentMarketplace,Files,SettingsTabbed(drawer footer), all registered in sharedMainNavDisplay. ConnectivityObserver(injected intoMainActivity) drives the offline banner.PresetManagerroute is registered inSettingsNavigation.kt(Settings → Chat).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

