← back to Kickbacks Ai Upstream
src/activation/capWarning.ts
70 lines
import * as vscode from "vscode";
import type { EarningCap } from "../earnings/client";
// Same red as the StatusBar "not earning" states (statusbar.ts RED). Kept in
// sync by value, not import, so this item stays fully independent of the main
// status-bar item (the whole point of the second-pill design).
const RED = "#f85149";
/** Format seconds-until-reset into a compact label: "<1m", "Nm", "NhMm"
* (the trailing minutes are dropped when zero). No existing helper for this
* exists in the extension, so it lives here, private to the cap pill. */
export function formatReset(seconds: number): string {
const s = Math.max(0, Math.floor(seconds));
if (s < 60) return "<1m";
const mins = Math.floor(s / 60);
if (mins < 60) return `${mins}m`;
const h = Math.floor(mins / 60);
const m = mins % 60;
return m === 0 ? `${h}h` : `${h}h${m}m`;
}
/** A SECOND, dedicated status-bar item that appears ONLY while the user has hit
* an earning cap — a red pill beside the green earnings item naming the cap
* and when it resets. Deliberately independent of StatusBar: it never touches
* the earnings item's reload-lock or ad-takeover logic, and the earnings
* figure stays visible alongside it.
*
* Both scopes render red; the icon distinguishes them ($(clock) hourly vs
* $(warning) daily). Click opens the Kickbacks menu (where the cap + payout
* are also explained). */
export class CapWarning {
// Priority 999 sits just to the RIGHT of the earnings item (priority 1000)
// in the right-aligned cluster.
private item = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Right, 999);
constructor() {
this.item.command = "kickbacks.debugMenu";
this.item.color = RED;
}
/** Paint the cap pill for the given cap state. Idempotent — safe to call on
* every earnings poll; the reset countdown refreshes at poll resolution. */
show(cap: EarningCap): void {
const reset = formatReset(cap.resetSeconds);
// capUsd is digits-only from the backend (e.g. "10.00"); prepend "$" here,
// matching how StatusBar renders the earnings figure.
const usd = `$${cap.capUsd}`;
if (cap.scope === "hourly") {
this.item.text = `$(clock) Hourly cap · ${reset}`;
this.item.tooltip =
`Hourly earning cap reached (${usd}/hr). `
+ `Earning resumes at the top of the hour (~${reset}). `
+ `You can earn more after that — this isn't an error.`;
} else {
this.item.text = `$(warning) Daily cap · ${reset}`;
this.item.tooltip =
`Daily earning cap reached (${usd}). `
+ `Earning resumes at 00:00 UTC (~${reset}). `
+ `You've earned the max for today — this isn't an error.`;
}
this.item.color = RED;
this.item.show();
}
/** Hide the pill (under both ceilings, or any not-earning safety state). */
hide(): void { this.item.hide(); }
dispose(): void { this.item.dispose(); }
}