dsh-opencode-go 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -1,8 +1,10 @@
1
1
  window.__ModuleLoader__.load({ id: "dsh-opencode-go", factory: (require) => { var module = { exports: {} }; var exports = module.exports;
2
2
  "use strict";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __export = (target, all) => {
8
10
  for (var name in all)
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/client/index.ts
@@ -28,29 +38,252 @@ __export(index_exports, {
28
38
  module.exports = __toCommonJS(index_exports);
29
39
 
30
40
  // src/client/Section.tsx
41
+ var import_react2 = require("react");
42
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
43
+ var primitives = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
44
+
45
+ // src/client/ModelEditor.tsx
31
46
  var import_react = require("react");
32
47
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
33
48
 
49
+ // src/models-contract.ts
50
+ function validReleaseDate(value) {
51
+ return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
52
+ }
53
+ function isNewModel(model, now = Date.now()) {
54
+ if (model.deprecated || !validReleaseDate(model.releaseDate)) return false;
55
+ const days = Math.floor(now / 864e5) - Date.parse(model.releaseDate) / 864e5;
56
+ return days >= 0 && days < 7;
57
+ }
58
+ function sortModels(models, now = Date.now()) {
59
+ const rank = (model) => model.deprecated ? 2 : isNewModel(model, now) ? 0 : 1;
60
+ return [...models].sort((a, b) => rank(a) - rank(b) || (isNewModel(a, now) && isNewModel(b, now) ? b.releaseDate.localeCompare(a.releaseDate) : 0));
61
+ }
62
+ function parseGoModels(value) {
63
+ if (!Array.isArray(value)) throw new Error("Invalid OpenCode Go model list");
64
+ return value.map((entry) => {
65
+ if (!entry || typeof entry !== "object") throw new Error("Invalid OpenCode Go model");
66
+ const row = entry;
67
+ if (typeof row.id !== "string" || !row.id) throw new Error("Missing OpenCode Go model id");
68
+ const model = { id: row.id };
69
+ if (typeof row.name === "string") model.name = row.name;
70
+ for (const key of ["contextWindow", "maxTokens"]) {
71
+ if (typeof row[key] === "number" && Number.isSafeInteger(row[key]) && row[key] > 0) model[key] = row[key];
72
+ }
73
+ if (typeof row.deprecated === "boolean") model.deprecated = row.deprecated;
74
+ if (validReleaseDate(row.releaseDate)) model.releaseDate = row.releaseDate;
75
+ return model;
76
+ });
77
+ }
78
+ var codec = {
79
+ mode: "strict",
80
+ typeSymbol: "dsh-opencode-go#GoModels",
81
+ schema: { parse: parseGoModels },
82
+ create: () => ({ parse: parseGoModels })
83
+ };
84
+ var modelsRemote = {
85
+ package: "dsh-opencode-go",
86
+ descriptors: [{
87
+ id: "dsh-opencode-go#opencodeGoModels/read",
88
+ service: "opencodeGoModels",
89
+ namespace: "opencodeGoModels",
90
+ method: "read",
91
+ invocation: { kind: "direct" },
92
+ parameters: [],
93
+ result: codec
94
+ }]
95
+ };
96
+
34
97
  // plugin-css:/Users/tanglei/Desktop/dsh-opencode-go/src/client/Section.module.css
35
98
  var id = "dsh-opencode-go/Section.module.css";
36
99
  if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")) {
37
100
  const style = document.createElement("style");
38
101
  style.dataset.plugin = "dsh-opencode-go";
39
102
  style.dataset.pluginCss = id;
40
- style.textContent = ".BtKYva_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.BtKYva_field+.BtKYva_field{border-top:.5px solid var(--dsw-alias-border-l2)}.BtKYva_head{align-items:center;gap:8px;display:flex}.BtKYva_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.BtKYva_badges{align-items:center;gap:8px;display:inline-flex}.BtKYva_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.BtKYva_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.BtKYva_reset:disabled{cursor:default}.BtKYva_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.BtKYva_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.BtKYva_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.BtKYva_inputInvalid{border-color:var(--dsw-alias-label-error);}.BtKYva_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.BtKYva_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.BtKYva_intro{color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-size:13px;line-height:1.5}.BtKYva_disclosure{font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;margin:0;padding:0;display:flex}.BtKYva_disclosure:hover .BtKYva_label{color:var(--dsw-alias-brand-primary)}.BtKYva_chevron,.BtKYva_chevronOpen{color:var(--dsw-alias-label-tertiary);flex:none}.BtKYva_chevron{transform:rotate(-90deg)}.BtKYva_advanced{flex-direction:column;gap:4px;padding:4px 0 0 22px;display:flex}.BtKYva_advanced .BtKYva_field+.BtKYva_field{border-top:none}.BtKYva_chips{flex-wrap:wrap;gap:6px;display:flex}.BtKYva_actions{align-items:center;gap:8px;padding-top:12px;display:flex}.BtKYva_failedNote{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}";
103
+ style.textContent = '.BtKYva_page{flex-direction:column;min-width:0;height:100%;min-height:0;display:flex;container-type:size}.BtKYva_pageBody{scrollbar-gutter:stable;flex:1;min-height:0;padding-right:4px;overflow-y:auto}.BtKYva_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.BtKYva_field+.BtKYva_field{border-top:.5px solid var(--dsw-alias-border-l2)}.BtKYva_head{align-items:center;gap:8px;display:flex}.BtKYva_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.BtKYva_badges{align-items:center;gap:8px;display:inline-flex}.BtKYva_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.BtKYva_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.BtKYva_reset:disabled{cursor:default}.BtKYva_reset:focus-visible,.BtKYva_disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.BtKYva_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.BtKYva_input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-color:var(--dsw-alias-brand-primary)}.BtKYva_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.BtKYva_inputInvalid{border-color:var(--dsw-alias-label-error);}.BtKYva_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.BtKYva_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.BtKYva_intro{color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-size:13px;line-height:1.5}.BtKYva_disclosure{font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;margin:0;padding:0;display:flex}.BtKYva_disclosure:hover .BtKYva_label{color:var(--dsw-alias-brand-primary)}.BtKYva_chevron,.BtKYva_chevronOpen{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none}.BtKYva_chevron{transform:rotate(-90deg)}.BtKYva_advanced{flex-direction:column;gap:4px;padding:4px 0 0 22px;display:flex}.BtKYva_advanced .BtKYva_field+.BtKYva_field{border-top:none}.BtKYva_chips{flex-wrap:wrap;gap:6px;display:flex}.BtKYva_limitsEditor{flex-direction:column;gap:10px;padding-top:4px;display:flex}.BtKYva_limitsToolbar{grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:6px 10px;display:grid}.BtKYva_limitsFilterLabel{color:var(--dsw-alias-label-secondary);grid-column:1/-1;font-size:12px;font-weight:500;line-height:1.5}.BtKYva_limitsSummary{color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-bottom:8px;font-size:12px;line-height:1.5}.BtKYva_limitsId,.BtKYva_limitsModelId{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.BtKYva_limitsId{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.4}.BtKYva_limitsModelId{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.4}.BtKYva_visuallyHidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width<=560px){.BtKYva_limitsToolbar{grid-template-columns:1fr}.BtKYva_limitsSummary{padding-bottom:0}}.BtKYva_actions{border-top:.5px solid var(--dsw-alias-border-l2);flex-wrap:wrap;flex:none;align-items:center;gap:8px;padding:12px 0;display:flex}.BtKYva_failedNote{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.BtKYva_pageTop{align-items:flex-start;gap:16px;display:flex}.BtKYva_pageTop .BtKYva_intro{flex:1}.BtKYva_advancedTrigger{flex-shrink:0;margin-left:auto;}.BtKYva_advancedTrigger .BtKYva_label{color:var(--dsw-alias-label-tertiary);font-size:12px}.BtKYva_filters{flex-wrap:wrap;gap:6px;display:flex}.BtKYva_filter{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;padding:6px 9px;font-size:12px}.BtKYva_filter[aria-pressed=true]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3)}.BtKYva_filter span{color:var(--dsw-alias-label-tertiary);margin-left:4px}.BtKYva_modelLayout{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;grid-template-columns:minmax(0,43fr) minmax(0,57fr);height:clamp(240px,100cqh - 460px,480px);display:grid;overflow:hidden}.BtKYva_modelList{border-right:.5px solid var(--dsw-alias-border-l2);min-height:0;padding:6px;overflow-y:auto}.BtKYva_modelChoice{text-align:left;width:100%;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:6px;padding:12px 9px;display:block}.BtKYva_modelChoice[aria-pressed=true],.BtKYva_modelChoice:hover{background:var(--dsw-alias-bg-layer-3)}.BtKYva_modelName{overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:5px;font-size:13px;display:flex}.BtKYva_modelDetails{flex-direction:column;align-items:stretch;gap:10px;min-width:0;padding:18px;display:flex;overflow-y:auto}.BtKYva_modelDetails h3{overflow-wrap:anywhere;margin:0;font-size:16px}.BtKYva_modelHeading{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.BtKYva_modelDetails .BtKYva_field{border:0;padding:8px 0}.BtKYva_modelDetails .BtKYva_input{box-sizing:border-box;width:100%;min-width:0}.BtKYva_modelDetails .BtKYva_reset{align-self:flex-start}.BtKYva_newBadge{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:4px;padding:2px 4px;font-size:9px}.BtKYva_releaseDate{color:var(--dsw-alias-label-tertiary);margin-top:4px;font-size:10px;display:block}.BtKYva_modelChoice:focus-visible,.BtKYva_filter:focus-visible,.BtKYva_advancedTrigger:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}@container (width<=440px){.BtKYva_pageTop{flex-wrap:wrap}.BtKYva_modelLayout{grid-template-columns:minmax(0,1fr);height:auto}.BtKYva_modelList{border-right:0;border-bottom:.5px solid var(--dsw-alias-border-l2);max-height:240px}.BtKYva_modelDetails{max-height:460px}}.BtKYva_keySection{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px 0}.BtKYva_keySection summary{cursor:pointer;list-style:none}.BtKYva_keySection summary:after{content:"\u2304";color:var(--dsw-alias-label-tertiary)}.BtKYva_keySection summary::-webkit-details-marker{display:none}.BtKYva_keySection>.BtKYva_input{box-sizing:border-box;width:100%;margin:10px 0 6px}.BtKYva_keySection summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}';
41
104
  document.head.appendChild(style);
42
105
  }
43
- var Section_default = { "failedNote": "BtKYva_failedNote", "intro": "BtKYva_intro", "badges": "BtKYva_badges", "head": "BtKYva_head", "input": "BtKYva_input", "field": "BtKYva_field", "label": "BtKYva_label", "invalid": "BtKYva_invalid", "chevronOpen": "BtKYva_chevronOpen", "chips": "BtKYva_chips", "actions": "BtKYva_actions", "chevron": "BtKYva_chevron", "disclosure": "BtKYva_disclosure", "reset": "BtKYva_reset", "hint": "BtKYva_hint", "inputInvalid": "BtKYva_inputInvalid", "advanced": "BtKYva_advanced" };
106
+ var Section_default = { "modelName": "BtKYva_modelName", "modelDetails": "BtKYva_modelDetails", "advanced": "BtKYva_advanced", "limitsEditor": "BtKYva_limitsEditor", "head": "BtKYva_head", "failedNote": "BtKYva_failedNote", "hint": "BtKYva_hint", "actions": "BtKYva_actions", "limitsFilterLabel": "BtKYva_limitsFilterLabel", "limitsId": "BtKYva_limitsId", "limitsToolbar": "BtKYva_limitsToolbar", "modelChoice": "BtKYva_modelChoice", "disclosure": "BtKYva_disclosure", "chevron": "BtKYva_chevron", "chevronOpen": "BtKYva_chevronOpen", "input": "BtKYva_input", "modelHeading": "BtKYva_modelHeading", "filter": "BtKYva_filter", "inputInvalid": "BtKYva_inputInvalid BtKYva_input", "page": "BtKYva_page", "label": "BtKYva_label", "reset": "BtKYva_reset", "badges": "BtKYva_badges", "advancedTrigger": "BtKYva_advancedTrigger BtKYva_disclosure", "visuallyHidden": "BtKYva_visuallyHidden", "keySection": "BtKYva_keySection", "limitsSummary": "BtKYva_limitsSummary", "invalid": "BtKYva_invalid", "field": "BtKYva_field", "pageTop": "BtKYva_pageTop", "modelLayout": "BtKYva_modelLayout", "newBadge": "BtKYva_newBadge", "filters": "BtKYva_filters", "releaseDate": "BtKYva_releaseDate", "chips": "BtKYva_chips", "limitsModelId": "BtKYva_limitsModelId", "pageBody": "BtKYva_pageBody", "modelList": "BtKYva_modelList", "intro": "BtKYva_intro" };
44
107
 
45
- // src/client/Section.tsx
108
+ // src/client/ModelEditor.tsx
46
109
  var import_jsx_runtime = require("react/jsx-runtime");
47
- function ValueField(props) {
48
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
110
+ function hasCapacityOverride(limit) {
111
+ return limit?.contextWindow != null || limit?.maxTokens != null;
112
+ }
113
+ function ModelEditor({ models, draft, t, disabled, onEdit }) {
114
+ const [query, setQuery] = (0, import_react.useState)("");
115
+ const [filter, setFilter] = (0, import_react.useState)("all");
116
+ const [selected, setSelected] = (0, import_react.useState)();
117
+ const [now, setNow] = (0, import_react.useState)(Date.now);
118
+ (0, import_react.useEffect)(() => {
119
+ const timer = setInterval(() => {
120
+ setNow(Date.now());
121
+ }, 6e4);
122
+ return () => {
123
+ clearInterval(timer);
124
+ };
125
+ }, []);
126
+ const all = sortModels(models.status === "ready" ? models.entries : [], now);
127
+ const normalized = query.trim().toLocaleLowerCase();
128
+ const entries = all.filter((model2) => `${model2.name ?? ""} ${model2.id}`.toLocaleLowerCase().includes(normalized) && (filter === "all" || filter === "new" && isNewModel(model2, now) || filter === "custom" && hasCapacityOverride(draft[model2.id]) || filter === "deprecated" && model2.deprecated));
129
+ const model = entries.find((entry) => entry.id === selected) ?? entries[0];
130
+ const customized = all.filter((entry) => hasCapacityOverride(draft[entry.id])).length;
131
+ const filters = [
132
+ ["all", "filterAll", all.length],
133
+ ["new", "filterNew", all.filter((entry) => isNewModel(entry, now)).length],
134
+ ["custom", "filterCustom", customized],
135
+ ["deprecated", "filterDeprecated", all.filter((entry) => entry.deprecated).length]
136
+ ];
137
+ const write = (id3, field, value) => {
138
+ const current = draft[id3] === null ? { contextWindow: null, maxTokens: null } : draft[id3] ?? {};
139
+ onEdit({ ...draft, [id3]: { ...current, [field]: value ?? null } });
140
+ };
141
+ const badges = (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
142
+ isNewModel(entry, now) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.newBadge, title: t("newHint"), children: "NEW" }) : null,
143
+ entry.deprecated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "warning", children: t("deprecatedBadge") }) : null
144
+ ] });
145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.limitsEditor, children: [
146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.visuallyHidden, htmlFor: "opencode-go-model-filter", children: t("limitsFilterLabel") }),
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
148
+ "input",
149
+ {
150
+ id: "opencode-go-model-filter",
151
+ className: Section_default.input,
152
+ type: "search",
153
+ autoComplete: "off",
154
+ placeholder: t("limitsFilterPlaceholder"),
155
+ value: query,
156
+ onChange: (event) => {
157
+ setQuery(event.target.value);
158
+ }
159
+ }
160
+ ),
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Section_default.filters, role: "group", "aria-label": t("filterLabel"), children: filters.map(([key, label, count]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
162
+ "button",
163
+ {
164
+ type: "button",
165
+ className: Section_default.filter,
166
+ "aria-pressed": filter === key,
167
+ onClick: () => {
168
+ setFilter(key);
169
+ },
170
+ children: [
171
+ t(label),
172
+ " ",
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: count })
174
+ ]
175
+ },
176
+ key
177
+ )) }),
178
+ model ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.modelLayout, children: [
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { className: Section_default.modelList, "aria-label": t("modelsLabel"), children: entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ className: Section_default.modelChoice,
184
+ "aria-pressed": entry.id === model.id,
185
+ onClick: () => {
186
+ setSelected(entry.id);
187
+ },
188
+ children: [
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: Section_default.modelName, children: [
190
+ entry.name ?? entry.id,
191
+ " ",
192
+ badges(entry)
193
+ ] }),
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: Section_default.limitsModelId, translate: "no", children: entry.id }),
195
+ isNewModel(entry, now) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.releaseDate, children: t("releasedOn", { date: entry.releaseDate }) }) : null
196
+ ]
197
+ },
198
+ entry.id
199
+ )) }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: Section_default.modelDetails, "aria-label": t("modelDetails"), children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.modelHeading, children: [
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: model.name ?? model.id }),
203
+ badges(model)
204
+ ] }),
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: Section_default.limitsModelId, translate: "no", children: model.id }),
206
+ model.deprecated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("deprecatedHint") }) : null,
207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Capacity, { model, field: "contextWindow", limit: draft[model.id], t, disabled, onChange: write }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Capacity, { model, field: "maxTokens", limit: draft[model.id], t, disabled, onChange: write }),
209
+ hasCapacityOverride(draft[model.id]) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
210
+ "button",
211
+ {
212
+ type: "button",
213
+ className: Section_default.reset,
214
+ disabled,
215
+ onClick: () => {
216
+ onEdit({ ...draft, [model.id]: null });
217
+ },
218
+ children: t("limitsResetModel")
219
+ }
220
+ ) : null,
221
+ model.releaseDate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("releaseSource", { date: model.releaseDate }) }) : null,
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("limitsHint") })
223
+ ] })
224
+ ] }) : models.status === "ready" && all.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("limitsNoMatches", { query }) }) : null,
49
225
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
50
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.label, htmlFor: props.id, children: props.label }),
51
- props.field.overridden ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: Section_default.badges, children: [
52
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: props.overriddenLabel }),
53
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
226
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.limitsSummary, children: t("limitsSummary", { count: customized }) }),
227
+ Object.values(draft).some(hasCapacityOverride) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
228
+ "button",
229
+ {
230
+ type: "button",
231
+ className: Section_default.reset,
232
+ disabled,
233
+ onClick: () => {
234
+ onEdit(Object.fromEntries(Object.keys(draft).map((id3) => [id3, null])));
235
+ },
236
+ children: t("limitsResetAll")
237
+ }
238
+ ) : null
239
+ ] })
240
+ ] });
241
+ }
242
+ function Capacity({ model, field, limit, disabled, t, onChange }) {
243
+ const id3 = `opencode-go-${field}-${encodeURIComponent(model.id)}`;
244
+ const defaultValue = model[field];
245
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.label, htmlFor: id3, children: t(field === "contextWindow" ? "limitsContext" : "limitsOutput") }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
248
+ "input",
249
+ {
250
+ id: id3,
251
+ className: Section_default.input,
252
+ type: "number",
253
+ min: 1,
254
+ step: 1,
255
+ inputMode: "numeric",
256
+ "aria-label": t(field === "contextWindow" ? "limitsContextLabel" : "limitsOutputLabel", { name: model.name ?? model.id }),
257
+ "aria-describedby": `${id3}-default`,
258
+ value: limit?.[field] ?? "",
259
+ placeholder: defaultValue === void 0 ? t("capacityMissing") : String(defaultValue),
260
+ disabled,
261
+ onChange: (event) => {
262
+ const text = event.target.value.trim();
263
+ const value = Number(text);
264
+ if (text === "") onChange(model.id, field, void 0);
265
+ else if (Number.isSafeInteger(value) && value > 0) onChange(model.id, field, value);
266
+ }
267
+ }
268
+ ),
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { id: `${id3}-default`, className: Section_default.hint, children: [
270
+ t("capacityDefault", { value: defaultValue === void 0 ? t("capacityMissing") : defaultValue.toLocaleString("en-US") }),
271
+ limit?.[field] != null ? ` \xB7 ${t("overridden")}` : ""
272
+ ] })
273
+ ] });
274
+ }
275
+
276
+ // src/client/Section.tsx
277
+ var import_jsx_runtime2 = require("react/jsx-runtime");
278
+ var ChevronDown = primitives.IconChevronDownOutlineRegular ?? primitives.IconChevronDownOutline14;
279
+ function ValueField(props) {
280
+ const hintId = `${props.id}-hint`;
281
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.field, children: [
282
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.head, children: [
283
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: Section_default.label, htmlFor: props.id, children: props.label }),
284
+ props.field.overridden ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: Section_default.badges, children: [
285
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Tag, { tone: "neutral", children: props.overriddenLabel }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
54
287
  "button",
55
288
  {
56
289
  type: "button",
@@ -62,12 +295,15 @@ function ValueField(props) {
62
295
  )
63
296
  ] }) : null
64
297
  ] }),
65
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
298
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
66
299
  "input",
67
300
  {
68
301
  id: props.id,
302
+ name: props.id,
303
+ autoComplete: "off",
304
+ "aria-describedby": hintId,
69
305
  className: props.field.invalid ? Section_default.inputInvalid : Section_default.input,
70
- type: "text",
306
+ type: props.numeric === true ? "number" : "text",
71
307
  ...props.numeric === true ? { inputMode: "numeric" } : {},
72
308
  ...props.field.invalid ? { "aria-invalid": true } : {},
73
309
  value: props.field.text,
@@ -77,24 +313,24 @@ function ValueField(props) {
77
313
  }
78
314
  }
79
315
  ),
80
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: props.field.invalid ? Section_default.invalid : Section_default.hint, children: props.field.invalid ? props.invalidLabel : props.hint })
316
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { id: hintId, className: props.field.invalid ? Section_default.invalid : Section_default.hint, children: props.field.invalid ? props.invalidLabel : props.hint })
81
317
  ] });
82
318
  }
83
319
  function ModelsBody({ models, t }) {
84
320
  if (models.status === "failed") {
85
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
86
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.failedNote, children: t("modelsFailed") }),
87
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: models.message })
321
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
322
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t("modelsFailed") }),
323
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: models.message })
88
324
  ] });
89
325
  }
90
- if (models.status !== "ready") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("modelsLoading") });
91
- if (models.count === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("modelsEmpty") });
92
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Section_default.chips, children: models.preview.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "outline", children: name }, name)) });
326
+ if (models.status !== "ready") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, role: "status", "aria-live": "polite", children: t("modelsLoading") });
327
+ if (models.count === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: t("modelsEmpty") });
328
+ return null;
93
329
  }
94
330
  function OpencodeGoSection(props) {
95
- const { useOpencodeGo, edit, resetField, save, discard, loadModels, setEnabled, t } = props;
96
- if (useOpencodeGo === void 0 || edit === void 0 || resetField === void 0 || save === void 0 || discard === void 0 || loadModels === void 0 || setEnabled === void 0 || t === void 0) return null;
97
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
331
+ const { useOpencodeGo, edit, resetField, save, discard, loadModels, setEnabled, setShowDeprecatedModels, t } = props;
332
+ if (useOpencodeGo === void 0 || edit === void 0 || resetField === void 0 || save === void 0 || discard === void 0 || loadModels === void 0 || setEnabled === void 0 || setShowDeprecatedModels === void 0 || t === void 0) return null;
333
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
98
334
  Loaded,
99
335
  {
100
336
  state: useOpencodeGo((snapshot) => snapshot),
@@ -104,18 +340,19 @@ function OpencodeGoSection(props) {
104
340
  save,
105
341
  discard,
106
342
  loadModels,
107
- setEnabled
343
+ setEnabled,
344
+ setShowDeprecatedModels
108
345
  }
109
346
  );
110
347
  }
111
348
  function Loaded(props) {
112
349
  const { t, state, loadModels } = props;
113
- const [advanced, setAdvanced] = (0, import_react.useState)(false);
114
- (0, import_react.useEffect)(() => {
350
+ const [advanced, setAdvanced] = (0, import_react2.useState)(false);
351
+ (0, import_react2.useEffect)(() => {
115
352
  if (state.available && state.models.status === "idle") loadModels();
116
353
  }, [state.available, state.models.status, loadModels]);
117
354
  if (!state.available) {
118
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.intro, children: t("unavailable") });
355
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.intro, children: t("unavailable") });
119
356
  }
120
357
  const disabled = !state.writable;
121
358
  const fieldProps = {
@@ -125,84 +362,31 @@ function Loaded(props) {
125
362
  disabled
126
363
  };
127
364
  const advancedOverridden = state.apiKeyEnv.overridden || state.baseURL.overridden || state.refreshMinutes.overridden || state.streamIdleTimeoutMs.overridden || state.maxRequestImageBytes.overridden || state.requestImagePixelBudget.overridden || state.requestImageMaxBytes.overridden;
128
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
129
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.intro, children: t("intro") }),
130
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
131
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
132
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("enabledLabel") }),
133
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
134
- import_dsh_client_ui_primitives.Switch,
365
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.page, children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.pageBody, children: [
367
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.pageTop, children: [
368
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.intro, children: t("intro") }),
369
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
370
+ "button",
135
371
  {
136
- checked: state.enabled,
137
- label: t("enabledLabel"),
138
- disabled,
139
- onChange: props.setEnabled
372
+ type: "button",
373
+ className: Section_default.advancedTrigger,
374
+ "aria-expanded": advanced,
375
+ "aria-controls": "opencode-go-advanced",
376
+ onClick: () => {
377
+ setAdvanced(!advanced);
378
+ },
379
+ children: [
380
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronDown, { className: advanced ? Section_default.chevronOpen : Section_default.chevron }),
381
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: Section_default.label, children: t("advancedLabel") }),
382
+ advancedOverridden ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Tag, { tone: "neutral", children: t("overridden") }) : null
383
+ ]
140
384
  }
141
385
  )
142
386
  ] }),
143
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: state.enabled ? t("enabledHint") : t("enabledOff") })
144
- ] }),
145
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
146
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
147
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.label, htmlFor: "opencode-go-key", children: t("keyLabel") }),
148
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.badges, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: state.apiKeyConfigured ? "success" : "warning", children: state.apiKeyConfigured ? t("keyConfigured") : t("keyMissing") }) })
149
- ] }),
150
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
151
- "input",
152
- {
153
- id: "opencode-go-key",
154
- className: Section_default.input,
155
- type: "password",
156
- autoComplete: "off",
157
- value: state.apiKey.text,
158
- disabled: !state.apiKeyWritable,
159
- onChange: (event) => {
160
- props.edit("apiKey", event.target.value);
161
- }
162
- }
163
- ),
164
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: state.apiKeyWritable ? t("keyHint") : t("keyNotWritable") })
165
- ] }),
166
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
167
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
168
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("modelsLabel") }),
169
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: Section_default.badges, children: [
170
- state.models.status === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: t("modelsCount", { count: state.models.count }) }) : null,
171
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
172
- "button",
173
- {
174
- type: "button",
175
- className: Section_default.reset,
176
- disabled: state.models.status === "loading",
177
- onClick: loadModels,
178
- children: t("modelsRefresh")
179
- }
180
- )
181
- ] })
182
- ] }),
183
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModelsBody, { models: state.models, t })
184
- ] }),
185
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
186
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
187
- "button",
188
- {
189
- type: "button",
190
- className: Section_default.disclosure,
191
- "aria-expanded": advanced,
192
- "aria-controls": "opencode-go-advanced",
193
- onClick: () => {
194
- setAdvanced(!advanced);
195
- },
196
- children: [
197
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutline14, { className: advanced ? Section_default.chevronOpen : Section_default.chevron }),
198
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("advancedLabel") }),
199
- advancedOverridden ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: t("overridden") }) : null
200
- ]
201
- }
202
- ),
203
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("advancedHint") }),
204
- advanced ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { id: "opencode-go-advanced", className: Section_default.advanced, children: [
205
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
387
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: advanced ? Section_default.field : void 0, children: advanced ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { id: "opencode-go-advanced", className: Section_default.advanced, children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: t("advancedHint") }),
389
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
206
390
  ValueField,
207
391
  {
208
392
  id: "opencode-go-api-key-env",
@@ -218,7 +402,7 @@ function Loaded(props) {
218
402
  }
219
403
  }
220
404
  ),
221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
222
406
  ValueField,
223
407
  {
224
408
  id: "opencode-go-base-url",
@@ -234,7 +418,7 @@ function Loaded(props) {
234
418
  }
235
419
  }
236
420
  ),
237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
238
422
  ValueField,
239
423
  {
240
424
  id: "opencode-go-refresh-minutes",
@@ -251,7 +435,7 @@ function Loaded(props) {
251
435
  }
252
436
  }
253
437
  ),
254
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
438
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
255
439
  ValueField,
256
440
  {
257
441
  id: "opencode-go-stream-idle",
@@ -268,7 +452,7 @@ function Loaded(props) {
268
452
  }
269
453
  }
270
454
  ),
271
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
455
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
272
456
  ValueField,
273
457
  {
274
458
  id: "opencode-go-max-request-image-bytes",
@@ -285,7 +469,7 @@ function Loaded(props) {
285
469
  }
286
470
  }
287
471
  ),
288
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
472
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
289
473
  ValueField,
290
474
  {
291
475
  id: "opencode-go-image-pixel-budget",
@@ -302,7 +486,7 @@ function Loaded(props) {
302
486
  }
303
487
  }
304
488
  ),
305
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
489
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
306
490
  ValueField,
307
491
  {
308
492
  id: "opencode-go-image-max-bytes",
@@ -319,14 +503,100 @@ function Loaded(props) {
319
503
  }
320
504
  }
321
505
  )
322
- ] }) : null
323
- ] }),
324
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.actions, children: [
325
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", size: "md", disabled: disabled || !state.dirty || state.invalid || state.saving, onClick: props.save, children: state.saving ? t("saving") : t("save") }),
326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "md", disabled: disabled || !state.dirty || state.saving, onClick: props.discard, children: t("discard") }),
327
- state.failed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.failedNote, children: t("savedFailed") }) : null
506
+ ] }) : null }),
507
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.field, children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.head, children: [
509
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: Section_default.label, children: t("enabledLabel") }),
510
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
511
+ import_dsh_client_ui_primitives2.Switch,
512
+ {
513
+ checked: state.enabled,
514
+ label: t("enabledLabel"),
515
+ disabled,
516
+ onChange: props.setEnabled
517
+ }
518
+ )
519
+ ] }),
520
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: state.enabled ? t("enabledHint") : t("enabledOff") })
521
+ ] }),
522
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: Section_default.keySection, open: !state.apiKeyConfigured || state.apiKey.text.length > 0, children: [
523
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("summary", { className: Section_default.head, children: [
524
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: Section_default.label, children: t("keyLabel") }),
525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: Section_default.badges, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Tag, { tone: state.apiKeyConfigured ? "success" : "warning", children: state.apiKeyConfigured ? t("keyConfigured") : t("keyMissing") }) })
526
+ ] }),
527
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
528
+ "input",
529
+ {
530
+ id: "opencode-go-key",
531
+ "aria-label": t("keyLabel"),
532
+ name: "api-key",
533
+ className: Section_default.input,
534
+ type: "password",
535
+ autoComplete: "off",
536
+ "aria-describedby": "opencode-go-key-hint",
537
+ value: state.apiKey.text,
538
+ disabled: !state.apiKeyWritable,
539
+ onChange: (event) => {
540
+ props.edit("apiKey", event.target.value);
541
+ }
542
+ }
543
+ ),
544
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { id: "opencode-go-key-hint", className: Section_default.hint, children: state.apiKeyWritable ? t("keyHint") : t("keyNotWritable") })
545
+ ] }),
546
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.field, children: [
547
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.head, children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: Section_default.label, children: t("modelsLabel") }),
549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: Section_default.badges, children: [
550
+ state.models.status === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Tag, { tone: "neutral", children: t("modelsCount", { count: state.models.count }) }) : null,
551
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
552
+ "button",
553
+ {
554
+ type: "button",
555
+ className: Section_default.reset,
556
+ disabled: state.models.status === "loading",
557
+ onClick: loadModels,
558
+ children: t("modelsRefresh")
559
+ }
560
+ )
561
+ ] })
562
+ ] }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ModelsBody, { models: state.models, t })
564
+ ] }),
565
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.field, children: [
566
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.head, children: [
567
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: Section_default.label, children: t("showDeprecatedLabel") }),
568
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
569
+ import_dsh_client_ui_primitives2.Switch,
570
+ {
571
+ checked: state.showDeprecatedModels,
572
+ label: t("showDeprecatedLabel"),
573
+ disabled: disabled || state.pickerSaving,
574
+ onChange: props.setShowDeprecatedModels
575
+ }
576
+ )
577
+ ] }),
578
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: t("showDeprecatedHint") }),
579
+ state.pickerFailed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t("pickerFailed") }) : null,
580
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
581
+ ModelEditor,
582
+ {
583
+ models: state.models,
584
+ draft: state.modelLimitDraft,
585
+ t,
586
+ disabled: disabled || state.saving,
587
+ onEdit: (next) => {
588
+ props.edit("modelLimits", JSON.stringify(next));
589
+ }
590
+ }
591
+ )
592
+ ] }),
593
+ disabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: t("readOnly") }) : null
328
594
  ] }),
329
- disabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("readOnly") }) : null
595
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.actions, children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", size: "md", disabled: disabled || !state.dirty || state.invalid || state.saving, onClick: props.save, children: state.saving ? t("saving") : t("save") }),
597
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "outline", size: "md", disabled: disabled || !state.dirty || state.saving, onClick: props.discard, children: t("discard") }),
598
+ state.failed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, children: t("savedFailed") }) : null
599
+ ] })
330
600
  ] });
331
601
  }
332
602
 
@@ -365,6 +635,21 @@ function textField(field) {
365
635
  }
366
636
  };
367
637
  }
638
+ function jsonField(field) {
639
+ return {
640
+ field,
641
+ format: (value) => value === void 0 || value === null ? "" : JSON.stringify(value, void 0, 2),
642
+ parse: (text) => {
643
+ const trimmed = text.trim();
644
+ if (trimmed === "") return { kind: "clear" };
645
+ try {
646
+ return { kind: "set", value: JSON.parse(trimmed) };
647
+ } catch {
648
+ return void 0;
649
+ }
650
+ }
651
+ };
652
+ }
368
653
  var StagedForm = class {
369
654
  /**
370
655
  * @param scope - the bound settings scope for this page's namespace.
@@ -375,7 +660,7 @@ var StagedForm = class {
375
660
  this.scope = scope;
376
661
  this.specs = new Map(specs.map((spec) => [spec.field, spec]));
377
662
  this.secretSpecs = new Map(secrets.map((spec) => [spec.field, spec]));
378
- scope.subscribe(() => {
663
+ this.unsubscribe = scope.subscribe(() => {
379
664
  this.publish();
380
665
  });
381
666
  }
@@ -383,8 +668,14 @@ var StagedForm = class {
383
668
  secretSpecs;
384
669
  staged = /* @__PURE__ */ new Map();
385
670
  listeners = /* @__PURE__ */ new Set();
671
+ unsubscribe;
386
672
  saving = false;
387
673
  failed = false;
674
+ /** Shared 0.1.7 forms outlive individual settings pages. */
675
+ dispose() {
676
+ this.unsubscribe();
677
+ this.listeners.clear();
678
+ }
388
679
  /**
389
680
  * Publish a projection of this form, rebuilt whenever the scope or a draft changes.
390
681
  * @param project - build the page state from the form's current reads.
@@ -515,7 +806,7 @@ var StagedForm = class {
515
806
  }
516
807
  async store(field, value) {
517
808
  await this.scope.set(field, value);
518
- return this.userLayer()?.[field] === value;
809
+ return sameJsonValue(this.userLayer()?.[field], value);
519
810
  }
520
811
  stage(field, edit) {
521
812
  this.staged.set(field, edit);
@@ -547,6 +838,19 @@ var StagedForm = class {
547
838
  for (const listener of this.listeners) listener();
548
839
  }
549
840
  };
841
+ function sameJsonValue(left, right) {
842
+ if (Object.is(left, right)) return true;
843
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
844
+ if (Array.isArray(left) || Array.isArray(right)) {
845
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
846
+ return left.every((value, index) => sameJsonValue(value, right[index]));
847
+ }
848
+ const leftRecord = left;
849
+ const rightRecord = right;
850
+ const leftKeys = Object.keys(leftRecord);
851
+ const rightKeys = Object.keys(rightRecord);
852
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(rightRecord, key) && sameJsonValue(leftRecord[key], rightRecord[key]));
853
+ }
550
854
 
551
855
  // src/client/section-controller.ts
552
856
  var OPENCODE_GO_NS = "llm-opencode-go";
@@ -559,9 +863,10 @@ var OpencodeGoSectionController = class {
559
863
  * @param ctx - the page plugin's context, whose `remote.credentials` namespace
560
864
  * answers for the credential the section references.
561
865
  */
562
- constructor(scope, ctx) {
866
+ constructor(scope, ctx, readModels = () => ctx.remote.llm.discoverModels(OPENCODE_GO_NS, { provider: OPENCODE_GO_PROVIDER })) {
563
867
  this.scope = scope;
564
868
  this.ctx = ctx;
869
+ this.readModels = readModels;
565
870
  this.form = new StagedForm(
566
871
  scope,
567
872
  [
@@ -574,12 +879,13 @@ var OpencodeGoSectionController = class {
574
879
  numberField("streamIdleTimeoutMs"),
575
880
  numberField("maxRequestImageBytes"),
576
881
  numberField("requestImagePixelBudget"),
577
- numberField("requestImageMaxBytes")
882
+ numberField("requestImageMaxBytes"),
883
+ jsonField("modelLimits")
578
884
  ],
579
885
  [{ field: API_KEY_FIELD, write: (text) => this.writeKey(text) }]
580
886
  );
581
887
  this.store = this.form.bind(() => this.projection());
582
- scope.subscribe(() => {
888
+ this.unsubscribe = scope.subscribe(() => {
583
889
  void this.readCredential();
584
890
  });
585
891
  void this.readCredential();
@@ -589,11 +895,23 @@ var OpencodeGoSectionController = class {
589
895
  credential = { ref: "", configured: false, writable: true };
590
896
  models = { status: "idle" };
591
897
  modelsRequest = 0;
898
+ pickerSaving = false;
899
+ pickerFailed = false;
592
900
  face;
901
+ unsubscribe;
902
+ /** Release subscriptions without disposing the host's shared form. */
903
+ dispose() {
904
+ this.modelsRequest++;
905
+ this.unsubscribe();
906
+ this.form.dispose();
907
+ }
593
908
  projection() {
594
909
  return {
595
910
  ...this.form.shell(),
596
911
  enabled: this.enabled(),
912
+ showDeprecatedModels: this.scope.getSnapshot().value?.showDeprecatedModels ?? false,
913
+ pickerSaving: this.pickerSaving,
914
+ pickerFailed: this.pickerFailed,
597
915
  apiKeyEnv: this.form.field("apiKeyEnv"),
598
916
  baseURL: this.form.field("baseURL"),
599
917
  refreshMinutes: this.form.field("refreshMinutes"),
@@ -604,9 +922,28 @@ var OpencodeGoSectionController = class {
604
922
  apiKey: this.form.field(API_KEY_FIELD),
605
923
  apiKeyConfigured: this.credential.configured,
606
924
  apiKeyWritable: this.credential.writable,
607
- models: this.models
925
+ models: this.models,
926
+ modelLimits: this.form.field("modelLimits"),
927
+ modelLimitDraft: this.limitDraft()
608
928
  };
609
929
  }
930
+ /**
931
+ * Read the overrides as the page currently shows them. A staged JSON draft
932
+ * wins while it is valid; malformed text falls back to the last accepted
933
+ * settings value so the table never renders phantom rows.
934
+ */
935
+ limitDraft() {
936
+ const staged = this.form.field("modelLimits");
937
+ if (staged.invalid) return modelLimitsOf(this.scope.getSnapshot().value?.modelLimits);
938
+ if (staged.overridden || this.form.shell().dirty) {
939
+ try {
940
+ return modelLimitsOf(JSON.parse(staged.text));
941
+ } catch {
942
+ return modelLimitsOf(this.scope.getSnapshot().value?.modelLimits);
943
+ }
944
+ }
945
+ return modelLimitsOf(this.scope.getSnapshot().value?.modelLimits);
946
+ }
610
947
  /**
611
948
  * The adapter's effective switch state: the resolved section's value, over
612
949
  * the Host's own default when the section carries none.
@@ -629,6 +966,22 @@ var OpencodeGoSectionController = class {
629
966
  setEnabled(next) {
630
967
  void this.scope.set("enabled", next);
631
968
  }
969
+ /** Visibility is immediate; failed writes leave the committed switch state visible. */
970
+ async setShowDeprecatedModels(next) {
971
+ if (this.pickerSaving || !this.scope.getSnapshot().writable) return;
972
+ this.pickerSaving = true;
973
+ this.pickerFailed = false;
974
+ this.store.set(this.projection());
975
+ try {
976
+ await this.scope.set("showDeprecatedModels", next);
977
+ this.pickerFailed = (this.scope.getSnapshot().value?.showDeprecatedModels ?? false) !== next;
978
+ } catch {
979
+ this.pickerFailed = true;
980
+ } finally {
981
+ this.pickerSaving = false;
982
+ this.store.set(this.projection());
983
+ }
984
+ }
632
985
  /**
633
986
  * Read the gateway's model listing through the Host's discovery for this
634
987
  * adapter. Called when the page mounts and again from its refresh control.
@@ -640,12 +993,13 @@ var OpencodeGoSectionController = class {
640
993
  const request = ++this.modelsRequest;
641
994
  this.models = { status: "loading" };
642
995
  this.store.set(this.projection());
643
- void this.ctx.remote.llm.discoverModels(OPENCODE_GO_NS, { provider: OPENCODE_GO_PROVIDER }).then((response) => {
996
+ void this.readModels().then((response) => {
644
997
  if (request !== this.modelsRequest) return;
645
998
  this.models = response.ok ? {
646
999
  status: "ready",
647
1000
  count: response.value.length,
648
- preview: response.value.map((model) => model.name ?? model.id)
1001
+ preview: response.value.map((model) => model.name ?? model.id),
1002
+ entries: response.value
649
1003
  } : { status: "failed", message: response.error.message };
650
1004
  this.store.set(this.projection());
651
1005
  }).catch((error) => {
@@ -708,6 +1062,9 @@ var OpencodeGoSectionController = class {
708
1062
  setEnabled: (next) => {
709
1063
  this.setEnabled(next);
710
1064
  },
1065
+ setShowDeprecatedModels: (next) => {
1066
+ void this.setShowDeprecatedModels(next);
1067
+ },
711
1068
  ...this.form.actions()
712
1069
  };
713
1070
  return this.face;
@@ -723,6 +1080,29 @@ var OpencodeGoSectionController = class {
723
1080
  return this.credential.configured;
724
1081
  }
725
1082
  };
1083
+ function modelLimitsOf(value) {
1084
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
1085
+ const limits = {};
1086
+ for (const [id3, entry] of Object.entries(value)) {
1087
+ if (entry === null) {
1088
+ limits[id3] = null;
1089
+ continue;
1090
+ }
1091
+ if (typeof entry !== "object" || Array.isArray(entry)) continue;
1092
+ const fields = entry;
1093
+ const limit = {};
1094
+ if (fields.contextWindow === null) limit.contextWindow = null;
1095
+ if (fields.maxTokens === null) limit.maxTokens = null;
1096
+ if (typeof fields.contextWindow === "number" && Number.isSafeInteger(fields.contextWindow) && fields.contextWindow > 0) {
1097
+ limit.contextWindow = fields.contextWindow;
1098
+ }
1099
+ if (typeof fields.maxTokens === "number" && Number.isSafeInteger(fields.maxTokens) && fields.maxTokens > 0) {
1100
+ limit.maxTokens = fields.maxTokens;
1101
+ }
1102
+ if (limit.contextWindow !== void 0 || limit.maxTokens !== void 0) limits[id3] = limit;
1103
+ }
1104
+ return limits;
1105
+ }
726
1106
  function refOf(snapshot) {
727
1107
  const declared = snapshot.value?.apiKeyEnv;
728
1108
  return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_API_KEY_REF;
@@ -742,7 +1122,7 @@ var en = {
742
1122
  usageUnavailable: "Unavailable",
743
1123
  nav: "OpenCode Go",
744
1124
  title: "OpenCode Go",
745
- intro: "Use your OpenCode Go subscription in the harness. Paste the API key below and save; the models the gateway currently serves are listed underneath.",
1125
+ intro: "Manage your OpenCode Go subscription models and their capacities.",
746
1126
  enabledLabel: "Enable OpenCode Go",
747
1127
  enabledHint: "While this is on, OpenCode Go appears in every model picker and can serve requests. Turning it off withdraws the provider and its models immediately; this page stays reachable so you can turn it back on.",
748
1128
  enabledOff: "OpenCode Go is off. Its models are withdrawn from every picker and no request reaches the gateway. Turn the switch back on to use it again.",
@@ -758,6 +1138,36 @@ var en = {
758
1138
  modelsFailed: "Could not read the gateway model list.",
759
1139
  modelsEmpty: "The gateway currently serves no model this adapter can route.",
760
1140
  modelsCount: "{count} models",
1141
+ limitsLabel: "Model capacities",
1142
+ limitsHint: "Capacities come from the model API. Leave blank to use the default; save to apply your overrides.",
1143
+ limitsFilterLabel: "Find a model",
1144
+ limitsFilterPlaceholder: "Search by model name or id\u2026",
1145
+ limitsSummary: "{count} customized",
1146
+ limitsModel: "Model",
1147
+ limitsContext: "Context window",
1148
+ limitsOutput: "Max output",
1149
+ capacityDefault: "Default: {value} tokens",
1150
+ capacityMissing: "API did not provide a value",
1151
+ showDeprecatedLabel: "Show deprecated models in conversations",
1152
+ showDeprecatedHint: "Off by default. Turning this on immediately adds deprecated models still served by the gateway to conversation model pickers.",
1153
+ pickerFailed: "Could not update model visibility. Please retry.",
1154
+ deprecatedBadge: "Deprecated",
1155
+ deprecatedHint: "Marked deprecated by models.dev, but still provided by this gateway.",
1156
+ newHint: "Released within the last seven UTC calendar days, according to models.dev.",
1157
+ releasedOn: "Released {date}",
1158
+ releaseSource: "Release date: {date} \xB7 models.dev",
1159
+ modelDetails: "Model configuration",
1160
+ filterLabel: "Filter models",
1161
+ filterAll: "All",
1162
+ filterNew: "New in 7 days",
1163
+ filterCustom: "Customized",
1164
+ filterDeprecated: "Deprecated",
1165
+ limitsResetModel: "Use catalog",
1166
+ limitsResetAll: "Clear all overrides",
1167
+ limitsNoMatches: "No models match \u201C{query}\u201D. Try a different name or id.",
1168
+ limitsEmpty: "Read the model list first; capacities are edited per model the gateway serves.",
1169
+ limitsContextLabel: "Context window for {name}",
1170
+ limitsOutputLabel: "Max output for {name}",
761
1171
  advancedLabel: "Advanced settings",
762
1172
  advancedHint: "Credential name, gateway URL, and adapter tuning. The inherited values work as they are; change them only if you know you need to.",
763
1173
  apiKeyEnvLabel: "Credential reference",
@@ -797,7 +1207,7 @@ var zh = {
797
1207
  usageUnavailable: "\u6682\u4E0D\u53EF\u7528",
798
1208
  nav: "OpenCode Go",
799
1209
  title: "OpenCode Go",
800
- intro: "\u5728 Harness \u4E2D\u4F7F\u7528\u4F60\u7684 OpenCode Go \u8BA2\u9605\u3002\u586B\u5165\u4E0B\u65B9 API key \u5E76\u4FDD\u5B58\u5373\u53EF\uFF1B\u7F51\u5173\u5F53\u524D\u63D0\u4F9B\u7684\u6A21\u578B\u5217\u5728\u4E0B\u9762\u3002",
1210
+ intro: "\u7BA1\u7406 OpenCode Go \u8BA2\u9605\u6A21\u578B\u53CA\u5176\u5BB9\u91CF\u3002",
801
1211
  enabledLabel: "\u542F\u7528 OpenCode Go",
802
1212
  enabledHint: "\u5F00\u542F\u65F6\uFF0COpenCode Go \u4F1A\u51FA\u73B0\u5728\u6240\u6709\u6A21\u578B\u9009\u62E9\u5668\u4E2D\u5E76\u53EF\u5904\u7406\u8BF7\u6C42\u3002\u5173\u95ED\u540E\u5C06\u7ACB\u5373\u64A4\u4E0B\u8BE5\u63D0\u4F9B\u65B9\u53CA\u5176\u6A21\u578B\uFF1B\u672C\u9875\u4ECD\u53EF\u8BBF\u95EE\uFF0C\u65B9\u4FBF\u4F60\u968F\u65F6\u91CD\u65B0\u5F00\u542F\u3002",
803
1213
  enabledOff: "OpenCode Go \u5DF2\u5173\u95ED\u3002\u5176\u6A21\u578B\u5DF2\u4ECE\u6240\u6709\u9009\u62E9\u5668\u4E2D\u64A4\u4E0B\uFF0C\u4E5F\u4E0D\u4F1A\u6709\u8BF7\u6C42\u53D1\u5F80\u7F51\u5173\u3002\u9700\u8981\u65F6\u628A\u5F00\u5173\u91CD\u65B0\u6253\u5F00\u5373\u53EF\u3002",
@@ -813,6 +1223,36 @@ var zh = {
813
1223
  modelsFailed: "\u672A\u80FD\u8BFB\u53D6\u7F51\u5173\u6A21\u578B\u5217\u8868\u3002",
814
1224
  modelsEmpty: "\u7F51\u5173\u5F53\u524D\u6CA1\u6709\u672C\u9002\u914D\u5668\u53EF\u8DEF\u7531\u7684\u6A21\u578B\u3002",
815
1225
  modelsCount: "{count} \u4E2A\u6A21\u578B",
1226
+ limitsLabel: "\u6A21\u578B\u5BB9\u91CF",
1227
+ limitsHint: "\u5BB9\u91CF\u6765\u81EA\u6A21\u578B API\u3002\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u4FEE\u6539\u540E\u4FDD\u5B58\u751F\u6548\u3002",
1228
+ limitsFilterLabel: "\u67E5\u627E\u6A21\u578B",
1229
+ limitsFilterPlaceholder: "\u6309\u6A21\u578B\u540D\u79F0\u6216 ID \u641C\u7D22\u2026",
1230
+ limitsSummary: "{count} \u4E2A\u5DF2\u81EA\u5B9A\u4E49",
1231
+ limitsModel: "\u6A21\u578B",
1232
+ limitsContext: "\u4E0A\u4E0B\u6587\u7A97\u53E3",
1233
+ limitsOutput: "\u6700\u5927\u8F93\u51FA",
1234
+ capacityDefault: "\u9ED8\u8BA4\uFF1A{value} tokens",
1235
+ capacityMissing: "API \u672A\u63D0\u4F9B",
1236
+ showDeprecatedLabel: "\u5728\u4F1A\u8BDD\u4E2D\u663E\u793A\u8FC7\u65F6\u6A21\u578B",
1237
+ showDeprecatedHint: "\u9ED8\u8BA4\u5173\u95ED\u3002\u5F00\u542F\u540E\uFF0C\u7F51\u5173\u4ECD\u63D0\u4F9B\u7684\u8FC7\u65F6\u6A21\u578B\u4F1A\u7ACB\u5373\u52A0\u5165\u4F1A\u8BDD\u6A21\u578B\u5217\u8868\u3002",
1238
+ pickerFailed: "\u6A21\u578B\u663E\u793A\u8BBE\u7F6E\u672A\u66F4\u65B0\uFF0C\u8BF7\u91CD\u8BD5\u3002",
1239
+ deprecatedBadge: "\u5DF2\u5F03\u7528",
1240
+ deprecatedHint: "models.dev \u6807\u8BB0\u4E3A\u5DF2\u5F03\u7528\uFF0C\u5F53\u524D\u7F51\u5173\u4ECD\u63D0\u4F9B\u6B64\u6A21\u578B\u3002",
1241
+ newHint: "\u4F9D\u636E models.dev \u53D1\u5E03\u65E5\u671F\uFF0C\u8FD1\u4E03\u4E2A UTC \u65E5\u5386\u65E5\u5185\u53D1\u5E03\u3002",
1242
+ releasedOn: "{date} \u53D1\u5E03",
1243
+ releaseSource: "\u53D1\u5E03\u65E5\u671F\uFF1A{date} \xB7 models.dev",
1244
+ modelDetails: "\u6A21\u578B\u914D\u7F6E",
1245
+ filterLabel: "\u7B5B\u9009\u6A21\u578B",
1246
+ filterAll: "\u5168\u90E8",
1247
+ filterNew: "\u8FD1 7 \u5929\u53D1\u5E03",
1248
+ filterCustom: "\u5DF2\u81EA\u5B9A\u4E49",
1249
+ filterDeprecated: "\u8FC7\u65F6\u6A21\u578B",
1250
+ limitsResetModel: "\u4F7F\u7528\u76EE\u5F55\u503C",
1251
+ limitsResetAll: "\u6E05\u9664\u5168\u90E8\u8986\u76D6",
1252
+ limitsNoMatches: "\u6CA1\u6709\u5339\u914D\u201C{query}\u201D\u7684\u6A21\u578B\u3002\u8BF7\u5C1D\u8BD5\u5176\u4ED6\u540D\u79F0\u6216 ID\u3002",
1253
+ limitsEmpty: "\u8BF7\u5148\u8BFB\u53D6\u6A21\u578B\u5217\u8868\uFF1B\u5BB9\u91CF\u6309\u7F51\u5173\u63D0\u4F9B\u7684\u6A21\u578B\u9010\u4E2A\u7F16\u8F91\u3002",
1254
+ limitsContextLabel: "{name} \u7684\u4E0A\u4E0B\u6587\u7A97\u53E3",
1255
+ limitsOutputLabel: "{name} \u7684\u6700\u5927\u8F93\u51FA",
816
1256
  advancedLabel: "\u9AD8\u7EA7\u8BBE\u7F6E",
817
1257
  advancedHint: "\u51ED\u8BC1\u540D\u79F0\u3001\u7F51\u5173\u5730\u5740\u4E0E\u9002\u914D\u5668\u8C03\u4F18\u53C2\u6570\u3002\u7EE7\u627F\u6765\u7684\u53D6\u503C\u5373\u53EF\u6B63\u5E38\u4F7F\u7528\uFF0C\u9664\u975E\u4F60\u660E\u786E\u77E5\u9053\u9700\u8981\u4FEE\u6539\uFF0C\u5426\u5219\u4E0D\u5FC5\u52A8\u8FD9\u91CC\u3002",
818
1258
  apiKeyEnvLabel: "\u51ED\u8BC1\u5F15\u7528\u540D",
@@ -873,8 +1313,14 @@ var usageRemote = {
873
1313
  }]
874
1314
  };
875
1315
 
1316
+ // src/remote-contract.ts
1317
+ var goRemote = {
1318
+ package: "dsh-opencode-go",
1319
+ descriptors: [...usageRemote.descriptors, ...modelsRemote.descriptors]
1320
+ };
1321
+
876
1322
  // src/client/UsagePill.tsx
877
- var import_react2 = require("react");
1323
+ var import_react3 = require("react");
878
1324
 
879
1325
  // plugin-css:/Users/tanglei/Desktop/dsh-opencode-go/src/client/UsagePill.module.css
880
1326
  var id2 = "dsh-opencode-go/UsagePill.module.css";
@@ -885,20 +1331,20 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
885
1331
  style.textContent = ".dmPm3q_root{min-width:0;display:inline-flex;position:relative}.dmPm3q_trigger{color:inherit;opacity:.75;font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:4px 6px;font-size:12px}.dmPm3q_trigger:hover,.dmPm3q_trigger:focus-visible{opacity:1;background:color-mix(in srgb, currentColor 8%, transparent)}.dmPm3q_panel{z-index:100;border:1px solid color-mix(in srgb, currentColor 18%, transparent);width:280px;max-width:calc(100vw - 32px);color:var(--dsw-alias-label-primary,CanvasText);background:var(--dsw-specific-menu,Canvas);border-radius:12px;padding:16px;font-size:12px;position:absolute;bottom:calc(100% + 12px);right:0;box-shadow:0 8px 30px #0002}.dmPm3q_hint{opacity:.65;font-size:11px;line-height:1.6}.dmPm3q_window{margin-top:14px}.dmPm3q_row{justify-content:space-between;margin-bottom:5px;display:flex}.dmPm3q_window progress{accent-color:#39a878;width:100%;height:5px;margin-bottom:5px;display:block}.dmPm3q_window progress::-webkit-progress-bar{background:color-mix(in srgb, currentColor 15%, transparent);border-radius:4px}.dmPm3q_window progress::-webkit-progress-value{background:#39a878;border-radius:4px}.dmPm3q_window progress::-moz-progress-bar{background:#39a878;border-radius:4px}";
886
1332
  document.head.appendChild(style);
887
1333
  }
888
- var UsagePill_default = { "root": "dmPm3q_root", "trigger": "dmPm3q_trigger", "window": "dmPm3q_window", "row": "dmPm3q_row", "panel": "dmPm3q_panel", "hint": "dmPm3q_hint" };
1334
+ var UsagePill_default = { "window": "dmPm3q_window", "row": "dmPm3q_row", "panel": "dmPm3q_panel", "hint": "dmPm3q_hint", "root": "dmPm3q_root", "trigger": "dmPm3q_trigger" };
889
1335
 
890
1336
  // src/client/UsagePill.tsx
891
- var import_jsx_runtime2 = require("react/jsx-runtime");
1337
+ var import_jsx_runtime3 = require("react/jsx-runtime");
892
1338
  function UsagePill({ directory, ...props }) {
893
- const state = (0, import_react2.useSyncExternalStore)(directory.subscribe, directory.getSnapshot, directory.getSnapshot);
894
- return state.current?.provider === "opencode-go" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ActiveUsage, { ...props }) : null;
1339
+ const state = (0, import_react3.useSyncExternalStore)(directory.subscribe, directory.getSnapshot, directory.getSnapshot);
1340
+ return state.current?.provider === "opencode-go" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActiveUsage, { ...props }) : null;
895
1341
  }
896
1342
  function ActiveUsage({ readUsage, t }) {
897
- const [usage, setUsage] = (0, import_react2.useState)(null);
898
- const [failed, setFailed] = (0, import_react2.useState)(false);
899
- const [open, setOpen] = (0, import_react2.useState)(false);
900
- const root = (0, import_react2.useRef)(null);
901
- (0, import_react2.useEffect)(() => {
1343
+ const [usage, setUsage] = (0, import_react3.useState)(null);
1344
+ const [failed, setFailed] = (0, import_react3.useState)(false);
1345
+ const [open, setOpen] = (0, import_react3.useState)(false);
1346
+ const root = (0, import_react3.useRef)(null);
1347
+ (0, import_react3.useEffect)(() => {
902
1348
  let alive = true;
903
1349
  let busy = false;
904
1350
  const refresh = async () => {
@@ -933,7 +1379,7 @@ function ActiveUsage({ readUsage, t }) {
933
1379
  document.removeEventListener("visibilitychange", visible);
934
1380
  };
935
1381
  }, [readUsage]);
936
- (0, import_react2.useEffect)(() => {
1382
+ (0, import_react3.useEffect)(() => {
937
1383
  if (!open) return;
938
1384
  const click = (event) => {
939
1385
  if (!root.current?.contains(event.target)) setOpen(false);
@@ -949,8 +1395,8 @@ function ActiveUsage({ readUsage, t }) {
949
1395
  };
950
1396
  }, [open]);
951
1397
  const label = usage ? `Go \xB7 5h ${usage.rolling.percent}% \xB7 ${t("usageWeekShort")} ${usage.weekly.percent}%` : `Go \xB7 ${failed ? t("usageUnavailable") : "\u2026"}`;
952
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: UsagePill_default.root, ref: root, children: [
953
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: UsagePill_default.root, ref: root, children: [
1399
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
954
1400
  "button",
955
1401
  {
956
1402
  type: "button",
@@ -964,25 +1410,25 @@ function ActiveUsage({ readUsage, t }) {
964
1410
  children: label
965
1411
  }
966
1412
  ),
967
- open && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.panel, role: "dialog", "aria-label": t("usageTitle"), children: [
968
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("usageTitle") }),
969
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: UsagePill_default.hint, children: t("usageHint") }),
970
- usage ? ["rolling", "weekly", "monthly"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.window, children: [
971
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.row, children: [
972
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t(`usage_${key}`) }),
973
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
1413
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.panel, role: "dialog", "aria-label": t("usageTitle"), children: [
1414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: t("usageTitle") }),
1415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: UsagePill_default.hint, children: t("usageHint") }),
1416
+ usage ? ["rolling", "weekly", "monthly"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.window, children: [
1417
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.row, children: [
1418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t(`usage_${key}`) }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("strong", { children: [
974
1420
  usage[key].percent,
975
1421
  "%"
976
1422
  ] })
977
1423
  ] }),
978
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("progress", { "aria-label": t(`usage_${key}`), max: 100, value: Math.min(100, usage[key].percent) }),
979
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.hint, children: [
1424
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("progress", { "aria-label": t(`usage_${key}`), max: 100, value: Math.min(100, usage[key].percent) }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.hint, children: [
980
1426
  t("usageResets"),
981
1427
  " ",
982
1428
  new Date(usage[key].resetsAt).toLocaleString()
983
1429
  ] }),
984
- usage[key].status === "rate-limited" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: t("usageLimited") })
985
- ] }, key)) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: failed ? t("usageUnavailable") : t("usageLoading") })
1430
+ usage[key].status === "rate-limited" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: t("usageLimited") })
1431
+ ] }, key)) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: failed ? t("usageUnavailable") : t("usageLoading") })
986
1432
  ] })
987
1433
  ] });
988
1434
  }
@@ -990,42 +1436,52 @@ function ActiveUsage({ readUsage, t }) {
990
1436
  // src/client/usage.ts
991
1437
  function registerUsagePill(ctx) {
992
1438
  ctx.inject(["modelDirectories", "sessions", "remote.session"], (scope) => {
993
- scope.effect(async () => {
994
- const unmount = await scope.remote.$mount(usageRemote);
995
- scope.inject(["remote.opencodeGoUsage"], (ready) => {
996
- const readUsage = async () => {
997
- const result = await ready.remote.opencodeGoUsage.read();
998
- if (!result.ok) throw new Error("OpenCode Go usage unavailable");
999
- return result.value;
1000
- };
1001
- const translate = ready.locale.bind("settings.opencode-go");
1002
- ready.slots.inject("conversation.input.right", () => ready.slots.register({
1003
- name: "conversation.input.right",
1004
- id: "opencode-go-usage",
1005
- order: 1e3,
1006
- inject: (sessionId) => ({
1007
- directory: ready.modelDirectories.directoryFor(sessionId).store,
1008
- readUsage,
1009
- t: (key) => translate(key)
1010
- })
1011
- }, UsagePill));
1012
- });
1013
- return unmount;
1439
+ scope.inject(["remote.opencodeGoUsage"], (ready) => {
1440
+ const readUsage = async () => {
1441
+ const result = await ready.remote.opencodeGoUsage.read();
1442
+ if (!result.ok) throw new Error("OpenCode Go usage unavailable");
1443
+ return result.value;
1444
+ };
1445
+ const translate = ready.locale.bind("settings.opencode-go");
1446
+ ready.slots.inject("conversation.input.right", () => ready.slots.register({
1447
+ name: "conversation.input.right",
1448
+ id: "opencode-go-usage",
1449
+ order: 1e3,
1450
+ inject: (sessionId) => ({
1451
+ directory: ready.modelDirectories.directoryFor(sessionId).store,
1452
+ readUsage,
1453
+ t: (key) => translate(key)
1454
+ })
1455
+ }, UsagePill));
1014
1456
  });
1015
1457
  });
1016
1458
  }
1017
1459
 
1018
1460
  // src/client/index.ts
1019
1461
  var NS = "settings.opencode-go";
1020
- var inject = ["slots", "locale", "remote", "remote.credentials", "remote.llm", "settingsScope"];
1462
+ var inject = ["slots", "locale", "remote", "remote.credentials", "remote.llm"];
1021
1463
  function apply(ctx) {
1022
1464
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "llm-opencode-go: copy dictionaries");
1023
1465
  registerUsagePill(ctx);
1024
- const scope = ctx.settingsScope.bind({
1025
- namespace: "llm-opencode-go",
1026
- decode: (section) => typeof section === "object" && section !== null ? section : void 0
1466
+ const modelsReady = ctx.remote.$mount(goRemote);
1467
+ ctx.effect(async () => await modelsReady);
1468
+ ctx.inject(["configForms", "remote.opencodeGoModels"], (child) => {
1469
+ const forms = child.get("configForms");
1470
+ mountSettings(child, forms.get("opencode-go"), modelsReady);
1471
+ });
1472
+ ctx.inject(["settingsScope", "remote.opencodeGoModels"], (child) => {
1473
+ mountSettings(child, child.settingsScope.bind({
1474
+ namespace: "llm-opencode-go",
1475
+ decode: (section) => typeof section === "object" && section !== null ? section : void 0
1476
+ }), modelsReady);
1477
+ });
1478
+ }
1479
+ function mountSettings(ctx, scope, modelsReady) {
1480
+ const controller = new OpencodeGoSectionController(scope, ctx, async () => {
1481
+ await modelsReady;
1482
+ return ctx.remote.opencodeGoModels.read();
1027
1483
  });
1028
- const controller = new OpencodeGoSectionController(scope, ctx);
1484
+ ctx.effect(() => () => controller.dispose());
1029
1485
  const t = ctx.locale.bind(NS);
1030
1486
  const injected = () => ({ ...controller.inject(), t });
1031
1487
  ctx.effect(() => {