@jcy2387/dsh-models-input-modalities 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.cjs CHANGED
@@ -24,7 +24,7 @@ window.__ModuleLoader__.load({
24
24
  return Array.isArray(value) ? value.map((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) ? entry : {}) : [];
25
25
  }
26
26
  /** Joins the settings Remote's document view and fenced writes for one card. */
27
- var ImageInputController = class {
27
+ var ModelCapabilityController = class {
28
28
  ctx;
29
29
  /** The cards waiting to hear that this namespace's stored section changed. */
30
30
  listeners = /* @__PURE__ */ new Set();
@@ -148,6 +148,8 @@ window.__ModuleLoader__.load({
148
148
  function parseImageInputChoice(value) {
149
149
  return value === "inherit" || value === "text" || value === "image" ? value : void 0;
150
150
  }
151
+ //#endregion
152
+ //#region src/model-row.ts
151
153
  /**
152
154
  * The row's model id for labels.
153
155
  * @param row - one stored model row.
@@ -159,8 +161,197 @@ window.__ModuleLoader__.load({
159
161
  return typeof id === "string" && id.length > 0 ? id : `#${String(index + 1)}`;
160
162
  }
161
163
  //#endregion
164
+ //#region src/reasoning-efforts.ts
165
+ /** Every level a row may declare, in escalation order — the adapter's own key set. */
166
+ const THINKING_LEVELS = [
167
+ "off",
168
+ "minimal",
169
+ "low",
170
+ "medium",
171
+ "high",
172
+ "xhigh",
173
+ "max"
174
+ ];
175
+ /**
176
+ * The spelling a newly offered level starts from: its own name, except for
177
+ * `off`, whose absence is the spelling most providers read as "do not think".
178
+ * @param level - the level being offered.
179
+ * @returns its default wire spelling.
180
+ */
181
+ function defaultWire(level) {
182
+ return level === "off" ? "" : level;
183
+ }
184
+ /**
185
+ * A level set offering exactly `offered`, each at its default spelling.
186
+ * @param offered - the levels to declare.
187
+ * @returns the seven level drafts.
188
+ */
189
+ function declaring(offered) {
190
+ const drafts = {};
191
+ for (const level of THINKING_LEVELS) {
192
+ const on = offered.includes(level);
193
+ drafts[level] = {
194
+ offered: on,
195
+ wire: on ? defaultWire(level) : ""
196
+ };
197
+ }
198
+ return drafts;
199
+ }
200
+ /**
201
+ * The levels a row with no dict of its own starts from when it is switched to a
202
+ * declared set: the three efforts an OpenAI-compatible gateway is most likely
203
+ * to serve, each spelled as its own name. `off` is deliberately left unticked:
204
+ * on the plain `reasoning_effort` wire an empty `off` is the very same request
205
+ * as naming no effort at all, so pre-offering it would promise a "stop
206
+ * thinking" choice the endpoint may not honour — the author ticks it, and
207
+ * spells it, once their endpoint says how.
208
+ */
209
+ const DEFAULT_LEVELS = declaring([
210
+ "low",
211
+ "medium",
212
+ "high"
213
+ ]);
214
+ /**
215
+ * The choice a row's stored `reasoningEfforts` displays. Anything that is not
216
+ * `false` and not a plain object states no claim this card understands, and
217
+ * reads as the inheritance it leaves in place; an empty object reads as a
218
+ * declared set offering nothing, which the validator then names.
219
+ * @param row - one stored model row.
220
+ * @returns the choice the row's select shows.
221
+ */
222
+ function reasoningChoice(row) {
223
+ const value = row["reasoningEfforts"];
224
+ if (value === false) return "none";
225
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return "custom";
226
+ return "inherit";
227
+ }
228
+ /**
229
+ * The row's declared levels as editor state. A level the dict omits is not
230
+ * offered; one it carries is, with its wire spelling — an empty value, which is
231
+ * what a valueless `off:` stores as, and a value of a type the schema refuses
232
+ * both read as no spelling yet. A row declaring nothing shows the defaults a
233
+ * fresh declaration starts from.
234
+ * @param row - one stored model row.
235
+ * @returns the seven level drafts.
236
+ */
237
+ function reasoningLevels(row) {
238
+ const value = row["reasoningEfforts"];
239
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return DEFAULT_LEVELS;
240
+ const stored = value;
241
+ const drafts = {};
242
+ for (const level of THINKING_LEVELS) {
243
+ if (!(level in stored)) {
244
+ drafts[level] = {
245
+ offered: false,
246
+ wire: ""
247
+ };
248
+ continue;
249
+ }
250
+ const wire = stored[level];
251
+ drafts[level] = {
252
+ offered: true,
253
+ wire: typeof wire === "string" ? wire : ""
254
+ };
255
+ }
256
+ return drafts;
257
+ }
258
+ /**
259
+ * The row with one reasoning choice applied: `inherit` removes the field,
260
+ * `none` stores `false`, and `custom` stores exactly the levels offered — in
261
+ * escalation order, a valueless `off` as `null`, spellings trimmed so a stray
262
+ * space cannot reach the wire. Every other field, including ones this card
263
+ * never shows, survives.
264
+ * @param row - the row to patch.
265
+ * @param choice - the selected state.
266
+ * @param levels - the editor's level drafts; read only for `custom`.
267
+ * @returns a new row carrying the choice.
268
+ */
269
+ function withReasoning(row, choice, levels) {
270
+ const next = { ...row };
271
+ delete next["reasoningEfforts"];
272
+ if (choice === "none") {
273
+ next["reasoningEfforts"] = false;
274
+ return next;
275
+ }
276
+ if (choice !== "custom") return next;
277
+ const declared = {};
278
+ for (const level of THINKING_LEVELS) {
279
+ const draft = levels[level];
280
+ if (!draft.offered) continue;
281
+ const wire = draft.wire.trim();
282
+ declared[level] = wire.length === 0 ? null : wire;
283
+ }
284
+ next["reasoningEfforts"] = declared;
285
+ return next;
286
+ }
287
+ /**
288
+ * The level set with one level offered or withdrawn. Offering a level always
289
+ * starts its spelling from the default, so a tick is never a blank the save
290
+ * then refuses, and what a withdrawn level carried is not what a reticked one
291
+ * silently revives; levels left alone keep theirs.
292
+ * @param levels - the drafts to patch.
293
+ * @param level - the level toggled.
294
+ * @param offered - whether the row should declare it.
295
+ * @returns the patched drafts, or the same object when nothing changes.
296
+ */
297
+ function toggleLevel(levels, level, offered) {
298
+ if (levels[level].offered === offered) return levels;
299
+ return {
300
+ ...levels,
301
+ [level]: {
302
+ offered,
303
+ wire: offered ? defaultWire(level) : ""
304
+ }
305
+ };
306
+ }
307
+ /**
308
+ * The level set with one level's wire spelling retyped.
309
+ * @param levels - the drafts to patch.
310
+ * @param level - the level whose spelling changed.
311
+ * @param wire - the text the field now carries.
312
+ * @returns the patched drafts.
313
+ */
314
+ function setWire(levels, level, wire) {
315
+ return {
316
+ ...levels,
317
+ [level]: {
318
+ ...levels[level],
319
+ wire
320
+ }
321
+ };
322
+ }
323
+ /**
324
+ * Why a row's declared levels would be refused, checked before the write so the
325
+ * card can name the row instead of answering a rejected settings mutation. The
326
+ * rules are the adapter's: every declared level but `off` needs a wire spelling,
327
+ * and a set offering no level beyond `off` declares nothing worth declaring.
328
+ * @param row - one stored model row.
329
+ * @returns the failure, or undefined for a row that can be saved.
330
+ */
331
+ function reasoningFailure(row) {
332
+ if (reasoningChoice(row) !== "custom") return void 0;
333
+ const levels = reasoningLevels(row);
334
+ let thinks = false;
335
+ for (const level of THINKING_LEVELS) {
336
+ const draft = levels[level];
337
+ if (!draft.offered || level === "off") continue;
338
+ if (draft.wire.trim().length === 0) return "needsWire";
339
+ thinks = true;
340
+ }
341
+ return thinks ? void 0 : "needsLevel";
342
+ }
343
+ /**
344
+ * Read a select's submitted value. The DOM hands over a bare string, so an
345
+ * unrecognized one is refused rather than cast into the union.
346
+ * @param value - the submitted option value.
347
+ * @returns the choice, or undefined for anything else.
348
+ */
349
+ function parseReasoningChoice(value) {
350
+ return value === "inherit" || value === "none" || value === "custom" ? value : void 0;
351
+ }
352
+ //#endregion
162
353
  //#region \0dsh-css:src/client/styles.module.css.mjs
163
- const css = ".q49TuG_fold{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-3,transparent);border-radius:10px;margin:8px 0 0}.q49TuG_summary{cursor:pointer;color:var(--dsw-alias-label-primary,inherit);user-select:none;padding:10px 14px;font-size:13px;font-weight:600}.q49TuG_body{flex-direction:column;gap:10px;padding:2px 14px 14px;display:flex}.q49TuG_row{align-items:center;gap:10px;display:flex}.q49TuG_rowId{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,inherit);flex:auto;font-size:13px;overflow:hidden}.q49TuG_select{min-width:9em;color:var(--dsw-alias-label-primary,inherit);border:1px solid var(--dsw-alias-border-l2,#0000001f);border-radius:8px;flex:none;padding:6px 8px;font-size:13px}.q49TuG_hint,.q49TuG_status{color:var(--dsw-alias-label-tertiary,inherit);margin:0;font-size:12px;line-height:1.5}.q49TuG_error{color:var(--dsw-alias-status-danger,#d64545);margin:0;font-size:12px;line-height:1.5}.q49TuG_footer{align-items:center;gap:10px;display:flex}";
354
+ const css = ".q49TuG_fold{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-3,transparent);border-radius:10px;margin:8px 0 0}.q49TuG_summary{cursor:pointer;color:var(--dsw-alias-label-primary,inherit);user-select:none;padding:10px 14px;font-size:13px;font-weight:600}.q49TuG_body{flex-direction:column;gap:10px;padding:2px 14px 14px;display:flex}.q49TuG_model{flex-direction:column;gap:6px;display:flex}.q49TuG_row{flex-wrap:wrap;align-items:flex-end;gap:10px;display:flex}.q49TuG_rowId{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,inherit);flex:auto;padding-bottom:6px;font-size:13px;overflow:hidden}.q49TuG_field{flex-direction:column;gap:3px;display:flex}.q49TuG_fieldLabel{color:var(--dsw-alias-label-tertiary,inherit);font-size:11px;line-height:1.2}.q49TuG_select{min-width:9em;color:var(--dsw-alias-label-primary,inherit);border:1px solid var(--dsw-alias-border-l2,#0000001f);border-radius:8px;flex:none;padding:6px 8px;font-size:13px}.q49TuG_levels{border:1px dashed var(--dsw-alias-border-l2,#0000001f);border-radius:8px;flex-direction:column;gap:6px;padding:8px 10px;display:flex}.q49TuG_levelGrid{grid-template-columns:repeat(auto-fill,minmax(15em,1fr));gap:6px 12px;display:grid}.q49TuG_levelRow{align-items:center;gap:8px;min-width:0;display:flex}.q49TuG_levelOffered{color:var(--dsw-alias-label-primary,inherit);cursor:pointer;flex:0 0 6.5em;align-items:center;gap:6px;font-size:12px;display:flex}.q49TuG_wire{min-width:0;color:var(--dsw-alias-label-primary,inherit);border:1px solid var(--dsw-alias-border-l2,#0000001f);background:0 0;border-radius:8px;flex:auto;padding:4px 8px;font-size:12px}.q49TuG_wire:disabled{opacity:.55}.q49TuG_hint,.q49TuG_status{color:var(--dsw-alias-label-tertiary,inherit);margin:0;font-size:12px;line-height:1.5}.q49TuG_error{color:var(--dsw-alias-status-danger,#d64545);margin:0;font-size:12px;line-height:1.5}.q49TuG_footer{align-items:center;gap:10px;display:flex}";
164
355
  const tagId = "@jcy2387/dsh-models-input-modalities/styles.module.css";
165
356
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
166
357
  const tag = document.createElement("style");
@@ -170,35 +361,45 @@ window.__ModuleLoader__.load({
170
361
  document.head.appendChild(tag);
171
362
  }
172
363
  var styles_module_css_default = {
173
- "hint": "q49TuG_hint",
174
- "row": "q49TuG_row",
364
+ "field": "q49TuG_field",
365
+ "status": "q49TuG_status",
175
366
  "body": "q49TuG_body",
367
+ "fold": "q49TuG_fold",
368
+ "row": "q49TuG_row",
369
+ "fieldLabel": "q49TuG_fieldLabel",
370
+ "levels": "q49TuG_levels",
371
+ "hint": "q49TuG_hint",
372
+ "levelOffered": "q49TuG_levelOffered",
373
+ "summary": "q49TuG_summary",
176
374
  "rowId": "q49TuG_rowId",
177
- "status": "q49TuG_status",
178
- "error": "q49TuG_error",
375
+ "select": "q49TuG_select",
376
+ "levelRow": "q49TuG_levelRow",
179
377
  "footer": "q49TuG_footer",
180
- "summary": "q49TuG_summary",
181
- "fold": "q49TuG_fold",
182
- "select": "q49TuG_select"
378
+ "levelGrid": "q49TuG_levelGrid",
379
+ "error": "q49TuG_error",
380
+ "wire": "q49TuG_wire",
381
+ "model": "q49TuG_model"
183
382
  };
184
383
  //#endregion
185
- //#region src/client/ImageInputCard.tsx
384
+ //#region src/client/ModelCapabilityCard.tsx
186
385
  /**
187
- * One pi-ai provider card's image-input fold: the per-model modality claim the
188
- * Models page's own form does not carry. The fold loads the provider's stored
189
- * rows when first opened, edits them locally, and writes the whole `models`
190
- * array back under the revision fence the load answered the same array
191
- * semantics the page's own cards use. A stored change elsewhere on the page (a
192
- * model added or removed in the catalog above) reaches the fold through the
193
- * pushed settings invalidation, so the list it shows never waits for the
194
- * section to remount.
386
+ * One pi-ai provider card's model-capability fold: the per-model claims the
387
+ * Models page's own form does not carry which inputs a model accepts, and
388
+ * which reasoning levels it offers. The fold loads the provider's stored rows
389
+ * when first opened, edits them locally, and writes the whole `models` array
390
+ * back under the revision fence the load answered the same array semantics
391
+ * the page's own cards use, and the reason both claims live in one fold: two
392
+ * folds would write the same array and fence each other into conflicts. A
393
+ * stored change elsewhere on the page (a model added or removed in the catalog
394
+ * above) reaches the fold through the pushed settings invalidation, so the list
395
+ * it shows never waits for the section to remount.
195
396
  */
196
397
  /**
197
- * Render the image-input fold of one provider card.
398
+ * Render the model-capability fold of one provider card.
198
399
  * @param props - the card's directory row plus the bound face and copy.
199
400
  * @returns the fold, or nothing while the provider is still a dormant row.
200
401
  */
201
- function ImageInputCard(props) {
402
+ function ModelCapabilityCard(props) {
202
403
  const { provider, configured, t, loadModels, saveModels, subscribeChanges } = props;
203
404
  const [status, setStatus] = (0, react.useState)("idle");
204
405
  const [view, setView] = (0, react.useState)(void 0);
@@ -286,11 +487,26 @@ window.__ModuleLoader__.load({
286
487
  reload
287
488
  ]);
288
489
  if (configured !== true) return null;
289
- const choose = (index, choice) => {
490
+ const chooseInput = (index, choice) => {
290
491
  setRows((current) => current.map((row, at) => at === index ? withImageInput(row, choice) : row));
291
492
  };
493
+ const chooseReasoning = (index, choice) => {
494
+ setRows((current) => current.map((row, at) => at === index ? withReasoning(row, choice, reasoningLevels(row)) : row));
495
+ };
496
+ /**
497
+ * Patch one row's declared levels. The edit runs inside the state updater and
498
+ * re-reads the row there, so two controls changed in one batch each see what
499
+ * the previous one wrote.
500
+ * @param index - the row to patch.
501
+ * @param edit - the level-set transformation.
502
+ */
503
+ const patchLevels = (index, edit) => {
504
+ setRows((current) => current.map((row, at) => at === index ? withReasoning(row, "custom", edit(reasoningLevels(row))) : row));
505
+ };
506
+ const unsavable = rows.some((row) => reasoningFailure(row) !== void 0);
507
+ const locked = view === void 0 || !view.writable || status === "saving";
292
508
  const submit = async () => {
293
- if (view === void 0) return;
509
+ if (view === void 0 || unsavable) return;
294
510
  setStatus("saving");
295
511
  const outcome = await saveModels(provider, rows, view.revision);
296
512
  if (outcome.kind === "written") {
@@ -350,36 +566,130 @@ window.__ModuleLoader__.load({
350
566
  className: styles_module_css_default["hint"],
351
567
  children: t("inheritsHint")
352
568
  }),
353
- rows.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
354
- className: styles_module_css_default["row"],
355
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
356
- className: styles_module_css_default["rowId"],
357
- children: rowId(row, index)
358
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
359
- className: styles_module_css_default["select"],
360
- value: imageInputChoice(row),
361
- "aria-label": `${t("title")} ${rowId(row, index)}`,
362
- disabled: !view.writable || status === "saving",
363
- onChange: (event) => {
364
- const next = parseImageInputChoice(event.target.value);
365
- if (next !== void 0) choose(index, next);
366
- },
367
- children: [
368
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
369
- value: "inherit",
370
- children: t("choiceDefault")
371
- }),
372
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
373
- value: "text",
374
- children: t("choiceText")
375
- }),
376
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
377
- value: "image",
378
- children: t("choiceImage")
379
- })
380
- ]
381
- })]
382
- }, index)),
569
+ rows.map((row, index) => {
570
+ const id = rowId(row, index);
571
+ const reasoning = reasoningChoice(row);
572
+ const levels = reasoningLevels(row);
573
+ const invalid = reasoningFailure(row);
574
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
575
+ className: styles_module_css_default["model"],
576
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
577
+ className: styles_module_css_default["row"],
578
+ children: [
579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
580
+ className: styles_module_css_default["rowId"],
581
+ children: id
582
+ }),
583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
584
+ className: styles_module_css_default["field"],
585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
586
+ className: styles_module_css_default["fieldLabel"],
587
+ children: t("inputLabel")
588
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
589
+ className: styles_module_css_default["select"],
590
+ value: imageInputChoice(row),
591
+ "aria-label": `${t("inputLabel")} ${id}`,
592
+ disabled: locked,
593
+ onChange: (event) => {
594
+ const next = parseImageInputChoice(event.target.value);
595
+ if (next !== void 0) chooseInput(index, next);
596
+ },
597
+ children: [
598
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
599
+ value: "inherit",
600
+ children: t("choiceDefault")
601
+ }),
602
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
603
+ value: "text",
604
+ children: t("choiceText")
605
+ }),
606
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
607
+ value: "image",
608
+ children: t("choiceImage")
609
+ })
610
+ ]
611
+ })]
612
+ }),
613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
614
+ className: styles_module_css_default["field"],
615
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
616
+ className: styles_module_css_default["fieldLabel"],
617
+ children: t("reasoningLabel")
618
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
619
+ className: styles_module_css_default["select"],
620
+ value: reasoning,
621
+ "aria-label": `${t("reasoningLabel")} ${id}`,
622
+ disabled: locked,
623
+ onChange: (event) => {
624
+ const next = parseReasoningChoice(event.target.value);
625
+ if (next !== void 0) chooseReasoning(index, next);
626
+ },
627
+ children: [
628
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
629
+ value: "inherit",
630
+ children: t("reasoningInherit")
631
+ }),
632
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
633
+ value: "none",
634
+ children: t("reasoningNone")
635
+ }),
636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
637
+ value: "custom",
638
+ children: t("reasoningCustom")
639
+ })
640
+ ]
641
+ })]
642
+ })
643
+ ]
644
+ }), reasoning === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
645
+ className: styles_module_css_default["levels"],
646
+ children: [
647
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
648
+ className: styles_module_css_default["levelGrid"],
649
+ children: THINKING_LEVELS.map((level) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
650
+ className: styles_module_css_default["levelRow"],
651
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
652
+ className: styles_module_css_default["levelOffered"],
653
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
654
+ type: "checkbox",
655
+ checked: levels[level].offered,
656
+ disabled: locked,
657
+ onChange: (event) => {
658
+ const offered = event.target.checked;
659
+ patchLevels(index, (current) => toggleLevel(current, level, offered));
660
+ }
661
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: level })]
662
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
663
+ className: styles_module_css_default["wire"],
664
+ type: "text",
665
+ value: levels[level].wire,
666
+ placeholder: level === "off" ? t("wireNothing") : level,
667
+ "aria-label": `${t("wireLabel")} ${level}`,
668
+ spellCheck: false,
669
+ disabled: locked || !levels[level].offered,
670
+ onChange: (event) => {
671
+ const wire = event.target.value;
672
+ patchLevels(index, (current) => setWire(current, level, wire));
673
+ }
674
+ })]
675
+ }, level))
676
+ }),
677
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
678
+ className: styles_module_css_default["hint"],
679
+ children: t("reasoningHint")
680
+ }),
681
+ invalid === "needsWire" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
682
+ className: styles_module_css_default["error"],
683
+ children: `${id}: ${t("needsWire")}`
684
+ }) : null,
685
+ invalid === "needsLevel" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
686
+ className: styles_module_css_default["error"],
687
+ children: `${id}: ${t("needsLevel")}`
688
+ }) : null
689
+ ]
690
+ }) : null]
691
+ }, index);
692
+ }),
383
693
  failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
384
694
  className: styles_module_css_default["error"],
385
695
  children: failure
@@ -390,7 +700,7 @@ window.__ModuleLoader__.load({
390
700
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
391
701
  variant: "outline",
392
702
  size: "sm",
393
- disabled: !view.writable || status === "saving" || !dirty,
703
+ disabled: locked || !dirty || unsavable,
394
704
  onClick: () => {
395
705
  submit();
396
706
  },
@@ -422,19 +732,29 @@ window.__ModuleLoader__.load({
422
732
  }
423
733
  //#endregion
424
734
  //#region src/client/locales.ts
425
- /** Copy dictionaries for the image-input card. */
735
+ /** Copy dictionaries for the model-capability card. */
426
736
  /** English strings (the key-set source of truth for this pair). */
427
737
  const en = {
428
- title: "Input modalities",
738
+ title: "Model capabilities",
429
739
  loading: "Loading the model list…",
430
740
  loadFailed: "Loading the model configuration failed.",
431
741
  retry: "Retry",
432
- empty: "No explicit model list yet — add models in the catalog above, then declare their input modalities here.",
742
+ empty: "No explicit model list yet — add models in the catalog above, then declare what each one accepts and reasons with here.",
433
743
  inheritsHint: "Showing the inherited model list; saving copies it into your user settings.",
434
744
  readOnly: "The settings document is read-only in this deployment.",
745
+ inputLabel: "Input modalities",
435
746
  choiceDefault: "Provider default",
436
747
  choiceText: "Text only",
437
748
  choiceImage: "Text and image",
749
+ reasoningLabel: "Reasoning levels",
750
+ reasoningInherit: "Catalog default",
751
+ reasoningNone: "Not a reasoning model",
752
+ reasoningCustom: "Declare levels",
753
+ reasoningHint: "A ticked level is one the model picker offers; the value beside it is the spelling sent on the wire, which a gateway may name its own way. Only off may stay empty — offered, and sent as no parameter at all. A level left unticked is not offered.",
754
+ wireLabel: "Wire value for",
755
+ wireNothing: "send nothing",
756
+ needsLevel: "declare at least one level beyond off, or choose “Not a reasoning model”.",
757
+ needsWire: "every level except off needs the wire value to send.",
438
758
  save: "Save",
439
759
  saving: "Saving…",
440
760
  saved: "Saved. The adapter picks it up on its next request.",
@@ -442,16 +762,26 @@ window.__ModuleLoader__.load({
442
762
  };
443
763
  /** Chinese strings (same keys as {@link en}). */
444
764
  const zh = {
445
- title: "输入模态",
765
+ title: "模型能力",
446
766
  loading: "正在读取模型列表…",
447
767
  loadFailed: "读取模型配置失败。",
448
768
  retry: "重试",
449
- empty: "还没有显式模型列表——请先在上方模型目录中添加模型,再回到这里声明输入模态。",
769
+ empty: "还没有显式模型列表——请先在上方模型目录中添加模型,再回到这里声明每个模型接受的输入与推理等级。",
450
770
  inheritsHint: "当前显示的是继承的模型列表;保存会将其复制到你的用户设置层。",
451
771
  readOnly: "当前部署的设置文档为只读。",
772
+ inputLabel: "输入模态",
452
773
  choiceDefault: "提供方默认",
453
774
  choiceText: "仅文本",
454
775
  choiceImage: "文本和图片",
776
+ reasoningLabel: "推理等级",
777
+ reasoningInherit: "目录默认",
778
+ reasoningNone: "非推理模型",
779
+ reasoningCustom: "声明等级",
780
+ reasoningHint: "勾选的等级就是模型选择器会提供的选项;旁边的值是实际发到网关的拼写,网关可以有自己的叫法。只有 off 可以留空——表示提供该等级但完全不发送参数。未勾选的等级不会提供。",
781
+ wireLabel: "发送值:",
782
+ wireNothing: "不发送",
783
+ needsLevel: "至少声明一个 off 以外的等级,或选择「非推理模型」。",
784
+ needsWire: "除 off 外,每个勾选的等级都要填写发送值。",
455
785
  save: "保存",
456
786
  saving: "保存中…",
457
787
  saved: "已保存。适配器会在下一次请求时生效。",
@@ -460,7 +790,7 @@ window.__ModuleLoader__.load({
460
790
  //#endregion
461
791
  //#region src/client/index.ts
462
792
  /** Dictionary namespace owned by this plugin. */
463
- const NS = "settings.models.imageInput";
793
+ const NS = "settings.models.modelCapabilities";
464
794
  /** The effect label prefix. */
465
795
  const PKG = "@jcy2387/dsh-models-input-modalities";
466
796
  /** Required browser services. */
@@ -471,7 +801,7 @@ window.__ModuleLoader__.load({
471
801
  "remote.settings"
472
802
  ];
473
803
  /**
474
- * Register the image-input fold on every llm-pi-ai provider card once the
804
+ * Register the model-capability fold on every llm-pi-ai provider card once the
475
805
  * Models section has declared the seat.
476
806
  * @param ctx - the plugin's client context.
477
807
  */
@@ -480,7 +810,7 @@ window.__ModuleLoader__.load({
480
810
  zh,
481
811
  en
482
812
  }), `${PKG}: dictionaries`);
483
- const controller = new ImageInputController(ctx);
813
+ const controller = new ModelCapabilityController(ctx);
484
814
  ctx.effect(() => controller.watch(), `${PKG}: settings invalidations`);
485
815
  const face = {
486
816
  loadModels: (entry) => controller.load(entry),
@@ -492,7 +822,7 @@ window.__ModuleLoader__.load({
492
822
  key: "llm-pi-ai",
493
823
  locale: NS,
494
824
  inject: () => face
495
- }, ImageInputCard));
825
+ }, ModelCapabilityCard));
496
826
  }
497
827
  //#endregion
498
828
  exports.apply = apply;