Skip to content

Commit 7913e93

Browse files
committed
feat(web): improve terminal workspace controls
1 parent 8c3da41 commit 7913e93

5 files changed

Lines changed: 594 additions & 334 deletions

File tree

apps/web/src/components/ChatView.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,11 @@ import {
221221
import { appendPreviewAnnotationPrompt } from "../lib/previewAnnotation";
222222
import { appendReviewCommentsToPrompt, type ReviewCommentContext } from "../reviewCommentContext";
223223
import { environmentCatalog } from "../connection/catalog";
224-
import { selectThreadTerminalUiState, useTerminalUiStateStore } from "../terminalUiStateStore";
224+
import {
225+
selectThreadTerminalCustomLabels,
226+
selectThreadTerminalUiState,
227+
useTerminalUiStateStore,
228+
} from "../terminalUiStateStore";
225229
import { useKnownTerminalSessions, useThreadRunningTerminalIds } from "../state/terminalSessions";
226230
import { projectEnvironment } from "../state/projects";
227231
import { useEnvironmentQuery } from "../state/query";
@@ -658,6 +662,7 @@ interface PersistentThreadTerminalDrawerProps {
658662
newShortcutLabel: string | undefined;
659663
closeShortcutLabel: string | undefined;
660664
keybindings: ResolvedKeybindingsConfig;
665+
onHide: () => void;
661666
onAddTerminalContext: (selection: TerminalContextSelection) => void;
662667
}
663668

@@ -672,6 +677,7 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra
672677
newShortcutLabel,
673678
closeShortcutLabel,
674679
keybindings,
680+
onHide,
675681
onAddTerminalContext,
676682
}: PersistentThreadTerminalDrawerProps) {
677683
const openTerminal = useAtomCommand(terminalEnvironment.open, "terminal open");
@@ -995,6 +1001,7 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra
9951001
onSplitTerminal={splitTerminal}
9961002
onSplitTerminalVertical={splitTerminalVertical}
9971003
onNewTerminal={createNewTerminal}
1004+
onHide={onHide}
9981005
splitShortcutLabel={visible ? splitShortcutLabel : undefined}
9991006
splitVerticalShortcutLabel={visible ? splitVerticalShortcutLabel : undefined}
10001007
newShortcutLabel={visible ? newShortcutLabel : undefined}
@@ -1557,6 +1564,16 @@ function ChatViewContent(props: ChatViewProps) {
15571564
const canCheckoutPullRequestIntoThread = isLocalDraftThread;
15581565
const activeThreadId = activeThread?.id ?? null;
15591566
const activeThreadEnvironmentId = activeThread?.environmentId ?? null;
1567+
const activeThreadRef = useMemo(
1568+
() =>
1569+
activeThreadEnvironmentId && activeThreadId
1570+
? scopeThreadRef(activeThreadEnvironmentId, activeThreadId)
1571+
: null,
1572+
[activeThreadEnvironmentId, activeThreadId],
1573+
);
1574+
const activeTerminalCustomLabels = useTerminalUiStateStore((state) =>
1575+
selectThreadTerminalCustomLabels(state.terminalCustomLabelsByThreadKey, activeThreadRef),
1576+
);
15601577
const runningTerminalIds = useThreadRunningTerminalIds({
15611578
environmentId: activeThread?.environmentId ?? null,
15621579
threadId: activeThreadId,
@@ -1586,18 +1603,15 @@ function ChatViewContent(props: ChatViewProps) {
15861603
for (const session of activeThreadKnownSessions) {
15871604
labels.set(
15881605
session.target.terminalId,
1589-
resolveTerminalSessionLabel(session.target.terminalId, session.state.summary),
1606+
activeTerminalCustomLabels[session.target.terminalId] ??
1607+
resolveTerminalSessionLabel(session.target.terminalId, session.state.summary),
15901608
);
15911609
}
1610+
for (const [terminalId, label] of Object.entries(activeTerminalCustomLabels)) {
1611+
if (!labels.has(terminalId)) labels.set(terminalId, label);
1612+
}
15921613
return labels;
1593-
}, [activeThreadKnownSessions]);
1594-
const activeThreadRef = useMemo(
1595-
() =>
1596-
activeThreadEnvironmentId && activeThreadId
1597-
? scopeThreadRef(activeThreadEnvironmentId, activeThreadId)
1598-
: null,
1599-
[activeThreadEnvironmentId, activeThreadId],
1600-
);
1614+
}, [activeTerminalCustomLabels, activeThreadKnownSessions]);
16011615
const activeThreadKey = activeThreadRef ? scopedThreadKey(activeThreadRef) : null;
16021616
const changeRequestSnapshotByKey = useAtomValue(threadChangeRequestSnapshotsAtom);
16031617
const [timelineAnchor, setTimelineAnchor] = useState<{
@@ -2826,6 +2840,7 @@ function ChatViewContent(props: ChatViewProps) {
28262840
},
28272841
[activeThreadRef, storeSetTerminalOpen],
28282842
);
2843+
const hideTerminal = useCallback(() => setTerminalOpen(false), [setTerminalOpen]);
28292844
const toggleTerminalVisibility = useCallback(() => {
28302845
if (!activeThreadRef) return;
28312846
const nextOpen = !terminalUiState.terminalOpen;
@@ -6604,6 +6619,7 @@ function ChatViewContent(props: ChatViewProps) {
66046619
newShortcutLabel={newTerminalShortcutLabel ?? undefined}
66056620
closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}
66066621
keybindings={keybindings}
6622+
onHide={hideTerminal}
66076623
onAddTerminalContext={addTerminalContextToDraft}
66086624
/>
66096625
))}

apps/web/src/components/ThreadTerminalDrawer.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@ import {
44
resolveTerminalSelectionActionPosition,
55
shouldHandleTerminalExit,
66
shouldHandleTerminalSelectionMouseUp,
7+
shouldShowTerminalSidebar,
78
terminalSelectionActionDelayForClickCount,
89
terminalSelectionLineRange,
910
} from "./ThreadTerminalDrawer";
1011

12+
describe("shouldShowTerminalSidebar", () => {
13+
it("shows the sidebar only when there is more than one terminal", () => {
14+
expect(shouldShowTerminalSidebar(0)).toBe(false);
15+
expect(shouldShowTerminalSidebar(1)).toBe(false);
16+
expect(shouldShowTerminalSidebar(2)).toBe(true);
17+
});
18+
});
19+
1120
describe("resolveTerminalSelectionActionPosition", () => {
1221
it("prefers the selection rect over the last pointer position", () => {
1322
expect(

0 commit comments

Comments
 (0)