Skip to content

Commit cd9a8f3

Browse files
authored
Merge pull request microsoft#296555 from microsoft/benibenj/fresh-chinchilla
sessions feedback improvements
2 parents a42bcd7 + 367b0f3 commit cd9a8f3

21 files changed

Lines changed: 1484 additions & 427 deletions

src/vs/base/browser/ui/tree/abstractTree.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ export enum RenderIndentGuides {
310310

311311
interface ITreeRendererOptions<T> {
312312
readonly indent?: number;
313+
readonly defaultIndent?: number;
313314
readonly renderIndentGuides?: RenderIndentGuides;
314315
// TODO@joao replace this with collapsible: boolean | 'ondemand'
315316
readonly hideTwistiesOfChildlessElements?: boolean;
@@ -347,6 +348,7 @@ export class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListR
347348
private renderedElements = new Map<T, ITreeNode<T, TFilterData>>();
348349
private renderedNodes = new Map<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>>();
349350
private indent: number = TreeRenderer.DefaultIndent;
351+
private defaultIndent: number = TreeRenderer.DefaultIndent;
350352
private hideTwistiesOfChildlessElements: boolean = false;
351353
private twistieAdditionalCssClass?: (element: T) => string | undefined;
352354

@@ -372,14 +374,19 @@ export class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListR
372374
}
373375

374376
updateOptions(options: ITreeRendererOptions<T> = {}): void {
375-
if (typeof options.indent !== 'undefined') {
376-
const indent = clamp(options.indent, 0, 40);
377+
if (typeof options.defaultIndent !== 'undefined') {
378+
this.defaultIndent = options.defaultIndent;
379+
}
380+
381+
if (typeof options.indent !== 'undefined' || typeof options.defaultIndent !== 'undefined') {
382+
const indent = typeof options.indent !== 'undefined' ? clamp(options.indent, 0, 40) : this.indent;
383+
const needsRerender = indent !== this.indent || typeof options.defaultIndent !== 'undefined';
377384

378-
if (indent !== this.indent) {
385+
if (needsRerender) {
379386
this.indent = indent;
380387

381388
for (const [node, templateData] of this.renderedNodes) {
382-
templateData.indentSize = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent;
389+
templateData.indentSize = this.defaultIndent + (node.depth - 1) * this.indent;
383390
this.renderTreeElement(node, templateData);
384391
}
385392
}
@@ -427,7 +434,7 @@ export class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListR
427434
}
428435

429436
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, details?: IListElementRenderDetails): void {
430-
templateData.indentSize = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent;
437+
templateData.indentSize = this.defaultIndent + (node.depth - 1) * this.indent;
431438

432439
this.renderedNodes.set(node, templateData);
433440
this.renderedElements.set(node.element, node);
@@ -2192,6 +2199,7 @@ function asTreeContextMenuEvent<T, TFilterData = void>(event: IListContextMenuEv
21922199
}
21932200

21942201
export interface IAbstractTreeOptionsUpdate<T> extends ITreeRendererOptions<T> {
2202+
readonly defaultIndent?: number; // Only recommended for compact layouts. Leave unchanged otherwise
21952203
readonly multipleSelectionSupport?: boolean;
21962204
readonly typeNavigationEnabled?: boolean;
21972205
readonly typeNavigationMode?: TypeNavigationMode;

src/vs/sessions/browser/workbench.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,9 +1065,15 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService {
10651065
!hidden,
10661066
);
10671067

1068+
// If sidebar becomes hidden, also hide the current active pane composite
1069+
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) {
1070+
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Sidebar);
1071+
}
1072+
10681073
// If sidebar becomes visible, show last active Viewlet or default viewlet
10691074
if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) {
1070-
const viewletToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Sidebar);
1075+
const viewletToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Sidebar) ??
1076+
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id;
10711077
if (viewletToOpen) {
10721078
this.paneCompositeService.openPaneComposite(viewletToOpen, ViewContainerLocation.Sidebar);
10731079
}
@@ -1088,9 +1094,15 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService {
10881094
!hidden,
10891095
);
10901096

1091-
// If auxiliary bar becomes visible, show last active pane composite
1097+
// If auxiliary bar becomes hidden, also hide the current active pane composite
1098+
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)) {
1099+
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.AuxiliaryBar);
1100+
}
1101+
1102+
// If auxiliary bar becomes visible, show last active pane composite or default
10921103
if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)) {
1093-
const paneCompositeToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.AuxiliaryBar);
1104+
const paneCompositeToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.AuxiliaryBar) ??
1105+
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.AuxiliaryBar)?.id;
10941106
if (paneCompositeToOpen) {
10951107
this.paneCompositeService.openPaneComposite(paneCompositeToOpen, ViewContainerLocation.AuxiliaryBar);
10961108
}
@@ -1125,9 +1137,15 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService {
11251137
!hidden,
11261138
);
11271139

1128-
// If panel becomes visible, show last active panel
1140+
// If panel becomes hidden, also hide the current active pane composite
1141+
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)) {
1142+
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Panel);
1143+
}
1144+
1145+
// If panel becomes visible, show last active panel or default
11291146
if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)) {
1130-
const panelToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Panel);
1147+
const panelToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Panel) ??
1148+
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Panel)?.id;
11311149
if (panelToOpen) {
11321150
this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.Panel);
11331151
}

src/vs/sessions/contrib/agentFeedback/browser/agentFeedback.contribution.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import './agentFeedbackEditorInputContribution.js';
7+
import './agentFeedbackEditorWidgetContribution.js';
78
import './agentFeedbackLineDecorationContribution.js';
89
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
10+
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
911
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
1012
import { AgentFeedbackService, IAgentFeedbackService } from './agentFeedbackService.js';
1113
import { AgentFeedbackAttachmentContribution } from './agentFeedbackAttachment.js';
@@ -25,8 +27,11 @@ registerSingleton(IAgentFeedbackService, AgentFeedbackService, InstantiationType
2527
// Register the custom attachment widget for agentFeedback attachments
2628
class AgentFeedbackAttachmentWidgetContribution {
2729
static readonly ID = 'workbench.contrib.agentFeedbackAttachmentWidgetFactory';
28-
constructor(@IChatAttachmentWidgetRegistry registry: IChatAttachmentWidgetRegistry) {
29-
registry.registerFactory('agentFeedback', (instantiationService, attachment, options, container) => {
30+
constructor(
31+
@IChatAttachmentWidgetRegistry registry: IChatAttachmentWidgetRegistry,
32+
@IInstantiationService instantiationService: IInstantiationService,
33+
) {
34+
registry.registerFactory('agentFeedback', (attachment, options, container) => {
3035
return instantiationService.createInstance(AgentFeedbackAttachmentWidget, attachment as IAgentFeedbackVariableEntry, options, container);
3136
});
3237
}

src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackAttachmentWidget.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ export class AgentFeedbackAttachmentWidget extends Disposable {
5050
const label = dom.$('span.chat-attached-context-custom-text', {}, this._attachment.name);
5151
this.element.appendChild(label);
5252

53+
const deletionCurrentlyNotSupported = true;
54+
5355
// Clear button
54-
if (options.supportsDeletion) {
56+
if (options.supportsDeletion && !deletionCurrentlyNotSupported) {
5557
const clearBtn = dom.append(this.element, dom.$('.chat-attached-context-clear-button'));
5658
const clearIcon = dom.$('span');
5759
clearIcon.classList.add(...ThemeIcon.asClassNameArray(Codicon.close));

src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackEditorActions.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ class NavigateFeedbackAction extends AgentFeedbackEditorAction {
131131
editorService.openEditor({
132132
resource: feedback.resourceUri,
133133
options: {
134-
selection: feedback.range,
135134
preserveFocus: false,
136135
revealIfVisible: true,
137136
}

src/vs/sessions/contrib/agentFeedback/browser/agentFeedbackEditorInputContribution.ts

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ import { localize } from '../../../../nls.js';
2222
class AgentFeedbackInputWidget implements IOverlayWidget {
2323

2424
private static readonly _ID = 'agentFeedback.inputWidget';
25+
private static readonly _MIN_WIDTH = 150;
26+
private static readonly _MAX_WIDTH = 400;
2527

2628
readonly allowEditorOverflow = false;
2729

2830
private readonly _domNode: HTMLElement;
29-
private readonly _inputElement: HTMLInputElement;
31+
private readonly _inputElement: HTMLTextAreaElement;
32+
private readonly _measureElement: HTMLElement;
3033
private _position: IOverlayWidgetPosition | null = null;
34+
private _lineHeight = 0;
3135

3236
constructor(
3337
private readonly _editor: ICodeEditor,
@@ -36,12 +40,19 @@ class AgentFeedbackInputWidget implements IOverlayWidget {
3640
this._domNode.classList.add('agent-feedback-input-widget');
3741
this._domNode.style.display = 'none';
3842

39-
this._inputElement = document.createElement('input');
40-
this._inputElement.type = 'text';
43+
this._inputElement = document.createElement('textarea');
44+
this._inputElement.rows = 1;
4145
this._inputElement.placeholder = localize('agentFeedback.addFeedback', "Add Feedback");
4246
this._domNode.appendChild(this._inputElement);
4347

48+
// Hidden element used to measure text width for auto-growing
49+
this._measureElement = document.createElement('span');
50+
this._measureElement.classList.add('agent-feedback-input-measure');
51+
this._domNode.appendChild(this._measureElement);
52+
4453
this._editor.applyFontInfo(this._inputElement);
54+
this._editor.applyFontInfo(this._measureElement);
55+
this._lineHeight = this._editor.getOption(EditorOption.lineHeight);
4556
}
4657

4758
getId(): string {
@@ -56,7 +67,7 @@ class AgentFeedbackInputWidget implements IOverlayWidget {
5667
return this._position;
5768
}
5869

59-
get inputElement(): HTMLInputElement {
70+
get inputElement(): HTMLTextAreaElement {
6071
return this._inputElement;
6172
}
6273

@@ -75,6 +86,28 @@ class AgentFeedbackInputWidget implements IOverlayWidget {
7586

7687
clearInput(): void {
7788
this._inputElement.value = '';
89+
this._autoSize();
90+
}
91+
92+
autoSize(): void {
93+
this._autoSize();
94+
}
95+
96+
private _autoSize(): void {
97+
const text = this._inputElement.value || this._inputElement.placeholder;
98+
99+
// Measure the text width using the hidden span
100+
this._measureElement.textContent = text;
101+
const textWidth = this._measureElement.scrollWidth;
102+
103+
// Clamp width between min and max
104+
const width = Math.max(AgentFeedbackInputWidget._MIN_WIDTH, Math.min(textWidth + 10, AgentFeedbackInputWidget._MAX_WIDTH));
105+
this._inputElement.style.width = `${width}px`;
106+
107+
// Reset height to auto then expand to fit all content, with a minimum of 1 line
108+
this._inputElement.style.height = 'auto';
109+
const newHeight = Math.max(this._inputElement.scrollHeight, this._lineHeight + 4 /* padding */);
110+
this._inputElement.style.height = `${newHeight}px`;
78111
}
79112
}
80113

@@ -110,8 +143,11 @@ export class AgentFeedbackEditorInputContribution extends Disposable implements
110143
this._mouseDown = true;
111144
this._hide();
112145
}));
113-
this._store.add(this._editor.onMouseUp(() => {
146+
this._store.add(this._editor.onMouseUp((e) => {
114147
this._mouseDown = false;
148+
if (this._isWidgetTarget(e.event.target)) {
149+
return;
150+
}
115151
this._onSelectionChanged();
116152
}));
117153
this._store.add(this._editor.onDidBlurEditorWidget(() => {
@@ -262,6 +298,12 @@ export class AgentFeedbackEditorInputContribution extends Disposable implements
262298
e.stopPropagation();
263299
}));
264300

301+
// Auto-size the textarea as the user types
302+
this._widgetListeners.add(addStandardDisposableListener(widget.inputElement, 'input', () => {
303+
widget.autoSize();
304+
this._updatePosition();
305+
}));
306+
265307
// Hide when input loses focus to something outside both editor and widget
266308
this._widgetListeners.add(addStandardDisposableListener(widget.inputElement, 'blur', () => {
267309
const win = getWindow(widget.inputElement);

0 commit comments

Comments
 (0)