diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
index f583ff69a71562e40ac9a94d9b2a9c59d6b69822..1119967bd6afc49e8c89de095a4fdd04104f914c 100644
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -107,7 +107,15 @@ import {
CheckIcon,
} from "lucide-react";
import { Button } from "./ui/button";
-import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "./ui/select";
+import {
+ Select,
+ SelectGroup,
+ SelectGroupLabel,
+ SelectItem,
+ SelectPopup,
+ SelectTrigger,
+ SelectValue,
+} from "./ui/select";
import { Separator } from "./ui/separator";
import { Group, GroupSeparator } from "./ui/group";
import { Menu, MenuItem, MenuPopup, MenuShortcut, MenuTrigger } from "./ui/menu";
@@ -2265,13 +2273,22 @@ export default function ChatView({ threadId }: ChatViewProps) {
},
[activeThread, scheduleComposerFocus, selectedProvider],
);
- const onProviderSelect = useCallback(
- (provider: ProviderKind) => {
+ const onProviderModelSelect = useCallback(
+ (provider: ProviderKind, model: ModelSlug) => {
+ const api = readNativeApi();
if (!activeThread) return;
setSelectedProviderByThread((existing) => ({
...existing,
[activeThread.id]: provider,
}));
+ if (api) {
+ void api.orchestration.dispatchCommand({
+ type: "thread.meta.update",
+ commandId: newCommandId(),
+ threadId: activeThread.id,
+ model: resolveModelSlugForProvider(provider, model),
+ });
+ }
scheduleComposerFocus();
},
[activeThread, scheduleComposerFocus],
@@ -2712,17 +2729,11 @@ export default function ChatView({ threadId }: ChatViewProps) {
{/* Bottom toolbar */}
- {/* Model picker */}
-
-
- {/* Divider */}
-
-
- {/* Model picker */}
-
{/* Divider */}
@@ -3866,54 +3877,65 @@ const MessagesTimeline = memo(function MessagesTimeline({
);
});
-const ModelPicker = memo(function ModelPicker(props: {
- model: ModelSlug;
- options: ReadonlyArray<{ readonly slug: ModelSlug; readonly name: string }>;
- onModelChange: (model: ModelSlug) => void;
-}) {
- return (
-
- );
-});
+const PROVIDER_MODEL_GROUPS: ReadonlyArray<{
+ readonly provider: ProviderKind;
+ readonly label: string;
+ readonly options: ReadonlyArray<{ readonly slug: ModelSlug; readonly name: string }>;
+}> = PROVIDER_OPTIONS.filter((option) => option.available).map((option) => ({
+ provider: option.value,
+ label: option.label,
+ options: getModelOptions(option.value),
+}));
-const ProviderPicker = memo(function ProviderPicker(props: {
+const ProviderModelPicker = memo(function ProviderModelPicker(props: {
provider: ProviderKind;
- onProviderChange: (provider: ProviderKind) => void;
+ model: ModelSlug;
+ onProviderModelChange: (provider: ProviderKind, model: ModelSlug) => void;
}) {
+ const items = PROVIDER_MODEL_GROUPS.flatMap(({ provider, label, options }) =>
+ options.map(({ slug, name }) => ({
+ label: `${label} ยท ${name}`,
+ value: `${provider}:${slug}`,
+ })),
+ );
+
return (