@nextclaw/ui 0.12.34 → 0.12.35-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +127 -0
- package/dist/assets/{api-rASx2-N4.js → api-De1PAch0.js} +3 -3
- package/dist/assets/{book-open-BcnAiKde.js → book-open-825Pcd0z.js} +1 -1
- package/dist/assets/channels-list-page-CGjXDSD-.js +8 -0
- package/dist/assets/chat-page-RsUTK_bY.js +105 -0
- package/dist/assets/{config-split-page-CcrEUtwu.js → config-split-page-BvfxFQSb.js} +1 -1
- package/dist/assets/{createLucideIcon-DzY6wN61.js → createLucideIcon-CeNHY6BW.js} +1 -1
- package/dist/assets/desktop-update-config-B_YIv5ZJ.js +1 -0
- package/dist/assets/{dist-NBjcwlsw.js → dist-uH7tiW6E.js} +1 -1
- package/dist/assets/{doc-browser-D2pq4IHR.js → doc-browser-CDe9yz9n.js} +1 -1
- package/dist/assets/doc-browser-DwT64GuF.js +1 -0
- package/dist/assets/{doc-browser-context-BPeeiNJ_.js → doc-browser-context-CQAx_92e.js} +1 -1
- package/dist/assets/{es2015-CVAuMqlM.js → es2015-c9XYpli5.js} +1 -1
- package/dist/assets/{external-link-CpEvG65F.js → external-link-BE1deZ_c.js} +1 -1
- package/dist/assets/i18n-DnubSHoK.js +1 -0
- package/dist/assets/index-6WZ6JjaU.css +1 -0
- package/dist/assets/index-BjH_p3R6.js +103 -0
- package/dist/assets/{key-round-DUq47t0P.js → key-round-CHo3gqpO.js} +1 -1
- package/dist/assets/mcp-marketplace-page-0C214hGi.js +40 -0
- package/dist/assets/mcp-marketplace-page-amWcMncu.js +1 -0
- package/dist/assets/model-config-B8zNrkag.js +1 -0
- package/dist/assets/notice-card-DO33_LzD.js +5 -0
- package/dist/assets/play-CqlWbVNo.js +1 -0
- package/dist/assets/plus-p61tQh2-.js +1 -0
- package/dist/assets/provider-scoped-model-input-B0_r_4EY.js +1 -0
- package/dist/assets/providers-list-B6v3foi7.js +1 -0
- package/dist/assets/remote-k671rLmY.js +1 -0
- package/dist/assets/{rotate-cw-BN9yjccP.js → rotate-cw-CYUByuqo.js} +1 -1
- package/dist/assets/runtime-config-page-DgH-XJwK.js +1 -0
- package/dist/assets/{save-CO_4qf6b.js → save-B6Ga8JIq.js} +1 -1
- package/dist/assets/{search-CRtQwr-h.js → search-5kKpgNoP.js} +1 -1
- package/dist/assets/search-config-DPZ6d9y6.js +1 -0
- package/dist/assets/{secrets-config-DFbHCb9n.js → secrets-config-CrXA11bo.js} +2 -2
- package/dist/assets/{select-HI5kiwX_.js → select-C0hGOCOK.js} +1 -1
- package/dist/assets/{setting-row-B4QnghjV.js → setting-row-xsM_0nTd.js} +1 -1
- package/dist/assets/{tabs-custom-D48zdZoc.js → tabs-custom-Bnqfhe4p.js} +1 -1
- package/dist/assets/{tag-chip-BPgMfp9l.js → tag-chip-cktJJMzh.js} +1 -1
- package/dist/assets/x-DDQw-C2x.js +1 -0
- package/dist/index.html +17 -29
- package/package.json +9 -9
- package/src/app/components/layout/app-layout.test.tsx +49 -4
- package/src/app/components/layout/app-layout.tsx +4 -7
- package/src/app/components/layout/settings-entry-page.test.tsx +9 -0
- package/src/app/components/layout/sidebar.layout.test.tsx +2 -2
- package/src/app/components/layout/sidebar.tsx +3 -3
- package/src/app/configs/app-navigation.config.ts +0 -12
- package/src/app/hooks/use-app-event-consumers.ts +1 -6
- package/src/app/hooks/use-viewport-layout.ts +0 -1
- package/src/app/index.tsx +2 -10
- package/src/features/channels/components/config/channel-form.tsx +85 -47
- package/src/features/channels/components/config/weixin-channel-auth-section.test.tsx +45 -1
- package/src/features/channels/components/config/weixin-channel-auth-section.tsx +119 -1
- package/src/features/channels/hooks/use-channel-auth.ts +8 -1
- package/src/features/channels/index.ts +1 -1
- package/src/features/chat/components/conversation/chat-conversation-panel.test.tsx +2 -2
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +2 -7
- package/src/features/chat/components/layout/chat-sidebar.test.tsx +27 -3
- package/src/features/chat/components/layout/chat-sidebar.tsx +2 -3
- package/src/features/chat/managers/chat-session-list.manager.test.ts +12 -11
- package/src/features/chat/managers/chat-session-list.manager.ts +2 -1
- package/src/features/chat/managers/chat-ui.manager.ts +2 -2
- package/src/features/chat/managers/ncp-chat-input.manager.test.ts +3 -3
- package/src/features/chat/pages/ncp-chat-page.test.ts +3 -0
- package/src/features/chat/pages/ncp-chat-page.tsx +19 -19
- package/src/features/chat/utils/chat-session-route.utils.test.ts +14 -0
- package/src/features/chat/utils/chat-session-route.utils.ts +3 -1
- package/src/features/chat/utils/ncp-chat-runtime-availability.utils.test.ts +6 -6
- package/src/features/marketplace/components/marketplace-list-card.tsx +4 -36
- package/src/features/marketplace/components/marketplace-page-detail.test.tsx +0 -1
- package/src/features/marketplace/components/marketplace-page.test.tsx +9 -128
- package/src/features/marketplace/components/marketplace-page.tsx +16 -72
- package/src/features/marketplace/hooks/use-marketplace-curated-scene-route.ts +1 -1
- package/src/features/marketplace/hooks/use-marketplace.ts +1 -10
- package/src/features/marketplace/utils/marketplace-installed-cache.utils.test.ts +23 -21
- package/src/features/system-status/managers/system-status.manager.bootstrap-polling.test.ts +12 -12
- package/src/features/system-status/managers/system-status.manager.test.ts +6 -6
- package/src/index.css +1 -1
- package/src/platforms/desktop/components/desktop-app-shell.test.tsx +29 -13
- package/src/platforms/desktop/components/desktop-app-shell.tsx +14 -16
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +2 -3
- package/src/platforms/mobile/components/mobile-app-shell.tsx +3 -1
- package/src/platforms/mobile/components/mobile-topbar.tsx +6 -5
- package/src/platforms/mobile/index.ts +1 -0
- package/src/shared/components/config/provider-form-support.ts +51 -33
- package/src/shared/components/config/provider-form.tsx +68 -35
- package/src/shared/components/config/provider-models-section.tsx +19 -4
- package/src/shared/components/model-config.test.tsx +2 -0
- package/src/shared/components/ui/button.tsx +1 -1
- package/src/shared/lib/api/channel-auth.types.ts +8 -0
- package/src/shared/lib/api/types.ts +17 -20
- package/src/shared/lib/api/utils/channel-auth.utils.ts +10 -1
- package/src/shared/lib/api/utils/marketplace.utils.ts +0 -5
- package/src/shared/lib/i18n/channel-auth.constants.ts +24 -10
- package/src/shared/lib/i18n/chat-labels.utils.ts +2 -2
- package/src/shared/lib/i18n/index.ts +2 -0
- package/src/shared/lib/i18n/marketplace.ts +0 -16
- package/src/shared/lib/provider-models/index.ts +41 -13
- package/src/shared/lib/ui-document-title/index.ts +0 -1
- package/dist/assets/app-manager-provider-CUkcbdWk.js +0 -1
- package/dist/assets/channels-list-page-ZWYqfcCe.js +0 -8
- package/dist/assets/chat-page-ACm58eUO.js +0 -1
- package/dist/assets/cpu-DKc3bQic.js +0 -3
- package/dist/assets/desktop-C-cDRCBY.js +0 -3
- package/dist/assets/desktop-update-config-DAD9JN9Q.js +0 -1
- package/dist/assets/dialog-CR80q3Rm.js +0 -5
- package/dist/assets/doc-browser-DhIkcy0-.js +0 -1
- package/dist/assets/doc-browser-fLs0TcdS.js +0 -1
- package/dist/assets/download-CMM8po31.js +0 -1
- package/dist/assets/i18n-D1144VAA.js +0 -1
- package/dist/assets/index-DV57tBmg.css +0 -1
- package/dist/assets/index-Db9Cufsy.js +0 -100
- package/dist/assets/marketplace-page-CAp_gpNe.js +0 -105
- package/dist/assets/marketplace-page-PPY5uMHK.js +0 -1
- package/dist/assets/mcp-marketplace-page-87__92b1.js +0 -1
- package/dist/assets/mcp-marketplace-page-C6HENB6V.js +0 -40
- package/dist/assets/model-config-CK3a8DWH.js +0 -1
- package/dist/assets/notice-card-CF4AJPM3.js +0 -1
- package/dist/assets/play-AqrNslHI.js +0 -1
- package/dist/assets/plus-B-YHtTNC.js +0 -1
- package/dist/assets/popover-DeAliE_4.js +0 -1
- package/dist/assets/provider-scoped-model-input-B8LJ6RCt.js +0 -1
- package/dist/assets/providers-list-BTPjqNdK.js +0 -1
- package/dist/assets/refresh-cw-CrbD8EkT.js +0 -1
- package/dist/assets/remote-DiPWGRZc.js +0 -1
- package/dist/assets/runtime-config-page-Bb9he7t8.js +0 -1
- package/dist/assets/search-config-BkbSXa9d.js +0 -1
- package/dist/assets/settings-MWL2SMyk.js +0 -1
- package/dist/assets/sparkles-BmgOD4nY.js +0 -1
- package/dist/assets/status-dot-l3kPFdq_.js +0 -1
- package/dist/assets/use-config-BFqzVF8Y.js +0 -1
- package/dist/assets/use-infinite-scroll-loader-DXhw9W4l.js +0 -1
- package/dist/assets/x-BeyYA_h6.js +0 -1
- /package/dist/assets/{config-hints-BNfpOL4J.js → config-hints-DGfwbumO.js} +0 -0
|
@@ -3,7 +3,10 @@ import type { getLanguage } from '@/shared/lib/i18n';
|
|
|
3
3
|
import type { ProviderConfigUpdate, ProviderConfigView, ThinkingLevel } from '@/shared/lib/api';
|
|
4
4
|
|
|
5
5
|
type WireApiType = 'auto' | 'chat' | 'responses';
|
|
6
|
-
type
|
|
6
|
+
type ModelConfig = Record<string, {
|
|
7
|
+
thinking?: { supported: ThinkingLevel[]; default?: ThinkingLevel | null };
|
|
8
|
+
vision?: boolean;
|
|
9
|
+
}>;
|
|
7
10
|
type ProviderAuthMethodOption = {
|
|
8
11
|
id: string;
|
|
9
12
|
};
|
|
@@ -17,7 +20,7 @@ const EMPTY_PROVIDER_CONFIG: ProviderConfigView = {
|
|
|
17
20
|
extraHeaders: null,
|
|
18
21
|
wireApi: null,
|
|
19
22
|
models: [],
|
|
20
|
-
|
|
23
|
+
modelConfig: {}
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
const THINKING_LEVELS: ThinkingLevel[] = ['off', 'minimal', 'low', 'medium', 'high', 'adaptive', 'xhigh'];
|
|
@@ -144,53 +147,65 @@ function normalizeThinkingLevels(values: unknown): ThinkingLevel[] {
|
|
|
144
147
|
return deduped;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
function
|
|
148
|
-
input: ProviderConfigView['
|
|
150
|
+
function normalizeModelConfig(
|
|
151
|
+
input: ProviderConfigView['modelConfig'],
|
|
149
152
|
aliases: string[]
|
|
150
|
-
):
|
|
153
|
+
): ModelConfig {
|
|
151
154
|
if (!input) {
|
|
152
155
|
return {};
|
|
153
156
|
}
|
|
154
|
-
const normalized:
|
|
157
|
+
const normalized: ModelConfig = {};
|
|
155
158
|
for (const [rawModel, rawValue] of Object.entries(input)) {
|
|
156
159
|
const model = toProviderLocalModelId(rawModel, aliases);
|
|
157
160
|
if (!model) {
|
|
158
161
|
continue;
|
|
159
162
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
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;
|
|
163
177
|
}
|
|
164
|
-
const defaultLevel = parseThinkingLevel(rawValue?.default);
|
|
165
|
-
normalized[model] =
|
|
166
|
-
defaultLevel && supported.includes(defaultLevel)
|
|
167
|
-
? { supported, default: defaultLevel }
|
|
168
|
-
: { supported };
|
|
169
178
|
}
|
|
170
179
|
return normalized;
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
function
|
|
182
|
+
function normalizeModelConfigForModels(modelConfig: ModelConfig, models: string[]): ModelConfig {
|
|
174
183
|
const modelSet = new Set(models.map((item) => item.trim()).filter(Boolean));
|
|
175
|
-
const normalized:
|
|
176
|
-
for (const [model, entry] of Object.entries(
|
|
184
|
+
const normalized: ModelConfig = {};
|
|
185
|
+
for (const [model, entry] of Object.entries(modelConfig)) {
|
|
177
186
|
if (!modelSet.has(model)) {
|
|
178
187
|
continue;
|
|
179
188
|
}
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
const nextEntry: NonNullable<ModelConfig[string]> = {};
|
|
190
|
+
const supported = normalizeThinkingLevels(entry.thinking?.supported);
|
|
191
|
+
if (supported.length > 0) {
|
|
192
|
+
const defaultLevel = parseThinkingLevel(entry.thinking?.default);
|
|
193
|
+
nextEntry.thinking =
|
|
194
|
+
defaultLevel && supported.includes(defaultLevel)
|
|
195
|
+
? { supported, default: defaultLevel }
|
|
196
|
+
: { supported };
|
|
197
|
+
}
|
|
198
|
+
if (entry.vision === true) {
|
|
199
|
+
nextEntry.vision = true;
|
|
200
|
+
}
|
|
201
|
+
if (nextEntry.thinking || nextEntry.vision === true) {
|
|
202
|
+
normalized[model] = nextEntry;
|
|
183
203
|
}
|
|
184
|
-
const defaultLevel = parseThinkingLevel(entry.default);
|
|
185
|
-
normalized[model] =
|
|
186
|
-
defaultLevel && supported.includes(defaultLevel)
|
|
187
|
-
? { supported, default: defaultLevel }
|
|
188
|
-
: { supported };
|
|
189
204
|
}
|
|
190
205
|
return normalized;
|
|
191
206
|
}
|
|
192
207
|
|
|
193
|
-
function
|
|
208
|
+
function modelConfigEqual(left: ModelConfig, right: ModelConfig): boolean {
|
|
194
209
|
const leftKeys = Object.keys(left).sort();
|
|
195
210
|
const rightKeys = Object.keys(right).sort();
|
|
196
211
|
if (leftKeys.length !== rightKeys.length) {
|
|
@@ -206,12 +221,15 @@ function modelThinkingEqual(left: ModelThinkingConfig, right: ModelThinkingConfi
|
|
|
206
221
|
if (!leftEntry || !rightEntry) {
|
|
207
222
|
return false;
|
|
208
223
|
}
|
|
209
|
-
const leftSupported = [...leftEntry.supported].sort();
|
|
210
|
-
const rightSupported = [...rightEntry.supported].sort();
|
|
224
|
+
const leftSupported = [...(leftEntry.thinking?.supported ?? [])].sort();
|
|
225
|
+
const rightSupported = [...(rightEntry.thinking?.supported ?? [])].sort();
|
|
211
226
|
if (!modelListsEqual(leftSupported, rightSupported)) {
|
|
212
227
|
return false;
|
|
213
228
|
}
|
|
214
|
-
if ((leftEntry.default ?? null) !== (rightEntry.default ?? null)) {
|
|
229
|
+
if ((leftEntry.thinking?.default ?? null) !== (rightEntry.thinking?.default ?? null)) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
if ((leftEntry.vision ?? false) !== (rightEntry.vision ?? false)) {
|
|
215
233
|
return false;
|
|
216
234
|
}
|
|
217
235
|
}
|
|
@@ -304,18 +322,18 @@ function shouldUsePillSelector(params: {
|
|
|
304
322
|
return params.required && params.hasDefault && params.optionCount > 1 && params.optionCount <= 3;
|
|
305
323
|
}
|
|
306
324
|
|
|
307
|
-
export type {
|
|
325
|
+
export type { ModelConfig, ProviderAuthMethodOption, WireApiType };
|
|
308
326
|
export {
|
|
309
327
|
applyEnabledPatch,
|
|
310
328
|
EMPTY_PROVIDER_CONFIG,
|
|
311
329
|
formatThinkingLevelLabel,
|
|
312
330
|
headersEqual,
|
|
313
331
|
modelListsEqual,
|
|
314
|
-
|
|
332
|
+
modelConfigEqual,
|
|
315
333
|
normalizeHeaders,
|
|
316
334
|
normalizeModelList,
|
|
317
|
-
|
|
318
|
-
|
|
335
|
+
normalizeModelConfig,
|
|
336
|
+
normalizeModelConfigForModels,
|
|
319
337
|
resolveEditableModels,
|
|
320
338
|
resolvePreferredAuthMethodId,
|
|
321
339
|
serializeModelsForSave,
|
|
@@ -36,18 +36,18 @@ import {
|
|
|
36
36
|
formatThinkingLevelLabel,
|
|
37
37
|
headersEqual,
|
|
38
38
|
modelListsEqual,
|
|
39
|
-
|
|
39
|
+
modelConfigEqual,
|
|
40
40
|
normalizeHeaders,
|
|
41
41
|
normalizeModelList,
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
normalizeModelConfig,
|
|
43
|
+
normalizeModelConfigForModels,
|
|
44
44
|
resolveEditableModels,
|
|
45
45
|
resolvePreferredAuthMethodId,
|
|
46
46
|
serializeModelsForSave,
|
|
47
47
|
shouldUsePillSelector,
|
|
48
48
|
THINKING_LEVELS,
|
|
49
49
|
toProviderLocalModelId,
|
|
50
|
-
type
|
|
50
|
+
type ModelConfig,
|
|
51
51
|
type WireApiType
|
|
52
52
|
} from './provider-form-support';
|
|
53
53
|
import { ProviderModelsSection } from './provider-models-section';
|
|
@@ -77,7 +77,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
77
77
|
const [extraHeaders, setExtraHeaders] = useState<Record<string, string> | null>(null);
|
|
78
78
|
const [wireApi, setWireApi] = useState<WireApiType>('auto');
|
|
79
79
|
const [models, setModels] = useState<string[]>([]);
|
|
80
|
-
const [
|
|
80
|
+
const [modelConfig, setModelConfig] = useState<ModelConfig>({});
|
|
81
81
|
const [modelDraft, setModelDraft] = useState('');
|
|
82
82
|
const [providerDisplayName, setProviderDisplayName] = useState('');
|
|
83
83
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
@@ -124,13 +124,13 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
124
124
|
[resolvedProviderConfig.models, providerModelAliases]
|
|
125
125
|
);
|
|
126
126
|
const currentEditableModels = useMemo(() => resolveEditableModels(defaultModels, currentModels), [defaultModels, currentModels]);
|
|
127
|
-
const
|
|
127
|
+
const currentModelConfig = useMemo(
|
|
128
128
|
() =>
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
normalizeModelConfigForModels(
|
|
130
|
+
normalizeModelConfig(resolvedProviderConfig.modelConfig ?? providerSpec?.modelConfig, providerModelAliases),
|
|
131
131
|
currentEditableModels
|
|
132
132
|
),
|
|
133
|
-
[currentEditableModels, providerModelAliases, resolvedProviderConfig.
|
|
133
|
+
[currentEditableModels, providerModelAliases, providerSpec?.modelConfig, resolvedProviderConfig.modelConfig]
|
|
134
134
|
);
|
|
135
135
|
const language = getLanguage();
|
|
136
136
|
const apiBaseHelpText =
|
|
@@ -235,7 +235,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
235
235
|
setExtraHeaders(null);
|
|
236
236
|
setWireApi('auto');
|
|
237
237
|
setModels([]);
|
|
238
|
-
|
|
238
|
+
setModelConfig({});
|
|
239
239
|
setModelDraft('');
|
|
240
240
|
setProviderDisplayName('');
|
|
241
241
|
setAuthSessionId(null);
|
|
@@ -251,7 +251,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
251
251
|
setExtraHeaders(resolvedProviderConfig.extraHeaders || null);
|
|
252
252
|
setWireApi(currentWireApi);
|
|
253
253
|
setModels(currentEditableModels);
|
|
254
|
-
|
|
254
|
+
setModelConfig(currentModelConfig);
|
|
255
255
|
setModelDraft('');
|
|
256
256
|
setProviderDisplayName(effectiveDisplayName);
|
|
257
257
|
setAuthSessionId(null);
|
|
@@ -265,14 +265,14 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
265
265
|
resolvedProviderConfig.extraHeaders,
|
|
266
266
|
currentWireApi,
|
|
267
267
|
currentEditableModels,
|
|
268
|
-
|
|
268
|
+
currentModelConfig,
|
|
269
269
|
effectiveDisplayName,
|
|
270
270
|
preferredAuthMethodId,
|
|
271
271
|
clearAuthPollTimer
|
|
272
272
|
]);
|
|
273
273
|
|
|
274
274
|
useEffect(() => () => clearAuthPollTimer(), [clearAuthPollTimer]);
|
|
275
|
-
useEffect(() =>
|
|
275
|
+
useEffect(() => setModelConfig((prev) => normalizeModelConfigForModels(prev, models)), [models]);
|
|
276
276
|
|
|
277
277
|
const hasChanges = useMemo(() => {
|
|
278
278
|
if (!providerName) {
|
|
@@ -285,7 +285,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
285
285
|
!headersEqual(extraHeaders, currentHeaders) ||
|
|
286
286
|
(supportsWireApi && wireApi !== currentWireApi) ||
|
|
287
287
|
!modelListsEqual(models, currentEditableModels) ||
|
|
288
|
-
!
|
|
288
|
+
!modelConfigEqual(modelConfig, currentModelConfig) ||
|
|
289
289
|
(isCustomProvider && providerDisplayName.trim() !== effectiveDisplayName)
|
|
290
290
|
);
|
|
291
291
|
}, [
|
|
@@ -302,8 +302,8 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
302
302
|
currentWireApi,
|
|
303
303
|
models,
|
|
304
304
|
currentEditableModels,
|
|
305
|
-
|
|
306
|
-
|
|
305
|
+
modelConfig,
|
|
306
|
+
currentModelConfig,
|
|
307
307
|
isCustomProvider,
|
|
308
308
|
providerDisplayName,
|
|
309
309
|
effectiveDisplayName
|
|
@@ -323,42 +323,74 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
323
323
|
};
|
|
324
324
|
|
|
325
325
|
const toggleModelThinkingLevel = (modelName: string, level: ThinkingLevel) => {
|
|
326
|
-
|
|
326
|
+
setModelConfig((prev) => {
|
|
327
327
|
const currentEntry = prev[modelName];
|
|
328
|
-
const currentLevels = currentEntry?.supported ?? [];
|
|
328
|
+
const currentLevels = currentEntry?.thinking?.supported ?? [];
|
|
329
329
|
const nextLevels = currentLevels.includes(level)
|
|
330
330
|
? currentLevels.filter((item) => item !== level)
|
|
331
331
|
: THINKING_LEVELS.filter((item) => item === level || currentLevels.includes(item));
|
|
332
|
-
if (nextLevels.length === 0) {
|
|
333
|
-
const next = { ...prev };
|
|
334
|
-
delete next[modelName];
|
|
335
|
-
return next;
|
|
336
|
-
}
|
|
337
332
|
const nextDefault =
|
|
338
|
-
currentEntry?.default && nextLevels.includes(currentEntry.default)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
333
|
+
currentEntry?.thinking?.default && nextLevels.includes(currentEntry.thinking.default)
|
|
334
|
+
? currentEntry.thinking.default
|
|
335
|
+
: undefined;
|
|
336
|
+
const nextEntry = {
|
|
337
|
+
...currentEntry,
|
|
338
|
+
thinking:
|
|
339
|
+
nextLevels.length > 0
|
|
340
|
+
? nextDefault
|
|
341
|
+
? { supported: nextLevels, default: nextDefault }
|
|
342
|
+
: { supported: nextLevels }
|
|
343
|
+
: undefined
|
|
342
344
|
};
|
|
345
|
+
const next = { ...prev };
|
|
346
|
+
if (nextEntry.thinking || nextEntry.vision === true) {
|
|
347
|
+
next[modelName] = nextEntry;
|
|
348
|
+
} else {
|
|
349
|
+
delete next[modelName];
|
|
350
|
+
}
|
|
351
|
+
return next;
|
|
343
352
|
});
|
|
344
353
|
};
|
|
345
354
|
|
|
346
355
|
const setModelThinkingDefault = (modelName: string, level: ThinkingLevel | null) => {
|
|
347
|
-
|
|
356
|
+
setModelConfig((prev) => {
|
|
348
357
|
const currentEntry = prev[modelName];
|
|
349
|
-
|
|
358
|
+
const currentThinking = currentEntry?.thinking;
|
|
359
|
+
if (!currentThinking || currentThinking.supported.length === 0) {
|
|
350
360
|
return prev;
|
|
351
361
|
}
|
|
352
|
-
if (level && !
|
|
362
|
+
if (level && !currentThinking.supported.includes(level)) {
|
|
353
363
|
return prev;
|
|
354
364
|
}
|
|
355
365
|
return {
|
|
356
366
|
...prev,
|
|
357
|
-
[modelName]:
|
|
367
|
+
[modelName]: {
|
|
368
|
+
...currentEntry,
|
|
369
|
+
thinking: level
|
|
370
|
+
? { supported: currentThinking.supported, default: level }
|
|
371
|
+
: { supported: currentThinking.supported }
|
|
372
|
+
}
|
|
358
373
|
};
|
|
359
374
|
});
|
|
360
375
|
};
|
|
361
376
|
|
|
377
|
+
const setModelVision = (modelName: string, vision: boolean) => {
|
|
378
|
+
setModelConfig((prev) => {
|
|
379
|
+
const currentEntry = prev[modelName];
|
|
380
|
+
const nextEntry = {
|
|
381
|
+
...currentEntry,
|
|
382
|
+
vision: vision ? true : undefined
|
|
383
|
+
};
|
|
384
|
+
const next = { ...prev };
|
|
385
|
+
if (nextEntry.thinking || nextEntry.vision === true) {
|
|
386
|
+
next[modelName] = nextEntry;
|
|
387
|
+
} else {
|
|
388
|
+
delete next[modelName];
|
|
389
|
+
}
|
|
390
|
+
return next;
|
|
391
|
+
});
|
|
392
|
+
};
|
|
393
|
+
|
|
362
394
|
const handleSubmit = (e: React.FormEvent) => {
|
|
363
395
|
e.preventDefault();
|
|
364
396
|
if (!providerName) {
|
|
@@ -390,8 +422,8 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
390
422
|
if (!modelListsEqual(models, currentEditableModels)) {
|
|
391
423
|
payload.models = serializeModelsForSave(models, defaultModels);
|
|
392
424
|
}
|
|
393
|
-
if (!
|
|
394
|
-
payload.
|
|
425
|
+
if (!modelConfigEqual(modelConfig, currentModelConfig)) {
|
|
426
|
+
payload.modelConfig = normalizeModelConfigForModels(modelConfig, models);
|
|
395
427
|
}
|
|
396
428
|
|
|
397
429
|
updateProvider.mutate({ provider: providerName, data: payload });
|
|
@@ -596,7 +628,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
596
628
|
|
|
597
629
|
<ProviderModelsSection
|
|
598
630
|
models={models}
|
|
599
|
-
|
|
631
|
+
modelConfig={modelConfig}
|
|
600
632
|
modelDraft={modelDraft}
|
|
601
633
|
showModelInput={showModelInput}
|
|
602
634
|
onModelDraftChange={setModelDraft}
|
|
@@ -604,7 +636,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
604
636
|
onAddModel={handleAddModel}
|
|
605
637
|
onRemoveModel={(modelName) => {
|
|
606
638
|
setModels((prev) => prev.filter((name) => name !== modelName));
|
|
607
|
-
|
|
639
|
+
setModelConfig((prev) => {
|
|
608
640
|
const next = { ...prev };
|
|
609
641
|
delete next[modelName];
|
|
610
642
|
return next;
|
|
@@ -612,6 +644,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
|
|
|
612
644
|
}}
|
|
613
645
|
onToggleModelThinkingLevel={toggleModelThinkingLevel}
|
|
614
646
|
onSetModelThinkingDefault={setModelThinkingDefault}
|
|
647
|
+
onSetModelVision={setModelVision}
|
|
615
648
|
thinkingLevels={THINKING_LEVELS}
|
|
616
649
|
formatThinkingLevelLabel={formatThinkingLevelLabel}
|
|
617
650
|
/>
|
|
@@ -3,15 +3,19 @@ import { Input } from '@/shared/components/ui/input';
|
|
|
3
3
|
import { Label } from '@/shared/components/ui/label';
|
|
4
4
|
import { Popover, PopoverContent, PopoverTrigger } from '@/shared/components/ui/popover';
|
|
5
5
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/components/ui/select';
|
|
6
|
+
import { Switch } from '@/shared/components/ui/switch';
|
|
6
7
|
import { t } from '@/shared/lib/i18n';
|
|
7
8
|
import type { ThinkingLevel } from '@/shared/lib/api';
|
|
8
9
|
import { Plus, Settings2, X } from 'lucide-react';
|
|
9
10
|
|
|
10
|
-
type
|
|
11
|
+
type ModelConfig = Record<string, {
|
|
12
|
+
thinking?: { supported: ThinkingLevel[]; default?: ThinkingLevel | null };
|
|
13
|
+
vision?: boolean;
|
|
14
|
+
}>;
|
|
11
15
|
|
|
12
16
|
type ProviderModelsSectionProps = {
|
|
13
17
|
models: string[];
|
|
14
|
-
|
|
18
|
+
modelConfig: ModelConfig;
|
|
15
19
|
modelDraft: string;
|
|
16
20
|
showModelInput: boolean;
|
|
17
21
|
onModelDraftChange: (value: string) => void;
|
|
@@ -20,6 +24,7 @@ type ProviderModelsSectionProps = {
|
|
|
20
24
|
onRemoveModel: (modelName: string) => void;
|
|
21
25
|
onToggleModelThinkingLevel: (modelName: string, level: ThinkingLevel) => void;
|
|
22
26
|
onSetModelThinkingDefault: (modelName: string, level: ThinkingLevel | null) => void;
|
|
27
|
+
onSetModelVision: (modelName: string, vision: boolean) => void;
|
|
23
28
|
thinkingLevels: ThinkingLevel[];
|
|
24
29
|
formatThinkingLevelLabel: (level: ThinkingLevel) => string;
|
|
25
30
|
};
|
|
@@ -27,7 +32,7 @@ type ProviderModelsSectionProps = {
|
|
|
27
32
|
export function ProviderModelsSection(props: ProviderModelsSectionProps) {
|
|
28
33
|
const {
|
|
29
34
|
models,
|
|
30
|
-
|
|
35
|
+
modelConfig,
|
|
31
36
|
modelDraft,
|
|
32
37
|
showModelInput,
|
|
33
38
|
onModelDraftChange,
|
|
@@ -36,6 +41,7 @@ export function ProviderModelsSection(props: ProviderModelsSectionProps) {
|
|
|
36
41
|
onRemoveModel,
|
|
37
42
|
onToggleModelThinkingLevel,
|
|
38
43
|
onSetModelThinkingDefault,
|
|
44
|
+
onSetModelVision,
|
|
39
45
|
thinkingLevels,
|
|
40
46
|
formatThinkingLevelLabel
|
|
41
47
|
} = props;
|
|
@@ -108,9 +114,11 @@ export function ProviderModelsSection(props: ProviderModelsSectionProps) {
|
|
|
108
114
|
) : (
|
|
109
115
|
<div className="flex flex-wrap gap-2">
|
|
110
116
|
{models.map((modelName) => {
|
|
111
|
-
const
|
|
117
|
+
const modelEntry = modelConfig[modelName];
|
|
118
|
+
const thinkingEntry = modelEntry?.thinking;
|
|
112
119
|
const supportedLevels = thinkingEntry?.supported ?? [];
|
|
113
120
|
const defaultThinkingLevel = thinkingEntry?.default ?? null;
|
|
121
|
+
const visionEnabled = modelEntry?.vision === true;
|
|
114
122
|
|
|
115
123
|
return (
|
|
116
124
|
<div
|
|
@@ -130,6 +138,13 @@ export function ProviderModelsSection(props: ProviderModelsSectionProps) {
|
|
|
130
138
|
</button>
|
|
131
139
|
</PopoverTrigger>
|
|
132
140
|
<PopoverContent className="w-80 space-y-3">
|
|
141
|
+
<div className="flex items-center justify-between gap-3 rounded-lg border border-gray-100 bg-gray-50 px-3 py-2">
|
|
142
|
+
<div className="min-w-0">
|
|
143
|
+
<p className="text-xs font-semibold text-gray-800">{t('providerModelVisionTitle')}</p>
|
|
144
|
+
<p className="text-xs text-gray-500">{t('providerModelVisionHint')}</p>
|
|
145
|
+
</div>
|
|
146
|
+
<Switch checked={visionEnabled} onCheckedChange={(checked) => onSetModelVision(modelName, checked)} />
|
|
147
|
+
</div>
|
|
133
148
|
<div className="space-y-1">
|
|
134
149
|
<p className="text-xs font-semibold text-gray-800">{t('providerModelThinkingTitle')}</p>
|
|
135
150
|
<p className="text-xs text-gray-500">{t('providerModelThinkingHint')}</p>
|
|
@@ -213,6 +213,7 @@ describe('ModelConfig', () => {
|
|
|
213
213
|
prefix: 'openai',
|
|
214
214
|
aliases: ['openai'],
|
|
215
215
|
models: ['gpt-5.2'],
|
|
216
|
+
modelConfig: {},
|
|
216
217
|
modelThinking: {},
|
|
217
218
|
configured: true
|
|
218
219
|
},
|
|
@@ -222,6 +223,7 @@ describe('ModelConfig', () => {
|
|
|
222
223
|
prefix: 'customhub',
|
|
223
224
|
aliases: ['customhub'],
|
|
224
225
|
models: [],
|
|
226
|
+
modelConfig: {},
|
|
225
227
|
modelThinking: {},
|
|
226
228
|
configured: true
|
|
227
229
|
}
|
|
@@ -3,7 +3,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
|
|
3
3
|
import { cn } from '@/shared/lib/utils';
|
|
4
4
|
|
|
5
5
|
const buttonVariants = cva(
|
|
6
|
-
'inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium ring-offset-background transition-
|
|
6
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium ring-offset-background transition-colors duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
7
7
|
{
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
@@ -4,6 +4,12 @@ export type ChannelAuthStartRequest = {
|
|
|
4
4
|
domain?: string;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
export type ChannelAuthConnectRequest = {
|
|
8
|
+
accountId?: string;
|
|
9
|
+
domain?: string;
|
|
10
|
+
fields?: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
|
|
7
13
|
export type ChannelAuthStartResult = {
|
|
8
14
|
channel: string;
|
|
9
15
|
kind: "qr_code";
|
|
@@ -27,3 +33,5 @@ export type ChannelAuthPollResult = {
|
|
|
27
33
|
accountId?: string | null;
|
|
28
34
|
notes?: string[];
|
|
29
35
|
};
|
|
36
|
+
|
|
37
|
+
export type ChannelAuthConnectResult = ChannelAuthPollResult;
|
|
@@ -35,10 +35,10 @@ export type BootstrapStatusView = {
|
|
|
35
35
|
completedAt?: string;
|
|
36
36
|
error?: string;
|
|
37
37
|
};
|
|
38
|
-
|
|
38
|
+
extensionLoading: {
|
|
39
39
|
state: BootstrapStageState;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
loadedExtensionCount: number;
|
|
41
|
+
totalExtensionCount: number;
|
|
42
42
|
startedAt?: string;
|
|
43
43
|
completedAt?: string;
|
|
44
44
|
error?: string;
|
|
@@ -66,7 +66,10 @@ export type ProviderConfigView = {
|
|
|
66
66
|
extraHeaders?: Record<string, string> | null;
|
|
67
67
|
wireApi?: "auto" | "chat" | "responses" | null;
|
|
68
68
|
models?: string[];
|
|
69
|
-
|
|
69
|
+
modelConfig?: Record<string, {
|
|
70
|
+
thinking?: { supported: ThinkingLevel[]; default?: ThinkingLevel | null };
|
|
71
|
+
vision?: boolean;
|
|
72
|
+
}>;
|
|
70
73
|
};
|
|
71
74
|
|
|
72
75
|
export type ProviderConfigUpdate = {
|
|
@@ -77,7 +80,10 @@ export type ProviderConfigUpdate = {
|
|
|
77
80
|
extraHeaders?: Record<string, string> | null;
|
|
78
81
|
wireApi?: "auto" | "chat" | "responses" | null;
|
|
79
82
|
models?: string[] | null;
|
|
80
|
-
|
|
83
|
+
modelConfig?: Record<string, {
|
|
84
|
+
thinking?: { supported?: ThinkingLevel[]; default?: ThinkingLevel | null };
|
|
85
|
+
vision?: boolean;
|
|
86
|
+
}> | null;
|
|
81
87
|
};
|
|
82
88
|
|
|
83
89
|
export type ProviderConnectionTestRequest = ProviderConfigUpdate & {
|
|
@@ -547,6 +553,10 @@ export type ProviderSpecView = {
|
|
|
547
553
|
supportsCliImport?: boolean;
|
|
548
554
|
};
|
|
549
555
|
defaultModels?: string[];
|
|
556
|
+
modelConfig?: Record<string, {
|
|
557
|
+
thinking?: { supported: ThinkingLevel[]; default?: ThinkingLevel | null };
|
|
558
|
+
vision?: boolean;
|
|
559
|
+
}>;
|
|
550
560
|
supportsWireApi?: boolean;
|
|
551
561
|
wireApiOptions?: Array<"auto" | "chat" | "responses">;
|
|
552
562
|
defaultWireApi?: "auto" | "chat" | "responses";
|
|
@@ -666,14 +676,13 @@ export type WsEvent =
|
|
|
666
676
|
| { type: 'connection.close'; payload?: Record<string, unknown> }
|
|
667
677
|
| { type: 'connection.error'; payload?: { message?: string } };
|
|
668
678
|
|
|
669
|
-
export type MarketplaceItemType = '
|
|
679
|
+
export type MarketplaceItemType = 'skill' | 'mcp';
|
|
670
680
|
|
|
671
681
|
export type MarketplaceSort = 'relevance' | 'updated';
|
|
672
682
|
|
|
673
|
-
export type MarketplacePluginInstallKind = 'npm';
|
|
674
683
|
export type MarketplaceSkillInstallKind = 'builtin' | 'marketplace';
|
|
675
684
|
export type MarketplaceMcpInstallKind = 'template';
|
|
676
|
-
export type MarketplaceInstallKind =
|
|
685
|
+
export type MarketplaceInstallKind = MarketplaceSkillInstallKind | MarketplaceMcpInstallKind;
|
|
677
686
|
|
|
678
687
|
export type MarketplaceInstallSpec = {
|
|
679
688
|
kind: MarketplaceInstallKind;
|
|
@@ -733,18 +742,6 @@ export type MarketplaceSkillContentView = {
|
|
|
733
742
|
sourceUrl?: string;
|
|
734
743
|
};
|
|
735
744
|
|
|
736
|
-
export type MarketplacePluginContentView = {
|
|
737
|
-
type: 'plugin';
|
|
738
|
-
slug: string;
|
|
739
|
-
name: string;
|
|
740
|
-
install: MarketplaceInstallSpec;
|
|
741
|
-
source: 'npm' | 'repo' | 'remote';
|
|
742
|
-
raw?: string;
|
|
743
|
-
bodyRaw?: string;
|
|
744
|
-
metadataRaw?: string;
|
|
745
|
-
sourceUrl?: string;
|
|
746
|
-
};
|
|
747
|
-
|
|
748
745
|
export type MarketplaceMcpContentView = {
|
|
749
746
|
type: 'mcp';
|
|
750
747
|
slug: string;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { nextclawClient } from '
|
|
1
|
+
import { nextclawClient } from '@/shared/lib/api/managers/client.manager';
|
|
2
2
|
import type {
|
|
3
3
|
ChannelAuthPollRequest,
|
|
4
4
|
ChannelAuthPollResult,
|
|
5
|
+
ChannelAuthConnectRequest,
|
|
6
|
+
ChannelAuthConnectResult,
|
|
5
7
|
ChannelAuthStartRequest,
|
|
6
8
|
ChannelAuthStartResult
|
|
7
9
|
} from '@/shared/lib/api/channel-auth.types';
|
|
@@ -19,3 +21,10 @@ export async function pollChannelAuth(
|
|
|
19
21
|
): Promise<ChannelAuthPollResult> {
|
|
20
22
|
return await nextclawClient.channelAuth.poll(channel, data);
|
|
21
23
|
}
|
|
24
|
+
|
|
25
|
+
export async function connectChannelAuth(
|
|
26
|
+
channel: string,
|
|
27
|
+
data: ChannelAuthConnectRequest
|
|
28
|
+
): Promise<ChannelAuthConnectResult> {
|
|
29
|
+
return await nextclawClient.channelAuth.connect(channel, data);
|
|
30
|
+
}
|
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
MarketplaceManageRequest,
|
|
6
6
|
MarketplaceManageResult,
|
|
7
7
|
MarketplaceInstalledView,
|
|
8
|
-
MarketplacePluginContentView,
|
|
9
8
|
MarketplaceItemType,
|
|
10
9
|
MarketplaceSkillContentView,
|
|
11
10
|
MarketplaceItemView,
|
|
@@ -37,10 +36,6 @@ export async function fetchMarketplaceSkillContent(slug: string): Promise<Market
|
|
|
37
36
|
return await nextclawClient.marketplace.fetchSkillContent(slug);
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
export async function fetchMarketplacePluginContent(slug: string): Promise<MarketplacePluginContentView> {
|
|
41
|
-
return await nextclawClient.marketplace.fetchPluginContent(slug);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
39
|
export async function fetchMarketplaceRecommendations(
|
|
45
40
|
type: MarketplaceItemType,
|
|
46
41
|
params: {
|