style: 完成所有文件的lint

This commit is contained in:
claude-code-best
2026-05-01 21:39:30 +08:00
parent d136872cc9
commit 6182015005
1333 changed files with 68255 additions and 77882 deletions

View File

@@ -168,9 +168,7 @@ export function Config({
const thinkingEnabled = useAppState(s => s.thinkingEnabled);
const isFastMode = useAppState(s => (isFastModeEnabled() ? s.fastMode : false));
const promptSuggestionEnabled = useAppState(s => s.promptSuggestionEnabled);
const currentDefaultPermissionMode = permissionModeFromString(
settingsData?.permissions?.defaultMode ?? 'default',
);
const currentDefaultPermissionMode = permissionModeFromString(settingsData?.permissions?.defaultMode ?? 'default');
// Show auto in the default-mode dropdown when the user has opted in OR the
// config is fully 'enabled' — even if currently circuit-broken ('disabled'),
// an opted-in user should still see it in settings (it's a temporary state).
@@ -572,9 +570,12 @@ export function Config({
const parsedMode = permissionModeFromString(mode);
// auto is an internal-only mode — store it directly, don't convert
// to its external mapping ('default') which would make it invisible.
const validatedMode = parsedMode === 'auto'
? parsedMode
: (isExternalPermissionMode(parsedMode) ? toExternalPermissionMode(parsedMode) : parsedMode);
const validatedMode =
parsedMode === 'auto'
? parsedMode
: isExternalPermissionMode(parsedMode)
? toExternalPermissionMode(parsedMode)
: parsedMode;
const result = updateSettingsForSource('userSettings', {
permissions: {
...settingsData?.permissions,

View File

@@ -1,41 +1,28 @@
// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
import * as React from 'react'
import { Suspense, useState } from 'react'
import { useKeybinding } from '../../keybindings/useKeybinding.js'
import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
import {
useIsInsideModal,
useModalOrTerminalSize,
} from '../../context/modalContext.js'
import { Pane, Tab, Tabs } from '@anthropic/ink'
import { Status, buildDiagnostics } from './Status.js'
import { Config } from './Config.js'
import { Usage } from './Usage.js'
import type {
LocalJSXCommandContext,
CommandResultDisplay,
} from '../../commands.js'
import * as React from 'react';
import { Suspense, useState } from 'react';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { useIsInsideModal, useModalOrTerminalSize } from '../../context/modalContext.js';
import { Pane, Tab, Tabs } from '@anthropic/ink';
import { Status, buildDiagnostics } from './Status.js';
import { Config } from './Config.js';
import { Usage } from './Usage.js';
import type { LocalJSXCommandContext, CommandResultDisplay } from '../../commands.js';
type Props = {
onClose: (
result?: string,
options?: { display?: CommandResultDisplay },
) => void
context: LocalJSXCommandContext
defaultTab: 'Status' | 'Config' | 'Usage'
}
onClose: (result?: string, options?: { display?: CommandResultDisplay }) => void;
context: LocalJSXCommandContext;
defaultTab: 'Status' | 'Config' | 'Usage';
};
export function Settings({
onClose,
context,
defaultTab,
}: Props): React.ReactNode {
const [selectedTab, setSelectedTab] = useState<string>(defaultTab)
const [tabsHidden, setTabsHidden] = useState(false)
export function Settings({ onClose, context, defaultTab }: Props): React.ReactNode {
const [selectedTab, setSelectedTab] = useState<string>(defaultTab);
const [tabsHidden, setTabsHidden] = useState(false);
// True while Config's own Esc handler is active (search mode with content
// focused). Settings must cede Esc so search can clear/exit first.
const [configOwnsEsc, setConfigOwnsEsc] = useState(false)
const [configOwnsEsc, setConfigOwnsEsc] = useState(false);
// Fixed content height so switching tabs doesn't shift the pane height.
// Outside modals cap at min(80% viewport, 30). Inside a Modal the modal's
// innerSize.rows IS the ScrollBox viewport — the 0.8 multiplier over-
@@ -45,40 +32,34 @@ export function Settings({
// marginY={1} (2 rows) which is stripped inside modals → +2 to recover.
// Then -2 for Tabs' header row + its marginTop=1. Plus +1 observed gap
// from the paneCap-10 estimate being slightly generous. Net: rows + 1.
const insideModal = useIsInsideModal()
const { rows } = useModalOrTerminalSize(useTerminalSize())
const contentHeight = insideModal
? rows + 1
: Math.max(15, Math.min(Math.floor(rows * 0.8), 30))
const insideModal = useIsInsideModal();
const { rows } = useModalOrTerminalSize(useTerminalSize());
const contentHeight = insideModal ? rows + 1 : Math.max(15, Math.min(Math.floor(rows * 0.8), 30));
// Kick off diagnostics once when the pane opens. Status use()s this so
// it resolves once per /config invocation — no re-fetch flash when
// tabbing back to Status (Tab unmounts children when not selected).
const [diagnosticsPromise] = useState(() =>
buildDiagnostics().catch(() => []),
)
const [diagnosticsPromise] = useState(() => buildDiagnostics().catch(() => []));
useExitOnCtrlCDWithKeybindings()
useExitOnCtrlCDWithKeybindings();
// Handle escape via keybinding - only when not in submenu
const handleEscape = () => {
// Don't handle escape when a submenu is showing (tabsHidden means submenu is open)
// Let the submenu handle escape to return to the main menu
if (tabsHidden) {
return
return;
}
// TODO: Update to "Settings" dialog once we define '/settings'.
onClose('Status dialog dismissed', { display: 'system' })
}
onClose('Status dialog dismissed', { display: 'system' });
};
// Disable when submenu is open so the submenu's Dialog can handle ESC,
// and when Config's search mode is active so its useInput handler
// (clear query → exit search) processes Escape first.
useKeybinding('confirm:no', handleEscape, {
context: 'Settings',
isActive:
!tabsHidden &&
!(selectedTab === 'Config' && configOwnsEsc),
})
isActive: !tabsHidden && !(selectedTab === 'Config' && configOwnsEsc),
});
const tabs = [
<Tab key="status" title="Status">
@@ -98,7 +79,7 @@ export function Settings({
<Tab key="usage" title="Usage">
<Usage />
</Tab>,
]
];
return (
<Pane color="permission">
@@ -119,5 +100,5 @@ export function Settings({
{tabs}
</Tabs>
</Pane>
)
);
}

View File

@@ -1,13 +1,13 @@
import figures from 'figures'
import * as React from 'react'
import { Suspense, use } from 'react'
import { getSessionId } from '../../bootstrap/state.js'
import type { LocalJSXCommandContext } from '../../commands.js'
import { useIsInsideModal } from '../../context/modalContext.js'
import { Box, Text, useTheme } from '@anthropic/ink'
import { type AppState, useAppState } from '../../state/AppState.js'
import { getCwd } from '../../utils/cwd.js'
import { getCurrentSessionTitle } from '../../utils/sessionStorage.js'
import figures from 'figures';
import * as React from 'react';
import { Suspense, use } from 'react';
import { getSessionId } from '../../bootstrap/state.js';
import type { LocalJSXCommandContext } from '../../commands.js';
import { useIsInsideModal } from '../../context/modalContext.js';
import { Box, Text, useTheme } from '@anthropic/ink';
import { type AppState, useAppState } from '../../state/AppState.js';
import { getCwd } from '../../utils/cwd.js';
import { getCurrentSessionTitle } from '../../utils/sessionStorage.js';
import {
buildAccountProperties,
buildAPIProviderProperties,
@@ -21,19 +21,19 @@ import {
type Diagnostic,
getModelDisplayLabel,
type Property,
} from '../../utils/status.js'
import type { ThemeName } from '../../utils/theme.js'
import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'
} from '../../utils/status.js';
import type { ThemeName } from '../../utils/theme.js';
import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js';
type Props = {
context: LocalJSXCommandContext
diagnosticsPromise: Promise<Diagnostic[]>
}
context: LocalJSXCommandContext;
diagnosticsPromise: Promise<Diagnostic[]>;
};
function buildPrimarySection(): Property[] {
const sessionId = getSessionId()
const customTitle = getCurrentSessionTitle(sessionId)
const nameValue = customTitle ?? <Text dimColor>/rename to add a name</Text>
const sessionId = getSessionId();
const customTitle = getCurrentSessionTitle(sessionId);
const nameValue = customTitle ?? <Text dimColor>/rename to add a name</Text>;
return [
{ label: 'Version', value: MACRO.VERSION },
@@ -42,7 +42,7 @@ function buildPrimarySection(): Property[] {
{ label: 'cwd', value: getCwd() },
...buildAccountProperties(),
...buildAPIProviderProperties(),
]
];
}
function buildSecondarySection({
@@ -51,24 +51,20 @@ function buildSecondarySection({
theme,
context,
}: {
mainLoopModel: AppState['mainLoopModel']
mcp: AppState['mcp']
theme: ThemeName
context: LocalJSXCommandContext
mainLoopModel: AppState['mainLoopModel'];
mcp: AppState['mcp'];
theme: ThemeName;
context: LocalJSXCommandContext;
}): Property[] {
const modelLabel = getModelDisplayLabel(mainLoopModel)
const modelLabel = getModelDisplayLabel(mainLoopModel);
return [
{ label: 'Model', value: modelLabel },
...buildIDEProperties(
mcp.clients,
context.options.ideInstallationStatus,
theme,
),
...buildIDEProperties(mcp.clients, context.options.ideInstallationStatus, theme),
...buildMcpProperties(mcp.clients, theme),
...buildSandboxProperties(),
...buildSettingSourcesProperties(),
]
];
}
export async function buildDiagnostics(): Promise<Diagnostic[]> {
@@ -76,14 +72,10 @@ export async function buildDiagnostics(): Promise<Diagnostic[]> {
...(await buildInstallationDiagnostics()),
...(await buildInstallationHealthDiagnostics()),
...(await buildMemoryDiagnostics()),
]
];
}
function PropertyValue({
value,
}: {
value: Property['value']
}): React.ReactNode {
function PropertyValue({ value }: { value: Property['value'] }): React.ReactNode {
if (Array.isArray(value)) {
return (
<Box flexWrap="wrap" columnGap={1} flexShrink={99}>
@@ -93,38 +85,32 @@ function PropertyValue({
{item}
{i < value.length - 1 ? ',' : ''}
</Text>
)
);
})}
</Box>
)
);
}
if (typeof value === 'string') {
return <Text>{value}</Text>
return <Text>{value}</Text>;
}
return value
return value;
}
export function Status({
context,
diagnosticsPromise,
}: Props): React.ReactNode {
const mainLoopModel = useAppState(s => s.mainLoopModel)
const mcp = useAppState(s => s.mcp)
const [theme] = useTheme()
export function Status({ context, diagnosticsPromise }: Props): React.ReactNode {
const mainLoopModel = useAppState(s => s.mainLoopModel);
const mcp = useAppState(s => s.mcp);
const [theme] = useTheme();
// Sections are synchronous — compute in render so they're never empty.
// diagnosticsPromise is created once in Settings.tsx so it resolves once
// per pane invocation instead of re-fetching on every tab switch (Tab
// unmounts children when not selected, which was causing the flash).
const sections = React.useMemo(
() => [
buildPrimarySection(),
buildSecondarySection({ mainLoopModel, mcp, theme, context }),
],
() => [buildPrimarySection(), buildSecondarySection({ mainLoopModel, mcp, theme, context })],
[mainLoopModel, mcp, theme, context],
)
);
// flexGrow so the "Esc to cancel" footer pins to the bottom of the
// Modal's inner ScrollBox when content is short. The ScrollBox content
@@ -132,7 +118,7 @@ export function Status({
// to match. Without it, short Status content floats at the top and the
// footer sits mid-modal with 2-3 trailing blank rows below. Outside a
// Modal (non-fullscreen), leave layout alone — no ScrollBox to fill.
const grow = useIsInsideModal() ? 1 : undefined
const grow = useIsInsideModal() ? 1 : undefined;
return (
<Box flexDirection="column" flexGrow={grow}>
@@ -156,37 +142,24 @@ export function Status({
</Suspense>
</Box>
<Text dimColor>
<ConfigurableShortcutHint
action="confirm:no"
context="Settings"
fallback="Esc"
description="cancel"
/>
<ConfigurableShortcutHint action="confirm:no" context="Settings" fallback="Esc" description="cancel" />
</Text>
</Box>
)
);
}
function Diagnostics({
promise,
}: {
promise: Promise<Diagnostic[]>
}): React.ReactNode {
const diagnostics = use(promise)
if (diagnostics.length === 0) return null
function Diagnostics({ promise }: { promise: Promise<Diagnostic[]> }): React.ReactNode {
const diagnostics = use(promise);
if (diagnostics.length === 0) return null;
return (
<Box flexDirection="column" paddingBottom={1}>
<Text bold>System Diagnostics</Text>
{diagnostics.map((diagnostic, i) => (
<Box key={i} flexDirection="row" gap={1} paddingX={1}>
<Text color="error">{figures.warning}</Text>
{typeof diagnostic === 'string' ? (
<Text wrap="wrap">{diagnostic}</Text>
) : (
diagnostic
)}
{typeof diagnostic === 'string' ? <Text wrap="wrap">{diagnostic}</Text> : diagnostic}
</Box>
))}
</Box>
)
);
}

View File

@@ -1,65 +1,51 @@
import * as React from 'react'
import { useEffect, useState } from 'react'
import { extraUsage as extraUsageCommand } from 'src/commands/extra-usage/index.js'
import { formatCost } from 'src/cost-tracker.js'
import { getSubscriptionType } from 'src/utils/auth.js'
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
import { Box, Text } from '@anthropic/ink'
import { useKeybinding } from '../../keybindings/useKeybinding.js'
import {
type ExtraUsage,
fetchUtilization,
type RateLimit,
type Utilization,
} from '../../services/api/usage.js'
import { formatResetText } from '../../utils/format.js'
import { logError } from '../../utils/log.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'
import { Byline, ProgressBar } from '@anthropic/ink'
import {
isEligibleForOverageCreditGrant,
OverageCreditUpsell,
} from '../LogoV2/OverageCreditUpsell.js'
import * as React from 'react';
import { useEffect, useState } from 'react';
import { extraUsage as extraUsageCommand } from 'src/commands/extra-usage/index.js';
import { formatCost } from 'src/cost-tracker.js';
import { getSubscriptionType } from 'src/utils/auth.js';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { Box, Text } from '@anthropic/ink';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
import { type ExtraUsage, fetchUtilization, type RateLimit, type Utilization } from '../../services/api/usage.js';
import { formatResetText } from '../../utils/format.js';
import { logError } from '../../utils/log.js';
import { jsonStringify } from '../../utils/slowOperations.js';
import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js';
import { Byline, ProgressBar } from '@anthropic/ink';
import { isEligibleForOverageCreditGrant, OverageCreditUpsell } from '../LogoV2/OverageCreditUpsell.js';
type LimitBarProps = {
title: string
limit: RateLimit
maxWidth: number
showTimeInReset?: boolean
extraSubtext?: string
}
title: string;
limit: RateLimit;
maxWidth: number;
showTimeInReset?: boolean;
extraSubtext?: string;
};
function LimitBar({
title,
limit,
maxWidth,
showTimeInReset = true,
extraSubtext,
}: LimitBarProps): React.ReactNode {
const { utilization, resets_at } = limit
function LimitBar({ title, limit, maxWidth, showTimeInReset = true, extraSubtext }: LimitBarProps): React.ReactNode {
const { utilization, resets_at } = limit;
if (utilization === null) {
return null
return null;
}
// Calculate usage percentage
const usedText = `${Math.floor(utilization)}% used`
const usedText = `${Math.floor(utilization)}% used`;
let subtext: string | undefined
let subtext: string | undefined;
if (resets_at) {
subtext = `Resets ${formatResetText(resets_at, true, showTimeInReset)}`
subtext = `Resets ${formatResetText(resets_at, true, showTimeInReset)}`;
}
if (extraSubtext) {
if (subtext) {
subtext = `${extraSubtext} · ${subtext}`
subtext = `${extraSubtext} · ${subtext}`;
} else {
subtext = extraSubtext
subtext = extraSubtext;
}
}
const maxBarWidth = 50
const usedLabelSpace = 12
const maxBarWidth = 50;
const usedLabelSpace = 12;
if (maxWidth >= maxBarWidth + usedLabelSpace) {
return (
<Box flexDirection="column">
@@ -75,7 +61,7 @@ function LimitBar({
</Box>
{subtext && <Text dimColor>{subtext}</Text>}
</Box>
)
);
} else {
return (
<Box flexDirection="column">
@@ -96,52 +82,46 @@ function LimitBar({
/>
<Text>{usedText}</Text>
</Box>
)
);
}
}
export function Usage(): React.ReactNode {
const [utilization, setUtilization] = useState<Utilization | null>(null)
const [error, setError] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(true)
const { columns } = useTerminalSize()
const [utilization, setUtilization] = useState<Utilization | null>(null);
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const { columns } = useTerminalSize();
const availableWidth = columns - 2 // 2 for screen padding
const maxWidth = Math.min(availableWidth, 80)
const availableWidth = columns - 2; // 2 for screen padding
const maxWidth = Math.min(availableWidth, 80);
const loadUtilization = React.useCallback(async () => {
setIsLoading(true)
setError(null)
setIsLoading(true);
setError(null);
try {
const data = await fetchUtilization()
setUtilization(data)
const data = await fetchUtilization();
setUtilization(data);
} catch (err) {
logError(err as Error)
const axiosError = err as { response?: { data?: unknown } }
const responseBody = axiosError.response?.data
? jsonStringify(axiosError.response.data)
: undefined
setError(
responseBody
? `Failed to load usage data: ${responseBody}`
: 'Failed to load usage data',
)
logError(err as Error);
const axiosError = err as { response?: { data?: unknown } };
const responseBody = axiosError.response?.data ? jsonStringify(axiosError.response.data) : undefined;
setError(responseBody ? `Failed to load usage data: ${responseBody}` : 'Failed to load usage data');
} finally {
setIsLoading(false)
setIsLoading(false);
}
}, [])
}, []);
useEffect(() => {
void loadUtilization()
}, [loadUtilization])
void loadUtilization();
}, [loadUtilization]);
useKeybinding(
'settings:retry',
() => {
void loadUtilization()
void loadUtilization();
},
{ context: 'Settings', isActive: !!error && !isLoading },
)
);
if (error) {
return (
@@ -149,22 +129,12 @@ export function Usage(): React.ReactNode {
<Text color="error">Error: {error}</Text>
<Text dimColor>
<Byline>
<ConfigurableShortcutHint
action="settings:retry"
context="Settings"
fallback="r"
description="retry"
/>
<ConfigurableShortcutHint
action="confirm:no"
context="Settings"
fallback="Esc"
description="cancel"
/>
<ConfigurableShortcutHint action="settings:retry" context="Settings" fallback="r" description="retry" />
<ConfigurableShortcutHint action="confirm:no" context="Settings" fallback="Esc" description="cancel" />
</Byline>
</Text>
</Box>
)
);
}
if (!utilization) {
@@ -172,26 +142,18 @@ export function Usage(): React.ReactNode {
<Box flexDirection="column" gap={1}>
<Text dimColor>Loading usage data</Text>
<Text dimColor>
<ConfigurableShortcutHint
action="confirm:no"
context="Settings"
fallback="Esc"
description="cancel"
/>
<ConfigurableShortcutHint action="confirm:no" context="Settings" fallback="Esc" description="cancel" />
</Text>
</Box>
)
);
}
// Only Max and Team plans have a Sonnet limit that differs from the weekly
// limit (see rateLimitMessages.ts). For other plans the bar is redundant.
// Show for null (unknown plan) to stay consistent with rateLimitMessages.ts,
// which labels it "Sonnet limit" in that case.
const subscriptionType = getSubscriptionType()
const showSonnetBar =
subscriptionType === 'max' ||
subscriptionType === 'team' ||
subscriptionType === null
const subscriptionType = getSubscriptionType();
const showSonnetBar = subscriptionType === 'max' || subscriptionType === 'team' || subscriptionType === null;
const limits = [
{
@@ -210,65 +172,40 @@ export function Usage(): React.ReactNode {
},
]
: []),
]
];
return (
<Box flexDirection="column" gap={1} width="100%">
{limits.some(({ limit }) => limit) || (
<Text dimColor>/usage is only available for subscription plans.</Text>
)}
{limits.some(({ limit }) => limit) || <Text dimColor>/usage is only available for subscription plans.</Text>}
{limits.map(
({ title, limit }) =>
limit && (
<LimitBar
key={title}
title={title}
limit={limit}
maxWidth={maxWidth}
/>
),
({ title, limit }) => limit && <LimitBar key={title} title={title} limit={limit} maxWidth={maxWidth} />,
)}
{utilization.extra_usage && (
<ExtraUsageSection
extraUsage={utilization.extra_usage}
maxWidth={maxWidth}
/>
)}
{utilization.extra_usage && <ExtraUsageSection extraUsage={utilization.extra_usage} maxWidth={maxWidth} />}
{isEligibleForOverageCreditGrant() && (
<OverageCreditUpsell maxWidth={maxWidth} />
)}
{isEligibleForOverageCreditGrant() && <OverageCreditUpsell maxWidth={maxWidth} />}
<Text dimColor>
<ConfigurableShortcutHint
action="confirm:no"
context="Settings"
fallback="Esc"
description="cancel"
/>
<ConfigurableShortcutHint action="confirm:no" context="Settings" fallback="Esc" description="cancel" />
</Text>
</Box>
)
);
}
type ExtraUsageSectionProps = {
extraUsage: ExtraUsage
maxWidth: number
}
extraUsage: ExtraUsage;
maxWidth: number;
};
const EXTRA_USAGE_SECTION_TITLE = 'Extra usage'
const EXTRA_USAGE_SECTION_TITLE = 'Extra usage';
function ExtraUsageSection({
extraUsage,
maxWidth,
}: ExtraUsageSectionProps): React.ReactNode {
const subscriptionType = getSubscriptionType()
const isProOrMax = subscriptionType === 'pro' || subscriptionType === 'max'
function ExtraUsageSection({ extraUsage, maxWidth }: ExtraUsageSectionProps): React.ReactNode {
const subscriptionType = getSubscriptionType();
const isProOrMax = subscriptionType === 'pro' || subscriptionType === 'max';
if (!isProOrMax) {
// Only show to Pro and Max, consistent with claude.ai non-admin usage settings
return false
return false;
}
if (!extraUsage.is_enabled) {
@@ -278,10 +215,10 @@ function ExtraUsageSection({
<Text bold>{EXTRA_USAGE_SECTION_TITLE}</Text>
<Text dimColor>Extra usage not enabled · /extra-usage to enable</Text>
</Box>
)
);
}
return null
return null;
}
if (extraUsage.monthly_limit === null) {
@@ -290,20 +227,17 @@ function ExtraUsageSection({
<Text bold>{EXTRA_USAGE_SECTION_TITLE}</Text>
<Text dimColor>Unlimited</Text>
</Box>
)
);
}
if (
typeof extraUsage.used_credits !== 'number' ||
typeof extraUsage.utilization !== 'number'
) {
return null
if (typeof extraUsage.used_credits !== 'number' || typeof extraUsage.utilization !== 'number') {
return null;
}
const formattedUsedCredits = formatCost(extraUsage.used_credits / 100, 2)
const formattedMonthlyLimit = formatCost(extraUsage.monthly_limit / 100, 2)
const now = new Date()
const oneMonthReset = new Date(now.getFullYear(), now.getMonth() + 1, 1)
const formattedUsedCredits = formatCost(extraUsage.used_credits / 100, 2);
const formattedMonthlyLimit = formatCost(extraUsage.monthly_limit / 100, 2);
const now = new Date();
const oneMonthReset = new Date(now.getFullYear(), now.getMonth() + 1, 1);
return (
<LimitBar
@@ -317,5 +251,5 @@ function ExtraUsageSection({
extraSubtext={`${formattedUsedCredits} / ${formattedMonthlyLimit} spent`}
maxWidth={maxWidth}
/>
)
);
}