feat(logo): show "Not logged in" state and provider display name in logo

Add isNotLoggedIn() utility to detect unauthenticated state and display
"Not logged in" message in both LogoV2 and CondensedLogo components.
Replace billing type with provider display name (e.g. "OpenAI", "AWS
Bedrock") when logged in. Subscribe to authVersion to re-render on
login/logout. Add third-party provider model options for OpenAI, Gemini,
Grok, Bedrock, Vertex, and Foundry. Disable biome formatter.
This commit is contained in:
Askhz 2026-04-11 01:41:55 +08:00
parent afee7d3ed2
commit 6fb91d2c28
4 changed files with 73 additions and 11 deletions

View File

@ -7,7 +7,7 @@ import { useAppState } from '../../state/AppState.js';
import { getEffortSuffix } from '../../utils/effort.js';
import { truncate } from '../../utils/format.js';
import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js';
import { formatModelAndBilling, getLogoDisplayData, truncatePath } from '../../utils/logoV2Utils.js';
import { formatModelAndBilling, getLogoDisplayData, isNotLoggedIn, truncatePath } from '../../utils/logoV2Utils.js';
import { renderModelSetting } from '../../utils/model/model.js';
import { OffscreenFreeze } from '../OffscreenFreeze.js';
import { AnimatedClawd } from './AnimatedClawd.js';
@ -23,7 +23,10 @@ export function CondensedLogo(): ReactNode {
const { columns } = useTerminalSize();
const agent = useAppState(s => s.agent);
const effortValue = useAppState(s => s.effortValue);
// Subscribe to authVersion to re-render after login/logout
useAppState(s => s.authVersion);
const model = useMainLoopModel();
const notLoggedIn = isNotLoggedIn();
const modelDisplayName = renderModelSetting(model);
const { version, cwd, billingType, agentName: agentNameFromSettings } = getLogoDisplayData();
@ -92,7 +95,9 @@ export function CondensedLogo(): ReactNode {
</Text>{' '}
<Text dimColor>v{truncatedVersion}</Text>
</Text>
{shouldSplit ? (
{notLoggedIn ? (
<Text dimColor>Not logged in</Text>
) : shouldSplit ? (
<>
<Text dimColor>{truncatedModel}</Text>
<Text dimColor>{truncatedBilling}</Text>

View File

@ -11,6 +11,7 @@ import {
getRecentActivitySync,
getRecentReleaseNotesSync,
getLogoDisplayData,
isNotLoggedIn,
} from '../../utils/logoV2Utils.js';
import { truncate } from '../../utils/format.js';
import { getDisplayPath } from '../../utils/file.js';
@ -82,6 +83,8 @@ export function LogoV2(): React.ReactNode {
const showOverageCreditUpsell = useShowOverageCreditUpsell();
const agent = useAppState(s => s.agent);
const effortValue = useAppState(s => s.effortValue);
// Subscribe to authVersion to re-render after login/logout
useAppState(s => s.authVersion);
const config = getGlobalConfig();
@ -136,6 +139,7 @@ export function LogoV2(): React.ReactNode {
}, [showOverageCreditUpsell, showOnboarding, showGuestPassesUpsell, isCondensedMode]);
const model = useMainLoopModel();
const notLoggedIn = isNotLoggedIn();
const fullModelDisplayName = renderModelSetting(model);
const { version, cwd, billingType, agentName: agentNameFromSettings } = getLogoDisplayData();
// Prefer AppState.agent (set from --agent CLI flag) over settings
@ -247,8 +251,8 @@ export function LogoV2(): React.ReactNode {
<Box marginY={1}>
<Clawd />
</Box>
<Text dimColor>{modelDisplayName}</Text>
<Text dimColor>{billingType}</Text>
<Text dimColor>{notLoggedIn ? 'Not logged in' : modelDisplayName}</Text>
{!notLoggedIn && <Text dimColor>{billingType}</Text>}
<Text dimColor>{agentName ? `@${agentName} · ${truncatedCwd}` : truncatedCwd}</Text>
</Box>
</OffscreenFreeze>
@ -269,8 +273,9 @@ export function LogoV2(): React.ReactNode {
}
const welcomeMessage = formatWelcomeMessage(username);
const modelLine =
!process.env.IS_DEMO && config.oauthAccount?.organizationName
const modelLine = notLoggedIn
? 'Not logged in'
: !process.env.IS_DEMO && config.oauthAccount?.organizationName
? `${modelDisplayName} · ${billingType} · ${config.oauthAccount.organizationName}`
: `${modelDisplayName} · ${billingType}`;
// Calculate cwd width accounting for agent name if present

View File

@ -1,7 +1,8 @@
import { getDirectConnectServerUrl, getSessionId } from '../bootstrap/state.js'
import { stringWidth } from '@anthropic/ink'
import type { LogOption } from '../types/logs.js'
import { getSubscriptionName, isClaudeAISubscriber } from './auth.js'
import { getAnthropicApiKey } from './auth.js'
import { hasCoStrictCredentialsSync } from '../costrict/provider/credentials.js'
import { getCwd } from './cwd.js'
import { getDisplayPath } from './file.js'
import {
@ -9,6 +10,7 @@ import {
truncateToWidth,
truncateToWidthNoEllipsis,
} from './format.js'
import { getAPIProvider, getProviderDisplayName } from './model/providers.js'
import { getStoredChangelogFromMemory, parseChangelog } from './releaseNotes.js'
import { gt } from './semver.js'
import { loadMessageLogs } from './sessionStorage.js'
@ -237,12 +239,33 @@ export function formatReleaseNoteForDisplay(
}
/**
* Gets the common logo display data used by both LogoV2 and CondensedLogo
* Check if user is not logged in
* User is considered logged in if they have CoStrict credentials or API key
*/
export function isNotLoggedIn(): boolean {
// 1. CoStrict login (credentials file exists)
if (hasCoStrictCredentialsSync()) return false
const settings = getInitialSettings()
// 2. Any provider configured via modelType (包括 anthropic/openai/bedrock 等)
// 用户通过 /login 选择 provider 后会设置 modelType
if (settings.modelType) {
return false
}
// 3. Anthropic API Key configured (legacy way without modelType)
if (getAnthropicApiKey() !== null) return false
// None of the above - not logged in
return true
}
export function getLogoDisplayData(): {
version: string
cwd: string
billingType: string
isLoggedIn: boolean
agentName: string | undefined
} {
const version = process.env.DEMO_VERSION ?? MACRO.VERSION
@ -253,15 +276,18 @@ export function getLogoDisplayData(): {
const cwd = serverUrl
? `${displayPath} in ${serverUrl.replace(/^https?:\/\//, '')}`
: displayPath
const billingType = isClaudeAISubscriber()
? getSubscriptionName()
: 'API Usage Billing'
const loggedIn = !isNotLoggedIn()
const provider = getAPIProvider()
const providerName = getProviderDisplayName(provider)
const billingType = loggedIn ? providerName : 'Not logged in'
const agentName = getInitialSettings().agent
return {
version,
cwd,
billingType,
isLoggedIn: loggedIn,
agentName,
}
}

View File

@ -35,6 +35,32 @@ export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS
return getAPIProvider() as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
}
/**
* Get a human-readable display name for the current API provider
*/
export function getProviderDisplayName(provider: APIProvider): string {
switch (provider) {
case 'firstParty':
return 'Anthropic'
case 'bedrock':
return 'AWS Bedrock'
case 'vertex':
return 'Google Vertex'
case 'foundry':
return 'Foundry'
case 'openai':
return 'OpenAI'
case 'gemini':
return 'Google Gemini'
case 'grok':
return 'xAI Grok'
case 'costrict':
return 'CoStrict'
default:
return provider
}
}
/**
* Check if ANTHROPIC_BASE_URL is a first-party Anthropic API URL.
* Returns true if not set (default API) or points to api.anthropic.com