From cd9ba2e9463242b0f7af36b3197ff821577cb55e Mon Sep 17 00:00:00 2001 From: Blake Gearin Date: Mon, 19 May 2025 20:53:29 -0700 Subject: [PATCH 1/4] feat: Add config to support more than 12 essential tabs --- src/browser/app/profile/features.inc | 3 ++- src/zen/tabs/ZenPinnedTabManager.mjs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/browser/app/profile/features.inc b/src/browser/app/profile/features.inc index 83993709f1..24ea572233 100644 --- a/src/browser/app/profile/features.inc +++ b/src/browser/app/profile/features.inc @@ -4,6 +4,7 @@ pref('zen.welcome-screen.seen', false, sticky); pref('zen.tabs.vertical', true); pref('zen.tabs.vertical.right-side', false); pref('zen.tabs.rename-tabs', true); +pref('zen.tabs.essentials.max', 12); pref('zen.tabs.show-newtab-vertical', true); pref('zen.ctrlTab.show-pending-tabs', false); @@ -68,7 +69,7 @@ pref('zen.urlbar.enable-overrides', false); // IMPORTANT: Remove once firefox 139 is released #ifdef XP_MACOSX pref('zen.view.experimental-rounded-view', false); -#else +#else pref('zen.view.experimental-rounded-view', true); #endif diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 96cd96627c..230746ff97 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -49,7 +49,7 @@ } class ZenPinnedTabManager extends ZenDOMOperatedFeature { - MAX_ESSENTIALS_TABS = 12; + MAX_ESSENTIALS_TABS = Services.prefs.getIntPref('zen.tabs.essentials.max', 12); async init() { if (!this.enabled) { From 144d55e9af7b3b631b69830b693977a0dd34b185 Mon Sep 17 00:00:00 2001 From: Blake Gearin Date: Fri, 30 May 2025 00:46:10 -0600 Subject: [PATCH 2/4] Add dynamic getter --- src/zen/tabs/ZenPinnedTabManager.mjs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index eb8f75d4b7..30387790b7 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -52,12 +52,18 @@ } class ZenPinnedTabManager extends ZenDOMOperatedFeature { - MAX_ESSENTIALS_TABS = Services.prefs.getIntPref('zen.tabs.essentials.max', 12); - async init() { if (!this.enabled) { return; } + + XPCOMUtils.defineLazyPreferenceGetter( + lazy, + 'zenTabsEssentialsMax', + 'zen.tabs.essentials.max', + 12 + ); + this._canLog = Services.prefs.getBoolPref('zen.pinned-tab-manager.debug', false); this.observer = new ZenPinnedTabsObserver(); this._initClosePinnedTabShortcut(); @@ -71,6 +77,10 @@ gZenWorkspaces._resolvePinnedInitialized(); } + get MAX_ESSENTIALS_TABS() { + return lazy.zenTabsEssentialsMax; + } + async onWorkspaceChange(newWorkspace, onInit) { if (!this.enabled || PrivateBrowsingUtils.isWindowPrivate(window)) { return; From 4a38fd8d305e98fea1740c08de43b53a19a00f2f Mon Sep 17 00:00:00 2001 From: Blake Gearin Date: Fri, 30 May 2025 04:37:10 -0600 Subject: [PATCH 3/4] Add overflow handling --- src/browser/app/profile/features.inc | 1 + src/zen/common/ZenUIManager.mjs | 21 ++++++++++ src/zen/tabs/zen-tabs/vertical-tabs.css | 56 ++++++++++++++++++++++++- src/zen/workspaces/ZenWorkspaces.mjs | 35 ++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/browser/app/profile/features.inc b/src/browser/app/profile/features.inc index 4e1b691d39..022760682a 100644 --- a/src/browser/app/profile/features.inc +++ b/src/browser/app/profile/features.inc @@ -5,6 +5,7 @@ pref('zen.tabs.vertical', true); pref('zen.tabs.vertical.right-side', false); pref('zen.tabs.rename-tabs', true); pref('zen.tabs.essentials.max', 12); +pref('zen.tabs.essentials.height', 0); // In vh units pref('zen.tabs.show-newtab-vertical', true); pref('zen.ctrlTab.show-pending-tabs', false); diff --git a/src/zen/common/ZenUIManager.mjs b/src/zen/common/ZenUIManager.mjs index ccad6bed05..70f69914a4 100644 --- a/src/zen/common/ZenUIManager.mjs +++ b/src/zen/common/ZenUIManager.mjs @@ -33,6 +33,13 @@ var gZenUIManager = { 'zen.urlbar.show-domain-only-in-sidebar', true ); + XPCOMUtils.defineLazyPreferenceGetter( + this, + 'essentialsHeight', + 'zen.tabs.essentials.height', + 0, + this._updateEssentialsHeight.bind(this) + ); gURLBar._zenTrimURL = this.urlbarTrim.bind(this); @@ -62,6 +69,7 @@ var gZenUIManager = { SessionStore.promiseAllWindowsRestored.then(() => { this._hasLoadedDOM = true; this.updateTabsToolbar(); + this._updateEssentialsHeight(); }); window.addEventListener('TabClose', this.onTabClose.bind(this)); @@ -69,6 +77,19 @@ var gZenUIManager = { gZenMediaController.init(); }, + _updateEssentialsHeight() { + const essentialsWrapper = document.getElementById('zen-essentials'); + if (!essentialsWrapper) return; + + essentialsWrapper.style.setProperty('--zen-essentials-height', `${this.essentialsHeight}vh`); + + if (this.essentialsHeight === 0) { + essentialsWrapper.setAttribute('data-essentials-height', '0'); + } else { + essentialsWrapper.removeAttribute('data-essentials-height'); + } + }, + handleMouseDown(event) { this._lastClickPosition = { clientX: event.clientX, diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index d1c4b00171..2d05e60e33 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -1253,6 +1253,11 @@ #zen-essentials { z-index: 1; + overflow: hidden; + + &:not([data-essentials-height="0"]) { + max-height: var(--zen-essentials-height); + } } /* Container for essential items (often pinned/favorite tabs) */ @@ -1260,12 +1265,19 @@ will-change: transform; padding-bottom: var(--zen-toolbox-padding); - overflow: hidden; + overflow-y: auto; + overflow-x: hidden; gap: 4px; transition: max-height 0.3s ease-out, grid-template-columns 0.3s ease-out; opacity: 1; + + #zen-essentials:not([data-essentials-height="0"]) & { + height: 100%; + max-height: var(--zen-essentials-height); + } + grid-template-columns: repeat(auto-fit, minmax(max(23.7%, 48px), 1fr)); &[data-hack-type='1'] { grid-template-columns: repeat(auto-fit, minmax(max(30%, 48px), auto)); @@ -1303,6 +1315,48 @@ &[cloned] { pointer-events: none; } + + &[overflowing] { + --zen-scrollbar-overflow-background: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.08)); + + &::before { + content: ''; + position: sticky; + top: 0; + left: 0; + right: 0; + width: 100%; + height: 1px; + opacity: 0; + pointer-events: none; + transition: opacity 0.1s; + background-color: var(--zen-scrollbar-overflow-background); + grid-column: 1 / -1; + } + + &::after { + content: ''; + position: sticky; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 1px; + opacity: 0; + pointer-events: none; + transition: opacity 0.1s; + background-color: var(--zen-scrollbar-overflow-background); + grid-column: 1 / -1; + } + } + + &[overflowing]:not([scrolledtostart])::before { + opacity: 1; + } + + &[overflowing]:not([scrolledtoend])::after { + opacity: 1; + } } /* Style tabs within the Essentials container (and a related welcome sidebar) */ diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index 6f65bb6b78..94b2c62b74 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -429,6 +429,17 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { essentialsContainer.setAttribute('container', container); document.getElementById('zen-essentials').appendChild(essentialsContainer); + this._updateEssentialsOverflow(essentialsContainer); + + essentialsContainer.addEventListener('scroll', () => { + this._updateEssentialsOverflow(essentialsContainer); + }); + + const resizeObserver = new ResizeObserver(() => { + this._updateEssentialsOverflow(essentialsContainer); + }); + resizeObserver.observe(essentialsContainer); + // Set an initial hidden state if the essentials section is not supposed // to be shown on the current workspace if ( @@ -441,6 +452,30 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { return essentialsContainer; } + _updateEssentialsOverflow(container) { + const isOverflowing = container.scrollHeight > container.clientHeight; + const isAtStart = container.scrollTop === 0; + const isAtEnd = (container.scrollTop + container.clientHeight) >= (container.scrollHeight - 1); + + if (isOverflowing) { + container.setAttribute('overflowing', 'true'); + } else { + container.removeAttribute('overflowing'); + } + + if (isAtStart) { + container.setAttribute('scrolledtostart', 'true'); + } else { + container.removeAttribute('scrolledtostart'); + } + + if (isAtEnd) { + container.setAttribute('scrolledtoend', 'true'); + } else { + container.removeAttribute('scrolledtoend'); + } + } + getCurrentEssentialsContainer() { const currentWorkspace = this.getActiveWorkspaceFromCache(); return this.getEssentialsSection(currentWorkspace?.containerTabId); From 98a2fc69e198c427b313408ee4cc57834a7d3a6a Mon Sep 17 00:00:00 2001 From: Blake Gearin Date: Sun, 22 Jun 2025 23:33:58 -0600 Subject: [PATCH 4/4] Fix merge conflict Signed-off-by: Blake Gearin --- src/zen/tabs/ZenPinnedTabManager.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index d719d165f5..2ca3428ad2 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -51,7 +51,7 @@ } } - class ZenPinnedTabManager extends ZenDOMOperatedFeature { + class nsZenPinnedTabManager extends ZenDOMOperatedFeature { async init() { if (!this.enabled) { return;