@nextclaw/ui 0.14.1-beta.0 → 0.14.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 +50 -0
- package/dist/assets/{api-C6EDHBDR.js → api-DPcvCLfa.js} +3 -3
- package/dist/assets/{channels-list-page-DKj6m1Hc.js → channels-list-page-D6jtziu4.js} +2 -2
- package/dist/assets/chat-page-BBnNJoKG.js +105 -0
- package/dist/assets/{config-split-page-CjlmJy8X.js → config-split-page-BCJhqdK9.js} +1 -1
- package/dist/assets/{confirm-dialog-B39X0pYp.js → confirm-dialog-CWh5FfK2.js} +1 -1
- package/dist/assets/{desktop-update-config-DfUQ8nly.js → desktop-update-config-DsRhBizi.js} +1 -1
- package/dist/assets/{dist-CmMro3nV.js → dist-DPrgcEC0.js} +1 -1
- package/dist/assets/{dist-BIYV2-Ia.js → dist-DsE4OsSS.js} +1 -1
- package/dist/assets/doc-browser-C8xOF_9F.js +1 -0
- package/dist/assets/{doc-browser-CDtUtcim.js → doc-browser-DgQedeAn.js} +1 -1
- package/dist/assets/{doc-browser-context-DZbC9zVB.js → doc-browser-context-B46tQGmO.js} +1 -1
- package/dist/assets/{ellipsis-DDpgTzgx.js → ellipsis-B81FStLm.js} +1 -1
- package/dist/assets/{external-link-D2cBOCqn.js → external-link-C_dmG-WJ.js} +1 -1
- package/dist/assets/index-7o1WdUbU.js +103 -0
- package/dist/assets/index-DQWmEWzk.css +1 -0
- package/dist/assets/mcp-marketplace-page-CWZN7ohK.js +1 -0
- package/dist/assets/mcp-marketplace-page-Dzull_4-.js +40 -0
- package/dist/assets/model-config-page-Cv0O3BIj.js +1 -0
- package/dist/assets/plus-Bnt56oqG.js +1 -0
- package/dist/assets/provider-scoped-model-input-Duuh7i3h.js +1 -0
- package/dist/assets/providers-config-page-CPDz0Lxc.js +1 -0
- package/dist/assets/{react-DYQXXGbH.js → react-sZz5HSCm.js} +1 -1
- package/dist/assets/remote-B0LMs8u7.js +1 -0
- package/dist/assets/rotate-cw-BfyG_KEF.js +1 -0
- package/dist/assets/runtime-config-page-q0fvv8ME.js +1 -0
- package/dist/assets/{save-BNVTgBVn.js → save-MzjoiHri.js} +1 -1
- package/dist/assets/{search-CKCzwLId.js → search-B29dE4GN.js} +1 -1
- package/dist/assets/{search-config-page-DG5Q2hF1.js → search-config-page-Dl5sZzob.js} +1 -1
- package/dist/assets/secrets-config-page-C_l-IFCK.js +3 -0
- package/dist/assets/{select-CIRwTNP4.js → select-BKmh05hd.js} +2 -2
- package/dist/assets/{tag-chip-DKhVFtTE.js → tag-chip-BS_7Ueom.js} +1 -1
- package/dist/assets/use-config-DZkBWIdL.js +1 -0
- package/dist/assets/x-CW2KO-Hz.js +1 -0
- package/dist/index.html +14 -14
- package/package.json +9 -9
- package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +12 -63
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +11 -50
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +15 -2
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +70 -7
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +104 -16
- package/src/features/chat/components/layout/chat-sidebar.tsx +21 -2
- package/src/features/chat/features/input/components/chat-input-bar.container.tsx +84 -6
- package/src/features/chat/features/input/hooks/use-chat-input-bar-query-state.ts +2 -0
- package/src/features/chat/features/input/hooks/use-chat-model-favorites.ts +88 -0
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +64 -24
- package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +1 -0
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +21 -12
- package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +80 -25
- package/src/features/chat/features/message/utils/__tests__/chat-message-file-operation.utils.test.ts +310 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-test.utils.ts +43 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +1 -346
- package/src/features/chat/features/message/utils/chat-message-tool-card.utils.ts +10 -9
- package/src/features/chat/features/message/utils/file-operation/diff.utils.ts +39 -36
- package/src/features/chat/features/message/utils/file-operation/line-builder.utils.ts +46 -38
- package/src/features/chat/features/message/utils/file-operation/parsed-block.utils.ts +18 -15
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +3 -2
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-badge.test.tsx +25 -25
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +15 -2
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-menu.test.tsx +24 -0
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +27 -1
- package/src/features/chat/features/session-type/components/{chat-sidebar-create-menu.tsx → chat-session-type-menu.tsx} +17 -4
- package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +15 -3
- package/src/features/chat/features/session-type/hooks/__tests__/use-chat-new-session-type-preference.test.tsx +152 -0
- package/src/features/chat/features/session-type/hooks/use-chat-new-session-type-preference.ts +159 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +193 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +123 -0
- package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +104 -0
- package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +72 -0
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +54 -0
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +132 -0
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +82 -0
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +121 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +53 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-selection.utils.test.ts +63 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-visibility.utils.test.ts +34 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +71 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-selection.utils.ts +79 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-visibility.utils.ts +17 -0
- package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +65 -0
- package/src/features/chat/managers/chat-input.manager.ts +24 -2
- package/src/features/chat/stores/chat-input.store.ts +2 -0
- package/src/features/chat/types/chat-input-bar.types.ts +5 -0
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +24 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +8 -3
- package/src/features/marketplace/components/marketplace-item-list-view.tsx +2 -2
- package/src/features/marketplace/components/marketplace-list-card.tsx +124 -98
- package/src/features/marketplace/components/marketplace-page-parts.tsx +11 -6
- package/src/features/marketplace/utils/marketplace-installed-cache.utils.ts +27 -23
- package/src/features/settings/components/config/provider-form.tsx +284 -387
- package/src/features/settings/components/config/provider-status-badge.tsx +4 -4
- package/src/features/settings/components/config/secrets-config-form.tsx +11 -135
- package/src/features/settings/hooks/use-provider-auth-flow.ts +140 -0
- package/src/features/settings/pages/__tests__/model-config-page.test.tsx +16 -0
- package/src/features/settings/pages/secrets-config-page.tsx +2 -1
- package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +60 -1
- package/src/features/settings/utils/__tests__/secrets-config-form.utils.test.ts +66 -0
- package/src/features/settings/utils/provider-form-context.utils.ts +131 -0
- package/src/features/settings/utils/provider-form-model.utils.ts +101 -0
- package/src/features/settings/utils/provider-form-support.utils.ts +102 -1
- package/src/features/settings/utils/secrets-config-form.utils.ts +139 -0
- package/src/features/system-status/components/config/runtime-agent-list-card.tsx +17 -11
- package/src/features/system-status/components/config/runtime-binding-list-card.tsx +14 -9
- package/src/features/system-status/components/config/runtime-config-editor.tsx +8 -5
- package/src/features/system-status/components/config/runtime-entry-list-card.tsx +14 -9
- package/src/features/system-status/components/config/runtime-settings-card.tsx +16 -7
- package/src/features/system-status/components/runtime-security-card.tsx +79 -25
- package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -1
- package/src/shared/components/common/__tests__/searchable-model-input.test.tsx +34 -0
- package/src/shared/components/common/agent-avatar.tsx +5 -2
- package/src/shared/components/common/provider-scoped-model-input.tsx +1 -0
- package/src/shared/components/common/searchable-model-input.tsx +102 -87
- package/src/shared/components/ui/README.md +7 -0
- package/src/shared/components/ui/popover.tsx +19 -3
- package/src/shared/components/ui/select.tsx +14 -4
- package/src/shared/hooks/use-infinite-scroll-loader.ts +14 -10
- package/src/shared/lib/api/index.ts +3 -0
- package/src/shared/lib/api/preferences/preference-keys.config.ts +9 -0
- package/src/shared/lib/api/preferences/preference.types.ts +22 -0
- package/src/shared/lib/api/preferences/preference.utils.ts +40 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +16 -1
- package/src/shared/lib/i18n/locales/en-US/core.json +1 -0
- package/src/shared/lib/i18n/locales/en-US/marketplace.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +16 -1
- package/src/shared/lib/i18n/locales/zh-CN/core.json +1 -0
- package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +2 -0
- package/dist/assets/chat-page-BXngLJY6.js +0 -105
- package/dist/assets/doc-browser-B6Wc_TIY.js +0 -1
- package/dist/assets/index-DTb_XVIH.js +0 -103
- package/dist/assets/index-ayiPYhf1.css +0 -1
- package/dist/assets/mcp-marketplace-page-DZ9H0v05.js +0 -1
- package/dist/assets/mcp-marketplace-page-cnVWf7QQ.js +0 -40
- package/dist/assets/model-config-page-Bcb35AVv.js +0 -1
- package/dist/assets/plus-CYE1onU2.js +0 -1
- package/dist/assets/provider-scoped-model-input-kdoNp4HG.js +0 -1
- package/dist/assets/providers-config-page-CQSibSwO.js +0 -1
- package/dist/assets/remote-BlJDbXOI.js +0 -1
- package/dist/assets/rotate-cw-BWbvFDAX.js +0 -1
- package/dist/assets/runtime-config-page-Tn2yLQ0c.js +0 -1
- package/dist/assets/secrets-config-page-BXn5Uuwp.js +0 -3
- package/dist/assets/use-config-DgKM9VIU.js +0 -1
- package/dist/assets/x-Cm3JjYut.js +0 -1
- package/src/features/chat/components/__tests__/chat-welcome.test.tsx +0 -45
- package/src/features/chat/components/chat-welcome.tsx +0 -102
|
@@ -13,6 +13,7 @@ import { chatRecentSkillsManager, CHAT_RECENT_SKILLS_MIN_OPTIONS } from '@/featu
|
|
|
13
13
|
import { deriveSelectedSkillsFromComposer } from '@/features/chat/features/input/utils/chat-composer-state.utils';
|
|
14
14
|
import { hasNcpChatModelOptions, isNcpChatComposerDisabled, isNcpChatModelOptionsEmpty, isNcpChatModelOptionsLoading, isNcpChatSendDisabled } from '@/features/chat/features/input/utils/ncp-chat-input-availability.utils';
|
|
15
15
|
import { useChatInputBarQueryState } from '@/features/chat/features/input/hooks/use-chat-input-bar-query-state';
|
|
16
|
+
import { useChatModelFavorites } from '@/features/chat/features/input/hooks/use-chat-model-favorites';
|
|
16
17
|
import { useSelectedSessionContextWindowIndicator } from '@/features/chat/features/session/hooks/use-selected-session-context-window-indicator';
|
|
17
18
|
import { useSystemStatus } from '@/features/system-status';
|
|
18
19
|
import { isNcpChatRuntimeBlocked } from '@/features/chat/features/runtime/utils/ncp-chat-runtime-availability.utils';
|
|
@@ -89,6 +90,11 @@ function useChatInputBarLabels(language: string) {
|
|
|
89
90
|
slashTexts,
|
|
90
91
|
recentModelsLabel: t('chatPickerRecentModels'),
|
|
91
92
|
allModelsLabel: t('chatPickerAllModels'),
|
|
93
|
+
favoriteModelsLabel: t('chatPickerFavoriteModels'),
|
|
94
|
+
modelSearchPlaceholder: t('chatModelSearchPlaceholder'),
|
|
95
|
+
modelSearchEmptyLabel: t('chatModelSearchEmpty'),
|
|
96
|
+
favoriteModelLabel: t('chatFavoriteModel'),
|
|
97
|
+
unfavoriteModelLabel: t('chatUnfavoriteModel'),
|
|
92
98
|
recentSkillsLabel: t('chatPickerRecent'),
|
|
93
99
|
allSkillsLabel: t('chatPickerAllSkills')
|
|
94
100
|
};
|
|
@@ -162,19 +168,69 @@ function useChatInputBarAttachments(params: { attachmentSupported: boolean; inpu
|
|
|
162
168
|
}, [handleFilesAdd])
|
|
163
169
|
};
|
|
164
170
|
}
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
|
|
172
|
+
type ToolbarSelectBuildParams = {
|
|
173
|
+
allModelsLabel: string;
|
|
174
|
+
favoriteModelLabel: string;
|
|
175
|
+
favoriteModelValues: string[];
|
|
176
|
+
favoriteModelsLabel: string;
|
|
177
|
+
hasModelOptions: boolean;
|
|
178
|
+
isModelOptionsLoading: boolean;
|
|
179
|
+
modelRecords: ChatModelRecord[];
|
|
180
|
+
modelSearchEmptyLabel: string;
|
|
181
|
+
modelSearchPlaceholder: string;
|
|
182
|
+
onFavoriteModelToggle: (value: string, favorite: boolean) => void;
|
|
183
|
+
onModelChange: (value: string) => void;
|
|
184
|
+
onThinkingChange: (value: ChatThinkingLevel | null) => void;
|
|
185
|
+
recentModelValues: string[];
|
|
186
|
+
recentModelsLabel: string;
|
|
187
|
+
selectedModel: string;
|
|
188
|
+
selectedThinkingLevel: ChatThinkingLevel | null;
|
|
189
|
+
thinkingDefaultLevel: ChatThinkingLevel | null;
|
|
190
|
+
thinkingSupportedLevels: ChatThinkingLevel[];
|
|
191
|
+
unfavoriteModelLabel: string;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
function buildToolbarSelects(params: ToolbarSelectBuildParams) {
|
|
195
|
+
const {
|
|
196
|
+
allModelsLabel,
|
|
197
|
+
favoriteModelLabel,
|
|
198
|
+
favoriteModelValues,
|
|
199
|
+
favoriteModelsLabel,
|
|
200
|
+
hasModelOptions,
|
|
201
|
+
isModelOptionsLoading,
|
|
202
|
+
modelRecords,
|
|
203
|
+
modelSearchEmptyLabel,
|
|
204
|
+
modelSearchPlaceholder,
|
|
205
|
+
onFavoriteModelToggle,
|
|
206
|
+
onModelChange,
|
|
207
|
+
onThinkingChange,
|
|
208
|
+
recentModelValues,
|
|
209
|
+
recentModelsLabel,
|
|
210
|
+
selectedModel,
|
|
211
|
+
selectedThinkingLevel,
|
|
212
|
+
thinkingDefaultLevel,
|
|
213
|
+
thinkingSupportedLevels,
|
|
214
|
+
unfavoriteModelLabel
|
|
215
|
+
} = params;
|
|
167
216
|
return [
|
|
168
217
|
buildModelToolbarSelect({
|
|
169
218
|
modelOptions: modelRecords,
|
|
219
|
+
favoriteModelValues,
|
|
170
220
|
recentModelValues,
|
|
171
221
|
selectedModel,
|
|
172
222
|
isModelOptionsLoading,
|
|
173
223
|
hasModelOptions,
|
|
224
|
+
onFavoriteToggle: onFavoriteModelToggle,
|
|
174
225
|
onValueChange: onModelChange,
|
|
175
226
|
texts: {
|
|
176
227
|
modelSelectPlaceholder: t('chatSelectModel'),
|
|
177
228
|
modelNoOptionsLabel: t('chatModelNoOptions'),
|
|
229
|
+
modelSearchPlaceholder,
|
|
230
|
+
modelSearchEmptyLabel,
|
|
231
|
+
favoriteModelsLabel,
|
|
232
|
+
favoriteModelLabel,
|
|
233
|
+
unfavoriteModelLabel,
|
|
178
234
|
recentModelsLabel,
|
|
179
235
|
allModelsLabel
|
|
180
236
|
}
|
|
@@ -211,7 +267,11 @@ function buildSkillPicker(params: { allSkillsLabel: string; isSkillsLoading: boo
|
|
|
211
267
|
});
|
|
212
268
|
}
|
|
213
269
|
|
|
214
|
-
|
|
270
|
+
type ChatInputBarContainerProps = {
|
|
271
|
+
surface?: 'default' | 'embedded';
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export function ChatInputBarContainer({ surface = 'default' }: ChatInputBarContainerProps) {
|
|
215
275
|
const presenter = usePresenter();
|
|
216
276
|
const { language } = useI18n();
|
|
217
277
|
const { isMobile } = useViewportLayout();
|
|
@@ -227,6 +287,14 @@ export function ChatInputBarContainer() {
|
|
|
227
287
|
skillRecords: inputQueryState.skillRecords,
|
|
228
288
|
skillScopeLabels: labels.skillScopeLabels
|
|
229
289
|
});
|
|
290
|
+
const modelRecordValues = useMemo(
|
|
291
|
+
() => modelRecords.map((option) => option.value),
|
|
292
|
+
[modelRecords]
|
|
293
|
+
);
|
|
294
|
+
const {
|
|
295
|
+
favoriteModelValues,
|
|
296
|
+
setModelFavorite,
|
|
297
|
+
} = useChatModelFavorites(modelRecordValues);
|
|
230
298
|
const availabilitySnapshot = {
|
|
231
299
|
...snapshot,
|
|
232
300
|
isProviderStateResolved: inputQueryState.isProviderStateResolved,
|
|
@@ -257,6 +325,7 @@ export function ChatInputBarContainer() {
|
|
|
257
325
|
modelOptions: inputQueryState.modelOptions,
|
|
258
326
|
skillRecords: inputQueryState.skillRecords,
|
|
259
327
|
defaultSessionType: inputQueryState.sessionTypeState.defaultSessionType,
|
|
328
|
+
defaultProjectRoot: inputQueryState.defaultProjectRoot,
|
|
260
329
|
sessionTypeOptions: inputQueryState.sessionTypeState.sessionTypeOptions,
|
|
261
330
|
selectedSessionType: inputQueryState.sessionTypeState.selectedSessionType,
|
|
262
331
|
canEditSessionType: inputQueryState.sessionTypeState.canEditSessionType,
|
|
@@ -274,6 +343,7 @@ export function ChatInputBarContainer() {
|
|
|
274
343
|
});
|
|
275
344
|
}, [
|
|
276
345
|
inputQueryState.defaultModel,
|
|
346
|
+
inputQueryState.defaultProjectRoot,
|
|
277
347
|
inputQueryState.fallbackPreferredModel,
|
|
278
348
|
inputQueryState.fallbackPreferredThinking,
|
|
279
349
|
inputQueryState.isProviderStateResolved,
|
|
@@ -295,15 +365,21 @@ export function ChatInputBarContainer() {
|
|
|
295
365
|
return;
|
|
296
366
|
}
|
|
297
367
|
if (request.placement === 'end') {
|
|
298
|
-
inputBarRef.current?.focusComposerAtEnd();
|
|
368
|
+
inputBarRef.current?.focusComposerAtEnd(snapshot.composerNodes);
|
|
299
369
|
}
|
|
300
370
|
presenter.chatInputManager.consumeComposerFocusRequest(request.id);
|
|
301
|
-
}, [presenter.chatInputManager, snapshot.composerFocusRequest]);
|
|
371
|
+
}, [presenter.chatInputManager, snapshot.composerFocusRequest, snapshot.composerNodes]);
|
|
302
372
|
const toolbarSelects = buildToolbarSelects({
|
|
303
373
|
allModelsLabel: labels.allModelsLabel,
|
|
374
|
+
favoriteModelLabel: labels.favoriteModelLabel,
|
|
375
|
+
favoriteModelValues,
|
|
376
|
+
favoriteModelsLabel: labels.favoriteModelsLabel,
|
|
304
377
|
hasModelOptions,
|
|
305
378
|
isModelOptionsLoading,
|
|
306
379
|
modelRecords,
|
|
380
|
+
modelSearchEmptyLabel: labels.modelSearchEmptyLabel,
|
|
381
|
+
modelSearchPlaceholder: labels.modelSearchPlaceholder,
|
|
382
|
+
onFavoriteModelToggle: setModelFavorite,
|
|
307
383
|
onModelChange: presenter.chatInputManager.selectModel,
|
|
308
384
|
onThinkingChange: (value) => {
|
|
309
385
|
if (value) {
|
|
@@ -315,7 +391,8 @@ export function ChatInputBarContainer() {
|
|
|
315
391
|
selectedModel: snapshot.selectedModel,
|
|
316
392
|
selectedThinkingLevel: snapshot.selectedThinkingLevel as ChatThinkingLevel | null,
|
|
317
393
|
thinkingSupportedLevels,
|
|
318
|
-
thinkingDefaultLevel: selectedModelOption?.thinkingCapability?.default ?? null
|
|
394
|
+
thinkingDefaultLevel: selectedModelOption?.thinkingCapability?.default ?? null,
|
|
395
|
+
unfavoriteModelLabel: labels.unfavoriteModelLabel
|
|
319
396
|
});
|
|
320
397
|
const skillPicker = buildSkillPicker({
|
|
321
398
|
allSkillsLabel: labels.allSkillsLabel,
|
|
@@ -333,6 +410,7 @@ export function ChatInputBarContainer() {
|
|
|
333
410
|
<>
|
|
334
411
|
<ChatInputBar
|
|
335
412
|
ref={inputBarRef}
|
|
413
|
+
surface={surface}
|
|
336
414
|
composer={{
|
|
337
415
|
nodes: snapshot.composerNodes,
|
|
338
416
|
placeholder: textareaPlaceholder,
|
|
@@ -14,6 +14,7 @@ import { chatRecentModelsManager } from '@/features/chat/managers/chat-recent-mo
|
|
|
14
14
|
import type { ChatInputSnapshot } from '@/features/chat/stores/chat-input.store';
|
|
15
15
|
import { useChatQueryStore } from '@/features/chat/stores/ncp-chat-query.store';
|
|
16
16
|
import { useChatSessionListStore } from '@/features/chat/stores/chat-session-list.store';
|
|
17
|
+
import { normalizeSessionProjectRootValue } from '@/shared/lib/session-project';
|
|
17
18
|
|
|
18
19
|
const EMPTY_SESSION_SKILL_RECORDS: SessionSkillEntryView[] = [];
|
|
19
20
|
const EMPTY_NCP_SESSION_SUMMARIES: NcpSessionSummaryView[] = [];
|
|
@@ -105,6 +106,7 @@ export function useChatInputBarQueryState(snapshot: ChatInputSnapshot) {
|
|
|
105
106
|
|
|
106
107
|
return {
|
|
107
108
|
defaultModel: config?.agents.defaults.model,
|
|
109
|
+
defaultProjectRoot: normalizeSessionProjectRootValue(config?.agents.defaults.workspace),
|
|
108
110
|
fallbackPreferredModel,
|
|
109
111
|
fallbackPreferredThinking,
|
|
110
112
|
isProviderStateResolved,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import {
|
|
4
|
+
fetchPreference,
|
|
5
|
+
PREFERENCE_KEYS,
|
|
6
|
+
updatePreference,
|
|
7
|
+
type PreferenceEntryView,
|
|
8
|
+
type PreferenceJsonValue,
|
|
9
|
+
} from '@/shared/lib/api';
|
|
10
|
+
|
|
11
|
+
const CHAT_MODEL_FAVORITES_PREFERENCE_KEY = PREFERENCE_KEYS.chat.modelFavorites;
|
|
12
|
+
const chatModelFavoritesQueryKey = ['preference', CHAT_MODEL_FAVORITES_PREFERENCE_KEY] as const;
|
|
13
|
+
|
|
14
|
+
function normalizeFavoriteModelValues(value: PreferenceJsonValue | null | undefined): string[] {
|
|
15
|
+
if (!Array.isArray(value)) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const values: string[] = [];
|
|
19
|
+
for (const item of value) {
|
|
20
|
+
if (typeof item !== 'string') {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const normalized = item.trim();
|
|
24
|
+
if (normalized && !values.includes(normalized)) {
|
|
25
|
+
values.push(normalized);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return values;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useChatModelFavorites(availableModelValues: string[]) {
|
|
32
|
+
const queryClient = useQueryClient();
|
|
33
|
+
const availableModelValueSet = useMemo(
|
|
34
|
+
() => new Set(availableModelValues),
|
|
35
|
+
[availableModelValues],
|
|
36
|
+
);
|
|
37
|
+
const favoritesQuery = useQuery({
|
|
38
|
+
queryKey: chatModelFavoritesQueryKey,
|
|
39
|
+
queryFn: () => fetchPreference(CHAT_MODEL_FAVORITES_PREFERENCE_KEY),
|
|
40
|
+
staleTime: 30_000,
|
|
41
|
+
});
|
|
42
|
+
const favoriteModelValues = useMemo(
|
|
43
|
+
() => normalizeFavoriteModelValues(favoritesQuery.data?.value),
|
|
44
|
+
[favoritesQuery.data?.value],
|
|
45
|
+
);
|
|
46
|
+
const visibleFavoriteModelValues = useMemo(
|
|
47
|
+
() => favoriteModelValues.filter((value) => availableModelValueSet.has(value)),
|
|
48
|
+
[availableModelValueSet, favoriteModelValues],
|
|
49
|
+
);
|
|
50
|
+
const updateFavorites = useMutation({
|
|
51
|
+
mutationFn: async (values: string[]) =>
|
|
52
|
+
await updatePreference(CHAT_MODEL_FAVORITES_PREFERENCE_KEY, values),
|
|
53
|
+
onMutate: async (values) => {
|
|
54
|
+
await queryClient.cancelQueries({ queryKey: chatModelFavoritesQueryKey });
|
|
55
|
+
const previous = queryClient.getQueryData<PreferenceEntryView>(chatModelFavoritesQueryKey);
|
|
56
|
+
queryClient.setQueryData<PreferenceEntryView>(chatModelFavoritesQueryKey, {
|
|
57
|
+
key: CHAT_MODEL_FAVORITES_PREFERENCE_KEY,
|
|
58
|
+
value: values,
|
|
59
|
+
updatedAt: new Date().toISOString(),
|
|
60
|
+
});
|
|
61
|
+
return { previous };
|
|
62
|
+
},
|
|
63
|
+
onError: (_error, _values, context) => {
|
|
64
|
+
if (context?.previous) {
|
|
65
|
+
queryClient.setQueryData(chatModelFavoritesQueryKey, context.previous);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
onSuccess: (entry) => {
|
|
69
|
+
queryClient.setQueryData(chatModelFavoritesQueryKey, entry);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const setModelFavorite = useCallback((value: string, favorite: boolean) => {
|
|
73
|
+
const normalized = value.trim();
|
|
74
|
+
if (!normalized) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const next = favorite
|
|
78
|
+
? [normalized, ...favoriteModelValues.filter((item) => item !== normalized)]
|
|
79
|
+
: favoriteModelValues.filter((item) => item !== normalized);
|
|
80
|
+
updateFavorites.mutate(next);
|
|
81
|
+
}, [favoriteModelValues, updateFavorites]);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
favoriteModelValues: visibleFavoriteModelValues,
|
|
85
|
+
isLoading: favoritesQuery.isLoading,
|
|
86
|
+
setModelFavorite,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -13,6 +13,20 @@ function createSkillRecord(partial: Partial<ChatSkillRecord>): ChatSkillRecord {
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function createModelTexts() {
|
|
17
|
+
return {
|
|
18
|
+
modelSelectPlaceholder: 'Select model',
|
|
19
|
+
modelNoOptionsLabel: 'No models',
|
|
20
|
+
modelSearchPlaceholder: 'Search models',
|
|
21
|
+
modelSearchEmptyLabel: 'No matching models',
|
|
22
|
+
favoriteModelsLabel: 'Favorites',
|
|
23
|
+
favoriteModelLabel: 'Favorite model',
|
|
24
|
+
unfavoriteModelLabel: 'Remove favorite',
|
|
25
|
+
recentModelsLabel: 'Recent',
|
|
26
|
+
allModelsLabel: 'All models'
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
describe('buildChatSlashItems', () => {
|
|
17
31
|
const texts = {
|
|
18
32
|
slashSkillSubtitle: 'Skill',
|
|
@@ -188,12 +202,7 @@ describe('buildModelToolbarSelect', () => {
|
|
|
188
202
|
isModelOptionsLoading: false,
|
|
189
203
|
hasModelOptions: true,
|
|
190
204
|
onValueChange,
|
|
191
|
-
texts:
|
|
192
|
-
modelSelectPlaceholder: 'Select model',
|
|
193
|
-
modelNoOptionsLabel: 'No models',
|
|
194
|
-
recentModelsLabel: 'Recent',
|
|
195
|
-
allModelsLabel: 'All models'
|
|
196
|
-
}
|
|
205
|
+
texts: createModelTexts()
|
|
197
206
|
});
|
|
198
207
|
|
|
199
208
|
expect(select.value).toBe('minimax/MiniMax-M2.7');
|
|
@@ -218,12 +227,7 @@ describe('buildModelToolbarSelect', () => {
|
|
|
218
227
|
isModelOptionsLoading: false,
|
|
219
228
|
hasModelOptions: true,
|
|
220
229
|
onValueChange: vi.fn(),
|
|
221
|
-
texts:
|
|
222
|
-
modelSelectPlaceholder: 'Select model',
|
|
223
|
-
modelNoOptionsLabel: 'No models',
|
|
224
|
-
recentModelsLabel: 'Recent',
|
|
225
|
-
allModelsLabel: 'All models'
|
|
226
|
-
}
|
|
230
|
+
texts: createModelTexts()
|
|
227
231
|
});
|
|
228
232
|
|
|
229
233
|
expect(select.selectedLabel).toBe('Anthropic/claude-sonnet-4-very-long-name');
|
|
@@ -254,12 +258,7 @@ describe('buildModelToolbarSelect', () => {
|
|
|
254
258
|
isModelOptionsLoading: false,
|
|
255
259
|
hasModelOptions: true,
|
|
256
260
|
onValueChange: vi.fn(),
|
|
257
|
-
texts:
|
|
258
|
-
modelSelectPlaceholder: 'Select model',
|
|
259
|
-
modelNoOptionsLabel: 'No models',
|
|
260
|
-
recentModelsLabel: 'Recent',
|
|
261
|
-
allModelsLabel: 'All models'
|
|
262
|
-
}
|
|
261
|
+
texts: createModelTexts()
|
|
263
262
|
});
|
|
264
263
|
|
|
265
264
|
expect(select.groups).toEqual([
|
|
@@ -290,6 +289,52 @@ describe('buildModelToolbarSelect', () => {
|
|
|
290
289
|
]);
|
|
291
290
|
});
|
|
292
291
|
|
|
292
|
+
it('groups favorite models ahead of recent models without duplicates', () => {
|
|
293
|
+
const onFavoriteToggle = vi.fn();
|
|
294
|
+
const select = buildModelToolbarSelect({
|
|
295
|
+
modelOptions: [
|
|
296
|
+
{
|
|
297
|
+
value: 'openai/gpt-5',
|
|
298
|
+
modelLabel: 'gpt-5',
|
|
299
|
+
providerLabel: 'OpenAI'
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
value: 'anthropic/claude-sonnet-4',
|
|
303
|
+
modelLabel: 'claude-sonnet-4',
|
|
304
|
+
providerLabel: 'Anthropic'
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
value: 'minimax/MiniMax-M2.7',
|
|
308
|
+
modelLabel: 'MiniMax-M2.7',
|
|
309
|
+
providerLabel: 'MiniMax'
|
|
310
|
+
}
|
|
311
|
+
],
|
|
312
|
+
favoriteModelValues: ['openai/gpt-5'],
|
|
313
|
+
recentModelValues: ['anthropic/claude-sonnet-4', 'openai/gpt-5'],
|
|
314
|
+
selectedModel: 'openai/gpt-5',
|
|
315
|
+
isModelOptionsLoading: false,
|
|
316
|
+
hasModelOptions: true,
|
|
317
|
+
onFavoriteToggle,
|
|
318
|
+
onValueChange: vi.fn(),
|
|
319
|
+
texts: createModelTexts()
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
expect(select.groups?.map((group) => group.key)).toEqual([
|
|
323
|
+
'favorite-models',
|
|
324
|
+
'recent-models',
|
|
325
|
+
'all-models'
|
|
326
|
+
]);
|
|
327
|
+
expect(select.groups?.[0]?.options.map((option) => option.value)).toEqual(['openai/gpt-5']);
|
|
328
|
+
expect(select.groups?.[1]?.options.map((option) => option.value)).toEqual(['anthropic/claude-sonnet-4']);
|
|
329
|
+
expect(select.groups?.[2]?.options.map((option) => option.value)).toEqual(['minimax/MiniMax-M2.7']);
|
|
330
|
+
expect(select.optionAction).toMatchObject({
|
|
331
|
+
kind: 'favorite',
|
|
332
|
+
activeValues: ['openai/gpt-5'],
|
|
333
|
+
activeLabel: 'Remove favorite',
|
|
334
|
+
inactiveLabel: 'Favorite model'
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
293
338
|
it('preserves recent model order from newest to oldest', () => {
|
|
294
339
|
const select = buildModelToolbarSelect({
|
|
295
340
|
modelOptions: [
|
|
@@ -314,12 +359,7 @@ describe('buildModelToolbarSelect', () => {
|
|
|
314
359
|
isModelOptionsLoading: false,
|
|
315
360
|
hasModelOptions: true,
|
|
316
361
|
onValueChange: vi.fn(),
|
|
317
|
-
texts:
|
|
318
|
-
modelSelectPlaceholder: 'Select model',
|
|
319
|
-
modelNoOptionsLabel: 'No models',
|
|
320
|
-
recentModelsLabel: 'Recent',
|
|
321
|
-
allModelsLabel: 'All models'
|
|
322
|
-
}
|
|
362
|
+
texts: createModelTexts()
|
|
323
363
|
});
|
|
324
364
|
|
|
325
365
|
expect(select.groups?.[0]?.options.map((option) => option.value)).toEqual([
|
|
@@ -208,8 +208,17 @@ export function buildSkillPickerModel(params: {
|
|
|
208
208
|
allSkillsLabel: string;
|
|
209
209
|
};
|
|
210
210
|
}): ChatSkillPickerProps {
|
|
211
|
-
const
|
|
212
|
-
|
|
211
|
+
const {
|
|
212
|
+
groupedRecentSkillValues,
|
|
213
|
+
isLoading,
|
|
214
|
+
onSelectedKeysChange,
|
|
215
|
+
recentSkillValues,
|
|
216
|
+
selectedSkills,
|
|
217
|
+
skillRecords,
|
|
218
|
+
texts,
|
|
219
|
+
} = params;
|
|
220
|
+
const prioritizedSkillRecords = prioritizeSkillRecords(skillRecords, recentSkillValues ?? []);
|
|
221
|
+
const recentKeySet = new Set(groupedRecentSkillValues ?? []);
|
|
213
222
|
const recentSkillOptions = buildSkillPickerOptions(
|
|
214
223
|
prioritizedSkillRecords.filter((record) => recentKeySet.has(record.key))
|
|
215
224
|
);
|
|
@@ -217,12 +226,12 @@ export function buildSkillPickerModel(params: {
|
|
|
217
226
|
prioritizedSkillRecords.filter((record) => !recentKeySet.has(record.key))
|
|
218
227
|
);
|
|
219
228
|
return {
|
|
220
|
-
title:
|
|
221
|
-
searchPlaceholder:
|
|
222
|
-
emptyLabel:
|
|
223
|
-
loadingLabel:
|
|
224
|
-
isLoading
|
|
225
|
-
manageLabel:
|
|
229
|
+
title: texts.title,
|
|
230
|
+
searchPlaceholder: texts.searchPlaceholder,
|
|
231
|
+
emptyLabel: texts.emptyLabel,
|
|
232
|
+
loadingLabel: texts.loadingLabel,
|
|
233
|
+
isLoading,
|
|
234
|
+
manageLabel: texts.manageLabel,
|
|
226
235
|
manageHref: '/marketplace/skills',
|
|
227
236
|
options: buildSkillPickerOptions(prioritizedSkillRecords),
|
|
228
237
|
groups:
|
|
@@ -230,17 +239,17 @@ export function buildSkillPickerModel(params: {
|
|
|
230
239
|
? [
|
|
231
240
|
{
|
|
232
241
|
key: 'recent-skills',
|
|
233
|
-
label:
|
|
242
|
+
label: texts.recentSkillsLabel,
|
|
234
243
|
options: recentSkillOptions
|
|
235
244
|
},
|
|
236
245
|
{
|
|
237
246
|
key: 'all-skills',
|
|
238
|
-
label:
|
|
247
|
+
label: texts.allSkillsLabel,
|
|
239
248
|
options: remainingSkillOptions
|
|
240
249
|
}
|
|
241
250
|
].filter((group) => group.options.length > 0)
|
|
242
251
|
: undefined,
|
|
243
|
-
selectedKeys:
|
|
244
|
-
onSelectedKeysChange
|
|
252
|
+
selectedKeys: selectedSkills,
|
|
253
|
+
onSelectedKeysChange
|
|
245
254
|
};
|
|
246
255
|
}
|
|
@@ -35,10 +35,16 @@ export function buildModelStateHint(params: {
|
|
|
35
35
|
"noModelOptionsLabel" | "configureProviderLabel"
|
|
36
36
|
>;
|
|
37
37
|
}): ChatInlineHint | null {
|
|
38
|
-
|
|
38
|
+
const {
|
|
39
|
+
isModelOptionsEmpty,
|
|
40
|
+
isModelOptionsLoading,
|
|
41
|
+
onGoToProviders,
|
|
42
|
+
texts,
|
|
43
|
+
} = params;
|
|
44
|
+
if (!isModelOptionsLoading && !isModelOptionsEmpty) {
|
|
39
45
|
return null;
|
|
40
46
|
}
|
|
41
|
-
if (
|
|
47
|
+
if (isModelOptionsLoading) {
|
|
42
48
|
return {
|
|
43
49
|
tone: "neutral",
|
|
44
50
|
loading: true,
|
|
@@ -46,25 +52,40 @@ export function buildModelStateHint(params: {
|
|
|
46
52
|
}
|
|
47
53
|
return {
|
|
48
54
|
tone: "warning",
|
|
49
|
-
text:
|
|
50
|
-
actionLabel:
|
|
51
|
-
onAction:
|
|
55
|
+
text: texts.noModelOptionsLabel,
|
|
56
|
+
actionLabel: texts.configureProviderLabel,
|
|
57
|
+
onAction: onGoToProviders,
|
|
52
58
|
};
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
export function buildModelToolbarSelect({
|
|
56
|
-
modelOptions,
|
|
62
|
+
modelOptions,
|
|
63
|
+
favoriteModelValues,
|
|
64
|
+
recentModelValues,
|
|
65
|
+
selectedModel,
|
|
66
|
+
isModelOptionsLoading,
|
|
67
|
+
hasModelOptions,
|
|
68
|
+
onFavoriteToggle,
|
|
69
|
+
onValueChange,
|
|
70
|
+
texts,
|
|
57
71
|
}: {
|
|
58
72
|
modelOptions: ChatModelRecord[];
|
|
73
|
+
favoriteModelValues?: string[];
|
|
59
74
|
recentModelValues?: string[];
|
|
60
75
|
selectedModel: string;
|
|
61
76
|
isModelOptionsLoading: boolean;
|
|
62
77
|
hasModelOptions: boolean;
|
|
78
|
+
onFavoriteToggle?: (value: string, favorite: boolean) => void;
|
|
63
79
|
onValueChange: (value: string) => void;
|
|
64
80
|
texts: Pick<
|
|
65
81
|
ChatInputBarAdapterTexts,
|
|
66
82
|
| "modelSelectPlaceholder"
|
|
67
83
|
| "modelNoOptionsLabel"
|
|
84
|
+
| "modelSearchPlaceholder"
|
|
85
|
+
| "modelSearchEmptyLabel"
|
|
86
|
+
| "favoriteModelsLabel"
|
|
87
|
+
| "favoriteModelLabel"
|
|
88
|
+
| "unfavoriteModelLabel"
|
|
68
89
|
| "recentModelsLabel"
|
|
69
90
|
| "allModelsLabel"
|
|
70
91
|
>;
|
|
@@ -76,19 +97,33 @@ export function buildModelToolbarSelect({
|
|
|
76
97
|
const resolvedValue = hasModelOptions
|
|
77
98
|
? resolvedModelOption?.value
|
|
78
99
|
: undefined;
|
|
79
|
-
const recentValueSet = new Set(recentModelValues ?? []);
|
|
80
100
|
const modelOptionMap = new Map(
|
|
81
101
|
modelOptions.map((option) => [option.value, option] as const),
|
|
82
102
|
);
|
|
83
|
-
const
|
|
103
|
+
const favoriteOptions = (favoriteModelValues ?? [])
|
|
84
104
|
.map((value) => modelOptionMap.get(value))
|
|
85
105
|
.filter((option): option is ChatModelRecord => Boolean(option));
|
|
106
|
+
const favoriteValueSet = new Set(favoriteOptions.map((option) => option.value));
|
|
107
|
+
const recentOptions = (recentModelValues ?? [])
|
|
108
|
+
.map((value) => modelOptionMap.get(value))
|
|
109
|
+
.filter(
|
|
110
|
+
(option): option is ChatModelRecord =>
|
|
111
|
+
option !== undefined && !favoriteValueSet.has(option.value),
|
|
112
|
+
);
|
|
113
|
+
const recentValueSet = new Set(recentOptions.map((option) => option.value));
|
|
86
114
|
const remainingOptions = modelOptions.filter(
|
|
87
|
-
(option) => !recentValueSet.has(option.value),
|
|
115
|
+
(option) => !favoriteValueSet.has(option.value) && !recentValueSet.has(option.value),
|
|
88
116
|
);
|
|
89
|
-
const optionGroups =
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
const optionGroups = favoriteOptions.length > 0 || recentOptions.length > 0
|
|
118
|
+
? [
|
|
119
|
+
{
|
|
120
|
+
key: "favorite-models",
|
|
121
|
+
label: texts.favoriteModelsLabel,
|
|
122
|
+
options: favoriteOptions.map((option) => ({
|
|
123
|
+
value: option.value,
|
|
124
|
+
label: formatModelOptionLabel(option),
|
|
125
|
+
})),
|
|
126
|
+
},
|
|
92
127
|
{
|
|
93
128
|
key: "recent-models",
|
|
94
129
|
label: texts.recentModelsLabel,
|
|
@@ -106,7 +141,7 @@ export function buildModelToolbarSelect({
|
|
|
106
141
|
})),
|
|
107
142
|
},
|
|
108
143
|
].filter((group) => group.options.length > 0)
|
|
109
|
-
|
|
144
|
+
: undefined;
|
|
110
145
|
|
|
111
146
|
return {
|
|
112
147
|
key: "model",
|
|
@@ -124,6 +159,19 @@ export function buildModelToolbarSelect({
|
|
|
124
159
|
disabled: !hasModelOptions,
|
|
125
160
|
loading: isModelOptionsLoading,
|
|
126
161
|
emptyLabel: texts.modelNoOptionsLabel,
|
|
162
|
+
search: {
|
|
163
|
+
placeholder: texts.modelSearchPlaceholder,
|
|
164
|
+
emptyLabel: texts.modelSearchEmptyLabel,
|
|
165
|
+
},
|
|
166
|
+
optionAction: onFavoriteToggle
|
|
167
|
+
? {
|
|
168
|
+
kind: "favorite",
|
|
169
|
+
activeValues: favoriteOptions.map((option) => option.value),
|
|
170
|
+
activeLabel: texts.unfavoriteModelLabel,
|
|
171
|
+
inactiveLabel: texts.favoriteModelLabel,
|
|
172
|
+
onToggle: onFavoriteToggle,
|
|
173
|
+
}
|
|
174
|
+
: undefined,
|
|
127
175
|
onValueChange,
|
|
128
176
|
};
|
|
129
177
|
}
|
|
@@ -135,31 +183,38 @@ export function buildThinkingToolbarSelect(params: {
|
|
|
135
183
|
onValueChange: (value: ChatThinkingLevel) => void;
|
|
136
184
|
texts: Pick<ChatInputBarAdapterTexts, "thinkingLabels">;
|
|
137
185
|
}): ChatToolbarSelect | null {
|
|
138
|
-
|
|
186
|
+
const {
|
|
187
|
+
defaultThinkingLevel,
|
|
188
|
+
onValueChange,
|
|
189
|
+
selectedThinkingLevel,
|
|
190
|
+
supportedLevels,
|
|
191
|
+
texts,
|
|
192
|
+
} = params;
|
|
193
|
+
if (supportedLevels.length === 0) {
|
|
139
194
|
return null;
|
|
140
195
|
}
|
|
141
196
|
|
|
142
|
-
const options = normalizeThinkingLevels(
|
|
197
|
+
const options = normalizeThinkingLevels(supportedLevels);
|
|
143
198
|
const fallback = options.includes("off") ? "off" : options[0];
|
|
144
199
|
const resolvedValue =
|
|
145
|
-
(
|
|
146
|
-
options.includes(
|
|
147
|
-
|
|
148
|
-
(
|
|
149
|
-
options.includes(
|
|
150
|
-
|
|
200
|
+
(selectedThinkingLevel &&
|
|
201
|
+
options.includes(selectedThinkingLevel) &&
|
|
202
|
+
selectedThinkingLevel) ||
|
|
203
|
+
(defaultThinkingLevel &&
|
|
204
|
+
options.includes(defaultThinkingLevel) &&
|
|
205
|
+
defaultThinkingLevel) ||
|
|
151
206
|
fallback;
|
|
152
207
|
|
|
153
208
|
return {
|
|
154
209
|
key: "thinking",
|
|
155
210
|
value: resolvedValue,
|
|
156
|
-
placeholder:
|
|
157
|
-
selectedLabel:
|
|
211
|
+
placeholder: texts.thinkingLabels[resolvedValue],
|
|
212
|
+
selectedLabel: texts.thinkingLabels[resolvedValue],
|
|
158
213
|
icon: "brain",
|
|
159
214
|
options: options.map((level) => ({
|
|
160
215
|
value: level,
|
|
161
|
-
label:
|
|
216
|
+
label: texts.thinkingLabels[level],
|
|
162
217
|
})),
|
|
163
|
-
onValueChange: (value) =>
|
|
218
|
+
onValueChange: (value) => onValueChange(value as ChatThinkingLevel),
|
|
164
219
|
};
|
|
165
220
|
}
|