dsh-plugin-subscription-oauth 0.12.0 → 0.12.3
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/auth/rpc.js +2 -0
- package/lib/client/SubscriptionsSection.js +37 -2
- package/lib/client/UsageChip.js +6 -0
- package/lib/client/locales.d.ts +2 -0
- package/lib/client/locales.js +2 -0
- package/lib/client.js +66 -0
- package/lib/client.js.map +1 -1
- package/lib/protocol.d.ts +5 -1
- package/lib/protocol.js +1 -0
- package/lib/providers/mimo.js +5 -4
- package/lib/providers/workbuddy-provider.js +8 -1
- package/lib/providers/workbuddy.js +5 -4
- package/package.json +1 -1
package/lib/auth/rpc.js
CHANGED
|
@@ -155,6 +155,8 @@ async function runEndpoint(registry, endpoint, payload, balance) {
|
|
|
155
155
|
}
|
|
156
156
|
case 'usage':
|
|
157
157
|
return registry.usage(requiredProvider(payload, endpoint), booleanField(payload, 'force'));
|
|
158
|
+
case 'auto-state':
|
|
159
|
+
return registry.autoState();
|
|
158
160
|
case 'setAutoOn': {
|
|
159
161
|
const enabled = booleanField(payload, 'enabled');
|
|
160
162
|
if (enabled === undefined)
|
|
@@ -244,6 +244,8 @@ const styles = {
|
|
|
244
244
|
},
|
|
245
245
|
/** The metered windows under one account row: no border, just the meters. */
|
|
246
246
|
usageInline: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 },
|
|
247
|
+
/** The auto provider's on/off row: a label and its switch. */
|
|
248
|
+
autoSwitchRow: { display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' },
|
|
247
249
|
modelRow: {
|
|
248
250
|
display: 'flex', alignItems: 'flex-start', gap: 8,
|
|
249
251
|
cursor: 'pointer', fontSize: 13, lineHeight: '20px',
|
|
@@ -517,7 +519,7 @@ function ProviderCard(props) {
|
|
|
517
519
|
return (_jsxs("div", { style: cardOpen ? { ...styles.card, ...styles.cardOpen } : styles.card, children: [_jsxs("div", { style: styles.cardHeader, role: "button", tabIndex: 0, "aria-expanded": cardOpen, onClick: toggleCardOpen, onKeyDown: (event) => { if (event.key === 'Enter' || event.key === ' ') {
|
|
518
520
|
event.preventDefault();
|
|
519
521
|
toggleCardOpen();
|
|
520
|
-
} }, children: [_jsx("span", { style: { ...styles.dot, ...dotStyle(status) }, "aria-hidden": "true" }), _jsx("span", { style: styles.name, children: status.displayName }), status.loggedIn && _jsx("span", { style: styles.badgeDefault, children: t('badgeLive') }), _jsx("span", { style: styles.cardSummary, children: summaryLine }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, "aria-expanded": cardOpen, "aria-controls": `dsh-subscription-card-${status.provider}`, onClick: (event) => { event.stopPropagation(); toggleCardOpen(); }, children: cardOpen ? t('collapse') : t('edit') })] }), _jsxs("div", { id: `dsh-subscription-card-${status.provider}`, style: cardOpen ? styles.cardBody : { ...styles.cardBody, display: 'none' }, children: [connectable && !status.loggedIn && !busy && _jsx("p", { style: styles.statusLine, children: t('statusSignIn') }), status.lastError !== undefined && (_jsx("p", { className: "dsh-subscription-error", children: t('lastError', { message: status.lastError }) })), message?.tone === 'error' && _jsx("p", { className: "dsh-subscription-error", children: message.text }), message?.tone === 'success' && _jsx("p", { className: "dsh-subscription-success", children: message.text }), accounts.map((account) => {
|
|
522
|
+
} }, children: [_jsx("span", { style: { ...styles.dot, ...dotStyle(status) }, "aria-hidden": "true" }), _jsx("span", { style: styles.name, children: status.displayName }), status.loggedIn && _jsx("span", { style: styles.badgeDefault, children: t('badgeLive') }), _jsx("span", { style: styles.cardSummary, children: summaryLine }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, "aria-expanded": cardOpen, "aria-controls": `dsh-subscription-card-${status.provider}`, onClick: (event) => { event.stopPropagation(); toggleCardOpen(); }, children: cardOpen ? t('collapse') : t('edit') })] }), _jsxs("div", { id: `dsh-subscription-card-${status.provider}`, style: cardOpen ? styles.cardBody : { ...styles.cardBody, display: 'none' }, children: [connectable && !status.loggedIn && !busy && _jsx("p", { style: styles.statusLine, children: t('statusSignIn') }), status.lastError !== undefined && (_jsx("p", { className: "dsh-subscription-error", children: t('lastError', { message: status.lastError }) })), message?.tone === 'error' && _jsx("p", { className: "dsh-subscription-error", children: message.text }), message?.tone === 'success' && _jsx("p", { className: "dsh-subscription-success", children: message.text }), status.provider === 'auto' && props.onSetAutoOn !== undefined && (_jsxs("label", { style: styles.autoSwitchRow, children: [_jsx("span", { style: styles.modelName, children: t('autoSwitchLabel') }), _jsx("input", { type: "checkbox", className: "dsh-subscription-switch", checked: props.autoOn ?? true, disabled: busyAction, onChange: (event) => { props.onSetAutoOn?.(event.target.checked); } })] })), accounts.map((account) => {
|
|
521
523
|
const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account);
|
|
522
524
|
return (_jsxs("div", { style: styles.accountRow, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("button", { type: "button", style: styles.starButton, title: account.isDefault ? t('defaultBadge') : t('makeDefault'), "aria-label": account.isDefault ? t('defaultBadge') : t('makeDefault'), disabled: busyAction || account.isDefault, onClick: () => { props.onSetDefault(account.account); }, children: account.isDefault ? '★' : '☆' }), _jsx("span", { style: styles.accountName, children: account.label }), account.isDefault && _jsx("span", { style: styles.badgeDefault, children: t('defaultBadge') }), account.needsLogin === true && (_jsx("span", { style: styles.badgeWarn, title: t('accountNeedsLoginHint'), children: t('accountNeedsLogin') })), usageEntry?.plan !== undefined && (_jsx("span", { style: styles.badge, children: t('usagePlan', { plan: usageEntry.plan }) })), _jsx("span", { style: styles.statusLine, children: account.expired
|
|
523
525
|
? t('accountExpired')
|
|
@@ -563,8 +565,20 @@ export function SubscriptionsSection(props) {
|
|
|
563
565
|
const [error, setError] = useState(undefined);
|
|
564
566
|
const [messages, setMessages] = useState({});
|
|
565
567
|
const [busyAction, setBusyAction] = useState(false);
|
|
568
|
+
const [autoOn, setAutoOn] = useState(true);
|
|
566
569
|
const mounted = useRef(true);
|
|
567
570
|
const timer = useRef(undefined);
|
|
571
|
+
/** Read whether the `auto` provider is offered, so its card can render the switch. */
|
|
572
|
+
useEffect(() => {
|
|
573
|
+
if (call === undefined)
|
|
574
|
+
return;
|
|
575
|
+
let live = true;
|
|
576
|
+
void call('auto-state', undefined)
|
|
577
|
+
.then((result) => { if (live)
|
|
578
|
+
setAutoOn(result.enabled); })
|
|
579
|
+
.catch(() => { });
|
|
580
|
+
return () => { live = false; };
|
|
581
|
+
}, [call]);
|
|
568
582
|
/** Fetch the current state; a failure is shown rather than swallowed. */
|
|
569
583
|
const refresh = useCallback(async () => {
|
|
570
584
|
if (call === undefined)
|
|
@@ -581,6 +595,27 @@ export function SubscriptionsSection(props) {
|
|
|
581
595
|
setError(t('actionFailed', { message: messageOf(caught) }));
|
|
582
596
|
}
|
|
583
597
|
}, [call, t]);
|
|
598
|
+
/** Flip the `auto` provider on or off. */
|
|
599
|
+
const setAuto = useCallback(async (enabled) => {
|
|
600
|
+
if (call === undefined)
|
|
601
|
+
return;
|
|
602
|
+
setBusyAction(true);
|
|
603
|
+
try {
|
|
604
|
+
const result = await call('setAutoOn', { enabled });
|
|
605
|
+
if (!mounted.current)
|
|
606
|
+
return;
|
|
607
|
+
setAutoOn(result.enabled);
|
|
608
|
+
await refresh();
|
|
609
|
+
}
|
|
610
|
+
catch (caught) {
|
|
611
|
+
if (mounted.current)
|
|
612
|
+
setError(t('actionFailed', { message: messageOf(caught) }));
|
|
613
|
+
}
|
|
614
|
+
finally {
|
|
615
|
+
if (mounted.current)
|
|
616
|
+
setBusyAction(false);
|
|
617
|
+
}
|
|
618
|
+
}, [call, refresh, t]);
|
|
584
619
|
const stopPolling = useCallback(() => {
|
|
585
620
|
if (timer.current !== undefined) {
|
|
586
621
|
clearInterval(timer.current);
|
|
@@ -824,5 +859,5 @@ export function SubscriptionsSection(props) {
|
|
|
824
859
|
}, [act, call]);
|
|
825
860
|
if (call === undefined)
|
|
826
861
|
return _jsx("p", { style: styles.intro, children: t('unavailable') });
|
|
827
|
-
return (_jsxs("div", { className: "dsh-subscriptions-settings", style: styles.section, children: [_jsx("style", { children: subscriptionsCss }), _jsxs("div", { style: styles.sectionHeader, children: [_jsx("h2", { style: styles.heading, children: t('nav') }), _jsx("p", { style: styles.intro, children: t('intro') })] }), error !== undefined && _jsx("p", { className: "dsh-subscription-error", children: error }), status === undefined && _jsx("p", { style: styles.statusLine, children: t('loading') }), orderedProviders.map((provider) => (_jsx(ProviderCard, { status: provider, t: t, busyAction: busyAction, ...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] }, onLogin: () => { login(provider.provider); }, onCancel: () => { cancel(provider.provider); }, onLogout: (account) => { logout(provider.provider, account); }, onSetDefault: (account) => { setDefault(provider.provider, account); }, onRefreshToken: (account) => { refreshToken(provider.provider, account); }, onRefreshModels: () => { refreshModels(provider.provider); }, onSetModelVisible: (id, visible) => { setModelVisible(provider.provider, id, visible); }, onSetModelOrder: (ids) => { setModelOrder(provider.provider, ids); }, onAddModel: (id) => { addModel(provider.provider, id); }, onRemoveModel: (id) => { removeModel(provider.provider, id); }, onRefreshUsage: () => { refreshUsage(provider.provider); }, onReload: () => { reload(provider.provider); } }, provider.provider)))] }));
|
|
862
|
+
return (_jsxs("div", { className: "dsh-subscriptions-settings", style: styles.section, children: [_jsx("style", { children: subscriptionsCss }), _jsxs("div", { style: styles.sectionHeader, children: [_jsx("h2", { style: styles.heading, children: t('nav') }), _jsx("p", { style: styles.intro, children: t('intro') })] }), error !== undefined && _jsx("p", { className: "dsh-subscription-error", children: error }), status === undefined && _jsx("p", { style: styles.statusLine, children: t('loading') }), orderedProviders.map((provider) => (_jsx(ProviderCard, { status: provider, t: t, busyAction: busyAction, ...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] }, onLogin: () => { login(provider.provider); }, onCancel: () => { cancel(provider.provider); }, onLogout: (account) => { logout(provider.provider, account); }, onSetDefault: (account) => { setDefault(provider.provider, account); }, onRefreshToken: (account) => { refreshToken(provider.provider, account); }, onRefreshModels: () => { refreshModels(provider.provider); }, onSetModelVisible: (id, visible) => { setModelVisible(provider.provider, id, visible); }, onSetModelOrder: (ids) => { setModelOrder(provider.provider, ids); }, onAddModel: (id) => { addModel(provider.provider, id); }, onRemoveModel: (id) => { removeModel(provider.provider, id); }, onRefreshUsage: () => { refreshUsage(provider.provider); }, onReload: () => { reload(provider.provider); }, ...provider.provider !== 'auto' ? {} : { autoOn, onSetAutoOn: (enabled) => { void setAuto(enabled); } } }, provider.provider)))] }));
|
|
828
863
|
}
|
package/lib/client/UsageChip.js
CHANGED
|
@@ -207,12 +207,18 @@ export function mergeAccountWindows(readings) {
|
|
|
207
207
|
if (withPercent.length === 0)
|
|
208
208
|
continue;
|
|
209
209
|
const usedPercent = withPercent.reduce((sum, window) => sum + (window.usedPercent ?? 0), 0) / withPercent.length;
|
|
210
|
+
// A credit window carries the raw figures too; keep them so the compact line can read
|
|
211
|
+
// money as money (0/1500) rather than as a spent share (0%), which misreads an empty
|
|
212
|
+
// balance as nothing to meter.
|
|
213
|
+
const used = list.reduce((sum, window) => sum + (window.used ?? 0), 0);
|
|
214
|
+
const limit = list.reduce((sum, window) => sum + (window.limit ?? 0), 0);
|
|
210
215
|
// The pool's reset is the soonest account reset: the window refills when the first
|
|
211
216
|
// account's does.
|
|
212
217
|
const resets = list.map((window) => window.resetsAt).filter((at) => typeof at === 'number');
|
|
213
218
|
merged.push({
|
|
214
219
|
label,
|
|
215
220
|
usedPercent: Math.max(0, Math.min(100, usedPercent)),
|
|
221
|
+
...limit > 0 ? { used, limit } : {},
|
|
216
222
|
...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) },
|
|
217
223
|
});
|
|
218
224
|
}
|
package/lib/client/locales.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export declare const en: {
|
|
|
60
60
|
readonly modelsDrag: "Drag to re-order (or press ↑ / ↓ here)";
|
|
61
61
|
readonly modelsAutoChainOff: "Take out of Auto";
|
|
62
62
|
readonly modelsAutoChainEmpty: "No model is enabled, so Auto has nothing to try.";
|
|
63
|
+
readonly autoSwitchLabel: "Offer Auto in the model picker";
|
|
63
64
|
readonly modelsPinned: "pinned";
|
|
64
65
|
readonly usageTitle: "Plan and usage";
|
|
65
66
|
readonly usagePlan: "Plan: {plan}";
|
|
@@ -157,6 +158,7 @@ export declare const zh: {
|
|
|
157
158
|
modelsDrag: string;
|
|
158
159
|
modelsAutoChainOff: string;
|
|
159
160
|
modelsAutoChainEmpty: string;
|
|
161
|
+
autoSwitchLabel: string;
|
|
160
162
|
modelsPinned: string;
|
|
161
163
|
usageTitle: string;
|
|
162
164
|
usagePlan: string;
|
package/lib/client/locales.js
CHANGED
|
@@ -60,6 +60,7 @@ export const en = {
|
|
|
60
60
|
modelsDrag: 'Drag to re-order (or press ↑ / ↓ here)',
|
|
61
61
|
modelsAutoChainOff: 'Take out of Auto',
|
|
62
62
|
modelsAutoChainEmpty: 'No model is enabled, so Auto has nothing to try.',
|
|
63
|
+
autoSwitchLabel: 'Offer Auto in the model picker',
|
|
63
64
|
modelsPinned: 'pinned',
|
|
64
65
|
usageTitle: 'Plan and usage',
|
|
65
66
|
usagePlan: 'Plan: {plan}',
|
|
@@ -155,6 +156,7 @@ export const zh = {
|
|
|
155
156
|
modelsDrag: '拖动调整顺序(也可在此按 ↑ / ↓)',
|
|
156
157
|
modelsAutoChainOff: '从自动中移除',
|
|
157
158
|
modelsAutoChainEmpty: '没有启用任何模型,自动没有可尝试的模型。',
|
|
159
|
+
autoSwitchLabel: '在模型选择器中提供 Auto',
|
|
158
160
|
modelsPinned: '固定',
|
|
159
161
|
usageTitle: '套餐与额度',
|
|
160
162
|
usagePlan: '套餐:{plan}',
|
package/lib/client.js
CHANGED
|
@@ -91,6 +91,7 @@ const en = {
|
|
|
91
91
|
modelsDrag: "Drag to re-order (or press ↑ / ↓ here)",
|
|
92
92
|
modelsAutoChainOff: "Take out of Auto",
|
|
93
93
|
modelsAutoChainEmpty: "No model is enabled, so Auto has nothing to try.",
|
|
94
|
+
autoSwitchLabel: "Offer Auto in the model picker",
|
|
94
95
|
modelsPinned: "pinned",
|
|
95
96
|
usageTitle: "Plan and usage",
|
|
96
97
|
usagePlan: "Plan: {plan}",
|
|
@@ -186,6 +187,7 @@ const zh = {
|
|
|
186
187
|
modelsDrag: "拖动调整顺序(也可在此按 ↑ / ↓)",
|
|
187
188
|
modelsAutoChainOff: "从自动中移除",
|
|
188
189
|
modelsAutoChainEmpty: "没有启用任何模型,自动没有可尝试的模型。",
|
|
190
|
+
autoSwitchLabel: "在模型选择器中提供 Auto",
|
|
189
191
|
modelsPinned: "固定",
|
|
190
192
|
usageTitle: "套餐与额度",
|
|
191
193
|
usagePlan: "套餐:{plan}",
|
|
@@ -1189,6 +1191,12 @@ const styles = {
|
|
|
1189
1191
|
gap: 6,
|
|
1190
1192
|
marginTop: 4
|
|
1191
1193
|
},
|
|
1194
|
+
autoSwitchRow: {
|
|
1195
|
+
display: "flex",
|
|
1196
|
+
alignItems: "center",
|
|
1197
|
+
gap: 8,
|
|
1198
|
+
justifyContent: "space-between"
|
|
1199
|
+
},
|
|
1192
1200
|
modelRow: {
|
|
1193
1201
|
display: "flex",
|
|
1194
1202
|
alignItems: "flex-start",
|
|
@@ -1628,6 +1636,21 @@ function ProviderCard(props) {
|
|
|
1628
1636
|
className: "dsh-subscription-success",
|
|
1629
1637
|
children: message.text
|
|
1630
1638
|
}),
|
|
1639
|
+
status.provider === "auto" && props.onSetAutoOn !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1640
|
+
style: styles.autoSwitchRow,
|
|
1641
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1642
|
+
style: styles.modelName,
|
|
1643
|
+
children: t("autoSwitchLabel")
|
|
1644
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1645
|
+
type: "checkbox",
|
|
1646
|
+
className: "dsh-subscription-switch",
|
|
1647
|
+
checked: props.autoOn ?? true,
|
|
1648
|
+
disabled: busyAction,
|
|
1649
|
+
onChange: (event) => {
|
|
1650
|
+
props.onSetAutoOn?.(event.target.checked);
|
|
1651
|
+
}
|
|
1652
|
+
})]
|
|
1653
|
+
}),
|
|
1631
1654
|
accounts.map((account) => {
|
|
1632
1655
|
const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account);
|
|
1633
1656
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2044,8 +2067,20 @@ function SubscriptionsSection(props) {
|
|
|
2044
2067
|
const [error, setError] = (0, react.useState)(void 0);
|
|
2045
2068
|
const [messages, setMessages] = (0, react.useState)({});
|
|
2046
2069
|
const [busyAction, setBusyAction] = (0, react.useState)(false);
|
|
2070
|
+
const [autoOn, setAutoOn] = (0, react.useState)(true);
|
|
2047
2071
|
const mounted = (0, react.useRef)(true);
|
|
2048
2072
|
const timer = (0, react.useRef)(void 0);
|
|
2073
|
+
/** Read whether the `auto` provider is offered, so its card can render the switch. */
|
|
2074
|
+
(0, react.useEffect)(() => {
|
|
2075
|
+
if (call === void 0) return;
|
|
2076
|
+
let live = true;
|
|
2077
|
+
call("auto-state", void 0).then((result) => {
|
|
2078
|
+
if (live) setAutoOn(result.enabled);
|
|
2079
|
+
}).catch(() => {});
|
|
2080
|
+
return () => {
|
|
2081
|
+
live = false;
|
|
2082
|
+
};
|
|
2083
|
+
}, [call]);
|
|
2049
2084
|
/** Fetch the current state; a failure is shown rather than swallowed. */
|
|
2050
2085
|
const refresh = (0, react.useCallback)(async () => {
|
|
2051
2086
|
if (call === void 0) return;
|
|
@@ -2058,6 +2093,25 @@ function SubscriptionsSection(props) {
|
|
|
2058
2093
|
if (mounted.current) setError(t("actionFailed", { message: messageOf(caught) }));
|
|
2059
2094
|
}
|
|
2060
2095
|
}, [call, t]);
|
|
2096
|
+
/** Flip the `auto` provider on or off. */
|
|
2097
|
+
const setAuto = (0, react.useCallback)(async (enabled) => {
|
|
2098
|
+
if (call === void 0) return;
|
|
2099
|
+
setBusyAction(true);
|
|
2100
|
+
try {
|
|
2101
|
+
const result = await call("setAutoOn", { enabled });
|
|
2102
|
+
if (!mounted.current) return;
|
|
2103
|
+
setAutoOn(result.enabled);
|
|
2104
|
+
await refresh();
|
|
2105
|
+
} catch (caught) {
|
|
2106
|
+
if (mounted.current) setError(t("actionFailed", { message: messageOf(caught) }));
|
|
2107
|
+
} finally {
|
|
2108
|
+
if (mounted.current) setBusyAction(false);
|
|
2109
|
+
}
|
|
2110
|
+
}, [
|
|
2111
|
+
call,
|
|
2112
|
+
refresh,
|
|
2113
|
+
t
|
|
2114
|
+
]);
|
|
2061
2115
|
const stopPolling = (0, react.useCallback)(() => {
|
|
2062
2116
|
if (timer.current !== void 0) {
|
|
2063
2117
|
clearInterval(timer.current);
|
|
@@ -2371,6 +2425,12 @@ function SubscriptionsSection(props) {
|
|
|
2371
2425
|
},
|
|
2372
2426
|
onReload: () => {
|
|
2373
2427
|
reload(provider.provider);
|
|
2428
|
+
},
|
|
2429
|
+
...provider.provider !== "auto" ? {} : {
|
|
2430
|
+
autoOn,
|
|
2431
|
+
onSetAutoOn: (enabled) => {
|
|
2432
|
+
setAuto(enabled);
|
|
2433
|
+
}
|
|
2374
2434
|
}
|
|
2375
2435
|
}, provider.provider))
|
|
2376
2436
|
]
|
|
@@ -2539,10 +2599,16 @@ function mergeAccountWindows(readings) {
|
|
|
2539
2599
|
const withPercent = list.filter((window$1) => window$1.usedPercent !== void 0);
|
|
2540
2600
|
if (withPercent.length === 0) continue;
|
|
2541
2601
|
const usedPercent = withPercent.reduce((sum, window$1) => sum + (window$1.usedPercent ?? 0), 0) / withPercent.length;
|
|
2602
|
+
const used = list.reduce((sum, window$1) => sum + (window$1.used ?? 0), 0);
|
|
2603
|
+
const limit = list.reduce((sum, window$1) => sum + (window$1.limit ?? 0), 0);
|
|
2542
2604
|
const resets = list.map((window$1) => window$1.resetsAt).filter((at) => typeof at === "number");
|
|
2543
2605
|
merged.push({
|
|
2544
2606
|
label,
|
|
2545
2607
|
usedPercent: Math.max(0, Math.min(100, usedPercent)),
|
|
2608
|
+
...limit > 0 ? {
|
|
2609
|
+
used,
|
|
2610
|
+
limit
|
|
2611
|
+
} : {},
|
|
2546
2612
|
...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) }
|
|
2547
2613
|
});
|
|
2548
2614
|
}
|