| ... | ... | @@ -304,7 +304,8 @@ type ComposerCommandItem = |
| 304 | 304 | | { |
| 305 | 305 | id: string; |
| 306 | 306 | type: "model"; |
| 307 | | model: string; |
| 307 | provider: ProviderKind; |
| 308 | model: ModelSlug; |
| 308 | 309 | label: string; |
| 309 | 310 | description: string; |
| 310 | 311 | }; |
| ... | ... | @@ -658,27 +659,21 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 658 | 659 | ); |
| 659 | 660 | const selectedModel = resolveModelSlugForProvider(selectedProvider, composerDraft.model ?? baseThreadModel); |
| 660 | 661 | const selectedEffort = composerDraft.effort ?? DEFAULT_REASONING; |
| 661 | | const modelOptions = useMemo( |
| 662 | const searchableModelOptions = useMemo( |
| 662 | 663 | () => |
| 663 | | selectedProvider === "codex" |
| 664 | | ? getAppModelOptions(settings.customCodexModels, selectedModel) |
| 665 | | : getModelOptions(selectedProvider), |
| 666 | | [selectedModel, selectedProvider, settings.customCodexModels], |
| 664 | PROVIDER_OPTIONS.filter((option) => option.available).flatMap((option) => |
| 665 | getModelOptions(option.value).map(({ slug, name }) => ({ |
| 666 | provider: option.value, |
| 667 | providerLabel: option.label, |
| 668 | slug, |
| 669 | name, |
| 670 | searchSlug: slug.toLowerCase(), |
| 671 | searchName: name.toLowerCase(), |
| 672 | searchProvider: option.label.toLowerCase(), |
| 673 | })), |
| 674 | ), |
| 675 | [], |
| 667 | 676 | ); |
| 668 | | const slashModelOptions = useMemo(() => { |
| 669 | | if (selectedProvider === "codex") { |
| 670 | | return getSlashModelOptions(settings.customCodexModels, composerTrigger?.query ?? "", selectedModel); |
| 671 | | } |
| 672 | | const query = (composerTrigger?.query ?? "").trim().toLowerCase(); |
| 673 | | if (!query) { |
| 674 | | return modelOptions; |
| 675 | | } |
| 676 | | return modelOptions.filter(({ slug, name }) => { |
| 677 | | const searchSlug = slug.toLowerCase(); |
| 678 | | const searchName = name.toLowerCase(); |
| 679 | | return searchSlug.includes(query) || searchName.includes(query); |
| 680 | | }); |
| 681 | | }, [composerTrigger?.query, modelOptions, selectedModel, selectedProvider, settings.customCodexModels]); |
| 682 | 677 | useEffect(() => { |
| 683 | 678 | if (!activeThread?.id || !sessionProvider) return; |
| 684 | 679 | setSelectedProviderByThread((existing) => { |
| ... | ... | @@ -961,14 +956,19 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 961 | 956 | ]; |
| 962 | 957 | } |
| 963 | 958 | |
| 964 | | return slashModelOptions.map(({ slug, name }) => ({ |
| 965 | | id: `model:${slug}`, |
| 966 | | type: "model" as const, |
| 959 | return searchableModelOptions.filter(({ searchSlug, searchName, searchProvider }) => { |
| 960 | const query = composerTrigger.query.trim().toLowerCase(); |
| 961 | if (!query) return true; |
| 962 | return searchSlug.includes(query) || searchName.includes(query) || searchProvider.includes(query); |
| 963 | }).map(({ provider, providerLabel, slug, name }) => ({ |
| 964 | id: `model:${provider}:${slug}`, |
| 965 | type: "model", |
| 966 | provider, |
| 967 | 967 | model: slug, |
| 968 | 968 | label: name, |
| 969 | | description: slug, |
| 969 | description: `${providerLabel} · ${slug}`, |
| 970 | 970 | })); |
| 971 | | }, [composerTrigger, slashModelOptions, workspaceEntries]); |
| 971 | }, [composerTrigger, searchableModelOptions, workspaceEntries]); |
| 972 | 972 | const composerMenuOpen = Boolean(composerTrigger); |
| 973 | 973 | const activeComposerMenuItem = useMemo( |
| 974 | 974 | () => |
| ... | ... | @@ -2255,24 +2255,6 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 2255 | 2255 | [activeThreadId, setStoreThreadError], |
| 2256 | 2256 | ); |
| 2257 | 2257 | |
| 2258 | | const onModelSelect = useCallback( |
| 2259 | | (model: string) => { |
| 2260 | | const normalizedModel = normalizeModelSlug(model); |
| 2261 | | const resolvedModel = normalizedModel ?? baseThreadModel; |
| 2262 | | setComposerDraftModel(threadId, resolvedModel === baseThreadModel ? null : resolvedModel); |
| 2263 | | const api = readNativeApi(); |
| 2264 | | if (api && isServerThread && activeThread) { |
| 2265 | | void api.orchestration.dispatchCommand({ |
| 2266 | | type: "thread.meta.update", |
| 2267 | | commandId: newCommandId(), |
| 2268 | | threadId: activeThread.id, |
| 2269 | | model: resolveModelSlugForProvider(selectedProvider, model), |
| 2270 | | }); |
| 2271 | | } |
| 2272 | | scheduleComposerFocus(); |
| 2273 | | }, |
| 2274 | | [activeThread, scheduleComposerFocus, selectedProvider], |
| 2275 | | ); |
| 2276 | 2258 | const onProviderModelSelect = useCallback( |
| 2277 | 2259 | (provider: ProviderKind, model: ModelSlug) => { |
| 2278 | 2260 | const api = readNativeApi(); |
| ... | ... | @@ -2390,7 +2372,7 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 2390 | 2372 | } |
| 2391 | 2373 | return; |
| 2392 | 2374 | } |
| 2393 | | onModelSelect(item.model); |
| 2375 | onProviderModelSelect(item.provider, item.model); |
| 2394 | 2376 | const applied = applyPromptReplacement(trigger.rangeStart, trigger.rangeEnd, "", { |
| 2395 | 2377 | expectedText: expectedToken, |
| 2396 | 2378 | }); |
| ... | ... | @@ -2398,7 +2380,7 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 2398 | 2380 | setComposerHighlightedItemId(null); |
| 2399 | 2381 | } |
| 2400 | 2382 | }, |
| 2401 | | [applyPromptReplacement, onModelSelect, resolveActiveComposerTrigger], |
| 2383 | [applyPromptReplacement, onProviderModelSelect, resolveActiveComposerTrigger], |
| 2402 | 2384 | ); |
| 2403 | 2385 | const onComposerMenuItemHighlighted = useCallback((itemId: string | null) => { |
| 2404 | 2386 | setComposerHighlightedItemId(itemId); |