Studio: new-chat shortcut, composer draft autosave, archive threads (#5771)
* new-chat shortcut, composer draft autosave, archive threads * fix * Studio: harden chat UX additions for legacy threads and unavailable storage Two robustness fixes on top of the new chat UX features: - groupThreads: coerce archived to a boolean before comparing (Boolean(t.archived) !== archived). Threads from the older browser-only Studio, or any record predating the archived field, can carry archived === undefined/null. The raw `!== archived` comparison dropped those from BOTH the Recents and Archived sidebar groups, hiding existing chats. Treat missing as not-archived so legacy chats still appear in Recents. - composer draft autosave: wrap the localStorage read and write in try/catch. When storage is unavailable (private mode, disabled cookies, blocked storage) or full (quota exceeded), getItem/setItem throw; the throw in the restore effect would surface to React and break the chat page. Draft persistence is best-effort, so degrade quietly. Verified with bun unit tests on the real groupThreads (legacy undefined no longer vanishes) and Playwright across chromium, firefox and webkit (draft save/restore/isolation/clear, new-chat shortcut + crypto.randomUUID, and localStorage blocked/quota throw handling). tsc clean; no new eslint findings. * Fix composer draft bleed and orphan cleanup for PR #5771 Centralize composer draft storage in a small util and tighten two edge cases: - New chat draft bleed: every new chat shared the chat-draft:__new__ slot, so starting a fresh chat could restore the previous one's half-typed text. Clear that slot at every new chat entry point (sidebar buttons and Cmd/Ctrl+Shift+O). - Orphan drafts: deleting a thread left its chat-draft:<id> key behind. Clear the draft for every deleted thread id. New util utils/composer-draft.ts owns the key format and wraps localStorage in try/catch (private mode, blocked storage, quota), replacing the inline copy in thread.tsx so reads and writes stay best effort everywhere. * address review * fix: remove unused chat sidebar binding --------- Co-authored-by: Daniel Han <michaelhan2050@gmail.com> Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> Co-authored-by: imagineer99 <samleejackson0@gmail.com>
This commit is contained in:
parent
3427e3fd62
commit
911ceba7fa
6 changed files with 289 additions and 13 deletions
|
|
@ -6,7 +6,7 @@ import { Navbar } from "@/components/navbar";
|
|||
import { fetchDeviceType, usePlatformStore } from "@/config/env";
|
||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { SettingsDialog, useSettingsDialogStore } from "@/features/settings";
|
||||
import { useChatRuntimeStore } from "@/features/chat";
|
||||
import { clearNewChatDraft, useChatRuntimeStore } from "@/features/chat";
|
||||
import { useTrainingUnloadGuard } from "@/features/training";
|
||||
import { useSidebarPin } from "@/hooks/use-sidebar-pin";
|
||||
import { useT, type TranslationKey } from "@/i18n";
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
createRootRoute,
|
||||
redirect,
|
||||
useMatches,
|
||||
useNavigate,
|
||||
useRouterState,
|
||||
} from "@tanstack/react-router";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
|
|
@ -78,6 +79,7 @@ function RootLayout() {
|
|||
const hideNavbar = HIDDEN_NAVBAR_ROUTES.includes(pathname);
|
||||
const isChatRoute = pathname.startsWith("/chat");
|
||||
const { pinned, setPinned, togglePinned } = useSidebarPin();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useTrainingUnloadGuard();
|
||||
|
||||
|
|
@ -107,11 +109,24 @@ function RootLayout() {
|
|||
if ((e.metaKey || e.ctrlKey) && e.key === ",") {
|
||||
e.preventDefault();
|
||||
useSettingsDialogStore.getState().openDialog();
|
||||
return;
|
||||
}
|
||||
// Cmd/Ctrl+Shift+O opens a new chat.
|
||||
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.code === "KeyO") {
|
||||
e.preventDefault();
|
||||
clearNewChatDraft(); // fresh chat starts empty, no bleed from the last one
|
||||
const chatRuntime = useChatRuntimeStore.getState();
|
||||
chatRuntime.setActiveThreadId(null);
|
||||
chatRuntime.setActiveProjectId(null);
|
||||
void navigate({
|
||||
to: "/chat",
|
||||
search: { new: crypto.randomUUID() },
|
||||
});
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handler);
|
||||
return () => window.removeEventListener("keydown", handler);
|
||||
}, []);
|
||||
}, [navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isChatRoute) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue