History

Load terminal keybindings via React Query server config helper

- add `serverConfigQueryOptions` in new `serverReactQuery` utility - switch `ChatView` from manual `getConfig` effect/state to `useQuery` - default to empty keybindings while config is unavailable

Julius Marminge committed 9 months ago

Commit 61e9ba41 parent ad624752 files · +28 −26Browse files

Unified · Split

2 files
  1. apps/web/src/components/ChatView.tsx +8 −26
  2. apps/web/src/lib/serverReactQuery.ts +20 −0
apps/web/src/components/ChatView.tsx+8−26
@@ -7,6 +7,7 @@ import { · 6 unmodified lines
77 PROVIDER_SEND_TURN_MAX_IMAGE_BYTES,
88 type ProviderApprovalDecision,
99 type ProviderSendTurnAttachmentInput,
10+ type KeybindingsConfig,
1011 } from "@t3tools/contracts";
1112 import {
1213 type ClipboardEvent,
@@ -26,6 +27,7 @@ import { · 13 unmodified lines
2627 gitBranchesQueryOptions,
2728 gitCreateWorktreeMutationOptions,
2829 } from "~/lib/gitReactQuery";
30+import { serverConfigQueryOptions } from "~/lib/serverReactQuery";
2931
3032 import { isElectron } from "../env";
3133 import { buildBootstrapInput } from "../historyBootstrap";
@@ -55,7 +57,6 @@ import { · 23 unmodified lines
5557 isTerminalNewShortcut,
5658 isTerminalSplitShortcut,
5759 isTerminalToggleShortcut,
58− type ResolvedTerminalKeybindings,
5960 shortcutLabelForCommand,
6061 } from "../keybindings";
6162 import ChatMarkdown from "./ChatMarkdown";
@@ -90,6 +91,7 @@ const MAX_VISIBLE_WORK_LOG_ENTRIES = 6; · 28 unmodified lines
9091 const IMAGE_SIZE_LIMIT_LABEL = `${Math.round(PROVIDER_SEND_TURN_MAX_IMAGE_BYTES / (1024 * 1024))}MB`;
9192 const IMAGE_ONLY_BOOTSTRAP_PROMPT =
9293 "[User attached one or more images without additional text. Respond using the conversation context and the attached image(s).]";
94+const EMPTY_KEYBINDINGS: KeybindingsConfig = [];
9395
9496 function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {
9597 if (tone === "error") return "text-rose-300/50 dark:text-rose-300/50";
@@ -151,7 +153,6 @@ export default function ChatView() { · 55 unmodified lines
151153 const [expandedWorkGroups, setExpandedWorkGroups] = useState<Record<string, boolean>>({});
152154 const [nowTick, setNowTick] = useState(() => Date.now());
153155 const [terminalFocusRequestId, setTerminalFocusRequestId] = useState(0);
154− const [terminalKeybindings, setTerminalKeybindings] = useState<ResolvedTerminalKeybindings>([]);
155156 const messagesScrollRef = useRef<HTMLDivElement>(null);
156157 const messagesEndRef = useRef<HTMLDivElement>(null);
157158 const shouldAutoScrollRef = useRef(true);
@@ -262,6 +263,11 @@ export default function ChatView() { · 104 unmodified lines
262263 } as const);
263264 const gitCwd = activeThread?.worktreePath ?? activeProject?.cwd ?? null;
264265 const branchesQuery = useQuery(gitBranchesQueryOptions(api, gitCwd));
266+ const terminalKeybindingsQuery = useQuery({
267+ ...serverConfigQueryOptions(api),
268+ select: (config) => config.keybindings,
269+ });
270+ const terminalKeybindings = terminalKeybindingsQuery.data ?? EMPTY_KEYBINDINGS;
265271 // Default true while loading to avoid toolbar flicker.
266272 const isGitRepo = branchesQuery.data?.isRepo ?? true;
267273 const splitTerminalShortcutLabel = useMemo(
@@ -279,30 +285,6 @@ export default function ChatView() { · 11 unmodified lines
279285 (activeThread.session !== null && activeThread.session.status !== "closed")),
280286 );
281287
282− useEffect(() => {
283− if (!api || !api.server || typeof api.server.getConfig !== "function") {
284− setTerminalKeybindings([]);
285− return;
286− }
287−
288− let disposed = false;
289−
290− void api.server
291− .getConfig()
292− .then((config) => {
293− if (disposed) return;
294− setTerminalKeybindings(config.keybindings);
295− })
296− .catch(() => {
297− if (disposed) return;
298− setTerminalKeybindings([]);
299− });
300−
301− return () => {
302− disposed = true;
303− };
304− }, [api]);
305−
306288 const revokePreviewUrls = useCallback((images: Array<{ previewUrl?: string }>) => {
307289 for (const image of images) {
308290 if (!image.previewUrl) continue;
apps/web/src/lib/serverReactQuery.ts+20−0
@@ -0,0 +1,20 @@
1+import type { NativeApi } from "@t3tools/contracts";
2+import { queryOptions } from "@tanstack/react-query";
3+
4+export const serverQueryKeys = {
5+ all: ["server"] as const,
6+ config: () => ["server", "config"] as const,
7+};
8+
9+export function serverConfigQueryOptions(api: NativeApi | undefined) {
10+ return queryOptions({
11+ queryKey: serverQueryKeys.config(),
12+ queryFn: async () => {
13+ if (!api || !api.server || typeof api.server.getConfig !== "function") {
14+ throw new Error("Server config is unavailable.");
15+ }
16+ return api.server.getConfig();
17+ },
18+ enabled: !!api && !!api.server && typeof api.server.getConfig === "function",
19+ });
20+}