дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
feat(terminal): reorder terminal tabs by dragging them (#836)
The Terminal tool window's tab strip uses the drag and drop Tabs already supports for editor tabs, with its own drag type so terminal and editor tabs don't mix; dropping moves the session tab to the insertion point. Fixes #775 Co-authored-by: kevinh458 <[email protected]>
Цей коміт міститься в:
1 джерело
91c15ff31b
коміт
df9a9e0be0
3 files changed
+40
No files matched your search
@@ -16,9 +16,11 @@ import {
|
||||
Tabs,
|
||||
type MenuItem,
|
||||
type TabItem,
|
||||
type TabsDnd,
|
||||
} from "@/ui";
|
||||
import {
|
||||
getSession,
|
||||
moveTab,
|
||||
newTab,
|
||||
publishFocus,
|
||||
renameTab,
|
||||
@@ -36,6 +38,13 @@ import styles from "./Terminal.module.css";
|
||||
|
||||
const TAB_PREFIX = "openidea-terminal";
|
||||
|
||||
/** Dragging a tab reorders the sessions, like IDEA's terminal tabs. */
|
||||
const TAB_DND: TabsDnd = {
|
||||
type: "application/x-openidea-terminal-tab",
|
||||
payload: (id) => id,
|
||||
onDrop: (id, index) => moveTab(id, index),
|
||||
};
|
||||
|
||||
/** Full title when the label is trimmed, then the working directory. */
|
||||
function tabTooltip(tab: TerminalTab, sessions: Parameters<typeof tabTitle>[1]): string | undefined {
|
||||
const full = tabFullTitle(tab, sessions);
|
||||
@@ -336,6 +345,7 @@ export function TerminalTabs() {
|
||||
onClose={(id) => void onClose(id)}
|
||||
onContextMenu={tabMenu}
|
||||
panelIdPrefix={TAB_PREFIX}
|
||||
dnd={TAB_DND}
|
||||
trailing={
|
||||
<div className={styles.tabActions}>
|
||||
<IconButton icon="plus" size="small" action="terminal.new" title="New Tab" />
|
||||
|
||||
@@ -205,6 +205,20 @@ export function selectTab(id: string) {
|
||||
useTerminals.setState({ activeTab: id });
|
||||
}
|
||||
|
||||
/** `tabs` with tab `id` moved to insertion `index`, an index into `tabs` before the move. */
|
||||
export function reorderTabs<T extends { id: string }>(tabs: readonly T[], id: string, index: number): T[] {
|
||||
const from = tabs.findIndex((t) => t.id === id);
|
||||
if (from < 0) return [...tabs];
|
||||
const rest = tabs.filter((t) => t.id !== id);
|
||||
rest.splice(Math.max(0, Math.min(index > from ? index - 1 : index, rest.length)), 0, tabs[from]!);
|
||||
return rest;
|
||||
}
|
||||
|
||||
/** Drag & drop in the tab strip. */
|
||||
export function moveTab(id: string, index: number) {
|
||||
useTerminals.setState((s) => ({ tabs: reorderTabs(s.tabs, id, index) }));
|
||||
}
|
||||
|
||||
export function renameTab(id: string, name: string) {
|
||||
const trimmed = name.trim();
|
||||
useTerminals.setState((s) => ({
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
layoutPanes,
|
||||
nextTabName,
|
||||
removeFromLayout,
|
||||
reorderTabs,
|
||||
splitLayout,
|
||||
tabFullTitle,
|
||||
tabTitle,
|
||||
@@ -230,6 +231,21 @@ describe("tab names", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("tab order", () => {
|
||||
const ids = (tabs: { id: string }[]) => tabs.map((t) => t.id).join("");
|
||||
const tabs = [{ id: "a" }, { id: "b" }, { id: "c" }];
|
||||
|
||||
it("moves a dragged tab to the insertion index", () => {
|
||||
expect(ids(reorderTabs(tabs, "a", 3))).toBe("bca");
|
||||
expect(ids(reorderTabs(tabs, "a", 2))).toBe("bac");
|
||||
expect(ids(reorderTabs(tabs, "c", 0))).toBe("cab");
|
||||
expect(ids(reorderTabs(tabs, "b", 1))).toBe("abc");
|
||||
expect(ids(reorderTabs(tabs, "b", 2))).toBe("abc");
|
||||
});
|
||||
|
||||
it("ignores unknown tabs", () => expect(ids(reorderTabs(tabs, "x", 0))).toBe("abc"));
|
||||
});
|
||||
|
||||
describe("terminal find", () => {
|
||||
it("formats the match counter", () => {
|
||||
expect(matchCounter(null, "x")).toBe("");
|
||||
|
||||
Reference in new issue
Заблокувати користувача