Path: blob/main/src/vs/workbench/contrib/chat/browser/actions/chatPromptNavigationActions.ts
4780 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';6import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js';7import { localize2 } from '../../../../../nls.js';8import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js';9import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';10import { CHAT_CATEGORY } from './chatActions.js';11import { IChatWidgetService } from '../chat.js';12import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';13import { IChatRequestViewModel, isRequestVM, isResponseVM } from '../../common/model/chatViewModel.js';1415export function registerChatPromptNavigationActions() {16registerAction2(class NextUserPromptAction extends Action2 {17constructor() {18super({19id: 'workbench.action.chat.nextUserPrompt',20title: localize2('interactive.nextUserPrompt.label', "Next User Prompt"),21keybinding: {22primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.DownArrow,23weight: KeybindingWeight.WorkbenchContrib,24when: ChatContextKeys.inChatSession,25},26precondition: ChatContextKeys.enabled,27f1: true,28category: CHAT_CATEGORY,29});30}3132run(accessor: ServicesAccessor, ...args: unknown[]) {33navigateUserPrompts(accessor, false);34}35});3637registerAction2(class PreviousUserPromptAction extends Action2 {38constructor() {39super({40id: 'workbench.action.chat.previousUserPrompt',41title: localize2('interactive.previousUserPrompt.label', "Previous User Prompt"),42keybinding: {43primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.UpArrow,44weight: KeybindingWeight.WorkbenchContrib,45when: ChatContextKeys.inChatSession,46},47precondition: ChatContextKeys.enabled,48f1: true,49category: CHAT_CATEGORY,50});51}5253run(accessor: ServicesAccessor, ...args: unknown[]) {54navigateUserPrompts(accessor, true);55}56});57}5859function navigateUserPrompts(accessor: ServicesAccessor, reverse: boolean) {60const chatWidgetService = accessor.get(IChatWidgetService);61const widget = chatWidgetService.lastFocusedWidget;62if (!widget) {63return;64}6566const items = widget.viewModel?.getItems();67if (!items || items.length === 0) {68return;69}7071// Get all user prompts (requests) in the conversation72const userPrompts = items.filter((item): item is IChatRequestViewModel => isRequestVM(item));73if (userPrompts.length === 0) {74return;75}7677// Find the currently focused item78const focused = widget.getFocus();79let currentIndex = -1;8081if (focused) {82if (isRequestVM(focused)) {83// If a request is focused, find its index in the user prompts array84currentIndex = userPrompts.findIndex(prompt => prompt.id === focused.id);85} else if (isResponseVM(focused)) {86// If a response is focused, find the associated request's index87// Response view models have a requestId property88currentIndex = userPrompts.findIndex(prompt => prompt.id === focused.requestId);89}90}9192// Calculate next index93let nextIndex: number;94if (currentIndex === -1) {95// No current focus, go to first or last prompt based on direction96nextIndex = reverse ? userPrompts.length - 1 : 0;97} else {98// Navigate to next/previous prompt99nextIndex = reverse ? currentIndex - 1 : currentIndex + 1;100101// Clamp instead of wrap and stay at boundaries when trying to navigate past ends102if (nextIndex < 0) {103nextIndex = 0; // already at first, do not move further104} else if (nextIndex >= userPrompts.length) {105nextIndex = userPrompts.length - 1; // already at last, do not move further106}107108// avoid re-focusing if we didn't actually move109if (nextIndex === currentIndex) {110return; // no change in focus111}112}113114// Focus and reveal the selected user prompt115const targetPrompt = userPrompts[nextIndex];116if (targetPrompt) {117widget.focus(targetPrompt);118widget.reveal(targetPrompt);119}120}121122123