dsh-subscriptions 0.1.5 → 0.2.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.
Files changed (110) hide show
  1. package/README.md +212 -154
  2. package/lib/auth/claude-code-creds.js +31 -9
  3. package/lib/auth/device-flow.js +16 -9
  4. package/lib/auth/oauth-flow.js +13 -10
  5. package/lib/auth/rpc.d.ts +73 -2
  6. package/lib/auth/rpc.js +124 -23
  7. package/lib/auth/store.js +80 -31
  8. package/lib/client/CursorCard.d.ts +1 -1
  9. package/lib/client/CursorCard.js +50 -16
  10. package/lib/client/ExternalUsageCards.d.ts +1 -1
  11. package/lib/client/ExternalUsageCards.js +37 -16
  12. package/lib/client/ImageGallery.d.ts +2 -2
  13. package/lib/client/ImageGallery.js +92 -33
  14. package/lib/client/ImageGenerateToolview.js +50 -14
  15. package/lib/client/OllamaCard.d.ts +13 -0
  16. package/lib/client/OllamaCard.js +94 -0
  17. package/lib/client/OllamaSettingsManager.d.ts +24 -0
  18. package/lib/client/OllamaSettingsManager.js +329 -0
  19. package/lib/client/ProviderAccountManager.d.ts +1 -1
  20. package/lib/client/ProviderAccountManager.js +164 -49
  21. package/lib/client/ProviderModelEditor.js +109 -26
  22. package/lib/client/SpeedSelect.d.ts +62 -14
  23. package/lib/client/SpeedSelect.js +257 -46
  24. package/lib/client/SubscriptionUsageBadge.d.ts +5 -11
  25. package/lib/client/SubscriptionUsageBadge.js +301 -112
  26. package/lib/client/SubscriptionsSection.d.ts +2 -0
  27. package/lib/client/SubscriptionsSection.js +259 -85
  28. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  29. package/lib/client/UsageMeter.d.ts +1 -1
  30. package/lib/client/UsageMeter.js +69 -27
  31. package/lib/client/VideoGenerateToolview.js +69 -17
  32. package/lib/client/account-preferences.d.ts +8 -0
  33. package/lib/client/account-preferences.js +20 -5
  34. package/lib/client/host-contract.d.ts +11 -2
  35. package/lib/client/host-contract.js +20 -3
  36. package/lib/client/index.d.ts +5 -5
  37. package/lib/client/index.js +34 -12
  38. package/lib/client/locales.d.ts +78 -0
  39. package/lib/client/locales.js +121 -34
  40. package/lib/client/ollama-draft.d.ts +229 -0
  41. package/lib/client/ollama-draft.js +255 -0
  42. package/lib/client/reset-action.js +6 -4
  43. package/lib/client/reset-credits-view.d.ts +1 -1
  44. package/lib/client/reset-credits-view.js +66 -17
  45. package/lib/client/subscription-card-styles.js +94 -24
  46. package/lib/client/usage-color-preferences.js +4 -2
  47. package/lib/client/usage-pace.d.ts +7 -0
  48. package/lib/client/usage-pace.js +43 -10
  49. package/lib/client.js +1647 -266
  50. package/lib/client.js.map +1 -1
  51. package/lib/http.js +1 -1
  52. package/lib/index.d.ts +28 -3
  53. package/lib/index.js +9329 -8217
  54. package/lib/model-defaults.js +4 -2
  55. package/lib/provider-settings.d.ts +14 -1
  56. package/lib/provider-settings.js +64 -20
  57. package/lib/providers/account-preferences.js +51 -22
  58. package/lib/providers/accounts.js +15 -9
  59. package/lib/providers/antigravity-oauth-client.js +2 -2
  60. package/lib/providers/antigravity.js +141 -68
  61. package/lib/providers/catalog-store.js +120 -41
  62. package/lib/providers/claude-cli-version.js +3 -3
  63. package/lib/providers/claude.d.ts +23 -3
  64. package/lib/providers/claude.js +167 -92
  65. package/lib/providers/codex-client-version.js +3 -3
  66. package/lib/providers/codex-search.js +40 -13
  67. package/lib/providers/codex.js +196 -112
  68. package/lib/providers/common.d.ts +14 -0
  69. package/lib/providers/common.js +118 -38
  70. package/lib/providers/copilot.js +143 -82
  71. package/lib/providers/cursor-adapter.js +42 -18
  72. package/lib/providers/cursor-auth.d.ts +1 -1
  73. package/lib/providers/cursor-auth.js +69 -24
  74. package/lib/providers/cursor-model-settings.js +14 -5
  75. package/lib/providers/cursor-usage.js +27 -13
  76. package/lib/providers/external-usage-controller.d.ts +9 -2
  77. package/lib/providers/external-usage-controller.js +30 -7
  78. package/lib/providers/external-usage.d.ts +8 -0
  79. package/lib/providers/external-usage.js +140 -20
  80. package/lib/providers/grok.js +152 -89
  81. package/lib/providers/image-pool.js +29 -11
  82. package/lib/providers/minimax-usage.js +31 -11
  83. package/lib/providers/npm-cli-version.js +10 -3
  84. package/lib/providers/ollama-settings.d.ts +90 -0
  85. package/lib/providers/ollama-settings.js +213 -0
  86. package/lib/providers/ollama-web.d.ts +46 -0
  87. package/lib/providers/ollama-web.js +231 -0
  88. package/lib/providers/ollama.d.ts +107 -0
  89. package/lib/providers/ollama.js +608 -0
  90. package/lib/providers/pool-family.js +11 -3
  91. package/lib/providers/pool-health.js +28 -8
  92. package/lib/providers/pool-usage.js +16 -9
  93. package/lib/providers/pool.d.ts +8 -0
  94. package/lib/providers/pool.js +81 -42
  95. package/lib/providers/rate-limit.js +8 -2
  96. package/lib/providers/replay.js +40 -17
  97. package/lib/providers/reset-redemption.js +18 -6
  98. package/lib/tools/image-generate.js +123 -65
  99. package/lib/tools/registration.js +5 -2
  100. package/lib/tools/video-generate.js +51 -31
  101. package/lib/tools/x-search.js +50 -22
  102. package/lib/translate/anthropic.js +85 -37
  103. package/lib/translate/antigravity-schema.js +71 -28
  104. package/lib/translate/antigravity-thinking.js +22 -9
  105. package/lib/translate/antigravity.js +141 -50
  106. package/lib/translate/chat-completions.js +67 -23
  107. package/lib/translate/resolved.js +55 -21
  108. package/lib/translate/responses.js +69 -29
  109. package/lib/translate/sse.js +4 -1
  110. package/package.json +26 -4
@@ -10,7 +10,7 @@ function message(error) {
10
10
  return error instanceof Error ? error.message : String(error);
11
11
  }
12
12
  /** Cursor's single connected account, rendered with the same card and Manage dialog as the other providers. */
13
- export function CursorCard({ rpc, t }) {
13
+ export function CursorCard({ rpc, t, }) {
14
14
  const [status, setStatus] = useState();
15
15
  const [usage, setUsage] = useState();
16
16
  const [observedAt, setObservedAt] = useState();
@@ -56,15 +56,21 @@ export function CursorCard({ rpc, t }) {
56
56
  setLoading(false);
57
57
  }
58
58
  }, [rpc]);
59
- useEffect(() => { void refreshStatus(); }, [refreshStatus]);
59
+ useEffect(() => {
60
+ void refreshStatus();
61
+ }, [refreshStatus]);
60
62
  useEffect(() => {
61
63
  if (status?.busy !== true)
62
64
  return;
63
- const timer = setInterval(() => { void refreshStatus(); }, 2000);
65
+ const timer = setInterval(() => {
66
+ void refreshStatus();
67
+ }, 2000);
64
68
  return () => clearInterval(timer);
65
69
  }, [status?.busy, refreshStatus]);
66
- useEffect(() => { if (status?.authenticated === true)
67
- void refreshUsage(); }, [status?.authenticated, refreshUsage]);
70
+ useEffect(() => {
71
+ if (status?.authenticated === true)
72
+ void refreshUsage();
73
+ }, [status?.authenticated, refreshUsage]);
68
74
  const login = async () => {
69
75
  setError(undefined);
70
76
  try {
@@ -102,16 +108,44 @@ export function CursorCard({ rpc, t }) {
102
108
  }
103
109
  };
104
110
  const connected = status?.authenticated === true;
105
- return _jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: { ...styles.dot, background: status?.busy ? 'var(--dsw-alias-state-warn-label)'
106
- : connected ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-dimmed)' } }), _jsx("span", { style: styles.name, children: t('cursorTitle') })] }), _jsx("p", { style: styles.status, children: status === undefined ? t('checking') : status.busy ? t('loginInProgress')
107
- : connected ? t('loggedInCount', { count: 1 }) : t('notLoggedIn') }), error !== undefined && _jsx("p", { style: styles.error, children: error }), connected && _jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: "Cursor" }), status.expiresAt !== undefined && _jsx("span", { style: styles.status, children: t('accountExpires', { date: new Date(status.expiresAt).toLocaleString() }) }), _jsx("button", { type: "button", style: { ...styles.button, marginLeft: 'auto', flexShrink: 0 }, onClick: () => { void logout(); }, children: t('logout') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), usage?.plan !== undefined && _jsx("span", { style: styles.status, children: t('usagePlan', { plan: usage.plan }) }), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading, onClick: () => { void refreshUsage(); }, children: t('usageRefresh') })] }), loading && usage === undefined && _jsx("p", { style: styles.status, children: t('usageLoading') }), usage?.windows?.length === 0 && _jsx("p", { style: styles.status, children: t('usageEmpty') }), usage?.windows?.map((window, index) => {
111
+ return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: {
112
+ ...styles.dot,
113
+ background: status?.busy
114
+ ? 'var(--dsw-alias-state-warn-label)'
115
+ : connected
116
+ ? 'var(--dsw-alias-state-success-primary)'
117
+ : 'var(--dsw-alias-label-dimmed)',
118
+ } }), _jsx("span", { style: styles.name, children: t('cursorTitle') })] }), _jsx("p", { style: styles.status, children: status === undefined
119
+ ? t('checking')
120
+ : status.busy
121
+ ? t('loginInProgress')
122
+ : connected
123
+ ? t('loggedInCount', { count: 1 })
124
+ : t('notLoggedIn') }), error !== undefined && _jsx("p", { style: styles.error, children: error }), connected && (_jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: "Cursor" }), status.expiresAt !== undefined && (_jsx("span", { style: styles.status, children: t('accountExpires', {
125
+ date: new Date(status.expiresAt).toLocaleString(),
126
+ }) })), _jsx("button", { type: "button", style: { ...styles.button, marginLeft: 'auto', flexShrink: 0 }, onClick: () => {
127
+ void logout();
128
+ }, children: t('logout') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), usage?.plan !== undefined && (_jsx("span", { style: styles.status, children: t('usagePlan', { plan: usage.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading, onClick: () => {
129
+ void refreshUsage();
130
+ }, children: t('usageRefresh') })] }), loading && usage === undefined && (_jsx("p", { style: styles.status, children: t('usageLoading') })), usage?.windows?.length === 0 && (_jsx("p", { style: styles.status, children: t('usageEmpty') })), usage?.windows?.map((window, index) => {
108
131
  const percent = displayUsedPercent(window.usedPercent);
109
- const label = window.scope === 'Included' ? t('cursorIncluded')
110
- : window.scope === 'Cursor Models' ? t('cursorModels')
111
- : window.scope === 'Other Models' ? t('cursorOtherModels')
112
- : window.scope === 'Included requests' ? t('cursorIncludedRequests')
113
- : window.scope ?? t('usageWindow');
114
- return _jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined ? t('usageMeterInvalid') : `${percent}%`, window.resetsAt === undefined ? ''
115
- : ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt, stale: usageStale })] }, index);
116
- })] })] }), _jsxs("div", { style: styles.actions, children: [status?.authenticated === false && !status.busy && _jsx("button", { type: "button", style: styles.button, onClick: () => { void login(); }, children: t('login') }), _jsx("button", { type: "button", style: styles.button, "aria-haspopup": "dialog", onClick: () => setManage(true), children: t('accountsManage') }), status?.busy === true && _jsx("button", { type: "button", style: styles.button, onClick: () => { void cancel(); }, children: t('cancel') })] }), status?.busy === true && authorizeUrl !== undefined && _jsx("a", { href: authorizeUrl, target: "_blank", rel: "noopener noreferrer", children: t('cursorOpenLogin') }), manage && _jsx(ProviderAccountManager, { provider: "cursor-subscription", name: "Cursor", rpc: rpc, t: t, onClose: () => setManage(false) })] });
132
+ const label = window.scope === 'Included'
133
+ ? t('cursorIncluded')
134
+ : window.scope === 'Cursor Models'
135
+ ? t('cursorModels')
136
+ : window.scope === 'Other Models'
137
+ ? t('cursorOtherModels')
138
+ : window.scope === 'Included requests'
139
+ ? t('cursorIncludedRequests')
140
+ : (window.scope ?? t('usageWindow'));
141
+ return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
142
+ ? t('usageMeterInvalid')
143
+ : `${percent}%`, window.resetsAt === undefined
144
+ ? ''
145
+ : ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt, stale: usageStale })] }, index));
146
+ })] })] })), _jsxs("div", { style: styles.actions, children: [status?.authenticated === false && !status.busy && (_jsx("button", { type: "button", style: styles.button, onClick: () => {
147
+ void login();
148
+ }, children: t('login') })), _jsx("button", { type: "button", style: styles.button, "aria-haspopup": "dialog", onClick: () => setManage(true), children: t('accountsManage') }), status?.busy === true && (_jsx("button", { type: "button", style: styles.button, onClick: () => {
149
+ void cancel();
150
+ }, children: t('cancel') }))] }), status?.busy === true && authorizeUrl !== undefined && (_jsx("a", { href: authorizeUrl, target: "_blank", rel: "noopener noreferrer", children: t('cursorOpenLogin') })), manage && (_jsx(ProviderAccountManager, { provider: "cursor-subscription", name: "Cursor", rpc: rpc, t: t, onClose: () => setManage(false) }))] }));
117
151
  }
@@ -12,7 +12,7 @@ type Translate = SubscriptionsSectionInjected['t'];
12
12
  */
13
13
  export declare function externalUsageWindowLabel(t: Translate, window: UsageWindow): string;
14
14
  /** Quota cards for API-key providers already available through DSH itself. */
15
- export declare function ExternalUsageCards({ rpc, t }: {
15
+ export declare function ExternalUsageCards({ rpc, t, }: {
16
16
  rpc: ConnectionHandle['rpc'];
17
17
  t: Translate;
18
18
  }): import("react").JSX.Element;
@@ -26,13 +26,17 @@ function errorText(error) {
26
26
  export function externalUsageWindowLabel(t, window) {
27
27
  const named = window.scope !== undefined && window.scope !== '';
28
28
  if (window.kind === 'other') {
29
- return window.scope === 'Monthly' ? t('usageMonthly') : named ? window.scope : t('usageWindow');
29
+ return window.scope === 'Monthly'
30
+ ? t('usageMonthly')
31
+ : named
32
+ ? window.scope
33
+ : t('usageWindow');
30
34
  }
31
35
  const base = window.kind === 'session' ? t('usageSession') : t('usageWeekly');
32
36
  return named ? `${base} · ${window.scope}` : base;
33
37
  }
34
38
  /** Quota cards for API-key providers already available through DSH itself. */
35
- export function ExternalUsageCards({ rpc, t }) {
39
+ export function ExternalUsageCards({ rpc, t, }) {
36
40
  const [status, setStatus] = useState();
37
41
  const [statusError, setStatusError] = useState();
38
42
  const [usage, setUsage] = useState({});
@@ -40,20 +44,23 @@ export function ExternalUsageCards({ rpc, t }) {
40
44
  const [errors, setErrors] = useState({});
41
45
  const [loading, setLoading] = useState({});
42
46
  const refresh = useCallback(async (source) => {
43
- setLoading(prev => ({ ...prev, [source]: true }));
47
+ setLoading((prev) => ({ ...prev, [source]: true }));
44
48
  try {
45
49
  const value = await callSubscriptionsAuth(rpc, 'externalUsage', { source });
46
- setUsage(prev => ({ ...prev, [source]: value }));
50
+ setUsage((prev) => ({ ...prev, [source]: value }));
47
51
  // Prefer the server's observation time; a cached reading must not look fresh.
48
- setObservedAt(prev => ({ ...prev, [source]: value.observedAt ?? Date.now() }));
49
- setErrors(prev => ({ ...prev, [source]: undefined }));
52
+ setObservedAt((prev) => ({
53
+ ...prev,
54
+ [source]: value.observedAt ?? Date.now(),
55
+ }));
56
+ setErrors((prev) => ({ ...prev, [source]: undefined }));
50
57
  window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
51
58
  }
52
59
  catch (error) {
53
- setErrors(prev => ({ ...prev, [source]: errorText(error) }));
60
+ setErrors((prev) => ({ ...prev, [source]: errorText(error) }));
54
61
  }
55
62
  finally {
56
- setLoading(prev => ({ ...prev, [source]: false }));
63
+ setLoading((prev) => ({ ...prev, [source]: false }));
57
64
  }
58
65
  }, [rpc]);
59
66
  const refreshStatus = useCallback(async () => {
@@ -72,16 +79,30 @@ export function ExternalUsageCards({ rpc, t }) {
72
79
  useEffect(() => {
73
80
  void refreshStatus();
74
81
  }, [refreshStatus]);
75
- return _jsx(_Fragment, { children: SOURCES.map(({ id, name, ref }) => {
82
+ return (_jsx(_Fragment, { children: SOURCES.map(({ id, name, ref }) => {
76
83
  const configured = status?.[id]?.configured === true;
77
84
  const snapshot = usage[id];
78
- return _jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: { ...styles.dot, background: configured
79
- ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-dimmed)' } }), _jsx("span", { style: styles.name, children: name })] }), !(status === undefined && statusError !== undefined) && _jsx("p", { style: styles.status, children: status === undefined ? t('checking')
80
- : configured ? t('externalUsageConnected') : t('externalUsageNotConfigured') }), statusError !== undefined && _jsx("p", { style: styles.error, children: t('externalUsageUnavailable', { message: statusError }) }), status !== undefined && !configured && _jsx("p", { style: styles.status, children: t('externalUsageConfigure', { ref }) }), configured && _jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: t('externalUsageApiKey') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), snapshot?.plan !== undefined && _jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: snapshot.plan }) }), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading[id] === true, onClick: () => { void refresh(id); }, children: t('usageRefresh') })] }), loading[id] === true && snapshot === undefined && _jsx("p", { style: styles.status, children: t('usageLoading') }), errors[id] !== undefined && _jsx("p", { style: styles.error, children: t('usageError', { message: errors[id] }) }), snapshot?.windows?.length === 0 && _jsx("p", { style: styles.status, children: t('usageEmpty') }), snapshot?.windows?.map((window, index) => {
85
+ return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: {
86
+ ...styles.dot,
87
+ background: configured
88
+ ? 'var(--dsw-alias-state-success-primary)'
89
+ : 'var(--dsw-alias-label-dimmed)',
90
+ } }), _jsx("span", { style: styles.name, children: name })] }), !(status === undefined && statusError !== undefined) && (_jsx("p", { style: styles.status, children: status === undefined
91
+ ? t('checking')
92
+ : configured
93
+ ? t('externalUsageConnected')
94
+ : t('externalUsageNotConfigured') })), statusError !== undefined && (_jsx("p", { style: styles.error, children: t('externalUsageUnavailable', { message: statusError }) })), status !== undefined && !configured && (_jsx("p", { style: styles.status, children: t('externalUsageConfigure', { ref }) })), configured && (_jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: t('externalUsageApiKey') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), snapshot?.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: snapshot.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading[id] === true, onClick: () => {
95
+ void refresh(id);
96
+ }, children: t('usageRefresh') })] }), loading[id] === true && snapshot === undefined && (_jsx("p", { style: styles.status, children: t('usageLoading') })), errors[id] !== undefined && (_jsx("p", { style: styles.error, children: t('usageError', { message: errors[id] }) })), snapshot !== undefined && snapshot.supported === false && (_jsx("p", { style: styles.status, children: t('usageUnsupported') })), snapshot?.windows?.length === 0 && (_jsx("p", { style: styles.status, children: t('usageEmpty') })), snapshot?.windows?.map((window, index) => {
81
97
  const percent = displayUsedPercent(window.usedPercent);
82
98
  const label = externalUsageWindowLabel(t, window);
83
- return _jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined ? t('usageMeterInvalid') : `${percent}%`, window.resetsAt === undefined ? ''
84
- : ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt[id], stale: errors[id] !== undefined || snapshot?.stale === true })] }, index);
85
- })] })] }), !configured && _jsx("div", { style: styles.actions, children: _jsx("button", { type: "button", style: styles.button, onClick: () => { void refreshStatus(); }, children: t('usageRefresh') }) })] }, id);
86
- }) });
99
+ return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
100
+ ? t('usageMeterInvalid')
101
+ : `${percent}%`, window.resetsAt === undefined
102
+ ? ''
103
+ : ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt[id], stale: errors[id] !== undefined || snapshot?.stale === true })] }, index));
104
+ })] })] })), !configured && (_jsx("div", { style: styles.actions, children: _jsx("button", { type: "button", style: styles.button, onClick: () => {
105
+ void refreshStatus();
106
+ }, children: t('usageRefresh') }) }))] }, id));
107
+ }) }));
87
108
  }
@@ -37,7 +37,7 @@ export interface MessageImageLabels {
37
37
  * preview. A lone image renders at its `singleFit` size; an image among
38
38
  * several renders as a fixed 64px square tile.
39
39
  */
40
- export declare function MessageImage({ attachment, load, variant, labels }: {
40
+ export declare function MessageImage({ attachment, load, variant, labels, }: {
41
41
  attachment: ImageAttachmentRef;
42
42
  load: ImageLoader;
43
43
  variant: 'single' | 'tile';
@@ -45,7 +45,7 @@ export declare function MessageImage({ attachment, load, variant, labels }: {
45
45
  }): import("react").JSX.Element;
46
46
  /** Wrapping image group: a lone image renders large, several render as 64px
47
47
  * square tiles (same rule as the platform gallery). */
48
- export declare function ImageGallery({ images, load, labels }: {
48
+ export declare function ImageGallery({ images, load, labels, }: {
49
49
  images: readonly {
50
50
  attachment: ImageAttachmentRef;
51
51
  }[];
@@ -20,7 +20,9 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
20
20
  function singleFit(attachment) {
21
21
  const natural = attachment.width / attachment.height;
22
22
  const ratio = Math.min(4, Math.max(0.25, natural));
23
- const box = ratio >= 1 ? { width: 240, height: 240 / ratio } : { width: 240 * ratio, height: 240 };
23
+ const box = ratio >= 1
24
+ ? { width: 240, height: 240 / ratio }
25
+ : { width: 240 * ratio, height: 240 };
24
26
  const scale = Math.min(1, attachment.width / box.width, attachment.height / box.height);
25
27
  return {
26
28
  width: Math.max(1, Math.round(box.width * scale)),
@@ -29,29 +31,68 @@ function singleFit(attachment) {
29
31
  };
30
32
  }
31
33
  const styles = {
32
- gallery: { display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-start' },
34
+ gallery: {
35
+ display: 'flex',
36
+ flexWrap: 'wrap',
37
+ gap: 8,
38
+ justifyContent: 'flex-start',
39
+ },
33
40
  frame: {
34
- display: 'grid', placeItems: 'center', overflow: 'hidden', padding: 0,
35
- border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)', borderRadius: 8,
36
- background: 'var(--dsw-alias-interactive-bg-hover-solid)', cursor: 'zoom-in',
41
+ display: 'grid',
42
+ placeItems: 'center',
43
+ overflow: 'hidden',
44
+ padding: 0,
45
+ border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)',
46
+ borderRadius: 8,
47
+ background: 'var(--dsw-alias-interactive-bg-hover-solid)',
48
+ cursor: 'zoom-in',
37
49
  },
38
50
  tile: { width: 64, height: 64 },
39
51
  img: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
40
- loading: { fontSize: 12, color: 'var(--dsw-alias-label-tertiary)', padding: '0 8px' },
52
+ loading: {
53
+ fontSize: 12,
54
+ color: 'var(--dsw-alias-label-tertiary)',
55
+ padding: '0 8px',
56
+ },
41
57
  error: {
42
- fontSize: 12, color: 'var(--dsw-alias-state-error-primary)', cursor: 'pointer',
43
- border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)', borderRadius: 8,
44
- background: 'transparent', padding: '6px 10px',
58
+ fontSize: 12,
59
+ color: 'var(--dsw-alias-state-error-primary)',
60
+ cursor: 'pointer',
61
+ border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)',
62
+ borderRadius: 8,
63
+ background: 'transparent',
64
+ padding: '6px 10px',
45
65
  },
46
66
  overlay: {
47
- position: 'fixed', inset: 0, zIndex: 1000, display: 'grid', placeItems: 'center',
48
- background: 'rgba(0, 0, 0, 0.72)', padding: 24,
67
+ position: 'fixed',
68
+ inset: 0,
69
+ zIndex: 1000,
70
+ display: 'grid',
71
+ placeItems: 'center',
72
+ background: 'rgba(0, 0, 0, 0.72)',
73
+ padding: 24,
74
+ },
75
+ overlayImg: {
76
+ maxWidth: '92vw',
77
+ maxHeight: '92vh',
78
+ objectFit: 'contain',
79
+ borderRadius: 4,
49
80
  },
50
- overlayImg: { maxWidth: '92vw', maxHeight: '92vh', objectFit: 'contain', borderRadius: 4 },
51
81
  close: {
52
- position: 'absolute', top: 12, right: 12, width: 32, height: 32, display: 'grid',
53
- placeItems: 'center', border: 'none', borderRadius: '50%', cursor: 'pointer',
54
- background: 'rgba(255, 255, 255, 0.16)', color: '#fff', fontSize: 16, lineHeight: 1,
82
+ position: 'absolute',
83
+ top: 12,
84
+ right: 12,
85
+ width: 32,
86
+ height: 32,
87
+ display: 'grid',
88
+ placeItems: 'center',
89
+ border: 'none',
90
+ borderRadius: '50%',
91
+ cursor: 'pointer',
92
+ background: 'rgba(255, 255, 255, 0.16)',
93
+ color: '#fff',
94
+ fontSize: 16,
95
+ lineHeight: 1,
55
96
  },
56
97
  };
57
98
  /**
@@ -59,52 +100,70 @@ const styles = {
59
100
  * Escape all dismiss; the image itself is inert so a click on it does not
60
101
  * fall through to the backdrop dismissal.
61
102
  */
62
- function ImageLightbox({ src, alt, labels, onClose }) {
103
+ function ImageLightbox({ src, alt, labels, onClose, }) {
63
104
  useEffect(() => {
64
- const onKey = (event) => { if (event.key === 'Escape')
65
- onClose(); };
105
+ const onKey = (event) => {
106
+ if (event.key === 'Escape')
107
+ onClose();
108
+ };
66
109
  window.addEventListener('keydown', onKey);
67
- return () => { window.removeEventListener('keydown', onKey); };
110
+ return () => {
111
+ window.removeEventListener('keydown', onKey);
112
+ };
68
113
  }, [onClose]);
69
- return (_jsxs("div", { role: "dialog", "aria-label": labels.dialog, style: styles.overlay, onClick: onClose, children: [_jsx("img", { src: src, alt: alt, style: styles.overlayImg, onClick: event => { event.stopPropagation(); } }), _jsx("button", { type: "button", "aria-label": labels.close, style: styles.close, onClick: onClose, children: "\u00D7" })] }));
114
+ return (_jsxs("div", { role: "dialog", "aria-label": labels.dialog, style: styles.overlay, onClick: onClose, children: [_jsx("img", { src: src, alt: alt, style: styles.overlayImg, onClick: (event) => {
115
+ event.stopPropagation();
116
+ } }), _jsx("button", { type: "button", "aria-label": labels.close, style: styles.close, onClick: onClose, children: "\u00D7" })] }));
70
117
  }
71
118
  /**
72
119
  * Compact history renderer with retryable loading and click-to-open original
73
120
  * preview. A lone image renders at its `singleFit` size; an image among
74
121
  * several renders as a fixed 64px square tile.
75
122
  */
76
- export function MessageImage({ attachment, load, variant, labels }) {
123
+ export function MessageImage({ attachment, load, variant, labels, }) {
77
124
  const [src, setSrc] = useState(null);
78
125
  const [error, setError] = useState(false);
79
126
  const [open, setOpen] = useState(false);
80
127
  // Retry re-arms the one load effect below, so every attempt — first load or
81
128
  // retry — runs under the same liveness guard and the same reset.
82
129
  const [attempt, setAttempt] = useState(0);
83
- const retry = useCallback(() => { setAttempt(a => a + 1); }, []);
84
- const close = useCallback(() => { setOpen(false); }, []);
130
+ const retry = useCallback(() => {
131
+ setAttempt((a) => a + 1);
132
+ }, []);
133
+ const close = useCallback(() => {
134
+ setOpen(false);
135
+ }, []);
85
136
  const fit = useMemo(() => (variant === 'single' ? singleFit(attachment) : undefined), [attachment, variant]);
86
137
  useEffect(() => {
87
138
  let live = true;
88
139
  setError(false);
89
140
  setSrc(null);
90
- void load(attachment).then((url) => { if (live)
91
- setSrc(url); }).catch(() => { if (live)
92
- setError(true); });
93
- return () => { live = false; };
141
+ void load(attachment)
142
+ .then((url) => {
143
+ if (live)
144
+ setSrc(url);
145
+ })
146
+ .catch(() => {
147
+ if (live)
148
+ setError(true);
149
+ });
150
+ return () => {
151
+ live = false;
152
+ };
94
153
  }, [attachment, load, attempt]);
95
154
  const label = attachment.name ?? labels.image;
96
155
  if (error) {
97
- return _jsx("button", { type: "button", style: styles.error, onClick: retry, children: labels.loadFailed });
156
+ return (_jsx("button", { type: "button", style: styles.error, onClick: retry, children: labels.loadFailed }));
98
157
  }
99
158
  const box = fit === undefined ? styles.tile : { width: fit.width, height: fit.height };
100
- return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", style: { ...styles.frame, ...box }, title: labels.open, "aria-label": labels.openNamed(label), onClick: () => { if (src !== null)
101
- setOpen(true); }, children: src === null
102
- ? _jsx("span", { style: styles.loading, children: labels.loading })
103
- : _jsx("img", { src: src, alt: label, style: { ...styles.img, objectPosition: fit?.objectPosition } }) }), open && src !== null && _jsx(ImageLightbox, { src: src, alt: label, labels: labels.lightbox, onClose: close })] }));
159
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", style: { ...styles.frame, ...box }, title: labels.open, "aria-label": labels.openNamed(label), onClick: () => {
160
+ if (src !== null)
161
+ setOpen(true);
162
+ }, children: src === null ? (_jsx("span", { style: styles.loading, children: labels.loading })) : (_jsx("img", { src: src, alt: label, style: { ...styles.img, objectPosition: fit?.objectPosition } })) }), open && src !== null && (_jsx(ImageLightbox, { src: src, alt: label, labels: labels.lightbox, onClose: close }))] }));
104
163
  }
105
164
  /** Wrapping image group: a lone image renders large, several render as 64px
106
165
  * square tiles (same rule as the platform gallery). */
107
- export function ImageGallery({ images, load, labels }) {
166
+ export function ImageGallery({ images, load, labels, }) {
108
167
  if (images.length === 0)
109
168
  return null;
110
169
  const variant = images.length === 1 ? 'single' : 'tile';
@@ -16,8 +16,9 @@ const PROMPT_MAX_LENGTH = 60;
16
16
  export function createImageLoader(rpc) {
17
17
  // The host validates a full ImageAttachmentRef payload (readImage takes the
18
18
  // whole ref), so forward the attachment verbatim.
19
- return attachment => callSubscriptionsAuth(rpc, 'image', { ...attachment })
20
- .then(result => `data:${result.mediaType};base64,${result.dataBase64}`);
19
+ return (attachment) => callSubscriptionsAuth(rpc, 'image', {
20
+ ...attachment,
21
+ }).then((result) => `data:${result.mediaType};base64,${result.dataBase64}`);
21
22
  }
22
23
  /**
23
24
  * English-dictionary fallback for a missing locale seat (standalone renders);
@@ -58,7 +59,9 @@ function derivePrompt(argsRaw) {
58
59
  }
59
60
  }
60
61
  const line = (prompt ?? argsRaw).split('\n', 1)[0] ?? '';
61
- return line.length > PROMPT_MAX_LENGTH ? `${line.slice(0, PROMPT_MAX_LENGTH)}…` : line;
62
+ return line.length > PROMPT_MAX_LENGTH
63
+ ? `${line.slice(0, PROMPT_MAX_LENGTH)}…`
64
+ : line;
62
65
  }
63
66
  /** Flatten a settled result's text blocks (the degraded text-only route and the error line). */
64
67
  function resultText(block) {
@@ -85,19 +88,46 @@ function resultImages(block) {
85
88
  return images;
86
89
  }
87
90
  const styles = {
88
- container: { display: 'flex', flexDirection: 'column', gap: 6, padding: '4px 0' },
91
+ container: {
92
+ display: 'flex',
93
+ flexDirection: 'column',
94
+ gap: 6,
95
+ padding: '4px 0',
96
+ },
89
97
  row: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 },
90
- icon: { display: 'inline-flex', flexShrink: 0, color: 'var(--dsw-alias-label-tertiary)' },
98
+ icon: {
99
+ display: 'inline-flex',
100
+ flexShrink: 0,
101
+ color: 'var(--dsw-alias-label-tertiary)',
102
+ },
91
103
  title: {
92
- fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)',
93
- overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
104
+ fontSize: 13,
105
+ lineHeight: '20px',
106
+ color: 'var(--dsw-alias-label-primary)',
107
+ overflow: 'hidden',
108
+ textOverflow: 'ellipsis',
109
+ whiteSpace: 'nowrap',
110
+ },
111
+ subtle: {
112
+ margin: 0,
113
+ fontSize: 12,
114
+ lineHeight: '18px',
115
+ color: 'var(--dsw-alias-label-tertiary)',
94
116
  },
95
- subtle: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
96
117
  output: {
97
- margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)',
98
- whiteSpace: 'pre-wrap', overflowWrap: 'anywhere',
118
+ margin: 0,
119
+ fontSize: 12,
120
+ lineHeight: '18px',
121
+ color: 'var(--dsw-alias-label-secondary)',
122
+ whiteSpace: 'pre-wrap',
123
+ overflowWrap: 'anywhere',
124
+ },
125
+ error: {
126
+ margin: 0,
127
+ fontSize: 12,
128
+ lineHeight: '18px',
129
+ color: 'var(--dsw-alias-state-error-primary)',
99
130
  },
100
- error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },
101
131
  };
102
132
  /**
103
133
  * The `image_generate` keyed toolview component.
@@ -110,21 +140,27 @@ export function ImageGenerateToolview(props) {
110
140
  if (block === undefined)
111
141
  return null;
112
142
  const settled = 'kind' in block;
113
- const argsRaw = (settled ? block.call?.argsRaw : block.phase === 'start' ? block.argsRaw : undefined) ?? '';
143
+ const argsRaw = (settled
144
+ ? block.call?.argsRaw
145
+ : block.phase === 'start'
146
+ ? block.argsRaw
147
+ : undefined) ?? '';
114
148
  let references = 0;
115
149
  try {
116
150
  const args = JSON.parse(argsRaw);
117
151
  if (Array.isArray(args?.referenceImages))
118
152
  references = args.referenceImages.length;
119
153
  }
120
- catch { /* Arguments may still be streaming. */ }
154
+ catch {
155
+ /* Arguments may still be streaming. */
156
+ }
121
157
  const title = `image_generate${references > 0 ? ` (${references} ref)` : ''}: ${derivePrompt(argsRaw)}`;
122
158
  const images = resultImages(block);
123
159
  const text = settled ? resultText(block) : '';
124
160
  const labels = {
125
161
  image: t('image'),
126
162
  open: t('viewImage'),
127
- openNamed: name => t('viewImageNamed', { name }),
163
+ openNamed: (name) => t('viewImageNamed', { name }),
128
164
  loading: t('imageLoading'),
129
165
  loadFailed: t('imageLoadFailed'),
130
166
  lightbox: { dialog: t('imagePreview'), close: t('imageClose') },
@@ -0,0 +1,13 @@
1
+ import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client';
2
+ import type { SubscriptionsSectionInjected } from './SubscriptionsSection.js';
3
+ type Translate = SubscriptionsSectionInjected['t'];
4
+ /**
5
+ * Ollama Cloud route card: the catalog the model picker shows, the web
6
+ * capability toggle, and the API-key usage row. The settings dialog owns
7
+ * every write; the card only reads, so a save is reflected by re-reading.
8
+ */
9
+ export declare function OllamaCard({ rpc, t, }: {
10
+ rpc: ConnectionHandle['rpc'];
11
+ t: Translate;
12
+ }): import("react").JSX.Element;
13
+ export {};
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { callSubscriptionsAuth } from './subscriptions-rpc.js';
4
+ import { externalUsageWindowLabel } from './ExternalUsageCards.js';
5
+ import { OllamaSettingsManager } from './OllamaSettingsManager.js';
6
+ import { USAGE_BADGE_REFRESH_EVENT } from './usage-badge-preferences.js';
7
+ import { subscriptionCardStyles as styles } from './subscription-card-styles.js';
8
+ import { UsageMeter } from './UsageMeter.js';
9
+ import { displayUsedPercent } from './usage-pace.js';
10
+ function message(error) {
11
+ return error instanceof Error ? error.message : String(error);
12
+ }
13
+ /**
14
+ * Ollama Cloud route card: the catalog the model picker shows, the web
15
+ * capability toggle, and the API-key usage row. The settings dialog owns
16
+ * every write; the card only reads, so a save is reflected by re-reading.
17
+ */
18
+ export function OllamaCard({ rpc, t, }) {
19
+ const [view, setView] = useState();
20
+ const [configured, setConfigured] = useState();
21
+ const [usage, setUsage] = useState();
22
+ const [observedAt, setObservedAt] = useState();
23
+ const [usageStale, setUsageStale] = useState(false);
24
+ const [loading, setLoading] = useState(false);
25
+ // Two failure surfaces: the card's own load and the usage lookup. They
26
+ // render in different places so a stale usage reading cannot masquerade as
27
+ // a settings failure.
28
+ const [statusError, setStatusError] = useState();
29
+ const [usageError, setUsageError] = useState();
30
+ const [manage, setManage] = useState(false);
31
+ const refreshUsage = useCallback(async () => {
32
+ setLoading(true);
33
+ try {
34
+ const value = await callSubscriptionsAuth(rpc, 'externalUsage', { source: 'ollama-cloud' });
35
+ setUsage(value);
36
+ // Prefer the server's observation time; a cached reading must not look fresh.
37
+ setObservedAt(value.observedAt ?? Date.now());
38
+ setUsageStale(value.stale === true);
39
+ setUsageError(undefined);
40
+ window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
41
+ }
42
+ catch (failure) {
43
+ setUsageError(message(failure));
44
+ setUsageStale(true);
45
+ }
46
+ finally {
47
+ setLoading(false);
48
+ }
49
+ }, [rpc]);
50
+ const refreshStatus = useCallback(async () => {
51
+ try {
52
+ const [settings, status] = await Promise.all([
53
+ callSubscriptionsAuth(rpc, 'ollamaSettings', {}),
54
+ callSubscriptionsAuth(rpc, 'externalStatus', {}),
55
+ ]);
56
+ setView(settings);
57
+ setStatusError(undefined);
58
+ const isConfigured = status['ollama-cloud']?.configured === true;
59
+ setConfigured(isConfigured);
60
+ if (isConfigured)
61
+ void refreshUsage();
62
+ }
63
+ catch (failure) {
64
+ setStatusError(message(failure));
65
+ }
66
+ }, [rpc, refreshUsage]);
67
+ useEffect(() => {
68
+ void refreshStatus();
69
+ }, [refreshStatus]);
70
+ return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: {
71
+ ...styles.dot,
72
+ background: configured === true
73
+ ? 'var(--dsw-alias-state-success-primary)'
74
+ : 'var(--dsw-alias-label-dimmed)',
75
+ } }), _jsx("span", { style: styles.name, children: t('ollamaTitle') })] }), _jsx("p", { style: styles.status, children: view === undefined
76
+ ? t('checking')
77
+ : view.models.length > 0
78
+ ? t('ollamaStatus', { count: view.models.length })
79
+ : t('ollamaStatusNone') }), view !== undefined && (_jsx("p", { style: styles.status, children: view.effective.web ? t('ollamaWebOn') : t('ollamaWebOff') })), statusError !== undefined && (_jsx("p", { style: styles.error, children: t('ollamaModelsLoadFailed', { message: statusError }) })), view !== undefined && configured === false && (_jsx("p", { style: styles.status, children: t('ollamaConfigureHint', { ref: view.effective.apiKeyRef }) })), configured === true && (_jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: t('externalUsageApiKey') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), usage?.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: usage.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading, onClick: () => {
80
+ void refreshUsage();
81
+ }, children: t('usageRefresh') })] }), loading && usage === undefined && (_jsx("p", { style: styles.status, children: t('usageLoading') })), usageError !== undefined && (_jsx("p", { style: styles.error, children: t('usageError', { message: usageError }) })), usage !== undefined && usage.supported === false && (_jsx("p", { style: styles.status, children: t('usageUnsupported') })), usage?.windows?.length === 0 && (_jsx("p", { style: styles.status, children: t('usageEmpty') })), usage?.windows?.map((window, index) => {
82
+ const percent = displayUsedPercent(window.usedPercent);
83
+ const label = externalUsageWindowLabel(t, window);
84
+ return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
85
+ ? t('usageMeterInvalid')
86
+ : `${percent}%`, window.resetsAt === undefined
87
+ ? ''
88
+ : ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt, stale: usageStale })] }, index));
89
+ })] })] })), view?.configOverridesModels === true && (_jsx("p", { style: styles.status, children: t('ollamaCatalogOverride') })), _jsxs("div", { style: styles.actions, children: [_jsx("button", { type: "button", style: styles.button, "aria-haspopup": "dialog", onClick: () => setManage(true), children: t('accountsManage') }), configured !== true && (_jsx("button", { type: "button", style: styles.button, onClick: () => {
90
+ void refreshStatus();
91
+ }, children: t('usageRefresh') }))] }), manage && (_jsx(OllamaSettingsManager, { rpc: rpc, t: t, onClose: () => setManage(false), onSaved: () => {
92
+ void refreshStatus();
93
+ } }))] }));
94
+ }