quotacap 0.0.28 → 0.0.30

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.
@@ -12,6 +12,8 @@ export function paceBadge(provider) {
12
12
  return "Not reporting";
13
13
  if (advisory.status === "at risk")
14
14
  return "Cap risk";
15
+ if (advisory.status === "unknown")
16
+ return "Measuring";
15
17
  switch (advisory.urgency) {
16
18
  case "burn now":
17
19
  case "use soon":
@@ -23,6 +25,46 @@ export function paceBadge(provider) {
23
25
  return "On track";
24
26
  }
25
27
  }
28
+ /** Split the advisory into its two displayed paces. `burnRate` doubles as the
29
+ * forecast input, so the recent figure only exists when it was measured. */
30
+ export function paceFigures(advisory) {
31
+ if (!advisory)
32
+ return { avg: null, recent: null };
33
+ const avg = advisory.avgPace ?? null;
34
+ const recent = advisory.paceSource === "recent" ? advisory.burnRate : null;
35
+ return { avg, recent };
36
+ }
37
+ /** Card pace cells: average and 24h rate side by side, each "—" when unknown.
38
+ * Unlike the collapsed summaries, both cells always render so the pair
39
+ * reads as two peer figures. */
40
+ export function paceCells(advisory) {
41
+ const { avg, recent } = paceFigures(advisory);
42
+ return {
43
+ avg: avg !== null ? `${avg.toFixed(1)}%/day` : "—",
44
+ recent: recent !== null ? `${recent.toFixed(1)}%/day` : "—",
45
+ };
46
+ }
47
+ /** Hover explanations shared by cards and rows. Plain words, no jargon. */
48
+ export const PACE_TIPS = {
49
+ avg: "Window average — quota used ÷ days elapsed. Your overall pace this window.",
50
+ recent: "Burn over the last 24 hours. Forecasts use this rate when recent readings exist.",
51
+ ideal: "The even pace that lands exactly on 100% at reset: remaining ÷ days left.",
52
+ };
53
+ /** Table-row pace stack: one labeled line per known pace plus the needed
54
+ * rate. Both paces always show when known (no collapsing) so rows read
55
+ * uniformly. Empty when no advisory is present. */
56
+ export function paceLines(advisory) {
57
+ if (!advisory)
58
+ return [];
59
+ const { avg, recent } = paceFigures(advisory);
60
+ const lines = [];
61
+ if (avg !== null)
62
+ lines.push({ label: "Avg", value: `${avg.toFixed(1)}%/day`, tip: PACE_TIPS.avg });
63
+ if (recent !== null)
64
+ lines.push({ label: "24h", value: `${recent.toFixed(1)}%/day`, tip: PACE_TIPS.recent });
65
+ lines.push({ label: "Ideal", value: `${advisory.idealRate.toFixed(1)}%/day`, tip: PACE_TIPS.ideal });
66
+ return lines;
67
+ }
26
68
  /** Plain-words reason shown beside "Not reporting". Null when the badge says it all. */
27
69
  export function exclusionDetail(reason) {
28
70
  switch (reason) {
@@ -39,16 +81,6 @@ export function exclusionDetail(reason) {
39
81
  return null;
40
82
  }
41
83
  }
42
- const EVIDENCE_LABELS = {
43
- measured: "Measured",
44
- "window-average": "Window avg",
45
- };
46
- /** Coordinator-locked evidence labels. Unknown tokens pass through verbatim. */
47
- export function evidenceLabels(evidence) {
48
- return evidence
49
- .filter((e) => e !== "estimated-reset")
50
- .map((e) => EVIDENCE_LABELS[e] ?? e);
51
- }
52
84
  export function isEstimated(provider) {
53
85
  return provider.evidence.includes("estimated-reset");
54
86
  }
@@ -103,6 +135,30 @@ export function resetCountdown(provider, asOfMs) {
103
135
  const estimated = isEstimated(provider) ? " (est.)" : "";
104
136
  return `in ${left}${estimated}`;
105
137
  }
138
+ /**
139
+ * Reset cell for the table view: clock time plus countdown, e.g.
140
+ * "Tue 10:25 · in 20h 30m". Estimated resets mark the timestamp, per the
141
+ * 0.0.25 convention. Falls back to the countdown-only wording for
142
+ * reset-passed, missing, and unparseable rows.
143
+ */
144
+ export function resetCell(provider, asOfMs) {
145
+ if (provider.exclusionReason === "reset-passed")
146
+ return "awaiting fresh window";
147
+ if (!provider.quota)
148
+ return "no readings";
149
+ const clock = resetClock(provider.quota.resetsAt);
150
+ const left = timeLeft(provider.quota.resetsAt, asOfMs);
151
+ const estimated = isEstimated(provider) ? " (est.)" : "";
152
+ if (clock && left)
153
+ return `${clock}${estimated} · in ${left}`;
154
+ if (left === null) {
155
+ const resetMs = Date.parse(provider.quota.resetsAt);
156
+ if (Number.isFinite(resetMs) && resetMs <= asOfMs)
157
+ return "awaiting fresh window";
158
+ return "reset unknown";
159
+ }
160
+ return "reset unknown";
161
+ }
106
162
  /** Bar geometry from server timestamps and percents. Presentation only. */
107
163
  export function barGeometry(provider, asOfMs) {
108
164
  const usedPct = provider.quota ? Math.min(100, Math.max(0, provider.quota.usedPct)) : 0;
@@ -126,6 +182,8 @@ export function badgeColor(badge) {
126
182
  return "var(--ahead)";
127
183
  case "Cap risk":
128
184
  return "var(--danger)";
185
+ case "Measuring":
186
+ return "var(--ink-soft)";
129
187
  case "Not reporting":
130
188
  return "var(--line-strong)";
131
189
  }
@@ -140,6 +198,8 @@ export function fillToken(badge) {
140
198
  return "var(--fill-ahead)";
141
199
  case "Cap risk":
142
200
  return "var(--fill-cap)";
201
+ case "Measuring":
202
+ return "var(--fill-out)";
143
203
  case "Not reporting":
144
204
  return "var(--fill-out)";
145
205
  }
@@ -154,6 +214,8 @@ export function edgeToken(badge) {
154
214
  return "var(--edge-ahead)";
155
215
  case "Cap risk":
156
216
  return "var(--edge-cap)";
217
+ case "Measuring":
218
+ return "var(--edge-out)";
157
219
  case "Not reporting":
158
220
  return "var(--edge-out)";
159
221
  }
@@ -188,7 +250,7 @@ export function Badge({ provider }) {
188
250
  ? "pace-ahead"
189
251
  : badge === "Cap risk"
190
252
  ? "pace-cap"
191
- : "pace-out";
253
+ : "pace-out"; // Not reporting and Measuring share the neutral style
192
254
  return (_jsxs("span", { "data-testid": "pace-badge", className: `pace ${badgeClass}`, children: [badge, detail ? ` · ${detail}` : ""] }));
193
255
  }
194
256
  /**
@@ -236,6 +298,9 @@ export function forecastLine(provider) {
236
298
  const advisory = provider.advisory;
237
299
  if (!advisory)
238
300
  return null;
301
+ if (advisory.remaining <= 0) {
302
+ return "Exhausted · quota fully used";
303
+ }
239
304
  if (advisory.paceSource === "unknown") {
240
305
  return `Measuring pace · ${Math.round(advisory.remaining)}% remaining`;
241
306
  }
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  const STATES = [
4
- { label: "On track", cls: "pace-ontrack", meaning: "Usage aligns with time (±20%)" },
4
+ { label: "On track", cls: "pace-ontrack", meaning: "Burning at a rate the window sustains" },
5
5
  { label: "Behind pace", cls: "pace-behind", meaning: "Burning slow; risk of unused quota waste" },
6
6
  { label: "Ahead of pace", cls: "pace-ahead", meaning: "Burning faster than time; monitor usage" },
7
7
  { label: "Cap risk", cls: "pace-cap", meaning: "Will hit cap before reset window closes" },
8
+ { label: "Measuring", cls: "pace-out", meaning: "Not enough data yet; pace appears after 6h" },
8
9
  { label: "Not reporting", cls: "pace-out", meaning: "No usable reading; excluded from ranking" },
9
10
  ];
10
11
  export function PacingLegend() {
@@ -28,7 +29,7 @@ export function PacingLegend() {
28
29
  document.removeEventListener("keydown", onKey);
29
30
  };
30
31
  }, [open]);
31
- return (_jsxs("div", { className: "legend-wrap", ref: rootRef, children: [_jsx("button", { type: "button", className: "legend-btn", "aria-expanded": open, "aria-controls": "pacing-legend", onClick: () => setOpen((v) => !v), children: "Pacing Legend \u25BE" }), open && (_jsxs("div", { id: "pacing-legend", className: "legend-popover", role: "dialog", "aria-label": "Pacing legend", children: [_jsxs("div", { className: "legend-head", children: [_jsx("b", { children: "Pacing Scale Legend" }), _jsx("button", { type: "button", className: "legend-close", "aria-label": "Close pacing legend", onClick: () => setOpen(false), children: "\u00D7" })] }), _jsx("p", { className: "legend-intro", children: "Pacing measures quota burn against elapsed window time:" }), _jsx("div", { className: "legend-rows", children: STATES.map((s) => (_jsxs("div", { className: "legend-row", children: [_jsx("span", { className: `pace ${s.cls}`, children: s.label }), _jsx("span", { children: s.meaning })] }, s.label))) }), _jsxs("div", { className: "legend-foot", children: [_jsx("code", { children: "\u2588" }), " allowance used \u00B7 ", _jsx("code", { children: "\u2502" }), " time elapsed \u00B7 ", _jsx("code", { children: "\u2591" }), " projected waste"] })] }))] }));
32
+ return (_jsxs("div", { className: "legend-wrap", ref: rootRef, children: [_jsx("button", { type: "button", className: "legend-btn", "aria-expanded": open, "aria-controls": "pacing-legend", onClick: () => setOpen((v) => !v), children: "Pacing Legend \u25BE" }), open && (_jsxs("div", { id: "pacing-legend", className: "legend-popover", role: "dialog", "aria-label": "Pacing legend", children: [_jsxs("div", { className: "legend-head", children: [_jsx("b", { children: "Pacing Scale Legend" }), _jsx("button", { type: "button", className: "legend-close", "aria-label": "Close pacing legend", onClick: () => setOpen(false), children: "\u00D7" })] }), _jsx("p", { className: "legend-intro", children: "Pacing measures quota burn against elapsed window time:" }), _jsx("div", { className: "legend-rows", children: STATES.map((s) => (_jsxs("div", { className: "legend-row", children: [_jsx("span", { className: `pace ${s.cls}`, children: s.label }), _jsx("span", { children: s.meaning })] }, s.label))) }), _jsxs("div", { className: "legend-foot", children: [_jsx("code", { children: "\u2588" }), " allowance used \u00B7 ", _jsx("code", { children: "\u2502" }), " time elapsed \u00B7 ", _jsx("code", { children: "\u2591" }), " projected waste"] }), _jsx("div", { className: "legend-foot", children: "Avg = used \u00F7 days elapsed \u00B7 24h = burn over the last day \u00B7 Ideal = remaining \u00F7 days left" })] }))] }));
32
33
  }
33
34
  function servingHost() {
34
35
  try {
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ageDuration } from "../state.js";
3
- import { Badge, PaceBar, isEstimated, resetClock, resetCountdown, timeLeft, } from "./PaceBar.js";
3
+ import { Badge, PACE_TIPS, PaceBar, isEstimated, paceCells, resetClock, resetCountdown, timeLeft, } from "./PaceBar.js";
4
4
  import { ProviderIcon, providerTint } from "./ProviderIcon.js";
5
5
  function resetsDate(provider) {
6
6
  if (!provider.quota)
@@ -13,6 +13,7 @@ function resetsDate(provider) {
13
13
  export function ProviderCard({ provider, recommended, asOf, onSelect, }) {
14
14
  const quota = provider.quota;
15
15
  const advisory = provider.advisory;
16
+ const pace = paceCells(advisory);
16
17
  const asOfMs = Date.parse(asOf);
17
18
  const updated = ageDuration(provider.ageMs);
18
19
  const resetsIn = quota ? timeLeft(quota.resetsAt, asOfMs) : null;
@@ -22,5 +23,5 @@ export function ProviderCard({ provider, recommended, asOf, onSelect, }) {
22
23
  e.preventDefault();
23
24
  onSelect(provider.id);
24
25
  }
25
- }, 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" }) }) }))] }), quota && quota.plan && _jsx("span", { className: "pl", children: quota.plan }), 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 }), _jsxs("div", { className: "pstats", children: [_jsxs("div", { children: [_jsx("span", { className: "l", children: "Pace" }), _jsx("span", { className: "v", children: advisory && advisory.burnRate !== null ? `${advisory.burnRate.toFixed(1)}%/d` : "—" })] }), _jsxs("div", { children: [_jsx("span", { className: "l", children: "Needed" }), _jsx("span", { className: "v", children: advisory ? `${advisory.idealRate.toFixed(1)}%/d` : "—" })] }), _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: [_jsx("span", { children: resetsDate(provider) ?? resetCountdown(provider, asOfMs) }), _jsx("span", { className: "rowbtn", "aria-hidden": "true", children: "Inspect" })] })] }));
26
+ }, 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" }) }) }))] }), quota && quota.plan && _jsx("span", { className: "pl", children: quota.plan }), 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 }), _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: [_jsx("span", { children: resetsDate(provider) ?? resetCountdown(provider, asOfMs) }), _jsx("span", { className: "rowbtn", "aria-hidden": "true", children: "Inspect" })] })] }));
26
27
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { ageDuration, providerSubtitle } from "../state.js";
4
- import { Badge, PaceBar, barGeometry, isEstimated, paceBadge, resetClock, timeLeft, } from "./PaceBar.js";
4
+ import { Badge, PaceBar, barGeometry, isEstimated, paceBadge, paceFigures, resetClock, timeLeft, } from "./PaceBar.js";
5
5
  import { ProviderIcon, providerTint } from "./ProviderIcon.js";
6
6
  import { renameProvider } from "../api.js";
7
7
  export function exclusionWords(reason) {
@@ -122,6 +122,8 @@ export function pacingStatusToken(provider) {
122
122
  return "ahead";
123
123
  case "Cap risk":
124
124
  return "cap";
125
+ case "Measuring":
126
+ return "measuring";
125
127
  case "Not reporting":
126
128
  return "out";
127
129
  }
@@ -153,11 +155,21 @@ export function rankingCopy(provider, recommendation) {
153
155
  if (!advisory || advisory.paceSource === "unknown" || advisory.burnRate === null) {
154
156
  pace = modelFailure(provider) ?? "No current pace.";
155
157
  }
156
- else if (advisory.status === "at risk") {
157
- pace = `${advisory.burnRate.toFixed(1)}%/day so far this window, against a ${advisory.idealRate.toFixed(1)}%/day target.`;
158
+ else if (advisory.remaining <= 0) {
159
+ pace = `Exhausted at ${quota?.usedPct ?? 100}% used.${stamp ? ` Resets ${stamp}.` : ""}`;
158
160
  }
159
161
  else {
160
- pace = `${advisory.burnRate.toFixed(1)}%/day so far this window. Finishing it needs ${advisory.idealRate.toFixed(1)}%/day.`;
162
+ const { avg, recent } = paceFigures(advisory);
163
+ const figures = avg !== null && recent !== null && avg.toFixed(1) !== recent.toFixed(1)
164
+ ? `${avg.toFixed(1)}%/day window average and ${recent.toFixed(1)}%/day over the last 24h`
165
+ : avg !== null
166
+ ? `${avg.toFixed(1)}%/day window average`
167
+ : `${recent.toFixed(1)}%/day over the last 24h`;
168
+ const ideal = `${advisory.idealRate.toFixed(1)}%/day`;
169
+ pace =
170
+ advisory.status === "at risk"
171
+ ? `${figures}, against an ideal pace of ${ideal}.`
172
+ : `${figures}. The ideal pace to finish is ${ideal}.`;
161
173
  }
162
174
  let decision;
163
175
  if (provider.exclusionReason) {
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Badge, PaceBar, evidenceLabels, forecastLine, resetCountdown, } from "./PaceBar.js";
2
+ import { Badge, PaceBar, forecastLine, paceLines, resetCell, } from "./PaceBar.js";
3
3
  export function ProviderRow({ provider, recommended, asOf, onSelect, }) {
4
4
  const quota = provider.quota;
5
5
  const advisory = provider.advisory;
6
6
  const asOfMs = Date.parse(asOf);
7
- const evidence = evidenceLabels(provider.evidence);
7
+ const lines = paceLines(advisory);
8
8
  const name = provider.displayName;
9
- 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" }), _jsxs("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" }) }) }))] }), quota?.plan && _jsx("span", { className: "pl", children: quota.plan }), quota?.sessionPct !== undefined && quota.sessionPct !== null && (_jsxs("span", { className: "src", children: ["Session use ", quota.sessionPct, "%"] }))] })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? _jsx(PaceBar, { provider: provider, asOf: asOf }) : "—" }), _jsx("div", { "data-label": "Resets in", children: _jsx("span", { className: "cell-b", children: resetCountdown(provider, asOfMs) }) }), _jsxs("div", { "data-label": "Pace", children: [_jsx(Badge, { provider: provider }), advisory && advisory.burnRate !== null && (_jsxs("div", { className: "cell-s", style: { marginTop: 2 }, children: ["Pace ", advisory.burnRate.toFixed(1), "%/d \u00B7 needed ", advisory.idealRate.toFixed(1), "%/d"] })), evidence.length > 0 && _jsx("div", { className: "cell-s", style: { marginTop: 2 }, children: evidence.join(" · ") })] }), _jsx("button", { type: "button", className: "rowbtn", onClick: (e) => {
9
+ 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" }), _jsxs("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" }) }) }))] }), quota?.plan && _jsx("span", { className: "pl", children: quota.plan }), quota?.sessionPct !== undefined && quota.sessionPct !== null && (_jsxs("span", { className: "src", children: ["Session use ", quota.sessionPct, "%"] }))] })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? _jsx(PaceBar, { provider: provider, asOf: asOf }) : "—" }), _jsx("div", { "data-label": "Reset", children: _jsx("span", { className: "cell-b", children: resetCell(provider, asOfMs) }) }), _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) => {
10
10
  e.stopPropagation();
11
11
  onSelect(provider.id);
12
12
  }, children: "Inspect" }), _jsx("span", { className: "sr-only", children: forecastLine(provider) })] }));
@@ -6,7 +6,7 @@ const RAIL_MS = RAIL_DAYS * 24 * 60 * 60 * 1000;
6
6
  /** Pins within this span of rail group into one collision window. */
7
7
  const COLLISION_PCT = 3;
8
8
  /** Pins per window before they group into a cluster pin. */
9
- const WINDOW_CAPACITY = 2;
9
+ const WINDOW_CAPACITY = 3;
10
10
  /**
11
11
  * Place reset pins proportionally over the next 7 days from `asOf`.
12
12
  * Crowded windows past tier capacity group into clusters; resets beyond the
@@ -102,5 +102,5 @@ export function SubscriptionList({ providers, recommendation, asOf, lastComplete
102
102
  const asOfMs = Date.parse(asOf);
103
103
  const ranked = enabled.filter((p) => p.exclusionReason === null);
104
104
  const excluded = enabled.filter((p) => p.exclusionReason !== null);
105
- return (_jsxs("section", { "aria-label": "Subscriptions", children: [_jsxs("div", { className: "section-head", children: [_jsx("h2", { children: "All subscriptions" }), _jsxs("div", { className: "section-head-controls", children: [_jsx(SortDropdown, { value: sortKey, onChange: setSortKey }), _jsxs("div", { role: "group", "aria-label": "Display density", className: "seg", children: [_jsxs("button", { type: "button", "aria-pressed": effective === "cards", onClick: () => setDensity("cards"), children: [_jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "3", width: "8", height: "8", rx: "2" }), _jsx("rect", { x: "13", y: "3", width: "8", height: "8", rx: "2" }), _jsx("rect", { x: "3", y: "13", width: "8", height: "8", rx: "2" }), _jsx("rect", { x: "13", y: "13", width: "8", height: "8", rx: "2" })] }), "Cards"] }), _jsxs("button", { type: "button", "aria-pressed": effective === "table", onClick: () => setDensity("table"), children: [_jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "5", width: "18", height: "2.6", rx: "1.3" }), _jsx("rect", { x: "3", y: "11", width: "18", height: "2.6", rx: "1.3" }), _jsx("rect", { x: "3", y: "17", width: "18", height: "2.6", rx: "1.3" })] }), "Table"] })] })] })] }), effective === "cards" ? (_jsx("div", { "data-testid": "cards-grid", className: "cards cards-grid", children: enabled.map((p) => (_jsx(ProviderCard, { provider: p, recommended: p.id === recommendation.use, asOf: asOf, onSelect: onSelectProvider }, p.id))) })) : (_jsx("div", { className: "table-scroll", children: _jsxs("div", { "data-testid": "ledger-table", className: "ledger", role: "table", children: [_jsxs("div", { className: "lrow lhead", role: "row", children: [_jsx("span", { role: "columnheader", children: "Provider" }), _jsx("span", { role: "columnheader", children: "Used vs elapsed" }), _jsx("span", { role: "columnheader", children: "Resets in" }), _jsx("span", { role: "columnheader", children: "Pace" }), _jsx("span", { role: "columnheader" })] }), enabled.map((p) => (_jsx(ProviderRow, { provider: p, recommended: p.id === recommendation.use, asOf: asOf, onSelect: onSelectProvider }, p.id)))] }) })), _jsxs("footer", { className: "health", children: [_jsxs("ul", { children: [_jsxs("li", { className: "legend-rec", children: [_jsx("svg", { className: "legend-star", width: "14", height: "14", 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("span", { children: "Recommended to use next" })] }), _jsxs("li", { children: [_jsx("span", { className: "dot", style: { background: "var(--good)" } }), _jsxs("span", { children: [ranked.length, " used in ranking"] })] }), excluded.length > 0 && (_jsxs("li", { children: [_jsx("span", { className: "dot", style: { background: "var(--fill-out)" } }), _jsxs("span", { children: [excluded.length, " not reporting", excluded.length <= 2 ? ` · ${excluded.map((p) => p.displayName).join(", ")}` : ""] })] }))] }), _jsx("span", { children: nextRefreshLabel(lastCompletedPollAt, polling) })] }), disabled.length > 0 && (_jsxs("section", { "aria-label": "Disabled providers", children: [_jsxs("h3", { children: ["Disabled (", disabled.length, ")"] }), _jsx("ul", { children: disabled.map((p) => (_jsxs("li", { style: { fontSize: "var(--t-2)" }, children: [p.displayName, " \u00B7 disabled \u00B7 ", resetCountdown(p, asOfMs)] }, p.id))) })] }))] }));
105
+ return (_jsxs("section", { "aria-label": "Subscriptions", children: [_jsxs("div", { className: "section-head", children: [_jsx("h2", { children: "All subscriptions" }), _jsxs("div", { className: "section-head-controls", children: [_jsx(SortDropdown, { value: sortKey, onChange: setSortKey }), _jsxs("div", { role: "group", "aria-label": "Display density", className: "seg", children: [_jsxs("button", { type: "button", "aria-pressed": effective === "cards", onClick: () => setDensity("cards"), children: [_jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "3", width: "8", height: "8", rx: "2" }), _jsx("rect", { x: "13", y: "3", width: "8", height: "8", rx: "2" }), _jsx("rect", { x: "3", y: "13", width: "8", height: "8", rx: "2" }), _jsx("rect", { x: "13", y: "13", width: "8", height: "8", rx: "2" })] }), "Cards"] }), _jsxs("button", { type: "button", "aria-pressed": effective === "table", onClick: () => setDensity("table"), children: [_jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "5", width: "18", height: "2.6", rx: "1.3" }), _jsx("rect", { x: "3", y: "11", width: "18", height: "2.6", rx: "1.3" }), _jsx("rect", { x: "3", y: "17", width: "18", height: "2.6", rx: "1.3" })] }), "Table"] })] })] })] }), effective === "cards" ? (_jsx("div", { "data-testid": "cards-grid", className: "cards cards-grid", children: enabled.map((p) => (_jsx(ProviderCard, { provider: p, recommended: p.id === recommendation.use, asOf: asOf, onSelect: onSelectProvider }, p.id))) })) : (_jsx("div", { className: "table-scroll", children: _jsxs("div", { "data-testid": "ledger-table", className: "ledger", role: "table", children: [_jsxs("div", { className: "lrow lhead", role: "row", children: [_jsx("span", { role: "columnheader", children: "Provider" }), _jsx("span", { role: "columnheader", children: "Used vs elapsed" }), _jsx("span", { role: "columnheader", children: "Reset" }), _jsx("span", { role: "columnheader", children: "Pace" }), _jsx("span", { role: "columnheader" })] }), enabled.map((p) => (_jsx(ProviderRow, { provider: p, recommended: p.id === recommendation.use, asOf: asOf, onSelect: onSelectProvider }, p.id)))] }) })), _jsxs("footer", { className: "health", children: [_jsxs("ul", { children: [_jsxs("li", { className: "legend-rec", children: [_jsx("svg", { className: "legend-star", width: "14", height: "14", 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("span", { children: "Recommended to use next" })] }), _jsxs("li", { children: [_jsx("span", { className: "dot", style: { background: "var(--good)" } }), _jsxs("span", { children: [ranked.length, " used in ranking"] })] }), excluded.length > 0 && (_jsxs("li", { children: [_jsx("span", { className: "dot", style: { background: "var(--fill-out)" } }), _jsxs("span", { children: [excluded.length, " not reporting", excluded.length <= 2 ? ` · ${excluded.map((p) => p.displayName).join(", ")}` : ""] })] }))] }), _jsx("span", { children: nextRefreshLabel(lastCompletedPollAt, polling) })] }), disabled.length > 0 && (_jsxs("section", { "aria-label": "Disabled providers", children: [_jsxs("h3", { children: ["Disabled (", disabled.length, ")"] }), _jsx("ul", { children: disabled.map((p) => (_jsxs("li", { style: { fontSize: "var(--t-2)" }, children: [p.displayName, " \u00B7 disabled \u00B7 ", resetCountdown(p, asOfMs)] }, p.id))) })] }))] }));
106
106
  }
@@ -9,6 +9,8 @@ export interface AdvisoryView {
9
9
  remaining: number;
10
10
  idealRate: number;
11
11
  burnRate: number | null;
12
+ /** Window-average pace (used % ÷ days elapsed); null before the window start is known. */
13
+ avgPace: number | null;
12
14
  burnMeasured: boolean;
13
15
  paceSource: PaceSource;
14
16
  daysToExhaust: number | null;
@@ -45,6 +47,11 @@ export interface AttemptView {
45
47
  succeededAt: string | null;
46
48
  success: boolean;
47
49
  failureCategory: FailureCategory;
50
+ diagnosticCode?: string | null;
51
+ summary?: string | null;
52
+ action?: string | null;
53
+ errorDetail?: string | null;
54
+ error?: string | null;
48
55
  }
49
56
  export interface ProviderView {
50
57
  id: string;