mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-14 09:45:55 +01:00
add basics
This commit is contained in:
@@ -121,6 +121,10 @@ export const enum TerminalSettingId {
|
||||
StickyScrollEnabled = 'terminal.integrated.stickyScroll.enabled',
|
||||
StickyScrollMaxLineCount = 'terminal.integrated.stickyScroll.maxLineCount',
|
||||
MouseWheelZoom = 'terminal.integrated.mouseWheelZoom',
|
||||
FocusChat = 'workbench.action.terminal.focusChat',
|
||||
HideChat = 'workbench.action.terminal.hideChat',
|
||||
SubmitChat = 'workbench.action.terminal.submitChat',
|
||||
CancelChat = 'workbench.action.terminal.cancelChat',
|
||||
|
||||
// Debug settings that are hidden from user
|
||||
|
||||
|
||||
@@ -565,3 +565,14 @@
|
||||
.monaco-workbench .xterm.terminal.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .terminal-chat-widget {
|
||||
z-index: 33 !important;
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench .terminal-chat-widget.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -500,6 +500,10 @@ export const enum TerminalCommandId {
|
||||
FontZoomIn = 'workbench.action.terminal.fontZoomIn',
|
||||
FontZoomOut = 'workbench.action.terminal.fontZoomOut',
|
||||
FontZoomReset = 'workbench.action.terminal.fontZoomReset',
|
||||
FocusChat = 'workbench.action.terminal.focusChat',
|
||||
HideChat = 'workbench.action.terminal.hideChat',
|
||||
SubmitChat = 'workbench.action.terminal.submitChat',
|
||||
CancelChat = 'workbench.action.terminal.cancelChat',
|
||||
|
||||
// Developer commands
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ export const enum TerminalContextKeyStrings {
|
||||
ShellType = 'terminalShellType',
|
||||
InTerminalRunCommandPicker = 'inTerminalRunCommandPicker',
|
||||
TerminalShellIntegrationEnabled = 'terminalShellIntegrationEnabled',
|
||||
ChatFocus = 'terminalChatFocus',
|
||||
ChatVisible = 'terminalChatVisible',
|
||||
ChatSessionInProgress = 'terminalChatSessionInProgress',
|
||||
ChatInputHasText = 'terminalChatInputHasText',
|
||||
}
|
||||
|
||||
export namespace TerminalContextKeys {
|
||||
@@ -158,4 +162,17 @@ export namespace TerminalContextKeys {
|
||||
ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActions}`, 'always')
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
/** Whether the chat widget is focused */
|
||||
export const chatFocused = new RawContextKey<boolean>(TerminalContextKeyStrings.ChatFocus, false, localize('chatFocusedContextKey', "Whether the chat view is focused."));
|
||||
|
||||
/** Whether the chat widget is visible */
|
||||
export const chatVisible = new RawContextKey<boolean>(TerminalContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible."));
|
||||
|
||||
/** Whether a chat session is in progress */
|
||||
export const chatSessionInProgress = new RawContextKey<boolean>(TerminalContextKeyStrings.ChatSessionInProgress, false, localize('chatSessionInProgressContextKey', "Whether a chat session is in progress."));
|
||||
|
||||
/** Whether the chat input has text */
|
||||
export const chatInputHasText = new RawContextKey<boolean>(TerminalContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text."));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.acce
|
||||
import 'vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution';
|
||||
import 'vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution';
|
||||
import 'vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution';
|
||||
import 'vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution';
|
||||
import 'vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution';
|
||||
import 'vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution';
|
||||
import 'vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution';
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDimension } from 'vs/base/browser/dom';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { localize2 } from 'vs/nls';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
|
||||
import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions';
|
||||
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
|
||||
import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
import type { Terminal as RawXtermTerminal } from '@xterm/xterm';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget';
|
||||
|
||||
export class TerminalChatContribution extends Disposable implements ITerminalContribution {
|
||||
static readonly ID = 'terminal.Chat';
|
||||
|
||||
/**
|
||||
* Currently focused Chat widget. This is used to track action context since
|
||||
* 'active terminals' are only tracked for non-detached terminal instanecs.
|
||||
*/
|
||||
static activeChatWidget?: TerminalChatContribution;
|
||||
|
||||
static get(instance: ITerminalInstance | IDetachedTerminalInstance): TerminalChatContribution | null {
|
||||
return instance.getContribution<TerminalChatContribution>(TerminalChatContribution.ID);
|
||||
}
|
||||
|
||||
private _chatWidget: Lazy<TerminalChatWidget> | undefined;
|
||||
private _lastLayoutDimensions: IDimension | undefined;
|
||||
|
||||
get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; }
|
||||
|
||||
constructor(
|
||||
private readonly _instance: ITerminalInstance | IDetachedTerminalInstance,
|
||||
processManager: ITerminalProcessManager | ITerminalProcessInfo,
|
||||
widgetManager: TerminalWidgetManager,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void {
|
||||
this._lastLayoutDimensions = dimension;
|
||||
this._chatWidget?.rawValue?.layout(dimension.width);
|
||||
}
|
||||
|
||||
xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void {
|
||||
this._chatWidget = new Lazy(() => {
|
||||
const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance);
|
||||
|
||||
// Track focus and set state so we can force the scroll bar to be visible
|
||||
chatWidget.onDidFocus(() => {
|
||||
TerminalChatContribution.activeChatWidget = this;
|
||||
this._instance.forceScrollbarVisibility();
|
||||
if (!isDetachedTerminalInstance(this._instance)) {
|
||||
this._terminalService.setActiveInstance(this._instance);
|
||||
}
|
||||
});
|
||||
// chatWidget.onDidBlur(() => {
|
||||
// TerminalChatContribution.activeChatWidget = undefined;
|
||||
// this._instance.resetScrollbarVisibility();
|
||||
// });
|
||||
|
||||
if (!this._instance.domElement) {
|
||||
throw new Error('FindWidget expected terminal DOM to be initialized');
|
||||
}
|
||||
|
||||
// this._instance.domElement?.appendChild(chatWidget.getDomNode());
|
||||
if (this._lastLayoutDimensions) {
|
||||
chatWidget.layout(this._lastLayoutDimensions.width);
|
||||
}
|
||||
|
||||
return chatWidget;
|
||||
});
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
if (TerminalChatContribution.activeChatWidget === this) {
|
||||
TerminalChatContribution.activeChatWidget = undefined;
|
||||
}
|
||||
super.dispose();
|
||||
this._chatWidget?.rawValue?.dispose();
|
||||
}
|
||||
}
|
||||
registerTerminalContribution(TerminalChatContribution.ID, TerminalChatContribution, true);
|
||||
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.FocusChat,
|
||||
title: localize2('workbench.action.terminal.focusChat', 'Terminal: Focus Chat'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KeyI,
|
||||
when: ContextKeyExpr.or(TerminalContextKeys.chatFocused, TerminalContextKeys.focusInAny),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (_xterm, _accessor, activeInstance) => {
|
||||
const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance);
|
||||
contr?.chatWidget?.reveal();
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.HideChat,
|
||||
title: localize2('workbench.action.terminal.hideChat', 'Terminal: Hide Chat'),
|
||||
keybinding: {
|
||||
primary: KeyCode.Escape,
|
||||
secondary: [KeyMod.Shift | KeyCode.Escape],
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (_xterm, _accessor, activeInstance) => {
|
||||
const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance);
|
||||
contr?.chatWidget?.hide();
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.SubmitChat,
|
||||
title: localize2('workbench.action.terminal.submitChat', 'Terminal: Submit Chat'),
|
||||
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatInputHasText),
|
||||
icon: Codicon.send,
|
||||
menu: {
|
||||
id: MenuId.ChatExecute,
|
||||
when: TerminalContextKeys.chatSessionInProgress.negate(),
|
||||
group: 'navigation',
|
||||
},
|
||||
run: (_xterm, _accessor, activeInstance) => {
|
||||
const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance);
|
||||
contr?.chatWidget?.acceptInput();
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.CancelChat,
|
||||
title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'),
|
||||
precondition: TerminalContextKeys.chatSessionInProgress,
|
||||
icon: Codicon.debugStop,
|
||||
menu: {
|
||||
id: MenuId.ChatExecute,
|
||||
group: 'navigation',
|
||||
},
|
||||
run: (_xterm, _accessor, activeInstance) => {
|
||||
const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance);
|
||||
contr?.chatWidget?.cancel();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
|
||||
export class TerminalChatWidget extends Disposable {
|
||||
private _widget: ChatWidget | undefined;
|
||||
private _scopedInstantiationService: IInstantiationService;
|
||||
private readonly _onDidFocus = this._register(new Emitter<void>());
|
||||
readonly onDidFocus = this._onDidFocus.event;
|
||||
private _widgetContainer: HTMLElement | undefined;
|
||||
private _chatWidgetFocused: IContextKey<boolean>;
|
||||
private _chatWidgetVisible: IContextKey<boolean>;
|
||||
constructor(
|
||||
private readonly _container: HTMLElement,
|
||||
private readonly _instance: ITerminalInstance | IDetachedTerminalInstance,
|
||||
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService) {
|
||||
super();
|
||||
const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container));
|
||||
this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService]));
|
||||
this._chatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService);
|
||||
this._chatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService);
|
||||
}
|
||||
reveal(): void {
|
||||
this._widgetContainer = document.createElement('div');
|
||||
this._widgetContainer.classList.add('terminal-chat-widget');
|
||||
this._widget = this._register(this._scopedInstantiationService.createInstance(
|
||||
ChatWidget,
|
||||
{ viewId: 'terminal' },
|
||||
{ supportsFileReferences: false, renderStyle: 'compact' },
|
||||
{
|
||||
listForeground: editorForeground,
|
||||
listBackground: editorBackground,
|
||||
inputEditorBackground: inputBackground,
|
||||
resultEditorBackground: editorBackground
|
||||
}));
|
||||
this._widget.render(this._widgetContainer);
|
||||
this._container.appendChild(this._widgetContainer);
|
||||
this._register(this._widget.onDidFocus(() => {
|
||||
this._onDidFocus.fire();
|
||||
this._chatWidgetFocused.set(true);
|
||||
}));
|
||||
this._widget.setVisible(true);
|
||||
this._chatWidgetVisible.set(true);
|
||||
this._widget.setInput('@terminal');
|
||||
this._widget.setInputPlaceholder('Request a terminal command');
|
||||
this._widget.focusInput();
|
||||
}
|
||||
hide(): void {
|
||||
if (this._widgetContainer) {
|
||||
this._container.removeChild(this._widgetContainer);
|
||||
}
|
||||
this._chatWidgetVisible.set(false);
|
||||
}
|
||||
cancel(): void {
|
||||
this._widget?.clear();
|
||||
}
|
||||
acceptInput(): void {
|
||||
this._widget?.acceptInput();
|
||||
}
|
||||
layout(width: number): void {
|
||||
this._widget?.layout(40, width);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user