chat: remember last model

This commit is contained in:
2026-02-14 22:06:30 -08:00
parent a76b4ba232
commit fb306e154a
8 changed files with 173 additions and 9 deletions

View File

@@ -37,6 +37,10 @@ type SidebarItem = SidebarSelection & {
title: string;
updatedAt: string;
createdAt: string;
initiatedProvider: Provider | null;
initiatedModel: string | null;
lastUsedProvider: Provider | null;
lastUsedModel: string | null;
};
type ContextMenuState = {
item: SidebarSelection;
@@ -89,11 +93,26 @@ function loadStoredModelPreferences() {
}
function pickProviderModel(options: string[], preferred: string | null, fallback: string | null = null) {
if (preferred && options.includes(preferred)) return preferred;
if (fallback && options.includes(fallback)) return fallback;
if (preferred && options.includes(preferred)) return preferred;
return options[0] ?? "";
}
function getProviderLabel(provider: Provider | null | undefined) {
if (provider === "openai") return "OpenAI";
if (provider === "anthropic") return "Anthropic";
if (provider === "xai") return "xAI";
return "";
}
function getChatModelSelection(chat: Pick<ChatSummary, "lastUsedProvider" | "lastUsedModel"> | Pick<ChatDetail, "lastUsedProvider" | "lastUsedModel"> | null) {
if (!chat?.lastUsedProvider || !chat.lastUsedModel?.trim()) return null;
return {
provider: chat.lastUsedProvider,
model: chat.lastUsedModel.trim(),
};
}
type ModelComboboxProps = {
options: string[];
value: string;
@@ -212,6 +231,10 @@ function buildSidebarItems(chats: ChatSummary[], searches: SearchSummary[]): Sid
title: getChatTitle(chat),
updatedAt: chat.updatedAt,
createdAt: chat.createdAt,
initiatedProvider: chat.initiatedProvider,
initiatedModel: chat.initiatedModel,
lastUsedProvider: chat.lastUsedProvider,
lastUsedModel: chat.lastUsedModel,
})),
...searches.map((search) => ({
kind: "search" as const,
@@ -219,6 +242,10 @@ function buildSidebarItems(chats: ChatSummary[], searches: SearchSummary[]): Sid
title: getSearchTitle(search),
updatedAt: search.updatedAt,
createdAt: search.createdAt,
initiatedProvider: null,
initiatedModel: null,
lastUsedProvider: null,
lastUsedModel: null,
})),
];
@@ -473,6 +500,16 @@ export default function App() {
return searches.find((search) => search.id === selectedItem.id) ?? null;
}, [searches, selectedItem]);
useEffect(() => {
if (draftKind || selectedItem?.kind !== "chat") return;
const detailSelection = selectedChat?.id === selectedItem.id ? getChatModelSelection(selectedChat) : null;
const summarySelection = getChatModelSelection(selectedChatSummary);
const nextSelection = detailSelection ?? summarySelection;
if (!nextSelection) return;
setProvider(nextSelection.provider);
setModel(nextSelection.model);
}, [draftKind, selectedChat, selectedChatSummary, selectedItem]);
const selectedTitle = useMemo(() => {
if (draftKind === "chat") return "New chat";
if (draftKind === "search") return "New search";
@@ -611,6 +648,10 @@ export default function App() {
title: chat.title,
createdAt: chat.createdAt,
updatedAt: chat.updatedAt,
initiatedProvider: chat.initiatedProvider,
initiatedModel: chat.initiatedModel,
lastUsedProvider: chat.lastUsedProvider,
lastUsedModel: chat.lastUsedModel,
messages: [],
});
setSelectedSearch(null);
@@ -937,6 +978,9 @@ export default function App() {
) : null}
{sidebarItems.map((item) => {
const active = selectedItem?.kind === item.kind && selectedItem.id === item.id;
const initiatedLabel = item.kind === "chat" && item.initiatedModel
? `${getProviderLabel(item.initiatedProvider)}${item.initiatedProvider ? " · " : ""}${item.initiatedModel}`
: null;
return (
<button
key={`${item.kind}-${item.id}`}
@@ -956,7 +1000,12 @@ export default function App() {
{item.kind === "chat" ? <MessageSquare className="h-3.5 w-3.5" /> : <Search className="h-3.5 w-3.5" />}
<p className="truncate text-sm font-medium">{item.title}</p>
</div>
<p className={cn("mt-1 text-xs", active ? "text-violet-100/90" : "text-violet-300/60")}>{formatDate(item.updatedAt)}</p>
<div className="mt-1 flex items-center gap-2 text-xs">
<p className={cn("shrink-0", active ? "text-violet-100/90" : "text-violet-300/60")}>{formatDate(item.updatedAt)}</p>
{initiatedLabel ? (
<p className={cn("ml-auto truncate text-right", active ? "text-violet-200/65" : "text-violet-300/45")}>{initiatedLabel}</p>
) : null}
</div>
</button>
);
})}