| author | |
| committer | |
| log | 1b4317ec35da7cb4fb416d49f7e2a8673d536d05 |
| tree | 589400839324efdc8cc88d2f83db2eaba8332499 |
| parent | f452d63fefdf00958e86b8d1f920aa25eb03f484 |
3 files changed, 47 insertions(+), 44 deletions(-)
apps/web/src/components/ChatView.browser.tsx+14-43| ... | ... | @@ -23,7 +23,6 @@ import { |
| 23 | 23 | ServerConfig as ServerConfigSchema, |
| 24 | 24 | } from "@t3tools/contracts"; |
| 25 | 25 | import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime"; |
| 26 | import { DEFAULT_RESOLVED_KEYBINDINGS } from "@t3tools/shared/keybindings"; | |
| 27 | 26 | import { createModelCapabilities, createModelSelection } from "@t3tools/shared/model"; |
| 28 | 27 | import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; |
| 29 | 28 | import * as Option from "effect/Option"; |
| ... | ... | @@ -2065,26 +2064,15 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2065 | 2064 | targetMessageId: "msg-user-open-empty-terminal-drawer" as MessageId, |
| 2066 | 2065 | targetText: "open empty terminal drawer", |
| 2067 | 2066 | }), |
| 2068 | configureFixture: (nextFixture) => { | |
| 2069 | nextFixture.serverConfig = { | |
| 2070 | ...nextFixture.serverConfig, | |
| 2071 | keybindings: DEFAULT_RESOLVED_KEYBINDINGS.filter( | |
| 2072 | (binding) => binding.command === "terminal.toggle", | |
| 2073 | ), | |
| 2074 | }; | |
| 2075 | }, | |
| 2076 | 2067 | }); |
| 2077 | 2068 | |
| 2078 | 2069 | 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.", | |
| 2087 | 2074 | ); |
| 2075 | terminalToggle.click(); | |
| 2088 | 2076 | |
| 2089 | 2077 | await vi.waitFor( |
| 2090 | 2078 | () => { |
| ... | ... | @@ -2116,28 +2104,14 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2116 | 2104 | targetMessageId: "msg-user-open-inline-terminal-panel" as MessageId, |
| 2117 | 2105 | targetText: "open inline terminal panel", |
| 2118 | 2106 | }), |
| 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 | }, | |
| 2128 | 2107 | }); |
| 2129 | 2108 | |
| 2130 | 2109 | 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.", | |
| 2140 | 2113 | ); |
| 2114 | rightPanelToggle.click(); | |
| 2141 | 2115 | |
| 2142 | 2116 | const addSurface = await waitForElement( |
| 2143 | 2117 | () => document.querySelector<HTMLButtonElement>('button[aria-label="Add panel surface"]'), |
| ... | ... | @@ -2213,15 +2187,12 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2213 | 2187 | { timeout: 8_000, interval: 16 }, |
| 2214 | 2188 | ); |
| 2215 | 2189 | |
| 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.", | |
| 2224 | 2194 | ); |
| 2195 | drawerToggle.click(); | |
| 2225 | 2196 | |
| 2226 | 2197 | await vi.waitFor(() => { |
| 2227 | 2198 | expect( |
apps/web/src/components/ChatView.tsx+3| ... | ... | @@ -4501,6 +4501,8 @@ export default function ChatView(props: ChatViewProps) { |
| 4501 | 4501 | } |
| 4502 | 4502 | keybindings={keybindings} |
| 4503 | 4503 | availableEditors={availableEditors} |
| 4504 | terminalAvailable={Boolean(activeProject)} | |
| 4505 | terminalOpen={Boolean(terminalUiState.terminalOpen)} | |
| 4504 | 4506 | rightPanelAvailable={Boolean(activeProject)} |
| 4505 | 4507 | rightPanelOpen={rightPanelOpen} |
| 4506 | 4508 | gitCwd={gitCwd} |
| ... | ... | @@ -4508,6 +4510,7 @@ export default function ChatView(props: ChatViewProps) { |
| 4508 | 4510 | onAddProjectScript={saveProjectScript} |
| 4509 | 4511 | onUpdateProjectScript={updateProjectScript} |
| 4510 | 4512 | onDeleteProjectScript={deleteProjectScript} |
| 4513 | onToggleTerminal={toggleTerminalVisibility} | |
| 4511 | 4514 | onToggleRightPanel={toggleRightPanel} |
| 4512 | 4515 | /> |
| 4513 | 4516 | </header> |
apps/web/src/components/chat/ChatHeader.tsx+30-1| ... | ... | @@ -9,7 +9,7 @@ import { scopeThreadRef } from "@t3tools/client-runtime"; |
| 9 | 9 | import { memo } from "react"; |
| 10 | 10 | import GitActionsControl from "../GitActionsControl"; |
| 11 | 11 | import { type DraftId } from "~/composerDraftStore"; |
| 12 | import { PanelRightIcon } from "lucide-react"; | |
| 12 | import { PanelBottomIcon, PanelRightIcon } from "lucide-react"; | |
| 13 | 13 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| 14 | 14 | import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl"; |
| 15 | 15 | import { Toggle } from "../ui/toggle"; |
| ... | ... | @@ -29,6 +29,8 @@ interface ChatHeaderProps { |
| 29 | 29 | preferredScriptId: string | null; |
| 30 | 30 | keybindings: ResolvedKeybindingsConfig; |
| 31 | 31 | availableEditors: ReadonlyArray<EditorId>; |
| 32 | terminalAvailable: boolean; | |
| 33 | terminalOpen: boolean; | |
| 32 | 34 | rightPanelAvailable: boolean; |
| 33 | 35 | rightPanelOpen: boolean; |
| 34 | 36 | gitCwd: string | null; |
| ... | ... | @@ -36,6 +38,7 @@ interface ChatHeaderProps { |
| 36 | 38 | onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>; |
| 37 | 39 | onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>; |
| 38 | 40 | onDeleteProjectScript: (scriptId: string) => Promise<void>; |
| 41 | onToggleTerminal: () => void; | |
| 39 | 42 | onToggleRightPanel: () => void; |
| 40 | 43 | } |
| 41 | 44 | |
| ... | ... | @@ -62,6 +65,8 @@ export const ChatHeader = memo(function ChatHeader({ |
| 62 | 65 | preferredScriptId, |
| 63 | 66 | keybindings, |
| 64 | 67 | availableEditors, |
| 68 | terminalAvailable, | |
| 69 | terminalOpen, | |
| 65 | 70 | rightPanelAvailable, |
| 66 | 71 | rightPanelOpen, |
| 67 | 72 | gitCwd, |
| ... | ... | @@ -69,6 +74,7 @@ export const ChatHeader = memo(function ChatHeader({ |
| 69 | 74 | onAddProjectScript, |
| 70 | 75 | onUpdateProjectScript, |
| 71 | 76 | onDeleteProjectScript, |
| 77 | onToggleTerminal, | |
| 72 | 78 | onToggleRightPanel, |
| 73 | 79 | }: ChatHeaderProps) { |
| 74 | 80 | const primaryEnvironmentId = usePrimaryEnvironmentId(); |
| ... | ... | @@ -77,6 +83,7 @@ export const ChatHeader = memo(function ChatHeader({ |
| 77 | 83 | activeThreadEnvironmentId, |
| 78 | 84 | primaryEnvironmentId, |
| 79 | 85 | }); |
| 86 | const terminalShortcutLabel = shortcutLabelForCommand(keybindings, "terminal.toggle"); | |
| 80 | 87 | const rightPanelShortcutLabel = shortcutLabelForCommand(keybindings, "rightPanel.toggle"); |
| 81 | 88 | |
| 82 | 89 | return ( |
| ... | ... | @@ -123,6 +130,28 @@ export const ChatHeader = memo(function ChatHeader({ |
| 123 | 130 | {...(draftId ? { draftId } : {})} |
| 124 | 131 | /> |
| 125 | 132 | )} |
| 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> | |
| 126 | 155 | <Tooltip> |
| 127 | 156 | <TooltipTrigger |
| 128 | 157 | render={ |