Compare commits

..

59 Commits

Author SHA1 Message Date
mr. m
0869fa55bb no-bug - Allow accent colors on the titlebar to be shown on windows 11, c=common, t=feat 2025-06-01 15:47:54 +02:00
mr. m
80b8f7b4d8 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-06-01 09:01:49 +02:00
mr. m
7c18b1d921 no-bug - Add support for accent color on titlebar preference for windows 11, c=no-component, t=feat 2025-06-01 09:01:45 +02:00
mr-cheff
eec99dc76b docs: Update monthly issue metrics, b=(no bug), c={docs} 2025-06-01 02:52:56 +00:00
mr. m
6663a47d4a no-bug - Fixed restoring pinned tabs having the wrong favicon, c=tabs, t=fix 2025-06-01 00:37:01 +02:00
Mr. M
c654c3f5de Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-05-31 22:19:41 +02:00
Mr. M
5e48064497 no-bug - Bump version, c=no-component, t=chore 2025-05-31 22:19:14 +02:00
UnownPlain
9c8aba3dcd Update release notes script (#8739) 2025-05-31 22:16:55 +02:00
Mr. M
c9fe95c00c closes #8713 - Fixed profile context menu not showing, c=common, t=fix 2025-05-31 22:16:05 +02:00
Mr. M
9bfa7b01b5 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-05-31 21:46:02 +02:00
Mr. M
bbf646439a closes #8694 - Fixed gradient not showing when restoring multiple windows, c=workspaces, t=fix 2025-05-31 21:45:36 +02:00
Mr. M
a710d5949e fix: FIxed restoring pined and essential tabs not being on the correct parent element, b=(bug #8726), c=tests, workspaces 2025-05-31 21:32:51 +02:00
Mr. M
4abb3d2249 feat: Fixed opening search mode on glance, b=(closes #8680), c=glance 2025-05-31 20:52:37 +02:00
Lukas Runge
6cdc640977 fix(shortcuts): 🐛 remove special characters on macos (#8719) 2025-05-31 20:49:08 +02:00
Mr. M
aa662910b2 feat: Do not hide toasts on welcome page, b=(no-bug), c=common, welcome 2025-05-31 20:36:06 +02:00
mr. m
0e3faf45b1 feat: Use default firefox proxy value, b=(no-bug), c=no-component 2025-05-31 00:24:48 +02:00
mr. m
8b25b0bbf3 feat: Set a new favicon to pinned tabs if previous didn't exist, b=(no-bug), c=tabs 2025-05-30 19:37:35 +02:00
mr. m
dad9475005 feat: Disable context menu separator spacings, b=(no-bug), c=common 2025-05-30 19:20:16 +02:00
mr. m
00d3ba11d2 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-05-30 19:02:08 +02:00
mr. m
c7333caf7d feat: Make sure XUL store persists compact mode after change, b=(no-bug), c=compact-mode 2025-05-30 19:02:04 +02:00
Mr. M
0630b65713 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-05-30 10:10:49 +02:00
Mr. M
69f9b05cbc feat: Prevent background being removed on welcome screens, b=(no-bug), c=common, tabs, welcome 2025-05-30 10:10:45 +02:00
cix
74f6d1a85e chore(issue-template): improve bug report template (#8673)
* chore(issue-template): improve bug report template

* chore(issue-template): small fix to align text with logo

* chore(issue-template): fix img not showing
2025-05-30 09:22:36 +02:00
mr. m
e0bf7d011b feat: Apply tab icons when animating, b=(no-bug), c=workspaces 2025-05-29 21:05:53 +02:00
mr. m
12011c7208 chore: Added new readme, p=(#8671), c=no-component 2025-05-29 19:33:33 +02:00
Mr. M
280f0ca27f chore: Updated to firefox 139.0.1, b=(no-bug), c=no-component 2025-05-29 18:17:13 +02:00
mr. m
6ec0af3182 chore: Fixed separation between borders on toasts, b=(no-bug), c=common 2025-05-29 11:39:30 +02:00
mr. m
e99c40bc06 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-05-29 11:38:26 +02:00
mr. m
e181381bd3 chore: Changed menu separator widths, b=(no-bug), c=common 2025-05-29 11:38:16 +02:00
Mr. M
6fa0e814de chore: Add a minimum spacing for toasts, b=(no-bug), c=common 2025-05-29 09:28:57 +02:00
Mr. M
68191d2b47 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2025-05-29 08:41:00 +02:00
Mr. M
81f7587958 fix: Fixed workspace switching translations when multiple insatnce with the same container exist, b=(no-bug), c=workspaces 2025-05-29 08:40:54 +02:00
CosmoCreeper
d5e2acfd5b Add a unique id to the checkbox for auto-updating Zen Mods on startup. (#8661)
* Updated id to be unique.

Signed-off-by: CosmoCreeper <179134799+CosmoCreeper@users.noreply.github.com>

* Replaced unique id with one that doesn't contain enabled.

Signed-off-by: CosmoCreeper <179134799+CosmoCreeper@users.noreply.github.com>

---------

Signed-off-by: CosmoCreeper <179134799+CosmoCreeper@users.noreply.github.com>
2025-05-29 01:00:51 +02:00
mr. m
663243264b feat: Prevent zen's session restore from being removed, b=(no-bug), c=common 2025-05-28 18:19:49 +02:00
mr. m
03ca00748c fix: Fixed inserting changed titles into pinned database, b=(no-bug), c=tabs 2025-05-28 17:59:51 +02:00
mr. m
cbb1a4bc44 feat: Remove border from menu popups, b=(no-bug), c=common 2025-05-28 17:13:21 +02:00
mr. m
21f3ab23d3 feat: Inherti the split view type when opening a new link, b=(no-bug), c=split-view 2025-05-28 14:17:20 +02:00
mr. m
18944d5ed8 fix: Fixed pinned tab icons not appearing at startup, b=(no-bug), c=tabs 2025-05-28 10:17:33 +02:00
Mr. M
5c6e5f7361 fix: Fixed import mods button not working, b=(no-bug), c=mods, split-view 2025-05-27 21:03:40 +02:00
Mr. M
bd72aebd98 chore: Only export global variable for mods, not the class, b=(no-bug), c=mods 2025-05-27 20:17:06 +02:00
Mr. M
0278aea4f7 chore: Remove support links that target mozilla's support page on the about dialog, b=(bug #8634), c=no-component 2025-05-27 20:14:34 +02:00
Mr. M
3c01004641 fix: Fixed workspace transition animations and icons not appearing on new profiles, b=(no-bug), c=tabs, workspaces 2025-05-27 17:59:26 +02:00
Mr. M
b8213569e5 feat: Disable opening link on split view if the limit is reached, b=(no-bug), c=common, split-view 2025-05-27 17:14:10 +02:00
Mr. M
4d27f9d741 chore: Updated to firefox 139.0, b=(no-bug), c=no-component 2025-05-27 16:53:31 +02:00
Mr. M
3b56abf090 fix: Small fixes for split view and glance, b=(no-bug), c=common, compact-mode, split-view, tabs 2025-05-27 16:51:35 +02:00
Jai A P
590ba6de1b Finally fix positioning of popups (#8532)
Co-authored-by: mr. m <91018726+mauro-balades@users.noreply.github.com>
2025-05-27 15:29:41 +02:00
Mr. M
4aa215e091 fix: Fixed changing tab having the wrong focus on the website, b=(closes #8587), c=workspaces 2025-05-27 13:38:16 +02:00
Mr. M
de175bff11 fix: Fixer resizing the sidebar break the urlbar formatting, b=(no-bug), c=common, workspaces 2025-05-27 13:38:10 +02:00
Mr. M
a6bc8d7105 chore: Cleanup calls to updateSplitViewButton, b=(no-bug), c=split-view 2025-05-27 13:02:28 +02:00
Mr. M
e48e7caef1 test: Fixed test test_Glance_Basic_Close, b=(no-bug), c=split-view 2025-05-27 12:51:17 +02:00
Mr. M
015cdad2df feat: Made 'search test on <search engine>' open tabs in glance, b=(no-bug), c=common, glance, split-view, tests 2025-05-27 12:49:12 +02:00
Mr. M
ef6cf5fae1 chore: Remove icons from context menus, b=(no-bug), c=common 2025-05-27 12:11:58 +02:00
Bryan Galdámez
797152da89 refactor(mods): rework ZenMods module (#8618)
Co-authored-by: mr. m <mr.m@tuta.com>
Co-authored-by: mr. m <91018726+mauro-balades@users.noreply.github.com>
2025-05-27 12:02:59 +02:00
Mr. M
316ff45859 feat: Remove titlebar option on context menu, b=(no-bug), c=tabs 2025-05-27 10:43:55 +02:00
Mr. M
e0ac9ba424 chore: Added licenses to remanining files, b=(no-bug), c=common, compact-mode, folders, glance, media, mods, tabs, tests, workspaces 2025-05-25 17:15:19 +02:00
Mr. M
09ca430b88 feat: Improved tab transitions and fixed a couple of issues with glance, b=(no-bug), c=tabs, common, glance, split-view, workspaces 2025-05-25 17:06:22 +02:00
Mr. M
dda1dab6f3 fix: Fixed menu items being missaligned on windows, b=(closes #8590), c=no-component 2025-05-25 11:22:44 +02:00
mr. m
fbf411c096 Merge pull request #8578 from zen-browser/restore-fix 2025-05-24 17:21:40 +02:00
mr. m
4b0c6f2ca5 Merge pull request #8512 from zen-browser/firefox-139 2025-05-24 09:34:46 +02:00
100 changed files with 3779 additions and 2700 deletions

View File

@@ -1 +1 @@
{type}: {message}, b=({bugId}), c={components}
{bugId} - {message}, c={components}, t={type}

View File

@@ -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

View File

@@ -5,7 +5,7 @@ if [ "$RELEASE_BRANCH" = "release" ]; then
RELEASE_TYPE="Stable"
echo "Fetching release notes from GitHub..."
RELEASE_NOTES_JSON=$(curl -s "$RELEASE_NOTES_URL")
RELEASE_NOTES_JSON=$(curl -s --retry 5 --retry-delay 5 "$RELEASE_NOTES_URL")
if [ -z "$RELEASE_NOTES_JSON" ]; then
echo "Error: Failed to fetch release notes from GitHub"
@@ -18,96 +18,62 @@ else
RELEASE_TYPE="Twilight"
fi
cat << EOF > "release_notes.md"
# Zen ${RELEASE_TYPE} Release
EOF
{
echo "# Zen ${RELEASE_TYPE} Release"
if [ "$RELEASE_BRANCH" = "release" ]; then
echo "${EXTRA_NOTES}" >> "release_notes.md"
if echo "$LATEST_RELEASE" | jq -e '(.features // []) | length > 0' > /dev/null; then
cat << EOF >> "release_notes.md"
## New Features
$(echo "$LATEST_RELEASE" | jq -r '.features[] | "- " + .')
EOF
if [ "$RELEASE_TYPE" = "Twilight" ]; then
echo
echo "> [!NOTE]"
echo "> You're currently in Twilight mode, this means you're downloading the latest experimental features and updates."
echo ">"
echo "> If you encounter any issues, please report them on the [issues page](https://github.com/zen-browser/desktop/issues)."
fi
if echo "$LATEST_RELEASE" | jq -e '(.fixes // []) | length > 0' > /dev/null; then
cat << EOF >> "release_notes.md"
if [ "$RELEASE_TYPE" = "Stable" ]; then
echo "${EXTRA_NOTES}"
## Fixes
EOF
echo "$LATEST_RELEASE" | jq -r '.fixes[] | if type=="object" then "- " + .description + " ([#" + (.issue|tostring) + "](" + "https://github.com/zen-browser/desktop/issues/" + (.issue|tostring) + "))" else "- " + . end' >> "release_notes.md"
if echo "$LATEST_RELEASE" | jq -e '.security != null and .security != ""' > /dev/null; then
echo
echo "## Security"
echo "[Various security fixes]($(echo "$LATEST_RELEASE" | jq -r '.security'))"
fi
if echo "$LATEST_RELEASE" | jq -e '(.features // []) | length > 0' > /dev/null; then
echo
echo "## New Features"
echo "$LATEST_RELEASE" | jq -r '.features[] | "- " + .'
fi
if echo "$LATEST_RELEASE" | jq -e '(.fixes // []) | length > 0' > /dev/null; then
echo
echo "## Fixes"
echo "$LATEST_RELEASE" | jq -r '.fixes[] | if type=="object" then "- " + .description + " ([#" + (.issue|tostring) + "](" + "https://github.com/zen-browser/desktop/issues/" + (.issue|tostring) + "))" else "- " + . end'
fi
if echo "$LATEST_RELEASE" | jq -e '(.breakingChanges // []) | length > 0' > /dev/null; then
echo
echo "## Breaking Changes"
echo "$LATEST_RELEASE" | jq -r '.breakingChanges[] | if type=="string" then "- " + . else "- " + .description + " [Learn more](" + .link + ")" end'
fi
if echo "$LATEST_RELEASE" | jq -e '(.themeChanges // []) | length > 0' > /dev/null; then
echo
echo "## Theme Changes"
echo "$LATEST_RELEASE" | jq -r '.themeChanges[] | "- " + .'
fi
if echo "$LATEST_RELEASE" | jq -e '(.changes // []) | length > 0' > /dev/null; then
echo
echo "## Changes"
echo "$LATEST_RELEASE" | jq -r '.changes[] | "- " + .'
fi
if echo "$LATEST_RELEASE" | jq -e '(.knownIssues // []) | length > 0' > /dev/null; then
echo
echo "## Known Issues"
echo "$LATEST_RELEASE" | jq -r '.knownIssues[] | "- " + .'
fi
fi
if echo "$LATEST_RELEASE" | jq -e '(.breakingChanges // []) | length > 0' > /dev/null; then
cat << EOF >> "release_notes.md"
## Breaking Changes
EOF
echo "$LATEST_RELEASE" | jq -r '.breakingChanges[] | if type=="string" then "- " + . else "- " + .description + " [Learn more](" + .link + ")" end' >> "release_notes.md"
fi
if echo "$LATEST_RELEASE" | jq -e '(.themeChanges // []) | length > 0' > /dev/null; then
cat << EOF >> "release_notes.md"
## Theme Changes
$(echo "$LATEST_RELEASE" | jq -r '.themeChanges[] | "- " + .')
EOF
fi
fi
cat << EOF >> "release_notes.md"
<details>
<summary>File Checksums (SHA-256)</summary>
\`\`\`
EOF
generate_checksum() {
local pattern=$1
echo "Generating checksum for $pattern"
sha256sum $pattern 2> /dev/null | awk '{sub(".*/", "", $2); print $1 " " $2}' >> "release_notes.md"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
echo "Warning: No files found matching $pattern, skipping checksum."
fi
}
files=(
"./zen.source.tar.zst/*"
"./zen.linux-x86_64.tar.xz/*"
"./zen.linux-aarch64.tar.xz/*"
"./zen-x86_64.AppImage/*"
"./zen-x86_64.AppImage.zsync/*"
"./zen-aarch64.AppImage/*"
"./zen-aarch64.AppImage.zsync/*"
"./.github/workflows/object/windows-x64-signed-x86_64/zen.win-x86_64.zip"
"./zen.win-x86_64.zip/*"
"./.github/workflows/object/windows-x64-signed-arm64/zen.win-arm64.zip"
"./zen.win-arm64.zip/*"
"./linux.mar/*"
"./linux-aarch64.mar/*"
"./.github/workflows/object/windows-x64-signed-x86_64/windows.mar"
"./windows.mar/*"
"./.github/workflows/object/windows-x64-signed-arm64/windows-arm64.mar"
"./windows-arm64.mar/*"
"./macos.mar/*"
"./.github/workflows/object/windows-x64-signed-x86_64/zen.installer.exe"
"./zen.installer.exe/*"
"./.github/workflows/object/windows-x64-signed-arm64/zen.installer-arm64.exe"
"./zen.installer-arm64.exe/*"
"./zen.macos-universal.dmg/*"
)
for file in "${files[@]}"; do
generate_checksum "$file"
done
cat << EOF >> "release_notes.md"
\`\`\`
</details>
EOF
} > "release_notes.md"
echo "Release notes generated: release_notes.md"

117
README.md
View File

@@ -7,7 +7,7 @@
[![Crowdin](https://badges.crowdin.net/zen-browser/localized.svg)](https://crowdin.com/project/zen-browser)
[![Zen Release builds](https://github.com/zen-browser/desktop/actions/workflows/build.yml/badge.svg?branch=stable)](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!
[![Star History Chart](https://api.star-history.com/svg?repos=zen-browser/desktop,chromium/chromium,brave/brave-browser&type=Date)](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.

View File

@@ -1 +1 @@
82a08ea3ce2d17f21f3d45f4b5607a37590b0158
6548d6b52a8969b347101142ed630393332ab274

View 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

View File

@@ -0,0 +1,545 @@
# Issue Metrics
| Metric | Average | Median | 90th percentile |
| --- | --- | --- | ---: |
| Time to first response | 1 day, 12:44:32 | 4:32:48 | 2 days, 6:10:24 |
| Time to close | 1 day, 6:45:20 | 4:43:46 | 2 days, 22:21:34 |
| Metric | Count |
| --- | ---: |
| Number of items that remain open | 248 |
| Number of items closed | 279 |
| Total number of items created | 527 |
| Title | URL | Time to first response | Time to close |
| --- | --- | --- | --- |
| Engine | https://github.com/zen-browser/desktop/issues/8743 | 0:20:22 | None |
| Zen suddenly eats all available memory [Windows 11] | https://github.com/zen-browser/desktop/issues/8742 | None | None |
| Some site-specfic buttons on address bar are missing | https://github.com/zen-browser/desktop/issues/8741 | None | 0:12:59 |
| Small "currently playing" at the bottom of the tabbar doesn't recognize livestreams. | https://github.com/zen-browser/desktop/issues/8740 | 1:38:39 | None |
| URL Bar flickers when toggling compact mode | https://github.com/zen-browser/desktop/issues/8738 | None | None |
| Missing bookmarks/pinned tabs icons | https://github.com/zen-browser/desktop/issues/8736 | 0:47:55 | None |
| Unable to access iCloud Passwords | https://github.com/zen-browser/desktop/issues/8735 | None | 1:13:00 |
| Some Extensions Like Firefox Multi Account Container No Longer Shows Thier Icons In The URL Bar | https://github.com/zen-browser/desktop/issues/8732 | 3:38:32 | 3:38:32 |
| Essential Tab Icon Vibrance | https://github.com/zen-browser/desktop/issues/8731 | 0:39:22 | 2:02:27 |
| Pinned Extensions not visible after browser restart in Compact Mode | https://github.com/zen-browser/desktop/issues/8730 | 5:05:37 | None |
| Essential tabs get out of order when creating a new window | https://github.com/zen-browser/desktop/issues/8729 | None | None |
| Zen browser doesn't allow entering TOTP for Proxmox access | https://github.com/zen-browser/desktop/issues/8728 | None | None |
| Vertical scrolling in the sidebar slows down when using the trackpad swipe gesture to switch workspaces in 1.12.9b. | https://github.com/zen-browser/desktop/issues/8727 | None | None |
| Restoring pinned tabs relegates them to normal tabs area | https://github.com/zen-browser/desktop/issues/8726 | 3:03:01 | None |
| Using SHIFT + some key with a modifier doesn't respond as a keyboard shortcut. | https://github.com/zen-browser/desktop/issues/8725 | 5:26:20 | None |
| Cant build Zen error 2 | https://github.com/zen-browser/desktop/issues/8723 | 2:31:17 | None |
| Pinned extensions sometimes aren't visible in toolbar on startup in single toolbar mode | https://github.com/zen-browser/desktop/issues/8722 | 2:52:31 | None |
| Gradient on top of window not displaying correctly | https://github.com/zen-browser/desktop/issues/8721 | 6:16:26 | None |
| Missing the Profiles menu item after update to 1.12.9b version | https://github.com/zen-browser/desktop/issues/8720 | None | 0:06:49 |
| Search with Google not working inside Glances window. | https://github.com/zen-browser/desktop/issues/8717 | 12:16:58 | None |
| Right-Click Menu Icon Not Displaying | https://github.com/zen-browser/desktop/issues/8716 | 0:05:47 | 0:58:20 |
| Can't share screen on Google Meet (Mac) | https://github.com/zen-browser/desktop/issues/8715 | None | None |
| Profile switcher missing in context menu | https://github.com/zen-browser/desktop/issues/8713 | 0:40:14 | 8:43:33 |
| New Tab Hover Menu transparent instead of translucent in maximized window | https://github.com/zen-browser/desktop/issues/8711 | 8:27:38 | 8:27:38 |
| Unloaded Tabs dim only the Favicon but not the Text | https://github.com/zen-browser/desktop/issues/8710 | 6:56:56 | None |
| Notification push don't work in linux if the page isn't loaded | https://github.com/zen-browser/desktop/issues/8709 | None | None |
| Zen Browser's Position Drags Slightly When Clicking its Sidebar on MacOS | https://github.com/zen-browser/desktop/issues/8706 | 13:34:24 | None |
| Extensions not opening Before focus to a tab | https://github.com/zen-browser/desktop/issues/8705 | None | None |
| Cmd + K (Focus Search) still active after closing it | https://github.com/zen-browser/desktop/issues/8704 | None | None |
| 2 video in PIP dont work if you have 2 screen | https://github.com/zen-browser/desktop/issues/8703 | None | None |
| SteamVR's Desktop Overlay Cannot Reveal Compact Mode Tabs | https://github.com/zen-browser/desktop/issues/8702 | None | None |
| Restore Last Closed Tab(s) right after startup breaks the UI | https://github.com/zen-browser/desktop/issues/8699 | 9:14:56 | None |
| Zen Browser doesnt store my tabs and half the features are missing in 1.12.8b | https://github.com/zen-browser/desktop/issues/8697 | 1:41:36 | None |
| Restoring Session With Even Number of Windows Results in At Least One Window Not Adhering to Theme Coloring | https://github.com/zen-browser/desktop/issues/8694 | 0:28:05 | 1 day, 3:00:32 |
| When in compact mode in Windows and when no tabs are open, the navigation tools of windows to close, minimize and maximize get overlapped and its difficult to use them. | https://github.com/zen-browser/desktop/issues/8692 | None | None |
| Pinned tab title not refreshing | https://github.com/zen-browser/desktop/issues/8691 | 6:20:06 | None |
| Zen browser doesn't check for duplicate bookmarks when importing them from other accounts | https://github.com/zen-browser/desktop/issues/8690 | 15:49:28 | None |
| Cannot add tabs to essentials anymore | https://github.com/zen-browser/desktop/issues/8689 | 2:19:39 | None |
| Address/search bar doesn't show search engines, when address is typed, no search is allowed | https://github.com/zen-browser/desktop/issues/8687 | 6:14:49 | None |
| Windows Single Sign-On does not seem to work from within Containers | https://github.com/zen-browser/desktop/issues/8686 | 4:55:19 | None |
| Spelling selection - added language does'nt show up | https://github.com/zen-browser/desktop/issues/8685 | 10:38:15 | None |
| Unable to open Glance windows, shortcut just opens new window regardless of selected activation key. | https://github.com/zen-browser/desktop/issues/8680 | 10:03:54 | 1 day, 16:09:30 |
| Pinned tabs don't show an updated version of a website after opening them again | https://github.com/zen-browser/desktop/issues/8679 | None | None |
| Dragging a local PDF into Zen Browser fails with “File not found” error | https://github.com/zen-browser/desktop/issues/8670 | 1 day, 3:11:59 | None |
| In the compact mode, there are some issues with the automatic hiding of the toolbar. | https://github.com/zen-browser/desktop/issues/8669 | None | None |
| Error when opening the app | https://github.com/zen-browser/desktop/issues/8668 | None | None |
| Blur not working anymore in maximized windows | https://github.com/zen-browser/desktop/issues/8667 | 0:36:09 | 0:36:09 |
| Back and forth arrows confusing | https://github.com/zen-browser/desktop/issues/8666 | 1:03:53 | 1:03:53 |
| pop up "Paste" on site | https://github.com/zen-browser/desktop/issues/8664 | 12:27:36 | 12:27:36 |
| Zen Browser Forgetting Tab Settings | https://github.com/zen-browser/desktop/issues/8662 | 3:12:47 | None |
| Windows taskber preview showing inactive tab | https://github.com/zen-browser/desktop/issues/8657 | None | None |
| [BUG] Middle Click not scrolling faster | https://github.com/zen-browser/desktop/issues/8656 | None | 16:39:10 |
| Toolbar hides when hovering off a bookmark but still within the toolbar area in Compact Mode | https://github.com/zen-browser/desktop/issues/8655 | 15:06:50 | 15:06:50 |
| Different zoom levels, while working with the pdf (drop down) is not visible unless howering over it. | https://github.com/zen-browser/desktop/issues/8653 | 17:36:47 | None |
| Sidebar unexpectedly toggles in compact mode when using Raycast focus timer | https://github.com/zen-browser/desktop/issues/8652 | 17:58:22 | 19:09:46 |
| Horizontal Scrolling with mouse wheel is not working on Mac | https://github.com/zen-browser/desktop/issues/8649 | 20:58:04 | None |
| Zen context menus have inconsistent indentation and toast notification color is too dark in private mode. | https://github.com/zen-browser/desktop/issues/8648 | 21:56:58 | None |
| Sidebar stuck open, does not collapse when compact mode turned on. | https://github.com/zen-browser/desktop/issues/8643 | 1 day, 10:21:36 | None |
| olk.exe is blocked by MS Defender ASR Security role when opening links from Outlook / MS Teams | https://github.com/zen-browser/desktop/issues/8642 | 3:20:57 | 2 days, 3:44:52 |
| Cmd+Shift+C doesn't follow browser.urlbar.decodeURLsOnCopy | https://github.com/zen-browser/desktop/issues/8641 | 5:19:57 | 5:19:57 |
| Hi, there is an issue with the sound icon in the shortcuts where it looks like it's beating. | https://github.com/zen-browser/desktop/issues/8640 | 0:31:19 | 6:28:53 |
| YouTube locks up during video and does not return and does not refresh | https://github.com/zen-browser/desktop/issues/8638 | None | None |
| Google Sheets error report when switching from split screen | https://github.com/zen-browser/desktop/issues/8635 | None | None |
| Remove link to Firefox Support | https://github.com/zen-browser/desktop/issues/8634 | 0:25:06 | None |
| Can't Open Hisotry Tab In Compact Mode with No Open Tabs | https://github.com/zen-browser/desktop/issues/8632 | 2:46:16 | None |
| division by zero, showing incorrectly in the search bar | https://github.com/zen-browser/desktop/issues/8631 | 23:16:38 | 1 day, 0:05:11 |
| No option to unpin tab from url bar | https://github.com/zen-browser/desktop/issues/8629 | 1:19:24 | 4:01:02 |
| Essential and Pinned Tab Icons Not Displaying Until Clicked | https://github.com/zen-browser/desktop/issues/8628 | 4:06:21 | 4 days, 1:51:29 |
| Colors in sidebar seem broken | https://github.com/zen-browser/desktop/issues/8627 | 0:51:23 | 0:51:23 |
| [BUG] Compact mode disales also Mini window brokes | https://github.com/zen-browser/desktop/issues/8626 | 0:32:32 | None |
| spotify keeps crashing | https://github.com/zen-browser/desktop/issues/8625 | 0:09:38 | 0:09:38 |
| [regression] tab bar doesn't follow the currently selected tab. | https://github.com/zen-browser/desktop/issues/8623 | 1:28:05 | None |
| can't download on glance page | https://github.com/zen-browser/desktop/issues/8620 | 8:43:05 | None |
| Keybinds not working after tab change | https://github.com/zen-browser/desktop/issues/8617 | 13:42:53 | None |
| Address bar not visible on 1280x720 screens | https://github.com/zen-browser/desktop/issues/8616 | None | None |
| Close button not centered when tabs are on the right | https://github.com/zen-browser/desktop/issues/8615 | None | None |
| OpenSearch is not working in Zen Browser | https://github.com/zen-browser/desktop/issues/8614 | None | 0:02:48 |
| Switching to an unloaded tab opens a duplicate, instead of actually switching to it. | https://github.com/zen-browser/desktop/issues/8613 | None | 0:51:33 |
| Split view tabs' custom red highlight color reset after opening new window | https://github.com/zen-browser/desktop/issues/8610 | 10:05:25 | None |
| Audio preview from osu! website not functional | https://github.com/zen-browser/desktop/issues/8608 | 7:14:22 | 7:35:21 |
| Zen disables compact mode on startup | https://github.com/zen-browser/desktop/issues/8606 | 8:06:57 | None |
| Overflow menu button only appears for automatically added buttons | https://github.com/zen-browser/desktop/issues/8605 | None | None |
| Youtube Fullscreen grey rounded shape in each corner | https://github.com/zen-browser/desktop/issues/8604 | None | None |
| Cannot higlight a part of URL in adress (or URL) bar | https://github.com/zen-browser/desktop/issues/8603 | 1 day, 15:33:14 | None |
| Toggling Sidebar's Width on a Floating Sidebar shows weird 2 frame animation | https://github.com/zen-browser/desktop/issues/8602 | None | None |
| Omnibox closes on keyboard layout switch (Wayland / KDE) | https://github.com/zen-browser/desktop/issues/8600 | None | 0:27:49 |
| Interaction between Essentials and containers makes tabs disappear | https://github.com/zen-browser/desktop/issues/8599 | 1:14:41 | None |
| Unclosable Search Popup in Zen Browser After Update | https://github.com/zen-browser/desktop/issues/8598 | None | 4:16:15 |
| MacOS Smooth Scrolling Stutter on HiDPI Resolutions | https://github.com/zen-browser/desktop/issues/8597 | None | 0:01:02 |
| When toggling compact mode with tabs on the right side on Windows, the buttons briefly pop up in the wrong place | https://github.com/zen-browser/desktop/issues/8595 | None | None |
| Unable to Remove Workspace Container after Update | https://github.com/zen-browser/desktop/issues/8594 | None | 1:32:31 |
| Displacement of icons on collapsed bar | https://github.com/zen-browser/desktop/issues/8593 | 1:43:44 | None |
| Inconsistent behavior between Windows (x64) and Linux (tar) regarding keyboard shortcuts | https://github.com/zen-browser/desktop/issues/8592 | 6:33:41 | None |
| Overflow menu missing in single toolbar mode | https://github.com/zen-browser/desktop/issues/8591 | 4:48:43 | 4:48:43 |
| Icons in right click menu out of allignment | https://github.com/zen-browser/desktop/issues/8590 | None | 13:32:10 |
| Sites without a background color use the theme gradient as the background | https://github.com/zen-browser/desktop/issues/8589 | 6:05:21 | 6:05:21 |
| Multiple home pages do not load on starting the browser with zen.urlbar.replace-newtab set to true | https://github.com/zen-browser/desktop/issues/8588 | None | None |
| Switching to WhatsApp Web tab does not focus the message input field | https://github.com/zen-browser/desktop/issues/8587 | 10:55:01 | 2 days, 13:08:21 |
| When opening a new window, pinned split screen tabs are split apart / not preserved | https://github.com/zen-browser/desktop/issues/8583 | None | None |
| widget.gtk.native-context-menus not being functional (most options) | https://github.com/zen-browser/desktop/issues/8582 | None | None |
| Icon Rendering Incorrectly in KDE Plasma 6 Wayland Window Switcher | https://github.com/zen-browser/desktop/issues/8580 | None | None |
| spotfy just refuses to work properly | https://github.com/zen-browser/desktop/issues/8577 | 1:39:16 | 1:39:16 |
| Compact view not working at all | https://github.com/zen-browser/desktop/issues/8575 | 4:33:12 | 1 day, 0:35:59 |
| Gap at the top of every windows | https://github.com/zen-browser/desktop/issues/8574 | 2:50:22 | None |
| Private Mode Forced Pinkish-Purple Theme | https://github.com/zen-browser/desktop/issues/8570 | 0:27:34 | 0:27:55 |
| Logging into google causing browser to crash | https://github.com/zen-browser/desktop/issues/8569 | None | None |
| Streaming websites do not work on Windows beta | https://github.com/zen-browser/desktop/issues/8568 | 0:48:09 | 0:48:09 |
| get unchange blue theme after last autoupdate to 1.12.8b | https://github.com/zen-browser/desktop/issues/8567 | 1:26:08 | 1:31:30 |
| Customize Toobar: No longer it's possible to drag entries to the url bar. | https://github.com/zen-browser/desktop/issues/8566 | 0:02:19 | 0:02:19 |
| Browser window sometimes disappears for less than a second, revealing applications running in the background. | https://github.com/zen-browser/desktop/issues/8565 | None | None |
| MMB Paste when setting is OFF | https://github.com/zen-browser/desktop/issues/8564 | None | None |
| The sidebar does not change colors in auto-hide mod in compact mode | https://github.com/zen-browser/desktop/issues/8563 | None | None |
| Split View: "Save PDF before leaving"-dialogue centered on browser window; covered by other split(s) | https://github.com/zen-browser/desktop/issues/8561 | None | None |
| No video with supported format and MIME type found | https://github.com/zen-browser/desktop/issues/8560 | 1:03:42 | 1 day, 0:42:39 |
| Floating URL bar appears way off the expected position | https://github.com/zen-browser/desktop/issues/8559 | 11:32:28 | None |
| gradient contrast issue when switching theme | https://github.com/zen-browser/desktop/issues/8558 | None | None |
| Tab corners aren't rounded on some pages | https://github.com/zen-browser/desktop/issues/8557 | None | None |
| Background defaults to Zen theme color, not white | https://github.com/zen-browser/desktop/issues/8554 | 17:48:16 | 1 day, 0:40:19 |
| Zen Crashes when handling anything related to audio sharing(discord or call) or video sharing | https://github.com/zen-browser/desktop/issues/8553 | 2 days, 0:42:30 | 4 days, 16:10:53 |
| Sidebar titles are not visible | https://github.com/zen-browser/desktop/issues/8552 | 14:31:33 | None |
| Gradient persists with zen.theme.gradient=false | https://github.com/zen-browser/desktop/issues/8551 | 8:00:02 | 16:48:52 |
| bookmark issue | https://github.com/zen-browser/desktop/issues/8550 | None | None |
| Exit button not in corner | https://github.com/zen-browser/desktop/issues/8549 | 0:21:02 | None |
| Workspace icons break when resizing window. The icon state persists after the window is maximized | https://github.com/zen-browser/desktop/issues/8548 | None | None |
| URL bar flickers when cursor is at specific position | https://github.com/zen-browser/desktop/issues/8547 | None | None |
| New tab button disappeared and lost fixed position after reenabling | https://github.com/zen-browser/desktop/issues/8546 | None | None |
| Single bar Mode Extension issue | https://github.com/zen-browser/desktop/issues/8545 | 18:47:17 | None |
| Deadzone on top right in macOS | https://github.com/zen-browser/desktop/issues/8540 | None | 0:12:33 |
| Extensions stopped working | https://github.com/zen-browser/desktop/issues/8538 | None | None |
| Add-on Element Overlap | https://github.com/zen-browser/desktop/issues/8537 | None | None |
| YouTube UI Rounded corners and theme colour bug | https://github.com/zen-browser/desktop/issues/8536 | 3:15:12 | 3:15:12 |
| Loading large JSON files in the built-in viewer, then using the "Save" button results in the downloaded file missing some JSON entries. | https://github.com/zen-browser/desktop/issues/8535 | None | None |
| Win10 1809 versionBaidu Apollo Config Center WEB-UIText Area Input Mouse focus on position error | https://github.com/zen-browser/desktop/issues/8533 | None | None |
| Webpages are flickering/flashing constantly | https://github.com/zen-browser/desktop/issues/8531 | None | None |
| Tabs reopen in new windows | https://github.com/zen-browser/desktop/issues/8529 | None | None |
| Workspace Default Container Randomised on Update | https://github.com/zen-browser/desktop/issues/8527 | 8:07:54 | None |
| Workspace right click not working | https://github.com/zen-browser/desktop/issues/8526 | 8:21:24 | None |
| When re-opening a recently closed window, window is opened with and focused on a New Tab page | https://github.com/zen-browser/desktop/issues/8525 | 16:10:13 | None |
| Battleye flag | https://github.com/zen-browser/desktop/issues/8524 | 0:30:25 | 0:37:32 |
| Close tab action removes Essential | https://github.com/zen-browser/desktop/issues/8523 | 0:48:59 | 1:37:49 |
| Update v1.12.7b broke compact tabs | https://github.com/zen-browser/desktop/issues/8522 | 1:17:05 | 1:17:05 |
| Patch apply failed for update v1.12.7b | https://github.com/zen-browser/desktop/issues/8521 | None | 1 day, 5:59:24 |
| More light middle bar of sidebar for better recognition of icons on tabs | https://github.com/zen-browser/desktop/issues/8520 | 2:58:50 | 2:58:50 |
| More pleasant corner in compact mode and collapsed option with hiding of top bar | https://github.com/zen-browser/desktop/issues/8519 | 3:43:41 | 6:46:24 |
| sometime alt tab does.t work | https://github.com/zen-browser/desktop/issues/8517 | None | None |
| dowload some pdf | https://github.com/zen-browser/desktop/issues/8516 | None | None |
| YouTube in full screen mode is laggy/jerky. In Firefox, same video is fine | https://github.com/zen-browser/desktop/issues/8515 | None | None |
| iCloud Passwords extension reports "macOS Sonoma or later to be installed" but I am running on Sequoia | https://github.com/zen-browser/desktop/issues/8514 | 0:10:31 | 0:10:31 |
| `New background tab opened` notification not issued in some cases | https://github.com/zen-browser/desktop/issues/8511 | None | None |
| Unable to see notifications from zen when in single toolbar browser layout, unless hovering over top bar. | https://github.com/zen-browser/desktop/issues/8510 | None | 1:08:12 |
| Hiding top bar and tab bar doesnt work in latest version | https://github.com/zen-browser/desktop/issues/8509 | None | 0:14:35 |
| Container-specific essentials don't work, even if enable and reboot - Windows ARM - Last update | https://github.com/zen-browser/desktop/issues/8508 | 0:24:00 | None |
| Clicking current workspace icon flashes previous workspace's theme colours | https://github.com/zen-browser/desktop/issues/8507 | 2:33:28 | 12:47:45 |
| Conflict with Zen Privacy app | https://github.com/zen-browser/desktop/issues/8506 | 2:40:37 | 2:40:37 |
| Can not play videos in Udemy and music in Spotify. | https://github.com/zen-browser/desktop/issues/8505 | 1:27:54 | 1:27:54 |
| When opening a new window, the order of essential tabs gets out of order (but only in the second window) | https://github.com/zen-browser/desktop/issues/8503 | 5:52:57 | 5:52:57 |
| Tab list doesn't retain scroll position after opening the browser | https://github.com/zen-browser/desktop/issues/8502 | 20:46:10 | None |
| YouTube player sometimes incorrectly opens in fullscreen mode | https://github.com/zen-browser/desktop/issues/8501 | 11:18:42 | None |
| Toolbar items can only be inserted into sidebar as nested in Top Buttons and Bottom Buttons blocks. | https://github.com/zen-browser/desktop/issues/8500 | 8:27:32 | 8:27:32 |
| Stupid updates enabled by default and hard to disable | https://github.com/zen-browser/desktop/issues/8499 | 9:19:18 | 9:19:06 |
| Gradient in sidebar is broken | https://github.com/zen-browser/desktop/issues/8498 | 9:25:28 | 9:25:28 |
| FirebaseError: Messaging: This browser doesn't support the API's required to use the Firebase SDK. | https://github.com/zen-browser/desktop/issues/8497 | None | None |
| Weird bar on top of desktop window on macos after 1.12.7b | https://github.com/zen-browser/desktop/issues/8496 | 0:22:14 | 1:19:03 |
| Mod Refuses to Uninstall | https://github.com/zen-browser/desktop/issues/8495 | 0:20:53 | 0:20:52 |
| Livestream video freezes when focusing other fullscreen window, but audio still works | https://github.com/zen-browser/desktop/issues/8491 | 16:45:00 | None |
| Toggle Sidebar button not visible/working | https://github.com/zen-browser/desktop/issues/8489 | 6:25:39 | None |
| all Zen UI elements outside the viewport are partially transparent (not blurred) | https://github.com/zen-browser/desktop/issues/8488 | None | 1 day, 8:57:38 |
| Top Bar Delay and Partial Display on Hover After Recent Update | https://github.com/zen-browser/desktop/issues/8487 | 1:35:56 | 13:37:40 |
| Firefox View icon disapears after restart | https://github.com/zen-browser/desktop/issues/8486 | 1 day, 17:22:21 | None |
| Sidebar double click not opens new tab anymore | https://github.com/zen-browser/desktop/issues/8485 | 0:09:42 | 10:01:18 |
| When Windows wakes my display from being powered off by the OS, Zen toolbar and sidebar become unresponsive and do not show in compact mode | https://github.com/zen-browser/desktop/issues/8484 | None | None |
| Compact mode toolbar dragging issue | https://github.com/zen-browser/desktop/issues/8483 | None | 0:03:29 |
| Shift + Alt - unexpectedly closing opened new tab spotlight input | https://github.com/zen-browser/desktop/issues/8481 | 1 day, 12:59:02 | None |
| Unable to hide Workspace Indicator | https://github.com/zen-browser/desktop/issues/8480 | 1 day, 10:43:05 | 0:05:56 |
| Url bar doesn't popup correctly on compact mode | https://github.com/zen-browser/desktop/issues/8478 | None | 0:06:58 |
| [1.12.6b] Initializing a workspace causes a empty workspace until you close out of the browser and reopen it. | https://github.com/zen-browser/desktop/issues/8476 | None | 1:04:58 |
| Renaming pinned tab input focus issue (when using Left/Right buttons) | https://github.com/zen-browser/desktop/issues/8475 | None | 2:46:26 |
| URL BAR APPEARS WHEN EXITING FULLSCREEN | https://github.com/zen-browser/desktop/issues/8474 | None | 0:29:12 |
| Mouse loses functionality in the sidebar and sidebar gets stuck on open im compact mode | https://github.com/zen-browser/desktop/issues/8471 | 11:10:16 | None |
| Tabs created by text being dragged into sidebar show container indicator when a workspace is assigned to a container. | https://github.com/zen-browser/desktop/issues/8470 | None | None |
| 1Password Extension - Desktop App Integration Setting - Connection Problem Error | https://github.com/zen-browser/desktop/issues/8469 | 2:41:44 | None |
| Dragging a tab onto a Glance tab appends its favicon and renders the tab inaccessible | https://github.com/zen-browser/desktop/issues/8465 | 0:31:46 | 22:19:59 |
| Screen Sharing Malfunction in Google Meet (Windows) | https://github.com/zen-browser/desktop/issues/8464 | 0:58:42 | None |
| Glance causes stutter | https://github.com/zen-browser/desktop/issues/8463 | None | None |
| visibility of the search bar with a dark background | https://github.com/zen-browser/desktop/issues/8462 | 2:40:56 | 2:40:56 |
| container, workspace and tab synchronization | https://github.com/zen-browser/desktop/issues/8460 | None | None |
| It's Dark mode when the Tips Box comes up | https://github.com/zen-browser/desktop/issues/8459 | 1:54:22 | 16:10:29 |
| Toolbar doesn't appear on hover after update when using compact mode with hidden bars | https://github.com/zen-browser/desktop/issues/8458 | 0:24:26 | 14:16:08 |
| Gradient sometimes lost on startup | https://github.com/zen-browser/desktop/issues/8457 | None | None |
| Workspace name visible again after udpate | https://github.com/zen-browser/desktop/issues/8455 | 0:45:18 | 2:35:37 |
| Workspaces not visible when compact mode | https://github.com/zen-browser/desktop/issues/8454 | 3:14:43 | None |
| Favicon Issue on grok.com Bookmarks | https://github.com/zen-browser/desktop/issues/8453 | None | None |
| Ctrl + Shift + C (pick an element from the page) not working in page devtools | https://github.com/zen-browser/desktop/issues/8452 | 3:38:50 | 1 day, 0:04:18 |
| Signing in with Google is Not working. | https://github.com/zen-browser/desktop/issues/8451 | 3 days, 18:52:30 | None |
| Browser tab switcher only displays 7 tabs even when more are open | https://github.com/zen-browser/desktop/issues/8450 | None | None |
| Compact Sidebar is not aligned properly | https://github.com/zen-browser/desktop/issues/8449 | None | 7:19:21 |
| TLS error connecting to accounts.microsoft.com | https://github.com/zen-browser/desktop/issues/8448 | None | None |
| Weird colored text highlighting in google search titles | https://github.com/zen-browser/desktop/issues/8447 | None | 5:45:04 |
| White block at the bottom of every page | https://github.com/zen-browser/desktop/issues/8446 | None | None |
| External links launch process hangs for 2 minutes | https://github.com/zen-browser/desktop/issues/8445 | 8:39:25 | None |
| Collapse and multi toolbar layouts are shortened vertically | https://github.com/zen-browser/desktop/issues/8444 | None | 4:29:53 |
| Audible online player not working | https://github.com/zen-browser/desktop/issues/8443 | 2:35:00 | 2:35:00 |
| YouTube views counter display issue. | https://github.com/zen-browser/desktop/issues/8439 | None | None |
| The workspaces icons at the bottom twinkle when i resize | https://github.com/zen-browser/desktop/issues/8437 | None | None |
| bug extension name/size icon when switch between single toolbar and multiple toolbar | https://github.com/zen-browser/desktop/issues/8436 | None | None |
| 1.12.5b can't auto-update on macOS, manual upgrage results in new profile | https://github.com/zen-browser/desktop/issues/8433 | 4:31:18 | 1 day, 20:07:07 |
| Gamepad breaks the browser on mac | https://github.com/zen-browser/desktop/issues/8432 | None | 4 days, 20:24:24 |
| Suerpins - Broke "Makes pinned tabs look similar to Essentials (icon only in a grid)" | https://github.com/zen-browser/desktop/issues/8431 | 0:56:23 | 4:32:12 |
| Browser hangs after closing a tab that was using picture-in-picture. | https://github.com/zen-browser/desktop/issues/8430 | None | None |
| Zen Browser is laggy and consumes excessive energy | https://github.com/zen-browser/desktop/issues/8428 | 1 day, 13:37:20 | None |
| Pinned tabs appear in incorrect workspace/container after using Ctrl + N or open new window in taskbar | https://github.com/zen-browser/desktop/issues/8427 | None | None |
| Glance and Tab Switching not working properly | https://github.com/zen-browser/desktop/issues/8426 | 0:08:15 | None |
| Copying Profile from Flatpak to AppImage gives "Older Version" error, even though AppImage is newer. | https://github.com/zen-browser/desktop/issues/8424 | None | None |
| Sidebar custom configuration resets between sessions in the app, and instead should persist. | https://github.com/zen-browser/desktop/issues/8422 | None | None |
| Blurred fonts on ultrawide monitor full screen | https://github.com/zen-browser/desktop/issues/8421 | 3:59:32 | None |
| Allow Drag-and-Drop to Open Tabs in a New Window | https://github.com/zen-browser/desktop/issues/8418 | 0:42:07 | None |
| Floating url bar unexpected behaviour (loosing focus and not showing instantly with ctrl+T) | https://github.com/zen-browser/desktop/issues/8417 | None | None |
| Reset URL and switch to next tab undesired behaviour | https://github.com/zen-browser/desktop/issues/8416 | 5:53:34 | 1 day, 12:25:32 |
| One tab required to remain loaded in every workspace when trying to unload all tabs | https://github.com/zen-browser/desktop/issues/8414 | None | None |
| edit : sorry for cloning the issue, the same issue was posted right below this issue by gordonn. | https://github.com/zen-browser/desktop/issues/8413 | None | 0:06:07 |
| [Twilight 1.13] Multi toolbar and collapse toolbar modes are misalligned | https://github.com/zen-browser/desktop/issues/8412 | 0:37:08 | 4:32:12 |
| Clicking already selected workspace causes incorrect scrollbar size | https://github.com/zen-browser/desktop/issues/8411 | None | None |
| Switch to tab indicator thinner in new twilight (not sure if its intentional or not) | https://github.com/zen-browser/desktop/issues/8410 | 0:53:15 | 0:53:15 |
| Side Toolbox Flash | https://github.com/zen-browser/desktop/issues/8409 | 3:16:36 | 10 days, 4:38:53 |
| Compact-mode toolbox background visibly cuts off on wide/hires screens | https://github.com/zen-browser/desktop/issues/8408 | None | None |
| ZenBrowser cannot disable the Save Page shortcut key. | https://github.com/zen-browser/desktop/issues/8406 | None | None |
| Bookmark management does not display which workspace it belongs to. | https://github.com/zen-browser/desktop/issues/8405 | None | None |
| Zen URL bar insists on showing history even when the option to do so is disabled. | https://github.com/zen-browser/desktop/issues/8404 | 9:21:34 | None |
| Ctrl-tab should not cycle through essential tabs | https://github.com/zen-browser/desktop/issues/8403 | 0:39:33 | None |
| Extensions being removed? | https://github.com/zen-browser/desktop/issues/8402 | None | None |
| Weird bookmark bar showing up | https://github.com/zen-browser/desktop/issues/8401 | None | None |
| HDR on Linux doesn't work when zen is windowed | https://github.com/zen-browser/desktop/issues/8400 | 22:32:03 | None |
| Compact mode doesnt work | https://github.com/zen-browser/desktop/issues/8397 | 9 days, 16:40:40 | None |
| Most of rendering is blurry if window exceeds 4k resolution | https://github.com/zen-browser/desktop/issues/8396 | 8 days, 15:12:32 | None |
| Changing keyboard shortcuts, "Conflict with another shortcut" | https://github.com/zen-browser/desktop/issues/8395 | 1:12:32 | None |
| Installed mods not showing up in list | https://github.com/zen-browser/desktop/issues/8394 | 6:03:57 | None |
| Inconsistent behaviour of expanding sidebar between different OS's and mouse movements | https://github.com/zen-browser/desktop/issues/8393 | None | None |
| Holding ⌥ on Mac to select the text inside a link is broken | https://github.com/zen-browser/desktop/issues/8391 | None | None |
| Workspace indicator unhideable on Linux | https://github.com/zen-browser/desktop/issues/8390 | 2:00:45 | 2:27:20 |
| Tabliss does not load in new window or tab. | https://github.com/zen-browser/desktop/issues/8387 | 9:41:37 | None |
| I could'nt use "Better ctrl+Tab panel" mod in desktop. | https://github.com/zen-browser/desktop/issues/8386 | 5:23:01 | None |
| Customizing toolbar freeze | https://github.com/zen-browser/desktop/issues/8383 | None | None |
| error code SHAK-6007 Crunchyroll playback error | https://github.com/zen-browser/desktop/issues/8382 | 6:10:43 | 6:10:43 |
| URL character get double | https://github.com/zen-browser/desktop/issues/8379 | None | None |
| Zen browser multiple windows in taskbar | https://github.com/zen-browser/desktop/issues/8376 | 0:03:50 | 0:09:58 |
| Essentials are unloaded if Clear search, site and downloaded history at zen close is checked | https://github.com/zen-browser/desktop/issues/8374 | 0:44:41 | 0:44:41 |
| Tuxedo OS (Ubuntu 24.04.1) - several errors that might lead into a "Crash Annotation GraphicsCriticalError" after starting Zen | https://github.com/zen-browser/desktop/issues/8373 | 2:10:11 | 2:10:11 |
| Selecting all tabs and closing them also removes the pinned tabs | https://github.com/zen-browser/desktop/issues/8371 | None | 5:03:07 |
| Invisible text in right click menu | https://github.com/zen-browser/desktop/issues/8369 | None | 5 days, 14:21:42 |
| [bug] i18n error, zen-background-tab-opened-toast has not been localized | https://github.com/zen-browser/desktop/issues/8368 | 9:27:59 | 9:27:59 |
| Split View flickers when moving split to the right | https://github.com/zen-browser/desktop/issues/8363 | None | 6:33:55 |
| Container Specific essentials not working | https://github.com/zen-browser/desktop/issues/8361 | None | 0:05:44 |
| Redundunt windows are opening up after launching zen browser | https://github.com/zen-browser/desktop/issues/8360 | 8:38:50 | None |
| zen.workspaces.show-workspace-indicator setting ignored | https://github.com/zen-browser/desktop/issues/8356 | 0:05:01 | 0:05:01 |
| Seemingly after each update, new icon shows in Dock | https://github.com/zen-browser/desktop/issues/8355 | 12:16:44 | None |
| Downloaded language pack not being respected (English GB) | https://github.com/zen-browser/desktop/issues/8354 | 0:35:35 | 1:02:34 |
| Can't disable Shortcut suggestions and it won't work if I disable the history | https://github.com/zen-browser/desktop/issues/8352 | 6:39:16 | 6:39:16 |
| Arabic language Custom font bug on arch linux | https://github.com/zen-browser/desktop/issues/8350 | None | None |
| Incorrect text on report option after deleting extension | https://github.com/zen-browser/desktop/issues/8349 | 15:25:03 | None |
| UI padding is not accurate in split tabs | https://github.com/zen-browser/desktop/issues/8347 | None | None |
| Cannot add tabs to Essentials panel nothing happens on click | https://github.com/zen-browser/desktop/issues/8345 | 16:45:06 | 16:59:08 |
| New tab doesn't open automatically if essential tabs are closed before regular tabs | https://github.com/zen-browser/desktop/issues/8344 | None | None |
| Pressing CTRL key unfocuses URL bar | https://github.com/zen-browser/desktop/issues/8343 | 2:09:34 | None |
| URL opacity blinks when leaving compact mode | https://github.com/zen-browser/desktop/issues/8342 | None | None |
| Cant Add more Essential tabs | https://github.com/zen-browser/desktop/issues/8341 | None | 2:59:08 |
| Sidebar not hidden in all windows on startup when using "Hide Both" in compact view | https://github.com/zen-browser/desktop/issues/8339 | 5:37:43 | 5:37:43 |
| No green dot on proton mail | https://github.com/zen-browser/desktop/issues/8337 | 4:15:47 | 4:15:47 |
| Mouse Cursor not disapearing while watching YouTube | https://github.com/zen-browser/desktop/issues/8336 | None | 3:07:20 |
| Very slow connecting on localhost website | https://github.com/zen-browser/desktop/issues/8334 | None | None |
| URL Bar: Can't disable Shortcut suggestions | https://github.com/zen-browser/desktop/issues/8333 | 2:02:02 | 4:12:37 |
| New tab does not open in same container as current tab | https://github.com/zen-browser/desktop/issues/8332 | None | 4:02:56 |
| Swipe on touchscreen does not scroll, it selects | https://github.com/zen-browser/desktop/issues/8331 | None | None |
| The floating bar is no longer displayed... | https://github.com/zen-browser/desktop/issues/8330 | 5:13:17 | 6:33:30 |
| Can't change the theme to a dark color, like black | https://github.com/zen-browser/desktop/issues/8329 | 5:15:54 | 6:24:16 |
| Secure connection failed | https://github.com/zen-browser/desktop/issues/8328 | None | 1 day, 5:03:14 |
| Essentials unload issue | https://github.com/zen-browser/desktop/issues/8326 | 9:04:50 | None |
| weird shadow when hightlighting text | https://github.com/zen-browser/desktop/issues/8325 | 10:20:39 | 10:20:39 |
| Mouse flickering on Browser MacOS | https://github.com/zen-browser/desktop/issues/8323 | 7 days, 7:55:25 | 7 days, 8:56:46 |
| DRM content checked yet can't play embedded videos. | https://github.com/zen-browser/desktop/issues/8320 | 6:30:09 | 6:30:09 |
| Issue when opening the New Tab url bar, and selecting a shortcut tab that's already an essential | https://github.com/zen-browser/desktop/issues/8319 | None | None |
| "Screenshot Copied" Toast Notification Unreadable | https://github.com/zen-browser/desktop/issues/8317 | 15:44:46 | None |
| Can't update to 1.12.4 | https://github.com/zen-browser/desktop/issues/8316 | None | 6:39:21 |
| Unable to add tabs to essentials after most recent update | https://github.com/zen-browser/desktop/issues/8315 | None | 2:14:49 |
| Transparent dialogs | https://github.com/zen-browser/desktop/issues/8314 | 1:11:28 | 2:29:34 |
| Unable to find a supported language | https://github.com/zen-browser/desktop/issues/8313 | 12:40:47 | 19:19:31 |
| Rearrange Split View keyboard shortcuts don't work on existing split views | https://github.com/zen-browser/desktop/issues/8310 | 9 days, 1:16:48 | None |
| Moving Essential Tab to New Window removes it from Essentials | https://github.com/zen-browser/desktop/issues/8309 | None | None |
| Adding new essentials is not working | https://github.com/zen-browser/desktop/issues/8308 | 5:24:00 | 23:11:34 |
| Mods get Reset/Removed when Updated | https://github.com/zen-browser/desktop/issues/8306 | 1 day, 1:03:16 | 1 day, 1:03:16 |
| Playing songs | https://github.com/zen-browser/desktop/issues/8305 | 2:53:37 | 2:53:37 |
| agency | https://github.com/zen-browser/desktop/issues/8304 | 3:31:16 | 1 day, 1:31:52 |
| crash | https://github.com/zen-browser/desktop/issues/8303 | 3:41:45 | None |
| collapse | https://github.com/zen-browser/desktop/issues/8302 | None | 0:01:39 |
| Glitch/flickering sidebar when switching spaces in compact mode. | https://github.com/zen-browser/desktop/issues/8301 | 14 days, 22:19:20 | None |
| workspace icons are missing | https://github.com/zen-browser/desktop/issues/8278 | 1:45:25 | None |
| URL bar and minize/maximize buttons overlap when the window is horziontally shrinked | https://github.com/zen-browser/desktop/issues/8277 | None | None |
| <option> dropdown background same color as text | https://github.com/zen-browser/desktop/issues/8276 | None | 0:02:32 |
| Full screen problem on macOS | https://github.com/zen-browser/desktop/issues/8275 | 11 days, 15:53:45 | None |
| When opening a Youtube video, cannot interact with certain elements like text, like/dislike, opening desc. and replies | https://github.com/zen-browser/desktop/issues/8273 | 21:55:54 | None |
| Media Playback Continues After Unloading a Pinned Tab | https://github.com/zen-browser/desktop/issues/8267 | 13:43:59 | None |
| Dark theme weird rendering | https://github.com/zen-browser/desktop/issues/8263 | 1:20:19 | 1:29:02 |
| Youtube picture in picture stops working randomly and restarting or opening a new window fixes it. | https://github.com/zen-browser/desktop/issues/8260 | None | None |
| Locked urlbar.suggest preferences in about:config | https://github.com/zen-browser/desktop/issues/8259 | 0:41:23 | 6:25:51 |
| The search engine selection window is truncated during initial configuration. | https://github.com/zen-browser/desktop/issues/8258 | None | 4:29:43 |
| Takes ~3 mins to open a link from vscode | https://github.com/zen-browser/desktop/issues/8257 | 6:14:20 | None |
| Mods cannot save settings on install | https://github.com/zen-browser/desktop/issues/8255 | None | 2:21:43 |
| Can't smoosh zen windows as small as we used to. | https://github.com/zen-browser/desktop/issues/8254 | 2:38:48 | 2:38:48 |
| Normal windows always open in previously used Windows virtual desktop instead of current one | https://github.com/zen-browser/desktop/issues/8252 | 1 day, 10:37:31 | None |
| Black Mark and Render Issue | https://github.com/zen-browser/desktop/issues/8251 | None | None |
| Fetch requests with `keepalive: true` not showing in the Network panel | https://github.com/zen-browser/desktop/issues/8249 | 0:21:23 | 0:21:23 |
| Unable to apply Theme Colors for Incognito mode | https://github.com/zen-browser/desktop/issues/8248 | None | None |
| Download animation is shown in multiple windows | https://github.com/zen-browser/desktop/issues/8247 | 0:35:42 | 1 day, 7:54:40 |
| Toast Notification is stuck on after opening a folder of bookmarks with middle mouse button when in compact mode | https://github.com/zen-browser/desktop/issues/8246 | 2:29:31 | 19:01:57 |
| Restoring session with multiple windows starts every window in a new tab | https://github.com/zen-browser/desktop/issues/8245 | 3:28:19 | 13:58:26 |
| I can't choose to never translate from Arabic. | https://github.com/zen-browser/desktop/issues/8244 | None | None |
| cannot uncheck shortcuts in adress bar | https://github.com/zen-browser/desktop/issues/8242 | 10:04:15 | 10:04:15 |
| Arabic font is not the same as latest default firefox font in the Linux build | https://github.com/zen-browser/desktop/issues/8241 | None | None |
| UI scaling issue on linux desktop | https://github.com/zen-browser/desktop/issues/8240 | None | 8 days, 0:09:35 |
| Can't close Tab via X on Tab. Only via Hotkey | https://github.com/zen-browser/desktop/issues/8239 | None | 0:01:41 |
| Top bar does not hide when activating compact mode | https://github.com/zen-browser/desktop/issues/8237 | 1:13:23 | 1:54:27 |
| Top bar auto-hide glitch when interacting with sidebar & bookmarks | https://github.com/zen-browser/desktop/issues/8234 | 22:44:31 | 1 day, 9:51:23 |
| Cloudflare captcha not working | https://github.com/zen-browser/desktop/issues/8230 | 2:31:04 | 2:31:04 |
| Moving tabs inside tab bar is buggy | https://github.com/zen-browser/desktop/issues/8229 | 2:53:47 | 3:29:02 |
| Media Controls are not visible when switched to full screen | https://github.com/zen-browser/desktop/issues/8228 | None | 0:21:20 |
| Cant Remove Predefined Site Suggestion from my Search Bar (CTRL+T) | https://github.com/zen-browser/desktop/issues/8227 | 2:31:43 | 4:57:01 |
| URL bar loses focus instead of resetting URL | https://github.com/zen-browser/desktop/issues/8226 | 0:41:33 | 0:41:33 |
| URL Bar loses focus when deleting last character | https://github.com/zen-browser/desktop/issues/8225 | 0:44:53 | 5 days, 6:37:15 |
| Hovering on a compact window to move a tab causes the sidebar to pop in and out | https://github.com/zen-browser/desktop/issues/8224 | None | None |
| Zen creates a .mozilla folder in $HOME directory on Linux | https://github.com/zen-browser/desktop/issues/8223 | None | None |
| Browser buttons and close window button unresponsive after closing the Zen application | https://github.com/zen-browser/desktop/issues/8219 | 0:48:05 | None |
| Cannot drag tabs from top to bottom to reorder a few positions when tabs are on the right | https://github.com/zen-browser/desktop/issues/8218 | None | None |
| Downloads Browser Crash | https://github.com/zen-browser/desktop/issues/8217 | None | None |
| Google Meet buttons don't working on sidebar | https://github.com/zen-browser/desktop/issues/8215 | 2:40:14 | 2:40:14 |
| không thể thêm thẻ thường trú(E) | https://github.com/zen-browser/desktop/issues/8214 | 0:01:54 | 0:01:54 |
| Blank screen when first launching Zen | https://github.com/zen-browser/desktop/issues/8213 | None | None |
| Cannot access certificate view from URL bar (padlock icon) | https://github.com/zen-browser/desktop/issues/8212 | 12 days, 6:42:18 | 12 days, 7:11:25 |
| Syncing workspace tabs not works | https://github.com/zen-browser/desktop/issues/8211 | 15 days, 5:13:05 | None |
| zen mods not working | https://github.com/zen-browser/desktop/issues/8208 | None | 0:08:14 |
| Workspace switch animation is blocking | https://github.com/zen-browser/desktop/issues/8206 | 9:29:26 | None |
| ZEN Mouse Dragging & Resizing (maximize) does not work sometimes | https://github.com/zen-browser/desktop/issues/8205 | 6:20:09 | None |
| (Hyprland)Topbar doesn't disappear in compact sidebar | https://github.com/zen-browser/desktop/issues/8204 | None | 2:16:10 |
| windows 10 transparency not working. | https://github.com/zen-browser/desktop/issues/8202 | 3:06:24 | 3:06:24 |
| Zen not restoring any tab from the already opened tabs from previous session | https://github.com/zen-browser/desktop/issues/8201 | 3:17:29 | 4:43:32 |
| website background colour chaning along with the theme colour of side bar | https://github.com/zen-browser/desktop/issues/8200 | 4:58:08 | 4:58:07 |
| Containerized Essential dissapear when 'Enable container-specific essentials' feature is switched on | https://github.com/zen-browser/desktop/issues/8199 | 4:32:24 | 4:43:46 |
| "New Window" windows don't have the 'Essential' tabs on them anymore | https://github.com/zen-browser/desktop/issues/8197 | 7:03:16 | 1 day, 7:55:21 |
| Compact Mode sidebar not showing/opening | https://github.com/zen-browser/desktop/issues/8196 | 7:07:23 | None |
| HTML <select> elements blank drop-down | https://github.com/zen-browser/desktop/issues/8195 | 4 days, 4:54:42 | None |
| Everything is bright white | https://github.com/zen-browser/desktop/issues/8194 | 1:23:35 | 1:23:35 |
| Search with google is pinned for no clear reason | https://github.com/zen-browser/desktop/issues/8193 | 1:10:15 | 1:11:58 |
| Popup Window shows side bar with no function in compact mode | https://github.com/zen-browser/desktop/issues/8191 | None | None |
| Button to list all tabs is transparent / disappears | https://github.com/zen-browser/desktop/issues/8188 | None | 6:09:10 |
| Color contrast on tab bar | https://github.com/zen-browser/desktop/issues/8187 | 5:36:28 | None |
| XDG Desktop Portal Termfilechooser doesn't work?! | https://github.com/zen-browser/desktop/issues/8186 | 16:11:53 | 22:30:50 |
| Address bar no longer shows 'Search with [engine]' option | https://github.com/zen-browser/desktop/issues/8185 | 0:17:41 | 0:55:52 |
| Checkboxes get activated on the Settings page when clicking on empty space along their width. | https://github.com/zen-browser/desktop/issues/8184 | 17:34:27 | 17:34:27 |
| Zen blocking keyboard in Deskflow | https://github.com/zen-browser/desktop/issues/8183 | None | None |
| Issue with the "Bottom Essentials" Zen Theme | https://github.com/zen-browser/desktop/issues/8177 | 4:02:27 | 4:07:49 |
| Dashlane failed connexion | https://github.com/zen-browser/desktop/issues/8176 | None | 5:19:09 |
| icons in split tabs shift to the right | https://github.com/zen-browser/desktop/issues/8175 | 0:20:13 | 1 day, 8:21:19 |
| Unable to open extensions when there is no tab | https://github.com/zen-browser/desktop/issues/8174 | 6:44:29 | None |
| Disable background tab notification by default | https://github.com/zen-browser/desktop/issues/8173 | 2:09:55 | 2:09:55 |
| [1.12.3] Mute/unmute icon slightly offset vertically on linux via AUR. | https://github.com/zen-browser/desktop/issues/8172 | None | 6 days, 1:08:02 |
| Workspace navigation shortcut not working on pages with conflicting shortcuts | https://github.com/zen-browser/desktop/issues/8171 | None | None |
| The popup for the shortcut copy URL looks broken. | https://github.com/zen-browser/desktop/issues/8169 | 3 days, 22:47:53 | 4 days, 23:49:18 |
| zen-empty-tab looses the selected="true" value sometimes when switching workspaces. | https://github.com/zen-browser/desktop/issues/8166 | 3:34:23 | None |
| In Compact Mode with 'Hide Toolbar' enabled, toolbar does not auto-hide after using the URL bar to open a link | https://github.com/zen-browser/desktop/issues/8164 | 0:35:09 | 5 days, 18:21:46 |
| When in fullscreen(F11) bookmarks bar disappear | https://github.com/zen-browser/desktop/issues/8163 | 13 days, 2:22:49 | None |
| Lost all tabs after a restart | https://github.com/zen-browser/desktop/issues/8162 | 0:07:51 | None |
| Login fails on specific sites (e.g. Keeper, school network) in normal mode on Windows, but works in Incognito | https://github.com/zen-browser/desktop/issues/8161 | None | None |
| When too many tabs are open in a private window to fit the tab bar, no scrollbar appears | https://github.com/zen-browser/desktop/issues/8160 | 7:48:44 | 7:48:44 |
| Print to PDF not working | https://github.com/zen-browser/desktop/issues/8159 | None | None |
| MacOS Text Replacements doesn't work | https://github.com/zen-browser/desktop/issues/8158 | 13 days, 23:41:07 | None |
| Zen RAM Consumption is too much (Possible Memory Leak??) | https://github.com/zen-browser/desktop/issues/8157 | None | None |
| Can't click on bookmark icons when in compact mode | https://github.com/zen-browser/desktop/issues/8155 | None | 0:04:33 |
| Sidebery extension: tabs from other workspaces become inaccessible (since 1.8b+) | https://github.com/zen-browser/desktop/issues/8154 | 1:57:05 | 1:57:05 |
| With dual monitor, when dual zen brower windows opened, Essentials' number will reduce to 1 | https://github.com/zen-browser/desktop/issues/8153 | None | None |
| Zen switches to irrelevant workspace on startup | https://github.com/zen-browser/desktop/issues/8151 | 0:17:36 | 0:17:36 |
| Youtube playback crashes after some time | https://github.com/zen-browser/desktop/issues/8150 | None | None |
| Cannot open live editing zen theme | https://github.com/zen-browser/desktop/issues/8148 | None | 0:08:04 |
| load in startup not working for the first essenntial tab only | https://github.com/zen-browser/desktop/issues/8147 | 0:14:09 | 3:11:28 |
| "New background tab" toast even for foreground tabs | https://github.com/zen-browser/desktop/issues/8146 | 0:35:34 | 3:51:23 |
| Compact sidebar keeps popping up flickering when switching to a zen window with focus in macOS | https://github.com/zen-browser/desktop/issues/8145 | None | None |
| When tabs on the right enabled, toast notifications appear off the screen | https://github.com/zen-browser/desktop/issues/8143 | None | 5:07:33 |
| GPU rendering does not work after crash | https://github.com/zen-browser/desktop/issues/8141 | 8:32:12 | None |
| Letterboxing bricks and violates the boundary between the tab bar and the rendering space of website when a website enters full screen mode | https://github.com/zen-browser/desktop/issues/8140 | 0:10:28 | None |
| picture in picture disappears when trying to navigate to split tab | https://github.com/zen-browser/desktop/issues/8139 | None | None |
| Clicking non-floating urlbar always selects all on mouse up | https://github.com/zen-browser/desktop/issues/8138 | None | 0:18:23 |
| Audible media playback doesn't work | https://github.com/zen-browser/desktop/issues/8137 | 1:22:13 | 2:25:29 |
| compact mode is not working and cant close the tabs some times in fedora | https://github.com/zen-browser/desktop/issues/8136 | 3:11:39 | 2 days, 0:05:39 |
| Cannot freely edit the url in the address bar | https://github.com/zen-browser/desktop/issues/8134 | 0:01:56 | 0:01:56 |
| New Tabs open in Essentials | https://github.com/zen-browser/desktop/issues/8133 | 0:35:39 | 5:02:21 |
| Developer tools not longer working? | https://github.com/zen-browser/desktop/issues/8132 | 0:37:16 | 1:48:35 |
| Black text on dark theme | https://github.com/zen-browser/desktop/issues/8131 | 0:21:09 | 8:18:43 |
| Option + Letter Shows Special Characters in Keyboard Shortcuts | https://github.com/zen-browser/desktop/issues/8130 | 8 days, 4:21:07 | None |
| Video Call (huddle) in Slack does not work | https://github.com/zen-browser/desktop/issues/8129 | None | None |
| Essential and Pinned tabs do not appear or get removed from existing windows | https://github.com/zen-browser/desktop/issues/8127 | 0:37:58 | 0:37:58 |
| Sidebar glitch | https://github.com/zen-browser/desktop/issues/8126 | None | 0:02:38 |
| No mods have worked since the update | https://github.com/zen-browser/desktop/issues/8125 | 5 days, 5:50:38 | None |
| [Twilight 1.12.2] Adding tab to essentials will automatically open that tab in the first workspace | https://github.com/zen-browser/desktop/issues/8123 | None | 1:53:34 |
| Unable to open Google/X/Youtube while using VPN | https://github.com/zen-browser/desktop/issues/8121 | 9:23:59 | None |
| Zen URL bar not working with Firefox Multi-Account Containers | https://github.com/zen-browser/desktop/issues/8120 | None | None |
| Duplicate Workspace Button | https://github.com/zen-browser/desktop/issues/8119 | 23:56:36 | 23:56:36 |
| Renamed Tabs Name keep ressetting at random | https://github.com/zen-browser/desktop/issues/8118 | 9:29:18 | 9:29:18 |
| Audio Indicator Missing on Inactive Tabs with Minimized Toolbar | https://github.com/zen-browser/desktop/issues/8115 | 2:57:00 | 6:18:33 |
| Zen crashes on reopen after closing last tab with `browser.tabs.closeWindowWithLastTab` enabled | https://github.com/zen-browser/desktop/issues/8114 | 2 days, 18:09:03 | 2 days, 21:48:46 |
| Local/lan links not opening | https://github.com/zen-browser/desktop/issues/8113 | None | 8 days, 8:42:53 |
| Some extensions doesn't pin, and others brake the UI | https://github.com/zen-browser/desktop/issues/8111 | 0:53:20 | None |
| Can't close split view | https://github.com/zen-browser/desktop/issues/8110 | 1:03:35 | 1:03:35 |
| Closing a tab opened from an essential tab gives blank page | https://github.com/zen-browser/desktop/issues/8107 | 6:06:03 | 2:08:05 |
| Cannot reset Jira page added to Essentials | https://github.com/zen-browser/desktop/issues/8106 | None | 2:38:16 |
| Custom Color Gradient creates MULTIPLE instances of the same color | https://github.com/zen-browser/desktop/issues/8105 | 13 days, 15:14:54 | None |
| Customize toolbar disturbed with system theme | https://github.com/zen-browser/desktop/issues/8104 | None | 2:06:43 |
| The vertical TAB bar does not respond to touch input | https://github.com/zen-browser/desktop/issues/8103 | 1 day, 6:09:41 | None |
| New tabs appear above pinned tabs | https://github.com/zen-browser/desktop/issues/8101 | 1:16:57 | 6:31:23 |
| Could not open any page | https://github.com/zen-browser/desktop/issues/8100 | 2:07:41 | None |
| Reload and Cmd+W for Essentials are not working. | https://github.com/zen-browser/desktop/issues/8099 | None | 7:51:14 |
| Two kinds of workspace indicators instead of one | https://github.com/zen-browser/desktop/issues/8098 | 3:21:10 | 3:21:10 |
| [Twilight 1.12.2] Switching tab to workspace retains a duplicate of the tab in the current workspace | https://github.com/zen-browser/desktop/issues/8095 | None | 2:24:21 |
| Browser with a lighter/darker tint when using Auto Theme in comparison to Light/Dark mode only since 1.12b | https://github.com/zen-browser/desktop/issues/8094 | None | 2:25:47 |
| [Twilight 1.12.2] Ctrl + k (search with google bar) is placed too low on screen | https://github.com/zen-browser/desktop/issues/8093 | 2:02:16 | 2:34:36 |
| [Twilight 1.12.2] Pressing CTRL causes strange behaviour in newtab panel for switch to tab indicator | https://github.com/zen-browser/desktop/issues/8092 | 4:15:13 | 4:15:21 |
| crash korean in private mode | https://github.com/zen-browser/desktop/issues/8091 | None | 12 days, 3:00:44 |
| toolbar doesnt pop up when opening tabs with middle mouse click | https://github.com/zen-browser/desktop/issues/8090 | 0:39:11 | None |
| Dragging tab bugs out when the sidebar is scrollable | https://github.com/zen-browser/desktop/issues/8089 | 13 days, 14:40:15 | 13 days, 20:40:31 |
| Essentials: Emulation mode breaks tab | https://github.com/zen-browser/desktop/issues/8088 | 22 days, 0:16:44 | None |
| The "Hide the default container indicator in the tab bar" button no longer hides "Default Workspace" | https://github.com/zen-browser/desktop/issues/8087 | 0:12:24 | 14:56:38 |
| scrollbars styling with scrollbar-width: thin; leads to unusable scrollbar | https://github.com/zen-browser/desktop/issues/8086 | None | None |
| Zen has become unberably laggy | https://github.com/zen-browser/desktop/issues/8081 | 1:29:54 | None |
| Select words in URL bar selects full URL not selected word | https://github.com/zen-browser/desktop/issues/8080 | 0:45:24 | 1 day, 13:57:39 |
| Hidden Sidebar in Compact Mode Styling Broken after 1.12b update | https://github.com/zen-browser/desktop/issues/8079 | 0:05:13 | 0:15:01 |
| Youtube description not opening when clicking on more | https://github.com/zen-browser/desktop/issues/8078 | 4 days, 1:31:56 | 4 days, 2:56:14 |
| Unable to access my company's local websites HTTPS Error with HSTS and No Bypass Option | https://github.com/zen-browser/desktop/issues/8077 | None | 19:34:22 |
| Ctrl+Tab missing unloaded tabs | https://github.com/zen-browser/desktop/issues/8076 | 1:38:20 | 3:22:45 |
| When Account Button is opened in a collapsed Sibebar, Most Text Doesn't appear (Profile Name, Account Settings options, etc...) | https://github.com/zen-browser/desktop/issues/8075 | None | None |
| Find in page. | https://github.com/zen-browser/desktop/issues/8074 | 4:37:54 | 4:37:54 |
| Visual glitch / artifact when scrolling past video. | https://github.com/zen-browser/desktop/issues/8073 | 22 days, 7:35:17 | None |
| Primary Colour resets after opening every time when System theme - auto is selected | https://github.com/zen-browser/desktop/issues/8072 | 8:42:39 | 8:42:39 |
| 'Customize Toolbar' menu transparent when title bar is switched off | https://github.com/zen-browser/desktop/issues/8070 | 12:00:44 | 11 days, 3:51:56 |
| Can't turn off workspaces | https://github.com/zen-browser/desktop/issues/8069 | 0:08:06 | 0:08:06 |
| URL bar loses focus after clearing text | https://github.com/zen-browser/desktop/issues/8068 | 0:47:48 | 0:47:48 |
| Developer tools shortcut not working anymore | https://github.com/zen-browser/desktop/issues/8067 | 0:32:03 | 0:32:03 |
| Collapsed Sidebar too small | https://github.com/zen-browser/desktop/issues/8066 | 0:01:19 | 0:01:19 |
| Bookmark editor closes immediately if window is at the edge of a display (macOS) | https://github.com/zen-browser/desktop/issues/8065 | 1:40:38 | None |
| DRM Proxy Error on Kinopoisk | https://github.com/zen-browser/desktop/issues/8063 | 2:17:49 | 2:25:55 |
| Cannot hide Workspace indicator: v1.12.1b | https://github.com/zen-browser/desktop/issues/8062 | 4:47:32 | 4:47:32 |
| Search bar double click editing broken when shortcut suggestions are disabled | https://github.com/zen-browser/desktop/issues/8060 | None | 4:09:56 |
| Toolbar's New Tab button stops working when removed and re-added | https://github.com/zen-browser/desktop/issues/8059 | 7:20:37 | None |
| Essential Tabs not visible | https://github.com/zen-browser/desktop/issues/8057 | 7:46:00 | 7:54:47 |
| Deleting the last workspace makes the browser unusable | https://github.com/zen-browser/desktop/issues/8056 | 2:40:09 | 13:11:09 |
| Pinned Tab preview new login? | https://github.com/zen-browser/desktop/issues/8055 | None | None |
| Compact mode Visual bug | https://github.com/zen-browser/desktop/issues/8053 | 0:08:48 | 0:21:52 |
| Ctrl+Left Click with Firefox Containeres don't open a new tab on the container anymore | https://github.com/zen-browser/desktop/issues/8052 | 16:39:47 | None |
| YouTube videos are having frame drops when using Wayland in Fedora 42. No frame drops in Librewolf. | https://github.com/zen-browser/desktop/issues/8051 | 3:57:37 | None |
| I have already downloaded the Zen Mod, but I can't see the mods I downloaded in the settings. | https://github.com/zen-browser/desktop/issues/8049 | 6 days, 7:12:05 | 7 days, 1:59:01 |
| Not working form on page: bohemiahokej.cz | https://github.com/zen-browser/desktop/issues/8048 | 1:42:11 | 1:42:11 |
| Can't revert/remove custom sidebar color with `zen.theme.gradient.show-custom-colors` enabled | https://github.com/zen-browser/desktop/issues/8047 | 0:12:06 | 0:12:06 |
| Compact mode causes the Windows taskbar to not appear. | https://github.com/zen-browser/desktop/issues/8046 | 0:33:43 | None |
| Selects whole url after selecting only fragment | https://github.com/zen-browser/desktop/issues/8045 | None | 0:38:26 |
| When URL is changed while URL bar is floating, only the domain is shown | https://github.com/zen-browser/desktop/issues/8042 | 23 days, 8:40:24 | 24 days, 8:18:57 |
| Nvidia RTX video super resolution doesn't work in Zen unless in PiP Fullscreen | https://github.com/zen-browser/desktop/issues/8041 | 17:38:25 | None |
| Super Resolution doesn't work | https://github.com/zen-browser/desktop/issues/8040 | 5:58:49 | 5:58:49 |
| Unable to Load Essentials Tab Content | https://github.com/zen-browser/desktop/issues/8039 | 4:33:58 | 1 day, 7:53:50 |
| Critical workflow issue with multilingual users: When emptying the URL bar in 'new tab mode', the browser should not lose focus on the URL bar by default. | https://github.com/zen-browser/desktop/issues/8038 | 22:34:12 | 23:41:42 |
| PDFs load slowly | https://github.com/zen-browser/desktop/issues/8037 | 6:45:05 | None |
| Sidebar appears narrower than it should, showing only part of the icons within | https://github.com/zen-browser/desktop/issues/8036 | 1:40:47 | 3:16:56 |
| YouTube speed-up shortcut (Maj+;) doesn't work | https://github.com/zen-browser/desktop/issues/8035 | 8:02:43 | 8:02:44 |
| Conflict between "container-specific essentials" and "enbale container tabs" | https://github.com/zen-browser/desktop/issues/8034 | None | 0:02:34 |
| Sidebar and Layout "Collapsed Toolbar" looks weird after new update (icon does not fit/clip thru the frame) | https://github.com/zen-browser/desktop/issues/8033 | None | 4:32:52 |
| Cannot scroll through the sidebar to see all of my tabs (in private mode only) | https://github.com/zen-browser/desktop/issues/8032 | 1 day, 3:36:41 | 4 days, 20:23:05 |
| Missing Tab Layout Icon After Recent Update | https://github.com/zen-browser/desktop/issues/8030 | 10:23:25 | 1 day, 0:17:56 |
| Update failed and not resolved even after redownloadig and reinstalling from website. | https://github.com/zen-browser/desktop/issues/8029 | 0:16:48 | None |
| Tab Group disbanding when moved | https://github.com/zen-browser/desktop/issues/8028 | 10:58:02 | 10:58:02 |
| Zen offers to update to 1.12.1b but fails because "1.12.1b" is not listed in releases. | https://github.com/zen-browser/desktop/issues/8027 | None | 0:04:20 |
| Missing `:empty` pseudo-element state on extension icon's badge | https://github.com/zen-browser/desktop/issues/8025 | None | None |
| [1.12b] Pinned tabs and essentials still used old audio indicator | https://github.com/zen-browser/desktop/issues/8023 | 15:43:19 | 15:43:19 |
| Zen Won't Update | https://github.com/zen-browser/desktop/issues/8022 | 0:10:55 | 14:31:46 |
| Tabs in a Firefox container can not be put into essentials. | https://github.com/zen-browser/desktop/issues/8019 | 0:09:31 | 14:15:02 |
| Opened tab jumps up and down when switching workspaces | https://github.com/zen-browser/desktop/issues/8018 | 21:08:38 | 1 day, 15:03:56 |
| Can't disable acrylic - mica not available | https://github.com/zen-browser/desktop/issues/8015 | 21:36:33 | 21:36:33 |
| [1.12b] Mica context menus sometimes utilizes windows wallpaper instead of browser window | https://github.com/zen-browser/desktop/issues/8014 | 15:09:55 | 2 days, 4:03:37 |
| Binding parentheses to switch workspaces does not function properly | https://github.com/zen-browser/desktop/issues/8013 | 24 days, 1:02:10 | None |
| Update 1.12b broke auto-hide sidebar formatting | https://github.com/zen-browser/desktop/issues/8012 | 0:01:53 | 0:17:30 |
| Vertical Tab bar is smaller in width compared to previous versions, causing tab icons to get cut off. | https://github.com/zen-browser/desktop/issues/8011 | 0:03:09 | 4:00:48 |
| [Linux][1.12b] DRM Broken | https://github.com/zen-browser/desktop/issues/8010 | 0:16:39 | 1:38:19 |
| I could not open side bar | https://github.com/zen-browser/desktop/issues/8009 | 0:25:19 | 16:55:20 |
| 我在商店下载了Zen Mod但是在设置中加载不出来 | https://github.com/zen-browser/desktop/issues/8006 | 9:33:02 | 23:19:18 |
| Mute/Unmute icon on | https://github.com/zen-browser/desktop/issues/8005 | 1 day, 1:31:01 | 17 days, 14:53:41 |
| Zen Browser has never been able to access Google, Chrome and FireFox does. | https://github.com/zen-browser/desktop/issues/8004 | 1 day, 1:32:04 | None |
| Url bar overflow | https://github.com/zen-browser/desktop/issues/8003 | 0:15:34 | None |
| window controls on right do not pop out with sidebar | https://github.com/zen-browser/desktop/issues/8002 | 21:56:50 | 1 day, 0:14:59 |
| Alignment issue in compact mode with hidden sidebar | https://github.com/zen-browser/desktop/issues/8000 | 0:02:32 | 4:26:16 |
| All my tabs except essenitials cannot be seen or clicked however they still open and work as per normal | https://github.com/zen-browser/desktop/issues/7999 | None | 0:07:12 |
| Bring back the old profiles switcher | https://github.com/zen-browser/desktop/issues/7998 | 0:12:46 | 1 day, 21:49:18 |
| macOS keyboard shortcut `option+cmd+e` opens `Network tab` of Developer tools instead of switching workspace | https://github.com/zen-browser/desktop/issues/7996 | 1 day, 5:12:44 | 1 day, 5:12:44 |
| when typing in the URL bar, the selected results don't show up well | https://github.com/zen-browser/desktop/issues/7995 | 7:58:12 | 1 day, 23:28:05 |
| URL doesnt always float even after choosing this option | https://github.com/zen-browser/desktop/issues/7993 | None | 3:05:24 |
| [Twilight 1.12] Compact mode sidebar jittering still occurs when newtab panel is opened | https://github.com/zen-browser/desktop/issues/7990 | None | 6:15:39 |
| [Twilight 1.12] Compact mode isn't disabled in newtab pannel if the user is hovering on the sidebar berforehand and sidebar jittering occurs | https://github.com/zen-browser/desktop/issues/7988 | None | 0:14:31 |
| Favicon Missing in Essentials on Launch | https://github.com/zen-browser/desktop/issues/7987 | 7:50:38 | 4 days, 12:35:25 |
| Closing a tab sometimes causes a popping sound | https://github.com/zen-browser/desktop/issues/7986 | None | None |
| File Drag and Drop Between Tabs Not Working | https://github.com/zen-browser/desktop/issues/7985 | 11:13:41 | None |
| Auto PiP doesn't work (Windows) | https://github.com/zen-browser/desktop/issues/7984 | 0:14:53 | 0:22:59 |
| Some websites don't load | https://github.com/zen-browser/desktop/issues/7983 | 1 day, 23:32:20 | 3 days, 0:32:47 |
| Annoying notifs and pop-up windows | https://github.com/zen-browser/desktop/issues/7982 | None | 3:58:00 |
| Single-Key keyboard shortcuts are taking precedence over input fields | https://github.com/zen-browser/desktop/issues/7980 | 2 days, 1:30:27 | None |
| The “Undo tab close” shortcut doesn't seem to work | https://github.com/zen-browser/desktop/issues/7979 | 2 days, 1:41:19 | 4 days, 2:55:52 |
| Window controls overlapping over URL Bar UI & extensions icon | https://github.com/zen-browser/desktop/issues/7978 | 25 days, 4:18:00 | None |
| Default keyboard shortcuts are overlapping | https://github.com/zen-browser/desktop/issues/7976 | 29 days, 10:30:04 | None |
| iCloud Notes show black squares on checklists | https://github.com/zen-browser/desktop/issues/7974 | 6:47:44 | 6:47:47 |
| [Twilight 1.12] Compact mode sidebar twitches when hovering | https://github.com/zen-browser/desktop/issues/7973 | 0:30:09 | 12:33:26 |
| Essentials disappear when clicking "back to tab" in PIP while in another workspace | https://github.com/zen-browser/desktop/issues/7972 | 2 days, 16:38:14 | 2 days, 16:38:14 |
| System sleep/suspend during video playback causes a memory leak | https://github.com/zen-browser/desktop/issues/7970 | None | None |
| BitWarden biometrics not working anymore | https://github.com/zen-browser/desktop/issues/7968 | 30 days, 1:15:16 | None |
| Appimage does not work on distros that depend on musl instead of glibc | https://github.com/zen-browser/desktop/issues/7967 | 3 days, 19:05:33 | None |
| YouTube WebPage not loading | https://github.com/zen-browser/desktop/issues/7966 | None | 2:46:37 |
| New Tab Bug - Opens the url of the existing tab even when new url is typed. | https://github.com/zen-browser/desktop/issues/7964 | 3 days, 0:39:48 | None |
| PDF rendering issues | https://github.com/zen-browser/desktop/issues/7963 | 0:02:45 | None |
| 1Password extension unable to communicate with desktop app, requires password reprompt | https://github.com/zen-browser/desktop/issues/7960 | 0:04:11 | 30 days, 6:03:10 |
| Moving a bookmark to another workspace doesn't persist | https://github.com/zen-browser/desktop/issues/7957 | 26 days, 6:50:27 | 26 days, 6:59:41 |
| Gradient completely messed up | https://github.com/zen-browser/desktop/issues/7955 | None | 1:01:12 |
| URL Bar dosent showing up and fading gradually while typing | https://github.com/zen-browser/desktop/issues/7954 | 26 days, 9:53:46 | None |
| URL bar Doesn't Show up on Ctrl+T Despite Behavior set to "Always floating" in Settings | https://github.com/zen-browser/desktop/issues/7953 | 0:37:17 | 7 days, 15:33:13 |
| Zen breaking youtube.com | https://github.com/zen-browser/desktop/issues/7952 | 4:30:14 | 4:30:14 |
| Zen doesn't open (flatpak) | https://github.com/zen-browser/desktop/issues/7951 | 3 days, 12:35:28 | None |
| Compact mode - 1 pixel line on sidebar | https://github.com/zen-browser/desktop/issues/7950 | 1:52:17 | 12:15:36 |
| Caching Tab Icon is Too Strict | https://github.com/zen-browser/desktop/issues/7949 | 3 days, 16:10:52 | 3 days, 16:10:52 |
_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_
Search query used to find these items: `repo:zen-browser/desktop is:issue created:2025-05-01..2025-05-31`

2
l10n

Submodule l10n updated: 644474b8c9...d0ffb8e391

8
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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", () => {

View File

@@ -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&#38;utm_medium=firefox-desktop&#38;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&#38;utm_medium=firefox-desktop&#38;utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
- </description>
</vbox>
</vbox>
</hbox>

View File

@@ -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);
}

View File

@@ -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"/>

View File

@@ -1,5 +1,5 @@
diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index 8f8c5536cb6e54300897180ca3f950974c60e930..79305485631f14712d8b2674f477b641ec53fcd6 100644
index 8f8c5536cb6e54300897180ca3f950974c60e930..403f821f561a2dfcf6ad23db7e61712aac867716 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -26,6 +26,7 @@
@@ -26,7 +26,7 @@ index 8f8c5536cb6e54300897180ca3f950974c60e930..79305485631f14712d8b2674f477b641
</vbox>
</html:template>
+<hbox id="zen-main-app-wrapper" flex="1" persist="zen-compact-mode">
+<hbox id="zen-main-app-wrapper" flex="1" persist="zen-compact-mode" class="browser-toolbox-background">
#include navigator-toolbox.inc.xhtml
#include browser-box.inc.xhtml

View File

@@ -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>

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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]);
},
};
@@ -1033,9 +1046,12 @@ var gZenCKSSettings = {
input.classList.remove(`${ZEN_CKS_INPUT_FIELD_CLASS}-not-set`);
// This is because on some OSs (windows/macos mostly) the key is not the same as the keycode
// e.g. CTRL+ALT+3 may be displayed as the euro sign
let shortcut = event.key;
// First, try to read the *physical* key via event.code.
// If event.code is like "KeyS", "KeyA", ..., strip off "Key" → "S".
// Otherwise, fall back to event.key (e.g. "F5", "Enter", etc.).
let shortcut;
if (event.code && event.code.startsWith('Key')) shortcut = event.code.slice(3);
else shortcut = event.key;
shortcut = shortcut.replace(/Ctrl|Control|Shift|Alt|Option|Cmd|Meta/, ''); // Remove all modifiers
@@ -1220,7 +1236,7 @@ Preferences.addAll([
default: false,
},
{
id: 'browser.tabs.unloadOnLowMemory',
id: 'zen.mods.auto-update',
type: 'bool',
default: true,
},

View File

@@ -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" />

View File

@@ -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"

View 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 };
},

View File

@@ -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..16b171c56081759e81d3efa6c0c7840fbd7902ff 100644
--- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -2088,7 +2088,6 @@ var SessionStoreInternal = {
@@ -31,17 +31,27 @@ 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 = {
@@ -4361,6 +4363,7 @@ var SessionStoreInternal = {
// Append the tab if we're opening into a different window,
index: aSource == aTargetWindow ? pos : Infinity,
pinned: state.pinned,
+ essential: state.zenEssential,
userContextId: state.userContextId,
skipLoad: true,
preferredRemoteType,
@@ -5239,7 +5242,7 @@ var SessionStoreInternal = {
}
let workspaceID = aWindow.getWorkspaceID();
@@ -50,7 +60,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
winData.workspaceID = workspaceID;
}
},
@@ -5430,14 +5430,15 @@ var SessionStoreInternal = {
@@ -5430,14 +5433,15 @@ var SessionStoreInternal = {
}
let tabbrowser = aWindow.gBrowser;
@@ -68,7 +78,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
continue;
}
let tabData = lazy.TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
@@ -5456,8 +5457,8 @@ var SessionStoreInternal = {
@@ -5456,8 +5460,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 +89,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
winData.title = tabbrowser.tabs[0].label;
}
winData.selected = selectedIndex;
@@ -5569,8 +5570,8 @@ var SessionStoreInternal = {
@@ -5569,8 +5573,8 @@ var SessionStoreInternal = {
// selectTab represents.
let selectTab = 0;
if (overwriteTabs) {
@@ -90,7 +100,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
selectTab = Math.min(selectTab, winData.tabs.length);
}
@@ -5613,6 +5614,7 @@ var SessionStoreInternal = {
@@ -5613,6 +5617,7 @@ var SessionStoreInternal = {
winData.tabs,
winData.groups ?? []
);
@@ -98,12 +108,13 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
this._log.debug(
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
);
@@ -6162,8 +6164,23 @@ var SessionStoreInternal = {
@@ -6162,6 +6167,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 +129,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);

View File

@@ -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);

View File

@@ -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..37c84d1f61e76bfcf4fdead2299f748ac461f6a9 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -413,11 +413,41 @@
@@ -182,15 +182,16 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
@@ -2680,6 +2726,7 @@
@@ -2680,6 +2726,8 @@
schemelessInput,
hasValidUserGestureActivation = false,
textDirectiveUserActivation = false,
+ _forZenEmptyTab,
+ essential,
} = {}
) {
// all callers of addTab that pass a params object need to pass
@@ -2690,6 +2737,12 @@
@@ -2690,6 +2738,12 @@
);
}
@@ -203,7 +204,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
if (!UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.start("browser.tabs.opening", "initting", window);
}
@@ -2753,6 +2806,16 @@
@@ -2753,6 +2807,19 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -216,11 +217,22 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
+ }
+ if (_forZenEmptyTab) {
+ t.setAttribute("zen-empty-tab", "true");
+ }
+ if (essential) {
+ t.setAttribute("zen-essential", "true");
+ }
if (insertTab) {
// Insert the tab into the tab container in the correct position.
// For now, we support `index` as an alias for `tabIndex`.
@@ -2783,6 +2846,7 @@
@@ -2765,6 +2832,7 @@
ownerTab,
openerTab,
pinned,
+ essential,
bulkOrderedOpen,
tabGroup: tabGroup ?? openerTab?.group,
});
@@ -2783,6 +2851,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -228,7 +240,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
}));
if (focusUrlBar) {
@@ -2902,6 +2966,12 @@
@@ -2902,6 +2971,12 @@
}
}
@@ -241,7 +253,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
// Additionally send pinned tab events
if (pinned) {
this._notifyPinnedStatus(t);
@@ -2990,10 +3060,10 @@
@@ -2990,10 +3065,10 @@
isAdoptingGroup = false,
isUserTriggered = false,
telemetryUserCreateSource = "unknown",
@@ -253,7 +265,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
}
if (!color) {
@@ -3014,7 +3084,12 @@
@@ -3014,7 +3089,12 @@
label,
isAdoptingGroup
);
@@ -267,7 +279,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
group,
insertBefore?.group ?? insertBefore
);
@@ -3303,6 +3378,7 @@
@@ -3303,6 +3383,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -275,7 +287,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
}
) {
// If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
@@ -3372,6 +3448,7 @@
@@ -3372,6 +3453,7 @@
openWindowInfo,
name,
skipLoad,
@@ -283,7 +295,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
});
}
@@ -3560,7 +3637,7 @@
@@ -3560,7 +3642,7 @@
// Add a new tab if needed.
if (!tab) {
let createLazyBrowser =
@@ -292,37 +304,17 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
let url = "about:blank";
if (tabData.entries?.length) {
@@ -3598,7 +3675,29 @@
@@ -3598,7 +3680,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 +3705,8 @@
// needs calling:
shouldUpdateForPinnedTabs = true;
}
@@ -332,7 +324,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 +3720,10 @@
tabGroup.stateData.id,
tabGroup.stateData.color,
tabGroup.stateData.collapsed,
@@ -344,7 +336,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
);
tabsFragment.appendChild(tabGroup.node);
}
@@ -3684,8 +3787,16 @@
@@ -3684,8 +3771,16 @@
// to remove the old selected tab.
if (tabToSelect) {
let leftoverTab = this.selectedTab;
@@ -363,7 +355,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
}
if (tabs.length > 1 || !tabs[0].selected) {
@@ -3881,7 +3992,7 @@
@@ -3881,7 +3976,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,16 +364,16 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -3904,7 +4015,7 @@
@@ -3904,7 +3999,7 @@
) {
elementIndex = Infinity;
} else if (previousTab.visible) {
- elementIndex = previousTab.elementIndex + 1;
+ elementIndex = (typeof previousTab.elementIndex === 'undefined') ? index : (previousTab.elementIndex + 1);
+ elementIndex = (typeof previousTab.elementIndex === 'undefined') ? elementIndex : (previousTab.elementIndex + 1);
} else if (previousTab == FirefoxViewHandler.tab) {
elementIndex = 0;
}
@@ -3932,10 +4043,10 @@
@@ -3932,14 +4027,14 @@
}
// Ensure index is within bounds.
if (tab.pinned) {
@@ -395,7 +387,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 +4042,7 @@
this.tabContainer._invalidateCachedTabs();
@@ -404,7 +401,15 @@ 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 @@
@@ -3980,6 +4075,7 @@
if (pinned) {
this._updateTabBarForPinnedTabs();
}
+ gZenWorkspaces.fixTabInsertLocation(tab, itemAfter);
TabBarVisibility.update();
}
@@ -4268,6 +4364,9 @@
return;
}
@@ -414,7 +419,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
this.removeTabs(selectedTabs, { telemetrySource });
}
@@ -4520,6 +4634,7 @@
@@ -4520,6 +4619,7 @@
telemetrySource,
} = {}
) {
@@ -422,7 +427,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 +4704,7 @@
if (lastToClose) {
this.removeTab(lastToClose, aParams);
}
@@ -430,7 +435,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
} catch (e) {
console.error(e);
}
@@ -4641,6 +4757,12 @@
@@ -4641,6 +4742,12 @@
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
}
@@ -443,7 +448,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
@@ -4655,7 +4777,9 @@
@@ -4655,7 +4762,9 @@
// frame created for it (for example, by updating the visually selected
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
@@ -454,7 +459,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -4821,7 +4945,7 @@
@@ -4821,7 +4930,7 @@
closeWindowWithLastTab != null
? closeWindowWithLastTab
: !window.toolbar.visible ||
@@ -463,7 +468,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 +4954,7 @@
newTab = true;
}
@@ -471,7 +476,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -4885,9 +5010,7 @@
@@ -4885,9 +4995,7 @@
aTab._mouseleave();
if (newTab) {
@@ -482,7 +487,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
} else {
TabBarVisibility.update();
}
@@ -5016,6 +5139,8 @@
@@ -5016,6 +5124,8 @@
this.tabs[i]._tPos = i;
}
@@ -491,7 +496,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
if (!this._windowIsClosing) {
if (wasPinned) {
this.tabContainer._positionPinnedTabs();
@@ -5230,6 +5355,7 @@
@@ -5230,6 +5340,7 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -499,7 +504,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 +5353,13 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -515,7 +520,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
);
let tab = this.tabContainer.findNextTab(aTab, {
@@ -5264,7 +5390,7 @@
@@ -5264,7 +5375,7 @@
}
if (tab) {
@@ -524,7 +529,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -5285,7 +5411,7 @@
@@ -5285,7 +5396,7 @@
});
}
@@ -533,7 +538,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
}
_blurTab(aTab) {
@@ -5686,10 +5812,10 @@
@@ -5686,10 +5797,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -546,7 +551,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 +6097,7 @@
// Don't allow mixing pinned and unpinned tabs.
if (this.isTab(element) && element.pinned) {
@@ -555,7 +560,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -6012,10 +6138,16 @@
@@ -6012,10 +6123,16 @@
this.#handleTabMove(
element,
() => {
@@ -574,7 +579,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
neighbor.after(element);
} else {
@@ -6084,17 +6216,26 @@
@@ -6084,17 +6201,29 @@
targetElement = targetElement.group;
}
}
@@ -583,8 +588,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 +613,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
if (element.pinned && this.tabContainer.verticalMode) {
return this.tabContainer.verticalPinnedTabsContainer;
}
@@ -6154,7 +6295,7 @@
@@ -6154,7 +6283,7 @@
if (!this.isTab(aTab)) {
throw new Error("Can only move a tab into a tab group");
}
@@ -613,7 +622,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
return;
}
if (aTab.group && aTab.group.id === aGroup.id) {
@@ -6248,6 +6389,10 @@
@@ -6248,6 +6377,10 @@
moveActionCallback();
@@ -624,7 +633,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 +7278,7 @@
// preventDefault(). It will still raise the window if appropriate.
break;
}
@@ -633,7 +642,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
window.focus();
aEvent.preventDefault();
break;
@@ -8044,6 +8189,7 @@
@@ -8044,6 +8177,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -641,7 +650,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -9009,7 +9155,7 @@ var TabContextMenu = {
@@ -9009,7 +9143,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;
@@ -650,7 +659,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
// Move Tab items
let contextMoveTabOptions = document.getElementById(
"context_moveTabOptions"
@@ -9278,6 +9424,7 @@ var TabContextMenu = {
@@ -9278,6 +9412,7 @@ var TabContextMenu = {
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
});
} else {

View 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 =

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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 {

View File

@@ -1,5 +1,5 @@
diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css
index 007aec91e089a1d2df20235890b268b820b0a529..ac0592cbcec62ffefb58b491dff48749852f2d88 100644
index 007aec91e089a1d2df20235890b268b820b0a529..a03a0d7b7837f6315f47f76a4491bf08d96e00e0 100644
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -31,7 +31,6 @@
@@ -10,3 +10,12 @@ index 007aec91e089a1d2df20235890b268b820b0a529..ac0592cbcec62ffefb58b491dff48749
}
}
@@ -56,7 +55,7 @@
/* This is needed for Windows 10, see bug 1961257 */
@media (-moz-windows-accent-color-in-titlebar) {
- :root[customtitlebar][sizemode="normal"] #navigator-toolbox {
+ :root[customtitlebar][sizemode="normal"] #browser {
border-top: .5px solid ActiveBorder;
&:-moz-window-inactive {
border-top-color: InactiveBorder;

View File

@@ -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;

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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".

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.
*/

View File

@@ -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}`);
}

View File

@@ -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',

View File

@@ -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

View 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();
}

View File

@@ -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', 'zen-toast-container'],
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],
},

View File

@@ -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;

View File

@@ -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/.
const lazy = {};
var { AppConstants } = ChromeUtils.importESModule('resource://gre/modules/AppConstants.sys.mjs');
ChromeUtils.defineESModuleGetters(lazy, {
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
});
@@ -10,7 +15,11 @@ class ZenUIMigration {
init(isNewProfile, win) {
if (!isNewProfile) {
this._migrate(win);
try {
this._migrate(win);
} catch (e) {
console.error('ZenUIMigration: Error during migration', e);
}
}
this.clearVariables();
}
@@ -87,6 +96,9 @@ class ZenUIMigration {
}
_migrateV4(win) {
if (AppConstants.platform === 'linux') {
return;
}
Services.prefs.setBoolPref(
'browser.tabs.unloadOnLowMemory',
Services.prefs.getBoolPref('zen.tab-unloader.enabled', true)

View File

@@ -26,7 +26,7 @@
}
}
:root[zen-before-loaded='true'] #browser > *,
:root[zen-before-loaded='true'] #browser > *:not(#zen-browser-background, #zen-toast-container),
: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,12 @@
z-index: 1;
}
@media (-moz-windows-accent-color-in-titlebar) and (-moz-windows-mica) {
background-color: ActiveCaption;
@media (-moz-windows-accent-color-in-titlebar) and ((-moz-windows-mica) or -moz-pref('browser.theme.windows.accent-color-in-tabs.enabled')) {
background: 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 +257,7 @@
opacity: 0;
transition: opacity 0.1s ease-in-out;
pointer-events: none;
transform: translateX(-50%);
}
&:hover::before {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
},

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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() {

View File

@@ -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 || parentTab.hasAttribute('glance-id')) {
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();

View File

@@ -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,
});

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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;
}
}
}
}

View File

@@ -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
View 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',
],
});
}

View File

@@ -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);
};
},
};

View File

@@ -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',
],
});

View 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 });
}
}

View 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 });
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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",
]

View File

@@ -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();

View File

@@ -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);

View File

@@ -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() {
@@ -624,7 +624,7 @@
// Remove everything except the entry we want to keep
state.entries = [state.entries[foundEntryIndex]];
}
state.image = pin.iconUrl || null;
state.image ||= pin.iconUrl || null;
state.index = 0;
SessionStore.setTabState(tab, state);
@@ -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;
}
@@ -766,19 +765,6 @@
document.getElementById('context_pinTab')?.before(element);
}
// TODO: remove this as it's not possible to know the base pinned url any more as it's now stored in tab state
resetPinnedTabData(tabData) {
if (
lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl &&
tabData.pinned &&
tabData.zenPinnedEntry
) {
tabData.entries = [JSON.parse(tabData.zenPinnedEntry)];
tabData.image = tabData.zenPinnedIcon;
tabData.index = 0;
}
}
updatePinnedTabContextMenu(contextTab) {
if (!this.enabled) {
document.getElementById('context_pinTab').hidden = true;
@@ -791,7 +777,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 +929,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 +990,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 +1033,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 +1110,8 @@
}
window.gZenPinnedTabManager = new ZenPinnedTabManager();
gZenPinnedTabManager.promisePinnedCacheInitialized = new Promise((resolve) => {
gZenPinnedTabManager._resolveInitializedPinnedCache = resolve;
});
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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,
})

View File

@@ -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",

View File

@@ -14,3 +14,4 @@
["browser_pinned_to_essential.js"]
["browser_issue_7654.js"]
["browser_issue_8726.js"]

View File

@@ -0,0 +1,59 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
const { TabStateFlusher } = ChromeUtils.importESModule(
'resource:///modules/sessionstore/TabStateFlusher.sys.mjs'
);
add_task(async function test_Restore_Pinned_Tab() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: 'https://example.com/',
},
async function (browser) {
let tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
ok(tab.pinned, 'The tab should be pinned after being created');
await BrowserTestUtils.removeTab(tab);
await TabStateFlusher.flushWindow(window);
restoreLastClosedTabOrWindowOrSession();
tab = gBrowser.selectedTab;
ok(tab.pinned, 'The tab should be pinned after restore');
ok(
tab.parentElement.closest('.zen-workspace-pinned-tabs-section'),
'The tab should be in the pinned tabs section after restore'
);
await BrowserTestUtils.removeTab(tab);
}
);
});
add_task(async function test_Restore_Essential_Tab() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: 'https://example.com/',
},
async function (browser) {
let tab = gBrowser.getTabForBrowser(browser);
gZenPinnedTabManager.addToEssentials(tab);
ok(
tab.hasAttribute('zen-essential'),
'The tab should be marked as essential after being created'
);
await BrowserTestUtils.removeTab(tab);
await TabStateFlusher.flushWindow(window);
restoreLastClosedTabOrWindowOrSession();
tab = gBrowser.selectedTab;
ok(tab.hasAttribute('zen-essential'), 'The tab should be marked as essential after restore');
ok(
tab.parentElement.closest('.zen-essentials-container'),
'The tab should be in the essentials tabs section after restore'
);
await BrowserTestUtils.removeTab(tab);
}
);
});

View File

@@ -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 += [

View File

@@ -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",

View File

@@ -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 += [

View File

@@ -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",

View File

@@ -6,8 +6,13 @@
var _tabsToPin = [];
var _tabsToPinEssentials = [];
const kZenElementsToIgnore = ['zen-browser-background', 'zen-toast-container'];
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');
}

View File

@@ -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 {

View File

@@ -23,6 +23,9 @@
) {
return;
}
this.promiseInitialized = new Promise((resolve) => {
this._resolveInitialized = resolve;
});
this.dragStartPosition = null;
ChromeUtils.defineLazyGetter(this, 'panel', () =>
@@ -73,7 +76,7 @@
initContextMenu() {
const menu = window.MozXULElement.parseXULToFragment(`
<menuitem class="zenToolbarThemePicker"
<menuitem id="zenToolbarThemePicker"
data-lazy-l10n-id="zen-workspaces-change-gradient"
command="cmd_zenOpenZenThemePicker"/>
`);
@@ -116,7 +119,8 @@
this.initContextMenu();
this.initPredefinedColors();
this._hasInitialized = true;
this._resolveInitialized();
delete this._resolveInitialized;
this.onDarkModeChange(null);
}
@@ -944,10 +948,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 +959,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})`;
}
}
@@ -1105,7 +1106,7 @@
let workspaceTheme = theme || workspace.theme;
await this.foreachWindowAsActive(async (browser) => {
if (!browser.gZenThemePicker?._hasInitialized) {
if (browser.closing || (await browser.gZenThemePicker?.promiseInitialized)) {
return;
}
// Do not rebuild if the workspace is not the same as the current one
@@ -1125,6 +1126,7 @@
}
}
const appBackground = browser.document.getElementById('zen-browser-background');
if (!skipUpdate) {
browser.document.documentElement.style.setProperty(
'--zen-main-browser-background-old',

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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];
@@ -2684,4 +3322,20 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
}
});
}
fixTabInsertLocation(tab) {
if (tab.hasAttribute('zen-essential')) {
// Essential tabs should always be inserted at the end of the essentials section
const essentialsSection = this.getEssentialsSection(tab);
if (essentialsSection) {
essentialsSection.appendChild(tab);
}
} else if (tab.pinned) {
// Pinned tabs should always be inserted at the end of the pinned tabs container
const pinnedContainer = this.pinnedTabsContainer;
if (pinnedContainer) {
pinnedContainer.insertBefore(tab, pinnedContainer.lastChild);
}
}
}
})();

View File

@@ -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;
}
}

View File

@@ -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.10b",
"github": {
"repo": "zen-browser/desktop"
},