dsh-model-extension 0.3.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -40
- package/assets/models-plus-demo-models-tab.png +0 -0
- package/assets/models-plus-demo-provider-tab.png +0 -0
- package/cordis.patch.yml +12 -0
- package/lib/client.js +1764 -1982
- package/lib/client.js.map +1 -1
- package/lib/index.js +182 -14
- package/lib/index.js.map +1 -1
- package/package.json +4 -3
- package/assets/model-extension-mockup.html +0 -105
- package/assets/model-extension-mockup.png +0 -0
package/lib/client.js
CHANGED
|
@@ -3,170 +3,323 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
|
-
var __extCss = ".Qji2zG_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.Qji2zG_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.Qji2zG_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.Qji2zG_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.Qji2zG_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.Qji2zG_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.Qji2zG_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.Qji2zG_rowHead{align-items:center;gap:10px;display:flex}.Qji2zG_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.Qji2zG_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.Qji2zG_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}.Qji2zG_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.Qji2zG_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.Qji2zG_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.Qji2zG_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.Qji2zG_primaryButton,.Qji2zG_secondaryButton,.Qji2zG_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}.Qji2zG_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.Qji2zG_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.Qji2zG_secondaryButton,.Qji2zG_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.Qji2zG_secondaryButton:hover:not(:disabled),.Qji2zG_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Qji2zG_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.Qji2zG_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}.Qji2zG_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.Qji2zG_rowActions .Qji2zG_secondaryButton,.Qji2zG_rowActions .Qji2zG_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.Qji2zG_primaryButton:disabled,.Qji2zG_secondaryButton:disabled,.Qji2zG_dangerButton:disabled,.Qji2zG_addButton:disabled,.Qji2zG_linkButton:disabled,.Qji2zG_addModelButton:disabled{opacity:.4;cursor:default}.Qji2zG_primaryButton:focus-visible,.Qji2zG_secondaryButton:focus-visible,.Qji2zG_dangerButton:focus-visible,.Qji2zG_addButton:focus-visible,.Qji2zG_linkButton:focus-visible,.Qji2zG_addModelButton:focus-visible,.Qji2zG_iconButton:focus-visible,.Qji2zG_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.Qji2zG_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.Qji2zG_editorHeader{align-items:baseline;gap:8px;display:flex}.Qji2zG_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.Qji2zG_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.Qji2zG_field{flex-direction:column;gap:6px;display:flex}.Qji2zG_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.Qji2zG_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}.Qji2zG_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.Qji2zG_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.Qji2zG_editorActions{justify-content:flex-end;gap:8px;display:flex}.Qji2zG_addBlock{flex-direction:column;gap:12px;display:flex}.Qji2zG_addActions{flex-wrap:wrap;gap:10px;display:flex}.Qji2zG_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.Qji2zG_addCard,.Qji2zG_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.Qji2zG_addCard .Qji2zG_editor,.Qji2zG_setupCard .Qji2zG_editor{background:0 0;padding:0}.Qji2zG_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.Qji2zG_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}.Qji2zG_customizedSummary::-webkit-details-marker{display:none}.Qji2zG_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)}.Qji2zG_customized[open]>.Qji2zG_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.Qji2zG_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.Qji2zG_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.Qji2zG_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.Qji2zG_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.Qji2zG_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.Qji2zG_modelCatalogMeta,.Qji2zG_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.Qji2zG_modelList{flex-direction:column;gap:8px;display:flex}.Qji2zG_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.Qji2zG_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.Qji2zG_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.Qji2zG_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}.Qji2zG_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.Qji2zG_iconButton:disabled{cursor:default;opacity:.4}.Qji2zG_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.Qji2zG_modelAdvanced{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;padding:8px 4px 2px;display:grid}.Qji2zG_modelField{flex-direction:column;gap:4px;display:flex}.Qji2zG_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.Qji2zG_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.Qji2zG_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}.Qji2zG_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Qji2zG_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.Qji2zG_input{cursor:pointer;max-width:240px}.Qji2zG_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.Qji2zG_input::placeholder{color:var(--dsw-alias-label-dimmed)}.Qji2zG_input:disabled{opacity:.6;cursor:default}.Qji2zG_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}.Qji2zG_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.Qji2zG_deleteDialog{width:min(480px,100%)}.Qji2zG_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.Qji2zG_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.Qji2zG_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.Qji2zG_customizedSummary:before,.Qji2zG_switchThumb{transition:none}}.Qji2zG_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.Qji2zG_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.Qji2zG_candidateSearch{flex:240px;min-width:0}.Qji2zG_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.Qji2zG_candidate{border-radius:6px}.Qji2zG_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.Qji2zG_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.Qji2zG_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}.cDGz_W_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.cDGz_W_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.cDGz_W_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.cDGz_W_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.cDGz_W_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.cDGz_W_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.cDGz_W_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.cDGz_W_rowHead{align-items:center;gap:10px;display:flex}.cDGz_W_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.cDGz_W_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.cDGz_W_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}.cDGz_W_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.cDGz_W_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.cDGz_W_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.cDGz_W_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.cDGz_W_primaryButton,.cDGz_W_secondaryButton,.cDGz_W_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}.cDGz_W_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.cDGz_W_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.cDGz_W_secondaryButton,.cDGz_W_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.cDGz_W_secondaryButton:hover:not(:disabled),.cDGz_W_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.cDGz_W_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.cDGz_W_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}.cDGz_W_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.cDGz_W_rowActions .cDGz_W_secondaryButton,.cDGz_W_rowActions .cDGz_W_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.cDGz_W_primaryButton:disabled,.cDGz_W_secondaryButton:disabled,.cDGz_W_dangerButton:disabled,.cDGz_W_addButton:disabled,.cDGz_W_linkButton:disabled,.cDGz_W_addModelButton:disabled{opacity:.4;cursor:default}.cDGz_W_primaryButton:focus-visible,.cDGz_W_secondaryButton:focus-visible,.cDGz_W_dangerButton:focus-visible,.cDGz_W_addButton:focus-visible,.cDGz_W_linkButton:focus-visible,.cDGz_W_addModelButton:focus-visible,.cDGz_W_iconButton:focus-visible,.cDGz_W_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.cDGz_W_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.cDGz_W_editorHeader{align-items:baseline;gap:8px;display:flex}.cDGz_W_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.cDGz_W_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.cDGz_W_field{flex-direction:column;gap:6px;display:flex}.cDGz_W_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.cDGz_W_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}.cDGz_W_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.cDGz_W_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.cDGz_W_editorActions{justify-content:flex-end;gap:8px;display:flex}.cDGz_W_addBlock{flex-direction:column;gap:12px;display:flex}.cDGz_W_addActions{flex-wrap:wrap;gap:10px;display:flex}.cDGz_W_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.cDGz_W_addCard,.cDGz_W_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.cDGz_W_addCard .cDGz_W_editor,.cDGz_W_setupCard .cDGz_W_editor{background:0 0;padding:0}.cDGz_W_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.cDGz_W_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}.cDGz_W_customizedSummary::-webkit-details-marker{display:none}.cDGz_W_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)}.cDGz_W_customized[open]>.cDGz_W_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.cDGz_W_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.cDGz_W_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.cDGz_W_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.cDGz_W_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.cDGz_W_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.cDGz_W_modelCatalogMeta,.cDGz_W_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.cDGz_W_modelList{flex-direction:column;gap:8px;display:flex}.cDGz_W_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.cDGz_W_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.cDGz_W_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.cDGz_W_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}.cDGz_W_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.cDGz_W_iconButton:disabled{cursor:default;opacity:.4}.cDGz_W_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.cDGz_W_modelAdvanced{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;padding:8px 4px 2px;display:grid}.cDGz_W_modelExtensionFields{box-sizing:border-box;width:100%;display:block}.cDGz_W_modelField{flex-direction:column;gap:4px;display:flex}.cDGz_W_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.cDGz_W_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.cDGz_W_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}.cDGz_W_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.cDGz_W_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.cDGz_W_input{cursor:pointer;max-width:240px}.cDGz_W_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.cDGz_W_input::placeholder{color:var(--dsw-alias-label-dimmed)}.cDGz_W_input:disabled{opacity:.6;cursor:default}.cDGz_W_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}.cDGz_W_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.cDGz_W_deleteDialog{width:min(480px,100%)}.cDGz_W_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.cDGz_W_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.cDGz_W_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.cDGz_W_customizedSummary:before,.cDGz_W_switchThumb{transition:none}}.cDGz_W_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.cDGz_W_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.cDGz_W_candidateSearch{flex:240px;min-width:0}.cDGz_W_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.cDGz_W_candidate{border-radius:6px}.cDGz_W_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.cDGz_W_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.cDGz_W_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}\n";
|
|
6
|
+
var __extCss = ".J84-9a_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.J84-9a_titleRow{align-items:center;gap:10px;display:flex}.J84-9a_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.J84-9a_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.J84-9a_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.J84-9a_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.J84-9a_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.J84-9a_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.J84-9a_rowHead{align-items:center;gap:10px;display:flex}.J84-9a_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.J84-9a_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.J84-9a_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}.J84-9a_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.J84-9a_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.J84-9a_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.J84-9a_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.J84-9a_primaryButton,.J84-9a_secondaryButton,.J84-9a_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}.J84-9a_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.J84-9a_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.J84-9a_secondaryButton,.J84-9a_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.J84-9a_secondaryButton:hover:not(:disabled),.J84-9a_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.J84-9a_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.J84-9a_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}.J84-9a_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.J84-9a_rowActions .J84-9a_secondaryButton,.J84-9a_rowActions .J84-9a_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.J84-9a_primaryButton:disabled,.J84-9a_secondaryButton:disabled,.J84-9a_dangerButton:disabled,.J84-9a_addButton:disabled,.J84-9a_linkButton:disabled,.J84-9a_addModelButton:disabled{opacity:.4;cursor:default}.J84-9a_primaryButton:focus-visible,.J84-9a_secondaryButton:focus-visible,.J84-9a_dangerButton:focus-visible,.J84-9a_addButton:focus-visible,.J84-9a_linkButton:focus-visible,.J84-9a_addModelButton:focus-visible,.J84-9a_iconButton:focus-visible,.J84-9a_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.J84-9a_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.J84-9a_field{flex-direction:column;gap:6px;display:flex}.J84-9a_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.J84-9a_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}.J84-9a_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.J84-9a_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.J84-9a_editorActions{justify-content:flex-end;gap:8px;display:flex}.J84-9a_addBlock{flex-direction:column;gap:12px;display:flex}.J84-9a_addActions{flex-wrap:wrap;gap:10px;display:flex}.J84-9a_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.J84-9a_addCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.J84-9a_addCard .J84-9a_editor,.J84-9a_setupCard .J84-9a_editor{background:0 0;padding:0}.J84-9a_tabs{flex-direction:column;gap:12px;display:flex}.J84-9a_tabBar{border-bottom:.5px solid var(--dsw-alias-border-l2);gap:4px;display:flex}.J84-9a_tabButton{appearance:none;font:inherit;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;padding:6px 12px 8px;font-size:13px;line-height:20px;position:relative}.J84-9a_tabButton:hover{color:var(--dsw-alias-label-primary)}.J84-9a_tabButton:disabled{opacity:.4;cursor:default}.J84-9a_tabButtonActive{color:var(--dsw-alias-label-primary);font-weight:500}.J84-9a_tabButtonActive:after{content:\"\";background:var(--dsw-alias-brand-primary);border-radius:2px;height:2px;position:absolute;bottom:-1px;left:10px;right:10px}.J84-9a_tabPanel{flex-direction:column;gap:12px;display:flex}.J84-9a_modelCatalog{flex-direction:column;gap:10px;display:flex}.J84-9a_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.J84-9a_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.J84-9a_modelCatalogMeta,.J84-9a_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.J84-9a_modelList{flex-direction:column;gap:8px;display:flex}.J84-9a_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.J84-9a_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.J84-9a_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.J84-9a_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}.J84-9a_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.J84-9a_iconButton:disabled{cursor:default;opacity:.4}.J84-9a_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.J84-9a_modelAdvanced{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;padding:8px 4px 2px;display:grid}.J84-9a_modelField{flex-direction:column;gap:4px;display:flex}.J84-9a_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.J84-9a_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.J84-9a_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}.J84-9a_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.J84-9a_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.J84-9a_input{cursor:pointer;max-width:240px}.J84-9a_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.J84-9a_input::placeholder{color:var(--dsw-alias-label-dimmed)}.J84-9a_input:disabled{opacity:.6;cursor:default}.J84-9a_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}.J84-9a_deleteDialog{width:min(480px,100%)}.J84-9a_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.J84-9a_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.J84-9a_fetchDialog{max-width:520px}.J84-9a_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.J84-9a_candidateSearch{flex:240px;min-width:0}.J84-9a_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.J84-9a_candidate{border-radius:6px}.J84-9a_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.J84-9a_candidateId{overflow-wrap:anywhere;flex:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.J84-9a_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}[hidden]{display:none!important}.J84-9a_extBlock{border:.5px solid var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-layer-1);border-radius:10px;flex-direction:column;gap:10px;margin:8px 4px 2px;padding:10px;display:flex}.J84-9a_extGroup{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px}.J84-9a_extGroupFirst{border-top:none;padding-top:0}.J84-9a_extCheck{cursor:pointer;align-items:flex-start;gap:8px;display:flex}.J84-9a_extCheckLabel{color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}.J84-9a_extHint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.J84-9a_extGated{flex-direction:column;gap:12px;padding-left:2px;display:flex}.J84-9a_extField{flex-direction:column;gap:6px;display:flex}.J84-9a_extLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.J84-9a_extTable{border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;overflow:hidden}.J84-9a_extTHead{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-module-platform);grid-template-columns:64px minmax(0,1fr) minmax(0,1.2fr);font-size:12px;line-height:18px;display:grid}.J84-9a_extTHead>div{padding:6px 10px}.J84-9a_extTHead>div+div{border-left:.5px solid var(--dsw-alias-border-l2)}.J84-9a_extTRow{border-top:.5px solid var(--dsw-alias-border-l2);grid-template-columns:64px minmax(0,1fr) minmax(0,1.2fr);align-items:center;display:grid}.J84-9a_extTRow>div{padding:6px 10px;font-size:14px}.J84-9a_extTRow>div+div{border-left:.5px solid var(--dsw-alias-border-l2)}.J84-9a_extTRow input[type=text]{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}.J84-9a_extTRow input[type=text]:disabled{opacity:.6;cursor:default}.J84-9a_extCheckbox{accent-color:var(--dsw-alias-brand-primary);flex:none;width:15px;height:15px}.J84-9a_switchRow{cursor:pointer;align-items:center;gap:8px;font-size:14px;line-height:22px;display:flex}.J84-9a_extTag{background:var(--dsw-static-deepseek-100);color:var(--dsw-static-deepseek-600);border:.5px solid #4176e659;border-radius:4px;flex:none;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px}.J84-9a_modalMask{background:var(--dsw-alias-bg-mask-1);z-index:100;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}.J84-9a_modal{background:var(--dsw-specific-menu);border-radius:16px;width:100%;padding:20px;box-shadow:0 12px 40px #0000002e}.J84-9a_modalTitle{color:var(--dsw-alias-label-primary);margin-bottom:8px;font-size:16px;font-weight:500;line-height:24px}.J84-9a_modalDesc{color:var(--dsw-alias-label-secondary);margin-bottom:16px;font-size:14px;line-height:22px}.J84-9a_modalFoot{justify-content:flex-end;gap:8px;display:flex}.J84-9a_quickWrap{flex-direction:column;gap:6px;display:flex;position:relative}.J84-9a_quickList{z-index:20;border:.5px solid var(--dsw-alias-border-l3);background:var(--dsw-specific-menu);border-radius:8px;max-height:264px;margin-top:2px;padding:4px;position:absolute;top:100%;left:0;right:0;overflow-y:auto;box-shadow:0 8px 24px #0000001f}.J84-9a_quickItem{width:100%;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;overflow-wrap:anywhere;background:0 0;border:none;border-radius:6px;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;display:block}.J84-9a_quickItem:hover{background:var(--dsw-alias-interactive-bg-hover)}.J84-9a_quickEmpty{color:var(--dsw-alias-label-tertiary);padding:6px 8px;font-size:12px;line-height:18px}@media (prefers-reduced-motion:reduce){.J84-9a_customizedSummary:before{transition:none}}\n";
|
|
7
7
|
if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="dsh-model-extension"]') === null) {
|
|
8
8
|
var __tag = document.createElement('style');
|
|
9
9
|
__tag.dataset.plugin = "dsh-model-extension";
|
|
10
|
-
__tag.dataset.pluginCss = "dsh-model-extension/
|
|
10
|
+
__tag.dataset.pluginCss = "dsh-model-extension/models-plus.module.css";
|
|
11
11
|
__tag.textContent = __extCss;
|
|
12
12
|
document.head.appendChild(__tag);
|
|
13
13
|
}
|
|
14
14
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
15
15
|
let react = require("react");
|
|
16
16
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
17
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
18
17
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
19
|
-
|
|
18
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
19
|
+
//#region src/client/vendor/store.ts
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Any route key walks a dict schema to the same profile node, so the lookup
|
|
22
|
+
* names one that cannot collide with a configured route.
|
|
23
23
|
*/
|
|
24
|
+
const PROBE_ROUTE = "\0probe";
|
|
24
25
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* Join declared configurable providers with the currently registered routes.
|
|
27
|
+
* @param registered - live provider routes in registration order.
|
|
28
|
+
* @param directory - declared configurable providers in declaration order.
|
|
29
|
+
* @returns declared rows followed by live routes with no declaration.
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
+
function joinProviderDirectory(registered, directory) {
|
|
32
|
+
const active = new Set(registered.map((provider) => provider.id));
|
|
33
|
+
const declared = new Set(directory.map((entry) => entry.provider));
|
|
34
|
+
const rows = directory.map((entry) => ({
|
|
35
|
+
provider: entry.provider,
|
|
36
|
+
displayName: entry.displayName,
|
|
37
|
+
settingsNs: entry.settingsNs,
|
|
38
|
+
settingsPath: [...entry.settingsPath],
|
|
39
|
+
active: active.has(entry.provider),
|
|
40
|
+
...entry.declared === void 0 ? {} : { declared: entry.declared }
|
|
41
|
+
}));
|
|
42
|
+
for (const provider of registered) {
|
|
43
|
+
if (declared.has(provider.id)) continue;
|
|
44
|
+
rows.push({
|
|
45
|
+
provider: provider.id,
|
|
46
|
+
displayName: provider.name,
|
|
47
|
+
settingsNs: "",
|
|
48
|
+
settingsPath: [],
|
|
49
|
+
active: true
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return rows;
|
|
53
|
+
}
|
|
31
54
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* lock a user out of a gateway whose key legitimately takes this shape, with
|
|
38
|
-
* the environment refusing it too and no way through.
|
|
55
|
+
* Derive the conventional credential reference for a provider route: the v1
|
|
56
|
+
* page never asks for an environment-variable name, so a typed key stores
|
|
57
|
+
* under this derived reference and the profile records it as `apiKeyEnv`.
|
|
58
|
+
* @param provider - provider route id (e.g. `anthropic`, `minimax-cn`).
|
|
59
|
+
* @returns the derived reference name (e.g. `MINIMAX_CN_API_KEY`).
|
|
39
60
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
function isQuoted(value) {
|
|
43
|
-
const first = value[0];
|
|
44
|
-
if (first !== "\"" && first !== "'" && first !== "`") return false;
|
|
45
|
-
return value.length > 1 && value.endsWith(first);
|
|
61
|
+
function deriveKeyRef(provider) {
|
|
62
|
+
return `${provider.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_API_KEY`;
|
|
46
63
|
}
|
|
47
64
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* @
|
|
55
|
-
* @returns the copy key for a field-level failure, or `undefined` to allow submit.
|
|
65
|
+
* The wire protocols a hand-declared route may name, read out of the owning
|
|
66
|
+
* namespace's own schema. This stays a schema read rather than a wire field so
|
|
67
|
+
* the choices the page offers cannot drift from the ones the adapter accepts:
|
|
68
|
+
* both come from the same `Config`.
|
|
69
|
+
* @param namespace - the namespace view whose schema declares the profile shape.
|
|
70
|
+
* @param schema - settings schema operations.
|
|
71
|
+
* @returns the protocol identifiers, or an empty list when the schema has none.
|
|
56
72
|
*/
|
|
57
|
-
function
|
|
58
|
-
if (
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
function protocolChoices(namespace, schema) {
|
|
74
|
+
if (namespace === void 0) return [];
|
|
75
|
+
const list = schema.nodeAtPath(schema.rehydrate(namespace.schema), [
|
|
76
|
+
"providers",
|
|
77
|
+
PROBE_ROUTE,
|
|
78
|
+
"api"
|
|
79
|
+
]);
|
|
80
|
+
if (list?.type !== "union" || list.list === void 0) return [];
|
|
81
|
+
return list.list.map((entry) => entry.value).filter((value) => typeof value === "string");
|
|
63
82
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
83
|
+
/** The credential reference a resolved profile names (its `apiKeyEnv` field). */
|
|
84
|
+
function apiKeyEnvOf(namespace, path, schema) {
|
|
85
|
+
if (namespace === void 0) return void 0;
|
|
86
|
+
const profile = schema.getPath(namespace.value, path);
|
|
87
|
+
if (typeof profile !== "object" || profile === null) return void 0;
|
|
88
|
+
const ref = profile.apiKeyEnv;
|
|
89
|
+
return typeof ref === "string" && ref.length > 0 ? ref : void 0;
|
|
90
|
+
}
|
|
91
|
+
/** The models settings page controller (one per settings surface). */
|
|
92
|
+
var ModelsSettingsStore = class {
|
|
93
|
+
ctx;
|
|
94
|
+
schema;
|
|
95
|
+
describeFace;
|
|
96
|
+
/** The snapshot the section renders from (uSES-safe store). */
|
|
97
|
+
store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
|
|
98
|
+
status: "idle",
|
|
99
|
+
error: null,
|
|
100
|
+
credentialError: null,
|
|
101
|
+
writable: false,
|
|
102
|
+
rows: [],
|
|
103
|
+
namespaces: /* @__PURE__ */ new Map()
|
|
104
|
+
});
|
|
105
|
+
/** Latest load wins; an older response never overwrites a newer one. */
|
|
106
|
+
generation = 0;
|
|
107
|
+
/**
|
|
108
|
+
* @param ctx - the page plugin's context, whose `remote.llm` and
|
|
109
|
+
* `remote.credentials` namespaces carry the directory and credential reads.
|
|
110
|
+
* @param schema - settings-owned schema and immutable path operations.
|
|
111
|
+
* @param describeFace - the shared mirror's describe face (namespace views and writability).
|
|
112
|
+
*/
|
|
113
|
+
constructor(ctx, schema, describeFace) {
|
|
114
|
+
this.ctx = ctx;
|
|
115
|
+
this.schema = schema;
|
|
116
|
+
this.describeFace = describeFace;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Refresh the whole page snapshot: the provider directory and the mirror's
|
|
120
|
+
* settings answer in parallel, then one batched credential describe over
|
|
121
|
+
* every referenced ref. Provider failure or absence of an initial settings
|
|
122
|
+
* answer keeps the last good rows and surfaces an error; a failed settings
|
|
123
|
+
* refresh reuses the mirror's held view.
|
|
124
|
+
* @returns nothing; the snapshot carries the outcome.
|
|
125
|
+
*/
|
|
126
|
+
async load() {
|
|
127
|
+
const generation = ++this.generation;
|
|
128
|
+
this.store.update((s) => {
|
|
129
|
+
s.status = "loading";
|
|
130
|
+
s.error = null;
|
|
131
|
+
});
|
|
132
|
+
const [registered, declared] = await Promise.all([
|
|
133
|
+
this.ctx.remote.llm.listProviders(),
|
|
134
|
+
this.ctx.remote.llm.listConfigurableProviders(),
|
|
135
|
+
this.describeFace.ensure()
|
|
136
|
+
]);
|
|
137
|
+
if (!registered.ok) {
|
|
138
|
+
this.failLoad(generation, registered.error.message);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!declared.ok) {
|
|
142
|
+
this.failLoad(generation, declared.error.message);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const mirrored = this.describeFace.getSnapshot();
|
|
146
|
+
if (mirrored.view === void 0) {
|
|
147
|
+
this.failLoad(generation, mirrored.error ?? "settings are unavailable in this browser");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const providers = joinProviderDirectory(registered.value, declared.value);
|
|
151
|
+
const writable = mirrored.view.writable;
|
|
152
|
+
const views = mirrored.view.namespaces;
|
|
153
|
+
const namespaces = new Map(views.map((view) => [view.ns, view]));
|
|
154
|
+
const rows = providers.map((entry) => {
|
|
155
|
+
const namespace = namespaces.get(entry.settingsNs);
|
|
156
|
+
return {
|
|
157
|
+
entry,
|
|
158
|
+
configured: namespace !== void 0 && (entry.settingsPath.length === 0 || this.schema.getPath(namespace.value, entry.settingsPath) !== void 0),
|
|
159
|
+
removable: namespace !== void 0 && entry.settingsPath.length > 0 && this.schema.hasPath(namespace.user, entry.settingsPath) && !this.schema.hasPath(namespace.base, entry.settingsPath),
|
|
160
|
+
apiKeyEnv: apiKeyEnvOf(namespace, entry.settingsPath, this.schema),
|
|
161
|
+
credential: void 0
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
const refs = [...new Set(rows.map((row) => row.apiKeyEnv ?? deriveKeyRef(row.entry.provider)))];
|
|
165
|
+
let credentials = {};
|
|
166
|
+
let credentialError = null;
|
|
167
|
+
if (refs.length > 0) {
|
|
168
|
+
const response = await this.ctx.remote.credentials.describe(refs);
|
|
169
|
+
if (response.ok) credentials = response.value;
|
|
170
|
+
else credentialError = response.error.message;
|
|
171
|
+
}
|
|
172
|
+
if (generation !== this.generation) return;
|
|
173
|
+
this.store.update((s) => {
|
|
174
|
+
s.status = "ready";
|
|
175
|
+
s.error = null;
|
|
176
|
+
s.credentialError = credentialError;
|
|
177
|
+
s.writable = writable;
|
|
178
|
+
s.rows = rows.map((row) => {
|
|
179
|
+
const named = row.apiKeyEnv === void 0 ? void 0 : credentials[row.apiKeyEnv];
|
|
180
|
+
const derived = row.apiKeyEnv !== void 0 ? void 0 : credentials[deriveKeyRef(row.entry.provider)];
|
|
181
|
+
return {
|
|
182
|
+
...row,
|
|
183
|
+
...named === void 0 ? {} : { credential: named },
|
|
184
|
+
...derived === void 0 ? {} : { derivedCredential: derived }
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
s.namespaces = namespaces;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/** Publish one load's failure text, unless a newer load already took over. */
|
|
191
|
+
failLoad(generation, message) {
|
|
192
|
+
if (generation !== this.generation) return;
|
|
193
|
+
this.store.update((s) => {
|
|
194
|
+
s.status = "error";
|
|
195
|
+
s.error = message;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
132
198
|
};
|
|
199
|
+
/**
|
|
200
|
+
* Whether a joined row can serve model requests as it stands: the route is
|
|
201
|
+
* registered with the adapter registry, and whatever credential its resolved
|
|
202
|
+
* profile names is stored. A profile naming no reference authenticates through
|
|
203
|
+
* the provider's own path (the Bedrock chain, Vertex ADC, a gateway that needs
|
|
204
|
+
* nothing), as does a live route with no settings address at all, so neither
|
|
205
|
+
* owes this page a key.
|
|
206
|
+
* @param row - one joined provider row.
|
|
207
|
+
* @returns whether the user already has this provider to talk to.
|
|
208
|
+
*/
|
|
209
|
+
function providerUsable(row) {
|
|
210
|
+
if (!row.entry.active) return false;
|
|
211
|
+
if (row.apiKeyEnv === void 0) return true;
|
|
212
|
+
return row.credential?.configured === true;
|
|
213
|
+
}
|
|
133
214
|
//#endregion
|
|
134
|
-
//#region
|
|
215
|
+
//#region src/client/models-plus/models-index.ts
|
|
216
|
+
/** Same-origin routes served by the server half. */
|
|
217
|
+
const INDEX_URL = "/plugins/dsh-model-extension/models-index";
|
|
218
|
+
const DOWNLOAD_URL = "/plugins/dsh-model-extension/models-download";
|
|
219
|
+
/** Module-level cache: one page load fetches the index at most once. */
|
|
220
|
+
let cached;
|
|
221
|
+
function normalize(raw) {
|
|
222
|
+
if (!Array.isArray(raw)) return [];
|
|
223
|
+
const entries = [];
|
|
224
|
+
for (const item of raw) {
|
|
225
|
+
if (typeof item !== "object" || item === null) continue;
|
|
226
|
+
const row = item;
|
|
227
|
+
if (typeof row["id"] !== "string" || row["id"].length === 0) continue;
|
|
228
|
+
const modalities = Array.isArray(row["input"]) ? row["input"].filter((m) => typeof m === "string") : [];
|
|
229
|
+
entries.push({
|
|
230
|
+
id: row["id"],
|
|
231
|
+
context: typeof row["context"] === "number" ? row["context"] : void 0,
|
|
232
|
+
output: typeof row["output"] === "number" ? row["output"] : void 0,
|
|
233
|
+
input: modalities,
|
|
234
|
+
reasoning: row["reasoning"] === true
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return entries;
|
|
238
|
+
}
|
|
135
239
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* @returns the cancel/commit row.
|
|
240
|
+
* The metadata index, fetched once per page load; `undefined` while the
|
|
241
|
+
* server has no metadata file (the caller shows the not-ready hint).
|
|
139
242
|
*/
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
243
|
+
async function getMetadataIndex() {
|
|
244
|
+
if (cached !== void 0) return cached;
|
|
245
|
+
try {
|
|
246
|
+
const response = await fetch(INDEX_URL);
|
|
247
|
+
if (!response.ok) return void 0;
|
|
248
|
+
cached = normalize(await response.json());
|
|
249
|
+
return cached;
|
|
250
|
+
} catch {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Download/refresh the metadata file through the server half (user-triggered
|
|
256
|
+
* from the title-row button). A success also refreshes the local cache.
|
|
257
|
+
*/
|
|
258
|
+
async function downloadMetadata() {
|
|
259
|
+
try {
|
|
260
|
+
const response = await fetch(DOWNLOAD_URL, { method: "POST" });
|
|
261
|
+
const body = await response.json().catch(() => void 0);
|
|
262
|
+
if (!response.ok || body?.ok !== true) return {
|
|
263
|
+
ok: false,
|
|
264
|
+
message: body?.message ?? `HTTP ${String(response.status)}`
|
|
265
|
+
};
|
|
266
|
+
cached = normalize(body["index"]);
|
|
267
|
+
return { ok: true };
|
|
268
|
+
} catch (error) {
|
|
269
|
+
return {
|
|
270
|
+
ok: false,
|
|
271
|
+
message: error instanceof Error ? error.message : String(error)
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Case-insensitive substring match over model ids, at most {@link limit}
|
|
277
|
+
* candidates (plan: show ids only, never the whole list).
|
|
278
|
+
* @param index - the metadata index, or undefined while unavailable.
|
|
279
|
+
* @param query - the user's typed text.
|
|
280
|
+
* @param limit - maximum candidates to return.
|
|
281
|
+
* @returns the matching ids; empty when the index is unavailable.
|
|
282
|
+
*/
|
|
283
|
+
function searchModelIds(index, query, limit = 10) {
|
|
284
|
+
if (index === void 0) return [];
|
|
285
|
+
const needle = query.trim().toLowerCase();
|
|
286
|
+
if (needle.length === 0) return [];
|
|
287
|
+
const hits = [];
|
|
288
|
+
for (const entry of index) {
|
|
289
|
+
if (!entry.id.toLowerCase().includes(needle)) continue;
|
|
290
|
+
hits.push(entry.id);
|
|
291
|
+
if (hits.length >= limit) break;
|
|
292
|
+
}
|
|
293
|
+
return hits;
|
|
158
294
|
}
|
|
159
295
|
//#endregion
|
|
160
|
-
//#region
|
|
296
|
+
//#region src/client/models-plus/compat.ts
|
|
161
297
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
298
|
+
* The minimal path ops carrying `after` over `before`, both as the card sees
|
|
299
|
+
* them. Only keys the card observed are named; fields absent from both sides
|
|
300
|
+
* produce no op, which is why edits are path-addressed rather than a rebuilt
|
|
301
|
+
* section.
|
|
302
|
+
* @param base - path of the edited subtree inside the user section.
|
|
303
|
+
* @param before - the subtree as loaded, or undefined when it is new.
|
|
304
|
+
* @param after - the subtree as edited.
|
|
305
|
+
* @returns ordered set/unset ops; empty when nothing changed.
|
|
166
306
|
*/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
307
|
+
function pathOps(base, before, after) {
|
|
308
|
+
const previous = typeof before === "object" && before !== null && !Array.isArray(before) ? before : {};
|
|
309
|
+
const ops = [];
|
|
310
|
+
for (const [key, value] of Object.entries(after)) {
|
|
311
|
+
if (JSON.stringify(previous[key]) === JSON.stringify(value)) continue;
|
|
312
|
+
ops.push({
|
|
313
|
+
op: "set",
|
|
314
|
+
path: [...base, key],
|
|
315
|
+
value
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
for (const key of Object.keys(previous)) if (!(key in after)) ops.push({
|
|
319
|
+
op: "unset",
|
|
320
|
+
path: [...base, key]
|
|
321
|
+
});
|
|
322
|
+
return ops;
|
|
170
323
|
}
|
|
171
324
|
/** Accepted capacity spellings: a decimal count with an optional K/M suffix. */
|
|
172
325
|
const CAPACITY_PATTERN = /^(\d+(?:\.\d+)?)([km])?$/i;
|
|
@@ -180,7 +333,7 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
180
333
|
* zeroes. The stored value stays a plain token count.
|
|
181
334
|
* @param text - raw field text.
|
|
182
335
|
* @returns the count; `undefined` when blank (inherit), `NaN` when unreadable
|
|
183
|
-
* (rejected by {@link
|
|
336
|
+
* (rejected by {@link validateModelRows} before any write).
|
|
184
337
|
*/
|
|
185
338
|
function parseCapacity(text) {
|
|
186
339
|
const trimmed = text.trim();
|
|
@@ -211,12 +364,52 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
211
364
|
if (!Array.isArray(value)) return [];
|
|
212
365
|
return value.map((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) ? entry : {});
|
|
213
366
|
}
|
|
367
|
+
/** Canonical key order for one models[] row (plugin-known fields first). */
|
|
368
|
+
const ROW_KEY_ORDER = [
|
|
369
|
+
"id",
|
|
370
|
+
"name",
|
|
371
|
+
"contextWindow",
|
|
372
|
+
"maxTokens",
|
|
373
|
+
"input",
|
|
374
|
+
"reasoningEfforts",
|
|
375
|
+
"compat"
|
|
376
|
+
];
|
|
377
|
+
/** Canonical key order for the row's compat sub-object. */
|
|
378
|
+
const COMPAT_KEY_ORDER = [
|
|
379
|
+
"supportsDeveloperRole",
|
|
380
|
+
"supportsReasoningEffort",
|
|
381
|
+
"thinkingFormat"
|
|
382
|
+
];
|
|
383
|
+
/**
|
|
384
|
+
* Re-key one model row into the canonical order (plugin-known fields in
|
|
385
|
+
* order, unknown/future fields kept at the tail so nothing is dropped).
|
|
386
|
+
* YAML emits keys in object insertion order, and interactive edits append
|
|
387
|
+
* keys at the tail — without this, rows saved after quick-load or gated-key
|
|
388
|
+
* removals come out with scrambled key orders (purely cosmetic, but noisy
|
|
389
|
+
* in settings.yaml diffs).
|
|
390
|
+
* @param row - the drafted row.
|
|
391
|
+
* @returns the re-keyed row (shallow copy).
|
|
392
|
+
*/
|
|
393
|
+
function normalizeModelRow(row) {
|
|
394
|
+
const next = {};
|
|
395
|
+
for (const key of ROW_KEY_ORDER) if (key in row) next[key] = row[key];
|
|
396
|
+
for (const key of Object.keys(row)) if (!ROW_KEY_ORDER.includes(key)) next[key] = row[key];
|
|
397
|
+
const compat = next["compat"];
|
|
398
|
+
if (typeof compat === "object" && compat !== null && !Array.isArray(compat)) {
|
|
399
|
+
const source = compat;
|
|
400
|
+
const normalized = {};
|
|
401
|
+
for (const key of COMPAT_KEY_ORDER) if (key in source) normalized[key] = source[key];
|
|
402
|
+
for (const key of Object.keys(source)) if (!COMPAT_KEY_ORDER.includes(key)) normalized[key] = source[key];
|
|
403
|
+
next["compat"] = normalized;
|
|
404
|
+
}
|
|
405
|
+
return next;
|
|
406
|
+
}
|
|
214
407
|
/**
|
|
215
408
|
* Validate adapter constraints that the serialized schema cannot express.
|
|
216
409
|
* @param value - user-owned `models` value, or undefined while inherited.
|
|
217
410
|
* @returns the first invalid row, or undefined when the adapter will accept it.
|
|
218
411
|
*/
|
|
219
|
-
function
|
|
412
|
+
function validateModelRows(value) {
|
|
220
413
|
if (value === void 0) return void 0;
|
|
221
414
|
const models = modelDrafts(value);
|
|
222
415
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -249,273 +442,33 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
249
442
|
};
|
|
250
443
|
}
|
|
251
444
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const at = rowOf(key);
|
|
276
|
-
if (at === index) continue;
|
|
277
|
-
next.set(at > index ? key.replace(/^\d+/, String(at - 1)) : key, text);
|
|
278
|
-
}
|
|
279
|
-
return next;
|
|
280
|
-
});
|
|
281
|
-
setExpanded((current) => {
|
|
282
|
-
const next = /* @__PURE__ */ new Set();
|
|
283
|
-
for (const at of current) {
|
|
284
|
-
if (at === index) continue;
|
|
285
|
-
next.add(at > index ? at - 1 : at);
|
|
286
|
-
}
|
|
287
|
-
return next;
|
|
288
|
-
});
|
|
289
|
-
props.onChange(props.models.filter((_model, at) => at !== index).map((model) => ({ ...model })));
|
|
290
|
-
};
|
|
291
|
-
const reset = () => {
|
|
292
|
-
setEditing(/* @__PURE__ */ new Map());
|
|
293
|
-
setExpanded(/* @__PURE__ */ new Set());
|
|
294
|
-
props.onReset();
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region src/client/models-plus/presets.ts
|
|
447
|
+
/** The tri-state `compat.supportsDeveloperRole` seed: absent (reads checked). */
|
|
448
|
+
const DEVELOPER_ROLE_SEED = {};
|
|
449
|
+
/** Draft seed for one new model row. */
|
|
450
|
+
function newModelDraft() {
|
|
451
|
+
return {
|
|
452
|
+
id: "",
|
|
453
|
+
input: ["text", "image"],
|
|
454
|
+
reasoningEfforts: {
|
|
455
|
+
off: null,
|
|
456
|
+
minimal: "minimal",
|
|
457
|
+
low: "low",
|
|
458
|
+
medium: "medium",
|
|
459
|
+
high: "high",
|
|
460
|
+
xhigh: "xhigh",
|
|
461
|
+
max: "max"
|
|
462
|
+
},
|
|
463
|
+
compat: {
|
|
464
|
+
...DEVELOPER_ROLE_SEED,
|
|
465
|
+
supportsReasoningEffort: true,
|
|
466
|
+
thinkingFormat: "openai"
|
|
467
|
+
}
|
|
295
468
|
};
|
|
296
|
-
const toggle = (index) => {
|
|
297
|
-
setExpanded((current) => {
|
|
298
|
-
const next = new Set(current);
|
|
299
|
-
if (!next.delete(index)) next.add(index);
|
|
300
|
-
return next;
|
|
301
|
-
});
|
|
302
|
-
};
|
|
303
|
-
/** The field's text: its live keystrokes, else the stored count spelled short. */
|
|
304
|
-
const capacityText = (model, index, field) => {
|
|
305
|
-
const typed = editing.get(`${String(index)}:${field}`);
|
|
306
|
-
if (typed !== void 0) return typed;
|
|
307
|
-
const value = model[field];
|
|
308
|
-
return typeof value === "number" ? formatCapacity(value) : "";
|
|
309
|
-
};
|
|
310
|
-
const settleCapacity = (index, field) => {
|
|
311
|
-
const key = `${String(index)}:${field}`;
|
|
312
|
-
const typed = editing.get(key);
|
|
313
|
-
if (typed === void 0) return;
|
|
314
|
-
const parsed = parseCapacity(typed);
|
|
315
|
-
if (parsed !== void 0 && Number.isNaN(parsed)) return;
|
|
316
|
-
setEditing((current) => {
|
|
317
|
-
const next = new Map(current);
|
|
318
|
-
next.delete(key);
|
|
319
|
-
return next;
|
|
320
|
-
});
|
|
321
|
-
};
|
|
322
|
-
/** One capacity field of one row, rendered inside the row's disclosure. */
|
|
323
|
-
const capacityField = (model, index, field, fallback) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
324
|
-
className: ModelsSection_module_default["modelField"],
|
|
325
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
326
|
-
className: ModelsSection_module_default["modelFieldLabel"],
|
|
327
|
-
children: props.t(field === "contextWindow" ? "contextWindow" : "maxTokens")
|
|
328
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
329
|
-
className: ModelsSection_module_default["input"],
|
|
330
|
-
type: "text",
|
|
331
|
-
inputMode: "numeric",
|
|
332
|
-
value: capacityText(model, index, field),
|
|
333
|
-
placeholder: fallback === void 0 ? props.t(field === "contextWindow" ? "contextWindowPlaceholder" : "maxTokensPlaceholder") : formatCapacity(fallback),
|
|
334
|
-
"aria-label": `${props.t(field === "contextWindow" ? "contextWindow" : "maxTokens")} ${String(index + 1)}`,
|
|
335
|
-
disabled: props.disabled,
|
|
336
|
-
onChange: (event) => {
|
|
337
|
-
const text = event.target.value;
|
|
338
|
-
setEditing((current) => new Map(current).set(`${String(index)}:${field}`, text));
|
|
339
|
-
update(index, field, parseCapacity(text));
|
|
340
|
-
},
|
|
341
|
-
onBlur: () => {
|
|
342
|
-
settleCapacity(index, field);
|
|
343
|
-
}
|
|
344
|
-
})]
|
|
345
|
-
});
|
|
346
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
347
|
-
className: ModelsSection_module_default["modelCatalog"],
|
|
348
|
-
"aria-label": props.t("models"),
|
|
349
|
-
children: [
|
|
350
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
351
|
-
className: ModelsSection_module_default["modelListHead"],
|
|
352
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
353
|
-
className: ModelsSection_module_default["modelCatalogHeading"],
|
|
354
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
355
|
-
className: ModelsSection_module_default["modelCatalogTitle"],
|
|
356
|
-
children: props.t("models")
|
|
357
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
358
|
-
className: ModelsSection_module_default["modelCatalogMeta"],
|
|
359
|
-
children: props.overridden ? props.t("modelsCustomized") : props.t("modelsInherited")
|
|
360
|
-
})]
|
|
361
|
-
}), props.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
362
|
-
type: "button",
|
|
363
|
-
className: ModelsSection_module_default["linkButton"],
|
|
364
|
-
disabled: props.disabled,
|
|
365
|
-
onClick: reset,
|
|
366
|
-
children: props.t("resetModels")
|
|
367
|
-
}) : null]
|
|
368
|
-
}),
|
|
369
|
-
props.models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
370
|
-
className: ModelsSection_module_default["modelEmpty"],
|
|
371
|
-
children: props.t("modelsEmpty")
|
|
372
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
373
|
-
className: ModelsSection_module_default["modelList"],
|
|
374
|
-
children: props.models.map((model, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
375
|
-
className: ModelsSection_module_default["modelEntry"],
|
|
376
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
377
|
-
className: ModelsSection_module_default["modelRow"],
|
|
378
|
-
children: [
|
|
379
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
380
|
-
className: ModelsSection_module_default["input"],
|
|
381
|
-
type: "text",
|
|
382
|
-
value: typeof model["id"] === "string" ? model["id"] : "",
|
|
383
|
-
placeholder: props.t("modelId"),
|
|
384
|
-
"aria-label": `${props.t("modelId")} ${String(index + 1)}`,
|
|
385
|
-
disabled: props.disabled,
|
|
386
|
-
onChange: (event) => {
|
|
387
|
-
update(index, "id", event.target.value);
|
|
388
|
-
},
|
|
389
|
-
onBlur: (event) => {
|
|
390
|
-
const trimmed = event.target.value.trim();
|
|
391
|
-
if (trimmed !== event.target.value) update(index, "id", trimmed);
|
|
392
|
-
}
|
|
393
|
-
}),
|
|
394
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
395
|
-
className: ModelsSection_module_default["input"],
|
|
396
|
-
type: "text",
|
|
397
|
-
value: typeof model["name"] === "string" ? model["name"] : "",
|
|
398
|
-
placeholder: props.t("modelName"),
|
|
399
|
-
"aria-label": `${props.t("modelName")} ${String(index + 1)}`,
|
|
400
|
-
disabled: props.disabled,
|
|
401
|
-
onChange: (event) => {
|
|
402
|
-
update(index, "name", event.target.value === "" ? void 0 : event.target.value);
|
|
403
|
-
}
|
|
404
|
-
}),
|
|
405
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
406
|
-
type: "button",
|
|
407
|
-
className: ModelsSection_module_default["iconButton"],
|
|
408
|
-
"aria-label": `${props.t("modelAdvanced")} ${String(index + 1)}`,
|
|
409
|
-
"aria-expanded": expanded.has(index),
|
|
410
|
-
title: props.t("modelAdvanced"),
|
|
411
|
-
onClick: () => {
|
|
412
|
-
toggle(index);
|
|
413
|
-
},
|
|
414
|
-
children: expanded.has(index) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
|
|
415
|
-
}),
|
|
416
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
417
|
-
type: "button",
|
|
418
|
-
className: `${ModelsSection_module_default["iconButton"]} ${ModelsSection_module_default["iconButtonDanger"]}`,
|
|
419
|
-
"aria-label": `${props.t("removeModel")} ${String(index + 1)}`,
|
|
420
|
-
title: props.t("removeModel"),
|
|
421
|
-
disabled: props.disabled,
|
|
422
|
-
onClick: () => {
|
|
423
|
-
remove(index);
|
|
424
|
-
},
|
|
425
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
|
|
426
|
-
})
|
|
427
|
-
]
|
|
428
|
-
}), expanded.has(index) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
429
|
-
className: ModelsSection_module_default["modelAdvanced"],
|
|
430
|
-
children: [capacityField(model, index, "contextWindow", props.defaultContextWindow), capacityField(model, index, "maxTokens", props.defaultMaxTokens)]
|
|
431
|
-
}) : null]
|
|
432
|
-
}, index))
|
|
433
|
-
}),
|
|
434
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
435
|
-
type: "button",
|
|
436
|
-
className: ModelsSection_module_default["addModelButton"],
|
|
437
|
-
disabled: props.disabled,
|
|
438
|
-
onClick: () => {
|
|
439
|
-
props.onChange([...props.models.map((model) => ({ ...model })), { id: "" }]);
|
|
440
|
-
},
|
|
441
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), props.t("addModel")]
|
|
442
|
-
})
|
|
443
|
-
]
|
|
444
|
-
});
|
|
445
469
|
}
|
|
446
470
|
//#endregion
|
|
447
|
-
//#region src/client/ModelsSection.ext.module.css
|
|
448
|
-
var ModelsSection_ext_module_default = {
|
|
449
|
-
"addActions": "cDGz_W_addActions",
|
|
450
|
-
"addBlock": "cDGz_W_addBlock",
|
|
451
|
-
"addButton": "cDGz_W_addButton",
|
|
452
|
-
"addCard": "cDGz_W_addCard",
|
|
453
|
-
"addModelButton": "cDGz_W_addModelButton",
|
|
454
|
-
"advancedHint": "cDGz_W_advancedHint",
|
|
455
|
-
"candidate": "cDGz_W_candidate",
|
|
456
|
-
"candidateEmpty": "cDGz_W_candidateEmpty",
|
|
457
|
-
"candidateId": "cDGz_W_candidateId",
|
|
458
|
-
"candidateLabel": "cDGz_W_candidateLabel",
|
|
459
|
-
"candidateList": "cDGz_W_candidateList",
|
|
460
|
-
"candidateSearch": "cDGz_W_candidateSearch",
|
|
461
|
-
"candidateToolbar": "cDGz_W_candidateToolbar",
|
|
462
|
-
"credentialDot": "cDGz_W_credentialDot",
|
|
463
|
-
"credentialDotConfigured": "cDGz_W_credentialDotConfigured",
|
|
464
|
-
"credentialDotMissing": "cDGz_W_credentialDotMissing",
|
|
465
|
-
"customized": "cDGz_W_customized",
|
|
466
|
-
"customizedBody": "cDGz_W_customizedBody",
|
|
467
|
-
"customizedSummary": "cDGz_W_customizedSummary",
|
|
468
|
-
"dangerButton": "cDGz_W_dangerButton",
|
|
469
|
-
"deleteConfirm": "cDGz_W_deleteConfirm",
|
|
470
|
-
"deleteDialog": "cDGz_W_deleteDialog",
|
|
471
|
-
"editor": "cDGz_W_editor",
|
|
472
|
-
"editorActions": "cDGz_W_editorActions",
|
|
473
|
-
"editorHeader": "cDGz_W_editorHeader",
|
|
474
|
-
"editorRoute": "cDGz_W_editorRoute",
|
|
475
|
-
"editorTitle": "cDGz_W_editorTitle",
|
|
476
|
-
"error": "cDGz_W_error",
|
|
477
|
-
"fetchDialog": "cDGz_W_fetchDialog",
|
|
478
|
-
"field": "cDGz_W_field",
|
|
479
|
-
"fieldLabel": "cDGz_W_fieldLabel",
|
|
480
|
-
"hiddenLabel": "cDGz_W_hiddenLabel",
|
|
481
|
-
"iconButton": "cDGz_W_iconButton",
|
|
482
|
-
"iconButtonDanger": "cDGz_W_iconButtonDanger",
|
|
483
|
-
"input": "cDGz_W_input",
|
|
484
|
-
"intro": "cDGz_W_intro",
|
|
485
|
-
"linkButton": "cDGz_W_linkButton",
|
|
486
|
-
"modelAdvanced": "cDGz_W_modelAdvanced",
|
|
487
|
-
"modelCatalog": "cDGz_W_modelCatalog",
|
|
488
|
-
"modelCatalogHeading": "cDGz_W_modelCatalogHeading",
|
|
489
|
-
"modelCatalogMeta": "cDGz_W_modelCatalogMeta",
|
|
490
|
-
"modelCatalogTitle": "cDGz_W_modelCatalogTitle",
|
|
491
|
-
"modelEmpty": "cDGz_W_modelEmpty",
|
|
492
|
-
"modelEntry": "cDGz_W_modelEntry",
|
|
493
|
-
"modelExtensionFields": "cDGz_W_modelExtensionFields",
|
|
494
|
-
"modelField": "cDGz_W_modelField",
|
|
495
|
-
"modelFieldLabel": "cDGz_W_modelFieldLabel",
|
|
496
|
-
"modelList": "cDGz_W_modelList",
|
|
497
|
-
"modelListHead": "cDGz_W_modelListHead",
|
|
498
|
-
"modelRow": "cDGz_W_modelRow",
|
|
499
|
-
"notice": "cDGz_W_notice",
|
|
500
|
-
"primaryButton": "cDGz_W_primaryButton",
|
|
501
|
-
"rowActions": "cDGz_W_rowActions",
|
|
502
|
-
"rowCard": "cDGz_W_rowCard",
|
|
503
|
-
"rowHead": "cDGz_W_rowHead",
|
|
504
|
-
"rowIdentity": "cDGz_W_rowIdentity",
|
|
505
|
-
"rowName": "cDGz_W_rowName",
|
|
506
|
-
"rows": "cDGz_W_rows",
|
|
507
|
-
"rowTag": "cDGz_W_rowTag",
|
|
508
|
-
"savedNotice": "cDGz_W_savedNotice",
|
|
509
|
-
"secondaryButton": "cDGz_W_secondaryButton",
|
|
510
|
-
"section": "cDGz_W_section",
|
|
511
|
-
"selectInput": "cDGz_W_selectInput",
|
|
512
|
-
"setupCard": "cDGz_W_setupCard",
|
|
513
|
-
"switchThumb": "cDGz_W_switchThumb",
|
|
514
|
-
"title": "cDGz_W_title"
|
|
515
|
-
};
|
|
516
|
-
//#endregion
|
|
517
471
|
//#region src/client/extension-meta.ts
|
|
518
|
-
const ADAPTER_VERSION = "0.1.2-rc.1";
|
|
519
472
|
/** Thinking levels offered by llm-pi-ai (THINKING_LEVEL_GATE @ 0.1.2-alpha.4). Only `off` may carry an empty wire value. */
|
|
520
473
|
const THINKING_LEVELS = [
|
|
521
474
|
"off",
|
|
@@ -526,8 +479,6 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
526
479
|
"xhigh",
|
|
527
480
|
"max"
|
|
528
481
|
];
|
|
529
|
-
/** Input modalities accepted by llm-pi-ai (MODALITY_GATE @ 0.1.2-alpha.4). */
|
|
530
|
-
const MODALITIES = ["text", "image"];
|
|
531
482
|
/** Reasoning wire formats understood by llm-pi-ai (SUPPORTED_THINKING_FORMATS @ 0.1.2-alpha.4). */
|
|
532
483
|
const THINKING_FORMATS = [
|
|
533
484
|
"openai",
|
|
@@ -637,7 +588,6 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
637
588
|
onboardingSave: "Save and continue",
|
|
638
589
|
onboardingSaving: "Saving…",
|
|
639
590
|
keyRequired: "Enter an API key to continue.",
|
|
640
|
-
adapterVersion: "Current adapter: {version}",
|
|
641
591
|
extDeclare: "Declared",
|
|
642
592
|
extSection: "Model extensions",
|
|
643
593
|
extSectionHint: "Extra per-model fields that live in settings.yaml.",
|
|
@@ -749,7 +699,6 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
749
699
|
onboardingSave: "保存并继续",
|
|
750
700
|
onboardingSaving: "保存中…",
|
|
751
701
|
keyRequired: "请输入 API 密钥以继续。",
|
|
752
|
-
adapterVersion: "当前适配器:{version}",
|
|
753
702
|
extDeclare: "声明",
|
|
754
703
|
extSection: "模型扩展",
|
|
755
704
|
extSectionHint: "settings.yaml 中按模型生效的扩展字段。",
|
|
@@ -767,167 +716,374 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
767
716
|
thinkingFormat: "思考格式"
|
|
768
717
|
};
|
|
769
718
|
//#endregion
|
|
770
|
-
//#region src/client/
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
719
|
+
//#region src/client/models-plus/models-plus.module.css
|
|
720
|
+
var models_plus_module_default = {
|
|
721
|
+
"addActions": "J84-9a_addActions",
|
|
722
|
+
"addBlock": "J84-9a_addBlock",
|
|
723
|
+
"addButton": "J84-9a_addButton",
|
|
724
|
+
"addCard": "J84-9a_addCard",
|
|
725
|
+
"addModelButton": "J84-9a_addModelButton",
|
|
726
|
+
"advancedHint": "J84-9a_advancedHint",
|
|
727
|
+
"candidate": "J84-9a_candidate",
|
|
728
|
+
"candidateEmpty": "J84-9a_candidateEmpty",
|
|
729
|
+
"candidateId": "J84-9a_candidateId",
|
|
730
|
+
"candidateLabel": "J84-9a_candidateLabel",
|
|
731
|
+
"candidateList": "J84-9a_candidateList",
|
|
732
|
+
"candidateSearch": "J84-9a_candidateSearch",
|
|
733
|
+
"candidateToolbar": "J84-9a_candidateToolbar",
|
|
734
|
+
"credentialDot": "J84-9a_credentialDot",
|
|
735
|
+
"credentialDotConfigured": "J84-9a_credentialDotConfigured",
|
|
736
|
+
"credentialDotMissing": "J84-9a_credentialDotMissing",
|
|
737
|
+
"customizedSummary": "J84-9a_customizedSummary",
|
|
738
|
+
"dangerButton": "J84-9a_dangerButton",
|
|
739
|
+
"deleteConfirm": "J84-9a_deleteConfirm",
|
|
740
|
+
"deleteDialog": "J84-9a_deleteDialog",
|
|
741
|
+
"editor": "J84-9a_editor",
|
|
742
|
+
"editorActions": "J84-9a_editorActions",
|
|
743
|
+
"error": "J84-9a_error",
|
|
744
|
+
"extBlock": "J84-9a_extBlock",
|
|
745
|
+
"extCheck": "J84-9a_extCheck",
|
|
746
|
+
"extCheckbox": "J84-9a_extCheckbox",
|
|
747
|
+
"extCheckLabel": "J84-9a_extCheckLabel",
|
|
748
|
+
"extField": "J84-9a_extField",
|
|
749
|
+
"extGated": "J84-9a_extGated",
|
|
750
|
+
"extGroup": "J84-9a_extGroup",
|
|
751
|
+
"extGroupFirst": "J84-9a_extGroupFirst",
|
|
752
|
+
"extHint": "J84-9a_extHint",
|
|
753
|
+
"extLabel": "J84-9a_extLabel",
|
|
754
|
+
"extTable": "J84-9a_extTable",
|
|
755
|
+
"extTag": "J84-9a_extTag",
|
|
756
|
+
"extTHead": "J84-9a_extTHead",
|
|
757
|
+
"extTRow": "J84-9a_extTRow",
|
|
758
|
+
"fetchDialog": "J84-9a_fetchDialog",
|
|
759
|
+
"field": "J84-9a_field",
|
|
760
|
+
"fieldLabel": "J84-9a_fieldLabel",
|
|
761
|
+
"iconButton": "J84-9a_iconButton",
|
|
762
|
+
"iconButtonDanger": "J84-9a_iconButtonDanger",
|
|
763
|
+
"input": "J84-9a_input",
|
|
764
|
+
"intro": "J84-9a_intro",
|
|
765
|
+
"linkButton": "J84-9a_linkButton",
|
|
766
|
+
"modal": "J84-9a_modal",
|
|
767
|
+
"modalDesc": "J84-9a_modalDesc",
|
|
768
|
+
"modalFoot": "J84-9a_modalFoot",
|
|
769
|
+
"modalMask": "J84-9a_modalMask",
|
|
770
|
+
"modalTitle": "J84-9a_modalTitle",
|
|
771
|
+
"modelAdvanced": "J84-9a_modelAdvanced",
|
|
772
|
+
"modelCatalog": "J84-9a_modelCatalog",
|
|
773
|
+
"modelCatalogHeading": "J84-9a_modelCatalogHeading",
|
|
774
|
+
"modelCatalogMeta": "J84-9a_modelCatalogMeta",
|
|
775
|
+
"modelCatalogTitle": "J84-9a_modelCatalogTitle",
|
|
776
|
+
"modelEmpty": "J84-9a_modelEmpty",
|
|
777
|
+
"modelEntry": "J84-9a_modelEntry",
|
|
778
|
+
"modelField": "J84-9a_modelField",
|
|
779
|
+
"modelFieldLabel": "J84-9a_modelFieldLabel",
|
|
780
|
+
"modelList": "J84-9a_modelList",
|
|
781
|
+
"modelListHead": "J84-9a_modelListHead",
|
|
782
|
+
"modelRow": "J84-9a_modelRow",
|
|
783
|
+
"primaryButton": "J84-9a_primaryButton",
|
|
784
|
+
"quickEmpty": "J84-9a_quickEmpty",
|
|
785
|
+
"quickItem": "J84-9a_quickItem",
|
|
786
|
+
"quickList": "J84-9a_quickList",
|
|
787
|
+
"quickWrap": "J84-9a_quickWrap",
|
|
788
|
+
"rowActions": "J84-9a_rowActions",
|
|
789
|
+
"rowCard": "J84-9a_rowCard",
|
|
790
|
+
"rowHead": "J84-9a_rowHead",
|
|
791
|
+
"rowIdentity": "J84-9a_rowIdentity",
|
|
792
|
+
"rowName": "J84-9a_rowName",
|
|
793
|
+
"rows": "J84-9a_rows",
|
|
794
|
+
"rowTag": "J84-9a_rowTag",
|
|
795
|
+
"savedNotice": "J84-9a_savedNotice",
|
|
796
|
+
"secondaryButton": "J84-9a_secondaryButton",
|
|
797
|
+
"section": "J84-9a_section",
|
|
798
|
+
"selectInput": "J84-9a_selectInput",
|
|
799
|
+
"setupCard": "J84-9a_setupCard",
|
|
800
|
+
"switchRow": "J84-9a_switchRow",
|
|
801
|
+
"tabBar": "J84-9a_tabBar",
|
|
802
|
+
"tabButton": "J84-9a_tabButton",
|
|
803
|
+
"tabButtonActive": "J84-9a_tabButtonActive",
|
|
804
|
+
"tabPanel": "J84-9a_tabPanel",
|
|
805
|
+
"tabs": "J84-9a_tabs",
|
|
806
|
+
"title": "J84-9a_title",
|
|
807
|
+
"titleRow": "J84-9a_titleRow"
|
|
820
808
|
};
|
|
809
|
+
//#endregion
|
|
810
|
+
//#region src/client/models-plus/QuickLoad.tsx
|
|
811
|
+
/**
|
|
812
|
+
* Quick-load: a searchable dropdown over the models.dev metadata index.
|
|
813
|
+
* Plan constraints: ids only, case-insensitive substring match, at most 10
|
|
814
|
+
* candidates rendered — the index is filtered on every keystroke and the
|
|
815
|
+
* candidate list is only mounted while open, so a 370-row index never lands
|
|
816
|
+
* in the DOM wholesale.
|
|
817
|
+
*/
|
|
818
|
+
/**
|
|
819
|
+
* Render the quick-load input with its filtered candidate dropdown.
|
|
820
|
+
* @param props - the pick handler and disabled flag.
|
|
821
|
+
* @returns the quick-load field.
|
|
822
|
+
*/
|
|
823
|
+
function QuickLoad(props) {
|
|
824
|
+
const [query, setQuery] = (0, react.useState)("");
|
|
825
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
826
|
+
const [index, setIndex] = (0, react.useState)(void 0);
|
|
827
|
+
const ensureIndex = () => {
|
|
828
|
+
if (index !== void 0) return;
|
|
829
|
+
getMetadataIndex().then((loaded) => {
|
|
830
|
+
setIndex(loaded);
|
|
831
|
+
});
|
|
832
|
+
};
|
|
833
|
+
const hits = searchModelIds(index, query, 10);
|
|
834
|
+
const pick = (id) => {
|
|
835
|
+
const entry = index?.find((candidate) => candidate.id === id);
|
|
836
|
+
setOpen(false);
|
|
837
|
+
setQuery("");
|
|
838
|
+
if (entry !== void 0) props.onPick(entry);
|
|
839
|
+
};
|
|
840
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
841
|
+
className: models_plus_module_default["quickWrap"],
|
|
842
|
+
children: [
|
|
843
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
844
|
+
className: models_plus_module_default["extLabel"],
|
|
845
|
+
children: "快速装入"
|
|
846
|
+
}),
|
|
847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
848
|
+
className: models_plus_module_default["input"],
|
|
849
|
+
type: "text",
|
|
850
|
+
value: query,
|
|
851
|
+
placeholder: "搜索模型 id(如 aper)…",
|
|
852
|
+
"aria-label": "快速装入",
|
|
853
|
+
disabled: props.disabled,
|
|
854
|
+
onFocus: () => {
|
|
855
|
+
ensureIndex();
|
|
856
|
+
setOpen(true);
|
|
857
|
+
},
|
|
858
|
+
onBlur: () => {
|
|
859
|
+
setOpen(false);
|
|
860
|
+
},
|
|
861
|
+
onChange: (event) => {
|
|
862
|
+
setQuery(event.target.value);
|
|
863
|
+
setOpen(true);
|
|
864
|
+
}
|
|
865
|
+
}),
|
|
866
|
+
open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
867
|
+
className: models_plus_module_default["quickList"],
|
|
868
|
+
children: index === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
869
|
+
className: models_plus_module_default["quickEmpty"],
|
|
870
|
+
children: "元数据预填不可用——点击页首「下载/更新元数据」"
|
|
871
|
+
}) : hits.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
872
|
+
className: models_plus_module_default["quickEmpty"],
|
|
873
|
+
children: query.trim().length === 0 ? "输入关键字搜索模型 id" : "没有匹配的模型"
|
|
874
|
+
}) : hits.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
875
|
+
type: "button",
|
|
876
|
+
className: models_plus_module_default["quickItem"],
|
|
877
|
+
onMouseDown: (event) => {
|
|
878
|
+
event.preventDefault();
|
|
879
|
+
pick(id);
|
|
880
|
+
},
|
|
881
|
+
children: id
|
|
882
|
+
}, id))
|
|
883
|
+
}) : null
|
|
884
|
+
]
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
//#endregion
|
|
888
|
+
//#region src/client/models-plus/ModelEntryPanel.tsx
|
|
889
|
+
/**
|
|
890
|
+
* One model entry's expanded panel: quick-load, capacities (the official
|
|
891
|
+
* modelAdvanced fields, merged into this block per the v1.0.0 design), input
|
|
892
|
+
* modalities, the developer-role tri-state, the reasoning-effort gate and its
|
|
893
|
+
* gated area (thinking format, then the seven-level effort table).
|
|
894
|
+
*
|
|
895
|
+
* Every edit patches the row's draft object through `onChange` — persistence
|
|
896
|
+
* rides the card's existing path-ops save with no extra wire calls. Clearing
|
|
897
|
+
* the effort gate drops `reasoningEfforts` AND `compat.thinkingFormat` in ONE
|
|
898
|
+
* commit so partial updates never overwrite each other (no orphan settings).
|
|
899
|
+
*/
|
|
821
900
|
/** The row's compat sub-object as a plain record (absent → empty). */
|
|
822
901
|
function compatOf(model) {
|
|
823
902
|
const value = model["compat"];
|
|
824
903
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
825
904
|
}
|
|
826
|
-
/** The row's reasoningEfforts dict (absent/
|
|
905
|
+
/** The row's reasoningEfforts dict (absent/invalid → undefined = not declared). */
|
|
827
906
|
function effortsOf(model) {
|
|
828
907
|
const value = model["reasoningEfforts"];
|
|
829
908
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
830
909
|
return value;
|
|
831
910
|
}
|
|
911
|
+
/** The row's input modality list (absent → empty, matching the host reader). */
|
|
912
|
+
function inputsOf(model) {
|
|
913
|
+
const value = model["input"];
|
|
914
|
+
return Array.isArray(value) ? value.filter((m) => typeof m === "string") : [];
|
|
915
|
+
}
|
|
832
916
|
/**
|
|
833
|
-
* Render
|
|
917
|
+
* Render one model entry's expanded panel.
|
|
834
918
|
* @param props - the row draft, its writer, and the disabled flag.
|
|
835
|
-
* @returns the
|
|
919
|
+
* @returns the expanded panel.
|
|
836
920
|
*/
|
|
837
|
-
function
|
|
838
|
-
const { model, onChange, disabled
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const supportsReasoningEffort = compat["supportsReasoningEffort"] === true;
|
|
845
|
-
const supportsDeveloperRole = compat["supportsDeveloperRole"] !== false;
|
|
846
|
-
/** Write one key into `compat`, merging into the row's existing compat and
|
|
847
|
-
* clearing the whole `compat` node when it would become empty. Used for
|
|
848
|
-
* `supportsReasoningEffort`, `thinkingFormat` and `supportsDeveloperRole`
|
|
849
|
-
* ONLY — those are the only model extension fields that live under `compat`. */
|
|
850
|
-
const setCompatKey = (key, value) => {
|
|
851
|
-
const next = { ...compat };
|
|
852
|
-
if (value === void 0) delete next[key];
|
|
921
|
+
function ModelEntryPanel(props) {
|
|
922
|
+
const { model, onChange, disabled } = props;
|
|
923
|
+
const [capacityText, setCapacityText] = (0, react.useState)(() => /* @__PURE__ */ new Map());
|
|
924
|
+
/** Patch one key onto the row; `undefined` clears it. */
|
|
925
|
+
const setKey = (key, value) => {
|
|
926
|
+
const next = { ...model };
|
|
927
|
+
if (value === void 0) Reflect.deleteProperty(next, key);
|
|
853
928
|
else next[key] = value;
|
|
854
|
-
onChange(
|
|
929
|
+
onChange(next);
|
|
930
|
+
};
|
|
931
|
+
/** Patch one key inside `compat`; an emptied compat leaves no trace. */
|
|
932
|
+
const setCompatKey = (key, value) => {
|
|
933
|
+
const compat = { ...compatOf(model) };
|
|
934
|
+
if (value === void 0) Reflect.deleteProperty(compat, key);
|
|
935
|
+
else compat[key] = value;
|
|
936
|
+
setKey("compat", Object.keys(compat).length === 0 ? void 0 : compat);
|
|
855
937
|
};
|
|
856
938
|
const setEfforts = (efforts) => {
|
|
857
|
-
|
|
939
|
+
setKey("reasoningEfforts", efforts);
|
|
858
940
|
};
|
|
859
941
|
const setInput = (modalities) => {
|
|
860
|
-
|
|
942
|
+
setKey("input", modalities.length === 0 ? void 0 : [...modalities]);
|
|
861
943
|
};
|
|
862
|
-
const
|
|
863
|
-
const
|
|
864
|
-
if (
|
|
865
|
-
else
|
|
866
|
-
|
|
944
|
+
const applyMetadata = (entry) => {
|
|
945
|
+
const next = { ...model };
|
|
946
|
+
if (entry.context === void 0) Reflect.deleteProperty(next, "contextWindow");
|
|
947
|
+
else next["contextWindow"] = entry.context;
|
|
948
|
+
if (entry.output === void 0) Reflect.deleteProperty(next, "maxTokens");
|
|
949
|
+
else next["maxTokens"] = entry.output;
|
|
950
|
+
const compat = { ...compatOf(model) };
|
|
951
|
+
compat["supportsReasoningEffort"] = entry.reasoning;
|
|
952
|
+
if (!entry.reasoning) {
|
|
953
|
+
Reflect.deleteProperty(compat, "thinkingFormat");
|
|
954
|
+
Reflect.deleteProperty(next, "reasoningEfforts");
|
|
955
|
+
}
|
|
956
|
+
setKey("compat", Object.keys(compat).length === 0 ? void 0 : compat);
|
|
957
|
+
const supported = entry.input.filter((m) => m === "text" || m === "image");
|
|
958
|
+
if (supported.length > 0) next["input"] = supported;
|
|
867
959
|
onChange(next);
|
|
960
|
+
setCapacityText(/* @__PURE__ */ new Map());
|
|
868
961
|
};
|
|
869
|
-
const
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
962
|
+
const capacityField = (field, label) => {
|
|
963
|
+
const stored = model[field];
|
|
964
|
+
const text = capacityText.get(field) ?? (typeof stored === "number" ? formatCapacity(stored) : "");
|
|
965
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
966
|
+
className: models_plus_module_default["modelField"],
|
|
967
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
968
|
+
className: models_plus_module_default["modelFieldLabel"],
|
|
969
|
+
children: label
|
|
970
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
971
|
+
className: models_plus_module_default["input"],
|
|
972
|
+
type: "text",
|
|
973
|
+
inputMode: "numeric",
|
|
974
|
+
value: text,
|
|
975
|
+
placeholder: "使用提供方默认值",
|
|
976
|
+
"aria-label": label,
|
|
977
|
+
disabled,
|
|
978
|
+
onChange: (event) => {
|
|
979
|
+
setCapacityText((current) => new Map(current).set(field, event.target.value));
|
|
980
|
+
setKey(field, parseCapacity(event.target.value));
|
|
981
|
+
},
|
|
982
|
+
onBlur: () => {
|
|
983
|
+
const raw = capacityText.get(field);
|
|
984
|
+
if (raw === void 0) return;
|
|
985
|
+
const parsed = parseCapacity(raw);
|
|
986
|
+
if (parsed !== void 0 && Number.isNaN(parsed)) return;
|
|
987
|
+
setCapacityText((current) => {
|
|
988
|
+
const next = new Map(current);
|
|
989
|
+
next.delete(field);
|
|
990
|
+
return next;
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
})]
|
|
994
|
+
});
|
|
878
995
|
};
|
|
996
|
+
const compat = compatOf(model);
|
|
997
|
+
const developerRole = compat["supportsDeveloperRole"] !== false;
|
|
998
|
+
const supportsReasoningEffort = compat["supportsReasoningEffort"] === true;
|
|
879
999
|
const efforts = effortsOf(model);
|
|
1000
|
+
const toggleGate = (checked) => {
|
|
1001
|
+
if (checked) {
|
|
1002
|
+
setCompatKey("supportsReasoningEffort", true);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
const nextCompat = { ...compat };
|
|
1006
|
+
Reflect.deleteProperty(nextCompat, "supportsReasoningEffort");
|
|
1007
|
+
Reflect.deleteProperty(nextCompat, "thinkingFormat");
|
|
1008
|
+
const next = {
|
|
1009
|
+
...model,
|
|
1010
|
+
compat: Object.keys(nextCompat).length === 0 ? void 0 : nextCompat
|
|
1011
|
+
};
|
|
1012
|
+
Reflect.deleteProperty(next, "reasoningEfforts");
|
|
1013
|
+
onChange(next);
|
|
1014
|
+
};
|
|
1015
|
+
const toggleLevel = (level, checked) => {
|
|
1016
|
+
const current = { ...efforts ?? {} };
|
|
1017
|
+
if (checked) current[level] = level === "off" ? null : level;
|
|
1018
|
+
else Reflect.deleteProperty(current, level);
|
|
1019
|
+
setEfforts(Object.keys(current).length === 0 ? void 0 : current);
|
|
1020
|
+
};
|
|
1021
|
+
const setWireValue = (level, text) => {
|
|
1022
|
+
if (efforts === void 0 || !(level in efforts)) return;
|
|
1023
|
+
const current = { ...efforts };
|
|
1024
|
+
current[level] = text.trim().length === 0 ? null : text.trim();
|
|
1025
|
+
setEfforts(current);
|
|
1026
|
+
};
|
|
880
1027
|
const declaredLevels = efforts === void 0 ? [] : THINKING_LEVELS.filter((level) => level in efforts);
|
|
881
|
-
const inputList = Array.isArray(model["input"]) ? model["input"] : [];
|
|
882
|
-
const thinkingFormat = typeof compat["thinkingFormat"] === "string" ? compat["thinkingFormat"] : void 0;
|
|
883
1028
|
const hasEmptyWire = declaredLevels.some((level) => level !== "off" && String(efforts?.[level] ?? "").trim().length === 0);
|
|
884
1029
|
const onlyOffDeclared = efforts !== void 0 && declaredLevels.length > 0 && !declaredLevels.some((level) => level !== "off");
|
|
885
|
-
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
fontSize: 12,
|
|
890
|
-
color: "var(--color-text-tertiary, #999)"
|
|
891
|
-
},
|
|
892
|
-
children: "—"
|
|
893
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
894
|
-
className: ModelsSection_ext_module_default["input"],
|
|
895
|
-
type: "text",
|
|
896
|
-
value: String(efforts?.[level] ?? ""),
|
|
897
|
-
"aria-label": `${t("reasoningEffortWire")} ${level}`,
|
|
898
|
-
placeholder: level,
|
|
899
|
-
disabled,
|
|
900
|
-
onChange: (event) => {
|
|
901
|
-
setWireValue(level, event.target.value);
|
|
902
|
-
}
|
|
903
|
-
});
|
|
1030
|
+
const inputList = inputsOf(model);
|
|
1031
|
+
const toggleModality = (modality, checked) => {
|
|
1032
|
+
const base = inputList.length === 0 && model["input"] === void 0 ? ["text"] : [...inputList];
|
|
1033
|
+
setInput(checked ? [.../* @__PURE__ */ new Set([...base, modality])] : base.filter((m) => m !== modality));
|
|
904
1034
|
};
|
|
905
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
906
|
-
|
|
907
|
-
border: "none",
|
|
908
|
-
padding: 0,
|
|
909
|
-
margin: 0,
|
|
910
|
-
display: "flex",
|
|
911
|
-
flexDirection: "column",
|
|
912
|
-
gap: 14
|
|
913
|
-
},
|
|
1035
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1036
|
+
className: models_plus_module_default["extBlock"],
|
|
914
1037
|
children: [
|
|
915
1038
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1039
|
+
className: `${models_plus_module_default["extGroup"]} ${models_plus_module_default["extGroupFirst"]}`,
|
|
1040
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickLoad, {
|
|
1041
|
+
onPick: applyMetadata,
|
|
1042
|
+
disabled
|
|
1043
|
+
})
|
|
1044
|
+
}),
|
|
1045
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1046
|
+
className: models_plus_module_default["extGroup"],
|
|
1047
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1048
|
+
className: models_plus_module_default["modelField"],
|
|
1049
|
+
style: { padding: "0 4px" },
|
|
1050
|
+
children: [capacityField("contextWindow", "上下文窗口"), capacityField("maxTokens", "最大输出 token")]
|
|
1051
|
+
})
|
|
1052
|
+
}),
|
|
1053
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1054
|
+
className: models_plus_module_default["extGroup"],
|
|
1055
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1056
|
+
className: models_plus_module_default["extLabel"],
|
|
1057
|
+
style: { marginBottom: 6 },
|
|
1058
|
+
children: "输入模态"
|
|
1059
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
921
1060
|
style: {
|
|
922
1061
|
display: "flex",
|
|
923
|
-
|
|
924
|
-
gap: 8,
|
|
925
|
-
cursor: "pointer"
|
|
1062
|
+
gap: 18
|
|
926
1063
|
},
|
|
1064
|
+
children: ["text", "image"].map((modality) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1065
|
+
className: models_plus_module_default["switchRow"],
|
|
1066
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1067
|
+
type: "checkbox",
|
|
1068
|
+
className: models_plus_module_default["extCheckbox"],
|
|
1069
|
+
checked: inputList.includes(modality),
|
|
1070
|
+
disabled,
|
|
1071
|
+
onChange: (event) => {
|
|
1072
|
+
toggleModality(modality, event.target.checked);
|
|
1073
|
+
}
|
|
1074
|
+
}), modality === "text" ? "文本" : "图像"]
|
|
1075
|
+
}, modality))
|
|
1076
|
+
})]
|
|
1077
|
+
}),
|
|
1078
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1079
|
+
className: models_plus_module_default["extGroup"],
|
|
1080
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1081
|
+
className: models_plus_module_default["extCheck"],
|
|
927
1082
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
928
1083
|
type: "checkbox",
|
|
929
|
-
|
|
930
|
-
|
|
1084
|
+
className: models_plus_module_default["extCheckbox"],
|
|
1085
|
+
style: { marginTop: 3 },
|
|
1086
|
+
checked: developerRole,
|
|
931
1087
|
disabled,
|
|
932
1088
|
onChange: (event) => {
|
|
933
1089
|
setCompatKey("supportsDeveloperRole", event.target.checked ? void 0 : false);
|
|
@@ -939,356 +1095,165 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
939
1095
|
gap: 2
|
|
940
1096
|
},
|
|
941
1097
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
942
|
-
className:
|
|
943
|
-
children:
|
|
1098
|
+
className: models_plus_module_default["extCheckLabel"],
|
|
1099
|
+
children: "允许以 developer 角色发送系统提示"
|
|
944
1100
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
color: "var(--color-text-tertiary, #999)"
|
|
948
|
-
},
|
|
949
|
-
children: t("supportsDeveloperRoleHint")
|
|
1101
|
+
className: models_plus_module_default["extHint"],
|
|
1102
|
+
children: "推理模型的系统提示将以 developer 角色发出;网关拒绝该角色时取消勾选,改用 system。"
|
|
950
1103
|
})]
|
|
951
1104
|
})]
|
|
952
1105
|
})
|
|
953
1106
|
}),
|
|
954
1107
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
955
|
-
|
|
956
|
-
borderTop: "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))",
|
|
957
|
-
paddingTop: 12
|
|
958
|
-
},
|
|
1108
|
+
className: models_plus_module_default["extGroup"],
|
|
959
1109
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
960
|
-
|
|
961
|
-
display: "inline-flex",
|
|
962
|
-
alignItems: "center",
|
|
963
|
-
gap: 8,
|
|
964
|
-
cursor: "pointer"
|
|
965
|
-
},
|
|
1110
|
+
className: models_plus_module_default["switchRow"],
|
|
966
1111
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
967
1112
|
type: "checkbox",
|
|
968
|
-
|
|
1113
|
+
className: models_plus_module_default["extCheckbox"],
|
|
969
1114
|
checked: supportsReasoningEffort,
|
|
970
1115
|
disabled,
|
|
971
1116
|
onChange: (event) => {
|
|
972
|
-
|
|
973
|
-
else {
|
|
974
|
-
const nextCompat = { ...compat };
|
|
975
|
-
delete nextCompat["supportsReasoningEffort"];
|
|
976
|
-
delete nextCompat["thinkingFormat"];
|
|
977
|
-
onChange({
|
|
978
|
-
compat: Object.keys(nextCompat).length === 0 ? void 0 : nextCompat,
|
|
979
|
-
reasoningEfforts: void 0
|
|
980
|
-
});
|
|
981
|
-
}
|
|
1117
|
+
toggleGate(event.target.checked);
|
|
982
1118
|
}
|
|
983
|
-
}),
|
|
984
|
-
className: ModelsSection_ext_module_default["modelFieldLabel"],
|
|
985
|
-
children: t("supportsReasoningEffort")
|
|
986
|
-
})]
|
|
1119
|
+
}), "端点接受推理挡位参数(reasoning_effort)"]
|
|
987
1120
|
})
|
|
988
1121
|
}),
|
|
989
|
-
!supportsReasoningEffort ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
990
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
991
|
-
className: ModelsSection_ext_module_default["modelFieldLabel"],
|
|
992
|
-
style: { marginBottom: 6 },
|
|
993
|
-
children: t("reasoningEfforts")
|
|
994
|
-
}),
|
|
995
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
996
|
-
style: {
|
|
997
|
-
border: "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))",
|
|
998
|
-
borderRadius: "var(--border-radius-md, 8px)",
|
|
999
|
-
overflow: "hidden"
|
|
1000
|
-
},
|
|
1001
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1002
|
-
style: {
|
|
1003
|
-
display: "grid",
|
|
1004
|
-
gridTemplateColumns: "56px 1fr 1fr",
|
|
1005
|
-
fontSize: 12,
|
|
1006
|
-
color: "var(--color-text-secondary, #666)",
|
|
1007
|
-
background: "var(--color-background-secondary, #f5f5f5)"
|
|
1008
|
-
},
|
|
1009
|
-
children: [
|
|
1010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1011
|
-
style: { padding: "5px 10px" },
|
|
1012
|
-
children: t("extDeclare")
|
|
1013
|
-
}),
|
|
1014
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1015
|
-
style: {
|
|
1016
|
-
padding: "5px 10px",
|
|
1017
|
-
borderLeft: "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))"
|
|
1018
|
-
},
|
|
1019
|
-
children: t("reasoningEffortLevel")
|
|
1020
|
-
}),
|
|
1021
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1022
|
-
style: {
|
|
1023
|
-
padding: "5px 10px",
|
|
1024
|
-
borderLeft: "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))"
|
|
1025
|
-
},
|
|
1026
|
-
children: t("reasoningEffortWire")
|
|
1027
|
-
})
|
|
1028
|
-
]
|
|
1029
|
-
}), THINKING_LEVELS.map((level, at) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1030
|
-
style: {
|
|
1031
|
-
display: "grid",
|
|
1032
|
-
gridTemplateColumns: "56px 1fr 1fr",
|
|
1033
|
-
alignItems: "center",
|
|
1034
|
-
borderTop: at === 0 ? "none" : "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))"
|
|
1035
|
-
},
|
|
1036
|
-
children: [
|
|
1037
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1038
|
-
style: {
|
|
1039
|
-
padding: "5px 10px",
|
|
1040
|
-
display: "flex",
|
|
1041
|
-
justifyContent: "center"
|
|
1042
|
-
},
|
|
1043
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1044
|
-
type: "checkbox",
|
|
1045
|
-
style: checkboxStyle,
|
|
1046
|
-
checked: level in (efforts ?? {}),
|
|
1047
|
-
disabled,
|
|
1048
|
-
onChange: (event) => {
|
|
1049
|
-
toggleLevel(level, event.target.checked);
|
|
1050
|
-
}
|
|
1051
|
-
})
|
|
1052
|
-
}),
|
|
1053
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1054
|
-
style: {
|
|
1055
|
-
padding: "5px 10px",
|
|
1056
|
-
fontSize: 13,
|
|
1057
|
-
borderLeft: "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))",
|
|
1058
|
-
opacity: level in (efforts ?? {}) ? 1 : .5
|
|
1059
|
-
},
|
|
1060
|
-
children: levelLabel(level)
|
|
1061
|
-
}),
|
|
1062
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1063
|
-
style: {
|
|
1064
|
-
padding: "5px 10px",
|
|
1065
|
-
borderLeft: "0.5px solid var(--color-border-tertiary, rgba(0,0,0,0.15))",
|
|
1066
|
-
opacity: level in (efforts ?? {}) ? 1 : .4
|
|
1067
|
-
},
|
|
1068
|
-
children: level in (efforts ?? {}) ? wireRow(level) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1069
|
-
style: {
|
|
1070
|
-
fontSize: 12,
|
|
1071
|
-
color: "var(--color-text-tertiary, #999)"
|
|
1072
|
-
},
|
|
1073
|
-
children: "—"
|
|
1074
|
-
})
|
|
1075
|
-
})
|
|
1076
|
-
]
|
|
1077
|
-
}, level))]
|
|
1078
|
-
}),
|
|
1079
|
-
hasEmptyWire || onlyOffDeclared ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1080
|
-
className: ModelsSection_ext_module_default["error"],
|
|
1081
|
-
style: { marginTop: 6 },
|
|
1082
|
-
children: hasEmptyWire ? t("reasoningEffortEmptyWire") : t("reasoningEffortsNeedNonOff")
|
|
1083
|
-
}) : null
|
|
1084
|
-
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1085
|
-
className: ModelsSection_ext_module_default["modelField"],
|
|
1086
|
-
style: {
|
|
1087
|
-
display: "flex",
|
|
1088
|
-
flexDirection: "column",
|
|
1089
|
-
gap: 4
|
|
1090
|
-
},
|
|
1091
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1092
|
-
className: ModelsSection_ext_module_default["modelFieldLabel"],
|
|
1093
|
-
children: t("thinkingFormat")
|
|
1094
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1095
|
-
className: ModelsSection_ext_module_default["input"],
|
|
1096
|
-
value: thinkingFormat ?? "",
|
|
1097
|
-
disabled,
|
|
1098
|
-
onChange: (event) => {
|
|
1099
|
-
const value = event.target.value;
|
|
1100
|
-
setCompatKey("thinkingFormat", value === "" ? void 0 : value);
|
|
1101
|
-
},
|
|
1102
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1103
|
-
value: "",
|
|
1104
|
-
children: "—"
|
|
1105
|
-
}), THINKING_FORMATS.map((format) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1106
|
-
value: format,
|
|
1107
|
-
children: format
|
|
1108
|
-
}, format))]
|
|
1109
|
-
})]
|
|
1110
|
-
})] }),
|
|
1111
1122
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
},
|
|
1126
|
-
children: MODALITIES.map((modality) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1127
|
-
style: {
|
|
1128
|
-
display: "inline-flex",
|
|
1129
|
-
alignItems: "center",
|
|
1130
|
-
gap: 6,
|
|
1131
|
-
cursor: "pointer",
|
|
1132
|
-
fontSize: 13
|
|
1123
|
+
className: models_plus_module_default["extGated"],
|
|
1124
|
+
hidden: !supportsReasoningEffort,
|
|
1125
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1126
|
+
className: models_plus_module_default["extField"],
|
|
1127
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1128
|
+
className: models_plus_module_default["extLabel"],
|
|
1129
|
+
children: "思考格式"
|
|
1130
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1131
|
+
className: `${models_plus_module_default["input"]} ${models_plus_module_default["selectInput"]}`,
|
|
1132
|
+
value: typeof compat["thinkingFormat"] === "string" ? compat["thinkingFormat"] : "",
|
|
1133
|
+
disabled,
|
|
1134
|
+
onChange: (event) => {
|
|
1135
|
+
setCompatKey("thinkingFormat", event.target.value === "" ? void 0 : event.target.value);
|
|
1133
1136
|
},
|
|
1134
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1137
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1138
|
+
value: "",
|
|
1139
|
+
children: "—"
|
|
1140
|
+
}), THINKING_FORMATS.map((format) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1141
|
+
value: format,
|
|
1142
|
+
children: format
|
|
1143
|
+
}, format))]
|
|
1144
|
+
})]
|
|
1145
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1146
|
+
className: models_plus_module_default["extField"],
|
|
1147
|
+
children: [
|
|
1148
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1149
|
+
className: models_plus_module_default["extLabel"],
|
|
1150
|
+
children: "推理挡位"
|
|
1151
|
+
}),
|
|
1152
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1153
|
+
className: models_plus_module_default["extTable"],
|
|
1154
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1155
|
+
className: models_plus_module_default["extTHead"],
|
|
1156
|
+
children: [
|
|
1157
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: "声明" }),
|
|
1158
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: "挡位" }),
|
|
1159
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: "线上取值" })
|
|
1160
|
+
]
|
|
1161
|
+
}), THINKING_LEVELS.map((level) => {
|
|
1162
|
+
const declared = efforts !== void 0 && level in efforts;
|
|
1163
|
+
const isOff = level === "off";
|
|
1164
|
+
const wire = declared ? String(efforts?.[level] ?? "") : "";
|
|
1165
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1166
|
+
className: models_plus_module_default["extTRow"],
|
|
1167
|
+
children: [
|
|
1168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1169
|
+
style: {
|
|
1170
|
+
display: "flex",
|
|
1171
|
+
justifyContent: "center"
|
|
1172
|
+
},
|
|
1173
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1174
|
+
type: "checkbox",
|
|
1175
|
+
className: models_plus_module_default["extCheckbox"],
|
|
1176
|
+
checked: declared,
|
|
1177
|
+
disabled,
|
|
1178
|
+
"aria-label": `声明 ${level}`,
|
|
1179
|
+
onChange: (event) => {
|
|
1180
|
+
toggleLevel(level, event.target.checked);
|
|
1181
|
+
}
|
|
1182
|
+
})
|
|
1183
|
+
}),
|
|
1184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1185
|
+
style: { opacity: declared ? 1 : .5 },
|
|
1186
|
+
children: level
|
|
1187
|
+
}),
|
|
1188
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1189
|
+
style: { opacity: declared ? 1 : .4 },
|
|
1190
|
+
children: declared && !isOff ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1191
|
+
type: "text",
|
|
1192
|
+
value: wire,
|
|
1193
|
+
placeholder: level,
|
|
1194
|
+
"aria-label": `${level} 线上取值`,
|
|
1195
|
+
disabled,
|
|
1196
|
+
onChange: (event) => {
|
|
1197
|
+
setWireValue(level, event.target.value);
|
|
1198
|
+
}
|
|
1199
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1200
|
+
style: {
|
|
1201
|
+
fontSize: 12,
|
|
1202
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
1203
|
+
},
|
|
1204
|
+
children: "—"
|
|
1205
|
+
})
|
|
1206
|
+
})
|
|
1207
|
+
]
|
|
1208
|
+
}, level);
|
|
1209
|
+
})]
|
|
1210
|
+
}),
|
|
1211
|
+
hasEmptyWire || onlyOffDeclared ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1212
|
+
className: models_plus_module_default["error"],
|
|
1213
|
+
style: { marginTop: 6 },
|
|
1214
|
+
children: hasEmptyWire ? "除 off 外勾选的挡位必须填写线上取值。" : "至少声明一个 off 以外的挡位。"
|
|
1215
|
+
}) : null
|
|
1216
|
+
]
|
|
1144
1217
|
})]
|
|
1145
1218
|
})
|
|
1146
1219
|
]
|
|
1147
1220
|
});
|
|
1148
1221
|
}
|
|
1149
1222
|
//#endregion
|
|
1150
|
-
//#region src/client/
|
|
1223
|
+
//#region src/client/models-plus/ModelCatalog.tsx
|
|
1151
1224
|
/**
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1156
|
-
* Only difference vs upstream: each row's expanded capacities area additionally
|
|
1157
|
-
* renders <ModelExtensionFields>, which edits the same draft object in place —
|
|
1158
|
-
* the extension fields ride the card's existing save path untouched.
|
|
1159
|
-
*
|
|
1160
|
-
* The list is the profile's `models` array as the card holds it: an empty list
|
|
1161
|
-
* means "serve this route's built-in catalog", and any entry replaces that
|
|
1162
|
-
* catalog, so a row is only ever added deliberately. Fetching asks the endpoint
|
|
1163
|
-
* **the form currently shows** — including a key typed but not yet saved — so
|
|
1164
|
-
* adding a provider is one pass instead of save-then-return; the reply is
|
|
1165
|
-
* candidates the user picks from, never configuration written behind them.
|
|
1166
|
-
*
|
|
1167
|
-
* A provider that cannot be interrogated (an unreachable endpoint, a protocol
|
|
1168
|
-
* with no readable listing) is not a dead end: the failure is shown next to the
|
|
1169
|
-
* rows the user can still fill in by hand.
|
|
1225
|
+
* One provider profile's model catalog: rows joined from the draft's `models`
|
|
1226
|
+
* array, the fetch action (interrogating the endpoint the form currently
|
|
1227
|
+
* shows), and the candidate-picker modal. Each row's expanded panel is the
|
|
1228
|
+
* plugin's ModelEntryPanel — capacities and extension fields in one block.
|
|
1170
1229
|
*/
|
|
1171
1230
|
/** A row's text field, or the empty string when unset or not a string. */
|
|
1172
1231
|
function textOf(model, key) {
|
|
1173
1232
|
const value = model[key];
|
|
1174
1233
|
return typeof value === "string" ? value : "";
|
|
1175
1234
|
}
|
|
1176
|
-
/**
|
|
1177
|
-
function numberOf(model, key) {
|
|
1178
|
-
const value = model[key];
|
|
1179
|
-
return typeof value === "number" ? value : void 0;
|
|
1180
|
-
}
|
|
1181
|
-
/** Disclosure chevron; rotates to point down while its row is open. */
|
|
1182
|
-
function IconChevron({ open }) {
|
|
1183
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1184
|
-
width: "14",
|
|
1185
|
-
height: "14",
|
|
1186
|
-
viewBox: "0 0 16 16",
|
|
1187
|
-
fill: "none",
|
|
1188
|
-
"aria-hidden": true,
|
|
1189
|
-
style: {
|
|
1190
|
-
transform: open ? "rotate(90deg)" : void 0,
|
|
1191
|
-
transition: "transform 120ms ease"
|
|
1192
|
-
},
|
|
1193
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1194
|
-
d: "M6 3.5L10.5 8L6 12.5",
|
|
1195
|
-
stroke: "currentColor",
|
|
1196
|
-
strokeWidth: "1.5",
|
|
1197
|
-
strokeLinecap: "round",
|
|
1198
|
-
strokeLinejoin: "round"
|
|
1199
|
-
})
|
|
1200
|
-
});
|
|
1201
|
-
}
|
|
1202
|
-
/** Removal glyph for one model row. */
|
|
1203
|
-
function IconTrash() {
|
|
1204
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1205
|
-
width: "14",
|
|
1206
|
-
height: "14",
|
|
1207
|
-
viewBox: "0 0 16 16",
|
|
1208
|
-
fill: "none",
|
|
1209
|
-
"aria-hidden": true,
|
|
1210
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1211
|
-
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",
|
|
1212
|
-
stroke: "currentColor",
|
|
1213
|
-
strokeWidth: "1.3",
|
|
1214
|
-
strokeLinecap: "round",
|
|
1215
|
-
strokeLinejoin: "round"
|
|
1216
|
-
})
|
|
1217
|
-
});
|
|
1218
|
-
}
|
|
1219
|
-
/**
|
|
1220
|
-
* What an empty capacity field is worth, shown as its placeholder so a row left
|
|
1221
|
-
* blank does not read as a model with no capacity at all.
|
|
1222
|
-
*
|
|
1223
|
-
* The magnitudes are the adapter's own route-level fallbacks (`llm-pi-ai`'s
|
|
1224
|
-
* `defaultContextWindow` and `defaultMaxTokens`), spelled the way a person
|
|
1225
|
-
* would say them. They are a hint, not a mirror: this page counts `K` as 1000,
|
|
1226
|
-
* so typing `256K` stores 256000 while leaving the field blank keeps the
|
|
1227
|
-
* adapter's 262144. A deployment that overrides those defaults is not
|
|
1228
|
-
* reflected here — nothing on this page can read them.
|
|
1229
|
-
*/
|
|
1230
|
-
const CAPACITY_HINT = {
|
|
1231
|
-
contextWindow: "256K",
|
|
1232
|
-
maxTokens: "32K"
|
|
1233
|
-
};
|
|
1234
|
-
/**
|
|
1235
|
-
* Spell a stored count for a field that may be unset. The spelling itself is
|
|
1236
|
-
* {@link formatCapacity}, shared with the DeepSeek catalog editor so both
|
|
1237
|
-
* surfaces read and write one K/M vocabulary.
|
|
1238
|
-
* @param value - stored capacity, or `undefined` for an unset field.
|
|
1239
|
-
* @returns the field text, empty when unset.
|
|
1240
|
-
*/
|
|
1241
|
-
function capacitySpelling(value) {
|
|
1242
|
-
return value === void 0 ? "" : formatCapacity(value);
|
|
1243
|
-
}
|
|
1244
|
-
/** Adopt a candidate, keeping whatever capacities the provider disclosed. */
|
|
1235
|
+
/** Adopt one candidate, v1.0.0 defaults + the endpoint's disclosed capacities. */
|
|
1245
1236
|
function adopt(candidate) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1237
|
+
const row = newModelDraft();
|
|
1238
|
+
row["id"] = candidate.id;
|
|
1239
|
+
row["name"] = candidate.name ?? candidate.id;
|
|
1240
|
+
if (candidate.contextWindow !== void 0) row["contextWindow"] = candidate.contextWindow;
|
|
1241
|
+
if (candidate.maxTokens !== void 0) row["maxTokens"] = candidate.maxTokens;
|
|
1242
|
+
return row;
|
|
1252
1243
|
}
|
|
1253
1244
|
/**
|
|
1254
|
-
* Render the model
|
|
1255
|
-
* @param props - the drafted rows, probe target, wire face, and
|
|
1256
|
-
* @returns the
|
|
1245
|
+
* Render the model catalog with its fetch action.
|
|
1246
|
+
* @param props - the drafted rows, probe target, wire face, and mutators.
|
|
1247
|
+
* @returns the catalog editor.
|
|
1257
1248
|
*/
|
|
1258
|
-
function
|
|
1259
|
-
const { models, onChange, probe, operations,
|
|
1249
|
+
function ModelCatalog(props) {
|
|
1250
|
+
const { models, onChange, probe, operations, disabled } = props;
|
|
1260
1251
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
1261
1252
|
const [failure, setFailure] = (0, react.useState)(void 0);
|
|
1262
1253
|
const [candidates, setCandidates] = (0, react.useState)(void 0);
|
|
1263
|
-
const [picked, setPicked] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
1264
|
-
const [
|
|
1265
|
-
const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
1266
|
-
const [editing, setEditing] = (0, react.useState)(/* @__PURE__ */ new Map());
|
|
1267
|
-
/** Buffer key for one capacity field; the row half moves when rows do. */
|
|
1268
|
-
const bufferKey = (index, field) => `${String(index)}:${field}`;
|
|
1269
|
-
const editCapacity = (index, field, text) => {
|
|
1270
|
-
setEditing((current) => new Map(current).set(bufferKey(index, field), text));
|
|
1271
|
-
patch(index, { [field]: parseCapacity(text) });
|
|
1272
|
-
};
|
|
1273
|
-
/** What a capacity field shows: the buffer while typing, else the stored count. */
|
|
1274
|
-
const capacityText = (model, index, field) => editing.get(bufferKey(index, field)) ?? capacitySpelling(numberOf(model, field));
|
|
1275
|
-
/** Drop one row's entries and shift the rows after it down, in one pass. */
|
|
1276
|
-
const reindexOnRemove = (current, index) => {
|
|
1277
|
-
const next = /* @__PURE__ */ new Map();
|
|
1278
|
-
for (const [key, value] of current) {
|
|
1279
|
-
const at = Number(key.slice(0, key.indexOf(":")));
|
|
1280
|
-
if (at === index) continue;
|
|
1281
|
-
next.set(at > index ? key.replace(/^\d+/, String(at - 1)) : key, value);
|
|
1282
|
-
}
|
|
1283
|
-
return next;
|
|
1284
|
-
};
|
|
1285
|
-
const toggleExpanded = (index) => {
|
|
1286
|
-
setExpanded((current) => {
|
|
1287
|
-
const next = new Set(current);
|
|
1288
|
-
if (!next.delete(index)) next.add(index);
|
|
1289
|
-
return next;
|
|
1290
|
-
});
|
|
1291
|
-
};
|
|
1254
|
+
const [picked, setPicked] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
1255
|
+
const [query, setQuery] = (0, react.useState)("");
|
|
1256
|
+
const [expanded, setExpanded] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
1292
1257
|
const patch = (index, next) => {
|
|
1293
1258
|
onChange(models.map((model, at) => {
|
|
1294
1259
|
if (at !== index) return model;
|
|
@@ -1299,6 +1264,22 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1299
1264
|
}).filter(([key]) => !cleared.has(key)));
|
|
1300
1265
|
}));
|
|
1301
1266
|
};
|
|
1267
|
+
const toggleExpanded = (index) => {
|
|
1268
|
+
setExpanded((current) => {
|
|
1269
|
+
const next = new Set(current);
|
|
1270
|
+
if (!next.delete(index)) next.add(index);
|
|
1271
|
+
return next;
|
|
1272
|
+
});
|
|
1273
|
+
};
|
|
1274
|
+
const removeRow = (index) => {
|
|
1275
|
+
onChange(models.filter((_model, at) => at !== index));
|
|
1276
|
+
setExpanded((current) => {
|
|
1277
|
+
const next = /* @__PURE__ */ new Set();
|
|
1278
|
+
for (const at of current) if (at < index) next.add(at);
|
|
1279
|
+
else if (at > index) next.add(at - 1);
|
|
1280
|
+
return next;
|
|
1281
|
+
});
|
|
1282
|
+
};
|
|
1302
1283
|
const fetchModels = async () => {
|
|
1303
1284
|
setBusy(true);
|
|
1304
1285
|
setFailure(void 0);
|
|
@@ -1315,11 +1296,11 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1315
1296
|
}
|
|
1316
1297
|
const found = answer.models;
|
|
1317
1298
|
if (found.length === 0) {
|
|
1318
|
-
setFailure(
|
|
1299
|
+
setFailure("该提供方没有列出任何模型,请手动添加。");
|
|
1319
1300
|
return;
|
|
1320
1301
|
}
|
|
1321
1302
|
const known = new Set(models.map((model) => textOf(model, "id")));
|
|
1322
|
-
|
|
1303
|
+
setQuery("");
|
|
1323
1304
|
setCandidates(found);
|
|
1324
1305
|
setPicked(new Set(found.filter((model) => !known.has(model.id)).map((model) => model.id)));
|
|
1325
1306
|
} finally {
|
|
@@ -1329,10 +1310,9 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1329
1310
|
const closePicker = () => {
|
|
1330
1311
|
setCandidates(void 0);
|
|
1331
1312
|
setPicked(/* @__PURE__ */ new Set());
|
|
1332
|
-
|
|
1313
|
+
setQuery("");
|
|
1333
1314
|
};
|
|
1334
1315
|
const adoptPicked = () => {
|
|
1335
|
-
/* v8 ignore next -- the dialog only renders with candidates loaded */
|
|
1336
1316
|
if (candidates === void 0) return;
|
|
1337
1317
|
const byId = new Map(models.map((model) => [textOf(model, "id"), model]));
|
|
1338
1318
|
for (const candidate of candidates) {
|
|
@@ -1350,220 +1330,219 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1350
1330
|
});
|
|
1351
1331
|
};
|
|
1352
1332
|
const activeCandidates = candidates ?? [];
|
|
1353
|
-
const
|
|
1354
|
-
const
|
|
1355
|
-
const
|
|
1356
|
-
const toggleVisibleCandidates = () => {
|
|
1357
|
-
setPicked((current) => {
|
|
1358
|
-
const next = new Set(current);
|
|
1359
|
-
if (visibleCandidates.every((candidate) => current.has(candidate.id))) for (const candidate of visibleCandidates) next.delete(candidate.id);
|
|
1360
|
-
else for (const candidate of visibleCandidates) next.add(candidate.id);
|
|
1361
|
-
return next;
|
|
1362
|
-
});
|
|
1363
|
-
};
|
|
1333
|
+
const needle = query.trim().toLowerCase();
|
|
1334
|
+
const visible = needle.length === 0 ? activeCandidates : activeCandidates.filter((candidate) => candidate.id.toLowerCase().includes(needle));
|
|
1335
|
+
const allPicked = visible.length > 0 && visible.every((candidate) => picked.has(candidate.id));
|
|
1364
1336
|
const askable = probe.provider !== void 0 || probe.baseURL !== void 0 && probe.baseURL.length > 0;
|
|
1365
1337
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1366
|
-
className:
|
|
1367
|
-
"aria-label":
|
|
1338
|
+
className: models_plus_module_default["modelCatalog"],
|
|
1339
|
+
"aria-label": "模型目录",
|
|
1368
1340
|
children: [
|
|
1369
1341
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1370
|
-
className:
|
|
1371
|
-
children: [
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1342
|
+
className: models_plus_module_default["modelListHead"],
|
|
1343
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1344
|
+
className: models_plus_module_default["modelCatalogHeading"],
|
|
1345
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1346
|
+
className: models_plus_module_default["modelCatalogTitle"],
|
|
1347
|
+
children: "模型目录"
|
|
1348
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1349
|
+
className: models_plus_module_default["modelCatalogMeta"],
|
|
1350
|
+
children: props.overridden ? "已自定义模型目录" : "正在使用适配器默认模型"
|
|
1351
|
+
})]
|
|
1352
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1353
|
+
style: {
|
|
1354
|
+
display: "flex",
|
|
1355
|
+
alignItems: "center",
|
|
1356
|
+
gap: 4
|
|
1357
|
+
},
|
|
1358
|
+
children: [props.overridden && props.onReset !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1383
1359
|
type: "button",
|
|
1384
|
-
className:
|
|
1360
|
+
className: models_plus_module_default["linkButton"],
|
|
1385
1361
|
disabled,
|
|
1386
1362
|
onClick: props.onReset,
|
|
1387
|
-
children:
|
|
1388
|
-
}) : null,
|
|
1389
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1363
|
+
children: "恢复默认模型"
|
|
1364
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1390
1365
|
type: "button",
|
|
1391
|
-
className:
|
|
1366
|
+
className: models_plus_module_default["linkButton"],
|
|
1392
1367
|
disabled: disabled || busy || !askable || props.probeBlocked !== void 0,
|
|
1393
|
-
title: props.probeBlocked !== void 0 ?
|
|
1368
|
+
title: props.probeBlocked !== void 0 ? props.probeBlocked : askable ? void 0 : "请先填写 API 地址,再获取。",
|
|
1394
1369
|
onClick: () => {
|
|
1395
1370
|
fetchModels();
|
|
1396
1371
|
},
|
|
1397
|
-
children: busy ?
|
|
1398
|
-
})
|
|
1399
|
-
]
|
|
1372
|
+
children: busy ? "正在询问提供方…" : "获取可用模型"
|
|
1373
|
+
})]
|
|
1374
|
+
})]
|
|
1400
1375
|
}),
|
|
1401
1376
|
models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1402
|
-
className:
|
|
1403
|
-
children:
|
|
1404
|
-
}) :
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
className: ModelsSection_ext_module_default["input"],
|
|
1488
|
-
type: "text",
|
|
1489
|
-
inputMode: "numeric",
|
|
1490
|
-
value: capacityText(model, index, "maxTokens"),
|
|
1491
|
-
placeholder: CAPACITY_HINT.maxTokens,
|
|
1492
|
-
"aria-label": `${t("modelMaxTokens")} ${index + 1}`,
|
|
1493
|
-
disabled,
|
|
1494
|
-
onChange: (event) => {
|
|
1495
|
-
editCapacity(index, "maxTokens", event.target.value);
|
|
1496
|
-
}
|
|
1497
|
-
})]
|
|
1498
|
-
})]
|
|
1499
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1500
|
-
className: ModelsSection_ext_module_default["modelExtensionFields"],
|
|
1501
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelExtensionFields, {
|
|
1377
|
+
className: models_plus_module_default["modelEmpty"],
|
|
1378
|
+
children: "模型选择器中将不显示任何模型;目录外 ID 仍可直接发送。"
|
|
1379
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1380
|
+
className: models_plus_module_default["modelList"],
|
|
1381
|
+
children: models.map((model, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1382
|
+
className: models_plus_module_default["modelEntry"],
|
|
1383
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1384
|
+
className: models_plus_module_default["modelRow"],
|
|
1385
|
+
children: [
|
|
1386
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1387
|
+
className: models_plus_module_default["input"],
|
|
1388
|
+
type: "text",
|
|
1389
|
+
value: textOf(model, "id"),
|
|
1390
|
+
placeholder: "模型 ID",
|
|
1391
|
+
"aria-label": `模型 ID ${String(index + 1)}`,
|
|
1392
|
+
disabled,
|
|
1393
|
+
onChange: (event) => {
|
|
1394
|
+
patch(index, { id: event.target.value });
|
|
1395
|
+
}
|
|
1396
|
+
}),
|
|
1397
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1398
|
+
className: models_plus_module_default["input"],
|
|
1399
|
+
type: "text",
|
|
1400
|
+
value: textOf(model, "name"),
|
|
1401
|
+
placeholder: "显示名称",
|
|
1402
|
+
"aria-label": `显示名称 ${String(index + 1)}`,
|
|
1403
|
+
disabled,
|
|
1404
|
+
onChange: (event) => {
|
|
1405
|
+
patch(index, { name: event.target.value === "" ? void 0 : event.target.value });
|
|
1406
|
+
}
|
|
1407
|
+
}),
|
|
1408
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1409
|
+
type: "button",
|
|
1410
|
+
className: models_plus_module_default["iconButton"],
|
|
1411
|
+
"aria-label": `容量 ${String(index + 1)}`,
|
|
1412
|
+
"aria-expanded": expanded.has(index),
|
|
1413
|
+
title: "容量",
|
|
1414
|
+
onClick: () => {
|
|
1415
|
+
toggleExpanded(index);
|
|
1416
|
+
},
|
|
1417
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1418
|
+
width: "14",
|
|
1419
|
+
height: "14",
|
|
1420
|
+
viewBox: "0 0 16 16",
|
|
1421
|
+
fill: "none",
|
|
1422
|
+
"aria-hidden": "true",
|
|
1423
|
+
style: {
|
|
1424
|
+
transform: expanded.has(index) ? "rotate(90deg)" : void 0,
|
|
1425
|
+
transition: "transform 120ms ease"
|
|
1426
|
+
},
|
|
1427
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1428
|
+
d: "M6 3.5L10.5 8L6 12.5",
|
|
1429
|
+
stroke: "currentColor",
|
|
1430
|
+
strokeWidth: "1.5",
|
|
1431
|
+
strokeLinecap: "round",
|
|
1432
|
+
strokeLinejoin: "round"
|
|
1433
|
+
})
|
|
1434
|
+
})
|
|
1435
|
+
}),
|
|
1436
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1437
|
+
type: "button",
|
|
1438
|
+
className: `${models_plus_module_default["iconButton"]} ${models_plus_module_default["iconButtonDanger"]}`,
|
|
1439
|
+
"aria-label": `删除模型 ${String(index + 1)}`,
|
|
1440
|
+
title: "删除模型",
|
|
1441
|
+
disabled,
|
|
1442
|
+
onClick: () => {
|
|
1443
|
+
removeRow(index);
|
|
1444
|
+
},
|
|
1445
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1446
|
+
width: "14",
|
|
1447
|
+
height: "14",
|
|
1448
|
+
viewBox: "0 0 16 16",
|
|
1449
|
+
fill: "none",
|
|
1450
|
+
"aria-hidden": "true",
|
|
1451
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1452
|
+
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",
|
|
1453
|
+
stroke: "currentColor",
|
|
1454
|
+
strokeWidth: "1.3",
|
|
1455
|
+
strokeLinecap: "round",
|
|
1456
|
+
strokeLinejoin: "round"
|
|
1457
|
+
})
|
|
1458
|
+
})
|
|
1459
|
+
})
|
|
1460
|
+
]
|
|
1461
|
+
}), expanded.has(index) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelEntryPanel, {
|
|
1502
1462
|
model,
|
|
1463
|
+
disabled,
|
|
1503
1464
|
onChange: (next) => {
|
|
1504
|
-
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1465
|
+
onChange(models.map((row, at) => at === index ? next : row));
|
|
1466
|
+
}
|
|
1467
|
+
}) : null]
|
|
1468
|
+
}, `${String(index)}:${textOf(model, "id")}`))
|
|
1469
|
+
}),
|
|
1470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1511
1471
|
type: "button",
|
|
1512
|
-
className:
|
|
1472
|
+
className: models_plus_module_default["addModelButton"],
|
|
1513
1473
|
disabled,
|
|
1514
1474
|
onClick: () => {
|
|
1515
|
-
onChange([...models,
|
|
1475
|
+
onChange([...models, newModelDraft()]);
|
|
1516
1476
|
},
|
|
1517
|
-
children:
|
|
1477
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1478
|
+
width: "14",
|
|
1479
|
+
height: "14",
|
|
1480
|
+
viewBox: "0 0 16 16",
|
|
1481
|
+
fill: "none",
|
|
1482
|
+
"aria-hidden": "true",
|
|
1483
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1484
|
+
d: "M8 3.5v9M3.5 8h9",
|
|
1485
|
+
stroke: "currentColor",
|
|
1486
|
+
strokeWidth: "1.5",
|
|
1487
|
+
strokeLinecap: "round"
|
|
1488
|
+
})
|
|
1489
|
+
}), "添加模型"]
|
|
1518
1490
|
}),
|
|
1519
1491
|
failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1520
|
-
className:
|
|
1492
|
+
className: models_plus_module_default["error"],
|
|
1521
1493
|
children: failure
|
|
1522
1494
|
}) : null,
|
|
1523
1495
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1524
1496
|
open: candidates !== void 0,
|
|
1525
1497
|
onClose: closePicker,
|
|
1526
|
-
title:
|
|
1527
|
-
closeLabel:
|
|
1528
|
-
description:
|
|
1529
|
-
className:
|
|
1530
|
-
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1531
|
-
|
|
1498
|
+
title: "选择要添加的模型",
|
|
1499
|
+
closeLabel: "关闭",
|
|
1500
|
+
description: "以下是模型提供方的可用模型,勾选要添加的模型。",
|
|
1501
|
+
className: models_plus_module_default["fetchDialog"],
|
|
1502
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1503
|
+
type: "button",
|
|
1504
|
+
className: models_plus_module_default["secondaryButton"],
|
|
1532
1505
|
onClick: closePicker,
|
|
1533
|
-
children:
|
|
1534
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1535
|
-
|
|
1506
|
+
children: "取消"
|
|
1507
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1508
|
+
type: "button",
|
|
1509
|
+
className: models_plus_module_default["primaryButton"],
|
|
1536
1510
|
onClick: adoptPicked,
|
|
1537
|
-
children:
|
|
1511
|
+
children: "添加所选"
|
|
1538
1512
|
})] }),
|
|
1539
1513
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1540
|
-
className:
|
|
1514
|
+
className: models_plus_module_default["candidateToolbar"],
|
|
1541
1515
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1542
|
-
className: `${
|
|
1516
|
+
className: `${models_plus_module_default["input"]} ${models_plus_module_default["candidateSearch"]}`,
|
|
1543
1517
|
type: "search",
|
|
1544
|
-
value:
|
|
1545
|
-
placeholder:
|
|
1546
|
-
"aria-label":
|
|
1518
|
+
value: query,
|
|
1519
|
+
placeholder: "搜索模型",
|
|
1520
|
+
"aria-label": "搜索模型",
|
|
1547
1521
|
onChange: (event) => {
|
|
1548
|
-
|
|
1522
|
+
setQuery(event.target.value);
|
|
1549
1523
|
}
|
|
1550
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
disabled:
|
|
1554
|
-
onClick:
|
|
1555
|
-
|
|
1524
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1525
|
+
type: "button",
|
|
1526
|
+
className: models_plus_module_default["linkButton"],
|
|
1527
|
+
disabled: visible.length === 0,
|
|
1528
|
+
onClick: () => {
|
|
1529
|
+
setPicked((current) => {
|
|
1530
|
+
if (visible.every((candidate) => current.has(candidate.id))) return new Set([...current].filter((id) => !visible.some((candidate) => candidate.id === id)));
|
|
1531
|
+
return /* @__PURE__ */ new Set([...current, ...visible.map((candidate) => candidate.id)]);
|
|
1532
|
+
});
|
|
1533
|
+
},
|
|
1534
|
+
children: allPicked ? "取消全选" : "全选"
|
|
1556
1535
|
})]
|
|
1557
|
-
}),
|
|
1558
|
-
className:
|
|
1536
|
+
}), visible.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1537
|
+
className: models_plus_module_default["candidateEmpty"],
|
|
1559
1538
|
role: "status",
|
|
1560
|
-
children:
|
|
1539
|
+
children: "没有匹配的模型。"
|
|
1561
1540
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1562
|
-
className:
|
|
1563
|
-
children:
|
|
1564
|
-
className:
|
|
1541
|
+
className: models_plus_module_default["candidateList"],
|
|
1542
|
+
children: visible.map((candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
1543
|
+
className: models_plus_module_default["candidate"],
|
|
1565
1544
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1566
|
-
className:
|
|
1545
|
+
className: models_plus_module_default["candidateLabel"],
|
|
1567
1546
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1568
1547
|
type: "checkbox",
|
|
1569
1548
|
checked: picked.has(candidate.id),
|
|
@@ -1571,7 +1550,7 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1571
1550
|
toggle(candidate.id);
|
|
1572
1551
|
}
|
|
1573
1552
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1574
|
-
className:
|
|
1553
|
+
className: models_plus_module_default["candidateId"],
|
|
1575
1554
|
children: candidate.id
|
|
1576
1555
|
})]
|
|
1577
1556
|
})
|
|
@@ -1582,300 +1561,152 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1582
1561
|
});
|
|
1583
1562
|
}
|
|
1584
1563
|
//#endregion
|
|
1585
|
-
//#region
|
|
1586
|
-
/**
|
|
1587
|
-
* Any route key walks a dict schema to the same profile node, so the lookup
|
|
1588
|
-
* names one that cannot collide with a configured route.
|
|
1589
|
-
*/
|
|
1590
|
-
const PROBE_ROUTE = "\0probe";
|
|
1564
|
+
//#region src/client/models-plus/ProviderEditor.tsx
|
|
1591
1565
|
/**
|
|
1592
|
-
*
|
|
1593
|
-
*
|
|
1594
|
-
*
|
|
1595
|
-
*
|
|
1566
|
+
* One existing provider's editor card, fully plugin-owned (v1.0.0 rewrite).
|
|
1567
|
+
* Primary field: a single write-only API key input; the collapsed
|
|
1568
|
+
* 「供应商」 section carries baseURL (plus displayName/protocol for a
|
|
1569
|
+
* hand-declared route), the 「模型」 section the model catalog with the
|
|
1570
|
+
* extension panel. Save semantics mirror the host card: minimal path ops
|
|
1571
|
+
* against the stored section with a revision fence, then the credential
|
|
1572
|
+
* write — the draft comes from the user layer, so a wholesale replace can
|
|
1573
|
+
* never delete fields outside the card.
|
|
1596
1574
|
*/
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
settingsPath: [...entry.settingsPath],
|
|
1605
|
-
active: active.has(entry.provider),
|
|
1606
|
-
...entry.declared === void 0 ? {} : { declared: entry.declared }
|
|
1607
|
-
}));
|
|
1608
|
-
for (const provider of registered) {
|
|
1609
|
-
if (declared.has(provider.id)) continue;
|
|
1610
|
-
rows.push({
|
|
1611
|
-
provider: provider.id,
|
|
1612
|
-
displayName: provider.name,
|
|
1613
|
-
settingsNs: "",
|
|
1614
|
-
settingsPath: [],
|
|
1615
|
-
active: true
|
|
1616
|
-
});
|
|
1617
|
-
}
|
|
1618
|
-
return rows;
|
|
1575
|
+
/** The public DeepSeek endpoint shown as the deepseek base-URL placeholder. */
|
|
1576
|
+
const DEEPSEEK_PUBLIC_BASE_URL = "https://api.deepseek.com";
|
|
1577
|
+
/** The editor layout the owning namespace selects. */
|
|
1578
|
+
function layoutOf(ns) {
|
|
1579
|
+
if (ns === "llm-deepseek") return "deepseek";
|
|
1580
|
+
if (ns === "llm-pi-ai") return "pi-ai";
|
|
1581
|
+
return "unknown";
|
|
1619
1582
|
}
|
|
1620
|
-
/**
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
*/
|
|
1627
|
-
function
|
|
1628
|
-
|
|
1583
|
+
/** A user-section subtree as a plain draft object (absent → empty). */
|
|
1584
|
+
function draftAt(schema, namespace, path) {
|
|
1585
|
+
const subtree = schema.getPath(namespace.user, path);
|
|
1586
|
+
if (typeof subtree !== "object" || subtree === null || Array.isArray(subtree)) return {};
|
|
1587
|
+
return structuredClone(subtree);
|
|
1588
|
+
}
|
|
1589
|
+
/** The credential reference this profile resolves keys through. */
|
|
1590
|
+
function refFor(schema, namespace, path, provider) {
|
|
1591
|
+
const profile = schema.getPath(namespace.value, path);
|
|
1592
|
+
const named = typeof profile === "object" && profile !== null ? profile.apiKeyEnv : void 0;
|
|
1593
|
+
return typeof named === "string" && named.length > 0 ? named : deriveKeyRef(provider);
|
|
1629
1594
|
}
|
|
1630
1595
|
/**
|
|
1631
|
-
*
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1634
|
-
* both come from the same `Config`.
|
|
1635
|
-
* @param namespace - the namespace view whose schema declares the profile shape.
|
|
1636
|
-
* @param schema - settings schema operations.
|
|
1637
|
-
* @returns the protocol identifiers, or an empty list when the schema has none.
|
|
1596
|
+
* Render one existing provider's editing card.
|
|
1597
|
+
* @param props - the addressed profile plus wire faces.
|
|
1598
|
+
* @returns the editor card.
|
|
1638
1599
|
*/
|
|
1639
|
-
function
|
|
1640
|
-
|
|
1641
|
-
const
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1600
|
+
function ProviderEditor(props) {
|
|
1601
|
+
const { namespace, schema, settingsPath, operations } = props;
|
|
1602
|
+
const [draft, setDraft] = (0, react.useState)(() => draftAt(schema, namespace, settingsPath));
|
|
1603
|
+
const [tab, setTab] = (0, react.useState)("provider");
|
|
1604
|
+
const [keyDraft, setKeyDraft] = (0, react.useState)("");
|
|
1605
|
+
const [keyState, setKeyState] = (0, react.useState)(void 0);
|
|
1606
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
1607
|
+
const [failure, setFailure] = (0, react.useState)(void 0);
|
|
1608
|
+
const [committedOriginal, setCommittedOriginal] = (0, react.useState)(() => schema.getPath(namespace.user, settingsPath));
|
|
1609
|
+
const [expectedRevision, setExpectedRevision] = (0, react.useState)(() => namespace.revision);
|
|
1610
|
+
const root = (0, react.useMemo)(() => schema.rehydrate(namespace.schema), [namespace.schema, schema]);
|
|
1611
|
+
const node = (0, react.useMemo)(() => schema.nodeAtPath(root, settingsPath), [
|
|
1612
|
+
root,
|
|
1613
|
+
schema,
|
|
1614
|
+
settingsPath
|
|
1645
1615
|
]);
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
describeFace;
|
|
1662
|
-
/** The snapshot the section renders from (uSES-safe store). */
|
|
1663
|
-
store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
|
|
1664
|
-
status: "idle",
|
|
1665
|
-
error: null,
|
|
1666
|
-
credentialError: null,
|
|
1667
|
-
writable: false,
|
|
1668
|
-
rows: [],
|
|
1669
|
-
namespaces: /* @__PURE__ */ new Map()
|
|
1670
|
-
});
|
|
1671
|
-
/** Latest load wins; an older response never overwrites a newer one. */
|
|
1672
|
-
generation = 0;
|
|
1673
|
-
/**
|
|
1674
|
-
* @param ctx - the page plugin's context, whose `remote.llm` and
|
|
1675
|
-
* `remote.credentials` namespaces carry the directory and credential reads.
|
|
1676
|
-
* @param schema - settings-owned schema and immutable path operations.
|
|
1677
|
-
* @param describeFace - the shared mirror's describe face (namespace views and writability).
|
|
1678
|
-
*/
|
|
1679
|
-
constructor(ctx, schema, describeFace) {
|
|
1680
|
-
this.ctx = ctx;
|
|
1681
|
-
this.schema = schema;
|
|
1682
|
-
this.describeFace = describeFace;
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
|
-
* Refresh the whole page snapshot: the provider directory and the mirror's
|
|
1686
|
-
* settings answer in parallel, then one batched credential describe over
|
|
1687
|
-
* every referenced ref. Provider failure or absence of an initial settings
|
|
1688
|
-
* answer keeps the last good rows and surfaces an error; a failed settings
|
|
1689
|
-
* refresh reuses the mirror's held view.
|
|
1690
|
-
* @returns nothing; the snapshot carries the outcome.
|
|
1691
|
-
*/
|
|
1692
|
-
async load() {
|
|
1693
|
-
const generation = ++this.generation;
|
|
1694
|
-
this.store.update((s) => {
|
|
1695
|
-
s.status = "loading";
|
|
1696
|
-
s.error = null;
|
|
1616
|
+
const fallback = schema.getPath(namespace.value, settingsPath);
|
|
1617
|
+
const disabled = props.readOnly || busy;
|
|
1618
|
+
const layout = layoutOf(namespace.ns);
|
|
1619
|
+
const keyRef = refFor(schema, namespace, settingsPath, props.provider);
|
|
1620
|
+
const protocols = (0, react.useMemo)(() => layout === "pi-ai" ? protocolChoices(namespace, schema) : [], [
|
|
1621
|
+
layout,
|
|
1622
|
+
namespace,
|
|
1623
|
+
schema
|
|
1624
|
+
]);
|
|
1625
|
+
(0, react.useEffect)(() => {
|
|
1626
|
+
let stale = false;
|
|
1627
|
+
setKeyState(void 0);
|
|
1628
|
+
operations.describeCredential(keyRef).then((described) => {
|
|
1629
|
+
if (stale) return;
|
|
1630
|
+
setKeyState(described);
|
|
1697
1631
|
});
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
if (
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
const
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const rows = providers.map((entry) => {
|
|
1721
|
-
const namespace = namespaces.get(entry.settingsNs);
|
|
1722
|
-
return {
|
|
1723
|
-
entry,
|
|
1724
|
-
configured: namespace !== void 0 && (entry.settingsPath.length === 0 || this.schema.getPath(namespace.value, entry.settingsPath) !== void 0),
|
|
1725
|
-
removable: namespace !== void 0 && entry.settingsPath.length > 0 && this.schema.hasPath(namespace.user, entry.settingsPath) && !this.schema.hasPath(namespace.base, entry.settingsPath),
|
|
1726
|
-
apiKeyEnv: apiKeyEnvOf(namespace, entry.settingsPath, this.schema),
|
|
1727
|
-
credential: void 0
|
|
1728
|
-
};
|
|
1729
|
-
});
|
|
1730
|
-
const refs = [...new Set(rows.map((row) => row.apiKeyEnv ?? deriveKeyRef(row.entry.provider)))];
|
|
1731
|
-
let credentials = {};
|
|
1732
|
-
let credentialError = null;
|
|
1733
|
-
if (refs.length > 0) {
|
|
1734
|
-
const response = await this.ctx.remote.credentials.describe(refs);
|
|
1735
|
-
if (response.ok) credentials = response.value;
|
|
1736
|
-
else credentialError = response.error.message;
|
|
1737
|
-
}
|
|
1738
|
-
if (generation !== this.generation) return;
|
|
1739
|
-
this.store.update((s) => {
|
|
1740
|
-
s.status = "ready";
|
|
1741
|
-
s.error = null;
|
|
1742
|
-
s.credentialError = credentialError;
|
|
1743
|
-
s.writable = writable;
|
|
1744
|
-
s.rows = rows.map((row) => {
|
|
1745
|
-
const named = row.apiKeyEnv === void 0 ? void 0 : credentials[row.apiKeyEnv];
|
|
1746
|
-
const derived = row.apiKeyEnv !== void 0 ? void 0 : credentials[deriveKeyRef(row.entry.provider)];
|
|
1747
|
-
return {
|
|
1748
|
-
...row,
|
|
1749
|
-
...named === void 0 ? {} : { credential: named },
|
|
1750
|
-
...derived === void 0 ? {} : { derivedCredential: derived }
|
|
1751
|
-
};
|
|
1752
|
-
});
|
|
1753
|
-
s.namespaces = namespaces;
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
/** Publish one load's failure text, unless a newer load already took over. */
|
|
1757
|
-
failLoad(generation, message) {
|
|
1758
|
-
if (generation !== this.generation) return;
|
|
1759
|
-
this.store.update((s) => {
|
|
1760
|
-
s.status = "error";
|
|
1761
|
-
s.error = message;
|
|
1762
|
-
});
|
|
1763
|
-
}
|
|
1764
|
-
};
|
|
1765
|
-
/**
|
|
1766
|
-
* Whether a joined row can serve model requests as it stands: the route is
|
|
1767
|
-
* registered with the adapter registry, and whatever credential its resolved
|
|
1768
|
-
* profile names is stored. A profile naming no reference authenticates through
|
|
1769
|
-
* the provider's own path (the Bedrock chain, Vertex ADC, a gateway that needs
|
|
1770
|
-
* nothing), as does a live route with no settings address at all, so neither
|
|
1771
|
-
* owes this page a key.
|
|
1772
|
-
* @param row - one joined provider row.
|
|
1773
|
-
* @returns whether the user already has this provider to talk to.
|
|
1774
|
-
*/
|
|
1775
|
-
function providerUsable(row) {
|
|
1776
|
-
if (!row.entry.active) return false;
|
|
1777
|
-
if (row.apiKeyEnv === void 0) return true;
|
|
1778
|
-
return row.credential?.configured === true;
|
|
1779
|
-
}
|
|
1780
|
-
//#endregion
|
|
1781
|
-
//#region
|
|
1782
|
-
/**
|
|
1783
|
-
* The card that declares a provider pi-ai does not ship — an OpenAI-compatible
|
|
1784
|
-
* gateway, a self-hosted server, or a provider newer than the installed
|
|
1785
|
-
* catalog.
|
|
1786
|
-
*
|
|
1787
|
-
* This is a create, not an edit, which is why it is its own card rather than
|
|
1788
|
-
* the provider editor with extra fields: the route id is being *chosen* here,
|
|
1789
|
-
* and the settings address does not exist until it is. One `settings.mutate`
|
|
1790
|
-
* sets the whole profile at `providers.<route>`; the key travels separately
|
|
1791
|
-
* through `credentials/set` under the reference the profile records, exactly as
|
|
1792
|
-
* an existing provider's key does.
|
|
1793
|
-
*
|
|
1794
|
-
* The three fields a hand-declared route cannot default — endpoint, protocol,
|
|
1795
|
-
* and at least one model — are required here rather than at load, so the
|
|
1796
|
-
* failure names the field while the user is still looking at it.
|
|
1797
|
-
*
|
|
1798
|
-
* There is deliberately no reasoning-effort control, here or on the editor
|
|
1799
|
-
* card: effort is a per-MODEL capability, and the models under one provider
|
|
1800
|
-
* disagree about it, so a provider-scoped control can only be set to a value
|
|
1801
|
-
* some of them reject. The composer's model picker offers each model its own
|
|
1802
|
-
* levels instead.
|
|
1803
|
-
*/
|
|
1804
|
-
/** The settings namespace a hand-declared provider is written into. */
|
|
1805
|
-
const NS = "llm-pi-ai";
|
|
1806
|
-
/**
|
|
1807
|
-
* A route id usable as a settings key AND as the stem of a credential name.
|
|
1808
|
-
* The leading letter is the second half of that: `deriveKeyRef` uppercases the
|
|
1809
|
-
* id and replaces every non-alphanumeric run with `_`, and a credential
|
|
1810
|
-
* reference is a POSIX shell identifier, which cannot start with a digit. A
|
|
1811
|
-
* digit-leading id passes every check this card makes and then fails at the
|
|
1812
|
-
* credential seam with a raw regular expression the user cannot act on.
|
|
1813
|
-
*/
|
|
1814
|
-
const ROUTE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
1815
|
-
/**
|
|
1816
|
-
* Render the custom-provider creation card.
|
|
1817
|
-
* @param props - existing routes, protocol choices, wire faces, and copy.
|
|
1818
|
-
* @returns the creation card.
|
|
1819
|
-
*/
|
|
1820
|
-
function CustomProviderCard(props) {
|
|
1821
|
-
const { taken, protocols, operations, t } = props;
|
|
1822
|
-
const [openedAt] = (0, react.useState)(() => props.revision);
|
|
1823
|
-
const [route, setRoute] = (0, react.useState)("");
|
|
1824
|
-
const [displayName, setDisplayName] = (0, react.useState)("");
|
|
1825
|
-
const [baseURL, setBaseURL] = (0, react.useState)("");
|
|
1826
|
-
const [protocol, setProtocol] = (0, react.useState)(protocols[0] ?? "");
|
|
1827
|
-
const [keyDraft, setKeyDraft] = (0, react.useState)("");
|
|
1828
|
-
const [models, setModels] = (0, react.useState)([]);
|
|
1829
|
-
const [busy, setBusy] = (0, react.useState)(false);
|
|
1830
|
-
const [failure, setFailure] = (0, react.useState)(void 0);
|
|
1831
|
-
/**
|
|
1832
|
-
* The profile write landed. Only the key write can still be outstanding, so
|
|
1833
|
-
* the fields that describe the provider are settled and the retry path is
|
|
1834
|
-
* the credential alone.
|
|
1835
|
-
*/
|
|
1836
|
-
const [committed, setCommitted] = (0, react.useState)(false);
|
|
1837
|
-
const disabled = props.readOnly || busy;
|
|
1838
|
-
/** Everything but the key stops being editable once the provider exists. */
|
|
1839
|
-
const profileDisabled = disabled || committed;
|
|
1840
|
-
const routeInvalid = route.length > 0 && !ROUTE_PATTERN.test(route);
|
|
1841
|
-
const routeTaken = taken.includes(route);
|
|
1842
|
-
const modelFailure = validateDeepSeekModels(models);
|
|
1843
|
-
const keyFailure = apiKeyFailure(keyDraft);
|
|
1632
|
+
return () => {
|
|
1633
|
+
stale = true;
|
|
1634
|
+
};
|
|
1635
|
+
}, [operations, keyRef]);
|
|
1636
|
+
const stringAt = (source, key) => {
|
|
1637
|
+
const value = schema.getPath(source, [key]);
|
|
1638
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1639
|
+
};
|
|
1640
|
+
const setField = (key, next) => {
|
|
1641
|
+
const value = next === void 0 || next.trim().length === 0 ? void 0 : next;
|
|
1642
|
+
setDraft((current) => value === void 0 ? schema.deletePath(current, [key]) : schema.setPath(current, [key], value));
|
|
1643
|
+
};
|
|
1644
|
+
const modelFailure = validateModelRows(schema.getPath(draft, ["models"]));
|
|
1645
|
+
const keyFailure = (() => {
|
|
1646
|
+
if (keyDraft.length === 0) return void 0;
|
|
1647
|
+
const value = keyDraft.trim();
|
|
1648
|
+
if (value.length === 0) return "keyBlank";
|
|
1649
|
+
if (/^[A-Z][A-Z0-9_]*=[^=]/.test(value)) return "keyIllegalCharacters";
|
|
1650
|
+
const first = value[0];
|
|
1651
|
+
if ((first === "\"" || first === "'" || first === "`") && value.length > 1 && value.endsWith(first)) return "keyIllegalCharacters";
|
|
1652
|
+
return /^[\x21-\x7E]+$/.test(value) ? void 0 : "keyIllegalCharacters";
|
|
1653
|
+
})();
|
|
1844
1654
|
const keyValue = keyDraft.trim();
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1655
|
+
const probe = {
|
|
1656
|
+
settingsNs: namespace.ns,
|
|
1657
|
+
provider: props.provider,
|
|
1658
|
+
...(() => {
|
|
1659
|
+
const baseURL = stringAt(draft, "baseURL") ?? stringAt(fallback, "baseURL");
|
|
1660
|
+
return baseURL === void 0 ? {} : { baseURL };
|
|
1661
|
+
})(),
|
|
1662
|
+
...(() => {
|
|
1663
|
+
const api = layout === "pi-ai" && props.declared === true ? stringAt(draft, "api") ?? stringAt(fallback, "api") : void 0;
|
|
1664
|
+
return api === void 0 ? {} : { api };
|
|
1665
|
+
})(),
|
|
1666
|
+
...keyValue.length === 0 ? {} : { apiKey: keyValue }
|
|
1667
|
+
};
|
|
1668
|
+
/** The write for this card, or a failure message. */
|
|
1669
|
+
const applyOnce = async () => {
|
|
1670
|
+
const ns = namespace.ns;
|
|
1671
|
+
let next = layout === "pi-ai" && stringAt(draft, "apiKeyEnv") === void 0 && stringAt(fallback, "apiKeyEnv") === void 0 && keyValue.length > 0 ? schema.setPath(draft, ["apiKeyEnv"], keyRef) : draft;
|
|
1672
|
+
const rowFailure = validateModelRows(schema.getPath(next, ["models"]));
|
|
1673
|
+
if (rowFailure !== void 0) return `模型 ${String(rowFailure.index + 1)}:${{
|
|
1674
|
+
modelIdRequired: "模型 ID 不能为空。",
|
|
1675
|
+
modelIdDuplicate: "模型 ID 不能重复。",
|
|
1676
|
+
modelNameInvalid: "显示名称不能为空。",
|
|
1677
|
+
modelContextInvalid: "上下文窗口须为正数,如 131072、256K、1M。",
|
|
1678
|
+
modelMaxTokensInvalid: "最大输出 token 数须为正数,如 8192、64K、1M。"
|
|
1679
|
+
}[rowFailure.key]}`;
|
|
1680
|
+
if (node !== void 0 && settingsPath.length === 0) {
|
|
1681
|
+
const sectionError = schema.validate(node, next);
|
|
1682
|
+
if (sectionError !== void 0) return sectionError;
|
|
1866
1683
|
}
|
|
1867
|
-
if (
|
|
1684
|
+
if (schema.hasPath(next, ["models"])) next = schema.setPath(next, ["models"], modelDrafts(schema.getPath(next, ["models"])).map(normalizeModelRow));
|
|
1685
|
+
const ops = layout === "pi-ai" && fallback === void 0 && committedOriginal === void 0 && Object.keys(next).length === 0 ? [{
|
|
1686
|
+
op: "set",
|
|
1687
|
+
path: [...settingsPath],
|
|
1688
|
+
value: {}
|
|
1689
|
+
}] : pathOps(settingsPath, committedOriginal, next);
|
|
1690
|
+
if (ops.length > 0) {
|
|
1691
|
+
const written = await operations.writeSettings(ns, ops, expectedRevision);
|
|
1692
|
+
if (written.kind !== "written") return written.kind === "conflict" ? "这张卡片打开期间,这些设置已被其他地方改动。请关闭后重新打开,在当前值上编辑。" : written.message;
|
|
1693
|
+
setCommittedOriginal(schema.getPath(written.view.user, settingsPath));
|
|
1694
|
+
setExpectedRevision(written.view.revision);
|
|
1695
|
+
setDraft(next);
|
|
1696
|
+
}
|
|
1697
|
+
if (keyValue.length > 0) {
|
|
1868
1698
|
const stored = await operations.storeCredential(keyRef, keyValue);
|
|
1869
1699
|
if (stored !== void 0) return stored;
|
|
1870
1700
|
}
|
|
1701
|
+
setKeyDraft("");
|
|
1871
1702
|
};
|
|
1872
|
-
const
|
|
1703
|
+
const apply = async () => {
|
|
1873
1704
|
setBusy(true);
|
|
1874
1705
|
setFailure(void 0);
|
|
1875
1706
|
try {
|
|
1876
|
-
const
|
|
1877
|
-
if (
|
|
1878
|
-
setFailure(
|
|
1707
|
+
const applyFailure = await applyOnce();
|
|
1708
|
+
if (applyFailure !== void 0) {
|
|
1709
|
+
setFailure(applyFailure);
|
|
1879
1710
|
return;
|
|
1880
1711
|
}
|
|
1881
1712
|
props.onClose(true);
|
|
@@ -1883,333 +1714,288 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
1883
1714
|
setBusy(false);
|
|
1884
1715
|
}
|
|
1885
1716
|
};
|
|
1717
|
+
if (node === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1718
|
+
className: models_plus_module_default["error"],
|
|
1719
|
+
children: [props.provider, ": 无法解析设置路径"]
|
|
1720
|
+
});
|
|
1721
|
+
/**
|
|
1722
|
+
* The catalog beneath the user layer: the composition entry's pin, or the
|
|
1723
|
+
* schema default — reading the effective value would echo the stored
|
|
1724
|
+
* override back the moment reset drops it.
|
|
1725
|
+
*/
|
|
1726
|
+
const inheritedModels = () => {
|
|
1727
|
+
return schema.getPath(namespace.base, [...settingsPath, "models"]) ?? schema.nodeAtPath(root, [...settingsPath, "models"])?.meta.default;
|
|
1728
|
+
};
|
|
1729
|
+
const customModels = schema.getPath(draft, ["models"]);
|
|
1730
|
+
const modelsOverridden = schema.hasPath(draft, ["models"]);
|
|
1731
|
+
const models = modelDrafts(modelsOverridden ? customModels : inheritedModels());
|
|
1732
|
+
const defaultContextWindow = schema.getPath(fallback, ["defaultContextWindow"]);
|
|
1733
|
+
const defaultMaxTokens = schema.getPath(fallback, ["maxTokens"]);
|
|
1734
|
+
const keyPlaceholder = keyState?.writable === false ? "由启动环境提供(只读)" : keyState?.configured === true ? "已配置——输入新值可替换" : layout === "pi-ai" && props.declared === true ? "输入 API 密钥,或留空使用环境认证" : "输入 API 密钥";
|
|
1735
|
+
const keyLocked = keyState?.writable === false;
|
|
1886
1736
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1887
|
-
className:
|
|
1737
|
+
className: models_plus_module_default["editor"],
|
|
1888
1738
|
children: [
|
|
1889
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1890
|
-
className:
|
|
1891
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1892
|
-
className: ModelsSection_module_default["editorTitle"],
|
|
1893
|
-
children: t("customTitle")
|
|
1894
|
-
})
|
|
1895
|
-
}),
|
|
1896
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1897
|
-
className: ModelsSection_module_default["field"],
|
|
1898
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1899
|
-
className: ModelsSection_module_default["fieldLabel"],
|
|
1900
|
-
children: t("customRoute")
|
|
1901
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1902
|
-
className: ModelsSection_module_default["input"],
|
|
1903
|
-
type: "text",
|
|
1904
|
-
value: route,
|
|
1905
|
-
placeholder: "acme-gateway",
|
|
1906
|
-
"aria-label": t("customRoute"),
|
|
1907
|
-
disabled: profileDisabled,
|
|
1908
|
-
onChange: (event) => {
|
|
1909
|
-
setRoute(event.target.value);
|
|
1910
|
-
}
|
|
1911
|
-
})]
|
|
1912
|
-
}),
|
|
1913
|
-
routeInvalid || routeTaken ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1914
|
-
className: ModelsSection_module_default["error"],
|
|
1915
|
-
children: t(routeInvalid ? "customRouteInvalid" : "customRouteTaken")
|
|
1916
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1917
|
-
className: ModelsSection_module_default["advancedHint"],
|
|
1918
|
-
children: t("customRouteHint")
|
|
1919
|
-
}),
|
|
1920
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1921
|
-
className: ModelsSection_module_default["field"],
|
|
1922
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1923
|
-
className: ModelsSection_module_default["fieldLabel"],
|
|
1924
|
-
children: t("customDisplayName")
|
|
1925
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1926
|
-
className: ModelsSection_module_default["input"],
|
|
1927
|
-
type: "text",
|
|
1928
|
-
value: displayName,
|
|
1929
|
-
placeholder: route.length === 0 ? t("customDisplayName") : route,
|
|
1930
|
-
"aria-label": t("customDisplayName"),
|
|
1931
|
-
disabled: profileDisabled,
|
|
1932
|
-
onChange: (event) => {
|
|
1933
|
-
setDisplayName(event.target.value);
|
|
1934
|
-
}
|
|
1935
|
-
})]
|
|
1936
|
-
}),
|
|
1937
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1938
|
-
className: ModelsSection_module_default["field"],
|
|
1939
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1940
|
-
className: ModelsSection_module_default["fieldLabel"],
|
|
1941
|
-
children: t("baseUrl")
|
|
1942
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1943
|
-
className: ModelsSection_module_default["input"],
|
|
1944
|
-
type: "text",
|
|
1945
|
-
value: baseURL,
|
|
1946
|
-
placeholder: t("customBaseUrlPlaceholder"),
|
|
1947
|
-
"aria-label": t("baseUrl"),
|
|
1948
|
-
disabled: profileDisabled,
|
|
1949
|
-
onChange: (event) => {
|
|
1950
|
-
setBaseURL(event.target.value);
|
|
1951
|
-
}
|
|
1952
|
-
})]
|
|
1953
|
-
}),
|
|
1954
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1955
|
-
className: ModelsSection_module_default["field"],
|
|
1956
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1957
|
-
className: ModelsSection_module_default["fieldLabel"],
|
|
1958
|
-
children: t("customApi")
|
|
1959
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
1960
|
-
className: `${ModelsSection_module_default["input"]} ${ModelsSection_module_default["selectInput"]}`,
|
|
1961
|
-
value: protocol,
|
|
1962
|
-
"aria-label": t("customApi"),
|
|
1963
|
-
disabled: profileDisabled,
|
|
1964
|
-
onChange: (event) => {
|
|
1965
|
-
setProtocol(event.target.value);
|
|
1966
|
-
},
|
|
1967
|
-
children: protocols.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1968
|
-
value: choice,
|
|
1969
|
-
children: choice
|
|
1970
|
-
}, choice))
|
|
1971
|
-
})]
|
|
1972
|
-
}),
|
|
1973
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1974
|
-
className: ModelsSection_module_default["field"],
|
|
1739
|
+
layout === "unknown" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1740
|
+
className: models_plus_module_default["advancedHint"],
|
|
1975
1741
|
children: [
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1742
|
+
"其余字段存于 settings.yaml;请直接编辑对应段。(",
|
|
1743
|
+
namespace.ns,
|
|
1744
|
+
")"
|
|
1745
|
+
]
|
|
1746
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1747
|
+
className: models_plus_module_default["tabs"],
|
|
1748
|
+
children: [
|
|
1749
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1750
|
+
className: models_plus_module_default["tabBar"],
|
|
1751
|
+
role: "tablist",
|
|
1752
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1753
|
+
type: "button",
|
|
1754
|
+
role: "tab",
|
|
1755
|
+
"aria-selected": tab === "provider",
|
|
1756
|
+
className: tab === "provider" ? `${models_plus_module_default["tabButton"]} ${models_plus_module_default["tabButtonActive"]}` : models_plus_module_default["tabButton"],
|
|
1757
|
+
onClick: () => {
|
|
1758
|
+
setTab("provider");
|
|
1759
|
+
},
|
|
1760
|
+
children: "供应商"
|
|
1761
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1762
|
+
type: "button",
|
|
1763
|
+
role: "tab",
|
|
1764
|
+
"aria-selected": tab === "models",
|
|
1765
|
+
className: tab === "models" ? `${models_plus_module_default["tabButton"]} ${models_plus_module_default["tabButtonActive"]}` : models_plus_module_default["tabButton"],
|
|
1766
|
+
onClick: () => {
|
|
1767
|
+
setTab("models");
|
|
1768
|
+
},
|
|
1769
|
+
children: "模型"
|
|
1770
|
+
})]
|
|
1979
1771
|
}),
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1772
|
+
" ",
|
|
1773
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1774
|
+
className: models_plus_module_default["tabPanel"],
|
|
1775
|
+
role: "tabpanel",
|
|
1776
|
+
hidden: tab !== "provider",
|
|
1777
|
+
children: [
|
|
1778
|
+
layout === "pi-ai" && props.declared === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1779
|
+
className: models_plus_module_default["field"],
|
|
1780
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1781
|
+
className: models_plus_module_default["fieldLabel"],
|
|
1782
|
+
children: "显示名称"
|
|
1783
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1784
|
+
className: models_plus_module_default["input"],
|
|
1785
|
+
type: "text",
|
|
1786
|
+
value: stringAt(draft, "displayName") ?? "",
|
|
1787
|
+
placeholder: stringAt(schema.getPath(namespace.base, settingsPath), "displayName") ?? props.provider,
|
|
1788
|
+
"aria-label": "显示名称",
|
|
1789
|
+
disabled,
|
|
1790
|
+
onChange: (event) => {
|
|
1791
|
+
setField("displayName", event.target.value);
|
|
1792
|
+
}
|
|
1793
|
+
})]
|
|
1794
|
+
}) : null,
|
|
1795
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1796
|
+
className: models_plus_module_default["field"],
|
|
1797
|
+
children: [
|
|
1798
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1799
|
+
className: models_plus_module_default["fieldLabel"],
|
|
1800
|
+
children: "API 密钥"
|
|
1801
|
+
}),
|
|
1802
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1803
|
+
className: models_plus_module_default["input"],
|
|
1804
|
+
type: "password",
|
|
1805
|
+
autoComplete: "off",
|
|
1806
|
+
value: keyDraft,
|
|
1807
|
+
placeholder: keyPlaceholder,
|
|
1808
|
+
"aria-label": "API 密钥",
|
|
1809
|
+
disabled: disabled || keyLocked,
|
|
1810
|
+
onChange: (event) => {
|
|
1811
|
+
setKeyDraft(event.target.value);
|
|
1812
|
+
}
|
|
1813
|
+
}),
|
|
1814
|
+
keyFailure === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1815
|
+
className: models_plus_module_default["error"],
|
|
1816
|
+
children: keyFailure === "keyBlank" ? "请输入 API 密钥;留空则保持已存储的密钥。" : "此 API 密钥格式无效,请检查。"
|
|
1817
|
+
})
|
|
1818
|
+
]
|
|
1819
|
+
}),
|
|
1820
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1821
|
+
className: models_plus_module_default["field"],
|
|
1822
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1823
|
+
className: models_plus_module_default["fieldLabel"],
|
|
1824
|
+
children: "API 地址"
|
|
1825
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1826
|
+
className: models_plus_module_default["input"],
|
|
1827
|
+
type: "text",
|
|
1828
|
+
value: stringAt(draft, "baseURL") ?? "",
|
|
1829
|
+
placeholder: layout === "deepseek" ? DEEPSEEK_PUBLIC_BASE_URL : stringAt(fallback, "baseURL") ?? "提供方默认",
|
|
1830
|
+
"aria-label": "API 地址",
|
|
1831
|
+
disabled,
|
|
1832
|
+
onChange: (event) => {
|
|
1833
|
+
setField("baseURL", event.target.value === "" ? void 0 : event.target.value);
|
|
1834
|
+
}
|
|
1835
|
+
})]
|
|
1836
|
+
}),
|
|
1837
|
+
layout === "pi-ai" && props.declared === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1838
|
+
className: models_plus_module_default["field"],
|
|
1839
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1840
|
+
className: models_plus_module_default["fieldLabel"],
|
|
1841
|
+
children: "API 协议"
|
|
1842
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1843
|
+
className: `${models_plus_module_default["input"]} ${models_plus_module_default["selectInput"]}`,
|
|
1844
|
+
value: stringAt(draft, "api") ?? stringAt(fallback, "api") ?? "",
|
|
1845
|
+
"aria-label": "API 协议",
|
|
1846
|
+
disabled,
|
|
1847
|
+
onChange: (event) => {
|
|
1848
|
+
setField("api", event.target.value);
|
|
1849
|
+
},
|
|
1850
|
+
children: [(stringAt(draft, "api") ?? stringAt(fallback, "api")) === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1851
|
+
value: "",
|
|
1852
|
+
children: "未选择"
|
|
1853
|
+
}) : null, protocols.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1854
|
+
value: choice,
|
|
1855
|
+
children: choice
|
|
1856
|
+
}, choice))]
|
|
1857
|
+
})]
|
|
1858
|
+
}) : null
|
|
1859
|
+
]
|
|
1991
1860
|
}),
|
|
1992
|
-
|
|
1993
|
-
className:
|
|
1994
|
-
|
|
1861
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1862
|
+
className: models_plus_module_default["tabPanel"],
|
|
1863
|
+
role: "tabpanel",
|
|
1864
|
+
hidden: tab !== "models",
|
|
1865
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalog, {
|
|
1866
|
+
models,
|
|
1867
|
+
overridden: modelsOverridden,
|
|
1868
|
+
defaultContextWindow: typeof defaultContextWindow === "number" ? defaultContextWindow : void 0,
|
|
1869
|
+
defaultMaxTokens: typeof defaultMaxTokens === "number" ? defaultMaxTokens : void 0,
|
|
1870
|
+
probe,
|
|
1871
|
+
probeBlocked: keyFailure,
|
|
1872
|
+
operations,
|
|
1873
|
+
disabled,
|
|
1874
|
+
onChange: (rows) => {
|
|
1875
|
+
setDraft((current) => schema.setPath(current, ["models"], rows));
|
|
1876
|
+
},
|
|
1877
|
+
onReset: () => {
|
|
1878
|
+
setDraft((current) => schema.deletePath(current, ["models"]));
|
|
1879
|
+
}
|
|
1880
|
+
})
|
|
1995
1881
|
})
|
|
1996
1882
|
]
|
|
1997
|
-
}),
|
|
1998
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelListEditor, {
|
|
1999
|
-
models,
|
|
2000
|
-
onChange: setModels,
|
|
2001
|
-
probe: {
|
|
2002
|
-
settingsNs: NS,
|
|
2003
|
-
baseURL,
|
|
2004
|
-
api: protocol,
|
|
2005
|
-
...keyValue.length === 0 ? {} : { apiKey: keyValue }
|
|
2006
|
-
},
|
|
2007
|
-
probeBlocked: keyFailure === "keyBlank" ? "keyBlankNew" : keyFailure,
|
|
2008
|
-
operations,
|
|
2009
|
-
t,
|
|
2010
|
-
disabled: profileDisabled
|
|
2011
|
-
}),
|
|
1883
|
+
}) }),
|
|
2012
1884
|
failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2013
|
-
className:
|
|
1885
|
+
className: models_plus_module_default["error"],
|
|
2014
1886
|
children: failure
|
|
2015
1887
|
}) : null,
|
|
2016
|
-
|
|
2017
|
-
className:
|
|
2018
|
-
children:
|
|
1888
|
+
modelFailure === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1889
|
+
className: models_plus_module_default["advancedHint"],
|
|
1890
|
+
children: `模型 ${String(modelFailure.index + 1)}:${modelFailure.key}`
|
|
2019
1891
|
}),
|
|
2020
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
1892
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1893
|
+
className: models_plus_module_default["editorActions"],
|
|
1894
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1895
|
+
type: "button",
|
|
1896
|
+
className: models_plus_module_default["secondaryButton"],
|
|
1897
|
+
disabled: busy,
|
|
1898
|
+
onClick: () => {
|
|
1899
|
+
props.onClose(false);
|
|
1900
|
+
},
|
|
1901
|
+
children: "取消"
|
|
1902
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1903
|
+
type: "button",
|
|
1904
|
+
className: models_plus_module_default["primaryButton"],
|
|
1905
|
+
disabled: disabled || layout === "unknown" || modelFailure !== void 0 || keyFailure !== void 0,
|
|
1906
|
+
onClick: () => {
|
|
1907
|
+
apply();
|
|
1908
|
+
},
|
|
1909
|
+
children: busy ? "保存中…" : "保存"
|
|
1910
|
+
})]
|
|
2032
1911
|
})
|
|
2033
1912
|
]
|
|
2034
1913
|
});
|
|
2035
1914
|
}
|
|
2036
1915
|
//#endregion
|
|
2037
|
-
//#region
|
|
2038
|
-
/**
|
|
2039
|
-
* One provider's editor card, hand-written per adapter family: the primary
|
|
2040
|
-
* field is a single write-only **API key** input (the page never asks for an
|
|
2041
|
-
* environment-variable name — a typed key stores through `credentials/set`
|
|
2042
|
-
* under the profile's reference, deriving `<ROUTE>_API_KEY` when the profile
|
|
2043
|
-
* has none. The pi-ai profile records that derivation as `apiKeyEnv` only when
|
|
2044
|
-
* a key is entered; a blank key materializes a reference-free profile for
|
|
2045
|
-
* provider-native authentication);
|
|
2046
|
-
* the collapsed 自定义设置 area carries the per-family extras (`baseURL` for
|
|
2047
|
-
* both families, DeepSeek's id/name/context-window model catalog, and the
|
|
2048
|
-
* display name and wire protocol of a pi-ai route the adapter does not ship —
|
|
2049
|
-
* the two fields the create card asked that route for, editable here for the
|
|
2050
|
-
* same reason).
|
|
2051
|
-
* Reasoning effort is deliberately absent: it is a per-MODEL capability, and
|
|
2052
|
-
* the models under one provider disagree about it, so a provider-scoped
|
|
2053
|
-
* control can only be set to a value some of them reject. The composer's
|
|
2054
|
-
* model picker offers each model its own levels; `settings.yaml` keeps the
|
|
2055
|
-
* profile field for a deployment that knows its route. Everything else stays
|
|
2056
|
-
* owned by `settings.yaml`. Profile edits land as minimal `settings.mutate`
|
|
2057
|
-
* path ops against the stored section — the card names only the fields it can
|
|
2058
|
-
* see instead of rebuilding the whole subtree from a partial descriptor.
|
|
2059
|
-
*/
|
|
2060
|
-
/** The public DeepSeek endpoint shown as the deepseek base-URL placeholder. */
|
|
2061
|
-
const DEEPSEEK_PUBLIC_BASE_URL = "https://api.deepseek.com";
|
|
2062
|
-
/** A user-section subtree as a plain draft object (absent → empty). */
|
|
2063
|
-
function draftAt(schema, namespace, path) {
|
|
2064
|
-
const subtree = schema.getPath(namespace.user, path);
|
|
2065
|
-
if (typeof subtree !== "object" || subtree === null || Array.isArray(subtree)) return {};
|
|
2066
|
-
return structuredClone(subtree);
|
|
2067
|
-
}
|
|
1916
|
+
//#region src/client/models-plus/CreateProviderCard.tsx
|
|
2068
1917
|
/**
|
|
2069
|
-
* The
|
|
2070
|
-
*
|
|
2071
|
-
*
|
|
2072
|
-
*
|
|
2073
|
-
*
|
|
2074
|
-
*
|
|
2075
|
-
* @param after - the subtree as edited.
|
|
2076
|
-
* @returns ordered set/unset ops; empty when nothing changed.
|
|
1918
|
+
* The card that declares a provider pi-ai does not ship — an OpenAI-compatible
|
|
1919
|
+
* gateway, a self-hosted server, or a provider newer than the installed
|
|
1920
|
+
* catalog. One `settings.mutate` sets the whole profile at `providers.<route>`;
|
|
1921
|
+
* the key travels separately through `credentials/set` under the reference
|
|
1922
|
+
* the profile records. The three fields a hand-declared route cannot default
|
|
1923
|
+
* (endpoint, protocol, at least one model) are required here.
|
|
2077
1924
|
*/
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
const ops = [];
|
|
2081
|
-
for (const [key, value] of Object.entries(after)) {
|
|
2082
|
-
if (JSON.stringify(previous[key]) === JSON.stringify(value)) continue;
|
|
2083
|
-
ops.push({
|
|
2084
|
-
op: "set",
|
|
2085
|
-
path: [...base, key],
|
|
2086
|
-
value
|
|
2087
|
-
});
|
|
2088
|
-
}
|
|
2089
|
-
for (const key of Object.keys(previous)) if (!(key in after)) ops.push({
|
|
2090
|
-
op: "unset",
|
|
2091
|
-
path: [...base, key]
|
|
2092
|
-
});
|
|
2093
|
-
return ops;
|
|
2094
|
-
}
|
|
2095
|
-
/** The editor layout the owning namespace selects. */
|
|
2096
|
-
function layoutOf(ns) {
|
|
2097
|
-
if (ns === "llm-deepseek") return "deepseek";
|
|
2098
|
-
if (ns === "llm-pi-ai") return "pi-ai";
|
|
2099
|
-
return "unknown";
|
|
2100
|
-
}
|
|
2101
|
-
/** The credential reference this profile resolves keys through. */
|
|
2102
|
-
function refFor(schema, namespace, path, provider) {
|
|
2103
|
-
const profile = schema.getPath(namespace.value, path);
|
|
2104
|
-
const named = typeof profile === "object" && profile !== null ? profile.apiKeyEnv : void 0;
|
|
2105
|
-
return typeof named === "string" && named.length > 0 ? named : deriveKeyRef(provider);
|
|
2106
|
-
}
|
|
1925
|
+
/** The settings namespace a hand-declared provider is written into. */
|
|
1926
|
+
const NS = "llm-pi-ai";
|
|
2107
1927
|
/**
|
|
2108
|
-
*
|
|
2109
|
-
* @param props - the addressed profile plus wire faces and copy.
|
|
2110
|
-
* @returns the editor card.
|
|
1928
|
+
* A route id usable as a settings key AND as the stem of a credential name.
|
|
2111
1929
|
*/
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
1930
|
+
const ROUTE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
1931
|
+
/**
|
|
1932
|
+
* Render the custom-provider creation card.
|
|
1933
|
+
* @param props - existing routes, protocol choices, wire faces.
|
|
1934
|
+
* @returns the creation card.
|
|
1935
|
+
*/
|
|
1936
|
+
function CreateProviderCard(props) {
|
|
1937
|
+
const { taken, protocols, operations } = props;
|
|
1938
|
+
const [openedAt] = (0, react.useState)(() => props.revision);
|
|
1939
|
+
const [route, setRoute] = (0, react.useState)("");
|
|
1940
|
+
const [displayName, setDisplayName] = (0, react.useState)("");
|
|
1941
|
+
const [baseURL, setBaseURL] = (0, react.useState)("");
|
|
1942
|
+
const [protocol, setProtocol] = (0, react.useState)(protocols[0] ?? "");
|
|
1943
|
+
const [tab, setTab] = (0, react.useState)("provider");
|
|
2115
1944
|
const [keyDraft, setKeyDraft] = (0, react.useState)("");
|
|
2116
|
-
const [
|
|
1945
|
+
const [models, setModels] = (0, react.useState)([]);
|
|
2117
1946
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
2118
1947
|
const [failure, setFailure] = (0, react.useState)(void 0);
|
|
2119
|
-
|
|
2120
|
-
const [
|
|
2121
|
-
const root = (0, react.useMemo)(() => schema.rehydrate(namespace.schema), [namespace.schema, schema]);
|
|
2122
|
-
const node = (0, react.useMemo)(() => schema.nodeAtPath(root, settingsPath), [
|
|
2123
|
-
root,
|
|
2124
|
-
schema,
|
|
2125
|
-
settingsPath
|
|
2126
|
-
]);
|
|
2127
|
-
const fallback = schema.getPath(namespace.value, settingsPath);
|
|
1948
|
+
/** The profile write landed; only the key write can still be outstanding. */
|
|
1949
|
+
const [committed, setCommitted] = (0, react.useState)(false);
|
|
2128
1950
|
const disabled = props.readOnly || busy;
|
|
2129
|
-
const
|
|
2130
|
-
const
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
});
|
|
2143
|
-
return () => {
|
|
2144
|
-
stale = true;
|
|
2145
|
-
};
|
|
2146
|
-
}, [operations, keyRef]);
|
|
2147
|
-
const stringAt = (source, key) => {
|
|
2148
|
-
const value = schema.getPath(source, [key]);
|
|
2149
|
-
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
2150
|
-
};
|
|
2151
|
-
const setField = (key, next) => {
|
|
2152
|
-
const value = next === void 0 || next.trim().length === 0 ? void 0 : next;
|
|
2153
|
-
setDraft((current) => value === void 0 ? schema.deletePath(current, [key]) : schema.setPath(current, [key], value));
|
|
2154
|
-
};
|
|
2155
|
-
const modelFailure = validateDeepSeekModels(schema.getPath(draft, ["models"]));
|
|
2156
|
-
const keyFailure = apiKeyFailure(keyDraft);
|
|
1951
|
+
const profileDisabled = disabled || committed;
|
|
1952
|
+
const routeInvalid = route.length > 0 && !ROUTE_PATTERN.test(route);
|
|
1953
|
+
const routeTaken = taken.includes(route);
|
|
1954
|
+
const modelFailure = validateModelRows(models);
|
|
1955
|
+
const keyFailure = (() => {
|
|
1956
|
+
if (keyDraft.length === 0) return void 0;
|
|
1957
|
+
const value = keyDraft.trim();
|
|
1958
|
+
if (value.length === 0) return "keyBlank";
|
|
1959
|
+
if (/^[A-Z][A-Z0-9_]*=[^=]/.test(value)) return "keyIllegalCharacters";
|
|
1960
|
+
const first = value[0];
|
|
1961
|
+
if ((first === "\"" || first === "'" || first === "`") && value.length > 1 && value.endsWith(first)) return "keyIllegalCharacters";
|
|
1962
|
+
return /^[\x21-\x7E]+$/.test(value) ? void 0 : "keyIllegalCharacters";
|
|
1963
|
+
})();
|
|
2157
1964
|
const keyValue = keyDraft.trim();
|
|
2158
|
-
const
|
|
2159
|
-
const
|
|
2160
|
-
|
|
2161
|
-
const
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
/* v8 ignore next 3 -- unreachable from the card: the same failure disables submit */
|
|
2180
|
-
if (failure !== void 0) return `${t("model")} ${String(failure.index + 1)}: ${t(failure.key)}`;
|
|
2181
|
-
}
|
|
2182
|
-
/* v8 ignore next -- apply is only reachable from the rendered card, which required a resolved node */
|
|
2183
|
-
if (props.credentialOnly !== true && node !== void 0 && settingsPath.length === 0) {
|
|
2184
|
-
const sectionError = schema.validate(node, next);
|
|
2185
|
-
if (sectionError !== void 0) return sectionError;
|
|
2186
|
-
}
|
|
2187
|
-
const materializesNativeProfile = layout === "pi-ai" && fallback === void 0 && committedOriginal === void 0 && Object.keys(next).length === 0;
|
|
2188
|
-
const ops = props.credentialOnly === true ? [] : materializesNativeProfile ? [{
|
|
2189
|
-
op: "set",
|
|
2190
|
-
path: [...settingsPath],
|
|
2191
|
-
value: {}
|
|
2192
|
-
}] : pathOps(settingsPath, committedOriginal, next);
|
|
2193
|
-
if (ops.length > 0) {
|
|
2194
|
-
const written = await operations.writeSettings(ns, ops, expectedRevision);
|
|
2195
|
-
if (written.kind !== "written") return written.kind === "conflict" ? t("conflict") : written.message;
|
|
2196
|
-
setCommittedOriginal(schema.getPath(written.view.user, settingsPath));
|
|
2197
|
-
setExpectedRevision(written.view.revision);
|
|
2198
|
-
setDraft(next);
|
|
1965
|
+
const ready = route.length > 0 && !routeInvalid && !routeTaken && baseURL.length > 0 && models.length > 0 && modelFailure === void 0 && keyFailure === void 0;
|
|
1966
|
+
const hint = failure !== void 0 || ready || keyFailure !== void 0 || route.length === 0 || routeInvalid || routeTaken ? void 0 : baseURL.length === 0 ? "自定义提供方需要填写 API 地址。" : modelFailure !== void 0 ? `模型 ${String(modelFailure.index + 1)}:${modelFailure.key}` : "自定义提供方至少需要一个模型。";
|
|
1967
|
+
/** Perform the create, returning a failure message or undefined. */
|
|
1968
|
+
const createOnce = async () => {
|
|
1969
|
+
const keyRef = deriveKeyRef(route);
|
|
1970
|
+
const storesKey = keyValue.length > 0;
|
|
1971
|
+
if (!committed) {
|
|
1972
|
+
const profile = {
|
|
1973
|
+
...displayName.length === 0 ? {} : { displayName },
|
|
1974
|
+
...storesKey ? { apiKeyEnv: keyRef } : {},
|
|
1975
|
+
api: protocol,
|
|
1976
|
+
baseURL,
|
|
1977
|
+
models: models.map(normalizeModelRow)
|
|
1978
|
+
};
|
|
1979
|
+
const written = await operations.writeSettings(NS, [{
|
|
1980
|
+
op: "set",
|
|
1981
|
+
path: ["providers", route],
|
|
1982
|
+
value: profile
|
|
1983
|
+
}], openedAt);
|
|
1984
|
+
if (written.kind !== "written") return written.kind === "conflict" ? "这张卡片打开期间,这些设置已被其他地方改动。请关闭后重新打开,在当前值上编辑。" : written.message;
|
|
1985
|
+
setCommitted(true);
|
|
2199
1986
|
}
|
|
2200
|
-
if (
|
|
1987
|
+
if (storesKey) {
|
|
2201
1988
|
const stored = await operations.storeCredential(keyRef, keyValue);
|
|
2202
1989
|
if (stored !== void 0) return stored;
|
|
2203
1990
|
}
|
|
2204
|
-
setKeyDraft("");
|
|
2205
1991
|
};
|
|
2206
|
-
const
|
|
1992
|
+
const create = async () => {
|
|
2207
1993
|
setBusy(true);
|
|
2208
1994
|
setFailure(void 0);
|
|
2209
1995
|
try {
|
|
2210
|
-
const
|
|
2211
|
-
if (
|
|
2212
|
-
setFailure(
|
|
1996
|
+
const outcome = await createOnce();
|
|
1997
|
+
if (outcome !== void 0) {
|
|
1998
|
+
setFailure(outcome);
|
|
2213
1999
|
return;
|
|
2214
2000
|
}
|
|
2215
2001
|
props.onClose(true);
|
|
@@ -2217,272 +2003,215 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2217
2003
|
setBusy(false);
|
|
2218
2004
|
}
|
|
2219
2005
|
};
|
|
2220
|
-
|
|
2221
|
-
className:
|
|
2006
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2007
|
+
className: models_plus_module_default["editor"],
|
|
2222
2008
|
children: [
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
t,
|
|
2257
|
-
disabled,
|
|
2258
|
-
onChange: (next) => {
|
|
2259
|
-
setDraft((current) => schema.setPath(current, ["models"], next));
|
|
2260
|
-
},
|
|
2261
|
-
onReset: () => {
|
|
2262
|
-
setDraft((current) => schema.deletePath(current, ["models"]));
|
|
2263
|
-
}
|
|
2264
|
-
};
|
|
2265
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2266
|
-
className: ModelsSection_module_default["field"],
|
|
2267
|
-
children: [
|
|
2268
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2269
|
-
className: ModelsSection_module_default["fieldLabel"],
|
|
2270
|
-
children: t("keyInput")
|
|
2271
|
-
}),
|
|
2272
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2273
|
-
className: ModelsSection_module_default["input"],
|
|
2274
|
-
type: "password",
|
|
2275
|
-
autoComplete: "off",
|
|
2276
|
-
value: keyDraft,
|
|
2277
|
-
placeholder: keyPlaceholder,
|
|
2278
|
-
"aria-label": t("keyInput"),
|
|
2279
|
-
"aria-invalid": shownKeyFailure !== void 0,
|
|
2280
|
-
required: props.credentialRequired === true,
|
|
2281
|
-
autoFocus: props.autoFocusCredential === true,
|
|
2282
|
-
disabled: disabled || keyLocked,
|
|
2283
|
-
onChange: (event) => {
|
|
2284
|
-
setKeyDraft(event.target.value);
|
|
2285
|
-
}
|
|
2286
|
-
}),
|
|
2287
|
-
shownKeyFailure === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2288
|
-
className: ModelsSection_module_default["error"],
|
|
2289
|
-
children: t(shownKeyFailure)
|
|
2290
|
-
})
|
|
2291
|
-
]
|
|
2292
|
-
}), props.credentialOnly === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
2293
|
-
className: ModelsSection_module_default["customized"],
|
|
2294
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
|
|
2295
|
-
className: ModelsSection_module_default["customizedSummary"],
|
|
2296
|
-
children: t("customized")
|
|
2297
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2298
|
-
className: ModelsSection_module_default["customizedBody"],
|
|
2009
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2010
|
+
className: models_plus_module_default["field"],
|
|
2011
|
+
style: {
|
|
2012
|
+
paddingTop: 12,
|
|
2013
|
+
borderTop: "0.5px solid var(--dsw-alias-border-l2)"
|
|
2014
|
+
},
|
|
2015
|
+
children: [
|
|
2016
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2017
|
+
className: models_plus_module_default["fieldLabel"],
|
|
2018
|
+
children: "Provider ID"
|
|
2019
|
+
}),
|
|
2020
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2021
|
+
className: models_plus_module_default["input"],
|
|
2022
|
+
type: "text",
|
|
2023
|
+
value: route,
|
|
2024
|
+
placeholder: "acme-gateway",
|
|
2025
|
+
"aria-label": "Provider ID",
|
|
2026
|
+
disabled: profileDisabled,
|
|
2027
|
+
onChange: (event) => {
|
|
2028
|
+
setRoute(event.target.value);
|
|
2029
|
+
}
|
|
2030
|
+
}),
|
|
2031
|
+
routeInvalid || routeTaken ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2032
|
+
className: models_plus_module_default["error"],
|
|
2033
|
+
children: routeInvalid ? "需以小写字母开头,之后可用小写字母、数字和短横线。" : "已有提供方使用了这个 ID。"
|
|
2034
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2035
|
+
className: models_plus_module_default["advancedHint"],
|
|
2036
|
+
children: "以小写字母开头的标识,在请求中唯一标识该提供方,并用于派生凭据名。"
|
|
2037
|
+
})
|
|
2038
|
+
]
|
|
2039
|
+
}),
|
|
2040
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2041
|
+
className: models_plus_module_default["tabs"],
|
|
2299
2042
|
children: [
|
|
2300
|
-
ownsIdentity ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2301
|
-
className: ModelsSection_module_default["field"],
|
|
2302
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2303
|
-
className: ModelsSection_module_default["fieldLabel"],
|
|
2304
|
-
children: t("customDisplayName")
|
|
2305
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2306
|
-
className: ModelsSection_module_default["input"],
|
|
2307
|
-
type: "text",
|
|
2308
|
-
value: stringAt(draft, "displayName") ?? "",
|
|
2309
|
-
placeholder: stringAt(schema.getPath(namespace.base, settingsPath), "displayName") ?? props.provider,
|
|
2310
|
-
"aria-label": t("customDisplayName"),
|
|
2311
|
-
disabled,
|
|
2312
|
-
onChange: (event) => {
|
|
2313
|
-
setField("displayName", event.target.value);
|
|
2314
|
-
}
|
|
2315
|
-
})]
|
|
2316
|
-
}) : null,
|
|
2317
2043
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2318
|
-
className:
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2044
|
+
className: models_plus_module_default["tabBar"],
|
|
2045
|
+
role: "tablist",
|
|
2046
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2047
|
+
type: "button",
|
|
2048
|
+
role: "tab",
|
|
2049
|
+
"aria-selected": tab === "provider",
|
|
2050
|
+
className: tab === "provider" ? `${models_plus_module_default["tabButton"]} ${models_plus_module_default["tabButtonActive"]}` : models_plus_module_default["tabButton"],
|
|
2051
|
+
onClick: () => {
|
|
2052
|
+
setTab("provider");
|
|
2053
|
+
},
|
|
2054
|
+
children: "供应商"
|
|
2055
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2056
|
+
type: "button",
|
|
2057
|
+
role: "tab",
|
|
2058
|
+
"aria-selected": tab === "models",
|
|
2059
|
+
className: tab === "models" ? `${models_plus_module_default["tabButton"]} ${models_plus_module_default["tabButtonActive"]}` : models_plus_module_default["tabButton"],
|
|
2060
|
+
onClick: () => {
|
|
2061
|
+
setTab("models");
|
|
2062
|
+
},
|
|
2063
|
+
children: "模型"
|
|
2332
2064
|
})]
|
|
2333
2065
|
}),
|
|
2334
|
-
|
|
2335
|
-
className:
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2066
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2067
|
+
className: models_plus_module_default["tabPanel"],
|
|
2068
|
+
role: "tabpanel",
|
|
2069
|
+
hidden: tab !== "provider",
|
|
2070
|
+
children: [
|
|
2071
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2072
|
+
className: models_plus_module_default["field"],
|
|
2073
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2074
|
+
className: models_plus_module_default["fieldLabel"],
|
|
2075
|
+
children: "显示名称"
|
|
2076
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2077
|
+
className: models_plus_module_default["input"],
|
|
2078
|
+
type: "text",
|
|
2079
|
+
value: displayName,
|
|
2080
|
+
placeholder: route.length === 0 ? "显示名称" : route,
|
|
2081
|
+
"aria-label": "显示名称",
|
|
2082
|
+
disabled: profileDisabled,
|
|
2083
|
+
onChange: (event) => {
|
|
2084
|
+
setDisplayName(event.target.value);
|
|
2085
|
+
}
|
|
2086
|
+
})]
|
|
2087
|
+
}),
|
|
2088
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2089
|
+
className: models_plus_module_default["field"],
|
|
2090
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2091
|
+
className: models_plus_module_default["fieldLabel"],
|
|
2092
|
+
children: "API 地址"
|
|
2093
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2094
|
+
className: models_plus_module_default["input"],
|
|
2095
|
+
type: "text",
|
|
2096
|
+
value: baseURL,
|
|
2097
|
+
placeholder: "https://gateway.example/v1",
|
|
2098
|
+
"aria-label": "API 地址",
|
|
2099
|
+
disabled: profileDisabled,
|
|
2100
|
+
onChange: (event) => {
|
|
2101
|
+
setBaseURL(event.target.value);
|
|
2102
|
+
}
|
|
2103
|
+
})]
|
|
2104
|
+
}),
|
|
2105
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2106
|
+
className: models_plus_module_default["field"],
|
|
2107
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2108
|
+
className: models_plus_module_default["fieldLabel"],
|
|
2109
|
+
children: "API 协议"
|
|
2110
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
2111
|
+
className: `${models_plus_module_default["input"]} ${models_plus_module_default["selectInput"]}`,
|
|
2112
|
+
value: protocol,
|
|
2113
|
+
"aria-label": "API 协议",
|
|
2114
|
+
disabled: profileDisabled,
|
|
2115
|
+
onChange: (event) => {
|
|
2116
|
+
setProtocol(event.target.value);
|
|
2117
|
+
},
|
|
2118
|
+
children: protocols.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2119
|
+
value: choice,
|
|
2120
|
+
children: choice
|
|
2121
|
+
}, choice))
|
|
2122
|
+
})]
|
|
2123
|
+
}),
|
|
2124
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2125
|
+
className: models_plus_module_default["field"],
|
|
2126
|
+
children: [
|
|
2127
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2128
|
+
className: models_plus_module_default["fieldLabel"],
|
|
2129
|
+
children: "API 密钥"
|
|
2130
|
+
}),
|
|
2131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2132
|
+
className: models_plus_module_default["input"],
|
|
2133
|
+
type: "password",
|
|
2134
|
+
autoComplete: "off",
|
|
2135
|
+
value: keyDraft,
|
|
2136
|
+
placeholder: "输入 API 密钥,或留空使用环境认证",
|
|
2137
|
+
"aria-label": "API 密钥",
|
|
2138
|
+
disabled,
|
|
2139
|
+
onChange: (event) => {
|
|
2140
|
+
setKeyDraft(event.target.value);
|
|
2141
|
+
}
|
|
2142
|
+
}),
|
|
2143
|
+
keyFailure === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2144
|
+
className: models_plus_module_default["error"],
|
|
2145
|
+
children: keyFailure === "keyBlank" ? "请输入 API 密钥;若该提供方以其他方式鉴权,可以留空。" : "此 API 密钥格式无效,请检查。"
|
|
2146
|
+
})
|
|
2147
|
+
]
|
|
2148
|
+
})
|
|
2149
|
+
]
|
|
2150
|
+
}),
|
|
2151
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2152
|
+
className: models_plus_module_default["tabPanel"],
|
|
2153
|
+
role: "tabpanel",
|
|
2154
|
+
hidden: tab !== "models",
|
|
2155
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalog, {
|
|
2156
|
+
models,
|
|
2157
|
+
overridden: true,
|
|
2158
|
+
defaultContextWindow: void 0,
|
|
2159
|
+
defaultMaxTokens: void 0,
|
|
2160
|
+
probe: {
|
|
2161
|
+
settingsNs: NS,
|
|
2162
|
+
baseURL,
|
|
2163
|
+
api: protocol,
|
|
2164
|
+
...keyValue.length === 0 ? {} : { apiKey: keyValue }
|
|
2346
2165
|
},
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
children: choice
|
|
2353
|
-
}, choice))]
|
|
2354
|
-
})]
|
|
2355
|
-
}) : null,
|
|
2356
|
-
family === "deepseek" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeepSeekModelsEditor, {
|
|
2357
|
-
...catalogProps,
|
|
2358
|
-
defaultContextWindow: typeof defaultContextWindow === "number" ? defaultContextWindow : void 0,
|
|
2359
|
-
defaultMaxTokens: typeof defaultMaxTokens === "number" ? defaultMaxTokens : void 0
|
|
2360
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelListEditor, {
|
|
2361
|
-
...catalogProps,
|
|
2362
|
-
probe,
|
|
2363
|
-
probeBlocked: keyFailure,
|
|
2364
|
-
operations
|
|
2166
|
+
probeBlocked: keyFailure === "keyBlank" ? "请输入 API 密钥;若该提供方以其他方式鉴权,可以留空。" : keyFailure,
|
|
2167
|
+
operations,
|
|
2168
|
+
disabled: profileDisabled,
|
|
2169
|
+
onChange: setModels
|
|
2170
|
+
})
|
|
2365
2171
|
})
|
|
2366
2172
|
]
|
|
2367
|
-
})]
|
|
2368
|
-
})] });
|
|
2369
|
-
};
|
|
2370
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2371
|
-
className: props.credentialOnly === true ? ModelsSection_module_default["addBlock"] : ModelsSection_module_default["editor"],
|
|
2372
|
-
children: [
|
|
2373
|
-
props.hideTitle === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2374
|
-
className: ModelsSection_module_default["editorHeader"],
|
|
2375
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2376
|
-
className: ModelsSection_module_default["editorTitle"],
|
|
2377
|
-
children: props.displayName
|
|
2378
|
-
}), props.provider !== props.displayName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2379
|
-
className: ModelsSection_module_default["editorRoute"],
|
|
2380
|
-
children: props.provider
|
|
2381
|
-
}) : null]
|
|
2382
2173
|
}),
|
|
2383
|
-
layout === "unknown" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2384
|
-
className: ModelsSection_module_default["advancedHint"],
|
|
2385
|
-
children: `${t("advancedHint")} (${namespace.ns})`
|
|
2386
|
-
}) : curatedFields(layout),
|
|
2387
2174
|
failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2388
|
-
className:
|
|
2175
|
+
className: models_plus_module_default["error"],
|
|
2389
2176
|
children: failure
|
|
2390
2177
|
}) : null,
|
|
2391
|
-
|
|
2392
|
-
className:
|
|
2393
|
-
children:
|
|
2178
|
+
hint === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2179
|
+
className: models_plus_module_default["advancedHint"],
|
|
2180
|
+
children: hint
|
|
2394
2181
|
}),
|
|
2395
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2182
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2183
|
+
className: models_plus_module_default["editorActions"],
|
|
2184
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2185
|
+
type: "button",
|
|
2186
|
+
className: models_plus_module_default["secondaryButton"],
|
|
2187
|
+
disabled: busy,
|
|
2188
|
+
onClick: () => {
|
|
2189
|
+
props.onClose(committed);
|
|
2190
|
+
},
|
|
2191
|
+
children: "取消"
|
|
2192
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2193
|
+
type: "button",
|
|
2194
|
+
className: models_plus_module_default["primaryButton"],
|
|
2195
|
+
disabled: disabled || !ready,
|
|
2196
|
+
onClick: () => {
|
|
2197
|
+
create();
|
|
2198
|
+
},
|
|
2199
|
+
children: busy ? "创建中…" : "创建提供方"
|
|
2200
|
+
})]
|
|
2408
2201
|
})
|
|
2409
2202
|
]
|
|
2410
2203
|
});
|
|
2411
2204
|
}
|
|
2412
2205
|
//#endregion
|
|
2413
|
-
//#region src/client/
|
|
2414
|
-
/**
|
|
2415
|
-
* Models settings section (EXT fork for dsh-model-extension).
|
|
2416
|
-
*
|
|
2417
|
-
* Forked from deepseek-harness packages/client/ui-settings-models
|
|
2418
|
-
* src/client/ModelsSection.tsx @ 0.1.2-alpha.4.
|
|
2419
|
-
* Differences vs upstream (keep this list exhaustive when merging drift):
|
|
2420
|
-
* 1. The h2 title row carries an adapter-version badge.
|
|
2421
|
-
* 2. No child-slot machinery: this is a standalone section (id
|
|
2422
|
-
* 'models-extension'), and the 'settings.models.provider-card' /
|
|
2423
|
-
* 'settings.models.footer' children are already declared globally by the
|
|
2424
|
-
* official Models section — redeclaring them throws, so every renderSlot
|
|
2425
|
-
* call (and the addRow/keyConfiguredOf helpers serving only the seat) is
|
|
2426
|
-
* removed. Third-party provider-card/footer extensions therefore render on
|
|
2427
|
-
* the official Models page only; that is an accepted trade-off of the fork.
|
|
2428
|
-
* 3. Styles come from the plugin's own ModelsSection.ext.module.css.
|
|
2429
|
-
* Everything else is upstream verbatim — keep this file minimal when merging
|
|
2430
|
-
* drift after a host upgrade.
|
|
2431
|
-
*
|
|
2432
|
-
* Models settings section: the provider rows joined from the configurable
|
|
2433
|
-
* directory, settings namespaces, and credential states, with one editor
|
|
2434
|
-
* card at a time. Rows expose only confirmed API-key state through accessible
|
|
2435
|
-
* solid configured or missing dots. A whole-section provider without a
|
|
2436
|
-
* configured key renders as its open setup card instead of a row, but only in
|
|
2437
|
-
* the first-run posture — no provider on the page can serve requests yet — and
|
|
2438
|
-
* only until the user closes that card; the add flow is a card carrying the
|
|
2439
|
-
* dormant-provider select. Each card kind owns its own open state, so closing
|
|
2440
|
-
* one never discards a draft in another. Every mutation writes through the
|
|
2441
|
-
* wire, while a provider removal first requires confirmation; the page
|
|
2442
|
-
* re-renders from pushed invalidations or the post-apply reload.
|
|
2443
|
-
*/
|
|
2444
|
-
/** Render an editor for either the setup posture or an expanded provider row. */
|
|
2445
|
-
function renderProviderEditor({ target, ...props }) {
|
|
2446
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderEditor, {
|
|
2447
|
-
provider: target.provider,
|
|
2448
|
-
displayName: target.displayName,
|
|
2449
|
-
settingsPath: target.settingsPath,
|
|
2450
|
-
...target.declared === true ? { declared: true } : {},
|
|
2451
|
-
...props
|
|
2452
|
-
});
|
|
2453
|
-
}
|
|
2206
|
+
//#region src/client/models-plus/ModelsPlusSection.tsx
|
|
2454
2207
|
/**
|
|
2455
|
-
*
|
|
2456
|
-
*
|
|
2457
|
-
*
|
|
2458
|
-
*
|
|
2459
|
-
*
|
|
2460
|
-
* @param operations - the page's Host operations.
|
|
2461
|
-
* @param controller - the page store to refresh.
|
|
2462
|
-
* @param target - the provider's settings address and optional managed credential.
|
|
2463
|
-
* @returns the failure message, or undefined once the write and reload landed.
|
|
2464
|
-
*/
|
|
2465
|
-
async function removeProviderProfile(operations, controller, target) {
|
|
2466
|
-
if (target.credentialRef !== void 0) {
|
|
2467
|
-
const credential = await operations.removeCredential(target.credentialRef);
|
|
2468
|
-
if (credential !== void 0) return credential;
|
|
2469
|
-
}
|
|
2470
|
-
const written = await operations.writeSettings(target.settingsNs, [{
|
|
2471
|
-
op: "unset",
|
|
2472
|
-
path: [...target.settingsPath]
|
|
2473
|
-
}], void 0);
|
|
2474
|
-
if (written.kind !== "written") return written.message;
|
|
2475
|
-
await controller.load();
|
|
2476
|
-
}
|
|
2477
|
-
/**
|
|
2478
|
-
* Whether a whole-section provider still needs its first key: an unconfigured
|
|
2479
|
-
* credential opens the setup card instead of showing a row. This is the
|
|
2480
|
-
* first-run posture alone — a user who can already reach some provider gets an
|
|
2481
|
-
* ordinary row with the missing-key dot, since nothing here is blocking them.
|
|
2482
|
-
* @param row - the joined provider row.
|
|
2483
|
-
* @param anyUsable - whether any joined row can already serve requests.
|
|
2484
|
-
* @returns whether to render the setup card.
|
|
2208
|
+
* The Models+ settings section (v1.0.0, fully plugin-owned UI): provider rows
|
|
2209
|
+
* joined from the configurable directory, settings namespaces, and credential
|
|
2210
|
+
* states, one editor card at a time. The title row carries the adapter badge
|
|
2211
|
+
* and the metadata download button — the only place models.dev data is ever
|
|
2212
|
+
* fetched, on an explicit user click.
|
|
2485
2213
|
*/
|
|
2214
|
+
/** One fact decides the first-run posture: whether any provider is usable. */
|
|
2486
2215
|
function needsSetup(row, anyUsable) {
|
|
2487
2216
|
if (anyUsable) return false;
|
|
2488
2217
|
if (row.entry.settingsPath.length > 0) return false;
|
|
@@ -2500,20 +2229,33 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2500
2229
|
...row.entry.declared === true ? { declared: true } : {}
|
|
2501
2230
|
};
|
|
2502
2231
|
}
|
|
2503
|
-
/**
|
|
2504
|
-
|
|
2505
|
-
|
|
2232
|
+
/**
|
|
2233
|
+
* Remove one user-added provider and its page-managed credential. Credential
|
|
2234
|
+
* removal comes first so a second-step failure leaves the row retryable.
|
|
2235
|
+
*/
|
|
2236
|
+
async function removeProviderProfile(operations, controller, target) {
|
|
2237
|
+
if (target.credentialRef !== void 0) {
|
|
2238
|
+
const credential = await operations.removeCredential(target.credentialRef);
|
|
2239
|
+
if (credential !== void 0) return credential;
|
|
2240
|
+
}
|
|
2241
|
+
const written = await operations.writeSettings(target.settingsNs, [{
|
|
2242
|
+
op: "unset",
|
|
2243
|
+
path: [...target.settingsPath]
|
|
2244
|
+
}], void 0);
|
|
2245
|
+
if (written.kind !== "written") return written.message;
|
|
2246
|
+
await controller.load();
|
|
2506
2247
|
}
|
|
2507
|
-
/** Replace the one provider placeholder in localized destructive
|
|
2248
|
+
/** Replace the one provider placeholder in localized destructive copy. */
|
|
2508
2249
|
function providerCopy(template, target) {
|
|
2509
|
-
|
|
2250
|
+
const label = target.provider === target.displayName ? target.provider : `${target.displayName} (${target.provider})`;
|
|
2251
|
+
return template.replace("{provider}", () => label);
|
|
2510
2252
|
}
|
|
2511
2253
|
/**
|
|
2512
|
-
* Render the Models section content column.
|
|
2254
|
+
* Render the Models+ section content column.
|
|
2513
2255
|
* @param props - slot-delivered injected dependencies.
|
|
2514
2256
|
* @returns the section, or null while the shell has not injected yet.
|
|
2515
2257
|
*/
|
|
2516
|
-
function
|
|
2258
|
+
function ModelsPlusSection(props) {
|
|
2517
2259
|
const { controller, useSnapshot, operations, schema, t } = props;
|
|
2518
2260
|
if (controller === void 0 || useSnapshot === void 0 || operations === void 0 || schema === void 0 || t === void 0) return null;
|
|
2519
2261
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Loaded, { injected: {
|
|
@@ -2526,15 +2268,18 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2526
2268
|
}
|
|
2527
2269
|
function Loaded({ injected }) {
|
|
2528
2270
|
const { controller, operations, schema, t } = injected;
|
|
2529
|
-
const
|
|
2271
|
+
const useSnapshot = injected.useSnapshot;
|
|
2272
|
+
const state = useSnapshot((snapshot) => snapshot);
|
|
2530
2273
|
const [editing, setEditing] = (0, react.useState)(void 0);
|
|
2531
2274
|
const [adding, setAdding] = (0, react.useState)(false);
|
|
2275
|
+
const [declaring, setDeclaring] = (0, react.useState)(false);
|
|
2532
2276
|
const [deleteTarget, setDeleteTarget] = (0, react.useState)(void 0);
|
|
2533
2277
|
const [deleting, setDeleting] = (0, react.useState)(false);
|
|
2534
2278
|
const [deleteFailure, setDeleteFailure] = (0, react.useState)(void 0);
|
|
2535
2279
|
const [savedTarget, setSavedTarget] = (0, react.useState)(void 0);
|
|
2536
|
-
const [declaring, setDeclaring] = (0, react.useState)(false);
|
|
2537
2280
|
const [dismissedSetup, setDismissedSetup] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
2281
|
+
const [metaBusy, setMetaBusy] = (0, react.useState)(false);
|
|
2282
|
+
const [metaMessage, setMetaMessage] = (0, react.useState)(void 0);
|
|
2538
2283
|
const announceSaved = (target) => {
|
|
2539
2284
|
controller.load().then(() => {
|
|
2540
2285
|
setSavedTarget(target);
|
|
@@ -2546,198 +2291,196 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2546
2291
|
setDeclaring(false);
|
|
2547
2292
|
if (changed) announceSaved(target);
|
|
2548
2293
|
};
|
|
2549
|
-
/**
|
|
2550
|
-
* Close a setup card, which owns none of the state above: the row-editor,
|
|
2551
|
-
* add, and declare cards each own one of those, so clearing them here would
|
|
2552
|
-
* discard a draft the user opened beside this card. Dismissal is this card's
|
|
2553
|
-
* own — the provider falls back to an ordinary row for the rest of the
|
|
2554
|
-
* session, and reopens through Edit.
|
|
2555
|
-
*/
|
|
2556
2294
|
const closeSetup = (changed, target) => {
|
|
2557
2295
|
setDismissedSetup((previous) => /* @__PURE__ */ new Set([...previous, target.provider]));
|
|
2558
2296
|
if (changed) announceSaved(target);
|
|
2559
2297
|
};
|
|
2560
|
-
const
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
setDeleteTarget(void 0);
|
|
2576
|
-
}).finally(() => {
|
|
2577
|
-
setDeleting(false);
|
|
2578
|
-
});
|
|
2298
|
+
const downloadMeta = async () => {
|
|
2299
|
+
setMetaBusy(true);
|
|
2300
|
+
setMetaMessage(void 0);
|
|
2301
|
+
try {
|
|
2302
|
+
const outcome = await downloadMetadata();
|
|
2303
|
+
setMetaMessage(outcome.ok ? {
|
|
2304
|
+
ok: true,
|
|
2305
|
+
text: "models.dev 元数据已下载并就绪。"
|
|
2306
|
+
} : {
|
|
2307
|
+
ok: false,
|
|
2308
|
+
text: `元数据下载失败:${outcome.message}`
|
|
2309
|
+
});
|
|
2310
|
+
} finally {
|
|
2311
|
+
setMetaBusy(false);
|
|
2312
|
+
}
|
|
2579
2313
|
};
|
|
2580
2314
|
if (state.status === "idle") controller.load();
|
|
2581
2315
|
if (state.status === "error") {
|
|
2582
|
-
/* v8 ignore next -- an error status always carries text; the fallback satisfies the nullable type */
|
|
2583
2316
|
const errorText = state.error ?? "";
|
|
2584
2317
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2585
|
-
className:
|
|
2318
|
+
className: models_plus_module_default["section"],
|
|
2586
2319
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2587
|
-
className:
|
|
2588
|
-
children:
|
|
2320
|
+
className: models_plus_module_default["error"],
|
|
2321
|
+
children: `加载提供方目录失败: ${errorText}`
|
|
2589
2322
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2590
2323
|
type: "button",
|
|
2591
|
-
className:
|
|
2324
|
+
className: models_plus_module_default["secondaryButton"],
|
|
2592
2325
|
onClick: () => {
|
|
2593
2326
|
controller.load();
|
|
2594
2327
|
},
|
|
2595
|
-
children:
|
|
2328
|
+
children: "重试"
|
|
2596
2329
|
})]
|
|
2597
2330
|
});
|
|
2598
2331
|
}
|
|
2599
2332
|
const savedRow = savedTarget === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === savedTarget.provider);
|
|
2600
2333
|
const savedIdentity = savedRow === void 0 ? savedTarget : {
|
|
2601
|
-
|
|
2602
|
-
|
|
2334
|
+
displayName: savedRow.entry.displayName,
|
|
2335
|
+
provider: savedRow.entry.provider
|
|
2603
2336
|
};
|
|
2604
2337
|
const anyUsable = state.rows.some(providerUsable);
|
|
2605
2338
|
const configured = state.rows.filter((row) => row.configured);
|
|
2606
2339
|
const addable = state.rows.filter((row) => !row.configured && row.entry.settingsNs !== "");
|
|
2607
2340
|
const addTarget = adding ? editing : void 0;
|
|
2608
2341
|
const addNamespace = addTarget === void 0 ? void 0 : state.namespaces.get(addTarget.settingsNs);
|
|
2609
|
-
const protocols =
|
|
2342
|
+
const protocols = (() => {
|
|
2343
|
+
return protocolChoices(state.namespaces.get("llm-pi-ai"), schema);
|
|
2344
|
+
})();
|
|
2345
|
+
const renderEditor = (target, namespace, onClose) => {
|
|
2346
|
+
if (namespace === void 0) return null;
|
|
2347
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderEditor, {
|
|
2348
|
+
provider: target.provider,
|
|
2349
|
+
displayName: target.displayName,
|
|
2350
|
+
settingsPath: target.settingsPath,
|
|
2351
|
+
...target.declared === true ? { declared: true } : {},
|
|
2352
|
+
namespace,
|
|
2353
|
+
schema,
|
|
2354
|
+
operations,
|
|
2355
|
+
readOnly: !state.writable,
|
|
2356
|
+
onClose
|
|
2357
|
+
});
|
|
2358
|
+
};
|
|
2610
2359
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2611
|
-
className:
|
|
2360
|
+
className: models_plus_module_default["section"],
|
|
2612
2361
|
children: [
|
|
2613
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
2614
|
-
className:
|
|
2615
|
-
children: [
|
|
2616
|
-
className:
|
|
2617
|
-
children:
|
|
2362
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2363
|
+
className: models_plus_module_default["titleRow"],
|
|
2364
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2365
|
+
className: models_plus_module_default["title"],
|
|
2366
|
+
children: t("nav")
|
|
2367
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2368
|
+
className: models_plus_module_default["rowActions"],
|
|
2369
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2370
|
+
type: "button",
|
|
2371
|
+
className: models_plus_module_default["linkButton"],
|
|
2372
|
+
disabled: metaBusy,
|
|
2373
|
+
title: "下载/更新 models.dev 元数据(供快速装入预填)",
|
|
2374
|
+
onClick: () => {
|
|
2375
|
+
downloadMeta();
|
|
2376
|
+
},
|
|
2377
|
+
children: metaBusy ? "正在下载元数据…" : "下载/更新元数据"
|
|
2378
|
+
})
|
|
2618
2379
|
})]
|
|
2619
2380
|
}),
|
|
2381
|
+
metaMessage === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2382
|
+
className: metaMessage.ok ? models_plus_module_default["savedNotice"] : models_plus_module_default["error"],
|
|
2383
|
+
children: metaMessage.text
|
|
2384
|
+
}),
|
|
2620
2385
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2621
|
-
className:
|
|
2386
|
+
className: models_plus_module_default["intro"],
|
|
2622
2387
|
children: t("intro")
|
|
2623
2388
|
}),
|
|
2624
|
-
!state.writable && state.status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2625
|
-
className: ModelsSection_ext_module_default["notice"],
|
|
2626
|
-
children: t("readOnly")
|
|
2627
|
-
}) : null,
|
|
2628
2389
|
savedIdentity === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2629
|
-
className:
|
|
2390
|
+
className: models_plus_module_default["savedNotice"],
|
|
2630
2391
|
role: "status",
|
|
2631
2392
|
"aria-live": "polite",
|
|
2632
|
-
children: providerCopy(
|
|
2393
|
+
children: providerCopy("已保存 {provider}。", savedIdentity)
|
|
2633
2394
|
}),
|
|
2634
2395
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
2635
|
-
className:
|
|
2396
|
+
className: models_plus_module_default["rows"],
|
|
2636
2397
|
children: configured.map((row) => {
|
|
2637
2398
|
const target = targetOf(row);
|
|
2638
2399
|
const namespace = state.namespaces.get(target.settingsNs);
|
|
2639
|
-
/* v8 ignore next -- the join marks a row configured only when its namespace resolved */
|
|
2640
2400
|
if (namespace === void 0) return null;
|
|
2641
2401
|
if (needsSetup(row, anyUsable) && !dismissedSetup.has(row.entry.provider)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
2642
|
-
className:
|
|
2643
|
-
children:
|
|
2644
|
-
target
|
|
2645
|
-
namespace,
|
|
2646
|
-
schema,
|
|
2647
|
-
operations,
|
|
2648
|
-
t,
|
|
2649
|
-
readOnly: !state.writable,
|
|
2650
|
-
onClose: (changed) => {
|
|
2651
|
-
closeSetup(changed, target);
|
|
2652
|
-
}
|
|
2402
|
+
className: models_plus_module_default["rowCard"],
|
|
2403
|
+
children: renderEditor(target, namespace, (changed) => {
|
|
2404
|
+
closeSetup(changed, target);
|
|
2653
2405
|
})
|
|
2654
2406
|
}, row.entry.provider);
|
|
2655
|
-
const open = !adding && editing?.provider === row.entry.provider;
|
|
2407
|
+
const open = !adding && !declaring && editing?.provider === row.entry.provider;
|
|
2656
2408
|
const credentialConfigured = row.credential?.configured === true;
|
|
2657
2409
|
const credentialMissing = !credentialConfigured && row.apiKeyEnv !== void 0 && row.credential?.configured === false;
|
|
2658
2410
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2659
|
-
className:
|
|
2411
|
+
className: models_plus_module_default["rowCard"],
|
|
2660
2412
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2661
|
-
className:
|
|
2413
|
+
className: models_plus_module_default["rowHead"],
|
|
2662
2414
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2663
|
-
className:
|
|
2415
|
+
className: models_plus_module_default["rowIdentity"],
|
|
2664
2416
|
children: [
|
|
2665
2417
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2666
|
-
className:
|
|
2418
|
+
className: models_plus_module_default["rowName"],
|
|
2667
2419
|
children: row.entry.displayName
|
|
2668
2420
|
}),
|
|
2669
2421
|
row.entry.declared === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2670
|
-
className:
|
|
2671
|
-
children:
|
|
2422
|
+
className: models_plus_module_default["rowTag"],
|
|
2423
|
+
children: "自定义"
|
|
2672
2424
|
}) : null,
|
|
2673
2425
|
credentialConfigured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2674
|
-
className: `${
|
|
2426
|
+
className: `${models_plus_module_default["credentialDot"]} ${models_plus_module_default["credentialDotConfigured"]}`,
|
|
2675
2427
|
role: "img",
|
|
2676
|
-
"aria-label":
|
|
2677
|
-
title:
|
|
2428
|
+
"aria-label": "API 密钥已配置",
|
|
2429
|
+
title: "API 密钥已配置"
|
|
2678
2430
|
}) : credentialMissing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2679
|
-
className: `${
|
|
2431
|
+
className: `${models_plus_module_default["credentialDot"]} ${models_plus_module_default["credentialDotMissing"]}`,
|
|
2680
2432
|
role: "img",
|
|
2681
|
-
"aria-label":
|
|
2682
|
-
title:
|
|
2433
|
+
"aria-label": "API 密钥缺失",
|
|
2434
|
+
title: "API 密钥缺失"
|
|
2683
2435
|
}) : null
|
|
2684
2436
|
]
|
|
2685
2437
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2686
|
-
className:
|
|
2438
|
+
className: models_plus_module_default["rowActions"],
|
|
2687
2439
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2688
2440
|
type: "button",
|
|
2689
|
-
className:
|
|
2690
|
-
"aria-label": providerCopy(
|
|
2441
|
+
className: models_plus_module_default["secondaryButton"],
|
|
2442
|
+
"aria-label": providerCopy("编辑 {provider}", target),
|
|
2691
2443
|
onClick: () => {
|
|
2692
2444
|
setSavedTarget(void 0);
|
|
2693
2445
|
setDeclaring(false);
|
|
2694
2446
|
setAdding(false);
|
|
2695
2447
|
setEditing(open ? void 0 : target);
|
|
2696
2448
|
},
|
|
2697
|
-
children:
|
|
2449
|
+
children: "编辑"
|
|
2698
2450
|
}), row.removable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2699
2451
|
type: "button",
|
|
2700
|
-
className:
|
|
2701
|
-
"aria-label": providerCopy(
|
|
2452
|
+
className: models_plus_module_default["dangerButton"],
|
|
2453
|
+
"aria-label": providerCopy("删除 {provider}", target),
|
|
2702
2454
|
disabled: !state.writable,
|
|
2703
2455
|
onClick: () => {
|
|
2704
2456
|
setSavedTarget(void 0);
|
|
2705
2457
|
setDeleteFailure(void 0);
|
|
2706
2458
|
setDeleteTarget(target);
|
|
2707
2459
|
},
|
|
2708
|
-
children:
|
|
2460
|
+
children: "删除"
|
|
2709
2461
|
}) : null]
|
|
2710
2462
|
})]
|
|
2711
|
-
}), open ?
|
|
2712
|
-
target
|
|
2713
|
-
namespace,
|
|
2714
|
-
schema,
|
|
2715
|
-
operations,
|
|
2716
|
-
t,
|
|
2717
|
-
readOnly: !state.writable,
|
|
2718
|
-
onClose: (changed) => {
|
|
2719
|
-
closeEditor(changed, target);
|
|
2720
|
-
}
|
|
2463
|
+
}), open ? renderEditor(target, namespace, (changed) => {
|
|
2464
|
+
closeEditor(changed, target);
|
|
2721
2465
|
}) : null]
|
|
2722
2466
|
}, row.entry.provider);
|
|
2723
2467
|
})
|
|
2724
2468
|
}),
|
|
2725
2469
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2726
|
-
className:
|
|
2470
|
+
className: models_plus_module_default["addBlock"],
|
|
2727
2471
|
children: addTarget !== void 0 && addNamespace !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2728
|
-
className:
|
|
2472
|
+
className: models_plus_module_default["addCard"],
|
|
2729
2473
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2730
|
-
className:
|
|
2731
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
2732
|
-
className:
|
|
2733
|
-
children:
|
|
2474
|
+
className: models_plus_module_default["field"],
|
|
2475
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2476
|
+
className: models_plus_module_default["fieldLabel"],
|
|
2477
|
+
children: "提供方"
|
|
2734
2478
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
2735
|
-
className: `${
|
|
2479
|
+
className: `${models_plus_module_default["input"]} ${models_plus_module_default["selectInput"]}`,
|
|
2736
2480
|
value: addTarget.provider,
|
|
2737
|
-
"aria-label":
|
|
2481
|
+
"aria-label": "提供方",
|
|
2738
2482
|
onChange: (event) => {
|
|
2739
2483
|
const row = addable.find((candidate) => candidate.entry.provider === event.target.value);
|
|
2740
|
-
/* v8 ignore next -- the select only lists addable rows */
|
|
2741
2484
|
if (row === void 0) return;
|
|
2742
2485
|
setEditing(targetOf(row));
|
|
2743
2486
|
},
|
|
@@ -2749,26 +2492,22 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2749
2492
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderEditor, {
|
|
2750
2493
|
provider: addTarget.provider,
|
|
2751
2494
|
displayName: addTarget.displayName,
|
|
2752
|
-
hideTitle: true,
|
|
2753
2495
|
namespace: addNamespace,
|
|
2754
2496
|
schema,
|
|
2755
2497
|
settingsPath: addTarget.settingsPath,
|
|
2756
2498
|
operations,
|
|
2757
|
-
t,
|
|
2758
2499
|
readOnly: !state.writable,
|
|
2759
2500
|
onClose: (changed) => {
|
|
2760
2501
|
closeEditor(changed, addTarget);
|
|
2761
2502
|
}
|
|
2762
2503
|
}, addTarget.provider)]
|
|
2763
2504
|
}) : declaring ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2764
|
-
className:
|
|
2765
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2505
|
+
className: models_plus_module_default["addCard"],
|
|
2506
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreateProviderCard, {
|
|
2766
2507
|
taken: state.rows.map((row) => row.entry.provider),
|
|
2767
2508
|
protocols,
|
|
2768
|
-
/* v8 ignore next -- the card only opens from a button disabled without this namespace */
|
|
2769
2509
|
revision: state.namespaces.get("llm-pi-ai")?.revision ?? 0,
|
|
2770
2510
|
operations,
|
|
2771
|
-
t,
|
|
2772
2511
|
readOnly: !state.writable,
|
|
2773
2512
|
onClose: (changed) => {
|
|
2774
2513
|
setDeclaring(false);
|
|
@@ -2776,24 +2515,35 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2776
2515
|
}
|
|
2777
2516
|
})
|
|
2778
2517
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2779
|
-
className:
|
|
2518
|
+
className: models_plus_module_default["addActions"],
|
|
2780
2519
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2781
2520
|
type: "button",
|
|
2782
|
-
className:
|
|
2521
|
+
className: models_plus_module_default["addButton"],
|
|
2783
2522
|
disabled: addable.length === 0 || !state.writable,
|
|
2784
2523
|
onClick: () => {
|
|
2785
2524
|
const first = addable[0];
|
|
2786
|
-
/* v8 ignore next -- the button is disabled while nothing is addable */
|
|
2787
2525
|
if (first === void 0) return;
|
|
2788
2526
|
setSavedTarget(void 0);
|
|
2789
2527
|
setDeclaring(false);
|
|
2790
2528
|
setAdding(true);
|
|
2791
2529
|
setEditing(targetOf(first));
|
|
2792
2530
|
},
|
|
2793
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2531
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
2532
|
+
width: "14",
|
|
2533
|
+
height: "14",
|
|
2534
|
+
viewBox: "0 0 16 16",
|
|
2535
|
+
fill: "none",
|
|
2536
|
+
"aria-hidden": "true",
|
|
2537
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
2538
|
+
d: "M8 3.5v9M3.5 8h9",
|
|
2539
|
+
stroke: "currentColor",
|
|
2540
|
+
strokeWidth: "1.5",
|
|
2541
|
+
strokeLinecap: "round"
|
|
2542
|
+
})
|
|
2543
|
+
}), "添加提供方"]
|
|
2794
2544
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2795
2545
|
type: "button",
|
|
2796
|
-
className:
|
|
2546
|
+
className: models_plus_module_default["addButton"],
|
|
2797
2547
|
disabled: protocols.length === 0 || !state.writable,
|
|
2798
2548
|
onClick: () => {
|
|
2799
2549
|
setSavedTarget(void 0);
|
|
@@ -2801,32 +2551,64 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2801
2551
|
setEditing(void 0);
|
|
2802
2552
|
setDeclaring(true);
|
|
2803
2553
|
},
|
|
2804
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2554
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
2555
|
+
width: "14",
|
|
2556
|
+
height: "14",
|
|
2557
|
+
viewBox: "0 0 16 16",
|
|
2558
|
+
fill: "none",
|
|
2559
|
+
"aria-hidden": "true",
|
|
2560
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
2561
|
+
d: "M8 3.5v9M3.5 8h9",
|
|
2562
|
+
stroke: "currentColor",
|
|
2563
|
+
strokeWidth: "1.5",
|
|
2564
|
+
strokeLinecap: "round"
|
|
2565
|
+
})
|
|
2566
|
+
}), "添加自定义提供方"]
|
|
2805
2567
|
})]
|
|
2806
2568
|
})
|
|
2807
2569
|
}),
|
|
2808
2570
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
2809
2571
|
open: deleteTarget !== void 0,
|
|
2810
|
-
onClose:
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2572
|
+
onClose: () => {
|
|
2573
|
+
if (deleting) return;
|
|
2574
|
+
setDeleteTarget(void 0);
|
|
2575
|
+
setDeleteFailure(void 0);
|
|
2576
|
+
},
|
|
2577
|
+
title: deleteTarget === void 0 ? "" : providerCopy("删除 {provider}?", deleteTarget),
|
|
2578
|
+
closeLabel: "关闭",
|
|
2579
|
+
description: deleteTarget === void 0 ? "" : providerCopy(deleteTarget.credentialRef === void 0 ? "删除 {provider} 会移除其配置;其使用的凭证(如有)由其他位置管理,将会保留。" : "删除 {provider} 会移除其配置和存储的 API 密钥。", deleteTarget),
|
|
2580
|
+
className: models_plus_module_default["deleteDialog"],
|
|
2581
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2582
|
+
type: "button",
|
|
2583
|
+
className: models_plus_module_default["secondaryButton"],
|
|
2818
2584
|
disabled: deleting,
|
|
2819
|
-
onClick:
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2585
|
+
onClick: () => {
|
|
2586
|
+
setDeleteTarget(void 0);
|
|
2587
|
+
setDeleteFailure(void 0);
|
|
2588
|
+
},
|
|
2589
|
+
children: "取消"
|
|
2590
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2591
|
+
type: "button",
|
|
2592
|
+
className: `${models_plus_module_default["secondaryButton"]} ${models_plus_module_default["deleteConfirm"]}`,
|
|
2824
2593
|
disabled: deleting,
|
|
2825
|
-
onClick:
|
|
2826
|
-
|
|
2594
|
+
onClick: () => {
|
|
2595
|
+
if (deleteTarget === void 0 || deleting) return;
|
|
2596
|
+
setDeleting(true);
|
|
2597
|
+
setDeleteFailure(void 0);
|
|
2598
|
+
removeProviderProfile(operations, controller, deleteTarget).then((removeFailure) => {
|
|
2599
|
+
if (removeFailure !== void 0) {
|
|
2600
|
+
setDeleteFailure(removeFailure);
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
setDeleteTarget(void 0);
|
|
2604
|
+
}).finally(() => {
|
|
2605
|
+
setDeleting(false);
|
|
2606
|
+
});
|
|
2607
|
+
},
|
|
2608
|
+
children: deleteTarget === void 0 ? "" : providerCopy(deleting ? "正在删除 {provider}…" : "删除 {provider}", deleteTarget)
|
|
2827
2609
|
})] }),
|
|
2828
2610
|
children: deleteFailure === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2829
|
-
className:
|
|
2611
|
+
className: models_plus_module_default["error"],
|
|
2830
2612
|
children: deleteFailure
|
|
2831
2613
|
})
|
|
2832
2614
|
})
|
|
@@ -2834,7 +2616,7 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2834
2616
|
});
|
|
2835
2617
|
}
|
|
2836
2618
|
//#endregion
|
|
2837
|
-
//#region
|
|
2619
|
+
//#region src/client/vendor/operations.ts
|
|
2838
2620
|
/**
|
|
2839
2621
|
* Bind the page's Host operations to the plugin's own Remote namespaces.
|
|
2840
2622
|
* @param ctx - the page plugin's context, which declares `remote.credentials`,
|
|
@@ -2883,7 +2665,7 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2883
2665
|
};
|
|
2884
2666
|
}
|
|
2885
2667
|
//#endregion
|
|
2886
|
-
//#region
|
|
2668
|
+
//#region src/client/vendor/schema-operations.ts
|
|
2887
2669
|
/**
|
|
2888
2670
|
* Hide the Cordis service identity behind bound schema callbacks.
|
|
2889
2671
|
* @param service - settings-owned schema service available in the apply context.
|
|
@@ -2904,7 +2686,7 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2904
2686
|
//#region src/client/index-extension.ts
|
|
2905
2687
|
/** Cordis service name (distinct from the npm package name). */
|
|
2906
2688
|
const name = "model-extension-client";
|
|
2907
|
-
/** Required services — mirrors the upstream Models section registration @ 0.1.2-
|
|
2689
|
+
/** Required services — mirrors the upstream Models section registration @ 0.1.2-rc.1. */
|
|
2908
2690
|
const inject = [
|
|
2909
2691
|
"slots",
|
|
2910
2692
|
"locale",
|
|
@@ -2960,7 +2742,7 @@ if (typeof document !== 'undefined' && document.querySelector('style[data-plugin
|
|
|
2960
2742
|
order: 11,
|
|
2961
2743
|
label: () => t("nav"),
|
|
2962
2744
|
inject: injected
|
|
2963
|
-
},
|
|
2745
|
+
}, ModelsPlusSection));
|
|
2964
2746
|
}
|
|
2965
2747
|
//#endregion
|
|
2966
2748
|
exports.apply = apply;
|