dsh-provider-qoder 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -14,6 +14,160 @@ 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
+ /** Clone a model catalog entry, detaching any contextOptions object. */
73
+ function cloneCatalogModel(model) {
74
+ return {
75
+ ...model,
76
+ ...model.contextOptions !== void 0 ? { contextOptions: { ...model.contextOptions } } : {}
77
+ };
78
+ }
79
+ /** Carry subscriber context choices only while the refreshed catalog supports them. */
80
+ function mergeAdvertisedContext(remembered, advertised, updated) {
81
+ const merged = { ...updated };
82
+ delete merged.contextOptions;
83
+ delete merged.contextTier;
84
+ if (advertised.contextOptions !== void 0) merged.contextOptions = { ...advertised.contextOptions };
85
+ const tier = selectedContextTier({
86
+ contextTier: remembered.contextTier,
87
+ contextOptions: merged.contextOptions
88
+ });
89
+ if (tier !== void 0) {
90
+ merged.contextTier = tier.key;
91
+ merged.contextWindow = tier.tokenCount;
92
+ } else if (advertised.contextWindow !== void 0) merged.contextWindow = remembered.contextTier !== void 0 ? advertised.contextWindow : Math.min(remembered.contextWindow ?? advertised.contextWindow, advertised.contextWindow);
93
+ return merged;
94
+ }
95
+ const defaultMaxTokens = 32768;
96
+ const defaultModels = [
97
+ {
98
+ id: "cmodel",
99
+ name: "Cantus (Qoder)",
100
+ description: "Default Global Qoder subscription model for quick validation",
101
+ contextWindow: 1e6,
102
+ maxTokens: defaultMaxTokens,
103
+ supportsImages: true
104
+ },
105
+ {
106
+ id: "auto",
107
+ name: "Qoder Auto",
108
+ description: "Server-routed Global Qoder model pool",
109
+ contextWindow: 18e4,
110
+ maxTokens: defaultMaxTokens,
111
+ supportsImages: true
112
+ },
113
+ {
114
+ id: "ultimate",
115
+ name: "Qoder Ultimate",
116
+ description: "Highest-capability Global Qoder model pool",
117
+ contextWindow: 1e6,
118
+ maxTokens: defaultMaxTokens,
119
+ supportsImages: true
120
+ },
121
+ {
122
+ id: "performance",
123
+ name: "Qoder Performance",
124
+ description: "Performance-oriented Global Qoder model pool",
125
+ contextWindow: 1e6,
126
+ maxTokens: defaultMaxTokens,
127
+ supportsImages: true
128
+ },
129
+ {
130
+ id: "efficient",
131
+ name: "Qoder Efficient",
132
+ description: "Efficiency-oriented Global Qoder model pool",
133
+ contextWindow: 18e4,
134
+ maxTokens: defaultMaxTokens,
135
+ supportsImages: true
136
+ },
137
+ {
138
+ id: "lite",
139
+ name: "Qoder Lite",
140
+ description: "Basic Global Qoder model pool",
141
+ contextWindow: 18e4,
142
+ maxTokens: defaultMaxTokens
143
+ }
144
+ ];
145
+ //#endregion
146
+ //#region src/client/credential-operations.ts
147
+ function regionOf(value) {
148
+ if (typeof value !== "object" || value === null) return "global";
149
+ return value.region === "china" ? "china" : "global";
150
+ }
151
+ function modelsOf(section, region) {
152
+ const scoped = section?.modelsByRegion?.[region];
153
+ return Array.isArray(scoped) ? scoped : defaultModels.map((model) => ({ ...model }));
154
+ }
155
+ function reconcileQoderModels(known, discovered) {
156
+ const availableIds = new Set(discovered.map((model) => model.id));
157
+ const unavailable = known.filter((model) => !availableIds.has(model.id)).map(cloneCatalogModel);
158
+ const previous = new Map(known.map((model) => [model.id, model]));
159
+ const reconciled = discovered.map((model) => {
160
+ const remembered = previous.get(model.id);
161
+ if (remembered === void 0) return cloneCatalogModel(model);
162
+ return mergeAdvertisedContext(remembered, model, model);
163
+ });
164
+ return {
165
+ catalog: [...reconciled, ...unavailable],
166
+ selected: reconciled,
167
+ unavailableIds: new Set(unavailable.map((model) => model.id))
168
+ };
169
+ }
170
+ //#endregion
17
171
  //#region src/client/locales.ts
18
172
  const zh = {
19
173
  nav: "Qoder",
@@ -52,7 +206,15 @@ window.__ModuleLoader__.load({
52
206
  modelsRequired: "至少需要保留一个模型。",
53
207
  modelUnavailable: "不可用",
54
208
  modelRate: "{value}x",
209
+ modelRateFree: "免费",
210
+ modelRateDiscount: "{value}x(原 {original}x)",
55
211
  modelRateUnknown: "倍率未知",
212
+ contextTierLabel: "上下文",
213
+ contextTierDefaultSuffix: "(默认)",
214
+ contextTierHint: "选择该模型使用的上下文档位;更大的档位会同时提升 DSH 的上下文预算并改变 Qoder 请求使用的档位。",
215
+ contextSelectFailed: "上下文切换失败,请重试。",
216
+ contextSelectTitle: "选择上下文大小",
217
+ contextSelectMenuHeader: "上下文容量",
56
218
  accountTitle: "Qoder",
57
219
  accountLoading: "正在读取账号与额度…",
58
220
  accountFailed: "无法读取账号信息与额度。",
@@ -115,7 +277,15 @@ window.__ModuleLoader__.load({
115
277
  modelsRequired: "Keep at least one model.",
116
278
  modelUnavailable: "Unavailable",
117
279
  modelRate: "{value}x",
280
+ modelRateFree: "Free",
281
+ modelRateDiscount: "{value}x (was {original}x)",
118
282
  modelRateUnknown: "Rate unknown",
283
+ contextTierLabel: "Context",
284
+ contextTierDefaultSuffix: " (default)",
285
+ contextTierHint: "Select the context tier for this model. A larger tier raises the DSH context budget and changes the tier the Qoder request uses.",
286
+ contextSelectFailed: "Could not change the context window. Please retry.",
287
+ contextSelectTitle: "Select context window size",
288
+ contextSelectMenuHeader: "Context Capacity",
119
289
  accountTitle: "Qoder",
120
290
  accountLoading: "Reading account and quota…",
121
291
  accountFailed: "Could not read account details or quota.",
@@ -193,13 +363,13 @@ window.__ModuleLoader__.load({
193
363
  }
194
364
  //#endregion
195
365
  //#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) {
366
+ 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}";
367
+ const tagId$1 = "dsh-provider-qoder/QoderCredentialCard.module.css";
368
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
199
369
  const tag = document.createElement("style");
200
370
  tag.dataset.plugin = "dsh-provider-qoder";
201
- tag.dataset.pluginCss = tagId;
202
- tag.textContent = css;
371
+ tag.dataset.pluginCss = tagId$1;
372
+ tag.textContent = css$1;
203
373
  document.head.appendChild(tag);
204
374
  }
205
375
  var QoderCredentialCard_module_css_default = {
@@ -234,12 +404,19 @@ window.__ModuleLoader__.load({
234
404
  "modelBadge": "SRqrhW_modelBadge",
235
405
  "modelCatalog": "SRqrhW_modelCatalog",
236
406
  "modelCatalogHead": "SRqrhW_modelCatalogHead",
407
+ "modelCatalogHeadCompact": "SRqrhW_modelCatalogHeadCompact",
237
408
  "modelCatalogMeta": "SRqrhW_modelCatalogMeta",
238
409
  "modelCatalogTitle": "SRqrhW_modelCatalogTitle",
239
410
  "modelChoice": "SRqrhW_modelChoice",
411
+ "modelChoiceHead": "SRqrhW_modelChoiceHead",
240
412
  "modelChoiceName": "SRqrhW_modelChoiceName",
241
413
  "modelList": "SRqrhW_modelList",
242
414
  "modelRate": "SRqrhW_modelRate",
415
+ "modelSummaryBadge": "SRqrhW_modelSummaryBadge",
416
+ "modelTier": "SRqrhW_modelTier",
417
+ "modelTierHint": "SRqrhW_modelTierHint",
418
+ "modelTierLabel": "SRqrhW_modelTierLabel",
419
+ "modelTierSelect": "SRqrhW_modelTierSelect",
243
420
  "modelUnavailable": "SRqrhW_modelUnavailable",
244
421
  "orgTag": "SRqrhW_orgTag",
245
422
  "planBadge": "SRqrhW_planBadge",
@@ -271,6 +448,159 @@ window.__ModuleLoader__.load({
271
448
  "userNameRow": "SRqrhW_userNameRow"
272
449
  };
273
450
  //#endregion
451
+ //#region src/client/QoderModelCatalog.tsx
452
+ function validateModelCatalog(models) {
453
+ return models.length === 0 ? "modelsRequired" : void 0;
454
+ }
455
+ function QoderModelCatalog(props) {
456
+ const { operations, models, disabled, fetchDisabled, hideTitle, onChange, t } = props;
457
+ const [fetching, setFetching] = (0, react.useState)(false);
458
+ const [failure, setFailure] = (0, react.useState)();
459
+ const [catalog, setCatalog] = (0, react.useState)();
460
+ const [unavailableIds, setUnavailableIds] = (0, react.useState)(/* @__PURE__ */ new Set());
461
+ const displayedModels = catalog ?? models;
462
+ const selectedIds = (0, react.useMemo)(() => new Set(models.map((model) => model.id)), [models]);
463
+ const fetchModels = async () => {
464
+ setFetching(true);
465
+ setFailure(void 0);
466
+ const result = await operations.discoverModels();
467
+ setFetching(false);
468
+ if (!result.ok) {
469
+ setFailure(result.error.message || t("modelsFetchFailed"));
470
+ return;
471
+ }
472
+ const reconciled = reconcileQoderModels(catalog ?? models, result.value);
473
+ setCatalog(reconciled.catalog);
474
+ setUnavailableIds(reconciled.unavailableIds);
475
+ onChange(reconciled.selected);
476
+ };
477
+ const toggleModel = (id, enabled) => {
478
+ if (unavailableIds.has(id)) return;
479
+ const source = catalog ?? models;
480
+ if (catalog === void 0) setCatalog(source);
481
+ const nextSelected = new Set(selectedIds);
482
+ if (enabled) nextSelected.add(id);
483
+ else nextSelected.delete(id);
484
+ onChange(source.filter((model) => nextSelected.has(model.id) && !unavailableIds.has(model.id)));
485
+ };
486
+ const changeContextTier = (id, tierKey) => {
487
+ const source = catalog ?? models;
488
+ if (catalog === void 0) setCatalog(source);
489
+ const apply = (model) => {
490
+ if (model.id !== id) return model;
491
+ const tokenCount = model.contextOptions?.[tierKey]?.tokenCount;
492
+ if (typeof tokenCount !== "number" || !Number.isFinite(tokenCount) || tokenCount <= 0) return model;
493
+ return {
494
+ ...model,
495
+ contextTier: tierKey,
496
+ contextWindow: tokenCount
497
+ };
498
+ };
499
+ const nextCatalog = source.map(apply);
500
+ setCatalog(nextCatalog);
501
+ onChange(models.map(apply));
502
+ };
503
+ const renderRate = (model) => {
504
+ if (model.isFree || model.priceFactor === 0) return t("modelRateFree");
505
+ if (model.priceFactor === void 0) return t("modelRateUnknown");
506
+ const current = formatRateFactor(model.priceFactor);
507
+ if (model.originalPriceFactor !== void 0 && model.originalPriceFactor > model.priceFactor) return t("modelRateDiscount", {
508
+ value: current,
509
+ original: formatRateFactor(model.originalPriceFactor)
510
+ });
511
+ return t("modelRate", { value: current });
512
+ };
513
+ const tieredModels = displayedModels.filter((model) => contextTiersOf(model).length > 1);
514
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
515
+ className: QoderCredentialCard_module_css_default.modelCatalog,
516
+ "aria-label": t("modelsTitle"),
517
+ children: [
518
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
519
+ className: hideTitle ? QoderCredentialCard_module_css_default.modelCatalogHeadCompact : QoderCredentialCard_module_css_default.modelCatalogHead,
520
+ children: [!hideTitle ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
521
+ className: QoderCredentialCard_module_css_default.modelCatalogTitle,
522
+ children: t("modelsTitle")
523
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
524
+ className: QoderCredentialCard_module_css_default.modelCatalogMeta,
525
+ children: t("modelsEnabled", { count: models.length })
526
+ })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
527
+ type: "button",
528
+ className: QoderCredentialCard_module_css_default.linkButton,
529
+ disabled: disabled || fetching || Boolean(fetchDisabled),
530
+ onClick: () => {
531
+ fetchModels();
532
+ },
533
+ children: fetching ? t("modelsFetching") : t("modelsFetch")
534
+ })]
535
+ }),
536
+ failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
537
+ className: QoderCredentialCard_module_css_default.error,
538
+ role: "alert",
539
+ children: failure
540
+ }) : null,
541
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
542
+ className: QoderCredentialCard_module_css_default.modelList,
543
+ children: displayedModels.map((model) => {
544
+ const unavailable = unavailableIds.has(model.id);
545
+ const tiers = contextTiersOf(model);
546
+ const fallbackTier = tiers.find((tier) => tier.isDefault) ?? tiers[tiers.length - 1];
547
+ const selectedTier = resolveContextTier(model)?.key ?? fallbackTier?.key;
548
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
549
+ className: `${QoderCredentialCard_module_css_default.modelChoice} ${unavailable ? QoderCredentialCard_module_css_default.modelUnavailable : ""}`,
550
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
551
+ className: QoderCredentialCard_module_css_default.modelChoiceHead,
552
+ children: [
553
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
554
+ type: "checkbox",
555
+ checked: !unavailable && selectedIds.has(model.id),
556
+ disabled: disabled || unavailable,
557
+ onChange: (event) => {
558
+ toggleModel(model.id, event.currentTarget.checked);
559
+ }
560
+ }),
561
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
562
+ className: QoderCredentialCard_module_css_default.modelChoiceName,
563
+ children: model.name
564
+ }),
565
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
566
+ className: QoderCredentialCard_module_css_default.modelRate,
567
+ children: renderRate(model)
568
+ }),
569
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.id }),
570
+ unavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
571
+ className: QoderCredentialCard_module_css_default.modelBadge,
572
+ children: t("modelUnavailable")
573
+ }) : null
574
+ ]
575
+ }), tiers.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
576
+ className: QoderCredentialCard_module_css_default.modelTier,
577
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
578
+ className: QoderCredentialCard_module_css_default.modelTierLabel,
579
+ children: t("contextTierLabel")
580
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
581
+ className: QoderCredentialCard_module_css_default.modelTierSelect,
582
+ value: selectedTier ?? "",
583
+ disabled: disabled || unavailable || !selectedIds.has(model.id),
584
+ onChange: (event) => {
585
+ changeContextTier(model.id, event.currentTarget.value);
586
+ },
587
+ children: tiers.map((tier) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
588
+ value: tier.key,
589
+ children: tier.isDefault ? `${formatContextTokens(tier.tokenCount)}${t("contextTierDefaultSuffix")}` : formatContextTokens(tier.tokenCount)
590
+ }, tier.key))
591
+ })]
592
+ }) : null]
593
+ }, model.id);
594
+ })
595
+ }),
596
+ tieredModels.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
597
+ className: QoderCredentialCard_module_css_default.modelTierHint,
598
+ children: t("contextTierHint")
599
+ }) : null
600
+ ]
601
+ });
602
+ }
603
+ //#endregion
274
604
  //#region src/client/QoderAccountCard.tsx
275
605
  function formatResetDate(dateStr) {
276
606
  if (!dateStr) return void 0;
@@ -292,6 +622,7 @@ window.__ModuleLoader__.load({
292
622
  const [accountState, setAccountState] = (0, react.useState)({ status: "idle" });
293
623
  const [refreshing, setRefreshing] = (0, react.useState)(false);
294
624
  const [credentialRevision, setCredentialRevision] = (0, react.useState)(0);
625
+ const [modelSaveError, setModelSaveError] = (0, react.useState)();
295
626
  const latestAccountRequest = (0, react.useRef)(0);
296
627
  const modelSnapshot = (0, react.useSyncExternalStore)(operations.subscribeModels, operations.getModelSnapshot, operations.getModelSnapshot);
297
628
  const loadCredential = (0, react.useCallback)(async () => {
@@ -412,9 +743,12 @@ window.__ModuleLoader__.load({
412
743
  }, key);
413
744
  };
414
745
  const renderAccount = () => {
415
- if (!configured) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
416
- className: QoderCredentialCard_module_css_default.hint,
417
- children: t("accountCredentialHint")
746
+ if (!configured) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
747
+ className: QoderCredentialCard_module_css_default.accountSection,
748
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
749
+ className: QoderCredentialCard_module_css_default.accountEmptyText,
750
+ children: t("accountCredentialHint")
751
+ })
418
752
  });
419
753
  if (accountState.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
420
754
  className: QoderCredentialCard_module_css_default.accountSection,
@@ -585,6 +919,54 @@ window.__ModuleLoader__.load({
585
919
  ]
586
920
  });
587
921
  };
922
+ const currentRegion = regionOf(modelSnapshot.value);
923
+ const currentModels = modelsOf(modelSnapshot.value, currentRegion);
924
+ const canModifyModels = modelSnapshot.status === "ready" && modelSnapshot.writable;
925
+ const handleModelsChange = async (models) => {
926
+ if (!canModifyModels) return;
927
+ setModelSaveError(void 0);
928
+ const failure = validateModelCatalog(models);
929
+ if (failure) {
930
+ setModelSaveError(t(failure));
931
+ return;
932
+ }
933
+ if (!await operations.storeModels(currentRegion, models)) setModelSaveError(t("saveFailed"));
934
+ };
935
+ const renderModelCatalogSection = () => {
936
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
937
+ className: QoderCredentialCard_module_css_default.customized,
938
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
939
+ className: QoderCredentialCard_module_css_default.customizedSummary,
940
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("modelsTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
941
+ className: QoderCredentialCard_module_css_default.modelSummaryBadge,
942
+ children: t("modelsEnabled", { count: currentModels.length })
943
+ })]
944
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
945
+ className: QoderCredentialCard_module_css_default.customizedBody,
946
+ children: [modelSnapshot.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
947
+ className: QoderCredentialCard_module_css_default.hint,
948
+ children: t("modelsLoading")
949
+ }) : modelSnapshot.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
950
+ className: QoderCredentialCard_module_css_default.error,
951
+ children: t("modelsUnavailable")
952
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QoderModelCatalog, {
953
+ operations,
954
+ models: currentModels,
955
+ disabled: !canModifyModels,
956
+ fetchDisabled: !configured,
957
+ hideTitle: true,
958
+ t,
959
+ onChange: (models) => {
960
+ handleModelsChange(models);
961
+ }
962
+ }), modelSaveError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
963
+ className: QoderCredentialCard_module_css_default.error,
964
+ role: "alert",
965
+ children: modelSaveError
966
+ }) : null]
967
+ })]
968
+ });
969
+ };
588
970
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
589
971
  className: QoderCredentialCard_module_css_default.credential,
590
972
  "aria-label": t("accountTitle"),
@@ -600,215 +982,25 @@ window.__ModuleLoader__.load({
600
982
  })]
601
983
  }),
602
984
  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
- })
985
+ renderWebSearchSection(),
986
+ renderModelCatalogSection()
773
987
  ]
774
988
  });
775
989
  }
776
990
  //#endregion
777
991
  //#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 defaultModels.map((model) => ({ ...model }));
785
- }
786
- function regionOf(value) {
787
- if (typeof value !== "object" || value === null) return "global";
788
- return value.region === "china" ? "china" : "global";
789
- }
790
992
  function QoderCredentialCard({ operations, t }) {
791
993
  const [state, setState] = (0, react.useState)({ status: "loading" });
792
994
  const [editing, setEditing] = (0, react.useState)(false);
793
995
  const [draft, setDraft] = (0, react.useState)("");
794
996
  const [regionDraft, setRegionDraft] = (0, react.useState)("global");
795
997
  const [regionDirty, setRegionDirty] = (0, react.useState)(false);
796
- const [modelDraft, setModelDraft] = (0, react.useState)([]);
797
- const [modelsDirty, setModelsDirty] = (0, react.useState)(false);
798
998
  const [mutation, setMutation] = (0, react.useState)();
799
999
  const [notice, setNotice] = (0, react.useState)();
800
1000
  const modelSnapshot = (0, react.useSyncExternalStore)(operations.subscribeModels, operations.getModelSnapshot, operations.getModelSnapshot);
801
1001
  (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
- ]);
1002
+ if (modelSnapshot.status === "ready" && !regionDirty) setRegionDraft(regionOf(modelSnapshot.value));
1003
+ }, [modelSnapshot, regionDirty]);
812
1004
  const load = (0, react.useCallback)(async () => {
813
1005
  const info = await operations.describe();
814
1006
  setState(info === void 0 ? { status: "failed" } : {
@@ -845,20 +1037,17 @@ window.__ModuleLoader__.load({
845
1037
  const writable = state.info.writable;
846
1038
  const busy = mutation !== void 0;
847
1039
  const normalized = draft.trim();
848
- const modelFailure = modelSnapshot.status === "ready" ? validateModelCatalog(modelDraft) : void 0;
849
- const modelWritable = modelSnapshot.status === "ready" && modelSnapshot.writable;
1040
+ const canModifyRegion = modelSnapshot.status === "ready" && modelSnapshot.writable;
850
1041
  const currentRegion = regionOf(modelSnapshot.value);
851
1042
  const closeEditor = () => {
852
1043
  setDraft("");
853
- setModelDraft(modelsOf(modelSnapshot.value, regionOf(modelSnapshot.value)));
854
1044
  setRegionDraft(regionOf(modelSnapshot.value));
855
- setModelsDirty(false);
856
1045
  setRegionDirty(false);
857
1046
  setNotice(void 0);
858
1047
  setEditing(false);
859
1048
  };
860
1049
  const save = async () => {
861
- if (busy || !modelsDirty && !regionDirty && normalized.length === 0 || modelsDirty && (!modelWritable || modelFailure !== void 0) || regionDirty && !modelWritable) return;
1050
+ if (busy || !regionDirty && normalized.length === 0 || regionDirty && !canModifyRegion) return;
862
1051
  setMutation("saving");
863
1052
  setNotice(void 0);
864
1053
  if (regionDirty) {
@@ -869,14 +1058,6 @@ window.__ModuleLoader__.load({
869
1058
  }
870
1059
  setRegionDirty(false);
871
1060
  }
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
1061
  if (normalized.length === 0 || await operations.store(normalized)) {
881
1062
  setDraft("");
882
1063
  setNotice("saved");
@@ -957,11 +1138,9 @@ window.__ModuleLoader__.load({
957
1138
  role: "radio",
958
1139
  "aria-checked": regionDraft === "global",
959
1140
  className: `${QoderCredentialCard_module_css_default.regionOption} ${regionDraft === "global" ? QoderCredentialCard_module_css_default.regionOptionActive : ""}`,
960
- disabled: !modelWritable || busy,
1141
+ disabled: !canModifyRegion || busy,
961
1142
  onClick: () => {
962
1143
  setRegionDraft("global");
963
- setModelDraft(modelsOf(modelSnapshot.value, "global"));
964
- setModelsDirty(false);
965
1144
  setRegionDirty(true);
966
1145
  },
967
1146
  children: t("regionGlobal")
@@ -970,11 +1149,9 @@ window.__ModuleLoader__.load({
970
1149
  role: "radio",
971
1150
  "aria-checked": regionDraft === "china",
972
1151
  className: `${QoderCredentialCard_module_css_default.regionOption} ${regionDraft === "china" ? QoderCredentialCard_module_css_default.regionOptionActive : ""}`,
973
- disabled: !modelWritable || busy,
1152
+ disabled: !canModifyRegion || busy,
974
1153
  onClick: () => {
975
1154
  setRegionDraft("china");
976
- setModelDraft(modelsOf(modelSnapshot.value, "china"));
977
- setModelsDirty(false);
978
1155
  setRegionDirty(true);
979
1156
  },
980
1157
  children: t("regionChina")
@@ -1001,34 +1178,6 @@ window.__ModuleLoader__.load({
1001
1178
  role: "alert",
1002
1179
  children: t("readOnly")
1003
1180
  }) : 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
1181
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1033
1182
  className: QoderCredentialCard_module_css_default.actions,
1034
1183
  children: [
@@ -1051,7 +1200,7 @@ window.__ModuleLoader__.load({
1051
1200
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1052
1201
  type: "button",
1053
1202
  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,
1203
+ disabled: busy || !regionDirty && normalized.length === 0 || regionDirty && !canModifyRegion || normalized.length > 0 && !writable,
1055
1204
  onClick: () => {
1056
1205
  save();
1057
1206
  },
@@ -1064,6 +1213,225 @@ window.__ModuleLoader__.load({
1064
1213
  ]
1065
1214
  });
1066
1215
  }
1216
+ //#endregion
1217
+ //#region src/dsh/provider.ts
1218
+ /** DSH model provider route, matching the plugin package name. */
1219
+ const QODER_PROVIDER_ID = "dsh-provider-qoder";
1220
+ //#endregion
1221
+ //#region src/client/context-selection.ts
1222
+ function isQoderProvider(provider) {
1223
+ return [
1224
+ QODER_PROVIDER_ID,
1225
+ "qoder-official",
1226
+ "qoder",
1227
+ "qoder-subscription"
1228
+ ].includes(provider ?? "");
1229
+ }
1230
+ function historicalContextWindow(current, lastUsed, contextWindow) {
1231
+ return current.provider === lastUsed?.provider && current.model === lastUsed?.model ? contextWindow : void 0;
1232
+ }
1233
+ /** Composer selections belong to the current session; model defaults live in settings. */
1234
+ async function saveContextSelection(operations, sessionId, region, modelId, tierKey) {
1235
+ try {
1236
+ return await operations.setSessionTier?.(sessionId, modelId, tierKey, region) ? "saved" : "session-failed";
1237
+ } catch {
1238
+ return "session-failed";
1239
+ }
1240
+ }
1241
+ //#endregion
1242
+ //#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderContextSelect.module.css.mjs
1243
+ 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-menu-surface-fill);min-width:180px;backdrop-filter:var(--dsw-menu-backdrop-filter);border:1px solid var(--dsw-alias-border-l1);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}";
1244
+ const tagId = "dsh-provider-qoder/QoderContextSelect.module.css";
1245
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1246
+ const tag = document.createElement("style");
1247
+ tag.dataset.plugin = "dsh-provider-qoder";
1248
+ tag.dataset.pluginCss = tagId;
1249
+ tag.textContent = css;
1250
+ document.head.appendChild(tag);
1251
+ }
1252
+ var QoderContextSelect_module_css_default = {
1253
+ "check": "hiDrJa_check",
1254
+ "chevron": "hiDrJa_chevron",
1255
+ "chevronOpen": "hiDrJa_chevronOpen",
1256
+ "error": "hiDrJa_error",
1257
+ "fadeIn": "hiDrJa_fadeIn",
1258
+ "icon": "hiDrJa_icon",
1259
+ "label": "hiDrJa_label",
1260
+ "menu": "hiDrJa_menu",
1261
+ "menuHeader": "hiDrJa_menuHeader",
1262
+ "option": "hiDrJa_option",
1263
+ "optionInfo": "hiDrJa_optionInfo",
1264
+ "optionSelected": "hiDrJa_optionSelected",
1265
+ "root": "hiDrJa_root",
1266
+ "tag": "hiDrJa_tag",
1267
+ "trigger": "hiDrJa_trigger",
1268
+ "triggerActive": "hiDrJa_triggerActive"
1269
+ };
1270
+ //#endregion
1271
+ //#region src/client/QoderContextSelect.tsx
1272
+ function QoderContextSelect(props) {
1273
+ const { directory, operations, t, sessionId, useProjection } = props;
1274
+ const [open, setOpen] = (0, react.useState)(false);
1275
+ const [manualTiers, setManualTiers] = (0, react.useState)({});
1276
+ const [pending, setPending] = (0, react.useState)(false);
1277
+ const [error, setError] = (0, react.useState)(null);
1278
+ const saving = (0, react.useRef)(false);
1279
+ const rootRef = (0, react.useRef)(null);
1280
+ const directoryState = (0, react.useSyncExternalStore)((fn) => directory ? directory.subscribe(fn) : () => () => {}, () => directory ? directory.getSnapshot() : null);
1281
+ const modelSnapshot = (0, react.useSyncExternalStore)((fn) => operations ? operations.subscribeModels(fn) : () => () => {}, () => operations ? operations.getModelSnapshot() : null);
1282
+ (0, react.useEffect)(() => {
1283
+ if (!open) return;
1284
+ const handleClickOutside = (e) => {
1285
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
1286
+ };
1287
+ const handleKeyDown = (e) => {
1288
+ if (e.key === "Escape") setOpen(false);
1289
+ };
1290
+ document.addEventListener("mousedown", handleClickOutside);
1291
+ document.addEventListener("keydown", handleKeyDown);
1292
+ return () => {
1293
+ document.removeEventListener("mousedown", handleClickOutside);
1294
+ document.removeEventListener("keydown", handleKeyDown);
1295
+ };
1296
+ }, [open]);
1297
+ const modelProj = useProjection ? useProjection("modelSelection") : void 0;
1298
+ const pressure = useProjection ? useProjection("contextPressure") : void 0;
1299
+ if (!operations) return null;
1300
+ const projectedCurrent = modelProj?.next ?? modelProj?.lastUsed;
1301
+ const current = directoryState?.current ?? (projectedCurrent?.model ? {
1302
+ provider: projectedCurrent.provider ?? "dsh-provider-qoder",
1303
+ model: projectedCurrent.model
1304
+ } : null);
1305
+ if (!current) return null;
1306
+ const region = regionOf(modelSnapshot?.value);
1307
+ const models = modelsOf(modelSnapshot?.value, region);
1308
+ const currentModelId = current.model.includes("/") ? current.model.split("/")[1] : current.model;
1309
+ const currentProviderId = current.provider ?? (current.model.includes("/") ? current.model.split("/")[0] : void 0);
1310
+ const activeModel = models.find((m) => m.id === currentModelId || m.id === current.model);
1311
+ if (!isQoderProvider(currentProviderId) || !activeModel) return null;
1312
+ const tiers = contextTiersOf(activeModel);
1313
+ if (tiers.length <= 1) return null;
1314
+ const sessionKey = sessionId ? JSON.stringify([
1315
+ region,
1316
+ sessionId,
1317
+ activeModel.id
1318
+ ]) : void 0;
1319
+ const historicalWindow = operations.canRestoreContextHistory?.() === false ? void 0 : historicalContextWindow(current, modelProj?.lastUsed, pressure?.contextWindow);
1320
+ const currentTier = resolveContextTier(activeModel, sessionKey ? manualTiers[sessionKey] : void 0, historicalWindow);
1321
+ if (!currentTier) return null;
1322
+ const selectedTierKey = currentTier.key;
1323
+ const selectTier = async (tierKey) => {
1324
+ setOpen(false);
1325
+ if (!sessionId || !sessionKey || saving.current) return;
1326
+ if (tierKey === selectedTierKey && error?.key !== sessionKey) return;
1327
+ saving.current = true;
1328
+ setPending(true);
1329
+ setError(null);
1330
+ try {
1331
+ if (await saveContextSelection(operations, sessionId, region, activeModel.id, tierKey) === "saved") setManualTiers((prev) => ({
1332
+ ...prev,
1333
+ [sessionKey]: tierKey
1334
+ }));
1335
+ else setError({ key: sessionKey });
1336
+ } finally {
1337
+ saving.current = false;
1338
+ setPending(false);
1339
+ }
1340
+ };
1341
+ const defaultSuffix = t ? t("contextTierDefaultSuffix").replace(/[()()]/g, "") : "默认";
1342
+ const selectTitle = t ? t("contextSelectTitle") : "选择上下文大小";
1343
+ const menuHeader = t ? t("contextSelectMenuHeader") : "上下文容量";
1344
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1345
+ className: QoderContextSelect_module_css_default.root,
1346
+ ref: rootRef,
1347
+ children: [
1348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1349
+ type: "button",
1350
+ className: `${QoderContextSelect_module_css_default.trigger} ${open ? QoderContextSelect_module_css_default.triggerActive : ""}`,
1351
+ disabled: pending || !sessionId,
1352
+ "aria-expanded": open,
1353
+ "aria-label": selectTitle,
1354
+ title: `${selectTitle}: ${formatContextTokens(currentTier.tokenCount)}`,
1355
+ onClick: () => setOpen((prev) => !prev),
1356
+ children: [
1357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1358
+ className: QoderContextSelect_module_css_default.icon,
1359
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1360
+ width: "14",
1361
+ height: "14",
1362
+ viewBox: "0 0 16 16",
1363
+ fill: "currentColor",
1364
+ 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" })
1365
+ })
1366
+ }),
1367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1368
+ className: QoderContextSelect_module_css_default.label,
1369
+ children: formatContextTokens(currentTier.tokenCount)
1370
+ }),
1371
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1372
+ className: `${QoderContextSelect_module_css_default.chevron} ${open ? QoderContextSelect_module_css_default.chevronOpen : ""}`,
1373
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1374
+ width: "10",
1375
+ height: "10",
1376
+ viewBox: "0 0 12 12",
1377
+ fill: "none",
1378
+ stroke: "currentColor",
1379
+ strokeWidth: "1.5",
1380
+ strokeLinecap: "round",
1381
+ strokeLinejoin: "round",
1382
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 4.5L6 7.5L9 4.5" })
1383
+ })
1384
+ })
1385
+ ]
1386
+ }),
1387
+ error && error.key === sessionKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1388
+ role: "alert",
1389
+ className: QoderContextSelect_module_css_default.error,
1390
+ children: t ? t("contextSelectFailed") : "contextSelectFailed"
1391
+ }),
1392
+ open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1393
+ className: QoderContextSelect_module_css_default.menu,
1394
+ role: "menu",
1395
+ "aria-label": selectTitle,
1396
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1397
+ className: QoderContextSelect_module_css_default.menuHeader,
1398
+ children: menuHeader
1399
+ }), tiers.map((tier) => {
1400
+ const isSelected = tier.key === selectedTierKey;
1401
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1402
+ type: "button",
1403
+ role: "menuitemradio",
1404
+ "aria-checked": isSelected,
1405
+ className: `${QoderContextSelect_module_css_default.option} ${isSelected ? QoderContextSelect_module_css_default.optionSelected : ""}`,
1406
+ onClick: () => {
1407
+ selectTier(tier.key);
1408
+ },
1409
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1410
+ className: QoderContextSelect_module_css_default.optionInfo,
1411
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatContextTokens(tier.tokenCount) }), tier.isDefault && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1412
+ className: QoderContextSelect_module_css_default.tag,
1413
+ children: defaultSuffix
1414
+ })]
1415
+ }), isSelected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1416
+ className: QoderContextSelect_module_css_default.check,
1417
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1418
+ width: "14",
1419
+ height: "14",
1420
+ viewBox: "0 0 16 16",
1421
+ fill: "none",
1422
+ stroke: "currentColor",
1423
+ strokeWidth: "2",
1424
+ strokeLinecap: "round",
1425
+ strokeLinejoin: "round",
1426
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3.5 8.5L6.5 11.5L12.5 4.5" })
1427
+ })
1428
+ })]
1429
+ }, tier.key);
1430
+ })]
1431
+ })
1432
+ ]
1433
+ });
1434
+ }
1067
1435
  /**
1068
1436
  * Absolute Fetch-route prefix. Connection mounts its shared, authenticated API
1069
1437
  * channel at `/api`, and every exact Fetch route lives directly below it.
@@ -1183,6 +1551,21 @@ window.__ModuleLoader__.load({
1183
1551
  }), "provider-qoder: credential copy");
1184
1552
  const credentials = ctx.remote.credentials;
1185
1553
  const rpc = createQoderRpcCaller();
1554
+ let historyRegion;
1555
+ let historyRegionChanged = false;
1556
+ const readModelSnapshot = () => {
1557
+ const snapshot = modelScope.getSnapshot();
1558
+ if (snapshot.value) {
1559
+ const region = snapshot.value.region ?? "global";
1560
+ if (historyRegion !== void 0 && historyRegion !== region) historyRegionChanged = true;
1561
+ historyRegion = region;
1562
+ }
1563
+ return snapshot;
1564
+ };
1565
+ readModelSnapshot();
1566
+ ctx.effect(() => modelScope.subscribe(() => {
1567
+ readModelSnapshot();
1568
+ }), "provider-qoder: context history region");
1186
1569
  const operations = {
1187
1570
  describe: async () => {
1188
1571
  try {
@@ -1207,14 +1590,16 @@ window.__ModuleLoader__.load({
1207
1590
  }
1208
1591
  },
1209
1592
  getAccount: async (force) => await rpc.call("account", { force }),
1210
- getModelSnapshot: () => modelScope.getSnapshot(),
1593
+ getModelSnapshot: readModelSnapshot,
1594
+ canRestoreContextHistory: () => !historyRegionChanged,
1211
1595
  subscribeModels: (listener) => modelScope.subscribe(listener),
1212
1596
  storeModels: async (region, models) => {
1213
1597
  try {
1214
1598
  const current = modelScope.getSnapshot().value;
1599
+ const clonedModels = models.map(cloneCatalogModel);
1215
1600
  return await modelScope.set("modelsByRegion", {
1216
1601
  ...current?.modelsByRegion,
1217
- [region]: models
1602
+ [region]: clonedModels
1218
1603
  }) !== false;
1219
1604
  } catch {
1220
1605
  return false;
@@ -1235,6 +1620,18 @@ window.__ModuleLoader__.load({
1235
1620
  }
1236
1621
  },
1237
1622
  discoverModels: async () => await rpc.call("models", {}),
1623
+ setSessionTier: async (sessionId, modelId, tierKey, region) => {
1624
+ try {
1625
+ return (await rpc.call("sessionTier", {
1626
+ sessionId,
1627
+ modelId,
1628
+ tierKey,
1629
+ region
1630
+ })).ok;
1631
+ } catch {
1632
+ return false;
1633
+ }
1634
+ },
1238
1635
  subscribe: (listener) => ctx.remote.$on("credentials/reference-updated", (ref) => {
1239
1636
  if (ref === "QODER_MANAGED_CREDENTIAL") listener();
1240
1637
  })
@@ -1271,6 +1668,31 @@ window.__ModuleLoader__.load({
1271
1668
  activeLocale
1272
1669
  })
1273
1670
  }, QoderCredentialCard));
1671
+ ctx.inject([
1672
+ "modelDirectories",
1673
+ "sessions",
1674
+ "remote.session"
1675
+ ], (scope) => {
1676
+ scope.slots.inject("conversation.input.right", () => scope.slots.register({
1677
+ name: "conversation.input.right",
1678
+ id: "qoder-context-select",
1679
+ order: 10,
1680
+ inject: (sessionId) => {
1681
+ let directory = void 0;
1682
+ try {
1683
+ const modelDirectories = scope.get("modelDirectories");
1684
+ directory = sessionId && modelDirectories ? modelDirectories.directoryFor(sessionId)?.store : void 0;
1685
+ } catch {}
1686
+ return {
1687
+ sessionId,
1688
+ directory,
1689
+ operations,
1690
+ t,
1691
+ activeLocale
1692
+ };
1693
+ }
1694
+ }, QoderContextSelect));
1695
+ });
1274
1696
  }
1275
1697
  //#endregion
1276
1698
  exports.apply = apply;