@jxgame2020/dsh-token-quota 0.1.35 → 0.1.37

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
@@ -7,6 +7,14 @@ window.__ModuleLoader__.load({
7
7
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  let react = require("react");
10
+ //#region lib/types/types.js
11
+ /**
12
+ * Provider route keys the host knows how to query for account balance. A
13
+ * directory provider outside this list renders as "not integrated for query"
14
+ * in the balance window.
15
+ */
16
+ const TOKEN_QUOTA_BALANCE_PROVIDERS = ["deepseek"];
17
+ //#endregion
10
18
  //#region lib/types/client/store.js
11
19
  /**
12
20
  * Token-quota panel store: the shared, remount-surviving view state. The
@@ -40,6 +48,9 @@ window.__ModuleLoader__.load({
40
48
  order: [],
41
49
  notes: [],
42
50
  notesLoaded: false,
51
+ balances: [],
52
+ balanceEnabled: true,
53
+ balancePollMinutes: 5,
43
54
  musicEnabled: false,
44
55
  musicVolume: .5,
45
56
  musicStyle: "pentatonic",
@@ -92,6 +103,13 @@ window.__ModuleLoader__.load({
92
103
  d.notes = notes;
93
104
  d.notesLoaded = true;
94
105
  },
106
+ setBalances: (d, balances) => {
107
+ d.balances = balances;
108
+ },
109
+ setBalanceSettings: (d, enabled, pollMinutes) => {
110
+ d.balanceEnabled = enabled;
111
+ d.balancePollMinutes = pollMinutes;
112
+ },
95
113
  setMusicEnabled: (d, enabled) => {
96
114
  d.musicEnabled = enabled;
97
115
  },
@@ -105,7 +123,7 @@ window.__ModuleLoader__.load({
105
123
  d.musicOnlyCurrentSession = only;
106
124
  },
107
125
  setUpgrade: (d, upgrade) => {
108
- const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.35" ? upgrade : null;
126
+ const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.36" ? upgrade : null;
109
127
  if (d.upgrade === null || d.upgrade.latestVersion !== normalized?.latestVersion) d.upgradeDismissed = false;
110
128
  d.upgrade = normalized;
111
129
  },
@@ -225,7 +243,7 @@ window.__ModuleLoader__.load({
225
243
  }
226
244
  //#endregion
227
245
  //#region \0dsh-css:/Users/jxgame/Desktop/works/deepseek-harness-package/dsh-token-quota/packages/token-quota/src/client/TokenQuotaPanel.module.css.mjs
228
- const css = ".NPLLIq_panel{box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);pointer-events:auto;z-index:40;opacity:1;border-radius:12px;flex-direction:column;width:320px;max-height:min(72vh,560px);transition:opacity .18s;display:flex;position:fixed;top:56px;right:12px;overflow:hidden;box-shadow:0 8px 28px #0000002e}.NPLLIq_panelDim{opacity:.55}.NPLLIq_panelDimStrong{opacity:.3}.NPLLIq_header{border-bottom:1px solid var(--dsw-alias-border-l1);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:flex-start;gap:8px;padding:10px 12px;display:flex}.NPLLIq_header:active{cursor:grabbing}.NPLLIq_headerText{flex-direction:column;gap:2px;min-width:0;display:flex}.NPLLIq_title{color:var(--dsw-alias-label-primary);align-items:baseline;gap:6px;font-size:13px;font-weight:600;display:flex}.NPLLIq_version{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:10px;font-weight:500}.NPLLIq_versionNew{color:var(--dsw-alias-status-warning-foreground,#b56900);background:var(--dsw-alias-status-warning-background,#fff6d7);cursor:default;border-radius:4px;padding:1px 6px}.NPLLIq_subtitle{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_notesBar{flex-wrap:wrap;align-items:center;gap:4px;margin-top:4px;display:flex}.NPLLIq_noteChip{width:20px;height:20px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:pointer;border-radius:5px;flex:none;justify-content:center;align-items:center;padding:0;font-size:11px;line-height:1;transition:color .12s,border-color .12s;display:inline-flex}.NPLLIq_noteChip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2)}.NPLLIq_noteChipOpen{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.NPLLIq_noteNew{width:22px;height:20px;color:var(--dsw-alias-label-secondary);border:1px dashed var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:5px;flex:none;justify-content:center;align-items:center;padding:0;transition:color .12s,border-color .12s;display:inline-flex;position:relative}.NPLLIq_noteNew:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.NPLLIq_noteNew:disabled{opacity:.4;cursor:not-allowed}.NPLLIq_noteNewPlus{color:inherit;font-size:10px;font-weight:700;line-height:1;position:absolute;bottom:-2px;right:-1px}.NPLLIq_noteWin{z-index:60;pointer-events:auto;box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;display:flex;position:fixed;overflow:hidden;box-shadow:0 8px 24px #00000038}.NPLLIq_noteWinCollapsed{height:auto!important}.NPLLIq_noteWinBar{background:var(--dsw-alias-bg-layer-1);border-bottom:1px solid var(--dsw-alias-border-l1);cursor:grab;user-select:none;flex:none;align-items:center;gap:4px;padding:4px 6px;display:flex}.NPLLIq_noteWinBar:active{cursor:grabbing}.NPLLIq_noteWinGripBar{color:var(--dsw-alias-label-secondary);opacity:.7;flex:none;font-size:10px}.NPLLIq_noteWinTitleText{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;padding:1px 2px;font-size:11px;font-weight:600;overflow:hidden}.NPLLIq_noteWinTitle{min-width:0;color:var(--dsw-alias-label-primary);background:0 0;border:1px solid #0000;border-radius:4px;outline:none;flex:1;padding:1px 2px;font-size:11px;font-weight:600}.NPLLIq_noteWinTitle:hover{border-color:var(--dsw-alias-border-l1)}.NPLLIq_noteWinTitle:focus{background:var(--dsw-alias-bg-overlay);border-color:var(--dsw-alias-brand-primary)}.NPLLIq_noteWinBtn{width:18px;height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;font-size:12px;line-height:1;display:inline-flex}.NPLLIq_noteWinBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.NPLLIq_noteWinText{box-sizing:border-box;width:100%;min-height:0;color:var(--dsw-alias-label-primary);resize:none;background:0 0;border:none;outline:none;flex:1;padding:6px 8px;font-family:inherit;font-size:11px;line-height:1.5}.NPLLIq_noteWinResize{cursor:nwse-resize;background:linear-gradient(135deg, transparent 0 45%, var(--dsw-alias-border-l2) 45% 55%, transparent 55% 70%, var(--dsw-alias-border-l2) 70% 80%, transparent 80%);width:14px;height:14px;position:absolute;bottom:0;right:0}.NPLLIq_noteMenuBackdrop{z-index:100;pointer-events:auto;position:fixed;inset:0}.NPLLIq_noteMenu{z-index:101;pointer-events:auto;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;min-width:96px;padding:4px;display:flex;position:fixed;box-shadow:0 8px 24px #0000003d}.NPLLIq_noteMenuItem{text-align:left;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:5px;padding:5px 8px;font-size:11px}.NPLLIq_noteMenuItem:hover{background:var(--dsw-alias-bg-layer-2)}.NPLLIq_collapse{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_collapse:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_body{padding:4px 0;overflow-y:auto}.NPLLIq_row{border-bottom:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:6px;padding:8px 12px;display:flex}.NPLLIq_rowHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.NPLLIq_rowDrag{cursor:grab;color:var(--dsw-alias-label-secondary);user-select:none;border-radius:4px;flex:none;margin-right:2px;padding:2px 3px;font-size:11px;line-height:1}.NPLLIq_rowDrag:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_rowDrag:active{cursor:grabbing}.NPLLIq_rowDragging{opacity:.45}.NPLLIq_rowDropTarget{box-shadow:inset 0 2px 0 var(--dsw-alias-brand-primary)}.NPLLIq_rowName{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;align-items:center;gap:6px;font-size:12px;font-weight:500;display:inline-flex;overflow:hidden}.NPLLIq_currentBadge{color:var(--dsw-alias-state-success-primary);border:1px solid var(--dsw-alias-state-success-primary);border-radius:8px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_rowMeta{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.NPLLIq_bar{background:var(--dsw-alias-bg-layer-1);border-radius:3px;height:6px;overflow:hidden}.NPLLIq_fillIdle{background:var(--dsw-alias-brand-primary);height:100%;transition:width .2s}.NPLLIq_fillWarn{background:var(--dsw-alias-state-warn-primary);height:100%;transition:width .2s}.NPLLIq_fillOver{background:var(--dsw-alias-state-error-primary);height:100%;transition:width .2s}.NPLLIq_controls{align-items:center;gap:6px;display:flex}.NPLLIq_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-width:0;color:var(--dsw-alias-label-primary);border-radius:6px;flex:1;padding:4px 8px;font-size:11px}.NPLLIq_input::placeholder{color:var(--dsw-alias-label-secondary)}.NPLLIq_save,.NPLLIq_switch{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px;font-size:11px}.NPLLIq_save:hover,.NPLLIq_switch:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}.NPLLIq_switch:disabled{opacity:.5;cursor:default}.NPLLIq_notice,.NPLLIq_noticeError{color:var(--dsw-alias-label-secondary);padding:10px 12px;font-size:12px}.NPLLIq_noticeError{color:var(--dsw-alias-state-error-primary)}.NPLLIq_tab{background:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay);pointer-events:auto;cursor:pointer;z-index:90;border-radius:10px;align-items:center;gap:6px;padding:9px 14px;font-size:12px;font-weight:600;display:inline-flex;position:fixed;top:56px;right:12px;box-shadow:0 6px 20px #00000040}.NPLLIq_tab:hover{filter:brightness(1.08)}.NPLLIq_tabIcon{font-size:12px;line-height:1}.NPLLIq_tabLabel{white-space:nowrap}.NPLLIq_headerActions{flex:none;align-items:center;gap:4px;display:flex}.NPLLIq_settingsBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_settingsBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_settingsBtnActive{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2,#0000000f);box-shadow:inset 0 0 0 1px var(--dsw-alias-brand-primary);font-weight:700}.NPLLIq_selectBtn{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_selectBtn:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_gearBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;flex:none;margin-left:6px;padding:0 2px;font-size:12px;line-height:1}.NPLLIq_gearBtn:hover{color:var(--dsw-alias-label-primary)}.NPLLIq_fullNotice{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;margin:6px 12px;padding:8px 10px;font-size:12px}.NPLLIq_upgradeBanner{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-divider-1,#0000001a);border-radius:8px;margin:6px 12px;padding:10px 30px 10px 10px;font-size:12px;position:relative}@keyframes NPLLIq_upgradeBannerFlash{0%,to{border-color:var(--dsw-alias-divider-1,#0000001a);box-shadow:0 0 #f0a02000}50%{border-color:var(--dsw-alias-status-warning-foreground,#b56900);box-shadow:0 0 0 5px #f0a02073}}.NPLLIq_upgradeBannerFlash{border-color:var(--dsw-alias-status-warning-foreground,#b56900);animation:.6s ease-in-out 4 NPLLIq_upgradeBannerFlash}.NPLLIq_upgradeBannerText{margin-bottom:6px;font-weight:600}.NPLLIq_upgradeCommands{flex-direction:column;gap:4px;display:flex}.NPLLIq_upgradeCommandRow{align-items:center;gap:6px;display:flex}.NPLLIq_upgradeCommand{background:var(--dsw-alias-bg-layer-2,#0000000a);white-space:nowrap;border-radius:4px;flex:1;min-width:0;padding:4px 6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow-x:auto}.NPLLIq_copyBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;flex:none;padding:3px 8px;font-size:11px;transition:transform 80ms,background .12s,color .12s}.NPLLIq_copyBtn:hover{background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_copyBtn:active{transform:scale(.92)}.NPLLIq_copyBtnDone{background:var(--dsw-alias-status-success-background,#14963c1f);border-color:var(--dsw-alias-status-success-foreground,#0a7d32);color:var(--dsw-alias-status-success-foreground,#0a7d32);font-weight:600}.NPLLIq_upgradeHint{color:var(--dsw-alias-label-secondary);margin-top:6px;font-size:11px}.NPLLIq_upgradeClose{width:20px;height:20px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;font-size:16px;line-height:1;position:absolute;top:4px;right:6px}.NPLLIq_upgradeClose:hover{background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary)}.NPLLIq_checkUpdatesRow{justify-content:space-between;align-items:center;gap:12px;display:flex}.NPLLIq_checkUpdatesLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.NPLLIq_checkUpdatesLabel input{margin:0}.NPLLIq_checkUpdatesLabel input[type=range]{min-width:90px;accent-color:var(--dsw-alias-brand-primary);flex:1}.NPLLIq_checkUpdatesLabel select{background:var(--dsw-alias-bg-layer-2,#0000000a);min-width:90px;color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-divider-1,#00000026);border-radius:4px;flex:1;padding:2px 4px;font-size:12px}.NPLLIq_checkBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-align:center;border-radius:4px;flex:none;min-width:72px;padding:4px 10px;font-size:12px}.NPLLIq_checkBtn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_checkBtn:disabled{opacity:.6;cursor:default}.NPLLIq_checkErrorHint{color:var(--dsw-alias-state-error-primary,#c00);background:var(--dsw-alias-state-error-background,#ff00000f);border:1px solid var(--dsw-alias-state-error-border,#b4000040);word-break:break-all;border-radius:6px;margin-top:8px;padding:6px 8px;font-size:11px;line-height:1.4}.NPLLIq_dialog{z-index:100;pointer-events:auto;box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;gap:12px;width:430px;max-width:94vw;max-height:80vh;padding:14px;display:flex;position:fixed;top:50%;left:50%;overflow-y:auto;transform:translate(-50%,-50%);box-shadow:0 8px 28px #00000040}.NPLLIq_dialogTabs{border-bottom:1px solid var(--dsw-alias-divider-1,#00000014);gap:2px;margin:-6px -14px 0;padding:0 14px;display:flex}.NPLLIq_dialogTab{appearance:none;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;padding:6px 12px 8px;font-size:12px}.NPLLIq_dialogTab:hover{color:var(--dsw-alias-label-primary)}.NPLLIq_dialogTabActive{color:var(--dsw-alias-brand-primary);border-bottom-color:var(--dsw-alias-brand-primary);font-weight:600}.NPLLIq_settingsDialog{height:min(560px,80vh)}.NPLLIq_dialogTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.NPLLIq_dialogSection{flex-direction:column;gap:6px;display:flex}.NPLLIq_dialogLabel{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500}.NPLLIq_monitorHint{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_dialogHint{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:11px;line-height:1.4}.NPLLIq_monitorList{flex-direction:column;gap:2px;max-height:180px;display:flex;overflow-y:auto}.NPLLIq_monitorRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:2px 4px;font-size:12px;display:flex}.NPLLIq_monitorRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_monitorName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.NPLLIq_radioRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:3px 4px;font-size:12px;display:flex}.NPLLIq_radioRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_dialogActions{justify-content:flex-end;gap:6px;display:flex}.NPLLIq_dialogBtn,.NPLLIq_dialogBtnPrimary{border:1px solid var(--dsw-alias-border-l1);cursor:pointer;border-radius:6px;padding:4px 12px;font-size:11px}.NPLLIq_dialogBtn{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.NPLLIq_dialogBtnPrimary{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay)}.NPLLIq_dialogHeader{z-index:2;background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:center;gap:8px;margin:-14px -14px 0;padding:14px 14px 8px;display:flex;position:sticky;top:-14px}.NPLLIq_dialogHeader:active{cursor:grabbing}.NPLLIq_dialogClose{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;font-size:18px;line-height:1;display:flex}.NPLLIq_dialogClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_resetRow{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.NPLLIq_resetSelect{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:6px;padding:3px 6px;font-size:12px}.NPLLIq_logTable{border-collapse:collapse;width:100%;font-size:11px}.NPLLIq_logTable th{text-align:left;color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);padding:4px 6px;font-weight:500}.NPLLIq_logTable td{color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);font-variant-numeric:tabular-nums;padding:4px 6px}.NPLLIq_logDayCol{white-space:nowrap;color:var(--dsw-alias-label-secondary)}.NPLLIq_logModelCol{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.NPLLIq_logUsedCol{text-align:right}.NPLLIq_logDialog{gap:0;width:380px;max-height:min(72vh,480px);padding:0}.NPLLIq_logHeader{background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;flex:none;justify-content:space-between;align-items:center;gap:8px;padding:14px 14px 8px;display:flex}.NPLLIq_logHeader:active{cursor:grabbing}.NPLLIq_logScroll{flex:1;min-height:0;padding:0 14px 14px;overflow:auto}.NPLLIq_logToolbar{flex:1;align-items:center;gap:6px;display:flex}.NPLLIq_logSearch{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);min-width:0;color:var(--dsw-alias-label-primary);border-radius:4px;flex:1;padding:3px 6px;font-size:11px}.NPLLIq_logSearch:focus{border-color:var(--dsw-alias-accent-1,#4a9eff);outline:none}.NPLLIq_logClearBtn{border:1px solid var(--dsw-alias-state-error-border,#b400004d);color:var(--dsw-alias-state-error-primary,#c00);cursor:pointer;white-space:nowrap;background:0 0;border-radius:4px;flex:none;padding:3px 8px;font-size:11px}.NPLLIq_logClearBtn:hover{background:var(--dsw-alias-state-error-background,#ff00000f)}.NPLLIq_logPager{border-top:1px solid var(--dsw-alias-divider-1,#00000014);background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-secondary);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:6px 14px;font-size:11px;display:flex}.NPLLIq_logPagerInfo{white-space:nowrap}.NPLLIq_logPagerControls{align-items:center;gap:4px;display:flex}.NPLLIq_logPagerBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);width:24px;height:24px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;font-size:13px;line-height:1}.NPLLIq_logPagerBtn:disabled{opacity:.4;cursor:default}.NPLLIq_logPagerPages{text-align:center;min-width:50px;color:var(--dsw-alias-label-secondary)}.NPLLIq_logPageSizeSelect{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;padding:2px 4px;font-size:11px}.NPLLIq_dialogOverlay{z-index:100001;pointer-events:auto;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.NPLLIq_logClearDialog{gap:0;width:320px;padding:16px}.NPLLIq_dialogBody{color:var(--dsw-alias-label-secondary);margin:6px 0 10px;font-size:12px;line-height:1.5}.NPLLIq_logClearDate{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);width:100%;color:var(--dsw-alias-label-primary);border-radius:6px;margin-bottom:12px;padding:6px 8px;font-size:13px}.NPLLIq_logClearDate:focus{border-color:var(--dsw-alias-accent-1,#4a9eff);outline:none}";
246
+ const css = ".NPLLIq_panel{box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);pointer-events:auto;z-index:40;opacity:1;border-radius:12px;flex-direction:column;width:320px;max-height:min(72vh,560px);transition:opacity .18s;display:flex;position:fixed;top:56px;right:12px;overflow:hidden;box-shadow:0 8px 28px #0000002e}.NPLLIq_panelDim{opacity:.55}.NPLLIq_panelDimStrong{opacity:.3}.NPLLIq_header{border-bottom:1px solid var(--dsw-alias-border-l1);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:flex-start;gap:8px;padding:10px 12px;display:flex}.NPLLIq_header:active{cursor:grabbing}.NPLLIq_headerText{flex-direction:column;gap:2px;min-width:0;display:flex}.NPLLIq_title{color:var(--dsw-alias-label-primary);align-items:baseline;gap:6px;font-size:13px;font-weight:600;display:flex}.NPLLIq_version{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:10px;font-weight:500}.NPLLIq_versionNew{color:var(--dsw-alias-status-warning-foreground,#b56900);background:var(--dsw-alias-status-warning-background,#fff6d7);cursor:default;border-radius:4px;padding:1px 6px}.NPLLIq_subtitle{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_notesBar{background:var(--dsw-alias-bg-layer-2);flex-wrap:wrap;align-items:center;gap:4px;padding:4px 12px 0;display:flex}.NPLLIq_noteChip{width:20px;height:20px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:pointer;border-radius:5px;flex:none;justify-content:center;align-items:center;padding:0;font-size:11px;line-height:1;transition:color .12s,border-color .12s;display:inline-flex}.NPLLIq_noteChip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2)}.NPLLIq_noteChipOpen{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.NPLLIq_noteNew{width:22px;height:20px;color:var(--dsw-alias-label-secondary);border:1px dashed var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:5px;flex:none;justify-content:center;align-items:center;padding:0;transition:color .12s,border-color .12s;display:inline-flex;position:relative}.NPLLIq_noteNew:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.NPLLIq_noteNew:disabled{opacity:.4;cursor:not-allowed}.NPLLIq_noteNewPlus{color:inherit;font-size:10px;font-weight:700;line-height:1;position:absolute;bottom:-2px;right:-1px}.NPLLIq_noteWin{z-index:60;pointer-events:auto;box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;display:flex;position:fixed;overflow:hidden;box-shadow:0 8px 24px #00000038}.NPLLIq_noteWinCollapsed{height:auto!important}.NPLLIq_noteWinBar{background:var(--dsw-alias-bg-layer-1);border-bottom:1px solid var(--dsw-alias-border-l1);cursor:grab;user-select:none;flex:none;align-items:center;gap:4px;padding:4px 6px;display:flex}.NPLLIq_noteWinBar:active{cursor:grabbing}.NPLLIq_noteWinGripBar{color:var(--dsw-alias-label-secondary);opacity:.7;flex:none;font-size:10px}.NPLLIq_noteWinTitleText{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;padding:1px 2px;font-size:11px;font-weight:600;overflow:hidden}.NPLLIq_noteWinTitle{min-width:0;color:var(--dsw-alias-label-primary);background:0 0;border:1px solid #0000;border-radius:4px;outline:none;flex:1;padding:1px 2px;font-size:11px;font-weight:600}.NPLLIq_noteWinTitle:hover{border-color:var(--dsw-alias-border-l1)}.NPLLIq_noteWinTitle:focus{background:var(--dsw-alias-bg-overlay);border-color:var(--dsw-alias-brand-primary)}.NPLLIq_noteWinBtn{width:18px;height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;font-size:12px;line-height:1;display:inline-flex}.NPLLIq_noteWinBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.NPLLIq_noteWinText{box-sizing:border-box;width:100%;min-height:0;color:var(--dsw-alias-label-primary);resize:none;background:0 0;border:none;outline:none;flex:1;padding:6px 8px;font-family:inherit;font-size:11px;line-height:1.5}.NPLLIq_noteWinResize{cursor:nwse-resize;background:linear-gradient(135deg, transparent 0 45%, var(--dsw-alias-border-l2) 45% 55%, transparent 55% 70%, var(--dsw-alias-border-l2) 70% 80%, transparent 80%);width:14px;height:14px;position:absolute;bottom:0;right:0}.NPLLIq_noteMenuBackdrop{z-index:100;pointer-events:auto;position:fixed;inset:0}.NPLLIq_noteMenu{z-index:101;pointer-events:auto;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;min-width:96px;padding:4px;display:flex;position:fixed;box-shadow:0 8px 24px #0000003d}.NPLLIq_noteMenuItem{text-align:left;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:5px;padding:5px 8px;font-size:11px}.NPLLIq_noteMenuItem:hover{background:var(--dsw-alias-bg-layer-2)}.NPLLIq_collapse{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_collapse:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_body{padding:4px 0;overflow-y:auto}.NPLLIq_row{border-bottom:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:6px;padding:8px 12px;display:flex}.NPLLIq_rowHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.NPLLIq_rowDrag{cursor:grab;color:var(--dsw-alias-label-secondary);user-select:none;border-radius:4px;flex:none;margin-right:2px;padding:2px 3px;font-size:11px;line-height:1}.NPLLIq_rowDrag:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_rowDrag:active{cursor:grabbing}.NPLLIq_rowDragging{opacity:.45}.NPLLIq_rowDropTarget{box-shadow:inset 0 2px 0 var(--dsw-alias-brand-primary)}.NPLLIq_rowName{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;align-items:center;gap:6px;font-size:12px;font-weight:500;display:inline-flex;overflow:hidden}.NPLLIq_currentBadge{color:var(--dsw-alias-state-success-primary);border:1px solid var(--dsw-alias-state-success-primary);border-radius:8px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_rowMeta{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.NPLLIq_bar{background:var(--dsw-alias-bg-layer-1);border-radius:3px;height:6px;overflow:hidden}.NPLLIq_fillIdle{background:var(--dsw-alias-brand-primary);height:100%;transition:width .2s}.NPLLIq_fillWarn{background:var(--dsw-alias-state-warn-primary);height:100%;transition:width .2s}.NPLLIq_fillOver{background:var(--dsw-alias-state-error-primary);height:100%;transition:width .2s}.NPLLIq_controls{align-items:center;gap:6px;display:flex}.NPLLIq_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-width:0;color:var(--dsw-alias-label-primary);border-radius:6px;flex:1;padding:4px 8px;font-size:11px}.NPLLIq_input::placeholder{color:var(--dsw-alias-label-secondary)}.NPLLIq_save,.NPLLIq_switch{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px;font-size:11px}.NPLLIq_save:hover,.NPLLIq_switch:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}.NPLLIq_switch:disabled{opacity:.5;cursor:default}.NPLLIq_balanceToggle{margin-left:auto}.NPLLIq_balanceToggleOpen{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_balanceWin{z-index:1199;background:var(--dsw-alias-bg-layer-2);width:280px;color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);pointer-events:auto;border-radius:12px;flex-direction:column;display:flex;position:fixed;overflow:hidden;box-shadow:0 8px 24px #00000029}.NPLLIq_balanceWinHeader{border-bottom:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-3);user-select:none;align-items:center;gap:6px;padding:6px 8px 6px 12px;display:flex}.NPLLIq_balanceWinTitle{flex:1;font-size:12px;font-weight:600;line-height:1}.NPLLIq_balanceWinBody{font-variant-numeric:tabular-nums;padding:6px 10px 10px;font-size:11px}.NPLLIq_balanceRowHeader{border-bottom:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);grid-template-columns:1fr auto 20px;align-items:center;gap:6px;padding:3px 2px;font-weight:600;display:grid}.NPLLIq_balanceSortBtn{all:unset;cursor:pointer;color:inherit;border-radius:4px;padding:1px 2px;font-size:10px;font-weight:600}.NPLLIq_balanceSortBtn:hover{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_balanceRefreshAll{width:18px;height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:0;font-size:12px;line-height:1;display:inline-flex}.NPLLIq_balanceRefreshAll:hover:not(:disabled){color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_balanceRefreshAll:disabled{opacity:.4;cursor:not-allowed}.NPLLIq_balanceRow{grid-template-columns:1fr auto 20px;align-items:center;gap:6px;padding:3px 2px;display:grid}.NPLLIq_balanceRow+.NPLLIq_balanceRow{border-top:1px dashed var(--dsw-alias-border-l1)}.NPLLIq_balanceRowName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-weight:500;overflow:hidden}.NPLLIq_balanceRowValue{text-align:right;font-weight:600}.NPLLIq_balanceRowRefresh{width:18px;height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:0;font-size:12px;line-height:1;display:inline-flex}.NPLLIq_balanceRowRefresh:hover:not(:disabled){color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_balanceRowRefresh:disabled{opacity:.4;cursor:not-allowed}.NPLLIq_balanceValueOk{color:var(--dsw-alias-label-primary)}.NPLLIq_balanceValueLow{color:var(--dsw-alias-state-warn-primary)}.NPLLIq_balanceValueError{color:var(--dsw-alias-state-error-primary)}.NPLLIq_balanceStateHint{color:var(--dsw-alias-label-secondary);font-style:italic;font-weight:500}.NPLLIq_balanceStateError{color:var(--dsw-alias-state-error-primary);font-weight:500}.NPLLIq_balanceLastLine{border-top:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);margin-top:6px;padding-top:5px;font-size:10px}.NPLLIq_balanceBar{color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:center;gap:6px;padding:5px 12px;font-size:11px;display:flex}.NPLLIq_balanceBarText{text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:6px;min-width:0;font-weight:600;display:inline-flex;overflow:hidden}.NPLLIq_balanceBarBadge{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-1);border-radius:4px;flex:none;padding:0 4px;font-size:10px;font-weight:600}.NPLLIq_balanceBarDim{color:var(--dsw-alias-label-secondary);font-weight:500}.NPLLIq_balanceBarLow{color:var(--dsw-alias-state-warn-primary)}.NPLLIq_balanceBarError{color:var(--dsw-alias-state-error-primary)}.NPLLIq_balanceBarRefresh{width:18px;height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;font-size:12px;line-height:1;display:inline-flex}.NPLLIq_balanceBarRefresh:hover:not(:disabled){color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_balanceBarRefresh:disabled{opacity:.4;cursor:not-allowed}.NPLLIq_notice,.NPLLIq_noticeError{color:var(--dsw-alias-label-secondary);padding:10px 12px;font-size:12px}.NPLLIq_noticeError{color:var(--dsw-alias-state-error-primary)}.NPLLIq_tab{background:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay);pointer-events:auto;cursor:pointer;z-index:90;border-radius:10px;align-items:center;gap:6px;padding:9px 14px;font-size:12px;font-weight:600;display:inline-flex;position:fixed;top:56px;right:12px;box-shadow:0 6px 20px #00000040}.NPLLIq_tab:hover{filter:brightness(1.08)}.NPLLIq_tabIcon{font-size:12px;line-height:1}.NPLLIq_tabLabel{white-space:nowrap}.NPLLIq_headerActions{flex:none;align-items:center;gap:4px;display:flex}.NPLLIq_settingsBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;padding:2px 6px;font-size:11px}.NPLLIq_settingsBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_settingsBtnActive{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2,#0000000f);box-shadow:inset 0 0 0 1px var(--dsw-alias-brand-primary);font-weight:700}.NPLLIq_selectBtn{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:1px 6px;font-size:10px}.NPLLIq_selectBtn:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_gearBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;flex:none;margin-left:6px;padding:0 2px;font-size:12px;line-height:1}.NPLLIq_gearBtn:hover{color:var(--dsw-alias-label-primary)}.NPLLIq_fullNotice{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;margin:6px 12px;padding:8px 10px;font-size:12px}.NPLLIq_upgradeBanner{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-divider-1,#0000001a);border-radius:8px;margin:6px 12px;padding:10px 30px 10px 10px;font-size:12px;position:relative}@keyframes NPLLIq_upgradeBannerFlash{0%,to{border-color:var(--dsw-alias-divider-1,#0000001a);box-shadow:0 0 #f0a02000}50%{border-color:var(--dsw-alias-status-warning-foreground,#b56900);box-shadow:0 0 0 5px #f0a02073}}.NPLLIq_upgradeBannerFlash{border-color:var(--dsw-alias-status-warning-foreground,#b56900);animation:.6s ease-in-out 4 NPLLIq_upgradeBannerFlash}.NPLLIq_upgradeBannerText{margin-bottom:6px;font-weight:600}.NPLLIq_upgradeCommands{flex-direction:column;gap:4px;display:flex}.NPLLIq_upgradeCommandRow{align-items:center;gap:6px;display:flex}.NPLLIq_upgradeCommand{background:var(--dsw-alias-bg-layer-2,#0000000a);white-space:nowrap;border-radius:4px;flex:1;min-width:0;padding:4px 6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow-x:auto}.NPLLIq_copyBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;flex:none;padding:3px 8px;font-size:11px;transition:transform 80ms,background .12s,color .12s}.NPLLIq_copyBtn:hover{background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_copyBtn:active{transform:scale(.92)}.NPLLIq_copyBtnDone{background:var(--dsw-alias-status-success-background,#14963c1f);border-color:var(--dsw-alias-status-success-foreground,#0a7d32);color:var(--dsw-alias-status-success-foreground,#0a7d32);font-weight:600}.NPLLIq_upgradeHint{color:var(--dsw-alias-label-secondary);margin-top:6px;font-size:11px}.NPLLIq_upgradeClose{width:20px;height:20px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;font-size:16px;line-height:1;position:absolute;top:4px;right:6px}.NPLLIq_upgradeClose:hover{background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary)}.NPLLIq_checkUpdatesRow{justify-content:space-between;align-items:center;gap:12px;display:flex}.NPLLIq_checkUpdatesLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.NPLLIq_checkUpdatesLabel input{margin:0}.NPLLIq_checkUpdatesLabel input[type=range]{min-width:90px;accent-color:var(--dsw-alias-brand-primary);flex:1}.NPLLIq_checkUpdatesLabel select{background:var(--dsw-alias-bg-layer-2,#0000000a);min-width:90px;color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-divider-1,#00000026);border-radius:4px;flex:1;padding:2px 4px;font-size:12px}.NPLLIq_checkBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-align:center;border-radius:4px;flex:none;min-width:72px;padding:4px 10px;font-size:12px}.NPLLIq_checkBtn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-3,#00000014)}.NPLLIq_checkBtn:disabled{opacity:.6;cursor:default}.NPLLIq_checkErrorHint{color:var(--dsw-alias-state-error-primary,#c00);background:var(--dsw-alias-state-error-background,#ff00000f);border:1px solid var(--dsw-alias-state-error-border,#b4000040);word-break:break-all;border-radius:6px;margin-top:8px;padding:6px 8px;font-size:11px;line-height:1.4}.NPLLIq_dialog{z-index:100;pointer-events:auto;box-sizing:border-box;background:var(--dsw-alias-bg-overlay);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;gap:12px;width:430px;max-width:94vw;max-height:80vh;padding:14px;display:flex;position:fixed;top:50%;left:50%;overflow-y:auto;transform:translate(-50%,-50%);box-shadow:0 8px 28px #00000040}.NPLLIq_dialogTabs{border-bottom:1px solid var(--dsw-alias-divider-1,#00000014);gap:2px;margin:-6px -14px 0;padding:0 14px;display:flex}.NPLLIq_dialogTab{appearance:none;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;padding:6px 12px 8px;font-size:12px}.NPLLIq_dialogTab:hover{color:var(--dsw-alias-label-primary)}.NPLLIq_dialogTabActive{color:var(--dsw-alias-brand-primary);border-bottom-color:var(--dsw-alias-brand-primary);font-weight:600}.NPLLIq_settingsDialog{height:min(560px,80vh)}.NPLLIq_dialogTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.NPLLIq_dialogSection{flex-direction:column;gap:6px;display:flex}.NPLLIq_dialogLabel{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500}.NPLLIq_monitorHint{color:var(--dsw-alias-label-secondary);font-size:11px}.NPLLIq_dialogHint{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:11px;line-height:1.4}.NPLLIq_monitorList{flex-direction:column;gap:2px;max-height:180px;display:flex;overflow-y:auto}.NPLLIq_monitorRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:2px 4px;font-size:12px;display:flex}.NPLLIq_monitorRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_monitorName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.NPLLIq_radioRow{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:6px;padding:3px 4px;font-size:12px;display:flex}.NPLLIq_radioRow:hover{background:var(--dsw-alias-bg-layer-1)}.NPLLIq_dialogActions{justify-content:flex-end;gap:6px;display:flex}.NPLLIq_dialogBtn,.NPLLIq_dialogBtnPrimary{border:1px solid var(--dsw-alias-border-l1);cursor:pointer;border-radius:6px;padding:4px 12px;font-size:11px}.NPLLIq_dialogBtn{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.NPLLIq_dialogBtnPrimary{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-overlay)}.NPLLIq_dialogHeader{z-index:2;background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;justify-content:space-between;align-items:center;gap:8px;margin:-14px -14px 0;padding:14px 14px 8px;display:flex;position:sticky;top:-14px}.NPLLIq_dialogHeader:active{cursor:grabbing}.NPLLIq_dialogClose{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;font-size:18px;line-height:1;display:flex}.NPLLIq_dialogClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.NPLLIq_resetRow{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.NPLLIq_resetSelect{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:6px;padding:3px 6px;font-size:12px}.NPLLIq_logTable{border-collapse:collapse;width:100%;font-size:11px}.NPLLIq_logTable th{text-align:left;color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);padding:4px 6px;font-weight:500}.NPLLIq_logTable td{color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);font-variant-numeric:tabular-nums;padding:4px 6px}.NPLLIq_logDayCol{white-space:nowrap;color:var(--dsw-alias-label-secondary)}.NPLLIq_logModelCol{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.NPLLIq_logUsedCol{text-align:right}.NPLLIq_logDialog{gap:0;width:380px;max-height:min(72vh,480px);padding:0}.NPLLIq_logHeader{background:var(--dsw-alias-bg-overlay);cursor:grab;user-select:none;touch-action:none;flex:none;justify-content:space-between;align-items:center;gap:8px;padding:14px 14px 8px;display:flex}.NPLLIq_logHeader:active{cursor:grabbing}.NPLLIq_logScroll{flex:1;min-height:0;padding:0 14px 14px;overflow:auto}.NPLLIq_logToolbar{flex:1;align-items:center;gap:6px;display:flex}.NPLLIq_logSearch{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);min-width:0;color:var(--dsw-alias-label-primary);border-radius:4px;flex:1;padding:3px 6px;font-size:11px}.NPLLIq_logSearch:focus{border-color:var(--dsw-alias-accent-1,#4a9eff);outline:none}.NPLLIq_logClearBtn{border:1px solid var(--dsw-alias-state-error-border,#b400004d);color:var(--dsw-alias-state-error-primary,#c00);cursor:pointer;white-space:nowrap;background:0 0;border-radius:4px;flex:none;padding:3px 8px;font-size:11px}.NPLLIq_logClearBtn:hover{background:var(--dsw-alias-state-error-background,#ff00000f)}.NPLLIq_logPager{border-top:1px solid var(--dsw-alias-divider-1,#00000014);background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-secondary);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:6px 14px;font-size:11px;display:flex}.NPLLIq_logPagerInfo{white-space:nowrap}.NPLLIq_logPagerControls{align-items:center;gap:4px;display:flex}.NPLLIq_logPagerBtn{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);width:24px;height:24px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;font-size:13px;line-height:1}.NPLLIq_logPagerBtn:disabled{opacity:.4;cursor:default}.NPLLIq_logPagerPages{text-align:center;min-width:50px;color:var(--dsw-alias-label-secondary)}.NPLLIq_logPageSizeSelect{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:4px;padding:2px 4px;font-size:11px}.NPLLIq_dialogOverlay{z-index:100001;pointer-events:auto;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.NPLLIq_logClearDialog{gap:0;width:320px;padding:16px}.NPLLIq_dialogBody{color:var(--dsw-alias-label-secondary);margin:6px 0 10px;font-size:12px;line-height:1.5}.NPLLIq_logClearDate{border:1px solid var(--dsw-alias-divider-1,#00000026);background:var(--dsw-alias-bg-layer-1,#0000000a);width:100%;color:var(--dsw-alias-label-primary);border-radius:6px;margin-bottom:12px;padding:6px 8px;font-size:13px}.NPLLIq_logClearDate:focus{border-color:var(--dsw-alias-accent-1,#4a9eff);outline:none}";
229
247
  const tagId = "@jxgame2020/dsh-token-quota/TokenQuotaPanel.module.css";
230
248
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
231
249
  const tag = document.createElement("style");
@@ -235,116 +253,142 @@ window.__ModuleLoader__.load({
235
253
  document.head.appendChild(tag);
236
254
  }
237
255
  var TokenQuotaPanel_module_css_default = {
238
- "logPager": "NPLLIq_logPager",
239
- "noteWinTitleText": "NPLLIq_noteWinTitleText",
240
- "controls": "NPLLIq_controls",
241
- "logPagerBtn": "NPLLIq_logPagerBtn",
242
- "logPagerPages": "NPLLIq_logPagerPages",
243
- "selectBtn": "NPLLIq_selectBtn",
256
+ "tabLabel": "NPLLIq_tabLabel",
244
257
  "logUsedCol": "NPLLIq_logUsedCol",
245
- "logPageSizeSelect": "NPLLIq_logPageSizeSelect",
246
- "fillWarn": "NPLLIq_fillWarn",
247
- "dialogOverlay": "NPLLIq_dialogOverlay",
248
- "dialogBody": "NPLLIq_dialogBody",
249
- "switch": "NPLLIq_switch",
250
- "fillOver": "NPLLIq_fillOver",
251
- "upgradeClose": "NPLLIq_upgradeClose",
252
- "dialogBtn": "NPLLIq_dialogBtn",
253
- "notice": "NPLLIq_notice",
254
- "logClearDate": "NPLLIq_logClearDate",
258
+ "logToolbar": "NPLLIq_logToolbar",
255
259
  "copyBtn": "NPLLIq_copyBtn",
256
- "rowHeader": "NPLLIq_rowHeader",
257
- "logClearBtn": "NPLLIq_logClearBtn",
258
- "upgradeCommandRow": "NPLLIq_upgradeCommandRow",
259
- "upgradeCommands": "NPLLIq_upgradeCommands",
260
- "monitorHint": "NPLLIq_monitorHint",
261
- "noteNew": "NPLLIq_noteNew",
262
- "checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
263
- "logClearDialog": "NPLLIq_logClearDialog",
264
- "logPagerInfo": "NPLLIq_logPagerInfo",
260
+ "checkBtn": "NPLLIq_checkBtn",
261
+ "balanceRowHeader": "NPLLIq_balanceRowHeader",
262
+ "dialogLabel": "NPLLIq_dialogLabel",
263
+ "dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
264
+ "noteWinBar": "NPLLIq_noteWinBar",
265
+ "dialogBody": "NPLLIq_dialogBody",
266
+ "noteWinText": "NPLLIq_noteWinText",
267
+ "dialogHeader": "NPLLIq_dialogHeader",
268
+ "balanceBarError": "NPLLIq_balanceBarError",
265
269
  "headerActions": "NPLLIq_headerActions",
266
- "rowDropTarget": "NPLLIq_rowDropTarget",
267
- "subtitle": "NPLLIq_subtitle",
268
- "tab": "NPLLIq_tab",
269
- "fillIdle": "NPLLIq_fillIdle",
270
- "noteWinTitle": "NPLLIq_noteWinTitle",
271
- "rowDragging": "NPLLIq_rowDragging",
272
- "currentBadge": "NPLLIq_currentBadge",
273
- "noteNewPlus": "NPLLIq_noteNewPlus",
274
- "panel": "NPLLIq_panel",
275
- "header": "NPLLIq_header",
276
270
  "panelDim": "NPLLIq_panelDim",
277
- "noteWin": "NPLLIq_noteWin",
278
- "noteWinBar": "NPLLIq_noteWinBar",
271
+ "collapse": "NPLLIq_collapse",
272
+ "dialogTab": "NPLLIq_dialogTab",
273
+ "logPageSizeSelect": "NPLLIq_logPageSizeSelect",
279
274
  "rowMeta": "NPLLIq_rowMeta",
280
- "copyBtnDone": "NPLLIq_copyBtnDone",
281
- "rowName": "NPLLIq_rowName",
282
- "settingsBtnActive": "NPLLIq_settingsBtnActive",
283
- "dialog": "NPLLIq_dialog",
284
- "settingsDialog": "NPLLIq_settingsDialog",
285
- "dialogLabel": "NPLLIq_dialogLabel",
286
- "noteMenu": "NPLLIq_noteMenu",
287
- "noteChipOpen": "NPLLIq_noteChipOpen",
275
+ "upgradeCommandRow": "NPLLIq_upgradeCommandRow",
276
+ "notesBar": "NPLLIq_notesBar",
277
+ "currentBadge": "NPLLIq_currentBadge",
278
+ "balanceRow": "NPLLIq_balanceRow",
279
+ "settingsBtn": "NPLLIq_settingsBtn",
280
+ "upgradeCommand": "NPLLIq_upgradeCommand",
281
+ "resetSelect": "NPLLIq_resetSelect",
282
+ "balanceRefreshAll": "NPLLIq_balanceRefreshAll",
283
+ "logClearDate": "NPLLIq_logClearDate",
284
+ "noteChip": "NPLLIq_noteChip",
285
+ "subtitle": "NPLLIq_subtitle",
288
286
  "monitorRow": "NPLLIq_monitorRow",
289
- "collapse": "NPLLIq_collapse",
290
- "monitorName": "NPLLIq_monitorName",
291
- "row": "NPLLIq_row",
292
- "dialogTitle": "NPLLIq_dialogTitle",
293
- "dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
287
+ "balanceValueOk": "NPLLIq_balanceValueOk",
288
+ "logPagerBtn": "NPLLIq_logPagerBtn",
289
+ "logModelCol": "NPLLIq_logModelCol",
290
+ "monitorHint": "NPLLIq_monitorHint",
291
+ "dialogOverlay": "NPLLIq_dialogOverlay",
292
+ "noteWinTitleText": "NPLLIq_noteWinTitleText",
293
+ "notice": "NPLLIq_notice",
294
+ "rowDropTarget": "NPLLIq_rowDropTarget",
295
+ "balanceSortBtn": "NPLLIq_balanceSortBtn",
296
+ "balanceRowValue": "NPLLIq_balanceRowValue",
297
+ "balanceWin": "NPLLIq_balanceWin",
298
+ "save": "NPLLIq_save",
299
+ "balanceBarRefresh": "NPLLIq_balanceBarRefresh",
300
+ "upgradeBannerText": "NPLLIq_upgradeBannerText",
301
+ "dialogTabActive": "NPLLIq_dialogTabActive",
302
+ "settingsDialog": "NPLLIq_settingsDialog",
303
+ "upgradeCommands": "NPLLIq_upgradeCommands",
304
+ "tabIcon": "NPLLIq_tabIcon",
305
+ "logClearDialog": "NPLLIq_logClearDialog",
294
306
  "noteWinResize": "NPLLIq_noteWinResize",
295
- "resetSelect": "NPLLIq_resetSelect",
296
- "logTable": "NPLLIq_logTable",
297
- "settingsBtn": "NPLLIq_settingsBtn",
307
+ "panelDimStrong": "NPLLIq_panelDimStrong",
298
308
  "versionNew": "NPLLIq_versionNew",
299
- "bar": "NPLLIq_bar",
300
- "input": "NPLLIq_input",
301
- "gearBtn": "NPLLIq_gearBtn",
302
- "save": "NPLLIq_save",
303
- "upgradeBannerFlash": "NPLLIq_upgradeBannerFlash",
304
- "upgradeCommand": "NPLLIq_upgradeCommand",
309
+ "noteWinCollapsed": "NPLLIq_noteWinCollapsed",
310
+ "logPagerPages": "NPLLIq_logPagerPages",
311
+ "noteWin": "NPLLIq_noteWin",
312
+ "switch": "NPLLIq_switch",
313
+ "upgradeHint": "NPLLIq_upgradeHint",
314
+ "body": "NPLLIq_body",
315
+ "copyBtnDone": "NPLLIq_copyBtnDone",
316
+ "dialogClose": "NPLLIq_dialogClose",
305
317
  "rowDrag": "NPLLIq_rowDrag",
306
- "headerText": "NPLLIq_headerText",
307
- "checkUpdatesRow": "NPLLIq_checkUpdatesRow",
308
- "checkBtn": "NPLLIq_checkBtn",
309
- "checkErrorHint": "NPLLIq_checkErrorHint",
318
+ "fillOver": "NPLLIq_fillOver",
310
319
  "noteWinGripBar": "NPLLIq_noteWinGripBar",
311
- "noteWinBtn": "NPLLIq_noteWinBtn",
312
- "dialogTab": "NPLLIq_dialogTab",
313
- "upgradeBanner": "NPLLIq_upgradeBanner",
314
- "dialogTabActive": "NPLLIq_dialogTabActive",
315
- "dialogHeader": "NPLLIq_dialogHeader",
316
320
  "resetRow": "NPLLIq_resetRow",
317
- "upgradeBannerText": "NPLLIq_upgradeBannerText",
318
- "logDayCol": "NPLLIq_logDayCol",
319
- "logHeader": "NPLLIq_logHeader",
320
- "noteMenuBackdrop": "NPLLIq_noteMenuBackdrop",
321
- "notesBar": "NPLLIq_notesBar",
322
- "dialogHint": "NPLLIq_dialogHint",
323
- "monitorList": "NPLLIq_monitorList",
324
- "logModelCol": "NPLLIq_logModelCol",
325
- "dialogSection": "NPLLIq_dialogSection",
326
- "logScroll": "NPLLIq_logScroll",
321
+ "logSearch": "NPLLIq_logSearch",
322
+ "logTable": "NPLLIq_logTable",
323
+ "dialogTabs": "NPLLIq_dialogTabs",
324
+ "headerText": "NPLLIq_headerText",
327
325
  "logPagerControls": "NPLLIq_logPagerControls",
328
- "panelDimStrong": "NPLLIq_panelDimStrong",
326
+ "noteNewPlus": "NPLLIq_noteNewPlus",
327
+ "noteWinBtn": "NPLLIq_noteWinBtn",
328
+ "input": "NPLLIq_input",
329
+ "logClearBtn": "NPLLIq_logClearBtn",
330
+ "balanceLastLine": "NPLLIq_balanceLastLine",
329
331
  "noticeError": "NPLLIq_noticeError",
332
+ "gearBtn": "NPLLIq_gearBtn",
333
+ "monitorName": "NPLLIq_monitorName",
334
+ "logPager": "NPLLIq_logPager",
330
335
  "title": "NPLLIq_title",
331
- "body": "NPLLIq_body",
332
- "dialogTabs": "NPLLIq_dialogTabs",
333
- "tabLabel": "NPLLIq_tabLabel",
334
- "radioRow": "NPLLIq_radioRow",
335
- "dialogActions": "NPLLIq_dialogActions",
336
+ "noteWinTitle": "NPLLIq_noteWinTitle",
337
+ "rowDragging": "NPLLIq_rowDragging",
338
+ "rowHeader": "NPLLIq_rowHeader",
339
+ "upgradeBanner": "NPLLIq_upgradeBanner",
340
+ "noteMenuBackdrop": "NPLLIq_noteMenuBackdrop",
341
+ "dialogHint": "NPLLIq_dialogHint",
336
342
  "version": "NPLLIq_version",
343
+ "balanceStateHint": "NPLLIq_balanceStateHint",
344
+ "dialogTitle": "NPLLIq_dialogTitle",
337
345
  "logDialog": "NPLLIq_logDialog",
338
- "logToolbar": "NPLLIq_logToolbar",
346
+ "balanceWinTitle": "NPLLIq_balanceWinTitle",
347
+ "balanceWinHeader": "NPLLIq_balanceWinHeader",
348
+ "settingsBtnActive": "NPLLIq_settingsBtnActive",
349
+ "header": "NPLLIq_header",
350
+ "logScroll": "NPLLIq_logScroll",
351
+ "controls": "NPLLIq_controls",
352
+ "balanceBarBadge": "NPLLIq_balanceBarBadge",
353
+ "row": "NPLLIq_row",
354
+ "balanceValueError": "NPLLIq_balanceValueError",
355
+ "dialogSection": "NPLLIq_dialogSection",
356
+ "balanceRowRefresh": "NPLLIq_balanceRowRefresh",
357
+ "logDayCol": "NPLLIq_logDayCol",
358
+ "noteMenu": "NPLLIq_noteMenu",
359
+ "balanceRowName": "NPLLIq_balanceRowName",
360
+ "balanceValueLow": "NPLLIq_balanceValueLow",
361
+ "balanceStateError": "NPLLIq_balanceStateError",
362
+ "dialogBtn": "NPLLIq_dialogBtn",
363
+ "noteChipOpen": "NPLLIq_noteChipOpen",
364
+ "logPagerInfo": "NPLLIq_logPagerInfo",
365
+ "rowName": "NPLLIq_rowName",
366
+ "selectBtn": "NPLLIq_selectBtn",
367
+ "balanceBarLow": "NPLLIq_balanceBarLow",
368
+ "balanceToggle": "NPLLIq_balanceToggle",
369
+ "balanceBarDim": "NPLLIq_balanceBarDim",
339
370
  "fullNotice": "NPLLIq_fullNotice",
340
- "upgradeHint": "NPLLIq_upgradeHint",
341
- "logSearch": "NPLLIq_logSearch",
342
- "noteChip": "NPLLIq_noteChip",
371
+ "tab": "NPLLIq_tab",
372
+ "logHeader": "NPLLIq_logHeader",
343
373
  "noteMenuItem": "NPLLIq_noteMenuItem",
344
- "noteWinText": "NPLLIq_noteWinText",
345
- "tabIcon": "NPLLIq_tabIcon",
346
- "noteWinCollapsed": "NPLLIq_noteWinCollapsed",
347
- "dialogClose": "NPLLIq_dialogClose"
374
+ "panel": "NPLLIq_panel",
375
+ "bar": "NPLLIq_bar",
376
+ "balanceToggleOpen": "NPLLIq_balanceToggleOpen",
377
+ "checkErrorHint": "NPLLIq_checkErrorHint",
378
+ "radioRow": "NPLLIq_radioRow",
379
+ "fillIdle": "NPLLIq_fillIdle",
380
+ "upgradeBannerFlash": "NPLLIq_upgradeBannerFlash",
381
+ "noteNew": "NPLLIq_noteNew",
382
+ "fillWarn": "NPLLIq_fillWarn",
383
+ "balanceWinBody": "NPLLIq_balanceWinBody",
384
+ "balanceBar": "NPLLIq_balanceBar",
385
+ "checkUpdatesRow": "NPLLIq_checkUpdatesRow",
386
+ "dialogActions": "NPLLIq_dialogActions",
387
+ "upgradeClose": "NPLLIq_upgradeClose",
388
+ "balanceBarText": "NPLLIq_balanceBarText",
389
+ "dialog": "NPLLIq_dialog",
390
+ "monitorList": "NPLLIq_monitorList",
391
+ "checkUpdatesLabel": "NPLLIq_checkUpdatesLabel"
348
392
  };
349
393
  //#endregion
350
394
  //#region lib/types/client/TokenQuotaPanel.js
@@ -449,6 +493,19 @@ window.__ModuleLoader__.load({
449
493
  if (trimmed === "") return "·";
450
494
  return Array.from(trimmed)[0] ?? "·";
451
495
  }
496
+ /** Currency symbol for the balance bar; a raw code is appended for unknowns. */
497
+ function currencySymbol(currency) {
498
+ if (currency === "CNY") return "¥";
499
+ if (currency === "USD") return "$";
500
+ if (currency === "EUR") return "€";
501
+ if (currency === "GBP") return "£";
502
+ return `${currency} `;
503
+ }
504
+ /** Short "Last: MM-dd HH:mm:ss" timestamp for the balance window footer. */
505
+ function formatBalanceTime(epochMs) {
506
+ const d = new Date(epochMs);
507
+ return `${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}`;
508
+ }
452
509
  /** Default geometry of a new note; later notes cascade so they don't stack. */
453
510
  const NOTE_DEFAULT_SIZE = 220;
454
511
  const NOTE_MIN_WIDTH = 150;
@@ -469,7 +526,7 @@ window.__ModuleLoader__.load({
469
526
  * @param props - composed slot props.
470
527
  * @returns the panel element tree.
471
528
  */
472
- function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, setModelOrder, saveNotes, setMusicEnabled, setMusicVolume, setMusicStyle, setMusicOnlyCurrentSession, useStore, actions, useSessions }) {
529
+ function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, setModelOrder, saveNotes, setBalanceSettings, refreshBalance, setMusicEnabled, setMusicVolume, setMusicStyle, setMusicOnlyCurrentSession, useStore, actions, useSessions }) {
473
530
  const [collapsed, setCollapsed] = (0, react.useState)(false);
474
531
  const [drafts, setDrafts] = (0, react.useState)({});
475
532
  const [editingKey, setEditingKey] = (0, react.useState)(null);
@@ -486,6 +543,18 @@ window.__ModuleLoader__.load({
486
543
  const notesPending = (0, react.useRef)(null);
487
544
  const [noteMenu, setNoteMenu] = (0, react.useState)(null);
488
545
  const [activeNote, setActiveNote] = (0, react.useState)(null);
546
+ const [balanceOpen, setBalanceOpen] = (0, react.useState)(false);
547
+ const [balancePos, setBalancePos] = (0, react.useState)(null);
548
+ const [balanceSort, setBalanceSort] = (0, react.useState)(null);
549
+ const [refreshingAll, setRefreshingAll] = (0, react.useState)(false);
550
+ const [refreshingProvider, setRefreshingProvider] = (0, react.useState)(null);
551
+ /** Which model row is currently hovered (model key), for quota/balance flip. */
552
+ const [hoveredRowKey, setHoveredRowKey] = (0, react.useState)(null);
553
+ /** Flip state in rowMeta: 0 = shows quota text, 1 = shows balance text. Cycles every 3s while hovering. */
554
+ const [metaToggle, setMetaToggle] = (0, react.useState)(0);
555
+ /** Last time we auto-refreshed a stale balance on row-hover (per-provider epoch ms),
556
+ * throttles refreshes to at most one per 60s per provider. */
557
+ const lastRowStaleRefreshRef = (0, react.useRef)({});
489
558
  const [editingTitle, setEditingTitle] = (0, react.useState)(null);
490
559
  const [titleDraft, setTitleDraft] = (0, react.useState)("");
491
560
  (0, react.useEffect)(() => {
@@ -622,6 +691,9 @@ window.__ModuleLoader__.load({
622
691
  const checkUpdates = useStore((s) => s.checkUpdates);
623
692
  const dimWhenIdle = useStore((s) => s.dimWhenIdle);
624
693
  const order = useStore((s) => s.order);
694
+ const balances = useStore((s) => s.balances);
695
+ const balanceEnabled = useStore((s) => s.balanceEnabled);
696
+ const balancePollMinutes = useStore((s) => s.balancePollMinutes);
625
697
  const musicEnabled = useStore((s) => s.musicEnabled);
626
698
  const musicVolume = useStore((s) => s.musicVolume);
627
699
  const musicStyle = useStore((s) => s.musicStyle);
@@ -792,6 +864,169 @@ window.__ModuleLoader__.load({
792
864
  setLimit(row.key, parsed);
793
865
  };
794
866
  const visibleRows = rows.filter((row) => isMonitoredKey(row.key) || row.current);
867
+ const providers = (0, react.useMemo)(() => {
868
+ const seen = /* @__PURE__ */ new Set();
869
+ const list = [];
870
+ for (const group of groups) if (!seen.has(group.id)) {
871
+ seen.add(group.id);
872
+ list.push(group.id);
873
+ }
874
+ if (list.length === 0) list.push("deepseek");
875
+ return list;
876
+ }, [groups]);
877
+ const balanceByProvider = (0, react.useMemo)(() => {
878
+ const map = /* @__PURE__ */ new Map();
879
+ for (const provider of providers) {
880
+ const bal = balances.find((b) => b.provider === provider);
881
+ if (bal !== void 0) map.set(provider, bal);
882
+ else if (TOKEN_QUOTA_BALANCE_PROVIDERS.includes(provider)) map.set(provider, "loading");
883
+ else map.set(provider, "unsupported");
884
+ }
885
+ return map;
886
+ }, [providers, balances]);
887
+ const sortedProviders = (0, react.useMemo)(() => {
888
+ const toNum = (entry) => {
889
+ if (typeof entry === "string") return -1;
890
+ if (entry.status === "ok") return entry.total;
891
+ return -1;
892
+ };
893
+ const statusOrder = (entry) => {
894
+ if (entry === "loading") return 0;
895
+ if (entry === "unsupported") return 3;
896
+ if (entry.status === "ok" && entry.isAvailable && entry.total >= 5) return 0;
897
+ if (entry.status === "ok" && entry.total < 5) return 1;
898
+ if (entry.status === "unconfigured") return 3;
899
+ if (entry.status === "error") return 2;
900
+ return 0;
901
+ };
902
+ return [...providers].sort((left, right) => {
903
+ const lEntry = balanceByProvider.get(left);
904
+ const rEntry = balanceByProvider.get(right);
905
+ if (lEntry === void 0 || rEntry === void 0) return 0;
906
+ const ls = statusOrder(lEntry), rs = statusOrder(rEntry);
907
+ if (ls !== rs) return ls - rs;
908
+ if (balanceSort === null) return left.localeCompare(right);
909
+ if (balanceSort.key === "provider") return left.localeCompare(right) * balanceSort.dir;
910
+ return (toNum(lEntry) - toNum(rEntry)) * balanceSort.dir;
911
+ });
912
+ }, [
913
+ providers,
914
+ balanceByProvider,
915
+ balanceSort
916
+ ]);
917
+ /** Most recent successful fetch time (epoch ms), for the "Last: ..." line. */
918
+ const lastFetchedAt = (0, react.useMemo)(() => {
919
+ let latest = 0;
920
+ for (const bal of balances) if (bal.status === "ok") latest = Math.max(latest, bal.fetchedAt);
921
+ return latest;
922
+ }, [balances]);
923
+ /** Format a balance value for display, or a state label. */
924
+ const renderProviderCell = (provider) => {
925
+ const entry = balanceByProvider.get(provider);
926
+ if (entry === void 0) return { text: "" };
927
+ if (entry === "loading") return {
928
+ text: t("balanceLoading"),
929
+ cls: TokenQuotaPanel_module_css_default.balanceStateHint
930
+ };
931
+ if (entry === "unsupported") return {
932
+ text: t("balanceUnsupported"),
933
+ cls: TokenQuotaPanel_module_css_default.balanceStateHint
934
+ };
935
+ switch (entry.status) {
936
+ case "unconfigured": return {
937
+ text: t("balanceUnconfiguredShort"),
938
+ cls: TokenQuotaPanel_module_css_default.balanceStateHint,
939
+ title: t("balanceUnconfiguredHint")
940
+ };
941
+ case "error":
942
+ if (entry.total > 0) return entry.error !== void 0 ? {
943
+ text: `${currencySymbol(entry.currency)}${entry.total.toFixed(2)}`,
944
+ cls: TokenQuotaPanel_module_css_default.balanceValueError,
945
+ title: entry.error
946
+ } : {
947
+ text: `${currencySymbol(entry.currency)}${entry.total.toFixed(2)}`,
948
+ cls: TokenQuotaPanel_module_css_default.balanceValueError
949
+ };
950
+ return entry.error !== void 0 ? {
951
+ text: t("balanceError"),
952
+ cls: TokenQuotaPanel_module_css_default.balanceStateError,
953
+ title: entry.error
954
+ } : {
955
+ text: t("balanceError"),
956
+ cls: TokenQuotaPanel_module_css_default.balanceStateError
957
+ };
958
+ case "ok":
959
+ if (entry.isAvailable) return entry.total < 5 ? {
960
+ text: `${currencySymbol(entry.currency)}${entry.total.toFixed(2)}`,
961
+ cls: TokenQuotaPanel_module_css_default.balanceValueLow
962
+ } : {
963
+ text: `${currencySymbol(entry.currency)}${entry.total.toFixed(2)}`,
964
+ cls: TokenQuotaPanel_module_css_default.balanceValueOk
965
+ };
966
+ return {
967
+ text: `${currencySymbol(entry.currency)}${entry.total.toFixed(2)}`,
968
+ cls: TokenQuotaPanel_module_css_default.balanceValueError,
969
+ title: t("balanceUnavailable")
970
+ };
971
+ }
972
+ };
973
+ const lastLabel = lastFetchedAt > 0 ? t("balanceLastUpdated").replace("{v}", formatBalanceTime(lastFetchedAt)) : "";
974
+ /**
975
+ * Flip quota/balance every 3s while any supported row is hovered. If nothing
976
+ * is hovered (or the hovered row's provider has no balance data) we reset the
977
+ * toggle so quota shows first next time.
978
+ */
979
+ (0, react.useEffect)(() => {
980
+ if (hoveredRowKey === null) {
981
+ setMetaToggle(0);
982
+ return;
983
+ }
984
+ const [prov] = hoveredRowKey.split("/", 1);
985
+ const bal = balances.find((b) => b.provider === prov);
986
+ if (bal === void 0 || bal.status !== "ok") {
987
+ setMetaToggle(0);
988
+ return;
989
+ }
990
+ setMetaToggle(1);
991
+ const id = window.setInterval(() => {
992
+ setMetaToggle((v) => (v + 1) % 2);
993
+ }, 3e3);
994
+ return () => {
995
+ window.clearInterval(id);
996
+ };
997
+ }, [hoveredRowKey, balances]);
998
+ /** On row hover, if the provider balance is stale (>5min since last ok fetch), kick off a single-provider refresh (throttled to 1/min). */
999
+ const triggerRowBalanceRefreshIfStale = (provider) => {
1000
+ const bal = balances.find((b) => b.provider === provider);
1001
+ if (bal === void 0 || bal.status !== "ok") return;
1002
+ if (Date.now() - bal.fetchedAt < 5 * 6e4) return;
1003
+ const last = lastRowStaleRefreshRef.current[provider] ?? 0;
1004
+ if (Date.now() - last < 6e4) return;
1005
+ lastRowStaleRefreshRef.current[provider] = Date.now();
1006
+ setRefreshingProvider(provider);
1007
+ refreshBalance(provider);
1008
+ window.setTimeout(() => {
1009
+ setRefreshingProvider((prev) => prev === provider ? null : prev);
1010
+ }, 1500);
1011
+ };
1012
+ /** Quota text shown on the right of each model row (used/lot or used · unlimited). */
1013
+ const quotaText = (row) => row.limit > 0 ? `${formatTokens(row.used)} / ${formatTokens(row.limit)}` : `${formatTokens(row.used)} · ${t("unlimited")}`;
1014
+ /** Balance text shown when hovering a supported-provider row, or '' when unavailable. */
1015
+ const balanceTextForRow = (row) => {
1016
+ const bal = balances.find((b) => b.provider === row.provider);
1017
+ if (bal === void 0) return { text: "" };
1018
+ if (bal.status === "ok" && bal.isAvailable) {
1019
+ if (bal.total < 5) return {
1020
+ text: `${currencySymbol(bal.currency)}${bal.total.toFixed(2)}`,
1021
+ cls: TokenQuotaPanel_module_css_default.balanceValueLow
1022
+ };
1023
+ return {
1024
+ text: `${currencySymbol(bal.currency)}${bal.total.toFixed(2)}`,
1025
+ cls: TokenQuotaPanel_module_css_default.balanceValueOk
1026
+ };
1027
+ }
1028
+ return { text: "" };
1029
+ };
795
1030
  const panelDimClass = dimWhenIdle ? hovered || upgradeFlash ? "" : inputActive ? TokenQuotaPanel_module_css_default.panelDimStrong : TokenQuotaPanel_module_css_default.panelDim : "";
796
1031
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
797
1032
  (0, react_jsx_runtime.jsxs)("div", {
@@ -808,34 +1043,78 @@ window.__ModuleLoader__.load({
808
1043
  onPointerLeave: () => {
809
1044
  setHovered(false);
810
1045
  },
811
- children: [(0, react_jsx_runtime.jsxs)("div", {
812
- className: TokenQuotaPanel_module_css_default.header,
813
- onPointerDown: (event) => {
814
- beginDrag(event, panelRef.current, applyPanelPos);
815
- },
816
- children: [(0, react_jsx_runtime.jsxs)("div", {
817
- className: TokenQuotaPanel_module_css_default.headerText,
818
- children: [(0, react_jsx_runtime.jsxs)("div", {
819
- className: TokenQuotaPanel_module_css_default.title,
820
- children: [t("title"), upgrade !== null ? (0, react_jsx_runtime.jsxs)("span", {
821
- className: `${TokenQuotaPanel_module_css_default.version} ${TokenQuotaPanel_module_css_default.versionNew}`,
822
- title: t("upgradeAvailableHint"),
823
- children: [
824
- "v",
825
- "0.1.35",
826
- " → v",
827
- upgrade.latestVersion
828
- ]
829
- }) : (0, react_jsx_runtime.jsxs)("span", {
830
- className: TokenQuotaPanel_module_css_default.version,
831
- children: ["v", "0.1.35"]
832
- })]
1046
+ children: [
1047
+ (0, react_jsx_runtime.jsxs)("div", {
1048
+ className: TokenQuotaPanel_module_css_default.header,
1049
+ onPointerDown: (event) => {
1050
+ beginDrag(event, panelRef.current, applyPanelPos);
1051
+ },
1052
+ children: [(0, react_jsx_runtime.jsx)("div", {
1053
+ className: TokenQuotaPanel_module_css_default.headerText,
1054
+ children: (0, react_jsx_runtime.jsxs)("div", {
1055
+ className: TokenQuotaPanel_module_css_default.title,
1056
+ children: [t("title"), upgrade !== null ? (0, react_jsx_runtime.jsxs)("span", {
1057
+ className: `${TokenQuotaPanel_module_css_default.version} ${TokenQuotaPanel_module_css_default.versionNew}`,
1058
+ title: t("upgradeAvailableHint"),
1059
+ children: [
1060
+ "v",
1061
+ "0.1.36",
1062
+ " → v",
1063
+ upgrade.latestVersion
1064
+ ]
1065
+ }) : (0, react_jsx_runtime.jsxs)("span", {
1066
+ className: TokenQuotaPanel_module_css_default.version,
1067
+ children: ["v", "0.1.36"]
1068
+ })]
1069
+ })
833
1070
  }), (0, react_jsx_runtime.jsxs)("div", {
834
- className: TokenQuotaPanel_module_css_default.notesBar,
1071
+ className: TokenQuotaPanel_module_css_default.headerActions,
835
1072
  onPointerDown: (event) => {
836
1073
  event.stopPropagation();
837
1074
  },
838
- children: [notes.map((note) => {
1075
+ children: [
1076
+ (0, react_jsx_runtime.jsx)("button", {
1077
+ type: "button",
1078
+ "aria-pressed": musicEnabled,
1079
+ className: `${TokenQuotaPanel_module_css_default.settingsBtn}${musicEnabled ? ` ${TokenQuotaPanel_module_css_default.settingsBtnActive}` : ""}`,
1080
+ title: `${t("musicLabel")}:${musicEnabled ? t("musicOn") : t("musicOff")}`,
1081
+ onClick: () => {
1082
+ setMusicEnabled(!musicEnabled);
1083
+ },
1084
+ children: "♪"
1085
+ }),
1086
+ (0, react_jsx_runtime.jsx)("button", {
1087
+ type: "button",
1088
+ className: TokenQuotaPanel_module_css_default.settingsBtn,
1089
+ onClick: () => {
1090
+ actions.setLogOpen(true);
1091
+ },
1092
+ children: t("logs")
1093
+ }),
1094
+ (0, react_jsx_runtime.jsx)("button", {
1095
+ type: "button",
1096
+ className: TokenQuotaPanel_module_css_default.settingsBtn,
1097
+ onClick: openSettings,
1098
+ children: t("settings")
1099
+ }),
1100
+ (0, react_jsx_runtime.jsx)("button", {
1101
+ type: "button",
1102
+ className: TokenQuotaPanel_module_css_default.collapse,
1103
+ onClick: () => {
1104
+ setCollapsed(true);
1105
+ },
1106
+ children: t("collapse")
1107
+ })
1108
+ ]
1109
+ })]
1110
+ }),
1111
+ (0, react_jsx_runtime.jsxs)("div", {
1112
+ className: TokenQuotaPanel_module_css_default.notesBar,
1113
+ onPointerDown: (event) => {
1114
+ event.stopPropagation();
1115
+ },
1116
+ children: [
1117
+ notes.map((note) => {
839
1118
  const open = note.visible && !note.collapsed;
840
1119
  return (0, react_jsx_runtime.jsx)("button", {
841
1120
  type: "button",
@@ -857,7 +1136,8 @@ window.__ModuleLoader__.load({
857
1136
  },
858
1137
  children: noteInitial(note.title)
859
1138
  }, note.id);
860
- }), (0, react_jsx_runtime.jsxs)("button", {
1139
+ }),
1140
+ (0, react_jsx_runtime.jsxs)("button", {
861
1141
  type: "button",
862
1142
  className: TokenQuotaPanel_module_css_default.noteNew,
863
1143
  disabled: notes.length >= 5,
@@ -885,217 +1165,237 @@ window.__ModuleLoader__.load({
885
1165
  className: TokenQuotaPanel_module_css_default.noteNewPlus,
886
1166
  children: "+"
887
1167
  })]
888
- })]
889
- })]
890
- }), (0, react_jsx_runtime.jsxs)("div", {
891
- className: TokenQuotaPanel_module_css_default.headerActions,
892
- onPointerDown: (event) => {
893
- event.stopPropagation();
894
- },
895
- children: [
896
- (0, react_jsx_runtime.jsx)("button", {
897
- type: "button",
898
- "aria-pressed": musicEnabled,
899
- className: `${TokenQuotaPanel_module_css_default.settingsBtn}${musicEnabled ? ` ${TokenQuotaPanel_module_css_default.settingsBtnActive}` : ""}`,
900
- title: `${t("musicLabel")}:${musicEnabled ? t("musicOn") : t("musicOff")}`,
901
- onClick: () => {
902
- setMusicEnabled(!musicEnabled);
903
- },
904
- children: "♪"
905
1168
  }),
906
- (0, react_jsx_runtime.jsx)("button", {
1169
+ balanceEnabled && (0, react_jsx_runtime.jsx)("button", {
907
1170
  type: "button",
908
- className: TokenQuotaPanel_module_css_default.settingsBtn,
1171
+ className: `${TokenQuotaPanel_module_css_default.noteNew} ${TokenQuotaPanel_module_css_default.balanceToggle}${balanceOpen ? ` ${TokenQuotaPanel_module_css_default.balanceToggleOpen}` : ""}`,
1172
+ title: t("balanceTitle"),
909
1173
  onClick: () => {
910
- actions.setLogOpen(true);
911
- },
912
- children: t("logs")
913
- }),
914
- (0, react_jsx_runtime.jsx)("button", {
915
- type: "button",
916
- className: TokenQuotaPanel_module_css_default.settingsBtn,
917
- onClick: openSettings,
918
- children: t("settings")
919
- }),
920
- (0, react_jsx_runtime.jsx)("button", {
921
- type: "button",
922
- className: TokenQuotaPanel_module_css_default.collapse,
923
- onClick: () => {
924
- setCollapsed(true);
1174
+ if (balanceOpen) {
1175
+ setBalanceOpen(false);
1176
+ return;
1177
+ }
1178
+ const winW = 280;
1179
+ const rect = panelRef.current?.getBoundingClientRect();
1180
+ if (rect !== void 0) setBalancePos({
1181
+ x: Math.max(8, rect.left - winW - 8),
1182
+ y: rect.top
1183
+ });
1184
+ else setBalancePos({
1185
+ x: Math.max(8, (typeof window !== "undefined" ? window.innerWidth : 800) - 12 - 320 - winW - 8),
1186
+ y: 56
1187
+ });
1188
+ setBalanceOpen(true);
925
1189
  },
926
- children: t("collapse")
1190
+ "aria-pressed": balanceOpen,
1191
+ children: (0, react_jsx_runtime.jsxs)("svg", {
1192
+ viewBox: "0 0 16 16",
1193
+ width: "13",
1194
+ height: "13",
1195
+ "aria-hidden": "true",
1196
+ children: [(0, react_jsx_runtime.jsx)("circle", {
1197
+ cx: "8",
1198
+ cy: "8",
1199
+ r: "6.3",
1200
+ fill: "none",
1201
+ stroke: "currentColor",
1202
+ strokeWidth: "1.3"
1203
+ }), (0, react_jsx_runtime.jsx)("text", {
1204
+ x: "8",
1205
+ y: "11",
1206
+ textAnchor: "middle",
1207
+ fontSize: "9",
1208
+ fontWeight: "700",
1209
+ fill: "currentColor",
1210
+ children: "¥"
1211
+ })]
1212
+ })
927
1213
  })
928
1214
  ]
929
- })]
930
- }), (0, react_jsx_runtime.jsxs)("div", {
931
- className: TokenQuotaPanel_module_css_default.body,
932
- children: [
933
- loading && (0, react_jsx_runtime.jsx)("div", {
934
- className: TokenQuotaPanel_module_css_default.notice,
935
- children: t("loading")
936
- }),
937
- error !== null && (0, react_jsx_runtime.jsx)("div", {
938
- className: TokenQuotaPanel_module_css_default.noticeError,
939
- children: error
940
- }),
941
- fullNotice !== null && (0, react_jsx_runtime.jsx)("div", {
942
- className: TokenQuotaPanel_module_css_default.fullNotice,
943
- role: "alert",
944
- children: fullNotice
945
- }),
946
- upgrade !== null && !upgradeDismissed && (0, react_jsx_runtime.jsxs)("div", {
947
- className: `${TokenQuotaPanel_module_css_default.upgradeBanner}${upgradeFlash ? ` ${TokenQuotaPanel_module_css_default.upgradeBannerFlash}` : ""}`,
948
- role: "alert",
949
- children: [
950
- (0, react_jsx_runtime.jsx)("div", {
951
- className: TokenQuotaPanel_module_css_default.upgradeBannerText,
952
- children: t("upgradeAvailable").replace("{version}", upgrade.latestVersion)
953
- }),
954
- (0, react_jsx_runtime.jsx)("div", {
955
- className: TokenQuotaPanel_module_css_default.upgradeCommands,
956
- children: upgrade.commands.map((cmd) => (0, react_jsx_runtime.jsxs)("div", {
957
- className: TokenQuotaPanel_module_css_default.upgradeCommandRow,
958
- children: [(0, react_jsx_runtime.jsx)("code", {
959
- className: TokenQuotaPanel_module_css_default.upgradeCommand,
960
- children: cmd
961
- }), (0, react_jsx_runtime.jsx)("button", {
962
- type: "button",
963
- className: `${TokenQuotaPanel_module_css_default.copyBtn}${copiedCmd === cmd ? ` ${TokenQuotaPanel_module_css_default.copyBtnDone}` : ""}`,
964
- onClick: () => {
965
- copyUpgradeCommand(cmd);
966
- },
967
- children: copiedCmd === cmd ? t("copied") : t("copy")
968
- })]
969
- }, cmd))
970
- }),
971
- (0, react_jsx_runtime.jsx)("div", {
972
- className: TokenQuotaPanel_module_css_default.upgradeHint,
973
- children: t("upgradeHint")
974
- }),
975
- (0, react_jsx_runtime.jsx)("button", {
976
- type: "button",
977
- className: TokenQuotaPanel_module_css_default.upgradeClose,
978
- onClick: () => {
979
- actions.setUpgradeDismissed(true);
980
- },
981
- "aria-label": t("close"),
982
- children: "×"
983
- })
984
- ]
985
- }),
986
- !loading && error === null && visibleRows.length === 0 && (0, react_jsx_runtime.jsx)("div", {
987
- className: TokenQuotaPanel_module_css_default.notice,
988
- children: monitored !== null && monitored.length === 0 ? t("noMonitored") : t("waiting")
989
- }),
990
- visibleRows.map((row) => {
991
- const state = barStateOf(row);
992
- const pct = row.limit > 0 ? Math.min(100, Math.round(row.used / row.limit * 100)) : 0;
993
- const barClass = state === "over" ? TokenQuotaPanel_module_css_default.fillOver : state === "warn" ? TokenQuotaPanel_module_css_default.fillWarn : TokenQuotaPanel_module_css_default.fillIdle;
994
- const editing = editingKey === row.key;
995
- return (0, react_jsx_runtime.jsxs)("div", {
996
- className: `${TokenQuotaPanel_module_css_default.row}${dragKey === row.key ? ` ${TokenQuotaPanel_module_css_default.rowDragging}` : ""}${dropKey === row.key && dragKey !== row.key ? ` ${TokenQuotaPanel_module_css_default.rowDropTarget}` : ""}`,
997
- onDragOver: (event) => {
998
- if (dragKey === null || dragKey === row.key) return;
999
- event.preventDefault();
1000
- setDropKey(row.key);
1001
- },
1002
- onDrop: (event) => {
1003
- event.preventDefault();
1004
- if (dragKey !== null && dragKey !== row.key) setModelOrder(reorderKeys(rows.map((r) => r.key), dragKey, row.key));
1005
- setDragKey(null);
1006
- setDropKey(null);
1007
- },
1008
- onDragEnd: () => {
1009
- setDragKey(null);
1010
- setDropKey(null);
1011
- },
1215
+ }),
1216
+ (0, react_jsx_runtime.jsxs)("div", {
1217
+ className: TokenQuotaPanel_module_css_default.body,
1218
+ children: [
1219
+ loading && (0, react_jsx_runtime.jsx)("div", {
1220
+ className: TokenQuotaPanel_module_css_default.notice,
1221
+ children: t("loading")
1222
+ }),
1223
+ error !== null && (0, react_jsx_runtime.jsx)("div", {
1224
+ className: TokenQuotaPanel_module_css_default.noticeError,
1225
+ children: error
1226
+ }),
1227
+ fullNotice !== null && (0, react_jsx_runtime.jsx)("div", {
1228
+ className: TokenQuotaPanel_module_css_default.fullNotice,
1229
+ role: "alert",
1230
+ children: fullNotice
1231
+ }),
1232
+ upgrade !== null && !upgradeDismissed && (0, react_jsx_runtime.jsxs)("div", {
1233
+ className: `${TokenQuotaPanel_module_css_default.upgradeBanner}${upgradeFlash ? ` ${TokenQuotaPanel_module_css_default.upgradeBannerFlash}` : ""}`,
1234
+ role: "alert",
1012
1235
  children: [
1013
- (0, react_jsx_runtime.jsxs)("div", {
1014
- className: TokenQuotaPanel_module_css_default.rowHeader,
1015
- children: [
1016
- (0, react_jsx_runtime.jsx)("span", {
1017
- className: TokenQuotaPanel_module_css_default.rowDrag,
1018
- draggable: true,
1019
- title: t("reorderHint"),
1020
- onDragStart: (event) => {
1021
- setDragKey(row.key);
1022
- event.dataTransfer.effectAllowed = "move";
1023
- event.dataTransfer.setData("text/plain", row.key);
1236
+ (0, react_jsx_runtime.jsx)("div", {
1237
+ className: TokenQuotaPanel_module_css_default.upgradeBannerText,
1238
+ children: t("upgradeAvailable").replace("{version}", upgrade.latestVersion)
1239
+ }),
1240
+ (0, react_jsx_runtime.jsx)("div", {
1241
+ className: TokenQuotaPanel_module_css_default.upgradeCommands,
1242
+ children: upgrade.commands.map((cmd) => (0, react_jsx_runtime.jsxs)("div", {
1243
+ className: TokenQuotaPanel_module_css_default.upgradeCommandRow,
1244
+ children: [(0, react_jsx_runtime.jsx)("code", {
1245
+ className: TokenQuotaPanel_module_css_default.upgradeCommand,
1246
+ children: cmd
1247
+ }), (0, react_jsx_runtime.jsx)("button", {
1248
+ type: "button",
1249
+ className: `${TokenQuotaPanel_module_css_default.copyBtn}${copiedCmd === cmd ? ` ${TokenQuotaPanel_module_css_default.copyBtnDone}` : ""}`,
1250
+ onClick: () => {
1251
+ copyUpgradeCommand(cmd);
1024
1252
  },
1025
- children: "⠿"
1026
- }),
1027
- (0, react_jsx_runtime.jsxs)("span", {
1028
- className: TokenQuotaPanel_module_css_default.rowName,
1029
- title: row.key,
1030
- children: [
1031
- row.name,
1032
- row.current && (0, react_jsx_runtime.jsx)("span", {
1033
- className: TokenQuotaPanel_module_css_default.currentBadge,
1034
- children: t("current")
1035
- }),
1036
- sessionId !== void 0 && !row.current && (0, react_jsx_runtime.jsx)("button", {
1037
- type: "button",
1038
- className: TokenQuotaPanel_module_css_default.selectBtn,
1039
- onClick: () => {
1040
- selectModel(sessionId, row.provider, row.model);
1041
- },
1042
- children: t("select")
1043
- })
1044
- ]
1045
- }),
1046
- (0, react_jsx_runtime.jsxs)("span", {
1047
- className: TokenQuotaPanel_module_css_default.rowMeta,
1048
- children: [row.limit > 0 ? `${formatTokens(row.used)} / ${formatTokens(row.limit)}` : `${formatTokens(row.used)} · ${t("unlimited")}`, (0, react_jsx_runtime.jsx)("button", {
1049
- type: "button",
1050
- className: TokenQuotaPanel_module_css_default.gearBtn,
1051
- title: t("setLimitHint"),
1052
- onClick: () => {
1053
- setEditingKey(editing ? null : row.key);
1054
- },
1055
- children: "⚙"
1056
- })]
1057
- })
1058
- ]
1253
+ children: copiedCmd === cmd ? t("copied") : t("copy")
1254
+ })]
1255
+ }, cmd))
1059
1256
  }),
1060
1257
  (0, react_jsx_runtime.jsx)("div", {
1061
- className: TokenQuotaPanel_module_css_default.bar,
1062
- children: (0, react_jsx_runtime.jsx)("div", {
1063
- className: barClass,
1064
- style: { width: `${pct}%` }
1065
- })
1258
+ className: TokenQuotaPanel_module_css_default.upgradeHint,
1259
+ children: t("upgradeHint")
1066
1260
  }),
1067
- editing && (0, react_jsx_runtime.jsxs)("div", {
1068
- className: TokenQuotaPanel_module_css_default.controls,
1069
- children: [(0, react_jsx_runtime.jsx)("input", {
1070
- className: TokenQuotaPanel_module_css_default.input,
1071
- type: "number",
1072
- min: 0,
1073
- step: 1e4,
1074
- placeholder: t("limitPlaceholder"),
1075
- value: drafts[row.key] ?? "",
1076
- onChange: (event) => {
1077
- setDrafts((prev) => ({
1078
- ...prev,
1079
- [row.key]: event.target.value
1080
- }));
1081
- },
1082
- onKeyDown: (event) => {
1083
- if (event.key === "Enter") commitLimit(row);
1084
- }
1085
- }), (0, react_jsx_runtime.jsx)("button", {
1086
- type: "button",
1087
- className: TokenQuotaPanel_module_css_default.save,
1088
- onClick: () => {
1089
- commitLimit(row);
1090
- },
1091
- children: t("save")
1092
- })]
1261
+ (0, react_jsx_runtime.jsx)("button", {
1262
+ type: "button",
1263
+ className: TokenQuotaPanel_module_css_default.upgradeClose,
1264
+ onClick: () => {
1265
+ actions.setUpgradeDismissed(true);
1266
+ },
1267
+ "aria-label": t("close"),
1268
+ children: "×"
1093
1269
  })
1094
1270
  ]
1095
- }, row.key);
1096
- })
1097
- ]
1098
- })]
1271
+ }),
1272
+ !loading && error === null && visibleRows.length === 0 && (0, react_jsx_runtime.jsx)("div", {
1273
+ className: TokenQuotaPanel_module_css_default.notice,
1274
+ children: monitored !== null && monitored.length === 0 ? t("noMonitored") : t("waiting")
1275
+ }),
1276
+ visibleRows.map((row) => {
1277
+ const state = barStateOf(row);
1278
+ const pct = row.limit > 0 ? Math.min(100, Math.round(row.used / row.limit * 100)) : 0;
1279
+ const barClass = state === "over" ? TokenQuotaPanel_module_css_default.fillOver : state === "warn" ? TokenQuotaPanel_module_css_default.fillWarn : TokenQuotaPanel_module_css_default.fillIdle;
1280
+ const editing = editingKey === row.key;
1281
+ return (0, react_jsx_runtime.jsxs)("div", {
1282
+ className: `${TokenQuotaPanel_module_css_default.row}${dragKey === row.key ? ` ${TokenQuotaPanel_module_css_default.rowDragging}` : ""}${dropKey === row.key && dragKey !== row.key ? ` ${TokenQuotaPanel_module_css_default.rowDropTarget}` : ""}`,
1283
+ onMouseEnter: () => {
1284
+ setHoveredRowKey(row.key);
1285
+ triggerRowBalanceRefreshIfStale(row.provider);
1286
+ },
1287
+ onMouseLeave: () => {
1288
+ setHoveredRowKey((prev) => prev === row.key ? null : prev);
1289
+ },
1290
+ onDragOver: (event) => {
1291
+ if (dragKey === null || dragKey === row.key) return;
1292
+ event.preventDefault();
1293
+ setDropKey(row.key);
1294
+ },
1295
+ onDrop: (event) => {
1296
+ event.preventDefault();
1297
+ if (dragKey !== null && dragKey !== row.key) setModelOrder(reorderKeys(rows.map((r) => r.key), dragKey, row.key));
1298
+ setDragKey(null);
1299
+ setDropKey(null);
1300
+ },
1301
+ onDragEnd: () => {
1302
+ setDragKey(null);
1303
+ setDropKey(null);
1304
+ },
1305
+ children: [
1306
+ (0, react_jsx_runtime.jsxs)("div", {
1307
+ className: TokenQuotaPanel_module_css_default.rowHeader,
1308
+ children: [
1309
+ (0, react_jsx_runtime.jsx)("span", {
1310
+ className: TokenQuotaPanel_module_css_default.rowDrag,
1311
+ draggable: true,
1312
+ title: t("reorderHint"),
1313
+ onDragStart: (event) => {
1314
+ setDragKey(row.key);
1315
+ event.dataTransfer.effectAllowed = "move";
1316
+ event.dataTransfer.setData("text/plain", row.key);
1317
+ },
1318
+ children: "⠿"
1319
+ }),
1320
+ (0, react_jsx_runtime.jsxs)("span", {
1321
+ className: TokenQuotaPanel_module_css_default.rowName,
1322
+ title: row.key,
1323
+ children: [
1324
+ row.name,
1325
+ row.current && (0, react_jsx_runtime.jsx)("span", {
1326
+ className: TokenQuotaPanel_module_css_default.currentBadge,
1327
+ children: t("current")
1328
+ }),
1329
+ sessionId !== void 0 && !row.current && (0, react_jsx_runtime.jsx)("button", {
1330
+ type: "button",
1331
+ className: TokenQuotaPanel_module_css_default.selectBtn,
1332
+ onClick: () => {
1333
+ selectModel(sessionId, row.provider, row.model);
1334
+ },
1335
+ children: t("select")
1336
+ })
1337
+ ]
1338
+ }),
1339
+ (0, react_jsx_runtime.jsxs)("span", {
1340
+ className: TokenQuotaPanel_module_css_default.rowMeta,
1341
+ children: [(() => {
1342
+ const bt = balanceTextForRow(row);
1343
+ return hoveredRowKey === row.key && bt.text !== "" && metaToggle === 1 ? (0, react_jsx_runtime.jsx)("span", {
1344
+ className: bt.cls,
1345
+ children: bt.text
1346
+ }) : (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: quotaText(row) });
1347
+ })(), (0, react_jsx_runtime.jsx)("button", {
1348
+ type: "button",
1349
+ className: TokenQuotaPanel_module_css_default.gearBtn,
1350
+ title: t("setLimitHint"),
1351
+ onClick: () => {
1352
+ setEditingKey(editing ? null : row.key);
1353
+ },
1354
+ children: "⚙"
1355
+ })]
1356
+ })
1357
+ ]
1358
+ }),
1359
+ (0, react_jsx_runtime.jsx)("div", {
1360
+ className: TokenQuotaPanel_module_css_default.bar,
1361
+ children: (0, react_jsx_runtime.jsx)("div", {
1362
+ className: barClass,
1363
+ style: { width: `${pct}%` }
1364
+ })
1365
+ }),
1366
+ editing && (0, react_jsx_runtime.jsxs)("div", {
1367
+ className: TokenQuotaPanel_module_css_default.controls,
1368
+ children: [(0, react_jsx_runtime.jsx)("input", {
1369
+ className: TokenQuotaPanel_module_css_default.input,
1370
+ type: "number",
1371
+ min: 0,
1372
+ step: 1e4,
1373
+ placeholder: t("limitPlaceholder"),
1374
+ value: drafts[row.key] ?? "",
1375
+ onChange: (event) => {
1376
+ setDrafts((prev) => ({
1377
+ ...prev,
1378
+ [row.key]: event.target.value
1379
+ }));
1380
+ },
1381
+ onKeyDown: (event) => {
1382
+ if (event.key === "Enter") commitLimit(row);
1383
+ }
1384
+ }), (0, react_jsx_runtime.jsx)("button", {
1385
+ type: "button",
1386
+ className: TokenQuotaPanel_module_css_default.save,
1387
+ onClick: () => {
1388
+ commitLimit(row);
1389
+ },
1390
+ children: t("save")
1391
+ })]
1392
+ })
1393
+ ]
1394
+ }, row.key);
1395
+ })
1396
+ ]
1397
+ })
1398
+ ]
1099
1399
  }),
1100
1400
  dialogOpen && (0, react_jsx_runtime.jsxs)("div", {
1101
1401
  ref: settingsRef,
@@ -1361,7 +1661,7 @@ window.__ModuleLoader__.load({
1361
1661
  })
1362
1662
  ]
1363
1663
  })] }),
1364
- settingsTab === "other" && (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: (0, react_jsx_runtime.jsxs)("div", {
1664
+ settingsTab === "other" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
1365
1665
  className: TokenQuotaPanel_module_css_default.dialogSection,
1366
1666
  children: [(0, react_jsx_runtime.jsxs)("div", {
1367
1667
  className: TokenQuotaPanel_module_css_default.checkUpdatesRow,
@@ -1386,9 +1686,168 @@ window.__ModuleLoader__.load({
1386
1686
  role: "alert",
1387
1687
  children: upgradeError
1388
1688
  })]
1389
- }) })
1689
+ }), (0, react_jsx_runtime.jsxs)("div", {
1690
+ className: TokenQuotaPanel_module_css_default.dialogSection,
1691
+ children: [
1692
+ (0, react_jsx_runtime.jsx)("div", {
1693
+ className: TokenQuotaPanel_module_css_default.dialogLabel,
1694
+ children: t("balanceSectionLabel")
1695
+ }),
1696
+ (0, react_jsx_runtime.jsxs)("label", {
1697
+ className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
1698
+ children: [(0, react_jsx_runtime.jsx)("input", {
1699
+ type: "checkbox",
1700
+ checked: balanceEnabled,
1701
+ onChange: (event) => {
1702
+ setBalanceSettings(event.target.checked, balancePollMinutes);
1703
+ }
1704
+ }), (0, react_jsx_runtime.jsx)("span", { children: t("balanceEnabledLabel") })]
1705
+ }),
1706
+ (0, react_jsx_runtime.jsx)("div", {
1707
+ className: TokenQuotaPanel_module_css_default.dialogHint,
1708
+ children: t("balanceHint").replace("{v}", String(balancePollMinutes))
1709
+ }),
1710
+ balanceEnabled && (0, react_jsx_runtime.jsxs)("label", {
1711
+ className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
1712
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("balancePollLabel") }), (0, react_jsx_runtime.jsx)("select", {
1713
+ value: balancePollMinutes,
1714
+ onChange: (event) => {
1715
+ setBalanceSettings(balanceEnabled, Number(event.target.value));
1716
+ },
1717
+ children: [
1718
+ 1,
1719
+ 5,
1720
+ 15,
1721
+ 30,
1722
+ 60
1723
+ ].map((minutes) => (0, react_jsx_runtime.jsx)("option", {
1724
+ value: minutes,
1725
+ children: t("balancePollMinutes").replace("{v}", String(minutes))
1726
+ }, minutes))
1727
+ })]
1728
+ })
1729
+ ]
1730
+ })] })
1390
1731
  ]
1391
1732
  }),
1733
+ balanceOpen && balancePos !== null && (0, react_jsx_runtime.jsxs)("div", {
1734
+ className: TokenQuotaPanel_module_css_default.balanceWin,
1735
+ style: {
1736
+ left: balancePos.x,
1737
+ top: balancePos.y
1738
+ },
1739
+ children: [(0, react_jsx_runtime.jsxs)("div", {
1740
+ className: TokenQuotaPanel_module_css_default.balanceWinHeader,
1741
+ children: [(0, react_jsx_runtime.jsx)("div", {
1742
+ className: TokenQuotaPanel_module_css_default.balanceWinTitle,
1743
+ children: t("balanceTitle")
1744
+ }), (0, react_jsx_runtime.jsx)("button", {
1745
+ type: "button",
1746
+ className: TokenQuotaPanel_module_css_default.dialogClose,
1747
+ title: t("close"),
1748
+ onClick: () => {
1749
+ setBalanceOpen(false);
1750
+ },
1751
+ children: "×"
1752
+ })]
1753
+ }), (0, react_jsx_runtime.jsxs)("div", {
1754
+ className: TokenQuotaPanel_module_css_default.balanceWinBody,
1755
+ children: [
1756
+ (0, react_jsx_runtime.jsxs)("div", {
1757
+ className: TokenQuotaPanel_module_css_default.balanceRowHeader,
1758
+ children: [
1759
+ (0, react_jsx_runtime.jsxs)("button", {
1760
+ type: "button",
1761
+ className: TokenQuotaPanel_module_css_default.balanceSortBtn,
1762
+ onClick: () => {
1763
+ setBalanceSort((prev) => {
1764
+ if (prev === null || prev.key !== "provider") return {
1765
+ key: "provider",
1766
+ dir: 1
1767
+ };
1768
+ if (prev.dir === 1) return {
1769
+ key: "provider",
1770
+ dir: -1
1771
+ };
1772
+ return null;
1773
+ });
1774
+ },
1775
+ children: [t("balanceColProvider"), balanceSort?.key === "provider" ? balanceSort.dir === 1 ? " ▲" : " ▼" : ""]
1776
+ }),
1777
+ (0, react_jsx_runtime.jsxs)("button", {
1778
+ type: "button",
1779
+ className: TokenQuotaPanel_module_css_default.balanceSortBtn,
1780
+ onClick: () => {
1781
+ setBalanceSort((prev) => {
1782
+ if (prev === null || prev.key !== "total") return {
1783
+ key: "total",
1784
+ dir: -1
1785
+ };
1786
+ if (prev.dir === -1) return {
1787
+ key: "total",
1788
+ dir: 1
1789
+ };
1790
+ return null;
1791
+ });
1792
+ },
1793
+ children: [t("balanceColBalance"), balanceSort?.key === "total" ? balanceSort.dir === 1 ? " ▲" : " ▼" : ""]
1794
+ }),
1795
+ (0, react_jsx_runtime.jsx)("button", {
1796
+ type: "button",
1797
+ className: TokenQuotaPanel_module_css_default.balanceRefreshAll,
1798
+ title: t("balanceRefreshAll"),
1799
+ disabled: refreshingAll,
1800
+ onClick: () => {
1801
+ setRefreshingAll(true);
1802
+ refreshBalance();
1803
+ window.setTimeout(() => {
1804
+ setRefreshingAll(false);
1805
+ }, 1500);
1806
+ },
1807
+ children: refreshingAll ? "…" : "↻"
1808
+ })
1809
+ ]
1810
+ }),
1811
+ sortedProviders.map((provider) => {
1812
+ const cell = renderProviderCell(provider);
1813
+ const busy = refreshingProvider === provider || balanceByProvider.get(provider) === "loading";
1814
+ return (0, react_jsx_runtime.jsxs)("div", {
1815
+ className: TokenQuotaPanel_module_css_default.balanceRow,
1816
+ children: [
1817
+ (0, react_jsx_runtime.jsx)("span", {
1818
+ className: TokenQuotaPanel_module_css_default.balanceRowName,
1819
+ title: provider,
1820
+ children: provider
1821
+ }),
1822
+ (0, react_jsx_runtime.jsx)("span", {
1823
+ className: `${TokenQuotaPanel_module_css_default.balanceRowValue}${cell.cls !== void 0 ? ` ${cell.cls}` : ""}`,
1824
+ title: cell.title,
1825
+ children: cell.text
1826
+ }),
1827
+ (0, react_jsx_runtime.jsx)("button", {
1828
+ type: "button",
1829
+ className: TokenQuotaPanel_module_css_default.balanceRowRefresh,
1830
+ title: t("balanceRefreshOne"),
1831
+ disabled: busy || refreshingAll || cell.text === t("balanceUnsupported"),
1832
+ onClick: () => {
1833
+ setRefreshingProvider(provider);
1834
+ refreshBalance(provider);
1835
+ window.setTimeout(() => {
1836
+ setRefreshingProvider(null);
1837
+ }, 1500);
1838
+ },
1839
+ children: busy ? "…" : "↻"
1840
+ })
1841
+ ]
1842
+ }, provider);
1843
+ }),
1844
+ lastLabel !== "" && (0, react_jsx_runtime.jsx)("div", {
1845
+ className: TokenQuotaPanel_module_css_default.balanceLastLine,
1846
+ children: lastLabel
1847
+ })
1848
+ ]
1849
+ })]
1850
+ }),
1392
1851
  logOpen && (0, react_jsx_runtime.jsxs)("div", {
1393
1852
  ref: logRef,
1394
1853
  className: `${TokenQuotaPanel_module_css_default.dialog} ${TokenQuotaPanel_module_css_default.logDialog}`,
@@ -2355,6 +2814,29 @@ window.__ModuleLoader__.load({
2355
2814
  settingsTabMusic: "音乐设置",
2356
2815
  settingsTabOther: "其它",
2357
2816
  reorderHint: "拖动排序",
2817
+ balanceLabel: "余额",
2818
+ balanceUnconfigured: "未配置 DeepSeek API Key",
2819
+ balanceUnconfiguredHint: "在 Models 页配置 API Key 后,这里自动显示账户余额",
2820
+ balanceError: "余额获取失败",
2821
+ balanceUnavailable: "账户不可用",
2822
+ balanceRefresh: "刷新余额",
2823
+ balanceUpdatedAt: "更新于 {v}",
2824
+ balanceGranted: "赠送 {v}",
2825
+ balanceToppedUp: "充值 {v}",
2826
+ balanceSectionLabel: "账户余额",
2827
+ balanceEnabledLabel: "显示账户余额",
2828
+ balanceHint: "每 {v} 分钟从服务商查询一次,也可在主界面点击 ↻ 手动刷新",
2829
+ balancePollLabel: "刷新间隔",
2830
+ balancePollMinutes: "{v} 分钟",
2831
+ balanceTitle: "账户余额",
2832
+ balanceColProvider: "提供方",
2833
+ balanceColBalance: "余额",
2834
+ balanceLoading: "查询中…",
2835
+ balanceUnsupported: "未接入查询",
2836
+ balanceUnconfiguredShort: "未配置 KEY",
2837
+ balanceRefreshAll: "刷新全部",
2838
+ balanceRefreshOne: "刷新单条",
2839
+ balanceLastUpdated: "Last: {v}",
2358
2840
  noteNewHint: "新建笔记(最多 5 个)",
2359
2841
  noteMaxHint: "最多 5 个笔记",
2360
2842
  noteUntitled: "未命名",
@@ -2448,6 +2930,29 @@ window.__ModuleLoader__.load({
2448
2930
  settingsTabMusic: "Music",
2449
2931
  settingsTabOther: "Other",
2450
2932
  reorderHint: "Drag to reorder",
2933
+ balanceLabel: "Balance",
2934
+ balanceUnconfigured: "No DeepSeek API key",
2935
+ balanceUnconfiguredHint: "Configure an API key on the Models page to show the account balance here",
2936
+ balanceError: "Balance fetch failed",
2937
+ balanceUnavailable: "Account unavailable",
2938
+ balanceRefresh: "Refresh balance",
2939
+ balanceUpdatedAt: "Updated {v}",
2940
+ balanceGranted: "Granted {v}",
2941
+ balanceToppedUp: "Topped up {v}",
2942
+ balanceSectionLabel: "Account balance",
2943
+ balanceEnabledLabel: "Show account balance",
2944
+ balanceHint: "Queried from the provider every {v} min; refresh manually from the panel with ↻",
2945
+ balancePollLabel: "Refresh interval",
2946
+ balancePollMinutes: "{v} min",
2947
+ balanceTitle: "Account balance",
2948
+ balanceColProvider: "Provider",
2949
+ balanceColBalance: "Balance",
2950
+ balanceLoading: "Loading…",
2951
+ balanceUnsupported: "Not available",
2952
+ balanceUnconfiguredShort: "No API key",
2953
+ balanceRefreshAll: "Refresh all",
2954
+ balanceRefreshOne: "Refresh",
2955
+ balanceLastUpdated: "Last: {v}",
2451
2956
  noteNewHint: "New note (max 5)",
2452
2957
  noteMaxHint: "At most 5 notes",
2453
2958
  noteUntitled: "Untitled",
@@ -2735,6 +3240,8 @@ window.__ModuleLoader__.load({
2735
3240
  bound?.setOrder(Array.isArray(doc?.order) ? doc.order : []);
2736
3241
  bound?.setNotes(normalizeNotes(doc?.notes));
2737
3242
  bound?.setReset(lastReset);
3243
+ const balanceRaw = doc?.balance;
3244
+ bound?.setBalanceSettings(balanceRaw?.enabled !== false, typeof balanceRaw?.pollMinutes === "number" ? Math.max(1, Math.min(120, Math.round(balanceRaw.pollMinutes))) : 5);
2738
3245
  const musicSettings = musicDefaults(doc?.music);
2739
3246
  lastOnlyCurrentSession = musicSettings.onlyCurrentSession;
2740
3247
  music.setEnabled(musicSettings.enabled);
@@ -2757,6 +3264,7 @@ window.__ModuleLoader__.load({
2757
3264
  bound?.setSnapshot(snapshot);
2758
3265
  bound?.setUpgrade(snapshot.upgrade ?? null);
2759
3266
  bound?.setUpgradeError(snapshot.upgradeError ?? null);
3267
+ bound?.setBalances(snapshot.balances ?? []);
2760
3268
  actOnFull(snapshot);
2761
3269
  });
2762
3270
  if (pullCount % 2 === 0) refreshCurrent();
@@ -2823,6 +3331,33 @@ window.__ModuleLoader__.load({
2823
3331
  const saveNotes = (notes) => {
2824
3332
  scope.set("notes", notes.slice(0, 5));
2825
3333
  };
3334
+ /** Persist the account-balance display settings. */
3335
+ const setBalanceSettings = (enabled, pollMinutes) => {
3336
+ bound?.setBalanceSettings(enabled, pollMinutes);
3337
+ scope.set("balance", {
3338
+ enabled,
3339
+ pollMinutes
3340
+ });
3341
+ };
3342
+ /**
3343
+ * Ask the Host to refresh account balances now and adopt the result.
3344
+ * With a provider key only that provider is refreshed; otherwise all of
3345
+ * them are.
3346
+ */
3347
+ const refreshBalance = (provider) => {
3348
+ const init = {
3349
+ method: "POST",
3350
+ headers: { "content-type": "application/json" }
3351
+ };
3352
+ if (provider !== void 0) init.body = JSON.stringify({ provider });
3353
+ fetch("/token-quota/refresh-balance", init).then((response) => response.ok ? response.json() : void 0, () => void 0).then((snapshot) => {
3354
+ if (snapshot === void 0) return;
3355
+ bound?.setSnapshot(snapshot);
3356
+ bound?.setUpgrade(snapshot.upgrade ?? null);
3357
+ bound?.setUpgradeError(snapshot.upgradeError ?? null);
3358
+ bound?.setBalances(snapshot.balances ?? []);
3359
+ });
3360
+ };
2826
3361
  const setMusicEnabled = (enabled) => {
2827
3362
  const doc = scope.getSnapshot().value;
2828
3363
  const current = musicDefaults(doc?.music);
@@ -2937,7 +3472,9 @@ window.__ModuleLoader__.load({
2937
3472
  setMusicStyle,
2938
3473
  setMusicOnlyCurrentSession,
2939
3474
  setModelOrder,
2940
- saveNotes
3475
+ saveNotes,
3476
+ setBalanceSettings,
3477
+ refreshBalance
2941
3478
  };
2942
3479
  };
2943
3480
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({