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 =...@@ -304,7 +304,8 @@ type ComposerCommandItem =
304 | {304 | {
305 id: string;305 id: string;
306 type: "model";306 type: "model";
307 model: string;307 provider: ProviderKind;
308 model: ModelSlug;
308 label: string;309 label: string;
309 description: string;310 description: string;
310 };311 };
...@@ -658,27 +659,21 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -658,27 +659,21 @@ export default function ChatView({ threadId }: ChatViewProps) {
658 );659 );
659 const selectedModel = resolveModelSlugForProvider(selectedProvider, composerDraft.model ?? baseThreadModel);660 const selectedModel = resolveModelSlugForProvider(selectedProvider, composerDraft.model ?? baseThreadModel);
660 const selectedEffort = composerDraft.effort ?? DEFAULT_REASONING;661 const selectedEffort = composerDraft.effort ?? DEFAULT_REASONING;
661 const modelOptions = useMemo(662 const searchableModelOptions = useMemo(
662 () =>663 () =>
663 selectedProvider === "codex"664 PROVIDER_OPTIONS.filter((option) => option.available).flatMap((option) =>
664 ? getAppModelOptions(settings.customCodexModels, selectedModel)665 getModelOptions(option.value).map(({ slug, name }) => ({
665 : getModelOptions(selectedProvider),666 provider: option.value,
666 [selectedModel, selectedProvider, settings.customCodexModels],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 useEffect(() => {677 useEffect(() => {
683 if (!activeThread?.id || !sessionProvider) return;678 if (!activeThread?.id || !sessionProvider) return;
684 setSelectedProviderByThread((existing) => {679 setSelectedProviderByThread((existing) => {
...@@ -961,14 +956,19 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -961,14 +956,19 @@ export default function ChatView({ threadId }: ChatViewProps) {
961 ];956 ];
962 }957 }
963958
964 return slashModelOptions.map(({ slug, name }) => ({959 return searchableModelOptions.filter(({ searchSlug, searchName, searchProvider }) => {
965 id: `model:${slug}`,960 const query = composerTrigger.query.trim().toLowerCase();
966 type: "model" as const,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 model: slug,967 model: slug,
968 label: name,968 label: name,
969 description: slug,969 description: `${providerLabel} · ${slug}`,
970 }));970 }));
971 }, [composerTrigger, slashModelOptions, workspaceEntries]);971 }, [composerTrigger, searchableModelOptions, workspaceEntries]);
972 const composerMenuOpen = Boolean(composerTrigger);972 const composerMenuOpen = Boolean(composerTrigger);
973 const activeComposerMenuItem = useMemo(973 const activeComposerMenuItem = useMemo(
974 () =>974 () =>
...@@ -2255,24 +2255,6 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -2255,24 +2255,6 @@ export default function ChatView({ threadId }: ChatViewProps) {
2255 [activeThreadId, setStoreThreadError],2255 [activeThreadId, setStoreThreadError],
2256 );2256 );
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 );
2276 const onProviderModelSelect = useCallback(2258 const onProviderModelSelect = useCallback(
2277 (provider: ProviderKind, model: ModelSlug) => {2259 (provider: ProviderKind, model: ModelSlug) => {
2278 const api = readNativeApi();2260 const api = readNativeApi();
...@@ -2390,7 +2372,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -2390,7 +2372,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
2390 }2372 }
2391 return;2373 return;
2392 }2374 }
2393 onModelSelect(item.model);2375 onProviderModelSelect(item.provider, item.model);
2394 const applied = applyPromptReplacement(trigger.rangeStart, trigger.rangeEnd, "", {2376 const applied = applyPromptReplacement(trigger.rangeStart, trigger.rangeEnd, "", {
2395 expectedText: expectedToken,2377 expectedText: expectedToken,
2396 });2378 });
...@@ -2398,7 +2380,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -2398,7 +2380,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
2398 setComposerHighlightedItemId(null);2380 setComposerHighlightedItemId(null);
2399 }2381 }
2400 },2382 },
2401 [applyPromptReplacement, onModelSelect, resolveActiveComposerTrigger],2383 [applyPromptReplacement, onProviderModelSelect, resolveActiveComposerTrigger],
2402 );2384 );
2403 const onComposerMenuItemHighlighted = useCallback((itemId: string | null) => {2385 const onComposerMenuItemHighlighted = useCallback((itemId: string | null) => {
2404 setComposerHighlightedItemId(itemId);2386 setComposerHighlightedItemId(itemId);