Skip to content

Commit 9675a34

Browse files
committed
Offset terminal when inline chat appears to ensure cursor is visible
This will overlay the terminal inline chat widget on top of the terminal the same as previously where the cursor would be visible. If it would not be visible, the terminal wrapper element is now shifted upwards to ensure it's visible. This comes with a couple of downsides: - The very top of the terminal cannot be accessed as overflow is clipped by the view. - The chat may overlap content if there is any below the cursor, which is an unlikely edge case. This isn't as good as a zone widget-like system in xterm.js could be, but that is quite the undertaking and complicates the renderer code quite a bit. So this is a good compromise. Fixes microsoft#4720
1 parent a43d090 commit 9675a34

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom';
77
import { Event } from 'vs/base/common/event';
88
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
9+
import { MicrotaskDelay } from 'vs/base/common/symbols';
910
import 'vs/css!./media/terminalChatWidget';
1011
import { localize } from 'vs/nls';
1112
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -15,6 +16,7 @@ import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService';
1516
import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget';
1617
import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
1718
import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId, TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat';
19+
import { TerminalStickyScrollContribution } from 'vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution';
1820

1921
const enum Constants {
2022
HorizontalMargin = 10
@@ -73,6 +75,7 @@ export class TerminalChatWidget extends Disposable {
7375
this._register(Event.any(
7476
this._inlineChatWidget.onDidChangeHeight,
7577
this._instance.onDimensionsChanged,
78+
Event.debounce(this._instance.xterm!.raw.onCursorMove, () => void 0, MicrotaskDelay),
7679
)(() => this._relayout()));
7780

7881
const observer = new ResizeObserver(() => this._relayout());
@@ -102,6 +105,7 @@ export class TerminalChatWidget extends Disposable {
102105
}
103106
this._dimension = new Dimension(width, height);
104107
this._inlineChatWidget.layout(this._dimension);
108+
105109
this._updateVerticalPosition();
106110
}
107111

@@ -134,7 +138,9 @@ export class TerminalChatWidget extends Disposable {
134138
return;
135139
}
136140
if (top > terminalWrapperHeight - widgetHeight) {
137-
this._container.style.top = '';
141+
this._setTerminalOffset(widgetHeight);
142+
} else {
143+
this._setTerminalOffset(undefined);
138144
}
139145
}
140146

@@ -154,6 +160,19 @@ export class TerminalChatWidget extends Disposable {
154160
this._visibleContextKey.set(false);
155161
this._inlineChatWidget.value = '';
156162
this._instance.focus();
163+
this._setTerminalOffset(undefined);
164+
}
165+
private _setTerminalOffset(offset: number | undefined) {
166+
if (offset === undefined || this._container.classList.contains('hide')) {
167+
this._terminalElement.style.position = '';
168+
this._terminalElement.style.bottom = '';
169+
TerminalStickyScrollContribution.get(this._instance)?.hideUnlock();
170+
} else {
171+
this._terminalElement.style.position = 'relative';
172+
this._terminalElement.style.bottom = `${offset}px`;
173+
TerminalStickyScrollContribution.get(this._instance)?.hideLock();
174+
175+
}
157176
}
158177
focus(): void {
159178
this._inlineChatWidget.focus();

src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ export class TerminalStickyScrollContribution extends Disposable implements ITer
6060
this._refreshState();
6161
}
6262

63+
hideLock() {
64+
this._overlay.value?.lockHide();
65+
}
66+
67+
hideUnlock() {
68+
this._overlay.value?.unlockHide();
69+
}
70+
6371
private _refreshState(): void {
6472
if (this._overlay.value) {
6573
this._tryDisable();

src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ export class TerminalStickyScrollOverlay extends Disposable {
129129
});
130130
}
131131

132+
lockHide() {
133+
this._element?.classList.add('lock-hide');
134+
}
135+
136+
unlockHide() {
137+
this._element?.classList.remove('lock-hide');
138+
}
139+
132140
private _setState(state: OverlayState) {
133141
if (this._state === state) {
134142
return;

0 commit comments

Comments
 (0)