codex-overleaf-link 2.4.0 → 2.5.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 +272 -311
- package/README.zh-CN.md +287 -326
- package/extension/bootstrap/background.js +47 -10
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +10 -16
- package/extension/bootstrap/update.js +25 -2
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +28 -3
- package/extension/src/backgroundSharedSessions.js +380 -0
- package/extension/src/backgroundUpdateCoordinator.js +442 -379
- package/extension/src/content/activeTurnControl.js +8 -0
- package/extension/src/content/assetTransferBroker.js +186 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +24 -22
- package/extension/src/content/composerPanel.js +23 -8
- package/extension/src/content/contentRuntime.js +572 -571
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diagnosticsExport.js +222 -0
- package/extension/src/content/diagnosticsPanel.js +48 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/generated/content.bundle.js +8974 -1781
- package/extension/src/content/generated/content.bundle.meta.json +22 -3
- package/extension/src/content/legacyGlobalRegistry.js +19 -1
- package/extension/src/content/localSkillsPanel.js +42 -7
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/pageBridgeClient.js +18 -3
- package/extension/src/content/panelMaintenance.js +51 -120
- package/extension/src/content/panelRenderer.js +4 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/providerSettingsCoordinator.js +17 -6
- package/extension/src/content/providerSettingsDialog.js +661 -108
- package/extension/src/content/recentProjects.js +364 -356
- package/extension/src/content/runActivitySummary.js +631 -0
- package/extension/src/content/runController.js +3 -1
- package/extension/src/content/runFailureNotice.js +14 -1
- package/extension/src/content/runGuidanceController.js +4 -3
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +2 -1
- package/extension/src/content/runResultActions.js +123 -3
- package/extension/src/content/runTimelineView.js +194 -164
- package/extension/src/content/scopedPersistenceCoordinator.js +2 -1
- package/extension/src/content/scopedPersistencePanelState.js +45 -2
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sessionPersistence.js +10 -2
- package/extension/src/content/settingsPanel.js +93 -54
- package/extension/src/content/settingsWorkbench.js +374 -0
- package/extension/src/content/sharedSessionViewSync.js +72 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateNotice.js +121 -50
- package/extension/src/content/writebackController.js +257 -0
- package/extension/src/content/writebackOrchestrator.js +146 -141
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +576 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +97 -19
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/referenceProjects.js +148 -0
- package/extension/src/page/saveState.js +58 -3
- package/extension/src/page/snapshotRouter.js +44 -13
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +350 -59
- package/extension/src/page/trackedChangeCapture.js +42 -56
- package/extension/src/page/trackedChangeOwnership.js +198 -0
- package/extension/src/page/trackedChangeReplay.js +164 -0
- package/extension/src/page/trackedChangesLifecycle.js +139 -150
- package/extension/src/page/treeOperations.js +36 -29
- package/extension/src/page/writebackReceiptJournal.js +112 -0
- package/extension/src/page/writebackRouter.js +176 -136
- package/extension/src/pageBridge.js +59 -101
- package/extension/src/popup.js +203 -59
- package/extension/src/shared/agentTranscript.js +18 -4
- package/extension/src/shared/auditRecords.js +71 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +42 -0
- package/extension/src/shared/failureReasons.js +20 -0
- package/extension/src/shared/globalPreferences.js +1 -1
- package/extension/src/shared/i18n.js +187 -65
- package/extension/src/shared/managedUpdateProjection.js +6 -3
- package/extension/src/shared/pageRpcContract.js +7 -1
- package/extension/src/shared/providerProfiles.js +109 -0
- package/extension/src/shared/runActivityModel.js +361 -0
- package/extension/src/shared/runExecutionSnapshot.js +8 -2
- package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +28 -0
- package/extension/src/shared/sessionState.js +97 -6
- package/extension/src/shared/sharedSessionBridge.js +288 -0
- package/extension/src/shared/storageDb.js +23 -55
- package/extension/src/shared/storageMigration.js +3 -0
- package/extension/src/shared/storageRunActions.js +54 -4
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +87 -0
- package/extension/src/shared/updateConsent.js +37 -8
- package/extension/src/shared/writebackIntent.js +62 -0
- package/extension/src/shared/writebackSettlement.js +29 -10
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +2136 -744
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +2 -1
- package/native-host/src/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +2 -0
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +146 -177
- package/native-host/src/codexSessionTiming.js +71 -0
- package/native-host/src/managedInstall.js +5 -4
- package/native-host/src/nativeEnvironment.js +70 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +13 -1
- package/native-host/src/providerReasoning.js +9 -5
- package/native-host/src/providerStore.js +4 -0
- package/native-host/src/readProgressGuard.js +84 -17
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/selectionScope.js +58 -0
- package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
- package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
- package/native-host/src/subagentBroker.js +102 -98
- package/native-host/src/subagentTelemetry.js +316 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +31 -0
- package/native-host/src/taskRunnerRuntime.js +55 -5
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +122 -12
- package/native-host/src/updateNetworkControl.js +75 -0
- package/native-host/src/updateTrust.js +21 -2
- package/native-host/src/vendor/pdfjs/LICENSE +177 -0
- package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
- package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/native-host/src/writingStyleEvidence.js +102 -0
- package/native-host/src/writingStylePdfWorker.js +35 -0
- package/native-host/src/writingStyleProfile.js +165 -0
- package/native-host/src/writingStyleRuntime.js +447 -0
- package/package.json +1 -1
|
@@ -4,6 +4,44 @@
|
|
|
4
4
|
// Official OpenAI blossom mark used by the Codex VS Code extension.
|
|
5
5
|
const CODEX_BLOSSOM_PATH = 'M13.798 23.976a5.7 5.7 0 0 1-2.26-.456 6.1 6.1 0 0 1-1.903-1.27 5.7 5.7 0 0 1-1.88.311 5.75 5.75 0 0 1-2.95-.79 6.2 6.2 0 0 1-2.188-2.159q-.81-1.366-.809-3.045 0-.695.19-1.51a6.4 6.4 0 0 1-1.475-2.038A5.95 5.95 0 0 1 0 10.573Q0 9.278.547 8.08q.547-1.2 1.523-2.062a5.5 5.5 0 0 1 2.307-1.223A5.7 5.7 0 0 1 5.472 2.35 6.1 6.1 0 0 1 7.565.623 5.8 5.8 0 0 1 10.206 0q1.19 0 2.26.456a6.1 6.1 0 0 1 1.903 1.27 5.7 5.7 0 0 1 1.88-.311q1.594 0 2.95.79a6 6 0 0 1 2.165 2.159q.832 1.366.832 3.045 0 .695-.19 1.51a6.3 6.3 0 0 1 1.475 2.062q.523 1.15.523 2.422a5.9 5.9 0 0 1-.547 2.493q-.547 1.2-1.546 2.086a5.4 5.4 0 0 1-2.284 1.199 5.56 5.56 0 0 1-1.118 2.445 5.9 5.9 0 0 1-2.07 1.727 5.8 5.8 0 0 1-2.64.623m-5.876-2.997q1.19 0 2.07-.504l4.472-2.589a.53.53 0 0 0 .238-.455v-2.062L8.945 18.7a.96.96 0 0 1-1.047 0l-4.496-2.613a.7.7 0 0 1-.024.168v.287q0 1.224.571 2.254a4.24 4.24 0 0 0 1.642 1.583q1.047.6 2.331.599m.238-3.908a.6.6 0 0 0 .262.072q.118 0 .238-.072l1.784-1.031-5.734-3.357q-.522-.312-.523-.935V6.545a4.3 4.3 0 0 0-1.903 1.63 4.25 4.25 0 0 0-.714 2.398q0 1.176.595 2.254.594 1.08 1.546 1.63zm5.638 5.323q1.26 0 2.284-.576a4.3 4.3 0 0 0 1.618-1.582q.595-1.008.595-2.254v-5.179a.47.47 0 0 0-.238-.431l-1.808-1.055v6.689q0 .624-.524.935l-4.496 2.613a4.3 4.3 0 0 0 2.57.84m.904-8.776v-3.26l-2.688-1.535-2.712 1.535v3.26l2.712 1.535zM7.756 5.97q0-.623.523-.935l4.496-2.613a4.3 4.3 0 0 0-2.569-.84q-1.26 0-2.284.576A4.3 4.3 0 0 0 6.304 3.74q-.57 1.008-.57 2.254v5.155q0 .287.237.455l1.785 1.055zM19.84 17.43a4.16 4.16 0 0 0 1.88-1.63 4.33 4.33 0 0 0 .713-2.397q0-1.176-.595-2.254-.594-1.08-1.546-1.63l-4.449-2.59q-.143-.096-.261-.072a.46.46 0 0 0-.238.072L13.56 7.936l5.758 3.38a.9.9 0 0 1 .38.384q.143.216.143.528zM15.059 5.25q.524-.335 1.047 0l4.52 2.662V7.48q0-1.15-.57-2.181A4.14 4.14 0 0 0 18.46 3.62q-1.023-.623-2.379-.623-1.19 0-2.07.503L9.54 6.09a.53.53 0 0 0-.238.455v2.062z';
|
|
6
6
|
|
|
7
|
+
// Anthropic mark: https://github.com/simple-icons/simple-icons/blob/develop/icons/anthropic.svg
|
|
8
|
+
// Upstream SVG blob: c917480d32596242d674985ee622d9e7dd51f81a
|
|
9
|
+
const ANTHROPIC_LOGO_PATH = 'M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z';
|
|
10
|
+
|
|
11
|
+
/*!
|
|
12
|
+
* Provider SVG marks adapted from LobeHub Icons.
|
|
13
|
+
* kimi: https://github.com/lobehub/lobe-icons/blob/master/packages/static-svg/icons/kimi.svg (blob 1915850ee50ea636cc34b37aa11ba7f1ae0a26a2)
|
|
14
|
+
* glm: https://github.com/lobehub/lobe-icons/blob/master/packages/static-svg/icons/zhipu.svg (blob f7b12528e1ac82ef8b8b58825164bc88fa8b9e4b)
|
|
15
|
+
* deepseek: https://github.com/lobehub/lobe-icons/blob/master/packages/static-svg/icons/deepseek.svg (blob dc224e43a4d68070ca6eed494476c8ddd900bf80)
|
|
16
|
+
*
|
|
17
|
+
* MIT License
|
|
18
|
+
*
|
|
19
|
+
* Copyright (c) 2023 LobeHub
|
|
20
|
+
*
|
|
21
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
22
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
23
|
+
* in the Software without restriction, including without limitation the rights
|
|
24
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
25
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
26
|
+
* furnished to do so, subject to the following conditions:
|
|
27
|
+
*
|
|
28
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
29
|
+
* copies or substantial portions of the Software.
|
|
30
|
+
*
|
|
31
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
33
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
34
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
35
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
36
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
37
|
+
* SOFTWARE.
|
|
38
|
+
*/
|
|
39
|
+
const PROVIDER_VENDOR_ICON_PATHS = Object.freeze({
|
|
40
|
+
"kimi": "M21.846 0a1.923 1.923 0 110 3.846H20.15a.226.226 0 01-.227-.226V1.923C19.923.861 20.784 0 21.846 0z M11.065 11.199l7.257-7.2c.137-.136.06-.41-.116-.41H14.3a.164.164 0 00-.117.051l-7.82 7.756c-.122.12-.302.013-.302-.179V3.82c0-.127-.083-.23-.185-.23H3.186c-.103 0-.186.103-.186.23V19.77c0 .128.083.23.186.23h2.69c.103 0 .186-.102.186-.23v-3.25c0-.069.025-.135.069-.178l2.424-2.406a.158.158 0 01.205-.023l6.484 4.772a7.677 7.677 0 003.453 1.283c.108.012.2-.095.2-.23v-3.06c0-.117-.07-.212-.164-.227a5.028 5.028 0 01-2.027-.807l-5.613-4.064c-.117-.078-.132-.279-.028-.381z",
|
|
41
|
+
"glm": "M11.991 23.503a.24.24 0 00-.244.248.24.24 0 00.244.249.24.24 0 00.245-.249.24.24 0 00-.22-.247l-.025-.001zM9.671 5.365a1.697 1.697 0 011.099 2.132l-.071.172-.016.04-.018.054c-.07.16-.104.32-.104.498-.035.71.47 1.279 1.186 1.314h.366c1.309.053 2.338 1.173 2.286 2.523-.052 1.332-1.152 2.38-2.478 2.327h-.174c-.715.018-1.274.64-1.239 1.368 0 .124.018.23.053.337.209.373.54.658.96.8.75.23 1.517-.125 1.9-.782l.018-.035c.402-.64 1.17-.96 1.92-.711.854.284 1.378 1.226 1.099 2.167a1.661 1.661 0 01-2.077 1.102 1.711 1.711 0 01-.907-.711l-.017-.035c-.2-.323-.463-.58-.851-.711l-.056-.018a1.646 1.646 0 00-1.954.746 1.66 1.66 0 01-1.065.764 1.677 1.677 0 01-1.989-1.279c-.209-.906.332-1.83 1.257-2.043a1.51 1.51 0 01.296-.035h.018c.68-.071 1.151-.622 1.116-1.333a1.307 1.307 0 00-.227-.693 2.515 2.515 0 01-.366-1.403 2.39 2.39 0 01.366-1.208c.14-.195.21-.444.227-.693.018-.71-.506-1.261-1.186-1.332l-.07-.018a1.43 1.43 0 01-.299-.07l-.05-.019a1.7 1.7 0 01-1.047-2.114 1.68 1.68 0 012.094-1.101zm-5.575 10.11c.26-.264.639-.367.994-.27.355.096.633.379.728.74.095.362-.007.748-.267 1.013-.402.41-1.053.41-1.455 0a1.062 1.062 0 010-1.482zm14.845-.294c.359-.09.738.024.992.297.254.274.344.665.237 1.025-.107.36-.396.634-.756.718-.551.128-1.1-.22-1.23-.781a1.05 1.05 0 01.757-1.26zm-.064-4.39c.314.32.49.753.49 1.206 0 .452-.176.886-.49 1.206-.315.32-.74.5-1.185.5-.444 0-.87-.18-1.184-.5a1.727 1.727 0 010-2.412 1.654 1.654 0 012.369 0zm-11.243.163c.364.484.447 1.128.218 1.691a1.665 1.665 0 01-2.188.923c-.855-.36-1.26-1.358-.907-2.228a1.68 1.68 0 011.33-1.038c.593-.08 1.183.169 1.547.652zm11.545-4.221c.368 0 .708.2.892.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.892.524c-.568 0-1.03-.47-1.03-1.048 0-.579.462-1.048 1.03-1.048zm-14.358 0c.368 0 .707.2.891.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.891.524c-.569 0-1.03-.47-1.03-1.048 0-.579.461-1.048 1.03-1.048zm10.031-1.475c.925 0 1.675.764 1.675 1.706s-.75 1.705-1.675 1.705-1.674-.763-1.674-1.705c0-.942.75-1.706 1.674-1.706zm-2.626-.684c.362-.082.653-.356.761-.718a1.062 1.062 0 00-.238-1.028 1.017 1.017 0 00-.996-.294c-.547.14-.881.7-.752 1.257.13.558.675.907 1.225.783zm0 16.876c.359-.087.644-.36.75-.72a1.062 1.062 0 00-.237-1.019 1.018 1.018 0 00-.985-.301 1.037 1.037 0 00-.762.717c-.108.361-.017.754.239 1.028.245.263.606.377.953.305l.043-.01zM17.19 3.5a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64a.631.631 0 00-.628.64c0 .355.28.64.628.64zm-10.38 0a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64a.631.631 0 00-.628.64c0 .355.279.64.628.64zm-5.182 7.852a.631.631 0 00-.628.64c0 .354.28.639.628.639a.63.63 0 00.627-.606l.001-.034a.62.62 0 00-.628-.64zm5.182 9.13a.631.631 0 00-.628.64c0 .355.279.64.628.64a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm10.38.018a.631.631 0 00-.628.64c0 .355.28.64.628.64a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64zm5.182-9.148a.631.631 0 00-.628.64c0 .354.279.639.628.639a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm-.384-4.992a.24.24 0 00.244-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249c0 .142.122.249.244.249zM11.991.497a.24.24 0 00.245-.248A.24.24 0 0011.99 0a.24.24 0 00-.244.249c0 .133.108.236.223.247l.021.001zM2.011 6.36a.24.24 0 00.245-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249.24.24 0 00.244.249zm0 11.263a.24.24 0 00-.243.248.24.24 0 00.244.249.24.24 0 00.244-.249.252.252 0 00-.244-.248zm19.995-.018a.24.24 0 00-.245.248.24.24 0 00.245.25.24.24 0 00.244-.25.252.252 0 00-.244-.248z",
|
|
42
|
+
"deepseek": "M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"
|
|
43
|
+
});
|
|
44
|
+
|
|
7
45
|
function create(options = {}) {
|
|
8
46
|
const Profiles = options.ProviderProfiles;
|
|
9
47
|
if (!Profiles) {
|
|
@@ -23,11 +61,20 @@
|
|
|
23
61
|
verifications: {},
|
|
24
62
|
pendingCatalog: null,
|
|
25
63
|
root: null,
|
|
26
|
-
returnFocus: null
|
|
64
|
+
returnFocus: null,
|
|
65
|
+
providerFilter: '',
|
|
66
|
+
modelFilter: '',
|
|
67
|
+
templatePicker: false,
|
|
68
|
+
previousProviderId: 'builtin',
|
|
69
|
+
modelEditor: null,
|
|
70
|
+
verificationStale: false,
|
|
71
|
+
embedded: false,
|
|
72
|
+
embeddedContainer: null,
|
|
73
|
+
confirmation: null
|
|
27
74
|
};
|
|
28
75
|
ensureRoot(instance);
|
|
29
76
|
return {
|
|
30
|
-
open: catalog => open(instance, catalog),
|
|
77
|
+
open: (catalog, presentation) => open(instance, catalog, presentation),
|
|
31
78
|
close: () => requestClose(instance),
|
|
32
79
|
setCatalog: (catalog, selectedId) => setCatalog(instance, catalog, selectedId),
|
|
33
80
|
offerCatalog: (catalog, selectedId) => offerCatalog(instance, catalog, selectedId),
|
|
@@ -36,7 +83,7 @@
|
|
|
36
83
|
setVerification: verification => setVerification(instance, verification),
|
|
37
84
|
setVerificationFailure: failure => setVerificationFailure(instance, failure),
|
|
38
85
|
setTestProgress: progress => setTestProgress(instance, progress),
|
|
39
|
-
hasUnsavedChanges: () => instance.dirty,
|
|
86
|
+
hasUnsavedChanges: () => instance.dirty || Boolean(instance.modelEditor || instance.confirmation),
|
|
40
87
|
isOpen: () => Boolean(instance.root && !instance.root.hidden),
|
|
41
88
|
destroy: () => destroy(instance),
|
|
42
89
|
_instance: instance
|
|
@@ -48,7 +95,7 @@
|
|
|
48
95
|
return instance.root;
|
|
49
96
|
}
|
|
50
97
|
const root = instance.document.createElement('dialog');
|
|
51
|
-
root.className = 'codex-provider-dialog-root';
|
|
98
|
+
root.className = 'codex-provider-dialog-root codex-provider-v2';
|
|
52
99
|
root.setAttribute('aria-labelledby', 'codex-provider-dialog-title');
|
|
53
100
|
root.hidden = true;
|
|
54
101
|
root.innerHTML = `
|
|
@@ -62,6 +109,7 @@
|
|
|
62
109
|
</div>
|
|
63
110
|
<p data-provider-dialog-subtitle></p>
|
|
64
111
|
</div>
|
|
112
|
+
<button type="button" class="codex-provider-inline-button" data-provider-action="reload" hidden></button>
|
|
65
113
|
<button type="button" class="codex-provider-dialog-close" data-provider-action="close" aria-label="Close">×</button>
|
|
66
114
|
</header>
|
|
67
115
|
<div class="codex-provider-dialog-body">
|
|
@@ -74,6 +122,23 @@
|
|
|
74
122
|
</footer>
|
|
75
123
|
</section>
|
|
76
124
|
`;
|
|
125
|
+
const modelDialog = instance.document.createElement('dialog');
|
|
126
|
+
modelDialog.className = 'codex-provider-model-editor';
|
|
127
|
+
modelDialog.setAttribute('data-provider-model-editor', '');
|
|
128
|
+
modelDialog.setAttribute('aria-labelledby', 'codex-provider-model-title');
|
|
129
|
+
modelDialog.addEventListener('cancel', event => {
|
|
130
|
+
event.preventDefault(); event.stopPropagation(); closeModelEditor(instance);
|
|
131
|
+
});
|
|
132
|
+
root.appendChild(modelDialog);
|
|
133
|
+
const confirmation = instance.document.createElement('dialog');
|
|
134
|
+
confirmation.className = 'codex-provider-confirmation';
|
|
135
|
+
confirmation.setAttribute('data-provider-confirmation', '');
|
|
136
|
+
confirmation.setAttribute('aria-labelledby', 'codex-provider-confirm-title');
|
|
137
|
+
confirmation.setAttribute('aria-describedby', 'codex-provider-confirm-message');
|
|
138
|
+
confirmation.addEventListener('cancel', event => {
|
|
139
|
+
event.preventDefault(); event.stopPropagation(); finishProviderConfirmation(instance, false);
|
|
140
|
+
});
|
|
141
|
+
root.appendChild(confirmation);
|
|
77
142
|
instance.document.documentElement.appendChild(root);
|
|
78
143
|
instance.root = root;
|
|
79
144
|
root.addEventListener('click', event => handleClick(instance, event));
|
|
@@ -86,22 +151,38 @@
|
|
|
86
151
|
return root;
|
|
87
152
|
}
|
|
88
153
|
|
|
89
|
-
function open(instance, catalog) {
|
|
154
|
+
function open(instance, catalog, presentation = {}) {
|
|
155
|
+
const host = presentation.container || null;
|
|
156
|
+
const parent = host || instance.document.documentElement;
|
|
157
|
+
if (instance.root.parentElement !== parent) {
|
|
158
|
+
instance.root.close?.();
|
|
159
|
+
parent.appendChild(instance.root);
|
|
160
|
+
}
|
|
161
|
+
instance.embedded = Boolean(host);
|
|
162
|
+
instance.embeddedContainer = host;
|
|
163
|
+
instance.root.dataset.embedded = String(instance.embedded);
|
|
164
|
+
instance.root.setAttribute('role', host ? 'region' : 'dialog');
|
|
90
165
|
instance.returnFocus = instance.document.activeElement;
|
|
91
166
|
instance.root.hidden = false;
|
|
92
167
|
syncTheme(instance);
|
|
93
|
-
setCatalog(instance, catalog || instance.catalog);
|
|
94
|
-
if (!instance.root.open)
|
|
168
|
+
if (!presentation.preserveDraft) setCatalog(instance, catalog || instance.catalog);
|
|
169
|
+
if (!instance.root.open) {
|
|
170
|
+
if (host) instance.root.show?.();
|
|
171
|
+
else instance.root.showModal?.();
|
|
172
|
+
}
|
|
173
|
+
if (presentation.preserveDraft) return;
|
|
95
174
|
queueMicrotask(() => {
|
|
96
|
-
if (!instance.root || instance.root.hidden) return;
|
|
175
|
+
if (!instance.root || instance.root.hidden || instance.root.closest('[hidden]')) return;
|
|
97
176
|
const target = instance.root.querySelector('[data-provider-row][aria-current="true"]')
|
|
98
177
|
|| instance.root.querySelector('input, button, select, textarea');
|
|
99
178
|
target?.focus?.();
|
|
100
179
|
});
|
|
101
180
|
}
|
|
102
181
|
|
|
103
|
-
function requestClose(instance) {
|
|
104
|
-
if (instance.
|
|
182
|
+
async function requestClose(instance) {
|
|
183
|
+
if (instance.confirmation) { finishProviderConfirmation(instance, false); return false; }
|
|
184
|
+
if (instance.modelEditor) { closeModelEditor(instance); return false; }
|
|
185
|
+
if (instance.dirty && !await confirmProviderAction(instance, instance.tx(
|
|
105
186
|
'Discard unsaved provider changes?',
|
|
106
187
|
'放弃尚未保存的模型服务配置吗?'
|
|
107
188
|
))) {
|
|
@@ -111,6 +192,9 @@
|
|
|
111
192
|
instance.callbacks.onCancelTest?.();
|
|
112
193
|
instance.busy = '';
|
|
113
194
|
}
|
|
195
|
+
const secretInput = instance.root.querySelector('[data-provider-field="apiKey"]');
|
|
196
|
+
if (secretInput) secretInput.value = '';
|
|
197
|
+
instance.secretAction = 'unchanged';
|
|
114
198
|
instance.root.close?.();
|
|
115
199
|
instance.root.hidden = true;
|
|
116
200
|
instance.busy = '';
|
|
@@ -119,6 +203,7 @@
|
|
|
119
203
|
}
|
|
120
204
|
|
|
121
205
|
function setCatalog(instance, catalog, selectedId) {
|
|
206
|
+
finishProviderConfirmation(instance, false, false);
|
|
122
207
|
instance.catalog = instance.Profiles.normalizeCatalog(catalog || {});
|
|
123
208
|
const nextId = selectedId || (
|
|
124
209
|
instance.catalog.providers.some(provider => provider.id === instance.selectedId)
|
|
@@ -131,13 +216,17 @@
|
|
|
131
216
|
delete instance.root.dataset.dirty;
|
|
132
217
|
instance.secretAction = 'unchanged';
|
|
133
218
|
instance.verifications = {};
|
|
219
|
+
instance.verificationStale = false;
|
|
220
|
+
instance.templatePicker = false;
|
|
221
|
+
instance.modelFilter = '';
|
|
222
|
+
closeModelEditor(instance, false);
|
|
134
223
|
instance.pendingCatalog = null;
|
|
135
224
|
setStatus(instance, { tone: '', title: '' });
|
|
136
225
|
render(instance);
|
|
137
226
|
}
|
|
138
227
|
|
|
139
228
|
function offerCatalog(instance, catalog, selectedId) {
|
|
140
|
-
if (!instance.dirty) {
|
|
229
|
+
if (!instance.dirty && !instance.modelEditor && !instance.confirmation) {
|
|
141
230
|
setCatalog(instance, catalog, selectedId);
|
|
142
231
|
return true;
|
|
143
232
|
}
|
|
@@ -156,14 +245,19 @@
|
|
|
156
245
|
|
|
157
246
|
function render(instance) {
|
|
158
247
|
const tx = instance.tx;
|
|
159
|
-
instance.root.querySelector('[data-provider-
|
|
248
|
+
const reload = instance.root.querySelector('[data-provider-action="reload"]');
|
|
249
|
+
if (reload) {
|
|
250
|
+
reload.hidden = !instance.embedded;
|
|
251
|
+
reload.textContent = tx('Refresh', '刷新');
|
|
252
|
+
}
|
|
253
|
+
instance.root.querySelector('[data-provider-dialog-title]').textContent = tx('Models & providers', '模型与服务');
|
|
160
254
|
instance.root.querySelector('[data-provider-dialog-experimental]').textContent = tx(
|
|
161
255
|
'Third-party experimental',
|
|
162
256
|
'第三方实验性'
|
|
163
257
|
);
|
|
164
258
|
instance.root.querySelector('[data-provider-dialog-subtitle]').textContent = tx(
|
|
165
|
-
|
|
166
|
-
'
|
|
259
|
+
'Connect a provider, configure its models, and choose it for this project.',
|
|
260
|
+
'连接模型服务,管理模型,并选择当前项目使用的连接。'
|
|
167
261
|
);
|
|
168
262
|
if (instance.callbacks.hasCurrentProject?.() === false) instance.root.querySelector('[data-provider-dialog-subtitle]').textContent = tx(
|
|
169
263
|
'Manage shared model API profiles. Open a project to choose which provider its future turns use.',
|
|
@@ -175,40 +269,32 @@
|
|
|
175
269
|
}
|
|
176
270
|
|
|
177
271
|
function renderProviderList(instance) {
|
|
178
|
-
const tx = instance.tx;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
rows.push(`
|
|
200
|
-
<button type="button" class="codex-provider-row" data-provider-row="__new__" aria-current="true">
|
|
201
|
-
<span class="codex-provider-row-main">${escapeHtml(tx('New provider', '新模型服务'))}</span>
|
|
202
|
-
<span class="codex-provider-row-status" data-tone="muted">${escapeHtml(tx('Draft', '草稿'))}</span>
|
|
203
|
-
</button>
|
|
204
|
-
`);
|
|
205
|
-
}
|
|
206
|
-
rows.push(`<button type="button" class="codex-provider-add" data-provider-action="add">+ ${escapeHtml(tx('Add provider', '添加模型服务'))}</button>`);
|
|
207
|
-
list.innerHTML = rows.join('');
|
|
272
|
+
const tx = instance.tx, list = instance.root.querySelector('[data-provider-list]');
|
|
273
|
+
list.innerHTML = '<label class="codex-provider-search"><span class="codex-provider-sr-only">'
|
|
274
|
+
+ escapeHtml(tx('Search providers', '搜索服务')) + '</span><input type="search" data-provider-search value="'
|
|
275
|
+
+ escapeAttr(instance.providerFilter) + '" placeholder="' + escapeAttr(tx('Search providers', '搜索服务')) + '"></label>'
|
|
276
|
+
+ '<div class="codex-provider-nav-items">' + instance.catalog.providers.map(provider => {
|
|
277
|
+
const active = provider.id === getCurrentProjectProviderId(instance);
|
|
278
|
+
const subtitle = active ? tx('Current project', '当前项目') : provider.kind === 'builtin'
|
|
279
|
+
? tx('Local authentication', '本地身份验证')
|
|
280
|
+
: tx(provider.models.length + ' models', provider.models.length + ' 个模型');
|
|
281
|
+
const searchText = (provider.name + ' ' + (provider.baseUrl || '')).toLowerCase();
|
|
282
|
+
return '<button type="button" class="codex-provider-row" data-provider-row="' + escapeAttr(provider.id)
|
|
283
|
+
+ '" data-provider-search-text="' + escapeAttr(searchText)
|
|
284
|
+
+ '" aria-current="' + String(provider.id === instance.selectedId) + '"'
|
|
285
|
+
+ (searchText.includes(instance.providerFilter.toLowerCase()) ? '' : ' hidden') + '>'
|
|
286
|
+
+ '<span class="codex-provider-nav-mark" aria-hidden="true">'
|
|
287
|
+
+ (provider.kind === 'builtin' ? renderProviderTemplateIcon({ id: 'openai-compatible' }) : escapeHtml(provider.name.slice(0, 1).toUpperCase())) + '</span>'
|
|
288
|
+
+ '<span class="codex-provider-nav-copy"><span class="codex-provider-row-main" title="' + escapeAttr(provider.name) + '">' + escapeHtml(provider.name)
|
|
289
|
+
+ '</span><span class="codex-provider-row-status" data-tone="' + (active ? 'active' : 'muted') + '">'
|
|
290
|
+
+ escapeHtml(subtitle) + '</span></span></button>';
|
|
291
|
+
}).join('') + '</div><button type="button" class="codex-provider-add" data-provider-action="add">+ '
|
|
292
|
+
+ escapeHtml(tx('Add provider', '添加模型服务')) + '</button>';
|
|
208
293
|
}
|
|
209
294
|
|
|
210
295
|
function renderDetail(instance) {
|
|
211
296
|
const detail = instance.root.querySelector('[data-provider-detail]');
|
|
297
|
+
if (instance.templatePicker) { renderTemplatePicker(instance, detail); return; }
|
|
212
298
|
const provider = getSelectedProvider(instance);
|
|
213
299
|
if (!provider || provider.kind === 'builtin') {
|
|
214
300
|
renderBuiltinDetail(instance, detail);
|
|
@@ -216,11 +302,6 @@
|
|
|
216
302
|
}
|
|
217
303
|
const tx = instance.tx;
|
|
218
304
|
const draft = instance.draft || provider;
|
|
219
|
-
const defaultModel = (draft.models || []).find(model => model.id === draft.defaultModelId) || {};
|
|
220
|
-
const additionalModels = (draft.models || [])
|
|
221
|
-
.map(model => model.id)
|
|
222
|
-
.filter(id => id !== draft.defaultModelId)
|
|
223
|
-
.join('\n');
|
|
224
305
|
const active = provider.id && provider.id === getCurrentProjectProviderId(instance);
|
|
225
306
|
const acceptedHost = getEndpointHost(draft.baseUrl);
|
|
226
307
|
const secretSavedAt = formatSecretSavedAt(instance, provider.secretUpdatedAt);
|
|
@@ -246,11 +327,13 @@
|
|
|
246
327
|
<span>${escapeHtml(tx('Base URL', '基础 URL'))}</span>
|
|
247
328
|
<input type="url" data-provider-field="baseUrl" value="${escapeAttr(draft.baseUrl || '')}" placeholder="https://provider.example/v1" spellcheck="false">
|
|
248
329
|
<small>${escapeHtml(tx('HTTPS is required except for localhost.', '除 localhost 外必须使用 HTTPS。'))}</small>
|
|
330
|
+
<small data-provider-connection-hint hidden></small>
|
|
249
331
|
</label>
|
|
250
332
|
<label class="codex-provider-field codex-provider-field--wide">
|
|
251
333
|
<span>${escapeHtml(tx('API key', 'API 密钥'))}</span>
|
|
252
334
|
<div class="codex-provider-secret-row">
|
|
253
335
|
<input type="password" data-provider-field="apiKey" autocomplete="new-password" placeholder="${escapeAttr(provider.hasSecret ? tx('Configured; enter a new key to replace it', '已配置;输入新密钥即可替换') : tx('Optional for local no-auth endpoints', '本地无鉴权端点可留空'))}">
|
|
336
|
+
<button type="button" class="codex-provider-inline-button" data-provider-action="toggle-secret">${escapeHtml(tx('Show', '显示'))}</button>
|
|
254
337
|
${provider.hasSecret ? `<button type="button" class="codex-provider-inline-button" data-provider-action="clear-secret">${escapeHtml(tx('Clear', '清除'))}</button>` : ''}
|
|
255
338
|
</div>
|
|
256
339
|
<small data-provider-secret-note>${provider.hasSecret
|
|
@@ -259,16 +342,8 @@
|
|
|
259
342
|
: tx('Stored by the Native Host. The plaintext is never returned to the Extension.', '由 Native Host 保管。扩展不会读回密钥明文。'))}`
|
|
260
343
|
: escapeHtml(tx('No API key is currently stored. Local no-auth endpoints may leave this empty.', '当前未存储 API 密钥。本地无鉴权端点可以留空。'))}</small>
|
|
261
344
|
</label>
|
|
262
|
-
<label class="codex-provider-field codex-provider-field--wide">
|
|
263
|
-
<span>${escapeHtml(tx('Default model', '默认模型'))}</span>
|
|
264
|
-
<input type="text" data-provider-field="defaultModelId" value="${escapeAttr(draft.defaultModelId || '')}" placeholder="model-id" spellcheck="false">
|
|
265
|
-
</label>
|
|
266
|
-
<label class="codex-provider-field codex-provider-field--wide">
|
|
267
|
-
<span>${escapeHtml(tx('Additional models', '其他模型'))}</span>
|
|
268
|
-
<textarea data-provider-field="additionalModels" rows="3" placeholder="one-model-id-per-line" spellcheck="false">${escapeHtml(additionalModels)}</textarea>
|
|
269
|
-
<small>${escapeHtml(tx('Enter one model ID per line.', '每行填写一个模型 ID。'))}</small>
|
|
270
|
-
</label>
|
|
271
345
|
</div>
|
|
346
|
+
<section class="codex-provider-model-section" data-provider-model-section></section>
|
|
272
347
|
<details class="codex-provider-advanced">
|
|
273
348
|
<summary>${escapeHtml(tx('Advanced compatibility', '高级兼容设置'))}</summary>
|
|
274
349
|
<div class="codex-provider-form-grid">
|
|
@@ -281,15 +356,6 @@
|
|
|
281
356
|
<option value="anthropic" ${draft.wireApiPreference === 'anthropic' ? 'selected' : ''}>Anthropic Messages</option>
|
|
282
357
|
</select>
|
|
283
358
|
</label>
|
|
284
|
-
<label class="codex-provider-field">
|
|
285
|
-
<span>${escapeHtml(tx('Upstream response mode', '上游响应模式'))}</span>
|
|
286
|
-
<select data-provider-field="upstreamResponseMode">
|
|
287
|
-
<option value="auto" ${defaultModel.upstreamResponseMode === 'auto' || !defaultModel.upstreamResponseMode ? 'selected' : ''}>${escapeHtml(tx('Auto (test streaming, then buffered)', '自动(先测流式,再测缓冲)'))}</option>
|
|
288
|
-
<option value="streaming" ${defaultModel.upstreamResponseMode === 'streaming' ? 'selected' : ''}>${escapeHtml(tx('Streaming', '流式'))}</option>
|
|
289
|
-
<option value="buffered" ${defaultModel.upstreamResponseMode === 'buffered' ? 'selected' : ''}>${escapeHtml(tx('Buffered', '缓冲'))}</option>
|
|
290
|
-
</select>
|
|
291
|
-
<small>${escapeHtml(tx('Applied to the default model. Buffered keeps Codex streaming locally while the provider returns one complete response.', '应用于默认模型。缓冲模式仍向 Codex 本地流式输出,但等待服务商返回完整响应。'))}</small>
|
|
292
|
-
</label>
|
|
293
359
|
<label class="codex-provider-field">
|
|
294
360
|
<span>${escapeHtml(tx('Request timeout', '请求超时'))}</span>
|
|
295
361
|
<select data-provider-field="requestTimeoutMs">
|
|
@@ -311,7 +377,7 @@
|
|
|
311
377
|
<input type="text" data-provider-field="apiKeyHeaderName" value="${escapeAttr(draft.apiKeyHeaderName || '')}" placeholder="X-API-Key" spellcheck="false">
|
|
312
378
|
</label>
|
|
313
379
|
<label class="codex-provider-field">
|
|
314
|
-
<span>${escapeHtml(tx('
|
|
380
|
+
<span>${escapeHtml(tx('Default context for new models', '新模型的默认上下文'))}</span>
|
|
315
381
|
<input type="number" data-provider-field="contextWindow" min="8192" max="4000000" step="1024" value="${Number(draft.contextWindow) || 262144}">
|
|
316
382
|
</label>
|
|
317
383
|
<label class="codex-provider-field">
|
|
@@ -450,6 +516,14 @@
|
|
|
450
516
|
<span class="codex-provider-test-state" data-provider-test-state>${escapeHtml(formatVerification(instance, provider, draft.defaultModelId))}</span>
|
|
451
517
|
</div>
|
|
452
518
|
`;
|
|
519
|
+
const protocol = detail.querySelector('[data-provider-field="wireApiPreference"]')?.closest('label');
|
|
520
|
+
const connection = detail.querySelector('.codex-provider-form-grid');
|
|
521
|
+
if (protocol && connection) { protocol.classList.add('codex-provider-field--wide'); connection.prepend(protocol); }
|
|
522
|
+
const name = detail.querySelector('[data-provider-field="name"]')?.closest('label');
|
|
523
|
+
const heading = detail.querySelector('.codex-provider-detail-titleline h3');
|
|
524
|
+
if (name && heading) { name.classList.add('codex-provider-name-field'); heading.replaceWith(name); }
|
|
525
|
+
updatePresetConnectionHint(instance);
|
|
526
|
+
renderModelList(instance);
|
|
453
527
|
}
|
|
454
528
|
|
|
455
529
|
function renderBuiltinDetail(instance, detail) {
|
|
@@ -484,8 +558,8 @@
|
|
|
484
558
|
const actions = instance.root.querySelector('[data-provider-footer-actions]');
|
|
485
559
|
const provider = getSelectedProvider(instance);
|
|
486
560
|
const tx = instance.tx;
|
|
487
|
-
if (!provider || provider.kind === 'builtin') {
|
|
488
|
-
actions.innerHTML = `<button type="button" class="codex-provider-secondary-button" data-provider-action="close">${escapeHtml(tx('Close', '关闭'))}</button>`;
|
|
561
|
+
if (instance.templatePicker || !provider || provider.kind === 'builtin') {
|
|
562
|
+
actions.innerHTML = instance.embedded ? '' : `<button type="button" class="codex-provider-secondary-button" data-provider-action="close">${escapeHtml(tx('Close', '关闭'))}</button>`;
|
|
489
563
|
return;
|
|
490
564
|
}
|
|
491
565
|
const active = provider.id && provider.id === getCurrentProjectProviderId(instance);
|
|
@@ -506,7 +580,7 @@
|
|
|
506
580
|
${provider.id ? `<button type="button" class="codex-provider-danger-button" data-provider-action="delete">${escapeHtml(tx('Delete', '删除'))}</button>` : ''}
|
|
507
581
|
${instance.pendingCatalog ? `<button type="button" class="codex-provider-secondary-button" data-provider-action="reload-external">${escapeHtml(tx('Reload latest', '加载最新配置'))}</button>` : ''}
|
|
508
582
|
<span class="codex-provider-footer-spacer"></span>
|
|
509
|
-
|
|
583
|
+
${!instance.embedded || instance.dirty ? `<button type="button" class="codex-provider-secondary-button" data-provider-action="close">${escapeHtml(instance.embedded ? tx('Discard changes', '放弃更改') : tx('Cancel', '取消'))}</button>` : ''}
|
|
510
584
|
${actionState.showSave ? `<button type="button" class="codex-provider-secondary-button" data-provider-action="save"${saveDisabled}>${escapeHtml(tx('Save', '保存'))}</button>` : ''}
|
|
511
585
|
${actionState.showSaveAndUse ? `<button type="button" class="codex-provider-primary-button" data-provider-action="save-use"${saveDisabled}>${escapeHtml(tx('Save and use for this project', '保存并用于当前项目'))}</button>` : ''}
|
|
512
586
|
${actionState.showUse ? `<button type="button" class="codex-provider-primary-button" data-provider-action="use"${useDisabled}>${escapeHtml(tx('Use for this project', '用于当前项目'))}</button>` : ''}
|
|
@@ -526,7 +600,32 @@
|
|
|
526
600
|
};
|
|
527
601
|
}
|
|
528
602
|
|
|
529
|
-
function handleClick(instance, event) {
|
|
603
|
+
async function handleClick(instance, event) {
|
|
604
|
+
const answer = event.target.closest('[data-provider-confirm-answer]');
|
|
605
|
+
if (answer) {
|
|
606
|
+
event.preventDefault();
|
|
607
|
+
finishProviderConfirmation(instance, answer.dataset.providerConfirmAnswer === 'confirm');
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (instance.confirmation) return;
|
|
611
|
+
const template = event.target.closest('[data-provider-template]');
|
|
612
|
+
if (template && (!instance.busy || instance.busy === 'failed')) {
|
|
613
|
+
instance.draft = instance.Profiles.buildPresetDraft(template.dataset.providerTemplate);
|
|
614
|
+
instance.draft.name = providerTemplateTitle(instance, template.dataset.providerTemplate);
|
|
615
|
+
instance.templatePicker = false;
|
|
616
|
+
instance.dirty = true;
|
|
617
|
+
instance.root.dataset.dirty = 'true';
|
|
618
|
+
instance.secretAction = 'unchanged';
|
|
619
|
+
instance.verificationStale = true;
|
|
620
|
+
render(instance);
|
|
621
|
+
instance.root.querySelector('[data-provider-field="apiKey"]')?.focus();
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const modelAction = event.target.closest('[data-provider-model-action]');
|
|
625
|
+
if (modelAction) {
|
|
626
|
+
if (!instance.busy || instance.busy === 'failed') handleModelAction(instance, modelAction.dataset.providerModelAction, modelAction.dataset.modelId || '');
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
530
629
|
const action = event.target.closest('[data-provider-action]')?.dataset.providerAction;
|
|
531
630
|
const providerRow = event.target.closest('[data-provider-row]');
|
|
532
631
|
if (providerRow) {
|
|
@@ -538,16 +637,41 @@
|
|
|
538
637
|
}
|
|
539
638
|
event.preventDefault();
|
|
540
639
|
if (action === 'close') {
|
|
541
|
-
|
|
640
|
+
if (instance.embedded) {
|
|
641
|
+
const host = instance.embeddedContainer;
|
|
642
|
+
const catalog = instance.pendingCatalog?.catalog || instance.catalog;
|
|
643
|
+
if (await requestClose(instance) && host?.isConnected) open(instance, catalog, { container: host });
|
|
644
|
+
} else requestClose(instance);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (action === 'reload') {
|
|
648
|
+
if (!instance.busy || instance.busy === 'failed') instance.callbacks.onReload?.();
|
|
542
649
|
return;
|
|
543
650
|
}
|
|
544
651
|
if (action === 'add') {
|
|
545
|
-
|
|
652
|
+
if (instance.selectedId === '__new__') {
|
|
653
|
+
if (instance.dirty && !await confirmProviderAction(instance, instance.tx('Discard unsaved provider changes?', '放弃尚未保存的模型服务配置吗?'))) return;
|
|
654
|
+
instance.draft = instance.Profiles.buildEmptyDraft();
|
|
655
|
+
instance.dirty = false;
|
|
656
|
+
delete instance.root.dataset.dirty;
|
|
657
|
+
instance.templatePicker = true;
|
|
658
|
+
render(instance);
|
|
659
|
+
} else selectProvider(instance, '__new__');
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (action === 'templates-back') {
|
|
663
|
+
selectProvider(instance, instance.previousProviderId || 'builtin');
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (action === 'toggle-secret') {
|
|
667
|
+
const input = instance.root.querySelector('[data-provider-field="apiKey"]');
|
|
668
|
+
if (input) input.type = input.type === 'password' ? 'text' : 'password';
|
|
669
|
+
event.target.closest('button').textContent = instance.tx(input?.type === 'text' ? 'Hide' : 'Show', input?.type === 'text' ? '隐藏' : '显示');
|
|
546
670
|
return;
|
|
547
671
|
}
|
|
548
672
|
if (action === 'reload-external') {
|
|
549
673
|
if (!instance.pendingCatalog) return;
|
|
550
|
-
if (instance.dirty && !
|
|
674
|
+
if (instance.dirty && !await confirmProviderAction(instance, instance.tx(
|
|
551
675
|
'Discard this draft and load the latest provider settings?',
|
|
552
676
|
'放弃当前草稿并加载最新模型服务配置吗?'
|
|
553
677
|
))) {
|
|
@@ -566,7 +690,7 @@
|
|
|
566
690
|
'立即删除已存储的 API 密钥吗?重新加载已保存配置时,未保存的表单改动会被放弃。'
|
|
567
691
|
)
|
|
568
692
|
: instance.tx('Remove the stored API key now?', '立即删除已存储的 API 密钥吗?');
|
|
569
|
-
if (instance
|
|
693
|
+
if (await confirmProviderAction(instance, warning, 'secret')) {
|
|
570
694
|
instance.callbacks.onClearSecret?.({
|
|
571
695
|
profileId: provider.id,
|
|
572
696
|
expectedRevision: provider.revision
|
|
@@ -588,15 +712,16 @@
|
|
|
588
712
|
}
|
|
589
713
|
if (action === 'delete') {
|
|
590
714
|
const context = readContext(instance);
|
|
591
|
-
if (
|
|
715
|
+
if (await confirmProviderAction(instance, instance.tx(
|
|
592
716
|
`Delete ${context.draft.name}?`,
|
|
593
717
|
`删除 ${context.draft.name} 吗?`
|
|
594
|
-
))) {
|
|
718
|
+
), 'provider')) {
|
|
595
719
|
instance.callbacks.onDelete?.(context);
|
|
596
720
|
}
|
|
597
721
|
return;
|
|
598
722
|
}
|
|
599
723
|
if (action === 'test') {
|
|
724
|
+
if (!validateConnectionForm(instance)) return;
|
|
600
725
|
const modelId = getSelectedTestModelId(instance);
|
|
601
726
|
invalidateVerification(instance, false, modelId);
|
|
602
727
|
instance.callbacks.onTest?.(readContext(instance));
|
|
@@ -628,6 +753,7 @@
|
|
|
628
753
|
return;
|
|
629
754
|
}
|
|
630
755
|
if (action === 'save' || action === 'save-use') {
|
|
756
|
+
if (!validateConnectionForm(instance)) return;
|
|
631
757
|
const context = readContext(instance);
|
|
632
758
|
if (action === 'save-use' && !instance.root.querySelector('[data-provider-disclosure]')?.checked) {
|
|
633
759
|
setStatus(instance, {
|
|
@@ -641,6 +767,21 @@
|
|
|
641
767
|
}
|
|
642
768
|
|
|
643
769
|
function handleInput(instance, event) {
|
|
770
|
+
if (instance.modelEditor && event.target.closest('[data-provider-model-editor]')) {
|
|
771
|
+
updateModelReasoningSummary(instance);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (event.target.matches('[data-provider-search]')) {
|
|
775
|
+
instance.providerFilter = event.target.value;
|
|
776
|
+
for (const row of instance.root.querySelectorAll('[data-provider-search-text]'))
|
|
777
|
+
row.hidden = !row.dataset.providerSearchText.includes(instance.providerFilter.toLowerCase());
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
if (event.target.matches('[data-provider-model-search]')) {
|
|
781
|
+
instance.modelFilter = event.target.value;
|
|
782
|
+
filterModelRows(instance);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
644
785
|
if (event.target.matches('[data-provider-test-model]')) {
|
|
645
786
|
refreshTestState(instance);
|
|
646
787
|
return;
|
|
@@ -650,13 +791,16 @@
|
|
|
650
791
|
if (event.target.dataset.providerField === 'apiKey') {
|
|
651
792
|
instance.secretAction = event.target.value ? 'replace' : (getSelectedProvider(instance)?.hasSecret ? 'unchanged' : 'clear');
|
|
652
793
|
}
|
|
653
|
-
|
|
794
|
+
const destinationChanged = field === 'baseUrl'
|
|
795
|
+
|| (field === 'wireApiPreference' && syncPresetProtocol(instance, event.target.value));
|
|
796
|
+
if (destinationChanged) {
|
|
654
797
|
const disclosure = instance.root.querySelector('[data-provider-disclosure]');
|
|
655
798
|
if (disclosure) disclosure.checked = false;
|
|
656
799
|
updateDisclosureText(instance);
|
|
657
800
|
}
|
|
658
801
|
markDirty(instance, { invalidateVerification: field !== 'name' });
|
|
659
|
-
if (field === '
|
|
802
|
+
if (field === 'wireApiPreference') updatePresetConnectionHint(instance);
|
|
803
|
+
if (destinationChanged) {
|
|
660
804
|
renderFooter(instance);
|
|
661
805
|
applyBusyState(instance);
|
|
662
806
|
}
|
|
@@ -664,6 +808,24 @@
|
|
|
664
808
|
}
|
|
665
809
|
|
|
666
810
|
function handleKeydown(instance, event) {
|
|
811
|
+
if (instance.confirmation) {
|
|
812
|
+
if (event.key === 'Escape') {
|
|
813
|
+
event.preventDefault(); event.stopPropagation();
|
|
814
|
+
finishProviderConfirmation(instance, false);
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
if (instance.modelEditor) {
|
|
819
|
+
if (event.key === 'Escape') {
|
|
820
|
+
event.preventDefault(); event.stopPropagation(); closeModelEditor(instance);
|
|
821
|
+
} else if (event.key === 'Enter' && !event.isComposing
|
|
822
|
+
&& event.target.matches('input:not([type="checkbox"])')) {
|
|
823
|
+
event.preventDefault(); commitModelEditor(instance);
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
// The outer settings dialog owns focus traversal and Escape when embedded.
|
|
828
|
+
if (instance.embedded) return;
|
|
667
829
|
if (event.key === 'Escape') {
|
|
668
830
|
event.preventDefault();
|
|
669
831
|
requestClose(instance);
|
|
@@ -688,16 +850,20 @@
|
|
|
688
850
|
}
|
|
689
851
|
}
|
|
690
852
|
|
|
691
|
-
function selectProvider(instance, id) {
|
|
853
|
+
async function selectProvider(instance, id) {
|
|
692
854
|
if (id === instance.selectedId) {
|
|
693
855
|
return;
|
|
694
856
|
}
|
|
695
|
-
if (instance.dirty && !
|
|
857
|
+
if (instance.dirty && !await confirmProviderAction(instance, instance.tx(
|
|
696
858
|
'Discard unsaved provider changes?',
|
|
697
859
|
'放弃尚未保存的模型服务配置吗?'
|
|
698
860
|
))) {
|
|
699
861
|
return;
|
|
700
862
|
}
|
|
863
|
+
if (id === '__new__') instance.previousProviderId = instance.selectedId;
|
|
864
|
+
instance.templatePicker = id === '__new__';
|
|
865
|
+
instance.modelFilter = '';
|
|
866
|
+
instance.verificationStale = false;
|
|
701
867
|
instance.selectedId = id;
|
|
702
868
|
instance.draft = id === '__new__'
|
|
703
869
|
? instance.Profiles.buildEmptyDraft()
|
|
@@ -714,32 +880,16 @@
|
|
|
714
880
|
function readContext(instance) {
|
|
715
881
|
const provider = getSelectedProvider(instance) || instance.Profiles.buildEmptyDraft();
|
|
716
882
|
const get = name => instance.root.querySelector(`[data-provider-field="${name}"]`);
|
|
717
|
-
const
|
|
718
|
-
const
|
|
719
|
-
.map(value => value.trim())
|
|
720
|
-
.filter((value, index, values) => value && values.indexOf(value) === index);
|
|
721
|
-
const existingModels = new Map((provider.models || []).map(model => [model.id, model]));
|
|
722
|
-
const upstreamResponseMode = get('upstreamResponseMode')?.value || 'auto';
|
|
883
|
+
const modelDraft = instance.draft || provider;
|
|
884
|
+
const defaultModelId = modelDraft.defaultModelId || '';
|
|
723
885
|
const draft = {
|
|
724
886
|
name: String(get('name')?.value || '').trim(),
|
|
725
887
|
baseUrl: String(get('baseUrl')?.value || '').trim(),
|
|
726
888
|
wireApiPreference: get('wireApiPreference')?.value || 'auto',
|
|
727
|
-
models:
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
id,
|
|
732
|
-
label: existing.label || id,
|
|
733
|
-
reasoningEfforts: Array.isArray(existing.reasoningEfforts) ? existing.reasoningEfforts : [],
|
|
734
|
-
upstreamResponseMode: id === defaultModelId
|
|
735
|
-
? upstreamResponseMode
|
|
736
|
-
: existing.upstreamResponseMode || 'auto',
|
|
737
|
-
resolvedUpstreamResponseMode: existing.resolvedUpstreamResponseMode || '',
|
|
738
|
-
contextWindow: Number(get('contextWindow')?.value || 262144),
|
|
739
|
-
supportsParallelToolCalls: Boolean(get('supportsParallelToolCalls')?.checked),
|
|
740
|
-
inputModalities: String(get('inputModalities')?.value || 'text').split(',')
|
|
741
|
-
};
|
|
742
|
-
}),
|
|
889
|
+
models: (modelDraft.models || []).map(model => ({
|
|
890
|
+
...model, reasoningEfforts: [...(model.reasoningEfforts || [])],
|
|
891
|
+
inputModalities: [...(model.inputModalities || ['text'])]
|
|
892
|
+
})),
|
|
743
893
|
defaultModelId,
|
|
744
894
|
requestTimeoutMs: Number(get('requestTimeoutMs')?.value || 30000),
|
|
745
895
|
reasoningAdapter: get('reasoningAdapter')?.value || 'auto',
|
|
@@ -794,7 +944,9 @@
|
|
|
794
944
|
instance.root.dataset.dirty = 'true';
|
|
795
945
|
if (options.invalidateVerification !== false) {
|
|
796
946
|
instance.verifications = {};
|
|
947
|
+
instance.verificationStale = true;
|
|
797
948
|
invalidateVerification(instance, true);
|
|
949
|
+
refreshModelStatuses(instance);
|
|
798
950
|
}
|
|
799
951
|
if (footerNeedsRefresh) {
|
|
800
952
|
// Saved providers initially render Use (inactive) or no primary action
|
|
@@ -813,6 +965,7 @@
|
|
|
813
965
|
setStatus(instance, { tone: '', title: '' });
|
|
814
966
|
}
|
|
815
967
|
syncTestAction(instance);
|
|
968
|
+
refreshModelStatuses(instance);
|
|
816
969
|
applyBusyState(instance);
|
|
817
970
|
}
|
|
818
971
|
|
|
@@ -822,7 +975,9 @@
|
|
|
822
975
|
const canActivate = canActivateCurrentProvider(instance);
|
|
823
976
|
instance.root.dataset.busy = instance.busy || '';
|
|
824
977
|
for (const element of instance.root.querySelectorAll('input, select, textarea, button')) {
|
|
825
|
-
if (element.matches('[data-provider-
|
|
978
|
+
if (element.matches('[data-provider-confirm-answer]')) {
|
|
979
|
+
element.disabled = false;
|
|
980
|
+
} else if (element.matches('[data-provider-action="close"]')) {
|
|
826
981
|
element.disabled = false;
|
|
827
982
|
} else if (element.matches('[data-provider-action="cancel-test"]')) {
|
|
828
983
|
element.disabled = false;
|
|
@@ -900,7 +1055,9 @@
|
|
|
900
1055
|
|
|
901
1056
|
function canSaveCurrentDraft(instance) {
|
|
902
1057
|
const provider = getSelectedProvider(instance);
|
|
903
|
-
|
|
1058
|
+
const draft = instance.draft || provider;
|
|
1059
|
+
return Boolean(!instance.templatePicker && !instance.modelEditor && provider?.kind === 'custom'
|
|
1060
|
+
&& draft?.models?.length && draft.models.some(model => model.id === draft.defaultModelId));
|
|
904
1061
|
}
|
|
905
1062
|
|
|
906
1063
|
function canActivateCurrentProvider(instance) {
|
|
@@ -914,11 +1071,12 @@
|
|
|
914
1071
|
function formatVerification(instance, provider, modelId) {
|
|
915
1072
|
const transient = instance.verifications[modelId];
|
|
916
1073
|
if (transient?.status === 'failed') {
|
|
917
|
-
return instance.tx('Failed in this dialog', '本次测试失败');
|
|
1074
|
+
return instance.tx('Failed in this dialog', '本次测试失败') + (transient.errorCode ? ' · ' + transient.errorCode : '');
|
|
918
1075
|
}
|
|
919
1076
|
if (transient?.status === 'tested') {
|
|
920
1077
|
return instance.tx('Tested for this draft', '当前草稿已测试');
|
|
921
1078
|
}
|
|
1079
|
+
if (instance.verificationStale) return instance.tx('Settings changed; retest when ready', '配置已更改,可重新测试');
|
|
922
1080
|
const diagnostic = provider?.modelDiagnostics?.[modelId];
|
|
923
1081
|
if (diagnostic?.status === 'tested') {
|
|
924
1082
|
const mode = diagnostic.upstreamResponseMode;
|
|
@@ -935,8 +1093,9 @@
|
|
|
935
1093
|
const provider = getSelectedProvider(instance);
|
|
936
1094
|
const modelId = getSelectedTestModelId(instance) || provider?.defaultModelId;
|
|
937
1095
|
state.textContent = formatVerification(instance, provider, modelId);
|
|
938
|
-
const status = instance.verifications[modelId]?.status || provider?.modelDiagnostics?.[modelId]?.status || '';
|
|
1096
|
+
const status = instance.verifications[modelId]?.status || (!instance.verificationStale && provider?.modelDiagnostics?.[modelId]?.status) || '';
|
|
939
1097
|
state.dataset.tone = status === 'failed' ? 'failed' : status === 'tested' ? 'success' : '';
|
|
1098
|
+
refreshModelStatuses(instance);
|
|
940
1099
|
}
|
|
941
1100
|
|
|
942
1101
|
function getSelectedTestModelId(instance) {
|
|
@@ -947,7 +1106,43 @@
|
|
|
947
1106
|
return String(instance.root.querySelector('[data-provider-field="baseUrl"]')?.value || '').trim();
|
|
948
1107
|
}
|
|
949
1108
|
|
|
1109
|
+
function syncPresetProtocol(instance, protocol) {
|
|
1110
|
+
const get = name => instance.root.querySelector('[data-provider-field="' + name + '"]');
|
|
1111
|
+
if (get('fullEndpoint')?.checked) return false;
|
|
1112
|
+
const info = instance.Profiles.getPresetConnectionInfo(get('baseUrl')?.value);
|
|
1113
|
+
const next = info && instance.Profiles.getPresetConnection(info.id, protocol);
|
|
1114
|
+
if (!next || next.baseUrl === info.connection.baseUrl) return false;
|
|
1115
|
+
get('baseUrl').value = next.baseUrl;
|
|
1116
|
+
// Respect manually overridden authentication and reasoning settings.
|
|
1117
|
+
for (const field of ['authMode', 'reasoningAdapter']) {
|
|
1118
|
+
const input = get(field);
|
|
1119
|
+
if (input?.value === info.connection[field]) input.value = next[field];
|
|
1120
|
+
}
|
|
1121
|
+
return true;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function updatePresetConnectionHint(instance) {
|
|
1125
|
+
const hint = instance.root.querySelector('[data-provider-connection-hint]');
|
|
1126
|
+
if (!hint) return;
|
|
1127
|
+
const info = instance.Profiles.getPresetConnectionInfo(getCurrentBaseUrl(instance));
|
|
1128
|
+
const protocol = instance.root.querySelector('[data-provider-field="wireApiPreference"]')?.value;
|
|
1129
|
+
let text = '';
|
|
1130
|
+
if (info && protocol !== 'auto' && protocol !== info.connection.wireApiPreference) {
|
|
1131
|
+
text = instance.tx('This preset address uses a different protocol. Enter an endpoint matching the selected API format.',
|
|
1132
|
+
'此预设地址与所选协议不同,请填写该协议对应的地址。');
|
|
1133
|
+
} else if (info?.id === 'kimi') {
|
|
1134
|
+
text = instance.tx('This is the Kimi Open Platform China endpoint. Kimi Code and international keys use their own endpoints.',
|
|
1135
|
+
'此处为 Kimi 开放平台中国站地址;Kimi Code 和国际站密钥需要各自对应的端点。');
|
|
1136
|
+
} else if (info?.id === 'glm' && info.connection.wireApiPreference === 'anthropic') {
|
|
1137
|
+
text = instance.tx('GLM Anthropic access and the standard pay-as-you-go API use different endpoints. Confirm the key has access to this endpoint; Chat uses the standard API.',
|
|
1138
|
+
'GLM Anthropic 接入与通用按量 API 的端点不同。请确认密钥具备此端点的权限;Chat 使用通用 API 地址。');
|
|
1139
|
+
}
|
|
1140
|
+
hint.textContent = text;
|
|
1141
|
+
hint.hidden = !text;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
950
1144
|
function updateDisclosureText(instance) {
|
|
1145
|
+
updatePresetConnectionHint(instance);
|
|
951
1146
|
const text = instance.root.querySelector('[data-provider-disclosure-text]');
|
|
952
1147
|
if (!text) return;
|
|
953
1148
|
const baseUrl = getCurrentBaseUrl(instance) || instance.tx('this endpoint', '此端点');
|
|
@@ -971,6 +1166,361 @@
|
|
|
971
1166
|
button.setAttribute('aria-label', actionLabel);
|
|
972
1167
|
}
|
|
973
1168
|
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
function providerTemplateTitle(instance, id) {
|
|
1172
|
+
if (id === 'kimi') return 'Kimi';
|
|
1173
|
+
if (id === 'glm') return 'GLM';
|
|
1174
|
+
if (id === 'deepseek') return 'DeepSeek';
|
|
1175
|
+
if (id === 'openai-compatible') return instance.tx('OpenAI-compatible API', 'OpenAI 兼容接口');
|
|
1176
|
+
if (id === 'anthropic-compatible') return instance.tx('Anthropic-compatible API', 'Anthropic 兼容接口');
|
|
1177
|
+
return instance.tx('Custom connection', '自定义连接');
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function confirmProviderAction(instance, message, kind = 'discard') {
|
|
1181
|
+
if (!instance.root || instance.root.hidden || instance.confirmation) return Promise.resolve(false);
|
|
1182
|
+
const modal = instance.root.querySelector('[data-provider-confirmation]');
|
|
1183
|
+
if (!modal) return Promise.resolve(false);
|
|
1184
|
+
const tx = instance.tx;
|
|
1185
|
+
const titles = {
|
|
1186
|
+
discard: tx('Unsaved changes', '尚未保存的修改'),
|
|
1187
|
+
secret: tx('Remove API key?', '清除 API 密钥?'),
|
|
1188
|
+
provider: tx('Delete connection?', '删除连接?'),
|
|
1189
|
+
model: tx('Remove model?', '移除模型?')
|
|
1190
|
+
};
|
|
1191
|
+
const labels = {
|
|
1192
|
+
discard: tx('Discard changes', '放弃修改'),
|
|
1193
|
+
secret: tx('Remove key', '清除密钥'),
|
|
1194
|
+
provider: tx('Delete connection', '删除连接'),
|
|
1195
|
+
model: tx('Remove model', '移除模型')
|
|
1196
|
+
};
|
|
1197
|
+
const cancel = kind === 'discard' ? tx('Keep editing', '继续编辑') : tx('Cancel', '取消');
|
|
1198
|
+
const provider = getSelectedProvider(instance);
|
|
1199
|
+
return new Promise(resolve => {
|
|
1200
|
+
instance.confirmation = { resolve, selectedId: instance.selectedId,
|
|
1201
|
+
revision: provider?.revision || 0, returnFocus: instance.document.activeElement };
|
|
1202
|
+
modal.innerHTML = '<header><span class="codex-provider-confirm-brand">Codex Overleaf Link</span>'
|
|
1203
|
+
+ '<h3 id="codex-provider-confirm-title">' + escapeHtml(titles[kind] || titles.discard) + '</h3></header>'
|
|
1204
|
+
+ '<p id="codex-provider-confirm-message">' + escapeHtml(message) + '</p>'
|
|
1205
|
+
+ '<footer><button type="button" class="codex-provider-secondary-button" data-provider-confirm-answer="cancel">'
|
|
1206
|
+
+ escapeHtml(cancel) + '</button><button type="button" class="codex-provider-danger-button" data-provider-confirm-answer="confirm">'
|
|
1207
|
+
+ escapeHtml(labels[kind] || labels.discard) + '</button></footer>';
|
|
1208
|
+
try {
|
|
1209
|
+
if (modal.showModal) modal.showModal(); else modal.setAttribute('open', '');
|
|
1210
|
+
modal.querySelector('[data-provider-confirm-answer="cancel"]')?.focus();
|
|
1211
|
+
} catch (_error) {
|
|
1212
|
+
finishProviderConfirmation(instance, false);
|
|
1213
|
+
setStatus(instance, { tone: 'failed', title: tx('The confirmation could not be opened. No changes were discarded.',
|
|
1214
|
+
'确认窗口无法打开,未丢弃任何修改。') });
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function finishProviderConfirmation(instance, confirmed, restoreFocus = true) {
|
|
1220
|
+
const pending = instance.confirmation;
|
|
1221
|
+
if (!pending) return;
|
|
1222
|
+
instance.confirmation = null;
|
|
1223
|
+
const modal = instance.root?.querySelector('[data-provider-confirmation]');
|
|
1224
|
+
modal?.close?.();
|
|
1225
|
+
modal?.removeAttribute('open');
|
|
1226
|
+
modal?.replaceChildren();
|
|
1227
|
+
const sameTarget = Boolean(instance.root && !instance.root.hidden
|
|
1228
|
+
&& instance.selectedId === pending.selectedId
|
|
1229
|
+
&& (getSelectedProvider(instance)?.revision || 0) === pending.revision);
|
|
1230
|
+
pending.resolve(confirmed === true && sameTarget);
|
|
1231
|
+
if (restoreFocus) pending.returnFocus?.focus?.();
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function renderProviderTemplateIcon(preset) {
|
|
1235
|
+
const vendorPath = PROVIDER_VENDOR_ICON_PATHS[preset.id] || '';
|
|
1236
|
+
const path = preset.id === 'openai-compatible' ? CODEX_BLOSSOM_PATH
|
|
1237
|
+
: preset.id === 'anthropic-compatible' ? ANTHROPIC_LOGO_PATH : vendorPath;
|
|
1238
|
+
return path
|
|
1239
|
+
? '<svg class="codex-provider-brand-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill-rule="'
|
|
1240
|
+
+ (vendorPath ? 'evenodd' : 'nonzero') + '" d="' + path + '"></path></svg>'
|
|
1241
|
+
: escapeHtml(preset.short || '+');
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
function renderTemplatePicker(instance, detail) {
|
|
1245
|
+
const tx = instance.tx;
|
|
1246
|
+
detail.innerHTML = '<div class="codex-provider-template-head"><button type="button" class="codex-provider-inline-button" data-provider-action="templates-back">'
|
|
1247
|
+
+ escapeHtml(tx('Back', '返回')) + '</button><h3>' + escapeHtml(tx('Add a connection', '添加连接')) + '</h3></div>'
|
|
1248
|
+
+ '<p class="codex-provider-help">' + escapeHtml(tx(
|
|
1249
|
+
'Service shortcuts default to Anthropic Messages. Chat Completions remains available; preset addresses follow the selected protocol. Add the matching API key and model IDs separately.',
|
|
1250
|
+
'服务快捷入口默认采用 Anthropic Messages,也可切换 Chat Completions,预设地址随协议联动。请另行填写对应密钥和模型 ID。')) + '</p>'
|
|
1251
|
+
+ '<div class="codex-provider-template-grid">' + instance.Profiles.getProviderPresets().map(preset =>
|
|
1252
|
+
'<button type="button" class="codex-provider-template" data-provider-template="' + escapeAttr(preset.id) + '">'
|
|
1253
|
+
+ '<span class="codex-provider-template-mark" aria-hidden="true">' + renderProviderTemplateIcon(preset) + '</span>'
|
|
1254
|
+
+ '<span><strong>' + escapeHtml(providerTemplateTitle(instance, preset.id))
|
|
1255
|
+
+ '</strong><small>' + escapeHtml(preset.id === 'custom' ? tx('Gateway or self-hosted API', '网关或自托管 API')
|
|
1256
|
+
: preset.baseUrl ? (preset.wireApiPreference === 'anthropic' ? 'Anthropic Messages' : 'Chat Completions') + ' · ' + getEndpointHost(preset.baseUrl)
|
|
1257
|
+
: preset.wireApiPreference === 'anthropic' ? 'Anthropic Messages'
|
|
1258
|
+
: preset.wireApiPreference === 'responses' ? 'OpenAI Responses' : 'Responses / Chat Completions')
|
|
1259
|
+
+ '</small></span><span aria-hidden="true">›</span></button>').join('') + '</div>'
|
|
1260
|
+
+ '<p class="codex-provider-help">' + escapeHtml(tx(
|
|
1261
|
+
'Adding or saving a connection does not switch the current project. Credentials remain with the Native Host.',
|
|
1262
|
+
'添加或保存连接不会切换当前项目。已保存的密钥仍由 Native Host 保管。')) + '</p>';
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function modelButton(action, id, label, path, selected = false) {
|
|
1266
|
+
return '<button type="button" class="codex-provider-model-action" data-provider-model-action="'
|
|
1267
|
+
+ action + '" data-model-id="' + escapeAttr(id) + '" title="' + escapeAttr(label)
|
|
1268
|
+
+ '" aria-label="' + escapeAttr(label) + '"' + (selected ? ' data-selected="true"' : '') + '>'
|
|
1269
|
+
+ '<svg viewBox="0 0 20 20" aria-hidden="true"><path d="' + path + '"></path></svg></button>';
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
function renderModelList(instance) {
|
|
1273
|
+
const section = instance.root.querySelector('[data-provider-model-section]');
|
|
1274
|
+
if (!section) return;
|
|
1275
|
+
const tx = instance.tx, provider = getSelectedProvider(instance), draft = instance.draft || provider;
|
|
1276
|
+
const models = draft.models || [];
|
|
1277
|
+
section.innerHTML = '<div class="codex-provider-section-head"><div><h4>' + escapeHtml(tx('Models', '模型'))
|
|
1278
|
+
+ ' <span>' + models.length + '/32</span></h4><p>' + escapeHtml(tx(
|
|
1279
|
+
'Model IDs are sent exactly as configured.', '模型 ID 将按配置原样发送。')) + '</p></div>'
|
|
1280
|
+
+ '<button type="button" class="codex-provider-inline-button" data-provider-model-action="add">+ '
|
|
1281
|
+
+ escapeHtml(tx('Add model', '添加模型')) + '</button></div>'
|
|
1282
|
+
+ (models.length ? '<label class="codex-provider-search"><span class="codex-provider-sr-only">'
|
|
1283
|
+
+ escapeHtml(tx('Search models', '搜索模型')) + '</span><input type="search" data-provider-model-search value="'
|
|
1284
|
+
+ escapeAttr(instance.modelFilter) + '" placeholder="' + escapeAttr(tx('Search models', '搜索模型')) + '"></label>' : '')
|
|
1285
|
+
+ '<div class="codex-provider-model-list">' + models.map(model => {
|
|
1286
|
+
const isDefault = model.id === draft.defaultModelId;
|
|
1287
|
+
const context = Number(model.contextWindow) || 262144;
|
|
1288
|
+
const facts = [
|
|
1289
|
+
context >= 1000000 ? (context / 1000000).toFixed(1).replace(/\.0$/, '') + 'M' : Math.round(context / 1024) + 'K',
|
|
1290
|
+
(model.inputModalities || []).includes('image') ? tx('Text + image', '文本与图像') : tx('Text', '文本')
|
|
1291
|
+
];
|
|
1292
|
+
return '<div class="codex-provider-model-row" data-provider-model-row data-provider-model-search-text="'
|
|
1293
|
+
+ escapeAttr((model.id + ' ' + model.label).toLowerCase()) + '">'
|
|
1294
|
+
+ '<button type="button" class="codex-provider-model-identity" data-provider-model-action="edit" data-model-id="' + escapeAttr(model.id) + '">'
|
|
1295
|
+
+ '<strong>' + escapeHtml(model.label || model.id) + (isDefault ? '<span class="codex-provider-default-tag">'
|
|
1296
|
+
+ escapeHtml(tx('Default', '默认')) + '</span>' : '') + '</strong>'
|
|
1297
|
+
+ (model.label && model.label !== model.id ? '<code>' + escapeHtml(model.id) + '</code>' : '')
|
|
1298
|
+
+ '<small>' + escapeHtml(facts.join(' · ')) + '</small></button>'
|
|
1299
|
+
+ '<div class="codex-provider-model-actions">'
|
|
1300
|
+
+ modelButton('test', model.id, tx('Test ', '测试 ') + model.id, 'M7 4l9 6-9 6V4Z')
|
|
1301
|
+
+ modelButton('default', model.id, tx('Set as default: ', '设为默认:') + model.id, 'm10 2 2.5 5 5.5.8-4 3.9.9 5.5-4.9-2.6-4.9 2.6.9-5.5-4-3.9L7.5 7Z', isDefault)
|
|
1302
|
+
+ modelButton('remove', model.id, tx('Remove ', '移除 ') + model.id, 'M5 5l10 10M15 5 5 15')
|
|
1303
|
+
+ '</div><span class="codex-provider-model-status" data-provider-model-status="' + escapeAttr(model.id) + '"></span></div>';
|
|
1304
|
+
}).join('') + '</div>'
|
|
1305
|
+
+ (!models.length ? '<div class="codex-provider-model-empty">' + escapeHtml(tx(
|
|
1306
|
+
'Add the first model to finish this connection. No model is inferred from the provider name.',
|
|
1307
|
+
'添加第一个模型以完成配置。服务名称不会被当成模型 ID。')) + '</div>' : '')
|
|
1308
|
+
+ '<p class="codex-provider-help" data-provider-model-no-results hidden>' + escapeHtml(tx('No matching models.', '没有匹配的模型。')) + '</p>';
|
|
1309
|
+
refreshModelStatuses(instance);
|
|
1310
|
+
filterModelRows(instance);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
function filterModelRows(instance) {
|
|
1314
|
+
let visible = 0;
|
|
1315
|
+
const rows = instance.root.querySelectorAll('[data-provider-model-row]');
|
|
1316
|
+
for (const row of rows) {
|
|
1317
|
+
row.hidden = !row.dataset.providerModelSearchText.includes(instance.modelFilter.toLowerCase());
|
|
1318
|
+
if (!row.hidden) visible++;
|
|
1319
|
+
}
|
|
1320
|
+
const empty = instance.root.querySelector('[data-provider-model-no-results]');
|
|
1321
|
+
if (empty) empty.hidden = !rows.length || visible > 0;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function refreshModelStatuses(instance) {
|
|
1325
|
+
const provider = getSelectedProvider(instance);
|
|
1326
|
+
for (const element of instance.root.querySelectorAll('[data-provider-model-status]')) {
|
|
1327
|
+
const id = element.dataset.providerModelStatus;
|
|
1328
|
+
const testing = instance.busy === 'testing' && id === getSelectedTestModelId(instance);
|
|
1329
|
+
const status = instance.verifications[id]?.status
|
|
1330
|
+
|| (!instance.verificationStale && provider?.modelDiagnostics?.[id]?.status) || '';
|
|
1331
|
+
element.textContent = testing ? instance.tx('Testing connection…', '正在测试连接…') : formatVerification(instance, provider, id);
|
|
1332
|
+
element.dataset.tone = testing ? 'progress' : status === 'tested' ? 'success' : status === 'failed' ? 'failed' : '';
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function replaceModelDraft(instance, next) {
|
|
1337
|
+
const secret = instance.root.querySelector('[data-provider-field="apiKey"]')?.value || '';
|
|
1338
|
+
const disclosure = instance.root.querySelector('[data-provider-disclosure]')?.checked === true;
|
|
1339
|
+
const advanced = instance.root.querySelector('.codex-provider-advanced')?.open === true;
|
|
1340
|
+
const detail = instance.root.querySelector('[data-provider-detail]');
|
|
1341
|
+
const scrollTop = detail?.scrollTop || 0;
|
|
1342
|
+
const selectedTest = getSelectedTestModelId(instance);
|
|
1343
|
+
instance.draft = { ...getSelectedProvider(instance), ...next };
|
|
1344
|
+
renderDetail(instance);
|
|
1345
|
+
const input = instance.root.querySelector('[data-provider-field="apiKey"]');
|
|
1346
|
+
if (input) input.value = secret;
|
|
1347
|
+
const checkbox = instance.root.querySelector('[data-provider-disclosure]');
|
|
1348
|
+
if (checkbox) checkbox.checked = disclosure;
|
|
1349
|
+
const advancedPanel = instance.root.querySelector('.codex-provider-advanced');
|
|
1350
|
+
if (advancedPanel) advancedPanel.open = advanced;
|
|
1351
|
+
const testSelect = instance.root.querySelector('[data-provider-test-model]');
|
|
1352
|
+
if (testSelect && next.models.some(model => model.id === selectedTest)) testSelect.value = selectedTest;
|
|
1353
|
+
if (detail) detail.scrollTop = scrollTop;
|
|
1354
|
+
markDirty(instance);
|
|
1355
|
+
renderFooter(instance);
|
|
1356
|
+
applyBusyState(instance);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
async function handleModelAction(instance, action, id) {
|
|
1360
|
+
if (action === 'cancel-editor') { closeModelEditor(instance); return; }
|
|
1361
|
+
if (action === 'apply-editor') { commitModelEditor(instance); return; }
|
|
1362
|
+
if (action === 'add' || action === 'edit') { openModelEditor(instance, action === 'edit' ? id : ''); return; }
|
|
1363
|
+
if (action === 'test') {
|
|
1364
|
+
if (!validateConnectionForm(instance)) return;
|
|
1365
|
+
const select = instance.root.querySelector('[data-provider-test-model]');
|
|
1366
|
+
if (select) select.value = id;
|
|
1367
|
+
invalidateVerification(instance, false, id);
|
|
1368
|
+
instance.callbacks.onTest?.(readContext(instance));
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
const context = readContext(instance), draft = context.draft;
|
|
1372
|
+
if (!draft.models.some(model => model.id === id)) return;
|
|
1373
|
+
if (action === 'default') {
|
|
1374
|
+
if (draft.defaultModelId !== id) replaceModelDraft(instance, { ...draft, defaultModelId: id });
|
|
1375
|
+
} else if (action === 'remove' && await confirmProviderAction(instance, instance.tx(
|
|
1376
|
+
'Remove ' + id + ' from this provider? This takes effect after Save.',
|
|
1377
|
+
'从此服务移除 ' + id + '?保存后生效。'), 'model')) {
|
|
1378
|
+
replaceModelDraft(instance, instance.Profiles.removeDraftModel(draft, id));
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
function openModelEditor(instance, id) {
|
|
1383
|
+
const provider = readContext(instance).draft;
|
|
1384
|
+
const existing = provider.models.find(model => model.id === id);
|
|
1385
|
+
if (id && !existing) return;
|
|
1386
|
+
const model = existing || { id: '', label: '', contextWindow: provider.contextWindow,
|
|
1387
|
+
maxOutputTokens: null, inputModalities: provider.inputModalities,
|
|
1388
|
+
supportsParallelToolCalls: provider.supportsParallelToolCalls,
|
|
1389
|
+
upstreamResponseMode: 'auto', reasoningEfforts: [], reasoningAdapter: '', reasoningCapability: '', baseInstructions: '' };
|
|
1390
|
+
const tx = instance.tx, modal = instance.root.querySelector('[data-provider-model-editor]');
|
|
1391
|
+
instance.modelEditor = { id, providerId: instance.selectedId, returnFocus: instance.document.activeElement };
|
|
1392
|
+
modal.classList.add('is-minimal');
|
|
1393
|
+
const field = (name, label, value, attributes = '', help = '') => '<label class="codex-provider-field"><span>'
|
|
1394
|
+
+ escapeHtml(label) + '</span><input data-model-field="' + name + '" value="' + escapeAttr(value ?? '')
|
|
1395
|
+
+ '" ' + attributes + '>' + (help ? '<small>' + escapeHtml(help) + '</small>' : '') + '</label>';
|
|
1396
|
+
const select = (name, label, value, options) => '<label class="codex-provider-field"><span>'
|
|
1397
|
+
+ escapeHtml(label) + '</span><select data-model-field="' + name + '">'
|
|
1398
|
+
+ options.map(([key, title]) => '<option value="' + key + '"' + (value === key ? ' selected' : '') + '>'
|
|
1399
|
+
+ escapeHtml(title) + '</option>').join('') + '</select></label>';
|
|
1400
|
+
modal.innerHTML = '<header class="codex-provider-model-editor-head"><h3 id="codex-provider-model-title">'
|
|
1401
|
+
+ escapeHtml(id ? tx('Edit model', '编辑模型') : tx('Add model', '添加模型'))
|
|
1402
|
+
+ '</h3><button type="button" class="codex-provider-model-close" data-provider-model-action="cancel-editor" aria-label="'
|
|
1403
|
+
+ escapeAttr(tx('Close model editor', '关闭模型编辑')) + '"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 5 10 10M15 5 5 15"></path></svg></button></header>'
|
|
1404
|
+
+ '<div class="codex-provider-model-editor-body"><div class="codex-provider-model-basics">'
|
|
1405
|
+
+ field('id', tx('Model ID', '模型 ID'), model.id, 'type="text" maxlength="200" spellcheck="false" autofocus placeholder="'
|
|
1406
|
+
+ escapeAttr(tx('Exact model ID from the provider', '填写服务商提供的模型 ID')) + '"')
|
|
1407
|
+
+ '<div class="codex-provider-form-grid">'
|
|
1408
|
+
+ field('contextWindow', tx('Context window', '上下文窗口'), model.contextWindow, 'type="number" min="8192" max="4000000" step="1"')
|
|
1409
|
+
+ field('maxOutputTokens', tx('Max output tokens', '最大输出 Token'), model.maxOutputTokens,
|
|
1410
|
+
'type="number" min="256" max="65536" placeholder="' + Number(provider.maxOutputTokens || 65536) + '"',
|
|
1411
|
+
tx('Optional; leave empty to inherit.', '可留空,沿用服务设置。'))
|
|
1412
|
+
+ '</div><section class="codex-provider-model-types"><div class="codex-provider-model-types-head"><span>'
|
|
1413
|
+
+ escapeHtml(tx('Input type', '输入类型')) + '</span><small>'
|
|
1414
|
+
+ escapeHtml(tx('Output: text', '输出:文本')) + '</small></div><div class="codex-provider-type-options">'
|
|
1415
|
+
+ '<span class="codex-provider-type-chip is-fixed" aria-label="' + escapeAttr(tx('Text input is required', '文本输入为必选项'))
|
|
1416
|
+
+ '"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="m3 8 3 3 7-7"></path></svg>'
|
|
1417
|
+
+ escapeHtml(tx('Text', '文本')) + '<small>' + escapeHtml(tx('Required', '必选')) + '</small></span>'
|
|
1418
|
+
+ '<label class="codex-provider-type-chip"><input type="checkbox" data-model-field="supportsImages"'
|
|
1419
|
+
+ ((model.inputModalities || []).includes('image') ? ' checked' : '') + '><span>' + escapeHtml(tx('Images', '图片'))
|
|
1420
|
+
+ '</span></label></div></section></div>'
|
|
1421
|
+
+ '<details class="codex-provider-model-settings" data-model-reasoning><summary><span>'
|
|
1422
|
+
+ escapeHtml(tx('Reasoning', '推理设置')) + '</span><small data-model-reasoning-summary></small></summary>'
|
|
1423
|
+
+ '<div class="codex-provider-model-settings-body"><p class="codex-provider-help">'
|
|
1424
|
+
+ escapeHtml(tx('Leave levels unchecked to use provider defaults. Only select levels supported by this endpoint.',
|
|
1425
|
+
'档位全部留空时沿用服务默认配置;仅选择该端点支持的档位。')) + '</p><div class="codex-provider-effort-chips">'
|
|
1426
|
+
+ instance.Profiles.MODEL_EFFORTS.map(effort => '<label><input type="checkbox" data-model-effort value="' + effort + '"'
|
|
1427
|
+
+ ((model.reasoningEfforts || []).includes(effort) ? ' checked' : '') + '><span>' + effort + '</span></label>').join('')
|
|
1428
|
+
+ '</div><div class="codex-provider-form-grid">'
|
|
1429
|
+
+ select('reasoningAdapter', tx('Parameter format', '参数格式'), model.reasoningAdapter || '',
|
|
1430
|
+
[['', tx('Inherit provider', '沿用服务配置')], ['auto', tx('Auto-detect', '自动识别')], ['none', tx('Disabled', '禁用')],
|
|
1431
|
+
['deepseek', 'DeepSeek'], ['anthropic', 'Anthropic'], ['reasoning_effort', 'reasoning_effort'],
|
|
1432
|
+
['openrouter', 'OpenRouter'], ['enable_thinking', 'enable_thinking'], ['thinking', 'thinking.type'], ['reasoning_split', 'reasoning_split']])
|
|
1433
|
+
+ select('reasoningCapability', tx('Reasoning control', '推理控制'), model.reasoningCapability || '',
|
|
1434
|
+
[['', tx('Inherit / selected levels', '继承或使用所选档位')], ['none', tx('Not supported', '不支持')],
|
|
1435
|
+
['toggle', tx('On / Off', '开启/关闭')], ['effort', tx('Effort levels', '档位选择')]])
|
|
1436
|
+
+ '</div></div></details><details class="codex-provider-model-settings"><summary><span>'
|
|
1437
|
+
+ escapeHtml(tx('Advanced', '高级设置')) + '</span><small>'
|
|
1438
|
+
+ escapeHtml(tx('Name and compatibility', '名称与兼容性')) + '</small></summary>'
|
|
1439
|
+
+ '<div class="codex-provider-model-settings-body"><div class="codex-provider-form-grid">'
|
|
1440
|
+
+ field('label', tx('Display name (optional)', '显示名称(可选)'), model.label, 'type="text" maxlength="200"')
|
|
1441
|
+
+ select('upstreamResponseMode', tx('Response mode', '响应模式'), model.upstreamResponseMode || 'auto',
|
|
1442
|
+
[['auto', tx('Auto', '自动')], ['streaming', tx('Streaming', '流式')], ['buffered', tx('Buffered', '缓冲')]])
|
|
1443
|
+
+ '</div><label class="codex-provider-model-checkbox"><input type="checkbox" data-model-field="supportsParallelToolCalls"'
|
|
1444
|
+
+ (model.supportsParallelToolCalls ? ' checked' : '') + '> ' + escapeHtml(tx('Parallel tool calls', '并行工具调用')) + '</label>'
|
|
1445
|
+
+ '<label class="codex-provider-field"><span>' + escapeHtml(tx('Base instructions (optional)', '基础指令(可选)'))
|
|
1446
|
+
+ '</span><textarea data-model-field="baseInstructions" rows="3" maxlength="8000">' + escapeHtml(model.baseInstructions || '')
|
|
1447
|
+
+ '</textarea></label></div></details><p role="alert" class="codex-provider-model-error" data-model-error></p></div>'
|
|
1448
|
+
+ '<footer class="codex-provider-model-editor-foot"><small>' + escapeHtml(tx('Saved with the provider.', '随服务配置一起保存。'))
|
|
1449
|
+
+ '</small><button type="button" class="codex-provider-secondary-button" data-provider-model-action="cancel-editor">'
|
|
1450
|
+
+ escapeHtml(tx('Cancel', '取消')) + '</button><button type="button" class="codex-provider-primary-button" data-provider-model-action="apply-editor">'
|
|
1451
|
+
+ escapeHtml(id ? tx('Apply', '应用') : tx('Add model', '添加模型')) + '</button></footer>';
|
|
1452
|
+
updateModelReasoningSummary(instance);
|
|
1453
|
+
if (modal.showModal) modal.showModal(); else modal.setAttribute('open', '');
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function updateModelReasoningSummary(instance) {
|
|
1457
|
+
const modal = instance.root.querySelector('[data-provider-model-editor]');
|
|
1458
|
+
const summary = modal?.querySelector('[data-model-reasoning-summary]');
|
|
1459
|
+
if (!summary) return;
|
|
1460
|
+
const capability = modal.querySelector('[data-model-field="reasoningCapability"]')?.value;
|
|
1461
|
+
const adapter = modal.querySelector('[data-model-field="reasoningAdapter"]')?.value;
|
|
1462
|
+
const levels = Array.from(modal.querySelectorAll('[data-model-effort]:checked')).map(input => input.value);
|
|
1463
|
+
summary.textContent = capability === 'none' || adapter === 'none' ? instance.tx('Disabled', '已关闭')
|
|
1464
|
+
: levels.length ? levels.join(' · ')
|
|
1465
|
+
: capability === 'toggle' ? instance.tx('On / Off', '开关控制')
|
|
1466
|
+
: adapter && adapter !== 'auto' ? adapter
|
|
1467
|
+
: capability === 'effort' ? instance.tx('Provider levels', '服务默认档位')
|
|
1468
|
+
: instance.tx('Provider defaults', '沿用服务配置');
|
|
1469
|
+
summary.title = summary.textContent;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function closeModelEditor(instance, restoreFocus = true) {
|
|
1473
|
+
if (!instance.modelEditor) return;
|
|
1474
|
+
const focus = instance.modelEditor.returnFocus;
|
|
1475
|
+
const modal = instance.root?.querySelector('[data-provider-model-editor]');
|
|
1476
|
+
instance.modelEditor = null;
|
|
1477
|
+
modal?.close?.();
|
|
1478
|
+
modal?.removeAttribute('open');
|
|
1479
|
+
if (modal) modal.replaceChildren();
|
|
1480
|
+
if (restoreFocus) focus?.focus?.();
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function commitModelEditor(instance) {
|
|
1484
|
+
const editor = instance.modelEditor;
|
|
1485
|
+
if (!editor || editor.providerId !== instance.selectedId) return;
|
|
1486
|
+
const modal = instance.root.querySelector('[data-provider-model-editor]');
|
|
1487
|
+
const get = key => modal.querySelector('[data-model-field="' + key + '"]');
|
|
1488
|
+
const patch = Object.fromEntries(['id', 'label', 'contextWindow', 'maxOutputTokens',
|
|
1489
|
+
'upstreamResponseMode', 'reasoningAdapter', 'reasoningCapability', 'baseInstructions']
|
|
1490
|
+
.map(key => [key, get(key)?.value || '']));
|
|
1491
|
+
patch.inputModalities = get('supportsImages')?.checked ? ['text', 'image'] : ['text'];
|
|
1492
|
+
patch.supportsParallelToolCalls = get('supportsParallelToolCalls')?.checked === true;
|
|
1493
|
+
patch.reasoningEfforts = Array.from(modal.querySelectorAll('[data-model-effort]:checked')).map(input => input.value);
|
|
1494
|
+
try {
|
|
1495
|
+
const draft = readContext(instance).draft;
|
|
1496
|
+
const next = instance.Profiles.updateDraftModel(draft, editor.id, patch);
|
|
1497
|
+
const unchanged = JSON.stringify(next.models) === JSON.stringify(draft.models)
|
|
1498
|
+
&& next.defaultModelId === draft.defaultModelId;
|
|
1499
|
+
closeModelEditor(instance, false);
|
|
1500
|
+
if (!unchanged) replaceModelDraft(instance, next);
|
|
1501
|
+
} catch (error) {
|
|
1502
|
+
const translated = {
|
|
1503
|
+
id: instance.tx(error.message, '请检查模型 ID:必填、最多 200 字符、不可重复,且单个服务最多 32 个模型。'),
|
|
1504
|
+
contextWindow: instance.tx(error.message, '上下文窗口必须为 8192 至 4000000 之间的整数。'),
|
|
1505
|
+
maxOutputTokens: instance.tx(error.message, '输出上限须为 256 至 65536 之间的整数,并小于上下文窗口。')
|
|
1506
|
+
};
|
|
1507
|
+
modal.querySelector('[data-model-error]').textContent = translated[error.field] || error.message;
|
|
1508
|
+
if (error.field) get(error.field)?.focus();
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function validateConnectionForm(instance) {
|
|
1513
|
+
const context = readContext(instance), tx = instance.tx;
|
|
1514
|
+
let message = '';
|
|
1515
|
+
if (!context.draft.name) message = tx('Enter a provider name.', '请填写服务名称。');
|
|
1516
|
+
else if (!context.draft.baseUrl) message = tx('Enter the API Base URL.', '请填写 API 地址。');
|
|
1517
|
+
else if (!context.draft.models.length || !context.draft.models.some(model => model.id === context.draft.defaultModelId))
|
|
1518
|
+
message = tx('Add at least one model and select a default.', '请添加至少一个模型,并选择默认模型。');
|
|
1519
|
+
if (message) { setStatus(instance, { tone: 'failed', title: message }); return false; }
|
|
1520
|
+
return true;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
|
|
974
1524
|
function formatSecretSavedAt(instance, value) {
|
|
975
1525
|
const timestamp = Number(value);
|
|
976
1526
|
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
|
@@ -1008,6 +1558,7 @@
|
|
|
1008
1558
|
}
|
|
1009
1559
|
|
|
1010
1560
|
function formatJsonRecord(value) {
|
|
1561
|
+
if (typeof value === 'string') return value;
|
|
1011
1562
|
if (!value || typeof value !== 'object' || Array.isArray(value) || !Object.keys(value).length) {
|
|
1012
1563
|
return '';
|
|
1013
1564
|
}
|
|
@@ -1019,6 +1570,8 @@
|
|
|
1019
1570
|
}
|
|
1020
1571
|
|
|
1021
1572
|
function destroy(instance) {
|
|
1573
|
+
finishProviderConfirmation(instance, false, false);
|
|
1574
|
+
closeModelEditor(instance, false);
|
|
1022
1575
|
instance.root?.close?.();
|
|
1023
1576
|
instance.root?.remove?.();
|
|
1024
1577
|
instance.root = null;
|