@qilin-agent/client-ui-settings-models 3.1.7 → 3.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -21,8 +21,8 @@
21
21
  en: 644c771c6398d855
22
22
  zh: 3fae98747222b713
23
23
  /qilin-agent-client-ui-settings-models/use-this-package/editing-a-provider:
24
- en: bf92096bdde5e93a
25
- zh: 4012a71b7d0f01d2
24
+ en: 79cb8c3a351644c2
25
+ zh: 05b6e54500e573dd
26
26
  /qilin-agent-client-ui-settings-models/use-this-package/adding-and-deleting-providers:
27
27
  en: 30c980f905a0245d
28
28
  zh: bc119152d4f1a16d
package/README.md CHANGED
@@ -47,7 +47,9 @@ The collapsed 自定义设置 fold carries the curated extras: `baseURL` for bot
47
47
 
48
48
  The DeepSeek card edits the shared `llm-deepseek` endpoint, credentials, and model catalog without a protocol selector. When Cordis YAML selects Messages, the public endpoint placeholder is `https://api.deepseek.com/anthropic`. Saving the card preserves protocol configuration.
49
49
 
50
- Expand **Customized settings → Model options** to edit each model. Both provider families share the same row layout, labels, and icons: context window and max output tokens occupy two columns, and **Input types** occupies a separate row with **Text** and **Image** checkboxes. A row without an input declaration displays the installed model’s input types, then the provider default, then Text. Known pi-ai providers load their installed catalog without endpoint I/O; opening a row does not write an override. Explicit input selections take precedence, including text-only overrides of vision models. Checkbox edits save the selected types, with at least one type required. DeepSeek writes `inputModalities`; pi-ai writes `input`. Unchecking Image for DeepSeek also removes `imagePixelBudget` and `imageMaxBytes`, because the adapter rejects those limits without image input. Clearing the input field in `settings.yaml` restores adapter inheritance; **Restore default models** resets the entire catalog override. Declare only input types the upstream model can actually process.
50
+ Drag a model’s three-line handle to change its position within the provider, or focus the handle and use the up and down arrow keys. Apply saves the order; Cancel discards it. Each model keeps its settings, expanded options, and unfinished capacity text when moved. The drag preview and the reduced-motion-aware row animation come from shared UI primitives, and before/after insertion markers show the target slot.
51
+
52
+ Expand **Customized settings → Model options** to edit each model. Both provider families share the same row layout, labels, and icons: context window and max output tokens occupy two columns, and **Input types** occupies a separate row with **Text** and **Image** checkboxes. A row narrower than 440px moves its display-name field under the id, so both identity fields stay editable instead of shrinking under their placeholders. A row without an input declaration displays the installed model’s input types, then the provider default, then Text. Known pi-ai providers load their installed catalog without endpoint I/O; opening a row does not write an override. Explicit input selections take precedence, including text-only overrides of vision models. Checkbox edits save the selected types, with at least one type required. DeepSeek writes `inputModalities`; pi-ai writes `input`. Unchecking Image for DeepSeek also removes `imagePixelBudget` and `imageMaxBytes`, because the adapter rejects those limits without image input. Clearing the input field in `settings.yaml` restores adapter inheritance; **Restore default models** resets the entire catalog override. Declare only input types the upstream model can actually process.
51
53
 
52
54
  A model row on a hand-declared pi-ai route also edits its **Reasoning levels**, the `reasoningEfforts` the adapter already reads from `settings.yaml`; the **?** beside the label explains the two columns. With the switch off the field is absent, so the model does not reason and the model picker offers no level; a stored `false` is kept until the switch is used. Turning it on always starts from rows for off, low, medium, and high; it does not restore levels removed earlier, and **Cancel** discards the edit instead. Each row pairs a level select — the keys `llm-pi-ai`'s configuration schema accepts in `reasoningEfforts`, read from the namespace schema like the protocol choices and today `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`, each usable once — with the **API value** sent for it, which starts as the level name; a field cleared while typing stores the level name and shows it once the field is left. Off only turns reasoning off, so its value field is disabled; a value already written for it in `settings.yaml` is shown and kept. Changing a row's level resets a value still spelled as the old level; a custom value carries over only between two thinking levels, so changing a row to or from Off resets it. A stored key the schema does not list keeps its row and stays selectable there. Rows can be added and deleted and are saved in row order, while the model picker lists the levels in escalation order. The section refuses what the adapter refuses: no level besides Off — including a `reasoningEfforts:` left without a value, which shows the switch on with no rows — or a value that is missing on a level besides Off or empty on any level. A refused value marks its field invalid, and leaving that field fills in the level name. The rows keep no state of their own, so a model row reused after a deletion, a reset, or a fold shows only its own model. Routes served from the installed catalog and the DeepSeek card keep the adapter's levels and show no reasoning control.
53
55
 
package/README.zh.md CHANGED
@@ -47,7 +47,9 @@ API 密钥输入框初始为空,并使用 `autocomplete="new-password"` 提示
47
47
 
48
48
  `llm-deepseek` 的 DeepSeek 卡片编辑共用的端点、凭据和模型目录,不提供协议选择器。Cordis YAML 选择 Messages 时,官方端点占位符为 `https://api.deepseek.com/anthropic`;保存卡片不会改写协议配置。
49
49
 
50
- 展开**自定义设置 → 模型选项**编辑模型。两个提供方家族共用相同的模型行布局、标签和图标:上下文窗口与最大输出 token 数分为两列,**输入类型**独占下一行,提供**文本**和**图片**复选框。未声明输入类型的模型行优先显示已安装模型的输入类型,其次是提供方默认值,最后回退为文本。已知 pi-ai 提供方会加载已安装目录,不向端点发送请求;打开模型行不会写入覆盖值。显式输入选择具有优先权,包括为视觉模型设置的仅文本覆盖。修改复选框会保存所选类型,且至少保留一种。DeepSeek 写入 `inputModalities`,pi-ai 写入 `input`。DeepSeek 取消勾选图片时,还会移除 `imagePixelBudget` 和 `imageMaxBytes`,因为适配器在没有图片输入时拒绝这些限制。在 `settings.yaml` 中清除输入字段可恢复适配器继承;**恢复默认模型**会重置整个模型目录覆盖。仅声明上游模型实际能够处理的输入类型。
50
+ 拖动模型的三横线手柄可调整它在当前提供方内的位置,也可以聚焦手柄后按上下方向键。保存会保留顺序;取消会放弃修改。移动时,每个模型的参数、已展开选项和未完成的容量输入都会随模型保留。拖拽预览与遵循减少动态效果设置的行动画来自共享 UI 原语,前后插入标记显示目标位置。
51
+
52
+ 展开**自定义设置 → 模型选项**编辑模型。两个提供方家族共用相同的模型行布局、标签和图标:上下文窗口与最大输出 token 数分为两列,**输入类型**独占下一行,提供**文本**和**图片**复选框。宽度窄于 440px 的行把显示名字段移到 id 下方,两个身份字段都保持可编辑,而不会被挤到占位符之下。未声明输入类型的模型行优先显示已安装模型的输入类型,其次是提供方默认值,最后回退为文本。已知 pi-ai 提供方会加载已安装目录,不向端点发送请求;打开模型行不会写入覆盖值。显式输入选择具有优先权,包括为视觉模型设置的仅文本覆盖。修改复选框会保存所选类型,且至少保留一种。DeepSeek 写入 `inputModalities`,pi-ai 写入 `input`。DeepSeek 取消勾选图片时,还会移除 `imagePixelBudget` 和 `imageMaxBytes`,因为适配器在没有图片输入时拒绝这些限制。在 `settings.yaml` 中清除输入字段可恢复适配器继承;**恢复默认模型**会重置整个模型目录覆盖。仅声明上游模型实际能够处理的输入类型。
51
53
 
52
54
  手工声明的 pi-ai 路由上,模型行还会编辑**推理等级**,即适配器本来就从 `settings.yaml` 读取的 `reasoningEfforts`;标签旁的 **?** 说明两列的含义。开关关闭时该字段不存在,模型不推理,模型选择器不提供任何档位;已存储的 `false` 在开关被操作前保持不变。打开开关总是从 off、low、medium 和 high 四行开始,不会恢复之前删掉的档位;要放弃修改请点**取消**。每行把一个档位选择框——可选值是 `llm-pi-ai` 配置 schema 允许作为 `reasoningEfforts` 键的档位,与协议选项一样从命名空间 schema 读取,目前为 `off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`,每个档位只能用一次——与发送给它的 **API 参数值**配成一对,参数值初始为档位名;输入时清空的字段会存为档位名,离开字段后显示出来。Off 只表示关闭思考,因此它的参数值输入框是禁用的;`settings.yaml` 中已为它写好的值会显示并保留。修改某行的档位时,仍是旧档位名的参数值会随之重置;自定义值只在两个思考档位之间保留,切到或切离 Off 都会重置。schema 未列出的已存键会保留自己的行,并可在该行中继续选择。行可以增删,并按行的顺序保存,模型选择器则按升级顺序列出档位。本节拒绝适配器会拒绝的配置:没有 Off 以外的档位(包括未写值的 `reasoningEfforts:`,此时开关显示为打开且没有任何行),或者 Off 以外的档位缺少参数值、任意档位的参数值为空字符串。被拒绝的值会把对应输入框标为无效,离开该输入框时会填入档位名。各行不保留自己的状态,因此删除、恢复默认或折叠之后被复用的模型行只显示它自己的模型。走已安装目录的路由和 DeepSeek 卡片沿用适配器的档位,不显示推理等级控件。
53
55
 
package/lib/client.js CHANGED
@@ -55,7 +55,7 @@ window.__ModuleLoader__.load({
55
55
  }
56
56
  //#endregion
57
57
  //#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-settings-models/src/client/ModelsSection.module.css.mjs
58
- const css$2 = ".PF5DZW_section{color:var(--qilin-alias-label-primary);flex-direction:column;gap:12px;display:flex}.PF5DZW_title{color:var(--qilin-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.PF5DZW_intro{color:var(--qilin-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.PF5DZW_notice{color:var(--qilin-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.PF5DZW_savedNotice{color:var(--qilin-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.PF5DZW_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.PF5DZW_rowCard{border:.5px solid var(--qilin-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.PF5DZW_rowHead{align-items:center;gap:10px;display:flex}.PF5DZW_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.PF5DZW_rowName{color:var(--qilin-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.PF5DZW_rowTag{border:.5px solid var(--qilin-alias-border-l3);color:var(--qilin-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.PF5DZW_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.PF5DZW_credentialDotConfigured{background:var(--qilin-alias-state-success-primary)}.PF5DZW_credentialDotMissing{background:var(--qilin-alias-state-error-primary)}.PF5DZW_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.PF5DZW_primaryButton,.PF5DZW_secondaryButton,.PF5DZW_addButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.PF5DZW_primaryButton{background:var(--qilin-alias-button-primary-fill);color:var(--qilin-alias-label-primary-foreground)}.PF5DZW_primaryButton:hover:not(:disabled){background:var(--qilin-alias-button-primary-hover)}.PF5DZW_secondaryButton,.PF5DZW_addButton{border:.5px solid var(--qilin-alias-border-l3);color:var(--qilin-alias-label-primary);background:0 0}.PF5DZW_secondaryButton:hover:not(:disabled),.PF5DZW_addButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover)}.PF5DZW_secondaryButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-solid)}.PF5DZW_dangerButton{box-sizing:border-box;height:36px;color:var(--qilin-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.PF5DZW_dangerButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-danger)}.PF5DZW_rowActions .PF5DZW_secondaryButton,.PF5DZW_rowActions .PF5DZW_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.PF5DZW_primaryButton:disabled,.PF5DZW_secondaryButton:disabled,.PF5DZW_dangerButton:disabled,.PF5DZW_addButton:disabled,.PF5DZW_linkButton:disabled,.PF5DZW_addModelButton:disabled{opacity:.4;cursor:default}.PF5DZW_primaryButton:focus-visible,.PF5DZW_secondaryButton:focus-visible,.PF5DZW_dangerButton:focus-visible,.PF5DZW_addButton:focus-visible,.PF5DZW_linkButton:focus-visible,.PF5DZW_addModelButton:focus-visible,.PF5DZW_iconButton:focus-visible,.PF5DZW_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--qilin-focus-ring-color,var(--qilin-alias-state-business-primary));outline:none}.PF5DZW_editor{background:var(--qilin-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.PF5DZW_editorHeader{align-items:baseline;gap:8px;display:flex}.PF5DZW_editorTitle{color:var(--qilin-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.PF5DZW_editorRoute{color:var(--qilin-alias-label-tertiary);font-size:12px;line-height:18px}.PF5DZW_field{flex-direction:column;gap:6px;display:flex}.PF5DZW_fieldLabel{color:var(--qilin-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.PF5DZW_helpButton{border:.5px solid var(--qilin-alias-border-l3);corner-shape:round;width:16px;height:16px;color:var(--qilin-alias-label-tertiary);font:inherit;text-align:center;cursor:help;background:0 0;border-radius:50%;padding:0;font-size:11px;line-height:15px}.PF5DZW_helpButton:hover,.PF5DZW_helpButton:focus-visible{color:var(--qilin-alias-label-primary);border-color:var(--qilin-alias-label-tertiary)}.PF5DZW_linkButton{box-sizing:border-box;height:28px;color:var(--qilin-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.PF5DZW_linkButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover);color:var(--qilin-alias-label-secondary)}.PF5DZW_advancedHint{color:var(--qilin-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.PF5DZW_editorActions{justify-content:flex-end;gap:8px;display:flex}.PF5DZW_addBlock{flex-direction:column;gap:12px;display:flex}.PF5DZW_addActions{flex-wrap:wrap;gap:10px;display:flex}.PF5DZW_addButton{border:1px dashed var(--qilin-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.PF5DZW_addCard,.PF5DZW_setupCard{background:var(--qilin-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.PF5DZW_addCard .PF5DZW_editor,.PF5DZW_setupCard .PF5DZW_editor{background:0 0;padding:0}.PF5DZW_customized{border-top:.5px solid var(--qilin-alias-border-l2);padding-top:10px}.PF5DZW_customizedSummary{cursor:pointer;width:fit-content;color:var(--qilin-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.PF5DZW_customizedSummary::-webkit-details-marker{display:none}.PF5DZW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.PF5DZW_customized[open]>.PF5DZW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.PF5DZW_customizedSummary:hover{color:var(--qilin-alias-label-primary)}.PF5DZW_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.PF5DZW_modelCatalog{border-top:.5px solid var(--qilin-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.PF5DZW_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.PF5DZW_modelCatalogTitle{color:var(--qilin-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.PF5DZW_modelCatalogMeta,.PF5DZW_modelEmpty{color:var(--qilin-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.PF5DZW_modelList{flex-direction:column;gap:8px;display:flex}.PF5DZW_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.PF5DZW_modelEntry{border:.5px solid var(--qilin-alias-border-l4);border-radius:10px;padding:6px}.PF5DZW_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.PF5DZW_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--qilin-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.PF5DZW_iconButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover);color:var(--qilin-alias-label-primary)}.PF5DZW_iconButton:disabled{cursor:default;opacity:.4}.PF5DZW_iconButtonDanger:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-danger);color:var(--qilin-alias-state-error-primary)}.PF5DZW_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.PF5DZW_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.PF5DZW_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.PF5DZW_modelInputTypes legend{margin-bottom:4px;padding:0}.PF5DZW_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.PF5DZW_modelFieldLabel{color:var(--qilin-alias-label-tertiary);font-size:12px;line-height:18px}.PF5DZW_modelReasoning{flex-direction:column;grid-column:1/-1;gap:6px;min-width:0;display:flex}.PF5DZW_modelReasoningLabel{align-items:center;gap:6px;display:inline-flex}.PF5DZW_modelReasoningHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.PF5DZW_modelReasoningRow{grid-template-columns:minmax(0,1fr) minmax(0,1fr) auto;align-items:center;gap:6px;display:grid}.PF5DZW_modelReasoningRow .PF5DZW_selectInput{max-width:none}.PF5DZW_modelReasoningValueInput{font-family:var(--ds-font-family-code);font-size:13px}.PF5DZW_modelEmpty{border:1px dashed var(--qilin-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.PF5DZW_addModelButton{box-sizing:border-box;border:.5px solid var(--qilin-alias-border-l3);height:28px;color:var(--qilin-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.PF5DZW_addModelButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover)}.PF5DZW_input{box-sizing:border-box;border:.5px solid var(--qilin-alias-border-l4);width:100%;height:32px;font:inherit;background:var(--qilin-alias-bg-layer-1);color:var(--qilin-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.PF5DZW_input{cursor:pointer;max-width:240px}.PF5DZW_input:focus{border-color:var(--qilin-alias-state-business-primary);outline:none}.PF5DZW_input::placeholder{color:var(--qilin-alias-label-dimmed)}.PF5DZW_input:disabled{opacity:.6;cursor:default}.PF5DZW_keyInput{position:relative}.PF5DZW_keyInputFilled{padding-right:40px}.PF5DZW_keyToggle{border-radius:var(--qilin-radius-md);width:32px;color:var(--qilin-alias-label-secondary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;display:inline-flex;position:absolute;inset:1px 1px 1px auto}.PF5DZW_keyToggle:hover:not(:disabled){color:var(--qilin-alias-label-primary)}.PF5DZW_keyToggle:focus-visible{outline:var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color,var(--qilin-alias-state-business-primary));outline-offset:-2px}.PF5DZW_keyToggle:disabled{opacity:.6;cursor:default}.PF5DZW_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.PF5DZW_error{color:var(--qilin-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.PF5DZW_deleteDialog{width:min(480px,100%)}.PF5DZW_deleteConfirm:not(:disabled){border-color:var(--qilin-alias-state-error-primary);color:var(--qilin-alias-state-error-primary)}.PF5DZW_deleteConfirm:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-danger)}.PF5DZW_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.PF5DZW_customizedSummary:before,.PF5DZW_switchThumb{transition:none}}.PF5DZW_fetchDialog{--qilin-scrollbar-thumb:var(--qilin-alias-scrollbar-bg-l2);--qilin-scrollbar-thumb-hover:var(--qilin-alias-scrollbar-hover-l2);max-width:520px}.PF5DZW_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.PF5DZW_candidateSearch{flex:240px;min-width:0}.PF5DZW_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.PF5DZW_candidate{border-radius:6px}.PF5DZW_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.PF5DZW_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.PF5DZW_candidateEmpty{color:var(--qilin-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
58
+ const css$2 = ".PF5DZW_section{color:var(--qilin-alias-label-primary);flex-direction:column;gap:12px;display:flex}.PF5DZW_title{color:var(--qilin-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.PF5DZW_intro{color:var(--qilin-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.PF5DZW_notice{color:var(--qilin-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.PF5DZW_savedNotice{color:var(--qilin-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.PF5DZW_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.PF5DZW_rowCard{border:1px solid var(--qilin-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.PF5DZW_rowHead{align-items:center;gap:10px;display:flex}.PF5DZW_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.PF5DZW_rowName{color:var(--qilin-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.PF5DZW_rowTag{border:1px solid var(--qilin-alias-border-l3);color:var(--qilin-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.PF5DZW_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.PF5DZW_credentialDotConfigured{background:var(--qilin-alias-state-success-primary)}.PF5DZW_credentialDotMissing{background:var(--qilin-alias-state-error-primary)}.PF5DZW_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.PF5DZW_primaryButton,.PF5DZW_secondaryButton,.PF5DZW_addButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.PF5DZW_primaryButton{background:var(--qilin-alias-button-primary-fill);color:var(--qilin-alias-label-primary-foreground)}.PF5DZW_primaryButton:hover:not(:disabled){background:var(--qilin-alias-button-primary-hover)}.PF5DZW_secondaryButton,.PF5DZW_addButton{border:1px solid var(--qilin-alias-border-l3);color:var(--qilin-alias-label-primary);background:0 0}.PF5DZW_secondaryButton:hover:not(:disabled),.PF5DZW_addButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover)}.PF5DZW_secondaryButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-solid)}.PF5DZW_dangerButton{box-sizing:border-box;height:36px;color:var(--qilin-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.PF5DZW_dangerButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-danger)}.PF5DZW_rowActions .PF5DZW_secondaryButton,.PF5DZW_rowActions .PF5DZW_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.PF5DZW_primaryButton:disabled,.PF5DZW_secondaryButton:disabled,.PF5DZW_dangerButton:disabled,.PF5DZW_addButton:disabled,.PF5DZW_linkButton:disabled,.PF5DZW_addModelButton:disabled{opacity:.4;cursor:default}.PF5DZW_primaryButton:focus-visible,.PF5DZW_secondaryButton:focus-visible,.PF5DZW_dangerButton:focus-visible,.PF5DZW_addButton:focus-visible,.PF5DZW_linkButton:focus-visible,.PF5DZW_addModelButton:focus-visible,.PF5DZW_iconButton:focus-visible,.PF5DZW_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--qilin-focus-ring-color,var(--qilin-alias-state-business-primary));outline:none}.PF5DZW_editor{background:var(--qilin-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.PF5DZW_editorHeader{align-items:baseline;gap:8px;display:flex}.PF5DZW_editorTitle{color:var(--qilin-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.PF5DZW_editorRoute{color:var(--qilin-alias-label-tertiary);font-size:12px;line-height:18px}.PF5DZW_field{flex-direction:column;gap:6px;display:flex}.PF5DZW_fieldLabel{color:var(--qilin-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.PF5DZW_helpButton{border:1px solid var(--qilin-alias-border-l3);corner-shape:round;width:16px;height:16px;color:var(--qilin-alias-label-tertiary);font:inherit;text-align:center;cursor:help;background:0 0;border-radius:50%;padding:0;font-size:11px;line-height:15px}.PF5DZW_helpButton:hover,.PF5DZW_helpButton:focus-visible{color:var(--qilin-alias-label-primary);border-color:var(--qilin-alias-label-tertiary)}.PF5DZW_linkButton{box-sizing:border-box;height:28px;color:var(--qilin-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.PF5DZW_linkButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover);color:var(--qilin-alias-label-secondary)}.PF5DZW_advancedHint{color:var(--qilin-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.PF5DZW_editorActions{justify-content:flex-end;gap:8px;display:flex}.PF5DZW_addBlock{flex-direction:column;gap:12px;display:flex}.PF5DZW_addActions{flex-wrap:wrap;gap:10px;display:flex}.PF5DZW_addButton{border:1px dashed var(--qilin-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.PF5DZW_addCard,.PF5DZW_setupCard{background:var(--qilin-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.PF5DZW_addCard .PF5DZW_editor,.PF5DZW_setupCard .PF5DZW_editor{background:0 0;padding:0}.PF5DZW_customized{border-top:1px solid var(--qilin-alias-border-l2);padding-top:10px}.PF5DZW_customizedSummary{cursor:pointer;width:fit-content;color:var(--qilin-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.PF5DZW_customizedSummary::-webkit-details-marker{display:none}.PF5DZW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.PF5DZW_customized[open]>.PF5DZW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.PF5DZW_customizedSummary:hover{color:var(--qilin-alias-label-primary)}.PF5DZW_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.PF5DZW_modelCatalog{border-top:1px solid var(--qilin-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex;position:relative}.PF5DZW_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.PF5DZW_modelCatalogTitle{color:var(--qilin-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.PF5DZW_modelCatalogMeta,.PF5DZW_modelEmpty{color:var(--qilin-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.PF5DZW_modelList{flex-direction:column;gap:8px;display:flex}.PF5DZW_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.PF5DZW_modelEntry{border:1px solid var(--qilin-alias-border-l4);border-radius:10px;padding:6px;position:relative;container:PF5DZW_model-row/inline-size}.PF5DZW_modelRow{grid-template-columns:auto minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}@container PF5DZW_model-row (width<=440px){.PF5DZW_modelRow{grid-template-columns:auto minmax(0,1fr) auto auto}.PF5DZW_modelRow>input:first-of-type{grid-area:1/2}.PF5DZW_modelRow>input:nth-of-type(2){grid-area:2/2/auto/-1}}.PF5DZW_modelDragHandle:not(:disabled){cursor:grab}.PF5DZW_modelEntry[data-dragging]{opacity:.5}.PF5DZW_modelDragPreview{background:var(--qilin-alias-bg-module-platform)}.PF5DZW_modelEntry:before,.PF5DZW_modelEntry:after{content:\"\";z-index:1;height:4px;position:absolute;left:0;right:0}.PF5DZW_modelEntry:before{top:-4px}.PF5DZW_modelEntry:after{bottom:-4px}.PF5DZW_modelEntry[data-drop-target=before]:before,.PF5DZW_modelEntry[data-drop-target=after]:after{background:linear-gradient(55deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--qilin-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;height:12px}.PF5DZW_modelEntry[data-drop-target=before]:before{top:-10px}.PF5DZW_modelEntry[data-drop-target=after]:after{bottom:-10px}.PF5DZW_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--qilin-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.PF5DZW_iconButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover);color:var(--qilin-alias-label-primary)}.PF5DZW_iconButton:disabled{cursor:default;opacity:.4}.PF5DZW_iconButtonDanger:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-danger);color:var(--qilin-alias-state-error-primary)}.PF5DZW_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.PF5DZW_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.PF5DZW_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.PF5DZW_modelInputTypes legend{margin-bottom:4px;padding:0}.PF5DZW_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.PF5DZW_modelFieldLabel{color:var(--qilin-alias-label-tertiary);font-size:12px;line-height:18px}.PF5DZW_modelReasoning{flex-direction:column;grid-column:1/-1;gap:6px;min-width:0;display:flex}.PF5DZW_modelReasoningLabel{align-items:center;gap:6px;display:inline-flex}.PF5DZW_modelReasoningHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.PF5DZW_modelReasoningRow{grid-template-columns:minmax(0,1fr) minmax(0,1fr) auto;align-items:center;gap:6px;display:grid}.PF5DZW_modelReasoningRow .PF5DZW_selectInput{max-width:none}.PF5DZW_modelReasoningValueInput{font-family:var(--ds-font-family-code);font-size:13px}.PF5DZW_modelEmpty{border:1px dashed var(--qilin-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.PF5DZW_addModelButton{box-sizing:border-box;border:1px solid var(--qilin-alias-border-l3);height:28px;color:var(--qilin-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.PF5DZW_addModelButton:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover)}.PF5DZW_input{box-sizing:border-box;border:1px solid var(--qilin-alias-border-l4);width:100%;height:32px;font:inherit;background:var(--qilin-alias-bg-layer-1);color:var(--qilin-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.PF5DZW_input{cursor:pointer;max-width:240px}.PF5DZW_input:focus{border-color:var(--qilin-alias-state-business-primary);outline:none}.PF5DZW_input::placeholder{color:var(--qilin-alias-label-dimmed)}.PF5DZW_input:disabled{opacity:.6;cursor:default}.PF5DZW_keyInput{position:relative}.PF5DZW_keyInputFilled{padding-right:40px}.PF5DZW_keyToggle{border-radius:var(--qilin-radius-md);width:32px;color:var(--qilin-alias-label-secondary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;display:inline-flex;position:absolute;inset:1px 1px 1px auto}.PF5DZW_keyToggle:hover:not(:disabled){color:var(--qilin-alias-label-primary)}.PF5DZW_keyToggle:focus-visible{outline:var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color,var(--qilin-alias-state-business-primary));outline-offset:-2px}.PF5DZW_keyToggle:disabled{opacity:.6;cursor:default}.PF5DZW_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.PF5DZW_error{color:var(--qilin-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.PF5DZW_deleteDialog{width:min(480px,100%)}.PF5DZW_deleteConfirm:not(:disabled){border-color:var(--qilin-alias-state-error-primary);color:var(--qilin-alias-state-error-primary)}.PF5DZW_deleteConfirm:hover:not(:disabled){background:var(--qilin-alias-interactive-bg-hover-danger)}.PF5DZW_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.PF5DZW_customizedSummary:before,.PF5DZW_switchThumb{transition:none}}.PF5DZW_fetchDialog{--qilin-scrollbar-thumb:var(--qilin-alias-scrollbar-bg-l2);--qilin-scrollbar-thumb-hover:var(--qilin-alias-scrollbar-hover-l2);max-width:520px}.PF5DZW_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.PF5DZW_candidateSearch{flex:240px;min-width:0}.PF5DZW_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.PF5DZW_candidate{border-radius:6px}.PF5DZW_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.PF5DZW_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.PF5DZW_candidateEmpty{color:var(--qilin-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
59
59
  const tagId$2 = "@qilin-agent/client-ui-settings-models/ModelsSection.module.css";
60
60
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
61
61
  const tag = document.createElement("style");
@@ -106,11 +106,14 @@ window.__ModuleLoader__.load({
106
106
  "keyInputFilled": "PF5DZW_keyInputFilled",
107
107
  "keyToggle": "PF5DZW_keyToggle",
108
108
  "linkButton": "PF5DZW_linkButton",
109
+ "model-row": "PF5DZW_model-row",
109
110
  "modelAdvanced": "PF5DZW_modelAdvanced",
110
111
  "modelCatalog": "PF5DZW_modelCatalog",
111
112
  "modelCatalogHeading": "PF5DZW_modelCatalogHeading",
112
113
  "modelCatalogMeta": "PF5DZW_modelCatalogMeta",
113
114
  "modelCatalogTitle": "PF5DZW_modelCatalogTitle",
115
+ "modelDragHandle": "PF5DZW_modelDragHandle",
116
+ "modelDragPreview": "PF5DZW_modelDragPreview",
114
117
  "modelEmpty": "PF5DZW_modelEmpty",
115
118
  "modelEntry": "PF5DZW_modelEntry",
116
119
  "modelField": "PF5DZW_modelField",
@@ -475,6 +478,10 @@ window.__ModuleLoader__.load({
475
478
  }
476
479
  //#endregion
477
480
  //#region lib/types/client/ModelRow.js
481
+ function rowHalf(event) {
482
+ const rect = event.currentTarget.getBoundingClientRect();
483
+ return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
484
+ }
478
485
  /**
479
486
  * Render consistent model identity, capacity, and input-type controls.
480
487
  * @param props - drafted fields and their owning editor's actions.
@@ -482,11 +489,58 @@ window.__ModuleLoader__.load({
482
489
  */
483
490
  function ModelRow(props) {
484
491
  const { model, position, t, disabled } = props;
492
+ const preview = (0, _qilin_agent_client_ui_primitives.useRowDragPreview)(ModelsSection_module_css_default["modelDragPreview"]);
485
493
  return (0, react_jsx_runtime.jsxs)("div", {
486
494
  className: ModelsSection_module_css_default["modelEntry"],
495
+ "data-row-key": props.rowKey,
496
+ "data-dragging": props.order.dragging || void 0,
497
+ "data-drop-target": props.order.dragging ? void 0 : props.order.target,
498
+ onDragOver: (event) => {
499
+ if (!props.order.canDrop) return;
500
+ event.preventDefault();
501
+ event.dataTransfer.dropEffect = "move";
502
+ props.order.onOver(rowHalf(event));
503
+ },
504
+ onDragLeave: (event) => {
505
+ if (event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget)) return;
506
+ props.order.onLeave();
507
+ },
508
+ onDrop: (event) => {
509
+ if (!props.order.canDrop) return;
510
+ event.preventDefault();
511
+ props.order.onDrop(rowHalf(event));
512
+ },
487
513
  children: [(0, react_jsx_runtime.jsxs)("div", {
488
514
  className: ModelsSection_module_css_default["modelRow"],
489
515
  children: [
516
+ (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Tooltip, {
517
+ label: t("reorderModelHelp"),
518
+ children: (0, react_jsx_runtime.jsx)("button", {
519
+ type: "button",
520
+ className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["modelDragHandle"]}`,
521
+ "aria-label": `${t("reorderModel")} ${String(position)}`,
522
+ "aria-keyshortcuts": "ArrowUp ArrowDown",
523
+ "aria-description": t("reorderModelHelp"),
524
+ disabled,
525
+ draggable: !disabled,
526
+ onDragStart: (event) => {
527
+ event.dataTransfer.effectAllowed = "move";
528
+ event.dataTransfer.setData("application/x-qilin-model-order", String(position));
529
+ props.order.onStart();
530
+ preview.start(event, event.currentTarget.closest("[data-row-key]"));
531
+ },
532
+ onDragEnd: () => {
533
+ if (preview.clear()) props.order.onEnd();
534
+ },
535
+ onKeyDown: (event) => {
536
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
537
+ event.preventDefault();
538
+ event.stopPropagation();
539
+ props.order.onMove(event.key === "ArrowUp" ? -1 : 1);
540
+ },
541
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconDragHandleOutlineRegular, { size: 14 })
542
+ })
543
+ }),
490
544
  ["id", "name"].map((field) => (0, react_jsx_runtime.jsx)("input", {
491
545
  className: ModelsSection_module_css_default["input"],
492
546
  type: "text",
@@ -563,17 +617,108 @@ window.__ModuleLoader__.load({
563
617
  });
564
618
  }
565
619
  //#endregion
566
- //#region lib/types/client/DeepSeekModelsEditor.js
620
+ //#region lib/types/client/useModelRows.js
621
+ /** Local model-row identities, disclosures, capacity text, and draft ordering. */
622
+ /** One editor-local identity; editing a model ID never replaces its controls. */
623
+ function createRow(key) {
624
+ return {
625
+ key,
626
+ expanded: false,
627
+ editing: /* @__PURE__ */ new Map()
628
+ };
629
+ }
567
630
  /**
568
- * Curated editor for the direct DeepSeek adapter's advisory model catalog.
569
- * The settings layer replaces `models` as one array, so the parent supplies
570
- * the effective inherited rows until the first edit materializes a user
571
- * override; reset removes that override instead of copying defaults into it.
631
+ * Keep row state with its model through draft moves and removals.
632
+ * @param models - current draft array, owned by the provider card.
633
+ * @param onChange - replaces only that card's draft array.
634
+ * @param disabled - prevents reordering while the card cannot be edited.
635
+ * @returns row state and local editing and ordering actions.
572
636
  */
573
- /** Row index encoded in an editing-buffer key. */
574
- function rowOf(key) {
575
- return Number(key.slice(0, key.indexOf(":")));
637
+ function useModelRows(models, onChange, disabled) {
638
+ const nextKey = (0, react.useRef)(0);
639
+ const [stored, setRows] = (0, react.useState)(() => models.map(() => createRow(nextKey.current++)));
640
+ const [dragged, setDragged] = (0, react.useState)();
641
+ const [target, setTarget] = (0, react.useState)();
642
+ const entries = models.map((model, index) => ({
643
+ model,
644
+ state: stored[index] ?? createRow(nextKey.current++)
645
+ }));
646
+ const rows = entries.map((entry) => entry.state);
647
+ if (stored.length !== models.length) setRows(rows);
648
+ const endDrag = () => {
649
+ setDragged(void 0);
650
+ setTarget(void 0);
651
+ };
652
+ const move = (from, to) => {
653
+ if (disabled || from === to || from < 0 || to < 0 || to >= models.length) return;
654
+ const order = [...entries];
655
+ const moved = order.splice(from, 1);
656
+ order.splice(to, 0, ...moved);
657
+ setRows(order.map((entry) => entry.state));
658
+ onChange(order.map((entry) => entry.model));
659
+ };
660
+ return {
661
+ rows: entries.map(({ model, state }, index) => ({
662
+ ...state,
663
+ model,
664
+ order: {
665
+ dragging: dragged === state.key,
666
+ target: target?.key === state.key ? target.half : void 0,
667
+ canDrop: !disabled && dragged !== void 0,
668
+ onStart: () => {
669
+ setDragged(state.key);
670
+ },
671
+ onEnd: endDrag,
672
+ onOver: (half) => {
673
+ setTarget((current) => current?.key === state.key && current.half === half ? current : {
674
+ key: state.key,
675
+ half
676
+ });
677
+ },
678
+ onLeave: () => {
679
+ setTarget((current) => current?.key === state.key ? void 0 : current);
680
+ },
681
+ onDrop: (half) => {
682
+ const from = rows.findIndex((row) => row.key === dragged);
683
+ const insertion = index + (half === "after" ? 1 : 0);
684
+ move(from, insertion > from ? insertion - 1 : insertion);
685
+ endDrag();
686
+ },
687
+ onMove: (offset) => {
688
+ move(index, index + offset);
689
+ }
690
+ }
691
+ })),
692
+ toggle: (index) => {
693
+ setRows(rows.map((row, at) => at === index ? {
694
+ ...row,
695
+ expanded: !row.expanded
696
+ } : row));
697
+ },
698
+ edit: (index, field, text) => {
699
+ setRows(rows.map((row, at) => {
700
+ if (at !== index) return row;
701
+ const editing = new Map(row.editing);
702
+ if (text === void 0) editing.delete(field);
703
+ else editing.set(field, text);
704
+ return {
705
+ ...row,
706
+ editing
707
+ };
708
+ }));
709
+ },
710
+ forget: (removed) => {
711
+ setRows(rows.filter((_row, index) => !removed.has(index)));
712
+ endDrag();
713
+ },
714
+ reset: () => {
715
+ setRows(models.map(() => createRow(nextKey.current++)));
716
+ endDrag();
717
+ }
718
+ };
576
719
  }
720
+ //#endregion
721
+ //#region lib/types/client/DeepSeekModelsEditor.js
577
722
  /** Accepted capacity spellings: a decimal count with an optional K/M suffix. */
578
723
  const CAPACITY_PATTERN = /^(\d+(?:\.\d+)?)([km])?$/i;
579
724
  /** Decimal suffix scales — `1M` is 1000K, matching how model capacities are quoted. */
@@ -662,8 +807,7 @@ window.__ModuleLoader__.load({
662
807
  * @returns the catalog editor.
663
808
  */
664
809
  function DeepSeekModelsEditor(props) {
665
- const [editing, setEditing] = (0, react.useState)(() => /* @__PURE__ */ new Map());
666
- const [expanded, setExpanded] = (0, react.useState)(() => /* @__PURE__ */ new Set());
810
+ const rowState = useModelRows(props.models, props.onChange, props.disabled);
667
811
  const update = (index, key, value) => {
668
812
  const next = props.models.map((model, at) => {
669
813
  const copy = { ...model };
@@ -675,61 +819,32 @@ window.__ModuleLoader__.load({
675
819
  props.onChange(next);
676
820
  };
677
821
  const remove = (index) => {
678
- setEditing((current) => {
679
- const next = /* @__PURE__ */ new Map();
680
- for (const [key, text] of current) {
681
- const at = rowOf(key);
682
- if (at === index) continue;
683
- next.set(at > index ? key.replace(/^\d+/, String(at - 1)) : key, text);
684
- }
685
- return next;
686
- });
687
- setExpanded((current) => {
688
- const next = /* @__PURE__ */ new Set();
689
- for (const at of current) {
690
- if (at === index) continue;
691
- next.add(at > index ? at - 1 : at);
692
- }
693
- return next;
694
- });
822
+ rowState.forget(new Set([index]));
695
823
  props.onChange(props.models.filter((_model, at) => at !== index).map((model) => ({ ...model })));
696
824
  };
697
825
  const reset = () => {
698
- setEditing(/* @__PURE__ */ new Map());
699
- setExpanded(/* @__PURE__ */ new Set());
826
+ rowState.reset();
700
827
  props.onReset();
701
828
  };
702
- const toggle = (index) => {
703
- setExpanded((current) => {
704
- const next = new Set(current);
705
- if (!next.delete(index)) next.add(index);
706
- return next;
707
- });
708
- };
709
829
  /** The field's text: its live keystrokes, else the stored count spelled short. */
710
830
  const capacityText = (model, index, field) => {
711
- const typed = editing.get(`${String(index)}:${field}`);
831
+ const typed = rowState.rows[index]?.editing.get(field);
712
832
  if (typed !== void 0) return typed;
713
833
  const value = model[field];
714
834
  return typeof value === "number" ? formatCapacity(value) : "";
715
835
  };
716
836
  const settleCapacity = (index, field) => {
717
- const key = `${String(index)}:${field}`;
718
- const typed = editing.get(key);
837
+ const typed = rowState.rows[index]?.editing.get(field);
719
838
  if (typed === void 0) return;
720
839
  const parsed = parseCapacity(typed);
721
840
  if (parsed !== void 0 && Number.isNaN(parsed)) return;
722
- setEditing((current) => {
723
- const next = new Map(current);
724
- next.delete(key);
725
- return next;
726
- });
841
+ rowState.edit(index, field, void 0);
727
842
  };
728
843
  const capacityInput = (model, index, field, fallback) => ({
729
844
  value: capacityText(model, index, field),
730
845
  placeholder: fallback === void 0 ? props.t(field === "contextWindow" ? "contextWindowPlaceholder" : "maxTokensPlaceholder") : formatCapacity(fallback),
731
846
  onChange: (text) => {
732
- setEditing((current) => new Map(current).set(`${String(index)}:${field}`, text));
847
+ rowState.edit(index, field, text);
733
848
  update(index, field, parseCapacity(text));
734
849
  },
735
850
  onBlur: () => {
@@ -759,16 +874,21 @@ window.__ModuleLoader__.load({
759
874
  children: props.t("resetModels")
760
875
  }) : null]
761
876
  }),
762
- props.models.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
763
- className: ModelsSection_module_css_default["modelEmpty"],
764
- children: props.t("modelsEmpty")
765
- }) : (0, react_jsx_runtime.jsx)("div", {
877
+ (0, react_jsx_runtime.jsxs)(_qilin_agent_client_ui_primitives.AnimatedRows, {
766
878
  className: ModelsSection_module_css_default["modelList"],
767
- children: props.models.map((model, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
879
+ rowKeys: rowState.rows.map((row) => String(row.key)),
880
+ ready: true,
881
+ resetKey: "",
882
+ children: [props.models.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
883
+ className: ModelsSection_module_css_default["modelEmpty"],
884
+ children: props.t("modelsEmpty")
885
+ }) : null, rowState.rows.map(({ model, key, expanded, order }, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
886
+ rowKey: key,
887
+ order,
768
888
  model,
769
889
  position: index + 1,
770
890
  inputField: "inputModalities",
771
- expanded: expanded.has(index),
891
+ expanded,
772
892
  disabled: props.disabled,
773
893
  t: props.t,
774
894
  contextWindow: capacityInput(model, index, "contextWindow", props.defaultContextWindow),
@@ -784,12 +904,12 @@ window.__ModuleLoader__.load({
784
904
  props.onChange(props.models.map((row, at) => at === index ? next : row));
785
905
  },
786
906
  onToggle: () => {
787
- toggle(index);
907
+ rowState.toggle(index);
788
908
  },
789
909
  onRemove: () => {
790
910
  remove(index);
791
911
  }
792
- }, index))
912
+ }, key))]
793
913
  }),
794
914
  (0, react_jsx_runtime.jsxs)("button", {
795
915
  type: "button",
@@ -903,33 +1023,13 @@ window.__ModuleLoader__.load({
903
1023
  const [candidates, setCandidates] = (0, react.useState)(void 0);
904
1024
  const [picked, setPicked] = (0, react.useState)(/* @__PURE__ */ new Set());
905
1025
  const [candidateQuery, setCandidateQuery] = (0, react.useState)("");
906
- const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
907
- const [editing, setEditing] = (0, react.useState)(/* @__PURE__ */ new Map());
908
- /** Buffer key for one capacity field; the row half moves when rows do. */
909
- const bufferKey = (index, field) => `${String(index)}:${field}`;
1026
+ const rowState = useModelRows(models, onChange, disabled);
910
1027
  const editCapacity = (index, field, text) => {
911
- setEditing((current) => new Map(current).set(bufferKey(index, field), text));
1028
+ rowState.edit(index, field, text);
912
1029
  patch(index, { [field]: parseCapacity(text) });
913
1030
  };
914
1031
  /** What a capacity field shows: the buffer while typing, else the stored count. */
915
- const capacityText = (model, index, field) => editing.get(bufferKey(index, field)) ?? capacitySpelling(numberOf(model, field));
916
- /** Drop one row's entries and shift the rows after it down, in one pass. */
917
- const reindexOnRemove = (current, index) => {
918
- const next = /* @__PURE__ */ new Map();
919
- for (const [key, value] of current) {
920
- const at = Number(key.slice(0, key.indexOf(":")));
921
- if (at === index) continue;
922
- next.set(at > index ? key.replace(/^\d+/, String(at - 1)) : key, value);
923
- }
924
- return next;
925
- };
926
- const toggleExpanded = (index) => {
927
- setExpanded((current) => {
928
- const next = new Set(current);
929
- if (!next.delete(index)) next.add(index);
930
- return next;
931
- });
932
- };
1032
+ const capacityText = (model, index, field) => rowState.rows[index]?.editing.get(field) ?? capacitySpelling(numberOf(model, field));
933
1033
  const patch = (index, next) => {
934
1034
  onChange(models.map((model, at) => {
935
1035
  if (at !== index) return model;
@@ -1028,7 +1128,10 @@ window.__ModuleLoader__.load({
1028
1128
  type: "button",
1029
1129
  className: ModelsSection_module_css_default["linkButton"],
1030
1130
  disabled,
1031
- onClick: props.onReset,
1131
+ onClick: () => {
1132
+ rowState.reset();
1133
+ props.onReset?.();
1134
+ },
1032
1135
  children: t("resetModels")
1033
1136
  }) : null,
1034
1137
  (0, react_jsx_runtime.jsx)("button", {
@@ -1047,16 +1150,21 @@ window.__ModuleLoader__.load({
1047
1150
  className: ModelsSection_module_css_default["modelEmpty"],
1048
1151
  children: t("modelsEmpty")
1049
1152
  }) : null,
1050
- (0, react_jsx_runtime.jsx)("div", {
1153
+ (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.AnimatedRows, {
1051
1154
  className: ModelsSection_module_css_default["modelList"],
1052
- children: models.map((model, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
1155
+ rowKeys: rowState.rows.map((row) => String(row.key)),
1156
+ ready: true,
1157
+ resetKey: "",
1158
+ children: rowState.rows.map(({ model, key, expanded, order }, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
1159
+ rowKey: key,
1160
+ order,
1053
1161
  model,
1054
1162
  position: index + 1,
1055
1163
  inputField: "input",
1056
1164
  inputFallback: inputDefaults.get(textOf(model, "id")) ?? props.defaultInput,
1057
1165
  inputLoading: catalogProvider !== void 0 && catalog === void 0,
1058
1166
  reasoningLevels: props.reasoningLevels,
1059
- expanded: expanded.has(index),
1167
+ expanded,
1060
1168
  disabled,
1061
1169
  t,
1062
1170
  contextWindow: {
@@ -1080,19 +1188,13 @@ window.__ModuleLoader__.load({
1080
1188
  onChange(models.map((row, at) => at === index ? next : row));
1081
1189
  },
1082
1190
  onToggle: () => {
1083
- toggleExpanded(index);
1191
+ rowState.toggle(index);
1084
1192
  },
1085
1193
  onRemove: () => {
1086
1194
  onChange(models.filter((_model, at) => at !== index));
1087
- setExpanded((current) => {
1088
- const next = /* @__PURE__ */ new Set();
1089
- for (const at of current) if (at < index) next.add(at);
1090
- else if (at > index) next.add(at - 1);
1091
- return next;
1092
- });
1093
- setEditing((current) => reindexOnRemove(current, index));
1195
+ rowState.forget(new Set([index]));
1094
1196
  }
1095
- }, index))
1197
+ }, key))
1096
1198
  }),
1097
1199
  (0, react_jsx_runtime.jsxs)("button", {
1098
1200
  type: "button",
@@ -2997,6 +3099,8 @@ window.__ModuleLoader__.load({
2997
3099
  modelReasoningOffValuePlaceholder: "Turns reasoning off",
2998
3100
  addModel: "Add model",
2999
3101
  removeModel: "Delete model",
3102
+ reorderModel: "Reorder model",
3103
+ reorderModelHelp: "Drag the handle, or focus it and press the up or down arrow key.",
3000
3104
  modelsEmpty: "No models will be shown in the selector. Unlisted IDs can still be sent directly.",
3001
3105
  keyBlank: "Enter the API key, or leave the field empty to keep the stored one.",
3002
3106
  keyBlankNew: "Enter the API key, or leave the field empty if this provider authenticates another way.",
@@ -3123,6 +3227,8 @@ window.__ModuleLoader__.load({
3123
3227
  modelReasoningOffValuePlaceholder: "关闭思考",
3124
3228
  addModel: "添加模型",
3125
3229
  removeModel: "删除模型",
3230
+ reorderModel: "调整模型顺序",
3231
+ reorderModelHelp: "拖动手柄,或聚焦手柄后按上下方向键。",
3126
3232
  modelsEmpty: "模型选择器中将不显示任何模型;目录外 ID 仍可直接发送。",
3127
3233
  keyBlank: "请输入 API 密钥;留空则保持已存储的密钥。",
3128
3234
  keyBlankNew: "请输入 API 密钥;若该提供方以其他方式鉴权,可以留空。",
@@ -2,6 +2,7 @@
2
2
  import type { ReactNode } from 'react';
3
3
  import type { DeepSeekModelDraft } from './DeepSeekModelsEditor.tsx';
4
4
  import type { ModelsKey } from './locales.ts';
5
+ import type { ModelRowOrder } from './useModelRows.ts';
5
6
  /** A capacity's editable text and adapter-specific inherited hint. */
6
7
  interface CapacityInput {
7
8
  value: string;
@@ -12,7 +13,9 @@ interface CapacityInput {
12
13
  /** Adapter-owned data and actions for one model row. */
13
14
  interface ModelRowProps {
14
15
  model: DeepSeekModelDraft;
16
+ rowKey: number;
15
17
  position: number;
18
+ order: ModelRowOrder;
16
19
  inputField: 'inputModalities' | 'input';
17
20
  inputFallback?: readonly string[] | undefined;
18
21
  inputLoading?: boolean;
@@ -74,6 +74,8 @@ export declare const en: {
74
74
  modelReasoningOffValuePlaceholder: string;
75
75
  addModel: string;
76
76
  removeModel: string;
77
+ reorderModel: string;
78
+ reorderModelHelp: string;
77
79
  modelsEmpty: string;
78
80
  keyBlank: string;
79
81
  keyBlankNew: string;
@@ -0,0 +1,41 @@
1
+ /** Local model-row identities, disclosures, capacity text, and draft ordering. */
2
+ import type { DeepSeekModelDraft } from './DeepSeekModelsEditor.tsx';
3
+ /** Text fields whose unfinished spelling belongs to one draft row. */
4
+ export type CapacityField = 'contextWindow' | 'maxTokens';
5
+ interface RowState {
6
+ key: number;
7
+ expanded: boolean;
8
+ editing: Map<CapacityField, string>;
9
+ }
10
+ /** Drag state and actions for one model row within its editor. */
11
+ export interface ModelRowOrder {
12
+ dragging: boolean;
13
+ target: 'before' | 'after' | undefined;
14
+ canDrop: boolean;
15
+ onStart: () => void;
16
+ onEnd: () => void;
17
+ onOver: (half: 'before' | 'after') => void;
18
+ onLeave: () => void;
19
+ onDrop: (half: 'before' | 'after') => void;
20
+ onMove: (offset: -1 | 1) => void;
21
+ }
22
+ interface ModelRows {
23
+ rows: (RowState & {
24
+ model: DeepSeekModelDraft;
25
+ order: ModelRowOrder;
26
+ })[];
27
+ toggle: (index: number) => void;
28
+ edit: (index: number, field: CapacityField, text: string | undefined) => void;
29
+ forget: (removed: ReadonlySet<number>) => void;
30
+ reset: () => void;
31
+ }
32
+ /**
33
+ * Keep row state with its model through draft moves and removals.
34
+ * @param models - current draft array, owned by the provider card.
35
+ * @param onChange - replaces only that card's draft array.
36
+ * @param disabled - prevents reordering while the card cannot be edited.
37
+ * @returns row state and local editing and ordering actions.
38
+ */
39
+ export declare function useModelRows(models: readonly DeepSeekModelDraft[], onChange: (models: DeepSeekModelDraft[]) => void, disabled: boolean): ModelRows;
40
+ export {};
41
+ //# sourceMappingURL=useModelRows.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qilin-agent/client-ui-settings-models",
3
3
  "description": "Models settings and shared product-onboarding dialogs over existing settings and credential joins",
4
- "version": "3.1.7",
4
+ "version": "3.1.8",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -40,27 +40,27 @@
40
40
  },
41
41
  "license": "MIT",
42
42
  "peerDependencies": {
43
- "@qilin-agent/kylin": "~4.0.5"
43
+ "@qilin-agent/kylin": "~4.0.6-alpha.2"
44
44
  },
45
45
  "devDependencies": {
46
+ "@qilin-agent/remote-mock": "3.1.8",
47
+ "@qilin-agent/api-remotes": "3.1.8",
48
+ "@qilin-agent/client-locale": "3.1.8",
49
+ "@qilin-agent/client-store": "3.1.8",
50
+ "@qilin-agent/client-test-runtime": "3.1.8",
51
+ "@qilin-agent/client-ui-primitives": "3.1.8",
52
+ "@qilin-agent/client-ui-settings": "3.1.8",
53
+ "@qilin-agent/client-ui-slots": "3.1.8",
54
+ "@qilin-agent/host-webserver": "3.1.8",
46
55
  "@types/react": "~18.3.1",
47
56
  "@testing-library/react": "^16.3.2",
57
+ "@qilin-agent/kylin": "~4.0.6-alpha.2",
48
58
  "react": "^18.2.0",
49
- "@qilin-agent/remote-mock": "3.1.7",
50
- "@qilin-agent/client-locale": "3.1.7",
51
- "@qilin-agent/api-remotes": "3.1.7",
52
- "@qilin-agent/client-store": "3.1.7",
53
- "@qilin-agent/client-test-runtime": "3.1.7",
54
- "@qilin-agent/client-ui-primitives": "3.1.7",
55
- "@qilin-agent/client-ui-settings": "3.1.7",
56
- "@qilin-agent/client-ui-slots": "3.1.7",
57
- "@qilin-agent/kylin": "~4.0.5",
58
- "@qilin-agent/host-webserver": "3.1.7",
59
- "@qilin-agent/client-ui-renderer": "3.1.7",
60
- "@qilin-agent/util-values": "3.1.7"
59
+ "@qilin-agent/client-ui-renderer": "3.1.8",
60
+ "@qilin-agent/util-values": "3.1.8"
61
61
  },
62
62
  "dependencies": {
63
- "@qilin-agent/schemastery": "~3.18.5"
63
+ "@qilin-agent/schemastery": "~3.18.6-alpha.2"
64
64
  },
65
65
  "files": [
66
66
  "lib/index.js",