@@ -20,13 +20,17 @@ import { IContextKeyService, ContextKeyExpr } from '../../../../platform/context
2020import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js' ;
2121import { IOpenerService } from '../../../../platform/opener/common/opener.js' ;
2222import { IHoverService } from '../../../../platform/hover/browser/hover.js' ;
23+ import { IMenuService } from '../../../../platform/actions/common/actions.js' ;
24+ import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js' ;
25+ import { DisposableStore } from '../../../../base/common/lifecycle.js' ;
2326import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js' ;
2427import { ViewPane , IViewPaneOptions } from '../../../browser/parts/views/viewPane.js' ;
2528import { Extensions , IViewContainersRegistry , IViewDescriptorService , ViewContainerLocation , IViewsRegistry , IViewDescriptor } from '../../../common/views.js' ;
2629import { IExtensionService } from '../../../services/extensions/common/extensions.js' ;
2730import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js' ;
2831import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js' ;
2932import { IChatSessionItem , IChatSessionItemProvider , IChatSessionsExtensionPoint , IChatSessionsService } from '../common/chatSessionsService.js' ;
33+ import { ChatContextKeys } from '../common/chatContextKeys.js' ;
3034import { IAsyncDataSource , ITreeRenderer , ITreeNode } from '../../../../base/browser/ui/tree/tree.js' ;
3135import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js' ;
3236import { CancellationToken } from '../../../../base/common/cancellation.js' ;
@@ -53,9 +57,26 @@ import { ThemeIcon } from '../../../../base/common/themables.js';
5357import { IChatEditorOptions } from './chatEditor.js' ;
5458import { ChatSessionUri } from '../common/chatUri.js' ;
5559import { coalesce } from '../../../../base/common/arrays.js' ;
60+ import { Action2 , registerAction2 } from '../../../../platform/actions/common/actions.js' ;
61+ import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js' ;
62+ import { INotificationService } from '../../../../platform/notification/common/notification.js' ;
5663
5764export const VIEWLET_ID = 'workbench.view.chat.sessions' ;
5865
66+ // Helper function to create context overlay for session items
67+ function getSessionItemContextOverlay ( session : IChatSessionItem , provider ?: IChatSessionItemProvider ) : [ string , any ] [ ] {
68+ const overlay : [ string , any ] [ ] = [
69+ [ ChatContextKeys . sessionId . key , session . id ] ,
70+ [ ChatContextKeys . sessionLabel . key , session . label ] ,
71+ ] ;
72+
73+ if ( provider ) {
74+ overlay . push ( [ ChatContextKeys . sessionType . key , provider . chatSessionType ] ) ;
75+ }
76+
77+ return overlay ;
78+ }
79+
5980// Extended interface for local chat session items that includes editor information or widget information
6081interface ILocalChatSessionItem extends IChatSessionItem {
6182 editor ?: EditorInput ;
@@ -546,6 +567,7 @@ interface ISessionTemplateData {
546567 container : HTMLElement ;
547568 resourceLabel : IResourceLabel ;
548569 actionBar : ActionBar ;
570+ elementDisposable : DisposableStore ;
549571}
550572
551573// Renderer for session items in the tree
@@ -557,6 +579,8 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
557579 private readonly labels : ResourceLabels ,
558580 @IThemeService private readonly themeService : IThemeService ,
559581 @ILogService private readonly logService : ILogService ,
582+ @IMenuService private readonly menuService : IMenuService ,
583+ @IContextKeyService private readonly contextKeyService : IContextKeyService ,
560584 ) {
561585 super ( ) ;
562586
@@ -616,16 +640,22 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
616640 const element = append ( container , $ ( '.chat-session-item' ) ) ;
617641 const resourceLabel = this . labels . create ( element , { supportHighlights : true } ) ;
618642 const actionBar = new ActionBar ( container ) ;
643+ const elementDisposable = new DisposableStore ( ) ;
619644
620645 return {
621646 container : element ,
622647 resourceLabel,
623- actionBar
648+ actionBar,
649+ elementDisposable
624650 } ;
625651 }
626652
627653 renderElement ( element : ITreeNode < IChatSessionItem , FuzzyScore > , index : number , templateData : ISessionTemplateData ) : void {
628654 const session = element . element ;
655+ const sessionWithProvider = session as IChatSessionItem & { provider : IChatSessionItemProvider } ;
656+
657+ // Clear previous element disposables
658+ templateData . elementDisposable . clear ( ) ;
629659
630660 // Handle different icon types
631661 let iconResource : URI | undefined ;
@@ -661,9 +691,42 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
661691 fileKind : undefined ,
662692 icon : iconTheme || iconUri
663693 } ) ;
694+
695+ // Create context overlay for this specific session item
696+ const contextOverlay = getSessionItemContextOverlay ( session , sessionWithProvider . provider ) ;
697+
698+ const contextKeyService = this . contextKeyService . createOverlay ( contextOverlay ) ;
699+
700+ // Create menu for this session item
701+ const menu = templateData . elementDisposable . add (
702+ this . menuService . createMenu ( MenuId . ChatSessionsMenu , contextKeyService )
703+ ) ;
704+
705+ // Setup action bar with contributed actions
706+ const setupActionBar = ( ) => {
707+ templateData . actionBar . clear ( ) ;
708+
709+ const { primary } = getActionBarActions (
710+ menu . getActions ( { arg : session , shouldForwardArgs : true } ) ,
711+ 'inline'
712+ ) ;
713+ templateData . actionBar . push ( primary , { icon : true , label : false } ) ;
714+
715+ // Set context for the action bar
716+ templateData . actionBar . context = session ;
717+ } ;
718+
719+ // Setup initial action bar and listen for menu changes
720+ templateData . elementDisposable . add ( menu . onDidChange ( ( ) => setupActionBar ( ) ) ) ;
721+ setupActionBar ( ) ;
722+ }
723+
724+ disposeElement ( _element : ITreeNode < IChatSessionItem , FuzzyScore > , _index : number , templateData : ISessionTemplateData ) : void {
725+ templateData . elementDisposable . clear ( ) ;
664726 }
665727
666728 disposeTemplate ( templateData : ISessionTemplateData ) : void {
729+ templateData . elementDisposable . dispose ( ) ;
667730 templateData . resourceLabel . dispose ( ) ;
668731 templateData . actionBar . dispose ( ) ;
669732 }
@@ -867,7 +930,7 @@ class SessionsViewPane extends ViewPane {
867930 this . dataSource = new SessionsDataSource ( this . provider ) ;
868931
869932 const delegate = new SessionsDelegate ( ) ;
870- const renderer = new SessionsRenderer ( this . labels , this . themeService , this . logService ) ;
933+ const renderer = this . instantiationService . createInstance ( SessionsRenderer , this . labels ) ;
871934 this . _register ( renderer ) ;
872935
873936 this . tree = this . instantiationService . createInstance (
@@ -962,3 +1025,45 @@ MenuRegistry.appendMenuItem(MenuId.ViewTitle, {
9621025 when : ContextKeyExpr . equals ( 'view' , `${ VIEWLET_ID } .local` ) ,
9631026} ) ;
9641027
1028+ // Test menu contribution for chat session items
1029+ MenuRegistry . appendMenuItem ( MenuId . ChatSessionsMenu , {
1030+ command : {
1031+ id : 'workbench.action.chat.testSessionCommand' ,
1032+ title : nls . localize2 ( 'chatSession.testCommand' , "Test Session Command" ) ,
1033+ icon : Codicon . gear
1034+ } ,
1035+ group : 'inline' ,
1036+ order : 1 ,
1037+ when : ContextKeyExpr . and (
1038+ ChatContextKeys . sessionType . isEqualTo ( 'local' ) ,
1039+ ChatContextKeys . sessionId . notEqualsTo ( '' )
1040+ ) ,
1041+ } ) ;
1042+
1043+ // Test command implementation for chat session items
1044+ class TestChatSessionCommand extends Action2 {
1045+ constructor ( ) {
1046+ super ( {
1047+ id : 'workbench.action.chat.testSessionCommand' ,
1048+ title : nls . localize2 ( 'chatSession.testCommand' , "Test Session Command" ) ,
1049+ f1 : false , // Don't show in command palette
1050+ } ) ;
1051+ }
1052+
1053+ async run ( accessor : ServicesAccessor , session ?: IChatSessionItem ) : Promise < void > {
1054+ const notificationService = accessor . get ( INotificationService ) ;
1055+
1056+ if ( session ) {
1057+ notificationService . info (
1058+ nls . localize ( 'chatSession.testCommandExecuted' , "Test command executed for session: {0} (ID: {1})" , session . label , session . id )
1059+ ) ;
1060+ } else {
1061+ notificationService . info (
1062+ nls . localize ( 'chatSession.testCommandNoSession' , "Test command executed without session context" )
1063+ ) ;
1064+ }
1065+ }
1066+ }
1067+
1068+ registerAction2 ( TestChatSessionCommand ) ;
1069+
0 commit comments