Skip to content

Commit d4ac201

Browse files
committed
Enable dnd for chat sessions
Fixes microsoft#261275
1 parent ce2b14b commit d4ac201

File tree

1 file changed

+76
-43
lines changed

1 file changed

+76
-43
lines changed

src/vs/workbench/contrib/chat/browser/chatSessions.ts

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,62 +3,67 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import './media/chatSessions.css';
6+
import { $, append, clearNode, getActiveWindow } from '../../../../base/browser/dom.js';
7+
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
8+
import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';
9+
import { IAsyncDataSource, ITreeNode, ITreeRenderer } from '../../../../base/browser/ui/tree/tree.js';
10+
import { coalesce } from '../../../../base/common/arrays.js';
11+
import { CancellationToken } from '../../../../base/common/cancellation.js';
12+
import { Codicon } from '../../../../base/common/codicons.js';
13+
import { Emitter, Event } from '../../../../base/common/event.js';
14+
import { FuzzyScore } from '../../../../base/common/filters.js';
15+
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
16+
import { MarshalledId } from '../../../../base/common/marshallingIds.js';
17+
import { ThemeIcon } from '../../../../base/common/themables.js';
18+
import { URI } from '../../../../base/common/uri.js';
719
import * as nls from '../../../../nls.js';
20+
import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
21+
import { IMenuService, MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js';
822
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
23+
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
924
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
25+
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
1026
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
1127
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
28+
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
29+
import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js';
1230
import { ILogService } from '../../../../platform/log/common/log.js';
31+
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
1332
import { IProgressService } from '../../../../platform/progress/common/progress.js';
1433
import { Registry } from '../../../../platform/registry/common/platform.js';
1534
import { IStorageService } from '../../../../platform/storage/common/storage.js';
1635
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
36+
import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';
1737
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
1838
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
19-
import { IContextKeyService, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
20-
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
21-
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
22-
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
23-
import { IMenuService, MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js';
24-
import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
25-
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
39+
import { fillEditorsDragData } from '../../../browser/dnd.js';
40+
import { IResourceLabel, ResourceLabels } from '../../../browser/labels.js';
41+
import { IViewPaneOptions, ViewPane } from '../../../browser/parts/views/viewPane.js';
2642
import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js';
27-
import { MarshalledId } from '../../../../base/common/marshallingIds.js';
28-
import { ViewPane, IViewPaneOptions } from '../../../browser/parts/views/viewPane.js';
29-
import { Extensions, IViewContainersRegistry, IViewDescriptorService, ViewContainerLocation, IViewsRegistry, IViewDescriptor } from '../../../common/views.js';
43+
import { IWorkbenchContribution } from '../../../common/contributions.js';
44+
import { GroupModelChangeKind } from '../../../common/editor.js';
45+
import { EditorInput } from '../../../common/editor/editorInput.js';
46+
import { Extensions, IViewContainersRegistry, IViewDescriptor, IViewDescriptorService, IViewsRegistry, ViewContainerLocation } from '../../../common/views.js';
47+
import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';
48+
import { IEditorService } from '../../../services/editor/common/editorService.js';
3049
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
3150
import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js';
32-
import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js';
33-
import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService } from '../common/chatSessionsService.js';
51+
import { IViewsService } from '../../../services/views/common/viewsService.js';
3452
import { ChatContextKeys } from '../common/chatContextKeys.js';
35-
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from '../../../../base/browser/ui/tree/tree.js';
36-
import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';
37-
import { CancellationToken } from '../../../../base/common/cancellation.js';
38-
import { FuzzyScore } from '../../../../base/common/filters.js';
39-
import { ResourceLabels, IResourceLabel } from '../../../browser/labels.js';
40-
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
41-
import { append, $, getActiveWindow, clearNode } from '../../../../base/browser/dom.js';
42-
import { URI } from '../../../../base/common/uri.js';
43-
import { IEditorGroupsService, IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
44-
import { GroupModelChangeKind } from '../../../common/editor.js';
45-
import { Emitter, Event } from '../../../../base/common/event.js';
46-
import { Codicon } from '../../../../base/common/codicons.js';
47-
import { IEditorService } from '../../../services/editor/common/editorService.js';
48-
import { EditorInput } from '../../../common/editor/editorInput.js';
49-
import { ChatEditorInput } from './chatEditorInput.js';
50-
import { IChatWidgetService, IChatWidget } from './chat.js';
53+
import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService } from '../common/chatSessionsService.js';
54+
import { ChatSessionUri } from '../common/chatUri.js';
5155
import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js';
52-
import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';
53-
import { IWorkbenchContribution } from '../../../common/contributions.js';
54-
import { IViewsService } from '../../../services/views/common/viewsService.js';
55-
import { ThemeIcon } from '../../../../base/common/themables.js';
56+
import { IChatWidget, IChatWidgetService } from './chat.js';
5657
import { IChatEditorOptions } from './chatEditor.js';
57-
import { ChatSessionUri } from '../common/chatUri.js';
58-
import { coalesce } from '../../../../base/common/arrays.js';
58+
import { ChatEditorInput } from './chatEditorInput.js';
59+
import './media/chatSessions.css';
5960

6061
export const VIEWLET_ID = 'workbench.view.chat.sessions';
6162

63+
type ChatSessionItemWithProvider = IChatSessionItem & {
64+
readonly provider: IChatSessionItemProvider;
65+
};
66+
6267
// Helper function to create context overlay for session items
6368
function getSessionItemContextOverlay(session: IChatSessionItem, provider?: IChatSessionItemProvider): [string, any][] {
6469
const overlay: [string, any][] = [];
@@ -517,18 +522,19 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer {
517522
}
518523
}
519524

525+
520526
// Chat sessions item data source for the tree
521-
class SessionsDataSource implements IAsyncDataSource<IChatSessionItemProvider, IChatSessionItem> {
527+
class SessionsDataSource implements IAsyncDataSource<IChatSessionItemProvider, ChatSessionItemWithProvider> {
522528
constructor(
523529
private readonly provider: IChatSessionItemProvider
524530
) { }
525531

526-
hasChildren(element: IChatSessionItemProvider | IChatSessionItem): boolean {
532+
hasChildren(element: IChatSessionItemProvider | ChatSessionItemWithProvider): boolean {
527533
// Only the provider (root) has children
528534
return element === this.provider;
529535
}
530536

531-
async getChildren(element: IChatSessionItemProvider | IChatSessionItem): Promise<IChatSessionItem[]> {
537+
async getChildren(element: IChatSessionItemProvider | ChatSessionItemWithProvider): Promise<ChatSessionItemWithProvider[]> {
532538
if (element === this.provider) {
533539
try {
534540
const items = await this.provider.provideChatSessionItems(CancellationToken.None);
@@ -645,7 +651,7 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
645651

646652
renderElement(element: ITreeNode<IChatSessionItem, FuzzyScore>, index: number, templateData: ISessionTemplateData): void {
647653
const session = element.element;
648-
const sessionWithProvider = session as IChatSessionItem & { provider: IChatSessionItemProvider };
654+
const sessionWithProvider = session as ChatSessionItemWithProvider;
649655

650656
// Clear previous element disposables
651657
templateData.elementDisposable.clear();
@@ -736,7 +742,7 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
736742

737743
// Sessions view pane for a specific provider
738744
class SessionsViewPane extends ViewPane {
739-
private tree?: WorkbenchAsyncDataTree<IChatSessionItemProvider, IChatSessionItem, FuzzyScore>;
745+
private tree?: WorkbenchAsyncDataTree<IChatSessionItemProvider, ChatSessionItemWithProvider, FuzzyScore>;
740746
private treeContainer?: HTMLElement;
741747
private dataSource?: SessionsDataSource;
742748
private labels?: ResourceLabels;
@@ -935,14 +941,41 @@ class SessionsViewPane extends ViewPane {
935941
const renderer = this.instantiationService.createInstance(SessionsRenderer, this.labels);
936942
this._register(renderer);
937943

944+
const getResourceForElement = (element: ChatSessionItemWithProvider): URI => {
945+
return ChatSessionUri.forSession(element.provider.chatSessionType, element.id);
946+
};
947+
938948
this.tree = this.instantiationService.createInstance(
939-
WorkbenchAsyncDataTree,
949+
WorkbenchAsyncDataTree<IChatSessionItemProvider, ChatSessionItemWithProvider, FuzzyScore>,
940950
'SessionsTree',
941951
this.treeContainer,
942952
delegate,
943953
[renderer],
944954
this.dataSource,
945955
{
956+
dnd: {
957+
onDragStart: (data, originalEvent) => {
958+
try {
959+
const elements = data.getData() as ChatSessionItemWithProvider[];
960+
const uris = coalesce(elements.map(getResourceForElement));
961+
this.instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, uris, originalEvent));
962+
} catch {
963+
// noop
964+
}
965+
},
966+
getDragURI: (element) => {
967+
return getResourceForElement(element).toString();
968+
},
969+
getDragLabel: (elements) => {
970+
if (elements.length === 1) {
971+
return elements[0].label;
972+
}
973+
return nls.localize('chatSessions.dragLabel', "{0} chat sessions", elements.length);
974+
},
975+
drop: () => { },
976+
onDragOver: () => false,
977+
dispose: () => { },
978+
},
946979
horizontalScrolling: false,
947980
setRowLineHeight: false,
948981
transformOptimization: false,
@@ -956,14 +989,14 @@ class SessionsViewPane extends ViewPane {
956989
hideTwistiesOfChildlessElements: true,
957990
allowNonCollapsibleParents: true // Allow nodes to be non-collapsible even if they have children
958991
}
959-
) as WorkbenchAsyncDataTree<IChatSessionItemProvider, IChatSessionItem, FuzzyScore>;
992+
);
960993

961994
this.logService.debug('Tree created with hideTwistiesOfChildlessElements: true');
962995
this._register(this.tree);
963996

964997
// Handle double-click and keyboard selection to open editors
965998
this._register(this.tree.onDidOpen(async e => {
966-
const element = e.element as IChatSessionItem & { provider: IChatSessionItemProvider };
999+
const element = e.element as ChatSessionItemWithProvider;
9671000

9681001
if (element && this.isLocalChatSessionItem(element)) {
9691002
if (element.sessionType === 'editor' && element.editor && element.group) {

0 commit comments

Comments
 (0)