@goodandready/dsh-subscriptions 0.5.0 → 0.5.2
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 +51 -4
- package/lib/plan.js +42 -0
- package/lib/relative-time.js +47 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -83,6 +83,8 @@ window.__ModuleLoader__.load({
|
|
|
83
83
|
'.dsub-brandOllama{background:rgba(107,114,128,.15);color:#9ca3af;border:1px solid rgba(107,114,128,.3)}' +
|
|
84
84
|
'.dsub-brandKimi{background:rgba(236,72,153,.15);color:#f472b6;border:1px solid rgba(236,72,153,.3)}' +
|
|
85
85
|
'.dsub-brandGlm{background:rgba(20,184,166,.15);color:#2dd4bf;border:1px solid rgba(20,184,166,.3)}' +
|
|
86
|
+
'.dsub-planBadge{display:inline-flex;align-items:center;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;background:rgba(99,102,241,.15);color:#818cf8;border:1px solid rgba(99,102,241,.3);margin-left:6px}' +
|
|
87
|
+
|
|
86
88
|
'.dsub-accInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}' +
|
|
87
89
|
'.dsub-accNameRow{display:flex;align-items:center;justify-content:space-between;gap:8px}' +
|
|
88
90
|
'.dsub-accName{font-size:13px;font-weight:600}' +
|
|
@@ -322,6 +324,47 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
322
324
|
}
|
|
323
325
|
|
|
324
326
|
// #51: live countdown to the quota window reset (account.quota.resetAt).
|
|
327
|
+
function normalizePlanBadge(provider, plan) {
|
|
328
|
+
if (!plan && provider !== 'kimi' && provider !== 'glm') return null
|
|
329
|
+
var p = String(plan || '').toLowerCase().replace(/[^a-z0-9]/g, '')
|
|
330
|
+
var label = plan
|
|
331
|
+
if (provider === 'codex') {
|
|
332
|
+
if (p.indexOf('pro20') >= 0 || p === 'pro') label = 'Pro 20x'
|
|
333
|
+
else if (p.indexOf('pro5') >= 0 || p.indexOf('prolite') >= 0) label = 'Pro 5x'
|
|
334
|
+
else if (p.indexOf('team') >= 0) label = 'Team'
|
|
335
|
+
else if (p.indexOf('plus') >= 0) label = 'Plus'
|
|
336
|
+
else if (p.indexOf('enterp') >= 0) label = 'Enterprise'
|
|
337
|
+
} else if (provider === 'grok') {
|
|
338
|
+
if (p.indexOf('super') >= 0) label = 'SuperGrok'
|
|
339
|
+
else if (p.indexOf('plus') >= 0) label = 'X Premium+'
|
|
340
|
+
else if (p.indexOf('premium') >= 0) label = 'X Premium'
|
|
341
|
+
} else if (provider === 'antigravity') {
|
|
342
|
+
if (p.indexOf('ultra') >= 0) label = 'Ultra'
|
|
343
|
+
else if (p.indexOf('pro') >= 0) label = 'Pro'
|
|
344
|
+
} else if (provider === 'kimi') {
|
|
345
|
+
label = 'Coding Plan'
|
|
346
|
+
} else if (provider === 'glm') {
|
|
347
|
+
label = '150% Boost'
|
|
348
|
+
}
|
|
349
|
+
return label ? React.createElement('span', { className: 'dsub-planBadge' }, label) : null
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function formatRelativeReset(resetAt, lang, now) {
|
|
353
|
+
if (!resetAt || !Number.isFinite(resetAt) || resetAt <= 0) return ''
|
|
354
|
+
var delta = resetAt - (now || Date.now())
|
|
355
|
+
var isRu = lang !== 'en'
|
|
356
|
+
if (delta <= 0) return isRu ? 'только что' : 'just now'
|
|
357
|
+
var totalMinutes = Math.max(1, Math.round(delta / 60000))
|
|
358
|
+
var days = Math.floor(totalMinutes / 1440)
|
|
359
|
+
var hours = Math.floor((totalMinutes % 1440) / 60)
|
|
360
|
+
var minutes = totalMinutes % 60
|
|
361
|
+
var bits = []
|
|
362
|
+
if (days) bits.push(days + (isRu ? ' дн' : 'd'))
|
|
363
|
+
if (hours) bits.push(hours + (isRu ? ' ч' : 'h'))
|
|
364
|
+
if (minutes || !bits.length) bits.push(minutes + (isRu ? ' мин' : 'm'))
|
|
365
|
+
return (isRu ? 'через ' : 'in ') + bits.join(' ')
|
|
366
|
+
}
|
|
367
|
+
|
|
325
368
|
function ResetCountdown(props) {
|
|
326
369
|
const [now, setNow] = React.useState(Date.now())
|
|
327
370
|
React.useEffect(() => {
|
|
@@ -331,12 +374,15 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
331
374
|
const ms = (props.resetAt || 0) - now
|
|
332
375
|
if (ms <= 0) return null
|
|
333
376
|
const total = Math.floor(ms / 1000)
|
|
334
|
-
|
|
335
|
-
|
|
377
|
+
if (total > 3600) {
|
|
378
|
+
return React.createElement('span', { className: 'dsub-sub', style: { fontVariantNumeric: 'tabular-nums' } },
|
|
379
|
+
t('resetLabel') + ' ' + formatRelativeReset(props.resetAt, t('lang'), now))
|
|
380
|
+
}
|
|
381
|
+
const m = Math.floor(total / 60)
|
|
336
382
|
const sec = total % 60
|
|
337
383
|
const pad = (n) => String(n).padStart(2, '0')
|
|
338
384
|
return React.createElement('span', { className: 'dsub-sub', style: { fontVariantNumeric: 'tabular-nums' } },
|
|
339
|
-
t('resetLabel') + ' ' + pad(
|
|
385
|
+
t('resetLabel') + ' ' + pad(m) + ':' + pad(sec))
|
|
340
386
|
}
|
|
341
387
|
|
|
342
388
|
function badgeFor(account, t) {
|
|
@@ -718,6 +764,7 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
718
764
|
},
|
|
719
765
|
}),
|
|
720
766
|
React.createElement('span', { className: 'dsub-badge' + (account.validationUrl ? ' dsub-badge-warn' : (on ? ' dsub-badge-on' : '')) }, badgeFor(account, t)),
|
|
767
|
+
normalizePlanBadge(row.slot.provider, account.plan || (account.quota && account.quota.plan)),
|
|
721
768
|
React.createElement('button', {
|
|
722
769
|
type: 'button', className: 'dsub-mini',
|
|
723
770
|
onClick: () => connect(row.slot.provider, row.slot.index).catch((e) => setErr(String(e.message || e))),
|
|
@@ -1222,7 +1269,7 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1222
1269
|
React.createElement('div', { className: 'dsub-heroBars' },
|
|
1223
1270
|
React.createElement('div', null,
|
|
1224
1271
|
React.createElement('div', { className: 'dsub-barLabelRow' },
|
|
1225
|
-
React.createElement('span', { style: { fontWeight: 600 } }, (primaryWin && primaryWin.label) ? primaryWin.label + ' window' : t('subs5hWindow')),
|
|
1272
|
+
React.createElement('span', { style: { fontWeight: 600 } }, ((primaryWin && primaryWin.label) ? primaryWin.label + ' window' : t('subs5hWindow')) + (primaryWin && primaryWin.resetAt ? ' (' + formatRelativeReset(primaryWin.resetAt, t('lang'), now) + ')' : '')),
|
|
1226
1273
|
React.createElement('span', { style: { fontVariantNumeric: 'tabular-nums', fontWeight: 600 } }, primPct + '% ' + t('subsUsed') + ' (' + (100 - primPct) + '% ' + t('subsFree') + ')'),
|
|
1227
1274
|
),
|
|
1228
1275
|
React.createElement('div', { className: 'dsub-barTrack' },
|
package/lib/plan.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize and pretty-print subscription plans.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function normalizePlanName(provider, rawPlan) {
|
|
6
|
+
if (!rawPlan || typeof rawPlan !== 'string') {
|
|
7
|
+
if (provider === 'kimi') return 'Coding Plan'
|
|
8
|
+
if (provider === 'glm') return 'Coding Plan 150%'
|
|
9
|
+
return ''
|
|
10
|
+
}
|
|
11
|
+
const key = rawPlan.trim().toLowerCase().replace(/[^a-z0-9]/g, '')
|
|
12
|
+
if (provider === 'codex') {
|
|
13
|
+
if (key.includes('pro20') || key === 'pro') return 'Pro 20x'
|
|
14
|
+
if (key.includes('pro5') || key.includes('prolite')) return 'Pro 5x'
|
|
15
|
+
if (key.includes('team')) return 'Team'
|
|
16
|
+
if (key.includes('plus')) return 'Plus'
|
|
17
|
+
if (key.includes('enterp')) return 'Enterprise'
|
|
18
|
+
if (key.includes('edu')) return 'Edu'
|
|
19
|
+
if (key.includes('free')) return 'Free'
|
|
20
|
+
}
|
|
21
|
+
if (provider === 'grok') {
|
|
22
|
+
if (key.includes('super')) return 'SuperGrok'
|
|
23
|
+
if (key.includes('plus') || key.includes('premiumplus')) return 'X Premium+'
|
|
24
|
+
if (key.includes('premium')) return 'X Premium'
|
|
25
|
+
if (key.includes('basic')) return 'X Basic'
|
|
26
|
+
if (key.includes('free')) return 'Free'
|
|
27
|
+
}
|
|
28
|
+
if (provider === 'claude') {
|
|
29
|
+
if (key.includes('team')) return 'Team'
|
|
30
|
+
if (key.includes('enterp')) return 'Enterprise'
|
|
31
|
+
if (key.includes('pro')) return 'Pro'
|
|
32
|
+
if (key.includes('free')) return 'Free'
|
|
33
|
+
}
|
|
34
|
+
if (provider === 'antigravity') {
|
|
35
|
+
if (key.includes('ultra')) return 'Ultra'
|
|
36
|
+
if (key.includes('pro')) return 'Pro'
|
|
37
|
+
if (key.includes('free')) return 'Free'
|
|
38
|
+
}
|
|
39
|
+
if (provider === 'kimi') return 'Coding Plan'
|
|
40
|
+
if (provider === 'glm') return 'Coding Plan 150%'
|
|
41
|
+
return rawPlan
|
|
42
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a future timestamp as a relative remaining duration down to the minute.
|
|
3
|
+
* Supports en and ru.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const RELATIVE_UNITS = {
|
|
7
|
+
en: {
|
|
8
|
+
soon: 'just now',
|
|
9
|
+
prefix: 'in ',
|
|
10
|
+
suffix: '',
|
|
11
|
+
minute: '{n}m',
|
|
12
|
+
hour: '{n}h',
|
|
13
|
+
day: '{n}d',
|
|
14
|
+
},
|
|
15
|
+
ru: {
|
|
16
|
+
soon: 'только что',
|
|
17
|
+
prefix: 'через ',
|
|
18
|
+
suffix: '',
|
|
19
|
+
minute: '{n} мин',
|
|
20
|
+
hour: '{n} ч',
|
|
21
|
+
day: '{n} дн',
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function fill(template, n) {
|
|
26
|
+
return String(template).replace('{n}', String(n))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function formatRelativeReset(resetAt, lang = 'ru', now = Date.now()) {
|
|
30
|
+
if (typeof resetAt !== 'number' || !Number.isFinite(resetAt) || resetAt <= 0) return ''
|
|
31
|
+
const delta = resetAt - now
|
|
32
|
+
const units = RELATIVE_UNITS[lang] || RELATIVE_UNITS.ru
|
|
33
|
+
if (delta <= 0) return units.soon
|
|
34
|
+
|
|
35
|
+
const totalMinutes = Math.max(1, Math.round(delta / 60_000))
|
|
36
|
+
const days = Math.floor(totalMinutes / 1440)
|
|
37
|
+
const hours = Math.floor((totalMinutes % 1440) / 60)
|
|
38
|
+
const minutes = totalMinutes % 60
|
|
39
|
+
|
|
40
|
+
const bits = []
|
|
41
|
+
if (days) bits.push(fill(units.day, days))
|
|
42
|
+
if (hours) bits.push(fill(units.hour, hours))
|
|
43
|
+
if (minutes || bits.length === 0) bits.push(fill(units.minute, minutes))
|
|
44
|
+
|
|
45
|
+
const durationStr = bits.join(' ')
|
|
46
|
+
return `${units.prefix}${durationStr}${units.suffix}`.trim()
|
|
47
|
+
}
|
package/package.json
CHANGED