Skip to content

Commit d8e9b30

Browse files
authored
Merge pull request microsoft#185213 from microsoft/joh/silly-swallow
joh/silly swallow
2 parents 83fa847 + be12637 commit d8e9b30

File tree

7 files changed

+229
-67
lines changed

7 files changed

+229
-67
lines changed

src/vs/base/browser/ui/button/button.css

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@
2525
text-decoration: none !important;
2626
}
2727

28+
.monaco-button {
29+
color: var(--vscode-button-foreground);
30+
background-color: var(--vscode-button-background);
31+
}
32+
33+
.monaco-button:hover {
34+
background-color: var(--vscode-button-hoverBackground);
35+
}
36+
37+
.monaco-button.secondary {
38+
color: var(--vscode-button-secondaryForeground);
39+
background-color: var(--vscode-button-secondaryBackground);
40+
}
41+
42+
.monaco-button.secondary:hover {
43+
background-color: var(--vscode-button-secondaryHoverBackground);
44+
}
45+
46+
2847
.monaco-button.disabled:focus,
2948
.monaco-button.disabled {
3049
opacity: 0.4 !important;
@@ -90,11 +109,19 @@
90109
.monaco-button-dropdown .monaco-button-dropdown-separator {
91110
padding: 4px 0;
92111
cursor: default;
112+
background-color: var(--vscode-button-background);
113+
border-top: 1px solid var(--vscode-button-border);
114+
border-bottom: 1px solid var(--vscode-button-border);
115+
}
116+
117+
.monaco-button.secondary + .monaco-button-dropdown-separator {
118+
background-color: var(--vscode-button-secondaryBackground);
93119
}
94120

95121
.monaco-button-dropdown .monaco-button-dropdown-separator > div {
96122
height: 100%;
97123
width: 1px;
124+
background-color: var(--vscode-button-separator);
98125
}
99126

100127
.monaco-button-dropdown > .monaco-button.monaco-dropdown-button {

src/vs/base/browser/ui/button/button.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { Color } from 'vs/base/common/color';
1616
import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
1717
import { IMarkdownString, isMarkdownString, markdownStringEqual } from 'vs/base/common/htmlContent';
1818
import { KeyCode } from 'vs/base/common/keyCodes';
19-
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
19+
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
2020
import { ThemeIcon } from 'vs/base/common/themables';
2121
import 'vs/css!./button';
2222
import { localize } from 'vs/nls';
@@ -89,6 +89,7 @@ export class Button extends Disposable implements IButton {
8989
this._element.tabIndex = 0;
9090
this._element.setAttribute('role', 'button');
9191

92+
this._element.classList.toggle('secondary', !!options.secondary);
9293
const background = options.secondary ? options.buttonSecondaryBackground : options.buttonBackground;
9394
const foreground = options.secondary ? options.buttonSecondaryForeground : options.buttonForeground;
9495

@@ -154,6 +155,11 @@ export class Button extends Disposable implements IButton {
154155
this._register(this.focusTracker.onDidBlur(() => { if (this.enabled) { this.updateBackground(false); } }));
155156
}
156157

158+
public override dispose(): void {
159+
super.dispose();
160+
this._element.remove();
161+
}
162+
157163
private getContentElements(content: string): HTMLElement[] {
158164
const elements: HTMLSpanElement[] = [];
159165
for (let segment of renderLabelWithIcons(content)) {
@@ -281,7 +287,7 @@ export class Button extends Disposable implements IButton {
281287

282288
export interface IButtonWithDropdownOptions extends IButtonOptions {
283289
readonly contextMenuProvider: IContextMenuProvider;
284-
readonly actions: IAction[];
290+
readonly actions: readonly IAction[];
285291
readonly actionRunner?: IActionRunner;
286292
readonly addPrimaryActionToDropdown?: boolean;
287293
}
@@ -344,6 +350,11 @@ export class ButtonWithDropdown extends Disposable implements IButton {
344350
}));
345351
}
346352

353+
override dispose() {
354+
super.dispose();
355+
this.element.remove();
356+
}
357+
347358
set label(value: string) {
348359
this.button.label = value;
349360
this.action.label = value;
@@ -433,32 +444,42 @@ export class ButtonWithDescription implements IButtonWithDescription {
433444
}
434445
}
435446

436-
export class ButtonBar extends Disposable {
447+
export class ButtonBar {
437448

438-
private _buttons: IButton[] = [];
449+
private readonly _buttons: IButton[] = [];
450+
private readonly _buttonStore = new DisposableStore();
439451

440452
constructor(private readonly container: HTMLElement) {
441-
super();
453+
454+
}
455+
456+
dispose(): void {
457+
this._buttonStore.dispose();
442458
}
443459

444460
get buttons(): IButton[] {
445461
return this._buttons;
446462
}
447463

464+
clear(): void {
465+
this._buttonStore.clear();
466+
this._buttons.length = 0;
467+
}
468+
448469
addButton(options: IButtonOptions): IButton {
449-
const button = this._register(new Button(this.container, options));
470+
const button = this._buttonStore.add(new Button(this.container, options));
450471
this.pushButton(button);
451472
return button;
452473
}
453474

454475
addButtonWithDescription(options: IButtonOptions): IButtonWithDescription {
455-
const button = this._register(new ButtonWithDescription(this.container, options));
476+
const button = this._buttonStore.add(new ButtonWithDescription(this.container, options));
456477
this.pushButton(button);
457478
return button;
458479
}
459480

460481
addButtonWithDropdown(options: IButtonWithDropdownOptions): IButton {
461-
const button = this._register(new ButtonWithDropdown(this.container, options));
482+
const button = this._buttonStore.add(new ButtonWithDropdown(this.container, options));
462483
this.pushButton(button);
463484
return button;
464485
}
@@ -467,7 +488,7 @@ export class ButtonBar extends Disposable {
467488
this._buttons.push(button);
468489

469490
const index = this._buttons.length - 1;
470-
this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => {
491+
this._buttonStore.add(addDisposableListener(button.element, EventType.KEY_DOWN, e => {
471492
const event = new StandardKeyboardEvent(e);
472493
let eventHandled = true;
473494

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button';
7+
import { ActionRunner, IAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
8+
import { Emitter, Event } from 'vs/base/common/event';
9+
import { DisposableStore } from 'vs/base/common/lifecycle';
10+
import { ThemeIcon } from 'vs/base/common/themables';
11+
import { localize } from 'vs/nls';
12+
import { MenuId, IMenuService, SubmenuItemAction, MenuItemAction } from 'vs/platform/actions/common/actions';
13+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
14+
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
15+
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
16+
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
17+
18+
export type IButtonConfigProvider = (action: IAction) => {
19+
showIcon?: boolean;
20+
showLabel?: boolean;
21+
} | undefined;
22+
23+
export interface IMenuWorkbenchButtonBarOptions {
24+
telemetrySource?: string;
25+
buttonConfigProvider?: IButtonConfigProvider;
26+
}
27+
28+
export class MenuWorkbenchButtonBar extends ButtonBar {
29+
30+
private readonly _store = new DisposableStore();
31+
32+
private readonly _onDidChangeMenuItems = new Emitter<this>();
33+
readonly onDidChangeMenuItems: Event<this> = this._onDidChangeMenuItems.event;
34+
35+
constructor(
36+
container: HTMLElement,
37+
menuId: MenuId,
38+
options: IMenuWorkbenchButtonBarOptions | undefined,
39+
@IMenuService menuService: IMenuService,
40+
@IContextKeyService contextKeyService: IContextKeyService,
41+
@IContextMenuService contextMenuService: IContextMenuService,
42+
@IKeybindingService keybindingService: IKeybindingService,
43+
@ITelemetryService telemetryService: ITelemetryService,
44+
) {
45+
super(container);
46+
47+
const menu = menuService.createMenu(menuId, contextKeyService);
48+
this._store.add(menu);
49+
50+
const actionRunner = this._store.add(new ActionRunner());
51+
if (options?.telemetrySource) {
52+
actionRunner.onDidRun(e => {
53+
telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>(
54+
'workbenchActionExecuted',
55+
{ id: e.action.id, from: options.telemetrySource! }
56+
);
57+
}, this._store);
58+
}
59+
60+
const conifgProvider: IButtonConfigProvider = options?.buttonConfigProvider ?? (() => ({ showLabel: true }));
61+
62+
const update = () => {
63+
64+
this.clear();
65+
66+
const actions = menu
67+
.getActions({ renderShortTitle: true })
68+
.flatMap(entry => entry[1]);
69+
70+
for (let i = 0; i < actions.length; i++) {
71+
const secondary = i > 0;
72+
const actionOrSubmenu = actions[i];
73+
let action: MenuItemAction | SubmenuItemAction;
74+
let btn: IButton;
75+
76+
if (actionOrSubmenu instanceof SubmenuItemAction && actionOrSubmenu.actions.length > 0) {
77+
const [first, ...rest] = actionOrSubmenu.actions;
78+
action = <MenuItemAction>first;
79+
btn = this.addButtonWithDropdown({
80+
secondary,
81+
actionRunner,
82+
actions: rest,
83+
contextMenuProvider: contextMenuService,
84+
});
85+
} else {
86+
action = actionOrSubmenu;
87+
btn = this.addButton({ secondary });
88+
}
89+
90+
btn.enabled = action.enabled;
91+
if (conifgProvider(action)?.showLabel ?? true) {
92+
btn.label = action.label;
93+
} else {
94+
btn.element.classList.add('monaco-text-button');
95+
}
96+
if (conifgProvider(action)?.showIcon && ThemeIcon.isThemeIcon(action.item.icon)) {
97+
btn.icon = action.item.icon;
98+
}
99+
const kb = keybindingService.lookupKeybinding(action.id);
100+
if (kb) {
101+
btn.element.title = localize('labelWithKeybinding', "{0} ({1})", action.label, kb.getLabel());
102+
} else {
103+
btn.element.title = action.label;
104+
105+
}
106+
btn.onDidClick(async () => {
107+
actionRunner.run(action);
108+
});
109+
}
110+
this._onDidChangeMenuItems.fire(this);
111+
};
112+
this._store.add(menu.onDidChange(update));
113+
update();
114+
}
115+
116+
override dispose() {
117+
this._onDidChangeMenuItems.dispose();
118+
this._store.dispose();
119+
super.dispose();
120+
}
121+
}

src/vs/workbench/contrib/inlineChat/browser/inlineChat.css

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
padding-left: 10px;
135135
padding-right: 4px;
136136
margin-left: auto;
137+
align-self: center;
137138
}
138139

139140
.monaco-editor .inline-chat .markdownMessage {
@@ -183,28 +184,44 @@
183184
color: var(--vscode-editorWarning-foreground);
184185
}
185186

187+
.monaco-editor .inline-chat .status .actions {
188+
display: flex;
189+
}
190+
191+
.monaco-editor .inline-chat .status .actions > .monaco-button,
192+
.monaco-editor .inline-chat .status .actions > .monaco-button-dropdown {
193+
margin-right: 6px;
194+
}
195+
196+
.monaco-editor .inline-chat .status .actions > .monaco-button-dropdown > .monaco-dropdown-button {
197+
display: flex;
198+
align-items: center;
199+
padding: 0 4px;
200+
}
201+
202+
.monaco-editor .inline-chat .status .actions > .monaco-button.codicon {
203+
display: flex;
204+
}
205+
206+
.monaco-editor .inline-chat .status .actions > .monaco-button.codicon::before {
207+
align-self: center;
208+
}
209+
210+
.monaco-editor .inline-chat .status .actions .monaco-text-button {
211+
padding: 2px 4px
212+
}
213+
186214
.monaco-editor .inline-chat .status .monaco-toolbar .action-item {
187215
padding: 0 2px;
188216
}
189217

218+
/* TODO@jrieken not needed? */
190219
.monaco-editor .inline-chat .status .monaco-toolbar .action-label.checked {
191220
color: var(--vscode-inputOption-activeForeground);
192221
background-color: var(--vscode-inputOption-activeBackground);
193222
outline: 1px solid var(--vscode-inputOption-activeBorder);
194223
}
195224

196-
.monaco-editor .inline-chat .status .monaco-toolbar .action-item.button-item .action-label {
197-
color: var(--vscode-button-foreground);
198-
background-color: var(--vscode-button-background);
199-
border-radius: 2px;
200-
padding: 4px 6px;
201-
line-height: 18px;
202-
}
203-
204-
.monaco-editor .inline-chat .status .monaco-toolbar .action-item.button-item .action-label>.codicon {
205-
color: unset;
206-
font-size: 14px;
207-
}
208225

209226
.monaco-editor .inline-chat .status .monaco-toolbar .action-item.button-item .action-label:is(:hover, :focus) {
210227
background-color: var(--vscode-button-hoverBackground);

src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { EditorAction2 } from 'vs/editor/browser/editorExtensions';
1010
import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
1111
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
1212
import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
13-
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_SHOWING_DIFF, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_ACCEPT_CHANGES } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
13+
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_SHOWING_DIFF, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
1414
import { localize } from 'vs/nls';
1515
import { IAction2Options, MenuRegistry } from 'vs/platform/actions/common/actions';
1616
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
@@ -163,14 +163,15 @@ export class ReRunRequestAction extends AbstractInlineChatAction {
163163

164164
constructor() {
165165
super({
166-
id: 'inlineChat.regenerate',
166+
id: ACTION_REGENERATE_RESPONSE,
167167
title: localize('rerun', 'Regenerate Response'),
168+
shortTitle: localize('rerunShort', 'Regenerate'),
168169
icon: Codicon.refresh,
169170
precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_EMPTY.negate(), CTX_INLINE_CHAT_LAST_RESPONSE_TYPE),
170171
menu: {
171172
id: MENU_INLINE_CHAT_WIDGET_STATUS,
172-
group: '0_main',
173-
order: 1,
173+
group: '2_feedback',
174+
order: 3,
174175
}
175176
});
176177
}

0 commit comments

Comments
 (0)