Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,50 @@ export const useDragDropHandlers = (taskId: string) => {
const sourceData = event.operation.source?.data;
const targetData = event.operation.target?.data;

// Tab reordering within same panel is handled by onDragOver
// Here we only handle cross-panel moves and splits

// Handle panel splitting/moving
if (
sourceData?.type !== "tab" ||
targetData?.type !== "panel" ||
!sourceData.tabId ||
!sourceData.panelId ||
!targetData.panelId ||
!targetData.zone
!sourceData.panelId
) {
return;
}

const { tabId, panelId: sourcePanelId } = sourceData;
const { panelId: targetPanelId, zone } = targetData;

if (zone === "center") {
moveTab(taskId, tabId, sourcePanelId, targetPanelId);
setFocusedPanel(taskId, targetPanelId);
} else if (isSplitDirection(zone)) {
splitPanel(taskId, tabId, sourcePanelId, targetPanelId, zone);
// For splits, the new panel gets a generated ID, so we can't easily focus it here
// The target panel remains focused which is reasonable behavior
setFocusedPanel(taskId, targetPanelId);

// Handle drop on panel drop zones (center or split directions)
if (targetData?.type === "panel" && targetData.panelId && targetData.zone) {
const { panelId: targetPanelId, zone } = targetData;

if (zone === "center") {
moveTab(taskId, tabId, sourcePanelId, targetPanelId);
setFocusedPanel(taskId, targetPanelId);
} else if (isSplitDirection(zone)) {
splitPanel(taskId, tabId, sourcePanelId, targetPanelId, zone);
setFocusedPanel(taskId, targetPanelId);
}
return;
}

// Handle drop on tab bar (cross-panel move)
if (
targetData?.type === "tab-bar" &&
targetData.panelId &&
targetData.panelId !== sourcePanelId
) {
moveTab(taskId, tabId, sourcePanelId, targetData.panelId);
setFocusedPanel(taskId, targetData.panelId);
return;
}

// Handle drop on another tab in a different panel (cross-panel move)
if (
targetData?.type === "tab" &&
targetData.panelId &&
targetData.panelId !== sourcePanelId
) {
moveTab(taskId, tabId, sourcePanelId, targetData.panelId);
setFocusedPanel(taskId, targetData.panelId);
return;
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,53 @@ describe("panelLayoutStore", () => {
const updatedMainPanel = getNestedPanel("task-1", 0);
expect(updatedMainPanel.type).toBe("leaf");
});

it("removes split pane when moving its only tab to another pane", () => {
// Create a split: main-panel becomes a group with [original, new-panel]
usePanelLayoutStore
.getState()
.splitPanel(
"task-1",
"file-src/App.tsx",
"main-panel",
"main-panel",
"right",
);

// Verify we have a split with 2 panels
const mainPanelNode = getNestedPanel("task-1", 0);
expect(mainPanelNode.type).toBe("group");
if (mainPanelNode.type !== "group") return;
expect(mainPanelNode.children).toHaveLength(2);

// Get the new panel (contains file-src/App.tsx)
const newPanel = mainPanelNode.children[1];
expect(newPanel.type).toBe("leaf");
if (newPanel.type !== "leaf") return;

// Original panel (contains logs and file-src/Other.tsx)
const originalPanel = mainPanelNode.children[0];
expect(originalPanel.type).toBe("leaf");
if (originalPanel.type !== "leaf") return;

// Move the tab from the new panel back to the original panel
usePanelLayoutStore
.getState()
.moveTab("task-1", "file-src/App.tsx", newPanel.id, originalPanel.id);

// The split should be collapsed since new panel is now empty
const updatedMainPanel = getNestedPanel("task-1", 0);
expect(updatedMainPanel.type).toBe("leaf");

// The tab should now be in the original panel
if (updatedMainPanel.type === "leaf") {
expect(
updatedMainPanel.content.tabs.some(
(t) => t.id === "file-src/App.tsx",
),
).toBe(true);
}
});
});

describe("preview tabs", () => {
Expand Down