@nextclaw/ui 0.13.4 → 0.13.6
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/CHANGELOG.md +128 -0
- package/dist/assets/api-BCDb13Uv.js +8 -0
- package/dist/assets/app-presenter-provider-C-QMjK7m.js +3 -0
- package/dist/assets/arrow-left-rIXFA2Zy.js +1 -0
- package/dist/assets/channels-list-page-DnqwsKGe.js +8 -0
- package/dist/assets/chat-page-CsQx20xL.js +105 -0
- package/dist/assets/config-split-page-BwhGqmUS.js +1 -0
- package/dist/assets/confirm-dialog-DBkKcQ1q.js +5 -0
- package/dist/assets/desktop-update-config-BE9KjiXi.js +1 -0
- package/dist/assets/{dist-CpwDZDtv.js → dist-BI0bX8jv.js} +1 -1
- package/dist/assets/dist-C8JRt-tf.js +1 -0
- package/dist/assets/doc-browser-context-VHUOIUXL.js +1 -0
- package/dist/assets/doc-browser-eqdNEerh.js +1 -0
- package/dist/assets/doc-browser-lpF_pyx4.js +1 -0
- package/dist/assets/ellipsis-C9VB2CxT.js +1 -0
- package/dist/assets/es2015-BCw3Q7nY.js +41 -0
- package/dist/assets/external-link-Bp7_4s9t.js +1 -0
- package/dist/assets/index-BCq7kbC8.css +1 -0
- package/dist/assets/index-DTdPDqto.js +101 -0
- package/dist/assets/key-round-Ccy6oND3.js +1 -0
- package/dist/assets/loader-circle-OpH4EErP.js +1 -0
- package/dist/assets/{mcp-marketplace-page-Dvi8mCBd.js → mcp-marketplace-page-B-GxKOqS.js} +2 -2
- package/dist/assets/mcp-marketplace-page-Dm-0-w51.js +1 -0
- package/dist/assets/model-config-BXPnV_7n.js +1 -0
- package/dist/assets/notice-card-Dsbkl7u7.js +1 -0
- package/dist/assets/play-CvgYhNbo.js +1 -0
- package/dist/assets/plus-DHcP-K1U.js +1 -0
- package/dist/assets/popover-BTLZE5Iz.js +1 -0
- package/dist/assets/provider-models-C_yOh6DE.js +1 -0
- package/dist/assets/provider-scoped-model-input-DKI9KN7N.js +1 -0
- package/dist/assets/providers-list-Bko6B7dE.js +1 -0
- package/dist/assets/react-BqS8oMyM.js +8 -0
- package/dist/assets/refresh-cw-BIaj7txb.js +1 -0
- package/dist/assets/remote-DHn_b8QU.js +1 -0
- package/dist/assets/rotate-cw-DMqSkRJQ.js +1 -0
- package/dist/assets/runtime-config-page-DX_wSK1J.js +1 -0
- package/dist/assets/save-BEoORten.js +1 -0
- package/dist/assets/search-COFh_SFI.js +1 -0
- package/dist/assets/{search-config-G2O1fcRb.js → search-config-DA5By-tE.js} +1 -1
- package/dist/assets/{secrets-config-TenzxYbE.js → secrets-config-eDxG7iN4.js} +2 -2
- package/dist/assets/{status-dot-BSMG7ZaC.js → status-dot-oWBjTovo.js} +1 -1
- package/dist/assets/{tabs-D1CjpWNX.js → tabs-CGaY0BVO.js} +1 -1
- package/dist/assets/{tabs-custom-CRSN1ekg.js → tabs-custom-D2fPYb4G.js} +1 -1
- package/dist/assets/{tag-chip-DEashiw6.js → tag-chip-TVkgofS1.js} +1 -1
- package/dist/assets/tooltip-BwHPKBv9.js +1 -0
- package/dist/assets/trash-2-D3psAGrz.js +1 -0
- package/dist/assets/x-CHAxzyp2.js +1 -0
- package/dist/index.html +25 -23
- package/dist/logos/mimo.svg +9 -0
- package/package.json +8 -8
- package/public/logos/mimo.svg +9 -0
- package/src/app/components/layout/app-layout.tsx +10 -2
- package/src/features/agents/components/agents-page.tsx +6 -4
- package/src/features/chat/components/chat-welcome.test.tsx +15 -0
- package/src/features/chat/components/chat-welcome.tsx +4 -7
- package/src/features/chat/hooks/use-ncp-chat-page-data.ts +17 -10
- package/src/features/marketplace/components/marketplace-list-card.tsx +22 -1
- package/src/features/marketplace/components/marketplace-page.tsx +3 -1
- package/src/features/marketplace/hooks/use-marketplace.ts +3 -1
- package/src/features/marketplace/utils/marketplace-installed-cache.utils.ts +8 -0
- package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +70 -6
- package/src/features/right-panel-resources/utils/right-panel-resource-route-resolver.utils.test.ts +25 -1
- package/src/features/right-panel-resources/utils/right-panel-resource-route-resolver.utils.ts +1 -1
- package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.test.ts +42 -0
- package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +22 -0
- package/src/features/service-apps/components/service-apps-panel.test.tsx +88 -0
- package/src/features/service-apps/components/service-apps-panel.tsx +246 -56
- package/src/features/service-apps/hooks/use-service-apps.ts +14 -0
- package/src/features/side-dock/components/side-dock.test.tsx +58 -3
- package/src/features/side-dock/components/side-dock.tsx +77 -24
- package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +37 -34
- package/src/features/side-dock/index.ts +2 -0
- package/src/features/side-dock/managers/side-dock.manager.test.ts +84 -3
- package/src/features/side-dock/managers/side-dock.manager.ts +135 -12
- package/src/features/side-dock/types/side-dock.types.ts +11 -4
- package/src/features/side-dock/utils/side-dock-item.utils.test.ts +8 -8
- package/src/features/side-dock/utils/side-dock-item.utils.ts +13 -5
- package/src/platforms/desktop/components/desktop-app-shell.tsx +5 -2
- package/src/platforms/mobile/components/mobile-app-shell.tsx +4 -1
- package/src/shared/components/config/provider-form-support.test.ts +21 -0
- package/src/shared/components/config/provider-form-support.ts +38 -78
- package/src/shared/components/config/provider-form.tsx +54 -67
- package/src/shared/components/config/providers-list.tsx +211 -73
- package/src/shared/components/doc-browser/doc-browser-context.test.tsx +73 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +3 -0
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +184 -98
- package/src/shared/components/doc-browser/doc-browser.test.tsx +168 -43
- package/src/shared/components/doc-browser/doc-browser.tsx +38 -7
- package/src/shared/components/doc-browser/index.ts +1 -0
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +30 -6
- package/src/shared/components/doc-browser/stores/doc-browser.store.ts +29 -0
- package/src/shared/components/doc-browser/types/doc-browser.types.ts +23 -0
- package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +7 -0
- package/src/shared/components/model-config.tsx +5 -4
- package/src/shared/components/provider-auth-section.tsx +2 -2
- package/src/shared/components/ui/switch.tsx +5 -2
- package/src/shared/hooks/use-config.ts +27 -6
- package/src/shared/lib/api/marketplace.types.ts +193 -0
- package/src/shared/lib/api/types.ts +23 -200
- package/src/shared/lib/api/utils/config.utils.ts +17 -7
- package/src/shared/lib/i18n/index.ts +10 -12
- package/src/shared/lib/i18n/{marketplace.ts → marketplace-labels.utils.ts} +3 -0
- package/src/shared/lib/i18n/runtime/doc-browser-labels.utils.ts +46 -2
- package/src/shared/lib/provider-models/index.test.ts +93 -15
- package/src/shared/lib/provider-models/index.ts +35 -20
- package/src/shared/lib/transport/request-raw-api-response.utils.ts +1 -1
- package/dist/assets/api-DruAcori.js +0 -15
- package/dist/assets/app-presenter-provider-DIcnNB9s.js +0 -3
- package/dist/assets/book-open-CRotBOF2.js +0 -1
- package/dist/assets/boxes-BvA-I63l.js +0 -1
- package/dist/assets/channels-list-page-wK3l7p5R.js +0 -8
- package/dist/assets/chat-page-DgehvdJT.js +0 -105
- package/dist/assets/config-split-page-BnTO1mAM.js +0 -1
- package/dist/assets/confirm-dialog-CGrKHbAW.js +0 -5
- package/dist/assets/createLucideIcon-Br1EFMEr.js +0 -1
- package/dist/assets/desktop-update-config-CehWxSSg.js +0 -1
- package/dist/assets/dist-rzakR_bu.js +0 -41
- package/dist/assets/doc-browser-BgxmVsTx.js +0 -1
- package/dist/assets/doc-browser-CZn5-Qbu.js +0 -1
- package/dist/assets/doc-browser-context-B8gk7HMr.js +0 -1
- package/dist/assets/external-link-DHjFdCpy.js +0 -1
- package/dist/assets/index-DORmh0Cy.css +0 -1
- package/dist/assets/index-DcFpvnge.js +0 -101
- package/dist/assets/key-round-D41Oe35x.js +0 -1
- package/dist/assets/mcp-marketplace-page-DBLhJWOJ.js +0 -1
- package/dist/assets/model-config-B-EJXGtO.js +0 -1
- package/dist/assets/play-C-0vo7hi.js +0 -1
- package/dist/assets/plus-Cr_akLp2.js +0 -1
- package/dist/assets/popover-YieElO8c.js +0 -1
- package/dist/assets/provider-scoped-model-input-BPRNGqYn.js +0 -1
- package/dist/assets/providers-list-CtBpL1ZY.js +0 -1
- package/dist/assets/react-B2X_ph0a.js +0 -1
- package/dist/assets/remote-BGHbHoHf.js +0 -1
- package/dist/assets/rotate-cw-jueVCNJQ.js +0 -1
- package/dist/assets/runtime-config-page-C1Hv7mad.js +0 -1
- package/dist/assets/save-aYdSdK97.js +0 -1
- package/dist/assets/search-CdhgFNwf.js +0 -1
- package/dist/assets/setting-row-BNu0qhOM.js +0 -1
- package/dist/assets/trash-2-DSWTsARW.js +0 -1
- package/dist/assets/x-D1v_vb54.js +0 -1
- package/src/shared/components/provider-enabled-field.tsx +0 -23
- /package/dist/assets/{config-hints-A7G_jMTU.js → config-hints-Ceiol9x4.js} +0 -0
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { t } from '@/shared/lib/i18n';
|
|
2
2
|
import type { getLanguage } from '@/shared/lib/i18n';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ProviderConfigView, ThinkingLevel } from '@/shared/lib/api';
|
|
4
|
+
import {
|
|
5
|
+
normalizeModelConfigMap,
|
|
6
|
+
normalizeThinkingLevels,
|
|
7
|
+
parseThinkingLevel,
|
|
8
|
+
THINKING_LEVELS,
|
|
9
|
+
type ModelConfig as ModelConfigEntry
|
|
10
|
+
} from '@/shared/lib/provider-models';
|
|
4
11
|
|
|
5
12
|
type WireApiType = 'auto' | 'chat' | 'responses';
|
|
6
|
-
type ModelConfig = Record<string,
|
|
7
|
-
thinking?: { supported: ThinkingLevel[]; default?: ThinkingLevel | null };
|
|
8
|
-
vision?: boolean;
|
|
9
|
-
}>;
|
|
13
|
+
type ModelConfig = Record<string, ModelConfigEntry>;
|
|
10
14
|
type ProviderAuthMethodOption = {
|
|
11
15
|
id: string;
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
const EMPTY_PROVIDER_CONFIG: ProviderConfigView = {
|
|
19
|
+
providerId: '',
|
|
20
|
+
providerType: null,
|
|
21
|
+
isBuiltInType: false,
|
|
22
|
+
isCustom: true,
|
|
15
23
|
enabled: true,
|
|
16
24
|
displayName: '',
|
|
17
25
|
apiKeySet: false,
|
|
@@ -23,9 +31,6 @@ const EMPTY_PROVIDER_CONFIG: ProviderConfigView = {
|
|
|
23
31
|
modelConfig: {}
|
|
24
32
|
};
|
|
25
33
|
|
|
26
|
-
const THINKING_LEVELS: ThinkingLevel[] = ['off', 'minimal', 'low', 'medium', 'high', 'adaptive', 'xhigh'];
|
|
27
|
-
const THINKING_LEVEL_SET = new Set<string>(THINKING_LEVELS);
|
|
28
|
-
|
|
29
34
|
function normalizeHeaders(input: Record<string, string> | null | undefined): Record<string, string> | null {
|
|
30
35
|
if (!input) {
|
|
31
36
|
return null;
|
|
@@ -107,76 +112,20 @@ function modelListsEqual(left: string[], right: string[]): boolean {
|
|
|
107
112
|
return left.every((item, index) => item === right[index]);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
function resolveEditableModels(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
function serializeModelsForSave(models: string[], _defaultModels: string[]): string[] {
|
|
115
|
-
return models;
|
|
115
|
+
function resolveEditableModels(defaultModels: string[], savedModels: string[]): string[] {
|
|
116
|
+
void defaultModels;
|
|
117
|
+
return normalizeModelList(savedModels);
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function parseThinkingLevel(value: unknown): ThinkingLevel | null {
|
|
125
|
-
if (typeof value !== 'string') {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
const normalized = value.trim().toLowerCase();
|
|
129
|
-
if (!normalized) {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
return THINKING_LEVEL_SET.has(normalized) ? (normalized as ThinkingLevel) : null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function normalizeThinkingLevels(values: unknown): ThinkingLevel[] {
|
|
136
|
-
if (!Array.isArray(values)) {
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
const deduped: ThinkingLevel[] = [];
|
|
140
|
-
for (const value of values) {
|
|
141
|
-
const level = parseThinkingLevel(value);
|
|
142
|
-
if (!level || deduped.includes(level)) {
|
|
143
|
-
continue;
|
|
120
|
+
function serializeModelsForSave(models: string[], providerId: string): string[] {
|
|
121
|
+
const normalizedModels = normalizeModelList(models);
|
|
122
|
+
return normalizedModels.map((model) => {
|
|
123
|
+
const trimmed = model.trim();
|
|
124
|
+
if (!trimmed || trimmed.startsWith(`${providerId}/`)) {
|
|
125
|
+
return trimmed;
|
|
144
126
|
}
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
return deduped;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function normalizeModelConfig(
|
|
151
|
-
input: ProviderConfigView['modelConfig'],
|
|
152
|
-
aliases: string[]
|
|
153
|
-
): ModelConfig {
|
|
154
|
-
if (!input) {
|
|
155
|
-
return {};
|
|
156
|
-
}
|
|
157
|
-
const normalized: ModelConfig = {};
|
|
158
|
-
for (const [rawModel, rawValue] of Object.entries(input)) {
|
|
159
|
-
const model = toProviderLocalModelId(rawModel, aliases);
|
|
160
|
-
if (!model) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
const entry: NonNullable<ModelConfig[string]> = {};
|
|
164
|
-
const supported = normalizeThinkingLevels(rawValue?.thinking?.supported);
|
|
165
|
-
if (supported.length > 0) {
|
|
166
|
-
const defaultLevel = parseThinkingLevel(rawValue?.thinking?.default);
|
|
167
|
-
entry.thinking =
|
|
168
|
-
defaultLevel && supported.includes(defaultLevel)
|
|
169
|
-
? { supported, default: defaultLevel }
|
|
170
|
-
: { supported };
|
|
171
|
-
}
|
|
172
|
-
if (rawValue?.vision === true) {
|
|
173
|
-
entry.vision = true;
|
|
174
|
-
}
|
|
175
|
-
if (entry.thinking || entry.vision === true) {
|
|
176
|
-
normalized[model] = entry;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return normalized;
|
|
127
|
+
return `${providerId}/${trimmed}`;
|
|
128
|
+
});
|
|
180
129
|
}
|
|
181
130
|
|
|
182
131
|
function normalizeModelConfigForModels(modelConfig: ModelConfig, models: string[]): ModelConfig {
|
|
@@ -205,6 +154,17 @@ function normalizeModelConfigForModels(modelConfig: ModelConfig, models: string[
|
|
|
205
154
|
return normalized;
|
|
206
155
|
}
|
|
207
156
|
|
|
157
|
+
function mergeModelConfig(
|
|
158
|
+
base: ProviderConfigView['modelConfig'],
|
|
159
|
+
override: ProviderConfigView['modelConfig'],
|
|
160
|
+
aliases: string[]
|
|
161
|
+
): ModelConfig {
|
|
162
|
+
return {
|
|
163
|
+
...normalizeModelConfigMap(base, aliases),
|
|
164
|
+
...normalizeModelConfigMap(override, aliases)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
208
168
|
function modelConfigEqual(left: ModelConfig, right: ModelConfig): boolean {
|
|
209
169
|
const leftKeys = Object.keys(left).sort();
|
|
210
170
|
const rightKeys = Object.keys(right).sort();
|
|
@@ -319,20 +279,20 @@ function shouldUsePillSelector(params: {
|
|
|
319
279
|
hasDefault: boolean;
|
|
320
280
|
optionCount: number;
|
|
321
281
|
}): boolean {
|
|
322
|
-
|
|
282
|
+
const { required, hasDefault, optionCount } = params;
|
|
283
|
+
return required && hasDefault && optionCount > 1 && optionCount <= 3;
|
|
323
284
|
}
|
|
324
285
|
|
|
325
286
|
export type { ModelConfig, ProviderAuthMethodOption, WireApiType };
|
|
326
287
|
export {
|
|
327
|
-
applyEnabledPatch,
|
|
328
288
|
EMPTY_PROVIDER_CONFIG,
|
|
329
289
|
formatThinkingLevelLabel,
|
|
330
290
|
headersEqual,
|
|
331
291
|
modelListsEqual,
|
|
332
292
|
modelConfigEqual,
|
|
293
|
+
mergeModelConfig,
|
|
333
294
|
normalizeHeaders,
|
|
334
295
|
normalizeModelList,
|
|
335
|
-
normalizeModelConfig,
|
|
336
296
|
normalizeModelConfigForModels,
|
|
337
297
|
resolveEditableModels,
|
|
338
298
|
resolvePreferredAuthMethodId,
|
|
@@ -3,12 +3,12 @@ import { useQueryClient } from '@tanstack/react-query';
|
|
|
3
3
|
import { CircleDotDashed, Trash2 } from 'lucide-react';
|
|
4
4
|
import { toast } from 'sonner';
|
|
5
5
|
import {
|
|
6
|
-
useConfig,
|
|
7
|
-
useConfigMeta,
|
|
8
6
|
useConfigSchema,
|
|
9
7
|
useDeleteProvider,
|
|
10
8
|
useImportProviderAuthFromCli,
|
|
11
9
|
usePollProviderAuth,
|
|
10
|
+
useProviders,
|
|
11
|
+
useProviderTemplates,
|
|
12
12
|
useStartProviderAuth,
|
|
13
13
|
useTestProviderConnection,
|
|
14
14
|
useUpdateProvider
|
|
@@ -26,20 +26,18 @@ import {
|
|
|
26
26
|
ConfigSplitPaneBody,
|
|
27
27
|
ConfigSplitPaneFooter,
|
|
28
28
|
ConfigSplitPaneHeader
|
|
29
|
-
} from '
|
|
30
|
-
import { ProviderAdvancedSettingsSection } from '
|
|
31
|
-
import { ProviderAuthSection } from '
|
|
32
|
-
import { ProviderEnabledField } from '../provider-enabled-field';
|
|
29
|
+
} from '@/shared/components/config-split-page';
|
|
30
|
+
import { ProviderAdvancedSettingsSection } from '@/shared/components/provider-advanced-settings-section';
|
|
31
|
+
import { ProviderAuthSection } from '@/shared/components/provider-auth-section';
|
|
33
32
|
import {
|
|
34
|
-
applyEnabledPatch,
|
|
35
33
|
EMPTY_PROVIDER_CONFIG,
|
|
36
34
|
formatThinkingLevelLabel,
|
|
37
35
|
headersEqual,
|
|
38
36
|
modelListsEqual,
|
|
39
37
|
modelConfigEqual,
|
|
38
|
+
mergeModelConfig,
|
|
40
39
|
normalizeHeaders,
|
|
41
40
|
normalizeModelList,
|
|
42
|
-
normalizeModelConfig,
|
|
43
41
|
normalizeModelConfigForModels,
|
|
44
42
|
resolveEditableModels,
|
|
45
43
|
resolvePreferredAuthMethodId,
|
|
@@ -51,8 +49,8 @@ import {
|
|
|
51
49
|
type WireApiType
|
|
52
50
|
} from './provider-form-support';
|
|
53
51
|
import { ProviderModelsSection } from './provider-models-section';
|
|
54
|
-
import type { PillSelectOption } from '
|
|
55
|
-
import { ProviderStatusBadge } from '
|
|
52
|
+
import type { PillSelectOption } from '@/shared/components/provider-pill-selector';
|
|
53
|
+
import { ProviderStatusBadge } from '@/shared/components/provider-status-badge';
|
|
56
54
|
|
|
57
55
|
type ProviderFormProps = {
|
|
58
56
|
providerName?: string;
|
|
@@ -61,8 +59,8 @@ type ProviderFormProps = {
|
|
|
61
59
|
|
|
62
60
|
export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormProps) {
|
|
63
61
|
const queryClient = useQueryClient();
|
|
64
|
-
const { data:
|
|
65
|
-
const { data:
|
|
62
|
+
const { data: providersView } = useProviders();
|
|
63
|
+
const { data: templatesView } = useProviderTemplates();
|
|
66
64
|
const { data: schema } = useConfigSchema();
|
|
67
65
|
const updateProvider = useUpdateProvider();
|
|
68
66
|
const deleteProvider = useDeleteProvider();
|
|
@@ -72,7 +70,6 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
72
70
|
const importProviderAuthFromCli = useImportProviderAuthFromCli();
|
|
73
71
|
|
|
74
72
|
const [apiKey, setApiKey] = useState('');
|
|
75
|
-
const [enabled, setEnabled] = useState(true);
|
|
76
73
|
const [apiBase, setApiBase] = useState('');
|
|
77
74
|
const [extraHeaders, setExtraHeaders] = useState<Record<string, string> | null>(null);
|
|
78
75
|
const [wireApi, setWireApi] = useState<WireApiType>('auto');
|
|
@@ -87,23 +84,23 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
87
84
|
const [authMethodId, setAuthMethodId] = useState('');
|
|
88
85
|
const authPollTimerRef = useRef<number | null>(null);
|
|
89
86
|
|
|
90
|
-
const
|
|
91
|
-
const
|
|
87
|
+
const providerConfig = providerName ? providersView?.providers[providerName] : null;
|
|
88
|
+
const providerSpec = templatesView?.providerTemplates.find((template) => template.providerType === providerConfig?.providerType);
|
|
92
89
|
const resolvedProviderConfig = providerConfig ?? EMPTY_PROVIDER_CONFIG;
|
|
93
90
|
const uiHints = schema?.uiHints;
|
|
94
|
-
const isCustomProvider = Boolean(
|
|
91
|
+
const isCustomProvider = Boolean(providerConfig?.isCustom);
|
|
95
92
|
const apiKeyHint = providerName ? hintForPath(`providers.${providerName}.apiKey`, uiHints) : undefined;
|
|
96
93
|
const apiBaseHint = providerName ? hintForPath(`providers.${providerName}.apiBase`, uiHints) : undefined;
|
|
97
94
|
const extraHeadersHint = providerName ? hintForPath(`providers.${providerName}.extraHeaders`, uiHints) : undefined;
|
|
98
95
|
const wireApiHint = providerName ? hintForPath(`providers.${providerName}.wireApi`, uiHints) : undefined;
|
|
99
|
-
const defaultDisplayName = providerSpec?.displayName || providerName || '';
|
|
96
|
+
const defaultDisplayName = providerSpec?.displayName || providerConfig?.displayName || providerName || '';
|
|
100
97
|
const currentDisplayName = (resolvedProviderConfig.displayName || '').trim();
|
|
101
98
|
const effectiveDisplayName = currentDisplayName || defaultDisplayName;
|
|
102
99
|
const currentEnabled = resolvedProviderConfig.enabled !== false;
|
|
103
100
|
const providerTitle = providerDisplayName.trim() || effectiveDisplayName || providerName || t('providersSelectPlaceholder');
|
|
104
101
|
const providerModelAliases = useMemo(
|
|
105
|
-
() => normalizeModelList([providerSpec?.modelPrefix ||
|
|
106
|
-
[providerName, providerSpec?.modelPrefix]
|
|
102
|
+
() => normalizeModelList([providerSpec?.modelPrefix || providerSpec?.providerType || '', providerName || '']),
|
|
103
|
+
[providerName, providerSpec?.modelPrefix, providerSpec?.providerType]
|
|
107
104
|
);
|
|
108
105
|
const defaultApiBase = providerSpec?.defaultApiBase || '';
|
|
109
106
|
const currentApiBase = resolvedProviderConfig.apiBase || defaultApiBase;
|
|
@@ -127,7 +124,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
127
124
|
const currentModelConfig = useMemo(
|
|
128
125
|
() =>
|
|
129
126
|
normalizeModelConfigForModels(
|
|
130
|
-
|
|
127
|
+
mergeModelConfig(providerSpec?.modelConfig, resolvedProviderConfig.modelConfig, providerModelAliases),
|
|
131
128
|
currentEditableModels
|
|
132
129
|
),
|
|
133
130
|
[currentEditableModels, providerModelAliases, providerSpec?.modelConfig, resolvedProviderConfig.modelConfig]
|
|
@@ -136,7 +133,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
136
133
|
const apiBaseHelpText =
|
|
137
134
|
providerSpec?.apiBaseHelp?.[language] || providerSpec?.apiBaseHelp?.en || apiBaseHint?.help || t('providerApiBaseHelp');
|
|
138
135
|
const providerAuth = providerSpec?.auth;
|
|
139
|
-
const providerAuthMethods = providerAuth?.methods ?? [];
|
|
136
|
+
const providerAuthMethods = useMemo(() => providerAuth?.methods ?? [], [providerAuth?.methods]);
|
|
140
137
|
const supportsWireApi = Boolean(providerSpec?.supportsWireApi) || isCustomProvider;
|
|
141
138
|
const providerAuthMethodOptions = useMemo(
|
|
142
139
|
() =>
|
|
@@ -147,8 +144,8 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
147
144
|
[providerAuthMethods, language]
|
|
148
145
|
);
|
|
149
146
|
const preferredAuthMethodId = useMemo(
|
|
150
|
-
() => resolvePreferredAuthMethodId({ providerName, methods: providerAuthMethods, defaultMethodId: providerAuth?.defaultMethodId, language }),
|
|
151
|
-
[providerName, providerAuth?.defaultMethodId, providerAuthMethods, language]
|
|
147
|
+
() => resolvePreferredAuthMethodId({ providerName: providerSpec?.providerType ?? providerName, methods: providerAuthMethods, defaultMethodId: providerAuth?.defaultMethodId, language }),
|
|
148
|
+
[providerName, providerAuth?.defaultMethodId, providerAuthMethods, language, providerSpec?.providerType]
|
|
152
149
|
);
|
|
153
150
|
const resolvedAuthMethodId = useMemo(() => {
|
|
154
151
|
if (!providerAuthMethods.length) {
|
|
@@ -207,7 +204,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
207
204
|
setAuthStatusMessage(t('providerAuthCompleted'));
|
|
208
205
|
toast.success(t('providerAuthCompleted'));
|
|
209
206
|
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
210
|
-
queryClient.invalidateQueries({ queryKey: ['
|
|
207
|
+
queryClient.invalidateQueries({ queryKey: ['providers'] });
|
|
211
208
|
return;
|
|
212
209
|
}
|
|
213
210
|
setAuthSessionId(null);
|
|
@@ -230,7 +227,6 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
230
227
|
useEffect(() => {
|
|
231
228
|
if (!providerName) {
|
|
232
229
|
setApiKey('');
|
|
233
|
-
setEnabled(true);
|
|
234
230
|
setApiBase('');
|
|
235
231
|
setExtraHeaders(null);
|
|
236
232
|
setWireApi('auto');
|
|
@@ -246,7 +242,6 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
246
242
|
}
|
|
247
243
|
|
|
248
244
|
setApiKey('');
|
|
249
|
-
setEnabled(currentEnabled);
|
|
250
245
|
setApiBase(currentApiBase);
|
|
251
246
|
setExtraHeaders(resolvedProviderConfig.extraHeaders || null);
|
|
252
247
|
setWireApi(currentWireApi);
|
|
@@ -261,7 +256,6 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
261
256
|
}, [
|
|
262
257
|
providerName,
|
|
263
258
|
currentApiBase,
|
|
264
|
-
currentEnabled,
|
|
265
259
|
resolvedProviderConfig.extraHeaders,
|
|
266
260
|
currentWireApi,
|
|
267
261
|
currentEditableModels,
|
|
@@ -280,19 +274,16 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
280
274
|
}
|
|
281
275
|
return (
|
|
282
276
|
apiKey.trim().length > 0 ||
|
|
283
|
-
enabled !== currentEnabled ||
|
|
284
277
|
apiBase.trim() !== currentApiBase.trim() ||
|
|
285
278
|
!headersEqual(extraHeaders, currentHeaders) ||
|
|
286
279
|
(supportsWireApi && wireApi !== currentWireApi) ||
|
|
287
280
|
!modelListsEqual(models, currentEditableModels) ||
|
|
288
281
|
!modelConfigEqual(modelConfig, currentModelConfig) ||
|
|
289
|
-
|
|
282
|
+
providerDisplayName.trim() !== effectiveDisplayName
|
|
290
283
|
);
|
|
291
284
|
}, [
|
|
292
285
|
providerName,
|
|
293
286
|
apiKey,
|
|
294
|
-
enabled,
|
|
295
|
-
currentEnabled,
|
|
296
287
|
apiBase,
|
|
297
288
|
currentApiBase,
|
|
298
289
|
extraHeaders,
|
|
@@ -304,7 +295,6 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
304
295
|
currentEditableModels,
|
|
305
296
|
modelConfig,
|
|
306
297
|
currentModelConfig,
|
|
307
|
-
isCustomProvider,
|
|
308
298
|
providerDisplayName,
|
|
309
299
|
effectiveDisplayName
|
|
310
300
|
]);
|
|
@@ -314,6 +304,10 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
314
304
|
if (!next) {
|
|
315
305
|
return;
|
|
316
306
|
}
|
|
307
|
+
if (next.includes('/')) {
|
|
308
|
+
toast.error(t('providerModelInvalidProviderPrefix'));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
317
311
|
if (models.includes(next)) {
|
|
318
312
|
setModelDraft('');
|
|
319
313
|
return;
|
|
@@ -403,13 +397,12 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
403
397
|
const normalizedHeaders = normalizeHeaders(extraHeaders);
|
|
404
398
|
const trimmedDisplayName = providerDisplayName.trim();
|
|
405
399
|
|
|
406
|
-
if (
|
|
400
|
+
if (trimmedDisplayName !== effectiveDisplayName) {
|
|
407
401
|
payload.displayName = trimmedDisplayName.length > 0 ? trimmedDisplayName : null;
|
|
408
402
|
}
|
|
409
403
|
if (trimmedApiKey.length > 0) {
|
|
410
404
|
payload.apiKey = trimmedApiKey;
|
|
411
405
|
}
|
|
412
|
-
applyEnabledPatch(payload, enabled, currentEnabled);
|
|
413
406
|
if (trimmedApiBase !== currentApiBase.trim()) {
|
|
414
407
|
payload.apiBase = trimmedApiBase.length > 0 && trimmedApiBase !== defaultApiBase ? trimmedApiBase : null;
|
|
415
408
|
}
|
|
@@ -420,7 +413,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
420
413
|
payload.wireApi = wireApi;
|
|
421
414
|
}
|
|
422
415
|
if (!modelListsEqual(models, currentEditableModels)) {
|
|
423
|
-
payload.models = serializeModelsForSave(models,
|
|
416
|
+
payload.models = serializeModelsForSave(models, providerName);
|
|
424
417
|
}
|
|
425
418
|
if (!modelConfigEqual(modelConfig, currentModelConfig)) {
|
|
426
419
|
payload.modelConfig = normalizeModelConfigForModels(modelConfig, models);
|
|
@@ -465,7 +458,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
465
458
|
};
|
|
466
459
|
|
|
467
460
|
const handleDeleteProvider = async () => {
|
|
468
|
-
if (!providerName
|
|
461
|
+
if (!providerName) {
|
|
469
462
|
return;
|
|
470
463
|
}
|
|
471
464
|
if (!window.confirm(t('providerDeleteConfirm'))) {
|
|
@@ -517,7 +510,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
517
510
|
setAuthStatusMessage(`${t('providerAuthImportStatusPrefix')}${result.expiresAt ? ` (expires: ${result.expiresAt})` : ''}`);
|
|
518
511
|
toast.success(t('providerAuthImportSuccess'));
|
|
519
512
|
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
520
|
-
queryClient.invalidateQueries({ queryKey: ['
|
|
513
|
+
queryClient.invalidateQueries({ queryKey: ['providers'] });
|
|
521
514
|
} catch (error) {
|
|
522
515
|
const message = error instanceof Error ? error.message : String(error);
|
|
523
516
|
setAuthStatusMessage(message);
|
|
@@ -525,7 +518,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
525
518
|
}
|
|
526
519
|
};
|
|
527
520
|
|
|
528
|
-
if (!providerName || !
|
|
521
|
+
if (!providerName || !providerConfig) {
|
|
529
522
|
return (
|
|
530
523
|
<ConfigSplitEmptyPane>
|
|
531
524
|
<div>
|
|
@@ -542,17 +535,15 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
542
535
|
<div className='flex items-center justify-between'>
|
|
543
536
|
<h3 className='truncate text-lg font-semibold text-gray-900'>{providerTitle}</h3>
|
|
544
537
|
<div className='flex items-center gap-3'>
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
</button>
|
|
555
|
-
)}
|
|
538
|
+
<button
|
|
539
|
+
type='button'
|
|
540
|
+
onClick={handleDeleteProvider}
|
|
541
|
+
disabled={deleteProvider.isPending}
|
|
542
|
+
className='text-gray-400 transition-colors hover:text-red-500'
|
|
543
|
+
title={t('providerDelete')}
|
|
544
|
+
>
|
|
545
|
+
<Trash2 className='h-4 w-4' />
|
|
546
|
+
</button>
|
|
556
547
|
<ProviderStatusBadge enabled={currentEnabled} apiKeySet={resolvedProviderConfig.apiKeySet} />
|
|
557
548
|
</div>
|
|
558
549
|
</div>
|
|
@@ -560,24 +551,20 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
560
551
|
|
|
561
552
|
<form onSubmit={handleSubmit} className='flex min-h-0 flex-1 flex-col'>
|
|
562
553
|
<ConfigSplitPaneBody className='space-y-5 px-6 py-5'>
|
|
563
|
-
<
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
/>
|
|
578
|
-
<p className='text-xs text-gray-500'>{t('providerDisplayNameHelpShort')}</p>
|
|
579
|
-
</div>
|
|
580
|
-
)}
|
|
554
|
+
<div className='space-y-2'>
|
|
555
|
+
<Label htmlFor='providerDisplayName' className='text-sm font-medium text-gray-900'>
|
|
556
|
+
{t('providerDisplayName')}
|
|
557
|
+
</Label>
|
|
558
|
+
<Input
|
|
559
|
+
id='providerDisplayName'
|
|
560
|
+
type='text'
|
|
561
|
+
value={providerDisplayName}
|
|
562
|
+
onChange={(e) => setProviderDisplayName(e.target.value)}
|
|
563
|
+
placeholder={defaultDisplayName || t('providerDisplayNamePlaceholder')}
|
|
564
|
+
className='rounded-xl'
|
|
565
|
+
/>
|
|
566
|
+
<p className='text-xs text-gray-500'>{t('providerDisplayNameHelpShort')}</p>
|
|
567
|
+
</div>
|
|
581
568
|
|
|
582
569
|
<div className='space-y-2'>
|
|
583
570
|
<Label htmlFor='apiKey' className='text-sm font-medium text-gray-900'>
|