dsh-opencode-go 0.1.7 → 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
@@ -38,9 +38,61 @@ __export(index_exports, {
38
38
  module.exports = __toCommonJS(index_exports);
39
39
 
40
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
41
46
  var import_react = require("react");
42
47
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
43
- var primitives = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
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
+ };
44
96
 
45
97
  // plugin-css:/Users/tanglei/Desktop/dsh-opencode-go/src/client/Section.module.css
46
98
  var id = "dsh-opencode-go/Section.module.css";
@@ -48,22 +100,190 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")
48
100
  const style = document.createElement("style");
49
101
  style.dataset.plugin = "dsh-opencode-go";
50
102
  style.dataset.pluginCss = id;
51
- 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_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_limitsScroll{overscroll-behavior-x:contain;border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;overflow-x:auto}.BtKYva_limitsTable{border-collapse:collapse;table-layout:fixed;width:100%;min-width:620px}.BtKYva_limitsTable th,.BtKYva_limitsTable td{text-align:left;vertical-align:middle;padding:10px}.BtKYva_limitsTable thead th{letter-spacing:.02em;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3);font-size:11px;font-weight:600}.BtKYva_limitsTable thead th:first-child{width:44%}.BtKYva_limitsTable thead th:nth-child(2),.BtKYva_limitsTable thead th:nth-child(3){width:22%}.BtKYva_limitsTable thead th:last-child{width:12%}.BtKYva_limitsTable tbody tr+tr{border-top:.5px solid var(--dsw-alias-border-l2)}.BtKYva_limitsTable tbody tr:hover{background:var(--dsw-alias-bg-layer-3)}.BtKYva_limitsTable .BtKYva_input{box-sizing:border-box;width:100%;min-width:112px}.BtKYva_limitsName{min-width:0;font-weight:400}.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{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}';
52
104
  document.head.appendChild(style);
53
105
  }
54
- var Section_default = { "limitsModelId": "BtKYva_limitsModelId", "advanced": "BtKYva_advanced", "input": "BtKYva_input", "limitsEditor": "BtKYva_limitsEditor", "hint": "BtKYva_hint", "limitsFilterLabel": "BtKYva_limitsFilterLabel", "disclosure": "BtKYva_disclosure", "chevron": "BtKYva_chevron", "actions": "BtKYva_actions", "inputInvalid": "BtKYva_inputInvalid", "intro": "BtKYva_intro", "limitsTable": "BtKYva_limitsTable", "limitsId": "BtKYva_limitsId", "failedNote": "BtKYva_failedNote", "label": "BtKYva_label", "reset": "BtKYva_reset", "invalid": "BtKYva_invalid", "limitsName": "BtKYva_limitsName", "visuallyHidden": "BtKYva_visuallyHidden", "head": "BtKYva_head", "field": "BtKYva_field", "chevronOpen": "BtKYva_chevronOpen", "chips": "BtKYva_chips", "limitsToolbar": "BtKYva_limitsToolbar", "limitsSummary": "BtKYva_limitsSummary", "limitsScroll": "BtKYva_limitsScroll", "badges": "BtKYva_badges" };
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" };
55
107
 
56
- // src/client/Section.tsx
108
+ // src/client/ModelEditor.tsx
57
109
  var import_jsx_runtime = require("react/jsx-runtime");
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,
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
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");
58
278
  var ChevronDown = primitives.IconChevronDownOutlineRegular ?? primitives.IconChevronDownOutline14;
59
279
  function ValueField(props) {
60
280
  const hintId = `${props.id}-hint`;
61
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
62
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
63
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.label, htmlFor: props.id, children: props.label }),
64
- props.field.overridden ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: Section_default.badges, children: [
65
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: props.overriddenLabel }),
66
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
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)(
67
287
  "button",
68
288
  {
69
289
  type: "button",
@@ -75,7 +295,7 @@ function ValueField(props) {
75
295
  )
76
296
  ] }) : null
77
297
  ] }),
78
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
298
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
79
299
  "input",
80
300
  {
81
301
  id: props.id,
@@ -93,176 +313,24 @@ function ValueField(props) {
93
313
  }
94
314
  }
95
315
  ),
96
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: hintId, 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 })
97
317
  ] });
98
318
  }
99
319
  function ModelsBody({ models, t }) {
100
320
  if (models.status === "failed") {
101
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
102
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t("modelsFailed") }),
103
- /* @__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 })
104
324
  ] });
105
325
  }
106
- if (models.status !== "ready") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, role: "status", "aria-live": "polite", children: t("modelsLoading") });
107
- if (models.count === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("modelsEmpty") });
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") });
108
328
  return null;
109
329
  }
110
- function LimitsTable({ models, draft, t, disabled, onEdit }) {
111
- const [query, setQuery] = (0, import_react.useState)("");
112
- const known = new Map((models.status === "ready" ? models.entries : []).map((model) => [model.id, model]));
113
- for (const [id3, limit] of Object.entries(draft)) {
114
- if (hasCapacityOverride(limit) && !known.has(id3)) known.set(id3, { id: id3 });
115
- }
116
- if (known.size === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("limitsEmpty") });
117
- const normalizedQuery = query.trim().toLocaleLowerCase();
118
- const entries = [...known.values()].filter((model) => {
119
- if (normalizedQuery === "") return true;
120
- return `${model.name ?? ""} ${model.id}`.toLocaleLowerCase().includes(normalizedQuery);
121
- });
122
- const customizedCount = Object.values(draft).filter(hasCapacityOverride).length;
123
- const write = (id3, field, value) => {
124
- const current = draft[id3] === null ? { contextWindow: null, maxTokens: null } : draft[id3] ?? {};
125
- const merged = { ...current };
126
- merged[field] = value ?? null;
127
- const next = { ...draft, [id3]: merged };
128
- onEdit(next);
129
- };
130
- const clearModel = (id3) => {
131
- const next = { ...draft };
132
- next[id3] = null;
133
- onEdit(next);
134
- };
135
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.limitsEditor, children: [
136
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.limitsToolbar, children: [
137
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.limitsFilterLabel, htmlFor: "opencode-go-model-filter", children: t("limitsFilterLabel") }),
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
- "input",
140
- {
141
- id: "opencode-go-model-filter",
142
- className: Section_default.input,
143
- type: "search",
144
- name: "model-filter",
145
- autoComplete: "off",
146
- spellCheck: false,
147
- placeholder: t("limitsFilterPlaceholder"),
148
- value: query,
149
- onChange: (event) => {
150
- setQuery(event.target.value);
151
- }
152
- }
153
- ),
154
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.limitsSummary, role: "status", "aria-live": "polite", children: t("limitsSummary", { count: customizedCount }) })
155
- ] }),
156
- entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("limitsNoMatches", { query }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: Section_default.limitsScroll, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: Section_default.limitsTable, children: [
157
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("caption", { className: Section_default.visuallyHidden, children: t("limitsLabel") }),
158
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
159
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: t("limitsModel") }),
160
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: t("limitsContext") }),
161
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: t("limitsOutput") }),
162
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.visuallyHidden, children: t("limitsResetModel") }) })
163
- ] }) }),
164
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: entries.map((model) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
165
- LimitRow,
166
- {
167
- model,
168
- limit: draft[model.id],
169
- disabled,
170
- t,
171
- onChange: write,
172
- onReset: clearModel
173
- },
174
- model.id
175
- )) })
176
- ] }) }),
177
- customizedCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: Section_default.reset, disabled, onClick: () => {
178
- onEdit(Object.fromEntries(Object.keys(draft).map((id3) => [id3, null])));
179
- }, children: t("limitsResetAll") }) : null
180
- ] });
181
- }
182
- function LimitRow({ model, limit, disabled, t, onChange, onReset }) {
183
- const name = model.name ?? model.id;
184
- const contextId = `opencode-go-context-${inputKey(model.id)}`;
185
- const outputId = `opencode-go-output-${inputKey(model.id)}`;
186
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
187
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("th", { scope: "row", className: Section_default.limitsName, children: [
188
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.limitsId, translate: "no", children: name }),
189
- model.name !== void 0 && model.name !== model.id ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: Section_default.limitsModelId, translate: "no", children: model.id }) : null,
190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.hint, children: t("limitsAdvertised", { context: capacityLabel(model.contextWindow), output: capacityLabel(model.maxTokens) }) })
191
- ] }),
192
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
193
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.visuallyHidden, htmlFor: contextId, children: t("limitsContextLabel", { name }) }),
194
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
195
- "input",
196
- {
197
- id: contextId,
198
- className: Section_default.input,
199
- type: "number",
200
- name: contextId,
201
- autoComplete: "off",
202
- inputMode: "numeric",
203
- min: 1,
204
- step: 1,
205
- value: limit?.contextWindow ?? "",
206
- disabled,
207
- onChange: (event) => {
208
- const text = event.target.value.trim();
209
- if (text === "") onChange(model.id, "contextWindow", void 0);
210
- else {
211
- const value = positiveInteger(text);
212
- if (value !== void 0) onChange(model.id, "contextWindow", value);
213
- }
214
- }
215
- }
216
- )
217
- ] }),
218
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
219
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.visuallyHidden, htmlFor: outputId, children: t("limitsOutputLabel", { name }) }),
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
- "input",
222
- {
223
- id: outputId,
224
- className: Section_default.input,
225
- type: "number",
226
- name: outputId,
227
- autoComplete: "off",
228
- inputMode: "numeric",
229
- min: 1,
230
- step: 1,
231
- value: limit?.maxTokens ?? "",
232
- disabled,
233
- onChange: (event) => {
234
- const text = event.target.value.trim();
235
- if (text === "") onChange(model.id, "maxTokens", void 0);
236
- else {
237
- const value = positiveInteger(text);
238
- if (value !== void 0) onChange(model.id, "maxTokens", value);
239
- }
240
- }
241
- }
242
- )
243
- ] }),
244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: hasCapacityOverride(limit) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: Section_default.reset, disabled, onClick: () => {
245
- onReset(model.id);
246
- }, children: t("limitsResetModel") }) : null })
247
- ] });
248
- }
249
- function hasCapacityOverride(limit) {
250
- return limit?.contextWindow != null || limit?.maxTokens != null;
251
- }
252
- function positiveInteger(value) {
253
- const parsed = Number(value);
254
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
255
- }
256
- function capacityLabel(value) {
257
- return value === void 0 ? "\u2014" : String(value);
258
- }
259
- function inputKey(value) {
260
- return encodeURIComponent(value);
261
- }
262
330
  function OpencodeGoSection(props) {
263
- const { useOpencodeGo, edit, resetField, save, discard, loadModels, setEnabled, t } = props;
264
- 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;
265
- 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)(
266
334
  Loaded,
267
335
  {
268
336
  state: useOpencodeGo((snapshot) => snapshot),
@@ -272,19 +340,19 @@ function OpencodeGoSection(props) {
272
340
  save,
273
341
  discard,
274
342
  loadModels,
275
- setEnabled
343
+ setEnabled,
344
+ setShowDeprecatedModels
276
345
  }
277
346
  );
278
347
  }
279
348
  function Loaded(props) {
280
349
  const { t, state, loadModels } = props;
281
- const [advanced, setAdvanced] = (0, import_react.useState)(false);
282
- const [limitsOpen, setLimitsOpen] = (0, import_react.useState)(false);
283
- (0, import_react.useEffect)(() => {
350
+ const [advanced, setAdvanced] = (0, import_react2.useState)(false);
351
+ (0, import_react2.useEffect)(() => {
284
352
  if (state.available && state.models.status === "idle") loadModels();
285
353
  }, [state.available, state.models.status, loadModels]);
286
354
  if (!state.available) {
287
- 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") });
288
356
  }
289
357
  const disabled = !state.writable;
290
358
  const fieldProps = {
@@ -294,119 +362,31 @@ function Loaded(props) {
294
362
  disabled
295
363
  };
296
364
  const advancedOverridden = state.apiKeyEnv.overridden || state.baseURL.overridden || state.refreshMinutes.overridden || state.streamIdleTimeoutMs.overridden || state.maxRequestImageBytes.overridden || state.requestImagePixelBudget.overridden || state.requestImageMaxBytes.overridden;
297
- const customizedModels = Object.values(state.modelLimitDraft).filter(hasCapacityOverride).length;
298
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.intro, children: t("intro") }),
300
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
301
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
302
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("enabledLabel") }),
303
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
304
- 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",
305
371
  {
306
- checked: state.enabled,
307
- label: t("enabledLabel"),
308
- disabled,
309
- 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
+ ]
310
384
  }
311
385
  )
312
386
  ] }),
313
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: state.enabled ? t("enabledHint") : t("enabledOff") })
314
- ] }),
315
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
316
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
317
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.label, htmlFor: "opencode-go-key", children: t("keyLabel") }),
318
- /* @__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") }) })
319
- ] }),
320
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
321
- "input",
322
- {
323
- id: "opencode-go-key",
324
- name: "api-key",
325
- className: Section_default.input,
326
- type: "password",
327
- autoComplete: "off",
328
- "aria-describedby": "opencode-go-key-hint",
329
- value: state.apiKey.text,
330
- disabled: !state.apiKeyWritable,
331
- onChange: (event) => {
332
- props.edit("apiKey", event.target.value);
333
- }
334
- }
335
- ),
336
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: "opencode-go-key-hint", className: Section_default.hint, children: state.apiKeyWritable ? t("keyHint") : t("keyNotWritable") })
337
- ] }),
338
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
339
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
340
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("modelsLabel") }),
341
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: Section_default.badges, children: [
342
- 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,
343
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
344
- "button",
345
- {
346
- type: "button",
347
- className: Section_default.reset,
348
- disabled: state.models.status === "loading",
349
- onClick: loadModels,
350
- children: t("modelsRefresh")
351
- }
352
- )
353
- ] })
354
- ] }),
355
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModelsBody, { models: state.models, t })
356
- ] }),
357
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
358
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
359
- "button",
360
- {
361
- type: "button",
362
- className: Section_default.disclosure,
363
- "aria-expanded": limitsOpen,
364
- "aria-controls": "opencode-go-model-limits",
365
- onClick: () => {
366
- setLimitsOpen(!limitsOpen);
367
- },
368
- children: [
369
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { "aria-hidden": "true", className: limitsOpen ? Section_default.chevronOpen : Section_default.chevron }),
370
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("limitsLabel") }),
371
- customizedModels > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: t("limitsSummary", { count: customizedModels }) }) : null
372
- ]
373
- }
374
- ),
375
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("limitsHint") }),
376
- limitsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { id: "opencode-go-model-limits", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
377
- LimitsTable,
378
- {
379
- models: state.models,
380
- draft: state.modelLimitDraft,
381
- t,
382
- disabled,
383
- onEdit: (next) => {
384
- props.edit("modelLimits", JSON.stringify(next));
385
- }
386
- }
387
- ) }) : null
388
- ] }),
389
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
390
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
391
- "button",
392
- {
393
- type: "button",
394
- className: Section_default.disclosure,
395
- "aria-expanded": advanced,
396
- "aria-controls": "opencode-go-advanced",
397
- onClick: () => {
398
- setAdvanced(!advanced);
399
- },
400
- children: [
401
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: advanced ? Section_default.chevronOpen : Section_default.chevron }),
402
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("advancedLabel") }),
403
- advancedOverridden ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: t("overridden") }) : null
404
- ]
405
- }
406
- ),
407
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("advancedHint") }),
408
- advanced ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { id: "opencode-go-advanced", className: Section_default.advanced, children: [
409
- /* @__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)(
410
390
  ValueField,
411
391
  {
412
392
  id: "opencode-go-api-key-env",
@@ -422,7 +402,7 @@ function Loaded(props) {
422
402
  }
423
403
  }
424
404
  ),
425
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
426
406
  ValueField,
427
407
  {
428
408
  id: "opencode-go-base-url",
@@ -438,7 +418,7 @@ function Loaded(props) {
438
418
  }
439
419
  }
440
420
  ),
441
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
442
422
  ValueField,
443
423
  {
444
424
  id: "opencode-go-refresh-minutes",
@@ -455,7 +435,7 @@ function Loaded(props) {
455
435
  }
456
436
  }
457
437
  ),
458
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
438
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
459
439
  ValueField,
460
440
  {
461
441
  id: "opencode-go-stream-idle",
@@ -472,7 +452,7 @@ function Loaded(props) {
472
452
  }
473
453
  }
474
454
  ),
475
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
455
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
476
456
  ValueField,
477
457
  {
478
458
  id: "opencode-go-max-request-image-bytes",
@@ -489,7 +469,7 @@ function Loaded(props) {
489
469
  }
490
470
  }
491
471
  ),
492
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
472
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
493
473
  ValueField,
494
474
  {
495
475
  id: "opencode-go-image-pixel-budget",
@@ -506,7 +486,7 @@ function Loaded(props) {
506
486
  }
507
487
  }
508
488
  ),
509
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
489
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
510
490
  ValueField,
511
491
  {
512
492
  id: "opencode-go-image-max-bytes",
@@ -523,14 +503,100 @@ function Loaded(props) {
523
503
  }
524
504
  }
525
505
  )
526
- ] }) : null
527
- ] }),
528
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.actions, children: [
529
- /* @__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") }),
530
- /* @__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") }),
531
- 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
532
594
  ] }),
533
- 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
+ ] })
534
600
  ] });
535
601
  }
536
602
 
@@ -797,9 +863,10 @@ var OpencodeGoSectionController = class {
797
863
  * @param ctx - the page plugin's context, whose `remote.credentials` namespace
798
864
  * answers for the credential the section references.
799
865
  */
800
- constructor(scope, ctx) {
866
+ constructor(scope, ctx, readModels = () => ctx.remote.llm.discoverModels(OPENCODE_GO_NS, { provider: OPENCODE_GO_PROVIDER })) {
801
867
  this.scope = scope;
802
868
  this.ctx = ctx;
869
+ this.readModels = readModels;
803
870
  this.form = new StagedForm(
804
871
  scope,
805
872
  [
@@ -828,6 +895,8 @@ var OpencodeGoSectionController = class {
828
895
  credential = { ref: "", configured: false, writable: true };
829
896
  models = { status: "idle" };
830
897
  modelsRequest = 0;
898
+ pickerSaving = false;
899
+ pickerFailed = false;
831
900
  face;
832
901
  unsubscribe;
833
902
  /** Release subscriptions without disposing the host's shared form. */
@@ -840,6 +909,9 @@ var OpencodeGoSectionController = class {
840
909
  return {
841
910
  ...this.form.shell(),
842
911
  enabled: this.enabled(),
912
+ showDeprecatedModels: this.scope.getSnapshot().value?.showDeprecatedModels ?? false,
913
+ pickerSaving: this.pickerSaving,
914
+ pickerFailed: this.pickerFailed,
843
915
  apiKeyEnv: this.form.field("apiKeyEnv"),
844
916
  baseURL: this.form.field("baseURL"),
845
917
  refreshMinutes: this.form.field("refreshMinutes"),
@@ -894,6 +966,22 @@ var OpencodeGoSectionController = class {
894
966
  setEnabled(next) {
895
967
  void this.scope.set("enabled", next);
896
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
+ }
897
985
  /**
898
986
  * Read the gateway's model listing through the Host's discovery for this
899
987
  * adapter. Called when the page mounts and again from its refresh control.
@@ -905,7 +993,7 @@ var OpencodeGoSectionController = class {
905
993
  const request = ++this.modelsRequest;
906
994
  this.models = { status: "loading" };
907
995
  this.store.set(this.projection());
908
- void this.ctx.remote.llm.discoverModels(OPENCODE_GO_NS, { provider: OPENCODE_GO_PROVIDER }).then((response) => {
996
+ void this.readModels().then((response) => {
909
997
  if (request !== this.modelsRequest) return;
910
998
  this.models = response.ok ? {
911
999
  status: "ready",
@@ -974,6 +1062,9 @@ var OpencodeGoSectionController = class {
974
1062
  setEnabled: (next) => {
975
1063
  this.setEnabled(next);
976
1064
  },
1065
+ setShowDeprecatedModels: (next) => {
1066
+ void this.setShowDeprecatedModels(next);
1067
+ },
977
1068
  ...this.form.actions()
978
1069
  };
979
1070
  return this.face;
@@ -1031,7 +1122,7 @@ var en = {
1031
1122
  usageUnavailable: "Unavailable",
1032
1123
  nav: "OpenCode Go",
1033
1124
  title: "OpenCode Go",
1034
- 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.",
1035
1126
  enabledLabel: "Enable OpenCode Go",
1036
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.",
1037
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.",
@@ -1048,14 +1139,29 @@ var en = {
1048
1139
  modelsEmpty: "The gateway currently serves no model this adapter can route.",
1049
1140
  modelsCount: "{count} models",
1050
1141
  limitsLabel: "Model capacities",
1051
- limitsHint: "Override the context window or max output for a model. Leave a field blank to use the catalog value.",
1142
+ limitsHint: "Capacities come from the model API. Leave blank to use the default; save to apply your overrides.",
1052
1143
  limitsFilterLabel: "Find a model",
1053
1144
  limitsFilterPlaceholder: "Search by model name or id\u2026",
1054
1145
  limitsSummary: "{count} customized",
1055
1146
  limitsModel: "Model",
1056
1147
  limitsContext: "Context window",
1057
1148
  limitsOutput: "Max output",
1058
- limitsAdvertised: "Catalog: {context} / {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",
1059
1165
  limitsResetModel: "Use catalog",
1060
1166
  limitsResetAll: "Clear all overrides",
1061
1167
  limitsNoMatches: "No models match \u201C{query}\u201D. Try a different name or id.",
@@ -1101,7 +1207,7 @@ var zh = {
1101
1207
  usageUnavailable: "\u6682\u4E0D\u53EF\u7528",
1102
1208
  nav: "OpenCode Go",
1103
1209
  title: "OpenCode Go",
1104
- 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",
1105
1211
  enabledLabel: "\u542F\u7528 OpenCode Go",
1106
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",
1107
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",
@@ -1118,14 +1224,29 @@ var zh = {
1118
1224
  modelsEmpty: "\u7F51\u5173\u5F53\u524D\u6CA1\u6709\u672C\u9002\u914D\u5668\u53EF\u8DEF\u7531\u7684\u6A21\u578B\u3002",
1119
1225
  modelsCount: "{count} \u4E2A\u6A21\u578B",
1120
1226
  limitsLabel: "\u6A21\u578B\u5BB9\u91CF",
1121
- limitsHint: "\u4E3A\u6A21\u578B\u8986\u76D6\u4E0A\u4E0B\u6587\u7A97\u53E3\u6216\u6700\u5927\u8F93\u51FA\u3002\u7559\u7A7A\u7684\u5B57\u6BB5\u4F1A\u4F7F\u7528\u76EE\u5F55\u516C\u5E03\u7684\u5BB9\u91CF\u3002",
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",
1122
1228
  limitsFilterLabel: "\u67E5\u627E\u6A21\u578B",
1123
1229
  limitsFilterPlaceholder: "\u6309\u6A21\u578B\u540D\u79F0\u6216 ID \u641C\u7D22\u2026",
1124
1230
  limitsSummary: "{count} \u4E2A\u5DF2\u81EA\u5B9A\u4E49",
1125
1231
  limitsModel: "\u6A21\u578B",
1126
1232
  limitsContext: "\u4E0A\u4E0B\u6587\u7A97\u53E3",
1127
1233
  limitsOutput: "\u6700\u5927\u8F93\u51FA",
1128
- limitsAdvertised: "\u76EE\u5F55\uFF1A{context} / {output}",
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",
1129
1250
  limitsResetModel: "\u4F7F\u7528\u76EE\u5F55\u503C",
1130
1251
  limitsResetAll: "\u6E05\u9664\u5168\u90E8\u8986\u76D6",
1131
1252
  limitsNoMatches: "\u6CA1\u6709\u5339\u914D\u201C{query}\u201D\u7684\u6A21\u578B\u3002\u8BF7\u5C1D\u8BD5\u5176\u4ED6\u540D\u79F0\u6216 ID\u3002",
@@ -1192,8 +1313,14 @@ var usageRemote = {
1192
1313
  }]
1193
1314
  };
1194
1315
 
1316
+ // src/remote-contract.ts
1317
+ var goRemote = {
1318
+ package: "dsh-opencode-go",
1319
+ descriptors: [...usageRemote.descriptors, ...modelsRemote.descriptors]
1320
+ };
1321
+
1195
1322
  // src/client/UsagePill.tsx
1196
- var import_react2 = require("react");
1323
+ var import_react3 = require("react");
1197
1324
 
1198
1325
  // plugin-css:/Users/tanglei/Desktop/dsh-opencode-go/src/client/UsagePill.module.css
1199
1326
  var id2 = "dsh-opencode-go/UsagePill.module.css";
@@ -1204,20 +1331,20 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
1204
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}";
1205
1332
  document.head.appendChild(style);
1206
1333
  }
1207
- var UsagePill_default = { "trigger": "dmPm3q_trigger", "panel": "dmPm3q_panel", "hint": "dmPm3q_hint", "window": "dmPm3q_window", "row": "dmPm3q_row", "root": "dmPm3q_root" };
1334
+ var UsagePill_default = { "window": "dmPm3q_window", "row": "dmPm3q_row", "panel": "dmPm3q_panel", "hint": "dmPm3q_hint", "root": "dmPm3q_root", "trigger": "dmPm3q_trigger" };
1208
1335
 
1209
1336
  // src/client/UsagePill.tsx
1210
- var import_jsx_runtime2 = require("react/jsx-runtime");
1337
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1211
1338
  function UsagePill({ directory, ...props }) {
1212
- const state = (0, import_react2.useSyncExternalStore)(directory.subscribe, directory.getSnapshot, directory.getSnapshot);
1213
- 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;
1214
1341
  }
1215
1342
  function ActiveUsage({ readUsage, t }) {
1216
- const [usage, setUsage] = (0, import_react2.useState)(null);
1217
- const [failed, setFailed] = (0, import_react2.useState)(false);
1218
- const [open, setOpen] = (0, import_react2.useState)(false);
1219
- const root = (0, import_react2.useRef)(null);
1220
- (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)(() => {
1221
1348
  let alive = true;
1222
1349
  let busy = false;
1223
1350
  const refresh = async () => {
@@ -1252,7 +1379,7 @@ function ActiveUsage({ readUsage, t }) {
1252
1379
  document.removeEventListener("visibilitychange", visible);
1253
1380
  };
1254
1381
  }, [readUsage]);
1255
- (0, import_react2.useEffect)(() => {
1382
+ (0, import_react3.useEffect)(() => {
1256
1383
  if (!open) return;
1257
1384
  const click = (event) => {
1258
1385
  if (!root.current?.contains(event.target)) setOpen(false);
@@ -1268,8 +1395,8 @@ function ActiveUsage({ readUsage, t }) {
1268
1395
  };
1269
1396
  }, [open]);
1270
1397
  const label = usage ? `Go \xB7 5h ${usage.rolling.percent}% \xB7 ${t("usageWeekShort")} ${usage.weekly.percent}%` : `Go \xB7 ${failed ? t("usageUnavailable") : "\u2026"}`;
1271
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: UsagePill_default.root, ref: root, children: [
1272
- /* @__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)(
1273
1400
  "button",
1274
1401
  {
1275
1402
  type: "button",
@@ -1283,25 +1410,25 @@ function ActiveUsage({ readUsage, t }) {
1283
1410
  children: label
1284
1411
  }
1285
1412
  ),
1286
- open && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.panel, role: "dialog", "aria-label": t("usageTitle"), children: [
1287
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("usageTitle") }),
1288
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: UsagePill_default.hint, children: t("usageHint") }),
1289
- usage ? ["rolling", "weekly", "monthly"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.window, children: [
1290
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: UsagePill_default.row, children: [
1291
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t(`usage_${key}`) }),
1292
- /* @__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: [
1293
1420
  usage[key].percent,
1294
1421
  "%"
1295
1422
  ] })
1296
1423
  ] }),
1297
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("progress", { "aria-label": t(`usage_${key}`), max: 100, value: Math.min(100, usage[key].percent) }),
1298
- /* @__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: [
1299
1426
  t("usageResets"),
1300
1427
  " ",
1301
1428
  new Date(usage[key].resetsAt).toLocaleString()
1302
1429
  ] }),
1303
- usage[key].status === "rate-limited" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: t("usageLimited") })
1304
- ] }, 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") })
1305
1432
  ] })
1306
1433
  ] });
1307
1434
  }
@@ -1309,27 +1436,23 @@ function ActiveUsage({ readUsage, t }) {
1309
1436
  // src/client/usage.ts
1310
1437
  function registerUsagePill(ctx) {
1311
1438
  ctx.inject(["modelDirectories", "sessions", "remote.session"], (scope) => {
1312
- scope.effect(async () => {
1313
- const unmount = await scope.remote.$mount(usageRemote);
1314
- scope.inject(["remote.opencodeGoUsage"], (ready) => {
1315
- const readUsage = async () => {
1316
- const result = await ready.remote.opencodeGoUsage.read();
1317
- if (!result.ok) throw new Error("OpenCode Go usage unavailable");
1318
- return result.value;
1319
- };
1320
- const translate = ready.locale.bind("settings.opencode-go");
1321
- ready.slots.inject("conversation.input.right", () => ready.slots.register({
1322
- name: "conversation.input.right",
1323
- id: "opencode-go-usage",
1324
- order: 1e3,
1325
- inject: (sessionId) => ({
1326
- directory: ready.modelDirectories.directoryFor(sessionId).store,
1327
- readUsage,
1328
- t: (key) => translate(key)
1329
- })
1330
- }, UsagePill));
1331
- });
1332
- 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));
1333
1456
  });
1334
1457
  });
1335
1458
  }
@@ -1340,19 +1463,24 @@ var inject = ["slots", "locale", "remote", "remote.credentials", "remote.llm"];
1340
1463
  function apply(ctx) {
1341
1464
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "llm-opencode-go: copy dictionaries");
1342
1465
  registerUsagePill(ctx);
1343
- ctx.inject(["configForms"], (child) => {
1466
+ const modelsReady = ctx.remote.$mount(goRemote);
1467
+ ctx.effect(async () => await modelsReady);
1468
+ ctx.inject(["configForms", "remote.opencodeGoModels"], (child) => {
1344
1469
  const forms = child.get("configForms");
1345
- mountSettings(child, forms.get("opencode-go"));
1470
+ mountSettings(child, forms.get("opencode-go"), modelsReady);
1346
1471
  });
1347
- ctx.inject(["settingsScope"], (child) => {
1472
+ ctx.inject(["settingsScope", "remote.opencodeGoModels"], (child) => {
1348
1473
  mountSettings(child, child.settingsScope.bind({
1349
1474
  namespace: "llm-opencode-go",
1350
1475
  decode: (section) => typeof section === "object" && section !== null ? section : void 0
1351
- }));
1476
+ }), modelsReady);
1352
1477
  });
1353
1478
  }
1354
- function mountSettings(ctx, scope) {
1355
- const controller = new OpencodeGoSectionController(scope, ctx);
1479
+ function mountSettings(ctx, scope, modelsReady) {
1480
+ const controller = new OpencodeGoSectionController(scope, ctx, async () => {
1481
+ await modelsReady;
1482
+ return ctx.remote.opencodeGoModels.read();
1483
+ });
1356
1484
  ctx.effect(() => () => controller.dispose());
1357
1485
  const t = ctx.locale.bind(NS);
1358
1486
  const injected = () => ({ ...controller.inject(), t });