Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ import {
import { CHAT_LIST_ANCHOR_OFFSET } from "@t3tools/shared/chatList";
import { projectScriptCwd, projectScriptRuntimeEnv } from "@t3tools/shared/projectScripts";
import { truncate } from "@t3tools/shared/String";
import { nextTerminalId, resolveTerminalSessionLabel } from "@t3tools/shared/terminalLabels";
import {
getTerminalLabel,
nextTerminalId,
resolveTerminalSessionLabel,
} from "@t3tools/shared/terminalLabels";
import { Debouncer } from "@tanstack/react-pacer";
import { useAtomValue } from "@effect/atom-react";
import {
Expand Down Expand Up @@ -192,6 +196,7 @@ import {
import { useNowMinute } from "../hooks/useNowMinute";
import { useNewThreadHandler } from "../hooks/useHandleNewThread";
import { resolveAppModelSelectionForInstance } from "../modelSelection";
import { confirmTerminalClose } from "../lib/terminalCloseConfirm";
import { getTerminalFocusOwner } from "../lib/terminalFocus";
import { preventRepeatedTerminalCloseShortcut } from "../lib/terminalCloseShortcut";
import { resolveNewDraftStartFromOrigin } from "../lib/chatThreadActions";
Expand Down Expand Up @@ -3453,6 +3458,24 @@ function ChatViewContent(props: ChatViewProps) {
},
[activeRightPanelSurface, activeThreadRef, closeTerminalMutation, storeCloseTerminal],
);
const requestCloseTerminal = useCallback(
(terminalId: string) => {
const label = activeTerminalLabelsById.get(terminalId) ?? getTerminalLabel(terminalId);
void confirmTerminalClose(label).then((confirmed) => {
if (confirmed) closeTerminal(terminalId);
});
},
[activeTerminalLabelsById, closeTerminal],
);
const requestClosePanelTerminal = useCallback(
(terminalId: string) => {
const label = activeTerminalLabelsById.get(terminalId) ?? getTerminalLabel(terminalId);
void confirmTerminalClose(label).then((confirmed) => {
if (confirmed) closePanelTerminal(terminalId);
});
},
[activeTerminalLabelsById, closePanelTerminal],
);
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
const activateRightPanelSurface = useCallback(
(surface: RightPanelSurface) => {
if (!activeThreadRef) return;
Expand Down Expand Up @@ -4807,11 +4830,11 @@ function ChatViewContent(props: ChatViewProps) {
event.preventDefault();
event.stopPropagation();
if (terminalFocusOwner === "right-panel" && activeRightPanelSurface?.kind === "terminal") {
closePanelTerminal(activeRightPanelSurface.activeTerminalId);
requestClosePanelTerminal(activeRightPanelSurface.activeTerminalId);
return;
}
if (!terminalUiState.terminalOpen) return;
closeTerminal(terminalUiState.activeTerminalId);
requestCloseTerminal(terminalUiState.activeTerminalId);
Comment thread
cursor[bot] marked this conversation as resolved.
return;
}

Expand Down Expand Up @@ -4860,8 +4883,8 @@ function ChatViewContent(props: ChatViewProps) {
terminalUiState.terminalOpen,
terminalUiState.activeTerminalId,
activeThreadId,
closeTerminal,
closePanelTerminal,
requestCloseTerminal,
requestClosePanelTerminal,
createNewTerminal,
setTerminalOpen,
runProjectScript,
Expand Down
16 changes: 13 additions & 3 deletions apps/web/src/components/ThreadTerminalDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
type ThreadTerminalGroup,
} from "../types";
import { readLocalApi } from "~/localApi";
import { confirmTerminalClose } from "~/lib/terminalCloseConfirm";
import { useClientSettings } from "../hooks/useSettings";
import { useLocalStorage } from "../hooks/useLocalStorage";
import { useAttachedTerminalSession } from "../state/terminalSessions";
Expand Down Expand Up @@ -1273,6 +1274,15 @@ export default function ThreadTerminalDrawer({
const onNewTerminalAction = useCallback(() => {
onNewTerminal();
}, [onNewTerminal]);
const confirmCloseTerminal = useCallback(
(terminalId: string) => {
const label = terminalLabelById.get(terminalId) ?? getTerminalLabel(terminalId);
void confirmTerminalClose(label).then((confirmed) => {
if (confirmed) onCloseTerminal(terminalId);
});
},
[onCloseTerminal, terminalLabelById],
);
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.

useEffect(() => {
onHeightChangeRef.current = onHeightChange;
Expand Down Expand Up @@ -1463,7 +1473,7 @@ export default function ThreadTerminalDrawer({
<div className="h-4 w-px bg-border/80" />
<TerminalActionButton
className="p-1 text-foreground/90 transition-colors hover:bg-accent"
onClick={() => onCloseTerminal(resolvedActiveTerminalId)}
onClick={() => confirmCloseTerminal(resolvedActiveTerminalId)}
label={closeTerminalActionLabel}
>
<Trash2 className="size-3.25" />
Expand Down Expand Up @@ -1598,7 +1608,7 @@ export default function ThreadTerminalDrawer({
</TerminalActionButton>
<TerminalActionButton
className="inline-flex h-full items-center border-l border-border/70 px-1 text-foreground/90 transition-colors hover:bg-accent/70"
onClick={() => onCloseTerminal(resolvedActiveTerminalId)}
onClick={() => confirmCloseTerminal(resolvedActiveTerminalId)}
label={closeTerminalActionLabel}
>
<Trash2 className="size-3.25" />
Expand Down Expand Up @@ -1668,7 +1678,7 @@ export default function ThreadTerminalDrawer({
<button
type="button"
className="inline-flex size-3.5 items-center justify-center rounded text-xs font-medium leading-none text-muted-foreground opacity-0 transition hover:bg-accent hover:text-foreground group-hover:opacity-100"
onClick={() => onCloseTerminal(terminalId)}
onClick={() => confirmCloseTerminal(terminalId)}
aria-label={closeTerminalLabel}
/>
}
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/lib/terminalCloseConfirm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { readLocalApi } from "~/localApi";

/**
* Shared confirmation for every user-initiated terminal close (drawer buttons,
* panel buttons, and the `terminal.close` keybinding). Auto-exit cleanup skips
* this path and closes directly.
*/
export async function confirmTerminalClose(label: string): Promise<boolean> {
const localApi = readLocalApi();
if (!localApi) return true;
return localApi.dialogs.confirm(
[`Close terminal "${label}"?`, "This stops the running process and clears its history."].join(
"\n",
),
{ variant: "destructive" },
);
}
Loading