dsh-plugin-subscription-oauth 0.7.0 → 0.8.1

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/lib/client.js CHANGED
@@ -557,6 +557,18 @@ const usageChipCss = `
557
557
  color: var(--dsw-alias-label-primary, #e8e8e8);
558
558
  font-variant-numeric: tabular-nums;
559
559
  }
560
+ .dsh-subscription-usage-chip-account {
561
+ display: flex;
562
+ flex-direction: column;
563
+ gap: 6px;
564
+ /* A hairline between accounts, so two sign-ins read as two groups not one run. */
565
+ padding-top: 6px;
566
+ border-top: 1px solid var(--dsw-alias-border-secondary, #ffffff14);
567
+ }
568
+ .dsh-subscription-usage-chip-account:first-of-type {
569
+ padding-top: 0;
570
+ border-top: 0;
571
+ }
560
572
  .dsh-subscription-usage-chip-panel {
561
573
  position: absolute;
562
574
  bottom: calc(100% + 6px);
@@ -564,9 +576,9 @@ const usageChipCss = `
564
576
  z-index: 20;
565
577
  display: flex;
566
578
  flex-direction: column;
567
- gap: 8px;
568
- min-width: 168px;
569
- padding: 10px 12px;
579
+ gap: 10px;
580
+ min-width: 180px;
581
+ padding: 12px 14px;
570
582
  border: 1px solid var(--dsw-alias-border-secondary, #ffffff1f);
571
583
  border-radius: var(--dsw-radius-md, 10px);
572
584
  background: var(--dsw-alias-bg-layer-2, #26262a);
@@ -581,7 +593,7 @@ const usageChipCss = `
581
593
  .dsh-subscription-usage-chip-meter {
582
594
  display: flex;
583
595
  flex-direction: column;
584
- gap: 4px;
596
+ gap: 5px;
585
597
  }
586
598
  .dsh-subscription-usage-chip-meterHead {
587
599
  display: flex;
@@ -2300,14 +2312,16 @@ function balanceOf(value) {
2300
2312
  * Format one wallet for the chip's compact line.
2301
2313
  *
2302
2314
  * A balance is money, not a percentage, so it renders as `¥6.89` rather than a meter;
2303
- * the currency prefix keeps two currencies from being mistaken for one sum.
2315
+ * the currency prefix keeps two currencies from being mistaken for one sum. The amount
2316
+ * is rounded to two decimal places (a balance reads to the cent, and a figure like
2317
+ * `1.1` for `1.10` would mislead) and always carries both places.
2304
2318
  *
2305
2319
  * @param wallet - the wallet to show.
2306
- * @returns the short form, e.g. `¥6.89` or `+¥2` for a bonus wallet.
2320
+ * @returns the short form, e.g. `¥6.89` or `+$2.00` for a bonus wallet.
2307
2321
  */
2308
2322
  function formatBalance(wallet) {
2309
2323
  const amount = Number.parseFloat(wallet.balance);
2310
- const trimmed = Number.isFinite(amount) ? String(Math.round(amount * 100) / 100) : wallet.balance;
2324
+ const trimmed = Number.isFinite(amount) ? amount.toFixed(2) : wallet.balance;
2311
2325
  const symbol = wallet.currency === "CNY" ? "¥" : wallet.currency === "USD" ? "$" : `${wallet.currency} `;
2312
2326
  return `${wallet.bonus ? "+" : ""}${symbol}${trimmed}`;
2313
2327
  }
@@ -2336,43 +2350,78 @@ function shortWindow(label) {
2336
2350
  return label.length <= 4 ? label : label.slice(0, 4);
2337
2351
  }
2338
2352
  /**
2339
- * Pick the window a single line should show.
2353
+ * Reduce a route's per-account usage to one reading per account.
2340
2354
  *
2341
- * A plan can meter several windows at once (a rolling five-hour one and a weekly one).
2342
- * The most constrained is the one that will stop work soonest, so it earns the space;
2343
- * ties fall to the earlier window, which is the order the providers state them in.
2355
+ * A provider can hold several accounts (two Antigravity sign-ins, say), and the chip
2356
+ * shows each one's allowance on its own line, the way the settings cards do — a merged
2357
+ * figure would hide that one account is exhausted while the other is free.
2344
2358
  *
2345
- * @param windows - the route's metered windows.
2346
- * @returns the window to show, or undefined when none states a figure.
2359
+ * @param usage - every account the route answered for.
2360
+ * @returns one reading per account that states a figure, in the route's order.
2347
2361
  */
2348
- function pickWindow(windows) {
2349
- let best;
2350
- for (const window$1 of windows) {
2351
- if (window$1.usedPercent === void 0) continue;
2352
- if (best?.usedPercent === void 0 || window$1.usedPercent > best.usedPercent) best = window$1;
2362
+ /**
2363
+ * Reduce a route's per-account usage to one reading per account.
2364
+ *
2365
+ * A provider can hold several accounts (two Antigravity sign-ins, say). Every account's
2366
+ * windows are kept as the route stated them — a percentage-metered quota and a credit
2367
+ * balance (`credits`) alike — so the per-account panel shows the real figures, and the
2368
+ * compact line's merge decides which to integrate.
2369
+ *
2370
+ * @param usage - every account the route answered for.
2371
+ * @returns one reading per account that states any window, in the route's order.
2372
+ */
2373
+ function accountReadingsOf(usage) {
2374
+ const out = [];
2375
+ for (const entry of usage) {
2376
+ if (entry.windows.length === 0) continue;
2377
+ out.push({
2378
+ account: entry.account,
2379
+ windows: entry.windows,
2380
+ ...entry.plan === void 0 ? {} : { plan: entry.plan }
2381
+ });
2353
2382
  }
2354
- return best;
2383
+ return out;
2355
2384
  }
2356
2385
  /**
2357
- * Reduce one route's usage to what the chip renders.
2386
+ * Merge every account's percentage-metered windows into one figure per label.
2387
+ *
2388
+ * The chip's compact line integrates the whole pool: two accounts at 50 percent read as
2389
+ * one window at 50 percent (the same allowance spent twice as wide), not two lines. A
2390
+ * credit balance (`credits`) is money, not a share of a shared allowance, so it is not
2391
+ * merged — it stays on the per-account panel, where it reads correctly. Only windows
2392
+ * that state a `usedPercent` (a share of an allowance) merge.
2358
2393
  *
2359
- * @param provider - the route the figures came from.
2360
- * @param label - the route's display name.
2361
- * @param usage - the accounts the route answered for.
2362
- * @returns the reading, or undefined when the route stated no figure.
2394
+ * @param readings - one per account, from {@link accountReadingsOf}.
2395
+ * @returns one merged window per percentage-metered label, most-spent first.
2363
2396
  */
2364
- function readingOf(provider, label, usage) {
2365
- for (const entry of usage) {
2366
- const window$1 = pickWindow(entry.windows);
2367
- if (window$1 === void 0) continue;
2368
- return {
2369
- provider,
2397
+ function mergeAccountWindows(readings) {
2398
+ const byLabel = /* @__PURE__ */ new Map();
2399
+ for (const reading of readings) for (const window$1 of reading.windows) {
2400
+ if (window$1.usedPercent === void 0) continue;
2401
+ const list = byLabel.get(window$1.label) ?? [];
2402
+ list.push(window$1);
2403
+ byLabel.set(window$1.label, list);
2404
+ }
2405
+ const merged = [];
2406
+ for (const [label, list] of byLabel) {
2407
+ const withPercent = list.filter((window$1) => window$1.usedPercent !== void 0);
2408
+ if (withPercent.length === 0) continue;
2409
+ const usedPercent = withPercent.reduce((sum, window$1) => sum + (window$1.usedPercent ?? 0), 0) / withPercent.length;
2410
+ const resets = list.map((window$1) => window$1.resetsAt).filter((at) => typeof at === "number");
2411
+ merged.push({
2370
2412
  label,
2371
- windows: entry.windows.filter((candidate) => candidate.usedPercent !== void 0),
2372
- window: window$1,
2373
- ...entry.plan === void 0 ? {} : { plan: entry.plan }
2374
- };
2413
+ usedPercent: Math.max(0, Math.min(100, usedPercent)),
2414
+ ...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) }
2415
+ });
2375
2416
  }
2417
+ const order = (label) => {
2418
+ const short = shortWindow(label);
2419
+ if (short === "5h" || short === "s") return 0;
2420
+ if (short === "1d" || short === "mo") return 1;
2421
+ if (short === "7d") return 2;
2422
+ return 3;
2423
+ };
2424
+ return merged.sort((left, right) => order(left.label) - order(right.label));
2376
2425
  }
2377
2426
  /**
2378
2427
  * Format a window's reset the way the settings cards do.
@@ -2402,6 +2451,7 @@ function formatReset(at, now) {
2402
2451
  */
2403
2452
  function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, useProjection }) {
2404
2453
  const [reading, setReading] = (0, react.useState)(void 0);
2454
+ const [accountReadings, setAccountReadings] = (0, react.useState)(void 0);
2405
2455
  const [balance, setBalance] = (0, react.useState)(void 0);
2406
2456
  const [ccBalance, setCcBalance] = (0, react.useState)(void 0);
2407
2457
  const [now, setNow] = (0, react.useState)(() => Date.now());
@@ -2438,8 +2488,7 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2438
2488
  ]);
2439
2489
  const load = (0, react.useCallback)(async (target) => {
2440
2490
  try {
2441
- const result = await call("usage", { provider: target });
2442
- return readingOf(target, result.status.providers.find((entry) => entry.provider === target)?.displayName ?? target, result.usage);
2491
+ return accountReadingsOf((await call("usage", { provider: target })).usage);
2443
2492
  } catch {
2444
2493
  return;
2445
2494
  }
@@ -2465,7 +2514,7 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2465
2514
  (0, react.useEffect)(() => {
2466
2515
  const target = selected !== void 0 && isOwnProvider(selected) ? selected : lastOwn.current;
2467
2516
  if (target === void 0) {
2468
- setReading(void 0);
2517
+ setAccountReadings(void 0);
2469
2518
  return;
2470
2519
  }
2471
2520
  let live = true;
@@ -2474,7 +2523,7 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2474
2523
  load(target).then((next) => {
2475
2524
  if (!live || next === void 0) return;
2476
2525
  lastOwn.current = target;
2477
- setReading(next);
2526
+ setAccountReadings(next);
2478
2527
  });
2479
2528
  };
2480
2529
  refresh();
@@ -2592,10 +2641,9 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2592
2641
  ]
2593
2642
  });
2594
2643
  }
2595
- if (reading === void 0) return null;
2596
- if (reading.window.usedPercent === void 0) return null;
2597
- formatReset(reading.window.resetsAt, now);
2598
- const short = shortLabel(reading.provider, reading.label);
2644
+ if (accountReadings === void 0 || accountReadings.length === 0) return null;
2645
+ const short = shortLabel(selected ?? "", selected ?? "");
2646
+ const merged = mergeAccountWindows(accountReadings);
2599
2647
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2600
2648
  className: "dsh-subscription-usage-chip",
2601
2649
  children: [
@@ -2604,7 +2652,7 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2604
2652
  type: "button",
2605
2653
  className: "dsh-subscription-usage-chip-trigger",
2606
2654
  "aria-expanded": open,
2607
- title: t("usageChipTitle", { provider: reading.label }),
2655
+ title: t("usageChipTitle", { provider: selected ?? "" }),
2608
2656
  onClick: (event) => {
2609
2657
  event.stopPropagation();
2610
2658
  setOpen((was) => !was);
@@ -2612,7 +2660,7 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2612
2660
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2613
2661
  className: "dsh-subscription-usage-chip-label",
2614
2662
  children: short
2615
- }), reading.windows.map((window$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2663
+ }), merged.map((window$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2616
2664
  className: "dsh-subscription-usage-chip-cell",
2617
2665
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2618
2666
  className: "dsh-subscription-usage-chip-window",
@@ -2626,22 +2674,26 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2626
2674
  open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2627
2675
  className: "dsh-subscription-usage-chip-panel",
2628
2676
  role: "status",
2629
- children: [
2630
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2631
- className: "dsh-subscription-usage-chip-full",
2632
- children: t("usageChipFull", { provider: reading.label })
2633
- }),
2634
- reading.windows.map((window$1) => {
2677
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2678
+ className: "dsh-subscription-usage-chip-full",
2679
+ children: t("usageChipFull", { provider: selected ?? "" })
2680
+ }), accountReadings.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2681
+ className: "dsh-subscription-usage-chip-account",
2682
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2683
+ className: "dsh-subscription-usage-chip-meterHead",
2684
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: account.plan ?? account.account })
2685
+ }), account.windows.map((window$1) => {
2635
2686
  const windowReset = formatReset(window$1.resetsAt, now);
2687
+ const amount = window$1.usedPercent === void 0 && window$1.used !== void 0 && window$1.limit !== void 0 ? `${String(window$1.used)}/${String(window$1.limit)}` : `${String(Math.round(window$1.usedPercent ?? 0))}%`;
2636
2688
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2637
2689
  className: "dsh-subscription-usage-chip-meter",
2638
2690
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2639
2691
  className: "dsh-subscription-usage-chip-meterHead",
2640
2692
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window$1.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2641
2693
  className: "dsh-subscription-usage-chip-detail",
2642
- children: `${String(Math.round(window$1.usedPercent ?? 0))}%${windowReset === void 0 ? "" : ` · ${windowReset}`}`
2694
+ children: `${amount}${windowReset === void 0 ? "" : ` · ${windowReset}`}`
2643
2695
  })]
2644
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2696
+ }), window$1.usedPercent !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2645
2697
  className: "dsh-subscription-usage-chip-track",
2646
2698
  "aria-hidden": "true",
2647
2699
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -2650,12 +2702,8 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2650
2702
  })
2651
2703
  })]
2652
2704
  }, window$1.label);
2653
- }),
2654
- reading.plan === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2655
- className: "dsh-subscription-usage-chip-detail",
2656
- children: reading.plan
2657
- })
2658
- ]
2705
+ })]
2706
+ }, account.account))]
2659
2707
  })
2660
2708
  ]
2661
2709
  });