From 831801add39ac953e3550d83a69ff1807e6faeb0 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 11 Sep 2024 20:59:02 +0200 Subject: [PATCH 01/42] Refactor tab navigation logic and add contextChangeContainerTab method --- src/ZenWorkspaces.mjs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 2c91f9f..f23375e 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -20,6 +20,10 @@ var ZenWorkspaces = { return Services.prefs.getBoolPref('zen.workspaces.enabled', false); }, + getActiveWorkspaceFromCache() { + return this._workspaceCache.workspaces.find((workspace) => workspace.used); + }, + // Wrorkspaces saving/loading get _storeFile() { return PathUtils.join(PathUtils.profileDir, 'zen-workspaces', 'Workspaces.json'); @@ -96,7 +100,7 @@ var ZenWorkspaces = { } }, - _kIcons: ['🏠', '📄', '💹', '💼', '📧', '✅', '👥'], + _kIcons: JSON.parse(Services-prefs.getStringPref("zen.workspaces.icons", '["🌐", "📁", "📎", "📝", "📅", "📊"]')), _initializeWorkspaceCreationIcons() { let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container'); @@ -575,6 +579,16 @@ var ZenWorkspaces = { } }, + async contextChangeContainerTab(event) { + let workspaces = await this._workspaces(); + let workspace = workspaces.workspaces.find((workspace) => workspace.uuid === this._contextMenuId); + let userContextId = parseInt( + event.target.getAttribute("data-usercontextid") + ); + workspace.containerTabId = userContextId; + await this.saveWorkspace(workspace); + }, + onContextMenuClose() { let target = document.querySelector( `#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions` @@ -643,6 +657,15 @@ var ZenWorkspaces = { const workspaces = await this._workspaces(); await this.changeWorkspace(workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID)); }, + + // Tab browser utilities + getContextIdIfNeeded(userContextId) { + if (typeof userContextId !== "undefined" || !this.workspaceEnabled) { + return userContextId; + } + const activeWorkspace = this.getActiveWorkspaceFromCache(); + return activeWorkspace?.containerTabId; + }, }; ZenWorkspaces.init(); From 014161404dcfe6a42cc86b2b65305d0331824d0a Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 11 Sep 2024 21:07:49 +0200 Subject: [PATCH 02/42] Refactor ZenWorkspaces.mjs to fix parsing of workspace icons --- src/ZenWorkspaces.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index f23375e..aa2f837 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -100,7 +100,7 @@ var ZenWorkspaces = { } }, - _kIcons: JSON.parse(Services-prefs.getStringPref("zen.workspaces.icons", '["🌐", "📁", "📎", "📝", "📅", "📊"]')), + _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")), _initializeWorkspaceCreationIcons() { let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container'); From b372ec2694039dc347b35d977afa0c5cd81a5ca2 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 11 Sep 2024 22:54:17 +0200 Subject: [PATCH 03/42] Refactor ZenWorkspaces.mjs to fix parsing of workspace icons and improve workspace creation - Refactor ZenWorkspaces.mjs to fix parsing of workspace icons and ensure compatibility with icons that have more than one character. - Improve workspace creation by displaying the first character of each icon and adding container information. --- src/ZenWorkspaces.mjs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index aa2f837..ba509cc 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -100,7 +100,9 @@ var ZenWorkspaces = { } }, - _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")), + // Convert all the icons to just the first character, just in case someone + // decides to use a string with more than one character + _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => icon[0]), _initializeWorkspaceCreationIcons() { let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container'); @@ -231,12 +233,31 @@ var ZenWorkspaces = { element.className = 'subviewbutton'; element.setAttribute('tooltiptext', workspace.name); element.setAttribute('zen-workspace-id', workspace.uuid); - //element.setAttribute("context", "zenWorkspaceActionsMenu"); + if (workspace.used) { + element.setAttribute('active', 'true'); + } + if (workspace.default) { + element.setAttribute('default', 'true'); + } + console.log(ContextualIdentityService.getPublicIdentities()) + console.log(workspace) + const containerGroup = ContextualIdentityService.getPublicIdentities().find( + (container) => container.userContextId === workspace.containerTabId + ); + console.log(containerGroup) + if (containerGroup) { + element.classList.add("identity-color-" + containerGroup.color); + element.setAttribute("data-usercontextid", containerGroup.userContextId); + } let childs = window.MozXULElement.parseXULToFragment(`
-
-
+ +
+
+
+
+
@@ -245,6 +266,11 @@ var ZenWorkspaces = { // use text content instead of innerHTML to avoid XSS childs.querySelector('.zen-workspace-icon').textContent = this.getWorkspaceIcon(workspace); childs.querySelector('.zen-workspace-name').textContent = workspace.name; + if (containerGroup) { + childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.formatContextLabel( + containerGroup.l10nId + ); + } childs.querySelector('.zen-workspace-actions').addEventListener('command', (event) => { let button = event.target; @@ -587,6 +613,7 @@ var ZenWorkspaces = { ); workspace.containerTabId = userContextId; await this.saveWorkspace(workspace); + await this._propagateWorkspaceData(); }, onContextMenuClose() { From c9c5c94e8d4d9f306bfe73fc79b153d9b979533e Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 11 Sep 2024 23:44:44 +0200 Subject: [PATCH 04/42] Refactor ZenWorkspaces.mjs to return an array with contextId and a boolean flag --- src/ZenWorkspaces.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index ba509cc..99e5c52 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -688,10 +688,10 @@ var ZenWorkspaces = { // Tab browser utilities getContextIdIfNeeded(userContextId) { if (typeof userContextId !== "undefined" || !this.workspaceEnabled) { - return userContextId; + return [userContextId, false]; } const activeWorkspace = this.getActiveWorkspaceFromCache(); - return activeWorkspace?.containerTabId; + return [activeWorkspace?.containerTabId, true]; }, }; From 89f28b307d8cd45ee5416a8dafb76f298bd6c212 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Thu, 12 Sep 2024 19:54:28 +0200 Subject: [PATCH 05/42] Refactor ZenSidebarManager.mjs to remove console.log statement Refactor ZenWorkspaces.mjs to remove console.log statements --- src/ZenSidebarManager.mjs | 1 - src/ZenWorkspaces.mjs | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/ZenSidebarManager.mjs b/src/ZenSidebarManager.mjs index 3851e4e..71f2e8e 100644 --- a/src/ZenSidebarManager.mjs +++ b/src/ZenSidebarManager.mjs @@ -304,7 +304,6 @@ var gZenBrowserManagerSidebar = { let data = this.sidebarData; let newPos = []; for (let element of this.__dragingElement.parentNode.children) { - console.log(element); let panelId = element.getAttribute('zen-sidebar-id'); newPos.push(panelId); } diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 99e5c52..b95a246 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -239,12 +239,9 @@ var ZenWorkspaces = { if (workspace.default) { element.setAttribute('default', 'true'); } - console.log(ContextualIdentityService.getPublicIdentities()) - console.log(workspace) const containerGroup = ContextualIdentityService.getPublicIdentities().find( (container) => container.userContextId === workspace.containerTabId ); - console.log(containerGroup) if (containerGroup) { element.classList.add("identity-color-" + containerGroup.color); element.setAttribute("data-usercontextid", containerGroup.userContextId); From e178b09a35d72a277608a5cad713090b57a1c669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Thu, 12 Sep 2024 21:26:51 -0600 Subject: [PATCH 06/42] feature(zenThemesImporter): write saved preferences to dom on startup --- src/ZenThemesImporter.mjs | 49 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 257e2b1..a5fbe63 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -36,8 +36,9 @@ var gZenThemeImporter = new (class { constructor() { console.info('ZenThemeImporter: Initiating Zen theme importer'); try { - window.SessionStore.promiseInitialized.then(() => { + window.SessionStore.promiseInitialized.then(async () => { this.insertStylesheet(); + await this.writeToDom(); }); console.info('ZenThemeImporter: Zen theme imported'); } catch (e) { @@ -108,9 +109,55 @@ var gZenThemeImporter = new (class { async updateStylesheet() { this.removeStylesheet(); await this.writeStylesheet(); + await this.writeToDom(); this.insertStylesheet(); } + _getBrowser() { + if (!this.__browser) { + this.__browser = Services.wm.getMostRecentWindow("navigator:browser") + } + + return this.__browser + } + + async _getThemePreferences(theme) { + const themePath = PathUtils.join(this.getThemeFolder(theme), 'preferences.json'); + + if (!(await IOUtils.exists(themePath)) || !theme.preferences) { + return []; + } + + return IOUtils.readJSON(themePath); + } + + async writeToDom() { + const browser = this._getBrowser() + + for (const theme of Object.values(await this.getThemes())) { + const themePreferences = (await this._getThemePreferences(theme)).filter(({ type }) => type === "dropdown") + + for (const { property } of themePreferences) { + const value = Services.prefs.getStringPref(property, "") + + if (value !== "") { + let element = browser.document.getElementById(theme.name) + + if (!element) { + element = browser.document.createElement("div") + + element.style.display = "none" + element.setAttribute("id", theme.name) + + browser.document.body.appendChild(element) + } + + element.setAttribute(property, value) + } + } + } + } + async writeStylesheet() { const themes = []; this._themes = null; From 82814b4c9a351e6b6f7cfb6d516d3c46a8b7a413 Mon Sep 17 00:00:00 2001 From: Jivan Flores Date: Thu, 12 Sep 2024 21:48:05 -0700 Subject: [PATCH 07/42] chore: Reformat ZenWorkspaces.mjs --- src/ZenWorkspaces.mjs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index b95a246..67424c2 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -102,7 +102,7 @@ var ZenWorkspaces = { // Convert all the icons to just the first character, just in case someone // decides to use a string with more than one character - _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => icon[0]), + _kIcons: JSON.parse(Services.prefs.getStringPref('zen.workspaces.icons')).map((icon) => icon[0]), _initializeWorkspaceCreationIcons() { let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container'); @@ -243,8 +243,8 @@ var ZenWorkspaces = { (container) => container.userContextId === workspace.containerTabId ); if (containerGroup) { - element.classList.add("identity-color-" + containerGroup.color); - element.setAttribute("data-usercontextid", containerGroup.userContextId); + element.classList.add('identity-color-' + containerGroup.color); + element.setAttribute('data-usercontextid', containerGroup.userContextId); } let childs = window.MozXULElement.parseXULToFragment(`
@@ -605,9 +605,7 @@ var ZenWorkspaces = { async contextChangeContainerTab(event) { let workspaces = await this._workspaces(); let workspace = workspaces.workspaces.find((workspace) => workspace.uuid === this._contextMenuId); - let userContextId = parseInt( - event.target.getAttribute("data-usercontextid") - ); + let userContextId = parseInt(event.target.getAttribute('data-usercontextid')); workspace.containerTabId = userContextId; await this.saveWorkspace(workspace); await this._propagateWorkspaceData(); @@ -684,7 +682,7 @@ var ZenWorkspaces = { // Tab browser utilities getContextIdIfNeeded(userContextId) { - if (typeof userContextId !== "undefined" || !this.workspaceEnabled) { + if (typeof userContextId !== 'undefined' || !this.workspaceEnabled) { return [userContextId, false]; } const activeWorkspace = this.getActiveWorkspaceFromCache(); From 569127a141613b09e68581a65aab4df6cb819f7e Mon Sep 17 00:00:00 2001 From: Jivan Flores Date: Fri, 13 Sep 2024 00:01:39 -0700 Subject: [PATCH 08/42] feat: Remember the last selected tab when cycling between workspaces --- src/ZenWorkspaces.mjs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 67424c2..347a052 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -1,4 +1,9 @@ var ZenWorkspaces = { + /** + * Stores workspace IDs and their last selected tabs. + */ + _lastSelectedWorkspaceTabs: {}, + async init() { let docElement = document.documentElement; if ( @@ -166,6 +171,7 @@ var ZenWorkspaces = { console.info('ZenWorkspaces: Removing workspace', windowID); await this.changeWorkspace(json.workspaces.find((workspace) => workspace.uuid !== windowID)); this._deleteAllTabsInWorkspace(windowID); + delete this._lastSelectedWorkspaceTabs[windowID]; json.workspaces = json.workspaces.filter((workspace) => workspace.uuid !== windowID); await this.unsafeSaveWorkspaces(json); await this._propagateWorkspaceData(); @@ -497,7 +503,7 @@ var ZenWorkspaces = { } } if (firstTab) { - gBrowser.selectedTab = firstTab; + gBrowser.selectedTab = this._lastSelectedWorkspaceTabs[window.uuid] ?? firstTab; } if (typeof firstTab === 'undefined' && !onInit) { this._createNewTabForWorkspace(window); @@ -567,7 +573,9 @@ var ZenWorkspaces = { return; } tab.setAttribute('zen-workspace-id', activeWorkspace.uuid); + workspaceID = activeWorkspace.uuid; } + this._lastSelectedWorkspaceTabs[workspaceID] = tab; }, // Context menu management @@ -673,8 +681,14 @@ var ZenWorkspaces = { async changeTabWorkspace(workspaceID) { const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab]; + const previousWorkspaceID = document.documentElement.getAttribute('zen-workspace-id'); for (let tab of tabs) { tab.setAttribute('zen-workspace-id', 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 + // the current workspace + delete this._lastSelectedWorkspaceTabs[previousWorkspaceID]; + } } const workspaces = await this._workspaces(); await this.changeWorkspace(workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID)); From 4cedd84ac877b4ebdf807eb2991cacfa754e8ac1 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Fri, 13 Sep 2024 20:12:05 +0200 Subject: [PATCH 09/42] Refactor ZenWorkspaces.mjs to add createContainerTabMenu method --- src/ZenWorkspaces.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index b95a246..b45e999 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -683,6 +683,20 @@ var ZenWorkspaces = { }, // Tab browser utilities + createContainerTabMenu(event) { + let window = event.target.ownerGlobal; + const workspace = this.getActiveWorkspaceFromCache(); + if (!workspace) { + return; + } + let containerTabId = workspace.containerTabId; + return window.createUserContextMenu(event, { + isContextMenu: true, + excludeUserContextId: containerTabId, + showDefaultTab: true, + }); + }, + getContextIdIfNeeded(userContextId) { if (typeof userContextId !== "undefined" || !this.workspaceEnabled) { return [userContextId, false]; From 5a9fb8d7be23cfe72f3a80768355a325f84788bd Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Fri, 13 Sep 2024 22:52:03 +0200 Subject: [PATCH 10/42] Allow pinned tabs to be workspace specific --- src/ZenWorkspaces.mjs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index b45e999..b5d9f73 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -1,3 +1,4 @@ + var ZenWorkspaces = { async init() { let docElement = document.documentElement; @@ -95,7 +96,7 @@ var ZenWorkspaces = { if (tabs.length === 1) { this._createNewTabForWorkspace({ uuid: workspaceID }); // We still need to close other tabs in the workspace - this.changeWorkspace({ uuid: workspaceID }); + this.changeWorkspace({ uuid: workspaceID }, true); } } }, @@ -469,10 +470,15 @@ var ZenWorkspaces = { button.removeAttribute('disabled'); }, + get _shouldAllowPinTab() { + return Services.prefs.getBoolPref('zen.workspaces.individual-pinned-tabs'); + }, + async changeWorkspace(window, onInit = false) { if (!this.workspaceEnabled) { return; } + const shouldAllowPinnedTabs = this._shouldAllowPinTab; let firstTab = undefined; let workspaces = await this._workspaces(); for (let workspace of workspaces.workspaces) { @@ -481,7 +487,7 @@ var ZenWorkspaces = { this.unsafeSaveWorkspaces(workspaces); console.info('ZenWorkspaces: Changing workspace to', window.uuid); for (let tab of gBrowser.tabs) { - if ((tab.getAttribute('zen-workspace-id') === window.uuid && !tab.pinned) || !tab.hasAttribute('zen-workspace-id')) { + if ((tab.getAttribute('zen-workspace-id') === window.uuid && !(tab.pinned && !shouldAllowPinnedTabs)) || !tab.hasAttribute('zen-workspace-id')) { if (!firstTab) { firstTab = tab; } else if (gBrowser.selectedTab === tab) { @@ -504,7 +510,7 @@ var ZenWorkspaces = { } for (let tab of gBrowser.tabs) { if (tab.getAttribute('zen-workspace-id') !== window.uuid) { - gBrowser.hideTab(tab); + gBrowser.hideTab(tab, undefined, shouldAllowPinnedTabs); } } document.documentElement.setAttribute('zen-workspace-id', window.uuid); @@ -512,6 +518,8 @@ var ZenWorkspaces = { await this._updateWorkspacesButton(); await this._propagateWorkspaceData(); await this._updateWorkspacesChangeContextMenu(); + + document.getElementById('tabbrowser-tabs')._positionPinnedTabs(); }, async _updateWorkspacesChangeContextMenu() { From cc4601f0e1460cad750e686f25ac8250519e34e8 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Fri, 13 Sep 2024 23:01:22 +0200 Subject: [PATCH 11/42] Fixed display when choosing containers for each workspace --- src/ZenWorkspaces.mjs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index b5d9f73..14bdfa9 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -693,10 +693,7 @@ var ZenWorkspaces = { // Tab browser utilities createContainerTabMenu(event) { let window = event.target.ownerGlobal; - const workspace = this.getActiveWorkspaceFromCache(); - if (!workspace) { - return; - } + const workspace = this._workspaceCache.workspaces.find((workspace) => this._contextMenuId === workspace.uuid); let containerTabId = workspace.containerTabId; return window.createUserContextMenu(event, { isContextMenu: true, From d0f32a6ee83c7d3515457687a9c7b7a032d03cd3 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sat, 14 Sep 2024 09:05:38 +0200 Subject: [PATCH 12/42] Refactor ZenWorkspaces.mjs to preserve full icons instead of just the first character --- src/ZenWorkspaces.mjs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 14bdfa9..36f3bd0 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -101,9 +101,7 @@ var ZenWorkspaces = { } }, - // Convert all the icons to just the first character, just in case someone - // decides to use a string with more than one character - _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => icon[0]), + _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => icon), _initializeWorkspaceCreationIcons() { let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container'); From c5619a7dc93d5db06cdc20a83343c03e4289a0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Sat, 14 Sep 2024 03:07:44 -0600 Subject: [PATCH 13/42] feature(zenThemesImporter): add compatibility mode with legacy preferences --- src/ZenThemesImporter.mjs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index a5fbe63..7b1d957 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -125,17 +125,30 @@ var gZenThemeImporter = new (class { const themePath = PathUtils.join(this.getThemeFolder(theme), 'preferences.json'); if (!(await IOUtils.exists(themePath)) || !theme.preferences) { - return []; + return { preferences: [], isLegacyMode: false }; } - return IOUtils.readJSON(themePath); + let preferences = await IOUtils.readJSON(themePath); + + // skip transformation, we won't be writing old preferences to dom, all of them can only be checkboxes + if (typeof preferences === "object" && !Array.isArray(preferences)) { + return { preferences: [], areOldPreferences: true }; + } + + return { preferences, areOldPreferences: false }; } async writeToDom() { const browser = this._getBrowser() for (const theme of Object.values(await this.getThemes())) { - const themePreferences = (await this._getThemePreferences(theme)).filter(({ type }) => type === "dropdown") + const { preferences, areOldPreferences } = await this._getThemePreferences(theme); + + if (areOldPreferences) { + continue; + } + + const themePreferences = preferences.filter(({ type }) => type === "dropdown") for (const { property } of themePreferences) { const value = Services.prefs.getStringPref(property, "") From 62c056b4d00c6426083ea23ed27f170b72ea145f Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sat, 14 Sep 2024 11:34:26 +0200 Subject: [PATCH 14/42] Refactor ZenWorkspaces.mjs to discard unloaded tabs when hiding workspace --- src/ZenWorkspaces.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 36f3bd0..ece391a 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -508,6 +508,8 @@ var ZenWorkspaces = { } for (let tab of gBrowser.tabs) { if (tab.getAttribute('zen-workspace-id') !== window.uuid) { + // FOR UNLOADING TABS: + // gBrowser.discardBrowser(tab, true); gBrowser.hideTab(tab, undefined, shouldAllowPinnedTabs); } } From 03514248e5466c93588ee8e69027f82b4320aeb4 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sat, 14 Sep 2024 12:34:43 +0200 Subject: [PATCH 15/42] Fixed split view shortcut to split a single tab if other are hidden --- src/ZenViewSplitter.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 4361fc5..51494af 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -562,7 +562,7 @@ var gZenViewSplitter = new (class { this.unsplitCurrentView(); return; } - const tabs = gBrowser.tabs; + const tabs = gBrowser.visibleTabs; if (tabs.length < 2) { return; } From 4d8d109e7878c85d500e8f0042b4f05cb28123fc Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sat, 14 Sep 2024 12:46:13 +0200 Subject: [PATCH 16/42] Refactor ZenThemesImporter to skip disabled themes when writing the stylesheet --- src/ZenThemesImporter.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 7b1d957..5d3f7b5 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -12,6 +12,9 @@ var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; for (let theme of themes) { + if (theme.disabled) { + continue; + } content += this.getThemeCSS(theme); } content += kenStylesheetFooter; From 304b688473e48c2dee2752a2c29a44a6adac87c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Sat, 14 Sep 2024 14:32:54 -0600 Subject: [PATCH 17/42] refactor(zenThemesImporter): renamed disabled to enabled - remove dom elements on enabled = false --- src/ZenThemesImporter.mjs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 5d3f7b5..3326214 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -12,7 +12,7 @@ var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; for (let theme of themes) { - if (theme.disabled) { + if (!theme.enabled) { continue; } content += this.getThemeCSS(theme); @@ -147,6 +147,16 @@ var gZenThemeImporter = new (class { for (const theme of Object.values(await this.getThemes())) { const { preferences, areOldPreferences } = await this._getThemePreferences(theme); + if (!theme.enabled) { + const element = browser.document.getElementById(theme.name); + + if (element) { + element.remove() + } + + continue; + } + if (areOldPreferences) { continue; } From ff7a75998bf984fcb18f88a74bae8b414c080f47 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sat, 14 Sep 2024 22:55:45 +0200 Subject: [PATCH 18/42] Refactor ZenThemeMarketplaceParent to enable the installed theme --- src/actors/ZenThemeMarketplaceParent.sys.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/actors/ZenThemeMarketplaceParent.sys.mjs b/src/actors/ZenThemeMarketplaceParent.sys.mjs index 443f7c0..036e5f8 100644 --- a/src/actors/ZenThemeMarketplaceParent.sys.mjs +++ b/src/actors/ZenThemeMarketplaceParent.sys.mjs @@ -8,6 +8,7 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent { case 'ZenThemeMarketplace:InstallTheme': { console.info('ZenThemeMarketplaceParent: Updating themes'); const theme = message.data.theme; + theme.enabled = true; const themes = await this.getThemes(); themes[theme.id] = theme; this.updateThemes(themes); From d7715eb7542283ae06b1d0ee0f17ef349aa6f343 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sat, 14 Sep 2024 23:15:45 +0200 Subject: [PATCH 19/42] Fixed themes glitching --- src/ZenThemesImporter.mjs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 3326214..21428d7 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -83,9 +83,9 @@ var gZenThemeImporter = new (class { return this._themes; } - rebuildThemeStylesheet() { + async rebuildThemeStylesheet() { this._themes = null; - this.updateStylesheet(); + await this.updateStylesheet(); } get styleSheetURI() { @@ -99,21 +99,21 @@ var gZenThemeImporter = new (class { return Services.io.newFileURI(new FileUtils.File(PathUtils.join(this.getThemeFolder(theme), 'chrome.css'))); } - insertStylesheet() { - if (IOUtils.exists(this.styleSheetPath)) { - this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET); + async insertStylesheet() { + if (await IOUtils.exists(this.styleSheetPath)) { + await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET); } } - removeStylesheet() { - this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET); + async removeStylesheet() { + await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET); } async updateStylesheet() { - this.removeStylesheet(); + await this.removeStylesheet(); await this.writeStylesheet(); await this.writeToDom(); - this.insertStylesheet(); + await this.insertStylesheet(); } _getBrowser() { From 9c29cf23e761192719e4c7707a3b4b53ee5be681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Sat, 14 Sep 2024 16:44:21 -0600 Subject: [PATCH 20/42] fix(zen-settings): sanitize properties and theme name to prevent css issues --- src/ZenThemesImporter.mjs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 21428d7..f30919c 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -118,10 +118,10 @@ var gZenThemeImporter = new (class { _getBrowser() { if (!this.__browser) { - this.__browser = Services.wm.getMostRecentWindow("navigator:browser") + this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); } - return this.__browser + return this.__browser; } async _getThemePreferences(theme) { @@ -134,7 +134,7 @@ var gZenThemeImporter = new (class { let preferences = await IOUtils.readJSON(themePath); // skip transformation, we won't be writing old preferences to dom, all of them can only be checkboxes - if (typeof preferences === "object" && !Array.isArray(preferences)) { + if (typeof preferences === 'object' && !Array.isArray(preferences)) { return { preferences: [], areOldPreferences: true }; } @@ -142,16 +142,17 @@ var gZenThemeImporter = new (class { } async writeToDom() { - const browser = this._getBrowser() + const browser = this._getBrowser(); for (const theme of Object.values(await this.getThemes())) { const { preferences, areOldPreferences } = await this._getThemePreferences(theme); + const sanitizedName = theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, ''); if (!theme.enabled) { - const element = browser.document.getElementById(theme.name); + const element = browser.document.getElementById(sanitizedName); if (element) { - element.remove() + element.remove(); } continue; @@ -161,24 +162,24 @@ var gZenThemeImporter = new (class { continue; } - const themePreferences = preferences.filter(({ type }) => type === "dropdown") + const themePreferences = preferences.filter(({ type }) => type === 'dropdown'); for (const { property } of themePreferences) { - const value = Services.prefs.getStringPref(property, "") + const value = Services.prefs.getStringPref(property, ''); - if (value !== "") { - let element = browser.document.getElementById(theme.name) + if (value !== '') { + let element = browser.document.getElementById(sanitizedName); if (!element) { - element = browser.document.createElement("div") + element = browser.document.createElement('div'); - element.style.display = "none" - element.setAttribute("id", theme.name) + element.style.display = 'none'; + element.setAttribute('id', sanitizedName); - browser.document.body.appendChild(element) + browser.document.body.appendChild(element); } - element.setAttribute(property, value) + element.setAttribute(property?.replaceAll(/\./g, '-'), value); } } } From 5a183e161e6207ac080ab7e1118bc0d7340219d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Sat, 14 Sep 2024 16:53:22 -0600 Subject: [PATCH 21/42] refactor(zen-settings): add theme- prefix --- src/ZenThemesImporter.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index f30919c..84d1338 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -146,7 +146,7 @@ var gZenThemeImporter = new (class { for (const theme of Object.values(await this.getThemes())) { const { preferences, areOldPreferences } = await this._getThemePreferences(theme); - const sanitizedName = theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, ''); + const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; if (!theme.enabled) { const element = browser.document.getElementById(sanitizedName); From 7692662dd76d087ca905fa898273e0059a4dba92 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sun, 15 Sep 2024 09:40:52 +0200 Subject: [PATCH 22/42] Refactor ZenViewSplitter to fix tab closing bug --- src/ZenViewSplitter.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 51494af..1bac78d 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -536,8 +536,9 @@ var gZenViewSplitter = new (class { if (this.currentView < 0) return; const currentTab = window.gBrowser.selectedTab; const tabs = this._data[this.currentView].tabs; - for (const tab of tabs) { - this.handleTabClose({ target: tab, forUnsplit: true }); + // note: This MUST be an index loop, as we are removing tabs from the array + for (let i = tabs.length - 1; i >= 0; i--) { + this.handleTabClose({ target: tabs[i], forUnsplit: true }); } window.gBrowser.selectedTab = currentTab; this.updateSplitViewButton(true); From 57817faf4c782e9799e03259e89478c04385e0ed Mon Sep 17 00:00:00 2001 From: Anatasia Date: Sun, 15 Sep 2024 15:24:24 +0700 Subject: [PATCH 23/42] Update ZenKeyboardShortcuts.mjs --- src/ZenKeyboardShortcuts.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ZenKeyboardShortcuts.mjs b/src/ZenKeyboardShortcuts.mjs index 7138be2..6f09277 100644 --- a/src/ZenKeyboardShortcuts.mjs +++ b/src/ZenKeyboardShortcuts.mjs @@ -23,6 +23,7 @@ const kZKSActions = { showNextTab: ['gBrowser.tabContainer.advanceSelectedTab(1, true)', 'show-next-tab', 'tab-action'], showPreviousTab: ['gBrowser.tabContainer.advanceSelectedTab(-1, true)', 'show-previous-tab', 'tab-action'], showAllTabsPanel: ['gTabsPanel.showAllTabsPanel()', 'show-all-tabs-panel', 'tab-action'], + focusUrlbar: ['command:Browser:OpenLocation', 'focus-urlbar', 'tab-action'], // Compact mode actions zenToggleCompactMode: ['gZenCompactModeManager.toggle()', 'zen-toggle-compact-mode', 'compact-mode-action'], From 6aa50058bff6d6be94d6bb1e1f155e409b5429e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?mauro=20=F0=9F=A4=99?= <91018726+mauro-balades@users.noreply.github.com> Date: Sun, 15 Sep 2024 03:32:29 -0700 Subject: [PATCH 24/42] Revert "Update ZenKeyboardShortcuts.mjs" --- src/ZenKeyboardShortcuts.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ZenKeyboardShortcuts.mjs b/src/ZenKeyboardShortcuts.mjs index 6f09277..7138be2 100644 --- a/src/ZenKeyboardShortcuts.mjs +++ b/src/ZenKeyboardShortcuts.mjs @@ -23,7 +23,6 @@ const kZKSActions = { showNextTab: ['gBrowser.tabContainer.advanceSelectedTab(1, true)', 'show-next-tab', 'tab-action'], showPreviousTab: ['gBrowser.tabContainer.advanceSelectedTab(-1, true)', 'show-previous-tab', 'tab-action'], showAllTabsPanel: ['gTabsPanel.showAllTabsPanel()', 'show-all-tabs-panel', 'tab-action'], - focusUrlbar: ['command:Browser:OpenLocation', 'focus-urlbar', 'tab-action'], // Compact mode actions zenToggleCompactMode: ['gZenCompactModeManager.toggle()', 'zen-toggle-compact-mode', 'compact-mode-action'], From 329b197bda1a40e3f0fe9a7d69b6ec3798e63bed Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Sun, 15 Sep 2024 19:20:32 +0200 Subject: [PATCH 25/42] Fixed workspaces --- src/ZenWorkspaces.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index f747e6a..fa877b3 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -490,13 +490,14 @@ var ZenWorkspaces = { } this.unsafeSaveWorkspaces(workspaces); console.info('ZenWorkspaces: Changing workspace to', window.uuid); - for (let tab of gBrowser.tabs) { + for (let i = 0; i < gBrowser.tabs.length; i++) { + let tab = gBrowser.tabs[i]; if ((tab.getAttribute('zen-workspace-id') === window.uuid && !(tab.pinned && !shouldAllowPinnedTabs)) || !tab.hasAttribute('zen-workspace-id')) { if (!firstTab) { firstTab = tab; } else if (gBrowser.selectedTab === tab) { // If the selected tab is already in the workspace, we don't want to change it - firstTab = undefined; + firstTab = null; // note: Do not add "undefined" here, a new tab would be created } gBrowser.showTab(tab); if (!tab.hasAttribute('zen-workspace-id')) { @@ -512,7 +513,8 @@ var ZenWorkspaces = { if (typeof firstTab === 'undefined' && !onInit) { this._createNewTabForWorkspace(window); } - for (let tab of gBrowser.tabs) { + for (let i = 0; i < gBrowser.tabs.length; i++) { + let tab = gBrowser.tabs[i]; if (tab.getAttribute('zen-workspace-id') !== window.uuid) { // FOR UNLOADING TABS: // gBrowser.discardBrowser(tab, true); From b9c35455ad1a299cdb9202dba15c8e1dc3780191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauro=20Balad=C3=A9s?= Date: Sun, 15 Sep 2024 20:00:14 -0700 Subject: [PATCH 26/42] Refactor ZenWorkspaces to use getUserContextLabel instead of formatContextLabel --- src/ZenWorkspaces.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index fa877b3..303933c 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -269,8 +269,8 @@ var ZenWorkspaces = { childs.querySelector('.zen-workspace-icon').textContent = this.getWorkspaceIcon(workspace); childs.querySelector('.zen-workspace-name').textContent = workspace.name; if (containerGroup) { - childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.formatContextLabel( - containerGroup.l10nId + childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel( + containerGroup.userContextId ); } From fa248aecd98dbdf2ad05968a67b98dd0148b96c1 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Mon, 16 Sep 2024 07:51:34 +0200 Subject: [PATCH 27/42] Refactor ZenWorkspaces to initialize workspace icons and context menus --- src/ZenWorkspaces.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index fa877b3..ec73d63 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -65,6 +65,9 @@ var ZenWorkspaces = { await IOUtils.writeJSON(this._storeFile, {}); } if (this.workspaceEnabled) { + this._initializeWorkspaceCreationIcons(); + this._initializeWorkspaceEditIcons(); + this._initializeWorkspaceTabContextMenus(); window.addEventListener('TabClose', this.handleTabClose.bind(this)); let workspaces = await this._workspaces(); if (workspaces.workspaces.length === 0) { @@ -83,9 +86,6 @@ var ZenWorkspaces = { } this.changeWorkspace(activeWorkspace, true); } - this._initializeWorkspaceCreationIcons(); - this._initializeWorkspaceEditIcons(); - this._initializeWorkspaceTabContextMenus(); } }, From a1072db227939066dfc74ed298f90e0a3348a974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Mon, 16 Sep 2024 01:21:00 -0600 Subject: [PATCH 28/42] feature(zenThemesImporter): add string type theme property writing to dom on startup --- src/ZenThemesImporter.mjs | 46 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 84d1338..90eddb6 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -145,7 +145,9 @@ var gZenThemeImporter = new (class { const browser = this._getBrowser(); for (const theme of Object.values(await this.getThemes())) { - const { preferences, areOldPreferences } = await this._getThemePreferences(theme); + const { preferences, areOldPreferences } = (await this._getThemePreferences(theme)).filter( + ({ type }) => type !== 'checkbox' + ); const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; if (!theme.enabled) { @@ -155,6 +157,10 @@ var gZenThemeImporter = new (class { element.remove(); } + if (document.querySelector(':root').style.hasProperty(`--${sanitizedProperty}`)) { + document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); + } + continue; } @@ -162,24 +168,40 @@ var gZenThemeImporter = new (class { continue; } - const themePreferences = preferences.filter(({ type }) => type === 'dropdown'); - - for (const { property } of themePreferences) { + for (const { property, type } of preferences) { const value = Services.prefs.getStringPref(property, ''); + const sanitizedProperty = property?.replaceAll(/\./g, '-'); - if (value !== '') { - let element = browser.document.getElementById(sanitizedName); + switch (type) { + case 'dropdown': { + if (value !== '') { + let element = browser.document.getElementById(sanitizedName); - if (!element) { - element = browser.document.createElement('div'); + if (!element) { + element = browser.document.createElement('div'); - element.style.display = 'none'; - element.setAttribute('id', sanitizedName); + element.style.display = 'none'; + element.setAttribute('id', sanitizedName); - browser.document.body.appendChild(element); + browser.document.body.appendChild(element); + } + + element.setAttribute(sanitizedProperty, value); + } + break; } - element.setAttribute(property?.replaceAll(/\./g, '-'), value); + case 'string': { + if (value === '') { + document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); + } else { + document.querySelector(':root').style.setProperty(`--${sanitizedProperty}`, value); + } + break; + } + + default: { + } } } } From df1c0886d6eac4babcd4b9e7dec64402942b440f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Mon, 16 Sep 2024 12:17:18 -0600 Subject: [PATCH 29/42] fix(zenThemesImporter): fixed wrong preferences filtering --- src/ZenThemesImporter.mjs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 90eddb6..337ae9c 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -145,9 +145,13 @@ var gZenThemeImporter = new (class { const browser = this._getBrowser(); for (const theme of Object.values(await this.getThemes())) { - const { preferences, areOldPreferences } = (await this._getThemePreferences(theme)).filter( - ({ type }) => type !== 'checkbox' - ); + const { preferences, areOldPreferences } = await this._getThemePreferences(theme); + + if (areOldPreferences) { + continue; + } + + const filteredPreferences = preferences.filter(({ type }) => type !== 'checkbox'); const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; if (!theme.enabled) { @@ -157,17 +161,17 @@ var gZenThemeImporter = new (class { element.remove(); } - if (document.querySelector(':root').style.hasProperty(`--${sanitizedProperty}`)) { - document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); + for (const { property } of filteredPreferences) { + const sanitizedProperty = property?.replaceAll(/\./g, '-'); + + if (document.querySelector(':root').style.hasProperty(`--${sanitizedProperty}`)) { + document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); + } } continue; } - if (areOldPreferences) { - continue; - } - for (const { property, type } of preferences) { const value = Services.prefs.getStringPref(property, ''); const sanitizedProperty = property?.replaceAll(/\./g, '-'); From f4c876c8dee8d2dd15243f096e36e05cfc6f63c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristijan=20Ribari=C4=87?= Date: Mon, 16 Sep 2024 21:10:34 +0200 Subject: [PATCH 30/42] fix: Await workspace save in `changeWorkspace` This change ensures that the `unsafeSaveWorkspaces` function is called asynchronously. This is necessary to prevent blocking the UI thread while saving the workspaces. --- src/ZenWorkspaces.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 1377f8b..77b8c7c 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -488,7 +488,7 @@ var ZenWorkspaces = { for (let workspace of workspaces.workspaces) { workspace.used = workspace.uuid === window.uuid; } - this.unsafeSaveWorkspaces(workspaces); + await this.unsafeSaveWorkspaces(workspaces); console.info('ZenWorkspaces: Changing workspace to', window.uuid); for (let i = 0; i < gBrowser.tabs.length; i++) { let tab = gBrowser.tabs[i]; From 437fdfecb5e45645084b135427193c2042af482f Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Mon, 16 Sep 2024 22:53:45 +0200 Subject: [PATCH 31/42] Refactor ZenWorkspaces to add event listener for TabBrowserInserted --- src/ZenWorkspaces.mjs | 27 +++++++++++++++++--- src/actors/ZenThemeMarketplaceParent.sys.mjs | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 1377f8b..fca62ab 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -69,6 +69,7 @@ var ZenWorkspaces = { this._initializeWorkspaceEditIcons(); this._initializeWorkspaceTabContextMenus(); window.addEventListener('TabClose', this.handleTabClose.bind(this)); + window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this)); let workspaces = await this._workspaces(); if (workspaces.workspaces.length === 0) { await this.createAndSaveWorkspace('Default Workspace', true); @@ -478,10 +479,16 @@ var ZenWorkspaces = { return Services.prefs.getBoolPref('zen.workspaces.individual-pinned-tabs'); }, + get tabContainer() { + delete this.tabContainer; + return (this.tabContainer = document.getElementById("tabbrowser-tabs")); + }, + async changeWorkspace(window, onInit = false) { if (!this.workspaceEnabled) { return; } + this.tabContainer._invalidateCachedTabs(); const shouldAllowPinnedTabs = this._shouldAllowPinTab; let firstTab = undefined; let workspaces = await this._workspaces(); @@ -490,8 +497,7 @@ var ZenWorkspaces = { } this.unsafeSaveWorkspaces(workspaces); console.info('ZenWorkspaces: Changing workspace to', window.uuid); - for (let i = 0; i < gBrowser.tabs.length; i++) { - let tab = gBrowser.tabs[i]; + for (let tab of gBrowser.tabs) { if ((tab.getAttribute('zen-workspace-id') === window.uuid && !(tab.pinned && !shouldAllowPinnedTabs)) || !tab.hasAttribute('zen-workspace-id')) { if (!firstTab) { firstTab = tab; @@ -513,14 +519,14 @@ var ZenWorkspaces = { if (typeof firstTab === 'undefined' && !onInit) { this._createNewTabForWorkspace(window); } - for (let i = 0; i < gBrowser.tabs.length; i++) { - let tab = gBrowser.tabs[i]; + for (let tab of gBrowser.tabs) { if (tab.getAttribute('zen-workspace-id') !== window.uuid) { // FOR UNLOADING TABS: // gBrowser.discardBrowser(tab, true); gBrowser.hideTab(tab, undefined, shouldAllowPinnedTabs); } } + this.tabContainer._invalidateCachedTabs(); document.documentElement.setAttribute('zen-workspace-id', window.uuid); await this.saveWorkspaces(); await this._updateWorkspacesButton(); @@ -573,6 +579,19 @@ var ZenWorkspaces = { await this.changeWorkspace(workspaceData); }, + async onTabBrowserInserted(event) { + let tab = event.originalTarget; + if (tab.getAttribute('zen-workspace-id') || !this.workspaceEnabled) { + return; + } + let workspaces = await this._workspaces(); + let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used); + if (!activeWorkspace) { + return; + } + tab.setAttribute('zen-workspace-id', activeWorkspace.uuid); + }, + async onLocationChange(browser) { let tab = gBrowser.getTabForBrowser(browser); let workspaceID = tab.getAttribute('zen-workspace-id'); diff --git a/src/actors/ZenThemeMarketplaceParent.sys.mjs b/src/actors/ZenThemeMarketplaceParent.sys.mjs index 036e5f8..b92abea 100644 --- a/src/actors/ZenThemeMarketplaceParent.sys.mjs +++ b/src/actors/ZenThemeMarketplaceParent.sys.mjs @@ -74,6 +74,7 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent { } if (!this.compareversion(themeInfo.version, theme.version || '0.0.0') && themeInfo.version != theme.version) { console.info('ZenThemeMarketplaceParent: Theme update found', theme.id, theme.version, themeInfo.version); + themeInfo.enabled = theme.enabled; updates.push(themeInfo); await this.removeTheme(theme.id, false); this._themes[themeInfo.id] = themeInfo; From efd88c476896b416aab4e8dc32f18849b510bae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Mon, 16 Sep 2024 22:39:17 -0600 Subject: [PATCH 32/42] refactor(zenSettings): parse legacy settings using new format - replace ! format with json property disabledOn - added new default property - write defaults on theme install --- src/ZenThemesImporter.mjs | 145 ++++++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 23 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 337ae9c..3613015 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -8,6 +8,13 @@ const kZenStylesheetThemeHeader = ` const kenStylesheetFooter = ` /* End of Zen Themes */ `; + +const kZenOSToSmallName = { + WINNT: 'windows', + Darwin: 'macos', + Linux: 'linux', +}; + var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; @@ -41,7 +48,20 @@ var gZenThemeImporter = new (class { try { window.SessionStore.promiseInitialized.then(async () => { this.insertStylesheet(); - await this.writeToDom(); + + const themesWithPreferences = await Promise.all( + Object.values(await this.getThemes()).map(async (theme) => { + const preferences = await this._getThemePreferences(theme); + + return { + name: theme.name, + enabled: theme.enabled, + preferences, + }; + }) + ); + + this.writeToDom(themesWithPreferences); }); console.info('ZenThemeImporter: Zen theme imported'); } catch (e) { @@ -78,6 +98,7 @@ var gZenThemeImporter = new (class { if (!(await IOUtils.exists(this.themesDataFile))) { await IOUtils.writeJSON(this.themesDataFile, {}); } + this._themes = await IOUtils.readJSON(this.themesDataFile); } return this._themes; @@ -111,8 +132,25 @@ var gZenThemeImporter = new (class { async updateStylesheet() { await this.removeStylesheet(); - await this.writeStylesheet(); - await this.writeToDom(); + + const themes = Object.values(await this.getThemes()); + await this.writeStylesheet(themes); + + const themesWithPreferences = await Promise.all( + themes.map(async (theme) => { + const preferences = await this._getThemePreferences(theme); + + return { + name: theme.name, + enabled: theme.enabled, + preferences, + }; + }) + ); + + this.setDefaults(themesWithPreferences); + this.writeToDom(themesWithPreferences); + await this.insertStylesheet(); } @@ -124,44 +162,103 @@ var gZenThemeImporter = new (class { return this.__browser; } + get currentOperatingSystem() { + let os = Services.appinfo.OS; + return kZenOSToSmallName[os]; + } + async _getThemePreferences(theme) { const themePath = PathUtils.join(this.getThemeFolder(theme), 'preferences.json'); if (!(await IOUtils.exists(themePath)) || !theme.preferences) { - return { preferences: [], isLegacyMode: false }; + return []; } - let preferences = await IOUtils.readJSON(themePath); + const preferences = await IOUtils.readJSON(themePath); - // skip transformation, we won't be writing old preferences to dom, all of them can only be checkboxes if (typeof preferences === 'object' && !Array.isArray(preferences)) { - return { preferences: [], areOldPreferences: true }; - } + console.warn( + `[ZenThemesImporter]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences` + ); + const newThemePreferences = []; - return { preferences, areOldPreferences: false }; - } + for (let [entry, label] of Object.entries(preferences)) { + const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry); + const isNegation = negation === '!'; - async writeToDom() { - const browser = this._getBrowser(); + if ( + (isNegation && os === this.currentOperatingSystem) || + (os !== '' && os !== this.currentOperatingSystem && !isNegation) + ) { + continue; + } - for (const theme of Object.values(await this.getThemes())) { - const { preferences, areOldPreferences } = await this._getThemePreferences(theme); - - if (areOldPreferences) { - continue; + newThemePreferences.push({ + property, + label, + type: 'checkbox', + disabledOn: os !== '' ? [os] : [], + }); } - const filteredPreferences = preferences.filter(({ type }) => type !== 'checkbox'); - const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; + return newThemePreferences; + } - if (!theme.enabled) { + return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); + } + + setDefaults(themesWithPreferences) { + for (const { preferences } of themesWithPreferences) { + for (const { type, property, defaultValue } of preferences) { + if (defaultValue === undefined) { + continue; + } + + switch (type) { + case 'checkbox': { + const value = Services.prefs.getBoolPref(property, false); + if (typeof defaultValue !== 'boolean') { + console.log(`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`); + continue; + } + + if (!value) { + Services.prefs.setBoolPref(property, defaultValue); + } + break; + } + + default: { + const value = Services.prefs.getStringPref(property, 'zen-property-no-saved'); + + if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') { + console.log(`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`); + continue; + } + + if (value === 'zen-property-no-saved') { + Services.prefs.setStringPref(property, defaultValue.toString()); + } + } + } + } + } + } + + writeToDom(themesWithPreferences) { + const browser = this._getBrowser(); + + for (const { enabled, preferences, name } of themesWithPreferences) { + const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; + + if (!enabled) { const element = browser.document.getElementById(sanitizedName); if (element) { element.remove(); } - for (const { property } of filteredPreferences) { + for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) { const sanitizedProperty = property?.replaceAll(/\./g, '-'); if (document.querySelector(':root').style.hasProperty(`--${sanitizedProperty}`)) { @@ -211,13 +308,15 @@ var gZenThemeImporter = new (class { } } - async writeStylesheet() { + async writeStylesheet(themeList) { const themes = []; this._themes = null; - for (let theme of Object.values(await this.getThemes())) { + + for (let theme of themeList) { theme._chromeURL = this.getStylesheetURIForTheme(theme).spec; themes.push(theme); } + await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes); } })(); From 0f775df5e64bfd41bfbbdf857a21fc5f92a44804 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 18 Sep 2024 00:06:02 +0200 Subject: [PATCH 33/42] Refactor ZenWorkspaces initialization and workspace check --- src/ZenWorkspaces.mjs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index b30371c..09ae4b1 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -5,13 +5,8 @@ var ZenWorkspaces = { */ _lastSelectedWorkspaceTabs: {}, - async init() { - let docElement = document.documentElement; - if ( - docElement.getAttribute('chromehidden').includes('toolbar') || - docElement.getAttribute('chromehidden').includes('menubar') || - docElement.hasAttribute('privatebrowsingmode') - ) { + init() { + if (!this.shouldHaveWorkspaces) { console.warn('ZenWorkspaces: !!! ZenWorkspaces is disabled in hidden windows !!!'); return; // We are in a hidden window, don't initialize ZenWorkspaces } @@ -22,8 +17,19 @@ var ZenWorkspaces = { }); }, + get shouldHaveWorkspaces() { + delete this.shouldHaveWorkspaces; + let docElement = document.documentElement; + this.shouldHaveWorkspaces = !(docElement.hasAttribute('privatebrowsingmode') + || docElement.getAttribute('chromehidden').includes('toolbar') + || docElement.getAttribute('chromehidden').includes('menubar')); + return this.shouldHaveWorkspaces; + }, + get workspaceEnabled() { - return Services.prefs.getBoolPref('zen.workspaces.enabled', false); + delete this.workspaceEnabled; + this.workspaceEnabled = Services.prefs.getBoolPref('zen.workspaces.enabled', false) && this.shouldHaveWorkspaces; + return this.workspaceEnabled; }, getActiveWorkspaceFromCache() { @@ -744,4 +750,3 @@ var ZenWorkspaces = { }, }; -ZenWorkspaces.init(); From 8f26f88495ad0e18ea8b7c2da56237e98357dc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 00:44:22 -0600 Subject: [PATCH 34/42] fix(zenThemesImporter): enable themes by default on no enabled flag --- src/ZenThemesImporter.mjs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 3613015..f305885 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -19,7 +19,7 @@ var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; for (let theme of themes) { - if (!theme.enabled) { + if (theme.enabled !== undefined && !theme.enabled) { continue; } content += this.getThemeCSS(theme); @@ -208,7 +208,11 @@ var gZenThemeImporter = new (class { } setDefaults(themesWithPreferences) { - for (const { preferences } of themesWithPreferences) { + for (const { preferences, enabled } of themesWithPreferences) { + if (enabled !== undefined && !enabled) { + continue; + } + for (const { type, property, defaultValue } of preferences) { if (defaultValue === undefined) { continue; @@ -251,7 +255,7 @@ var gZenThemeImporter = new (class { for (const { enabled, preferences, name } of themesWithPreferences) { const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; - if (!enabled) { + if (enabled !== undefined && !enabled) { const element = browser.document.getElementById(sanitizedName); if (element) { From 72396b8f55f74b0bc8e47d0fde89160622d1839b Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 18 Sep 2024 22:05:31 +0200 Subject: [PATCH 35/42] Refactor ZenSidebarManager to adjust parentRelativeHeight calculation --- src/ZenSidebarManager.mjs | 2 +- src/ZenWorkspaces.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ZenSidebarManager.mjs b/src/ZenSidebarManager.mjs index 71f2e8e..298ca30 100644 --- a/src/ZenSidebarManager.mjs +++ b/src/ZenSidebarManager.mjs @@ -79,7 +79,7 @@ var gZenBrowserManagerSidebar = { // relative to avoid the top margin // 20px is the padding let parentRelativeHeight = - parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + sidebar.getBoundingClientRect().top - 20; + parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + sidebar.getBoundingClientRect().top - 30; let minHeight = parseInt(computedStyle.getPropertyValue('min-height').replace('px', '')); if (!this._isDragging) { // Prevent multiple resizes diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 09ae4b1..ca3d49f 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -344,14 +344,14 @@ var ZenWorkspaces = { button.removeAttribute('hidden'); return; } - let browserTabs = document.getElementById('tabbrowser-tabs'); + const nextSibling = document.getElementById('zen-sidepanel-button'); let button = document.createElement('toolbarbutton'); button.id = 'zen-workspaces-button'; button.className = 'toolbarbutton-1 chromeclass-toolbar-additional'; button.setAttribute('label', 'Workspaces'); button.setAttribute('tooltiptext', 'Workspaces'); button.onclick = this.openWorkspacesDialog.bind(this); - browserTabs.insertAdjacentElement('beforebegin', button); + nextSibling.before(button); }, async _updateWorkspacesButton() { From da8446fc27539ab8bf623b3d43ce1e8fe93cc841 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Wed, 18 Sep 2024 22:09:34 +0200 Subject: [PATCH 36/42] Add ZenThemesCommon module --- src/ZenThemesCommon.mjs | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/ZenThemesCommon.mjs diff --git a/src/ZenThemesCommon.mjs b/src/ZenThemesCommon.mjs new file mode 100644 index 0000000..e69de29 From 36b93499d20b2b686168d0f2c90d20d0b6745a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 17:39:29 -0600 Subject: [PATCH 37/42] refactor(zenThemes): moved common utils to ZenThemesCommon --- src/ZenThemesCommon.mjs | 97 +++++++++++++++++++++++++++++++++++++++ src/ZenThemesImporter.mjs | 94 +++---------------------------------- 2 files changed, 103 insertions(+), 88 deletions(-) diff --git a/src/ZenThemesCommon.mjs b/src/ZenThemesCommon.mjs index e69de29..6e2b4ce 100644 --- a/src/ZenThemesCommon.mjs +++ b/src/ZenThemesCommon.mjs @@ -0,0 +1,97 @@ +var ZenThemesCommon = { + kZenOSToSmallName: { + WINNT: 'windows', + Darwin: 'macos', + Linux: 'linux', + }, + + kZenColors: ['#aac7ff', '#74d7cb', '#a0d490', '#dec663', '#ffb787', '#dec1b1', '#ffb1c0', '#ddbfc3', '#f6b0ea', '#d4bbff'], + + get currentOperatingSystem() { + let os = Services.appinfo.OS; + return this.kZenOSToSmallName[os]; + }, + + get themesRootPath() { + return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes'); + }, + + get themesDataFile() { + return PathUtils.join(PathUtils.profileDir, 'zen-themes.json'); + }, + + getThemeFolder(themeId) { + return PathUtils.join(this.themesRootPath, themeId); + }, + + getBrowser() { + if (!this.__browser) { + this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); + } + + return this.__browser; + }, + + async getThemes() { + if (!this._themes) { + if (!(await IOUtils.exists(this.themesDataFile))) { + await IOUtils.writeJSON(this.themesDataFile, {}); + } + this._themes = await IOUtils.readJSON(this.themesDataFile); + } + return this._themes; + }, + + async getThemePreferences(theme) { + const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json'); + if (!(await IOUtils.exists(themePath)) || !theme.preferences) { + return []; + } + + const preferences = await IOUtils.readJSON(themePath); + + // compat mode for old preferences, all of them can only be checkboxes + if (typeof preferences === 'object' && !Array.isArray(preferences)) { + console.warn( + `[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences` + ); + const newThemePreferences = []; + + for (let [entry, label] of Object.entries(preferences)) { + const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry); + const isNegation = negation === '!'; + + if ( + (isNegation && os === this.currentOperatingSystem) || + (os !== '' && os !== this.currentOperatingSystem && !isNegation) + ) { + continue; + } + + newThemePreferences.push({ + property, + label, + type: 'checkbox', + disabledOn: os !== '' ? [os] : [], + }); + } + + return newThemePreferences; + } + + return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); + }, + + throttle(mainFunction, delay) { + let timerFlag = null; + + return (...args) => { + if (timerFlag === null) { + mainFunction(...args); + timerFlag = setTimeout(() => { + timerFlag = null; + }, delay); + } + }; + }, +}; diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index f305885..2e2d357 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -9,12 +9,6 @@ const kenStylesheetFooter = ` /* End of Zen Themes */ `; -const kZenOSToSmallName = { - WINNT: 'windows', - Darwin: 'macos', - Linux: 'linux', -}; - var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; @@ -50,8 +44,8 @@ var gZenThemeImporter = new (class { this.insertStylesheet(); const themesWithPreferences = await Promise.all( - Object.values(await this.getThemes()).map(async (theme) => { - const preferences = await this._getThemePreferences(theme); + Object.values(await ZenThemesCommon.getThemes()).map(async (theme) => { + const preferences = await ZenThemesCommon.getThemePreferences(theme); return { name: theme.name, @@ -81,29 +75,6 @@ var gZenThemeImporter = new (class { return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css'); } - get themesRootPath() { - return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes'); - } - - get themesDataFile() { - return PathUtils.join(PathUtils.profileDir, 'zen-themes.json'); - } - - getThemeFolder(theme) { - return PathUtils.join(this.themesRootPath, theme.id); - } - - async getThemes() { - if (!this._themes) { - if (!(await IOUtils.exists(this.themesDataFile))) { - await IOUtils.writeJSON(this.themesDataFile, {}); - } - - this._themes = await IOUtils.readJSON(this.themesDataFile); - } - return this._themes; - } - async rebuildThemeStylesheet() { this._themes = null; await this.updateStylesheet(); @@ -117,7 +88,7 @@ var gZenThemeImporter = new (class { } getStylesheetURIForTheme(theme) { - return Services.io.newFileURI(new FileUtils.File(PathUtils.join(this.getThemeFolder(theme), 'chrome.css'))); + return Services.io.newFileURI(new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))); } async insertStylesheet() { @@ -133,12 +104,12 @@ var gZenThemeImporter = new (class { async updateStylesheet() { await this.removeStylesheet(); - const themes = Object.values(await this.getThemes()); + const themes = Object.values(await ZenThemesCommon.getThemes()); await this.writeStylesheet(themes); const themesWithPreferences = await Promise.all( themes.map(async (theme) => { - const preferences = await this._getThemePreferences(theme); + const preferences = await ZenThemesCommon.getThemePreferences(theme); return { name: theme.name, @@ -154,59 +125,6 @@ var gZenThemeImporter = new (class { await this.insertStylesheet(); } - _getBrowser() { - if (!this.__browser) { - this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); - } - - return this.__browser; - } - - get currentOperatingSystem() { - let os = Services.appinfo.OS; - return kZenOSToSmallName[os]; - } - - async _getThemePreferences(theme) { - const themePath = PathUtils.join(this.getThemeFolder(theme), 'preferences.json'); - - if (!(await IOUtils.exists(themePath)) || !theme.preferences) { - return []; - } - - const preferences = await IOUtils.readJSON(themePath); - - if (typeof preferences === 'object' && !Array.isArray(preferences)) { - console.warn( - `[ZenThemesImporter]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences` - ); - const newThemePreferences = []; - - for (let [entry, label] of Object.entries(preferences)) { - const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry); - const isNegation = negation === '!'; - - if ( - (isNegation && os === this.currentOperatingSystem) || - (os !== '' && os !== this.currentOperatingSystem && !isNegation) - ) { - continue; - } - - newThemePreferences.push({ - property, - label, - type: 'checkbox', - disabledOn: os !== '' ? [os] : [], - }); - } - - return newThemePreferences; - } - - return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); - } - setDefaults(themesWithPreferences) { for (const { preferences, enabled } of themesWithPreferences) { if (enabled !== undefined && !enabled) { @@ -250,7 +168,7 @@ var gZenThemeImporter = new (class { } writeToDom(themesWithPreferences) { - const browser = this._getBrowser(); + const browser = ZenThemesCommon.getBrowser(); for (const { enabled, preferences, name } of themesWithPreferences) { const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; From f84a42cb7e4200dc1ceab31027706ea9bfcffdf7 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Thu, 19 Sep 2024 07:50:28 +0200 Subject: [PATCH 38/42] Refactor ZenWorkspaces to add event listener for TabBrowserInserted --- src/ZenWorkspaces.mjs | 48 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index ca3d49f..c11940f 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -65,7 +65,7 @@ var ZenWorkspaces = { async initializeWorkspaces() { Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this)); - this.initializeWorkspacesButton(); + await this.initializeWorkspacesButton(); let file = new FileUtils.File(this._storeFile); if (!file.exists()) { await IOUtils.writeJSON(this._storeFile, {}); @@ -240,6 +240,7 @@ var ZenWorkspaces = { async _propagateWorkspaceData() { let currentContainer = document.getElementById('PanelUI-zen-workspaces-current-info'); let workspaceList = document.getElementById('PanelUI-zen-workspaces-list'); + await this._expandWorkspacesStrip(); const createWorkspaceElement = (workspace) => { let element = document.createXULElement('toolbarbutton'); element.className = 'subviewbutton'; @@ -336,7 +337,7 @@ var ZenWorkspaces = { }).catch(console.error); }, - initializeWorkspacesButton() { + async initializeWorkspacesButton() { if (!this.workspaceEnabled) { return; } else if (document.getElementById('zen-workspaces-button')) { @@ -345,13 +346,42 @@ var ZenWorkspaces = { return; } const nextSibling = document.getElementById('zen-sidepanel-button'); - let button = document.createElement('toolbarbutton'); - button.id = 'zen-workspaces-button'; - button.className = 'toolbarbutton-1 chromeclass-toolbar-additional'; - button.setAttribute('label', 'Workspaces'); - button.setAttribute('tooltiptext', 'Workspaces'); - button.onclick = this.openWorkspacesDialog.bind(this); - nextSibling.before(button); + const wrapper = document.createXULElement('toolbarbutton'); + wrapper.id = 'zen-workspaces-buttons'; + wrapper.className = 'subviewbutton'; + nextSibling.before(wrapper); + await this._expandWorkspacesStrip(); + }, + + async _expandWorkspacesStrip() { + let workspaces = await this._workspaces(); + let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used); + let workspaceList = document.getElementById('zen-workspaces-buttons'); + workspaceList.innerHTML = ''; + for (let workspace of workspaces.workspaces) { + let button = document.createXULElement('toolbarbutton'); + button.className = 'subviewbutton'; + button.setAttribute('tooltiptext', workspace.name); + button.setAttribute('zen-workspace-id', workspace.uuid); + if (workspace.used) { + button.setAttribute('active', 'true'); + } + if (workspace.default) { + button.setAttribute('default', 'true'); + } + button.onclick = (async () => { + await this.changeWorkspace(workspace); + }).bind(this, workspace); + let icon = document.createXULElement('div'); + icon.className = 'zen-workspace-icon'; + icon.textContent = this.getWorkspaceIcon(workspace); + let name = document.createXULElement('div'); + name.className = 'zen-workspace-name'; + name.textContent = workspace.name; + button.appendChild(icon); + button.appendChild(name); + workspaceList.appendChild(button); + } }, async _updateWorkspacesButton() { From 1d5e336462a828b4c492a8049be04477c3a96847 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Thu, 19 Sep 2024 21:05:33 +0200 Subject: [PATCH 39/42] Refactor ZenWorkspaces to improve workspace icon strip functionality --- src/ZenWorkspaces.mjs | 117 +++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index c11940f..28ef6c4 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -17,6 +17,12 @@ var ZenWorkspaces = { }); }, + get shouldShowIconStrip() { + delete this.shouldShowIconStrip; + this.shouldShowIconStrip = Services.prefs.getBoolPref('zen.workspaces.show-icon-strip', true); + return this.shouldShowIconStrip; + }, + get shouldHaveWorkspaces() { delete this.shouldHaveWorkspaces; let docElement = document.documentElement; @@ -63,8 +69,14 @@ var ZenWorkspaces = { } }, + async onWorkspacesIconStripChanged() { + this.shouldShowIconStrip = Services.prefs.getBoolPref('zen.workspaces.show-icon-strip', true); + await this._expandWorkspacesStrip(); + }, + async initializeWorkspaces() { Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this)); + Services.prefs.addObserver('zen.workspaces.show-icon-strip', this.onWorkspacesIconStripChanged.bind(this)); await this.initializeWorkspacesButton(); let file = new FileUtils.File(this._storeFile); if (!file.exists()) { @@ -237,10 +249,14 @@ var ZenWorkspaces = { return workspace.name[0].toUpperCase(); }, - async _propagateWorkspaceData() { + async _propagateWorkspaceData({ + ignoreStrip = false + } = {}) { let currentContainer = document.getElementById('PanelUI-zen-workspaces-current-info'); let workspaceList = document.getElementById('PanelUI-zen-workspaces-list'); - await this._expandWorkspacesStrip(); + if (!ignoreStrip) { + await this._expandWorkspacesStrip(); + } const createWorkspaceElement = (workspace) => { let element = document.createXULElement('toolbarbutton'); element.className = 'subviewbutton'; @@ -330,7 +346,9 @@ var ZenWorkspaces = { } let target = event.target; let panel = document.getElementById('PanelUI-zen-workspaces'); - await this._propagateWorkspaceData(); + await this._propagateWorkspaceData({ + ignoreStrip: true + }); PanelMultiView.openPopup(panel, target, { position: 'bottomright topright', triggerEvent: event, @@ -347,40 +365,46 @@ var ZenWorkspaces = { } const nextSibling = document.getElementById('zen-sidepanel-button'); const wrapper = document.createXULElement('toolbarbutton'); - wrapper.id = 'zen-workspaces-buttons'; - wrapper.className = 'subviewbutton'; - nextSibling.before(wrapper); + wrapper.id = 'zen-workspaces-button'; + nextSibling.after(wrapper); await this._expandWorkspacesStrip(); }, async _expandWorkspacesStrip() { let workspaces = await this._workspaces(); - let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used); - let workspaceList = document.getElementById('zen-workspaces-buttons'); - workspaceList.innerHTML = ''; - for (let workspace of workspaces.workspaces) { - let button = document.createXULElement('toolbarbutton'); - button.className = 'subviewbutton'; - button.setAttribute('tooltiptext', workspace.name); - button.setAttribute('zen-workspace-id', workspace.uuid); - if (workspace.used) { - button.setAttribute('active', 'true'); + let workspaceList = document.getElementById('zen-workspaces-button'); + const newWorkspacesButton = document.createXULElement(this.shouldShowIconStrip ? 'hbox' : 'toolbarbutton'); + newWorkspacesButton.id = 'zen-workspaces-button'; + newWorkspacesButton.setAttribute('tooltiptext', 'Workspaces'); + + if (this.shouldShowIconStrip) { + for (let workspace of workspaces.workspaces) { + let button = document.createXULElement('toolbarbutton'); + button.className = 'subviewbutton'; + button.setAttribute('tooltiptext', workspace.name); + button.setAttribute('zen-workspace-id', workspace.uuid); + if (workspace.used) { + button.setAttribute('active', 'true'); + } + if (workspace.default) { + button.setAttribute('default', 'true'); + } + button.onclick = (async () => { + await this.changeWorkspace(workspace); + }).bind(this, workspace); + let icon = document.createXULElement('div'); + icon.className = 'zen-workspace-icon'; + icon.textContent = this.getWorkspaceIcon(workspace); + button.appendChild(icon); + newWorkspacesButton.appendChild(button); } - if (workspace.default) { - button.setAttribute('default', 'true'); - } - button.onclick = (async () => { - await this.changeWorkspace(workspace); - }).bind(this, workspace); - let icon = document.createXULElement('div'); - icon.className = 'zen-workspace-icon'; - icon.textContent = this.getWorkspaceIcon(workspace); - let name = document.createXULElement('div'); - name.className = 'zen-workspace-name'; - name.textContent = workspace.name; - button.appendChild(icon); - button.appendChild(name); - workspaceList.appendChild(button); + } + + workspaceList.after(newWorkspacesButton); + workspaceList.remove(); + + if (!this.shouldShowIconStrip) { + await this._updateWorkspacesButton(); } }, @@ -391,20 +415,33 @@ var ZenWorkspaces = { } let activeWorkspace = (await this._workspaces()).workspaces.find((workspace) => workspace.used); if (activeWorkspace) { - button.innerHTML = ` -
-
-
-
- `; + button.setAttribute('as-button', 'true'); + button.classList.add('toolbarbutton-1', 'zen-sidebar-action-button'); + + button.addEventListener('click', this.openWorkspacesDialog.bind(this)); + + const wrapper = document.createXULElement('hbox'); + wrapper.className = 'zen-workspace-sidebar-wrapper'; + + const icon = document.createElement('div'); + icon.className = 'zen-workspace-sidebar-icon'; + icon.textContent = this.getWorkspaceIcon(activeWorkspace); + // use text content instead of innerHTML to avoid XSS - button.querySelector('.zen-workspace-sidebar-name').textContent = activeWorkspace.name; - button.querySelector('.zen-workspace-sidebar-icon').textContent = this.getWorkspaceIcon(activeWorkspace); + const name = document.createElement('div'); + name.className = 'zen-workspace-sidebar-name'; + name.textContent = activeWorkspace.name; if (!this.workspaceHasIcon(activeWorkspace)) { - button.querySelector('.zen-workspace-sidebar-icon').setAttribute('no-icon', 'true'); + icon.setAttribute('no-icon', 'true'); } + + wrapper.appendChild(icon); + wrapper.appendChild(name); + + button.innerHTML = ''; + button.appendChild(wrapper); } }, From 20c2fc640f92fa0dbfdb720217cf0be0e8c411e7 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Fri, 20 Sep 2024 09:59:55 +0200 Subject: [PATCH 40/42] Refactor ZenWorkspaces to add attributes to newWorkspacesButton --- src/ZenWorkspaces.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 28ef6c4..1234cc2 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -375,6 +375,8 @@ var ZenWorkspaces = { let workspaceList = document.getElementById('zen-workspaces-button'); const newWorkspacesButton = document.createXULElement(this.shouldShowIconStrip ? 'hbox' : 'toolbarbutton'); newWorkspacesButton.id = 'zen-workspaces-button'; + newWorkspacesButton.setAttribute('removable', 'false'); + newWorkspacesButton.setAttribute('showInPrivateBrowsing', 'false'); newWorkspacesButton.setAttribute('tooltiptext', 'Workspaces'); if (this.shouldShowIconStrip) { From 3c66cbdc24936c4827443d6946baa708173c6d62 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Fri, 20 Sep 2024 13:33:01 +0200 Subject: [PATCH 41/42] Refactor ZenWorkspaces to fix newWorkspacesButton height calculation --- src/ZenSidebarManager.mjs | 2 +- src/ZenWorkspaces.mjs | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/ZenSidebarManager.mjs b/src/ZenSidebarManager.mjs index 298ca30..fbb2320 100644 --- a/src/ZenSidebarManager.mjs +++ b/src/ZenSidebarManager.mjs @@ -79,7 +79,7 @@ var gZenBrowserManagerSidebar = { // relative to avoid the top margin // 20px is the padding let parentRelativeHeight = - parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + sidebar.getBoundingClientRect().top - 30; + parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + sidebar.getBoundingClientRect().top; let minHeight = parseInt(computedStyle.getPropertyValue('min-height').replace('px', '')); if (!this._isDragging) { // Prevent multiple resizes diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 1234cc2..7281e9d 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -77,11 +77,11 @@ var ZenWorkspaces = { async initializeWorkspaces() { Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this)); Services.prefs.addObserver('zen.workspaces.show-icon-strip', this.onWorkspacesIconStripChanged.bind(this)); - await this.initializeWorkspacesButton(); let file = new FileUtils.File(this._storeFile); if (!file.exists()) { await IOUtils.writeJSON(this._storeFile, {}); } + await this.initializeWorkspacesButton(); if (this.workspaceEnabled) { this._initializeWorkspaceCreationIcons(); this._initializeWorkspaceEditIcons(); @@ -391,7 +391,11 @@ var ZenWorkspaces = { if (workspace.default) { button.setAttribute('default', 'true'); } - button.onclick = (async () => { + button.onclick = (async (_, event) => { + // Make sure it's not a context menu event + if (event.button !== 0) { + return; + } await this.changeWorkspace(workspace); }).bind(this, workspace); let icon = document.createXULElement('div'); @@ -400,6 +404,11 @@ var ZenWorkspaces = { button.appendChild(icon); newWorkspacesButton.appendChild(button); } + // Listen for context menu events and open the all workspaces dialog + newWorkspacesButton.addEventListener('contextmenu', (event) => { + event.preventDefault(); + this.openWorkspacesDialog(event); + }); } workspaceList.after(newWorkspacesButton); @@ -568,6 +577,10 @@ var ZenWorkspaces = { let firstTab = undefined; let workspaces = await this._workspaces(); for (let workspace of workspaces.workspaces) { + if (workspace.uuid === window.uuid && workspace.used) { + // If the workspace is already active, do nothing + return; + } workspace.used = workspace.uuid === window.uuid; } await this.unsafeSaveWorkspaces(workspaces); From 4a9f91b6cbd81941e1f79fb6cc3d9a2b4797f603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Fri, 20 Sep 2024 22:22:37 -0600 Subject: [PATCH 42/42] fix(zenThemes): themes not being updated in other windows --- src/ZenThemesCommon.mjs | 23 ++++++++++++----------- src/ZenThemesImporter.mjs | 20 +++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/ZenThemesCommon.mjs b/src/ZenThemesCommon.mjs index 6e2b4ce..b59ef92 100644 --- a/src/ZenThemesCommon.mjs +++ b/src/ZenThemesCommon.mjs @@ -12,6 +12,14 @@ var ZenThemesCommon = { return this.kZenOSToSmallName[os]; }, + get browsers() { + return Services.wm.getEnumerator('navigator:browser'); + }, + + get currentBrowser() { + return Services.wm.getMostRecentWindow('navigator:browser'); + }, + get themesRootPath() { return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes'); }, @@ -24,22 +32,15 @@ var ZenThemesCommon = { return PathUtils.join(this.themesRootPath, themeId); }, - getBrowser() { - if (!this.__browser) { - this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); - } - - return this.__browser; - }, - async getThemes() { - if (!this._themes) { + if (!this.themes) { if (!(await IOUtils.exists(this.themesDataFile))) { await IOUtils.writeJSON(this.themesDataFile, {}); } - this._themes = await IOUtils.readJSON(this.themesDataFile); + + this.themes = await IOUtils.readJSON(this.themesDataFile); } - return this._themes; + return this.themes; }, async getThemePreferences(theme) { diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 2e2d357..7302f34 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -12,14 +12,18 @@ const kenStylesheetFooter = ` var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; + for (let theme of themes) { if (theme.enabled !== undefined && !theme.enabled) { continue; } content += this.getThemeCSS(theme); } + content += kenStylesheetFooter; - let buffer = new TextEncoder().encode(content); + + const buffer = new TextEncoder().encode(content); + await IOUtils.write(path, buffer); }, @@ -76,7 +80,7 @@ var gZenThemeImporter = new (class { } async rebuildThemeStylesheet() { - this._themes = null; + ZenThemesCommon.themes = null; await this.updateStylesheet(); } @@ -168,7 +172,7 @@ var gZenThemeImporter = new (class { } writeToDom(themesWithPreferences) { - const browser = ZenThemesCommon.getBrowser(); + const browser = ZenThemesCommon.currentBrowser; for (const { enabled, preferences, name } of themesWithPreferences) { const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; @@ -183,9 +187,7 @@ var gZenThemeImporter = new (class { for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) { const sanitizedProperty = property?.replaceAll(/\./g, '-'); - if (document.querySelector(':root').style.hasProperty(`--${sanitizedProperty}`)) { - document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); - } + browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); } continue; @@ -216,9 +218,9 @@ var gZenThemeImporter = new (class { case 'string': { if (value === '') { - document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); + browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`); } else { - document.querySelector(':root').style.setProperty(`--${sanitizedProperty}`, value); + browser.document.querySelector(':root').style.setProperty(`--${sanitizedProperty}`, value); } break; } @@ -232,7 +234,7 @@ var gZenThemeImporter = new (class { async writeStylesheet(themeList) { const themes = []; - this._themes = null; + ZenThemesCommon.themes = null; for (let theme of themeList) { theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;