Skip to content

Commit a2565d4

Browse files
committed
In Agent mode: Auto scroll to prompts like 'Continue'.
User must be made aware some action is needed from them to continue the work by agent. Usually this happens when "Too many requests" or "Command line run prompt" etc. see https://github.com/microsoft/copilot-eclipse-feedback/issues/184
1 parent 5b2397d commit a2565d4

8 files changed

Lines changed: 100 additions & 3 deletions

File tree

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private Constants() {
5151
public static final String CHAT_CHANNEL = "chatProgress";
5252
public static final String AUTO_SHOW_WHAT_IS_NEW = "autoShowWhatsNew";
5353
public static final String AUTO_BREAKPOINT_RESPONSE = "autoBreakpointResponse";
54+
public static final String AUTO_SCROLL_TO_NEW_DIALOG = "autoScrollToNewDialog";
5455
public static final String GITHUB_JOBS_VIEW_ID = "com.microsoft.copilot.eclipse.ui.jobs.JobsView";
5556
public static final String SUPPRESS_TERMINAL_DEPENDENCY_DIALOG = "suppressTerminalDependencyDialog";
5657

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/BaseTurnWidget.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.eclipse.ui.PlatformUI;
2424
import org.osgi.service.event.EventHandler;
2525

26+
import com.microsoft.copilot.eclipse.core.Constants;
2627
import com.microsoft.copilot.eclipse.core.CopilotCore;
2728
import com.microsoft.copilot.eclipse.core.chat.ConfirmationContent;
2829
import com.microsoft.copilot.eclipse.core.events.CopilotEventConstants;
@@ -36,6 +37,7 @@
3637
import com.microsoft.copilot.eclipse.core.persistence.CopilotTurnData.EditAgentRoundData;
3738
import com.microsoft.copilot.eclipse.core.persistence.CopilotTurnData.ReplyData;
3839
import com.microsoft.copilot.eclipse.core.persistence.CopilotTurnData.ToolCallData;
40+
import com.microsoft.copilot.eclipse.ui.CopilotUi;
3941
import com.microsoft.copilot.eclipse.ui.chat.services.AvatarService;
4042
import com.microsoft.copilot.eclipse.ui.chat.services.ChatServiceManager;
4143
import com.microsoft.copilot.eclipse.ui.utils.SwtUtils;
@@ -627,9 +629,24 @@ protected void createWarnDialog(String message, int code, String modelProviderNa
627629
&& quotaStatus.premiumInteractions().overagePermitted();
628630
canUpgradePlan = quotaStatus.canUpgradePlan();
629631
}
630-
new WarnWidget(this, SWT.NONE, displayMessage, planForActions, overageEnabled, canUpgradePlan);
632+
WarnWidget warnWidget = new WarnWidget(this, SWT.NONE, displayMessage, planForActions, overageEnabled, canUpgradePlan);
631633
ensureFooterAtBottom();
632634
requestLayout();
635+
// Ensure the chat content viewer scrolls to show the newly created warning banner. Walk up the composite hierarchy
636+
// to find a ChatContentViewer and request scrolling. Use async exec because layout needs to complete first.
637+
SwtUtils.invokeOnDisplayThreadAsync(() -> {
638+
if (!isAutoScrollPromptsEnabled()) {
639+
return;
640+
}
641+
ChatContentViewer viewer = SwtUtils.findParentOfType(this.getParent(), ChatContentViewer.class);
642+
if (viewer != null) {
643+
viewer.refreshScrollerLayout();
644+
if (warnWidget != null && !warnWidget.isDisposed()) {
645+
viewer.showControl(warnWidget);
646+
}
647+
}
648+
649+
}, this.getParent());
633650
}
634651

635652
/**
@@ -666,6 +683,23 @@ public CompletableFuture<LanguageModelToolConfirmationResult> requestToolExecuti
666683

667684
this.getParent().requestLayout();
668685

686+
// Ensure the chat content viewer scrolls to show the newly created confirmation
687+
// dialog. Walk up the composite hierarchy to find a ChatContentViewer
688+
// and request scrolling. Use async exec because layout needs to complete first.
689+
SwtUtils.invokeOnDisplayThreadAsync(() -> {
690+
if (!isAutoScrollPromptsEnabled()) {
691+
return;
692+
}
693+
ChatContentViewer viewer = SwtUtils.findParentOfType(this.getParent(), ChatContentViewer.class);
694+
if (viewer != null) {
695+
viewer.refreshScrollerLayout();
696+
if (this.confirmDialog != null && !this.confirmDialog.isDisposed()) {
697+
viewer.showControl(this.confirmDialog);
698+
}
699+
}
700+
701+
}, this.getParent());
702+
669703
return toolConfirmationFuture;
670704
}
671705

@@ -710,4 +744,8 @@ public void dispose() {
710744
this.cancelMsgEventHandler = null;
711745
}
712746
}
747+
748+
public static boolean isAutoScrollPromptsEnabled() {
749+
return CopilotUi.getPlugin().getPreferenceStore().getBoolean(Constants.AUTO_SCROLL_TO_NEW_DIALOG);
750+
}
713751
}

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/ChatContentViewer.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,9 @@ public BaseTurnWidget getTurnWidget(String turnId) {
421421
private void renderWarnMessageWithUpgradePlanButton(String errorMessage, int code, String modelProviderName) {
422422
latestTurnWidget.createWarnDialog(errorMessage, code, modelProviderName);
423423
refreshScrollerLayout();
424-
scrollToLatestUserTurn();
424+
if (!BaseTurnWidget.isAutoScrollPromptsEnabled()) {
425+
scrollToLatestUserTurn();
426+
}
425427
}
426428

427429
/**
@@ -433,7 +435,19 @@ public void renderErrorMessage(String errorMessage) {
433435
}
434436
this.errorWidget = new ErrorWidget(cmpContent, SWT.BOTTOM, errorMessage);
435437
refreshScrollerLayout();
436-
scrollToLatestUserTurn();
438+
// Ensure the chat content viewer scrolls to show the newly created error banner.
439+
SwtUtils.invokeOnDisplayThreadAsync(() -> {
440+
if (!BaseTurnWidget.isAutoScrollPromptsEnabled()) {
441+
return;
442+
}
443+
if (this.errorWidget != null && !this.errorWidget.isDisposed()) {
444+
this.showControl(this.errorWidget);
445+
}
446+
}, this.getParent());
447+
448+
if (!BaseTurnWidget.isAutoScrollPromptsEnabled()) {
449+
scrollToLatestUserTurn();
450+
}
437451
}
438452

439453
/**

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/preferences/CopilotPreferenceInitializer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public void initializeDefaultPreferences() {
3939
pref.setDefault(Constants.CUSTOM_INSTRUCTIONS_CHAT_LOAD_SCOPE,
4040
CustomInstructionsChatLoadScope.DEFAULT_VALUE.getValue());
4141
pref.setDefault(Constants.AUTO_BREAKPOINT_RESPONSE, false);
42+
pref.setDefault(Constants.AUTO_SCROLL_TO_NEW_DIALOG, true);
4243
pref.setDefault(Constants.MCP, """
4344
{
4445
"servers": {

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/preferences/GeneralPreferencesPage.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ public void createFieldEditors() {
101101
showWhatsNewField.getDescriptionControl(whatsNewComposite)
102102
.setToolTipText(Messages.preferences_page_enable_whats_new_tooltip);
103103
addField(showWhatsNewField);
104+
105+
// Chat group
106+
Group grpChat = new Group(parent, SWT.NONE);
107+
grpChat.setLayout(gl);
108+
gdf.applyTo(grpChat);
109+
grpChat.setText(Messages.preferences_page_auto_scroll_group);
110+
111+
Composite ctnAutoScroll = new Composite(grpChat, SWT.NONE);
112+
ctnAutoScroll.setLayout(gl);
113+
BooleanFieldEditor bfeAutoScroll = new BooleanFieldEditor(Constants.AUTO_SCROLL_TO_NEW_DIALOG,
114+
Messages.preferences_page_auto_scroll_to_new_dialog, ctnAutoScroll);
115+
bfeAutoScroll.getDescriptionControl(ctnAutoScroll)
116+
.setToolTipText(Messages.preferences_page_auto_scroll_to_new_dialog_tooltip);
117+
addField(bfeAutoScroll);
104118
}
105119

106120
@Override

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/preferences/Messages.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public class Messages extends NLS {
5454
public static String preferences_page_whats_new_settings;
5555
public static String preferences_page_enable_whats_new;
5656
public static String preferences_page_enable_whats_new_tooltip;
57+
58+
public static String preferences_page_auto_scroll_group;
59+
public static String preferences_page_auto_scroll_to_new_dialog;
60+
public static String preferences_page_auto_scroll_to_new_dialog_tooltip;
5761
public static String preferences_page_github_enterprise;
5862
public static String preferences_page_watched_files;
5963
public static String preferences_page_watched_files_note_content;

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/preferences/messages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ preferences_page_enable_strict_ssl= Enable Strict SSL
4343
preferences_page_whats_new_settings= What's New
4444
preferences_page_enable_whats_new= Always display the 'What's New' dialog after GitHub Copilot updates.
4545
preferences_page_enable_whats_new_tooltip= Enable this to automatically display the "What's New" page whenever GitHub Copilot is updated.
46+
preferences_page_auto_scroll_group= Chat
47+
preferences_page_auto_scroll_to_new_dialog= Automatically scroll to new confirmation dialogs/prompts in Chat View.
48+
preferences_page_auto_scroll_to_new_dialog_tooltip= When enabled, the chat view will automatically scroll to show any confirmation dialogs or prompts.
4649
preferences_page_github_enterprise= GitHub Enterprise Authentication Endpoint
4750
preferences_page_mcp= Server Configurations
4851
preferences_page_proxy_config_link= <a>Configure Proxy</a>

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/utils/SwtUtils.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,30 @@ private SwtUtils() {
4848
}
4949

5050
private static final String INLINE_ANNOTATION_COLOR_KEY = "org.eclipse.ui.editors.inlineAnnotationColor";
51+
5152
private static final int DEFAULT_GHOST_TEXT_SCALE = 128;
5253

54+
/**
55+
* Walks up the parent chain of the given control and returns the first ancestor that is an instance of the specified
56+
* type, or {@code null} if none is found.
57+
*
58+
* @param <T> the target type
59+
* @param control the starting control (may be {@code null})
60+
* @param type the class to search for
61+
* @return the first matching ancestor, or {@code null}
62+
*/
63+
@Nullable
64+
public static <T> T findParentOfType(Control control, Class<T> type) {
65+
Control current = control;
66+
while (current != null) {
67+
if (type.isInstance(current)) {
68+
return type.cast(current);
69+
}
70+
current = current.getParent();
71+
}
72+
return null;
73+
}
74+
5375
/**
5476
* Invokes the given runnable on the display thread.
5577
*/

0 commit comments

Comments
 (0)