@mofeng2223/dsh-claude-provider 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -56,7 +56,7 @@ window.__ModuleLoader__.load({
56
56
  }
57
57
  //#endregion
58
58
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/ModelsSection.module.css.mjs
59
- 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}";
59
+ 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_candidateName{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;font-size:13px;overflow:hidden}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
60
60
  const tagId$3 = "@mofeng2223/dsh-claude-provider/ModelsSection.module.css";
61
61
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
62
62
  const tag = document.createElement("style");
@@ -71,12 +71,14 @@ window.__ModuleLoader__.load({
71
71
  "addButton": "zGbnIq_addButton",
72
72
  "addCard": "zGbnIq_addCard",
73
73
  "addModelButton": "zGbnIq_addModelButton",
74
+ "addModes": "zGbnIq_addModes",
75
+ "addPanel": "zGbnIq_addPanel",
74
76
  "advancedHint": "zGbnIq_advancedHint",
75
77
  "candidate": "zGbnIq_candidate",
76
78
  "candidateEmpty": "zGbnIq_candidateEmpty",
77
- "candidateId": "zGbnIq_candidateId",
78
79
  "candidateLabel": "zGbnIq_candidateLabel",
79
80
  "candidateList": "zGbnIq_candidateList",
81
+ "candidateName": "zGbnIq_candidateName",
80
82
  "candidateSearch": "zGbnIq_candidateSearch",
81
83
  "candidateToolbar": "zGbnIq_candidateToolbar",
82
84
  "credentialDot": "zGbnIq_credentialDot",
@@ -112,6 +114,8 @@ window.__ModuleLoader__.load({
112
114
  "modelEntry": "zGbnIq_modelEntry",
113
115
  "modelField": "zGbnIq_modelField",
114
116
  "modelFieldLabel": "zGbnIq_modelFieldLabel",
117
+ "modelInputChoices": "zGbnIq_modelInputChoices",
118
+ "modelInputTypes": "zGbnIq_modelInputTypes",
115
119
  "modelList": "zGbnIq_modelList",
116
120
  "modelListHead": "zGbnIq_modelListHead",
117
121
  "modelRow": "zGbnIq_modelRow",
@@ -159,6 +163,132 @@ window.__ModuleLoader__.load({
159
163
  });
160
164
  }
161
165
  //#endregion
166
+ //#region lib/types/client/ModelInputTypes.js
167
+ /**
168
+ * Edit a nonempty set of input types, displaying inherited types before an override exists.
169
+ * @param props - model declaration and row replacement action.
170
+ * @returns the labeled text and image checkboxes.
171
+ */
172
+ function ModelInputTypes({ model, field, position, disabled, fallback, t, onChange }) {
173
+ const modalities = model[field];
174
+ const selected = Array.isArray(modalities) && modalities.length > 0 ? modalities : fallback ?? ["text"];
175
+ return (0, react_jsx_runtime.jsxs)("fieldset", {
176
+ className: ModelsSection_module_css_default["modelInputTypes"],
177
+ "aria-label": `${t("modelInputTypes")} ${String(position)}`,
178
+ children: [(0, react_jsx_runtime.jsx)("legend", {
179
+ className: ModelsSection_module_css_default["modelFieldLabel"],
180
+ children: t("modelInputTypes")
181
+ }), (0, react_jsx_runtime.jsx)("div", {
182
+ className: ModelsSection_module_css_default["modelInputChoices"],
183
+ children: ["text", "image"].map((modality) => (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
184
+ label: t(modality === "text" ? "modelInputText" : "modelInputImage"),
185
+ checked: selected.includes(modality),
186
+ disabled: disabled || selected.length === 1 && selected.includes(modality),
187
+ onChange: (checked) => {
188
+ const nextSelected = ["text", "image"].filter((value) => value === modality ? checked : selected.includes(value));
189
+ const next = {
190
+ ...model,
191
+ [field]: nextSelected
192
+ };
193
+ if (field === "inputModalities" && !nextSelected.includes("image")) {
194
+ Reflect.deleteProperty(next, "imagePixelBudget");
195
+ Reflect.deleteProperty(next, "imageMaxBytes");
196
+ }
197
+ onChange(next);
198
+ }
199
+ }, modality))
200
+ })]
201
+ });
202
+ }
203
+ //#endregion
204
+ //#region lib/types/client/ModelRow.js
205
+ /**
206
+ * Render consistent model identity, capacity, and input-type controls.
207
+ * @param props - drafted fields and their owning editor's actions.
208
+ * @returns one expandable model entry.
209
+ */
210
+ function ModelRow(props) {
211
+ const { model, position, t, disabled } = props;
212
+ return (0, react_jsx_runtime.jsxs)("div", {
213
+ className: ModelsSection_module_css_default["modelEntry"],
214
+ children: [(0, react_jsx_runtime.jsxs)("div", {
215
+ className: ModelsSection_module_css_default["modelRow"],
216
+ children: [
217
+ ["id", "name"].map((field) => (0, react_jsx_runtime.jsx)("input", {
218
+ className: ModelsSection_module_css_default["input"],
219
+ type: "text",
220
+ value: typeof model[field] === "string" ? model[field] : "",
221
+ placeholder: t(field === "id" ? "modelId" : "modelName"),
222
+ "aria-label": `${t(field === "id" ? "modelId" : "modelName")} ${String(position)}`,
223
+ disabled,
224
+ onChange: (event) => {
225
+ const value = event.target.value;
226
+ props.onFieldChange(field, field === "name" && value === "" ? void 0 : value);
227
+ },
228
+ onBlur: field === "id" ? (event) => props.onIdBlur?.(event.target.value) : void 0
229
+ }, field)),
230
+ (0, react_jsx_runtime.jsx)("button", {
231
+ type: "button",
232
+ className: ModelsSection_module_css_default["iconButton"],
233
+ "aria-label": `${t("modelAdvanced")} ${String(position)}`,
234
+ "aria-expanded": props.expanded,
235
+ title: t("modelAdvanced"),
236
+ onClick: props.onToggle,
237
+ 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, {})
238
+ }),
239
+ (0, react_jsx_runtime.jsx)("button", {
240
+ type: "button",
241
+ className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
242
+ "aria-label": `${t("removeModel")} ${String(position)}`,
243
+ title: t("removeModel"),
244
+ disabled,
245
+ onClick: props.onRemove,
246
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 })
247
+ })
248
+ ]
249
+ }), props.expanded ? (0, react_jsx_runtime.jsxs)("div", {
250
+ className: ModelsSection_module_css_default["modelAdvanced"],
251
+ children: [["contextWindow", "maxTokens"].map((field) => (0, react_jsx_runtime.jsxs)("label", {
252
+ className: ModelsSection_module_css_default["modelField"],
253
+ children: [(0, react_jsx_runtime.jsx)("span", {
254
+ className: ModelsSection_module_css_default["modelFieldLabel"],
255
+ children: t(field)
256
+ }), (0, react_jsx_runtime.jsx)("input", {
257
+ className: ModelsSection_module_css_default["input"],
258
+ type: "text",
259
+ inputMode: "numeric",
260
+ value: props[field].value,
261
+ placeholder: props[field].placeholder,
262
+ "aria-label": `${t(field)} ${String(position)}`,
263
+ disabled,
264
+ onChange: (event) => {
265
+ props[field].onChange(event.target.value);
266
+ },
267
+ onBlur: props[field].onBlur
268
+ })]
269
+ }, field)), (0, react_jsx_runtime.jsx)(ModelInputTypes, {
270
+ model,
271
+ field: props.inputField,
272
+ position,
273
+ fallback: props.inputFallback,
274
+ disabled: disabled || props.inputLoading === true,
275
+ t,
276
+ onChange: props.onChange
277
+ }), props.thinkingPresets ? (0, react_jsx_runtime.jsxs)("label", {
278
+ className: ModelsSection_module_css_default["modelField"],
279
+ children: [t("thinkingPreset"), (0, react_jsx_runtime.jsxs)("select", {
280
+ className: ModelsSection_module_css_default["input"] + " " + ModelsSection_module_css_default["selectInput"],
281
+ value: thinkingPresetOf(model),
282
+ "aria-label": t("thinkingPreset") + " " + position,
283
+ disabled,
284
+ onChange: (event) => props.onChange({ ...model, reasoningEfforts: reasoningEffortsForPreset(event.target.value) }),
285
+ children: ["toggle", "four", "five"].map((preset) => (0, react_jsx_runtime.jsx)("option", { value: preset, children: t({ toggle: "thinkingPresetToggle", four: "thinkingPresetFour", five: "thinkingPresetFive" }[preset]) }, preset))
286
+ })]
287
+ }) : null]
288
+ }) : null]
289
+ });
290
+ }
291
+ //#endregion
162
292
  //#region lib/types/client/DeepSeekModelsEditor.js
163
293
  /**
164
294
  * Curated editor for the direct DeepSeek adapter's advisory model catalog.
@@ -253,7 +383,7 @@ window.__ModuleLoader__.load({
253
383
  }
254
384
  /**
255
385
  * Render the direct DeepSeek adapter's model catalog: id and display name on
256
- * each row, capacities behind the row's own disclosure.
386
+ * each row, capacities and input types behind the row's own disclosure.
257
387
  * @param props - effective rows plus the array-level override actions.
258
388
  * @returns the catalog editor.
259
389
  */
@@ -321,29 +451,16 @@ window.__ModuleLoader__.load({
321
451
  return next;
322
452
  });
323
453
  };
324
- /** One capacity field of one row, rendered inside the row's disclosure. */
325
- const capacityField = (model, index, field, fallback) => (0, react_jsx_runtime.jsxs)("label", {
326
- className: ModelsSection_module_css_default["modelField"],
327
- children: [(0, react_jsx_runtime.jsx)("span", {
328
- className: ModelsSection_module_css_default["modelFieldLabel"],
329
- children: props.t(field === "contextWindow" ? "contextWindow" : "maxTokens")
330
- }), (0, react_jsx_runtime.jsx)("input", {
331
- className: ModelsSection_module_css_default["input"],
332
- type: "text",
333
- inputMode: "numeric",
334
- value: capacityText(model, index, field),
335
- placeholder: fallback === void 0 ? props.t(field === "contextWindow" ? "contextWindowPlaceholder" : "maxTokensPlaceholder") : formatCapacity(fallback),
336
- "aria-label": `${props.t(field === "contextWindow" ? "contextWindow" : "maxTokens")} ${String(index + 1)}`,
337
- disabled: props.disabled,
338
- onChange: (event) => {
339
- const text = event.target.value;
340
- setEditing((current) => new Map(current).set(`${String(index)}:${field}`, text));
341
- update(index, field, parseCapacity(text));
342
- },
343
- onBlur: () => {
344
- settleCapacity(index, field);
345
- }
346
- })]
454
+ const capacityInput = (model, index, field, fallback) => ({
455
+ value: capacityText(model, index, field),
456
+ placeholder: fallback === void 0 ? props.t(field === "contextWindow" ? "contextWindowPlaceholder" : "maxTokensPlaceholder") : formatCapacity(fallback),
457
+ onChange: (text) => {
458
+ setEditing((current) => new Map(current).set(`${String(index)}:${field}`, text));
459
+ update(index, field, parseCapacity(text));
460
+ },
461
+ onBlur: () => {
462
+ settleCapacity(index, field);
463
+ }
347
464
  });
348
465
  return (0, react_jsx_runtime.jsxs)("section", {
349
466
  className: ModelsSection_module_css_default["modelCatalog"],
@@ -373,64 +490,31 @@ window.__ModuleLoader__.load({
373
490
  children: props.t("modelsEmpty")
374
491
  }) : (0, react_jsx_runtime.jsx)("div", {
375
492
  className: ModelsSection_module_css_default["modelList"],
376
- children: props.models.map((model, index) => (0, react_jsx_runtime.jsxs)("div", {
377
- className: ModelsSection_module_css_default["modelEntry"],
378
- children: [(0, react_jsx_runtime.jsxs)("div", {
379
- className: ModelsSection_module_css_default["modelRow"],
380
- children: [
381
- (0, react_jsx_runtime.jsx)("input", {
382
- className: ModelsSection_module_css_default["input"],
383
- type: "text",
384
- value: typeof model["id"] === "string" ? model["id"] : "",
385
- placeholder: props.t("modelId"),
386
- "aria-label": `${props.t("modelId")} ${String(index + 1)}`,
387
- disabled: props.disabled,
388
- onChange: (event) => {
389
- update(index, "id", event.target.value);
390
- },
391
- onBlur: (event) => {
392
- const trimmed = event.target.value.trim();
393
- if (trimmed !== event.target.value) update(index, "id", trimmed);
394
- }
395
- }),
396
- (0, react_jsx_runtime.jsx)("input", {
397
- className: ModelsSection_module_css_default["input"],
398
- type: "text",
399
- value: typeof model["name"] === "string" ? model["name"] : "",
400
- placeholder: props.t("modelName"),
401
- "aria-label": `${props.t("modelName")} ${String(index + 1)}`,
402
- disabled: props.disabled,
403
- onChange: (event) => {
404
- update(index, "name", event.target.value === "" ? void 0 : event.target.value);
405
- }
406
- }),
407
- (0, react_jsx_runtime.jsx)("button", {
408
- type: "button",
409
- className: ModelsSection_module_css_default["iconButton"],
410
- "aria-label": `${props.t("modelAdvanced")} ${String(index + 1)}`,
411
- "aria-expanded": expanded.has(index),
412
- title: props.t("modelAdvanced"),
413
- onClick: () => {
414
- toggle(index);
415
- },
416
- 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, {})
417
- }),
418
- (0, react_jsx_runtime.jsx)("button", {
419
- type: "button",
420
- className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
421
- "aria-label": `${props.t("removeModel")} ${String(index + 1)}`,
422
- title: props.t("removeModel"),
423
- disabled: props.disabled,
424
- onClick: () => {
425
- remove(index);
426
- },
427
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
428
- })
429
- ]
430
- }), expanded.has(index) ? (0, react_jsx_runtime.jsxs)("div", {
431
- className: ModelsSection_module_css_default["modelAdvanced"],
432
- children: [capacityField(model, index, "contextWindow", props.defaultContextWindow), capacityField(model, index, "maxTokens", props.defaultMaxTokens)]
433
- }) : null]
493
+ children: props.models.map((model, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
494
+ model,
495
+ position: index + 1,
496
+ inputField: "inputModalities",
497
+ expanded: expanded.has(index),
498
+ disabled: props.disabled,
499
+ t: props.t,
500
+ contextWindow: capacityInput(model, index, "contextWindow", props.defaultContextWindow),
501
+ maxTokens: capacityInput(model, index, "maxTokens", props.defaultMaxTokens),
502
+ onFieldChange: (field, value) => {
503
+ update(index, field, value);
504
+ },
505
+ onIdBlur: (value) => {
506
+ const trimmed = value.trim();
507
+ if (trimmed !== value) update(index, "id", trimmed);
508
+ },
509
+ onChange: (next) => {
510
+ props.onChange(props.models.map((row, at) => at === index ? next : row));
511
+ },
512
+ onToggle: () => {
513
+ toggle(index);
514
+ },
515
+ onRemove: () => {
516
+ remove(index);
517
+ }
434
518
  }, index))
435
519
  }),
436
520
  (0, react_jsx_runtime.jsxs)("button", {
@@ -440,7 +524,7 @@ window.__ModuleLoader__.load({
440
524
  onClick: () => {
441
525
  props.onChange([...props.models.map((model) => ({ ...model })), { id: "" }]);
442
526
  },
443
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), props.t("addModel")]
527
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), props.t("addModel")]
444
528
  })
445
529
  ]
446
530
  });
@@ -472,44 +556,6 @@ window.__ModuleLoader__.load({
472
556
  const value = model[key];
473
557
  return typeof value === "number" ? value : void 0;
474
558
  }
475
- /** Disclosure chevron; rotates to point down while its row is open. */
476
- function IconChevron({ open }) {
477
- return (0, react_jsx_runtime.jsx)("svg", {
478
- width: "14",
479
- height: "14",
480
- viewBox: "0 0 16 16",
481
- fill: "none",
482
- "aria-hidden": true,
483
- style: {
484
- transform: open ? "rotate(90deg)" : void 0,
485
- transition: "transform 120ms ease"
486
- },
487
- children: (0, react_jsx_runtime.jsx)("path", {
488
- d: "M6 3.5L10.5 8L6 12.5",
489
- stroke: "currentColor",
490
- strokeWidth: "1.5",
491
- strokeLinecap: "round",
492
- strokeLinejoin: "round"
493
- })
494
- });
495
- }
496
- /** Removal glyph for one model row. */
497
- function IconTrash() {
498
- return (0, react_jsx_runtime.jsx)("svg", {
499
- width: "14",
500
- height: "14",
501
- viewBox: "0 0 16 16",
502
- fill: "none",
503
- "aria-hidden": true,
504
- children: (0, react_jsx_runtime.jsx)("path", {
505
- 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",
506
- stroke: "currentColor",
507
- strokeWidth: "1.3",
508
- strokeLinecap: "round",
509
- strokeLinejoin: "round"
510
- })
511
- });
512
- }
513
559
  /**
514
560
  * What an empty capacity field is worth, shown as its placeholder so a row left
515
561
  * blank does not read as a model with no capacity at all.
@@ -535,13 +581,14 @@ window.__ModuleLoader__.load({
535
581
  function capacitySpelling(value) {
536
582
  return value === void 0 ? "" : formatCapacity(value);
537
583
  }
538
- /** Adopt a candidate, keeping whatever capacities the provider disclosed. */
584
+ /** Adopt a candidate, preserving disclosed capacities and input types. */
539
585
  function adopt(candidate) {
540
586
  return {
541
587
  id: candidate.id,
542
588
  ...candidate.name === void 0 ? {} : { name: candidate.name },
543
589
  ...candidate.contextWindow === void 0 ? {} : { contextWindow: candidate.contextWindow },
544
- ...candidate.maxTokens === void 0 ? {} : { maxTokens: candidate.maxTokens }
590
+ ...candidate.maxTokens === void 0 ? {} : { maxTokens: candidate.maxTokens },
591
+ ...candidate.inputModalities === void 0 ? {} : { input: [...candidate.inputModalities] }
545
592
  };
546
593
  }
547
594
  /**
@@ -550,9 +597,35 @@ window.__ModuleLoader__.load({
550
597
  * @returns the model-list editor.
551
598
  */
552
599
  function ModelListEditor(props) {
553
- const { models, onChange, probe, operations, t, disabled } = props;
600
+ const { models, onChange, probe, operations, t, disabled, onBusyChange } = props;
601
+ const { catalogProvider } = props;
554
602
  const [busy, setBusy] = (0, react.useState)(false);
603
+ (0, react.useEffect)(() => {
604
+ onBusyChange(busy);
605
+ }, [busy, onBusyChange]);
555
606
  const [failure, setFailure] = (0, react.useState)(void 0);
607
+ const [inheritedCatalog, setInheritedCatalog] = (0, react.useState)(void 0);
608
+ (0, react.useEffect)(() => {
609
+ if (catalogProvider === void 0) return;
610
+ let current = true;
611
+ operations.discoverModels(probe.settingsNs, { provider: catalogProvider }).then((answer) => {
612
+ if (!current) return;
613
+ setInheritedCatalog({
614
+ provider: catalogProvider,
615
+ models: answer.kind === "found" ? answer.models : []
616
+ });
617
+ setFailure(answer.kind === "refused" ? answer.message : void 0);
618
+ });
619
+ return () => {
620
+ current = false;
621
+ };
622
+ }, [
623
+ catalogProvider,
624
+ operations,
625
+ probe.settingsNs
626
+ ]);
627
+ const catalog = inheritedCatalog?.provider === catalogProvider ? inheritedCatalog?.models : void 0;
628
+ const inputDefaults = (0, react.useMemo)(() => new Map(catalog?.map((model) => [model.id, model.inputModalities])), [catalog]);
556
629
  const [candidates, setCandidates] = (0, react.useState)(void 0);
557
630
  const [picked, setPicked] = (0, react.useState)(/* @__PURE__ */ new Set());
558
631
  const [candidateQuery, setCandidateQuery] = (0, react.useState)("");
@@ -608,6 +681,10 @@ window.__ModuleLoader__.load({
608
681
  return;
609
682
  }
610
683
  const found = answer.models;
684
+ if (catalogProvider !== void 0) setInheritedCatalog({
685
+ provider: catalogProvider,
686
+ models: found
687
+ });
611
688
  if (found.length === 0) {
612
689
  setFailure(t("fetchEmpty"));
613
690
  return;
@@ -696,125 +773,61 @@ window.__ModuleLoader__.load({
696
773
  className: ModelsSection_module_css_default["modelEmpty"],
697
774
  children: t("modelsEmpty")
698
775
  }) : null,
699
- models.map((model, index) => (0, react_jsx_runtime.jsxs)("div", {
700
- className: ModelsSection_module_css_default["modelEntry"],
701
- children: [(0, react_jsx_runtime.jsxs)("div", {
702
- className: ModelsSection_module_css_default["modelRow"],
703
- children: [
704
- (0, react_jsx_runtime.jsx)("input", {
705
- className: ModelsSection_module_css_default["input"],
706
- type: "text",
707
- value: textOf(model, "id"),
708
- placeholder: t("modelId"),
709
- "aria-label": `${t("modelId")} ${index + 1}`,
710
- disabled,
711
- onChange: (event) => {
712
- patch(index, { id: event.target.value });
713
- }
714
- }),
715
- (0, react_jsx_runtime.jsx)("input", {
716
- className: ModelsSection_module_css_default["input"],
717
- type: "text",
718
- value: textOf(model, "name"),
719
- placeholder: t("modelName"),
720
- "aria-label": `${t("modelName")} ${index + 1}`,
721
- disabled,
722
- onChange: (event) => {
723
- patch(index, { name: event.target.value === "" ? void 0 : event.target.value });
724
- }
725
- }),
726
- (0, react_jsx_runtime.jsx)("button", {
727
- type: "button",
728
- className: ModelsSection_module_css_default["iconButton"],
729
- "aria-label": `${t("modelAdvanced")} ${index + 1}`,
730
- "aria-expanded": expanded.has(index),
731
- title: t("modelAdvanced"),
732
- onClick: () => {
733
- toggleExpanded(index);
734
- },
735
- children: (0, react_jsx_runtime.jsx)(IconChevron, { open: expanded.has(index) })
736
- }),
737
- (0, react_jsx_runtime.jsx)("button", {
738
- type: "button",
739
- className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
740
- "aria-label": `${t("removeModel")} ${index + 1}`,
741
- title: t("removeModel"),
742
- disabled,
743
- onClick: () => {
744
- onChange(models.filter((_model, at) => at !== index));
745
- setExpanded((current) => {
746
- const next = /* @__PURE__ */ new Set();
747
- for (const at of current) if (at < index) next.add(at);
748
- else if (at > index) next.add(at - 1);
749
- return next;
750
- });
751
- setEditing((current) => reindexOnRemove(current, index));
752
- },
753
- children: (0, react_jsx_runtime.jsx)(IconTrash, {})
754
- })
755
- ]
756
- }), expanded.has(index) ? (0, react_jsx_runtime.jsxs)("div", {
757
- className: ModelsSection_module_css_default["modelAdvanced"],
758
- children: [(0, react_jsx_runtime.jsxs)("label", {
759
- className: ModelsSection_module_css_default["modelField"],
760
- children: [(0, react_jsx_runtime.jsx)("span", {
761
- className: ModelsSection_module_css_default["modelFieldLabel"],
762
- children: t("modelContextWindow")
763
- }), (0, react_jsx_runtime.jsx)("input", {
764
- className: ModelsSection_module_css_default["input"],
765
- type: "text",
766
- inputMode: "numeric",
767
- value: capacityText(model, index, "contextWindow"),
768
- placeholder: CAPACITY_HINT.contextWindow,
769
- "aria-label": `${t("modelContextWindow")} ${index + 1}`,
770
- disabled,
771
- onChange: (event) => {
772
- editCapacity(index, "contextWindow", event.target.value);
773
- }
774
- })]
775
- }), (0, react_jsx_runtime.jsxs)("label", {
776
- className: ModelsSection_module_css_default["modelField"],
777
- children: [(0, react_jsx_runtime.jsx)("span", {
778
- className: ModelsSection_module_css_default["modelFieldLabel"],
779
- children: t("modelMaxTokens")
780
- }), (0, react_jsx_runtime.jsx)("input", {
781
- className: ModelsSection_module_css_default["input"],
782
- type: "text",
783
- inputMode: "numeric",
784
- value: capacityText(model, index, "maxTokens"),
785
- placeholder: CAPACITY_HINT.maxTokens,
786
- "aria-label": `${t("modelMaxTokens")} ${index + 1}`,
787
- disabled,
788
- onChange: (event) => {
789
- editCapacity(index, "maxTokens", event.target.value);
790
- }
791
- })]
792
- }), props.thinkingPresets ? (0, react_jsx_runtime.jsxs)("label", {
793
- className: ModelsSection_module_css_default["modelField"],
794
- children: [(0, react_jsx_runtime.jsx)("span", {
795
- className: ModelsSection_module_css_default["modelFieldLabel"],
796
- children: t("thinkingPreset")
797
- }), (0, react_jsx_runtime.jsxs)("select", {
798
- className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
799
- value: thinkingPresetOf(model),
800
- "aria-label": `${t("thinkingPreset")} ${index + 1}`,
801
- disabled,
802
- onChange: (event) => {
803
- patch(index, { reasoningEfforts: reasoningEffortsForPreset(event.target.value) });
804
- },
805
- children: [(0, react_jsx_runtime.jsx)("option", { value: "toggle", children: t("thinkingPresetToggle") }), (0, react_jsx_runtime.jsx)("option", { value: "four", children: t("thinkingPresetFour") }), (0, react_jsx_runtime.jsx)("option", { value: "five", children: t("thinkingPresetFive") })]
806
- })]
807
- }) : null]
808
- }) : null]
809
- }, index)),
810
- (0, react_jsx_runtime.jsx)("button", {
776
+ (0, react_jsx_runtime.jsx)("div", {
777
+ className: ModelsSection_module_css_default["modelList"],
778
+ children: models.map((model, index) => (0, react_jsx_runtime.jsx)(ModelRow, {
779
+ model,
780
+ position: index + 1,
781
+ inputField: "input",
782
+ thinkingPresets: props.thinkingPresets,
783
+ inputFallback: props.thinkingPresets && CLAUDE_KNOWN_MODELS[model.id] ? ["text", "image"] : inputDefaults.get(textOf(model, "id")) ?? props.defaultInput,
784
+ inputLoading: catalogProvider !== void 0 && catalog === void 0,
785
+ expanded: expanded.has(index),
786
+ disabled,
787
+ t,
788
+ contextWindow: {
789
+ value: capacityText(model, index, "contextWindow"),
790
+ placeholder: CAPACITY_HINT.contextWindow,
791
+ onChange: (text) => {
792
+ editCapacity(index, "contextWindow", text);
793
+ }
794
+ },
795
+ maxTokens: {
796
+ value: capacityText(model, index, "maxTokens"),
797
+ placeholder: CAPACITY_HINT.maxTokens,
798
+ onChange: (text) => {
799
+ editCapacity(index, "maxTokens", text);
800
+ }
801
+ },
802
+ onFieldChange: (field, value) => {
803
+ patch(index, { [field]: value });
804
+ },
805
+ onChange: (next) => {
806
+ onChange(models.map((row, at) => at === index ? next : row));
807
+ },
808
+ onToggle: () => {
809
+ toggleExpanded(index);
810
+ },
811
+ onRemove: () => {
812
+ onChange(models.filter((_model, at) => at !== index));
813
+ setExpanded((current) => {
814
+ const next = /* @__PURE__ */ new Set();
815
+ for (const at of current) if (at < index) next.add(at);
816
+ else if (at > index) next.add(at - 1);
817
+ return next;
818
+ });
819
+ setEditing((current) => reindexOnRemove(current, index));
820
+ }
821
+ }, index))
822
+ }),
823
+ (0, react_jsx_runtime.jsxs)("button", {
811
824
  type: "button",
812
825
  className: ModelsSection_module_css_default["addModelButton"],
813
826
  disabled,
814
827
  onClick: () => {
815
828
  onChange([...models, props.thinkingPresets ? withDefaultClaudeReasoning({ id: "" }) : { id: "" }]);
816
829
  },
817
- children: t("addModel")
830
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("addModel")]
818
831
  }),
819
832
  failure !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
820
833
  className: ModelsSection_module_css_default["error"],
@@ -871,8 +884,9 @@ window.__ModuleLoader__.load({
871
884
  toggle(candidate.id);
872
885
  }
873
886
  }), (0, react_jsx_runtime.jsx)("span", {
874
- className: ModelsSection_module_css_default["candidateId"],
875
- children: candidate.id
887
+ className: ModelsSection_module_css_default["candidateName"],
888
+ title: candidate.name ?? candidate.id,
889
+ children: candidate.name ?? candidate.id
876
890
  })]
877
891
  })
878
892
  }, candidate.id))
@@ -1124,18 +1138,46 @@ window.__ModuleLoader__.load({
1124
1138
  return { kind: "credential-missing" };
1125
1139
  }
1126
1140
  //#endregion
1141
+ //#region lib/types/client/protocol-label.js
1142
+ /**
1143
+ * Product names for the wire protocols a pi-ai route may speak. The pickers
1144
+ * show these instead of the schema identifiers (`openai-completions`), which
1145
+ * are what `settings.yaml` carries and what the option values stay.
1146
+ */
1147
+ /** The protocols this page names, keyed by their schema identifier. */
1148
+ const PROTOCOL_LABEL_KEYS = {
1149
+ "openai-completions": "protocolOpenAiCompletions",
1150
+ "openai-responses": "protocolOpenAiResponses",
1151
+ "anthropic-messages": "protocolAnthropicMessages"
1152
+ };
1153
+ /**
1154
+ * The picker text for one protocol identifier.
1155
+ * @param t - section copy.
1156
+ * @param protocol - the schema identifier of the protocol.
1157
+ * @returns the product name this page knows the protocol by; a protocol the
1158
+ * adapter adds before this page names it shows its identifier, the spelling
1159
+ * `settings.yaml` needs anyway.
1160
+ */
1161
+ function protocolLabel(t, protocol) {
1162
+ const key = PROTOCOL_LABEL_KEYS[protocol];
1163
+ return key === void 0 ? protocol : t(key);
1164
+ }
1165
+ //#endregion
1127
1166
  //#region lib/types/client/CustomProviderCard.js
1128
1167
  /**
1129
1168
  * The card that declares a provider pi-ai does not ship — an OpenAI-compatible
1130
1169
  * gateway, a self-hosted server, or a provider newer than the installed
1131
1170
  * catalog.
1132
1171
  *
1133
- * This is a create, not an edit, which is why it is its own card rather than
1172
+ * This is a create, not an edit, which is why it is its own form rather than
1134
1173
  * the provider editor with extra fields: the route id is being *chosen* here,
1135
- * and the settings address does not exist until it is. One `settings.mutate`
1136
- * sets the whole profile at `providers.<route>`; the key travels separately
1137
- * through `credentials/set` under the reference the profile records, exactly as
1138
- * an existing provider's key does.
1174
+ * and the settings address does not exist until it is. It renders as the
1175
+ * custom-API panel of the section's add card; the card's mode switch names it
1176
+ * when both modes are offered, and with the custom mode alone the card shows
1177
+ * this form directly. One `settings.mutate` sets the whole profile at
1178
+ * `providers.<route>`; the key travels separately through `credentials/set`
1179
+ * under the reference the profile records, exactly as an existing provider's
1180
+ * key does.
1139
1181
  *
1140
1182
  * The three fields a hand-declared route cannot default — endpoint, protocol,
1141
1183
  * and at least one model — are required here rather than at load, so the
@@ -1172,7 +1214,7 @@ window.__ModuleLoader__.load({
1172
1214
  * @returns the creation card.
1173
1215
  */
1174
1216
  function CustomProviderCard(props) {
1175
- const { taken, protocols, operations, t } = props;
1217
+ const { taken, protocols, operations, t, onBusyChange } = props;
1176
1218
  const [openedAt] = (0, react.useState)(() => props.revision);
1177
1219
  const [route, setRoute] = (0, react.useState)("");
1178
1220
  const [displayName, setDisplayName] = (0, react.useState)("");
@@ -1183,6 +1225,14 @@ window.__ModuleLoader__.load({
1183
1225
  const [keyDraft, setKeyDraft] = (0, react.useState)("");
1184
1226
  const [models, setModels] = (0, react.useState)([]);
1185
1227
  const [busy, setBusy] = (0, react.useState)(false);
1228
+ const [listBusy, setListBusy] = (0, react.useState)(false);
1229
+ (0, react.useEffect)(() => {
1230
+ onBusyChange?.(busy || listBusy);
1231
+ }, [
1232
+ busy,
1233
+ listBusy,
1234
+ onBusyChange
1235
+ ]);
1186
1236
  const [failure, setFailure] = (0, react.useState)(void 0);
1187
1237
  /**
1188
1238
  * The profile write landed. Only the key write can still be outstanding, so
@@ -1253,13 +1303,6 @@ window.__ModuleLoader__.load({
1253
1303
  return (0, react_jsx_runtime.jsxs)("div", {
1254
1304
  className: ModelsSection_module_css_default["editor"],
1255
1305
  children: [
1256
- (0, react_jsx_runtime.jsx)("div", {
1257
- className: ModelsSection_module_css_default["editorHeader"],
1258
- children: (0, react_jsx_runtime.jsx)("span", {
1259
- className: ModelsSection_module_css_default["editorTitle"],
1260
- children: t(isAdaptiveClaude ? "claudeCustomTitle" : "customTitle")
1261
- })
1262
- }),
1263
1306
  isAdaptiveClaude ? (0, react_jsx_runtime.jsxs)("p", {
1264
1307
  className: ModelsSection_module_css_default["savedNotice"],
1265
1308
  role: "status",
@@ -1315,7 +1358,7 @@ window.__ModuleLoader__.load({
1315
1358
  className: ModelsSection_module_css_default["input"],
1316
1359
  type: "text",
1317
1360
  value: baseURL,
1318
- placeholder: t("customBaseUrlPlaceholder"),
1361
+ placeholder: t(protocol === "anthropic-messages" ? "customAnthropicBaseUrlPlaceholder" : "customBaseUrlPlaceholder"),
1319
1362
  "aria-label": t("baseUrl"),
1320
1363
  "aria-invalid": baseUrlInvalid,
1321
1364
  disabled: profileDisabled,
@@ -1343,7 +1386,7 @@ window.__ModuleLoader__.load({
1343
1386
  },
1344
1387
  children: protocols.map((choice) => (0, react_jsx_runtime.jsx)("option", {
1345
1388
  value: choice,
1346
- children: choice
1389
+ children: protocolLabel(t, choice)
1347
1390
  }, choice))
1348
1391
  })]
1349
1392
  }),
@@ -1385,7 +1428,8 @@ window.__ModuleLoader__.load({
1385
1428
  thinkingPresets: isAdaptiveClaude,
1386
1429
  operations,
1387
1430
  t,
1388
- disabled: profileDisabled
1431
+ disabled: profileDisabled,
1432
+ onBusyChange: setListBusy
1389
1433
  }),
1390
1434
  failure !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
1391
1435
  className: ModelsSection_module_css_default["error"],
@@ -1429,9 +1473,9 @@ window.__ModuleLoader__.load({
1429
1473
  * Reasoning effort is deliberately absent: it is a per-MODEL capability, and
1430
1474
  * the models under one provider disagree about it, so a provider-scoped
1431
1475
  * control can only be set to a value some of them reject. The composer's
1432
- * model picker offers each model its own levels; `settings.yaml` keeps the
1476
+ * model picker offers each model its own levels; `cordis.patch.yml` keeps the
1433
1477
  * profile field for a deployment that knows its route. Everything else stays
1434
- * owned by `settings.yaml`. Profile edits land as minimal `settings.mutate`
1478
+ * owned by `cordis.patch.yml`. Profile edits land as minimal `settings.mutate`
1435
1479
  * path ops against the stored section — the card names only the fields it can
1436
1480
  * see instead of rebuilding the whole subtree from a partial descriptor.
1437
1481
  */
@@ -1491,6 +1535,15 @@ window.__ModuleLoader__.load({
1491
1535
  const [keyDraft, setKeyDraft] = (0, react.useState)("");
1492
1536
  const [keyState, setKeyState] = (0, react.useState)(void 0);
1493
1537
  const [busy, setBusy] = (0, react.useState)(false);
1538
+ const [listBusy, setListBusy] = (0, react.useState)(false);
1539
+ const { onBusyChange } = props;
1540
+ (0, react.useEffect)(() => {
1541
+ onBusyChange?.(busy || listBusy);
1542
+ }, [
1543
+ busy,
1544
+ listBusy,
1545
+ onBusyChange
1546
+ ]);
1494
1547
  const [failure, setFailure] = (0, react.useState)(void 0);
1495
1548
  const [committedOriginal, setCommittedOriginal] = (0, react.useState)(() => schema.getPath(namespace.user, settingsPath));
1496
1549
  const [expectedRevision, setExpectedRevision] = (0, react.useState)(() => namespace.revision);
@@ -1625,6 +1678,7 @@ window.__ModuleLoader__.load({
1625
1678
  const models = modelDrafts(modelsOverridden ? customModels : inheritedModels());
1626
1679
  const defaultContextWindow = schema.getPath(fallback, ["defaultContextWindow"]);
1627
1680
  const defaultMaxTokens = schema.getPath(fallback, ["maxTokens"]);
1681
+ const defaultInput = schema.getPath(fallback, ["defaultInput"]);
1628
1682
  const keyPlaceholder = keyLocked ? t("keyEnvLocked") : keyState?.configured === true && props.credentialRequired !== true ? t("keyStored") : family === "pi-ai" ? t("keyPlaceholderNative") : t("keyPlaceholder");
1629
1683
  /** What both family editors take: the rows, whose layer owns them, and the two writes. */
1630
1684
  const catalogProps = {
@@ -1702,7 +1756,7 @@ window.__ModuleLoader__.load({
1702
1756
  className: ModelsSection_module_css_default["input"],
1703
1757
  type: "text",
1704
1758
  value: stringAt(draft, "baseURL") ?? "",
1705
- placeholder: family === "deepseek" ? t(stringAt(fallback, "protocol") === "messages" ? "deepSeekMessagesBaseUrl" : "deepSeekChatBaseUrl") : stringAt(fallback, "baseURL") ?? t("baseUrlDefault"),
1759
+ placeholder: family === "deepseek" ? t("deepSeekBaseUrl") : stringAt(fallback, "baseURL") ?? t("baseUrlDefault"),
1706
1760
  "aria-describedby": family === "deepseek" ? `${props.provider}-endpoint-hint` : void 0,
1707
1761
  "aria-label": t("baseUrl"),
1708
1762
  disabled,
@@ -1735,7 +1789,7 @@ window.__ModuleLoader__.load({
1735
1789
  children: t("customApiUnset")
1736
1790
  }) : null, protocols.map((choice) => (0, react_jsx_runtime.jsx)("option", {
1737
1791
  value: choice,
1738
- children: choice
1792
+ children: protocolLabel(t, choice)
1739
1793
  }, choice))]
1740
1794
  })]
1741
1795
  }) : null,
@@ -1745,10 +1799,13 @@ window.__ModuleLoader__.load({
1745
1799
  defaultMaxTokens: typeof defaultMaxTokens === "number" ? defaultMaxTokens : void 0
1746
1800
  }) : (0, react_jsx_runtime.jsx)(ModelListEditor, {
1747
1801
  ...catalogProps,
1802
+ catalogProvider: props.declared === true ? void 0 : props.provider,
1803
+ defaultInput: Array.isArray(defaultInput) ? defaultInput : void 0,
1748
1804
  probe,
1749
1805
  probeBlocked: keyFailure,
1750
1806
  thinkingPresets: props.thinkingPresets === true,
1751
- operations
1807
+ operations,
1808
+ onBusyChange: setListBusy
1752
1809
  })
1753
1810
  ]
1754
1811
  })]
@@ -1805,11 +1862,19 @@ window.__ModuleLoader__.load({
1805
1862
  * solid configured or missing dots. A whole-section provider without a
1806
1863
  * configured key renders as its open setup card instead of a row, but only in
1807
1864
  * the first-run posture — no provider on the page can serve requests yet — and
1808
- * only until the user closes that card; the add flow is a card carrying the
1809
- * dormant-provider select. Each card kind owns its own open state, so closing
1810
- * one never discards a draft in another. Every mutation writes through the
1811
- * wire, while a provider removal first requires confirmation; the page
1812
- * re-renders from pushed invalidations or the post-apply reload.
1865
+ * only until the user closes that card. The add flow is one card behind one
1866
+ * button: a mode switch chooses between adopting a dormant directory provider
1867
+ * (the catalog select over the provider editor) and declaring a custom model
1868
+ * API (the create form). A panel mounts the first time its mode is shown and
1869
+ * stays mounted, hidden, while the card is open and its mode stays offered,
1870
+ * so switching modes discards neither draft and an unvisited mode costs
1871
+ * nothing; the switch holds still while either panel has a write or an
1872
+ * endpoint interrogation in flight, since a switch underneath one would
1873
+ * orphan the answer. Each card kind owns its own open state, so closing one
1874
+ * never discards a draft in another. Every
1875
+ * mutation writes through the wire, while a provider removal first requires
1876
+ * confirmation; the page re-renders from pushed invalidations or the
1877
+ * post-apply reload.
1813
1878
  */
1814
1879
  /** Render an editor for either the setup posture or an expanded provider row. */
1815
1880
  function renderProviderEditor({ target, ...props }) {
@@ -1908,10 +1973,14 @@ window.__ModuleLoader__.load({
1908
1973
  if (preset === "four") return { ...CLAUDE_FOUR_EFFORTS };
1909
1974
  return { ...CLAUDE_FIVE_EFFORTS };
1910
1975
  }
1976
+ function withKnownClaudeInput(model) {
1977
+ if (!CLAUDE_KNOWN_MODELS[model?.id] || (Array.isArray(model.input) && model.input.length > 0)) return { ...model };
1978
+ return { ...model, input: ["text", "image"] };
1979
+ }
1911
1980
  function withFetchedClaudeMetadata(model) {
1912
1981
  const known = CLAUDE_KNOWN_MODELS[model?.id];
1913
1982
  if (known === undefined) return withDefaultClaudeReasoning(model);
1914
- return { ...model, contextWindow: known.contextWindow, maxTokens: known.maxTokens, reasoningEfforts: reasoningEffortsForPreset(known.preset) };
1983
+ return { ...withKnownClaudeInput(model), contextWindow: known.contextWindow, maxTokens: known.maxTokens, reasoningEfforts: reasoningEffortsForPreset(known.preset) };
1915
1984
  }
1916
1985
  function thinkingPresetOf(model) {
1917
1986
  const efforts = model?.reasoningEfforts;
@@ -1928,7 +1997,7 @@ window.__ModuleLoader__.load({
1928
1997
  : { ...model, reasoningEfforts: reasoningEffortsForPreset("five") };
1929
1998
  }
1930
1999
  function withNativeClaudeModel(model) {
1931
- const next = withDefaultClaudeReasoning(model);
2000
+ const next = withKnownClaudeInput(withDefaultClaudeReasoning(model));
1932
2001
  const efforts = next.reasoningEfforts;
1933
2002
  const adaptive = efforts !== null && typeof efforts === "object" && !Array.isArray(efforts)
1934
2003
  && ["low", "medium", "high", "max"].every((level) => typeof efforts[level] === "string" && efforts[level].length > 0);
@@ -2035,30 +2104,50 @@ window.__ModuleLoader__.load({
2035
2104
  const { controller, operations, schema, t } = injected;
2036
2105
  const state = injected.useSnapshot((snapshot) => snapshot);
2037
2106
  const [editing, setEditing] = (0, react.useState)(void 0);
2038
- const [adding, setAdding] = (0, react.useState)(false);
2107
+ const [addOpen, setAddOpen] = (0, react.useState)(false);
2108
+ const [addMode, setAddMode] = (0, react.useState)("catalog");
2109
+ /** The modes shown since the add card opened; each keeps its panel mounted. */
2110
+ const [visited, setVisited] = (0, react.useState)(() => /* @__PURE__ */ new Set());
2111
+ /** Whether each add panel has a write or an interrogation in flight. */
2112
+ const [catalogBusy, setCatalogBusy] = (0, react.useState)(false);
2113
+ const [customBusy, setCustomBusy] = (0, react.useState)(false);
2114
+ const [claudeBusy, setClaudeBusy] = (0, react.useState)(false);
2115
+ /** Base of the add card's tab and panel ids. */
2116
+ const addId = (0, react.useId)();
2039
2117
  const [deleteTarget, setDeleteTarget] = (0, react.useState)(void 0);
2040
2118
  const [deleting, setDeleting] = (0, react.useState)(false);
2041
2119
  const [deleteFailure, setDeleteFailure] = (0, react.useState)(void 0);
2042
2120
  const [savedTarget, setSavedTarget] = (0, react.useState)(void 0);
2043
- const [declaringType, setDeclaringType] = (0, react.useState)(void 0);
2044
2121
  const [dismissedSetup, setDismissedSetup] = (0, react.useState)(() => /* @__PURE__ */ new Set());
2045
2122
  const announceSaved = (target) => {
2046
2123
  controller.load().then(() => {
2047
2124
  setSavedTarget(target);
2048
2125
  });
2049
2126
  };
2050
- const closeEditor = (changed, target) => {
2127
+ /**
2128
+ * Close the add card whole. The catalog target is forgotten with it, since
2129
+ * `editing` doubles as the row editor's target once the card is closed and a
2130
+ * refresh could otherwise open the row of a provider the draft never saved.
2131
+ * The busy flags reset here because a panel that closes itself on success
2132
+ * unmounts before it can report idle.
2133
+ */
2134
+ const closeAdd = () => {
2051
2135
  setEditing(void 0);
2052
- setAdding(false);
2053
- setDeclaringType(void 0);
2136
+ setAddOpen(false);
2137
+ setCatalogBusy(false);
2138
+ setCustomBusy(false);
2139
+ setClaudeBusy(false);
2140
+ };
2141
+ const closeEditor = (changed, target) => {
2142
+ closeAdd();
2054
2143
  if (changed) announceSaved(target);
2055
2144
  };
2056
2145
  /**
2057
- * Close a setup card, which owns none of the state above: the row-editor,
2058
- * add, and declare cards each own one of those, so clearing them here would
2059
- * discard a draft the user opened beside this card. Dismissal is this card's
2060
- * own — the provider falls back to an ordinary row for the rest of the
2061
- * session, and reopens through Edit.
2146
+ * Close a setup card, which owns none of the state above: the row-editor
2147
+ * and add cards each own one of those, so clearing them here would discard
2148
+ * a draft the user opened beside this card. Dismissal is this card's own —
2149
+ * the provider falls back to an ordinary row for the rest of the session,
2150
+ * and reopens through Edit.
2062
2151
  */
2063
2152
  const closeSetup = (changed, target) => {
2064
2153
  setDismissedSetup((previous) => new Set([...previous, target.provider]));
@@ -2111,11 +2200,38 @@ window.__ModuleLoader__.load({
2111
2200
  const anyUsable = state.rows.some(providerUsable);
2112
2201
  const configured = state.rows.filter((row) => row.configured);
2113
2202
  const configurable = state.rows.filter((row) => state.namespaces.has(row.entry.settingsNs));
2114
- const addable = configurable.filter((row) => !row.configured);
2115
- const addTarget = adding ? editing : void 0;
2116
- const addNamespace = addTarget === void 0 ? void 0 : state.namespaces.get(addTarget.settingsNs);
2117
- const addRow = addTarget === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === addTarget.provider);
2118
- const protocols = protocolChoices(state.namespaces.get("llm-pi-ai"), schema);
2203
+ const addable = state.rows.flatMap((row) => {
2204
+ const namespace = state.namespaces.get(row.entry.settingsNs);
2205
+ return namespace === void 0 || row.configured ? [] : [{
2206
+ row,
2207
+ namespace
2208
+ }];
2209
+ });
2210
+ const piAi = state.namespaces.get("llm-pi-ai");
2211
+ const protocols = protocolChoices(piAi, schema);
2212
+ const catalogOffered = configurable.length > 0;
2213
+ const catalogEnabled = addable.length > 0;
2214
+ const customOffered = piAi !== void 0;
2215
+ const customEnabled = protocols.length > 0;
2216
+ const claudeEnabled = customEnabled && state.namespaces.has(CLAUDE_PROVIDER_SETTINGS_NS);
2217
+ const bothOffered = customOffered;
2218
+ const mode = bothOffered ? addMode : customOffered ? "custom" : "catalog";
2219
+ const mounted = (candidate) => mode === candidate || visited.has(candidate);
2220
+ const switchLocked = catalogBusy || customBusy || claudeBusy;
2221
+ const draft = (() => {
2222
+ if (!addOpen || !catalogOffered) return void 0;
2223
+ const kept = editing === void 0 ? void 0 : state.namespaces.get(editing.settingsNs);
2224
+ if (editing !== void 0 && kept !== void 0) return {
2225
+ target: editing,
2226
+ namespace: kept
2227
+ };
2228
+ const first = addable[0];
2229
+ return first === void 0 ? void 0 : {
2230
+ target: targetOf(first.row),
2231
+ namespace: first.namespace
2232
+ };
2233
+ })();
2234
+ const addRow = draft === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === draft.target.provider);
2119
2235
  return (0, react_jsx_runtime.jsxs)("div", {
2120
2236
  className: ModelsSection_module_css_default["section"],
2121
2237
  children: [
@@ -2172,7 +2288,7 @@ window.__ModuleLoader__.load({
2172
2288
  }, { entryKey: row.entry.settingsNs })
2173
2289
  ]
2174
2290
  }, row.entry.provider);
2175
- const open = !adding && editing?.provider === row.entry.provider;
2291
+ const open = !addOpen && editing?.provider === row.entry.provider;
2176
2292
  const adaptiveClaude = isClaudeProvider(row, state);
2177
2293
  const credentialConfigured = row.credential?.configured === true;
2178
2294
  const credentialMissing = !credentialConfigured && row.apiKeyEnv !== void 0 && row.credential?.configured === false;
@@ -2216,8 +2332,7 @@ window.__ModuleLoader__.load({
2216
2332
  "aria-label": providerCopy(t("editProvider"), target),
2217
2333
  onClick: () => {
2218
2334
  setSavedTarget(void 0);
2219
- setDeclaringType(void 0);
2220
- setAdding(false);
2335
+ setAddOpen(false);
2221
2336
  setEditing(open ? void 0 : target);
2222
2337
  },
2223
2338
  children: t("edit")
@@ -2259,106 +2374,160 @@ window.__ModuleLoader__.load({
2259
2374
  }),
2260
2375
  (0, react_jsx_runtime.jsx)("div", {
2261
2376
  className: ModelsSection_module_css_default["addBlock"],
2262
- children: addTarget !== void 0 && addNamespace !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2377
+ children: addOpen ? (0, react_jsx_runtime.jsxs)("div", {
2263
2378
  className: ModelsSection_module_css_default["addCard"],
2264
2379
  children: [
2265
2380
  (0, react_jsx_runtime.jsxs)("div", {
2266
- className: ModelsSection_module_css_default["field"],
2267
- children: [(0, react_jsx_runtime.jsx)("span", {
2268
- className: ModelsSection_module_css_default["fieldLabel"],
2269
- children: t("provider")
2270
- }), (0, react_jsx_runtime.jsx)("select", {
2271
- className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
2272
- value: addTarget.provider,
2273
- "aria-label": t("provider"),
2274
- onChange: (event) => {
2275
- const row = addable.find((candidate) => candidate.entry.provider === event.target.value);
2276
- /* v8 ignore next -- the select only lists addable rows */
2277
- if (row === void 0) return;
2278
- setEditing(targetOf(row));
2279
- },
2280
- children: addable.map((row) => (0, react_jsx_runtime.jsx)("option", {
2281
- value: row.entry.provider,
2282
- children: row.entry.displayName
2283
- }, row.entry.provider))
2381
+ className: ModelsSection_module_css_default["addModes"],
2382
+ children: [bothOffered ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
2383
+ id: addId,
2384
+ label: t("addMode"),
2385
+ value: mode,
2386
+ disabled: switchLocked,
2387
+ options: [{
2388
+ value: "catalog",
2389
+ label: t("addCatalog"),
2390
+ disabled: !catalogEnabled,
2391
+ ...catalogEnabled ? {} : { title: t("addCatalogExhausted") }
2392
+ }, {
2393
+ value: "custom",
2394
+ label: t("addCustom"),
2395
+ disabled: !customEnabled,
2396
+ ...customEnabled ? {} : { title: t("addCustomUnavailable") }
2397
+ }, { value: "claude", label: t("claudeCustomTitle"), disabled: !claudeEnabled }],
2398
+ onChange: (next) => {
2399
+ setAddMode(next);
2400
+ setVisited((previous) => new Set([...previous, next]));
2401
+ }
2402
+ }) : (0, react_jsx_runtime.jsx)("div", {
2403
+ className: ModelsSection_module_css_default["editorHeader"],
2404
+ children: (0, react_jsx_runtime.jsx)("span", {
2405
+ className: ModelsSection_module_css_default["editorTitle"],
2406
+ children: t(mode === "catalog" ? "addCatalog" : "addCustom")
2407
+ })
2408
+ }), (0, react_jsx_runtime.jsx)("p", {
2409
+ hidden: mode === "claude",
2410
+ className: ModelsSection_module_css_default["advancedHint"],
2411
+ children: t(mode === "catalog" ? "addCatalogHint" : "addCustomHint")
2284
2412
  })]
2285
2413
  }),
2286
- (0, react_jsx_runtime.jsx)(ProviderEditor, {
2287
- provider: addTarget.provider,
2288
- displayName: addTarget.displayName,
2289
- hideTitle: true,
2290
- namespace: addNamespace,
2291
- schema,
2292
- settingsPath: addTarget.settingsPath,
2293
- operations,
2294
- t,
2295
- readOnly: !state.writable,
2296
- onClose: (changed) => {
2297
- closeEditor(changed, addTarget);
2298
- }
2299
- }, addTarget.provider),
2300
- addRow === void 0 ? null : renderSlot("settings.models.provider-card", {
2301
- provider: addRow.entry,
2302
- configured: addRow.configured,
2303
- keyConfigured: keyConfiguredOf(addRow)
2304
- }, { entryKey: addRow.entry.settingsNs })
2414
+ mounted("catalog") && draft !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2415
+ id: `${addId}-catalog-panel`,
2416
+ ...bothOffered ? {
2417
+ role: "tabpanel",
2418
+ "aria-labelledby": `${addId}-catalog`
2419
+ } : {},
2420
+ hidden: mode !== "catalog",
2421
+ className: ModelsSection_module_css_default["addPanel"],
2422
+ children: [
2423
+ (0, react_jsx_runtime.jsxs)("div", {
2424
+ className: ModelsSection_module_css_default["field"],
2425
+ children: [(0, react_jsx_runtime.jsx)("span", {
2426
+ className: ModelsSection_module_css_default["fieldLabel"],
2427
+ children: t("provider")
2428
+ }), (0, react_jsx_runtime.jsx)("select", {
2429
+ className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
2430
+ value: draft.target.provider,
2431
+ "aria-label": t("provider"),
2432
+ disabled: catalogBusy,
2433
+ onChange: (event) => {
2434
+ const picked = addable.find((candidate) => candidate.row.entry.provider === event.target.value);
2435
+ /* v8 ignore next -- the select only lists addable rows */
2436
+ if (picked === void 0) return;
2437
+ setEditing(targetOf(picked.row));
2438
+ },
2439
+ children: addable.map(({ row }) => (0, react_jsx_runtime.jsx)("option", {
2440
+ value: row.entry.provider,
2441
+ children: row.entry.displayName
2442
+ }, row.entry.provider))
2443
+ })]
2444
+ }),
2445
+ (0, react_jsx_runtime.jsx)(ProviderEditor, {
2446
+ provider: draft.target.provider,
2447
+ displayName: draft.target.displayName,
2448
+ hideTitle: true,
2449
+ namespace: draft.namespace,
2450
+ schema,
2451
+ settingsPath: draft.target.settingsPath,
2452
+ operations,
2453
+ t,
2454
+ readOnly: !state.writable,
2455
+ onClose: (changed) => {
2456
+ closeEditor(changed, draft.target);
2457
+ },
2458
+ onBusyChange: setCatalogBusy
2459
+ }, draft.target.provider),
2460
+ addRow === void 0 ? null : renderSlot("settings.models.provider-card", {
2461
+ provider: addRow.entry,
2462
+ configured: addRow.configured,
2463
+ keyConfigured: keyConfiguredOf(addRow)
2464
+ }, { entryKey: addRow.entry.settingsNs })
2465
+ ]
2466
+ }) : null,
2467
+ mounted("custom") && piAi !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2468
+ id: `${addId}-custom-panel`,
2469
+ ...bothOffered ? {
2470
+ role: "tabpanel",
2471
+ "aria-labelledby": `${addId}-custom`
2472
+ } : {},
2473
+ hidden: mode !== "custom",
2474
+ className: ModelsSection_module_css_default["addPanel"],
2475
+ children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2476
+ taken: state.rows.map((row) => row.entry.provider),
2477
+ protocols,
2478
+ revision: piAi.revision,
2479
+ operations,
2480
+ t,
2481
+ readOnly: !state.writable,
2482
+ onClose: (changed) => {
2483
+ closeAdd();
2484
+ if (changed) controller.load();
2485
+ },
2486
+ onBusyChange: setCustomBusy
2487
+ })
2488
+ }) : null,
2489
+ mounted("claude") && piAi !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2490
+ id: `${addId}-claude-panel`,
2491
+ ...bothOffered ? {
2492
+ role: "tabpanel",
2493
+ "aria-labelledby": `${addId}-claude`
2494
+ } : {},
2495
+ hidden: mode !== "claude",
2496
+ className: ModelsSection_module_css_default["addPanel"],
2497
+ children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2498
+ providerType: CLAUDE_PROVIDER_TYPE,
2499
+ taken: state.rows.map((row) => row.entry.provider),
2500
+ protocols,
2501
+ revision: piAi.revision,
2502
+ operations,
2503
+ t,
2504
+ readOnly: !state.writable,
2505
+ onClose: (changed) => {
2506
+ closeAdd();
2507
+ if (changed) controller.load();
2508
+ },
2509
+ onBusyChange: setClaudeBusy
2510
+ })
2511
+ }) : null
2305
2512
  ]
2306
- }) : declaringType !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2307
- className: ModelsSection_module_css_default["addCard"],
2308
- children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2309
- taken: state.rows.map((row) => row.entry.provider),
2310
- protocols,
2311
- providerType: declaringType,
2312
- /* v8 ignore next -- the card only opens from a button disabled without this namespace */
2313
- revision: state.namespaces.get("llm-pi-ai")?.revision ?? 0,
2314
- operations,
2315
- t,
2316
- readOnly: !state.writable,
2317
- onClose: (changed) => {
2318
- setDeclaringType(void 0);
2319
- if (changed) controller.load();
2320
- }
2321
- })
2322
- }) : (0, react_jsx_runtime.jsxs)("div", {
2513
+ }) : catalogOffered || customOffered ? (0, react_jsx_runtime.jsx)("div", {
2323
2514
  className: ModelsSection_module_css_default["addActions"],
2324
- children: [configurable.length > 0 && (0, react_jsx_runtime.jsxs)("button", {
2515
+ children: (0, react_jsx_runtime.jsxs)("button", {
2325
2516
  type: "button",
2326
2517
  className: ModelsSection_module_css_default["addButton"],
2327
- disabled: addable.length === 0 || !state.writable,
2518
+ disabled: !state.writable || !catalogEnabled && !customEnabled,
2328
2519
  onClick: () => {
2329
2520
  const first = addable[0];
2330
- /* v8 ignore next -- the button is disabled while nothing is addable */
2331
- if (first === void 0) return;
2521
+ const initial = catalogEnabled ? "catalog" : "custom";
2332
2522
  setSavedTarget(void 0);
2333
- setDeclaringType(void 0);
2334
- setAdding(true);
2335
- setEditing(targetOf(first));
2523
+ setEditing(first === void 0 ? void 0 : targetOf(first.row));
2524
+ setAddMode(initial);
2525
+ setVisited(new Set([initial]));
2526
+ setAddOpen(true);
2336
2527
  },
2337
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("add")]
2338
- }), state.namespaces.has("llm-pi-ai") && (0, react_jsx_runtime.jsxs)("button", {
2339
- type: "button",
2340
- className: ModelsSection_module_css_default["addButton"],
2341
- disabled: protocols.length === 0 || !state.writable,
2342
- onClick: () => {
2343
- setSavedTarget(void 0);
2344
- setAdding(false);
2345
- setEditing(void 0);
2346
- setDeclaringType("generic");
2347
- },
2348
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("customAdd")]
2349
- }), (0, react_jsx_runtime.jsxs)("button", {
2350
- type: "button",
2351
- className: ModelsSection_module_css_default["addButton"],
2352
- disabled: !state.namespaces.has("llm-pi-ai") || !state.namespaces.has(CLAUDE_PROVIDER_SETTINGS_NS) || !state.writable,
2353
- onClick: () => {
2354
- setSavedTarget(void 0);
2355
- setAdding(false);
2356
- setEditing(void 0);
2357
- setDeclaringType(CLAUDE_PROVIDER_TYPE);
2358
- },
2359
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("claudeCustomAdd")]
2360
- })]
2361
- })
2528
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("add")]
2529
+ })
2530
+ }) : null
2362
2531
  }),
2363
2532
  renderSlot("settings.models.footer", {}),
2364
2533
  (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
@@ -2391,7 +2560,7 @@ window.__ModuleLoader__.load({
2391
2560
  }
2392
2561
  //#endregion
2393
2562
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/OnboardingModal.module.css.mjs
2394
- 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}}";
2563
+ 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}}";
2395
2564
  const tagId$2 = "@mofeng2223/dsh-claude-provider/OnboardingModal.module.css";
2396
2565
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
2397
2566
  const tag = document.createElement("style");
@@ -2486,20 +2655,34 @@ window.__ModuleLoader__.load({
2486
2655
  * @returns the onboarding modal or null when onboarding needs no intervention.
2487
2656
  */
2488
2657
  function DeepSeekOnboardingDialog(props) {
2489
- const { complete, controller, useModels, operations, schema, t } = props;
2658
+ const { complete, controller, useModels, operations, schema, t, renderSlot, automatic, explicit = false } = props;
2659
+ const [apiKey, setApiKey] = (0, react.useState)(explicit);
2490
2660
  const state = useModels((snapshot) => snapshot);
2491
2661
  const readiness = onboardingReadiness(state);
2492
2662
  (0, react.useEffect)(() => {
2493
- if (state.status === "idle") controller.load();
2494
- }, [controller, state.status]);
2663
+ if ((automatic || explicit) && state.status === "idle") controller.load();
2664
+ }, [
2665
+ controller,
2666
+ state.status,
2667
+ automatic,
2668
+ explicit
2669
+ ]);
2495
2670
  (0, react.useEffect)(() => {
2496
- if (readiness.kind === "adapter-absent" || readiness.kind === "provider-ready" || readiness.kind === "unavailable") complete();
2497
- }, [complete, readiness.kind]);
2671
+ if (!automatic && !explicit || readiness.kind === "adapter-absent" || !explicit && readiness.kind === "provider-ready" || readiness.kind === "unavailable") complete();
2672
+ }, [
2673
+ complete,
2674
+ readiness.kind,
2675
+ explicit,
2676
+ automatic
2677
+ ]);
2678
+ if (!automatic && !explicit) return null;
2498
2679
  switch (readiness.kind) {
2499
2680
  case "loading":
2500
2681
  case "adapter-absent":
2501
- case "provider-ready":
2502
2682
  case "unavailable": return null;
2683
+ case "provider-ready":
2684
+ if (!explicit) return null;
2685
+ break;
2503
2686
  case "credential-missing": break;
2504
2687
  /* v8 ignore next -- every current readiness variant is handled above */
2505
2688
  default: return assertNever$1(readiness);
@@ -2515,7 +2698,7 @@ window.__ModuleLoader__.load({
2515
2698
  }
2516
2699
  controller.load();
2517
2700
  };
2518
- return (0, react_jsx_runtime.jsxs)(OnboardingModal, {
2701
+ const editor = (0, react_jsx_runtime.jsxs)(OnboardingModal, {
2519
2702
  title: t("onboardingTitle"),
2520
2703
  children: [(0, react_jsx_runtime.jsx)("p", {
2521
2704
  className: DeepSeekOnboardingDialog_module_css_default.description,
@@ -2542,6 +2725,12 @@ window.__ModuleLoader__.load({
2542
2725
  })
2543
2726
  })]
2544
2727
  });
2728
+ return apiKey ? editor : renderSlot("settings.models.sign-in", {
2729
+ complete,
2730
+ useApiKey: () => {
2731
+ setApiKey(true);
2732
+ }
2733
+ }, { fallback: editor });
2545
2734
  }
2546
2735
  //#endregion
2547
2736
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/WelcomeNotice.module.css.mjs
@@ -2619,7 +2808,7 @@ window.__ModuleLoader__.load({
2619
2808
  //#endregion
2620
2809
  //#region lib/types/onboarding-copy.js
2621
2810
  /** Durable settings namespace for product-wide GUI onboarding facts. */
2622
- const WELCOME_NOTICE_SETTINGS_NAMESPACE = "ui-onboarding";
2811
+ const WELCOME_NOTICE_SETTINGS_NAMESPACE = "ui-settings-general";
2623
2812
  /** Field storing the last welcome notice version the user acknowledged. */
2624
2813
  const WELCOME_NOTICE_ACK_FIELD = "welcomeNoticeVersion";
2625
2814
  /**
@@ -2635,16 +2824,6 @@ window.__ModuleLoader__.load({
2635
2824
  * remote browser's memory-mode scope never answers and the acknowledgement
2636
2825
  * stays process-local here.
2637
2826
  */
2638
- /**
2639
- * Accept any object section verbatim; a malformed durable value reads as an
2640
- * empty section, so the notice treats it as unacknowledged instead of leaving
2641
- * the scope stuck on its previous value.
2642
- * @param section - the wire section value.
2643
- * @returns the section object, or an empty one for non-object values.
2644
- */
2645
- function decodeWelcomeSection(section) {
2646
- return typeof section === "object" && section !== null && !Array.isArray(section) ? section : {};
2647
- }
2648
2827
  /* v8 ignore next 3 -- closed-union default only defends future source widening */
2649
2828
  function assertNever(_value) {
2650
2829
  throw new Error("unexpected welcome settings status");
@@ -2843,7 +3022,14 @@ window.__ModuleLoader__.load({
2843
3022
  deleteDescriptionWithCredential: "Deleting {provider} removes its configuration and stored API key.",
2844
3023
  deleteConfirm: "Delete {provider}",
2845
3024
  deleting: "Deleting {provider}…",
2846
- add: "Add provider",
3025
+ add: "Add model provider",
3026
+ addMode: "How to add",
3027
+ addCatalog: "Third-party providers",
3028
+ addCustom: "Custom API",
3029
+ addCatalogHint: "Pick OpenAI, Anthropic, Kimi, or another provider from the built-in catalog and enter its API key.",
3030
+ addCustomHint: "Connect a relay, a self-hosted server, or any other OpenAI- or Anthropic-compatible endpoint by its base URL, protocol, and models.",
3031
+ addCatalogExhausted: "Every catalog provider is already configured.",
3032
+ addCustomUnavailable: "No API protocol is available to declare.",
2847
3033
  provider: "Provider",
2848
3034
  close: "Close",
2849
3035
  cancel: "Cancel",
@@ -2864,9 +3050,8 @@ window.__ModuleLoader__.load({
2864
3050
  customized: "Customized settings",
2865
3051
  baseUrl: "Base URL",
2866
3052
  baseUrlDefault: "Provider default",
2867
- deepSeekChatBaseUrl: "https://api.deepseek.com",
2868
- deepSeekMessagesBaseUrl: "https://api.deepseek.com/anthropic",
2869
- deepSeekEndpointHint: "Use an endpoint compatible with the configured connection.",
3053
+ deepSeekBaseUrl: "https://api.deepseek.com/anthropic",
3054
+ deepSeekEndpointHint: "Use an API endpoint compatible with Anthropic Messages.",
2870
3055
  models: "Models",
2871
3056
  modelsInherited: "Using the adapter defaults",
2872
3057
  modelsCustomized: "Customized model catalog",
@@ -2879,7 +3064,10 @@ window.__ModuleLoader__.load({
2879
3064
  contextWindowPlaceholder: "Uses the provider default",
2880
3065
  maxTokens: "Max output tokens",
2881
3066
  maxTokensPlaceholder: "Uses the provider default",
2882
- modelAdvanced: "Capacities",
3067
+ modelAdvanced: "Model options",
3068
+ modelInputTypes: "Input types",
3069
+ modelInputText: "Text",
3070
+ modelInputImage: "Image",
2883
3071
  addModel: "Add model",
2884
3072
  removeModel: "Delete model",
2885
3073
  modelsEmpty: "No models will be shown in the selector. Unlisted IDs can still be sent directly.",
@@ -2891,11 +3079,9 @@ window.__ModuleLoader__.load({
2891
3079
  modelNameInvalid: "Display name cannot be empty.",
2892
3080
  modelContextInvalid: "Context window must be a positive count, like 131072, 256K, or 1M.",
2893
3081
  modelMaxTokensInvalid: "Max output tokens must be a positive count, like 8192, 64K, or 1M.",
2894
- advancedHint: "Other fields live in settings.yaml; edit that section directly.",
3082
+ advancedHint: "Other fields live in cordis.patch.yml; edit that section directly.",
2895
3083
  modelCapacityInvalid: "A capacity must be a number, optionally suffixed K or M.",
2896
3084
  modelDuplicate: "Each model ID may appear once.",
2897
- modelContextWindow: "Context window",
2898
- modelMaxTokens: "Max output tokens",
2899
3085
  fetchModels: "Fetch available models",
2900
3086
  fetching: "Asking the provider…",
2901
3087
  fetchNeedsBaseUrl: "Enter the base URL first, then fetch.",
@@ -2907,8 +3093,6 @@ window.__ModuleLoader__.load({
2907
3093
  fetchSelectAll: "Select all",
2908
3094
  fetchDeselectAll: "Deselect all",
2909
3095
  fetchAdopt: "Add selected",
2910
- customAdd: "Add a custom provider",
2911
- customTitle: "Custom provider",
2912
3096
  customTag: "Custom",
2913
3097
  claudeCustomAdd: "Add Claude provider",
2914
3098
  claudeCustomTitle: "Claude provider",
@@ -2930,10 +3114,14 @@ window.__ModuleLoader__.load({
2930
3114
  customDisplayName: "Display name",
2931
3115
  customApi: "API protocol",
2932
3116
  customApiUnset: "Not selected",
3117
+ protocolOpenAiCompletions: "OpenAI Chat Completions",
3118
+ protocolOpenAiResponses: "OpenAI Responses",
3119
+ protocolAnthropicMessages: "Anthropic Messages",
2933
3120
  customNeedsBaseUrl: "A custom provider needs a base URL.",
2934
3121
  customBaseUrlInvalid: "Enter a valid HTTP or HTTPS URL.",
2935
3122
  customNeedsModels: "A custom provider needs at least one model.",
2936
3123
  customBaseUrlPlaceholder: "https://gateway.example/v1",
3124
+ customAnthropicBaseUrlPlaceholder: "https://gateway.example",
2937
3125
  settingsPathUnresolvable: "unresolvable settings path",
2938
3126
  create: "Create provider",
2939
3127
  creating: "Creating…",
@@ -2952,7 +3140,7 @@ window.__ModuleLoader__.load({
2952
3140
  const zh = {
2953
3141
  nav: "模型",
2954
3142
  title: "模型",
2955
- intro: "填入各提供方的 API 密钥即可使用其模型。",
3143
+ intro: "填入各提供商的 API 密钥即可使用其模型。",
2956
3144
  edit: "编辑",
2957
3145
  editProvider: "编辑 {provider}",
2958
3146
  remove: "删除",
@@ -2962,8 +3150,15 @@ window.__ModuleLoader__.load({
2962
3150
  deleteDescriptionWithCredential: "删除 {provider} 会移除其配置和存储的 API 密钥。",
2963
3151
  deleteConfirm: "删除 {provider}",
2964
3152
  deleting: "正在删除 {provider}…",
2965
- add: "添加提供方",
2966
- provider: "提供方",
3153
+ add: "添加模型提供商",
3154
+ addMode: "添加方式",
3155
+ addCatalog: "第三方模型提供商",
3156
+ addCustom: "自定义模型 API",
3157
+ addCatalogHint: "从内置目录中选择 OpenAI、Anthropic、Kimi 等提供商,填入其 API 密钥即可使用。",
3158
+ addCustomHint: "连接中转站、自部署服务或其他兼容 OpenAI / Anthropic 协议的接口,需填写 API 地址、协议和模型。",
3159
+ addCatalogExhausted: "目录中的提供商都已添加。",
3160
+ addCustomUnavailable: "没有可用的 API 协议。",
3161
+ provider: "提供商",
2967
3162
  close: "关闭",
2968
3163
  cancel: "取消",
2969
3164
  apply: "保存",
@@ -2972,7 +3167,7 @@ window.__ModuleLoader__.load({
2972
3167
  credentialConfigured: "API 密钥已配置",
2973
3168
  credentialMissing: "API 密钥缺失",
2974
3169
  readOnly: "当前部署的设置文档为只读。",
2975
- loadFailed: "加载提供方目录失败",
3170
+ loadFailed: "加载提供商目录失败",
2976
3171
  conflict: "这张卡片打开期间,这些设置已被其他地方改动。请关闭后重新打开,在当前值上编辑。",
2977
3172
  retry: "重试",
2978
3173
  keyInput: "API 密钥",
@@ -2982,10 +3177,9 @@ window.__ModuleLoader__.load({
2982
3177
  keyEnvLocked: "由启动环境提供(只读)",
2983
3178
  customized: "自定义设置",
2984
3179
  baseUrl: "API 地址",
2985
- baseUrlDefault: "提供方默认",
2986
- deepSeekChatBaseUrl: "https://api.deepseek.com",
2987
- deepSeekMessagesBaseUrl: "https://api.deepseek.com/anthropic",
2988
- deepSeekEndpointHint: "请填写与当前连接配置兼容的 API 地址。",
3180
+ baseUrlDefault: "提供商默认",
3181
+ deepSeekBaseUrl: "https://api.deepseek.com/anthropic",
3182
+ deepSeekEndpointHint: "请填写兼容 Anthropic Messages 协议的 API 地址。",
2989
3183
  models: "模型目录",
2990
3184
  modelsInherited: "正在使用适配器默认模型",
2991
3185
  modelsCustomized: "已自定义模型目录",
@@ -2995,39 +3189,38 @@ window.__ModuleLoader__.load({
2995
3189
  modelName: "显示名称",
2996
3190
  modelNamePlaceholder: "留空时使用模型 ID",
2997
3191
  contextWindow: "上下文窗口",
2998
- contextWindowPlaceholder: "使用提供方默认值",
3192
+ contextWindowPlaceholder: "使用提供商默认值",
2999
3193
  maxTokens: "最大输出 token 数",
3000
- maxTokensPlaceholder: "使用提供方默认值",
3001
- modelAdvanced: "容量",
3194
+ maxTokensPlaceholder: "使用提供商默认值",
3195
+ modelAdvanced: "模型选项",
3196
+ modelInputTypes: "输入类型",
3197
+ modelInputText: "文本",
3198
+ modelInputImage: "图片",
3002
3199
  addModel: "添加模型",
3003
3200
  removeModel: "删除模型",
3004
3201
  modelsEmpty: "模型选择器中将不显示任何模型;目录外 ID 仍可直接发送。",
3005
3202
  keyBlank: "请输入 API 密钥;留空则保持已存储的密钥。",
3006
- keyBlankNew: "请输入 API 密钥;若该提供方以其他方式鉴权,可以留空。",
3203
+ keyBlankNew: "请输入 API 密钥;若该提供商以其他方式鉴权,可以留空。",
3007
3204
  keyIllegalCharacters: "该 API 密钥格式错误,请检查。",
3008
3205
  modelIdRequired: "模型 ID 不能为空。",
3009
3206
  modelIdDuplicate: "模型 ID 不能重复。",
3010
3207
  modelNameInvalid: "显示名称不能为空。",
3011
3208
  modelContextInvalid: "上下文窗口必须是正数,例如 131072、256K 或 1M。",
3012
3209
  modelMaxTokensInvalid: "最大输出 token 数必须是正数,例如 8192、64K 或 1M。",
3013
- advancedHint: "其余字段在 settings.yaml 中,请直接编辑对应段。",
3210
+ advancedHint: "其余字段在 cordis.patch.yml 中,请直接编辑对应段。",
3014
3211
  modelCapacityInvalid: "容量需为数字,可加 K 或 M 后缀。",
3015
3212
  modelDuplicate: "每个模型 ID 只能出现一次。",
3016
- modelContextWindow: "上下文窗口",
3017
- modelMaxTokens: "最大输出 token",
3018
3213
  fetchModels: "获取可用模型",
3019
- fetching: "正在询问提供方…",
3214
+ fetching: "正在询问提供商…",
3020
3215
  fetchNeedsBaseUrl: "请先填写 API 地址,再获取。",
3021
- fetchEmpty: "该提供方没有列出任何模型,请手动添加。",
3216
+ fetchEmpty: "该提供商没有列出任何模型,请手动添加。",
3022
3217
  fetchTitle: "选择要添加的模型",
3023
- fetchDescription: "以下是模型提供方的可用模型,勾选要添加的模型。",
3218
+ fetchDescription: "以下是模型提供商的可用模型,勾选要添加的模型。",
3024
3219
  fetchSearch: "搜索模型",
3025
3220
  fetchNoMatches: "没有匹配的模型。",
3026
3221
  fetchSelectAll: "全选",
3027
3222
  fetchDeselectAll: "取消全选",
3028
3223
  fetchAdopt: "添加所选",
3029
- customAdd: "添加自定义提供方",
3030
- customTitle: "自定义提供方",
3031
3224
  customTag: "自定义",
3032
3225
  claudeCustomAdd: "添加 Claude 提供方",
3033
3226
  claudeCustomTitle: "Claude 提供方",
@@ -3043,18 +3236,22 @@ window.__ModuleLoader__.load({
3043
3236
  thinkingPresetFour: "四档",
3044
3237
  thinkingPresetFive: "五档(默认)",
3045
3238
  customRoute: "Provider ID",
3046
- customRouteHint: "以小写字母开头的标识,在请求中唯一标识该提供方,并用于派生凭据名。",
3239
+ customRouteHint: "以小写字母开头的标识,在请求中唯一标识该提供商,并用于派生凭据名。",
3047
3240
  customRouteInvalid: "需以小写字母开头,之后可用小写字母、数字和短横线。",
3048
- customRouteTaken: "已有提供方使用了这个 ID。",
3241
+ customRouteTaken: "已有提供商使用了这个 ID。",
3049
3242
  customDisplayName: "显示名称",
3050
3243
  customApi: "API 协议",
3051
3244
  customApiUnset: "未选择",
3052
- customNeedsBaseUrl: "自定义提供方需要填写 API 地址。",
3245
+ protocolOpenAiCompletions: "OpenAI Chat Completions",
3246
+ protocolOpenAiResponses: "OpenAI Responses",
3247
+ protocolAnthropicMessages: "Anthropic Messages",
3248
+ customNeedsBaseUrl: "自定义模型 API 需要填写 API 地址。",
3053
3249
  customBaseUrlInvalid: "请输入有效的 HTTP 或 HTTPS 地址。",
3054
- customNeedsModels: "自定义提供方至少需要一个模型。",
3250
+ customNeedsModels: "自定义模型 API 至少需要一个模型。",
3055
3251
  customBaseUrlPlaceholder: "https://gateway.example/v1",
3252
+ customAnthropicBaseUrlPlaceholder: "https://gateway.example",
3056
3253
  settingsPathUnresolvable: "无法解析设置路径",
3057
- create: "创建提供方",
3254
+ create: "创建提供商",
3058
3255
  creating: "创建中…",
3059
3256
  welcomeTitle: "内测声明",
3060
3257
  welcomeBody: "DeepSeek Harness 目前的 0.1 版本仍处在面向 Harness 开发者进行测试的阶段,还有许多地方需要持续改进和打磨,希望听取广大开发者的反馈建议。预计 DeepSeek Harness 的核心插件以及基础 API 都会在接下来的一段时间内快速迭代、持续演化。\n\n我们期待与全球开发者一起,在开源、开放、可复用、可组合的基础设施之上,共同探索智能上限。欢迎全球 Harness 开发者加入 DSH 插件生态。",
@@ -3068,6 +3265,904 @@ window.__ModuleLoader__.load({
3068
3265
  keyRequired: "请输入 API 密钥后继续。"
3069
3266
  };
3070
3267
  //#endregion
3268
+ //#region ../../../vendor/cosmokit/src/misc.ts
3269
+ /** Return true when a value is `null` or `undefined`. */
3270
+ function isNullable(value) {
3271
+ return value === null || value === void 0;
3272
+ }
3273
+ /** Return true for non-array object values. */
3274
+ function isPlainObject(data) {
3275
+ return data && typeof data === "object" && !Array.isArray(data);
3276
+ }
3277
+ /** Filter object entries and return a new object. */
3278
+ function filterKeys(object, filter) {
3279
+ return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
3280
+ }
3281
+ /** Map object values while preserving the original key set. */
3282
+ function mapValues(object, transform) {
3283
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
3284
+ }
3285
+ /** Pick selected keys from an object, optionally including `undefined` values. */
3286
+ function pick(source, keys, forced) {
3287
+ if (!keys) return { ...source };
3288
+ const result = {};
3289
+ for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
3290
+ return result;
3291
+ }
3292
+ //#endregion
3293
+ //#region ../../../vendor/cosmokit/src/volatile.ts
3294
+ /** Shared config references used by schema validators and plugin runtimes. */
3295
+ const write = Symbol.for("cosmokit.volatile.write");
3296
+ function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
3297
+ if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
3298
+ if (value === null || typeof value !== "object") return value;
3299
+ if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
3300
+ ancestors.add(value);
3301
+ try {
3302
+ if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
3303
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
3304
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
3305
+ } finally {
3306
+ ancestors.delete(value);
3307
+ }
3308
+ }
3309
+ /**
3310
+ * Create a detached reference containing an immutable copy of the supplied data.
3311
+ * @param value - validated config data; class instances and functions are unsupported.
3312
+ * @returns a reference whose value is updated only by its owning runtime.
3313
+ */
3314
+ function createVolatile(value) {
3315
+ let current = snapshot(value);
3316
+ return Object.freeze({
3317
+ get: () => current,
3318
+ [write]: (value) => {
3319
+ current = value;
3320
+ }
3321
+ });
3322
+ }
3323
+ /**
3324
+ * Identify references across ESM/CJS copies of the shared library.
3325
+ * @param value - a parsed config value.
3326
+ * @returns whether the value implements the shared reference protocol.
3327
+ */
3328
+ function isVolatile(value) {
3329
+ return typeof value === "object" && value !== null && write in value;
3330
+ }
3331
+ //#endregion
3332
+ //#region ../../../vendor/cosmokit/src/types.ts
3333
+ /** Test values using `instanceof` with a `toStringTag` fallback. */
3334
+ function is(type, value) {
3335
+ if (arguments.length === 1) return (value) => is(type, value);
3336
+ return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
3337
+ }
3338
+ function isArrayBufferLike(value) {
3339
+ return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
3340
+ }
3341
+ function isArrayBufferSource(value) {
3342
+ return isArrayBufferLike(value) || ArrayBuffer.isView(value);
3343
+ }
3344
+ let Binary;
3345
+ (function(_Binary) {
3346
+ _Binary.is = isArrayBufferLike;
3347
+ _Binary.isSource = isArrayBufferSource;
3348
+ function fromSource(source) {
3349
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
3350
+ else return source;
3351
+ }
3352
+ _Binary.fromSource = fromSource;
3353
+ function toBase64(source) {
3354
+ source = fromSource(source);
3355
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
3356
+ let binary = "";
3357
+ const bytes = new Uint8Array(source);
3358
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
3359
+ return btoa(binary);
3360
+ }
3361
+ _Binary.toBase64 = toBase64;
3362
+ function fromBase64(source) {
3363
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
3364
+ return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
3365
+ }
3366
+ _Binary.fromBase64 = fromBase64;
3367
+ function toHex(source) {
3368
+ source = fromSource(source);
3369
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
3370
+ return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
3371
+ }
3372
+ _Binary.toHex = toHex;
3373
+ function fromHex(source) {
3374
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
3375
+ const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
3376
+ const buffer = [];
3377
+ for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
3378
+ return Uint8Array.from(buffer).buffer;
3379
+ }
3380
+ _Binary.fromHex = fromHex;
3381
+ })(Binary || (Binary = {}));
3382
+ Binary.fromBase64;
3383
+ Binary.toBase64;
3384
+ Binary.fromHex;
3385
+ Binary.toHex;
3386
+ /** Deep-clone common JavaScript values while preserving prototypes and cycles. */
3387
+ function clone(source, refs = /* @__PURE__ */ new Map()) {
3388
+ if (!source || typeof source !== "object") return source;
3389
+ if (is("Date", source)) return new Date(source.valueOf());
3390
+ if (is("RegExp", source)) return new RegExp(source.source, source.flags);
3391
+ if (isArrayBufferLike(source)) return source.slice(0);
3392
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
3393
+ const cached = refs.get(source);
3394
+ if (cached) return cached;
3395
+ if (Array.isArray(source)) {
3396
+ const result = [];
3397
+ refs.set(source, result);
3398
+ source.forEach((value, index) => {
3399
+ result[index] = Reflect.apply(clone, null, [value, refs]);
3400
+ });
3401
+ return result;
3402
+ }
3403
+ const result = Object.create(Object.getPrototypeOf(source));
3404
+ refs.set(source, result);
3405
+ for (const key of Reflect.ownKeys(source)) {
3406
+ const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
3407
+ if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
3408
+ Reflect.defineProperty(result, key, descriptor);
3409
+ }
3410
+ return result;
3411
+ }
3412
+ /**
3413
+ * Compare values recursively, treating two volatile references as equal regardless of value.
3414
+ * Strict comparison distinguishes null/undefined, treats opaque objects by identity,
3415
+ * compares URLs by normalized href, treats array holes as undefined, and considers distinct cyclic structures unequal.
3416
+ * @param a - first value.
3417
+ * @param b - second value.
3418
+ * @param strict - whether to require strict data equality outside volatile references.
3419
+ * @returns whether the values compare equal.
3420
+ */
3421
+ function deepEqual(a, b, strict) {
3422
+ const ancestors = /* @__PURE__ */ new Set();
3423
+ function compare(a, b) {
3424
+ if (a === b) return true;
3425
+ if (isVolatile(a) || isVolatile(b)) return isVolatile(a) && isVolatile(b);
3426
+ if (!strict && isNullable(a) && isNullable(b)) return true;
3427
+ if (typeof a !== typeof b || typeof a !== "object" || !a || !b) return false;
3428
+ if (ancestors.has(a)) return false;
3429
+ function check(test, then) {
3430
+ return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
3431
+ }
3432
+ ancestors.add(a);
3433
+ try {
3434
+ return check(Array.isArray, (a, b) => {
3435
+ if (a.length !== b.length) return false;
3436
+ for (let index = 0; index < a.length; index++) if (!compare(a[index], b[index])) return false;
3437
+ return true;
3438
+ }) ?? 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) => {
3439
+ if (a.byteLength !== b.byteLength) return false;
3440
+ const viewA = new Uint8Array(a);
3441
+ const viewB = new Uint8Array(b);
3442
+ for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
3443
+ return true;
3444
+ }) ?? ((!strict || [a, b].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
3445
+ ...a,
3446
+ ...b
3447
+ }).every((key) => compare(a[key], b[key])));
3448
+ } finally {
3449
+ ancestors.delete(a);
3450
+ }
3451
+ }
3452
+ return compare(a, b);
3453
+ }
3454
+ //#endregion
3455
+ //#region ../../../vendor/cosmokit/src/time.ts
3456
+ let Time;
3457
+ (function(_Time) {
3458
+ _Time.millisecond = 1;
3459
+ const second = _Time.second = 1e3;
3460
+ const minute = _Time.minute = second * 60;
3461
+ const hour = _Time.hour = minute * 60;
3462
+ const day = _Time.day = hour * 24;
3463
+ const week = _Time.week = day * 7;
3464
+ let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
3465
+ function setTimezoneOffset(offset) {
3466
+ timezoneOffset = offset;
3467
+ }
3468
+ _Time.setTimezoneOffset = setTimezoneOffset;
3469
+ function getTimezoneOffset() {
3470
+ return timezoneOffset;
3471
+ }
3472
+ _Time.getTimezoneOffset = getTimezoneOffset;
3473
+ function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
3474
+ if (typeof date === "number") date = new Date(date);
3475
+ if (offset === void 0) offset = timezoneOffset;
3476
+ return Math.floor((date.valueOf() / minute - offset) / 1440);
3477
+ }
3478
+ _Time.getDateNumber = getDateNumber;
3479
+ function fromDateNumber(value, offset) {
3480
+ const date = new Date(value * day);
3481
+ if (offset === void 0) offset = timezoneOffset;
3482
+ return new Date(+date + offset * minute);
3483
+ }
3484
+ _Time.fromDateNumber = fromDateNumber;
3485
+ const numeric = /\d+(?:\.\d+)?/.source;
3486
+ const timeRegExp = new RegExp(`^${[
3487
+ "w(?:eek(?:s)?)?",
3488
+ "d(?:ay(?:s)?)?",
3489
+ "h(?:our(?:s)?)?",
3490
+ "m(?:in(?:ute)?(?:s)?)?",
3491
+ "s(?:ec(?:ond)?(?:s)?)?"
3492
+ ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
3493
+ function parseTime(source) {
3494
+ const capture = timeRegExp.exec(source);
3495
+ if (!capture) return 0;
3496
+ 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);
3497
+ }
3498
+ _Time.parseTime = parseTime;
3499
+ function parseDate(date) {
3500
+ const parsed = parseTime(date);
3501
+ if (parsed) date = Date.now() + parsed;
3502
+ else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
3503
+ else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
3504
+ return date ? new Date(date) : /* @__PURE__ */ new Date();
3505
+ }
3506
+ _Time.parseDate = parseDate;
3507
+ function format(ms) {
3508
+ const abs = Math.abs(ms);
3509
+ if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
3510
+ else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
3511
+ else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
3512
+ else if (abs >= second) return Math.round(ms / second) + "s";
3513
+ return ms + "ms";
3514
+ }
3515
+ _Time.format = format;
3516
+ function toDigits(source, length = 2) {
3517
+ return source.toString().padStart(length, "0");
3518
+ }
3519
+ _Time.toDigits = toDigits;
3520
+ function template(template, time = /* @__PURE__ */ new Date()) {
3521
+ 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));
3522
+ }
3523
+ _Time.template = template;
3524
+ })(Time || (Time = {}));
3525
+ //#endregion
3526
+ //#region ../../../vendor/schemastery/src/index.ts
3527
+ const kSchema = Symbol.for("schemastery");
3528
+ const kValidationError = Symbol.for("ValidationError");
3529
+ globalThis.__schemastery_index__ ??= 0;
3530
+ globalThis.__schemastery_refs__ = void 0;
3531
+ var ValidationError = class extends TypeError {
3532
+ options;
3533
+ name = "ValidationError";
3534
+ constructor(message, options) {
3535
+ let prefix = "$";
3536
+ for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
3537
+ else if (typeof segment === "number") prefix += "[" + segment + "]";
3538
+ else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
3539
+ if (prefix.startsWith(".")) prefix = prefix.slice(1);
3540
+ super((prefix === "$" ? "" : `${prefix} `) + message);
3541
+ this.options = options;
3542
+ }
3543
+ static is(error) {
3544
+ return !!error?.[kValidationError];
3545
+ }
3546
+ };
3547
+ Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
3548
+ const Schema = function(options) {
3549
+ const schema = function(data, options = {}) {
3550
+ return Schema.resolve(data, schema, options)[0];
3551
+ };
3552
+ if (options.refs) {
3553
+ const refs = mapValues(options.refs, (options) => new Schema(options));
3554
+ const getRef = (uid) => refs[uid];
3555
+ for (const key in refs) {
3556
+ const options = refs[key];
3557
+ options.sKey = getRef(options.sKey);
3558
+ options.inner = getRef(options.inner);
3559
+ options.list = options.list && options.list.map(getRef);
3560
+ options.dict = options.dict && mapValues(options.dict, getRef);
3561
+ }
3562
+ return refs[options.uid];
3563
+ }
3564
+ Object.assign(schema, options);
3565
+ if (typeof schema.callback === "string") try {
3566
+ schema.callback = new Function("return " + schema.callback)();
3567
+ } catch {}
3568
+ Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
3569
+ Object.setPrototypeOf(schema, Schema.prototype);
3570
+ schema.meta ||= {};
3571
+ schema.toString = schema.toString.bind(schema);
3572
+ return schema;
3573
+ };
3574
+ Schema.prototype = Object.create(Function.prototype);
3575
+ Schema.prototype[kSchema] = true;
3576
+ Object.defineProperty(Schema.prototype, "~standard", { get() {
3577
+ return {
3578
+ version: 1,
3579
+ vendor: "schemastery",
3580
+ validate: (value) => {
3581
+ try {
3582
+ return { value: Schema.resolve(value, this, {})[0] };
3583
+ } catch (error) {
3584
+ if (ValidationError.is(error)) return { issues: [{
3585
+ message: error.message,
3586
+ path: error.options.path
3587
+ }] };
3588
+ throw error;
3589
+ }
3590
+ }
3591
+ };
3592
+ } });
3593
+ Schema.ValidationError = ValidationError;
3594
+ Schema.prototype.toJSON = function toJSON() {
3595
+ if (globalThis.__schemastery_refs__) {
3596
+ globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
3597
+ return this.uid;
3598
+ }
3599
+ globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
3600
+ globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
3601
+ const result = {
3602
+ uid: this.uid,
3603
+ refs: globalThis.__schemastery_refs__
3604
+ };
3605
+ globalThis.__schemastery_refs__ = void 0;
3606
+ return result;
3607
+ };
3608
+ Schema.prototype.set = function set(key, value) {
3609
+ this.dict[key] = value;
3610
+ return this;
3611
+ };
3612
+ Schema.prototype.push = function push(value) {
3613
+ this.list.push(value);
3614
+ return this;
3615
+ };
3616
+ function mergeDesc(original, messages) {
3617
+ const result = typeof original === "string" ? { "": original } : { ...original };
3618
+ for (const locale in messages) {
3619
+ const value = messages[locale];
3620
+ if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
3621
+ else if (typeof value === "string") result[locale] = value;
3622
+ }
3623
+ return result;
3624
+ }
3625
+ function getInner(value) {
3626
+ return value?.$value ?? value?.$inner;
3627
+ }
3628
+ function extractKeys(data) {
3629
+ return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
3630
+ }
3631
+ Schema.prototype.i18n = function i18n(messages) {
3632
+ const schema = Schema(this);
3633
+ const desc = mergeDesc(schema.meta.description, messages);
3634
+ if (Object.keys(desc).length) schema.meta.description = desc;
3635
+ if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
3636
+ return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
3637
+ });
3638
+ if (schema.list) schema.list = schema.list.map((inner, index) => {
3639
+ return inner.i18n(mapValues(messages, (data = {}) => {
3640
+ if (Array.isArray(getInner(data))) return getInner(data)[index];
3641
+ if (Array.isArray(data)) return data[index];
3642
+ return extractKeys(data);
3643
+ }));
3644
+ });
3645
+ if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
3646
+ if (getInner(data)) return getInner(data);
3647
+ return extractKeys(data);
3648
+ }));
3649
+ if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
3650
+ return schema;
3651
+ };
3652
+ Schema.prototype.extra = function extra(key, value) {
3653
+ const schema = Schema(this);
3654
+ schema.meta = {
3655
+ ...schema.meta,
3656
+ [key]: value
3657
+ };
3658
+ return schema;
3659
+ };
3660
+ for (const key of [
3661
+ "required",
3662
+ "disabled",
3663
+ "collapse",
3664
+ "hidden",
3665
+ "loose"
3666
+ ]) Object.assign(Schema.prototype, { [key](value = true) {
3667
+ const schema = Schema(this);
3668
+ schema.meta = {
3669
+ ...schema.meta,
3670
+ [key]: value
3671
+ };
3672
+ return schema;
3673
+ } });
3674
+ Schema.prototype.deprecated = function deprecated() {
3675
+ const schema = Schema(this);
3676
+ schema.meta.badges ||= [];
3677
+ schema.meta.badges.push({
3678
+ text: "deprecated",
3679
+ type: "danger"
3680
+ });
3681
+ return schema;
3682
+ };
3683
+ Schema.prototype.experimental = function experimental() {
3684
+ const schema = Schema(this);
3685
+ schema.meta.badges ||= [];
3686
+ schema.meta.badges.push({
3687
+ text: "experimental",
3688
+ type: "warning"
3689
+ });
3690
+ return schema;
3691
+ };
3692
+ Schema.prototype.pattern = function pattern(regexp) {
3693
+ const schema = Schema(this);
3694
+ const pattern = pick(regexp, ["source", "flags"]);
3695
+ schema.meta = {
3696
+ ...schema.meta,
3697
+ pattern
3698
+ };
3699
+ return schema;
3700
+ };
3701
+ Schema.prototype.simplify = function simplify(value) {
3702
+ if (isVolatile(value)) value = value.get();
3703
+ if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
3704
+ if (isNullable(value)) return value;
3705
+ if (this.type === "object" || this.type === "dict") {
3706
+ const result = {};
3707
+ for (const key in value) {
3708
+ const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
3709
+ if (this.type === "dict" || !isNullable(item)) result[key] = item;
3710
+ }
3711
+ if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
3712
+ return result;
3713
+ } else if (this.type === "array" || this.type === "tuple") {
3714
+ const result = [];
3715
+ value.forEach((value, index) => {
3716
+ const schema = this.type === "array" ? this.inner : this.list[index];
3717
+ const item = schema ? schema.simplify(value) : value;
3718
+ result.push(item);
3719
+ });
3720
+ return result;
3721
+ } else if (this.type === "intersect") {
3722
+ const result = {};
3723
+ for (const item of this.list) Object.assign(result, item.simplify(value));
3724
+ return result;
3725
+ } else if (this.type === "union") for (const schema of this.list) try {
3726
+ Schema.resolve(value, schema, {});
3727
+ return schema.simplify(value);
3728
+ } catch {}
3729
+ return value;
3730
+ };
3731
+ Schema.prototype.toString = function toString(inline) {
3732
+ return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
3733
+ };
3734
+ Schema.prototype.role = function role(role, extra) {
3735
+ const schema = Schema(this);
3736
+ schema.meta = {
3737
+ ...schema.meta,
3738
+ role,
3739
+ extra
3740
+ };
3741
+ return schema;
3742
+ };
3743
+ for (const key of [
3744
+ "default",
3745
+ "link",
3746
+ "comment",
3747
+ "description",
3748
+ "max",
3749
+ "min",
3750
+ "step"
3751
+ ]) Object.assign(Schema.prototype, { [key](value) {
3752
+ const schema = Schema(this);
3753
+ schema.meta = {
3754
+ ...schema.meta,
3755
+ [key]: value
3756
+ };
3757
+ return schema;
3758
+ } });
3759
+ Schema.prototype.volatile = function volatile() {
3760
+ if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
3761
+ return this.extra("volatile", true);
3762
+ };
3763
+ const resolvers = {};
3764
+ const checkedVolatile = Symbol("checked-volatile-schema");
3765
+ function validateVolatileSchema(schema, path = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
3766
+ const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
3767
+ if (states.has(blocked)) return;
3768
+ states.add(blocked);
3769
+ seen.set(schema, states);
3770
+ if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path });
3771
+ const nested = blocked || !!schema.meta?.volatile;
3772
+ if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path, key], nested, seen);
3773
+ if (schema.sKey) validateVolatileSchema(schema.sKey, [...path, "<key>"], true, seen);
3774
+ if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path, "*"], true, seen);
3775
+ if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path, String(index)], true, seen);
3776
+ }
3777
+ Schema.extend = function extend(type, resolve) {
3778
+ resolvers[type] = resolve;
3779
+ };
3780
+ Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
3781
+ if (!schema) return [data];
3782
+ if (!options[checkedVolatile]) {
3783
+ validateVolatileSchema(schema, options.path);
3784
+ options = {
3785
+ ...options,
3786
+ [checkedVolatile]: true
3787
+ };
3788
+ }
3789
+ if (schema.meta?.volatile) {
3790
+ const inner = Schema(schema);
3791
+ inner.meta = {
3792
+ ...schema.meta,
3793
+ volatile: false
3794
+ };
3795
+ const [value, adapted] = Schema.resolve(data, inner, options, strict);
3796
+ try {
3797
+ return [createVolatile(value), adapted];
3798
+ } catch (error) {
3799
+ throw new ValidationError(error instanceof Error ? error.message : String(error), options);
3800
+ }
3801
+ }
3802
+ if (options.ignore?.(data, schema)) return [data];
3803
+ if (isNullable(data) && schema.type !== "lazy") {
3804
+ if (schema.meta.required) throw new ValidationError(`missing required value`, options);
3805
+ let current = schema;
3806
+ let fallback = schema.meta.default;
3807
+ while (current?.type === "intersect" && isNullable(fallback)) {
3808
+ current = current.list[0];
3809
+ fallback = current?.meta.default;
3810
+ }
3811
+ if (isNullable(fallback)) return [data];
3812
+ data = clone(fallback);
3813
+ }
3814
+ const callback = resolvers[schema.type];
3815
+ if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
3816
+ try {
3817
+ return callback(data, schema, options, strict);
3818
+ } catch (error) {
3819
+ if (!schema.meta.loose) throw error;
3820
+ return [schema.meta.default];
3821
+ }
3822
+ };
3823
+ Schema.from = function from(source) {
3824
+ if (isNullable(source)) return Schema.any();
3825
+ else if ([
3826
+ "string",
3827
+ "number",
3828
+ "boolean"
3829
+ ].includes(typeof source)) return Schema.const(source).required();
3830
+ else if (source[kSchema]) return source;
3831
+ else if (typeof source === "function") switch (source) {
3832
+ case String: return Schema.string().required();
3833
+ case Number: return Schema.number().required();
3834
+ case Boolean: return Schema.boolean().required();
3835
+ case Function: return Schema.function().required();
3836
+ default: return Schema.is(source).required();
3837
+ }
3838
+ else throw new TypeError(`cannot infer schema from ${source}`);
3839
+ };
3840
+ Schema.lazy = function lazy(builder) {
3841
+ const toJSON = () => {
3842
+ if (!schema.inner[kSchema]) {
3843
+ schema.inner = schema.builder();
3844
+ schema.inner.meta = {
3845
+ ...schema.meta,
3846
+ ...schema.inner.meta
3847
+ };
3848
+ }
3849
+ return schema.inner.toJSON();
3850
+ };
3851
+ const schema = new Schema({
3852
+ type: "lazy",
3853
+ builder,
3854
+ inner: { toJSON }
3855
+ });
3856
+ return schema;
3857
+ };
3858
+ Schema.natural = function natural() {
3859
+ return Schema.number().step(1).min(0);
3860
+ };
3861
+ Schema.percent = function percent() {
3862
+ return Schema.number().step(.01).min(0).max(1).role("slider");
3863
+ };
3864
+ Schema.date = function date() {
3865
+ return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
3866
+ const date = new Date(value);
3867
+ if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
3868
+ return date;
3869
+ }, true)]);
3870
+ };
3871
+ Schema.regExp = function regExp(flag = "") {
3872
+ return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
3873
+ try {
3874
+ return new RegExp(value, flag);
3875
+ } catch (e) {
3876
+ throw new ValidationError(e.message, options);
3877
+ }
3878
+ }, true)]);
3879
+ };
3880
+ Schema.arrayBuffer = function arrayBuffer(encoding) {
3881
+ return Schema.union([
3882
+ Schema.is(ArrayBuffer),
3883
+ Schema.is(SharedArrayBuffer),
3884
+ Schema.transform(Schema.any(), (value, options) => {
3885
+ if (Binary.isSource(value)) return Binary.fromSource(value);
3886
+ throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
3887
+ }, true),
3888
+ ...encoding ? [Schema.transform(Schema.string(), (value, options) => {
3889
+ try {
3890
+ return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
3891
+ } catch (e) {
3892
+ throw new ValidationError(e.message, options);
3893
+ }
3894
+ }, true)] : []
3895
+ ]);
3896
+ };
3897
+ Schema.extend("lazy", (data, schema, options, strict) => {
3898
+ if (!schema.inner[kSchema]) {
3899
+ schema.inner = schema.builder();
3900
+ schema.inner.meta = {
3901
+ ...schema.meta,
3902
+ ...schema.inner.meta
3903
+ };
3904
+ validateVolatileSchema(schema.inner, options.path, true);
3905
+ }
3906
+ return Schema.resolve(data, schema.inner, options, strict);
3907
+ });
3908
+ Schema.extend("any", (data) => {
3909
+ return [data];
3910
+ });
3911
+ Schema.extend("never", (data, _, options) => {
3912
+ throw new ValidationError(`expected nullable but got ${data}`, options);
3913
+ });
3914
+ Schema.extend("const", (data, { value }, options) => {
3915
+ if (deepEqual(data, value)) return [value];
3916
+ throw new ValidationError(`expected ${value} but got ${data}`, options);
3917
+ });
3918
+ function checkWithinRange(data, meta, description, options, skipMin = false) {
3919
+ const { max = Infinity, min = -Infinity } = meta;
3920
+ if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
3921
+ if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
3922
+ }
3923
+ Schema.extend("string", (data, { meta }, options) => {
3924
+ if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
3925
+ if (meta.pattern) {
3926
+ const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
3927
+ if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
3928
+ }
3929
+ checkWithinRange(data.length, meta, "string length", options);
3930
+ return [data];
3931
+ });
3932
+ function decimalShift(data, digits) {
3933
+ const str = data.toString();
3934
+ if (str.includes("e")) return data * Math.pow(10, digits);
3935
+ const index = str.indexOf(".");
3936
+ if (index === -1) return data * Math.pow(10, digits);
3937
+ const frac = str.slice(index + 1);
3938
+ const integer = str.slice(0, index);
3939
+ if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
3940
+ return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
3941
+ }
3942
+ function isMultipleOf(data, min, step) {
3943
+ step = Math.abs(step);
3944
+ if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
3945
+ const index = step.toString().indexOf(".");
3946
+ const digits = step.toString().slice(index + 1).length;
3947
+ return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
3948
+ }
3949
+ Schema.extend("number", (data, { meta }, options) => {
3950
+ if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
3951
+ checkWithinRange(data, meta, "number", options);
3952
+ const { step } = meta;
3953
+ if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
3954
+ return [data];
3955
+ });
3956
+ Schema.extend("boolean", (data, _, options) => {
3957
+ if (typeof data === "boolean") return [data];
3958
+ throw new ValidationError(`expected boolean but got ${data}`, options);
3959
+ });
3960
+ Schema.extend("bitset", (data, { bits, meta }, options) => {
3961
+ let value = 0, keys = [];
3962
+ if (typeof data === "number") {
3963
+ value = data;
3964
+ for (const key in bits) if (data & bits[key]) keys.push(key);
3965
+ } else if (Array.isArray(data)) {
3966
+ keys = data;
3967
+ for (const key of keys) {
3968
+ if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
3969
+ if (key in bits) value |= bits[key];
3970
+ }
3971
+ } else throw new ValidationError(`expected number or array but got ${data}`, options);
3972
+ if (value === meta.default) return [value];
3973
+ return [value, keys];
3974
+ });
3975
+ Schema.extend("function", (data, _, options) => {
3976
+ if (typeof data === "function") return [data];
3977
+ throw new ValidationError(`expected function but got ${data}`, options);
3978
+ });
3979
+ Schema.extend("is", (data, { constructor }, options) => {
3980
+ if (typeof constructor === "function") {
3981
+ if (data instanceof constructor) return [data];
3982
+ throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
3983
+ } else {
3984
+ if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
3985
+ let prototype = Object.getPrototypeOf(data);
3986
+ while (prototype) {
3987
+ if (prototype.constructor?.name === constructor) return [data];
3988
+ prototype = Object.getPrototypeOf(prototype);
3989
+ }
3990
+ throw new ValidationError(`expected ${constructor} but got ${data}`, options);
3991
+ }
3992
+ });
3993
+ function property(data, key, schema, options) {
3994
+ try {
3995
+ const [value, adapted] = Schema.resolve(data[key], schema, {
3996
+ ...options,
3997
+ path: [...options.path || [], key]
3998
+ });
3999
+ if (adapted !== void 0) data[key] = adapted;
4000
+ return value;
4001
+ } catch (e) {
4002
+ if (!options?.autofix) throw e;
4003
+ delete data[key];
4004
+ return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
4005
+ }
4006
+ }
4007
+ Schema.extend("array", (data, { inner, meta }, options) => {
4008
+ if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
4009
+ checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
4010
+ return [data.map((_, index) => property(data, index, inner, options))];
4011
+ });
4012
+ Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
4013
+ if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
4014
+ const result = {};
4015
+ for (const key in data) {
4016
+ let rKey;
4017
+ try {
4018
+ rKey = Schema.resolve(key, sKey, options)[0];
4019
+ } catch (error) {
4020
+ if (strict) continue;
4021
+ throw error;
4022
+ }
4023
+ result[rKey] = property(data, key, inner, options);
4024
+ data[rKey] = data[key];
4025
+ if (key !== rKey) delete data[key];
4026
+ }
4027
+ return [result];
4028
+ });
4029
+ Schema.extend("tuple", (data, { list }, options, strict) => {
4030
+ if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
4031
+ const result = list.map((inner, index) => property(data, index, inner, options));
4032
+ if (strict) return [result];
4033
+ result.push(...data.slice(list.length));
4034
+ return [result];
4035
+ });
4036
+ function merge(result, data) {
4037
+ for (const key in data) {
4038
+ if (key in result) continue;
4039
+ result[key] = data[key];
4040
+ }
4041
+ }
4042
+ Schema.extend("object", (data, { dict }, options, strict) => {
4043
+ if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
4044
+ const result = {};
4045
+ for (const key in dict) {
4046
+ const value = property(data, key, dict[key], options);
4047
+ if (!isNullable(value) || key in data) result[key] = value;
4048
+ }
4049
+ if (!strict) merge(result, data);
4050
+ return [result];
4051
+ });
4052
+ Schema.extend("union", (data, { list, toString }, options, strict) => {
4053
+ const messages = [];
4054
+ for (const inner of list) try {
4055
+ return Schema.resolve(data, inner, options, strict);
4056
+ } catch (error) {
4057
+ messages.push(error);
4058
+ }
4059
+ throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
4060
+ });
4061
+ Schema.extend("intersect", (data, { list, toString }, options, strict) => {
4062
+ if (!list.length) return [data];
4063
+ let result;
4064
+ for (const inner of list) {
4065
+ const value = Schema.resolve(data, inner, options, true)[0];
4066
+ if (isNullable(value)) continue;
4067
+ if (isNullable(result)) result = value;
4068
+ else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
4069
+ else if (typeof value === "object") merge(result ??= {}, value);
4070
+ else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
4071
+ }
4072
+ if (!strict && isPlainObject(data)) merge(result, data);
4073
+ return [result];
4074
+ });
4075
+ Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
4076
+ const [result, adapted = data] = Schema.resolve(data, inner, options, true);
4077
+ if (preserve) return [callback(result)];
4078
+ else return [callback(result), callback(adapted)];
4079
+ });
4080
+ const formatters = {};
4081
+ function defineMethod(name, keys, format) {
4082
+ formatters[name] = format;
4083
+ Object.assign(Schema, { [name](...args) {
4084
+ const schema = new Schema({ type: name });
4085
+ keys.forEach((key, index) => {
4086
+ switch (key) {
4087
+ case "sKey":
4088
+ schema.sKey = args[index] ?? Schema.string();
4089
+ break;
4090
+ case "inner":
4091
+ schema.inner = Schema.from(args[index]);
4092
+ break;
4093
+ case "list":
4094
+ schema.list = args[index].map(Schema.from);
4095
+ break;
4096
+ case "dict":
4097
+ schema.dict = mapValues(args[index], Schema.from);
4098
+ break;
4099
+ case "bits":
4100
+ schema.bits = {};
4101
+ for (const key in args[index]) {
4102
+ if (typeof args[index][key] !== "number") continue;
4103
+ schema.bits[key] = args[index][key];
4104
+ }
4105
+ break;
4106
+ case "callback": {
4107
+ const callback = schema.callback = args[index];
4108
+ callback["toJSON"] ||= () => callback.toString();
4109
+ break;
4110
+ }
4111
+ case "constructor": {
4112
+ const constructor = schema.constructor = args[index];
4113
+ if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
4114
+ break;
4115
+ }
4116
+ default: schema[key] = args[index];
4117
+ }
4118
+ });
4119
+ if (name === "object" || name === "dict") schema.meta.default = {};
4120
+ else if (name === "array" || name === "tuple") schema.meta.default = [];
4121
+ else if (name === "bitset") schema.meta.default = 0;
4122
+ return schema;
4123
+ } });
4124
+ }
4125
+ defineMethod("is", ["constructor"], ({ constructor }) => {
4126
+ if (typeof constructor === "function") return constructor.name;
4127
+ else return constructor;
4128
+ });
4129
+ defineMethod("any", [], () => "any");
4130
+ defineMethod("never", [], () => "never");
4131
+ defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
4132
+ defineMethod("string", [], () => "string");
4133
+ defineMethod("number", [], () => "number");
4134
+ defineMethod("boolean", [], () => "boolean");
4135
+ defineMethod("bitset", ["bits"], () => "bitset");
4136
+ defineMethod("function", [], () => "function");
4137
+ defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
4138
+ defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
4139
+ defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
4140
+ defineMethod("object", ["dict"], ({ dict }) => {
4141
+ if (Object.keys(dict).length === 0) return "{}";
4142
+ return `{ ${Object.entries(dict).map(([key, inner]) => {
4143
+ return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
4144
+ }).join(", ")} }`;
4145
+ });
4146
+ defineMethod("union", ["list"], ({ list }, inline) => {
4147
+ const result = list.map(({ toString: format }) => format()).join(" | ");
4148
+ return inline ? `(${result})` : result;
4149
+ });
4150
+ defineMethod("intersect", ["list"], ({ list }) => {
4151
+ return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
4152
+ });
4153
+ defineMethod("transform", [
4154
+ "inner",
4155
+ "callback",
4156
+ "preserve"
4157
+ ], ({ inner }, isInner) => inner.toString(isInner));
4158
+ //#endregion
4159
+ //#region lib/types/onboarding-config.js
4160
+ /** Public page-bootstrap options shared by the Host and Client halves. */
4161
+ /** Validate Host configuration and its public page-bootstrap payload. */
4162
+ const Config = Schema.object({ credentialOnboarding: Schema.boolean().default(true) });
4163
+ /** Page-global key carrying only the public onboarding options. */
4164
+ const ONBOARDING_CONFIG_GLOBAL = "__DSH_MODELS_ONBOARDING__";
4165
+ //#endregion
3071
4166
  //#region lib/types/client/index.js
3072
4167
  /** Dictionary namespace owned by this plugin. */
3073
4168
  const NS = "settings.models";
@@ -3092,7 +4187,7 @@ window.__ModuleLoader__.load({
3092
4187
  "remote.credentials",
3093
4188
  "remote.llm",
3094
4189
  "remote.settings",
3095
- "settingsScope",
4190
+ "configForms",
3096
4191
  "settingsSchema"
3097
4192
  ];
3098
4193
  /**
@@ -3102,13 +4197,15 @@ window.__ModuleLoader__.load({
3102
4197
  * @param ctx - client root context.
3103
4198
  */
3104
4199
  function apply(ctx) {
4200
+ const payload = globalThis[ONBOARDING_CONFIG_GLOBAL];
4201
+ const credentialOnboarding = Config(payload === void 0 ? {} : payload).credentialOnboarding && !("dshDesktop" in globalThis);
3105
4202
  ctx.effect(() => ctx.locale.register(NS, {
3106
4203
  zh,
3107
4204
  en
3108
4205
  }), "ui-settings-models: copy dictionaries");
3109
4206
  const schema = createSettingsSchemaOperations(ctx.settingsSchema);
3110
4207
  const operations = createModelsOperations(ctx);
3111
- const controller = new ModelsSettingsStore(ctx, schema, ctx.settingsScope.describe());
4208
+ const controller = new ModelsSettingsStore(ctx, schema, ctx.configForms.describe());
3112
4209
  const t = ctx.locale.bind(NS);
3113
4210
  const injected = () => ({
3114
4211
  controller,
@@ -3118,16 +4215,14 @@ window.__ModuleLoader__.load({
3118
4215
  t
3119
4216
  });
3120
4217
  const deepSeekOnboardingInjected = () => ({
4218
+ automatic: credentialOnboarding,
3121
4219
  controller,
3122
4220
  hooks: { models: controller.store },
3123
4221
  operations,
3124
4222
  schema,
3125
4223
  t
3126
4224
  });
3127
- const welcomeController = new WelcomeNoticeStore(ctx.settingsScope.bind({
3128
- namespace: WELCOME_NOTICE_SETTINGS_NAMESPACE,
3129
- decode: decodeWelcomeSection
3130
- }));
4225
+ const welcomeController = new WelcomeNoticeStore(ctx.configForms.get(WELCOME_NOTICE_SETTINGS_NAMESPACE));
3131
4226
  const welcomeInjected = () => ({
3132
4227
  controller: welcomeController,
3133
4228
  hooks: { welcome: welcomeController.store },
@@ -3177,6 +4272,10 @@ window.__ModuleLoader__.load({
3177
4272
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
3178
4273
  name: "settings.onboarding",
3179
4274
  id: "deepseek-official",
4275
+ children: { "settings.models.sign-in": {
4276
+ kind: "single",
4277
+ scope: "root"
4278
+ } },
3180
4279
  order: 0,
3181
4280
  inject: deepSeekOnboardingInjected
3182
4281
  }, DeepSeekOnboardingDialog));