import { scopeProjectRef } from "@t3tools/client-runtime";import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";import { useEffect } from "react"; import { ensurePrimaryEnvironmentReady, resolveInitialServerAuthGateState,} from "../environments/primary";import { useHandleNewThread } from "../hooks/useHandleNewThread";import { isTerminalFocused } from "../lib/terminalFocus";import { resolveShortcutCommand } from "../keybindings";import { selectThreadTerminalState, useTerminalStateStore } from "../terminalStateStore";import { useThreadSelectionStore } from "../threadSelectionStore";import { resolveSidebarNewThreadEnvMode } from "~/components/Sidebar.logic";import { useSettings } from "~/hooks/useSettings";import { useServerKeybindings } from "~/rpc/serverState"; function ChatRouteGlobalShortcuts() { const clearSelection = useThreadSelectionStore((state) => state.clearSelection); const selectedThreadKeysSize = useThreadSelectionStore((state) => state.selectedThreadKeys.size); const { activeDraftThread, activeThread, defaultProjectRef, handleNewThread, routeThreadRef } = useHandleNewThread(); const keybindings = useServerKeybindings(); const terminalOpen = useTerminalStateStore((state) => routeThreadRef ? selectThreadTerminalState(state.terminalStateByThreadKey, routeThreadRef).terminalOpen : false, ); const appSettings = useSettings(); useEffect(() => { const onWindowKeyDown = (event: KeyboardEvent) => { if (event.defaultPrevented) return; if (event.key === "Escape" && selectedThreadKeysSize > 0) { event.preventDefault(); clearSelection(); return; } const projectRef = activeThread ? scopeProjectRef(activeThread.environmentId, activeThread.projectId) : activeDraftThread && routeThreadRef ? scopeProjectRef(routeThreadRef.environmentId, activeDraftThread.projectId) : defaultProjectRef; if (!projectRef) return; const command = resolveShortcutCommand(event, keybindings, { context: { terminalFocus: isTerminalFocused(), terminalOpen, }, }); if (command === "chat.newLocal") { event.preventDefault(); event.stopPropagation(); void handleNewThread(projectRef, { envMode: resolveSidebarNewThreadEnvMode({ defaultEnvMode: appSettings.defaultThreadEnvMode, }), }); return; } if (command === "chat.new") { event.preventDefault(); event.stopPropagation(); void handleNewThread(projectRef, { branch: activeThread?.branch ?? activeDraftThread?.branch ?? null, worktreePath: activeThread?.worktreePath ?? activeDraftThread?.worktreePath ?? null, envMode: activeDraftThread?.envMode ?? (activeThread?.worktreePath ? "worktree" : "local"), }); return; } }; window.addEventListener("keydown", onWindowKeyDown); return () => { window.removeEventListener("keydown", onWindowKeyDown); }; }, [ activeDraftThread, activeThread, clearSelection, handleNewThread, keybindings, defaultProjectRef, routeThreadRef, selectedThreadKeysSize, terminalOpen, appSettings.defaultThreadEnvMode, ]); return null;} function ChatRouteLayout() { return ( <> <ChatRouteGlobalShortcuts /> <Outlet /> </> );} export const Route = createFileRoute("/_chat")({ beforeLoad: async () => { const [, authGateState] = await Promise.all([ ensurePrimaryEnvironmentReady(), resolveInitialServerAuthGateState(), ]); if (authGateState.status !== "authenticated") { throw redirect({ to: "/pair", replace: true }); } }, component: ChatRouteLayout,});
import { scopeProjectRef } from "@t3tools/client-runtime";
import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
import { useEffect } from "react";
import {
ensurePrimaryEnvironmentReady,
resolveInitialServerAuthGateState,
} from "../environments/primary";
import { useHandleNewThread } from "../hooks/useHandleNewThread";
import { isTerminalFocused } from "../lib/terminalFocus";
import { resolveShortcutCommand } from "../keybindings";
import { selectThreadTerminalState, useTerminalStateStore } from "../terminalStateStore";
import { useThreadSelectionStore } from "../threadSelectionStore";
import { resolveSidebarNewThreadEnvMode } from "~/components/Sidebar.logic";
import { useSettings } from "~/hooks/useSettings";
import { useServerKeybindings } from "~/rpc/serverState";
function ChatRouteGlobalShortcuts() {
const clearSelection = useThreadSelectionStore((state) => state.clearSelection);
const selectedThreadKeysSize = useThreadSelectionStore((state) => state.selectedThreadKeys.size);
const { activeDraftThread, activeThread, defaultProjectRef, handleNewThread, routeThreadRef } =
useHandleNewThread();
const keybindings = useServerKeybindings();
const terminalOpen = useTerminalStateStore((state) =>
routeThreadRef
? selectThreadTerminalState(state.terminalStateByThreadKey, routeThreadRef).terminalOpen
: false,
);
const appSettings = useSettings();
useEffect(() => {
const onWindowKeyDown = (event: KeyboardEvent) => {
if (event.defaultPrevented) return;
if (event.key === "Escape" && selectedThreadKeysSize > 0) {
event.preventDefault();
clearSelection();
return;
}
const projectRef = activeThread
? scopeProjectRef(activeThread.environmentId, activeThread.projectId)
: activeDraftThread && routeThreadRef
? scopeProjectRef(routeThreadRef.environmentId, activeDraftThread.projectId)
: defaultProjectRef;
if (!projectRef) return;
const command = resolveShortcutCommand(event, keybindings, {
context: {
terminalFocus: isTerminalFocused(),
terminalOpen,
},
});
if (command === "chat.newLocal") {
event.preventDefault();
event.stopPropagation();
void handleNewThread(projectRef, {
envMode: resolveSidebarNewThreadEnvMode({
defaultEnvMode: appSettings.defaultThreadEnvMode,
}),
});
return;
}
if (command === "chat.new") {
event.preventDefault();
event.stopPropagation();
void handleNewThread(projectRef, {
branch: activeThread?.branch ?? activeDraftThread?.branch ?? null,
worktreePath: activeThread?.worktreePath ?? activeDraftThread?.worktreePath ?? null,
envMode:
activeDraftThread?.envMode ?? (activeThread?.worktreePath ? "worktree" : "local"),
});
return;
}
};
window.addEventListener("keydown", onWindowKeyDown);
return () => {
window.removeEventListener("keydown", onWindowKeyDown);
};
}, [
activeDraftThread,
activeThread,
clearSelection,
handleNewThread,
keybindings,
defaultProjectRef,
routeThreadRef,
selectedThreadKeysSize,
terminalOpen,
appSettings.defaultThreadEnvMode,
]);
return null;
}
function ChatRouteLayout() {
return (
<>
<ChatRouteGlobalShortcuts />
<Outlet />
</>
);
}
export const Route = createFileRoute("/_chat")({
beforeLoad: async () => {
const [, authGateState] = await Promise.all([
ensurePrimaryEnvironmentReady(),
resolveInitialServerAuthGateState(),
]);
if (authGateState.status !== "authenticated") {
throw redirect({ to: "/pair", replace: true });
}
},
component: ChatRouteLayout,
});