@loommii/dsh-provider-usage 0.6.1 → 0.8.0
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/README.md +15 -3
- package/cordis.patch.yml +4 -1
- package/docs/CHANGELOG.md +114 -0
- package/lib/client.js +282 -70
- package/lib/daily-stats.js +67 -9
- package/lib/index.js +148 -14
- package/package.json +3 -6
package/lib/client.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
// (OpenCode Go 订阅 / DeepSeek 余额),卡片按结果字段自适应渲染(%+倒计时=订阅卡;
|
|
4
4
|
// 金额+币种=余额卡)。
|
|
5
5
|
// 设置项全部即时生效(localStorage 持久化,无需重启)。
|
|
6
|
+
// v0.8.0: 注册 id 必须等于包名 @loommii/dsh-provider-usage —— dsh 0.1.2-alpha 的
|
|
7
|
+
// boot graph 行 id = 包名,serveBundle 按 id 应答 bundle,不一致即客户端静默失联。
|
|
6
8
|
|
|
7
9
|
window.__ModuleLoader__.load({
|
|
8
|
-
id: 'dsh-provider-usage',
|
|
10
|
+
id: '@loommii/dsh-provider-usage',
|
|
9
11
|
factory: (require) => {
|
|
10
12
|
var module = { exports: {} };
|
|
11
13
|
var exports = module.exports;
|
|
@@ -62,7 +64,7 @@ window.__ModuleLoader__.load({
|
|
|
62
64
|
if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="' + styleId + '"]') === null) {
|
|
63
65
|
var css = [
|
|
64
66
|
// 官方「模型」设置页完整样式(1:1 注入,dsh-client-ui-settings-models 同款)
|
|
65
|
-
".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_addButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;height:36px;color:var(--dsw-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{flex-wrap:wrap;gap:10px;display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:12px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addCard,.zGbnIq_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px}.zGbnIq_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateActions{justify-content:flex-end;margin-bottom:6px;display:flex}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:6px}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}",
|
|
67
|
+
".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_addButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;height:36px;color:var(--dsw-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_dangerButton{box-sizing:border-box;height:28px;color:var(--dsw-alias-state-warn-label);font:inherit;cursor:pointer;background:0 0;border:1px solid transparent;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex;transition:border-color .15s,color .15s,background .15s}.zGbnIq_dangerButton:hover:not(:disabled){color:var(--dsw-alias-state-danger-primary,var(--dsw-alias-state-warn-label))}.zGbnIq_dangerButton:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_dangerButton:disabled{opacity:.6;cursor:default}.zGbnIq_dangerButton[data-arm=true]{color:var(--dsw-alias-state-danger-primary,#e5484d);border-color:var(--dsw-alias-state-danger-primary,#e5484d);background:color-mix(in srgb,var(--dsw-alias-state-danger-primary,#e5484d) 8%,transparent)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{flex-wrap:wrap;gap:10px;display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:12px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addCard,.zGbnIq_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px}.zGbnIq_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateActions{justify-content:flex-end;margin-bottom:6px;display:flex}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:6px}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}",
|
|
66
68
|
// 官方「插件」设置页二级菜单样式(1:1 注入,dsh-client-ui-settings-plugins PluginsSettingsSection 同款)
|
|
67
69
|
".pbvGtq_section{max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.pbvGtq_heading{margin:0;font-size:18px;font-weight:600}.pbvGtq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}.pbvGtq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:22px;margin-top:2px;display:flex}.pbvGtq_tab{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}.pbvGtq_tab:hover,.pbvGtq_tab[data-active=true]{color:var(--dsw-alias-label-primary)}.pbvGtq_tab[data-active=true]:after,.pbvGtq_tab:focus-visible:after{background:var(--dsw-alias-label-primary);content:\"\";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}.pbvGtq_tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary);border-radius:2px}.pbvGtq_panel{min-width:0;padding-top:2px}",
|
|
68
70
|
// 用量统计页样式
|
|
@@ -119,6 +121,20 @@ window.__ModuleLoader__.load({
|
|
|
119
121
|
'.zGbnIq_credentialDot.dsh-pu-dot-gray{background:var(--dsw-alias-label-tertiary,#8b93a7)}',
|
|
120
122
|
'@keyframes dsh-pu-ms-breathe{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(.72)}}@media (prefers-reduced-motion:reduce){.dsh-pu-chip.dsh-pu-chip-loading i,.zGbnIq_credentialDot.dsh-pu-dot-loading{animation:none}}',
|
|
121
123
|
'.ms-plus{color:var(--dsw-alias-brand-primary);font-size:15px;line-height:1}',
|
|
124
|
+
// ── 提供方「启用/暂停」开关(官方 switch 视觉;开=启用=绿 / 关=暂停=灰)──
|
|
125
|
+
'.dsh-pu-switch{box-sizing:border-box;width:36px;height:20px;background:var(--dsw-alias-border-l3);cursor:pointer;border:0;border-radius:10px;flex:none;padding:2px;position:relative}',
|
|
126
|
+
'.dsh-pu-switch:hover:not(:disabled){background:var(--dsw-alias-border-l4)}',
|
|
127
|
+
'.dsh-pu-switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}',
|
|
128
|
+
'.dsh-pu-switch-on{background:var(--dsw-alias-state-success-primary)}',
|
|
129
|
+
'.dsh-pu-switch-on:hover:not(:disabled){background:var(--dsw-alias-state-success-primary)}',
|
|
130
|
+
'.dsh-pu-switch-thumb{box-sizing:border-box;background:var(--dsw-alias-label-primary-foreground);border-radius:50%;width:16px;height:16px;display:block;transition:transform .12s}',
|
|
131
|
+
'.dsh-pu-switch-on .dsh-pu-switch-thumb{transform:translate(16px)}',
|
|
132
|
+
'.zGbnIq_editorSwitchRow{align-items:center;gap:10px;display:flex}',
|
|
133
|
+
'.dsh-pu-switchText{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}',
|
|
134
|
+
// ── 提供方列表:暂停行(灰点 + 名称变灰,编辑/删除保持可用)──
|
|
135
|
+
'.zGbnIq_rowNameDim{color:var(--dsw-alias-label-tertiary)}',
|
|
136
|
+
'.zGbnIq_rowCard .zGbnIq_credentialDot.dsh-pu-dot-paused{background:var(--dsw-alias-label-tertiary,#8b93a7)}',
|
|
137
|
+
'.zGbnIq_rowPaused .zGbnIq_rowIdentity .zGbnIq_rowName{color:var(--dsw-alias-label-tertiary)}',
|
|
122
138
|
].join('');
|
|
123
139
|
var tag = document.createElement('style');
|
|
124
140
|
tag.dataset.plugin = 'dsh-provider-usage';
|
|
@@ -169,35 +185,67 @@ window.__ModuleLoader__.load({
|
|
|
169
185
|
return s;
|
|
170
186
|
}
|
|
171
187
|
|
|
172
|
-
// ── 供应商实例清单(localStorage
|
|
173
|
-
|
|
188
|
+
// ── 供应商实例清单(v0.8.0:host 端 providers.json 为单一事实源,localStorage 只是写穿透缓存)──
|
|
189
|
+
// 修复「重启后提供方清单被静默重建」:此前清单只存 localStorage,存储 key 一旦丢失
|
|
190
|
+
// (换 origin/清站点数据/浏览器驱逐临时存储),启动就走 DEFAULT_PROVIDERS 兜底 →
|
|
191
|
+
// 已删除的默认实例(OpenCode Go/DeepSeek)复活、自定义实例(如 Command Code)永久消失。
|
|
192
|
+
// 现在启动时从 host 恢复;host 无数据时才一次性迁移本地缓存/默认清单并立即写回 host。
|
|
193
|
+
function normalizeProviders(arr) {
|
|
194
|
+
if (!Array.isArray(arr)) return null;
|
|
195
|
+
return arr.filter(function (p) {
|
|
196
|
+
return p && typeof p === 'object' && typeof p.adapter === 'string' && ADAPTER_META[p.adapter];
|
|
197
|
+
}).map(function (p) {
|
|
198
|
+
var name = typeof p.name === 'string' && p.name ? p.name : (ADAPTER_META[p.adapter] || {}).displayName;
|
|
199
|
+
if (String(p.id || '') === 'deepseek-balance' && name === 'DeepSeek 余额') name = 'DeepSeek'; // 0.4.0 更名
|
|
200
|
+
// source:Key 解析位置(vault=插件私有库直取;dsh=DSH 凭证链);type:创建方式(import=导入 / manual=自定义)
|
|
201
|
+
var source = p.source === 'vault' ? 'vault' : 'dsh';
|
|
202
|
+
// 显式 type 优先;旧数据无 type 时由 source 推导(vault→manual,其余→import)
|
|
203
|
+
var type = p.type === 'manual' || p.type === 'import' ? p.type : (source === 'vault' ? 'manual' : 'import');
|
|
204
|
+
// paused:true=暂停(不轮询查询、卡片菜单不可选);旧数据无该字段 = false(启用)
|
|
205
|
+
return { id: String(p.id || ''), name: name, adapter: p.adapter, ref: typeof p.ref === 'string' ? p.ref : '', source: source, type: type, paused: p.paused === true };
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function readProvidersCache() {
|
|
174
209
|
try {
|
|
175
|
-
|
|
176
|
-
if (Array.isArray(arr)) {
|
|
177
|
-
if (arr.length === 0) return []; // 用户删光后保持空清单,不复活默认实例
|
|
178
|
-
return arr.filter(function (p) {
|
|
179
|
-
return p && typeof p === 'object' && typeof p.adapter === 'string' && ADAPTER_META[p.adapter];
|
|
180
|
-
}).map(function (p) {
|
|
181
|
-
var name = typeof p.name === 'string' && p.name ? p.name : (ADAPTER_META[p.adapter] || {}).displayName;
|
|
182
|
-
if (String(p.id || '') === 'deepseek-balance' && name === 'DeepSeek 余额') name = 'DeepSeek'; // 0.4.0 更名
|
|
183
|
-
// source:Key 解析位置(vault=插件私有库直取;dsh=DSH 凭证链);type:创建方式(import=导入 / manual=自定义)
|
|
184
|
-
var source = p.source === 'vault' ? 'vault' : 'dsh';
|
|
185
|
-
// 显式 type 优先;旧数据无 type 时由 source 推导(vault→manual,其余→import)
|
|
186
|
-
var type = p.type === 'manual' || p.type === 'import' ? p.type : (source === 'vault' ? 'manual' : 'import');
|
|
187
|
-
return { id: String(p.id || ''), name: name, adapter: p.adapter, ref: typeof p.ref === 'string' ? p.ref : '', source: source, type: type };
|
|
188
|
-
});
|
|
189
|
-
}
|
|
210
|
+
return normalizeProviders(JSON.parse(window.localStorage.getItem(PROVIDERS_KEY) || 'null'));
|
|
190
211
|
} catch (e) { /* ignore */ }
|
|
191
|
-
return
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
function saveProvidersCache(list) {
|
|
215
|
+
try { window.localStorage.setItem(PROVIDERS_KEY, JSON.stringify(list)); } catch (e) { /* ignore */ }
|
|
192
216
|
}
|
|
193
|
-
|
|
217
|
+
// host 恢复完成前的乐观缓存:优先用 localStorage 旧数据(多数场景与 host 一致),否则默认清单
|
|
218
|
+
var providersState = readProvidersCache() || DEFAULT_PROVIDERS.slice();
|
|
194
219
|
var providersListeners = new Set();
|
|
220
|
+
// 本次页面会话内的写入计数:恢复响应迟到时,若期间用户已写入则放弃应用 host 过期数据(防复活)
|
|
221
|
+
var providersMutations = 0;
|
|
195
222
|
function saveProviders() {
|
|
196
|
-
|
|
223
|
+
// 写穿透:localStorage 缓存 + host 落盘(fire-and-forget;host 失联时本地缓存仍可用)
|
|
224
|
+
saveProvidersCache(providersState);
|
|
225
|
+
try {
|
|
226
|
+
fetch('/api/provider-usage/providers', {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
229
|
+
body: JSON.stringify({ providers: providersState }),
|
|
230
|
+
}).catch(function () { /* ignore */ });
|
|
231
|
+
} catch (e) { /* ignore */ }
|
|
232
|
+
}
|
|
233
|
+
// 内部应用(不记写入数):host 恢复/一次性迁移专用
|
|
234
|
+
function applyProvidersUpdate(update) {
|
|
235
|
+
var next = typeof update === 'function' ? update(providersState) : update;
|
|
236
|
+
providersState = next;
|
|
237
|
+
saveProviders();
|
|
238
|
+
providersListeners.forEach(function (fn) { fn(); });
|
|
197
239
|
}
|
|
198
240
|
function getProviders() { return providersState; }
|
|
199
|
-
|
|
200
|
-
|
|
241
|
+
// v0.8.0 修复(丢更新竞态):所有写方共享同一份 providersState 快照,整体覆盖式写入
|
|
242
|
+
// 在写入交错时会把先写的整单覆盖掉(如连删两个实例会复活第一个)。
|
|
243
|
+
// 现支持函数式更新:setProviders(function (prev) { return next; }) —— next 基于写入时刻的最新状态计算;
|
|
244
|
+
// 兼容旧用法:传数组视为恒等函数。
|
|
245
|
+
function setProviders(update) {
|
|
246
|
+
providersMutations += 1;
|
|
247
|
+
var next = typeof update === 'function' ? update(providersState) : update;
|
|
248
|
+
providersState = next;
|
|
201
249
|
saveProviders();
|
|
202
250
|
providersListeners.forEach(function (fn) { fn(); });
|
|
203
251
|
}
|
|
@@ -205,15 +253,43 @@ window.__ModuleLoader__.load({
|
|
|
205
253
|
providersListeners.add(fn);
|
|
206
254
|
return function () { providersListeners.delete(fn); };
|
|
207
255
|
}
|
|
256
|
+
// host 恢复只执行一次(首个订阅者触发;fetch 去重)
|
|
257
|
+
var hostRestoreStarted = false;
|
|
208
258
|
function useProviders() {
|
|
209
259
|
var [p, setP] = useState(getProviders);
|
|
210
|
-
useEffect(function () {
|
|
260
|
+
useEffect(function () {
|
|
261
|
+
var unsub = subscribeProviders(function () { setP(getProviders()); });
|
|
262
|
+
if (!hostRestoreStarted) {
|
|
263
|
+
hostRestoreStarted = true;
|
|
264
|
+
fetch('/api/provider-usage/providers', { headers: { accept: 'application/json' } })
|
|
265
|
+
.then(function (r) { return r.json().catch(function () { return null; }); })
|
|
266
|
+
.then(function (j) {
|
|
267
|
+
if (!j || !j.ok) return;
|
|
268
|
+
// 恢复期间用户已做过修改(增删改实例)→ 放弃 host 过期数据,避免把用户刚删的实例复活
|
|
269
|
+
if (providersMutations > 0) return;
|
|
270
|
+
if (j.exists && Array.isArray(j.providers)) {
|
|
271
|
+
// 主路径:host 有清单 → 恢复(host 为单一事实源;本地缓存仅作 host 失联时的乐观值)
|
|
272
|
+
applyProvidersUpdate(j.providers);
|
|
273
|
+
} else {
|
|
274
|
+
// 一次性迁移:host 无文件(首次升级到 0.8.0)→ 把当前缓存清单写回 host 固化
|
|
275
|
+
applyProvidersUpdate(getProviders());
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
.catch(function () { /* host 失联:继续用本地缓存,后续写操作会重试落盘 */ });
|
|
279
|
+
}
|
|
280
|
+
return unsub;
|
|
281
|
+
}, []);
|
|
211
282
|
return p;
|
|
212
283
|
}
|
|
213
|
-
/**
|
|
284
|
+
/** 可用(未暂停)的实例清单:卡片轮询与切换只在这些实例里进行。 */
|
|
285
|
+
function activeProviders(providers) {
|
|
286
|
+
return providers.filter(function (p) { return !p.paused; });
|
|
287
|
+
}
|
|
288
|
+
/** 当前选中实例;若被删除或已暂停则回落到第一个可用实例(全暂停/无实例 → null)。 */
|
|
214
289
|
function currentProvider(providers, providerId) {
|
|
215
|
-
|
|
216
|
-
|
|
290
|
+
var actives = activeProviders(providers);
|
|
291
|
+
for (var i = 0; i < actives.length; i++) if (actives[i].id === providerId) return actives[i];
|
|
292
|
+
return actives[0] || null;
|
|
217
293
|
}
|
|
218
294
|
|
|
219
295
|
// ── 工具 ──────────────────────────────────────────────────────────
|
|
@@ -326,7 +402,7 @@ window.__ModuleLoader__.load({
|
|
|
326
402
|
return function () { window.removeEventListener('resize', onResize); };
|
|
327
403
|
}, [s.right, s.bottom, s.visible, data]);
|
|
328
404
|
var current = currentProvider(providers, s.providerId);
|
|
329
|
-
var multiple = providers.length > 1;
|
|
405
|
+
var multiple = activeProviders(providers).length > 1;
|
|
330
406
|
var curKey = current ? current.id + '|' + current.adapter + '|' + current.ref : 'none';
|
|
331
407
|
|
|
332
408
|
var load = useCallback(function (force) {
|
|
@@ -406,10 +482,18 @@ window.__ModuleLoader__.load({
|
|
|
406
482
|
var warn = data.isValid === false && data.invalidMessage
|
|
407
483
|
? React.createElement('div', { className: 'dsh-pu-error' }, data.invalidMessage)
|
|
408
484
|
: null;
|
|
409
|
-
// Command Code 混合卡:主数字 =
|
|
485
|
+
// Command Code 混合卡:主数字 = 月剩余百分比(3 位小数向下取整),副行 = 已用/总额 + 剩余额度
|
|
410
486
|
var cc = null;
|
|
411
487
|
if (isCommandCode) {
|
|
412
|
-
var
|
|
488
|
+
var monthlyInfo = data.monthly || null;
|
|
489
|
+
// 新服务端直接给 remainingPct;旧服务端只有 usedPct → 按 100−usedPct 兜底(与 host 公式数学等价)
|
|
490
|
+
var remainPct = monthlyInfo
|
|
491
|
+
? (monthlyInfo.remainingPct !== undefined && monthlyInfo.remainingPct !== null
|
|
492
|
+
? monthlyInfo.remainingPct
|
|
493
|
+
: (monthlyInfo.usedPct === undefined || monthlyInfo.usedPct === null
|
|
494
|
+
? null
|
|
495
|
+
: Math.floor(Math.max(0, Math.min(100, 100 - monthlyInfo.usedPct)) * 1000) / 1000))
|
|
496
|
+
: null;
|
|
413
497
|
var usedStr = data.used === null ? '--' : Number(data.used).toFixed(2);
|
|
414
498
|
var quotaStr = data.totalQuota === null ? '--' : Number(data.totalQuota).toFixed(2);
|
|
415
499
|
var remainStr = data.remaining === null ? '--' : Number(data.remaining).toFixed(2);
|
|
@@ -421,8 +505,8 @@ window.__ModuleLoader__.load({
|
|
|
421
505
|
var extra = null;
|
|
422
506
|
body = React.createElement('div', null,
|
|
423
507
|
React.createElement('div', { className: 'dsh-pu-remain' },
|
|
424
|
-
React.createElement('span', { className: 'dsh-pu-remain-big' }, isCommandCode ? (
|
|
425
|
-
React.createElement('span', { className: 'dsh-pu-remain-label' }, isCommandCode ? ' ' + zh.
|
|
508
|
+
React.createElement('span', { className: 'dsh-pu-remain-big' }, isCommandCode ? (remainPct === null ? '--' : remainPct.toFixed(3) + '%') : main),
|
|
509
|
+
React.createElement('span', { className: 'dsh-pu-remain-label' }, isCommandCode ? ' ' + zh.remaining + '(' + zh.monthly + ')' : ' ' + mainLabel)),
|
|
426
510
|
warn,
|
|
427
511
|
cc,
|
|
428
512
|
winRows,
|
|
@@ -432,7 +516,7 @@ window.__ModuleLoader__.load({
|
|
|
432
516
|
var menu = null;
|
|
433
517
|
if (multiple && menuOpen) {
|
|
434
518
|
menu = React.createElement('div', { ref: menuRef, className: 'dsh-pu-menu' },
|
|
435
|
-
providers.map(function (p) {
|
|
519
|
+
activeProviders(providers).map(function (p) {
|
|
436
520
|
var active = p.id === current.id;
|
|
437
521
|
var mini = p.adapter === 'balance-json'
|
|
438
522
|
? React.createElement('img', { src: LOGO_DEEPSEEK, width: 16, height: 16, alt: '' })
|
|
@@ -542,6 +626,9 @@ window.__ModuleLoader__.load({
|
|
|
542
626
|
var [provider, setProvider] = useState(''); // '' = 全部
|
|
543
627
|
var [range, setRange] = useState('1'); // 默认近 1 天;'0'=全部 / '1'=近1天 / '7'=近7天 / '30'=近30天
|
|
544
628
|
var dataRef = useRef(null); // 轮询静默刷新判定用(避免闭包陈旧)
|
|
629
|
+
var [resetState, setResetState] = useState('idle'); // 重置统计:idle → arm(待确认)→ busy → idle
|
|
630
|
+
var [resetMsg, setResetMsg] = useState(null);
|
|
631
|
+
var resetTimerRef = useRef(null); // 两段式确认的 4s 自动解除
|
|
545
632
|
function setDataBoth(v) { dataRef.current = v; setData(v); }
|
|
546
633
|
function load(force) {
|
|
547
634
|
// 自然日语义:近 1 天 = 今天 00:00 起;近 7/30 天 = 今天 + 前 N-1 个完整自然日(0 点边界)
|
|
@@ -560,7 +647,20 @@ window.__ModuleLoader__.load({
|
|
|
560
647
|
else { setErr('无法读取本地用量统计'); }
|
|
561
648
|
})
|
|
562
649
|
.catch(function () { setErr('读取失败'); })
|
|
563
|
-
.finally(function () { setLoading(false); });
|
|
650
|
+
.finally(function () { setLoading(false); setResetMsg(null); });
|
|
651
|
+
}
|
|
652
|
+
function doReset() {
|
|
653
|
+
if (resetState === 'busy') return;
|
|
654
|
+
setResetState('busy'); setResetMsg(null);
|
|
655
|
+
setDataBoth(null); setLoading(true); // 重置语义:立即清空旧数据展示,进入「查询中」重建态
|
|
656
|
+
fetch('/api/provider-usage/reset-local-stats', { method: 'POST', headers: { accept: 'application/json' } })
|
|
657
|
+
.then(function (r) { return r.json().catch(function () { return null; }); })
|
|
658
|
+
.then(function (j) {
|
|
659
|
+
if (j && j.ok) setResetMsg('已重置(清除 ' + (j.removed || 0) + ' 个缓存文件),正在重新统计…');
|
|
660
|
+
else setResetMsg('重置失败' + (j && j.error && j.error.message ? ':' + j.error.message : ''));
|
|
661
|
+
})
|
|
662
|
+
.catch(function () { setResetMsg('重置失败:网络错误'); })
|
|
663
|
+
.finally(function () { setResetState('idle'); load(true); });
|
|
564
664
|
}
|
|
565
665
|
useEffect(function () { if (active) load(false); }, [active, provider, range]);
|
|
566
666
|
useEffect(function () {
|
|
@@ -574,7 +674,42 @@ window.__ModuleLoader__.load({
|
|
|
574
674
|
var provs = data && Array.isArray(data.providers) ? data.providers.slice() : [];
|
|
575
675
|
provs.sort();
|
|
576
676
|
var byModel = (data && data.byModel && typeof data.byModel === 'object') ? data.byModel : {};
|
|
577
|
-
|
|
677
|
+
// 形态判别:v0.7.0 = 嵌套 { 提供方: { 模型: 计数 } }(顶层值无 requests 数字);
|
|
678
|
+
// v0.6.1 旧服务端 = 扁平 { 模型: 计数 }(顶层值直接是计数条目)。
|
|
679
|
+
function byModelShape(m) {
|
|
680
|
+
for (var k in m) {
|
|
681
|
+
if (!Object.prototype.hasOwnProperty.call(m, k)) continue;
|
|
682
|
+
var v = m[k];
|
|
683
|
+
if (v && typeof v === 'object') return (typeof v.requests === 'number') ? 'flat' : 'nested';
|
|
684
|
+
}
|
|
685
|
+
return 'flat'; // 空对象:无模型行,形态无影响
|
|
686
|
+
}
|
|
687
|
+
var modelShape = byModelShape(byModel);
|
|
688
|
+
var modelData = {}; // 模型名 -> 计数条目(已按当前提供方选择过滤/合并)
|
|
689
|
+
if (modelShape === 'nested') {
|
|
690
|
+
if (provider === '') {
|
|
691
|
+
// 全部:同名模型跨提供方合并求和(unknown 桶=旧归属缺失事件,一并计入)
|
|
692
|
+
var merged = {};
|
|
693
|
+
for (var pk in byModel) {
|
|
694
|
+
if (!Object.prototype.hasOwnProperty.call(byModel, pk)) continue;
|
|
695
|
+
var bucket = byModel[pk];
|
|
696
|
+
if (!bucket || typeof bucket !== 'object' || typeof bucket.requests === 'number') continue;
|
|
697
|
+
for (var mk in bucket) {
|
|
698
|
+
if (!Object.prototype.hasOwnProperty.call(bucket, mk)) continue;
|
|
699
|
+
var e = bucket[mk], g = merged[mk];
|
|
700
|
+
if (!g) g = merged[mk] = { requests: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0 };
|
|
701
|
+
g.requests += e.requests || 0; g.inputTokens += e.inputTokens || 0;
|
|
702
|
+
g.outputTokens += e.outputTokens || 0; g.cacheReadTokens += e.cacheReadTokens || 0;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
modelData = merged;
|
|
706
|
+
} else {
|
|
707
|
+
modelData = (byModel[provider] && typeof byModel[provider] === 'object' && typeof byModel[provider].requests !== 'number') ? byModel[provider] : {};
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
modelData = byModel; // v0.6.1 旧服务端:无提供方维度,行为同旧版
|
|
711
|
+
}
|
|
712
|
+
var modelNames = Object.keys(modelData).sort();
|
|
578
713
|
// ── 按模型汇总表 ──
|
|
579
714
|
function ModelNameCell(props) {
|
|
580
715
|
var name = props.name;
|
|
@@ -637,25 +772,37 @@ window.__ModuleLoader__.load({
|
|
|
637
772
|
}
|
|
638
773
|
|
|
639
774
|
var modelRows = modelNames.map(function (mn) {
|
|
640
|
-
var md =
|
|
775
|
+
var md = modelData[mn] || { requests: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0 };
|
|
641
776
|
var hit = (md.inputTokens + md.cacheReadTokens) > 0 ? md.cacheReadTokens / (md.inputTokens + md.cacheReadTokens) : 0;
|
|
642
777
|
return { name: mn, requests: md.requests, inputTokens: md.inputTokens, outputTokens: md.outputTokens, cacheReadTokens: md.cacheReadTokens, hitRate: fmtPct(hit) };
|
|
643
778
|
});
|
|
644
779
|
|
|
645
780
|
return React.createElement("div", null,
|
|
646
781
|
React.createElement("div", null,
|
|
647
|
-
React.createElement("p", { className: "zGbnIq_intro", style: { margin: "0 0 8px" } }, "统计 DSH 会话日志中的 Token
|
|
782
|
+
React.createElement("p", { className: "zGbnIq_intro", style: { margin: "0 0 8px" } }, "统计 DSH 会话日志中的 Token 用量(本机解析;历史按天存储,只算当天;下方汇总跟随所选提供方)。" + (data && data.days ? " 已聚合 " + data.days + " 天数据。" : "")),
|
|
648
783
|
React.createElement("div", { className: "zGbnIq_rowHead", style: { marginBottom: "4px" } },
|
|
649
784
|
React.createElement("select", { className: "zGbnIq_input zGbnIq_selectInput", style: { width: "auto" }, value: range, onChange: function (e) { setRange(e.target.value); } },
|
|
650
785
|
React.createElement("option", { value: "1" }, "近 1 天"),
|
|
651
786
|
React.createElement("option", { value: "7" }, "近 7 天"),
|
|
652
787
|
React.createElement("option", { value: "30" }, "近 30 天"),
|
|
653
788
|
React.createElement("option", { value: "0" }, "全部时间")),
|
|
654
|
-
React.createElement("button", { type: "button", className: "zGbnIq_linkButton", onClick: function () { load(true); } }, "刷新")
|
|
789
|
+
React.createElement("button", { type: "button", className: "zGbnIq_linkButton", onClick: function () { load(true); } }, "刷新"),
|
|
790
|
+
React.createElement("button", {
|
|
791
|
+
type: "button",
|
|
792
|
+
className: "zGbnIq_dangerButton",
|
|
793
|
+
"data-arm": resetState === 'arm' ? "true" : undefined,
|
|
794
|
+
disabled: resetState === 'busy',
|
|
795
|
+
"aria-label": resetState === 'arm' ? "再次点击确认重置本地统计" : "重置本地统计(清除按天缓存并重新统计,不影响会话记录)",
|
|
796
|
+
onClick: function () {
|
|
797
|
+
if (resetState === 'arm') { doReset(); }
|
|
798
|
+
else { setResetState('arm'); setResetMsg(null); window.clearTimeout(resetTimerRef.current); resetTimerRef.current = window.setTimeout(function () { setResetState('idle'); }, 4000); }
|
|
799
|
+
},
|
|
800
|
+
}, resetState === 'arm' ? "确认重置?" : resetState === 'busy' ? "重置中…" : "重置统计"))),
|
|
655
801
|
loading ? React.createElement("div", null,
|
|
656
802
|
React.createElement("div", { className: "zGbnIq_progress" }, React.createElement("i", null)),
|
|
657
803
|
React.createElement("p", { className: "zGbnIq_advancedHint" }, "查询中…")) : null,
|
|
658
804
|
err ? React.createElement("p", { className: "zGbnIq_error" }, err) : null,
|
|
805
|
+
resetMsg ? React.createElement("p", { className: "zGbnIq_advancedHint" }, resetMsg) : null,
|
|
659
806
|
total ? React.createElement("div", null,
|
|
660
807
|
React.createElement("div", { className: "zGbnIq_chips" }, chips),
|
|
661
808
|
stats,
|
|
@@ -663,7 +810,8 @@ window.__ModuleLoader__.load({
|
|
|
663
810
|
React.createElement("div", { className: "zGbnIq_modelCatalogHeading" },
|
|
664
811
|
React.createElement("span", { className: "zGbnIq_modelCatalogTitle" }, "按模型汇总")),
|
|
665
812
|
React.createElement(ModelSummaryTable, { rows: modelRows }))) : null,
|
|
666
|
-
!total && !loading && !err && data ? React.createElement("div", { className: "zGbnIq_modelEmpty" }, "没有匹配的 DSH 会话。") : null
|
|
813
|
+
!total && !loading && !err && data ? React.createElement("div", { className: "zGbnIq_modelEmpty" }, "没有匹配的 DSH 会话。") : null,
|
|
814
|
+
total && modelRows.length === 0 ? React.createElement("div", { className: "zGbnIq_modelEmpty" }, total.requests > 0 ? "旧版本统计的用量暂无模型归属明细。" : "该时间范围内没有此提供方的模型用量。") : null);
|
|
667
815
|
}
|
|
668
816
|
// 提供方状态归一:dot 颜色/标签/可点击性/展开内容都从这里出,6 个 if-else 收敛为 1 个表
|
|
669
817
|
// level: 'ok' 绿 / 'err' 红 / 'loading' 脉冲灰 / 'unknown' 静默灰
|
|
@@ -695,6 +843,8 @@ window.__ModuleLoader__.load({
|
|
|
695
843
|
var [queryStatus, setQueryStatus] = useState({});
|
|
696
844
|
var [errOpen, setErrOpen] = useState({}); // 展开的错误详情(按实例 id)
|
|
697
845
|
var refsKey = providers.map(function (p) { return p.ref || ''; }).join(',');
|
|
846
|
+
// 暂停态也进依赖:切「恢复/暂停」后让设置页状态查询重新跑(暂停的不查、恢复的马上查)
|
|
847
|
+
var refsPauseKey = providers.map(function (p) { return (p.paused ? '1' : '0') + ':' + (p.ref || ''); }).join(',');
|
|
698
848
|
// 添加卡:mode = null | 'import' | 'manual';编辑卡:edit = null | { id, kind, adapter, ref }
|
|
699
849
|
var [mode, setMode] = useState(null);
|
|
700
850
|
var [edit, setEdit] = useState(null);
|
|
@@ -705,14 +855,22 @@ window.__ModuleLoader__.load({
|
|
|
705
855
|
var [formError, setFormError] = useState(null);
|
|
706
856
|
|
|
707
857
|
function removeProvider(p) {
|
|
708
|
-
var next = providers.filter(function (x) { return x.id !== p.id; });
|
|
709
858
|
if (p.source === 'vault' && p.ref) {
|
|
710
859
|
// 删除自定义实例时同步清理插件私有库中的 Key(fire-and-forget)
|
|
711
860
|
fetch('/api/provider-usage/credentials?ref=' + encodeURIComponent(p.ref), { headers: { accept: 'application/json' } })
|
|
712
861
|
.catch(function () { /* ignore */ });
|
|
713
862
|
}
|
|
714
|
-
|
|
715
|
-
|
|
863
|
+
// v0.8.0:函数式更新 —— 基于写入时刻的最新清单计算,连删/交错写入不再互相覆盖
|
|
864
|
+
var next = null;
|
|
865
|
+
setProviders(function (prev) {
|
|
866
|
+
next = prev.filter(function (x) { return x.id !== p.id; });
|
|
867
|
+
return next;
|
|
868
|
+
});
|
|
869
|
+
// 若当前卡片实例被删除,回落到第一个可用实例(跳过已暂停的)
|
|
870
|
+
if (currentProvider(next, s.providerId || '') === null) {
|
|
871
|
+
var first = activeProviders(next)[0];
|
|
872
|
+
setSettings({ providerId: first ? first.id : '' });
|
|
873
|
+
}
|
|
716
874
|
}
|
|
717
875
|
|
|
718
876
|
function openImport() {
|
|
@@ -746,8 +904,8 @@ window.__ModuleLoader__.load({
|
|
|
746
904
|
var item = null;
|
|
747
905
|
for (var i = 0; i < dshItems.length; i++) if (dshItems[i].route === form.provider) item = dshItems[i];
|
|
748
906
|
if (!item) { setFormError('导入项不存在'); return; }
|
|
749
|
-
var inst = { id: item.route + '-' + Date.now(), name: name, adapter: item.adapter, ref: item.ref, source: 'dsh', type: 'import' };
|
|
750
|
-
setProviders(
|
|
907
|
+
var inst = { id: item.route + '-' + Date.now(), name: name, adapter: item.adapter, ref: item.ref, source: 'dsh', type: 'import', paused: false };
|
|
908
|
+
setProviders(function (prev) { return prev.concat([inst]); });
|
|
751
909
|
setSettings({ providerId: inst.id });
|
|
752
910
|
setMode(null);
|
|
753
911
|
}
|
|
@@ -768,8 +926,8 @@ window.__ModuleLoader__.load({
|
|
|
768
926
|
});
|
|
769
927
|
var j = await resp.json().catch(function () { return null; });
|
|
770
928
|
if (!j || !j.ok) { setFormError((j && j.error && j.error.message) || '保存 Key 失败'); return; }
|
|
771
|
-
var inst = { id: instId, name: name, adapter: form.adapter, ref: instId, source: 'vault', type: 'manual' };
|
|
772
|
-
setProviders(
|
|
929
|
+
var inst = { id: instId, name: name, adapter: form.adapter, ref: instId, source: 'vault', type: 'manual', paused: false };
|
|
930
|
+
setProviders(function (prev) { return prev.concat([inst]); });
|
|
773
931
|
setSettings({ providerId: inst.id });
|
|
774
932
|
setMode(null);
|
|
775
933
|
} catch (e) {
|
|
@@ -779,11 +937,12 @@ window.__ModuleLoader__.load({
|
|
|
779
937
|
}
|
|
780
938
|
}
|
|
781
939
|
|
|
782
|
-
// ──
|
|
940
|
+
// ── 编辑:导入的只改名称 + 启用状态;自定义(vault)可改名称 + 覆盖 Key + 启用状态 ──
|
|
941
|
+
// enabled = 表单暂存值(false 表示“暂停”);开关只暂存在表单里,点「保存」才真正写入实例
|
|
783
942
|
function startEdit(p) {
|
|
784
943
|
setMode(null);
|
|
785
|
-
setEdit({ id: p.id, kind: p.type === 'manual' ? 'manual' : 'import', adapter: p.adapter, ref: p.ref });
|
|
786
|
-
setForm({ provider: '', name: p.name, adapter: p.adapter, key: '', ref: p.ref, nameTouched: true });
|
|
944
|
+
setEdit({ id: p.id, kind: p.type === 'manual' ? 'manual' : 'import', adapter: p.adapter, ref: p.ref, paused: !!p.paused });
|
|
945
|
+
setForm({ provider: '', name: p.name, adapter: p.adapter, key: '', ref: p.ref, nameTouched: true, enabled: !p.paused, _editId: p.id });
|
|
787
946
|
setFormError(null);
|
|
788
947
|
}
|
|
789
948
|
|
|
@@ -811,10 +970,30 @@ window.__ModuleLoader__.load({
|
|
|
811
970
|
setBusy(false);
|
|
812
971
|
}
|
|
813
972
|
}
|
|
814
|
-
|
|
973
|
+
// 名称 + 启用状态在同一次 setProviders 里写入(避免两次基于旧数组的写入互相覆盖)
|
|
974
|
+
var enabled2 = form.enabled === false ? false : true;
|
|
975
|
+
// v0.8.0:函数式更新 —— next 基于写入时刻的最新清单计算,不再依赖渲染闭包的旧 providers
|
|
976
|
+
var next = null;
|
|
977
|
+
setProviders(function (prev) {
|
|
978
|
+
next = prev.map(function (p) {
|
|
979
|
+
return p.id === edit.id ? Object.assign({}, p, { name: name, paused: !enabled2 }) : p;
|
|
980
|
+
});
|
|
981
|
+
return next;
|
|
982
|
+
});
|
|
983
|
+
// 卡片当前实例处理(基于更新后的 next):
|
|
984
|
+
// - 停用的正是当前卡片实例 → 显式切到下一个仍启用的(currentProvider 会自动回落,不能依赖它判空)
|
|
985
|
+
// - 恢复某实例且此前没有任何可用实例(卡片隐藏态)→ 恢复的这个直接成为卡片当前实例
|
|
986
|
+
if (!enabled2 && (s.providerId || '') === edit.id) {
|
|
987
|
+
var fallback = null;
|
|
988
|
+
var act = activeProviders(next);
|
|
989
|
+
for (var i = 0; i < act.length; i++) if (act[i].id !== edit.id) { fallback = act[i]; break; }
|
|
990
|
+
setSettings({ providerId: fallback ? fallback.id : '' });
|
|
991
|
+
} else if (enabled2 && !currentProvider(next, s.providerId || '')) {
|
|
992
|
+
setSettings({ providerId: edit.id });
|
|
993
|
+
}
|
|
815
994
|
if (keyChanged) setNonce(function (n) { return n + 1; });
|
|
816
995
|
setEdit(null);
|
|
817
|
-
setForm({ provider: '', name: '', adapter: 'usage-percent', key: '', ref: '', nameTouched: false });
|
|
996
|
+
setForm({ provider: '', name: '', adapter: 'usage-percent', key: '', ref: '', nameTouched: false, enabled: true, _editId: null });
|
|
818
997
|
}
|
|
819
998
|
|
|
820
999
|
// ── 状态查询(打开设置页时:describe + 每实例实际查询)──
|
|
@@ -828,6 +1007,7 @@ window.__ModuleLoader__.load({
|
|
|
828
1007
|
var jobs = [];
|
|
829
1008
|
providers.forEach(function (p) {
|
|
830
1009
|
if (!p.ref) return;
|
|
1010
|
+
if (p.paused) return; // 已暂停的实例不做任何查询(设置页打开时也只显示「已暂停」灰点)
|
|
831
1011
|
if (configuredMap && !configuredMap[p.ref]) return;
|
|
832
1012
|
var key = p.adapter + '|' + p.ref;
|
|
833
1013
|
setQueryStatus(function (prev) {
|
|
@@ -869,13 +1049,16 @@ window.__ModuleLoader__.load({
|
|
|
869
1049
|
j.refs.forEach(function (x) { map[x.name] = !!x.configured; storeMap[x.name] = x.store; });
|
|
870
1050
|
setRefStatus(map);
|
|
871
1051
|
// 旧数据自愈:source 非 vault 但 Key 只存在插件私有库 → 升级为手动实例(可改名称+Key)
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1052
|
+
// v0.8.0:函数式更新,避免覆盖恢复/添加等交错写入
|
|
1053
|
+
setProviders(function (prev) {
|
|
1054
|
+
var changed = false;
|
|
1055
|
+
var next = prev.map(function (p) {
|
|
1056
|
+
if (p.source === 'vault' || !p.ref) return p;
|
|
1057
|
+
if (storeMap[p.ref] === 'vault') { changed = true; return Object.assign({}, p, { source: 'vault' }); }
|
|
1058
|
+
return p;
|
|
1059
|
+
});
|
|
1060
|
+
return changed ? next : prev;
|
|
877
1061
|
});
|
|
878
|
-
if (changed) setProviders(next);
|
|
879
1062
|
return startQueries(map, nonce > 0);
|
|
880
1063
|
}
|
|
881
1064
|
return startQueries(null, nonce > 0);
|
|
@@ -886,7 +1069,7 @@ window.__ModuleLoader__.load({
|
|
|
886
1069
|
startQueries(null, nonce > 0);
|
|
887
1070
|
});
|
|
888
1071
|
return function () { alive = false; };
|
|
889
|
-
}, [refsKey, nonce]);
|
|
1072
|
+
}, [refsKey, refsPauseKey, nonce]);
|
|
890
1073
|
|
|
891
1074
|
var adapterOptions = Object.keys(ADAPTER_META).map(function (a) {
|
|
892
1075
|
return React.createElement('option', { key: a, value: a }, ADAPTER_META[a].displayName);
|
|
@@ -961,6 +1144,19 @@ window.__ModuleLoader__.load({
|
|
|
961
1144
|
React.createElement('button', { type: 'button', className: 'zGbnIq_primaryButton', disabled: busy, onClick: saveManual }, '保存')));
|
|
962
1145
|
} else if (edit) {
|
|
963
1146
|
var editManual = edit.kind === 'manual';
|
|
1147
|
+
var enabled = form.enabled === false ? false : true;
|
|
1148
|
+
var enableToggle = React.createElement('div', { className: 'zGbnIq_field' },
|
|
1149
|
+
React.createElement('span', { className: 'zGbnIq_fieldLabel' }, '启用'),
|
|
1150
|
+
React.createElement('label', { className: 'zGbnIq_editorSwitchRow' },
|
|
1151
|
+
React.createElement('button', {
|
|
1152
|
+
type: 'button', role: 'switch',
|
|
1153
|
+
'aria-checked': enabled ? 'true' : 'false',
|
|
1154
|
+
'aria-label': enabled ? ('已启用 ' + form.name + ',点击暂停') : ('已暂停 ' + form.name + ',点击启用'),
|
|
1155
|
+
className: 'dsh-pu-switch' + (enabled ? ' dsh-pu-switch-on' : ''),
|
|
1156
|
+
onClick: function () { setForm(Object.assign({}, form, { enabled: !enabled })); },
|
|
1157
|
+
}, React.createElement('span', { className: 'dsh-pu-switch-thumb' })),
|
|
1158
|
+
React.createElement('span', { className: 'dsh-pu-switchText' }, enabled ? '已启用' : '已暂停')),
|
|
1159
|
+
React.createElement('span', { className: 'zGbnIq_advancedHint' }, enabled ? '启用中:正常轮询查询,卡片可切换选择' : '已暂停:不再轮询查询,卡片菜单中不可选(Key 与配置保留)'));
|
|
964
1160
|
addCard = React.createElement('div', { className: 'zGbnIq_editor' },
|
|
965
1161
|
React.createElement('div', { className: 'zGbnIq_editorHeader' },
|
|
966
1162
|
React.createElement('span', { className: 'zGbnIq_editorTitle' }, editManual ? '编辑自定义提供方' : '编辑提供方')),
|
|
@@ -976,6 +1172,7 @@ window.__ModuleLoader__.load({
|
|
|
976
1172
|
className: 'zGbnIq_input', value: form.name,
|
|
977
1173
|
onChange: function (e) { setForm(Object.assign({}, form, { name: e.target.value, nameTouched: true })); },
|
|
978
1174
|
})),
|
|
1175
|
+
enableToggle,
|
|
979
1176
|
editManual ? React.createElement('div', { className: 'zGbnIq_field' },
|
|
980
1177
|
React.createElement('span', { className: 'zGbnIq_fieldLabel' }, 'API 密钥'),
|
|
981
1178
|
React.createElement('input', {
|
|
@@ -1022,7 +1219,8 @@ window.__ModuleLoader__.load({
|
|
|
1022
1219
|
: providers.map(function (pl) {
|
|
1023
1220
|
var qs = pl.ref ? queryStatus[pl.adapter + '|' + pl.ref] : null;
|
|
1024
1221
|
var refKnownRow = pl.ref ? Object.prototype.hasOwnProperty.call(refStatus, pl.ref) : true;
|
|
1025
|
-
|
|
1222
|
+
// 已暂停的实例不再轮询/查询,状态点固定为「已暂停」灰点,查询状态一律不展示
|
|
1223
|
+
var status = (pl.paused || !pl.ref) ? null : statusOf(refKnownRow, refCheckFailed, !!refStatus[pl.ref], qs);
|
|
1026
1224
|
return React.createElement(ProviderRow, {
|
|
1027
1225
|
key: pl.id,
|
|
1028
1226
|
provider: pl,
|
|
@@ -1070,7 +1268,7 @@ window.__ModuleLoader__.load({
|
|
|
1070
1268
|
usagePanel);
|
|
1071
1269
|
}
|
|
1072
1270
|
// 提供方单行:状态点 + 名称 + 编辑/删除 + 展开详情
|
|
1073
|
-
// 状态判定全部交给 statusOf
|
|
1271
|
+
// 状态判定全部交给 statusOf;本组件只做渲染;启用/暂停开关在编辑弹层里管理
|
|
1074
1272
|
function ProviderRow(props) {
|
|
1075
1273
|
var pl = props.provider;
|
|
1076
1274
|
var status = props.status;
|
|
@@ -1078,24 +1276,36 @@ window.__ModuleLoader__.load({
|
|
|
1078
1276
|
var onToggle = props.onToggle;
|
|
1079
1277
|
var onEdit = props.onEdit;
|
|
1080
1278
|
var onRemove = props.onRemove;
|
|
1081
|
-
var
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1279
|
+
var paused = pl.paused === true;
|
|
1280
|
+
var dotEl;
|
|
1281
|
+
if (paused) {
|
|
1282
|
+
// 已暂停:灰点「已暂停」,不可展开(暂停时不再查询,无新鲜结果/错误可看)
|
|
1283
|
+
dotEl = React.createElement('span', {
|
|
1284
|
+
className: 'zGbnIq_credentialDot dsh-pu-dot-paused',
|
|
1285
|
+
role: 'img', 'aria-label': '已暂停',
|
|
1286
|
+
});
|
|
1287
|
+
} else if (status && status.dotCls) {
|
|
1288
|
+
dotEl = React.createElement('span', {
|
|
1289
|
+
className: 'zGbnIq_credentialDot' + status.dotCls,
|
|
1290
|
+
role: 'img', 'aria-label': status.label,
|
|
1291
|
+
style: status.clickable ? { cursor: 'pointer' } : undefined,
|
|
1292
|
+
onClick: status.clickable ? onToggle : undefined,
|
|
1293
|
+
});
|
|
1294
|
+
} else {
|
|
1295
|
+
dotEl = null;
|
|
1296
|
+
}
|
|
1087
1297
|
var detailEl = null;
|
|
1088
|
-
if (expanded && status.detailKind) {
|
|
1298
|
+
if (!paused && expanded && status && status.detailKind) {
|
|
1089
1299
|
if (status.detailKind === 'err') {
|
|
1090
1300
|
detailEl = React.createElement('p', { className: 'zGbnIq_error', style: { margin: '10px 0 0' } }, status.detailText);
|
|
1091
1301
|
} else if (status.detailKind === 'resp') {
|
|
1092
1302
|
detailEl = React.createElement('div', { className: 'zGbnIq_json' }, status.detailText);
|
|
1093
1303
|
}
|
|
1094
1304
|
}
|
|
1095
|
-
return React.createElement('li', { key: pl.id, className: 'zGbnIq_rowCard' },
|
|
1305
|
+
return React.createElement('li', { key: pl.id, className: 'zGbnIq_rowCard' + (paused ? ' zGbnIq_rowPaused' : '') },
|
|
1096
1306
|
React.createElement('div', { className: 'zGbnIq_rowHead' },
|
|
1097
1307
|
React.createElement('span', { className: 'zGbnIq_rowIdentity' },
|
|
1098
|
-
React.createElement('span', { className: 'zGbnIq_rowName' }, pl.name),
|
|
1308
|
+
React.createElement('span', { className: 'zGbnIq_rowName' + (paused ? ' zGbnIq_rowNameDim' : '') }, pl.name),
|
|
1099
1309
|
dotEl),
|
|
1100
1310
|
React.createElement('span', { className: 'zGbnIq_rowActions' },
|
|
1101
1311
|
React.createElement('button', { type: 'button', className: 'zGbnIq_secondaryButton', onClick: onEdit }, '编辑'),
|
|
@@ -1124,6 +1334,8 @@ window.__ModuleLoader__.load({
|
|
|
1124
1334
|
try {
|
|
1125
1335
|
if (!getSettings().visible) return; // 用户主动隐藏,不自动复活
|
|
1126
1336
|
if (getProviders().length === 0) return; // 无提供方实例:卡片本就渲染 null,避免 10s 空转重挂
|
|
1337
|
+
// 全部已暂停同理:currentProvider 回落为 null → 卡片渲染 null,无需重挂
|
|
1338
|
+
if (!currentProvider(getProviders(), getSettings().providerId)) return;
|
|
1127
1339
|
if (rootEl && rootEl.isConnected && document.querySelector('.dsh-pu-card')) return;
|
|
1128
1340
|
mount();
|
|
1129
1341
|
} catch (e) {
|