forked from ZenBrowserMirrors/zen-desktop
Compare commits
2 commits
dev
...
zen-librar
Author | SHA1 | Date | |
---|---|---|---|
|
11a671be05 | ||
|
3724f2a759 |
85 changed files with 2411 additions and 2879 deletions
|
@ -29,7 +29,7 @@
|
|||
|
||||
## 🖥️ Compatibility
|
||||
|
||||
Zen is currently built using Firefox version `139.0`! 🚀
|
||||
Zen is currently built using Firefox version `138.0.4`! 🚀
|
||||
|
||||
- [`Zen Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 139.0`!
|
||||
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
|
||||
|
|
|
@ -1 +1 @@
|
|||
da30619f3ea895b356ded705b8dff9e4f271198f
|
||||
82a08ea3ce2d17f21f3d45f4b5607a37590b0158
|
2
l10n
2
l10n
|
@ -1 +1 @@
|
|||
Subproject commit ebecb32da8929e4f14f9a20f40acb9dab401101c
|
||||
Subproject commit 644474b8c92e306288d835698eb6714081a650d8
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -9,7 +9,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@zen-browser/surfer": "^1.11.13"
|
||||
"@zen-browser/surfer": "^1.11.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.27.0",
|
||||
|
@ -817,9 +817,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zen-browser/surfer": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.11.13.tgz",
|
||||
"integrity": "sha512-D0TyunAWYtTdJkuUkYeOc6VBXzE9aoDs58kWu/Oi/aU3vd8IbqXPbZZfYwj5FWPWDReMrJUNkkKAEdbL44y9aw==",
|
||||
"version": "1.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.11.12.tgz",
|
||||
"integrity": "sha512-wny52xOFvZe5aPXxLVxEcAzDNEiWWoDiCZFlzsNxkyQ5Lw6vzqroMWpjQPJwBRJOc/JssgiXMdd1uwl2LLnovQ==",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@resvg/resvg-js": "^1.4.0",
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/zen-browser/desktop#readme",
|
||||
"dependencies": {
|
||||
"@zen-browser/surfer": "^1.11.13"
|
||||
"@zen-browser/surfer": "^1.11.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.27.0",
|
||||
|
|
|
@ -16,7 +16,6 @@ pref('browser.toolbars.bookmarks.visibility', 'never');
|
|||
pref("browser.bookmarks.openInTabClosesMenu", false);
|
||||
pref("browser.menu.showViewImageInfo", true);
|
||||
pref("findbar.highlightAll", true);
|
||||
|
||||
pref("layout.word_select.eat_space_to_next_word", false);
|
||||
|
||||
// Better Windows theming
|
||||
|
|
|
@ -22,17 +22,13 @@ pref('zen.mediacontrols.enabled', true);
|
|||
// Exposure:
|
||||
pref('zen.haptic-feedback.enabled', true);
|
||||
|
||||
pref('zen.mods.auto-update-days', 20); // In days
|
||||
#ifdef MOZILLA_OFFICIAL
|
||||
pref('zen.mods.auto-update', true);
|
||||
pref('zen.rice.api.url', 'https://share.zen-browser.app', locked);
|
||||
pref('zen.injections.match-urls', 'https://zen-browser.app/*,https://share.zen-browser.app/*', locked);
|
||||
#else
|
||||
pref('zen.mods.auto-update', false);
|
||||
pref('zen.rice.api.url', "http://localhost", locked);
|
||||
pref('zen.injections.match-urls', 'http://localhost/*', locked);
|
||||
#endif
|
||||
|
||||
pref('zen.rice.share.notice.accepted', false);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
|
||||
index f6e1391baf12abb91c85a95107bb3923118746c0..cac04aa288e8a305d0c8b28e0c919abce87658e5 100644
|
||||
index f6e1391baf12abb91c85a95107bb3923118746c0..76c7b75a4e29056110f1631a50047c4ddd8b1f4a 100644
|
||||
--- a/browser/base/content/aboutDialog.js
|
||||
+++ b/browser/base/content/aboutDialog.js
|
||||
@@ -52,7 +52,7 @@ function init() {
|
||||
|
@ -20,18 +20,3 @@ index f6e1391baf12abb91c85a95107bb3923118746c0..cac04aa288e8a305d0c8b28e0c919abc
|
|||
versionIdKey += "-nightly";
|
||||
let buildID = Services.appinfo.appBuildID;
|
||||
let year = buildID.slice(0, 4);
|
||||
@@ -125,14 +125,6 @@ function init() {
|
||||
window.close();
|
||||
});
|
||||
if (AppConstants.MOZ_UPDATER) {
|
||||
- document
|
||||
- .getElementById("aboutDialogHelpLink")
|
||||
- .addEventListener("click", () => {
|
||||
- openHelpLink("firefox-help");
|
||||
- });
|
||||
- document
|
||||
- .getElementById("submit-feedback")
|
||||
- .addEventListener("click", openFeedbackPage);
|
||||
document
|
||||
.getElementById("checkForUpdatesButton")
|
||||
.addEventListener("command", () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
diff --git a/browser/base/content/aboutDialog.xhtml b/browser/base/content/aboutDialog.xhtml
|
||||
index c64980810570fcea84e33fdc2d66ac42a79f4e46..6ef9bf4b88f0a0539d833f662c4dd890fd1fde93 100644
|
||||
index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88a08d42c1 100644
|
||||
--- a/browser/base/content/aboutDialog.xhtml
|
||||
+++ b/browser/base/content/aboutDialog.xhtml
|
||||
@@ -35,6 +35,7 @@
|
||||
|
@ -10,18 +10,7 @@ index c64980810570fcea84e33fdc2d66ac42a79f4e46..6ef9bf4b88f0a0539d833f662c4dd890
|
|||
</linkset>
|
||||
|
||||
<html:div id="aboutDialogContainer">
|
||||
@@ -102,10 +103,6 @@
|
||||
<label id="version" class="update"/>
|
||||
<label id="releasenotes" is="text-link" hidden="true" data-l10n-id="releaseNotes-link"/>
|
||||
</hbox>
|
||||
- <description class="text-blurb">
|
||||
- <label id="aboutDialogHelpLink" is="text-link" data-l10n-id="aboutdialog-help-user"/>
|
||||
- <label id="submit-feedback" is="text-link" data-l10n-id="aboutdialog-submit-feedback"/>
|
||||
- </description>
|
||||
</vbox>
|
||||
#endif
|
||||
</hbox>
|
||||
@@ -125,21 +122,17 @@
|
||||
@@ -125,21 +126,23 @@
|
||||
</description>
|
||||
</vbox>
|
||||
<description class="text-blurb" id="communityDesc" data-l10n-id="community-2">
|
||||
|
@ -29,10 +18,12 @@ index c64980810570fcea84e33fdc2d66ac42a79f4e46..6ef9bf4b88f0a0539d833f662c4dd890
|
|||
+ <label is="text-link" href="https://github.com/zen-browser/desktop" data-l10n-name="community-mozillaLink"/>
|
||||
<label is="text-link" useoriginprincipal="true" href="about:credits" data-l10n-name="community-creditsLink"/>
|
||||
</description>
|
||||
- <description class="text-blurb" id="contributeDesc" data-l10n-id="helpus">
|
||||
- <label is="text-link" href="https://foundation.mozilla.org/?form=firefox-about" data-l10n-name="helpus-donateLink"/>
|
||||
- <label is="text-link" href="https://www.mozilla.org/contribute/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
|
||||
- </description>
|
||||
+#if 0
|
||||
<description class="text-blurb" id="contributeDesc" data-l10n-id="helpus">
|
||||
<label is="text-link" href="https://foundation.mozilla.org/?form=firefox-about" data-l10n-name="helpus-donateLink"/>
|
||||
<label is="text-link" href="https://www.mozilla.org/contribute/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
|
||||
</description>
|
||||
+#endif
|
||||
</vbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
|
||||
index 73593191936cc345ee8e2c28cb251dc13f4c2fd4..e6c459c1ebc60a1f3930a55e212570f696bf07a0 100644
|
||||
index 992d07daaef1abc4554a43aa654888f66963c575..73e620b70b7ed14e9d140e875c2cd5f5ac31456b 100644
|
||||
--- a/browser/base/content/browser-addons.js
|
||||
+++ b/browser/base/content/browser-addons.js
|
||||
@@ -735,7 +735,7 @@ var gXPInstallObserver = {
|
||||
persistent: true,
|
||||
hideClose: true,
|
||||
popupOptions: {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -942,7 +942,7 @@ var gXPInstallObserver = {
|
||||
hideClose: true,
|
||||
timeout: Date.now() + 30000,
|
||||
popupOptions: {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2125,7 +2125,7 @@ var gUnifiedExtensions = {
|
||||
|
||||
panel.hidden = false;
|
||||
PanelMultiView.openPopup(panel, this._button, {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition,
|
||||
triggerEvent: aEvent,
|
||||
});
|
||||
}
|
||||
@@ -2294,18 +2294,20 @@ var gUnifiedExtensions = {
|
||||
@@ -2105,18 +2105,20 @@ var gUnifiedExtensions = {
|
||||
this._maybeMoveWidgetNodeBack(widgetId);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
|
||||
index 7d7e8697f02f90d4f336c9ab0a73a89848e0c21c..64e950106dd05b443ce72107613ac9cc405d56ea 100644
|
||||
index 7d7e8697f02f90d4f336c9ab0a73a89848e0c21c..3819ae72f97900b6d212f8a54550ae569d497741 100644
|
||||
--- a/browser/base/content/browser-box.inc.xhtml
|
||||
+++ b/browser/base/content/browser-box.inc.xhtml
|
||||
@@ -23,7 +23,15 @@
|
||||
@@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
<hbox flex="1" id="browser">
|
||||
+ <zen-library/>
|
||||
<box context="sidebar-context-menu" id="sidebar-main" hidden="true">
|
||||
<html:sidebar-main flex="1">
|
||||
<box id="vertical-tabs" slot="tabstrip" customizable="true" contextmenu="toolbar-context-menu"></box>
|
||||
@@ -23,7 +24,15 @@
|
||||
<browser id="sidebar" autoscroll="false" disablehistory="true" disablefullscreen="true" tooltip="aHTMLTooltip"/>
|
||||
</vbox>
|
||||
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-urlbar.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-workspaces.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-decks.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-library.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-folders.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-glance.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-popup.css" />
|
||||
|
@ -31,7 +32,8 @@
|
|||
# Scripts used all over the browser
|
||||
<script src="chrome://browser/content/ZenUIManager.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenMods.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
|
||||
|
@ -41,3 +43,4 @@
|
|||
<script src="chrome://browser/content/zen-components/ZenGlanceManager.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenMediaController.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenLibrary.mjs"></script>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
content/browser/ZenCustomizableUI.sys.mjs (../../zen/common/ZenCustomizableUI.sys.mjs)
|
||||
content/browser/zen-components/ZenUIMigration.mjs (../../zen/common/ZenUIMigration.mjs)
|
||||
content/browser/zen-components/ZenCommonUtils.mjs (../../zen/common/ZenCommonUtils.mjs)
|
||||
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/ZenSessionStore.mjs)
|
||||
|
||||
content/browser/zen-styles/zen-theme.css (../../zen/common/styles/zen-theme.css)
|
||||
content/browser/zen-styles/zen-buttons.css (../../zen/common/styles/zen-buttons.css)
|
||||
|
@ -35,7 +34,13 @@
|
|||
content/browser/zen-components/ZenViewSplitter.mjs (../../zen/split-view/ZenViewSplitter.mjs)
|
||||
content/browser/zen-styles/zen-decks.css (../../zen/split-view/zen-decks.css)
|
||||
|
||||
content/browser/zen-components/ZenMods.mjs (../../zen/mods/ZenMods.mjs)
|
||||
content/browser/zen-components/ZenThemesCommon.mjs (../../zen/mods/ZenThemesCommon.mjs)
|
||||
content/browser/zen-components/ZenThemesImporter.mjs (../../zen/mods/ZenThemesImporter.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (../../zen/mods/actors/ZenThemeMarketplaceParent.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (../../zen/mods/actors/ZenThemeMarketplaceChild.sys.mjs)
|
||||
|
||||
content/browser/zen-components/ZenLibrary.mjs (../../zen/library/ZenLibrary.mjs)
|
||||
content/browser/zen-styles/zen-library.css (../../zen/library/zen-library.css)
|
||||
|
||||
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
||||
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
||||
|
@ -55,6 +60,8 @@
|
|||
|
||||
content/browser/zen-components/ZenGlanceManager.mjs (../../zen/glance/ZenGlanceManager.mjs)
|
||||
content/browser/zen-styles/zen-glance.css (../../zen/glance/zen-glance.css)
|
||||
content/browser/zen-components/actors/ZenGlanceChild.sys.mjs (../../zen/glance/actors/ZenGlanceChild.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenGlanceParent.sys.mjs (../../zen/glance/actors/ZenGlanceParent.sys.mjs)
|
||||
|
||||
content/browser/zen-components/ZenFolders.mjs (../../zen/folders/ZenFolders.mjs)
|
||||
content/browser/zen-styles/zen-folders.css (../../zen/folders/zen-folders.css)
|
||||
|
|
|
@ -43,6 +43,11 @@
|
|||
|
||||
<command id="cmd_zenCopyCurrentURL" />
|
||||
<command id="cmd_zenCopyCurrentURLMarkdown" />
|
||||
|
||||
<command id="cmd_zenCtxDeleteWorkspace" />
|
||||
<command id="cmd_zenChangeWorkspaceName" />
|
||||
|
||||
<command id="cmd_zenToggleLibrary" />
|
||||
</commandset>
|
||||
|
||||
<keyset id="zenKeyset"></keyset>
|
||||
|
|
|
@ -57,67 +57,11 @@
|
|||
</panelmultiview>
|
||||
</panel>
|
||||
|
||||
<panel flip="slide" type="arrow" orient="vertical" id="PanelUI-zen-workspaces" position="bottomright topright" mainview="true" side="left">
|
||||
<panelmultiview id="PanelUI-zen-workspaces-multiview" mainViewId="PanelUI-zen-workspaces-view">
|
||||
<panelview id="PanelUI-zen-workspaces-view" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true" closemenu="none">
|
||||
<vbox>
|
||||
<hbox>
|
||||
<h3 data-l10n-id="zen-panel-ui-workspaces-text" id="PanelUI-zen-workspaces-header"></h3>
|
||||
<toolbarbutton id="PanelUI-zen-workspaces-reorder-mode" class="subviewbutton">
|
||||
<image></image>
|
||||
</toolbarbutton>
|
||||
<toolbarbutton id="PanelUI-zen-workspaces-new" class="subviewbutton">
|
||||
<image></image>
|
||||
</toolbarbutton>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<html:div id="PanelUI-zen-workspaces-list">
|
||||
</html:div>
|
||||
</panelview>
|
||||
<panelview id="PanelUI-zen-workspaces-create" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true">
|
||||
<vbox class="PanelUI-zen-workspaces-user-create">
|
||||
<h1 data-l10n-id="zen-panel-ui-workspaces-create-text"></h1>
|
||||
<hbox class="PanelUI-zen-workspaces-creation-wraper">
|
||||
<hbox class="PanelUI-zen-workspaces-icons-container create"></hbox>
|
||||
<html:input autofocus="true" id="PanelUI-zen-workspaces-create-input" type="text" placeholder="Enter workspace name" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
<html:moz-button-group class="panel-footer" id="PanelUI-zen-workspaces-create-footer">
|
||||
<button disabled="true" default="true" slot="primary" id="PanelUI-zen-workspaces-create-save" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-create-save">
|
||||
</button>
|
||||
<button id="PanelUI-zen-workspaces-create-cancel" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-create-cancel">
|
||||
</button>
|
||||
</html:moz-button-group>
|
||||
</panelview>
|
||||
<panelview id="PanelUI-zen-workspaces-edit" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true">
|
||||
<vbox class="PanelUI-zen-workspaces-user-create">
|
||||
<h1 data-l10n-id="zen-panel-ui-workspaces-edit-text"></h1>
|
||||
<hbox class="PanelUI-zen-workspaces-creation-wraper">
|
||||
<hbox class="PanelUI-zen-workspaces-icons-container edit"></hbox>
|
||||
<html:input autofocus="true" id="PanelUI-zen-workspaces-edit-input" type="text" placeholder="Enter workspace name" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
<html:moz-button-group class="panel-footer" id="PanelUI-zen-workspaces-edit-footer">
|
||||
<button disabled="true" default="true" slot="primary" id="PanelUI-zen-workspaces-edit-save" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-edit-save">
|
||||
</button>
|
||||
<button id="PanelUI-zen-workspaces-edit-cancel" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-edit-cancel">
|
||||
</button>
|
||||
</html:moz-button-group>
|
||||
</panelview>
|
||||
<panelview id="PanelUI-zen-workspaces-icon-picker" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true">
|
||||
<vbox id="PanelUI-zen-workspaces-icon-picker-wrapper">
|
||||
<html:div id="PanelUI-zen-workspaces-icon-search-bar">
|
||||
<html:input autofocus="true" type="text" id="PanelUI-zen-workspaces-icon-search-input"/>
|
||||
</html:div>
|
||||
</vbox>
|
||||
</panelview>
|
||||
</panelmultiview>
|
||||
</panel>
|
||||
|
||||
<menupopup id="zenWorkspaceActionsMenu">
|
||||
<menuitem id="context_zenOpenWorkspace" data-l10n-id="zen-workspaces-panel-context-open"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenEditWorkspace" data-l10n-id="zen-workspaces-panel-context-edit"/>
|
||||
<menupopup id="zenWorkspaceMoreActions">
|
||||
<menuitem id="context_zenEditWorkspace" data-l10n-id="zen-workspaces-panel-change-name" command="cmd_zenChangeWorkspaceName"/>
|
||||
<menuitem class="zenToolbarThemePicker"
|
||||
data-l10n-id="zen-workspaces-change-gradient"
|
||||
command="cmd_zenOpenZenThemePicker"/>
|
||||
<menu id="context_zenWorkspacesOpenInContainerTab"
|
||||
data-l10n-id="zen-workspaces-panel-context-open-in-container-tab"
|
||||
selection-type="single"
|
||||
|
@ -127,5 +71,5 @@
|
|||
<menupopup />
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete"/>
|
||||
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete" command="cmd_zenCtxDeleteWorkspace"/>
|
||||
</menupopup>
|
||||
|
|
|
@ -9,5 +9,3 @@
|
|||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaces.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspacesSync.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenActorsManager.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenSessionStore.mjs"></script>
|
||||
|
||||
|
|
|
@ -9,5 +9,6 @@
|
|||
context="toolbar-context-menu"
|
||||
mode="icons">
|
||||
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-expand-sidebar-button" command="cmd_zenToggleSidebar" data-l10n-id="sidebar-zen-expand"></toolbarbutton>
|
||||
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-open-library" command="cmd_zenToggleLibrary"></toolbarbutton>
|
||||
<zen-workspace-icons id="zen-workspaces-button" overflows="false" removable="false"></zen-workspace-icons>
|
||||
</toolbar>
|
||||
|
|
|
@ -14,37 +14,30 @@ var gZenMarketplaceManager = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!window.gZenMods) {
|
||||
window.gZenMods = ZenMultiWindowFeature.currentBrowser.gZenMods;
|
||||
}
|
||||
|
||||
header.appendChild(this._initDisableAll());
|
||||
|
||||
this._initImportExport();
|
||||
|
||||
this.__hasInitializedEvents = true;
|
||||
|
||||
await this._buildModsList();
|
||||
await this._buildThemesList();
|
||||
|
||||
Services.prefs.addObserver(gZenMods.updatePref, this);
|
||||
Services.prefs.addObserver(this.updatePref, this);
|
||||
|
||||
const checkForUpdateClick = (event) => {
|
||||
if (event.target === checkForUpdates) {
|
||||
event.preventDefault();
|
||||
|
||||
this._checkForThemeUpdates(event);
|
||||
}
|
||||
};
|
||||
|
||||
checkForUpdates.addEventListener('click', checkForUpdateClick);
|
||||
|
||||
document.addEventListener('ZenModsMarketplace:CheckForUpdatesFinished', (event) => {
|
||||
document.addEventListener('ZenThemeMarketplace:CheckForUpdatesFinished', (event) => {
|
||||
checkForUpdates.disabled = false;
|
||||
|
||||
const updates = event.detail.updates;
|
||||
const success = document.getElementById('zenThemeMarketplaceUpdatesSuccess');
|
||||
const error = document.getElementById('zenThemeMarketplaceUpdatesFailure');
|
||||
|
||||
if (updates) {
|
||||
success.hidden = false;
|
||||
error.hidden = true;
|
||||
|
@ -55,16 +48,13 @@ var gZenMarketplaceManager = {
|
|||
});
|
||||
|
||||
window.addEventListener('unload', () => {
|
||||
Services.prefs.removeObserver(gZenMods.updatePref, this);
|
||||
Services.prefs.removeObserver(this.updatePref, this);
|
||||
this.__hasInitializedEvents = false;
|
||||
|
||||
document.removeEventListener('ZenModsMarketplace:CheckForUpdatesFinished', this);
|
||||
document.removeEventListener('ZenCheckForModUpdates', this);
|
||||
|
||||
document.removeEventListener('ZenThemeMarketplace:CheckForUpdatesFinished', this);
|
||||
document.removeEventListener('ZenCheckForThemeUpdates', this);
|
||||
checkForUpdates.removeEventListener('click', checkForUpdateClick);
|
||||
|
||||
this.modsList.innerHTML = '';
|
||||
this._doNotRebuildModsList = false;
|
||||
this.themesList.innerHTML = '';
|
||||
this._doNotRebuildThemesList = false;
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -73,32 +63,36 @@ var gZenMarketplaceManager = {
|
|||
const exportButton = document.getElementById('zenThemeMarketplaceExport');
|
||||
|
||||
if (importButton) {
|
||||
importButton.addEventListener('click', this._importThemes.bind(this));
|
||||
importButton.addEventListener('click', async () => {
|
||||
await this._importThemes();
|
||||
});
|
||||
}
|
||||
|
||||
if (exportButton) {
|
||||
exportButton.addEventListener('click', this._exportThemes.bind(this));
|
||||
exportButton.addEventListener('click', async () => {
|
||||
await this._exportThemes();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_initDisableAll() {
|
||||
const areModsDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
|
||||
const browser = ZenMultiWindowFeature.currentBrowser;
|
||||
const areThemesDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
|
||||
const browser = ZenThemesCommon.currentBrowser;
|
||||
const mozToggle = document.createElement('moz-toggle');
|
||||
|
||||
mozToggle.className =
|
||||
'zenThemeMarketplaceItemPreferenceToggle zenThemeMarketplaceDisableAllToggle';
|
||||
mozToggle.pressed = !areModsDisabled;
|
||||
mozToggle.pressed = !areThemesDisabled;
|
||||
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
`zen-theme-disable-all-${!areModsDisabled ? 'enabled' : 'disabled'}`
|
||||
`zen-theme-disable-all-${!areThemesDisabled ? 'enabled' : 'disabled'}`
|
||||
);
|
||||
|
||||
mozToggle.addEventListener('toggle', async (event) => {
|
||||
const { pressed = false } = event.target || {};
|
||||
|
||||
this.modsList.style.display = pressed ? '' : 'none';
|
||||
this.themesList.style.display = pressed ? '' : 'none';
|
||||
Services.prefs.setBoolPref('zen.themes.disable-all', !pressed);
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
|
@ -106,65 +100,90 @@ var gZenMarketplaceManager = {
|
|||
);
|
||||
});
|
||||
|
||||
if (areModsDisabled) {
|
||||
this.modsList.style.display = 'none';
|
||||
if (areThemesDisabled) {
|
||||
this.themesList.style.display = 'none';
|
||||
}
|
||||
|
||||
return mozToggle;
|
||||
},
|
||||
|
||||
async observe() {
|
||||
await this._buildModsList();
|
||||
await this._buildThemesList();
|
||||
},
|
||||
|
||||
_checkForThemeUpdates(event) {
|
||||
// Send a message to the child to check for theme updates.
|
||||
event.target.disabled = true;
|
||||
// send an event that will be listened by the child process.
|
||||
document.dispatchEvent(new CustomEvent('ZenCheckForModUpdates'));
|
||||
document.dispatchEvent(new CustomEvent('ZenCheckForThemeUpdates'));
|
||||
},
|
||||
|
||||
get modsList() {
|
||||
if (!this._modsList) {
|
||||
this._modsList = document.getElementById('zenThemeMarketplaceList');
|
||||
get updatePref() {
|
||||
return 'zen.themes.updated-value-observer';
|
||||
},
|
||||
|
||||
triggerThemeUpdate() {
|
||||
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
|
||||
},
|
||||
|
||||
get themesList() {
|
||||
if (!this._themesList) {
|
||||
this._themesList = document.getElementById('zenThemeMarketplaceList');
|
||||
}
|
||||
return this._modsList;
|
||||
return this._themesList;
|
||||
},
|
||||
|
||||
async removeMod(modId) {
|
||||
await gZenMods.removeMod(modId);
|
||||
async removeTheme(themeId) {
|
||||
const themePath = ZenThemesCommon.getThemeFolder(themeId);
|
||||
|
||||
gZenMods.triggerModsUpdate();
|
||||
console.info(`[ZenThemeMarketplaceParent:settings]: Removing theme ${themePath}`);
|
||||
|
||||
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
|
||||
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
delete themes[themeId];
|
||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
||||
|
||||
this.triggerThemeUpdate();
|
||||
},
|
||||
|
||||
async disableMod(modId) {
|
||||
await gZenMods.disableMod(modId);
|
||||
async disableTheme(themeId) {
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const theme = themes[themeId];
|
||||
|
||||
this._doNotRebuildModsList = true;
|
||||
gZenMods.triggerModsUpdate();
|
||||
console.log(`[ZenThemeMarketplaceParent:settings]: Disabling theme ${theme.name}`);
|
||||
|
||||
theme.enabled = false;
|
||||
|
||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
||||
this._doNotRebuildThemesList = true;
|
||||
this.triggerThemeUpdate();
|
||||
},
|
||||
|
||||
async enableMod(modId) {
|
||||
await gZenMods.enableMod(modId);
|
||||
async enableTheme(themeId) {
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const theme = themes[themeId];
|
||||
|
||||
this._doNotRebuildModsList = true;
|
||||
gZenMods.triggerModsUpdate();
|
||||
console.log(`[ZenThemeMarketplaceParent:settings]: Enabling theme ${theme.name}`);
|
||||
|
||||
theme.enabled = true;
|
||||
|
||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
||||
this._doNotRebuildThemesList = true;
|
||||
this.triggerThemeUpdate();
|
||||
},
|
||||
|
||||
_triggerBuildUpdateWithoutRebuild() {
|
||||
this._doNotRebuildModsList = true;
|
||||
gZenMods.triggerModsUpdate();
|
||||
this._doNotRebuildThemesList = true;
|
||||
this.triggerThemeUpdate();
|
||||
},
|
||||
|
||||
async _importThemes() {
|
||||
const errorBox = document.getElementById('zenThemeMarketplaceImportFailure');
|
||||
const successBox = document.getElementById('zenThemeMarketplaceImportSuccess');
|
||||
|
||||
successBox.hidden = true;
|
||||
errorBox.hidden = true;
|
||||
|
||||
const input = document.createElement('input');
|
||||
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
input.style.display = 'none';
|
||||
|
@ -172,52 +191,37 @@ var gZenMarketplaceManager = {
|
|||
input.setAttribute('accept', '.json');
|
||||
|
||||
let timeout;
|
||||
|
||||
const filePromise = new Promise((resolve) => {
|
||||
input.addEventListener('change', (event) => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
if (timeout) clearTimeout(timeout);
|
||||
const file = event.target.files[0];
|
||||
resolve(file);
|
||||
});
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
console.warn('[ZenSettings:ZenMods]: Import timeout reached, aborting.');
|
||||
console.warn('[ZenThemeMarketplaceParent:settings]: Import timeout reached, aborting.');
|
||||
resolve(null);
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
input.addEventListener('cancel', () => {
|
||||
console.warn('[ZenSettings:ZenMods]: Import cancelled by user.');
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
|
||||
input.click();
|
||||
|
||||
try {
|
||||
const file = await filePromise;
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = await file.text();
|
||||
|
||||
const mods = JSON.parse(content);
|
||||
|
||||
for (const mod of Object.values(mods)) {
|
||||
mod.modId = mod.id;
|
||||
await window.ZenInstallMod(mod);
|
||||
const themes = JSON.parse(content);
|
||||
for (const theme of Object.values(themes)) {
|
||||
theme.themeId = theme.id;
|
||||
window.ZenInstallTheme(theme);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ZenSettings:ZenMods]: Error while importing mods:', error);
|
||||
console.error('[ZenThemeMarketplaceParent:settings]: Error while importing themes:', error);
|
||||
errorBox.hidden = false;
|
||||
}
|
||||
|
||||
if (input) {
|
||||
input.remove();
|
||||
} finally {
|
||||
if (input) input.remove();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -228,54 +232,51 @@ var gZenMarketplaceManager = {
|
|||
successBox.hidden = true;
|
||||
errorBox.hidden = true;
|
||||
|
||||
let temporalAnchor, temporalUrl;
|
||||
let a, url;
|
||||
try {
|
||||
const mods = await gZenMods.getMods();
|
||||
const modsJson = JSON.stringify(mods, null, 2);
|
||||
const blob = new Blob([modsJson], { type: 'application/json' });
|
||||
|
||||
temporalUrl = URL.createObjectURL(blob);
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const themesJson = JSON.stringify(themes, null, 2);
|
||||
const blob = new Blob([themesJson], { type: 'application/json' });
|
||||
url = URL.createObjectURL(blob);
|
||||
// Creating a link to download the JSON file
|
||||
temporalAnchor = document.createElement('a');
|
||||
temporalAnchor.href = temporalUrl;
|
||||
temporalAnchor.download = 'zen-mods-export.json';
|
||||
|
||||
document.body.appendChild(temporalAnchor);
|
||||
temporalAnchor.click();
|
||||
temporalAnchor.remove();
|
||||
a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'zen-themes-export.json';
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
successBox.hidden = false;
|
||||
} catch (error) {
|
||||
console.error('[ZenSettings:ZenMods]: Error while exporting mods:', error);
|
||||
console.error('[ZenThemeMarketplaceParent:settings]: Error while exporting themes:', error);
|
||||
errorBox.hidden = false;
|
||||
} finally {
|
||||
if (a) {
|
||||
a.remove();
|
||||
}
|
||||
|
||||
if (temporalAnchor) {
|
||||
temporalAnchor.remove();
|
||||
if (url) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
if (temporalUrl) {
|
||||
URL.revokeObjectURL(temporalUrl);
|
||||
}
|
||||
},
|
||||
|
||||
async _buildModsList() {
|
||||
if (!this.modsList) {
|
||||
async _buildThemesList() {
|
||||
if (!this.themesList) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._doNotRebuildModsList) {
|
||||
this._doNotRebuildModsList = false;
|
||||
if (this._doNotRebuildThemesList) {
|
||||
this._doNotRebuildThemesList = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const mods = await gZenMods.getMods();
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const browser = ZenMultiWindowFeature.currentBrowser;
|
||||
const modList = document.createElement('div');
|
||||
const themeList = document.createElement('div');
|
||||
|
||||
for (const mod of Object.values(mods).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
const sanitizedName = gZenMods.sanitizeModName(mod.name);
|
||||
const isModEnabled = mod.enabled === undefined || mod.enabled;
|
||||
for (const theme of Object.values(themes).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||
const isThemeEnabled = theme.enabled === undefined || theme.enabled;
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="zenThemeMarketplaceItem">
|
||||
<vbox class="zenThemeMarketplaceItemContent">
|
||||
|
@ -285,14 +286,14 @@ var gZenMarketplaceManager = {
|
|||
<description class="description-deemphasized zenThemeMarketplaceItemDescription"></description>
|
||||
</vbox>
|
||||
<hbox class="zenThemeMarketplaceItemActions">
|
||||
${mod.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
|
||||
${mod.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-mod-id="${mod.id}"></button>` : ''}
|
||||
<button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-mod-id="${mod.id}"></button>
|
||||
${theme.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
|
||||
${theme.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-theme-id="${theme.id}"></button>` : ''}
|
||||
<button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-theme-id="${theme.id}"></button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
`);
|
||||
|
||||
const modName = `${mod.name} (v${mod.version ?? '1.0.0'})`;
|
||||
const themeName = `${theme.name} (v${theme.version || '1.0.0'})`;
|
||||
|
||||
const base = fragment.querySelector('.zenThemeMarketplaceItem');
|
||||
const baseHeader = fragment.querySelector('#zenThemeMarketplaceItemContentHeader');
|
||||
|
@ -307,7 +308,7 @@ var gZenMarketplaceManager = {
|
|||
|
||||
mainDialogDiv.className = 'zenThemeMarketplaceItemPreferenceDialog';
|
||||
headerDiv.className = 'zenThemeMarketplaceItemPreferenceDialogTopBar';
|
||||
headerTitle.textContent = modName;
|
||||
headerTitle.textContent = themeName;
|
||||
browser.document.l10n.setAttributes(headerTitle, 'zen-theme-marketplace-theme-header-title', {
|
||||
name: sanitizedName,
|
||||
});
|
||||
|
@ -318,10 +319,10 @@ var gZenMarketplaceManager = {
|
|||
contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent';
|
||||
mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle';
|
||||
|
||||
mozToggle.pressed = isModEnabled;
|
||||
mozToggle.pressed = isThemeEnabled;
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
`zen-theme-marketplace-toggle-${isModEnabled ? 'enabled' : 'disabled'}-button`
|
||||
`zen-theme-marketplace-toggle-${isThemeEnabled ? 'enabled' : 'disabled'}-button`
|
||||
);
|
||||
|
||||
baseHeader.appendChild(mozToggle);
|
||||
|
@ -339,34 +340,34 @@ var gZenMarketplaceManager = {
|
|||
});
|
||||
|
||||
mozToggle.addEventListener('toggle', async (event) => {
|
||||
const modId = event.target
|
||||
const themeId = event.target
|
||||
.closest('.zenThemeMarketplaceItem')
|
||||
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
||||
.getAttribute('zen-mod-id');
|
||||
.getAttribute('zen-theme-id');
|
||||
event.target.setAttribute('disabled', true);
|
||||
|
||||
if (!event.target.hasAttribute('pressed')) {
|
||||
await this.disableMod(modId);
|
||||
await this.disableTheme(themeId);
|
||||
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
'zen-theme-marketplace-toggle-disabled-button'
|
||||
);
|
||||
|
||||
if (mod.preferences) {
|
||||
if (theme.preferences) {
|
||||
document
|
||||
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
||||
.setAttribute('hidden', true);
|
||||
}
|
||||
} else {
|
||||
await this.enableMod(modId);
|
||||
await this.enableTheme(themeId);
|
||||
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
'zen-theme-marketplace-toggle-enabled-button'
|
||||
);
|
||||
|
||||
if (mod.preferences) {
|
||||
if (theme.preferences) {
|
||||
document
|
||||
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
||||
.removeAttribute('hidden');
|
||||
|
@ -378,8 +379,8 @@ var gZenMarketplaceManager = {
|
|||
}, 400);
|
||||
});
|
||||
|
||||
fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = modName;
|
||||
fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = mod.description;
|
||||
fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = themeName;
|
||||
fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = theme.description;
|
||||
fragment
|
||||
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
||||
.addEventListener('click', async (event) => {
|
||||
|
@ -391,34 +392,34 @@ var gZenMarketplaceManager = {
|
|||
return;
|
||||
}
|
||||
|
||||
await this.removeMod(event.target.getAttribute('zen-mod-id'));
|
||||
await this.removeTheme(event.target.getAttribute('zen-theme-id'));
|
||||
});
|
||||
|
||||
if (mod.homepage) {
|
||||
if (theme.homepage) {
|
||||
const homepageButton = fragment.querySelector('.zenThemeMarketplaceItemHomepageButton');
|
||||
homepageButton.addEventListener('click', () => {
|
||||
// open the homepage url in a new tab
|
||||
const url = mod.homepage;
|
||||
const url = theme.homepage;
|
||||
|
||||
window.open(url, '_blank');
|
||||
});
|
||||
}
|
||||
|
||||
if (mod.preferences) {
|
||||
if (theme.preferences) {
|
||||
fragment
|
||||
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
||||
.addEventListener('click', () => {
|
||||
dialog.showModal();
|
||||
});
|
||||
|
||||
if (isModEnabled) {
|
||||
if (isThemeEnabled) {
|
||||
fragment
|
||||
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
||||
.removeAttribute('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
const preferences = await gZenMods.getModPreferences(mod);
|
||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
||||
|
||||
if (preferences.length > 0) {
|
||||
const preferencesWrapper = document.createXULElement('vbox');
|
||||
|
@ -470,7 +471,7 @@ var gZenMarketplaceManager = {
|
|||
|
||||
if (!['string', 'number'].includes(valueType)) {
|
||||
console.log(
|
||||
`[ZenSettings:ZenMods]: Warning, invalid data type received (${valueType}), skipping.`
|
||||
`[ZenThemeMarketplaceParent:settings]: Warning, invalid data type received (${valueType}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -582,7 +583,7 @@ var gZenMarketplaceManager = {
|
|||
|
||||
input.addEventListener(
|
||||
'change',
|
||||
gZenMods.debounce((event) => {
|
||||
ZenThemesCommon.debounce((event) => {
|
||||
const value = event.target.value;
|
||||
|
||||
Services.prefs.setStringPref(property, value);
|
||||
|
@ -616,18 +617,18 @@ var gZenMarketplaceManager = {
|
|||
|
||||
default:
|
||||
console.log(
|
||||
`[ZenSettings:ZenMods]: Warning, unknown preference type received (${type}), skipping.`
|
||||
`[ZenThemeMarketplaceParent:settings]: Warning, unknown preference type received (${type}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
contentDiv.appendChild(preferencesWrapper);
|
||||
}
|
||||
modList.appendChild(fragment);
|
||||
themeList.appendChild(fragment);
|
||||
}
|
||||
|
||||
this.modsList.replaceChildren(...modList.children);
|
||||
modList.remove();
|
||||
this.themesList.replaceChildren(...themeList.children);
|
||||
themeList.remove();
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -635,19 +636,6 @@ const kZenExtendedSidebar = 'zen.view.sidebar-expanded';
|
|||
const kZenSingleToolbar = 'zen.view.use-single-toolbar';
|
||||
|
||||
var gZenLooksAndFeel = {
|
||||
kZenColors: [
|
||||
'#aac7ff',
|
||||
'#74d7cb',
|
||||
'#a0d490',
|
||||
'#dec663',
|
||||
'#ffb787',
|
||||
'#dec1b1',
|
||||
'#ffb1c0',
|
||||
'#ddbfc3',
|
||||
'#f6b0ea',
|
||||
'#d4bbff',
|
||||
],
|
||||
|
||||
init() {
|
||||
if (this.__hasInitialized) return;
|
||||
this.__hasInitialized = true;
|
||||
|
@ -749,8 +737,7 @@ var gZenLooksAndFeel = {
|
|||
_initializeColorPicker(accentColor) {
|
||||
let elem = document.getElementById('zenLooksAndFeelColorOptions');
|
||||
elem.innerHTML = '';
|
||||
|
||||
for (let color of this.kZenColors) {
|
||||
for (let color of ZenThemesCommon.kZenColors) {
|
||||
let colorElemParen = document.createElement('div');
|
||||
let colorElem = document.createElement('div');
|
||||
colorElemParen.classList.add('zenLooksAndFeelColorOptionParen');
|
||||
|
@ -774,7 +761,7 @@ var gZenLooksAndFeel = {
|
|||
},
|
||||
|
||||
_getInitialAccentColor() {
|
||||
return Services.prefs.getStringPref('zen.theme.accent-color', this.kZenColors[0]);
|
||||
return Services.prefs.getStringPref('zen.theme.accent-color', ZenThemesCommon.kZenColors[0]);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1233,7 +1220,7 @@ Preferences.addAll([
|
|||
default: false,
|
||||
},
|
||||
{
|
||||
id: 'zen.mods.auto-update',
|
||||
id: 'browser.tabs.unloadOnLowMemory',
|
||||
type: 'bool',
|
||||
default: true,
|
||||
},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script src="chrome://browser/content/zen-components/ZenCommonUtils.mjs" defer=""/>
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs" defer=""/>
|
||||
<html:template id="template-paneZenMarketplace">
|
||||
<hbox id="ZenMarketplaceCategory"
|
||||
class="subcategory"
|
||||
|
@ -20,10 +21,6 @@
|
|||
<button id="zenThemeMarketplaceCheckForUpdates" data-l10n-id="zen-theme-marketplace-check-for-updates-button" />
|
||||
</hbox>
|
||||
|
||||
<checkbox id="zenWorkspacesForceContainerTabsToWorkspace"
|
||||
data-l10n-id="zen-themes-auto-update"
|
||||
preference="zen.mods.auto-update"/>
|
||||
|
||||
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-success" hidden="true" id="zenThemeMarketplaceUpdatesSuccess" />
|
||||
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-failure" hidden="true" id="zenThemeMarketplaceUpdatesFailure" />
|
||||
|
||||
|
|
|
@ -31,6 +31,15 @@
|
|||
<html:h1 data-l10n-id="pane-zen-tabs-unloader-title"/>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="zenTabsUnloadGroup" data-category="paneZenTabManagement" hidden="true" class="highlighting-group">
|
||||
<label><html:h2 data-l10n-id="zen-tabs-unloader-header"/></label>
|
||||
<description class="description-deemphasized" data-l10n-id="zen-tabs-unloader-description" />
|
||||
|
||||
<checkbox id="zenTabsUnloadActivate"
|
||||
data-l10n-id="zen-tabs-unloader-enabled"
|
||||
preference="browser.tabs.unloadOnLowMemory"/>
|
||||
</groupbox>
|
||||
|
||||
<hbox id="zenPinnedTabsManagerCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
diff --git a/browser/components/search/SearchUIUtils.sys.mjs b/browser/components/search/SearchUIUtils.sys.mjs
|
||||
index ecebaad93acfc9eb7dfd9d9f56fec2e1a4abe392..8bb1348b3258dbc518d23ec39181a81f87fc8c1e 100644
|
||||
--- a/browser/components/search/SearchUIUtils.sys.mjs
|
||||
+++ b/browser/components/search/SearchUIUtils.sys.mjs
|
||||
@@ -403,7 +403,7 @@ export var SearchUIUtils = {
|
||||
triggeringSearchEngine: engine.name,
|
||||
},
|
||||
});
|
||||
-
|
||||
+ window.gZenGlanceManager?.onSearchSelectCommand(where);
|
||||
return { engine, url: submission.uri };
|
||||
},
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d634dadd4d 100644
|
||||
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d81447514e2c 100644
|
||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
@@ -2088,7 +2088,6 @@ var SessionStoreInternal = {
|
||||
|
@ -31,19 +31,17 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
return;
|
||||
}
|
||||
|
||||
@@ -3925,6 +3922,11 @@ var SessionStoreInternal = {
|
||||
@@ -3925,6 +3922,9 @@ var SessionStoreInternal = {
|
||||
Math.min(tabState.index, tabState.entries.length)
|
||||
);
|
||||
tabState.pinned = false;
|
||||
+ tabState.zenEssential = false;
|
||||
+ tabState.zenPinnedId = null;
|
||||
+ tabState.zenIsGlance = false;
|
||||
+ tabState.zenGlanceId = null;
|
||||
+ tabState.zenHasStaticLabel = false;
|
||||
|
||||
if (inBackground === false) {
|
||||
aWindow.gBrowser.selectedTab = newTab;
|
||||
@@ -5239,7 +5241,7 @@ var SessionStoreInternal = {
|
||||
@@ -5239,7 +5239,7 @@ var SessionStoreInternal = {
|
||||
}
|
||||
|
||||
let workspaceID = aWindow.getWorkspaceID();
|
||||
|
@ -52,7 +50,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
winData.workspaceID = workspaceID;
|
||||
}
|
||||
},
|
||||
@@ -5430,14 +5432,15 @@ var SessionStoreInternal = {
|
||||
@@ -5430,14 +5430,15 @@ var SessionStoreInternal = {
|
||||
}
|
||||
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
|
@ -70,7 +68,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
continue;
|
||||
}
|
||||
let tabData = lazy.TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
|
||||
@@ -5456,8 +5459,8 @@ var SessionStoreInternal = {
|
||||
@@ -5456,8 +5457,8 @@ var SessionStoreInternal = {
|
||||
// We don't store the Firefox View tab in Session Store, so if it was the last selected "tab" when
|
||||
// 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).
|
||||
|
@ -81,7 +79,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
winData.title = tabbrowser.tabs[0].label;
|
||||
}
|
||||
winData.selected = selectedIndex;
|
||||
@@ -5569,8 +5572,8 @@ var SessionStoreInternal = {
|
||||
@@ -5569,8 +5570,8 @@ var SessionStoreInternal = {
|
||||
// selectTab represents.
|
||||
let selectTab = 0;
|
||||
if (overwriteTabs) {
|
||||
|
@ -92,7 +90,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
selectTab = Math.min(selectTab, winData.tabs.length);
|
||||
}
|
||||
|
||||
@@ -5613,6 +5616,7 @@ var SessionStoreInternal = {
|
||||
@@ -5613,6 +5614,7 @@ var SessionStoreInternal = {
|
||||
winData.tabs,
|
||||
winData.groups ?? []
|
||||
);
|
||||
|
@ -100,13 +98,12 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
this._log.debug(
|
||||
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
|
||||
);
|
||||
@@ -6162,6 +6166,22 @@ var SessionStoreInternal = {
|
||||
@@ -6162,8 +6164,23 @@ var SessionStoreInternal = {
|
||||
|
||||
// Most of tabData has been restored, now continue with restoring
|
||||
// attributes that may trigger external events.
|
||||
+ if (tabData.zenEssential) {
|
||||
+ tab.setAttribute("zen-essential", "true");
|
||||
+ tabData.pinned = true; // Essential tabs are always pinned.
|
||||
+ }
|
||||
+ if (tabData.zenIsEmpty) {
|
||||
+ tab.setAttribute("zen-empty-tab", "true");
|
||||
|
@ -121,5 +118,8 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d6
|
|||
+ tab.setAttribute("zenDefaultUserContextId", true);
|
||||
+ }
|
||||
|
||||
if (tabData.pinned) {
|
||||
- if (tabData.pinned) {
|
||||
+ if (tabData.pinned || tabData.zenEssential) {
|
||||
tabbrowser.pinTab(tab);
|
||||
} else {
|
||||
tabbrowser.unpinTab(tab);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
||||
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..76f4cf5aef30cb580ef0295fe6928b5a6a362f4b 100644
|
||||
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..254849e13f7566029dc780c45e376e0f0d427cb5 100644
|
||||
--- a/browser/components/sessionstore/TabState.sys.mjs
|
||||
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
||||
@@ -84,6 +84,18 @@ class _TabState {
|
||||
@@ -84,6 +84,16 @@ class _TabState {
|
||||
tabData.groupId = tab.group.id;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,6 @@ index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..76f4cf5aef30cb580ef0295fe6928b5a
|
|||
+ tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon");
|
||||
+ tabData.zenIsEmpty = tab.hasAttribute("zen-empty-tab");
|
||||
+ tabData.zenHasStaticLabel = tab.hasAttribute("zen-has-static-label");
|
||||
+ tabData.zenGlanceId = tab.getAttribute("glance-id");
|
||||
+ tabData.zenIsGlance = tab.hasAttribute("zen-glance-tab");
|
||||
+
|
||||
tabData.searchMode = tab.ownerGlobal.gURLBar.getSearchMode(browser, true);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b598a513c2 100644
|
||||
index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b032cf200f1 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -413,11 +413,41 @@
|
||||
|
@ -292,17 +292,37 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
|
||||
let url = "about:blank";
|
||||
if (tabData.entries?.length) {
|
||||
@@ -3598,7 +3675,8 @@
|
||||
@@ -3598,7 +3675,29 @@
|
||||
skipLoad: true,
|
||||
preferredRemoteType,
|
||||
});
|
||||
-
|
||||
+ tab._originalUrl = url;
|
||||
+ gZenSessionStore.restoreInitialTabData(tab, tabData);
|
||||
|
||||
+ if (tabData.zenWorkspace) {
|
||||
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
|
||||
+ }
|
||||
+ if (tabData.zenPinnedId) {
|
||||
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
|
||||
+ }
|
||||
+ if (tabData.zenIsEmpty) {
|
||||
+ tab.setAttribute("zen-empty-tab", "true");
|
||||
+ }
|
||||
+ if (tabData.zenHasStaticLabel) {
|
||||
+ tab.setAttribute("zen-has-static-label", "true");
|
||||
+ }
|
||||
+ if (tabData.zenEssential) {
|
||||
+ tab.setAttribute("zen-essential", "true");
|
||||
+ }
|
||||
+ if (tabData.zenDefaultUserContextId) {
|
||||
+ tab.setAttribute("zenDefaultUserContextId", "true");
|
||||
+ }
|
||||
+ if (tabData.zenPinnedEntry) {
|
||||
+ tab.setAttribute("zen-pinned-entry", tabData.zenPinnedEntry);
|
||||
+ }
|
||||
if (select) {
|
||||
tabToSelect = tab;
|
||||
}
|
||||
@@ -3622,7 +3700,8 @@
|
||||
@@ -3622,7 +3721,8 @@
|
||||
// needs calling:
|
||||
shouldUpdateForPinnedTabs = true;
|
||||
}
|
||||
|
@ -312,7 +332,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
let { groupId } = tabData;
|
||||
const tabGroup = tabGroupWorkingData.get(groupId);
|
||||
// if a tab refers to a tab group we don't know, skip any group
|
||||
@@ -3636,7 +3715,10 @@
|
||||
@@ -3636,7 +3736,10 @@
|
||||
tabGroup.stateData.id,
|
||||
tabGroup.stateData.color,
|
||||
tabGroup.stateData.collapsed,
|
||||
|
@ -324,7 +344,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
);
|
||||
tabsFragment.appendChild(tabGroup.node);
|
||||
}
|
||||
@@ -3684,8 +3766,16 @@
|
||||
@@ -3684,8 +3787,16 @@
|
||||
// to remove the old selected tab.
|
||||
if (tabToSelect) {
|
||||
let leftoverTab = this.selectedTab;
|
||||
|
@ -343,7 +363,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
}
|
||||
|
||||
if (tabs.length > 1 || !tabs[0].selected) {
|
||||
@@ -3881,7 +3971,7 @@
|
||||
@@ -3881,7 +3992,7 @@
|
||||
// Ensure we have an index if one was not provided.
|
||||
if (typeof elementIndex != "number" && typeof tabIndex != "number") {
|
||||
// Move the new tab after another tab if needed, to the end otherwise.
|
||||
|
@ -352,7 +372,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
if (
|
||||
!bulkOrderedOpen &&
|
||||
((openerTab &&
|
||||
@@ -3904,7 +3994,7 @@
|
||||
@@ -3904,7 +4015,7 @@
|
||||
) {
|
||||
elementIndex = Infinity;
|
||||
} else if (previousTab.visible) {
|
||||
|
@ -361,7 +381,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
} else if (previousTab == FirefoxViewHandler.tab) {
|
||||
elementIndex = 0;
|
||||
}
|
||||
@@ -3932,14 +4022,14 @@
|
||||
@@ -3932,10 +4043,10 @@
|
||||
}
|
||||
// Ensure index is within bounds.
|
||||
if (tab.pinned) {
|
||||
|
@ -375,12 +395,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
index = Math.min(index, allItems.length);
|
||||
}
|
||||
/** @type {MozTabbrowserTab|undefined} */
|
||||
- let itemAfter = allItems.at(index);
|
||||
+ let itemAfter = gZenGlanceManager.getTabOrGlanceParent(allItems.at(index));
|
||||
|
||||
// Prevent a flash of unstyled content by setting up the tab content
|
||||
// and inherited attributes before appending it (see Bug 1592054):
|
||||
@@ -3947,7 +4037,7 @@
|
||||
@@ -3947,7 +4058,7 @@
|
||||
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
|
||||
|
@ -389,7 +404,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
if (this.isTab(itemAfter) && itemAfter.group == tabGroup) {
|
||||
// Place at the front of, or between tabs in, the same tab group
|
||||
this.tabContainer.insertBefore(tab, itemAfter);
|
||||
@@ -4268,6 +4358,9 @@
|
||||
@@ -4268,6 +4379,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -399,7 +414,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
this.removeTabs(selectedTabs, { telemetrySource });
|
||||
}
|
||||
|
||||
@@ -4520,6 +4613,7 @@
|
||||
@@ -4520,6 +4634,7 @@
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
|
@ -407,7 +422,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||
// can be considered equivalent to closing the window.
|
||||
if (
|
||||
@@ -4604,6 +4698,7 @@
|
||||
@@ -4604,6 +4719,7 @@
|
||||
if (lastToClose) {
|
||||
this.removeTab(lastToClose, aParams);
|
||||
}
|
||||
|
@ -415,7 +430,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -4641,6 +4736,12 @@
|
||||
@@ -4641,6 +4757,12 @@
|
||||
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
||||
}
|
||||
|
||||
|
@ -428,7 +443,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate && aTab.closing) {
|
||||
@@ -4655,7 +4756,9 @@
|
||||
@@ -4655,7 +4777,9 @@
|
||||
// frame created for it (for example, by updating the visually selected
|
||||
// state).
|
||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
|
@ -439,7 +454,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -4821,7 +4924,7 @@
|
||||
@@ -4821,7 +4945,7 @@
|
||||
closeWindowWithLastTab != null
|
||||
? closeWindowWithLastTab
|
||||
: !window.toolbar.visible ||
|
||||
|
@ -448,7 +463,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
|
||||
if (closeWindow) {
|
||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||
@@ -4845,6 +4948,7 @@
|
||||
@@ -4845,6 +4969,7 @@
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
|
@ -456,7 +471,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -4885,9 +4989,7 @@
|
||||
@@ -4885,9 +5010,7 @@
|
||||
aTab._mouseleave();
|
||||
|
||||
if (newTab) {
|
||||
|
@ -467,7 +482,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
} else {
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -5016,6 +5118,8 @@
|
||||
@@ -5016,6 +5139,8 @@
|
||||
this.tabs[i]._tPos = i;
|
||||
}
|
||||
|
||||
|
@ -476,7 +491,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
if (!this._windowIsClosing) {
|
||||
if (wasPinned) {
|
||||
this.tabContainer._positionPinnedTabs();
|
||||
@@ -5230,6 +5334,7 @@
|
||||
@@ -5230,6 +5355,7 @@
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
|
@ -484,7 +499,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
|
||||
// If this tab has a successor, it should be selectable, since
|
||||
// hiding or closing a tab removes that tab as a successor.
|
||||
@@ -5242,13 +5347,13 @@
|
||||
@@ -5242,13 +5368,13 @@
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
|
@ -500,7 +515,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
);
|
||||
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
@@ -5264,7 +5369,7 @@
|
||||
@@ -5264,7 +5390,7 @@
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
|
@ -509,7 +524,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
}
|
||||
|
||||
// If no qualifying visible tab was found, see if there is a tab in
|
||||
@@ -5285,7 +5390,7 @@
|
||||
@@ -5285,7 +5411,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -518,7 +533,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
}
|
||||
|
||||
_blurTab(aTab) {
|
||||
@@ -5686,10 +5791,10 @@
|
||||
@@ -5686,10 +5812,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
}
|
||||
|
||||
|
@ -531,7 +546,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -5986,7 +6091,7 @@
|
||||
@@ -5986,7 +6112,7 @@
|
||||
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
if (this.isTab(element) && element.pinned) {
|
||||
|
@ -540,7 +555,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -6012,10 +6117,16 @@
|
||||
@@ -6012,10 +6138,16 @@
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
|
@ -559,7 +574,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||
neighbor.after(element);
|
||||
} else {
|
||||
@@ -6084,17 +6195,29 @@
|
||||
@@ -6084,17 +6216,26 @@
|
||||
targetElement = targetElement.group;
|
||||
}
|
||||
}
|
||||
|
@ -568,12 +583,8 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
+ element = element.group;
|
||||
+ }
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
- if (element.pinned && !targetElement?.pinned) {
|
||||
if (element.pinned && !targetElement?.pinned) {
|
||||
- targetElement = this.tabs[this.pinnedTabCount - 1];
|
||||
+ if (element.hasAttribute('zen-essential') && !targetElement?.hasAttribute('zen-essential')) {
|
||||
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this._numZenEssentials - 1];
|
||||
+ moveBefore = false;
|
||||
+ } else if (element.pinned && !targetElement?.pinned) {
|
||||
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this.pinnedTabCount - 1];
|
||||
moveBefore = false;
|
||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||
|
@ -593,7 +604,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
if (element.pinned && this.tabContainer.verticalMode) {
|
||||
return this.tabContainer.verticalPinnedTabsContainer;
|
||||
}
|
||||
@@ -6154,7 +6277,7 @@
|
||||
@@ -6154,7 +6295,7 @@
|
||||
if (!this.isTab(aTab)) {
|
||||
throw new Error("Can only move a tab into a tab group");
|
||||
}
|
||||
|
@ -602,7 +613,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
return;
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
@@ -6248,6 +6371,10 @@
|
||||
@@ -6248,6 +6389,10 @@
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
|
@ -613,7 +624,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -7145,7 +7272,7 @@
|
||||
@@ -7145,7 +7290,7 @@
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
break;
|
||||
}
|
||||
|
@ -622,7 +633,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
window.focus();
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
@@ -8044,6 +8171,7 @@
|
||||
@@ -8044,6 +8189,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
|
@ -630,7 +641,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
}
|
||||
@@ -9009,7 +9137,7 @@ var TabContextMenu = {
|
||||
@@ -9009,7 +9155,7 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !this.multiselected;
|
||||
|
@ -639,7 +650,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b5
|
|||
// Move Tab items
|
||||
let contextMoveTabOptions = document.getElementById(
|
||||
"context_moveTabOptions"
|
||||
@@ -9278,6 +9406,7 @@ var TabContextMenu = {
|
||||
@@ -9278,6 +9424,7 @@ var TabContextMenu = {
|
||||
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
|
||||
index 6dc774ea335b0c5dba7dcf76cdb23728faae1343..b0b9ef236c2e8517db4bcf3270596456bbefe11d 100644
|
||||
--- a/browser/components/tabbrowser/content/tabgroup.js
|
||||
+++ b/browser/components/tabbrowser/content/tabgroup.js
|
||||
@@ -301,7 +301,7 @@
|
||||
*/
|
||||
addTabs(tabs, metricsContext) {
|
||||
for (let tab of tabs) {
|
||||
- if (tab.pinned) {
|
||||
+ if (tab.pinned !== this.pinned) {
|
||||
tab.ownerGlobal.gBrowser.unpinTab(tab);
|
||||
}
|
||||
let tabToMove =
|
|
@ -1,5 +1,5 @@
|
|||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||
index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c574dfc103 100644
|
||||
index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea9596403cef 100644
|
||||
--- a/browser/components/tabbrowser/content/tabs.js
|
||||
+++ b/browser/components/tabbrowser/content/tabs.js
|
||||
@@ -83,7 +83,7 @@
|
||||
|
@ -46,15 +46,6 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
expandGroupOnDrop = true;
|
||||
}
|
||||
}
|
||||
@@ -868,7 +869,7 @@
|
||||
? event.screenY - window.screenY - tabOffset
|
||||
: event.screenY - window.screenY,
|
||||
scrollPos:
|
||||
- this.verticalMode && tab.pinned
|
||||
+ this.verticalMode && tab.pinned && false
|
||||
? this.verticalPinnedTabsContainer.scrollPosition
|
||||
: this.arrowScrollbox.scrollPosition,
|
||||
screenX: event.screenX,
|
||||
@@ -921,6 +922,10 @@
|
||||
}
|
||||
|
||||
|
@ -100,23 +91,16 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
);
|
||||
let size = this.verticalMode ? "height" : "width";
|
||||
let screenAxis = this.verticalMode ? "screenY" : "screenX";
|
||||
@@ -1135,8 +1153,14 @@
|
||||
(lastMovingTabScreen + tabSize);
|
||||
|
||||
if (this.verticalMode) {
|
||||
+ if (oldTranslateY > 0 && translateOffsetY > tabHeight / 2) {
|
||||
+ newTranslateY += tabHeight;
|
||||
+ }
|
||||
+ if (oldTranslateY < 0 && -translateOffsetY > tabHeight / 2) {
|
||||
+ newTranslateY -= tabHeight;
|
||||
+ }
|
||||
newTranslateY = Math.min(
|
||||
- Math.max(oldTranslateY, firstBound),
|
||||
+ Math.max(newTranslateY, firstBound),
|
||||
lastBound
|
||||
);
|
||||
@@ -1211,7 +1229,7 @@
|
||||
item.removeAttribute("tabdrop-samewindow");
|
||||
resolve();
|
||||
};
|
||||
- if (gReduceMotion) {
|
||||
+ if (true || gReduceMotion) {
|
||||
postTransitionCleanup();
|
||||
} else {
|
||||
@@ -1337,6 +1361,7 @@
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -1337,6 +1355,7 @@
|
||||
|
||||
let nextItem = this.ariaFocusableItems[newIndex];
|
||||
let tabGroup = isTab(nextItem) && nextItem.group;
|
||||
|
@ -124,7 +108,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
gBrowser.loadTabs(urls, {
|
||||
inBackground,
|
||||
replace,
|
||||
@@ -1369,6 +1394,17 @@
|
||||
@@ -1369,6 +1388,17 @@
|
||||
|
||||
this.finishMoveTogetherSelectedTabs(draggedTab);
|
||||
this.finishAnimateTabMove();
|
||||
|
@ -142,7 +126,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
this.#expandGroupOnDrop(draggedTab);
|
||||
|
||||
if (
|
||||
@@ -1597,7 +1633,7 @@
|
||||
@@ -1597,7 +1627,7 @@
|
||||
}
|
||||
|
||||
get newTabButton() {
|
||||
|
@ -151,7 +135,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
}
|
||||
|
||||
get verticalMode() {
|
||||
@@ -1621,29 +1657,54 @@
|
||||
@@ -1621,29 +1651,54 @@
|
||||
if (this.#allTabs) {
|
||||
return this.#allTabs;
|
||||
}
|
||||
|
@ -195,7 +179,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
+ if (glanceTab) {
|
||||
+ // insert right after the parent tab. note: it must be inserted before
|
||||
+ // the last pinned tab so it can be inserted in the correct order
|
||||
+ allTabs.splice(Math.max(i++ + 1, lastPinnedTabIdx), 0, glanceTab);
|
||||
+ allTabs.splice(Math.max(i++, lastPinnedTabIdx), 0, glanceTab);
|
||||
+ } else if (tab.classList.contains("vertical-pinned-tabs-container-separator")) {
|
||||
+ // remove the separator from the list
|
||||
+ allTabs.splice(i, 1);
|
||||
|
@ -214,7 +198,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
}
|
||||
|
||||
/**
|
||||
@@ -1698,23 +1759,18 @@
|
||||
@@ -1698,23 +1753,18 @@
|
||||
}
|
||||
|
||||
let elementIndex = 0;
|
||||
|
@ -242,7 +226,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
|
||||
visibleTabsInGroup.forEach(tab => {
|
||||
tab.elementIndex = elementIndex++;
|
||||
@@ -1724,10 +1780,7 @@
|
||||
@@ -1724,10 +1774,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +238,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
|
||||
return this.#focusableItems;
|
||||
}
|
||||
@@ -1735,6 +1788,7 @@
|
||||
@@ -1735,6 +1782,7 @@
|
||||
_invalidateCachedTabs() {
|
||||
this.#allTabs = null;
|
||||
this._invalidateCachedVisibleTabs();
|
||||
|
@ -262,7 +246,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
}
|
||||
|
||||
_invalidateCachedVisibleTabs() {
|
||||
@@ -1749,8 +1803,8 @@
|
||||
@@ -1749,8 +1797,8 @@
|
||||
#isContainerVerticalPinnedGrid(tab) {
|
||||
return (
|
||||
this.verticalMode &&
|
||||
|
@ -273,7 +257,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
!this.expandOnHover
|
||||
);
|
||||
}
|
||||
@@ -1766,7 +1820,7 @@
|
||||
@@ -1766,7 +1814,7 @@
|
||||
|
||||
if (node == null) {
|
||||
// We have a container for non-tab elements at the end of the scrollbox.
|
||||
|
@ -282,7 +266,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
}
|
||||
|
||||
node.before(tab);
|
||||
@@ -1861,7 +1915,7 @@
|
||||
@@ -1861,7 +1909,7 @@
|
||||
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
|
||||
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
|
||||
// Attach the long click popup to all of them.
|
||||
|
@ -291,7 +275,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
const newTab2 = this.newTabButton;
|
||||
const newTabVertical = document.getElementById(
|
||||
"vertical-tabs-newtab-button"
|
||||
@@ -1956,10 +2010,12 @@
|
||||
@@ -1956,10 +2004,12 @@
|
||||
|
||||
_handleTabSelect(aInstant) {
|
||||
let selectedTab = this.selectedItem;
|
||||
|
@ -304,7 +288,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
selectedTab._notselectedsinceload = false;
|
||||
}
|
||||
|
||||
@@ -2132,6 +2188,7 @@
|
||||
@@ -2132,6 +2182,7 @@
|
||||
}
|
||||
|
||||
_positionPinnedTabs() {
|
||||
|
@ -312,7 +296,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
let tabs = this.visibleTabs;
|
||||
let numPinned = gBrowser.pinnedTabCount;
|
||||
let absPositionHorizontalTabs =
|
||||
@@ -2206,7 +2263,7 @@
|
||||
@@ -2206,7 +2257,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -321,25 +305,16 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
|
||||
let directionX = screenX > dragData.animLastScreenX;
|
||||
let directionY = screenY > dragData.animLastScreenY;
|
||||
@@ -2215,6 +2272,8 @@
|
||||
@@ -2214,7 +2265,7 @@
|
||||
dragData.animLastScreenX = screenX;
|
||||
|
||||
let { width: tabWidth, height: tabHeight } =
|
||||
draggedTab.getBoundingClientRect();
|
||||
+ tabWidth += 4; // Add 4px to account for the gap
|
||||
+ tabHeight += 4;
|
||||
- draggedTab.getBoundingClientRect();
|
||||
+ (draggedTab.group?.hasAttribute("split-view-group") ? draggedTab.group : draggedTab).getBoundingClientRect();
|
||||
let shiftSizeX = tabWidth * movingTabs.length;
|
||||
let shiftSizeY = tabHeight;
|
||||
dragData.tabWidth = tabWidth;
|
||||
@@ -2244,7 +2303,7 @@
|
||||
let translateX = screenX - dragData.screenX;
|
||||
let translateY = screenY - dragData.screenY;
|
||||
translateY +=
|
||||
- this.verticalPinnedTabsContainer.scrollPosition - dragData.scrollPos;
|
||||
+ dragData.scrollPos;
|
||||
let firstBoundX = firstTabInRow.screenX - firstMovingTabScreenX;
|
||||
let firstBoundY = firstTabInRow.screenY - firstMovingTabScreenY;
|
||||
let lastBoundX =
|
||||
@@ -2389,12 +2448,16 @@
|
||||
@@ -2389,12 +2440,16 @@
|
||||
|
||||
this.#clearDragOverCreateGroupTimer();
|
||||
|
||||
|
@ -360,7 +335,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
|
||||
if (this.#rtlMode) {
|
||||
tabs.reverse();
|
||||
@@ -2408,7 +2471,7 @@
|
||||
@@ -2408,7 +2463,7 @@
|
||||
let size = this.verticalMode ? "height" : "width";
|
||||
let translateAxis = this.verticalMode ? "translateY" : "translateX";
|
||||
let scrollDirection = this.verticalMode ? "scrollTop" : "scrollLeft";
|
||||
|
@ -369,7 +344,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
let translateX = event.screenX - dragData.screenX;
|
||||
let translateY = event.screenY - dragData.screenY;
|
||||
|
||||
@@ -2422,12 +2485,21 @@
|
||||
@@ -2422,12 +2477,21 @@
|
||||
let lastTab = tabs.at(-1);
|
||||
let lastMovingTab = movingTabs.at(-1);
|
||||
let firstMovingTab = movingTabs[0];
|
||||
|
@ -392,7 +367,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
translate +=
|
||||
this.arrowScrollbox.scrollbox[scrollDirection] - dragData.scrollPos;
|
||||
} else if (isPinned && this.verticalMode) {
|
||||
@@ -2446,6 +2518,9 @@
|
||||
@@ -2446,6 +2510,9 @@
|
||||
// Shift the `.tab-group-label-container` to shift the label element.
|
||||
item = item.parentElement;
|
||||
}
|
||||
|
@ -402,7 +377,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
item.style.transform = `${translateAxis}(${translate}px)`;
|
||||
}
|
||||
|
||||
@@ -2583,6 +2658,9 @@
|
||||
@@ -2583,6 +2650,9 @@
|
||||
break;
|
||||
}
|
||||
let element = tabs[mid];
|
||||
|
@ -412,7 +387,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
let elementForSize = isTabGroupLabel(element)
|
||||
? element.parentElement
|
||||
: element;
|
||||
@@ -2605,6 +2683,10 @@
|
||||
@@ -2605,6 +2675,10 @@
|
||||
if (!dropElement) {
|
||||
dropElement = this.ariaFocusableItems[oldDropElementIndex];
|
||||
}
|
||||
|
@ -423,7 +398,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
let newDropElementIndex = dropElement
|
||||
? dropElement.elementIndex
|
||||
: oldDropElementIndex;
|
||||
@@ -2613,7 +2695,7 @@
|
||||
@@ -2613,7 +2687,7 @@
|
||||
let shouldCreateGroupOnDrop;
|
||||
let dropBefore;
|
||||
if (dropElement) {
|
||||
|
@ -432,7 +407,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
? dropElement.parentElement
|
||||
: dropElement;
|
||||
|
||||
@@ -2675,12 +2757,12 @@
|
||||
@@ -2675,12 +2749,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,16 +422,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
dropElement != draggedTab &&
|
||||
isTab(dropElement) &&
|
||||
!dropElement?.group &&
|
||||
@@ -2720,7 +2802,7 @@
|
||||
// Dropping right before the tab group.
|
||||
dropElement = dropElementGroup;
|
||||
colorCode = undefined;
|
||||
- } else if (dropElementGroup.collapsed) {
|
||||
+ } else if (dropElement?.group?.hasAttribute("split-view-group")) {
|
||||
// Dropping right after the collapsed tab group.
|
||||
dropElement = dropElementGroup;
|
||||
colorCode = undefined;
|
||||
@@ -2750,7 +2832,7 @@
|
||||
@@ -2750,7 +2824,7 @@
|
||||
// Shift background tabs to leave a gap where the dragged tab
|
||||
// would currently be dropped.
|
||||
for (let item of tabs) {
|
||||
|
@ -465,7 +431,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
continue;
|
||||
}
|
||||
|
||||
@@ -2759,6 +2841,9 @@
|
||||
@@ -2759,6 +2833,9 @@
|
||||
if (isTabGroupLabel(item)) {
|
||||
// Shift the `.tab-group-label-container` to shift the label element.
|
||||
item = item.parentElement;
|
||||
|
@ -475,7 +441,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
}
|
||||
item.style.transform = transform;
|
||||
}
|
||||
@@ -2811,8 +2896,9 @@
|
||||
@@ -2811,8 +2888,9 @@
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -487,7 +453,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
return;
|
||||
}
|
||||
|
||||
@@ -2824,6 +2910,12 @@
|
||||
@@ -2824,6 +2902,12 @@
|
||||
item = item.parentElement;
|
||||
}
|
||||
item.style.transform = "";
|
||||
|
@ -500,7 +466,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
item.removeAttribute("dragover-createGroup");
|
||||
}
|
||||
this.removeAttribute("movingtab-createGroup");
|
||||
@@ -2870,7 +2962,7 @@
|
||||
@@ -2870,7 +2954,7 @@
|
||||
let postTransitionCleanup = () => {
|
||||
movingTab._moveTogetherSelectedTabsData.animate = false;
|
||||
};
|
||||
|
@ -509,7 +475,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
postTransitionCleanup();
|
||||
} else {
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -3043,7 +3135,7 @@
|
||||
@@ -3043,7 +3127,7 @@
|
||||
}
|
||||
|
||||
_notifyBackgroundTab(aTab) {
|
||||
|
@ -518,7 +484,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c5
|
|||
return;
|
||||
}
|
||||
|
||||
@@ -3169,6 +3261,9 @@
|
||||
@@ -3169,6 +3253,9 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
diff --git a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||
index dfa91b76ad3890ceadb1b1b5d7a63b7074fbb776..6369fa1cdb242de32338bbce6debcdab2a04ca02 100644
|
||||
--- a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||
@@ -585,6 +585,7 @@ export class UrlbarValueFormatter {
|
||||
this.window.requestAnimationFrame(() => {
|
||||
if (instance == this._resizeInstance) {
|
||||
this.#ensureFormattedHostVisible();
|
||||
+ this._formatURL();
|
||||
}
|
||||
});
|
||||
}, 100);
|
|
@ -60,6 +60,26 @@
|
|||
list-style-image: url('sidebars-right.svg') !important;
|
||||
}
|
||||
|
||||
#zen-library-sidebar-workspaces {
|
||||
list-style-image: url('duplicate-tab.svg');
|
||||
}
|
||||
|
||||
#zen-library-sidebar-mods {
|
||||
list-style-image: url('edit.svg');
|
||||
}
|
||||
|
||||
#context_zenSplitTabs {
|
||||
--menu-image: url('sidebars-right.svg') !important;
|
||||
}
|
||||
|
||||
#context-zen-change-workspace-tab {
|
||||
--menu-image: url('move-tab.svg') !important;
|
||||
}
|
||||
|
||||
#context-zenSplitLink {
|
||||
--menu-image: url('split.svg') !important;
|
||||
}
|
||||
|
||||
#sidebar-button:-moz-locale-dir(ltr):not([positionend]),
|
||||
#sidebar-button:-moz-locale-dir(rtl)[positionend] {
|
||||
list-style-image: url('chrome://browser/skin/sidebars.svg') !important;
|
||||
|
@ -107,6 +127,7 @@
|
|||
|
||||
#PanelUI-menu-button,
|
||||
#appMenu-more-button2,
|
||||
.zen-workspaces-actions,
|
||||
#zen-workspace-actions-menu-icon {
|
||||
list-style-image: url('menu.svg') !important;
|
||||
}
|
||||
|
@ -121,10 +142,6 @@
|
|||
list-style-image: url('tab.svg') !important;
|
||||
}
|
||||
|
||||
#context-navigation > menuitem {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#history-panelmenu,
|
||||
.urlbarView-row[source='history']
|
||||
> .urlbarView-row-inner
|
||||
|
@ -165,6 +182,10 @@
|
|||
list-style-image: url('open.svg') !important;
|
||||
}
|
||||
|
||||
.zenToolbarThemePicker {
|
||||
--menu-image: url('edit-theme.svg') !important;
|
||||
}
|
||||
|
||||
#add-ons-button,
|
||||
#appMenu-extensions-themes-button,
|
||||
#unified-extensions-button {
|
||||
|
@ -182,16 +203,10 @@
|
|||
#appMenu-zoomEnlarge-button2,
|
||||
#PanelUI-zen-profiles-newProfile,
|
||||
#zen-sidebar-add-panel-button,
|
||||
#PanelUI-zen-workspaces-new image,
|
||||
#PanelUI-zen-gradient-generator-color-custom-add image {
|
||||
list-style-image: url('plus.svg') !important;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-reorder-mode image {
|
||||
list-style-image: url('move-tab.svg') !important;
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
#cut-button {
|
||||
list-style-image: url('edit-cut.svg') !important;
|
||||
}
|
||||
|
@ -286,12 +301,8 @@
|
|||
list-style-image: url('home.svg') !important;
|
||||
}
|
||||
|
||||
#toggle_toolbar-menubar,
|
||||
#appMenu_menu_openHelp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#library-button {
|
||||
#library-button,
|
||||
#zen-open-library {
|
||||
list-style-image: url('library.svg') !important;
|
||||
}
|
||||
|
||||
|
@ -435,11 +446,6 @@
|
|||
|
||||
#zen-glance-sidebar-split {
|
||||
list-style-image: url('split.svg');
|
||||
|
||||
&[disabled='true'] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar-box[sidebarcommand='viewTabsSidebar']
|
||||
|
@ -628,6 +634,372 @@
|
|||
list-style-image: url('manage.svg') !important;
|
||||
}
|
||||
|
||||
/* Context Menu Icons */
|
||||
|
||||
#context-video-pictureinpicture:not([checked='true']) .menu-iconic-icon {
|
||||
list-style-image: url('media-pip.svg') !important;
|
||||
}
|
||||
|
||||
#context-media-loop:not([checked='true']) .menu-iconic-icon {
|
||||
list-style-image: url('media-loop.svg') !important;
|
||||
}
|
||||
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menuitem:not(
|
||||
.menuitem-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menuitem,
|
||||
.unified-nav-current
|
||||
),
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menu:not(
|
||||
.menu-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menu,
|
||||
.unified-nav-current
|
||||
),
|
||||
#toggle_toolbar-menubar,
|
||||
#PanelUI-history toolbarbutton,
|
||||
#unified-extensions-context-menu menuitem {
|
||||
background-image: var(--menu-image) !important;
|
||||
background-size: 16px !important;
|
||||
background-position: var(--zen-contextmenu-menuitem-padding-inline) center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
-moz-context-properties: fill, fill-opacity !important;
|
||||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
@media not (-moz-platform: windows) {
|
||||
menu > .menu-iconic-text,
|
||||
menuitem > .menu-iconic-text {
|
||||
padding-inline-start: var(--zen-contextmenu-menuicon-margin-inline) !important;
|
||||
}
|
||||
}
|
||||
|
||||
#context-savepage {
|
||||
--menu-image: url('save.svg');
|
||||
}
|
||||
|
||||
#context-selectall,
|
||||
.textbox-contextmenu menuitem[cmd*='selectAll'],
|
||||
#context_selectAllTabs,
|
||||
#toolbar-context-selectAllTabs {
|
||||
--menu-image: url('edit-select-all.svg');
|
||||
}
|
||||
|
||||
#context-undo,
|
||||
.textbox-contextmenu menuitem[cmd*='undo'],
|
||||
#context_undoCloseTab,
|
||||
#toolbar-context-undoCloseTab {
|
||||
--menu-image: url('edit-undo.svg');
|
||||
}
|
||||
|
||||
#toggle_toolbar-menubar {
|
||||
--menu-image: url('menu-bar.svg');
|
||||
}
|
||||
|
||||
#context-redo,
|
||||
.textbox-contextmenu menuitem[cmd*='redo'] {
|
||||
--menu-image: url('edit-redo.svg');
|
||||
}
|
||||
|
||||
#context-copy,
|
||||
.textbox-contextmenu menuitem[cmd*='copy'],
|
||||
.textbox-contextmenu #strip-on-share,
|
||||
#placesContext_copy {
|
||||
--menu-image: url('edit-copy.svg');
|
||||
}
|
||||
|
||||
#context-paste,
|
||||
.textbox-contextmenu menuitem[cmd*='paste'],
|
||||
#placesContext_paste_group {
|
||||
--menu-image: url('edit-paste.svg');
|
||||
}
|
||||
|
||||
#context-cut,
|
||||
.textbox-contextmenu menuitem[cmd*='cut'],
|
||||
#placesContext_cut {
|
||||
--menu-image: url('edit-cut.svg');
|
||||
}
|
||||
|
||||
#context-delete,
|
||||
.customize-context-removeExtension,
|
||||
.unified-extensions-context-menu-remove-extension,
|
||||
.textbox-contextmenu menuitem[cmd*='delete'],
|
||||
menuitem[id='placesContext_deleteBookmark'],
|
||||
menuitem[id='placesContext_deleteFolder'],
|
||||
menuitem[id='placesContext_delete'],
|
||||
menuitem[id='placesContext_delete_history'],
|
||||
menuitem[id='placesContext_deleteHost'],
|
||||
#context_zenDeleteWebPanel,
|
||||
#context_zenDeleteWorkspace {
|
||||
--menu-image: url('edit-delete.svg');
|
||||
}
|
||||
|
||||
#paste-and-go {
|
||||
--menu-image: url('paste-and-go.svg');
|
||||
}
|
||||
|
||||
#context-print-selection {
|
||||
--menu-image: url('print.svg');
|
||||
}
|
||||
|
||||
#context-take-screenshot {
|
||||
--menu-image: url('screenshot.svg');
|
||||
}
|
||||
|
||||
#context-viewsource {
|
||||
--menu-image: url('source-code.svg');
|
||||
}
|
||||
|
||||
#context-inspect-a11y {
|
||||
--menu-image: url('accessibility.svg');
|
||||
}
|
||||
|
||||
#context-inspect {
|
||||
--menu-image: url('inspect.svg');
|
||||
}
|
||||
|
||||
#context-searchselect {
|
||||
--menu-image: url('search-glass.svg');
|
||||
}
|
||||
|
||||
#context-viewimage {
|
||||
--menu-image: url('image-open.svg');
|
||||
}
|
||||
|
||||
#context-viewimageinfo {
|
||||
--menu-image: url('info.svg');
|
||||
}
|
||||
|
||||
#context-saveimage,
|
||||
#context-video-saveimage {
|
||||
--menu-image: url('image-save.svg');
|
||||
}
|
||||
|
||||
#context-savevideo {
|
||||
--menu-image: url('video-save.svg');
|
||||
}
|
||||
|
||||
#context-viewvideo {
|
||||
--menu-image: url('video-open.svg');
|
||||
}
|
||||
|
||||
#context-saveaudio {
|
||||
--menu-image: url('audio-save.svg');
|
||||
}
|
||||
|
||||
#context-copyimage-contents {
|
||||
--menu-image: url('image-copy.svg');
|
||||
}
|
||||
|
||||
#context-copyimage,
|
||||
#context-copyvideourl,
|
||||
#context-copylink,
|
||||
#context-stripOnShareLink,
|
||||
#context_zenOpenNewTabWebPanel,
|
||||
#context-pdfjs-copy {
|
||||
--menu-image: url('link.svg');
|
||||
}
|
||||
|
||||
#context-openlinkincurrent {
|
||||
--menu-image: url('ext-link.svg');
|
||||
}
|
||||
|
||||
#context-viewsource,
|
||||
#context-viewframesource,
|
||||
#context-viewpartialsource-selection {
|
||||
--menu-image: url('source-code.svg');
|
||||
}
|
||||
|
||||
#context-sendimage,
|
||||
#context-sendvideo,
|
||||
#context-sendaudio {
|
||||
--menu-image: url('mail.svg');
|
||||
}
|
||||
|
||||
#context-setDesktopBackground,
|
||||
.viewCustomizeToolbar {
|
||||
--menu-image: url('customize.svg');
|
||||
}
|
||||
|
||||
#context-reloadimage,
|
||||
#context_reloadTab,
|
||||
#context_reloadSelectedTabs,
|
||||
#toolbar-context-reloadSelectedTab,
|
||||
#toolbar-context-reloadSelectedTabs,
|
||||
#context_zen-reset-pinned-tab {
|
||||
--menu-image: url('reload.svg');
|
||||
}
|
||||
|
||||
#context-sendlinktodevice,
|
||||
#context_sendTabToDevice,
|
||||
#context-sendpagetodevice {
|
||||
--menu-image: url('send-to-device.svg');
|
||||
}
|
||||
|
||||
#context-openlinkintab,
|
||||
#context-openlinkincontainertab,
|
||||
#context_zenWorkspacesOpenInContainerTab,
|
||||
#context_zenWebPanelContextInContainer,
|
||||
menuitem[id='placesContext_open:newtab'],
|
||||
menuitem[id='placesContext_openLinks:tabs'],
|
||||
menuitem[id='placesContext_openBookmarkLinks:tabs'],
|
||||
menuitem[id='placesContext_openBookmarkContainer:tabs'] {
|
||||
--menu-image: url('tab.svg');
|
||||
}
|
||||
|
||||
#context_openANewTab,
|
||||
#toolbar-context-openANewTab {
|
||||
--menu-image: url('new-tab-image.svg');
|
||||
}
|
||||
|
||||
#context-openlinkinusercontext-menu,
|
||||
menu[id='placesContext_open:newcontainertab'],
|
||||
menu[id='placesContext_openContainer:tabs'] {
|
||||
--menu-image: url('container-tab.svg');
|
||||
}
|
||||
|
||||
#context-openlink,
|
||||
menuitem[id='placesContext_open:newwindow'] {
|
||||
--menu-image: url('window.svg');
|
||||
}
|
||||
|
||||
#context-openlinkprivate,
|
||||
menuitem[id='placesContext_open:newprivatewindow'] {
|
||||
--menu-image: url('private-window.svg');
|
||||
}
|
||||
|
||||
#context-savelink {
|
||||
--menu-image: url('downloads.svg');
|
||||
}
|
||||
|
||||
#spell-add-to-dictionary {
|
||||
--menu-image: url('add-to-dictionary.svg');
|
||||
}
|
||||
|
||||
#manage-saved-logins {
|
||||
--menu-image: url('passwords.svg');
|
||||
}
|
||||
|
||||
#context-media-play,
|
||||
#context_playTab,
|
||||
#context_playSelectedTabs {
|
||||
--menu-image: url('media-play.svg');
|
||||
}
|
||||
|
||||
#context-media-pause {
|
||||
--menu-image: url('media-pause.svg');
|
||||
}
|
||||
|
||||
#context-media-mute,
|
||||
#context_toggleMuteTab,
|
||||
#context_toggleMuteSelectedTabs,
|
||||
#context_zenToggleMuteWebPanel {
|
||||
--menu-image: url('media-mute.svg');
|
||||
}
|
||||
|
||||
#context-media-unmute,
|
||||
#context_toggleMuteTab[muted],
|
||||
#context_toggleMuteSelectedTabs[muted],
|
||||
#context_zenToggleMuteWebPanel[muted] {
|
||||
--menu-image: url('media-unmute.svg');
|
||||
}
|
||||
|
||||
#context-media-playbackrate {
|
||||
--menu-image: url('media-speed.svg');
|
||||
}
|
||||
|
||||
#context-video-fullscreen {
|
||||
--menu-image: url('fullscreen.svg');
|
||||
}
|
||||
|
||||
#context-leave-dom-fullscreen,
|
||||
menuitem[contexttype='fullscreen'][label*='Exit'] {
|
||||
--menu-image: url('fullscreen-exit.svg');
|
||||
}
|
||||
|
||||
#context-media-hidecontrols,
|
||||
#context-media-showcontrols {
|
||||
--menu-image: url('permissions.svg');
|
||||
}
|
||||
|
||||
#context_pinTab,
|
||||
#context_unpinTab,
|
||||
#context_pinSelectedTabs,
|
||||
#context_unpinSelectedTabs,
|
||||
.customize-context-moveToPanel,
|
||||
#context_zen-replace-pinned-url-with-current {
|
||||
--menu-image: url('pin.svg');
|
||||
}
|
||||
|
||||
#context_zen-add-essential {
|
||||
--menu-image: url('essential-add.svg');
|
||||
}
|
||||
|
||||
#context_zen-remove-essential {
|
||||
--menu-image: url('essential-remove.svg');
|
||||
}
|
||||
|
||||
.customize-context-removeFromToolbar {
|
||||
--menu-image: url('unpin.svg');
|
||||
}
|
||||
|
||||
#zen-sidebar-web-panel-pinned[pinned='true'] {
|
||||
list-style-image: url('pin.svg') !important;
|
||||
}
|
||||
|
||||
#zen-sidebar-web-panel-pinned {
|
||||
list-style-image: url('unpin.svg') !important;
|
||||
}
|
||||
|
||||
#context_duplicateTab,
|
||||
#context_duplicateTabs {
|
||||
--menu-image: url('duplicate-tab.svg');
|
||||
}
|
||||
|
||||
#zen-context-menu-compact-mode {
|
||||
--menu-image: url('sidebar.svg');
|
||||
}
|
||||
|
||||
#context_bookmarkTab,
|
||||
#context_bookmarkSelectedTabs,
|
||||
#toggle_PersonalToolbar,
|
||||
#context-bookmarklink,
|
||||
#toolbar-context-bookmarkSelectedTab,
|
||||
#toolbar-context-bookmarkSelectedTabs {
|
||||
--menu-image: url('bookmark-hollow.svg');
|
||||
}
|
||||
|
||||
menuitem[id='placesContext_show_bookmark:info'],
|
||||
menuitem[id='placesContext_show_folder:info'],
|
||||
menuitem[id='placesContext_show:info'],
|
||||
#context_zenEditWorkspace {
|
||||
--menu-image: url('edit.svg');
|
||||
}
|
||||
|
||||
menuitem[id='placesContext_showAllBookmarks'],
|
||||
#BMB_bookmarksShowAllTop,
|
||||
#BMB_bookmarksShowAll,
|
||||
.customize-context-manageExtension,
|
||||
.unified-extensions-context-menu-manage-extension {
|
||||
--menu-image: url('manage.svg');
|
||||
}
|
||||
|
||||
#BMB_viewBookmarksSidebar {
|
||||
--menu-image: url('chrome://browser/skin/sidebars.svg');
|
||||
}
|
||||
|
||||
#BMB_searchBookmarks {
|
||||
--menu-image: url('search-page.svg');
|
||||
}
|
||||
|
||||
#appMenuRecentlyClosedTabs {
|
||||
list-style-image: url('container-tab.svg') !important;
|
||||
}
|
||||
|
@ -648,12 +1020,57 @@
|
|||
list-style-image: url('manage.svg') !important;
|
||||
}
|
||||
|
||||
menuitem[id='placesContext_new:bookmark'],
|
||||
menuitem[id='placesContext_new:folder'],
|
||||
menuitem[id='placesContext_new:separator'] {
|
||||
--menu-image: url('plus.svg');
|
||||
}
|
||||
|
||||
#context-savelinktopocket,
|
||||
#context-pocket {
|
||||
--menu-image: url('pocket-outline.svg');
|
||||
}
|
||||
|
||||
#context_moveTabOptions {
|
||||
--menu-image: url('move-tab.svg');
|
||||
}
|
||||
|
||||
.share-tab-url-item {
|
||||
--menu-image: url('share.svg');
|
||||
}
|
||||
|
||||
#context_reopenInContainer {
|
||||
--menu-image: url('container-tab.svg');
|
||||
}
|
||||
|
||||
#context_closeTab {
|
||||
--menu-image: url('close.svg');
|
||||
}
|
||||
|
||||
#context_closeTabOptions {
|
||||
--menu-image: url('close-all.svg');
|
||||
}
|
||||
|
||||
#context_unloadTab,
|
||||
#context_zenTabActions {
|
||||
--menu-image: url('close-all.svg');
|
||||
}
|
||||
|
||||
.customize-context-reportExtension,
|
||||
.unified-extensions-context-menu-report-extension {
|
||||
--menu-image: url('report.svg');
|
||||
}
|
||||
|
||||
/* FIX header icons for the app menu sub menus (eg. fx account, history...) */
|
||||
.panel-header > h1 {
|
||||
text-align: left;
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
|
||||
.wordmark::after {
|
||||
content: 'Plus' !important;
|
||||
}
|
||||
|
||||
/* header icons for the app menu sub menus (eg. fx account, history...) */
|
||||
.panel-header > h1 > span::before {
|
||||
content: '';
|
||||
|
@ -698,10 +1115,67 @@
|
|||
--fp-enabled: 1;
|
||||
}
|
||||
|
||||
@media not (-moz-platform: linux) {
|
||||
.unified-extensions-context-menu-pin-to-toolbar {
|
||||
--menu-image: url('pin.svg');
|
||||
}
|
||||
}
|
||||
|
||||
.unified-extensions-context-menu-move-widget-down {
|
||||
--menu-image: url('arrow-down.svg');
|
||||
}
|
||||
|
||||
.unified-extensions-context-menu-move-widget-up {
|
||||
--menu-image: url('arrow-up.svg');
|
||||
}
|
||||
|
||||
#alltabs-button {
|
||||
list-style-image: url('chrome://browser/skin/tabs.svg') !important;
|
||||
}
|
||||
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menuitem:not(
|
||||
.menuitem-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menuitem,
|
||||
.unified-nav-current
|
||||
),
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menu:not(
|
||||
.menu-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menu,
|
||||
.unified-nav-current
|
||||
),
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown) > menupopup > menucaption {
|
||||
padding-inline-start: calc(
|
||||
var(--zen-contextmenu-menuitem-padding-inline) + var(--zen-contextmenu-menuicon-margin-inline) /
|
||||
2
|
||||
) !important;
|
||||
}
|
||||
|
||||
menupopup > menuitem:is([type='checkbox']) .menu-iconic-left {
|
||||
--menu-image: none !important;
|
||||
margin-inline-start: 4px;
|
||||
|
||||
@media not (-moz-platform: windows) {
|
||||
margin-inline-end: 0;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-platform: windows) {
|
||||
menupopup > menuitem[checked='true'] {
|
||||
padding-inline-start: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
#toolbar-context-toggle-vertical-tabs,
|
||||
#toolbar-context-customize-sidebar,
|
||||
#sidebarRevampSeparator {
|
||||
|
|
3
src/zen/@types/lib.gecko.darwin.d.ts
vendored
3
src/zen/@types/lib.gecko.darwin.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
|
3
src/zen/@types/lib.gecko.dom.d.ts
vendored
3
src/zen/@types/lib.gecko.dom.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source .webidl files.
|
||||
|
|
3
src/zen/@types/lib.gecko.glean.d.ts
vendored
3
src/zen/@types/lib.gecko.glean.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source metrics.yaml files.
|
||||
|
|
3
src/zen/@types/lib.gecko.linux.d.ts
vendored
3
src/zen/@types/lib.gecko.linux.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
|
3
src/zen/@types/lib.gecko.modules.d.ts
vendored
3
src/zen/@types/lib.gecko.modules.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated by running "mach ts paths".
|
||||
|
|
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from xpc.msg and error_list.json.
|
||||
|
|
3
src/zen/@types/lib.gecko.services.d.ts
vendored
3
src/zen/@types/lib.gecko.services.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from services.json.
|
||||
|
|
3
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
3
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* Gecko generic/specialized adjustments for xpcom and webidl types.
|
||||
*/
|
||||
|
|
3
src/zen/@types/lib.gecko.win32.d.ts
vendored
3
src/zen/@types/lib.gecko.win32.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
|
3
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
3
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
|
3
src/zen/@types/lib.gecko.xpidl.d.ts
vendored
3
src/zen/@types/lib.gecko.xpidl.d.ts
vendored
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* Gecko XPIDL base types.
|
||||
*/
|
||||
|
|
|
@ -1,32 +1,16 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Utility to register JSWindowActors
|
||||
|
||||
var gZenActorsManager = {
|
||||
_actors: new Set(),
|
||||
_lazy: {},
|
||||
|
||||
init() {
|
||||
ChromeUtils.defineESModuleGetters(this._lazy, {
|
||||
ActorManagerParent: 'resource://gre/modules/ActorManagerParent.sys.mjs',
|
||||
});
|
||||
},
|
||||
|
||||
addJSWindowActor(name, data) {
|
||||
if (!this._lazy.ActorManagerParent) {
|
||||
this.init();
|
||||
}
|
||||
if (this._actors.has(name)) {
|
||||
addJSWindowActor(...args) {
|
||||
if (this._actors.has(args[0])) {
|
||||
// Actor already registered, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const decl = {};
|
||||
decl[name] = data;
|
||||
try {
|
||||
this._lazy.ActorManagerParent.addJSWindowActors(decl);
|
||||
this._actors.add(name);
|
||||
ChromeUtils.registerWindowActor(...args);
|
||||
this._actors.add(args[0]);
|
||||
} catch (e) {
|
||||
console.warn(`Failed to register JSWindowActor: ${e}`);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
var gZenOperatingSystemCommonUtils = {
|
||||
kZenOSToSmallName: {
|
||||
WINNT: 'windows',
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
|
||||
|
||||
export var ZenCustomizableUI = new (class {
|
||||
constructor() {}
|
||||
|
||||
TYPE_TOOLBAR = 'toolbar';
|
||||
defaultSidebarIcons = ['preferences-button', 'zen-workspaces-button', 'downloads-button'];
|
||||
defaultSidebarIcons = ['zen-open-library', 'zen-workspaces-button', 'downloads-button'];
|
||||
|
||||
startup(CustomizableUIInternal) {
|
||||
CustomizableUIInternal.registerArea(
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,50 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenSessionStore extends ZenPreloadedFeature {
|
||||
init() {
|
||||
this.#waitAndCleanup();
|
||||
}
|
||||
|
||||
promiseInitialized = new Promise((resolve) => {
|
||||
this._resolveInitialized = resolve;
|
||||
});
|
||||
|
||||
restoreInitialTabData(tab, tabData) {
|
||||
if (tabData.zenWorkspace) {
|
||||
tab.setAttribute('zen-workspace-id', tabData.zenWorkspace);
|
||||
}
|
||||
if (tabData.zenPinnedId) {
|
||||
tab.setAttribute('zen-pin-id', tabData.zenPinnedId);
|
||||
}
|
||||
if (tabData.zenIsEmpty) {
|
||||
tab.setAttribute('zen-empty-tab', 'true');
|
||||
}
|
||||
if (tabData.zenHasStaticLabel) {
|
||||
tab.setAttribute('zen-has-static-label', 'true');
|
||||
}
|
||||
if (tabData.zenEssential) {
|
||||
tab.setAttribute('zen-essential', 'true');
|
||||
}
|
||||
if (tabData.zenDefaultUserContextId) {
|
||||
tab.setAttribute('zenDefaultUserContextId', 'true');
|
||||
}
|
||||
if (tabData.zenPinnedEntry) {
|
||||
tab.setAttribute('zen-pinned-entry', tabData.zenPinnedEntry);
|
||||
}
|
||||
}
|
||||
|
||||
async #waitAndCleanup() {
|
||||
await SessionStore.promiseAllWindowsRestored;
|
||||
await SessionStore.promiseInitialized;
|
||||
this.#cleanup();
|
||||
}
|
||||
|
||||
#cleanup() {
|
||||
this._resolveInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenSessionStore = new ZenSessionStore();
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
var ZenStartup = {
|
||||
init() {
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
var gZenUIManager = {
|
||||
_popupTrackingElements: [],
|
||||
_hoverPausedForExpand: false,
|
||||
_hasLoadedDOM: false,
|
||||
testingEnabled: Services.prefs.getBoolPref('zen.testing.enabled', false),
|
||||
|
||||
_lastClickPosition: null,
|
||||
|
||||
_toastTimeouts: [],
|
||||
|
||||
init() {
|
||||
|
@ -36,8 +31,6 @@ var gZenUIManager = {
|
|||
|
||||
gURLBar._zenTrimURL = this.urlbarTrim.bind(this);
|
||||
|
||||
document.addEventListener('mousedown', this.handleMouseDown.bind(this), true);
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, 'motion', () => {
|
||||
return ChromeUtils.importESModule('chrome://browser/content/zen-vendor/motion.min.mjs', {
|
||||
global: 'current',
|
||||
|
@ -69,13 +62,6 @@ var gZenUIManager = {
|
|||
gZenMediaController.init();
|
||||
},
|
||||
|
||||
handleMouseDown(event) {
|
||||
this._lastClickPosition = {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
};
|
||||
},
|
||||
|
||||
updateTabsToolbar() {
|
||||
const kUrlbarHeight = 440;
|
||||
gURLBar.textbox.style.setProperty(
|
||||
|
@ -84,6 +70,7 @@ var gZenUIManager = {
|
|||
);
|
||||
gZenVerticalTabsManager.actualWindowButtons.removeAttribute('zen-has-hover');
|
||||
gZenVerticalTabsManager.recalculateURLBarHeight();
|
||||
setTimeout(gURLBar.formatValue.bind(gURLBar), 350);
|
||||
if (!this._preventToolbarRebuild) {
|
||||
setTimeout(() => {
|
||||
gZenWorkspaces.updateTabsContainers();
|
||||
|
@ -1002,18 +989,10 @@ var gZenVerticalTabsManager = {
|
|||
async renameTabKeydown(event) {
|
||||
event.stopPropagation();
|
||||
if (event.key === 'Enter') {
|
||||
const isTab = !!event.target.closest('.tabbrowser-tab');
|
||||
let label = isTab
|
||||
? this._tabEdited.querySelector('.tab-label-container-editing')
|
||||
: this._tabEdited;
|
||||
let input = document.getElementById('tab-label-input');
|
||||
let label = this._tabEdited.querySelector('.tab-label-container-editing');
|
||||
let input = this._tabEdited.querySelector('#tab-label-input');
|
||||
let newName = input.value.trim();
|
||||
|
||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||
input.remove();
|
||||
if (!isTab) {
|
||||
await this._tabEdited.onRenameFinished(newName);
|
||||
} else {
|
||||
// Check if name is blank, reset if so
|
||||
// Always remove, so we can always rename and if it's empty,
|
||||
// it will reset to the original name anyway
|
||||
|
@ -1033,6 +1012,7 @@ var gZenVerticalTabsManager = {
|
|||
!!newName
|
||||
);
|
||||
}
|
||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||
|
||||
// Maybe add some confetti here?!?
|
||||
gZenUIManager.motion.animate(
|
||||
|
@ -1044,12 +1024,8 @@ var gZenVerticalTabsManager = {
|
|||
duration: 0.25,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const editorContainer = this._tabEdited.querySelector('.tab-editor-container');
|
||||
if (editorContainer) {
|
||||
editorContainer.remove();
|
||||
}
|
||||
this._tabEdited.querySelector('.tab-editor-container').remove();
|
||||
label.classList.remove('tab-label-container-editing');
|
||||
|
||||
this._tabEdited = null;
|
||||
|
@ -1059,40 +1035,34 @@ var gZenVerticalTabsManager = {
|
|||
},
|
||||
|
||||
renameTabStart(event) {
|
||||
const isTab = !!event.target.closest('.tabbrowser-tab');
|
||||
if (
|
||||
this._tabEdited ||
|
||||
((!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
|
||||
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick')) &&
|
||||
isTab) ||
|
||||
!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
|
||||
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick') ||
|
||||
!gZenVerticalTabsManager._prefsSidebarExpanded
|
||||
)
|
||||
return;
|
||||
this._tabEdited = event.target.closest('.tabbrowser-tab');
|
||||
if (
|
||||
!this._tabEdited ||
|
||||
((!this._tabEdited.pinned || this._tabEdited.hasAttribute('zen-essential')) && isTab)
|
||||
!this._tabEdited.pinned ||
|
||||
this._tabEdited.hasAttribute('zen-essential')
|
||||
) {
|
||||
this._tabEdited = null;
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
document.documentElement.setAttribute('zen-renaming-tab', 'true');
|
||||
const label = isTab ? this._tabEdited.querySelector('.tab-label-container') : this._tabEdited;
|
||||
const label = this._tabEdited.querySelector('.tab-label-container');
|
||||
label.classList.add('tab-label-container-editing');
|
||||
|
||||
if (isTab) {
|
||||
const container = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="tab-label-container tab-editor-container" flex="1" align="start" pack="center"></vbox>
|
||||
`);
|
||||
label.after(container);
|
||||
}
|
||||
const containerHtml = isTab
|
||||
? this._tabEdited.querySelector('.tab-editor-container')
|
||||
: this._tabEdited.parentNode;
|
||||
const containerHtml = this._tabEdited.querySelector('.tab-editor-container');
|
||||
const input = document.createElement('input');
|
||||
input.id = 'tab-label-input';
|
||||
input.value = isTab ? this._tabEdited.label : this._tabEdited.textContent;
|
||||
input.value = this._tabEdited.label;
|
||||
input.addEventListener('keydown', this.renameTabKeydown.bind(this));
|
||||
|
||||
containerHtml.appendChild(input);
|
||||
|
@ -1107,16 +1077,8 @@ var gZenVerticalTabsManager = {
|
|||
return;
|
||||
}
|
||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||
const editorContainer = this._tabEdited.querySelector('.tab-editor-container');
|
||||
let input = document.getElementById('tab-label-input');
|
||||
input.remove();
|
||||
if (editorContainer) {
|
||||
editorContainer.remove();
|
||||
}
|
||||
const isTab = !!this._tabEdited.closest('.tabbrowser-tab');
|
||||
const label = isTab
|
||||
? this._tabEdited.querySelector('.tab-label-container-editing')
|
||||
: this._tabEdited;
|
||||
this._tabEdited.querySelector('.tab-editor-container').remove();
|
||||
const label = this._tabEdited.querySelector('.tab-label-container-editing');
|
||||
label.classList.remove('tab-label-container-editing');
|
||||
|
||||
this._tabEdited = null;
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
|
@ -90,9 +87,6 @@ class ZenUIMigration {
|
|||
}
|
||||
|
||||
_migrateV4(win) {
|
||||
if (AppConstants.platform === 'linux') {
|
||||
return;
|
||||
}
|
||||
Services.prefs.setBoolPref(
|
||||
'browser.tabs.unloadOnLowMemory',
|
||||
Services.prefs.getBoolPref('zen.tab-unloader.enabled', true)
|
||||
|
|
|
@ -78,6 +78,16 @@
|
|||
transition: background-color var(--inactive-window-transition);
|
||||
}
|
||||
|
||||
@media (not (-moz-windows-mica)) and -moz-pref('zen.view.grey-out-inactive-windows') {
|
||||
transition: color var(--inactive-window-transition);
|
||||
:root:not([zen-welcome-stage]) &:-moz-window-inactive {
|
||||
color: var(--toolbox-textcolor-inactive);
|
||||
&::before {
|
||||
background-color: var(--toolbox-bgcolor-inactive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zen-browser-grain {
|
||||
content: '';
|
||||
width: 100%;
|
||||
|
@ -121,7 +131,9 @@
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
@media -moz-pref('zen.view.grey-out-inactive-windows') {
|
||||
@media (-moz-windows-accent-color-in-titlebar) and (-moz-windows-mica) {
|
||||
background-color: ActiveCaption;
|
||||
color: CaptionText;
|
||||
transition: background-color var(--inactive-window-transition);
|
||||
&:-moz-window-inactive {
|
||||
background-color: InactiveCaption;
|
||||
|
@ -252,7 +264,6 @@
|
|||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
|
|
|
@ -35,13 +35,17 @@
|
|||
--uc-permission-item-margin-block: 4px;
|
||||
--uc-permission-item-padding-inline: 16px;
|
||||
--zen-panel-separator-width: 1px;
|
||||
|
||||
--zen-contextmenu-menuitem-padding-inline: 10px;
|
||||
--zen-contextmenu-menuicon-margin-inline: 12px;
|
||||
--zen-contextmenu-menuitem-margin: 0px 2px;
|
||||
}
|
||||
|
||||
menupopup,
|
||||
panel {
|
||||
--panel-background: var(--arrowpanel-background);
|
||||
--panel-border-radius: var(--zen-native-inner-radius);
|
||||
--menuitem-padding: 6px !important;
|
||||
--menuitem-padding: 6px 5px !important;
|
||||
}
|
||||
|
||||
/* split-view popup */
|
||||
|
@ -243,6 +247,11 @@ panel {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
menupopup::part(content),
|
||||
panel::part(content) {
|
||||
border: var(--zen-appcontent-border);
|
||||
}
|
||||
|
||||
menupopup,
|
||||
panel {
|
||||
box-shadow: none;
|
||||
|
|
|
@ -73,6 +73,9 @@ document.addEventListener(
|
|||
event.sourceEvent.target.getAttribute('zen-workspace-id')
|
||||
);
|
||||
break;
|
||||
case 'cmd_zenToggleLibrary':
|
||||
gZenLibrary.toggle();
|
||||
break;
|
||||
case 'cmd_zenToggleTabsOnRight':
|
||||
gZenVerticalTabsManager.toggleTabsOnRight();
|
||||
break;
|
||||
|
@ -88,6 +91,12 @@ document.addEventListener(
|
|||
case 'cmd_zenRemoveFromEssentials':
|
||||
gZenPinnedTabManager.removeEssentials();
|
||||
break;
|
||||
case 'cmd_zenCtxDeleteWorkspace':
|
||||
gZenWorkspaces.contextDeleteWorkspace(event);
|
||||
break;
|
||||
case 'cmd_zenChangeWorkspaceName':
|
||||
gZenVerticalTabsManager.renameTabStart(event);
|
||||
break;
|
||||
default:
|
||||
if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
|
||||
const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
const lazyCompactMode = {};
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
@ -91,6 +88,10 @@ var gZenCompactModeManager = {
|
|||
this.preference === value ||
|
||||
document.documentElement.hasAttribute('zen-compact-animating')
|
||||
) {
|
||||
if (typeof this._wasInCompactMode !== 'undefined') {
|
||||
// We wont do anything with it anyway, so we remove it
|
||||
delete this._wasInCompactMode;
|
||||
}
|
||||
// We dont want the user to be able to spam the button
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
const { Downloads } = ChromeUtils.importESModule('resource://gre/modules/Downloads.sys.mjs');
|
||||
|
||||
|
@ -14,10 +11,24 @@
|
|||
});
|
||||
|
||||
class ZenDownloadAnimation extends ZenDOMOperatedFeature {
|
||||
#lastClickPosition = null;
|
||||
|
||||
async init() {
|
||||
this.#setupClickListener();
|
||||
await this.#setupDownloadListeners();
|
||||
}
|
||||
|
||||
#setupClickListener() {
|
||||
document.addEventListener('mousedown', this.#handleClick.bind(this), true);
|
||||
}
|
||||
|
||||
#handleClick(event) {
|
||||
this.#lastClickPosition = {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
};
|
||||
}
|
||||
|
||||
async #setupDownloadListeners() {
|
||||
try {
|
||||
const list = await Downloads.getList(Downloads.ALL);
|
||||
|
@ -39,14 +50,14 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (!gZenUIManager._lastClickPosition) {
|
||||
if (!this.#lastClickPosition) {
|
||||
console.warn(
|
||||
`[${ZenDownloadAnimation.name}] No recent click position available for animation`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#animateDownload(gZenUIManager._lastClickPosition);
|
||||
this.#animateDownload(this.#lastClickPosition);
|
||||
}
|
||||
|
||||
#animateDownload(startPosition) {
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenFolders {
|
||||
constructor() {
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenGlanceManager extends ZenDOMOperatedFeature {
|
||||
_animating = false;
|
||||
|
@ -154,8 +151,8 @@
|
|||
this.animatingOpen = true;
|
||||
this._animating = true;
|
||||
|
||||
const initialX = data.clientX;
|
||||
const initialY = data.clientY;
|
||||
const initialX = data.x;
|
||||
const initialY = data.y;
|
||||
const initialWidth = data.width;
|
||||
const initialHeight = data.height;
|
||||
|
||||
|
@ -597,10 +594,8 @@
|
|||
this.openGlance(
|
||||
{
|
||||
url: undefined,
|
||||
...(gZenUIManager._lastClickPosition || {
|
||||
clientX: browserRect.width / 2,
|
||||
clientY: browserRect.height / 2,
|
||||
}),
|
||||
x: browserRect.width / 2,
|
||||
y: browserRect.height / 2,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
|
@ -641,12 +636,7 @@
|
|||
.classList.remove('zen-glance-background');
|
||||
this.#currentParentTab._visuallySelected = false;
|
||||
this.hideSidebarButtons();
|
||||
if (forSplit) {
|
||||
this.finishOpeningGlance();
|
||||
return;
|
||||
}
|
||||
if (gReduceMotion || forSplit) {
|
||||
gZenViewSplitter.deactivateCurrentSplitView();
|
||||
this.finishOpeningGlance();
|
||||
return;
|
||||
}
|
||||
|
@ -661,7 +651,6 @@
|
|||
type: 'spring',
|
||||
}
|
||||
);
|
||||
gZenViewSplitter.deactivateCurrentSplitView();
|
||||
this.finishOpeningGlance();
|
||||
}
|
||||
|
||||
|
@ -686,8 +675,8 @@
|
|||
const rect = event.target.getBoundingClientRect();
|
||||
const data = {
|
||||
url: event.target._placesNode.uri,
|
||||
clientX: rect.left,
|
||||
clientY: rect.top,
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
};
|
||||
|
@ -716,8 +705,8 @@
|
|||
}
|
||||
|
||||
getTabOrGlanceParent(tab) {
|
||||
if (tab?.hasAttribute('glance-id') && this.#glances) {
|
||||
const parentTab = this.#glances.get(tab.getAttribute('glance-id'))?.parentTab;
|
||||
if (tab?.hasAttribute('glance-id')) {
|
||||
const parentTab = this.#glances.get(tab.getAttribute('glance-id')).parentTab;
|
||||
if (parentTab) {
|
||||
return parentTab;
|
||||
}
|
||||
|
@ -745,43 +734,18 @@
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onSearchSelectCommand(where) {
|
||||
if (where !== 'tab') {
|
||||
return;
|
||||
}
|
||||
const currentTab = gBrowser.selectedTab;
|
||||
const parentTab = currentTab.owner;
|
||||
if (!parentTab) {
|
||||
return;
|
||||
}
|
||||
// Open a new glance if the current tab is a glance tab
|
||||
const browserRect = gBrowser.tabbox.getBoundingClientRect();
|
||||
this.openGlance(
|
||||
{
|
||||
url: undefined,
|
||||
...(gZenUIManager._lastClickPosition || {
|
||||
clientX: browserRect.width / 2,
|
||||
clientY: browserRect.height / 2,
|
||||
}),
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
currentTab,
|
||||
parentTab
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenGlanceManager = new ZenGlanceManager();
|
||||
|
||||
function registerWindowActors() {
|
||||
if (Services.prefs.getBoolPref('zen.glance.enabled', true)) {
|
||||
gZenActorsManager.addJSWindowActor('ZenGlance', {
|
||||
parent: {
|
||||
esModuleURI: 'resource:///actors/ZenGlanceParent.sys.mjs',
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenGlanceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'resource:///actors/ZenGlanceChild.sys.mjs',
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenGlanceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
keydown: {
|
||||
|
@ -791,9 +755,9 @@
|
|||
},
|
||||
allFrames: true,
|
||||
matches: ['*://*/*'],
|
||||
enablePreference: 'zen.glance.enabled',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerWindowActors();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
export class ZenGlanceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -74,8 +71,8 @@ export class ZenGlanceChild extends JSWindowActorChild {
|
|||
const rect = target.getBoundingClientRect();
|
||||
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
||||
url,
|
||||
clientX: rect.left,
|
||||
clientY: rect.top,
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
export class ZenGlanceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
|
|
270
src/zen/library/ZenLibrary.mjs
Normal file
270
src/zen/library/ZenLibrary.mjs
Normal file
|
@ -0,0 +1,270 @@
|
|||
{
|
||||
class ZenLibraryElement extends MozXULElement {
|
||||
#currentTab = null;
|
||||
#availableTabs = ['workspaces', 'mods'];
|
||||
|
||||
static get markup() {
|
||||
return `
|
||||
<vbox id="zen-library-sidebar">
|
||||
<vbox id="zen-library-sidebar-buttons" flex="1">
|
||||
<toolbarbutton class="toolbarbutton-1 zen-library-sidebar-button" id="zen-library-sidebar-workspaces" data-l10n-id="zen-library-sidebar-workspaces"/>
|
||||
<toolbarbutton class="toolbarbutton-1 zen-library-sidebar-button" id="zen-library-sidebar-mods" data-l10n-id="zen-library-sidebar-mods"/>
|
||||
</vbox>
|
||||
<hbox id="zen-library-sidebar-footer">
|
||||
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-open-library" command="cmd_zenToggleLibrary"></toolbarbutton>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="zen-library-content">
|
||||
<hbox content="workspaces" size="big">
|
||||
</hbox>
|
||||
<hbox content="mods" size="small"></hbox>
|
||||
</vbox>
|
||||
`;
|
||||
}
|
||||
|
||||
static get inheritedAttributes() {
|
||||
return {
|
||||
'#zen-library-content': 'content,size=content-size',
|
||||
'#zen-library-sidebar': 'content',
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
set currentTab(tab) {
|
||||
if (!this.#availableTabs.includes(tab)) {
|
||||
throw new Error(`Tab "${tab}" is not available in Zen Library.`);
|
||||
}
|
||||
this.#currentTab = tab;
|
||||
this.setAttribute('content', tab);
|
||||
// Also set if the size is big or small based on the tab.
|
||||
const element = this.querySelector(`#zen-library-content hbox[content="${tab}"]`);
|
||||
this.setAttribute('content-size', element.getAttribute('size') || 'small');
|
||||
for (const availableTab of this.#availableTabs) {
|
||||
const button = this.querySelector(`#zen-library-sidebar-${availableTab}`);
|
||||
if (availableTab === tab) {
|
||||
button.setAttribute('active', 'true');
|
||||
} else {
|
||||
button.removeAttribute('active');
|
||||
}
|
||||
const contentContainer = this.#getContentContainer(availableTab);
|
||||
if (availableTab === tab) {
|
||||
contentContainer.removeAttribute('hidden');
|
||||
} else {
|
||||
contentContainer.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get currentTab() {
|
||||
return this.#currentTab;
|
||||
}
|
||||
|
||||
set open(value) {
|
||||
if (value) {
|
||||
this.setAttribute('open', 'true');
|
||||
this.onOpen(); // Trigger the onOpen method to populate the content
|
||||
} else {
|
||||
this.removeAttribute('open');
|
||||
this.onClose(); // Trigger the onClose method if needed
|
||||
}
|
||||
}
|
||||
|
||||
get open() {
|
||||
return this.getAttribute('open') === 'true';
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.delayConnectedCallback()) {
|
||||
// If we are not ready yet, or if we have already connected, we
|
||||
// don't need to do anything.
|
||||
return;
|
||||
}
|
||||
|
||||
this.id = 'zen-library';
|
||||
this.appendChild(this.constructor.fragment);
|
||||
this.initializeAttributeInheritance();
|
||||
|
||||
for (const availableTab of this.#availableTabs) {
|
||||
const button = this.querySelector(`#zen-library-sidebar-${availableTab}`);
|
||||
button.addEventListener('command', () => {
|
||||
this.currentTab = availableTab;
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('TabSelect', this);
|
||||
|
||||
this.currentTab = 'workspaces'; // Default tab
|
||||
}
|
||||
|
||||
#getContentContainer(tab) {
|
||||
return this.querySelector(`#zen-library-content hbox[content="${tab}"]`);
|
||||
}
|
||||
|
||||
#createWorkspaceElement(workspace) {
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="zen-workspace-item" zen-workspace-id="${workspace.uuid}">
|
||||
<hbox class="zen-workspace-item-header">
|
||||
<label class="zen-workspace-item-name"></label>
|
||||
</hbox>
|
||||
<vbox class="zen-workspace-item-content">
|
||||
</vbox>
|
||||
</vbox>
|
||||
`);
|
||||
|
||||
const workspaceLabel = fragment.querySelector('.zen-workspace-item-name');
|
||||
workspaceLabel.textContent = workspace.name;
|
||||
workspaceLabel.addEventListener(
|
||||
'click',
|
||||
gZenVerticalTabsManager.renameTabStart.bind(gZenVerticalTabsManager)
|
||||
);
|
||||
|
||||
const workspaceItem = fragment.querySelector('.zen-workspace-item');
|
||||
workspaceItem.style.setProperty(
|
||||
'--zen-workspace-gradient',
|
||||
gZenThemePicker.getGradient(workspace.theme.gradientColors, true, workspace.theme.rotation)
|
||||
);
|
||||
|
||||
// TODO: Not jet! Figure this out
|
||||
//const workspaceElement = gZenWorkspaces.workspaceElement(workspace.uuid);
|
||||
//fragment.querySelector('.zen-workspace-item-content').appendChild(workspaceElement);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
async onOpen(event) {
|
||||
const conatainer = this.#getContentContainer('workspaces');
|
||||
conatainer.innerHTML = ''; // Clear the container
|
||||
|
||||
const workspaces = await gZenWorkspaces._workspaces();
|
||||
for (const workspace of workspaces.workspaces) {
|
||||
const workspaceElement = this.#createWorkspaceElement(workspace);
|
||||
conatainer.appendChild(workspaceElement);
|
||||
}
|
||||
}
|
||||
|
||||
async onClose(event) {}
|
||||
|
||||
on_TabSelect(event) {
|
||||
if (!this.open) return;
|
||||
gZenLibrary.close();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('zen-library', ZenLibraryElement);
|
||||
|
||||
class ZenLibrary {
|
||||
#animating = false;
|
||||
|
||||
constructor() {
|
||||
ChromeUtils.defineLazyGetter(this, 'wrapper', () => document.getElementById('zen-library'));
|
||||
}
|
||||
|
||||
get isOpen() {
|
||||
return this.wrapper.hasAttribute('open');
|
||||
}
|
||||
|
||||
set isOpen(value) {
|
||||
if (this.#animating) {
|
||||
return; // Prevent multiple animations from running at the same time
|
||||
}
|
||||
this.#animating = true;
|
||||
if (value) {
|
||||
this.wrapper.open = value;
|
||||
this.animateLibrary(false).then(() => {
|
||||
this.#animating = false;
|
||||
});
|
||||
} else {
|
||||
this.animateLibrary(true).then(() => {
|
||||
this.wrapper.open = value;
|
||||
this.#animating = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
}
|
||||
|
||||
async animateLibrary(open) {
|
||||
window.docShell.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIAppWindow)
|
||||
.rollupAllPopups();
|
||||
|
||||
let elementsToAnimate = [gNavToolbox];
|
||||
if (gZenVerticalTabsManager._hasSetSingleToolbar) {
|
||||
elementsToAnimate.push(gURLBar.textbox);
|
||||
}
|
||||
const wrapperWidth = this.wrapper.getBoundingClientRect().width;
|
||||
const appContentWrapper = document.getElementById('zen-appcontent-wrapper');
|
||||
if (open) {
|
||||
await Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
elementsToAnimate,
|
||||
{
|
||||
transform: ['translateX(100%)', 'translateX(0)'],
|
||||
opacity: [0, 1],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.wrapper,
|
||||
{
|
||||
marginLeft: ['0px', `${-wrapperWidth}px`],
|
||||
transform: ['translateX(0)', 'translateX(100%)'],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
]);
|
||||
appContentWrapper.style.minWidth = ''; // Reset the min-width after the animation
|
||||
gNavToolbox.style.display = ''; // Hide the toolbox during the animation
|
||||
} else {
|
||||
appContentWrapper.style.minWidth = `${appContentWrapper.getBoundingClientRect().width}px`;
|
||||
await Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
elementsToAnimate,
|
||||
{
|
||||
transform: ['translateX(0)', 'translateX(100%)'],
|
||||
opacity: [1, 0],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.wrapper,
|
||||
{
|
||||
marginLeft: [`${-wrapperWidth}px`, '0px'],
|
||||
transform: ['translateX(-100%)', 'translateX(0%)'],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
]);
|
||||
gNavToolbox.style.display = 'none'; // Show the toolbox after the animation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenLibrary = new ZenLibrary();
|
||||
}
|
103
src/zen/library/zen-library.css
Normal file
103
src/zen/library/zen-library.css
Normal file
|
@ -0,0 +1,103 @@
|
|||
#zen-library {
|
||||
order: -1;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
left: -200%;
|
||||
|
||||
&[open='true'] {
|
||||
display: flex;
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-sidebar {
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 0.6rem;
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
-moz-window-dragging: drag;
|
||||
border-right: 1px solid var(--zen-colors-border);
|
||||
|
||||
#zen-library-sidebar-buttons {
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
& .zen-library-sidebar-button {
|
||||
transition: background-color 0.1s;
|
||||
padding: 0.8rem 0.7rem;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
appearance: none;
|
||||
gap: 0.5rem;
|
||||
border-radius: 6px !important;
|
||||
&:hover,
|
||||
&[active='true'] {
|
||||
background: light-dark(rgba(255, 255, 255, 0.2), rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-sidebar-footer {
|
||||
justify-content: space-between;
|
||||
|
||||
& toolbarbutton {
|
||||
appearance: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-content {
|
||||
transition: width 0.15s;
|
||||
width: 20vw;
|
||||
overflow-x: auto;
|
||||
|
||||
&[size='big'] {
|
||||
width: 60vw;
|
||||
}
|
||||
|
||||
& > * {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
& > [content='workspaces'] {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
padding: 6rem;
|
||||
|
||||
& .zen-workspace-item {
|
||||
width: 16rem;
|
||||
padding: 0.7rem;
|
||||
border-radius: var(--zen-border-radius);
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
background: var(--zen-workspace-gradient);
|
||||
border: 1px solid light-dark(
|
||||
rgba(255, 255, 255, 0.2),
|
||||
rgba(0, 0, 0, 0.2)
|
||||
);
|
||||
height: 100%;
|
||||
|
||||
&::before {
|
||||
background-image: url(chrome://browser/content/zen-images/grain-bg.png);
|
||||
opacity: var(--zen-grainy-background-opacity, 0);
|
||||
mix-blend-mode: hard-light;
|
||||
width: 60%;
|
||||
height: 60%;
|
||||
pointer-events: none;
|
||||
top: 50%;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
content: '';
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
const lazy = {};
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
|
|
@ -1,735 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
{
|
||||
class ZenMods extends ZenPreloadedFeature {
|
||||
// private properties start
|
||||
#kZenStylesheetModHeader = '/* Zen Mods - Generated by ZenMods.';
|
||||
#kZenStylesheetModHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
||||
* Your changes will be overwritten.
|
||||
* Instead, go to the preferences and edit the mods there.
|
||||
*/
|
||||
`;
|
||||
#kZenStylesheetModFooter = `
|
||||
/* End of Zen Mods */
|
||||
`;
|
||||
#getCurrentDateTime = () =>
|
||||
new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'full',
|
||||
}).format(new Date().getTime());
|
||||
|
||||
constructor() {
|
||||
console.log('[ZenMods]: Initializing ZenMods module');
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
// Stylesheet service
|
||||
#sss = null;
|
||||
|
||||
get #stylesheetService() {
|
||||
if (!this.#sss) {
|
||||
this.#sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
||||
Ci.nsIStyleSheetService
|
||||
);
|
||||
}
|
||||
return this.#sss;
|
||||
}
|
||||
|
||||
#ssu = null;
|
||||
|
||||
get #styleSheetUri() {
|
||||
if (!this.#ssu) {
|
||||
this.#ssu = Services.io.newFileURI(new FileUtils.File(this.#styleSheetPath));
|
||||
}
|
||||
return this.#ssu;
|
||||
}
|
||||
|
||||
get #styleSheetPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
||||
}
|
||||
|
||||
async #handleDisableMods() {
|
||||
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
||||
console.log('[ZenMods]: Disabling mods module.');
|
||||
|
||||
await this.#removeStylesheet();
|
||||
} else {
|
||||
console.log('[ZenMods]: Enabling mods module.');
|
||||
|
||||
await this.#rebuildModsStylesheet();
|
||||
}
|
||||
}
|
||||
|
||||
#getStylesheetURIForMod(mod) {
|
||||
return Services.io.newFileURI(
|
||||
new FileUtils.File(PathUtils.join(this.getModFolder(mod.id), 'chrome.css'))
|
||||
);
|
||||
}
|
||||
|
||||
async #insertStylesheet() {
|
||||
if (await IOUtils.exists(this.#styleSheetPath)) {
|
||||
await this.#stylesheetService.loadAndRegisterSheet(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!this.#stylesheetService.sheetRegistered(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
)
|
||||
) {
|
||||
console.error(`[ZenMods]: Failed to register stylesheet at ${this.#styleSheetUri.spec}.`);
|
||||
}
|
||||
}
|
||||
|
||||
async #removeStylesheet() {
|
||||
await this.#stylesheetService.unregisterSheet(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
);
|
||||
const rv = this.#stylesheetService.sheetRegistered(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
);
|
||||
await IOUtils.remove(this.#styleSheetPath, { ignoreAbsent: true });
|
||||
|
||||
if (rv || (await IOUtils.exists(this.#styleSheetPath))) {
|
||||
console.error(`[ZenMods]: Failed to unregister stylesheet at ${this.#styleSheetUri.spec}.`);
|
||||
}
|
||||
}
|
||||
|
||||
async #rebuildModsStylesheet() {
|
||||
await this.#removeStylesheet();
|
||||
|
||||
const mods = await this.#getEnabledMods();
|
||||
|
||||
await this.#writeStylesheet(mods);
|
||||
|
||||
const modsWithPreferences = await Promise.all(
|
||||
mods.map(async (mod) => {
|
||||
const preferences = await this.getModPreferences(mod);
|
||||
|
||||
return {
|
||||
name: mod.name,
|
||||
enabled: mod.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.#setDefaults(modsWithPreferences);
|
||||
this.#writeToDom(modsWithPreferences);
|
||||
|
||||
await this.#insertStylesheet();
|
||||
}
|
||||
|
||||
async #getEnabledMods() {
|
||||
const modsObject = await this.getMods();
|
||||
const mods = Object.values(modsObject).filter(
|
||||
(mod) => mod.enabled === undefined || mod.enabled
|
||||
);
|
||||
|
||||
const modList = mods.map(({ name }) => name).join(', ');
|
||||
|
||||
const message =
|
||||
modList !== ''
|
||||
? `[ZenMods]: Loading enabled Zen mods: ${modList}.`
|
||||
: '[ZenMods]: No enabled Zen mods.';
|
||||
|
||||
console.log(message);
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
#setDefaults(modsWithPreferences) {
|
||||
for (const { preferences, enabled } of modsWithPreferences) {
|
||||
if (enabled !== undefined && !enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { type, property, defaultValue } of preferences) {
|
||||
if (defaultValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'checkbox') {
|
||||
const value = Services.prefs.getBoolPref(property, false);
|
||||
if (typeof defaultValue !== 'boolean') {
|
||||
console.warn(
|
||||
'[ZenMods]: Warning, invalid data type received for expected type boolean, skipping.'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
Services.prefs.setBoolPref(property, defaultValue);
|
||||
}
|
||||
} else {
|
||||
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
||||
|
||||
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
||||
console.warn(
|
||||
`[ZenMods]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value === 'zen-property-no-saved') {
|
||||
Services.prefs.setStringPref(property, defaultValue.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#writeToDom(modsWithPreferences) {
|
||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||
for (const { enabled, preferences, name } of modsWithPreferences) {
|
||||
const sanitizedName = this.sanitizeModName(name);
|
||||
|
||||
if (enabled !== undefined && !enabled) {
|
||||
const element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.removeProperty(`--${sanitizedProperty}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { property, type } of preferences) {
|
||||
const value = Services.prefs.getStringPref(property, '');
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
switch (type) {
|
||||
case 'dropdown': {
|
||||
if (value !== '') {
|
||||
let element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (!element) {
|
||||
element = browser.document.createElement('div');
|
||||
|
||||
element.style.display = 'none';
|
||||
element.setAttribute('id', sanitizedName);
|
||||
|
||||
browser.document.body.appendChild(element);
|
||||
}
|
||||
|
||||
element.setAttribute(sanitizedProperty, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'string': {
|
||||
if (value === '') {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.removeProperty(`--${sanitizedProperty}`);
|
||||
} else {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.setProperty(`--${sanitizedProperty}`, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #writeStylesheet(modList = []) {
|
||||
const mods = [];
|
||||
|
||||
for (let mod of modList) {
|
||||
mod._chromeURL = this.#getStylesheetURIForMod(mod).spec;
|
||||
mods.push(mod);
|
||||
}
|
||||
|
||||
let content = this.#kZenStylesheetModHeader;
|
||||
content += `\n* FILE GENERATED AT: ${this.#getCurrentDateTime()}\n`;
|
||||
content += this.#kZenStylesheetModHeaderBody;
|
||||
|
||||
for (let mod of mods) {
|
||||
if (mod.enabled !== undefined && !mod.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
content += `\n/* Name: ${mod.name} */\n`;
|
||||
content += `/* Description: ${mod.description} */\n`;
|
||||
content += `/* Author: @${mod.author} */\n`;
|
||||
|
||||
if (mod._readmeURL) {
|
||||
content += `/* Readme: ${mod.readme} */\n`;
|
||||
}
|
||||
|
||||
content += `@import url("${mod._chromeURL}");\n`;
|
||||
}
|
||||
|
||||
content += this.#kZenStylesheetModFooter;
|
||||
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
|
||||
await IOUtils.write(this.#styleSheetPath, buffer);
|
||||
}
|
||||
|
||||
#compareVersions(version1, version2) {
|
||||
let result = false;
|
||||
|
||||
if (typeof version1 !== 'object') {
|
||||
version1 = version1.toString().split('.');
|
||||
}
|
||||
|
||||
if (typeof version2 !== 'object') {
|
||||
version2 = version2.toString().split('.');
|
||||
}
|
||||
|
||||
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
|
||||
if (version1[i] == undefined) {
|
||||
version1[i] = 0;
|
||||
}
|
||||
if (version2[i] == undefined) {
|
||||
version2[i] = 0;
|
||||
}
|
||||
if (Number(version1[i]) < Number(version2[i])) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
if (version1[i] != version2[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#composeModApiUrl(modId) {
|
||||
// keeping theme here as it would require changes to CI to change the name
|
||||
return `https://zen-browser.github.io/theme-store/themes/${modId}/theme.json`;
|
||||
}
|
||||
|
||||
async #downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status} for url: ${url}`);
|
||||
}
|
||||
|
||||
const data = await response.text();
|
||||
|
||||
let content = data;
|
||||
|
||||
if (isStyleSheet) {
|
||||
content = '@-moz-document url-prefix("chrome:") {\n';
|
||||
|
||||
for (const line of data.split('\n')) {
|
||||
content += ` ${line}\n`;
|
||||
}
|
||||
|
||||
content += '}';
|
||||
}
|
||||
|
||||
// convert the data into a Uint8Array
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
await IOUtils.write(path, buffer);
|
||||
|
||||
return; // to exit the loop
|
||||
} catch (e) {
|
||||
attempt++;
|
||||
if (attempt >= maxRetries) {
|
||||
console.error('[ZenMods]: Error downloading file after retries', url, e);
|
||||
} else {
|
||||
console.warn(
|
||||
`[ZenMods]: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
|
||||
url,
|
||||
e
|
||||
);
|
||||
await new Promise((res) => setTimeout(res, retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private properties end
|
||||
|
||||
// public properties start
|
||||
|
||||
throttle(mainFunction, delay) {
|
||||
let timerFlag = null;
|
||||
|
||||
return (...args) => {
|
||||
if (timerFlag === null) {
|
||||
mainFunction(...args);
|
||||
timerFlag = setTimeout(() => {
|
||||
timerFlag = null;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
debounce(mainFunction, wait) {
|
||||
let timerFlag;
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timerFlag);
|
||||
timerFlag = setTimeout(() => {
|
||||
mainFunction(...args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
|
||||
sanitizeModName(name) {
|
||||
// Do not change to "mod-" for backwards compatibility
|
||||
return `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||
}
|
||||
|
||||
get updatePref() {
|
||||
return 'zen.themes.updated-value-observer';
|
||||
}
|
||||
|
||||
get modsRootPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
||||
}
|
||||
|
||||
get modsDataFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||
}
|
||||
|
||||
getModFolder(modId) {
|
||||
return PathUtils.join(this.modsRootPath, modId);
|
||||
}
|
||||
|
||||
async getMods() {
|
||||
if (!(await IOUtils.exists(this.modsDataFile))) {
|
||||
await IOUtils.writeJSON(this.modsDataFile, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
let mods = {};
|
||||
|
||||
try {
|
||||
mods = await IOUtils.readJSON(this.modsDataFile);
|
||||
|
||||
if (mods === null || typeof mods !== 'object') {
|
||||
throw new Error('Mods data file is invalid');
|
||||
}
|
||||
} catch {
|
||||
// If we have a corrupted file, reset it
|
||||
await IOUtils.writeJSON(this.modsDataFile, {});
|
||||
|
||||
Services.wm
|
||||
.getMostRecentWindow('navigator:browser')
|
||||
.gZenUIManager.showToast('zen-themes-corrupted', {
|
||||
timeout: 8000,
|
||||
});
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
async getModPreferences(mod) {
|
||||
const modPath = PathUtils.join(this.modsRootPath, mod.id, 'preferences.json');
|
||||
|
||||
if (!(await IOUtils.exists(modPath)) || !mod.preferences) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const preferences = await IOUtils.readJSON(modPath);
|
||||
|
||||
return preferences.filter(({ disabledOn = [] }) => {
|
||||
return !disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`[ZenMods]: Error reading mod preferences for ${mod.name}:`, e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
await SessionStore.promiseInitialized;
|
||||
|
||||
if (
|
||||
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
||||
Services.appinfo.inSafeMode
|
||||
) {
|
||||
console.log('[ZenMods]: Mods disabled by user or in safe mode.');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.getMods(); // Check for any errors in the themes data file
|
||||
const mods = await this.#getEnabledMods();
|
||||
|
||||
const modsWithPreferences = await Promise.all(
|
||||
mods.map(async (mod) => {
|
||||
const preferences = await this.getModPreferences(mod);
|
||||
|
||||
return {
|
||||
name: mod.name,
|
||||
enabled: mod.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.#writeToDom(modsWithPreferences);
|
||||
|
||||
await this.#insertStylesheet();
|
||||
|
||||
this.#setNewMilestoneIfNeeded();
|
||||
if (this.#shouldAutoUpdate()) {
|
||||
requestIdleCallback(
|
||||
() => {
|
||||
if (!window.closed) {
|
||||
requestAnimationFrame(() => {
|
||||
this.checkForModsUpdates();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ timeout: 1000 }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error loading Zen Mods:', e);
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(this.updatePref, this.#rebuildModsStylesheet.bind(this), false);
|
||||
Services.prefs.addObserver(
|
||||
'zen.themes.disable-all',
|
||||
this.#handleDisableMods.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#setNewMilestoneIfNeeded() {
|
||||
const previousMilestone = Services.prefs.getStringPref('zen.mods.milestone', '');
|
||||
if (previousMilestone != Services.appinfo.version) {
|
||||
Services.prefs.setStringPref('zen.mods.milestone', Services.appinfo.version);
|
||||
Services.prefs.clearUserPref('zen.mods.last-update');
|
||||
}
|
||||
}
|
||||
|
||||
#shouldAutoUpdate() {
|
||||
const daysBeforeUpdate = Services.prefs.getIntPref('zen.mods.auto-update-days');
|
||||
const lastUpdatedSec = Services.prefs.getIntPref('zen.mods.last-update', -1);
|
||||
const nowSec = Math.floor(Date.now() / 1000);
|
||||
const daysSinceUpdate = (nowSec - lastUpdatedSec) / (60 * 60 * 24);
|
||||
|
||||
return (
|
||||
(Services.prefs.getBoolPref('zen.mods.auto-update', true) &&
|
||||
daysSinceUpdate >= daysBeforeUpdate) ||
|
||||
lastUpdatedSec < 0
|
||||
);
|
||||
}
|
||||
|
||||
async checkForModsUpdates() {
|
||||
const mods = await this.getMods();
|
||||
|
||||
const updates = await Promise.all(
|
||||
Object.values(mods).map(async (currentMod) => {
|
||||
try {
|
||||
const possibleNewModVersion = await this.requestMod(currentMod.id);
|
||||
|
||||
if (!possibleNewModVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.#compareVersions(
|
||||
possibleNewModVersion.version,
|
||||
currentMod.version ?? '0.0.0'
|
||||
) &&
|
||||
possibleNewModVersion.version != currentMod.version
|
||||
) {
|
||||
console.log(
|
||||
`[ZenMods]: Mod update found for mod ${currentMod.name} (${currentMod.id}), current: ${currentMod.version}, new: ${possibleNewModVersion.version}`
|
||||
);
|
||||
|
||||
possibleNewModVersion.enabled = currentMod.enabled;
|
||||
|
||||
await this.removeMod(currentMod.id, false);
|
||||
|
||||
mods[currentMod.id] = possibleNewModVersion;
|
||||
|
||||
return possibleNewModVersion;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error checking for mod updates', e);
|
||||
|
||||
return null;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
await this.updateMods(mods);
|
||||
Services.prefs.setIntPref('zen.mods.last-update', Math.floor(Date.now() / 1000));
|
||||
return updates.filter((update) => {
|
||||
return update !== null;
|
||||
});
|
||||
}
|
||||
|
||||
async removeMod(modId, triggerUpdate = true) {
|
||||
const modPath = this.getModFolder(modId);
|
||||
|
||||
console.log(`[ZenMods]: Removing mod ${modPath}`);
|
||||
|
||||
await IOUtils.remove(modPath, { recursive: true, ignoreAbsent: true });
|
||||
|
||||
const mods = await this.getMods();
|
||||
|
||||
delete mods[modId];
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
|
||||
if (triggerUpdate) {
|
||||
this.triggerModsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
async enableMod(modId) {
|
||||
const mods = await this.getMods();
|
||||
const mod = mods[modId];
|
||||
|
||||
console.log(`[ZenMods]: Enabling mod ${mod.name}`);
|
||||
|
||||
mod.enabled = true;
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
}
|
||||
|
||||
async disableMod(modId) {
|
||||
const mods = await this.getMods();
|
||||
const mod = mods[modId];
|
||||
|
||||
console.log(`[ZenMods]: Disabling mod ${mod.name}`);
|
||||
|
||||
mod.enabled = false;
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
}
|
||||
|
||||
async updateMods(mods = undefined) {
|
||||
if (!mods) {
|
||||
mods = await this.getMods();
|
||||
}
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
await this.checkForModChanges();
|
||||
}
|
||||
|
||||
triggerModsUpdate() {
|
||||
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
|
||||
}
|
||||
|
||||
async installMod(mod) {
|
||||
try {
|
||||
const modPath = PathUtils.join(this.modsRootPath, mod.id);
|
||||
await IOUtils.makeDirectory(modPath, { ignoreExisting: true });
|
||||
|
||||
await this.#downloadUrlToFile(mod.style, PathUtils.join(modPath, 'chrome.css'), true);
|
||||
await this.#downloadUrlToFile(mod.readme, PathUtils.join(modPath, 'readme.md'));
|
||||
|
||||
if (mod.preferences) {
|
||||
await this.#downloadUrlToFile(
|
||||
mod.preferences,
|
||||
PathUtils.join(modPath, 'preferences.json')
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error installing mod', mod.id, e);
|
||||
}
|
||||
}
|
||||
|
||||
async checkForModChanges() {
|
||||
const mods = await this.getMods();
|
||||
|
||||
for (const [modId, mod] of Object.entries(mods)) {
|
||||
try {
|
||||
if (!mod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(await IOUtils.exists(this.getModFolder(modId)))) {
|
||||
await this.installMod(mod);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error checking for mod changes', e);
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerModsUpdate();
|
||||
}
|
||||
|
||||
async requestMod(modId) {
|
||||
const url = this.#composeModApiUrl(modId);
|
||||
|
||||
console.debug(`[ZenMods]: Fetching mod ${modId} info from ${url}`);
|
||||
|
||||
const data = await fetch(url, {
|
||||
mode: 'no-cors',
|
||||
});
|
||||
|
||||
if (data.ok) {
|
||||
try {
|
||||
const obj = await data.json();
|
||||
|
||||
return obj;
|
||||
} catch (e) {
|
||||
console.error(`[ZenMods]: Error parsing mod ${modId} info:`, e);
|
||||
}
|
||||
} else {
|
||||
console.error(`[ZenMods]: Error fetching mod ${modId} info:`, data.status);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async isModInstalled(modId) {
|
||||
const mods = await this.getMods();
|
||||
return Boolean(mods?.[modId]);
|
||||
}
|
||||
|
||||
// public properties end
|
||||
}
|
||||
|
||||
window.gZenMods = new ZenMods();
|
||||
|
||||
gZenActorsManager.addJSWindowActor('ZenModsMarketplace', {
|
||||
parent: {
|
||||
esModuleURI: 'resource:///actors/ZenModsMarketplaceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'resource:///actors/ZenModsMarketplaceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
},
|
||||
},
|
||||
matches: [
|
||||
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
||||
'about:preferences',
|
||||
],
|
||||
});
|
||||
}
|
138
src/zen/mods/ZenThemesCommon.mjs
Normal file
138
src/zen/mods/ZenThemesCommon.mjs
Normal file
|
@ -0,0 +1,138 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
var ZenThemesCommon = {
|
||||
kZenColors: [
|
||||
'#aac7ff',
|
||||
'#74d7cb',
|
||||
'#a0d490',
|
||||
'#dec663',
|
||||
'#ffb787',
|
||||
'#dec1b1',
|
||||
'#ffb1c0',
|
||||
'#ddbfc3',
|
||||
'#f6b0ea',
|
||||
'#d4bbff',
|
||||
],
|
||||
|
||||
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');
|
||||
},
|
||||
|
||||
get themesDataFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||
},
|
||||
|
||||
getThemeFolder(themeId) {
|
||||
return PathUtils.join(this.themesRootPath, themeId);
|
||||
},
|
||||
|
||||
async getThemes() {
|
||||
if (!(await IOUtils.exists(this.themesDataFile))) {
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
}
|
||||
|
||||
let themes = {};
|
||||
try {
|
||||
themes = await IOUtils.readJSON(this.themesDataFile);
|
||||
if (themes === null || typeof themes !== 'object') {
|
||||
throw new Error('Themes data file is null');
|
||||
}
|
||||
} catch {
|
||||
// If we have a corrupted file, reset it
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
|
||||
Services.wm
|
||||
.getMostRecentWindow('navigator:browser')
|
||||
.gZenUIManager.showToast('zen-themes-corrupted', {
|
||||
timeout: 8000,
|
||||
});
|
||||
}
|
||||
return themes;
|
||||
},
|
||||
|
||||
async getThemePreferences(theme) {
|
||||
const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json');
|
||||
|
||||
if (!(await IOUtils.exists(themePath)) || !theme.preferences) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
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-Za-z0-9-_.]+)/g.exec(entry);
|
||||
const isNegation = negation === '!';
|
||||
|
||||
if (
|
||||
(isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) ||
|
||||
(os !== '' &&
|
||||
os !== gZenOperatingSystemCommonUtils.currentOperatingSystem &&
|
||||
!isNegation)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newThemePreferences.push({
|
||||
property,
|
||||
label,
|
||||
type: 'checkbox',
|
||||
disabledOn: os !== '' ? [os] : [],
|
||||
});
|
||||
}
|
||||
|
||||
return newThemePreferences;
|
||||
}
|
||||
|
||||
return preferences.filter(
|
||||
({ disabledOn = [] }) =>
|
||||
!disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(`[ZenThemes]: Error reading preferences for ${theme.name}:`, e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
throttle(mainFunction, delay) {
|
||||
let timerFlag = null;
|
||||
|
||||
return (...args) => {
|
||||
if (timerFlag === null) {
|
||||
mainFunction(...args);
|
||||
timerFlag = setTimeout(() => {
|
||||
timerFlag = null;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
debounce(mainFunction, wait) {
|
||||
let timerFlag;
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timerFlag);
|
||||
timerFlag = setTimeout(() => {
|
||||
mainFunction(...args);
|
||||
}, wait);
|
||||
};
|
||||
},
|
||||
};
|
335
src/zen/mods/ZenThemesImporter.mjs
Normal file
335
src/zen/mods/ZenThemesImporter.mjs
Normal file
|
@ -0,0 +1,335 @@
|
|||
const kZenStylesheetThemeHeader = '/* Zen Themes - Generated by ZenThemesImporter.';
|
||||
const kZenStylesheetThemeHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
||||
* Your changes will be overwritten.
|
||||
* Instead, go to the preferences and edit the themes there.
|
||||
*/
|
||||
`;
|
||||
const kenStylesheetFooter = `
|
||||
/* End of Zen Themes */
|
||||
`;
|
||||
const getCurrentDateTime = () =>
|
||||
new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'full',
|
||||
}).format(new Date().getTime());
|
||||
|
||||
var gZenStylesheetManager = {
|
||||
async writeStylesheet(path, themes) {
|
||||
let content = kZenStylesheetThemeHeader;
|
||||
content += `\n* FILE GENERATED AT: ${getCurrentDateTime()}\n`;
|
||||
content += kZenStylesheetThemeHeaderBody;
|
||||
|
||||
for (let theme of themes) {
|
||||
if (theme.enabled !== undefined && !theme.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
content += this.getThemeCSS(theme);
|
||||
}
|
||||
|
||||
content += kenStylesheetFooter;
|
||||
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
|
||||
await IOUtils.write(path, buffer);
|
||||
},
|
||||
|
||||
getThemeCSS(theme) {
|
||||
let css = '\n';
|
||||
|
||||
css += `/* Name: ${theme.name} */\n`;
|
||||
css += `/* Description: ${theme.description} */\n`;
|
||||
css += `/* Author: @${theme.author} */\n`;
|
||||
|
||||
if (theme._readmeURL) {
|
||||
css += `/* Readme: ${theme.readme} */\n`;
|
||||
}
|
||||
|
||||
css += `@import url("${theme._chromeURL}");\n`;
|
||||
|
||||
return css;
|
||||
},
|
||||
};
|
||||
|
||||
var gZenThemesImporter = new (class {
|
||||
constructor() {
|
||||
try {
|
||||
window.SessionStore.promiseInitialized.then(async () => {
|
||||
if (
|
||||
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
||||
Services.appinfo.inSafeMode
|
||||
) {
|
||||
console.log('[ZenThemesImporter]: Disabling all themes.');
|
||||
return;
|
||||
}
|
||||
|
||||
await ZenThemesCommon.getThemes(); // Check for any errors in the themes data file
|
||||
const themes = await this.getEnabledThemes();
|
||||
|
||||
const themesWithPreferences = await Promise.all(
|
||||
themes.map(async (theme) => {
|
||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
||||
|
||||
return {
|
||||
name: theme.name,
|
||||
enabled: theme.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.writeToDom(themesWithPreferences);
|
||||
|
||||
await this.insertStylesheet();
|
||||
});
|
||||
console.info('[ZenThemesImporter]: Zen Themes imported');
|
||||
} catch (e) {
|
||||
console.error('[ZenThemesImporter]: Error importing Zen Themes: ', e);
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(
|
||||
'zen.themes.updated-value-observer',
|
||||
this.rebuildThemeStylesheet.bind(this),
|
||||
false
|
||||
);
|
||||
Services.prefs.addObserver(
|
||||
'zen.themes.disable-all',
|
||||
this.handleDisableThemes.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
get sss() {
|
||||
if (!this._sss) {
|
||||
this._sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
||||
Ci.nsIStyleSheetService
|
||||
);
|
||||
}
|
||||
return this._sss;
|
||||
}
|
||||
|
||||
get styleSheetPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
||||
}
|
||||
|
||||
async handleDisableThemes() {
|
||||
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
||||
console.log('[ZenThemesImporter]: Disabling themes module.');
|
||||
|
||||
await this.removeStylesheet();
|
||||
} else {
|
||||
console.log('[ZenThemesImporter]: Enabling themes module.');
|
||||
|
||||
await this.rebuildThemeStylesheet();
|
||||
}
|
||||
}
|
||||
|
||||
get styleSheetURI() {
|
||||
if (!this._styleSheetURI) {
|
||||
this._styleSheetURI = Services.io.newFileURI(new FileUtils.File(this.styleSheetPath));
|
||||
}
|
||||
return this._styleSheetURI;
|
||||
}
|
||||
|
||||
getStylesheetURIForTheme(theme) {
|
||||
return Services.io.newFileURI(
|
||||
new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))
|
||||
);
|
||||
}
|
||||
|
||||
async insertStylesheet() {
|
||||
if (await IOUtils.exists(this.styleSheetPath)) {
|
||||
await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
||||
}
|
||||
|
||||
if (this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET)) {
|
||||
console.debug('[ZenThemesImporter]: Sheet successfully registered');
|
||||
}
|
||||
}
|
||||
|
||||
async removeStylesheet() {
|
||||
await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
||||
const rv = this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET);
|
||||
await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });
|
||||
|
||||
if (!rv && !(await IOUtils.exists(this.styleSheetPath))) {
|
||||
console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
|
||||
}
|
||||
}
|
||||
|
||||
async rebuildThemeStylesheet() {
|
||||
await this.removeStylesheet();
|
||||
|
||||
const themes = await this.getEnabledThemes();
|
||||
|
||||
await this.writeStylesheet(themes);
|
||||
|
||||
const themesWithPreferences = await Promise.all(
|
||||
themes.map(async (theme) => {
|
||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
||||
|
||||
return {
|
||||
name: theme.name,
|
||||
enabled: theme.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.setDefaults(themesWithPreferences);
|
||||
this.writeToDom(themesWithPreferences);
|
||||
|
||||
await this.insertStylesheet();
|
||||
}
|
||||
|
||||
async getEnabledThemes() {
|
||||
const themeObject = await ZenThemesCommon.getThemes();
|
||||
const themes = Object.values(themeObject).filter(
|
||||
(theme) => theme.enabled === undefined || theme.enabled
|
||||
);
|
||||
|
||||
const themeList = themes.map(({ name }) => name).join(', ');
|
||||
|
||||
const message =
|
||||
themeList !== ''
|
||||
? `[ZenThemesImporter]: Loading enabled Zen themes: ${themeList}.`
|
||||
: '[ZenThemesImporter]: No enabled Zen themes.';
|
||||
|
||||
console.log(message);
|
||||
|
||||
return themes;
|
||||
}
|
||||
|
||||
setDefaults(themesWithPreferences) {
|
||||
for (const { preferences, enabled } of themesWithPreferences) {
|
||||
if (enabled !== undefined && !enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { type, property, defaultValue } of preferences) {
|
||||
if (defaultValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === '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);
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||
for (const { enabled, preferences, name } of themesWithPreferences) {
|
||||
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||
|
||||
if (enabled !== undefined && !enabled) {
|
||||
const element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { property, type } of preferences) {
|
||||
const value = Services.prefs.getStringPref(property, '');
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
switch (type) {
|
||||
case 'dropdown': {
|
||||
if (value !== '') {
|
||||
let element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (!element) {
|
||||
element = browser.document.createElement('div');
|
||||
|
||||
element.style.display = 'none';
|
||||
element.setAttribute('id', sanitizedName);
|
||||
|
||||
browser.document.body.appendChild(element);
|
||||
}
|
||||
|
||||
element.setAttribute(sanitizedProperty, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'string': {
|
||||
if (value === '') {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.removeProperty(`--${sanitizedProperty}`);
|
||||
} else {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.setProperty(`--${sanitizedProperty}`, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async writeStylesheet(themeList = []) {
|
||||
const themes = [];
|
||||
|
||||
for (let theme of themeList) {
|
||||
theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
|
||||
themes.push(theme);
|
||||
}
|
||||
|
||||
await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
|
||||
}
|
||||
})();
|
||||
|
||||
gZenActorsManager.addJSWindowActor('ZenThemeMarketplace', {
|
||||
parent: {
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
},
|
||||
},
|
||||
matches: [
|
||||
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
||||
'about:preferences',
|
||||
],
|
||||
});
|
|
@ -1,142 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenModsMarketplaceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type === 'DOMContentLoaded') {
|
||||
const verifier = this.contentWindow.document.querySelector(
|
||||
'meta[name="zen-content-verified"]'
|
||||
);
|
||||
|
||||
if (verifier) {
|
||||
verifier.setAttribute('content', 'verified');
|
||||
}
|
||||
|
||||
this.initiateModsMarketplace();
|
||||
|
||||
this.contentWindow.document.addEventListener(
|
||||
'ZenCheckForModUpdates',
|
||||
this.checkForModUpdates.bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This function will be called from about:preferences
|
||||
checkForModUpdates(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdates');
|
||||
}
|
||||
|
||||
initiateModsMarketplace() {
|
||||
this.contentWindow.setTimeout(() => {
|
||||
this.addButtons();
|
||||
this.injectMarketplaceAPI();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get actionButton() {
|
||||
return this.contentWindow.document.getElementById('install-theme');
|
||||
}
|
||||
|
||||
get actionButtonUninstall() {
|
||||
return this.contentWindow.document.getElementById('install-theme-uninstall');
|
||||
}
|
||||
|
||||
async isThemeInstalled(themeId) {
|
||||
return await this.sendQuery('ZenModsMarketplace:IsModInstalled', { themeId });
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenModsMarketplace:ModChanged': {
|
||||
const modId = message.data.modId;
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonInstalled = this.actionButtonUninstall;
|
||||
|
||||
if (actionButton && actionButtonInstalled) {
|
||||
actionButton.disabled = false;
|
||||
actionButtonInstalled.disabled = false;
|
||||
|
||||
if (await this.isThemeInstalled(modId)) {
|
||||
actionButton.classList.add('hidden');
|
||||
actionButtonInstalled.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
actionButtonInstalled.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenModsMarketplace:CheckForUpdatesFinished': {
|
||||
const updates = message.data.updates;
|
||||
|
||||
this.contentWindow.document.dispatchEvent(
|
||||
new CustomEvent('ZenModsMarketplace:CheckForUpdatesFinished', { detail: { updates } })
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
injectMarketplaceAPI() {
|
||||
Cu.exportFunction(this.handleModInstallationEvent.bind(this), this.contentWindow, {
|
||||
defineAs: 'ZenInstallMod',
|
||||
});
|
||||
}
|
||||
|
||||
async addButtons() {
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonUninstall = this.actionButtonUninstall;
|
||||
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
|
||||
if (!actionButton || !actionButtonUninstall) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessage.classList.add('hidden');
|
||||
|
||||
const themeId = actionButton.getAttribute('zen-theme-id');
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButtonUninstall.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
}
|
||||
|
||||
actionButton.addEventListener('click', this.handleModInstallationEvent.bind(this));
|
||||
actionButtonUninstall.addEventListener('click', this.handleModUninstallEvent.bind(this));
|
||||
}
|
||||
|
||||
async handleModUninstallEvent(event) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
|
||||
const modId = button.getAttribute('zen-theme-id');
|
||||
|
||||
this.sendAsyncMessage('ZenModsMarketplace:UninstallMod', { modId });
|
||||
}
|
||||
|
||||
async handleModInstallationEvent(event) {
|
||||
// Object can be an event or a theme id
|
||||
let modId;
|
||||
|
||||
if (event.target) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
|
||||
modId = button.getAttribute('zen-theme-id');
|
||||
} else {
|
||||
// Backwards compatibility is... Interesting
|
||||
modId = event.themeId ?? event.modId ?? event.id;
|
||||
}
|
||||
|
||||
this.sendAsyncMessage('ZenModsMarketplace:InstallMod', { modId });
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenModsMarketplaceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
get modsManager() {
|
||||
return this.browsingContext.topChromeWindow.gZenMods;
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenModsMarketplace:InstallMod': {
|
||||
const modId = message.data.modId;
|
||||
const mod = await this.modsManager.requestMod(modId);
|
||||
|
||||
console.log(`[ZenModsMarketplaceParent]: Installing mod ${mod.id}`);
|
||||
|
||||
mod.enabled = true;
|
||||
|
||||
const mods = await this.modsManager.getMods();
|
||||
mods[mod.id] = mod;
|
||||
|
||||
await this.modsManager.updateMods(mods);
|
||||
await this.updateChildProcesses(mod.id);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ZenModsMarketplace:UninstallMod': {
|
||||
const modId = message.data.modId;
|
||||
console.log(`[ZenModsMarketplaceParent]: Uninstalling mod ${modId}`);
|
||||
|
||||
const mods = await this.modsManager.getMods();
|
||||
|
||||
delete mods[modId];
|
||||
|
||||
await this.modsManager.removeMod(modId);
|
||||
await this.modsManager.updateMods(mods);
|
||||
|
||||
await this.updateChildProcesses(modId);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ZenModsMarketplace:CheckForUpdates': {
|
||||
const updates = await this.modsManager.checkForModsUpdates();
|
||||
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdatesFinished', { updates });
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenModsMarketplace:IsModInstalled': {
|
||||
const themeId = message.data.themeId;
|
||||
const themes = await this.modsManager.getMods();
|
||||
|
||||
return Boolean(themes?.[themeId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateChildProcesses(modId) {
|
||||
this.sendAsyncMessage('ZenModsMarketplace:ModChanged', { modId });
|
||||
}
|
||||
}
|
200
src/zen/mods/actors/ZenThemeMarketplaceChild.sys.mjs
Normal file
200
src/zen/mods/actors/ZenThemeMarketplaceChild.sys.mjs
Normal file
|
@ -0,0 +1,200 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenThemeMarketplaceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'DOMContentLoaded':
|
||||
this.initalizeZenAPI(event);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
initalizeZenAPI(event) {
|
||||
const verifier = this.contentWindow.document.querySelector('meta[name="zen-content-verified"]');
|
||||
|
||||
if (verifier) {
|
||||
verifier.setAttribute('content', 'verified');
|
||||
}
|
||||
|
||||
const possibleRicePage = this.collectRiceMetadata();
|
||||
|
||||
if (possibleRicePage?.id) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:RicePage', possibleRicePage);
|
||||
return;
|
||||
}
|
||||
|
||||
this.initiateThemeMarketplace();
|
||||
this.contentWindow.document.addEventListener(
|
||||
'ZenCheckForThemeUpdates',
|
||||
this.checkForThemeUpdates.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
collectRiceMetadata() {
|
||||
const meta = this.contentWindow.document.querySelector('meta[name="zen-rice-data"]');
|
||||
if (meta) {
|
||||
return {
|
||||
id: meta.getAttribute('data-id'),
|
||||
name: meta.getAttribute('data-name'),
|
||||
author: meta.getAttribute('data-author'),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This function will be called from about:preferences
|
||||
checkForThemeUpdates(event) {
|
||||
event.preventDefault();
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdates');
|
||||
}
|
||||
|
||||
initiateThemeMarketplace() {
|
||||
this.contentWindow.setTimeout(() => {
|
||||
this.addInstallButtons();
|
||||
this.injectMarketplaceAPI();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get actionButton() {
|
||||
return this.contentWindow.document.getElementById('install-theme');
|
||||
}
|
||||
|
||||
get actionButtonUninstall() {
|
||||
return this.contentWindow.document.getElementById('install-theme-uninstall');
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenThemeMarketplace:ThemeChanged': {
|
||||
const themeId = message.data.themeId;
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonInstalled = this.actionButtonUninstall;
|
||||
|
||||
if (actionButton && actionButtonInstalled) {
|
||||
actionButton.disabled = false;
|
||||
actionButtonInstalled.disabled = false;
|
||||
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButton.classList.add('hidden');
|
||||
actionButtonInstalled.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
actionButtonInstalled.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenThemeMarketplace:CheckForUpdatesFinished': {
|
||||
const updates = message.data.updates;
|
||||
|
||||
this.contentWindow.document.dispatchEvent(
|
||||
new CustomEvent('ZenThemeMarketplace:CheckForUpdatesFinished', { detail: { updates } })
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenThemeMarketplace:GetThemeInfo': {
|
||||
const themeId = message.data.themeId;
|
||||
const theme = await this.getThemeInfo(themeId);
|
||||
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
injectMarketplaceAPI() {
|
||||
Cu.exportFunction(this.installTheme.bind(this), this.contentWindow, {
|
||||
defineAs: 'ZenInstallTheme',
|
||||
});
|
||||
}
|
||||
|
||||
async addInstallButtons() {
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonUninstall = this.actionButtonUninstall;
|
||||
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
|
||||
if (!actionButton || !actionButtonUninstall) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessage.classList.add('hidden');
|
||||
|
||||
const themeId = actionButton.getAttribute('zen-theme-id');
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButtonUninstall.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
}
|
||||
|
||||
actionButton.addEventListener('click', this.installTheme.bind(this));
|
||||
actionButtonUninstall.addEventListener('click', this.uninstallTheme.bind(this));
|
||||
}
|
||||
|
||||
async isThemeInstalled(themeId) {
|
||||
return await this.sendQuery('ZenThemeMarketplace:IsThemeInstalled', { themeId });
|
||||
}
|
||||
|
||||
addTheme(theme) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:InstallTheme', { theme });
|
||||
}
|
||||
|
||||
getThemeAPIUrl(themeId) {
|
||||
return `https://zen-browser.github.io/theme-store/themes/${themeId}/theme.json`;
|
||||
}
|
||||
|
||||
async getThemeInfo(themeId) {
|
||||
const url = this.getThemeAPIUrl(themeId);
|
||||
const data = await fetch(url, {
|
||||
mode: 'no-cors',
|
||||
});
|
||||
|
||||
if (data.ok) {
|
||||
try {
|
||||
const obj = await data.json();
|
||||
return obj;
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplace: Error parsing theme info: ', e);
|
||||
}
|
||||
} else {
|
||||
console.error('ZenThemeMarketplace: Error fetching theme info: ', data.status);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async uninstallTheme(event) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
const themeId = button.getAttribute('zen-theme-id');
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:UninstallTheme', { themeId });
|
||||
}
|
||||
|
||||
async installTheme(object) {
|
||||
// Object can be an event or a theme id
|
||||
let themeId;
|
||||
if (object.target) {
|
||||
const button = object.target;
|
||||
button.disabled = true;
|
||||
themeId = button.getAttribute('zen-theme-id');
|
||||
} else {
|
||||
themeId = object.themeId;
|
||||
}
|
||||
console.info('ZenThemeMarketplace: Installing theme with id: ', themeId);
|
||||
|
||||
const theme = await this.getThemeInfo(themeId);
|
||||
if (!theme) {
|
||||
console.error('ZenThemeMarketplace: Error fetching theme info');
|
||||
return;
|
||||
}
|
||||
this.addTheme(theme);
|
||||
}
|
||||
}
|
256
src/zen/mods/actors/ZenThemeMarketplaceParent.sys.mjs
Normal file
256
src/zen/mods/actors/ZenThemeMarketplaceParent.sys.mjs
Normal file
|
@ -0,0 +1,256 @@
|
|||
export class ZenThemeMarketplaceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
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);
|
||||
this.updateChildProcesses(theme.id);
|
||||
break;
|
||||
}
|
||||
case 'ZenThemeMarketplace:UninstallTheme': {
|
||||
console.info('ZenThemeMarketplaceParent: Uninstalling theme');
|
||||
const themeId = message.data.themeId;
|
||||
const themes = await this.getThemes();
|
||||
delete themes[themeId];
|
||||
this.removeTheme(themeId);
|
||||
this.updateThemes(themes);
|
||||
this.updateChildProcesses(themeId);
|
||||
break;
|
||||
}
|
||||
case 'ZenThemeMarketplace:IsThemeInstalled': {
|
||||
const themeId = message.data.themeId;
|
||||
const themes = await this.getThemes();
|
||||
|
||||
return Boolean(themes?.[themeId]);
|
||||
}
|
||||
case 'ZenThemeMarketplace:CheckForUpdates': {
|
||||
this.checkForThemeUpdates();
|
||||
break;
|
||||
}
|
||||
case 'ZenThemeMarketplace:RicePage': {
|
||||
this.openRicePage(this.browsingContext.topChromeWindow, message.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openRicePage(window, data) {
|
||||
window.gZenThemePicker.riceManager.openRicePage(data);
|
||||
}
|
||||
|
||||
compareVersions(version1, version2) {
|
||||
let result = false;
|
||||
|
||||
if (typeof version1 !== 'object') {
|
||||
version1 = version1.toString().split('.');
|
||||
}
|
||||
|
||||
if (typeof version2 !== 'object') {
|
||||
version2 = version2.toString().split('.');
|
||||
}
|
||||
|
||||
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
|
||||
if (version1[i] == undefined) {
|
||||
version1[i] = 0;
|
||||
}
|
||||
if (version2[i] == undefined) {
|
||||
version2[i] = 0;
|
||||
}
|
||||
if (Number(version1[i]) < Number(version2[i])) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
if (version1[i] != version2[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async checkForThemeUpdates() {
|
||||
console.info('ZenThemeMarketplaceParent: Checking for theme updates');
|
||||
|
||||
let updates = [];
|
||||
const themes = await this.getThemes();
|
||||
for (const theme of Object.values(themes)) {
|
||||
try {
|
||||
const themeInfo = await this.sendQuery('ZenThemeMarketplace:GetThemeInfo', {
|
||||
themeId: theme.id,
|
||||
});
|
||||
|
||||
if (!themeInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.compareVersions(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);
|
||||
themes[themeInfo.id] = themeInfo;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error checking for theme updates', e);
|
||||
}
|
||||
}
|
||||
|
||||
await this.updateThemes(themes);
|
||||
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdatesFinished', { updates });
|
||||
}
|
||||
|
||||
async updateChildProcesses(themeId) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:ThemeChanged', { themeId });
|
||||
}
|
||||
|
||||
async getThemes() {
|
||||
return await IOUtils.readJSON(this.themesDataFile);
|
||||
}
|
||||
|
||||
async updateThemes(themes = undefined) {
|
||||
if (!themes) {
|
||||
themes = await this.getThemes();
|
||||
}
|
||||
await IOUtils.writeJSON(this.themesDataFile, themes);
|
||||
await this.checkForThemeChanges();
|
||||
}
|
||||
|
||||
getStyleSheetFullContent(style = '') {
|
||||
let stylesheet = '@-moz-document url-prefix("chrome:") {\n';
|
||||
|
||||
for (const line of style.split('\n')) {
|
||||
stylesheet += ` ${line}\n`;
|
||||
}
|
||||
|
||||
stylesheet += '}';
|
||||
|
||||
return stylesheet;
|
||||
}
|
||||
|
||||
async downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`ZenThemeMarketplaceParent: HTTP error! status: ${response.status} for url: ${url}`
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.text();
|
||||
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
|
||||
// convert the data into a Uint8Array
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
await IOUtils.write(path, buffer);
|
||||
|
||||
return;
|
||||
} catch (e) {
|
||||
attempt++;
|
||||
if (attempt >= maxRetries) {
|
||||
console.error('ZenThemeMarketplaceParent: Error downloading file after retries', url, e);
|
||||
} else {
|
||||
console.warn(
|
||||
`ZenThemeMarketplaceParent: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
|
||||
url,
|
||||
e
|
||||
);
|
||||
await new Promise((res) => setTimeout(res, retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async downloadThemeFileContents(theme) {
|
||||
try {
|
||||
const themePath = PathUtils.join(this.themesRootPath, theme.id);
|
||||
await IOUtils.makeDirectory(themePath, { ignoreExisting: true });
|
||||
|
||||
await this.downloadUrlToFile(theme.style, PathUtils.join(themePath, 'chrome.css'), true);
|
||||
await this.downloadUrlToFile(theme.readme, PathUtils.join(themePath, 'readme.md'));
|
||||
|
||||
if (theme.preferences) {
|
||||
await this.downloadUrlToFile(
|
||||
theme.preferences,
|
||||
PathUtils.join(themePath, 'preferences.json')
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('ZenThemeMarketplaceParent: Error downloading theme file contents', theme.id, e);
|
||||
}
|
||||
}
|
||||
|
||||
get themesRootPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
||||
}
|
||||
|
||||
get themesDataFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||
}
|
||||
|
||||
triggerThemeUpdate() {
|
||||
const pref = 'zen.themes.updated-value-observer';
|
||||
Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
|
||||
}
|
||||
|
||||
async installTheme(theme) {
|
||||
try {
|
||||
await this.downloadThemeFileContents(theme);
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error installing theme', theme.id, e);
|
||||
}
|
||||
}
|
||||
|
||||
async checkForThemeChanges() {
|
||||
const themes = await this.getThemes();
|
||||
|
||||
const themeIds = Object.keys(themes);
|
||||
|
||||
for (const themeId of themeIds) {
|
||||
try {
|
||||
const theme = themes[themeId];
|
||||
if (!theme) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
||||
if (!(await IOUtils.exists(themePath))) {
|
||||
await this.installTheme(theme);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error checking for theme changes', e);
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerThemeUpdate();
|
||||
}
|
||||
|
||||
async removeTheme(themeId, triggerUpdate = true) {
|
||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
||||
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
|
||||
|
||||
if (triggerUpdate) {
|
||||
this.triggerThemeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
FINAL_TARGET_FILES.actors += [
|
||||
"actors/ZenModsMarketplaceChild.sys.mjs",
|
||||
"actors/ZenModsMarketplaceParent.sys.mjs",
|
||||
"actors/ZenThemeMarketplaceChild.sys.mjs",
|
||||
"actors/ZenThemeMarketplaceParent.sys.mjs",
|
||||
]
|
||||
|
|
|
@ -104,7 +104,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
);
|
||||
|
||||
window.addEventListener('TabClose', this.handleTabClose.bind(this));
|
||||
window.addEventListener('TabBrowserDiscarded', this.handleTabBrowserDiscarded.bind(this));
|
||||
window.addEventListener('TabSelect', this.onTabSelect.bind(this));
|
||||
this.initializeContextMenu();
|
||||
this.insertIntoContextMenu();
|
||||
|
@ -151,22 +150,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
this.removeTabFromGroup(tab, groupIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event - The event that triggered the tab browser discard.
|
||||
* @description Handles the tab browser discard event.
|
||||
*/
|
||||
async handleTabBrowserDiscarded(event) {
|
||||
const tab = event.target;
|
||||
if (tab.group?.hasAttribute('split-view-group')) {
|
||||
gBrowser.explicitUnloadTabs(tab.group.tabs);
|
||||
for (const t of tab.group.tabs) {
|
||||
if (t.glanceTab) {
|
||||
gBrowser.explicitUnloadTabs([t.glanceTab]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event - The event that triggered the tab select.
|
||||
* @description Handles the tab select event.
|
||||
|
@ -210,10 +193,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
const node = this.getSplitNodeFromTab(tab);
|
||||
const toUpdate = this.removeNode(node);
|
||||
this.applyGridLayout(toUpdate);
|
||||
// Select next tab if the removed tab was selected
|
||||
if (gBrowser.selectedTab === tab) {
|
||||
gBrowser.selectedTab = group.tabs[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -897,9 +876,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
tabCount: window.gBrowser.selectedTabs.length,
|
||||
});
|
||||
document.getElementById('context_zenSplitTabs').setAttribute('data-l10n-args', tabCountInfo);
|
||||
document
|
||||
.getElementById('context_zenSplitTabs')
|
||||
.setAttribute('disabled', !this.contextCanSplitTabs());
|
||||
document.getElementById('context_zenSplitTabs').disabled = !this.contextCanSplitTabs();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -952,8 +929,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
window.gContextMenu.contentData.docLocation ||
|
||||
window.gContextMenu.target.ownerDocument.location.href;
|
||||
const currentTab = gZenGlanceManager.getTabOrGlanceParent(window.gBrowser.selectedTab);
|
||||
const newTab = this.openAndSwitchToTab(url, { inBackground: false });
|
||||
this.splitTabs([currentTab, newTab], undefined, 1);
|
||||
const newTab = this.openAndSwitchToTab(url);
|
||||
this.splitTabs([currentTab, newTab]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -999,6 +976,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
tab = tab.parentNode.closest('.tabbrowser-tab');
|
||||
console.assert(tab, 'Tab not found for zen-glance-tab');
|
||||
}
|
||||
this.updateSplitViewButton(!tab?.splitView);
|
||||
if (tab) {
|
||||
this.updateSplitView(tab);
|
||||
tab.linkedBrowser.docShellIsActive = true;
|
||||
|
@ -1030,7 +1008,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
* Splits the given tabs.
|
||||
*
|
||||
* @param {Tab[]} tabs - The tabs to split.
|
||||
* @param {string|undefined} gridType - The type of grid layout.
|
||||
* @param {string} gridType - The type of grid layout.
|
||||
*/
|
||||
splitTabs(tabs, gridType, initialIndex = 0) {
|
||||
// TODO: Add support for splitting essential tabs
|
||||
|
@ -1096,7 +1074,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
};
|
||||
this._data.push(splitData);
|
||||
if (!this._sessionRestoring) {
|
||||
window.gBrowser.selectedTab = tabs[initialIndex] ?? tabs[0];
|
||||
window.gBrowser.selectedTab = tabs[0];
|
||||
}
|
||||
|
||||
// Add tabs to the split view group
|
||||
|
@ -1132,6 +1110,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
|
||||
if (oldView === newView) return;
|
||||
if (newView < 0 && oldView >= 0) {
|
||||
this.updateSplitViewButton(true);
|
||||
this.deactivateCurrentSplitView();
|
||||
return;
|
||||
}
|
||||
|
@ -1143,7 +1122,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
* Deactivates the split view.
|
||||
*/
|
||||
deactivateCurrentSplitView() {
|
||||
if (this.currentView < 0) return;
|
||||
this.setTabsDocShellState(this._data[this.currentView].tabs, false);
|
||||
for (const tab of this._data[this.currentView].tabs) {
|
||||
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
|
||||
|
@ -1151,9 +1129,9 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
}
|
||||
this.removeSplitters();
|
||||
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
||||
this.updateSplitViewButton(true);
|
||||
this.currentView = -1;
|
||||
this.toggleWrapperDisplay(false);
|
||||
this.maybeDisableOpeningTabOnSplitView();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1175,6 +1153,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
|
||||
this.tabBrowserPanel.setAttribute('zen-split-view', 'true');
|
||||
|
||||
this.updateSplitViewButton(false);
|
||||
this.applyGridToTabs(splitData.tabs);
|
||||
this.applyGridLayout(splitData.layoutTree);
|
||||
this.setTabsDocShellState(splitData.tabs, true);
|
||||
|
@ -1311,7 +1290,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
});
|
||||
}
|
||||
});
|
||||
this.maybeDisableOpeningTabOnSplitView();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1502,6 +1480,20 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
container.style.inset = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the split view button visibility.
|
||||
*
|
||||
* @param {boolean} hidden - Indicates if the button should be hidden.
|
||||
*/
|
||||
updateSplitViewButton(hidden) {
|
||||
const button = document.getElementById('zen-split-views-box');
|
||||
if (hidden) {
|
||||
button?.setAttribute('hidden', 'true');
|
||||
} else {
|
||||
button?.removeAttribute('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI of the panel.
|
||||
*
|
||||
|
@ -1676,15 +1668,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
|
||||
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
||||
gBrowser.moveTabToGroup(draggedTab, splitGroup);
|
||||
if (hoverSide === 'left' || hoverSide === 'top') {
|
||||
try {
|
||||
splitGroup.tabs[0].before(draggedTab);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Failed to move tab ${draggedTab.id} before ${splitGroup.tabs[0].id}: ${e}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
||||
|
@ -1874,26 +1857,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||
this.onLocationChange(gBrowser.selectedTab.linkedBrowser);
|
||||
}
|
||||
}
|
||||
|
||||
maybeDisableOpeningTabOnSplitView() {
|
||||
const shouldBeDisabled = !this.canOpenLinkInSplitView();
|
||||
document
|
||||
.getElementById('cmd_zenSplitViewLinkInNewTab')
|
||||
.setAttribute('disabled', shouldBeDisabled);
|
||||
document.getElementById('zen-glance-sidebar-split').setAttribute('disabled', shouldBeDisabled);
|
||||
}
|
||||
|
||||
canOpenLinkInSplitView() {
|
||||
const currentView = this.currentView;
|
||||
if (currentView < 0) {
|
||||
return true;
|
||||
}
|
||||
const group = this._data[currentView];
|
||||
if (!group || group.tabs.length >= this.MAX_TABS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenViewSplitter = new ZenViewSplitter();
|
||||
|
|
|
@ -41,10 +41,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
#zen-splitview-dropzone {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split='true'] {
|
||||
transition: inset 0.09s ease-out !important;
|
||||
& browser {
|
||||
|
@ -74,9 +70,16 @@
|
|||
margin-right: calc(-1 * var(--zen-split-column-gap));
|
||||
}
|
||||
|
||||
:root:not([customizing]) #zen-splitview-overlay {
|
||||
#tabbrowser-tabpanels:has(> [zen-split='true']),
|
||||
#zen-splitview-overlay {
|
||||
:root:not([zen-compact-mode='true']):not([customizing]) & {
|
||||
@media -moz-pref('zen.view.compact.hide-toolbar') {
|
||||
& {
|
||||
margin-top: calc(var(--zen-split-column-gap) * -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view] {
|
||||
.zen-split-view-splitter {
|
||||
|
@ -109,6 +112,10 @@
|
|||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
#zen-split-views-box:not([hidden='true']) {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.zen-view-splitter-header-container {
|
||||
position: absolute;
|
||||
top: calc(var(--zen-split-column-gap) / -2);
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
const lazy = {};
|
||||
|
||||
|
@ -52,6 +49,8 @@
|
|||
}
|
||||
|
||||
class ZenPinnedTabManager extends ZenDOMOperatedFeature {
|
||||
MAX_ESSENTIALS_TABS = 12;
|
||||
|
||||
async init() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
|
@ -88,14 +87,10 @@
|
|||
|
||||
onTabIconChanged(tab, url = null) {
|
||||
const iconUrl = url ?? tab.iconImage.src;
|
||||
if (!iconUrl && tab.hasAttribute('zen-pin-id')) {
|
||||
if (!iconUrl) {
|
||||
try {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await this.promisePinnedCacheInitialized;
|
||||
const pin = this._pinsCache?.find(
|
||||
(pin) => pin.uuid === tab.getAttribute('zen-pin-id')
|
||||
);
|
||||
let favicon = await PlacesUtils.favicons.getFaviconForPage(
|
||||
Services.io.newURI(pin.url)
|
||||
);
|
||||
|
@ -161,9 +156,6 @@
|
|||
await gZenWorkspaces.promiseSectionsInitialized;
|
||||
await this._initializePinsCache();
|
||||
await this._initializePinnedTabs(init);
|
||||
if (init) {
|
||||
this._resolveInitializedPinnedCache();
|
||||
}
|
||||
}
|
||||
|
||||
async _initializePinsCache() {
|
||||
|
@ -667,7 +659,7 @@
|
|||
for (let i = 0; i < tabs.length; i++) {
|
||||
let tab = tabs[i];
|
||||
const section = gZenWorkspaces.getEssentialsSection(tab);
|
||||
if (!this.canEssentialBeAdded(tab)) {
|
||||
if (section.children.length >= this.MAX_ESSENTIALS_TABS) {
|
||||
movedAll = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -799,7 +791,7 @@
|
|||
document.getElementById('context_zen-add-essential').hidden =
|
||||
contextTab.getAttribute('zen-essential') ||
|
||||
!!contextTab.group ||
|
||||
!this.canEssentialBeAdded(contextTab);
|
||||
gBrowser._numZenEssentials >= this.MAX_ESSENTIALS_TABS;
|
||||
document.getElementById('context_zen-remove-essential').hidden =
|
||||
!contextTab.getAttribute('zen-essential');
|
||||
document.getElementById('context_unpinTab').hidden =
|
||||
|
@ -951,7 +943,7 @@
|
|||
} else {
|
||||
tab.setAttribute('zen-pinned-changed', 'true');
|
||||
}
|
||||
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl.spec})`);
|
||||
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl})`);
|
||||
}
|
||||
|
||||
removeTabContainersDragoverClass() {
|
||||
|
@ -1012,16 +1004,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
canEssentialBeAdded(tab) {
|
||||
return (
|
||||
!(
|
||||
(tab.getAttribute('usercontextid') || 0) !=
|
||||
gZenWorkspaces.getActiveWorkspaceFromCache().containerTabId &&
|
||||
gZenWorkspaces.containerSpecificEssentials
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
applyDragoverClass(event, draggedTab) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
|
@ -1055,7 +1037,10 @@
|
|||
shouldAddDragOverElement = true;
|
||||
}
|
||||
} else if (essentialTabsTarget) {
|
||||
if (!draggedTab.hasAttribute('zen-essential') && this.canEssentialBeAdded(draggedTab)) {
|
||||
if (
|
||||
!draggedTab.hasAttribute('zen-essential') &&
|
||||
gBrowser._numZenEssentials < this.MAX_ESSENTIALS_TABS
|
||||
) {
|
||||
shouldAddDragOverElement = true;
|
||||
isVertical = false;
|
||||
}
|
||||
|
@ -1132,8 +1117,4 @@
|
|||
}
|
||||
|
||||
window.gZenPinnedTabManager = new ZenPinnedTabManager();
|
||||
|
||||
gZenPinnedTabManager.promisePinnedCacheInitialized = new Promise((resolve) => {
|
||||
gZenPinnedTabManager._resolveInitializedPinnedCache = resolve;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
var ZenPinnedTabsStorage = {
|
||||
async init() {
|
||||
await this._ensureTable();
|
||||
|
@ -109,8 +106,8 @@ var ZenPinnedTabsStorage = {
|
|||
`
|
||||
INSERT OR REPLACE INTO zen_pins (
|
||||
uuid, title, url, container_id, workspace_uuid, position,
|
||||
is_essential, is_group, parent_uuid, edited_title, created_at,
|
||||
updated_at
|
||||
is_essential, is_group, parent_uuid, created_at, updated_at,
|
||||
edited_title
|
||||
) VALUES (
|
||||
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
|
||||
:is_essential, :is_group, :parent_uuid, :edited_title,
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
height: var(--zen-toolbar-height);
|
||||
z-index: 1;
|
||||
|
||||
|
|
|
@ -356,7 +356,6 @@
|
|||
margin-block: 0 !important;
|
||||
margin-inline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: calc(var(--border-radius-medium) - 2px);
|
||||
}
|
||||
/* Adjust padding for content */
|
||||
& .tab-content {
|
||||
|
@ -375,27 +374,6 @@
|
|||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
& .tab-audio-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&[soundplaying]:not([muted]) .tab-icon-stack::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 110%;
|
||||
height: 110%;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 1;
|
||||
background: url('chrome://browser/content/zen-images/note-indicator.svg') no-repeat;
|
||||
top: -70%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.8s ease;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Essentials Glance Tab Specifics (Floating) --- */
|
||||
|
@ -731,6 +709,7 @@
|
|||
|
||||
/* Hide text labels */
|
||||
& .zen-current-workspace-indicator-name,
|
||||
& .zen-workspace-actions,
|
||||
& zen-workspace .toolbarbutton-text {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -1261,7 +1240,7 @@
|
|||
|
||||
padding-bottom: var(--zen-toolbox-padding);
|
||||
overflow: hidden;
|
||||
gap: 4px;
|
||||
gap: calc(var(--zen-toolbox-padding) - 2px);
|
||||
transition:
|
||||
max-height 0.3s ease-out,
|
||||
grid-template-columns 0.3s ease-out;
|
||||
|
|
|
@ -9,8 +9,8 @@ function openGlanceOnTab(callback, close = true) {
|
|||
gZenGlanceManager
|
||||
.openGlance({
|
||||
url: 'https://example.com',
|
||||
clientX: 0,
|
||||
clientY: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
})
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"compact_mode/browser.toml",
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
SOURCES += [
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIZenCommonUtils.idl",
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
SOURCES += [
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
"common",
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
|
||||
initContextMenu() {
|
||||
const menu = window.MozXULElement.parseXULToFragment(`
|
||||
<menuitem id="zenToolbarThemePicker"
|
||||
<menuitem class="zenToolbarThemePicker"
|
||||
data-lazy-l10n-id="zen-workspaces-change-gradient"
|
||||
command="cmd_zenOpenZenThemePicker"/>
|
||||
`);
|
||||
|
@ -944,7 +944,10 @@
|
|||
return `rgba(${color.c[0]}, ${color.c[1]}, ${color.c[2]}, ${this.currentOpacity})`;
|
||||
}
|
||||
|
||||
getGradient(colors, forToolbar = false) {
|
||||
getGradient(colors, forToolbar = false, rotation = undefined) {
|
||||
if (typeof rotation === 'undefined') {
|
||||
rotation = this.currentRotation;
|
||||
}
|
||||
const themedColors = this.themedColors(colors);
|
||||
this.useAlgo = themedColors[0]?.algorithm ?? '';
|
||||
|
||||
|
@ -955,12 +958,12 @@
|
|||
} else if (themedColors.length === 1) {
|
||||
return this.getSingleRGBColor(themedColors[0], forToolbar);
|
||||
} else if (themedColors.length !== 3) {
|
||||
return `linear-gradient(${this.currentRotation}deg, ${themedColors.map((color) => this.getSingleRGBColor(color, forToolbar)).join(', ')})`;
|
||||
return `linear-gradient(${rotation}deg, ${themedColors.map((color) => this.getSingleRGBColor(color, forToolbar)).join(', ')})`;
|
||||
} else {
|
||||
let color1 = this.getSingleRGBColor(themedColors[2], forToolbar);
|
||||
let color2 = this.getSingleRGBColor(themedColors[0], forToolbar);
|
||||
let color3 = this.getSingleRGBColor(themedColors[1], forToolbar);
|
||||
return `linear-gradient(${this.currentRotation}deg, ${color1}, ${color2}, ${color3})`;
|
||||
return `linear-gradient(${rotation}deg, ${color1}, ${color2}, ${color3})`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1125,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const appBackground = browser.document.getElementById('zen-browser-background');
|
||||
if (!skipUpdate) {
|
||||
browser.document.documentElement.style.setProperty(
|
||||
'--zen-main-browser-background-old',
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenWorkspace extends MozXULElement {
|
||||
static get markup() {
|
||||
|
@ -8,8 +5,9 @@
|
|||
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator" flex="1">
|
||||
<hbox class="zen-current-workspace-indicator-icon"></hbox>
|
||||
<hbox class="zen-current-workspace-indicator-name"></hbox>
|
||||
<toolbarbutton class="toolbarbutton-1 chromeclass-toolbar-additional zen-workspaces-actions" context="zenWorkspaceMoreActions"></toolbarbutton>
|
||||
</vbox>
|
||||
<arrowscrollbox orient="vertical" class="workspace-arrowscrollbox">
|
||||
<arrowscrollbox orient="vertical" tabindex="-1" class="workspace-arrowscrollbox">
|
||||
<vbox class="zen-workspace-tabs-section zen-workspace-pinned-tabs-section">
|
||||
<html:div class="vertical-pinned-tabs-container-separator"></html:div>
|
||||
</vbox>
|
||||
|
@ -60,6 +58,13 @@
|
|||
false
|
||||
);
|
||||
|
||||
this.indicator.querySelector('.zen-current-workspace-indicator-name').onRenameFinished =
|
||||
this.onIndicatorRenameFinished.bind(this);
|
||||
|
||||
this.indicator
|
||||
.querySelector('.zen-workspaces-actions')
|
||||
.addEventListener('click', this.onActionsCommand.bind(this));
|
||||
|
||||
this.scrollbox.addEventListener('wheel', this, true);
|
||||
this.scrollbox.addEventListener('underflow', this);
|
||||
this.scrollbox.addEventListener('overflow', this);
|
||||
|
@ -171,6 +176,38 @@
|
|||
gBrowser.tabContainer.handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
get workspaceUuid() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
async onIndicatorRenameFinished(newName) {
|
||||
if (newName === '') {
|
||||
return;
|
||||
}
|
||||
let workspaces = (await gZenWorkspaces._workspaces()).workspaces;
|
||||
let workspaceData = workspaces.find((workspace) => workspace.uuid === this.workspaceUuid);
|
||||
workspaceData.name = newName;
|
||||
await gZenWorkspaces.saveWorkspace(workspaceData);
|
||||
this.indicator.querySelector('.zen-current-workspace-indicator-name').textContent = newName;
|
||||
gZenUIManager.showToast('zen-workspace-renamed-toast');
|
||||
}
|
||||
|
||||
onActionsCommand(event) {
|
||||
event.stopPropagation();
|
||||
const popup = document.getElementById('zenWorkspaceMoreActions');
|
||||
event.target.setAttribute('open', 'true');
|
||||
this.indicator.setAttribute('open', 'true');
|
||||
popup.addEventListener(
|
||||
'popuphidden',
|
||||
() => {
|
||||
event.target.removeAttribute('open');
|
||||
this.indicator.removeAttribute('open');
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
popup.openPopup(event.target, 'after_start');
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('zen-workspace', ZenWorkspace);
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenWorkspaceIcons extends MozXULElement {
|
||||
constructor() {
|
||||
|
|
|
@ -837,7 +837,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
|
||||
async initializeWorkspaces() {
|
||||
if (this.workspaceEnabled) {
|
||||
this._initializeWorkspaceCreationIcons();
|
||||
this._initializeWorkspaceTabContextMenus();
|
||||
await this.workspaceBookmarks();
|
||||
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
|
||||
|
@ -853,9 +852,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
console.error('gZenWorkspaces: Error initializing theme picker', e);
|
||||
}
|
||||
this.onWindowResize();
|
||||
if (window.gZenSessionStore) {
|
||||
await gZenSessionStore.promiseInitialized;
|
||||
}
|
||||
await this._selectStartPage();
|
||||
this._fixTabPositions();
|
||||
this._resolveInitialized();
|
||||
|
@ -913,7 +909,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
) {
|
||||
this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`);
|
||||
setTimeout(() => {
|
||||
gBrowser.selectedTab = gZenGlanceManager.getTabOrGlanceParent(tabs[this._tabToSelect]);
|
||||
gBrowser.selectedTab = tabs[this._tabToSelect];
|
||||
this._removedByStartupPage = true;
|
||||
gBrowser.removeTab(this._tabToRemoveForEmpty, {
|
||||
skipSessionStore: true,
|
||||
|
@ -1064,84 +1060,48 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
}
|
||||
|
||||
addPopupListeners() {
|
||||
const popup = document.getElementById('PanelUI-zen-workspaces');
|
||||
const contextMenu = document.getElementById('zenWorkspaceActionsMenu');
|
||||
const workspaceActions = document.getElementById('zenWorkspaceMoreActions');
|
||||
workspaceActions.addEventListener('popupshowing', this.updateWorkspaceActionsMenu.bind(this));
|
||||
|
||||
popup.addEventListener('popuphidden', this.handlePanelHidden.bind(this));
|
||||
popup.addEventListener('command', this.handlePanelCommand.bind(this));
|
||||
|
||||
contextMenu.addEventListener('popuphidden', (event) => {
|
||||
if (event.target === contextMenu) {
|
||||
this.onContextMenuClose(event);
|
||||
const contextChangeContainerTabMenu = document.getElementById(
|
||||
'context_zenWorkspacesOpenInContainerTab'
|
||||
);
|
||||
contextChangeContainerTabMenu.addEventListener(
|
||||
'popupshowing',
|
||||
this.updateWorkspaceActionsMenuContainer.bind(this)
|
||||
);
|
||||
contextChangeContainerTabMenu.addEventListener(
|
||||
'command',
|
||||
this.contextChangeContainerTab.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
updateWorkspaceActionsMenu(event) {
|
||||
if (event.target.id !== 'zenWorkspaceMoreActions') {
|
||||
return;
|
||||
}
|
||||
const openInContainerMenuItem = document.getElementById(
|
||||
'context_zenWorkspacesOpenInContainerTab'
|
||||
);
|
||||
if (this.shouldShowContainers) {
|
||||
openInContainerMenuItem.removeAttribute('hidden');
|
||||
} else {
|
||||
openInContainerMenuItem.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
updateWorkspaceActionsMenuContainer(event) {
|
||||
const workspace = this.getActiveWorkspaceFromCache();
|
||||
let containerTabId = workspace.containerTabId;
|
||||
return window.createUserContextMenu(event, {
|
||||
isContextMenu: true,
|
||||
excludeUserContextId: containerTabId,
|
||||
showDefaultTab: true,
|
||||
});
|
||||
contextMenu.addEventListener('popupshowing', this.updateContextMenu.bind(this));
|
||||
contextMenu.addEventListener('command', this.handleContextMenuCommand.bind(this));
|
||||
|
||||
const submenu = document.querySelector('#context_zenWorkspacesOpenInContainerTab > menupopup');
|
||||
if (submenu) {
|
||||
submenu.addEventListener('popupshowing', this.createContainerTabMenu.bind(this));
|
||||
submenu.addEventListener('command', this.contextChangeContainerTab.bind(this));
|
||||
}
|
||||
|
||||
const onWorkspaceIconContainerClick = this.onWorkspaceIconContainerClick.bind(this);
|
||||
for (const element of document.querySelectorAll('.PanelUI-zen-workspaces-icons-container')) {
|
||||
element.addEventListener('click', onWorkspaceIconContainerClick);
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById('PanelUI-zen-workspaces-create-input')
|
||||
.addEventListener('input', this.onWorkspaceCreationNameChange.bind(this));
|
||||
document
|
||||
.getElementById('PanelUI-zen-workspaces-edit-input')
|
||||
.addEventListener('input', this.onWorkspaceEditChange.bind(this));
|
||||
document
|
||||
.getElementById('PanelUI-zen-workspaces-icon-search-input')
|
||||
.addEventListener('input', this.conductSearch.bind(this));
|
||||
}
|
||||
|
||||
handlePanelCommand(event) {
|
||||
let target = event.target.closest('toolbarbutton');
|
||||
target ??= event.target.closest('button');
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
switch (target.id) {
|
||||
case 'PanelUI-zen-workspaces-reorder-mode':
|
||||
this.toggleReorderMode();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-new':
|
||||
this.openSaveDialog();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-create-save':
|
||||
this.saveWorkspaceFromCreate();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-edit-cancel':
|
||||
case 'PanelUI-zen-workspaces-create-cancel':
|
||||
this.closeWorkspacesSubView();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-edit-save':
|
||||
this.saveWorkspaceFromEdit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenuCommand(event) {
|
||||
const target = event.target.closest('menuitem');
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
switch (target.id) {
|
||||
case 'context_zenOpenWorkspace':
|
||||
this.openWorkspace();
|
||||
break;
|
||||
case 'context_zenEditWorkspace':
|
||||
this.contextEdit(event);
|
||||
break;
|
||||
case 'context_zenDeleteWorkspace':
|
||||
this.contextDelete(event);
|
||||
break;
|
||||
}
|
||||
async contextDeleteWorkspace() {
|
||||
await this.removeWorkspace(this.activeWorkspace, true);
|
||||
}
|
||||
|
||||
searchIcons(input, icons) {
|
||||
|
@ -1205,69 +1165,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
return filteredEmojiScores.map((score) => score.emoji);
|
||||
}
|
||||
|
||||
resetWorkspaceIconSearch() {
|
||||
let container = document.getElementById('PanelUI-zen-workspaces-icon-picker-wrapper');
|
||||
let searchInput = document.getElementById('PanelUI-zen-workspaces-icon-search-input');
|
||||
|
||||
// Clear the search input field
|
||||
searchInput.value = '';
|
||||
for (let button of container.querySelectorAll('.toolbarbutton-1')) {
|
||||
button.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
_initializeWorkspaceCreationIcons() {
|
||||
let container = document.getElementById('PanelUI-zen-workspaces-icon-picker-wrapper');
|
||||
let searchInput = document.getElementById('PanelUI-zen-workspaces-icon-search-input');
|
||||
searchInput.value = '';
|
||||
for (let iconData of this.emojis) {
|
||||
const icon = iconData[0];
|
||||
let button = document.createXULElement('toolbarbutton');
|
||||
button.className = 'toolbarbutton-1 workspace-icon-button';
|
||||
button.setAttribute('label', icon);
|
||||
button.onclick = (event) => {
|
||||
const button = event.target;
|
||||
let wasSelected = button.hasAttribute('selected');
|
||||
for (let button of container.children) {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
if (!wasSelected) {
|
||||
button.setAttribute('selected', 'true');
|
||||
}
|
||||
if (this.onIconChangeConnectedCallback) {
|
||||
this.onIconChangeConnectedCallback(icon);
|
||||
} else {
|
||||
this.onWorkspaceIconChangeInner('create', icon);
|
||||
}
|
||||
};
|
||||
container.appendChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
conductSearch() {
|
||||
const container = document.getElementById('PanelUI-zen-workspaces-icon-picker-wrapper');
|
||||
const searchInput = document.getElementById('PanelUI-zen-workspaces-icon-search-input');
|
||||
const query = searchInput.value.toLowerCase();
|
||||
|
||||
if (query === '') {
|
||||
this.resetWorkspaceIconSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
const buttons = Array.from(container.querySelectorAll('.toolbarbutton-1'));
|
||||
buttons.forEach((button) => (button.style.display = 'none'));
|
||||
|
||||
const filteredIcons = this.searchIcons(query, this.emojis);
|
||||
|
||||
filteredIcons.forEach((emoji) => {
|
||||
const matchingButton = buttons.find((button) => button.getAttribute('label') === emoji);
|
||||
if (matchingButton) {
|
||||
matchingButton.style.display = '';
|
||||
container.appendChild(matchingButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async saveWorkspace(workspaceData, preventPropagation = false) {
|
||||
if (this.privateWindowOrDisabled) {
|
||||
return;
|
||||
|
@ -1314,83 +1211,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
);
|
||||
}
|
||||
// Workspaces dialog UI management
|
||||
|
||||
openSaveDialog() {
|
||||
let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
|
||||
// randomly select an icon
|
||||
let icon = this.emojis[Math.floor(Math.random() * (this.emojis.length - 257))][0];
|
||||
this._workspaceCreateInput.textContent = '';
|
||||
this._workspaceCreateInput.value = '';
|
||||
this._workspaceCreateInput.setAttribute('data-initial-value', '');
|
||||
document
|
||||
.querySelectorAll('#PanelUI-zen-workspaces-icon-picker-wrapper toolbarbutton')
|
||||
.forEach((button) => {
|
||||
if (button.label === icon) {
|
||||
button.setAttribute('selected', 'true');
|
||||
} else {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
});
|
||||
document.querySelector('.PanelUI-zen-workspaces-icons-container.create').textContent = icon;
|
||||
|
||||
PanelUI.showSubView('PanelUI-zen-workspaces-create', parentPanel);
|
||||
}
|
||||
|
||||
async openEditDialog(workspaceUuid) {
|
||||
this._workspaceEditDialog.setAttribute('data-workspace-uuid', workspaceUuid);
|
||||
document.getElementById('PanelUI-zen-workspaces-edit-save').setAttribute('disabled', 'true');
|
||||
let workspaces = (await this._workspaces()).workspaces;
|
||||
let workspaceData = workspaces.find((workspace) => workspace.uuid === workspaceUuid);
|
||||
this._workspaceEditInput.textContent = workspaceData.name;
|
||||
this._workspaceEditInput.value = workspaceData.name;
|
||||
this._workspaceEditInput.setAttribute('data-initial-value', workspaceData.name);
|
||||
this._workspaceEditIconsContainer.setAttribute('data-initial-value', workspaceData.icon);
|
||||
this.onIconChangeConnectedCallback = (...args) => {
|
||||
this.onWorkspaceIconChangeInner('edit', ...args);
|
||||
this.onWorkspaceEditChange(...args);
|
||||
};
|
||||
document
|
||||
.querySelectorAll('#PanelUI-zen-workspaces-icon-picker-wrapper toolbarbutton')
|
||||
.forEach((button) => {
|
||||
if (button.label === workspaceData.icon) {
|
||||
button.setAttribute('selected', 'true');
|
||||
} else {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
});
|
||||
document.querySelector('.PanelUI-zen-workspaces-icons-container.edit').textContent =
|
||||
this.getWorkspaceIcon(workspaceData);
|
||||
let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
PanelUI.showSubView('PanelUI-zen-workspaces-edit', parentPanel);
|
||||
}
|
||||
|
||||
onWorkspaceIconChangeInner(type = 'create', icon) {
|
||||
const container = document.querySelector(`.PanelUI-zen-workspaces-icons-container.${type}`);
|
||||
if (container.textContent !== icon) {
|
||||
container.textContent = icon;
|
||||
}
|
||||
this.goToPreviousSubView();
|
||||
}
|
||||
|
||||
onWorkspaceIconContainerClick(event) {
|
||||
event.preventDefault();
|
||||
const parentPanel = document.getElementById('PanelUI-zen-workspaces-edit');
|
||||
PanelUI.showSubView('PanelUI-zen-workspaces-icon-picker', parentPanel);
|
||||
|
||||
const container = parentPanel.parentNode.querySelector('.panel-viewcontainer');
|
||||
setTimeout(() => {
|
||||
if (container) {
|
||||
container.style.minHeight = 'unset';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
goToPreviousSubView() {
|
||||
const parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
parentPanel.goBack();
|
||||
}
|
||||
|
||||
workspaceHasIcon(workspace) {
|
||||
return workspace.icon && workspace.icon !== '';
|
||||
}
|
||||
|
@ -1424,230 +1244,15 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
return;
|
||||
}
|
||||
|
||||
let workspaceList = browser.document.getElementById('PanelUI-zen-workspaces-list');
|
||||
const createWorkspaceElement = (workspace) => {
|
||||
let element = browser.document.createXULElement('toolbarbutton');
|
||||
element.className = 'subviewbutton zen-workspace-button';
|
||||
element.setAttribute('tooltiptext', workspace.name);
|
||||
element.setAttribute('zen-workspace-id', workspace.uuid);
|
||||
if (this.isWorkspaceActive(workspace)) {
|
||||
element.setAttribute('active', 'true');
|
||||
}
|
||||
let containerGroup = undefined;
|
||||
try {
|
||||
containerGroup = browser.ContextualIdentityService.getPublicIdentities().find(
|
||||
(container) => container.userContextId === workspace.containerTabId
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn('gZenWorkspaces: Error setting container color', e);
|
||||
}
|
||||
if (containerGroup) {
|
||||
element.classList.add('identity-color-' + containerGroup.color);
|
||||
element.setAttribute('data-usercontextid', containerGroup.userContextId);
|
||||
}
|
||||
// Set draggable attribute based on reorder mode
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
element.setAttribute('draggable', 'true');
|
||||
}
|
||||
element.addEventListener(
|
||||
'dragstart',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
this.draggedElement = element;
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('text/plain', element.getAttribute('zen-workspace-id'));
|
||||
|
||||
// Create a transparent drag image for Linux
|
||||
if (AppConstants.platform === 'linux') {
|
||||
const dragImage = document.createElement('canvas');
|
||||
dragImage.width = 1;
|
||||
dragImage.height = 1;
|
||||
event.dataTransfer.setDragImage(dragImage, 0, 0);
|
||||
}
|
||||
|
||||
element.classList.add('dragging');
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragover',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// Ensure the dragover effect is visible on Linux
|
||||
if (AppConstants.platform === 'linux') {
|
||||
const targetId = element.getAttribute('zen-workspace-id');
|
||||
const draggedId = this.draggedElement.getAttribute('zen-workspace-id');
|
||||
if (targetId !== draggedId) {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener('dragenter', function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('dragleave', function (event) {
|
||||
element.classList.remove('dragover');
|
||||
});
|
||||
|
||||
element.addEventListener(
|
||||
'drop',
|
||||
async function (event) {
|
||||
event.preventDefault();
|
||||
element.classList.remove('dragover');
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
const draggedWorkspaceId = event.dataTransfer.getData('text/plain');
|
||||
const targetWorkspaceId = element.getAttribute('zen-workspace-id');
|
||||
if (draggedWorkspaceId !== targetWorkspaceId) {
|
||||
await this.moveWorkspace(draggedWorkspaceId, targetWorkspaceId);
|
||||
}
|
||||
if (this.draggedElement) {
|
||||
this.draggedElement.classList.remove('dragging');
|
||||
this.draggedElement = null;
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragend',
|
||||
function (event) {
|
||||
if (this.draggedElement) {
|
||||
this.draggedElement.classList.remove('dragging');
|
||||
this.draggedElement = null;
|
||||
}
|
||||
const workspaceElements = browser.document.querySelectorAll('.zen-workspace-button');
|
||||
for (const elem of workspaceElements) {
|
||||
elem.classList.remove('dragover');
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
let childs = browser.MozXULElement.parseXULToFragment(`
|
||||
<div class="zen-workspace-icon">
|
||||
</div>
|
||||
<vbox>
|
||||
<div class="zen-workspace-name">
|
||||
</div>
|
||||
<div class="zen-workspace-container" ${containerGroup ? '' : 'hidden="true"'}>
|
||||
</div>
|
||||
</vbox>
|
||||
<image class="toolbarbutton-icon zen-workspace-actions-reorder-icon" ></image>
|
||||
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-actions">
|
||||
<image class="toolbarbutton-icon" id="zen-workspace-actions-menu-icon"></image>
|
||||
</toolbarbutton>
|
||||
`);
|
||||
|
||||
// use text content instead of innerHTML to avoid XSS
|
||||
childs.querySelector('.zen-workspace-icon').textContent =
|
||||
browser.gZenWorkspaces.getWorkspaceIcon(workspace);
|
||||
childs.querySelector('.zen-workspace-name').textContent = workspace.name;
|
||||
if (containerGroup) {
|
||||
childs.querySelector('.zen-workspace-container').textContent =
|
||||
ContextualIdentityService.getUserContextLabel(containerGroup.userContextId);
|
||||
}
|
||||
|
||||
childs.querySelector('.zen-workspace-actions').addEventListener(
|
||||
'command',
|
||||
((event) => {
|
||||
let button = event.target;
|
||||
this._contextMenuId = button
|
||||
.closest('toolbarbutton[zen-workspace-id]')
|
||||
.getAttribute('zen-workspace-id');
|
||||
const popup = button.ownerDocument.getElementById('zenWorkspaceActionsMenu');
|
||||
popup.openPopup(button, 'after_end');
|
||||
}).bind(browser.gZenWorkspaces)
|
||||
);
|
||||
element.appendChild(childs);
|
||||
element.onclick = (async () => {
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
return; // Return early if reorder mode is on
|
||||
}
|
||||
if (event.target.closest('.zen-workspace-actions')) {
|
||||
return; // Ignore clicks on the actions button
|
||||
}
|
||||
const workspaceId = element.getAttribute('zen-workspace-id');
|
||||
const workspaces = await this._workspaces();
|
||||
const workspace = workspaces.workspaces.find((w) => w.uuid === workspaceId);
|
||||
await this.changeWorkspace(workspace);
|
||||
let panel = this.ownerWindow.document.getElementById('PanelUI-zen-workspaces');
|
||||
PanelMultiView.hidePopup(panel);
|
||||
}).bind(browser.gZenWorkspaces);
|
||||
return element;
|
||||
};
|
||||
|
||||
const createLastPositionDropTarget = () => {
|
||||
const element = browser.document.createXULElement('div');
|
||||
element.className = 'zen-workspace-last-place-drop-target';
|
||||
|
||||
element.addEventListener(
|
||||
'dragover',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// Ensure the dragover effect is visible on Linux
|
||||
if (AppConstants.platform === 'linux') {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragenter',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragleave',
|
||||
function (event) {
|
||||
element.classList.remove('dragover');
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'drop',
|
||||
async function (event) {
|
||||
event.preventDefault();
|
||||
element.classList.remove('dragover');
|
||||
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
const draggedWorkspaceId = event.dataTransfer.getData('text/plain');
|
||||
await this.moveWorkspaceToEnd(draggedWorkspaceId);
|
||||
|
||||
if (this.draggedElement) {
|
||||
this.draggedElement.classList.remove('dragging');
|
||||
this.draggedElement = null;
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
if (clearCache) {
|
||||
browser.gZenWorkspaces._workspaceCache = null;
|
||||
browser.gZenWorkspaces._workspaceBookmarksCache = null;
|
||||
await browser.gZenWorkspaces.workspaceBookmarks();
|
||||
}
|
||||
let workspaces = await browser.gZenWorkspaces._workspaces();
|
||||
browser.document
|
||||
.getElementById('cmd_zenCtxDeleteWorkspace')
|
||||
.setAttribute('disabled', workspaces.workspaces.length <= 1);
|
||||
if (clearCache) {
|
||||
browser.dispatchEvent(
|
||||
new CustomEvent('ZenWorkspacesUIUpdate', {
|
||||
|
@ -1656,39 +1261,12 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
})
|
||||
);
|
||||
}
|
||||
await browser.gZenWorkspaces.workspaceBookmarks();
|
||||
workspaceList.innerHTML = '';
|
||||
workspaceList.parentNode.style.display = 'flex';
|
||||
if (workspaces.workspaces.length <= 0) {
|
||||
workspaceList.innerHTML = 'No workspaces available';
|
||||
workspaceList.setAttribute('empty', 'true');
|
||||
} else {
|
||||
workspaceList.removeAttribute('empty');
|
||||
}
|
||||
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
let workspaceElement = createWorkspaceElement(workspace);
|
||||
workspaceList.appendChild(workspaceElement);
|
||||
}
|
||||
|
||||
workspaceList.appendChild(createLastPositionDropTarget());
|
||||
|
||||
if (!ignoreStrip) {
|
||||
browser.gZenWorkspaces._fixIndicatorsNames(workspaces);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handlePanelHidden() {
|
||||
const workspacesList = document.getElementById('PanelUI-zen-workspaces-list');
|
||||
const reorderModeButton = document.getElementById('PanelUI-zen-workspaces-reorder-mode');
|
||||
|
||||
workspacesList?.removeAttribute('reorder-mode');
|
||||
reorderModeButton?.removeAttribute('active');
|
||||
this.resetWorkspaceIconSearch();
|
||||
this.clearEmojis();
|
||||
}
|
||||
|
||||
async moveWorkspaceToEnd(draggedWorkspaceId) {
|
||||
const workspaces = (await this._workspaces()).workspaces;
|
||||
const draggedIndex = workspaces.findIndex((w) => w.uuid === draggedWorkspaceId);
|
||||
|
@ -1699,39 +1277,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
await this._propagateWorkspaceData();
|
||||
}
|
||||
|
||||
isReorderModeOn(browser) {
|
||||
return (
|
||||
browser.document
|
||||
.getElementById('PanelUI-zen-workspaces-list')
|
||||
.getAttribute('reorder-mode') === 'true'
|
||||
);
|
||||
}
|
||||
|
||||
toggleReorderMode() {
|
||||
const workspacesList = document.getElementById('PanelUI-zen-workspaces-list');
|
||||
const reorderModeButton = document.getElementById('PanelUI-zen-workspaces-reorder-mode');
|
||||
const isActive = workspacesList.getAttribute('reorder-mode') === 'true';
|
||||
if (isActive) {
|
||||
workspacesList.removeAttribute('reorder-mode');
|
||||
reorderModeButton.removeAttribute('active');
|
||||
} else {
|
||||
workspacesList.setAttribute('reorder-mode', 'true');
|
||||
reorderModeButton.setAttribute('active', 'true');
|
||||
}
|
||||
|
||||
// Update draggable attribute
|
||||
const workspaceElements = document.querySelectorAll('.zen-workspace-button');
|
||||
workspaceElements.forEach((elem) => {
|
||||
// When reorder mode is toggled off, remove draggable attribute
|
||||
// When reorder mode is toggled on, set draggable attribute
|
||||
if (isActive) {
|
||||
elem.removeAttribute('draggable');
|
||||
} else {
|
||||
elem.setAttribute('draggable', 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async moveWorkspace(draggedWorkspaceId, targetWorkspaceId) {
|
||||
const workspaces = (await this._workspaces()).workspaces;
|
||||
const draggedIndex = workspaces.findIndex((w) => w.uuid === draggedWorkspaceId);
|
||||
|
@ -1743,45 +1288,8 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
await this._propagateWorkspaceData();
|
||||
}
|
||||
|
||||
async openWorkspacesDialog(event) {
|
||||
if (!this.workspaceEnabled || this.isPrivateWindow) {
|
||||
return;
|
||||
}
|
||||
let target = event.target.closest('.zen-current-workspace-indicator');
|
||||
let panel = document.getElementById('PanelUI-zen-workspaces');
|
||||
await this._propagateWorkspaceData({
|
||||
ignoreStrip: true,
|
||||
clearCache: false,
|
||||
});
|
||||
PanelMultiView.openPopup(panel, target, {
|
||||
position: 'bottomright topright',
|
||||
triggerEvent: event,
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
closeWorkspacesSubView() {
|
||||
let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
parentPanel.goBack(parentPanel);
|
||||
}
|
||||
|
||||
// Workspaces management
|
||||
|
||||
get _workspaceCreateInput() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-create-input');
|
||||
}
|
||||
|
||||
get _workspaceEditDialog() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-edit');
|
||||
}
|
||||
|
||||
get _workspaceEditInput() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-edit-input');
|
||||
}
|
||||
|
||||
get _workspaceEditIconsContainer() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-icon-picker');
|
||||
}
|
||||
|
||||
_deleteAllTabsInWorkspace(workspaceID) {
|
||||
gBrowser.removeTabs(
|
||||
Array.from(this.allStoredTabs).filter(
|
||||
|
@ -1847,57 +1355,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
}
|
||||
}
|
||||
|
||||
async saveWorkspaceFromCreate() {
|
||||
let workspaceName = this._workspaceCreateInput.value;
|
||||
if (!workspaceName) {
|
||||
return;
|
||||
}
|
||||
this._workspaceCreateInput.value = '';
|
||||
let icon = document.querySelector('#PanelUI-zen-workspaces-icon-picker-wrapper [selected]');
|
||||
icon?.removeAttribute('selected');
|
||||
await this.createAndSaveWorkspace(workspaceName, icon?.label);
|
||||
this.goToPreviousSubView();
|
||||
}
|
||||
|
||||
async saveWorkspaceFromEdit() {
|
||||
let workspaceUuid = this._workspaceEditDialog.getAttribute('data-workspace-uuid');
|
||||
let workspaceName = this._workspaceEditInput.value;
|
||||
if (!workspaceName) {
|
||||
return;
|
||||
}
|
||||
this._workspaceEditInput.value = '';
|
||||
let icon = document.querySelector('#PanelUI-zen-workspaces-icon-picker-wrapper [selected]');
|
||||
icon?.removeAttribute('selected');
|
||||
let workspaces = (await this._workspaces()).workspaces;
|
||||
let workspaceData = workspaces.find((workspace) => workspace.uuid === workspaceUuid);
|
||||
workspaceData.name = workspaceName;
|
||||
workspaceData.icon = icon?.label;
|
||||
await this.saveWorkspace(workspaceData);
|
||||
this.goToPreviousSubView();
|
||||
}
|
||||
|
||||
onWorkspaceCreationNameChange() {
|
||||
let button = document.getElementById('PanelUI-zen-workspaces-create-save');
|
||||
if (this._workspaceCreateInput.value === '') {
|
||||
button.setAttribute('disabled', 'true');
|
||||
return;
|
||||
}
|
||||
button.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
onWorkspaceEditChange(icon) {
|
||||
let button = document.getElementById('PanelUI-zen-workspaces-edit-save');
|
||||
let name = this._workspaceEditInput.value;
|
||||
if (
|
||||
name === this._workspaceEditInput.getAttribute('data-initial-value') &&
|
||||
icon === this._workspaceEditIconsContainer.getAttribute('data-initial-value')
|
||||
) {
|
||||
button.setAttribute('disabled', 'true');
|
||||
return;
|
||||
}
|
||||
button.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
addChangeListeners(func) {
|
||||
if (!this._changeListeners) {
|
||||
this._changeListeners = [];
|
||||
|
@ -2323,7 +1780,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
existingTransform = container.style.transform;
|
||||
}
|
||||
if (shouldAnimate) {
|
||||
container.style.transform = existingTransform;
|
||||
container.style.transform = newTransform;
|
||||
animations.push(
|
||||
gZenUIManager.motion.animate(
|
||||
container,
|
||||
|
@ -2540,6 +1997,8 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
setTimeout(gURLBar.formatValue.bind(gURLBar), 0);
|
||||
}
|
||||
|
||||
async _fixCtrlTabBehavior() {
|
||||
|
@ -2806,49 +2265,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
}
|
||||
}
|
||||
|
||||
// Context menu management
|
||||
|
||||
_contextMenuId = null;
|
||||
async updateContextMenu(_) {
|
||||
console.assert(this._contextMenuId, 'No context menu ID set');
|
||||
document
|
||||
.querySelector(
|
||||
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
|
||||
)
|
||||
.setAttribute('active', 'true');
|
||||
const workspaces = await this._workspaces();
|
||||
let deleteMenuItem = document.getElementById('context_zenDeleteWorkspace');
|
||||
if (workspaces.workspaces.length <= 1) {
|
||||
deleteMenuItem.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
deleteMenuItem.removeAttribute('disabled');
|
||||
}
|
||||
let openMenuItem = document.getElementById('context_zenOpenWorkspace');
|
||||
if (
|
||||
workspaces.workspaces.find(
|
||||
(workspace) => workspace.uuid === this._contextMenuId && this.isWorkspaceActive(workspace)
|
||||
)
|
||||
) {
|
||||
openMenuItem.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
openMenuItem.removeAttribute('disabled');
|
||||
}
|
||||
const openInContainerMenuItem = document.getElementById(
|
||||
'context_zenWorkspacesOpenInContainerTab'
|
||||
);
|
||||
if (this.shouldShowContainers) {
|
||||
openInContainerMenuItem.removeAttribute('hidden');
|
||||
} else {
|
||||
openInContainerMenuItem.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
async contextChangeContainerTab(event) {
|
||||
this._organizingWorkspaceStrip = true;
|
||||
let workspaces = await this._workspaces();
|
||||
let workspace = workspaces.workspaces.find(
|
||||
(workspace) => workspace.uuid === this._contextMenuId
|
||||
);
|
||||
let workspace = await this.getActiveWorkspace();
|
||||
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
|
||||
workspace.containerTabId = userContextId + 0; // +0 to convert to number
|
||||
await this.saveWorkspace(workspace);
|
||||
|
@ -2861,16 +2280,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
}, 0);
|
||||
}
|
||||
|
||||
onContextMenuClose() {
|
||||
let target = document.querySelector(
|
||||
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
|
||||
);
|
||||
if (target) {
|
||||
target.removeAttribute('active');
|
||||
}
|
||||
this._contextMenuId = null;
|
||||
}
|
||||
|
||||
findTabToBlur(tab) {
|
||||
if ((!this._shouldChangeToTab(tab) || !tab) && this._emptyTab) {
|
||||
return this._emptyTab;
|
||||
|
@ -2878,26 +2287,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
return tab;
|
||||
}
|
||||
|
||||
async openWorkspace() {
|
||||
let workspaces = await this._workspaces();
|
||||
let workspace = workspaces.workspaces.find(
|
||||
(workspace) => workspace.uuid === this._contextMenuId
|
||||
);
|
||||
await this.changeWorkspace(workspace);
|
||||
}
|
||||
|
||||
async contextDelete(event) {
|
||||
this.__contextIsDelete = true;
|
||||
event.stopPropagation();
|
||||
await this.removeWorkspace(this._contextMenuId);
|
||||
this.__contextIsDelete = false;
|
||||
}
|
||||
|
||||
async contextEdit(event) {
|
||||
event.stopPropagation();
|
||||
await this.openEditDialog(this._contextMenuId);
|
||||
}
|
||||
|
||||
get emojis() {
|
||||
if (this._emojis) {
|
||||
return this._emojis;
|
||||
|
@ -2976,18 +2365,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
);
|
||||
}
|
||||
|
||||
// Tab browser utilities
|
||||
createContainerTabMenu(event) {
|
||||
let window = event.target.ownerGlobal;
|
||||
const workspace = this.getWorkspaceFromId(this._contextMenuId);
|
||||
let containerTabId = workspace.containerTabId;
|
||||
return window.createUserContextMenu(event, {
|
||||
isContextMenu: true,
|
||||
excludeUserContextId: containerTabId,
|
||||
showDefaultTab: true,
|
||||
});
|
||||
}
|
||||
|
||||
getContextIdIfNeeded(userContextId, fromExternal, allowInheritPrincipal) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return [userContextId, false, undefined];
|
||||
|
|
|
@ -122,327 +122,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Workspaces Panel UI */
|
||||
|
||||
#PanelUI-zen-workspaces {
|
||||
--panel-width: 320px;
|
||||
--panel-padding: 0;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces > panelmultiview {
|
||||
align-items: flex-start;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces panelmultiview panelview {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
width: var(--panel-width);
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton[selected='true'] {
|
||||
border-color: var(--zen-colors-secondary);
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton .toolbarbutton-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton .toolbarbutton-text {
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker {
|
||||
padding: 5px !important;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker-wrapper {
|
||||
overflow-x: hidden;
|
||||
justify-items: center;
|
||||
overflow-y: auto;
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
max-height: 250px;
|
||||
|
||||
.workspace-icon-button {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
font-size: 16px;
|
||||
margin: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list[empty='true'] {
|
||||
font-weight: 600;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
text-align: start;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.PanelUI-zen-workspaces-user-create {
|
||||
height: 100%;
|
||||
|
||||
.PanelUI-zen-workspaces-creation-wraper {
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
|
||||
margin-top: 10px;
|
||||
|
||||
& .PanelUI-zen-workspaces-icons-container {
|
||||
padding: 10px 0;
|
||||
min-width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-right: 1px solid var(--zen-colors-border);
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
& html|input {
|
||||
border: none;
|
||||
outline: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Workspace icon picker styles */
|
||||
#PanelUI-zen-workspaces-icon-picker-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-search-bar {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: inherit;
|
||||
z-index: 1000;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-search-input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
border: 1px solid var(--panel-separator-color, #ccc);
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list toolbarbutton {
|
||||
padding: 5px;
|
||||
border-radius: var(--zen-button-border-radius);
|
||||
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
& .zen-workspace-icon {
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: var(--zen-button-border-radius);
|
||||
margin-right: 10px;
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&[data-usercontextid] .zen-workspace-icon {
|
||||
border-color: color-mix(in srgb, var(--identity-tab-color) 40%, transparent 60%);
|
||||
}
|
||||
|
||||
& > vbox:has(> .zen-workspace-name) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
& .zen-workspace-name {
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& .zen-workspace-container {
|
||||
font-size: 12px;
|
||||
opacity: 0.5;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
& .zen-workspace-actions,
|
||||
.zen-workspace-actions-reorder-icon {
|
||||
display: none;
|
||||
margin: 0;
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
&.zen-workspace-button[active='true'] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.zen-workspace-button[active='true'] .zen-workspace-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: -2px;
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-workspace-button.dragging {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.zen-workspace-button.dragover::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
}
|
||||
|
||||
/* Enhanced visual feedback for Linux platform */
|
||||
@supports (-moz-gtk-csd-available) {
|
||||
.zen-workspace-button.dragover {
|
||||
background-color: color-mix(in srgb, var(--toolbarbutton-icon-fill-attention) 15%, transparent);
|
||||
}
|
||||
|
||||
.zen-workspace-button.dragover::after {
|
||||
height: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target.dragover {
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
}
|
||||
|
||||
/* Enhanced visual feedback for Linux platform */
|
||||
@supports (-moz-gtk-csd-available) {
|
||||
.zen-workspace-last-place-drop-target {
|
||||
height: 6px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target.dragover {
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
box-shadow: 0 0 4px var(--toolbarbutton-icon-fill-attention);
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-reorder-mode[active='true'] {
|
||||
color: var(--toolbarbutton-icon-fill-attention) !important;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list:not([reorder-mode='true']) toolbarbutton {
|
||||
&:hover .zen-workspace-actions,
|
||||
& .zen-workspace-actions[active='true'] {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list[reorder-mode='true'] toolbarbutton {
|
||||
.zen-workspace-actions-reorder-icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list[reorder-mode='true'] .zen-workspace-last-place-drop-target {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target {
|
||||
display: none;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-view > vbox:nth-child(2) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-new,
|
||||
#PanelUI-zen-workspaces-reorder-mode,
|
||||
#PanelUI-zen-gradient-generator-color-custom-add {
|
||||
min-height: 1px !important;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-create-footer,
|
||||
#PanelUI-zen-workspaces-edit-footer {
|
||||
padding-bottom: 0 !important;
|
||||
margin-top: 10px;
|
||||
margin-left: 0;
|
||||
margin-bottom: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-create-footer button[default='true'],
|
||||
#PanelUI-zen-workspaces-edit-footer button[default='true'] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-header {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* Mark workspaces indicator */
|
||||
.zen-current-workspace-indicator {
|
||||
padding: calc(15px + var(--zen-toolbox-padding))
|
||||
calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
max-height: var(--zen-workspace-indicator-height);
|
||||
|
@ -452,6 +134,8 @@
|
|||
flex-direction: row !important;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: small;
|
||||
padding-right: 10px;
|
||||
|
||||
&::before {
|
||||
border-radius: var(--border-radius-medium);
|
||||
|
@ -486,8 +170,23 @@
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: none;
|
||||
font-size: small;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.zen-workspaces-actions {
|
||||
--toolbarbutton-inner-padding: 4px;
|
||||
margin-left: auto !important;
|
||||
opacity: 0;
|
||||
visibility: collapse;
|
||||
transition: opacity 0.1s;
|
||||
order: 5;
|
||||
}
|
||||
|
||||
:root:not([zen-private-window]) &:hover .zen-workspaces-actions,
|
||||
& .zen-workspaces-actions[open='true'] {
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"binaryName": "zen",
|
||||
"version": {
|
||||
"product": "firefox",
|
||||
"version": "139.0",
|
||||
"version": "138.0.4",
|
||||
"candidate": "139.0"
|
||||
},
|
||||
"buildOptions": {
|
||||
|
@ -19,7 +19,7 @@
|
|||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.12.9b",
|
||||
"displayVersion": "1.12.8b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue