dsh-opencode-go 0.1.6 → 0.1.7

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
@@ -30,6 +40,7 @@ module.exports = __toCommonJS(index_exports);
30
40
  // src/client/Section.tsx
31
41
  var import_react = require("react");
32
42
  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);
33
44
 
34
45
  // plugin-css:/Users/tanglei/Desktop/dsh-opencode-go/src/client/Section.module.css
35
46
  var id = "dsh-opencode-go/Section.module.css";
@@ -37,14 +48,16 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")
37
48
  const style = document.createElement("style");
38
49
  style.dataset.plugin = "dsh-opencode-go";
39
50
  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}";
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}";
41
52
  document.head.appendChild(style);
42
53
  }
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" };
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" };
44
55
 
45
56
  // src/client/Section.tsx
46
57
  var import_jsx_runtime = require("react/jsx-runtime");
58
+ var ChevronDown = primitives.IconChevronDownOutlineRegular ?? primitives.IconChevronDownOutline14;
47
59
  function ValueField(props) {
60
+ const hintId = `${props.id}-hint`;
48
61
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
49
62
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
50
63
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: Section_default.label, htmlFor: props.id, children: props.label }),
@@ -66,8 +79,11 @@ function ValueField(props) {
66
79
  "input",
67
80
  {
68
81
  id: props.id,
82
+ name: props.id,
83
+ autoComplete: "off",
84
+ "aria-describedby": hintId,
69
85
  className: props.field.invalid ? Section_default.inputInvalid : Section_default.input,
70
- type: "text",
86
+ type: props.numeric === true ? "number" : "text",
71
87
  ...props.numeric === true ? { inputMode: "numeric" } : {},
72
88
  ...props.field.invalid ? { "aria-invalid": true } : {},
73
89
  value: props.field.text,
@@ -77,19 +93,171 @@ function ValueField(props) {
77
93
  }
78
94
  }
79
95
  ),
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 })
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 })
81
97
  ] });
82
98
  }
83
99
  function ModelsBody({ models, t }) {
84
100
  if (models.status === "failed") {
85
101
  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") }),
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t("modelsFailed") }),
87
103
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: models.message })
88
104
  ] });
89
105
  }
90
- if (models.status !== "ready") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("modelsLoading") });
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") });
91
107
  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)) });
108
+ return null;
109
+ }
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);
93
261
  }
94
262
  function OpencodeGoSection(props) {
95
263
  const { useOpencodeGo, edit, resetField, save, discard, loadModels, setEnabled, t } = props;
@@ -111,6 +279,7 @@ function OpencodeGoSection(props) {
111
279
  function Loaded(props) {
112
280
  const { t, state, loadModels } = props;
113
281
  const [advanced, setAdvanced] = (0, import_react.useState)(false);
282
+ const [limitsOpen, setLimitsOpen] = (0, import_react.useState)(false);
114
283
  (0, import_react.useEffect)(() => {
115
284
  if (state.available && state.models.status === "idle") loadModels();
116
285
  }, [state.available, state.models.status, loadModels]);
@@ -125,6 +294,7 @@ function Loaded(props) {
125
294
  disabled
126
295
  };
127
296
  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;
128
298
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
129
299
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.intro, children: t("intro") }),
130
300
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
@@ -151,9 +321,11 @@ function Loaded(props) {
151
321
  "input",
152
322
  {
153
323
  id: "opencode-go-key",
324
+ name: "api-key",
154
325
  className: Section_default.input,
155
326
  type: "password",
156
327
  autoComplete: "off",
328
+ "aria-describedby": "opencode-go-key-hint",
157
329
  value: state.apiKey.text,
158
330
  disabled: !state.apiKeyWritable,
159
331
  onChange: (event) => {
@@ -161,7 +333,7 @@ function Loaded(props) {
161
333
  }
162
334
  }
163
335
  ),
164
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: state.apiKeyWritable ? t("keyHint") : t("keyNotWritable") })
336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: "opencode-go-key-hint", className: Section_default.hint, children: state.apiKeyWritable ? t("keyHint") : t("keyNotWritable") })
165
337
  ] }),
166
338
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
167
339
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.head, children: [
@@ -182,6 +354,38 @@ function Loaded(props) {
182
354
  ] }),
183
355
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModelsBody, { models: state.models, t })
184
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
+ ] }),
185
389
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: Section_default.field, children: [
186
390
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
187
391
  "button",
@@ -194,7 +398,7 @@ function Loaded(props) {
194
398
  setAdvanced(!advanced);
195
399
  },
196
400
  children: [
197
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutline14, { className: advanced ? Section_default.chevronOpen : Section_default.chevron }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: advanced ? Section_default.chevronOpen : Section_default.chevron }),
198
402
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.label, children: t("advancedLabel") }),
199
403
  advancedOverridden ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: t("overridden") }) : null
200
404
  ]
@@ -365,6 +569,21 @@ function textField(field) {
365
569
  }
366
570
  };
367
571
  }
572
+ function jsonField(field) {
573
+ return {
574
+ field,
575
+ format: (value) => value === void 0 || value === null ? "" : JSON.stringify(value, void 0, 2),
576
+ parse: (text) => {
577
+ const trimmed = text.trim();
578
+ if (trimmed === "") return { kind: "clear" };
579
+ try {
580
+ return { kind: "set", value: JSON.parse(trimmed) };
581
+ } catch {
582
+ return void 0;
583
+ }
584
+ }
585
+ };
586
+ }
368
587
  var StagedForm = class {
369
588
  /**
370
589
  * @param scope - the bound settings scope for this page's namespace.
@@ -375,7 +594,7 @@ var StagedForm = class {
375
594
  this.scope = scope;
376
595
  this.specs = new Map(specs.map((spec) => [spec.field, spec]));
377
596
  this.secretSpecs = new Map(secrets.map((spec) => [spec.field, spec]));
378
- scope.subscribe(() => {
597
+ this.unsubscribe = scope.subscribe(() => {
379
598
  this.publish();
380
599
  });
381
600
  }
@@ -383,8 +602,14 @@ var StagedForm = class {
383
602
  secretSpecs;
384
603
  staged = /* @__PURE__ */ new Map();
385
604
  listeners = /* @__PURE__ */ new Set();
605
+ unsubscribe;
386
606
  saving = false;
387
607
  failed = false;
608
+ /** Shared 0.1.7 forms outlive individual settings pages. */
609
+ dispose() {
610
+ this.unsubscribe();
611
+ this.listeners.clear();
612
+ }
388
613
  /**
389
614
  * Publish a projection of this form, rebuilt whenever the scope or a draft changes.
390
615
  * @param project - build the page state from the form's current reads.
@@ -515,7 +740,7 @@ var StagedForm = class {
515
740
  }
516
741
  async store(field, value) {
517
742
  await this.scope.set(field, value);
518
- return this.userLayer()?.[field] === value;
743
+ return sameJsonValue(this.userLayer()?.[field], value);
519
744
  }
520
745
  stage(field, edit) {
521
746
  this.staged.set(field, edit);
@@ -547,6 +772,19 @@ var StagedForm = class {
547
772
  for (const listener of this.listeners) listener();
548
773
  }
549
774
  };
775
+ function sameJsonValue(left, right) {
776
+ if (Object.is(left, right)) return true;
777
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
778
+ if (Array.isArray(left) || Array.isArray(right)) {
779
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
780
+ return left.every((value, index) => sameJsonValue(value, right[index]));
781
+ }
782
+ const leftRecord = left;
783
+ const rightRecord = right;
784
+ const leftKeys = Object.keys(leftRecord);
785
+ const rightKeys = Object.keys(rightRecord);
786
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(rightRecord, key) && sameJsonValue(leftRecord[key], rightRecord[key]));
787
+ }
550
788
 
551
789
  // src/client/section-controller.ts
552
790
  var OPENCODE_GO_NS = "llm-opencode-go";
@@ -574,12 +812,13 @@ var OpencodeGoSectionController = class {
574
812
  numberField("streamIdleTimeoutMs"),
575
813
  numberField("maxRequestImageBytes"),
576
814
  numberField("requestImagePixelBudget"),
577
- numberField("requestImageMaxBytes")
815
+ numberField("requestImageMaxBytes"),
816
+ jsonField("modelLimits")
578
817
  ],
579
818
  [{ field: API_KEY_FIELD, write: (text) => this.writeKey(text) }]
580
819
  );
581
820
  this.store = this.form.bind(() => this.projection());
582
- scope.subscribe(() => {
821
+ this.unsubscribe = scope.subscribe(() => {
583
822
  void this.readCredential();
584
823
  });
585
824
  void this.readCredential();
@@ -590,6 +829,13 @@ var OpencodeGoSectionController = class {
590
829
  models = { status: "idle" };
591
830
  modelsRequest = 0;
592
831
  face;
832
+ unsubscribe;
833
+ /** Release subscriptions without disposing the host's shared form. */
834
+ dispose() {
835
+ this.modelsRequest++;
836
+ this.unsubscribe();
837
+ this.form.dispose();
838
+ }
593
839
  projection() {
594
840
  return {
595
841
  ...this.form.shell(),
@@ -604,9 +850,28 @@ var OpencodeGoSectionController = class {
604
850
  apiKey: this.form.field(API_KEY_FIELD),
605
851
  apiKeyConfigured: this.credential.configured,
606
852
  apiKeyWritable: this.credential.writable,
607
- models: this.models
853
+ models: this.models,
854
+ modelLimits: this.form.field("modelLimits"),
855
+ modelLimitDraft: this.limitDraft()
608
856
  };
609
857
  }
858
+ /**
859
+ * Read the overrides as the page currently shows them. A staged JSON draft
860
+ * wins while it is valid; malformed text falls back to the last accepted
861
+ * settings value so the table never renders phantom rows.
862
+ */
863
+ limitDraft() {
864
+ const staged = this.form.field("modelLimits");
865
+ if (staged.invalid) return modelLimitsOf(this.scope.getSnapshot().value?.modelLimits);
866
+ if (staged.overridden || this.form.shell().dirty) {
867
+ try {
868
+ return modelLimitsOf(JSON.parse(staged.text));
869
+ } catch {
870
+ return modelLimitsOf(this.scope.getSnapshot().value?.modelLimits);
871
+ }
872
+ }
873
+ return modelLimitsOf(this.scope.getSnapshot().value?.modelLimits);
874
+ }
610
875
  /**
611
876
  * The adapter's effective switch state: the resolved section's value, over
612
877
  * the Host's own default when the section carries none.
@@ -645,7 +910,8 @@ var OpencodeGoSectionController = class {
645
910
  this.models = response.ok ? {
646
911
  status: "ready",
647
912
  count: response.value.length,
648
- preview: response.value.map((model) => model.name ?? model.id)
913
+ preview: response.value.map((model) => model.name ?? model.id),
914
+ entries: response.value
649
915
  } : { status: "failed", message: response.error.message };
650
916
  this.store.set(this.projection());
651
917
  }).catch((error) => {
@@ -723,6 +989,29 @@ var OpencodeGoSectionController = class {
723
989
  return this.credential.configured;
724
990
  }
725
991
  };
992
+ function modelLimitsOf(value) {
993
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
994
+ const limits = {};
995
+ for (const [id3, entry] of Object.entries(value)) {
996
+ if (entry === null) {
997
+ limits[id3] = null;
998
+ continue;
999
+ }
1000
+ if (typeof entry !== "object" || Array.isArray(entry)) continue;
1001
+ const fields = entry;
1002
+ const limit = {};
1003
+ if (fields.contextWindow === null) limit.contextWindow = null;
1004
+ if (fields.maxTokens === null) limit.maxTokens = null;
1005
+ if (typeof fields.contextWindow === "number" && Number.isSafeInteger(fields.contextWindow) && fields.contextWindow > 0) {
1006
+ limit.contextWindow = fields.contextWindow;
1007
+ }
1008
+ if (typeof fields.maxTokens === "number" && Number.isSafeInteger(fields.maxTokens) && fields.maxTokens > 0) {
1009
+ limit.maxTokens = fields.maxTokens;
1010
+ }
1011
+ if (limit.contextWindow !== void 0 || limit.maxTokens !== void 0) limits[id3] = limit;
1012
+ }
1013
+ return limits;
1014
+ }
726
1015
  function refOf(snapshot) {
727
1016
  const declared = snapshot.value?.apiKeyEnv;
728
1017
  return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_API_KEY_REF;
@@ -758,6 +1047,21 @@ var en = {
758
1047
  modelsFailed: "Could not read the gateway model list.",
759
1048
  modelsEmpty: "The gateway currently serves no model this adapter can route.",
760
1049
  modelsCount: "{count} models",
1050
+ limitsLabel: "Model capacities",
1051
+ limitsHint: "Override the context window or max output for a model. Leave a field blank to use the catalog value.",
1052
+ limitsFilterLabel: "Find a model",
1053
+ limitsFilterPlaceholder: "Search by model name or id\u2026",
1054
+ limitsSummary: "{count} customized",
1055
+ limitsModel: "Model",
1056
+ limitsContext: "Context window",
1057
+ limitsOutput: "Max output",
1058
+ limitsAdvertised: "Catalog: {context} / {output}",
1059
+ limitsResetModel: "Use catalog",
1060
+ limitsResetAll: "Clear all overrides",
1061
+ limitsNoMatches: "No models match \u201C{query}\u201D. Try a different name or id.",
1062
+ limitsEmpty: "Read the model list first; capacities are edited per model the gateway serves.",
1063
+ limitsContextLabel: "Context window for {name}",
1064
+ limitsOutputLabel: "Max output for {name}",
761
1065
  advancedLabel: "Advanced settings",
762
1066
  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
1067
  apiKeyEnvLabel: "Credential reference",
@@ -813,6 +1117,21 @@ var zh = {
813
1117
  modelsFailed: "\u672A\u80FD\u8BFB\u53D6\u7F51\u5173\u6A21\u578B\u5217\u8868\u3002",
814
1118
  modelsEmpty: "\u7F51\u5173\u5F53\u524D\u6CA1\u6709\u672C\u9002\u914D\u5668\u53EF\u8DEF\u7531\u7684\u6A21\u578B\u3002",
815
1119
  modelsCount: "{count} \u4E2A\u6A21\u578B",
1120
+ 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",
1122
+ limitsFilterLabel: "\u67E5\u627E\u6A21\u578B",
1123
+ limitsFilterPlaceholder: "\u6309\u6A21\u578B\u540D\u79F0\u6216 ID \u641C\u7D22\u2026",
1124
+ limitsSummary: "{count} \u4E2A\u5DF2\u81EA\u5B9A\u4E49",
1125
+ limitsModel: "\u6A21\u578B",
1126
+ limitsContext: "\u4E0A\u4E0B\u6587\u7A97\u53E3",
1127
+ limitsOutput: "\u6700\u5927\u8F93\u51FA",
1128
+ limitsAdvertised: "\u76EE\u5F55\uFF1A{context} / {output}",
1129
+ limitsResetModel: "\u4F7F\u7528\u76EE\u5F55\u503C",
1130
+ limitsResetAll: "\u6E05\u9664\u5168\u90E8\u8986\u76D6",
1131
+ limitsNoMatches: "\u6CA1\u6709\u5339\u914D\u201C{query}\u201D\u7684\u6A21\u578B\u3002\u8BF7\u5C1D\u8BD5\u5176\u4ED6\u540D\u79F0\u6216 ID\u3002",
1132
+ 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",
1133
+ limitsContextLabel: "{name} \u7684\u4E0A\u4E0B\u6587\u7A97\u53E3",
1134
+ limitsOutputLabel: "{name} \u7684\u6700\u5927\u8F93\u51FA",
816
1135
  advancedLabel: "\u9AD8\u7EA7\u8BBE\u7F6E",
817
1136
  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
1137
  apiKeyEnvLabel: "\u51ED\u8BC1\u5F15\u7528\u540D",
@@ -885,7 +1204,7 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
885
1204
  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
1205
  document.head.appendChild(style);
887
1206
  }
888
- var UsagePill_default = { "root": "dmPm3q_root", "trigger": "dmPm3q_trigger", "window": "dmPm3q_window", "row": "dmPm3q_row", "panel": "dmPm3q_panel", "hint": "dmPm3q_hint" };
1207
+ var UsagePill_default = { "trigger": "dmPm3q_trigger", "panel": "dmPm3q_panel", "hint": "dmPm3q_hint", "window": "dmPm3q_window", "row": "dmPm3q_row", "root": "dmPm3q_root" };
889
1208
 
890
1209
  // src/client/UsagePill.tsx
891
1210
  var import_jsx_runtime2 = require("react/jsx-runtime");
@@ -1017,15 +1336,24 @@ function registerUsagePill(ctx) {
1017
1336
 
1018
1337
  // src/client/index.ts
1019
1338
  var NS = "settings.opencode-go";
1020
- var inject = ["slots", "locale", "remote", "remote.credentials", "remote.llm", "settingsScope"];
1339
+ var inject = ["slots", "locale", "remote", "remote.credentials", "remote.llm"];
1021
1340
  function apply(ctx) {
1022
1341
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "llm-opencode-go: copy dictionaries");
1023
1342
  registerUsagePill(ctx);
1024
- const scope = ctx.settingsScope.bind({
1025
- namespace: "llm-opencode-go",
1026
- decode: (section) => typeof section === "object" && section !== null ? section : void 0
1343
+ ctx.inject(["configForms"], (child) => {
1344
+ const forms = child.get("configForms");
1345
+ mountSettings(child, forms.get("opencode-go"));
1027
1346
  });
1347
+ ctx.inject(["settingsScope"], (child) => {
1348
+ mountSettings(child, child.settingsScope.bind({
1349
+ namespace: "llm-opencode-go",
1350
+ decode: (section) => typeof section === "object" && section !== null ? section : void 0
1351
+ }));
1352
+ });
1353
+ }
1354
+ function mountSettings(ctx, scope) {
1028
1355
  const controller = new OpencodeGoSectionController(scope, ctx);
1356
+ ctx.effect(() => () => controller.dispose());
1029
1357
  const t = ctx.locale.bind(NS);
1030
1358
  const injected = () => ({ ...controller.inject(), t });
1031
1359
  ctx.effect(() => {