dsh-subscriptions 0.2.0 → 0.2.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/README.md +220 -180
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.d.ts +8 -4
- package/lib/auth/rpc.js +89 -25
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +1 -1
- package/lib/client/OllamaCard.js +26 -8
- package/lib/client/OllamaSettingsManager.js +113 -30
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +137 -34
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +261 -49
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.d.ts +2 -0
- package/lib/client/SubscriptionsSection.js +265 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +62 -24
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +22 -0
- package/lib/client/locales.js +73 -51
- package/lib/client/model-filter.d.ts +37 -0
- package/lib/client/model-filter.js +45 -0
- package/lib/client/ollama-draft.js +21 -12
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.js +30 -12
- package/lib/client/usage-window-filter.d.ts +7 -0
- package/lib/client/usage-window-filter.js +12 -0
- package/lib/client.js +480 -136
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +167 -76
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +17 -0
- package/lib/provider-settings.js +79 -16
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +9 -2
- package/lib/providers/claude.js +153 -90
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.js +71 -39
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.js +14 -5
- package/lib/providers/external-usage.js +59 -23
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.js +24 -8
- package/lib/providers/ollama-web.js +29 -11
- package/lib/providers/ollama.js +129 -66
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +151 -52
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- package/package.json +27 -4
package/lib/client/locales.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export declare const en: {
|
|
|
29
29
|
modelsContextBounds: string;
|
|
30
30
|
modelsContextHint: string;
|
|
31
31
|
modelsContextInvalid: string;
|
|
32
|
+
modelsSubagentSpeed: string;
|
|
33
|
+
modelsSubagentSpeedHint: string;
|
|
32
34
|
modelsTools: string;
|
|
33
35
|
modelsToolsHint: string;
|
|
34
36
|
modelsImage: string;
|
|
@@ -37,6 +39,11 @@ export declare const en: {
|
|
|
37
39
|
modelsWebSearch: string;
|
|
38
40
|
modelsSave: string;
|
|
39
41
|
modelsUnavailable: string;
|
|
42
|
+
claudeCacheTtl: string;
|
|
43
|
+
claudeCacheTtlFollowConfig: string;
|
|
44
|
+
claudeCacheTtl5m: string;
|
|
45
|
+
claudeCacheTtl1h: string;
|
|
46
|
+
claudeCacheTtlHint: string;
|
|
40
47
|
nav: string;
|
|
41
48
|
intro: string;
|
|
42
49
|
unavailable: string;
|
|
@@ -73,6 +80,7 @@ export declare const en: {
|
|
|
73
80
|
usageRefresh: string;
|
|
74
81
|
usageLoading: string;
|
|
75
82
|
usageEmpty: string;
|
|
83
|
+
usageNoVisibleModels: string;
|
|
76
84
|
usageUnsupported: string;
|
|
77
85
|
usageError: string;
|
|
78
86
|
usageSession: string;
|
|
@@ -171,6 +179,9 @@ export declare const en: {
|
|
|
171
179
|
modelDefaultsSummaryEmpty: string;
|
|
172
180
|
modelDefaultsFilterPlaceholder: string;
|
|
173
181
|
modelDefaultsFilterEmpty: string;
|
|
182
|
+
modelDefaultsNoModels: string;
|
|
183
|
+
modelDefaultsNoAccount: string;
|
|
184
|
+
modelDefaultsDiscoveryFailed: string;
|
|
174
185
|
generating: string;
|
|
175
186
|
image: string;
|
|
176
187
|
viewImage: string;
|
|
@@ -264,6 +275,8 @@ export declare const zh: {
|
|
|
264
275
|
modelsContextBounds: string;
|
|
265
276
|
modelsContextHint: string;
|
|
266
277
|
modelsContextInvalid: string;
|
|
278
|
+
modelsSubagentSpeed: string;
|
|
279
|
+
modelsSubagentSpeedHint: string;
|
|
267
280
|
modelsTools: string;
|
|
268
281
|
modelsToolsHint: string;
|
|
269
282
|
modelsImage: string;
|
|
@@ -272,6 +285,11 @@ export declare const zh: {
|
|
|
272
285
|
modelsWebSearch: string;
|
|
273
286
|
modelsSave: string;
|
|
274
287
|
modelsUnavailable: string;
|
|
288
|
+
claudeCacheTtl: string;
|
|
289
|
+
claudeCacheTtlFollowConfig: string;
|
|
290
|
+
claudeCacheTtl5m: string;
|
|
291
|
+
claudeCacheTtl1h: string;
|
|
292
|
+
claudeCacheTtlHint: string;
|
|
275
293
|
nav: string;
|
|
276
294
|
intro: string;
|
|
277
295
|
unavailable: string;
|
|
@@ -308,6 +326,7 @@ export declare const zh: {
|
|
|
308
326
|
usageRefresh: string;
|
|
309
327
|
usageLoading: string;
|
|
310
328
|
usageEmpty: string;
|
|
329
|
+
usageNoVisibleModels: string;
|
|
311
330
|
usageUnsupported: string;
|
|
312
331
|
usageError: string;
|
|
313
332
|
usageSession: string;
|
|
@@ -406,6 +425,9 @@ export declare const zh: {
|
|
|
406
425
|
modelDefaultsSummaryEmpty: string;
|
|
407
426
|
modelDefaultsFilterPlaceholder: string;
|
|
408
427
|
modelDefaultsFilterEmpty: string;
|
|
428
|
+
modelDefaultsNoModels: string;
|
|
429
|
+
modelDefaultsNoAccount: string;
|
|
430
|
+
modelDefaultsDiscoveryFailed: string;
|
|
409
431
|
generating: string;
|
|
410
432
|
image: string;
|
|
411
433
|
viewImage: string;
|
package/lib/client/locales.js
CHANGED
|
@@ -20,23 +20,30 @@ export const en = {
|
|
|
20
20
|
accountsNoModels: 'No models reported for this account.',
|
|
21
21
|
accountsCatalogUnavailable: 'Model discovery is currently unavailable. Saved model selections are preserved.',
|
|
22
22
|
modelsPartialSave: 'Some reasoning defaults were saved. Remaining changes are still in the draft; retry to finish.',
|
|
23
|
-
modelsEdit:
|
|
24
|
-
modelsHint:
|
|
25
|
-
modelsAutomatic:
|
|
26
|
-
modelsSelectAll:
|
|
27
|
-
modelsSelectNone:
|
|
28
|
-
modelsContext:
|
|
29
|
-
modelsContextBounds:
|
|
30
|
-
modelsContextHint:
|
|
31
|
-
modelsContextInvalid:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
23
|
+
modelsEdit: 'Edit model list',
|
|
24
|
+
modelsHint: 'Configure model visibility, default reasoning effort, subagent speed, context and tools here; Save changes below submits them together with the account settings. Hidden models remain usable by existing sessions; tool changes apply to new sessions.',
|
|
25
|
+
modelsAutomatic: 'Automatically show all models, including new models',
|
|
26
|
+
modelsSelectAll: 'Select all',
|
|
27
|
+
modelsSelectNone: 'Clear selection',
|
|
28
|
+
modelsContext: 'Context (tokens)',
|
|
29
|
+
modelsContextBounds: 'Default {default} · Maximum {max}',
|
|
30
|
+
modelsContextHint: 'Leave blank to follow the provider. The requested window is capped by the account’s advertised maximum; account pools use the smallest member window. This controls local history and compaction, not the API’s capacity.',
|
|
31
|
+
modelsContextInvalid: 'Enter a positive whole number of tokens for {model}.',
|
|
32
|
+
modelsSubagentSpeed: 'Subagent default speed',
|
|
33
|
+
modelsSubagentSpeedHint: 'Applies to subsequent subagent requests using this model, regardless of the parent model. Main conversations keep their own Speed setting. Fast requires model support.',
|
|
34
|
+
modelsTools: 'Provider tools',
|
|
35
|
+
modelsToolsHint: 'Changes apply only to sessions created after saving. Existing sessions keep their tools. Shared tools remain available if any supporting provider is enabled.',
|
|
36
|
+
modelsImage: 'Image generation',
|
|
37
|
+
modelsVideo: 'Video generation',
|
|
38
|
+
modelsSearch: 'X search',
|
|
39
|
+
modelsWebSearch: 'Web search',
|
|
40
|
+
modelsSave: 'Save changes',
|
|
41
|
+
modelsUnavailable: 'Currently unavailable',
|
|
42
|
+
claudeCacheTtl: 'Prompt cache lifetime',
|
|
43
|
+
claudeCacheTtlFollowConfig: 'Follow config',
|
|
44
|
+
claudeCacheTtl5m: '5 minutes',
|
|
45
|
+
claudeCacheTtl1h: '1 hour',
|
|
46
|
+
claudeCacheTtlHint: 'Overrides the claudePromptCacheTtl config for this Claude route; Follow config uses the configured value. Saved changes apply from the next request. One hour keeps cached prefixes longer but writes them at a higher input price; compaction and session-title requests stay at five minutes.',
|
|
40
47
|
nav: 'Subscriptions',
|
|
41
48
|
intro: 'Log a subscription provider in or out. Login opens the provider’s authorization page in a new tab; headless setups can paste the callback URL or code instead.',
|
|
42
49
|
unavailable: 'Connection unavailable; subscription status cannot be loaded.',
|
|
@@ -73,6 +80,7 @@ export const en = {
|
|
|
73
80
|
usageRefresh: 'Refresh',
|
|
74
81
|
usageLoading: 'Loading usage…',
|
|
75
82
|
usageEmpty: 'No usage windows reported.',
|
|
83
|
+
usageNoVisibleModels: 'No usage windows for the models visible in the picker.',
|
|
76
84
|
usageUnsupported: 'This endpoint has no usage surface.',
|
|
77
85
|
usageError: 'Usage lookup failed: {message}',
|
|
78
86
|
usageSession: '5-hour window',
|
|
@@ -171,6 +179,9 @@ export const en = {
|
|
|
171
179
|
modelDefaultsSummaryEmpty: 'No model advertises reasoning levels',
|
|
172
180
|
modelDefaultsFilterPlaceholder: 'Filter models',
|
|
173
181
|
modelDefaultsFilterEmpty: 'No model matches “{query}”.',
|
|
182
|
+
modelDefaultsNoModels: 'This provider reports no models.',
|
|
183
|
+
modelDefaultsNoAccount: 'No connected accounts. Connect one on the provider card first.',
|
|
184
|
+
modelDefaultsDiscoveryFailed: 'Model discovery failed or timed out. Use Refresh to try again.',
|
|
174
185
|
generating: 'Generating image…',
|
|
175
186
|
image: 'image',
|
|
176
187
|
viewImage: 'View image',
|
|
@@ -206,21 +217,21 @@ export const en = {
|
|
|
206
217
|
ollamaWebOn: 'Web search/fetch enabled',
|
|
207
218
|
ollamaWebOff: 'Web search/fetch off',
|
|
208
219
|
ollamaManageTitle: 'Manage Ollama Cloud',
|
|
209
|
-
ollamaManageHint: 'Fetch the Cloud list, tick the models this route serves, then Save. The picker '
|
|
210
|
-
|
|
220
|
+
ollamaManageHint: 'Fetch the Cloud list, tick the models this route serves, then Save. The picker ' +
|
|
221
|
+
'shows the ticked models in order; the API key itself stays in DSH credentials.',
|
|
211
222
|
ollamaFetch: 'Fetch cloud models',
|
|
212
223
|
ollamaFetching: 'Fetching…',
|
|
213
224
|
ollamaKeyPlaceholder: 'One-time API key (optional)',
|
|
214
|
-
ollamaKeyHint: 'A key typed here is used for this fetch only and is never saved. Leave it '
|
|
215
|
-
|
|
225
|
+
ollamaKeyHint: 'A key typed here is used for this fetch only and is never saved. Leave it ' +
|
|
226
|
+
'blank to use the stored credential.',
|
|
216
227
|
ollamaFetchFailed: 'Could not fetch cloud models: {message}',
|
|
217
228
|
ollamaCandidatesCount: '{count} returned',
|
|
218
229
|
ollamaCandidatesEmpty: 'No cloud models returned.',
|
|
219
230
|
ollamaModelsEmpty: 'No models configured. Fetch the cloud list and tick the ones to keep.',
|
|
220
231
|
ollamaModelsLoadFailed: 'Could not load Ollama settings: {message}',
|
|
221
232
|
ollamaModelsSaveFailed: 'Could not save Ollama settings: {message}',
|
|
222
|
-
ollamaConfigureHint: 'No API key found at {ref}. Add it to the credentials file '
|
|
223
|
-
|
|
233
|
+
ollamaConfigureHint: 'No API key found at {ref}. Add it to the credentials file ' +
|
|
234
|
+
'(~/.dsh/.credentials.yaml).',
|
|
224
235
|
ollamaDisplayName: 'Display name',
|
|
225
236
|
ollamaRowDetails: 'Details',
|
|
226
237
|
ollamaEffortsUnknown: 'This model reasons; its level set will come from live discovery.',
|
|
@@ -228,16 +239,16 @@ export const en = {
|
|
|
228
239
|
ollamaMoveDown: 'Move down',
|
|
229
240
|
ollamaBaseURL: 'Base URL',
|
|
230
241
|
ollamaApiKeyRef: 'API key reference',
|
|
231
|
-
ollamaConnectionHint: "The base URL receives this route's requests and the credential lookup; the "
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
ollamaWebHint: 'Enables the Ollama Cloud web search and fetch providers. The host still '
|
|
235
|
-
|
|
236
|
-
ollamaCatalogOverride: "A profile pins this route's model list; the picker ignores store models "
|
|
237
|
-
|
|
242
|
+
ollamaConnectionHint: "The base URL receives this route's requests and the credential lookup; the " +
|
|
243
|
+
'reference names the DSH credential, not the key itself. Requests send ' +
|
|
244
|
+
'the resolved key to that address, so point it only at a host you trust.',
|
|
245
|
+
ollamaWebHint: 'Enables the Ollama Cloud web search and fetch providers. The host still ' +
|
|
246
|
+
'has to select them before the web tools use them.',
|
|
247
|
+
ollamaCatalogOverride: "A profile pins this route's model list; the picker ignores store models " +
|
|
248
|
+
'until that override is removed.',
|
|
238
249
|
ollamaResetDefaults: 'Restore deployment defaults',
|
|
239
|
-
ollamaResetConfirm: 'Clear every saved Ollama override? The profile values and built-in '
|
|
240
|
-
|
|
250
|
+
ollamaResetConfirm: 'Clear every saved Ollama override? The profile values and built-in ' +
|
|
251
|
+
'defaults apply again.',
|
|
241
252
|
ollamaCapVision: 'vision',
|
|
242
253
|
ollamaCapThinking: 'thinking',
|
|
243
254
|
ollamaContext: 'context {tokens}',
|
|
@@ -263,23 +274,30 @@ export const zh = {
|
|
|
263
274
|
accountsNoModels: '此账号暂未返回模型。',
|
|
264
275
|
accountsCatalogUnavailable: '暂时无法获取模型目录,已保存的模型选择会被保留。',
|
|
265
276
|
modelsPartialSave: '部分推理档已保存,其余更改仍保留在草稿中,请重试完成保存。',
|
|
266
|
-
modelsEdit:
|
|
267
|
-
modelsHint:
|
|
268
|
-
modelsAutomatic:
|
|
269
|
-
modelsSelectAll:
|
|
270
|
-
modelsSelectNone:
|
|
271
|
-
modelsContext:
|
|
272
|
-
modelsContextBounds:
|
|
273
|
-
modelsContextHint:
|
|
274
|
-
modelsContextInvalid:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
modelsEdit: '编辑模型列表',
|
|
278
|
+
modelsHint: '在此配置模型显示、默认推理档、子代理速度、上下文与工具,底部的「保存更改」会连同账号设置一起提交。隐藏模型不影响已有会话;工具开关对新建会话生效。',
|
|
279
|
+
modelsAutomatic: '自动显示全部模型(包含以后新增的模型)',
|
|
280
|
+
modelsSelectAll: '全选',
|
|
281
|
+
modelsSelectNone: '清空选择',
|
|
282
|
+
modelsContext: '上下文(token)',
|
|
283
|
+
modelsContextBounds: '默认 {default} · 最大 {max}',
|
|
284
|
+
modelsContextHint: '留空跟随服务商。配置值按账号返回的最大窗口限制;账号池取各成员的最小窗口。此设置控制本地历史保留和压缩时机,不会扩大 API 本身的容量。',
|
|
285
|
+
modelsContextInvalid: '请为 {model} 输入正整数 token 数。',
|
|
286
|
+
modelsSubagentSpeed: '子代理默认速度',
|
|
287
|
+
modelsSubagentSpeedHint: '对使用此模型的子代理后续请求生效,与父会话模型无关。主会话仍使用自己的速度设置;快速档仅在模型支持时生效。',
|
|
288
|
+
modelsTools: 'Provider 工具',
|
|
289
|
+
modelsToolsHint: '仅对保存后新建的会话生效,已有会话保留原工具。任一支持的服务商启用共享工具时,该工具仍会显示。',
|
|
290
|
+
modelsImage: '图片生成',
|
|
291
|
+
modelsVideo: '视频生成',
|
|
292
|
+
modelsSearch: 'X 搜索',
|
|
293
|
+
modelsWebSearch: '网页搜索',
|
|
294
|
+
modelsSave: '保存更改',
|
|
295
|
+
modelsUnavailable: '当前不可用',
|
|
296
|
+
claudeCacheTtl: '提示词缓存时长',
|
|
297
|
+
claudeCacheTtlFollowConfig: '跟随配置',
|
|
298
|
+
claudeCacheTtl5m: '5 分钟',
|
|
299
|
+
claudeCacheTtl1h: '1 小时',
|
|
300
|
+
claudeCacheTtlHint: '覆盖此 Claude 路由的 claudePromptCacheTtl 配置;选择「跟随配置」时使用配置值。保存后自下一次请求生效。1 小时可更久保留缓存前缀,但写入按更高的输入价格计费;压缩与会话标题请求仍为 5 分钟。',
|
|
283
301
|
nav: '订阅',
|
|
284
302
|
intro: '在此登录或退出订阅服务商。点击登录会在新标签页打开服务商的授权页面;无浏览器环境可改为粘贴回调 URL 或授权码。',
|
|
285
303
|
unavailable: '连接不可用,无法加载订阅状态。',
|
|
@@ -316,6 +334,7 @@ export const zh = {
|
|
|
316
334
|
usageRefresh: '刷新',
|
|
317
335
|
usageLoading: '用量加载中…',
|
|
318
336
|
usageEmpty: '服务商未返回任何用量窗口。',
|
|
337
|
+
usageNoVisibleModels: '选择器中的可见模型没有任何用量窗口。',
|
|
319
338
|
usageUnsupported: '该端点不提供用量查询。',
|
|
320
339
|
usageError: '用量查询失败:{message}',
|
|
321
340
|
usageSession: '5 小时窗口',
|
|
@@ -414,6 +433,9 @@ export const zh = {
|
|
|
414
433
|
modelDefaultsSummaryEmpty: '没有模型声明推理档',
|
|
415
434
|
modelDefaultsFilterPlaceholder: '筛选模型',
|
|
416
435
|
modelDefaultsFilterEmpty: '没有匹配「{query}」的模型。',
|
|
436
|
+
modelDefaultsNoModels: '这个服务商没有报告任何模型。',
|
|
437
|
+
modelDefaultsNoAccount: '还没有连接账号。请先在卡片上连接一个账号。',
|
|
438
|
+
modelDefaultsDiscoveryFailed: '模型列表发现失败或超时。可以点「刷新」重试。',
|
|
417
439
|
generating: '正在生成图片…',
|
|
418
440
|
image: '图片',
|
|
419
441
|
viewImage: '查看图片',
|
|
@@ -468,8 +490,8 @@ export const zh = {
|
|
|
468
490
|
ollamaMoveDown: '下移',
|
|
469
491
|
ollamaBaseURL: '基础 URL',
|
|
470
492
|
ollamaApiKeyRef: 'API 密钥引用',
|
|
471
|
-
ollamaConnectionHint: '基础 URL 用于此路由的请求与凭据查询;引用是 DSH 凭据的名称,不是密钥本身。'
|
|
472
|
-
|
|
493
|
+
ollamaConnectionHint: '基础 URL 用于此路由的请求与凭据查询;引用是 DSH 凭据的名称,不是密钥本身。' +
|
|
494
|
+
'请求会把解析出的密钥发往该地址,请只填写可信主机。',
|
|
473
495
|
ollamaWebHint: '启用 Ollama Cloud 的网页搜索与抓取能力。宿主仍需选择它们,网页工具才会使用。',
|
|
474
496
|
ollamaCatalogOverride: '配置档案固定了此路由的模型列表;在移除该覆盖前,选择器会忽略存储中的模型。',
|
|
475
497
|
ollamaResetDefaults: '恢复部署默认值',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SubagentSpeedTier } from '../provider-settings.js';
|
|
2
|
+
import type { AccountCatalogRow } from './account-preferences.js';
|
|
3
|
+
/** What the model editor shows for the current filter state. */
|
|
4
|
+
export type ModelListState = 'provider-empty' | 'filter-empty' | 'rows';
|
|
5
|
+
/** Distinguish an empty provider from a query that filtered out every row. */
|
|
6
|
+
export declare function modelListState(total: number, matched: number): ModelListState;
|
|
7
|
+
/**
|
|
8
|
+
* Retain saved model ids when discovery returns no rows. A temporary catalog
|
|
9
|
+
* failure must not hide the visibility or subagent-speed settings they edit.
|
|
10
|
+
*/
|
|
11
|
+
export declare function savedModelIds(visibleModels: readonly string[] | undefined, subagentSpeeds: Readonly<Record<string, SubagentSpeedTier>> | undefined): string[];
|
|
12
|
+
/** Which notice replaces inert model controls. */
|
|
13
|
+
export type ModelEditorNotice = 'noAccount' | 'discoveryFailed' | 'noModels';
|
|
14
|
+
/**
|
|
15
|
+
* Use account discovery evidence to explain an empty provider. Connected
|
|
16
|
+
* accounts with failed discovery need a retry, not another login prompt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function modelEditorNotice(accounts: readonly AccountCatalogRow[] | undefined): ModelEditorNotice;
|
|
19
|
+
/** Everything the component needs to choose rows or one explanatory notice. */
|
|
20
|
+
export interface ModelEditorState {
|
|
21
|
+
listState: ModelListState;
|
|
22
|
+
/** Rendered only when `listState` is `provider-empty`. */
|
|
23
|
+
noticeKey: (typeof NOTICE_KEYS)[ModelEditorNotice];
|
|
24
|
+
}
|
|
25
|
+
/** Locale keys for the three empty states. */
|
|
26
|
+
export declare const NOTICE_KEYS: {
|
|
27
|
+
readonly noAccount: "modelDefaultsNoAccount";
|
|
28
|
+
readonly discoveryFailed: "modelDefaultsDiscoveryFailed";
|
|
29
|
+
readonly noModels: "modelDefaultsNoModels";
|
|
30
|
+
};
|
|
31
|
+
/** Combine discovered and saved rows, filter results, and discovery evidence. */
|
|
32
|
+
export declare function modelEditorState(input: {
|
|
33
|
+
discovered: number;
|
|
34
|
+
saved: number;
|
|
35
|
+
matched: number;
|
|
36
|
+
accounts: readonly AccountCatalogRow[] | undefined;
|
|
37
|
+
}): ModelEditorState;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Distinguish an empty provider from a query that filtered out every row. */
|
|
2
|
+
export function modelListState(total, matched) {
|
|
3
|
+
if (total === 0)
|
|
4
|
+
return 'provider-empty';
|
|
5
|
+
return matched === 0 ? 'filter-empty' : 'rows';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Retain saved model ids when discovery returns no rows. A temporary catalog
|
|
9
|
+
* failure must not hide the visibility or subagent-speed settings they edit.
|
|
10
|
+
*/
|
|
11
|
+
export function savedModelIds(visibleModels, subagentSpeeds) {
|
|
12
|
+
return [
|
|
13
|
+
...new Set([
|
|
14
|
+
...(visibleModels ?? []),
|
|
15
|
+
...Object.keys(subagentSpeeds ?? {}),
|
|
16
|
+
]),
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Use account discovery evidence to explain an empty provider. Connected
|
|
21
|
+
* accounts with failed discovery need a retry, not another login prompt.
|
|
22
|
+
*/
|
|
23
|
+
export function modelEditorNotice(accounts) {
|
|
24
|
+
// Account-neutral editors such as Cursor use the neutral empty-catalog copy.
|
|
25
|
+
if (accounts === undefined)
|
|
26
|
+
return 'noModels';
|
|
27
|
+
if (accounts.length === 0)
|
|
28
|
+
return 'noAccount';
|
|
29
|
+
return accounts.every((account) => account.unavailable === true)
|
|
30
|
+
? 'discoveryFailed'
|
|
31
|
+
: 'noModels';
|
|
32
|
+
}
|
|
33
|
+
/** Locale keys for the three empty states. */
|
|
34
|
+
export const NOTICE_KEYS = {
|
|
35
|
+
noAccount: 'modelDefaultsNoAccount',
|
|
36
|
+
discoveryFailed: 'modelDefaultsDiscoveryFailed',
|
|
37
|
+
noModels: 'modelDefaultsNoModels',
|
|
38
|
+
};
|
|
39
|
+
/** Combine discovered and saved rows, filter results, and discovery evidence. */
|
|
40
|
+
export function modelEditorState(input) {
|
|
41
|
+
return {
|
|
42
|
+
listState: modelListState(input.discovered + input.saved, input.matched),
|
|
43
|
+
noticeKey: NOTICE_KEYS[modelEditorNotice(input.accounts)],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
export function adoptCandidates(current, candidates, selectedIds) {
|
|
19
19
|
const models = [...(current ?? [])];
|
|
20
|
-
const present = new Set(models.map(model => model.id));
|
|
20
|
+
const present = new Set(models.map((model) => model.id));
|
|
21
21
|
const selected = new Set(selectedIds);
|
|
22
22
|
for (const candidate of candidates) {
|
|
23
23
|
if (!selected.has(candidate.id) || present.has(candidate.id))
|
|
@@ -26,7 +26,9 @@ export function adoptCandidates(current, candidates, selectedIds) {
|
|
|
26
26
|
models.push({
|
|
27
27
|
id: candidate.id,
|
|
28
28
|
name: candidate.name,
|
|
29
|
-
...(candidate.contextWindow === undefined
|
|
29
|
+
...(candidate.contextWindow === undefined
|
|
30
|
+
? {}
|
|
31
|
+
: { contextWindow: candidate.contextWindow }),
|
|
30
32
|
...(candidate.vision ? { vision: true } : {}),
|
|
31
33
|
...(candidate.thinking ? { thinking: true } : {}),
|
|
32
34
|
// The vendor's default is not copied: defaultEffort in the store is
|
|
@@ -48,7 +50,7 @@ export function adoptCandidates(current, candidates, selectedIds) {
|
|
|
48
50
|
*/
|
|
49
51
|
export function moveModel(models, id, delta) {
|
|
50
52
|
const next = [...models];
|
|
51
|
-
const index = next.findIndex(model => model.id === id);
|
|
53
|
+
const index = next.findIndex((model) => model.id === id);
|
|
52
54
|
const target = index + delta;
|
|
53
55
|
if (index === -1 || target < 0 || target >= next.length)
|
|
54
56
|
return next;
|
|
@@ -64,7 +66,7 @@ export function moveModel(models, id, delta) {
|
|
|
64
66
|
* @returns a new list; the input array is not mutated.
|
|
65
67
|
*/
|
|
66
68
|
export function removeModel(models, id) {
|
|
67
|
-
return models.filter(model => model.id !== id);
|
|
69
|
+
return models.filter((model) => model.id !== id);
|
|
68
70
|
}
|
|
69
71
|
/**
|
|
70
72
|
* Short context-window label for a model row.
|
|
@@ -91,7 +93,7 @@ export function contextLabel(contextWindow) {
|
|
|
91
93
|
* @returns a new list; the input array is not mutated.
|
|
92
94
|
*/
|
|
93
95
|
export function renameModel(models, id, name) {
|
|
94
|
-
return models.map(model => {
|
|
96
|
+
return models.map((model) => {
|
|
95
97
|
if (model.id !== id)
|
|
96
98
|
return model;
|
|
97
99
|
const next = { ...model };
|
|
@@ -112,7 +114,7 @@ export function renameModel(models, id, name) {
|
|
|
112
114
|
* @returns a new list; the input array is not mutated.
|
|
113
115
|
*/
|
|
114
116
|
export function setModelEffort(models, id, effort) {
|
|
115
|
-
return models.map(model => {
|
|
117
|
+
return models.map((model) => {
|
|
116
118
|
if (model.id !== id)
|
|
117
119
|
return model;
|
|
118
120
|
if (effort === '') {
|
|
@@ -133,7 +135,7 @@ export function setModelEffort(models, id, effort) {
|
|
|
133
135
|
* @returns a new list; the input array is not mutated.
|
|
134
136
|
*/
|
|
135
137
|
export function setModelContext(models, id, tokens) {
|
|
136
|
-
return models.map(model => {
|
|
138
|
+
return models.map((model) => {
|
|
137
139
|
if (model.id !== id)
|
|
138
140
|
return model;
|
|
139
141
|
const next = { ...model };
|
|
@@ -169,7 +171,7 @@ export function parseContextInput(text) {
|
|
|
169
171
|
* @returns the new list and stash; neither input is mutated.
|
|
170
172
|
*/
|
|
171
173
|
export function untickModel(models, stash, id) {
|
|
172
|
-
const row = models.find(model => model.id === id);
|
|
174
|
+
const row = models.find((model) => model.id === id);
|
|
173
175
|
if (row === undefined)
|
|
174
176
|
return { models: [...models], stash };
|
|
175
177
|
return { models: removeModel(models, id), stash: new Map(stash).set(id, row) };
|
|
@@ -186,7 +188,7 @@ export function untickModel(models, stash, id) {
|
|
|
186
188
|
* @returns a new list and the stash without the restored row.
|
|
187
189
|
*/
|
|
188
190
|
export function tickModel(models, stash, candidates, id) {
|
|
189
|
-
if (models.some(model => model.id === id))
|
|
191
|
+
if (models.some((model) => model.id === id))
|
|
190
192
|
return { models: [...models], stash };
|
|
191
193
|
const stashed = stash.get(id);
|
|
192
194
|
if (stashed === undefined)
|
|
@@ -237,10 +239,17 @@ export function untickModels(models, stash, ids) {
|
|
|
237
239
|
* @returns the options in picker order.
|
|
238
240
|
*/
|
|
239
241
|
export function effortOptions(row, fetched, labels) {
|
|
240
|
-
const ids = row.efforts ?? fetched ?? labels?.map(entry => entry.id) ?? [];
|
|
241
|
-
const options = ids.map(id => ({
|
|
242
|
+
const ids = row.efforts ?? fetched ?? labels?.map((entry) => entry.id) ?? [];
|
|
243
|
+
const options = ids.map((id) => ({
|
|
244
|
+
id,
|
|
245
|
+
name: labels?.find((entry) => entry.id === id)?.name ?? id,
|
|
246
|
+
}));
|
|
242
247
|
if (row.defaultEffort !== undefined && !ids.includes(row.defaultEffort)) {
|
|
243
|
-
options.push({
|
|
248
|
+
options.push({
|
|
249
|
+
id: row.defaultEffort,
|
|
250
|
+
name: row.defaultEffort,
|
|
251
|
+
unavailable: true,
|
|
252
|
+
});
|
|
244
253
|
}
|
|
245
254
|
return options;
|
|
246
255
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/** Explain client-side gating; the server independently revalidates before spending. */
|
|
2
2
|
export function resetActionBlock(usage, now = Date.now()) {
|
|
3
|
-
const weekly = usage.windows?.find(w => w.kind === 'weekly' && w.scope === undefined);
|
|
3
|
+
const weekly = usage.windows?.find((w) => w.kind === 'weekly' && w.scope === undefined);
|
|
4
4
|
if (!weekly || !Number.isFinite(weekly.usedPercent))
|
|
5
5
|
return 'resetUseRefresh';
|
|
6
6
|
if (weekly.usedPercent < 80)
|
|
7
7
|
return 'resetUseGuard';
|
|
8
|
-
if (weekly.resetsAt === undefined ||
|
|
8
|
+
if (weekly.resetsAt === undefined ||
|
|
9
|
+
weekly.resetsAt <= now ||
|
|
10
|
+
usage.resetCreditsError)
|
|
9
11
|
return 'resetUseRefresh';
|
|
10
|
-
if (!usage.resetCredits?.some(c => c.id))
|
|
12
|
+
if (!usage.resetCredits?.some((c) => c.id))
|
|
11
13
|
return 'resetUseRestart';
|
|
12
|
-
if (!usage.resetCredits.some(c => c.id && (c.expiresAt === undefined || c.expiresAt > now)))
|
|
14
|
+
if (!usage.resetCredits.some((c) => c.id && (c.expiresAt === undefined || c.expiresAt > now)))
|
|
13
15
|
return 'resetUseRefresh';
|
|
14
16
|
return undefined;
|
|
15
17
|
}
|
|
@@ -29,7 +29,7 @@ export declare function resetCreditRowName(t: Translate, credit: ResetCreditView
|
|
|
29
29
|
/** Claude omits an empty grant list; Codex still shows a defined, possibly empty, list. */
|
|
30
30
|
export declare function showsResetCredits(provider: 'codex' | 'claude', credits: readonly ResetCreditView[] | undefined): boolean;
|
|
31
31
|
/** The error line the settings card mounts. Claude never interpolates provider text. */
|
|
32
|
-
export declare function ResetCreditsErrorLine({ provider, message, t }: {
|
|
32
|
+
export declare function ResetCreditsErrorLine({ provider, message, t, }: {
|
|
33
33
|
provider: 'codex' | 'claude';
|
|
34
34
|
message: string;
|
|
35
35
|
t: Translate;
|
|
@@ -19,7 +19,8 @@ export function resetCreditState(credit, now) {
|
|
|
19
19
|
return 'paused';
|
|
20
20
|
if (credit.resetsLeft === 0)
|
|
21
21
|
return 'exhausted';
|
|
22
|
-
if ((credit.cooldownUntil !== undefined && credit.cooldownUntil > now) ||
|
|
22
|
+
if ((credit.cooldownUntil !== undefined && credit.cooldownUntil > now) ||
|
|
23
|
+
credit.usableNow === false)
|
|
23
24
|
return 'notUsable';
|
|
24
25
|
return 'available';
|
|
25
26
|
}
|
|
@@ -37,7 +38,9 @@ export function resetCreditsAvailableCount(credits, now, mode) {
|
|
|
37
38
|
const state = resetCreditState(credit, now);
|
|
38
39
|
if (state === 'expired' || state === 'paused')
|
|
39
40
|
continue;
|
|
40
|
-
if (typeof credit.resetsLeft === 'number' &&
|
|
41
|
+
if (typeof credit.resetsLeft === 'number' &&
|
|
42
|
+
Number.isFinite(credit.resetsLeft) &&
|
|
43
|
+
credit.resetsLeft > 0) {
|
|
41
44
|
sum += credit.resetsLeft;
|
|
42
45
|
}
|
|
43
46
|
}
|
|
@@ -46,7 +49,9 @@ export function resetCreditsAvailableCount(credits, now, mode) {
|
|
|
46
49
|
function expiryText(t, credit) {
|
|
47
50
|
return credit.expiresAt === undefined
|
|
48
51
|
? t('resetCreditExpiryUnknown')
|
|
49
|
-
: t('resetCreditExpires', {
|
|
52
|
+
: t('resetCreditExpires', {
|
|
53
|
+
date: new Date(credit.expiresAt).toLocaleString(),
|
|
54
|
+
});
|
|
50
55
|
}
|
|
51
56
|
function remainingText(t, credit) {
|
|
52
57
|
const left = credit.resetsLeft;
|
|
@@ -62,9 +67,18 @@ function remainingText(t, credit) {
|
|
|
62
67
|
/** Accessible name of one Claude grant row: state, remaining, and expiry, not a control name. */
|
|
63
68
|
export function resetCreditRowName(t, credit, now) {
|
|
64
69
|
const state = resetCreditState(credit, now);
|
|
65
|
-
const parts = [
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
const parts = [
|
|
71
|
+
t('resetCreditFull'),
|
|
72
|
+
t(STATE_KEYS[state]),
|
|
73
|
+
remainingText(t, credit),
|
|
74
|
+
expiryText(t, credit),
|
|
75
|
+
];
|
|
76
|
+
if (state === 'notUsable' &&
|
|
77
|
+
credit.cooldownUntil !== undefined &&
|
|
78
|
+
credit.cooldownUntil > now) {
|
|
79
|
+
parts.push(t('resetCreditCooldownUntil', {
|
|
80
|
+
date: new Date(credit.cooldownUntil).toLocaleString(),
|
|
81
|
+
}));
|
|
68
82
|
}
|
|
69
83
|
return parts.join(', ');
|
|
70
84
|
}
|
|
@@ -75,15 +89,21 @@ export function showsResetCredits(provider, credits) {
|
|
|
75
89
|
return (credits?.length ?? 0) > 0;
|
|
76
90
|
}
|
|
77
91
|
/** The error line the settings card mounts. Claude never interpolates provider text. */
|
|
78
|
-
export function ResetCreditsErrorLine({ provider, message, t }) {
|
|
79
|
-
return _jsx("p", { style: cardStyles.error, children: resetCreditsErrorText(provider, message, t) });
|
|
92
|
+
export function ResetCreditsErrorLine({ provider, message, t, }) {
|
|
93
|
+
return (_jsx("p", { style: cardStyles.error, children: resetCreditsErrorText(provider, message, t) }));
|
|
80
94
|
}
|
|
81
95
|
/** Fixed sentence for Claude. Codex may include the lookup message; Claude must not. */
|
|
82
96
|
export function resetCreditsErrorText(provider, message, t) {
|
|
83
|
-
return provider === 'claude'
|
|
97
|
+
return provider === 'claude'
|
|
98
|
+
? t('resetCreditsErrorFixed')
|
|
99
|
+
: t('resetCreditsError', { message });
|
|
84
100
|
}
|
|
85
101
|
const summaryStyle = {
|
|
86
|
-
...cardStyles.usageMeta,
|
|
102
|
+
...cardStyles.usageMeta,
|
|
103
|
+
cursor: 'pointer',
|
|
104
|
+
listStyle: 'none',
|
|
105
|
+
justifyContent: 'flex-start',
|
|
106
|
+
gap: 6,
|
|
87
107
|
};
|
|
88
108
|
/**
|
|
89
109
|
* The closed disclosure both providers share. Claude rows are plain text:
|
|
@@ -92,12 +112,41 @@ const summaryStyle = {
|
|
|
92
112
|
*/
|
|
93
113
|
export function ResetCreditsDisclosure({ mode, credits, t, now = Date.now(), nextCredit, useAction, statusMessage, }) {
|
|
94
114
|
const count = resetCreditsAvailableCount(credits, now, mode);
|
|
95
|
-
return (_jsxs("details", { className: "subscriptions-reset-credits", style: cardStyles.usageRow, children: [_jsxs("summary", { style: summaryStyle, children: [_jsx("svg", { "aria-hidden": "true", width: "12", height: "12", viewBox: "0 0 16 16", style: { flexShrink: 0 }, children: _jsx("path", { d: "m6 3 5 5-5 5", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }), _jsx("span", { children: t('resetCreditsTitle') }), mode === 'grants' && _jsx("span", { children: t('resetCreditReadOnlyLabel') }), _jsx("span", { style: { marginLeft: 'auto' }, children: t('resetCreditsAvailable', { count }) })] }), _jsx("style", { children: '.subscriptions-reset-credits > summary::-webkit-details-marker { display: none; }'
|
|
96
|
-
|
|
115
|
+
return (_jsxs("details", { className: "subscriptions-reset-credits", style: cardStyles.usageRow, children: [_jsxs("summary", { style: summaryStyle, children: [_jsx("svg", { "aria-hidden": "true", width: "12", height: "12", viewBox: "0 0 16 16", style: { flexShrink: 0 }, children: _jsx("path", { d: "m6 3 5 5-5 5", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }), _jsx("span", { children: t('resetCreditsTitle') }), mode === 'grants' && _jsx("span", { children: t('resetCreditReadOnlyLabel') }), _jsx("span", { style: { marginLeft: 'auto' }, children: t('resetCreditsAvailable', { count }) })] }), _jsx("style", { children: '.subscriptions-reset-credits > summary::-webkit-details-marker { display: none; }' +
|
|
116
|
+
'.subscriptions-reset-credits[open] > summary > svg { transform: rotate(90deg); }' }), _jsxs("div", { style: { paddingLeft: 18, marginTop: 6 }, children: [mode === 'grants' && (_jsx("p", { style: cardStyles.status, children: t('resetCreditReadOnly') })), credits.map((credit, index) => {
|
|
97
117
|
const state = resetCreditState(credit, now);
|
|
98
|
-
const showUse = mode === 'credits' &&
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
const showUse = mode === 'credits' &&
|
|
119
|
+
useAction !== undefined &&
|
|
120
|
+
credit === nextCredit;
|
|
121
|
+
return (_jsxs("div", { "data-reset-state": mode === 'grants' ? state : undefined, "data-reset-claimable": mode === 'grants' && credit.claimable === true
|
|
122
|
+
? 'true'
|
|
123
|
+
: undefined, role: mode === 'grants' ? 'group' : undefined, "aria-label": mode === 'grants'
|
|
124
|
+
? resetCreditRowName(t, credit, now)
|
|
125
|
+
: undefined, style: {
|
|
126
|
+
...cardStyles.usageMeta,
|
|
127
|
+
minHeight: 28,
|
|
128
|
+
gap: 12,
|
|
129
|
+
cursor: mode === 'grants' ? 'default' : undefined,
|
|
130
|
+
}, children: [_jsxs("span", { style: {
|
|
131
|
+
display: 'inline-flex',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
gap: 8,
|
|
134
|
+
flexShrink: 0,
|
|
135
|
+
}, children: [t('resetCreditFull'), mode === 'grants' && (_jsx("span", { "aria-hidden": "true", "data-reset-state-word": state, children: t(STATE_KEYS[state]) })), showUse && (_jsx("button", { type: "button", "aria-disabled": useAction.disabled, title: useAction.title, style: {
|
|
136
|
+
minHeight: 22,
|
|
137
|
+
height: 22,
|
|
138
|
+
padding: '0 7px',
|
|
139
|
+
borderRadius: 6,
|
|
140
|
+
opacity: useAction.disabled ? 0.35 : 1,
|
|
141
|
+
cursor: useAction.disabled ? 'not-allowed' : 'pointer',
|
|
142
|
+
}, onClick: () => {
|
|
143
|
+
if (!useAction.disabled)
|
|
144
|
+
useAction.onUse();
|
|
145
|
+
}, children: useAction.busy ? t('resetUseChecking') : useAction.label }))] }), _jsxs("span", { style: { textAlign: 'right' }, children: [mode === 'grants' && (_jsxs("span", { "aria-hidden": "true", children: [remainingText(t, credit), " \u00B7 "] })), mode === 'grants' &&
|
|
146
|
+
state === 'notUsable' &&
|
|
147
|
+
credit.cooldownUntil !== undefined &&
|
|
148
|
+
credit.cooldownUntil > now && (_jsxs("span", { "aria-hidden": "true", children: [t('resetCreditCooldownUntil', {
|
|
149
|
+
date: new Date(credit.cooldownUntil).toLocaleString(),
|
|
150
|
+
}), ' ', "\u00B7", ' '] })), _jsx("span", { "aria-hidden": mode === 'grants' ? true : undefined, children: expiryText(t, credit) })] })] }, credit.id ?? index));
|
|
151
|
+
}), statusMessage !== undefined && statusMessage !== '' && (_jsx("p", { role: "status", style: cardStyles.status, children: statusMessage }))] })] }));
|
|
103
152
|
}
|