dsh-codex-subscription 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -50,7 +50,9 @@ window.__ModuleLoader__.load({
50
50
  //#endregion
51
51
  //#region src/settings-contract.js
52
52
  const SETTINGS_NAMESPACE = "codex-subscription";
53
- const QUICK_QUOTA_FIELD = "quickQuotaVisible";
53
+ const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
54
+ const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
55
+ const QUICK_QUOTA_MODE_PERCENT = "percent";
54
56
  const SEARCH_PROVIDER_FIELD = "searchProvider";
55
57
  const SEARCH_PROVIDER_CODEX = "codex";
56
58
  const SPEED_MODE_FIELD = "speedMode";
@@ -59,6 +61,11 @@ window.__ModuleLoader__.load({
59
61
  const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
60
62
  const normalizeSearchProvider = (value) => ["dsh", "codex"].includes(value) ? value : "dsh";
61
63
  const normalizeSpeedMode = (value) => ["standard", "fast"].includes(value) ? value : DEFAULT_SPEED_MODE;
64
+ const normalizeQuickQuotaMode = (value, legacyVisible = false) => [
65
+ "off",
66
+ "percent",
67
+ "bar"
68
+ ].includes(value) ? value : legacyVisible === true ? QUICK_QUOTA_MODE_PERCENT : "off";
62
69
  const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
63
70
  //#endregion
64
71
  //#region src/sidebar-quota.js
@@ -176,12 +183,36 @@ window.__ModuleLoader__.load({
176
183
  monthlyCreditLimit: "Credits 月度消费上限",
177
184
  resetCredits: "可用额度重置次数",
178
185
  resetCreditsValue: "{count} 次",
186
+ resetUse: "使用一次重置",
187
+ resetUnavailable: "仅当至少一个 Codex 模型额度已用尽时可使用。",
188
+ resetPreparing: "正在读取重置详情…",
189
+ resetConfirmTitle: "确认使用额度重置",
190
+ resetWarning: "此操作会立即消耗 1 次额度重置,且无法撤销。请确认你确实需要恢复已用尽的模型额度。",
191
+ resetAcknowledge: "我知道这会消耗 1 次额度重置",
192
+ resetTypeHint: "输入 {phrase} 以继续",
193
+ resetWait: "请再等待 {count} 秒",
194
+ resetFinal: "消耗 1 次并重置额度",
195
+ resetUsing: "正在重置…",
196
+ resetSuccess: "额度重置已完成。",
197
+ resetNothing: "当前没有可重置的额度,未消耗新的重置次数。",
198
+ resetNoCredit: "没有可用的额度重置。",
199
+ resetAlready: "这次重置请求已处理。",
200
+ resetFailed: "无法使用额度重置。",
201
+ resetRenewLogin: "登录状态已失效,请重新登录。",
202
+ resetExpired: "本次确认已失效,请重新开始。",
203
+ resetInProgress: "额度重置正在处理中。",
204
+ resetTooEarly: "请等待冷静期结束后再确认。",
205
+ resetPhraseMismatch: "确认短语不一致。",
206
+ resetNotExhausted: "当前 Codex 模型额度尚未用尽。",
207
+ resetAccountChanged: "登录账号已变更,请重新开始。",
179
208
  creditsNote: "仅显示 Codex 为此账户或工作区实际返回的额外 Credits、消费上限或额度重置次数;三者不是同一项。",
180
209
  creditsUsed: "已用 {used} / {limit} credits",
181
210
  spendReached: "Credits 月度消费上限已用尽。",
182
211
  unavailable: "暂无数据",
183
212
  quickQuotaSetting: "输入框额度",
184
- quickQuotaBeta: "Beta",
213
+ quickQuotaOff: "关闭",
214
+ quickQuotaPercent: "百分比",
215
+ quickQuotaBar: "进度条",
185
216
  quickQuotaStatus: "Codex 剩余额度 {value}%",
186
217
  speedTitle: "速度",
187
218
  speedStandard: "标准",
@@ -265,12 +296,36 @@ window.__ModuleLoader__.load({
265
296
  monthlyCreditLimit: "Monthly Credits spending cap",
266
297
  resetCredits: "Available quota resets",
267
298
  resetCreditsValue: "{count} available",
299
+ resetUse: "Use one reset",
300
+ resetUnavailable: "Available only when at least one Codex model quota is exhausted.",
301
+ resetPreparing: "Reading reset details…",
302
+ resetConfirmTitle: "Confirm quota reset",
303
+ resetWarning: "This immediately consumes one quota reset and cannot be undone. Continue only if you intend to restore an exhausted model quota.",
304
+ resetAcknowledge: "I understand this consumes one quota reset",
305
+ resetTypeHint: "Type {phrase} to continue",
306
+ resetWait: "Wait {count} more seconds",
307
+ resetFinal: "Consume one reset",
308
+ resetUsing: "Resetting…",
309
+ resetSuccess: "Quota reset completed.",
310
+ resetNothing: "There is currently nothing to reset; no new reset was consumed.",
311
+ resetNoCredit: "No quota reset is available.",
312
+ resetAlready: "This reset request was already processed.",
313
+ resetFailed: "Could not use the quota reset.",
314
+ resetRenewLogin: "Your sign-in expired. Sign in again.",
315
+ resetExpired: "This confirmation expired. Start again.",
316
+ resetInProgress: "A quota reset is already in progress.",
317
+ resetTooEarly: "Wait for the cooldown before confirming.",
318
+ resetPhraseMismatch: "The confirmation phrase does not match.",
319
+ resetNotExhausted: "The current Codex model quota is not exhausted.",
320
+ resetAccountChanged: "The signed-in account changed. Start again.",
268
321
  creditsNote: "Shows only extra Credits, spending caps, or quota resets returned for this account or workspace; these are separate items.",
269
322
  creditsUsed: "{used} / {limit} credits used",
270
323
  spendReached: "The monthly Credits spending cap has been reached.",
271
324
  unavailable: "No data yet",
272
325
  quickQuotaSetting: "Composer quota",
273
- quickQuotaBeta: "Beta",
326
+ quickQuotaOff: "Off",
327
+ quickQuotaPercent: "Percent",
328
+ quickQuotaBar: "Progress bar",
274
329
  quickQuotaStatus: "Codex quota: {value}% remaining",
275
330
  speedTitle: "Speed",
276
331
  speedStandard: "Standard",
@@ -308,6 +363,7 @@ window.__ModuleLoader__.load({
308
363
  .codexSubscriptionNote{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
309
364
  .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}
310
365
  .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}
366
+ .codexSubscriptionQuotaModes{display:flex;align-items:center;gap:3px;padding:2px;border-radius:9px;background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionQuotaMode{position:relative;display:flex;align-items:center;justify-content:center;min-height:26px;padding:0 9px;border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;cursor:pointer}.codexSubscriptionQuotaMode:has(input:checked){background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:0 0 0 1px var(--dsw-alias-border-l3)}.codexSubscriptionQuotaMode:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}.codexSubscriptionQuotaMode:has(input:disabled){cursor:not-allowed;opacity:.5}.codexSubscriptionQuotaMode input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
311
367
  .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)}
312
368
  .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)}
313
369
  .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}
@@ -326,8 +382,9 @@ window.__ModuleLoader__.load({
326
382
  .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)}
327
383
  .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)}
328
384
  .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}
385
+ .codexSubscriptionResetSummary{display:flex;align-items:center;justify-content:space-between;gap:10px}.codexSubscriptionResetBalance{display:flex;flex-direction:column;gap:8px}.codexSubscriptionResetBalance .codexSubscriptionActions{justify-content:flex-start}.codexSubscriptionResetFlow{display:flex;flex-direction:column;gap:9px;border:1px solid var(--dsw-alias-state-warning-border,var(--dsw-alias-border-l2));border-radius:10px;padding:11px 12px;background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionResetFlow h4{margin:0;font-size:13px;line-height:20px;font-weight:600}.codexSubscriptionResetWarning{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionResetCheck{display:flex;align-items:flex-start;gap:8px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);cursor:pointer}.codexSubscriptionResetCheck input{margin:3px 0 0;accent-color:var(--dsw-alias-label-primary)}.codexSubscriptionResetPhrase{font:600 12px/18px ui-monospace,SFMono-Regular,Consolas,monospace}.codexSubscriptionResetFinal{border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.codexSubscriptionResetResult{font-size:12px;line-height:18px;color:var(--dsw-alias-state-success-primary)}
329
386
  .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}
330
- .codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;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}
387
+ .codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;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}.codexComposerQuotaBar{display:block;width:40px;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-label-secondary);-webkit-appearance:none;appearance:none}.codexComposerQuotaBar::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexComposerQuotaBar::-webkit-progress-value{background:var(--dsw-alias-label-secondary);border-radius:999px}.codexComposerQuotaBar::-moz-progress-bar{background:var(--dsw-alias-label-secondary);border-radius:999px}
331
388
  .codexModelSelect{position:relative;min-width:0}.codexModelSelectTrigger{display:flex;align-items:center;gap:4px;min-width:0;max-width:min(360px,45cqw);height:28px;padding:0 4px 0 8px;border:0;border-radius:24px;outline:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:500;line-height:20px;cursor:pointer}.codexModelSelectTrigger:hover:not(:disabled),.codexModelSelectTrigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.codexModelSelectTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectBolt{display:block;flex:none;width:14px;height:14px;color:var(--dsw-alias-label-primary)}.codexModelSelectLabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectEffort{flex:none;color:var(--dsw-alias-label-caption)}.codexModelSelectChevron{flex:none;color:var(--dsw-alias-label-caption);transition:transform 120ms}.codexModelSelectTrigger[aria-expanded=true] .codexModelSelectChevron{transform:rotate(180deg)}
332
389
  .codexModelSelectMenu,.codexModelSelectSubmenu{position:absolute;z-index:30;box-sizing:border-box;width:max-content;min-width:min(240px,calc(100vw - 32px));max-width:min(420px,calc(100vw - 32px));max-height:min(360px,calc(100vh - 96px));padding:4px;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);overflow:hidden}.codexModelSelectMenu{right:0;bottom:calc(100% + 8px)}.codexModelSelectSubmenu{right:calc(100% + 8px);bottom:0;min-width:min(230px,calc(100vw - 32px))}.codexModelSelectCell{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;height:40px;box-sizing:border-box;padding:0 10px;border:0;border-radius:10px;background:transparent;color:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer}.codexModelSelectCell:hover,.codexModelSelectCell:focus-visible,.codexModelSelectCell[data-open=true]{background:var(--dsw-alias-interactive-bg-hover);outline:0}.codexModelSelectCell:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectCellLabel{flex:none;white-space:nowrap}.codexModelSelectCellValue{flex:auto;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary);text-align:right;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCellChevron{flex:none;color:var(--dsw-alias-label-tertiary)}.codexModelSelectGroups{min-height:0;max-height:352px;overflow-y:auto}.codexModelSelectGroup+.codexModelSelectGroup{margin-top:4px}.codexModelSelectGroupTitle{position:sticky;top:0;z-index:1;padding:5px 8px 3px;background:var(--dsw-specific-menu);color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:500;line-height:18px}.codexModelSelectOption{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;min-height:38px;box-sizing:border-box;padding:6px 8px;border:0;border-radius:10px;outline:0;background:transparent;color:inherit;text-align:left;cursor:pointer}.codexModelSelectOption:hover:not(:disabled),.codexModelSelectOption:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectOption:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectOptionCopy{display:flex;flex:1;min-width:0;flex-direction:column}.codexModelSelectOptionName{overflow:hidden;color:inherit;font-size:14px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectOptionDescription{overflow:hidden;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCheck{display:grid;place-items:center;flex:0 0 18px;color:var(--dsw-alias-label-primary)}.codexModelSelectStatus,.codexModelSelectEmpty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.codexModelSelectError,.codexModelSelectWarning{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:4px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.codexModelSelectWarning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}.codexModelSelectRetry{flex:none;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer}
333
390
  .codexModelSelectMenu{overflow:visible}
@@ -494,7 +551,7 @@ window.__ModuleLoader__.load({
494
551
  const current = native.status === "ready" ? native : fallbackStatus === "ready" ? fallback : native;
495
552
  return Object.freeze({
496
553
  status: updating ? "updating" : current.status,
497
- visible: current.value?.[QUICK_QUOTA_FIELD] === true,
554
+ quickQuotaMode: normalizeQuickQuotaMode(current.value?.[QUICK_QUOTA_MODE_FIELD], current.value?.[LEGACY_QUICK_QUOTA_FIELD]),
498
555
  searchProvider: normalizeSearchProvider(current.value?.[SEARCH_PROVIDER_FIELD]),
499
556
  speedMode: normalizeSpeedMode(current.value?.[SPEED_MODE_FIELD]),
500
557
  writable: !updating && current.status === "ready" && current.writable === true,
@@ -517,7 +574,7 @@ window.__ModuleLoader__.load({
517
574
  fallback = {
518
575
  status: "ready",
519
576
  value: {
520
- [QUICK_QUOTA_FIELD]: value?.[QUICK_QUOTA_FIELD] === true,
577
+ [QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
521
578
  [SEARCH_PROVIDER_FIELD]: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
522
579
  [SPEED_MODE_FIELD]: normalizeSpeedMode(value?.[SPEED_MODE_FIELD])
523
580
  },
@@ -638,33 +695,36 @@ window.__ModuleLoader__.load({
638
695
  }
639
696
  function QuickQuotaPreference({ preference, t }) {
640
697
  const snapshot = usePreferenceSnapshot(preference);
641
- const visible = snapshot.visible;
642
698
  const writable = snapshot.status === "ready" && snapshot.writable === true;
699
+ const choice = (value, label) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
700
+ className: "codexSubscriptionQuotaMode",
701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
702
+ type: "radio",
703
+ name: "codex-subscription-quota-mode",
704
+ checked: snapshot.quickQuotaMode === value,
705
+ disabled: !writable,
706
+ onChange: () => {
707
+ preference.set({ [QUICK_QUOTA_MODE_FIELD]: value });
708
+ }
709
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
710
+ });
643
711
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
644
712
  className: "codexSubscriptionPreference",
645
713
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
646
714
  className: "codexSubscriptionPreferenceCopy",
647
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
715
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
648
716
  className: "codexSubscriptionPreferenceLabel",
649
- children: [t("quickQuotaSetting"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
650
- className: "codexSubscriptionTag",
651
- children: t("quickQuotaBeta")
652
- })]
717
+ children: t("quickQuotaSetting")
653
718
  })
654
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
655
- className: "codexSubscriptionSwitch",
656
- type: "button",
657
- role: "switch",
658
- "aria-checked": visible,
719
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
720
+ className: "codexSubscriptionQuotaModes",
721
+ role: "radiogroup",
659
722
  "aria-label": t("quickQuotaSetting"),
660
- disabled: !writable,
661
- onClick: () => {
662
- preference.set({ [QUICK_QUOTA_FIELD]: !visible });
663
- },
664
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
665
- className: "codexSubscriptionSwitchKnob",
666
- "aria-hidden": "true"
667
- })
723
+ children: [
724
+ choice("off", t("quickQuotaOff")),
725
+ choice(QUICK_QUOTA_MODE_PERCENT, t("quickQuotaPercent")),
726
+ choice("bar", t("quickQuotaBar"))
727
+ ]
668
728
  })]
669
729
  });
670
730
  }
@@ -733,17 +793,23 @@ window.__ModuleLoader__.load({
733
793
  const preferenceSnapshot = usePreferenceSnapshot(preference);
734
794
  const current = (0, react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot()).current;
735
795
  const codex = current?.provider === "openai-codex";
736
- const quotaEnabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.visible && codex;
796
+ const quotaEnabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.quickQuotaMode !== "off" && codex;
737
797
  const quota = useQuickQuota(rpc, quotaEnabled, current?.model);
738
798
  if (!quotaEnabled || quota === void 0) return null;
739
- const value = percent(quota.remainingPercent);
740
- const label = fill(t("quickQuotaStatus"), { value });
741
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
799
+ const value = Math.round(Number(quota.remainingPercent) * 10) / 10;
800
+ const display = percent(value);
801
+ const label = fill(t("quickQuotaStatus"), { value: display });
802
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
742
803
  className: "codexComposerQuota",
743
804
  role: "status",
744
805
  "aria-label": label,
745
806
  title: label,
746
- children: [value, "%"]
807
+ children: preferenceSnapshot.quickQuotaMode === "bar" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
808
+ className: "codexComposerQuotaBar",
809
+ max: 100,
810
+ value,
811
+ "aria-hidden": "true"
812
+ }) : `${display}%`
747
813
  });
748
814
  }
749
815
  function CodexModelSelect({ locked, available, directory, load, select, preference, t }) {
@@ -1283,6 +1349,171 @@ window.__ModuleLoader__.load({
1283
1349
  children: fill(t("resets"), { value })
1284
1350
  });
1285
1351
  }
1352
+ function ResetCreditControl({ rpc, t, count, exhausted, onConsumed }) {
1353
+ const [challenge, setChallenge] = (0, react.useState)();
1354
+ const [resetBusy, setResetBusy] = (0, react.useState)(false);
1355
+ const [resetAcknowledged, setResetAcknowledged] = (0, react.useState)(false);
1356
+ const [resetPhrase, setResetPhrase] = (0, react.useState)("");
1357
+ const [resetCountdown, setResetCountdown] = (0, react.useState)(0);
1358
+ const [resetError, setResetError] = (0, react.useState)();
1359
+ const [resetResult, setResetResult] = (0, react.useState)();
1360
+ (0, react.useEffect)(() => {
1361
+ if (challenge === void 0) {
1362
+ setResetCountdown(0);
1363
+ return;
1364
+ }
1365
+ const update = () => setResetCountdown(Math.max(0, Math.ceil((challenge.readyAt - Date.now()) / 1e3)));
1366
+ update();
1367
+ const timer = window.setInterval(update, 250);
1368
+ return () => window.clearInterval(timer);
1369
+ }, [challenge]);
1370
+ const prepareReset = () => {
1371
+ if (resetBusy || !exhausted) return;
1372
+ setResetBusy(true);
1373
+ setResetError(void 0);
1374
+ setResetResult(void 0);
1375
+ rpc.call(CHANNEL, "reset-credit/prepare", {}).then(unwrap).then((next) => {
1376
+ setChallenge(next);
1377
+ setResetAcknowledged(false);
1378
+ setResetPhrase("");
1379
+ }).catch((error) => setResetError(resetCreditErrorText(error, t))).finally(() => setResetBusy(false));
1380
+ };
1381
+ const cancelReset = () => {
1382
+ if (resetBusy) return;
1383
+ setChallenge(void 0);
1384
+ setResetAcknowledged(false);
1385
+ setResetPhrase("");
1386
+ setResetError(void 0);
1387
+ };
1388
+ const resetReady = challenge !== void 0 && resetAcknowledged && resetPhrase === challenge.confirmPhrase && resetCountdown === 0;
1389
+ const consumeReset = () => {
1390
+ if (resetBusy) return;
1391
+ if (!resetReady) return;
1392
+ setResetBusy(true);
1393
+ setResetError(void 0);
1394
+ setResetResult(void 0);
1395
+ rpc.call(CHANNEL, "reset-credit/consume", {
1396
+ challengeId: challenge.challengeId,
1397
+ phrase: resetPhrase
1398
+ }).then(unwrap).then((result) => {
1399
+ setChallenge(void 0);
1400
+ setResetAcknowledged(false);
1401
+ setResetPhrase("");
1402
+ const message = result.code === "reset" ? t("resetSuccess") : result.code === "nothing_to_reset" ? t("resetNothing") : result.code === "no_credit" ? t("resetNoCredit") : t("resetAlready");
1403
+ setResetResult(message);
1404
+ onConsumed();
1405
+ }).catch((error) => setResetError(resetCreditErrorText(error, t))).finally(() => setResetBusy(false));
1406
+ };
1407
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1408
+ className: "codexSubscriptionResetBalance",
1409
+ children: [
1410
+ challenge === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1411
+ className: "codexSubscriptionResetSummary",
1412
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: fill(t("resetCreditsValue"), { count }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1413
+ className: "codexSubscriptionActions",
1414
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1415
+ type: "button",
1416
+ variant: "outline",
1417
+ disabled: !exhausted || resetBusy,
1418
+ "aria-busy": resetBusy,
1419
+ title: !exhausted ? t("resetUnavailable") : void 0,
1420
+ onClick: prepareReset,
1421
+ children: resetBusy ? t("resetPreparing") : t("resetUse")
1422
+ })
1423
+ })]
1424
+ }) }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1425
+ className: "codexSubscriptionResetFlow",
1426
+ role: "group",
1427
+ "aria-labelledby": "codex-reset-confirm-title",
1428
+ children: [
1429
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1430
+ id: "codex-reset-confirm-title",
1431
+ children: challenge.title ?? t("resetConfirmTitle")
1432
+ }),
1433
+ challenge.description ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1434
+ className: "codexSubscriptionResetWarning",
1435
+ children: challenge.description
1436
+ }) : null,
1437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1438
+ className: "codexSubscriptionResetWarning",
1439
+ children: t("resetWarning")
1440
+ }),
1441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1442
+ className: "codexSubscriptionResetCheck",
1443
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1444
+ type: "checkbox",
1445
+ checked: resetAcknowledged,
1446
+ disabled: resetBusy,
1447
+ onChange: (event) => setResetAcknowledged(event.target.checked)
1448
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetAcknowledge") })]
1449
+ }),
1450
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1451
+ className: "codexSubscriptionPreferenceCopy",
1452
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1453
+ className: "codexSubscriptionCreditLabel",
1454
+ children: fill(t("resetTypeHint"), { phrase: challenge.confirmPhrase })
1455
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1456
+ className: "codexSubscriptionInput codexSubscriptionResetPhrase",
1457
+ value: resetPhrase,
1458
+ disabled: resetBusy,
1459
+ autoComplete: "off",
1460
+ spellCheck: false,
1461
+ onChange: (event) => setResetPhrase(event.target.value)
1462
+ })]
1463
+ }),
1464
+ resetCountdown > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1465
+ className: "codexSubscriptionCreditNote",
1466
+ role: "status",
1467
+ children: fill(t("resetWait"), { count: resetCountdown })
1468
+ }) : null,
1469
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1470
+ className: "codexSubscriptionActions",
1471
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1472
+ type: "button",
1473
+ variant: "outline",
1474
+ disabled: resetBusy,
1475
+ onClick: cancelReset,
1476
+ children: t("cancel")
1477
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1478
+ className: "codexSubscriptionResetFinal",
1479
+ type: "button",
1480
+ variant: "outline",
1481
+ disabled: !resetReady || resetBusy,
1482
+ "aria-busy": resetBusy,
1483
+ onClick: consumeReset,
1484
+ children: resetBusy ? t("resetUsing") : t("resetFinal")
1485
+ })]
1486
+ })
1487
+ ]
1488
+ }),
1489
+ resetResult ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1490
+ className: "codexSubscriptionResetResult",
1491
+ role: "status",
1492
+ children: resetResult
1493
+ }) : null,
1494
+ resetError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1495
+ className: "codexSubscriptionError",
1496
+ role: "alert",
1497
+ children: resetError || t("resetFailed")
1498
+ }) : null
1499
+ ]
1500
+ });
1501
+ }
1502
+ function resetCreditErrorText(error, t) {
1503
+ return t((/* @__PURE__ */ new Map([
1504
+ ["ChatGPT subscription is not signed in", "resetRenewLogin"],
1505
+ ["ChatGPT sign-in needs to be renewed", "resetRenewLogin"],
1506
+ ["No quota reset is available", "resetNoCredit"],
1507
+ ["No usable quota reset is available", "resetNoCredit"],
1508
+ ["The available quota reset expires too soon", "resetExpired"],
1509
+ ["This quota reset confirmation is no longer valid", "resetExpired"],
1510
+ ["This quota reset is already in progress", "resetInProgress"],
1511
+ ["Wait before confirming this quota reset", "resetTooEarly"],
1512
+ ["The quota reset confirmation phrase does not match", "resetPhraseMismatch"],
1513
+ ["The current Codex quota is not exhausted", "resetNotExhausted"],
1514
+ ["The signed-in ChatGPT account changed", "resetAccountChanged"]
1515
+ ])).get(error instanceof Error ? error.message : "") ?? "resetFailed");
1516
+ }
1286
1517
  function UsageCard({ rpc, t, signedIn, resetKey }) {
1287
1518
  const [usage, setUsage] = (0, react.useState)();
1288
1519
  const [busy, setBusy] = (0, react.useState)(false);
@@ -1318,6 +1549,7 @@ window.__ModuleLoader__.load({
1318
1549
  }, [signedIn, resetKey]);
1319
1550
  const visibleUsage = signedIn ? usage : void 0;
1320
1551
  const limits = visibleUsage?.rateLimits ?? [];
1552
+ const exhausted = limits.some((limit) => limit.id !== "code_review" && limit.windows.some((window) => window.usedPercent >= 100));
1321
1553
  const hasUsageDetails = limits.length > 0 || visibleUsage?.credits !== void 0 || visibleUsage?.individualLimit !== void 0 || visibleUsage?.resetCredits?.availableCount > 0;
1322
1554
  const fetchedAt = typeof visibleUsage?.fetchedAt === "number" ? validDate(visibleUsage.fetchedAt) : void 0;
1323
1555
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1412,7 +1644,13 @@ window.__ModuleLoader__.load({
1412
1644
  }) : null,
1413
1645
  visibleUsage?.resetCredits?.availableCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1414
1646
  className: "codexSubscriptionCreditBalance",
1415
- 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 }) })]
1647
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetCredits") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetCreditControl, {
1648
+ rpc,
1649
+ t,
1650
+ count: visibleUsage.resetCredits.availableCount,
1651
+ exhausted,
1652
+ onConsumed: () => load(true)
1653
+ })]
1416
1654
  }) : null,
1417
1655
  visibleUsage?.individualLimit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1418
1656
  className: "codexSubscriptionSpendLimit",