feat(agents): dock a session in the Agents tool window (#588)

Agent sessions can be opened in the Agents tool window beside the editor (Open in Tool Window / Open in Editor in the task menus, or by dragging between the editor and the tool window); opening in an editor tab stays the default.

Fixes in review:
- A docked conversation kept the previous task's subagent view after switching tasks: the session's local state is now keyed by task.
- Moving a conversation between the editor and the tool window lost its scroll position: position and follow-output are kept per conversation (task or subagent).
- In a short bottom tool window the message area shrank to a few pixels: wide windows place the task list beside the conversation, narrow ones cap the list height.
- Drag and drop and list selection now go through the agents.dockTask / undockTask / openTask actions.

Co-authored-by: a-rookie-of-C-language <[email protected]>
Цей коміт міститься в:
arookieofcтаa-rookie-of-C-language authored and GitHub committed 2026-11-01 11:31:53 +08:00
1 джерело fa26ba842c
коміт 9847fd2c01
18 files changed
+982 -103

No files matched your search

+42
Переглянути файл
@@ -19,6 +19,7 @@ import {
} from "react";
import type { FileMatches, SearchQuery, RunConfig } from "@/bindings";
import type { MenuItem } from "@/ui/Menu";
import type { DragData } from "@/ui/pointerDrag";
import type { TreeReorder } from "@/ui/Tree";
import { create } from "zustand";
import { useShallow } from "zustand/react/shallow";
@@ -501,6 +502,9 @@ export interface OpenEditorTab {
lastActive: number;
}
/** MIME of a dragged editor tab: the payload is `{ paneId, tabId }` as JSON. */
export const EDITOR_TAB_MIME = "application/x-openidea-editor-tab";
export interface EditorService {
openFile(path: string, options?: OpenOptions): Promise<void>;
openInput(input: EditorInput, options?: OpenOptions): Promise<void>;
@@ -518,6 +522,8 @@ export interface EditorService {
openPaths?(): string[];
/** Every open tab, files or not, most recently active first (Switcher). */
openTabs?(): OpenEditorTab[];
/** Input of the tab `tabId` in pane `paneId` (dragging tabs between views), null if there is none. */
tabInput?(paneId: string, tabId: string): EditorInput | null;
/** Input of the active tab (any kind), if any. */
activeInput?(): EditorInput | null;
/** Calls `fn` whenever the active tab changes; returns the unsubscribe. */
@@ -632,6 +638,40 @@ export const maybeEditorService = (): EditorService | undefined => services.edit
/** Creates terminal widgets; provided by the terminal feature. */
export const embeddedTerminal = (): EmbeddedTerminalFactory | undefined => services.embeddedTerminal;
/**
* Handles a pointer drag dropped on the editor content: features register handlers for
* their own drag types (e.g. an agent task dragged from the tool window) without the
* editor depending on them. The editor shows its center drop-zone for accepted types.
*/
export interface EditorDropHandler {
accepts(type: string): boolean;
drop(data: DragData): void;
}
const editorDropHandlers: EditorDropHandler[] = [];
/** Registers a handler for drags dropped on editor content; returns the unregister. */
export function registerEditorDropHandler(handler: EditorDropHandler): () => void {
editorDropHandlers.push(handler);
return () => {
const i = editorDropHandlers.indexOf(handler);
if (i >= 0) editorDropHandlers.splice(i, 1);
};
}
/** Whether any registered handler takes this drag type. */
export function acceptsEditorDrop(type: string): boolean {
return editorDropHandlers.some((h) => h.accepts(type));
}
/** Hands a drag dropped on editor content to its registered handler, if any. */
export function dropOnEditor(data: DragData): boolean {
const handler = editorDropHandlers.find((h) => h.accepts(data.type));
if (!handler) return false;
handler.drop(data);
return true;
}
/** Editor-backed results, recomputed on rerun without reading files from disk. */
export interface FindResultsSource {
title: string;
@@ -897,6 +937,8 @@ export interface MenuGroupContext {
text?: string;
/** Editor menu: 0-based caret line; gutter menu: the clicked line. */
line?: number;
/** The tab's input, when the menu is for an editor tab without a path. */
input?: EditorInput;
}
export interface MenuGroupContribution {