mirror of
https://github.com/Microsoft/vscode
synced 2024-10-01 08:50:48 +00:00
Inline chat UI overhaul (#215927)
* don't dimiss inline chat content widget when already having typed something https://github.com/microsoft/vscode-copilot/issues/6067 * extract `TextOnlyMenuEntryActionViewItem` for reuse * remove unused variables * * show chat input below request/response pairs * setting for text-only buttons * more dynamic buttons * always show the first request, don't repopulate input with last message * keep progress bar hidden, rely on "Generating..." * no more special background color * add `minimal` renderer style for chat renderings * tweak font-size for details when render mode is minimal * stable scroll position for inline chat, don't push down the lines chat is editing but push the inline chat upwards * more buttons more compact, tweak labels * * add missing service dependency * repopulate input for some unit test * allow output from `InteractiveChatController` suite
This commit is contained in:
parent
0131b07802
commit
88d860624c
|
@ -818,8 +818,6 @@
|
|||
"--vscode-hover-maxWidth",
|
||||
"--vscode-hover-sourceWhiteSpace",
|
||||
"--vscode-hover-whiteSpace",
|
||||
"--vscode-inline-chat-quick-voice-height",
|
||||
"--vscode-inline-chat-quick-voice-width",
|
||||
"--vscode-editor-dictation-widget-height",
|
||||
"--vscode-editor-dictation-widget-width",
|
||||
"--vscode-interactive-session-foreground",
|
||||
|
@ -855,4 +853,4 @@
|
|||
"--zoom-factor",
|
||||
"--test-bar-width"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,16 @@ export interface IToolBarOptions {
|
|||
* If true, toggled primary items are highlighted with a background color.
|
||||
*/
|
||||
highlightToggledItems?: boolean;
|
||||
|
||||
/**
|
||||
* Render action with icons (default: `true`)
|
||||
*/
|
||||
icon?: boolean;
|
||||
|
||||
/**
|
||||
* Render action with label (default: `false`)
|
||||
*/
|
||||
label?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +60,6 @@ export class ToolBar extends Disposable {
|
|||
private toggleMenuActionViewItem: DropdownMenuActionViewItem | undefined;
|
||||
private submenuActionViewItems: DropdownMenuActionViewItem[] = [];
|
||||
private hasSecondaryActions: boolean = false;
|
||||
private readonly lookupKeybindings: boolean;
|
||||
private readonly element: HTMLElement;
|
||||
|
||||
private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer<boolean>());
|
||||
|
@ -62,7 +71,6 @@ export class ToolBar extends Disposable {
|
|||
|
||||
options.hoverDelegate = options.hoverDelegate ?? this._register(createInstantHoverDelegate());
|
||||
this.options = options;
|
||||
this.lookupKeybindings = typeof this.options.getKeyBinding === 'function';
|
||||
|
||||
this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem?.show(), options.toggleMenuTitle));
|
||||
|
||||
|
@ -198,7 +206,7 @@ export class ToolBar extends Disposable {
|
|||
}
|
||||
|
||||
primaryActionsToSet.forEach(action => {
|
||||
this.actionBar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) });
|
||||
this.actionBar.push(action, { icon: this.options.icon ?? true, label: this.options.label ?? false, keybinding: this.getKeybindingLabel(action) });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -207,7 +215,7 @@ export class ToolBar extends Disposable {
|
|||
}
|
||||
|
||||
private getKeybindingLabel(action: IAction): string | undefined {
|
||||
const key = this.lookupKeybindings ? this.options.getKeyBinding?.(action) : undefined;
|
||||
const key = this.options.getKeyBinding?.(action);
|
||||
|
||||
return key?.getLabel() ?? undefined;
|
||||
}
|
||||
|
|
|
@ -6,31 +6,12 @@
|
|||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { TextOnlyMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
class StatusBarViewItem extends MenuEntryActionViewItem {
|
||||
|
||||
protected override updateLabel() {
|
||||
const kb = this._keybindingService.lookupKeybinding(this._action.id, this._contextKeyService);
|
||||
if (!kb) {
|
||||
return super.updateLabel();
|
||||
}
|
||||
if (this.label) {
|
||||
this.label.textContent = localize({ key: 'content', comment: ['A label', 'A keybinding'] }, '{0} ({1})', this._action.label, StatusBarViewItem.symbolPrintEnter(kb));
|
||||
}
|
||||
}
|
||||
|
||||
static symbolPrintEnter(kb: ResolvedKeybinding) {
|
||||
return kb.getLabel()?.replace(/\benter\b/gi, '\u23CE');
|
||||
}
|
||||
}
|
||||
|
||||
export class SuggestWidgetStatus {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
|
@ -49,7 +30,7 @@ export class SuggestWidgetStatus {
|
|||
this.element = dom.append(container, dom.$('.suggest-status-bar'));
|
||||
|
||||
const actionViewItemProvider = <IActionViewItemProvider>(action => {
|
||||
return action instanceof MenuItemAction ? instantiationService.createInstance(StatusBarViewItem, action, undefined) : undefined;
|
||||
return action instanceof MenuItemAction ? instantiationService.createInstance(TextOnlyMenuEntryActionViewItem, action, undefined) : undefined;
|
||||
});
|
||||
this._leftActions = new ActionBar(this.element, { actionViewItemProvider });
|
||||
this._rightActions = new ActionBar(this.element, { actionViewItemProvider });
|
||||
|
|
|
@ -6,11 +6,14 @@
|
|||
import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button';
|
||||
import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
import { ActionRunner, IAction, IActionRunner, SubmenuAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IToolBarRenderOptions } from 'vs/platform/actions/browser/toolbar';
|
||||
import { MenuId, IMenuService, MenuItemAction, IMenuActionOptions } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IHoverService } from 'vs/platform/hover/browser/hover';
|
||||
|
@ -66,7 +69,7 @@ export class WorkbenchButtonBar extends ButtonBar {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
update(actions: IAction[]): void {
|
||||
update(actions: IAction[], secondary: IAction[]): void {
|
||||
|
||||
const conifgProvider: IButtonConfigProvider = this._options?.buttonConfigProvider ?? (() => ({ showLabel: true }));
|
||||
|
||||
|
@ -127,16 +130,46 @@ export class WorkbenchButtonBar extends ButtonBar {
|
|||
this._actionRunner.run(action);
|
||||
}));
|
||||
}
|
||||
|
||||
if (secondary.length > 0) {
|
||||
|
||||
const btn = this.addButton({
|
||||
secondary: true,
|
||||
ariaLabel: localize('moreActions', "More Actions")
|
||||
});
|
||||
|
||||
btn.icon = Codicon.dropDownButton;
|
||||
btn.element.classList.add('default-colors', 'monaco-text-button');
|
||||
|
||||
btn.enabled = true;
|
||||
this._updateStore.add(this._hoverService.setupManagedHover(hoverDelegate, btn.element, localize('moreActions', "More Actions")));
|
||||
this._updateStore.add(btn.onDidClick(async () => {
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => btn.element,
|
||||
getActions: () => secondary,
|
||||
actionRunner: this._actionRunner,
|
||||
onHide: () => btn.element.setAttribute('aria-expanded', 'false')
|
||||
});
|
||||
btn.element.setAttribute('aria-expanded', 'true');
|
||||
|
||||
}));
|
||||
}
|
||||
this._onDidChange.fire(this);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IMenuWorkbenchButtonBarOptions extends IWorkbenchButtonBarOptions {
|
||||
menuOptions?: IMenuActionOptions;
|
||||
|
||||
toolbarOptions?: IToolBarRenderOptions;
|
||||
}
|
||||
|
||||
export class MenuWorkbenchButtonBar extends WorkbenchButtonBar {
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
menuId: MenuId,
|
||||
options: IWorkbenchButtonBarOptions | undefined,
|
||||
options: IMenuWorkbenchButtonBarOptions | undefined,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
|
@ -153,12 +186,16 @@ export class MenuWorkbenchButtonBar extends WorkbenchButtonBar {
|
|||
|
||||
this.clear();
|
||||
|
||||
const actions = menu
|
||||
.getActions({ renderShortTitle: true })
|
||||
.flatMap(entry => entry[1]);
|
||||
|
||||
super.update(actions);
|
||||
const primary: IAction[] = [];
|
||||
const secondary: IAction[] = [];
|
||||
createAndFillInActionBarActions(
|
||||
menu,
|
||||
options?.menuOptions,
|
||||
{ primary, secondary },
|
||||
options?.toolbarOptions?.primaryGroup
|
||||
);
|
||||
|
||||
super.update(primary, secondary);
|
||||
};
|
||||
this._store.add(menu.onDidChange(update));
|
||||
update();
|
||||
|
|
|
@ -11,6 +11,20 @@
|
|||
background-size: 16px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.menu-entry.text-only .action-label {
|
||||
color: var(--vscode-descriptionForeground);
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.menu-entry.text-only:not(:last-of-type) .action-label::after {
|
||||
content: ', ';
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.menu-entry.text-only + .action-item:not(.text-only) > .monaco-dropdown .action-label {
|
||||
color: var(--vscode-descriptionForeground);
|
||||
}
|
||||
|
||||
.monaco-dropdown-with-default {
|
||||
display: flex !important;
|
||||
flex-direction: row;
|
||||
|
|
|
@ -31,6 +31,7 @@ import { assertType } from 'vs/base/common/types';
|
|||
import { asCssVariable, selectBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
|
||||
|
||||
export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void {
|
||||
const groups = menu.getActions(options);
|
||||
|
@ -121,7 +122,7 @@ export interface IMenuEntryActionViewItemOptions {
|
|||
hoverDelegate?: IHoverDelegate;
|
||||
}
|
||||
|
||||
export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
export class MenuEntryActionViewItem<T extends IMenuEntryActionViewItemOptions = IMenuEntryActionViewItemOptions> extends ActionViewItem {
|
||||
|
||||
private _wantsAltCommand: boolean = false;
|
||||
private readonly _itemClassDispose = this._register(new MutableDisposable());
|
||||
|
@ -129,7 +130,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
|||
|
||||
constructor(
|
||||
action: MenuItemAction,
|
||||
options: IMenuEntryActionViewItemOptions | undefined,
|
||||
protected _options: T | undefined,
|
||||
@IKeybindingService protected readonly _keybindingService: IKeybindingService,
|
||||
@INotificationService protected _notificationService: INotificationService,
|
||||
@IContextKeyService protected _contextKeyService: IContextKeyService,
|
||||
|
@ -137,7 +138,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
|||
@IContextMenuService protected _contextMenuService: IContextMenuService,
|
||||
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService
|
||||
) {
|
||||
super(undefined, action, { icon: !!(action.class || action.item.icon), label: !action.class && !action.item.icon, draggable: options?.draggable, keybinding: options?.keybinding, hoverDelegate: options?.hoverDelegate });
|
||||
super(undefined, action, { icon: !!(action.class || action.item.icon), label: !action.class && !action.item.icon, draggable: _options?.draggable, keybinding: _options?.keybinding, hoverDelegate: _options?.hoverDelegate });
|
||||
this._altKey = ModifierKeyEmitter.getInstance();
|
||||
}
|
||||
|
||||
|
@ -285,6 +286,43 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
|||
}
|
||||
}
|
||||
|
||||
export interface ITextOnlyMenuEntryActionViewItemOptions extends IMenuEntryActionViewItemOptions {
|
||||
conversational?: boolean;
|
||||
}
|
||||
|
||||
export class TextOnlyMenuEntryActionViewItem extends MenuEntryActionViewItem<ITextOnlyMenuEntryActionViewItemOptions> {
|
||||
|
||||
override render(container: HTMLElement): void {
|
||||
this.options.label = true;
|
||||
this.options.icon = false;
|
||||
super.render(container);
|
||||
container.classList.add('text-only');
|
||||
}
|
||||
|
||||
protected override updateLabel() {
|
||||
const kb = this._keybindingService.lookupKeybinding(this._action.id, this._contextKeyService);
|
||||
if (!kb) {
|
||||
return super.updateLabel();
|
||||
}
|
||||
if (this.label) {
|
||||
const kb2 = TextOnlyMenuEntryActionViewItem._symbolPrintEnter(kb);
|
||||
|
||||
if (this._options?.conversational) {
|
||||
this.label.textContent = localize({ key: 'content2', comment: ['A label with keybindg like "ESC to dismiss"'] }, '{1} to {0}', this._action.label, kb2);
|
||||
|
||||
} else {
|
||||
this.label.textContent = localize({ key: 'content', comment: ['A label', 'A keybinding'] }, '{0} ({1})', this._action.label, kb2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static _symbolPrintEnter(kb: ResolvedKeybinding) {
|
||||
return kb.getLabel()
|
||||
?.replace(/\benter\b/gi, '\u23CE')
|
||||
.replace(/\bEscape\b/gi, 'Esc');
|
||||
}
|
||||
}
|
||||
|
||||
export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem {
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -92,7 +92,7 @@ export interface IChatFileTreeInfo {
|
|||
export type ChatTreeItem = IChatRequestViewModel | IChatResponseViewModel | IChatWelcomeMessageViewModel;
|
||||
|
||||
export interface IChatListItemRendererOptions {
|
||||
readonly renderStyle?: 'default' | 'compact';
|
||||
readonly renderStyle?: 'default' | 'compact' | 'minimal';
|
||||
readonly noHeader?: boolean;
|
||||
readonly noPadding?: boolean;
|
||||
readonly editableCodeBlock?: boolean;
|
||||
|
@ -102,7 +102,7 @@ export interface IChatListItemRendererOptions {
|
|||
export interface IChatWidgetViewOptions {
|
||||
renderInputOnTop?: boolean;
|
||||
renderFollowups?: boolean;
|
||||
renderStyle?: 'default' | 'compact';
|
||||
renderStyle?: 'default' | 'compact' | 'minimal';
|
||||
supportsFileReferences?: boolean;
|
||||
filter?: (item: ChatTreeItem) => boolean;
|
||||
rendererOptions?: IChatListItemRendererOptions;
|
||||
|
|
|
@ -271,17 +271,42 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
if (this.rendererOptions.noPadding) {
|
||||
rowContainer.classList.add('no-padding');
|
||||
}
|
||||
const header = dom.append(rowContainer, $('.header'));
|
||||
|
||||
let headerParent = rowContainer;
|
||||
let referencesListParent = rowContainer;
|
||||
let valueParent = rowContainer;
|
||||
let detailContainerParent: HTMLElement | undefined;
|
||||
let toolbarParent: HTMLElement | undefined;
|
||||
|
||||
if (this.rendererOptions.renderStyle === 'minimal') {
|
||||
rowContainer.classList.add('interactive-item-compact');
|
||||
rowContainer.classList.add('minimal');
|
||||
// -----------------------------------------------------
|
||||
// icon | details
|
||||
// | references
|
||||
// | value
|
||||
// -----------------------------------------------------
|
||||
const lhsContainer = dom.append(rowContainer, $('.column.left'));
|
||||
const rhsContainer = dom.append(rowContainer, $('.column'));
|
||||
|
||||
headerParent = lhsContainer;
|
||||
detailContainerParent = rhsContainer;
|
||||
referencesListParent = rhsContainer;
|
||||
valueParent = rhsContainer;
|
||||
toolbarParent = dom.append(rowContainer, $('.header'));
|
||||
}
|
||||
|
||||
const header = dom.append(headerParent, $('.header'));
|
||||
const user = dom.append(header, $('.user'));
|
||||
user.tabIndex = 0;
|
||||
user.role = 'toolbar';
|
||||
const avatarContainer = dom.append(user, $('.avatar-container'));
|
||||
const username = dom.append(user, $('h3.username'));
|
||||
const detailContainer = dom.append(user, $('span.detail-container'));
|
||||
const detailContainer = dom.append(detailContainerParent ?? user, $('span.detail-container'));
|
||||
const detail = dom.append(detailContainer, $('span.detail'));
|
||||
dom.append(detailContainer, $('span.chat-animated-ellipsis'));
|
||||
const referencesListContainer = dom.append(rowContainer, $('.referencesListContainer'));
|
||||
const value = dom.append(rowContainer, $('.value'));
|
||||
const referencesListContainer = dom.append(referencesListParent, $('.referencesListContainer'));
|
||||
const value = dom.append(valueParent, $('.value'));
|
||||
const elementDisposables = new DisposableStore();
|
||||
|
||||
const contextKeyService = templateDisposables.add(this.contextKeyService.createScoped(rowContainer));
|
||||
|
@ -290,7 +315,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
if (this.rendererOptions.noHeader) {
|
||||
header.classList.add('hidden');
|
||||
} else {
|
||||
titleToolbar = templateDisposables.add(scopedInstantiationService.createInstance(MenuWorkbenchToolBar, header, MenuId.ChatMessageTitle, {
|
||||
titleToolbar = templateDisposables.add(scopedInstantiationService.createInstance(MenuWorkbenchToolBar, toolbarParent ?? header, MenuId.ChatMessageTitle, {
|
||||
menuOptions: {
|
||||
shouldForwardArgs: true
|
||||
},
|
||||
|
|
|
@ -107,6 +107,9 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
private _onDidChangeParsedInput = this._register(new Emitter<void>());
|
||||
readonly onDidChangeParsedInput = this._onDidChangeParsedInput.event;
|
||||
|
||||
private readonly _onWillMaybeChangeHeight = new Emitter<void>();
|
||||
readonly onWillMaybeChangeHeight: Event<void> = this._onWillMaybeChangeHeight.event;
|
||||
|
||||
private _onDidChangeHeight = this._register(new Emitter<number>());
|
||||
readonly onDidChangeHeight = this._onDidChangeHeight.event;
|
||||
|
||||
|
@ -374,6 +377,8 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
};
|
||||
});
|
||||
|
||||
this._onWillMaybeChangeHeight.fire();
|
||||
|
||||
this.tree.setChildren(null, treeItems, {
|
||||
diffIdentityProvider: {
|
||||
getId: (element) => {
|
||||
|
@ -549,12 +554,12 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
this._onDidChangeContentHeight.fire();
|
||||
}
|
||||
|
||||
private createInput(container: HTMLElement, options?: { renderFollowups: boolean; renderStyle?: 'default' | 'compact' }): void {
|
||||
private createInput(container: HTMLElement, options?: { renderFollowups: boolean; renderStyle?: 'default' | 'compact' | 'minimal' }): void {
|
||||
this.inputPart = this._register(this.instantiationService.createInstance(ChatInputPart,
|
||||
this.location,
|
||||
{
|
||||
renderFollowups: options?.renderFollowups ?? true,
|
||||
renderStyle: options?.renderStyle,
|
||||
renderStyle: options?.renderStyle === 'minimal' ? 'compact' : options?.renderStyle,
|
||||
menus: { executeToolbar: MenuId.ChatExecute, ...this.viewOptions.menus },
|
||||
editorOverflowWidgetsDomNode: this.viewOptions.editorOverflowWidgetsDomNode,
|
||||
}
|
||||
|
|
|
@ -49,12 +49,12 @@
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.interactive-item-container .header .detail-container {
|
||||
.interactive-item-container .detail-container {
|
||||
font-size: 12px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
}
|
||||
|
||||
.interactive-item-container .header .detail-container .detail .agentOrSlashCommandDetected A {
|
||||
.interactive-item-container .detail-container .detail .agentOrSlashCommandDetected A {
|
||||
cursor: pointer;
|
||||
color: var(--vscode-textLink-foreground);
|
||||
}
|
||||
|
@ -342,6 +342,28 @@
|
|||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.interactive-item-container.minimal {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.interactive-item-container.minimal .column.left {
|
||||
width: 20px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.interactive-item-container.minimal .user > .username {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.interactive-item-container.minimal .detail-container {
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
.interactive-item-container.minimal > .header {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.interactive-session .interactive-input-and-execute-toolbar {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { IMenuItem, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
|
||||
import * as InlineChatActions from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions';
|
||||
import { INLINE_CHAT_ID } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { CTX_INLINE_CHAT_CONFIG_TXT_BTNS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, INLINE_CHAT_ID, MENU_INLINE_CHAT_CONTENT_STATUS, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
@ -19,6 +19,10 @@ import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browse
|
|||
import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService';
|
||||
import { InlineChatEnabler, InlineChatSessionServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl';
|
||||
import { AccessibleViewRegistry } from 'vs/platform/accessibility/browser/accessibleViewRegistry';
|
||||
import { CancelAction, SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CONTEXT_CHAT_INPUT_HAS_TEXT } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
|
||||
// --- browser
|
||||
|
@ -28,6 +32,41 @@ registerSingleton(IInlineChatSavingService, InlineChatSavingServiceImpl, Instant
|
|||
|
||||
registerEditorContribution(INLINE_CHAT_ID, InlineChatController, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors
|
||||
|
||||
// --- MENU special ---
|
||||
|
||||
const sendActionMenuItem: IMenuItem = {
|
||||
group: '0_main',
|
||||
order: 0,
|
||||
command: {
|
||||
id: SubmitAction.ID,
|
||||
title: localize('edit', "Send"),
|
||||
},
|
||||
when: ContextKeyExpr.and(
|
||||
CONTEXT_CHAT_INPUT_HAS_TEXT,
|
||||
CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.toNegated(),
|
||||
CTX_INLINE_CHAT_CONFIG_TXT_BTNS
|
||||
),
|
||||
};
|
||||
|
||||
MenuRegistry.appendMenuItem(MENU_INLINE_CHAT_CONTENT_STATUS, sendActionMenuItem);
|
||||
MenuRegistry.appendMenuItem(MENU_INLINE_CHAT_WIDGET_STATUS, sendActionMenuItem);
|
||||
|
||||
const cancelActionMenuItem: IMenuItem = {
|
||||
group: '0_main',
|
||||
order: 0,
|
||||
command: {
|
||||
id: CancelAction.ID,
|
||||
title: localize('cancel', "Cancel Request"),
|
||||
shortTitle: localize('cancelShort', "Cancel"),
|
||||
},
|
||||
when: ContextKeyExpr.and(
|
||||
CTX_INLINE_CHAT_REQUEST_IN_PROGRESS,
|
||||
),
|
||||
};
|
||||
|
||||
MenuRegistry.appendMenuItem(MENU_INLINE_CHAT_WIDGET_STATUS, cancelActionMenuItem);
|
||||
|
||||
// --- actions ---
|
||||
|
||||
registerAction2(InlineChatActions.StartSessionAction);
|
||||
registerAction2(InlineChatActions.CloseAction);
|
||||
|
@ -36,7 +75,6 @@ registerAction2(InlineChatActions.UnstashSessionAction);
|
|||
registerAction2(InlineChatActions.DiscardHunkAction);
|
||||
registerAction2(InlineChatActions.DiscardAction);
|
||||
registerAction2(InlineChatActions.RerunAction);
|
||||
registerAction2(InlineChatActions.CancelSessionAction);
|
||||
registerAction2(InlineChatActions.MoveToNextHunk);
|
||||
registerAction2(InlineChatActions.MoveToPreviousHunk);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/em
|
|||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
|
||||
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_HAS_STASHED_SESSION, ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, MENU_INLINE_CHAT_WIDGET, ACTION_TOGGLE_DIFF, ACTION_REGENERATE_RESPONSE } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_CONFIG_TXT_BTNS } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { localize, localize2 } from 'vs/nls';
|
||||
import { Action2, IAction2Options } from 'vs/platform/actions/common/actions';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
@ -29,7 +29,6 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
|||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { CONTEXT_CHAT_REQUEST_IN_PROGRESS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
|
||||
CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start');
|
||||
CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES);
|
||||
|
@ -228,27 +227,6 @@ export class FocusInlineChat extends EditorAction2 {
|
|||
}
|
||||
}
|
||||
|
||||
export class DiscardHunkAction extends AbstractInlineChatAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'inlineChat.discardHunkChange',
|
||||
title: localize('discard', 'Discard'),
|
||||
icon: Codicon.clearAll,
|
||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.OnlyMessages), CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty), CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live)),
|
||||
group: '0_main',
|
||||
order: 3
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): Promise<void> {
|
||||
return ctrl.discardHunk();
|
||||
}
|
||||
}
|
||||
|
||||
export class DiscardAction extends AbstractInlineChatAction {
|
||||
|
||||
|
@ -271,33 +249,6 @@ export class DiscardAction extends AbstractInlineChatAction {
|
|||
}
|
||||
}
|
||||
|
||||
export class ToggleDiffForChange extends AbstractInlineChatAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: ACTION_TOGGLE_DIFF,
|
||||
precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live), CTX_INLINE_CHAT_CHANGE_HAS_DIFF),
|
||||
title: localize2('showChanges', 'Toggle Changes'),
|
||||
icon: Codicon.diffSingle,
|
||||
toggled: {
|
||||
condition: CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF,
|
||||
},
|
||||
menu: [
|
||||
{
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: '1_main',
|
||||
when: ContextKeyExpr.and(CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live), CTX_INLINE_CHAT_CHANGE_HAS_DIFF),
|
||||
order: 10,
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
override runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController): void {
|
||||
ctrl.toggleDiff();
|
||||
}
|
||||
}
|
||||
|
||||
export class AcceptChanges extends AbstractInlineChatAction {
|
||||
|
||||
constructor() {
|
||||
|
@ -313,10 +264,13 @@ export class AcceptChanges extends AbstractInlineChatAction {
|
|||
primary: KeyMod.CtrlCmd | KeyCode.Enter,
|
||||
}],
|
||||
menu: {
|
||||
when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.OnlyMessages), CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)),
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: '0_main',
|
||||
order: 0
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(
|
||||
CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.toNegated(),
|
||||
CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.MessagesAndEdits)
|
||||
),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -326,32 +280,72 @@ export class AcceptChanges extends AbstractInlineChatAction {
|
|||
}
|
||||
}
|
||||
|
||||
export class CancelSessionAction extends AbstractInlineChatAction {
|
||||
export class DiscardHunkAction extends AbstractInlineChatAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'inlineChat.cancel',
|
||||
title: localize('cancel', 'Cancel'),
|
||||
id: 'inlineChat.discardHunkChange',
|
||||
title: localize('discard', 'Discard'),
|
||||
icon: Codicon.clearAll,
|
||||
precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Preview)),
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorContrib - 1,
|
||||
primary: KeyCode.Escape
|
||||
},
|
||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
when: ContextKeyExpr.and(CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Preview), CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)),
|
||||
group: '0_main',
|
||||
order: 3
|
||||
order: 2,
|
||||
when: ContextKeyExpr.and(
|
||||
CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate(),
|
||||
CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.MessagesAndEdits),
|
||||
CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live)
|
||||
),
|
||||
},
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.Escape,
|
||||
when: CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.MessagesAndEdits)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): Promise<void> {
|
||||
ctrl.cancelSession();
|
||||
return ctrl.discardHunk();
|
||||
}
|
||||
}
|
||||
|
||||
export class RerunAction extends AbstractInlineChatAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: ACTION_REGENERATE_RESPONSE,
|
||||
title: localize2('chat.rerun.label', "Rerun Request"),
|
||||
shortTitle: localize('rerun', 'Rerun'),
|
||||
f1: false,
|
||||
icon: Codicon.refresh,
|
||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: '0_main',
|
||||
order: 5,
|
||||
when: ContextKeyExpr.and(
|
||||
CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate(),
|
||||
CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.None)
|
||||
)
|
||||
},
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KeyR
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): Promise<void> {
|
||||
const chatService = accessor.get(IChatService);
|
||||
const model = ctrl.chatWidget.viewModel?.model;
|
||||
|
||||
const lastRequest = model?.getRequests().at(-1);
|
||||
if (lastRequest) {
|
||||
await chatService.resendRequest(lastRequest, { noCommandDetection: false, attempt: lastRequest.attempt + 1, location: ctrl.chatWidget.location });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CloseAction extends AbstractInlineChatAction {
|
||||
|
||||
|
@ -362,15 +356,25 @@ export class CloseAction extends AbstractInlineChatAction {
|
|||
icon: Codicon.close,
|
||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorContrib - 1,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.Escape,
|
||||
when: CTX_INLINE_CHAT_USER_DID_EDIT.negate()
|
||||
},
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET,
|
||||
group: 'navigation',
|
||||
menu: [{
|
||||
id: MENU_INLINE_CHAT_CONTENT_STATUS,
|
||||
group: '0_main',
|
||||
order: 10,
|
||||
}
|
||||
}, {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: '0_main',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(
|
||||
CTX_INLINE_CHAT_CONFIG_TXT_BTNS,
|
||||
ContextKeyExpr.or(
|
||||
CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.Messages),
|
||||
CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Preview)
|
||||
)
|
||||
),
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -387,6 +391,11 @@ export class ConfigureInlineChatAction extends AbstractInlineChatAction {
|
|||
icon: Codicon.settingsGear,
|
||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||
f1: true,
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: 'zzz',
|
||||
order: 5
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -478,10 +487,23 @@ export class ViewInChatAction extends AbstractInlineChatAction {
|
|||
title: localize('viewInChat', 'View in Chat'),
|
||||
icon: Codicon.commentDiscussion,
|
||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET,
|
||||
group: 'navigation',
|
||||
order: 5
|
||||
menu: [{
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: 'more',
|
||||
order: 1,
|
||||
when: CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.Messages)
|
||||
}, {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: '0_main',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(
|
||||
CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.Messages),
|
||||
CTX_INLINE_CHAT_CONFIG_TXT_BTNS
|
||||
)
|
||||
}],
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -490,29 +512,27 @@ export class ViewInChatAction extends AbstractInlineChatAction {
|
|||
}
|
||||
}
|
||||
|
||||
export class RerunAction extends AbstractInlineChatAction {
|
||||
export class ToggleDiffForChange extends AbstractInlineChatAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: ACTION_REGENERATE_RESPONSE,
|
||||
title: localize2('chat.rerun.label', "Rerun Request"),
|
||||
f1: false,
|
||||
icon: Codicon.refresh,
|
||||
precondition: CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate(),
|
||||
id: ACTION_TOGGLE_DIFF,
|
||||
precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live), CTX_INLINE_CHAT_CHANGE_HAS_DIFF),
|
||||
title: localize2('showChanges', 'Toggle Changes'),
|
||||
icon: Codicon.diffSingle,
|
||||
toggled: {
|
||||
condition: CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF,
|
||||
},
|
||||
menu: {
|
||||
id: MENU_INLINE_CHAT_WIDGET_STATUS,
|
||||
group: '0_main',
|
||||
order: 5,
|
||||
group: 'more',
|
||||
when: ContextKeyExpr.and(CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live), CTX_INLINE_CHAT_CHANGE_HAS_DIFF),
|
||||
order: 10,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): Promise<void> {
|
||||
const chatService = accessor.get(IChatService);
|
||||
const model = ctrl.chatWidget.viewModel?.model;
|
||||
|
||||
const lastRequest = model?.getRequests().at(-1);
|
||||
if (lastRequest) {
|
||||
await chatService.resendRequest(lastRequest, { noCommandDetection: false, attempt: lastRequest.attempt + 1, location: ctrl.chatWidget.location });
|
||||
}
|
||||
override runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController): void {
|
||||
ctrl.toggleDiff();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { inlineChatBackground } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { inlineChatBackground, MENU_INLINE_CHAT_CONTENT_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
||||
import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
|
@ -22,6 +22,9 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
|||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
|
||||
import { MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { TextOnlyMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
|
||||
export class InlineChatContentWidget implements IContentWidget {
|
||||
|
||||
|
@ -31,7 +34,7 @@ export class InlineChatContentWidget implements IContentWidget {
|
|||
private readonly _store = new DisposableStore();
|
||||
private readonly _domNode = document.createElement('div');
|
||||
private readonly _inputContainer = document.createElement('div');
|
||||
private readonly _messageContainer = document.createElement('div');
|
||||
private readonly _toolbarContainer = document.createElement('div');
|
||||
|
||||
private _position?: IPosition;
|
||||
|
||||
|
@ -68,7 +71,7 @@ export class InlineChatContentWidget implements IContentWidget {
|
|||
{
|
||||
defaultElementHeight: 32,
|
||||
editorOverflowWidgetsDomNode: _editor.getOverflowWidgetsDomNode(),
|
||||
renderStyle: 'compact',
|
||||
renderStyle: 'minimal',
|
||||
renderInputOnTop: true,
|
||||
renderFollowups: true,
|
||||
supportsFileReferences: false,
|
||||
|
@ -94,13 +97,19 @@ export class InlineChatContentWidget implements IContentWidget {
|
|||
|
||||
this._domNode.appendChild(this._inputContainer);
|
||||
|
||||
this._messageContainer.classList.add('hidden', 'message');
|
||||
this._domNode.appendChild(this._messageContainer);
|
||||
this._toolbarContainer.classList.add('toolbar');
|
||||
this._domNode.appendChild(this._toolbarContainer);
|
||||
|
||||
this._store.add(scopedInstaService.createInstance(MenuWorkbenchToolBar, this._toolbarContainer, MENU_INLINE_CHAT_CONTENT_STATUS, {
|
||||
actionViewItemProvider: action => action instanceof MenuItemAction ? instaService.createInstance(TextOnlyMenuEntryActionViewItem, action, { conversational: true }) : undefined,
|
||||
toolbarOptions: { primaryGroup: '0_main' },
|
||||
icon: false,
|
||||
label: true,
|
||||
}));
|
||||
|
||||
const tracker = dom.trackFocus(this._domNode);
|
||||
this._store.add(tracker.onDidBlur(() => {
|
||||
if (this._visible
|
||||
if (this._visible && this._widget.inputEditor.getModel()?.getValueLength() === 0
|
||||
// && !"ON"
|
||||
) {
|
||||
this._onDidBlur.fire();
|
||||
|
|
|
@ -35,7 +35,7 @@ import { EmptyResponse, ErrorResponse, ReplyResponse, Session, SessionPrompt } f
|
|||
import { IInlineChatSessionService } from './inlineChatSessionService';
|
||||
import { EditModeStrategy, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies';
|
||||
import { InlineChatZoneWidget } from './inlineChatZoneWidget';
|
||||
import { CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { StashedSession } from './inlineChatSession';
|
||||
import { IModelDeltaDecoration, ITextModel, IValidEditOperation } from 'vs/editor/common/model';
|
||||
import { InlineChatContentWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget';
|
||||
|
@ -110,8 +110,9 @@ export class InlineChatController implements IEditorContribution {
|
|||
private readonly _ui: Lazy<{ content: InlineChatContentWidget; zone: InlineChatZoneWidget }>;
|
||||
|
||||
private readonly _ctxVisible: IContextKey<boolean>;
|
||||
private readonly _ctxResponseTypes: IContextKey<undefined | InlineChatResponseTypes>;
|
||||
private readonly _ctxResponseType: IContextKey<undefined | InlineChatResponseType>;
|
||||
private readonly _ctxUserDidEdit: IContextKey<boolean>;
|
||||
private readonly _ctxRequestInProgress: IContextKey<boolean>;
|
||||
|
||||
private _messages = this._store.add(new Emitter<Message>());
|
||||
|
||||
|
@ -148,7 +149,8 @@ export class InlineChatController implements IEditorContribution {
|
|||
) {
|
||||
this._ctxVisible = CTX_INLINE_CHAT_VISIBLE.bindTo(contextKeyService);
|
||||
this._ctxUserDidEdit = CTX_INLINE_CHAT_USER_DID_EDIT.bindTo(contextKeyService);
|
||||
this._ctxResponseTypes = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(contextKeyService);
|
||||
this._ctxResponseType = CTX_INLINE_CHAT_RESPONSE_TYPE.bindTo(contextKeyService);
|
||||
this._ctxRequestInProgress = CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService);
|
||||
|
||||
this._ui = new Lazy(() => {
|
||||
let location = ChatAgentLocation.Editor;
|
||||
|
@ -302,7 +304,6 @@ export class InlineChatController implements IEditorContribution {
|
|||
if (m === Message.ACCEPT_INPUT) {
|
||||
// user accepted the input before having a session
|
||||
options.autoSend = true;
|
||||
this._ui.value.zone.widget.updateProgress(true);
|
||||
this._ui.value.zone.widget.updateInfo(localize('welcome.2', "Getting ready..."));
|
||||
} else {
|
||||
createSessionCts.cancel();
|
||||
|
@ -384,6 +385,7 @@ export class InlineChatController implements IEditorContribution {
|
|||
this._updatePlaceholder();
|
||||
|
||||
this._showWidget(!this._session.chatModel.hasRequests);
|
||||
this._ui.value.zone.widget.updateToolbar(true);
|
||||
|
||||
this._sessionStore.add(this._editor.onDidChangeModel((e) => {
|
||||
const msg = this._session?.chatModel.hasRequests
|
||||
|
@ -422,17 +424,7 @@ export class InlineChatController implements IEditorContribution {
|
|||
}));
|
||||
|
||||
this._sessionStore.add(this._session.chatModel.onDidChange(async e => {
|
||||
if (e.kind === 'addRequest' && e.request.response) {
|
||||
this._ui.value.zone.widget.updateProgress(true);
|
||||
|
||||
const listener = e.request.response.onDidChange(() => {
|
||||
|
||||
if (e.request.response?.isCanceled || e.request.response?.isComplete) {
|
||||
this._ui.value.zone.widget.updateProgress(false);
|
||||
listener.dispose();
|
||||
}
|
||||
});
|
||||
} else if (e.kind === 'removeRequest') {
|
||||
if (e.kind === 'removeRequest') {
|
||||
// TODO@jrieken there is still some work left for when a request "in the middle"
|
||||
// is removed. We will undo all changes till that point but not remove those
|
||||
// later request
|
||||
|
@ -607,9 +599,6 @@ export class InlineChatController implements IEditorContribution {
|
|||
return State.WAIT_FOR_INPUT;
|
||||
}
|
||||
|
||||
const input = request.message.text;
|
||||
this._ui.value.zone.widget.value = input;
|
||||
|
||||
this._session.addInput(new SessionPrompt(request, this._editor.getModel()!.getAlternativeVersionId()));
|
||||
|
||||
return State.SHOW_REQUEST;
|
||||
|
@ -620,6 +609,8 @@ export class InlineChatController implements IEditorContribution {
|
|||
assertType(this._session);
|
||||
assertType(this._session.chatModel.requestInProgress);
|
||||
|
||||
this._ctxRequestInProgress.set(true);
|
||||
|
||||
const { chatModel } = this._session;
|
||||
const request: IChatRequestModel | undefined = chatModel.getRequests().at(-1);
|
||||
|
||||
|
@ -627,7 +618,7 @@ export class InlineChatController implements IEditorContribution {
|
|||
assertType(request.response);
|
||||
|
||||
this._showWidget(false);
|
||||
this._ui.value.zone.widget.value = request.message.text;
|
||||
// this._ui.value.zone.widget.value = request.message.text;
|
||||
this._ui.value.zone.widget.selectAll(false);
|
||||
this._ui.value.zone.widget.updateInfo('');
|
||||
|
||||
|
@ -684,6 +675,8 @@ export class InlineChatController implements IEditorContribution {
|
|||
// apply edits
|
||||
const handleResponse = () => {
|
||||
|
||||
this._updateCtxResponseType();
|
||||
|
||||
if (!localEditGroup) {
|
||||
localEditGroup = <IChatTextEditGroup | undefined>response.response.value.find(part => part.kind === 'textEditGroup' && isEqual(part.uri, this._session?.textModelN.uri));
|
||||
}
|
||||
|
@ -747,8 +740,7 @@ export class InlineChatController implements IEditorContribution {
|
|||
this._session.wholeRange.fixup(diff?.changes ?? []);
|
||||
await this._session.hunkData.recompute(editState, diff);
|
||||
|
||||
this._ui.value.zone.widget.updateToolbar(true);
|
||||
this._ui.value.zone.widget.updateProgress(false);
|
||||
this._ctxRequestInProgress.set(false);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
@ -759,20 +751,6 @@ export class InlineChatController implements IEditorContribution {
|
|||
|
||||
const { response } = this._session.lastExchange!;
|
||||
|
||||
let responseTypes: InlineChatResponseTypes | undefined;
|
||||
for (const request of this._session.chatModel.getRequests()) {
|
||||
if (!request.response) {
|
||||
continue;
|
||||
}
|
||||
const thisType = asInlineChatResponseType(request.response.response);
|
||||
if (responseTypes === undefined) {
|
||||
responseTypes = thisType;
|
||||
} else if (responseTypes !== thisType) {
|
||||
responseTypes = InlineChatResponseTypes.Mixed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._ctxResponseTypes.set(responseTypes);
|
||||
|
||||
let newPosition: Position | undefined;
|
||||
|
||||
|
@ -792,7 +770,6 @@ export class InlineChatController implements IEditorContribution {
|
|||
} else if (response instanceof ReplyResponse) {
|
||||
// real response -> complex...
|
||||
this._ui.value.zone.widget.updateStatus('');
|
||||
this._ui.value.zone.widget.updateToolbar(true);
|
||||
|
||||
newPosition = await this._strategy.renderChanges(response);
|
||||
}
|
||||
|
@ -867,6 +844,7 @@ export class InlineChatController implements IEditorContribution {
|
|||
|
||||
private _showWidget(initialRender: boolean = false, position?: Position) {
|
||||
assertType(this._editor.hasModel());
|
||||
this._ctxVisible.set(true);
|
||||
|
||||
let widgetPosition: Position;
|
||||
if (position) {
|
||||
|
@ -904,11 +882,6 @@ export class InlineChatController implements IEditorContribution {
|
|||
}
|
||||
}
|
||||
|
||||
if (this._session && this._ui.rawValue?.zone) {
|
||||
this._ui.rawValue?.zone.updateBackgroundColor(widgetPosition, this._session.wholeRange.value);
|
||||
}
|
||||
|
||||
this._ctxVisible.set(true);
|
||||
return widgetPosition;
|
||||
}
|
||||
|
||||
|
@ -926,6 +899,31 @@ export class InlineChatController implements IEditorContribution {
|
|||
}
|
||||
}
|
||||
|
||||
private _updateCtxResponseType(): void {
|
||||
|
||||
if (!this._session) {
|
||||
this._ctxResponseType.set(InlineChatResponseType.None);
|
||||
return;
|
||||
}
|
||||
|
||||
const hasLocalEdit = (response: IResponse): boolean => {
|
||||
return response.value.some(part => part.kind === 'textEditGroup' && isEqual(part.uri, this._session?.textModelN.uri));
|
||||
};
|
||||
|
||||
let responseType = InlineChatResponseType.None;
|
||||
for (const request of this._session.chatModel.getRequests()) {
|
||||
if (!request.response) {
|
||||
continue;
|
||||
}
|
||||
responseType = InlineChatResponseType.Messages;
|
||||
if (hasLocalEdit(request.response.response)) {
|
||||
responseType = InlineChatResponseType.MessagesAndEdits;
|
||||
break; // no need to check further
|
||||
}
|
||||
}
|
||||
this._ctxResponseType.set(responseType);
|
||||
}
|
||||
|
||||
private async _makeChanges(edits: TextEdit[], opts: ProgressingEditsOptions | undefined, undoStopBefore: boolean) {
|
||||
assertType(this._session);
|
||||
assertType(this._strategy);
|
||||
|
@ -1135,25 +1133,3 @@ async function moveToPanelChat(accessor: ServicesAccessor, model: ChatModel | un
|
|||
widget.focusLastMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function asInlineChatResponseType(response: IResponse): InlineChatResponseTypes {
|
||||
let result: InlineChatResponseTypes | undefined;
|
||||
for (const item of response.value) {
|
||||
let thisType: InlineChatResponseTypes;
|
||||
switch (item.kind) {
|
||||
case 'textEditGroup':
|
||||
thisType = InlineChatResponseTypes.OnlyEdits;
|
||||
break;
|
||||
case 'markdownContent':
|
||||
default:
|
||||
thisType = InlineChatResponseTypes.OnlyMessages;
|
||||
break;
|
||||
}
|
||||
if (result === undefined) {
|
||||
result = thisType;
|
||||
} else if (result !== thisType) {
|
||||
return InlineChatResponseTypes.Mixed;
|
||||
}
|
||||
}
|
||||
return result ?? InlineChatResponseTypes.Empty;
|
||||
}
|
||||
|
|
|
@ -1,256 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Dimension, h } from 'vs/base/browser/dom';
|
||||
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
|
||||
import * as editorColorRegistry from 'vs/editor/common/core/editorColorRegistry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { INLINE_CHAT_ID, inlineChatRegionHighlight } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
|
||||
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button';
|
||||
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { SaveReason, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IAction, toAction } from 'vs/base/common/actions';
|
||||
import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
|
||||
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { TAB_ACTIVE_MODIFIED_BORDER } from 'vs/workbench/common/theme';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export class InlineChatFileCreatePreviewWidget extends ZoneWidget {
|
||||
|
||||
private static TitleHeight = 35;
|
||||
|
||||
private readonly _elements = h('div.inline-chat-newfile-widget@domNode', [
|
||||
h('div.title@title', [
|
||||
h('span.name.show-file-icons@name'),
|
||||
h('span.detail@detail'),
|
||||
]),
|
||||
h('div.editor@editor'),
|
||||
]);
|
||||
|
||||
private readonly _name: ResourceLabel;
|
||||
private readonly _previewEditor: ICodeEditor;
|
||||
private readonly _previewStore = new MutableDisposable();
|
||||
private readonly _buttonBar: ButtonBarWidget;
|
||||
private _dim: Dimension | undefined;
|
||||
|
||||
constructor(
|
||||
parentEditor: ICodeEditor,
|
||||
@IInstantiationService instaService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ITextModelService private readonly _textModelResolverService: ITextModelService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
) {
|
||||
super(parentEditor, {
|
||||
showArrow: false,
|
||||
showFrame: true,
|
||||
frameColor: colorRegistry.asCssVariable(TAB_ACTIVE_MODIFIED_BORDER),
|
||||
frameWidth: 1,
|
||||
isResizeable: true,
|
||||
isAccessible: true,
|
||||
showInHiddenAreas: true,
|
||||
ordinal: 10000 + 2
|
||||
});
|
||||
super.create();
|
||||
|
||||
this._name = instaService.createInstance(ResourceLabel, this._elements.name, { supportIcons: true });
|
||||
this._elements.detail.appendChild(renderIcon(Codicon.circleFilled));
|
||||
|
||||
const contributions = EditorExtensionsRegistry
|
||||
.getEditorContributions()
|
||||
.filter(c => c.id !== INLINE_CHAT_ID);
|
||||
|
||||
this._previewEditor = instaService.createInstance(EmbeddedCodeEditorWidget, this._elements.editor, {
|
||||
scrollBeyondLastLine: false,
|
||||
stickyScroll: { enabled: false },
|
||||
minimap: { enabled: false },
|
||||
scrollbar: { alwaysConsumeMouseWheel: false, useShadows: true, ignoreHorizontalScrollbarInContentHeight: true, },
|
||||
}, { isSimpleWidget: true, contributions }, parentEditor);
|
||||
|
||||
const doStyle = () => {
|
||||
const theme = themeService.getColorTheme();
|
||||
const overrides: [target: string, source: string][] = [
|
||||
[colorRegistry.editorBackground, inlineChatRegionHighlight],
|
||||
[editorColorRegistry.editorGutter, inlineChatRegionHighlight],
|
||||
];
|
||||
|
||||
for (const [target, source] of overrides) {
|
||||
const value = theme.getColor(source);
|
||||
if (value) {
|
||||
this._elements.domNode.style.setProperty(colorRegistry.asCssVariableName(target), String(value));
|
||||
}
|
||||
}
|
||||
};
|
||||
doStyle();
|
||||
this._disposables.add(themeService.onDidColorThemeChange(doStyle));
|
||||
|
||||
this._buttonBar = instaService.createInstance(ButtonBarWidget);
|
||||
this._elements.title.appendChild(this._buttonBar.domNode);
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this._name.dispose();
|
||||
this._buttonBar.dispose();
|
||||
this._previewEditor.dispose();
|
||||
this._previewStore.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected override _fillContainer(container: HTMLElement): void {
|
||||
container.appendChild(this._elements.domNode);
|
||||
}
|
||||
|
||||
override show(): void {
|
||||
throw new Error('Use showFileCreation');
|
||||
}
|
||||
|
||||
async showCreation(where: Position, untitledTextModel: IUntitledTextEditorModel): Promise<void> {
|
||||
|
||||
const store = new DisposableStore();
|
||||
this._previewStore.value = store;
|
||||
|
||||
this._name.element.setFile(untitledTextModel.resource, {
|
||||
fileKind: FileKind.FILE,
|
||||
fileDecorations: { badges: true, colors: true }
|
||||
});
|
||||
|
||||
const actionSave = toAction({
|
||||
id: '1',
|
||||
label: localize('save', "Create"),
|
||||
run: () => untitledTextModel.save({ reason: SaveReason.EXPLICIT })
|
||||
});
|
||||
const actionSaveAs = toAction({
|
||||
id: '2',
|
||||
label: localize('saveAs', "Create As"),
|
||||
run: async () => {
|
||||
const ids = this._editorService.findEditors(untitledTextModel.resource, { supportSideBySide: SideBySideEditor.ANY });
|
||||
await this._editorService.save(ids.slice(), { saveAs: true, reason: SaveReason.EXPLICIT });
|
||||
}
|
||||
});
|
||||
|
||||
this._buttonBar.update([
|
||||
[actionSave, actionSaveAs],
|
||||
[(toAction({ id: '3', label: localize('discard', "Discard"), run: () => untitledTextModel.revert() }))]
|
||||
]);
|
||||
|
||||
store.add(Event.any(
|
||||
untitledTextModel.onDidRevert,
|
||||
untitledTextModel.onDidSave,
|
||||
untitledTextModel.onDidChangeDirty,
|
||||
untitledTextModel.onWillDispose
|
||||
)(() => this.hide()));
|
||||
|
||||
await untitledTextModel.resolve();
|
||||
|
||||
const ref = await this._textModelResolverService.createModelReference(untitledTextModel.resource);
|
||||
store.add(ref);
|
||||
|
||||
const model = ref.object.textEditorModel;
|
||||
this._previewEditor.setModel(model);
|
||||
|
||||
const lineHeight = this.editor.getOption(EditorOption.lineHeight);
|
||||
|
||||
this._elements.title.style.height = `${InlineChatFileCreatePreviewWidget.TitleHeight}px`;
|
||||
const titleHightInLines = InlineChatFileCreatePreviewWidget.TitleHeight / lineHeight;
|
||||
|
||||
const maxLines = Math.max(4, Math.floor((this.editor.getLayoutInfo().height / lineHeight) * .33));
|
||||
const lines = Math.min(maxLines, model.getLineCount());
|
||||
|
||||
super.show(where, titleHightInLines + lines);
|
||||
}
|
||||
|
||||
override hide(): void {
|
||||
this._previewStore.clear();
|
||||
super.hide();
|
||||
}
|
||||
|
||||
// --- layout
|
||||
|
||||
protected override revealRange(range: Range, isLastLine: boolean): void {
|
||||
// ignore
|
||||
}
|
||||
|
||||
protected override _onWidth(widthInPixel: number): void {
|
||||
if (this._dim) {
|
||||
this._doLayout(this._dim.height, widthInPixel);
|
||||
}
|
||||
}
|
||||
|
||||
protected override _doLayout(heightInPixel: number, widthInPixel: number): void {
|
||||
|
||||
const { lineNumbersLeft } = this.editor.getLayoutInfo();
|
||||
this._elements.title.style.marginLeft = `${lineNumbersLeft}px`;
|
||||
|
||||
const newDim = new Dimension(widthInPixel, heightInPixel);
|
||||
if (!Dimension.equals(this._dim, newDim)) {
|
||||
this._dim = newDim;
|
||||
this._previewEditor.layout(this._dim.with(undefined, this._dim.height - InlineChatFileCreatePreviewWidget.TitleHeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ButtonBarWidget {
|
||||
|
||||
private readonly _domNode = h('div.buttonbar-widget');
|
||||
private readonly _buttonBar: ButtonBar;
|
||||
private readonly _store = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||
) {
|
||||
this._buttonBar = new ButtonBar(this.domNode);
|
||||
|
||||
}
|
||||
|
||||
update(allActions: IAction[][]): void {
|
||||
this._buttonBar.clear();
|
||||
let secondary = false;
|
||||
for (const actions of allActions) {
|
||||
let btn: IButton;
|
||||
const [first, ...rest] = actions;
|
||||
if (!first) {
|
||||
continue;
|
||||
} else if (rest.length === 0) {
|
||||
// single action
|
||||
btn = this._buttonBar.addButton({ ...defaultButtonStyles, secondary });
|
||||
} else {
|
||||
btn = this._buttonBar.addButtonWithDropdown({
|
||||
...defaultButtonStyles,
|
||||
addPrimaryActionToDropdown: false,
|
||||
actions: rest,
|
||||
contextMenuProvider: this._contextMenuService
|
||||
});
|
||||
}
|
||||
btn.label = first.label;
|
||||
this._store.add(btn.onDidClick(() => first.run()));
|
||||
secondary = true;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._buttonBar.dispose();
|
||||
this._store.dispose();
|
||||
}
|
||||
|
||||
get domNode() {
|
||||
return this._domNode.root;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ import { localize } from 'vs/nls';
|
|||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IWorkbenchButtonBarOptions, MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar';
|
||||
import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -35,9 +35,9 @@ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibil
|
|||
import { IAccessibleViewService } from 'vs/platform/accessibility/browser/accessibleView';
|
||||
import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands';
|
||||
import { ChatModel, IChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
||||
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED, inlineChatBackground } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED, inlineChatBackground, InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { chatRequestBackground } from 'vs/workbench/contrib/chat/common/chatColors';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
|
@ -48,6 +48,7 @@ import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateF
|
|||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IHoverService } from 'vs/platform/hover/browser/hover';
|
||||
import { IChatWidgetViewOptions } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { TextOnlyMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
|
||||
|
||||
export interface InlineChatWidgetViewState {
|
||||
|
@ -62,6 +63,7 @@ export interface IInlineChatWidgetConstructionOptions {
|
|||
* The menu that rendered as button bar, use for accept, discard etc
|
||||
*/
|
||||
statusMenuId: MenuId | { menu: MenuId; options: IWorkbenchButtonBarOptions };
|
||||
|
||||
/**
|
||||
* The men that rendered in the lower right corner, use for feedback
|
||||
*/
|
||||
|
@ -91,7 +93,6 @@ export class InlineChatWidget {
|
|||
[
|
||||
h('div.chat-widget@chatWidget'),
|
||||
h('div.progress@progress'),
|
||||
h('div.followUps.hidden@followUps'),
|
||||
h('div.previewDiff.hidden@previewDiff'),
|
||||
h('div.accessibleViewer@accessibleViewer'),
|
||||
h('div.status@status', [
|
||||
|
@ -139,8 +140,6 @@ export class InlineChatWidget {
|
|||
this._progressBar = new ProgressBar(this._elements.progress);
|
||||
this._store.add(this._progressBar);
|
||||
|
||||
let allowRequests = false;
|
||||
|
||||
this.scopedContextKeyService = this._store.add(_contextKeyService.createScoped(this._elements.chatWidget));
|
||||
const scopedInstaService = _instantiationService.createChild(
|
||||
new ServiceCollection([
|
||||
|
@ -156,20 +155,11 @@ export class InlineChatWidget {
|
|||
{ resource: true },
|
||||
{
|
||||
defaultElementHeight: 32,
|
||||
renderStyle: 'compact',
|
||||
renderInputOnTop: true,
|
||||
renderStyle: 'minimal',
|
||||
renderInputOnTop: false,
|
||||
renderFollowups: true,
|
||||
supportsFileReferences: true,
|
||||
// editorOverflowWidgetsDomNode: options.editorOverflowWidgetsDomNode,
|
||||
filter: item => {
|
||||
if (isWelcomeVM(item)) {
|
||||
return false;
|
||||
}
|
||||
if (isRequestVM(item)) {
|
||||
return allowRequests;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
filter: item => !isWelcomeVM(item),
|
||||
...options.chatWidgetViewOptions
|
||||
},
|
||||
{
|
||||
|
@ -184,34 +174,6 @@ export class InlineChatWidget {
|
|||
this._chatWidget.setVisible(true);
|
||||
this._store.add(this._chatWidget);
|
||||
|
||||
const viewModelListener = this._store.add(new MutableDisposable());
|
||||
this._store.add(this._chatWidget.onDidChangeViewModel(() => {
|
||||
const model = this._chatWidget.viewModel;
|
||||
|
||||
if (!model) {
|
||||
allowRequests = false;
|
||||
viewModelListener.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const updateAllowRequestsFilter = () => {
|
||||
let requestCount = 0;
|
||||
for (const item of model.getItems()) {
|
||||
if (isRequestVM(item)) {
|
||||
if (++requestCount >= 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const newAllowRequest = requestCount >= 2;
|
||||
if (newAllowRequest !== allowRequests) {
|
||||
allowRequests = newAllowRequest;
|
||||
this._chatWidget.refilter();
|
||||
}
|
||||
};
|
||||
viewModelListener.value = model.onDidChange(updateAllowRequestsFilter);
|
||||
}));
|
||||
|
||||
const viewModelStore = this._store.add(new DisposableStore());
|
||||
this._store.add(this._chatWidget.onDidChangeViewModel(() => {
|
||||
viewModelStore.clear();
|
||||
|
@ -237,12 +199,31 @@ export class InlineChatWidget {
|
|||
this._store.add(this._chatWidget.inputEditor.onDidBlurEditorWidget(() => this._ctxInputEditorFocused.set(false)));
|
||||
|
||||
const statusMenuId = options.statusMenuId instanceof MenuId ? options.statusMenuId : options.statusMenuId.menu;
|
||||
const statusMenuOptions = options.statusMenuId instanceof MenuId ? undefined : options.statusMenuId.options;
|
||||
|
||||
const statusButtonBar = this._instantiationService.createInstance(MenuWorkbenchButtonBar, this._elements.statusToolbar, statusMenuId, statusMenuOptions);
|
||||
this._store.add(statusButtonBar.onDidChange(() => this._onDidChangeHeight.fire()));
|
||||
this._store.add(statusButtonBar);
|
||||
if (this._configurationService.getValue(InlineChatConfigKeys.ExpTextButtons)) {
|
||||
// TEXT-ONLY bar
|
||||
const statusToolbarMenu = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.statusToolbar, statusMenuId, {
|
||||
hiddenItemStrategy: HiddenItemStrategy.NoHide,
|
||||
actionViewItemProvider: action => action instanceof MenuItemAction ? this._instantiationService.createInstance(TextOnlyMenuEntryActionViewItem, action, { conversational: true }) : undefined,
|
||||
toolbarOptions: { primaryGroup: '0_main' },
|
||||
menuOptions: { renderShortTitle: true },
|
||||
label: true,
|
||||
icon: false
|
||||
});
|
||||
this._store.add(statusToolbarMenu.onDidChangeMenuItems(() => this._onDidChangeHeight.fire()));
|
||||
this._store.add(statusToolbarMenu);
|
||||
|
||||
} else {
|
||||
// BUTTON bar
|
||||
const statusMenuOptions = options.statusMenuId instanceof MenuId ? undefined : options.statusMenuId.options;
|
||||
const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.statusToolbar, statusMenuId, {
|
||||
toolbarOptions: { primaryGroup: '0_main' },
|
||||
menuOptions: { renderShortTitle: true },
|
||||
...statusMenuOptions,
|
||||
});
|
||||
this._store.add(statusButtonBar.onDidChange(() => this._onDidChangeHeight.fire()));
|
||||
this._store.add(statusButtonBar);
|
||||
}
|
||||
|
||||
const workbenchToolbarOptions = {
|
||||
hiddenItemStrategy: HiddenItemStrategy.NoHide,
|
||||
|
@ -265,7 +246,6 @@ export class InlineChatWidget {
|
|||
}));
|
||||
|
||||
this._elements.root.tabIndex = 0;
|
||||
this._elements.followUps.tabIndex = 0;
|
||||
this._elements.statusLabel.tabIndex = 0;
|
||||
this._updateAriaLabel();
|
||||
|
||||
|
@ -332,7 +312,6 @@ export class InlineChatWidget {
|
|||
protected _doLayout(dimension: Dimension): void {
|
||||
const extraHeight = this._getExtraHeight();
|
||||
const progressHeight = getTotalHeight(this._elements.progress);
|
||||
const followUpsHeight = getTotalHeight(this._elements.followUps);
|
||||
const statusHeight = getTotalHeight(this._elements.status);
|
||||
|
||||
// console.log('ZONE#Widget#layout', { height: dimension.height, extraHeight, progressHeight, followUpsHeight, statusHeight, LIST: dimension.height - progressHeight - followUpsHeight - statusHeight - extraHeight });
|
||||
|
@ -342,7 +321,7 @@ export class InlineChatWidget {
|
|||
this._elements.progress.style.width = `${dimension.width}px`;
|
||||
|
||||
this._chatWidget.layout(
|
||||
dimension.height - progressHeight - followUpsHeight - statusHeight - extraHeight,
|
||||
dimension.height - progressHeight - statusHeight - extraHeight,
|
||||
dimension.width
|
||||
);
|
||||
}
|
||||
|
@ -352,13 +331,12 @@ export class InlineChatWidget {
|
|||
*/
|
||||
get contentHeight(): number {
|
||||
const data = {
|
||||
followUpsHeight: getTotalHeight(this._elements.followUps),
|
||||
chatWidgetContentHeight: this._chatWidget.contentHeight,
|
||||
progressHeight: getTotalHeight(this._elements.progress),
|
||||
statusHeight: getTotalHeight(this._elements.status),
|
||||
extraHeight: this._getExtraHeight()
|
||||
};
|
||||
const result = data.progressHeight + data.chatWidgetContentHeight + data.followUpsHeight + data.statusHeight + data.extraHeight;
|
||||
const result = data.progressHeight + data.chatWidgetContentHeight + data.statusHeight + data.extraHeight;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -381,7 +359,7 @@ export class InlineChatWidget {
|
|||
}
|
||||
|
||||
protected _getExtraHeight(): number {
|
||||
return 12 /* padding */ + 2 /*border*/ + 12 /*shadow*/;
|
||||
return 4 /* padding */ + 2 /*border*/ + 12 /*shadow*/;
|
||||
}
|
||||
|
||||
updateProgress(show: boolean) {
|
||||
|
@ -421,6 +399,7 @@ export class InlineChatWidget {
|
|||
}
|
||||
|
||||
updateToolbar(show: boolean) {
|
||||
this._elements.root.classList.toggle('toolbar', show);
|
||||
this._elements.statusToolbar.classList.toggle('hidden', !show);
|
||||
this._elements.feedbackToolbar.classList.toggle('hidden', !show);
|
||||
this._elements.status.classList.toggle('actions', show);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { assertType } from 'vs/base/common/types';
|
|||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
@ -75,13 +75,23 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
|||
}
|
||||
}
|
||||
});
|
||||
this._disposables.add(this.widget);
|
||||
|
||||
let scrollState: StableEditorBottomScrollState | undefined;
|
||||
this._disposables.add(this.widget.chatWidget.onWillMaybeChangeHeight(() => {
|
||||
if (this.position) {
|
||||
scrollState = StableEditorBottomScrollState.capture(this.editor);
|
||||
}
|
||||
}));
|
||||
this._disposables.add(this.widget.onDidChangeHeight(() => {
|
||||
if (this.position) {
|
||||
// only relayout when visible
|
||||
scrollState ??= StableEditorBottomScrollState.capture(this.editor);
|
||||
this._relayout(this._computeHeight().linesValue);
|
||||
scrollState.restore(this.editor);
|
||||
}
|
||||
}));
|
||||
this._disposables.add(this.widget);
|
||||
|
||||
this.create();
|
||||
|
||||
this._disposables.add(addDisposableListener(this.domNode, 'click', e => {
|
||||
|
@ -172,20 +182,16 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
|||
}
|
||||
|
||||
override updatePositionAndHeight(position: Position): void {
|
||||
const scrollState = StableEditorBottomScrollState.capture(this.editor);
|
||||
super.updatePositionAndHeight(position, this._computeHeight().linesValue);
|
||||
this._setWidgetMargins(position);
|
||||
scrollState.restore(this.editor);
|
||||
}
|
||||
|
||||
protected override _getWidth(info: EditorLayoutInfo): number {
|
||||
return info.width - info.minimap.minimapWidth;
|
||||
}
|
||||
|
||||
updateBackgroundColor(newPosition: Position, wholeRange: IRange) {
|
||||
assertType(this.container);
|
||||
const widgetLineNumber = newPosition.lineNumber;
|
||||
this.container.classList.toggle('inside-selection', widgetLineNumber > wholeRange.startLineNumber && widgetLineNumber < wholeRange.endLineNumber);
|
||||
}
|
||||
|
||||
private _calculateIndentationWidth(position: Position): number {
|
||||
const viewModel = this.editor._getViewModel();
|
||||
if (!viewModel) {
|
||||
|
@ -224,11 +230,12 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
|||
}
|
||||
|
||||
override hide(): void {
|
||||
this.container!.classList.remove('inside-selection');
|
||||
const scrollState = StableEditorBottomScrollState.capture(this.editor);
|
||||
this._ctxCursorPosition.reset();
|
||||
this.widget.reset();
|
||||
this.widget.chatWidget.setVisible(false);
|
||||
super.hide();
|
||||
aria.status(localize('inlineChatClosed', 'Closed inline chat widget'));
|
||||
scrollState.restore(this.editor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,6 @@
|
|||
max-width: unset;
|
||||
}
|
||||
|
||||
.monaco-workbench .zone-widget-container.inside-selection {
|
||||
background-color: var(--vscode-inlineChat-regionHighlight);
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat {
|
||||
color: inherit;
|
||||
padding: 0 8px 8px 8px;
|
||||
|
@ -25,6 +21,10 @@
|
|||
background: var(--vscode-inlineChat-background);
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat.toolbar {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-execute-toolbar {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
|||
}
|
||||
|
||||
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.interactive-item-compact {
|
||||
padding: 6px 4px;
|
||||
padding: 6px 0;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@
|
|||
.monaco-workbench .inline-chat .status .label {
|
||||
overflow: hidden;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
|
@ -146,9 +146,25 @@
|
|||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat .status .actions .action-item.text-only .action-label,
|
||||
.monaco-workbench .inline-chat-content-widget .status .actions .action-item.text-only .action-label {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
padding: 2px;
|
||||
margin: 2px 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.menu-entry.text-only + .action-item:not(.text-only) > .monaco-dropdown .action-label {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
width: unset;
|
||||
height: unset;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat .status .actions > .monaco-button,
|
||||
.monaco-workbench .inline-chat .status .actions > .monaco-button-dropdown {
|
||||
margin-right: 6px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat .status .actions > .monaco-button-dropdown > .monaco-dropdown-button {
|
||||
|
@ -166,12 +182,8 @@
|
|||
}
|
||||
|
||||
.monaco-workbench .inline-chat .status .actions .monaco-text-button {
|
||||
padding: 2px 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat .status .monaco-toolbar .action-item {
|
||||
padding: 0 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* TODO@jrieken not needed? */
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
.monaco-workbench .inline-chat-content-widget {
|
||||
z-index: 50;
|
||||
padding: 6px 6px 6px 6px;
|
||||
padding: 6px 6px 0px 6px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--vscode-inlineChat-background);
|
||||
box-shadow: 0 4px 8px var(--vscode-inlineChat-shadow);
|
||||
|
@ -23,20 +23,3 @@
|
|||
.monaco-workbench .inline-chat-content-widget.interactive-session .interactive-input-part.compact {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat-content-widget .message {
|
||||
overflow: hidden;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 11px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat-content-widget .message > .codicon {
|
||||
padding-right: 5px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat-content-widget .hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -6,95 +6,24 @@
|
|||
import { localize } from 'vs/nls';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { diffInserted, diffRemoved, editorHoverHighlight, editorWidgetBackground, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, transparent, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Extensions as ExtensionsMigration, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration';
|
||||
|
||||
|
||||
export const enum InlineChatResponseTypes {
|
||||
Empty = 'empty',
|
||||
OnlyEdits = 'onlyEdits',
|
||||
OnlyMessages = 'onlyMessages',
|
||||
Mixed = 'mixed'
|
||||
}
|
||||
|
||||
export const INLINE_CHAT_ID = 'interactiveEditor';
|
||||
export const INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID = 'interactiveEditorAccessiblityHelp';
|
||||
|
||||
export const enum EditMode {
|
||||
Live = 'live',
|
||||
Preview = 'preview'
|
||||
}
|
||||
|
||||
export const CTX_INLINE_CHAT_HAS_AGENT = new RawContextKey<boolean>('inlineChatHasProvider', false, localize('inlineChatHasProvider', "Whether a provider for interactive editors exists"));
|
||||
export const CTX_INLINE_CHAT_VISIBLE = new RawContextKey<boolean>('inlineChatVisible', false, localize('inlineChatVisible', "Whether the interactive editor input is visible"));
|
||||
export const CTX_INLINE_CHAT_FOCUSED = new RawContextKey<boolean>('inlineChatFocused', false, localize('inlineChatFocused', "Whether the interactive editor input is focused"));
|
||||
export const CTX_INLINE_CHAT_RESPONSE_FOCUSED = new RawContextKey<boolean>('inlineChatResponseFocused', false, localize('inlineChatResponseFocused', "Whether the interactive widget's response is focused"));
|
||||
export const CTX_INLINE_CHAT_EMPTY = new RawContextKey<boolean>('inlineChatEmpty', false, localize('inlineChatEmpty', "Whether the interactive editor input is empty"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_FIRST = new RawContextKey<boolean>('inlineChatInnerCursorFirst', false, localize('inlineChatInnerCursorFirst', "Whether the cursor of the iteractive editor input is on the first line"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_LAST = new RawContextKey<boolean>('inlineChatInnerCursorLast', false, localize('inlineChatInnerCursorLast', "Whether the cursor of the iteractive editor input is on the last line"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_START = new RawContextKey<boolean>('inlineChatInnerCursorStart', false, localize('inlineChatInnerCursorStart', "Whether the cursor of the iteractive editor input is on the start of the input"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_END = new RawContextKey<boolean>('inlineChatInnerCursorEnd', false, localize('inlineChatInnerCursorEnd', "Whether the cursor of the iteractive editor input is on the end of the input"));
|
||||
export const CTX_INLINE_CHAT_OUTER_CURSOR_POSITION = new RawContextKey<'above' | 'below' | ''>('inlineChatOuterCursorPosition', '', localize('inlineChatOuterCursorPosition', "Whether the cursor of the outer editor is above or below the interactive editor input"));
|
||||
export const CTX_INLINE_CHAT_HAS_STASHED_SESSION = new RawContextKey<boolean>('inlineChatHasStashedSession', false, localize('inlineChatHasStashedSession', "Whether interactive editor has kept a session for quick restore"));
|
||||
export const CTX_INLINE_CHAT_RESPONSE_TYPES = new RawContextKey<InlineChatResponseTypes | undefined>('inlineChatResponseTypes', InlineChatResponseTypes.Empty, localize('inlineChatResponseTypes', "What type was the responses have been receieved"));
|
||||
export const CTX_INLINE_CHAT_USER_DID_EDIT = new RawContextKey<boolean>('inlineChatUserDidEdit', undefined, localize('inlineChatUserDidEdit', "Whether the user did changes ontop of the inline chat"));
|
||||
export const CTX_INLINE_CHAT_DOCUMENT_CHANGED = new RawContextKey<boolean>('inlineChatDocumentChanged', false, localize('inlineChatDocumentChanged', "Whether the document has changed concurrently"));
|
||||
export const CTX_INLINE_CHAT_CHANGE_HAS_DIFF = new RawContextKey<boolean>('inlineChatChangeHasDiff', false, localize('inlineChatChangeHasDiff', "Whether the current change supports showing a diff"));
|
||||
export const CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF = new RawContextKey<boolean>('inlineChatChangeShowsDiff', false, localize('inlineChatChangeShowsDiff', "Whether the current change showing a diff"));
|
||||
export const CTX_INLINE_CHAT_EDIT_MODE = new RawContextKey<EditMode>('config.inlineChat.mode', EditMode.Live);
|
||||
|
||||
// --- (select) action identifier
|
||||
|
||||
export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges';
|
||||
export const ACTION_REGENERATE_RESPONSE = 'inlineChat.regenerate';
|
||||
export const ACTION_VIEW_IN_CHAT = 'inlineChat.viewInChat';
|
||||
export const ACTION_TOGGLE_DIFF = 'inlineChat.toggleDiff';
|
||||
|
||||
// --- menus
|
||||
|
||||
export const MENU_INLINE_CHAT_WIDGET = MenuId.for('inlineChatWidget');
|
||||
export const MENU_INLINE_CHAT_WIDGET_STATUS = MenuId.for('inlineChatWidget.status');
|
||||
|
||||
// --- colors
|
||||
|
||||
|
||||
export const inlineChatBackground = registerColor('inlineChat.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, localize('inlineChat.background', "Background color of the interactive editor widget"));
|
||||
export const inlineChatBorder = registerColor('inlineChat.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('inlineChat.border', "Border color of the interactive editor widget"));
|
||||
export const inlineChatShadow = registerColor('inlineChat.shadow', { dark: widgetShadow, light: widgetShadow, hcDark: widgetShadow, hcLight: widgetShadow }, localize('inlineChat.shadow', "Shadow color of the interactive editor widget"));
|
||||
export const inlineChatRegionHighlight = registerColor('inlineChat.regionHighlight', { dark: editorHoverHighlight, light: editorHoverHighlight, hcDark: editorHoverHighlight, hcLight: editorHoverHighlight }, localize('inlineChat.regionHighlight', "Background highlighting of the current interactive region. Must be transparent."), true);
|
||||
export const inlineChatInputBorder = registerColor('inlineChatInput.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('inlineChatInput.border', "Border color of the interactive editor input"));
|
||||
export const inlineChatInputFocusBorder = registerColor('inlineChatInput.focusBorder', { dark: focusBorder, light: focusBorder, hcDark: focusBorder, hcLight: focusBorder }, localize('inlineChatInput.focusBorder', "Border color of the interactive editor input when focused"));
|
||||
export const inlineChatInputPlaceholderForeground = registerColor('inlineChatInput.placeholderForeground', { dark: inputPlaceholderForeground, light: inputPlaceholderForeground, hcDark: inputPlaceholderForeground, hcLight: inputPlaceholderForeground }, localize('inlineChatInput.placeholderForeground', "Foreground color of the interactive editor input placeholder"));
|
||||
export const inlineChatInputBackground = registerColor('inlineChatInput.background', { dark: inputBackground, light: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('inlineChatInput.background', "Background color of the interactive editor input"));
|
||||
|
||||
export const inlineChatDiffInserted = registerColor('inlineChatDiff.inserted', { dark: transparent(diffInserted, .5), light: transparent(diffInserted, .5), hcDark: transparent(diffInserted, .5), hcLight: transparent(diffInserted, .5) }, localize('inlineChatDiff.inserted', "Background color of inserted text in the interactive editor input"));
|
||||
export const overviewRulerInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.'));
|
||||
export const minimapInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.'));
|
||||
|
||||
export const inlineChatDiffRemoved = registerColor('inlineChatDiff.removed', { dark: transparent(diffRemoved, .5), light: transparent(diffRemoved, .5), hcDark: transparent(diffRemoved, .5), hcLight: transparent(diffRemoved, .5) }, localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input"));
|
||||
export const overviewRulerInlineChatDiffRemoved = registerColor('editorOverviewRuler.inlineChatRemoved', { dark: transparent(diffRemoved, 0.6), light: transparent(diffRemoved, 0.8), hcDark: transparent(diffRemoved, 0.6), hcLight: transparent(diffRemoved, 0.8) }, localize('editorOverviewRuler.inlineChatRemoved', 'Overview ruler marker color for inline chat removed content.'));
|
||||
|
||||
import { diffInserted, diffRemoved, editorWidgetBackground, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, transparent, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
// settings
|
||||
|
||||
|
||||
|
||||
Registry.as<IConfigurationMigrationRegistry>(ExtensionsMigration.ConfigurationMigration).registerConfigurationMigrations(
|
||||
[{
|
||||
key: 'interactiveEditor.editMode', migrateFn: (value: any) => {
|
||||
return [['inlineChat.mode', { value: value }]];
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
export const enum InlineChatConfigKeys {
|
||||
Mode = 'inlineChat.mode',
|
||||
FinishOnType = 'inlineChat.finishOnType',
|
||||
AcceptedOrDiscardBeforeSave = 'inlineChat.acceptedOrDiscardBeforeSave',
|
||||
HoldToSpeech = 'inlineChat.holdToSpeech',
|
||||
AccessibleDiffView = 'inlineChat.accessibleDiffView'
|
||||
AccessibleDiffView = 'inlineChat.accessibleDiffView',
|
||||
ExpTextButtons = 'inlineChat.experimental.textButtons'
|
||||
}
|
||||
|
||||
export const enum EditMode {
|
||||
Live = 'live',
|
||||
Preview = 'preview'
|
||||
}
|
||||
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
|
@ -136,6 +65,75 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
|
|||
localize('accessibleDiffView.on', "The accessible diff viewer is always enabled."),
|
||||
localize('accessibleDiffView.off', "The accessible diff viewer is never enabled."),
|
||||
],
|
||||
}
|
||||
},
|
||||
[InlineChatConfigKeys.ExpTextButtons]: {
|
||||
description: localize('txtButtons', "Whether to use textual buttons (Requires restart)."),
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export const INLINE_CHAT_ID = 'interactiveEditor';
|
||||
export const INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID = 'interactiveEditorAccessiblityHelp';
|
||||
|
||||
// --- CONTEXT
|
||||
|
||||
export const enum InlineChatResponseType {
|
||||
None = 'none',
|
||||
Messages = 'messages',
|
||||
MessagesAndEdits = 'messagesAndEdits'
|
||||
}
|
||||
|
||||
export const CTX_INLINE_CHAT_HAS_AGENT = new RawContextKey<boolean>('inlineChatHasProvider', false, localize('inlineChatHasProvider', "Whether a provider for interactive editors exists"));
|
||||
export const CTX_INLINE_CHAT_VISIBLE = new RawContextKey<boolean>('inlineChatVisible', false, localize('inlineChatVisible', "Whether the interactive editor input is visible"));
|
||||
export const CTX_INLINE_CHAT_FOCUSED = new RawContextKey<boolean>('inlineChatFocused', false, localize('inlineChatFocused', "Whether the interactive editor input is focused"));
|
||||
export const CTX_INLINE_CHAT_RESPONSE_FOCUSED = new RawContextKey<boolean>('inlineChatResponseFocused', false, localize('inlineChatResponseFocused', "Whether the interactive widget's response is focused"));
|
||||
export const CTX_INLINE_CHAT_EMPTY = new RawContextKey<boolean>('inlineChatEmpty', false, localize('inlineChatEmpty', "Whether the interactive editor input is empty"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_FIRST = new RawContextKey<boolean>('inlineChatInnerCursorFirst', false, localize('inlineChatInnerCursorFirst', "Whether the cursor of the iteractive editor input is on the first line"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_LAST = new RawContextKey<boolean>('inlineChatInnerCursorLast', false, localize('inlineChatInnerCursorLast', "Whether the cursor of the iteractive editor input is on the last line"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_START = new RawContextKey<boolean>('inlineChatInnerCursorStart', false, localize('inlineChatInnerCursorStart', "Whether the cursor of the iteractive editor input is on the start of the input"));
|
||||
export const CTX_INLINE_CHAT_INNER_CURSOR_END = new RawContextKey<boolean>('inlineChatInnerCursorEnd', false, localize('inlineChatInnerCursorEnd', "Whether the cursor of the iteractive editor input is on the end of the input"));
|
||||
export const CTX_INLINE_CHAT_OUTER_CURSOR_POSITION = new RawContextKey<'above' | 'below' | ''>('inlineChatOuterCursorPosition', '', localize('inlineChatOuterCursorPosition', "Whether the cursor of the outer editor is above or below the interactive editor input"));
|
||||
export const CTX_INLINE_CHAT_HAS_STASHED_SESSION = new RawContextKey<boolean>('inlineChatHasStashedSession', false, localize('inlineChatHasStashedSession', "Whether interactive editor has kept a session for quick restore"));
|
||||
export const CTX_INLINE_CHAT_USER_DID_EDIT = new RawContextKey<boolean>('inlineChatUserDidEdit', undefined, localize('inlineChatUserDidEdit', "Whether the user did changes ontop of the inline chat"));
|
||||
export const CTX_INLINE_CHAT_DOCUMENT_CHANGED = new RawContextKey<boolean>('inlineChatDocumentChanged', false, localize('inlineChatDocumentChanged', "Whether the document has changed concurrently"));
|
||||
export const CTX_INLINE_CHAT_CHANGE_HAS_DIFF = new RawContextKey<boolean>('inlineChatChangeHasDiff', false, localize('inlineChatChangeHasDiff', "Whether the current change supports showing a diff"));
|
||||
export const CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF = new RawContextKey<boolean>('inlineChatChangeShowsDiff', false, localize('inlineChatChangeShowsDiff', "Whether the current change showing a diff"));
|
||||
export const CTX_INLINE_CHAT_EDIT_MODE = new RawContextKey<EditMode>('config.inlineChat.mode', EditMode.Live);
|
||||
export const CTX_INLINE_CHAT_REQUEST_IN_PROGRESS = new RawContextKey<boolean>('inlineChatRequestInProgress', false, localize('inlineChatRequestInProgress', "Whether an inline chat request is currently in progress"));
|
||||
export const CTX_INLINE_CHAT_RESPONSE_TYPE = new RawContextKey<InlineChatResponseType>('inlineChatResponseType', InlineChatResponseType.None, localize('inlineChatResponseTypes', "What type was the responses have been receieved, nothing yet, just messages, or messaged and local edits"));
|
||||
|
||||
export const CTX_INLINE_CHAT_CONFIG_TXT_BTNS = ContextKeyExpr.equals(`config.${[InlineChatConfigKeys.ExpTextButtons]}`, true);
|
||||
|
||||
// --- (selected) action identifier
|
||||
|
||||
export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges';
|
||||
export const ACTION_REGENERATE_RESPONSE = 'inlineChat.regenerate';
|
||||
export const ACTION_VIEW_IN_CHAT = 'inlineChat.viewInChat';
|
||||
export const ACTION_TOGGLE_DIFF = 'inlineChat.toggleDiff';
|
||||
|
||||
// --- menus
|
||||
|
||||
export const MENU_INLINE_CHAT_WIDGET = MenuId.for('inlineChatWidget');
|
||||
export const MENU_INLINE_CHAT_CONTENT_STATUS = MenuId.for('inlineChat.content.status');
|
||||
export const MENU_INLINE_CHAT_WIDGET_STATUS = MenuId.for('inlineChatWidget.status');
|
||||
|
||||
// --- colors
|
||||
|
||||
|
||||
export const inlineChatBackground = registerColor('inlineChat.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, localize('inlineChat.background', "Background color of the interactive editor widget"));
|
||||
export const inlineChatBorder = registerColor('inlineChat.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('inlineChat.border', "Border color of the interactive editor widget"));
|
||||
export const inlineChatShadow = registerColor('inlineChat.shadow', { dark: widgetShadow, light: widgetShadow, hcDark: widgetShadow, hcLight: widgetShadow }, localize('inlineChat.shadow', "Shadow color of the interactive editor widget"));
|
||||
export const inlineChatInputBorder = registerColor('inlineChatInput.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('inlineChatInput.border', "Border color of the interactive editor input"));
|
||||
export const inlineChatInputFocusBorder = registerColor('inlineChatInput.focusBorder', { dark: focusBorder, light: focusBorder, hcDark: focusBorder, hcLight: focusBorder }, localize('inlineChatInput.focusBorder', "Border color of the interactive editor input when focused"));
|
||||
export const inlineChatInputPlaceholderForeground = registerColor('inlineChatInput.placeholderForeground', { dark: inputPlaceholderForeground, light: inputPlaceholderForeground, hcDark: inputPlaceholderForeground, hcLight: inputPlaceholderForeground }, localize('inlineChatInput.placeholderForeground', "Foreground color of the interactive editor input placeholder"));
|
||||
export const inlineChatInputBackground = registerColor('inlineChatInput.background', { dark: inputBackground, light: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('inlineChatInput.background', "Background color of the interactive editor input"));
|
||||
|
||||
export const inlineChatDiffInserted = registerColor('inlineChatDiff.inserted', { dark: transparent(diffInserted, .5), light: transparent(diffInserted, .5), hcDark: transparent(diffInserted, .5), hcLight: transparent(diffInserted, .5) }, localize('inlineChatDiff.inserted', "Background color of inserted text in the interactive editor input"));
|
||||
export const overviewRulerInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.'));
|
||||
export const minimapInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.'));
|
||||
|
||||
export const inlineChatDiffRemoved = registerColor('inlineChatDiff.removed', { dark: transparent(diffRemoved, .5), light: transparent(diffRemoved, .5), hcDark: transparent(diffRemoved, .5), hcLight: transparent(diffRemoved, .5) }, localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input"));
|
||||
export const overviewRulerInlineChatDiffRemoved = registerColor('editorOverviewRuler.inlineChatRemoved', { dark: transparent(diffRemoved, 0.6), light: transparent(diffRemoved, 0.8), hcDark: transparent(diffRemoved, 0.6), hcLight: transparent(diffRemoved, 0.8) }, localize('editorOverviewRuler.inlineChatRemoved', 'Overview ruler marker color for inline chat removed content.'));
|
||||
|
|
|
@ -31,7 +31,7 @@ import { IView, IViewDescriptorService } from 'vs/workbench/common/views';
|
|||
import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { IAccessibleViewService } from 'vs/platform/accessibility/browser/accessibleView';
|
||||
import { IChatAccessibilityService, IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatAgentLocation, ChatAgentService, IChatAgentData, IChatAgentNameService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { InlineChatController, InlineChatRunOptions, State } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
|
||||
import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
||||
|
@ -156,6 +156,11 @@ suite('InteractiveChatController', function () {
|
|||
[IChatWidgetService, new SyncDescriptor(ChatWidgetService)],
|
||||
[IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)],
|
||||
[IChatService, new SyncDescriptor(ChatService)],
|
||||
[IChatAgentNameService, new class extends mock<IChatAgentNameService>() {
|
||||
override getAgentNameRestriction(chatAgentData: IChatAgentData): boolean {
|
||||
return false;
|
||||
}
|
||||
}],
|
||||
[IEditorWorkerService, new SyncDescriptor(TestWorkerService)],
|
||||
[IContextKeyService, contextKeyService],
|
||||
[IChatAgentService, new SyncDescriptor(ChatAgentService)],
|
||||
|
@ -323,6 +328,7 @@ suite('InteractiveChatController', function () {
|
|||
assert.ok(session);
|
||||
assert.deepStrictEqual(session.wholeRange.value, new Range(3, 1, 3, 3)); // initial
|
||||
|
||||
ctrl.chatWidget.setInput('GENGEN');
|
||||
ctrl.acceptInput();
|
||||
assert.strictEqual(await ctrl.awaitStates([State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]), undefined);
|
||||
|
||||
|
@ -540,6 +546,7 @@ suite('InteractiveChatController', function () {
|
|||
|
||||
// REQUEST 2
|
||||
const p2 = ctrl.awaitStates([State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]);
|
||||
ctrl.chatWidget.setInput('1');
|
||||
await ctrl.acceptInput();
|
||||
assert.strictEqual(await p2, undefined);
|
||||
|
||||
|
@ -623,6 +630,7 @@ suite('InteractiveChatController', function () {
|
|||
|
||||
// REQUEST 2
|
||||
const p2 = ctrl.awaitStates([State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]);
|
||||
ctrl.chatWidget.setInput('1');
|
||||
await ctrl.acceptInput();
|
||||
assert.strictEqual(await p2, undefined);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
|||
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION, CTX_NOTEBOOK_CHAT_USER_DID_EDIT, MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext';
|
||||
import { NotebookChatController } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController';
|
||||
import { CELL_TITLE_CELL_GROUP_ID, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, getEditorFromArgsOrActivePane } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
|
||||
|
@ -261,7 +261,7 @@ registerAction2(class extends NotebookAction {
|
|||
id: MENU_CELL_CHAT_WIDGET_STATUS,
|
||||
group: 'inline',
|
||||
order: 0,
|
||||
when: CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.OnlyMessages),
|
||||
when: CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.Messages),
|
||||
}
|
||||
],
|
||||
f1: false
|
||||
|
|
|
@ -203,13 +203,17 @@ async function loadTests(opts) {
|
|||
'throw ListenerLeakError'
|
||||
]);
|
||||
|
||||
const _allowedSuitesWithOutput = new Set([
|
||||
'InteractiveChatController'
|
||||
]);
|
||||
|
||||
let _testsWithUnexpectedOutput = false;
|
||||
|
||||
for (const consoleFn of [console.log, console.error, console.info, console.warn, console.trace, console.debug]) {
|
||||
console[consoleFn.name] = function (msg) {
|
||||
if (!currentTest) {
|
||||
consoleFn.apply(console, arguments);
|
||||
} else if (!_allowedTestOutput.some(a => a.test(msg)) && !_allowedTestsWithOutput.has(currentTest.title)) {
|
||||
} else if (!_allowedTestOutput.some(a => a.test(msg)) && !_allowedTestsWithOutput.has(currentTest.title) && !_allowedSuitesWithOutput.has(currentTest.parent?.title)) {
|
||||
_testsWithUnexpectedOutput = true;
|
||||
consoleFn.apply(console, arguments);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue