quotacap 0.0.41 → 0.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude.js +1 -1
- package/dist/adapters/index.d.ts +2 -2
- package/dist/adapters/opencode-go.d.ts +1 -1
- package/dist/adapters/opencode-go.js +8 -1
- package/dist/cli/providers.js +2 -1
- package/dist/config.d.ts +5 -23
- package/dist/format/rows.d.ts +1 -1
- package/dist/format/rows.js +9 -6
- package/dist/mcp/server.d.ts +2 -2
- package/dist/version.js +1 -1
- package/dist/web/src/App.js +1 -1
- package/dist/web/src/components/AdviceDrawer.js +1 -1
- package/dist/web/src/components/OpencodeGoBanner.d.ts +1 -1
- package/dist/web/src/components/OpencodeGoBanner.js +9 -2
- package/dist/web/src/components/PaceBar.d.ts +9 -4
- package/dist/web/src/components/PaceBar.js +26 -7
- package/dist/web/src/components/ProviderCard.js +2 -3
- package/dist/web/src/components/ProviderDrawer.js +3 -3
- package/dist/web/src/components/ProviderIcon.js +1 -1
- package/dist/web/src/components/ProviderRow.d.ts +1 -1
- package/dist/web/src/components/ProviderRow.js +2 -4
- package/dist/web/src/components/Recommendation.js +1 -1
- package/dist/web/src/components/ResetRail.js +1 -1
- package/dist/web/src/components/WindowsRow.d.ts +1 -1
- package/dist/web/src/components/WindowsRow.js +4 -4
- package/dist/web/src/components/settings/SettingsDrawer.js +1 -1
- package/dist/webAssets.js +1 -1
- package/dist/webHtml.js +1 -1
- package/package.json +5 -5
- package/web/dist/assets/{index-Dk6l5h27.css → index-C6ltEeGL.css} +1 -1
- package/web/dist/assets/index-DRDDTtxF.js +13 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-BUK8xUHm.js +0 -12
package/dist/adapters/claude.js
CHANGED
|
@@ -13,7 +13,7 @@ export function parseClaudeUsage(result, now = new Date()) {
|
|
|
13
13
|
const resets = new Date(resetsAt).getTime();
|
|
14
14
|
const periodStart = (parsedReset ? new Date(resets - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
|
|
15
15
|
return {
|
|
16
|
-
provider: "claude", plan: "
|
|
16
|
+
provider: "claude", plan: "unknown", weeklyPct: usedPct, fiveHourPct: sessionPct,
|
|
17
17
|
resetsAt, periodStart, source: "cli", fetchedAt: now.toISOString(), raw: result,
|
|
18
18
|
};
|
|
19
19
|
}
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -9,13 +9,13 @@ export declare function pollAll(enabled: string[], opts?: PollAllOptions): Promi
|
|
|
9
9
|
value: unknown;
|
|
10
10
|
reason?: undefined;
|
|
11
11
|
} | {
|
|
12
|
+
value?: undefined;
|
|
12
13
|
provider: string;
|
|
13
14
|
status: "skipped";
|
|
14
15
|
reason: any;
|
|
15
|
-
value?: undefined;
|
|
16
16
|
} | {
|
|
17
|
+
value?: undefined;
|
|
17
18
|
provider: string;
|
|
18
19
|
status: "rejected";
|
|
19
20
|
reason: any;
|
|
20
|
-
value?: undefined;
|
|
21
21
|
})[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ParsedQuota } from "./types.js";
|
|
2
|
-
export declare const OPENCODE_GO_CONSENT_NOTICE
|
|
2
|
+
export declare const OPENCODE_GO_CONSENT_NOTICE: string;
|
|
3
3
|
export declare function openCodeGoAuthPath(): string;
|
|
4
4
|
/** Detection is existence only — no content read before enablement. */
|
|
5
5
|
export declare function opencodeGoDetected(): boolean;
|
|
@@ -8,7 +8,14 @@ import path from "node:path";
|
|
|
8
8
|
import { adapterSignal } from "../runtime/spawn.js";
|
|
9
9
|
const USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
10
10
|
const POLL_TIMEOUT_MS = 8000;
|
|
11
|
-
export const OPENCODE_GO_CONSENT_NOTICE =
|
|
11
|
+
export const OPENCODE_GO_CONSENT_NOTICE = [
|
|
12
|
+
"• What is read: API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` → `opencode` fallback).",
|
|
13
|
+
"• Read frequency: in-memory, read-only once per poll (every 15 minutes by default).",
|
|
14
|
+
"• Request destination: sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`.",
|
|
15
|
+
"• Never-list: never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`).",
|
|
16
|
+
"• Revocation instructions: revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling.",
|
|
17
|
+
"• Optional environment override: if you prefer no file access, set `OPENCODE_API_KEY` instead.",
|
|
18
|
+
].join("\n");
|
|
12
19
|
export function openCodeGoAuthPath() {
|
|
13
20
|
return path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
|
|
14
21
|
}
|
package/dist/cli/providers.js
CHANGED
|
@@ -248,7 +248,8 @@ export function registerProvidersCommand(program, deps = {}) {
|
|
|
248
248
|
return;
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
console.log(`enabled ${id}
|
|
251
|
+
console.log(`enabled ${id}`);
|
|
252
|
+
console.log("Next: run quotacap service restart to start polling");
|
|
252
253
|
});
|
|
253
254
|
providers
|
|
254
255
|
.command("disable <id>")
|
package/dist/config.d.ts
CHANGED
|
@@ -1,35 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare function getConfigPath(p?: string): string;
|
|
3
3
|
export declare function getDbPath(p?: string): string;
|
|
4
|
-
export declare const ProviderDisplayNameSchema: z.
|
|
4
|
+
export declare const ProviderDisplayNameSchema: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
5
5
|
declare const ConfigSchema: z.ZodObject<{
|
|
6
6
|
port: z.ZodDefault<z.ZodNumber>;
|
|
7
7
|
pollMinutes: z.ZodDefault<z.ZodNumber>;
|
|
8
8
|
catalogTtlHours: z.ZodDefault<z.ZodNumber>;
|
|
9
|
-
enabledProviders: z.ZodDefault<z.ZodArray<z.ZodString
|
|
10
|
-
knownProviders: z.ZodDefault<z.ZodArray<z.ZodString
|
|
11
|
-
providerNames: z.ZodDefault<z.ZodRecord<z.ZodString, z.
|
|
9
|
+
enabledProviders: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
10
|
+
knownProviders: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
11
|
+
providerNames: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
12
12
|
opencodeGoConsentAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
13
13
|
experimentalIngest: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
-
},
|
|
15
|
-
port: number;
|
|
16
|
-
pollMinutes: number;
|
|
17
|
-
catalogTtlHours: number;
|
|
18
|
-
enabledProviders: string[];
|
|
19
|
-
knownProviders: string[];
|
|
20
|
-
providerNames: Record<string, string>;
|
|
21
|
-
opencodeGoConsentAt: string | null;
|
|
22
|
-
experimentalIngest?: boolean | undefined;
|
|
23
|
-
}, {
|
|
24
|
-
port?: number | undefined;
|
|
25
|
-
pollMinutes?: number | undefined;
|
|
26
|
-
catalogTtlHours?: number | undefined;
|
|
27
|
-
enabledProviders?: string[] | undefined;
|
|
28
|
-
knownProviders?: string[] | undefined;
|
|
29
|
-
providerNames?: Record<string, string> | undefined;
|
|
30
|
-
opencodeGoConsentAt?: string | null | undefined;
|
|
31
|
-
experimentalIngest?: boolean | undefined;
|
|
32
|
-
}>;
|
|
14
|
+
}, z.core.$strip>;
|
|
33
15
|
export type Config = z.infer<typeof ConfigSchema>;
|
|
34
16
|
/**
|
|
35
17
|
* Manual ingest (CLI `ingest`, POST /api/ingest) is off unless this returns
|
package/dist/format/rows.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare function weeklyPctOf(q: {
|
|
|
21
21
|
usedPct?: number;
|
|
22
22
|
} | null | undefined): number | undefined;
|
|
23
23
|
export declare function fmtDuration(ms: number): string;
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function dayOf(iso: string | undefined): string;
|
|
25
25
|
export declare function countdownText(ps: ProviderSnapshot, now: Date): string;
|
|
26
26
|
export declare function elapsedPct(ps: ProviderSnapshot, now: Date): number | null;
|
|
27
27
|
export declare function usedPctOf(ps: ProviderSnapshot): number | null;
|
package/dist/format/rows.js
CHANGED
|
@@ -23,11 +23,14 @@ export function fmtDuration(ms) {
|
|
|
23
23
|
return `${Math.floor(ms / HOUR_MS)}h`;
|
|
24
24
|
return `${Math.max(1, Math.floor(ms / MIN_MS))}m`;
|
|
25
25
|
}
|
|
26
|
-
// Short weekday of an ISO instant in the locked English
|
|
27
|
-
// Pinned to en-US: the sentence it feeds
|
|
28
|
-
// a non-English machine must not
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
// Short weekday plus calendar date of an ISO instant in the locked English
|
|
27
|
+
// copy, e.g. "Tue Sep 15". Pinned to en-US: the sentence it feeds
|
|
28
|
+
// ("unused until Tue Sep 15") is English, so a non-English machine must not
|
|
29
|
+
// print localized parts inside it. A bare weekday scans as this coming week,
|
|
30
|
+
// which misreads a monthly reset sitting weeks out.
|
|
31
|
+
export function dayOf(iso) {
|
|
32
|
+
const when = new Date(iso ?? "");
|
|
33
|
+
return `${when.toLocaleDateString("en-US", { weekday: "short" })} ${when.toLocaleDateString("en-US", { day: "numeric", month: "short" })}`;
|
|
31
34
|
}
|
|
32
35
|
export function countdownText(ps, now) {
|
|
33
36
|
const resetsMs = msOf(ps.quota?.resetsAt);
|
|
@@ -140,7 +143,7 @@ export function forecastText(ps, now) {
|
|
|
140
143
|
return "no readings yet";
|
|
141
144
|
let text;
|
|
142
145
|
if (adv.bindingWindow === "monthly" && adv.bindingRemaining <= 0) {
|
|
143
|
-
text = `unused until ${
|
|
146
|
+
text = `unused until ${dayOf(ps.quota?.monthlyResetsAt)}`;
|
|
144
147
|
}
|
|
145
148
|
else if (adv.bindingWindow === "monthly") {
|
|
146
149
|
text = `${Math.round(adv.bindingRemaining)}% of month left`;
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -29,10 +29,10 @@ export declare const tools: ({
|
|
|
29
29
|
inputSchema: {
|
|
30
30
|
type: string;
|
|
31
31
|
properties: {
|
|
32
|
+
task?: undefined;
|
|
32
33
|
provider: {
|
|
33
34
|
type: string;
|
|
34
35
|
};
|
|
35
|
-
task?: undefined;
|
|
36
36
|
};
|
|
37
37
|
required?: undefined;
|
|
38
38
|
};
|
|
@@ -42,10 +42,10 @@ export declare const tools: ({
|
|
|
42
42
|
inputSchema: {
|
|
43
43
|
type: string;
|
|
44
44
|
properties: {
|
|
45
|
+
task?: undefined;
|
|
45
46
|
provider: {
|
|
46
47
|
type: string;
|
|
47
48
|
};
|
|
48
|
-
task?: undefined;
|
|
49
49
|
};
|
|
50
50
|
required: string[];
|
|
51
51
|
};
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// generated by scripts/build-embed.mjs — do not edit
|
|
2
|
-
export const VERSION = "0.0.
|
|
2
|
+
export const VERSION = "0.0.42";
|
package/dist/web/src/App.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
import "./theme.css";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
import { paceBadge, timeLeft } from "./PaceBar.js";
|
|
4
4
|
import { ProviderIcon, providerTint } from "./ProviderIcon.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ProviderView, RuntimeView } from "../state.js";
|
|
3
|
-
export declare const OPENCODE_GO_CONSENT_COPY
|
|
3
|
+
export declare const OPENCODE_GO_CONSENT_COPY: string;
|
|
4
4
|
export declare function showOpenCodeGoBanner(runtime: Pick<RuntimeView, "detectedProviders"> | undefined, providers: Array<Pick<ProviderView, "id" | "enabled">>, suppressed: boolean): boolean;
|
|
5
5
|
export declare function OpencodeGoBanner({ onEnable }: {
|
|
6
6
|
onEnable: () => void;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
-
export const OPENCODE_GO_CONSENT_COPY =
|
|
3
|
+
export const OPENCODE_GO_CONSENT_COPY = [
|
|
4
|
+
"• What is read: API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` → `opencode` fallback).",
|
|
5
|
+
"• Read frequency: in-memory, read-only once per poll (every 15 minutes by default).",
|
|
6
|
+
"• Request destination: sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`.",
|
|
7
|
+
"• Never-list: never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`).",
|
|
8
|
+
"• Revocation instructions: revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling.",
|
|
9
|
+
"• Optional environment override: if you prefer no file access, set `OPENCODE_API_KEY` instead.",
|
|
10
|
+
].join("\n");
|
|
4
11
|
export function showOpenCodeGoBanner(runtime, providers, suppressed) {
|
|
5
12
|
const detected = runtime?.detectedProviders?.includes("opencode-go") ?? false;
|
|
6
13
|
const row = providers.find((p) => p.id === "opencode-go");
|
|
@@ -32,5 +39,5 @@ export function ConsentModal({ onConfirm, onCancel, busy, }) {
|
|
|
32
39
|
padding: "var(--s4) var(--s5)",
|
|
33
40
|
maxWidth: 560,
|
|
34
41
|
margin: "var(--s4)",
|
|
35
|
-
}, children: [_jsx("h2", { id: "opencode-go-consent-title", style: { fontSize: "var(--t-4)", margin: "0 0 var(--s3)" }, children: "Enable OpenCode Go?" }), _jsx("p", { style: { fontSize: "var(--t-2)", color: "var(--ink-soft)", margin: "0 0 var(--s4)" }, children: OPENCODE_GO_CONSENT_COPY }), _jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--s2)" }, children: [_jsx("button", { type: "button", className: "btn btn-quiet", onClick: onCancel, disabled: busy, children: "Cancel" }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: onConfirm, disabled: busy, children: busy ? "Enabling…" : "Enable" })] })] }) }));
|
|
42
|
+
}, children: [_jsx("h2", { id: "opencode-go-consent-title", style: { fontSize: "var(--t-4)", margin: "0 0 var(--s3)" }, children: "Enable OpenCode Go?" }), _jsx("p", { style: { fontSize: "var(--t-2)", color: "var(--ink-soft)", margin: "0 0 var(--s4)", whiteSpace: "pre-line" }, children: OPENCODE_GO_CONSENT_COPY }), _jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--s2)" }, children: [_jsx("button", { type: "button", className: "btn btn-quiet", onClick: onCancel, disabled: busy, children: "Cancel" }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: onConfirm, disabled: busy, children: busy ? "Enabling…" : "Enable" })] })] }) }));
|
|
36
43
|
}
|
|
@@ -63,10 +63,15 @@ export declare function exclusionDetail(reason: ExclusionReason): string | null;
|
|
|
63
63
|
export declare function isEstimated(provider: ProviderView): boolean;
|
|
64
64
|
/** Weekday plus 24h clock, e.g. "Thu 21:00". Null when unparseable. */
|
|
65
65
|
export declare function resetClock(resetsAt: string, locale?: string): string | null;
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
export declare function
|
|
66
|
+
/** Weekday, calendar date and 24h clock, e.g. "Thu Oct 22 23:05". Monthly
|
|
67
|
+
* resets sit weeks out, where resetClock's weekday-only reading ("Thu 23:05")
|
|
68
|
+
* scans as this coming Thursday. Null when unparseable. */
|
|
69
|
+
export declare function resetDateClock(resetsAt: string, locale?: string): string | null;
|
|
70
|
+
/** Short weekday plus calendar date in the locked English copy, e.g.
|
|
71
|
+
* "Tue Sep 15". Mirrors dayOf in src/format/rows.ts, including its pinned
|
|
72
|
+
* en-US locale, so the CLI and the dashboard word the same sentence
|
|
73
|
+
* identically on any machine. */
|
|
74
|
+
export declare function resetDay(iso: string | undefined): string;
|
|
70
75
|
/** "6d 12h" style time left from server timestamps. Null when unparseable. */
|
|
71
76
|
export declare function timeLeft(resetsAt: string, asOfMs: number): string | null;
|
|
72
77
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ageDuration } from "../state.js";
|
|
3
3
|
/**
|
|
4
4
|
* Pace-state badge from the mapping table. Exclusion always wins; a blocked
|
|
@@ -112,11 +112,30 @@ export function resetClock(resetsAt, locale) {
|
|
|
112
112
|
});
|
|
113
113
|
return `${day} ${time}`;
|
|
114
114
|
}
|
|
115
|
-
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
export function
|
|
119
|
-
|
|
115
|
+
/** Weekday, calendar date and 24h clock, e.g. "Thu Oct 22 23:05". Monthly
|
|
116
|
+
* resets sit weeks out, where resetClock's weekday-only reading ("Thu 23:05")
|
|
117
|
+
* scans as this coming Thursday. Null when unparseable. */
|
|
118
|
+
export function resetDateClock(resetsAt, locale) {
|
|
119
|
+
const ms = Date.parse(resetsAt);
|
|
120
|
+
if (!Number.isFinite(ms))
|
|
121
|
+
return null;
|
|
122
|
+
const when = new Date(ms);
|
|
123
|
+
const day = when.toLocaleDateString(locale, { weekday: "short" });
|
|
124
|
+
const date = when.toLocaleDateString(locale, { day: "numeric", month: "short" });
|
|
125
|
+
const time = when.toLocaleTimeString(locale, {
|
|
126
|
+
hour: "2-digit",
|
|
127
|
+
minute: "2-digit",
|
|
128
|
+
hour12: false,
|
|
129
|
+
});
|
|
130
|
+
return `${day} ${date} ${time}`;
|
|
131
|
+
}
|
|
132
|
+
/** Short weekday plus calendar date in the locked English copy, e.g.
|
|
133
|
+
* "Tue Sep 15". Mirrors dayOf in src/format/rows.ts, including its pinned
|
|
134
|
+
* en-US locale, so the CLI and the dashboard word the same sentence
|
|
135
|
+
* identically on any machine. */
|
|
136
|
+
export function resetDay(iso) {
|
|
137
|
+
const when = new Date(iso ?? "");
|
|
138
|
+
return `${when.toLocaleDateString("en-US", { weekday: "short" })} ${when.toLocaleDateString("en-US", { day: "numeric", month: "short" })}`;
|
|
120
139
|
}
|
|
121
140
|
/** "6d 12h" style time left from server timestamps. Null when unparseable. */
|
|
122
141
|
export function timeLeft(resetsAt, asOfMs) {
|
|
@@ -360,7 +379,7 @@ export function forecastLine(provider) {
|
|
|
360
379
|
if (!advisory)
|
|
361
380
|
return null;
|
|
362
381
|
if (advisory.bindingWindow === "monthly" && advisory.bindingRemaining <= 0) {
|
|
363
|
-
return `unused until ${
|
|
382
|
+
return `unused until ${resetDay(provider.quota.monthlyResetsAt)}`;
|
|
364
383
|
}
|
|
365
384
|
if (advisory.bindingWindow === "monthly") {
|
|
366
385
|
return `${Math.round(advisory.bindingRemaining)}% of month left`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ageDuration
|
|
2
|
+
import { ageDuration } from "../state.js";
|
|
3
3
|
import { Badge, PACE_TIPS, PaceBar, isEstimated, paceCells, resetClock, resetCountdown, timeLeft, } from "./PaceBar.js";
|
|
4
4
|
import { ProviderIcon, providerTint } from "./ProviderIcon.js";
|
|
5
5
|
import { lastClose } from "./ClosedWeeks.js";
|
|
@@ -20,7 +20,6 @@ export function ProviderCard({ provider, recommended, asOf, onSelect, }) {
|
|
|
20
20
|
const updated = ageDuration(provider.ageMs);
|
|
21
21
|
const resetsIn = quota ? timeLeft(quota.resetsAt, asOfMs) : null;
|
|
22
22
|
const name = provider.displayName;
|
|
23
|
-
const plan = displayPlan(quota?.plan);
|
|
24
23
|
const last = lastClose(provider);
|
|
25
24
|
return (_jsxs("article", { "data-testid": `provider-card-${provider.id}`, role: "button", tabIndex: 0, "aria-label": `${name} details`, onClick: () => onSelect(provider.id), onKeyDown: (e) => {
|
|
26
25
|
// Space or Enter aimed at the windows chevron must not also open the drawer.
|
|
@@ -28,5 +27,5 @@ export function ProviderCard({ provider, recommended, asOf, onSelect, }) {
|
|
|
28
27
|
e.preventDefault();
|
|
29
28
|
onSelect(provider.id);
|
|
30
29
|
}
|
|
31
|
-
}, className: `pcard ${recommended ? "is-recommended is-top-pick" : ""}`, children: [_jsxs("div", { className: "pcard-head", children: [_jsx("span", { className: "picon", style: providerTint(provider.id), "aria-hidden": "true", children: _jsx(ProviderIcon, { id: provider.id, title: name }) }), _jsxs("div", { className: "who", children: [_jsxs("b", { children: [name, recommended && (_jsx("span", { className: "rec-icon", title: "Recommended for next session", role: "img", "aria-label": "recommended", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z" }) }) }))] }),
|
|
30
|
+
}, className: `pcard ${recommended ? "is-recommended is-top-pick" : ""}`, children: [_jsxs("div", { className: "pcard-head", children: [_jsx("span", { className: "picon", style: providerTint(provider.id), "aria-hidden": "true", children: _jsx(ProviderIcon, { id: provider.id, title: name }) }), _jsxs("div", { className: "who", children: [_jsxs("b", { children: [name, recommended && (_jsx("span", { className: "rec-icon", title: "Recommended for next session", role: "img", "aria-label": "recommended", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z" }) }) }))] }), updated && _jsxs("span", { className: "src", children: ["Updated ", updated, " ago"] })] }), _jsx(Badge, { provider: provider })] }), _jsxs("div", { className: "pcard-body", children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), _jsx(WindowsRow, { provider: provider, asOf: asOf, variant: "card" }), _jsxs("div", { className: "pstats", children: [_jsxs("div", { title: PACE_TIPS.avg, children: [_jsx("span", { className: "l", children: "Avg pace" }), _jsx("span", { className: "v", children: pace.avg })] }), _jsxs("div", { title: PACE_TIPS.recent, children: [_jsx("span", { className: "l", children: "24h pace" }), _jsx("span", { className: "v", children: pace.recent })] }), _jsxs("div", { title: PACE_TIPS.ideal, children: [_jsx("span", { className: "l", children: "Ideal pace" }), _jsx("span", { className: "v", children: advisory ? `${advisory.idealRate.toFixed(1)}%/day` : "—" })] }), _jsxs("div", { children: [_jsx("span", { className: "l", children: "Resets in" }), _jsx("span", { className: "v", children: resetsIn ? `${resetsIn}${isEstimated(provider) ? " (est.)" : ""}` : resetCountdown(provider, asOfMs) })] })] })] }), _jsxs("div", { className: "pcard-foot", children: [_jsxs("div", { className: "foot-meta", children: [_jsx("span", { children: resetsDate(provider) ?? resetCountdown(provider, asOfMs) }), last && (_jsxs("span", { className: "last-week", children: ["Last week ", last.leftoverPct, "% leftover", last.resetsAtEstimated ? " (est.)" : ""] }))] }), _jsx("span", { className: "rowbtn", "aria-hidden": "true", children: "Inspect" })] })] }));
|
|
32
31
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { ageDuration, displayPlan, providerSubtitle, shortWindowLabel } from "../state.js";
|
|
4
|
-
import { Badge, PaceBar, barGeometry, isEstimated, paceBadge, paceFigures, resetClock, timeLeft, } from "./PaceBar.js";
|
|
4
|
+
import { Badge, PaceBar, barGeometry, isEstimated, paceBadge, paceFigures, resetClock, resetDateClock, timeLeft, } from "./PaceBar.js";
|
|
5
5
|
import { ProviderIcon, providerTint } from "./ProviderIcon.js";
|
|
6
6
|
import { ClosedWeeks } from "./ClosedWeeks.js";
|
|
7
7
|
import { renameProvider } from "../api.js";
|
|
@@ -166,7 +166,7 @@ export function monthlyMeta(provider) {
|
|
|
166
166
|
const q = provider.quota;
|
|
167
167
|
if (q?.monthlyKind !== "included" || !q.monthlyResetsAt)
|
|
168
168
|
return null;
|
|
169
|
-
const clock =
|
|
169
|
+
const clock = resetDateClock(q.monthlyResetsAt);
|
|
170
170
|
const base = clock ? `Resets ${clock}` : "Reset unknown";
|
|
171
171
|
const adv = provider.advisory;
|
|
172
172
|
if (q.monthlyStatus === "exhausted" || (adv?.bindingWindow === "monthly" && adv.bindingRemaining === 0)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/** Brand-mark paths from the signed-off concept. Unknown ids fall back to a letter. */
|
|
3
3
|
const PATHS = {
|
|
4
4
|
kimi: 'M12 7v5l3 2M12 12m-8.5 0a8.5 8.5 0 1 0 17 0a8.5 8.5 0 1 0-17 0',
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { displayPlan } from "../state.js";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { Badge, PaceBar, forecastLine, paceLines, resetCellLines, } from "./PaceBar.js";
|
|
4
3
|
import { lastClose } from "./ClosedWeeks.js";
|
|
5
4
|
import { WindowsRow } from "./WindowsRow.js";
|
|
@@ -9,10 +8,9 @@ export function ProviderRow({ provider, recommended, asOf, onSelect, }) {
|
|
|
9
8
|
const asOfMs = Date.parse(asOf);
|
|
10
9
|
const lines = paceLines(advisory);
|
|
11
10
|
const name = provider.displayName;
|
|
12
|
-
const plan = displayPlan(quota?.plan);
|
|
13
11
|
const reset = resetCellLines(provider, asOfMs);
|
|
14
12
|
const last = lastClose(provider);
|
|
15
|
-
return (_jsxs("div", { "data-testid": `provider-row-${provider.id}`, className: `lrow ${recommended ? "is-recommended is-top-pick" : ""}`, onClick: () => onSelect(provider.id), children: [_jsxs("div", { className: "prov", "data-label": "Provider", children: [_jsx("span", { className: "dot", style: { background: `var(--p-${provider.id.split(":")[0]}, var(--surface-raised))` }, "aria-hidden": "true" }),
|
|
13
|
+
return (_jsxs("div", { "data-testid": `provider-row-${provider.id}`, className: `lrow ${recommended ? "is-recommended is-top-pick" : ""}`, onClick: () => onSelect(provider.id), children: [_jsxs("div", { className: "prov", "data-label": "Provider", children: [_jsx("span", { className: "dot", style: { background: `var(--p-${provider.id.split(":")[0]}, var(--surface-raised))` }, "aria-hidden": "true" }), _jsx("span", { children: _jsxs("span", { className: "nm", children: [name, recommended && (_jsx("span", { className: "rec-icon", title: "Recommended for next session", role: "img", "aria-label": "recommended", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z" }) }) }))] }) })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? (_jsxs(_Fragment, { children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), _jsx(WindowsRow, { provider: provider, asOf: asOf, variant: "row" })] })) : "—" }), _jsxs("div", { "data-label": "Reset", className: "reset-cell", children: [_jsx("span", { className: "cell-b", children: reset.top }), reset.bottom && _jsx("span", { className: "cell-s", children: reset.bottom }), last && (_jsxs("span", { className: "cell-s", children: ["last week ", last.leftoverPct, "% leftover", last.resetsAtEstimated ? " (est.)" : ""] }))] }), _jsxs("div", { "data-label": "Pace", children: [_jsx(Badge, { provider: provider }), lines.length > 0 && (_jsx("div", { style: { marginTop: 2 }, children: lines.map((l) => (_jsxs("div", { className: "cell-s paceline", title: l.tip, children: [_jsx("span", { children: l.label }), _jsx("span", { className: "cell-b", children: l.value })] }, l.label))) }))] }), _jsx("button", { type: "button", className: "rowbtn", onClick: (e) => {
|
|
16
14
|
e.stopPropagation();
|
|
17
15
|
onSelect(provider.id);
|
|
18
16
|
}, children: "Inspect" }), _jsx("span", { className: "sr-only", children: forecastLine(provider) })] }));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ageLabel } from "../state.js";
|
|
3
3
|
import { timeLeft } from "./PaceBar.js";
|
|
4
4
|
import { ProviderIcon, providerTint } from "./ProviderIcon.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { paceBadge, resetClock, resetCountdown, timeLeft } from "./PaceBar.js";
|
|
4
4
|
export const RAIL_DAYS = 7;
|
|
@@ -10,7 +10,7 @@ export interface WindowUnit {
|
|
|
10
10
|
pct: number;
|
|
11
11
|
/** Presentation-only heat: < 80 none, 80–99 hot, 100 or exhausted full. */
|
|
12
12
|
heat: "" | "hot" | "full";
|
|
13
|
-
/** Tag time outside the pill: "in 7d", or the weekday
|
|
13
|
+
/** Tag time outside the pill: "in 7d", or the weekday plus date when the month is exhausted. */
|
|
14
14
|
time: string | null;
|
|
15
15
|
/** Expanded card track's reset line. */
|
|
16
16
|
reset: string | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { resetDateClock, resetDay } from "./PaceBar.js";
|
|
4
4
|
const MIN_MS = 60_000;
|
|
5
5
|
const HOUR_MS = 60 * MIN_MS;
|
|
6
6
|
const DAY_MS = 24 * HOUR_MS;
|
|
@@ -34,10 +34,10 @@ export function windowUnits(provider, asOfMs) {
|
|
|
34
34
|
if (q.monthlyKind === "included" && typeof q.monthlyPct === "number" && q.monthlyResetsAt) {
|
|
35
35
|
const exhausted = q.monthlyStatus === "exhausted";
|
|
36
36
|
const left = windowCountdown(q.monthlyResetsAt, asOfMs);
|
|
37
|
-
const clock =
|
|
37
|
+
const clock = resetDateClock(q.monthlyResetsAt);
|
|
38
38
|
units.push({ win: "month", tag: "Mth", cardLabel: "Monthly", rowLabel: "Month",
|
|
39
39
|
pct: q.monthlyPct, heat: heatOf(q.monthlyPct, exhausted),
|
|
40
|
-
time: exhausted ?
|
|
40
|
+
time: exhausted ? resetDay(q.monthlyResetsAt) : left ? `in ${left}` : null,
|
|
41
41
|
reset: exhausted ? (clock ? `resets ${clock}` : null) : left ? `in ${left}` : null });
|
|
42
42
|
}
|
|
43
43
|
return units;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { ProvidersPanel } from "./ProvidersPanel.js";
|
|
4
4
|
import { CliIntegrationPanel } from "./CliIntegrationPanel.js";
|