返回 DeepSeek-Reasonix
format.ts
根目录 / desktop / frontend / src / lib / format.ts
1 /**
2 * Shared formatting utilities for token counts and numeric displays.
3 *
4 * Centralises the previously duplicated fmtTokens / fmtFullTokens helpers
5 * scattered across ContextPanel and Composer so every surface renders
6 * identical compact token strings (e.g. "1.5K", "2.0M").
7 */
8
9 /** Options controlling how {@link formatTokens} renders a count. */
10 export interface TokenFormatOptions {
11 /**
12 * When `true` (the default) large values are abbreviated with K / M suffixes.
13 * When `false` the raw locale-formatted number is returned instead.
14 */
15 compact?: boolean;
16
17 /** Maximum fractional digits kept in the abbreviated form (default `1`). */
18 decimals?: number;
19
20 /**
21 * Minimum value before the K-suffix kicks in (default `1000`).
22 * Useful for contexts that want to show plain numbers up to a higher bound.
23 */
24 threshold?: number;
25 }
26
27 /** Strip trailing zeros after the decimal point: "1.50" → "1.5", "1.00" → "1". */
28 function stripTrailingZeros(s: string): string {
29 return s.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
30 }
31
32 /**
33 * Format a token count for human-readable display.
34 *
35 * Behaviour:
36 * - Returns `"-"` for missing, zero, or negative inputs.
37 * - In compact mode (default): values >= 1 000 000 use the `M` suffix,
38 * values >= `threshold` (default 1 000) use the `K` suffix, and smaller
39 * values are rendered as-is.
40 * - Trailing `.0` is stripped so `1000` becomes `"1K"` rather than `"1.0K"`.
41 * - In non-compact mode `toLocaleString()` is used for locale-aware grouping.
42 *
43 * @example
44 * formatTokens(1500) // "1.5K"
45 * formatTokens(1000) // "1K"
46 * formatTokens(142000) // "142K"
47 * formatTokens(1_500_000) // "1.5M"
48 * formatTokens(999) // "999"
49 * formatTokens(undefined) // "-"
50 */
51 export function formatTokens(tokens: number | undefined, options?: TokenFormatOptions): string {
52 if (typeof tokens !== "number" || tokens <= 0) return "-";
53
54 const { compact = true, decimals = 1, threshold = 1000 } = options ?? {};
55
56 if (!compact) {
57 return tokens.toLocaleString();
58 }
59
60 if (tokens >= 1_000_000) {
61 return `${stripTrailingZeros((tokens / 1_000_000).toFixed(decimals))}M`;
62 }
63 if (tokens >= threshold) {
64 return `${stripTrailingZeros((tokens / 1000).toFixed(decimals))}K`;
65 }
66
67 return String(tokens);
68 }
69
70 /**
71 * Convenience wrapper for optional token counts (session / turn totals).
72 *
73 * Delegates to {@link formatTokens} and returns `"-"` when the value is
74 * absent, zero, or negative — matching the old `fmtOptionalTokens` helper
75 * it replaces.
76 */
77 export function formatOptionalTokens(tokens?: number | null, options?: TokenFormatOptions): string {
78 if (typeof tokens !== "number" || tokens <= 0) return "-";
79 return formatTokens(tokens, options);
80 }
81
81 lines TYPESCRIPT