Открыть боковую панель
code
vscode
Коммиты
59baae6e
Не подтверждена
Коммит
59baae6e
создал
Сен 14, 2025
по автору
Justin Chen
Зафиксировано автором
GitHub
Сен 15, 2025
Просмотр файлов
chat history cleanup (#266587)
* chat history cleanup * use toolbar instead * fix flicker
владелец
28580e24
Изменения
4
Скрыть пробелы
Построчно
Рядом
src/vs/platform/actions/common/actions.ts
Просмотр файла @
59baae6e
...
...
@@ -230,6 +230,7 @@ export class MenuId {
static
readonly
ChatCodeBlock
=
new
MenuId
(
'
ChatCodeblock
'
);
static
readonly
ChatCompareBlock
=
new
MenuId
(
'
ChatCompareBlock
'
);
static
readonly
ChatMessageTitle
=
new
MenuId
(
'
ChatMessageTitle
'
);
static
readonly
ChatHistory
=
new
MenuId
(
'
ChatHistory
'
);
static
readonly
ChatMessageFooter
=
new
MenuId
(
'
ChatMessageFooter
'
);
static
readonly
ChatExecute
=
new
MenuId
(
'
ChatExecute
'
);
static
readonly
ChatExecuteSecondary
=
new
MenuId
(
'
ChatExecuteSecondary
'
);
...
...
src/vs/workbench/contrib/chat/browser/actions/chatActions.ts
Просмотр файла @
59baae6e
...
...
@@ -522,6 +522,11 @@ export function registerChatActions() {
id
:
MenuId
.
EditorTitle
,
when
:
ActiveEditorContext
.
isEqualTo
(
ChatEditorInput
.
EditorID
),
},
{
id
:
MenuId
.
ChatHistory
,
when
:
ChatContextKeys
.
inEmptyStateWithHistoryEnabled
,
group
:
'
navigation
'
,
}
],
category
:
CHAT_CATEGORY
,
icon
:
Codicon
.
history
,
...
...
src/vs/workbench/contrib/chat/browser/chatWidget.ts
Просмотр файла @
59baae6e
...
...
@@ -81,10 +81,9 @@ import './media/chatViewWelcome.css';
import
{
ChatViewWelcomePart
,
IChatSuggestedPrompts
,
IChatViewWelcomeContent
}
from
'
./viewsWelcome/chatViewWelcomeController.js
'
;
import
{
ChatViewPane
}
from
'
./chatViewPane.js
'
;
import
{
IViewsService
}
from
'
../../../services/views/common/viewsService.js
'
;
import
{
ICommandService
}
from
'
../../../../platform/commands/common/commands.js
'
;
import
{
IHoverService
}
from
'
../../../../platform/hover/browser/hover.js
'
;
import
product
from
'
../../../../platform/product/common/product.js
'
;
import
{
ChatEntitlement
,
IChatEntitlementService
}
from
'
../../../services/chat/common/chatEntitlementService.js
'
;
import
{
MenuWorkbenchToolBar
}
from
'
../../../../platform/actions/browser/toolbar.js
'
;
const
$
=
dom
.
$
;
...
...
@@ -165,7 +164,6 @@ class ChatHistoryListRenderer implements IListRenderer<IChatHistoryListItem, ICh
constructor
(
private
readonly
onDidClickItem
:
(
item
:
IChatHistoryListItem
)
=>
void
,
private
readonly
hoverService
:
IHoverService
,
private
readonly
formatHistoryTimestamp
:
(
timestamp
:
number
,
todayMidnightMs
:
number
)
=>
string
,
private
readonly
todayMidnightMs
:
number
)
{
}
...
...
@@ -186,25 +184,12 @@ class ChatHistoryListRenderer implements IListRenderer<IChatHistoryListItem, ICh
renderElement
(
element
:
IChatHistoryListItem
,
index
:
number
,
templateData
:
IChatHistoryTemplate
):
void
{
const
{
container
,
title
,
date
,
disposables
}
=
templateData
;
// Clear previous disposables
disposables
.
clear
();
// Set content
title
.
textContent
=
element
.
title
;
date
.
textContent
=
this
.
formatHistoryTimestamp
(
element
.
lastMessageDate
,
this
.
todayMidnightMs
);
// Set accessibility
container
.
setAttribute
(
'
aria-label
'
,
element
.
title
);
// Setup hover for full title
const
titleHoverEl
=
dom
.
$
(
'
div.chat-history-item-hover
'
);
titleHoverEl
.
textContent
=
element
.
title
;
disposables
.
add
(
this
.
hoverService
.
setupDelayedHover
(
container
,
{
content
:
titleHoverEl
,
appearance
:
{
showPointer
:
false
,
compact
:
true
}
}));
// Setup click and keyboard handlers
disposables
.
add
(
dom
.
addDisposableListener
(
container
,
dom
.
EventType
.
CLICK
,
()
=>
{
this
.
onDidClickItem
(
element
);
}));
...
...
@@ -284,6 +269,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
private
listContainer
!
:
HTMLElement
;
private
container
!
:
HTMLElement
;
private
historyListContainer
!
:
HTMLElement
;
get
domNode
()
{
return
this
.
container
;
}
...
...
@@ -427,7 +413,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
@
ILanguageModelToolsService
private
readonly
toolsService
:
ILanguageModelToolsService
,
@
IWorkspaceContextService
private
readonly
contextService
:
IWorkspaceContextService
,
@
IChatModeService
private
readonly
chatModeService
:
IChatModeService
,
@
IHoverService
private
readonly
hoverService
:
IHoverService
,
@
IChatTodoListService
private
readonly
chatTodoListService
:
IChatTodoListService
,
@
IChatLayoutService
private
readonly
chatLayoutService
:
IChatLayoutService
,
@
IChatEntitlementService
private
readonly
chatEntitlementService
:
IChatEntitlementService
,
...
...
@@ -993,6 +978,10 @@ export class ChatWidget extends Disposable implements IChatWidget {
if
(
this
.
viewModel
)
{
dom
.
setVisibility
(
numItems
===
0
,
this
.
welcomeMessageContainer
);
dom
.
setVisibility
(
numItems
!==
0
,
this
.
listContainer
);
if
(
numItems
>
0
)
{
this
.
refreshHistoryList
();
}
}
}
...
...
@@ -1010,82 +999,37 @@ export class ChatWidget extends Disposable implements IChatWidget {
const
header
=
dom
.
append
(
container
,
$
(
'
.chat-welcome-history-header
'
));
const
headerTitle
=
dom
.
append
(
header
,
$
(
'
.chat-welcome-history-header-title
'
));
headerTitle
.
textContent
=
localize
(
'
chat.history.title
'
,
'
History
'
);
const
header
Actions
=
dom
.
append
(
header
,
$
(
'
.chat-welcome-history-header-
actions
'
));
const
header
ToolbarContainer
=
dom
.
append
(
header
,
$
(
'
.chat-welcome-history-header-
toolbar
'
));
const
items
=
await
this
.
chatService
.
getHistory
();
const
filtered
=
items
.
filter
(
i
=>
!
i
.
isActive
)
.
sort
((
a
,
b
)
=>
(
b
.
lastMessageDate
??
0
)
-
(
a
.
lastMessageDate
??
0
))
.
slice
(
0
,
3
);
// toolbar for previous sessions
this
.
historyViewStore
.
add
(
this
.
instantiationService
.
createInstance
(
MenuWorkbenchToolBar
,
headerToolbarContainer
,
MenuId
.
ChatHistory
,
{}));
header
.
appendChild
(
headerToolbarContainer
);
// If no items to show, hide the entire chat history section
if
(
filtered
.
length
===
0
)
{
const
initialHistoryItems
=
await
this
.
computeHistoryItems
();
if
(
initialHistoryItems
.
length
===
0
)
{
historyRoot
.
remove
();
return
;
}
const
showAllButton
=
dom
.
append
(
headerActions
,
$
(
'
.chat-welcome-history-show-all
'
));
showAllButton
.
classList
.
add
(
'
codicon
'
,
`codicon-
${
Codicon
.
history
.
id
}
`
,
'
chat-welcome-history-show-all
'
);
showAllButton
.
tabIndex
=
0
;
showAllButton
.
setAttribute
(
'
role
'
,
'
button
'
);
const
showAllHover
=
localize
(
'
chat.history.showAllHover
'
,
'
Show history...
'
);
showAllButton
.
setAttribute
(
'
aria-label
'
,
showAllHover
);
const
showAllHoverText
=
dom
.
$
(
'
div.chat-history-button-hover
'
);
showAllHoverText
.
textContent
=
showAllHover
;
this
.
historyViewStore
.
add
(
this
.
hoverService
.
setupDelayedHover
(
showAllButton
,
{
content
:
showAllHoverText
,
appearance
:
{
showPointer
:
false
,
compact
:
true
}
}));
this
.
historyViewStore
.
add
(
dom
.
addDisposableListener
(
showAllButton
,
dom
.
EventType
.
CLICK
,
e
=>
{
e
.
preventDefault
();
e
.
stopPropagation
();
setTimeout
(()
=>
{
this
.
instantiationService
.
invokeFunction
(
accessor
=>
accessor
.
get
(
ICommandService
).
executeCommand
(
'
workbench.action.chat.history
'
));
},
0
);
}));
this
.
historyViewStore
.
add
(
dom
.
addStandardDisposableListener
(
showAllButton
,
dom
.
EventType
.
KEY_DOWN
,
e
=>
{
if
(
e
.
equals
(
KeyCode
.
Enter
)
||
e
.
equals
(
KeyCode
.
Space
))
{
e
.
preventDefault
();
e
.
stopPropagation
();
setTimeout
(()
=>
{
this
.
instantiationService
.
invokeFunction
(
accessor
=>
accessor
.
get
(
ICommandService
).
executeCommand
(
'
workbench.action.chat.history
'
));
},
0
);
}
}));
const
welcomeHistoryContainer
=
dom
.
append
(
container
,
$
(
'
.chat-welcome-history-list
'
));
this
.
welcomeMessageContainer
.
classList
.
toggle
(
'
has-chat-history
'
,
filtered
.
length
>
0
);
this
.
historyListContainer
=
dom
.
append
(
container
,
$
(
'
.chat-welcome-history-list
'
));
this
.
welcomeMessageContainer
.
classList
.
toggle
(
'
has-chat-history
'
,
initialHistoryItems
.
length
>
0
);
// Compute today's midnight once for label decisions
const
todayMidnight
=
new
Date
();
todayMidnight
.
setHours
(
0
,
0
,
0
,
0
);
const
todayMidnightMs
=
todayMidnight
.
getTime
();
// Create WorkbenchList for chat history items (limit to top 3)
const
historyItems
:
IChatHistoryListItem
[]
=
filtered
.
slice
(
0
,
3
).
map
(
item
=>
({
sessionId
:
item
.
sessionId
,
title
:
item
.
title
,
lastMessageDate
:
typeof
item
.
lastMessageDate
===
'
number
'
?
item
.
lastMessageDate
:
Date
.
now
(),
isActive
:
item
.
isActive
}));
const
listHeight
=
historyItems
.
length
*
22
;
welcomeHistoryContainer
.
style
.
height
=
`
${
listHeight
}
px`
;
welcomeHistoryContainer
.
style
.
minHeight
=
`
${
listHeight
}
px`
;
welcomeHistoryContainer
.
style
.
overflow
=
'
hidden
'
;
if
(
!
this
.
historyList
)
{
const
delegate
=
new
ChatHistoryListDelegate
();
const
renderer
=
new
ChatHistoryListRenderer
(
async
(
item
)
=>
await
this
.
openHistorySession
(
item
.
sessionId
),
this
.
hoverService
,
(
timestamp
,
todayMs
)
=>
this
.
formatHistoryTimestamp
(
timestamp
,
todayMs
),
todayMidnightMs
);
const
list
=
this
.
instantiationService
.
createInstance
(
this
.
historyList
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
WorkbenchList
<
IChatHistoryListItem
>
,
'
ChatHistoryList
'
,
welcomeH
istoryContainer
,
this
.
h
istory
List
Container
,
delegate
,
[
renderer
],
{
...
...
@@ -1101,24 +1045,53 @@ export class ChatWidget extends Disposable implements IChatWidget {
getWidgetAriaLabel
:
()
=>
localize
(
'
chat.history.list
'
,
'
Chat History
'
)
}
}
);
this
.
historyList
=
this
.
_register
(
list
)
;
)
);
this
.
historyList
.
getHTMLElement
().
tabIndex
=
-
1
;
}
else
{
const
currentHistoryList
=
this
.
historyList
.
getHTMLElement
();
if
(
currentHistoryList
&&
currentHistoryList
.
parentElement
!==
welcomeH
istoryContainer
)
{
welcomeH
istoryContainer
.
appendChild
(
currentHistoryList
);
if
(
currentHistoryList
&&
currentHistoryList
.
parentElement
!==
this
.
h
istory
List
Container
)
{
this
.
h
istory
List
Container
.
appendChild
(
currentHistoryList
);
}
}
this
.
historyList
.
splice
(
0
,
this
.
historyList
.
length
,
historyItems
);
this
.
historyList
.
layout
(
undefined
,
listHeight
);
// Deprecated text link replaced by icon button in header
this
.
renderHistoryItems
(
initialHistoryItems
);
}
catch
(
err
)
{
this
.
logService
.
error
(
'
Failed to render welcome history
'
,
err
);
}
}
private
async
computeHistoryItems
():
Promise
<
IChatHistoryListItem
[]
>
{
try
{
const
items
=
await
this
.
chatService
.
getHistory
();
return
items
.
filter
(
i
=>
!
i
.
isActive
)
.
sort
((
a
,
b
)
=>
(
b
.
lastMessageDate
??
0
)
-
(
a
.
lastMessageDate
??
0
))
.
slice
(
0
,
3
)
.
map
(
item
=>
({
sessionId
:
item
.
sessionId
,
title
:
item
.
title
,
lastMessageDate
:
typeof
item
.
lastMessageDate
===
'
number
'
?
item
.
lastMessageDate
:
Date
.
now
(),
isActive
:
item
.
isActive
}));
}
catch
(
err
)
{
this
.
logService
.
error
(
'
Failed to compute chat history items
'
,
err
);
return
[];
}
}
private
renderHistoryItems
(
historyItems
:
IChatHistoryListItem
[]):
void
{
if
(
!
this
.
historyList
)
{
return
;
}
const
listHeight
=
historyItems
.
length
*
22
;
if
(
this
.
historyListContainer
)
{
this
.
historyListContainer
.
style
.
height
=
`
${
listHeight
}
px`
;
this
.
historyListContainer
.
style
.
minHeight
=
`
${
listHeight
}
px`
;
}
this
.
historyList
.
splice
(
0
,
this
.
historyList
.
length
,
historyItems
);
this
.
historyList
.
layout
(
undefined
,
listHeight
);
}
private
formatHistoryTimestamp
(
last
:
number
,
todayMidnightMs
:
number
):
string
{
if
(
last
>
todayMidnightMs
)
{
const
diffMs
=
Date
.
now
()
-
last
;
...
...
@@ -1139,6 +1112,15 @@ export class ChatWidget extends Disposable implements IChatWidget {
}
}
private
async
refreshHistoryList
():
Promise
<
void
>
{
const
numItems
=
this
.
viewModel
?.
getItems
().
length
??
0
;
if
(
numItems
===
0
||
!
this
.
historyList
)
{
return
;
}
const
historyItems
=
await
this
.
computeHistoryItems
();
this
.
renderHistoryItems
(
historyItems
);
}
private
renderChatTodoListWidget
():
void
{
const
sessionId
=
this
.
viewModel
?.
sessionId
;
if
(
!
sessionId
)
{
...
...
src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css
Просмотр файла @
59baae6e
...
...
@@ -247,7 +247,6 @@ div.chat-welcome-view {
}
}
/* Recent history list shown above the welcome content */
.chat-welcome-history-root
{
width
:
100%
;
padding
:
0px
8px
0
8px
;
...
...
@@ -256,11 +255,16 @@ div.chat-welcome-view {
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
2px
4px
4
px
4px
;
padding
:
2px
4px
2
px
4px
;
font-size
:
11px
;
text-transform
:
uppercase
;
letter-spacing
:
0.5px
;
min-height
:
22px
;
color
:
var
(
--vscode-descriptionForeground
);
.chat-welcome-history-header-toolbar
{
padding-right
:
15px
;
}
}
.chat-welcome-history-header-title
{
...
...
@@ -276,21 +280,6 @@ div.chat-welcome-view {
padding-right
:
16px
;
}
.chat-welcome-history-show-all
{
cursor
:
pointer
;
color
:
var
(
--vscode-icon-foreground
);
padding
:
2px
;
border-radius
:
4px
;
}
.chat-welcome-history-show-all
:hover
{
background
:
var
(
--vscode-toolbar-hoverBackground
,
var
(
--vscode-list-hoverBackground
));
}
.chat-welcome-history-show-all
:focus-visible
{
outline
:
1px
solid
var
(
--vscode-focusBorder
);
outline-offset
:
1px
;
}
.chat-welcome-history
{
margin
:
0
0
12px
;
...
...
@@ -313,11 +302,13 @@ div.chat-welcome-view {
align-items
:
center
;
justify-content
:
space-between
;
padding
:
2px
12px
4px
12px
;
line-height
:
18px
;
gap
:
8px
;
cursor
:
pointer
;
outline
:
none
;
&:hover
{
background
:
var
(
--vscode-list-hoverBackground
);
}
&:hover
{
background
:
var
(
--vscode-list-hoverBackground
);
}
.chat-welcome-history-title
{
font-size
:
13px
;
white-space
:
nowrap
;
...
...
@@ -325,6 +316,7 @@ div.chat-welcome-view {
text-overflow
:
ellipsis
;
flex
:
1
1
auto
;
}
.chat-welcome-history-date
{
font-size
:
11px
;
color
:
var
(
--vscode-descriptionForeground
);
...
...
@@ -332,11 +324,4 @@ div.chat-welcome-view {
margin-left
:
8px
;
}
}
.chat-welcome-history-more
{
margin
:
4px
0
0
;
padding-left
:
12px
;
a
{
color
:
var
(
--vscode-textLink-foreground
);
cursor
:
pointer
;
}
}
}
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать