diff --git a/frontend/js/ui.js b/frontend/js/ui.js index bd3ba34..077efaf 100644 --- a/frontend/js/ui.js +++ b/frontend/js/ui.js @@ -2267,9 +2267,11 @@ export const TabManager = { this._showTabContextMenu(e.clientX, e.clientY, tab.id); }); - // Drag and drop + // Drag and drop — JSON format for cross-pane compatibility el.addEventListener("dragstart", (e) => { - e.dataTransfer.setData("text/plain", String(idx)); + const payload = JSON.stringify({ paneId: 0, tabId: tab.id, index: idx }); + e.dataTransfer.setData("text/plain", payload); + e.dataTransfer.effectAllowed = "move"; el.classList.add("dragging"); }); el.addEventListener("dragend", () => { @@ -2293,13 +2295,29 @@ export const TabManager = { el.addEventListener("drop", (e) => { e.preventDefault(); document.querySelectorAll(".tab-drop-indicator").forEach(d => d.remove()); - const fromIdx = parseInt(e.dataTransfer.getData("text/plain")); - const rect = el.getBoundingClientRect(); - const mid = rect.left + rect.width / 2; - const toIdx = e.clientX < mid ? idx : idx + 1; - if (fromIdx !== toIdx && fromIdx !== toIdx - 1) { - this.moveTab(fromIdx, toIdx); + const raw = e.dataTransfer.getData("text/plain"); + if (!raw) return; + let dragData; + try { dragData = JSON.parse(raw); } catch (e2) { + // Legacy format: plain index string + const fromIdx = parseInt(raw); + if (isNaN(fromIdx)) return; + const rect = el.getBoundingClientRect(); + const mid = rect.left + rect.width / 2; + const toIdx = e.clientX < mid ? idx : idx + 1; + if (fromIdx !== toIdx && fromIdx !== toIdx - 1) + this.moveTab(fromIdx, toIdx); + return; } + if (dragData.paneId === 0) { + // Same pane — reorder + const rect = el.getBoundingClientRect(); + const mid = rect.left + rect.width / 2; + const toIdx = e.clientX < mid ? idx : idx + 1; + if (dragData.index !== toIdx && dragData.index !== toIdx - 1) + this.moveTab(dragData.index, toIdx); + } + // Cross-pane transfers handled by PaneManager's container drop zone }); this._tabList.appendChild(el);