dsh-provider-qoder 0.3.2 → 0.4.1

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
@@ -14,6 +14,112 @@ window.__ModuleLoader__.load({
14
14
  return source === "env" || source?.endsWith("-env") === true;
15
15
  }
16
16
  //#endregion
17
+ //#region src/qoder/catalog.ts
18
+ function positiveNumber(value) {
19
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
20
+ }
21
+ /**
22
+ * Provider-advertised context tiers of one model, smallest first. A tier without
23
+ * a usable capacity cannot be selected and is dropped.
24
+ */
25
+ function contextTiersOf(model) {
26
+ const options = model.contextOptions;
27
+ if (options === void 0) return [];
28
+ return Object.entries(options).flatMap(([key, value]) => {
29
+ const tokenCount = value.tokenCount;
30
+ return typeof tokenCount === "number" && Number.isFinite(tokenCount) && tokenCount > 0 ? [{
31
+ key,
32
+ tokenCount,
33
+ isDefault: value.isDefault === true
34
+ }] : [];
35
+ }).sort((left, right) => left.tokenCount - right.tokenCount);
36
+ }
37
+ /** Resolve a usable selection consistently for the composer and adapter. */
38
+ function resolveContextTier(model, manualTier, historicalWindow) {
39
+ const tiers = contextTiersOf(model);
40
+ return tiers.find((tier) => tier.key === manualTier) ?? tiers.find((tier) => tier.tokenCount === historicalWindow) ?? tiers.find((tier) => tier.key === model.contextTier) ?? tiers.find((tier) => tier.tokenCount === model.contextWindow) ?? (model.contextWindow === void 0 ? tiers.find((tier) => tier.isDefault) : void 0);
41
+ }
42
+ /** Compact capacity label, e.g. `200K` or `1M`. */
43
+ function formatContextTokens(tokenCount) {
44
+ if (tokenCount >= 1e6) {
45
+ const millions = tokenCount / 1e6;
46
+ return `${Number.isInteger(millions) ? millions : millions.toFixed(1)}M`;
47
+ }
48
+ if (tokenCount >= 1e3) {
49
+ const thousands = tokenCount / 1e3;
50
+ return `${Number.isInteger(thousands) ? thousands : thousands.toFixed(1)}K`;
51
+ }
52
+ return String(tokenCount);
53
+ }
54
+ /** Format a price factor number to string (e.g. 1 -> '1', 1.6 -> '1.6', 0.5 -> '0.5', 1.25 -> '1.25'). */
55
+ function formatRateFactor(value) {
56
+ const rounded = Number(value.toFixed(2));
57
+ return String(rounded);
58
+ }
59
+ /**
60
+ * The provider-advertised context tier the subscriber selected, when the
61
+ * provider still advertises that tier with a usable capacity.
62
+ */
63
+ function selectedContextTier(model) {
64
+ const key = model.contextTier;
65
+ if (typeof key !== "string" || key.length === 0) return void 0;
66
+ const tokenCount = positiveNumber(model.contextOptions?.[key]?.tokenCount);
67
+ return tokenCount === void 0 ? void 0 : {
68
+ key,
69
+ tokenCount
70
+ };
71
+ }
72
+ const defaultMaxTokens = 32768;
73
+ const defaultModels = [
74
+ {
75
+ id: "cmodel",
76
+ name: "Cantus (Qoder)",
77
+ description: "Default Global Qoder subscription model for quick validation",
78
+ contextWindow: 1e6,
79
+ maxTokens: defaultMaxTokens,
80
+ supportsImages: true
81
+ },
82
+ {
83
+ id: "auto",
84
+ name: "Qoder Auto",
85
+ description: "Server-routed Global Qoder model pool",
86
+ contextWindow: 18e4,
87
+ maxTokens: defaultMaxTokens,
88
+ supportsImages: true
89
+ },
90
+ {
91
+ id: "ultimate",
92
+ name: "Qoder Ultimate",
93
+ description: "Highest-capability Global Qoder model pool",
94
+ contextWindow: 1e6,
95
+ maxTokens: defaultMaxTokens,
96
+ supportsImages: true
97
+ },
98
+ {
99
+ id: "performance",
100
+ name: "Qoder Performance",
101
+ description: "Performance-oriented Global Qoder model pool",
102
+ contextWindow: 1e6,
103
+ maxTokens: defaultMaxTokens,
104
+ supportsImages: true
105
+ },
106
+ {
107
+ id: "efficient",
108
+ name: "Qoder Efficient",
109
+ description: "Efficiency-oriented Global Qoder model pool",
110
+ contextWindow: 18e4,
111
+ maxTokens: defaultMaxTokens,
112
+ supportsImages: true
113
+ },
114
+ {
115
+ id: "lite",
116
+ name: "Qoder Lite",
117
+ description: "Basic Global Qoder model pool",
118
+ contextWindow: 18e4,
119
+ maxTokens: defaultMaxTokens
120
+ }
121
+ ];
122
+ //#endregion
17
123
  //#region src/client/locales.ts
18
124
  const zh = {
19
125
  nav: "Qoder",
@@ -52,7 +158,16 @@ window.__ModuleLoader__.load({
52
158
  modelsRequired: "至少需要保留一个模型。",
53
159
  modelUnavailable: "不可用",
54
160
  modelRate: "{value}x",
161
+ modelRateFree: "免费",
162
+ modelRateDiscount: "{value}x(原 {original}x)",
55
163
  modelRateUnknown: "倍率未知",
164
+ contextTierLabel: "上下文",
165
+ contextTierDefaultSuffix: "(默认)",
166
+ contextTierHint: "选择该模型使用的上下文档位;更大的档位会同时提升 DSH 的上下文预算并改变 Qoder 请求使用的档位。",
167
+ contextSelectFailed: "上下文切换失败,请重试。",
168
+ contextDefaultFailed: "当前会话已切换,但新会话默认值保存失败。",
169
+ contextSelectTitle: "选择上下文大小",
170
+ contextSelectMenuHeader: "上下文容量",
56
171
  accountTitle: "Qoder",
57
172
  accountLoading: "正在读取账号与额度…",
58
173
  accountFailed: "无法读取账号信息与额度。",
@@ -115,7 +230,16 @@ window.__ModuleLoader__.load({
115
230
  modelsRequired: "Keep at least one model.",
116
231
  modelUnavailable: "Unavailable",
117
232
  modelRate: "{value}x",
233
+ modelRateFree: "Free",
234
+ modelRateDiscount: "{value}x (was {original}x)",
118
235
  modelRateUnknown: "Rate unknown",
236
+ contextTierLabel: "Context",
237
+ contextTierDefaultSuffix: " (default)",
238
+ contextTierHint: "Select the context tier for this model. A larger tier raises the DSH context budget and changes the tier the Qoder request uses.",
239
+ contextSelectFailed: "Could not change the context window. Please retry.",
240
+ contextDefaultFailed: "Session updated, but the default for new sessions could not be saved.",
241
+ contextSelectTitle: "Select context window size",
242
+ contextSelectMenuHeader: "Context Capacity",
119
243
  accountTitle: "Qoder",
120
244
  accountLoading: "Reading account and quota…",
121
245
  accountFailed: "Could not read account details or quota.",
@@ -192,14 +316,57 @@ window.__ModuleLoader__.load({
192
316
  }
193
317
  }
194
318
  //#endregion
319
+ //#region src/client/credential-operations.ts
320
+ function reconcileQoderModels(known, discovered) {
321
+ const availableIds = new Set(discovered.map((model) => model.id));
322
+ const unavailable = known.filter((model) => !availableIds.has(model.id)).map((model) => ({
323
+ ...model,
324
+ ...model.contextOptions !== void 0 ? { contextOptions: { ...model.contextOptions } } : {}
325
+ }));
326
+ const previous = new Map(known.map((model) => [model.id, model]));
327
+ const reconciled = discovered.map((model) => {
328
+ const remembered = previous.get(model.id);
329
+ if (remembered === void 0) return {
330
+ ...model,
331
+ ...model.contextOptions !== void 0 ? { contextOptions: { ...model.contextOptions } } : {}
332
+ };
333
+ const carried = selectedContextTier({
334
+ contextTier: remembered.contextTier,
335
+ contextOptions: model.contextOptions ?? remembered.contextOptions
336
+ });
337
+ const options = model.contextOptions ?? remembered.contextOptions;
338
+ const clonedOptions = options !== void 0 ? { contextOptions: { ...options } } : {};
339
+ if (carried !== void 0) return {
340
+ ...model,
341
+ ...clonedOptions,
342
+ contextTier: carried.key,
343
+ contextWindow: carried.tokenCount
344
+ };
345
+ const budget = remembered.contextWindow;
346
+ return budget === void 0 || model.contextWindow === void 0 ? {
347
+ ...model,
348
+ ...clonedOptions
349
+ } : {
350
+ ...model,
351
+ ...clonedOptions,
352
+ contextWindow: Math.min(budget, model.contextWindow)
353
+ };
354
+ });
355
+ return {
356
+ catalog: [...reconciled, ...unavailable],
357
+ selected: reconciled,
358
+ unavailableIds: new Set(unavailable.map((model) => model.id))
359
+ };
360
+ }
361
+ //#endregion
195
362
  //#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderCredentialCard.module.css.mjs
196
- const css = ".SRqrhW_credential{color:var(--dsw-alias-label-primary);border-radius:10px;margin-top:12px}.SRqrhW_credential p{margin:8px 0 0}.SRqrhW_credentialEditor{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.SRqrhW_credentialRowHead{align-items:center;gap:10px;display:flex}.SRqrhW_credentialIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.SRqrhW_credentialName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.SRqrhW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.SRqrhW_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.SRqrhW_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.SRqrhW_regionBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px;display:inline-flex}.SRqrhW_regionSelector{flex-direction:column;gap:8px;margin-bottom:14px;display:flex}.SRqrhW_regionGroup{gap:8px;display:flex}.SRqrhW_regionOption{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);user-select:none;border-radius:8px;align-items:center;gap:6px;padding:6px 12px;font-size:12px;line-height:18px;transition:all .12s;display:inline-flex}.SRqrhW_regionOption:hover:not(.SRqrhW_regionOptionDisabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.SRqrhW_credential button.SRqrhW_regionOptionActive{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-selected);color:var(--dsw-alias-brand-primary);font-weight:500}.SRqrhW_regionOptionDisabled{cursor:not-allowed;opacity:.5}.SRqrhW_editButton{box-sizing:border-box;font:inherit;background:0 0;margin-left:auto;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_editButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.SRqrhW_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;padding:14px 16px}.SRqrhW_customized{border-top:1px solid var(--dsw-alias-border-l2);margin-top:14px;padding-top:10px}.SRqrhW_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.SRqrhW_customizedSummary::-webkit-details-marker{display:none}.SRqrhW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.SRqrhW_customizedBody{padding-top:12px}.SRqrhW_modelCatalog,.SRqrhW_modelList{flex-direction:column;gap:10px;display:flex}.SRqrhW_modelCatalogHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.SRqrhW_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.SRqrhW_modelCatalogMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:2px 0 0!important}.SRqrhW_linkButton{box-sizing:border-box;font:inherit;background:0 0;font-size:12px;line-height:18px;border:none!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoice{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:grid}.SRqrhW_modelChoice:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoiceName{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.SRqrhW_modelChoice code{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_modelRate{min-width:34px;color:var(--dsw-alias-brand-primary);text-align:right;font-size:12px;font-weight:600}.SRqrhW_modelUnavailable{cursor:not-allowed;opacity:.55}.SRqrhW_modelBadge{color:var(--dsw-alias-state-error-primary);font-size:11px}@media (prefers-reduced-motion:reduce){.SRqrhW_customizedSummary:before{transition:none}}.SRqrhW_head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;display:flex}.SRqrhW_heading{margin:0;font-size:18px;font-weight:600}.SRqrhW_status{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_configured,.SRqrhW_success{color:var(--dsw-alias-state-success-primary)}.SRqrhW_label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;display:block}.SRqrhW_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;margin-top:6px;padding:0 10px}.SRqrhW_input:focus{border-color:var(--dsw-alias-brand-primary)}.SRqrhW_input::placeholder{color:var(--dsw-alias-label-dimmed)}.SRqrhW_input:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_hint{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.SRqrhW_credential button{cursor:pointer;height:32px;color:var(--dsw-alias-label-primary);border:1px solid #0000;border-radius:16px;padding:0 12px}.SRqrhW_credential button:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)!important}.SRqrhW_primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.SRqrhW_secondary{background:0 0;border-color:var(--dsw-alias-border-l2)!important}.SRqrhW_secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_danger{background:0 0;border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.SRqrhW_danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.SRqrhW_error{color:var(--dsw-alias-state-error-primary);font-size:12px}.SRqrhW_success{font-size:12px}.SRqrhW_accountSection{background:var(--dsw-alias-bg-module-platform);border-radius:8px;flex-direction:column;gap:10px;margin-top:12px;padding:12px;display:flex}.SRqrhW_accountHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_userInfo{align-items:center;gap:10px;min-width:0;display:flex}.SRqrhW_avatar{background:var(--dsw-alias-brand-primary);width:32px;height:32px;color:var(--dsw-alias-label-primary-foreground);text-transform:uppercase;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}.SRqrhW_userDetails{flex-direction:column;gap:2px;min-width:0;display:flex}.SRqrhW_userName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.SRqrhW_userEmail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.SRqrhW_refreshBtn{border:1px solid var(--dsw-alias-border-l2)!important;height:26px!important;color:var(--dsw-alias-label-secondary)!important;background:0 0!important;border-radius:13px!important;padding:0 8px!important;font-size:12px!important}.SRqrhW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important}.SRqrhW_quotaBlock{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:8px;display:flex}.SRqrhW_quotaHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_quotaLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.SRqrhW_quotaTop{justify-content:space-between;align-items:baseline;gap:8px;font-size:12px;display:flex}.SRqrhW_quotaTop strong{color:var(--dsw-alias-brand-primary);font-size:13px}.SRqrhW_quotaDescription{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0!important}.SRqrhW_quotaProgress{background:var(--dsw-alias-border-l3);width:100%;height:6px;accent-color:var(--dsw-alias-brand-primary);-webkit-appearance:none;appearance:none;border:0;border-radius:999px;overflow:hidden}.SRqrhW_quotaProgress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.SRqrhW_quotaProgress::-webkit-progress-value{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaProgress::-moz-progress-bar{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaMeta{color:var(--dsw-alias-label-tertiary);justify-content:space-between;align-items:center;gap:8px;font-size:11px;display:flex}.SRqrhW_accountLoadingText,.SRqrhW_accountEmptyText{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.SRqrhW_userNameRow{align-items:center;gap:8px;display:flex}.SRqrhW_planBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-brand-primary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:600;line-height:16px;display:inline-flex}.SRqrhW_orgTag{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_suspendedBanner{background:var(--dsw-alias-state-error-subtle,#ff4d4f1a);color:var(--dsw-alias-state-error-primary);border-radius:6px;align-items:center;gap:6px;margin-bottom:8px;padding:6px 10px;font-size:12px;display:flex}.SRqrhW_searchModeSection{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;margin-top:16px;padding-top:14px;display:flex}.SRqrhW_searchModeHintText{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:12px;line-height:18px}";
197
- const tagId = "dsh-provider-qoder/QoderCredentialCard.module.css";
198
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
363
+ const css$1 = ".SRqrhW_credential{color:var(--dsw-alias-label-primary);border-radius:10px;margin-top:12px}.SRqrhW_credential p{margin:8px 0 0}.SRqrhW_credentialEditor{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);flex-direction:column;gap:12px;padding:12px 14px;display:flex}.SRqrhW_credentialRowHead{align-items:center;gap:10px;display:flex}.SRqrhW_credentialIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.SRqrhW_credentialName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.SRqrhW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.SRqrhW_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.SRqrhW_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.SRqrhW_regionBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px;display:inline-flex}.SRqrhW_regionSelector{flex-direction:column;gap:8px;margin-bottom:14px;display:flex}.SRqrhW_regionGroup{gap:8px;display:flex}.SRqrhW_regionOption{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);user-select:none;border-radius:8px;align-items:center;gap:6px;padding:6px 12px;font-size:12px;line-height:18px;transition:all .12s;display:inline-flex}.SRqrhW_regionOption:hover:not(.SRqrhW_regionOptionDisabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.SRqrhW_credential button.SRqrhW_regionOptionActive{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-selected);color:var(--dsw-alias-brand-primary);font-weight:500}.SRqrhW_regionOptionDisabled{cursor:not-allowed;opacity:.5}.SRqrhW_editButton{box-sizing:border-box;font:inherit;background:0 0;margin-left:auto;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_editButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.SRqrhW_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;padding:14px 16px}.SRqrhW_customized{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-bg-module-platform);margin-top:12px;transition:all .12s;overflow:hidden}.SRqrhW_customizedSummary{box-sizing:border-box;border-radius:var(--dsw-radius-xl,12px);cursor:pointer;width:100%;color:var(--dsw-alias-label-primary);user-select:none;align-items:center;gap:8px;margin:0;padding:12px 14px;font-size:13px;font-weight:600;line-height:18px;list-style:none;transition:background .12s;display:flex}.SRqrhW_customizedSummary:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelSummaryBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:400;line-height:16px;display:inline-flex}.SRqrhW_customizedSummary::-webkit-details-marker{display:none}.SRqrhW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary{border-bottom:.5px solid var(--dsw-alias-border-l2);border-bottom-right-radius:0;border-bottom-left-radius:0}.SRqrhW_customizedBody{padding:12px 14px 14px}.SRqrhW_modelCatalog{flex-direction:column;gap:10px;display:flex}.SRqrhW_modelList{flex-direction:column;gap:10px;max-height:360px;padding-right:4px;display:flex;overflow-y:auto}.SRqrhW_modelList::-webkit-scrollbar{width:4px}.SRqrhW_modelList::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3);border-radius:2px}.SRqrhW_modelCatalogHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.SRqrhW_modelCatalogHeadCompact{justify-content:flex-end;align-items:center;gap:12px;display:flex}.SRqrhW_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.SRqrhW_modelCatalogMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:2px 0 0!important}.SRqrhW_linkButton{box-sizing:border-box;font:inherit;background:0 0;font-size:12px;line-height:18px;border:none!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoice{border-radius:6px;flex-direction:column;gap:4px;padding:6px 8px;font-size:13px;display:flex}.SRqrhW_modelChoice:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoiceHead{cursor:pointer;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:8px;display:grid}.SRqrhW_modelTier{align-items:center;gap:8px;padding-left:24px;display:flex}.SRqrhW_modelTierLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px}.SRqrhW_modelTierSelect{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);height:24px;color:inherit;font:inherit;cursor:pointer;border:0;border-radius:6px;padding:0 6px;font-size:12px}.SRqrhW_modelTierHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px;margin:0!important}.SRqrhW_modelChoiceName{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.SRqrhW_modelChoice code{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_modelRate{min-width:34px;color:var(--dsw-alias-brand-primary);text-align:right;font-size:12px;font-weight:600}.SRqrhW_modelUnavailable{cursor:not-allowed;opacity:.55}.SRqrhW_modelBadge{color:var(--dsw-alias-state-error-primary);font-size:11px}@media (prefers-reduced-motion:reduce){.SRqrhW_customizedSummary:before{transition:none}}.SRqrhW_head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;display:flex}.SRqrhW_heading{margin:0;font-size:18px;font-weight:600}.SRqrhW_status{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_configured,.SRqrhW_success{color:var(--dsw-alias-state-success-primary)}.SRqrhW_label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;display:block}.SRqrhW_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;margin-top:6px;padding:0 10px}.SRqrhW_input:focus{border-color:var(--dsw-alias-brand-primary)}.SRqrhW_input::placeholder{color:var(--dsw-alias-label-dimmed)}.SRqrhW_input:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_hint{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.SRqrhW_credential button{cursor:pointer;height:32px;color:var(--dsw-alias-label-primary);border:1px solid #0000;border-radius:16px;padding:0 12px}.SRqrhW_credential button:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)!important}.SRqrhW_primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.SRqrhW_secondary{background:0 0;border-color:var(--dsw-alias-border-l2)!important}.SRqrhW_secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_danger{background:0 0;border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.SRqrhW_danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.SRqrhW_error{color:var(--dsw-alias-state-error-primary);font-size:12px}.SRqrhW_success{font-size:12px}.SRqrhW_accountSection{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;display:flex}.SRqrhW_accountHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_userInfo{align-items:center;gap:10px;min-width:0;display:flex}.SRqrhW_avatar{background:var(--dsw-alias-brand-primary);width:32px;height:32px;color:var(--dsw-alias-label-primary-foreground);text-transform:uppercase;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}.SRqrhW_userDetails{flex-direction:column;gap:2px;min-width:0;display:flex}.SRqrhW_userName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.SRqrhW_userEmail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.SRqrhW_refreshBtn{border:1px solid var(--dsw-alias-border-l2)!important;height:26px!important;color:var(--dsw-alias-label-secondary)!important;background:0 0!important;border-radius:13px!important;padding:0 8px!important;font-size:12px!important}.SRqrhW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important}.SRqrhW_quotaBlock{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:8px;display:flex}.SRqrhW_quotaHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_quotaLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.SRqrhW_quotaTop{justify-content:space-between;align-items:baseline;gap:8px;font-size:12px;display:flex}.SRqrhW_quotaTop strong{color:var(--dsw-alias-brand-primary);font-size:13px}.SRqrhW_quotaDescription{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0!important}.SRqrhW_quotaProgress{background:var(--dsw-alias-border-l3);width:100%;height:6px;accent-color:var(--dsw-alias-brand-primary);-webkit-appearance:none;appearance:none;border:0;border-radius:999px;overflow:hidden}.SRqrhW_quotaProgress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.SRqrhW_quotaProgress::-webkit-progress-value{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaProgress::-moz-progress-bar{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaMeta{color:var(--dsw-alias-label-tertiary);justify-content:space-between;align-items:center;gap:8px;font-size:11px;display:flex}.SRqrhW_accountLoadingText,.SRqrhW_accountEmptyText{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.SRqrhW_userNameRow{align-items:center;gap:8px;display:flex}.SRqrhW_planBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-brand-primary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:600;line-height:16px;display:inline-flex}.SRqrhW_orgTag{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_suspendedBanner{background:var(--dsw-alias-state-error-subtle,#ff4d4f1a);color:var(--dsw-alias-state-error-primary);border-radius:6px;align-items:center;gap:6px;margin-bottom:8px;padding:6px 10px;font-size:12px;display:flex}.SRqrhW_searchModeSection{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;display:flex}.SRqrhW_searchModeHintText{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:12px;line-height:18px}";
364
+ const tagId$1 = "dsh-provider-qoder/QoderCredentialCard.module.css";
365
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
199
366
  const tag = document.createElement("style");
200
367
  tag.dataset.plugin = "dsh-provider-qoder";
201
- tag.dataset.pluginCss = tagId;
202
- tag.textContent = css;
368
+ tag.dataset.pluginCss = tagId$1;
369
+ tag.textContent = css$1;
203
370
  document.head.appendChild(tag);
204
371
  }
205
372
  var QoderCredentialCard_module_css_default = {
@@ -234,12 +401,19 @@ window.__ModuleLoader__.load({
234
401
  "modelBadge": "SRqrhW_modelBadge",
235
402
  "modelCatalog": "SRqrhW_modelCatalog",
236
403
  "modelCatalogHead": "SRqrhW_modelCatalogHead",
404
+ "modelCatalogHeadCompact": "SRqrhW_modelCatalogHeadCompact",
237
405
  "modelCatalogMeta": "SRqrhW_modelCatalogMeta",
238
406
  "modelCatalogTitle": "SRqrhW_modelCatalogTitle",
239
407
  "modelChoice": "SRqrhW_modelChoice",
408
+ "modelChoiceHead": "SRqrhW_modelChoiceHead",
240
409
  "modelChoiceName": "SRqrhW_modelChoiceName",
241
410
  "modelList": "SRqrhW_modelList",
242
411
  "modelRate": "SRqrhW_modelRate",
412
+ "modelSummaryBadge": "SRqrhW_modelSummaryBadge",
413
+ "modelTier": "SRqrhW_modelTier",
414
+ "modelTierHint": "SRqrhW_modelTierHint",
415
+ "modelTierLabel": "SRqrhW_modelTierLabel",
416
+ "modelTierSelect": "SRqrhW_modelTierSelect",
243
417
  "modelUnavailable": "SRqrhW_modelUnavailable",
244
418
  "orgTag": "SRqrhW_orgTag",
245
419
  "planBadge": "SRqrhW_planBadge",
@@ -271,6 +445,159 @@ window.__ModuleLoader__.load({
271
445
  "userNameRow": "SRqrhW_userNameRow"
272
446
  };
273
447
  //#endregion
448
+ //#region src/client/QoderModelCatalog.tsx
449
+ function validateModelCatalog(models) {
450
+ return models.length === 0 ? "modelsRequired" : void 0;
451
+ }
452
+ function QoderModelCatalog(props) {
453
+ const { operations, models, disabled, fetchDisabled, hideTitle, onChange, t } = props;
454
+ const [fetching, setFetching] = (0, react.useState)(false);
455
+ const [failure, setFailure] = (0, react.useState)();
456
+ const [catalog, setCatalog] = (0, react.useState)();
457
+ const [unavailableIds, setUnavailableIds] = (0, react.useState)(/* @__PURE__ */ new Set());
458
+ const displayedModels = catalog ?? models;
459
+ const selectedIds = (0, react.useMemo)(() => new Set(models.map((model) => model.id)), [models]);
460
+ const fetchModels = async () => {
461
+ setFetching(true);
462
+ setFailure(void 0);
463
+ const result = await operations.discoverModels();
464
+ setFetching(false);
465
+ if (!result.ok) {
466
+ setFailure(result.error.message || t("modelsFetchFailed"));
467
+ return;
468
+ }
469
+ const reconciled = reconcileQoderModels(catalog ?? models, result.value);
470
+ setCatalog(reconciled.catalog);
471
+ setUnavailableIds(reconciled.unavailableIds);
472
+ onChange(reconciled.selected);
473
+ };
474
+ const toggleModel = (id, enabled) => {
475
+ if (unavailableIds.has(id)) return;
476
+ const source = catalog ?? models;
477
+ if (catalog === void 0) setCatalog(source);
478
+ const nextSelected = new Set(selectedIds);
479
+ if (enabled) nextSelected.add(id);
480
+ else nextSelected.delete(id);
481
+ onChange(source.filter((model) => nextSelected.has(model.id) && !unavailableIds.has(model.id)));
482
+ };
483
+ const changeContextTier = (id, tierKey) => {
484
+ const source = catalog ?? models;
485
+ if (catalog === void 0) setCatalog(source);
486
+ const apply = (model) => {
487
+ if (model.id !== id) return model;
488
+ const tokenCount = model.contextOptions?.[tierKey]?.tokenCount;
489
+ if (typeof tokenCount !== "number" || !Number.isFinite(tokenCount) || tokenCount <= 0) return model;
490
+ return {
491
+ ...model,
492
+ contextTier: tierKey,
493
+ contextWindow: tokenCount
494
+ };
495
+ };
496
+ const nextCatalog = source.map(apply);
497
+ setCatalog(nextCatalog);
498
+ onChange(models.map(apply));
499
+ };
500
+ const renderRate = (model) => {
501
+ if (model.isFree || model.priceFactor === 0) return t("modelRateFree");
502
+ if (model.priceFactor === void 0) return t("modelRateUnknown");
503
+ const current = formatRateFactor(model.priceFactor);
504
+ if (model.originalPriceFactor !== void 0 && model.originalPriceFactor > model.priceFactor) return t("modelRateDiscount", {
505
+ value: current,
506
+ original: formatRateFactor(model.originalPriceFactor)
507
+ });
508
+ return t("modelRate", { value: current });
509
+ };
510
+ const tieredModels = displayedModels.filter((model) => contextTiersOf(model).length > 1);
511
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
512
+ className: QoderCredentialCard_module_css_default.modelCatalog,
513
+ "aria-label": t("modelsTitle"),
514
+ children: [
515
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
516
+ className: hideTitle ? QoderCredentialCard_module_css_default.modelCatalogHeadCompact : QoderCredentialCard_module_css_default.modelCatalogHead,
517
+ children: [!hideTitle ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
518
+ className: QoderCredentialCard_module_css_default.modelCatalogTitle,
519
+ children: t("modelsTitle")
520
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
521
+ className: QoderCredentialCard_module_css_default.modelCatalogMeta,
522
+ children: t("modelsEnabled", { count: models.length })
523
+ })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
524
+ type: "button",
525
+ className: QoderCredentialCard_module_css_default.linkButton,
526
+ disabled: disabled || fetching || Boolean(fetchDisabled),
527
+ onClick: () => {
528
+ fetchModels();
529
+ },
530
+ children: fetching ? t("modelsFetching") : t("modelsFetch")
531
+ })]
532
+ }),
533
+ failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
534
+ className: QoderCredentialCard_module_css_default.error,
535
+ role: "alert",
536
+ children: failure
537
+ }) : null,
538
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
539
+ className: QoderCredentialCard_module_css_default.modelList,
540
+ children: displayedModels.map((model) => {
541
+ const unavailable = unavailableIds.has(model.id);
542
+ const tiers = contextTiersOf(model);
543
+ const fallbackTier = tiers.find((tier) => tier.isDefault) ?? tiers[tiers.length - 1];
544
+ const selectedTier = resolveContextTier(model)?.key ?? fallbackTier?.key;
545
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
546
+ className: `${QoderCredentialCard_module_css_default.modelChoice} ${unavailable ? QoderCredentialCard_module_css_default.modelUnavailable : ""}`,
547
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
548
+ className: QoderCredentialCard_module_css_default.modelChoiceHead,
549
+ children: [
550
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
551
+ type: "checkbox",
552
+ checked: !unavailable && selectedIds.has(model.id),
553
+ disabled: disabled || unavailable,
554
+ onChange: (event) => {
555
+ toggleModel(model.id, event.currentTarget.checked);
556
+ }
557
+ }),
558
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
559
+ className: QoderCredentialCard_module_css_default.modelChoiceName,
560
+ children: model.name
561
+ }),
562
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
563
+ className: QoderCredentialCard_module_css_default.modelRate,
564
+ children: renderRate(model)
565
+ }),
566
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.id }),
567
+ unavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
568
+ className: QoderCredentialCard_module_css_default.modelBadge,
569
+ children: t("modelUnavailable")
570
+ }) : null
571
+ ]
572
+ }), tiers.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
573
+ className: QoderCredentialCard_module_css_default.modelTier,
574
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
575
+ className: QoderCredentialCard_module_css_default.modelTierLabel,
576
+ children: t("contextTierLabel")
577
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
578
+ className: QoderCredentialCard_module_css_default.modelTierSelect,
579
+ value: selectedTier ?? "",
580
+ disabled: disabled || unavailable || !selectedIds.has(model.id),
581
+ onChange: (event) => {
582
+ changeContextTier(model.id, event.currentTarget.value);
583
+ },
584
+ children: tiers.map((tier) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
585
+ value: tier.key,
586
+ children: tier.isDefault ? `${formatContextTokens(tier.tokenCount)}${t("contextTierDefaultSuffix")}` : formatContextTokens(tier.tokenCount)
587
+ }, tier.key))
588
+ })]
589
+ }) : null]
590
+ }, model.id);
591
+ })
592
+ }),
593
+ tieredModels.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
594
+ className: QoderCredentialCard_module_css_default.modelTierHint,
595
+ children: t("contextTierHint")
596
+ }) : null
597
+ ]
598
+ });
599
+ }
600
+ //#endregion
274
601
  //#region src/client/QoderAccountCard.tsx
275
602
  function formatResetDate(dateStr) {
276
603
  if (!dateStr) return void 0;
@@ -283,6 +610,18 @@ window.__ModuleLoader__.load({
283
610
  minute: "2-digit"
284
611
  });
285
612
  }
613
+ function modelsOf$1(value, selectedRegion) {
614
+ if (typeof value !== "object" || value === null) return defaultModels.map((model) => ({ ...model }));
615
+ const section = value;
616
+ const region = selectedRegion ?? (section.region === "china" ? "china" : "global");
617
+ const scoped = section.modelsByRegion?.[region];
618
+ if (Array.isArray(scoped)) return scoped;
619
+ return defaultModels.map((model) => ({ ...model }));
620
+ }
621
+ function regionOf$1(value) {
622
+ if (typeof value !== "object" || value === null) return "global";
623
+ return value.region === "china" ? "china" : "global";
624
+ }
286
625
  /**
287
626
  * Settings card for the Qoder subscription: credential state, subscriber
288
627
  * profile, and every quota block the provider reports.
@@ -292,6 +631,7 @@ window.__ModuleLoader__.load({
292
631
  const [accountState, setAccountState] = (0, react.useState)({ status: "idle" });
293
632
  const [refreshing, setRefreshing] = (0, react.useState)(false);
294
633
  const [credentialRevision, setCredentialRevision] = (0, react.useState)(0);
634
+ const [modelSaveError, setModelSaveError] = (0, react.useState)();
295
635
  const latestAccountRequest = (0, react.useRef)(0);
296
636
  const modelSnapshot = (0, react.useSyncExternalStore)(operations.subscribeModels, operations.getModelSnapshot, operations.getModelSnapshot);
297
637
  const loadCredential = (0, react.useCallback)(async () => {
@@ -412,9 +752,12 @@ window.__ModuleLoader__.load({
412
752
  }, key);
413
753
  };
414
754
  const renderAccount = () => {
415
- if (!configured) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
416
- className: QoderCredentialCard_module_css_default.hint,
417
- children: t("accountCredentialHint")
755
+ if (!configured) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
756
+ className: QoderCredentialCard_module_css_default.accountSection,
757
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
758
+ className: QoderCredentialCard_module_css_default.accountEmptyText,
759
+ children: t("accountCredentialHint")
760
+ })
418
761
  });
419
762
  if (accountState.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
420
763
  className: QoderCredentialCard_module_css_default.accountSection,
@@ -585,6 +928,54 @@ window.__ModuleLoader__.load({
585
928
  ]
586
929
  });
587
930
  };
931
+ const currentRegion = regionOf$1(modelSnapshot.value);
932
+ const currentModels = modelsOf$1(modelSnapshot.value, currentRegion);
933
+ const canModifyModels = modelSnapshot.status === "ready" && modelSnapshot.writable;
934
+ const handleModelsChange = async (models) => {
935
+ if (!canModifyModels) return;
936
+ setModelSaveError(void 0);
937
+ const failure = validateModelCatalog(models);
938
+ if (failure) {
939
+ setModelSaveError(t(failure));
940
+ return;
941
+ }
942
+ if (!await operations.storeModels(currentRegion, models)) setModelSaveError(t("saveFailed"));
943
+ };
944
+ const renderModelCatalogSection = () => {
945
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
946
+ className: QoderCredentialCard_module_css_default.customized,
947
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
948
+ className: QoderCredentialCard_module_css_default.customizedSummary,
949
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("modelsTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
950
+ className: QoderCredentialCard_module_css_default.modelSummaryBadge,
951
+ children: t("modelsEnabled", { count: currentModels.length })
952
+ })]
953
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
954
+ className: QoderCredentialCard_module_css_default.customizedBody,
955
+ children: [modelSnapshot.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
956
+ className: QoderCredentialCard_module_css_default.hint,
957
+ children: t("modelsLoading")
958
+ }) : modelSnapshot.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
959
+ className: QoderCredentialCard_module_css_default.error,
960
+ children: t("modelsUnavailable")
961
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QoderModelCatalog, {
962
+ operations,
963
+ models: currentModels,
964
+ disabled: !canModifyModels,
965
+ fetchDisabled: !configured,
966
+ hideTitle: true,
967
+ t,
968
+ onChange: (models) => {
969
+ handleModelsChange(models);
970
+ }
971
+ }), modelSaveError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
972
+ className: QoderCredentialCard_module_css_default.error,
973
+ role: "alert",
974
+ children: modelSaveError
975
+ }) : null]
976
+ })]
977
+ });
978
+ };
588
979
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
589
980
  className: QoderCredentialCard_module_css_default.credential,
590
981
  "aria-label": t("accountTitle"),
@@ -600,189 +991,13 @@ window.__ModuleLoader__.load({
600
991
  })]
601
992
  }),
602
993
  renderAccount(),
603
- renderWebSearchSection()
604
- ]
605
- });
606
- }
607
- //#endregion
608
- //#region src/qoder/catalog.ts
609
- const defaultMaxTokens = 32768;
610
- const defaultModels = [
611
- {
612
- id: "cmodel",
613
- name: "Cantus (Qoder)",
614
- description: "Default Global Qoder subscription model for quick validation",
615
- contextWindow: 1e6,
616
- maxTokens: defaultMaxTokens,
617
- supportsImages: true
618
- },
619
- {
620
- id: "auto",
621
- name: "Qoder Auto",
622
- description: "Server-routed Global Qoder model pool",
623
- contextWindow: 18e4,
624
- maxTokens: defaultMaxTokens,
625
- supportsImages: true
626
- },
627
- {
628
- id: "ultimate",
629
- name: "Qoder Ultimate",
630
- description: "Highest-capability Global Qoder model pool",
631
- contextWindow: 1e6,
632
- maxTokens: defaultMaxTokens,
633
- supportsImages: true
634
- },
635
- {
636
- id: "performance",
637
- name: "Qoder Performance",
638
- description: "Performance-oriented Global Qoder model pool",
639
- contextWindow: 1e6,
640
- maxTokens: defaultMaxTokens,
641
- supportsImages: true
642
- },
643
- {
644
- id: "efficient",
645
- name: "Qoder Efficient",
646
- description: "Efficiency-oriented Global Qoder model pool",
647
- contextWindow: 18e4,
648
- maxTokens: defaultMaxTokens,
649
- supportsImages: true
650
- },
651
- {
652
- id: "lite",
653
- name: "Qoder Lite",
654
- description: "Basic Global Qoder model pool",
655
- contextWindow: 18e4,
656
- maxTokens: defaultMaxTokens
657
- }
658
- ];
659
- //#endregion
660
- //#region src/client/credential-operations.ts
661
- function reconcileQoderModels(known, discovered) {
662
- const availableIds = new Set(discovered.map((model) => model.id));
663
- const unavailable = known.filter((model) => !availableIds.has(model.id));
664
- const previous = new Map(known.map((model) => [model.id, model]));
665
- const reconciled = discovered.map((model) => {
666
- const budget = previous.get(model.id)?.contextWindow;
667
- return budget === void 0 || model.contextWindow === void 0 ? model : {
668
- ...model,
669
- contextWindow: Math.min(budget, model.contextWindow)
670
- };
671
- });
672
- return {
673
- catalog: [...reconciled, ...unavailable],
674
- selected: reconciled,
675
- unavailableIds: new Set(unavailable.map((model) => model.id))
676
- };
677
- }
678
- //#endregion
679
- //#region src/client/QoderModelCatalog.tsx
680
- function validateModelCatalog(models) {
681
- return models.length === 0 ? "modelsRequired" : void 0;
682
- }
683
- function QoderModelCatalog(props) {
684
- const { operations, models, disabled, onChange, t } = props;
685
- const [fetching, setFetching] = (0, react.useState)(false);
686
- const [failure, setFailure] = (0, react.useState)();
687
- const [catalog, setCatalog] = (0, react.useState)();
688
- const [unavailableIds, setUnavailableIds] = (0, react.useState)(/* @__PURE__ */ new Set());
689
- const displayedModels = catalog ?? models;
690
- const selectedIds = (0, react.useMemo)(() => new Set(models.map((model) => model.id)), [models]);
691
- const fetchModels = async () => {
692
- setFetching(true);
693
- setFailure(void 0);
694
- const result = await operations.discoverModels();
695
- setFetching(false);
696
- if (!result.ok) {
697
- setFailure(result.error.message || t("modelsFetchFailed"));
698
- return;
699
- }
700
- const reconciled = reconcileQoderModels(catalog ?? models, result.value);
701
- setCatalog(reconciled.catalog);
702
- setUnavailableIds(reconciled.unavailableIds);
703
- onChange(reconciled.selected);
704
- };
705
- const toggleModel = (id, enabled) => {
706
- if (unavailableIds.has(id)) return;
707
- const source = catalog ?? models;
708
- if (catalog === void 0) setCatalog(source);
709
- const nextSelected = new Set(selectedIds);
710
- if (enabled) nextSelected.add(id);
711
- else nextSelected.delete(id);
712
- onChange(source.filter((model) => nextSelected.has(model.id) && !unavailableIds.has(model.id)));
713
- };
714
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
715
- className: QoderCredentialCard_module_css_default.modelCatalog,
716
- "aria-label": t("modelsTitle"),
717
- children: [
718
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
719
- className: QoderCredentialCard_module_css_default.modelCatalogHead,
720
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
721
- className: QoderCredentialCard_module_css_default.modelCatalogTitle,
722
- children: t("modelsTitle")
723
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
724
- className: QoderCredentialCard_module_css_default.modelCatalogMeta,
725
- children: t("modelsEnabled", { count: models.length })
726
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
727
- type: "button",
728
- className: QoderCredentialCard_module_css_default.linkButton,
729
- disabled: disabled || fetching,
730
- onClick: () => {
731
- fetchModels();
732
- },
733
- children: fetching ? t("modelsFetching") : t("modelsFetch")
734
- })]
735
- }),
736
- failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
737
- className: QoderCredentialCard_module_css_default.error,
738
- role: "alert",
739
- children: failure
740
- }) : null,
741
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
742
- className: QoderCredentialCard_module_css_default.modelList,
743
- children: displayedModels.map((model) => {
744
- const unavailable = unavailableIds.has(model.id);
745
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
746
- className: `${QoderCredentialCard_module_css_default.modelChoice} ${unavailable ? QoderCredentialCard_module_css_default.modelUnavailable : ""}`,
747
- children: [
748
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
749
- type: "checkbox",
750
- checked: !unavailable && selectedIds.has(model.id),
751
- disabled: disabled || unavailable,
752
- onChange: (event) => {
753
- toggleModel(model.id, event.currentTarget.checked);
754
- }
755
- }),
756
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
757
- className: QoderCredentialCard_module_css_default.modelChoiceName,
758
- children: model.name
759
- }),
760
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
761
- className: QoderCredentialCard_module_css_default.modelRate,
762
- children: model.priceFactor === void 0 ? t("modelRateUnknown") : t("modelRate", { value: model.priceFactor })
763
- }),
764
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.id }),
765
- unavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
766
- className: QoderCredentialCard_module_css_default.modelBadge,
767
- children: t("modelUnavailable")
768
- }) : null
769
- ]
770
- }, model.id);
771
- })
772
- })
994
+ renderWebSearchSection(),
995
+ renderModelCatalogSection()
773
996
  ]
774
997
  });
775
998
  }
776
999
  //#endregion
777
1000
  //#region src/client/QoderCredentialCard.tsx
778
- function modelsOf(value, selectedRegion) {
779
- if (typeof value !== "object" || value === null) return [];
780
- const section = value;
781
- const region = selectedRegion ?? (section.region === "china" ? "china" : "global");
782
- const scoped = section.modelsByRegion?.[region];
783
- if (Array.isArray(scoped)) return scoped;
784
- return region === (section.region === "china" ? "china" : "global") && Array.isArray(section.models) ? section.models : defaultModels.map((model) => ({ ...model }));
785
- }
786
1001
  function regionOf(value) {
787
1002
  if (typeof value !== "object" || value === null) return "global";
788
1003
  return value.region === "china" ? "china" : "global";
@@ -793,22 +1008,12 @@ window.__ModuleLoader__.load({
793
1008
  const [draft, setDraft] = (0, react.useState)("");
794
1009
  const [regionDraft, setRegionDraft] = (0, react.useState)("global");
795
1010
  const [regionDirty, setRegionDirty] = (0, react.useState)(false);
796
- const [modelDraft, setModelDraft] = (0, react.useState)([]);
797
- const [modelsDirty, setModelsDirty] = (0, react.useState)(false);
798
1011
  const [mutation, setMutation] = (0, react.useState)();
799
1012
  const [notice, setNotice] = (0, react.useState)();
800
1013
  const modelSnapshot = (0, react.useSyncExternalStore)(operations.subscribeModels, operations.getModelSnapshot, operations.getModelSnapshot);
801
1014
  (0, react.useEffect)(() => {
802
- if (modelSnapshot.status === "ready") {
803
- if (!modelsDirty) setModelDraft(modelsOf(modelSnapshot.value, regionDraft));
804
- if (!regionDirty) setRegionDraft(regionOf(modelSnapshot.value));
805
- }
806
- }, [
807
- modelSnapshot,
808
- modelsDirty,
809
- regionDirty,
810
- regionDraft
811
- ]);
1015
+ if (modelSnapshot.status === "ready" && !regionDirty) setRegionDraft(regionOf(modelSnapshot.value));
1016
+ }, [modelSnapshot, regionDirty]);
812
1017
  const load = (0, react.useCallback)(async () => {
813
1018
  const info = await operations.describe();
814
1019
  setState(info === void 0 ? { status: "failed" } : {
@@ -845,20 +1050,17 @@ window.__ModuleLoader__.load({
845
1050
  const writable = state.info.writable;
846
1051
  const busy = mutation !== void 0;
847
1052
  const normalized = draft.trim();
848
- const modelFailure = modelSnapshot.status === "ready" ? validateModelCatalog(modelDraft) : void 0;
849
- const modelWritable = modelSnapshot.status === "ready" && modelSnapshot.writable;
1053
+ const canModifyRegion = modelSnapshot.status === "ready" && modelSnapshot.writable;
850
1054
  const currentRegion = regionOf(modelSnapshot.value);
851
1055
  const closeEditor = () => {
852
1056
  setDraft("");
853
- setModelDraft(modelsOf(modelSnapshot.value, regionOf(modelSnapshot.value)));
854
1057
  setRegionDraft(regionOf(modelSnapshot.value));
855
- setModelsDirty(false);
856
1058
  setRegionDirty(false);
857
1059
  setNotice(void 0);
858
1060
  setEditing(false);
859
1061
  };
860
1062
  const save = async () => {
861
- if (busy || !modelsDirty && !regionDirty && normalized.length === 0 || modelsDirty && (!modelWritable || modelFailure !== void 0) || regionDirty && !modelWritable) return;
1063
+ if (busy || !regionDirty && normalized.length === 0 || regionDirty && !canModifyRegion) return;
862
1064
  setMutation("saving");
863
1065
  setNotice(void 0);
864
1066
  if (regionDirty) {
@@ -869,14 +1071,6 @@ window.__ModuleLoader__.load({
869
1071
  }
870
1072
  setRegionDirty(false);
871
1073
  }
872
- if (modelsDirty) {
873
- if (!await operations.storeModels(regionDraft, modelDraft)) {
874
- setNotice("saveFailed");
875
- setMutation(void 0);
876
- return;
877
- }
878
- setModelsDirty(false);
879
- }
880
1074
  if (normalized.length === 0 || await operations.store(normalized)) {
881
1075
  setDraft("");
882
1076
  setNotice("saved");
@@ -957,11 +1151,9 @@ window.__ModuleLoader__.load({
957
1151
  role: "radio",
958
1152
  "aria-checked": regionDraft === "global",
959
1153
  className: `${QoderCredentialCard_module_css_default.regionOption} ${regionDraft === "global" ? QoderCredentialCard_module_css_default.regionOptionActive : ""}`,
960
- disabled: !modelWritable || busy,
1154
+ disabled: !canModifyRegion || busy,
961
1155
  onClick: () => {
962
1156
  setRegionDraft("global");
963
- setModelDraft(modelsOf(modelSnapshot.value, "global"));
964
- setModelsDirty(false);
965
1157
  setRegionDirty(true);
966
1158
  },
967
1159
  children: t("regionGlobal")
@@ -970,11 +1162,9 @@ window.__ModuleLoader__.load({
970
1162
  role: "radio",
971
1163
  "aria-checked": regionDraft === "china",
972
1164
  className: `${QoderCredentialCard_module_css_default.regionOption} ${regionDraft === "china" ? QoderCredentialCard_module_css_default.regionOptionActive : ""}`,
973
- disabled: !modelWritable || busy,
1165
+ disabled: !canModifyRegion || busy,
974
1166
  onClick: () => {
975
1167
  setRegionDraft("china");
976
- setModelDraft(modelsOf(modelSnapshot.value, "china"));
977
- setModelsDirty(false);
978
1168
  setRegionDirty(true);
979
1169
  },
980
1170
  children: t("regionChina")
@@ -1001,34 +1191,6 @@ window.__ModuleLoader__.load({
1001
1191
  role: "alert",
1002
1192
  children: t("readOnly")
1003
1193
  }) : null,
1004
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
1005
- className: QoderCredentialCard_module_css_default.customized,
1006
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
1007
- className: QoderCredentialCard_module_css_default.customizedSummary,
1008
- children: t("customized")
1009
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1010
- className: QoderCredentialCard_module_css_default.customizedBody,
1011
- children: [modelSnapshot.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1012
- className: QoderCredentialCard_module_css_default.hint,
1013
- children: t("modelsLoading")
1014
- }) : modelSnapshot.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1015
- className: QoderCredentialCard_module_css_default.error,
1016
- children: t("modelsUnavailable")
1017
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QoderModelCatalog, {
1018
- operations,
1019
- models: modelDraft,
1020
- disabled: !modelWritable || busy,
1021
- t,
1022
- onChange: (models) => {
1023
- setModelDraft(models);
1024
- setModelsDirty(true);
1025
- }
1026
- }), modelSnapshot.status === "ready" && modelFailure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1027
- className: QoderCredentialCard_module_css_default.error,
1028
- children: t(modelFailure)
1029
- }) : null]
1030
- })]
1031
- }),
1032
1194
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1033
1195
  className: QoderCredentialCard_module_css_default.actions,
1034
1196
  children: [
@@ -1051,7 +1213,7 @@ window.__ModuleLoader__.load({
1051
1213
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1052
1214
  type: "button",
1053
1215
  className: QoderCredentialCard_module_css_default.primary,
1054
- disabled: busy || !modelsDirty && !regionDirty && normalized.length === 0 || modelsDirty && (modelSnapshot.status !== "ready" || modelFailure !== void 0 || !modelWritable) || regionDirty && (modelSnapshot.status !== "ready" || !modelWritable) || normalized.length > 0 && !writable,
1216
+ disabled: busy || !regionDirty && normalized.length === 0 || regionDirty && !canModifyRegion || normalized.length > 0 && !writable,
1055
1217
  onClick: () => {
1056
1218
  save();
1057
1219
  },
@@ -1064,6 +1226,250 @@ window.__ModuleLoader__.load({
1064
1226
  ]
1065
1227
  });
1066
1228
  }
1229
+ //#endregion
1230
+ //#region src/dsh/provider.ts
1231
+ /** DSH model provider route, matching the plugin package name. */
1232
+ const QODER_PROVIDER_ID = "dsh-provider-qoder";
1233
+ //#endregion
1234
+ //#region src/client/context-selection.ts
1235
+ function isQoderProvider(provider) {
1236
+ return [
1237
+ QODER_PROVIDER_ID,
1238
+ "qoder-official",
1239
+ "qoder",
1240
+ "qoder-subscription"
1241
+ ].includes(provider ?? "");
1242
+ }
1243
+ function modelsOf(section, region, _legacyRegion) {
1244
+ const scoped = section?.modelsByRegion?.[region];
1245
+ if (scoped !== void 0) return scoped;
1246
+ return defaultModels.map((model) => ({ ...model }));
1247
+ }
1248
+ function historicalContextWindow(current, lastUsed, contextWindow) {
1249
+ return current.provider === lastUsed?.provider && current.model === lastUsed?.model ? contextWindow : void 0;
1250
+ }
1251
+ /** A failed session write must not change defaults; a failed default write is a partial success. */
1252
+ async function saveContextSelection(operations, sessionId, region, modelId, tierKey, legacyRegion) {
1253
+ try {
1254
+ if (!await operations.setSessionTier?.(sessionId, modelId, tierKey, region)) return "session-failed";
1255
+ } catch {
1256
+ return "session-failed";
1257
+ }
1258
+ try {
1259
+ const models = modelsOf(operations.getModelSnapshot().value, region, legacyRegion);
1260
+ const model = models.find((candidate) => candidate.id === modelId);
1261
+ const tier = model && contextTiersOf(model).find((candidate) => candidate.key === tierKey);
1262
+ if (!tier) return "default-failed";
1263
+ const updated = models.map((candidate) => candidate.id === modelId ? {
1264
+ ...candidate,
1265
+ contextTier: tierKey,
1266
+ contextWindow: tier.tokenCount
1267
+ } : candidate);
1268
+ return await operations.storeModels(region, updated) ? "saved" : "default-failed";
1269
+ } catch {
1270
+ return "default-failed";
1271
+ }
1272
+ }
1273
+ //#endregion
1274
+ //#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderContextSelect.module.css.mjs
1275
+ const css = ".hiDrJa_root{align-items:center;display:inline-flex;position:relative}.hiDrJa_trigger{height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;user-select:none;background:0 0;border:none;border-radius:24px;outline:none;align-items:center;gap:4px;padding:0 8px;font-size:13px;font-weight:500;line-height:20px;transition:background .12s,color .12s;display:flex}.hiDrJa_trigger:hover,.hiDrJa_triggerActive{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.hiDrJa_trigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.hiDrJa_icon{color:var(--dsw-alias-label-caption);justify-content:center;align-items:center;display:inline-flex}.hiDrJa_label{white-space:nowrap}.hiDrJa_chevron{color:var(--dsw-alias-label-caption);align-items:center;transition:transform .12s;display:inline-flex}.hiDrJa_chevronOpen{transform:rotate(180deg)}.hiDrJa_menu{z-index:1100;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);min-width:180px;box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;padding:4px;animation:.12s hiDrJa_fadeIn;display:flex;position:absolute;bottom:calc(100% + 8px);right:0}@keyframes hiDrJa_fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.hiDrJa_menuHeader{text-transform:uppercase;letter-spacing:.5px;color:var(--dsw-alias-label-tertiary);border-bottom:1px solid var(--dsw-alias-border-l1);margin-bottom:4px;padding:6px 10px 4px;font-size:11px;font-weight:600}.hiDrJa_option{box-sizing:border-box;width:100%;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;border-radius:8px;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;font-size:13px;line-height:20px;transition:background .1s;display:flex}.hiDrJa_option:hover{background:var(--dsw-alias-interactive-bg-hover)}.hiDrJa_optionSelected{color:var(--dsw-alias-state-brand);font-weight:600}.hiDrJa_optionInfo{align-items:center;gap:6px;display:flex}.hiDrJa_tag{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-caption);border-radius:4px;padding:1px 5px;font-size:11px;font-weight:500}.hiDrJa_check{color:var(--dsw-alias-state-brand);align-items:center;font-size:12px;display:inline-flex}.hiDrJa_error{color:var(--dsw-alias-label-primary);max-width:240px;font-size:12px}";
1276
+ const tagId = "dsh-provider-qoder/QoderContextSelect.module.css";
1277
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1278
+ const tag = document.createElement("style");
1279
+ tag.dataset.plugin = "dsh-provider-qoder";
1280
+ tag.dataset.pluginCss = tagId;
1281
+ tag.textContent = css;
1282
+ document.head.appendChild(tag);
1283
+ }
1284
+ var QoderContextSelect_module_css_default = {
1285
+ "check": "hiDrJa_check",
1286
+ "chevron": "hiDrJa_chevron",
1287
+ "chevronOpen": "hiDrJa_chevronOpen",
1288
+ "error": "hiDrJa_error",
1289
+ "fadeIn": "hiDrJa_fadeIn",
1290
+ "icon": "hiDrJa_icon",
1291
+ "label": "hiDrJa_label",
1292
+ "menu": "hiDrJa_menu",
1293
+ "menuHeader": "hiDrJa_menuHeader",
1294
+ "option": "hiDrJa_option",
1295
+ "optionInfo": "hiDrJa_optionInfo",
1296
+ "optionSelected": "hiDrJa_optionSelected",
1297
+ "root": "hiDrJa_root",
1298
+ "tag": "hiDrJa_tag",
1299
+ "trigger": "hiDrJa_trigger",
1300
+ "triggerActive": "hiDrJa_triggerActive"
1301
+ };
1302
+ //#endregion
1303
+ //#region src/client/QoderContextSelect.tsx
1304
+ function QoderContextSelect(props) {
1305
+ const { directory, operations, t, sessionId, useProjection } = props;
1306
+ const [open, setOpen] = (0, react.useState)(false);
1307
+ const [manualTiers, setManualTiers] = (0, react.useState)({});
1308
+ const [pending, setPending] = (0, react.useState)(false);
1309
+ const [error, setError] = (0, react.useState)(null);
1310
+ const saving = (0, react.useRef)(false);
1311
+ const legacyRegion = (0, react.useRef)(void 0);
1312
+ const rootRef = (0, react.useRef)(null);
1313
+ const directoryState = (0, react.useSyncExternalStore)((fn) => directory ? directory.subscribe(fn) : () => () => {}, () => directory ? directory.getSnapshot() : null);
1314
+ const modelSnapshot = (0, react.useSyncExternalStore)((fn) => operations ? operations.subscribeModels(fn) : () => () => {}, () => operations ? operations.getModelSnapshot() : null);
1315
+ (0, react.useEffect)(() => {
1316
+ if (!open) return;
1317
+ const handleClickOutside = (e) => {
1318
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
1319
+ };
1320
+ const handleKeyDown = (e) => {
1321
+ if (e.key === "Escape") setOpen(false);
1322
+ };
1323
+ document.addEventListener("mousedown", handleClickOutside);
1324
+ document.addEventListener("keydown", handleKeyDown);
1325
+ return () => {
1326
+ document.removeEventListener("mousedown", handleClickOutside);
1327
+ document.removeEventListener("keydown", handleKeyDown);
1328
+ };
1329
+ }, [open]);
1330
+ const modelProj = useProjection ? useProjection("modelSelection") : void 0;
1331
+ const pressure = useProjection ? useProjection("contextPressure") : void 0;
1332
+ if (!operations) return null;
1333
+ const projectedCurrent = modelProj?.next ?? modelProj?.lastUsed;
1334
+ const current = directoryState?.current ?? (projectedCurrent?.model ? {
1335
+ provider: projectedCurrent.provider ?? "dsh-provider-qoder",
1336
+ model: projectedCurrent.model
1337
+ } : null);
1338
+ if (!current) return null;
1339
+ const region = modelSnapshot?.value?.region === "china" ? "china" : "global";
1340
+ if (legacyRegion.current === void 0 && modelSnapshot?.value) legacyRegion.current = region;
1341
+ const models = modelsOf(modelSnapshot?.value, region, legacyRegion.current ?? region);
1342
+ const currentModelId = current.model.includes("/") ? current.model.split("/")[1] : current.model;
1343
+ const currentProviderId = current.provider ?? (current.model.includes("/") ? current.model.split("/")[0] : void 0);
1344
+ const activeModel = models.find((m) => m.id === currentModelId || m.id === current.model);
1345
+ if (!isQoderProvider(currentProviderId) || !activeModel) return null;
1346
+ const tiers = contextTiersOf(activeModel);
1347
+ if (tiers.length <= 1) return null;
1348
+ const sessionKey = sessionId ? JSON.stringify([
1349
+ region,
1350
+ sessionId,
1351
+ activeModel.id
1352
+ ]) : void 0;
1353
+ const historicalWindow = operations.canRestoreContextHistory?.() === false ? void 0 : historicalContextWindow(current, modelProj?.lastUsed, pressure?.contextWindow);
1354
+ const currentTier = resolveContextTier(activeModel, sessionKey ? manualTiers[sessionKey] : void 0, historicalWindow);
1355
+ if (!currentTier) return null;
1356
+ const selectedTierKey = currentTier.key;
1357
+ const selectTier = async (tierKey) => {
1358
+ setOpen(false);
1359
+ if (!sessionId || !sessionKey || saving.current) return;
1360
+ if (tierKey === selectedTierKey && error?.key !== sessionKey) return;
1361
+ saving.current = true;
1362
+ setPending(true);
1363
+ setError(null);
1364
+ try {
1365
+ const result = await saveContextSelection(operations, sessionId, region, activeModel.id, tierKey, legacyRegion.current ?? region);
1366
+ if (result !== "session-failed") setManualTiers((prev) => ({
1367
+ ...prev,
1368
+ [sessionKey]: tierKey
1369
+ }));
1370
+ if (result !== "saved") setError({
1371
+ key: sessionKey,
1372
+ message: result === "session-failed" ? "contextSelectFailed" : "contextDefaultFailed"
1373
+ });
1374
+ } finally {
1375
+ saving.current = false;
1376
+ setPending(false);
1377
+ }
1378
+ };
1379
+ const defaultSuffix = t ? t("contextTierDefaultSuffix").replace(/[()()]/g, "") : "默认";
1380
+ const selectTitle = t ? t("contextSelectTitle") : "选择上下文大小";
1381
+ const menuHeader = t ? t("contextSelectMenuHeader") : "上下文容量";
1382
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1383
+ className: QoderContextSelect_module_css_default.root,
1384
+ ref: rootRef,
1385
+ children: [
1386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1387
+ type: "button",
1388
+ className: `${QoderContextSelect_module_css_default.trigger} ${open ? QoderContextSelect_module_css_default.triggerActive : ""}`,
1389
+ disabled: pending || !sessionId,
1390
+ "aria-expanded": open,
1391
+ "aria-label": selectTitle,
1392
+ title: `${selectTitle}: ${formatContextTokens(currentTier.tokenCount)}`,
1393
+ onClick: () => setOpen((prev) => !prev),
1394
+ children: [
1395
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1396
+ className: QoderContextSelect_module_css_default.icon,
1397
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1398
+ width: "14",
1399
+ height: "14",
1400
+ viewBox: "0 0 16 16",
1401
+ fill: "currentColor",
1402
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2 4.5A2.5 2.5 0 0 1 4.5 2h7A2.5 2.5 0 0 1 14 4.5v7a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 11.5v-7zm2.5-1A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3.5h-7zM5 6h6v1H5V6zm0 3h4v1H5V9z" })
1403
+ })
1404
+ }),
1405
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1406
+ className: QoderContextSelect_module_css_default.label,
1407
+ children: formatContextTokens(currentTier.tokenCount)
1408
+ }),
1409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1410
+ className: `${QoderContextSelect_module_css_default.chevron} ${open ? QoderContextSelect_module_css_default.chevronOpen : ""}`,
1411
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1412
+ width: "10",
1413
+ height: "10",
1414
+ viewBox: "0 0 12 12",
1415
+ fill: "none",
1416
+ stroke: "currentColor",
1417
+ strokeWidth: "1.5",
1418
+ strokeLinecap: "round",
1419
+ strokeLinejoin: "round",
1420
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 4.5L6 7.5L9 4.5" })
1421
+ })
1422
+ })
1423
+ ]
1424
+ }),
1425
+ error && error.key === sessionKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1426
+ role: "alert",
1427
+ className: QoderContextSelect_module_css_default.error,
1428
+ children: t ? t(error.message) : error.message
1429
+ }),
1430
+ open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1431
+ className: QoderContextSelect_module_css_default.menu,
1432
+ role: "menu",
1433
+ "aria-label": selectTitle,
1434
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1435
+ className: QoderContextSelect_module_css_default.menuHeader,
1436
+ children: menuHeader
1437
+ }), tiers.map((tier) => {
1438
+ const isSelected = tier.key === selectedTierKey;
1439
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1440
+ type: "button",
1441
+ role: "menuitemradio",
1442
+ "aria-checked": isSelected,
1443
+ className: `${QoderContextSelect_module_css_default.option} ${isSelected ? QoderContextSelect_module_css_default.optionSelected : ""}`,
1444
+ onClick: () => {
1445
+ selectTier(tier.key);
1446
+ },
1447
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1448
+ className: QoderContextSelect_module_css_default.optionInfo,
1449
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatContextTokens(tier.tokenCount) }), tier.isDefault && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1450
+ className: QoderContextSelect_module_css_default.tag,
1451
+ children: defaultSuffix
1452
+ })]
1453
+ }), isSelected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1454
+ className: QoderContextSelect_module_css_default.check,
1455
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1456
+ width: "14",
1457
+ height: "14",
1458
+ viewBox: "0 0 16 16",
1459
+ fill: "none",
1460
+ stroke: "currentColor",
1461
+ strokeWidth: "2",
1462
+ strokeLinecap: "round",
1463
+ strokeLinejoin: "round",
1464
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3.5 8.5L6.5 11.5L12.5 4.5" })
1465
+ })
1466
+ })]
1467
+ }, tier.key);
1468
+ })]
1469
+ })
1470
+ ]
1471
+ });
1472
+ }
1067
1473
  /**
1068
1474
  * Absolute Fetch-route prefix. Connection mounts its shared, authenticated API
1069
1475
  * channel at `/api`, and every exact Fetch route lives directly below it.
@@ -1166,24 +1572,15 @@ window.__ModuleLoader__.load({
1166
1572
  "slots",
1167
1573
  "locale",
1168
1574
  "remote",
1169
- "remote.credentials"
1575
+ "remote.credentials",
1576
+ "configForms"
1170
1577
  ];
1171
1578
  const fill = (text, values) => {
1172
1579
  if (!values) return text;
1173
1580
  return Object.entries(values).reduce((next, [key, value]) => next.replace(new RegExp(`\\{${key}\\}`, "g"), String(value)), text);
1174
1581
  };
1175
1582
  function apply(ctx) {
1176
- const configForms = ctx.get?.("configForms");
1177
- if (configForms) {
1178
- mount(ctx, configForms.get(settingsNamespace));
1179
- return;
1180
- }
1181
- const settingsScope = ctx.get?.("settingsScope");
1182
- if (settingsScope) {
1183
- mount(ctx, settingsScope.bind({ namespace: settingsNamespace }));
1184
- return;
1185
- }
1186
- console.warn("[provider-qoder] Neither configForms nor settingsScope available; settings panel will not mount");
1583
+ mount(ctx, ctx.configForms.get(settingsNamespace));
1187
1584
  }
1188
1585
  function mount(ctx, modelScope) {
1189
1586
  ctx.effect(() => ctx.locale.register(localeNamespace, {
@@ -1192,6 +1589,21 @@ window.__ModuleLoader__.load({
1192
1589
  }), "provider-qoder: credential copy");
1193
1590
  const credentials = ctx.remote.credentials;
1194
1591
  const rpc = createQoderRpcCaller();
1592
+ let historyRegion;
1593
+ let historyRegionChanged = false;
1594
+ const readModelSnapshot = () => {
1595
+ const snapshot = modelScope.getSnapshot();
1596
+ if (snapshot.value) {
1597
+ const region = snapshot.value.region ?? "global";
1598
+ if (historyRegion !== void 0 && historyRegion !== region) historyRegionChanged = true;
1599
+ historyRegion = region;
1600
+ }
1601
+ return snapshot;
1602
+ };
1603
+ readModelSnapshot();
1604
+ ctx.effect(() => modelScope.subscribe(() => {
1605
+ readModelSnapshot();
1606
+ }), "provider-qoder: context history region");
1195
1607
  const operations = {
1196
1608
  describe: async () => {
1197
1609
  try {
@@ -1216,14 +1628,19 @@ window.__ModuleLoader__.load({
1216
1628
  }
1217
1629
  },
1218
1630
  getAccount: async (force) => await rpc.call("account", { force }),
1219
- getModelSnapshot: () => modelScope.getSnapshot(),
1631
+ getModelSnapshot: readModelSnapshot,
1632
+ canRestoreContextHistory: () => !historyRegionChanged,
1220
1633
  subscribeModels: (listener) => modelScope.subscribe(listener),
1221
1634
  storeModels: async (region, models) => {
1222
1635
  try {
1223
1636
  const current = modelScope.getSnapshot().value;
1637
+ const clonedModels = models.map((m) => ({
1638
+ ...m,
1639
+ ...m.contextOptions !== void 0 ? { contextOptions: { ...m.contextOptions } } : {}
1640
+ }));
1224
1641
  return await modelScope.set("modelsByRegion", {
1225
1642
  ...current?.modelsByRegion,
1226
- [region]: models
1643
+ [region]: clonedModels
1227
1644
  }) !== false;
1228
1645
  } catch {
1229
1646
  return false;
@@ -1244,6 +1661,18 @@ window.__ModuleLoader__.load({
1244
1661
  }
1245
1662
  },
1246
1663
  discoverModels: async () => await rpc.call("models", {}),
1664
+ setSessionTier: async (sessionId, modelId, tierKey, region) => {
1665
+ try {
1666
+ return (await rpc.call("sessionTier", {
1667
+ sessionId,
1668
+ modelId,
1669
+ tierKey,
1670
+ region
1671
+ })).ok;
1672
+ } catch {
1673
+ return false;
1674
+ }
1675
+ },
1247
1676
  subscribe: (listener) => ctx.remote.$on("credentials/reference-updated", (ref) => {
1248
1677
  if (ref === "QODER_MANAGED_CREDENTIAL") listener();
1249
1678
  })
@@ -1280,6 +1709,25 @@ window.__ModuleLoader__.load({
1280
1709
  activeLocale
1281
1710
  })
1282
1711
  }, QoderCredentialCard));
1712
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
1713
+ name: "conversation.input.right",
1714
+ id: "qoder-context-select",
1715
+ order: 10,
1716
+ inject: (sessionId) => {
1717
+ let directory = void 0;
1718
+ try {
1719
+ const modelDirectories = ctx.get("modelDirectories");
1720
+ directory = sessionId && modelDirectories ? modelDirectories.directoryFor(sessionId)?.store : void 0;
1721
+ } catch {}
1722
+ return {
1723
+ sessionId,
1724
+ directory,
1725
+ operations,
1726
+ t,
1727
+ activeLocale
1728
+ };
1729
+ }
1730
+ }, QoderContextSelect));
1283
1731
  }
1284
1732
  //#endregion
1285
1733
  exports.apply = apply;