mirror of
https://github.com/zen-browser/desktop.git
synced 2025-09-05 19:08:18 +00:00
Compare commits
163 Commits
1.0.1-a.14
...
1.0.1-a.18
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d24a9a2583 | ||
![]() |
6a7ce1639a | ||
![]() |
e75f6f85e7 | ||
![]() |
9d4a8702cb | ||
![]() |
7a91ea7107 | ||
![]() |
0e818dfdc8 | ||
![]() |
2228f00c0a | ||
![]() |
e2e46d675e | ||
![]() |
14b8a1d8a2 | ||
![]() |
7ab07c69aa | ||
![]() |
e9e2e9a711 | ||
![]() |
84de31e85b | ||
![]() |
31cc0ce7a8 | ||
![]() |
5c7dc3097a | ||
![]() |
3a09999cca | ||
![]() |
cf1e468395 | ||
![]() |
871e529bf2 | ||
![]() |
5e9ccd440e | ||
![]() |
657bc32e9b | ||
![]() |
d7a48bd855 | ||
![]() |
d2746689f9 | ||
![]() |
f093228f71 | ||
![]() |
f3e054d15e | ||
![]() |
86a3ad4b1f | ||
![]() |
aa5a768ef0 | ||
![]() |
b6cb37db43 | ||
![]() |
3ca25590c7 | ||
![]() |
c3b902ae65 | ||
![]() |
014602266b | ||
![]() |
696a2148f0 | ||
![]() |
e086cf56fc | ||
![]() |
6d5dcbe89f | ||
![]() |
54f5042c3c | ||
![]() |
927f38e999 | ||
![]() |
638ec9c2ea | ||
![]() |
27c5119664 | ||
![]() |
8542000540 | ||
![]() |
9c5c53f4ae | ||
![]() |
a2ca9361e1 | ||
![]() |
9042baf13d | ||
![]() |
957cff3447 | ||
![]() |
fbd6d11a5c | ||
![]() |
6ccb766b2e | ||
![]() |
df7be136f8 | ||
![]() |
3abbb08e31 | ||
![]() |
715b6df2fb | ||
![]() |
a64fe45175 | ||
![]() |
f0690d0ac5 | ||
![]() |
3049bc8215 | ||
![]() |
4f9b59ed6d | ||
![]() |
d05e849299 | ||
![]() |
76f06e120c | ||
![]() |
fdf83eaef8 | ||
![]() |
b214364ee9 | ||
![]() |
adfcaa2f09 | ||
![]() |
bd3c8d19fa | ||
![]() |
7ae2d6d5bb | ||
![]() |
44e5a98188 | ||
![]() |
093722c7d6 | ||
![]() |
ca2dd38f9b | ||
![]() |
bf0342eb06 | ||
![]() |
cf7af8e3aa | ||
![]() |
19f8f58618 | ||
![]() |
649d82425a | ||
![]() |
1f45db0fac | ||
![]() |
49ceb6f6e5 | ||
![]() |
9d0eb9fb08 | ||
![]() |
ec65a69496 | ||
![]() |
f498a64413 | ||
![]() |
4b993c932e | ||
![]() |
5aed1d0e97 | ||
![]() |
4f70e6b881 | ||
![]() |
e3751f5a57 | ||
![]() |
46d9280490 | ||
![]() |
40c0a9bf51 | ||
![]() |
0ca828325d | ||
![]() |
aa74a68ca8 | ||
![]() |
419922a2aa | ||
![]() |
e970e6cbc5 | ||
![]() |
90b04ddd9b | ||
![]() |
96ce701f64 | ||
![]() |
b506c94d9a | ||
![]() |
d4962685d6 | ||
![]() |
24a6013195 | ||
![]() |
de3962412f | ||
![]() |
0603c57a60 | ||
![]() |
71464e6914 | ||
![]() |
2a96fd466f | ||
![]() |
d6e27ee9b5 | ||
![]() |
a21511b683 | ||
![]() |
984fa08f14 | ||
![]() |
fbc2f999bf | ||
![]() |
86cb96cd21 | ||
![]() |
9bc955a5d4 | ||
![]() |
49dc6ac30b | ||
![]() |
5b93ce3481 | ||
![]() |
369adbdfe9 | ||
![]() |
c5b6376980 | ||
![]() |
64c7bc78af | ||
![]() |
3a4873aa1b | ||
![]() |
e0a3b2d8c3 | ||
![]() |
10f6e4e751 | ||
![]() |
3a374dcdba | ||
![]() |
b6f4611994 | ||
![]() |
b58f7a0c4d | ||
![]() |
f94b51e410 | ||
![]() |
6185336c3a | ||
![]() |
bd8829a432 | ||
![]() |
89d8d11db3 | ||
![]() |
644baca439 | ||
![]() |
3cc111e5dd | ||
![]() |
baa614c094 | ||
![]() |
95cb9460d4 | ||
![]() |
e433c5160a | ||
![]() |
440b7171b0 | ||
![]() |
cfd2e57950 | ||
![]() |
a5d4855530 | ||
![]() |
448a034766 | ||
![]() |
d228a96944 | ||
![]() |
51a3fb98e1 | ||
![]() |
04dd345ef4 | ||
![]() |
3ba221831d | ||
![]() |
b30a65015d | ||
![]() |
0ea71db628 | ||
![]() |
b64d007315 | ||
![]() |
88f954fc28 | ||
![]() |
f6acb174a4 | ||
![]() |
b8a927b163 | ||
![]() |
4e93b06772 | ||
![]() |
65934f5e00 | ||
![]() |
fb89aab252 | ||
![]() |
db9a0edce5 | ||
![]() |
a85baa5cca | ||
![]() |
7d6125c367 | ||
![]() |
2caeea2a14 | ||
![]() |
c3e2447924 | ||
![]() |
f12d888e42 | ||
![]() |
f92966d57a | ||
![]() |
245c75ab2c | ||
![]() |
ef0b6d947e | ||
![]() |
bd511d17e9 | ||
![]() |
b0b73b5d97 | ||
![]() |
a8d2773f5d | ||
![]() |
9749a79f7f | ||
![]() |
6a7cbf1a47 | ||
![]() |
ff1d1474d4 | ||
![]() |
2b18df8398 | ||
![]() |
d978d35533 | ||
![]() |
10cd70e858 | ||
![]() |
0552c2f245 | ||
![]() |
a08df8f9ef | ||
![]() |
1a906b746e | ||
![]() |
c916732f4e | ||
![]() |
22df6d5638 | ||
![]() |
4783f264df | ||
![]() |
64c3ff4573 | ||
![]() |
9c105c988c | ||
![]() |
c7bf92f20b | ||
![]() |
15548d9bb2 | ||
![]() |
441379b6e8 | ||
![]() |
6e5d117720 | ||
![]() |
c1aea93389 | ||
![]() |
e28af61ac2 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
||||
[submodule "src/browser/base/content/zen-components"]
|
||||
path = src/browser/base/content/zen-components
|
||||
url = https://github.com/zen-browser/components
|
||||
[submodule "l10n"]
|
||||
path = l10n
|
||||
url = https://github.com/zen-browser/l10n-packs
|
||||
|
@@ -21,14 +21,14 @@
|
||||
Documentation
|
||||
</a>
|
||||
•
|
||||
<a href="https//zen-browser.app/release-notes/latest">
|
||||
<a href="https://zen-browser.app/release-notes/latest">
|
||||
Release Notes
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 🖥️ Compatibility
|
||||
|
||||
Zen is currently built using firefox version `131.0.3`! 🚀
|
||||
Zen is currently built using firefox version `132.0.1`! 🚀
|
||||
|
||||
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
|
||||
- Part of our mission is to keep Zen up-to-date with the latest version of Firefox, so you can enjoy the latest features and security updates!
|
||||
|
@@ -1,24 +1,25 @@
|
||||
|
||||
# Setting the compiler based on the existence of clang bin directory
|
||||
if test -d "$HOME/.mozbuild/clang/bin"; then
|
||||
export CC="$HOME"/.mozbuild/clang/bin/clang
|
||||
export CXX="$HOME"/.mozbuild/clang/bin/clang++
|
||||
export CC="$HOME/.mozbuild/clang/bin/clang"
|
||||
export CXX="$HOME/.mozbuild/clang/bin/clang++"
|
||||
else
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
fi
|
||||
|
||||
if test "$ZEN_RELEASE"; then
|
||||
|
||||
# Disable DMD and ELF hacks, enable linker lld
|
||||
ac_add_options --disable-dmd
|
||||
ac_add_options --enable-linker=lld
|
||||
ac_add_options --disable-elf-hack
|
||||
|
||||
ac_add_options --disable-necko-wifi
|
||||
|
||||
# Stripping options for release builds
|
||||
ac_add_options --enable-install-strip
|
||||
ac_add_options --enable-strip
|
||||
export STRIP_FLAGS="--strip-debug --strip-unneeded"
|
||||
|
||||
# Using sccache if available
|
||||
if [ -f /home/runner/.mozbuild/sccache/sccache ]; then
|
||||
mk_add_options 'export RUSTC_WRAPPER=/home/runner/.mozbuild/sccache/sccache'
|
||||
mk_add_options 'export CCACHE_CPP2=yes'
|
||||
@@ -26,10 +27,12 @@ if test "$ZEN_RELEASE"; then
|
||||
mk_add_options 'export SCCACHE_GHA_ENABLED=on'
|
||||
fi
|
||||
|
||||
# Enable Profile Guided Optimization
|
||||
export MOZ_PGO=1
|
||||
ac_add_options MOZ_PGO=1
|
||||
|
||||
if test "$SURFER_COMPAT" = "true"; then
|
||||
# Optimization flags for SURFER_COMPAT
|
||||
ac_add_options --enable-optimize="-O3 -march=x86-64"
|
||||
|
||||
export CFLAGS="$CFLAGS -O3 -ffp-contract=fast -march=x86-64"
|
||||
@@ -38,20 +41,23 @@ if test "$ZEN_RELEASE"; then
|
||||
export LDFLAGS="$LDFLAGS -Wl,-O3 -Wl,-mllvm,-fp-contract=fast -march=x86-64"
|
||||
export RUSTFLAGS="$RUSTFLAGS -C target-cpu=x86-64 -C codegen-units=1 -Clink-args=--icf=safe"
|
||||
else
|
||||
# Optimization flags for general release
|
||||
ac_add_options --enable-wasm-avx
|
||||
ac_add_options --enable-optimize="-O3 -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -maes -mpopcnt -mpclmul"
|
||||
|
||||
export CFLAGS="$CFLAGS -O3 -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
|
||||
export CFLAGS="$CFLAGS -O3 -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
|
||||
export CPPFLAGS="$CPPFLAGS -O3 -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
|
||||
export CXXFLAGS="$CXXFLAGS -O3 -flto=thin -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
|
||||
export LDFLAGS="$LDFLAGS -Wl,-O3 -Wl,-mllvm,-fp-contract=fast -march=x86-64-v3"
|
||||
export RUSTFLAGS="$RUSTFLAGS -C target-cpu=x86-64-v3 -C target-feature=+sse4.1 -C target-feature=+avx2 -C codegen-units=1 -Clink-args=--icf=safe"
|
||||
fi
|
||||
|
||||
export VERBOSE=1
|
||||
# Uncomment if you want to enable Polly optimizations
|
||||
#export POLLY="-mllvm -polly -mllvm -polly-2nd-level-tiling -mllvm -polly-loopfusion-greedy -mllvm -polly-pattern-matching-based-opts -mllvm -polly-position=before-vectorizer -mllvm -polly-vectorizer=stripmine"
|
||||
fi
|
||||
|
||||
# Common options
|
||||
ac_add_options --target=x86_64-pc-linux
|
||||
|
||||
ac_add_options --enable-alsa
|
||||
ac_add_options --enable-pulseaudio
|
||||
|
@@ -36,4 +36,7 @@ else
|
||||
export RUSTFLAGS="-C target-feature=+v8.3a -C codegen-units=1 -Ctarget-cpu=apple-m1"
|
||||
fi
|
||||
export VERBOSE=1
|
||||
#export POLLY="-mllvm -polly -mllvm -polly-2nd-level-tiling -mllvm -polly-loopfusion-greedy -mllvm -polly-pattern-matching-based-opts -mllvm -polly-position=before-vectorizer -mllvm -polly-vectorizer=stripmine"
|
||||
|
||||
# Enable polly for macos, since they have a more stable set of GPU drivers,
|
||||
# unlike Linux, which has a lot of different drivers.
|
||||
export POLLY="-mllvm -polly -mllvm -polly-2nd-level-tiling -mllvm -polly-loopfusion-greedy -mllvm -polly-pattern-matching-based-opts -mllvm -polly-position=before-vectorizer -mllvm -polly-vectorizer=stripmine"
|
||||
|
2
l10n
2
l10n
Submodule l10n updated: 3351a875d9...e7c0c33127
@@ -66,15 +66,9 @@ pref("dom.security.sanitizer.enabled", true);
|
||||
pref("urlclassifier.trackingSkipURLs", "*.reddit.com, *.twitter.com, *.twimg.com, *.tiktok.com");
|
||||
pref("urlclassifier.features.socialtracking.skipURLs", "*.instagram.com, *.twitter.com, *.twimg.com");
|
||||
pref("network.cookie.sameSite.noneRequiresSecure", true);
|
||||
pref("browser.download.start_downloads_in_tmp_dir", true);
|
||||
pref("browser.helperApps.deleteTempFileOnExit", true);
|
||||
pref("browser.uitour.enabled", false);
|
||||
|
||||
/** OCSP & CERTS / HPKP ***/
|
||||
pref("security.OCSP.enabled", 0);
|
||||
pref("security.remote_settings.crlite_filters.enabled", true);
|
||||
pref("security.pki.crlite_mode", 2);
|
||||
|
||||
/** SSL / TLS ***/
|
||||
pref("security.ssl.treat_unsafe_negotiation_as_broken", true);
|
||||
pref("browser.xul.error_pages.expert_bad_cert", true);
|
||||
@@ -106,12 +100,6 @@ pref("privacy.userContext.ui.enabled", true);
|
||||
/** SAFE BROWSING ***/
|
||||
pref("browser.safebrowsing.downloads.remote.enabled", false);
|
||||
|
||||
/** MOZILLA ***/
|
||||
pref("permissions.default.desktop-notification", 2);
|
||||
pref("permissions.default.geo", 2);
|
||||
pref("permissions.manager.defaultsUrl", "");
|
||||
pref("webchannel.allowObject.urlWhitelist", "");
|
||||
|
||||
/** TELEMETRY ***/
|
||||
pref("datareporting.policy.dataSubmissionEnabled", false, locked);
|
||||
pref("datareporting.healthreport.uploadEnabled", false, locked);
|
||||
@@ -141,9 +129,6 @@ pref("browser.tabs.crashReporting.sendReport", false);
|
||||
pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);
|
||||
|
||||
/** DETECTION ***/
|
||||
pref("captivedetect.canonicalURL", "");
|
||||
pref("network.captive-portal-service.enabled", false);
|
||||
pref("network.connectivity-service.enabled", false);
|
||||
pref("dom.private-attribution.submission.enabled", false);
|
||||
|
||||
/****************************************************************************
|
||||
|
File diff suppressed because one or more lines are too long
@@ -2,14 +2,14 @@ export var ZenCustomizableUI = new (class {
|
||||
constructor() {}
|
||||
|
||||
TYPE_TOOLBAR = 'toolbar';
|
||||
defaultSidebarIcons = ['zen-sidepanel-button', 'zen-workspaces-button', 'new-tab-button'];
|
||||
defaultSidebarIcons = ['zen-profile-button', 'zen-workspaces-button', 'downloads-button'];
|
||||
|
||||
startup(CustomizableUIInternal) {
|
||||
CustomizableUIInternal.registerArea(
|
||||
'zen-sidebar-top-buttons',
|
||||
{
|
||||
type: this.TYPE_TOOLBAR,
|
||||
defaultPlacements: ['preferences-button', 'zen-expand-sidebar-button', 'zen-profile-button'],
|
||||
defaultPlacements: ['preferences-button', 'zen-expand-sidebar-button', 'zen-sidepanel-button'],
|
||||
defaultCollapsed: null,
|
||||
},
|
||||
true
|
||||
@@ -61,6 +61,7 @@ export var ZenCustomizableUI = new (class {
|
||||
<image id="zen-profile-button-icon" />
|
||||
</vbox>
|
||||
</toolbarbutton>
|
||||
<toolbarbutton removeable="true" class="toolbarbutton-1 zen-sidebar-action-button" id="zen-sidepanel-button" data-l10n-id="sidebar-zen-sidepanel" onclick="gZenBrowserManagerSidebar.toggle();"></toolbarbutton>
|
||||
</hbox>
|
||||
</toolbar>
|
||||
`);
|
||||
@@ -97,7 +98,7 @@ export var ZenCustomizableUI = new (class {
|
||||
}
|
||||
|
||||
_hideToolbarButtons(window) {
|
||||
const elementsToHide = ['alltabs-button'];
|
||||
const elementsToHide = ['alltabs-button', 'new-tab-button'];
|
||||
for (let id of elementsToHide) {
|
||||
const elem = window.document.getElementById(id);
|
||||
if (elem) {
|
||||
|
@@ -35,6 +35,7 @@
|
||||
// Disable smooth scroll
|
||||
gBrowser.tabContainer.arrowScrollbox.smoothScroll = false;
|
||||
|
||||
gZenPinnedTabManager.initTabs();
|
||||
ZenWorkspaces.init();
|
||||
gZenUIManager.init();
|
||||
gZenVerticalTabsManager.init();
|
||||
@@ -72,6 +73,7 @@
|
||||
const tabs = document.getElementById('tabbrowser-arrowscrollbox');
|
||||
tabs.style.maxHeight = '0px'; // reset to 0
|
||||
const toolbarRect = toolbarItems.getBoundingClientRect();
|
||||
let height = toolbarRect.height;
|
||||
// -5 for the controls padding
|
||||
let totalHeight = toolbarRect.height - (this.contentElementSeparation * 2) - 5;
|
||||
// remove the height from other elements that aren't hidden
|
||||
@@ -121,7 +123,7 @@
|
||||
}
|
||||
|
||||
// remove all styles except for the width, since we are xulstoring the complet style list
|
||||
const width = toolbox.style.width || '250px';
|
||||
const width = toolbox.style.width || '270px';
|
||||
toolbox.removeAttribute('style');
|
||||
toolbox.style.width = width;
|
||||
|
||||
|
@@ -47,7 +47,9 @@ var gZenUIManager = {
|
||||
|
||||
onPopupShowing(showEvent) {
|
||||
for (const el of this._popupTrackingElements) {
|
||||
if (!el.contains(event.explicitOriginalTarget)) {
|
||||
// target may be inside a shadow root, not directly under the element
|
||||
// we also ignore menus inside panels
|
||||
if (!el.contains(showEvent.explicitOriginalTarget) || (showEvent.explicitOriginalTarget instanceof Element && showEvent.explicitOriginalTarget?.closest('panel'))) {
|
||||
continue;
|
||||
}
|
||||
document.removeEventListener('mousemove', this.__removeHasPopupAttribute);
|
||||
@@ -115,7 +117,6 @@ var gZenVerticalTabsManager = {
|
||||
const onHover = Services.prefs.getBoolPref('zen.view.sidebar-expanded.on-hover');
|
||||
const expanded = Services.prefs.getBoolPref('zen.view.sidebar-expanded');
|
||||
const sidebar = this.navigatorToolbox;
|
||||
|
||||
|
||||
if (onHover) {
|
||||
// if the sidebar is not expanded, and hover detection is enabled, show the sidebar
|
||||
|
@@ -1,21 +1,18 @@
|
||||
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
|
||||
index 108160d9469d44f47c93a3808402e4b27ff59777..39a7b691302bf13e1b5990f46428cff6807a8e56 100644
|
||||
index 7f71abe7d80e4c09dd088517ec9ef106c7cb8654..281a3716d56edda243bfca7ebbe0797d75c844e7 100644
|
||||
--- a/browser/base/content/browser-box.inc.xhtml
|
||||
+++ b/browser/base/content/browser-box.inc.xhtml
|
||||
@@ -23,11 +23,16 @@
|
||||
@@ -22,7 +22,13 @@
|
||||
<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"/>
|
||||
<vbox id="appcontent" flex="1">
|
||||
+ <html:div id="zen-appcontent-navbar-container"></html:div>
|
||||
<!-- gNotificationBox will be added here lazily. -->
|
||||
+ <hbox id="zen-tabbox-wrapper" flex="1">
|
||||
<tabbox id="tabbrowser-tabbox"
|
||||
flex="1" tabcontainer="tabbrowser-tabs">
|
||||
+#include zen-sidebar-panel.inc.xhtml
|
||||
+#include zen-splitview-overlay.inc.xhtml
|
||||
<tabpanels id="tabbrowser-tabpanels"
|
||||
flex="1" selectedIndex="0"/>
|
||||
</tabbox>
|
||||
+ </hbox>
|
||||
</vbox>
|
||||
+<vbox flex="1" id="zen-appcontent-wrapper">
|
||||
+<html:div id="zen-appcontent-navbar-container"></html:div>
|
||||
+<hbox id="zen-tabbox-wrapper" flex="1">
|
||||
<tabbox id="tabbrowser-tabbox" flex="1" tabcontainer="tabbrowser-tabs">
|
||||
+ #include zen-tabbrowser-elements.inc.xhtml
|
||||
<tabpanels id="tabbrowser-tabpanels" flex="1" selectedIndex="0"/>
|
||||
</tabbox>
|
||||
</hbox>
|
||||
+</vbox>
|
||||
+</hbox>
|
||||
|
@@ -1,11 +1,11 @@
|
||||
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js
|
||||
index bee5309c04775adff8652bfe6c54b2d466e821ac..cfeaf7cf2e98c35e76bdd5451f90b004a04d4474 100644
|
||||
index 9df41bb3c82919839ee1408aa4d177ea7ee40a56..e37c64fa2c2ea39762be4285a1a7055463ded537 100644
|
||||
--- a/browser/base/content/browser-init.js
|
||||
+++ b/browser/base/content/browser-init.js
|
||||
@@ -143,13 +143,15 @@ var gBrowserInit = {
|
||||
gNavToolbox.palette = document.getElementById(
|
||||
"BrowserToolbarPalette"
|
||||
).content;
|
||||
@@ -152,13 +152,15 @@ var gBrowserInit = {
|
||||
// tell CUI to ignore this element when it builds the toolbar areas
|
||||
elem.setAttribute("skipintoolbarset", "true");
|
||||
}
|
||||
+ ZenCustomizableUI.init(window);
|
||||
for (let area of CustomizableUI.areas) {
|
||||
let type = CustomizableUI.getAreaType(area);
|
||||
@@ -16,16 +16,16 @@ index bee5309c04775adff8652bfe6c54b2d466e821ac..cfeaf7cf2e98c35e76bdd5451f90b004
|
||||
}
|
||||
}
|
||||
+ ZenCustomizableUI.registerToolbarNodes(window);
|
||||
BrowserSearch.initPlaceHolder();
|
||||
|
||||
// Hack to ensure that the various initial pages favicon is loaded
|
||||
@@ -239,6 +241,10 @@ var gBrowserInit = {
|
||||
for (let elem of nonRemovables) {
|
||||
elem.setAttribute("removable", "false");
|
||||
elem.removeAttribute("skipintoolbarset");
|
||||
@@ -253,6 +255,10 @@ var gBrowserInit = {
|
||||
gPrivateBrowsingUI.init();
|
||||
BrowserSearch.init();
|
||||
BrowserPageActions.init();
|
||||
+
|
||||
+ Services.scriptloader.loadSubScript("chrome://browser/content/ZenStartup.mjs", window);
|
||||
+ Services.scriptloader.loadSubScript("chrome://browser/content/zenThemeModifier.js", window);
|
||||
+Services.scriptloader.loadSubScript("chrome://browser/content/ZenStartup.mjs", window);
|
||||
+Services.scriptloader.loadSubScript("chrome://browser/content/zenThemeModifier.js", window);
|
||||
+
|
||||
if (gToolbarKeyNavEnabled) {
|
||||
ToolbarKeyboardNavigator.init();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
|
||||
index a29d7a84e84651ea0bdc9be8e4ac650bde2e048a..bc22e55a66686fbae95047686f845f71a0c3aae9 100644
|
||||
index ccd83c15d0d73a1e53bdbfdfbe6fed43a26c961d..f18a6abf7debb97539a4cdf8422315b4dff08adb 100644
|
||||
--- a/browser/base/content/browser.js
|
||||
+++ b/browser/base/content/browser.js
|
||||
@@ -32,6 +32,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
||||
@@ -10,7 +10,7 @@ index a29d7a84e84651ea0bdc9be8e4ac650bde2e048a..bc22e55a66686fbae95047686f845f71
|
||||
DevToolsSocketStatus:
|
||||
"resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs",
|
||||
DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs",
|
||||
@@ -629,6 +630,15 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
@@ -632,6 +633,15 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
false
|
||||
);
|
||||
|
||||
@@ -26,27 +26,19 @@ index a29d7a84e84651ea0bdc9be8e4ac650bde2e048a..bc22e55a66686fbae95047686f845f71
|
||||
customElements.setElementCreationCallback("screenshots-buttons", () => {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://browser/content/screenshots/screenshots-buttons.js",
|
||||
@@ -3435,6 +3445,10 @@ var XULBrowserWindow = {
|
||||
@@ -3438,6 +3448,11 @@ var XULBrowserWindow = {
|
||||
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
|
||||
TranslationsParent.onLocationChange(gBrowser.selectedBrowser);
|
||||
|
||||
+ gZenViewSplitter.onLocationChange(gBrowser.selectedBrowser);
|
||||
+ ZenWorkspaces.onLocationChange(gBrowser.selectedBrowser);
|
||||
+ gZenTabUnloader.onLocationChange(gBrowser.selectedBrowser);
|
||||
+ gZenGlanceManager.onLocationChange(gBrowser.selectedBrowser);
|
||||
+
|
||||
PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser);
|
||||
|
||||
if (!gMultiProcessBrowser) {
|
||||
@@ -4769,7 +4783,7 @@ function setToolbarVisibility(
|
||||
);
|
||||
}
|
||||
|
||||
- const overlapAttr = "BookmarksToolbarOverlapsBrowser";
|
||||
+ const overlapAttr = "BookmarksToolbarOverlapsBrowser__ignore"; // Original string was "BookmarksToolbarOverlapsBrowser" but it's not used and it only bugs the UI.
|
||||
switch (isVisible) {
|
||||
case true:
|
||||
case "always":
|
||||
@@ -7609,6 +7623,12 @@ var gDialogBox = {
|
||||
@@ -7289,6 +7304,12 @@ var gDialogBox = {
|
||||
parentElement.showModal();
|
||||
this._didOpenHTMLDialog = true;
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js
|
||||
index 6992d22069643b58b249ae16edfe354304e56a39..38a006799feb5f4f41e582074901bb7226397ae1 100644
|
||||
index 62b3665394a8256ebedf88a8f10bb6766977cf57..12ac643f913290b20fb7adba8f224f49bb82bf5c 100644
|
||||
--- a/browser/base/content/browser-places.js
|
||||
+++ b/browser/base/content/browser-places.js
|
||||
@@ -243,6 +243,8 @@ var StarUI = {
|
||||
@@ -252,6 +252,8 @@ var StarUI = {
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ index 6992d22069643b58b249ae16edfe354304e56a39..38a006799feb5f4f41e582074901bb72
|
||||
let onPanelReady = fn => {
|
||||
let target = this.panel;
|
||||
if (target.parentNode) {
|
||||
@@ -294,6 +296,21 @@ var StarUI = {
|
||||
@@ -303,6 +305,21 @@ var StarUI = {
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d945d53ac 100644
|
||||
index 12fa0cf79aade28581016adf96df85386dabdcef..846add9b040abf0f7378ebaaadef007013f5a1ec 100644
|
||||
--- a/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
@@ -2,7 +2,7 @@
|
||||
@@ -11,7 +11,7 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
|
||||
|
||||
<vbox id="titlebar">
|
||||
<!-- Menu -->
|
||||
@@ -32,10 +32,11 @@
|
||||
@@ -32,13 +32,14 @@
|
||||
|
||||
<hbox class="titlebar-spacer" type="pre-tabs"/>
|
||||
|
||||
@@ -20,11 +20,14 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
|
||||
<toolbartabstop/>
|
||||
<hbox id="TabsToolbar-customization-target" flex="1">
|
||||
<toolbarbutton id="firefox-view-button"
|
||||
+ hidden="true"
|
||||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
data-l10n-id="toolbar-button-firefox-view-2"
|
||||
role="button"
|
||||
@@ -50,7 +51,7 @@
|
||||
+ hidden="true"
|
||||
aria-pressed="false"
|
||||
oncommand="FirefoxViewHandler.openTab();"
|
||||
onmousedown="FirefoxViewHandler.openToolbarMouseEvent(event);"
|
||||
@@ -50,16 +51,21 @@
|
||||
aria-multiselectable="true"
|
||||
setfocus="false"
|
||||
tooltip="tabbrowser-tab-tooltip"
|
||||
@@ -33,16 +36,22 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
|
||||
stopwatchid="FX_TAB_CLICK_MS">
|
||||
<hbox class="tab-drop-indicator" hidden="true"/>
|
||||
# If the name (tabbrowser-arrowscrollbox) or structure of this changes
|
||||
@@ -66,7 +67,7 @@
|
||||
tooltip="dynamic-shortcut-tooltip"
|
||||
data-l10n-id="tabs-toolbar-new-tab"/>
|
||||
</html:div>
|
||||
- <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" style="min-width: 1px;" clicktoscroll="" scrolledtostart="" scrolledtoend="">
|
||||
+ <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="vertical" style="min-width: 1px;" clicktoscroll="" scrolledtostart="" scrolledtoend="">
|
||||
# significantly, there is an optimization in
|
||||
# DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered based
|
||||
# the current structure that we may want to revisit.
|
||||
+ <html:div id="zen-essentials-container"></html:div>
|
||||
+ <hbox id="zen-current-workspace-indicator">
|
||||
+ <hbox id="zen-current-workspace-indicator-icon"></hbox>
|
||||
+ <hbox id="zen-current-workspace-indicator-name"></hbox>
|
||||
+ </hbox>
|
||||
<html:div id="vertical-pinned-tabs-container"></html:div>
|
||||
<html:div id="vertical-pinned-tabs-container-separator"></html:div>
|
||||
- <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
|
||||
+ <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="vertical" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
|
||||
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="" fadein=""/>
|
||||
<hbox id="tabbrowser-arrowscrollbox-periphery">
|
||||
<toolbartabstop/>
|
||||
@@ -113,9 +114,10 @@
|
||||
@@ -113,9 +119,10 @@
|
||||
<toolbarbutton id="content-analysis-indicator"
|
||||
oncommand="ContentAnalysis.showPanel(this, PanelUI);"
|
||||
class="toolbarbutton-1 content-analysis-indicator-icon"/>
|
||||
@@ -55,15 +64,7 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
|
||||
</toolbar>
|
||||
|
||||
</vbox>
|
||||
@@ -471,6 +473,7 @@
|
||||
|
||||
<toolbarbutton id="fxa-toolbar-menu-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
|
||||
badged="true"
|
||||
+ hidden="true"
|
||||
delegatesanchor="true"
|
||||
onmousedown="gSync.toggleAccountPanel(this, event)"
|
||||
onkeypress="gSync.toggleAccountPanel(this, event)"
|
||||
@@ -522,6 +525,7 @@
|
||||
@@ -531,6 +538,7 @@
|
||||
consumeanchor="PanelUI-button"
|
||||
data-l10n-id="appmenu-menu-button-closed2"/>
|
||||
</toolbaritem>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/base/content/nsContextMenu.sys.mjs b/browser/base/content/nsContextMenu.sys.mjs
|
||||
index 6c2317b8036378c6b8e0ad9a4fe71388bcb385f5..4bf277b02d3c69efc0f2d46c8b0f9deb7d7fb45a 100644
|
||||
index 7f3dc585937c5ac96c0d09a786515305afb4fe1a..9216931e7d805743f0b00e74039be042456e63f3 100644
|
||||
--- a/browser/base/content/nsContextMenu.sys.mjs
|
||||
+++ b/browser/base/content/nsContextMenu.sys.mjs
|
||||
@@ -1153,6 +1153,13 @@ export class nsContextMenu {
|
||||
@@ -1047,6 +1047,13 @@ export class nsContextMenu {
|
||||
!this.isSecureAboutPage()
|
||||
);
|
||||
|
||||
@@ -13,6 +13,6 @@ index 6c2317b8036378c6b8e0ad9a4fe71388bcb385f5..4bf277b02d3c69efc0f2d46c8b0f9deb
|
||||
+
|
||||
+ this.showItem("context-zenSplitLink", this.onLink && !this.onMailtoLink && !this.onTelLink);
|
||||
+
|
||||
let copyLinkSeparator = this.document.getElementById(
|
||||
"context-sep-copylink"
|
||||
);
|
||||
let canNotStrip =
|
||||
lazy.STRIP_ON_SHARE_CAN_DISABLE && !this.#canStripParams();
|
||||
|
||||
|
@@ -16,23 +16,27 @@
|
||||
<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-glance.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-profile-dialog.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-sidebar-panels.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-popup.css" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-compact-mode.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/skin/zen-icons/icons.css" />
|
||||
|
||||
</linkset>
|
||||
|
||||
<!-- Scripts used all over the browser -->
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenActorsManager.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenGlanceManager.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs" />
|
||||
<script src="chrome://browser/content/ZenUIManager.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenSidebarManager.mjs"/>
|
||||
<script src="chrome://browser/content/zen-components/ZenTabUnloader.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenWorkspaces.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenWorkspacesSync.mjs" />
|
||||
<script src="chrome://browser/content/zen-components/ZenGradientGenerator.mjs" />
|
||||
|
@@ -4,21 +4,24 @@
|
||||
content/browser/ZenStartup.mjs (content/ZenStartup.mjs)
|
||||
content/browser/ZenUIManager.mjs (content/ZenUIManager.mjs)
|
||||
content/browser/ZenCustomizableUI.sys.mjs (content/ZenCustomizableUI.sys.mjs)
|
||||
content/browser/zen-components/ZenCompactMode.mjs (content/zen-components/src/ZenCompactMode.mjs)
|
||||
content/browser/zen-components/ZenViewSplitter.mjs (content/zen-components/src/ZenViewSplitter.mjs)
|
||||
content/browser/zen-components/ZenThemesCommon.mjs (content/zen-components/src/ZenThemesCommon.mjs)
|
||||
content/browser/zen-components/ZenWorkspaces.mjs (content/zen-components/src/ZenWorkspaces.mjs)
|
||||
content/browser/zen-components/ZenWorkspacesStorage.mjs (content/zen-components/src/ZenWorkspacesStorage.mjs)
|
||||
content/browser/zen-components/ZenWorkspacesSync.mjs (content/zen-components/src/ZenWorkspacesSync.mjs)
|
||||
content/browser/zen-components/ZenSidebarManager.mjs (content/zen-components/src/ZenSidebarManager.mjs)
|
||||
content/browser/zen-components/ZenProfileDialogUI.mjs (content/zen-components/src/ZenProfileDialogUI.mjs)
|
||||
content/browser/zen-components/ZenKeyboardShortcuts.mjs (content/zen-components/src/ZenKeyboardShortcuts.mjs)
|
||||
content/browser/zen-components/ZenThemeBuilder.mjs (content/zen-components/src/ZenThemeBuilder.mjs)
|
||||
content/browser/zen-components/ZenThemesImporter.mjs (content/zen-components/src/ZenThemesImporter.mjs)
|
||||
content/browser/zen-components/ZenTabUnloader.mjs (content/zen-components/src/ZenTabUnloader.mjs)
|
||||
content/browser/zen-components/ZenPinnedTabManager.mjs (content/zen-components/src/ZenPinnedTabManager.mjs)
|
||||
content/browser/zen-components/ZenCommonUtils.mjs (content/zen-components/src/ZenCommonUtils.mjs)
|
||||
content/browser/zen-components/ZenGradientGenerator.mjs (content/zen-components/src/ZenGradientGenerator.mjs)
|
||||
content/browser/zen-components/ZenCompactMode.mjs (zen-components/ZenCompactMode.mjs)
|
||||
content/browser/zen-components/ZenViewSplitter.mjs (zen-components/ZenViewSplitter.mjs)
|
||||
content/browser/zen-components/ZenThemesCommon.mjs (zen-components/ZenThemesCommon.mjs)
|
||||
content/browser/zen-components/ZenWorkspaces.mjs (zen-components/ZenWorkspaces.mjs)
|
||||
content/browser/zen-components/ZenWorkspacesStorage.mjs (zen-components/ZenWorkspacesStorage.mjs)
|
||||
content/browser/zen-components/ZenWorkspacesSync.mjs (zen-components/ZenWorkspacesSync.mjs)
|
||||
content/browser/zen-components/ZenSidebarManager.mjs (zen-components/ZenSidebarManager.mjs)
|
||||
content/browser/zen-components/ZenProfileDialogUI.mjs (zen-components/ZenProfileDialogUI.mjs)
|
||||
content/browser/zen-components/ZenKeyboardShortcuts.mjs (zen-components/ZenKeyboardShortcuts.mjs)
|
||||
content/browser/zen-components/ZenThemeBuilder.mjs (zen-components/ZenThemeBuilder.mjs)
|
||||
content/browser/zen-components/ZenThemesImporter.mjs (zen-components/ZenThemesImporter.mjs)
|
||||
content/browser/zen-components/ZenTabUnloader.mjs (zen-components/ZenTabUnloader.mjs)
|
||||
content/browser/zen-components/ZenPinnedTabsStorage.mjs (zen-components/ZenPinnedTabsStorage.mjs)
|
||||
content/browser/zen-components/ZenPinnedTabManager.mjs (zen-components/ZenPinnedTabManager.mjs)
|
||||
content/browser/zen-components/ZenCommonUtils.mjs (zen-components/ZenCommonUtils.mjs)
|
||||
content/browser/zen-components/ZenGradientGenerator.mjs (zen-components/ZenGradientGenerator.mjs)
|
||||
content/browser/zen-components/ZenGlanceManager.mjs (zen-components/ZenGlanceManager.mjs)
|
||||
content/browser/zen-components/ZenActorsManager.mjs (zen-components/ZenActorsManager.mjs)
|
||||
|
||||
content/browser/zen-styles/zen-theme.css (content/zen-styles/zen-theme.css)
|
||||
content/browser/zen-styles/zen-buttons.css (content/zen-styles/zen-buttons.css)
|
||||
@@ -32,6 +35,7 @@
|
||||
content/browser/zen-styles/zen-sidebar.css (content/zen-styles/zen-sidebar.css)
|
||||
content/browser/zen-styles/zen-toolbar.css (content/zen-styles/zen-toolbar.css)
|
||||
content/browser/zen-styles/zen-decks.css (content/zen-styles/zen-decks.css)
|
||||
content/browser/zen-styles/zen-glance.css (content/zen-styles/zen-glance.css)
|
||||
content/browser/zen-styles/zen-browser-container.css (content/zen-styles/zen-browser-container.css)
|
||||
content/browser/zen-styles/zen-workspaces.css (content/zen-styles/zen-workspaces.css)
|
||||
content/browser/zen-styles/zen-profile-dialog.css (content/zen-styles/zen-profile-dialog.css)
|
||||
@@ -52,5 +56,7 @@
|
||||
content/browser/zen-images/gradient-display.png (content/zen-images/gradient-display.png)
|
||||
|
||||
# Actors
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (content/zen-components/src/actors/ZenThemeMarketplaceParent.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (content/zen-components/src/actors/ZenThemeMarketplaceChild.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (zen-components/actors/ZenThemeMarketplaceParent.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (zen-components/actors/ZenThemeMarketplaceChild.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenGlanceChild.sys.mjs (zen-components/actors/ZenGlanceChild.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenGlanceParent.sys.mjs (zen-components/actors/ZenGlanceParent.sys.mjs)
|
||||
|
Submodule src/browser/base/content/zen-components deleted from b035ca8b0f
4
src/browser/base/content/zen-glance.inc.xhtml
Normal file
4
src/browser/base/content/zen-glance.inc.xhtml
Normal file
@@ -0,0 +1,4 @@
|
||||
<vbox id="zen-glance-sidebar-container">
|
||||
<toolbarbutton id="zen-glance-sidebar-close" class="toolbarbutton-1" oncommand="gZenGlanceManager.closeGlance()"/>
|
||||
<toolbarbutton id="zen-glance-sidebar-open" class="toolbarbutton-1" oncommand="gZenGlanceManager.fullyOpenGlance()"/>
|
||||
</vbox>
|
@@ -4,5 +4,4 @@
|
||||
<script type="text/javascript">
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenCommonUtils.mjs", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenKeyboardShortcuts.mjs", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenPinnedTabManager.mjs", this);
|
||||
</script>
|
@@ -7,5 +7,4 @@
|
||||
skipintoolbarset="true"
|
||||
context="toolbar-context-menu"
|
||||
mode="icons">
|
||||
<toolbarbutton removeable="true" class="toolbarbutton-1 zen-sidebar-action-button" id="zen-sidepanel-button" data-l10n-id="sidebar-zen-sidepanel" onclick="gZenBrowserManagerSidebar.toggle();"></toolbarbutton>
|
||||
</toolbar>
|
@@ -213,3 +213,129 @@
|
||||
right: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark: Zen Glance */
|
||||
@keyframes zen-glance-overlay-animation {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-glance-overlay-animation-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-glance-content-animation {
|
||||
/* make the box appear from initial width/height and x/y coordinates */
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
30% {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) translateZ(0);
|
||||
}
|
||||
|
||||
70% {
|
||||
/* make the box grow to full width/height */
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) translateZ(0);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 87%;
|
||||
height: 87%;
|
||||
}
|
||||
|
||||
100% {
|
||||
/* make the box shrink to final width/height and x/y coordinates */
|
||||
transform: translate(-50%, -50%) translateZ(0);
|
||||
opacity: 1;
|
||||
width: 85%;
|
||||
height: 85%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-glance-content-animation-out {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
5% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 1;
|
||||
width: 85%;
|
||||
height: 85%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-glance-buttons-animation-full {
|
||||
from {
|
||||
width: 85%;
|
||||
height: 85%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
to {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-glance-loading-animation {
|
||||
0% {
|
||||
opacity: 1;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
90% {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-glance-buttons-animation {
|
||||
from {
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-100%) translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
@@ -4,16 +4,19 @@
|
||||
border-radius: var(--zen-webview-border-radius, var(--zen-border-radius));
|
||||
position: relative;
|
||||
|
||||
box-shadow: 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.1), var(--zen-colors-border));
|
||||
box-shadow: 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.5));
|
||||
|
||||
& .browserContainer {
|
||||
overflow: hidden;
|
||||
border-radius: var(--zen-webview-border-radius, var(--zen-border-radius));
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: 'zen.view.experimental-rounded-view') {
|
||||
&.deck-selected .browserContainer {
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
@media (-moz-bool-pref: 'zen.view.experimental-rounded-view') {
|
||||
#tabbrowser-tabpanels {
|
||||
mix-blend-mode: multiply;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
isolation: isolate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#browser,
|
||||
#appcontent,
|
||||
|
||||
#tabbrowser-tabpanels {
|
||||
background: transparent !important;
|
||||
}
|
||||
@@ -14,6 +13,7 @@
|
||||
& #tabbrowser-tabpanels {
|
||||
padding-right: var(--zen-element-separation);
|
||||
padding-bottom: var(--zen-element-separation);
|
||||
padding-left: 1px; /* Shadow offset */
|
||||
|
||||
#browser:has(#navigator-toolbox[zen-right-side='true']) & {
|
||||
padding-right: 0;
|
||||
@@ -31,17 +31,40 @@
|
||||
|
||||
#browser {
|
||||
width: 100%;
|
||||
background: var(--zen-main-browser-background);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--zen-grainy-background-opacity, 0);
|
||||
}
|
||||
}
|
||||
|
||||
:root:not([inDOMFullscreen='true']) #appcontent,
|
||||
#sidebar-box {
|
||||
/** Sidebar is already hidden in full screen mode */
|
||||
border: none;
|
||||
}
|
||||
|
||||
#zen-main-app-wrapper {
|
||||
background: var(--zen-main-browser-background);
|
||||
|
||||
@supports (-moz-osx-font-smoothing: auto) {
|
||||
#zen-main-app-wrapper,
|
||||
#zen-appcontent-wrapper,
|
||||
#zen-sidebar-splitter {
|
||||
appearance: -moz-window-titlebar !important;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-main-app-wrapper {
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -59,22 +82,14 @@
|
||||
animation: zen-main-app-wrapper-animation .5s ease-in-out forwards;
|
||||
border-radius: env(-moz-gtk-csd-titlebar-radius);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--zen-grainy-background-opacity, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zen-appcontent-wrapper {
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#tabbrowser-tabbox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -85,6 +100,8 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:root:not([inDOMFullscreen='true']) #tabbrowser-tabbox {
|
||||
padding: 2px; /* To allow the web view's shadow to be visible */
|
||||
@media (-moz-platform: macos) {
|
||||
.titlebar-buttonbox-container {
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
}
|
||||
|
@@ -16,10 +16,13 @@ xul|button {
|
||||
transition: 0.1s;
|
||||
min-width: 100px !important;
|
||||
font-weight: 500 !important;
|
||||
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05) !important;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.95);
|
||||
button:not(#zen-workspaces-button):active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
html|button:not(:is(.tab-button, .ghost-button, .button-toggle, .button-edit, .button-add, )),
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
#navigator-toolbox {
|
||||
--zen-toolbox-max-width: 54px !important;
|
||||
--zen-compact-float: calc(var(--zen-element-separation) + 1px);
|
||||
--zen-compact-float: calc(var(--zen-element-separation) - 1px);
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
|
||||
@@ -41,6 +41,7 @@
|
||||
padding-top: 0 !important;
|
||||
|
||||
#browser:has(#navigator-toolbox[zen-right-side='true']) & {
|
||||
--zen-compact-float: calc(var(--zen-element-separation) + 1px);
|
||||
padding-left: unset !important;
|
||||
padding-right: var(--zen-compact-float) !important;
|
||||
left: calc(100% - var(--zen-element-separation));
|
||||
@@ -49,13 +50,19 @@
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
background: var(--zen-main-browser-background) !important;
|
||||
background-attachment: fixed !important;
|
||||
background-size: 2000px !important; /* Dont ask me why */
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
border-radius: calc(var(--zen-border-radius) + 2px);
|
||||
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
border-radius: calc(var(--zen-border-radius) - 2px);
|
||||
padding: var(--zen-toolbox-padding) !important;
|
||||
position: relative;
|
||||
background: var(--zen-dialog-background);
|
||||
outline: 1px solid light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
|
||||
|
||||
@media (-moz-bool-pref: 'zen.view.compact.color-sidebar') {
|
||||
background: var(--zen-main-browser-background-toolbar) !important;
|
||||
background-attachment: fixed !important;
|
||||
background-size: 2000px !important; /* Dont ask me why */
|
||||
backdrop-filter: blur(5px) !important;
|
||||
}
|
||||
}
|
||||
/* Mark: toolbox as collapsed */
|
||||
#zen-tabbox-wrapper > #navigator-toolbox:not(#navigator-toolbox:is(#navigator-toolbox[zen-expanded='true'])) {
|
||||
@@ -112,26 +119,26 @@
|
||||
}
|
||||
|
||||
#zen-appcontent-navbar-container {
|
||||
--zen-compact-toolbar-offset: 5px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform: translateY(calc(-100% + var(--zen-element-separation) + 1px));
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
background: var(--zen-themed-toolbar-bg);
|
||||
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.3) !important;
|
||||
border-bottom-left-radius: var(--zen-border-radius);
|
||||
border-bottom-right-radius: var(--zen-border-radius);
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
border-top-width: 0px;
|
||||
transition: all 0.1s ease-in-out;
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
background: var(--zen-dialog-background);
|
||||
|
||||
:root[tabsintitlebar][sizemode='normal']:not([gtktiledwindow='true']) & {
|
||||
border-top-left-radius: env(-moz-gtk-csd-titlebar-radius);
|
||||
border-top-right-radius: env(-moz-gtk-csd-titlebar-radius);
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
@media (-moz-bool-pref: 'zen.view.compact.color-toolbar') {
|
||||
background-attachment: fixed;
|
||||
backdrop-filter: blur(5px);
|
||||
background: var(--zen-main-browser-background-toolbar);
|
||||
background-size: 100% 2000px;
|
||||
border-bottom: 1px solid var(--zen-colors-border);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,7 @@
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view='true'] > [zen-split='true'], #zen-splitview-dropzone {
|
||||
flex: 1;
|
||||
margin: calc(var(--zen-split-column-gap) / 2) calc(var(--zen-split-row-gap) / 2);
|
||||
margin: calc(var(--zen-split-column-gap) / 2) calc(var(--zen-split-row-gap) / 2 + 1px);
|
||||
position: absolute !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view='true'] .browserSidebarContainer.deck-selected {
|
||||
outline: 2px solid var(--zen-primary-color) !important;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
#tabbrowser-tabbox {
|
||||
|
141
src/browser/base/content/zen-styles/zen-glance.css
Normal file
141
src/browser/base/content/zen-styles/zen-glance.css
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
.browserSidebarContainer:has([zen-glance-selected]),
|
||||
.browserSidebarContainer.zen-glance-overlay {
|
||||
visibility: inherit;
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels {
|
||||
transition: transform 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels:has(.zen-glance-background) {
|
||||
transform: scale(0.99);
|
||||
backdrop-filter: blur(5px);
|
||||
|
||||
& .zen-glance-background {
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-glance-sidebar-container {
|
||||
display: none;
|
||||
|
||||
& toolbarbutton:hover {
|
||||
background: var(--button-background-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.browserSidebarContainer.zen-glance-overlay {
|
||||
&[fade-out='true'] {
|
||||
background: transparent;
|
||||
opacity: 1;
|
||||
|
||||
& .browserContainer {
|
||||
animation: zen-glance-content-animation-out .3s ease-in-out forwards !important;
|
||||
animation-direction: reverse !important;
|
||||
|
||||
& browser {
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease-in-out;
|
||||
}
|
||||
|
||||
& #zen-glance-sidebar-container {
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .browserContainer {
|
||||
background: var(--zen-dialog-background);
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
transform: translate(var(--initial-x), var(--initial-y));
|
||||
width: var(--initial-width);
|
||||
height: var(--initial-height);
|
||||
position: absolute;
|
||||
border-radius: var(--zen-border-radius);
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
flex: unset !important;
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
|
||||
&[has-finished-animation='true'] {
|
||||
position: relative !important;
|
||||
transition: 0s !important;
|
||||
transform: none !important;
|
||||
margin: auto !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
& #zen-glance-sidebar-container {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
top: 10%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
opacity: 0;
|
||||
background: var(--zen-dialog-background);
|
||||
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
border-right: none;
|
||||
|
||||
border-top-left-radius: var(--zen-border-radius);
|
||||
border-bottom-left-radius: var(--zen-border-radius);
|
||||
|
||||
padding: 5px;
|
||||
gap: 6px;
|
||||
|
||||
animation: zen-glance-buttons-animation 0.2s ease-in-out forwards;
|
||||
animation-delay: 0.3s;
|
||||
|
||||
& toolbarbutton {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
& label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& browser {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
transition-delay: 0.2s;
|
||||
}
|
||||
|
||||
&[animate-full='true'] {
|
||||
opacity: 1;
|
||||
animation: zen-glance-buttons-animation-full 0.3s ease-in-out forwards !important;
|
||||
|
||||
& #zen-glance-sidebar-container {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&[animate='true'] {
|
||||
animation: zen-glance-content-animation .4s ease-in-out forwards;
|
||||
animation-delay: 0.1s;
|
||||
|
||||
&:not([animate-end='true']) {
|
||||
pointer-events: none;
|
||||
|
||||
& browser {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
& #zen-glance-sidebar-container {
|
||||
opacity: 0;
|
||||
animation: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
panel[type='arrow'][animate][animate='open'] {
|
||||
animation: zen-jello-animation 0.2s ease-in-out;
|
||||
panel[type='arrow'][animate][animate='open']::part(content) {
|
||||
animation: zen-jello-animation 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
panel[type='arrow'][animate]:not([animate='open']) {
|
||||
animation: zen-jello-out-animation 0.2s ease-in-out;
|
||||
panel[type='arrow'][animate]:not([animate='open'])::part(content) {
|
||||
animation: zen-jello-out-animation 0.3s ease-in-out;
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
}
|
||||
|
||||
#PanelUI-zen-profiles-header {
|
||||
width: 280px;
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
background: color-mix(in srgb, var(--zen-primary-color) 80%, white 20%);
|
||||
position: relative;
|
||||
|
@@ -21,14 +21,15 @@
|
||||
}
|
||||
|
||||
& #zen-profile-button-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
list-style-image: var(--avatar-image-url);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
list-style-image: var(--avatar-image-url);
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,4 +73,4 @@
|
||||
/* Menubar */
|
||||
#toolbar-menubar {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@
|
||||
/* Watermark */
|
||||
|
||||
#zen-watermark {
|
||||
--zen-themed-toolbar-bg-transparency: 1 !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@@ -16,7 +16,19 @@
|
||||
}
|
||||
|
||||
#browser {
|
||||
--zen-toolbox-padding: 5px;
|
||||
--zen-toolbox-padding: calc(var(--zen-element-separation) / 1.5);
|
||||
}
|
||||
|
||||
#vertical-pinned-tabs-container-separator {
|
||||
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.1));
|
||||
margin: 8px auto;
|
||||
border: none;
|
||||
height: 1px;
|
||||
width: 98%;
|
||||
|
||||
#vertical-pinned-tabs-container:not(:has(tab:not([hidden]))) + & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#navigator-toolbox {
|
||||
@@ -24,7 +36,7 @@
|
||||
--tab-border-radius: var(--border-radius-medium);
|
||||
--zen-toolbox-min-width: 1px;
|
||||
|
||||
--tab-hover-background-color: var(--toolbarbutton-hover-background) !important;
|
||||
--tab-hover-background-color: color-mix(in srgb, var(--toolbarbutton-hover-background) 50%, transparent 50%);
|
||||
|
||||
min-width: var(--zen-toolbox-min-width);
|
||||
margin-top: 0 !important; /* Fix full screen mode */
|
||||
@@ -32,6 +44,7 @@
|
||||
padding-top: var(--zen-toolbox-top-align);
|
||||
padding-bottom: var(--zen-element-separation) !important;
|
||||
|
||||
border: none;
|
||||
order: 0 !important;
|
||||
|
||||
display: flex;
|
||||
@@ -44,6 +57,7 @@
|
||||
|
||||
#TabsToolbar-customization-target {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
gap: 0;
|
||||
&::after {
|
||||
content: '';
|
||||
@@ -60,7 +74,7 @@
|
||||
|
||||
@media (-moz-bool-pref: 'zen.view.show-bottom-border') {
|
||||
&::after {
|
||||
background: color-mix(in srgb, var(--zen-colors-border) 70%, transparent 30%);
|
||||
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,24 +94,28 @@
|
||||
margin-inline-start: 0 !important;
|
||||
padding-inline-start: 0 !important;
|
||||
|
||||
--tab-inner-inline-margin: 0;
|
||||
|
||||
position: relative;
|
||||
border-bottom: 0px solid transparent !important;
|
||||
|
||||
--tab-block-margin: 2px;
|
||||
--tab-selected-bgcolor: light-dark(rgba(255,255,255,.7), color-mix(in srgb, var(--zen-colors-secondary) 50%, transparent 50%));
|
||||
--tab-selected-bgcolor: light-dark(rgba(255,255,255,.8), rgba(255,255,255,.25));
|
||||
grid-gap: 0 !important;
|
||||
|
||||
&[overflow]::after,
|
||||
#vertical-tabs-newtab-button {
|
||||
/* Hide separator they give us, eww */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& .tabbrowser-tab {
|
||||
animation: zen-slide-in 0.2s ease-in-out;
|
||||
|
||||
max-width: unset !important;
|
||||
max-width: unset;
|
||||
padding: 0 !important;
|
||||
|
||||
position: relative;
|
||||
|
||||
height: calc(var(--tab-min-height) + var(--tab-block-margin) * 2) !important;
|
||||
min-height: calc(var(--tab-min-height) + var(--tab-block-margin) * 2) !important;
|
||||
|
||||
border-radius: var(--border-radius-medium);
|
||||
|
||||
& .tab-background {
|
||||
@@ -118,6 +136,27 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have a tab inside a tab, this means, it's a glance tab */
|
||||
& .tabbrowser-tab {
|
||||
border-radius: 5px;
|
||||
pointer-events: none;
|
||||
margin: 0;
|
||||
--toolbarbutton-inner-padding: 0;
|
||||
|
||||
& .tab-background {
|
||||
background: transparent;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
& label { display: none !important; }
|
||||
& .tab-close-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& .tab-icon-image {
|
||||
--toolbarbutton-inner-padding: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,26 +184,13 @@
|
||||
|
||||
#vertical-pinned-tabs-container {
|
||||
padding-inline-end: 0 !important;
|
||||
column-gap: 2px !important;
|
||||
row-gap: 3px !important;
|
||||
max-height: unset !important;
|
||||
overflow: visible !important;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
|
||||
@media not (prefers-color-scheme: dark) {
|
||||
--zen-colors-border: var(--zen-colors-tertiary);
|
||||
}
|
||||
|
||||
& .tab-background:not(:hover):not([selected]):not([multiselected]) {
|
||||
--zen-pinned-tabs-bgcolor: rgba(255, 255, 255, 0.12);
|
||||
@media not (prefers-color-scheme: dark) {
|
||||
--zen-pinned-tabs-bgcolor: rgba(1, 1, 1, 0.075);
|
||||
}
|
||||
background: var(--zen-pinned-tabs-bgcolor) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
&[selected] {
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
& .tabbrowser-tab:not(:hover) .tab-background:not([selected]):not([multiselected]) {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
& .tabbrowser-tab .tab-content {
|
||||
@@ -172,6 +198,10 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tabbrowser-tab[zen-glance-tab='true'] {
|
||||
width: fit-content !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark: toolbox as expanded */
|
||||
@@ -185,6 +215,7 @@
|
||||
#navigator-toolbox[zen-expanded='true']:not([zen-user-hover='true'])
|
||||
) {
|
||||
--zen-toolbox-min-width: fit-content;
|
||||
--tab-icon-end-margin: 8.5px;
|
||||
padding: var(--zen-toolbox-padding);
|
||||
padding-left: 0;
|
||||
padding-top: 0;
|
||||
@@ -193,10 +224,18 @@
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
& #tabbrowser-arrowscrollbox-periphery {
|
||||
margin-inline: var(--tab-block-margin);
|
||||
}
|
||||
|
||||
& #zen-sidebar-top-buttons {
|
||||
padding-left: var(--zen-toolbox-padding);
|
||||
}
|
||||
|
||||
& #zen-workspaces-button[as-button='true'] {
|
||||
width: calc(100% - 10px) !important;
|
||||
}
|
||||
|
||||
& #zen-workspaces-button {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
@@ -207,19 +246,10 @@
|
||||
}
|
||||
|
||||
& #vertical-pinned-tabs-container:has(tab:not([hidden])) {
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
bottom: -6px;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 1px;
|
||||
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.075));
|
||||
width: 99%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
position: absolute;
|
||||
& .tabbrowser-tab {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,8 +261,10 @@
|
||||
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
--toolbarbutton-inner-padding: var(--zen-toolbar-button-inner-padding) !important;
|
||||
& #zen-essentials-container {
|
||||
--tab-min-height: 44px;
|
||||
}
|
||||
|
||||
/* Mark: Fix separator paddings */
|
||||
@@ -293,18 +325,26 @@
|
||||
}
|
||||
|
||||
& #tabbrowser-tabs {
|
||||
--tab-inline-padding: 8px;
|
||||
& .tabbrowser-tab {
|
||||
|
||||
& .tab-label-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
& .tab-background {
|
||||
@media not (prefers-color-scheme: dark) {
|
||||
&:is([selected], [multiselected]) {
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.15) !important;
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
margin-inline: var(--tab-block-margin);
|
||||
width: -moz-available;
|
||||
}
|
||||
|
||||
&:not([pinned]):is(:hover, [visuallyselected]) .tab-close-button {
|
||||
display: block !important;
|
||||
&:is(:hover, [visuallyselected]) .tab-close-button {
|
||||
display: block;
|
||||
--tab-inline-padding: 0; /* Avoid weird padding */
|
||||
}
|
||||
|
||||
.tab-throbber,
|
||||
@@ -312,10 +352,8 @@
|
||||
.tab-icon-image,
|
||||
.tab-sharing-icon-overlay,
|
||||
.tab-icon-overlay {
|
||||
&:not([pinned]) {
|
||||
margin-inline-end: var(--toolbarbutton-inner-padding) !important;
|
||||
margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 4) !important;
|
||||
}
|
||||
margin-inline-end: var(--toolbarbutton-inner-padding) !important;
|
||||
margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 4) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,12 +377,39 @@
|
||||
#navigator-toolbox[zen-expanded='true']:not([zen-user-hover='true'])
|
||||
)
|
||||
) {
|
||||
--tab-min-width: 36px !important;
|
||||
|
||||
/* Important: When changin this value, make sure expand on hover doesn't break! */
|
||||
--zen-toolbox-max-width: 49px; /* 1px more because the browser view has one pixel of padding to avoid the border being cut off */
|
||||
--zen-toolbox-padding: 8px;
|
||||
--zen-toolbox-padding: calc(var(--zen-element-separation) / 2 + 1px);
|
||||
--zen-toolbox-max-width: calc(var(--tab-min-width) + var(--zen-toolbox-padding) * 2);
|
||||
max-width: var(--zen-toolbox-max-width) !important;
|
||||
min-width: var(--zen-toolbox-max-width) !important;
|
||||
|
||||
--zen-tabbar-offset: 1px; /* Fix the tabbar offset, because there's a shadow */
|
||||
&[zen-right-side='true'] {
|
||||
margin-left: var(--zen-tabbar-offset);
|
||||
}
|
||||
|
||||
&:not([zen-right-side='true']) {
|
||||
margin-right: var(--zen-tabbar-offset);
|
||||
}
|
||||
|
||||
& #zen-current-workspace-indicator-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& #zen-current-workspace-indicator {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
& #zen-essentials-container {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
& #vertical-tabs-newtab-button {
|
||||
padding: 0 !important;
|
||||
background: transparent !important;
|
||||
@@ -372,24 +437,23 @@
|
||||
}
|
||||
|
||||
&::before {
|
||||
width: 60% !important;
|
||||
width: 90% !important;
|
||||
}
|
||||
}
|
||||
|
||||
& #EssentialsToolbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& #essentials-accordion-header {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& #vertical-pinned-tabs-container:has(tab:not([hidden])) {
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
bottom: -6px;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 1px;
|
||||
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.075));
|
||||
width: 70%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
position: absolute;
|
||||
& .tabbrowser-tab {
|
||||
max-width: var(--tab-min-width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,16 +466,20 @@
|
||||
}
|
||||
|
||||
& #tabbrowser-tabs {
|
||||
--tab-min-width: 36px !important;
|
||||
margin-top: -2px;
|
||||
|
||||
& .tabbrowser-tab {
|
||||
margin: 0 auto;
|
||||
width: var(--tab-min-width) !important;
|
||||
height: var(--tab-min-width) !important;
|
||||
|
||||
& .tab-background:is([selected], [multiselected]) {
|
||||
@media not (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.12) !important;
|
||||
& .tab-background {
|
||||
margin-inline: auto !important;
|
||||
|
||||
&:is([selected], [multiselected]) {
|
||||
box-shadow: 0 0 1px 1px rgba(0,0,0,.1);
|
||||
|
||||
@media not (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.12) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,22 +487,22 @@
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
padding: 0 !important;
|
||||
|
||||
& .tab-label-container {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& .tab-icon-image,
|
||||
& .tab-icon-pending {
|
||||
margin-inline-end: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#vertical-pinned-tabs-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0 !important;
|
||||
/* Hide glances */
|
||||
& .tabbrowser-tab {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: 'zen.view.sidebar-collapsed.hide-mute-button') {
|
||||
@@ -543,17 +611,20 @@
|
||||
& #TabsToolbar {
|
||||
z-index: 100 !important;
|
||||
width: 250px !important;
|
||||
background-color: var(--zen-dialog-background);
|
||||
border-top-color: var(--zen-colors-border);
|
||||
|
||||
background: var(--zen-main-browser-background-toolbar) !important;
|
||||
background-attachment: fixed !important;
|
||||
background-size: 2000px !important; /* Dont ask me why */
|
||||
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
|
||||
position: absolute;
|
||||
padding: var(--zen-toolbox-padding);
|
||||
padding-top: 0;
|
||||
transition: 0 !important;
|
||||
/*animation: zen-vtabs-animation 0.3s ease-in-out;*/
|
||||
-moz-window-dragging: no-drag;
|
||||
overflow: hidden;
|
||||
transition: width 0.2s !important;
|
||||
border-right: 1px solid var(--zen-colors-border);
|
||||
|
||||
& .tab-throbber,
|
||||
& .tab-icon-pending,
|
||||
@@ -564,39 +635,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Make pinned tabs stay in a single line */
|
||||
#vertical-pinned-tabs-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0 !important;
|
||||
|
||||
position: relative;
|
||||
|
||||
& .tabbrowser-tab {
|
||||
& .tab-label-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
& .tab-throbber,
|
||||
& .tab-icon-pending,
|
||||
& .tab-icon-image,
|
||||
& .tab-sharing-icon-overlay,
|
||||
& .tab-icon-overlay {
|
||||
margin-inline-end: var(--toolbarbutton-inner-padding) !important;
|
||||
}
|
||||
|
||||
&:not(:hover):not([selected]):not([multiselected]) .tab-background {
|
||||
box-shadow: none;
|
||||
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[zen-right-side='true'] #TabsToolbar {
|
||||
right: 0;
|
||||
border-right: 0;
|
||||
border-left: 1px solid var(--zen-colors-border);
|
||||
border-top-left-radius: var(--zen-border-radius);
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
@@ -628,7 +668,7 @@
|
||||
#tabbrowser-tabs {
|
||||
& .tabbrowser-tab {
|
||||
&[pinned] .tab-close-button {
|
||||
display: none !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&[selected] .tab-background {
|
||||
@@ -686,7 +726,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: 'zen.tabs.show-newtab-under') {
|
||||
#tabs-newtab-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: 'zen.tabs.show-newtab-vertical') {
|
||||
#tabs-newtab-button {
|
||||
display: flex !important;
|
||||
}
|
||||
@@ -696,20 +740,77 @@
|
||||
--zen-colors-border: var(--zen-colors-tertiary);
|
||||
}
|
||||
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
@media (-moz-bool-pref: 'zen.view.show-newtab-button-top') {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 1px;
|
||||
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.075));
|
||||
width: 96%;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
@media (-moz-bool-pref: 'zen.view.show-newtab-button-border-top') and (not (-moz-bool-pref: 'zen.view.show-newtab-button-top')) {
|
||||
margin-top: 15px;
|
||||
position: relative;
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 1px;
|
||||
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.1));
|
||||
width: 98%;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark: Essentials Toolbar */
|
||||
#zen-essentials-container {
|
||||
padding-bottom: var(--zen-toolbox-padding);
|
||||
overflow: hidden;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
#zen-essentials-container {
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out;
|
||||
opacity: 1;
|
||||
grid-template-columns: repeat(auto-fit, minmax(var(--tab-pinned-min-width-expanded), auto));
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: thin;
|
||||
display: grid;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#zen-essentials-container .tabbrowser-tab {
|
||||
--toolbarbutton-inner-padding: 0;
|
||||
max-width: unset;
|
||||
width: 100% !important;
|
||||
border-radius: var(--border-radius-medium);
|
||||
|
||||
&:not([selected]) .tab-background {
|
||||
background: light-dark(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.1));
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
& .tab-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
& .tab-label-container {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& .tab-close-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& .tab-icon-image,
|
||||
& .tab-icon-overlay {
|
||||
margin-inline-end: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -32,13 +32,16 @@
|
||||
|
||||
--in-content-primary-button-background: light-dark(
|
||||
color-mix(in srgb, var(--zen-primary-color) 35%, black 65%),
|
||||
color-mix(in srgb, var(--zen-primary-color) 35%, white 65%)
|
||||
color-mix(in srgb, var(--zen-primary-color) 85%, black 15%)
|
||||
) !important;
|
||||
--in-content-primary-button-background-hover: light-dark(
|
||||
color-mix(in srgb, var(--zen-primary-color) 40%, black 60%),
|
||||
color-mix(in srgb, var(--zen-primary-color) 40%, white 60%)
|
||||
) !important;
|
||||
--in-content-primary-button-background-active: var(--zen-colors-hover-bg) !important;
|
||||
--in-content-primary-button-background-active: light-dark(
|
||||
color-mix(in srgb, var(--zen-primary-color) 50%, black 50%),
|
||||
color-mix(in srgb, var(--zen-primary-color) 80%, rgba(255,255,255,.2) 20%)
|
||||
) !important;
|
||||
--in-content-primary-button-text-color: light-dark(white, black) !important;
|
||||
--in-content-primary-button-border-color: transparent !important;
|
||||
--in-content-primary-button-border-hover: transparent !important;
|
||||
@@ -53,7 +56,7 @@
|
||||
color-mix(in srgb, var(--zen-primary-color) 10%, transparent 90%)
|
||||
) !important;
|
||||
--in-content-button-background-hover: light-dark(
|
||||
color-mix(in srgb, var(--zen-primary-color) 5%, rgba(0, 0, 0, 0.1) 95%),
|
||||
color-mix(in srgb, var(--zen-primary-color) 10%, rgb(255, 255, 255) 90%),
|
||||
color-mix(in srgb, var(--zen-primary-color) 15%, transparent 85%)
|
||||
) !important;
|
||||
--button-bgcolor: var(--in-content-button-background) !important;
|
||||
@@ -69,16 +72,22 @@
|
||||
--button-primary-active-bgcolor: var(--in-content-primary-button-background-active) !important;
|
||||
--button-primary-color: var(--in-content-primary-button-text-color) !important;
|
||||
|
||||
--color-accent-primary-hover: var(--zen-primary-color) !important;
|
||||
--button-background-color: var(--in-content-button-background) !important;
|
||||
--button-background-color-hover: var(--in-content-button-background-hover) !important;
|
||||
--button-background-color-active: var(--in-content-primary-button-background-active) !important;
|
||||
|
||||
--color-accent-primary: var(--button-primary-bgcolor) !important;
|
||||
--color-accent-primary-hover: var(--button-primary-hover-bgcolor) !important;
|
||||
--color-accent-primary-active: var(--button-primary-active-bgcolor) !important;
|
||||
|
||||
--in-content-page-background: var(--zen-colors-tertiary) !important;
|
||||
--zen-in-content-dialog-background: var(--zen-colors-tertiary);
|
||||
|
||||
--zen-button-border-radius: 7px;
|
||||
--zen-button-border-radius: 5px;
|
||||
--zen-button-padding: 0.6rem 1.2rem;
|
||||
|
||||
/* Toolbar */
|
||||
--zen-toolbar-height: 39px;
|
||||
--zen-toolbar-height: 41px;
|
||||
--zen-toolbar-button-inner-padding: 6px;
|
||||
--toolbarbutton-outer-padding: 4px;
|
||||
|
||||
@@ -90,6 +99,7 @@
|
||||
|
||||
/* XUL */
|
||||
--zen-main-browser-background: light-dark(rgb(235, 235, 235), #1b1b1b);
|
||||
--zen-main-browser-background-toolbar: var(--zen-main-browser-background);
|
||||
--zen-browser-gradient-base: color-mix(in srgb, var(--zen-primary-color) 50%, white 50%);
|
||||
|
||||
--zen-appcontent-border: 1px solid var(--zen-colors-border);
|
||||
@@ -113,11 +123,17 @@
|
||||
--fp-contextmenu-bgcolor: light-dark(Menu, rgb(43 42 51 / 0.95));
|
||||
--toolbar-bgcolor: transparent;
|
||||
|
||||
--toolbarbutton-active-background: var(--zen-colors-border) !important;
|
||||
--toolbarbutton-active-background: light-dark(rgba(255, 255, 255, .9), color-mix(in srgb, var(--zen-primary-color) 50%, rgba(255, 255, 255, .1)));
|
||||
|
||||
--input-bgcolor: var(--zen-colors-tertiary) !important;
|
||||
--input-border-color: var(--zen-input-border-color) !important;
|
||||
--zen-themed-toolbar-bg: light-dark(#f7f7f7, var(--zen-colors-tertiary)) !important;
|
||||
--zen-themed-toolbar-bg: light-dark(#f7f7f7, var(--zen-colors-tertiary));
|
||||
--zen-themed-toolbar-bg-transparent: light-dark(#f7f7f7, var(--zen-colors-tertiary));
|
||||
|
||||
@supports (-moz-osx-font-smoothing: auto) {
|
||||
--zen-themed-toolbar-bg-transparency: 0.05;
|
||||
--zen-themed-toolbar-bg-transparent: light-dark(rgba(255, 255, 255, var(--zen-themed-toolbar-bg-transparency)), rgba(0, 0, 0, var(--zen-themed-toolbar-bg-transparency)));
|
||||
}
|
||||
|
||||
--toolbar-field-background-color: var(--zen-colors-input-bg) !important;
|
||||
--arrowpanel-background: var(--zen-dialog-background) !important;
|
||||
@@ -174,10 +190,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media not (-moz-bool-pref: 'zen.theme.toolbar-themed') {
|
||||
:root {
|
||||
--toolbar-bgcolor: light-dark(#e6e6e6, #1b1b1b) !important;
|
||||
--zen-themed-toolbar-bg: var(--toolbar-bgcolor);
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
}
|
||||
|
||||
#urlbar {
|
||||
--toolbarbutton-border-radius: 10px;
|
||||
--toolbarbutton-border-radius: 6px;
|
||||
--urlbarView-separator-color: var(--zen-colors-border);
|
||||
--urlbarView-hover-background: var(--toolbarbutton-hover-background);
|
||||
--urlbarView-highlight-background: var(--toolbarbutton-hover-background);
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
|
||||
#urlbar:not([focused='true']):not([breakout-extend="true"]) > #urlbar-background {
|
||||
background: color-mix(in srgb, light-dark(#fff, var(--zen-colors-secondary)) 65%, transparent 35%) !important;
|
||||
background: color-mix(in srgb, light-dark(#fff, var(--zen-colors-tertiary)) 60%, transparent 40%) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
@@ -51,25 +51,6 @@
|
||||
outline-color: none !important;
|
||||
}
|
||||
|
||||
#urlbar #tracking-protection-icon-container {
|
||||
border-radius: var(--toolbarbutton-border-radius);
|
||||
}
|
||||
|
||||
#urlbar .urlbar-page-action {
|
||||
border-radius: calc(var(--toolbarbutton-border-radius) / 1.5);
|
||||
}
|
||||
|
||||
#urlbar[breakout-extend='true'] .urlbar-page-action,
|
||||
#urlbar[breakout-extend='true'] #tracking-protection-icon-container,
|
||||
#urlbar[breakout-extend='true'] #identity-box #identity-icon-box,
|
||||
.searchbar-engine-one-off-item {
|
||||
border-radius: var(--toolbarbutton-border-radius) !important;
|
||||
}
|
||||
|
||||
#urlbar[breakout-extend='true'] {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
#identity-icon-box,
|
||||
#identity-permission-box {
|
||||
background: color-mix(in srgb, var(--zen-colors-secondary) 50%, transparent 50%) !important;
|
||||
@@ -111,16 +92,14 @@
|
||||
|
||||
.urlbar-page-action,
|
||||
#tracking-protection-icon-container {
|
||||
width: calc(var(--urlbar-min-height) - 4px - 4 * var(--urlbar-container-padding)) !important;
|
||||
height: calc(var(--urlbar-min-height) - 4px - 4 * var(--urlbar-container-padding)) !important;
|
||||
padding: 0 !important;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
margin: 0 0 0 2px !important;
|
||||
}
|
||||
|
||||
#urlbar[breakout-extend='true'] .urlbar-page-action,
|
||||
#urlbar[breakout-extend='true'] #tracking-protection-icon-container {
|
||||
.urlbar-page-action,
|
||||
#tracking-protection-icon-container {
|
||||
width: calc(var(--urlbar-min-height) - 4 * var(--urlbar-container-padding)) !important;
|
||||
height: calc(var(--urlbar-min-height) - 4 * var(--urlbar-container-padding)) !important;
|
||||
}
|
||||
@@ -253,7 +232,8 @@ button.popup-notification-dropmarker {
|
||||
max-width: 30rem !important;
|
||||
|
||||
& notification-message {
|
||||
background: var(--zen-colors-tertiary);
|
||||
background: color-mix(in srgb, var(--zen-colors-tertiary) 70%, transparent 30%);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--arrowpanel-border-color);
|
||||
border-radius: var(--zen-border-radius);
|
||||
|
||||
|
@@ -7,14 +7,20 @@
|
||||
|
||||
position: relative;
|
||||
|
||||
&[dont-show='true'] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&:not([as-button='true']) {
|
||||
--toolbarbutton-hover-background: transparent !important;
|
||||
border-radius: var(--zen-button-border-radius) !important;
|
||||
background: transparent;
|
||||
padding: 5px;
|
||||
padding: 2px;
|
||||
appearance: unset !important;
|
||||
height: fit-content;
|
||||
gap: 3px;
|
||||
background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
|
||||
& toolbarbutton {
|
||||
margin: auto;
|
||||
@@ -25,10 +31,6 @@
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
& > * {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: 'zen.workspaces.hide-deactivated-workspaces') {
|
||||
&:not([active='true']):not(:hover) {
|
||||
&::after {
|
||||
@@ -79,7 +81,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
:root:has(#navigator-toolbox:not([zen-has-hover='true'])) &[as-button='true'] {
|
||||
&[as-button='true'] {
|
||||
margin: auto;
|
||||
padding: var(--toolbarbutton-inner-padding) !important;
|
||||
width: calc(2 * var(--toolbarbutton-inner-padding) + 16px) !important;
|
||||
@@ -168,7 +170,7 @@
|
||||
|
||||
#PanelUI-zen-workspaces panelmultiview panelview {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
padding: 10px;
|
||||
width: var(--panel-width);
|
||||
}
|
||||
|
||||
@@ -323,7 +325,7 @@
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: -2px;
|
||||
width: 3px;
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
border-radius: 5px;
|
||||
@@ -340,9 +342,8 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
height: 2px;
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target.dragover {
|
||||
@@ -406,3 +407,38 @@
|
||||
#PanelUI-zen-workspaces-edit-footer button[default='true'] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Mark workspaces indicator */
|
||||
#zen-current-workspace-indicator {
|
||||
padding: 15px calc(4px + var(--tab-inline-padding));
|
||||
font-weight: 600;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
& #zen-current-workspace-indicator-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#zen-current-workspace-indicator-name {
|
||||
font-size: 13px;
|
||||
opacity: 0.5;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
& #zen-current-workspace-indicator-icon:not([hidden]) + #zen-current-workspace-indicator-name {
|
||||
margin-left: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@media not (-moz-bool-pref: 'zen.workspaces.show-workspace-indicator') {
|
||||
#zen-current-workspace-indicator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,3 @@
|
||||
#include zen-sidebar-panel.inc.xhtml
|
||||
#include zen-splitview-overlay.inc.xhtml
|
||||
#include zen-glance.inc.xhtml
|
19
src/browser/base/zen-components/ZenActorsManager.mjs
Normal file
19
src/browser/base/zen-components/ZenActorsManager.mjs
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
// Utility to register JSWindowActors
|
||||
var gZenActorsManager = {
|
||||
_actors: new Set(),
|
||||
|
||||
addJSWindowActor(...args) {
|
||||
if (this._actors.has(args[0])) {
|
||||
// Actor already registered, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ChromeUtils.registerWindowActor(...args);
|
||||
this._actors.add(args[0]);
|
||||
} catch (e) {
|
||||
console.warn(`Failed to register JSWindowActor: ${e}`);
|
||||
}
|
||||
},
|
||||
}
|
55
src/browser/base/zen-components/ZenCommonUtils.mjs
Normal file
55
src/browser/base/zen-components/ZenCommonUtils.mjs
Normal file
@@ -0,0 +1,55 @@
|
||||
var gZenOperatingSystemCommonUtils = {
|
||||
kZenOSToSmallName: {
|
||||
WINNT: 'windows',
|
||||
Darwin: 'macos',
|
||||
Linux: 'linux',
|
||||
},
|
||||
|
||||
get currentOperatingSystem() {
|
||||
let os = Services.appinfo.OS;
|
||||
return this.kZenOSToSmallName[os];
|
||||
},
|
||||
};
|
||||
|
||||
class ZenMultiWindowFeature {
|
||||
constructor() {}
|
||||
|
||||
static get browsers() {
|
||||
return Services.wm.getEnumerator('navigator:browser');
|
||||
}
|
||||
|
||||
static get currentBrowser() {
|
||||
return Services.wm.getMostRecentWindow('navigator:browser');
|
||||
}
|
||||
|
||||
isActiveWindow() {
|
||||
return ZenMultiWindowFeature.currentBrowser === window;
|
||||
}
|
||||
|
||||
async foreachWindowAsActive(callback) {
|
||||
if (!this.isActiveWindow()) {
|
||||
return;
|
||||
}
|
||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||
try {
|
||||
await callback(browser);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ZenDOMOperatedFeature {
|
||||
constructor() {
|
||||
var initBound = this.init.bind(this);
|
||||
document.addEventListener('DOMContentLoaded', initBound, { once: true });
|
||||
}
|
||||
}
|
||||
|
||||
class ZenPreloadedFeature {
|
||||
constructor() {
|
||||
var initBound = this.init.bind(this);
|
||||
document.addEventListener('MozBeforeInitialXULLayout', initBound, { once: true });
|
||||
}
|
||||
}
|
253
src/browser/base/zen-components/ZenCompactMode.mjs
Normal file
253
src/browser/base/zen-components/ZenCompactMode.mjs
Normal file
@@ -0,0 +1,253 @@
|
||||
const lazyCompactMode = {};
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazyCompactMode,
|
||||
'COMPACT_MODE_FLASH_DURATION',
|
||||
'zen.view.compact.toolbar-flash-popup.duration',
|
||||
800
|
||||
);
|
||||
|
||||
var gZenCompactModeManager = {
|
||||
_flashTimeouts: {},
|
||||
_evenListeners: [],
|
||||
_removeHoverFrames: {},
|
||||
|
||||
init() {
|
||||
Services.prefs.addObserver('zen.view.compact', this._updateEvent.bind(this));
|
||||
Services.prefs.addObserver('zen.view.sidebar-expanded.on-hover', this._disableTabsOnHoverIfConflict.bind(this));
|
||||
Services.prefs.addObserver('zen.tabs.vertical.right-side', this._updateSidebarIsOnRight.bind(this));
|
||||
|
||||
gZenUIManager.addPopupTrackingAttribute(this.sidebar);
|
||||
gZenUIManager.addPopupTrackingAttribute(document.getElementById('zen-appcontent-navbar-container'));
|
||||
|
||||
this.addMouseActions();
|
||||
this.addContextMenu();
|
||||
},
|
||||
|
||||
get prefefence() {
|
||||
return Services.prefs.getBoolPref('zen.view.compact');
|
||||
},
|
||||
|
||||
set preference(value) {
|
||||
Services.prefs.setBoolPref('zen.view.compact', value);
|
||||
return value;
|
||||
},
|
||||
|
||||
get sidebarIsOnRight() {
|
||||
if (this._sidebarIsOnRight) {
|
||||
return this._sidebarIsOnRight;
|
||||
}
|
||||
return Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
|
||||
},
|
||||
|
||||
get sidebar() {
|
||||
if (!this._sidebar) {
|
||||
this._sidebar = document.getElementById('navigator-toolbox');
|
||||
}
|
||||
return this._sidebar;
|
||||
},
|
||||
|
||||
addContextMenu() {
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<menu id="zen-context-menu-compact-mode" data-l10n-id="zen-toolbar-context-compact-mode">
|
||||
<menupopup>
|
||||
<menuitem id="zen-context-menu-compact-mode-toggle" data-l10n-id="zen-toolbar-context-compact-mode-enable" type="checkbox" oncommand="gZenCompactModeManager.toggle();"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="zen-context-menu-compact-mode-hide-sidebar" data-l10n-id="zen-toolbar-context-compact-mode-just-tabs" type="radio" oncommand="gZenCompactModeManager.hideSidebar();"/>
|
||||
<menuitem id="zen-context-menu-compact-mode-hide-toolbar" data-l10n-id="zen-toolbar-context-compact-mode-just-toolbar" type="radio" oncommand="gZenCompactModeManager.hideToolbar();"/>
|
||||
<menuitem id="zen-context-menu-compact-mode-hide-both" data-l10n-id="zen-toolbar-context-compact-mode-hide-both" type="radio" oncommand="gZenCompactModeManager.hideBoth();"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
`);
|
||||
document.getElementById('viewToolbarsMenuSeparator').before(fragment);
|
||||
this.updateContextMenu();
|
||||
},
|
||||
|
||||
hideSidebar() {
|
||||
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
|
||||
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', false);
|
||||
},
|
||||
|
||||
hideToolbar() {
|
||||
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
|
||||
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', false);
|
||||
},
|
||||
|
||||
hideBoth() {
|
||||
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
|
||||
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
|
||||
},
|
||||
|
||||
addEventListener(callback) {
|
||||
this._evenListeners.push(callback);
|
||||
},
|
||||
|
||||
_updateEvent() {
|
||||
this._evenListeners.forEach((callback) => callback());
|
||||
this._disableTabsOnHoverIfConflict();
|
||||
this.updateContextMenu();
|
||||
},
|
||||
|
||||
updateContextMenu() {
|
||||
document
|
||||
.getElementById('zen-context-menu-compact-mode-toggle')
|
||||
.setAttribute('checked', Services.prefs.getBoolPref('zen.view.compact'));
|
||||
|
||||
const hideTabBar = Services.prefs.getBoolPref('zen.view.compact.hide-tabbar');
|
||||
const hideToolbar = Services.prefs.getBoolPref('zen.view.compact.hide-toolbar');
|
||||
const hideBoth = hideTabBar && hideToolbar;
|
||||
|
||||
const idName = 'zen-context-menu-compact-mode-hide-';
|
||||
document.getElementById(idName + 'sidebar').setAttribute('checked', !hideBoth && hideTabBar);
|
||||
document.getElementById(idName + 'toolbar').setAttribute('checked', !hideBoth && hideToolbar);
|
||||
document.getElementById(idName + 'both').setAttribute('checked', hideBoth);
|
||||
},
|
||||
|
||||
_removeOpenStateOnUnifiedExtensions() {
|
||||
// Fix for bug https://github.com/zen-browser/desktop/issues/1925
|
||||
const buttons = document.querySelectorAll('toolbarbutton:is(#unified-extensions-button, .webextension-browser-action)');
|
||||
for (let button of buttons) {
|
||||
button.removeAttribute('open');
|
||||
}
|
||||
},
|
||||
|
||||
_disableTabsOnHoverIfConflict() {
|
||||
if (Services.prefs.getBoolPref('zen.view.compact') && Services.prefs.getBoolPref('zen.view.compact.hide-tabbar')) {
|
||||
Services.prefs.setBoolPref('zen.view.sidebar-expanded.on-hover', false);
|
||||
}
|
||||
},
|
||||
|
||||
toggle() {
|
||||
return (this.preference = !this.prefefence);
|
||||
},
|
||||
|
||||
_updateSidebarIsOnRight() {
|
||||
this._sidebarIsOnRight = Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
|
||||
},
|
||||
|
||||
toggleSidebar() {
|
||||
this.sidebar.toggleAttribute('zen-user-show');
|
||||
},
|
||||
|
||||
get hideAfterHoverDuration() {
|
||||
if (this._hideAfterHoverDuration) {
|
||||
return this._hideAfterHoverDuration;
|
||||
}
|
||||
return Services.prefs.getIntPref('zen.view.compact.toolbar-hide-after-hover.duration');
|
||||
},
|
||||
|
||||
get hoverableElements() {
|
||||
return [
|
||||
{
|
||||
element: this.sidebar,
|
||||
screenEdge: this.sidebarIsOnRight ? 'right' : 'left',
|
||||
},
|
||||
{
|
||||
element: document.getElementById('zen-appcontent-navbar-container'),
|
||||
screenEdge: 'top',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
flashSidebar(duration = lazyCompactMode.COMPACT_MODE_FLASH_DURATION) {
|
||||
let tabPanels = document.getElementById('tabbrowser-tabpanels');
|
||||
if (!tabPanels.matches("[zen-split-view='true']")) {
|
||||
this.flashElement(this.sidebar, duration, this.sidebar.id);
|
||||
}
|
||||
},
|
||||
|
||||
flashElement(element, duration, id, attrName = 'flash-popup') {
|
||||
if (element.matches(':hover')) {
|
||||
return;
|
||||
}
|
||||
if (this._flashTimeouts[id]) {
|
||||
clearTimeout(this._flashTimeouts[id]);
|
||||
} else {
|
||||
requestAnimationFrame(() => element.setAttribute(attrName, 'true'));
|
||||
}
|
||||
this._flashTimeouts[id] = setTimeout(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
element.removeAttribute(attrName);
|
||||
this._flashTimeouts[id] = null;
|
||||
});
|
||||
}, duration);
|
||||
},
|
||||
|
||||
clearFlashTimeout(id) {
|
||||
clearTimeout(this._flashTimeouts[id]);
|
||||
this._flashTimeouts[id] = null;
|
||||
},
|
||||
|
||||
addMouseActions() {
|
||||
for (let i = 0; i < this.hoverableElements.length; i++) {
|
||||
let target = this.hoverableElements[i].element;
|
||||
target.addEventListener('mouseenter', (event) => {
|
||||
this.clearFlashTimeout('has-hover' + target.id);
|
||||
window.requestAnimationFrame(() => target.setAttribute('zen-has-hover', 'true'));
|
||||
});
|
||||
|
||||
target.addEventListener('mouseleave', (event) => {
|
||||
// If on Mac, ignore mouseleave in the area of window buttons
|
||||
if (AppConstants.platform == 'macosx') {
|
||||
const MAC_WINDOW_BUTTONS_X_BORDER = 75;
|
||||
const MAC_WINDOW_BUTTONS_Y_BORDER = 40;
|
||||
if (event.clientX < MAC_WINDOW_BUTTONS_X_BORDER && event.clientY < MAC_WINDOW_BUTTONS_Y_BORDER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hoverableElements[i].keepHoverDuration) {
|
||||
this.flashElement(target, keepHoverDuration, 'has-hover' + target.id, 'zen-has-hover');
|
||||
} else {
|
||||
this._removeHoverFrames[target.id] = window.requestAnimationFrame(() => target.removeAttribute('zen-has-hover'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.documentElement.addEventListener('mouseleave', (event) => {
|
||||
const screenEdgeCrossed = this._getCrossedEdge(event.pageX, event.pageY);
|
||||
if (!screenEdgeCrossed) return;
|
||||
for (let entry of this.hoverableElements) {
|
||||
if (screenEdgeCrossed !== entry.screenEdge) continue;
|
||||
const target = entry.element;
|
||||
const boundAxis = entry.screenEdge === 'right' || entry.screenEdge === 'left' ? 'y' : 'x';
|
||||
if (!this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) {
|
||||
continue;
|
||||
}
|
||||
window.cancelAnimationFrame(this._removeHoverFrames[target.id]);
|
||||
|
||||
this.flashElement(target, this.hideAfterHoverDuration, 'has-hover' + target.id, 'zen-has-hover');
|
||||
document.addEventListener(
|
||||
'mousemove',
|
||||
() => {
|
||||
if (target.matches(':hover')) return;
|
||||
target.removeAttribute('zen-has-hover');
|
||||
this.clearFlashTimeout('has-hover' + target.id);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getCrossedEdge(posX, posY, element = document.documentElement, maxDistance = 10) {
|
||||
const targetBox = element.getBoundingClientRect();
|
||||
posX = Math.max(targetBox.left, Math.min(posX, targetBox.right));
|
||||
posY = Math.max(targetBox.top, Math.min(posY, targetBox.bottom));
|
||||
return ['top', 'bottom', 'left', 'right'].find((edge, i) => {
|
||||
const distance = Math.abs((i < 2 ? posY : posX) - targetBox[edge]);
|
||||
return distance <= maxDistance;
|
||||
});
|
||||
},
|
||||
|
||||
_positionInBounds(axis = 'x', element, x, y, error = 0) {
|
||||
const bBox = element.getBoundingClientRect();
|
||||
if (axis === 'y') return bBox.top - error < y && y < bBox.bottom + error;
|
||||
else return bBox.left - error < x && x < bBox.right + error;
|
||||
},
|
||||
|
||||
toggleToolbar() {
|
||||
let toolbar = document.getElementById('zen-appcontent-navbar-container');
|
||||
toolbar.toggleAttribute('zen-user-show');
|
||||
},
|
||||
};
|
352
src/browser/base/zen-components/ZenGlanceManager.mjs
Normal file
352
src/browser/base/zen-components/ZenGlanceManager.mjs
Normal file
@@ -0,0 +1,352 @@
|
||||
|
||||
{
|
||||
|
||||
class ZenGlanceManager extends ZenDOMOperatedFeature {
|
||||
#currentBrowser = null;
|
||||
#currentTab = null;
|
||||
|
||||
#animating = false;
|
||||
|
||||
init() {
|
||||
document.documentElement.setAttribute("zen-glance-uuid", gZenUIManager.generateUuidv4());
|
||||
window.addEventListener("keydown", this.onKeyDown.bind(this));
|
||||
window.addEventListener("TabClose", this.onTabClose.bind(this));
|
||||
|
||||
ChromeUtils.defineLazyGetter(
|
||||
this,
|
||||
'sidebarButtons',
|
||||
() => document.getElementById('zen-glance-sidebar-container')
|
||||
);
|
||||
|
||||
document.getElementById('tabbrowser-tabpanels').addEventListener("click", this.onOverlayClick.bind(this));
|
||||
|
||||
Services.obs.addObserver(this, "quit-application-requested");
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
if (event.key === "Escape" && this.#currentBrowser) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.closeGlance();
|
||||
}
|
||||
}
|
||||
|
||||
onOverlayClick(event) {
|
||||
if (event.target === this.overlay && event.originalTarget !== this.contentWrapper) {
|
||||
this.closeGlance();
|
||||
}
|
||||
}
|
||||
|
||||
observe(subject, topic) {
|
||||
switch (topic) {
|
||||
case "quit-application-requested":
|
||||
this.onUnload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onUnload() {
|
||||
// clear everything
|
||||
if (this.#currentBrowser) {
|
||||
gBrowser.removeTab(this.#currentTab);
|
||||
}
|
||||
}
|
||||
|
||||
createBrowserElement(url, currentTab) {
|
||||
const newTabOptions = {
|
||||
userContextId: currentTab.getAttribute("usercontextid") || "",
|
||||
skipBackgroundNotify: true,
|
||||
insertTab: true,
|
||||
skipLoad: false,
|
||||
index: currentTab._tPos + 1,
|
||||
};
|
||||
this.currentParentTab = currentTab;
|
||||
const newTab = gBrowser.addTrustedTab(Services.io.newURI(url).spec, newTabOptions);
|
||||
|
||||
gBrowser.selectedTab = newTab;
|
||||
currentTab.querySelector(".tab-content").appendChild(newTab);
|
||||
newTab.setAttribute("zen-glance-tab", true);
|
||||
this.#currentBrowser = newTab.linkedBrowser;
|
||||
this.#currentTab = newTab;
|
||||
return this.#currentBrowser;
|
||||
}
|
||||
|
||||
openGlance(data) {
|
||||
if (this.#currentBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const initialX = data.x;
|
||||
const initialY = data.y;
|
||||
const initialWidth = data.width;
|
||||
const initialHeight = data.height;
|
||||
|
||||
this.browserWrapper?.removeAttribute("animate");
|
||||
this.browserWrapper?.removeAttribute("animate-end");
|
||||
this.browserWrapper?.removeAttribute("animate-full");
|
||||
this.browserWrapper?.removeAttribute("animate-full-end");
|
||||
this.browserWrapper?.removeAttribute("has-finished-animation");
|
||||
|
||||
const url = data.url;
|
||||
const currentTab = gBrowser.selectedTab;
|
||||
|
||||
this.animatingOpen = true;
|
||||
const browserElement = this.createBrowserElement(url, currentTab);
|
||||
|
||||
this.overlay = browserElement.closest(".browserSidebarContainer");
|
||||
this.browserWrapper = browserElement.closest(".browserContainer");
|
||||
this.contentWrapper = browserElement.closest(".browserStack");
|
||||
|
||||
this.browserWrapper.prepend(this.sidebarButtons);
|
||||
|
||||
this.overlay.classList.add("zen-glance-overlay");
|
||||
|
||||
this.browserWrapper.removeAttribute("animate-end");
|
||||
window.requestAnimationFrame(() => {
|
||||
this.quickOpenGlance();
|
||||
|
||||
this.browserWrapper.style.setProperty("--initial-x", `${initialX}px`);
|
||||
this.browserWrapper.style.setProperty("--initial-y", `${initialY}px`);
|
||||
this.browserWrapper.style.setProperty("--initial-width", initialWidth + "px");
|
||||
this.browserWrapper.style.setProperty("--initial-height", initialHeight + "px");
|
||||
|
||||
this.overlay.removeAttribute("fade-out");
|
||||
this.browserWrapper.setAttribute("animate", true);
|
||||
this.#animating = true;
|
||||
setTimeout(() => {
|
||||
this.browserWrapper.setAttribute("animate-end", true);
|
||||
this.browserWrapper.setAttribute("has-finished-animation", true);
|
||||
this.#animating = false;
|
||||
this.animatingOpen = false;
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
|
||||
closeGlance({ noAnimation = false, onTabClose = false } = {}) {
|
||||
if (this.#animating || !this.#currentBrowser || this.animatingOpen || this._duringOpening) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.browserWrapper.removeAttribute("has-finished-animation");
|
||||
if (noAnimation) {
|
||||
this.quickCloseGlance({ closeCurrentTab: false });
|
||||
this.#currentBrowser = null;
|
||||
this.#currentTab = null;
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser._insertTabAtIndex(this.#currentTab, {
|
||||
index: this.currentParentTab._tPos + 1,
|
||||
});
|
||||
|
||||
let quikcCloseZen = false;
|
||||
if (onTabClose) {
|
||||
// Create new tab if no more ex
|
||||
if (gBrowser.tabs.length === 1) {
|
||||
gBrowser.selectedTab = gZenUIManager.openAndChangeToTab(Services.prefs.getStringPref('browser.startup.homepage'));
|
||||
return;
|
||||
} else if (gBrowser.selectedTab === this.#currentTab) {
|
||||
this._duringOpening = true;
|
||||
gBrowser.tabContainer.advanceSelectedTab(1, true); // to skip the current tab
|
||||
this._duringOpening = false;
|
||||
quikcCloseZen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// do NOT touch here, I don't know what it does, but it works...
|
||||
window.requestAnimationFrame(() => {
|
||||
this.#currentTab.style.display = "none";
|
||||
this.browserWrapper.removeAttribute("animate");
|
||||
this.browserWrapper.removeAttribute("animate-end");
|
||||
this.overlay.setAttribute("fade-out", true);
|
||||
window.requestAnimationFrame(() => {
|
||||
this.browserWrapper.setAttribute("animate", true);
|
||||
setTimeout(() => {
|
||||
if (!this.currentParentTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!onTabClose || quikcCloseZen) {
|
||||
this.quickCloseGlance();
|
||||
}
|
||||
this.overlay.removeAttribute("fade-out");
|
||||
this.browserWrapper.removeAttribute("animate");
|
||||
|
||||
this.lastCurrentTab = this.#currentTab;
|
||||
|
||||
this.overlay.classList.remove("zen-glance-overlay");
|
||||
gBrowser._getSwitcher().setTabStateNoAction(this.lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED);
|
||||
|
||||
if (!onTabClose && gBrowser.selectedTab === this.lastCurrentTab) {
|
||||
this._duringOpening = true;
|
||||
gBrowser.selectedTab = this.currentParentTab;
|
||||
}
|
||||
|
||||
// reset everything
|
||||
this.currentParentTab = null;
|
||||
this.browserWrapper = null;
|
||||
this.overlay = null;
|
||||
this.contentWrapper = null;
|
||||
|
||||
this.lastCurrentTab.removeAttribute("zen-glance-tab");
|
||||
|
||||
gBrowser.tabContainer._invalidateCachedTabs();
|
||||
this.lastCurrentTab.closing = true;
|
||||
gBrowser.removeTab(this.lastCurrentTab, { animate: false });
|
||||
|
||||
this.#currentTab = null;
|
||||
this.#currentBrowser = null;
|
||||
|
||||
this.lastCurrentTab = null;
|
||||
this._duringOpening = false;
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
quickOpenGlance() {
|
||||
if (!this.#currentBrowser || this._duringOpening) {
|
||||
return;
|
||||
}
|
||||
this._duringOpening = true;
|
||||
try {
|
||||
gBrowser.selectedTab = this.#currentTab;
|
||||
} catch (e) {}
|
||||
|
||||
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.add("deck-selected", "zen-glance-background");
|
||||
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("zen-glance-overlay");
|
||||
this.currentParentTab.linkedBrowser.zenModeActive = true;
|
||||
this.#currentBrowser.zenModeActive = true;
|
||||
this.currentParentTab.linkedBrowser.docShellIsActive = true;
|
||||
this.#currentBrowser.docShellIsActive = true;
|
||||
this.#currentBrowser.setAttribute("zen-glance-selected", true);
|
||||
|
||||
this.currentParentTab._visuallySelected = true;
|
||||
this.overlay.classList.add("deck-selected");
|
||||
|
||||
this._duringOpening = false;
|
||||
}
|
||||
|
||||
quickCloseGlance({ closeCurrentTab = true, closeParentTab = true } = {}) {
|
||||
const parentHasBrowser = !!(this.currentParentTab.linkedBrowser);
|
||||
if (parentHasBrowser) {
|
||||
if (closeParentTab) {
|
||||
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("deck-selected");
|
||||
}
|
||||
this.currentParentTab.linkedBrowser.zenModeActive = false;
|
||||
}
|
||||
this.#currentBrowser.zenModeActive = false;
|
||||
if (closeParentTab && parentHasBrowser) {
|
||||
this.currentParentTab.linkedBrowser.docShellIsActive = false;
|
||||
}
|
||||
if (closeCurrentTab) {
|
||||
this.#currentBrowser.docShellIsActive = false;
|
||||
this.overlay.classList.remove("deck-selected");
|
||||
}
|
||||
if (!this.currentParentTab._visuallySelected && closeParentTab) {
|
||||
this.currentParentTab._visuallySelected = false;
|
||||
}
|
||||
this.#currentBrowser.removeAttribute("zen-glance-selected");
|
||||
if (parentHasBrowser) {
|
||||
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("zen-glance-background");
|
||||
}
|
||||
}
|
||||
|
||||
onLocationChange(_) {
|
||||
if (this._duringOpening) {
|
||||
return;
|
||||
}
|
||||
if (gBrowser.selectedTab === this.#currentTab && !this.animatingOpen && !this._duringOpening && this.#currentBrowser) {
|
||||
this.quickOpenGlance();
|
||||
return;
|
||||
}
|
||||
if (gBrowser.selectedTab === this.currentParentTab && this.#currentBrowser) {
|
||||
this.quickOpenGlance();
|
||||
} else if ((!this.animatingFullOpen || this.animatingOpen) && this.#currentBrowser) {
|
||||
this.closeGlance();
|
||||
}
|
||||
}
|
||||
|
||||
onTabClose(event) {
|
||||
if (event.target === this.currentParentTab) {
|
||||
this.closeGlance({ onTabClose: true });
|
||||
}
|
||||
}
|
||||
|
||||
fullyOpenGlance() {
|
||||
gBrowser._insertTabAtIndex(this.#currentTab, {
|
||||
index: this.#currentTab._tPos + 1,
|
||||
});
|
||||
|
||||
this.animatingFullOpen = true;
|
||||
this.currentParentTab._visuallySelected = false;
|
||||
|
||||
this.browserWrapper.removeAttribute("has-finished-animation");
|
||||
this.browserWrapper.setAttribute("animate-full", true);
|
||||
this.#currentTab.removeAttribute("zen-glance-tab");
|
||||
gBrowser.selectedTab = this.#currentTab;
|
||||
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("zen-glance-background");
|
||||
setTimeout(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.browserWrapper.setAttribute("animate-full-end", true);
|
||||
setTimeout(() => {
|
||||
this.animatingFullOpen = false;
|
||||
this.closeGlance({ noAnimation: true });
|
||||
}, 600);
|
||||
});
|
||||
}, 300);
|
||||
}
|
||||
|
||||
openGlanceForBookmark(event) {
|
||||
const activationMethod = Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl');
|
||||
|
||||
if (activationMethod === 'ctrl' && !event.ctrlKey) {
|
||||
return;
|
||||
} else if (activationMethod === 'alt' && !event.altKey) {
|
||||
return;
|
||||
} else if (activationMethod === 'shift' && !event.shiftKey) {
|
||||
return;
|
||||
} else if (activationMethod === 'meta' && !event.metaKey) {
|
||||
return;
|
||||
}else if (activationMethod === 'mantain' || typeof activationMethod === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
const data = {
|
||||
url: event.target._placesNode.uri,
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
};
|
||||
|
||||
this.openGlance(data);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenGlanceManager = new ZenGlanceManager();
|
||||
|
||||
function registerWindowActors() {
|
||||
if (Services.prefs.getBoolPref("zen.glance.enabled", true)) {
|
||||
gZenActorsManager.addJSWindowActor("ZenGlance", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://browser/content/zen-components/actors/ZenGlanceParent.sys.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://browser/content/zen-components/actors/ZenGlanceChild.sys.mjs",
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerWindowActors();
|
||||
}
|
742
src/browser/base/zen-components/ZenGradientGenerator.mjs
Normal file
742
src/browser/base/zen-components/ZenGradientGenerator.mjs
Normal file
@@ -0,0 +1,742 @@
|
||||
{
|
||||
class ZenThemePicker extends ZenMultiWindowFeature {
|
||||
static GRADIENT_IMAGE_URL = 'chrome://browser/content/zen-images/gradient.png';
|
||||
static GRADIENT_DISPLAY_URL = 'chrome://browser/content/zen-images/gradient-display.png';
|
||||
static MAX_DOTS = 5;
|
||||
|
||||
currentOpacity = 0.5;
|
||||
currentRotation = 45;
|
||||
|
||||
numberOfDots = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (!Services.prefs.getBoolPref('zen.theme.gradient', true) || !ZenWorkspaces.shouldHaveWorkspaces) {
|
||||
return;
|
||||
}
|
||||
this.dragStartPosition = null;
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, 'panel', () => document.getElementById('PanelUI-zen-gradient-generator'));
|
||||
ChromeUtils.defineLazyGetter(this, 'toolbox', () => document.getElementById('TabsToolbar'));
|
||||
ChromeUtils.defineLazyGetter(this, 'customColorInput', () => document.getElementById('PanelUI-zen-gradient-generator-custom-input'));
|
||||
ChromeUtils.defineLazyGetter(this, 'customColorList', () => document.getElementById('PanelUI-zen-gradient-generator-custom-list'));
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
'allowWorkspaceColors',
|
||||
'zen.theme.color-prefs.use-workspace-colors',
|
||||
true,
|
||||
this.onDarkModeChange.bind(this)
|
||||
)
|
||||
|
||||
this.initRotation();
|
||||
this.initCanvas();
|
||||
this.initCustomColorInput();
|
||||
|
||||
ZenWorkspaces.addChangeListeners(this.onWorkspaceChange.bind(this));
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addListener(this.onDarkModeChange.bind(this));
|
||||
}
|
||||
|
||||
get isDarkMode() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
async onDarkModeChange(event, skipUpdate = false) {
|
||||
const currentWorkspace = await ZenWorkspaces.getActiveWorkspace();
|
||||
this.onWorkspaceChange(currentWorkspace, skipUpdate);
|
||||
}
|
||||
|
||||
initContextMenu() {
|
||||
const menu = window.MozXULElement.parseXULToFragment(`
|
||||
<menuitem id="zenToolbarThemePicker"
|
||||
data-lazy-l10n-id="zen-workspaces-change-gradient"
|
||||
oncommand="gZenThemePicker.openThemePicker(event);"/>
|
||||
`);
|
||||
document.getElementById('toolbar-context-customize').before(menu);
|
||||
}
|
||||
|
||||
openThemePicker(event) {
|
||||
PanelMultiView.openPopup(this.panel, this.toolbox, {
|
||||
position: 'topright topleft',
|
||||
triggerEvent: event,
|
||||
});
|
||||
}
|
||||
|
||||
initCanvas() {
|
||||
this.image = new Image();
|
||||
this.image.src = ZenThemePicker.GRADIENT_IMAGE_URL;
|
||||
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.panel.appendChild(this.canvas);
|
||||
this.canvasCtx = this.canvas.getContext('2d');
|
||||
|
||||
// wait for the image to load
|
||||
this.image.onload = this.onImageLoad.bind(this);
|
||||
}
|
||||
|
||||
|
||||
onImageLoad() {
|
||||
// resize the image to fit the panel
|
||||
const imageSize = 300 - 20; // 20 is the padding (10px)
|
||||
const scale = imageSize / Math.max(this.image.width, this.image.height);
|
||||
this.image.width *= scale;
|
||||
this.image.height *= scale;
|
||||
|
||||
this.canvas.width = this.image.width;
|
||||
this.canvas.height = this.image.height;
|
||||
this.canvasCtx.drawImage(this.image, 0, 0);
|
||||
|
||||
this.canvas.setAttribute('hidden', 'true');
|
||||
|
||||
// Call the rest of the initialization
|
||||
this.initContextMenu();
|
||||
this.initThemePicker();
|
||||
|
||||
|
||||
this._hasInitialized = true;
|
||||
this.onDarkModeChange(null);
|
||||
}
|
||||
|
||||
initRotation() {
|
||||
this.rotationInput = document.getElementById('PanelUI-zen-gradient-degrees');
|
||||
this.rotationInputDot = this.rotationInput.querySelector('.dot');
|
||||
this.rotationInputText = this.rotationInput.querySelector('.text');
|
||||
this.rotationInputDot.addEventListener('mousedown', this.onRotationMouseDown.bind(this));
|
||||
this.rotationInput.addEventListener('wheel', this.onRotationWheel.bind(this));
|
||||
}
|
||||
|
||||
onRotationWheel(event) {
|
||||
event.preventDefault();
|
||||
const delta = event.deltaY;
|
||||
const degrees = this.currentRotation + (delta > 0 ? 10 : -10);
|
||||
this.setRotationInput(degrees);
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
|
||||
onRotationMouseDown(event) {
|
||||
event.preventDefault();
|
||||
this.rotationDragging = true;
|
||||
this.rotationInputDot.style.zIndex = 2;
|
||||
this.rotationInputDot.classList.add('dragging');
|
||||
document.addEventListener('mousemove', this.onRotationMouseMove.bind(this));
|
||||
document.addEventListener('mouseup', this.onRotationMouseUp.bind(this));
|
||||
}
|
||||
|
||||
onRotationMouseUp(event) {
|
||||
this.rotationDragging = false;
|
||||
this.rotationInputDot.style.zIndex = 1;
|
||||
this.rotationInputDot.classList.remove('dragging');
|
||||
document.removeEventListener('mousemove', this.onRotationMouseMove.bind(this));
|
||||
document.removeEventListener('mouseup', this.onRotationMouseUp.bind(this));
|
||||
}
|
||||
|
||||
onRotationMouseMove(event) {
|
||||
if (this.rotationDragging) {
|
||||
event.preventDefault();
|
||||
const rect = this.rotationInput.getBoundingClientRect();
|
||||
// Make the dot follow the mouse in a circle, it can't go outside or inside the circle
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const angle = Math.atan2(event.clientY - centerY, event.clientX - centerX);
|
||||
const distance = Math.sqrt((event.clientX - centerX) ** 2 + (event.clientY - centerY) ** 2);
|
||||
const radius = rect.width / 2;
|
||||
let x = centerX + Math.cos(angle) * radius;
|
||||
let y = centerY + Math.sin(angle) * radius;
|
||||
if (distance > radius) {
|
||||
x = event.clientX;
|
||||
y = event.clientY;
|
||||
}
|
||||
const degrees = Math.round(Math.atan2(y - centerY, x - centerX) * 180 / Math.PI);
|
||||
this.setRotationInput(degrees);
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
}
|
||||
|
||||
setRotationInput(degrees) {
|
||||
let fixedRotation = degrees;
|
||||
while (fixedRotation < 0) {
|
||||
fixedRotation += 360;
|
||||
}
|
||||
while (fixedRotation >= 360) {
|
||||
fixedRotation -= 360;
|
||||
}
|
||||
this.currentRotation = degrees;
|
||||
this.rotationInputDot.style.transform = `rotate(${degrees - 20}deg)`;
|
||||
this.rotationInputText.textContent = `${fixedRotation}°`;
|
||||
}
|
||||
|
||||
initCustomColorInput() {
|
||||
this.customColorInput.addEventListener('keydown', this.onCustomColorKeydown.bind(this));
|
||||
}
|
||||
|
||||
onCustomColorKeydown(event) {
|
||||
//checks for enter key for custom colors
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
this.addCustomColor();
|
||||
}
|
||||
}
|
||||
|
||||
initThemePicker() {
|
||||
const themePicker = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
themePicker.style.setProperty('--zen-theme-picker-gradient-image', `url(${ZenThemePicker.GRADIENT_DISPLAY_URL})`);
|
||||
themePicker.addEventListener('mousemove', this.onDotMouseMove.bind(this));
|
||||
themePicker.addEventListener('mouseup', this.onDotMouseUp.bind(this));
|
||||
themePicker.addEventListener('click', this.onThemePickerClick.bind(this));
|
||||
}
|
||||
|
||||
calculateInitialPosition(color) {
|
||||
const [r, g, b] = color.c;
|
||||
const imageData = this.canvasCtx.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||||
// Find all pixels that are at least 90% similar to the color
|
||||
const similarPixels = [];
|
||||
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||
const pixelR = imageData.data[i];
|
||||
const pixelG = imageData.data[i + 1];
|
||||
const pixelB = imageData.data[i + 2];
|
||||
if (Math.abs(r - pixelR) < 25 && Math.abs(g - pixelG) < 25 && Math.abs(b - pixelB) < 25) {
|
||||
similarPixels.push(i);
|
||||
}
|
||||
}
|
||||
// Check if there's an exact match
|
||||
for (const pixel of similarPixels) {
|
||||
const x = (pixel / 4) % this.canvas.width;
|
||||
const y = Math.floor((pixel / 4) / this.canvas.width);
|
||||
const pixelColor = this.getColorFromPosition(x, y);
|
||||
if (pixelColor[0] === r && pixelColor[1] === g && pixelColor[2] === b) {
|
||||
return {x: x / this.canvas.width, y: y / this.canvas.height};
|
||||
}
|
||||
}
|
||||
// If there's no exact match, return the first similar pixel
|
||||
const pixel = similarPixels[0];
|
||||
const x = (pixel / 4) % this.canvas.width;
|
||||
const y = Math.floor((pixel / 4) / this.canvas.width);
|
||||
return {x: x / this.canvas.width, y: y / this.canvas.height};
|
||||
}
|
||||
|
||||
getColorFromPosition(x, y) {
|
||||
// get the color from the x and y from the image
|
||||
const imageData = this.canvasCtx.getImageData(x, y, 1, 1);
|
||||
return imageData.data;
|
||||
}
|
||||
|
||||
createDot(color, fromWorkspace = false) {
|
||||
const [r, g, b] = color.c;
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot');
|
||||
if (color.isCustom) {
|
||||
if (!color.c) {
|
||||
return;
|
||||
}
|
||||
dot.classList.add('custom');
|
||||
dot.style.opacity = 0;
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', color.c);
|
||||
} else {
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`);
|
||||
const { x, y } = this.calculateInitialPosition(color);
|
||||
dot.style.left = `${x * 100}%`;
|
||||
dot.style.top = `${y * 100}%`;
|
||||
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
|
||||
}
|
||||
this.panel.querySelector('.zen-theme-picker-gradient').appendChild(dot);
|
||||
if (!fromWorkspace) {
|
||||
this.updateCurrentWorkspace(true);
|
||||
}
|
||||
}
|
||||
|
||||
onThemePickerClick(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.button !== 0 || this.dragging ) return;
|
||||
|
||||
const gradient = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
const rect = gradient.getBoundingClientRect();
|
||||
const padding = 90; // each side
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const radius = (rect.width - padding) / 2;
|
||||
let pixelX = event.clientX;
|
||||
let pixelY = event.clientY;
|
||||
|
||||
// Check if the click is within the circle
|
||||
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
|
||||
if (distance > radius) {
|
||||
return; // Don't create a dot if clicking outside the circle
|
||||
}
|
||||
|
||||
// Check if we clicked on an existing dot
|
||||
const clickedElement = event.target;
|
||||
const isExistingDot = clickedElement.classList.contains('zen-theme-picker-dot');
|
||||
|
||||
// Only proceed if not clicking on an existing dot
|
||||
if (!isExistingDot) {
|
||||
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
|
||||
const color = this.getColorFromPosition(relativeX, relativeY);
|
||||
|
||||
// Create new dot
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot');
|
||||
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
|
||||
|
||||
dot.style.left = `${relativeX}px`;
|
||||
dot.style.top = `${relativeY}px`;
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
|
||||
|
||||
gradient.appendChild(dot);
|
||||
|
||||
this.updateCurrentWorkspace(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
onDotMouseDown(event) {
|
||||
event.preventDefault();
|
||||
if (event.button === 2) {
|
||||
return;
|
||||
}
|
||||
this.dragging = true;
|
||||
this.draggedDot = event.target;
|
||||
this.draggedDot.style.zIndex = 1;
|
||||
this.draggedDot.classList.add('dragging');
|
||||
|
||||
// Store the starting position of the drag
|
||||
this.dragStartPosition = {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
onDotMouseMove(event) {
|
||||
if (this.dragging) {
|
||||
event.preventDefault();
|
||||
const rect = this.panel.querySelector('.zen-theme-picker-gradient').getBoundingClientRect();
|
||||
const padding = 90; // each side
|
||||
// do NOT let the ball be draged outside of an imaginary circle. You can drag it anywhere inside the circle
|
||||
// if the distance between the center of the circle and the dragged ball is bigger than the radius, then the ball
|
||||
// should be placed on the edge of the circle. If it's inside the circle, then the ball just follows the mouse
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const radius = (rect.width - padding) / 2;
|
||||
let pixelX = event.clientX;
|
||||
let pixelY = event.clientY;
|
||||
const distance = Math.sqrt((pixelX - centerX) **2 + (pixelY - centerY) **2);
|
||||
if (distance > radius) {
|
||||
const angle = Math.atan2(pixelY - centerY, pixelX - centerX);
|
||||
pixelX = centerX + Math.cos(angle) * radius;
|
||||
pixelY = centerY + Math.sin(angle) * radius;
|
||||
}
|
||||
|
||||
// set the location of the dot in pixels
|
||||
const relativeX = pixelX - rect.left;
|
||||
const relativeY = pixelY - rect.top;
|
||||
this.draggedDot.style.left = `${relativeX}px`;
|
||||
this.draggedDot.style.top = `${relativeY}px`;
|
||||
const color = this.getColorFromPosition(relativeX, relativeY);
|
||||
this.draggedDot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
}
|
||||
|
||||
addColorToCustomList(color) {
|
||||
const listItems = window.MozXULElement.parseXULToFragment(`
|
||||
<hbox class="zen-theme-picker-custom-list-item">
|
||||
<html:div class="zen-theme-picker-dot-custom"></html:div>
|
||||
<label class="zen-theme-picker-custom-list-item-label"></label>
|
||||
<toolbarbutton class="zen-theme-picker-custom-list-item-remove toolbarbutton-1" oncommand="gZenThemePicker.removeCustomColor(event);"></toolbarbutton>
|
||||
</hbox>
|
||||
`);
|
||||
listItems.querySelector('.zen-theme-picker-custom-list-item').setAttribute('data-color', color);
|
||||
listItems.querySelector('.zen-theme-picker-dot-custom').style.setProperty('--zen-theme-picker-dot-color', color);
|
||||
listItems.querySelector('.zen-theme-picker-custom-list-item-label').textContent = color;
|
||||
|
||||
|
||||
this.customColorList.appendChild(listItems);
|
||||
}
|
||||
|
||||
async addCustomColor() {
|
||||
|
||||
const color = this.customColorInput.value;
|
||||
if (!color) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// can be any color format, we just add it to the list as a dot, but hidden
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot', 'hidden', 'custom');
|
||||
dot.style.opacity = 0;
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', color);
|
||||
this.panel.querySelector('.zen-theme-picker-gradient').appendChild(dot);
|
||||
this.customColorInput.value = '';
|
||||
await this.updateCurrentWorkspace();
|
||||
}
|
||||
|
||||
|
||||
|
||||
onThemePickerClick(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.button !== 0 || this.dragging) return;
|
||||
|
||||
const gradient = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
const rect = gradient.getBoundingClientRect();
|
||||
const padding = 90; // each side
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const radius = (rect.width - padding) / 2;
|
||||
let pixelX = event.clientX;
|
||||
let pixelY = event.clientY;
|
||||
|
||||
// Check if the click is within the circle
|
||||
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
|
||||
if (distance > radius) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const clickedElement = event.target;
|
||||
const isExistingDot = clickedElement.classList.contains('zen-theme-picker-dot');
|
||||
|
||||
|
||||
if (!isExistingDot && this.numberOfDots < ZenThemePicker.MAX_DOTS) {
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
const color = this.getColorFromPosition(relativeX, relativeY);
|
||||
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot');
|
||||
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
|
||||
|
||||
dot.style.left = `${relativeX}px`;
|
||||
dot.style.top = `${relativeY}px`;
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
|
||||
|
||||
gradient.appendChild(dot);
|
||||
|
||||
this.updateCurrentWorkspace(true);
|
||||
}
|
||||
}
|
||||
|
||||
onDotMouseDown(event) {
|
||||
event.preventDefault();
|
||||
if (event.button === 2) {
|
||||
return;
|
||||
}
|
||||
this.dragging = true;
|
||||
this.draggedDot = event.target;
|
||||
this.draggedDot.style.zIndex = 1;
|
||||
this.draggedDot.classList.add('dragging');
|
||||
|
||||
// Store the starting position of the drag
|
||||
this.dragStartPosition = {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
};
|
||||
}
|
||||
|
||||
onDotMouseUp(event) {
|
||||
if (event.button === 2) {
|
||||
if (!event.target.classList.contains('zen-theme-picker-dot')) {
|
||||
return;
|
||||
}
|
||||
event.target.remove();
|
||||
this.updateCurrentWorkspace();
|
||||
this.numberOfDots--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dragging) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.dragging = false;
|
||||
this.draggedDot.style.zIndex = 1;
|
||||
this.draggedDot.classList.remove('dragging');
|
||||
this.draggedDot = null;
|
||||
this.dragStartPosition = null; // Reset the drag start position
|
||||
return;
|
||||
}
|
||||
|
||||
this.numberOfDots = this.panel.querySelectorAll('.zen-theme-picker-dot').length;
|
||||
}
|
||||
|
||||
|
||||
themedColors(colors) {
|
||||
const isDarkMode = this.isDarkMode;
|
||||
const factor = isDarkMode ? 0.5 : 1.1;
|
||||
return colors.map(color => {
|
||||
return {
|
||||
c: color.isCustom ? color.c : [
|
||||
Math.min(255, color.c[0] * factor),
|
||||
Math.min(255, color.c[1] * factor),
|
||||
Math.min(255, color.c[2] * factor),
|
||||
],
|
||||
isCustom: color.isCustom,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onOpacityChange(event) {
|
||||
this.currentOpacity = event.target.value;
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
|
||||
onTextureChange(event) {
|
||||
this.currentTexture = event.target.value;
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
|
||||
getSingleRGBColor(color, forToolbar = false) {
|
||||
if (color.isCustom) {
|
||||
return color.c;
|
||||
}
|
||||
const toolbarBg = forToolbar ? 'var(--zen-themed-toolbar-bg)' : 'var(--zen-themed-toolbar-bg-transparent)';
|
||||
return `color-mix(in srgb, rgb(${color.c[0]}, ${color.c[1]}, ${color.c[2]}) ${this.currentOpacity * 100}%, ${toolbarBg} ${(1 - this.currentOpacity) * 100}%)`;
|
||||
}
|
||||
|
||||
getGradient(colors, forToolbar = false) {
|
||||
const themedColors = this.themedColors(colors);
|
||||
if (themedColors.length === 0) {
|
||||
return forToolbar ? "var(--zen-themed-toolbar-bg)" : "var(--zen-themed-toolbar-bg-transparent)";
|
||||
} else if (themedColors.length === 1) {
|
||||
return this.getSingleRGBColor(themedColors[0], forToolbar);
|
||||
}
|
||||
return `linear-gradient(${this.currentRotation}deg, ${themedColors.map(color => this.getSingleRGBColor(color, forToolbar)).join(', ')})`;
|
||||
}
|
||||
|
||||
getTheme(colors, opacity = 0.5, rotation = 45, texture = 0) {
|
||||
return {
|
||||
type: 'gradient',
|
||||
gradientColors: colors.filter(color => color), // remove undefined
|
||||
opacity,
|
||||
rotation,
|
||||
texture,
|
||||
};
|
||||
}
|
||||
//TODO: add a better noise system that adds noise not just changes transparency
|
||||
updateNoise(texture) {
|
||||
const wrapper = document.getElementById('zen-main-app-wrapper');
|
||||
wrapper.style.setProperty('--zen-grainy-background-opacity', texture);
|
||||
}
|
||||
|
||||
hexToRgb(hex) {
|
||||
if (hex.startsWith('#')) {
|
||||
hex = hex.substring(1);
|
||||
}
|
||||
if (hex.length === 3) {
|
||||
hex = hex.split('').map(char => char + char).join('');
|
||||
}
|
||||
return [
|
||||
parseInt(hex.substring(0, 2), 16),
|
||||
parseInt(hex.substring(2, 4), 16),
|
||||
parseInt(hex.substring(4, 6), 16),
|
||||
];
|
||||
}
|
||||
|
||||
pSBC=(p,c0,c1,l)=>{
|
||||
let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
|
||||
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
|
||||
if(!this.pSBCr)this.pSBCr=(d)=>{
|
||||
let n=d.length,x={};
|
||||
if(n>9){
|
||||
[r,g,b,a]=d=d.split(","),n=d.length;
|
||||
if(n<3||n>4)return null;
|
||||
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
|
||||
}else{
|
||||
if(n==8||n==6||n<4)return null;
|
||||
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
|
||||
d=i(d.slice(1),16);
|
||||
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
|
||||
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
|
||||
}return x};
|
||||
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
|
||||
if(!f||!t)return null;
|
||||
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
|
||||
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
|
||||
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
|
||||
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
|
||||
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
|
||||
}
|
||||
|
||||
getMostDominantColor(allColors) {
|
||||
const colors = this.themedColors(allColors);
|
||||
const themedColors = colors.filter(color => !color.isCustom);
|
||||
if (themedColors.length === 0 || !this.allowWorkspaceColors) {
|
||||
return null;
|
||||
}
|
||||
// get the most dominant color in the gradient
|
||||
let dominantColor = themedColors[0].c;
|
||||
let dominantColorCount = 0;
|
||||
for (const color of themedColors) {
|
||||
const count = themedColors.filter(c => c.c[0] === color.c[0] && c.c[1] === color.c[1] && c.c[2] === color.c[2]).length;
|
||||
if (count > dominantColorCount) {
|
||||
dominantColorCount = count;
|
||||
dominantColor = color.c;
|
||||
}
|
||||
}
|
||||
const result = this.pSBC(
|
||||
this.isDarkMode ? 0.2 : -0.5,
|
||||
`rgb(${dominantColor[0]}, ${dominantColor[1]}, ${dominantColor[2]})`);
|
||||
return result?.match(/\d+/g).map(Number);
|
||||
}
|
||||
|
||||
async onWorkspaceChange(workspace, skipUpdate = false, theme = null) {
|
||||
|
||||
const uuid = workspace.uuid;
|
||||
// Use theme from workspace object or passed theme
|
||||
let workspaceTheme = theme || workspace.theme;
|
||||
|
||||
await this.foreachWindowAsActive(async (browser) => {
|
||||
if (!browser.gZenThemePicker._hasInitialized) {
|
||||
return;
|
||||
}
|
||||
// Do not rebuild if the workspace is not the same as the current one
|
||||
const windowWorkspace = await browser.ZenWorkspaces.getActiveWorkspace();
|
||||
if (windowWorkspace.uuid !== uuid && theme !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the theme from the window
|
||||
workspaceTheme = theme || windowWorkspace.theme;
|
||||
|
||||
if (!skipUpdate) {
|
||||
for (const dot of browser.gZenThemePicker.panel.querySelectorAll('.zen-theme-picker-dot')) {
|
||||
dot.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const appWrapper = browser.document.getElementById('zen-main-app-wrapper');
|
||||
if (!skipUpdate) {
|
||||
appWrapper.removeAttribute('animating');
|
||||
appWrapper.setAttribute('animating', 'true');
|
||||
browser.document.body.style.setProperty('--zen-main-browser-background-old',
|
||||
browser.document.body.style.getPropertyValue('--zen-main-browser-background')
|
||||
);
|
||||
browser.window.requestAnimationFrame(() => {
|
||||
setTimeout(() => {
|
||||
appWrapper.removeAttribute('animating');
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
browser.gZenThemePicker.resetCustomColorList();
|
||||
if (!workspaceTheme || workspaceTheme.type !== 'gradient') {
|
||||
const gradient = browser.gZenThemePicker.getGradient([]);
|
||||
const gradientToolbar = browser.gZenThemePicker.getGradient([], true);
|
||||
browser.document.documentElement.style.setProperty('--zen-main-browser-background', gradient);
|
||||
browser.document.documentElement.style.setProperty('--zen-main-browser-background-toolbar', gradientToolbar);
|
||||
browser.gZenThemePicker.updateNoise(0);
|
||||
browser.document.documentElement.style.setProperty('--zen-primary-color', this.getNativeAccentColor());
|
||||
return;
|
||||
}
|
||||
|
||||
browser.gZenThemePicker.currentOpacity = workspaceTheme.opacity ?? 0.5;
|
||||
browser.gZenThemePicker.currentRotation = workspaceTheme.rotation ?? 45;
|
||||
browser.gZenThemePicker.currentTexture = workspaceTheme.texture ?? 0;
|
||||
|
||||
browser.gZenThemePicker.numberOfDots = workspaceTheme.gradientColors.length;
|
||||
|
||||
browser.document.getElementById('PanelUI-zen-gradient-generator-opacity').value = browser.gZenThemePicker.currentOpacity;
|
||||
browser.document.getElementById('PanelUI-zen-gradient-generator-texture').value = browser.gZenThemePicker.currentTexture;
|
||||
browser.gZenThemePicker.setRotationInput(browser.gZenThemePicker.currentRotation);
|
||||
|
||||
const gradient = browser.gZenThemePicker.getGradient(workspaceTheme.gradientColors);
|
||||
const gradientToolbar = browser.gZenThemePicker.getGradient(workspaceTheme.gradientColors, true);
|
||||
browser.gZenThemePicker.updateNoise(workspaceTheme.texture);
|
||||
|
||||
for (const dot of workspaceTheme.gradientColors) {
|
||||
if (dot.isCustom) {
|
||||
browser.gZenThemePicker.addColorToCustomList(dot.c);
|
||||
}
|
||||
}
|
||||
|
||||
browser.document.documentElement.style.setProperty('--zen-main-browser-background', gradient);
|
||||
browser.document.documentElement.style.setProperty('--zen-main-browser-background-toolbar', gradientToolbar);
|
||||
|
||||
const dominantColor = this.getMostDominantColor(workspaceTheme.gradientColors);
|
||||
if (dominantColor) {
|
||||
browser.document.documentElement.style.setProperty('--zen-primary-color', `rgb(${dominantColor[0]}, ${dominantColor[1]}, ${dominantColor[2]})`);
|
||||
}
|
||||
|
||||
if (!skipUpdate) {
|
||||
browser.gZenThemePicker.recalculateDots(workspaceTheme.gradientColors);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getNativeAccentColor() {
|
||||
return Services.prefs.getStringPref('zen.theme.accent-color');
|
||||
}
|
||||
|
||||
resetCustomColorList() {
|
||||
this.customColorList.innerHTML = '';
|
||||
}
|
||||
|
||||
removeCustomColor(event) {
|
||||
const target = event.target.closest('.zen-theme-picker-custom-list-item');
|
||||
const color = target.getAttribute('data-color');
|
||||
const dots = this.panel.querySelectorAll('.zen-theme-picker-dot');
|
||||
for (const dot of dots) {
|
||||
if (dot.style.getPropertyValue('--zen-theme-picker-dot-color') === color) {
|
||||
dot.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
target.remove();
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
|
||||
recalculateDots(colors) {
|
||||
//THIS IS PART OF THE ISSUE
|
||||
for (const color of colors) {
|
||||
this.createDot(color, true);
|
||||
}
|
||||
}
|
||||
|
||||
async updateCurrentWorkspace(skipSave = true) {
|
||||
|
||||
this.updated = skipSave;
|
||||
const dots = this.panel.querySelectorAll('.zen-theme-picker-dot');
|
||||
const colors = Array.from(dots).map(dot => {
|
||||
const color = dot.style.getPropertyValue('--zen-theme-picker-dot-color');
|
||||
if (color === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const isCustom = dot.classList.contains('custom');
|
||||
return {c: isCustom ? color : color.match(/\d+/g).map(Number), isCustom};
|
||||
});
|
||||
const gradient = this.getTheme(colors, this.currentOpacity, this.currentRotation, this.currentTexture);
|
||||
let currentWorkspace = await ZenWorkspaces.getActiveWorkspace();
|
||||
|
||||
if(!skipSave) {
|
||||
await ZenWorkspacesStorage.saveWorkspaceTheme(currentWorkspace.uuid, gradient);
|
||||
await ZenWorkspaces._propagateWorkspaceData();
|
||||
ConfirmationHint.show(document.getElementById("PanelUI-menu-button"), "zen-panel-ui-gradient-generator-saved-message");
|
||||
currentWorkspace = await ZenWorkspaces.getActiveWorkspace();
|
||||
}
|
||||
|
||||
await this.onWorkspaceChange(currentWorkspace, true, skipSave ? gradient : null);
|
||||
}
|
||||
|
||||
async handlePanelClose() {
|
||||
if(this.updated) {
|
||||
await this.updateCurrentWorkspace(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
window.ZenThemePicker = ZenThemePicker;
|
||||
}
|
1014
src/browser/base/zen-components/ZenKeyboardShortcuts.mjs
Normal file
1014
src/browser/base/zen-components/ZenKeyboardShortcuts.mjs
Normal file
File diff suppressed because it is too large
Load Diff
464
src/browser/base/zen-components/ZenPinnedTabManager.mjs
Normal file
464
src/browser/base/zen-components/ZenPinnedTabManager.mjs
Normal file
@@ -0,0 +1,464 @@
|
||||
{
|
||||
const lazy = {};
|
||||
|
||||
class ZenPinnedTabsObserver {
|
||||
static ALL_EVENTS = ['TabPinned', 'TabUnpinned', 'TabClose'];
|
||||
|
||||
#listeners = [];
|
||||
|
||||
constructor() {
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenPinnedTabRestorePinnedTabsToPinnedUrl', 'zen.pinned-tab-manager.restore-pinned-tabs-to-pinned-url', false);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenPinnedTabCloseShortcutBehavior', 'zen.pinned-tab-manager.close-shortcut-behavior', 'switch');
|
||||
ChromeUtils.defineESModuleGetters(lazy, {E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs"});
|
||||
this.#listenPinnedTabEvents();
|
||||
}
|
||||
|
||||
#listenPinnedTabEvents() {
|
||||
const eventListener = this.#eventListener.bind(this);
|
||||
for (const event of ZenPinnedTabsObserver.ALL_EVENTS) {
|
||||
window.addEventListener(event, eventListener);
|
||||
}
|
||||
window.addEventListener('unload', () => {
|
||||
for (const event of ZenPinnedTabsObserver.ALL_EVENTS) {
|
||||
window.removeEventListener(event, eventListener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#eventListener(event) {
|
||||
for (const listener of this.#listeners) {
|
||||
listener(event.type, event);
|
||||
}
|
||||
}
|
||||
|
||||
addPinnedTabListener(listener) {
|
||||
this.#listeners.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
class ZenPinnedTabManager extends ZenPreloadedFeature {
|
||||
|
||||
init() {
|
||||
this.observer = new ZenPinnedTabsObserver();
|
||||
this._initClosePinnedTabShortcut();
|
||||
this._insertItemsIntoTabContextMenu();
|
||||
this.observer.addPinnedTabListener(this._onPinnedTabEvent.bind(this));
|
||||
|
||||
this._zenClickEventListener = this._onTabClick.bind(this);
|
||||
}
|
||||
|
||||
async initTabs() {
|
||||
await ZenPinnedTabsStorage.init();
|
||||
}
|
||||
|
||||
async _refreshPinnedTabs() {
|
||||
await this._initializePinsCache();
|
||||
this._initializePinnedTabs();
|
||||
}
|
||||
|
||||
async _initializePinsCache() {
|
||||
try {
|
||||
// Get pin data
|
||||
const pins = await ZenPinnedTabsStorage.getPins();
|
||||
|
||||
// Enhance pins with favicons
|
||||
const enhancedPins = await Promise.all(pins.map(async pin => {
|
||||
try {
|
||||
const faviconData = await PlacesUtils.promiseFaviconData(pin.url);
|
||||
return {
|
||||
...pin,
|
||||
iconUrl: faviconData?.uri?.spec || null
|
||||
};
|
||||
} catch(ex) {
|
||||
// If favicon fetch fails, continue without icon
|
||||
return {
|
||||
...pin,
|
||||
iconUrl: null
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
this._pinsCache = enhancedPins.sort((a, b) => {
|
||||
if (!a.workspaceUuid && b.workspaceUuid) return -1;
|
||||
if (a.workspaceUuid && !b.workspaceUuid) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
} catch (ex) {
|
||||
console.error("Failed to initialize pins cache:", ex);
|
||||
this._pinsCache = [];
|
||||
}
|
||||
|
||||
return this._pinsCache;
|
||||
}
|
||||
|
||||
_initializePinnedTabs() {
|
||||
const pins = this._pinsCache;
|
||||
if (!pins?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeTab = gBrowser.selectedTab;
|
||||
const pinnedTabsByUUID = new Map();
|
||||
const pinsToCreate = new Set(pins.map(p => p.uuid));
|
||||
|
||||
// First pass: identify existing tabs and remove those without pins
|
||||
for (let tab of gBrowser.tabs) {
|
||||
const pinId = tab.getAttribute("zen-pin-id");
|
||||
if (!pinId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pinsToCreate.has(pinId)) {
|
||||
// This is a valid pinned tab that matches a pin
|
||||
pinnedTabsByUUID.set(pinId, tab);
|
||||
pinsToCreate.delete(pinId);
|
||||
} else {
|
||||
// This is a pinned tab that no longer has a corresponding pin
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: create new tabs for pins that don't have tabs
|
||||
for (let pin of pins) {
|
||||
if (!pinsToCreate.has(pin.uuid)) {
|
||||
continue; // Skip pins that already have tabs
|
||||
}
|
||||
|
||||
let newTab = gBrowser.addTrustedTab(pin.url, {
|
||||
skipAnimation: true,
|
||||
userContextId: pin.containerTabId || 0,
|
||||
allowInheritPrincipal: false,
|
||||
createLazyBrowser: true,
|
||||
skipLoad: true,
|
||||
});
|
||||
|
||||
// Set the favicon from cache
|
||||
if (!!pin.iconUrl) {
|
||||
// TODO: Figure out if there is a better way -
|
||||
// calling gBrowser.setIcon messes shit up and should be avoided. I think this works for now.
|
||||
newTab.setAttribute("image", pin.iconUrl);
|
||||
}
|
||||
|
||||
newTab.setAttribute("zen-pin-id", pin.uuid);
|
||||
gBrowser.setInitialTabTitle(newTab, pin.title);
|
||||
|
||||
if (pin.workspaceUuid) {
|
||||
newTab.setAttribute("zen-workspace-id", pin.workspaceUuid);
|
||||
}
|
||||
|
||||
if (pin.isEssential) {
|
||||
newTab.setAttribute("zen-essential", "true");
|
||||
}
|
||||
|
||||
gBrowser.pinTab(newTab);
|
||||
}
|
||||
|
||||
// Restore active tab
|
||||
if (!activeTab.closing) {
|
||||
gBrowser.selectedTab = activeTab;
|
||||
}
|
||||
}
|
||||
|
||||
_onPinnedTabEvent(action, event) {
|
||||
const tab = event.target;
|
||||
switch (action) {
|
||||
case "TabPinned":
|
||||
tab._zenClickEventListener = this._zenClickEventListener;
|
||||
tab.addEventListener("click", tab._zenClickEventListener);
|
||||
this._setPinnedAttributes(tab);
|
||||
break;
|
||||
case "TabUnpinned":
|
||||
this._removePinnedAttributes(tab);
|
||||
if (tab._zenClickEventListener) {
|
||||
tab.removeEventListener("click", tab._zenClickEventListener);
|
||||
delete tab._zenClickEventListener;
|
||||
}
|
||||
break;
|
||||
case "TabClose":
|
||||
this._removePinnedAttributes(tab);
|
||||
break;
|
||||
default:
|
||||
console.warn('ZenPinnedTabManager: Unhandled tab event', action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onTabClick(e) {
|
||||
const tab = e.target?.closest("tab");
|
||||
if (e.button === 1 && tab) {
|
||||
this._onCloseTabShortcut(e, tab);
|
||||
}
|
||||
}
|
||||
|
||||
async resetPinnedTab(tab) {
|
||||
|
||||
if (!tab) {
|
||||
tab = TabContextMenu.contextTab;
|
||||
}
|
||||
|
||||
if (!tab || !tab.pinned) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._resetTabToStoredState(tab);
|
||||
}
|
||||
|
||||
async replacePinnedUrlWithCurrent() {
|
||||
const tab = TabContextMenu.contextTab;
|
||||
if (!tab || !tab.pinned || !tab.getAttribute("zen-pin-id")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browser = tab.linkedBrowser;
|
||||
|
||||
const pin = this._pinsCache.find(pin => pin.uuid === tab.getAttribute("zen-pin-id"));
|
||||
|
||||
if (!pin) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userContextId = tab.getAttribute("usercontextid");
|
||||
|
||||
pin.title = tab.label || browser.contentTitle;
|
||||
pin.url = browser.currentURI.spec;
|
||||
pin.workspaceUuid = tab.getAttribute("zen-workspace-id");
|
||||
pin.userContextId = userContextId ? parseInt(userContextId, 10) : 0;
|
||||
|
||||
await ZenPinnedTabsStorage.savePin(pin);
|
||||
await this._refreshPinnedTabs();
|
||||
}
|
||||
|
||||
async _setPinnedAttributes(tab) {
|
||||
|
||||
if (tab.hasAttribute("zen-pin-id")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browser = tab.linkedBrowser;
|
||||
|
||||
const uuid = gZenUIManager.generateUuidv4();
|
||||
const userContextId = tab.getAttribute("usercontextid");
|
||||
|
||||
let entry = null;
|
||||
|
||||
if(tab.getAttribute("zen-pinned-entry")) {
|
||||
entry = JSON.parse(tab.getAttribute("zen-pinned-entry"));
|
||||
}
|
||||
|
||||
await ZenPinnedTabsStorage.savePin({
|
||||
uuid,
|
||||
title: entry?.title || tab.label || browser.contentTitle,
|
||||
url: entry?.url || browser.currentURI.spec,
|
||||
containerTabId: userContextId ? parseInt(userContextId, 10) : 0,
|
||||
workspaceUuid: tab.getAttribute("zen-workspace-id"),
|
||||
isEssential: tab.getAttribute("zen-essential") === "true"
|
||||
});
|
||||
|
||||
tab.setAttribute("zen-pin-id", uuid);
|
||||
|
||||
// This is used while migrating old pins to new system - we don't want to refresh when migrating
|
||||
if (tab.getAttribute("zen-pinned-entry")) {
|
||||
tab.removeAttribute("zen-pinned-entry");
|
||||
return;
|
||||
}
|
||||
|
||||
await this._refreshPinnedTabs();
|
||||
}
|
||||
|
||||
async _removePinnedAttributes(tab) {
|
||||
if(!tab.getAttribute("zen-pin-id")) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ZenPinnedTabsStorage.removePin(tab.getAttribute("zen-pin-id"));
|
||||
|
||||
tab.removeAttribute("zen-pin-id");
|
||||
|
||||
if(!tab.hasAttribute("zen-workspace-id") && ZenWorkspaces.workspaceEnabled) {
|
||||
const workspace = await ZenWorkspaces.getActiveWorkspace();
|
||||
tab.setAttribute("zen-workspace-id", workspace.uuid);
|
||||
}
|
||||
|
||||
await this._refreshPinnedTabs();
|
||||
}
|
||||
|
||||
_initClosePinnedTabShortcut() {
|
||||
let cmdClose = document.getElementById('cmd_close');
|
||||
|
||||
if (cmdClose) {
|
||||
cmdClose.addEventListener('command', this._onCloseTabShortcut.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
_onCloseTabShortcut(event, selectedTab = gBrowser.selectedTab) {
|
||||
if (
|
||||
!selectedTab?.pinned
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const behavior = lazy.zenPinnedTabCloseShortcutBehavior;
|
||||
|
||||
switch (behavior) {
|
||||
case 'close':
|
||||
gBrowser.removeTab(selectedTab, { animate: true });
|
||||
break;
|
||||
case 'reset-unload-switch':
|
||||
case 'unload-switch':
|
||||
case 'reset-switch':
|
||||
case 'switch':
|
||||
this._handleTabSwitch(selectedTab);
|
||||
if (behavior.includes('reset')) {
|
||||
this._resetTabToStoredState(selectedTab);
|
||||
}
|
||||
if (behavior.includes('unload')) {
|
||||
gBrowser.discardBrowser(selectedTab);
|
||||
}
|
||||
break;
|
||||
case 'reset':
|
||||
this._resetTabToStoredState(selectedTab);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_handleTabSwitch(selectedTab) {
|
||||
if(selectedTab !== gBrowser.selectedTab) {
|
||||
return;
|
||||
}
|
||||
const findNextTab = (direction) =>
|
||||
gBrowser.tabContainer.findNextTab(selectedTab, {
|
||||
direction,
|
||||
filter: tab => !tab.hidden && !tab.pinned,
|
||||
});
|
||||
|
||||
let nextTab = findNextTab(1) || findNextTab(-1);
|
||||
|
||||
if (!nextTab) {
|
||||
ZenWorkspaces._createNewTabForWorkspace({ uuid: ZenWorkspaces.activeWorkspace });
|
||||
|
||||
nextTab = findNextTab(1) || findNextTab(-1);
|
||||
}
|
||||
|
||||
if (nextTab) {
|
||||
gBrowser.selectedTab = nextTab;
|
||||
}
|
||||
}
|
||||
|
||||
async _resetTabToStoredState(tab) {
|
||||
const id = tab.getAttribute("zen-pin-id");
|
||||
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pin = this._pinsCache.find(pin => pin.uuid === id);
|
||||
|
||||
if (pin) {
|
||||
const tabState = SessionStore.getTabState(tab);
|
||||
const state = JSON.parse(tabState);
|
||||
let icon = undefined;
|
||||
try {
|
||||
icon = await PlacesUtils.promiseFaviconData(pin.url);
|
||||
} catch (e) {
|
||||
console.warn("Error trying to get favicon for pinned tab", e);
|
||||
}
|
||||
|
||||
state.entries = [{
|
||||
url: pin.url,
|
||||
title: pin.title,
|
||||
triggeringPrincipal_base64: lazy.E10SUtils.SERIALIZED_SYSTEMPRINCIPAL
|
||||
}];
|
||||
if (icon instanceof Ci.nsIURI || typeof icon === "string") {
|
||||
state.image = icon;
|
||||
} else {
|
||||
state.image = null;
|
||||
}
|
||||
state.index = 0;
|
||||
|
||||
SessionStore.setTabState(tab, state);
|
||||
}
|
||||
}
|
||||
|
||||
addToEssentials() {
|
||||
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
tab.setAttribute("zen-essential", "true");
|
||||
if(tab.hasAttribute("zen-workspace-id")) {
|
||||
tab.removeAttribute("zen-workspace-id");
|
||||
}
|
||||
if (tab.pinned) {
|
||||
gBrowser.unpinTab(tab);
|
||||
}
|
||||
gBrowser.pinTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
removeEssentials() {
|
||||
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
tab.removeAttribute("zen-essential");
|
||||
if(ZenWorkspaces.workspaceEnabled && ZenWorkspaces.getActiveWorkspaceFromCache.uuid) {
|
||||
tab.setAttribute("zen-workspace-id", ZenWorkspaces.getActiveWorkspaceFromCache.uuid);
|
||||
}
|
||||
gBrowser.unpinTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
_insertItemsIntoTabContextMenu() {
|
||||
const elements = window.MozXULElement.parseXULToFragment(`
|
||||
<menuseparator id="context_zen-pinned-tab-separator" hidden="true"/>
|
||||
<menuitem id="context_zen-replace-pinned-url-with-current"
|
||||
data-lazy-l10n-id="tab-context-zen-replace-pinned-url-with-current"
|
||||
hidden="true"
|
||||
oncommand="gZenPinnedTabManager.replacePinnedUrlWithCurrent();"/>
|
||||
<menuitem id="context_zen-reset-pinned-tab"
|
||||
data-lazy-l10n-id="tab-context-zen-reset-pinned-tab"
|
||||
hidden="true"
|
||||
oncommand="gZenPinnedTabManager.resetPinnedTab();"/>
|
||||
`);
|
||||
document.getElementById('tabContextMenu').appendChild(elements);
|
||||
|
||||
const element = window.MozXULElement.parseXULToFragment(`
|
||||
<menuitem id="context_zen-add-essential"
|
||||
data-lazy-l10n-id="tab-context-zen-add-essential"
|
||||
hidden="true"
|
||||
oncommand="gZenPinnedTabManager.addToEssentials();"/>
|
||||
<menuitem id="context_zen-remove-essential"
|
||||
data-lazy-l10n-id="tab-context-zen-remove-essential"
|
||||
hidden="true"
|
||||
oncommand="gZenPinnedTabManager.removeEssentials();"/>
|
||||
`);
|
||||
|
||||
document.getElementById('context_pinTab')?.after(element);
|
||||
}
|
||||
|
||||
resetPinnedTabData(tabData) {
|
||||
if (lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl && tabData.pinned && tabData.zenPinnedEntry) {
|
||||
tabData.entries = [JSON.parse(tabData.zenPinnedEntry)];
|
||||
tabData.image = tabData.zenPinnedIcon;
|
||||
tabData.index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updatePinnedTabContextMenu(contextTab) {
|
||||
const isVisible = contextTab.pinned && !contextTab.multiselected;
|
||||
document.getElementById("context_zen-reset-pinned-tab").hidden = !isVisible || !contextTab.getAttribute("zen-pin-id");
|
||||
document.getElementById("context_zen-replace-pinned-url-with-current").hidden = !isVisible;
|
||||
document.getElementById("context_zen-add-essential").hidden = contextTab.getAttribute("zen-essential");
|
||||
document.getElementById("context_zen-remove-essential").hidden = !contextTab.getAttribute("zen-essential");
|
||||
document.getElementById("context_unpinTab").hidden = document.getElementById("context_unpinTab").hidden || contextTab.getAttribute("zen-essential");
|
||||
document.getElementById("context_unpinSelectedTabs").hidden = document.getElementById("context_unpinSelectedTabs").hidden || contextTab.getAttribute("zen-essential");
|
||||
document.getElementById("context_zen-pinned-tab-separator").hidden = !isVisible;
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenPinnedTabManager = new ZenPinnedTabManager();
|
||||
}
|
341
src/browser/base/zen-components/ZenPinnedTabsStorage.mjs
Normal file
341
src/browser/base/zen-components/ZenPinnedTabsStorage.mjs
Normal file
@@ -0,0 +1,341 @@
|
||||
var ZenPinnedTabsStorage = {
|
||||
async init() {
|
||||
console.log('ZenPinnedTabsStorage: Initializing...');
|
||||
await this._ensureTable();
|
||||
},
|
||||
|
||||
async _ensureTable() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage._ensureTable', async (db) => {
|
||||
// Create the pins table if it doesn't exist
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS zen_pins (
|
||||
id INTEGER PRIMARY KEY,
|
||||
uuid TEXT UNIQUE NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
url TEXT,
|
||||
container_id INTEGER,
|
||||
workspace_uuid TEXT,
|
||||
position INTEGER NOT NULL DEFAULT 0,
|
||||
is_essential BOOLEAN NOT NULL DEFAULT 0,
|
||||
is_group BOOLEAN NOT NULL DEFAULT 0,
|
||||
parent_uuid TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (parent_uuid) REFERENCES zen_pins(uuid) ON DELETE SET NULL
|
||||
)
|
||||
`);
|
||||
|
||||
|
||||
// Create indices
|
||||
await db.execute(`
|
||||
CREATE INDEX IF NOT EXISTS idx_zen_pins_uuid ON zen_pins(uuid)
|
||||
`);
|
||||
|
||||
await db.execute(`
|
||||
CREATE INDEX IF NOT EXISTS idx_zen_pins_parent_uuid ON zen_pins(parent_uuid)
|
||||
`);
|
||||
|
||||
// Create the changes tracking table if it doesn't exist
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS zen_pins_changes (
|
||||
uuid TEXT PRIMARY KEY,
|
||||
timestamp INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Create an index on the uuid column for changes tracking table
|
||||
await db.execute(`
|
||||
CREATE INDEX IF NOT EXISTS idx_zen_pins_changes_uuid ON zen_pins_changes(uuid)
|
||||
`);
|
||||
|
||||
await gZenPinnedTabManager._refreshPinnedTabs();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Private helper method to notify observers with a list of changed UUIDs.
|
||||
* @param {string} event - The observer event name.
|
||||
* @param {Array<string>} uuids - Array of changed workspace UUIDs.
|
||||
*/
|
||||
_notifyPinsChanged(event, uuids) {
|
||||
if (uuids.length === 0) return; // No changes to notify
|
||||
|
||||
// Convert the array of UUIDs to a JSON string
|
||||
const data = JSON.stringify(uuids);
|
||||
|
||||
Services.obs.notifyObservers(null, event, data);
|
||||
},
|
||||
|
||||
async savePin(pin, notifyObservers = true) {
|
||||
const changedUUIDs = new Set();
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.savePin', async (db) => {
|
||||
await db.executeTransaction(async () => {
|
||||
const now = Date.now();
|
||||
|
||||
let newPosition;
|
||||
if ('position' in pin && Number.isFinite(pin.position)) {
|
||||
newPosition = pin.position;
|
||||
} else {
|
||||
// Get the maximum position within the same parent group (or null for root level)
|
||||
const maxPositionResult = await db.execute(`
|
||||
SELECT MAX("position") as max_position
|
||||
FROM zen_pins
|
||||
WHERE COALESCE(parent_uuid, '') = COALESCE(:parent_uuid, '')
|
||||
`, { parent_uuid: pin.parentUuid || null });
|
||||
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
|
||||
newPosition = maxPosition + 1000;
|
||||
}
|
||||
|
||||
// Insert or replace the pin
|
||||
await db.executeCached(`
|
||||
INSERT OR REPLACE INTO zen_pins (
|
||||
uuid, title, url, container_id, workspace_uuid, position,
|
||||
is_essential, is_group, parent_uuid, created_at, updated_at
|
||||
) VALUES (
|
||||
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
|
||||
:is_essential, :is_group, :parent_uuid,
|
||||
COALESCE((SELECT created_at FROM zen_pins WHERE uuid = :uuid), :now),
|
||||
:now
|
||||
)
|
||||
`, {
|
||||
uuid: pin.uuid,
|
||||
title: pin.title,
|
||||
url: pin.isGroup ? null : pin.url,
|
||||
container_id: pin.containerTabId || null,
|
||||
workspace_uuid: pin.workspaceUuid || null,
|
||||
position: newPosition,
|
||||
is_essential: pin.isEssential || false,
|
||||
is_group: pin.isGroup || false,
|
||||
parent_uuid: pin.parentUuid || null,
|
||||
now
|
||||
});
|
||||
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid: pin.uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
|
||||
changedUUIDs.add(pin.uuid);
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
});
|
||||
|
||||
if (notifyObservers) {
|
||||
this._notifyPinsChanged("zen-pin-updated", Array.from(changedUUIDs));
|
||||
}
|
||||
},
|
||||
|
||||
async getPins() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const rows = await db.executeCached(`
|
||||
SELECT * FROM zen_pins
|
||||
ORDER BY parent_uuid NULLS FIRST, position ASC
|
||||
`);
|
||||
return rows.map((row) => ({
|
||||
uuid: row.getResultByName('uuid'),
|
||||
title: row.getResultByName('title'),
|
||||
url: row.getResultByName('url'),
|
||||
containerTabId: row.getResultByName('container_id'),
|
||||
workspaceUuid: row.getResultByName('workspace_uuid'),
|
||||
position: row.getResultByName('position'),
|
||||
isEssential: Boolean(row.getResultByName('is_essential')),
|
||||
isGroup: Boolean(row.getResultByName('is_group')),
|
||||
parentUuid: row.getResultByName('parent_uuid')
|
||||
}));
|
||||
},
|
||||
|
||||
async getGroupChildren(groupUuid) {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const rows = await db.executeCached(`
|
||||
SELECT * FROM zen_pins
|
||||
WHERE parent_uuid = :groupUuid
|
||||
ORDER BY position ASC
|
||||
`, { groupUuid });
|
||||
|
||||
return rows.map((row) => ({
|
||||
uuid: row.getResultByName('uuid'),
|
||||
title: row.getResultByName('title'),
|
||||
url: row.getResultByName('url'),
|
||||
containerTabId: row.getResultByName('container_id'),
|
||||
workspaceUuid: row.getResultByName('workspace_uuid'),
|
||||
position: row.getResultByName('position'),
|
||||
isEssential: Boolean(row.getResultByName('is_essential')),
|
||||
isGroup: Boolean(row.getResultByName('is_group')),
|
||||
parentUuid: row.getResultByName('parent_uuid')
|
||||
}));
|
||||
},
|
||||
|
||||
async removePin(uuid, notifyObservers = true) {
|
||||
const changedUUIDs = [uuid];
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.removePin', async (db) => {
|
||||
await db.executeTransaction(async () => {
|
||||
// Get all child UUIDs first for change tracking
|
||||
const children = await db.execute(
|
||||
`SELECT uuid FROM zen_pins WHERE parent_uuid = :uuid`,
|
||||
{ uuid }
|
||||
);
|
||||
|
||||
// Add child UUIDs to changedUUIDs array
|
||||
for (const child of children) {
|
||||
changedUUIDs.push(child.getResultByName('uuid'));
|
||||
}
|
||||
|
||||
// Delete all children in a single statement
|
||||
await db.execute(
|
||||
`DELETE FROM zen_pins WHERE parent_uuid = :uuid`,
|
||||
{ uuid }
|
||||
);
|
||||
|
||||
// Delete the pin/group itself
|
||||
await db.execute(
|
||||
`DELETE FROM zen_pins WHERE uuid = :uuid`,
|
||||
{ uuid }
|
||||
);
|
||||
|
||||
// Record the changes
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
for (const changedUuid of changedUUIDs) {
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid: changedUuid,
|
||||
timestamp: now
|
||||
});
|
||||
}
|
||||
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
});
|
||||
|
||||
if (notifyObservers) {
|
||||
this._notifyPinsChanged("zen-pin-removed", changedUUIDs);
|
||||
}
|
||||
},
|
||||
|
||||
async wipeAllPins() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.wipeAllPins', async (db) => {
|
||||
await db.execute(`DELETE FROM zen_pins`);
|
||||
await db.execute(`DELETE FROM zen_pins_changes`);
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
},
|
||||
|
||||
async markChanged(uuid) {
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.markChanged', async (db) => {
|
||||
const now = Date.now();
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async getChangedIDs() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const rows = await db.execute(`
|
||||
SELECT uuid, timestamp FROM zen_pins_changes
|
||||
`);
|
||||
const changes = {};
|
||||
for (const row of rows) {
|
||||
changes[row.getResultByName('uuid')] = row.getResultByName('timestamp');
|
||||
}
|
||||
return changes;
|
||||
},
|
||||
|
||||
async clearChangedIDs() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.clearChangedIDs', async (db) => {
|
||||
await db.execute(`DELETE FROM zen_pins_changes`);
|
||||
});
|
||||
},
|
||||
|
||||
shouldReorderPins(before, current, after) {
|
||||
const minGap = 1; // Minimum allowed gap between positions
|
||||
return (before !== null && current - before < minGap) || (after !== null && after - current < minGap);
|
||||
},
|
||||
|
||||
async reorderAllPins(db, changedUUIDs) {
|
||||
const pins = await db.execute(`
|
||||
SELECT uuid
|
||||
FROM zen_pins
|
||||
ORDER BY position ASC
|
||||
`);
|
||||
|
||||
for (let i = 0; i < pins.length; i++) {
|
||||
const newPosition = (i + 1) * 1000; // Use large increments
|
||||
await db.execute(`
|
||||
UPDATE zen_pins
|
||||
SET position = :newPosition
|
||||
WHERE uuid = :uuid
|
||||
`, { newPosition, uuid: pins[i].getResultByName('uuid') });
|
||||
changedUUIDs.add(pins[i].getResultByName('uuid'));
|
||||
}
|
||||
},
|
||||
|
||||
async updateLastChangeTimestamp(db) {
|
||||
const now = Date.now();
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO moz_meta (key, value)
|
||||
VALUES ('zen_pins_last_change', :now)
|
||||
`, { now });
|
||||
},
|
||||
|
||||
async getLastChangeTimestamp() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const result = await db.executeCached(`
|
||||
SELECT value FROM moz_meta WHERE key = 'zen_pins_last_change'
|
||||
`);
|
||||
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
|
||||
},
|
||||
|
||||
async updatePinPositions(pins) {
|
||||
const changedUUIDs = new Set();
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.updatePinPositions', async (db) => {
|
||||
await db.executeTransaction(async () => {
|
||||
const now = Date.now();
|
||||
|
||||
for (let i = 0; i < pins.length; i++) {
|
||||
const pin = pins[i];
|
||||
const newPosition = (i + 1) * 1000;
|
||||
|
||||
await db.execute(`
|
||||
UPDATE zen_pins
|
||||
SET position = :newPosition
|
||||
WHERE uuid = :uuid
|
||||
`, { newPosition, uuid: pin.uuid });
|
||||
|
||||
changedUUIDs.add(pin.uuid);
|
||||
|
||||
// Record the change
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid: pin.uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
}
|
||||
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
});
|
||||
|
||||
this._notifyPinsChanged("zen-pin-updated", Array.from(changedUUIDs));
|
||||
},
|
||||
|
||||
async __dropTables() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.__dropTables', async (db) => {
|
||||
await db.execute(`DROP TABLE IF EXISTS zen_pins`);
|
||||
await db.execute(`DROP TABLE IF EXISTS zen_pins_changes`);
|
||||
});
|
||||
}
|
||||
};
|
138
src/browser/base/zen-components/ZenProfileDialogUI.mjs
Normal file
138
src/browser/base/zen-components/ZenProfileDialogUI.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 ZenProfileDialogUI = {
|
||||
showSubView(parent, event) {
|
||||
let element = parent.querySelector('.zen-side-bar-profiles-button-panel-correction') || parent;
|
||||
PanelUI.showSubView('PanelUI-zen-profiles', element, event);
|
||||
this._updateProfilesList();
|
||||
this._updateCurentProfileId();
|
||||
},
|
||||
|
||||
_updateProfilesList() {
|
||||
let parentList = document.getElementById('PanelUI-zen-profiles-list');
|
||||
this._emptyUserList(parentList);
|
||||
if (this._getProfilesSize(ProfileService.profiles) <= 1) {
|
||||
return;
|
||||
}
|
||||
parentList.appendChild(document.createElement('toolbarseparator'));
|
||||
for (let profile of ProfileService.profiles) {
|
||||
if (profile == ProfileService.currentProfile) {
|
||||
continue;
|
||||
}
|
||||
let item = document.createElement('div');
|
||||
item.onclick = () => this._openProfile(profile);
|
||||
item.className = 'PanelUI-zen-profiles-item';
|
||||
let avatar = document.createElement('img');
|
||||
avatar.className = 'PanelUI-zen-profiles-item-avatar';
|
||||
let name = document.createElement('div');
|
||||
name.className = 'PanelUI-zen-profiles-item-name';
|
||||
name.appendChild(document.createTextNode(profile.name));
|
||||
name.container = true;
|
||||
avatar.setAttribute('src', ZenThemeModifier._getThemedAvatar(profile.zenAvatarPath));
|
||||
item.appendChild(avatar);
|
||||
item.appendChild(name);
|
||||
parentList.appendChild(item);
|
||||
}
|
||||
},
|
||||
|
||||
_emptyUserList(element) {
|
||||
element.innerHTML = '';
|
||||
},
|
||||
|
||||
_updateCurentProfileId() {
|
||||
let currentProfile = ProfileService.currentProfile;
|
||||
if (!currentProfile) return;
|
||||
let nameContainer = document.getElementById('PanelUI-zen-profiles-current-name');
|
||||
nameContainer.textContent = currentProfile.name;
|
||||
},
|
||||
|
||||
_openProfile(profile) {
|
||||
Services.startup.createInstanceWithProfile(profile);
|
||||
},
|
||||
|
||||
_getProfilesSize(profiles) {
|
||||
let size = 0;
|
||||
for (let _ of profiles) {
|
||||
size += 1;
|
||||
}
|
||||
return size;
|
||||
},
|
||||
|
||||
createProfileWizard() {
|
||||
// This should be rewritten in HTML eventually.
|
||||
// TODO: it could be `window.browsingContext.topChromeWindow.gDialogBox.open` but it does not work with the callback?
|
||||
window.browsingContext.topChromeWindow.openDialog(
|
||||
'chrome://mozapps/content/profile/createProfileWizard.xhtml',
|
||||
'',
|
||||
'centerscreen,chrome,modal,titlebar',
|
||||
ProfileService,
|
||||
{
|
||||
CreateProfile: async (profile) => {
|
||||
try {
|
||||
ProfileService.defaultProfile = profile;
|
||||
this._flush();
|
||||
this._openProfile(profile);
|
||||
} catch (e) {
|
||||
// This can happen on dev-edition.
|
||||
let [title, msg] = await document.l10n.formatValues([
|
||||
{ id: 'profiles-cannot-set-as-default-title' },
|
||||
{ id: 'profiles-cannot-set-as-default-message' },
|
||||
]);
|
||||
|
||||
Services.prompt.alert(window, title, msg);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async _flush() {
|
||||
try {
|
||||
ProfileService.flush();
|
||||
this._updateProfilesList();
|
||||
} catch (e) {
|
||||
let [title, msg, button] = await document.l10n.formatValues([
|
||||
{ id: 'profiles-flush-fail-title' },
|
||||
{
|
||||
id: e.result == Cr.NS_ERROR_DATABASE_CHANGED ? 'profiles-flush-conflict' : 'profiles-flush-failed',
|
||||
},
|
||||
{ id: 'profiles-flush-restart-button' },
|
||||
]);
|
||||
|
||||
const PS = Ci.nsIPromptService;
|
||||
let result = Services.prompt.confirmEx(
|
||||
window,
|
||||
title,
|
||||
msg,
|
||||
PS.BUTTON_POS_0 * PS.BUTTON_TITLE_CANCEL + PS.BUTTON_POS_1 * PS.BUTTON_TITLE_IS_STRING,
|
||||
null,
|
||||
button,
|
||||
null,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
if (result == 1) {
|
||||
this._restart(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_restart(safeMode) {
|
||||
let cancelQuit = Cc['@mozilla.org/supports-PRBool;1'].createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, 'quit-application-requested', 'restart');
|
||||
|
||||
if (cancelQuit.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let flags = Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart;
|
||||
|
||||
if (safeMode) {
|
||||
Services.startup.restartInSafeMode(flags);
|
||||
} else {
|
||||
Services.startup.quit(flags);
|
||||
}
|
||||
},
|
||||
};
|
788
src/browser/base/zen-components/ZenSidebarManager.mjs
Normal file
788
src/browser/base/zen-components/ZenSidebarManager.mjs
Normal file
@@ -0,0 +1,788 @@
|
||||
class ZenBrowserManagerSidebar extends ZenDOMOperatedFeature {
|
||||
_sidebarElement = null;
|
||||
_currentPanel = null;
|
||||
_lastOpenedPanel = null;
|
||||
_hasChangedConfig = true;
|
||||
_splitterElement = null;
|
||||
_hSplitterElement = null;
|
||||
_hasRegisteredPinnedClickOutside = false;
|
||||
_isDragging = false;
|
||||
contextTab = null;
|
||||
sidebar = null;
|
||||
forwardButton = null;
|
||||
backButton = null;
|
||||
progressListener = null;
|
||||
_tabBrowserSet = new WeakMap();
|
||||
tabBox;
|
||||
|
||||
DEFAULT_MOBILE_USER_AGENT = `Mozilla/5.0 (Android 12; Mobile; rv:129.0) Gecko/20100101 Firefox/${AppConstants.ZEN_FIREFOX_VERSION}`;
|
||||
MAX_SIDEBAR_PANELS = Services.prefs.getIntPref('zen.sidebar.max-webpanels');
|
||||
|
||||
init() {
|
||||
ChromeUtils.defineLazyGetter(this, 'sidebar', () => document.getElementById('zen-sidebar-web-panel'));
|
||||
ChromeUtils.defineLazyGetter(this, 'forwardButton', () => document.getElementById('zen-sidebar-web-panel-forward'));
|
||||
ChromeUtils.defineLazyGetter(this, 'backButton', () => document.getElementById('zen-sidebar-web-panel-back'));
|
||||
ChromeUtils.defineLazyGetter(this, 'tabBox', () => document.getElementById('tabbrowser-tabbox'));
|
||||
|
||||
this.onlySafeWidthAndHeight();
|
||||
|
||||
this.initProgressListener();
|
||||
this.update();
|
||||
this.close(); // avoid caching
|
||||
this.listenForPrefChanges();
|
||||
this.insertIntoContextMenu();
|
||||
this.addPositioningListeners();
|
||||
}
|
||||
|
||||
onlySafeWidthAndHeight() {
|
||||
const panel = document.getElementById('zen-sidebar-web-panel');
|
||||
const width = panel.style.width;
|
||||
const height = panel.style.height;
|
||||
panel.setAttribute('style', '');
|
||||
panel.style.width = width;
|
||||
panel.style.height = height;
|
||||
}
|
||||
|
||||
initProgressListener() {
|
||||
this.progressListener = {
|
||||
QueryInterface: ChromeUtils.generateQI(['nsIWebProgressListener', 'nsISupportsWeakReference']),
|
||||
onLocationChange: function (aWebProgress, aRequest, aLocation, aFlags) {
|
||||
const browser = this._getCurrentBrowser();
|
||||
if (!browser) return;
|
||||
const forwardDisabled = this.forwardButton.hasAttribute('disabled');
|
||||
const backDisabled = this.backButton.hasAttribute('disabled');
|
||||
|
||||
if (browser.canGoForward === forwardDisabled) {
|
||||
if (browser.canGoForward) {
|
||||
this.forwardButton.removeAttribute('disabled');
|
||||
} else {
|
||||
this.forwardButton.setAttribute('disabled', true);
|
||||
}
|
||||
}
|
||||
if (browser.canGoBack === backDisabled) {
|
||||
if (browser.canGoBack) {
|
||||
this.backButton.removeAttribute('disabled');
|
||||
} else {
|
||||
this.backButton.setAttribute('disabled', true);
|
||||
}
|
||||
}
|
||||
}.bind(gZenBrowserManagerSidebar),
|
||||
};
|
||||
}
|
||||
|
||||
get sidebarData() {
|
||||
let services = Services.prefs.getStringPref('zen.sidebar.data');
|
||||
if (services === '') {
|
||||
return {};
|
||||
}
|
||||
return JSON.parse(services);
|
||||
}
|
||||
|
||||
get shouldCloseOnBlur() {
|
||||
return Services.prefs.getBoolPref('zen.sidebar.close-on-blur');
|
||||
}
|
||||
|
||||
listenForPrefChanges() {
|
||||
Services.prefs.addObserver('zen.sidebar.data', this.handleEvent.bind(this));
|
||||
Services.prefs.addObserver('zen.sidebar.enabled', this.handleEvent.bind(this));
|
||||
|
||||
this.handleEvent();
|
||||
}
|
||||
|
||||
addPositioningListeners() {
|
||||
this.sidebar
|
||||
.querySelectorAll('.zen-sidebar-web-panel-splitter')
|
||||
.forEach((s) => s.addEventListener('mousedown', this.handleSplitterMouseDown.bind(this)));
|
||||
this.sidebarHeader.addEventListener('mousedown', this.handleDragPanel.bind(this));
|
||||
window.addEventListener('resize', this.onWindowResize.bind(this));
|
||||
}
|
||||
|
||||
handleSplitterMouseDown(mouseDownEvent) {
|
||||
if (this._isDragging) return;
|
||||
this._isDragging = true;
|
||||
|
||||
const isHorizontal = mouseDownEvent.target.getAttribute('orient') === 'horizontal';
|
||||
setCursor(isHorizontal ? 'n-resize' : 'ew-resize');
|
||||
const reverse = ['left', 'top'].includes(mouseDownEvent.target.getAttribute('side'));
|
||||
const direction = isHorizontal ? 'height' : 'width';
|
||||
const axis = isHorizontal ? 'Y' : 'X';
|
||||
|
||||
const computedStyle = window.getComputedStyle(this.sidebar);
|
||||
const maxSize = parseInt(computedStyle.getPropertyValue(`max-${direction}`).match(/(\d+)px/)?.[1]) || Infinity;
|
||||
const minSize = parseInt(computedStyle.getPropertyValue(`min-${direction}`).match(/(\d+)px/)?.[1]) || 0;
|
||||
|
||||
const sidebarSizeStart = this.sidebar.getBoundingClientRect()[direction];
|
||||
|
||||
const startPos = mouseDownEvent[`screen${axis}`];
|
||||
|
||||
const toAdjust = isHorizontal ? 'top' : 'left';
|
||||
const sidebarPosStart = parseInt(this.sidebar.style[toAdjust].match(/\d+/));
|
||||
|
||||
let mouseMove = function (e) {
|
||||
let mouseMoved = e[`screen${axis}`] - startPos;
|
||||
if (reverse) {
|
||||
mouseMoved *= -1;
|
||||
}
|
||||
let newSize = sidebarSizeStart + mouseMoved;
|
||||
let currentMax = maxSize;
|
||||
const wrapperBox = this.sidebarWrapper.getBoundingClientRect();
|
||||
let maxWrapperSize = Infinity;
|
||||
if (this.isFloating) {
|
||||
maxWrapperSize = reverse ? sidebarPosStart + sidebarSizeStart : wrapperBox[direction] - sidebarPosStart;
|
||||
}
|
||||
newSize = Math.max(minSize, Math.min(currentMax, maxWrapperSize, newSize));
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
if (reverse) {
|
||||
const actualMoved = newSize - sidebarSizeStart;
|
||||
this.sidebar.style[toAdjust] = sidebarPosStart - actualMoved + 'px';
|
||||
}
|
||||
this.sidebar.style[direction] = `${newSize}px`;
|
||||
});
|
||||
}.bind(this);
|
||||
|
||||
document.addEventListener('mousemove', mouseMove);
|
||||
document.addEventListener(
|
||||
'mouseup',
|
||||
() => {
|
||||
document.removeEventListener('mousemove', mouseMove);
|
||||
this._isDragging = false;
|
||||
setCursor('auto');
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
handleDragPanel(mouseDownEvent) {
|
||||
if (this.sidebarHeaderButtons.find((b) => b.contains(mouseDownEvent.target))) {
|
||||
return;
|
||||
}
|
||||
this._isDragging = true;
|
||||
const startTop = this.sidebar.style.top?.match(/\d+/)?.[0] || 0;
|
||||
const startLeft = this.sidebar.style.left?.match(/\d+/)?.[0] || 0;
|
||||
|
||||
const sidebarBBox = this.sidebar.getBoundingClientRect();
|
||||
const sideBarHeight = sidebarBBox.height;
|
||||
const sideBarWidth = sidebarBBox.width;
|
||||
|
||||
const topMouseOffset = startTop - mouseDownEvent.screenY;
|
||||
const leftMouseOffset = startLeft - mouseDownEvent.screenX;
|
||||
const moveListener = (mouseMoveEvent) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
let top = mouseMoveEvent.screenY + topMouseOffset;
|
||||
let left = mouseMoveEvent.screenX + leftMouseOffset;
|
||||
|
||||
const wrapperBounds = this.sidebarWrapper.getBoundingClientRect();
|
||||
top = Math.max(0, Math.min(top, wrapperBounds.height - sideBarHeight));
|
||||
left = Math.max(0, Math.min(left, wrapperBounds.width - sideBarWidth));
|
||||
|
||||
this.sidebar.style.top = top + 'px';
|
||||
this.sidebar.style.left = left + 'px';
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', moveListener);
|
||||
document.addEventListener(
|
||||
'mouseup',
|
||||
() => {
|
||||
document.removeEventListener('mousemove', moveListener);
|
||||
this._isDragging = false;
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
onWindowResize() {
|
||||
if (!this.isFloating) return;
|
||||
const top = parseInt(this.sidebar.style.top?.match(/\d+/)?.[0] || 0);
|
||||
const left = parseInt(this.sidebar.style.left?.match(/\d+/)?.[0] || 0);
|
||||
const wrapperRect = this.sidebarWrapper.getBoundingClientRect();
|
||||
const sidebarRect = this.sidebar.getBoundingClientRect();
|
||||
|
||||
if (sidebarRect.height < wrapperRect.height && top + sidebarRect.height > wrapperRect.height) {
|
||||
this.sidebar.style.top = wrapperRect.height - sidebarRect.height + 'px';
|
||||
}
|
||||
if (sidebarRect.width < wrapperRect.width && left + sidebarRect.width > wrapperRect.width) {
|
||||
this.sidebar.style.left = wrapperRect.width - sidebarRect.width + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
get isFloating() {
|
||||
return document.getElementById('zen-sidebar-web-panel').hasAttribute('pinned');
|
||||
}
|
||||
|
||||
handleEvent() {
|
||||
this._hasChangedConfig = true;
|
||||
this.update();
|
||||
this._hasChangedConfig = false;
|
||||
|
||||
// https://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind
|
||||
var clickOutsideHandler = this._handleClickOutside.bind(this);
|
||||
let isFloating = this.isFloating;
|
||||
if (isFloating && !this._hasRegisteredPinnedClickOutside) {
|
||||
document.addEventListener('mouseup', clickOutsideHandler);
|
||||
this._hasRegisteredPinnedClickOutside = true;
|
||||
} else if (!isFloating && this._hasRegisteredPinnedClickOutside) {
|
||||
document.removeEventListener('mouseup', clickOutsideHandler);
|
||||
this._hasRegisteredPinnedClickOutside = false;
|
||||
}
|
||||
|
||||
const button = document.getElementById('zen-sidepanel-button');
|
||||
if (!button) return;
|
||||
if (Services.prefs.getBoolPref('zen.sidebar.enabled')) {
|
||||
button.removeAttribute('hidden');
|
||||
} else {
|
||||
button.setAttribute('hidden', 'true');
|
||||
this._closeSidebarPanel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_handleClickOutside(event) {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
if (!sidebar.hasAttribute('pinned') || this._isDragging || !this.shouldCloseOnBlur) {
|
||||
return;
|
||||
}
|
||||
let target = event.target;
|
||||
const closestSelector = [
|
||||
'#zen-sidebar-web-panel',
|
||||
'#zen-sidebar-panels-wrapper',
|
||||
'#zenWebPanelContextMenu',
|
||||
'#zen-sidebar-web-panel-splitter',
|
||||
'#contentAreaContextMenu',
|
||||
].join(', ');
|
||||
if (target.closest(closestSelector)) {
|
||||
return;
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (!this._currentPanel) {
|
||||
this._currentPanel = this._lastOpenedPanel;
|
||||
}
|
||||
if (document.getElementById('zen-sidebar-web-panel').hasAttribute('hidden')) {
|
||||
this.open();
|
||||
return;
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
|
||||
open() {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
if (!this.sidebar.hasAttribute('pinned')) {
|
||||
this.moveToTabBoxWrapper();
|
||||
}
|
||||
sidebar.removeAttribute('hidden');
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
this._updateWebPanels();
|
||||
this._updateSidebarButton();
|
||||
this._updateWebPanel();
|
||||
this._updateButtons();
|
||||
}
|
||||
|
||||
_updateSidebarButton() {
|
||||
let button = document.getElementById('zen-sidepanel-button');
|
||||
if (!button) return;
|
||||
if (!document.getElementById('zen-sidebar-web-panel').hasAttribute('hidden')) {
|
||||
button.setAttribute('open', 'true');
|
||||
} else {
|
||||
button.removeAttribute('open');
|
||||
}
|
||||
}
|
||||
|
||||
_updateWebPanels() {
|
||||
if (Services.prefs.getBoolPref('zen.sidebar.enabled')) {
|
||||
this.sidebarElement.removeAttribute('hidden');
|
||||
} else {
|
||||
this.sidebarElement.setAttribute('hidden', 'true');
|
||||
this._closeSidebarPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
let data = this.sidebarData;
|
||||
if (!data.data || !data.index) {
|
||||
return;
|
||||
}
|
||||
this.sidebarElement.innerHTML = '';
|
||||
for (let site of data.index) {
|
||||
let panel = data.data[site];
|
||||
if (!panel || !panel.url) {
|
||||
continue;
|
||||
}
|
||||
let button = document.createXULElement('toolbarbutton');
|
||||
button.classList.add('zen-sidebar-panel-button', 'toolbarbutton-1', 'chromeclass-toolbar-additional');
|
||||
button.setAttribute('flex', '1');
|
||||
button.setAttribute('zen-sidebar-id', site);
|
||||
button.setAttribute('context', 'zenWebPanelContextMenu');
|
||||
this._getWebPanelIcon(panel.url, button);
|
||||
button.addEventListener('click', this._handleClick.bind(this));
|
||||
button.addEventListener('dragstart', this._handleDragStart.bind(this));
|
||||
button.addEventListener('dragover', this._handleDragOver.bind(this));
|
||||
button.addEventListener('dragenter', this._handleDragEnter.bind(this));
|
||||
button.addEventListener('dragend', this._handleDragEnd.bind(this));
|
||||
this.sidebarElement.appendChild(button);
|
||||
}
|
||||
const addButton = document.getElementById('zen-sidebar-add-panel-button');
|
||||
if (data.index.length < this.MAX_SIDEBAR_PANELS) {
|
||||
addButton.removeAttribute('hidden');
|
||||
} else {
|
||||
addButton.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
async _openAddPanelDialog() {
|
||||
let dialogURL = 'chrome://browser/content/places/zenNewWebPanel.xhtml';
|
||||
let features = 'centerscreen,chrome,modal,resizable=no';
|
||||
let aParentWindow = Services.wm.getMostRecentWindow('navigator:browser');
|
||||
|
||||
if (aParentWindow?.gDialogBox) {
|
||||
await aParentWindow.gDialogBox.open(dialogURL, {});
|
||||
} else {
|
||||
aParentWindow.openDialog(dialogURL, '', features, {});
|
||||
}
|
||||
}
|
||||
|
||||
_setPinnedToElements() {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
sidebar.setAttribute('pinned', 'true');
|
||||
document.getElementById('zen-sidebar-web-panel-pinned').setAttribute('pinned', 'true');
|
||||
}
|
||||
|
||||
_removePinnedFromElements() {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
sidebar.removeAttribute('pinned');
|
||||
document.getElementById('zen-sidebar-web-panel-pinned').removeAttribute('pinned');
|
||||
}
|
||||
|
||||
_closeSidebarPanel() {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
sidebar.setAttribute('hidden', 'true');
|
||||
this._lastOpenedPanel = this._currentPanel;
|
||||
this._currentPanel = null;
|
||||
}
|
||||
|
||||
_handleClick(event) {
|
||||
let target = event.target;
|
||||
let panelId = target.getAttribute('zen-sidebar-id');
|
||||
if (this._currentPanel === panelId) {
|
||||
return;
|
||||
}
|
||||
this._currentPanel = panelId;
|
||||
this._updateWebPanel();
|
||||
}
|
||||
|
||||
_handleDragStart(event) {
|
||||
this.__dragingElement = event.target;
|
||||
this.__dragingIndex = Array.prototype.indexOf.call(event.target.parentNode.children, event.target);
|
||||
event.target.style.opacity = '0.7';
|
||||
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('text/html', event.target.innerHTML);
|
||||
event.dataTransfer.setData('text/plain', event.target.id);
|
||||
}
|
||||
|
||||
_handleDragOver(event) {}
|
||||
|
||||
_handleDragEnter(event) {
|
||||
if (typeof this.__dragingElement === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const target = event.target;
|
||||
const elIndex = Array.prototype.indexOf.call(target.parentNode.children, target);
|
||||
if (elIndex < this.__dragingIndex) {
|
||||
target.before(this.__dragingElement);
|
||||
this.__dragingIndex = elIndex - 1;
|
||||
}
|
||||
target.after(this.__dragingElement);
|
||||
this.__dragingIndex = elIndex + 1;
|
||||
}
|
||||
|
||||
_handleDragEnd(event) {
|
||||
event.target.style.opacity = '1';
|
||||
|
||||
let data = this.sidebarData;
|
||||
let newPos = [];
|
||||
for (let element of this.__dragingElement.parentNode.children) {
|
||||
let panelId = element.getAttribute('zen-sidebar-id');
|
||||
newPos.push(panelId);
|
||||
}
|
||||
data.index = newPos;
|
||||
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
|
||||
this._currentPanel = this.__dragingElement.getAttribute('zen-sidebar-id');
|
||||
this.open();
|
||||
this.__dragingElement = undefined;
|
||||
}
|
||||
|
||||
_createNewPanel(url) {
|
||||
let data = this.sidebarData;
|
||||
let newName = 'p' + new Date().getTime();
|
||||
data.index.push(newName);
|
||||
data.data[newName] = {
|
||||
url: url,
|
||||
ua: false,
|
||||
};
|
||||
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
|
||||
this._currentPanel = newName;
|
||||
this.open();
|
||||
}
|
||||
|
||||
_updateButtons() {
|
||||
for (let button of this.sidebarElement.querySelectorAll('.zen-sidebar-panel-button')) {
|
||||
if (button.getAttribute('zen-sidebar-id') === this._currentPanel) {
|
||||
button.setAttribute('selected', 'true');
|
||||
} else {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hideAllWebPanels() {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
for (let browser of sidebar.querySelectorAll('browser[zen-sidebar-id]')) {
|
||||
browser.setAttribute('hidden', 'true');
|
||||
browser.docShellIsActive = false;
|
||||
browser.zenModeActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
get introductionPanel() {
|
||||
return document.getElementById('zen-sidebar-introduction-panel');
|
||||
}
|
||||
|
||||
_updateWebPanel() {
|
||||
this._updateButtons();
|
||||
// let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
this._hideAllWebPanels();
|
||||
if (!this._currentPanel) {
|
||||
this.introductionPanel.removeAttribute('hidden');
|
||||
this.forwardButton.setAttribute('disabled', true);
|
||||
this.backButton.setAttribute('disabled', true);
|
||||
return;
|
||||
}
|
||||
this.introductionPanel.setAttribute('hidden', 'true');
|
||||
let existantWebview = this._getCurrentBrowser();
|
||||
if (existantWebview) {
|
||||
existantWebview.docShellIsActive = true;
|
||||
existantWebview.zenModeActive = true;
|
||||
existantWebview.removeAttribute('hidden');
|
||||
document.getElementById('zen-sidebar-web-panel-title').textContent = existantWebview.contentTitle;
|
||||
return;
|
||||
}
|
||||
let data = this._getWebPanelData(this._currentPanel);
|
||||
let browser = this._createWebPanelBrowser(data);
|
||||
let browserContainers = document.getElementById('zen-sidebar-web-panel-browser-containers');
|
||||
browserContainers.appendChild(browser);
|
||||
browser.addProgressListener(this.progressListener, Ci.nsIWebProgress.NOTIFY_LOCATION);
|
||||
if (data.ua) {
|
||||
browser.browsingContext.customUserAgent = this.DEFAULT_MOBILE_USER_AGENT;
|
||||
browser.reload();
|
||||
}
|
||||
browser.docShellIsActive = true;
|
||||
browser.zenModeActive = true;
|
||||
}
|
||||
|
||||
_getWebPanelData(id) {
|
||||
let data = this.sidebarData;
|
||||
let panel = data.data[id];
|
||||
if (!panel || !panel.url) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
id: id,
|
||||
...panel,
|
||||
};
|
||||
}
|
||||
|
||||
getTabForBrowser(browser) {
|
||||
return this._tabBrowserSet.get(browser);
|
||||
}
|
||||
|
||||
setTabForBrowser(browser, tab) {
|
||||
this._tabBrowserSet.set(browser, tab);
|
||||
}
|
||||
|
||||
removeTabForBrowser(browser) {
|
||||
this._tabBrowserSet.delete(browser);
|
||||
}
|
||||
|
||||
_createWebPanelBrowser(data) {
|
||||
const titleContainer = document.getElementById('zen-sidebar-web-panel-title');
|
||||
titleContainer.textContent = 'Loading...';
|
||||
let browser = gBrowser.createBrowser({
|
||||
userContextId: data.userContextId,
|
||||
});
|
||||
const tab = this.sidebar.querySelector(`[zen-sidebar-id='${data.id}']`);
|
||||
this.setTabForBrowser(browser, tab);
|
||||
tab.linkedBrowser = browser;
|
||||
tab.permanentKey = browser.permanentKey;
|
||||
browser.setAttribute('disablefullscreen', 'true');
|
||||
browser.setAttribute('src', data.url);
|
||||
browser.setAttribute('zen-sidebar-id', data.id);
|
||||
browser.addEventListener(
|
||||
'pagetitlechanged',
|
||||
function (event) {
|
||||
let browser = event.target;
|
||||
let title = browser.contentTitle;
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
let id = browser.getAttribute('zen-sidebar-id');
|
||||
if (id === this._currentPanel) {
|
||||
titleContainer.textContent = title;
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
return browser;
|
||||
}
|
||||
|
||||
_getWebPanelIcon(url, element) {
|
||||
let { preferredURI } = Services.uriFixup.getFixupURIInfo(url);
|
||||
element.setAttribute('image', `page-icon:${preferredURI.spec}`);
|
||||
fetch(`https://s2.googleusercontent.com/s2/favicons?domain_url=${preferredURI.spec}`).then(async (response) => {
|
||||
if (response.ok) {
|
||||
let blob = await response.blob();
|
||||
let reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
element.setAttribute('image', reader.result);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_getBrowserById(id) {
|
||||
let sidebar = document.getElementById('zen-sidebar-web-panel');
|
||||
return sidebar.querySelector(`browser[zen-sidebar-id="${id}"]`);
|
||||
}
|
||||
|
||||
_getCurrentBrowser() {
|
||||
return this._getBrowserById(this._currentPanel);
|
||||
}
|
||||
|
||||
reload() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.reload();
|
||||
}
|
||||
}
|
||||
|
||||
forward() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.goForward();
|
||||
}
|
||||
}
|
||||
|
||||
back() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
home() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.gotoIndex();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this._hideAllWebPanels();
|
||||
this._closeSidebarPanel();
|
||||
this._updateSidebarButton();
|
||||
}
|
||||
|
||||
moveToTabBoxWrapper() {
|
||||
this.tabBox.before(this.sidebarWrapper);
|
||||
this.sidebarWrapper.style.order = '';
|
||||
}
|
||||
|
||||
moveToTabBox() {
|
||||
this.tabBox.prepend(this.sidebarWrapper);
|
||||
}
|
||||
|
||||
togglePinned(elem) {
|
||||
if (this.sidebar.hasAttribute('pinned')) {
|
||||
this._removePinnedFromElements();
|
||||
//this.moveToTabBoxWrapper();
|
||||
} else {
|
||||
this._setPinnedToElements();
|
||||
//this.moveToTabBox();
|
||||
}
|
||||
this.update();
|
||||
}
|
||||
|
||||
get sidebarElement() {
|
||||
if (!this._sidebarElement) {
|
||||
this._sidebarElement = document.getElementById('zen-sidebar-panels-sites');
|
||||
}
|
||||
return this._sidebarElement;
|
||||
}
|
||||
|
||||
get splitterElement() {
|
||||
if (!this._splitterElement) {
|
||||
this._splitterElement = document.getElementById('zen-sidebar-web-panel-splitter');
|
||||
}
|
||||
return this._splitterElement;
|
||||
}
|
||||
|
||||
get hSplitterElement() {
|
||||
if (!this._hSplitterElement) {
|
||||
this._hSplitterElement = document.getElementById('zen-sidebar-web-panel-hsplitter');
|
||||
}
|
||||
return this._hSplitterElement;
|
||||
}
|
||||
|
||||
get sidebarHeader() {
|
||||
if (!this._sidebarHeader) {
|
||||
this._sidebarHeader = document.getElementById('zen-sidebar-web-header');
|
||||
}
|
||||
return this._sidebarHeader;
|
||||
}
|
||||
|
||||
get sidebarWrapper() {
|
||||
if (!this._sideBarWrapper) {
|
||||
this._sideBarWrapper = document.getElementById('zen-sidebar-web-panel-wrapper');
|
||||
}
|
||||
return this._sideBarWrapper;
|
||||
}
|
||||
|
||||
get sidebarHeaderButtons() {
|
||||
if (!this._sidebarHeaderButtons) {
|
||||
this._sidebarHeaderButtons = [...this.sidebarHeader.querySelectorAll('.toolbarbutton-1')];
|
||||
}
|
||||
return this._sidebarHeaderButtons;
|
||||
}
|
||||
|
||||
// Context menu
|
||||
|
||||
updateContextMenu(aPopupMenu) {
|
||||
let panel =
|
||||
aPopupMenu.triggerNode && (aPopupMenu.triggerNode || aPopupMenu.triggerNode.closest('toolbarbutton[zen-sidebar-id]'));
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
let id = panel.getAttribute('zen-sidebar-id');
|
||||
this.contextTab = id;
|
||||
let data = this._getWebPanelData(id);
|
||||
let browser = this._getBrowserById(id);
|
||||
let isMuted = browser && browser.audioMuted;
|
||||
let mutedContextItem = document.getElementById('context_zenToggleMuteWebPanel');
|
||||
document.l10n.setAttributes(
|
||||
mutedContextItem,
|
||||
!isMuted ? 'zen-web-side-panel-context-mute-panel' : 'zen-web-side-panel-context-unmute-panel'
|
||||
);
|
||||
if (!isMuted) {
|
||||
mutedContextItem.setAttribute('muted', 'true');
|
||||
} else {
|
||||
mutedContextItem.removeAttribute('muted');
|
||||
}
|
||||
document.l10n.setAttributes(
|
||||
document.getElementById('context_zenToogleUAWebPanel'),
|
||||
data.ua ? 'zen-web-side-panel-context-disable-ua' : 'zen-web-side-panel-context-enable-ua'
|
||||
);
|
||||
if (!browser) {
|
||||
document.getElementById('context_zenUnloadWebPanel').setAttribute('disabled', 'true');
|
||||
} else {
|
||||
document.getElementById('context_zenUnloadWebPanel').removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
createContainerTabMenu(event) {
|
||||
let window = event.target.ownerGlobal;
|
||||
let data = this.sidebarData;
|
||||
let panelData = data.data[this.contextTab];
|
||||
return window.createUserContextMenu(event, {
|
||||
isContextMenu: true,
|
||||
excludeUserContextId: panelData.userContextId,
|
||||
showDefaultTab: true,
|
||||
});
|
||||
}
|
||||
|
||||
contextChangeContainerTab(event) {
|
||||
let data = this.sidebarData;
|
||||
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
|
||||
data.data[this.contextTab].userContextId = userContextId;
|
||||
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
if (browser) {
|
||||
browser.remove();
|
||||
// We need to re-apply a new browser so it takes the new userContextId
|
||||
this._updateWebPanel();
|
||||
}
|
||||
}
|
||||
|
||||
contextOpenNewTab() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
let data = this.sidebarData;
|
||||
let panel = data.data[this.contextTab];
|
||||
let url = browser == null ? panel.url : browser.currentURI.spec;
|
||||
gZenUIManager.openAndChangeToTab(url);
|
||||
this.close();
|
||||
}
|
||||
|
||||
contextToggleMuteAudio() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
if (browser.audioMuted) {
|
||||
browser.unmute();
|
||||
} else {
|
||||
browser.mute();
|
||||
}
|
||||
}
|
||||
|
||||
contextToggleUserAgent() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
browser.browsingContext.customUserAgent = browser.browsingContext.customUserAgent ? null : this.DEFAULT_MOBILE_USER_AGENT;
|
||||
let data = this.sidebarData;
|
||||
data.data[this.contextTab].ua = !data.data[this.contextTab].ua;
|
||||
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
|
||||
browser.reload();
|
||||
}
|
||||
|
||||
contextDelete() {
|
||||
let data = this.sidebarData;
|
||||
delete data.data[this.contextTab];
|
||||
data.index = data.index.filter((id) => id !== this.contextTab);
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
if (browser) {
|
||||
browser.remove();
|
||||
document.getElementById('zen-sidebar-web-panel-title').textContent = '';
|
||||
}
|
||||
this._currentPanel = null;
|
||||
this._lastOpenedPanel = null;
|
||||
this.update();
|
||||
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
|
||||
}
|
||||
|
||||
contextUnload() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
this.removeTabForBrowser(browser);
|
||||
browser.remove();
|
||||
document.getElementById('zen-sidebar-web-panel-title').textContent = '';
|
||||
this._closeSidebarPanel();
|
||||
this.close();
|
||||
this._lastOpenedPanel = null;
|
||||
}
|
||||
|
||||
insertIntoContextMenu() {
|
||||
const sibling = document.getElementById('context-stripOnShareLink');
|
||||
const menuitem = document.createXULElement('menuitem');
|
||||
menuitem.setAttribute('id', 'context-zenAddToWebPanel');
|
||||
menuitem.setAttribute('hidden', 'true');
|
||||
menuitem.setAttribute('oncommand', 'gZenBrowserManagerSidebar.addPanelFromContextMenu();');
|
||||
menuitem.setAttribute('data-l10n-id', 'zen-web-side-panel-context-add-to-panel');
|
||||
sibling.insertAdjacentElement('afterend', menuitem);
|
||||
}
|
||||
|
||||
addPanelFromContextMenu() {
|
||||
const url = gContextMenu.linkURL || gContextMenu.target.ownerDocument.location.href;
|
||||
this._createNewPanel(url);
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenBrowserManagerSidebar = new ZenBrowserManagerSidebar();
|
283
src/browser/base/zen-components/ZenTabUnloader.mjs
Normal file
283
src/browser/base/zen-components/ZenTabUnloader.mjs
Normal file
@@ -0,0 +1,283 @@
|
||||
{
|
||||
const lazy = {};
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenTabUnloaderEnabled', 'zen.tab-unloader.enabled', false);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenTabUnloaderTimeout', 'zen.tab-unloader.timeout-minutes', 20);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenTabUnloaderExcludedUrls', 'zen.tab-unloader.excluded-urls', '');
|
||||
|
||||
const ZEN_TAB_UNLOADER_DEFAULT_EXCLUDED_URLS = [
|
||||
'^about:',
|
||||
'^chrome:',
|
||||
'^devtools:',
|
||||
'^file:',
|
||||
'^resource:',
|
||||
'^view-source:',
|
||||
'^view-image:',
|
||||
];
|
||||
|
||||
class ZenTabsObserver {
|
||||
static ALL_EVENTS = [
|
||||
'TabAttrModified',
|
||||
'TabPinned',
|
||||
'TabUnpinned',
|
||||
'TabBrowserInserted',
|
||||
'TabBrowserDiscarded',
|
||||
'TabShow',
|
||||
'TabHide',
|
||||
'TabOpen',
|
||||
'TabClose',
|
||||
'TabSelect',
|
||||
'TabMultiSelect',
|
||||
];
|
||||
|
||||
#listeners = [];
|
||||
|
||||
constructor() {
|
||||
this.#listenAllEvents();
|
||||
}
|
||||
|
||||
#listenAllEvents() {
|
||||
const eventListener = this.#eventListener.bind(this);
|
||||
for (const event of ZenTabsObserver.ALL_EVENTS) {
|
||||
window.addEventListener(event, eventListener);
|
||||
}
|
||||
window.addEventListener('unload', () => {
|
||||
for (const event of ZenTabsObserver.ALL_EVENTS) {
|
||||
window.removeEventListener(event, eventListener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#eventListener(event) {
|
||||
for (const listener of this.#listeners) {
|
||||
listener(event.type, event);
|
||||
}
|
||||
}
|
||||
|
||||
addTabsListener(listener) {
|
||||
this.#listeners.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
class ZenTabsIntervalUnloader {
|
||||
static INTERVAL = 1000 * 60; // 1 minute
|
||||
|
||||
interval = null;
|
||||
unloader = null;
|
||||
|
||||
#excludedUrls = [];
|
||||
#compiledExcludedUrls = [];
|
||||
|
||||
constructor(unloader) {
|
||||
this.unloader = unloader;
|
||||
this.interval = setInterval(this.intervalListener.bind(this), ZenTabsIntervalUnloader.INTERVAL);
|
||||
this.#excludedUrls = this.lazyExcludeUrls;
|
||||
}
|
||||
|
||||
get lazyExcludeUrls() {
|
||||
return [
|
||||
...ZEN_TAB_UNLOADER_DEFAULT_EXCLUDED_URLS,
|
||||
...lazy.zenTabUnloaderExcludedUrls.split(',').map((url) => url.trim()),
|
||||
];
|
||||
}
|
||||
|
||||
arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
// If you don't care about the order of the elements inside
|
||||
// the array, you should sort both arrays here.
|
||||
// Please note that calling sort on an array will modify that array.
|
||||
// you might want to clone your array first.
|
||||
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
get excludedUrls() {
|
||||
// Check if excludedrls is the same as the pref value
|
||||
const excludedUrls = this.lazyExcludeUrls;
|
||||
if (!this.arraysEqual(this.#excludedUrls, excludedUrls) || !this.#compiledExcludedUrls.length) {
|
||||
this.#excludedUrls = excludedUrls;
|
||||
this.#compiledExcludedUrls = excludedUrls.map((url) => new RegExp(url));
|
||||
}
|
||||
return this.#compiledExcludedUrls;
|
||||
}
|
||||
|
||||
intervalListener() {
|
||||
if (!lazy.zenTabUnloaderEnabled) {
|
||||
return;
|
||||
}
|
||||
const currentTimestamp = Date.now();
|
||||
const excludedUrls = this.excludedUrls;
|
||||
const tabs = gBrowser.tabs;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
if (this.unloader.canUnloadTab(tab, currentTimestamp, excludedUrls)) {
|
||||
this.unloader.unload(tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ZenTabUnloader extends ZenDOMOperatedFeature {
|
||||
static ACTIVITY_MODIFIERS = ['muted', 'soundplaying', 'label', 'attention'];
|
||||
|
||||
init() {
|
||||
if (!lazy.zenTabUnloaderEnabled) {
|
||||
return;
|
||||
}
|
||||
this.insertIntoContextMenu();
|
||||
this.observer = new ZenTabsObserver();
|
||||
this.intervalUnloader = new ZenTabsIntervalUnloader(this);
|
||||
this.observer.addTabsListener(this.onTabEvent.bind(this));
|
||||
}
|
||||
|
||||
onTabEvent(action, event) {
|
||||
const tab = event.target;
|
||||
switch (action) {
|
||||
case 'TabPinned':
|
||||
case 'TabUnpinned':
|
||||
case 'TabBrowserInserted':
|
||||
case 'TabBrowserDiscarded':
|
||||
case 'TabShow':
|
||||
case 'TabHide':
|
||||
break;
|
||||
case 'TabAttrModified':
|
||||
this.handleTabAttrModified(tab, event);
|
||||
break;
|
||||
case 'TabOpen':
|
||||
this.handleTabOpen(tab);
|
||||
break;
|
||||
case 'TabClose':
|
||||
this.handleTabClose(tab);
|
||||
break;
|
||||
case 'TabSelect':
|
||||
case 'TabMultiSelect':
|
||||
this.updateTabActivity(tab);
|
||||
break;
|
||||
default:
|
||||
console.warn('ZenTabUnloader: Unhandled tab event', action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onLocationChange(browser) {
|
||||
const tab = browser.ownerGlobal.gBrowser.getTabForBrowser(browser);
|
||||
this.updateTabActivity(tab);
|
||||
}
|
||||
|
||||
handleTabClose(tab) {
|
||||
// Nothing yet
|
||||
}
|
||||
|
||||
handleTabOpen(tab) {
|
||||
this.updateTabActivity(tab);
|
||||
}
|
||||
|
||||
handleTabAttrModified(tab, event) {
|
||||
for (const modifier of ZenTabUnloader.ACTIVITY_MODIFIERS) {
|
||||
if (event.detail.changed.includes(modifier)) {
|
||||
this.updateTabActivity(tab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateTabActivity(tab) {
|
||||
const currentTimestamp = Date.now();
|
||||
tab.lastActivity = currentTimestamp;
|
||||
}
|
||||
|
||||
get tabs() {
|
||||
return gBrowser.tabs;
|
||||
}
|
||||
|
||||
insertIntoContextMenu() {
|
||||
const element = window.MozXULElement.parseXULToFragment(`
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenUnloadTab"
|
||||
data-lazy-l10n-id="tab-zen-unload"
|
||||
oncommand="gZenTabUnloader.unloadTab();"/>
|
||||
<menu data-lazy-l10n-id="zen-tabs-unloader-tab-actions" id="context_zenTabActions">
|
||||
<menupopup>
|
||||
<menuitem id="context_zenPreventUnloadTab"
|
||||
data-lazy-l10n-id="tab-zen-prevent-unload"
|
||||
oncommand="gZenTabUnloader.preventUnloadTab();"/>
|
||||
<menuitem id="context_zenIgnoreUnloadTab"
|
||||
data-lazy-l10n-id="tab-zen-ignore-unload"
|
||||
oncommand="gZenTabUnloader.ignoreUnloadTab();"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
`);
|
||||
document.getElementById('context_closeDuplicateTabs').parentNode.appendChild(element);
|
||||
}
|
||||
|
||||
unload(tab) {
|
||||
gBrowser.discardBrowser(tab);
|
||||
tab.removeAttribute('linkedpanel');
|
||||
}
|
||||
|
||||
unloadTab() {
|
||||
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
if (this.canUnloadTab(tabs[i], Date.now(), [], true)) {
|
||||
this.unload(tabs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preventUnloadTab() {
|
||||
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
tab.zenIgnoreUnload = true;
|
||||
}
|
||||
}
|
||||
|
||||
ignoreUnloadTab() {
|
||||
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
tab.zenIgnoreUnload = false;
|
||||
}
|
||||
}
|
||||
|
||||
canUnloadTab(tab, currentTimestamp, excludedUrls, ignoreTimestamp = false) {
|
||||
if (
|
||||
tab.pinned ||
|
||||
tab.selected ||
|
||||
tab.multiselected ||
|
||||
tab.hasAttribute('busy') ||
|
||||
tab.hasAttribute('pending') ||
|
||||
!tab.linkedPanel ||
|
||||
tab.splitView ||
|
||||
tab.attention ||
|
||||
tab.linkedBrowser?.zenModeActive ||
|
||||
tab.pictureinpicture ||
|
||||
tab.soundPlaying ||
|
||||
tab.zenIgnoreUnload ||
|
||||
excludedUrls.some((url) => url.test(tab.linkedBrowser.currentURI.spec))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (ignoreTimestamp) {
|
||||
return true;
|
||||
}
|
||||
const lastActivity = tab.lastActivity;
|
||||
if (!lastActivity) {
|
||||
return false;
|
||||
}
|
||||
const diff = currentTimestamp - lastActivity;
|
||||
// Check if the tab has been inactive for more than the timeout
|
||||
return diff > lazy.zenTabUnloaderTimeout * 60 * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenTabUnloader = new ZenTabUnloader();
|
||||
}
|
176
src/browser/base/zen-components/ZenThemeBuilder.mjs
Normal file
176
src/browser/base/zen-components/ZenThemeBuilder.mjs
Normal file
@@ -0,0 +1,176 @@
|
||||
const kZenAccentColorConfigKey = 'zen.theme.accent-color';
|
||||
|
||||
var gZenThemeBuilder = {
|
||||
init() {
|
||||
return; // TODO:
|
||||
this._mouseMoveListener = this._handleThumbMouseMove.bind(this);
|
||||
setTimeout(() => {
|
||||
this._initBuilderUI();
|
||||
}, 500);
|
||||
},
|
||||
|
||||
get _builderWrapper() {
|
||||
if (this.__builderWrapper) {
|
||||
return this.__builderWrapper;
|
||||
}
|
||||
this.__builderWrapper = document.getElementById('zen-theme-builder-wrapper');
|
||||
return this.__builderWrapper;
|
||||
},
|
||||
|
||||
_initBuilderUI() {
|
||||
let wrapper = this._builderWrapper;
|
||||
if (!wrapper) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.info('gZenThemeBuilder: init builder UI');
|
||||
|
||||
const kTemplate = `
|
||||
<html:div id="zen-theme-builder">
|
||||
<html:div id="zen-theme-builder-color-picker">
|
||||
<html:canvas id="zen-theme-builder-color-picker-canvas"></html:canvas>
|
||||
<html:div id="zen-theme-builder-color-picker-deck">
|
||||
<html:div id="zen-theme-builder-color-picker-thumb"></html:div>
|
||||
</html:div>
|
||||
</html:div>
|
||||
</html:div>
|
||||
`;
|
||||
wrapper.innerHTML = kTemplate;
|
||||
this._initColorPicker();
|
||||
},
|
||||
|
||||
_getPositionFromColor(ctx, color) {
|
||||
var w = ctx.canvas.width,
|
||||
h = ctx.canvas.height,
|
||||
data = ctx.getImageData(0, 0, w, h), /// get image data
|
||||
buffer = data.data, /// and its pixel buffer
|
||||
len = buffer.length, /// cache length
|
||||
x,
|
||||
y = 0,
|
||||
p,
|
||||
px; /// for iterating
|
||||
/// iterating x/y instead of forward to get position the easy way
|
||||
for (; y < h; y++) {
|
||||
/// common value for all x
|
||||
p = y * 4 * w;
|
||||
for (x = 0; x < w; x++) {
|
||||
/// next pixel (skipping 4 bytes as each pixel is RGBA bytes)
|
||||
px = p + x * 4;
|
||||
/// if red component match check the others
|
||||
if (buffer[px] === color[0]) {
|
||||
if (buffer[px + 1] === color[1] && buffer[px + 2] === color[2]) {
|
||||
return [x, y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_hexToRgb(hex) {
|
||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
||||
},
|
||||
|
||||
_componentToHex(c) {
|
||||
var hex = c.toString(16);
|
||||
return hex.length == 1 ? '0' + hex : hex;
|
||||
},
|
||||
|
||||
_rgbToHex(r, g, b) {
|
||||
return '#' + this._componentToHex(r) + this._componentToHex(g) + this._componentToHex(b);
|
||||
},
|
||||
|
||||
_initColorPicker() {
|
||||
const canvas = document.getElementById('zen-theme-builder-color-picker-canvas');
|
||||
const thumb = document.getElementById('zen-theme-builder-color-picker-thumb');
|
||||
|
||||
// A all the main colors are all blended together towards the center.
|
||||
// But we also add some random gradients to make it look more interesting.
|
||||
// Instead of using a simple gradient, we use a radial gradient.
|
||||
const ctx = canvas.getContext('2d');
|
||||
const size = 180;
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const center = size / 2;
|
||||
const radius = size / 2;
|
||||
const gradient = ctx.createConicGradient(0, center, center);
|
||||
gradient.addColorStop(0, '#fff490');
|
||||
gradient.addColorStop(1 / 12, '#f9e380');
|
||||
gradient.addColorStop(2 / 12, '#fecc87');
|
||||
gradient.addColorStop(3 / 12, '#ffa894');
|
||||
gradient.addColorStop(4 / 12, '#f98089');
|
||||
gradient.addColorStop(5 / 12, '#f9b7c5');
|
||||
gradient.addColorStop(6 / 12, '#c193b8');
|
||||
gradient.addColorStop(7 / 12, '#a8b7e0');
|
||||
gradient.addColorStop(8 / 12, '#88d2f9');
|
||||
gradient.addColorStop(9 / 12, '#81e8e5');
|
||||
gradient.addColorStop(10 / 12, '#b7e5a5');
|
||||
gradient.addColorStop(11 / 12, '#eaefac');
|
||||
gradient.addColorStop(1, '#fff490');
|
||||
|
||||
const radialGradient = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
|
||||
radialGradient.addColorStop(0, 'rgba(255,255,255,1)');
|
||||
radialGradient.addColorStop(1, 'rgba(255,255,255,0)');
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
//ctx.fillStyle = radialGradient;
|
||||
//ctx.fillRect(0, 0, size, size);
|
||||
|
||||
// Add the thumb.
|
||||
const accentColor = Services.prefs.getStringPref(kZenAccentColorConfigKey, '#aac7ff');
|
||||
const pos = this._getPositionFromColor(ctx, this._hexToRgb(accentColor));
|
||||
|
||||
let x = pos ? pos[0] : center;
|
||||
let y = pos ? pos[1] : center;
|
||||
|
||||
thumb.style.left = `${x}px`;
|
||||
thumb.style.top = `${y}px`;
|
||||
|
||||
thumb.addEventListener('mousedown', this._handleThumbMouseDown.bind(this));
|
||||
document.addEventListener('mouseup', this._handleThumbMouseUp.bind(this));
|
||||
},
|
||||
|
||||
_handleThumbMouseDown(e) {
|
||||
document.addEventListener('mousemove', this._mouseMoveListener);
|
||||
},
|
||||
|
||||
_handleThumbMouseUp(e) {
|
||||
document.removeEventListener('mousemove', this._mouseMoveListener);
|
||||
},
|
||||
|
||||
_handleThumbMouseMove(e) {
|
||||
const kThumbOffset = 15;
|
||||
const deck = document.getElementById('zen-theme-builder-color-picker-deck');
|
||||
|
||||
const thumb = document.getElementById('zen-theme-builder-color-picker-thumb');
|
||||
const rect = deck.getBoundingClientRect();
|
||||
let x = e.clientX - rect.left;
|
||||
let y = e.clientY - rect.top;
|
||||
|
||||
if (x > rect.width - kThumbOffset) {
|
||||
x = rect.width - kThumbOffset;
|
||||
}
|
||||
if (y > rect.height - kThumbOffset) {
|
||||
y = rect.height - kThumbOffset;
|
||||
}
|
||||
if (x < kThumbOffset) {
|
||||
x = kThumbOffset;
|
||||
}
|
||||
if (y < kThumbOffset) {
|
||||
y = kThumbOffset;
|
||||
}
|
||||
|
||||
thumb.style.left = `${x}px`;
|
||||
thumb.style.top = `${y}px`;
|
||||
|
||||
const canvas = document.getElementById('zen-theme-builder-color-picker-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imageData = ctx.getImageData(x, y, 1, 1);
|
||||
|
||||
// Update the accent color.
|
||||
Services.prefs.setStringPref(kZenAccentColorConfigKey, this._rgbToHex(...imageData.data));
|
||||
},
|
||||
};
|
120
src/browser/base/zen-components/ZenThemesCommon.mjs
Normal file
120
src/browser/base/zen-components/ZenThemesCommon.mjs
Normal file
@@ -0,0 +1,120 @@
|
||||
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);
|
||||
},
|
||||
|
||||
resetThemesCache() {
|
||||
this.themes = null;
|
||||
},
|
||||
|
||||
async getThemes() {
|
||||
if (!this.themes) {
|
||||
if (!(await IOUtils.exists(this.themesDataFile))) {
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
}
|
||||
|
||||
try {
|
||||
this.themes = await IOUtils.readJSON(this.themesDataFile);
|
||||
} catch (e) {
|
||||
// If we have a corrupted file, reset it
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
this.themes = {};
|
||||
gNotificationBox.appendNotification(
|
||||
"zen-themes-corrupted",
|
||||
{
|
||||
label: { "l10n-id": "zen-themes-corrupted" },
|
||||
image: "chrome://browser/skin/notification-icons/persistent-storage-blocked.svg",
|
||||
priority: gNotificationBox.PRIORITY_INFO_MEDIUM,
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this.themes;
|
||||
},
|
||||
|
||||
async getThemePreferences(theme) {
|
||||
const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json');
|
||||
if (!(await IOUtils.exists(themePath)) || !theme.preferences) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const preferences = await IOUtils.readJSON(themePath);
|
||||
|
||||
// compat mode for old preferences, all of them can only be checkboxes
|
||||
if (typeof preferences === 'object' && !Array.isArray(preferences)) {
|
||||
console.warn(
|
||||
`[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences`
|
||||
);
|
||||
const newThemePreferences = [];
|
||||
|
||||
for (let [entry, label] of Object.entries(preferences)) {
|
||||
const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry);
|
||||
const isNegation = negation === '!';
|
||||
|
||||
if (
|
||||
(isNegation && os === 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)
|
||||
);
|
||||
},
|
||||
|
||||
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);
|
||||
};
|
||||
},
|
||||
};
|
320
src/browser/base/zen-components/ZenThemesImporter.mjs
Normal file
320
src/browser/base/zen-components/ZenThemesImporter.mjs
Normal file
@@ -0,0 +1,320 @@
|
||||
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() {
|
||||
console.info('[ZenThemesImporter]: Initializing Zen Themes Importer');
|
||||
|
||||
try {
|
||||
window.SessionStore.promiseInitialized.then(async () => {
|
||||
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
||||
console.log('[ZenThemesImporter]: Disabling all themes.');
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });
|
||||
|
||||
if (!this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET) && !(await IOUtils.exists(this.styleSheetPath))) {
|
||||
console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
|
||||
}
|
||||
}
|
||||
|
||||
async rebuildThemeStylesheet() {
|
||||
if (Services.focus.activeWindow !== window) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZenThemesCommon.themes = null;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'checkbox': {
|
||||
const value = Services.prefs.getBoolPref(property, false);
|
||||
if (typeof defaultValue !== 'boolean') {
|
||||
console.log(`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
Services.prefs.setBoolPref(property, defaultValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
||||
|
||||
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
||||
console.log(`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value === 'zen-property-no-saved') {
|
||||
Services.prefs.setStringPref(property, defaultValue.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeToDom(themesWithPreferences) {
|
||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||
for (const { enabled, preferences, name } of themesWithPreferences) {
|
||||
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-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 = [];
|
||||
ZenThemesCommon.themes = null;
|
||||
|
||||
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: ["https://*.zen-browser.app/*", "about:preferences"],
|
||||
allFrames: true,
|
||||
});
|
1146
src/browser/base/zen-components/ZenViewSplitter.mjs
Normal file
1146
src/browser/base/zen-components/ZenViewSplitter.mjs
Normal file
File diff suppressed because it is too large
Load Diff
1393
src/browser/base/zen-components/ZenWorkspaces.mjs
Normal file
1393
src/browser/base/zen-components/ZenWorkspaces.mjs
Normal file
File diff suppressed because it is too large
Load Diff
407
src/browser/base/zen-components/ZenWorkspacesStorage.mjs
Normal file
407
src/browser/base/zen-components/ZenWorkspacesStorage.mjs
Normal file
@@ -0,0 +1,407 @@
|
||||
var ZenWorkspacesStorage = {
|
||||
async init() {
|
||||
console.log('ZenWorkspacesStorage: Initializing...');
|
||||
await this._ensureTable();
|
||||
},
|
||||
|
||||
async _ensureTable() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage._ensureTable', async (db) => {
|
||||
// Create the main workspaces table if it doesn't exist
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS zen_workspaces (
|
||||
id INTEGER PRIMARY KEY,
|
||||
uuid TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
icon TEXT,
|
||||
is_default INTEGER NOT NULL DEFAULT 0,
|
||||
container_id INTEGER,
|
||||
position INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Add new columns if they don't exist
|
||||
// SQLite doesn't have a direct "ADD COLUMN IF NOT EXISTS" syntax,
|
||||
// so we need to check if the columns exist first
|
||||
const columns = await db.execute(`PRAGMA table_info(zen_workspaces)`);
|
||||
const columnNames = columns.map(row => row.getResultByName('name'));
|
||||
|
||||
// Helper function to add column if it doesn't exist
|
||||
const addColumnIfNotExists = async (columnName, definition) => {
|
||||
if (!columnNames.includes(columnName)) {
|
||||
await db.execute(`ALTER TABLE zen_workspaces ADD COLUMN ${columnName} ${definition}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Add each new column if it doesn't exist
|
||||
await addColumnIfNotExists('theme_type', 'TEXT');
|
||||
await addColumnIfNotExists('theme_colors', 'TEXT');
|
||||
await addColumnIfNotExists('theme_opacity', 'REAL');
|
||||
await addColumnIfNotExists('theme_rotation', 'INTEGER');
|
||||
await addColumnIfNotExists('theme_texture', 'REAL');
|
||||
|
||||
// Create an index on the uuid column
|
||||
await db.execute(`
|
||||
CREATE INDEX IF NOT EXISTS idx_zen_workspaces_uuid ON zen_workspaces(uuid)
|
||||
`);
|
||||
|
||||
// Create the changes tracking table if it doesn't exist
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS zen_workspaces_changes (
|
||||
uuid TEXT PRIMARY KEY,
|
||||
timestamp INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Create an index on the uuid column for changes tracking table
|
||||
await db.execute(`
|
||||
CREATE INDEX IF NOT EXISTS idx_zen_workspaces_changes_uuid ON zen_workspaces_changes(uuid)
|
||||
`);
|
||||
|
||||
if (!Weave.Service.engineManager.get('workspaces')) {
|
||||
Weave.Service.engineManager.register(ZenWorkspacesEngine);
|
||||
await ZenWorkspacesStorage.migrateWorkspacesFromJSON();
|
||||
}
|
||||
|
||||
ZenWorkspaces._delayedStartup();
|
||||
});
|
||||
},
|
||||
|
||||
async migrateWorkspacesFromJSON() {
|
||||
const oldWorkspacesPath = PathUtils.join(PathUtils.profileDir, 'zen-workspaces', 'Workspaces.json');
|
||||
if (await IOUtils.exists(oldWorkspacesPath)) {
|
||||
console.info('ZenWorkspacesStorage: Migrating workspaces from JSON...');
|
||||
const oldWorkspaces = await IOUtils.readJSON(oldWorkspacesPath);
|
||||
if (oldWorkspaces.workspaces) {
|
||||
for (const workspace of oldWorkspaces.workspaces) {
|
||||
await this.saveWorkspace(workspace);
|
||||
}
|
||||
}
|
||||
await IOUtils.remove(oldWorkspacesPath);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Private helper method to notify observers with a list of changed UUIDs.
|
||||
* @param {string} event - The observer event name.
|
||||
* @param {Array<string>} uuids - Array of changed workspace UUIDs.
|
||||
*/
|
||||
_notifyWorkspacesChanged(event, uuids) {
|
||||
if (uuids.length === 0) return; // No changes to notify
|
||||
|
||||
// Convert the array of UUIDs to a JSON string
|
||||
const data = JSON.stringify(uuids);
|
||||
|
||||
Services.obs.notifyObservers(null, event, data);
|
||||
},
|
||||
|
||||
async saveWorkspace(workspace, notifyObservers = true) {
|
||||
const changedUUIDs = new Set();
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.saveWorkspace', async (db) => {
|
||||
await db.executeTransaction(async () => {
|
||||
const now = Date.now();
|
||||
|
||||
// Handle default workspace
|
||||
if (workspace.default) {
|
||||
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid: workspace.uuid });
|
||||
const unsetDefaultRows = await db.execute(`SELECT uuid FROM zen_workspaces WHERE is_default = 0 AND uuid != :uuid`, { uuid: workspace.uuid });
|
||||
for (const row of unsetDefaultRows) {
|
||||
changedUUIDs.add(row.getResultByName('uuid'));
|
||||
}
|
||||
}
|
||||
|
||||
let newPosition;
|
||||
if ('position' in workspace && Number.isFinite(workspace.position)) {
|
||||
newPosition = workspace.position;
|
||||
} else {
|
||||
// Get the maximum position
|
||||
const maxPositionResult = await db.execute(`SELECT MAX("position") as max_position FROM zen_workspaces`);
|
||||
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
|
||||
newPosition = maxPosition + 1000; // Add a large increment to avoid frequent reordering
|
||||
}
|
||||
|
||||
// Insert or replace the workspace
|
||||
await db.executeCached(`
|
||||
INSERT OR REPLACE INTO zen_workspaces (
|
||||
uuid, name, icon, is_default, container_id, created_at, updated_at, "position",
|
||||
theme_type, theme_colors, theme_opacity, theme_rotation, theme_texture
|
||||
) VALUES (
|
||||
:uuid, :name, :icon, :is_default, :container_id,
|
||||
COALESCE((SELECT created_at FROM zen_workspaces WHERE uuid = :uuid), :now),
|
||||
:now,
|
||||
:position,
|
||||
:theme_type, :theme_colors, :theme_opacity, :theme_rotation, :theme_texture
|
||||
)
|
||||
`, {
|
||||
uuid: workspace.uuid,
|
||||
name: workspace.name,
|
||||
icon: workspace.icon || null,
|
||||
is_default: workspace.default ? 1 : 0,
|
||||
container_id: workspace.containerTabId || null,
|
||||
now,
|
||||
position: newPosition,
|
||||
theme_type: workspace.theme?.type || null,
|
||||
theme_colors: workspace.theme ? JSON.stringify(workspace.theme.gradientColors) : null,
|
||||
theme_opacity: workspace.theme?.opacity || null,
|
||||
theme_rotation: workspace.theme?.rotation || null,
|
||||
theme_texture: workspace.theme?.texture || null
|
||||
});
|
||||
|
||||
// Record the change
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid: workspace.uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
|
||||
changedUUIDs.add(workspace.uuid);
|
||||
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
});
|
||||
|
||||
if (notifyObservers) {
|
||||
this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
|
||||
}
|
||||
},
|
||||
|
||||
async getWorkspaces() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const rows = await db.executeCached(`
|
||||
SELECT * FROM zen_workspaces ORDER BY created_at ASC
|
||||
`);
|
||||
return rows.map((row) => ({
|
||||
uuid: row.getResultByName('uuid'),
|
||||
name: row.getResultByName('name'),
|
||||
icon: row.getResultByName('icon'),
|
||||
default: !!row.getResultByName('is_default'),
|
||||
containerTabId: row.getResultByName('container_id'),
|
||||
position: row.getResultByName('position'),
|
||||
theme: row.getResultByName('theme_type') ? {
|
||||
type: row.getResultByName('theme_type'),
|
||||
gradientColors: JSON.parse(row.getResultByName('theme_colors')),
|
||||
opacity: row.getResultByName('theme_opacity'),
|
||||
rotation: row.getResultByName('theme_rotation'),
|
||||
texture: row.getResultByName('theme_texture')
|
||||
} : null
|
||||
}));
|
||||
},
|
||||
|
||||
async removeWorkspace(uuid, notifyObservers = true) {
|
||||
const changedUUIDs = [uuid];
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.removeWorkspace', async (db) => {
|
||||
await db.execute(
|
||||
`
|
||||
DELETE FROM zen_workspaces WHERE uuid = :uuid
|
||||
`,
|
||||
{ uuid }
|
||||
);
|
||||
|
||||
// Record the removal as a change
|
||||
const now = Date.now();
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
|
||||
if (notifyObservers) {
|
||||
this._notifyWorkspacesChanged("zen-workspace-removed", changedUUIDs);
|
||||
}
|
||||
},
|
||||
|
||||
async wipeAllWorkspaces() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.wipeAllWorkspaces', async (db) => {
|
||||
await db.execute(`DELETE FROM zen_workspaces`);
|
||||
await db.execute(`DELETE FROM zen_workspaces_changes`);
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
},
|
||||
|
||||
async setDefaultWorkspace(uuid, notifyObservers = true) {
|
||||
const changedUUIDs = [];
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.setDefaultWorkspace', async (db) => {
|
||||
await db.executeTransaction(async () => {
|
||||
const now = Date.now();
|
||||
// Unset the default flag for all other workspaces
|
||||
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid });
|
||||
|
||||
// Collect UUIDs of workspaces that were unset as default
|
||||
const unsetDefaultRows = await db.execute(`SELECT uuid FROM zen_workspaces WHERE is_default = 0 AND uuid != :uuid`, { uuid });
|
||||
for (const row of unsetDefaultRows) {
|
||||
changedUUIDs.push(row.getResultByName('uuid'));
|
||||
}
|
||||
|
||||
// Set the default flag for the specified workspace
|
||||
await db.execute(`UPDATE zen_workspaces SET is_default = 1 WHERE uuid = :uuid`, { uuid });
|
||||
|
||||
// Record the change for the specified workspace
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
|
||||
// Add the main workspace UUID to the changed set
|
||||
changedUUIDs.push(uuid);
|
||||
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
});
|
||||
|
||||
if (notifyObservers) {
|
||||
this._notifyWorkspacesChanged("zen-workspace-updated", changedUUIDs);
|
||||
}
|
||||
},
|
||||
|
||||
async markChanged(uuid) {
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.markChanged', async (db) => {
|
||||
const now = Date.now();
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async saveWorkspaceTheme(uuid, theme, notifyObservers = true) {
|
||||
const changedUUIDs = [uuid];
|
||||
await PlacesUtils.withConnectionWrapper('saveWorkspaceTheme', async (db) => {
|
||||
await db.execute(`
|
||||
UPDATE zen_workspaces
|
||||
SET
|
||||
theme_type = :type,
|
||||
theme_colors = :colors,
|
||||
theme_opacity = :opacity,
|
||||
theme_rotation = :rotation,
|
||||
theme_texture = :texture,
|
||||
updated_at = :now
|
||||
WHERE uuid = :uuid
|
||||
`, {
|
||||
type: theme.type,
|
||||
colors: JSON.stringify(theme.gradientColors),
|
||||
opacity: theme.opacity,
|
||||
rotation: theme.rotation,
|
||||
texture: theme.texture,
|
||||
now: Date.now(),
|
||||
uuid
|
||||
});
|
||||
|
||||
await this.markChanged(uuid);
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
|
||||
if (notifyObservers) {
|
||||
this._notifyWorkspacesChanged("zen-workspace-updated", changedUUIDs);
|
||||
}
|
||||
},
|
||||
|
||||
async getChangedIDs() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const rows = await db.execute(`
|
||||
SELECT uuid, timestamp FROM zen_workspaces_changes
|
||||
`);
|
||||
const changes = {};
|
||||
for (const row of rows) {
|
||||
changes[row.getResultByName('uuid')] = row.getResultByName('timestamp');
|
||||
}
|
||||
return changes;
|
||||
},
|
||||
|
||||
async clearChangedIDs() {
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.clearChangedIDs', async (db) => {
|
||||
await db.execute(`DELETE FROM zen_workspaces_changes`);
|
||||
});
|
||||
},
|
||||
|
||||
shouldReorderWorkspaces(before, current, after) {
|
||||
const minGap = 1; // Minimum allowed gap between positions
|
||||
return (before !== null && current - before < minGap) || (after !== null && after - current < minGap);
|
||||
},
|
||||
|
||||
async reorderAllWorkspaces(db, changedUUIDs) {
|
||||
const workspaces = await db.execute(`
|
||||
SELECT uuid
|
||||
FROM zen_workspaces
|
||||
ORDER BY "position" ASC
|
||||
`);
|
||||
|
||||
for (let i = 0; i < workspaces.length; i++) {
|
||||
const newPosition = (i + 1) * 1000; // Use large increments
|
||||
await db.execute(`
|
||||
UPDATE zen_workspaces
|
||||
SET "position" = :newPosition
|
||||
WHERE uuid = :uuid
|
||||
`, { newPosition, uuid: workspaces[i].getResultByName('uuid') });
|
||||
changedUUIDs.add(workspaces[i].getResultByName('uuid'));
|
||||
}
|
||||
},
|
||||
|
||||
async updateLastChangeTimestamp(db) {
|
||||
const now = Date.now();
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO moz_meta (key, value)
|
||||
VALUES ('zen_workspaces_last_change', :now)
|
||||
`, { now });
|
||||
},
|
||||
|
||||
async getLastChangeTimestamp() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const result = await db.executeCached(`
|
||||
SELECT value FROM moz_meta WHERE key = 'zen_workspaces_last_change'
|
||||
`);
|
||||
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
|
||||
},
|
||||
|
||||
async updateWorkspacePositions(workspaces) {
|
||||
const changedUUIDs = new Set();
|
||||
|
||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspacePositions', async (db) => {
|
||||
await db.executeTransaction(async () => {
|
||||
const now = Date.now();
|
||||
|
||||
for (let i = 0; i < workspaces.length; i++) {
|
||||
const workspace = workspaces[i];
|
||||
const newPosition = (i + 1) * 1000;
|
||||
|
||||
await db.execute(`
|
||||
UPDATE zen_workspaces
|
||||
SET "position" = :newPosition
|
||||
WHERE uuid = :uuid
|
||||
`, { newPosition, uuid: workspace.uuid });
|
||||
|
||||
changedUUIDs.add(workspace.uuid);
|
||||
|
||||
// Record the change
|
||||
await db.execute(`
|
||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||
VALUES (:uuid, :timestamp)
|
||||
`, {
|
||||
uuid: workspace.uuid,
|
||||
timestamp: Math.floor(now / 1000)
|
||||
});
|
||||
}
|
||||
|
||||
await this.updateLastChangeTimestamp(db);
|
||||
});
|
||||
});
|
||||
|
||||
this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
|
||||
},
|
||||
};
|
452
src/browser/base/zen-components/ZenWorkspacesSync.mjs
Normal file
452
src/browser/base/zen-components/ZenWorkspacesSync.mjs
Normal file
@@ -0,0 +1,452 @@
|
||||
var { Tracker, Store, SyncEngine } = ChromeUtils.importESModule("resource://services-sync/engines.sys.mjs");
|
||||
var { CryptoWrapper } = ChromeUtils.importESModule("resource://services-sync/record.sys.mjs");
|
||||
var { Utils } = ChromeUtils.importESModule("resource://services-sync/util.sys.mjs");
|
||||
var { SCORE_INCREMENT_XLARGE } = ChromeUtils.importESModule("resource://services-sync/constants.sys.mjs");
|
||||
|
||||
|
||||
|
||||
// Define ZenWorkspaceRecord
|
||||
function ZenWorkspaceRecord(collection, id) {
|
||||
CryptoWrapper.call(this, collection, id);
|
||||
}
|
||||
|
||||
ZenWorkspaceRecord.prototype = Object.create(CryptoWrapper.prototype);
|
||||
ZenWorkspaceRecord.prototype.constructor = ZenWorkspaceRecord;
|
||||
|
||||
ZenWorkspaceRecord.prototype._logName = "Sync.Record.ZenWorkspace";
|
||||
|
||||
Utils.deferGetSet(ZenWorkspaceRecord, "cleartext", [
|
||||
"name",
|
||||
"icon",
|
||||
"default",
|
||||
"containerTabId",
|
||||
"position",
|
||||
"theme_type",
|
||||
"theme_colors",
|
||||
"theme_opacity",
|
||||
"theme_rotation",
|
||||
"theme_texture"
|
||||
]);
|
||||
|
||||
// Define ZenWorkspacesStore
|
||||
function ZenWorkspacesStore(name, engine) {
|
||||
Store.call(this, name, engine);
|
||||
}
|
||||
|
||||
ZenWorkspacesStore.prototype = Object.create(Store.prototype);
|
||||
ZenWorkspacesStore.prototype.constructor = ZenWorkspacesStore;
|
||||
|
||||
/**
|
||||
* Initializes the store by loading the current changeset.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.initialize = async function () {
|
||||
await Store.prototype.initialize.call(this);
|
||||
// Additional initialization if needed
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves all workspace IDs from the storage.
|
||||
* @returns {Object} An object mapping workspace UUIDs to true.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.getAllIDs = async function () {
|
||||
try {
|
||||
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
const ids = {};
|
||||
for (const workspace of workspaces) {
|
||||
ids[workspace.uuid] = true;
|
||||
}
|
||||
return ids;
|
||||
} catch (error) {
|
||||
this._log.error("Error fetching all workspace IDs", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles changing the ID of a workspace.
|
||||
* @param {String} oldID - The old UUID.
|
||||
* @param {String} newID - The new UUID.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.changeItemID = async function (oldID, newID) {
|
||||
try {
|
||||
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
const workspace = workspaces.find(ws => ws.uuid === oldID);
|
||||
if (workspace) {
|
||||
workspace.uuid = newID;
|
||||
await ZenWorkspacesStorage.saveWorkspace(workspace,false);
|
||||
// Mark the new ID as changed for sync
|
||||
await ZenWorkspacesStorage.markChanged(newID);
|
||||
}
|
||||
} catch (error) {
|
||||
this._log.error(`Error changing workspace ID from ${oldID} to ${newID}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a workspace exists.
|
||||
* @param {String} id - The UUID of the workspace.
|
||||
* @returns {Boolean} True if the workspace exists, false otherwise.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.itemExists = async function (id) {
|
||||
try {
|
||||
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
return workspaces.some(ws => ws.uuid === id);
|
||||
} catch (error) {
|
||||
this._log.error(`Error checking if workspace exists with ID ${id}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a record for a workspace.
|
||||
* @param {String} id - The UUID of the workspace.
|
||||
* @param {String} collection - The collection name.
|
||||
* @returns {ZenWorkspaceRecord} The workspace record.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.createRecord = async function (id, collection) {
|
||||
try {
|
||||
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
const workspace = workspaces.find(ws => ws.uuid === id);
|
||||
const record = new ZenWorkspaceRecord(collection, id);
|
||||
|
||||
if (workspace) {
|
||||
record.name = workspace.name;
|
||||
record.icon = workspace.icon;
|
||||
record.default = workspace.default;
|
||||
record.containerTabId = workspace.containerTabId;
|
||||
record.position = workspace.position;
|
||||
if (workspace.theme) {
|
||||
record.theme_type = workspace.theme.type;
|
||||
record.theme_colors = JSON.stringify(workspace.theme.gradientColors);
|
||||
record.theme_opacity = workspace.theme.opacity;
|
||||
record.theme_rotation = workspace.theme.rotation;
|
||||
record.theme_texture = workspace.theme.texture;
|
||||
}
|
||||
record.deleted = false;
|
||||
} else {
|
||||
record.deleted = true;
|
||||
}
|
||||
|
||||
return record;
|
||||
} catch (error) {
|
||||
this._log.error(`Error creating record for workspace ID ${id}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new workspace.
|
||||
* @param {ZenWorkspaceRecord} record - The workspace record to create.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.create = async function (record) {
|
||||
try {
|
||||
this._validateRecord(record);
|
||||
const workspace = {
|
||||
uuid: record.id,
|
||||
name: record.name,
|
||||
icon: record.icon,
|
||||
default: record.default,
|
||||
containerTabId: record.containerTabId,
|
||||
position: record.position,
|
||||
theme: record.theme_type ? {
|
||||
type: record.theme_type,
|
||||
gradientColors: JSON.parse(record.theme_colors),
|
||||
opacity: record.theme_opacity,
|
||||
rotation: record.theme_rotation,
|
||||
texture: record.theme_texture
|
||||
} : null
|
||||
};
|
||||
await ZenWorkspacesStorage.saveWorkspace(workspace,false);
|
||||
} catch (error) {
|
||||
this._log.error(`Error creating workspace with ID ${record.id}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an existing workspace.
|
||||
* @param {ZenWorkspaceRecord} record - The workspace record to update.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.update = async function (record) {
|
||||
try {
|
||||
this._validateRecord(record);
|
||||
await this.create(record); // Reuse create for update
|
||||
} catch (error) {
|
||||
this._log.error(`Error updating workspace with ID ${record.id}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a workspace.
|
||||
* @param {ZenWorkspaceRecord} record - The workspace record to remove.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.remove = async function (record) {
|
||||
try {
|
||||
await ZenWorkspacesStorage.removeWorkspace(record.id, false);
|
||||
} catch (error) {
|
||||
this._log.error(`Error removing workspace with ID ${record.id}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wipes all workspaces from the storage.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.wipe = async function () {
|
||||
try {
|
||||
await ZenWorkspacesStorage.wipeAllWorkspaces();
|
||||
} catch (error) {
|
||||
this._log.error("Error wiping all workspaces", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a workspace record.
|
||||
* @param {ZenWorkspaceRecord} record - The workspace record to validate.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype._validateRecord = function (record) {
|
||||
if (!record.id || typeof record.id !== "string") {
|
||||
throw new Error("Invalid workspace ID");
|
||||
}
|
||||
if (!record.name || typeof record.name !== "string") {
|
||||
throw new Error(`Invalid workspace name for ID ${record.id}`);
|
||||
}
|
||||
if (typeof record.default !== "boolean") {
|
||||
record.default = false;
|
||||
}
|
||||
if (record.icon != null && typeof record.icon !== "string") {
|
||||
throw new Error(`Invalid icon for workspace ID ${record.id}`);
|
||||
}
|
||||
if (record.containerTabId != null && typeof record.containerTabId !== "number") {
|
||||
throw new Error(`Invalid containerTabId for workspace ID ${record.id}`);
|
||||
}
|
||||
if(record.position != null && typeof record.position !== "number") {
|
||||
throw new Error(`Invalid position for workspace ID ${record.id}`);
|
||||
}
|
||||
|
||||
// Validate theme properties if they exist
|
||||
if (record.theme_type) {
|
||||
if (typeof record.theme_type !== "string") {
|
||||
throw new Error(`Invalid theme_type for workspace ID ${record.id}`);
|
||||
}
|
||||
if (!record.theme_colors || typeof record.theme_colors !== "string") {
|
||||
throw new Error(`Invalid theme_colors for workspace ID ${record.id}`);
|
||||
}
|
||||
try {
|
||||
JSON.parse(record.theme_colors);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid theme_colors JSON for workspace ID ${record.id}`);
|
||||
}
|
||||
if (record.theme_opacity != null && typeof record.theme_opacity !== "number") {
|
||||
throw new Error(`Invalid theme_opacity for workspace ID ${record.id}`);
|
||||
}
|
||||
if (record.theme_rotation != null && typeof record.theme_rotation !== "number") {
|
||||
throw new Error(`Invalid theme_rotation for workspace ID ${record.id}`);
|
||||
}
|
||||
if (record.theme_texture != null && typeof record.theme_texture !== "number") {
|
||||
throw new Error(`Invalid theme_texture for workspace ID ${record.id}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves changed workspace IDs since the last sync.
|
||||
* @returns {Object} An object mapping workspace UUIDs to their change timestamps.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.getChangedIDs = async function () {
|
||||
try {
|
||||
return await ZenWorkspacesStorage.getChangedIDs();
|
||||
} catch (error) {
|
||||
this._log.error("Error retrieving changed IDs from storage", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears all recorded changes after a successful sync.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.clearChangedIDs = async function () {
|
||||
try {
|
||||
await ZenWorkspacesStorage.clearChangedIDs();
|
||||
} catch (error) {
|
||||
this._log.error("Error clearing changed IDs in storage", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marks a workspace as changed.
|
||||
* @param {String} uuid - The UUID of the workspace that changed.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.markChanged = async function (uuid) {
|
||||
try {
|
||||
await ZenWorkspacesStorage.markChanged(uuid);
|
||||
} catch (error) {
|
||||
this._log.error(`Error marking workspace ${uuid} as changed`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finalizes the store by ensuring all pending operations are completed.
|
||||
*/
|
||||
ZenWorkspacesStore.prototype.finalize = async function () {
|
||||
await Store.prototype.finalize.call(this);
|
||||
};
|
||||
|
||||
|
||||
// Define ZenWorkspacesTracker
|
||||
function ZenWorkspacesTracker(name, engine) {
|
||||
Tracker.call(this, name, engine);
|
||||
this._ignoreAll = false;
|
||||
|
||||
// Observe profile-before-change to stop the tracker gracefully
|
||||
Services.obs.addObserver(this.asyncObserver, "profile-before-change");
|
||||
}
|
||||
|
||||
ZenWorkspacesTracker.prototype = Object.create(Tracker.prototype);
|
||||
ZenWorkspacesTracker.prototype.constructor = ZenWorkspacesTracker;
|
||||
|
||||
/**
|
||||
* Retrieves changed workspace IDs by delegating to the store.
|
||||
* @returns {Object} An object mapping workspace UUIDs to their change timestamps.
|
||||
*/
|
||||
ZenWorkspacesTracker.prototype.getChangedIDs = async function () {
|
||||
try {
|
||||
return await this.engine._store.getChangedIDs();
|
||||
} catch (error) {
|
||||
this._log.error("Error retrieving changed IDs from store", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears all recorded changes after a successful sync.
|
||||
*/
|
||||
ZenWorkspacesTracker.prototype.clearChangedIDs = async function () {
|
||||
try {
|
||||
await this.engine._store.clearChangedIDs();
|
||||
} catch (error) {
|
||||
this._log.error("Error clearing changed IDs in store", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the tracker starts. Registers observers to listen for workspace changes.
|
||||
*/
|
||||
ZenWorkspacesTracker.prototype.onStart = function () {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._log.trace("Starting tracker");
|
||||
// Register observers for workspace changes
|
||||
Services.obs.addObserver(this.asyncObserver, "zen-workspace-added");
|
||||
Services.obs.addObserver(this.asyncObserver, "zen-workspace-removed");
|
||||
Services.obs.addObserver(this.asyncObserver, "zen-workspace-updated");
|
||||
this._started = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the tracker stops. Unregisters observers.
|
||||
*/
|
||||
ZenWorkspacesTracker.prototype.onStop = function () {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._log.trace("Stopping tracker");
|
||||
// Unregister observers for workspace changes
|
||||
Services.obs.removeObserver(this.asyncObserver, "zen-workspace-added");
|
||||
Services.obs.removeObserver(this.asyncObserver, "zen-workspace-removed");
|
||||
Services.obs.removeObserver(this.asyncObserver, "zen-workspace-updated");
|
||||
this._started = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles observed events and marks workspaces as changed accordingly.
|
||||
* @param {nsISupports} subject - The subject of the notification.
|
||||
* @param {String} topic - The topic of the notification.
|
||||
* @param {String} data - Additional data (JSON stringified array of UUIDs).
|
||||
*/
|
||||
ZenWorkspacesTracker.prototype.observe = async function (subject, topic, data) {
|
||||
if (this.ignoreAll) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (topic) {
|
||||
case "profile-before-change":
|
||||
await this.stop();
|
||||
break;
|
||||
case "zen-workspace-removed":
|
||||
case "zen-workspace-updated":
|
||||
case "zen-workspace-added":
|
||||
let workspaceIDs;
|
||||
if (data) {
|
||||
try {
|
||||
workspaceIDs = JSON.parse(data);
|
||||
if (!Array.isArray(workspaceIDs)) {
|
||||
throw new Error("Parsed data is not an array");
|
||||
}
|
||||
} catch (parseError) {
|
||||
this._log.error(`Failed to parse workspace UUIDs from data: ${data}`, parseError);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this._log.error(`No data received for event ${topic}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.trace(`Observed ${topic} for UUIDs: ${workspaceIDs.join(", ")}`);
|
||||
|
||||
// Process each UUID
|
||||
for (const workspaceID of workspaceIDs) {
|
||||
if (typeof workspaceID === "string") {
|
||||
// Inform the store about the change
|
||||
await this.engine._store.markChanged(workspaceID);
|
||||
} else {
|
||||
this._log.warn(`Invalid workspace ID encountered: ${workspaceID}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Bump the score once after processing all changes
|
||||
if (workspaceIDs.length > 0) {
|
||||
this.score += SCORE_INCREMENT_XLARGE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
this._log.error(`Error handling ${topic} in observe method`, error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finalizes the tracker by ensuring all pending operations are completed.
|
||||
*/
|
||||
ZenWorkspacesTracker.prototype.finalize = async function () {
|
||||
await Tracker.prototype.finalize.call(this);
|
||||
};
|
||||
|
||||
|
||||
// Define ZenWorkspacesEngine
|
||||
function ZenWorkspacesEngine(service) {
|
||||
SyncEngine.call(this, "Workspaces", service);
|
||||
}
|
||||
|
||||
ZenWorkspacesEngine.prototype = Object.create(SyncEngine.prototype);
|
||||
ZenWorkspacesEngine.prototype.constructor = ZenWorkspacesEngine;
|
||||
|
||||
ZenWorkspacesEngine.prototype._storeObj = ZenWorkspacesStore;
|
||||
ZenWorkspacesEngine.prototype._trackerObj = ZenWorkspacesTracker;
|
||||
ZenWorkspacesEngine.prototype._recordObj = ZenWorkspaceRecord;
|
||||
ZenWorkspacesEngine.prototype.version = 2;
|
||||
|
||||
ZenWorkspacesEngine.prototype.syncPriority = 10;
|
||||
ZenWorkspacesEngine.prototype.allowSkippedRecord = false;
|
||||
|
||||
Object.setPrototypeOf(ZenWorkspacesEngine.prototype, SyncEngine.prototype);
|
||||
|
||||
|
119
src/browser/base/zen-components/actors/ZenGlanceChild.sys.mjs
Normal file
119
src/browser/base/zen-components/actors/ZenGlanceChild.sys.mjs
Normal file
@@ -0,0 +1,119 @@
|
||||
export class ZenGlanceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.mouseUpListener = this.handleMouseUp.bind(this);
|
||||
this.mouseDownListener = this.handleMouseDown.bind(this);
|
||||
this.clickListener = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
async handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'DOMContentLoaded':
|
||||
await this.initiateGlance();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
async getActivationMethod() {
|
||||
if (this._activationMethod === undefined) {
|
||||
this._activationMethod = await this.sendQuery('ZenGlance:GetActivationMethod');
|
||||
}
|
||||
return this._activationMethod;
|
||||
}
|
||||
|
||||
async getHoverActivationDelay() {
|
||||
if (this._hoverActivationDelay === undefined) {
|
||||
this._hoverActivationDelay = await this.sendQuery('ZenGlance:GetHoverActivationDelay');
|
||||
}
|
||||
return this._hoverActivationDelay;
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
}
|
||||
}
|
||||
|
||||
async initiateGlance() {
|
||||
this.mouseIsDown = false;
|
||||
const activationMethod = await this.getActivationMethod();
|
||||
if (activationMethod === 'mantain') {
|
||||
this.contentWindow.addEventListener('mousedown', this.mouseDownListener);
|
||||
this.contentWindow.addEventListener('mouseup', this.mouseUpListener);
|
||||
|
||||
this.contentWindow.document.removeEventListener('click', this.clickListener);
|
||||
} else if (activationMethod === 'ctrl' || activationMethod === 'alt' || activationMethod === 'shift') {
|
||||
this.contentWindow.document.addEventListener('click', this.clickListener);
|
||||
|
||||
this.contentWindow.removeEventListener('mousedown', this.mouseDownListener);
|
||||
this.contentWindow.removeEventListener('mouseup', this.mouseUpListener);
|
||||
}
|
||||
}
|
||||
|
||||
ensureOnlyKeyModifiers(event) {
|
||||
return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey);
|
||||
}
|
||||
|
||||
openGlance(target) {
|
||||
const rect = target.getBoundingClientRect();
|
||||
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
||||
url: target.href,
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
}
|
||||
|
||||
handleMouseUp(event) {
|
||||
if (this.hasClicked) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.hasClicked = false;
|
||||
}
|
||||
this.mouseIsDown = null;
|
||||
}
|
||||
|
||||
async handleMouseDown(event) {
|
||||
const target = event.target.closest('A');
|
||||
console.log('target', target);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
this.mouseIsDown = target;
|
||||
const hoverActivationDelay = await this.getHoverActivationDelay();
|
||||
this.contentWindow.setTimeout(() => {
|
||||
if (this.mouseIsDown === target) {
|
||||
this.hasClicked = true;
|
||||
this.openGlance(target);
|
||||
}
|
||||
}, hoverActivationDelay);
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
if (this.ensureOnlyKeyModifiers(event)) {
|
||||
return;
|
||||
}
|
||||
const activationMethod = this._activationMethod;
|
||||
if (activationMethod === 'ctrl' && !event.ctrlKey) {
|
||||
return;
|
||||
} else if (activationMethod === 'alt' && !event.altKey) {
|
||||
return;
|
||||
} else if (activationMethod === 'shift' && !event.shiftKey) {
|
||||
return;
|
||||
} else if (activationMethod === 'meta' && !event.metaKey) {
|
||||
return;
|
||||
}else if (activationMethod === 'mantain' || typeof activationMethod === 'undefined') {
|
||||
return;
|
||||
}
|
||||
// get closest A element
|
||||
const target = event.target.closest('A');
|
||||
if (target) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.openGlance(target);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
export class ZenGlanceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenGlance:GetActivationMethod': {
|
||||
return Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl');
|
||||
}
|
||||
case 'ZenGlance:GetHoverActivationDelay': {
|
||||
return Services.prefs.getIntPref('zen.glance.hold-duration', 500);
|
||||
}
|
||||
case 'ZenGlance:OpenGlance': {
|
||||
this.openGlance(this.browsingContext.topChromeWindow, message.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openGlance(window, data) {
|
||||
window.gZenGlanceManager.openGlance(data);
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
export class ZenThemeMarketplaceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'DOMContentLoaded':
|
||||
this.initiateThemeMarketplace();
|
||||
this.contentWindow.document.addEventListener('ZenCheckForThemeUpdates', this.checkForThemeUpdates.bind(this));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// This function will be caleld from about:preferences
|
||||
checkForThemeUpdates(event) {
|
||||
event.preventDefault();
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdates');
|
||||
}
|
||||
|
||||
initiateThemeMarketplace() {
|
||||
this.contentWindow.setTimeout(() => {
|
||||
this.addIntallButtons();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get actionButton() {
|
||||
return this.contentWindow.document.getElementById('install-theme');
|
||||
}
|
||||
|
||||
get actionButtonUnnstall() {
|
||||
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.actionButtonUnnstall;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async addIntallButtons() {
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonUnnstall = this.actionButtonUnnstall;
|
||||
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
|
||||
if (!actionButton || !actionButtonUnnstall) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessage.classList.add('hidden');
|
||||
|
||||
const themeId = actionButton.getAttribute('zen-theme-id');
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButtonUnnstall.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
}
|
||||
|
||||
actionButton.addEventListener('click', this.installTheme.bind(this));
|
||||
actionButtonUnnstall.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);
|
||||
console.info('ZTM: Fetching theme 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('ZTM: Error parsing theme info: ', e);
|
||||
}
|
||||
} else console.log(data.status);
|
||||
return null;
|
||||
}
|
||||
|
||||
async uninstallTheme(event) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
const themeId = button.getAttribute('zen-theme-id');
|
||||
console.info('ZTM: Uninstalling theme with id: ', themeId);
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:UninstallTheme', { themeId });
|
||||
}
|
||||
|
||||
async installTheme(event) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
const themeId = button.getAttribute('zen-theme-id');
|
||||
console.info('ZTM: Installing theme with id: ', themeId);
|
||||
|
||||
const theme = await this.getThemeInfo(themeId);
|
||||
if (!theme) {
|
||||
console.error('ZTM: Error fetching theme info');
|
||||
return;
|
||||
}
|
||||
this.addTheme(theme);
|
||||
}
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
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 themes[themeId] ? true : false;
|
||||
}
|
||||
case 'ZenThemeMarketplace:CheckForUpdates': {
|
||||
this.checkForThemeUpdates();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compareversion(version1, version2) {
|
||||
var result = false;
|
||||
if (typeof version1 !== 'object') {
|
||||
version1 = version1.toString().split('.');
|
||||
}
|
||||
if (typeof version2 !== 'object') {
|
||||
version2 = version2.toString().split('.');
|
||||
}
|
||||
for (var 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 = [];
|
||||
this._themes = null;
|
||||
for (const theme of Object.values(await this.getThemes())) {
|
||||
const themeInfo = await this.sendQuery('ZenThemeMarketplace:GetThemeInfo', { themeId: theme.id });
|
||||
if (!themeInfo) {
|
||||
continue;
|
||||
}
|
||||
if (!this.compareversion(themeInfo.version, theme.version || '0.0.0') && themeInfo.version != theme.version) {
|
||||
console.info('ZenThemeMarketplaceParent: Theme update found', theme.id, theme.version, themeInfo.version);
|
||||
themeInfo.enabled = theme.enabled;
|
||||
updates.push(themeInfo);
|
||||
await this.removeTheme(theme.id, false);
|
||||
this._themes[themeInfo.id] = themeInfo;
|
||||
}
|
||||
}
|
||||
await this.updateThemes(this._themes);
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdatesFinished', { updates });
|
||||
}
|
||||
|
||||
async updateChildProcesses(themeId) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:ThemeChanged', { themeId });
|
||||
}
|
||||
|
||||
async getThemes() {
|
||||
if (!this._themes) {
|
||||
if (!(await IOUtils.exists(this.themesDataFile))) {
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
}
|
||||
this._themes = await IOUtils.readJSON(this.themesDataFile);
|
||||
}
|
||||
return this._themes;
|
||||
}
|
||||
|
||||
async updateThemes(themes) {
|
||||
this._themes = themes;
|
||||
await IOUtils.writeJSON(this.themesDataFile, themes);
|
||||
await this.checkForThemeChanges();
|
||||
}
|
||||
|
||||
getStyleSheetFullContent(style = '') {
|
||||
let stylesheet = '@-moz-document url-prefix("chrome:") {';
|
||||
|
||||
for (const line of style.split('\n')) {
|
||||
stylesheet += ` ${line}`;
|
||||
}
|
||||
|
||||
stylesheet += '}';
|
||||
|
||||
return stylesheet;
|
||||
}
|
||||
|
||||
async downloadUrlToFile(url, path, isStyleSheet = false) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const data = await response.text();
|
||||
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
|
||||
// convert the data into a Uint8Array
|
||||
let buffer = new TextEncoder().encode(content);
|
||||
await IOUtils.write(path, buffer);
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error downloading file', url, e);
|
||||
}
|
||||
}
|
||||
|
||||
async downloadThemeFileContents(theme) {
|
||||
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'));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
await this.downloadThemeFileContents(theme);
|
||||
}
|
||||
|
||||
async checkForThemeChanges() {
|
||||
const themes = await this.getThemes();
|
||||
const themeIds = Object.keys(themes);
|
||||
let changed = false;
|
||||
for (const themeId of themeIds) {
|
||||
const theme = themes[themeId];
|
||||
if (!theme) {
|
||||
continue;
|
||||
}
|
||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
||||
if (!(await IOUtils.exists(themePath))) {
|
||||
await this.installTheme(theme);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
diff --git a/browser/components/controlcenter/content/identityPanel.inc.xhtml b/browser/components/controlcenter/content/identityPanel.inc.xhtml
|
||||
index 0344fcb566e8e0bedd92b0d45e42aea38de5c329..0d5296700590dd0c8a90929976b8cbc5370e7b47 100644
|
||||
index 768768c7d44860e1725f41594da9bcc8c92f4a1d..c04b0d54a41fbf8a22b516524809186d29842b21 100644
|
||||
--- a/browser/components/controlcenter/content/identityPanel.inc.xhtml
|
||||
+++ b/browser/components/controlcenter/content/identityPanel.inc.xhtml
|
||||
@@ -30,7 +30,7 @@
|
||||
@@ -28,7 +28,7 @@
|
||||
<toolbarseparator/>
|
||||
<vbox class="panel-subview-body">
|
||||
<toolbarbutton id="identity-popup-security-button"
|
||||
- class="identity-popup-security-connection subviewbutton subviewbutton-iconic"
|
||||
+ class="identity-popup-security-connection subviewbutton"
|
||||
oncommand="gIdentityHandler.showSecuritySubView();">
|
||||
- class="identity-popup-security-connection subviewbutton subviewbutton-iconic">
|
||||
+ class="identity-popup-security-connection subviewbutton">
|
||||
<image class="identity-popup-security-connection-icon toolbarbutton-icon"></image>
|
||||
<hbox class="toolbarbutton-text" flex="1">
|
||||
<label class="identity-popup-connection-not-secure"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a3c5f7fce 100644
|
||||
index 989e69245aeb1185125752db6b9c58e462d554e4..a4825653b48aa5012e58a277c862caaf0e37abab 100644
|
||||
--- a/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
@@ -13,6 +13,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -19,12 +19,13 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
|
||||
|
||||
const kSpecialWidgetPfx = "customizableui-special-";
|
||||
|
||||
@@ -253,13 +254,14 @@ var CustomizableUIInternal = {
|
||||
@@ -323,13 +324,14 @@ var CustomizableUIInternal = {
|
||||
"spring",
|
||||
"urlbar-container",
|
||||
"spring",
|
||||
- "save-to-pocket-button",
|
||||
"downloads-button",
|
||||
- "downloads-button",
|
||||
+// "downloads-button",
|
||||
AppConstants.MOZ_DEV_EDITION ? "developer-button" : null,
|
||||
- "fxa-toolbar-menu-button",
|
||||
+ "wrapper-sidebar-button",
|
||||
@@ -36,7 +37,16 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
|
||||
this.registerArea(
|
||||
CustomizableUI.AREA_NAVBAR,
|
||||
{
|
||||
@@ -288,10 +290,10 @@ var CustomizableUIInternal = {
|
||||
@@ -338,7 +340,7 @@ var CustomizableUIInternal = {
|
||||
defaultPlacements: navbarPlacements,
|
||||
verticalTabsDefaultPlacements: [
|
||||
"firefox-view-button",
|
||||
- "new-tab-button",
|
||||
+// "new-tab-button",
|
||||
"alltabs-button",
|
||||
],
|
||||
defaultCollapsed: false,
|
||||
@@ -363,10 +365,10 @@ var CustomizableUIInternal = {
|
||||
{
|
||||
type: CustomizableUI.TYPE_TOOLBAR,
|
||||
defaultPlacements: [
|
||||
@@ -48,9 +58,9 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
|
||||
+// "new-tab-button",
|
||||
+// "alltabs-button",
|
||||
],
|
||||
verticalTabsDefaultPlacements: [],
|
||||
defaultCollapsed: null,
|
||||
},
|
||||
@@ -331,6 +333,7 @@ var CustomizableUIInternal = {
|
||||
@@ -429,6 +431,7 @@ var CustomizableUIInternal = {
|
||||
CustomizableUI.AREA_NAVBAR,
|
||||
CustomizableUI.AREA_BOOKMARKS,
|
||||
CustomizableUI.AREA_TABSTRIP,
|
||||
@@ -58,7 +68,7 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
|
||||
]);
|
||||
if (AppConstants.platform != "macosx") {
|
||||
toolbars.add(CustomizableUI.AREA_MENUBAR);
|
||||
@@ -1033,6 +1036,9 @@ var CustomizableUIInternal = {
|
||||
@@ -1144,6 +1147,9 @@ var CustomizableUIInternal = {
|
||||
placements = gPlacements.get(area);
|
||||
}
|
||||
|
||||
@@ -68,7 +78,7 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
|
||||
// For toolbars that need it, mark as dirty.
|
||||
let defaultPlacements = areaProperties.get("defaultPlacements");
|
||||
if (
|
||||
@@ -3289,6 +3295,9 @@ var CustomizableUIInternal = {
|
||||
@@ -3521,6 +3527,9 @@ var CustomizableUIInternal = {
|
||||
gSeenWidgets.add(widgetId);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
diff --git a/browser/components/places/content/browserPlacesViews.js b/browser/components/places/content/browserPlacesViews.js
|
||||
index 1bfa0af16178c9b42172bc1b1e0249d28ff8e9e6..e7e76a6d548b32887c1d39053e42c5e3dafbb839 100644
|
||||
--- a/browser/components/places/content/browserPlacesViews.js
|
||||
+++ b/browser/components/places/content/browserPlacesViews.js
|
||||
@@ -393,6 +393,7 @@ class PlacesViewBase {
|
||||
"scheme",
|
||||
PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri)
|
||||
);
|
||||
+ element.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager));
|
||||
} else if (PlacesUtils.containerTypes.includes(type)) {
|
||||
element = document.createXULElement("menu");
|
||||
element.setAttribute("container", "true");
|
||||
@@ -1087,6 +1088,8 @@ class PlacesToolbar extends PlacesViewBase {
|
||||
"scheme",
|
||||
PlacesUIUtils.guessUrlSchemeForUI(aChild.uri)
|
||||
);
|
||||
+ button.hidden = ZenWorkspaces.isBookmarkInAnotherWorkspace(aChild);
|
||||
+ button.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2235,7 +2238,7 @@ this.PlacesPanelview = class PlacesPanelview extends PlacesViewBase {
|
||||
PlacesUIUtils.guessUrlSchemeForUI(placesNode.uri)
|
||||
);
|
||||
element.setAttribute("label", PlacesUIUtils.getBestTitle(placesNode));
|
||||
-
|
||||
+ element.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager));
|
||||
let icon = placesNode.icon;
|
||||
if (icon) {
|
||||
element.setAttribute("image", icon);
|
@@ -645,6 +645,7 @@ var gZenLooksAndFeel = {
|
||||
colorElemParen.setAttribute('selected', 'true');
|
||||
}
|
||||
colorElemParen.addEventListener('click', () => {
|
||||
Services.prefs.setBoolPref('zen.theme.color-prefs.use-workspace-colors', false);
|
||||
Services.prefs.setStringPref('zen.theme.accent-color', color);
|
||||
});
|
||||
colorElemParen.appendChild(colorElem);
|
||||
@@ -674,9 +675,13 @@ var gZenWorkspacesSettings = {
|
||||
};
|
||||
Services.prefs.addObserver('zen.workspaces.enabled', this);
|
||||
Services.prefs.addObserver('zen.tab-unloader.enabled', tabsUnloaderPrefListener);
|
||||
Services.prefs.addObserver('zen.glance.enabled', tabsUnloaderPrefListener); // We can use the same listener for both prefs
|
||||
Services.prefs.addObserver('zen.glance.activation-method', tabsUnloaderPrefListener);
|
||||
window.addEventListener('unload', () => {
|
||||
Services.prefs.removeObserver('zen.workspaces.enabled', this);
|
||||
Services.prefs.removeObserver('zen.tab-unloader.enabled', tabsUnloaderPrefListener);
|
||||
Services.prefs.removeObserver('zen.glance.enabled', tabsUnloaderPrefListener);
|
||||
Services.prefs.removeObserver('zen.glance.activation-method', tabsUnloaderPrefListener);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -965,11 +970,6 @@ var gZenCKSSettings = {
|
||||
};
|
||||
|
||||
Preferences.addAll([
|
||||
{
|
||||
id: 'zen.theme.toolbar-themed',
|
||||
type: 'bool',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: 'zen.sidebar.enabled',
|
||||
type: 'bool',
|
||||
@@ -1074,5 +1074,50 @@ Preferences.addAll([
|
||||
id: 'zen.tabs.show-newtab-under',
|
||||
type: 'bool',
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "zen.glance.activation-method",
|
||||
type: "string",
|
||||
default: "ctrl",
|
||||
},
|
||||
{
|
||||
id: "zen.glance.enabled",
|
||||
type: "bool",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "zen.theme.color-prefs.use-workspace-colors",
|
||||
type: "bool",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "zen.view.compact.color-toolbar",
|
||||
type: "bool",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "zen.view.compact.color-sidebar",
|
||||
type: "bool",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "zen.essentials.enabled",
|
||||
type: "bool",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "zen.tabs.show-newtab-vertical",
|
||||
type: "bool",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "zen.view.show-newtab-button-border-top",
|
||||
type: "bool",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "zen.view.show-newtab-button-top",
|
||||
type: "bool",
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
|
@@ -18,9 +18,9 @@
|
||||
<html:div id="zen-theme-builder-wrapper"></html:div>
|
||||
|
||||
<box class="indent">
|
||||
<checkbox id="zenLooksAndFeelUseThemedToolbar"
|
||||
data-l10n-id="zen-look-and-feel-themed-toolbar"
|
||||
preference="zen.theme.toolbar-themed"/>
|
||||
<checkbox id="zenLooksAndFeelUseWorkspaceColors"
|
||||
data-l10n-id="zen-look-and-feel-use-workspace-colors"
|
||||
preference="zen.theme.color-prefs.use-workspace-colors"/>
|
||||
</box>
|
||||
|
||||
<hbox id="zenLooksAndFeelGradientPickerParent"></hbox>
|
||||
@@ -236,6 +236,12 @@
|
||||
<checkbox id="zenLooksAndFeelEnableToolbarFlashPopup"
|
||||
data-l10n-id="zen-look-and-feel-compact-toolbar-flash-popup"
|
||||
preference="zen.view.compact.toolbar-flash-popup"/>
|
||||
<checkbox id="zenLooksAndFeelEnableThemedToolbarCompactMode"
|
||||
data-l10n-id="zen-look-and-feel-compact-toolbar-themed"
|
||||
preference="zen.view.compact.color-toolbar"/>
|
||||
<checkbox id="zenLooksAndFeelEnableThemedSidebarCompactMode"
|
||||
data-l10n-id="zen-look-and-feel-compact-sidebar-themed"
|
||||
preference="zen.view.compact.color-sidebar"/>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
@@ -260,6 +266,36 @@
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<hbox id="zenGlanceCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
data-category="paneZenLooks">
|
||||
<html:h1 data-l10n-id="zen-glance-title"/>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="zenGlanceGroup" data-category="paneZenLooks" hidden="true" class="highlighting-group">
|
||||
<label><html:h2 data-l10n-id="zen-glance-header"/></label>
|
||||
<description class="description-deemphasized" data-l10n-id="zen-glance-description" />
|
||||
|
||||
<checkbox id="zenLooksAndFeelGlanceEnabled"
|
||||
data-l10n-id="zen-glance-enabled"
|
||||
preference="zen.glance.enabled"/>
|
||||
<hbox align="center">
|
||||
<label id="zenGlanceTriggerLabel" data-l10n-id="zen-glance-trigger-label"/>
|
||||
<menulist id="zenGlanceTrigger" preference="zen.glance.activation-method">
|
||||
<menupopup>
|
||||
<menuitem data-l10n-id="zen-glance-trigger-ctrl-click" value="ctrl"/>
|
||||
<menuitem data-l10n-id="zen-glance-trigger-alt-click" value="alt"/>
|
||||
<menuitem data-l10n-id="zen-glance-trigger-shift-click" value="shift"/>
|
||||
#ifdef XP_MACOSX
|
||||
<menuitem data-l10n-id="zen-glance-trigger-meta-click" value="meta"/>
|
||||
#endif
|
||||
<menuitem data-l10n-id="zen-glance-trigger-mantain-click" value="mantain" disabled="true"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<hbox id="zenSplitViewCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
@@ -288,13 +324,19 @@
|
||||
<description class="description-deemphasized" data-l10n-id="zen-vertical-tabs-description" />
|
||||
|
||||
<checkbox id="zenLooksAndFeelNewtabUnderLastTab"
|
||||
data-l10n-id="zen-vertical-tabs-newtab-under-last-tab"
|
||||
preference="zen.tabs.show-newtab-under"/>
|
||||
<hbox class="indent">
|
||||
data-l10n-id="zen-vertical-tabs-newtab-on-tab-list"
|
||||
preference="zen.tabs.show-newtab-vertical"/>
|
||||
<vbox class="indent">
|
||||
<checkbox id="zenLooksAndFeelShowTopNewtabBorder"
|
||||
data-l10n-id="zen-vertical-tabs-newtab-top-button-border"
|
||||
preference="zen.view.show-newtab-button-border-top"/>
|
||||
<checkbox id="zenLooksAndFeelMoveNewtabToTop"
|
||||
data-l10n-id="zen-vertical-tabs-newtab-top-button-up"
|
||||
preference="zen.view.show-newtab-button-top"/>
|
||||
<checkbox id="zenLooksAndFeelShowBottomBorder"
|
||||
data-l10n-id="zen-vertical-tabs-show-bottom-border"
|
||||
preference="zen.view.show-bottom-border"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<label><html:h2 data-l10n-id="zen-vertical-tabs-expand-tabs-header"/></label>
|
||||
<description class="description-deemphasized" data-l10n-id="zen-vertical-tabs-expand-tabs-description" />
|
||||
|
@@ -1,12 +1,14 @@
|
||||
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
||||
index 26f5671c849d9b0a126d79b07bc7d3d7870826ec..21d63856a06cd46424e1c1a3697b2f3d225d5c08 100644
|
||||
index 26f5671c849d9b0a126d79b07bc7d3d7870826ec..36b535321f17a4e3dfdfdbb677a4e4b869038154 100644
|
||||
--- a/browser/components/sessionstore/TabState.sys.mjs
|
||||
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
||||
@@ -98,6 +98,11 @@ var TabStateInternal = {
|
||||
@@ -98,6 +98,13 @@ var TabStateInternal = {
|
||||
tabData.muteReason = tab.muteReason;
|
||||
}
|
||||
|
||||
+ tabData.zenWorkspace = tab.getAttribute("zen-workspace-id");
|
||||
+ tabData.zenPinnedId = tab.getAttribute("zen-pin-id");
|
||||
+ tabData.zenEssential = tab.getAttribute("zen-essential");
|
||||
+ tabData.zenDefaultUserContextId = tab.getAttribute("zenDefaultUserContextId");
|
||||
+ tabData.zenPinnedEntry = tab.getAttribute("zen-pinned-entry");
|
||||
+ tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon");
|
||||
|
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/sidebar/browser-sidebar.js b/browser/components/sidebar/browser-sidebar.js
|
||||
index c68a32794996348d95f3b1ee363be2eec587065e..3a163c75ac10a204754648dfd1332c79895799c7 100644
|
||||
index 4a124003976684e014435854aef69ce29da541d2..61ce44751c36eea3e5ae2ddcc62e42c01459629b 100644
|
||||
--- a/browser/components/sidebar/browser-sidebar.js
|
||||
+++ b/browser/components/sidebar/browser-sidebar.js
|
||||
@@ -521,7 +521,7 @@ var SidebarController = {
|
||||
@@ -578,7 +578,7 @@ var SidebarController = {
|
||||
*/
|
||||
setPosition() {
|
||||
// First reset all ordinals to match DOM ordering.
|
||||
@@ -11,12 +11,11 @@ index c68a32794996348d95f3b1ee363be2eec587065e..3a163c75ac10a204754648dfd1332c79
|
||||
[...browser.children].forEach((node, i) => {
|
||||
node.style.order = i + 1;
|
||||
});
|
||||
@@ -535,9 +535,11 @@ var SidebarController = {
|
||||
@@ -592,9 +592,10 @@ var SidebarController = {
|
||||
let boxOrdinal = this._box.style.order;
|
||||
this._box.style.order = appcontent.style.order;
|
||||
this._box.style.order = tabbox.style.order;
|
||||
|
||||
- appcontent.style.order = boxOrdinal;
|
||||
+ // appcontent.style.order = boxOrdinal;
|
||||
- tabbox.style.order = boxOrdinal;
|
||||
// the launcher should be on the right of the sidebar-box
|
||||
- sidebarContainer.style.order = parseInt(this._box.style.order) + 1;
|
||||
+ this._box.style.order = browser.children.length + 2;
|
||||
@@ -25,7 +24,7 @@ index c68a32794996348d95f3b1ee363be2eec587065e..3a163c75ac10a204754648dfd1332c79
|
||||
// Indicate we've switched ordering to the box
|
||||
this._box.setAttribute("positionend", true);
|
||||
sidebarMain.setAttribute("positionend", true);
|
||||
@@ -546,6 +548,9 @@ var SidebarController = {
|
||||
@@ -603,6 +604,9 @@ var SidebarController = {
|
||||
this._box.removeAttribute("positionend");
|
||||
sidebarMain.removeAttribute("positionend");
|
||||
sidebarContainer.removeAttribute("positionend");
|
||||
|
@@ -1,13 +0,0 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tab-hover-preview.mjs b/browser/components/tabbrowser/content/tab-hover-preview.mjs
|
||||
index b07dba3e060dd730e603997b2c374f218a1eb591..412d35a263d1e6a789927206a506c184a5ac0d46 100644
|
||||
--- a/browser/components/tabbrowser/content/tab-hover-preview.mjs
|
||||
+++ b/browser/components/tabbrowser/content/tab-hover-preview.mjs
|
||||
@@ -7,7 +7,7 @@ var { XPCOMUtils } = ChromeUtils.importESModule(
|
||||
);
|
||||
|
||||
const POPUP_OPTIONS = {
|
||||
- position: "bottomleft topleft",
|
||||
+ position: "topright topleft",
|
||||
x: 0,
|
||||
y: -2,
|
||||
};
|
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e5741caf6 100644
|
||||
index 14de79b543cf07b04d06ef5a3f94d9aa988ea39a..af0f2a13e957331d48643fe4430408924c55b08c 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -456,11 +456,26 @@
|
||||
@@ -462,11 +462,26 @@
|
||||
return duplicateTabs;
|
||||
},
|
||||
|
||||
@@ -31,7 +31,34 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
}
|
||||
return i;
|
||||
},
|
||||
@@ -1340,6 +1355,7 @@
|
||||
@@ -860,7 +875,7 @@
|
||||
if (this.tabContainer.verticalMode) {
|
||||
let wasFocused = document.activeElement == this.selectedTab;
|
||||
let oldPosition = aTab._tPos;
|
||||
- this.verticalPinnedTabsContainer.appendChild(aTab);
|
||||
+ aTab.hasAttribute("zen-essential") ? document.getElementById("zen-essentials-container").appendChild(aTab) : this.verticalPinnedTabsContainer.appendChild(aTab);
|
||||
this._updateAfterMoveTabTo(aTab, oldPosition, wasFocused);
|
||||
} else {
|
||||
this.moveTabTo(aTab, this._numPinnedTabs);
|
||||
@@ -1107,6 +1122,7 @@
|
||||
|
||||
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
|
||||
|
||||
+ try {
|
||||
if (
|
||||
aIconURL &&
|
||||
!aLoadingPrincipal &&
|
||||
@@ -1117,6 +1133,9 @@
|
||||
);
|
||||
return;
|
||||
}
|
||||
+ } catch (e) {
|
||||
+ console.warn(e);
|
||||
+ }
|
||||
|
||||
let browser = this.getBrowserForTab(aTab);
|
||||
browser.mIconURL = aIconURL;
|
||||
@@ -1346,6 +1365,7 @@
|
||||
if (!this._previewMode) {
|
||||
newTab.recordTimeFromUnloadToReload();
|
||||
newTab.updateLastAccessed();
|
||||
@@ -39,7 +66,25 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
oldTab.updateLastAccessed();
|
||||
// if this is the foreground window, update the last-seen timestamps.
|
||||
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
|
||||
@@ -2705,6 +2721,11 @@
|
||||
@@ -2431,7 +2451,7 @@
|
||||
|
||||
let panel = this.getPanel(browser);
|
||||
let uniqueId = this._generateUniquePanelID();
|
||||
- panel.id = uniqueId;
|
||||
+ if (!panel.id?.startsWith("zen-")) panel.id = uniqueId;
|
||||
aTab.linkedPanel = uniqueId;
|
||||
|
||||
// Inject the <browser> into the DOM if necessary.
|
||||
@@ -2491,7 +2511,7 @@
|
||||
// hasSiblings=false on both the existing browser and the new browser.
|
||||
if (this.tabs.length == 2) {
|
||||
this.tabs[0].linkedBrowser.browsingContext.hasSiblings = true;
|
||||
- this.tabs[1].linkedBrowser.browsingContext.hasSiblings = true;
|
||||
+ if (this.tabs[1].linkedBrowser.browsingContext) this.tabs[1].linkedBrowser.browsingContext.hasSiblings = true;
|
||||
} else {
|
||||
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
|
||||
}
|
||||
@@ -2711,6 +2731,11 @@
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +96,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
if (!UserInteraction.running("browser.tabs.opening", window)) {
|
||||
UserInteraction.start("browser.tabs.opening", "initting", window);
|
||||
}
|
||||
@@ -2773,6 +2794,9 @@
|
||||
@@ -2780,6 +2805,9 @@
|
||||
noInitialLabel,
|
||||
skipBackgroundNotify,
|
||||
});
|
||||
@@ -61,7 +106,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
if (insertTab) {
|
||||
// insert the tab into the tab container in the correct position
|
||||
this._insertTabAtIndex(t, {
|
||||
@@ -3262,6 +3286,14 @@
|
||||
@@ -3291,6 +3319,23 @@
|
||||
) {
|
||||
tabWasReused = true;
|
||||
tab = this.selectedTab;
|
||||
@@ -69,14 +114,23 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
+ if (tabData.zenWorkspace) {
|
||||
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
|
||||
+ }
|
||||
+ if (tabData.zenPinnedId) {
|
||||
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
|
||||
+ }
|
||||
+ 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 (!tabData.pinned) {
|
||||
this.unpinTab(tab);
|
||||
} else {
|
||||
@@ -3275,6 +3307,9 @@
|
||||
@@ -3304,6 +3349,9 @@
|
||||
restoreTabsLazily && !select && !tabData.pinned;
|
||||
|
||||
let url = "about:blank";
|
||||
@@ -86,29 +140,37 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
if (tabData.entries?.length) {
|
||||
let activeIndex = (tabData.index || tabData.entries.length) - 1;
|
||||
// Ensure the index is in bounds.
|
||||
@@ -3311,6 +3346,12 @@
|
||||
@@ -3340,6 +3388,21 @@
|
||||
preferredRemoteType,
|
||||
});
|
||||
|
||||
+ if (tabData.zenWorkspace) {
|
||||
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
|
||||
+ }
|
||||
+ if (tabData.zenPinnedId) {
|
||||
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
|
||||
+ }
|
||||
+ 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;
|
||||
}
|
||||
@@ -3345,7 +3386,7 @@
|
||||
@@ -3374,7 +3437,6 @@
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
}
|
||||
}
|
||||
-
|
||||
+ gZenPinnedTabManager.updatePinnedTabForSessionRestore(tabData, tab);
|
||||
tab.initialize();
|
||||
}
|
||||
|
||||
@@ -4152,6 +4193,13 @@
|
||||
@@ -4148,6 +4210,13 @@
|
||||
TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
|
||||
}
|
||||
|
||||
@@ -122,15 +184,18 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate && aTab.closing) {
|
||||
@@ -4198,6 +4246,7 @@
|
||||
isLastTab ||
|
||||
aTab.pinned ||
|
||||
aTab.hidden ||
|
||||
+ true ||
|
||||
this._removingTabs.size >
|
||||
3 /* don't want lots of concurrent animations */ ||
|
||||
!aTab.hasAttribute(
|
||||
@@ -5131,10 +5180,10 @@
|
||||
@@ -4163,6 +4232,10 @@
|
||||
// state).
|
||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
|
||||
+ if (aTab.hasAttribute("zen-glance-tab")) {
|
||||
+ gZenGlanceManager.closeGlance();
|
||||
+ return;
|
||||
+ }
|
||||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -5123,10 +5196,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
},
|
||||
|
||||
@@ -143,7 +208,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -7050,6 +7099,7 @@
|
||||
@@ -7042,6 +7115,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
@@ -151,7 +216,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
|
||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
gBrowser.syncThrobberAnimations(this.mTab);
|
||||
@@ -7870,7 +7920,7 @@ var TabContextMenu = {
|
||||
@@ -7874,7 +7948,7 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !multiselectionContext;
|
||||
|
@@ -1,8 +1,17 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||
index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a85488137940 100644
|
||||
index f3a2f226a9056c5a75023281fdeb704cec49b4a6..39c46b141145864fb3b8043d2a9865299561a6cc 100644
|
||||
--- a/browser/components/tabbrowser/content/tabs.js
|
||||
+++ b/browser/components/tabbrowser/content/tabs.js
|
||||
@@ -840,7 +840,7 @@
|
||||
@@ -894,7 +894,7 @@
|
||||
let postTransitionCleanup = () => {
|
||||
tab.removeAttribute("tabdrop-samewindow");
|
||||
|
||||
- this._finishAnimateTabMove();
|
||||
+ this._finishAnimateTabMove(true);
|
||||
if (dropIndex !== false) {
|
||||
gBrowser.moveTabTo(tab, dropIndex);
|
||||
if (incrementDropIndex) {
|
||||
@@ -904,7 +904,7 @@
|
||||
|
||||
gBrowser.syncThrobberAnimations(tab);
|
||||
};
|
||||
@@ -11,7 +20,16 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
postTransitionCleanup();
|
||||
} else {
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -1411,7 +1411,7 @@
|
||||
@@ -1284,7 +1284,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- let allChildren = [...verticalPinnedTabsContainer.children, ...children];
|
||||
+ let allChildren = [...document.getElementById("zen-essentials-container").children, ...verticalPinnedTabsContainer.children, ...children];
|
||||
this._allTabs = allChildren;
|
||||
return allChildren;
|
||||
}
|
||||
@@ -1480,7 +1480,7 @@
|
||||
let rect = ele => {
|
||||
return window.windowUtils.getBoundsWithoutFlushing(ele);
|
||||
};
|
||||
@@ -20,11 +38,11 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
if (tab && rect(tab).width <= this._tabClipWidth) {
|
||||
this.setAttribute("closebuttons", "activetab");
|
||||
} else {
|
||||
@@ -1430,10 +1430,12 @@
|
||||
@@ -1499,10 +1499,12 @@
|
||||
|
||||
_handleTabSelect(aInstant) {
|
||||
let selectedTab = this.selectedItem;
|
||||
- if (this.hasAttribute("overflow")) {
|
||||
- if (this.overflowing) {
|
||||
- this.arrowScrollbox.ensureElementIsVisible(selectedTab, aInstant);
|
||||
- }
|
||||
+ this.arrowScrollbox.ensureElementIsVisible(selectedTab, aInstant);
|
||||
@@ -36,7 +54,7 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
selectedTab._notselectedsinceload = false;
|
||||
}
|
||||
|
||||
@@ -1477,7 +1479,7 @@
|
||||
@@ -1550,7 +1552,7 @@
|
||||
if (isEndTab && !this._hasTabTempMaxWidth) {
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +63,7 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
// Force tabs to stay the same width, unless we're closing the last tab,
|
||||
// which case we need to let them expand just enough so that the overall
|
||||
// tabbar width is the same.
|
||||
@@ -1492,7 +1494,7 @@
|
||||
@@ -1565,7 +1567,7 @@
|
||||
let tabsToReset = [];
|
||||
for (let i = numPinned; i < tabs.length; i++) {
|
||||
let tab = tabs[i];
|
||||
@@ -54,10 +72,10 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
if (!isEndTab) {
|
||||
// keep tabs the same width
|
||||
tab.style.transition = "none";
|
||||
@@ -1558,9 +1560,9 @@
|
||||
@@ -1630,13 +1632,13 @@
|
||||
let verticalTabsContainer = document.getElementById(
|
||||
"vertical-pinned-tabs-container"
|
||||
);
|
||||
let newTabButton = document.getElementById("newtab-button-container");
|
||||
- let numPinned = gBrowser._numPinnedTabs;
|
||||
+ let numPinned = gBrowser._numVisiblePinTabs;
|
||||
|
||||
@@ -66,25 +84,21 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
let tabs = this._getVisibleTabs();
|
||||
for (let i = 0; i < numPinned; i++) {
|
||||
tabs[i].style.marginInlineStart = "";
|
||||
@@ -1568,7 +1570,7 @@
|
||||
- verticalTabsContainer.appendChild(tabs[i]);
|
||||
+ tabs[i].hasAttribute("zen-essential") ? document.getElementById("zen-essentials-container").appendChild(tabs[i]) : verticalTabsContainer.appendChild(tabs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
- newTabButton.toggleAttribute("showborder", gBrowser._numPinnedTabs !== 0);
|
||||
+ newTabButton.toggleAttribute("showborder", gBrowser._numVisiblePinTabs !== 0);
|
||||
this.style.removeProperty("--tab-overflow-pinned-tabs-width");
|
||||
}
|
||||
|
||||
@@ -1589,7 +1591,7 @@
|
||||
@@ -1660,7 +1662,7 @@
|
||||
|
||||
_positionPinnedTabs() {
|
||||
let tabs = this._getVisibleTabs();
|
||||
- let numPinned = gBrowser._numPinnedTabs;
|
||||
+ let numPinned = gBrowser._numVisiblePinTabs;
|
||||
let absPositionHorizontalTabs =
|
||||
this.hasAttribute("overflow") &&
|
||||
tabs.length > numPinned &&
|
||||
@@ -1672,7 +1674,7 @@
|
||||
this.overflowing && tabs.length > numPinned && numPinned > 0;
|
||||
|
||||
@@ -1934,7 +1936,7 @@
|
||||
}
|
||||
|
||||
let pinned = draggedTab.pinned;
|
||||
@@ -93,7 +107,18 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
|
||||
let tabs = this._getVisibleTabs().slice(
|
||||
pinned ? 0 : numPinned,
|
||||
pinned ? numPinned : undefined
|
||||
@@ -1949,9 +1951,9 @@
|
||||
@@ -2059,8 +2061,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
- _finishAnimateTabMove() {
|
||||
- if (!this.hasAttribute("movingtab")) {
|
||||
+ _finishAnimateTabMove(always = false) {
|
||||
+ if (!this.hasAttribute("movingtab") && !always) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2218,9 +2220,9 @@
|
||||
function newIndex(aTab, index) {
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
if (aTab.pinned) {
|
||||
|
@@ -1,68 +0,0 @@
|
||||
diff --git a/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs b/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs
|
||||
index 9f4aa535e07adab496788165f4089be6732b1444..7b7955c0d1f2f3277cb750652458649d181e90e5 100644
|
||||
--- a/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs
|
||||
+++ b/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs
|
||||
@@ -39,6 +39,10 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
300
|
||||
);
|
||||
|
||||
+function zenSplitViewIsEnabled() {
|
||||
+ return Services.prefs.getBoolPref("zen.splitView.working", false);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* The tab switcher is responsible for asynchronously switching
|
||||
* tabs in e10s. It waits until the new tab is ready (i.e., the
|
||||
@@ -279,7 +283,7 @@ export class AsyncTabSwitcher {
|
||||
browser.docShellIsActive = true;
|
||||
}
|
||||
|
||||
- if (remoteTab) {
|
||||
+ if (remoteTab && !zenSplitViewIsEnabled()) {
|
||||
browser.renderLayers = true;
|
||||
remoteTab.priorityHint = true;
|
||||
}
|
||||
@@ -291,7 +295,7 @@ export class AsyncTabSwitcher {
|
||||
// Setting the docShell to be inactive will also cause it
|
||||
// to stop rendering layers.
|
||||
browser.docShellIsActive = false;
|
||||
- if (remoteTab) {
|
||||
+ if (remoteTab && !zenSplitViewIsEnabled()) {
|
||||
remoteTab.priorityHint = false;
|
||||
}
|
||||
if (!browser.hasLayers) {
|
||||
@@ -364,7 +368,7 @@ export class AsyncTabSwitcher {
|
||||
// constructing BrowserChild's, layer trees, etc, by showing a blank
|
||||
// tab instead and focusing it immediately.
|
||||
let shouldBeBlank = false;
|
||||
- if (requestedBrowser.isRemoteBrowser) {
|
||||
+ if (requestedBrowser.isRemoteBrowser && !zenSplitViewIsEnabled()) {
|
||||
// If a tab is remote and the window is not minimized, we can show a
|
||||
// blank tab instead of a spinner in the following cases:
|
||||
//
|
||||
@@ -399,7 +403,7 @@ export class AsyncTabSwitcher {
|
||||
}
|
||||
}
|
||||
|
||||
- if (requestedBrowser.isRemoteBrowser) {
|
||||
+ if (requestedBrowser.isRemoteBrowser && !zenSplitViewIsEnabled()) {
|
||||
this.addLogFlag("isRemote");
|
||||
}
|
||||
|
||||
@@ -825,7 +829,7 @@ export class AsyncTabSwitcher {
|
||||
`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`
|
||||
);
|
||||
if (!tab.linkedBrowser.isRemoteBrowser) {
|
||||
- if (this.getTabState(tab) == this.STATE_LOADING) {
|
||||
+ if (this.getTabState(tab) == this.STATE_LOADING && !zenSplitViewIsEnabled()) {
|
||||
this.onLayersReady(tab.linkedBrowser);
|
||||
} else if (this.getTabState(tab) == this.STATE_UNLOADING) {
|
||||
this.onLayersCleared(tab.linkedBrowser);
|
||||
@@ -1018,6 +1022,7 @@ export class AsyncTabSwitcher {
|
||||
lazy.gTabCacheSize > 1 &&
|
||||
tab.linkedBrowser.isRemoteBrowser &&
|
||||
tab.linkedBrowser.currentURI.spec != "about:blank"
|
||||
+ && !zenSplitViewIsEnabled()
|
||||
) {
|
||||
let tabIndex = this.tabLayerCache.indexOf(tab);
|
||||
|
@@ -1,21 +1,8 @@
|
||||
diff --git a/browser/themes/BuiltInThemeConfig.sys.mjs b/browser/themes/BuiltInThemeConfig.sys.mjs
|
||||
index 7cfea4b705a1338b1eb5c4f255808aeac4bdb819..cf7dc720a3bd2b14535d57262ad2297f9478eddc 100644
|
||||
index f738f7f1e37d20d9d9158f052572334714475107..f4b0e9a7077076f2fbe8ac17fa6aadf586c6b37c 100644
|
||||
--- a/browser/themes/BuiltInThemeConfig.sys.mjs
|
||||
+++ b/browser/themes/BuiltInThemeConfig.sys.mjs
|
||||
@@ -22,24 +22,17 @@ export const BuiltInThemeConfig = new Map([
|
||||
[
|
||||
"firefox-compact-light@mozilla.org",
|
||||
{
|
||||
- version: "1.2",
|
||||
+ version: "1.3",
|
||||
path: "resource://builtin-themes/light/",
|
||||
},
|
||||
],
|
||||
[
|
||||
"firefox-compact-dark@mozilla.org",
|
||||
{
|
||||
- version: "1.2",
|
||||
+ version: "1.3",
|
||||
@@ -33,13 +33,6 @@ export const BuiltInThemeConfig = new Map([
|
||||
path: "resource://builtin-themes/dark/",
|
||||
},
|
||||
],
|
||||
|
@@ -11,15 +11,6 @@ index c2d5dd7a18895ae8b4afbf386f122e7899c48cda..377d904bbf5adc92bf7cb0aa4c4c6417
|
||||
);
|
||||
let activeBuiltInTheme = this.builtInThemeMap.get(activeThemeID);
|
||||
|
||||
@@ -82,7 +82,7 @@ class _BuiltInThemes {
|
||||
lazy.AddonManager.maybeInstallBuiltinAddon(
|
||||
activeThemeID,
|
||||
activeBuiltInTheme.version,
|
||||
- `resource://builtin-themes/${activeBuiltInTheme.path}`
|
||||
+ `${activeBuiltInTheme.path}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ class _BuiltInThemes {
|
||||
async _uninstallExpiredThemes() {
|
||||
const activeThemeID = Services.prefs.getStringPref(
|
||||
|
@@ -10,9 +10,8 @@ index bfebde683d00b0acf26509139a0f662029d37c72..976b553f49e40b7ed02743d79ccc102f
|
||||
+ "name": "Zen Dark",
|
||||
"description": "A theme with a dark color scheme.",
|
||||
- "author": "Mozilla",
|
||||
- "version": "1.2",
|
||||
+ "author": "Zen",
|
||||
+ "version": "1.3",
|
||||
"version": "1.3.2",
|
||||
|
||||
"icons": { "32": "icon.svg" },
|
||||
|
||||
|
@@ -10,9 +10,8 @@ index d490f8f08d203ded55b65fe1a19be105b61ee6c0..eb4349e939b979b3a4d7c525ee0c0f9a
|
||||
+ "name": "Zen Light",
|
||||
"description": "A theme with a light color scheme.",
|
||||
- "author": "Mozilla",
|
||||
- "version": "1.2",
|
||||
+ "author": "Zen",
|
||||
+ "version": "1.3",
|
||||
"version": "1.3",
|
||||
|
||||
"icons": { "32": "icon.svg" },
|
||||
|
||||
|
@@ -1,31 +0,0 @@
|
||||
diff --git a/browser/themes/shared/browser-custom-colors.css b/browser/themes/shared/browser-custom-colors.css
|
||||
index 23187312740d440b3cd4b376a06cb2ab2e885604..ac9aafc0ceed04364ae5b468cf7d2b2843aa7fe5 100644
|
||||
--- a/browser/themes/shared/browser-custom-colors.css
|
||||
+++ b/browser/themes/shared/browser-custom-colors.css
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
@media not (prefers-contrast) {
|
||||
:root:not([lwtheme]) {
|
||||
- --button-primary-bgcolor: light-dark(rgb(0, 97, 224), rgb(0, 221, 255));
|
||||
+ --button-primary-bgcolor: light-dark(#000, #fff);
|
||||
--button-primary-hover-bgcolor: light-dark(rgb(2, 80, 187), rgb(128, 235, 255));
|
||||
--button-primary-active-bgcolor: light-dark(rgb(5, 62, 148), rgb(170, 242, 255));
|
||||
--button-primary-color: light-dark(rgb(251, 251, 254), rgb(43, 42, 51));
|
||||
@@ -15,7 +15,7 @@
|
||||
rgba(0, 0, 0, .33)
|
||||
);
|
||||
--button-hover-bgcolor: light-dark(
|
||||
- rgba(207, 207, 216, .66),
|
||||
+ rgba(228, 228, 235, 0.66),
|
||||
rgba(207, 207, 216, .20)
|
||||
);
|
||||
--button-active-bgcolor: light-dark(
|
||||
@@ -49,7 +49,7 @@
|
||||
--tab-icon-overlay-fill: light-dark(rgb(91, 91, 102), rgb(251, 251, 254));
|
||||
--tabs-navbar-separator-style: none;
|
||||
|
||||
- --toolbox-non-lwt-bgcolor: light-dark(rgb(240, 240, 244), rgb(28, 27, 34));
|
||||
+ --toolbox-non-lwt-bgcolor: light-dark(rgb(210, 210, 213), rgb(28, 27, 34));
|
||||
--toolbox-non-lwt-textcolor: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
|
||||
--toolbox-non-lwt-bgcolor-inactive: var(--toolbox-non-lwt-bgcolor);
|
||||
--toolbox-non-lwt-textcolor-inactive: var(--toolbox-non-lwt-textcolor);
|
@@ -2,14 +2,6 @@ diff --git a/browser/themes/shared/browser-shared.css b/browser/themes/shared/br
|
||||
index f2171eb033a1143870f4708c63f565fabb535c4b..4280bc4b0f7cdbd94179fa2111f8001a331ea42b 100644
|
||||
--- a/browser/themes/shared/browser-shared.css
|
||||
+++ b/browser/themes/shared/browser-shared.css
|
||||
@@ -179,7 +179,6 @@ body {
|
||||
appearance: none;
|
||||
|
||||
/* Toolbar / content area border */
|
||||
- border-bottom: 0.01px solid var(--chrome-content-separator-color);
|
||||
|
||||
background-color: var(--toolbox-non-lwt-bgcolor);
|
||||
color: var(--toolbox-non-lwt-textcolor);
|
||||
@@ -188,7 +187,6 @@ body {
|
||||
transition: background-color var(--inactive-window-transition);
|
||||
|
||||
|
@@ -3,9 +3,9 @@ index 9f7331f2f00a8b0de6ce28a7029358a86eeb8873..2dc56a8455df9009bd1f524b377b8fdf
|
||||
--- a/browser/themes/shared/tabbrowser/tabs.css
|
||||
+++ b/browser/themes/shared/tabbrowser/tabs.css
|
||||
@@ -15,7 +15,7 @@
|
||||
--tab-border-radius: 4px;
|
||||
--tab-shadow-max-size: 6px;
|
||||
--tab-block-margin: 4px;
|
||||
--tab-icon-end-margin: 5.5px;
|
||||
--tab-icon-overlay-fill: light-dark(black, white);
|
||||
--tab-icon-overlay-stroke: light-dark(white, black);
|
||||
- --tab-loading-fill: #0A84FF;
|
||||
+ --tab-loading-fill: var(--zen-primary-color);
|
||||
--tab-hover-background-color: color-mix(in srgb, currentColor 11%, transparent);
|
||||
@@ -24,8 +24,8 @@ index 9f7331f2f00a8b0de6ce28a7029358a86eeb8873..2dc56a8455df9009bd1f524b377b8fdf
|
||||
@@ -952,7 +951,7 @@ sidebar-main[expanded] > #vertical-tabs > #tabbrowser-tabs[orient="vertical"] >
|
||||
toolbarbutton:not(#firefox-view-button),
|
||||
toolbarpaletteitem:not(#wrapper-firefox-view-button)
|
||||
) + #tabbrowser-tabs {
|
||||
- border-inline-start: 1px solid color-mix(in srgb, currentColor 25%, transparent);
|
||||
) ~ #tabbrowser-tabs {
|
||||
- border-inline-start: var(--tabstrip-inner-border);
|
||||
+ border-inline-start: transparent;
|
||||
padding-inline-start: calc(var(--tab-overflow-pinned-tabs-width) + 2px);
|
||||
margin-inline-start: 2px;
|
||||
@@ -35,6 +35,6 @@ index 9f7331f2f00a8b0de6ce28a7029358a86eeb8873..2dc56a8455df9009bd1f524b377b8fdf
|
||||
}
|
||||
|
||||
-#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow]) ~ #new-tab-button,
|
||||
#tabbrowser-tabs[orient="vertical"] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
|
||||
#tabbrowser-tabs[overflow] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
|
||||
#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
|
||||
#tabbrowser-tabs[orient="horizontal"] > #vertical-tabs-newtab-button,
|
||||
#tabbrowser-tabs[orient="vertical"]:not([overflow]) > #vertical-tabs-newtab-button,
|
||||
#tabbrowser-arrowscrollbox[overflowing] > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
|
||||
|
1
src/browser/themes/shared/zen-icons/essential-add.svg
Normal file
1
src/browser/themes/shared/zen-icons/essential-add.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 122.88 109.57" fill="context-fill" fill-opacity="context-fill-opacity" xml:space="preserve"><g><path d="M65.46,19.57c-0.68,0.72-1.36,1.45-2.2,2.32l-2.31,2.41l-2.4-2.33c-0.71-0.69-1.43-1.4-2.13-2.09 c-7.42-7.3-13.01-12.8-24.52-12.95c-0.45-0.01-0.93,0-1.43,0.02c-6.44,0.23-12.38,2.6-16.72,6.65c-4.28,4-7.01,9.67-7.1,16.57 c-0.01,0.43,0,0.88,0.02,1.37c0.69,19.27,19.13,36.08,34.42,50.01c2.95,2.69,5.78,5.27,8.49,7.88l11.26,10.85l14.15-14.04 c2.28-2.26,4.86-4.73,7.62-7.37c4.69-4.5,9.91-9.49,14.77-14.52c3.49-3.61,6.8-7.24,9.61-10.73c2.76-3.42,5.02-6.67,6.47-9.57 c2.38-4.76,3.13-9.52,2.62-13.97c-0.5-4.39-2.23-8.49-4.82-11.99c-2.63-3.55-6.13-6.49-10.14-8.5C96.5,7.29,91.21,6.2,85.8,6.82 C76.47,7.9,71.5,13.17,65.46,19.57L65.46,19.57z M60.77,14.85C67.67,7.54,73.4,1.55,85.04,0.22c6.72-0.77,13.3,0.57,19.03,3.45 c4.95,2.48,9.27,6.1,12.51,10.47c3.27,4.42,5.46,9.61,6.1,15.19c0.65,5.66-0.29,11.69-3.3,17.69c-1.7,3.39-4.22,7.03-7.23,10.76 c-2.95,3.66-6.39,7.44-10,11.17C97.2,74.08,91.94,79.12,87.2,83.66c-2.77,2.65-5.36,5.13-7.54,7.29L63.2,107.28l-2.31,2.29 l-2.34-2.25l-13.6-13.1c-2.49-2.39-5.37-5.02-8.36-7.75C20.38,71.68,0.81,53.85,0.02,31.77C0,31.23,0,30.67,0,30.09 c0.12-8.86,3.66-16.18,9.21-21.36c5.5-5.13,12.97-8.13,21.01-8.42c0.55-0.02,1.13-0.03,1.74-0.02C46,0.48,52.42,6.63,60.77,14.85 L60.77,14.85z"/></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
src/browser/themes/shared/zen-icons/essential-remove.svg
Normal file
1
src/browser/themes/shared/zen-icons/essential-remove.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 109.57" fill="context-fill" fill-opacity="context-fill-opacity"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><path class="cls-1" d="M94.26,44.46A26.88,26.88,0,1,1,67.38,71.34,26.88,26.88,0,0,1,94.26,44.46ZM65.45,19.57l-4.52,4.74-4.52-4.43C49,12.58,43.4,7.08,31.89,6.93L30.46,7A25.75,25.75,0,0,0,13.74,13.6,22.58,22.58,0,0,0,6.63,30.17l0,1.37c.69,19.27,19.13,36.08,34.42,50,3,2.69,5.78,5.27,8.49,7.88l11.27,10.85,5.91-5.87c.68.8,1.39,1.58,2.13,2.32l0,0c.82.83,1.69,1.61,2.59,2.36l-10.59,10.5L45,94.22c-2.49-2.39-5.37-5-8.37-7.75C20.37,71.68.81,53.85,0,31.77l0-1.68A29.12,29.12,0,0,1,9.21,8.73a32.42,32.42,0,0,1,21-8.42H32c14,.18,20.45,6.34,28.8,14.55C67.66,7.54,73.39,1.55,85,.21a33.88,33.88,0,0,1,19,3.45,35.14,35.14,0,0,1,12.51,10.48,31.53,31.53,0,0,1,6.1,15.18,31,31,0,0,1-2.86,16.81l-.17-.18a36.18,36.18,0,0,0-5.14-4.27,24.26,24.26,0,0,0,1.57-11.6,25,25,0,0,0-4.83-12,28.62,28.62,0,0,0-10.14-8.5A27.27,27.27,0,0,0,85.79,6.82C76.47,7.89,71.5,13.17,65.45,19.57ZM106.15,68a4,4,0,0,1,1.51.3,3.94,3.94,0,0,1,1.29.86,4,4,0,0,1,.85,1.28l0,.06a4,4,0,0,1,.28,1.38V72a3.82,3.82,0,0,1-.3,1.44,4,4,0,0,1-.86,1.29,3.88,3.88,0,0,1-1.27.85l-.07,0a4.23,4.23,0,0,1-1.43.28H82.37a3.85,3.85,0,0,1-1.51-.31l-.06,0a3.92,3.92,0,0,1-1.23-.83,3.87,3.87,0,0,1-.85-1.28l0-.07A4.23,4.23,0,0,1,78.41,72v0a3.8,3.8,0,0,1,.3-1.51A3.95,3.95,0,0,1,82.34,68Z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@@ -44,6 +44,7 @@
|
||||
#stop-button,
|
||||
.close-icon,
|
||||
#zen-sidebar-web-panel-close,
|
||||
#zen-glance-sidebar-close,
|
||||
.zen-theme-picker-custom-list-item-remove {
|
||||
list-style-image: url('close.svg') !important;
|
||||
}
|
||||
@@ -263,6 +264,7 @@
|
||||
|
||||
#restore-button,
|
||||
#fullscreen-button,
|
||||
#zen-glance-sidebar-open,
|
||||
#appMenu-fullscreen-button2 {
|
||||
list-style-image: url('fullscreen.svg') !important;
|
||||
}
|
||||
@@ -891,10 +893,18 @@ menuitem[contexttype='fullscreen'][label*='Exit'] {
|
||||
#context_pinSelectedTabs,
|
||||
#context_unpinSelectedTabs,
|
||||
.customize-context-moveToPanel,
|
||||
#context_zen-replace-pinned-url-with-current {
|
||||
#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');
|
||||
}
|
||||
|
@@ -36,6 +36,8 @@
|
||||
skin/classic/browser/zen-icons/edit-select-all.svg (../shared/zen-icons/edit-select-all.svg)
|
||||
skin/classic/browser/zen-icons/edit-undo.svg (../shared/zen-icons/edit-undo.svg)
|
||||
skin/classic/browser/zen-icons/edit.svg (../shared/zen-icons/edit.svg)
|
||||
skin/classic/browser/zen-icons/essential-add.svg (../shared/zen-icons/essential-add.svg)
|
||||
skin/classic/browser/zen-icons/essential-remove.svg (../shared/zen-icons/essential-remove.svg)
|
||||
skin/classic/browser/zen-icons/ext-link.svg (../shared/zen-icons/ext-link.svg)
|
||||
skin/classic/browser/zen-icons/extension-blocked.svg (../shared/zen-icons/extension-blocked.svg)
|
||||
skin/classic/browser/zen-icons/extension.svg (../shared/zen-icons/extension.svg)
|
||||
|
@@ -1,13 +0,0 @@
|
||||
diff --git a/dom/ipc/BrowserHost.cpp b/dom/ipc/BrowserHost.cpp
|
||||
index 489f07a612e52a3edb00f59e53495dcd9e16c0f5..1799e0d416f9d7be2c9e34c237d6ff741e5e2420 100644
|
||||
--- a/dom/ipc/BrowserHost.cpp
|
||||
+++ b/dom/ipc/BrowserHost.cpp
|
||||
@@ -106,7 +106,7 @@ void BrowserHost::UpdateEffects(EffectsInfo aEffects) {
|
||||
/* attribute boolean renderLayers; */
|
||||
NS_IMETHODIMP
|
||||
BrowserHost::GetRenderLayers(bool* aRenderLayers) {
|
||||
- if (!mRoot) {
|
||||
+ if (!mRoot && !Preferences::GetBool("zen.splitView.working", false)) {
|
||||
*aRenderLayers = false;
|
||||
return NS_OK;
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
diff --git a/toolkit/modules/ActorManagerParent.sys.mjs b/toolkit/modules/ActorManagerParent.sys.mjs
|
||||
index 71409e58271802fc506999712519df63eff3b1a6..684c9da0139db1dc0962fbb50304ca4f013950ca 100644
|
||||
--- a/toolkit/modules/ActorManagerParent.sys.mjs
|
||||
+++ b/toolkit/modules/ActorManagerParent.sys.mjs
|
||||
@@ -666,6 +666,20 @@ if (!Services.prefs.getBoolPref("browser.pagedata.enabled", false)) {
|
||||
};
|
||||
}
|
||||
|
||||
+JSWINDOWACTORS.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: ["https://*.zen-browser.app/*", "about:preferences"],
|
||||
+ allFrames: true,
|
||||
+};
|
||||
+
|
||||
if (AppConstants.platform != "android") {
|
||||
// Note that GeckoView has another implementation in mobile/android/actors.
|
||||
JSWINDOWACTORS.Select = {
|
@@ -1,5 +1,5 @@
|
||||
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
|
||||
index 011eee093dcf77c3c8ad3cd20188b61457d8dc09..d6ffe47cf4d1dfe7a49d7e5f5d84c162b8589cc7 100644
|
||||
index 06281a55f43ddff4f4d4a09ba05e9a830a221415..269728dccdd8a286b563a5d20da5280b7b3b1639 100644
|
||||
--- a/toolkit/moz.configure
|
||||
+++ b/toolkit/moz.configure
|
||||
@@ -81,6 +81,9 @@ option(
|
||||
@@ -25,3 +25,12 @@ index 011eee093dcf77c3c8ad3cd20188b61457d8dc09..d6ffe47cf4d1dfe7a49d7e5f5d84c162
|
||||
return True
|
||||
|
||||
|
||||
@@ -2036,7 +2039,7 @@ set_define("A11Y_LOG", True, when=a11y_log)
|
||||
# ==============================================================
|
||||
@depends(milestone)
|
||||
def require_signing(milestone):
|
||||
- return milestone.is_release_or_beta and not milestone.is_esr
|
||||
+ return False
|
||||
|
||||
|
||||
option(
|
||||
|
@@ -1,13 +1,15 @@
|
||||
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
|
||||
index 12d4fa1172f4f19df63521fac2bc6f065ae455df..981e3c2e22e59447343cd4b35411041656b2b7ed 100644
|
||||
index 6aeb6a6dc7fd46eefac6a0cd56fadaee11efc6c0..abbd17afaadfadb3f97a0553764ac35aab67116c 100644
|
||||
--- a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
|
||||
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
|
||||
@@ -2546,7 +2546,7 @@ export var XPIProvider = {
|
||||
);
|
||||
@@ -2535,8 +2535,8 @@ export var XPIProvider = {
|
||||
}
|
||||
// Keep version in sync with toolkit/mozapps/extensions/default-theme/manifest.json
|
||||
this.maybeInstallBuiltinAddon(
|
||||
- "default-theme@mozilla.org",
|
||||
- "1.4.1",
|
||||
+ "firefox-compact-dark@mozilla.org",
|
||||
"1.3",
|
||||
+ "1.3",
|
||||
"resource://default-theme/"
|
||||
);
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
diff --git a/toolkit/profile/nsToolkitProfileService.cpp b/toolkit/profile/nsToolkitProfileService.cpp
|
||||
index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc56339484 100644
|
||||
index 0587b214ddcf4b12877ca3698ca7d4308b2f0af3..3846515a4fa7b849233de6026f55152478c04f03 100644
|
||||
--- a/toolkit/profile/nsToolkitProfileService.cpp
|
||||
+++ b/toolkit/profile/nsToolkitProfileService.cpp
|
||||
@@ -234,7 +234,7 @@ void RemoveProfileFiles(nsIToolkitProfile* aProfile, bool aInBackground) {
|
||||
@@ -229,7 +229,7 @@ void RemoveProfileFiles(nsIToolkitProfile* aProfile, bool aInBackground) {
|
||||
}
|
||||
|
||||
nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
@@ -11,7 +11,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
const nsACString& aStoreID = VoidCString(),
|
||||
bool aShowProfileSelector = false)
|
||||
: mName(aName),
|
||||
@@ -244,7 +244,8 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
@@ -239,7 +239,8 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
mShowProfileSelector(aShowProfileSelector),
|
||||
mLock(nullptr),
|
||||
mIndex(0),
|
||||
@@ -21,7 +21,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
NS_ASSERTION(aRootDir, "No file!");
|
||||
|
||||
RefPtr<nsToolkitProfile> prev =
|
||||
@@ -259,6 +260,10 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
@@ -254,6 +255,10 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
// If this profile isn't in the database already add it.
|
||||
if (!aFromDB) {
|
||||
nsINIParser* db = &nsToolkitProfileService::gService->mProfileDB;
|
||||
@@ -32,7 +32,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
db->SetString(mSection.get(), "Name", mName.get());
|
||||
|
||||
bool isRelative = false;
|
||||
@@ -268,6 +273,7 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
@@ -263,6 +268,7 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
|
||||
db->SetString(mSection.get(), "IsRelative", isRelative ? "1" : "0");
|
||||
db->SetString(mSection.get(), "Path", descriptor.get());
|
||||
@@ -40,7 +40,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
if (!mStoreID.IsVoid()) {
|
||||
db->SetString(mSection.get(), "StoreID",
|
||||
PromiseFlatCString(mStoreID).get());
|
||||
@@ -466,6 +472,8 @@ nsToolkitProfile::SetShowProfileSelector(bool aShowProfileSelector) {
|
||||
@@ -462,6 +468,8 @@ nsToolkitProfile::SetShowProfileSelector(bool aShowProfileSelector) {
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -49,14 +49,13 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
|
||||
bool aInBackground) {
|
||||
NS_ASSERTION(nsToolkitProfileService::gService, "Whoa, my service is gone.");
|
||||
@@ -1158,8 +1166,15 @@ nsresult nsToolkitProfileService::Init() {
|
||||
@@ -1145,8 +1153,14 @@ nsresult nsToolkitProfileService::Init() {
|
||||
}
|
||||
}
|
||||
|
||||
- currentProfile = new nsToolkitProfile(name, rootDir, localDir, true,
|
||||
- storeID, showProfileSelector);
|
||||
+ nsAutoCString zenProfileAvatar;
|
||||
+
|
||||
+ rv = mProfileDB.GetString(profileID.get(), "ZenAvatarPath", zenProfileAvatar);
|
||||
+ if (NS_FAILED(rv)) {
|
||||
+ NS_ERROR("Malformed profiles.ini: ZenAvatarPath= not found");
|
||||
@@ -67,7 +66,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
|
||||
// If a user has modified the ini file path it may make for a valid profile
|
||||
// path but not match what we would have serialised and so may not match
|
||||
@@ -1384,7 +1399,7 @@ nsresult nsToolkitProfileService::CreateDefaultProfile(
|
||||
@@ -1375,7 +1389,7 @@ nsresult nsToolkitProfileService::CreateDefaultProfile(
|
||||
if (mUseDevEditionProfile) {
|
||||
name.AssignLiteral(DEV_EDITION_NAME);
|
||||
} else if (mUseDedicatedProfile) {
|
||||
@@ -76,12 +75,12 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
|
||||
} else {
|
||||
name.AssignLiteral(DEFAULT_NAME);
|
||||
}
|
||||
@@ -2180,7 +2195,7 @@ nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
|
||||
@@ -2185,7 +2199,7 @@ nsresult nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
|
||||
rv = CreateTimesInternal(rootDir);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIToolkitProfile> profile =
|
||||
- new nsToolkitProfile(aName, rootDir, localDir, false);
|
||||
+ new nsToolkitProfile(aName, rootDir, localDir, false, ""_ns);
|
||||
- profile = new nsToolkitProfile(aName, rootDir, localDir, false);
|
||||
+ profile = new nsToolkitProfile(aName, rootDir, localDir, false, ""_ns);
|
||||
|
||||
if (aName.Equals(DEV_EDITION_NAME)) {
|
||||
mDevEditionDefault = profile;
|
||||
|
@@ -1,13 +1,12 @@
|
||||
diff --git a/toolkit/themes/linux/global/menu.css b/toolkit/themes/linux/global/menu.css
|
||||
index da47f78790a251bb573d992476dc5dac9a870857..c7d019df2164b95fca29ec5a72e7fe4188d1e1e6 100644
|
||||
index da47f78790a251bb573d992476dc5dac9a870857..6c7d6cbdaf3f0bd35a52c80785577a6bc0d55e29 100644
|
||||
--- a/toolkit/themes/linux/global/menu.css
|
||||
+++ b/toolkit/themes/linux/global/menu.css
|
||||
@@ -52,7 +52,7 @@ menulist > menupopup > :is(menuitem, menucaption, menu) {
|
||||
@@ -52,7 +52,6 @@ menulist > menupopup > :is(menuitem, menucaption, menu) {
|
||||
|
||||
.menu-text {
|
||||
/* This is (18 + the size of end-padding on .menu-iconic-left)px */
|
||||
- margin-inline-start: 21px;
|
||||
+ margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.menu-accel,
|
||||
|
@@ -1,19 +1,14 @@
|
||||
diff --git a/toolkit/themes/shared/menulist-shared.css b/toolkit/themes/shared/menulist-shared.css
|
||||
index fa7e68c7b71b28c6dc1b2f67a9868f9ad4090034..9d082381e2d491d63647f8047a748354918686fe 100644
|
||||
index 5dd5549674570170a694afbd9ea4526e52e3192a..187fcb57183df6a0ab3701ab79c46d86c5e984b3 100644
|
||||
--- a/toolkit/themes/shared/menulist-shared.css
|
||||
+++ b/toolkit/themes/shared/menulist-shared.css
|
||||
@@ -14,12 +14,13 @@
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
:host(:not([native])) {
|
||||
appearance: none;
|
||||
- background-color: var(--button-bgcolor, ButtonFace);
|
||||
- background-color: var(--button-background-color);
|
||||
+ background-color: var(--zen-colors-tertiary, ButtonFace);
|
||||
color: var(--button-color, ButtonText);
|
||||
+ border: 1px solid var(--input-border-color, ThreeDShadow);
|
||||
color: var(--button-text-color);
|
||||
border-radius: 4px;
|
||||
padding-block: 4px;
|
||||
padding-inline: 12px 8px;
|
||||
margin: 5px 2px 3px;
|
||||
+ border: 1px solid var(--input-border-color, ThreeDShadow);
|
||||
}
|
||||
|
||||
:host(:not([native])[size="medium"]) {
|
||||
|
@@ -1,20 +0,0 @@
|
||||
diff --git a/widget/ThemeColors.h b/widget/ThemeColors.h
|
||||
index 739a7ca0f0ff4d1647c5c204b49ba7cc157c76fd..86a89bc890ec5ef824f0a0295876fddb9e363c7c 100644
|
||||
--- a/widget/ThemeColors.h
|
||||
+++ b/widget/ThemeColors.h
|
||||
@@ -13,8 +13,14 @@
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
||||
+#ifndef ZEN_DEFAULT_ACCENT_COLOR
|
||||
+// Like a normal hex code:
|
||||
+// 0xRRGGBBAA
|
||||
+#define ZEN_DEFAULT_ACCENT_COLOR 0x000000df
|
||||
+#endif
|
||||
+
|
||||
static constexpr gfx::sRGBColor sDefaultAccent(
|
||||
- gfx::sRGBColor::UnusualFromARGB(0xff0060df)); // Luminance: 13.69346%
|
||||
+ gfx::sRGBColor::UnusualFromARGB(ZEN_DEFAULT_ACCENT_COLOR)); // Luminance: 13.69346%
|
||||
static constexpr gfx::sRGBColor sDefaultAccentText(
|
||||
gfx::sRGBColor::OpaqueWhite());
|
||||
|
13
src/widget/cocoa/VibrancyManager-mm.patch
Normal file
13
src/widget/cocoa/VibrancyManager-mm.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm
|
||||
index e8263ee480a0249cae760ee5b9037bb87acafecc..aefc887208659e46191eebfde1f9f74f1eac77a5 100644
|
||||
--- a/widget/cocoa/VibrancyManager.mm
|
||||
+++ b/widget/cocoa/VibrancyManager.mm
|
||||
@@ -39,7 +39,7 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType(
|
||||
case VibrancyType::Sidebar:
|
||||
return NSVisualEffectMaterialSidebar;
|
||||
case VibrancyType::Titlebar:
|
||||
- return NSVisualEffectMaterialTitlebar;
|
||||
+ return NSVisualEffectMaterialUnderWindowBackground;
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
"binaryName": "zen",
|
||||
"version": {
|
||||
"product": "firefox",
|
||||
"version": "131.0.3"
|
||||
"version": "132.0.1"
|
||||
},
|
||||
"buildOptions": {
|
||||
"generateBranding": true
|
||||
@@ -18,7 +18,7 @@
|
||||
"brandShortName": "Zen Browser",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.0.1-a.14",
|
||||
"displayVersion": "1.0.1-a.18",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
@@ -38,7 +38,7 @@
|
||||
"brandShortName": "Zen Twilight",
|
||||
"brandFullName": "Zen Twilight",
|
||||
"release": {
|
||||
"displayVersion": "1.0.1-t.14",
|
||||
"displayVersion": "1.0.1-t.18",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
Reference in New Issue
Block a user