mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
feat(terminal): add a Scroll to End toggle to the terminal pane (#395)
Adds a Scroll to End button to the terminal pane (shown on hover/focus). It reflects whether the viewport follows new output, turns off when you scroll up and back on at the bottom, and clicking it jumps to the end. Co-authored-by: a-rookie-of-C-language <186315048+a-rookie-of-C-language@users.noreply.github.com>
This commit is contained in:
co-authored by
a-rookie-of-C-language
parent
a1294598a5
commit
86cf348310
@@ -57,6 +57,26 @@
|
||||
padding: 6px 0 0 8px;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.paneActions {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
bottom: 8px;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
background: var(--bg-elev, var(--bg-app));
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pane:hover .paneActions,
|
||||
.pane:focus-within .paneActions,
|
||||
.paneActions:focus-within {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.exited :global(.xterm-cursor-layer),
|
||||
|
||||
@@ -47,6 +47,7 @@ function tabTooltip(tab: TerminalTab, sessions: Parameters<typeof tabTitle>[1]):
|
||||
function TerminalPane({ sessionKey, autofocus }: { sessionKey: string; autofocus: boolean }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const status = useTerminals((s) => s.sessions[sessionKey]?.status);
|
||||
const follow = useTerminals((s) => s.sessions[sessionKey]?.follow ?? true);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const el = ref.current;
|
||||
@@ -67,7 +68,17 @@ function TerminalPane({ sessionKey, autofocus }: { sessionKey: string; autofocus
|
||||
if (e.target === e.currentTarget) getSession(sessionKey)?.focus();
|
||||
}}
|
||||
onContextMenu={(e) => paneMenu(e, sessionKey)}
|
||||
/>
|
||||
>
|
||||
<div className={styles.paneActions} role="toolbar" aria-label="Terminal actions">
|
||||
<IconButton
|
||||
icon="arrow-down"
|
||||
iconSize={14}
|
||||
title="Scroll to End"
|
||||
pressed={follow}
|
||||
onClick={() => getSession(sessionKey)?.setFollow(!follow)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ export interface SessionSnapshot {
|
||||
commandRunning: boolean;
|
||||
/** Current directory (initial cwd, updated by OSC 7). */
|
||||
cwd: string;
|
||||
/** True while the viewport is pinned to the newest output. */
|
||||
follow: boolean;
|
||||
}
|
||||
|
||||
export interface SessionOptions {
|
||||
@@ -192,6 +194,7 @@ export class TerminalSession {
|
||||
appTitle: null,
|
||||
commandRunning: false,
|
||||
cwd: opts.cwd,
|
||||
follow: true,
|
||||
};
|
||||
this.host = document.createElement("div");
|
||||
this.host.className = "openidea-terminal-host";
|
||||
@@ -266,6 +269,11 @@ export class TerminalSession {
|
||||
const modes = params.map((p) => (Array.isArray(p) ? p[0] : p) ?? 0);
|
||||
return modes.length > 0 && modes.every((m) => MOUSE_MODES.has(m));
|
||||
}),
|
||||
// Track scroll position: follow when the user scrolls back to the bottom.
|
||||
this.term.onScroll(() => {
|
||||
const atBottom = this.isAtBottom();
|
||||
if (atBottom !== this.snapshot.follow) this.update({ follow: atBottom });
|
||||
}),
|
||||
);
|
||||
this.host.addEventListener("mousedown", this.onMiddleClick);
|
||||
}
|
||||
@@ -562,6 +570,16 @@ export class TerminalSession {
|
||||
this.term.clear();
|
||||
}
|
||||
|
||||
isAtBottom(): boolean {
|
||||
return this.term.buffer.active.viewportY >= this.term.buffer.active.baseY;
|
||||
}
|
||||
|
||||
/** Sets the follow flag; when turning it on also scrolls to the bottom. */
|
||||
setFollow(follow: boolean) {
|
||||
if (follow) this.term.scrollToBottom();
|
||||
if (follow !== this.snapshot.follow) this.update({ follow });
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.term.focus();
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ describe("terminalFont", () => {
|
||||
describe("terminal tab titles", () => {
|
||||
const tab: TerminalTab = { id: "t1", name: "Local", customName: null, panes: ["s1"], layout: "s1", activePane: "s1" };
|
||||
const snap = (appTitle: string | null, commandRunning = true): Record<string, SessionSnapshot> => ({
|
||||
s1: { status: "running", exitCode: null, title: "zsh", appTitle, commandRunning, cwd: "/p" },
|
||||
s1: { status: "running", exitCode: null, title: "zsh", appTitle, commandRunning, cwd: "/p", follow: true },
|
||||
});
|
||||
|
||||
it("shows the default name at the prompt, whatever title the shell sets", () => {
|
||||
|
||||
Reference in New Issue
Block a user