@deepseek-ai/dsh-client-ui-settings-models 0.1.6-alpha.1 → 0.1.7-alpha.1

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
@@ -55,7 +55,7 @@ window.__ModuleLoader__.load({
55
55
  }
56
56
  //#endregion
57
57
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/ModelsSection.module.css.mjs
58
- const css$3 = ".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_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}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;height:36px;color:var(--dsw-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}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-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}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{flex-wrap:wrap;gap:10px;display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addCard,.zGbnIq_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-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}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_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)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelCatalogMeta,.zGbnIq_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_modelList{flex-direction:column;gap:8px;display:flex}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.zGbnIq_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.zGbnIq_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:28px;color:var(--dsw-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}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_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}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before,.zGbnIq_switchThumb{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.zGbnIq_candidateSearch{flex:240px;min-width:0}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:6px}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
58
+ const css$3 = ".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_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}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;height:36px;color:var(--dsw-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}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-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}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addModes{flex-direction:column;align-items:flex-start;gap:8px;display:flex}.zGbnIq_addPanel{flex-direction:column;gap:14px;display:flex}.zGbnIq_addPanel[hidden]{display:none}.zGbnIq_addCard,.zGbnIq_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-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}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_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)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelCatalogMeta,.zGbnIq_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_modelList{flex-direction:column;gap:8px;display:flex}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.zGbnIq_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.zGbnIq_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.zGbnIq_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.zGbnIq_modelInputTypes legend{margin-bottom:4px;padding:0}.zGbnIq_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:28px;color:var(--dsw-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}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_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}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before,.zGbnIq_switchThumb{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.zGbnIq_candidateSearch{flex:240px;min-width:0}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:6px}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
59
59
  const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-models/ModelsSection.module.css";
60
60
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
61
61
  const tag = document.createElement("style");
@@ -70,6 +70,8 @@ window.__ModuleLoader__.load({
70
70
  "addButton": "zGbnIq_addButton",
71
71
  "addCard": "zGbnIq_addCard",
72
72
  "addModelButton": "zGbnIq_addModelButton",
73
+ "addModes": "zGbnIq_addModes",
74
+ "addPanel": "zGbnIq_addPanel",
73
75
  "advancedHint": "zGbnIq_advancedHint",
74
76
  "candidate": "zGbnIq_candidate",
75
77
  "candidateEmpty": "zGbnIq_candidateEmpty",
@@ -111,6 +113,8 @@ window.__ModuleLoader__.load({
111
113
  "modelEntry": "zGbnIq_modelEntry",
112
114
  "modelField": "zGbnIq_modelField",
113
115
  "modelFieldLabel": "zGbnIq_modelFieldLabel",
116
+ "modelInputChoices": "zGbnIq_modelInputChoices",
117
+ "modelInputTypes": "zGbnIq_modelInputTypes",
114
118
  "modelList": "zGbnIq_modelList",
115
119
  "modelListHead": "zGbnIq_modelListHead",
116
120
  "modelRow": "zGbnIq_modelRow",
@@ -158,6 +162,122 @@ window.__ModuleLoader__.load({
158
162
  });
159
163
  }
160
164
  //#endregion
165
+ //#region lib/types/client/ModelInputTypes.js
166
+ /**
167
+ * Edit a nonempty set of input types, displaying inherited types before an override exists.
168
+ * @param props - model declaration and row replacement action.
169
+ * @returns the labeled text and image checkboxes.
170
+ */
171
+ function ModelInputTypes({ model, field, position, disabled, fallback, t, onChange }) {
172
+ const modalities = model[field];
173
+ const selected = Array.isArray(modalities) && modalities.length > 0 ? modalities : fallback ?? ["text"];
174
+ return (0, react_jsx_runtime.jsxs)("fieldset", {
175
+ className: ModelsSection_module_css_default["modelInputTypes"],
176
+ "aria-label": `${t("modelInputTypes")} ${String(position)}`,
177
+ children: [(0, react_jsx_runtime.jsx)("legend", {
178
+ className: ModelsSection_module_css_default["modelFieldLabel"],
179
+ children: t("modelInputTypes")
180
+ }), (0, react_jsx_runtime.jsx)("div", {
181
+ className: ModelsSection_module_css_default["modelInputChoices"],
182
+ children: ["text", "image"].map((modality) => (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
183
+ label: t(modality === "text" ? "modelInputText" : "modelInputImage"),
184
+ checked: selected.includes(modality),
185
+ disabled: disabled || selected.length === 1 && selected.includes(modality),
186
+ onChange: (checked) => {
187
+ const nextSelected = ["text", "image"].filter((value) => value === modality ? checked : selected.includes(value));
188
+ const next = {
189
+ ...model,
190
+ [field]: nextSelected
191
+ };
192
+ if (field === "inputModalities" && !nextSelected.includes("image")) {
193
+ Reflect.deleteProperty(next, "imagePixelBudget");
194
+ Reflect.deleteProperty(next, "imageMaxBytes");
195
+ }
196
+ onChange(next);
197
+ }
198
+ }, modality))
199
+ })]
200
+ });
201
+ }
202
+ //#endregion
203
+ //#region lib/types/client/ModelRow.js
204
+ /**
205
+ * Render consistent model identity, capacity, and input-type controls.
206
+ * @param props - drafted fields and their owning editor's actions.
207
+ * @returns one expandable model entry.
208
+ */
209
+ function ModelRow(props) {
210
+ const { model, position, t, disabled } = props;
211
+ return (0, react_jsx_runtime.jsxs)("div", {
212
+ className: ModelsSection_module_css_default["modelEntry"],
213
+ children: [(0, react_jsx_runtime.jsxs)("div", {
214
+ className: ModelsSection_module_css_default["modelRow"],
215
+ children: [
216
+ ["id", "name"].map((field) => (0, react_jsx_runtime.jsx)("input", {
217
+ className: ModelsSection_module_css_default["input"],
218
+ type: "text",
219
+ value: typeof model[field] === "string" ? model[field] : "",
220
+ placeholder: t(field === "id" ? "modelId" : "modelName"),
221
+ "aria-label": `${t(field === "id" ? "modelId" : "modelName")} ${String(position)}`,
222
+ disabled,
223
+ onChange: (event) => {
224
+ const value = event.target.value;
225
+ props.onFieldChange(field, field === "name" && value === "" ? void 0 : value);
226
+ },
227
+ onBlur: field === "id" ? (event) => props.onIdBlur?.(event.target.value) : void 0
228
+ }, field)),
229
+ (0, react_jsx_runtime.jsx)("button", {
230
+ type: "button",
231
+ className: ModelsSection_module_css_default["iconButton"],
232
+ "aria-label": `${t("modelAdvanced")} ${String(position)}`,
233
+ "aria-expanded": props.expanded,
234
+ title: t("modelAdvanced"),
235
+ onClick: props.onToggle,
236
+ children: props.expanded ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, {})
237
+ }),
238
+ (0, react_jsx_runtime.jsx)("button", {
239
+ type: "button",
240
+ className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
241
+ "aria-label": `${t("removeModel")} ${String(position)}`,
242
+ title: t("removeModel"),
243
+ disabled,
244
+ onClick: props.onRemove,
245
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 })
246
+ })
247
+ ]
248
+ }), props.expanded ? (0, react_jsx_runtime.jsxs)("div", {
249
+ className: ModelsSection_module_css_default["modelAdvanced"],
250
+ children: [["contextWindow", "maxTokens"].map((field) => (0, react_jsx_runtime.jsxs)("label", {
251
+ className: ModelsSection_module_css_default["modelField"],
252
+ children: [(0, react_jsx_runtime.jsx)("span", {
253
+ className: ModelsSection_module_css_default["modelFieldLabel"],
254
+ children: t(field)
255
+ }), (0, react_jsx_runtime.jsx)("input", {
256
+ className: ModelsSection_module_css_default["input"],
257
+ type: "text",
258
+ inputMode: "numeric",
259
+ value: props[field].value,
260
+ placeholder: props[field].placeholder,
261
+ "aria-label": `${t(field)} ${String(position)}`,
262
+ disabled,
263
+ onChange: (event) => {
264
+ props[field].onChange(event.target.value);
265
+ },
266
+ onBlur: props[field].onBlur
267
+ })]
268
+ }, field)), (0, react_jsx_runtime.jsx)(ModelInputTypes, {
269
+ model,
270
+ field: props.inputField,
271
+ position,
272
+ fallback: props.inputFallback,
273
+ disabled: disabled || props.inputLoading === true,
274
+ t,
275
+ onChange: props.onChange
276
+ })]
277
+ }) : null]
278
+ });
279
+ }
280
+ //#endregion
161
281
  //#region lib/types/client/DeepSeekModelsEditor.js
162
282
  /**
163
283
  * Curated editor for the direct DeepSeek adapter's advisory model catalog.
@@ -252,7 +372,7 @@ window.__ModuleLoader__.load({
252
372
  }
253
373
  /**
254
374
  * Render the direct DeepSeek adapter's model catalog: id and display name on
255
- * each row, capacities behind the row's own disclosure.
375
+ * each row, capacities and input types behind the row's own disclosure.
256
376
  * @param props - effective rows plus the array-level override actions.
257
377
  * @returns the catalog editor.
258
378
  */
@@ -320,29 +440,16 @@ window.__ModuleLoader__.load({
320
440
  return next;
321
441
  });
322
442
  };
323
- /** One capacity field of one row, rendered inside the row's disclosure. */
324
- const capacityField = (model, index, field, fallback) => (0, react_jsx_runtime.jsxs)("label", {
325
- className: ModelsSection_module_css_default["modelField"],
326
- children: [(0, react_jsx_runtime.jsx)("span", {
327
- className: ModelsSection_module_css_default["modelFieldLabel"],
328
- children: props.t(field === "contextWindow" ? "contextWindow" : "maxTokens")
329
- }), (0, react_jsx_runtime.jsx)("input", {
330
- className: ModelsSection_module_css_default["input"],
331
- type: "text",
332
- inputMode: "numeric",
333
- value: capacityText(model, index, field),
334
- placeholder: fallback === void 0 ? props.t(field === "contextWindow" ? "contextWindowPlaceholder" : "maxTokensPlaceholder") : formatCapacity(fallback),
335
- "aria-label": `${props.t(field === "contextWindow" ? "contextWindow" : "maxTokens")} ${String(index + 1)}`,
336
- disabled: props.disabled,
337
- onChange: (event) => {
338
- const text = event.target.value;
339
- setEditing((current) => new Map(current).set(`${String(index)}:${field}`, text));
340
- update(index, field, parseCapacity(text));
341
- },
342
- onBlur: () => {
343
- settleCapacity(index, field);
344
- }
345
- })]
443
+ const capacityInput = (model, index, field, fallback) => ({
444
+ value: capacityText(model, index, field),
445
+ placeholder: fallback === void 0 ? props.t(field === "contextWindow" ? "contextWindowPlaceholder" : "maxTokensPlaceholder") : formatCapacity(fallback),
446
+ onChange: (text) => {
447
+ setEditing((current) => new Map(current).set(`${String(index)}:${field}`, text));
448
+ update(index, field, parseCapacity(text));
449
+ },
450
+ onBlur: () => {
451
+ settleCapacity(index, field);
452
+ }
346
453
  });
347
454
  return (0, react_jsx_runtime.jsxs)("section", {
348
455
  className: ModelsSection_module_css_default["modelCatalog"],
@@ -372,64 +479,31 @@ window.__ModuleLoader__.load({
372
479
  children: props.t("modelsEmpty")
373
480
  }) : (0, react_jsx_runtime.jsx)("div", {
374
481
  className: ModelsSection_module_css_default["modelList"],
375
- children: props.models.map((model, index) => (0, react_jsx_runtime.jsxs)("div", {
376
- className: ModelsSection_module_css_default["modelEntry"],
377
- children: [(0, react_jsx_runtime.jsxs)("div", {
378
- className: ModelsSection_module_css_default["modelRow"],
379
- children: [
380
- (0, react_jsx_runtime.jsx)("input", {
381
- className: ModelsSection_module_css_default["input"],
382
- type: "text",
383
- value: typeof model["id"] === "string" ? model["id"] : "",
384
- placeholder: props.t("modelId"),
385
- "aria-label": `${props.t("modelId")} ${String(index + 1)}`,
386
- disabled: props.disabled,
387
- onChange: (event) => {
388
- update(index, "id", event.target.value);
389
- },
390
- onBlur: (event) => {
391
- const trimmed = event.target.value.trim();
392
- if (trimmed !== event.target.value) update(index, "id", trimmed);
393
- }
394
- }),
395
- (0, react_jsx_runtime.jsx)("input", {
396
- className: ModelsSection_module_css_default["input"],
397
- type: "text",
398
- value: typeof model["name"] === "string" ? model["name"] : "",
399
- placeholder: props.t("modelName"),
400
- "aria-label": `${props.t("modelName")} ${String(index + 1)}`,
401
- disabled: props.disabled,
402
- onChange: (event) => {
403
- update(index, "name", event.target.value === "" ? void 0 : event.target.value);
404
- }
405
- }),
406
- (0, react_jsx_runtime.jsx)("button", {
407
- type: "button",
408
- className: ModelsSection_module_css_default["iconButton"],
409
- "aria-label": `${props.t("modelAdvanced")} ${String(index + 1)}`,
410
- "aria-expanded": expanded.has(index),
411
- title: props.t("modelAdvanced"),
412
- onClick: () => {
413
- toggle(index);
414
- },
415
- children: expanded.has(index) ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
416
- }),
417
- (0, react_jsx_runtime.jsx)("button", {
418
- type: "button",
419
- className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
420
- "aria-label": `${props.t("removeModel")} ${String(index + 1)}`,
421
- title: props.t("removeModel"),
422
- disabled: props.disabled,
423
- onClick: () => {
424
- remove(index);
425
- },
426
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
427
- })
428
- ]
429
- }), expanded.has(index) ? (0, react_jsx_runtime.jsxs)("div", {
430
- className: ModelsSection_module_css_default["modelAdvanced"],
431
- children: [capacityField(model, index, "contextWindow", props.defaultContextWindow), capacityField(model, index, "maxTokens", props.defaultMaxTokens)]
432
- }) : null]
482
+ children: props.models.map((model, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
483
+ model,
484
+ position: index + 1,
485
+ inputField: "inputModalities",
486
+ expanded: expanded.has(index),
487
+ disabled: props.disabled,
488
+ t: props.t,
489
+ contextWindow: capacityInput(model, index, "contextWindow", props.defaultContextWindow),
490
+ maxTokens: capacityInput(model, index, "maxTokens", props.defaultMaxTokens),
491
+ onFieldChange: (field, value) => {
492
+ update(index, field, value);
493
+ },
494
+ onIdBlur: (value) => {
495
+ const trimmed = value.trim();
496
+ if (trimmed !== value) update(index, "id", trimmed);
497
+ },
498
+ onChange: (next) => {
499
+ props.onChange(props.models.map((row, at) => at === index ? next : row));
500
+ },
501
+ onToggle: () => {
502
+ toggle(index);
503
+ },
504
+ onRemove: () => {
505
+ remove(index);
506
+ }
433
507
  }, index))
434
508
  }),
435
509
  (0, react_jsx_runtime.jsxs)("button", {
@@ -439,7 +513,7 @@ window.__ModuleLoader__.load({
439
513
  onClick: () => {
440
514
  props.onChange([...props.models.map((model) => ({ ...model })), { id: "" }]);
441
515
  },
442
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), props.t("addModel")]
516
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), props.t("addModel")]
443
517
  })
444
518
  ]
445
519
  });
@@ -471,44 +545,6 @@ window.__ModuleLoader__.load({
471
545
  const value = model[key];
472
546
  return typeof value === "number" ? value : void 0;
473
547
  }
474
- /** Disclosure chevron; rotates to point down while its row is open. */
475
- function IconChevron({ open }) {
476
- return (0, react_jsx_runtime.jsx)("svg", {
477
- width: "14",
478
- height: "14",
479
- viewBox: "0 0 16 16",
480
- fill: "none",
481
- "aria-hidden": true,
482
- style: {
483
- transform: open ? "rotate(90deg)" : void 0,
484
- transition: "transform 120ms ease"
485
- },
486
- children: (0, react_jsx_runtime.jsx)("path", {
487
- d: "M6 3.5L10.5 8L6 12.5",
488
- stroke: "currentColor",
489
- strokeWidth: "1.5",
490
- strokeLinecap: "round",
491
- strokeLinejoin: "round"
492
- })
493
- });
494
- }
495
- /** Removal glyph for one model row. */
496
- function IconTrash() {
497
- return (0, react_jsx_runtime.jsx)("svg", {
498
- width: "14",
499
- height: "14",
500
- viewBox: "0 0 16 16",
501
- fill: "none",
502
- "aria-hidden": true,
503
- children: (0, react_jsx_runtime.jsx)("path", {
504
- d: "M2.5 4h11M6.5 4V2.5h3V4M4 4l.7 9a1 1 0 001 .9h4.6a1 1 0 001-.9L12 4M6.5 6.8v4.4M9.5 6.8v4.4",
505
- stroke: "currentColor",
506
- strokeWidth: "1.3",
507
- strokeLinecap: "round",
508
- strokeLinejoin: "round"
509
- })
510
- });
511
- }
512
548
  /**
513
549
  * What an empty capacity field is worth, shown as its placeholder so a row left
514
550
  * blank does not read as a model with no capacity at all.
@@ -534,13 +570,14 @@ window.__ModuleLoader__.load({
534
570
  function capacitySpelling(value) {
535
571
  return value === void 0 ? "" : formatCapacity(value);
536
572
  }
537
- /** Adopt a candidate, keeping whatever capacities the provider disclosed. */
573
+ /** Adopt a candidate, preserving disclosed capacities and input types. */
538
574
  function adopt(candidate) {
539
575
  return {
540
576
  id: candidate.id,
541
577
  ...candidate.name === void 0 ? {} : { name: candidate.name },
542
578
  ...candidate.contextWindow === void 0 ? {} : { contextWindow: candidate.contextWindow },
543
- ...candidate.maxTokens === void 0 ? {} : { maxTokens: candidate.maxTokens }
579
+ ...candidate.maxTokens === void 0 ? {} : { maxTokens: candidate.maxTokens },
580
+ ...candidate.inputModalities === void 0 ? {} : { input: [...candidate.inputModalities] }
544
581
  };
545
582
  }
546
583
  /**
@@ -549,9 +586,35 @@ window.__ModuleLoader__.load({
549
586
  * @returns the model-list editor.
550
587
  */
551
588
  function ModelListEditor(props) {
552
- const { models, onChange, probe, operations, t, disabled } = props;
589
+ const { models, onChange, probe, operations, t, disabled, onBusyChange } = props;
590
+ const { catalogProvider } = props;
553
591
  const [busy, setBusy] = (0, react.useState)(false);
592
+ (0, react.useEffect)(() => {
593
+ onBusyChange(busy);
594
+ }, [busy, onBusyChange]);
554
595
  const [failure, setFailure] = (0, react.useState)(void 0);
596
+ const [inheritedCatalog, setInheritedCatalog] = (0, react.useState)(void 0);
597
+ (0, react.useEffect)(() => {
598
+ if (catalogProvider === void 0) return;
599
+ let current = true;
600
+ operations.discoverModels(probe.settingsNs, { provider: catalogProvider }).then((answer) => {
601
+ if (!current) return;
602
+ setInheritedCatalog({
603
+ provider: catalogProvider,
604
+ models: answer.kind === "found" ? answer.models : []
605
+ });
606
+ setFailure(answer.kind === "refused" ? answer.message : void 0);
607
+ });
608
+ return () => {
609
+ current = false;
610
+ };
611
+ }, [
612
+ catalogProvider,
613
+ operations,
614
+ probe.settingsNs
615
+ ]);
616
+ const catalog = inheritedCatalog?.provider === catalogProvider ? inheritedCatalog?.models : void 0;
617
+ const inputDefaults = (0, react.useMemo)(() => new Map(catalog?.map((model) => [model.id, model.inputModalities])), [catalog]);
555
618
  const [candidates, setCandidates] = (0, react.useState)(void 0);
556
619
  const [picked, setPicked] = (0, react.useState)(/* @__PURE__ */ new Set());
557
620
  const [candidateQuery, setCandidateQuery] = (0, react.useState)("");
@@ -607,6 +670,10 @@ window.__ModuleLoader__.load({
607
670
  return;
608
671
  }
609
672
  const found = answer.models;
673
+ if (catalogProvider !== void 0) setInheritedCatalog({
674
+ provider: catalogProvider,
675
+ models: found
676
+ });
610
677
  if (found.length === 0) {
611
678
  setFailure(t("fetchEmpty"));
612
679
  return;
@@ -695,110 +762,60 @@ window.__ModuleLoader__.load({
695
762
  className: ModelsSection_module_css_default["modelEmpty"],
696
763
  children: t("modelsEmpty")
697
764
  }) : null,
698
- models.map((model, index) => (0, react_jsx_runtime.jsxs)("div", {
699
- className: ModelsSection_module_css_default["modelEntry"],
700
- children: [(0, react_jsx_runtime.jsxs)("div", {
701
- className: ModelsSection_module_css_default["modelRow"],
702
- children: [
703
- (0, react_jsx_runtime.jsx)("input", {
704
- className: ModelsSection_module_css_default["input"],
705
- type: "text",
706
- value: textOf(model, "id"),
707
- placeholder: t("modelId"),
708
- "aria-label": `${t("modelId")} ${index + 1}`,
709
- disabled,
710
- onChange: (event) => {
711
- patch(index, { id: event.target.value });
712
- }
713
- }),
714
- (0, react_jsx_runtime.jsx)("input", {
715
- className: ModelsSection_module_css_default["input"],
716
- type: "text",
717
- value: textOf(model, "name"),
718
- placeholder: t("modelName"),
719
- "aria-label": `${t("modelName")} ${index + 1}`,
720
- disabled,
721
- onChange: (event) => {
722
- patch(index, { name: event.target.value === "" ? void 0 : event.target.value });
723
- }
724
- }),
725
- (0, react_jsx_runtime.jsx)("button", {
726
- type: "button",
727
- className: ModelsSection_module_css_default["iconButton"],
728
- "aria-label": `${t("modelAdvanced")} ${index + 1}`,
729
- "aria-expanded": expanded.has(index),
730
- title: t("modelAdvanced"),
731
- onClick: () => {
732
- toggleExpanded(index);
733
- },
734
- children: (0, react_jsx_runtime.jsx)(IconChevron, { open: expanded.has(index) })
735
- }),
736
- (0, react_jsx_runtime.jsx)("button", {
737
- type: "button",
738
- className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
739
- "aria-label": `${t("removeModel")} ${index + 1}`,
740
- title: t("removeModel"),
741
- disabled,
742
- onClick: () => {
743
- onChange(models.filter((_model, at) => at !== index));
744
- setExpanded((current) => {
745
- const next = /* @__PURE__ */ new Set();
746
- for (const at of current) if (at < index) next.add(at);
747
- else if (at > index) next.add(at - 1);
748
- return next;
749
- });
750
- setEditing((current) => reindexOnRemove(current, index));
751
- },
752
- children: (0, react_jsx_runtime.jsx)(IconTrash, {})
753
- })
754
- ]
755
- }), expanded.has(index) ? (0, react_jsx_runtime.jsxs)("div", {
756
- className: ModelsSection_module_css_default["modelAdvanced"],
757
- children: [(0, react_jsx_runtime.jsxs)("label", {
758
- className: ModelsSection_module_css_default["modelField"],
759
- children: [(0, react_jsx_runtime.jsx)("span", {
760
- className: ModelsSection_module_css_default["modelFieldLabel"],
761
- children: t("modelContextWindow")
762
- }), (0, react_jsx_runtime.jsx)("input", {
763
- className: ModelsSection_module_css_default["input"],
764
- type: "text",
765
- inputMode: "numeric",
766
- value: capacityText(model, index, "contextWindow"),
767
- placeholder: CAPACITY_HINT.contextWindow,
768
- "aria-label": `${t("modelContextWindow")} ${index + 1}`,
769
- disabled,
770
- onChange: (event) => {
771
- editCapacity(index, "contextWindow", event.target.value);
772
- }
773
- })]
774
- }), (0, react_jsx_runtime.jsxs)("label", {
775
- className: ModelsSection_module_css_default["modelField"],
776
- children: [(0, react_jsx_runtime.jsx)("span", {
777
- className: ModelsSection_module_css_default["modelFieldLabel"],
778
- children: t("modelMaxTokens")
779
- }), (0, react_jsx_runtime.jsx)("input", {
780
- className: ModelsSection_module_css_default["input"],
781
- type: "text",
782
- inputMode: "numeric",
783
- value: capacityText(model, index, "maxTokens"),
784
- placeholder: CAPACITY_HINT.maxTokens,
785
- "aria-label": `${t("modelMaxTokens")} ${index + 1}`,
786
- disabled,
787
- onChange: (event) => {
788
- editCapacity(index, "maxTokens", event.target.value);
789
- }
790
- })]
791
- })]
792
- }) : null]
793
- }, index)),
794
- (0, react_jsx_runtime.jsx)("button", {
765
+ (0, react_jsx_runtime.jsx)("div", {
766
+ className: ModelsSection_module_css_default["modelList"],
767
+ children: models.map((model, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
768
+ model,
769
+ position: index + 1,
770
+ inputField: "input",
771
+ inputFallback: inputDefaults.get(textOf(model, "id")) ?? props.defaultInput,
772
+ inputLoading: catalogProvider !== void 0 && catalog === void 0,
773
+ expanded: expanded.has(index),
774
+ disabled,
775
+ t,
776
+ contextWindow: {
777
+ value: capacityText(model, index, "contextWindow"),
778
+ placeholder: CAPACITY_HINT.contextWindow,
779
+ onChange: (text) => {
780
+ editCapacity(index, "contextWindow", text);
781
+ }
782
+ },
783
+ maxTokens: {
784
+ value: capacityText(model, index, "maxTokens"),
785
+ placeholder: CAPACITY_HINT.maxTokens,
786
+ onChange: (text) => {
787
+ editCapacity(index, "maxTokens", text);
788
+ }
789
+ },
790
+ onFieldChange: (field, value) => {
791
+ patch(index, { [field]: value });
792
+ },
793
+ onChange: (next) => {
794
+ onChange(models.map((row, at) => at === index ? next : row));
795
+ },
796
+ onToggle: () => {
797
+ toggleExpanded(index);
798
+ },
799
+ onRemove: () => {
800
+ onChange(models.filter((_model, at) => at !== index));
801
+ setExpanded((current) => {
802
+ const next = /* @__PURE__ */ new Set();
803
+ for (const at of current) if (at < index) next.add(at);
804
+ else if (at > index) next.add(at - 1);
805
+ return next;
806
+ });
807
+ setEditing((current) => reindexOnRemove(current, index));
808
+ }
809
+ }, index))
810
+ }),
811
+ (0, react_jsx_runtime.jsxs)("button", {
795
812
  type: "button",
796
813
  className: ModelsSection_module_css_default["addModelButton"],
797
814
  disabled,
798
815
  onClick: () => {
799
816
  onChange([...models, { id: "" }]);
800
817
  },
801
- children: t("addModel")
818
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("addModel")]
802
819
  }),
803
820
  failure !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
804
821
  className: ModelsSection_module_css_default["error"],
@@ -1108,18 +1125,46 @@ window.__ModuleLoader__.load({
1108
1125
  return { kind: "credential-missing" };
1109
1126
  }
1110
1127
  //#endregion
1128
+ //#region lib/types/client/protocol-label.js
1129
+ /**
1130
+ * Product names for the wire protocols a pi-ai route may speak. The pickers
1131
+ * show these instead of the schema identifiers (`openai-completions`), which
1132
+ * are what `settings.yaml` carries and what the option values stay.
1133
+ */
1134
+ /** The protocols this page names, keyed by their schema identifier. */
1135
+ const PROTOCOL_LABEL_KEYS = {
1136
+ "openai-completions": "protocolOpenAiCompletions",
1137
+ "openai-responses": "protocolOpenAiResponses",
1138
+ "anthropic-messages": "protocolAnthropicMessages"
1139
+ };
1140
+ /**
1141
+ * The picker text for one protocol identifier.
1142
+ * @param t - section copy.
1143
+ * @param protocol - the schema identifier of the protocol.
1144
+ * @returns the product name this page knows the protocol by; a protocol the
1145
+ * adapter adds before this page names it shows its identifier, the spelling
1146
+ * `settings.yaml` needs anyway.
1147
+ */
1148
+ function protocolLabel(t, protocol) {
1149
+ const key = PROTOCOL_LABEL_KEYS[protocol];
1150
+ return key === void 0 ? protocol : t(key);
1151
+ }
1152
+ //#endregion
1111
1153
  //#region lib/types/client/CustomProviderCard.js
1112
1154
  /**
1113
1155
  * The card that declares a provider pi-ai does not ship — an OpenAI-compatible
1114
1156
  * gateway, a self-hosted server, or a provider newer than the installed
1115
1157
  * catalog.
1116
1158
  *
1117
- * This is a create, not an edit, which is why it is its own card rather than
1159
+ * This is a create, not an edit, which is why it is its own form rather than
1118
1160
  * the provider editor with extra fields: the route id is being *chosen* here,
1119
- * and the settings address does not exist until it is. One `settings.mutate`
1120
- * sets the whole profile at `providers.<route>`; the key travels separately
1121
- * through `credentials/set` under the reference the profile records, exactly as
1122
- * an existing provider's key does.
1161
+ * and the settings address does not exist until it is. It renders as the
1162
+ * custom-API panel of the section's add card; the card's mode switch names it
1163
+ * when both modes are offered, and with the custom mode alone the card shows
1164
+ * this form directly. One `settings.mutate` sets the whole profile at
1165
+ * `providers.<route>`; the key travels separately through `credentials/set`
1166
+ * under the reference the profile records, exactly as an existing provider's
1167
+ * key does.
1123
1168
  *
1124
1169
  * The three fields a hand-declared route cannot default — endpoint, protocol,
1125
1170
  * and at least one model — are required here rather than at load, so the
@@ -1156,7 +1201,7 @@ window.__ModuleLoader__.load({
1156
1201
  * @returns the creation card.
1157
1202
  */
1158
1203
  function CustomProviderCard(props) {
1159
- const { taken, protocols, operations, t } = props;
1204
+ const { taken, protocols, operations, t, onBusyChange } = props;
1160
1205
  const [openedAt] = (0, react.useState)(() => props.revision);
1161
1206
  const [route, setRoute] = (0, react.useState)("");
1162
1207
  const [displayName, setDisplayName] = (0, react.useState)("");
@@ -1165,6 +1210,14 @@ window.__ModuleLoader__.load({
1165
1210
  const [keyDraft, setKeyDraft] = (0, react.useState)("");
1166
1211
  const [models, setModels] = (0, react.useState)([]);
1167
1212
  const [busy, setBusy] = (0, react.useState)(false);
1213
+ const [listBusy, setListBusy] = (0, react.useState)(false);
1214
+ (0, react.useEffect)(() => {
1215
+ onBusyChange?.(busy || listBusy);
1216
+ }, [
1217
+ busy,
1218
+ listBusy,
1219
+ onBusyChange
1220
+ ]);
1168
1221
  const [failure, setFailure] = (0, react.useState)(void 0);
1169
1222
  /**
1170
1223
  * The profile write landed. Only the key write can still be outstanding, so
@@ -1226,13 +1279,6 @@ window.__ModuleLoader__.load({
1226
1279
  return (0, react_jsx_runtime.jsxs)("div", {
1227
1280
  className: ModelsSection_module_css_default["editor"],
1228
1281
  children: [
1229
- (0, react_jsx_runtime.jsx)("div", {
1230
- className: ModelsSection_module_css_default["editorHeader"],
1231
- children: (0, react_jsx_runtime.jsx)("span", {
1232
- className: ModelsSection_module_css_default["editorTitle"],
1233
- children: t("customTitle")
1234
- })
1235
- }),
1236
1282
  (0, react_jsx_runtime.jsxs)("div", {
1237
1283
  className: ModelsSection_module_css_default["field"],
1238
1284
  children: [(0, react_jsx_runtime.jsx)("span", {
@@ -1283,7 +1329,7 @@ window.__ModuleLoader__.load({
1283
1329
  className: ModelsSection_module_css_default["input"],
1284
1330
  type: "text",
1285
1331
  value: baseURL,
1286
- placeholder: t("customBaseUrlPlaceholder"),
1332
+ placeholder: t(protocol === "anthropic-messages" ? "customAnthropicBaseUrlPlaceholder" : "customBaseUrlPlaceholder"),
1287
1333
  "aria-label": t("baseUrl"),
1288
1334
  "aria-invalid": baseUrlInvalid,
1289
1335
  disabled: profileDisabled,
@@ -1311,7 +1357,7 @@ window.__ModuleLoader__.load({
1311
1357
  },
1312
1358
  children: protocols.map((choice) => (0, react_jsx_runtime.jsx)("option", {
1313
1359
  value: choice,
1314
- children: choice
1360
+ children: protocolLabel(t, choice)
1315
1361
  }, choice))
1316
1362
  })]
1317
1363
  }),
@@ -1352,7 +1398,8 @@ window.__ModuleLoader__.load({
1352
1398
  probeBlocked: baseUrlInvalid ? "customBaseUrlInvalid" : keyFailure === "keyBlank" ? "keyBlankNew" : keyFailure,
1353
1399
  operations,
1354
1400
  t,
1355
- disabled: profileDisabled
1401
+ disabled: profileDisabled,
1402
+ onBusyChange: setListBusy
1356
1403
  }),
1357
1404
  failure !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
1358
1405
  className: ModelsSection_module_css_default["error"],
@@ -1396,9 +1443,9 @@ window.__ModuleLoader__.load({
1396
1443
  * Reasoning effort is deliberately absent: it is a per-MODEL capability, and
1397
1444
  * the models under one provider disagree about it, so a provider-scoped
1398
1445
  * control can only be set to a value some of them reject. The composer's
1399
- * model picker offers each model its own levels; `settings.yaml` keeps the
1446
+ * model picker offers each model its own levels; `cordis.patch.yml` keeps the
1400
1447
  * profile field for a deployment that knows its route. Everything else stays
1401
- * owned by `settings.yaml`. Profile edits land as minimal `settings.mutate`
1448
+ * owned by `cordis.patch.yml`. Profile edits land as minimal `settings.mutate`
1402
1449
  * path ops against the stored section — the card names only the fields it can
1403
1450
  * see instead of rebuilding the whole subtree from a partial descriptor.
1404
1451
  */
@@ -1458,6 +1505,15 @@ window.__ModuleLoader__.load({
1458
1505
  const [keyDraft, setKeyDraft] = (0, react.useState)("");
1459
1506
  const [keyState, setKeyState] = (0, react.useState)(void 0);
1460
1507
  const [busy, setBusy] = (0, react.useState)(false);
1508
+ const [listBusy, setListBusy] = (0, react.useState)(false);
1509
+ const { onBusyChange } = props;
1510
+ (0, react.useEffect)(() => {
1511
+ onBusyChange?.(busy || listBusy);
1512
+ }, [
1513
+ busy,
1514
+ listBusy,
1515
+ onBusyChange
1516
+ ]);
1461
1517
  const [failure, setFailure] = (0, react.useState)(void 0);
1462
1518
  const [committedOriginal, setCommittedOriginal] = (0, react.useState)(() => schema.getPath(namespace.user, settingsPath));
1463
1519
  const [expectedRevision, setExpectedRevision] = (0, react.useState)(() => namespace.revision);
@@ -1591,6 +1647,7 @@ window.__ModuleLoader__.load({
1591
1647
  const models = modelDrafts(modelsOverridden ? customModels : inheritedModels());
1592
1648
  const defaultContextWindow = schema.getPath(fallback, ["defaultContextWindow"]);
1593
1649
  const defaultMaxTokens = schema.getPath(fallback, ["maxTokens"]);
1650
+ const defaultInput = schema.getPath(fallback, ["defaultInput"]);
1594
1651
  const keyPlaceholder = keyLocked ? t("keyEnvLocked") : keyState?.configured === true && props.credentialRequired !== true ? t("keyStored") : family === "pi-ai" ? t("keyPlaceholderNative") : t("keyPlaceholder");
1595
1652
  /** What both family editors take: the rows, whose layer owns them, and the two writes. */
1596
1653
  const catalogProps = {
@@ -1668,7 +1725,7 @@ window.__ModuleLoader__.load({
1668
1725
  className: ModelsSection_module_css_default["input"],
1669
1726
  type: "text",
1670
1727
  value: stringAt(draft, "baseURL") ?? "",
1671
- placeholder: family === "deepseek" ? t(stringAt(fallback, "protocol") === "messages" ? "deepSeekMessagesBaseUrl" : "deepSeekChatBaseUrl") : stringAt(fallback, "baseURL") ?? t("baseUrlDefault"),
1728
+ placeholder: family === "deepseek" ? t("deepSeekBaseUrl") : stringAt(fallback, "baseURL") ?? t("baseUrlDefault"),
1672
1729
  "aria-describedby": family === "deepseek" ? `${props.provider}-endpoint-hint` : void 0,
1673
1730
  "aria-label": t("baseUrl"),
1674
1731
  disabled,
@@ -1701,7 +1758,7 @@ window.__ModuleLoader__.load({
1701
1758
  children: t("customApiUnset")
1702
1759
  }) : null, protocols.map((choice) => (0, react_jsx_runtime.jsx)("option", {
1703
1760
  value: choice,
1704
- children: choice
1761
+ children: protocolLabel(t, choice)
1705
1762
  }, choice))]
1706
1763
  })]
1707
1764
  }) : null,
@@ -1711,9 +1768,12 @@ window.__ModuleLoader__.load({
1711
1768
  defaultMaxTokens: typeof defaultMaxTokens === "number" ? defaultMaxTokens : void 0
1712
1769
  }) : (0, react_jsx_runtime.jsx)(ModelListEditor, {
1713
1770
  ...catalogProps,
1771
+ catalogProvider: props.declared === true ? void 0 : props.provider,
1772
+ defaultInput: Array.isArray(defaultInput) ? defaultInput : void 0,
1714
1773
  probe,
1715
1774
  probeBlocked: keyFailure,
1716
- operations
1775
+ operations,
1776
+ onBusyChange: setListBusy
1717
1777
  })
1718
1778
  ]
1719
1779
  })]
@@ -1770,11 +1830,19 @@ window.__ModuleLoader__.load({
1770
1830
  * solid configured or missing dots. A whole-section provider without a
1771
1831
  * configured key renders as its open setup card instead of a row, but only in
1772
1832
  * the first-run posture — no provider on the page can serve requests yet — and
1773
- * only until the user closes that card; the add flow is a card carrying the
1774
- * dormant-provider select. Each card kind owns its own open state, so closing
1775
- * one never discards a draft in another. Every mutation writes through the
1776
- * wire, while a provider removal first requires confirmation; the page
1777
- * re-renders from pushed invalidations or the post-apply reload.
1833
+ * only until the user closes that card. The add flow is one card behind one
1834
+ * button: a mode switch chooses between adopting a dormant directory provider
1835
+ * (the catalog select over the provider editor) and declaring a custom model
1836
+ * API (the create form). A panel mounts the first time its mode is shown and
1837
+ * stays mounted, hidden, while the card is open and its mode stays offered,
1838
+ * so switching modes discards neither draft and an unvisited mode costs
1839
+ * nothing; the switch holds still while either panel has a write or an
1840
+ * endpoint interrogation in flight, since a switch underneath one would
1841
+ * orphan the answer. Each card kind owns its own open state, so closing one
1842
+ * never discards a draft in another. Every
1843
+ * mutation writes through the wire, while a provider removal first requires
1844
+ * confirmation; the page re-renders from pushed invalidations or the
1845
+ * post-apply reload.
1778
1846
  */
1779
1847
  /** Render an editor for either the setup posture or an expanded provider row. */
1780
1848
  function renderProviderEditor({ target, ...props }) {
@@ -1876,30 +1944,48 @@ window.__ModuleLoader__.load({
1876
1944
  const { controller, operations, schema, t } = injected;
1877
1945
  const state = injected.useSnapshot((snapshot) => snapshot);
1878
1946
  const [editing, setEditing] = (0, react.useState)(void 0);
1879
- const [adding, setAdding] = (0, react.useState)(false);
1947
+ const [addOpen, setAddOpen] = (0, react.useState)(false);
1948
+ const [addMode, setAddMode] = (0, react.useState)("catalog");
1949
+ /** The modes shown since the add card opened; each keeps its panel mounted. */
1950
+ const [visited, setVisited] = (0, react.useState)(() => /* @__PURE__ */ new Set());
1951
+ /** Whether each add panel has a write or an interrogation in flight. */
1952
+ const [catalogBusy, setCatalogBusy] = (0, react.useState)(false);
1953
+ const [customBusy, setCustomBusy] = (0, react.useState)(false);
1954
+ /** Base of the add card's tab and panel ids. */
1955
+ const addId = (0, react.useId)();
1880
1956
  const [deleteTarget, setDeleteTarget] = (0, react.useState)(void 0);
1881
1957
  const [deleting, setDeleting] = (0, react.useState)(false);
1882
1958
  const [deleteFailure, setDeleteFailure] = (0, react.useState)(void 0);
1883
1959
  const [savedTarget, setSavedTarget] = (0, react.useState)(void 0);
1884
- const [declaring, setDeclaring] = (0, react.useState)(false);
1885
1960
  const [dismissedSetup, setDismissedSetup] = (0, react.useState)(() => /* @__PURE__ */ new Set());
1886
1961
  const announceSaved = (target) => {
1887
1962
  controller.load().then(() => {
1888
1963
  setSavedTarget(target);
1889
1964
  });
1890
1965
  };
1891
- const closeEditor = (changed, target) => {
1966
+ /**
1967
+ * Close the add card whole. The catalog target is forgotten with it, since
1968
+ * `editing` doubles as the row editor's target once the card is closed and a
1969
+ * refresh could otherwise open the row of a provider the draft never saved.
1970
+ * The busy flags reset here because a panel that closes itself on success
1971
+ * unmounts before it can report idle.
1972
+ */
1973
+ const closeAdd = () => {
1892
1974
  setEditing(void 0);
1893
- setAdding(false);
1894
- setDeclaring(false);
1975
+ setAddOpen(false);
1976
+ setCatalogBusy(false);
1977
+ setCustomBusy(false);
1978
+ };
1979
+ const closeEditor = (changed, target) => {
1980
+ closeAdd();
1895
1981
  if (changed) announceSaved(target);
1896
1982
  };
1897
1983
  /**
1898
- * Close a setup card, which owns none of the state above: the row-editor,
1899
- * add, and declare cards each own one of those, so clearing them here would
1900
- * discard a draft the user opened beside this card. Dismissal is this card's
1901
- * own — the provider falls back to an ordinary row for the rest of the
1902
- * session, and reopens through Edit.
1984
+ * Close a setup card, which owns none of the state above: the row-editor
1985
+ * and add cards each own one of those, so clearing them here would discard
1986
+ * a draft the user opened beside this card. Dismissal is this card's own —
1987
+ * the provider falls back to an ordinary row for the rest of the session,
1988
+ * and reopens through Edit.
1903
1989
  */
1904
1990
  const closeSetup = (changed, target) => {
1905
1991
  setDismissedSetup((previous) => new Set([...previous, target.provider]));
@@ -1952,11 +2038,37 @@ window.__ModuleLoader__.load({
1952
2038
  const anyUsable = state.rows.some(providerUsable);
1953
2039
  const configured = state.rows.filter((row) => row.configured);
1954
2040
  const configurable = state.rows.filter((row) => state.namespaces.has(row.entry.settingsNs));
1955
- const addable = configurable.filter((row) => !row.configured);
1956
- const addTarget = adding ? editing : void 0;
1957
- const addNamespace = addTarget === void 0 ? void 0 : state.namespaces.get(addTarget.settingsNs);
1958
- const addRow = addTarget === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === addTarget.provider);
1959
- const protocols = protocolChoices(state.namespaces.get("llm-pi-ai"), schema);
2041
+ const addable = state.rows.flatMap((row) => {
2042
+ const namespace = state.namespaces.get(row.entry.settingsNs);
2043
+ return namespace === void 0 || row.configured ? [] : [{
2044
+ row,
2045
+ namespace
2046
+ }];
2047
+ });
2048
+ const piAi = state.namespaces.get("llm-pi-ai");
2049
+ const protocols = protocolChoices(piAi, schema);
2050
+ const catalogOffered = configurable.length > 0;
2051
+ const catalogEnabled = addable.length > 0;
2052
+ const customOffered = piAi !== void 0;
2053
+ const customEnabled = protocols.length > 0;
2054
+ const bothOffered = catalogOffered && customOffered;
2055
+ const mode = bothOffered ? addMode : customOffered ? "custom" : "catalog";
2056
+ const mounted = (candidate) => mode === candidate || visited.has(candidate);
2057
+ const switchLocked = catalogBusy || customBusy;
2058
+ const draft = (() => {
2059
+ if (!addOpen || !catalogOffered) return void 0;
2060
+ const kept = editing === void 0 ? void 0 : state.namespaces.get(editing.settingsNs);
2061
+ if (editing !== void 0 && kept !== void 0) return {
2062
+ target: editing,
2063
+ namespace: kept
2064
+ };
2065
+ const first = addable[0];
2066
+ return first === void 0 ? void 0 : {
2067
+ target: targetOf(first.row),
2068
+ namespace: first.namespace
2069
+ };
2070
+ })();
2071
+ const addRow = draft === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === draft.target.provider);
1960
2072
  return (0, react_jsx_runtime.jsxs)("div", {
1961
2073
  className: ModelsSection_module_css_default["section"],
1962
2074
  children: [
@@ -2012,7 +2124,7 @@ window.__ModuleLoader__.load({
2012
2124
  }, { entryKey: row.entry.settingsNs })
2013
2125
  ]
2014
2126
  }, row.entry.provider);
2015
- const open = !adding && editing?.provider === row.entry.provider;
2127
+ const open = !addOpen && editing?.provider === row.entry.provider;
2016
2128
  const credentialConfigured = row.credential?.configured === true;
2017
2129
  const credentialMissing = !credentialConfigured && row.apiKeyEnv !== void 0 && row.credential?.configured === false;
2018
2130
  return (0, react_jsx_runtime.jsxs)("li", {
@@ -2051,8 +2163,7 @@ window.__ModuleLoader__.load({
2051
2163
  "aria-label": providerCopy(t("editProvider"), target),
2052
2164
  onClick: () => {
2053
2165
  setSavedTarget(void 0);
2054
- setDeclaring(false);
2055
- setAdding(false);
2166
+ setAddOpen(false);
2056
2167
  setEditing(open ? void 0 : target);
2057
2168
  },
2058
2169
  children: t("edit")
@@ -2093,94 +2204,136 @@ window.__ModuleLoader__.load({
2093
2204
  }),
2094
2205
  (0, react_jsx_runtime.jsx)("div", {
2095
2206
  className: ModelsSection_module_css_default["addBlock"],
2096
- children: addTarget !== void 0 && addNamespace !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2207
+ children: addOpen ? (0, react_jsx_runtime.jsxs)("div", {
2097
2208
  className: ModelsSection_module_css_default["addCard"],
2098
2209
  children: [
2099
2210
  (0, react_jsx_runtime.jsxs)("div", {
2100
- className: ModelsSection_module_css_default["field"],
2101
- children: [(0, react_jsx_runtime.jsx)("span", {
2102
- className: ModelsSection_module_css_default["fieldLabel"],
2103
- children: t("provider")
2104
- }), (0, react_jsx_runtime.jsx)("select", {
2105
- className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
2106
- value: addTarget.provider,
2107
- "aria-label": t("provider"),
2108
- onChange: (event) => {
2109
- const row = addable.find((candidate) => candidate.entry.provider === event.target.value);
2110
- /* v8 ignore next -- the select only lists addable rows */
2111
- if (row === void 0) return;
2112
- setEditing(targetOf(row));
2113
- },
2114
- children: addable.map((row) => (0, react_jsx_runtime.jsx)("option", {
2115
- value: row.entry.provider,
2116
- children: row.entry.displayName
2117
- }, row.entry.provider))
2211
+ className: ModelsSection_module_css_default["addModes"],
2212
+ children: [bothOffered ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
2213
+ id: addId,
2214
+ label: t("addMode"),
2215
+ value: mode,
2216
+ disabled: switchLocked,
2217
+ options: [{
2218
+ value: "catalog",
2219
+ label: t("addCatalog"),
2220
+ disabled: !catalogEnabled,
2221
+ ...catalogEnabled ? {} : { title: t("addCatalogExhausted") }
2222
+ }, {
2223
+ value: "custom",
2224
+ label: t("addCustom"),
2225
+ disabled: !customEnabled,
2226
+ ...customEnabled ? {} : { title: t("addCustomUnavailable") }
2227
+ }],
2228
+ onChange: (next) => {
2229
+ setAddMode(next);
2230
+ setVisited((previous) => new Set([...previous, next]));
2231
+ }
2232
+ }) : (0, react_jsx_runtime.jsx)("div", {
2233
+ className: ModelsSection_module_css_default["editorHeader"],
2234
+ children: (0, react_jsx_runtime.jsx)("span", {
2235
+ className: ModelsSection_module_css_default["editorTitle"],
2236
+ children: t(mode === "catalog" ? "addCatalog" : "addCustom")
2237
+ })
2238
+ }), (0, react_jsx_runtime.jsx)("p", {
2239
+ className: ModelsSection_module_css_default["advancedHint"],
2240
+ children: t(mode === "catalog" ? "addCatalogHint" : "addCustomHint")
2118
2241
  })]
2119
2242
  }),
2120
- (0, react_jsx_runtime.jsx)(ProviderEditor, {
2121
- provider: addTarget.provider,
2122
- displayName: addTarget.displayName,
2123
- hideTitle: true,
2124
- namespace: addNamespace,
2125
- schema,
2126
- settingsPath: addTarget.settingsPath,
2127
- operations,
2128
- t,
2129
- readOnly: !state.writable,
2130
- onClose: (changed) => {
2131
- closeEditor(changed, addTarget);
2132
- }
2133
- }, addTarget.provider),
2134
- addRow === void 0 ? null : renderSlot("settings.models.provider-card", {
2135
- provider: addRow.entry,
2136
- configured: addRow.configured,
2137
- keyConfigured: keyConfiguredOf(addRow)
2138
- }, { entryKey: addRow.entry.settingsNs })
2243
+ mounted("catalog") && draft !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2244
+ id: `${addId}-catalog-panel`,
2245
+ ...bothOffered ? {
2246
+ role: "tabpanel",
2247
+ "aria-labelledby": `${addId}-catalog`
2248
+ } : {},
2249
+ hidden: mode !== "catalog",
2250
+ className: ModelsSection_module_css_default["addPanel"],
2251
+ children: [
2252
+ (0, react_jsx_runtime.jsxs)("div", {
2253
+ className: ModelsSection_module_css_default["field"],
2254
+ children: [(0, react_jsx_runtime.jsx)("span", {
2255
+ className: ModelsSection_module_css_default["fieldLabel"],
2256
+ children: t("provider")
2257
+ }), (0, react_jsx_runtime.jsx)("select", {
2258
+ className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
2259
+ value: draft.target.provider,
2260
+ "aria-label": t("provider"),
2261
+ disabled: catalogBusy,
2262
+ onChange: (event) => {
2263
+ const picked = addable.find((candidate) => candidate.row.entry.provider === event.target.value);
2264
+ /* v8 ignore next -- the select only lists addable rows */
2265
+ if (picked === void 0) return;
2266
+ setEditing(targetOf(picked.row));
2267
+ },
2268
+ children: addable.map(({ row }) => (0, react_jsx_runtime.jsx)("option", {
2269
+ value: row.entry.provider,
2270
+ children: row.entry.displayName
2271
+ }, row.entry.provider))
2272
+ })]
2273
+ }),
2274
+ (0, react_jsx_runtime.jsx)(ProviderEditor, {
2275
+ provider: draft.target.provider,
2276
+ displayName: draft.target.displayName,
2277
+ hideTitle: true,
2278
+ namespace: draft.namespace,
2279
+ schema,
2280
+ settingsPath: draft.target.settingsPath,
2281
+ operations,
2282
+ t,
2283
+ readOnly: !state.writable,
2284
+ onClose: (changed) => {
2285
+ closeEditor(changed, draft.target);
2286
+ },
2287
+ onBusyChange: setCatalogBusy
2288
+ }, draft.target.provider),
2289
+ addRow === void 0 ? null : renderSlot("settings.models.provider-card", {
2290
+ provider: addRow.entry,
2291
+ configured: addRow.configured,
2292
+ keyConfigured: keyConfiguredOf(addRow)
2293
+ }, { entryKey: addRow.entry.settingsNs })
2294
+ ]
2295
+ }) : null,
2296
+ mounted("custom") && piAi !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2297
+ id: `${addId}-custom-panel`,
2298
+ ...bothOffered ? {
2299
+ role: "tabpanel",
2300
+ "aria-labelledby": `${addId}-custom`
2301
+ } : {},
2302
+ hidden: mode !== "custom",
2303
+ className: ModelsSection_module_css_default["addPanel"],
2304
+ children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2305
+ taken: state.rows.map((row) => row.entry.provider),
2306
+ protocols,
2307
+ revision: piAi.revision,
2308
+ operations,
2309
+ t,
2310
+ readOnly: !state.writable,
2311
+ onClose: (changed) => {
2312
+ closeAdd();
2313
+ if (changed) controller.load();
2314
+ },
2315
+ onBusyChange: setCustomBusy
2316
+ })
2317
+ }) : null
2139
2318
  ]
2140
- }) : declaring ? (0, react_jsx_runtime.jsx)("div", {
2141
- className: ModelsSection_module_css_default["addCard"],
2142
- children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2143
- taken: state.rows.map((row) => row.entry.provider),
2144
- protocols,
2145
- /* v8 ignore next -- the card only opens from a button disabled without this namespace */
2146
- revision: state.namespaces.get("llm-pi-ai")?.revision ?? 0,
2147
- operations,
2148
- t,
2149
- readOnly: !state.writable,
2150
- onClose: (changed) => {
2151
- setDeclaring(false);
2152
- if (changed) controller.load();
2153
- }
2154
- })
2155
- }) : (0, react_jsx_runtime.jsxs)("div", {
2319
+ }) : catalogOffered || customOffered ? (0, react_jsx_runtime.jsx)("div", {
2156
2320
  className: ModelsSection_module_css_default["addActions"],
2157
- children: [configurable.length > 0 && (0, react_jsx_runtime.jsxs)("button", {
2321
+ children: (0, react_jsx_runtime.jsxs)("button", {
2158
2322
  type: "button",
2159
2323
  className: ModelsSection_module_css_default["addButton"],
2160
- disabled: addable.length === 0 || !state.writable,
2324
+ disabled: !state.writable || !catalogEnabled && !customEnabled,
2161
2325
  onClick: () => {
2162
2326
  const first = addable[0];
2163
- /* v8 ignore next -- the button is disabled while nothing is addable */
2164
- if (first === void 0) return;
2327
+ const initial = catalogEnabled ? "catalog" : "custom";
2165
2328
  setSavedTarget(void 0);
2166
- setDeclaring(false);
2167
- setAdding(true);
2168
- setEditing(targetOf(first));
2329
+ setEditing(first === void 0 ? void 0 : targetOf(first.row));
2330
+ setAddMode(initial);
2331
+ setVisited(new Set([initial]));
2332
+ setAddOpen(true);
2169
2333
  },
2170
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("add")]
2171
- }), state.namespaces.has("llm-pi-ai") && (0, react_jsx_runtime.jsxs)("button", {
2172
- type: "button",
2173
- className: ModelsSection_module_css_default["addButton"],
2174
- disabled: protocols.length === 0 || !state.writable,
2175
- onClick: () => {
2176
- setSavedTarget(void 0);
2177
- setAdding(false);
2178
- setEditing(void 0);
2179
- setDeclaring(true);
2180
- },
2181
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("customAdd")]
2182
- })]
2183
- })
2334
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("add")]
2335
+ })
2336
+ }) : null
2184
2337
  }),
2185
2338
  renderSlot("settings.models.footer", {}),
2186
2339
  (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
@@ -2213,7 +2366,7 @@ window.__ModuleLoader__.load({
2213
2366
  }
2214
2367
  //#endregion
2215
2368
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/OnboardingModal.module.css.mjs
2216
- const css$2 = ".jLrgrW_dialog{width:min(600px,100%);padding:0}.jLrgrW_content{box-sizing:border-box;flex-direction:column;max-height:calc(100vh - 48px);padding:28px;display:flex;overflow-y:auto}.jLrgrW_title{color:var(--dsw-alias-label-primary);outline:none;margin:0;font-size:20px;font-weight:500;line-height:28px}.jLrgrW_body{margin-top:20px}@media (width<=560px){.jLrgrW_content{padding:24px}}";
2369
+ const css$2 = ".jLrgrW_dialog{width:min(600px,100%);max-height:100%;padding:0}.jLrgrW_content{box-sizing:border-box;flex-direction:column;min-height:0;padding:28px;display:flex;overflow-y:auto}.jLrgrW_title{color:var(--dsw-alias-label-primary);outline:none;margin:0;font-size:20px;font-weight:500;line-height:28px}.jLrgrW_body{margin-top:20px}@media (width<=560px){.jLrgrW_content{padding:24px}}";
2217
2370
  const tagId$2 = "@deepseek-ai/dsh-client-ui-settings-models/OnboardingModal.module.css";
2218
2371
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
2219
2372
  const tag = document.createElement("style");
@@ -2308,20 +2461,34 @@ window.__ModuleLoader__.load({
2308
2461
  * @returns the onboarding modal or null when onboarding needs no intervention.
2309
2462
  */
2310
2463
  function DeepSeekOnboardingDialog(props) {
2311
- const { complete, controller, useModels, operations, schema, t } = props;
2464
+ const { complete, controller, useModels, operations, schema, t, renderSlot, automatic, explicit = false } = props;
2465
+ const [apiKey, setApiKey] = (0, react.useState)(explicit);
2312
2466
  const state = useModels((snapshot) => snapshot);
2313
2467
  const readiness = onboardingReadiness(state);
2314
2468
  (0, react.useEffect)(() => {
2315
- if (state.status === "idle") controller.load();
2316
- }, [controller, state.status]);
2469
+ if ((automatic || explicit) && state.status === "idle") controller.load();
2470
+ }, [
2471
+ controller,
2472
+ state.status,
2473
+ automatic,
2474
+ explicit
2475
+ ]);
2317
2476
  (0, react.useEffect)(() => {
2318
- if (readiness.kind === "adapter-absent" || readiness.kind === "provider-ready" || readiness.kind === "unavailable") complete();
2319
- }, [complete, readiness.kind]);
2477
+ if (!automatic && !explicit || readiness.kind === "adapter-absent" || !explicit && readiness.kind === "provider-ready" || readiness.kind === "unavailable") complete();
2478
+ }, [
2479
+ complete,
2480
+ readiness.kind,
2481
+ explicit,
2482
+ automatic
2483
+ ]);
2484
+ if (!automatic && !explicit) return null;
2320
2485
  switch (readiness.kind) {
2321
2486
  case "loading":
2322
2487
  case "adapter-absent":
2323
- case "provider-ready":
2324
2488
  case "unavailable": return null;
2489
+ case "provider-ready":
2490
+ if (!explicit) return null;
2491
+ break;
2325
2492
  case "credential-missing": break;
2326
2493
  /* v8 ignore next -- every current readiness variant is handled above */
2327
2494
  default: return assertNever$1(readiness);
@@ -2337,7 +2504,7 @@ window.__ModuleLoader__.load({
2337
2504
  }
2338
2505
  controller.load();
2339
2506
  };
2340
- return (0, react_jsx_runtime.jsxs)(OnboardingModal, {
2507
+ const editor = (0, react_jsx_runtime.jsxs)(OnboardingModal, {
2341
2508
  title: t("onboardingTitle"),
2342
2509
  children: [(0, react_jsx_runtime.jsx)("p", {
2343
2510
  className: DeepSeekOnboardingDialog_module_css_default.description,
@@ -2364,6 +2531,12 @@ window.__ModuleLoader__.load({
2364
2531
  })
2365
2532
  })]
2366
2533
  });
2534
+ return apiKey ? editor : renderSlot("settings.models.sign-in", {
2535
+ complete,
2536
+ useApiKey: () => {
2537
+ setApiKey(true);
2538
+ }
2539
+ }, { fallback: editor });
2367
2540
  }
2368
2541
  //#endregion
2369
2542
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/WelcomeNotice.module.css.mjs
@@ -2441,7 +2614,7 @@ window.__ModuleLoader__.load({
2441
2614
  //#endregion
2442
2615
  //#region lib/types/onboarding-copy.js
2443
2616
  /** Durable settings namespace for product-wide GUI onboarding facts. */
2444
- const WELCOME_NOTICE_SETTINGS_NAMESPACE = "ui-onboarding";
2617
+ const WELCOME_NOTICE_SETTINGS_NAMESPACE = "ui-settings-general";
2445
2618
  /** Field storing the last welcome notice version the user acknowledged. */
2446
2619
  const WELCOME_NOTICE_ACK_FIELD = "welcomeNoticeVersion";
2447
2620
  /**
@@ -2457,16 +2630,6 @@ window.__ModuleLoader__.load({
2457
2630
  * remote browser's memory-mode scope never answers and the acknowledgement
2458
2631
  * stays process-local here.
2459
2632
  */
2460
- /**
2461
- * Accept any object section verbatim; a malformed durable value reads as an
2462
- * empty section, so the notice treats it as unacknowledged instead of leaving
2463
- * the scope stuck on its previous value.
2464
- * @param section - the wire section value.
2465
- * @returns the section object, or an empty one for non-object values.
2466
- */
2467
- function decodeWelcomeSection(section) {
2468
- return typeof section === "object" && section !== null && !Array.isArray(section) ? section : {};
2469
- }
2470
2633
  /* v8 ignore next 3 -- closed-union default only defends future source widening */
2471
2634
  function assertNever(_value) {
2472
2635
  throw new Error("unexpected welcome settings status");
@@ -2665,7 +2828,14 @@ window.__ModuleLoader__.load({
2665
2828
  deleteDescriptionWithCredential: "Deleting {provider} removes its configuration and stored API key.",
2666
2829
  deleteConfirm: "Delete {provider}",
2667
2830
  deleting: "Deleting {provider}…",
2668
- add: "Add provider",
2831
+ add: "Add model provider",
2832
+ addMode: "How to add",
2833
+ addCatalog: "Third-party model provider",
2834
+ addCustom: "Custom model API",
2835
+ addCatalogHint: "Pick OpenAI, Anthropic, Kimi, or another provider from the built-in catalog and enter its API key.",
2836
+ addCustomHint: "Connect a relay, a self-hosted server, or any other OpenAI- or Anthropic-compatible endpoint by its base URL, protocol, and models.",
2837
+ addCatalogExhausted: "Every catalog provider is already configured.",
2838
+ addCustomUnavailable: "No API protocol is available to declare.",
2669
2839
  provider: "Provider",
2670
2840
  close: "Close",
2671
2841
  cancel: "Cancel",
@@ -2686,9 +2856,8 @@ window.__ModuleLoader__.load({
2686
2856
  customized: "Customized settings",
2687
2857
  baseUrl: "Base URL",
2688
2858
  baseUrlDefault: "Provider default",
2689
- deepSeekChatBaseUrl: "https://api.deepseek.com",
2690
- deepSeekMessagesBaseUrl: "https://api.deepseek.com/anthropic",
2691
- deepSeekEndpointHint: "Use an endpoint compatible with the configured connection.",
2859
+ deepSeekBaseUrl: "https://api.deepseek.com/anthropic",
2860
+ deepSeekEndpointHint: "Use an API endpoint compatible with Anthropic Messages.",
2692
2861
  models: "Models",
2693
2862
  modelsInherited: "Using the adapter defaults",
2694
2863
  modelsCustomized: "Customized model catalog",
@@ -2701,7 +2870,10 @@ window.__ModuleLoader__.load({
2701
2870
  contextWindowPlaceholder: "Uses the provider default",
2702
2871
  maxTokens: "Max output tokens",
2703
2872
  maxTokensPlaceholder: "Uses the provider default",
2704
- modelAdvanced: "Capacities",
2873
+ modelAdvanced: "Model options",
2874
+ modelInputTypes: "Input types",
2875
+ modelInputText: "Text",
2876
+ modelInputImage: "Image",
2705
2877
  addModel: "Add model",
2706
2878
  removeModel: "Delete model",
2707
2879
  modelsEmpty: "No models will be shown in the selector. Unlisted IDs can still be sent directly.",
@@ -2713,11 +2885,9 @@ window.__ModuleLoader__.load({
2713
2885
  modelNameInvalid: "Display name cannot be empty.",
2714
2886
  modelContextInvalid: "Context window must be a positive count, like 131072, 256K, or 1M.",
2715
2887
  modelMaxTokensInvalid: "Max output tokens must be a positive count, like 8192, 64K, or 1M.",
2716
- advancedHint: "Other fields live in settings.yaml; edit that section directly.",
2888
+ advancedHint: "Other fields live in cordis.patch.yml; edit that section directly.",
2717
2889
  modelCapacityInvalid: "A capacity must be a number, optionally suffixed K or M.",
2718
2890
  modelDuplicate: "Each model ID may appear once.",
2719
- modelContextWindow: "Context window",
2720
- modelMaxTokens: "Max output tokens",
2721
2891
  fetchModels: "Fetch available models",
2722
2892
  fetching: "Asking the provider…",
2723
2893
  fetchNeedsBaseUrl: "Enter the base URL first, then fetch.",
@@ -2729,8 +2899,6 @@ window.__ModuleLoader__.load({
2729
2899
  fetchSelectAll: "Select all",
2730
2900
  fetchDeselectAll: "Deselect all",
2731
2901
  fetchAdopt: "Add selected",
2732
- customAdd: "Add a custom provider",
2733
- customTitle: "Custom provider",
2734
2902
  customTag: "Custom",
2735
2903
  customRoute: "Provider ID",
2736
2904
  customRouteHint: "Lowercase identifier, starting with a letter, that uniquely names this provider in requests and as its credential name.",
@@ -2739,10 +2907,14 @@ window.__ModuleLoader__.load({
2739
2907
  customDisplayName: "Display name",
2740
2908
  customApi: "API protocol",
2741
2909
  customApiUnset: "Not selected",
2910
+ protocolOpenAiCompletions: "OpenAI Chat Completions",
2911
+ protocolOpenAiResponses: "OpenAI Responses",
2912
+ protocolAnthropicMessages: "Anthropic Messages",
2742
2913
  customNeedsBaseUrl: "A custom provider needs a base URL.",
2743
2914
  customBaseUrlInvalid: "Enter a valid HTTP or HTTPS URL.",
2744
2915
  customNeedsModels: "A custom provider needs at least one model.",
2745
2916
  customBaseUrlPlaceholder: "https://gateway.example/v1",
2917
+ customAnthropicBaseUrlPlaceholder: "https://gateway.example",
2746
2918
  settingsPathUnresolvable: "unresolvable settings path",
2747
2919
  create: "Create provider",
2748
2920
  creating: "Creating…",
@@ -2761,7 +2933,7 @@ window.__ModuleLoader__.load({
2761
2933
  const zh = {
2762
2934
  nav: "模型",
2763
2935
  title: "模型",
2764
- intro: "填入各提供方的 API 密钥即可使用其模型。",
2936
+ intro: "填入各提供商的 API 密钥即可使用其模型。",
2765
2937
  edit: "编辑",
2766
2938
  editProvider: "编辑 {provider}",
2767
2939
  remove: "删除",
@@ -2771,8 +2943,15 @@ window.__ModuleLoader__.load({
2771
2943
  deleteDescriptionWithCredential: "删除 {provider} 会移除其配置和存储的 API 密钥。",
2772
2944
  deleteConfirm: "删除 {provider}",
2773
2945
  deleting: "正在删除 {provider}…",
2774
- add: "添加提供方",
2775
- provider: "提供方",
2946
+ add: "添加模型提供商",
2947
+ addMode: "添加方式",
2948
+ addCatalog: "第三方模型提供商",
2949
+ addCustom: "自定义模型 API",
2950
+ addCatalogHint: "从内置目录中选择 OpenAI、Anthropic、Kimi 等提供商,填入其 API 密钥即可使用。",
2951
+ addCustomHint: "连接中转站、自部署服务或其他兼容 OpenAI / Anthropic 协议的接口,需填写 API 地址、协议和模型。",
2952
+ addCatalogExhausted: "目录中的提供商都已添加。",
2953
+ addCustomUnavailable: "没有可用的 API 协议。",
2954
+ provider: "提供商",
2776
2955
  close: "关闭",
2777
2956
  cancel: "取消",
2778
2957
  apply: "保存",
@@ -2781,7 +2960,7 @@ window.__ModuleLoader__.load({
2781
2960
  credentialConfigured: "API 密钥已配置",
2782
2961
  credentialMissing: "API 密钥缺失",
2783
2962
  readOnly: "当前部署的设置文档为只读。",
2784
- loadFailed: "加载提供方目录失败",
2963
+ loadFailed: "加载提供商目录失败",
2785
2964
  conflict: "这张卡片打开期间,这些设置已被其他地方改动。请关闭后重新打开,在当前值上编辑。",
2786
2965
  retry: "重试",
2787
2966
  keyInput: "API 密钥",
@@ -2791,10 +2970,9 @@ window.__ModuleLoader__.load({
2791
2970
  keyEnvLocked: "由启动环境提供(只读)",
2792
2971
  customized: "自定义设置",
2793
2972
  baseUrl: "API 地址",
2794
- baseUrlDefault: "提供方默认",
2795
- deepSeekChatBaseUrl: "https://api.deepseek.com",
2796
- deepSeekMessagesBaseUrl: "https://api.deepseek.com/anthropic",
2797
- deepSeekEndpointHint: "请填写与当前连接配置兼容的 API 地址。",
2973
+ baseUrlDefault: "提供商默认",
2974
+ deepSeekBaseUrl: "https://api.deepseek.com/anthropic",
2975
+ deepSeekEndpointHint: "请填写兼容 Anthropic Messages 协议的 API 地址。",
2798
2976
  models: "模型目录",
2799
2977
  modelsInherited: "正在使用适配器默认模型",
2800
2978
  modelsCustomized: "已自定义模型目录",
@@ -2804,53 +2982,56 @@ window.__ModuleLoader__.load({
2804
2982
  modelName: "显示名称",
2805
2983
  modelNamePlaceholder: "留空时使用模型 ID",
2806
2984
  contextWindow: "上下文窗口",
2807
- contextWindowPlaceholder: "使用提供方默认值",
2985
+ contextWindowPlaceholder: "使用提供商默认值",
2808
2986
  maxTokens: "最大输出 token 数",
2809
- maxTokensPlaceholder: "使用提供方默认值",
2810
- modelAdvanced: "容量",
2987
+ maxTokensPlaceholder: "使用提供商默认值",
2988
+ modelAdvanced: "模型选项",
2989
+ modelInputTypes: "输入类型",
2990
+ modelInputText: "文本",
2991
+ modelInputImage: "图片",
2811
2992
  addModel: "添加模型",
2812
2993
  removeModel: "删除模型",
2813
2994
  modelsEmpty: "模型选择器中将不显示任何模型;目录外 ID 仍可直接发送。",
2814
2995
  keyBlank: "请输入 API 密钥;留空则保持已存储的密钥。",
2815
- keyBlankNew: "请输入 API 密钥;若该提供方以其他方式鉴权,可以留空。",
2996
+ keyBlankNew: "请输入 API 密钥;若该提供商以其他方式鉴权,可以留空。",
2816
2997
  keyIllegalCharacters: "该 API 密钥格式错误,请检查。",
2817
2998
  modelIdRequired: "模型 ID 不能为空。",
2818
2999
  modelIdDuplicate: "模型 ID 不能重复。",
2819
3000
  modelNameInvalid: "显示名称不能为空。",
2820
3001
  modelContextInvalid: "上下文窗口必须是正数,例如 131072、256K 或 1M。",
2821
3002
  modelMaxTokensInvalid: "最大输出 token 数必须是正数,例如 8192、64K 或 1M。",
2822
- advancedHint: "其余字段在 settings.yaml 中,请直接编辑对应段。",
3003
+ advancedHint: "其余字段在 cordis.patch.yml 中,请直接编辑对应段。",
2823
3004
  modelCapacityInvalid: "容量需为数字,可加 K 或 M 后缀。",
2824
3005
  modelDuplicate: "每个模型 ID 只能出现一次。",
2825
- modelContextWindow: "上下文窗口",
2826
- modelMaxTokens: "最大输出 token",
2827
3006
  fetchModels: "获取可用模型",
2828
- fetching: "正在询问提供方…",
3007
+ fetching: "正在询问提供商…",
2829
3008
  fetchNeedsBaseUrl: "请先填写 API 地址,再获取。",
2830
- fetchEmpty: "该提供方没有列出任何模型,请手动添加。",
3009
+ fetchEmpty: "该提供商没有列出任何模型,请手动添加。",
2831
3010
  fetchTitle: "选择要添加的模型",
2832
- fetchDescription: "以下是模型提供方的可用模型,勾选要添加的模型。",
3011
+ fetchDescription: "以下是模型提供商的可用模型,勾选要添加的模型。",
2833
3012
  fetchSearch: "搜索模型",
2834
3013
  fetchNoMatches: "没有匹配的模型。",
2835
3014
  fetchSelectAll: "全选",
2836
3015
  fetchDeselectAll: "取消全选",
2837
3016
  fetchAdopt: "添加所选",
2838
- customAdd: "添加自定义提供方",
2839
- customTitle: "自定义提供方",
2840
3017
  customTag: "自定义",
2841
3018
  customRoute: "Provider ID",
2842
- customRouteHint: "以小写字母开头的标识,在请求中唯一标识该提供方,并用于派生凭据名。",
3019
+ customRouteHint: "以小写字母开头的标识,在请求中唯一标识该提供商,并用于派生凭据名。",
2843
3020
  customRouteInvalid: "需以小写字母开头,之后可用小写字母、数字和短横线。",
2844
- customRouteTaken: "已有提供方使用了这个 ID。",
3021
+ customRouteTaken: "已有提供商使用了这个 ID。",
2845
3022
  customDisplayName: "显示名称",
2846
3023
  customApi: "API 协议",
2847
3024
  customApiUnset: "未选择",
2848
- customNeedsBaseUrl: "自定义提供方需要填写 API 地址。",
3025
+ protocolOpenAiCompletions: "OpenAI Chat Completions",
3026
+ protocolOpenAiResponses: "OpenAI Responses",
3027
+ protocolAnthropicMessages: "Anthropic Messages",
3028
+ customNeedsBaseUrl: "自定义模型 API 需要填写 API 地址。",
2849
3029
  customBaseUrlInvalid: "请输入有效的 HTTP 或 HTTPS 地址。",
2850
- customNeedsModels: "自定义提供方至少需要一个模型。",
3030
+ customNeedsModels: "自定义模型 API 至少需要一个模型。",
2851
3031
  customBaseUrlPlaceholder: "https://gateway.example/v1",
3032
+ customAnthropicBaseUrlPlaceholder: "https://gateway.example",
2852
3033
  settingsPathUnresolvable: "无法解析设置路径",
2853
- create: "创建提供方",
3034
+ create: "创建提供商",
2854
3035
  creating: "创建中…",
2855
3036
  welcomeTitle: "内测声明",
2856
3037
  welcomeBody: "DeepSeek Harness 目前的 0.1 版本仍处在面向 Harness 开发者进行测试的阶段,还有许多地方需要持续改进和打磨,希望听取广大开发者的反馈建议。预计 DeepSeek Harness 的核心插件以及基础 API 都会在接下来的一段时间内快速迭代、持续演化。\n\n我们期待与全球开发者一起,在开源、开放、可复用、可组合的基础设施之上,共同探索智能上限。欢迎全球 Harness 开发者加入 DSH 插件生态。",
@@ -2864,6 +3045,904 @@ window.__ModuleLoader__.load({
2864
3045
  keyRequired: "请输入 API 密钥后继续。"
2865
3046
  };
2866
3047
  //#endregion
3048
+ //#region ../../../vendor/cosmokit/src/misc.ts
3049
+ /** Return true when a value is `null` or `undefined`. */
3050
+ function isNullable(value) {
3051
+ return value === null || value === void 0;
3052
+ }
3053
+ /** Return true for non-array object values. */
3054
+ function isPlainObject(data) {
3055
+ return data && typeof data === "object" && !Array.isArray(data);
3056
+ }
3057
+ /** Filter object entries and return a new object. */
3058
+ function filterKeys(object, filter) {
3059
+ return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
3060
+ }
3061
+ /** Map object values while preserving the original key set. */
3062
+ function mapValues(object, transform) {
3063
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
3064
+ }
3065
+ /** Pick selected keys from an object, optionally including `undefined` values. */
3066
+ function pick(source, keys, forced) {
3067
+ if (!keys) return { ...source };
3068
+ const result = {};
3069
+ for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
3070
+ return result;
3071
+ }
3072
+ //#endregion
3073
+ //#region ../../../vendor/cosmokit/src/volatile.ts
3074
+ /** Shared config references used by schema validators and plugin runtimes. */
3075
+ const write = Symbol.for("cosmokit.volatile.write");
3076
+ function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
3077
+ if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
3078
+ if (value === null || typeof value !== "object") return value;
3079
+ if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
3080
+ ancestors.add(value);
3081
+ try {
3082
+ if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
3083
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
3084
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
3085
+ } finally {
3086
+ ancestors.delete(value);
3087
+ }
3088
+ }
3089
+ /**
3090
+ * Create a detached reference containing an immutable copy of the supplied data.
3091
+ * @param value - validated config data; class instances and functions are unsupported.
3092
+ * @returns a reference whose value is updated only by its owning runtime.
3093
+ */
3094
+ function createVolatile(value) {
3095
+ let current = snapshot(value);
3096
+ return Object.freeze({
3097
+ get: () => current,
3098
+ [write]: (value) => {
3099
+ current = value;
3100
+ }
3101
+ });
3102
+ }
3103
+ /**
3104
+ * Identify references across ESM/CJS copies of the shared library.
3105
+ * @param value - a parsed config value.
3106
+ * @returns whether the value implements the shared reference protocol.
3107
+ */
3108
+ function isVolatile(value) {
3109
+ return typeof value === "object" && value !== null && write in value;
3110
+ }
3111
+ //#endregion
3112
+ //#region ../../../vendor/cosmokit/src/types.ts
3113
+ /** Test values using `instanceof` with a `toStringTag` fallback. */
3114
+ function is(type, value) {
3115
+ if (arguments.length === 1) return (value) => is(type, value);
3116
+ return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
3117
+ }
3118
+ function isArrayBufferLike(value) {
3119
+ return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
3120
+ }
3121
+ function isArrayBufferSource(value) {
3122
+ return isArrayBufferLike(value) || ArrayBuffer.isView(value);
3123
+ }
3124
+ let Binary;
3125
+ (function(_Binary) {
3126
+ _Binary.is = isArrayBufferLike;
3127
+ _Binary.isSource = isArrayBufferSource;
3128
+ function fromSource(source) {
3129
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
3130
+ else return source;
3131
+ }
3132
+ _Binary.fromSource = fromSource;
3133
+ function toBase64(source) {
3134
+ source = fromSource(source);
3135
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
3136
+ let binary = "";
3137
+ const bytes = new Uint8Array(source);
3138
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
3139
+ return btoa(binary);
3140
+ }
3141
+ _Binary.toBase64 = toBase64;
3142
+ function fromBase64(source) {
3143
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
3144
+ return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
3145
+ }
3146
+ _Binary.fromBase64 = fromBase64;
3147
+ function toHex(source) {
3148
+ source = fromSource(source);
3149
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
3150
+ return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
3151
+ }
3152
+ _Binary.toHex = toHex;
3153
+ function fromHex(source) {
3154
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
3155
+ const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
3156
+ const buffer = [];
3157
+ for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
3158
+ return Uint8Array.from(buffer).buffer;
3159
+ }
3160
+ _Binary.fromHex = fromHex;
3161
+ })(Binary || (Binary = {}));
3162
+ Binary.fromBase64;
3163
+ Binary.toBase64;
3164
+ Binary.fromHex;
3165
+ Binary.toHex;
3166
+ /** Deep-clone common JavaScript values while preserving prototypes and cycles. */
3167
+ function clone(source, refs = /* @__PURE__ */ new Map()) {
3168
+ if (!source || typeof source !== "object") return source;
3169
+ if (is("Date", source)) return new Date(source.valueOf());
3170
+ if (is("RegExp", source)) return new RegExp(source.source, source.flags);
3171
+ if (isArrayBufferLike(source)) return source.slice(0);
3172
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
3173
+ const cached = refs.get(source);
3174
+ if (cached) return cached;
3175
+ if (Array.isArray(source)) {
3176
+ const result = [];
3177
+ refs.set(source, result);
3178
+ source.forEach((value, index) => {
3179
+ result[index] = Reflect.apply(clone, null, [value, refs]);
3180
+ });
3181
+ return result;
3182
+ }
3183
+ const result = Object.create(Object.getPrototypeOf(source));
3184
+ refs.set(source, result);
3185
+ for (const key of Reflect.ownKeys(source)) {
3186
+ const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
3187
+ if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
3188
+ Reflect.defineProperty(result, key, descriptor);
3189
+ }
3190
+ return result;
3191
+ }
3192
+ /**
3193
+ * Compare values recursively, treating two volatile references as equal regardless of value.
3194
+ * Strict comparison distinguishes null/undefined, treats opaque objects by identity,
3195
+ * compares URLs by normalized href, treats array holes as undefined, and considers distinct cyclic structures unequal.
3196
+ * @param a - first value.
3197
+ * @param b - second value.
3198
+ * @param strict - whether to require strict data equality outside volatile references.
3199
+ * @returns whether the values compare equal.
3200
+ */
3201
+ function deepEqual(a, b, strict) {
3202
+ const ancestors = /* @__PURE__ */ new Set();
3203
+ function compare(a, b) {
3204
+ if (a === b) return true;
3205
+ if (isVolatile(a) || isVolatile(b)) return isVolatile(a) && isVolatile(b);
3206
+ if (!strict && isNullable(a) && isNullable(b)) return true;
3207
+ if (typeof a !== typeof b || typeof a !== "object" || !a || !b) return false;
3208
+ if (ancestors.has(a)) return false;
3209
+ function check(test, then) {
3210
+ return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
3211
+ }
3212
+ ancestors.add(a);
3213
+ try {
3214
+ return check(Array.isArray, (a, b) => {
3215
+ if (a.length !== b.length) return false;
3216
+ for (let index = 0; index < a.length; index++) if (!compare(a[index], b[index])) return false;
3217
+ return true;
3218
+ }) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("URL"), (a, b) => a.href === b.href) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
3219
+ if (a.byteLength !== b.byteLength) return false;
3220
+ const viewA = new Uint8Array(a);
3221
+ const viewB = new Uint8Array(b);
3222
+ for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
3223
+ return true;
3224
+ }) ?? ((!strict || [a, b].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
3225
+ ...a,
3226
+ ...b
3227
+ }).every((key) => compare(a[key], b[key])));
3228
+ } finally {
3229
+ ancestors.delete(a);
3230
+ }
3231
+ }
3232
+ return compare(a, b);
3233
+ }
3234
+ //#endregion
3235
+ //#region ../../../vendor/cosmokit/src/time.ts
3236
+ let Time;
3237
+ (function(_Time) {
3238
+ _Time.millisecond = 1;
3239
+ const second = _Time.second = 1e3;
3240
+ const minute = _Time.minute = second * 60;
3241
+ const hour = _Time.hour = minute * 60;
3242
+ const day = _Time.day = hour * 24;
3243
+ const week = _Time.week = day * 7;
3244
+ let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
3245
+ function setTimezoneOffset(offset) {
3246
+ timezoneOffset = offset;
3247
+ }
3248
+ _Time.setTimezoneOffset = setTimezoneOffset;
3249
+ function getTimezoneOffset() {
3250
+ return timezoneOffset;
3251
+ }
3252
+ _Time.getTimezoneOffset = getTimezoneOffset;
3253
+ function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
3254
+ if (typeof date === "number") date = new Date(date);
3255
+ if (offset === void 0) offset = timezoneOffset;
3256
+ return Math.floor((date.valueOf() / minute - offset) / 1440);
3257
+ }
3258
+ _Time.getDateNumber = getDateNumber;
3259
+ function fromDateNumber(value, offset) {
3260
+ const date = new Date(value * day);
3261
+ if (offset === void 0) offset = timezoneOffset;
3262
+ return new Date(+date + offset * minute);
3263
+ }
3264
+ _Time.fromDateNumber = fromDateNumber;
3265
+ const numeric = /\d+(?:\.\d+)?/.source;
3266
+ const timeRegExp = new RegExp(`^${[
3267
+ "w(?:eek(?:s)?)?",
3268
+ "d(?:ay(?:s)?)?",
3269
+ "h(?:our(?:s)?)?",
3270
+ "m(?:in(?:ute)?(?:s)?)?",
3271
+ "s(?:ec(?:ond)?(?:s)?)?"
3272
+ ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
3273
+ function parseTime(source) {
3274
+ const capture = timeRegExp.exec(source);
3275
+ if (!capture) return 0;
3276
+ return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
3277
+ }
3278
+ _Time.parseTime = parseTime;
3279
+ function parseDate(date) {
3280
+ const parsed = parseTime(date);
3281
+ if (parsed) date = Date.now() + parsed;
3282
+ else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
3283
+ else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
3284
+ return date ? new Date(date) : /* @__PURE__ */ new Date();
3285
+ }
3286
+ _Time.parseDate = parseDate;
3287
+ function format(ms) {
3288
+ const abs = Math.abs(ms);
3289
+ if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
3290
+ else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
3291
+ else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
3292
+ else if (abs >= second) return Math.round(ms / second) + "s";
3293
+ return ms + "ms";
3294
+ }
3295
+ _Time.format = format;
3296
+ function toDigits(source, length = 2) {
3297
+ return source.toString().padStart(length, "0");
3298
+ }
3299
+ _Time.toDigits = toDigits;
3300
+ function template(template, time = /* @__PURE__ */ new Date()) {
3301
+ return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
3302
+ }
3303
+ _Time.template = template;
3304
+ })(Time || (Time = {}));
3305
+ //#endregion
3306
+ //#region ../../../vendor/schemastery/src/index.ts
3307
+ const kSchema = Symbol.for("schemastery");
3308
+ const kValidationError = Symbol.for("ValidationError");
3309
+ globalThis.__schemastery_index__ ??= 0;
3310
+ globalThis.__schemastery_refs__ = void 0;
3311
+ var ValidationError = class extends TypeError {
3312
+ options;
3313
+ name = "ValidationError";
3314
+ constructor(message, options) {
3315
+ let prefix = "$";
3316
+ for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
3317
+ else if (typeof segment === "number") prefix += "[" + segment + "]";
3318
+ else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
3319
+ if (prefix.startsWith(".")) prefix = prefix.slice(1);
3320
+ super((prefix === "$" ? "" : `${prefix} `) + message);
3321
+ this.options = options;
3322
+ }
3323
+ static is(error) {
3324
+ return !!error?.[kValidationError];
3325
+ }
3326
+ };
3327
+ Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
3328
+ const Schema = function(options) {
3329
+ const schema = function(data, options = {}) {
3330
+ return Schema.resolve(data, schema, options)[0];
3331
+ };
3332
+ if (options.refs) {
3333
+ const refs = mapValues(options.refs, (options) => new Schema(options));
3334
+ const getRef = (uid) => refs[uid];
3335
+ for (const key in refs) {
3336
+ const options = refs[key];
3337
+ options.sKey = getRef(options.sKey);
3338
+ options.inner = getRef(options.inner);
3339
+ options.list = options.list && options.list.map(getRef);
3340
+ options.dict = options.dict && mapValues(options.dict, getRef);
3341
+ }
3342
+ return refs[options.uid];
3343
+ }
3344
+ Object.assign(schema, options);
3345
+ if (typeof schema.callback === "string") try {
3346
+ schema.callback = new Function("return " + schema.callback)();
3347
+ } catch {}
3348
+ Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
3349
+ Object.setPrototypeOf(schema, Schema.prototype);
3350
+ schema.meta ||= {};
3351
+ schema.toString = schema.toString.bind(schema);
3352
+ return schema;
3353
+ };
3354
+ Schema.prototype = Object.create(Function.prototype);
3355
+ Schema.prototype[kSchema] = true;
3356
+ Object.defineProperty(Schema.prototype, "~standard", { get() {
3357
+ return {
3358
+ version: 1,
3359
+ vendor: "schemastery",
3360
+ validate: (value) => {
3361
+ try {
3362
+ return { value: Schema.resolve(value, this, {})[0] };
3363
+ } catch (error) {
3364
+ if (ValidationError.is(error)) return { issues: [{
3365
+ message: error.message,
3366
+ path: error.options.path
3367
+ }] };
3368
+ throw error;
3369
+ }
3370
+ }
3371
+ };
3372
+ } });
3373
+ Schema.ValidationError = ValidationError;
3374
+ Schema.prototype.toJSON = function toJSON() {
3375
+ if (globalThis.__schemastery_refs__) {
3376
+ globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
3377
+ return this.uid;
3378
+ }
3379
+ globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
3380
+ globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
3381
+ const result = {
3382
+ uid: this.uid,
3383
+ refs: globalThis.__schemastery_refs__
3384
+ };
3385
+ globalThis.__schemastery_refs__ = void 0;
3386
+ return result;
3387
+ };
3388
+ Schema.prototype.set = function set(key, value) {
3389
+ this.dict[key] = value;
3390
+ return this;
3391
+ };
3392
+ Schema.prototype.push = function push(value) {
3393
+ this.list.push(value);
3394
+ return this;
3395
+ };
3396
+ function mergeDesc(original, messages) {
3397
+ const result = typeof original === "string" ? { "": original } : { ...original };
3398
+ for (const locale in messages) {
3399
+ const value = messages[locale];
3400
+ if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
3401
+ else if (typeof value === "string") result[locale] = value;
3402
+ }
3403
+ return result;
3404
+ }
3405
+ function getInner(value) {
3406
+ return value?.$value ?? value?.$inner;
3407
+ }
3408
+ function extractKeys(data) {
3409
+ return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
3410
+ }
3411
+ Schema.prototype.i18n = function i18n(messages) {
3412
+ const schema = Schema(this);
3413
+ const desc = mergeDesc(schema.meta.description, messages);
3414
+ if (Object.keys(desc).length) schema.meta.description = desc;
3415
+ if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
3416
+ return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
3417
+ });
3418
+ if (schema.list) schema.list = schema.list.map((inner, index) => {
3419
+ return inner.i18n(mapValues(messages, (data = {}) => {
3420
+ if (Array.isArray(getInner(data))) return getInner(data)[index];
3421
+ if (Array.isArray(data)) return data[index];
3422
+ return extractKeys(data);
3423
+ }));
3424
+ });
3425
+ if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
3426
+ if (getInner(data)) return getInner(data);
3427
+ return extractKeys(data);
3428
+ }));
3429
+ if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
3430
+ return schema;
3431
+ };
3432
+ Schema.prototype.extra = function extra(key, value) {
3433
+ const schema = Schema(this);
3434
+ schema.meta = {
3435
+ ...schema.meta,
3436
+ [key]: value
3437
+ };
3438
+ return schema;
3439
+ };
3440
+ for (const key of [
3441
+ "required",
3442
+ "disabled",
3443
+ "collapse",
3444
+ "hidden",
3445
+ "loose"
3446
+ ]) Object.assign(Schema.prototype, { [key](value = true) {
3447
+ const schema = Schema(this);
3448
+ schema.meta = {
3449
+ ...schema.meta,
3450
+ [key]: value
3451
+ };
3452
+ return schema;
3453
+ } });
3454
+ Schema.prototype.deprecated = function deprecated() {
3455
+ const schema = Schema(this);
3456
+ schema.meta.badges ||= [];
3457
+ schema.meta.badges.push({
3458
+ text: "deprecated",
3459
+ type: "danger"
3460
+ });
3461
+ return schema;
3462
+ };
3463
+ Schema.prototype.experimental = function experimental() {
3464
+ const schema = Schema(this);
3465
+ schema.meta.badges ||= [];
3466
+ schema.meta.badges.push({
3467
+ text: "experimental",
3468
+ type: "warning"
3469
+ });
3470
+ return schema;
3471
+ };
3472
+ Schema.prototype.pattern = function pattern(regexp) {
3473
+ const schema = Schema(this);
3474
+ const pattern = pick(regexp, ["source", "flags"]);
3475
+ schema.meta = {
3476
+ ...schema.meta,
3477
+ pattern
3478
+ };
3479
+ return schema;
3480
+ };
3481
+ Schema.prototype.simplify = function simplify(value) {
3482
+ if (isVolatile(value)) value = value.get();
3483
+ if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
3484
+ if (isNullable(value)) return value;
3485
+ if (this.type === "object" || this.type === "dict") {
3486
+ const result = {};
3487
+ for (const key in value) {
3488
+ const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
3489
+ if (this.type === "dict" || !isNullable(item)) result[key] = item;
3490
+ }
3491
+ if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
3492
+ return result;
3493
+ } else if (this.type === "array" || this.type === "tuple") {
3494
+ const result = [];
3495
+ value.forEach((value, index) => {
3496
+ const schema = this.type === "array" ? this.inner : this.list[index];
3497
+ const item = schema ? schema.simplify(value) : value;
3498
+ result.push(item);
3499
+ });
3500
+ return result;
3501
+ } else if (this.type === "intersect") {
3502
+ const result = {};
3503
+ for (const item of this.list) Object.assign(result, item.simplify(value));
3504
+ return result;
3505
+ } else if (this.type === "union") for (const schema of this.list) try {
3506
+ Schema.resolve(value, schema, {});
3507
+ return schema.simplify(value);
3508
+ } catch {}
3509
+ return value;
3510
+ };
3511
+ Schema.prototype.toString = function toString(inline) {
3512
+ return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
3513
+ };
3514
+ Schema.prototype.role = function role(role, extra) {
3515
+ const schema = Schema(this);
3516
+ schema.meta = {
3517
+ ...schema.meta,
3518
+ role,
3519
+ extra
3520
+ };
3521
+ return schema;
3522
+ };
3523
+ for (const key of [
3524
+ "default",
3525
+ "link",
3526
+ "comment",
3527
+ "description",
3528
+ "max",
3529
+ "min",
3530
+ "step"
3531
+ ]) Object.assign(Schema.prototype, { [key](value) {
3532
+ const schema = Schema(this);
3533
+ schema.meta = {
3534
+ ...schema.meta,
3535
+ [key]: value
3536
+ };
3537
+ return schema;
3538
+ } });
3539
+ Schema.prototype.volatile = function volatile() {
3540
+ if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
3541
+ return this.extra("volatile", true);
3542
+ };
3543
+ const resolvers = {};
3544
+ const checkedVolatile = Symbol("checked-volatile-schema");
3545
+ function validateVolatileSchema(schema, path = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
3546
+ const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
3547
+ if (states.has(blocked)) return;
3548
+ states.add(blocked);
3549
+ seen.set(schema, states);
3550
+ if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path });
3551
+ const nested = blocked || !!schema.meta?.volatile;
3552
+ if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path, key], nested, seen);
3553
+ if (schema.sKey) validateVolatileSchema(schema.sKey, [...path, "<key>"], true, seen);
3554
+ if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path, "*"], true, seen);
3555
+ if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path, String(index)], true, seen);
3556
+ }
3557
+ Schema.extend = function extend(type, resolve) {
3558
+ resolvers[type] = resolve;
3559
+ };
3560
+ Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
3561
+ if (!schema) return [data];
3562
+ if (!options[checkedVolatile]) {
3563
+ validateVolatileSchema(schema, options.path);
3564
+ options = {
3565
+ ...options,
3566
+ [checkedVolatile]: true
3567
+ };
3568
+ }
3569
+ if (schema.meta?.volatile) {
3570
+ const inner = Schema(schema);
3571
+ inner.meta = {
3572
+ ...schema.meta,
3573
+ volatile: false
3574
+ };
3575
+ const [value, adapted] = Schema.resolve(data, inner, options, strict);
3576
+ try {
3577
+ return [createVolatile(value), adapted];
3578
+ } catch (error) {
3579
+ throw new ValidationError(error instanceof Error ? error.message : String(error), options);
3580
+ }
3581
+ }
3582
+ if (options.ignore?.(data, schema)) return [data];
3583
+ if (isNullable(data) && schema.type !== "lazy") {
3584
+ if (schema.meta.required) throw new ValidationError(`missing required value`, options);
3585
+ let current = schema;
3586
+ let fallback = schema.meta.default;
3587
+ while (current?.type === "intersect" && isNullable(fallback)) {
3588
+ current = current.list[0];
3589
+ fallback = current?.meta.default;
3590
+ }
3591
+ if (isNullable(fallback)) return [data];
3592
+ data = clone(fallback);
3593
+ }
3594
+ const callback = resolvers[schema.type];
3595
+ if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
3596
+ try {
3597
+ return callback(data, schema, options, strict);
3598
+ } catch (error) {
3599
+ if (!schema.meta.loose) throw error;
3600
+ return [schema.meta.default];
3601
+ }
3602
+ };
3603
+ Schema.from = function from(source) {
3604
+ if (isNullable(source)) return Schema.any();
3605
+ else if ([
3606
+ "string",
3607
+ "number",
3608
+ "boolean"
3609
+ ].includes(typeof source)) return Schema.const(source).required();
3610
+ else if (source[kSchema]) return source;
3611
+ else if (typeof source === "function") switch (source) {
3612
+ case String: return Schema.string().required();
3613
+ case Number: return Schema.number().required();
3614
+ case Boolean: return Schema.boolean().required();
3615
+ case Function: return Schema.function().required();
3616
+ default: return Schema.is(source).required();
3617
+ }
3618
+ else throw new TypeError(`cannot infer schema from ${source}`);
3619
+ };
3620
+ Schema.lazy = function lazy(builder) {
3621
+ const toJSON = () => {
3622
+ if (!schema.inner[kSchema]) {
3623
+ schema.inner = schema.builder();
3624
+ schema.inner.meta = {
3625
+ ...schema.meta,
3626
+ ...schema.inner.meta
3627
+ };
3628
+ }
3629
+ return schema.inner.toJSON();
3630
+ };
3631
+ const schema = new Schema({
3632
+ type: "lazy",
3633
+ builder,
3634
+ inner: { toJSON }
3635
+ });
3636
+ return schema;
3637
+ };
3638
+ Schema.natural = function natural() {
3639
+ return Schema.number().step(1).min(0);
3640
+ };
3641
+ Schema.percent = function percent() {
3642
+ return Schema.number().step(.01).min(0).max(1).role("slider");
3643
+ };
3644
+ Schema.date = function date() {
3645
+ return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
3646
+ const date = new Date(value);
3647
+ if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
3648
+ return date;
3649
+ }, true)]);
3650
+ };
3651
+ Schema.regExp = function regExp(flag = "") {
3652
+ return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
3653
+ try {
3654
+ return new RegExp(value, flag);
3655
+ } catch (e) {
3656
+ throw new ValidationError(e.message, options);
3657
+ }
3658
+ }, true)]);
3659
+ };
3660
+ Schema.arrayBuffer = function arrayBuffer(encoding) {
3661
+ return Schema.union([
3662
+ Schema.is(ArrayBuffer),
3663
+ Schema.is(SharedArrayBuffer),
3664
+ Schema.transform(Schema.any(), (value, options) => {
3665
+ if (Binary.isSource(value)) return Binary.fromSource(value);
3666
+ throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
3667
+ }, true),
3668
+ ...encoding ? [Schema.transform(Schema.string(), (value, options) => {
3669
+ try {
3670
+ return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
3671
+ } catch (e) {
3672
+ throw new ValidationError(e.message, options);
3673
+ }
3674
+ }, true)] : []
3675
+ ]);
3676
+ };
3677
+ Schema.extend("lazy", (data, schema, options, strict) => {
3678
+ if (!schema.inner[kSchema]) {
3679
+ schema.inner = schema.builder();
3680
+ schema.inner.meta = {
3681
+ ...schema.meta,
3682
+ ...schema.inner.meta
3683
+ };
3684
+ validateVolatileSchema(schema.inner, options.path, true);
3685
+ }
3686
+ return Schema.resolve(data, schema.inner, options, strict);
3687
+ });
3688
+ Schema.extend("any", (data) => {
3689
+ return [data];
3690
+ });
3691
+ Schema.extend("never", (data, _, options) => {
3692
+ throw new ValidationError(`expected nullable but got ${data}`, options);
3693
+ });
3694
+ Schema.extend("const", (data, { value }, options) => {
3695
+ if (deepEqual(data, value)) return [value];
3696
+ throw new ValidationError(`expected ${value} but got ${data}`, options);
3697
+ });
3698
+ function checkWithinRange(data, meta, description, options, skipMin = false) {
3699
+ const { max = Infinity, min = -Infinity } = meta;
3700
+ if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
3701
+ if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
3702
+ }
3703
+ Schema.extend("string", (data, { meta }, options) => {
3704
+ if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
3705
+ if (meta.pattern) {
3706
+ const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
3707
+ if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
3708
+ }
3709
+ checkWithinRange(data.length, meta, "string length", options);
3710
+ return [data];
3711
+ });
3712
+ function decimalShift(data, digits) {
3713
+ const str = data.toString();
3714
+ if (str.includes("e")) return data * Math.pow(10, digits);
3715
+ const index = str.indexOf(".");
3716
+ if (index === -1) return data * Math.pow(10, digits);
3717
+ const frac = str.slice(index + 1);
3718
+ const integer = str.slice(0, index);
3719
+ if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
3720
+ return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
3721
+ }
3722
+ function isMultipleOf(data, min, step) {
3723
+ step = Math.abs(step);
3724
+ if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
3725
+ const index = step.toString().indexOf(".");
3726
+ const digits = step.toString().slice(index + 1).length;
3727
+ return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
3728
+ }
3729
+ Schema.extend("number", (data, { meta }, options) => {
3730
+ if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
3731
+ checkWithinRange(data, meta, "number", options);
3732
+ const { step } = meta;
3733
+ if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
3734
+ return [data];
3735
+ });
3736
+ Schema.extend("boolean", (data, _, options) => {
3737
+ if (typeof data === "boolean") return [data];
3738
+ throw new ValidationError(`expected boolean but got ${data}`, options);
3739
+ });
3740
+ Schema.extend("bitset", (data, { bits, meta }, options) => {
3741
+ let value = 0, keys = [];
3742
+ if (typeof data === "number") {
3743
+ value = data;
3744
+ for (const key in bits) if (data & bits[key]) keys.push(key);
3745
+ } else if (Array.isArray(data)) {
3746
+ keys = data;
3747
+ for (const key of keys) {
3748
+ if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
3749
+ if (key in bits) value |= bits[key];
3750
+ }
3751
+ } else throw new ValidationError(`expected number or array but got ${data}`, options);
3752
+ if (value === meta.default) return [value];
3753
+ return [value, keys];
3754
+ });
3755
+ Schema.extend("function", (data, _, options) => {
3756
+ if (typeof data === "function") return [data];
3757
+ throw new ValidationError(`expected function but got ${data}`, options);
3758
+ });
3759
+ Schema.extend("is", (data, { constructor }, options) => {
3760
+ if (typeof constructor === "function") {
3761
+ if (data instanceof constructor) return [data];
3762
+ throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
3763
+ } else {
3764
+ if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
3765
+ let prototype = Object.getPrototypeOf(data);
3766
+ while (prototype) {
3767
+ if (prototype.constructor?.name === constructor) return [data];
3768
+ prototype = Object.getPrototypeOf(prototype);
3769
+ }
3770
+ throw new ValidationError(`expected ${constructor} but got ${data}`, options);
3771
+ }
3772
+ });
3773
+ function property(data, key, schema, options) {
3774
+ try {
3775
+ const [value, adapted] = Schema.resolve(data[key], schema, {
3776
+ ...options,
3777
+ path: [...options.path || [], key]
3778
+ });
3779
+ if (adapted !== void 0) data[key] = adapted;
3780
+ return value;
3781
+ } catch (e) {
3782
+ if (!options?.autofix) throw e;
3783
+ delete data[key];
3784
+ return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
3785
+ }
3786
+ }
3787
+ Schema.extend("array", (data, { inner, meta }, options) => {
3788
+ if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
3789
+ checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
3790
+ return [data.map((_, index) => property(data, index, inner, options))];
3791
+ });
3792
+ Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
3793
+ if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
3794
+ const result = {};
3795
+ for (const key in data) {
3796
+ let rKey;
3797
+ try {
3798
+ rKey = Schema.resolve(key, sKey, options)[0];
3799
+ } catch (error) {
3800
+ if (strict) continue;
3801
+ throw error;
3802
+ }
3803
+ result[rKey] = property(data, key, inner, options);
3804
+ data[rKey] = data[key];
3805
+ if (key !== rKey) delete data[key];
3806
+ }
3807
+ return [result];
3808
+ });
3809
+ Schema.extend("tuple", (data, { list }, options, strict) => {
3810
+ if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
3811
+ const result = list.map((inner, index) => property(data, index, inner, options));
3812
+ if (strict) return [result];
3813
+ result.push(...data.slice(list.length));
3814
+ return [result];
3815
+ });
3816
+ function merge(result, data) {
3817
+ for (const key in data) {
3818
+ if (key in result) continue;
3819
+ result[key] = data[key];
3820
+ }
3821
+ }
3822
+ Schema.extend("object", (data, { dict }, options, strict) => {
3823
+ if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
3824
+ const result = {};
3825
+ for (const key in dict) {
3826
+ const value = property(data, key, dict[key], options);
3827
+ if (!isNullable(value) || key in data) result[key] = value;
3828
+ }
3829
+ if (!strict) merge(result, data);
3830
+ return [result];
3831
+ });
3832
+ Schema.extend("union", (data, { list, toString }, options, strict) => {
3833
+ const messages = [];
3834
+ for (const inner of list) try {
3835
+ return Schema.resolve(data, inner, options, strict);
3836
+ } catch (error) {
3837
+ messages.push(error);
3838
+ }
3839
+ throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
3840
+ });
3841
+ Schema.extend("intersect", (data, { list, toString }, options, strict) => {
3842
+ if (!list.length) return [data];
3843
+ let result;
3844
+ for (const inner of list) {
3845
+ const value = Schema.resolve(data, inner, options, true)[0];
3846
+ if (isNullable(value)) continue;
3847
+ if (isNullable(result)) result = value;
3848
+ else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
3849
+ else if (typeof value === "object") merge(result ??= {}, value);
3850
+ else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
3851
+ }
3852
+ if (!strict && isPlainObject(data)) merge(result, data);
3853
+ return [result];
3854
+ });
3855
+ Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
3856
+ const [result, adapted = data] = Schema.resolve(data, inner, options, true);
3857
+ if (preserve) return [callback(result)];
3858
+ else return [callback(result), callback(adapted)];
3859
+ });
3860
+ const formatters = {};
3861
+ function defineMethod(name, keys, format) {
3862
+ formatters[name] = format;
3863
+ Object.assign(Schema, { [name](...args) {
3864
+ const schema = new Schema({ type: name });
3865
+ keys.forEach((key, index) => {
3866
+ switch (key) {
3867
+ case "sKey":
3868
+ schema.sKey = args[index] ?? Schema.string();
3869
+ break;
3870
+ case "inner":
3871
+ schema.inner = Schema.from(args[index]);
3872
+ break;
3873
+ case "list":
3874
+ schema.list = args[index].map(Schema.from);
3875
+ break;
3876
+ case "dict":
3877
+ schema.dict = mapValues(args[index], Schema.from);
3878
+ break;
3879
+ case "bits":
3880
+ schema.bits = {};
3881
+ for (const key in args[index]) {
3882
+ if (typeof args[index][key] !== "number") continue;
3883
+ schema.bits[key] = args[index][key];
3884
+ }
3885
+ break;
3886
+ case "callback": {
3887
+ const callback = schema.callback = args[index];
3888
+ callback["toJSON"] ||= () => callback.toString();
3889
+ break;
3890
+ }
3891
+ case "constructor": {
3892
+ const constructor = schema.constructor = args[index];
3893
+ if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
3894
+ break;
3895
+ }
3896
+ default: schema[key] = args[index];
3897
+ }
3898
+ });
3899
+ if (name === "object" || name === "dict") schema.meta.default = {};
3900
+ else if (name === "array" || name === "tuple") schema.meta.default = [];
3901
+ else if (name === "bitset") schema.meta.default = 0;
3902
+ return schema;
3903
+ } });
3904
+ }
3905
+ defineMethod("is", ["constructor"], ({ constructor }) => {
3906
+ if (typeof constructor === "function") return constructor.name;
3907
+ else return constructor;
3908
+ });
3909
+ defineMethod("any", [], () => "any");
3910
+ defineMethod("never", [], () => "never");
3911
+ defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
3912
+ defineMethod("string", [], () => "string");
3913
+ defineMethod("number", [], () => "number");
3914
+ defineMethod("boolean", [], () => "boolean");
3915
+ defineMethod("bitset", ["bits"], () => "bitset");
3916
+ defineMethod("function", [], () => "function");
3917
+ defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
3918
+ defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
3919
+ defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
3920
+ defineMethod("object", ["dict"], ({ dict }) => {
3921
+ if (Object.keys(dict).length === 0) return "{}";
3922
+ return `{ ${Object.entries(dict).map(([key, inner]) => {
3923
+ return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
3924
+ }).join(", ")} }`;
3925
+ });
3926
+ defineMethod("union", ["list"], ({ list }, inline) => {
3927
+ const result = list.map(({ toString: format }) => format()).join(" | ");
3928
+ return inline ? `(${result})` : result;
3929
+ });
3930
+ defineMethod("intersect", ["list"], ({ list }) => {
3931
+ return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
3932
+ });
3933
+ defineMethod("transform", [
3934
+ "inner",
3935
+ "callback",
3936
+ "preserve"
3937
+ ], ({ inner }, isInner) => inner.toString(isInner));
3938
+ //#endregion
3939
+ //#region lib/types/onboarding-config.js
3940
+ /** Public page-bootstrap options shared by the Host and Client halves. */
3941
+ /** Validate Host configuration and its public page-bootstrap payload. */
3942
+ const Config = Schema.object({ credentialOnboarding: Schema.boolean().default(true) });
3943
+ /** Page-global key carrying only the public onboarding options. */
3944
+ const ONBOARDING_CONFIG_GLOBAL = "__DSH_MODELS_ONBOARDING__";
3945
+ //#endregion
2867
3946
  //#region lib/types/client/index.js
2868
3947
  /** Dictionary namespace owned by this plugin. */
2869
3948
  const NS = "settings.models";
@@ -2888,7 +3967,7 @@ window.__ModuleLoader__.load({
2888
3967
  "remote.credentials",
2889
3968
  "remote.llm",
2890
3969
  "remote.settings",
2891
- "settingsScope",
3970
+ "configForms",
2892
3971
  "settingsSchema"
2893
3972
  ];
2894
3973
  /**
@@ -2898,13 +3977,15 @@ window.__ModuleLoader__.load({
2898
3977
  * @param ctx - client root context.
2899
3978
  */
2900
3979
  function apply(ctx) {
3980
+ const payload = globalThis[ONBOARDING_CONFIG_GLOBAL];
3981
+ const credentialOnboarding = Config(payload === void 0 ? {} : payload).credentialOnboarding && !("dshDesktop" in globalThis);
2901
3982
  ctx.effect(() => ctx.locale.register(NS, {
2902
3983
  zh,
2903
3984
  en
2904
3985
  }), "ui-settings-models: copy dictionaries");
2905
3986
  const schema = createSettingsSchemaOperations(ctx.settingsSchema);
2906
3987
  const operations = createModelsOperations(ctx);
2907
- const controller = new ModelsSettingsStore(ctx, schema, ctx.settingsScope.describe());
3988
+ const controller = new ModelsSettingsStore(ctx, schema, ctx.configForms.describe());
2908
3989
  const t = ctx.locale.bind(NS);
2909
3990
  const injected = () => ({
2910
3991
  controller,
@@ -2914,16 +3995,14 @@ window.__ModuleLoader__.load({
2914
3995
  t
2915
3996
  });
2916
3997
  const deepSeekOnboardingInjected = () => ({
3998
+ automatic: credentialOnboarding,
2917
3999
  controller,
2918
4000
  hooks: { models: controller.store },
2919
4001
  operations,
2920
4002
  schema,
2921
4003
  t
2922
4004
  });
2923
- const welcomeController = new WelcomeNoticeStore(ctx.settingsScope.bind({
2924
- namespace: WELCOME_NOTICE_SETTINGS_NAMESPACE,
2925
- decode: decodeWelcomeSection
2926
- }));
4005
+ const welcomeController = new WelcomeNoticeStore(ctx.configForms.get(WELCOME_NOTICE_SETTINGS_NAMESPACE));
2927
4006
  const welcomeInjected = () => ({
2928
4007
  controller: welcomeController,
2929
4008
  hooks: { welcome: welcomeController.store },
@@ -2972,6 +4051,10 @@ window.__ModuleLoader__.load({
2972
4051
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
2973
4052
  name: "settings.onboarding",
2974
4053
  id: "deepseek-official",
4054
+ children: { "settings.models.sign-in": {
4055
+ kind: "single",
4056
+ scope: "root"
4057
+ } },
2975
4058
  order: 0,
2976
4059
  inject: deepSeekOnboardingInjected
2977
4060
  }, DeepSeekOnboardingDialog));