diff --git a/locales/en-US/browser/browser/zen-general.ftl b/locales/en-US/browser/browser/zen-general.ftl index f88f215fd..d1229b04f 100644 --- a/locales/en-US/browser/browser/zen-general.ftl +++ b/locales/en-US/browser/browser/zen-general.ftl @@ -58,6 +58,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL! zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode diff --git a/locales/en-US/browser/browser/zen-split-view.ftl b/locales/en-US/browser/browser/zen-split-view.ftl index 8e9aaaed5..ee1e7081f 100644 --- a/locales/en-US/browser/browser/zen-split-view.ftl +++ b/locales/en-US/browser/browser/zen-split-view.ftl @@ -6,7 +6,7 @@ tab-zen-split-tabs = .label = { $tabCount -> [-1] Split out tab - [1] Join Tab (multiple selected tabs needed) + [1] Add split view... *[other] Join { $tabCount } Tabs } .accesskey = S diff --git a/prefs/zen/glance.yaml b/prefs/zen/glance.yaml index 299e616c7..cf73cddb9 100644 --- a/prefs/zen/glance.yaml +++ b/prefs/zen/glance.yaml @@ -18,4 +18,4 @@ value: 300 # in milliseconds - name: zen.glance.deactivate-docshell-during-animation - value: true + value: false diff --git a/src/browser/components/tabbrowser/content/tab-js.patch b/src/browser/components/tabbrowser/content/tab-js.patch index 06007dfe2..8ac45c8e5 100644 --- a/src/browser/components/tabbrowser/content/tab-js.patch +++ b/src/browser/components/tabbrowser/content/tab-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js -index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78a831e8ee 100644 +index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..3ac8d2facb224ae39d2199f2c5ec08b77e0c1dfd 100644 --- a/browser/components/tabbrowser/content/tab.js +++ b/browser/components/tabbrowser/content/tab.js @@ -21,6 +21,7 @@ @@ -151,7 +151,18 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78 on_click(event) { if (event.button != 0) { return; -@@ -620,11 +659,12 @@ +@@ -617,14 +656,23 @@ + trigger: "alt_click", + }); + } ++ if ( ++ !event.target.classList.contains("tab-close-button") && ++ !event.target.classList.contains("tab-icon-overlay") && ++ !event.target.classList.contains("tab-audio-button") && ++ !this.splitView ++ ) { ++ gZenViewSplitter.contextSplitTabs(this); ++ } return; } @@ -165,7 +176,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78 gBrowser.multiSelectedTabsCount > 0 && !event.target.classList.contains("tab-close-button") && !event.target.classList.contains("tab-icon-overlay") && -@@ -636,8 +676,9 @@ +@@ -636,8 +684,9 @@ } if ( @@ -177,7 +188,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78 ) { if (this.activeMediaBlocked) { if (this.multiselected) { -@@ -655,7 +696,7 @@ +@@ -655,7 +704,7 @@ return; } @@ -186,7 +197,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78 if (this.multiselected) { gBrowser.removeMultiSelectedTabs( lazy.TabMetrics.userTriggeredContext( -@@ -675,6 +716,14 @@ +@@ -675,6 +724,14 @@ // (see tabbrowser-tabs 'click' handler). gBrowser.tabContainer._blockDblClick = true; } @@ -201,7 +212,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78 } on_dblclick(event) { -@@ -698,6 +747,8 @@ +@@ -698,6 +755,8 @@ animate: true, triggeringEvent: event, }); diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 1e24da5bd..5becce548 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25b964ccbb 100644 +index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35e66ff55a 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -413,6 +413,7 @@ @@ -587,7 +587,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } catch (e) { console.error(e); } -@@ -5603,6 +5757,13 @@ +@@ -5603,6 +5757,14 @@ return; } @@ -597,11 +597,12 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 + this.selectedTab = newTab; + } + } ++ animate &&= !aTab.splitView; + let isVisibleTab = aTab.visible; // We have to sample the tab width now, since _beginRemoveTab might // end up modifying the DOM in such a way that aTab gets a new -@@ -5610,6 +5771,9 @@ +@@ -5610,6 +5772,9 @@ // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; let isLastTab = this.#isLastTabInWindow(aTab); @@ -611,7 +612,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -5621,13 +5785,14 @@ +@@ -5621,13 +5786,14 @@ telemetrySource, }) ) { @@ -627,7 +628,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 let lockTabSizing = !this.tabContainer.verticalMode && !aTab.pinned && -@@ -5658,7 +5823,13 @@ +@@ -5658,7 +5824,13 @@ // We're not animating, so we can cancel the animation stopwatch. Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId); aTab._closeTimeAnimTimerId = null; @@ -642,7 +643,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 return; } -@@ -5792,7 +5963,7 @@ +@@ -5792,7 +5964,7 @@ closeWindowWithLastTab != null ? closeWindowWithLastTab : !window.toolbar.visible || @@ -651,7 +652,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 if (closeWindow) { // We've already called beforeunload on all the relevant tabs if we get here, -@@ -5816,6 +5987,7 @@ +@@ -5816,6 +5988,7 @@ newTab = true; } @@ -659,7 +660,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -5856,13 +6028,7 @@ +@@ -5856,13 +6029,7 @@ aTab._mouseleave(); if (newTab) { @@ -674,7 +675,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } else { TabBarVisibility.update(); } -@@ -5995,6 +6161,7 @@ +@@ -5995,6 +6162,7 @@ this.tabs[i]._tPos = i; } @@ -682,7 +683,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 if (!this._windowIsClosing) { // update tab close buttons state this.tabContainer._updateCloseButtons(); -@@ -6225,6 +6392,7 @@ +@@ -6225,6 +6393,7 @@ } let excludeTabs = new Set(aExcludeTabs); @@ -690,7 +691,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 // If this tab has a successor, it should be selectable, since // hiding or closing a tab removes that tab as a successor. -@@ -6237,15 +6405,22 @@ +@@ -6237,15 +6406,22 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -715,7 +716,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 let tab = this.tabContainer.findNextTab(aTab, { direction: 1, filter: _tab => remainingTabs.includes(_tab), -@@ -6259,7 +6434,7 @@ +@@ -6259,7 +6435,7 @@ } if (tab) { @@ -724,7 +725,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } // If no qualifying visible tab was found, see if there is a tab in -@@ -6280,7 +6455,7 @@ +@@ -6280,7 +6456,7 @@ }); } @@ -733,7 +734,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } _blurTab(aTab) { -@@ -6291,7 +6466,7 @@ +@@ -6291,7 +6467,7 @@ * @returns {boolean} * False if swapping isn't permitted, true otherwise. */ @@ -742,7 +743,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 // Do not allow transfering a private tab to a non-private window // and vice versa. if ( -@@ -6345,6 +6520,7 @@ +@@ -6345,6 +6521,7 @@ // fire the beforeunload event in the process. Close the other // window if this was its last tab. if ( @@ -750,7 +751,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 !remoteBrowser._beginRemoveTab(aOtherTab, { adoptedByTab: aOurTab, closeWindowWithLastTab: true, -@@ -6356,7 +6532,7 @@ +@@ -6356,7 +6533,7 @@ // If this is the last tab of the window, hide the window // immediately without animation before the docshell swap, to avoid // about:blank being painted. @@ -759,7 +760,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 if (closeWindow) { let win = aOtherTab.ownerGlobal; win.windowUtils.suppressAnimation(true); -@@ -6484,11 +6660,13 @@ +@@ -6484,11 +6661,13 @@ } // Finish tearing down the tab that's going away. @@ -773,7 +774,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 this.setTabTitle(aOurTab); -@@ -6690,10 +6868,10 @@ +@@ -6690,10 +6869,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -786,7 +787,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -6753,7 +6931,8 @@ +@@ -6753,7 +6932,8 @@ * @param {object} [aOptions={}] * Key-value pairs that will be serialized into the features string. */ @@ -796,7 +797,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 if (this.tabs.length == 1) { return null; } -@@ -6770,7 +6949,7 @@ +@@ -6770,7 +6950,7 @@ // tell a new window to take the "dropped" tab let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); args.appendElement(aTab.splitview ?? aTab); @@ -805,7 +806,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 private: PrivateBrowsingUtils.isWindowPrivate(window), features: Object.entries(aOptions) .map(([key, value]) => `${key}=${value}`) -@@ -6778,6 +6957,8 @@ +@@ -6778,6 +6958,8 @@ openerWindow: window, args, }); @@ -814,7 +815,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } /** -@@ -6890,7 +7071,7 @@ +@@ -6890,7 +7072,7 @@ * `true` if element is a `` */ isTabGroup(element) { @@ -823,7 +824,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } /** -@@ -6975,8 +7156,8 @@ +@@ -6975,8 +7157,8 @@ } // Don't allow mixing pinned and unpinned tabs. @@ -834,7 +835,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -7005,13 +7186,19 @@ +@@ -7005,13 +7187,19 @@ this.#handleTabMove( element, () => { @@ -856,7 +857,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 let useAfter = false; if (this.isTab(element)) { useAfter = neighbor && tabIndex > element._tPos; -@@ -7076,23 +7263,31 @@ +@@ -7076,23 +7264,31 @@ #moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) { if (this.isTabGroupLabel(targetElement)) { targetElement = targetElement.group; @@ -894,7 +895,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } else if (!element.pinned && targetElement && targetElement.pinned) { // If the caller asks to move an unpinned element next to a pinned // tab, move the unpinned element to be the first unpinned element -@@ -7105,12 +7300,35 @@ +@@ -7105,12 +7301,35 @@ // move the tab group right before the first unpinned tab. // 4. Moving a tab group and the first unpinned tab is grouped: // move the tab group right before the first unpinned tab's tab group. @@ -931,7 +932,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 // We want to include the splitview wrapper if it's the targetElement, but // not in the case where we want to reverse tabs within the same splitview. -@@ -7119,6 +7337,7 @@ +@@ -7119,6 +7338,7 @@ } let getContainer = () => @@ -939,7 +940,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 element.pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; -@@ -7127,11 +7346,15 @@ +@@ -7127,11 +7347,15 @@ element, () => { if (moveBefore) { @@ -956,7 +957,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } }, metricsContext -@@ -7205,10 +7428,10 @@ +@@ -7205,10 +7429,10 @@ * @param {TabMetricsContext} [metricsContext] */ moveTabToExistingGroup(aTab, aGroup, metricsContext) { @@ -969,7 +970,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 return; } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -7281,6 +7504,7 @@ +@@ -7281,6 +7505,7 @@ let state = { tabIndex: tab._tPos, @@ -977,7 +978,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 }; if (tab.visible) { state.elementIndex = tab.elementIndex; -@@ -7312,7 +7536,7 @@ +@@ -7312,7 +7537,7 @@ let changedSplitView = previousTabState.splitViewId != currentTabState.splitViewId; @@ -986,7 +987,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 tab.dispatchEvent( new CustomEvent("TabMove", { bubbles: true, -@@ -7354,6 +7578,10 @@ +@@ -7354,6 +7579,10 @@ moveActionCallback(); @@ -997,7 +998,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -7404,7 +7632,22 @@ +@@ -7404,7 +7633,22 @@ * @returns {object} * The new tab in the current window, null if the tab couldn't be adopted. */ @@ -1021,7 +1022,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 // Swap the dropped tab with a new one we create and then close // it in the other window (making it seem to have moved between // windows). We also ensure that the tab we create to swap into has -@@ -7447,6 +7690,8 @@ +@@ -7447,6 +7691,8 @@ } params.skipLoad = true; let newTab = this.addWebTab("about:blank", params); @@ -1030,7 +1031,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 aTab.container.tabDragAndDrop.finishAnimateTabMove(); -@@ -8149,7 +8394,7 @@ +@@ -8149,7 +8395,7 @@ // preventDefault(). It will still raise the window if appropriate. return; } @@ -1039,7 +1040,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 window.focus(); aEvent.preventDefault(); } -@@ -8166,7 +8411,6 @@ +@@ -8166,7 +8412,6 @@ on_TabGroupCollapse(aEvent) { aEvent.target.tabs.forEach(tab => { @@ -1047,7 +1048,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 }); } -@@ -8500,7 +8744,9 @@ +@@ -8500,7 +8745,9 @@ let filter = this._tabFilters.get(tab); if (filter) { @@ -1057,7 +1058,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 let listener = this._tabListeners.get(tab); if (listener) { -@@ -9306,6 +9552,7 @@ +@@ -9306,6 +9553,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -1065,7 +1066,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -9386,6 +9633,7 @@ +@@ -9386,6 +9634,7 @@ // known defaults. Note we use the original URL since about:newtab // redirects to a prerendered page. const shouldRemoveFavicon = @@ -1073,7 +1074,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 !this.mBrowser.mIconURL && !ignoreBlank && !(originalLocation.spec in FAVICON_DEFAULTS); -@@ -9560,13 +9808,6 @@ +@@ -9560,13 +9809,6 @@ this.mBrowser.originalURI = aRequest.originalURI; } @@ -1087,7 +1088,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25 } let userContextId = this.mBrowser.getAttribute("usercontextid") || 0; -@@ -10450,7 +10691,7 @@ var TabContextMenu = { +@@ -10450,7 +10692,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; diff --git a/src/browser/themes/shared/tabbrowser/tabs-css.patch b/src/browser/themes/shared/tabbrowser/tabs-css.patch index b75e177ea..c639f757b 100644 --- a/src/browser/themes/shared/tabbrowser/tabs-css.patch +++ b/src/browser/themes/shared/tabbrowser/tabs-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css -index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d3409383217e2ca 100644 +index 203b546933842f4b0134188fda020c4db4dcd0d2..0d67deabac2984574636248a16e58d1669f2e1a4 100644 --- a/browser/themes/shared/tabbrowser/tabs.css +++ b/browser/themes/shared/tabbrowser/tabs.css @@ -24,7 +24,7 @@ @@ -97,7 +97,15 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 &:is([soundplaying], [muted], [activemedia-blocked]) { display: flex; } -@@ -1614,7 +1606,7 @@ tab-group { +@@ -1048,7 +1040,6 @@ tab-split-view-wrapper[dragtarget] { + .tabbrowser-tab:is([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected]), + .tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected]), + #tabbrowser-tabs[orient="vertical"] .tabbrowser-tab > .tab-stack > .tab-content[titlechanged]:not([selected]) { +- background-image: radial-gradient(circle, var(--tab-attention-dot-color), var(--tab-attention-dot-color) 2px, transparent 2px); + background-position: center bottom 6.5px; + background-size: 4px 4px; + background-repeat: no-repeat; +@@ -1614,7 +1605,7 @@ tab-group { } #tabbrowser-tabs[orient="vertical"][expanded] { @@ -106,7 +114,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 &[movingtab][movingtab-addToGroup]:not([movingtab-group], [movingtab-ungroup]) tab-group > .tabbrowser-tab:is(:active, [multiselected]) { margin-inline-start: var(--space-medium); } -@@ -2089,7 +2081,7 @@ tab-group { +@@ -2089,7 +2080,7 @@ tab-group { } } @@ -115,7 +123,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 #vertical-tabs-newtab-button { appearance: none; min-height: var(--tab-min-height); -@@ -2100,7 +2092,7 @@ tab-group { +@@ -2100,7 +2091,7 @@ tab-group { margin-inline: var(--tab-inner-inline-margin); #tabbrowser-tabs[orient="vertical"]:not([expanded]) & > .toolbarbutton-text { @@ -124,7 +132,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 } &:hover { -@@ -2124,7 +2116,7 @@ tab-group { +@@ -2124,7 +2115,7 @@ tab-group { * flex container. #tabs-newtab-button is a child of the arrowscrollbox where * we don't want a gap (between tabs), so we have to add some margin. */ @@ -133,7 +141,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 margin-block: var(--tab-block-margin); } -@@ -2312,7 +2304,6 @@ tab-group { +@@ -2312,7 +2303,6 @@ tab-group { &:not([expanded]) { .tabbrowser-tab[pinned] { @@ -141,7 +149,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 } .tab-background { -@@ -2352,8 +2343,8 @@ tab-group { +@@ -2352,8 +2342,8 @@ tab-group { display: block; position: absolute; inset: auto; @@ -152,7 +160,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 &:-moz-window-inactive { background-image: -@@ -2438,9 +2429,6 @@ tab-group { +@@ -2438,9 +2428,6 @@ tab-group { :root:not([privatebrowsingmode]) :is(toolbarbutton, toolbarpaletteitem) ~ #tabbrowser-tabs, :root[privatebrowsingmode] :is(toolbarbutton:not(#firefox-view-button), toolbarpaletteitem:not(#wrapper-firefox-view-button)) ~ #tabbrowser-tabs { @@ -162,7 +170,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938 } :root[privatebrowsingmode] :is(#firefox-view-button, #menu_openFirefoxView) { -@@ -2472,7 +2460,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button { +@@ -2472,7 +2459,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button { list-style-image: url(chrome://global/skin/icons/plus.svg); } diff --git a/src/zen/common/modules/ZenUIManager.mjs b/src/zen/common/modules/ZenUIManager.mjs index ff0af0495..fa9fe5591 100644 --- a/src/zen/common/modules/ZenUIManager.mjs +++ b/src/zen/common/modules/ZenUIManager.mjs @@ -101,10 +101,12 @@ window.gZenUIManager = { ) { const yValues = rawKeyframes.y || []; const xValues = rawKeyframes.x || []; - const scaleValues = rawKeyframes.scale || []; + const scaleYValues = rawKeyframes.scaleY || []; + const scaleXValues = rawKeyframes.scaleX || []; delete rawKeyframes.y; delete rawKeyframes.x; - delete rawKeyframes.scale; + delete rawKeyframes.scaleY; + delete rawKeyframes.scaleX; rawKeyframes.transform = []; if ( yValues.length !== 0 && @@ -116,14 +118,17 @@ window.gZenUIManager = { const keyframeLength = Math.max( yValues.length, xValues.length, - scaleValues.length + scaleYValues.length, + scaleXValues.length ); for (let i = 0; i < keyframeLength; i++) { const y = yValues[i] !== undefined ? `translateY(${yValues[i]}px)` : ""; const x = xValues[i] !== undefined ? `translateX(${xValues[i]}px)` : ""; - const scale = - scaleValues[i] !== undefined ? `scale(${scaleValues[i]})` : ""; - rawKeyframes.transform.push(`${x} ${y} ${scale}`.trim()); + const scaleY = + scaleYValues[i] !== undefined ? `scaleY(${scaleYValues[i]})` : ""; + const scaleX = + scaleXValues[i] !== undefined ? `scaleX(${scaleXValues[i]})` : ""; + rawKeyframes.transform.push(`${x} ${y} ${scaleX} ${scaleY}`.trim()); } } let keyframes = []; diff --git a/src/zen/common/styles/zen-animations.css b/src/zen/common/styles/zen-animations.css index 9b0185ada..8e5aaee3e 100644 --- a/src/zen/common/styles/zen-animations.css +++ b/src/zen/common/styles/zen-animations.css @@ -58,18 +58,15 @@ } @keyframes zen-progress-bar-pulse { - 0% { - transform: scale(0.8) translate(-50%, -50%); + from { + transform: scale(0.85) translate(-50%, -50%); opacity: 0.6; } - 50% { + + to { transform: scale(0.95) translate(-50%, -50%); opacity: 1; } - 100% { - transform: scale(0.8) translate(-50%, -50%); - opacity: 0.6; - } } @keyframes zen-progress-bar-long-load { diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index 2feb7948b..28373c559 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -695,6 +695,7 @@ :root:is([zen-no-padding="true"], [inDOMFullscreen="true"]) & { top: 4px; } + left: 50%; transform: translate(-50%, -50%) scale(0); background: var(--zen-loading-progress-bar-color); @@ -707,8 +708,10 @@ transform .3s ease-in-out; overflow: clip; pointer-events: none; - animation: zen-progress-bar-pulse 1.5s linear infinite forwards; + animation: zen-progress-bar-pulse 1s ease-in-out infinite forwards; + animation-direction: alternate; transform-origin: 0 0; + contain: content; &[long-load="true"] { opacity: 1; diff --git a/src/zen/common/sys/ui/ZenProgressBar.sys.mjs b/src/zen/common/sys/ui/ZenProgressBar.sys.mjs index e8fa8157e..d1add0fc6 100644 --- a/src/zen/common/sys/ui/ZenProgressBar.sys.mjs +++ b/src/zen/common/sys/ui/ZenProgressBar.sys.mjs @@ -50,7 +50,7 @@ export class ZenProgressBar extends ZenUIComponent { this.window.gZenUIManager.elementAnimate( this.#element, { - opacity: [0, 0.6], + opacity: [0, 0.8], }, { duration: 400, diff --git a/src/zen/drag-and-drop/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js index 0e87be0be..30154a4db 100644 --- a/src/zen/drag-and-drop/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -119,6 +119,10 @@ init() { super.init(); this.handle_windowDragEnter = this.handle_windowDragEnter.bind(this); + gZenWorkspaces.workspaceIcons.addEventListener( + "dragover", + this.handle_spaceIconDragOver.bind(this) + ); window.addEventListener( "dragleave", this.handle_windowDragLeave.bind(this), @@ -676,6 +680,31 @@ } } + #onSpaceChanged(spaceChanged, dt) { + if (AppConstants.platform !== "macosx") { + // See the hack in #createDragImageForTabs for more details which + // explains why we need to do this on non-macOS platforms. + return; + } + let tabs = this.originalDragImageArgs[0].children; + const { isDarkMode, isExplicitMode } = + gZenThemePicker.getGradientForWorkspace(spaceChanged, { + getGradient: false, + }); + for (let tab of tabs) { + if (isExplicitMode) { + tab.style.colorScheme = isDarkMode ? "dark" : "light"; + } else { + tab.style.colorScheme = ""; + } + } + requestAnimationFrame(() => { + requestAnimationFrame(() => { + dt.updateDragImage(...this.originalDragImageArgs); + }); + }); + } + #handle_sidebarDragOver(event) { const dt = event.dataTransfer; const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); @@ -696,28 +725,7 @@ /* Disable wrapping */ true ) .then(spaceChanged => { - if (AppConstants.platform !== "macosx") { - // See the hack in #createDragImageForTabs for more details which - // explains why we need to do this on non-macOS platforms. - return; - } - let tabs = this.originalDragImageArgs[0].children; - const { isDarkMode, isExplicitMode } = - gZenThemePicker.getGradientForWorkspace(spaceChanged, { - getGradient: false, - }); - for (let tab of tabs) { - if (isExplicitMode) { - tab.style.colorScheme = isDarkMode ? "dark" : "light"; - } else { - tab.style.colorScheme = ""; - } - } - requestAnimationFrame(() => { - requestAnimationFrame(() => { - dt.updateDragImage(...this.originalDragImageArgs); - }); - }); + this.#onSpaceChanged(spaceChanged, dt); }); this.#changeSpaceTimer = null; }, this._dndSwitchSpaceDelay); @@ -727,6 +735,27 @@ } } + handle_spaceIconDragOver(event) { + const dt = event.dataTransfer; + const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); + if (draggedTab.hasAttribute("zen-essential")) { + return; + } + const target = event.target; + const spaceId = target.getAttribute("zen-workspace-id"); + if (!spaceId) { + return; + } + this.clearDragOverVisuals(); + const currentSpaceId = gZenWorkspaces.activeWorkspace; + if (spaceId === currentSpaceId || gZenWorkspaces._animatingChange) { + return; + } + gZenWorkspaces.changeWorkspaceWithID(spaceId).then(spaceChanged => { + this.#onSpaceChanged(spaceChanged, dt); + }); + } + #handle_tabDragOverToSplit(event) { if (!this._dndSplitEnabled) { return; @@ -1238,18 +1267,24 @@ ) { let lastTab = gBrowser.tabs.at(-1); let pinnedTabsCount = gBrowser._numVisiblePinTabsWithoutCollapsed; + let isHoveringSeparator = + event.target.parentElement.classList.contains( + "zen-workspace-pinned-tabs-section" + ); // Only if there are no normal tabs to drop after showIndicatorUnderNewTabButton = lastTab.hasAttribute("zen-empty-tab"); - let useLastPinnd = - (hoveringPeriphery || - (showIndicatorUnderNewTabButton && - !(pinnedTabsCount - gBrowser._numZenEssentials))) && - Services.prefs.getBoolPref("zen.view.show-newtab-button-top"); + let useLastPinned = + (showIndicatorUnderNewTabButton && + !(pinnedTabsCount - gBrowser._numZenEssentials) && + Services.prefs.getBoolPref("zen.view.show-newtab-button-top")) || + isHoveringSeparator; dropElement = - (useLastPinnd - ? this._tabbrowserTabs.ariaFocusableItems.at(pinnedTabsCount) + (useLastPinned + ? this._tabbrowserTabs.ariaFocusableItems.at( + pinnedTabsCount ? pinnedTabsCount - 1 : 0 + ) : this._tabbrowserTabs.ariaFocusableItems.at(-1)) || lastTab; } } diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index d5a579c4a..08a60a24c 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -744,8 +744,14 @@ class nsZenFolders extends nsZenDOMOperatedFeature { const onKeyDown = event => { // Arrow down and up to navigate through the list - if (event.key === "ArrowDown" || event.key === "ArrowUp") { + if ( + event.key === "ArrowDown" || + event.key === "ArrowUp" || + event.key === "Tab" + ) { event.preventDefault(); + let isUp = + event.key === "ArrowUp" || (event.key === "Tab" && event.shiftKey); const items = Array.from(tabsList.children).filter( item => !item.hidden ); @@ -755,9 +761,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature { let index = items.indexOf( tabsList.querySelector(".folders-tabs-list-item[selected]") ); - if (event.key === "ArrowDown") { + if (!isUp) { index = (index + 1) % items.length; - } else if (event.key === "ArrowUp") { + } else { index = (index - 1 + items.length) % items.length; } items.forEach(item => item.removeAttribute("selected")); diff --git a/src/zen/glance/ZenGlanceManager.mjs b/src/zen/glance/ZenGlanceManager.mjs index cd6c8d5d2..9c738cd01 100644 --- a/src/zen/glance/ZenGlanceManager.mjs +++ b/src/zen/glance/ZenGlanceManager.mjs @@ -39,8 +39,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { ARC_HEIGHT_RATIO: 0.2, // Arc height = distance * ratio (capped at MAX_ARC_HEIGHT) }); - #GLANCE_ANIMATION_DURATION = - Services.prefs.getIntPref("zen.glance.animation-duration") / 1000; + #GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref( + "zen.glance.animation-duration" + ); init() { this.#setupEventListeners(); @@ -294,7 +295,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { { duration: 0.2, type: "spring", - delay: this.#GLANCE_ANIMATION_DURATION - 0.2, + delay: this.#GLANCE_ANIMATION_DURATION / 1000 - 0.2, bounce: 0, } ); @@ -450,7 +451,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { opacity: [1, 0.3], }, { - duration: this.#GLANCE_ANIMATION_DURATION, + duration: this.#GLANCE_ANIMATION_DURATION / 1000, type: "spring", bounce: 0.2, } @@ -536,13 +537,13 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { // nice fade-in effect to the content. But if it doesn't exist, // we just fall back to always showing the browser directly. if (data.elementData) { - gZenUIManager.motion - .animate( + gZenUIManager + .elementAnimate( this.contentWrapper, { opacity: [0, 1] }, { duration: this.#GLANCE_ANIMATION_DURATION / 4, - easing: "easeInOut", + easing: "ease-in-out", } ) .then(() => { @@ -559,12 +560,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { browserElement.zenModeActive = false; browserElement.docShellIsActive = false; } - gZenUIManager.motion - .animate(this.browserWrapper, arcSequence, { + gZenUIManager + .elementAnimate(this.browserWrapper, arcSequence, { duration: gZenUIManager.testingEnabled ? 0 : this.#GLANCE_ANIMATION_DURATION, - ease: "easeInOut", + easing: "ease-in-out", }) .then(() => { if (shouldDeactivateDocShell) { @@ -971,7 +972,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { { duration: 0.2, type: "spring", - bounce: this.#GLANCE_ANIMATION_DURATION - 0.1, + bounce: this.#GLANCE_ANIMATION_DURATION / 1000 - 0.1, } ) .then(() => { @@ -1019,7 +1020,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { opacity: [0.3, 1], }, { - duration: this.#GLANCE_ANIMATION_DURATION / 1.5, + duration: this.#GLANCE_ANIMATION_DURATION / 1000 / 1.5, type: "spring", bounce: 0, } @@ -1062,10 +1063,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.browserWrapper.style.width = ""; this.browserWrapper.style.height = ""; - gZenUIManager.motion - .animate(this.browserWrapper, arcSequence, { + gZenUIManager + .elementAnimate(this.browserWrapper, arcSequence, { duration: this.#GLANCE_ANIMATION_DURATION, - ease: "easeOut", + easing: "ease-out", }) .then(() => { // Remove element preview after closing animation @@ -1573,9 +1574,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.#handleZenFolderPinning(); gBrowser.moveTabAfter(this.#currentTab, this.#currentParentTab); - const browserRect = window.windowUtils.getBoundsWithoutFlushing( - this.browserWrapper - ); this.#prepareTabForFullOpen(); const sidebarButtons = this.browserWrapper.querySelector( @@ -1596,7 +1594,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { return; } - await this.#animateFullOpen(browserRect); + await this.#animateFullOpen(); this.finishOpeningGlance(); } @@ -1632,31 +1630,27 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { /** * Animate the full opening process - * - * @param {object} browserRect - The browser rectangle */ - async #animateFullOpen(browserRect) { + async #animateFullOpen() { // Write styles early to avoid flickering - this.browserWrapper.style.opacity = 1; - this.browserWrapper.style.width = `${browserRect.width}px`; - this.browserWrapper.style.height = `${browserRect.height}px`; + this.browserWrapper.style.width = "100%"; + this.browserWrapper.style.height = "100%"; - await gZenUIManager.motion.animate( - this.browserWrapper, + await gZenUIManager.elementAnimate( + this.browserWrapper.parentElement, { - width: ["80%", "100%"], - height: ["100%", "100%"], + scale: [1, 1.005, 1], }, { - duration: this.#GLANCE_ANIMATION_DURATION, - type: "spring", - bounce: 0, + duration: 250, + easing: "ease-in-out", } ); + this.browserWrapper.style.scale = ""; + this.browserWrapper.style.opacity = ""; this.browserWrapper.style.width = ""; this.browserWrapper.style.height = ""; - this.browserWrapper.style.opacity = ""; gZenViewSplitter.deactivateCurrentSplitView({ removeDeckSelected: true }); } diff --git a/src/zen/glance/zen-glance.css b/src/zen/glance/zen-glance.css index bca099ab7..44bffb1a0 100644 --- a/src/zen/glance/zen-glance.css +++ b/src/zen/glance/zen-glance.css @@ -9,6 +9,9 @@ display: flex; z-index: 999; + will-change: transform, opacity; + contain: content; + top: 15px; padding: 12px; gap: 12px; @@ -121,6 +124,7 @@ &:not([has-finished-animation="true"]) { will-change: transform; + contain: layout size; transform-origin: 0 0; #statuspanel { diff --git a/src/zen/spaces/ZenSpaceManager.mjs b/src/zen/spaces/ZenSpaceManager.mjs index daa8f3f08..dc9c2b660 100644 --- a/src/zen/spaces/ZenSpaceManager.mjs +++ b/src/zen/spaces/ZenSpaceManager.mjs @@ -1602,7 +1602,6 @@ class nsZenWorkspaces { } #prepareNewWorkspace(space) { - document.documentElement.setAttribute("zen-workspace-id", space.uuid); let tabCount = 0; for (let tab of gBrowser.tabs) { const isEssential = tab.getAttribute("zen-essential") === "true"; @@ -1643,12 +1642,12 @@ class nsZenWorkspaces { async changeWorkspaceWithID(workspaceID, ...args) { const workspace = this.getWorkspaceFromId(workspaceID); - await this.changeWorkspace(workspace, ...args); + return await this.changeWorkspace(workspace, ...args); } async changeWorkspace(workspace, ...args) { if (!this.workspaceEnabled) { - return; + return workspace; } this.#currentSpaceSwitchContext.animations.forEach(animation => { animation.complete(); @@ -1668,6 +1667,7 @@ class nsZenWorkspaces { } this.#inChangingWorkspace = false; resolve(); + return workspace; } _cancelSwipeAnimation() { @@ -2429,9 +2429,6 @@ class nsZenWorkspaces { tabToSelect, { previousWorkspaceIndex, previousWorkspace } = {} ) { - // Update document state - document.documentElement.setAttribute("zen-workspace-id", workspace.uuid); - // Recalculate new tab observers gBrowser.tabContainer.observe( null, @@ -2975,8 +2972,7 @@ class nsZenWorkspaces { } let nextWorkspace = workspaces[targetIndex]; - await this.changeWorkspace(nextWorkspace, { whileScrolling }); - return nextWorkspace; + return await this.changeWorkspace(nextWorkspace, { whileScrolling }); } #initializeWorkspaceTabContextMenus() { @@ -3000,9 +2996,8 @@ class nsZenWorkspaces { ? gBrowser.selectedTabs : [TabContextMenu.contextTab]; document.getElementById("tabContextMenu").hidePopup(); - const previousWorkspaceID = - document.documentElement.getAttribute("zen-workspace-id"); for (let tab of tabs) { + const previousWorkspaceID = tab.getAttribute("zen-workspace-id"); this.moveTabToWorkspace(tab, workspaceID); if (this.lastSelectedWorkspaceTabs[previousWorkspaceID] === tab) { // This tab is no longer the last selected tab in the previous workspace because it's being moved to diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index dbc99e678..521ecd1d0 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -1239,13 +1239,24 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { /** * Splits the selected tabs. + * + * @param {Tab|null} otherTabHint - An optional hint for another tab to split with (used for glance tabs). */ - contextSplitTabs() { + contextSplitTabs(otherTabHint = null) { let tabs; - if (TabContextMenu.contextTab.multiselected) { + let currentTab = TabContextMenu.contextTab || gBrowser.selectedTab; + if (currentTab.multiselected) { tabs = gBrowser.selectedTabs; } else { - tabs = [TabContextMenu.contextTab]; + tabs = [currentTab]; + } + if (otherTabHint && !tabs.includes(otherTabHint)) { + tabs.push(otherTabHint); + } + if (tabs.length < 2) { + gBrowser.selectedTab = tabs[0]; + this.createEmptySplit(); + return; } // If all are already in a split view, we unsplit them first. if (tabs.every(tab => tab.splitView)) { @@ -1265,10 +1276,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { * @returns {boolean} True if the tabs can be split, false otherwise. */ contextCanSplitTabs() { - if ( - window.gBrowser.selectedTabs.length < 2 || - window.gBrowser.selectedTabs.length > this.MAX_TABS - ) { + if (window.gBrowser.selectedTabs.length > this.MAX_TABS) { return false; } for (const tab of window.gBrowser.selectedTabs) { @@ -1323,7 +1331,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { * @param {Array} tabs * @param {Tab} relativeTab */ - _moveTabsToContainer(tabs, relativeTab) { + #moveTabsToContainer(tabs, relativeTab) { const relativeTabIsPinned = relativeTab.pinned; const relativeTabIsEssential = relativeTab.hasAttribute("zen-essential"); @@ -1340,6 +1348,32 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { } } + #useTabsToSplit(tabs) { + // If there's ANY pinned tab on the list, we clone the pinned tab + // state to all the tabs + const allArePinned = tabs.every(tab => tab.pinned); + const thereIsOnePinned = tabs.some(tab => tab.pinned); + const thereIsOneEssential = tabs.some(tab => + tab.hasAttribute("zen-essential") + ); + const thereIsOneLiveFolder = tabs.some(tab => + tab.hasAttribute("zen-live-folder-item-id") + ); + + if ( + thereIsOneEssential || + (thereIsOnePinned && !allArePinned) || + thereIsOneLiveFolder + ) { + for (let i = 0; i < tabs.length; i++) { + const tab = tabs[i]; + if (tab.pinned) { + tabs[i] = gBrowser.duplicateTab(tab, true); + } + } + } + } + /** * Splits the given tabs. * @@ -1364,17 +1398,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { let shouldActivateSplit = (initialIndex >= 0 || tabs.includes(window.gBrowser.selectedTab)) && !this._sessionRestoring; + if (existingSplitTab) { - this._moveTabsToContainer(tabs, tabs[tabIndexToUse]); const groupIndex = this._data.findIndex(group => group.tabs.includes(existingSplitTab) ); const group = this._data[groupIndex]; - const gridTypeChange = gridType && group.gridType !== gridType; - const newTabsAdded = tabs.find(t => !group.tabs.includes(t)); if (group.tabs.length >= this.MAX_TABS) { + gZenUIManager.showToast("zen-split-view-limit-toast"); return; } + this.#useTabsToSplit(tabs); + this.#moveTabsToContainer(tabs, tabs[tabIndexToUse]); + const gridTypeChange = gridType && group.gridType !== gridType; + const newTabsAdded = tabs.find(t => !group.tabs.includes(t)); if (gridTypeChange && !newTabsAdded) { // reset layout group.gridType = gridType; @@ -1406,30 +1443,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { return group; } - // We are here if none of the tabs have been previously split - // If there's ANY pinned tab on the list, we clone the pinned tab - // state to all the tabs - const allArePinned = tabs.every(tab => tab.pinned); - const thereIsOnePinned = tabs.some(tab => tab.pinned); - const thereIsOneEssential = tabs.some(tab => - tab.hasAttribute("zen-essential") - ); - const thereIsOneLiveFolder = tabs.some(tab => - tab.hasAttribute("zen-live-folder-item-id") - ); - - if ( - thereIsOneEssential || - (thereIsOnePinned && !allArePinned) || - thereIsOneLiveFolder - ) { - for (let i = 0; i < tabs.length; i++) { - const tab = tabs[i]; - if (tab.pinned) { - tabs[i] = gBrowser.duplicateTab(tab, true); - } - } - } + this.#useTabsToSplit(tabs); gridType ??= "grid"; @@ -2172,7 +2186,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup) ) { - this._moveTabsToContainer([draggedTab], droppedOnTab); + this.#moveTabsToContainer([draggedTab], droppedOnTab); gBrowser.moveTabToExistingGroup(draggedTab, splitGroup); if (hoverSide === "left" || hoverSide === "top") { try { diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 71009f921..7270eef67 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -857,7 +857,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0]; const currentUrl = location.split("#")[0]; // Add an indicator that the pin has been changed - if (Services.io.newURI(currentUrl).spec === Services.io.newURI(pinUrl).spec) { + if ( + Services.io.newURI(currentUrl).spec === Services.io.newURI(pinUrl).spec + ) { this.resetPinChangedUrl(tab); return; } diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index b324a7116..f3d5777e7 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -474,13 +474,13 @@ overflow-y: auto; height: 100%; - :root[zen-workspace-id][zen-sidebar-expanded="true"] & { + :root[zen-sidebar-expanded="true"] & { margin-left: calc(-1 * var(--zen-toolbox-padding)); width: calc(100% + var(--zen-toolbox-padding) * 2); } } -:root[zen-workspace-id] #pinned-tabs-container { +#pinned-tabs-container { display: none; } diff --git a/src/zen/tests/spaces/browser_issue_10455.js b/src/zen/tests/spaces/browser_issue_10455.js index 48578c798..ed7644d39 100644 --- a/src/zen/tests/spaces/browser_issue_10455.js +++ b/src/zen/tests/spaces/browser_issue_10455.js @@ -12,10 +12,6 @@ add_task(async function test_Issue_10455() { let newWindow = await BrowserTestUtils.openNewBrowserWindow(); await newWindow.gZenWorkspaces.promiseInitialized; - ok( - newWindow.document.documentElement.hasAttribute("zen-workspace-id"), - "New window should have a zen-workspace-id attribute" - ); const unloadEvent = BrowserTestUtils.waitForEvent(newWindow, "unload"); newWindow.BrowserCommands.closeTabOrWindow(); @@ -33,10 +29,6 @@ add_task(async function test_Issue_10455_Dont_Close() { let newWindow = await BrowserTestUtils.openNewBrowserWindow(); await newWindow.gZenWorkspaces.promiseInitialized; - ok( - newWindow.document.documentElement.hasAttribute("zen-workspace-id"), - "New window should have a zen-workspace-id attribute" - ); newWindow.BrowserCommands.closeTabOrWindow(); Assert.strictEqual( diff --git a/src/zen/tests/spaces/browser_private_mode.js b/src/zen/tests/spaces/browser_private_mode.js index b3f3bd66d..e2c0e19fd 100644 --- a/src/zen/tests/spaces/browser_private_mode.js +++ b/src/zen/tests/spaces/browser_private_mode.js @@ -12,10 +12,6 @@ add_task(async function test_Private_Mode() { private: true, }); await privateWindow.gZenWorkspaces.promiseInitialized; - ok( - privateWindow.document.documentElement.hasAttribute("zen-workspace-id"), - "Private window should have a zen-workspace-id attribute" - ); await BrowserTestUtils.closeWindow(privateWindow); await SpecialPowers.popPrefEnv();