authorgravatar for 51714798+juliusmarminge@users.noreply.github.comJulius Marminge <51714798+juliusmarminge@users.noreply.github.com> 2026-06-12 19:54:08-07:00
committergravatar for me@theo.liTheo Browne <me@theo.li> 2026-06-13 22:18:37-07:00
log1b4317ec35da7cb4fb416d49f7e2a8673d536d05
tree589400839324efdc8cc88d2f83db2eaba8332499
parentf452d63fefdf00958e86b8d1f920aa25eb03f484

Restore terminal drawer header toggle


3 files changed, 47 insertions(+), 44 deletions(-)

apps/web/src/components/ChatView.browser.tsx+14-43
......@@ -23,7 +23,6 @@ import {
2323 ServerConfig as ServerConfigSchema,
2424} from "@t3tools/contracts";
2525import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime";
26import { DEFAULT_RESOLVED_KEYBINDINGS } from "@t3tools/shared/keybindings";
2726import { createModelCapabilities, createModelSelection } from "@t3tools/shared/model";
2827import { RouterProvider, createMemoryHistory } from "@tanstack/react-router";
2928import * as Option from "effect/Option";
......@@ -2065,26 +2064,15 @@ describe("ChatView timeline estimator parity (full app)", () => {
20652064 targetMessageId: "msg-user-open-empty-terminal-drawer" as MessageId,
20662065 targetText: "open empty terminal drawer",
20672066 }),
2068 configureFixture: (nextFixture) => {
2069 nextFixture.serverConfig = {
2070 ...nextFixture.serverConfig,
2071 keybindings: DEFAULT_RESOLVED_KEYBINDINGS.filter(
2072 (binding) => binding.command === "terminal.toggle",
2073 ),
2074 };
2075 },
20762067 });
20772068
20782069 try {
2079 window.dispatchEvent(
2080 new KeyboardEvent("keydown", {
2081 key: "j",
2082 metaKey: isMacPlatform(navigator.platform),
2083 ctrlKey: !isMacPlatform(navigator.platform),
2084 bubbles: true,
2085 cancelable: true,
2086 }),
2070 const terminalToggle = await waitForElement(
2071 () =>
2072 document.querySelector<HTMLButtonElement>('button[aria-label="Toggle terminal drawer"]'),
2073 "Unable to find terminal drawer toggle.",
20872074 );
2075 terminalToggle.click();
20882076
20892077 await vi.waitFor(
20902078 () => {
......@@ -2116,28 +2104,14 @@ describe("ChatView timeline estimator parity (full app)", () => {
21162104 targetMessageId: "msg-user-open-inline-terminal-panel" as MessageId,
21172105 targetText: "open inline terminal panel",
21182106 }),
2119 configureFixture: (nextFixture) => {
2120 nextFixture.serverConfig = {
2121 ...nextFixture.serverConfig,
2122 keybindings: DEFAULT_RESOLVED_KEYBINDINGS.filter(
2123 (binding) =>
2124 binding.command === "rightPanel.toggle" || binding.command === "terminal.toggle",
2125 ),
2126 };
2127 },
21282107 });
21292108
21302109 try {
2131 window.dispatchEvent(
2132 new KeyboardEvent("keydown", {
2133 key: "b",
2134 altKey: true,
2135 metaKey: isMacPlatform(navigator.platform),
2136 ctrlKey: !isMacPlatform(navigator.platform),
2137 bubbles: true,
2138 cancelable: true,
2139 }),
2110 const rightPanelToggle = await waitForElement(
2111 () => document.querySelector<HTMLButtonElement>('button[aria-label="Toggle right panel"]'),
2112 "Unable to find right panel toggle.",
21402113 );
2114 rightPanelToggle.click();
21412115
21422116 const addSurface = await waitForElement(
21432117 () => document.querySelector<HTMLButtonElement>('button[aria-label="Add panel surface"]'),
......@@ -2213,15 +2187,12 @@ describe("ChatView timeline estimator parity (full app)", () => {
22132187 { timeout: 8_000, interval: 16 },
22142188 );
22152189
2216 window.dispatchEvent(
2217 new KeyboardEvent("keydown", {
2218 key: "j",
2219 metaKey: isMacPlatform(navigator.platform),
2220 ctrlKey: !isMacPlatform(navigator.platform),
2221 bubbles: true,
2222 cancelable: true,
2223 }),
2190 const drawerToggle = await waitForElement(
2191 () =>
2192 document.querySelector<HTMLButtonElement>('button[aria-label="Toggle terminal drawer"]'),
2193 "Unable to find terminal drawer toggle.",
22242194 );
2195 drawerToggle.click();
22252196
22262197 await vi.waitFor(() => {
22272198 expect(
apps/web/src/components/ChatView.tsx+3
......@@ -4501,6 +4501,8 @@ export default function ChatView(props: ChatViewProps) {
45014501 }
45024502 keybindings={keybindings}
45034503 availableEditors={availableEditors}
4504 terminalAvailable={Boolean(activeProject)}
4505 terminalOpen={Boolean(terminalUiState.terminalOpen)}
45044506 rightPanelAvailable={Boolean(activeProject)}
45054507 rightPanelOpen={rightPanelOpen}
45064508 gitCwd={gitCwd}
......@@ -4508,6 +4510,7 @@ export default function ChatView(props: ChatViewProps) {
45084510 onAddProjectScript={saveProjectScript}
45094511 onUpdateProjectScript={updateProjectScript}
45104512 onDeleteProjectScript={deleteProjectScript}
4513 onToggleTerminal={toggleTerminalVisibility}
45114514 onToggleRightPanel={toggleRightPanel}
45124515 />
45134516 </header>
apps/web/src/components/chat/ChatHeader.tsx+30-1
......@@ -9,7 +9,7 @@ import { scopeThreadRef } from "@t3tools/client-runtime";
99import { memo } from "react";
1010import GitActionsControl from "../GitActionsControl";
1111import { type DraftId } from "~/composerDraftStore";
12import { PanelRightIcon } from "lucide-react";
12import { PanelBottomIcon, PanelRightIcon } from "lucide-react";
1313import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
1414import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl";
1515import { Toggle } from "../ui/toggle";
......@@ -29,6 +29,8 @@ interface ChatHeaderProps {
2929 preferredScriptId: string | null;
3030 keybindings: ResolvedKeybindingsConfig;
3131 availableEditors: ReadonlyArray<EditorId>;
32 terminalAvailable: boolean;
33 terminalOpen: boolean;
3234 rightPanelAvailable: boolean;
3335 rightPanelOpen: boolean;
3436 gitCwd: string | null;
......@@ -36,6 +38,7 @@ interface ChatHeaderProps {
3638 onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;
3739 onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;
3840 onDeleteProjectScript: (scriptId: string) => Promise<void>;
41 onToggleTerminal: () => void;
3942 onToggleRightPanel: () => void;
4043}
4144
......@@ -62,6 +65,8 @@ export const ChatHeader = memo(function ChatHeader({
6265 preferredScriptId,
6366 keybindings,
6467 availableEditors,
68 terminalAvailable,
69 terminalOpen,
6570 rightPanelAvailable,
6671 rightPanelOpen,
6772 gitCwd,
......@@ -69,6 +74,7 @@ export const ChatHeader = memo(function ChatHeader({
6974 onAddProjectScript,
7075 onUpdateProjectScript,
7176 onDeleteProjectScript,
77 onToggleTerminal,
7278 onToggleRightPanel,
7379}: ChatHeaderProps) {
7480 const primaryEnvironmentId = usePrimaryEnvironmentId();
......@@ -77,6 +83,7 @@ export const ChatHeader = memo(function ChatHeader({
7783 activeThreadEnvironmentId,
7884 primaryEnvironmentId,
7985 });
86 const terminalShortcutLabel = shortcutLabelForCommand(keybindings, "terminal.toggle");
8087 const rightPanelShortcutLabel = shortcutLabelForCommand(keybindings, "rightPanel.toggle");
8188
8289 return (
......@@ -123,6 +130,28 @@ export const ChatHeader = memo(function ChatHeader({
123130 {...(draftId ? { draftId } : {})}
124131 />
125132 )}
133 <Tooltip>
134 <TooltipTrigger
135 render={
136 <Toggle
137 className="shrink-0"
138 pressed={terminalOpen}
139 onPressedChange={onToggleTerminal}
140 aria-label="Toggle terminal drawer"
141 variant="ghost"
142 size="xs"
143 disabled={!terminalAvailable}
144 >
145 <PanelBottomIcon className="size-3.5" />
146 </Toggle>
147 }
148 />
149 <TooltipPopup side="bottom">
150 {terminalAvailable
151 ? `Toggle terminal drawer${terminalShortcutLabel ? ` (${terminalShortcutLabel})` : ""}`
152 : "Terminal drawer is unavailable"}
153 </TooltipPopup>
154 </Tooltip>
126155 <Tooltip>
127156 <TooltipTrigger
128157 render={