From 84a54c9dbbfb23ec935dd542fb14cd6c5baa7a45 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:05:37 +0100 Subject: [PATCH] feat: Add option to only sync up pinned tabs, b=closes #12173, p=#12246, c=common, tabs --- .../browser/preferences/zen-preferences.ftl | 3 ++ prefs/zen/window-sync.yaml | 3 ++ .../preferences/zenTabsManagement.inc.xhtml | 3 ++ src/zen/common/sys/ZenUIMigration.sys.mjs | 6 ++- .../sessionstore/ZenSessionManager.sys.mjs | 17 ++++++-- src/zen/sessionstore/ZenWindowSync.sys.mjs | 41 ++++++++++++++++--- src/zen/tabs/ZenPinnedTabManager.mjs | 3 -- 7 files changed, 63 insertions(+), 13 deletions(-) diff --git a/locales/en-US/browser/browser/preferences/zen-preferences.ftl b/locales/en-US/browser/browser/preferences/zen-preferences.ftl index 6eea5c8a5..7b304613c 100644 --- a/locales/en-US/browser/browser/preferences/zen-preferences.ftl +++ b/locales/en-US/browser/browser/preferences/zen-preferences.ftl @@ -59,6 +59,9 @@ zen-tabs-select-recently-used-on-close = zen-tabs-close-on-back-with-no-history = .label = Close tab and switch to its owner tab (or most recently used tab) when going back with no history +zen-settings-workspaces-sync-unpinned-tabs = + .label = Sync only pinned tabs in workspaces + zen-tabs-cycle-by-attribute = .label = Ctrl+Tab cycles within Essential or Workspace tabs only zen-tabs-cycle-ignore-pending-tabs = diff --git a/prefs/zen/window-sync.yaml b/prefs/zen/window-sync.yaml index 93b6fc95f..d19b95f5b 100644 --- a/prefs/zen/window-sync.yaml +++ b/prefs/zen/window-sync.yaml @@ -13,3 +13,6 @@ - name: zen.window-sync.open-link-in-new-unsynced-window value: true + +- name: zen.window-sync.sync-only-pinned-tabs + value: false diff --git a/src/browser/components/preferences/zenTabsManagement.inc.xhtml b/src/browser/components/preferences/zenTabsManagement.inc.xhtml index 037dcd88c..2cf7c9c9d 100644 --- a/src/browser/components/preferences/zenTabsManagement.inc.xhtml +++ b/src/browser/components/preferences/zenTabsManagement.inc.xhtml @@ -20,6 +20,9 @@ + diff --git a/src/zen/common/sys/ZenUIMigration.sys.mjs b/src/zen/common/sys/ZenUIMigration.sys.mjs index 1c6cac68e..1d6a8a7a7 100644 --- a/src/zen/common/sys/ZenUIMigration.sys.mjs +++ b/src/zen/common/sys/ZenUIMigration.sys.mjs @@ -12,7 +12,7 @@ ChromeUtils.defineESModuleGetters(lazy, { class nsZenUIMigration { PREF_NAME = "zen.ui.migration.version"; - MIGRATION_VERSION = 6; + MIGRATION_VERSION = 7; init(isNewProfile) { if (!isNewProfile) { @@ -127,6 +127,10 @@ class nsZenUIMigration { }, 1000); }); } + + _migrateV7() { + Services.prefs.setBoolPref("zen.window-sync.sync-only-pinned-tabs", true); + } } export var gZenUIMigration = new nsZenUIMigration(); diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index e965fd984..c812ffd07 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -11,6 +11,7 @@ ChromeUtils.defineESModuleGetters(lazy, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs", gWindowSyncEnabled: "resource:///modules/zen/ZenWindowSync.sys.mjs", + gSyncOnlyPinnedTabs: "resource:///modules/zen/ZenWindowSync.sys.mjs", DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", }); @@ -562,7 +563,17 @@ export class nsZenSessionManager { if (!sidebar) { return; } - aWindowData.tabs = sidebar.tabs || []; + // If we should only sync the pinned tabs, we should only edit the unpinned + // tabs in the window data and keep the pinned tabs from the window data, + // as they should be the same as the ones in the sidebar. + if (lazy.gSyncOnlyPinnedTabs) { + let pinnedTabs = (sidebar.tabs || []).filter((tab) => tab.pinned); + let unpinedWindowTabs = (aWindowData.tabs || []).filter((tab) => !tab.pinned); + aWindowData.tabs = [...pinnedTabs, ...unpinedWindowTabs]; + } else { + aWindowData.tabs = sidebar.tabs || []; + } + aWindowData.splitViewData = sidebar.splitViewData; aWindowData.folders = sidebar.folders; aWindowData.groups = sidebar.groups; @@ -599,8 +610,8 @@ export class nsZenSessionManager { this.#restoreWindowData(newWindow); } newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []); - if (!lazy.gWindowSyncEnabled) { - // Don't bring over any unpinned tabs if window sync is disabled. + if (!lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs) { + // Don't bring over any unpinned tabs if window sync is disabled or if syncing only pinned tabs. newWindow.tabs = newWindow.tabs.filter((tab) => tab.pinned); } diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index cb6c2094a..fd837cf6e 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -19,6 +19,12 @@ ChromeUtils.defineESModuleGetters(lazy, { }); XPCOMUtils.defineLazyPreferenceGetter(lazy, "gWindowSyncEnabled", "zen.window-sync.enabled", true); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "gSyncOnlyPinnedTabs", + "zen.window-sync.sync-only-pinned-tabs", + true +); XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.window-sync.log", true); const OBSERVING = ["browser-window-before-show", "sessionstore-windows-restored"]; @@ -241,7 +247,7 @@ class nsZenWindowSync { if (tab.pinned && !tab._zenPinnedInitialState) { await this.setPinnedTabState(tab); } - if (!lazy.gWindowSyncEnabled) { + if (!lazy.gWindowSyncEnabled || (lazy.gSyncOnlyPinnedTabs && !tab.pinned)) { tab._zenContentsVisible = true; } } @@ -953,6 +959,9 @@ class nsZenWindowSync { */ #delegateGenericSyncEvent(aEvent, flags = 0) { const item = aEvent.target; + if (lazy.gSyncOnlyPinnedTabs && !item.pinned) { + return; + } this.#syncItemForAllWindows(item, flags); } @@ -1089,16 +1098,19 @@ class nsZenWindowSync { /* Mark: Event Handlers */ - on_TabOpen(aEvent) { + on_TabOpen(aEvent, { duringPinning = false } = {}) { const tab = aEvent.target; const window = tab.ownerGlobal; const isUnsyncedWindow = window.gZenWorkspaces.privateWindowOrDisabled; - if (tab.id) { + if (tab.id && !duringPinning) { // This tab was opened as part of a sync operation. return; } tab._zenContentsVisible = true; tab.id = this.#newTabSyncId; + if (lazy.gSyncOnlyPinnedTabs && !tab.pinned) { + return; + } if (isUnsyncedWindow || !lazy.gWindowSyncEnabled) { return; } @@ -1116,6 +1128,9 @@ class nsZenWindowSync { SYNC_FLAG_ICON | SYNC_FLAG_LABEL | SYNC_FLAG_MOVE ); }); + if (duringPinning && tab?.splitView) { + this.on_ZenSplitViewTabsSplit({ target: tab.group }); + } this.#maybeFlushTabState(tab); } @@ -1137,7 +1152,8 @@ class nsZenWindowSync { } on_TabMove(aEvent) { - return this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_MOVE); + this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_MOVE); + return Promise.resolve(); } on_TabPinned(aEvent) { @@ -1149,7 +1165,14 @@ class nsZenWindowSync { if (!tab._zenPinnedInitialState) { tabStatePromise = this.setPinnedTabState(tab); } - return Promise.all([tabStatePromise, this.on_TabMove(aEvent)]); + return Promise.all([ + tabStatePromise, + this.on_TabMove(aEvent).then(() => { + if (lazy.gSyncOnlyPinnedTabs) { + this.on_TabOpen({ target: tab }, { duringPinning: true }); + } + }), + ]); } on_TabUnpinned(aEvent) { @@ -1160,7 +1183,11 @@ class nsZenWindowSync { delete targetTab._zenPinnedInitialState; } }); - return this.on_TabMove(aEvent); + return this.on_TabMove(aEvent).then(() => { + if (lazy.gSyncOnlyPinnedTabs) { + this.on_TabClose({ target: tab }); + } + }); } on_TabAddedToEssentials(aEvent) { @@ -1351,4 +1378,6 @@ class nsZenWindowSync { // eslint-disable-next-line mozilla/valid-lazy export const gWindowSyncEnabled = lazy.gWindowSyncEnabled; +// eslint-disable-next-line mozilla/valid-lazy +export const gSyncOnlyPinnedTabs = lazy.gSyncOnlyPinnedTabs; export const ZenWindowSync = new nsZenWindowSync(); diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index c7a8d694f..0c4877aa9 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -560,9 +560,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { // eslint-disable-next-line complexity moveToAnotherTabContainerIfNecessary(event, draggedTab, movingTabs, dropIndex) { - if (!this.enabled) { - return false; - } let newIndex = dropIndex; let fromDifferentWindow = false; movingTabs = Array.from(movingTabs || draggedTab)