dsh-codex-subscription 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,865 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-codex-subscription",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/settings-contract.js
11
+ const QUICK_QUOTA_FIELD = "quickQuotaVisible";
12
+ const SEARCH_PROVIDER_FIELD = "searchProvider";
13
+ const SEARCH_PROVIDER_CODEX = "codex";
14
+ const normalizeSearchProvider = (value) => ["dsh", "codex"].includes(value) ? value : "dsh";
15
+ //#endregion
16
+ //#region src/sidebar-quota.js
17
+ const isDisplayableWindow = (window) => Number.isFinite(window?.remainingPercent) && window.remainingPercent >= 0 && window.remainingPercent <= 100 && Number.isFinite(window?.windowSeconds) && window.windowSeconds > 0;
18
+ const normalized = (value) => String(value ?? "").toLocaleLowerCase("en-US").replaceAll(/[^a-z0-9]+/gu, "-");
19
+ const limitMatchesModel = (limit, model) => {
20
+ if (/\bspark\b/u.test(normalized(model))) return /\bspark\b/u.test(normalized(`${limit?.id ?? ""} ${limit?.name ?? ""}`));
21
+ return limit?.id === "codex";
22
+ };
23
+ function selectModelQuota(usage, model) {
24
+ const windows = Array.isArray(usage?.rateLimits) ? usage.rateLimits.filter((limit) => limitMatchesModel(limit, model) && Array.isArray(limit.windows)).flatMap((limit) => limit.windows).filter(isDisplayableWindow) : [];
25
+ if (windows.length === 0) return void 0;
26
+ const selected = windows.reduce((lowest, candidate) => candidate.remainingPercent < lowest.remainingPercent ? candidate : lowest);
27
+ return {
28
+ remainingPercent: selected.remainingPercent,
29
+ windowSeconds: selected.windowSeconds,
30
+ ...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {}
31
+ };
32
+ }
33
+ //#endregion
34
+ //#region src/client.jsx
35
+ const inject = [
36
+ "slots",
37
+ "locale",
38
+ "connection",
39
+ "modelDirectories"
40
+ ];
41
+ const NS = "settings.codexSubscription";
42
+ const CHANNEL = "/codex-subscription";
43
+ const QUICK_QUOTA_REFRESH_EVENT = "dsh-codex-subscription:refresh-quick-quota";
44
+ const QUICK_QUOTA_REFRESH_MS = 6e4;
45
+ const zh = {
46
+ nav: "Codex 订阅",
47
+ title: "Codex 订阅",
48
+ connected: "已登录",
49
+ disconnected: "未登录",
50
+ accountLoading: "正在读取账户状态…",
51
+ browserLogin: "浏览器登录",
52
+ deviceLogin: "设备代码登录",
53
+ logout: "退出登录",
54
+ cancel: "取消",
55
+ submit: "提交授权码",
56
+ openLogin: "打开登录页",
57
+ manualCode: "若浏览器回调没有自动完成,请粘贴授权码或完整重定向地址。",
58
+ deviceHint: "在登录页输入此设备代码:",
59
+ waiting: "正在等待登录完成…",
60
+ failed: "登录失败,请重试。",
61
+ loadFailed: "无法读取 Codex 状态。",
62
+ searchTitle: "搜索来源",
63
+ searchDsh: "DSH 默认",
64
+ searchDshHint: "当前搜索服务",
65
+ searchCodex: "Codex 订阅",
66
+ searchCodexHint: "ChatGPT 订阅搜索",
67
+ preferenceFailed: "无法更新设置,请重试。",
68
+ usage: "订阅额度",
69
+ refresh: "刷新",
70
+ refreshing: "刷新中…",
71
+ noUsage: "登录后可读取 ChatGPT 返回的额度窗口。",
72
+ usageLoading: "正在读取额度…",
73
+ usageEmpty: "当前账户没有返回可显示的额度窗口。请稍后刷新;这不代表额度为零。",
74
+ usageUpdated: "更新于 {value}",
75
+ remaining: "剩余 {value}%",
76
+ windowFiveHours: "5 小时额度",
77
+ windowDaily: "每日额度",
78
+ windowWeekly: "每周额度",
79
+ windowMonthly: "每月额度",
80
+ windowAnnual: "年度额度",
81
+ windowHours: "{value} 小时额度",
82
+ windowDays: "{value} 天额度",
83
+ resets: "重置于 {value}",
84
+ resetUnknown: "重置时间未提供",
85
+ creditsBalance: "额外 Credits 余额",
86
+ creditsUnit: "credits",
87
+ unlimited: "不限额",
88
+ monthlyCreditLimit: "Credits 月度消费上限",
89
+ resetCredits: "可用额度重置次数",
90
+ resetCreditsValue: "{count} 次",
91
+ creditsNote: "仅显示 Codex 为此账户或工作区实际返回的额外 Credits、消费上限或额度重置次数;三者不是同一项。",
92
+ creditsUsed: "已用 {used} / {limit} credits",
93
+ spendReached: "Credits 月度消费上限已用尽。",
94
+ unavailable: "暂无数据",
95
+ quickQuotaSetting: "输入框额度",
96
+ quickQuotaBeta: "Beta",
97
+ quickQuotaStatus: "Codex 剩余额度 {value}%"
98
+ };
99
+ const en = {
100
+ nav: "Codex",
101
+ title: "Codex subscription",
102
+ connected: "Signed in",
103
+ disconnected: "Not signed in",
104
+ accountLoading: "Reading account status…",
105
+ browserLogin: "Browser sign-in",
106
+ deviceLogin: "Device-code sign-in",
107
+ logout: "Sign out",
108
+ cancel: "Cancel",
109
+ submit: "Submit authorization code",
110
+ openLogin: "Open sign-in page",
111
+ manualCode: "If the browser callback did not finish automatically, paste the code or full redirect URL.",
112
+ deviceHint: "Enter this device code on the sign-in page:",
113
+ waiting: "Waiting for sign-in to finish…",
114
+ failed: "Sign-in failed. Try again.",
115
+ loadFailed: "Could not read Codex state.",
116
+ searchTitle: "Search source",
117
+ searchDsh: "DSH default",
118
+ searchDshHint: "Current search service",
119
+ searchCodex: "Codex subscription",
120
+ searchCodexHint: "ChatGPT subscription search",
121
+ preferenceFailed: "Could not update the setting. Try again.",
122
+ usage: "Subscription quota",
123
+ refresh: "Refresh",
124
+ refreshing: "Refreshing…",
125
+ noUsage: "Sign in to read quota windows reported by ChatGPT.",
126
+ usageLoading: "Reading quota…",
127
+ usageEmpty: "This account returned no displayable quota windows. Refresh later; this does not mean zero quota.",
128
+ usageUpdated: "Updated {value}",
129
+ remaining: "{value}% remaining",
130
+ windowFiveHours: "5-hour quota",
131
+ windowDaily: "Daily quota",
132
+ windowWeekly: "Weekly quota",
133
+ windowMonthly: "Monthly quota",
134
+ windowAnnual: "Annual quota",
135
+ windowHours: "{value}-hour quota",
136
+ windowDays: "{value}-day quota",
137
+ resets: "Resets {value}",
138
+ resetUnknown: "Reset time not provided",
139
+ creditsBalance: "Extra Credits balance",
140
+ creditsUnit: "credits",
141
+ unlimited: "Unlimited",
142
+ monthlyCreditLimit: "Monthly Credits spending cap",
143
+ resetCredits: "Available quota resets",
144
+ resetCreditsValue: "{count} available",
145
+ creditsNote: "Shows only extra Credits, spending caps, or quota resets returned for this account or workspace; these are separate items.",
146
+ creditsUsed: "{used} / {limit} credits used",
147
+ spendReached: "The monthly Credits spending cap has been reached.",
148
+ unavailable: "No data yet",
149
+ quickQuotaSetting: "Composer quota",
150
+ quickQuotaBeta: "Beta",
151
+ quickQuotaStatus: "Codex quota: {value}% remaining"
152
+ };
153
+ const STYLE = `
154
+ .codexSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
155
+ .codexSubscription h2,.codexSubscription h3,.codexSubscription p{margin:0}.codexSubscriptionHead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
156
+ .codexSubscription h2{font-size:16px;line-height:24px;font-weight:500}.codexSubscription h3{font-size:14px;line-height:22px;font-weight:500}
157
+ .codexSubscriptionTag{border:1px solid var(--dsw-alias-border-l3);border-radius:4px;padding:1px 6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary)}
158
+ .codexSubscriptionNote{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
159
+ .codexSubscriptionCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:14px 16px;display:flex;flex-direction:column;gap:12px}
160
+ .codexSubscriptionUsageCard{padding:12px 14px;gap:9px}.codexSubscriptionPreferencesCard{padding:12px 14px;gap:10px}.codexSubscriptionPreference{min-height:32px;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}.codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}
161
+ .codexSubscriptionSwitch{position:relative;flex:0 0 auto;width:32px;height:18px;padding:0;border:1px solid var(--dsw-alias-border-l3);border-radius:999px;background:var(--dsw-alias-bg-module-platform);cursor:pointer}.codexSubscriptionSwitch:disabled{cursor:not-allowed;opacity:.5}.codexSubscriptionSwitch[aria-checked=true]{background:var(--dsw-alias-label-secondary);border-color:var(--dsw-alias-label-secondary)}.codexSubscriptionSwitchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--dsw-alias-bg-layer-1);transition:transform 120ms var(--ds-ease-in-out)}.codexSubscriptionSwitch[aria-checked=true] .codexSubscriptionSwitchKnob{transform:translateX(14px)}
162
+ .codexSubscriptionSearch{display:flex;flex-direction:column;gap:7px}.codexSubscriptionSearchChoices{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}.codexSubscriptionSearchChoice{display:grid;grid-template-columns:14px minmax(0,1fr);align-items:center;column-gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);padding:9px 10px;text-align:left;cursor:pointer}.codexSubscriptionSearchChoice:has(input:disabled){cursor:not-allowed;opacity:.5}.codexSubscriptionSearchChoice:has(input:checked){border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.codexSubscriptionSearchChoice:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}.codexSubscriptionSearchInput{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-label-primary);cursor:inherit}.codexSubscriptionSearchCopy{display:block;min-width:0;pointer-events:none}.codexSubscriptionSearchCopy strong,.codexSubscriptionSearchCopy span{display:block}.codexSubscriptionSearchCopy strong{font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-secondary)}.codexSubscriptionSearchChoice:has(input:checked) strong{color:var(--dsw-alias-label-primary)}.codexSubscriptionSearchCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionDivider{height:1px;background:var(--dsw-alias-border-l2)}
163
+ .codexSubscriptionAccountRow,.codexSubscriptionSectionHead{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionStatus{display:flex;align-items:center;gap:8px;font-size:14px;line-height:22px;font-weight:500}
164
+ .codexSubscriptionDot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-dimmed)}.codexSubscriptionDot[data-state=connected]{background:var(--dsw-alias-state-success-primary)}.codexSubscriptionDot[data-state=disconnected]{background:var(--dsw-alias-state-error-primary)}
165
+ .codexSubscriptionActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.codexSubscriptionFlow{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-module-platform)}
166
+ .codexSubscriptionFlow p{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCode{width:max-content;max-width:100%;font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;overflow-wrap:anywhere}
167
+ .codexSubscriptionError{font-size:13px;line-height:20px;color:var(--dsw-alias-state-error-primary)}.codexSubscriptionInput{width:100%;box-sizing:border-box}
168
+ .codexSubscriptionSectionTitle{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionFreshness{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
169
+ .codexSubscriptionRefresh{flex:0 0 auto;min-width:72px;width:max-content;white-space:nowrap!important;word-break:keep-all!important;overflow-wrap:normal!important;writing-mode:horizontal-tb!important}.codexSubscriptionRefresh *{white-space:nowrap!important;word-break:keep-all!important;writing-mode:horizontal-tb!important}
170
+ .codexSubscriptionEmpty{padding:18px;border:1px dashed var(--dsw-alias-border-l3);border-radius:10px;text-align:center;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
171
+ .codexSubscriptionLimits{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:6px}.codexSubscriptionLimit{min-width:0;border-radius:10px;padding:9px 12px;background:var(--dsw-alias-bg-module-platform);display:flex;flex-direction:column;gap:6px}
172
+ .codexSubscriptionLimitTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionLimitLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionLimit strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}
173
+ .codexSubscriptionLimit progress{width:100%;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-brand-primary,#3964fe);-webkit-appearance:none;appearance:none}
174
+ .codexSubscriptionLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionLimitMeta{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
175
+ .codexSubscriptionCreditSection{display:flex;flex-direction:column;gap:7px}.codexSubscriptionCreditNote{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionCreditRows{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px}.codexSubscriptionCreditBalance,.codexSubscriptionSpendLimit{min-width:0;border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-module-platform)}
176
+ .codexSubscriptionCreditBalance{display:flex;flex-direction:column;gap:6px}.codexSubscriptionCreditBalance span,.codexSubscriptionCreditLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCreditBalance strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
177
+ .codexSubscriptionSpendLimit{display:flex;flex-direction:column;gap:8px}.codexSubscriptionSpendTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionSpendTop strong{font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}.codexSubscriptionSpendLimit progress{width:100%;height:6px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-brand-primary,#3964fe);-webkit-appearance:none;appearance:none}.codexSubscriptionSpendLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionSpendLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionSpendLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
178
+ .codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;margin-right:-4px;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}
179
+ @container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
180
+ @container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
181
+ @media(max-width:640px){.codexSubscriptionCard{padding:14px}}
182
+ `;
183
+ const unwrap = (response) => {
184
+ if (!response?.ok) throw new Error(response?.error?.message ?? "Codex RPC failed");
185
+ return response.value;
186
+ };
187
+ const fill = (text, values) => Object.entries(values).reduce((next, [key, value]) => next.replace(`{${key}}`, String(value)), text);
188
+ const hours = (seconds) => Math.round(seconds / 3600 * 10) / 10;
189
+ const percent = (value) => Number(value).toLocaleString(void 0, { maximumFractionDigits: 1 });
190
+ const isApproximateWindow = (seconds, expected) => seconds >= expected * .95 && seconds <= expected * 1.05;
191
+ const windowLabel = (seconds, t) => {
192
+ if (isApproximateWindow(seconds, 18e3)) return t("windowFiveHours");
193
+ if (isApproximateWindow(seconds, 86400)) return t("windowDaily");
194
+ if (isApproximateWindow(seconds, 604800)) return t("windowWeekly");
195
+ if (isApproximateWindow(seconds, 2592e3)) return t("windowMonthly");
196
+ if (isApproximateWindow(seconds, 31536e3)) return t("windowAnnual");
197
+ return seconds >= 86400 && seconds % 86400 === 0 ? fill(t("windowDays"), { value: seconds / 86400 }) : fill(t("windowHours"), { value: hours(seconds) });
198
+ };
199
+ const validDate = (value) => {
200
+ const date = new Date(value);
201
+ return Number.isFinite(date.getTime()) ? date : void 0;
202
+ };
203
+ function createPreferenceController(rpc) {
204
+ let snapshot = Object.freeze({
205
+ status: "loading",
206
+ visible: false,
207
+ searchProvider: "dsh",
208
+ writable: false,
209
+ error: false
210
+ });
211
+ let generation = 0;
212
+ const listeners = /* @__PURE__ */ new Set();
213
+ const publish = (next) => {
214
+ snapshot = Object.freeze(next);
215
+ for (const listener of listeners) listener();
216
+ };
217
+ const accept = (value) => publish({
218
+ status: "ready",
219
+ visible: value?.[QUICK_QUOTA_FIELD] === true,
220
+ searchProvider: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
221
+ writable: value?.writable === true,
222
+ error: false
223
+ });
224
+ const load = async () => {
225
+ const current = ++generation;
226
+ try {
227
+ const value = unwrap(await rpc.call(CHANNEL, "preferences/status", {}));
228
+ if (current === generation) accept(value);
229
+ } catch {
230
+ if (current === generation) publish({
231
+ status: "unavailable",
232
+ visible: false,
233
+ searchProvider: "dsh",
234
+ writable: false,
235
+ error: false
236
+ });
237
+ }
238
+ };
239
+ const set = async (patch) => {
240
+ if (snapshot.status !== "ready" || snapshot.writable !== true) return;
241
+ const previous = snapshot;
242
+ const current = ++generation;
243
+ publish({
244
+ status: "updating",
245
+ visible: patch["quickQuotaVisible"] ?? snapshot.visible,
246
+ searchProvider: patch["searchProvider"] ?? snapshot.searchProvider,
247
+ writable: false,
248
+ error: false
249
+ });
250
+ try {
251
+ const value = unwrap(await rpc.call(CHANNEL, "preferences/update", patch));
252
+ if (current === generation) accept(value);
253
+ } catch {
254
+ if (current === generation) publish({
255
+ ...previous,
256
+ error: true
257
+ });
258
+ }
259
+ };
260
+ return {
261
+ getSnapshot: () => snapshot,
262
+ subscribe: (listener) => {
263
+ listeners.add(listener);
264
+ return () => listeners.delete(listener);
265
+ },
266
+ load,
267
+ set
268
+ };
269
+ }
270
+ const usePreferenceSnapshot = (preference) => (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot);
271
+ const notifyQuickQuota = () => window.dispatchEvent(new Event(QUICK_QUOTA_REFRESH_EVENT));
272
+ function useQuickQuota(rpc, enabled, model) {
273
+ const [quota, setQuota] = (0, react.useState)();
274
+ (0, react.useEffect)(() => {
275
+ if (!enabled) {
276
+ setQuota(void 0);
277
+ return;
278
+ }
279
+ let live = true;
280
+ let loading = false;
281
+ const load = async () => {
282
+ if (loading) return;
283
+ loading = true;
284
+ try {
285
+ const account = unwrap(await rpc.call(CHANNEL, "status", {}));
286
+ if (!live) return;
287
+ if (account?.authenticated !== true) {
288
+ setQuota(void 0);
289
+ return;
290
+ }
291
+ const usage = unwrap(await rpc.call(CHANNEL, "usage", { force: false }));
292
+ if (live) setQuota(selectModelQuota(usage, model));
293
+ } catch {
294
+ if (live) setQuota(void 0);
295
+ } finally {
296
+ loading = false;
297
+ }
298
+ };
299
+ const refresh = () => {
300
+ load();
301
+ };
302
+ load();
303
+ const timer = window.setInterval(refresh, QUICK_QUOTA_REFRESH_MS);
304
+ window.addEventListener(QUICK_QUOTA_REFRESH_EVENT, refresh);
305
+ return () => {
306
+ live = false;
307
+ window.clearInterval(timer);
308
+ window.removeEventListener(QUICK_QUOTA_REFRESH_EVENT, refresh);
309
+ };
310
+ }, [
311
+ rpc,
312
+ enabled,
313
+ model
314
+ ]);
315
+ return quota;
316
+ }
317
+ function QuickQuotaPreference({ preference, t }) {
318
+ const snapshot = usePreferenceSnapshot(preference);
319
+ const visible = snapshot.visible;
320
+ const writable = snapshot.status === "ready" && snapshot.writable === true;
321
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
322
+ className: "codexSubscriptionPreference",
323
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
324
+ className: "codexSubscriptionPreferenceCopy",
325
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
326
+ className: "codexSubscriptionPreferenceLabel",
327
+ children: [t("quickQuotaSetting"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
328
+ className: "codexSubscriptionTag",
329
+ children: t("quickQuotaBeta")
330
+ })]
331
+ })
332
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
333
+ className: "codexSubscriptionSwitch",
334
+ type: "button",
335
+ role: "switch",
336
+ "aria-checked": visible,
337
+ "aria-label": t("quickQuotaSetting"),
338
+ disabled: !writable,
339
+ onClick: () => {
340
+ preference.set({ [QUICK_QUOTA_FIELD]: !visible });
341
+ },
342
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
343
+ className: "codexSubscriptionSwitchKnob",
344
+ "aria-hidden": "true"
345
+ })
346
+ })]
347
+ });
348
+ }
349
+ function SearchProviderPreference({ preference, t }) {
350
+ const snapshot = usePreferenceSnapshot(preference);
351
+ const writable = snapshot.status === "ready" && snapshot.writable === true;
352
+ const choice = (value, label, hint) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
353
+ className: "codexSubscriptionSearchChoice",
354
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
355
+ className: "codexSubscriptionSearchInput",
356
+ type: "radio",
357
+ name: "codex-subscription-search-provider",
358
+ checked: snapshot.searchProvider === value,
359
+ disabled: !writable,
360
+ onChange: () => {
361
+ preference.set({ [SEARCH_PROVIDER_FIELD]: value });
362
+ }
363
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
364
+ className: "codexSubscriptionSearchCopy",
365
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: hint })]
366
+ })]
367
+ });
368
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
369
+ className: "codexSubscriptionSearch",
370
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("searchTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
371
+ className: "codexSubscriptionSearchChoices",
372
+ role: "radiogroup",
373
+ "aria-label": t("searchTitle"),
374
+ children: [choice("dsh", t("searchDsh"), t("searchDshHint")), choice(SEARCH_PROVIDER_CODEX, t("searchCodex"), t("searchCodexHint"))]
375
+ })]
376
+ });
377
+ }
378
+ function PreferencesCard({ preference, t }) {
379
+ const snapshot = usePreferenceSnapshot(preference);
380
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
381
+ className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
382
+ children: [
383
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchProviderPreference, {
384
+ preference,
385
+ t
386
+ }),
387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "codexSubscriptionDivider" }),
388
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickQuotaPreference, {
389
+ preference,
390
+ t
391
+ }),
392
+ snapshot.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
393
+ className: "codexSubscriptionError",
394
+ role: "alert",
395
+ children: t("preferenceFailed")
396
+ }) : null
397
+ ]
398
+ });
399
+ }
400
+ function CodexComposerQuota({ preference, rpc, t, directory }) {
401
+ const preferenceSnapshot = usePreferenceSnapshot(preference);
402
+ const current = (0, react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot()).current;
403
+ const enabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.visible && current?.provider === "openai-codex";
404
+ const quota = useQuickQuota(rpc, enabled, current?.model);
405
+ if (!enabled || quota === void 0) return null;
406
+ const value = percent(quota.remainingPercent);
407
+ const label = fill(t("quickQuotaStatus"), { value });
408
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
409
+ className: "codexComposerQuota",
410
+ role: "status",
411
+ "aria-label": label,
412
+ title: label,
413
+ children: [value, "%"]
414
+ });
415
+ }
416
+ function AccountCard({ rpc, t, account, setAccount, onSignedOut }) {
417
+ const [flow, setFlow] = (0, react.useState)();
418
+ const [manualCode, setManualCode] = (0, react.useState)("");
419
+ const [busy, setBusy] = (0, react.useState)(false);
420
+ const [error, setError] = (0, react.useState)();
421
+ const call = (endpoint, payload = {}) => rpc.call(CHANNEL, endpoint, payload).then(unwrap);
422
+ (0, react.useEffect)(() => {
423
+ if (flow?.id === void 0 || [
424
+ "authenticated",
425
+ "failed",
426
+ "cancelled"
427
+ ].includes(flow.phase)) return void 0;
428
+ const timer = window.setInterval(() => {
429
+ call("login/status", { id: flow.id }).then((next) => {
430
+ setFlow(next);
431
+ if (next.phase === "authenticated") call("status").then((account) => {
432
+ setAccount(account);
433
+ notifyQuickQuota();
434
+ });
435
+ }).catch(() => setError(t("failed")));
436
+ }, 800);
437
+ return () => window.clearInterval(timer);
438
+ }, [flow?.id, flow?.phase]);
439
+ const begin = (method) => {
440
+ setBusy(true);
441
+ setError(void 0);
442
+ call("login/start", {
443
+ method,
444
+ openExternal: true
445
+ }).then(setFlow).catch(() => setError(t("failed"))).finally(() => setBusy(false));
446
+ };
447
+ const cancel = () => {
448
+ if (flow?.id === void 0) return;
449
+ setBusy(true);
450
+ call("login/cancel", { id: flow.id }).then(setFlow).finally(() => setBusy(false));
451
+ };
452
+ const submit = (event) => {
453
+ event.preventDefault();
454
+ if (flow?.id === void 0 || manualCode.trim() === "") return;
455
+ setBusy(true);
456
+ call("login/submit", {
457
+ id: flow.id,
458
+ value: manualCode.trim()
459
+ }).then((next) => {
460
+ setManualCode("");
461
+ setFlow(next);
462
+ }).catch(() => setError(t("failed"))).finally(() => setBusy(false));
463
+ };
464
+ const logout = () => {
465
+ setBusy(true);
466
+ setError(void 0);
467
+ call("logout").then((next) => {
468
+ setAccount(next);
469
+ setFlow(void 0);
470
+ onSignedOut();
471
+ notifyQuickQuota();
472
+ }).catch(() => setError(t("failed"))).finally(() => setBusy(false));
473
+ };
474
+ const signedIn = account?.authenticated === true;
475
+ const accountReady = account !== void 0;
476
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
477
+ className: "codexSubscriptionCard",
478
+ children: [
479
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
480
+ className: "codexSubscriptionAccountRow",
481
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
482
+ className: "codexSubscriptionStatus",
483
+ role: "status",
484
+ "aria-live": "polite",
485
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
486
+ className: "codexSubscriptionDot",
487
+ "data-state": accountReady ? signedIn ? "connected" : "disconnected" : "loading",
488
+ "aria-hidden": "true"
489
+ }), accountReady ? signedIn ? t("connected") : t("disconnected") : t("accountLoading")]
490
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
491
+ className: "codexSubscriptionActions",
492
+ children: signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
493
+ type: "button",
494
+ variant: "outline",
495
+ disabled: busy,
496
+ onClick: logout,
497
+ children: t("logout")
498
+ }) : accountReady && (flow === void 0 || ["failed", "cancelled"].includes(flow.phase)) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
499
+ type: "button",
500
+ variant: "primary",
501
+ disabled: busy,
502
+ onClick: () => begin("browser"),
503
+ children: t("browserLogin")
504
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
505
+ type: "button",
506
+ variant: "outline",
507
+ disabled: busy,
508
+ onClick: () => begin("device_code"),
509
+ children: t("deviceLogin")
510
+ })] }) : null
511
+ })]
512
+ }),
513
+ !signedIn && flow?.phase === "waiting_device" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
514
+ className: "codexSubscriptionFlow",
515
+ children: [
516
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("deviceHint") }),
517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
518
+ className: "codexSubscriptionCode",
519
+ children: flow.deviceCode?.userCode
520
+ }),
521
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
522
+ href: flow.deviceCode?.verificationUri,
523
+ target: "_blank",
524
+ rel: "noreferrer",
525
+ children: t("openLogin")
526
+ }),
527
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("waiting") })
528
+ ]
529
+ }) : null,
530
+ !signedIn && flow?.phase === "waiting_input" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
531
+ className: "codexSubscriptionFlow",
532
+ onSubmit: submit,
533
+ children: [
534
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("manualCode") }),
535
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
536
+ className: "codexSubscriptionInput",
537
+ value: manualCode,
538
+ onChange: (event) => setManualCode(event.currentTarget.value),
539
+ autoComplete: "off",
540
+ spellCheck: false
541
+ }),
542
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
543
+ className: "codexSubscriptionActions",
544
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
545
+ type: "submit",
546
+ variant: "primary",
547
+ disabled: busy || manualCode.trim() === "",
548
+ children: t("submit")
549
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
550
+ type: "button",
551
+ variant: "outline",
552
+ disabled: busy,
553
+ onClick: cancel,
554
+ children: t("cancel")
555
+ })]
556
+ })
557
+ ]
558
+ }) : null,
559
+ !signedIn && flow !== void 0 && ["starting", "waiting_browser"].includes(flow.phase) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
560
+ className: "codexSubscriptionFlow",
561
+ children: [
562
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("waiting") }),
563
+ flow.authUrl === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
564
+ href: flow.authUrl,
565
+ target: "_blank",
566
+ rel: "noreferrer",
567
+ children: t("openLogin")
568
+ }),
569
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
570
+ type: "button",
571
+ variant: "outline",
572
+ disabled: busy,
573
+ onClick: cancel,
574
+ children: t("cancel")
575
+ })
576
+ ]
577
+ }) : null,
578
+ flow?.phase === "failed" || error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
579
+ className: "codexSubscriptionError",
580
+ role: "alert",
581
+ children: error ?? t("failed")
582
+ }) : null
583
+ ]
584
+ });
585
+ }
586
+ function ResetTime({ resetsAt, t }) {
587
+ const date = Number.isSafeInteger(resetsAt) ? validDate(resetsAt * 1e3) : void 0;
588
+ if (date === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetUnknown") });
589
+ const value = date.toLocaleString(void 0, {
590
+ month: "numeric",
591
+ day: "numeric",
592
+ hour: "2-digit",
593
+ minute: "2-digit"
594
+ });
595
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
596
+ dateTime: date.toISOString(),
597
+ title: date.toLocaleString(),
598
+ children: fill(t("resets"), { value })
599
+ });
600
+ }
601
+ function UsageCard({ rpc, t, signedIn, resetKey }) {
602
+ const [usage, setUsage] = (0, react.useState)();
603
+ const [busy, setBusy] = (0, react.useState)(false);
604
+ const [error, setError] = (0, react.useState)();
605
+ const request = (0, react.useRef)(0);
606
+ const load = (force) => {
607
+ if (!signedIn) return;
608
+ const id = ++request.current;
609
+ setBusy(true);
610
+ setError(void 0);
611
+ rpc.call(CHANNEL, "usage", { force }).then(unwrap).then((next) => {
612
+ if (request.current === id) {
613
+ setUsage(next);
614
+ if (force) notifyQuickQuota();
615
+ }
616
+ }).catch((error) => {
617
+ if (request.current === id) setError(error.message);
618
+ }).finally(() => {
619
+ if (request.current === id) setBusy(false);
620
+ });
621
+ };
622
+ (0, react.useEffect)(() => {
623
+ if (signedIn) load(false);
624
+ else {
625
+ request.current += 1;
626
+ setUsage(void 0);
627
+ setError(void 0);
628
+ setBusy(false);
629
+ }
630
+ return () => {
631
+ request.current += 1;
632
+ };
633
+ }, [signedIn, resetKey]);
634
+ const visibleUsage = signedIn ? usage : void 0;
635
+ const limits = visibleUsage?.rateLimits ?? [];
636
+ const hasUsageDetails = limits.length > 0 || visibleUsage?.credits !== void 0 || visibleUsage?.individualLimit !== void 0 || visibleUsage?.resetCredits?.availableCount > 0;
637
+ const fetchedAt = typeof visibleUsage?.fetchedAt === "number" ? validDate(visibleUsage.fetchedAt) : void 0;
638
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
639
+ className: "codexSubscriptionCard codexSubscriptionUsageCard",
640
+ children: [
641
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
642
+ className: "codexSubscriptionSectionHead",
643
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
644
+ className: "codexSubscriptionSectionTitle",
645
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("usage") }), fetchedAt === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
646
+ className: "codexSubscriptionFreshness",
647
+ dateTime: fetchedAt.toISOString(),
648
+ children: fill(t("usageUpdated"), { value: fetchedAt.toLocaleString() })
649
+ })]
650
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
651
+ className: "codexSubscriptionRefresh",
652
+ type: "button",
653
+ variant: "outline",
654
+ disabled: !signedIn || busy,
655
+ "aria-busy": busy,
656
+ onClick: () => load(true),
657
+ children: busy ? t("refreshing") : t("refresh")
658
+ })]
659
+ }),
660
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
661
+ "aria-live": "polite",
662
+ children: [
663
+ !signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
664
+ className: "codexSubscriptionEmpty",
665
+ children: t("noUsage")
666
+ }) : null,
667
+ signedIn && busy && usage === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
668
+ className: "codexSubscriptionEmpty",
669
+ role: "status",
670
+ children: t("usageLoading")
671
+ }) : null,
672
+ signedIn && !busy && error === void 0 && usage !== void 0 && !hasUsageDetails ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
673
+ className: "codexSubscriptionEmpty",
674
+ role: "status",
675
+ children: t("usageEmpty")
676
+ }) : null
677
+ ]
678
+ }),
679
+ error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
680
+ className: "codexSubscriptionError",
681
+ role: "alert",
682
+ children: error
683
+ }),
684
+ visibleUsage?.spendControlReached === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
685
+ className: "codexSubscriptionError",
686
+ role: "alert",
687
+ children: t("spendReached")
688
+ }) : null,
689
+ limits.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
690
+ className: "codexSubscriptionLimits",
691
+ children: limits.flatMap((limit) => limit.windows.map((window, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
692
+ className: "codexSubscriptionLimit",
693
+ children: [
694
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
695
+ className: "codexSubscriptionLimitTop",
696
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
697
+ className: "codexSubscriptionLimitLabel",
698
+ children: limit.name ?? limit.id
699
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [percent(window.remainingPercent), "%"] })]
700
+ }),
701
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
702
+ max: "100",
703
+ value: window.remainingPercent,
704
+ "aria-label": `${limit.name ?? limit.id} ${fill(t("remaining"), { value: percent(window.remainingPercent) })}`
705
+ }),
706
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
707
+ className: "codexSubscriptionLimitMeta",
708
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: windowLabel(window.windowSeconds, t) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetTime, {
709
+ resetsAt: window.resetsAt,
710
+ t
711
+ })]
712
+ })
713
+ ]
714
+ }, `${limit.id}-${window.windowSeconds}-${index}`)))
715
+ }),
716
+ visibleUsage?.credits === void 0 && visibleUsage?.individualLimit === void 0 && !(visibleUsage?.resetCredits?.availableCount > 0) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
717
+ className: "codexSubscriptionCreditSection",
718
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
719
+ className: "codexSubscriptionCreditNote",
720
+ children: t("creditsNote")
721
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
722
+ className: "codexSubscriptionCreditRows",
723
+ children: [
724
+ visibleUsage?.credits ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
725
+ className: "codexSubscriptionCreditBalance",
726
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("creditsBalance") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: visibleUsage.credits.unlimited ? t("unlimited") : `${visibleUsage.credits.balance ?? t("unavailable")} ${t("creditsUnit")}` })]
727
+ }) : null,
728
+ visibleUsage?.resetCredits?.availableCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
729
+ className: "codexSubscriptionCreditBalance",
730
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetCredits") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: fill(t("resetCreditsValue"), { count: visibleUsage.resetCredits.availableCount }) })]
731
+ }) : null,
732
+ visibleUsage?.individualLimit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
733
+ className: "codexSubscriptionSpendLimit",
734
+ children: [
735
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
736
+ className: "codexSubscriptionSpendTop",
737
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
738
+ className: "codexSubscriptionCreditLabel",
739
+ children: t("monthlyCreditLimit")
740
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: fill(t("remaining"), { value: percent(visibleUsage.individualLimit.remainingPercent) }) })]
741
+ }),
742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
743
+ max: "100",
744
+ value: visibleUsage.individualLimit.remainingPercent,
745
+ "aria-label": `${t("monthlyCreditLimit")} ${fill(t("remaining"), { value: percent(visibleUsage.individualLimit.remainingPercent) })}`
746
+ }),
747
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
748
+ className: "codexSubscriptionLimitMeta",
749
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("creditsUsed"), {
750
+ used: visibleUsage.individualLimit.used,
751
+ limit: visibleUsage.individualLimit.limit
752
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetTime, {
753
+ resetsAt: visibleUsage.individualLimit.resetsAt,
754
+ t
755
+ })]
756
+ })
757
+ ]
758
+ }) : null
759
+ ]
760
+ })]
761
+ })
762
+ ]
763
+ });
764
+ }
765
+ function CodexSection({ preference, rpc, t }) {
766
+ const [account, setAccount] = (0, react.useState)();
767
+ const [error, setError] = (0, react.useState)();
768
+ const [resetKey, setResetKey] = (0, react.useState)(0);
769
+ (0, react.useEffect)(() => {
770
+ let live = true;
771
+ rpc.call(CHANNEL, "status", {}).then(unwrap).then((next) => {
772
+ if (live) setAccount(next);
773
+ }).catch(() => {
774
+ if (live) setError(t("loadFailed"));
775
+ });
776
+ return () => {
777
+ live = false;
778
+ };
779
+ }, []);
780
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
781
+ className: "codexSubscription",
782
+ children: [
783
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
784
+ className: "codexSubscriptionHead",
785
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("title") })
786
+ }),
787
+ error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
788
+ className: "codexSubscriptionError",
789
+ role: "alert",
790
+ children: error
791
+ }),
792
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
793
+ rpc,
794
+ t,
795
+ account,
796
+ setAccount,
797
+ onSignedOut: () => setResetKey((value) => value + 1)
798
+ }),
799
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferencesCard, {
800
+ preference,
801
+ t
802
+ }),
803
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageCard, {
804
+ rpc,
805
+ t,
806
+ signedIn: account?.authenticated === true,
807
+ resetKey
808
+ })
809
+ ]
810
+ });
811
+ }
812
+ function apply(ctx) {
813
+ ctx.effect(() => ctx.locale.register(NS, {
814
+ zh,
815
+ en
816
+ }), "codex-subscription: copy");
817
+ ctx.effect(() => {
818
+ const tag = document.createElement("style");
819
+ tag.dataset.plugin = "dsh-codex-subscription";
820
+ tag.textContent = STYLE;
821
+ document.head.append(tag);
822
+ return () => tag.remove();
823
+ }, "codex-subscription: style");
824
+ const connection = ctx.get("connection");
825
+ const preference = createPreferenceController(connection.rpc);
826
+ ctx.effect(() => {
827
+ preference.load();
828
+ return ctx.on("connection/reset", () => {
829
+ preference.load();
830
+ });
831
+ }, "codex-subscription: preferences");
832
+ const t = ctx.locale.bind(NS);
833
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
834
+ name: "settings.section",
835
+ id: "codex-subscription",
836
+ order: 15,
837
+ label: () => t("nav"),
838
+ locale: NS,
839
+ inject: () => ({
840
+ preference,
841
+ rpc: connection.rpc,
842
+ t
843
+ })
844
+ }, CodexSection));
845
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
846
+ name: "conversation.input.right",
847
+ id: "codex-subscription-quota",
848
+ order: 15,
849
+ locale: NS,
850
+ inject: (sessionId) => ({
851
+ preference,
852
+ rpc: connection.rpc,
853
+ t,
854
+ directory: ctx.modelDirectories.directoryFor(sessionId).store
855
+ })
856
+ }, CodexComposerQuota));
857
+ }
858
+ //#endregion
859
+ exports.apply = apply;
860
+ exports.inject = inject;
861
+ return module.exports;
862
+ }
863
+ });
864
+
865
+ //# sourceMappingURL=client.js.map