@goodandready/dsh-subscriptions 0.4.19 → 0.4.20
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 +238 -85
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -13,7 +13,6 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
const setT = (fn) => { t = fn }
|
|
14
14
|
|
|
15
15
|
const CSS =
|
|
16
|
-
// card — same structure as core PluginCard (bash, agent-loop, web-search)
|
|
17
16
|
'.dsub-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;transition:border-color .16s,background .16s}' +
|
|
18
17
|
'.dsub-card:hover{border-color:var(--dsw-alias-label-dimmed)}' +
|
|
19
18
|
'.dsub-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}' +
|
|
@@ -24,38 +23,22 @@ window.__ModuleLoader__.load({
|
|
|
24
23
|
'.dsub-description{color:var(--dsw-alias-label-secondary);font-size:13px}' +
|
|
25
24
|
'.dsub-chev{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}' +
|
|
26
25
|
'.dsub-chevOpen{transform:rotate(180deg)}' +
|
|
27
|
-
'.dsub-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:
|
|
28
|
-
|
|
29
|
-
'.dsub-
|
|
30
|
-
'.dsub-
|
|
31
|
-
'.dsub-h{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}' +
|
|
26
|
+
'.dsub-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:12px}' +
|
|
27
|
+
'.dsub-block{margin-top:10px}' +
|
|
28
|
+
'.dsub-row{display:flex;align-items:center;gap:8px;margin-top:8px}' +
|
|
29
|
+
'.dsub-h{font-size:13.5px;font-weight:600;color:var(--dsw-alias-label-primary)}' +
|
|
32
30
|
'.dsub-sub{font-size:12px;color:var(--dsw-alias-label-secondary)}' +
|
|
33
|
-
'.dsub-
|
|
34
|
-
'.dsub-
|
|
35
|
-
'.dsub-
|
|
36
|
-
'.dsub-field{display:flex;flex-direction:column;gap:6px;padding:12px 0}' +
|
|
37
|
-
'.dsub-input{height:34px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px}' +
|
|
38
|
-
'.dsub-mini{border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);border-radius:6px;height:28px;padding:0 8px;cursor:pointer}' +
|
|
39
|
-
'.dsub-foot{border-top:1px solid var(--dsw-alias-border-l2);display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px}' +
|
|
40
|
-
'.dsub-save{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}' +
|
|
31
|
+
'.dsub-dim{font-size:11.5px;color:var(--dsw-alias-label-tertiary)}' +
|
|
32
|
+
'.dsub-mini{appearance:none;font:inherit;font-size:12px;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:3px 8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);transition:all .15s}' +
|
|
33
|
+
'.dsub-mini:hover{background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-label-dimmed)}' +
|
|
41
34
|
'.dsub-ok{font-size:12px;color:var(--dsw-alias-state-success-primary)}' +
|
|
42
35
|
'.dsub-bad{font-size:12px;color:var(--dsw-alias-state-error-primary)}' +
|
|
43
|
-
'.dsub-
|
|
44
|
-
'.dsub-
|
|
45
|
-
'.dsub-
|
|
46
|
-
'.dsub-
|
|
47
|
-
'.dsub-
|
|
48
|
-
'.dsub-
|
|
49
|
-
'.dsub-chipDanger{color:var(--dsw-alias-state-error-primary);border-color:currentColor}' +
|
|
50
|
-
'.dsub-chipDot{width:6px;height:6px;border-radius:999px;background:currentColor;flex:none}' +
|
|
51
|
-
'.dsub-chipActive{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);font-size:12px;font-weight:600;line-height:1;white-space:nowrap;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3)}' +
|
|
52
|
-
'.dsub-chipActiveWarn{color:var(--dsw-alias-state-warning-primary);border-color:currentColor}' +
|
|
53
|
-
'.dsub-chipActiveDanger{color:var(--dsw-alias-state-error-primary);border-color:currentColor}' +
|
|
54
|
-
'.dsub-chipActiveOk{color:var(--dsw-alias-state-success-primary);border-color:currentColor}' +
|
|
55
|
-
'.dsub-chipActiveBar{height:3px;width:24px;background:var(--dsw-alias-bg-layer-1);border-radius:2px;flex:none;overflow:hidden}' +
|
|
56
|
-
'.dsub-chipActiveFill{height:100%;border-radius:2px;background:currentColor}' +
|
|
57
|
-
|
|
58
|
-
'.dsub-link{background:none;border:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:12px;padding:0}' +
|
|
36
|
+
'.dsub-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 12px;font-size:12.5px;font-weight:500;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);transition:all .15s}' +
|
|
37
|
+
'.dsub-btn:hover{background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-label-dimmed)}' +
|
|
38
|
+
'.dsub-inp{height:30px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:6px;padding:0 8px;font-size:12px;min-width:180px}' +
|
|
39
|
+
'.dsub-inp:focus{outline:none;border-color:var(--dsw-alias-brand-primary)}' +
|
|
40
|
+
'.dsub-box{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px;margin-top:8px;background:var(--dsw-alias-bg-layer-2)}' +
|
|
41
|
+
'.dsub-cooldown{display:inline-block;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-state-warning-primary);color:#000;font-size:11px;font-weight:600}' +
|
|
59
42
|
'.dsub-bar{position:relative;height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden;flex:1;min-width:80px}' +
|
|
60
43
|
'.dsub-barFill{height:100%;border-radius:3px;background:var(--dsw-alias-state-success-primary);transition:width .2s}' +
|
|
61
44
|
'.dsub-barWarn .dsub-barFill{background:var(--dsw-alias-state-warning-primary)}' +
|
|
@@ -64,20 +47,53 @@ window.__ModuleLoader__.load({
|
|
|
64
47
|
'.dsub-manual{display:flex;flex-direction:column;gap:6px;margin-top:4px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2)}' +
|
|
65
48
|
'.dsub-verify{font-size:12px;color:var(--dsw-alias-state-warning-primary)}' +
|
|
66
49
|
'.dsub-verify a{color:var(--dsw-alias-brand-primary)}' +
|
|
67
|
-
'background:var(--dsw-alias-bg-layer-
|
|
68
|
-
'.dsub-pill{
|
|
69
|
-
'.dsub-pill:
|
|
70
|
-
'.dsub-
|
|
71
|
-
'.dsub-
|
|
72
|
-
'.dsub-
|
|
73
|
-
'.dsub-
|
|
74
|
-
'.dsub-
|
|
75
|
-
'.dsub-
|
|
76
|
-
'.dsub-
|
|
77
|
-
'.dsub-
|
|
78
|
-
'
|
|
79
|
-
'.
|
|
80
|
-
'.dsub-
|
|
50
|
+
'.dsub-pill{display:inline-flex;align-items:center;gap:7px;height:28px;padding:0 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:12px;font-weight:600;cursor:pointer;box-shadow:0 1px 3px rgba(0,0,0,.15),inset 0 1px 0 rgba(255,255,255,.06);transition:all .16s ease;user-select:none}' +
|
|
51
|
+
'.dsub-pill:hover{background:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-tertiary);transform:translateY(-1px);box-shadow:0 3px 8px rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.1)}' +
|
|
52
|
+
'.dsub-pill:active{transform:translateY(0)}' +
|
|
53
|
+
'.dsub-pillTag{padding:1px 5px;border-radius:4px;font-size:10.5px;font-weight:700;letter-spacing:.02em;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary)}' +
|
|
54
|
+
'.dsub-led{width:7px;height:7px;border-radius:50%;flex:none;transition:all .2s ease}' +
|
|
55
|
+
'.dsub-ledOk{background:var(--dsw-alias-state-success-primary,#10b981);box-shadow:0 0 6px rgba(16,185,129,.7)}' +
|
|
56
|
+
'.dsub-ledWarn{background:var(--dsw-alias-state-warning-primary,#f59e0b);box-shadow:0 0 6px rgba(245,158,11,.7)}' +
|
|
57
|
+
'.dsub-ledBad{background:var(--dsw-alias-state-error-primary,#ef4444);box-shadow:0 0 6px rgba(239,68,68,.7)}' +
|
|
58
|
+
'.dsub-ledOff{background:var(--dsw-alias-label-tertiary,#6b7280)}' +
|
|
59
|
+
'.dsub-modalWrap{position:fixed;inset:0;z-index:10020;display:grid;place-items:center;background:rgba(0,0,0,.65);backdrop-filter:blur(10px);animation:dsubFadeIn .16s ease-out}' +
|
|
60
|
+
'.dsub-modal{width:min(520px,94vw);max-height:min(85vh,720px);overflow-y:auto;border:1px solid var(--dsw-alias-border-l1);border-radius:16px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);box-shadow:0 24px 64px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.07);padding:20px;animation:dsubScaleIn .18s cubic-bezier(.16,1,.3,1)}' +
|
|
61
|
+
'@keyframes dsubFadeIn{from{opacity:0}to{opacity:1}}' +
|
|
62
|
+
'@keyframes dsubScaleIn{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}' +
|
|
63
|
+
'.dsub-modalHead{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:14px;border-bottom:1px solid var(--dsw-alias-border-l2)}' +
|
|
64
|
+
'.dsub-modalTitleWrap{display:flex;align-items:center;gap:10px}' +
|
|
65
|
+
'.dsub-modalIcon{width:30px;height:30px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);display:grid;place-items:center;color:var(--dsw-alias-brand-primary);font-size:14px}' +
|
|
66
|
+
'.dsub-modalBadge{font-size:11px;font-weight:600;padding:2px 7px;border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary)}' +
|
|
67
|
+
'.dsub-heroCard{margin-top:14px;padding:14px 16px;border-radius:12px;background:linear-gradient(135deg,var(--dsw-alias-bg-layer-2) 0%,var(--dsw-alias-bg-layer-3) 100%);border:1px solid var(--dsw-alias-border-l1);box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}' +
|
|
68
|
+
'.dsub-heroHead{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}' +
|
|
69
|
+
'.dsub-heroTitle{font-size:14px;font-weight:700;display:flex;align-items:center;gap:6px}' +
|
|
70
|
+
'.dsub-heroModel{font-size:11.5px;color:var(--dsw-alias-label-secondary);font-family:ui-monospace,monospace}' +
|
|
71
|
+
'.dsub-heroBars{display:flex;flex-direction:column;gap:8px;margin-top:10px}' +
|
|
72
|
+
'.dsub-barLabelRow{display:flex;justify-content:space-between;font-size:11.5px;color:var(--dsw-alias-label-secondary);margin-bottom:3px}' +
|
|
73
|
+
'.dsub-barTrack{height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}' +
|
|
74
|
+
'.dsub-barFillGrad{height:100%;border-radius:3px;transition:width .3s ease}' +
|
|
75
|
+
'.dsub-poolTitle{margin-top:16px;margin-bottom:8px;font-size:11.5px;font-weight:700;color:var(--dsw-alias-label-secondary);text-transform:uppercase;letter-spacing:.05em}' +
|
|
76
|
+
'.dsub-accountCard{display:flex;align-items:center;gap:12px;padding:10px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2);margin-top:6px;transition:border-color .15s,background .15s}' +
|
|
77
|
+
'.dsub-accountCard:hover{background:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-tertiary)}' +
|
|
78
|
+
'.dsub-brandBadge{width:32px;height:32px;border-radius:8px;display:grid;place-items:center;font-size:12px;font-weight:700;flex-shrink:0}' +
|
|
79
|
+
'.dsub-brandCodex{background:rgba(16,185,129,.15);color:#10b981;border:1px solid rgba(16,185,129,.3)}' +
|
|
80
|
+
'.dsub-brandClaude{background:rgba(245,158,11,.15);color:#f59e0b;border:1px solid rgba(245,158,11,.3)}' +
|
|
81
|
+
'.dsub-brandGrok{background:rgba(168,85,247,.15);color:#c084fc;border:1px solid rgba(168,85,247,.3)}' +
|
|
82
|
+
'.dsub-brandAgy{background:rgba(59,130,246,.15);color:#60a5fa;border:1px solid rgba(59,130,246,.3)}' +
|
|
83
|
+
'.dsub-brandOllama{background:rgba(107,114,128,.15);color:#9ca3af;border:1px solid rgba(107,114,128,.3)}' +
|
|
84
|
+
'.dsub-accInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}' +
|
|
85
|
+
'.dsub-accNameRow{display:flex;align-items:center;justify-content:space-between;gap:8px}' +
|
|
86
|
+
'.dsub-accName{font-size:13px;font-weight:600}' +
|
|
87
|
+
'.dsub-accStatusTag{font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:4px}' +
|
|
88
|
+
'.dsub-statusOk{background:rgba(16,185,129,.15);color:#34d399}' +
|
|
89
|
+
'.dsub-statusWarn{background:rgba(245,158,11,.15);color:#fbbf24}' +
|
|
90
|
+
'.dsub-statusBad{background:rgba(239,68,68,.15);color:#f87171}' +
|
|
91
|
+
'.dsub-statusOff{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-tertiary)}' +
|
|
92
|
+
'.dsub-modalFoot{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2)}' +
|
|
93
|
+
'.dsub-btnPrimary{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:8px;font-size:12px;font-weight:600;background:var(--dsw-alias-brand-primary,#3b82f6);color:#fff;border:0;cursor:pointer;transition:opacity .15s}' +
|
|
94
|
+
'.dsub-btnPrimary:hover{opacity:.9}' +
|
|
95
|
+
'.dsub-btnSec{display:inline-flex;align-items:center;gap:5px;padding:5px 10px;border-radius:7px;font-size:11.5px;font-weight:500;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;text-decoration:none;transition:all .15s}' +
|
|
96
|
+
'.dsub-btnSec:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-tertiary)}' +
|
|
81
97
|
'.dsub-cq{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary);padding:0 6px}' +
|
|
82
98
|
'.dsub-cqBar{position:relative;width:40px;height:5px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}' +
|
|
83
99
|
'.dsub-cqBarFill{height:100%;border-radius:3px;background:var(--dsw-alias-state-success-primary)}' +
|
|
@@ -132,7 +148,15 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
132
148
|
'diagIssues': 'Open issue tracker',
|
|
133
149
|
'diagHint': 'The report has no tokens, emails, proxy addresses or personal data.',
|
|
134
150
|
'subsPill': 'SUBS',
|
|
135
|
-
'subsModalTitle': 'Subscription
|
|
151
|
+
'subsModalTitle': 'Subscription Hub',
|
|
152
|
+
'subsActiveHero': 'Active Subscription',
|
|
153
|
+
'subsPoolTitle': 'Provider Accounts',
|
|
154
|
+
'subsRefresh': 'Refresh',
|
|
155
|
+
'subsOpenSettings': 'All Settings →',
|
|
156
|
+
'subsFastMode': 'Fast Mode 1.5x',
|
|
157
|
+
'subsHealthy': 'Healthy',
|
|
158
|
+
'subsFree': 'free',
|
|
159
|
+
'subsUsed': 'used',
|
|
136
160
|
'subsModalClose': 'Close',
|
|
137
161
|
'subsLogged': 'connected',
|
|
138
162
|
'subsNotLogged': 'not connected',
|
|
@@ -233,6 +257,14 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
233
257
|
'diagHint': 'В отчёте нет токенов, email, адресов прокси и персональных данных.',
|
|
234
258
|
'subsPill': 'SUBS',
|
|
235
259
|
'subsModalTitle': 'Консоль подписок',
|
|
260
|
+
'subsActiveHero': 'Активная подписка',
|
|
261
|
+
'subsPoolTitle': 'Пул аккаунтов',
|
|
262
|
+
'subsRefresh': 'Обновить',
|
|
263
|
+
'subsOpenSettings': 'Все настройки →',
|
|
264
|
+
'subsFastMode': 'Режим Fast 1.5x',
|
|
265
|
+
'subsHealthy': 'Исправен',
|
|
266
|
+
'subsFree': 'свободно',
|
|
267
|
+
'subsUsed': 'израсходовано',
|
|
236
268
|
'subsModalClose': 'Закрыть',
|
|
237
269
|
'subsLogged': 'подключен',
|
|
238
270
|
'subsNotLogged': 'не подключен',
|
|
@@ -1082,37 +1114,56 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1082
1114
|
|
|
1083
1115
|
// #83: пилл SUBS(N) в шапке сессии со светодиодом здоровья пула и
|
|
1084
1116
|
// модальной консолью аккаунтов (закрытие: крестик, Escape, клик мимо).
|
|
1117
|
+
function brandBadge(prov) {
|
|
1118
|
+
const p = String(prov || '').toLowerCase()
|
|
1119
|
+
if (p.includes('codex') || p.includes('chatgpt') || p.includes('openai')) return { label: 'OpenAI', cls: 'dsub-brandCodex', icon: '⚡' }
|
|
1120
|
+
if (p.includes('claude') || p.includes('anthropic')) return { label: 'Claude', cls: 'dsub-brandClaude', icon: '✳' }
|
|
1121
|
+
if (p.includes('grok') || p.includes('xai')) return { label: 'Grok', cls: 'dsub-brandGrok', icon: '✦' }
|
|
1122
|
+
if (p.includes('antigravity') || p.includes('google') || p.includes('gemini')) return { label: 'AGY', cls: 'dsub-brandAgy', icon: '◆' }
|
|
1123
|
+
if (p.includes('ollama')) return { label: 'Ollama', cls: 'dsub-brandOllama', icon: '🦙' }
|
|
1124
|
+
return { label: (p.charAt(0).toUpperCase() || 'P'), cls: 'dsub-brandCodex', icon: '●' }
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1085
1127
|
function SubsPill(props) {
|
|
1086
1128
|
const t = (props && props.t) || ((k) => k)
|
|
1087
|
-
const [state, setState] = React.useState({ logged: [], usage: 0, accounts: [], open: false, active: null })
|
|
1129
|
+
const [state, setState] = React.useState({ logged: [], usage: 0, accounts: [], open: false, active: null, loading: false })
|
|
1130
|
+
|
|
1131
|
+
const pull = () => {
|
|
1132
|
+
setState((s) => Object.assign({}, s, { loading: true }))
|
|
1133
|
+
refreshLoggedIn().then((r) => {
|
|
1134
|
+
const logged = r.logged || []
|
|
1135
|
+
fetch('/dsh-subscriptions/config', { cache: 'no-store' })
|
|
1136
|
+
.then((res) => res.json())
|
|
1137
|
+
.then((cfg) => {
|
|
1138
|
+
setState((s) => Object.assign({}, s, { logged, usage: r.usage, accounts: (cfg && cfg.accounts) || [], active: r.active, loading: false }))
|
|
1139
|
+
})
|
|
1140
|
+
.catch(() => {
|
|
1141
|
+
setState((s) => Object.assign({}, s, { logged, usage: r.usage, accounts: [], active: r.active, loading: false }))
|
|
1142
|
+
})
|
|
1143
|
+
}).catch(() => {
|
|
1144
|
+
setState((s) => Object.assign({}, s, { loading: false }))
|
|
1145
|
+
})
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1088
1148
|
React.useEffect(() => {
|
|
1089
|
-
let alive = true
|
|
1090
|
-
const pull = () => {
|
|
1091
|
-
refreshLoggedIn().then((r) => {
|
|
1092
|
-
if (!alive) return
|
|
1093
|
-
const logged = r.logged || []
|
|
1094
|
-
fetch('/dsh-subscriptions/config', { cache: 'no-store' })
|
|
1095
|
-
.then((res) => res.json())
|
|
1096
|
-
.then((cfg) => {
|
|
1097
|
-
if (!alive) return
|
|
1098
|
-
setState({ logged, usage: r.usage, accounts: (cfg && cfg.accounts) || [], open: false, active: r.active })
|
|
1099
|
-
})
|
|
1100
|
-
.catch(() => { if (alive) setState({ logged, usage: r.usage, accounts: [], open: false, active: r.active }) })
|
|
1101
|
-
}).catch(() => {})
|
|
1102
|
-
}
|
|
1103
1149
|
pull()
|
|
1104
|
-
const id = setInterval(pull,
|
|
1105
|
-
return () =>
|
|
1150
|
+
const id = setInterval(pull, 25 * 1000)
|
|
1151
|
+
return () => clearInterval(id)
|
|
1106
1152
|
}, [])
|
|
1153
|
+
|
|
1107
1154
|
React.useEffect(() => {
|
|
1108
1155
|
if (!state.open) return undefined
|
|
1109
1156
|
const onKey = (e) => { if (e.key === 'Escape') setState((s) => Object.assign({}, s, { open: false })) }
|
|
1110
1157
|
document.addEventListener('keydown', onKey)
|
|
1111
1158
|
return () => document.removeEventListener('keydown', onKey)
|
|
1112
1159
|
}, [state.open])
|
|
1160
|
+
|
|
1113
1161
|
const n = state.logged.length
|
|
1162
|
+
const total = state.accounts.length || n
|
|
1114
1163
|
const led = n === 0 ? 'dsub-ledOff' : (state.usage >= 90 ? 'dsub-ledBad' : (state.usage >= 50 ? 'dsub-ledWarn' : 'dsub-ledOk'))
|
|
1115
1164
|
const now = Date.now()
|
|
1165
|
+
|
|
1166
|
+
// Header Pill Label
|
|
1116
1167
|
let pillText = t('subsPill') + ' (' + n + ')'
|
|
1117
1168
|
if (state.active) {
|
|
1118
1169
|
const a = state.active
|
|
@@ -1121,29 +1172,116 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1121
1172
|
const provName = a.provider ? (a.provider.charAt(0).toUpperCase() + a.provider.slice(1)) : ''
|
|
1122
1173
|
pillText = (a.provider === 'codex' && a.fastMode ? '⚡ ' : '') + provName + (winStr ? ' · ' + winStr : (a.usagePercent != null ? ' · ' + Math.round(a.usagePercent) + '%' : ''))
|
|
1123
1174
|
}
|
|
1175
|
+
|
|
1176
|
+
// Active Hero Card Renderer
|
|
1177
|
+
const renderActiveHero = () => {
|
|
1178
|
+
if (!state.active) return null
|
|
1179
|
+
const a = state.active
|
|
1180
|
+
const b = brandBadge(a.provider)
|
|
1181
|
+
const wins = Array.isArray(a.windows) ? a.windows : []
|
|
1182
|
+
const primaryWin = wins.find((w) => (w.label || w.id) === '5h') || wins[0]
|
|
1183
|
+
const secWin = wins.find((w) => (w.label || w.id) === '7d') || wins[1]
|
|
1184
|
+
const primPct = primaryWin && primaryWin.usedPercent != null ? Math.round(primaryWin.usedPercent) : (a.usagePercent != null ? Math.round(a.usagePercent) : 0)
|
|
1185
|
+
const secPct = secWin && secWin.usedPercent != null ? Math.round(secWin.usedPercent) : null
|
|
1186
|
+
|
|
1187
|
+
return React.createElement('div', { className: 'dsub-heroCard' },
|
|
1188
|
+
React.createElement('div', { className: 'dsub-heroHead' },
|
|
1189
|
+
React.createElement('div', { className: 'dsub-heroTitle' },
|
|
1190
|
+
React.createElement('span', { className: 'dsub-brandBadge ' + b.cls, style: { width: 24, height: 24, fontSize: 11 } }, b.icon),
|
|
1191
|
+
React.createElement('span', null, (a.provider ? a.provider.toUpperCase() : 'LLM') + (a.index ? ' #' + a.index : '')),
|
|
1192
|
+
a.fastMode ? React.createElement('span', { className: 'dsub-pillTag', style: { background: 'rgba(245,158,11,.15)', color: '#fbbf24' } }, '⚡ FAST 1.5x') : null,
|
|
1193
|
+
),
|
|
1194
|
+
React.createElement('span', { className: 'dsub-heroModel' }, a.model || 'active model'),
|
|
1195
|
+
),
|
|
1196
|
+
React.createElement('div', { className: 'dsub-heroBars' },
|
|
1197
|
+
React.createElement('div', null,
|
|
1198
|
+
React.createElement('div', { className: 'dsub-barLabelRow' },
|
|
1199
|
+
React.createElement('span', { style: { fontWeight: 600 } }, (primaryWin && primaryWin.label) ? primaryWin.label + ' window' : t('subs5hWindow')),
|
|
1200
|
+
React.createElement('span', { style: { fontVariantNumeric: 'tabular-nums', fontWeight: 600 } }, primPct + '% ' + t('subsUsed') + ' (' + (100 - primPct) + '% ' + t('subsFree') + ')'),
|
|
1201
|
+
),
|
|
1202
|
+
React.createElement('div', { className: 'dsub-barTrack' },
|
|
1203
|
+
React.createElement('div', {
|
|
1204
|
+
className: 'dsub-barFillGrad',
|
|
1205
|
+
style: {
|
|
1206
|
+
width: Math.min(100, Math.max(0, primPct)) + '%',
|
|
1207
|
+
background: primPct >= 90 ? 'var(--dsw-alias-state-error-primary,#ef4444)' : (primPct >= 50 ? 'var(--dsw-alias-state-warning-primary,#f59e0b)' : 'var(--dsw-alias-state-success-primary,#10b981)'),
|
|
1208
|
+
},
|
|
1209
|
+
}),
|
|
1210
|
+
),
|
|
1211
|
+
),
|
|
1212
|
+
secWin ? React.createElement('div', null,
|
|
1213
|
+
React.createElement('div', { className: 'dsub-barLabelRow' },
|
|
1214
|
+
React.createElement('span', null, (secWin.label || '7d') + ' window'),
|
|
1215
|
+
React.createElement('span', { style: { fontVariantNumeric: 'tabular-nums' } }, secPct + '%'),
|
|
1216
|
+
),
|
|
1217
|
+
React.createElement('div', { className: 'dsub-barTrack' },
|
|
1218
|
+
React.createElement('div', {
|
|
1219
|
+
className: 'dsub-barFillGrad',
|
|
1220
|
+
style: {
|
|
1221
|
+
width: Math.min(100, Math.max(0, secPct)) + '%',
|
|
1222
|
+
background: secPct >= 90 ? '#ef4444' : (secPct >= 50 ? '#f59e0b' : '#3b82f6'),
|
|
1223
|
+
},
|
|
1224
|
+
}),
|
|
1225
|
+
),
|
|
1226
|
+
) : null,
|
|
1227
|
+
),
|
|
1228
|
+
)
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// Accounts list
|
|
1124
1232
|
const rows = state.accounts.map((a, i) => {
|
|
1125
1233
|
const isLogged = state.logged.includes(a.provider)
|
|
1126
1234
|
const cooled = a.cooldownUntil && a.cooldownUntil > now
|
|
1127
1235
|
const pct = a.usagePercent != null ? Math.round(a.usagePercent) : null
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1236
|
+
const b = brandBadge(a.provider)
|
|
1237
|
+
const isActiveThis = state.active && state.active.provider === a.provider && state.active.index === a.index
|
|
1238
|
+
|
|
1239
|
+
let statusClass = 'dsub-statusOff'
|
|
1240
|
+
let statusText = t('subsNotLogged')
|
|
1241
|
+
if (cooled) {
|
|
1242
|
+
statusClass = 'dsub-statusWarn'
|
|
1243
|
+
statusText = t('subsCooldown')
|
|
1244
|
+
} else if (isLogged) {
|
|
1245
|
+
if (pct >= 90) { statusClass = 'dsub-statusBad'; statusText = '90%+ ' + t('subsUsed') }
|
|
1246
|
+
else if (pct >= 50) { statusClass = 'dsub-statusWarn'; statusText = pct + '% ' + t('subsUsed') }
|
|
1247
|
+
else { statusClass = 'dsub-statusOk'; statusText = (pct != null ? pct + '% · ' : '') + t('subsLogged') }
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
return React.createElement('div', { className: 'dsub-accountCard', key: i },
|
|
1251
|
+
React.createElement('div', { className: 'dsub-brandBadge ' + b.cls }, b.icon),
|
|
1252
|
+
React.createElement('div', { className: 'dsub-accInfo' },
|
|
1253
|
+
React.createElement('div', { className: 'dsub-accNameRow' },
|
|
1254
|
+
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
1255
|
+
React.createElement('span', { className: 'dsub-accName' }, a.provider.toUpperCase() + (a.index ? ' #' + a.index : '')),
|
|
1256
|
+
isActiveThis ? React.createElement('span', { className: 'dsub-pillTag', style: { background: 'rgba(16,185,129,.15)', color: '#34d399' } }, 'ACTIVE') : null,
|
|
1257
|
+
a.proxy ? React.createElement('span', { className: 'dsub-dim', title: a.proxy }, '🌐 proxy') : null,
|
|
1258
|
+
),
|
|
1259
|
+
React.createElement('span', { className: 'dsub-accStatusTag ' + statusClass }, statusText),
|
|
1260
|
+
),
|
|
1261
|
+
(isLogged && pct != null) ? React.createElement('div', { className: 'dsub-barTrack', style: { marginTop: 4, height: 4 } },
|
|
1262
|
+
React.createElement('div', {
|
|
1263
|
+
className: 'dsub-barFillGrad',
|
|
1264
|
+
style: {
|
|
1265
|
+
width: Math.min(100, Math.max(0, pct)) + '%',
|
|
1266
|
+
background: pct >= 90 ? '#ef4444' : (pct >= 50 ? '#f59e0b' : '#10b981'),
|
|
1267
|
+
},
|
|
1268
|
+
}),
|
|
1269
|
+
) : null,
|
|
1136
1270
|
),
|
|
1137
1271
|
)
|
|
1138
1272
|
})
|
|
1273
|
+
|
|
1139
1274
|
return React.createElement(React.Fragment, null,
|
|
1140
1275
|
React.createElement('button', {
|
|
1141
|
-
type: 'button',
|
|
1276
|
+
type: 'button',
|
|
1277
|
+
className: 'dsub-pill',
|
|
1278
|
+
'aria-expanded': state.open ? 'true' : 'false',
|
|
1142
1279
|
title: t('subsPill') + ': ' + (n > 0 ? (n + ' ' + t('connected')) : t('notConnected')),
|
|
1143
1280
|
onClick: () => setState((s) => Object.assign({}, s, { open: !s.open })),
|
|
1144
1281
|
},
|
|
1145
1282
|
React.createElement('span', { className: 'dsub-led ' + led }),
|
|
1146
|
-
pillText,
|
|
1283
|
+
React.createElement('span', null, pillText),
|
|
1284
|
+
React.createElement('span', { style: { fontSize: 9, opacity: 0.6, marginLeft: 2 } }, '▼'),
|
|
1147
1285
|
),
|
|
1148
1286
|
state.open ? React.createElement('div', {
|
|
1149
1287
|
className: 'dsub-modalWrap',
|
|
@@ -1151,16 +1289,36 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1151
1289
|
},
|
|
1152
1290
|
React.createElement('div', { className: 'dsub-modal', role: 'dialog', 'aria-label': t('subsModalTitle') },
|
|
1153
1291
|
React.createElement('div', { className: 'dsub-modalHead' },
|
|
1154
|
-
React.createElement('
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1292
|
+
React.createElement('div', { className: 'dsub-modalTitleWrap' },
|
|
1293
|
+
React.createElement('div', { className: 'dsub-modalIcon' }, '⚡'),
|
|
1294
|
+
React.createElement('div', null,
|
|
1295
|
+
React.createElement('div', { style: { fontWeight: 700, fontSize: 15 } }, t('subsModalTitle')),
|
|
1296
|
+
React.createElement('div', { className: 'dsub-dim' }, n + ' of ' + total + ' ' + t('connected')),
|
|
1297
|
+
),
|
|
1298
|
+
),
|
|
1299
|
+
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
1300
|
+
React.createElement('button', {
|
|
1301
|
+
type: 'button',
|
|
1302
|
+
className: 'dsub-mini',
|
|
1303
|
+
title: t('subsRefresh'),
|
|
1304
|
+
onClick: () => pull(),
|
|
1305
|
+
}, state.loading ? '…' : '🔄'),
|
|
1306
|
+
React.createElement('button', {
|
|
1307
|
+
type: 'button',
|
|
1308
|
+
className: 'dsub-mini',
|
|
1309
|
+
onClick: () => setState((s) => Object.assign({}, s, { open: false })),
|
|
1310
|
+
}, '✕'),
|
|
1311
|
+
),
|
|
1159
1312
|
),
|
|
1160
|
-
|
|
1161
|
-
React.createElement('div', { className: 'dsub-
|
|
1313
|
+
renderActiveHero(),
|
|
1314
|
+
React.createElement('div', { className: 'dsub-poolTitle' }, t('subsPoolTitle')),
|
|
1315
|
+
rows.length ? rows : React.createElement('div', { className: 'dsub-sub', style: { padding: '12px 0' } }, t('subsNotLogged')),
|
|
1316
|
+
React.createElement('div', { className: 'dsub-modalFoot' },
|
|
1317
|
+
React.createElement('span', { className: 'dsub-dim' }, 'DSH Subscriptions'),
|
|
1162
1318
|
React.createElement('a', {
|
|
1163
|
-
className: 'dsub-
|
|
1319
|
+
className: 'dsub-btnSec',
|
|
1320
|
+
href: '#',
|
|
1321
|
+
onClick: (e) => {
|
|
1164
1322
|
e.preventDefault()
|
|
1165
1323
|
setState((s) => Object.assign({}, s, { open: false }))
|
|
1166
1324
|
try {
|
|
@@ -1174,11 +1332,6 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1174
1332
|
) : null,
|
|
1175
1333
|
)
|
|
1176
1334
|
}
|
|
1177
|
-
// #82: перетаскиваемый HUD-бабл (shell.overlay) с кольцевой шкалой
|
|
1178
|
-
// остатка активной подписки, прилипанием к краям и frosted-панелью.
|
|
1179
|
-
const HUD_KEY = 'dsh-subscriptions.hud'
|
|
1180
|
-
const HUD_SIZE = 64
|
|
1181
|
-
|
|
1182
1335
|
|
|
1183
1336
|
function registerSubsPill(ctx) {
|
|
1184
1337
|
ctx.effect(() => {
|
package/package.json
CHANGED