@jxgame2020/dsh-token-quota 0.1.27 → 0.1.29
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 +143 -188
- package/lib/index.js +1 -1
- package/lib/types/client/TokenQuotaPanel.js +1 -1
- package/lib/types/client/index.js +5 -0
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/locales.js +4 -0
- package/lib/types/client/music.d.ts +27 -27
- package/lib/types/client/music.js +81 -129
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -95,7 +95,7 @@ window.__ModuleLoader__.load({
|
|
|
95
95
|
d.musicOnlyCurrentSession = only;
|
|
96
96
|
},
|
|
97
97
|
setUpgrade: (d, upgrade) => {
|
|
98
|
-
const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.
|
|
98
|
+
const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.29" ? upgrade : null;
|
|
99
99
|
if (d.upgrade === null || d.upgrade.latestVersion !== normalized?.latestVersion) d.upgradeDismissed = false;
|
|
100
100
|
d.upgrade = normalized;
|
|
101
101
|
},
|
|
@@ -191,7 +191,7 @@ window.__ModuleLoader__.load({
|
|
|
191
191
|
}
|
|
192
192
|
//#endregion
|
|
193
193
|
//#region \0dsh-css:/Users/jxgame/Desktop/works/deepseek-harness-package/dsh-token-quota/packages/token-quota/src/client/TokenQuotaPanel.module.css.mjs
|
|
194
|
-
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_settingsBtnActive{color:var(--dsw-alias-brand-primary)}.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;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}";
|
|
194
|
+
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_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;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}";
|
|
195
195
|
const tagId = "@jxgame2020/dsh-token-quota/TokenQuotaPanel.module.css";
|
|
196
196
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
197
197
|
const tag = document.createElement("style");
|
|
@@ -201,92 +201,92 @@ window.__ModuleLoader__.load({
|
|
|
201
201
|
document.head.appendChild(tag);
|
|
202
202
|
}
|
|
203
203
|
var TokenQuotaPanel_module_css_default = {
|
|
204
|
-
"
|
|
205
|
-
"tab": "NPLLIq_tab",
|
|
206
|
-
"rowHeader": "NPLLIq_rowHeader",
|
|
207
|
-
"logTable": "NPLLIq_logTable",
|
|
208
|
-
"logPagerInfo": "NPLLIq_logPagerInfo",
|
|
209
|
-
"copyBtn": "NPLLIq_copyBtn",
|
|
210
|
-
"dialogBody": "NPLLIq_dialogBody",
|
|
211
|
-
"logClearDialog": "NPLLIq_logClearDialog",
|
|
212
|
-
"input": "NPLLIq_input",
|
|
213
|
-
"monitorHint": "NPLLIq_monitorHint",
|
|
214
|
-
"monitorName": "NPLLIq_monitorName",
|
|
215
|
-
"checkErrorHint": "NPLLIq_checkErrorHint",
|
|
216
|
-
"fillWarn": "NPLLIq_fillWarn",
|
|
217
|
-
"dialogBtn": "NPLLIq_dialogBtn",
|
|
204
|
+
"controls": "NPLLIq_controls",
|
|
218
205
|
"logModelCol": "NPLLIq_logModelCol",
|
|
219
|
-
"
|
|
220
|
-
"
|
|
206
|
+
"resetRow": "NPLLIq_resetRow",
|
|
207
|
+
"noticeError": "NPLLIq_noticeError",
|
|
208
|
+
"selectBtn": "NPLLIq_selectBtn",
|
|
209
|
+
"monitorList": "NPLLIq_monitorList",
|
|
210
|
+
"upgradeCommandRow": "NPLLIq_upgradeCommandRow",
|
|
211
|
+
"dialogBtn": "NPLLIq_dialogBtn",
|
|
212
|
+
"logDayCol": "NPLLIq_logDayCol",
|
|
213
|
+
"fillIdle": "NPLLIq_fillIdle",
|
|
214
|
+
"logPagerBtn": "NPLLIq_logPagerBtn",
|
|
215
|
+
"panel": "NPLLIq_panel",
|
|
221
216
|
"subtitle": "NPLLIq_subtitle",
|
|
222
|
-
"
|
|
217
|
+
"notice": "NPLLIq_notice",
|
|
218
|
+
"settingsBtnActive": "NPLLIq_settingsBtnActive",
|
|
219
|
+
"gearBtn": "NPLLIq_gearBtn",
|
|
220
|
+
"panelDimStrong": "NPLLIq_panelDimStrong",
|
|
223
221
|
"logClearDate": "NPLLIq_logClearDate",
|
|
224
|
-
"
|
|
225
|
-
"
|
|
226
|
-
"
|
|
222
|
+
"settingsBtn": "NPLLIq_settingsBtn",
|
|
223
|
+
"copyBtn": "NPLLIq_copyBtn",
|
|
224
|
+
"logPager": "NPLLIq_logPager",
|
|
225
|
+
"tab": "NPLLIq_tab",
|
|
226
|
+
"tabIcon": "NPLLIq_tabIcon",
|
|
227
227
|
"dialog": "NPLLIq_dialog",
|
|
228
|
-
"
|
|
229
|
-
"dialogHeader": "NPLLIq_dialogHeader",
|
|
228
|
+
"upgradeCommands": "NPLLIq_upgradeCommands",
|
|
230
229
|
"rowMeta": "NPLLIq_rowMeta",
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"dialogLabel": "NPLLIq_dialogLabel",
|
|
234
|
-
"logPagerBtn": "NPLLIq_logPagerBtn",
|
|
235
|
-
"logClearBtn": "NPLLIq_logClearBtn",
|
|
236
|
-
"noticeError": "NPLLIq_noticeError",
|
|
237
|
-
"logUsedCol": "NPLLIq_logUsedCol",
|
|
238
|
-
"panelDim": "NPLLIq_panelDim",
|
|
239
|
-
"body": "NPLLIq_body",
|
|
240
|
-
"dialogClose": "NPLLIq_dialogClose",
|
|
241
|
-
"selectBtn": "NPLLIq_selectBtn",
|
|
242
|
-
"dialogOverlay": "NPLLIq_dialogOverlay",
|
|
243
|
-
"header": "NPLLIq_header",
|
|
244
|
-
"dialogActions": "NPLLIq_dialogActions",
|
|
245
|
-
"logDayCol": "NPLLIq_logDayCol",
|
|
230
|
+
"version": "NPLLIq_version",
|
|
231
|
+
"checkUpdatesRow": "NPLLIq_checkUpdatesRow",
|
|
246
232
|
"dialogTitle": "NPLLIq_dialogTitle",
|
|
247
233
|
"headerText": "NPLLIq_headerText",
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
"panel": "NPLLIq_panel",
|
|
234
|
+
"dialogActions": "NPLLIq_dialogActions",
|
|
235
|
+
"logDialog": "NPLLIq_logDialog",
|
|
251
236
|
"fillOver": "NPLLIq_fillOver",
|
|
252
|
-
"
|
|
253
|
-
"
|
|
254
|
-
"logPageSizeSelect": "NPLLIq_logPageSizeSelect",
|
|
237
|
+
"monitorRow": "NPLLIq_monitorRow",
|
|
238
|
+
"upgradeBanner": "NPLLIq_upgradeBanner",
|
|
255
239
|
"dialogHint": "NPLLIq_dialogHint",
|
|
256
|
-
"
|
|
257
|
-
"
|
|
258
|
-
"
|
|
259
|
-
"
|
|
240
|
+
"dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
|
|
241
|
+
"logClearDialog": "NPLLIq_logClearDialog",
|
|
242
|
+
"logHeader": "NPLLIq_logHeader",
|
|
243
|
+
"upgradeHint": "NPLLIq_upgradeHint",
|
|
244
|
+
"row": "NPLLIq_row",
|
|
260
245
|
"logToolbar": "NPLLIq_logToolbar",
|
|
261
|
-
"tabIcon": "NPLLIq_tabIcon",
|
|
262
|
-
"radioRow": "NPLLIq_radioRow",
|
|
263
|
-
"settingsBtnActive": "NPLLIq_settingsBtnActive",
|
|
264
|
-
"copyBtnDone": "NPLLIq_copyBtnDone",
|
|
265
|
-
"headerActions": "NPLLIq_headerActions",
|
|
266
|
-
"dialogSection": "NPLLIq_dialogSection",
|
|
267
|
-
"upgradeBanner": "NPLLIq_upgradeBanner",
|
|
268
|
-
"logScroll": "NPLLIq_logScroll",
|
|
269
|
-
"resetSelect": "NPLLIq_resetSelect",
|
|
270
246
|
"logSearch": "NPLLIq_logSearch",
|
|
247
|
+
"switch": "NPLLIq_switch",
|
|
271
248
|
"checkBtn": "NPLLIq_checkBtn",
|
|
272
|
-
"
|
|
273
|
-
"
|
|
274
|
-
"
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
"
|
|
249
|
+
"dialogSection": "NPLLIq_dialogSection",
|
|
250
|
+
"bar": "NPLLIq_bar",
|
|
251
|
+
"logPagerPages": "NPLLIq_logPagerPages",
|
|
252
|
+
"input": "NPLLIq_input",
|
|
253
|
+
"rowHeader": "NPLLIq_rowHeader",
|
|
254
|
+
"copyBtnDone": "NPLLIq_copyBtnDone",
|
|
255
|
+
"dialogClose": "NPLLIq_dialogClose",
|
|
256
|
+
"logScroll": "NPLLIq_logScroll",
|
|
257
|
+
"versionNew": "NPLLIq_versionNew",
|
|
258
|
+
"header": "NPLLIq_header",
|
|
259
|
+
"upgradeCommand": "NPLLIq_upgradeCommand",
|
|
278
260
|
"logPagerControls": "NPLLIq_logPagerControls",
|
|
279
|
-
"
|
|
261
|
+
"currentBadge": "NPLLIq_currentBadge",
|
|
262
|
+
"tabLabel": "NPLLIq_tabLabel",
|
|
263
|
+
"logPageSizeSelect": "NPLLIq_logPageSizeSelect",
|
|
264
|
+
"upgradeBannerText": "NPLLIq_upgradeBannerText",
|
|
265
|
+
"logClearBtn": "NPLLIq_logClearBtn",
|
|
266
|
+
"fullNotice": "NPLLIq_fullNotice",
|
|
267
|
+
"upgradeClose": "NPLLIq_upgradeClose",
|
|
268
|
+
"dialogBody": "NPLLIq_dialogBody",
|
|
269
|
+
"resetSelect": "NPLLIq_resetSelect",
|
|
280
270
|
"rowName": "NPLLIq_rowName",
|
|
281
|
-
"
|
|
282
|
-
"
|
|
271
|
+
"checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
|
|
272
|
+
"logUsedCol": "NPLLIq_logUsedCol",
|
|
273
|
+
"collapse": "NPLLIq_collapse",
|
|
274
|
+
"dialogOverlay": "NPLLIq_dialogOverlay",
|
|
275
|
+
"radioRow": "NPLLIq_radioRow",
|
|
276
|
+
"dialogHeader": "NPLLIq_dialogHeader",
|
|
283
277
|
"title": "NPLLIq_title",
|
|
284
|
-
"
|
|
285
|
-
"
|
|
286
|
-
"
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
278
|
+
"logTable": "NPLLIq_logTable",
|
|
279
|
+
"save": "NPLLIq_save",
|
|
280
|
+
"checkErrorHint": "NPLLIq_checkErrorHint",
|
|
281
|
+
"upgradeBannerFlash": "NPLLIq_upgradeBannerFlash",
|
|
282
|
+
"dialogLabel": "NPLLIq_dialogLabel",
|
|
283
|
+
"logPagerInfo": "NPLLIq_logPagerInfo",
|
|
284
|
+
"panelDim": "NPLLIq_panelDim",
|
|
285
|
+
"fillWarn": "NPLLIq_fillWarn",
|
|
286
|
+
"monitorHint": "NPLLIq_monitorHint",
|
|
287
|
+
"body": "NPLLIq_body",
|
|
288
|
+
"headerActions": "NPLLIq_headerActions",
|
|
289
|
+
"monitorName": "NPLLIq_monitorName"
|
|
290
290
|
};
|
|
291
291
|
//#endregion
|
|
292
292
|
//#region lib/types/client/TokenQuotaPanel.js
|
|
@@ -592,13 +592,13 @@ window.__ModuleLoader__.load({
|
|
|
592
592
|
title: t("upgradeAvailableHint"),
|
|
593
593
|
children: [
|
|
594
594
|
"v",
|
|
595
|
-
"0.1.
|
|
595
|
+
"0.1.29",
|
|
596
596
|
" → v",
|
|
597
597
|
upgrade.latestVersion
|
|
598
598
|
]
|
|
599
599
|
}) : (0, react_jsx_runtime.jsxs)("span", {
|
|
600
600
|
className: TokenQuotaPanel_module_css_default.version,
|
|
601
|
-
children: ["v", "0.1.
|
|
601
|
+
children: ["v", "0.1.29"]
|
|
602
602
|
})]
|
|
603
603
|
}), (0, react_jsx_runtime.jsx)("div", {
|
|
604
604
|
className: TokenQuotaPanel_module_css_default.subtitle,
|
|
@@ -612,12 +612,13 @@ window.__ModuleLoader__.load({
|
|
|
612
612
|
children: [
|
|
613
613
|
(0, react_jsx_runtime.jsx)("button", {
|
|
614
614
|
type: "button",
|
|
615
|
+
"aria-pressed": musicEnabled,
|
|
615
616
|
className: `${TokenQuotaPanel_module_css_default.settingsBtn}${musicEnabled ? ` ${TokenQuotaPanel_module_css_default.settingsBtnActive}` : ""}`,
|
|
616
|
-
title: t("
|
|
617
|
+
title: `${t("musicLabel")}:${musicEnabled ? t("musicOn") : t("musicOff")}`,
|
|
617
618
|
onClick: () => {
|
|
618
619
|
setMusicEnabled(!musicEnabled);
|
|
619
620
|
},
|
|
620
|
-
children:
|
|
621
|
+
children: "♪"
|
|
621
622
|
}),
|
|
622
623
|
(0, react_jsx_runtime.jsx)("button", {
|
|
623
624
|
type: "button",
|
|
@@ -1235,21 +1236,25 @@ window.__ModuleLoader__.load({
|
|
|
1235
1236
|
* 古风 live-composed soundtrack driven by host actions.
|
|
1236
1237
|
*
|
|
1237
1238
|
* Design: when a request starts the engine composes a *piece* — a guzheng-like
|
|
1238
|
-
* plucked-string voice
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1239
|
+
* plucked-string voice (Web Audio synthesis; the MIDI output is switched to
|
|
1240
|
+
* the Koto program so hardware synths match). The piece plays phrase after
|
|
1241
|
+
* phrase so it stays alive while the model thinks or streams; incoming events
|
|
1242
|
+
* steer the piece instead of interrupting it: a step change rotates the mode
|
|
1243
|
+
* centre and inserts a fast 过门 (fill); tool calls get a high plink; the
|
|
1244
|
+
* ending gets a dizi-like breath note and a descending glissando.
|
|
1245
|
+
*
|
|
1246
|
+
* **No sustained tones by design.** Every voice is a decaying pluck (≤1.6s)
|
|
1247
|
+
* or a fixed-duration breath note — there is no drone/pad oscillator that
|
|
1248
|
+
* depends on a later stop event. A lost SSE event can at worst leave the
|
|
1249
|
+
* phrase chain running, and the stream watchdog below stops that too, so a
|
|
1250
|
+
* hanging "呜呜" hum is physically impossible.
|
|
1245
1251
|
*
|
|
1246
|
-
* Anti-runaway guards
|
|
1252
|
+
* Anti-runaway guards:
|
|
1247
1253
|
* - the host sends an SSE heartbeat every 5s; if it stops arriving the whole
|
|
1248
|
-
* engine
|
|
1249
|
-
* - every MIDI NoteOn is registered and swept;
|
|
1254
|
+
* engine stops (a dead stream means no stop event will ever come),
|
|
1255
|
+
* - every MIDI NoteOn is registered and swept; stops also send
|
|
1250
1256
|
* All Notes Off (CC 123),
|
|
1251
|
-
* - Web Audio
|
|
1252
|
-
* explicitly stopped.
|
|
1257
|
+
* - every Web Audio oscillator is explicitly stopped at envelope end.
|
|
1253
1258
|
*
|
|
1254
1259
|
* @module @jxgame2020/dsh-token-quota/music
|
|
1255
1260
|
*/
|
|
@@ -1489,11 +1494,11 @@ window.__ModuleLoader__.load({
|
|
|
1489
1494
|
playing = false;
|
|
1490
1495
|
tonicIndex = 0;
|
|
1491
1496
|
phraseIndex = 0;
|
|
1497
|
+
/** True when the next piece start should play the full intro glissando. */
|
|
1498
|
+
introPending = true;
|
|
1499
|
+
/** Session the current piece belongs to (turn/start carries it). */
|
|
1500
|
+
sessionId;
|
|
1492
1501
|
phraseTimer;
|
|
1493
|
-
droneOscs = [];
|
|
1494
|
-
droneGain = null;
|
|
1495
|
-
droneFilter = null;
|
|
1496
|
-
droneActive = false;
|
|
1497
1502
|
lastStreamActivity = 0;
|
|
1498
1503
|
watchdogTimer;
|
|
1499
1504
|
activeMidiNotes = /* @__PURE__ */ new Set();
|
|
@@ -1558,15 +1563,24 @@ window.__ModuleLoader__.load({
|
|
|
1558
1563
|
markStreamActivity() {
|
|
1559
1564
|
this.lastStreamActivity = Date.now();
|
|
1560
1565
|
}
|
|
1566
|
+
/** Stop any playing piece — e.g. when the panel switches sessions. */
|
|
1567
|
+
stop() {
|
|
1568
|
+
this.stopPiece(false);
|
|
1569
|
+
}
|
|
1561
1570
|
/** Handle one incoming host action and steer the piece. */
|
|
1562
1571
|
onAction(action) {
|
|
1563
1572
|
if (!this.enabled || this.audio === null) return;
|
|
1564
1573
|
const base = TokenQuotaMusic.BASE_MIDI;
|
|
1565
1574
|
switch (action.type) {
|
|
1566
1575
|
case "turn/start":
|
|
1567
|
-
this.stopPiece(false);
|
|
1576
|
+
if (this.sessionId !== void 0 && this.sessionId !== action.sessionId) this.stopPiece(false);
|
|
1577
|
+
this.sessionId = action.sessionId;
|
|
1578
|
+
this.stopMelody();
|
|
1579
|
+
this.playing = false;
|
|
1568
1580
|
this.tonicIndex = 0;
|
|
1569
1581
|
this.phraseIndex = 0;
|
|
1582
|
+
this.introPending = true;
|
|
1583
|
+
this.startPiece();
|
|
1570
1584
|
break;
|
|
1571
1585
|
case "request/header":
|
|
1572
1586
|
if (!this.playing) this.startPiece();
|
|
@@ -1575,9 +1589,10 @@ window.__ModuleLoader__.load({
|
|
|
1575
1589
|
case "step/start": {
|
|
1576
1590
|
const prog = PROGRESSION[this.style];
|
|
1577
1591
|
this.tonicIndex = (this.tonicIndex + 1) % prog.length;
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1592
|
+
if (this.playing) {
|
|
1593
|
+
const newRoot = base + (prog[this.tonicIndex] ?? 0);
|
|
1594
|
+
this.insertFill(newRoot);
|
|
1595
|
+
}
|
|
1581
1596
|
break;
|
|
1582
1597
|
}
|
|
1583
1598
|
case "tool/call":
|
|
@@ -1599,7 +1614,6 @@ window.__ModuleLoader__.load({
|
|
|
1599
1614
|
setTimeout(() => this.pluck(base + 5, .4, .8), 220);
|
|
1600
1615
|
break;
|
|
1601
1616
|
case "assistant/message":
|
|
1602
|
-
this.stopMelody();
|
|
1603
1617
|
this.breath(action.interrupted ? base + 6 : base + 12, 1.6);
|
|
1604
1618
|
break;
|
|
1605
1619
|
case "turn/end":
|
|
@@ -1607,29 +1621,36 @@ window.__ModuleLoader__.load({
|
|
|
1607
1621
|
break;
|
|
1608
1622
|
}
|
|
1609
1623
|
}
|
|
1610
|
-
/** Start a new piece:
|
|
1624
|
+
/** Start a new piece: intro run, then the phrase chain. */
|
|
1611
1625
|
startPiece() {
|
|
1612
1626
|
if (!this.audio || this.playing) return;
|
|
1613
1627
|
this.playing = true;
|
|
1614
|
-
this.tonicIndex
|
|
1615
|
-
this.phraseIndex = 0;
|
|
1616
|
-
const root = TokenQuotaMusic.BASE_MIDI + (PROGRESSION[this.style][0] ?? 0);
|
|
1617
|
-
this.startDrone(root);
|
|
1628
|
+
const root = TokenQuotaMusic.BASE_MIDI + (PROGRESSION[this.style][this.tonicIndex] ?? 0);
|
|
1618
1629
|
const scale = SCALES[this.style];
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1630
|
+
if (this.introPending) {
|
|
1631
|
+
this.introPending = false;
|
|
1632
|
+
let t = 0;
|
|
1633
|
+
for (let i = 0; i < 8; i++) {
|
|
1634
|
+
const note = root + 12 * Math.floor(i / scale.length) + (scale[i % scale.length] ?? 0);
|
|
1635
|
+
setTimeout(() => {
|
|
1636
|
+
if (this.playing) this.pluck(note, .3);
|
|
1637
|
+
}, t);
|
|
1638
|
+
t += 150;
|
|
1639
|
+
}
|
|
1640
|
+
this.phraseTimer = setTimeout(() => this.playPhrase(), t + 100);
|
|
1641
|
+
} else {
|
|
1642
|
+
for (let i = 0; i < 3; i++) setTimeout(() => {
|
|
1643
|
+
if (this.playing) this.pluck(root + 12 + (scale[i] ?? 0), .3);
|
|
1644
|
+
}, i * 140);
|
|
1645
|
+
this.phraseTimer = setTimeout(() => this.playPhrase(), 480);
|
|
1625
1646
|
}
|
|
1626
|
-
this.phraseTimer = setTimeout(() => this.playPhrase(), t + 100);
|
|
1627
1647
|
}
|
|
1628
1648
|
/** Play one phrase, then chain into the next after a one-beat rest. */
|
|
1629
1649
|
playPhrase() {
|
|
1630
1650
|
if (!this.playing || !this.audio) return;
|
|
1631
1651
|
const root = TokenQuotaMusic.BASE_MIDI + (PROGRESSION[this.style][this.tonicIndex] ?? 0);
|
|
1632
1652
|
const scale = SCALES[this.style];
|
|
1653
|
+
this.pluck(root - 12, .28, 1.6);
|
|
1633
1654
|
const phrase = PHRASES[this.phraseIndex % PHRASES.length];
|
|
1634
1655
|
this.phraseIndex += 1;
|
|
1635
1656
|
let t = 0;
|
|
@@ -1657,7 +1678,7 @@ window.__ModuleLoader__.load({
|
|
|
1657
1678
|
}
|
|
1658
1679
|
this.phraseTimer = setTimeout(() => this.playPhrase(), t + 60);
|
|
1659
1680
|
}
|
|
1660
|
-
/** Stop the
|
|
1681
|
+
/** Stop the phrase chain. */
|
|
1661
1682
|
stopMelody() {
|
|
1662
1683
|
if (this.phraseTimer !== void 0) {
|
|
1663
1684
|
clearTimeout(this.phraseTimer);
|
|
@@ -1673,8 +1694,7 @@ window.__ModuleLoader__.load({
|
|
|
1673
1694
|
const scale = SCALES[this.style];
|
|
1674
1695
|
let t = 0;
|
|
1675
1696
|
for (let i = 7; i >= 0; i--) {
|
|
1676
|
-
const
|
|
1677
|
-
const note = base + 12 * Math.floor(i / scale.length) + (scale[deg] ?? 0);
|
|
1697
|
+
const note = base + 12 * Math.floor(i / scale.length) + (scale[i % scale.length] ?? 0);
|
|
1678
1698
|
setTimeout(() => this.pluck(note, .3), t);
|
|
1679
1699
|
t += 120;
|
|
1680
1700
|
}
|
|
@@ -1685,86 +1705,16 @@ window.__ModuleLoader__.load({
|
|
|
1685
1705
|
this.pluck(base - 12, .5);
|
|
1686
1706
|
}, t + 150);
|
|
1687
1707
|
}
|
|
1688
|
-
this.fadeDrone(final ? 1.2 : .8);
|
|
1689
1708
|
this.allMidiOff();
|
|
1690
1709
|
}
|
|
1691
1710
|
/** Hard stop everything (master switch off / teardown). */
|
|
1692
1711
|
stopAll() {
|
|
1693
1712
|
this.stopPiece(false);
|
|
1694
1713
|
}
|
|
1695
|
-
/** Start the sustained open fifth (root + fifth below octave root). */
|
|
1696
|
-
startDrone(root) {
|
|
1697
|
-
if (!this.audio || this.droneActive) return;
|
|
1698
|
-
const now = this.audio.currentTime;
|
|
1699
|
-
const gain = this.audio.createGain();
|
|
1700
|
-
gain.gain.setValueAtTime(0, now);
|
|
1701
|
-
const filter = this.audio.createBiquadFilter();
|
|
1702
|
-
filter.type = "lowpass";
|
|
1703
|
-
filter.frequency.value = 620;
|
|
1704
|
-
filter.Q.value = .4;
|
|
1705
|
-
gain.connect(filter);
|
|
1706
|
-
filter.connect(this.audio.destination);
|
|
1707
|
-
const oscs = [];
|
|
1708
|
-
for (const interval of [-12, -5]) {
|
|
1709
|
-
const osc = this.audio.createOscillator();
|
|
1710
|
-
osc.type = "sine";
|
|
1711
|
-
osc.frequency.value = midiToFreq(root + interval);
|
|
1712
|
-
osc.connect(gain);
|
|
1713
|
-
osc.start(now);
|
|
1714
|
-
oscs.push(osc);
|
|
1715
|
-
}
|
|
1716
|
-
this.droneOscs = oscs;
|
|
1717
|
-
this.droneGain = gain;
|
|
1718
|
-
this.droneFilter = filter;
|
|
1719
|
-
this.droneActive = true;
|
|
1720
|
-
gain.gain.linearRampToValueAtTime(this.volume * .07, now + 1.4);
|
|
1721
|
-
}
|
|
1722
|
-
/** Glide the drone to a new tonic (mode rotation). */
|
|
1723
|
-
glideDrone(root) {
|
|
1724
|
-
if (!this.audio || !this.droneActive) return;
|
|
1725
|
-
const now = this.audio.currentTime;
|
|
1726
|
-
const targets = [root - 12, root - 5];
|
|
1727
|
-
this.droneOscs.forEach((osc, i) => {
|
|
1728
|
-
const target = targets[i] ?? root;
|
|
1729
|
-
osc.frequency.setValueAtTime(osc.frequency.value, now);
|
|
1730
|
-
osc.frequency.exponentialRampToValueAtTime(midiToFreq(target), now + .9);
|
|
1731
|
-
});
|
|
1732
|
-
}
|
|
1733
|
-
/** Fade the drone out and stop its oscillators. */
|
|
1734
|
-
fadeDrone(seconds) {
|
|
1735
|
-
if (!this.audio || !this.droneActive || this.droneGain === null) return;
|
|
1736
|
-
const now = this.audio.currentTime;
|
|
1737
|
-
const gain = this.droneGain;
|
|
1738
|
-
gain.gain.cancelScheduledValues(now);
|
|
1739
|
-
gain.gain.setValueAtTime(Math.max(gain.gain.value, 1e-4), now);
|
|
1740
|
-
gain.gain.exponentialRampToValueAtTime(1e-4, now + seconds);
|
|
1741
|
-
const oscs = this.droneOscs;
|
|
1742
|
-
const filter = this.droneFilter;
|
|
1743
|
-
setTimeout(() => {
|
|
1744
|
-
for (const osc of oscs) {
|
|
1745
|
-
try {
|
|
1746
|
-
osc.stop();
|
|
1747
|
-
} catch {}
|
|
1748
|
-
try {
|
|
1749
|
-
osc.disconnect();
|
|
1750
|
-
} catch {}
|
|
1751
|
-
}
|
|
1752
|
-
try {
|
|
1753
|
-
filter?.disconnect();
|
|
1754
|
-
} catch {}
|
|
1755
|
-
try {
|
|
1756
|
-
gain.disconnect();
|
|
1757
|
-
} catch {}
|
|
1758
|
-
}, seconds * 1e3 + 120);
|
|
1759
|
-
this.droneActive = false;
|
|
1760
|
-
this.droneOscs = [];
|
|
1761
|
-
this.droneGain = null;
|
|
1762
|
-
this.droneFilter = null;
|
|
1763
|
-
}
|
|
1764
1714
|
/**
|
|
1765
|
-
* Guzheng-like pluck:
|
|
1766
|
-
* lowpass, fast attack and a long exponential decay (notes
|
|
1767
|
-
* keeps the piece continuous instead of choppy).
|
|
1715
|
+
* Guzheng-like pluck: a triangle voice plus a soft octave partial through a
|
|
1716
|
+
* sweeping lowpass, fast attack and a long exponential decay (notes
|
|
1717
|
+
* overlap, which keeps the piece continuous instead of choppy).
|
|
1768
1718
|
*/
|
|
1769
1719
|
pluck(midi, velocity, decaySec = 1.3) {
|
|
1770
1720
|
if (!this.audio) return;
|
|
@@ -1788,9 +1738,9 @@ window.__ModuleLoader__.load({
|
|
|
1788
1738
|
osc1.frequency.value = freq;
|
|
1789
1739
|
const osc2 = this.audio.createOscillator();
|
|
1790
1740
|
osc2.type = "sine";
|
|
1791
|
-
osc2.frequency.value = freq * 2
|
|
1741
|
+
osc2.frequency.value = freq * 2;
|
|
1792
1742
|
const octGain = this.audio.createGain();
|
|
1793
|
-
octGain.gain.value = .
|
|
1743
|
+
octGain.gain.value = .25;
|
|
1794
1744
|
osc1.connect(filter);
|
|
1795
1745
|
osc2.connect(octGain);
|
|
1796
1746
|
octGain.connect(filter);
|
|
@@ -1946,6 +1896,8 @@ window.__ModuleLoader__.load({
|
|
|
1946
1896
|
dimWhenIdleHint: "鼠标移开面板时降低不透明度,输入文字时更透明;鼠标悬停恢复",
|
|
1947
1897
|
musicLabel: "动作音乐",
|
|
1948
1898
|
musicHint: "根据 Harness 动作实时作曲:请求开始、工具调用、回合结束等(需点击开关启动音频;检测到 MIDI 输出时同步发送 MIDI)",
|
|
1899
|
+
musicOn: "开",
|
|
1900
|
+
musicOff: "关",
|
|
1949
1901
|
musicVolume: "音量",
|
|
1950
1902
|
musicStyle: "风格",
|
|
1951
1903
|
musicStyleMajor: "大调",
|
|
@@ -2022,6 +1974,8 @@ window.__ModuleLoader__.load({
|
|
|
2022
1974
|
dimWhenIdleHint: "Fade the panel out when the pointer is away (more while typing); hover to restore",
|
|
2023
1975
|
musicLabel: "Action music",
|
|
2024
1976
|
musicHint: "Live-composes from Harness actions: request starts, tool calls, turn ends, etc. (click the switch to start audio; a detected MIDI output is driven as well)",
|
|
1977
|
+
musicOn: "On",
|
|
1978
|
+
musicOff: "Off",
|
|
2025
1979
|
musicVolume: "Volume",
|
|
2026
1980
|
musicStyle: "Style",
|
|
2027
1981
|
musicStyleMajor: "Major",
|
|
@@ -2245,6 +2199,7 @@ window.__ModuleLoader__.load({
|
|
|
2245
2199
|
};
|
|
2246
2200
|
}, "token-quota-ui: snapshot poll + full strategy");
|
|
2247
2201
|
const load = (sessionId) => {
|
|
2202
|
+
if (lastSessionId !== void 0 && lastSessionId !== sessionId) music.stop();
|
|
2248
2203
|
lastSessionId = sessionId;
|
|
2249
2204
|
bound?.setLoading(true);
|
|
2250
2205
|
bound?.setError(null);
|
package/lib/index.js
CHANGED
|
@@ -515,7 +515,7 @@ var TokenQuotaService = class TokenQuotaService extends Service {
|
|
|
515
515
|
return this.upgradeCheck;
|
|
516
516
|
}
|
|
517
517
|
async doRefreshUpgrade() {
|
|
518
|
-
const current = "0.1.
|
|
518
|
+
const current = "0.1.29";
|
|
519
519
|
let latest;
|
|
520
520
|
try {
|
|
521
521
|
const response = await fetch(REGISTRY_LATEST_URL, {
|
|
@@ -298,7 +298,7 @@ export function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored,
|
|
|
298
298
|
: '';
|
|
299
299
|
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
|
|
300
300
|
? (_jsxs("span", { className: `${css.version} ${css.versionNew}`, title: t('upgradeAvailableHint'), children: ["v", __TOKEN_QUOTA_VERSION__, " \u2192 v", upgrade.latestVersion] }))
|
|
301
|
-
: (_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}${musicEnabled ? ` ${css.settingsBtnActive}` : ''}`, title: t('
|
|
301
|
+
: (_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", "aria-pressed": musicEnabled, className: `${css.settingsBtn}${musicEnabled ? ` ${css.settingsBtnActive}` : ''}`, title: `${t('musicLabel')}:${musicEnabled ? t('musicOn') : t('musicOff')}`, onClick: () => { setMusicEnabled(!musicEnabled); }, children: "\u266A" }), _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) => {
|
|
302
302
|
const state = barStateOf(row);
|
|
303
303
|
const pct = row.limit > 0 ? Math.min(100, Math.round((row.used / row.limit) * 100)) : 0;
|
|
304
304
|
const barClass = state === 'over'
|
|
@@ -262,6 +262,11 @@ export function apply(ctx) {
|
|
|
262
262
|
return () => { clearInterval(timer); };
|
|
263
263
|
}, 'token-quota-ui: snapshot poll + full strategy');
|
|
264
264
|
const load = (sessionId) => {
|
|
265
|
+
// Switching the panel to another session: stop the previous session's
|
|
266
|
+
// soundtrack — while scoped to the current session its stop events are
|
|
267
|
+
// filtered out, so without this it could play forever.
|
|
268
|
+
if (lastSessionId !== undefined && lastSessionId !== sessionId)
|
|
269
|
+
music.stop();
|
|
265
270
|
lastSessionId = sessionId;
|
|
266
271
|
bound?.setLoading(true);
|
|
267
272
|
bound?.setError(null);
|
|
@@ -68,6 +68,8 @@ export declare const zh: {
|
|
|
68
68
|
dimWhenIdleHint: string;
|
|
69
69
|
musicLabel: string;
|
|
70
70
|
musicHint: string;
|
|
71
|
+
musicOn: string;
|
|
72
|
+
musicOff: string;
|
|
71
73
|
musicVolume: string;
|
|
72
74
|
musicStyle: string;
|
|
73
75
|
musicStyleMajor: string;
|
|
@@ -146,6 +148,8 @@ export declare const en: {
|
|
|
146
148
|
dimWhenIdleHint: string;
|
|
147
149
|
musicLabel: string;
|
|
148
150
|
musicHint: string;
|
|
151
|
+
musicOn: string;
|
|
152
|
+
musicOff: string;
|
|
149
153
|
musicVolume: string;
|
|
150
154
|
musicStyle: string;
|
|
151
155
|
musicStyleMajor: string;
|
|
@@ -68,6 +68,8 @@ export const zh = {
|
|
|
68
68
|
dimWhenIdleHint: '鼠标移开面板时降低不透明度,输入文字时更透明;鼠标悬停恢复',
|
|
69
69
|
musicLabel: '动作音乐',
|
|
70
70
|
musicHint: '根据 Harness 动作实时作曲:请求开始、工具调用、回合结束等(需点击开关启动音频;检测到 MIDI 输出时同步发送 MIDI)',
|
|
71
|
+
musicOn: '开',
|
|
72
|
+
musicOff: '关',
|
|
71
73
|
musicVolume: '音量',
|
|
72
74
|
musicStyle: '风格',
|
|
73
75
|
musicStyleMajor: '大调',
|
|
@@ -144,6 +146,8 @@ export const en = {
|
|
|
144
146
|
dimWhenIdleHint: 'Fade the panel out when the pointer is away (more while typing); hover to restore',
|
|
145
147
|
musicLabel: 'Action music',
|
|
146
148
|
musicHint: 'Live-composes from Harness actions: request starts, tool calls, turn ends, etc. (click the switch to start audio; a detected MIDI output is driven as well)',
|
|
149
|
+
musicOn: 'On',
|
|
150
|
+
musicOff: 'Off',
|
|
147
151
|
musicVolume: 'Volume',
|
|
148
152
|
musicStyle: 'Style',
|
|
149
153
|
musicStyleMajor: 'Major',
|
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
* 古风 live-composed soundtrack driven by host actions.
|
|
3
3
|
*
|
|
4
4
|
* Design: when a request starts the engine composes a *piece* — a guzheng-like
|
|
5
|
-
* plucked-string voice
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* breath note and a descending glissando.
|
|
5
|
+
* plucked-string voice (Web Audio synthesis; the MIDI output is switched to
|
|
6
|
+
* the Koto program so hardware synths match). The piece plays phrase after
|
|
7
|
+
* phrase so it stays alive while the model thinks or streams; incoming events
|
|
8
|
+
* steer the piece instead of interrupting it: a step change rotates the mode
|
|
9
|
+
* centre and inserts a fast 过门 (fill); tool calls get a high plink; the
|
|
10
|
+
* ending gets a dizi-like breath note and a descending glissando.
|
|
12
11
|
*
|
|
13
|
-
*
|
|
12
|
+
* **No sustained tones by design.** Every voice is a decaying pluck (≤1.6s)
|
|
13
|
+
* or a fixed-duration breath note — there is no drone/pad oscillator that
|
|
14
|
+
* depends on a later stop event. A lost SSE event can at worst leave the
|
|
15
|
+
* phrase chain running, and the stream watchdog below stops that too, so a
|
|
16
|
+
* hanging "呜呜" hum is physically impossible.
|
|
17
|
+
*
|
|
18
|
+
* Anti-runaway guards:
|
|
14
19
|
* - the host sends an SSE heartbeat every 5s; if it stops arriving the whole
|
|
15
|
-
* engine
|
|
16
|
-
* - every MIDI NoteOn is registered and swept;
|
|
20
|
+
* engine stops (a dead stream means no stop event will ever come),
|
|
21
|
+
* - every MIDI NoteOn is registered and swept; stops also send
|
|
17
22
|
* All Notes Off (CC 123),
|
|
18
|
-
* - Web Audio
|
|
19
|
-
* explicitly stopped.
|
|
23
|
+
* - every Web Audio oscillator is explicitly stopped at envelope end.
|
|
20
24
|
*
|
|
21
25
|
* @module @jxgame2020/dsh-token-quota/music
|
|
22
26
|
*/
|
|
@@ -36,11 +40,11 @@ export declare class TokenQuotaMusic {
|
|
|
36
40
|
private playing;
|
|
37
41
|
private tonicIndex;
|
|
38
42
|
private phraseIndex;
|
|
43
|
+
/** True when the next piece start should play the full intro glissando. */
|
|
44
|
+
private introPending;
|
|
45
|
+
/** Session the current piece belongs to (turn/start carries it). */
|
|
46
|
+
private sessionId;
|
|
39
47
|
private phraseTimer;
|
|
40
|
-
private droneOscs;
|
|
41
|
-
private droneGain;
|
|
42
|
-
private droneFilter;
|
|
43
|
-
private droneActive;
|
|
44
48
|
private lastStreamActivity;
|
|
45
49
|
private watchdogTimer;
|
|
46
50
|
private readonly activeMidiNotes;
|
|
@@ -62,30 +66,26 @@ export declare class TokenQuotaMusic {
|
|
|
62
66
|
setStyle(value: TokenQuotaMusicStyle): void;
|
|
63
67
|
/** Feed the engine one SSE activity timestamp (action or heartbeat). */
|
|
64
68
|
markStreamActivity(): void;
|
|
69
|
+
/** Stop any playing piece — e.g. when the panel switches sessions. */
|
|
70
|
+
stop(): void;
|
|
65
71
|
/** Handle one incoming host action and steer the piece. */
|
|
66
72
|
onAction(action: TokenQuotaMusicAction): void;
|
|
67
|
-
/** Start a new piece:
|
|
73
|
+
/** Start a new piece: intro run, then the phrase chain. */
|
|
68
74
|
private startPiece;
|
|
69
75
|
/** Play one phrase, then chain into the next after a one-beat rest. */
|
|
70
76
|
private playPhrase;
|
|
71
77
|
/** Insert a 过门 fill: pause the chain, play the run, resume after. */
|
|
72
78
|
private insertFill;
|
|
73
|
-
/** Stop the
|
|
79
|
+
/** Stop the phrase chain. */
|
|
74
80
|
private stopMelody;
|
|
75
81
|
/** Stop the whole piece; `final` adds the closing cadence gestures. */
|
|
76
82
|
private stopPiece;
|
|
77
83
|
/** Hard stop everything (master switch off / teardown). */
|
|
78
84
|
private stopAll;
|
|
79
|
-
/** Start the sustained open fifth (root + fifth below octave root). */
|
|
80
|
-
private startDrone;
|
|
81
|
-
/** Glide the drone to a new tonic (mode rotation). */
|
|
82
|
-
private glideDrone;
|
|
83
|
-
/** Fade the drone out and stop its oscillators. */
|
|
84
|
-
private fadeDrone;
|
|
85
85
|
/**
|
|
86
|
-
* Guzheng-like pluck:
|
|
87
|
-
* lowpass, fast attack and a long exponential decay (notes
|
|
88
|
-
* keeps the piece continuous instead of choppy).
|
|
86
|
+
* Guzheng-like pluck: a triangle voice plus a soft octave partial through a
|
|
87
|
+
* sweeping lowpass, fast attack and a long exponential decay (notes
|
|
88
|
+
* overlap, which keeps the piece continuous instead of choppy).
|
|
89
89
|
*/
|
|
90
90
|
private pluck;
|
|
91
91
|
/** Dizi-like breath note: sine with vibrato and a soft envelope. */
|
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
* 古风 live-composed soundtrack driven by host actions.
|
|
3
3
|
*
|
|
4
4
|
* Design: when a request starts the engine composes a *piece* — a guzheng-like
|
|
5
|
-
* plucked-string voice
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* breath note and a descending glissando.
|
|
5
|
+
* plucked-string voice (Web Audio synthesis; the MIDI output is switched to
|
|
6
|
+
* the Koto program so hardware synths match). The piece plays phrase after
|
|
7
|
+
* phrase so it stays alive while the model thinks or streams; incoming events
|
|
8
|
+
* steer the piece instead of interrupting it: a step change rotates the mode
|
|
9
|
+
* centre and inserts a fast 过门 (fill); tool calls get a high plink; the
|
|
10
|
+
* ending gets a dizi-like breath note and a descending glissando.
|
|
12
11
|
*
|
|
13
|
-
*
|
|
12
|
+
* **No sustained tones by design.** Every voice is a decaying pluck (≤1.6s)
|
|
13
|
+
* or a fixed-duration breath note — there is no drone/pad oscillator that
|
|
14
|
+
* depends on a later stop event. A lost SSE event can at worst leave the
|
|
15
|
+
* phrase chain running, and the stream watchdog below stops that too, so a
|
|
16
|
+
* hanging "呜呜" hum is physically impossible.
|
|
17
|
+
*
|
|
18
|
+
* Anti-runaway guards:
|
|
14
19
|
* - the host sends an SSE heartbeat every 5s; if it stops arriving the whole
|
|
15
|
-
* engine
|
|
16
|
-
* - every MIDI NoteOn is registered and swept;
|
|
20
|
+
* engine stops (a dead stream means no stop event will ever come),
|
|
21
|
+
* - every MIDI NoteOn is registered and swept; stops also send
|
|
17
22
|
* All Notes Off (CC 123),
|
|
18
|
-
* - Web Audio
|
|
19
|
-
* explicitly stopped.
|
|
23
|
+
* - every Web Audio oscillator is explicitly stopped at envelope end.
|
|
20
24
|
*
|
|
21
25
|
* @module @jxgame2020/dsh-token-quota/music
|
|
22
26
|
*/
|
|
@@ -74,12 +78,11 @@ export class TokenQuotaMusic {
|
|
|
74
78
|
playing = false;
|
|
75
79
|
tonicIndex = 0;
|
|
76
80
|
phraseIndex = 0;
|
|
81
|
+
/** True when the next piece start should play the full intro glissando. */
|
|
82
|
+
introPending = true;
|
|
83
|
+
/** Session the current piece belongs to (turn/start carries it). */
|
|
84
|
+
sessionId;
|
|
77
85
|
phraseTimer;
|
|
78
|
-
// Drone layer (sustained open fifth under the plucks)
|
|
79
|
-
droneOscs = [];
|
|
80
|
-
droneGain = null;
|
|
81
|
-
droneFilter = null;
|
|
82
|
-
droneActive = false;
|
|
83
86
|
// Watchdog: host heartbeat freshness (ms timestamp)
|
|
84
87
|
lastStreamActivity = 0;
|
|
85
88
|
watchdogTimer;
|
|
@@ -138,7 +141,7 @@ export class TokenQuotaMusic {
|
|
|
138
141
|
this.lastStreamActivity = Date.now();
|
|
139
142
|
this.watchdogTimer = setInterval(() => {
|
|
140
143
|
if (this.playing && Date.now() - this.lastStreamActivity > TokenQuotaMusic.STREAM_TIMEOUT_MS) {
|
|
141
|
-
// Dead stream: no stop event will ever arrive —
|
|
144
|
+
// Dead stream: no stop event will ever arrive — stop the piece.
|
|
142
145
|
this.stopPiece(false);
|
|
143
146
|
}
|
|
144
147
|
}, 4_000);
|
|
@@ -160,6 +163,10 @@ export class TokenQuotaMusic {
|
|
|
160
163
|
markStreamActivity() {
|
|
161
164
|
this.lastStreamActivity = Date.now();
|
|
162
165
|
}
|
|
166
|
+
/** Stop any playing piece — e.g. when the panel switches sessions. */
|
|
167
|
+
stop() {
|
|
168
|
+
this.stopPiece(false);
|
|
169
|
+
}
|
|
163
170
|
/** Handle one incoming host action and steer the piece. */
|
|
164
171
|
onAction(action) {
|
|
165
172
|
if (!this.enabled || this.audio === null)
|
|
@@ -167,25 +174,37 @@ export class TokenQuotaMusic {
|
|
|
167
174
|
const base = TokenQuotaMusic.BASE_MIDI;
|
|
168
175
|
switch (action.type) {
|
|
169
176
|
case 'turn/start':
|
|
170
|
-
|
|
177
|
+
// A new turn = the user sent a message: begin a fresh piece right
|
|
178
|
+
// away (request/header only fires on the first request of a session
|
|
179
|
+
// or on model changes — it cannot be the music's start trigger).
|
|
180
|
+
// If another session's music was still playing, stop it first.
|
|
181
|
+
if (this.sessionId !== undefined && this.sessionId !== action.sessionId) {
|
|
182
|
+
this.stopPiece(false);
|
|
183
|
+
}
|
|
184
|
+
this.sessionId = action.sessionId;
|
|
185
|
+
this.stopMelody();
|
|
186
|
+
this.playing = false;
|
|
171
187
|
this.tonicIndex = 0;
|
|
172
188
|
this.phraseIndex = 0;
|
|
189
|
+
this.introPending = true;
|
|
190
|
+
this.startPiece();
|
|
173
191
|
break;
|
|
174
192
|
case 'request/header': {
|
|
193
|
+
// Safety net (and a light "request fired" accent when already playing).
|
|
175
194
|
if (!this.playing)
|
|
176
195
|
this.startPiece();
|
|
177
196
|
else
|
|
178
|
-
this.pluck(base + 24, 0.3);
|
|
197
|
+
this.pluck(base + 24, 0.3);
|
|
179
198
|
break;
|
|
180
199
|
}
|
|
181
200
|
case 'step/start': {
|
|
182
|
-
// New phase: rotate the
|
|
201
|
+
// New phase: rotate the mode centre and insert a 过门 fill.
|
|
183
202
|
const prog = PROGRESSION[this.style];
|
|
184
203
|
this.tonicIndex = (this.tonicIndex + 1) % prog.length;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (this.playing)
|
|
204
|
+
if (this.playing) {
|
|
205
|
+
const newRoot = base + (prog[this.tonicIndex] ?? 0);
|
|
188
206
|
this.insertFill(newRoot);
|
|
207
|
+
}
|
|
189
208
|
break;
|
|
190
209
|
}
|
|
191
210
|
case 'tool/call':
|
|
@@ -211,9 +230,9 @@ export class TokenQuotaMusic {
|
|
|
211
230
|
setTimeout(() => this.pluck(base + 5, 0.4, 0.8), 220);
|
|
212
231
|
break;
|
|
213
232
|
case 'assistant/message':
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
|
|
233
|
+
// Sentence end: a soft breath cadence, but the bed keeps playing
|
|
234
|
+
// until the turn fully ends (turn/end), so thinking + streaming stay
|
|
235
|
+
// audible the whole way through.
|
|
217
236
|
this.breath(action.interrupted ? base + 6 : base + 12, 1.6);
|
|
218
237
|
break;
|
|
219
238
|
case 'turn/end':
|
|
@@ -222,27 +241,37 @@ export class TokenQuotaMusic {
|
|
|
222
241
|
}
|
|
223
242
|
}
|
|
224
243
|
// ── piece lifecycle ───────────────────────────────────────────────────
|
|
225
|
-
/** Start a new piece:
|
|
244
|
+
/** Start a new piece: intro run, then the phrase chain. */
|
|
226
245
|
startPiece() {
|
|
227
246
|
if (!this.audio || this.playing)
|
|
228
247
|
return;
|
|
229
248
|
this.playing = true;
|
|
230
|
-
this.tonicIndex
|
|
231
|
-
this.phraseIndex = 0;
|
|
232
|
-
const root = TokenQuotaMusic.BASE_MIDI + (PROGRESSION[this.style][0] ?? 0);
|
|
233
|
-
this.startDrone(root);
|
|
234
|
-
// Intro glissando — the guzheng sweep that announces the piece.
|
|
249
|
+
const root = TokenQuotaMusic.BASE_MIDI + (PROGRESSION[this.style][this.tonicIndex] ?? 0);
|
|
235
250
|
const scale = SCALES[this.style];
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
251
|
+
if (this.introPending) {
|
|
252
|
+
// Full intro: the ascending guzheng glissando that announces the piece.
|
|
253
|
+
this.introPending = false;
|
|
254
|
+
let t = 0;
|
|
255
|
+
for (let i = 0; i < 8; i++) {
|
|
256
|
+
const note = root + 12 * Math.floor(i / scale.length) + (scale[i % scale.length] ?? 0);
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
if (this.playing)
|
|
259
|
+
this.pluck(note, 0.3);
|
|
260
|
+
}, t);
|
|
261
|
+
t += 150;
|
|
262
|
+
}
|
|
263
|
+
this.phraseTimer = setTimeout(() => this.playPhrase(), t + 100);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// Restart within the same turn: a short three-note pickup, no glissando.
|
|
267
|
+
for (let i = 0; i < 3; i++) {
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
if (this.playing)
|
|
270
|
+
this.pluck(root + 12 + (scale[i] ?? 0), 0.3);
|
|
271
|
+
}, i * 140);
|
|
272
|
+
}
|
|
273
|
+
this.phraseTimer = setTimeout(() => this.playPhrase(), 480);
|
|
243
274
|
}
|
|
244
|
-
// First phrase starts right after the glissando.
|
|
245
|
-
this.phraseTimer = setTimeout(() => this.playPhrase(), t + 100);
|
|
246
275
|
}
|
|
247
276
|
/** Play one phrase, then chain into the next after a one-beat rest. */
|
|
248
277
|
playPhrase() {
|
|
@@ -250,6 +279,9 @@ export class TokenQuotaMusic {
|
|
|
250
279
|
return;
|
|
251
280
|
const root = TokenQuotaMusic.BASE_MIDI + (PROGRESSION[this.style][this.tonicIndex] ?? 0);
|
|
252
281
|
const scale = SCALES[this.style];
|
|
282
|
+
// Soft low root pluck under each phrase — a foundation that decays away
|
|
283
|
+
// instead of a sustained drone (nothing here can hang).
|
|
284
|
+
this.pluck(root - 12, 0.28, 1.6);
|
|
253
285
|
const phrase = PHRASES[this.phraseIndex % PHRASES.length];
|
|
254
286
|
this.phraseIndex += 1;
|
|
255
287
|
let t = 0;
|
|
@@ -282,7 +314,7 @@ export class TokenQuotaMusic {
|
|
|
282
314
|
}
|
|
283
315
|
this.phraseTimer = setTimeout(() => this.playPhrase(), t + 60);
|
|
284
316
|
}
|
|
285
|
-
/** Stop the
|
|
317
|
+
/** Stop the phrase chain. */
|
|
286
318
|
stopMelody() {
|
|
287
319
|
if (this.phraseTimer !== undefined) {
|
|
288
320
|
clearTimeout(this.phraseTimer);
|
|
@@ -299,9 +331,7 @@ export class TokenQuotaMusic {
|
|
|
299
331
|
const scale = SCALES[this.style];
|
|
300
332
|
let t = 0;
|
|
301
333
|
for (let i = 7; i >= 0; i--) {
|
|
302
|
-
const
|
|
303
|
-
const oct = Math.floor(i / scale.length);
|
|
304
|
-
const note = base + 12 * oct + (scale[deg] ?? 0);
|
|
334
|
+
const note = base + 12 * Math.floor(i / scale.length) + (scale[i % scale.length] ?? 0);
|
|
305
335
|
setTimeout(() => this.pluck(note, 0.3), t);
|
|
306
336
|
t += 120;
|
|
307
337
|
}
|
|
@@ -312,95 +342,17 @@ export class TokenQuotaMusic {
|
|
|
312
342
|
this.pluck(base - 12, 0.5);
|
|
313
343
|
}, t + 150);
|
|
314
344
|
}
|
|
315
|
-
this.fadeDrone(final ? 1.2 : 0.8);
|
|
316
345
|
this.allMidiOff();
|
|
317
346
|
}
|
|
318
347
|
/** Hard stop everything (master switch off / teardown). */
|
|
319
348
|
stopAll() {
|
|
320
349
|
this.stopPiece(false);
|
|
321
350
|
}
|
|
322
|
-
// ── drone layer ───────────────────────────────────────────────────────
|
|
323
|
-
/** Start the sustained open fifth (root + fifth below octave root). */
|
|
324
|
-
startDrone(root) {
|
|
325
|
-
if (!this.audio || this.droneActive)
|
|
326
|
-
return;
|
|
327
|
-
const now = this.audio.currentTime;
|
|
328
|
-
const gain = this.audio.createGain();
|
|
329
|
-
gain.gain.setValueAtTime(0, now);
|
|
330
|
-
const filter = this.audio.createBiquadFilter();
|
|
331
|
-
filter.type = 'lowpass';
|
|
332
|
-
filter.frequency.value = 620;
|
|
333
|
-
filter.Q.value = 0.4;
|
|
334
|
-
gain.connect(filter);
|
|
335
|
-
filter.connect(this.audio.destination);
|
|
336
|
-
const oscs = [];
|
|
337
|
-
for (const interval of [-12, -5]) {
|
|
338
|
-
const osc = this.audio.createOscillator();
|
|
339
|
-
osc.type = 'sine';
|
|
340
|
-
osc.frequency.value = midiToFreq(root + interval);
|
|
341
|
-
osc.connect(gain);
|
|
342
|
-
osc.start(now);
|
|
343
|
-
oscs.push(osc);
|
|
344
|
-
}
|
|
345
|
-
this.droneOscs = oscs;
|
|
346
|
-
this.droneGain = gain;
|
|
347
|
-
this.droneFilter = filter;
|
|
348
|
-
this.droneActive = true;
|
|
349
|
-
gain.gain.linearRampToValueAtTime(this.volume * 0.07, now + 1.4);
|
|
350
|
-
}
|
|
351
|
-
/** Glide the drone to a new tonic (mode rotation). */
|
|
352
|
-
glideDrone(root) {
|
|
353
|
-
if (!this.audio || !this.droneActive)
|
|
354
|
-
return;
|
|
355
|
-
const now = this.audio.currentTime;
|
|
356
|
-
const targets = [root - 12, root - 5];
|
|
357
|
-
this.droneOscs.forEach((osc, i) => {
|
|
358
|
-
const target = targets[i] ?? root;
|
|
359
|
-
osc.frequency.setValueAtTime(osc.frequency.value, now);
|
|
360
|
-
osc.frequency.exponentialRampToValueAtTime(midiToFreq(target), now + 0.9);
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
/** Fade the drone out and stop its oscillators. */
|
|
364
|
-
fadeDrone(seconds) {
|
|
365
|
-
if (!this.audio || !this.droneActive || this.droneGain === null)
|
|
366
|
-
return;
|
|
367
|
-
const now = this.audio.currentTime;
|
|
368
|
-
const gain = this.droneGain;
|
|
369
|
-
gain.gain.cancelScheduledValues(now);
|
|
370
|
-
gain.gain.setValueAtTime(Math.max(gain.gain.value, 0.0001), now);
|
|
371
|
-
gain.gain.exponentialRampToValueAtTime(0.0001, now + seconds);
|
|
372
|
-
const oscs = this.droneOscs;
|
|
373
|
-
const filter = this.droneFilter;
|
|
374
|
-
setTimeout(() => {
|
|
375
|
-
for (const osc of oscs) {
|
|
376
|
-
try {
|
|
377
|
-
osc.stop();
|
|
378
|
-
}
|
|
379
|
-
catch { /* ignore */ }
|
|
380
|
-
try {
|
|
381
|
-
osc.disconnect();
|
|
382
|
-
}
|
|
383
|
-
catch { /* ignore */ }
|
|
384
|
-
}
|
|
385
|
-
try {
|
|
386
|
-
filter?.disconnect();
|
|
387
|
-
}
|
|
388
|
-
catch { /* ignore */ }
|
|
389
|
-
try {
|
|
390
|
-
gain.disconnect();
|
|
391
|
-
}
|
|
392
|
-
catch { /* ignore */ }
|
|
393
|
-
}, seconds * 1000 + 120);
|
|
394
|
-
this.droneActive = false;
|
|
395
|
-
this.droneOscs = [];
|
|
396
|
-
this.droneGain = null;
|
|
397
|
-
this.droneFilter = null;
|
|
398
|
-
}
|
|
399
351
|
// ── voices ────────────────────────────────────────────────────────────
|
|
400
352
|
/**
|
|
401
|
-
* Guzheng-like pluck:
|
|
402
|
-
* lowpass, fast attack and a long exponential decay (notes
|
|
403
|
-
* keeps the piece continuous instead of choppy).
|
|
353
|
+
* Guzheng-like pluck: a triangle voice plus a soft octave partial through a
|
|
354
|
+
* sweeping lowpass, fast attack and a long exponential decay (notes
|
|
355
|
+
* overlap, which keeps the piece continuous instead of choppy).
|
|
404
356
|
*/
|
|
405
357
|
pluck(midi, velocity, decaySec = 1.3) {
|
|
406
358
|
if (!this.audio)
|
|
@@ -426,9 +378,9 @@ export class TokenQuotaMusic {
|
|
|
426
378
|
osc1.frequency.value = freq;
|
|
427
379
|
const osc2 = this.audio.createOscillator();
|
|
428
380
|
osc2.type = 'sine';
|
|
429
|
-
osc2.frequency.value = freq * 2
|
|
381
|
+
osc2.frequency.value = freq * 2; // octave partial (no detune beating)
|
|
430
382
|
const octGain = this.audio.createGain();
|
|
431
|
-
octGain.gain.value = 0.
|
|
383
|
+
octGain.gain.value = 0.25;
|
|
432
384
|
osc1.connect(filter);
|
|
433
385
|
osc2.connect(octGain);
|
|
434
386
|
octGain.connect(filter);
|
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.
|
|
4
|
+
"version": "0.1.29",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|