authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-27 10:18:01-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-05 23:00:41-08:00
log4797f99cf8645c9e2bd81ea421b788742eeb6ec7
treee0b083d9f68955f4be2ca03471c3feb31b45892e
parent50f35351a2c96c022de1f4329ce92d1f0b60a990

Include all provider models in /model slash command

Co-authored-by: codex <codex@users.noreply.github.com>

1 files changed, 27 insertions(+), 45 deletions(-)

apps/web/src/components/ChatView.tsx+27-45
......@@ -304,7 +304,8 @@ type ComposerCommandItem =
304304 | {
305305 id: string;
306306 type: "model";
307 model: string;
307 provider: ProviderKind;
308 model: ModelSlug;
308309 label: string;
309310 description: string;
310311 };
......@@ -658,27 +659,21 @@ export default function ChatView({ threadId }: ChatViewProps) {
658659 );
659660 const selectedModel = resolveModelSlugForProvider(selectedProvider, composerDraft.model ?? baseThreadModel);
660661 const selectedEffort = composerDraft.effort ?? DEFAULT_REASONING;
661 const modelOptions = useMemo(
662 const searchableModelOptions = useMemo(
662663 () =>
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 [],
667676 );
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]);
682677 useEffect(() => {
683678 if (!activeThread?.id || !sessionProvider) return;
684679 setSelectedProviderByThread((existing) => {
......@@ -961,14 +956,19 @@ export default function ChatView({ threadId }: ChatViewProps) {
961956 ];
962957 }
963958
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,
967967 model: slug,
968968 label: name,
969 description: slug,
969 description: `${providerLabel} · ${slug}`,
970970 }));
971 }, [composerTrigger, slashModelOptions, workspaceEntries]);
971 }, [composerTrigger, searchableModelOptions, workspaceEntries]);
972972 const composerMenuOpen = Boolean(composerTrigger);
973973 const activeComposerMenuItem = useMemo(
974974 () =>
......@@ -2255,24 +2255,6 @@ export default function ChatView({ threadId }: ChatViewProps) {
22552255 [activeThreadId, setStoreThreadError],
22562256 );
22572257
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 );
22762258 const onProviderModelSelect = useCallback(
22772259 (provider: ProviderKind, model: ModelSlug) => {
22782260 const api = readNativeApi();
......@@ -2390,7 +2372,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
23902372 }
23912373 return;
23922374 }
2393 onModelSelect(item.model);
2375 onProviderModelSelect(item.provider, item.model);
23942376 const applied = applyPromptReplacement(trigger.rangeStart, trigger.rangeEnd, "", {
23952377 expectedText: expectedToken,
23962378 });
......@@ -2398,7 +2380,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
23982380 setComposerHighlightedItemId(null);
23992381 }
24002382 },
2401 [applyPromptReplacement, onModelSelect, resolveActiveComposerTrigger],
2383 [applyPromptReplacement, onProviderModelSelect, resolveActiveComposerTrigger],
24022384 );
24032385 const onComposerMenuItemHighlighted = useCallback((itemId: string | null) => {
24042386 setComposerHighlightedItemId(itemId);