Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,4 @@ docs-site/node_modules/
tmp/
.trae/
.sisyphus
.ace-tool/
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,31 @@

---

## v0.5.3 (2026-02-03)

### 新增

- 扩展只读密钥访问权限,支持更多 API 端点访问 (#704) [@AptS:1547](https://github.com/AptS1547)
- 支持 Zeabur 一键部署 (#679) [@h7ml](https://github.com/h7ml)
- Anthropic 供应商支持参数覆写功能,可自定义 API 请求参数 (#689)

### 优化

- 重构代理架构,移除格式转换器并强制同格式路由,提升性能和稳定性 (#709)
- 优化 Thinking Budget 整流器,改进思考模式下的令牌预算管理

### 修复

- 修复 Gemini 供应商 buildProxyUrl 重复拼接版本前缀的问题 (#693) [@sunxyw](https://github.com/sunxyw)
- 修复 Gemini SSE 响应中 usageMetadata 提取逻辑,采用 last-wins 策略 (#691) [@sususu98](https://github.com/sususu98)

### 其他

- 新增 Thinking Budget 整流器单元测试覆盖
- 更新 i18n 翻译和系统配置

---

## [v0.5.2](https://github.com/ding113/claude-code-hub/releases/tag/v0.5.2) - 2026-01-29

### 新增
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const COLUMN_LABEL_KEYS: Record<LogsTableColumn, string> = {
sessionId: "logs.columns.sessionId",
provider: "logs.columns.provider",
tokens: "logs.columns.tokens",
cost: "logs.columns.cost",
cache: "logs.columns.cache",
performance: "logs.columns.performance",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ export function UsageLogsTable({
</TooltipProvider>
) : isNonBilling ? (
"-"
) : log.costUsd ? (
) : log.costUsd != null ? (
<TooltipProvider>
<Tooltip delayDuration={250}>
<TooltipTrigger asChild>
Expand Down
106 changes: 53 additions & 53 deletions src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
import { RelativeTime } from "@/components/ui/relative-time";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useVirtualizer } from "@/hooks/use-virtualizer";
import type { LogsTableColumn } from "@/lib/column-visibility";
import { cn, formatTokenAmount } from "@/lib/utils";
import { copyTextToClipboard } from "@/lib/utils/clipboard";
import type { CurrencyCode } from "@/lib/utils/currency";
Expand Down Expand Up @@ -44,15 +45,6 @@ export interface VirtualizedLogsTableFilters {
minRetryCount?: number;
}

type VirtualizedLogsTableColumn =
| "user"
| "key"
| "sessionId"
| "provider"
| "tokens"
| "cache"
| "performance";

interface VirtualizedLogsTableProps {
filters: VirtualizedLogsTableFilters;
currencyCode?: CurrencyCode;
Expand All @@ -61,7 +53,7 @@ interface VirtualizedLogsTableProps {
autoRefreshIntervalMs?: number;
hideStatusBar?: boolean;
hideScrollToTop?: boolean;
hiddenColumns?: VirtualizedLogsTableColumn[];
hiddenColumns?: LogsTableColumn[];
bodyClassName?: string;
}

Expand All @@ -87,6 +79,7 @@ export function VirtualizedLogsTable({
const hideSessionIdColumn = hiddenColumns?.includes("sessionId") ?? false;
const hideTokensColumn = hiddenColumns?.includes("tokens") ?? false;
const hideCacheColumn = hiddenColumns?.includes("cache") ?? false;
const hideCostColumn = hiddenColumns?.includes("cost") ?? false;
const hidePerformanceColumn = hiddenColumns?.includes("performance") ?? false;

// Dialog state for model redirect click and chain item click
Expand Down Expand Up @@ -273,12 +266,14 @@ export function VirtualizedLogsTable({
{t("logs.columns.cache")}
</div>
)}
<div
className="flex-[0.7] min-w-[60px] text-right px-1.5 truncate"
title={t("logs.columns.cost")}
>
{t("logs.columns.cost")}
</div>
{hideCostColumn ? null : (
<div
className="flex-[0.7] min-w-[60px] text-right px-1.5 truncate"
title={t("logs.columns.cost")}
>
{t("logs.columns.cost")}
</div>
)}
{hidePerformanceColumn ? null : (
<div
className="flex-[0.8] min-w-[80px] text-right px-1.5 truncate"
Expand Down Expand Up @@ -612,46 +607,51 @@ export function VirtualizedLogsTable({
)}

{/* Cost */}
<div className="flex-[0.7] min-w-[60px] text-right font-mono text-xs px-1.5">
{isNonBilling ? (
"-"
) : log.costUsd ? (
<TooltipProvider>
<Tooltip delayDuration={250}>
<TooltipTrigger asChild>
<span className="cursor-help inline-flex items-center gap-1">
{formatCurrency(log.costUsd, currencyCode, 6)}
{hideCostColumn ? null : (
<div className="flex-[0.7] min-w-[60px] text-right font-mono text-xs px-1.5">
{isNonBilling ? (
"-"
) : log.costUsd != null ? (
<TooltipProvider>
<Tooltip delayDuration={250}>
<TooltipTrigger asChild>
<span className="cursor-help inline-flex items-center gap-1">
{formatCurrency(log.costUsd, currencyCode, 6)}
{log.context1mApplied && (
<Badge
variant="outline"
className="text-[10px] leading-tight px-1 bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-800"
>
1M
</Badge>
)}
</span>
</TooltipTrigger>
<TooltipContent
align="end"
className="text-xs space-y-1 max-w-[300px]"
>
{log.context1mApplied && (
<Badge
variant="outline"
className="text-[10px] leading-tight px-1 bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-800"
>
1M
</Badge>
<div className="text-purple-600 dark:text-purple-400 font-medium">
{t("logs.billingDetails.context1m")}
</div>
)}
</span>
</TooltipTrigger>
<TooltipContent align="end" className="text-xs space-y-1 max-w-[300px]">
{log.context1mApplied && (
<div className="text-purple-600 dark:text-purple-400 font-medium">
{t("logs.billingDetails.context1m")}
<div>
{t("logs.billingDetails.input")}:{" "}
{formatTokenAmount(log.inputTokens)} tokens
</div>
)}
<div>
{t("logs.billingDetails.input")}:{" "}
{formatTokenAmount(log.inputTokens)} tokens
</div>
<div>
{t("logs.billingDetails.output")}:{" "}
{formatTokenAmount(log.outputTokens)} tokens
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
"-"
)}
</div>
<div>
{t("logs.billingDetails.output")}:{" "}
{formatTokenAmount(log.outputTokens)} tokens
</div>
Comment on lines +621 to +646
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

将成本提示中的 "1M" 与 "tokens" 文案接入 i18n。

当前为硬编码字符串,需改为翻译键并补齐各语言文案。

建议修改
-                                      1M
+                                      {t("logs.billingDetails.context1mShort")}
...
-                                  {formatTokenAmount(log.inputTokens)} tokens
+                                  {formatTokenAmount(log.inputTokens)} {t("logs.billingDetails.tokensUnit")}
...
-                                  {formatTokenAmount(log.outputTokens)} tokens
+                                  {formatTokenAmount(log.outputTokens)} {t("logs.billingDetails.tokensUnit")}
As per coding guidelines: All user-facing strings must use i18n (5 languages supported: zh-CN, zh-TW, en, ja, ru). Never hardcode display text
🤖 Prompt for AI Agents
In `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
around lines 621 - 646, Replace the hardcoded "1M" badge label and the literal
"tokens" suffixes with i18n keys: update the Badge content to use
t("logs.billingDetails.context1m") or a new key like
t("logs.billingDetails.context1mLabel") and replace the "tokens" text in the
TooltipContent lines with t("logs.billingDetails.tokens") so the strings for the
badge and the token unit are localized for zh-CN/zh-TW/en/ja/ru; ensure you call
the same i18n function used in this file (t) and keep the numeric values
produced by formatTokenAmount(log.inputTokens) and
formatTokenAmount(log.outputTokens) unchanged.

</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
"-"
)}
</div>
)}

{/* Performance */}
{hidePerformanceColumn ? null : (
Expand Down
11 changes: 11 additions & 0 deletions src/lib/column-visibility.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ describe("column-visibility", () => {
const result3 = toggleColumn(userId, tableId, "user");
expect(result3).toEqual(["provider"]);
});

test("toggles cost column visibility", () => {
const hiddenAfterToggle = toggleColumn(userId, tableId, "cost");
expect(hiddenAfterToggle).toContain("cost");
expect(getVisibleColumns(userId, tableId)).not.toContain("cost");

const visibleAfterToggleBack = toggleColumn(userId, tableId, "cost");
expect(visibleAfterToggleBack).not.toContain("cost");
expect(getVisibleColumns(userId, tableId)).toContain("cost");
});
});

describe("resetColumns", () => {
Expand Down Expand Up @@ -209,6 +219,7 @@ describe("column-visibility", () => {
expect(DEFAULT_VISIBLE_COLUMNS).toContain("sessionId");
expect(DEFAULT_VISIBLE_COLUMNS).toContain("provider");
expect(DEFAULT_VISIBLE_COLUMNS).toContain("tokens");
expect(DEFAULT_VISIBLE_COLUMNS).toContain("cost");
expect(DEFAULT_VISIBLE_COLUMNS).toContain("cache");
expect(DEFAULT_VISIBLE_COLUMNS).toContain("performance");
});
Expand Down
6 changes: 4 additions & 2 deletions src/lib/column-visibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export type LogsTableColumn =
| "provider"
| "tokens"
| "cache"
| "performance";
| "performance"
| "cost";

/**
* Default visible columns (all visible by default)
Expand All @@ -30,12 +31,13 @@ export const DEFAULT_VISIBLE_COLUMNS: LogsTableColumn[] = [
"tokens",
"cache",
"performance",
"cost",
];

/**
* Columns that cannot be hidden (always visible)
*/
export const ALWAYS_VISIBLE_COLUMNS = ["time", "model", "cost", "status"] as const;
export const ALWAYS_VISIBLE_COLUMNS = ["time", "model", "status"] as const;

/**
* Get the storage key for a specific user and table
Expand Down
Loading