Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/actions/chatPromptNavigationActions.ts
4780 views
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 { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';
7
import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js';
8
import { localize2 } from '../../../../../nls.js';
9
import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js';
10
import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';
11
import { CHAT_CATEGORY } from './chatActions.js';
12
import { IChatWidgetService } from '../chat.js';
13
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
14
import { IChatRequestViewModel, isRequestVM, isResponseVM } from '../../common/model/chatViewModel.js';
15
16
export function registerChatPromptNavigationActions() {
17
registerAction2(class NextUserPromptAction extends Action2 {
18
constructor() {
19
super({
20
id: 'workbench.action.chat.nextUserPrompt',
21
title: localize2('interactive.nextUserPrompt.label', "Next User Prompt"),
22
keybinding: {
23
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.DownArrow,
24
weight: KeybindingWeight.WorkbenchContrib,
25
when: ChatContextKeys.inChatSession,
26
},
27
precondition: ChatContextKeys.enabled,
28
f1: true,
29
category: CHAT_CATEGORY,
30
});
31
}
32
33
run(accessor: ServicesAccessor, ...args: unknown[]) {
34
navigateUserPrompts(accessor, false);
35
}
36
});
37
38
registerAction2(class PreviousUserPromptAction extends Action2 {
39
constructor() {
40
super({
41
id: 'workbench.action.chat.previousUserPrompt',
42
title: localize2('interactive.previousUserPrompt.label', "Previous User Prompt"),
43
keybinding: {
44
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.UpArrow,
45
weight: KeybindingWeight.WorkbenchContrib,
46
when: ChatContextKeys.inChatSession,
47
},
48
precondition: ChatContextKeys.enabled,
49
f1: true,
50
category: CHAT_CATEGORY,
51
});
52
}
53
54
run(accessor: ServicesAccessor, ...args: unknown[]) {
55
navigateUserPrompts(accessor, true);
56
}
57
});
58
}
59
60
function navigateUserPrompts(accessor: ServicesAccessor, reverse: boolean) {
61
const chatWidgetService = accessor.get(IChatWidgetService);
62
const widget = chatWidgetService.lastFocusedWidget;
63
if (!widget) {
64
return;
65
}
66
67
const items = widget.viewModel?.getItems();
68
if (!items || items.length === 0) {
69
return;
70
}
71
72
// Get all user prompts (requests) in the conversation
73
const userPrompts = items.filter((item): item is IChatRequestViewModel => isRequestVM(item));
74
if (userPrompts.length === 0) {
75
return;
76
}
77
78
// Find the currently focused item
79
const focused = widget.getFocus();
80
let currentIndex = -1;
81
82
if (focused) {
83
if (isRequestVM(focused)) {
84
// If a request is focused, find its index in the user prompts array
85
currentIndex = userPrompts.findIndex(prompt => prompt.id === focused.id);
86
} else if (isResponseVM(focused)) {
87
// If a response is focused, find the associated request's index
88
// Response view models have a requestId property
89
currentIndex = userPrompts.findIndex(prompt => prompt.id === focused.requestId);
90
}
91
}
92
93
// Calculate next index
94
let nextIndex: number;
95
if (currentIndex === -1) {
96
// No current focus, go to first or last prompt based on direction
97
nextIndex = reverse ? userPrompts.length - 1 : 0;
98
} else {
99
// Navigate to next/previous prompt
100
nextIndex = reverse ? currentIndex - 1 : currentIndex + 1;
101
102
// Clamp instead of wrap and stay at boundaries when trying to navigate past ends
103
if (nextIndex < 0) {
104
nextIndex = 0; // already at first, do not move further
105
} else if (nextIndex >= userPrompts.length) {
106
nextIndex = userPrompts.length - 1; // already at last, do not move further
107
}
108
109
// avoid re-focusing if we didn't actually move
110
if (nextIndex === currentIndex) {
111
return; // no change in focus
112
}
113
}
114
115
// Focus and reveal the selected user prompt
116
const targetPrompt = userPrompts[nextIndex];
117
if (targetPrompt) {
118
widget.focus(targetPrompt);
119
widget.reveal(targetPrompt);
120
}
121
}
122
123