jarrodwatts--claude-hud
238 行
9.9 KiB
JavaScript
238 行
9.9 KiB
JavaScript
import { isLimitReached } from "../../types.js";
|
|
import { shouldHideUsage } from "../../stdin.js";
|
|
import { critical, label, getQuotaColor, quotaBar, RESET } from "../colors.js";
|
|
import { getAdaptiveBarWidth } from "../../utils/terminal.js";
|
|
import { t } from "../../i18n/index.js";
|
|
import { progressLabel, } from "./label-align.js";
|
|
import { formatResetTime } from "../format-reset-time.js";
|
|
const FIVE_HOUR_WINDOW_MS = 5 * 60 * 60 * 1000;
|
|
const SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
export function renderUsageLine(ctx, labelOptions = {}) {
|
|
const display = ctx.config?.display;
|
|
const colors = ctx.config?.colors;
|
|
if (display?.showUsage === false) {
|
|
return null;
|
|
}
|
|
if (!ctx.usageData) {
|
|
return null;
|
|
}
|
|
if (shouldHideUsage(ctx.stdin)) {
|
|
return null;
|
|
}
|
|
const usageLabel = progressLabel("label.usage", colors, labelOptions);
|
|
const balanceLabel = ctx.usageData.balanceLabel ?? null;
|
|
const scopedWindows = ctx.usageData.scopedWindows ?? [];
|
|
const hasWindowData = ctx.usageData.fiveHour !== null
|
|
|| ctx.usageData.sevenDay !== null
|
|
|| scopedWindows.length > 0;
|
|
if (balanceLabel && !hasWindowData) {
|
|
return `${usageLabel} ${balanceLabel}`;
|
|
}
|
|
const timeFormat = normalizeTimeFormat(display?.timeFormat);
|
|
const showResetLabel = display?.showResetLabel ?? true;
|
|
const resetsKey = limitResetTimeFormat(timeFormat) === 'absolute' ? "format.resets" : "format.resetsIn";
|
|
const usageCompact = display?.usageCompact ?? false;
|
|
const usageValueMode = display?.usageValue ?? 'percent';
|
|
const barWidthForScoped = getAdaptiveBarWidth();
|
|
const scopedSuffix = scopedWindows.length
|
|
? ' | ' + scopedWindows
|
|
.map((w) => usageCompact
|
|
? formatCompactWindowPart(w.label, w.percent, w.resetAt, SEVEN_DAY_WINDOW_MS, timeFormat, colors, usageValueMode)
|
|
: formatUsageWindowPart({
|
|
label: w.label,
|
|
percent: w.percent,
|
|
resetAt: w.resetAt,
|
|
windowMs: SEVEN_DAY_WINDOW_MS,
|
|
colors,
|
|
usageBarEnabled: display?.usageBarEnabled ?? true,
|
|
barWidth: barWidthForScoped,
|
|
timeFormat,
|
|
showResetLabel,
|
|
forceLabel: true,
|
|
labelOptions,
|
|
usageValueMode,
|
|
}))
|
|
.join(' | ')
|
|
: '';
|
|
if (isLimitReached(ctx.usageData)) {
|
|
const limitTimeFormat = limitResetTimeFormat(timeFormat);
|
|
const resetTime = ctx.usageData.fiveHour === 100
|
|
? formatResetTime(ctx.usageData.fiveHourResetAt, limitTimeFormat)
|
|
: formatResetTime(ctx.usageData.sevenDayResetAt, limitTimeFormat);
|
|
if (usageCompact) {
|
|
return appendBalance(`${critical(`⚠ Limit${resetTime ? ` (${resetTime})` : ""}`, colors)}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
const resetSuffix = resetTime
|
|
? showResetLabel
|
|
? ` (${t(resetsKey)} ${resetTime})`
|
|
: ` (${resetTime})`
|
|
: "";
|
|
return appendBalance(`${usageLabel} ${critical(`⚠ ${t("status.limitReached")}${resetSuffix}`, colors)}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
const threshold = display?.usageThreshold ?? 0;
|
|
const fiveHour = ctx.usageData.fiveHour;
|
|
const sevenDay = ctx.usageData.sevenDay;
|
|
const effectiveUsage = Math.max(fiveHour ?? 0, sevenDay ?? 0, ...scopedWindows.map((window) => window.percent ?? 0));
|
|
if (effectiveUsage < threshold) {
|
|
return balanceLabel ? `${usageLabel} ${balanceLabel}` : null;
|
|
}
|
|
const sevenDayThreshold = display?.sevenDayThreshold ?? 80;
|
|
if (usageCompact) {
|
|
const fiveHourPart = fiveHour !== null
|
|
? formatCompactWindowPart("5h", fiveHour, ctx.usageData.fiveHourResetAt, FIVE_HOUR_WINDOW_MS, timeFormat, colors, usageValueMode)
|
|
: null;
|
|
const sevenDayPart = (sevenDay !== null && (fiveHour === null || sevenDay >= sevenDayThreshold))
|
|
? formatCompactWindowPart("7d", sevenDay, ctx.usageData.sevenDayResetAt, SEVEN_DAY_WINDOW_MS, timeFormat, colors, usageValueMode)
|
|
: null;
|
|
if (fiveHourPart && sevenDayPart) {
|
|
return appendBalance(`${fiveHourPart} | ${sevenDayPart}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
const compactLine = fiveHourPart ?? sevenDayPart;
|
|
if (compactLine) {
|
|
return appendBalance(`${compactLine}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
return scopedSuffix ? appendBalance(scopedSuffix.slice(3), balanceLabel) : null;
|
|
}
|
|
const usageBarEnabled = display?.usageBarEnabled ?? true;
|
|
const barWidth = getAdaptiveBarWidth();
|
|
if (fiveHour === null && sevenDay === null) {
|
|
return scopedSuffix
|
|
? appendBalance(`${usageLabel} ${scopedSuffix.slice(3)}`, balanceLabel)
|
|
: balanceLabel
|
|
? `${usageLabel} ${balanceLabel}`
|
|
: null;
|
|
}
|
|
if (fiveHour === null && sevenDay !== null) {
|
|
const weeklyOnlyPart = formatUsageWindowPart({
|
|
label: t("label.weekly"),
|
|
labelKey: "label.weekly",
|
|
percent: sevenDay,
|
|
resetAt: ctx.usageData.sevenDayResetAt,
|
|
windowMs: SEVEN_DAY_WINDOW_MS,
|
|
colors,
|
|
usageBarEnabled,
|
|
barWidth,
|
|
timeFormat,
|
|
showResetLabel,
|
|
forceLabel: true,
|
|
labelOptions,
|
|
usageValueMode,
|
|
});
|
|
return appendBalance(`${usageLabel} ${weeklyOnlyPart}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
const fiveHourPart = formatUsageWindowPart({
|
|
label: "5h",
|
|
percent: fiveHour,
|
|
resetAt: ctx.usageData.fiveHourResetAt,
|
|
windowMs: FIVE_HOUR_WINDOW_MS,
|
|
colors,
|
|
usageBarEnabled,
|
|
barWidth,
|
|
timeFormat,
|
|
showResetLabel,
|
|
usageValueMode,
|
|
});
|
|
if (sevenDay !== null && sevenDay >= sevenDayThreshold) {
|
|
const sevenDayPart = formatUsageWindowPart({
|
|
label: t("label.weekly"),
|
|
labelKey: "label.weekly",
|
|
percent: sevenDay,
|
|
resetAt: ctx.usageData.sevenDayResetAt,
|
|
windowMs: SEVEN_DAY_WINDOW_MS,
|
|
colors,
|
|
usageBarEnabled,
|
|
barWidth,
|
|
timeFormat,
|
|
showResetLabel,
|
|
forceLabel: true,
|
|
labelOptions,
|
|
usageValueMode,
|
|
});
|
|
return appendBalance(`${usageLabel} ${fiveHourPart} | ${sevenDayPart}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
return appendBalance(`${usageLabel} ${fiveHourPart}${scopedSuffix}`, balanceLabel);
|
|
}
|
|
function appendBalance(line, balanceLabel) {
|
|
return balanceLabel ? `${line} | ${balanceLabel}` : line;
|
|
}
|
|
function formatCompactWindowPart(windowLabel, percent, resetAt, windowMs, timeFormat, colors, usageValueMode = 'percent') {
|
|
const usageDisplay = formatUsagePercent(percent, colors, usageValueMode);
|
|
const reset = formatWindowTime(resetAt, windowMs, timeFormat);
|
|
const styledLabel = label(`${windowLabel}:`, colors);
|
|
return reset
|
|
? `${styledLabel} ${usageDisplay} ${label(`(${reset})`, colors)}`
|
|
: `${styledLabel} ${usageDisplay}`;
|
|
}
|
|
function formatUsagePercent(percent, colors, mode = 'percent') {
|
|
if (percent === null) {
|
|
return label("--", colors);
|
|
}
|
|
const color = getQuotaColor(percent, colors);
|
|
const displayPercent = mode === 'remaining' ? Math.max(0, 100 - percent) : percent;
|
|
return `${color}${displayPercent}%${RESET}`;
|
|
}
|
|
function formatUsageWindowPart({ label: windowLabel, labelKey, percent, resetAt, windowMs, colors, usageBarEnabled, barWidth, timeFormat = 'relative', showResetLabel, forceLabel = false, labelOptions = {}, usageValueMode = 'percent', }) {
|
|
const usageDisplay = formatUsagePercent(percent, colors, usageValueMode);
|
|
const reset = formatWindowTime(resetAt, windowMs, timeFormat);
|
|
const styledLabel = labelKey
|
|
? progressLabel(labelKey, colors, labelOptions)
|
|
: label(windowLabel, colors);
|
|
const showResetWording = timeFormat !== 'elapsed' && timeFormat !== 'elapsedAndAbsolute';
|
|
const resetsKey = timeFormat === 'absolute' ? "format.resets" : "format.resetsIn";
|
|
const resetSuffix = reset
|
|
? showResetLabel && showResetWording
|
|
? `(${t(resetsKey)} ${reset})`
|
|
: `(${reset})`
|
|
: "";
|
|
if (usageBarEnabled) {
|
|
const body = resetSuffix
|
|
? `${quotaBar(percent ?? 0, barWidth, colors)} ${usageDisplay} ${resetSuffix}`
|
|
: `${quotaBar(percent ?? 0, barWidth, colors)} ${usageDisplay}`;
|
|
return forceLabel ? `${styledLabel} ${body}` : body;
|
|
}
|
|
return resetSuffix
|
|
? `${styledLabel} ${usageDisplay} ${resetSuffix}`
|
|
: `${styledLabel} ${usageDisplay}`;
|
|
}
|
|
function normalizeTimeFormat(value) {
|
|
if (value === 'absolute'
|
|
|| value === 'both'
|
|
|| value === 'elapsed'
|
|
|| value === 'elapsedAndAbsolute') {
|
|
return value;
|
|
}
|
|
return 'relative';
|
|
}
|
|
function limitResetTimeFormat(timeFormat) {
|
|
if (timeFormat === 'elapsedAndAbsolute') {
|
|
return 'absolute';
|
|
}
|
|
if (timeFormat === 'elapsed') {
|
|
return 'relative';
|
|
}
|
|
return timeFormat;
|
|
}
|
|
function formatWindowTime(resetAt, windowMs, timeFormat) {
|
|
if (timeFormat === 'elapsed') {
|
|
return formatElapsedWindow(resetAt, windowMs);
|
|
}
|
|
if (timeFormat === 'elapsedAndAbsolute') {
|
|
const elapsed = formatElapsedWindow(resetAt, windowMs);
|
|
const absolute = formatResetTime(resetAt, 'absolute');
|
|
if (elapsed && absolute) {
|
|
return `${elapsed}, ${absolute}`;
|
|
}
|
|
return elapsed || absolute;
|
|
}
|
|
return formatResetTime(resetAt, timeFormat);
|
|
}
|
|
function formatElapsedWindow(resetAt, windowMs) {
|
|
if (!resetAt) {
|
|
return '';
|
|
}
|
|
const windowStart = resetAt.getTime() - windowMs;
|
|
const rawElapsed = ((Date.now() - windowStart) / windowMs) * 100;
|
|
const elapsed = Math.max(0, Math.min(100, Math.round(rawElapsed)));
|
|
return `${elapsed}% elapsed`;
|
|
}
|
|
//# sourceMappingURL=usage.js.map
|