@jeffusion/bungee-core 5.6.2 → 5.7.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/dist/main.js +17624 -17549
- package/dist/plugins/chatgpt-oauth/manifest.json +14 -0
- package/dist/plugins/chatgpt-oauth/ui/ChatgptQuotaWidget.svelte +44 -35
- package/package.json +2 -2
|
@@ -272,6 +272,13 @@
|
|
|
272
272
|
"en": {
|
|
273
273
|
"ui.widgetTitle": "ChatGPT quota",
|
|
274
274
|
"ui.widgetAccounts": "Account quota usage",
|
|
275
|
+
"ui.widgetPage": "Account page",
|
|
276
|
+
"ui.widgetPrevious": "Previous account page",
|
|
277
|
+
"ui.widgetNext": "Next account page",
|
|
278
|
+
"ui.widgetPause": "Pause account rotation",
|
|
279
|
+
"ui.widgetPlay": "Start account rotation",
|
|
280
|
+
"ui.widgetReducedMotion": "Account rotation disabled by reduced motion preference",
|
|
281
|
+
"ui.widgetPosition": "Page {position} of {total}",
|
|
275
282
|
"ui.widgetEmpty": "No ChatGPT accounts",
|
|
276
283
|
"ui.widgetPartialAccounts": "Some account details are invalid. Showing the remaining accounts.",
|
|
277
284
|
"ui.widgetResetUnknown": "unknown time",
|
|
@@ -472,6 +479,13 @@
|
|
|
472
479
|
"zh-CN": {
|
|
473
480
|
"ui.widgetTitle": "ChatGPT 额度",
|
|
474
481
|
"ui.widgetAccounts": "账号额度用量",
|
|
482
|
+
"ui.widgetPage": "账号页",
|
|
483
|
+
"ui.widgetPrevious": "上一页账号",
|
|
484
|
+
"ui.widgetNext": "下一页账号",
|
|
485
|
+
"ui.widgetPause": "暂停账号轮播",
|
|
486
|
+
"ui.widgetPlay": "开始账号轮播",
|
|
487
|
+
"ui.widgetReducedMotion": "已按减少动态效果偏好关闭账号轮播",
|
|
488
|
+
"ui.widgetPosition": "第 {position} 页,共 {total} 页",
|
|
475
489
|
"ui.widgetEmpty": "暂无 ChatGPT 账号",
|
|
476
490
|
"ui.widgetPartialAccounts": "部分账号详情无效,已显示其余账号。",
|
|
477
491
|
"ui.widgetResetUnknown": "时间未知",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { isLoading } from 'svelte-i18n';
|
|
6
6
|
import { getPluginText } from '$utils/plugin-i18n';
|
|
7
7
|
import type { NativeWidgetHeaderChange } from '$components/native-widgets/widget-header';
|
|
8
|
-
import { LoadingIndicator, MetricBar, StatusBadge } from '$components/industrial';
|
|
8
|
+
import { BCarouselList, LoadingIndicator, MetricBar, StatusBadge } from '$components/industrial';
|
|
9
9
|
import { accountSummary, accountUsage, errorText } from './account-model.js';
|
|
10
10
|
|
|
11
11
|
let { pluginName = 'chatgpt-oauth', onHeaderChange }: { pluginName?: string; selectedRange?: string; onHeaderChange?: NativeWidgetHeaderChange } = $props();
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
let rows = $state<Row[]>([]), busy = $state(true), loaded = $state(false), notice = $state('');
|
|
14
14
|
let generation = 0, disposed = false, controller: AbortController | undefined;
|
|
15
15
|
const t = (key: string, values: Record<string, string | number> = {}) => $isLoading ? '' : getPluginText(key, pluginName, (key, options) => $_(key, { ...options, values }));
|
|
16
|
+
const carouselLabels = $derived({
|
|
17
|
+
previous: t('ui.widgetPrevious'), next: t('ui.widgetNext'),
|
|
18
|
+
pause: t('ui.widgetPause'), play: t('ui.widgetPlay'),
|
|
19
|
+
reducedMotion: t('ui.widgetReducedMotion'), slide: t('ui.widgetPage'),
|
|
20
|
+
goTo: (position: number, total: number) => t('ui.widgetPosition', { position, total }),
|
|
21
|
+
position: (position: number, total: number) => t('ui.widgetPosition', { position, total }),
|
|
22
|
+
});
|
|
16
23
|
const dateText = (value: unknown) => typeof value === 'number' && Number.isFinite(value) && Math.abs(value) <= 8640000000000000
|
|
17
24
|
? new Date(value).toLocaleString($locale ?? 'en', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : t('ui.widgetResetUnknown');
|
|
18
25
|
const validPercent = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 100;
|
|
@@ -103,47 +110,49 @@
|
|
|
103
110
|
});
|
|
104
111
|
</script>
|
|
105
112
|
|
|
113
|
+
{#snippet accountContent(row: Row, measuring = false)}
|
|
114
|
+
{@const account = row.account}
|
|
115
|
+
{@const state = usageState(row)}
|
|
116
|
+
<section class="quota-account grid shrink-0 min-w-0 grid-cols-1 items-start gap-3" data-testid={measuring ? undefined : 'quota-account'}>
|
|
117
|
+
<div class="min-w-0 space-y-1.5">
|
|
118
|
+
<div class="flex flex-wrap items-start justify-between gap-1">
|
|
119
|
+
<span class="min-w-0 flex-1 break-all text-sm font-semibold text-zinc-200">{account.label || account.email || t('ui.account')}</span>
|
|
120
|
+
<StatusBadge variant={account.available ? 'active' : account.status === 'revoked' ? 'muted' : 'standby'}>{t(account.available ? 'ui.available' : account.status === 'active' ? 'account.reauth_required' : `account.${account.status}`)}</StatusBadge>
|
|
121
|
+
</div>
|
|
122
|
+
{#if account.email && account.email !== account.label}<p class="break-all text-xs text-zinc-400">{account.email}</p>{/if}
|
|
123
|
+
{#if account.plan}<p class="text-xs text-zinc-400">{t('ui.accountType', { plan: account.plan })}</p>{/if}
|
|
124
|
+
<div class="flex flex-wrap items-baseline gap-x-2 text-xs text-zinc-400"><span>{t('ui.resetCredits')} · <span class="nx-display tabular-nums text-zinc-100" data-testid="quota-count">{creditCount(row) ?? '—'}</span></span>{#if state !== 'fresh'}<span data-testid="quota-state" class={state === 'stale' || state === 'partial' ? 'text-amber-300' : 'text-zinc-400'}>{t(`ui.usage.${state}`)}</span>{/if}</div>
|
|
125
|
+
{#if row.error}<p class="text-xs text-amber-300">{t(row.error)}</p>{/if}
|
|
126
|
+
</div>
|
|
127
|
+
<div class="min-w-0 space-y-1.5">
|
|
128
|
+
{#if account.status !== 'active'}<p class="text-xs text-zinc-400">{t('ui.usageSkipped')}</p>
|
|
129
|
+
{:else if !row.usage && busy && !row.error}<LoadingIndicator size="xs" centered={false} height="none" label={t('ui.usageLoading')} />
|
|
130
|
+
{:else}
|
|
131
|
+
{#each [row.usage?.usage.value?.primary, row.usage?.usage.value?.secondary].filter(Boolean) as window}
|
|
132
|
+
<div class="space-y-1">
|
|
133
|
+
{#if validPercent(window?.usedPercent)}<MetricBar label={windowLabel(window?.windowSeconds)} value={window.usedPercent} valueLabel={`${window.usedPercent}% ${t('ui.used')}`} />
|
|
134
|
+
{:else}<p class="text-xs text-zinc-400">{windowLabel(window?.windowSeconds)} · {t('ui.widgetUsageUnknown')}</p>{/if}
|
|
135
|
+
<p class="text-xs tabular-nums text-zinc-400">{t('ui.resetAt', { value: dateText(window?.resetAt) })}</p>
|
|
136
|
+
</div>
|
|
137
|
+
{/each}
|
|
138
|
+
{#if !row.usage?.usage.value?.primary && !row.usage?.usage.value?.secondary}<p class="text-xs text-zinc-400">{t('ui.noUsageWindows')}</p>{/if}
|
|
139
|
+
{/if}
|
|
140
|
+
</div>
|
|
141
|
+
</section>
|
|
142
|
+
{/snippet}
|
|
143
|
+
|
|
106
144
|
<div class="quota-widget flex h-full min-h-0 min-w-0 flex-col" data-testid="chatgpt-quota-widget">
|
|
107
|
-
|
|
108
|
-
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
109
|
-
<div class="min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1 focus-visible:outline focus-visible:outline-1 focus-visible:outline-nexus-500" role="region" aria-label={t('ui.widgetAccounts')} tabindex="0" data-testid="quota-list">
|
|
110
|
-
{#if notice}<p role="alert" class="mb-2 text-xs text-amber-300">{t(notice)}</p>{/if}
|
|
145
|
+
{#if notice}<p role="alert" class="mb-2 max-h-12 shrink-0 overflow-y-auto text-xs text-amber-300">{t(notice)}</p>{/if}
|
|
111
146
|
{#if busy && !loaded}<LoadingIndicator size="xs" height="none" label={t('ui.accountsLoading')} />
|
|
112
147
|
{:else if loaded && !rows.length}<p class="py-3 text-sm text-zinc-400">{t('ui.widgetEmpty')}</p>
|
|
113
148
|
{:else}
|
|
114
|
-
<
|
|
115
|
-
{#
|
|
116
|
-
|
|
117
|
-
{@const state = usageState(row)}
|
|
118
|
-
<section class="min-w-0 space-y-1.5 border-b border-carbon-600 pb-2" data-testid="quota-account">
|
|
119
|
-
<div class="flex flex-wrap items-start justify-between gap-1">
|
|
120
|
-
<span class="min-w-0 flex-1 break-all text-sm font-semibold text-zinc-200">{account.label || account.email || t('ui.account')}</span>
|
|
121
|
-
<StatusBadge variant={account.available ? 'active' : account.status === 'revoked' ? 'muted' : 'standby'}>{t(account.available ? 'ui.available' : account.status === 'active' ? 'account.reauth_required' : `account.${account.status}`)}</StatusBadge>
|
|
122
|
-
</div>
|
|
123
|
-
{#if account.email && account.email !== account.label}<p class="break-all text-xs text-zinc-400">{account.email}</p>{/if}
|
|
124
|
-
{#if account.plan}<p class="text-xs text-zinc-400">{t('ui.accountType', { plan: account.plan })}</p>{/if}
|
|
125
|
-
<div class="flex flex-wrap items-baseline gap-x-2 text-xs text-zinc-400"><span>{t('ui.resetCredits')} · <span class="nx-display tabular-nums text-zinc-100" data-testid="quota-count">{creditCount(row) ?? '—'}</span></span>{#if state !== 'fresh'}<span data-testid="quota-state" class={state === 'stale' || state === 'partial' ? 'text-amber-300' : 'text-zinc-400'}>{t(`ui.usage.${state}`)}</span>{/if}</div>
|
|
126
|
-
{#if row.error}<p class="text-xs text-amber-300">{t(row.error)}</p>{/if}
|
|
127
|
-
{#if account.status !== 'active'}<p class="text-xs text-zinc-400">{t('ui.usageSkipped')}</p>
|
|
128
|
-
{:else if !row.usage && busy && !row.error}<LoadingIndicator size="xs" centered={false} height="none" label={t('ui.usageLoading')} />
|
|
129
|
-
{:else}
|
|
130
|
-
{#each [row.usage?.usage.value?.primary, row.usage?.usage.value?.secondary].filter(Boolean) as window}
|
|
131
|
-
<div class="space-y-1">
|
|
132
|
-
{#if validPercent(window?.usedPercent)}<MetricBar label={windowLabel(window?.windowSeconds)} value={window.usedPercent} valueLabel={`${window.usedPercent}% ${t('ui.used')}`} />
|
|
133
|
-
{:else}<p class="text-xs text-zinc-400">{windowLabel(window?.windowSeconds)} · {t('ui.widgetUsageUnknown')}</p>{/if}
|
|
134
|
-
<p class="text-xs tabular-nums text-zinc-400">{t('ui.resetAt', { value: dateText(window?.resetAt) })}</p>
|
|
135
|
-
</div>
|
|
136
|
-
{/each}
|
|
137
|
-
{#if !row.usage?.usage.value?.primary && !row.usage?.usage.value?.secondary}<p class="text-xs text-zinc-400">{t('ui.noUsageWindows')}</p>{/if}
|
|
138
|
-
{/if}
|
|
139
|
-
</section>
|
|
140
|
-
{/each}
|
|
141
|
-
</div>
|
|
149
|
+
<BCarouselList items={rows} itemKey={row => row.account.id} separated ariaLabel={t('ui.widgetAccounts')} labels={carouselLabels}>
|
|
150
|
+
{#snippet children(row, measuring)}{@render accountContent(row, measuring)}{/snippet}
|
|
151
|
+
</BCarouselList>
|
|
142
152
|
{/if}
|
|
143
|
-
</div>
|
|
144
153
|
</div>
|
|
145
154
|
|
|
146
155
|
<style>
|
|
147
156
|
.quota-widget { container-type: inline-size; }
|
|
148
|
-
@container (min-width: 32rem) { .quota-
|
|
157
|
+
@container (min-width: 32rem) { .quota-account { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
149
158
|
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffusion/bungee-core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.0",
|
|
4
4
|
"description": "Bungee reverse proxy core engine",
|
|
5
5
|
"main": "./dist/main.js",
|
|
6
6
|
"types": "./dist/main.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"test": "bun test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@jeffusion/bungee-types": "^5.
|
|
15
|
+
"@jeffusion/bungee-types": "^5.7.0",
|
|
16
16
|
"@types/lodash-es": "^4.17.12",
|
|
17
17
|
"acorn": "^8.15.0",
|
|
18
18
|
"canonicalize": "3.0.0",
|