From e41818893bd6c00c9972fd26584367e07f3f523f Mon Sep 17 00:00:00 2001 From: SAPHID Date: Thu, 12 Nov 2026 02:48:24 +0000 Subject: [PATCH 1/3] fix(mobile): Claude accounts in Providers fit on one row A Claude account's config folder wrapped mid-word under its status, so those rows stood taller than every other provider. The status and folder now share one line and a long path is cut at its start, keeping the part that tells accounts apart. The Add Claude account row drops its two-line description; the form it opens already says what the folder needs. Co-Authored-By: Claude Fable 5.2 --- .../src/features/settings/ClaudeAccounts.tsx | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/mobile/src/features/settings/ClaudeAccounts.tsx b/apps/mobile/src/features/settings/ClaudeAccounts.tsx index 9b41e2a6b35b..06c2b1862871 100644 --- a/apps/mobile/src/features/settings/ClaudeAccounts.tsx +++ b/apps/mobile/src/features/settings/ClaudeAccounts.tsx @@ -67,6 +67,7 @@ export function ClaudeAccount({ const { run, pending, error } = useProviderInstanceMutation(environment); const instance = environment.serverConfig.settings.providerInstances[provider.instanceId]; const configDir = claudeConfigDir(instance); + const folder = configDir || "Default config folder"; const name = provider.displayName ?? "Claude"; const status = !provider.enabled ? "Turned off" @@ -119,9 +120,26 @@ export function ClaudeAccount({ {name} - - {[status, configDir || "Default config folder"].filter(Boolean).join(" · ")} - + + {status ? ( + + {`${status} · `} + + ) : null} + {/* The folder's last segments tell accounts apart, so a long path loses its start. */} + + {folder} + + {editing ? null : ( setAdding(false)} /> ) : ( - setAdding(true)} - /> + setAdding(true)} /> )} ); From 5f413ccf82b7deb6652ffd637aff822a2b6eeb9f Mon Sep 17 00:00:00 2001 From: SAPHID Date: Thu, 12 Nov 2026 06:54:42 +0000 Subject: [PATCH 2/3] fix(mobile): Providers lists turned-on providers first, without folder paths Cutting the Claude config folder to one line still left a clipped path under every Claude row. The rows now show a name and a status like every other provider, and the folder stays in the Edit form. Providers that are turned on lead the list, so turned-off ones no longer fill the top of the screen. Co-Authored-By: Claude Fable 5.2 --- .../src/features/settings/ClaudeAccounts.tsx | 30 ++++----------- .../SettingsProviderAccountsRouteScreen.tsx | 37 +++++++++++-------- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/apps/mobile/src/features/settings/ClaudeAccounts.tsx b/apps/mobile/src/features/settings/ClaudeAccounts.tsx index 06c2b1862871..b9fa96244d6b 100644 --- a/apps/mobile/src/features/settings/ClaudeAccounts.tsx +++ b/apps/mobile/src/features/settings/ClaudeAccounts.tsx @@ -59,15 +59,16 @@ function useProviderInstanceMutation(environment: SettingsTarget) { export function ClaudeAccount({ environment, provider, + divider, }: { readonly environment: SettingsTarget; readonly provider: ServerProvider; + readonly divider: boolean; }) { const [editing, setEditing] = useState(false); const { run, pending, error } = useProviderInstanceMutation(environment); const instance = environment.serverConfig.settings.providerInstances[provider.instanceId]; const configDir = claudeConfigDir(instance); - const folder = configDir || "Default config folder"; const name = provider.displayName ?? "Claude"; const status = !provider.enabled ? "Turned off" @@ -77,7 +78,7 @@ export function ClaudeAccount({ ? "Not signed in" : !provider.installed ? "Not installed" - : null; + : "Sign-in not checked"; const removable = provider.instanceId !== DEFAULT_CLAUDE_INSTANCE_ID && !isManagedSettingPath( @@ -114,32 +115,15 @@ export function ClaudeAccount({ ); } return ( - + {name} - - {status ? ( - - {`${status} · `} - - ) : null} - {/* The folder's last segments tell accounts apart, so a long path loses its start. */} - - {folder} - - + + {status} + {editing ? null : ( ; }) { - const accounts = providers.filter(canManageProvider); // Claude has no in-app sign-in; each account is its own instance on a signed-in config folder. - const claudeAccounts = providers.filter( - (provider) => provider.driver === CLAUDE_DRIVER && !canManageProvider(provider), + const listed = providers.filter( + (provider) => canManageProvider(provider) || provider.driver === CLAUDE_DRIVER, ); + const hasInAppSignIn = listed.some(canManageProvider); + // Providers that are turned on lead; turned-off ones follow, each group in the server's order. + const ordered = [ + ...listed.filter((provider) => provider.enabled), + ...listed.filter((provider) => !provider.enabled), + ]; const canAddClaude = isManagedDriverAllowed( environment.serverConfig.managedPolicy, CLAUDE_DRIVER, ); return ( <> - {accounts.map((provider, index) => ( - 0} - /> - ))} - {accounts.length === 0 ? ( + {hasInAppSignIn ? null : ( Configure a provider with in-app sign-in in web or desktop Settings. - ) : null} - {claudeAccounts.map((provider) => ( - - ))} + )} + {ordered.map((provider, index) => { + const Row = canManageProvider(provider) ? ProviderAccount : ClaudeAccount; + return ( + 0 || !hasInAppSignIn} + /> + ); + })} {canAddClaude ? : null} ); From 724a642678bb4e879db2bce2d56db3cb688f7576 Mon Sep 17 00:00:00 2001 From: SAPHID Date: Fri, 13 Nov 2026 00:47:47 +0000 Subject: [PATCH 3/3] fix(mobile): Providers rows are one line, with Turned off beside the name Co-Authored-By: Claude Fable 5.2 --- .../src/features/settings/ClaudeAccounts.tsx | 35 ++++--- .../SettingsProviderAccountsRouteScreen.tsx | 98 +++++++++++-------- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/apps/mobile/src/features/settings/ClaudeAccounts.tsx b/apps/mobile/src/features/settings/ClaudeAccounts.tsx index b9fa96244d6b..85f898ba1f76 100644 --- a/apps/mobile/src/features/settings/ClaudeAccounts.tsx +++ b/apps/mobile/src/features/settings/ClaudeAccounts.tsx @@ -10,7 +10,7 @@ import { type ServerProvider, } from "@t3tools/contracts"; import { useRef, useState } from "react"; -import { TextInput, View } from "react-native"; +import { TextInput, useWindowDimensions, View } from "react-native"; import * as Dialog from "../../components/ui/Dialog"; import { AppText as Text } from "../../components/ui/AppText"; @@ -59,26 +59,27 @@ function useProviderInstanceMutation(environment: SettingsTarget) { export function ClaudeAccount({ environment, provider, - divider, }: { readonly environment: SettingsTarget; readonly provider: ServerProvider; - readonly divider: boolean; }) { const [editing, setEditing] = useState(false); const { run, pending, error } = useProviderInstanceMutation(environment); const instance = environment.serverConfig.settings.providerInstances[provider.instanceId]; const configDir = claudeConfigDir(instance); const name = provider.displayName ?? "Claude"; + // A signed-in account needs no label; the row only speaks up when something is off. const status = !provider.enabled ? "Turned off" : provider.auth.status === "authenticated" - ? "Signed in" + ? null : provider.auth.status === "unauthenticated" ? "Not signed in" : !provider.installed ? "Not installed" : "Sign-in not checked"; + // Matches ListRowLayout: large text stacks the status below instead of squeezing the name. + const stacked = useWindowDimensions().fontScale >= 1.5; const removable = provider.instanceId !== DEFAULT_CLAUDE_INSTANCE_ID && !isManagedSettingPath( @@ -115,15 +116,27 @@ export function ClaudeAccount({ ); } return ( - - - - + + + + {name} - - {status} - + {status ? ( + + {status} + + ) : null} {editing ? null : ( ; }) { + const accounts = providers.filter(canManageProvider); // Claude has no in-app sign-in; each account is its own instance on a signed-in config folder. - const listed = providers.filter( - (provider) => canManageProvider(provider) || provider.driver === CLAUDE_DRIVER, + const claudeAccounts = providers.filter( + (provider) => provider.driver === CLAUDE_DRIVER && !canManageProvider(provider), ); - const hasInAppSignIn = listed.some(canManageProvider); - // Providers that are turned on lead; turned-off ones follow, each group in the server's order. - const ordered = [ - ...listed.filter((provider) => provider.enabled), - ...listed.filter((provider) => !provider.enabled), - ]; const canAddClaude = isManagedDriverAllowed( environment.serverConfig.managedPolicy, CLAUDE_DRIVER, ); return ( <> - {hasInAppSignIn ? null : ( + {accounts.map((provider, index) => ( + 0} + /> + ))} + {accounts.length === 0 ? ( Configure a provider with in-app sign-in in web or desktop Settings. - )} - {ordered.map((provider, index) => { - const Row = canManageProvider(provider) ? ProviderAccount : ClaudeAccount; - return ( - 0 || !hasInAppSignIn} - /> - ); - })} + ) : null} + {claudeAccounts.map((provider) => ( + + ))} {canAddClaude ? : null} ); @@ -248,22 +243,23 @@ function ProviderAccount({ const usesCredentials = provider.driver === "antigravity" && (provider.auth.type === "gemini-api-key" || provider.auth.type === "agent-platform"); - const status = - active || state?.phase === "failed" || state?.phase === "cancelled" - ? state.message - : !provider.enabled - ? "Turned off" - : signedIn - ? "Signed in" - : isDiscovering - ? "Discovering sign-in methods…" - : needsExternalSetup - ? "No in-app sign-in advertised. Follow the provider's docs to finish setup." - : !provider.installed - ? "Not installed" - : usesCredentials - ? "Connect to load models" - : "Not signed in"; + const showsFlow = active || state?.phase === "failed" || state?.phase === "cancelled"; + const turnedOff = !showsFlow && !provider.enabled; + const status = showsFlow + ? state.message + : turnedOff + ? "Turned off" + : signedIn + ? "Signed in" + : isDiscovering + ? "Discovering sign-in methods…" + : needsExternalSetup + ? "No in-app sign-in advertised. Follow the provider's docs to finish setup." + : !provider.installed + ? "Not installed" + : usesCredentials + ? "Connect to load models" + : "Not signed in"; // A provider with several sign-in methods picks one from a menu on the same control. const methodActions: MenuAction[] = methods.map((method) => ({ id: `method:${method.id}`, @@ -407,10 +403,18 @@ function ProviderAccount({ className={ stacked ? "min-h-16 items-start gap-3 px-4 py-3" - : "min-h-16 flex-row items-center gap-3 px-4 py-3" + : turnedOff + ? "min-h-14 flex-row items-center gap-3 px-4 py-1.5" + : "min-h-16 flex-row items-center gap-3 px-4 py-3" } > - + {trailing} {interaction?.type === "deviceCode" || @@ -516,15 +520,25 @@ function ProviderAccountSummary({ name, status, email, + inline, }: { readonly name: string; readonly status: string | null | undefined; readonly email: string | undefined; + /** Puts a short status beside the name, so the row is one line tall. */ + readonly inline: boolean; }) { const [revealed, setRevealed] = useState(false); + const layout = inline + ? "min-w-0 shrink grow flex-row items-center gap-3" + : "min-w-0 shrink grow gap-0.5"; const content = ( <> - + {name} @@ -537,7 +551,7 @@ function ProviderAccountSummary({ {content} @@ -549,7 +563,7 @@ function ProviderAccountSummary({ accessibilityLabel={[name, status, revealed ? email : null].filter(Boolean).join(", ")} accessibilityHint={revealed ? "Hides the account email" : "Reveals the account email"} onPress={() => setRevealed((value) => !value)} - className="min-w-0 shrink grow gap-0.5" + className={layout} > {content}