From 4deb54b3a34b6487aa1b40f9252e57ab5ee8938f Mon Sep 17 00:00:00 2001 From: bruno Date: Mon, 27 Jul 2026 23:28:39 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20dragstart=20singleton=20TabManager=20?= =?UTF-8?q?=E2=80=94=20JSON=20au=20lieu=20de=20String(idx)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cause: le TabManager singleton (single-pane) écrasait text/plain avec String(idx) → getData retournait '1' au lieu du JSON {paneId,tabId,index} → le drop handler PaneManager échouait sur 'missing paneId/tabId' Fix: - singleton dragstart: JSON.stringify({paneId:0, tabId, index}) + effectAllowed - singleton drop: parse JSON, fallback legacy parseInt pour rétrocompatibilité - dragover/drop inchangés (indicateurs visuels) --- frontend/js/ui.js | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) 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);