Открыть боковую панель
code
vscode
Коммиты
10cf37de
Коммит
10cf37de
создал
Апр 26, 2025
по автору
Rob Lourens
Просмотр файлов
Add per-chatMode actions and keybindings
владелец
29d5a6d4
Изменения
8
Скрыть пробелы
Построчно
Рядом
src/vs/platform/actionWidget/browser/actionWidgetDropdown.ts
Просмотр файла @
10cf37de
...
...
@@ -10,6 +10,7 @@ import { ActionListItemKind, IActionListDelegate, IActionListItem } from './acti
import
{
ThemeIcon
}
from
'
../../../base/common/themables.js
'
;
import
{
Codicon
}
from
'
../../../base/common/codicons.js
'
;
import
{
getActiveElement
,
isHTMLElement
}
from
'
../../../base/browser/dom.js
'
;
import
{
IKeybindingService
}
from
'
../../keybinding/common/keybinding.js
'
;
export
interface
IActionWidgetDropdownAction
extends
IAction
{
category
?:
{
label
:
string
;
order
:
number
};
...
...
@@ -40,6 +41,7 @@ export class ActionWidgetDropdown extends BaseDropdown {
container
:
HTMLElement
,
private
readonly
_options
:
IActionWidgetDropdownOptions
,
@
IActionWidgetService
private
readonly
actionWidgetService
:
IActionWidgetService
,
@
IKeybindingService
private
readonly
keybindingService
:
IKeybindingService
,
)
{
super
(
container
,
_options
);
}
...
...
@@ -93,6 +95,7 @@ export class ActionWidgetDropdown extends BaseDropdown {
disabled
:
false
,
hideIcon
:
false
,
label
:
action
.
label
,
keybinding
:
this
.
keybindingService
.
lookupKeybinding
(
action
.
id
)
});
}
}
...
...
src/vs/platform/actions/browser/actionWidgetDropdownActionViewItem.ts
Просмотр файла @
10cf37de
...
...
@@ -40,7 +40,7 @@ export class ActionWidgetDropdownActionViewItem extends BaseActionViewItem {
return
this
.
renderLabel
(
this
.
element
);
};
this
.
actionWidgetDropdown
=
this
.
_register
(
new
ActionWidgetDropdown
(
container
,
{
...
this
.
actionWidgetOptions
,
labelRenderer
},
this
.
_actionWidgetService
));
this
.
actionWidgetDropdown
=
this
.
_register
(
new
ActionWidgetDropdown
(
container
,
{
...
this
.
actionWidgetOptions
,
labelRenderer
},
this
.
_actionWidgetService
,
this
.
_keybindingService
));
this
.
_register
(
this
.
actionWidgetDropdown
.
onDidChangeVisibility
(
visible
=>
{
this
.
element
?.
setAttribute
(
'
aria-expanded
'
,
`
${
visible
}
`
);
}));
...
...
src/vs/platform/actions/common/actions.ts
Просмотр файла @
10cf37de
...
...
@@ -604,7 +604,7 @@ interface IBaseAction2Options extends IAction2CommonOptions {
f1
?:
false
;
}
interface
ICommandPaletteOptions
extends
IAction2CommonOptions
{
export
interface
ICommandPaletteOptions
extends
IAction2CommonOptions
{
/**
* The title of the command that will be displayed in the command palette after the category.
...
...
src/vs/workbench/contrib/chat/browser/actions/chatActions.ts
Просмотр файла @
10cf37de
...
...
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
isAncestorOfActiveElement
}
from
'
../../../../../base/browser/dom.js
'
;
import
{
toAction
,
WorkbenchActionExecutedClassification
,
WorkbenchActionExecutedEvent
}
from
'
../../../../../base/common/actions.js
'
;
import
{
coalesce
}
from
'
../../../../../base/common/arrays.js
'
;
import
{
Codicon
}
from
'
../../../../../base/common/codicons.js
'
;
...
...
@@ -21,7 +22,7 @@ import { SuggestController } from '../../../../../editor/contrib/suggest/browser
import
{
localize
,
localize2
}
from
'
../../../../../nls.js
'
;
import
{
IActionViewItemService
}
from
'
../../../../../platform/actions/browser/actionViewItemService.js
'
;
import
{
DropdownWithPrimaryActionViewItem
}
from
'
../../../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js
'
;
import
{
Action2
,
MenuId
,
MenuItemAction
,
MenuRegistry
,
registerAction2
,
SubmenuItemAction
}
from
'
../../../../../platform/actions/common/actions.js
'
;
import
{
Action2
,
ICommandPaletteOptions
,
MenuId
,
MenuItemAction
,
MenuRegistry
,
registerAction2
,
SubmenuItemAction
}
from
'
../../../../../platform/actions/common/actions.js
'
;
import
{
ICommandService
}
from
'
../../../../../platform/commands/common/commands.js
'
;
import
{
ContextKeyExpr
}
from
'
../../../../../platform/contextkey/common/contextkey.js
'
;
import
{
IsLinuxContext
,
IsWindowsContext
}
from
'
../../../../../platform/contextkey/common/contextkeys.js
'
;
...
...
@@ -50,7 +51,7 @@ import { extractAgentAndCommand } from '../../common/chatParserTypes.js';
import
{
IChatDetail
,
IChatService
}
from
'
../../common/chatService.js
'
;
import
{
IChatRequestViewModel
,
IChatResponseViewModel
,
isRequestVM
}
from
'
../../common/chatViewModel.js
'
;
import
{
IChatWidgetHistoryService
}
from
'
../../common/chatWidgetHistoryService.js
'
;
import
{
Chat
Mode
,
validateChatMode
}
from
'
../../common/constants.js
'
;
import
{
Chat
Configuration
,
ChatMode
,
modeToString
,
validateChatMode
}
from
'
../../common/constants.js
'
;
import
{
CopilotUsageExtensionFeatureId
}
from
'
../../common/languageModelStats.js
'
;
import
{
ILanguageModelToolsService
}
from
'
../../common/languageModelToolsService.js
'
;
import
{
ChatViewId
,
IChatWidget
,
IChatWidgetService
,
showChatView
,
showCopilotView
}
from
'
../chat.js
'
;
...
...
@@ -100,88 +101,140 @@ export interface IChatViewOpenRequestEntry {
const
OPEN_CHAT_QUOTA_EXCEEDED_DIALOG
=
'
workbench.action.chat.openQuotaExceededDialog
'
;
export
function
registerChatActions
()
{
registerAction2
(
class
OpenChatGlobalAction
extends
Action2
{
abstract
class
OpenChatGlobalAction
extends
Action2
{
constructor
(
overrides
:
Pick
<
ICommandPaletteOptions
,
'
keybinding
'
|
'
title
'
|
'
id
'
|
'
menu
'
>
,
private
readonly
mode
?:
ChatMode
)
{
super
({
...
overrides
,
icon
:
Codicon
.
copilot
,
f1
:
true
,
category
:
CHAT_CATEGORY
,
precondition
:
ChatContextKeys
.
Setup
.
hidden
.
negate
(),
});
}
constructor
()
{
super
({
id
:
CHAT_OPEN_ACTION_ID
,
title
:
localize2
(
'
openChat
'
,
"
Open Chat
"
),
icon
:
Codicon
.
copilot
,
f1
:
true
,
category
:
CHAT_CATEGORY
,
precondition
:
ChatContextKeys
.
Setup
.
hidden
.
negate
(),
keybinding
:
{
weight
:
KeybindingWeight
.
WorkbenchContrib
,
primary
:
KeyMod
.
CtrlCmd
|
KeyMod
.
Alt
|
KeyCode
.
KeyI
,
mac
:
{
primary
:
KeyMod
.
CtrlCmd
|
KeyMod
.
WinCtrl
|
KeyCode
.
KeyI
}
},
menu
:
[{
id
:
MenuId
.
ChatTitleBarMenu
,
group
:
'
a_open
'
,
order
:
1
}]
});
}
override
async
run
(
accessor
:
ServicesAccessor
,
opts
?:
string
|
IChatViewOpenOptions
):
Promise
<
void
>
{
opts
=
typeof
opts
===
'
string
'
?
{
query
:
opts
}
:
opts
;
override
async
run
(
accessor
:
ServicesAccessor
,
opts
?:
string
|
IChatViewOpenOptions
):
Promise
<
void
>
{
opts
=
typeof
opts
===
'
string
'
?
{
query
:
opts
}
:
opts
;
const
chatService
=
accessor
.
get
(
IChatService
);
const
widgetService
=
accessor
.
get
(
IChatWidgetService
);
const
toolsService
=
accessor
.
get
(
ILanguageModelToolsService
);
const
viewsService
=
accessor
.
get
(
IViewsService
);
const
hostService
=
accessor
.
get
(
IHostService
);
const
chatService
=
accessor
.
get
(
IChatService
);
const
toolsService
=
accessor
.
get
(
ILanguageModelToolsService
);
const
viewsService
=
accessor
.
get
(
IViewsService
);
const
hostService
=
accessor
.
get
(
IHostService
);
const
chatWidget
=
await
showChatView
(
viewsService
);
if
(
!
chatWidget
)
{
return
;
let
chatWidget
=
widgetService
.
lastFocusedWidget
;
// When this was invoked to switch to a mode via keybinding, and some chat widget is focused, use that one.
// Otherwise, open the view.
if
(
!
this
.
mode
||
!
chatWidget
||
!
isAncestorOfActiveElement
(
chatWidget
.
domNode
))
{
chatWidget
=
await
showChatView
(
viewsService
);
}
if
(
!
chatWidget
)
{
return
;
}
const
mode
=
opts
?.
mode
??
this
.
mode
;
if
(
mode
&&
validateChatMode
(
mode
))
{
chatWidget
.
input
.
setChatMode
(
mode
);
}
if
(
opts
?.
previousRequests
?.
length
&&
chatWidget
.
viewModel
)
{
for
(
const
{
request
,
response
}
of
opts
.
previousRequests
)
{
chatService
.
addCompleteRequest
(
chatWidget
.
viewModel
.
sessionId
,
request
,
undefined
,
0
,
{
message
:
response
});
}
if
(
opts
?.
mode
&&
validateChatMode
(
opts
.
mode
))
{
chatWidget
.
input
.
setChatMode
(
opts
.
mode
);
}
if
(
opts
?.
attachScreenshot
)
{
const
screenshot
=
await
hostService
.
getScreenshot
();
if
(
screenshot
)
{
chatWidget
.
attachmentModel
.
addContext
(
convertBufferToScreenshotVariable
(
screenshot
));
}
if
(
opts
?.
previousRequests
?.
length
&&
chatWidget
.
viewModel
)
{
for
(
const
{
request
,
response
}
of
opts
.
previousRequests
)
{
chatService
.
addCompleteRequest
(
chatWidget
.
viewModel
.
sessionId
,
request
,
undefined
,
0
,
{
message
:
response
});
}
}
if
(
opts
?.
query
)
{
if
(
opts
.
query
.
startsWith
(
'
@
'
)
&&
(
chatWidget
.
input
.
currentMode
===
ChatMode
.
Agent
||
chatService
.
edits2Enabled
))
{
chatWidget
.
input
.
setChatMode
(
ChatMode
.
Ask
);
}
if
(
opts
?.
attachScreenshot
)
{
c
onst
screenshot
=
await
hostService
.
getScreenshot
(
);
if
(
screenshot
)
{
chatWidget
.
attachmentModel
.
addContext
(
convertBufferToScreenshotVariable
(
screenshot
)
);
}
if
(
opts
.
isPartialQuery
)
{
c
hatWidget
.
setInput
(
opts
.
query
);
}
else
{
await
chatWidget
.
waitForReady
(
);
chatWidget
.
acceptInput
(
opts
.
query
);
}
if
(
opts
?.
query
)
{
if
(
opts
.
query
.
startsWith
(
'
@
'
)
&&
(
chatWidget
.
input
.
currentMode
===
ChatMode
.
Agent
||
chatService
.
edits2Enabled
))
{
chatWidget
.
input
.
setChatMode
(
ChatMode
.
Ask
);
}
if
(
opts
.
isPartialQuery
)
{
chatWidget
.
setInput
(
opts
.
query
);
}
else
{
await
chatWidget
.
waitForReady
();
chatWidget
.
acceptInput
(
opts
.
query
);
}
if
(
opts
?.
toolIds
&&
opts
.
toolIds
.
length
>
0
)
{
for
(
const
toolId
of
opts
.
toolIds
)
{
const
tool
=
toolsService
.
getTool
(
toolId
);
if
(
tool
)
{
chatWidget
.
attachmentModel
.
addContext
({
id
:
tool
.
id
,
name
:
tool
.
displayName
,
fullName
:
tool
.
displayName
,
value
:
undefined
,
icon
:
ThemeIcon
.
isThemeIcon
(
tool
.
icon
)
?
tool
.
icon
:
undefined
,
kind
:
'
tool
'
});
}
}
if
(
opts
?.
toolIds
&&
opts
.
toolIds
.
length
>
0
)
{
for
(
const
toolId
of
opts
.
toolIds
)
{
const
tool
=
toolsService
.
getTool
(
toolId
);
if
(
tool
)
{
chatWidget
.
attachmentModel
.
addContext
({
id
:
tool
.
id
,
name
:
tool
.
displayName
,
fullName
:
tool
.
displayName
,
value
:
undefined
,
icon
:
ThemeIcon
.
isThemeIcon
(
tool
.
icon
)
?
tool
.
icon
:
undefined
,
kind
:
'
tool
'
});
}
}
chatWidget
.
focusInput
();
}
}
class
PrimaryOpenChatGlobalAction
extends
OpenChatGlobalAction
{
constructor
()
{
super
({
id
:
CHAT_OPEN_ACTION_ID
,
title
:
localize2
(
'
openChat
'
,
"
Open Chat
"
),
keybinding
:
{
weight
:
KeybindingWeight
.
WorkbenchContrib
,
primary
:
KeyMod
.
CtrlCmd
|
KeyMod
.
Alt
|
KeyCode
.
KeyI
,
mac
:
{
primary
:
KeyMod
.
CtrlCmd
|
KeyMod
.
WinCtrl
|
KeyCode
.
KeyI
}
}
},
menu
:
[{
id
:
MenuId
.
ChatTitleBarMenu
,
group
:
'
a_open
'
,
order
:
1
}]
});
}
}
export
function
getOpenChatActionIdForMode
(
mode
:
ChatMode
):
string
{
const
modeStr
=
modeToString
(
mode
);
return
`workbench.action.chat.open
${
modeStr
}
`
;
}
abstract
class
ModeOpenChatGlobalAction
extends
OpenChatGlobalAction
{
constructor
(
mode
:
ChatMode
,
keybinding
?:
ICommandPaletteOptions
[
'
keybinding
'
])
{
super
({
id
:
getOpenChatActionIdForMode
(
mode
),
title
:
localize2
(
'
openChatMode
'
,
"
Open Chat ({0})
"
,
modeToString
(
mode
)),
keybinding
},
mode
);
}
}
chatWidget
.
focusInput
();
export
function
registerChatActions
()
{
registerAction2
(
PrimaryOpenChatGlobalAction
);
registerAction2
(
class
extends
ModeOpenChatGlobalAction
{
constructor
()
{
super
(
ChatMode
.
Ask
);
}
});
registerAction2
(
class
extends
ModeOpenChatGlobalAction
{
constructor
()
{
super
(
ChatMode
.
Agent
,
{
when
:
ContextKeyExpr
.
has
(
`config.
${
ChatConfiguration
.
AgentEnabled
}
`
),
weight
:
KeybindingWeight
.
WorkbenchContrib
,
primary
:
KeyMod
.
CtrlCmd
|
KeyMod
.
Shift
|
KeyCode
.
KeyI
,
linux
:
{
primary
:
KeyMod
.
CtrlCmd
|
KeyMod
.
Alt
|
KeyMod
.
Shift
|
KeyCode
.
KeyI
}
},);
}
});
registerAction2
(
class
extends
ModeOpenChatGlobalAction
{
constructor
()
{
super
(
ChatMode
.
Edit
);
}
});
registerAction2
(
class
ToggleChatAction
extends
Action2
{
constructor
()
{
...
...
src/vs/workbench/contrib/chat/browser/chat.ts
Просмотр файла @
10cf37de
...
...
@@ -169,6 +169,7 @@ export interface IChatAcceptInputOptions {
}
export
interface
IChatWidget
{
readonly
domNode
:
HTMLElement
;
readonly
onDidChangeViewModel
:
Event
<
void
>
;
readonly
onDidAcceptInput
:
Event
<
void
>
;
readonly
onDidHide
:
Event
<
void
>
;
...
...
src/vs/workbench/contrib/chat/browser/chatWidget.ts
Просмотр файла @
10cf37de
...
...
@@ -154,6 +154,10 @@ export class ChatWidget extends Disposable implements IChatWidget {
private
listContainer
!
:
HTMLElement
;
private
container
!
:
HTMLElement
;
get
domNode
()
{
return
this
.
container
;
}
private
welcomeMessageContainer
!
:
HTMLElement
;
private
readonly
welcomePart
:
MutableDisposable
<
ChatViewWelcomePart
>
=
this
.
_register
(
new
MutableDisposable
());
...
...
src/vs/workbench/contrib/chat/browser/modelPicker/modePickerActionItem.ts
Просмотр файла @
10cf37de
...
...
@@ -8,7 +8,6 @@ import { renderLabelWithIcons } from '../../../../../base/browser/ui/iconLabel/i
import
{
IAction
}
from
'
../../../../../base/common/actions.js
'
;
import
{
Event
}
from
'
../../../../../base/common/event.js
'
;
import
{
IDisposable
}
from
'
../../../../../base/common/lifecycle.js
'
;
import
{
localize
}
from
'
../../../../../nls.js
'
;
import
{
ActionWidgetDropdownActionViewItem
}
from
'
../../../../../platform/actions/browser/actionWidgetDropdownActionViewItem.js
'
;
import
{
MenuItemAction
}
from
'
../../../../../platform/actions/common/actions.js
'
;
import
{
IActionWidgetService
}
from
'
../../../../../platform/actionWidget/browser/actionWidget.js
'
;
...
...
@@ -16,7 +15,8 @@ import { IActionWidgetDropdownActionProvider, IActionWidgetDropdownOptions } fro
import
{
IContextKeyService
}
from
'
../../../../../platform/contextkey/common/contextkey.js
'
;
import
{
IKeybindingService
}
from
'
../../../../../platform/keybinding/common/keybinding.js
'
;
import
{
IChatAgentService
}
from
'
../../common/chatAgents.js
'
;
import
{
ChatMode
}
from
'
../../common/constants.js
'
;
import
{
ChatMode
,
modeToString
}
from
'
../../common/constants.js
'
;
import
{
getOpenChatActionIdForMode
}
from
'
../actions/chatActions.js
'
;
import
{
IToggleChatModeArgs
}
from
'
../actions/chatExecuteActions.js
'
;
export
interface
IModePickerDelegate
{
...
...
@@ -35,8 +35,8 @@ export class ModePickerActionItem extends ActionWidgetDropdownActionViewItem {
)
{
const
makeAction
=
(
mode
:
ChatMode
):
IAction
=>
({
...
action
,
id
:
mode
,
label
:
this
.
modeToString
(
mode
),
id
:
getOpenChatActionIdForMode
(
mode
)
,
label
:
modeToString
(
mode
),
class
:
undefined
,
enabled
:
true
,
checked
:
delegate
.
getMode
()
===
mode
,
...
...
@@ -70,21 +70,9 @@ export class ModePickerActionItem extends ActionWidgetDropdownActionViewItem {
this
.
_register
(
delegate
.
onDidChangeMode
(()
=>
this
.
renderLabel
(
this
.
element
!
)));
}
private
modeToString
(
mode
:
ChatMode
)
{
switch
(
mode
)
{
case
ChatMode
.
Agent
:
return
localize
(
'
chat.agentMode
'
,
"
Agent
"
);
case
ChatMode
.
Edit
:
return
localize
(
'
chat.normalMode
'
,
"
Edit
"
);
case
ChatMode
.
Ask
:
default
:
return
localize
(
'
chat.askMode
'
,
"
Ask
"
);
}
}
protected
override
renderLabel
(
element
:
HTMLElement
):
IDisposable
|
null
{
this
.
setAriaLabelAttributes
(
element
);
const
state
=
this
.
modeToString
(
this
.
delegate
.
getMode
());
const
state
=
modeToString
(
this
.
delegate
.
getMode
());
dom
.
reset
(
element
,
dom
.
$
(
'
span.chat-model-label
'
,
undefined
,
state
),
...
renderLabelWithIcons
(
`$(chevron-down)`
));
return
null
;
}
...
...
src/vs/workbench/contrib/chat/common/constants.ts
Просмотр файла @
10cf37de
...
...
@@ -16,6 +16,18 @@ export enum ChatMode {
Agent
=
'
agent
'
}
export
function
modeToString
(
mode
:
ChatMode
)
{
switch
(
mode
)
{
case
ChatMode
.
Agent
:
return
'
Agent
'
;
case
ChatMode
.
Edit
:
return
'
Edit
'
;
case
ChatMode
.
Ask
:
default
:
return
'
Ask
'
;
}
}
export
function
validateChatMode
(
mode
:
unknown
):
ChatMode
|
undefined
{
switch
(
mode
)
{
case
ChatMode
.
Ask
:
...
...
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать