picoclaw/web/frontend/src/hooks/use-pico-chat.ts
Guoguo 6ca7311273
feat(agent): add context usage ring indicator and /context command (#2537)
Add a context window usage indicator to the web chat UI and a /context
slash command that works across all channels.

Backend:
- Add computeContextUsage() estimating history + system + tool tokens
- Attach ContextUsage to outbound messages via the pico WebSocket protocol
- Add /context command showing context stats as formatted text
- Add EstimateSystemTokens() on ContextBuilder for system prompt estimation

Frontend:
- Add ContextUsageRing component (SVG ring + hover/tap popover)
- Show usage percentage, token counts, and compression threshold
- Hover on desktop (150ms leave delay), tap on mobile
- "View Details" sends /context with 1s cooldown
- i18n support (en/zh) for popover labels

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 16:30:02 +08:00

71 lines
1.6 KiB
TypeScript

import dayjs from "dayjs"
import { useAtomValue } from "jotai"
import {
newChatSession,
sendChatMessage,
switchChatSession,
} from "@/features/chat/controller"
import { chatAtom } from "@/store/chat"
const UNIX_MS_THRESHOLD = 1e12
function normalizeUnixTimestamp(timestamp: number): number {
return timestamp < UNIX_MS_THRESHOLD ? timestamp * 1000 : timestamp
}
function parseTimestamp(dateRaw: number | string | Date) {
if (typeof dateRaw === "number") {
return dayjs(normalizeUnixTimestamp(dateRaw))
}
if (typeof dateRaw === "string") {
const trimmed = dateRaw.trim()
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
const numeric = Number(trimmed)
if (Number.isFinite(numeric)) {
return dayjs(normalizeUnixTimestamp(numeric))
}
}
return dayjs(trimmed)
}
return dayjs(dateRaw)
}
export function formatMessageTime(dateRaw: number | string | Date): string {
const date = parseTimestamp(dateRaw)
if (!date.isValid()) {
return ""
}
const now = dayjs()
const isToday = date.isSame(now, "day")
const isThisYear = date.isSame(now, "year")
if (isToday) {
return date.format("LT")
}
if (isThisYear) {
return date.format("MMM D LT")
}
return date.format("ll LT")
}
export function usePicoChat() {
const { messages, connectionState, isTyping, activeSessionId, contextUsage } =
useAtomValue(chatAtom)
return {
messages,
connectionState,
isTyping,
activeSessionId,
contextUsage,
sendMessage: sendChatMessage,
switchSession: switchChatSession,
newChat: newChatSession,
}
}