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 @@
+ |
+
+
+
+ |
+