Theseus: a tab can go back into another window

A tab moved to its own window had no way back. Dragging a tab onto
another window's tab bar now moves it there, page and all, and the
right-click menu lists the other windows under Move to window. A
window whose last tab leaves closes.
This commit is contained in:
Local Dev 2026-10-08 00:57:46 +02:00
parent 852d198eef
commit 3861925811

47
main.js
View file

@ -5179,6 +5179,37 @@ function moveTabToNewWindow(id, at) {
}));
return true;
}
// Moves a tab into an existing window (dest), page and all, at the end of
// its tab bar, and brings that window forward. A window left without tabs
// closes, like one whose last tab was closed.
function moveTabToWindow(id, dest) {
const t = tabById(id);
if (!t || !t.w || !windows.includes(t.w) || !dest || dest === t.w || !windows.includes(dest)) return false;
const src = t.w;
withWin(src, () => {
if (fsTabId === id) leaveHtmlFullscreen(t, true);
const mine = winTabs();
const i = mine.indexOf(t);
const next = mine[i + 1] || mine[i - 1];
try { win.contentView.removeChildView(t.view); } catch {}
t.w = null; // between windows
if (next) { if (activeId === id) setActive(next.id); else emitTabs(); }
});
t.w = dest;
// After the destination window's last tab in the shared list.
tabs.splice(tabs.indexOf(t), 1);
const there = winTabs(dest);
tabs.splice(there.length ? tabs.indexOf(there[there.length - 1]) + 1 : tabs.length, 0, t);
try { dest.win.contentView.addChildView(t.view); } catch {}
withWin(dest, () => {
if (curW === primaryW && leftPanelKey() && settings.sidebarPanelMode !== "push") raisePanelGrip();
setActive(id);
layout();
});
if (!winTabs(src).length) { try { src.win.close(); } catch {} }
try { dest.win.show(); dest.win.focus(); t.view.webContents.focus(); } catch {}
return true;
}
function closeTab(id) {
const owner = tabById(id)?.w;
if (owner && owner !== curW) return withWin(owner, () => closeTabHere(id));
@ -5811,6 +5842,16 @@ ipcMain.handle("tab-detach", (e, tabId, x, y) => {
const b = win.getBounds();
const inside = px >= b.x && px < b.x + b.width && py >= b.y && py < b.y + b.height;
if (inside && py < b.y + CHROME_H + 24) return false;
// Let go over another window's toolbar: the tab joins that window. Only
// when the point is off its own window, which is the one on top while the
// tab is dragged from it.
if (!inside) {
for (const w of windows) {
if (w === curW || !w.win || w.win.isDestroyed() || w.win.isMinimized()) continue;
const wb = w.win.getBounds();
if (px >= wb.x && px < wb.x + wb.width && py >= wb.y && py < wb.y + (w.chromeH || 84) + 24) return moveTabToWindow(t.id, w);
}
}
return moveTabToNewWindow(t.id, { x: px, y: py });
});
ipcMain.handle("tab-context-menu-popup", (e, tabId, rect) => {
@ -5850,6 +5891,12 @@ ipcMain.handle("tab-context-menu-popup", (e, tabId, rect) => {
{ label: "Reload", click: () => { try { t.view.webContents.reload(); } catch {} } },
{ label: "Duplicate", click: () => { if (t.url) createTab(t.url, { after: t.id }); else createTab(null, { after: t.id }); } },
{ label: "Move to new window", enabled: winTabs(t.w).length > 1, click: () => moveTabToNewWindow(t.id) },
...(windows.length > 1 ? [{ label: "Move to window", submenu: windows.filter((w) => w !== t.w).map((w) => {
const front = tabById(w === curW ? activeId : w.activeId);
const n = winTabs(w).length;
const name = String(front?.title || front?.url || "Window").slice(0, 60);
return { label: n > 1 ? `${name} (and ${n - 1} more)` : name, click: () => moveTabToWindow(t.id, w) };
}) }] : []),
{ label: "Group", submenu: groupSubmenu },
{ label: "Add to Bookmarks", enabled: !bmDisabled, click: () => {
if (bmDisabled) return;