From 4d0a1dd13c6c95d56732c51ae80f21d3345f7154 Mon Sep 17 00:00:00 2001 From: "F. N. Cheng" <153695068+FNCheng137@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:56:51 +0000 Subject: [PATCH] Feature / popup: open tab counters, lists, tab moving now work with tabs in non-current windows --- src/css/popup.css | 11 ++++- src/js/background/backgroundLogic.js | 8 +++- src/js/popup.js | 66 ++++++++++++++++++++-------- src/popup.html | 22 +++++++++- 4 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/css/popup.css b/src/css/popup.css index 8502e553..143690ea 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -242,7 +242,8 @@ body { [data-theme="dark"] img.clear-storage-icon, [data-theme="dark"] img.delete-assignment, [data-theme="dark"] #edit-sites-assigned .menu-icon, -[data-theme="dark"] #container-info-table .menu-icon, +[data-theme="dark"] #container-info-table-curr-windows .menu-icon, +[data-theme="dark"] #container-info-table-other-windows .menu-icon, [data-theme="dark"] #always-open .menu-icon, [data-theme="dark"] #always-open-in .menu-icon { filter: invert(0); @@ -2084,7 +2085,8 @@ h3.title { /* Maintain 1:1 square ratio for favicons of websites added to a specific container */ #edit-sites-assigned .menu-icon, -#container-info-table .menu-icon { +#container-info-table-curr-windows .menu-icon, +#container-info-table-other-windows .menu-icon { inline-size: 16px; } /* stylelint-enable no-descending-specificity */ @@ -2135,6 +2137,11 @@ hr { display: block; } +#container-info-table-other-windows hr { + margin-block: 4px; + margin-inline: 4px; +} + .sub-header-wrapper { margin-block-start: 12px; padding-inline: 16px; diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index e9d4b4fa..326f6402 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -255,7 +255,7 @@ const backgroundLogic = { }, async getTabs(options) { - const requiredArguments = ["cookieStoreId", "windowId"]; + const requiredArguments = ["cookieStoreId"]; this.checkArgs(requiredArguments, options, "getTabs"); const { cookieStoreId, windowId } = options; @@ -299,7 +299,7 @@ const backgroundLogic = { }, async moveTabsToWindow(options) { - const requiredArguments = ["cookieStoreId", "windowId"]; + const requiredArguments = ["cookieStoreId"]; this.checkArgs(requiredArguments, options, "moveTabsToWindow"); const { cookieStoreId, windowId } = options; @@ -397,6 +397,9 @@ const backgroundLogic = { const containerState = await identityState.storageArea.get(cookieStoreId); const openTabs = await browser.tabs.query({ cookieStoreId, + }); + const openTabsCurrWin = await browser.tabs.query({ + cookieStoreId, windowId }); identitiesOutput[cookieStoreId] = { @@ -404,6 +407,7 @@ const backgroundLogic = { hasOpenTabs: !!openTabs.length, numberOfHiddenTabs: containerState.hiddenTabs.length, numberOfOpenTabs: openTabs.length, + numberOfOpenTabsCurrWin: openTabsCurrWin.length, isIsolated: !!containerState.isIsolated }; return; diff --git a/src/js/popup.js b/src/js/popup.js index e2a12039..b0709557 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -200,7 +200,8 @@ const Logic = { icon: "default-tab", color: "default-tab", numberOfHiddenTabs: 0, - numberOfOpenTabs: 0 + numberOfOpenTabs: 0, + numberOfOpenTabsCurrWin: 0 }; // Handle old style rejection with null and also Promise.reject new style try { @@ -215,14 +216,14 @@ const Logic = { return activeTabs.length; }, - _disableMenuItem(message, elementToDisable = document.querySelector("#move-to-new-window")) { + _disableMenuItem(message, elementToDisable) { elementToDisable.setAttribute("title", message); elementToDisable.removeAttribute("tabindex"); elementToDisable.classList.remove("hover-highlight"); elementToDisable.classList.add("disabled-menu-item"); }, - _enableMenuItems(elementToEnable = document.querySelector("#move-to-new-window")) { + _enableMenuItems(elementToEnable) { elementToEnable.removeAttribute("title"); elementToEnable.setAttribute("tabindex", "0"); elementToEnable.classList.add("hover-highlight"); @@ -264,6 +265,7 @@ const Logic = { identity.hasHiddenTabs = stateObject.hasHiddenTabs; identity.numberOfHiddenTabs = stateObject.numberOfHiddenTabs; identity.numberOfOpenTabs = stateObject.numberOfOpenTabs; + identity.numberOfOpenTabsCurrWin = stateObject.numberOfOpenTabsCurrWin; identity.isIsolated = stateObject.isIsolated; } if (containerOrder) { @@ -924,15 +926,20 @@ Logic.registerPanel(P_CONTAINER_INFO, { } const moveTabsEl = document.querySelector("#move-to-new-window"); - const numTabs = await Logic.numTabs(); + const moveAllTabsEl = document.querySelector("#move-all-to-new-window"); if (incompatible) { - Logic._disableMenuItem("Moving container tabs is incompatible with Pulse, PageShot, and SnoozeTabs."); - return; - } else if (numTabs === 1) { - Logic._disableMenuItem("Cannot move a tab from a single-tab window."); + Logic._disableMenuItem("Moving container tabs is incompatible with Pulse, PageShot, and SnoozeTabs.", moveTabsEl); + Logic._disableMenuItem("Moving container tabs is incompatible with Pulse, PageShot, and SnoozeTabs.", moveAllTabsEl); return; } + Utils.addEnterHandler(moveAllTabsEl, async () => { + await browser.runtime.sendMessage({ + method: "moveTabsToWindow", + cookieStoreId: Logic.currentIdentity().cookieStoreId, + }); + window.close(); + }); Utils.addEnterHandler(moveTabsEl, async () => { await browser.runtime.sendMessage({ method: "moveTabsToWindow", @@ -972,10 +979,13 @@ Logic.registerPanel(P_CONTAINER_INFO, { trHasTabs.style.display = !identity.hasHiddenTabs && !identity.hasOpenTabs ? "none" : ""; } + const moveTabs = document.querySelector("#move-to-new-window"); + const moveTabsAll = document.querySelector("#move-all-to-new-window"); + if (identity.numberOfOpenTabsCurrWin === 0) { + Logic._disableMenuItem("No tabs available for this container in this window", moveTabs); + } if (identity.numberOfOpenTabs === 0) { - Logic._disableMenuItem("No tabs available for this container"); - } else { - Logic._enableMenuItems(); + Logic._disableMenuItem("No tabs available for this container", moveTabsAll); } this.intializeShowHide(identity); @@ -983,9 +993,9 @@ Logic.registerPanel(P_CONTAINER_INFO, { // Let's retrieve the list of tabs. const tabs = await browser.runtime.sendMessage({ method: "getTabs", - windowId: browser.windows.WINDOW_ID_CURRENT, cookieStoreId: Logic.currentIdentity().cookieStoreId }); + const currentWindowId = (await browser.windows.getCurrent()).id; const manageContainer = document.querySelector("#manage-container-link"); Utils.addEnterHandler(manageContainer, async () => { Logic.showPanel(P_CONTAINER_EDIT, identity); @@ -997,7 +1007,7 @@ Logic.registerPanel(P_CONTAINER_INFO, { Logic.showPanel(P_CLEAR_CONTAINER_STORAGE, identity); } }); - return this.buildOpenTabTable(tabs); + return this.buildOpenTabTable(tabs, currentWindowId); }, intializeShowHide(identity) { @@ -1029,13 +1039,24 @@ Logic.registerPanel(P_CONTAINER_INFO, { return; }, - buildOpenTabTable(tabs) { + buildOpenTabTable(tabs, currentWindowId) { // Let's remove all the previous tabs. - const table = document.getElementById("container-info-table"); - while (table.firstChild) { - table.firstChild.remove(); + const tableCurrWin = document.getElementById("container-info-table-curr-windows"); + while (tableCurrWin.firstChild) { + tableCurrWin.firstChild.remove(); + } + + const tableOtherWin = document.getElementById("container-info-table-other-windows"); + while (tableOtherWin.firstChild) { + tableOtherWin.firstChild.remove(); } + // Sort tabs by window id so that we'll be able to display horizontal rules between tabs in different windows below + tabs.sort((a, b) => a.windowId - b.windowId); + // For making horizontal rules between tabs of different windows + let prevWindowId = undefined; + const makeHorizontalRule = () => document.createElement("hr"); + // For each one, let's create a new line. const fragment = document.createDocumentFragment(); for (let tab of tabs) { // eslint-disable-line prefer-const @@ -1051,12 +1072,21 @@ Logic.registerPanel(P_CONTAINER_INFO, { `; tr.querySelector(".favicon").appendChild(Utils.createFavIconElement(tab.favIconUrl)); tr.setAttribute("tabindex", "0"); - table.appendChild(fragment); + if (tab.windowId === currentWindowId) { + tableCurrWin.appendChild(fragment); + } else { + if (prevWindowId !== undefined && tab.windowId !== prevWindowId) { + tableOtherWin.appendChild(makeHorizontalRule()); + } + prevWindowId = tab.windowId; + tableOtherWin.appendChild(fragment); + } // On click, we activate this tab. But only if this tab is active. if (!tab.hiddenState) { Utils.addEnterHandler(tr, async () => { await browser.tabs.update(tab.id, { active: true }); + await browser.windows.update(tab.windowId, { focused: true }); window.close(); }); diff --git a/src/popup.html b/src/popup.html index d33f0026..da58a1a6 100644 --- a/src/popup.html +++ b/src/popup.html @@ -229,6 +229,14 @@

+ + + + + + + @@ -251,7 +259,19 @@

- + + + + + +
+
+
+