Compare commits

..

2 Commits

Author SHA1 Message Date
Haileyesus
d638a8982c fix: do not show model description in chat view 2026-06-05 21:28:08 +03:00
Simos Mikelatos
f238050b85 feat(chat): open cost modal from token usage 2026-06-05 17:33:22 +00:00
5 changed files with 38 additions and 10 deletions

View File

@@ -311,7 +311,7 @@ export function useChatComposerState({
}, [addMessage]); }, [addMessage]);
const executeCommand = useCallback( const executeCommand = useCallback(
async (command: SlashCommand, rawInput?: string) => { async (command: SlashCommand, rawInput?: string, options?: { preserveInput?: boolean }) => {
if (!command || !selectedProject) { if (!command || !selectedProject) {
return; return;
} }
@@ -368,8 +368,10 @@ export function useChatComposerState({
const result = (await response.json()) as CommandExecutionResult; const result = (await response.json()) as CommandExecutionResult;
if (result.type === 'builtin') { if (result.type === 'builtin') {
handleBuiltInCommand(result); handleBuiltInCommand(result);
setInput(''); if (!options?.preserveInput) {
inputValueRef.current = ''; setInput('');
inputValueRef.current = '';
}
} else if (result.type === 'custom') { } else if (result.type === 'custom') {
await handleCustomCommand(result); await handleCustomCommand(result);
} }
@@ -400,6 +402,19 @@ export function useChatComposerState({
], ],
); );
const showCostModal = useCallback(() => {
executeCommand(
{
name: '/cost',
description: 'Display token usage information',
namespace: 'builtin',
metadata: { type: 'builtin' },
} as SlashCommand,
'/cost',
{ preserveInput: true },
);
}, [executeCommand]);
const { const {
slashCommands, slashCommands,
slashCommandsCount, slashCommandsCount,
@@ -1049,5 +1064,6 @@ export function useChatComposerState({
isInputFocused, isInputFocused,
commandModalPayload, commandModalPayload,
closeCommandModal, closeCommandModal,
showCostModal,
}; };
} }

View File

@@ -178,6 +178,7 @@ function ChatInterface({
isInputFocused: _isInputFocused, isInputFocused: _isInputFocused,
commandModalPayload, commandModalPayload,
closeCommandModal, closeCommandModal,
showCostModal,
} = useChatComposerState({ } = useChatComposerState({
selectedProject, selectedProject,
selectedSession, selectedSession,
@@ -368,6 +369,7 @@ function ChatInterface({
permissionMode={permissionMode} permissionMode={permissionMode}
onModeSwitch={cyclePermissionMode} onModeSwitch={cyclePermissionMode}
tokenBudget={tokenBudget} tokenBudget={tokenBudget}
onShowTokenUsage={showCostModal}
slashCommandsCount={slashCommandsCount} slashCommandsCount={slashCommandsCount}
onToggleCommandMenu={handleToggleCommandMenu} onToggleCommandMenu={handleToggleCommandMenu}
hasInput={Boolean(input.trim())} hasInput={Boolean(input.trim())}

View File

@@ -58,6 +58,7 @@ interface ChatComposerProps {
permissionMode: PermissionMode | string; permissionMode: PermissionMode | string;
onModeSwitch: () => void; onModeSwitch: () => void;
tokenBudget: Record<string, unknown> | null; tokenBudget: Record<string, unknown> | null;
onShowTokenUsage: () => void;
slashCommandsCount: number; slashCommandsCount: number;
onToggleCommandMenu: () => void; onToggleCommandMenu: () => void;
hasInput: boolean; hasInput: boolean;
@@ -111,6 +112,7 @@ export default function ChatComposer({
permissionMode, permissionMode,
onModeSwitch, onModeSwitch,
tokenBudget, tokenBudget,
onShowTokenUsage,
slashCommandsCount, slashCommandsCount,
onToggleCommandMenu, onToggleCommandMenu,
hasInput, hasInput,
@@ -353,7 +355,7 @@ export default function ChatComposer({
</div> </div>
</button> </button>
<TokenUsageSummary usage={tokenBudget} /> <TokenUsageSummary usage={tokenBudget} onClick={onShowTokenUsage} />
<PromptInputButton <PromptInputButton
tooltip={{ content: t('input.showAllCommands') }} tooltip={{ content: t('input.showAllCommands') }}

View File

@@ -277,11 +277,15 @@ export default function ProviderSelectionEmptyState({
> >
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="truncate">{model.label}</div> <div className="truncate">{model.label}</div>
{model.description && ( {/*
// * Temporarly commented out because the description of models from claude
// * was a bit inconsistent. Will return it back when it becomes more consistent.
*/}
{/* {model.description && (
<div className="truncate text-xs text-muted-foreground"> <div className="truncate text-xs text-muted-foreground">
{model.description} {model.description}
</div> </div>
)} )} */}
</div> </div>
{isSelected && ( {isSelected && (
<Check className="ml-auto h-4 w-4 shrink-0 text-primary" /> <Check className="ml-auto h-4 w-4 shrink-0 text-primary" />

View File

@@ -2,6 +2,7 @@ import { ActivityIcon } from 'lucide-react';
type TokenUsageSummaryProps = { type TokenUsageSummaryProps = {
usage: Record<string, unknown> | null; usage: Record<string, unknown> | null;
onClick?: () => void;
}; };
const formatTokenCount = (value: number) => { const formatTokenCount = (value: number) => {
@@ -29,7 +30,7 @@ const readUsageNumber = (value: unknown) => {
return Number.isFinite(parsed) ? parsed : 0; return Number.isFinite(parsed) ? parsed : 0;
}; };
export default function TokenUsageSummary({ usage }: TokenUsageSummaryProps) { export default function TokenUsageSummary({ usage, onClick }: TokenUsageSummaryProps) {
const breakdown = const breakdown =
usage?.breakdown && typeof usage.breakdown === 'object' usage?.breakdown && typeof usage.breakdown === 'object'
? usage.breakdown as Record<string, unknown> ? usage.breakdown as Record<string, unknown>
@@ -39,15 +40,18 @@ export default function TokenUsageSummary({ usage }: TokenUsageSummaryProps) {
const usedTokens = readUsageNumber(usage?.used) || inputTokens + outputTokens; const usedTokens = readUsageNumber(usage?.used) || inputTokens + outputTokens;
return ( return (
<div <button
className="inline-flex h-9 items-center gap-1.5 rounded-lg border border-border/70 bg-background/70 px-2 text-xs text-muted-foreground shadow-sm transition-colors hover:border-primary/25 hover:text-foreground sm:gap-2 sm:px-2.5" type="button"
onClick={onClick}
className="inline-flex h-9 items-center gap-1.5 rounded-lg border border-border/70 bg-background/70 px-2 text-xs text-muted-foreground shadow-sm transition-colors hover:border-primary/25 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 sm:gap-2 sm:px-2.5"
title={`${usedTokens.toLocaleString()} tokens used`} title={`${usedTokens.toLocaleString()} tokens used`}
aria-label="Show token usage"
> >
<span className="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary"> <span className="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary">
<ActivityIcon className="h-3.5 w-3.5" /> <ActivityIcon className="h-3.5 w-3.5" />
</span> </span>
<span className="font-medium text-foreground">{formatTokenCount(usedTokens)}</span> <span className="font-medium text-foreground">{formatTokenCount(usedTokens)}</span>
<span className="hidden text-muted-foreground/70 sm:inline">tokens</span> <span className="hidden text-muted-foreground/70 sm:inline">tokens</span>
</div> </button>
); );
} }