@jxgame2020/dsh-token-quota 0.1.15 → 0.1.17

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
@@ -36,6 +36,7 @@ window.__ModuleLoader__.load({
36
36
  monitored: null,
37
37
  onFull: "stop",
38
38
  checkUpdates: true,
39
+ dimWhenIdle: false,
39
40
  upgrade: null,
40
41
  upgradeDismissed: false,
41
42
  checkingUpdates: false,
@@ -74,9 +75,13 @@ window.__ModuleLoader__.load({
74
75
  setCheckUpdates: (d, checkUpdates) => {
75
76
  d.checkUpdates = checkUpdates;
76
77
  },
78
+ setDimWhenIdle: (d, dimWhenIdle) => {
79
+ d.dimWhenIdle = dimWhenIdle;
80
+ },
77
81
  setUpgrade: (d, upgrade) => {
78
- if (d.upgrade === null || d.upgrade.latestVersion !== upgrade?.latestVersion) d.upgradeDismissed = false;
79
- d.upgrade = upgrade;
82
+ const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.17" ? upgrade : null;
83
+ if (d.upgrade === null || d.upgrade.latestVersion !== normalized?.latestVersion) d.upgradeDismissed = false;
84
+ d.upgrade = normalized;
80
85
  },
81
86
  setUpgradeDismissed: (d, dismissed) => {
82
87
  d.upgradeDismissed = dismissed;
@@ -170,7 +175,7 @@ window.__ModuleLoader__.load({
170
175
  }
171
176
  //#endregion
172
177
  //#region \0dsh-css:/Users/jxgame/Desktop/works/deepseek-harness-package/dsh-token-quota/packages/token-quota/src/client/TokenQuotaPanel.module.css.mjs
173
- 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_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_rowName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;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_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}.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}.NPLLIq_copyBtn:hover{background:var(--dsw-alias-bg-layer-3,#00000014)}.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_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;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:280px;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_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_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;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}";
178
+ 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_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_rowName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;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_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_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;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:280px;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_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;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}";
174
179
  const tagId = "@jxgame2020/dsh-token-quota/TokenQuotaPanel.module.css";
175
180
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
176
181
  const tag = document.createElement("style");
@@ -180,88 +185,91 @@ window.__ModuleLoader__.load({
180
185
  document.head.appendChild(tag);
181
186
  }
182
187
  var TokenQuotaPanel_module_css_default = {
183
- "fullNotice": "NPLLIq_fullNotice",
184
- "upgradeBanner": "NPLLIq_upgradeBanner",
185
- "logHeader": "NPLLIq_logHeader",
186
- "title": "NPLLIq_title",
187
- "rowHeader": "NPLLIq_rowHeader",
188
- "dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
189
- "settingsBtn": "NPLLIq_settingsBtn",
190
- "version": "NPLLIq_version",
191
- "headerText": "NPLLIq_headerText",
188
+ "logPager": "NPLLIq_logPager",
189
+ "logToolbar": "NPLLIq_logToolbar",
190
+ "radioRow": "NPLLIq_radioRow",
191
+ "dialogClose": "NPLLIq_dialogClose",
192
192
  "gearBtn": "NPLLIq_gearBtn",
193
+ "panelDim": "NPLLIq_panelDim",
194
+ "fillIdle": "NPLLIq_fillIdle",
195
+ "monitorHint": "NPLLIq_monitorHint",
196
+ "logPagerControls": "NPLLIq_logPagerControls",
197
+ "currentBadge": "NPLLIq_currentBadge",
198
+ "tabLabel": "NPLLIq_tabLabel",
199
+ "dialogTitle": "NPLLIq_dialogTitle",
200
+ "logClearDialog": "NPLLIq_logClearDialog",
201
+ "checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
202
+ "settingsBtn": "NPLLIq_settingsBtn",
203
+ "dialogActions": "NPLLIq_dialogActions",
204
+ "logPageSizeSelect": "NPLLIq_logPageSizeSelect",
205
+ "rowName": "NPLLIq_rowName",
206
+ "logHeader": "NPLLIq_logHeader",
207
+ "row": "NPLLIq_row",
193
208
  "checkUpdatesRow": "NPLLIq_checkUpdatesRow",
194
- "monitorList": "NPLLIq_monitorList",
195
- "switch": "NPLLIq_switch",
196
- "upgradeBannerText": "NPLLIq_upgradeBannerText",
197
- "checkBtn": "NPLLIq_checkBtn",
198
- "monitorName": "NPLLIq_monitorName",
209
+ "panel": "NPLLIq_panel",
199
210
  "copyBtn": "NPLLIq_copyBtn",
200
- "logDayCol": "NPLLIq_logDayCol",
201
- "logPager": "NPLLIq_logPager",
202
- "resetSelect": "NPLLIq_resetSelect",
203
- "logPagerInfo": "NPLLIq_logPagerInfo",
204
- "logClearDate": "NPLLIq_logClearDate",
205
- "logPagerBtn": "NPLLIq_logPagerBtn",
206
- "fillWarn": "NPLLIq_fillWarn",
211
+ "dialogLabel": "NPLLIq_dialogLabel",
212
+ "upgradeCommand": "NPLLIq_upgradeCommand",
213
+ "logSearch": "NPLLIq_logSearch",
214
+ "version": "NPLLIq_version",
215
+ "logDialog": "NPLLIq_logDialog",
207
216
  "rowMeta": "NPLLIq_rowMeta",
208
- "subtitle": "NPLLIq_subtitle",
209
- "dialogBody": "NPLLIq_dialogBody",
210
- "fillIdle": "NPLLIq_fillIdle",
211
217
  "selectBtn": "NPLLIq_selectBtn",
218
+ "dialogOverlay": "NPLLIq_dialogOverlay",
212
219
  "controls": "NPLLIq_controls",
213
- "resetRow": "NPLLIq_resetRow",
220
+ "fullNotice": "NPLLIq_fullNotice",
221
+ "upgradeBannerFlash": "NPLLIq_upgradeBannerFlash",
222
+ "dialog": "NPLLIq_dialog",
214
223
  "upgradeCommands": "NPLLIq_upgradeCommands",
215
- "versionNew": "NPLLIq_versionNew",
216
- "body": "NPLLIq_body",
217
- "row": "NPLLIq_row",
218
- "upgradeHint": "NPLLIq_upgradeHint",
219
- "monitorHint": "NPLLIq_monitorHint",
220
- "logPagerControls": "NPLLIq_logPagerControls",
221
- "logClearDialog": "NPLLIq_logClearDialog",
222
- "currentBadge": "NPLLIq_currentBadge",
223
- "dialogSection": "NPLLIq_dialogSection",
224
- "bar": "NPLLIq_bar",
225
- "panelDim": "NPLLIq_panelDim",
226
- "headerActions": "NPLLIq_headerActions",
227
- "upgradeClose": "NPLLIq_upgradeClose",
224
+ "copyBtnDone": "NPLLIq_copyBtnDone",
225
+ "input": "NPLLIq_input",
228
226
  "header": "NPLLIq_header",
229
- "dialogLabel": "NPLLIq_dialogLabel",
230
- "upgradeCommand": "NPLLIq_upgradeCommand",
231
- "noticeError": "NPLLIq_noticeError",
232
- "notice": "NPLLIq_notice",
233
- "dialogBtn": "NPLLIq_dialogBtn",
234
- "logToolbar": "NPLLIq_logToolbar",
235
- "tab": "NPLLIq_tab",
236
227
  "tabIcon": "NPLLIq_tabIcon",
237
- "panel": "NPLLIq_panel",
238
- "collapse": "NPLLIq_collapse",
239
- "dialogTitle": "NPLLIq_dialogTitle",
240
- "logTable": "NPLLIq_logTable",
241
- "fillOver": "NPLLIq_fillOver",
242
- "dialog": "NPLLIq_dialog",
243
- "radioRow": "NPLLIq_radioRow",
228
+ "monitorName": "NPLLIq_monitorName",
229
+ "checkBtn": "NPLLIq_checkBtn",
230
+ "rowHeader": "NPLLIq_rowHeader",
231
+ "panelDimStrong": "NPLLIq_panelDimStrong",
232
+ "bar": "NPLLIq_bar",
233
+ "save": "NPLLIq_save",
234
+ "upgradeClose": "NPLLIq_upgradeClose",
244
235
  "dialogHeader": "NPLLIq_dialogHeader",
245
- "logClearBtn": "NPLLIq_logClearBtn",
236
+ "upgradeBanner": "NPLLIq_upgradeBanner",
237
+ "resetRow": "NPLLIq_resetRow",
238
+ "notice": "NPLLIq_notice",
239
+ "logClearDate": "NPLLIq_logClearDate",
240
+ "switch": "NPLLIq_switch",
241
+ "headerActions": "NPLLIq_headerActions",
246
242
  "logModelCol": "NPLLIq_logModelCol",
247
- "panelDimStrong": "NPLLIq_panelDimStrong",
248
- "logPagerPages": "NPLLIq_logPagerPages",
249
- "logPageSizeSelect": "NPLLIq_logPageSizeSelect",
250
- "dialogOverlay": "NPLLIq_dialogOverlay",
251
- "upgradeCommandRow": "NPLLIq_upgradeCommandRow",
252
- "dialogClose": "NPLLIq_dialogClose",
243
+ "dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
253
244
  "checkErrorHint": "NPLLIq_checkErrorHint",
254
- "rowName": "NPLLIq_rowName",
255
- "checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
256
- "logSearch": "NPLLIq_logSearch",
257
- "dialogActions": "NPLLIq_dialogActions",
245
+ "logPagerPages": "NPLLIq_logPagerPages",
246
+ "dialogSection": "NPLLIq_dialogSection",
258
247
  "monitorRow": "NPLLIq_monitorRow",
259
- "save": "NPLLIq_save",
260
- "logUsedCol": "NPLLIq_logUsedCol",
261
- "logDialog": "NPLLIq_logDialog",
262
248
  "logScroll": "NPLLIq_logScroll",
263
- "tabLabel": "NPLLIq_tabLabel",
264
- "input": "NPLLIq_input"
249
+ "logTable": "NPLLIq_logTable",
250
+ "logClearBtn": "NPLLIq_logClearBtn",
251
+ "headerText": "NPLLIq_headerText",
252
+ "fillWarn": "NPLLIq_fillWarn",
253
+ "logDayCol": "NPLLIq_logDayCol",
254
+ "logPagerBtn": "NPLLIq_logPagerBtn",
255
+ "collapse": "NPLLIq_collapse",
256
+ "dialogHint": "NPLLIq_dialogHint",
257
+ "monitorList": "NPLLIq_monitorList",
258
+ "logPagerInfo": "NPLLIq_logPagerInfo",
259
+ "title": "NPLLIq_title",
260
+ "subtitle": "NPLLIq_subtitle",
261
+ "logUsedCol": "NPLLIq_logUsedCol",
262
+ "noticeError": "NPLLIq_noticeError",
263
+ "dialogBody": "NPLLIq_dialogBody",
264
+ "body": "NPLLIq_body",
265
+ "dialogBtn": "NPLLIq_dialogBtn",
266
+ "fillOver": "NPLLIq_fillOver",
267
+ "upgradeCommandRow": "NPLLIq_upgradeCommandRow",
268
+ "upgradeHint": "NPLLIq_upgradeHint",
269
+ "tab": "NPLLIq_tab",
270
+ "upgradeBannerText": "NPLLIq_upgradeBannerText",
271
+ "resetSelect": "NPLLIq_resetSelect",
272
+ "versionNew": "NPLLIq_versionNew"
265
273
  };
266
274
  //#endregion
267
275
  //#region lib/types/client/TokenQuotaPanel.js
@@ -348,12 +356,17 @@ window.__ModuleLoader__.load({
348
356
  * @param props - composed slot props.
349
357
  * @returns the panel element tree.
350
358
  */
351
- function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, useStore, actions, useSessions }) {
359
+ function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, useStore, actions, useSessions }) {
352
360
  const [collapsed, setCollapsed] = (0, react.useState)(false);
353
361
  const [drafts, setDrafts] = (0, react.useState)({});
354
362
  const [editingKey, setEditingKey] = (0, react.useState)(null);
355
363
  const [hovered, setHovered] = (0, react.useState)(false);
356
364
  const [inputActive, setInputActive] = (0, react.useState)(false);
365
+ const [copiedCmd, setCopiedCmd] = (0, react.useState)(null);
366
+ const copyTimerRef = (0, react.useRef)(null);
367
+ const [upgradeFlash, setUpgradeFlash] = (0, react.useState)(false);
368
+ const flashTimerRef = (0, react.useRef)(null);
369
+ const prevUpgradeRef = (0, react.useRef)(null);
357
370
  const [panelPos, setPanelPos] = (0, react.useState)(loadPanelPos);
358
371
  const [settingsPos, setSettingsPos] = (0, react.useState)(null);
359
372
  const [logPos, setLogPos] = (0, react.useState)(null);
@@ -373,6 +386,7 @@ window.__ModuleLoader__.load({
373
386
  const monitored = useStore((s) => s.monitored);
374
387
  const onFull = useStore((s) => s.onFull);
375
388
  const checkUpdates = useStore((s) => s.checkUpdates);
389
+ const dimWhenIdle = useStore((s) => s.dimWhenIdle);
376
390
  const upgrade = useStore((s) => s.upgrade);
377
391
  const upgradeDismissed = useStore((s) => s.upgradeDismissed);
378
392
  const checkingUpdates = useStore((s) => s.checkingUpdates);
@@ -405,6 +419,26 @@ window.__ModuleLoader__.load({
405
419
  window.removeEventListener("focusin", onFocusIn);
406
420
  };
407
421
  }, []);
422
+ (0, react.useEffect)(() => {
423
+ const latest = upgrade?.latestVersion ?? null;
424
+ if (latest !== null && prevUpgradeRef.current !== latest) {
425
+ setCollapsed(false);
426
+ setUpgradeFlash(true);
427
+ if (flashTimerRef.current !== null) clearTimeout(flashTimerRef.current);
428
+ flashTimerRef.current = window.setTimeout(() => {
429
+ setUpgradeFlash(false);
430
+ flashTimerRef.current = null;
431
+ }, 2500);
432
+ }
433
+ prevUpgradeRef.current = latest;
434
+ return () => {};
435
+ }, [upgrade]);
436
+ (0, react.useEffect)(() => {
437
+ return () => {
438
+ if (flashTimerRef.current !== null) clearTimeout(flashTimerRef.current);
439
+ if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
440
+ };
441
+ }, []);
408
442
  const rows = (0, react.useMemo)(() => mergeModelRows(groups, snapshot, current), [
409
443
  groups,
410
444
  snapshot,
@@ -448,6 +482,16 @@ window.__ModuleLoader__.load({
448
482
  document.body.removeChild(el);
449
483
  }
450
484
  };
485
+ /** Copy an upgrade command and show per-button success feedback. */
486
+ const copyUpgradeCommand = (cmd) => {
487
+ copyToClipboard(cmd);
488
+ setCopiedCmd(cmd);
489
+ if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
490
+ copyTimerRef.current = window.setTimeout(() => {
491
+ setCopiedCmd(null);
492
+ copyTimerRef.current = null;
493
+ }, 1600);
494
+ };
451
495
  (0, react.useEffect)(() => {
452
496
  if (!logOpen) return;
453
497
  let cancelled = false;
@@ -498,7 +542,7 @@ window.__ModuleLoader__.load({
498
542
  setLimit(row.key, parsed);
499
543
  };
500
544
  const visibleRows = rows.filter((row) => isMonitoredKey(row.key) || row.current);
501
- const panelDimClass = hovered ? "" : inputActive ? TokenQuotaPanel_module_css_default.panelDimStrong : TokenQuotaPanel_module_css_default.panelDim;
545
+ const panelDimClass = dimWhenIdle ? hovered || upgradeFlash ? "" : inputActive ? TokenQuotaPanel_module_css_default.panelDimStrong : TokenQuotaPanel_module_css_default.panelDim : "";
502
546
  return (0, react_jsx_runtime.jsxs)("div", {
503
547
  ref: panelRef,
504
548
  className: `${TokenQuotaPanel_module_css_default.panel}${panelDimClass !== "" ? ` ${panelDimClass}` : ""}`,
@@ -528,13 +572,13 @@ window.__ModuleLoader__.load({
528
572
  title: t("upgradeAvailableHint"),
529
573
  children: [
530
574
  "v",
531
- "0.1.15",
575
+ "0.1.17",
532
576
  " → v",
533
577
  upgrade.latestVersion
534
578
  ]
535
579
  }) : (0, react_jsx_runtime.jsxs)("span", {
536
580
  className: TokenQuotaPanel_module_css_default.version,
537
- children: ["v", "0.1.15"]
581
+ children: ["v", "0.1.17"]
538
582
  })]
539
583
  }), (0, react_jsx_runtime.jsx)("div", {
540
584
  className: TokenQuotaPanel_module_css_default.subtitle,
@@ -588,7 +632,7 @@ window.__ModuleLoader__.load({
588
632
  children: fullNotice
589
633
  }),
590
634
  upgrade !== null && !upgradeDismissed && (0, react_jsx_runtime.jsxs)("div", {
591
- className: TokenQuotaPanel_module_css_default.upgradeBanner,
635
+ className: `${TokenQuotaPanel_module_css_default.upgradeBanner}${upgradeFlash ? ` ${TokenQuotaPanel_module_css_default.upgradeBannerFlash}` : ""}`,
592
636
  role: "alert",
593
637
  children: [
594
638
  (0, react_jsx_runtime.jsx)("div", {
@@ -604,11 +648,11 @@ window.__ModuleLoader__.load({
604
648
  children: cmd
605
649
  }), (0, react_jsx_runtime.jsx)("button", {
606
650
  type: "button",
607
- className: TokenQuotaPanel_module_css_default.copyBtn,
651
+ className: `${TokenQuotaPanel_module_css_default.copyBtn}${copiedCmd === cmd ? ` ${TokenQuotaPanel_module_css_default.copyBtnDone}` : ""}`,
608
652
  onClick: () => {
609
- copyToClipboard(cmd);
653
+ copyUpgradeCommand(cmd);
610
654
  },
611
- children: t("copy")
655
+ children: copiedCmd === cmd ? t("copied") : t("copy")
612
656
  })]
613
657
  }, cmd))
614
658
  }),
@@ -855,6 +899,22 @@ window.__ModuleLoader__.load({
855
899
  })
856
900
  ]
857
901
  }),
902
+ (0, react_jsx_runtime.jsxs)("div", {
903
+ className: TokenQuotaPanel_module_css_default.dialogSection,
904
+ children: [(0, react_jsx_runtime.jsxs)("label", {
905
+ className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
906
+ children: [(0, react_jsx_runtime.jsx)("input", {
907
+ type: "checkbox",
908
+ checked: dimWhenIdle,
909
+ onChange: (event) => {
910
+ setDimWhenIdle(event.target.checked);
911
+ }
912
+ }), (0, react_jsx_runtime.jsx)("span", { children: t("dimWhenIdleLabel") })]
913
+ }), (0, react_jsx_runtime.jsx)("div", {
914
+ className: TokenQuotaPanel_module_css_default.dialogHint,
915
+ children: t("dimWhenIdleHint")
916
+ })]
917
+ }),
858
918
  (0, react_jsx_runtime.jsxs)("div", {
859
919
  className: TokenQuotaPanel_module_css_default.dialogSection,
860
920
  children: [(0, react_jsx_runtime.jsxs)("div", {
@@ -1134,8 +1194,11 @@ window.__ModuleLoader__.load({
1134
1194
  upgradeAvailableHint: "有新版本可用",
1135
1195
  upgradeAvailable: "发现新版本 v{version},请在终端执行以下命令升级:",
1136
1196
  copy: "复制",
1197
+ copied: "已复制 ✓",
1137
1198
  upgradeHint: "升级后请重启 dsh web 并刷新浏览器页面",
1138
1199
  checkUpdatesLabel: "自动检查更新",
1200
+ dimWhenIdleLabel: "失焦时窗口透明",
1201
+ dimWhenIdleHint: "鼠标移开面板时降低不透明度,输入文字时更透明;鼠标悬停恢复",
1139
1202
  checkUpdatesNow: "立即检查",
1140
1203
  checkingUpdates: "检查中…",
1141
1204
  checkUpToDate: "已是最新",
@@ -1198,8 +1261,11 @@ window.__ModuleLoader__.load({
1198
1261
  upgradeAvailableHint: "A new version is available",
1199
1262
  upgradeAvailable: "A new version v{version} is available. Run one of the following commands in your terminal to upgrade:",
1200
1263
  copy: "Copy",
1264
+ copied: "Copied ✓",
1201
1265
  upgradeHint: "After upgrading, restart dsh web and refresh the browser",
1202
1266
  checkUpdatesLabel: "Check for updates automatically",
1267
+ dimWhenIdleLabel: "Dim panel when unfocused",
1268
+ dimWhenIdleHint: "Fade the panel out when the pointer is away (more while typing); hover to restore",
1203
1269
  checkUpdatesNow: "Check now",
1204
1270
  checkingUpdates: "Checking…",
1205
1271
  checkUpToDate: "Up to date",
@@ -1341,6 +1407,7 @@ window.__ModuleLoader__.load({
1341
1407
  lastReset = doc?.reset !== void 0 && doc.reset !== null && typeof doc.reset === "object" && typeof doc.reset.offsetHours === "number" ? doc.reset : null;
1342
1408
  bound?.setSettings(lastMonitored, lastOnFull);
1343
1409
  bound?.setCheckUpdates(doc?.checkUpdates ?? true);
1410
+ bound?.setDimWhenIdle(doc?.dimWhenIdle ?? false);
1344
1411
  bound?.setReset(lastReset);
1345
1412
  fetch("/token-quota", { headers: { accept: "application/json" } }).then((response) => {
1346
1413
  if (!response.ok) {
@@ -1406,6 +1473,10 @@ window.__ModuleLoader__.load({
1406
1473
  bound?.setCheckUpdates(enabled);
1407
1474
  scope.set("checkUpdates", enabled);
1408
1475
  };
1476
+ const setDimWhenIdle = (enabled) => {
1477
+ bound?.setDimWhenIdle(enabled);
1478
+ scope.set("dimWhenIdle", enabled);
1479
+ };
1409
1480
  /** Ask the Host to check the registry right now, then refresh the snapshot. */
1410
1481
  const checkUpdatesNow = () => {
1411
1482
  bound?.setCheckingUpdates(true);
@@ -1426,7 +1497,8 @@ window.__ModuleLoader__.load({
1426
1497
  bound?.setUpgrade(snapshot.upgrade ?? null);
1427
1498
  bound?.setUpgradeError(snapshot.upgradeError ?? null);
1428
1499
  actOnFull(snapshot);
1429
- if (snapshot.upgrade === null) bound?.setLastCheckResult(snapshot.upgradeError !== null ? "error" : "up-to-date");
1500
+ if (snapshot.upgrade !== null) bound?.setDialogOpen(false);
1501
+ else bound?.setLastCheckResult(snapshot.upgradeError !== null ? "error" : "up-to-date");
1430
1502
  }
1431
1503
  bound?.setCheckingUpdates(false);
1432
1504
  setTimeout(() => {
@@ -1487,7 +1559,8 @@ window.__ModuleLoader__.load({
1487
1559
  setReset,
1488
1560
  setCheckUpdates,
1489
1561
  checkUpdatesNow,
1490
- clearLogBefore
1562
+ clearLogBefore,
1563
+ setDimWhenIdle
1491
1564
  };
1492
1565
  };
1493
1566
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
package/lib/index.js CHANGED
@@ -163,6 +163,7 @@ const TOKEN_QUOTA_SETTINGS_SCHEMA = z.object({
163
163
  "switchPriority"
164
164
  ]).default("stop"),
165
165
  checkUpdates: z.boolean().default(true),
166
+ dimWhenIdle: z.boolean().default(false),
166
167
  reset: z.any().default(null)
167
168
  });
168
169
  /** Two-digit zero-pad helper. */
@@ -219,7 +220,8 @@ var TokenQuotaService = class TokenQuotaService extends Service {
219
220
  limits: {},
220
221
  monitored: [],
221
222
  onFull: "stop",
222
- checkUpdates: true
223
+ checkUpdates: true,
224
+ dimWhenIdle: false
223
225
  });
224
226
  /** Whether update checks are enabled (mirrors the settings document). */
225
227
  checkUpdates = true;
@@ -266,6 +268,7 @@ var TokenQuotaService = class TokenQuotaService extends Service {
266
268
  monitored: doc.monitored ?? [],
267
269
  onFull: doc.onFull === "switchPriority" ? "switchAll" : doc.onFull ?? "stop",
268
270
  checkUpdates: doc.checkUpdates ?? true,
271
+ dimWhenIdle: doc.dimWhenIdle ?? false,
269
272
  reset: doc.reset ?? void 0
270
273
  };
271
274
  };
@@ -406,7 +409,7 @@ var TokenQuotaService = class TokenQuotaService extends Service {
406
409
  return this.upgradeCheck;
407
410
  }
408
411
  async doRefreshUpgrade() {
409
- const current = "0.1.15";
412
+ const current = "0.1.17";
410
413
  let latest;
411
414
  try {
412
415
  const response = await fetch(REGISTRY_LATEST_URL, {
@@ -18,6 +18,8 @@ export interface TokenQuotaPanelInjected {
18
18
  setReset: (reset: TokenQuotaReset | null) => void;
19
19
  /** Persist whether the Host may check npm for newer versions. */
20
20
  setCheckUpdates: (enabled: boolean) => void;
21
+ /** Persist whether the panel dims while the pointer is away / typing. */
22
+ setDimWhenIdle: (enabled: boolean) => void;
21
23
  /** Ask the Host to check the registry right now, then refresh the snapshot. */
22
24
  checkUpdatesNow: () => void;
23
25
  /** Ask the Host to clear log entries before a date, then refresh the log. */
@@ -30,5 +32,5 @@ export type TokenQuotaPanelComponentProps = PropsRuntime<'shell.overlay'> & Prop
30
32
  * @param props - composed slot props.
31
33
  * @returns the panel element tree.
32
34
  */
33
- export declare function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, useStore, actions, useSessions, }: TokenQuotaPanelComponentProps): import("react").JSX.Element;
35
+ export declare function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, useStore, actions, useSessions, }: TokenQuotaPanelComponentProps): import("react").JSX.Element;
34
36
  //# sourceMappingURL=TokenQuotaPanel.d.ts.map
@@ -86,7 +86,7 @@ function loadPanelPos() {
86
86
  * @param props - composed slot props.
87
87
  * @returns the panel element tree.
88
88
  */
89
- export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, useStore, actions, useSessions, }) {
89
+ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, useStore, actions, useSessions, }) {
90
90
  const [collapsed, setCollapsed] = useState(false);
91
91
  const [drafts, setDrafts] = useState({});
92
92
  const [editingKey, setEditingKey] = useState(null);
@@ -95,6 +95,15 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
95
95
  // so the chat content behind it stays readable; hovering restores it.
96
96
  const [hovered, setHovered] = useState(false);
97
97
  const [inputActive, setInputActive] = useState(false);
98
+ // Copy-button feedback: which command was just copied (label flips to
99
+ // 「已复制」for a moment so the click is perceivable).
100
+ const [copiedCmd, setCopiedCmd] = useState(null);
101
+ const copyTimerRef = useRef(null);
102
+ // Flash state for the upgrade banner: armed when a NEW latest version shows
103
+ // up (either from a manual check or a periodic poll).
104
+ const [upgradeFlash, setUpgradeFlash] = useState(false);
105
+ const flashTimerRef = useRef(null);
106
+ const prevUpgradeRef = useRef(null);
98
107
  // Draggable placements: the panel persists across reloads; the dialogs
99
108
  // start centered (null) and remember where they were dragged to.
100
109
  const [panelPos, setPanelPos] = useState(loadPanelPos);
@@ -119,6 +128,7 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
119
128
  const monitored = useStore(s => s.monitored);
120
129
  const onFull = useStore(s => s.onFull);
121
130
  const checkUpdates = useStore(s => s.checkUpdates);
131
+ const dimWhenIdle = useStore(s => s.dimWhenIdle);
122
132
  const upgrade = useStore(s => s.upgrade);
123
133
  const upgradeDismissed = useStore(s => s.upgradeDismissed);
124
134
  const checkingUpdates = useStore(s => s.checkingUpdates);
@@ -154,6 +164,34 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
154
164
  window.addEventListener('focusin', onFocusIn);
155
165
  return () => { window.removeEventListener('focusin', onFocusIn); };
156
166
  }, []);
167
+ // When a NEW latest version appears (manual check or periodic poll):
168
+ // expand the panel (it may be collapsed), force it opaque, and flash the
169
+ // upgrade banner a few times so the discovery is unmissable.
170
+ useEffect(() => {
171
+ const latest = upgrade?.latestVersion ?? null;
172
+ if (latest !== null && prevUpgradeRef.current !== latest) {
173
+ setCollapsed(false);
174
+ setUpgradeFlash(true);
175
+ if (flashTimerRef.current !== null)
176
+ clearTimeout(flashTimerRef.current);
177
+ flashTimerRef.current = window.setTimeout(() => {
178
+ setUpgradeFlash(false);
179
+ flashTimerRef.current = null;
180
+ }, 2500);
181
+ }
182
+ prevUpgradeRef.current = latest;
183
+ return () => {
184
+ // Keep the timer across re-renders; cleared only on unmount.
185
+ };
186
+ }, [upgrade]);
187
+ useEffect(() => {
188
+ return () => {
189
+ if (flashTimerRef.current !== null)
190
+ clearTimeout(flashTimerRef.current);
191
+ if (copyTimerRef.current !== null)
192
+ clearTimeout(copyTimerRef.current);
193
+ };
194
+ }, []);
157
195
  const rows = useMemo(() => mergeModelRows(groups, snapshot, current), [groups, snapshot, current]);
158
196
  // All directory models, flattened for the monitoring picker.
159
197
  const allModels = useMemo(() => groups.flatMap(group => group.models.map((model) => ({
@@ -197,6 +235,17 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
197
235
  document.body.removeChild(el);
198
236
  }
199
237
  };
238
+ /** Copy an upgrade command and show per-button success feedback. */
239
+ const copyUpgradeCommand = (cmd) => {
240
+ void copyToClipboard(cmd);
241
+ setCopiedCmd(cmd);
242
+ if (copyTimerRef.current !== null)
243
+ clearTimeout(copyTimerRef.current);
244
+ copyTimerRef.current = window.setTimeout(() => {
245
+ setCopiedCmd(null);
246
+ copyTimerRef.current = null;
247
+ }, 1600);
248
+ };
200
249
  // Fetch the usage log whenever the log dialog opens. Kept ABOVE the
201
250
  // collapsed early-return: every hook must run on every render, otherwise
202
251
  // React unmounts the component the moment the panel collapses (the old
@@ -237,12 +286,15 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
237
286
  setLimit(row.key, parsed);
238
287
  };
239
288
  const visibleRows = rows.filter(row => isMonitoredKey(row.key) || row.current);
240
- // The panel dims when the pointer is away; dimming is stronger while the
241
- // user is typing in an input elsewhere so the text behind stays readable.
242
- const panelDimClass = hovered ? '' : inputActive ? css.panelDimStrong : css.panelDim;
289
+ // The panel dims when the pointer is away ONLY when the user opted in
290
+ // (settings → 失焦窗口透明). While the upgrade banner flashes, the panel
291
+ // is forced opaque regardless.
292
+ const panelDimClass = dimWhenIdle
293
+ ? (hovered || upgradeFlash ? '' : inputActive ? css.panelDimStrong : css.panelDim)
294
+ : '';
243
295
  return (_jsxs("div", { ref: panelRef, className: `${css.panel}${panelDimClass !== '' ? ` ${panelDimClass}` : ''}`, style: panelPos !== null ? { left: panelPos.x, top: panelPos.y, right: 'auto' } : undefined, onPointerEnter: () => { setHovered(true); }, onPointerLeave: () => { setHovered(false); }, children: [_jsxs("div", { className: css.header, onPointerDown: (event) => { beginDrag(event, panelRef.current, applyPanelPos); }, children: [_jsxs("div", { className: css.headerText, children: [_jsxs("div", { className: css.title, children: [t('title'), __TOKEN_QUOTA_VERSION__ !== undefined && __TOKEN_QUOTA_VERSION__ !== '' && (upgrade !== null
244
296
  ? (_jsxs("span", { className: `${css.version} ${css.versionNew}`, title: t('upgradeAvailableHint'), children: ["v", __TOKEN_QUOTA_VERSION__, " \u2192 v", upgrade.latestVersion] }))
245
- : (_jsxs("span", { className: css.version, children: ["v", __TOKEN_QUOTA_VERSION__] })))] }), _jsx("div", { className: css.subtitle, children: t('subtitle') })] }), _jsxs("div", { className: css.headerActions, onPointerDown: (event) => { event.stopPropagation(); }, children: [_jsx("button", { type: "button", className: css.settingsBtn, onClick: () => { actions.setLogOpen(true); }, children: t('logs') }), _jsx("button", { type: "button", className: css.settingsBtn, onClick: openSettings, children: t('settings') }), _jsx("button", { type: "button", className: css.collapse, onClick: () => { setCollapsed(true); }, children: t('collapse') })] })] }), _jsxs("div", { className: css.body, children: [loading && _jsx("div", { className: css.notice, children: t('loading') }), error !== null && _jsx("div", { className: css.noticeError, children: error }), fullNotice !== null && (_jsx("div", { className: css.fullNotice, role: "alert", children: fullNotice })), upgrade !== null && !upgradeDismissed && (_jsxs("div", { className: css.upgradeBanner, role: "alert", children: [_jsx("div", { className: css.upgradeBannerText, children: t('upgradeAvailable').replace('{version}', upgrade.latestVersion) }), _jsx("div", { className: css.upgradeCommands, children: upgrade.commands.map(cmd => (_jsxs("div", { className: css.upgradeCommandRow, children: [_jsx("code", { className: css.upgradeCommand, children: cmd }), _jsx("button", { type: "button", className: css.copyBtn, onClick: () => { void copyToClipboard(cmd); }, children: t('copy') })] }, cmd))) }), _jsx("div", { className: css.upgradeHint, children: t('upgradeHint') }), _jsx("button", { type: "button", className: css.upgradeClose, onClick: () => { actions.setUpgradeDismissed(true); }, "aria-label": t('close'), children: "\u00D7" })] })), !loading && error === null && visibleRows.length === 0 && (_jsx("div", { className: css.notice, children: monitored !== null && monitored.length === 0 ? t('noMonitored') : t('waiting') })), visibleRows.map((row) => {
297
+ : (_jsxs("span", { className: css.version, children: ["v", __TOKEN_QUOTA_VERSION__] })))] }), _jsx("div", { className: css.subtitle, children: t('subtitle') })] }), _jsxs("div", { className: css.headerActions, onPointerDown: (event) => { event.stopPropagation(); }, children: [_jsx("button", { type: "button", className: css.settingsBtn, onClick: () => { actions.setLogOpen(true); }, children: t('logs') }), _jsx("button", { type: "button", className: css.settingsBtn, onClick: openSettings, children: t('settings') }), _jsx("button", { type: "button", className: css.collapse, onClick: () => { setCollapsed(true); }, children: t('collapse') })] })] }), _jsxs("div", { className: css.body, children: [loading && _jsx("div", { className: css.notice, children: t('loading') }), error !== null && _jsx("div", { className: css.noticeError, children: error }), fullNotice !== null && (_jsx("div", { className: css.fullNotice, role: "alert", children: fullNotice })), upgrade !== null && !upgradeDismissed && (_jsxs("div", { className: `${css.upgradeBanner}${upgradeFlash ? ` ${css.upgradeBannerFlash}` : ''}`, role: "alert", children: [_jsx("div", { className: css.upgradeBannerText, children: t('upgradeAvailable').replace('{version}', upgrade.latestVersion) }), _jsx("div", { className: css.upgradeCommands, children: upgrade.commands.map(cmd => (_jsxs("div", { className: css.upgradeCommandRow, children: [_jsx("code", { className: css.upgradeCommand, children: cmd }), _jsx("button", { type: "button", className: `${css.copyBtn}${copiedCmd === cmd ? ` ${css.copyBtnDone}` : ''}`, onClick: () => { copyUpgradeCommand(cmd); }, children: copiedCmd === cmd ? t('copied') : t('copy') })] }, cmd))) }), _jsx("div", { className: css.upgradeHint, children: t('upgradeHint') }), _jsx("button", { type: "button", className: css.upgradeClose, onClick: () => { actions.setUpgradeDismissed(true); }, "aria-label": t('close'), children: "\u00D7" })] })), !loading && error === null && visibleRows.length === 0 && (_jsx("div", { className: css.notice, children: monitored !== null && monitored.length === 0 ? t('noMonitored') : t('waiting') })), visibleRows.map((row) => {
246
298
  const state = barStateOf(row);
247
299
  const pct = row.limit > 0 ? Math.min(100, Math.round((row.used / row.limit) * 100)) : 0;
248
300
  const barClass = state === 'over'
@@ -282,7 +334,7 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
282
334
  hour: reset?.hour ?? 0,
283
335
  minute: Number(event.target.value),
284
336
  });
285
- }, children: Array.from({ length: 12 }, (_, i) => i * 5).map(minute => (_jsxs("option", { value: minute, children: [String(minute).padStart(2, '0'), " \u5206"] }, minute))) })] })] }), _jsxs("div", { className: css.dialogSection, children: [_jsxs("div", { className: css.checkUpdatesRow, children: [_jsxs("label", { className: css.checkUpdatesLabel, children: [_jsx("input", { type: "checkbox", checked: checkUpdates, onChange: (event) => { setCheckUpdates(event.target.checked); } }), _jsx("span", { children: t('checkUpdatesLabel') })] }), _jsx("button", { type: "button", className: css.checkBtn, disabled: checkingUpdates, onClick: checkUpdatesNow, children: checkingUpdates
337
+ }, children: Array.from({ length: 12 }, (_, i) => i * 5).map(minute => (_jsxs("option", { value: minute, children: [String(minute).padStart(2, '0'), " \u5206"] }, minute))) })] })] }), _jsxs("div", { className: css.dialogSection, children: [_jsxs("label", { className: css.checkUpdatesLabel, children: [_jsx("input", { type: "checkbox", checked: dimWhenIdle, onChange: (event) => { setDimWhenIdle(event.target.checked); } }), _jsx("span", { children: t('dimWhenIdleLabel') })] }), _jsx("div", { className: css.dialogHint, children: t('dimWhenIdleHint') })] }), _jsxs("div", { className: css.dialogSection, children: [_jsxs("div", { className: css.checkUpdatesRow, children: [_jsxs("label", { className: css.checkUpdatesLabel, children: [_jsx("input", { type: "checkbox", checked: checkUpdates, onChange: (event) => { setCheckUpdates(event.target.checked); } }), _jsx("span", { children: t('checkUpdatesLabel') })] }), _jsx("button", { type: "button", className: css.checkBtn, disabled: checkingUpdates, onClick: checkUpdatesNow, children: checkingUpdates
286
338
  ? t('checkingUpdates')
287
339
  : lastCheckResult === 'up-to-date'
288
340
  ? t('checkUpToDate')
@@ -171,6 +171,7 @@ export function apply(ctx) {
171
171
  : null;
172
172
  bound?.setSettings(lastMonitored, lastOnFull);
173
173
  bound?.setCheckUpdates(doc?.checkUpdates ?? true);
174
+ bound?.setDimWhenIdle(doc?.dimWhenIdle ?? false);
174
175
  bound?.setReset(lastReset);
175
176
  void fetch('/token-quota', { headers: { accept: 'application/json' } }).then((response) => {
176
177
  if (!response.ok) {
@@ -234,6 +235,10 @@ export function apply(ctx) {
234
235
  bound?.setCheckUpdates(enabled);
235
236
  void scope.set('checkUpdates', enabled);
236
237
  };
238
+ const setDimWhenIdle = (enabled) => {
239
+ bound?.setDimWhenIdle(enabled);
240
+ void scope.set('dimWhenIdle', enabled);
241
+ };
237
242
  /** Ask the Host to check the registry right now, then refresh the snapshot. */
238
243
  const checkUpdatesNow = () => {
239
244
  bound?.setCheckingUpdates(true);
@@ -254,7 +259,12 @@ export function apply(ctx) {
254
259
  bound?.setUpgrade(snapshot.upgrade ?? null);
255
260
  bound?.setUpgradeError(snapshot.upgradeError ?? null);
256
261
  actOnFull(snapshot);
257
- if (snapshot.upgrade === null) {
262
+ if (snapshot.upgrade !== null) {
263
+ // A new version was found: close the settings dialog so the
264
+ // flashing upgrade banner in the panel is immediately visible.
265
+ bound?.setDialogOpen(false);
266
+ }
267
+ else {
258
268
  // No newer version known: distinguish "checked OK, up to date"
259
269
  // from "check failed" via the host-provided error message.
260
270
  bound?.setLastCheckResult(snapshot.upgradeError !== null ? 'error' : 'up-to-date');
@@ -315,7 +325,7 @@ export function apply(ctx) {
315
325
  bound = actions;
316
326
  return {
317
327
  load, setLimit, selectModel, setMonitored, setOnFull, setReset,
318
- setCheckUpdates, checkUpdatesNow, clearLogBefore,
328
+ setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle,
319
329
  };
320
330
  };
321
331
  ctx.slots.inject('shell.overlay', () => ctx.slots.register({
@@ -61,8 +61,11 @@ export declare const zh: {
61
61
  upgradeAvailableHint: string;
62
62
  upgradeAvailable: string;
63
63
  copy: string;
64
+ copied: string;
64
65
  upgradeHint: string;
65
66
  checkUpdatesLabel: string;
67
+ dimWhenIdleLabel: string;
68
+ dimWhenIdleHint: string;
66
69
  checkUpdatesNow: string;
67
70
  checkingUpdates: string;
68
71
  checkUpToDate: string;
@@ -127,8 +130,11 @@ export declare const en: {
127
130
  upgradeAvailableHint: string;
128
131
  upgradeAvailable: string;
129
132
  copy: string;
133
+ copied: string;
130
134
  upgradeHint: string;
131
135
  checkUpdatesLabel: string;
136
+ dimWhenIdleLabel: string;
137
+ dimWhenIdleHint: string;
132
138
  checkUpdatesNow: string;
133
139
  checkingUpdates: string;
134
140
  checkUpToDate: string;
@@ -61,8 +61,11 @@ export const zh = {
61
61
  upgradeAvailableHint: '有新版本可用',
62
62
  upgradeAvailable: '发现新版本 v{version},请在终端执行以下命令升级:',
63
63
  copy: '复制',
64
+ copied: '已复制 ✓',
64
65
  upgradeHint: '升级后请重启 dsh web 并刷新浏览器页面',
65
66
  checkUpdatesLabel: '自动检查更新',
67
+ dimWhenIdleLabel: '失焦时窗口透明',
68
+ dimWhenIdleHint: '鼠标移开面板时降低不透明度,输入文字时更透明;鼠标悬停恢复',
66
69
  checkUpdatesNow: '立即检查',
67
70
  checkingUpdates: '检查中…',
68
71
  checkUpToDate: '已是最新',
@@ -125,8 +128,11 @@ export const en = {
125
128
  upgradeAvailableHint: 'A new version is available',
126
129
  upgradeAvailable: 'A new version v{version} is available. Run one of the following commands in your terminal to upgrade:',
127
130
  copy: 'Copy',
131
+ copied: 'Copied ✓',
128
132
  upgradeHint: 'After upgrading, restart dsh web and refresh the browser',
129
133
  checkUpdatesLabel: 'Check for updates automatically',
134
+ dimWhenIdleLabel: 'Dim panel when unfocused',
135
+ dimWhenIdleHint: 'Fade the panel out when the pointer is away (more while typing); hover to restore',
130
136
  checkUpdatesNow: 'Check now',
131
137
  checkingUpdates: 'Checking…',
132
138
  checkUpToDate: 'Up to date',
@@ -48,6 +48,8 @@ export interface TokenQuotaPanelState {
48
48
  onFull: TokenQuotaFullAction;
49
49
  /** Whether the Host is allowed to check npm for newer versions. */
50
50
  checkUpdates: boolean;
51
+ /** Whether the panel dims while the pointer is away / while typing. */
52
+ dimWhenIdle: boolean;
51
53
  /** Latest upgrade availability from the Host; null = up to date or unknown. */
52
54
  upgrade: TokenQuotaUpgrade | null;
53
55
  /** Whether the one-shot upgrade banner was dismissed for the current latest version. */
@@ -89,6 +91,7 @@ export type TokenQuotaPanelActions = {
89
91
  setError: (d: TokenQuotaPanelState, error: string | null) => void;
90
92
  setSettings: (d: TokenQuotaPanelState, monitored: string[] | null, onFull: TokenQuotaFullAction) => void;
91
93
  setCheckUpdates: (d: TokenQuotaPanelState, checkUpdates: boolean) => void;
94
+ setDimWhenIdle: (d: TokenQuotaPanelState, dimWhenIdle: boolean) => void;
92
95
  setUpgrade: (d: TokenQuotaPanelState, upgrade: TokenQuotaUpgrade | null) => void;
93
96
  setUpgradeDismissed: (d: TokenQuotaPanelState, dismissed: boolean) => void;
94
97
  setCheckingUpdates: (d: TokenQuotaPanelState, checking: boolean) => void;
@@ -27,6 +27,7 @@ export function createTokenQuotaPanelStore() {
27
27
  monitored: null,
28
28
  onFull: 'stop',
29
29
  checkUpdates: true,
30
+ dimWhenIdle: false,
30
31
  upgrade: null,
31
32
  upgradeDismissed: false,
32
33
  checkingUpdates: false,
@@ -51,13 +52,23 @@ export function createTokenQuotaPanelStore() {
51
52
  setError: (d, error) => { d.error = error; },
52
53
  setSettings: (d, monitored, onFull) => { d.monitored = monitored; d.onFull = onFull; },
53
54
  setCheckUpdates: (d, checkUpdates) => { d.checkUpdates = checkUpdates; },
55
+ setDimWhenIdle: (d, dimWhenIdle) => { d.dimWhenIdle = dimWhenIdle; },
54
56
  setUpgrade: (d, upgrade) => {
57
+ // Guard against host/client version skew (e.g. the host process still
58
+ // runs an older bundle after an upgrade): when the reported "latest"
59
+ // equals the client's own version, there is nothing to upgrade to.
60
+ // This prevents a confusing `v0.1.16 → v0.1.16` banner.
61
+ const normalized = upgrade !== null
62
+ && upgrade.latestVersion !== ''
63
+ && upgrade.latestVersion !== __TOKEN_QUOTA_VERSION__
64
+ ? upgrade
65
+ : null;
55
66
  // A new latest version re-arms the one-shot banner; the same version
56
67
  // stays dismissed until the user dismisses it again (or it changes).
57
- if (d.upgrade === null || d.upgrade.latestVersion !== upgrade?.latestVersion) {
68
+ if (d.upgrade === null || d.upgrade.latestVersion !== normalized?.latestVersion) {
58
69
  d.upgradeDismissed = false;
59
70
  }
60
- d.upgrade = upgrade;
71
+ d.upgrade = normalized;
61
72
  },
62
73
  setUpgradeDismissed: (d, dismissed) => { d.upgradeDismissed = dismissed; },
63
74
  setCheckingUpdates: (d, checking) => { d.checkingUpdates = checking; },
@@ -156,6 +156,8 @@ const TOKEN_QUOTA_SETTINGS_SCHEMA = z.object({
156
156
  // without reconfiguring.
157
157
  onFull: z.union(['stop', 'switchQuota', 'switchAll', 'switchPriority']).default('stop'),
158
158
  checkUpdates: z.boolean().default(true),
159
+ // Client-side appearance preference; the host only stores it.
160
+ dimWhenIdle: z.boolean().default(false),
159
161
  // `reset` is a user-facing preference outside the validated surface: the
160
162
  // panel writes it and the host validates the shape at runtime. `z.any` with
161
163
  // a null default keeps it out of the strict fields above.
@@ -220,7 +222,7 @@ export class TokenQuotaService extends Service {
220
222
  history = {};
221
223
  limits = {};
222
224
  monitored = undefined;
223
- settingsSource = () => ({ limits: {}, monitored: [], onFull: 'stop', checkUpdates: true });
225
+ settingsSource = () => ({ limits: {}, monitored: [], onFull: 'stop', checkUpdates: true, dimWhenIdle: false });
224
226
  /** Whether update checks are enabled (mirrors the settings document). */
225
227
  checkUpdates = true;
226
228
  /** Cached upgrade availability; recomputed by {@link refreshUpgrade}. */
@@ -275,6 +277,7 @@ export class TokenQuotaService extends Service {
275
277
  // capped models first, then uncapped — both monitored-only.
276
278
  onFull: doc.onFull === 'switchPriority' ? 'switchAll' : (doc.onFull ?? 'stop'),
277
279
  checkUpdates: doc.checkUpdates ?? true,
280
+ dimWhenIdle: doc.dimWhenIdle ?? false,
278
281
  reset: doc.reset ?? undefined,
279
282
  };
280
283
  };
@@ -68,6 +68,12 @@ export interface TokenQuotaSettings {
68
68
  * and surfaces an update notice in the panel. `true` by default.
69
69
  */
70
70
  checkUpdates: boolean;
71
+ /**
72
+ * Client-side appearance preference: dim the floating panel while the
73
+ * pointer is away (and more while typing) so the content behind stays
74
+ * readable. `false` by default — the panel stays fully opaque.
75
+ */
76
+ dimWhenIdle: boolean;
71
77
  /**
72
78
  * Daily reset moment (timezone + clock time). Absent = machine-local
73
79
  * midnight (the historical behaviour).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jxgame2020/dsh-token-quota",
3
3
  "description": "Daily per-model token quota accounting, enforcement, and a floating quota panel for the DeepSeek Harness — a fully self-contained plugin (no core-Harness wiring, no generated RPC contract)",
4
- "version": "0.1.15",
4
+ "version": "0.1.17",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },