@goodandready/dsh-subscriptions 0.5.0 → 0.5.1

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 CHANGED
@@ -322,6 +322,22 @@ const cssId = 'dsh-subscriptions/settings.module.css'
322
322
  }
323
323
 
324
324
  // #51: live countdown to the quota window reset (account.quota.resetAt).
325
+ function formatRelativeReset(resetAt, lang, now) {
326
+ if (!resetAt || !Number.isFinite(resetAt) || resetAt <= 0) return ''
327
+ var delta = resetAt - (now || Date.now())
328
+ var isRu = lang !== 'en'
329
+ if (delta <= 0) return isRu ? 'только что' : 'just now'
330
+ var totalMinutes = Math.max(1, Math.round(delta / 60000))
331
+ var days = Math.floor(totalMinutes / 1440)
332
+ var hours = Math.floor((totalMinutes % 1440) / 60)
333
+ var minutes = totalMinutes % 60
334
+ var bits = []
335
+ if (days) bits.push(days + (isRu ? ' дн' : 'd'))
336
+ if (hours) bits.push(hours + (isRu ? ' ч' : 'h'))
337
+ if (minutes || !bits.length) bits.push(minutes + (isRu ? ' мин' : 'm'))
338
+ return (isRu ? 'через ' : 'in ') + bits.join(' ')
339
+ }
340
+
325
341
  function ResetCountdown(props) {
326
342
  const [now, setNow] = React.useState(Date.now())
327
343
  React.useEffect(() => {
@@ -331,12 +347,15 @@ const cssId = 'dsh-subscriptions/settings.module.css'
331
347
  const ms = (props.resetAt || 0) - now
332
348
  if (ms <= 0) return null
333
349
  const total = Math.floor(ms / 1000)
334
- const h = Math.floor(total / 3600)
335
- const m = Math.floor((total % 3600) / 60)
350
+ if (total > 3600) {
351
+ return React.createElement('span', { className: 'dsub-sub', style: { fontVariantNumeric: 'tabular-nums' } },
352
+ t('resetLabel') + ' ' + formatRelativeReset(props.resetAt, t('lang'), now))
353
+ }
354
+ const m = Math.floor(total / 60)
336
355
  const sec = total % 60
337
356
  const pad = (n) => String(n).padStart(2, '0')
338
357
  return React.createElement('span', { className: 'dsub-sub', style: { fontVariantNumeric: 'tabular-nums' } },
339
- t('resetLabel') + ' ' + pad(h) + ':' + pad(m) + ':' + pad(sec))
358
+ t('resetLabel') + ' ' + pad(m) + ':' + pad(sec))
340
359
  }
341
360
 
342
361
  function badgeFor(account, t) {
@@ -1222,7 +1241,7 @@ const cssId = 'dsh-subscriptions/settings.module.css'
1222
1241
  React.createElement('div', { className: 'dsub-heroBars' },
1223
1242
  React.createElement('div', null,
1224
1243
  React.createElement('div', { className: 'dsub-barLabelRow' },
1225
- React.createElement('span', { style: { fontWeight: 600 } }, (primaryWin && primaryWin.label) ? primaryWin.label + ' window' : t('subs5hWindow')),
1244
+ React.createElement('span', { style: { fontWeight: 600 } }, ((primaryWin && primaryWin.label) ? primaryWin.label + ' window' : t('subs5hWindow')) + (primaryWin && primaryWin.resetAt ? ' (' + formatRelativeReset(primaryWin.resetAt, t('lang'), now) + ')' : '')),
1226
1245
  React.createElement('span', { style: { fontVariantNumeric: 'tabular-nums', fontWeight: 600 } }, primPct + '% ' + t('subsUsed') + ' (' + (100 - primPct) + '% ' + t('subsFree') + ')'),
1227
1246
  ),
1228
1247
  React.createElement('div', { className: 'dsub-barTrack' },
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Use ChatGPT Codex, Claude, Grok, and Antigravity subscriptions as DeepSeek Harness LLM providers via OAuth.",
5
5
  "license": "MIT",
6
6
  "type": "module",