From aecea1f5658ebe772952c7ccb300aecd490f1573 Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Fri, 5 Dec 2025 09:35:22 +0100 Subject: [PATCH] feat: Improved support for unsynced windows support, b=no-bug, c=workspaces, folders --- .../en-US/browser/browser/zen-workspaces.ftl | 4 + .../base/content/zen-panels/popups.inc | 5 + .../sessionstore/SessionStore-sys-mjs.patch | 72 ++++++++++---- src/zen/folders/ZenFolders.mjs | 15 +-- src/zen/sessionstore/ZenWindowSync.sys.mjs | 12 ++- src/zen/workspaces/ZenWorkspace.mjs | 52 ++++++++++- src/zen/workspaces/ZenWorkspaceIcons.mjs | 2 +- src/zen/workspaces/ZenWorkspaces.mjs | 93 ++++++++++--------- src/zen/workspaces/zen-workspaces.css | 32 +++++-- 9 files changed, 197 insertions(+), 90 deletions(-) diff --git a/locales/en-US/browser/browser/zen-workspaces.ftl b/locales/en-US/browser/browser/zen-workspaces.ftl index 67f43f7fe..cc8d4601f 100644 --- a/locales/en-US/browser/browser/zen-workspaces.ftl +++ b/locales/en-US/browser/browser/zen-workspaces.ftl @@ -66,6 +66,10 @@ zen-panel-ui-gradient-click-to-add = Click to add a color zen-workspace-creation-name = .placeholder = Space Name +zen-move-tab-to-workspace-button = + .label = Move Tabs + .tooltiptext = Move all tabs in this window to a Space + zen-workspaces-panel-context-reorder = .label = Reorder Spaces diff --git a/src/browser/base/content/zen-panels/popups.inc b/src/browser/base/content/zen-panels/popups.inc index d94f76ae0..b4ecd5e14 100644 --- a/src/browser/base/content/zen-panels/popups.inc +++ b/src/browser/base/content/zen-panels/popups.inc @@ -59,3 +59,8 @@ + + +# Popup to move tabs to a synced workspace. +# This would be automatically populated with the list of available synced workspaces. + diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index a928cdc70..009fed300 100644 --- a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch +++ b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs -index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4d5afeafa 100644 +index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..ddeb16347bd279934ad3b8fcd158961ff1220f29 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -127,6 +127,8 @@ const TAB_EVENTS = [ @@ -28,7 +28,18 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 this.saveStateDelayed(win); break; case "TabGroupCreate": -@@ -2151,7 +2156,6 @@ var SessionStoreInternal = { +@@ -2020,6 +2025,10 @@ var SessionStoreInternal = { + this._windows[aWindow.__SSi].isTaskbarTab = true; + } + ++ if (aWindow.document.documentElement.hasAttribute("zen-unsynced-window")) { ++ this._windows[aWindow.__SSi].isZenUnsynced = true; ++ } ++ + let tabbrowser = aWindow.gBrowser; + + // add tab change listeners to all already existing tabs +@@ -2151,7 +2160,6 @@ var SessionStoreInternal = { if (closedWindowState) { let newWindowState; if ( @@ -36,7 +47,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 !lazy.SessionStartup.willRestore() ) { // We want to split the window up into pinned tabs and unpinned tabs. -@@ -2215,6 +2219,9 @@ var SessionStoreInternal = { +@@ -2215,6 +2223,9 @@ var SessionStoreInternal = { }); this._shouldRestoreLastSession = false; } @@ -46,7 +57,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 if (this._restoreLastWindow && aWindow.toolbar.visible) { // always reset (if not a popup window) -@@ -2384,11 +2391,9 @@ var SessionStoreInternal = { +@@ -2384,11 +2395,9 @@ var SessionStoreInternal = { tabbrowser.selectedTab.label; } @@ -58,7 +69,25 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 // Store the window's close date to figure out when each individual tab // was closed. This timestamp should allow re-arranging data based on how -@@ -3373,7 +3378,7 @@ var SessionStoreInternal = { +@@ -2465,7 +2474,7 @@ var SessionStoreInternal = { + // 2) Flush the window. + // 3) When the flush is complete, revisit our decision to store the window + // in _closedWindows, and add/remove as necessary. +- if (!winData.isPrivate && !winData.isTaskbarTab) { ++ if (!winData.isPrivate && !winData.isTaskbarTab && !winData.isZenUnsynced) { + this.maybeSaveClosedWindow(winData, isLastWindow); + } + +@@ -2486,7 +2495,7 @@ var SessionStoreInternal = { + + // Save non-private windows if they have at + // least one saveable tab or are the last window. +- if (!winData.isPrivate && !winData.isTaskbarTab) { ++ if (!winData.isPrivate && !winData.isTaskbarTab && !winData.isZenUnsynced) { + this.maybeSaveClosedWindow(winData, isLastWindow); + + if (!isLastWindow && winData.closedId > -1) { +@@ -3373,7 +3382,7 @@ var SessionStoreInternal = { if (!isPrivateWindow && tabState.isPrivate) { return; } @@ -67,7 +96,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 return; } -@@ -4089,6 +4094,12 @@ var SessionStoreInternal = { +@@ -4089,6 +4098,12 @@ var SessionStoreInternal = { Math.min(tabState.index, tabState.entries.length) ); tabState.pinned = false; @@ -80,7 +109,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 if (inBackground === false) { aWindow.gBrowser.selectedTab = newTab; -@@ -4525,6 +4536,7 @@ var SessionStoreInternal = { +@@ -4525,6 +4540,7 @@ var SessionStoreInternal = { // Append the tab if we're opening into a different window, tabIndex: aSource == aTargetWindow ? pos : Infinity, pinned: state.pinned, @@ -88,7 +117,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 userContextId: state.userContextId, skipLoad: true, preferredRemoteType, -@@ -5374,7 +5386,7 @@ var SessionStoreInternal = { +@@ -5374,7 +5390,7 @@ var SessionStoreInternal = { for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) { let tab = tabbrowser.tabs[i]; @@ -97,7 +126,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 removableTabs.push(tab); } } -@@ -5434,7 +5446,7 @@ var SessionStoreInternal = { +@@ -5434,7 +5450,7 @@ var SessionStoreInternal = { } let workspaceID = aWindow.getWorkspaceID(); @@ -106,7 +135,16 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 winData.workspaceID = workspaceID; } }, -@@ -5625,11 +5637,12 @@ var SessionStoreInternal = { +@@ -5483,7 +5499,7 @@ var SessionStoreInternal = { + + // collect the data for all windows + for (ix in this._windows) { +- if (this._windows[ix]._restoring || this._windows[ix].isTaskbarTab) { ++ if (this._windows[ix]._restoring || this._windows[ix].isTaskbarTab || this._windows[ix].isZenUnsynced) { + // window data is still in _statesToRestore + continue; + } +@@ -5625,11 +5641,12 @@ var SessionStoreInternal = { } let tabbrowser = aWindow.gBrowser; @@ -120,7 +158,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 // update the internal state data for this window for (let tab of tabs) { if (tab == aWindow.FirefoxViewHandler.tab) { -@@ -5640,6 +5653,7 @@ var SessionStoreInternal = { +@@ -5640,6 +5657,7 @@ var SessionStoreInternal = { tabsData.push(tabData); } @@ -128,7 +166,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 // update tab group state for this window winData.groups = []; for (let tabGroup of aWindow.gBrowser.tabGroups) { -@@ -5652,7 +5666,7 @@ var SessionStoreInternal = { +@@ -5652,7 +5670,7 @@ var SessionStoreInternal = { // a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab, // since it's only inserted into the tab strip after it's selected). if (aWindow.FirefoxViewHandler.tab?.selected) { @@ -137,7 +175,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 winData.title = tabbrowser.tabs[0].label; } winData.selected = selectedIndex; -@@ -5765,8 +5779,8 @@ var SessionStoreInternal = { +@@ -5765,8 +5783,8 @@ var SessionStoreInternal = { // selectTab represents. let selectTab = 0; if (overwriteTabs) { @@ -148,7 +186,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 selectTab = Math.min(selectTab, winData.tabs.length); } -@@ -5809,6 +5823,8 @@ var SessionStoreInternal = { +@@ -5809,6 +5827,8 @@ var SessionStoreInternal = { winData.tabs, winData.groups ?? [] ); @@ -157,7 +195,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 this._log.debug( `restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs` ); -@@ -6372,6 +6388,25 @@ var SessionStoreInternal = { +@@ -6372,6 +6392,25 @@ var SessionStoreInternal = { // Most of tabData has been restored, now continue with restoring // attributes that may trigger external events. @@ -183,7 +221,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 if (tabData.pinned) { tabbrowser.pinTab(tab); -@@ -7290,7 +7325,7 @@ var SessionStoreInternal = { +@@ -7290,7 +7329,7 @@ var SessionStoreInternal = { let groupsToSave = new Map(); for (let tIndex = 0; tIndex < window.tabs.length; ) { @@ -192,7 +230,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..c4a1d14b1eb4e2b26dd903c462fb5cf4 // Adjust window.selected if (tIndex + 1 < window.selected) { window.selected -= 1; -@@ -7305,7 +7340,7 @@ var SessionStoreInternal = { +@@ -7305,7 +7344,7 @@ var SessionStoreInternal = { ); // We don't want to increment tIndex here. continue; diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index 22ff89845..80535e693 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -101,20 +101,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature { .querySelector('menupopup'); changeFolderSpace.innerHTML = ''; for (const workspace of [...gZenWorkspaces._workspaceCache.workspaces].reverse()) { - const item = document.createXULElement('menuitem'); - item.className = 'zen-workspace-context-menu-item'; - item.setAttribute('zen-workspace-id', workspace.uuid); - item.setAttribute('disabled', workspace.uuid === gZenWorkspaces.activeWorkspace); - let name = workspace.name; - const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg'); - if (workspace.icon && workspace.icon !== '' && !iconIsSvg) { - name = `${workspace.icon} ${name}`; - } - item.setAttribute('label', name); - if (iconIsSvg) { - item.setAttribute('image', workspace.icon); - item.classList.add('zen-workspace-context-icon'); - } + const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace); item.addEventListener('command', (event) => { if (!this.#lastFolderContextMenu) return; this.changeFolderToSpace( diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index a10b172f8..5bbe1a0f2 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -428,9 +428,11 @@ class nsZenWindowSync { if (aOurTab.linkedBrowser?.currentURI.spec !== 'about:blank') { aOurTab.linkedBrowser.loadURI(Services.io.newURI('about:blank'), { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY, }); } aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false); + aOtherTab.permanentKey = aOurTab.permanentKey; const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture']; // swapBrowsersAndCloseOther already takes care of transferring attributes like 'muted', // but we need to manually remove some attributes from the other tab. @@ -443,10 +445,12 @@ class nsZenWindowSync { aOurTab.linkedBrowser.blur(); aOurTab.ownerGlobal.gBrowser._adjustFocusAfterTabSwitch(aOurTab); } - for (const tab of [aOurTab, aOtherTab]) { - // Ensure the tab's state is flushed after the swap. - lazy.TabStateFlusher.flush(tab.linkedBrowser); - } + // Ensure the tab's state is flushed after the swap. By doing this, + // we can re-schedule another session store delayed process to fire. + // It's also important to note that if we don't flush the state here, + // we would start recieving invalid history changes from the the incorrect + // browser view that was just swapped out. + lazy.TabStateFlusher.flush(aOurTab.linkedBrowser); } /** diff --git a/src/zen/workspaces/ZenWorkspace.mjs b/src/zen/workspaces/ZenWorkspace.mjs index ce6baa122..921ae4239 100644 --- a/src/zen/workspaces/ZenWorkspace.mjs +++ b/src/zen/workspaces/ZenWorkspace.mjs @@ -37,6 +37,14 @@ class nsZenWorkspace extends MozXULElement { `; } + static get moveTabToButtonMarkup() { + return ` + + `; + } + static get inheritedAttributes() { return { '.zen-workspace-tabs-section': 'zen-workspace-id=id', @@ -88,6 +96,20 @@ class nsZenWorkspace extends MozXULElement { gZenWorkspaces.changeWorkspaceIcon(); }); + if (!gZenWorkspaces.currentWindowIsSyncing) { + let actionsButton = this.indicator.querySelector('.zen-workspaces-actions'); + const moveTabToFragment = window.MozXULElement.parseXULToFragment( + nsZenWorkspace.moveTabToButtonMarkup + ); + actionsButton.after(moveTabToFragment); + actionsButton.setAttribute('hidden', 'true'); + actionsButton = actionsButton.nextElementSibling; + actionsButton.addEventListener('command', (event) => { + event.stopPropagation(); + this.#openMoveTabsToWorkspacePanel(event.target); + }); + } + this.scrollbox._getScrollableElements = () => { const children = [...this.pinnedTabsContainer.children, ...this.tabsContainer.children]; if (Services.prefs.getBoolPref('zen.view.show-newtab-button-top', false)) { @@ -209,7 +231,7 @@ class nsZenWorkspace extends MozXULElement { if (newName === '') { return; } - let workspaces = (await gZenWorkspaces._workspaces()).workspaces; + let workspaces = (await gZenWorkspaces.getWorkspaces()).workspaces; let workspaceData = workspaces.find((workspace) => workspace.uuid === this.workspaceUuid); workspaceData.name = newName; await gZenWorkspaces.saveWorkspace(workspaceData); @@ -256,6 +278,34 @@ class nsZenWorkspace extends MozXULElement { this.style.removeProperty('--toolbox-textcolor'); this.style.removeProperty('--zen-primary-color'); } + + #openMoveTabsToWorkspacePanel(button) { + button = button.closest('toolbarbutton'); + if (!button) return; + + const popup = document.getElementById('zenMoveTabsToSyncedWorkspacePopup'); + popup.innerHTML = ''; + + gZenWorkspaces.getWorkspaces(true).then((workspaces) => { + for (const workspace of workspaces.workspaces) { + const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace); + item.addEventListener('command', async () => { + console.log('TODO: Move tab to workspace', workspace); + }); + popup.appendChild(item); + } + + button.setAttribute('open', 'true'); + popup.addEventListener( + 'popuphidden', + () => { + button.removeAttribute('open'); + }, + { once: true } + ); + popup.openPopup(button, 'after_start', 0, 0, true /* isContextMenu */); + }); + } } customElements.define('zen-workspace', nsZenWorkspace); diff --git a/src/zen/workspaces/ZenWorkspaceIcons.mjs b/src/zen/workspaces/ZenWorkspaceIcons.mjs index f9186c43f..d56f6ecea 100644 --- a/src/zen/workspaces/ZenWorkspaceIcons.mjs +++ b/src/zen/workspaces/ZenWorkspaceIcons.mjs @@ -126,7 +126,7 @@ class nsZenWorkspaceIcons extends MozXULElement { } async #updateIcons() { - const workspaces = await gZenWorkspaces._workspaces(); + const workspaces = await gZenWorkspaces.getWorkspaces(); this.innerHTML = ''; for (const workspace of workspaces.workspaces) { const button = this.#createWorkspaceIcon(workspace); diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index a66d4c30a..dc1e14548 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -129,7 +129,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { this.addPopupListeners(); await this.#waitForPromises(); - await this._workspaces(); + await this.getWorkspaces(); await this.afterLoadInit(); } @@ -319,7 +319,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { } async _createDefaultWorkspaceIfNeeded() { - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); if (!workspaces.workspaces.length) { await this.createAndSaveWorkspace('Space', null, true); this._workspaceCache = null; @@ -414,7 +414,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { await new Promise((resolve) => { setTimeout(async () => { const tabs = gBrowser.tabContainer.allTabs; - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); for (const workspace of workspaces.workspaces) { await this._createWorkspaceTabsSection(workspace, tabs); } @@ -865,12 +865,12 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { } } - async _workspaces() { - if (this._workspaceCache) { + async getWorkspaces(lieToMe = false) { + if (this._workspaceCache && !lieToMe) { return this._workspaceCache; } - if (!this.currentWindowIsSyncing) { + if (!this.currentWindowIsSyncing && !lieToMe) { this._workspaceCache = { workspaces: this._tempWorkspace ? [this._tempWorkspace] : [], lastChangeTimestamp: 0, @@ -884,7 +884,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { ZenWorkspacesStorage.getLastChangeTimestamp(), ]); - this._workspaceCache = { workspaces, lastChangeTimestamp }; + const workspaceCache = { workspaces, lastChangeTimestamp }; // Get the active workspace ID from preferences const activeWorkspaceId = this.activeWorkspace; @@ -892,18 +892,20 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { const activeWorkspace = this.getWorkspaceFromId(activeWorkspaceId); // Set the active workspace ID to the first one if the one with selected id doesn't exist if (!activeWorkspace) { - this.activeWorkspace = this._workspaceCache.workspaces[0]?.uuid; + this.activeWorkspace = workspaceCache.workspaces[0]?.uuid; } } else { // Set the active workspace ID to the first one if active workspace doesn't exist - this.activeWorkspace = this._workspaceCache.workspaces[0]?.uuid; + this.activeWorkspace = workspaceCache.workspaces[0]?.uuid; } // sort by position - this._workspaceCache.workspaces.sort( - (a, b) => (a.position ?? Infinity) - (b.position ?? Infinity) - ); + workspaceCache.workspaces.sort((a, b) => (a.position ?? Infinity) - (b.position ?? Infinity)); - return this._workspaceCache; + if (!lieToMe) { + // We don't want our cache to remain for the next calls + this._workspaceCache = workspaceCache; + } + return workspaceCache; } async workspaceBookmarks() { @@ -1124,7 +1126,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { async #clearAnyZombieTabs() { const tabs = this.allStoredTabs; - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); for (let tab of tabs) { const workspaceID = tab.getAttribute('zen-workspace-id'); if ( @@ -1212,6 +1214,24 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { ); } + generateMenuItemForWorkspace(workspace) { + const item = document.createXULElement('menuitem'); + item.className = 'zen-workspace-context-menu-item'; + item.setAttribute('zen-workspace-id', workspace.uuid); + item.setAttribute('disabled', workspace.uuid === this.activeWorkspace); + let name = workspace.name; + const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg'); + if (workspace.icon && workspace.icon !== '' && !iconIsSvg) { + name = `${workspace.icon} ${name}`; + } + item.setAttribute('label', name); + if (iconIsSvg) { + item.setAttribute('image', workspace.icon); + item.classList.add('zen-workspace-context-icon'); + } + return item; + } + #contextMenuData = null; updateWorkspaceActionsMenu(event) { if (event.target.id !== 'zenWorkspaceMoreActions') { @@ -1255,20 +1275,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { if (!this.#contextMenuData.workspaceId) { separator.hidden = false; for (const workspace of [...this._workspaceCache.workspaces].reverse()) { - const item = document.createXULElement('menuitem'); - item.className = 'zen-workspace-context-menu-item'; - item.setAttribute('zen-workspace-id', workspace.uuid); - item.setAttribute('disabled', workspace.uuid === this.activeWorkspace); - let name = workspace.name; - const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg'); - if (workspace.icon && workspace.icon !== '' && !iconIsSvg) { - name = `${workspace.icon} ${name}`; - } - item.setAttribute('label', name); - if (iconIsSvg) { - item.setAttribute('image', workspace.icon); - item.classList.add('zen-workspace-context-icon'); - } + const item = this.generateMenuItemForWorkspace(workspace); item.addEventListener('command', (e) => { this.changeWorkspaceWithID(e.target.closest('menuitem').getAttribute('zen-workspace-id')); }); @@ -1313,7 +1320,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { } async removeWorkspace(windowID) { - let workspacesData = await this._workspaces(); + let workspacesData = await this.getWorkspaces(); await this.changeWorkspace( workspacesData.workspaces.find((workspace) => workspace.uuid !== windowID) ); @@ -1336,7 +1343,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { } async getActiveWorkspace() { - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); return ( workspaces.workspaces.find((workspace) => workspace.uuid === this.activeWorkspace) ?? workspaces.workspaces[0] @@ -1370,7 +1377,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { browser.gZenWorkspaces._workspaceCache = null; browser.gZenWorkspaces._workspaceBookmarksCache = null; } - let workspaces = await browser.gZenWorkspaces._workspaces(); + let workspaces = await browser.gZenWorkspaces.getWorkspaces(); browser.document .getElementById('cmd_zenCtxDeleteWorkspace') .setAttribute('disabled', workspaces.workspaces.length <= 1); @@ -1423,7 +1430,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { if (this.privateWindowOrDisabled) { return; } - const workspaces = (await this._workspaces()).workspaces; + const workspaces = (await this.getWorkspaces()).workspaces; const workspace = workspaces.find((w) => w.uuid === id); if (!workspace) { console.warn(`Workspace with ID ${id} not found for reordering.`); @@ -1449,7 +1456,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { } async moveWorkspace(draggedWorkspaceId, targetWorkspaceId) { - const workspaces = (await this._workspaces()).workspaces; + const workspaces = (await this.getWorkspaces()).workspaces; const draggedIndex = workspaces.findIndex((w) => w.uuid === draggedWorkspaceId); const draggedWorkspace = workspaces.splice(draggedIndex, 1)[0]; const targetIndex = workspaces.findIndex((w) => w.uuid === targetWorkspaceId); @@ -1662,7 +1669,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { return; } - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); gZenFolders.cancelPopupTimer(); // Refresh tab cache @@ -1778,7 +1785,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { return; } this._organizingWorkspaceStrip = true; - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); let workspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === workspace.uuid); if (!justMove) { this._fixIndicatorsNames(workspaces); @@ -1912,7 +1919,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { const kGlobalAnimationDuration = 0.2; this._animatingChange = true; const animations = []; - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); const newWorkspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === newWorkspace.uuid); const isGoingLeft = newWorkspaceIndex <= previousWorkspaceIndex; const clonedEssentials = []; @@ -2215,7 +2222,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { tab, currentWorkspace.uuid, currentWorkspace.containerTabId, - await this._workspaces() + await this.getWorkspaces() ); } @@ -2274,7 +2281,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { const lastSelectedTab = this._lastSelectedWorkspaceTabs[workspace.uuid]; const containerId = workspace.containerTabId?.toString(); - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); // Save current tab as last selected for old workspace if it shouldn't be visible in new workspace if (oldWorkspaceId && oldWorkspaceId !== workspace.uuid) { @@ -2407,7 +2414,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { async _updateWorkspacesChangeContextMenu() { if (gZenWorkspaces.privateWindowOrDisabled) return; - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); const menuPopup = document.getElementById('context-zen-change-workspace-tab-menu-popup'); if (!menuPopup) { @@ -2711,7 +2718,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { // Context menu management async contextChangeContainerTab(event) { this._organizingWorkspaceStrip = true; - let workspaces = await this._workspaces(); + let workspaces = await this.getWorkspaces(); let workspace = workspaces.workspaces.find( (workspace) => workspace.uuid === (this.#contextMenuData?.workspaceId || this.activeWorkspace) ); @@ -2765,7 +2772,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { async changeWorkspaceShortcut(offset = 1, whileScrolling = false) { // Cycle through workspaces - let workspaces = await this._workspaces(); + let workspaces = await this.getWorkspaces(); let activeWorkspace = await this.getActiveWorkspace(); let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace); @@ -2829,7 +2836,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { this._lastSelectedWorkspaceTabs[workspaceID] = gZenGlanceManager.getTabOrGlanceParent( tabs[tabs.length - 1] ); - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); await this.changeWorkspace( workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID) ); @@ -2888,7 +2895,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature { } async shortcutSwitchTo(index) { - const workspaces = await this._workspaces(); + const workspaces = await this.getWorkspaces(); // The index may be out of bounds, if it doesnt exist, don't do anything if (index >= workspaces.workspaces.length || index < 0) { return; diff --git a/src/zen/workspaces/zen-workspaces.css b/src/zen/workspaces/zen-workspaces.css index 674a9cd44..8c6767c22 100644 --- a/src/zen/workspaces/zen-workspaces.css +++ b/src/zen/workspaces/zen-workspaces.css @@ -181,13 +181,8 @@ height: calc(100% - var(--zen-toolbox-padding) * 2); } - :root:not([zen-private-window]) & { - &:hover, - &[open='true'] { - &::before { - background: var(--tab-hover-background-color); - } - } + :root[zen-private-window] & { + pointer-events: none; } & .zen-current-workspace-indicator-icon { @@ -228,16 +223,23 @@ font-weight: 600; align-items: center; margin: 0; + + :root[zen-unsynced-window] & { + pointer-events: none; + } } .zen-workspaces-actions { margin-left: auto !important; - opacity: 0; - visibility: collapse; transition: opacity 0.1s; order: 5; --toolbarbutton-inner-padding: 6px !important; + :root:not([zen-unsynced-window]) & { + opacity: 0; + visibility: collapse; + } + & image { border-radius: max(calc(var(--border-radius-medium) - 4px), 4px) !important; width: 26px; @@ -248,9 +250,19 @@ :root[zen-renaming-tab='true'] & { display: none; } + + :root[zen-unsynced-window] & { + .toolbarbutton-text { + display: flex; + } + + .toolbarbutton-icon { + display: none; + } + } } - :root:not([zen-private-window]) &:hover .zen-workspaces-actions, + :root:not([zen-unsynced-window]) &:hover .zen-workspaces-actions, & .zen-workspaces-actions[open='true'] { visibility: visible; pointer-events: auto;