| 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,7 +23,6 @@ import { |
| 23 | ServerConfig as ServerConfigSchema, | 23 | ServerConfig as ServerConfigSchema, |
| 24 | } from "@t3tools/contracts"; | 24 | } from "@t3tools/contracts"; |
| 25 | import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime"; | 25 | import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime"; |
| 26 | import { DEFAULT_RESOLVED_KEYBINDINGS } from "@t3tools/shared/keybindings"; | ||
| 27 | import { createModelCapabilities, createModelSelection } from "@t3tools/shared/model"; | 26 | import { createModelCapabilities, createModelSelection } from "@t3tools/shared/model"; |
| 28 | import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; | 27 | import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; |
| 29 | import * as Option from "effect/Option"; | 28 | import * as Option from "effect/Option"; |
| ... | @@ -2065,26 +2064,15 @@ describe("ChatView timeline estimator parity (full app)", () => { | ... | @@ -2065,26 +2064,15 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2065 | targetMessageId: "msg-user-open-empty-terminal-drawer" as MessageId, | 2064 | targetMessageId: "msg-user-open-empty-terminal-drawer" as MessageId, |
| 2066 | targetText: "open empty terminal drawer", | 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 | try { | 2069 | try { |
| 2079 | window.dispatchEvent( | 2070 | const terminalToggle = await waitForElement( |
| 2080 | new KeyboardEvent("keydown", { | 2071 | () => |
| 2081 | key: "j", | 2072 | document.querySelector<HTMLButtonElement>('button[aria-label="Toggle terminal drawer"]'), |
| 2082 | metaKey: isMacPlatform(navigator.platform), | 2073 | "Unable to find terminal drawer toggle.", |
| 2083 | ctrlKey: !isMacPlatform(navigator.platform), | ||
| 2084 | bubbles: true, | ||
| 2085 | cancelable: true, | ||
| 2086 | }), | ||
| 2087 | ); | 2074 | ); |
| 2075 | terminalToggle.click(); | ||
| 2088 | 2076 | ||
| 2089 | await vi.waitFor( | 2077 | await vi.waitFor( |
| 2090 | () => { | 2078 | () => { |
| ... | @@ -2116,28 +2104,14 @@ describe("ChatView timeline estimator parity (full app)", () => { | ... | @@ -2116,28 +2104,14 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2116 | targetMessageId: "msg-user-open-inline-terminal-panel" as MessageId, | 2104 | targetMessageId: "msg-user-open-inline-terminal-panel" as MessageId, |
| 2117 | targetText: "open inline terminal panel", | 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 | try { | 2109 | try { |
| 2131 | window.dispatchEvent( | 2110 | const rightPanelToggle = await waitForElement( |
| 2132 | new KeyboardEvent("keydown", { | 2111 | () => document.querySelector<HTMLButtonElement>('button[aria-label="Toggle right panel"]'), |
| 2133 | key: "b", | 2112 | "Unable to find right panel toggle.", |
| 2134 | altKey: true, | ||
| 2135 | metaKey: isMacPlatform(navigator.platform), | ||
| 2136 | ctrlKey: !isMacPlatform(navigator.platform), | ||
| 2137 | bubbles: true, | ||
| 2138 | cancelable: true, | ||
| 2139 | }), | ||
| 2140 | ); | 2113 | ); |
| 2114 | rightPanelToggle.click(); | ||
| 2141 | 2115 | ||
| 2142 | const addSurface = await waitForElement( | 2116 | const addSurface = await waitForElement( |
| 2143 | () => document.querySelector<HTMLButtonElement>('button[aria-label="Add panel surface"]'), | 2117 | () => document.querySelector<HTMLButtonElement>('button[aria-label="Add panel surface"]'), |
| ... | @@ -2213,15 +2187,12 @@ describe("ChatView timeline estimator parity (full app)", () => { | ... | @@ -2213,15 +2187,12 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 2213 | { timeout: 8_000, interval: 16 }, | 2187 | { timeout: 8_000, interval: 16 }, |
| 2214 | ); | 2188 | ); |
| 2215 | 2189 | ||
| 2216 | window.dispatchEvent( | 2190 | const drawerToggle = await waitForElement( |
| 2217 | new KeyboardEvent("keydown", { | 2191 | () => |
| 2218 | key: "j", | 2192 | document.querySelector<HTMLButtonElement>('button[aria-label="Toggle terminal drawer"]'), |
| 2219 | metaKey: isMacPlatform(navigator.platform), | 2193 | "Unable to find terminal drawer toggle.", |
| 2220 | ctrlKey: !isMacPlatform(navigator.platform), | ||
| 2221 | bubbles: true, | ||
| 2222 | cancelable: true, | ||
| 2223 | }), | ||
| 2224 | ); | 2194 | ); |
| 2195 | drawerToggle.click(); | ||
| 2225 | 2196 | ||
| 2226 | await vi.waitFor(() => { | 2197 | await vi.waitFor(() => { |
| 2227 | expect( | 2198 | expect( |
apps/web/src/components/ChatView.tsx+3| ... | @@ -4501,6 +4501,8 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -4501,6 +4501,8 @@ export default function ChatView(props: ChatViewProps) { |
| 4501 | } | 4501 | } |
| 4502 | keybindings={keybindings} | 4502 | keybindings={keybindings} |
| 4503 | availableEditors={availableEditors} | 4503 | availableEditors={availableEditors} |
| 4504 | terminalAvailable={Boolean(activeProject)} | ||
| 4505 | terminalOpen={Boolean(terminalUiState.terminalOpen)} | ||
| 4504 | rightPanelAvailable={Boolean(activeProject)} | 4506 | rightPanelAvailable={Boolean(activeProject)} |
| 4505 | rightPanelOpen={rightPanelOpen} | 4507 | rightPanelOpen={rightPanelOpen} |
| 4506 | gitCwd={gitCwd} | 4508 | gitCwd={gitCwd} |
| ... | @@ -4508,6 +4510,7 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -4508,6 +4510,7 @@ export default function ChatView(props: ChatViewProps) { |
| 4508 | onAddProjectScript={saveProjectScript} | 4510 | onAddProjectScript={saveProjectScript} |
| 4509 | onUpdateProjectScript={updateProjectScript} | 4511 | onUpdateProjectScript={updateProjectScript} |
| 4510 | onDeleteProjectScript={deleteProjectScript} | 4512 | onDeleteProjectScript={deleteProjectScript} |
| 4513 | onToggleTerminal={toggleTerminalVisibility} | ||
| 4511 | onToggleRightPanel={toggleRightPanel} | 4514 | onToggleRightPanel={toggleRightPanel} |
| 4512 | /> | 4515 | /> |
| 4513 | </header> | 4516 | </header> |
apps/web/src/components/chat/ChatHeader.tsx+30-1| ... | @@ -9,7 +9,7 @@ import { scopeThreadRef } from "@t3tools/client-runtime"; | ... | @@ -9,7 +9,7 @@ import { scopeThreadRef } from "@t3tools/client-runtime"; |
| 9 | import { memo } from "react"; | 9 | import { memo } from "react"; |
| 10 | import GitActionsControl from "../GitActionsControl"; | 10 | import GitActionsControl from "../GitActionsControl"; |
| 11 | import { type DraftId } from "~/composerDraftStore"; | 11 | import { type DraftId } from "~/composerDraftStore"; |
| 12 | import { PanelRightIcon } from "lucide-react"; | 12 | import { PanelBottomIcon, PanelRightIcon } from "lucide-react"; |
| 13 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; | 13 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| 14 | import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl"; | 14 | import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl"; |
| 15 | import { Toggle } from "../ui/toggle"; | 15 | import { Toggle } from "../ui/toggle"; |
| ... | @@ -29,6 +29,8 @@ interface ChatHeaderProps { | ... | @@ -29,6 +29,8 @@ interface ChatHeaderProps { |
| 29 | preferredScriptId: string | null; | 29 | preferredScriptId: string | null; |
| 30 | keybindings: ResolvedKeybindingsConfig; | 30 | keybindings: ResolvedKeybindingsConfig; |
| 31 | availableEditors: ReadonlyArray<EditorId>; | 31 | availableEditors: ReadonlyArray<EditorId>; |
| 32 | terminalAvailable: boolean; | ||
| 33 | terminalOpen: boolean; | ||
| 32 | rightPanelAvailable: boolean; | 34 | rightPanelAvailable: boolean; |
| 33 | rightPanelOpen: boolean; | 35 | rightPanelOpen: boolean; |
| 34 | gitCwd: string | null; | 36 | gitCwd: string | null; |
| ... | @@ -36,6 +38,7 @@ interface ChatHeaderProps { | ... | @@ -36,6 +38,7 @@ interface ChatHeaderProps { |
| 36 | onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>; | 38 | onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>; |
| 37 | onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>; | 39 | onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>; |
| 38 | onDeleteProjectScript: (scriptId: string) => Promise<void>; | 40 | onDeleteProjectScript: (scriptId: string) => Promise<void>; |
| 41 | onToggleTerminal: () => void; | ||
| 39 | onToggleRightPanel: () => void; | 42 | onToggleRightPanel: () => void; |
| 40 | } | 43 | } |
| 41 | 44 | ||
| ... | @@ -62,6 +65,8 @@ export const ChatHeader = memo(function ChatHeader({ | ... | @@ -62,6 +65,8 @@ export const ChatHeader = memo(function ChatHeader({ |
| 62 | preferredScriptId, | 65 | preferredScriptId, |
| 63 | keybindings, | 66 | keybindings, |
| 64 | availableEditors, | 67 | availableEditors, |
| 68 | terminalAvailable, | ||
| 69 | terminalOpen, | ||
| 65 | rightPanelAvailable, | 70 | rightPanelAvailable, |
| 66 | rightPanelOpen, | 71 | rightPanelOpen, |
| 67 | gitCwd, | 72 | gitCwd, |
| ... | @@ -69,6 +74,7 @@ export const ChatHeader = memo(function ChatHeader({ | ... | @@ -69,6 +74,7 @@ export const ChatHeader = memo(function ChatHeader({ |
| 69 | onAddProjectScript, | 74 | onAddProjectScript, |
| 70 | onUpdateProjectScript, | 75 | onUpdateProjectScript, |
| 71 | onDeleteProjectScript, | 76 | onDeleteProjectScript, |
| 77 | onToggleTerminal, | ||
| 72 | onToggleRightPanel, | 78 | onToggleRightPanel, |
| 73 | }: ChatHeaderProps) { | 79 | }: ChatHeaderProps) { |
| 74 | const primaryEnvironmentId = usePrimaryEnvironmentId(); | 80 | const primaryEnvironmentId = usePrimaryEnvironmentId(); |
| ... | @@ -77,6 +83,7 @@ export const ChatHeader = memo(function ChatHeader({ | ... | @@ -77,6 +83,7 @@ export const ChatHeader = memo(function ChatHeader({ |
| 77 | activeThreadEnvironmentId, | 83 | activeThreadEnvironmentId, |
| 78 | primaryEnvironmentId, | 84 | primaryEnvironmentId, |
| 79 | }); | 85 | }); |
| 86 | const terminalShortcutLabel = shortcutLabelForCommand(keybindings, "terminal.toggle"); | ||
| 80 | const rightPanelShortcutLabel = shortcutLabelForCommand(keybindings, "rightPanel.toggle"); | 87 | const rightPanelShortcutLabel = shortcutLabelForCommand(keybindings, "rightPanel.toggle"); |
| 81 | 88 | ||
| 82 | return ( | 89 | return ( |
| ... | @@ -123,6 +130,28 @@ export const ChatHeader = memo(function ChatHeader({ | ... | @@ -123,6 +130,28 @@ export const ChatHeader = memo(function ChatHeader({ |
| 123 | {...(draftId ? { draftId } : {})} | 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 | <Tooltip> | 155 | <Tooltip> |
| 127 | <TooltipTrigger | 156 | <TooltipTrigger |
| 128 | render={ | 157 | render={ |