Files
CodePilot/src/hooks/useContextUsage.ts
T
7418andClaude Opus 4.6 111fa8c92e feat: refactor composer to two-layer layout, add session permission & context indicator
Composer restructure:
- MessageInput internal footer now only contains: file button, model
  selector, slash command button, and send button
- Removed mode toggle (Code/Plan) from composer area (logic preserved)
- New ChatComposerActionBar below input with three slots: left (Design
  Agent), center (permission selector), right (context indicator)

New components:
- SlashCommandButton: inserts `/` at cursor, reuses existing slash
  command detection and popover logic
- ChatPermissionSelector: session-level permission toggle (default /
  full_access) with danger confirmation dialog
- ContextUsageIndicator: SVG donut chart showing context usage ratio
  with HoverCard displaying model, tokens, cache stats
- ChatComposerActionBar: slot-based container for action bar items

Session permission (permission_profile):
- DB: new column on chat_sessions, default 'default'
- API: PATCH sessions supports updating permission_profile
- Chat API: bypassPermissions = global setting OR session full_access
- PermissionPrompt: auto-approves and hides when full_access
- Claude client: combines global and per-session bypass flags

Bridge integration:
- permission-broker: auto-approves permissions for full_access sessions
  without sending IM notifications
- conversation-engine: passes bypassPermissions to SDK for full_access
- PATCH route: auto-approves pending bridge permissions on switch to
  full_access

Other:
- ImageGenToggle relabeled to "Design Agent" / "设计 Agent"
- New model-context.ts with MODEL_CONTEXT_WINDOWS lookup
- Provider models API returns optional contextWindow field
- New useContextUsage hook for token usage calculation
- i18n: added composer.*, permission.*, context.* keys (en + zh)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 14:55:38 +08:00

64 lines
1.7 KiB
TypeScript

import { useMemo } from 'react';
import type { Message } from '@/types';
import { getContextWindow } from '@/lib/model-context';
export interface ContextUsageData {
modelName: string;
contextWindow: number | null;
used: number;
ratio: number;
cacheReadTokens: number;
cacheCreationTokens: number;
outputTokens: number;
hasData: boolean;
}
export function useContextUsage(messages: Message[], modelName: string): ContextUsageData {
return useMemo(() => {
const contextWindow = getContextWindow(modelName);
const noData: ContextUsageData = {
modelName,
contextWindow,
used: 0,
ratio: 0,
cacheReadTokens: 0,
cacheCreationTokens: 0,
outputTokens: 0,
hasData: false,
};
// Find the last assistant message with token_usage
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
if (msg.role !== 'assistant' || !msg.token_usage) continue;
try {
const usage = typeof msg.token_usage === 'string'
? JSON.parse(msg.token_usage)
: msg.token_usage;
const inputTokens = usage.input_tokens || 0;
const cacheRead = usage.cache_read_input_tokens || 0;
const cacheCreation = usage.cache_creation_input_tokens || 0;
const outputTokens = usage.output_tokens || 0;
const used = inputTokens + cacheRead + cacheCreation;
return {
modelName,
contextWindow,
used,
ratio: contextWindow ? used / contextWindow : 0,
cacheReadTokens: cacheRead,
cacheCreationTokens: cacheCreation,
outputTokens,
hasData: true,
};
} catch {
continue;
}
}
return noData;
}, [messages, modelName]);
}