@goodandready/dsh-subscriptions 0.4.19 → 0.4.21
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/lib/index.js +3 -3
- 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/lib/index.js
CHANGED
|
@@ -373,7 +373,7 @@ export function apply(ctx, config) {
|
|
|
373
373
|
verifier: row.verifier,
|
|
374
374
|
challenge: row.challenge,
|
|
375
375
|
state: row.state,
|
|
376
|
-
}, code,
|
|
376
|
+
}, code, fetchForRef(ref))
|
|
377
377
|
const slots = normalizeSlots(live().slots)
|
|
378
378
|
const slot = slots.find((s) => s.ref === ref)
|
|
379
379
|
if (slot && slot.label) blob.label = slot.label
|
|
@@ -921,7 +921,7 @@ export function apply(ctx, config) {
|
|
|
921
921
|
}
|
|
922
922
|
try {
|
|
923
923
|
const cfg = vendorConfig(provider, live())
|
|
924
|
-
const start = await vendor.deviceStart(cfg,
|
|
924
|
+
const start = await vendor.deviceStart(cfg, fetchForRef(oauthRef(provider, index)))
|
|
925
925
|
const state = 'dev-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10)
|
|
926
926
|
sweepPending(Date.now())
|
|
927
927
|
pending.set(state, {
|
|
@@ -970,7 +970,7 @@ export function apply(ctx, config) {
|
|
|
970
970
|
const vendor = getVendor(row.provider)
|
|
971
971
|
try {
|
|
972
972
|
const cfg = vendorConfig(row.provider, live())
|
|
973
|
-
const out = await vendor.devicePoll(cfg, { deviceAuthId: row.deviceAuthId, userCode: row.userCode },
|
|
973
|
+
const out = await vendor.devicePoll(cfg, { deviceAuthId: row.deviceAuthId, userCode: row.userCode }, fetchForRef(row.ref))
|
|
974
974
|
if (out.status !== 'authorized') {
|
|
975
975
|
writeJson(res, 200, { ok: true, status: out.status })
|
|
976
976
|
return
|
package/package.json
CHANGED