feat(terminal): a text drag (a Files/Changes row, a skill) is pasted into the pane

xterm.js has no drop handling of its own, so dropped text was lost; the pane
now pastes it as a local terminal does, with a "Drop to paste" hint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 14:43:03 +07:00
parent 763759189f
commit b08482c6aa
4 changed files with 40 additions and 8 deletions
@@ -512,6 +512,23 @@ describe("TerminalView", () => {
);
});
it("pastes the text of a dragged Files-panel row, uploading nothing", () => {
dropFileMock.mockReset();
pasteMock.mockClear();
render(<TerminalView runId="r1" wsBaseUrl="ws://x" />);
const data: Record<string, string> = {
"application/x-agenthub-attach": '{"path":"@src/a.ts","name":"a.ts"}',
"text/plain": "src/a.ts",
};
const dataTransfer = { types: Object.keys(data), files: [], getData: (k: string) => data[k] ?? "" };
fireEvent.dragEnter(screen.getByTestId("terminal-view"), { dataTransfer });
expect(screen.getByTestId("terminal-drop")).toHaveTextContent("Drop to paste into the terminal");
fireEvent.drop(screen.getByTestId("terminal-view"), { dataTransfer });
expect(pasteMock).toHaveBeenCalledWith("src/a.ts ");
expect(screen.queryByTestId("terminal-drop")).toBeNull();
expect(dropFileMock).not.toHaveBeenCalled();
});
it("uploads a pasted image and pastes its path, like a drop", async () => {
dropFileMock.mockReset().mockResolvedValue({ path: "/tmp/agenthub-drops-1/ab-image.png" });
pasteMock.mockClear();