Compare commits
44 Commits
zen-librar
...
1.12.9b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e3faf45b1 | ||
|
|
8b25b0bbf3 | ||
|
|
dad9475005 | ||
|
|
00d3ba11d2 | ||
|
|
c7333caf7d | ||
|
|
0630b65713 | ||
|
|
69f9b05cbc | ||
|
|
74f6d1a85e | ||
|
|
e0bf7d011b | ||
|
|
12011c7208 | ||
|
|
280f0ca27f | ||
|
|
6ec0af3182 | ||
|
|
e99c40bc06 | ||
|
|
e181381bd3 | ||
|
|
6fa0e814de | ||
|
|
68191d2b47 | ||
|
|
81f7587958 | ||
|
|
d5e2acfd5b | ||
|
|
663243264b | ||
|
|
03ca00748c | ||
|
|
cbb1a4bc44 | ||
|
|
21f3ab23d3 | ||
|
|
18944d5ed8 | ||
|
|
5c6e5f7361 | ||
|
|
bd72aebd98 | ||
|
|
0278aea4f7 | ||
|
|
3c01004641 | ||
|
|
b8213569e5 | ||
|
|
4d27f9d741 | ||
|
|
3b56abf090 | ||
|
|
590ba6de1b | ||
|
|
4aa215e091 | ||
|
|
de175bff11 | ||
|
|
a6bc8d7105 | ||
|
|
e48e7caef1 | ||
|
|
015cdad2df | ||
|
|
ef6cf5fae1 | ||
|
|
797152da89 | ||
|
|
316ff45859 | ||
|
|
e0ac9ba424 | ||
|
|
09ca430b88 | ||
|
|
dda1dab6f3 | ||
|
|
fbf411c096 | ||
|
|
4b0c6f2ca5 |
105
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
105
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -4,35 +4,76 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for filing a bug report!
|
||||
<p align="center">
|
||||
<a href="https://zen-browser.app">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/zen-browser/desktop/dev/docs/assets/zen-light.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/zen-browser/desktop/dev/docs/assets/zen-dark.svg">
|
||||
<img src="https://raw.githubusercontent.com/zen-browser/desktop/dev/docs/assets/zen-light.svg" alt="Zen Logo" width="64" height="64">
|
||||
</picture>
|
||||
<img src="https://raw.githubusercontent.com/zen-browser/desktop/dev/docs/assets/zen-browser.svg" alt="Zen Browser Text" width="156" height="64">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## IMPORTANT READ
|
||||
- Please provide descriptive titles. Bad titles do more harm than good.
|
||||
- Please search existing issues to avoid creating duplicates.
|
||||
- For enhancement requests, please use GitHub Discussions.
|
||||
- Please fill out the template below to the best of your ability.
|
||||
- Please describe the issue as much as possible and provide steps to reproduce it along with visual aids if possible.
|
||||
<p align="center"><em>Thank you for filing a bug report!</em></p>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> - Please provide descriptive titles. Bad titles do more harm than good.
|
||||
> - Please search existing issues to avoid creating duplicates.
|
||||
> - For enhancement requests, please use GitHub Discussions.
|
||||
> - Please fill out the template below to the best of your ability.
|
||||
> - Please describe the issue as much as possible and provide steps to reproduce it along with visual aids if possible.
|
||||
- type: checkboxes
|
||||
id: captchas
|
||||
attributes:
|
||||
label: Captchas
|
||||
description: Just making sure you did actually read the instructions.
|
||||
label: Preliminary Checks
|
||||
description: Please confirm the following before submitting a bug report. This helps us triage and resolve issues more efficiently.
|
||||
options:
|
||||
- label: I have read the instructions.
|
||||
- label: I have read and understood the important section above.
|
||||
required: true
|
||||
- label: I have searched existing issues and avoided creating duplicates.
|
||||
required: true
|
||||
- label: I am not filing an enhancement request.
|
||||
required: true
|
||||
- label: I have checked that this issue cannot be reproduced on Mozilla Firefox.
|
||||
required: true
|
||||
- label: I have checked that this issue can be reproduced once I removed all my Mods and Custom CSS.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
description: Describe the bug clearly and concisely.
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Please write a description of what was supposed to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: actual-behavior
|
||||
attributes:
|
||||
label: Actual behavior
|
||||
description: Please write a description of what actually happened.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please Provide numbered steps to reproduce this issue so developers can replicate them easily.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots and videos
|
||||
description: |
|
||||
If applicable, add screenshots or videos to help explain your problem.
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
@@ -52,8 +93,8 @@ body:
|
||||
- Linux (Tarball)
|
||||
- macOS - aarch64
|
||||
- macOS - Intel
|
||||
- Windows - x64
|
||||
- Windows - aarch64
|
||||
- Windows - x64
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
@@ -63,26 +104,26 @@ body:
|
||||
label: What component is this issue related to?
|
||||
options:
|
||||
- Other
|
||||
- Compact Mode
|
||||
- Workspaces
|
||||
- Mods / Themes
|
||||
- Glance
|
||||
- URL Bar
|
||||
- Tabs
|
||||
- Split View
|
||||
- Settings
|
||||
- Privacy
|
||||
- Performance
|
||||
- Media Controler
|
||||
- Tab unloading
|
||||
- Tab Folders
|
||||
- Keyboard Shortcuts
|
||||
- Security
|
||||
- Extensions
|
||||
- Customizable UI / Toolbars
|
||||
- Localization
|
||||
- Bookmarks
|
||||
- Compact Mode
|
||||
- Customizable UI / Toolbars
|
||||
- Extensions
|
||||
- Glance
|
||||
- Keyboard Shortcuts
|
||||
- Localization
|
||||
- Media Controler
|
||||
- Mods / Themes
|
||||
- Performance
|
||||
- Privacy
|
||||
- Security
|
||||
- Settings
|
||||
- Split View
|
||||
- Sync
|
||||
- Tab Folders
|
||||
- Tab unloading
|
||||
- Tabs
|
||||
- URL Bar
|
||||
- Workspaces
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
117
README.md
117
README.md
@@ -7,7 +7,7 @@
|
||||
[](https://crowdin.com/project/zen-browser)
|
||||
[](https://github.com/zen-browser/desktop/actions/workflows/build.yml)
|
||||
|
||||
✨ Experience tranquillity while browsing the internet with Zen! Our mission is to give you a balance between speed, privacy and productivity!
|
||||
Zen is a firefox-based browser with the aim of pushing your productivity to a new level!
|
||||
|
||||
<div flex="true">
|
||||
<a href="https://zen-browser.app/download">
|
||||
@@ -27,118 +27,13 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 🖥️ Compatibility
|
||||
### Firefox Versions
|
||||
|
||||
Zen is currently built using Firefox version `138.0.4`! 🚀
|
||||
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `139.0.1`! 🚀
|
||||
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 139.0.1`!
|
||||
|
||||
- [`Zen Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 139.0`!
|
||||
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
|
||||
- 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!
|
||||
### Contributing
|
||||
|
||||
## 🤝 Contribution
|
||||
If you'd like to report a bug, please do so on our [GitHub Issues page](https://github.com/zen-browser/desktop/issues/) and for feature requests, you can use [Github Discussions](https://github.com/zen-browser/desktop/discussions).
|
||||
|
||||
Zen is an open-source project, and we welcome contributions from the community! Please take a look at the [contribution guidelines](./docs/contribute.md) before getting started!
|
||||
|
||||
### Issue metrics
|
||||
|
||||
We keep track of how many issues are closed at the end of the month in [docs/issue-metrics](./docs/issue-metrics). We use this to keep track of our issues and see our progress! 📈
|
||||
|
||||
### Versioning
|
||||
|
||||
Zen uses [Semantic Versioning](https://semver.org/), meaning versions are displayed as `a.b.cd` where:
|
||||
|
||||
- `a` is the major version
|
||||
- `b` is the minor version
|
||||
- `c` is the branch prefix
|
||||
- `d` is the patch version
|
||||
|
||||
### Branches
|
||||
|
||||
Zen is divided into 2 main branches. We use `dev` for development and `stable` for stable releases. The `dev` branch is where all the new features are added and where `twilight` builds are generated. The `stable` branch is where the stable releases are generated.
|
||||
|
||||
We divide into 2 branches in case there's any really important security update (for example) that needs to be released before the next stable release. This allows us to do patches without releasing unstable versions to the public.
|
||||
|
||||
## 📥 Installation
|
||||
|
||||
### Supported Operating Systems
|
||||
|
||||
Zen is available for Linux, macOS, and Windows. You can download the latest version from the official website at [zen-browser.app](https://zen-browser.app/download), or from the [GitHub Releases](https://github.com/zen-browser/desktop/releases) page.
|
||||
|
||||
If you don't see your OS listed below, that's because we already have it in our [downloads page](https://zen-browser.app/download)! Make sure to check it out!
|
||||
|
||||
#### Windows
|
||||
|
||||
##### Winget
|
||||
|
||||
```ps
|
||||
winget install --id Zen-Team.Zen-Browser
|
||||
```
|
||||
|
||||
#### macOS
|
||||
|
||||
- Requires macOS 10.15 or later
|
||||
- Available for ARM and Intel architectures
|
||||
|
||||
You can also install Zen using Homebrew:
|
||||
|
||||
```
|
||||
brew install zen-browser
|
||||
```
|
||||
|
||||
#### Linux
|
||||
|
||||
##### Arch-based distributions
|
||||
|
||||
```sh
|
||||
yay -S zen-browser-bin
|
||||
```
|
||||
|
||||
##### Other Linux distributions (Tarball or AppImage)
|
||||
|
||||
- `Tarball` install:
|
||||
|
||||
```sh
|
||||
bash <(curl -s https://updates.zen-browser.app/install.sh)
|
||||
```
|
||||
|
||||
- `AppImage` install:
|
||||
|
||||
```sh
|
||||
bash <(curl https://updates.zen-browser.app/appimage.sh)
|
||||
```
|
||||
|
||||
> AppImage install requires `zsync` for the Update feature
|
||||
|
||||
- Again, if you don't see your OS listed above, that's because we already have it in our [downloads page](https://zen-browser.app/download)! 🔄
|
||||
|
||||
To upgrade the browser to a newer version, use the embedded update functionality in `About Zen`.
|
||||
|
||||
## 👨💻 Development and Contributing
|
||||
|
||||
Some components used by @zen-browser as an attempt to make Firefox forks a better place, and for other to enjoy the beauty of OSS. You can find them [here](https://github.com/zen-browser/desktop/tree/dev/src/zen).
|
||||
|
||||
#### `Run Locally`
|
||||
|
||||
In order to download and run Zen locally, please follow [these instructions](https://docs.zen-browser.app/guides/building).
|
||||
|
||||
#### `Special Thanks`
|
||||
|
||||
Special thanks to... EVERYONE 🎉! Checkout the team and contributors page [here](https://zen-browser.app/about)
|
||||
|
||||
#### `Third Party Code`
|
||||
|
||||
Zen couldn't be in its current state without the help of these amazing projects! 🙏
|
||||
|
||||
- Zen's default preferences are based on [BetterFox](https://github.com/yokoffing/Betterfox)
|
||||
- Gradient image extracted from [Arc Palette](https://github.com/neurokitti/Arc_Palette)
|
||||
- `icons.css` has been modified from [Edge Firefox](https://github.com/bmFtZQ/edge-frfox) (MIT licensed file).
|
||||
|
||||
### 🖥️ Comparison with other browsers
|
||||
|
||||
Thanks everyone for making Zen stand out among these giants!
|
||||
|
||||
[](https://star-history.com/#zen-browser/desktop&chromium/chromium&brave/brave-browser&Date)
|
||||
|
||||
## 📄 License
|
||||
|
||||
Zen browser is under the [MPL 2.0 LICENSE](./LICENSE). All the code is open-source and free to use! Attribution is appreciated but not required.
|
||||
|
||||
@@ -1 +1 @@
|
||||
82a08ea3ce2d17f21f3d45f4b5607a37590b0158
|
||||
6548d6b52a8969b347101142ed630393332ab274
|
||||
19
docs/assets/zen-browser.svg
Normal file
19
docs/assets/zen-browser.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 156 64" height="64" width="156">
|
||||
<defs>
|
||||
<style>
|
||||
.label {
|
||||
font-size: 1.5rem;
|
||||
font-family: sans-serif;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
fill: #1f1f1f;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.label {
|
||||
fill: #d1cfc0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<text class="label" y="38" x="5">Zen Browser</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 454 B |
2
l10n
2
l10n
Submodule l10n updated: 644474b8c9...d0ffb8e391
8
package-lock.json
generated
8
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@zen-browser/surfer": "^1.11.12"
|
||||
"@zen-browser/surfer": "^1.11.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.27.0",
|
||||
@@ -817,9 +817,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zen-browser/surfer": {
|
||||
"version": "1.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.11.12.tgz",
|
||||
"integrity": "sha512-wny52xOFvZe5aPXxLVxEcAzDNEiWWoDiCZFlzsNxkyQ5Lw6vzqroMWpjQPJwBRJOc/JssgiXMdd1uwl2LLnovQ==",
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.11.13.tgz",
|
||||
"integrity": "sha512-D0TyunAWYtTdJkuUkYeOc6VBXzE9aoDs58kWu/Oi/aU3vd8IbqXPbZZfYwj5FWPWDReMrJUNkkKAEdbL44y9aw==",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@resvg/resvg-js": "^1.4.0",
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/zen-browser/desktop#readme",
|
||||
"dependencies": {
|
||||
"@zen-browser/surfer": "^1.11.12"
|
||||
"@zen-browser/surfer": "^1.11.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.27.0",
|
||||
|
||||
@@ -16,6 +16,7 @@ pref('browser.toolbars.bookmarks.visibility', 'never');
|
||||
pref("browser.bookmarks.openInTabClosesMenu", false);
|
||||
pref("browser.menu.showViewImageInfo", true);
|
||||
pref("findbar.highlightAll", true);
|
||||
|
||||
pref("layout.word_select.eat_space_to_next_word", false);
|
||||
|
||||
// Better Windows theming
|
||||
|
||||
@@ -22,13 +22,17 @@ pref('zen.mediacontrols.enabled', true);
|
||||
// Exposure:
|
||||
pref('zen.haptic-feedback.enabled', true);
|
||||
|
||||
pref('zen.mods.auto-update-days', 20); // In days
|
||||
#ifdef MOZILLA_OFFICIAL
|
||||
pref('zen.mods.auto-update', true);
|
||||
pref('zen.rice.api.url', 'https://share.zen-browser.app', locked);
|
||||
pref('zen.injections.match-urls', 'https://zen-browser.app/*,https://share.zen-browser.app/*', locked);
|
||||
#else
|
||||
pref('zen.mods.auto-update', false);
|
||||
pref('zen.rice.api.url', "http://localhost", locked);
|
||||
pref('zen.injections.match-urls', 'http://localhost/*', locked);
|
||||
#endif
|
||||
|
||||
pref('zen.rice.share.notice.accepted', false);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
|
||||
@@ -36,6 +36,3 @@ pref("network.http.rcwn.enabled", false);
|
||||
// Strategy to use for bytecode cache (Thanks https://github.com/gunir)
|
||||
pref('dom.script_loader.bytecode_cache.strategy', 2);
|
||||
pref("layout.css.grid-template-masonry-value.enabled", true);
|
||||
|
||||
// No Proxy should be default, Use system proxy allows antivirus, virus or system proxy to MITM or slowing down Zen
|
||||
pref("network.proxy.type", 0);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
|
||||
index f6e1391baf12abb91c85a95107bb3923118746c0..76c7b75a4e29056110f1631a50047c4ddd8b1f4a 100644
|
||||
index f6e1391baf12abb91c85a95107bb3923118746c0..cac04aa288e8a305d0c8b28e0c919abce87658e5 100644
|
||||
--- a/browser/base/content/aboutDialog.js
|
||||
+++ b/browser/base/content/aboutDialog.js
|
||||
@@ -52,7 +52,7 @@ function init() {
|
||||
@@ -20,3 +20,18 @@ index f6e1391baf12abb91c85a95107bb3923118746c0..76c7b75a4e29056110f1631a50047c4d
|
||||
versionIdKey += "-nightly";
|
||||
let buildID = Services.appinfo.appBuildID;
|
||||
let year = buildID.slice(0, 4);
|
||||
@@ -125,14 +125,6 @@ function init() {
|
||||
window.close();
|
||||
});
|
||||
if (AppConstants.MOZ_UPDATER) {
|
||||
- document
|
||||
- .getElementById("aboutDialogHelpLink")
|
||||
- .addEventListener("click", () => {
|
||||
- openHelpLink("firefox-help");
|
||||
- });
|
||||
- document
|
||||
- .getElementById("submit-feedback")
|
||||
- .addEventListener("click", openFeedbackPage);
|
||||
document
|
||||
.getElementById("checkForUpdatesButton")
|
||||
.addEventListener("command", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/aboutDialog.xhtml b/browser/base/content/aboutDialog.xhtml
|
||||
index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88a08d42c1 100644
|
||||
index c64980810570fcea84e33fdc2d66ac42a79f4e46..6ef9bf4b88f0a0539d833f662c4dd890fd1fde93 100644
|
||||
--- a/browser/base/content/aboutDialog.xhtml
|
||||
+++ b/browser/base/content/aboutDialog.xhtml
|
||||
@@ -35,6 +35,7 @@
|
||||
@@ -10,7 +10,18 @@ index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88
|
||||
</linkset>
|
||||
|
||||
<html:div id="aboutDialogContainer">
|
||||
@@ -125,21 +126,23 @@
|
||||
@@ -102,10 +103,6 @@
|
||||
<label id="version" class="update"/>
|
||||
<label id="releasenotes" is="text-link" hidden="true" data-l10n-id="releaseNotes-link"/>
|
||||
</hbox>
|
||||
- <description class="text-blurb">
|
||||
- <label id="aboutDialogHelpLink" is="text-link" data-l10n-id="aboutdialog-help-user"/>
|
||||
- <label id="submit-feedback" is="text-link" data-l10n-id="aboutdialog-submit-feedback"/>
|
||||
- </description>
|
||||
</vbox>
|
||||
#endif
|
||||
</hbox>
|
||||
@@ -125,21 +122,17 @@
|
||||
</description>
|
||||
</vbox>
|
||||
<description class="text-blurb" id="communityDesc" data-l10n-id="community-2">
|
||||
@@ -18,12 +29,10 @@ index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88
|
||||
+ <label is="text-link" href="https://github.com/zen-browser/desktop" data-l10n-name="community-mozillaLink"/>
|
||||
<label is="text-link" useoriginprincipal="true" href="about:credits" data-l10n-name="community-creditsLink"/>
|
||||
</description>
|
||||
+#if 0
|
||||
<description class="text-blurb" id="contributeDesc" data-l10n-id="helpus">
|
||||
<label is="text-link" href="https://foundation.mozilla.org/?form=firefox-about" data-l10n-name="helpus-donateLink"/>
|
||||
<label is="text-link" href="https://www.mozilla.org/contribute/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
|
||||
</description>
|
||||
+#endif
|
||||
- <description class="text-blurb" id="contributeDesc" data-l10n-id="helpus">
|
||||
- <label is="text-link" href="https://foundation.mozilla.org/?form=firefox-about" data-l10n-name="helpus-donateLink"/>
|
||||
- <label is="text-link" href="https://www.mozilla.org/contribute/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
|
||||
- </description>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
@@ -1,8 +1,35 @@
|
||||
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
|
||||
index 992d07daaef1abc4554a43aa654888f66963c575..73e620b70b7ed14e9d140e875c2cd5f5ac31456b 100644
|
||||
index 73593191936cc345ee8e2c28cb251dc13f4c2fd4..e6c459c1ebc60a1f3930a55e212570f696bf07a0 100644
|
||||
--- a/browser/base/content/browser-addons.js
|
||||
+++ b/browser/base/content/browser-addons.js
|
||||
@@ -2105,18 +2105,20 @@ var gUnifiedExtensions = {
|
||||
@@ -735,7 +735,7 @@ var gXPInstallObserver = {
|
||||
persistent: true,
|
||||
hideClose: true,
|
||||
popupOptions: {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -942,7 +942,7 @@ var gXPInstallObserver = {
|
||||
hideClose: true,
|
||||
timeout: Date.now() + 30000,
|
||||
popupOptions: {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2125,7 +2125,7 @@ var gUnifiedExtensions = {
|
||||
|
||||
panel.hidden = false;
|
||||
PanelMultiView.openPopup(panel, this._button, {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition,
|
||||
triggerEvent: aEvent,
|
||||
});
|
||||
}
|
||||
@@ -2294,18 +2294,20 @@ var gUnifiedExtensions = {
|
||||
this._maybeMoveWidgetNodeBack(widgetId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
|
||||
index 7d7e8697f02f90d4f336c9ab0a73a89848e0c21c..3819ae72f97900b6d212f8a54550ae569d497741 100644
|
||||
index 7d7e8697f02f90d4f336c9ab0a73a89848e0c21c..64e950106dd05b443ce72107613ac9cc405d56ea 100644
|
||||
--- a/browser/base/content/browser-box.inc.xhtml
|
||||
+++ b/browser/base/content/browser-box.inc.xhtml
|
||||
@@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
<hbox flex="1" id="browser">
|
||||
+ <zen-library/>
|
||||
<box context="sidebar-context-menu" id="sidebar-main" hidden="true">
|
||||
<html:sidebar-main flex="1">
|
||||
<box id="vertical-tabs" slot="tabstrip" customizable="true" contextmenu="toolbar-context-menu"></box>
|
||||
@@ -23,7 +24,15 @@
|
||||
@@ -23,7 +23,15 @@
|
||||
<browser id="sidebar" autoscroll="false" disablehistory="true" disablefullscreen="true" tooltip="aHTMLTooltip"/>
|
||||
</vbox>
|
||||
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-urlbar.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-workspaces.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-decks.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-library.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-folders.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-glance.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-popup.css" />
|
||||
@@ -30,10 +29,9 @@
|
||||
</linkset>
|
||||
|
||||
# Scripts used all over the browser
|
||||
<script src="chrome://browser/content/ZenUIManager.mjs"></script>
|
||||
<script src="chrome://browser/content/ZenUIManager.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenMods.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
|
||||
@@ -43,4 +41,3 @@
|
||||
<script src="chrome://browser/content/zen-components/ZenGlanceManager.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenMediaController.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
|
||||
<script src="chrome://browser/content/zen-components/ZenLibrary.mjs"></script>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
content/browser/ZenCustomizableUI.sys.mjs (../../zen/common/ZenCustomizableUI.sys.mjs)
|
||||
content/browser/zen-components/ZenUIMigration.mjs (../../zen/common/ZenUIMigration.mjs)
|
||||
content/browser/zen-components/ZenCommonUtils.mjs (../../zen/common/ZenCommonUtils.mjs)
|
||||
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/ZenSessionStore.mjs)
|
||||
|
||||
content/browser/zen-styles/zen-theme.css (../../zen/common/styles/zen-theme.css)
|
||||
content/browser/zen-styles/zen-buttons.css (../../zen/common/styles/zen-buttons.css)
|
||||
@@ -34,13 +35,7 @@
|
||||
content/browser/zen-components/ZenViewSplitter.mjs (../../zen/split-view/ZenViewSplitter.mjs)
|
||||
content/browser/zen-styles/zen-decks.css (../../zen/split-view/zen-decks.css)
|
||||
|
||||
content/browser/zen-components/ZenThemesCommon.mjs (../../zen/mods/ZenThemesCommon.mjs)
|
||||
content/browser/zen-components/ZenThemesImporter.mjs (../../zen/mods/ZenThemesImporter.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (../../zen/mods/actors/ZenThemeMarketplaceParent.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (../../zen/mods/actors/ZenThemeMarketplaceChild.sys.mjs)
|
||||
|
||||
content/browser/zen-components/ZenLibrary.mjs (../../zen/library/ZenLibrary.mjs)
|
||||
content/browser/zen-styles/zen-library.css (../../zen/library/zen-library.css)
|
||||
content/browser/zen-components/ZenMods.mjs (../../zen/mods/ZenMods.mjs)
|
||||
|
||||
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
||||
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
||||
@@ -60,8 +55,6 @@
|
||||
|
||||
content/browser/zen-components/ZenGlanceManager.mjs (../../zen/glance/ZenGlanceManager.mjs)
|
||||
content/browser/zen-styles/zen-glance.css (../../zen/glance/zen-glance.css)
|
||||
content/browser/zen-components/actors/ZenGlanceChild.sys.mjs (../../zen/glance/actors/ZenGlanceChild.sys.mjs)
|
||||
content/browser/zen-components/actors/ZenGlanceParent.sys.mjs (../../zen/glance/actors/ZenGlanceParent.sys.mjs)
|
||||
|
||||
content/browser/zen-components/ZenFolders.mjs (../../zen/folders/ZenFolders.mjs)
|
||||
content/browser/zen-styles/zen-folders.css (../../zen/folders/zen-folders.css)
|
||||
@@ -77,7 +70,7 @@
|
||||
content/browser/zen-styles/zen-download-box-animation.css (../../zen/downloads/zen-download-box-animation.css)
|
||||
|
||||
|
||||
# Images
|
||||
# Images
|
||||
content/browser/zen-images/gradient.png (../../zen/images/gradient.png)
|
||||
content/browser/zen-images/brand-header.svg (../../zen/images/brand-header.svg)
|
||||
content/browser/zen-images/layouts/collapsed.png (../../zen/images/layouts/collapsed.png)
|
||||
@@ -89,7 +82,7 @@
|
||||
content/browser/zen-images/downloads/download.svg (../../zen/images/downloads/download.svg)
|
||||
content/browser/zen-images/downloads/archive.svg (../../zen/images/downloads/archive.svg)
|
||||
|
||||
# Fonts
|
||||
# Fonts
|
||||
content/browser/zen-fonts/JunicodeVF-Italic.woff2 (../../zen/fonts/JunicodeVF-Italic.woff2)
|
||||
content/browser/zen-fonts/JunicodeVF-Roman.woff2 (../../zen/fonts/JunicodeVF-Roman.woff2)
|
||||
|
||||
@@ -107,4 +100,4 @@
|
||||
content/browser/zen-images/favicons/slack.ico (../../zen/images/favicons/slack.ico)
|
||||
content/browser/zen-images/favicons/reddit.ico (../../zen/images/favicons/reddit.ico)
|
||||
content/browser/zen-images/favicons/x.ico (../../zen/images/favicons/x.ico)
|
||||
content/browser/zen-images/favicons/trello.ico (../../zen/images/favicons/trello.ico)
|
||||
content/browser/zen-images/favicons/trello.ico (../../zen/images/favicons/trello.ico)
|
||||
|
||||
@@ -43,11 +43,6 @@
|
||||
|
||||
<command id="cmd_zenCopyCurrentURL" />
|
||||
<command id="cmd_zenCopyCurrentURLMarkdown" />
|
||||
|
||||
<command id="cmd_zenCtxDeleteWorkspace" />
|
||||
<command id="cmd_zenChangeWorkspaceName" />
|
||||
|
||||
<command id="cmd_zenToggleLibrary" />
|
||||
</commandset>
|
||||
|
||||
<keyset id="zenKeyset"></keyset>
|
||||
|
||||
@@ -57,11 +57,67 @@
|
||||
</panelmultiview>
|
||||
</panel>
|
||||
|
||||
<menupopup id="zenWorkspaceMoreActions">
|
||||
<menuitem id="context_zenEditWorkspace" data-l10n-id="zen-workspaces-panel-change-name" command="cmd_zenChangeWorkspaceName"/>
|
||||
<menuitem class="zenToolbarThemePicker"
|
||||
data-l10n-id="zen-workspaces-change-gradient"
|
||||
command="cmd_zenOpenZenThemePicker"/>
|
||||
<panel flip="slide" type="arrow" orient="vertical" id="PanelUI-zen-workspaces" position="bottomright topright" mainview="true" side="left">
|
||||
<panelmultiview id="PanelUI-zen-workspaces-multiview" mainViewId="PanelUI-zen-workspaces-view">
|
||||
<panelview id="PanelUI-zen-workspaces-view" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true" closemenu="none">
|
||||
<vbox>
|
||||
<hbox>
|
||||
<h3 data-l10n-id="zen-panel-ui-workspaces-text" id="PanelUI-zen-workspaces-header"></h3>
|
||||
<toolbarbutton id="PanelUI-zen-workspaces-reorder-mode" class="subviewbutton">
|
||||
<image></image>
|
||||
</toolbarbutton>
|
||||
<toolbarbutton id="PanelUI-zen-workspaces-new" class="subviewbutton">
|
||||
<image></image>
|
||||
</toolbarbutton>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<html:div id="PanelUI-zen-workspaces-list">
|
||||
</html:div>
|
||||
</panelview>
|
||||
<panelview id="PanelUI-zen-workspaces-create" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true">
|
||||
<vbox class="PanelUI-zen-workspaces-user-create">
|
||||
<h1 data-l10n-id="zen-panel-ui-workspaces-create-text"></h1>
|
||||
<hbox class="PanelUI-zen-workspaces-creation-wraper">
|
||||
<hbox class="PanelUI-zen-workspaces-icons-container create"></hbox>
|
||||
<html:input autofocus="true" id="PanelUI-zen-workspaces-create-input" type="text" placeholder="Enter workspace name" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
<html:moz-button-group class="panel-footer" id="PanelUI-zen-workspaces-create-footer">
|
||||
<button disabled="true" default="true" slot="primary" id="PanelUI-zen-workspaces-create-save" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-create-save">
|
||||
</button>
|
||||
<button id="PanelUI-zen-workspaces-create-cancel" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-create-cancel">
|
||||
</button>
|
||||
</html:moz-button-group>
|
||||
</panelview>
|
||||
<panelview id="PanelUI-zen-workspaces-edit" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true">
|
||||
<vbox class="PanelUI-zen-workspaces-user-create">
|
||||
<h1 data-l10n-id="zen-panel-ui-workspaces-edit-text"></h1>
|
||||
<hbox class="PanelUI-zen-workspaces-creation-wraper">
|
||||
<hbox class="PanelUI-zen-workspaces-icons-container edit"></hbox>
|
||||
<html:input autofocus="true" id="PanelUI-zen-workspaces-edit-input" type="text" placeholder="Enter workspace name" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
<html:moz-button-group class="panel-footer" id="PanelUI-zen-workspaces-edit-footer">
|
||||
<button disabled="true" default="true" slot="primary" id="PanelUI-zen-workspaces-edit-save" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-edit-save">
|
||||
</button>
|
||||
<button id="PanelUI-zen-workspaces-edit-cancel" class="footer-button" data-l10n-id="zen-panel-ui-workspaces-edit-cancel">
|
||||
</button>
|
||||
</html:moz-button-group>
|
||||
</panelview>
|
||||
<panelview id="PanelUI-zen-workspaces-icon-picker" class="PanelUI-subView" role="document" mainview-with-header="true" has-custom-header="true">
|
||||
<vbox id="PanelUI-zen-workspaces-icon-picker-wrapper">
|
||||
<html:div id="PanelUI-zen-workspaces-icon-search-bar">
|
||||
<html:input autofocus="true" type="text" id="PanelUI-zen-workspaces-icon-search-input"/>
|
||||
</html:div>
|
||||
</vbox>
|
||||
</panelview>
|
||||
</panelmultiview>
|
||||
</panel>
|
||||
|
||||
<menupopup id="zenWorkspaceActionsMenu">
|
||||
<menuitem id="context_zenOpenWorkspace" data-l10n-id="zen-workspaces-panel-context-open"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenEditWorkspace" data-l10n-id="zen-workspaces-panel-context-edit"/>
|
||||
<menu id="context_zenWorkspacesOpenInContainerTab"
|
||||
data-l10n-id="zen-workspaces-panel-context-open-in-container-tab"
|
||||
selection-type="single"
|
||||
@@ -71,5 +127,5 @@
|
||||
<menupopup />
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete" command="cmd_zenCtxDeleteWorkspace"/>
|
||||
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete"/>
|
||||
</menupopup>
|
||||
|
||||
@@ -9,3 +9,5 @@
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaces.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspacesSync.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenActorsManager.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenSessionStore.mjs"></script>
|
||||
|
||||
|
||||
@@ -9,6 +9,5 @@
|
||||
context="toolbar-context-menu"
|
||||
mode="icons">
|
||||
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-expand-sidebar-button" command="cmd_zenToggleSidebar" data-l10n-id="sidebar-zen-expand"></toolbarbutton>
|
||||
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-open-library" command="cmd_zenToggleLibrary"></toolbarbutton>
|
||||
<zen-workspace-icons id="zen-workspaces-button" overflows="false" removable="false"></zen-workspace-icons>
|
||||
</toolbar>
|
||||
|
||||
@@ -14,30 +14,37 @@ var gZenMarketplaceManager = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.gZenMods) {
|
||||
window.gZenMods = ZenMultiWindowFeature.currentBrowser.gZenMods;
|
||||
}
|
||||
|
||||
header.appendChild(this._initDisableAll());
|
||||
|
||||
this._initImportExport();
|
||||
|
||||
this.__hasInitializedEvents = true;
|
||||
|
||||
await this._buildThemesList();
|
||||
await this._buildModsList();
|
||||
|
||||
Services.prefs.addObserver(this.updatePref, this);
|
||||
Services.prefs.addObserver(gZenMods.updatePref, this);
|
||||
|
||||
const checkForUpdateClick = (event) => {
|
||||
if (event.target === checkForUpdates) {
|
||||
event.preventDefault();
|
||||
|
||||
this._checkForThemeUpdates(event);
|
||||
}
|
||||
};
|
||||
|
||||
checkForUpdates.addEventListener('click', checkForUpdateClick);
|
||||
|
||||
document.addEventListener('ZenThemeMarketplace:CheckForUpdatesFinished', (event) => {
|
||||
document.addEventListener('ZenModsMarketplace:CheckForUpdatesFinished', (event) => {
|
||||
checkForUpdates.disabled = false;
|
||||
|
||||
const updates = event.detail.updates;
|
||||
const success = document.getElementById('zenThemeMarketplaceUpdatesSuccess');
|
||||
const error = document.getElementById('zenThemeMarketplaceUpdatesFailure');
|
||||
|
||||
if (updates) {
|
||||
success.hidden = false;
|
||||
error.hidden = true;
|
||||
@@ -48,13 +55,16 @@ var gZenMarketplaceManager = {
|
||||
});
|
||||
|
||||
window.addEventListener('unload', () => {
|
||||
Services.prefs.removeObserver(this.updatePref, this);
|
||||
Services.prefs.removeObserver(gZenMods.updatePref, this);
|
||||
this.__hasInitializedEvents = false;
|
||||
document.removeEventListener('ZenThemeMarketplace:CheckForUpdatesFinished', this);
|
||||
document.removeEventListener('ZenCheckForThemeUpdates', this);
|
||||
|
||||
document.removeEventListener('ZenModsMarketplace:CheckForUpdatesFinished', this);
|
||||
document.removeEventListener('ZenCheckForModUpdates', this);
|
||||
|
||||
checkForUpdates.removeEventListener('click', checkForUpdateClick);
|
||||
this.themesList.innerHTML = '';
|
||||
this._doNotRebuildThemesList = false;
|
||||
|
||||
this.modsList.innerHTML = '';
|
||||
this._doNotRebuildModsList = false;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -63,36 +73,32 @@ var gZenMarketplaceManager = {
|
||||
const exportButton = document.getElementById('zenThemeMarketplaceExport');
|
||||
|
||||
if (importButton) {
|
||||
importButton.addEventListener('click', async () => {
|
||||
await this._importThemes();
|
||||
});
|
||||
importButton.addEventListener('click', this._importThemes.bind(this));
|
||||
}
|
||||
|
||||
if (exportButton) {
|
||||
exportButton.addEventListener('click', async () => {
|
||||
await this._exportThemes();
|
||||
});
|
||||
exportButton.addEventListener('click', this._exportThemes.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
_initDisableAll() {
|
||||
const areThemesDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
|
||||
const browser = ZenThemesCommon.currentBrowser;
|
||||
const areModsDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
|
||||
const browser = ZenMultiWindowFeature.currentBrowser;
|
||||
const mozToggle = document.createElement('moz-toggle');
|
||||
|
||||
mozToggle.className =
|
||||
'zenThemeMarketplaceItemPreferenceToggle zenThemeMarketplaceDisableAllToggle';
|
||||
mozToggle.pressed = !areThemesDisabled;
|
||||
mozToggle.pressed = !areModsDisabled;
|
||||
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
`zen-theme-disable-all-${!areThemesDisabled ? 'enabled' : 'disabled'}`
|
||||
`zen-theme-disable-all-${!areModsDisabled ? 'enabled' : 'disabled'}`
|
||||
);
|
||||
|
||||
mozToggle.addEventListener('toggle', async (event) => {
|
||||
const { pressed = false } = event.target || {};
|
||||
|
||||
this.themesList.style.display = pressed ? '' : 'none';
|
||||
this.modsList.style.display = pressed ? '' : 'none';
|
||||
Services.prefs.setBoolPref('zen.themes.disable-all', !pressed);
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
@@ -100,90 +106,65 @@ var gZenMarketplaceManager = {
|
||||
);
|
||||
});
|
||||
|
||||
if (areThemesDisabled) {
|
||||
this.themesList.style.display = 'none';
|
||||
if (areModsDisabled) {
|
||||
this.modsList.style.display = 'none';
|
||||
}
|
||||
|
||||
return mozToggle;
|
||||
},
|
||||
|
||||
async observe() {
|
||||
await this._buildThemesList();
|
||||
await this._buildModsList();
|
||||
},
|
||||
|
||||
_checkForThemeUpdates(event) {
|
||||
// Send a message to the child to check for theme updates.
|
||||
event.target.disabled = true;
|
||||
// send an event that will be listened by the child process.
|
||||
document.dispatchEvent(new CustomEvent('ZenCheckForThemeUpdates'));
|
||||
document.dispatchEvent(new CustomEvent('ZenCheckForModUpdates'));
|
||||
},
|
||||
|
||||
get updatePref() {
|
||||
return 'zen.themes.updated-value-observer';
|
||||
},
|
||||
|
||||
triggerThemeUpdate() {
|
||||
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
|
||||
},
|
||||
|
||||
get themesList() {
|
||||
if (!this._themesList) {
|
||||
this._themesList = document.getElementById('zenThemeMarketplaceList');
|
||||
get modsList() {
|
||||
if (!this._modsList) {
|
||||
this._modsList = document.getElementById('zenThemeMarketplaceList');
|
||||
}
|
||||
return this._themesList;
|
||||
return this._modsList;
|
||||
},
|
||||
|
||||
async removeTheme(themeId) {
|
||||
const themePath = ZenThemesCommon.getThemeFolder(themeId);
|
||||
async removeMod(modId) {
|
||||
await gZenMods.removeMod(modId);
|
||||
|
||||
console.info(`[ZenThemeMarketplaceParent:settings]: Removing theme ${themePath}`);
|
||||
|
||||
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
|
||||
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
delete themes[themeId];
|
||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
||||
|
||||
this.triggerThemeUpdate();
|
||||
gZenMods.triggerModsUpdate();
|
||||
},
|
||||
|
||||
async disableTheme(themeId) {
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const theme = themes[themeId];
|
||||
async disableMod(modId) {
|
||||
await gZenMods.disableMod(modId);
|
||||
|
||||
console.log(`[ZenThemeMarketplaceParent:settings]: Disabling theme ${theme.name}`);
|
||||
|
||||
theme.enabled = false;
|
||||
|
||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
||||
this._doNotRebuildThemesList = true;
|
||||
this.triggerThemeUpdate();
|
||||
this._doNotRebuildModsList = true;
|
||||
gZenMods.triggerModsUpdate();
|
||||
},
|
||||
|
||||
async enableTheme(themeId) {
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const theme = themes[themeId];
|
||||
async enableMod(modId) {
|
||||
await gZenMods.enableMod(modId);
|
||||
|
||||
console.log(`[ZenThemeMarketplaceParent:settings]: Enabling theme ${theme.name}`);
|
||||
|
||||
theme.enabled = true;
|
||||
|
||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
||||
this._doNotRebuildThemesList = true;
|
||||
this.triggerThemeUpdate();
|
||||
this._doNotRebuildModsList = true;
|
||||
gZenMods.triggerModsUpdate();
|
||||
},
|
||||
|
||||
_triggerBuildUpdateWithoutRebuild() {
|
||||
this._doNotRebuildThemesList = true;
|
||||
this.triggerThemeUpdate();
|
||||
this._doNotRebuildModsList = true;
|
||||
gZenMods.triggerModsUpdate();
|
||||
},
|
||||
|
||||
async _importThemes() {
|
||||
const errorBox = document.getElementById('zenThemeMarketplaceImportFailure');
|
||||
const successBox = document.getElementById('zenThemeMarketplaceImportSuccess');
|
||||
|
||||
successBox.hidden = true;
|
||||
errorBox.hidden = true;
|
||||
|
||||
const input = document.createElement('input');
|
||||
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
input.style.display = 'none';
|
||||
@@ -191,37 +172,52 @@ var gZenMarketplaceManager = {
|
||||
input.setAttribute('accept', '.json');
|
||||
|
||||
let timeout;
|
||||
|
||||
const filePromise = new Promise((resolve) => {
|
||||
input.addEventListener('change', (event) => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
const file = event.target.files[0];
|
||||
resolve(file);
|
||||
});
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
console.warn('[ZenThemeMarketplaceParent:settings]: Import timeout reached, aborting.');
|
||||
console.warn('[ZenSettings:ZenMods]: Import timeout reached, aborting.');
|
||||
resolve(null);
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
input.addEventListener('cancel', () => {
|
||||
console.warn('[ZenSettings:ZenMods]: Import cancelled by user.');
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
|
||||
input.click();
|
||||
|
||||
try {
|
||||
const file = await filePromise;
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = await file.text();
|
||||
|
||||
const themes = JSON.parse(content);
|
||||
for (const theme of Object.values(themes)) {
|
||||
theme.themeId = theme.id;
|
||||
window.ZenInstallTheme(theme);
|
||||
const mods = JSON.parse(content);
|
||||
|
||||
for (const mod of Object.values(mods)) {
|
||||
mod.modId = mod.id;
|
||||
await window.ZenInstallMod(mod);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ZenThemeMarketplaceParent:settings]: Error while importing themes:', error);
|
||||
console.error('[ZenSettings:ZenMods]: Error while importing mods:', error);
|
||||
errorBox.hidden = false;
|
||||
} finally {
|
||||
if (input) input.remove();
|
||||
}
|
||||
|
||||
if (input) {
|
||||
input.remove();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -232,51 +228,54 @@ var gZenMarketplaceManager = {
|
||||
successBox.hidden = true;
|
||||
errorBox.hidden = true;
|
||||
|
||||
let a, url;
|
||||
let temporalAnchor, temporalUrl;
|
||||
try {
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const themesJson = JSON.stringify(themes, null, 2);
|
||||
const blob = new Blob([themesJson], { type: 'application/json' });
|
||||
url = URL.createObjectURL(blob);
|
||||
// Creating a link to download the JSON file
|
||||
a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'zen-themes-export.json';
|
||||
const mods = await gZenMods.getMods();
|
||||
const modsJson = JSON.stringify(mods, null, 2);
|
||||
const blob = new Blob([modsJson], { type: 'application/json' });
|
||||
|
||||
temporalUrl = URL.createObjectURL(blob);
|
||||
// Creating a link to download the JSON file
|
||||
temporalAnchor = document.createElement('a');
|
||||
temporalAnchor.href = temporalUrl;
|
||||
temporalAnchor.download = 'zen-mods-export.json';
|
||||
|
||||
document.body.appendChild(temporalAnchor);
|
||||
temporalAnchor.click();
|
||||
temporalAnchor.remove();
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
successBox.hidden = false;
|
||||
} catch (error) {
|
||||
console.error('[ZenThemeMarketplaceParent:settings]: Error while exporting themes:', error);
|
||||
console.error('[ZenSettings:ZenMods]: Error while exporting mods:', error);
|
||||
errorBox.hidden = false;
|
||||
} finally {
|
||||
if (a) {
|
||||
a.remove();
|
||||
}
|
||||
if (url) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
if (temporalAnchor) {
|
||||
temporalAnchor.remove();
|
||||
}
|
||||
|
||||
if (temporalUrl) {
|
||||
URL.revokeObjectURL(temporalUrl);
|
||||
}
|
||||
},
|
||||
|
||||
async _buildThemesList() {
|
||||
if (!this.themesList) {
|
||||
async _buildModsList() {
|
||||
if (!this.modsList) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._doNotRebuildThemesList) {
|
||||
this._doNotRebuildThemesList = false;
|
||||
if (this._doNotRebuildModsList) {
|
||||
this._doNotRebuildModsList = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const themes = await ZenThemesCommon.getThemes();
|
||||
const mods = await gZenMods.getMods();
|
||||
const browser = ZenMultiWindowFeature.currentBrowser;
|
||||
const themeList = document.createElement('div');
|
||||
const modList = document.createElement('div');
|
||||
|
||||
for (const theme of Object.values(themes).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||
const isThemeEnabled = theme.enabled === undefined || theme.enabled;
|
||||
for (const mod of Object.values(mods).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
const sanitizedName = gZenMods.sanitizeModName(mod.name);
|
||||
const isModEnabled = mod.enabled === undefined || mod.enabled;
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="zenThemeMarketplaceItem">
|
||||
<vbox class="zenThemeMarketplaceItemContent">
|
||||
@@ -286,14 +285,14 @@ var gZenMarketplaceManager = {
|
||||
<description class="description-deemphasized zenThemeMarketplaceItemDescription"></description>
|
||||
</vbox>
|
||||
<hbox class="zenThemeMarketplaceItemActions">
|
||||
${theme.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
|
||||
${theme.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-theme-id="${theme.id}"></button>` : ''}
|
||||
<button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-theme-id="${theme.id}"></button>
|
||||
${mod.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
|
||||
${mod.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-mod-id="${mod.id}"></button>` : ''}
|
||||
<button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-mod-id="${mod.id}"></button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
`);
|
||||
|
||||
const themeName = `${theme.name} (v${theme.version || '1.0.0'})`;
|
||||
const modName = `${mod.name} (v${mod.version ?? '1.0.0'})`;
|
||||
|
||||
const base = fragment.querySelector('.zenThemeMarketplaceItem');
|
||||
const baseHeader = fragment.querySelector('#zenThemeMarketplaceItemContentHeader');
|
||||
@@ -308,7 +307,7 @@ var gZenMarketplaceManager = {
|
||||
|
||||
mainDialogDiv.className = 'zenThemeMarketplaceItemPreferenceDialog';
|
||||
headerDiv.className = 'zenThemeMarketplaceItemPreferenceDialogTopBar';
|
||||
headerTitle.textContent = themeName;
|
||||
headerTitle.textContent = modName;
|
||||
browser.document.l10n.setAttributes(headerTitle, 'zen-theme-marketplace-theme-header-title', {
|
||||
name: sanitizedName,
|
||||
});
|
||||
@@ -319,10 +318,10 @@ var gZenMarketplaceManager = {
|
||||
contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent';
|
||||
mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle';
|
||||
|
||||
mozToggle.pressed = isThemeEnabled;
|
||||
mozToggle.pressed = isModEnabled;
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
`zen-theme-marketplace-toggle-${isThemeEnabled ? 'enabled' : 'disabled'}-button`
|
||||
`zen-theme-marketplace-toggle-${isModEnabled ? 'enabled' : 'disabled'}-button`
|
||||
);
|
||||
|
||||
baseHeader.appendChild(mozToggle);
|
||||
@@ -340,34 +339,34 @@ var gZenMarketplaceManager = {
|
||||
});
|
||||
|
||||
mozToggle.addEventListener('toggle', async (event) => {
|
||||
const themeId = event.target
|
||||
const modId = event.target
|
||||
.closest('.zenThemeMarketplaceItem')
|
||||
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
||||
.getAttribute('zen-theme-id');
|
||||
.getAttribute('zen-mod-id');
|
||||
event.target.setAttribute('disabled', true);
|
||||
|
||||
if (!event.target.hasAttribute('pressed')) {
|
||||
await this.disableTheme(themeId);
|
||||
await this.disableMod(modId);
|
||||
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
'zen-theme-marketplace-toggle-disabled-button'
|
||||
);
|
||||
|
||||
if (theme.preferences) {
|
||||
if (mod.preferences) {
|
||||
document
|
||||
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
||||
.setAttribute('hidden', true);
|
||||
}
|
||||
} else {
|
||||
await this.enableTheme(themeId);
|
||||
await this.enableMod(modId);
|
||||
|
||||
browser.document.l10n.setAttributes(
|
||||
mozToggle,
|
||||
'zen-theme-marketplace-toggle-enabled-button'
|
||||
);
|
||||
|
||||
if (theme.preferences) {
|
||||
if (mod.preferences) {
|
||||
document
|
||||
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
||||
.removeAttribute('hidden');
|
||||
@@ -379,8 +378,8 @@ var gZenMarketplaceManager = {
|
||||
}, 400);
|
||||
});
|
||||
|
||||
fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = themeName;
|
||||
fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = theme.description;
|
||||
fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = modName;
|
||||
fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = mod.description;
|
||||
fragment
|
||||
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
||||
.addEventListener('click', async (event) => {
|
||||
@@ -392,34 +391,34 @@ var gZenMarketplaceManager = {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.removeTheme(event.target.getAttribute('zen-theme-id'));
|
||||
await this.removeMod(event.target.getAttribute('zen-mod-id'));
|
||||
});
|
||||
|
||||
if (theme.homepage) {
|
||||
if (mod.homepage) {
|
||||
const homepageButton = fragment.querySelector('.zenThemeMarketplaceItemHomepageButton');
|
||||
homepageButton.addEventListener('click', () => {
|
||||
// open the homepage url in a new tab
|
||||
const url = theme.homepage;
|
||||
const url = mod.homepage;
|
||||
|
||||
window.open(url, '_blank');
|
||||
});
|
||||
}
|
||||
|
||||
if (theme.preferences) {
|
||||
if (mod.preferences) {
|
||||
fragment
|
||||
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
||||
.addEventListener('click', () => {
|
||||
dialog.showModal();
|
||||
});
|
||||
|
||||
if (isThemeEnabled) {
|
||||
if (isModEnabled) {
|
||||
fragment
|
||||
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
||||
.removeAttribute('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
||||
const preferences = await gZenMods.getModPreferences(mod);
|
||||
|
||||
if (preferences.length > 0) {
|
||||
const preferencesWrapper = document.createXULElement('vbox');
|
||||
@@ -471,7 +470,7 @@ var gZenMarketplaceManager = {
|
||||
|
||||
if (!['string', 'number'].includes(valueType)) {
|
||||
console.log(
|
||||
`[ZenThemeMarketplaceParent:settings]: Warning, invalid data type received (${valueType}), skipping.`
|
||||
`[ZenSettings:ZenMods]: Warning, invalid data type received (${valueType}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -583,7 +582,7 @@ var gZenMarketplaceManager = {
|
||||
|
||||
input.addEventListener(
|
||||
'change',
|
||||
ZenThemesCommon.debounce((event) => {
|
||||
gZenMods.debounce((event) => {
|
||||
const value = event.target.value;
|
||||
|
||||
Services.prefs.setStringPref(property, value);
|
||||
@@ -617,18 +616,18 @@ var gZenMarketplaceManager = {
|
||||
|
||||
default:
|
||||
console.log(
|
||||
`[ZenThemeMarketplaceParent:settings]: Warning, unknown preference type received (${type}), skipping.`
|
||||
`[ZenSettings:ZenMods]: Warning, unknown preference type received (${type}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
contentDiv.appendChild(preferencesWrapper);
|
||||
}
|
||||
themeList.appendChild(fragment);
|
||||
modList.appendChild(fragment);
|
||||
}
|
||||
|
||||
this.themesList.replaceChildren(...themeList.children);
|
||||
themeList.remove();
|
||||
this.modsList.replaceChildren(...modList.children);
|
||||
modList.remove();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -636,6 +635,19 @@ const kZenExtendedSidebar = 'zen.view.sidebar-expanded';
|
||||
const kZenSingleToolbar = 'zen.view.use-single-toolbar';
|
||||
|
||||
var gZenLooksAndFeel = {
|
||||
kZenColors: [
|
||||
'#aac7ff',
|
||||
'#74d7cb',
|
||||
'#a0d490',
|
||||
'#dec663',
|
||||
'#ffb787',
|
||||
'#dec1b1',
|
||||
'#ffb1c0',
|
||||
'#ddbfc3',
|
||||
'#f6b0ea',
|
||||
'#d4bbff',
|
||||
],
|
||||
|
||||
init() {
|
||||
if (this.__hasInitialized) return;
|
||||
this.__hasInitialized = true;
|
||||
@@ -737,7 +749,8 @@ var gZenLooksAndFeel = {
|
||||
_initializeColorPicker(accentColor) {
|
||||
let elem = document.getElementById('zenLooksAndFeelColorOptions');
|
||||
elem.innerHTML = '';
|
||||
for (let color of ZenThemesCommon.kZenColors) {
|
||||
|
||||
for (let color of this.kZenColors) {
|
||||
let colorElemParen = document.createElement('div');
|
||||
let colorElem = document.createElement('div');
|
||||
colorElemParen.classList.add('zenLooksAndFeelColorOptionParen');
|
||||
@@ -761,7 +774,7 @@ var gZenLooksAndFeel = {
|
||||
},
|
||||
|
||||
_getInitialAccentColor() {
|
||||
return Services.prefs.getStringPref('zen.theme.accent-color', ZenThemesCommon.kZenColors[0]);
|
||||
return Services.prefs.getStringPref('zen.theme.accent-color', this.kZenColors[0]);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1220,7 +1233,7 @@ Preferences.addAll([
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: 'browser.tabs.unloadOnLowMemory',
|
||||
id: 'zen.mods.auto-update',
|
||||
type: 'bool',
|
||||
default: true,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script src="chrome://browser/content/zen-components/ZenCommonUtils.mjs" defer=""/>
|
||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs" defer=""/>
|
||||
<html:template id="template-paneZenMarketplace">
|
||||
<hbox id="ZenMarketplaceCategory"
|
||||
class="subcategory"
|
||||
@@ -21,6 +20,10 @@
|
||||
<button id="zenThemeMarketplaceCheckForUpdates" data-l10n-id="zen-theme-marketplace-check-for-updates-button" />
|
||||
</hbox>
|
||||
|
||||
<checkbox id="zenThemeMarketplaceAutoUpdate"
|
||||
data-l10n-id="zen-themes-auto-update"
|
||||
preference="zen.mods.auto-update"/>
|
||||
|
||||
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-success" hidden="true" id="zenThemeMarketplaceUpdatesSuccess" />
|
||||
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-failure" hidden="true" id="zenThemeMarketplaceUpdatesFailure" />
|
||||
|
||||
|
||||
@@ -31,15 +31,6 @@
|
||||
<html:h1 data-l10n-id="pane-zen-tabs-unloader-title"/>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="zenTabsUnloadGroup" data-category="paneZenTabManagement" hidden="true" class="highlighting-group">
|
||||
<label><html:h2 data-l10n-id="zen-tabs-unloader-header"/></label>
|
||||
<description class="description-deemphasized" data-l10n-id="zen-tabs-unloader-description" />
|
||||
|
||||
<checkbox id="zenTabsUnloadActivate"
|
||||
data-l10n-id="zen-tabs-unloader-enabled"
|
||||
preference="browser.tabs.unloadOnLowMemory"/>
|
||||
</groupbox>
|
||||
|
||||
<hbox id="zenPinnedTabsManagerCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
||||
13
src/browser/components/search/SearchUIUtils-sys-mjs.patch
Normal file
13
src/browser/components/search/SearchUIUtils-sys-mjs.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/browser/components/search/SearchUIUtils.sys.mjs b/browser/components/search/SearchUIUtils.sys.mjs
|
||||
index ecebaad93acfc9eb7dfd9d9f56fec2e1a4abe392..8bb1348b3258dbc518d23ec39181a81f87fc8c1e 100644
|
||||
--- a/browser/components/search/SearchUIUtils.sys.mjs
|
||||
+++ b/browser/components/search/SearchUIUtils.sys.mjs
|
||||
@@ -403,7 +403,7 @@ export var SearchUIUtils = {
|
||||
triggeringSearchEngine: engine.name,
|
||||
},
|
||||
});
|
||||
-
|
||||
+ window.gZenGlanceManager?.onSearchSelectCommand(where);
|
||||
return { engine, url: submission.uri };
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d81447514e2c 100644
|
||||
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d634dadd4d 100644
|
||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
@@ -2088,7 +2088,6 @@ var SessionStoreInternal = {
|
||||
@@ -31,17 +31,19 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3925,6 +3922,9 @@ var SessionStoreInternal = {
|
||||
@@ -3925,6 +3922,11 @@ var SessionStoreInternal = {
|
||||
Math.min(tabState.index, tabState.entries.length)
|
||||
);
|
||||
tabState.pinned = false;
|
||||
+ tabState.zenEssential = false;
|
||||
+ tabState.zenPinnedId = null;
|
||||
+ tabState.zenIsGlance = false;
|
||||
+ tabState.zenGlanceId = null;
|
||||
+ tabState.zenHasStaticLabel = false;
|
||||
|
||||
if (inBackground === false) {
|
||||
aWindow.gBrowser.selectedTab = newTab;
|
||||
@@ -5239,7 +5239,7 @@ var SessionStoreInternal = {
|
||||
@@ -5239,7 +5241,7 @@ var SessionStoreInternal = {
|
||||
}
|
||||
|
||||
let workspaceID = aWindow.getWorkspaceID();
|
||||
@@ -50,7 +52,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
winData.workspaceID = workspaceID;
|
||||
}
|
||||
},
|
||||
@@ -5430,14 +5430,15 @@ var SessionStoreInternal = {
|
||||
@@ -5430,14 +5432,15 @@ var SessionStoreInternal = {
|
||||
}
|
||||
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
@@ -68,7 +70,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
continue;
|
||||
}
|
||||
let tabData = lazy.TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
|
||||
@@ -5456,8 +5457,8 @@ var SessionStoreInternal = {
|
||||
@@ -5456,8 +5459,8 @@ var SessionStoreInternal = {
|
||||
// We don't store the Firefox View tab in Session Store, so if it was the last selected "tab" when
|
||||
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
||||
// since it's only inserted into the tab strip after it's selected).
|
||||
@@ -79,7 +81,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
winData.title = tabbrowser.tabs[0].label;
|
||||
}
|
||||
winData.selected = selectedIndex;
|
||||
@@ -5569,8 +5570,8 @@ var SessionStoreInternal = {
|
||||
@@ -5569,8 +5572,8 @@ var SessionStoreInternal = {
|
||||
// selectTab represents.
|
||||
let selectTab = 0;
|
||||
if (overwriteTabs) {
|
||||
@@ -90,7 +92,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
selectTab = Math.min(selectTab, winData.tabs.length);
|
||||
}
|
||||
|
||||
@@ -5613,6 +5614,7 @@ var SessionStoreInternal = {
|
||||
@@ -5613,6 +5616,7 @@ var SessionStoreInternal = {
|
||||
winData.tabs,
|
||||
winData.groups ?? []
|
||||
);
|
||||
@@ -98,12 +100,13 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
this._log.debug(
|
||||
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
|
||||
);
|
||||
@@ -6162,8 +6164,23 @@ var SessionStoreInternal = {
|
||||
@@ -6162,6 +6166,22 @@ var SessionStoreInternal = {
|
||||
|
||||
// Most of tabData has been restored, now continue with restoring
|
||||
// attributes that may trigger external events.
|
||||
+ if (tabData.zenEssential) {
|
||||
+ tab.setAttribute("zen-essential", "true");
|
||||
+ tabData.pinned = true; // Essential tabs are always pinned.
|
||||
+ }
|
||||
+ if (tabData.zenIsEmpty) {
|
||||
+ tab.setAttribute("zen-empty-tab", "true");
|
||||
@@ -118,8 +121,5 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||
+ tab.setAttribute("zenDefaultUserContextId", true);
|
||||
+ }
|
||||
|
||||
- if (tabData.pinned) {
|
||||
+ if (tabData.pinned || tabData.zenEssential) {
|
||||
if (tabData.pinned) {
|
||||
tabbrowser.pinTab(tab);
|
||||
} else {
|
||||
tabbrowser.unpinTab(tab);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
||||
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..254849e13f7566029dc780c45e376e0f0d427cb5 100644
|
||||
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..76f4cf5aef30cb580ef0295fe6928b5a6a362f4b 100644
|
||||
--- a/browser/components/sessionstore/TabState.sys.mjs
|
||||
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
||||
@@ -84,6 +84,16 @@ class _TabState {
|
||||
@@ -84,6 +84,18 @@ class _TabState {
|
||||
tabData.groupId = tab.group.id;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..254849e13f7566029dc780c45e376e0f
|
||||
+ tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon");
|
||||
+ tabData.zenIsEmpty = tab.hasAttribute("zen-empty-tab");
|
||||
+ tabData.zenHasStaticLabel = tab.hasAttribute("zen-has-static-label");
|
||||
+ tabData.zenGlanceId = tab.getAttribute("glance-id");
|
||||
+ tabData.zenIsGlance = tab.hasAttribute("zen-glance-tab");
|
||||
+
|
||||
tabData.searchMode = tab.ownerGlobal.gURLBar.getSearchMode(browser, true);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b032cf200f1 100644
|
||||
index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b598a513c2 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -413,11 +413,41 @@
|
||||
@@ -292,37 +292,17 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
|
||||
let url = "about:blank";
|
||||
if (tabData.entries?.length) {
|
||||
@@ -3598,7 +3675,29 @@
|
||||
@@ -3598,7 +3675,8 @@
|
||||
skipLoad: true,
|
||||
preferredRemoteType,
|
||||
});
|
||||
-
|
||||
+ tab._originalUrl = url;
|
||||
|
||||
+ if (tabData.zenWorkspace) {
|
||||
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
|
||||
+ }
|
||||
+ if (tabData.zenPinnedId) {
|
||||
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
|
||||
+ }
|
||||
+ if (tabData.zenIsEmpty) {
|
||||
+ tab.setAttribute("zen-empty-tab", "true");
|
||||
+ }
|
||||
+ if (tabData.zenHasStaticLabel) {
|
||||
+ tab.setAttribute("zen-has-static-label", "true");
|
||||
+ }
|
||||
+ if (tabData.zenEssential) {
|
||||
+ tab.setAttribute("zen-essential", "true");
|
||||
+ }
|
||||
+ if (tabData.zenDefaultUserContextId) {
|
||||
+ tab.setAttribute("zenDefaultUserContextId", "true");
|
||||
+ }
|
||||
+ if (tabData.zenPinnedEntry) {
|
||||
+ tab.setAttribute("zen-pinned-entry", tabData.zenPinnedEntry);
|
||||
+ }
|
||||
+ gZenSessionStore.restoreInitialTabData(tab, tabData);
|
||||
if (select) {
|
||||
tabToSelect = tab;
|
||||
}
|
||||
@@ -3622,7 +3721,8 @@
|
||||
@@ -3622,7 +3700,8 @@
|
||||
// needs calling:
|
||||
shouldUpdateForPinnedTabs = true;
|
||||
}
|
||||
@@ -332,7 +312,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
let { groupId } = tabData;
|
||||
const tabGroup = tabGroupWorkingData.get(groupId);
|
||||
// if a tab refers to a tab group we don't know, skip any group
|
||||
@@ -3636,7 +3736,10 @@
|
||||
@@ -3636,7 +3715,10 @@
|
||||
tabGroup.stateData.id,
|
||||
tabGroup.stateData.color,
|
||||
tabGroup.stateData.collapsed,
|
||||
@@ -344,7 +324,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
);
|
||||
tabsFragment.appendChild(tabGroup.node);
|
||||
}
|
||||
@@ -3684,8 +3787,16 @@
|
||||
@@ -3684,8 +3766,16 @@
|
||||
// to remove the old selected tab.
|
||||
if (tabToSelect) {
|
||||
let leftoverTab = this.selectedTab;
|
||||
@@ -363,7 +343,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
}
|
||||
|
||||
if (tabs.length > 1 || !tabs[0].selected) {
|
||||
@@ -3881,7 +3992,7 @@
|
||||
@@ -3881,7 +3971,7 @@
|
||||
// Ensure we have an index if one was not provided.
|
||||
if (typeof elementIndex != "number" && typeof tabIndex != "number") {
|
||||
// Move the new tab after another tab if needed, to the end otherwise.
|
||||
@@ -372,7 +352,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
if (
|
||||
!bulkOrderedOpen &&
|
||||
((openerTab &&
|
||||
@@ -3904,7 +4015,7 @@
|
||||
@@ -3904,7 +3994,7 @@
|
||||
) {
|
||||
elementIndex = Infinity;
|
||||
} else if (previousTab.visible) {
|
||||
@@ -381,7 +361,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
} else if (previousTab == FirefoxViewHandler.tab) {
|
||||
elementIndex = 0;
|
||||
}
|
||||
@@ -3932,10 +4043,10 @@
|
||||
@@ -3932,14 +4022,14 @@
|
||||
}
|
||||
// Ensure index is within bounds.
|
||||
if (tab.pinned) {
|
||||
@@ -395,7 +375,12 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
index = Math.min(index, allItems.length);
|
||||
}
|
||||
/** @type {MozTabbrowserTab|undefined} */
|
||||
@@ -3947,7 +4058,7 @@
|
||||
- let itemAfter = allItems.at(index);
|
||||
+ let itemAfter = gZenGlanceManager.getTabOrGlanceParent(allItems.at(index));
|
||||
|
||||
// Prevent a flash of unstyled content by setting up the tab content
|
||||
// and inherited attributes before appending it (see Bug 1592054):
|
||||
@@ -3947,7 +4037,7 @@
|
||||
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
|
||||
@@ -404,7 +389,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
if (this.isTab(itemAfter) && itemAfter.group == tabGroup) {
|
||||
// Place at the front of, or between tabs in, the same tab group
|
||||
this.tabContainer.insertBefore(tab, itemAfter);
|
||||
@@ -4268,6 +4379,9 @@
|
||||
@@ -4268,6 +4358,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -414,7 +399,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
this.removeTabs(selectedTabs, { telemetrySource });
|
||||
}
|
||||
|
||||
@@ -4520,6 +4634,7 @@
|
||||
@@ -4520,6 +4613,7 @@
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
@@ -422,7 +407,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||
// can be considered equivalent to closing the window.
|
||||
if (
|
||||
@@ -4604,6 +4719,7 @@
|
||||
@@ -4604,6 +4698,7 @@
|
||||
if (lastToClose) {
|
||||
this.removeTab(lastToClose, aParams);
|
||||
}
|
||||
@@ -430,7 +415,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -4641,6 +4757,12 @@
|
||||
@@ -4641,6 +4736,12 @@
|
||||
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
||||
}
|
||||
|
||||
@@ -443,7 +428,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate && aTab.closing) {
|
||||
@@ -4655,7 +4777,9 @@
|
||||
@@ -4655,7 +4756,9 @@
|
||||
// frame created for it (for example, by updating the visually selected
|
||||
// state).
|
||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
@@ -454,7 +439,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -4821,7 +4945,7 @@
|
||||
@@ -4821,7 +4924,7 @@
|
||||
closeWindowWithLastTab != null
|
||||
? closeWindowWithLastTab
|
||||
: !window.toolbar.visible ||
|
||||
@@ -463,7 +448,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
|
||||
if (closeWindow) {
|
||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||
@@ -4845,6 +4969,7 @@
|
||||
@@ -4845,6 +4948,7 @@
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
@@ -471,7 +456,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -4885,9 +5010,7 @@
|
||||
@@ -4885,9 +4989,7 @@
|
||||
aTab._mouseleave();
|
||||
|
||||
if (newTab) {
|
||||
@@ -482,7 +467,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
} else {
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -5016,6 +5139,8 @@
|
||||
@@ -5016,6 +5118,8 @@
|
||||
this.tabs[i]._tPos = i;
|
||||
}
|
||||
|
||||
@@ -491,7 +476,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
if (!this._windowIsClosing) {
|
||||
if (wasPinned) {
|
||||
this.tabContainer._positionPinnedTabs();
|
||||
@@ -5230,6 +5355,7 @@
|
||||
@@ -5230,6 +5334,7 @@
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
@@ -499,7 +484,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
|
||||
// If this tab has a successor, it should be selectable, since
|
||||
// hiding or closing a tab removes that tab as a successor.
|
||||
@@ -5242,13 +5368,13 @@
|
||||
@@ -5242,13 +5347,13 @@
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
@@ -515,7 +500,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
);
|
||||
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
@@ -5264,7 +5390,7 @@
|
||||
@@ -5264,7 +5369,7 @@
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
@@ -524,7 +509,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
}
|
||||
|
||||
// If no qualifying visible tab was found, see if there is a tab in
|
||||
@@ -5285,7 +5411,7 @@
|
||||
@@ -5285,7 +5390,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
@@ -533,7 +518,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
}
|
||||
|
||||
_blurTab(aTab) {
|
||||
@@ -5686,10 +5812,10 @@
|
||||
@@ -5686,10 +5791,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
}
|
||||
|
||||
@@ -546,7 +531,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -5986,7 +6112,7 @@
|
||||
@@ -5986,7 +6091,7 @@
|
||||
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
if (this.isTab(element) && element.pinned) {
|
||||
@@ -555,7 +540,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -6012,10 +6138,16 @@
|
||||
@@ -6012,10 +6117,16 @@
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
@@ -574,7 +559,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||
neighbor.after(element);
|
||||
} else {
|
||||
@@ -6084,17 +6216,26 @@
|
||||
@@ -6084,17 +6195,29 @@
|
||||
targetElement = targetElement.group;
|
||||
}
|
||||
}
|
||||
@@ -583,8 +568,12 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
+ element = element.group;
|
||||
+ }
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
if (element.pinned && !targetElement?.pinned) {
|
||||
- if (element.pinned && !targetElement?.pinned) {
|
||||
- targetElement = this.tabs[this.pinnedTabCount - 1];
|
||||
+ if (element.hasAttribute('zen-essential') && !targetElement?.hasAttribute('zen-essential')) {
|
||||
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this._numZenEssentials - 1];
|
||||
+ moveBefore = false;
|
||||
+ } else if (element.pinned && !targetElement?.pinned) {
|
||||
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this.pinnedTabCount - 1];
|
||||
moveBefore = false;
|
||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||
@@ -604,7 +593,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
if (element.pinned && this.tabContainer.verticalMode) {
|
||||
return this.tabContainer.verticalPinnedTabsContainer;
|
||||
}
|
||||
@@ -6154,7 +6295,7 @@
|
||||
@@ -6154,7 +6277,7 @@
|
||||
if (!this.isTab(aTab)) {
|
||||
throw new Error("Can only move a tab into a tab group");
|
||||
}
|
||||
@@ -613,7 +602,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
return;
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
@@ -6248,6 +6389,10 @@
|
||||
@@ -6248,6 +6371,10 @@
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
@@ -624,7 +613,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -7145,7 +7290,7 @@
|
||||
@@ -7145,7 +7272,7 @@
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
break;
|
||||
}
|
||||
@@ -633,7 +622,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
window.focus();
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
@@ -8044,6 +8189,7 @@
|
||||
@@ -8044,6 +8171,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
@@ -641,7 +630,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
}
|
||||
@@ -9009,7 +9155,7 @@ var TabContextMenu = {
|
||||
@@ -9009,7 +9137,7 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !this.multiselected;
|
||||
@@ -650,7 +639,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||
// Move Tab items
|
||||
let contextMoveTabOptions = document.getElementById(
|
||||
"context_moveTabOptions"
|
||||
@@ -9278,6 +9424,7 @@ var TabContextMenu = {
|
||||
@@ -9278,6 +9406,7 @@ var TabContextMenu = {
|
||||
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||
});
|
||||
} else {
|
||||
|
||||
13
src/browser/components/tabbrowser/content/tabgroup-js.patch
Normal file
13
src/browser/components/tabbrowser/content/tabgroup-js.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
|
||||
index 6dc774ea335b0c5dba7dcf76cdb23728faae1343..b0b9ef236c2e8517db4bcf3270596456bbefe11d 100644
|
||||
--- a/browser/components/tabbrowser/content/tabgroup.js
|
||||
+++ b/browser/components/tabbrowser/content/tabgroup.js
|
||||
@@ -301,7 +301,7 @@
|
||||
*/
|
||||
addTabs(tabs, metricsContext) {
|
||||
for (let tab of tabs) {
|
||||
- if (tab.pinned) {
|
||||
+ if (tab.pinned !== this.pinned) {
|
||||
tab.ownerGlobal.gBrowser.unpinTab(tab);
|
||||
}
|
||||
let tabToMove =
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||
index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea9596403cef 100644
|
||||
index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c574dfc103 100644
|
||||
--- a/browser/components/tabbrowser/content/tabs.js
|
||||
+++ b/browser/components/tabbrowser/content/tabs.js
|
||||
@@ -83,7 +83,7 @@
|
||||
@@ -46,6 +46,15 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
expandGroupOnDrop = true;
|
||||
}
|
||||
}
|
||||
@@ -868,7 +869,7 @@
|
||||
? event.screenY - window.screenY - tabOffset
|
||||
: event.screenY - window.screenY,
|
||||
scrollPos:
|
||||
- this.verticalMode && tab.pinned
|
||||
+ this.verticalMode && tab.pinned && false
|
||||
? this.verticalPinnedTabsContainer.scrollPosition
|
||||
: this.arrowScrollbox.scrollPosition,
|
||||
screenX: event.screenX,
|
||||
@@ -921,6 +922,10 @@
|
||||
}
|
||||
|
||||
@@ -91,16 +100,23 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
);
|
||||
let size = this.verticalMode ? "height" : "width";
|
||||
let screenAxis = this.verticalMode ? "screenY" : "screenX";
|
||||
@@ -1211,7 +1229,7 @@
|
||||
item.removeAttribute("tabdrop-samewindow");
|
||||
resolve();
|
||||
};
|
||||
- if (gReduceMotion) {
|
||||
+ if (true || gReduceMotion) {
|
||||
postTransitionCleanup();
|
||||
} else {
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -1337,6 +1355,7 @@
|
||||
@@ -1135,8 +1153,14 @@
|
||||
(lastMovingTabScreen + tabSize);
|
||||
|
||||
if (this.verticalMode) {
|
||||
+ if (oldTranslateY > 0 && translateOffsetY > tabHeight / 2) {
|
||||
+ newTranslateY += tabHeight;
|
||||
+ }
|
||||
+ if (oldTranslateY < 0 && -translateOffsetY > tabHeight / 2) {
|
||||
+ newTranslateY -= tabHeight;
|
||||
+ }
|
||||
newTranslateY = Math.min(
|
||||
- Math.max(oldTranslateY, firstBound),
|
||||
+ Math.max(newTranslateY, firstBound),
|
||||
lastBound
|
||||
);
|
||||
} else {
|
||||
@@ -1337,6 +1361,7 @@
|
||||
|
||||
let nextItem = this.ariaFocusableItems[newIndex];
|
||||
let tabGroup = isTab(nextItem) && nextItem.group;
|
||||
@@ -108,7 +124,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
gBrowser.loadTabs(urls, {
|
||||
inBackground,
|
||||
replace,
|
||||
@@ -1369,6 +1388,17 @@
|
||||
@@ -1369,6 +1394,17 @@
|
||||
|
||||
this.finishMoveTogetherSelectedTabs(draggedTab);
|
||||
this.finishAnimateTabMove();
|
||||
@@ -126,7 +142,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
this.#expandGroupOnDrop(draggedTab);
|
||||
|
||||
if (
|
||||
@@ -1597,7 +1627,7 @@
|
||||
@@ -1597,7 +1633,7 @@
|
||||
}
|
||||
|
||||
get newTabButton() {
|
||||
@@ -135,7 +151,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
}
|
||||
|
||||
get verticalMode() {
|
||||
@@ -1621,29 +1651,54 @@
|
||||
@@ -1621,29 +1657,54 @@
|
||||
if (this.#allTabs) {
|
||||
return this.#allTabs;
|
||||
}
|
||||
@@ -179,7 +195,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
+ if (glanceTab) {
|
||||
+ // insert right after the parent tab. note: it must be inserted before
|
||||
+ // the last pinned tab so it can be inserted in the correct order
|
||||
+ allTabs.splice(Math.max(i++, lastPinnedTabIdx), 0, glanceTab);
|
||||
+ allTabs.splice(Math.max(i++ + 1, lastPinnedTabIdx), 0, glanceTab);
|
||||
+ } else if (tab.classList.contains("vertical-pinned-tabs-container-separator")) {
|
||||
+ // remove the separator from the list
|
||||
+ allTabs.splice(i, 1);
|
||||
@@ -198,7 +214,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1698,23 +1753,18 @@
|
||||
@@ -1698,23 +1759,18 @@
|
||||
}
|
||||
|
||||
let elementIndex = 0;
|
||||
@@ -226,7 +242,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
|
||||
visibleTabsInGroup.forEach(tab => {
|
||||
tab.elementIndex = elementIndex++;
|
||||
@@ -1724,10 +1774,7 @@
|
||||
@@ -1724,10 +1780,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +254,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
|
||||
return this.#focusableItems;
|
||||
}
|
||||
@@ -1735,6 +1782,7 @@
|
||||
@@ -1735,6 +1788,7 @@
|
||||
_invalidateCachedTabs() {
|
||||
this.#allTabs = null;
|
||||
this._invalidateCachedVisibleTabs();
|
||||
@@ -246,7 +262,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
}
|
||||
|
||||
_invalidateCachedVisibleTabs() {
|
||||
@@ -1749,8 +1797,8 @@
|
||||
@@ -1749,8 +1803,8 @@
|
||||
#isContainerVerticalPinnedGrid(tab) {
|
||||
return (
|
||||
this.verticalMode &&
|
||||
@@ -257,7 +273,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
!this.expandOnHover
|
||||
);
|
||||
}
|
||||
@@ -1766,7 +1814,7 @@
|
||||
@@ -1766,7 +1820,7 @@
|
||||
|
||||
if (node == null) {
|
||||
// We have a container for non-tab elements at the end of the scrollbox.
|
||||
@@ -266,7 +282,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
}
|
||||
|
||||
node.before(tab);
|
||||
@@ -1861,7 +1909,7 @@
|
||||
@@ -1861,7 +1915,7 @@
|
||||
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
|
||||
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
|
||||
// Attach the long click popup to all of them.
|
||||
@@ -275,7 +291,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
const newTab2 = this.newTabButton;
|
||||
const newTabVertical = document.getElementById(
|
||||
"vertical-tabs-newtab-button"
|
||||
@@ -1956,10 +2004,12 @@
|
||||
@@ -1956,10 +2010,12 @@
|
||||
|
||||
_handleTabSelect(aInstant) {
|
||||
let selectedTab = this.selectedItem;
|
||||
@@ -288,7 +304,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
selectedTab._notselectedsinceload = false;
|
||||
}
|
||||
|
||||
@@ -2132,6 +2182,7 @@
|
||||
@@ -2132,6 +2188,7 @@
|
||||
}
|
||||
|
||||
_positionPinnedTabs() {
|
||||
@@ -296,7 +312,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
let tabs = this.visibleTabs;
|
||||
let numPinned = gBrowser.pinnedTabCount;
|
||||
let absPositionHorizontalTabs =
|
||||
@@ -2206,7 +2257,7 @@
|
||||
@@ -2206,7 +2263,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -305,16 +321,25 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
|
||||
let directionX = screenX > dragData.animLastScreenX;
|
||||
let directionY = screenY > dragData.animLastScreenY;
|
||||
@@ -2214,7 +2265,7 @@
|
||||
dragData.animLastScreenX = screenX;
|
||||
@@ -2215,6 +2272,8 @@
|
||||
|
||||
let { width: tabWidth, height: tabHeight } =
|
||||
- draggedTab.getBoundingClientRect();
|
||||
+ (draggedTab.group?.hasAttribute("split-view-group") ? draggedTab.group : draggedTab).getBoundingClientRect();
|
||||
draggedTab.getBoundingClientRect();
|
||||
+ tabWidth += 4; // Add 4px to account for the gap
|
||||
+ tabHeight += 4;
|
||||
let shiftSizeX = tabWidth * movingTabs.length;
|
||||
let shiftSizeY = tabHeight;
|
||||
dragData.tabWidth = tabWidth;
|
||||
@@ -2389,12 +2440,16 @@
|
||||
@@ -2244,7 +2303,7 @@
|
||||
let translateX = screenX - dragData.screenX;
|
||||
let translateY = screenY - dragData.screenY;
|
||||
translateY +=
|
||||
- this.verticalPinnedTabsContainer.scrollPosition - dragData.scrollPos;
|
||||
+ dragData.scrollPos;
|
||||
let firstBoundX = firstTabInRow.screenX - firstMovingTabScreenX;
|
||||
let firstBoundY = firstTabInRow.screenY - firstMovingTabScreenY;
|
||||
let lastBoundX =
|
||||
@@ -2389,12 +2448,16 @@
|
||||
|
||||
this.#clearDragOverCreateGroupTimer();
|
||||
|
||||
@@ -335,7 +360,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
|
||||
if (this.#rtlMode) {
|
||||
tabs.reverse();
|
||||
@@ -2408,7 +2463,7 @@
|
||||
@@ -2408,7 +2471,7 @@
|
||||
let size = this.verticalMode ? "height" : "width";
|
||||
let translateAxis = this.verticalMode ? "translateY" : "translateX";
|
||||
let scrollDirection = this.verticalMode ? "scrollTop" : "scrollLeft";
|
||||
@@ -344,7 +369,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
let translateX = event.screenX - dragData.screenX;
|
||||
let translateY = event.screenY - dragData.screenY;
|
||||
|
||||
@@ -2422,12 +2477,21 @@
|
||||
@@ -2422,12 +2485,21 @@
|
||||
let lastTab = tabs.at(-1);
|
||||
let lastMovingTab = movingTabs.at(-1);
|
||||
let firstMovingTab = movingTabs[0];
|
||||
@@ -367,7 +392,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
translate +=
|
||||
this.arrowScrollbox.scrollbox[scrollDirection] - dragData.scrollPos;
|
||||
} else if (isPinned && this.verticalMode) {
|
||||
@@ -2446,6 +2510,9 @@
|
||||
@@ -2446,6 +2518,9 @@
|
||||
// Shift the `.tab-group-label-container` to shift the label element.
|
||||
item = item.parentElement;
|
||||
}
|
||||
@@ -377,7 +402,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
item.style.transform = `${translateAxis}(${translate}px)`;
|
||||
}
|
||||
|
||||
@@ -2583,6 +2650,9 @@
|
||||
@@ -2583,6 +2658,9 @@
|
||||
break;
|
||||
}
|
||||
let element = tabs[mid];
|
||||
@@ -387,7 +412,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
let elementForSize = isTabGroupLabel(element)
|
||||
? element.parentElement
|
||||
: element;
|
||||
@@ -2605,6 +2675,10 @@
|
||||
@@ -2605,6 +2683,10 @@
|
||||
if (!dropElement) {
|
||||
dropElement = this.ariaFocusableItems[oldDropElementIndex];
|
||||
}
|
||||
@@ -398,7 +423,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
let newDropElementIndex = dropElement
|
||||
? dropElement.elementIndex
|
||||
: oldDropElementIndex;
|
||||
@@ -2613,7 +2687,7 @@
|
||||
@@ -2613,7 +2695,7 @@
|
||||
let shouldCreateGroupOnDrop;
|
||||
let dropBefore;
|
||||
if (dropElement) {
|
||||
@@ -407,7 +432,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
? dropElement.parentElement
|
||||
: dropElement;
|
||||
|
||||
@@ -2675,12 +2749,12 @@
|
||||
@@ -2675,12 +2757,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +447,16 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
dropElement != draggedTab &&
|
||||
isTab(dropElement) &&
|
||||
!dropElement?.group &&
|
||||
@@ -2750,7 +2824,7 @@
|
||||
@@ -2720,7 +2802,7 @@
|
||||
// Dropping right before the tab group.
|
||||
dropElement = dropElementGroup;
|
||||
colorCode = undefined;
|
||||
- } else if (dropElementGroup.collapsed) {
|
||||
+ } else if (dropElement?.group?.hasAttribute("split-view-group")) {
|
||||
// Dropping right after the collapsed tab group.
|
||||
dropElement = dropElementGroup;
|
||||
colorCode = undefined;
|
||||
@@ -2750,7 +2832,7 @@
|
||||
// Shift background tabs to leave a gap where the dragged tab
|
||||
// would currently be dropped.
|
||||
for (let item of tabs) {
|
||||
@@ -431,7 +465,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2759,6 +2833,9 @@
|
||||
@@ -2759,6 +2841,9 @@
|
||||
if (isTabGroupLabel(item)) {
|
||||
// Shift the `.tab-group-label-container` to shift the label element.
|
||||
item = item.parentElement;
|
||||
@@ -441,7 +475,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
}
|
||||
item.style.transform = transform;
|
||||
}
|
||||
@@ -2811,8 +2888,9 @@
|
||||
@@ -2811,8 +2896,9 @@
|
||||
);
|
||||
}
|
||||
|
||||
@@ -453,7 +487,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2824,6 +2902,12 @@
|
||||
@@ -2824,6 +2910,12 @@
|
||||
item = item.parentElement;
|
||||
}
|
||||
item.style.transform = "";
|
||||
@@ -466,7 +500,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
item.removeAttribute("dragover-createGroup");
|
||||
}
|
||||
this.removeAttribute("movingtab-createGroup");
|
||||
@@ -2870,7 +2954,7 @@
|
||||
@@ -2870,7 +2962,7 @@
|
||||
let postTransitionCleanup = () => {
|
||||
movingTab._moveTogetherSelectedTabsData.animate = false;
|
||||
};
|
||||
@@ -475,7 +509,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
postTransitionCleanup();
|
||||
} else {
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -3043,7 +3127,7 @@
|
||||
@@ -3043,7 +3135,7 @@
|
||||
}
|
||||
|
||||
_notifyBackgroundTab(aTab) {
|
||||
@@ -484,7 +518,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3169,6 +3253,9 @@
|
||||
@@ -3169,6 +3261,9 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||
index dfa91b76ad3890ceadb1b1b5d7a63b7074fbb776..6369fa1cdb242de32338bbce6debcdab2a04ca02 100644
|
||||
--- a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||
@@ -585,6 +585,7 @@ export class UrlbarValueFormatter {
|
||||
this.window.requestAnimationFrame(() => {
|
||||
if (instance == this._resizeInstance) {
|
||||
this.#ensureFormattedHostVisible();
|
||||
+ this._formatURL();
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
@@ -60,26 +60,6 @@
|
||||
list-style-image: url('sidebars-right.svg') !important;
|
||||
}
|
||||
|
||||
#zen-library-sidebar-workspaces {
|
||||
list-style-image: url('duplicate-tab.svg');
|
||||
}
|
||||
|
||||
#zen-library-sidebar-mods {
|
||||
list-style-image: url('edit.svg');
|
||||
}
|
||||
|
||||
#context_zenSplitTabs {
|
||||
--menu-image: url('sidebars-right.svg') !important;
|
||||
}
|
||||
|
||||
#context-zen-change-workspace-tab {
|
||||
--menu-image: url('move-tab.svg') !important;
|
||||
}
|
||||
|
||||
#context-zenSplitLink {
|
||||
--menu-image: url('split.svg') !important;
|
||||
}
|
||||
|
||||
#sidebar-button:-moz-locale-dir(ltr):not([positionend]),
|
||||
#sidebar-button:-moz-locale-dir(rtl)[positionend] {
|
||||
list-style-image: url('chrome://browser/skin/sidebars.svg') !important;
|
||||
@@ -127,7 +107,6 @@
|
||||
|
||||
#PanelUI-menu-button,
|
||||
#appMenu-more-button2,
|
||||
.zen-workspaces-actions,
|
||||
#zen-workspace-actions-menu-icon {
|
||||
list-style-image: url('menu.svg') !important;
|
||||
}
|
||||
@@ -142,6 +121,10 @@
|
||||
list-style-image: url('tab.svg') !important;
|
||||
}
|
||||
|
||||
#context-navigation > menuitem {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#history-panelmenu,
|
||||
.urlbarView-row[source='history']
|
||||
> .urlbarView-row-inner
|
||||
@@ -182,10 +165,6 @@
|
||||
list-style-image: url('open.svg') !important;
|
||||
}
|
||||
|
||||
.zenToolbarThemePicker {
|
||||
--menu-image: url('edit-theme.svg') !important;
|
||||
}
|
||||
|
||||
#add-ons-button,
|
||||
#appMenu-extensions-themes-button,
|
||||
#unified-extensions-button {
|
||||
@@ -203,10 +182,16 @@
|
||||
#appMenu-zoomEnlarge-button2,
|
||||
#PanelUI-zen-profiles-newProfile,
|
||||
#zen-sidebar-add-panel-button,
|
||||
#PanelUI-zen-workspaces-new image,
|
||||
#PanelUI-zen-gradient-generator-color-custom-add image {
|
||||
list-style-image: url('plus.svg') !important;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-reorder-mode image {
|
||||
list-style-image: url('move-tab.svg') !important;
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
#cut-button {
|
||||
list-style-image: url('edit-cut.svg') !important;
|
||||
}
|
||||
@@ -301,8 +286,12 @@
|
||||
list-style-image: url('home.svg') !important;
|
||||
}
|
||||
|
||||
#library-button,
|
||||
#zen-open-library {
|
||||
#toggle_toolbar-menubar,
|
||||
#appMenu_menu_openHelp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#library-button {
|
||||
list-style-image: url('library.svg') !important;
|
||||
}
|
||||
|
||||
@@ -446,6 +435,11 @@
|
||||
|
||||
#zen-glance-sidebar-split {
|
||||
list-style-image: url('split.svg');
|
||||
|
||||
&[disabled='true'] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar-box[sidebarcommand='viewTabsSidebar']
|
||||
@@ -634,372 +628,6 @@
|
||||
list-style-image: url('manage.svg') !important;
|
||||
}
|
||||
|
||||
/* Context Menu Icons */
|
||||
|
||||
#context-video-pictureinpicture:not([checked='true']) .menu-iconic-icon {
|
||||
list-style-image: url('media-pip.svg') !important;
|
||||
}
|
||||
|
||||
#context-media-loop:not([checked='true']) .menu-iconic-icon {
|
||||
list-style-image: url('media-loop.svg') !important;
|
||||
}
|
||||
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menuitem:not(
|
||||
.menuitem-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menuitem,
|
||||
.unified-nav-current
|
||||
),
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menu:not(
|
||||
.menu-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menu,
|
||||
.unified-nav-current
|
||||
),
|
||||
#toggle_toolbar-menubar,
|
||||
#PanelUI-history toolbarbutton,
|
||||
#unified-extensions-context-menu menuitem {
|
||||
background-image: var(--menu-image) !important;
|
||||
background-size: 16px !important;
|
||||
background-position: var(--zen-contextmenu-menuitem-padding-inline) center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
-moz-context-properties: fill, fill-opacity !important;
|
||||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
@media not (-moz-platform: windows) {
|
||||
menu > .menu-iconic-text,
|
||||
menuitem > .menu-iconic-text {
|
||||
padding-inline-start: var(--zen-contextmenu-menuicon-margin-inline) !important;
|
||||
}
|
||||
}
|
||||
|
||||
#context-savepage {
|
||||
--menu-image: url('save.svg');
|
||||
}
|
||||
|
||||
#context-selectall,
|
||||
.textbox-contextmenu menuitem[cmd*='selectAll'],
|
||||
#context_selectAllTabs,
|
||||
#toolbar-context-selectAllTabs {
|
||||
--menu-image: url('edit-select-all.svg');
|
||||
}
|
||||
|
||||
#context-undo,
|
||||
.textbox-contextmenu menuitem[cmd*='undo'],
|
||||
#context_undoCloseTab,
|
||||
#toolbar-context-undoCloseTab {
|
||||
--menu-image: url('edit-undo.svg');
|
||||
}
|
||||
|
||||
#toggle_toolbar-menubar {
|
||||
--menu-image: url('menu-bar.svg');
|
||||
}
|
||||
|
||||
#context-redo,
|
||||
.textbox-contextmenu menuitem[cmd*='redo'] {
|
||||
--menu-image: url('edit-redo.svg');
|
||||
}
|
||||
|
||||
#context-copy,
|
||||
.textbox-contextmenu menuitem[cmd*='copy'],
|
||||
.textbox-contextmenu #strip-on-share,
|
||||
#placesContext_copy {
|
||||
--menu-image: url('edit-copy.svg');
|
||||
}
|
||||
|
||||
#context-paste,
|
||||
.textbox-contextmenu menuitem[cmd*='paste'],
|
||||
#placesContext_paste_group {
|
||||
--menu-image: url('edit-paste.svg');
|
||||
}
|
||||
|
||||
#context-cut,
|
||||
.textbox-contextmenu menuitem[cmd*='cut'],
|
||||
#placesContext_cut {
|
||||
--menu-image: url('edit-cut.svg');
|
||||
}
|
||||
|
||||
#context-delete,
|
||||
.customize-context-removeExtension,
|
||||
.unified-extensions-context-menu-remove-extension,
|
||||
.textbox-contextmenu menuitem[cmd*='delete'],
|
||||
menuitem[id='placesContext_deleteBookmark'],
|
||||
menuitem[id='placesContext_deleteFolder'],
|
||||
menuitem[id='placesContext_delete'],
|
||||
menuitem[id='placesContext_delete_history'],
|
||||
menuitem[id='placesContext_deleteHost'],
|
||||
#context_zenDeleteWebPanel,
|
||||
#context_zenDeleteWorkspace {
|
||||
--menu-image: url('edit-delete.svg');
|
||||
}
|
||||
|
||||
#paste-and-go {
|
||||
--menu-image: url('paste-and-go.svg');
|
||||
}
|
||||
|
||||
#context-print-selection {
|
||||
--menu-image: url('print.svg');
|
||||
}
|
||||
|
||||
#context-take-screenshot {
|
||||
--menu-image: url('screenshot.svg');
|
||||
}
|
||||
|
||||
#context-viewsource {
|
||||
--menu-image: url('source-code.svg');
|
||||
}
|
||||
|
||||
#context-inspect-a11y {
|
||||
--menu-image: url('accessibility.svg');
|
||||
}
|
||||
|
||||
#context-inspect {
|
||||
--menu-image: url('inspect.svg');
|
||||
}
|
||||
|
||||
#context-searchselect {
|
||||
--menu-image: url('search-glass.svg');
|
||||
}
|
||||
|
||||
#context-viewimage {
|
||||
--menu-image: url('image-open.svg');
|
||||
}
|
||||
|
||||
#context-viewimageinfo {
|
||||
--menu-image: url('info.svg');
|
||||
}
|
||||
|
||||
#context-saveimage,
|
||||
#context-video-saveimage {
|
||||
--menu-image: url('image-save.svg');
|
||||
}
|
||||
|
||||
#context-savevideo {
|
||||
--menu-image: url('video-save.svg');
|
||||
}
|
||||
|
||||
#context-viewvideo {
|
||||
--menu-image: url('video-open.svg');
|
||||
}
|
||||
|
||||
#context-saveaudio {
|
||||
--menu-image: url('audio-save.svg');
|
||||
}
|
||||
|
||||
#context-copyimage-contents {
|
||||
--menu-image: url('image-copy.svg');
|
||||
}
|
||||
|
||||
#context-copyimage,
|
||||
#context-copyvideourl,
|
||||
#context-copylink,
|
||||
#context-stripOnShareLink,
|
||||
#context_zenOpenNewTabWebPanel,
|
||||
#context-pdfjs-copy {
|
||||
--menu-image: url('link.svg');
|
||||
}
|
||||
|
||||
#context-openlinkincurrent {
|
||||
--menu-image: url('ext-link.svg');
|
||||
}
|
||||
|
||||
#context-viewsource,
|
||||
#context-viewframesource,
|
||||
#context-viewpartialsource-selection {
|
||||
--menu-image: url('source-code.svg');
|
||||
}
|
||||
|
||||
#context-sendimage,
|
||||
#context-sendvideo,
|
||||
#context-sendaudio {
|
||||
--menu-image: url('mail.svg');
|
||||
}
|
||||
|
||||
#context-setDesktopBackground,
|
||||
.viewCustomizeToolbar {
|
||||
--menu-image: url('customize.svg');
|
||||
}
|
||||
|
||||
#context-reloadimage,
|
||||
#context_reloadTab,
|
||||
#context_reloadSelectedTabs,
|
||||
#toolbar-context-reloadSelectedTab,
|
||||
#toolbar-context-reloadSelectedTabs,
|
||||
#context_zen-reset-pinned-tab {
|
||||
--menu-image: url('reload.svg');
|
||||
}
|
||||
|
||||
#context-sendlinktodevice,
|
||||
#context_sendTabToDevice,
|
||||
#context-sendpagetodevice {
|
||||
--menu-image: url('send-to-device.svg');
|
||||
}
|
||||
|
||||
#context-openlinkintab,
|
||||
#context-openlinkincontainertab,
|
||||
#context_zenWorkspacesOpenInContainerTab,
|
||||
#context_zenWebPanelContextInContainer,
|
||||
menuitem[id='placesContext_open:newtab'],
|
||||
menuitem[id='placesContext_openLinks:tabs'],
|
||||
menuitem[id='placesContext_openBookmarkLinks:tabs'],
|
||||
menuitem[id='placesContext_openBookmarkContainer:tabs'] {
|
||||
--menu-image: url('tab.svg');
|
||||
}
|
||||
|
||||
#context_openANewTab,
|
||||
#toolbar-context-openANewTab {
|
||||
--menu-image: url('new-tab-image.svg');
|
||||
}
|
||||
|
||||
#context-openlinkinusercontext-menu,
|
||||
menu[id='placesContext_open:newcontainertab'],
|
||||
menu[id='placesContext_openContainer:tabs'] {
|
||||
--menu-image: url('container-tab.svg');
|
||||
}
|
||||
|
||||
#context-openlink,
|
||||
menuitem[id='placesContext_open:newwindow'] {
|
||||
--menu-image: url('window.svg');
|
||||
}
|
||||
|
||||
#context-openlinkprivate,
|
||||
menuitem[id='placesContext_open:newprivatewindow'] {
|
||||
--menu-image: url('private-window.svg');
|
||||
}
|
||||
|
||||
#context-savelink {
|
||||
--menu-image: url('downloads.svg');
|
||||
}
|
||||
|
||||
#spell-add-to-dictionary {
|
||||
--menu-image: url('add-to-dictionary.svg');
|
||||
}
|
||||
|
||||
#manage-saved-logins {
|
||||
--menu-image: url('passwords.svg');
|
||||
}
|
||||
|
||||
#context-media-play,
|
||||
#context_playTab,
|
||||
#context_playSelectedTabs {
|
||||
--menu-image: url('media-play.svg');
|
||||
}
|
||||
|
||||
#context-media-pause {
|
||||
--menu-image: url('media-pause.svg');
|
||||
}
|
||||
|
||||
#context-media-mute,
|
||||
#context_toggleMuteTab,
|
||||
#context_toggleMuteSelectedTabs,
|
||||
#context_zenToggleMuteWebPanel {
|
||||
--menu-image: url('media-mute.svg');
|
||||
}
|
||||
|
||||
#context-media-unmute,
|
||||
#context_toggleMuteTab[muted],
|
||||
#context_toggleMuteSelectedTabs[muted],
|
||||
#context_zenToggleMuteWebPanel[muted] {
|
||||
--menu-image: url('media-unmute.svg');
|
||||
}
|
||||
|
||||
#context-media-playbackrate {
|
||||
--menu-image: url('media-speed.svg');
|
||||
}
|
||||
|
||||
#context-video-fullscreen {
|
||||
--menu-image: url('fullscreen.svg');
|
||||
}
|
||||
|
||||
#context-leave-dom-fullscreen,
|
||||
menuitem[contexttype='fullscreen'][label*='Exit'] {
|
||||
--menu-image: url('fullscreen-exit.svg');
|
||||
}
|
||||
|
||||
#context-media-hidecontrols,
|
||||
#context-media-showcontrols {
|
||||
--menu-image: url('permissions.svg');
|
||||
}
|
||||
|
||||
#context_pinTab,
|
||||
#context_unpinTab,
|
||||
#context_pinSelectedTabs,
|
||||
#context_unpinSelectedTabs,
|
||||
.customize-context-moveToPanel,
|
||||
#context_zen-replace-pinned-url-with-current {
|
||||
--menu-image: url('pin.svg');
|
||||
}
|
||||
|
||||
#context_zen-add-essential {
|
||||
--menu-image: url('essential-add.svg');
|
||||
}
|
||||
|
||||
#context_zen-remove-essential {
|
||||
--menu-image: url('essential-remove.svg');
|
||||
}
|
||||
|
||||
.customize-context-removeFromToolbar {
|
||||
--menu-image: url('unpin.svg');
|
||||
}
|
||||
|
||||
#zen-sidebar-web-panel-pinned[pinned='true'] {
|
||||
list-style-image: url('pin.svg') !important;
|
||||
}
|
||||
|
||||
#zen-sidebar-web-panel-pinned {
|
||||
list-style-image: url('unpin.svg') !important;
|
||||
}
|
||||
|
||||
#context_duplicateTab,
|
||||
#context_duplicateTabs {
|
||||
--menu-image: url('duplicate-tab.svg');
|
||||
}
|
||||
|
||||
#zen-context-menu-compact-mode {
|
||||
--menu-image: url('sidebar.svg');
|
||||
}
|
||||
|
||||
#context_bookmarkTab,
|
||||
#context_bookmarkSelectedTabs,
|
||||
#toggle_PersonalToolbar,
|
||||
#context-bookmarklink,
|
||||
#toolbar-context-bookmarkSelectedTab,
|
||||
#toolbar-context-bookmarkSelectedTabs {
|
||||
--menu-image: url('bookmark-hollow.svg');
|
||||
}
|
||||
|
||||
menuitem[id='placesContext_show_bookmark:info'],
|
||||
menuitem[id='placesContext_show_folder:info'],
|
||||
menuitem[id='placesContext_show:info'],
|
||||
#context_zenEditWorkspace {
|
||||
--menu-image: url('edit.svg');
|
||||
}
|
||||
|
||||
menuitem[id='placesContext_showAllBookmarks'],
|
||||
#BMB_bookmarksShowAllTop,
|
||||
#BMB_bookmarksShowAll,
|
||||
.customize-context-manageExtension,
|
||||
.unified-extensions-context-menu-manage-extension {
|
||||
--menu-image: url('manage.svg');
|
||||
}
|
||||
|
||||
#BMB_viewBookmarksSidebar {
|
||||
--menu-image: url('chrome://browser/skin/sidebars.svg');
|
||||
}
|
||||
|
||||
#BMB_searchBookmarks {
|
||||
--menu-image: url('search-page.svg');
|
||||
}
|
||||
|
||||
#appMenuRecentlyClosedTabs {
|
||||
list-style-image: url('container-tab.svg') !important;
|
||||
}
|
||||
@@ -1020,57 +648,12 @@ menuitem[id='placesContext_showAllBookmarks'],
|
||||
list-style-image: url('manage.svg') !important;
|
||||
}
|
||||
|
||||
menuitem[id='placesContext_new:bookmark'],
|
||||
menuitem[id='placesContext_new:folder'],
|
||||
menuitem[id='placesContext_new:separator'] {
|
||||
--menu-image: url('plus.svg');
|
||||
}
|
||||
|
||||
#context-savelinktopocket,
|
||||
#context-pocket {
|
||||
--menu-image: url('pocket-outline.svg');
|
||||
}
|
||||
|
||||
#context_moveTabOptions {
|
||||
--menu-image: url('move-tab.svg');
|
||||
}
|
||||
|
||||
.share-tab-url-item {
|
||||
--menu-image: url('share.svg');
|
||||
}
|
||||
|
||||
#context_reopenInContainer {
|
||||
--menu-image: url('container-tab.svg');
|
||||
}
|
||||
|
||||
#context_closeTab {
|
||||
--menu-image: url('close.svg');
|
||||
}
|
||||
|
||||
#context_closeTabOptions {
|
||||
--menu-image: url('close-all.svg');
|
||||
}
|
||||
|
||||
#context_unloadTab,
|
||||
#context_zenTabActions {
|
||||
--menu-image: url('close-all.svg');
|
||||
}
|
||||
|
||||
.customize-context-reportExtension,
|
||||
.unified-extensions-context-menu-report-extension {
|
||||
--menu-image: url('report.svg');
|
||||
}
|
||||
|
||||
/* FIX header icons for the app menu sub menus (eg. fx account, history...) */
|
||||
.panel-header > h1 {
|
||||
text-align: left;
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
|
||||
.wordmark::after {
|
||||
content: 'Plus' !important;
|
||||
}
|
||||
|
||||
/* header icons for the app menu sub menus (eg. fx account, history...) */
|
||||
.panel-header > h1 > span::before {
|
||||
content: '';
|
||||
@@ -1115,67 +698,10 @@ menuitem[id='placesContext_new:separator'] {
|
||||
--fp-enabled: 1;
|
||||
}
|
||||
|
||||
@media not (-moz-platform: linux) {
|
||||
.unified-extensions-context-menu-pin-to-toolbar {
|
||||
--menu-image: url('pin.svg');
|
||||
}
|
||||
}
|
||||
|
||||
.unified-extensions-context-menu-move-widget-down {
|
||||
--menu-image: url('arrow-down.svg');
|
||||
}
|
||||
|
||||
.unified-extensions-context-menu-move-widget-up {
|
||||
--menu-image: url('arrow-up.svg');
|
||||
}
|
||||
|
||||
#alltabs-button {
|
||||
list-style-image: url('chrome://browser/skin/tabs.svg') !important;
|
||||
}
|
||||
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menuitem:not(
|
||||
.menuitem-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menuitem,
|
||||
.unified-nav-current
|
||||
),
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
||||
> menupopup
|
||||
> menu:not(
|
||||
.menu-iconic,
|
||||
[type='checkbox'],
|
||||
[type='radio'],
|
||||
.in-menulist,
|
||||
.in-menulist menu,
|
||||
.unified-nav-current
|
||||
),
|
||||
:not(:not(menubar) > menu, #ContentSelectDropdown) > menupopup > menucaption {
|
||||
padding-inline-start: calc(
|
||||
var(--zen-contextmenu-menuitem-padding-inline) + var(--zen-contextmenu-menuicon-margin-inline) /
|
||||
2
|
||||
) !important;
|
||||
}
|
||||
|
||||
menupopup > menuitem:is([type='checkbox']) .menu-iconic-left {
|
||||
--menu-image: none !important;
|
||||
margin-inline-start: 4px;
|
||||
|
||||
@media not (-moz-platform: windows) {
|
||||
margin-inline-end: 0;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-platform: windows) {
|
||||
menupopup > menuitem[checked='true'] {
|
||||
padding-inline-start: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
#toolbar-context-toggle-vertical-tabs,
|
||||
#toolbar-context-customize-sidebar,
|
||||
#sidebarRevampSeparator {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
diff --git a/toolkit/themes/shared/menulist-shared.css b/toolkit/themes/shared/menulist-shared.css
|
||||
index 5dd5549674570170a694afbd9ea4526e52e3192a..187fcb57183df6a0ab3701ab79c46d86c5e984b3 100644
|
||||
index 5dd5549674570170a694afbd9ea4526e52e3192a..a24ded413065ce1493e8622c5777c5b5bdac128e 100644
|
||||
--- a/toolkit/themes/shared/menulist-shared.css
|
||||
+++ b/toolkit/themes/shared/menulist-shared.css
|
||||
@@ -14,7 +14,8 @@
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
:host(:not([native])) {
|
||||
appearance: none;
|
||||
- background-color: var(--button-background-color);
|
||||
+ background-color: var(--zen-colors-tertiary, ButtonFace);
|
||||
+ border: 1px solid var(--input-border-color, ThreeDShadow);
|
||||
+ background-color: light-dark(rgba(0,0,0,.1), rgba(255,255,255,.1));
|
||||
color: var(--button-text-color);
|
||||
border-radius: 4px;
|
||||
padding-block: 4px;
|
||||
|
||||
3
src/zen/@types/lib.gecko.darwin.d.ts
vendored
3
src/zen/@types/lib.gecko.darwin.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
||||
3
src/zen/@types/lib.gecko.dom.d.ts
vendored
3
src/zen/@types/lib.gecko.dom.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source .webidl files.
|
||||
|
||||
3
src/zen/@types/lib.gecko.glean.d.ts
vendored
3
src/zen/@types/lib.gecko.glean.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source metrics.yaml files.
|
||||
|
||||
3
src/zen/@types/lib.gecko.linux.d.ts
vendored
3
src/zen/@types/lib.gecko.linux.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
||||
3
src/zen/@types/lib.gecko.modules.d.ts
vendored
3
src/zen/@types/lib.gecko.modules.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated by running "mach ts paths".
|
||||
|
||||
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from xpc.msg and error_list.json.
|
||||
|
||||
3
src/zen/@types/lib.gecko.services.d.ts
vendored
3
src/zen/@types/lib.gecko.services.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from services.json.
|
||||
|
||||
3
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
3
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* Gecko generic/specialized adjustments for xpcom and webidl types.
|
||||
*/
|
||||
|
||||
3
src/zen/@types/lib.gecko.win32.d.ts
vendored
3
src/zen/@types/lib.gecko.win32.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
||||
3
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
3
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* NOTE: Do not modify this file by hand.
|
||||
* Content was generated from source XPCOM .idl files.
|
||||
|
||||
3
src/zen/@types/lib.gecko.xpidl.d.ts
vendored
3
src/zen/@types/lib.gecko.xpidl.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/**
|
||||
* Gecko XPIDL base types.
|
||||
*/
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Utility to register JSWindowActors
|
||||
|
||||
var gZenActorsManager = {
|
||||
_actors: new Set(),
|
||||
_lazy: {},
|
||||
|
||||
addJSWindowActor(...args) {
|
||||
if (this._actors.has(args[0])) {
|
||||
init() {
|
||||
ChromeUtils.defineESModuleGetters(this._lazy, {
|
||||
ActorManagerParent: 'resource://gre/modules/ActorManagerParent.sys.mjs',
|
||||
});
|
||||
},
|
||||
|
||||
addJSWindowActor(name, data) {
|
||||
if (!this._lazy.ActorManagerParent) {
|
||||
this.init();
|
||||
}
|
||||
if (this._actors.has(name)) {
|
||||
// Actor already registered, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const decl = {};
|
||||
decl[name] = data;
|
||||
try {
|
||||
ChromeUtils.registerWindowActor(...args);
|
||||
this._actors.add(args[0]);
|
||||
this._lazy.ActorManagerParent.addJSWindowActors(decl);
|
||||
this._actors.add(name);
|
||||
} catch (e) {
|
||||
console.warn(`Failed to register JSWindowActor: ${e}`);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
var gZenOperatingSystemCommonUtils = {
|
||||
kZenOSToSmallName: {
|
||||
WINNT: 'windows',
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export var ZenCustomizableUI = new (class {
|
||||
constructor() {}
|
||||
|
||||
TYPE_TOOLBAR = 'toolbar';
|
||||
defaultSidebarIcons = ['zen-open-library', 'zen-workspaces-button', 'downloads-button'];
|
||||
defaultSidebarIcons = ['preferences-button', 'zen-workspaces-button', 'downloads-button'];
|
||||
|
||||
startup(CustomizableUIInternal) {
|
||||
CustomizableUIInternal.registerArea(
|
||||
|
||||
File diff suppressed because one or more lines are too long
50
src/zen/common/ZenSessionStore.mjs
Normal file
50
src/zen/common/ZenSessionStore.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenSessionStore extends ZenPreloadedFeature {
|
||||
init() {
|
||||
this.#waitAndCleanup();
|
||||
}
|
||||
|
||||
promiseInitialized = new Promise((resolve) => {
|
||||
this._resolveInitialized = resolve;
|
||||
});
|
||||
|
||||
restoreInitialTabData(tab, tabData) {
|
||||
if (tabData.zenWorkspace) {
|
||||
tab.setAttribute('zen-workspace-id', tabData.zenWorkspace);
|
||||
}
|
||||
if (tabData.zenPinnedId) {
|
||||
tab.setAttribute('zen-pin-id', tabData.zenPinnedId);
|
||||
}
|
||||
if (tabData.zenIsEmpty) {
|
||||
tab.setAttribute('zen-empty-tab', 'true');
|
||||
}
|
||||
if (tabData.zenHasStaticLabel) {
|
||||
tab.setAttribute('zen-has-static-label', 'true');
|
||||
}
|
||||
if (tabData.zenEssential) {
|
||||
tab.setAttribute('zen-essential', 'true');
|
||||
}
|
||||
if (tabData.zenDefaultUserContextId) {
|
||||
tab.setAttribute('zenDefaultUserContextId', 'true');
|
||||
}
|
||||
if (tabData.zenPinnedEntry) {
|
||||
tab.setAttribute('zen-pinned-entry', tabData.zenPinnedEntry);
|
||||
}
|
||||
}
|
||||
|
||||
async #waitAndCleanup() {
|
||||
await SessionStore.promiseAllWindowsRestored;
|
||||
await SessionStore.promiseInitialized;
|
||||
this.#cleanup();
|
||||
}
|
||||
|
||||
#cleanup() {
|
||||
this._resolveInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenSessionStore = new ZenSessionStore();
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
var ZenStartup = {
|
||||
_watermarkIgnoreElements: ['zen-browser-background'],
|
||||
|
||||
init() {
|
||||
this.openWatermark();
|
||||
this._initBrowserBackground();
|
||||
@@ -104,9 +109,10 @@
|
||||
closeWatermark() {
|
||||
document.documentElement.removeAttribute('zen-before-loaded');
|
||||
if (Services.prefs.getBoolPref('zen.watermark.enabled', false)) {
|
||||
let elementsToIgnore = this._watermarkIgnoreElements.map((id) => '#' + id).join(', ');
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
'#browser > *, #urlbar, #tabbrowser-tabbox > *',
|
||||
'#browser > *:not(' + elementsToIgnore + '), #urlbar, #tabbrowser-tabbox > *',
|
||||
{
|
||||
opacity: [0, 1],
|
||||
},
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
var gZenUIManager = {
|
||||
_popupTrackingElements: [],
|
||||
_hoverPausedForExpand: false,
|
||||
_hasLoadedDOM: false,
|
||||
testingEnabled: Services.prefs.getBoolPref('zen.testing.enabled', false),
|
||||
|
||||
_lastClickPosition: null,
|
||||
|
||||
_toastTimeouts: [],
|
||||
|
||||
init() {
|
||||
@@ -31,6 +36,8 @@ var gZenUIManager = {
|
||||
|
||||
gURLBar._zenTrimURL = this.urlbarTrim.bind(this);
|
||||
|
||||
document.addEventListener('mousedown', this.handleMouseDown.bind(this), true);
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, 'motion', () => {
|
||||
return ChromeUtils.importESModule('chrome://browser/content/zen-vendor/motion.min.mjs', {
|
||||
global: 'current',
|
||||
@@ -62,6 +69,13 @@ var gZenUIManager = {
|
||||
gZenMediaController.init();
|
||||
},
|
||||
|
||||
handleMouseDown(event) {
|
||||
this._lastClickPosition = {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
};
|
||||
},
|
||||
|
||||
updateTabsToolbar() {
|
||||
const kUrlbarHeight = 440;
|
||||
gURLBar.textbox.style.setProperty(
|
||||
@@ -70,7 +84,6 @@ var gZenUIManager = {
|
||||
);
|
||||
gZenVerticalTabsManager.actualWindowButtons.removeAttribute('zen-has-hover');
|
||||
gZenVerticalTabsManager.recalculateURLBarHeight();
|
||||
setTimeout(gURLBar.formatValue.bind(gURLBar), 350);
|
||||
if (!this._preventToolbarRebuild) {
|
||||
setTimeout(() => {
|
||||
gZenWorkspaces.updateTabsContainers();
|
||||
@@ -989,43 +1002,54 @@ var gZenVerticalTabsManager = {
|
||||
async renameTabKeydown(event) {
|
||||
event.stopPropagation();
|
||||
if (event.key === 'Enter') {
|
||||
let label = this._tabEdited.querySelector('.tab-label-container-editing');
|
||||
let input = this._tabEdited.querySelector('#tab-label-input');
|
||||
const isTab = !!event.target.closest('.tabbrowser-tab');
|
||||
let label = isTab
|
||||
? this._tabEdited.querySelector('.tab-label-container-editing')
|
||||
: this._tabEdited;
|
||||
let input = document.getElementById('tab-label-input');
|
||||
let newName = input.value.trim();
|
||||
|
||||
// Check if name is blank, reset if so
|
||||
// Always remove, so we can always rename and if it's empty,
|
||||
// it will reset to the original name anyway
|
||||
this._tabEdited.removeAttribute('zen-has-static-label');
|
||||
if (newName) {
|
||||
gBrowser._setTabLabel(this._tabEdited, newName);
|
||||
this._tabEdited.setAttribute('zen-has-static-label', 'true');
|
||||
gZenUIManager.showToast('zen-tabs-renamed');
|
||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||
input.remove();
|
||||
if (!isTab) {
|
||||
await this._tabEdited.onRenameFinished(newName);
|
||||
} else {
|
||||
gBrowser.setTabTitle(this._tabEdited);
|
||||
}
|
||||
if (this._tabEdited.getAttribute('zen-pin-id')) {
|
||||
// Update pin title in storage
|
||||
await gZenPinnedTabManager.updatePinTitle(
|
||||
// Check if name is blank, reset if so
|
||||
// Always remove, so we can always rename and if it's empty,
|
||||
// it will reset to the original name anyway
|
||||
this._tabEdited.removeAttribute('zen-has-static-label');
|
||||
if (newName) {
|
||||
gBrowser._setTabLabel(this._tabEdited, newName);
|
||||
this._tabEdited.setAttribute('zen-has-static-label', 'true');
|
||||
gZenUIManager.showToast('zen-tabs-renamed');
|
||||
} else {
|
||||
gBrowser.setTabTitle(this._tabEdited);
|
||||
}
|
||||
if (this._tabEdited.getAttribute('zen-pin-id')) {
|
||||
// Update pin title in storage
|
||||
await gZenPinnedTabManager.updatePinTitle(
|
||||
this._tabEdited,
|
||||
this._tabEdited.label,
|
||||
!!newName
|
||||
);
|
||||
}
|
||||
|
||||
// Maybe add some confetti here?!?
|
||||
gZenUIManager.motion.animate(
|
||||
this._tabEdited,
|
||||
this._tabEdited.label,
|
||||
!!newName
|
||||
{
|
||||
scale: [1, 0.98, 1],
|
||||
},
|
||||
{
|
||||
duration: 0.25,
|
||||
}
|
||||
);
|
||||
}
|
||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||
|
||||
// Maybe add some confetti here?!?
|
||||
gZenUIManager.motion.animate(
|
||||
this._tabEdited,
|
||||
{
|
||||
scale: [1, 0.98, 1],
|
||||
},
|
||||
{
|
||||
duration: 0.25,
|
||||
}
|
||||
);
|
||||
|
||||
this._tabEdited.querySelector('.tab-editor-container').remove();
|
||||
const editorContainer = this._tabEdited.querySelector('.tab-editor-container');
|
||||
if (editorContainer) {
|
||||
editorContainer.remove();
|
||||
}
|
||||
label.classList.remove('tab-label-container-editing');
|
||||
|
||||
this._tabEdited = null;
|
||||
@@ -1035,34 +1059,40 @@ var gZenVerticalTabsManager = {
|
||||
},
|
||||
|
||||
renameTabStart(event) {
|
||||
const isTab = !!event.target.closest('.tabbrowser-tab');
|
||||
if (
|
||||
this._tabEdited ||
|
||||
!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
|
||||
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick') ||
|
||||
((!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
|
||||
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick')) &&
|
||||
isTab) ||
|
||||
!gZenVerticalTabsManager._prefsSidebarExpanded
|
||||
)
|
||||
return;
|
||||
this._tabEdited = event.target.closest('.tabbrowser-tab');
|
||||
if (
|
||||
!this._tabEdited ||
|
||||
!this._tabEdited.pinned ||
|
||||
this._tabEdited.hasAttribute('zen-essential')
|
||||
((!this._tabEdited.pinned || this._tabEdited.hasAttribute('zen-essential')) && isTab)
|
||||
) {
|
||||
this._tabEdited = null;
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
document.documentElement.setAttribute('zen-renaming-tab', 'true');
|
||||
const label = this._tabEdited.querySelector('.tab-label-container');
|
||||
const label = isTab ? this._tabEdited.querySelector('.tab-label-container') : this._tabEdited;
|
||||
label.classList.add('tab-label-container-editing');
|
||||
|
||||
const container = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="tab-label-container tab-editor-container" flex="1" align="start" pack="center"></vbox>
|
||||
`);
|
||||
label.after(container);
|
||||
const containerHtml = this._tabEdited.querySelector('.tab-editor-container');
|
||||
if (isTab) {
|
||||
const container = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="tab-label-container tab-editor-container" flex="1" align="start" pack="center"></vbox>
|
||||
`);
|
||||
label.after(container);
|
||||
}
|
||||
const containerHtml = isTab
|
||||
? this._tabEdited.querySelector('.tab-editor-container')
|
||||
: this._tabEdited.parentNode;
|
||||
const input = document.createElement('input');
|
||||
input.id = 'tab-label-input';
|
||||
input.value = this._tabEdited.label;
|
||||
input.value = isTab ? this._tabEdited.label : this._tabEdited.textContent;
|
||||
input.addEventListener('keydown', this.renameTabKeydown.bind(this));
|
||||
|
||||
containerHtml.appendChild(input);
|
||||
@@ -1077,8 +1107,16 @@ var gZenVerticalTabsManager = {
|
||||
return;
|
||||
}
|
||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||
this._tabEdited.querySelector('.tab-editor-container').remove();
|
||||
const label = this._tabEdited.querySelector('.tab-label-container-editing');
|
||||
const editorContainer = this._tabEdited.querySelector('.tab-editor-container');
|
||||
let input = document.getElementById('tab-label-input');
|
||||
input.remove();
|
||||
if (editorContainer) {
|
||||
editorContainer.remove();
|
||||
}
|
||||
const isTab = !!this._tabEdited.closest('.tabbrowser-tab');
|
||||
const label = isTab
|
||||
? this._tabEdited.querySelector('.tab-label-container-editing')
|
||||
: this._tabEdited;
|
||||
label.classList.remove('tab-label-container-editing');
|
||||
|
||||
this._tabEdited = null;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -87,6 +90,9 @@ class ZenUIMigration {
|
||||
}
|
||||
|
||||
_migrateV4(win) {
|
||||
if (AppConstants.platform === 'linux') {
|
||||
return;
|
||||
}
|
||||
Services.prefs.setBoolPref(
|
||||
'browser.tabs.unloadOnLowMemory',
|
||||
Services.prefs.getBoolPref('zen.tab-unloader.enabled', true)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
:root[zen-before-loaded='true'] #browser > *,
|
||||
:root[zen-before-loaded='true'] #browser > *:not(#zen-browser-background),
|
||||
:root[zen-before-loaded='true'] #urlbar {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
@@ -78,16 +78,6 @@
|
||||
transition: background-color var(--inactive-window-transition);
|
||||
}
|
||||
|
||||
@media (not (-moz-windows-mica)) and -moz-pref('zen.view.grey-out-inactive-windows') {
|
||||
transition: color var(--inactive-window-transition);
|
||||
:root:not([zen-welcome-stage]) &:-moz-window-inactive {
|
||||
color: var(--toolbox-textcolor-inactive);
|
||||
&::before {
|
||||
background-color: var(--toolbox-bgcolor-inactive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zen-browser-grain {
|
||||
content: '';
|
||||
width: 100%;
|
||||
@@ -131,9 +121,7 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (-moz-windows-accent-color-in-titlebar) and (-moz-windows-mica) {
|
||||
background-color: ActiveCaption;
|
||||
color: CaptionText;
|
||||
@media -moz-pref('zen.view.grey-out-inactive-windows') {
|
||||
transition: background-color var(--inactive-window-transition);
|
||||
&:-moz-window-inactive {
|
||||
background-color: InactiveCaption;
|
||||
@@ -264,6 +252,7 @@
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
|
||||
@@ -35,17 +35,13 @@
|
||||
--uc-permission-item-margin-block: 4px;
|
||||
--uc-permission-item-padding-inline: 16px;
|
||||
--zen-panel-separator-width: 1px;
|
||||
|
||||
--zen-contextmenu-menuitem-padding-inline: 10px;
|
||||
--zen-contextmenu-menuicon-margin-inline: 12px;
|
||||
--zen-contextmenu-menuitem-margin: 0px 2px;
|
||||
}
|
||||
|
||||
menupopup,
|
||||
panel {
|
||||
--panel-background: var(--arrowpanel-background);
|
||||
--panel-border-radius: var(--zen-native-inner-radius);
|
||||
--menuitem-padding: 6px 5px !important;
|
||||
--menuitem-padding: 6px !important;
|
||||
}
|
||||
|
||||
/* split-view popup */
|
||||
@@ -247,11 +243,6 @@ panel {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
menupopup::part(content),
|
||||
panel::part(content) {
|
||||
border: var(--zen-appcontent-border);
|
||||
}
|
||||
|
||||
menupopup,
|
||||
panel {
|
||||
box-shadow: none;
|
||||
@@ -272,6 +263,10 @@ panel {
|
||||
--arrowpanel-menuitem-padding-inline: 15px;
|
||||
}
|
||||
|
||||
menuseparator {
|
||||
padding-inline: 0 !important;
|
||||
}
|
||||
|
||||
toolbarseparator,
|
||||
menuseparator {
|
||||
border-width: var(--zen-panel-separator-width);
|
||||
@@ -339,17 +334,18 @@ menuseparator {
|
||||
|
||||
#zen-toast-container {
|
||||
position: fixed;
|
||||
top: calc(var(--zen-element-separation) * 2);
|
||||
--zen-toast-spacing: max(4px, var(--zen-element-separation));
|
||||
top: var(--zen-toast-spacing);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
|
||||
:root:not([zen-right-side='true']) & {
|
||||
right: calc(var(--zen-element-separation) * 2);
|
||||
right: var(--zen-toast-spacing);
|
||||
}
|
||||
|
||||
:root[zen-right-side='true'] & {
|
||||
left: calc(var(--zen-element-separation) * 2);
|
||||
left: var(--zen-toast-spacing);
|
||||
}
|
||||
|
||||
& .zen-toast {
|
||||
@@ -400,7 +396,7 @@ menuseparator {
|
||||
& button {
|
||||
color-scheme: dark;
|
||||
width: min-content;
|
||||
padding: 0 12px !important;
|
||||
padding: 0 10px !important;
|
||||
min-width: unset !important;
|
||||
margin: 0px !important;
|
||||
border-radius: calc(var(--zen-native-inner-radius) + 2px) !important;
|
||||
|
||||
@@ -197,6 +197,8 @@
|
||||
--toolbar-field-background-color: var(--zen-colors-input-bg) !important;
|
||||
--arrowpanel-background: var(--zen-dialog-background) !important;
|
||||
|
||||
--panel-separator-color: color-mix(in srgb, currentColor 15%, transparent) !important;
|
||||
|
||||
--zen-big-shadow: 0 0 9.73px 0px light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.25));
|
||||
--zen-active-tab-scale: 0.98;
|
||||
|
||||
|
||||
@@ -73,9 +73,6 @@ document.addEventListener(
|
||||
event.sourceEvent.target.getAttribute('zen-workspace-id')
|
||||
);
|
||||
break;
|
||||
case 'cmd_zenToggleLibrary':
|
||||
gZenLibrary.toggle();
|
||||
break;
|
||||
case 'cmd_zenToggleTabsOnRight':
|
||||
gZenVerticalTabsManager.toggleTabsOnRight();
|
||||
break;
|
||||
@@ -91,12 +88,6 @@ document.addEventListener(
|
||||
case 'cmd_zenRemoveFromEssentials':
|
||||
gZenPinnedTabManager.removeEssentials();
|
||||
break;
|
||||
case 'cmd_zenCtxDeleteWorkspace':
|
||||
gZenWorkspaces.contextDeleteWorkspace(event);
|
||||
break;
|
||||
case 'cmd_zenChangeWorkspaceName':
|
||||
gZenVerticalTabsManager.renameTabStart(event);
|
||||
break;
|
||||
default:
|
||||
if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
|
||||
const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
const lazyCompactMode = {};
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
@@ -100,6 +103,7 @@ var gZenCompactModeManager = {
|
||||
// main-window can't store attributes other than window sizes, so we use this instead
|
||||
lazyCompactMode.mainAppWrapper.setAttribute('zen-compact-mode', value);
|
||||
document.documentElement.setAttribute('zen-compact-mode', value);
|
||||
Services.xulStore.persist(lazyCompactMode.mainAppWrapper, 'zen-compact-mode');
|
||||
this._updateEvent();
|
||||
return value;
|
||||
},
|
||||
|
||||
@@ -152,6 +152,7 @@
|
||||
border-radius: calc(var(--zen-native-inner-radius) + var(--zen-element-separation) / 4);
|
||||
@media -moz-pref('zen.view.compact.color-sidebar') {
|
||||
background: var(--zen-main-browser-background-toolbar) !important;
|
||||
background-attachment: fixed !important;
|
||||
background-size: 2000px !important;
|
||||
@media -moz-pref('zen.theme.acrylic-elements') {
|
||||
backdrop-filter: blur(42px) saturate(110%) brightness(0.25) contrast(100%) !important;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
const { Downloads } = ChromeUtils.importESModule('resource://gre/modules/Downloads.sys.mjs');
|
||||
|
||||
@@ -11,24 +14,10 @@
|
||||
});
|
||||
|
||||
class ZenDownloadAnimation extends ZenDOMOperatedFeature {
|
||||
#lastClickPosition = null;
|
||||
|
||||
async init() {
|
||||
this.#setupClickListener();
|
||||
await this.#setupDownloadListeners();
|
||||
}
|
||||
|
||||
#setupClickListener() {
|
||||
document.addEventListener('mousedown', this.#handleClick.bind(this), true);
|
||||
}
|
||||
|
||||
#handleClick(event) {
|
||||
this.#lastClickPosition = {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
};
|
||||
}
|
||||
|
||||
async #setupDownloadListeners() {
|
||||
try {
|
||||
const list = await Downloads.getList(Downloads.ALL);
|
||||
@@ -50,14 +39,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#lastClickPosition) {
|
||||
if (!gZenUIManager._lastClickPosition) {
|
||||
console.warn(
|
||||
`[${ZenDownloadAnimation.name}] No recent click position available for animation`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#animateDownload(this.#lastClickPosition);
|
||||
this.#animateDownload(gZenUIManager._lastClickPosition);
|
||||
}
|
||||
|
||||
#animateDownload(startPosition) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenFolders {
|
||||
constructor() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenGlanceManager extends ZenDOMOperatedFeature {
|
||||
_animating = false;
|
||||
@@ -151,8 +154,8 @@
|
||||
this.animatingOpen = true;
|
||||
this._animating = true;
|
||||
|
||||
const initialX = data.x;
|
||||
const initialY = data.y;
|
||||
const initialX = data.clientX;
|
||||
const initialY = data.clientY;
|
||||
const initialWidth = data.width;
|
||||
const initialHeight = data.height;
|
||||
|
||||
@@ -594,8 +597,10 @@
|
||||
this.openGlance(
|
||||
{
|
||||
url: undefined,
|
||||
x: browserRect.width / 2,
|
||||
y: browserRect.height / 2,
|
||||
...(gZenUIManager._lastClickPosition || {
|
||||
clientX: browserRect.width / 2,
|
||||
clientY: browserRect.height / 2,
|
||||
}),
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
@@ -636,7 +641,12 @@
|
||||
.classList.remove('zen-glance-background');
|
||||
this.#currentParentTab._visuallySelected = false;
|
||||
this.hideSidebarButtons();
|
||||
if (forSplit) {
|
||||
this.finishOpeningGlance();
|
||||
return;
|
||||
}
|
||||
if (gReduceMotion || forSplit) {
|
||||
gZenViewSplitter.deactivateCurrentSplitView();
|
||||
this.finishOpeningGlance();
|
||||
return;
|
||||
}
|
||||
@@ -651,6 +661,7 @@
|
||||
type: 'spring',
|
||||
}
|
||||
);
|
||||
gZenViewSplitter.deactivateCurrentSplitView();
|
||||
this.finishOpeningGlance();
|
||||
}
|
||||
|
||||
@@ -675,8 +686,8 @@
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
const data = {
|
||||
url: event.target._placesNode.uri,
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
clientX: rect.left,
|
||||
clientY: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
};
|
||||
@@ -705,8 +716,8 @@
|
||||
}
|
||||
|
||||
getTabOrGlanceParent(tab) {
|
||||
if (tab?.hasAttribute('glance-id')) {
|
||||
const parentTab = this.#glances.get(tab.getAttribute('glance-id')).parentTab;
|
||||
if (tab?.hasAttribute('glance-id') && this.#glances) {
|
||||
const parentTab = this.#glances.get(tab.getAttribute('glance-id'))?.parentTab;
|
||||
if (parentTab) {
|
||||
return parentTab;
|
||||
}
|
||||
@@ -734,29 +745,54 @@
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onSearchSelectCommand(where) {
|
||||
if (where !== 'tab') {
|
||||
return;
|
||||
}
|
||||
const currentTab = gBrowser.selectedTab;
|
||||
const parentTab = currentTab.owner;
|
||||
if (!parentTab) {
|
||||
return;
|
||||
}
|
||||
// Open a new glance if the current tab is a glance tab
|
||||
const browserRect = gBrowser.tabbox.getBoundingClientRect();
|
||||
this.openGlance(
|
||||
{
|
||||
url: undefined,
|
||||
...(gZenUIManager._lastClickPosition || {
|
||||
clientX: browserRect.width / 2,
|
||||
clientY: browserRect.height / 2,
|
||||
}),
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
currentTab,
|
||||
parentTab
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenGlanceManager = new ZenGlanceManager();
|
||||
|
||||
function registerWindowActors() {
|
||||
if (Services.prefs.getBoolPref('zen.glance.enabled', true)) {
|
||||
gZenActorsManager.addJSWindowActor('ZenGlance', {
|
||||
parent: {
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenGlanceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenGlanceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
keydown: {
|
||||
capture: true,
|
||||
},
|
||||
gZenActorsManager.addJSWindowActor('ZenGlance', {
|
||||
parent: {
|
||||
esModuleURI: 'resource:///actors/ZenGlanceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'resource:///actors/ZenGlanceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
keydown: {
|
||||
capture: true,
|
||||
},
|
||||
},
|
||||
allFrames: true,
|
||||
matches: ['*://*/*'],
|
||||
});
|
||||
}
|
||||
},
|
||||
allFrames: true,
|
||||
matches: ['*://*/*'],
|
||||
enablePreference: 'zen.glance.enabled',
|
||||
});
|
||||
}
|
||||
|
||||
registerWindowActors();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
export class ZenGlanceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -71,8 +74,8 @@ export class ZenGlanceChild extends JSWindowActorChild {
|
||||
const rect = target.getBoundingClientRect();
|
||||
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
||||
url,
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
clientX: rect.left,
|
||||
clientY: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
export class ZenGlanceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
{
|
||||
class ZenLibraryElement extends MozXULElement {
|
||||
#currentTab = null;
|
||||
#availableTabs = ['workspaces', 'mods'];
|
||||
|
||||
static get markup() {
|
||||
return `
|
||||
<vbox id="zen-library-sidebar">
|
||||
<vbox id="zen-library-sidebar-buttons" flex="1">
|
||||
<toolbarbutton class="toolbarbutton-1 zen-library-sidebar-button" id="zen-library-sidebar-workspaces" data-l10n-id="zen-library-sidebar-workspaces"/>
|
||||
<toolbarbutton class="toolbarbutton-1 zen-library-sidebar-button" id="zen-library-sidebar-mods" data-l10n-id="zen-library-sidebar-mods"/>
|
||||
</vbox>
|
||||
<hbox id="zen-library-sidebar-footer">
|
||||
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-open-library" command="cmd_zenToggleLibrary"></toolbarbutton>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="zen-library-content">
|
||||
<hbox content="workspaces" size="big">
|
||||
</hbox>
|
||||
<hbox content="mods" size="small"></hbox>
|
||||
</vbox>
|
||||
`;
|
||||
}
|
||||
|
||||
static get inheritedAttributes() {
|
||||
return {
|
||||
'#zen-library-content': 'content,size=content-size',
|
||||
'#zen-library-sidebar': 'content',
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
set currentTab(tab) {
|
||||
if (!this.#availableTabs.includes(tab)) {
|
||||
throw new Error(`Tab "${tab}" is not available in Zen Library.`);
|
||||
}
|
||||
this.#currentTab = tab;
|
||||
this.setAttribute('content', tab);
|
||||
// Also set if the size is big or small based on the tab.
|
||||
const element = this.querySelector(`#zen-library-content hbox[content="${tab}"]`);
|
||||
this.setAttribute('content-size', element.getAttribute('size') || 'small');
|
||||
for (const availableTab of this.#availableTabs) {
|
||||
const button = this.querySelector(`#zen-library-sidebar-${availableTab}`);
|
||||
if (availableTab === tab) {
|
||||
button.setAttribute('active', 'true');
|
||||
} else {
|
||||
button.removeAttribute('active');
|
||||
}
|
||||
const contentContainer = this.#getContentContainer(availableTab);
|
||||
if (availableTab === tab) {
|
||||
contentContainer.removeAttribute('hidden');
|
||||
} else {
|
||||
contentContainer.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get currentTab() {
|
||||
return this.#currentTab;
|
||||
}
|
||||
|
||||
set open(value) {
|
||||
if (value) {
|
||||
this.setAttribute('open', 'true');
|
||||
this.onOpen(); // Trigger the onOpen method to populate the content
|
||||
} else {
|
||||
this.removeAttribute('open');
|
||||
this.onClose(); // Trigger the onClose method if needed
|
||||
}
|
||||
}
|
||||
|
||||
get open() {
|
||||
return this.getAttribute('open') === 'true';
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.delayConnectedCallback()) {
|
||||
// If we are not ready yet, or if we have already connected, we
|
||||
// don't need to do anything.
|
||||
return;
|
||||
}
|
||||
|
||||
this.id = 'zen-library';
|
||||
this.appendChild(this.constructor.fragment);
|
||||
this.initializeAttributeInheritance();
|
||||
|
||||
for (const availableTab of this.#availableTabs) {
|
||||
const button = this.querySelector(`#zen-library-sidebar-${availableTab}`);
|
||||
button.addEventListener('command', () => {
|
||||
this.currentTab = availableTab;
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('TabSelect', this);
|
||||
|
||||
this.currentTab = 'workspaces'; // Default tab
|
||||
}
|
||||
|
||||
#getContentContainer(tab) {
|
||||
return this.querySelector(`#zen-library-content hbox[content="${tab}"]`);
|
||||
}
|
||||
|
||||
#createWorkspaceElement(workspace) {
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<vbox class="zen-workspace-item" zen-workspace-id="${workspace.uuid}">
|
||||
<hbox class="zen-workspace-item-header">
|
||||
<label class="zen-workspace-item-name"></label>
|
||||
</hbox>
|
||||
<vbox class="zen-workspace-item-content">
|
||||
</vbox>
|
||||
</vbox>
|
||||
`);
|
||||
|
||||
const workspaceLabel = fragment.querySelector('.zen-workspace-item-name');
|
||||
workspaceLabel.textContent = workspace.name;
|
||||
workspaceLabel.addEventListener(
|
||||
'click',
|
||||
gZenVerticalTabsManager.renameTabStart.bind(gZenVerticalTabsManager)
|
||||
);
|
||||
|
||||
const workspaceItem = fragment.querySelector('.zen-workspace-item');
|
||||
workspaceItem.style.setProperty(
|
||||
'--zen-workspace-gradient',
|
||||
gZenThemePicker.getGradient(workspace.theme.gradientColors, true, workspace.theme.rotation)
|
||||
);
|
||||
|
||||
// TODO: Not jet! Figure this out
|
||||
//const workspaceElement = gZenWorkspaces.workspaceElement(workspace.uuid);
|
||||
//fragment.querySelector('.zen-workspace-item-content').appendChild(workspaceElement);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
async onOpen(event) {
|
||||
const conatainer = this.#getContentContainer('workspaces');
|
||||
conatainer.innerHTML = ''; // Clear the container
|
||||
|
||||
const workspaces = await gZenWorkspaces._workspaces();
|
||||
for (const workspace of workspaces.workspaces) {
|
||||
const workspaceElement = this.#createWorkspaceElement(workspace);
|
||||
conatainer.appendChild(workspaceElement);
|
||||
}
|
||||
}
|
||||
|
||||
async onClose(event) {}
|
||||
|
||||
on_TabSelect(event) {
|
||||
if (!this.open) return;
|
||||
gZenLibrary.close();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('zen-library', ZenLibraryElement);
|
||||
|
||||
class ZenLibrary {
|
||||
#animating = false;
|
||||
|
||||
constructor() {
|
||||
ChromeUtils.defineLazyGetter(this, 'wrapper', () => document.getElementById('zen-library'));
|
||||
}
|
||||
|
||||
get isOpen() {
|
||||
return this.wrapper.hasAttribute('open');
|
||||
}
|
||||
|
||||
set isOpen(value) {
|
||||
if (this.#animating) {
|
||||
return; // Prevent multiple animations from running at the same time
|
||||
}
|
||||
this.#animating = true;
|
||||
if (value) {
|
||||
this.wrapper.open = value;
|
||||
this.animateLibrary(false).then(() => {
|
||||
this.#animating = false;
|
||||
});
|
||||
} else {
|
||||
this.animateLibrary(true).then(() => {
|
||||
this.wrapper.open = value;
|
||||
this.#animating = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
}
|
||||
|
||||
async animateLibrary(open) {
|
||||
window.docShell.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIAppWindow)
|
||||
.rollupAllPopups();
|
||||
|
||||
let elementsToAnimate = [gNavToolbox];
|
||||
if (gZenVerticalTabsManager._hasSetSingleToolbar) {
|
||||
elementsToAnimate.push(gURLBar.textbox);
|
||||
}
|
||||
const wrapperWidth = this.wrapper.getBoundingClientRect().width;
|
||||
const appContentWrapper = document.getElementById('zen-appcontent-wrapper');
|
||||
if (open) {
|
||||
await Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
elementsToAnimate,
|
||||
{
|
||||
transform: ['translateX(100%)', 'translateX(0)'],
|
||||
opacity: [0, 1],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.wrapper,
|
||||
{
|
||||
marginLeft: ['0px', `${-wrapperWidth}px`],
|
||||
transform: ['translateX(0)', 'translateX(100%)'],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
]);
|
||||
appContentWrapper.style.minWidth = ''; // Reset the min-width after the animation
|
||||
gNavToolbox.style.display = ''; // Hide the toolbox during the animation
|
||||
} else {
|
||||
appContentWrapper.style.minWidth = `${appContentWrapper.getBoundingClientRect().width}px`;
|
||||
await Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
elementsToAnimate,
|
||||
{
|
||||
transform: ['translateX(0)', 'translateX(100%)'],
|
||||
opacity: [1, 0],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.wrapper,
|
||||
{
|
||||
marginLeft: [`${-wrapperWidth}px`, '0px'],
|
||||
transform: ['translateX(-100%)', 'translateX(0%)'],
|
||||
},
|
||||
{
|
||||
duration: 0.2,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
),
|
||||
]);
|
||||
gNavToolbox.style.display = 'none'; // Show the toolbox after the animation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenLibrary = new ZenLibrary();
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
#zen-library {
|
||||
order: -1;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
left: -200%;
|
||||
|
||||
&[open='true'] {
|
||||
display: flex;
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-sidebar {
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 0.6rem;
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
-moz-window-dragging: drag;
|
||||
border-right: 1px solid var(--zen-colors-border);
|
||||
|
||||
#zen-library-sidebar-buttons {
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
& .zen-library-sidebar-button {
|
||||
transition: background-color 0.1s;
|
||||
padding: 0.8rem 0.7rem;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
appearance: none;
|
||||
gap: 0.5rem;
|
||||
border-radius: 6px !important;
|
||||
&:hover,
|
||||
&[active='true'] {
|
||||
background: light-dark(rgba(255, 255, 255, 0.2), rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-sidebar-footer {
|
||||
justify-content: space-between;
|
||||
|
||||
& toolbarbutton {
|
||||
appearance: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-content {
|
||||
transition: width 0.15s;
|
||||
width: 20vw;
|
||||
overflow-x: auto;
|
||||
|
||||
&[size='big'] {
|
||||
width: 60vw;
|
||||
}
|
||||
|
||||
& > * {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
& > [content='workspaces'] {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
padding: 6rem;
|
||||
|
||||
& .zen-workspace-item {
|
||||
width: 16rem;
|
||||
padding: 0.7rem;
|
||||
border-radius: var(--zen-border-radius);
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
background: var(--zen-workspace-gradient);
|
||||
border: 1px solid light-dark(
|
||||
rgba(255, 255, 255, 0.2),
|
||||
rgba(0, 0, 0, 0.2)
|
||||
);
|
||||
height: 100%;
|
||||
|
||||
&::before {
|
||||
background-image: url(chrome://browser/content/zen-images/grain-bg.png);
|
||||
opacity: var(--zen-grainy-background-opacity, 0);
|
||||
mix-blend-mode: hard-light;
|
||||
width: 60%;
|
||||
height: 60%;
|
||||
pointer-events: none;
|
||||
top: 50%;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
content: '';
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
const lazy = {};
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
||||
735
src/zen/mods/ZenMods.mjs
Normal file
735
src/zen/mods/ZenMods.mjs
Normal file
@@ -0,0 +1,735 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
{
|
||||
class ZenMods extends ZenPreloadedFeature {
|
||||
// private properties start
|
||||
#kZenStylesheetModHeader = '/* Zen Mods - Generated by ZenMods.';
|
||||
#kZenStylesheetModHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
||||
* Your changes will be overwritten.
|
||||
* Instead, go to the preferences and edit the mods there.
|
||||
*/
|
||||
`;
|
||||
#kZenStylesheetModFooter = `
|
||||
/* End of Zen Mods */
|
||||
`;
|
||||
#getCurrentDateTime = () =>
|
||||
new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'full',
|
||||
}).format(new Date().getTime());
|
||||
|
||||
constructor() {
|
||||
console.log('[ZenMods]: Initializing ZenMods module');
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
// Stylesheet service
|
||||
#sss = null;
|
||||
|
||||
get #stylesheetService() {
|
||||
if (!this.#sss) {
|
||||
this.#sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
||||
Ci.nsIStyleSheetService
|
||||
);
|
||||
}
|
||||
return this.#sss;
|
||||
}
|
||||
|
||||
#ssu = null;
|
||||
|
||||
get #styleSheetUri() {
|
||||
if (!this.#ssu) {
|
||||
this.#ssu = Services.io.newFileURI(new FileUtils.File(this.#styleSheetPath));
|
||||
}
|
||||
return this.#ssu;
|
||||
}
|
||||
|
||||
get #styleSheetPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
||||
}
|
||||
|
||||
async #handleDisableMods() {
|
||||
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
||||
console.log('[ZenMods]: Disabling mods module.');
|
||||
|
||||
await this.#removeStylesheet();
|
||||
} else {
|
||||
console.log('[ZenMods]: Enabling mods module.');
|
||||
|
||||
await this.#rebuildModsStylesheet();
|
||||
}
|
||||
}
|
||||
|
||||
#getStylesheetURIForMod(mod) {
|
||||
return Services.io.newFileURI(
|
||||
new FileUtils.File(PathUtils.join(this.getModFolder(mod.id), 'chrome.css'))
|
||||
);
|
||||
}
|
||||
|
||||
async #insertStylesheet() {
|
||||
if (await IOUtils.exists(this.#styleSheetPath)) {
|
||||
await this.#stylesheetService.loadAndRegisterSheet(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!this.#stylesheetService.sheetRegistered(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
)
|
||||
) {
|
||||
console.error(`[ZenMods]: Failed to register stylesheet at ${this.#styleSheetUri.spec}.`);
|
||||
}
|
||||
}
|
||||
|
||||
async #removeStylesheet() {
|
||||
await this.#stylesheetService.unregisterSheet(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
);
|
||||
const rv = this.#stylesheetService.sheetRegistered(
|
||||
this.#styleSheetUri,
|
||||
this.#stylesheetService.AGENT_SHEET
|
||||
);
|
||||
await IOUtils.remove(this.#styleSheetPath, { ignoreAbsent: true });
|
||||
|
||||
if (rv || (await IOUtils.exists(this.#styleSheetPath))) {
|
||||
console.error(`[ZenMods]: Failed to unregister stylesheet at ${this.#styleSheetUri.spec}.`);
|
||||
}
|
||||
}
|
||||
|
||||
async #rebuildModsStylesheet() {
|
||||
await this.#removeStylesheet();
|
||||
|
||||
const mods = await this.#getEnabledMods();
|
||||
|
||||
await this.#writeStylesheet(mods);
|
||||
|
||||
const modsWithPreferences = await Promise.all(
|
||||
mods.map(async (mod) => {
|
||||
const preferences = await this.getModPreferences(mod);
|
||||
|
||||
return {
|
||||
name: mod.name,
|
||||
enabled: mod.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.#setDefaults(modsWithPreferences);
|
||||
this.#writeToDom(modsWithPreferences);
|
||||
|
||||
await this.#insertStylesheet();
|
||||
}
|
||||
|
||||
async #getEnabledMods() {
|
||||
const modsObject = await this.getMods();
|
||||
const mods = Object.values(modsObject).filter(
|
||||
(mod) => mod.enabled === undefined || mod.enabled
|
||||
);
|
||||
|
||||
const modList = mods.map(({ name }) => name).join(', ');
|
||||
|
||||
const message =
|
||||
modList !== ''
|
||||
? `[ZenMods]: Loading enabled Zen mods: ${modList}.`
|
||||
: '[ZenMods]: No enabled Zen mods.';
|
||||
|
||||
console.log(message);
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
#setDefaults(modsWithPreferences) {
|
||||
for (const { preferences, enabled } of modsWithPreferences) {
|
||||
if (enabled !== undefined && !enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { type, property, defaultValue } of preferences) {
|
||||
if (defaultValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'checkbox') {
|
||||
const value = Services.prefs.getBoolPref(property, false);
|
||||
if (typeof defaultValue !== 'boolean') {
|
||||
console.warn(
|
||||
'[ZenMods]: Warning, invalid data type received for expected type boolean, skipping.'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
Services.prefs.setBoolPref(property, defaultValue);
|
||||
}
|
||||
} else {
|
||||
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
||||
|
||||
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
||||
console.warn(
|
||||
`[ZenMods]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value === 'zen-property-no-saved') {
|
||||
Services.prefs.setStringPref(property, defaultValue.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#writeToDom(modsWithPreferences) {
|
||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||
for (const { enabled, preferences, name } of modsWithPreferences) {
|
||||
const sanitizedName = this.sanitizeModName(name);
|
||||
|
||||
if (enabled !== undefined && !enabled) {
|
||||
const element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.removeProperty(`--${sanitizedProperty}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { property, type } of preferences) {
|
||||
const value = Services.prefs.getStringPref(property, '');
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
switch (type) {
|
||||
case 'dropdown': {
|
||||
if (value !== '') {
|
||||
let element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (!element) {
|
||||
element = browser.document.createElement('div');
|
||||
|
||||
element.style.display = 'none';
|
||||
element.setAttribute('id', sanitizedName);
|
||||
|
||||
browser.document.body.appendChild(element);
|
||||
}
|
||||
|
||||
element.setAttribute(sanitizedProperty, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'string': {
|
||||
if (value === '') {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.removeProperty(`--${sanitizedProperty}`);
|
||||
} else {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.setProperty(`--${sanitizedProperty}`, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #writeStylesheet(modList = []) {
|
||||
const mods = [];
|
||||
|
||||
for (let mod of modList) {
|
||||
mod._chromeURL = this.#getStylesheetURIForMod(mod).spec;
|
||||
mods.push(mod);
|
||||
}
|
||||
|
||||
let content = this.#kZenStylesheetModHeader;
|
||||
content += `\n* FILE GENERATED AT: ${this.#getCurrentDateTime()}\n`;
|
||||
content += this.#kZenStylesheetModHeaderBody;
|
||||
|
||||
for (let mod of mods) {
|
||||
if (mod.enabled !== undefined && !mod.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
content += `\n/* Name: ${mod.name} */\n`;
|
||||
content += `/* Description: ${mod.description} */\n`;
|
||||
content += `/* Author: @${mod.author} */\n`;
|
||||
|
||||
if (mod._readmeURL) {
|
||||
content += `/* Readme: ${mod.readme} */\n`;
|
||||
}
|
||||
|
||||
content += `@import url("${mod._chromeURL}");\n`;
|
||||
}
|
||||
|
||||
content += this.#kZenStylesheetModFooter;
|
||||
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
|
||||
await IOUtils.write(this.#styleSheetPath, buffer);
|
||||
}
|
||||
|
||||
#compareVersions(version1, version2) {
|
||||
let result = false;
|
||||
|
||||
if (typeof version1 !== 'object') {
|
||||
version1 = version1.toString().split('.');
|
||||
}
|
||||
|
||||
if (typeof version2 !== 'object') {
|
||||
version2 = version2.toString().split('.');
|
||||
}
|
||||
|
||||
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
|
||||
if (version1[i] == undefined) {
|
||||
version1[i] = 0;
|
||||
}
|
||||
if (version2[i] == undefined) {
|
||||
version2[i] = 0;
|
||||
}
|
||||
if (Number(version1[i]) < Number(version2[i])) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
if (version1[i] != version2[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#composeModApiUrl(modId) {
|
||||
// keeping theme here as it would require changes to CI to change the name
|
||||
return `https://zen-browser.github.io/theme-store/themes/${modId}/theme.json`;
|
||||
}
|
||||
|
||||
async #downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status} for url: ${url}`);
|
||||
}
|
||||
|
||||
const data = await response.text();
|
||||
|
||||
let content = data;
|
||||
|
||||
if (isStyleSheet) {
|
||||
content = '@-moz-document url-prefix("chrome:") {\n';
|
||||
|
||||
for (const line of data.split('\n')) {
|
||||
content += ` ${line}\n`;
|
||||
}
|
||||
|
||||
content += '}';
|
||||
}
|
||||
|
||||
// convert the data into a Uint8Array
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
await IOUtils.write(path, buffer);
|
||||
|
||||
return; // to exit the loop
|
||||
} catch (e) {
|
||||
attempt++;
|
||||
if (attempt >= maxRetries) {
|
||||
console.error('[ZenMods]: Error downloading file after retries', url, e);
|
||||
} else {
|
||||
console.warn(
|
||||
`[ZenMods]: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
|
||||
url,
|
||||
e
|
||||
);
|
||||
await new Promise((res) => setTimeout(res, retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private properties end
|
||||
|
||||
// public properties start
|
||||
|
||||
throttle(mainFunction, delay) {
|
||||
let timerFlag = null;
|
||||
|
||||
return (...args) => {
|
||||
if (timerFlag === null) {
|
||||
mainFunction(...args);
|
||||
timerFlag = setTimeout(() => {
|
||||
timerFlag = null;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
debounce(mainFunction, wait) {
|
||||
let timerFlag;
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timerFlag);
|
||||
timerFlag = setTimeout(() => {
|
||||
mainFunction(...args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
|
||||
sanitizeModName(name) {
|
||||
// Do not change to "mod-" for backwards compatibility
|
||||
return `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||
}
|
||||
|
||||
get updatePref() {
|
||||
return 'zen.themes.updated-value-observer';
|
||||
}
|
||||
|
||||
get modsRootPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
||||
}
|
||||
|
||||
get modsDataFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||
}
|
||||
|
||||
getModFolder(modId) {
|
||||
return PathUtils.join(this.modsRootPath, modId);
|
||||
}
|
||||
|
||||
async getMods() {
|
||||
if (!(await IOUtils.exists(this.modsDataFile))) {
|
||||
await IOUtils.writeJSON(this.modsDataFile, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
let mods = {};
|
||||
|
||||
try {
|
||||
mods = await IOUtils.readJSON(this.modsDataFile);
|
||||
|
||||
if (mods === null || typeof mods !== 'object') {
|
||||
throw new Error('Mods data file is invalid');
|
||||
}
|
||||
} catch {
|
||||
// If we have a corrupted file, reset it
|
||||
await IOUtils.writeJSON(this.modsDataFile, {});
|
||||
|
||||
Services.wm
|
||||
.getMostRecentWindow('navigator:browser')
|
||||
.gZenUIManager.showToast('zen-themes-corrupted', {
|
||||
timeout: 8000,
|
||||
});
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
async getModPreferences(mod) {
|
||||
const modPath = PathUtils.join(this.modsRootPath, mod.id, 'preferences.json');
|
||||
|
||||
if (!(await IOUtils.exists(modPath)) || !mod.preferences) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const preferences = await IOUtils.readJSON(modPath);
|
||||
|
||||
return preferences.filter(({ disabledOn = [] }) => {
|
||||
return !disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`[ZenMods]: Error reading mod preferences for ${mod.name}:`, e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
await SessionStore.promiseInitialized;
|
||||
|
||||
if (
|
||||
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
||||
Services.appinfo.inSafeMode
|
||||
) {
|
||||
console.log('[ZenMods]: Mods disabled by user or in safe mode.');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.getMods(); // Check for any errors in the themes data file
|
||||
const mods = await this.#getEnabledMods();
|
||||
|
||||
const modsWithPreferences = await Promise.all(
|
||||
mods.map(async (mod) => {
|
||||
const preferences = await this.getModPreferences(mod);
|
||||
|
||||
return {
|
||||
name: mod.name,
|
||||
enabled: mod.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.#writeToDom(modsWithPreferences);
|
||||
|
||||
await this.#insertStylesheet();
|
||||
|
||||
this.#setNewMilestoneIfNeeded();
|
||||
if (this.#shouldAutoUpdate()) {
|
||||
requestIdleCallback(
|
||||
() => {
|
||||
if (!window.closed) {
|
||||
requestAnimationFrame(() => {
|
||||
this.checkForModsUpdates();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ timeout: 1000 }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error loading Zen Mods:', e);
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(this.updatePref, this.#rebuildModsStylesheet.bind(this), false);
|
||||
Services.prefs.addObserver(
|
||||
'zen.themes.disable-all',
|
||||
this.#handleDisableMods.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#setNewMilestoneIfNeeded() {
|
||||
const previousMilestone = Services.prefs.getStringPref('zen.mods.milestone', '');
|
||||
if (previousMilestone != Services.appinfo.version) {
|
||||
Services.prefs.setStringPref('zen.mods.milestone', Services.appinfo.version);
|
||||
Services.prefs.clearUserPref('zen.mods.last-update');
|
||||
}
|
||||
}
|
||||
|
||||
#shouldAutoUpdate() {
|
||||
const daysBeforeUpdate = Services.prefs.getIntPref('zen.mods.auto-update-days');
|
||||
const lastUpdatedSec = Services.prefs.getIntPref('zen.mods.last-update', -1);
|
||||
const nowSec = Math.floor(Date.now() / 1000);
|
||||
const daysSinceUpdate = (nowSec - lastUpdatedSec) / (60 * 60 * 24);
|
||||
|
||||
return (
|
||||
(Services.prefs.getBoolPref('zen.mods.auto-update', true) &&
|
||||
daysSinceUpdate >= daysBeforeUpdate) ||
|
||||
lastUpdatedSec < 0
|
||||
);
|
||||
}
|
||||
|
||||
async checkForModsUpdates() {
|
||||
const mods = await this.getMods();
|
||||
|
||||
const updates = await Promise.all(
|
||||
Object.values(mods).map(async (currentMod) => {
|
||||
try {
|
||||
const possibleNewModVersion = await this.requestMod(currentMod.id);
|
||||
|
||||
if (!possibleNewModVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.#compareVersions(
|
||||
possibleNewModVersion.version,
|
||||
currentMod.version ?? '0.0.0'
|
||||
) &&
|
||||
possibleNewModVersion.version != currentMod.version
|
||||
) {
|
||||
console.log(
|
||||
`[ZenMods]: Mod update found for mod ${currentMod.name} (${currentMod.id}), current: ${currentMod.version}, new: ${possibleNewModVersion.version}`
|
||||
);
|
||||
|
||||
possibleNewModVersion.enabled = currentMod.enabled;
|
||||
|
||||
await this.removeMod(currentMod.id, false);
|
||||
|
||||
mods[currentMod.id] = possibleNewModVersion;
|
||||
|
||||
return possibleNewModVersion;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error checking for mod updates', e);
|
||||
|
||||
return null;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
await this.updateMods(mods);
|
||||
Services.prefs.setIntPref('zen.mods.last-update', Math.floor(Date.now() / 1000));
|
||||
return updates.filter((update) => {
|
||||
return update !== null;
|
||||
});
|
||||
}
|
||||
|
||||
async removeMod(modId, triggerUpdate = true) {
|
||||
const modPath = this.getModFolder(modId);
|
||||
|
||||
console.log(`[ZenMods]: Removing mod ${modPath}`);
|
||||
|
||||
await IOUtils.remove(modPath, { recursive: true, ignoreAbsent: true });
|
||||
|
||||
const mods = await this.getMods();
|
||||
|
||||
delete mods[modId];
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
|
||||
if (triggerUpdate) {
|
||||
this.triggerModsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
async enableMod(modId) {
|
||||
const mods = await this.getMods();
|
||||
const mod = mods[modId];
|
||||
|
||||
console.log(`[ZenMods]: Enabling mod ${mod.name}`);
|
||||
|
||||
mod.enabled = true;
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
}
|
||||
|
||||
async disableMod(modId) {
|
||||
const mods = await this.getMods();
|
||||
const mod = mods[modId];
|
||||
|
||||
console.log(`[ZenMods]: Disabling mod ${mod.name}`);
|
||||
|
||||
mod.enabled = false;
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
}
|
||||
|
||||
async updateMods(mods = undefined) {
|
||||
if (!mods) {
|
||||
mods = await this.getMods();
|
||||
}
|
||||
|
||||
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||
await this.checkForModChanges();
|
||||
}
|
||||
|
||||
triggerModsUpdate() {
|
||||
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
|
||||
}
|
||||
|
||||
async installMod(mod) {
|
||||
try {
|
||||
const modPath = PathUtils.join(this.modsRootPath, mod.id);
|
||||
await IOUtils.makeDirectory(modPath, { ignoreExisting: true });
|
||||
|
||||
await this.#downloadUrlToFile(mod.style, PathUtils.join(modPath, 'chrome.css'), true);
|
||||
await this.#downloadUrlToFile(mod.readme, PathUtils.join(modPath, 'readme.md'));
|
||||
|
||||
if (mod.preferences) {
|
||||
await this.#downloadUrlToFile(
|
||||
mod.preferences,
|
||||
PathUtils.join(modPath, 'preferences.json')
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error installing mod', mod.id, e);
|
||||
}
|
||||
}
|
||||
|
||||
async checkForModChanges() {
|
||||
const mods = await this.getMods();
|
||||
|
||||
for (const [modId, mod] of Object.entries(mods)) {
|
||||
try {
|
||||
if (!mod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(await IOUtils.exists(this.getModFolder(modId)))) {
|
||||
await this.installMod(mod);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ZenMods]: Error checking for mod changes', e);
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerModsUpdate();
|
||||
}
|
||||
|
||||
async requestMod(modId) {
|
||||
const url = this.#composeModApiUrl(modId);
|
||||
|
||||
console.debug(`[ZenMods]: Fetching mod ${modId} info from ${url}`);
|
||||
|
||||
const data = await fetch(url, {
|
||||
mode: 'no-cors',
|
||||
});
|
||||
|
||||
if (data.ok) {
|
||||
try {
|
||||
const obj = await data.json();
|
||||
|
||||
return obj;
|
||||
} catch (e) {
|
||||
console.error(`[ZenMods]: Error parsing mod ${modId} info:`, e);
|
||||
}
|
||||
} else {
|
||||
console.error(`[ZenMods]: Error fetching mod ${modId} info:`, data.status);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async isModInstalled(modId) {
|
||||
const mods = await this.getMods();
|
||||
return Boolean(mods?.[modId]);
|
||||
}
|
||||
|
||||
// public properties end
|
||||
}
|
||||
|
||||
window.gZenMods = new ZenMods();
|
||||
|
||||
gZenActorsManager.addJSWindowActor('ZenModsMarketplace', {
|
||||
parent: {
|
||||
esModuleURI: 'resource:///actors/ZenModsMarketplaceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'resource:///actors/ZenModsMarketplaceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
},
|
||||
},
|
||||
matches: [
|
||||
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
||||
'about:preferences',
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
var ZenThemesCommon = {
|
||||
kZenColors: [
|
||||
'#aac7ff',
|
||||
'#74d7cb',
|
||||
'#a0d490',
|
||||
'#dec663',
|
||||
'#ffb787',
|
||||
'#dec1b1',
|
||||
'#ffb1c0',
|
||||
'#ddbfc3',
|
||||
'#f6b0ea',
|
||||
'#d4bbff',
|
||||
],
|
||||
|
||||
get browsers() {
|
||||
return Services.wm.getEnumerator('navigator:browser');
|
||||
},
|
||||
|
||||
get currentBrowser() {
|
||||
return Services.wm.getMostRecentWindow('navigator:browser');
|
||||
},
|
||||
|
||||
get themesRootPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
||||
},
|
||||
|
||||
get themesDataFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||
},
|
||||
|
||||
getThemeFolder(themeId) {
|
||||
return PathUtils.join(this.themesRootPath, themeId);
|
||||
},
|
||||
|
||||
async getThemes() {
|
||||
if (!(await IOUtils.exists(this.themesDataFile))) {
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
}
|
||||
|
||||
let themes = {};
|
||||
try {
|
||||
themes = await IOUtils.readJSON(this.themesDataFile);
|
||||
if (themes === null || typeof themes !== 'object') {
|
||||
throw new Error('Themes data file is null');
|
||||
}
|
||||
} catch {
|
||||
// If we have a corrupted file, reset it
|
||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
||||
|
||||
Services.wm
|
||||
.getMostRecentWindow('navigator:browser')
|
||||
.gZenUIManager.showToast('zen-themes-corrupted', {
|
||||
timeout: 8000,
|
||||
});
|
||||
}
|
||||
return themes;
|
||||
},
|
||||
|
||||
async getThemePreferences(theme) {
|
||||
const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json');
|
||||
|
||||
if (!(await IOUtils.exists(themePath)) || !theme.preferences) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const preferences = await IOUtils.readJSON(themePath);
|
||||
|
||||
// compat mode for old preferences, all of them can only be checkboxes
|
||||
if (typeof preferences === 'object' && !Array.isArray(preferences)) {
|
||||
console.warn(
|
||||
`[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences`
|
||||
);
|
||||
const newThemePreferences = [];
|
||||
|
||||
for (let [entry, label] of Object.entries(preferences)) {
|
||||
const [, negation = '', os = '', property] =
|
||||
/(!?)(?:(macos|windows|linux):)?([A-Za-z0-9-_.]+)/g.exec(entry);
|
||||
const isNegation = negation === '!';
|
||||
|
||||
if (
|
||||
(isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) ||
|
||||
(os !== '' &&
|
||||
os !== gZenOperatingSystemCommonUtils.currentOperatingSystem &&
|
||||
!isNegation)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newThemePreferences.push({
|
||||
property,
|
||||
label,
|
||||
type: 'checkbox',
|
||||
disabledOn: os !== '' ? [os] : [],
|
||||
});
|
||||
}
|
||||
|
||||
return newThemePreferences;
|
||||
}
|
||||
|
||||
return preferences.filter(
|
||||
({ disabledOn = [] }) =>
|
||||
!disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(`[ZenThemes]: Error reading preferences for ${theme.name}:`, e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
throttle(mainFunction, delay) {
|
||||
let timerFlag = null;
|
||||
|
||||
return (...args) => {
|
||||
if (timerFlag === null) {
|
||||
mainFunction(...args);
|
||||
timerFlag = setTimeout(() => {
|
||||
timerFlag = null;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
debounce(mainFunction, wait) {
|
||||
let timerFlag;
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timerFlag);
|
||||
timerFlag = setTimeout(() => {
|
||||
mainFunction(...args);
|
||||
}, wait);
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,335 +0,0 @@
|
||||
const kZenStylesheetThemeHeader = '/* Zen Themes - Generated by ZenThemesImporter.';
|
||||
const kZenStylesheetThemeHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
||||
* Your changes will be overwritten.
|
||||
* Instead, go to the preferences and edit the themes there.
|
||||
*/
|
||||
`;
|
||||
const kenStylesheetFooter = `
|
||||
/* End of Zen Themes */
|
||||
`;
|
||||
const getCurrentDateTime = () =>
|
||||
new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'full',
|
||||
}).format(new Date().getTime());
|
||||
|
||||
var gZenStylesheetManager = {
|
||||
async writeStylesheet(path, themes) {
|
||||
let content = kZenStylesheetThemeHeader;
|
||||
content += `\n* FILE GENERATED AT: ${getCurrentDateTime()}\n`;
|
||||
content += kZenStylesheetThemeHeaderBody;
|
||||
|
||||
for (let theme of themes) {
|
||||
if (theme.enabled !== undefined && !theme.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
content += this.getThemeCSS(theme);
|
||||
}
|
||||
|
||||
content += kenStylesheetFooter;
|
||||
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
|
||||
await IOUtils.write(path, buffer);
|
||||
},
|
||||
|
||||
getThemeCSS(theme) {
|
||||
let css = '\n';
|
||||
|
||||
css += `/* Name: ${theme.name} */\n`;
|
||||
css += `/* Description: ${theme.description} */\n`;
|
||||
css += `/* Author: @${theme.author} */\n`;
|
||||
|
||||
if (theme._readmeURL) {
|
||||
css += `/* Readme: ${theme.readme} */\n`;
|
||||
}
|
||||
|
||||
css += `@import url("${theme._chromeURL}");\n`;
|
||||
|
||||
return css;
|
||||
},
|
||||
};
|
||||
|
||||
var gZenThemesImporter = new (class {
|
||||
constructor() {
|
||||
try {
|
||||
window.SessionStore.promiseInitialized.then(async () => {
|
||||
if (
|
||||
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
||||
Services.appinfo.inSafeMode
|
||||
) {
|
||||
console.log('[ZenThemesImporter]: Disabling all themes.');
|
||||
return;
|
||||
}
|
||||
|
||||
await ZenThemesCommon.getThemes(); // Check for any errors in the themes data file
|
||||
const themes = await this.getEnabledThemes();
|
||||
|
||||
const themesWithPreferences = await Promise.all(
|
||||
themes.map(async (theme) => {
|
||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
||||
|
||||
return {
|
||||
name: theme.name,
|
||||
enabled: theme.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.writeToDom(themesWithPreferences);
|
||||
|
||||
await this.insertStylesheet();
|
||||
});
|
||||
console.info('[ZenThemesImporter]: Zen Themes imported');
|
||||
} catch (e) {
|
||||
console.error('[ZenThemesImporter]: Error importing Zen Themes: ', e);
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(
|
||||
'zen.themes.updated-value-observer',
|
||||
this.rebuildThemeStylesheet.bind(this),
|
||||
false
|
||||
);
|
||||
Services.prefs.addObserver(
|
||||
'zen.themes.disable-all',
|
||||
this.handleDisableThemes.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
get sss() {
|
||||
if (!this._sss) {
|
||||
this._sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
||||
Ci.nsIStyleSheetService
|
||||
);
|
||||
}
|
||||
return this._sss;
|
||||
}
|
||||
|
||||
get styleSheetPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
||||
}
|
||||
|
||||
async handleDisableThemes() {
|
||||
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
||||
console.log('[ZenThemesImporter]: Disabling themes module.');
|
||||
|
||||
await this.removeStylesheet();
|
||||
} else {
|
||||
console.log('[ZenThemesImporter]: Enabling themes module.');
|
||||
|
||||
await this.rebuildThemeStylesheet();
|
||||
}
|
||||
}
|
||||
|
||||
get styleSheetURI() {
|
||||
if (!this._styleSheetURI) {
|
||||
this._styleSheetURI = Services.io.newFileURI(new FileUtils.File(this.styleSheetPath));
|
||||
}
|
||||
return this._styleSheetURI;
|
||||
}
|
||||
|
||||
getStylesheetURIForTheme(theme) {
|
||||
return Services.io.newFileURI(
|
||||
new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))
|
||||
);
|
||||
}
|
||||
|
||||
async insertStylesheet() {
|
||||
if (await IOUtils.exists(this.styleSheetPath)) {
|
||||
await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
||||
}
|
||||
|
||||
if (this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET)) {
|
||||
console.debug('[ZenThemesImporter]: Sheet successfully registered');
|
||||
}
|
||||
}
|
||||
|
||||
async removeStylesheet() {
|
||||
await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
||||
const rv = this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET);
|
||||
await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });
|
||||
|
||||
if (!rv && !(await IOUtils.exists(this.styleSheetPath))) {
|
||||
console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
|
||||
}
|
||||
}
|
||||
|
||||
async rebuildThemeStylesheet() {
|
||||
await this.removeStylesheet();
|
||||
|
||||
const themes = await this.getEnabledThemes();
|
||||
|
||||
await this.writeStylesheet(themes);
|
||||
|
||||
const themesWithPreferences = await Promise.all(
|
||||
themes.map(async (theme) => {
|
||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
||||
|
||||
return {
|
||||
name: theme.name,
|
||||
enabled: theme.enabled,
|
||||
preferences,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
this.setDefaults(themesWithPreferences);
|
||||
this.writeToDom(themesWithPreferences);
|
||||
|
||||
await this.insertStylesheet();
|
||||
}
|
||||
|
||||
async getEnabledThemes() {
|
||||
const themeObject = await ZenThemesCommon.getThemes();
|
||||
const themes = Object.values(themeObject).filter(
|
||||
(theme) => theme.enabled === undefined || theme.enabled
|
||||
);
|
||||
|
||||
const themeList = themes.map(({ name }) => name).join(', ');
|
||||
|
||||
const message =
|
||||
themeList !== ''
|
||||
? `[ZenThemesImporter]: Loading enabled Zen themes: ${themeList}.`
|
||||
: '[ZenThemesImporter]: No enabled Zen themes.';
|
||||
|
||||
console.log(message);
|
||||
|
||||
return themes;
|
||||
}
|
||||
|
||||
setDefaults(themesWithPreferences) {
|
||||
for (const { preferences, enabled } of themesWithPreferences) {
|
||||
if (enabled !== undefined && !enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { type, property, defaultValue } of preferences) {
|
||||
if (defaultValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'checkbox') {
|
||||
const value = Services.prefs.getBoolPref(property, false);
|
||||
if (typeof defaultValue !== 'boolean') {
|
||||
console.log(
|
||||
`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
Services.prefs.setBoolPref(property, defaultValue);
|
||||
}
|
||||
} else {
|
||||
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
||||
|
||||
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
||||
console.log(
|
||||
`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value === 'zen-property-no-saved') {
|
||||
Services.prefs.setStringPref(property, defaultValue.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeToDom(themesWithPreferences) {
|
||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||
for (const { enabled, preferences, name } of themesWithPreferences) {
|
||||
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||
|
||||
if (enabled !== undefined && !enabled) {
|
||||
const element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { property, type } of preferences) {
|
||||
const value = Services.prefs.getStringPref(property, '');
|
||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||
|
||||
switch (type) {
|
||||
case 'dropdown': {
|
||||
if (value !== '') {
|
||||
let element = browser.document.getElementById(sanitizedName);
|
||||
|
||||
if (!element) {
|
||||
element = browser.document.createElement('div');
|
||||
|
||||
element.style.display = 'none';
|
||||
element.setAttribute('id', sanitizedName);
|
||||
|
||||
browser.document.body.appendChild(element);
|
||||
}
|
||||
|
||||
element.setAttribute(sanitizedProperty, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'string': {
|
||||
if (value === '') {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.removeProperty(`--${sanitizedProperty}`);
|
||||
} else {
|
||||
browser.document
|
||||
.querySelector(':root')
|
||||
.style.setProperty(`--${sanitizedProperty}`, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async writeStylesheet(themeList = []) {
|
||||
const themes = [];
|
||||
|
||||
for (let theme of themeList) {
|
||||
theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
|
||||
themes.push(theme);
|
||||
}
|
||||
|
||||
await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
|
||||
}
|
||||
})();
|
||||
|
||||
gZenActorsManager.addJSWindowActor('ZenThemeMarketplace', {
|
||||
parent: {
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs',
|
||||
events: {
|
||||
DOMContentLoaded: {},
|
||||
},
|
||||
},
|
||||
matches: [
|
||||
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
||||
'about:preferences',
|
||||
],
|
||||
});
|
||||
142
src/zen/mods/actors/ZenModsMarketplaceChild.sys.mjs
Normal file
142
src/zen/mods/actors/ZenModsMarketplaceChild.sys.mjs
Normal file
@@ -0,0 +1,142 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenModsMarketplaceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type === 'DOMContentLoaded') {
|
||||
const verifier = this.contentWindow.document.querySelector(
|
||||
'meta[name="zen-content-verified"]'
|
||||
);
|
||||
|
||||
if (verifier) {
|
||||
verifier.setAttribute('content', 'verified');
|
||||
}
|
||||
|
||||
this.initiateModsMarketplace();
|
||||
|
||||
this.contentWindow.document.addEventListener(
|
||||
'ZenCheckForModUpdates',
|
||||
this.checkForModUpdates.bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This function will be called from about:preferences
|
||||
checkForModUpdates(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdates');
|
||||
}
|
||||
|
||||
initiateModsMarketplace() {
|
||||
this.contentWindow.setTimeout(() => {
|
||||
this.addButtons();
|
||||
this.injectMarketplaceAPI();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get actionButton() {
|
||||
return this.contentWindow.document.getElementById('install-theme');
|
||||
}
|
||||
|
||||
get actionButtonUninstall() {
|
||||
return this.contentWindow.document.getElementById('install-theme-uninstall');
|
||||
}
|
||||
|
||||
async isThemeInstalled(themeId) {
|
||||
return await this.sendQuery('ZenModsMarketplace:IsModInstalled', { themeId });
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenModsMarketplace:ModChanged': {
|
||||
const modId = message.data.modId;
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonInstalled = this.actionButtonUninstall;
|
||||
|
||||
if (actionButton && actionButtonInstalled) {
|
||||
actionButton.disabled = false;
|
||||
actionButtonInstalled.disabled = false;
|
||||
|
||||
if (await this.isThemeInstalled(modId)) {
|
||||
actionButton.classList.add('hidden');
|
||||
actionButtonInstalled.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
actionButtonInstalled.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenModsMarketplace:CheckForUpdatesFinished': {
|
||||
const updates = message.data.updates;
|
||||
|
||||
this.contentWindow.document.dispatchEvent(
|
||||
new CustomEvent('ZenModsMarketplace:CheckForUpdatesFinished', { detail: { updates } })
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
injectMarketplaceAPI() {
|
||||
Cu.exportFunction(this.handleModInstallationEvent.bind(this), this.contentWindow, {
|
||||
defineAs: 'ZenInstallMod',
|
||||
});
|
||||
}
|
||||
|
||||
async addButtons() {
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonUninstall = this.actionButtonUninstall;
|
||||
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
|
||||
if (!actionButton || !actionButtonUninstall) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessage.classList.add('hidden');
|
||||
|
||||
const themeId = actionButton.getAttribute('zen-theme-id');
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButtonUninstall.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
}
|
||||
|
||||
actionButton.addEventListener('click', this.handleModInstallationEvent.bind(this));
|
||||
actionButtonUninstall.addEventListener('click', this.handleModUninstallEvent.bind(this));
|
||||
}
|
||||
|
||||
async handleModUninstallEvent(event) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
|
||||
const modId = button.getAttribute('zen-theme-id');
|
||||
|
||||
this.sendAsyncMessage('ZenModsMarketplace:UninstallMod', { modId });
|
||||
}
|
||||
|
||||
async handleModInstallationEvent(event) {
|
||||
// Object can be an event or a theme id
|
||||
let modId;
|
||||
|
||||
if (event.target) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
|
||||
modId = button.getAttribute('zen-theme-id');
|
||||
} else {
|
||||
// Backwards compatibility is... Interesting
|
||||
modId = event.themeId ?? event.modId ?? event.id;
|
||||
}
|
||||
|
||||
this.sendAsyncMessage('ZenModsMarketplace:InstallMod', { modId });
|
||||
}
|
||||
}
|
||||
65
src/zen/mods/actors/ZenModsMarketplaceParent.sys.mjs
Normal file
65
src/zen/mods/actors/ZenModsMarketplaceParent.sys.mjs
Normal file
@@ -0,0 +1,65 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenModsMarketplaceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
get modsManager() {
|
||||
return this.browsingContext.topChromeWindow.gZenMods;
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenModsMarketplace:InstallMod': {
|
||||
const modId = message.data.modId;
|
||||
const mod = await this.modsManager.requestMod(modId);
|
||||
|
||||
console.log(`[ZenModsMarketplaceParent]: Installing mod ${mod.id}`);
|
||||
|
||||
mod.enabled = true;
|
||||
|
||||
const mods = await this.modsManager.getMods();
|
||||
mods[mod.id] = mod;
|
||||
|
||||
await this.modsManager.updateMods(mods);
|
||||
await this.updateChildProcesses(mod.id);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ZenModsMarketplace:UninstallMod': {
|
||||
const modId = message.data.modId;
|
||||
console.log(`[ZenModsMarketplaceParent]: Uninstalling mod ${modId}`);
|
||||
|
||||
const mods = await this.modsManager.getMods();
|
||||
|
||||
delete mods[modId];
|
||||
|
||||
await this.modsManager.removeMod(modId);
|
||||
await this.modsManager.updateMods(mods);
|
||||
|
||||
await this.updateChildProcesses(modId);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ZenModsMarketplace:CheckForUpdates': {
|
||||
const updates = await this.modsManager.checkForModsUpdates();
|
||||
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdatesFinished', { updates });
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenModsMarketplace:IsModInstalled': {
|
||||
const themeId = message.data.themeId;
|
||||
const themes = await this.modsManager.getMods();
|
||||
|
||||
return Boolean(themes?.[themeId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateChildProcesses(modId) {
|
||||
this.sendAsyncMessage('ZenModsMarketplace:ModChanged', { modId });
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenThemeMarketplaceChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'DOMContentLoaded':
|
||||
this.initalizeZenAPI(event);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
initalizeZenAPI(event) {
|
||||
const verifier = this.contentWindow.document.querySelector('meta[name="zen-content-verified"]');
|
||||
|
||||
if (verifier) {
|
||||
verifier.setAttribute('content', 'verified');
|
||||
}
|
||||
|
||||
const possibleRicePage = this.collectRiceMetadata();
|
||||
|
||||
if (possibleRicePage?.id) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:RicePage', possibleRicePage);
|
||||
return;
|
||||
}
|
||||
|
||||
this.initiateThemeMarketplace();
|
||||
this.contentWindow.document.addEventListener(
|
||||
'ZenCheckForThemeUpdates',
|
||||
this.checkForThemeUpdates.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
collectRiceMetadata() {
|
||||
const meta = this.contentWindow.document.querySelector('meta[name="zen-rice-data"]');
|
||||
if (meta) {
|
||||
return {
|
||||
id: meta.getAttribute('data-id'),
|
||||
name: meta.getAttribute('data-name'),
|
||||
author: meta.getAttribute('data-author'),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This function will be called from about:preferences
|
||||
checkForThemeUpdates(event) {
|
||||
event.preventDefault();
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdates');
|
||||
}
|
||||
|
||||
initiateThemeMarketplace() {
|
||||
this.contentWindow.setTimeout(() => {
|
||||
this.addInstallButtons();
|
||||
this.injectMarketplaceAPI();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get actionButton() {
|
||||
return this.contentWindow.document.getElementById('install-theme');
|
||||
}
|
||||
|
||||
get actionButtonUninstall() {
|
||||
return this.contentWindow.document.getElementById('install-theme-uninstall');
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenThemeMarketplace:ThemeChanged': {
|
||||
const themeId = message.data.themeId;
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonInstalled = this.actionButtonUninstall;
|
||||
|
||||
if (actionButton && actionButtonInstalled) {
|
||||
actionButton.disabled = false;
|
||||
actionButtonInstalled.disabled = false;
|
||||
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButton.classList.add('hidden');
|
||||
actionButtonInstalled.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
actionButtonInstalled.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenThemeMarketplace:CheckForUpdatesFinished': {
|
||||
const updates = message.data.updates;
|
||||
|
||||
this.contentWindow.document.dispatchEvent(
|
||||
new CustomEvent('ZenThemeMarketplace:CheckForUpdatesFinished', { detail: { updates } })
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ZenThemeMarketplace:GetThemeInfo': {
|
||||
const themeId = message.data.themeId;
|
||||
const theme = await this.getThemeInfo(themeId);
|
||||
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
injectMarketplaceAPI() {
|
||||
Cu.exportFunction(this.installTheme.bind(this), this.contentWindow, {
|
||||
defineAs: 'ZenInstallTheme',
|
||||
});
|
||||
}
|
||||
|
||||
async addInstallButtons() {
|
||||
const actionButton = this.actionButton;
|
||||
const actionButtonUninstall = this.actionButtonUninstall;
|
||||
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
|
||||
if (!actionButton || !actionButtonUninstall) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessage.classList.add('hidden');
|
||||
|
||||
const themeId = actionButton.getAttribute('zen-theme-id');
|
||||
if (await this.isThemeInstalled(themeId)) {
|
||||
actionButtonUninstall.classList.remove('hidden');
|
||||
} else {
|
||||
actionButton.classList.remove('hidden');
|
||||
}
|
||||
|
||||
actionButton.addEventListener('click', this.installTheme.bind(this));
|
||||
actionButtonUninstall.addEventListener('click', this.uninstallTheme.bind(this));
|
||||
}
|
||||
|
||||
async isThemeInstalled(themeId) {
|
||||
return await this.sendQuery('ZenThemeMarketplace:IsThemeInstalled', { themeId });
|
||||
}
|
||||
|
||||
addTheme(theme) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:InstallTheme', { theme });
|
||||
}
|
||||
|
||||
getThemeAPIUrl(themeId) {
|
||||
return `https://zen-browser.github.io/theme-store/themes/${themeId}/theme.json`;
|
||||
}
|
||||
|
||||
async getThemeInfo(themeId) {
|
||||
const url = this.getThemeAPIUrl(themeId);
|
||||
const data = await fetch(url, {
|
||||
mode: 'no-cors',
|
||||
});
|
||||
|
||||
if (data.ok) {
|
||||
try {
|
||||
const obj = await data.json();
|
||||
return obj;
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplace: Error parsing theme info: ', e);
|
||||
}
|
||||
} else {
|
||||
console.error('ZenThemeMarketplace: Error fetching theme info: ', data.status);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async uninstallTheme(event) {
|
||||
const button = event.target;
|
||||
button.disabled = true;
|
||||
const themeId = button.getAttribute('zen-theme-id');
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:UninstallTheme', { themeId });
|
||||
}
|
||||
|
||||
async installTheme(object) {
|
||||
// Object can be an event or a theme id
|
||||
let themeId;
|
||||
if (object.target) {
|
||||
const button = object.target;
|
||||
button.disabled = true;
|
||||
themeId = button.getAttribute('zen-theme-id');
|
||||
} else {
|
||||
themeId = object.themeId;
|
||||
}
|
||||
console.info('ZenThemeMarketplace: Installing theme with id: ', themeId);
|
||||
|
||||
const theme = await this.getThemeInfo(themeId);
|
||||
if (!theme) {
|
||||
console.error('ZenThemeMarketplace: Error fetching theme info');
|
||||
return;
|
||||
}
|
||||
this.addTheme(theme);
|
||||
}
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
export class ZenThemeMarketplaceParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case 'ZenThemeMarketplace:InstallTheme': {
|
||||
console.info('ZenThemeMarketplaceParent: Updating themes');
|
||||
const theme = message.data.theme;
|
||||
theme.enabled = true;
|
||||
const themes = await this.getThemes();
|
||||
themes[theme.id] = theme;
|
||||
this.updateThemes(themes);
|
||||
this.updateChildProcesses(theme.id);
|
||||
break;
|
||||
}
|
||||
case 'ZenThemeMarketplace:UninstallTheme': {
|
||||
console.info('ZenThemeMarketplaceParent: Uninstalling theme');
|
||||
const themeId = message.data.themeId;
|
||||
const themes = await this.getThemes();
|
||||
delete themes[themeId];
|
||||
this.removeTheme(themeId);
|
||||
this.updateThemes(themes);
|
||||
this.updateChildProcesses(themeId);
|
||||
break;
|
||||
}
|
||||
case 'ZenThemeMarketplace:IsThemeInstalled': {
|
||||
const themeId = message.data.themeId;
|
||||
const themes = await this.getThemes();
|
||||
|
||||
return Boolean(themes?.[themeId]);
|
||||
}
|
||||
case 'ZenThemeMarketplace:CheckForUpdates': {
|
||||
this.checkForThemeUpdates();
|
||||
break;
|
||||
}
|
||||
case 'ZenThemeMarketplace:RicePage': {
|
||||
this.openRicePage(this.browsingContext.topChromeWindow, message.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openRicePage(window, data) {
|
||||
window.gZenThemePicker.riceManager.openRicePage(data);
|
||||
}
|
||||
|
||||
compareVersions(version1, version2) {
|
||||
let result = false;
|
||||
|
||||
if (typeof version1 !== 'object') {
|
||||
version1 = version1.toString().split('.');
|
||||
}
|
||||
|
||||
if (typeof version2 !== 'object') {
|
||||
version2 = version2.toString().split('.');
|
||||
}
|
||||
|
||||
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
|
||||
if (version1[i] == undefined) {
|
||||
version1[i] = 0;
|
||||
}
|
||||
if (version2[i] == undefined) {
|
||||
version2[i] = 0;
|
||||
}
|
||||
if (Number(version1[i]) < Number(version2[i])) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
if (version1[i] != version2[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async checkForThemeUpdates() {
|
||||
console.info('ZenThemeMarketplaceParent: Checking for theme updates');
|
||||
|
||||
let updates = [];
|
||||
const themes = await this.getThemes();
|
||||
for (const theme of Object.values(themes)) {
|
||||
try {
|
||||
const themeInfo = await this.sendQuery('ZenThemeMarketplace:GetThemeInfo', {
|
||||
themeId: theme.id,
|
||||
});
|
||||
|
||||
if (!themeInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.compareVersions(themeInfo.version, theme.version || '0.0.0') &&
|
||||
themeInfo.version != theme.version
|
||||
) {
|
||||
console.info(
|
||||
'ZenThemeMarketplaceParent: Theme update found',
|
||||
theme.id,
|
||||
theme.version,
|
||||
themeInfo.version
|
||||
);
|
||||
|
||||
themeInfo.enabled = theme.enabled;
|
||||
updates.push(themeInfo);
|
||||
|
||||
await this.removeTheme(theme.id, false);
|
||||
themes[themeInfo.id] = themeInfo;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error checking for theme updates', e);
|
||||
}
|
||||
}
|
||||
|
||||
await this.updateThemes(themes);
|
||||
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdatesFinished', { updates });
|
||||
}
|
||||
|
||||
async updateChildProcesses(themeId) {
|
||||
this.sendAsyncMessage('ZenThemeMarketplace:ThemeChanged', { themeId });
|
||||
}
|
||||
|
||||
async getThemes() {
|
||||
return await IOUtils.readJSON(this.themesDataFile);
|
||||
}
|
||||
|
||||
async updateThemes(themes = undefined) {
|
||||
if (!themes) {
|
||||
themes = await this.getThemes();
|
||||
}
|
||||
await IOUtils.writeJSON(this.themesDataFile, themes);
|
||||
await this.checkForThemeChanges();
|
||||
}
|
||||
|
||||
getStyleSheetFullContent(style = '') {
|
||||
let stylesheet = '@-moz-document url-prefix("chrome:") {\n';
|
||||
|
||||
for (const line of style.split('\n')) {
|
||||
stylesheet += ` ${line}\n`;
|
||||
}
|
||||
|
||||
stylesheet += '}';
|
||||
|
||||
return stylesheet;
|
||||
}
|
||||
|
||||
async downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`ZenThemeMarketplaceParent: HTTP error! status: ${response.status} for url: ${url}`
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.text();
|
||||
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
|
||||
// convert the data into a Uint8Array
|
||||
const buffer = new TextEncoder().encode(content);
|
||||
await IOUtils.write(path, buffer);
|
||||
|
||||
return;
|
||||
} catch (e) {
|
||||
attempt++;
|
||||
if (attempt >= maxRetries) {
|
||||
console.error('ZenThemeMarketplaceParent: Error downloading file after retries', url, e);
|
||||
} else {
|
||||
console.warn(
|
||||
`ZenThemeMarketplaceParent: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
|
||||
url,
|
||||
e
|
||||
);
|
||||
await new Promise((res) => setTimeout(res, retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async downloadThemeFileContents(theme) {
|
||||
try {
|
||||
const themePath = PathUtils.join(this.themesRootPath, theme.id);
|
||||
await IOUtils.makeDirectory(themePath, { ignoreExisting: true });
|
||||
|
||||
await this.downloadUrlToFile(theme.style, PathUtils.join(themePath, 'chrome.css'), true);
|
||||
await this.downloadUrlToFile(theme.readme, PathUtils.join(themePath, 'readme.md'));
|
||||
|
||||
if (theme.preferences) {
|
||||
await this.downloadUrlToFile(
|
||||
theme.preferences,
|
||||
PathUtils.join(themePath, 'preferences.json')
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('ZenThemeMarketplaceParent: Error downloading theme file contents', theme.id, e);
|
||||
}
|
||||
}
|
||||
|
||||
get themesRootPath() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
||||
}
|
||||
|
||||
get themesDataFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||
}
|
||||
|
||||
triggerThemeUpdate() {
|
||||
const pref = 'zen.themes.updated-value-observer';
|
||||
Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
|
||||
}
|
||||
|
||||
async installTheme(theme) {
|
||||
try {
|
||||
await this.downloadThemeFileContents(theme);
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error installing theme', theme.id, e);
|
||||
}
|
||||
}
|
||||
|
||||
async checkForThemeChanges() {
|
||||
const themes = await this.getThemes();
|
||||
|
||||
const themeIds = Object.keys(themes);
|
||||
|
||||
for (const themeId of themeIds) {
|
||||
try {
|
||||
const theme = themes[themeId];
|
||||
if (!theme) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
||||
if (!(await IOUtils.exists(themePath))) {
|
||||
await this.installTheme(theme);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ZenThemeMarketplaceParent: Error checking for theme changes', e);
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerThemeUpdate();
|
||||
}
|
||||
|
||||
async removeTheme(themeId, triggerUpdate = true) {
|
||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
||||
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
|
||||
|
||||
if (triggerUpdate) {
|
||||
this.triggerThemeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
FINAL_TARGET_FILES.actors += [
|
||||
"actors/ZenThemeMarketplaceChild.sys.mjs",
|
||||
"actors/ZenThemeMarketplaceParent.sys.mjs",
|
||||
"actors/ZenModsMarketplaceChild.sys.mjs",
|
||||
"actors/ZenModsMarketplaceParent.sys.mjs",
|
||||
]
|
||||
|
||||
@@ -104,6 +104,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
);
|
||||
|
||||
window.addEventListener('TabClose', this.handleTabClose.bind(this));
|
||||
window.addEventListener('TabBrowserDiscarded', this.handleTabBrowserDiscarded.bind(this));
|
||||
window.addEventListener('TabSelect', this.onTabSelect.bind(this));
|
||||
this.initializeContextMenu();
|
||||
this.insertIntoContextMenu();
|
||||
@@ -150,6 +151,22 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
this.removeTabFromGroup(tab, groupIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event - The event that triggered the tab browser discard.
|
||||
* @description Handles the tab browser discard event.
|
||||
*/
|
||||
async handleTabBrowserDiscarded(event) {
|
||||
const tab = event.target;
|
||||
if (tab.group?.hasAttribute('split-view-group')) {
|
||||
gBrowser.explicitUnloadTabs(tab.group.tabs);
|
||||
for (const t of tab.group.tabs) {
|
||||
if (t.glanceTab) {
|
||||
gBrowser.explicitUnloadTabs([t.glanceTab]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event - The event that triggered the tab select.
|
||||
* @description Handles the tab select event.
|
||||
@@ -193,6 +210,10 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
const node = this.getSplitNodeFromTab(tab);
|
||||
const toUpdate = this.removeNode(node);
|
||||
this.applyGridLayout(toUpdate);
|
||||
// Select next tab if the removed tab was selected
|
||||
if (gBrowser.selectedTab === tab) {
|
||||
gBrowser.selectedTab = group.tabs[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,7 +897,9 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
tabCount: window.gBrowser.selectedTabs.length,
|
||||
});
|
||||
document.getElementById('context_zenSplitTabs').setAttribute('data-l10n-args', tabCountInfo);
|
||||
document.getElementById('context_zenSplitTabs').disabled = !this.contextCanSplitTabs();
|
||||
document
|
||||
.getElementById('context_zenSplitTabs')
|
||||
.setAttribute('disabled', !this.contextCanSplitTabs());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -929,8 +952,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
window.gContextMenu.contentData.docLocation ||
|
||||
window.gContextMenu.target.ownerDocument.location.href;
|
||||
const currentTab = gZenGlanceManager.getTabOrGlanceParent(window.gBrowser.selectedTab);
|
||||
const newTab = this.openAndSwitchToTab(url);
|
||||
this.splitTabs([currentTab, newTab]);
|
||||
const newTab = this.openAndSwitchToTab(url, { inBackground: false });
|
||||
this.splitTabs([currentTab, newTab], undefined, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -976,7 +999,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
tab = tab.parentNode.closest('.tabbrowser-tab');
|
||||
console.assert(tab, 'Tab not found for zen-glance-tab');
|
||||
}
|
||||
this.updateSplitViewButton(!tab?.splitView);
|
||||
if (tab) {
|
||||
this.updateSplitView(tab);
|
||||
tab.linkedBrowser.docShellIsActive = true;
|
||||
@@ -1008,7 +1030,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
* Splits the given tabs.
|
||||
*
|
||||
* @param {Tab[]} tabs - The tabs to split.
|
||||
* @param {string} gridType - The type of grid layout.
|
||||
* @param {string|undefined} gridType - The type of grid layout.
|
||||
*/
|
||||
splitTabs(tabs, gridType, initialIndex = 0) {
|
||||
// TODO: Add support for splitting essential tabs
|
||||
@@ -1074,7 +1096,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
};
|
||||
this._data.push(splitData);
|
||||
if (!this._sessionRestoring) {
|
||||
window.gBrowser.selectedTab = tabs[0];
|
||||
window.gBrowser.selectedTab = tabs[initialIndex] ?? tabs[0];
|
||||
}
|
||||
|
||||
// Add tabs to the split view group
|
||||
@@ -1110,7 +1132,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
|
||||
if (oldView === newView) return;
|
||||
if (newView < 0 && oldView >= 0) {
|
||||
this.updateSplitViewButton(true);
|
||||
this.deactivateCurrentSplitView();
|
||||
return;
|
||||
}
|
||||
@@ -1122,6 +1143,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
* Deactivates the split view.
|
||||
*/
|
||||
deactivateCurrentSplitView() {
|
||||
if (this.currentView < 0) return;
|
||||
this.setTabsDocShellState(this._data[this.currentView].tabs, false);
|
||||
for (const tab of this._data[this.currentView].tabs) {
|
||||
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
|
||||
@@ -1129,9 +1151,9 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
}
|
||||
this.removeSplitters();
|
||||
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
||||
this.updateSplitViewButton(true);
|
||||
this.currentView = -1;
|
||||
this.toggleWrapperDisplay(false);
|
||||
this.maybeDisableOpeningTabOnSplitView();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1153,7 +1175,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
|
||||
this.tabBrowserPanel.setAttribute('zen-split-view', 'true');
|
||||
|
||||
this.updateSplitViewButton(false);
|
||||
this.applyGridToTabs(splitData.tabs);
|
||||
this.applyGridLayout(splitData.layoutTree);
|
||||
this.setTabsDocShellState(splitData.tabs, true);
|
||||
@@ -1290,6 +1311,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
});
|
||||
}
|
||||
});
|
||||
this.maybeDisableOpeningTabOnSplitView();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1480,20 +1502,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
container.style.inset = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the split view button visibility.
|
||||
*
|
||||
* @param {boolean} hidden - Indicates if the button should be hidden.
|
||||
*/
|
||||
updateSplitViewButton(hidden) {
|
||||
const button = document.getElementById('zen-split-views-box');
|
||||
if (hidden) {
|
||||
button?.setAttribute('hidden', 'true');
|
||||
} else {
|
||||
button?.removeAttribute('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI of the panel.
|
||||
*
|
||||
@@ -1668,6 +1676,15 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
|
||||
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
||||
gBrowser.moveTabToGroup(draggedTab, splitGroup);
|
||||
if (hoverSide === 'left' || hoverSide === 'top') {
|
||||
try {
|
||||
splitGroup.tabs[0].before(draggedTab);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Failed to move tab ${draggedTab.id} before ${splitGroup.tabs[0].id}: ${e}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
||||
@@ -1857,6 +1874,26 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||
this.onLocationChange(gBrowser.selectedTab.linkedBrowser);
|
||||
}
|
||||
}
|
||||
|
||||
maybeDisableOpeningTabOnSplitView() {
|
||||
const shouldBeDisabled = !this.canOpenLinkInSplitView();
|
||||
document
|
||||
.getElementById('cmd_zenSplitViewLinkInNewTab')
|
||||
.setAttribute('disabled', shouldBeDisabled);
|
||||
document.getElementById('zen-glance-sidebar-split').setAttribute('disabled', shouldBeDisabled);
|
||||
}
|
||||
|
||||
canOpenLinkInSplitView() {
|
||||
const currentView = this.currentView;
|
||||
if (currentView < 0) {
|
||||
return true;
|
||||
}
|
||||
const group = this._data[currentView];
|
||||
if (!group || group.tabs.length >= this.MAX_TABS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenViewSplitter = new ZenViewSplitter();
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
#zen-splitview-dropzone {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split='true'] {
|
||||
transition: inset 0.09s ease-out !important;
|
||||
& browser {
|
||||
@@ -70,15 +74,8 @@
|
||||
margin-right: calc(-1 * var(--zen-split-column-gap));
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels:has(> [zen-split='true']),
|
||||
#zen-splitview-overlay {
|
||||
:root:not([zen-compact-mode='true']):not([customizing]) & {
|
||||
@media -moz-pref('zen.view.compact.hide-toolbar') {
|
||||
& {
|
||||
margin-top: calc(var(--zen-split-column-gap) * -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
:root:not([customizing]) #zen-splitview-overlay {
|
||||
margin-top: calc(var(--zen-split-column-gap) * -1);
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view] {
|
||||
@@ -112,10 +109,6 @@
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
#zen-split-views-box:not([hidden='true']) {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.zen-view-splitter-header-container {
|
||||
position: absolute;
|
||||
top: calc(var(--zen-split-column-gap) / -2);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
const lazy = {};
|
||||
|
||||
@@ -87,18 +90,12 @@
|
||||
|
||||
onTabIconChanged(tab, url = null) {
|
||||
const iconUrl = url ?? tab.iconImage.src;
|
||||
if (!iconUrl) {
|
||||
if (!iconUrl && tab.hasAttribute('zen-pin-id')) {
|
||||
try {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
let favicon = await PlacesUtils.favicons.getFaviconForPage(
|
||||
Services.io.newURI(pin.url)
|
||||
);
|
||||
if (favicon) {
|
||||
gBrowser.setIcon(tab, favicon.dataURI);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error getting favicon URL:', error);
|
||||
const favicon = await this.getFaviconAsBase64(tab.linkedBrowser.currentURI);
|
||||
if (favicon) {
|
||||
gBrowser.setIcon(tab, favicon);
|
||||
}
|
||||
});
|
||||
} catch {}
|
||||
@@ -156,6 +153,9 @@
|
||||
await gZenWorkspaces.promiseSectionsInitialized;
|
||||
await this._initializePinsCache();
|
||||
await this._initializePinnedTabs(init);
|
||||
if (init) {
|
||||
this._resolveInitializedPinnedCache();
|
||||
}
|
||||
}
|
||||
|
||||
async _initializePinsCache() {
|
||||
@@ -641,8 +641,7 @@
|
||||
return faviconData.dataURI;
|
||||
} catch (ex) {
|
||||
console.error('Failed to get favicon:', ex);
|
||||
// console.error("Failed to get favicon:", ex);
|
||||
return `page-icon:${pageUrl}`; // Use this as a fallback
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,7 +658,7 @@
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
let tab = tabs[i];
|
||||
const section = gZenWorkspaces.getEssentialsSection(tab);
|
||||
if (section.children.length >= this.MAX_ESSENTIALS_TABS) {
|
||||
if (!this.canEssentialBeAdded(tab)) {
|
||||
movedAll = false;
|
||||
continue;
|
||||
}
|
||||
@@ -791,7 +790,7 @@
|
||||
document.getElementById('context_zen-add-essential').hidden =
|
||||
contextTab.getAttribute('zen-essential') ||
|
||||
!!contextTab.group ||
|
||||
gBrowser._numZenEssentials >= this.MAX_ESSENTIALS_TABS;
|
||||
!this.canEssentialBeAdded(contextTab);
|
||||
document.getElementById('context_zen-remove-essential').hidden =
|
||||
!contextTab.getAttribute('zen-essential');
|
||||
document.getElementById('context_unpinTab').hidden =
|
||||
@@ -943,7 +942,7 @@
|
||||
} else {
|
||||
tab.setAttribute('zen-pinned-changed', 'true');
|
||||
}
|
||||
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl})`);
|
||||
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl.spec})`);
|
||||
}
|
||||
|
||||
removeTabContainersDragoverClass() {
|
||||
@@ -1004,6 +1003,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
canEssentialBeAdded(tab) {
|
||||
return (
|
||||
!(
|
||||
(tab.getAttribute('usercontextid') || 0) !=
|
||||
gZenWorkspaces.getActiveWorkspaceFromCache().containerTabId &&
|
||||
gZenWorkspaces.containerSpecificEssentials
|
||||
) && gBrowser._numZenEssentials < this.MAX_ESSENTIALS_TABS
|
||||
);
|
||||
}
|
||||
|
||||
applyDragoverClass(event, draggedTab) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
@@ -1037,10 +1046,7 @@
|
||||
shouldAddDragOverElement = true;
|
||||
}
|
||||
} else if (essentialTabsTarget) {
|
||||
if (
|
||||
!draggedTab.hasAttribute('zen-essential') &&
|
||||
gBrowser._numZenEssentials < this.MAX_ESSENTIALS_TABS
|
||||
) {
|
||||
if (!draggedTab.hasAttribute('zen-essential') && this.canEssentialBeAdded(draggedTab)) {
|
||||
shouldAddDragOverElement = true;
|
||||
isVertical = false;
|
||||
}
|
||||
@@ -1117,4 +1123,8 @@
|
||||
}
|
||||
|
||||
window.gZenPinnedTabManager = new ZenPinnedTabManager();
|
||||
|
||||
gZenPinnedTabManager.promisePinnedCacheInitialized = new Promise((resolve) => {
|
||||
gZenPinnedTabManager._resolveInitializedPinnedCache = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
var ZenPinnedTabsStorage = {
|
||||
async init() {
|
||||
await this._ensureTable();
|
||||
@@ -106,8 +109,8 @@ var ZenPinnedTabsStorage = {
|
||||
`
|
||||
INSERT OR REPLACE INTO zen_pins (
|
||||
uuid, title, url, container_id, workspace_uuid, position,
|
||||
is_essential, is_group, parent_uuid, created_at, updated_at,
|
||||
edited_title
|
||||
is_essential, is_group, parent_uuid, edited_title, created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
|
||||
:is_essential, :is_group, :parent_uuid, :edited_title,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
height: var(--zen-toolbar-height);
|
||||
z-index: 1;
|
||||
|
||||
|
||||
@@ -356,6 +356,7 @@
|
||||
margin-block: 0 !important;
|
||||
margin-inline: 0 !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: calc(var(--border-radius-medium) - 2px);
|
||||
}
|
||||
/* Adjust padding for content */
|
||||
& .tab-content {
|
||||
@@ -374,6 +375,27 @@
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
& .tab-audio-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&[soundplaying]:not([muted]) .tab-icon-stack::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 110%;
|
||||
height: 110%;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 1;
|
||||
background: url('chrome://browser/content/zen-images/note-indicator.svg') no-repeat;
|
||||
top: -70%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.8s ease;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Essentials Glance Tab Specifics (Floating) --- */
|
||||
@@ -709,7 +731,6 @@
|
||||
|
||||
/* Hide text labels */
|
||||
& .zen-current-workspace-indicator-name,
|
||||
& .zen-workspace-actions,
|
||||
& zen-workspace .toolbarbutton-text {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -1240,7 +1261,7 @@
|
||||
|
||||
padding-bottom: var(--zen-toolbox-padding);
|
||||
overflow: hidden;
|
||||
gap: calc(var(--zen-toolbox-padding) - 2px);
|
||||
gap: 4px;
|
||||
transition:
|
||||
max-height 0.3s ease-out,
|
||||
grid-template-columns 0.3s ease-out;
|
||||
@@ -1293,15 +1314,15 @@
|
||||
|
||||
/* Style background */
|
||||
& .tab-background {
|
||||
border-radius: var(--border-radius-medium) !important; /* Use medium radius */
|
||||
transition: background 0.1s ease-in-out; /* Smooth background transition */
|
||||
border-radius: var(--border-radius-medium) !important;
|
||||
transition: background 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
--tab-selected-bgcolor: light-dark(rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.2));
|
||||
|
||||
&:not([visuallyselected], [multiselected='true']) .tab-background {
|
||||
background: var(--zen-toolbar-element-bg); /* Use generic element background */
|
||||
border: none; /* No border */
|
||||
background: var(--zen-toolbar-element-bg);
|
||||
border: none;
|
||||
}
|
||||
|
||||
& .tab-content {
|
||||
|
||||
@@ -9,8 +9,8 @@ function openGlanceOnTab(callback, close = true) {
|
||||
gZenGlanceManager
|
||||
.openGlance({
|
||||
url: 'https://example.com',
|
||||
x: 0,
|
||||
y: 0,
|
||||
clientX: 0,
|
||||
clientY: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
})
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"compact_mode/browser.toml",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
SOURCES += [
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIZenCommonUtils.idl",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
SOURCES += [
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
"common",
|
||||
|
||||
@@ -6,8 +6,13 @@
|
||||
var _tabsToPin = [];
|
||||
var _tabsToPinEssentials = [];
|
||||
|
||||
const kZenElementsToIgnore = ['zen-browser-background'];
|
||||
|
||||
function clearBrowserElements() {
|
||||
for (const element of document.getElementById('browser').children) {
|
||||
if (kZenElementsToIgnore.includes(element.id)) {
|
||||
continue;
|
||||
}
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
@@ -266,11 +271,15 @@
|
||||
document.getElementById('zen-welcome').remove();
|
||||
document.documentElement.removeAttribute('zen-welcome-stage');
|
||||
for (const element of document.getElementById('browser').children) {
|
||||
if (kZenElementsToIgnore.includes(element.id)) {
|
||||
continue;
|
||||
}
|
||||
element.style.opacity = 0;
|
||||
element.style.removeProperty('display');
|
||||
}
|
||||
gZenUIManager.updateTabsToolbar();
|
||||
await animate('#browser > *', { opacity: [0, 1] });
|
||||
let elementsToIgnore = kZenElementsToIgnore.map((id) => `#${id}`).join(', ');
|
||||
await animate(`#browser > *:not(${elementsToIgnore})`, { opacity: [0, 1] });
|
||||
gZenUIManager.showToast('zen-welcome-finished');
|
||||
}
|
||||
|
||||
|
||||
@@ -207,8 +207,8 @@
|
||||
}
|
||||
|
||||
#zen-welcome-initial-essentials-browser {
|
||||
width: 70%;
|
||||
height: 80%;
|
||||
min-width: 70%;
|
||||
min-height: 80%;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
margin-top: auto;
|
||||
@@ -282,7 +282,7 @@
|
||||
--border-radius-medium: 1rem;
|
||||
|
||||
&[visuallyselected] {
|
||||
transform: scale(1.04);
|
||||
transform: scale(1.06);
|
||||
}
|
||||
|
||||
& .tab-background::after {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
initContextMenu() {
|
||||
const menu = window.MozXULElement.parseXULToFragment(`
|
||||
<menuitem class="zenToolbarThemePicker"
|
||||
<menuitem id="zenToolbarThemePicker"
|
||||
data-lazy-l10n-id="zen-workspaces-change-gradient"
|
||||
command="cmd_zenOpenZenThemePicker"/>
|
||||
`);
|
||||
@@ -944,10 +944,7 @@
|
||||
return `rgba(${color.c[0]}, ${color.c[1]}, ${color.c[2]}, ${this.currentOpacity})`;
|
||||
}
|
||||
|
||||
getGradient(colors, forToolbar = false, rotation = undefined) {
|
||||
if (typeof rotation === 'undefined') {
|
||||
rotation = this.currentRotation;
|
||||
}
|
||||
getGradient(colors, forToolbar = false) {
|
||||
const themedColors = this.themedColors(colors);
|
||||
this.useAlgo = themedColors[0]?.algorithm ?? '';
|
||||
|
||||
@@ -958,12 +955,12 @@
|
||||
} else if (themedColors.length === 1) {
|
||||
return this.getSingleRGBColor(themedColors[0], forToolbar);
|
||||
} else if (themedColors.length !== 3) {
|
||||
return `linear-gradient(${rotation}deg, ${themedColors.map((color) => this.getSingleRGBColor(color, forToolbar)).join(', ')})`;
|
||||
return `linear-gradient(${this.currentRotation}deg, ${themedColors.map((color) => this.getSingleRGBColor(color, forToolbar)).join(', ')})`;
|
||||
} else {
|
||||
let color1 = this.getSingleRGBColor(themedColors[2], forToolbar);
|
||||
let color2 = this.getSingleRGBColor(themedColors[0], forToolbar);
|
||||
let color3 = this.getSingleRGBColor(themedColors[1], forToolbar);
|
||||
return `linear-gradient(${rotation}deg, ${color1}, ${color2}, ${color3})`;
|
||||
return `linear-gradient(${this.currentRotation}deg, ${color1}, ${color2}, ${color3})`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,6 +1122,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const appBackground = browser.document.getElementById('zen-browser-background');
|
||||
if (!skipUpdate) {
|
||||
browser.document.documentElement.style.setProperty(
|
||||
'--zen-main-browser-background-old',
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenWorkspace extends MozXULElement {
|
||||
static get markup() {
|
||||
@@ -5,9 +8,8 @@
|
||||
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator" flex="1">
|
||||
<hbox class="zen-current-workspace-indicator-icon"></hbox>
|
||||
<hbox class="zen-current-workspace-indicator-name"></hbox>
|
||||
<toolbarbutton class="toolbarbutton-1 chromeclass-toolbar-additional zen-workspaces-actions" context="zenWorkspaceMoreActions"></toolbarbutton>
|
||||
</vbox>
|
||||
<arrowscrollbox orient="vertical" tabindex="-1" class="workspace-arrowscrollbox">
|
||||
<arrowscrollbox orient="vertical" class="workspace-arrowscrollbox">
|
||||
<vbox class="zen-workspace-tabs-section zen-workspace-pinned-tabs-section">
|
||||
<html:div class="vertical-pinned-tabs-container-separator"></html:div>
|
||||
</vbox>
|
||||
@@ -58,13 +60,6 @@
|
||||
false
|
||||
);
|
||||
|
||||
this.indicator.querySelector('.zen-current-workspace-indicator-name').onRenameFinished =
|
||||
this.onIndicatorRenameFinished.bind(this);
|
||||
|
||||
this.indicator
|
||||
.querySelector('.zen-workspaces-actions')
|
||||
.addEventListener('click', this.onActionsCommand.bind(this));
|
||||
|
||||
this.scrollbox.addEventListener('wheel', this, true);
|
||||
this.scrollbox.addEventListener('underflow', this);
|
||||
this.scrollbox.addEventListener('overflow', this);
|
||||
@@ -176,38 +171,6 @@
|
||||
gBrowser.tabContainer.handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
get workspaceUuid() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
async onIndicatorRenameFinished(newName) {
|
||||
if (newName === '') {
|
||||
return;
|
||||
}
|
||||
let workspaces = (await gZenWorkspaces._workspaces()).workspaces;
|
||||
let workspaceData = workspaces.find((workspace) => workspace.uuid === this.workspaceUuid);
|
||||
workspaceData.name = newName;
|
||||
await gZenWorkspaces.saveWorkspace(workspaceData);
|
||||
this.indicator.querySelector('.zen-current-workspace-indicator-name').textContent = newName;
|
||||
gZenUIManager.showToast('zen-workspace-renamed-toast');
|
||||
}
|
||||
|
||||
onActionsCommand(event) {
|
||||
event.stopPropagation();
|
||||
const popup = document.getElementById('zenWorkspaceMoreActions');
|
||||
event.target.setAttribute('open', 'true');
|
||||
this.indicator.setAttribute('open', 'true');
|
||||
popup.addEventListener(
|
||||
'popuphidden',
|
||||
() => {
|
||||
event.target.removeAttribute('open');
|
||||
this.indicator.removeAttribute('open');
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
popup.openPopup(event.target, 'after_start');
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('zen-workspace', ZenWorkspace);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
class ZenWorkspaceIcons extends MozXULElement {
|
||||
constructor() {
|
||||
|
||||
@@ -837,6 +837,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
|
||||
async initializeWorkspaces() {
|
||||
if (this.workspaceEnabled) {
|
||||
this._initializeWorkspaceCreationIcons();
|
||||
this._initializeWorkspaceTabContextMenus();
|
||||
await this.workspaceBookmarks();
|
||||
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
|
||||
@@ -852,6 +853,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
console.error('gZenWorkspaces: Error initializing theme picker', e);
|
||||
}
|
||||
this.onWindowResize();
|
||||
if (window.gZenSessionStore) {
|
||||
await gZenSessionStore.promiseInitialized;
|
||||
}
|
||||
await this._selectStartPage();
|
||||
this._fixTabPositions();
|
||||
this._resolveInitialized();
|
||||
@@ -909,7 +913,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
) {
|
||||
this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`);
|
||||
setTimeout(() => {
|
||||
gBrowser.selectedTab = tabs[this._tabToSelect];
|
||||
gBrowser.selectedTab = gZenGlanceManager.getTabOrGlanceParent(tabs[this._tabToSelect]);
|
||||
this._removedByStartupPage = true;
|
||||
gBrowser.removeTab(this._tabToRemoveForEmpty, {
|
||||
skipSessionStore: true,
|
||||
@@ -1060,48 +1064,84 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
}
|
||||
|
||||
addPopupListeners() {
|
||||
const workspaceActions = document.getElementById('zenWorkspaceMoreActions');
|
||||
workspaceActions.addEventListener('popupshowing', this.updateWorkspaceActionsMenu.bind(this));
|
||||
const popup = document.getElementById('PanelUI-zen-workspaces');
|
||||
const contextMenu = document.getElementById('zenWorkspaceActionsMenu');
|
||||
|
||||
const contextChangeContainerTabMenu = document.getElementById(
|
||||
'context_zenWorkspacesOpenInContainerTab'
|
||||
);
|
||||
contextChangeContainerTabMenu.addEventListener(
|
||||
'popupshowing',
|
||||
this.updateWorkspaceActionsMenuContainer.bind(this)
|
||||
);
|
||||
contextChangeContainerTabMenu.addEventListener(
|
||||
'command',
|
||||
this.contextChangeContainerTab.bind(this)
|
||||
);
|
||||
popup.addEventListener('popuphidden', this.handlePanelHidden.bind(this));
|
||||
popup.addEventListener('command', this.handlePanelCommand.bind(this));
|
||||
|
||||
contextMenu.addEventListener('popuphidden', (event) => {
|
||||
if (event.target === contextMenu) {
|
||||
this.onContextMenuClose(event);
|
||||
}
|
||||
});
|
||||
contextMenu.addEventListener('popupshowing', this.updateContextMenu.bind(this));
|
||||
contextMenu.addEventListener('command', this.handleContextMenuCommand.bind(this));
|
||||
|
||||
const submenu = document.querySelector('#context_zenWorkspacesOpenInContainerTab > menupopup');
|
||||
if (submenu) {
|
||||
submenu.addEventListener('popupshowing', this.createContainerTabMenu.bind(this));
|
||||
submenu.addEventListener('command', this.contextChangeContainerTab.bind(this));
|
||||
}
|
||||
|
||||
const onWorkspaceIconContainerClick = this.onWorkspaceIconContainerClick.bind(this);
|
||||
for (const element of document.querySelectorAll('.PanelUI-zen-workspaces-icons-container')) {
|
||||
element.addEventListener('click', onWorkspaceIconContainerClick);
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById('PanelUI-zen-workspaces-create-input')
|
||||
.addEventListener('input', this.onWorkspaceCreationNameChange.bind(this));
|
||||
document
|
||||
.getElementById('PanelUI-zen-workspaces-edit-input')
|
||||
.addEventListener('input', this.onWorkspaceEditChange.bind(this));
|
||||
document
|
||||
.getElementById('PanelUI-zen-workspaces-icon-search-input')
|
||||
.addEventListener('input', this.conductSearch.bind(this));
|
||||
}
|
||||
|
||||
updateWorkspaceActionsMenu(event) {
|
||||
if (event.target.id !== 'zenWorkspaceMoreActions') {
|
||||
handlePanelCommand(event) {
|
||||
let target = event.target.closest('toolbarbutton');
|
||||
target ??= event.target.closest('button');
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const openInContainerMenuItem = document.getElementById(
|
||||
'context_zenWorkspacesOpenInContainerTab'
|
||||
);
|
||||
if (this.shouldShowContainers) {
|
||||
openInContainerMenuItem.removeAttribute('hidden');
|
||||
} else {
|
||||
openInContainerMenuItem.setAttribute('hidden', 'true');
|
||||
switch (target.id) {
|
||||
case 'PanelUI-zen-workspaces-reorder-mode':
|
||||
this.toggleReorderMode();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-new':
|
||||
this.openSaveDialog();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-create-save':
|
||||
this.saveWorkspaceFromCreate();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-edit-cancel':
|
||||
case 'PanelUI-zen-workspaces-create-cancel':
|
||||
this.closeWorkspacesSubView();
|
||||
break;
|
||||
case 'PanelUI-zen-workspaces-edit-save':
|
||||
this.saveWorkspaceFromEdit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateWorkspaceActionsMenuContainer(event) {
|
||||
const workspace = this.getActiveWorkspaceFromCache();
|
||||
let containerTabId = workspace.containerTabId;
|
||||
return window.createUserContextMenu(event, {
|
||||
isContextMenu: true,
|
||||
excludeUserContextId: containerTabId,
|
||||
showDefaultTab: true,
|
||||
});
|
||||
}
|
||||
|
||||
async contextDeleteWorkspace() {
|
||||
await this.removeWorkspace(this.activeWorkspace, true);
|
||||
handleContextMenuCommand(event) {
|
||||
const target = event.target.closest('menuitem');
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
switch (target.id) {
|
||||
case 'context_zenOpenWorkspace':
|
||||
this.openWorkspace();
|
||||
break;
|
||||
case 'context_zenEditWorkspace':
|
||||
this.contextEdit(event);
|
||||
break;
|
||||
case 'context_zenDeleteWorkspace':
|
||||
this.contextDelete(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
searchIcons(input, icons) {
|
||||
@@ -1165,6 +1205,69 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
return filteredEmojiScores.map((score) => score.emoji);
|
||||
}
|
||||
|
||||
resetWorkspaceIconSearch() {
|
||||
let container = document.getElementById('PanelUI-zen-workspaces-icon-picker-wrapper');
|
||||
let searchInput = document.getElementById('PanelUI-zen-workspaces-icon-search-input');
|
||||
|
||||
// Clear the search input field
|
||||
searchInput.value = '';
|
||||
for (let button of container.querySelectorAll('.toolbarbutton-1')) {
|
||||
button.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
_initializeWorkspaceCreationIcons() {
|
||||
let container = document.getElementById('PanelUI-zen-workspaces-icon-picker-wrapper');
|
||||
let searchInput = document.getElementById('PanelUI-zen-workspaces-icon-search-input');
|
||||
searchInput.value = '';
|
||||
for (let iconData of this.emojis) {
|
||||
const icon = iconData[0];
|
||||
let button = document.createXULElement('toolbarbutton');
|
||||
button.className = 'toolbarbutton-1 workspace-icon-button';
|
||||
button.setAttribute('label', icon);
|
||||
button.onclick = (event) => {
|
||||
const button = event.target;
|
||||
let wasSelected = button.hasAttribute('selected');
|
||||
for (let button of container.children) {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
if (!wasSelected) {
|
||||
button.setAttribute('selected', 'true');
|
||||
}
|
||||
if (this.onIconChangeConnectedCallback) {
|
||||
this.onIconChangeConnectedCallback(icon);
|
||||
} else {
|
||||
this.onWorkspaceIconChangeInner('create', icon);
|
||||
}
|
||||
};
|
||||
container.appendChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
conductSearch() {
|
||||
const container = document.getElementById('PanelUI-zen-workspaces-icon-picker-wrapper');
|
||||
const searchInput = document.getElementById('PanelUI-zen-workspaces-icon-search-input');
|
||||
const query = searchInput.value.toLowerCase();
|
||||
|
||||
if (query === '') {
|
||||
this.resetWorkspaceIconSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
const buttons = Array.from(container.querySelectorAll('.toolbarbutton-1'));
|
||||
buttons.forEach((button) => (button.style.display = 'none'));
|
||||
|
||||
const filteredIcons = this.searchIcons(query, this.emojis);
|
||||
|
||||
filteredIcons.forEach((emoji) => {
|
||||
const matchingButton = buttons.find((button) => button.getAttribute('label') === emoji);
|
||||
if (matchingButton) {
|
||||
matchingButton.style.display = '';
|
||||
container.appendChild(matchingButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async saveWorkspace(workspaceData, preventPropagation = false) {
|
||||
if (this.privateWindowOrDisabled) {
|
||||
return;
|
||||
@@ -1211,6 +1314,83 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
);
|
||||
}
|
||||
// Workspaces dialog UI management
|
||||
|
||||
openSaveDialog() {
|
||||
let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
|
||||
// randomly select an icon
|
||||
let icon = this.emojis[Math.floor(Math.random() * (this.emojis.length - 257))][0];
|
||||
this._workspaceCreateInput.textContent = '';
|
||||
this._workspaceCreateInput.value = '';
|
||||
this._workspaceCreateInput.setAttribute('data-initial-value', '');
|
||||
document
|
||||
.querySelectorAll('#PanelUI-zen-workspaces-icon-picker-wrapper toolbarbutton')
|
||||
.forEach((button) => {
|
||||
if (button.label === icon) {
|
||||
button.setAttribute('selected', 'true');
|
||||
} else {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
});
|
||||
document.querySelector('.PanelUI-zen-workspaces-icons-container.create').textContent = icon;
|
||||
|
||||
PanelUI.showSubView('PanelUI-zen-workspaces-create', parentPanel);
|
||||
}
|
||||
|
||||
async openEditDialog(workspaceUuid) {
|
||||
this._workspaceEditDialog.setAttribute('data-workspace-uuid', workspaceUuid);
|
||||
document.getElementById('PanelUI-zen-workspaces-edit-save').setAttribute('disabled', 'true');
|
||||
let workspaces = (await this._workspaces()).workspaces;
|
||||
let workspaceData = workspaces.find((workspace) => workspace.uuid === workspaceUuid);
|
||||
this._workspaceEditInput.textContent = workspaceData.name;
|
||||
this._workspaceEditInput.value = workspaceData.name;
|
||||
this._workspaceEditInput.setAttribute('data-initial-value', workspaceData.name);
|
||||
this._workspaceEditIconsContainer.setAttribute('data-initial-value', workspaceData.icon);
|
||||
this.onIconChangeConnectedCallback = (...args) => {
|
||||
this.onWorkspaceIconChangeInner('edit', ...args);
|
||||
this.onWorkspaceEditChange(...args);
|
||||
};
|
||||
document
|
||||
.querySelectorAll('#PanelUI-zen-workspaces-icon-picker-wrapper toolbarbutton')
|
||||
.forEach((button) => {
|
||||
if (button.label === workspaceData.icon) {
|
||||
button.setAttribute('selected', 'true');
|
||||
} else {
|
||||
button.removeAttribute('selected');
|
||||
}
|
||||
});
|
||||
document.querySelector('.PanelUI-zen-workspaces-icons-container.edit').textContent =
|
||||
this.getWorkspaceIcon(workspaceData);
|
||||
let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
PanelUI.showSubView('PanelUI-zen-workspaces-edit', parentPanel);
|
||||
}
|
||||
|
||||
onWorkspaceIconChangeInner(type = 'create', icon) {
|
||||
const container = document.querySelector(`.PanelUI-zen-workspaces-icons-container.${type}`);
|
||||
if (container.textContent !== icon) {
|
||||
container.textContent = icon;
|
||||
}
|
||||
this.goToPreviousSubView();
|
||||
}
|
||||
|
||||
onWorkspaceIconContainerClick(event) {
|
||||
event.preventDefault();
|
||||
const parentPanel = document.getElementById('PanelUI-zen-workspaces-edit');
|
||||
PanelUI.showSubView('PanelUI-zen-workspaces-icon-picker', parentPanel);
|
||||
|
||||
const container = parentPanel.parentNode.querySelector('.panel-viewcontainer');
|
||||
setTimeout(() => {
|
||||
if (container) {
|
||||
container.style.minHeight = 'unset';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
goToPreviousSubView() {
|
||||
const parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
parentPanel.goBack();
|
||||
}
|
||||
|
||||
workspaceHasIcon(workspace) {
|
||||
return workspace.icon && workspace.icon !== '';
|
||||
}
|
||||
@@ -1244,15 +1424,230 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
return;
|
||||
}
|
||||
|
||||
let workspaceList = browser.document.getElementById('PanelUI-zen-workspaces-list');
|
||||
const createWorkspaceElement = (workspace) => {
|
||||
let element = browser.document.createXULElement('toolbarbutton');
|
||||
element.className = 'subviewbutton zen-workspace-button';
|
||||
element.setAttribute('tooltiptext', workspace.name);
|
||||
element.setAttribute('zen-workspace-id', workspace.uuid);
|
||||
if (this.isWorkspaceActive(workspace)) {
|
||||
element.setAttribute('active', 'true');
|
||||
}
|
||||
let containerGroup = undefined;
|
||||
try {
|
||||
containerGroup = browser.ContextualIdentityService.getPublicIdentities().find(
|
||||
(container) => container.userContextId === workspace.containerTabId
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn('gZenWorkspaces: Error setting container color', e);
|
||||
}
|
||||
if (containerGroup) {
|
||||
element.classList.add('identity-color-' + containerGroup.color);
|
||||
element.setAttribute('data-usercontextid', containerGroup.userContextId);
|
||||
}
|
||||
// Set draggable attribute based on reorder mode
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
element.setAttribute('draggable', 'true');
|
||||
}
|
||||
element.addEventListener(
|
||||
'dragstart',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
this.draggedElement = element;
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('text/plain', element.getAttribute('zen-workspace-id'));
|
||||
|
||||
// Create a transparent drag image for Linux
|
||||
if (AppConstants.platform === 'linux') {
|
||||
const dragImage = document.createElement('canvas');
|
||||
dragImage.width = 1;
|
||||
dragImage.height = 1;
|
||||
event.dataTransfer.setDragImage(dragImage, 0, 0);
|
||||
}
|
||||
|
||||
element.classList.add('dragging');
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragover',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// Ensure the dragover effect is visible on Linux
|
||||
if (AppConstants.platform === 'linux') {
|
||||
const targetId = element.getAttribute('zen-workspace-id');
|
||||
const draggedId = this.draggedElement.getAttribute('zen-workspace-id');
|
||||
if (targetId !== draggedId) {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener('dragenter', function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('dragleave', function (event) {
|
||||
element.classList.remove('dragover');
|
||||
});
|
||||
|
||||
element.addEventListener(
|
||||
'drop',
|
||||
async function (event) {
|
||||
event.preventDefault();
|
||||
element.classList.remove('dragover');
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
const draggedWorkspaceId = event.dataTransfer.getData('text/plain');
|
||||
const targetWorkspaceId = element.getAttribute('zen-workspace-id');
|
||||
if (draggedWorkspaceId !== targetWorkspaceId) {
|
||||
await this.moveWorkspace(draggedWorkspaceId, targetWorkspaceId);
|
||||
}
|
||||
if (this.draggedElement) {
|
||||
this.draggedElement.classList.remove('dragging');
|
||||
this.draggedElement = null;
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragend',
|
||||
function (event) {
|
||||
if (this.draggedElement) {
|
||||
this.draggedElement.classList.remove('dragging');
|
||||
this.draggedElement = null;
|
||||
}
|
||||
const workspaceElements = browser.document.querySelectorAll('.zen-workspace-button');
|
||||
for (const elem of workspaceElements) {
|
||||
elem.classList.remove('dragover');
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
let childs = browser.MozXULElement.parseXULToFragment(`
|
||||
<div class="zen-workspace-icon">
|
||||
</div>
|
||||
<vbox>
|
||||
<div class="zen-workspace-name">
|
||||
</div>
|
||||
<div class="zen-workspace-container" ${containerGroup ? '' : 'hidden="true"'}>
|
||||
</div>
|
||||
</vbox>
|
||||
<image class="toolbarbutton-icon zen-workspace-actions-reorder-icon" ></image>
|
||||
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-actions">
|
||||
<image class="toolbarbutton-icon" id="zen-workspace-actions-menu-icon"></image>
|
||||
</toolbarbutton>
|
||||
`);
|
||||
|
||||
// use text content instead of innerHTML to avoid XSS
|
||||
childs.querySelector('.zen-workspace-icon').textContent =
|
||||
browser.gZenWorkspaces.getWorkspaceIcon(workspace);
|
||||
childs.querySelector('.zen-workspace-name').textContent = workspace.name;
|
||||
if (containerGroup) {
|
||||
childs.querySelector('.zen-workspace-container').textContent =
|
||||
ContextualIdentityService.getUserContextLabel(containerGroup.userContextId);
|
||||
}
|
||||
|
||||
childs.querySelector('.zen-workspace-actions').addEventListener(
|
||||
'command',
|
||||
((event) => {
|
||||
let button = event.target;
|
||||
this._contextMenuId = button
|
||||
.closest('toolbarbutton[zen-workspace-id]')
|
||||
.getAttribute('zen-workspace-id');
|
||||
const popup = button.ownerDocument.getElementById('zenWorkspaceActionsMenu');
|
||||
popup.openPopup(button, 'after_end');
|
||||
}).bind(browser.gZenWorkspaces)
|
||||
);
|
||||
element.appendChild(childs);
|
||||
element.onclick = (async () => {
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
return; // Return early if reorder mode is on
|
||||
}
|
||||
if (event.target.closest('.zen-workspace-actions')) {
|
||||
return; // Ignore clicks on the actions button
|
||||
}
|
||||
const workspaceId = element.getAttribute('zen-workspace-id');
|
||||
const workspaces = await this._workspaces();
|
||||
const workspace = workspaces.workspaces.find((w) => w.uuid === workspaceId);
|
||||
await this.changeWorkspace(workspace);
|
||||
let panel = this.ownerWindow.document.getElementById('PanelUI-zen-workspaces');
|
||||
PanelMultiView.hidePopup(panel);
|
||||
}).bind(browser.gZenWorkspaces);
|
||||
return element;
|
||||
};
|
||||
|
||||
const createLastPositionDropTarget = () => {
|
||||
const element = browser.document.createXULElement('div');
|
||||
element.className = 'zen-workspace-last-place-drop-target';
|
||||
|
||||
element.addEventListener(
|
||||
'dragover',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// Ensure the dragover effect is visible on Linux
|
||||
if (AppConstants.platform === 'linux') {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragenter',
|
||||
function (event) {
|
||||
if (this.isReorderModeOn(browser) && this.draggedElement) {
|
||||
element.classList.add('dragover');
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'dragleave',
|
||||
function (event) {
|
||||
element.classList.remove('dragover');
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
'drop',
|
||||
async function (event) {
|
||||
event.preventDefault();
|
||||
element.classList.remove('dragover');
|
||||
|
||||
if (this.isReorderModeOn(browser)) {
|
||||
const draggedWorkspaceId = event.dataTransfer.getData('text/plain');
|
||||
await this.moveWorkspaceToEnd(draggedWorkspaceId);
|
||||
|
||||
if (this.draggedElement) {
|
||||
this.draggedElement.classList.remove('dragging');
|
||||
this.draggedElement = null;
|
||||
}
|
||||
}
|
||||
}.bind(browser.gZenWorkspaces)
|
||||
);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
if (clearCache) {
|
||||
browser.gZenWorkspaces._workspaceCache = null;
|
||||
browser.gZenWorkspaces._workspaceBookmarksCache = null;
|
||||
await browser.gZenWorkspaces.workspaceBookmarks();
|
||||
}
|
||||
let workspaces = await browser.gZenWorkspaces._workspaces();
|
||||
browser.document
|
||||
.getElementById('cmd_zenCtxDeleteWorkspace')
|
||||
.setAttribute('disabled', workspaces.workspaces.length <= 1);
|
||||
if (clearCache) {
|
||||
browser.dispatchEvent(
|
||||
new CustomEvent('ZenWorkspacesUIUpdate', {
|
||||
@@ -1261,12 +1656,39 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
})
|
||||
);
|
||||
}
|
||||
await browser.gZenWorkspaces.workspaceBookmarks();
|
||||
workspaceList.innerHTML = '';
|
||||
workspaceList.parentNode.style.display = 'flex';
|
||||
if (workspaces.workspaces.length <= 0) {
|
||||
workspaceList.innerHTML = 'No workspaces available';
|
||||
workspaceList.setAttribute('empty', 'true');
|
||||
} else {
|
||||
workspaceList.removeAttribute('empty');
|
||||
}
|
||||
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
let workspaceElement = createWorkspaceElement(workspace);
|
||||
workspaceList.appendChild(workspaceElement);
|
||||
}
|
||||
|
||||
workspaceList.appendChild(createLastPositionDropTarget());
|
||||
|
||||
if (!ignoreStrip) {
|
||||
browser.gZenWorkspaces._fixIndicatorsNames(workspaces);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handlePanelHidden() {
|
||||
const workspacesList = document.getElementById('PanelUI-zen-workspaces-list');
|
||||
const reorderModeButton = document.getElementById('PanelUI-zen-workspaces-reorder-mode');
|
||||
|
||||
workspacesList?.removeAttribute('reorder-mode');
|
||||
reorderModeButton?.removeAttribute('active');
|
||||
this.resetWorkspaceIconSearch();
|
||||
this.clearEmojis();
|
||||
}
|
||||
|
||||
async moveWorkspaceToEnd(draggedWorkspaceId) {
|
||||
const workspaces = (await this._workspaces()).workspaces;
|
||||
const draggedIndex = workspaces.findIndex((w) => w.uuid === draggedWorkspaceId);
|
||||
@@ -1277,6 +1699,39 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
await this._propagateWorkspaceData();
|
||||
}
|
||||
|
||||
isReorderModeOn(browser) {
|
||||
return (
|
||||
browser.document
|
||||
.getElementById('PanelUI-zen-workspaces-list')
|
||||
.getAttribute('reorder-mode') === 'true'
|
||||
);
|
||||
}
|
||||
|
||||
toggleReorderMode() {
|
||||
const workspacesList = document.getElementById('PanelUI-zen-workspaces-list');
|
||||
const reorderModeButton = document.getElementById('PanelUI-zen-workspaces-reorder-mode');
|
||||
const isActive = workspacesList.getAttribute('reorder-mode') === 'true';
|
||||
if (isActive) {
|
||||
workspacesList.removeAttribute('reorder-mode');
|
||||
reorderModeButton.removeAttribute('active');
|
||||
} else {
|
||||
workspacesList.setAttribute('reorder-mode', 'true');
|
||||
reorderModeButton.setAttribute('active', 'true');
|
||||
}
|
||||
|
||||
// Update draggable attribute
|
||||
const workspaceElements = document.querySelectorAll('.zen-workspace-button');
|
||||
workspaceElements.forEach((elem) => {
|
||||
// When reorder mode is toggled off, remove draggable attribute
|
||||
// When reorder mode is toggled on, set draggable attribute
|
||||
if (isActive) {
|
||||
elem.removeAttribute('draggable');
|
||||
} else {
|
||||
elem.setAttribute('draggable', 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async moveWorkspace(draggedWorkspaceId, targetWorkspaceId) {
|
||||
const workspaces = (await this._workspaces()).workspaces;
|
||||
const draggedIndex = workspaces.findIndex((w) => w.uuid === draggedWorkspaceId);
|
||||
@@ -1288,8 +1743,45 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
await this._propagateWorkspaceData();
|
||||
}
|
||||
|
||||
async openWorkspacesDialog(event) {
|
||||
if (!this.workspaceEnabled || this.isPrivateWindow) {
|
||||
return;
|
||||
}
|
||||
let target = event.target.closest('.zen-current-workspace-indicator');
|
||||
let panel = document.getElementById('PanelUI-zen-workspaces');
|
||||
await this._propagateWorkspaceData({
|
||||
ignoreStrip: true,
|
||||
clearCache: false,
|
||||
});
|
||||
PanelMultiView.openPopup(panel, target, {
|
||||
position: 'bottomright topright',
|
||||
triggerEvent: event,
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
closeWorkspacesSubView() {
|
||||
let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview');
|
||||
parentPanel.goBack(parentPanel);
|
||||
}
|
||||
|
||||
// Workspaces management
|
||||
|
||||
get _workspaceCreateInput() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-create-input');
|
||||
}
|
||||
|
||||
get _workspaceEditDialog() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-edit');
|
||||
}
|
||||
|
||||
get _workspaceEditInput() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-edit-input');
|
||||
}
|
||||
|
||||
get _workspaceEditIconsContainer() {
|
||||
return document.getElementById('PanelUI-zen-workspaces-icon-picker');
|
||||
}
|
||||
|
||||
_deleteAllTabsInWorkspace(workspaceID) {
|
||||
gBrowser.removeTabs(
|
||||
Array.from(this.allStoredTabs).filter(
|
||||
@@ -1355,6 +1847,57 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
}
|
||||
}
|
||||
|
||||
async saveWorkspaceFromCreate() {
|
||||
let workspaceName = this._workspaceCreateInput.value;
|
||||
if (!workspaceName) {
|
||||
return;
|
||||
}
|
||||
this._workspaceCreateInput.value = '';
|
||||
let icon = document.querySelector('#PanelUI-zen-workspaces-icon-picker-wrapper [selected]');
|
||||
icon?.removeAttribute('selected');
|
||||
await this.createAndSaveWorkspace(workspaceName, icon?.label);
|
||||
this.goToPreviousSubView();
|
||||
}
|
||||
|
||||
async saveWorkspaceFromEdit() {
|
||||
let workspaceUuid = this._workspaceEditDialog.getAttribute('data-workspace-uuid');
|
||||
let workspaceName = this._workspaceEditInput.value;
|
||||
if (!workspaceName) {
|
||||
return;
|
||||
}
|
||||
this._workspaceEditInput.value = '';
|
||||
let icon = document.querySelector('#PanelUI-zen-workspaces-icon-picker-wrapper [selected]');
|
||||
icon?.removeAttribute('selected');
|
||||
let workspaces = (await this._workspaces()).workspaces;
|
||||
let workspaceData = workspaces.find((workspace) => workspace.uuid === workspaceUuid);
|
||||
workspaceData.name = workspaceName;
|
||||
workspaceData.icon = icon?.label;
|
||||
await this.saveWorkspace(workspaceData);
|
||||
this.goToPreviousSubView();
|
||||
}
|
||||
|
||||
onWorkspaceCreationNameChange() {
|
||||
let button = document.getElementById('PanelUI-zen-workspaces-create-save');
|
||||
if (this._workspaceCreateInput.value === '') {
|
||||
button.setAttribute('disabled', 'true');
|
||||
return;
|
||||
}
|
||||
button.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
onWorkspaceEditChange(icon) {
|
||||
let button = document.getElementById('PanelUI-zen-workspaces-edit-save');
|
||||
let name = this._workspaceEditInput.value;
|
||||
if (
|
||||
name === this._workspaceEditInput.getAttribute('data-initial-value') &&
|
||||
icon === this._workspaceEditIconsContainer.getAttribute('data-initial-value')
|
||||
) {
|
||||
button.setAttribute('disabled', 'true');
|
||||
return;
|
||||
}
|
||||
button.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
addChangeListeners(func) {
|
||||
if (!this._changeListeners) {
|
||||
this._changeListeners = [];
|
||||
@@ -1591,6 +2134,21 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
repeat: 0,
|
||||
});
|
||||
essentialsContainer.parentNode.appendChild(essentialsClone);
|
||||
for (let i = 0; i < essentialsClone.children.length; i++) {
|
||||
const child = essentialsClone.children[i];
|
||||
const originalChild = essentialsContainer.children[i];
|
||||
if (!gBrowser.isTab(child) || !gBrowser.isTab(originalChild)) {
|
||||
continue;
|
||||
}
|
||||
const childBg = child.querySelector('.tab-background');
|
||||
const originalChildBg = originalChild.querySelector('.tab-background');
|
||||
if (childBg && originalChildBg) {
|
||||
childBg.style.setProperty(
|
||||
'--zen-tab-icon',
|
||||
originalChildBg.style.getPropertyValue('--zen-tab-icon')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.documentElement.setAttribute('animating-background', 'true');
|
||||
@@ -1776,11 +2334,11 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
|
||||
const newTransform = `translateX(${newOffset}%)`;
|
||||
let existingTransform = `translateX(${existingOffset}%)`;
|
||||
if (container.style.transform) {
|
||||
if (container.style.transform && container.style.transform !== 'none') {
|
||||
existingTransform = container.style.transform;
|
||||
}
|
||||
if (shouldAnimate) {
|
||||
container.style.transform = newTransform;
|
||||
container.style.transform = existingTransform;
|
||||
animations.push(
|
||||
gZenUIManager.motion.animate(
|
||||
container,
|
||||
@@ -1997,8 +2555,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
setTimeout(gURLBar.formatValue.bind(gURLBar), 0);
|
||||
}
|
||||
|
||||
async _fixCtrlTabBehavior() {
|
||||
@@ -2265,9 +2821,49 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
}
|
||||
}
|
||||
|
||||
// Context menu management
|
||||
|
||||
_contextMenuId = null;
|
||||
async updateContextMenu(_) {
|
||||
console.assert(this._contextMenuId, 'No context menu ID set');
|
||||
document
|
||||
.querySelector(
|
||||
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
|
||||
)
|
||||
.setAttribute('active', 'true');
|
||||
const workspaces = await this._workspaces();
|
||||
let deleteMenuItem = document.getElementById('context_zenDeleteWorkspace');
|
||||
if (workspaces.workspaces.length <= 1) {
|
||||
deleteMenuItem.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
deleteMenuItem.removeAttribute('disabled');
|
||||
}
|
||||
let openMenuItem = document.getElementById('context_zenOpenWorkspace');
|
||||
if (
|
||||
workspaces.workspaces.find(
|
||||
(workspace) => workspace.uuid === this._contextMenuId && this.isWorkspaceActive(workspace)
|
||||
)
|
||||
) {
|
||||
openMenuItem.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
openMenuItem.removeAttribute('disabled');
|
||||
}
|
||||
const openInContainerMenuItem = document.getElementById(
|
||||
'context_zenWorkspacesOpenInContainerTab'
|
||||
);
|
||||
if (this.shouldShowContainers) {
|
||||
openInContainerMenuItem.removeAttribute('hidden');
|
||||
} else {
|
||||
openInContainerMenuItem.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
async contextChangeContainerTab(event) {
|
||||
this._organizingWorkspaceStrip = true;
|
||||
let workspace = await this.getActiveWorkspace();
|
||||
let workspaces = await this._workspaces();
|
||||
let workspace = workspaces.workspaces.find(
|
||||
(workspace) => workspace.uuid === this._contextMenuId
|
||||
);
|
||||
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
|
||||
workspace.containerTabId = userContextId + 0; // +0 to convert to number
|
||||
await this.saveWorkspace(workspace);
|
||||
@@ -2280,6 +2876,16 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
onContextMenuClose() {
|
||||
let target = document.querySelector(
|
||||
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
|
||||
);
|
||||
if (target) {
|
||||
target.removeAttribute('active');
|
||||
}
|
||||
this._contextMenuId = null;
|
||||
}
|
||||
|
||||
findTabToBlur(tab) {
|
||||
if ((!this._shouldChangeToTab(tab) || !tab) && this._emptyTab) {
|
||||
return this._emptyTab;
|
||||
@@ -2287,6 +2893,26 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
return tab;
|
||||
}
|
||||
|
||||
async openWorkspace() {
|
||||
let workspaces = await this._workspaces();
|
||||
let workspace = workspaces.workspaces.find(
|
||||
(workspace) => workspace.uuid === this._contextMenuId
|
||||
);
|
||||
await this.changeWorkspace(workspace);
|
||||
}
|
||||
|
||||
async contextDelete(event) {
|
||||
this.__contextIsDelete = true;
|
||||
event.stopPropagation();
|
||||
await this.removeWorkspace(this._contextMenuId);
|
||||
this.__contextIsDelete = false;
|
||||
}
|
||||
|
||||
async contextEdit(event) {
|
||||
event.stopPropagation();
|
||||
await this.openEditDialog(this._contextMenuId);
|
||||
}
|
||||
|
||||
get emojis() {
|
||||
if (this._emojis) {
|
||||
return this._emojis;
|
||||
@@ -2365,6 +2991,18 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
);
|
||||
}
|
||||
|
||||
// Tab browser utilities
|
||||
createContainerTabMenu(event) {
|
||||
let window = event.target.ownerGlobal;
|
||||
const workspace = this.getWorkspaceFromId(this._contextMenuId);
|
||||
let containerTabId = workspace.containerTabId;
|
||||
return window.createUserContextMenu(event, {
|
||||
isContextMenu: true,
|
||||
excludeUserContextId: containerTabId,
|
||||
showDefaultTab: true,
|
||||
});
|
||||
}
|
||||
|
||||
getContextIdIfNeeded(userContextId, fromExternal, allowInheritPrincipal) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return [userContextId, false, undefined];
|
||||
|
||||
@@ -122,9 +122,327 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Workspaces Panel UI */
|
||||
|
||||
#PanelUI-zen-workspaces {
|
||||
--panel-width: 320px;
|
||||
--panel-padding: 0;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces > panelmultiview {
|
||||
align-items: flex-start;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces panelmultiview panelview {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
width: var(--panel-width);
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton[selected='true'] {
|
||||
border-color: var(--zen-colors-secondary);
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton .toolbarbutton-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker toolbarbutton .toolbarbutton-text {
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker {
|
||||
padding: 5px !important;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-picker-wrapper {
|
||||
overflow-x: hidden;
|
||||
justify-items: center;
|
||||
overflow-y: auto;
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
max-height: 250px;
|
||||
|
||||
.workspace-icon-button {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
font-size: 16px;
|
||||
margin: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list[empty='true'] {
|
||||
font-weight: 600;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
text-align: start;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.PanelUI-zen-workspaces-user-create {
|
||||
height: 100%;
|
||||
|
||||
.PanelUI-zen-workspaces-creation-wraper {
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
|
||||
margin-top: 10px;
|
||||
|
||||
& .PanelUI-zen-workspaces-icons-container {
|
||||
padding: 10px 0;
|
||||
min-width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-right: 1px solid var(--zen-colors-border);
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
& html|input {
|
||||
border: none;
|
||||
outline: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Workspace icon picker styles */
|
||||
#PanelUI-zen-workspaces-icon-picker-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-search-bar {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: inherit;
|
||||
z-index: 1000;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-icon-search-input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
border: 1px solid var(--panel-separator-color, #ccc);
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list toolbarbutton {
|
||||
padding: 5px;
|
||||
border-radius: var(--zen-button-border-radius);
|
||||
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
& .zen-workspace-icon {
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: var(--zen-button-border-radius);
|
||||
margin-right: 10px;
|
||||
border: 1px solid var(--zen-colors-border);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&[data-usercontextid] .zen-workspace-icon {
|
||||
border-color: color-mix(in srgb, var(--identity-tab-color) 40%, transparent 60%);
|
||||
}
|
||||
|
||||
& > vbox:has(> .zen-workspace-name) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
& .zen-workspace-name {
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& .zen-workspace-container {
|
||||
font-size: 12px;
|
||||
opacity: 0.5;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
& .zen-workspace-actions,
|
||||
.zen-workspace-actions-reorder-icon {
|
||||
display: none;
|
||||
margin: 0;
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
&.zen-workspace-button[active='true'] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.zen-workspace-button[active='true'] .zen-workspace-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: -2px;
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-workspace-button.dragging {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.zen-workspace-button.dragover::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
}
|
||||
|
||||
/* Enhanced visual feedback for Linux platform */
|
||||
@supports (-moz-gtk-csd-available) {
|
||||
.zen-workspace-button.dragover {
|
||||
background-color: color-mix(in srgb, var(--toolbarbutton-icon-fill-attention) 15%, transparent);
|
||||
}
|
||||
|
||||
.zen-workspace-button.dragover::after {
|
||||
height: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target.dragover {
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
}
|
||||
|
||||
/* Enhanced visual feedback for Linux platform */
|
||||
@supports (-moz-gtk-csd-available) {
|
||||
.zen-workspace-last-place-drop-target {
|
||||
height: 6px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target.dragover {
|
||||
background-color: var(--toolbarbutton-icon-fill-attention);
|
||||
box-shadow: 0 0 4px var(--toolbarbutton-icon-fill-attention);
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-reorder-mode[active='true'] {
|
||||
color: var(--toolbarbutton-icon-fill-attention) !important;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list:not([reorder-mode='true']) toolbarbutton {
|
||||
&:hover .zen-workspace-actions,
|
||||
& .zen-workspace-actions[active='true'] {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list[reorder-mode='true'] toolbarbutton {
|
||||
.zen-workspace-actions-reorder-icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-list[reorder-mode='true'] .zen-workspace-last-place-drop-target {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.zen-workspace-last-place-drop-target {
|
||||
display: none;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-view > vbox:nth-child(2) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-new,
|
||||
#PanelUI-zen-workspaces-reorder-mode,
|
||||
#PanelUI-zen-gradient-generator-color-custom-add {
|
||||
min-height: 1px !important;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-create-footer,
|
||||
#PanelUI-zen-workspaces-edit-footer {
|
||||
padding-bottom: 0 !important;
|
||||
margin-top: 10px;
|
||||
margin-left: 0;
|
||||
margin-bottom: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-create-footer button[default='true'],
|
||||
#PanelUI-zen-workspaces-edit-footer button[default='true'] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#PanelUI-zen-workspaces-header {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* Mark workspaces indicator */
|
||||
.zen-current-workspace-indicator {
|
||||
padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
padding: calc(15px + var(--zen-toolbox-padding))
|
||||
calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
max-height: var(--zen-workspace-indicator-height);
|
||||
@@ -134,8 +452,6 @@
|
||||
flex-direction: row !important;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: small;
|
||||
padding-right: 10px;
|
||||
|
||||
&::before {
|
||||
border-radius: var(--border-radius-medium);
|
||||
@@ -170,23 +486,8 @@
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.zen-workspaces-actions {
|
||||
--toolbarbutton-inner-padding: 4px;
|
||||
margin-left: auto !important;
|
||||
opacity: 0;
|
||||
visibility: collapse;
|
||||
transition: opacity 0.1s;
|
||||
order: 5;
|
||||
}
|
||||
|
||||
:root:not([zen-private-window]) &:hover .zen-workspaces-actions,
|
||||
& .zen-workspaces-actions[open='true'] {
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
pointer-events: none;
|
||||
font-size: small;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"binaryName": "zen",
|
||||
"version": {
|
||||
"product": "firefox",
|
||||
"version": "138.0.4",
|
||||
"candidate": "139.0"
|
||||
"version": "139.0.1",
|
||||
"candidate": "139.0.1"
|
||||
},
|
||||
"buildOptions": {
|
||||
"generateBranding": true
|
||||
@@ -19,7 +19,7 @@
|
||||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.12.8b",
|
||||
"displayVersion": "1.12.9b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user