@fonlan/dsh-model-parameters 0.1.0 → 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.js CHANGED
@@ -14,7 +14,9 @@ window.__ModuleLoader__.load({
14
14
  const LOCALE_NS = "model-parameters";
15
15
  const zh = {
16
16
  settingsTitle: "模型参数补全",
17
- description: "同步模型时自动从 models.dev 目录补全缺失的显示名称、最大上下文、最大输出、推理强度与输入模态。",
17
+ cardSub: "补全字段、目录刷新、Provider 映射与补全报告。",
18
+ collapse: "收起",
19
+ expand: "展开",
18
20
  master: "启用自动补全",
19
21
  masterDesc: "关闭后不再自动补全任何字段(已补全的值保留)。",
20
22
  fields: "补全字段",
@@ -58,7 +60,9 @@ window.__ModuleLoader__.load({
58
60
  };
59
61
  const en = {
60
62
  settingsTitle: "Model Parameters",
61
- description: "Auto-fill missing display name, context window, max output, reasoning efforts and input modalities from the models.dev catalog when syncing models.",
63
+ cardSub: "Field toggles, catalog refresh, provider mapping and the fill report.",
64
+ collapse: "Collapse",
65
+ expand: "Expand",
62
66
  master: "Enable auto-fill",
63
67
  masterDesc: "When off, no fields are filled (already-filled values are kept).",
64
68
  fields: "Fields to fill",
@@ -131,9 +135,9 @@ window.__ModuleLoader__.load({
131
135
  setConfig: (patch) => call("set-config", { patch })
132
136
  };
133
137
  //#endregion
134
- //#region \0dsh-css:/Users/fonlan/Repos/dsh-model-parameters/src/client/settings-section.css.mjs
135
- const css = ".mp-root {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n}\n\n.mp-desc {\n margin: 0;\n opacity: 0.85;\n font-size: 13px;\n line-height: 1.5;\n}\n\n.mp-card {\n border: 1px solid var(--dsh-border-color, rgba(128, 128, 128, 0.25));\n border-radius: 10px;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background: var(--dsh-surface-color, transparent);\n}\n\n.mp-card-title {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n opacity: 0.9;\n}\n\n.mp-toggle {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.mp-toggle input[type='checkbox'] {\n margin-top: 3px;\n accent-color: var(--dsh-accent-color, #4f8cff);\n}\n\n.mp-toggle-sub {\n padding-left: 22px;\n}\n\n.mp-toggle-body {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mp-toggle-label {\n font-size: 13px;\n}\n\n.mp-toggle-desc {\n font-size: 12px;\n opacity: 0.65;\n}\n\n.mp-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 6px 16px;\n font-size: 13px;\n}\n\n.mp-ttl {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n\n.mp-ttl input {\n width: 64px;\n padding: 3px 6px;\n border-radius: 6px;\n border: 1px solid var(--dsh-border-color, rgba(128, 128, 128, 0.35));\n background: var(--dsh-input-bg, transparent);\n color: inherit;\n}\n\n.mp-actions {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.mp-button {\n padding: 6px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsh-border-color, rgba(128, 128, 128, 0.35));\n background: var(--dsh-accent-color, #4f8cff);\n color: #fff;\n font-size: 13px;\n cursor: pointer;\n}\n\n.mp-button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.mp-button-ghost {\n background: transparent;\n color: inherit;\n}\n\n.mp-map-row {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mp-map-row input {\n flex: 1;\n padding: 4px 8px;\n border-radius: 6px;\n border: 1px solid var(--dsh-border-color, rgba(128, 128, 128, 0.35));\n background: var(--dsh-input-bg, transparent);\n color: inherit;\n font-size: 13px;\n}\n\n.mp-map-arrow {\n opacity: 0.6;\n}\n\n.mp-report {\n margin: 0;\n padding-left: 18px;\n font-size: 13px;\n line-height: 1.7;\n}\n\n.mp-code {\n background: var(--dsh-code-bg, rgba(128, 128, 128, 0.15));\n border-radius: 4px;\n padding: 1px 5px;\n font-size: 12px;\n word-break: break-all;\n}\n\n.mp-badge {\n font-size: 11px;\n padding: 1px 7px;\n border-radius: 999px;\n margin-left: 4px;\n}\n\n.mp-badge-ok {\n background: rgba(52, 199, 89, 0.18);\n color: #34c759;\n}\n\n.mp-badge-warn {\n background: rgba(255, 149, 0, 0.18);\n color: #ff9500;\n}\n\n.mp-muted {\n font-size: 13px;\n opacity: 0.6;\n}\n\n.mp-error {\n font-size: 13px;\n color: #ff3b30;\n}\n";
136
- const tagId = "@fonlan/dsh-model-parameters/settings-section.css";
138
+ //#region \0dsh-css:/Users/fonlan/Repos/dsh-model-parameters/src/client/settings-card.css.mjs
139
+ const css = "/**\n * Styles for the Model Parameters Settings Card (设置 → 插件 → 插件配置).\n *\n * The expandable chrome mirrors the built-in plugin cards\n * (dsh-client-ui-settings-plugins PluginCard.module.css): external plugins\n * cannot import PluginCard, so the shell/head/chevron/body are re-drawn with\n * the same tokens and geometry. Inner controls follow the built-in cards'\n * field language (ValueField): border-l2 inputs on bg-layer-3, inverted\n * primary buttons, divider-separated groups.\n */\n\n/* ── card chrome (aligned with the built-in PluginCard) ─────────────────── */\n\n.mp-card {\n border: 1px solid var(--dsw-alias-border-l2, #2a3346);\n background: var(--dsw-alias-bg-layer-3, #0d1017);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n\n.mp-card:hover {\n border-color: var(--dsw-alias-label-dimmed, #4a5468);\n}\n\n.mp-card[data-open] {\n background: var(--dsw-alias-bg-layer-2, #141a26);\n border-color: var(--dsw-alias-label-dimmed, #4a5468);\n}\n\n.mp-card-head {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: transparent;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n\n.mp-card-head:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4f8cff);\n outline-offset: -2px;\n}\n\n.mp-card-headText {\n flex-direction: column;\n flex: 1;\n gap: 4px;\n min-width: 0;\n display: flex;\n}\n\n.mp-card-title {\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n.mp-card-sub {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n font-size: 13px;\n line-height: 1.5;\n}\n\n.mp-card-chevron {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n flex: none;\n transition: transform .16s;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.mp-card-chevron[data-open] {\n transform: rotate(180deg);\n}\n\n.mp-card-body {\n border-top: 1px solid var(--dsw-alias-border-l2, #2a3346);\n margin: 0 16px;\n padding: 12px 0 8px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n/* ── body layout ────────────────────────────────────────────────────────── */\n\n.mp-body {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.mp-group {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mp-group-title {\n margin: 0;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n\n.mp-divider {\n border: none;\n border-top: 1px solid var(--dsw-alias-border-l2, #2a3346);\n margin: 0;\n}\n\n.mp-desc {\n margin: 0;\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n font-size: 12px;\n line-height: 1.6;\n}\n\n.mp-muted {\n margin: 0;\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n font-size: 13px;\n line-height: 1.6;\n}\n\n.mp-error {\n margin: 0;\n color: var(--dsw-alias-label-error, #ff8f8f);\n font-size: 13px;\n line-height: 1.6;\n}\n\n/* ── toggles ────────────────────────────────────────────────────────────── */\n\n.mp-toggle {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.mp-toggle input[type='checkbox'] {\n margin-top: 3px;\n accent-color: var(--dsw-alias-brand-primary, #4f8cff);\n}\n\n.mp-toggle-sub {\n padding-left: 22px;\n}\n\n.mp-toggle-body {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mp-toggle-label {\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 13px;\n line-height: 1.5;\n}\n\n.mp-toggle-desc {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n font-size: 12px;\n line-height: 1.5;\n}\n\n/* ── catalog meta / ttl ─────────────────────────────────────────────────── */\n\n.mp-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px 16px;\n color: var(--dsw-alias-label-secondary, #aab2c5);\n font-size: 13px;\n line-height: 1.6;\n}\n\n.mp-ttl {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--dsw-alias-label-secondary, #aab2c5);\n font-size: 13px;\n line-height: 1.6;\n}\n\n/* ── inputs (ValueField language) ───────────────────────────────────────── */\n\n.mp-input {\n border: 1px solid var(--dsw-alias-border-l2, #2a3346);\n background: var(--dsw-alias-bg-layer-3, #0d1017);\n color: var(--dsw-alias-label-primary, #e6e6e6);\n height: 34px;\n border-radius: 8px;\n padding: 0 12px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n}\n\n.mp-input:focus-visible {\n border-color: var(--dsw-alias-brand-primary, #4f8cff);\n outline: none;\n}\n\n.mp-input:disabled {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n cursor: default;\n}\n\n.mp-input-narrow {\n width: 72px;\n}\n\n/* ── mapping rows ───────────────────────────────────────────────────────── */\n\n.mp-map-row {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mp-map-row .mp-input {\n flex: 1;\n min-width: 0;\n}\n\n.mp-map-arrow {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n flex: none;\n}\n\n/* ── buttons (PluginCard footer language) ────────────────────────────────── */\n\n.mp-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.mp-button {\n appearance: none;\n font: inherit;\n cursor: pointer;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font-size: 13px;\n line-height: 1.5;\n background: var(--dsw-alias-label-primary, #e6e6e6);\n color: var(--dsw-alias-bg-layer-3, #0d1017);\n}\n\n.mp-button:hover:not(:disabled) {\n opacity: .9;\n}\n\n.mp-button:disabled {\n opacity: .4;\n cursor: default;\n}\n\n.mp-button:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4f8cff);\n outline-offset: 1px;\n}\n\n.mp-button-ghost {\n background: transparent;\n color: var(--dsw-alias-label-secondary, #aab2c5);\n border-color: var(--dsw-alias-border-l2, #2a3346);\n}\n\n.mp-button-ghost:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary, #e6e6e6);\n border-color: var(--dsw-alias-label-dimmed, #4a5468);\n}\n\n/* ── report / badges / code ─────────────────────────────────────────────── */\n\n.mp-report {\n margin: 0;\n padding-left: 18px;\n color: var(--dsw-alias-label-secondary, #aab2c5);\n font-size: 13px;\n line-height: 1.7;\n}\n\n.mp-code {\n background: var(--dsw-alias-bg-module-platform, rgba(128, 128, 128, 0.15));\n border-radius: 4px;\n padding: 1px 5px;\n font-size: 12px;\n word-break: break-all;\n}\n\n.mp-badge {\n white-space: nowrap;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n padding: 1px 8px;\n border-radius: 999px;\n margin-left: 4px;\n}\n\n.mp-badge-ok {\n background: rgba(52, 199, 89, 0.18);\n color: #34c759;\n}\n\n.mp-badge-warn {\n background: rgba(255, 149, 0, 0.18);\n color: #ff9500;\n}\n";
140
+ const tagId = "@fonlan/dsh-model-parameters/settings-card.css";
137
141
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
138
142
  const tag = document.createElement("style");
139
143
  tag.dataset.plugin = "@fonlan/dsh-model-parameters";
@@ -142,13 +146,22 @@ window.__ModuleLoader__.load({
142
146
  document.head.appendChild(tag);
143
147
  }
144
148
  //#endregion
145
- //#region src/client/settings-section.tsx
149
+ //#region src/client/settings-card.tsx
146
150
  /**
147
- * The Model Parameters settings page: master + per-field toggles, catalog TTL,
151
+ * The Model Parameters Settings Card: master + per-field toggles, catalog TTL,
148
152
  * provider→models.dev mapping overrides, a manual refresh/fill action, the
149
- * catalog freshness card, and the last fill report (filled / touched /
150
- * unmatched). All reads/mutations go through the plugin's fenced API; config
151
- * persists into the `model-parameters` settings namespace (settings.yaml).
153
+ * catalog freshness line, and the last fill report (filled / touched /
154
+ * unmatched).
155
+ *
156
+ * The card registers into the `settings.plugin.item` slot keyed by the
157
+ * `model-parameters` settings namespace, so it renders inside
158
+ * 设置 → 插件 → 插件配置, stacked with the built-in plugin cards. The chrome is
159
+ * self-drawn (external plugins cannot import the built-in PluginCard) with
160
+ * styles aligned to it: an expandable header (collapsed by default) and a body
161
+ * that discloses the controls in place.
162
+ *
163
+ * All reads/mutations go through the plugin's fenced API; config persists into
164
+ * the `model-parameters` settings namespace (settings.yaml).
152
165
  */
153
166
  function useLocaleRevision(ctx) {
154
167
  const subscribe = (0, react.useCallback)((onChange) => {
@@ -174,7 +187,7 @@ window.__ModuleLoader__.load({
174
187
  return String(epochMs);
175
188
  }
176
189
  }
177
- function makeSettingsSection(ctx) {
190
+ function makeSettingsCard(ctx) {
178
191
  const t = (() => {
179
192
  try {
180
193
  return ctx.locale.bind(LOCALE_NS);
@@ -182,8 +195,9 @@ window.__ModuleLoader__.load({
182
195
  return (key) => key;
183
196
  }
184
197
  })();
185
- return function ModelParametersSettingsSection() {
198
+ return function ModelParametersCard() {
186
199
  useLocaleRevision(ctx);
200
+ const [open, setOpen] = (0, react.useState)(false);
187
201
  const [state, setState] = (0, react.useState)(null);
188
202
  const [error, setError] = (0, react.useState)(null);
189
203
  const [loading, setLoading] = (0, react.useState)(true);
@@ -258,308 +272,373 @@ window.__ModuleLoader__.load({
258
272
  }
259
273
  await patchConfig({ providerMap });
260
274
  }, [mappingRows, patchConfig]);
261
- if (error !== null && state === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
262
- className: "mp-error",
263
- children: [
264
- t("error"),
265
- ": ",
266
- error
267
- ]
268
- });
269
- if (loading || state === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
270
- className: "mp-muted",
271
- children: t("saving")
272
- });
273
- const config = state.config;
274
- const catalog = state.catalog;
275
- const report = state.report;
276
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
277
- className: "mp-root",
278
- children: [
279
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
280
- className: "mp-desc",
281
- children: t("description")
282
- }),
283
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
284
- className: "mp-card",
275
+ const title = t("settingsTitle");
276
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
277
+ className: "mp-card",
278
+ "data-open": open ? "" : void 0,
279
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
280
+ type: "button",
281
+ className: "mp-card-head",
282
+ "aria-expanded": open,
283
+ "aria-label": (open ? t("collapse") : t("expand")) + ":" + title,
284
+ onClick: () => setOpen(!open),
285
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
286
+ className: "mp-card-headText",
287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
288
+ className: "mp-card-title",
289
+ children: title
290
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
291
+ className: "mp-card-sub",
292
+ children: t("cardSub")
293
+ })]
294
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
295
+ className: "mp-card-chevron",
296
+ "data-open": open ? "" : void 0,
297
+ "aria-hidden": "true",
298
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
299
+ width: "14",
300
+ height: "14",
301
+ viewBox: "0 0 14 14",
302
+ fill: "none",
303
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
304
+ d: "M3.5 5.25L7 8.75L10.5 5.25",
305
+ stroke: "currentColor",
306
+ strokeWidth: "1.5",
307
+ strokeLinecap: "round",
308
+ strokeLinejoin: "round"
309
+ })
310
+ })
311
+ })]
312
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
313
+ className: "mp-card-body",
314
+ children: error !== null && state === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
315
+ className: "mp-error",
285
316
  children: [
286
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
287
- className: "mp-card-title",
288
- children: t("fields")
289
- }),
290
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
291
- className: "mp-toggle",
292
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
293
- type: "checkbox",
294
- checked: config.enabled,
295
- disabled: saving,
296
- onChange: (event) => void patchConfig({ enabled: event.target.checked })
297
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
298
- className: "mp-toggle-body",
299
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
300
- className: "mp-toggle-label",
301
- children: t("master")
302
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
303
- className: "mp-toggle-desc",
304
- children: t("masterDesc")
305
- })]
317
+ t("error"),
318
+ ": ",
319
+ error
320
+ ]
321
+ }) : loading || state === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
322
+ className: "mp-muted",
323
+ children: t("saving")
324
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelParametersCardBody, {
325
+ t,
326
+ state,
327
+ error,
328
+ saving,
329
+ busy,
330
+ mappingRows,
331
+ newFrom,
332
+ newTo,
333
+ setNewFrom,
334
+ setNewTo,
335
+ setMappingRows,
336
+ onPatchConfig: patchConfig,
337
+ onRefresh: refresh,
338
+ onReconcile: reconcileNow,
339
+ onApplyMappings: applyMappings
340
+ })
341
+ })]
342
+ });
343
+ };
344
+ }
345
+ /** The card body: divider-separated control groups, styled like the built-in cards. */
346
+ function ModelParametersCardBody(props) {
347
+ const { t, state, error, saving, busy, mappingRows, newFrom, newTo, setNewFrom, setNewTo, setMappingRows, onPatchConfig, onRefresh, onReconcile, onApplyMappings } = props;
348
+ const config = state.config;
349
+ const catalog = state.catalog;
350
+ const report = state.report;
351
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
352
+ className: "mp-body",
353
+ children: [
354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
355
+ className: "mp-group",
356
+ children: [
357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
358
+ className: "mp-group-title",
359
+ children: t("fields")
360
+ }),
361
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
362
+ className: "mp-toggle",
363
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
364
+ type: "checkbox",
365
+ checked: config.enabled,
366
+ disabled: saving,
367
+ onChange: (event) => void onPatchConfig({ enabled: event.target.checked })
368
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
369
+ className: "mp-toggle-body",
370
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
371
+ className: "mp-toggle-label",
372
+ children: t("master")
373
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
374
+ className: "mp-toggle-desc",
375
+ children: t("masterDesc")
306
376
  })]
307
- }),
377
+ })]
378
+ }),
379
+ [
308
380
  [
309
- [
310
- "fillName",
311
- t("fillName"),
312
- t("fillNameDesc")
313
- ],
314
- [
315
- "fillContext",
316
- t("fillContext"),
317
- t("fillContextDesc")
318
- ],
319
- [
320
- "fillMaxTokens",
321
- t("fillMaxTokens"),
322
- t("fillMaxTokensDesc")
323
- ],
324
- [
325
- "fillReasoning",
326
- t("fillReasoning"),
327
- t("fillReasoningDesc")
328
- ],
329
- [
330
- "fillInput",
331
- t("fillInput"),
332
- t("fillInputDesc")
333
- ]
334
- ].map(([key, label, desc]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
335
- className: "mp-toggle mp-toggle-sub",
336
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
337
- type: "checkbox",
338
- checked: config[key],
339
- disabled: saving || !config.enabled,
340
- onChange: (event) => void patchConfig({ [key]: event.target.checked })
341
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
342
- className: "mp-toggle-body",
343
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
344
- className: "mp-toggle-label",
345
- children: label
346
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
347
- className: "mp-toggle-desc",
348
- children: desc
349
- })]
350
- })]
351
- }, key))
352
- ]
353
- }),
354
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
355
- className: "mp-card",
356
- children: [
357
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
358
- className: "mp-card-title",
359
- children: t("catalog")
360
- }),
361
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
362
- className: "mp-meta",
363
- children: [
364
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
365
- t("catalogEntries"),
366
- ": ",
367
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalog.entries.toLocaleString() })
368
- ] }),
369
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
370
- t("catalogProviders"),
371
- ": ",
372
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalog.providers.toLocaleString() })
373
- ] }),
374
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
375
- t("catalogFetched"),
376
- ":",
377
- " ",
378
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalog.fetchedAt > 0 ? formatTime(catalog.fetchedAt) : t("catalogNever") }),
379
- " ",
380
- catalog.fetchedAt > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
381
- className: catalog.fresh ? "mp-badge mp-badge-ok" : "mp-badge mp-badge-warn",
382
- children: catalog.fresh ? t("catalogFresh") : t("catalogStale")
383
- })
384
- ] }),
385
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
386
- className: "mp-ttl",
387
- children: [t("ttl"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
388
- type: "number",
389
- min: 1,
390
- max: 90,
391
- value: config.ttlDays,
392
- disabled: saving,
393
- onChange: (event) => {
394
- const days = Number(event.target.value);
395
- if (Number.isFinite(days) && days >= 1 && days <= 90) patchConfig({ ttlDays: Math.round(days) });
396
- }
397
- })]
398
- })
399
- ]
400
- }),
401
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
402
- className: "mp-actions",
403
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
404
- className: "mp-button",
405
- disabled: busy,
406
- onClick: () => void refresh(),
407
- children: busy ? t("refreshBusy") : t("refresh")
408
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
409
- className: "mp-button mp-button-ghost",
410
- disabled: busy,
411
- onClick: () => void reconcileNow(),
412
- children: t("reconcileNow")
381
+ "fillName",
382
+ t("fillName"),
383
+ t("fillNameDesc")
384
+ ],
385
+ [
386
+ "fillContext",
387
+ t("fillContext"),
388
+ t("fillContextDesc")
389
+ ],
390
+ [
391
+ "fillMaxTokens",
392
+ t("fillMaxTokens"),
393
+ t("fillMaxTokensDesc")
394
+ ],
395
+ [
396
+ "fillReasoning",
397
+ t("fillReasoning"),
398
+ t("fillReasoningDesc")
399
+ ],
400
+ [
401
+ "fillInput",
402
+ t("fillInput"),
403
+ t("fillInputDesc")
404
+ ]
405
+ ].map(([key, label, desc]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
406
+ className: "mp-toggle mp-toggle-sub",
407
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
408
+ type: "checkbox",
409
+ checked: config[key],
410
+ disabled: saving || !config.enabled,
411
+ onChange: (event) => void onPatchConfig({ [key]: event.target.checked })
412
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
413
+ className: "mp-toggle-body",
414
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
415
+ className: "mp-toggle-label",
416
+ children: label
417
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
418
+ className: "mp-toggle-desc",
419
+ children: desc
413
420
  })]
414
- })
415
- ]
416
- }),
417
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
418
- className: "mp-card",
419
- children: [
420
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
421
- className: "mp-card-title",
422
- children: t("providerMap")
423
- }),
424
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
425
- className: "mp-toggle-desc",
426
- children: t("providerMapDesc")
427
- }),
428
- mappingRows.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
429
- className: "mp-map-row",
430
- children: [
431
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
432
- value: row.from,
433
- disabled: saving,
434
- onChange: (event) => {
435
- const next = [...mappingRows];
436
- next[index] = {
437
- ...row,
438
- from: event.target.value
439
- };
440
- setMappingRows(next);
441
- }
442
- }),
443
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
444
- className: "mp-map-arrow",
445
- children: "→"
446
- }),
447
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
448
- value: row.to,
449
- disabled: saving,
450
- onChange: (event) => {
451
- const next = [...mappingRows];
452
- next[index] = {
453
- ...row,
454
- to: event.target.value
455
- };
456
- setMappingRows(next);
457
- }
458
- }),
459
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
460
- className: "mp-button mp-button-ghost",
461
- disabled: saving,
462
- onClick: () => setMappingRows(mappingRows.filter((_, i) => i !== index)),
463
- children: t("removeMapping")
464
- })
465
- ]
466
- }, index)),
467
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
468
- className: "mp-map-row",
469
- children: [
470
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
471
- placeholder: "dsh provider",
472
- value: newFrom,
473
- onChange: (event) => setNewFrom(event.target.value)
474
- }),
475
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
476
- className: "mp-map-arrow",
477
- children: "→"
478
- }),
479
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
480
- placeholder: "models.dev provider",
481
- value: newTo,
482
- onChange: (event) => setNewTo(event.target.value)
483
- }),
484
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
485
- className: "mp-button",
486
- disabled: newFrom.trim().length === 0 || newTo.trim().length === 0,
487
- onClick: () => {
488
- setMappingRows([...mappingRows, {
489
- from: newFrom.trim(),
490
- to: newTo.trim()
491
- }]);
492
- setNewFrom("");
493
- setNewTo("");
494
- },
495
- children: t("addMapping")
496
- })
497
- ]
498
- }),
499
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
500
- className: "mp-actions",
501
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
502
- className: "mp-button mp-button-ghost",
503
- disabled: saving,
504
- onClick: () => void applyMappings(),
505
- children: saving ? t("saving") : "✓ " + t("providerMap")
506
- })
507
- })
508
- ]
509
- }),
510
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
511
- className: "mp-card",
512
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
513
- className: "mp-card-title",
514
- children: t("report")
515
- }), report === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
516
- className: "mp-muted",
517
- children: t("reportNever")
518
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
519
- className: "mp-report",
421
+ })]
422
+ }, key))
423
+ ]
424
+ }),
425
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { className: "mp-divider" }),
426
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
427
+ className: "mp-group",
428
+ children: [
429
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
430
+ className: "mp-group-title",
431
+ children: t("catalog")
432
+ }),
433
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
434
+ className: "mp-meta",
520
435
  children: [
521
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
522
- t("reportAt"),
523
- ": ",
524
- formatTime(report.at)
525
- ] }),
526
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
527
- t("reportFilled"),
436
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
437
+ t("catalogEntries"),
528
438
  ": ",
529
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: report.filled })
439
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalog.entries.toLocaleString() })
530
440
  ] }),
531
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
532
- t("reportTouched"),
441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
442
+ t("catalogProviders"),
533
443
  ": ",
534
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: report.touched })
444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalog.providers.toLocaleString() })
535
445
  ] }),
536
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
537
- t("reportUnmatched"),
446
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
447
+ t("catalogFetched"),
538
448
  ":",
539
449
  " ",
540
- report.unmatched.length === 0 ? t("reportEmpty") : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
541
- className: "mp-code",
542
- children: report.unmatched.join(", ")
450
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalog.fetchedAt > 0 ? formatTime(catalog.fetchedAt) : t("catalogNever") }),
451
+ " ",
452
+ catalog.fetchedAt > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
453
+ className: catalog.fresh ? "mp-badge mp-badge-ok" : "mp-badge mp-badge-warn",
454
+ children: catalog.fresh ? t("catalogFresh") : t("catalogStale")
543
455
  })
544
- ] })
456
+ ] }),
457
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
458
+ className: "mp-ttl",
459
+ children: [t("ttl"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
460
+ className: "mp-input mp-input-narrow",
461
+ type: "number",
462
+ min: 1,
463
+ max: 90,
464
+ value: config.ttlDays,
465
+ disabled: saving,
466
+ onChange: (event) => {
467
+ const days = Number(event.target.value);
468
+ if (Number.isFinite(days) && days >= 1 && days <= 90) onPatchConfig({ ttlDays: Math.round(days) });
469
+ }
470
+ })]
471
+ })
545
472
  ]
546
- })]
547
- }),
548
- error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
549
- className: "mp-error",
550
- children: error
551
- })
552
- ]
553
- });
554
- };
473
+ }),
474
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
475
+ className: "mp-actions",
476
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
477
+ className: "mp-button",
478
+ disabled: busy,
479
+ onClick: () => void onRefresh(),
480
+ children: busy ? t("refreshBusy") : t("refresh")
481
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
482
+ className: "mp-button mp-button-ghost",
483
+ disabled: busy,
484
+ onClick: () => void onReconcile(),
485
+ children: t("reconcileNow")
486
+ })]
487
+ })
488
+ ]
489
+ }),
490
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { className: "mp-divider" }),
491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
492
+ className: "mp-group",
493
+ children: [
494
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
495
+ className: "mp-group-title",
496
+ children: t("providerMap")
497
+ }),
498
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
499
+ className: "mp-desc",
500
+ children: t("providerMapDesc")
501
+ }),
502
+ mappingRows.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
503
+ className: "mp-map-row",
504
+ children: [
505
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
506
+ className: "mp-input",
507
+ value: row.from,
508
+ disabled: saving,
509
+ onChange: (event) => {
510
+ const next = [...mappingRows];
511
+ next[index] = {
512
+ ...row,
513
+ from: event.target.value
514
+ };
515
+ setMappingRows(next);
516
+ }
517
+ }),
518
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
519
+ className: "mp-map-arrow",
520
+ children: "→"
521
+ }),
522
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
523
+ className: "mp-input",
524
+ value: row.to,
525
+ disabled: saving,
526
+ onChange: (event) => {
527
+ const next = [...mappingRows];
528
+ next[index] = {
529
+ ...row,
530
+ to: event.target.value
531
+ };
532
+ setMappingRows(next);
533
+ }
534
+ }),
535
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
536
+ className: "mp-button mp-button-ghost",
537
+ disabled: saving,
538
+ onClick: () => setMappingRows(mappingRows.filter((_, i) => i !== index)),
539
+ children: t("removeMapping")
540
+ })
541
+ ]
542
+ }, index)),
543
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
544
+ className: "mp-map-row",
545
+ children: [
546
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
547
+ className: "mp-input",
548
+ placeholder: "dsh provider",
549
+ value: newFrom,
550
+ onChange: (event) => setNewFrom(event.target.value)
551
+ }),
552
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
553
+ className: "mp-map-arrow",
554
+ children: "→"
555
+ }),
556
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
557
+ className: "mp-input",
558
+ placeholder: "models.dev provider",
559
+ value: newTo,
560
+ onChange: (event) => setNewTo(event.target.value)
561
+ }),
562
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
563
+ className: "mp-button mp-button-ghost",
564
+ disabled: newFrom.trim().length === 0 || newTo.trim().length === 0,
565
+ onClick: () => {
566
+ setMappingRows([...mappingRows, {
567
+ from: newFrom.trim(),
568
+ to: newTo.trim()
569
+ }]);
570
+ setNewFrom("");
571
+ setNewTo("");
572
+ },
573
+ children: t("addMapping")
574
+ })
575
+ ]
576
+ }),
577
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
578
+ className: "mp-actions",
579
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
580
+ className: "mp-button",
581
+ disabled: saving,
582
+ onClick: () => void onApplyMappings(),
583
+ children: saving ? t("saving") : "✓ " + t("providerMap")
584
+ })
585
+ })
586
+ ]
587
+ }),
588
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { className: "mp-divider" }),
589
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
590
+ className: "mp-group",
591
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
592
+ className: "mp-group-title",
593
+ children: t("report")
594
+ }), report === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
595
+ className: "mp-muted",
596
+ children: t("reportNever")
597
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
598
+ className: "mp-report",
599
+ children: [
600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
601
+ t("reportAt"),
602
+ ": ",
603
+ formatTime(report.at)
604
+ ] }),
605
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
606
+ t("reportFilled"),
607
+ ": ",
608
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: report.filled })
609
+ ] }),
610
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
611
+ t("reportTouched"),
612
+ ": ",
613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: report.touched })
614
+ ] }),
615
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
616
+ t("reportUnmatched"),
617
+ ":",
618
+ " ",
619
+ report.unmatched.length === 0 ? t("reportEmpty") : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
620
+ className: "mp-code",
621
+ children: report.unmatched.join(", ")
622
+ })
623
+ ] })
624
+ ]
625
+ })]
626
+ }),
627
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
628
+ className: "mp-error",
629
+ children: error
630
+ })
631
+ ]
632
+ });
555
633
  }
556
634
  //#endregion
557
635
  //#region src/client/index.tsx
636
+ /** The settings namespace this card edits (must match the host half). */
637
+ const PLUGIN_NS = "model-parameters";
558
638
  /** Services required before mounting (provided by the client runtime). */
559
639
  const inject = ["slots", "locale"];
560
640
  /** Client plugin body. */
561
641
  function apply(ctx) {
562
- const t = ctx.locale.bind(LOCALE_NS);
563
642
  ctx.effect(() => {
564
643
  const off = ctx.locale.register(LOCALE_NS, {
565
644
  zh,
@@ -567,14 +646,12 @@ window.__ModuleLoader__.load({
567
646
  });
568
647
  return () => off();
569
648
  }, "model-parameters: dictionaries");
570
- const SettingsSection = makeSettingsSection(ctx);
571
- ctx.slots.inject("settings.section", () => ctx.slots.register({
572
- name: "settings.section",
573
- id: "model-parameters",
574
- order: 320,
575
- label: () => t("settingsTitle"),
576
- locale: LOCALE_NS
577
- }, SettingsSection));
649
+ const Card = makeSettingsCard(ctx);
650
+ const slots = ctx.slots;
651
+ slots.inject("settings.plugin.item", () => slots.register({
652
+ name: "settings.plugin.item",
653
+ key: PLUGIN_NS
654
+ }, Card));
578
655
  }
579
656
  //#endregion
580
657
  exports.apply = apply;