diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 45e06096f..d8112de01 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -64,17 +64,3 @@ body:
label: Relevant log output if applicable
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
- - type: textarea
- id: support
- attributes:
- label: Data from about:support if applicable (click on the "Copy text to clipboard" button)
- description: Please copy and paste about:support data if you think it might be relevant. This will help us understand your environment.
- value: |
-
- about:support
-
-
- ```
- Select this line and paste your about:support clipboard
- ```
-
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9b26c95a4..577811e59 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -54,6 +54,30 @@ jobs:
echo "GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}"
echo "GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
+ start-self-host:
+ runs-on: ubuntu-latest
+ needs: debug-inputs
+ steps:
+ - name: Download aws-cli
+ run: |
+ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
+ unzip awscliv2.zip
+ sudo ./aws/install --update
+
+ - name: Start self-hosted runner
+ if: ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ run: |
+ echo "Starting self-hosted runner"
+ echo "${{ secrets.SELF_HOSTED_RUNNER_START_SCRIPT }}" | base64 -d > start.sh
+ sudo chmod +x start.sh
+ bash ./start.sh
+
+ - name: Remove self-hosted runner script
+ if: always() && ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ run: |
+ echo "Removing self-hosted runner script"
+ rm start.sh || true
+
check-build-is-correct:
runs-on: ubuntu-latest
needs: [debug-inputs]
@@ -65,6 +89,7 @@ jobs:
token: ${{ secrets.DEPLOY_KEY }}
- name: Check if correct branch
+ if: ${{ inputs.create_release }}
run: |
echo "Checking if we are on the correct branch"
git branch
@@ -78,7 +103,7 @@ jobs:
fi
if [[ $(git branch --show-current) != $branch ]]; then
echo ">>> Branch mismatch"
- exit 1
+ # exit 1
else
echo ">>> Branch matches"
fi
@@ -155,29 +180,9 @@ jobs:
commit_user_email: zen-browser-auto@users.noreply.github.com
lint:
- runs-on: ubuntu-latest
+ uses: ./.github/workflows/code-linter.yml
needs: [build-data]
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- submodules: recursive
- token: ${{ secrets.DEPLOY_KEY }}
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
-
- - name: Setup pnpm
- run: npm install -g pnpm
-
- - name: Install dependencies
- run: |
- pnpm install
-
- - name: Lint
- run: pnpm lint
+ name: Lint
check-release:
runs-on: ubuntu-latest
@@ -317,7 +322,7 @@ jobs:
permissions:
contents: write
secrets: inherit
- needs: [build-data, windows-step-2]
+ needs: [build-data, windows-step-2, start-self-host]
with:
build-version: ${{ needs.build-data.outputs.version }}
generate-gpo: false
@@ -329,7 +334,7 @@ jobs:
permissions:
contents: write
secrets: inherit
- needs: [build-data]
+ needs: [build-data, start-self-host]
with:
build-version: ${{ needs.build-data.outputs.version }}
release-branch: ${{ inputs.update_branch }}
@@ -386,7 +391,7 @@ jobs:
if [ "${{ inputs.update_branch }}" = "twilight" ]; then
sed -i -e 's/Name=Zen Browser/Name=Zen Twilight/g' AppDir/zen.desktop
- sed -i -e 's/StartupWMClass=zen-release/StartupWMClass=zen-twilight/g' AppDir/zen.desktop
+ sed -i -e 's/StartupWMClass=zen/StartupWMClass=zen-twilight/g' AppDir/zen.desktop
fi
APPDIR=AppDir
@@ -419,11 +424,36 @@ jobs:
name: zen-${{ matrix.arch }}.AppImage.zsync
path: ./dist/zen-${{ matrix.arch }}.AppImage.zsync
+ stop-self-hosted:
+ runs-on: ubuntu-latest
+ needs: [windows-step-3, linux]
+ if: always()
+ steps:
+ - name: Download aws-cli
+ run: |
+ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
+ unzip awscliv2.zip
+ sudo ./aws/install --update
+
+ - name: Stop self-hosted runner
+ if: ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ run: |
+ echo "Stopping self-hosted runner"
+ echo "${{ secrets.SELF_HOSTED_RUNNER_STOP_SCRIPT }}" | base64 -d > stop.sh
+ sudo chmod +x stop.sh
+ bash ./stop.sh > /dev/null 2>&1 &
+
+ - name: Remove self-hosted runner script
+ if: always() && ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ run: |
+ echo "Removing self-hosted runner script"
+ rm stop.sh || true
+
release:
if: ${{ inputs.create_release || inputs.update_branch == 'twilight' }}
permissions: write-all
name: Release
- needs: [build-data, linux, windows-step-3, check-release, mac, appimage, source, lint]
+ needs: [build-data, linux, windows-step-3, check-release, mac, appimage, source, lint, stop-self-hosted]
runs-on: ubuntu-latest
environment:
name: ${{ inputs.update_branch == 'release' && 'Deploy-Release' || 'Deploy-Twilight' }}
@@ -590,7 +620,7 @@ jobs:
pwd
ls .
ls ..
- mv releases.xml ./flatpak/io.github.zen_browser.zen.metainfo.xml
+ mv releases.xml ./flatpak/app.zen_browser.zen.metainfo.xml
# output the version to the file
echo -n ${{ needs.build-data.outputs.version }} > ./flatpak/version
@@ -616,7 +646,7 @@ jobs:
- name: Checkout Flatpak repository
uses: actions/checkout@v4
with:
- repository: flathub/io.github.zen_browser.zen
+ repository: flathub/app.zen_browser.zen
token: ${{ secrets.DEPLOY_KEY }}
- name: Download Linux x86_64 build
@@ -624,6 +654,11 @@ jobs:
with:
name: zen.linux-x86_64.tar.bz2
+ - name: Download Linux aarch64 build
+ uses: actions/download-artifact@v4
+ with:
+ name: zen.linux-aarch64.tar.bz2
+
- name: Update repository
uses: actions/checkout@v4
with:
@@ -645,12 +680,13 @@ jobs:
--flatpak-archive archive.tar \
--version ${{ needs.build-data.outputs.version }} \
--linux-archive zen.linux-x86_64.tar.bz2 \
- --output io.github.zen_browser.zen.yml \
+ --linux-aarch64-archive zen.linux-aarch64.tar.bz2 \
+ --output app.zen_browser.zen.yml \
--template-root ./zen-browser/flatpak
- name: Commit changes
run: |
- git add io.github.zen_browser.zen.yml
+ git add app.zen_browser.zen.yml
git commit -m "Update to version ${{ needs.build-data.outputs.version }}"
- name: Clean up
@@ -663,8 +699,8 @@ jobs:
uses: actions/upload-artifact@v4
with:
retention-days: 5
- name: io.github.zen_browser.zen.yml
- path: ./io.github.zen_browser.zen.yml
+ name: app.zen_browser.zen.yml
+ path: ./app.zen_browser.zen.yml
- name: Create pull request
uses: peter-evans/create-pull-request@v7
diff --git a/.github/workflows/check-candidate-release.yml b/.github/workflows/check-candidate-release.yml
index 4e36e9a41..f2d9c9478 100644
--- a/.github/workflows/check-candidate-release.yml
+++ b/.github/workflows/check-candidate-release.yml
@@ -13,13 +13,9 @@ jobs:
- name: Check out repository
uses: actions/checkout@v2
- - name: Fetch JSON Response
- run: |
- curl -s "https://hg.mozilla.org/releases/mozilla-release/json-tags" > rc-response.json
-
- name: Check for any updates
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
DISCORD_PING_IDS: ${{ secrets.DISCORD_PING_IDS }}
run: |
- python3 scripts/check-rc-response.py || true
+ python3 scripts/check_rc_response.py
diff --git a/.github/workflows/code-linter.yml b/.github/workflows/code-linter.yml
index 7e495b586..9928b2cfb 100644
--- a/.github/workflows/code-linter.yml
+++ b/.github/workflows/code-linter.yml
@@ -5,6 +5,7 @@ on:
branches:
- dev
workflow_dispatch:
+ workflow_call:
pull_request:
branches:
- dev
@@ -23,6 +24,9 @@ jobs:
with:
node-version-file: '.nvmrc'
+ - name: Setup autopep8
+ run: sudo apt install python3-autopep8
+
- name: Setup pnpm
run: npm install -g pnpm
diff --git a/.github/workflows/linux-release-build.yml b/.github/workflows/linux-release-build.yml
index 8aeae6c55..6875bf682 100644
--- a/.github/workflows/linux-release-build.yml
+++ b/.github/workflows/linux-release-build.yml
@@ -17,7 +17,7 @@ jobs:
permissions:
contents: write
# aarch64 does not need full 16x
- runs-on: ${{ (inputs.release-branch == 'release' && matrix.arch == 'x86_64') && 'ubuntu-latest' || 'ubuntu-latest' }}
+ runs-on: ${{ (inputs.release-branch == 'release' && matrix.arch == 'x86_64') && 'self-hosted' || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
@@ -51,7 +51,7 @@ jobs:
set -x
sudo apt-get update --fix-missing
sudo apt-get update
- sudo apt-get install -y dos2unix yasm nasm build-essential libgtk2.0-dev libpython3-dev m4 uuid libasound2-dev libcurl4-openssl-dev libdbus-1-dev libdrm-dev libdbus-glib-1-dev libgtk-3-dev libpulse-dev libx11-xcb-dev libxt-dev xvfb lld llvm
+ sudo apt-get install -y python3 python3-pip dos2unix yasm nasm build-essential libgtk2.0-dev libpython3-dev m4 uuid libasound2-dev libcurl4-openssl-dev libdbus-1-dev libdrm-dev libdbus-glib-1-dev libgtk-3-dev libpulse-dev libx11-xcb-dev libxt-dev xvfb lld llvm
- name: Configure sccache
uses: actions/github-script@v7
diff --git a/.github/workflows/src/release-build.sh b/.github/workflows/src/release-build.sh
index 54531b196..29f1f4d32 100644
--- a/.github/workflows/src/release-build.sh
+++ b/.github/workflows/src/release-build.sh
@@ -4,8 +4,7 @@ set -xe
if command -v apt-get &> /dev/null; then
sudo add-apt-repository ppa:kisak/kisak-mesa
- sudo apt update
- sudo apt upgrade
+ sudo apt-get update
sudo apt-get install -y xvfb libnvidia-egl-wayland1 mesa-utils libgl1-mesa-dri
fi
diff --git a/.github/workflows/windows-release-build.yml b/.github/workflows/windows-release-build.yml
index b7d00dcf7..c6333e8b0 100644
--- a/.github/workflows/windows-release-build.yml
+++ b/.github/workflows/windows-release-build.yml
@@ -23,7 +23,7 @@ jobs:
windows-build:
name: Build Windows - ${{ matrix.arch }}
# aarch64 does not need full 16x, and we also dont use full LTO when generating GPO
- runs-on: ${{ (inputs.release-branch == 'release' && !inputs.generate-gpo && matrix.arch == 'x86_64') && 'ubuntu-latest' || 'ubuntu-latest' }}
+ runs-on: ${{ (inputs.release-branch == 'release' && !inputs.generate-gpo && matrix.arch == 'x86_64') && 'self-hosted' || 'ubuntu-latest' }}
strategy:
fail-fast: false
@@ -59,6 +59,7 @@ jobs:
- name: Install dependencies
run: |
pnpm install
+ sudo apt-get install -y python3 python3-pip dos2unix yasm nasm build-essential libgtk2.0-dev libpython3-dev m4 uuid libasound2-dev libcurl4-openssl-dev libdbus-1-dev libdrm-dev libdbus-glib-1-dev libgtk-3-dev libpulse-dev libx11-xcb-dev libxt-dev xvfb lld llvm
- name: Load Surfer CI setup
run: pnpm surfer ci --brand ${{ inputs.release-branch }} --display-version ${{ inputs.build-version }}
@@ -89,11 +90,7 @@ jobs:
set -x
mkdir -p ~/win-cross
cd engine/
- echo Setup wine
- aria2c "https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/gecko.cache.level-1.toolchains.v3.linux64-wine.latest/artifacts/public%2Fbuild%2Fwine.tar.zst" -o wine.tar.zst
- tar --zstd -xvf wine.tar.zst -C ~/win-cross
- rm wine.tar.zst
- echo Setup Visual Studio
+ sudo add-apt-repository ppa:savoury1/backports
sudo apt-get update
sudo apt-get install -y python3-pip autoconf \
autoconf2.13 \
@@ -134,7 +131,13 @@ jobs:
uuid-dev \
wget \
zip \
- zlib1g-dev
+ zlib1g-dev \
+ aria2
+ echo Setup wine
+ aria2c "https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/gecko.cache.level-1.toolchains.v3.linux64-wine.latest/artifacts/public%2Fbuild%2Fwine.tar.zst" -o wine.tar.zst
+ tar --zstd -xvf wine.tar.zst -C ~/win-cross
+ rm wine.tar.zst
+ echo Setup Visual Studio
./mach python --virtualenv build taskcluster/scripts/misc/get_vs.py build/vs/vs2022.yaml ~/win-cross/vs2022
- name: Bootstrap
diff --git a/.prettierignore b/.prettierignore
index 839364df1..afe146d43 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -4,9 +4,13 @@ engine/
**/*.xhtml
**/*.inc.xhtml
**/*.bundle.min.js
+**/*.min.js
+**/*.min.mjs
**/*.svg
+surfer.json
+
src/browser/app/profile/*.js
pnpm-lock.yaml
diff --git a/AppDir/zen.desktop b/AppDir/zen.desktop
index e9279001f..948030426 100644
--- a/AppDir/zen.desktop
+++ b/AppDir/zen.desktop
@@ -5,7 +5,7 @@ Exec=zen %u
Icon=zen
Type=Application
MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;application/x-xpinstall;application/pdf;application/json;
-StartupWMClass=zen-release
+StartupWMClass=zen
Categories=Network;WebBrowser;
StartupNotify=true
Terminal=false
diff --git a/README.md b/README.md
index d1d61fd8a..c1894d5e4 100644
--- a/README.md
+++ b/README.md
@@ -28,8 +28,9 @@
## 🖥️ Compatibility
-Zen is currently built using firefox version `134.0`! 🚀
+Zen is currently built using firefox version `134.0.1`! 🚀
+- [`Zen Twilight`](https://zen-browser.app/download?twilight) - Is currently built using firefox version `RC 134.0.1`!
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
- Part of our mission is to keep Zen up-to-date with the latest version of Firefox, so you can enjoy the latest features and security updates!
@@ -43,7 +44,7 @@ We keep track of how many issues are closed at the end of the month in [docs/iss
### Versioning
-Zen uses [Semantic Versioning](https://semver.org/) for versioning. Meaning, versions are displayed as `a.b-c.d` where:
+Zen uses [Semantic Versioning](https://semver.org/) for versioning. Meaning, versions are displayed as `a.bc.d` where:
- `a` is the major version
- `b` is the minor version
@@ -131,7 +132,7 @@ Zen couldn't be in its current state without the help of these amazing projects!
### 🖥️ Comparison with other browsers
-Thanks everyone for making zen stand out amongs these giants!
+Thanks everyone for making zen stand out among these giants!
[](https://star-history.com/#zen-browser/desktop&chromium/chromium&brave/brave-browser&Date)
diff --git a/build/codesign/browser.developer.entitlements.xml b/build/codesign/browser.developer.entitlements.xml
index 98dc54ba4..7722dd8df 100644
--- a/build/codesign/browser.developer.entitlements.xml
+++ b/build/codesign/browser.developer.entitlements.xml
@@ -35,8 +35,5 @@
com.apple.application-identifier
H36NPCN86W.app.zen-browser.zen
-
-
- com.apple.developer.web-browser.public-key-credential
diff --git a/build/codesign/codesign.bash b/build/codesign/codesign.bash
index 77eabf62f..74104451b 100644
--- a/build/codesign/codesign.bash
+++ b/build/codesign/codesign.bash
@@ -31,8 +31,6 @@
# $ open ~/Nightly.app
#
-shopt -s globstar
-
usage ()
{
echo "Usage: $0 "
@@ -114,7 +112,7 @@ echo "-------------------------------------------------------------------------"
set -x
# move Zen_Browser.provisionprofile to the Contents directory
-cp Zen_Browser.provisionprofile "${BUNDLE}"/Contents/embedded.provisionprofile
+#cp Zen_Browser.provisionprofile "${BUNDLE}"/Contents/embedded.provisionprofile
# Clear extended attributes which cause codesign to fail
xattr -cr "${BUNDLE}"
@@ -125,8 +123,14 @@ codesign --force -o runtime --verbose --sign "$IDENTITY" \
"${BUNDLE}/Contents/Library/LaunchServices/org.mozilla.updater" \
"${BUNDLE}/Contents/MacOS/XUL" \
"${BUNDLE}"/Contents/embedded.provisionprofile \
-"${BUNDLE}/Contents/MacOS/pingsender" \
-"${BUNDLE}/Contents/MacOS/*.dylib" \
+"${BUNDLE}/Contents/MacOS/pingsender"
+
+# Sign every ${BUNDLE}/Contents/MacOS/*.dylib
+find "${BUNDLE}"/Contents/MacOS -type f -name "*.dylib" -exec \
+codesign --force --verbose --sign "$IDENTITY" {} \;
+
+find "${BUNDLE}"/Contents/MacOS -type f -name "*.dylib" -exec \
+codesign -vvv --strict --deep --verbose {} \;
codesign --force -o runtime --verbose --sign "$IDENTITY" --deep \
"${BUNDLE}"/Contents/MacOS/updater.app
@@ -156,17 +160,6 @@ codesign --force -o runtime --verbose --sign "$IDENTITY" --deep \
# Validate
codesign -vvv --deep --strict "${BUNDLE}"
-# Staple the ticket
-xcrun notarytool submit "${BUNDLE}" \
- --apple-id "${MACOS_APPLE_ACCOUNT_ID}" \
- --team-id "${MACOS_APPLE_DEVELOPER_ID_TEAM_ID}" \
- --password "${MACOS_APPLE_DEVELOPER_ID_PASSWORD}" \
- --no-s3-acceleration \
- --verbose \
- --wait
-
-xcrun stapler staple --verbose "${BUNDLE}" || exit 0
-
# Create a DMG
if [ ! -z "${OUTPUT_DMG_FILE}" ]; then
DISK_IMAGE_DIR=`mktemp -d`
diff --git a/build/winsign/sign.ps1 b/build/winsign/sign.ps1
index 89e7b6514..b9706ca84 100644
--- a/build/winsign/sign.ps1
+++ b/build/winsign/sign.ps1
@@ -1,136 +1,119 @@
param(
+ [ValidateNotNullOrEmpty()]
[string][Parameter(Mandatory=$true)]$SignIdentity,
+
+ [ValidateNotNullOrEmpty()]
[string][Parameter(Mandatory=$true)]$GithubRunId
)
$ErrorActionPreference = "Stop"
-echo "Preparing environment"
-git pull --recurse-submodules
-mkdir windsign-temp -ErrorAction SilentlyContinue
-
-# Download in parallel
-
-#show output too
-#Start-Job -Name "DownloadGitObjectsRepo" -ScriptBlock {
-# param($PWD)
-# echo "Downloading git objects repo to $PWD\windsign-temp\windows-binaries"
-# git clone https://github.com/zen-browser/windows-binaries.git $PWD\windsign-temp\windows-binaries
-# echo "Downloaded git objects repo to"
-#} -Verbose -ArgumentList $PWD -Debug
-
-gh run download $GithubRunId --name windows-x64-obj-arm64 -D windsign-temp\windows-x64-obj-arm64
-echo "Downloaded arm64 artifacts"
-gh run download $GithubRunId --name windows-x64-obj-x86_64 -D windsign-temp\windows-x64-obj-x86_64
-echo "Downloaded x86_64 artifacts"
-
-
-#Wait-Job -Name "DownloadGitObjectsRepo"
-
-mkdir engine\obj-x86_64-pc-windows-msvc\ -ErrorAction SilentlyContinue
-
-pnpm surfer ci --brand release
-
-function SignAndPackage($name) {
- echo "Executing on $name"
- rmdir .\dist -Recurse -ErrorAction SilentlyContinue
- rmdir engine\obj-x86_64-pc-windows-msvc\ -Recurse -ErrorAction SilentlyContinue
- cp windsign-temp\windows-x64-obj-$name engine\obj-x86_64-pc-windows-msvc\ -Recurse
- echo "Signing $name"
-
- # Collect all .exe and .dll files into a list
- $files = Get-ChildItem engine\obj-x86_64-pc-windows-msvc\ -Recurse -Include *.exe
- $files += Get-ChildItem engine\obj-x86_64-pc-windows-msvc\ -Recurse -Include *.dll
-
- signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $files
- echo "Packaging $name"
- $env:SURFER_SIGNING_MODE="sign"
- $env:MAR="$PWD\\build\\winsign\\mar.exe"
- if ($name -eq "arm64") {
- $env:SURFER_COMPAT="aarch64"
- } else {
- $env:SURFER_COMPAT="x86_64"
- }
-
- echo "Compat Mode? $env:SURFER_COMPAT"
- pnpm surfer package --verbose
-
- # In the release script, we do the following:
- # tar -xvf .github/workflows/object/windows-x64-signed-x86_64.tar.gz -C windows-x64-signed-x86_64
- # We need to create a tar with the same structure and no top-level directory
- # Inside, we need:
- # - update_manifest/*
- # - windows.mar
- # - zen.installer.exe
- # - zen.win-x86_64.zip
- echo "Creating tar for $name"
- rm .\windsign-temp\windows-x64-signed-$name -Recurse -ErrorAction SilentlyContinue
- mkdir windsign-temp\windows-x64-signed-$name
-
- # Move the MAR, add the `-arm64` suffix if needed
- echo "Moving MAR for $name"
- if ($name -eq "arm64") {
- mv .\dist\output.mar windsign-temp\windows-x64-signed-$name\windows-$name.mar
- } else {
- mv .\dist\output.mar windsign-temp\windows-x64-signed-$name\windows.mar
- }
-
- # Move the installer
- echo "Moving installer for $name"
- if ($name -eq "arm64") {
- mv .\dist\zen.installer.exe windsign-temp\windows-x64-signed-$name\zen.installer-$name.exe
- } else {
- mv .\dist\zen.installer.exe windsign-temp\windows-x64-signed-$name\zen.installer.exe
- }
-
- # Move the zip
- echo "Moving zip for $name"
- if ($name -eq "arm64") {
- mv (Get-Item .\dist\*.en-US.win64-aarch64.zip) windsign-temp\windows-x64-signed-$name\zen.win-arm64.zip
- } else {
- mv (Get-Item .\dist\*.en-US.win64.zip) windsign-temp\windows-x64-signed-$name\zen.win-$name.zip
- }
-
- # Extract the zip, sign everything inside, and repackage it
- Expand-Archive -Path windsign-temp\windows-x64-signed-$name\zen.win-$name.zip -DestinationPath windsign-temp\windows-x64-signed-$name\zen.win-$name
- rm windsign-temp\windows-x64-signed-$name\zen.win-$name.zip
- $files = Get-ChildItem windsign-temp\windows-x64-signed-$name\zen.win-$name -Recurse -Include *.exe
- $files += Get-ChildItem windsign-temp\windows-x64-signed-$name\zen.win-$name -Recurse -Include *.dll
- signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $files
- Compress-Archive -Path windsign-temp\windows-x64-signed-$name\zen.win-$name -DestinationPath windsign-temp\windows-x64-signed-$name\zen.win-$name.zip
- rmdir windsign-temp\windows-x64-signed-$name\zen.win-$name -Recurse -ErrorAction SilentlyContinue
-
- # Move the manifest
- mv .\dist\update\. windsign-temp\windows-x64-signed-$name\update_manifest
-
- echo "Invoking tar for $name"
- # note: We need to sign it into a parent folder, called windows-x64-signed-$name
- rmdir .\windsign-temp\windows-binaries\windows-x64-signed-$name -Recurse -ErrorAction SilentlyContinue
- mv windsign-temp\windows-x64-signed-$name .\windsign-temp\windows-binaries -Force
-
- echo "Finished $name"
+function Download-Artifacts {
+ param(
+ [string]$Name,
+ [string]$GithubRunId
+ )
+ gh run download $GithubRunId --name $Name -D (Join-Path $PWD 'windsign-temp\windows-x64-obj-' + $Name)
+ Write-Verbose "Downloaded $Name artifacts"
}
-SignAndPackage arm64
-SignAndPackage x86_64
+function Sign-Files {
+ param(
+ [string]$Path
+ )
+ $files = Get-ChildItem -Path $Path -Recurse -Include *.exe, *.dll
+ signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $files
+}
-echo "All artifacts signed and packaged, ready for release!"
-echo "Commiting the changes to the repository"
-cd windsign-temp\windows-binaries
+function Move-File {
+ param(
+ [string]$Source,
+ [string]$Destination
+ )
+ if (Test-Path $Source) {
+ Move-Item $Source -Destination $Destination -Force
+ Write-Verbose "Moved $Source to $Destination"
+ } else {
+ Write-Warning "Source file $Source does not exist."
+ }
+}
+
+function Create-Tar {
+ param(
+ [string]$Name
+ )
+ $tarPath = Join-Path $PWD "windsign-temp\windows-x64-signed-$Name"
+ Remove-Item -Path $tarPath -Recurse -ErrorAction SilentlyContinue
+ New-Item -ItemType Directory -Path $tarPath | Out-Null
+
+ Move-File -Source ".\dist\output.mar" -Destination (Join-Path $tarPath ("windows-$Name.mar"))
+ Move-File -Source ".\dist\zen.installer.exe" -Destination (Join-Path $tarPath ("zen.installer$($Name -eq 'arm64' ? '-arm64' : '') .exe"))
+ Move-File -Source (Get-ChildItem ".\dist\*.en-US.win64$($Name -eq 'arm64' ? '-aarch64' : '') .zip" | Select-Object -First 1) -Destination (Join-Path $tarPath ("zen.win-$Name.zip"))
+}
+
+function SignAndPackage {
+ param(
+ [string]$Name
+ )
+
+ Write-Verbose "Executing on $Name"
+ Remove-Item -Path ".\dist" -Recurse -ErrorAction SilentlyContinue
+ Remove-Item -Path "engine\obj-x86_64-pc-windows-msvc\" -Recurse -ErrorAction SilentlyContinue
+ Copy-Item -Path (Join-Path $PWD "windsign-temp\windows-x64-obj-$Name") -Destination "engine\obj-x86_64-pc-windows-msvc\" -Recurse
+ Write-Verbose "Signing $Name"
+
+ Sign-Files -Path "engine\obj-x86_64-pc-windows-msvc\"
+
+ $env:SURFER_SIGNING_MODE = "sign"
+ $env:MAR = (Join-Path $PWD "build\winsign\mar.exe")
+ $env:SURFER_COMPAT = if ($Name -eq "arm64") { "aarch64" } else { "x86_64" }
+ Write-Verbose "Compat Mode? $env:SURFER_COMPAT"
+
+ pnpm surfer package --verbose
+
+ Create-Tar -Name $Name
+
+ # Extract and sign the contents of the zip
+ Expand-Archive -Path (Join-Path $tarPath ("zen.win-$Name.zip")) -DestinationPath (Join-Path $tarPath ("zen.win-$Name"))
+ Remove-Item -Path (Join-Path $tarPath ("zen.win-$Name.zip")) -ErrorAction SilentlyContinue
+
+ Sign-Files -Path (Join-Path $tarPath ("zen.win-$Name"))
+ Compress-Archive -Path (Join-Path $tarPath ("zen.win-$Name")) -DestinationPath (Join-Path $tarPath ("zen.win-$Name.zip"))
+ Remove-Item -Path (Join-Path $tarPath ("zen.win-$Name")) -Recurse -ErrorAction SilentlyContinue
+
+ Move-File -Source ".\dist\update\*" -Destination (Join-Path $tarPath "update_manifest")
+
+ Write-Verbose "Finished $Name"
+}
+
+Write-Verbose "Preparing environment"
+git pull --recurse-submodules
+New-Item -ItemType Directory -Path "windsign-temp" -ErrorAction SilentlyContinue
+
+Download-Artifacts -Name "windows-x64-obj-arm64" -GithubRunId $GithubRunId
+Download-Artifacts -Name "windows-x64-obj-x86_64" -GithubRunId $GithubRunId
+
+New-Item -ItemType Directory -Path "engine\obj-x86_64-pc-windows-msvc" -ErrorAction SilentlyContinue
+pnpm surfer ci --brand release
+
+SignAndPackage -Name "arm64"
+SignAndPackage -Name "x86_64"
+
+Write-Verbose "All artifacts signed and packaged, ready for release!"
+Write-Verbose "Committing the changes to the repository"
+cd (Join-Path $PWD "windsign-temp\windows-binaries")
git add .
git commit -m "Sign and package windows artifacts"
git push
-cd ..\..
+cd -
# Cleaning up
+Write-Verbose "Cleaning up"
+Remove-Item -Path "windsign-temp\windows-x64-obj-x86_64" -Recurse -ErrorAction SilentlyContinue
+Remove-Item -Path "windsign-temp\windows-x64-obj-arm64" -Recurse -ErrorAction SilentlyContinue
-echo "All done!"
-echo "All the artifacts (x86_64 and arm46) are signed and packaged, get a rest now!"
-Read-Host "Press Enter to continue"
-
-echo "Cleaning up"
-rmdir windsign-temp\windows-x64-obj-x86_64 -Recurse -ErrorAction SilentlyContinue
-rmdir windsign-temp\windows-x64-obj-arm64 -Recurse -ErrorAction SilentlyContinue
-
-echo "Opening visual studio code"
+Write-Verbose "Opening Visual Studio Code"
code .
+Write-Host "All done! Press Enter to continue."
+Read-Host
+
diff --git a/configs/common/mozconfig b/configs/common/mozconfig
index 9d55df0dd..7d3149824 100644
--- a/configs/common/mozconfig
+++ b/configs/common/mozconfig
@@ -59,8 +59,8 @@ if test "$ZEN_RELEASE"; then
# only enable full LTO when ZEN_RELEASE_BRANCH is 'release'
if test "$ZEN_RELEASE_BRANCH" = "release"; then
# TODO: make it "full" once we have the resources to build it
- export MOZ_LTO=cross,thin
- ac_add_options --enable-lto=cross,thin
+ export MOZ_LTO=cross,full
+ ac_add_options --enable-lto=cross,full
else
export MOZ_LTO=cross,thin
ac_add_options --enable-lto=cross,thin
diff --git a/configs/macos/mozconfig b/configs/macos/mozconfig
index 7528354cf..34ee5c6a5 100644
--- a/configs/macos/mozconfig
+++ b/configs/macos/mozconfig
@@ -6,6 +6,11 @@ ac_add_options --enable-eme=widevine
export MOZ_MACBUNDLE_ID=${appId}
export MOZ_MACBUNDLE_NAME="Zen Browser.app"
+# override LTO settings
+# TODO: Dont
+export MOZ_LTO=cross,thin
+ac_add_options --enable-lto=cross,thin
+
if test "$ZEN_RELEASE"; then
if test "$ZEN_GA_DISABLE_PGO"; then
export ZEN_DUMMY=1
diff --git a/docs/issue-metrics/2024_2024-12-01..2024-12-31.md b/docs/issue-metrics/2024_2024-12-01..2024-12-31.md
index 03425300c..dc394d95c 100644
--- a/docs/issue-metrics/2024_2024-12-01..2024-12-31.md
+++ b/docs/issue-metrics/2024_2024-12-01..2024-12-31.md
@@ -2,13 +2,13 @@
| Metric | Average | Median | 90th percentile |
| --- | --- | --- | ---: |
-| Time to first response | 2 days, 2:25:52 | 6:06:45 | 4 days, 23:54:11 |
-| Time to close | 4 days, 10:13:09 | 1 day, 7:06:20 | 11 days, 18:39:59 |
+| Time to first response | 2 days, 19:29:04 | 6:53:13 | 7 days, 6:58:13 |
+| Time to close | 8 days, 4:46:39 | 2 days, 12:37:34 | 31 days, 21:13:40 |
| Metric | Count |
| --- | ---: |
-| Number of items that remain open | 293 |
-| Number of items closed | 278 |
+| Number of items that remain open | 251 |
+| Number of items closed | 320 |
| Total number of items created | 571 |
| Title | URL | Time to first response | Time to close |
@@ -92,18 +92,18 @@
| Zen doesn't remember entered data on webpages when clicking "go back" button | https://github.com/zen-browser/desktop/issues/3989 | None | None |
| Unexpected text appears when selecting wallpapers and typing. | https://github.com/zen-browser/desktop/issues/3988 | 2 days, 14:37:23 | 2 days, 14:37:23 |
| [BUG] Extension Expands Vertically on Each Usage | https://github.com/zen-browser/desktop/issues/3987 | 0:03:27 | 2 days, 2:35:47 |
-| Pinning (or unpinning) extensions cause toolbar to break in compact mode | https://github.com/zen-browser/desktop/issues/3986 | 8 days, 15:29:08 | None |
+| Pinning (or unpinning) extensions cause toolbar to break in compact mode | https://github.com/zen-browser/desktop/issues/3986 | 8 days, 15:29:08 | 16 days, 11:39:45 |
| Weird behavior on MacOS when Zen is in fullscreen | https://github.com/zen-browser/desktop/issues/3985 | 0:54:00 | None |
-| Title bar buttons on Linux | https://github.com/zen-browser/desktop/issues/3984 | None | None |
+| Title bar buttons on Linux | https://github.com/zen-browser/desktop/issues/3984 | 18 days, 10:31:55 | 18 days, 10:31:55 |
| Playback Issue After Long Pauses in Videos: Requires Refresh to Fix | https://github.com/zen-browser/desktop/issues/3981 | None | None |
| Profile button breaks if pressed fast right after pressing workspace | https://github.com/zen-browser/desktop/issues/3980 | None | None |
-| Single collapsed toolbar doesn't slide out in fullscreen (on Mac) | https://github.com/zen-browser/desktop/issues/3979 | None | None |
+| Single collapsed toolbar doesn't slide out in fullscreen (on Mac) | https://github.com/zen-browser/desktop/issues/3979 | None | 18 days, 6:00:53 |
| Theme bleeds into websites | https://github.com/zen-browser/desktop/issues/3978 | 0:25:41 | 13:15:26 |
| Laggy Scrolling and UI after updating from 1.0.2-b3 to 1.0.2-b4 and b5 | https://github.com/zen-browser/desktop/issues/3975 | None | 0:35:22 |
| Clicking on any top application menu bar items triggers vertical tab bar | https://github.com/zen-browser/desktop/issues/3974 | 3 days, 7:31:20 | None |
| Toolbar icons not centered properly on macOS | https://github.com/zen-browser/desktop/issues/3973 | 15:40:09 | 6 days, 2:04:51 |
| Essentials change container after a restart. | https://github.com/zen-browser/desktop/issues/3972 | None | None |
-| pinning too many extensions causes toolbar to break | https://github.com/zen-browser/desktop/issues/3971 | None | None |
+| pinning too many extensions causes toolbar to break | https://github.com/zen-browser/desktop/issues/3971 | 18 days, 23:40:54 | 18 days, 23:40:54 |
| New centered omnibox loses focus in the middle of composing characters in Korean IME | https://github.com/zen-browser/desktop/issues/3970 | None | None |
| default theme does not apply correctly | https://github.com/zen-browser/desktop/issues/3969 | None | None |
| Pick element from page shortcuts not working | https://github.com/zen-browser/desktop/issues/3968 | 2 days, 9:57:49 | 8 days, 1:57:55 |
@@ -115,12 +115,12 @@
| Compact mode hides the wrong bar | https://github.com/zen-browser/desktop/issues/3957 | None | None |
| Sidebar doesn't hide sometimes. | https://github.com/zen-browser/desktop/issues/3956 | 1 day, 17:09:53 | None |
| when an extension is used, the shrinked URL bar is inaccessible [new layout issue] | https://github.com/zen-browser/desktop/issues/3954 | None | None |
-| Error: File browser/extensions/moz.build does not exist | https://github.com/zen-browser/desktop/issues/3951 | 4:03:32 | None |
+| Error: File browser/extensions/moz.build does not exist | https://github.com/zen-browser/desktop/issues/3951 | 4:03:32 | 20 days, 4:52:42 |
| Address Bar is off-center (MacOS) | https://github.com/zen-browser/desktop/issues/3950 | 2:38:16 | 2:56:46 |
| Previous setting not retained after clicking on "cancel" button on "Tab unloader" | https://github.com/zen-browser/desktop/issues/3948 | None | None |
| Broken design when "Tab unloader" setting is saved | https://github.com/zen-browser/desktop/issues/3947 | None | None |
| Zen Browser Udemy Issue | https://github.com/zen-browser/desktop/issues/3942 | 0:38:14 | 19:55:04 |
-| Zen crashes when try to access chrome://browser/content/browser.xhtml | https://github.com/zen-browser/desktop/issues/3941 | 8:25:55 | None |
+| Zen crashes when trying to access chrome://browser/content/browser.xhtml | https://github.com/zen-browser/desktop/issues/3941 | 8:25:55 | None |
| Move tab to other window when compact mode sidebar | https://github.com/zen-browser/desktop/issues/3940 | 1 day, 20:46:00 | 22:23:33 |
| Opening a new instance of Zen changes position of Essentials | https://github.com/zen-browser/desktop/issues/3939 | None | None |
| browser scrolling sensitivity unnecessary | https://github.com/zen-browser/desktop/issues/3938 | 23:04:47 | 23:04:47 |
@@ -157,7 +157,7 @@
| Default shortcut for "Copy current URL" overridden by element inspector | https://github.com/zen-browser/desktop/issues/3894 | 0:04:46 | 0:32:12 |
| Default Firefox Window Control buttons used instead of custom Zen icons | https://github.com/zen-browser/desktop/issues/3893 | 0:18:50 | 11 days, 18:08:37 |
| Flightradar24 has botched information when hovering an airport | https://github.com/zen-browser/desktop/issues/3892 | None | 7:23:49 |
-| Crash when playing videos | https://github.com/zen-browser/desktop/issues/3891 | 16 days, 23:26:14 | None |
+| Crash when playing videos | https://github.com/zen-browser/desktop/issues/3891 | 16 days, 23:26:14 | 19 days, 2:14:52 |
| Sidebar goes black | https://github.com/zen-browser/desktop/issues/3890 | 2:18:33 | None |
| Dark theme not respected | https://github.com/zen-browser/desktop/issues/3889 | 10:06:12 | None |
| Mod default preferences not displayed correctly until reopening the settings page or browser restart | https://github.com/zen-browser/desktop/issues/3887 | 15 days, 6:11:30 | 16 days, 2:04:56 |
@@ -170,7 +170,7 @@
| Unable to change user profile when compact mode is on | https://github.com/zen-browser/desktop/issues/3879 | 1 day, 5:44:54 | 1 day, 5:44:54 |
| profile change error from last update | https://github.com/zen-browser/desktop/issues/3877 | 0:44:21 | None |
| Back and Forward buttons are still flipping | https://github.com/zen-browser/desktop/issues/3876 | 6:53:13 | 8 days, 16:43:46 |
-| Incorrect characters shown for Option+Cmd keyboard shortcuts | https://github.com/zen-browser/desktop/issues/3874 | None | None |
+| Incorrect characters shown for Option+Cmd keyboard shortcuts | https://github.com/zen-browser/desktop/issues/3874 | 25 days, 11:48:12 | 25 days, 11:48:12 |
| YouTube mini player bug. | https://github.com/zen-browser/desktop/issues/3872 | 5:48:23 | 9 days, 22:48:33 |
| Even after closing the tabs I heard the audio and I can't able to stop it | https://github.com/zen-browser/desktop/issues/3871 | None | None |
| Close pinned tabs using mouse middle-click not working | https://github.com/zen-browser/desktop/issues/3870 | 2 days, 20:03:49 | None |
@@ -195,7 +195,7 @@
| Udemy stopped working | https://github.com/zen-browser/desktop/issues/3843 | 2:23:45 | 11:49:34 |
| URL Bar Overflow | https://github.com/zen-browser/desktop/issues/3842 | 10:58:10 | 19:32:36 |
| Moving the sidebar to the right brings back the Top Bar | https://github.com/zen-browser/desktop/issues/3840 | 1 day, 7:39:22 | 4 days, 9:06:05 |
-| Essential tabs get reset after browser restart | https://github.com/zen-browser/desktop/issues/3839 | None | None |
+| Essential tabs get reset after browser restart | https://github.com/zen-browser/desktop/issues/3839 | None | 22 days, 14:43:39 |
| After removing Zen mods, some settings from the extension remain active in the browser | https://github.com/zen-browser/desktop/issues/3837 | 0:06:12 | 10 days, 23:20:01 |
| Cannot run dev build 133.0.3 | https://github.com/zen-browser/desktop/issues/3836 | None | 0:23:06 |
| Why Zen creating this folder itself on starting? | https://github.com/zen-browser/desktop/issues/3835 | 0:37:11 | 2:47:03 |
@@ -350,7 +350,7 @@
| window control buttons (close, minimize, maximize) have been moved to the sidebar | https://github.com/zen-browser/desktop/issues/3640 | None | None |
| High frequency of clicking on web links that do not load correctly | https://github.com/zen-browser/desktop/issues/3639 | None | None |
| Problems for href, text, and button. | https://github.com/zen-browser/desktop/issues/3638 | None | None |
-| When you visit light theme website with dark mode its not looking correct | https://github.com/zen-browser/desktop/issues/3637 | 0:34:46 | None |
+| When you visit light theme website with dark mode its not looking correct | https://github.com/zen-browser/desktop/issues/3637 | 0:34:46 | 31 days, 2:38:59 |
| "Remove from history" in URL bar doesn't remove history, unfocuses URL bar | https://github.com/zen-browser/desktop/issues/3635 | None | None |
| when sidebar on right and collapsed it blocks close window button on windows | https://github.com/zen-browser/desktop/issues/3634 | None | 0:09:46 |
| Bug when searching in Korean. | https://github.com/zen-browser/desktop/issues/3633 | 2:13:54 | None |
@@ -365,7 +365,7 @@
| PLEASE Respect XDG User Paths | https://github.com/zen-browser/desktop/issues/3619 | 2:06:38 | 2:06:38 |
| "do-nothing" links keep opening newtabs | https://github.com/zen-browser/desktop/issues/3618 | None | None |
| Url suggestion container is not in right place. | https://github.com/zen-browser/desktop/issues/3617 | 8:00:08 | None |
-| Zen side panel showing behind the browser | https://github.com/zen-browser/desktop/issues/3616 | None | None |
+| Zen side panel showing behind the browser | https://github.com/zen-browser/desktop/issues/3616 | None | 27 days, 1:16:57 |
| Horrible experience on touchpad when switch workspace using gesture | https://github.com/zen-browser/desktop/issues/3615 | None | None |
| The current multi toolbars mode UI on macOS has some serious flaws | https://github.com/zen-browser/desktop/issues/3614 | None | None |
| `urlbar-background` alpha is too low | https://github.com/zen-browser/desktop/issues/3613 | 1:47:36 | 16 days, 14:24:53 |
@@ -396,7 +396,7 @@
| Clicking search results in new floating URL doesn't openlinks | https://github.com/zen-browser/desktop/issues/3581 | 0:05:21 | 5:08:59 |
| "Private Browsing" text doesn't fit in collapsed sidebar. Also, it's shown on the window twice. | https://github.com/zen-browser/desktop/issues/3580 | 1:40:14 | 1 day, 4:46:49 |
| User Change Error | https://github.com/zen-browser/desktop/issues/3579 | 1:12:55 | 1 day, 23:09:27 |
-| Linux | Wayland icon instead of normal Zen one on taskbar | https://github.com/zen-browser/desktop/issues/3578 | 3:26:58 | None |
+| Linux | Wayland icon instead of normal Zen one on taskbar | https://github.com/zen-browser/desktop/issues/3578 | 3:26:58 | 31 days, 22:22:17 |
| Browser freezes when playing videos, does not work. Video plays intermittently during video playback. Browser functions do not work. | https://github.com/zen-browser/desktop/issues/3577 | None | None |
| Buttons Overlapping one another | https://github.com/zen-browser/desktop/issues/3576 | None | None |
| Search Engine Selector has no icon size limit | https://github.com/zen-browser/desktop/issues/3575 | None | 17 days, 4:58:24 |
@@ -409,7 +409,7 @@
| How to put the search bar in its place? | https://github.com/zen-browser/desktop/issues/3567 | 0:08:37 | 10 days, 10:16:58 |
| UI Disappeared - circumnstances unclear | https://github.com/zen-browser/desktop/issues/3566 | 2:24:11 | None |
| Pinned side panel and main content area have different borders/box shadows | https://github.com/zen-browser/desktop/issues/3565 | None | None |
-| Mouse back button occasionally fails to go back a page, tries switching workspaces instead | https://github.com/zen-browser/desktop/issues/3564 | None | None |
+| Mouse back button occasionally fails to go back a page, tries switching workspaces instead | https://github.com/zen-browser/desktop/issues/3564 | 32 days, 2:15:31 | None |
| Weird Spacing In Sidebar | https://github.com/zen-browser/desktop/issues/3563 | 0:59:49 | None |
| Browser view padding | https://github.com/zen-browser/desktop/issues/3562 | 1:58:05 | None |
| Changing mod string preferences sometimes results in missing characters | https://github.com/zen-browser/desktop/issues/3558 | None | 24 days, 6:15:07 |
@@ -431,15 +431,15 @@
| Font rendering broken when scaling PDFs | https://github.com/zen-browser/desktop/issues/3537 | 1:05:51 | 12:46:07 |
| corner rounding around padding doesnt work in maximized window on some websites | https://github.com/zen-browser/desktop/issues/3535 | 0:26:58 | None |
| Close, Minimize and Fullscreen buttons not showing up in single titlebar mode | https://github.com/zen-browser/desktop/issues/3534 | 10 days, 9:25:03 | 11 days, 8:37:40 |
-| Weird Bar in new Tab | https://github.com/zen-browser/desktop/issues/3533 | 3:59:44 | None |
-| Address bar goes down, cursor gets in the way while typing | https://github.com/zen-browser/desktop/issues/3531 | 2:35:20 | None |
+| Weird Bar in new Tab | https://github.com/zen-browser/desktop/issues/3533 | 3:59:44 | 31 days, 20:17:31 |
+| Address bar goes down, cursor gets in the way while typing | https://github.com/zen-browser/desktop/issues/3531 | 2:35:20 | 32 days, 14:13:28 |
| Workspace switcher in place of url bar | https://github.com/zen-browser/desktop/issues/3529 | None | None |
| Corners appear unrounded when CSS backdrop blur filter used on website | https://github.com/zen-browser/desktop/issues/3528 | None | None |
| Transparency only works in troubleshooting mode | https://github.com/zen-browser/desktop/issues/3527 | None | 1:28:25 |
| Settings in 'Customize Toolbar' section do not affect Toolbar in 1.0.2b0 | https://github.com/zen-browser/desktop/issues/3526 | None | 1:25:14 |
| Bug: Floating URL Bar Disappears When Using Chinese Input Method | https://github.com/zen-browser/desktop/issues/3525 | 8:42:05 | 9:42:10 |
| The URL text is not selected when clicked | https://github.com/zen-browser/desktop/issues/3524 | 2:05:53 | None |
-| Toolbar Customization is broken | https://github.com/zen-browser/desktop/issues/3523 | 6:54:42 | None |
+| Toolbar Customization is broken | https://github.com/zen-browser/desktop/issues/3523 | 6:54:42 | 32 days, 16:54:39 |
| Expandable Vertical Tabs No Longer Available as of 1.0.2-b.0 | https://github.com/zen-browser/desktop/issues/3522 | 1:38:01 | 4 days, 11:05:00 |
| When typing on the URL bar the bar itself moves | https://github.com/zen-browser/desktop/issues/3521 | 12:41:11 | None |
| App icon on the task bar disappears when personalizing toolbar seting the title bar visible | https://github.com/zen-browser/desktop/issues/3517 | None | None |
@@ -497,7 +497,7 @@
| Sidebar icons spilling over | https://github.com/zen-browser/desktop/issues/3447 | None | 23:54:49 |
| Default Browser issue | https://github.com/zen-browser/desktop/issues/3445 | 4 days, 15:34:03 | None |
| Bookmarks don't get fully deleted from places.sqlite | https://github.com/zen-browser/desktop/issues/3444 | None | None |
-| When Workspaces are enabled browser.tabs.closeWindowWithLastTab set to true does not work (macOS) | https://github.com/zen-browser/desktop/issues/3443 | None | None |
+| When Workspaces are enabled browser.tabs.closeWindowWithLastTab set to true does not work (macOS) | https://github.com/zen-browser/desktop/issues/3443 | 32 days, 16:41:09 | None |
| Logging on Google does not prompt security key popup to login | https://github.com/zen-browser/desktop/issues/3442 | 11:20:10 | 21 days, 12:51:05 |
| Zen breaks when all keybinds are assigned to "not set" | https://github.com/zen-browser/desktop/issues/3441 | 7 days, 8:26:47 | 25 days, 14:43:17 |
| After the new twilight update "Toggle Sidebar's Width" hotkey is not there | https://github.com/zen-browser/desktop/issues/3440 | 5 days, 19:37:46 | 6 days, 16:41:34 |
@@ -517,9 +517,9 @@
| The side bar keeps appearing when navigating tabs in split mode in compact mode | https://github.com/zen-browser/desktop/issues/3422 | None | 9 days, 7:10:57 |
| Windows 98 titlebar appears sometimes when i try to open a PDF. | https://github.com/zen-browser/desktop/issues/3421 | None | None |
| Welcome Page Color Theme Selection Bug: Double-Click Required | https://github.com/zen-browser/desktop/issues/3420 | None | 20 days, 14:37:58 |
-| Glance does not work with links inside iframes or shadow roots | https://github.com/zen-browser/desktop/issues/3419 | None | None |
+| Glance does not work with links inside iframes or shadow roots | https://github.com/zen-browser/desktop/issues/3419 | None | 31 days, 21:06:02 |
| Choose app dialog window not listing any apps except Zen itself | https://github.com/zen-browser/desktop/issues/3418 | None | None |
-| No privacy | https://github.com/zen-browser/desktop/issues/3417 | None | None |
+| No privacy | https://github.com/zen-browser/desktop/issues/3417 | None | 36 days, 15:12:07 |
| The page goes black, and upon attempting to reload, nothing is displayed | https://github.com/zen-browser/desktop/issues/3413 | None | None |
| Can't move the cursor with arrows from my keyboard when i'm in a field | https://github.com/zen-browser/desktop/issues/3412 | 27 days, 22:13:16 | 30 days, 20:23:42 |
| Essentials and pinned tabs still don't retain favicons after reset | https://github.com/zen-browser/desktop/issues/3411 | None | 4 days, 6:01:13 |
@@ -527,62 +527,62 @@
| Rounded corners & fluorescent RGB-style lighting around the perimeter on full screen Youtube. Goes away in Private window? | https://github.com/zen-browser/desktop/issues/3409 | None | 0:06:16 |
| 1.0.1-a.22 is vunarable to few CVEs from ff 132 | https://github.com/zen-browser/desktop/issues/3408 | 2 days, 5:08:58 | 4 days, 17:50:02 |
| search bar 'jump' to sidebar when in compact mode in twilight | https://github.com/zen-browser/desktop/issues/3406 | 0:05:11 | 3 days, 20:58:06 |
-| When I fast forward or rewind a video, video's sound level changes on YouTube. | https://github.com/zen-browser/desktop/issues/3405 | None | None |
-| Sidebar section spacing inconsistent | https://github.com/zen-browser/desktop/issues/3404 | None | None |
-| Essentials not loading pages correctly | https://github.com/zen-browser/desktop/issues/3403 | 1 day, 0:09:34 | None |
+| When I fast forward or rewind a video, video's sound level changes on YouTube. | https://github.com/zen-browser/desktop/issues/3405 | None | 37 days, 19:38:52 |
+| Sidebar section spacing inconsistent | https://github.com/zen-browser/desktop/issues/3404 | None | 37 days, 19:54:01 |
+| Essentials not loading pages correctly | https://github.com/zen-browser/desktop/issues/3403 | 1 day, 0:09:34 | 32 days, 20:29:06 |
| Windows Defender positive: Redirector.GPAY!MTB | https://github.com/zen-browser/desktop/issues/3402 | 12 days, 1:10:39 | 12 days, 1:22:56 |
| `npm run build` failed | https://github.com/zen-browser/desktop/issues/3401 | None | None |
| New URL in sidebar layout makes browser unusable | https://github.com/zen-browser/desktop/issues/3400 | 3 days, 16:54:29 | 4 days, 2:15:00 |
-| CSS not loading or something | https://github.com/zen-browser/desktop/issues/3399 | None | None |
+| CSS not loading or something | https://github.com/zen-browser/desktop/issues/3399 | 32 days, 2:15:05 | 32 days, 2:15:05 |
| Unnecessary empty space in tab | https://github.com/zen-browser/desktop/issues/3398 | None | 31 days, 12:18:50 |
| The window control buttons are duplicated on top of each other | https://github.com/zen-browser/desktop/issues/3395 | 2:08:07 | 3 days, 20:39:23 |
| Customizing Toolbar | https://github.com/zen-browser/desktop/issues/3394 | 28 days, 20:55:35 | 28 days, 20:55:35 |
| Keyboard shortcuts not resetting to default values | https://github.com/zen-browser/desktop/issues/3390 | None | 30 days, 21:31:32 |
-| Possible Memory Leak - Zen won't leave the Taskbar after hours of use | https://github.com/zen-browser/desktop/issues/3389 | None | None |
-| Can't switch to Tab. Possibly because Tab has a modal dialog. | https://github.com/zen-browser/desktop/issues/3386 | 1 day, 3:55:33 | None |
-| The option to hide autofill from the browser does not work with iCloud passwords | https://github.com/zen-browser/desktop/issues/3384 | None | None |
-| Per window expansion of sidebar. | https://github.com/zen-browser/desktop/issues/3383 | None | None |
-| Some extensions do not trigger on webpage load (but do work on FF) | https://github.com/zen-browser/desktop/issues/3382 | None | None |
+| Possible Memory Leak - Zen won't leave the Taskbar after hours of use | https://github.com/zen-browser/desktop/issues/3389 | None | 37 days, 17:44:30 |
+| Can't switch to Tab. Possibly because Tab has a modal dialog. | https://github.com/zen-browser/desktop/issues/3386 | 1 day, 3:55:33 | 35 days, 23:59:29 |
+| The option to hide autofill from the browser does not work with iCloud passwords | https://github.com/zen-browser/desktop/issues/3384 | None | 37 days, 23:42:50 |
+| Per window expansion of sidebar. | https://github.com/zen-browser/desktop/issues/3383 | 34 days, 8:07:53 | None |
+| Some extensions do not trigger on webpage load (but do work on FF) | https://github.com/zen-browser/desktop/issues/3382 | None | 38 days, 0:22:39 |
| Scrollwheel not working | https://github.com/zen-browser/desktop/issues/3380 | None | None |
-| SideBar Compact mode bug | https://github.com/zen-browser/desktop/issues/3379 | None | None |
+| SideBar Compact mode bug | https://github.com/zen-browser/desktop/issues/3379 | None | 38 days, 2:46:44 |
| Joining Microsoft Teams call crashes browser tabs | https://github.com/zen-browser/desktop/issues/3378 | 11 days, 22:11:35 | None |
| can not quit browser by ctrl + q | https://github.com/zen-browser/desktop/issues/3377 | 2 days, 8:01:56 | None |
-| RIME input method cannot input information | https://github.com/zen-browser/desktop/issues/3374 | None | None |
+| RIME input method cannot input information | https://github.com/zen-browser/desktop/issues/3374 | None | 38 days, 9:36:51 |
| Workspace behaves badly when switching between them with touchpad | https://github.com/zen-browser/desktop/issues/3373 | None | None |
-| the z-index of both compact modes are messed up | https://github.com/zen-browser/desktop/issues/3371 | None | None |
+| the z-index of both compact modes are messed up | https://github.com/zen-browser/desktop/issues/3371 | None | 37 days, 16:11:05 |
| Open/close sidebar icon is confusing | https://github.com/zen-browser/desktop/issues/3370 | None | None |
-| Tab Management - Workspaces - Display workspaces as an icon strip bug | https://github.com/zen-browser/desktop/issues/3369 | None | None |
-| Google meet not loading | https://github.com/zen-browser/desktop/issues/3368 | 16:55:47 | None |
+| Tab Management - Workspaces - Display workspaces as an icon strip bug | https://github.com/zen-browser/desktop/issues/3369 | None | 37 days, 16:55:24 |
+| Google meet not loading | https://github.com/zen-browser/desktop/issues/3368 | 16:55:47 | 38 days, 16:59:55 |
| Workspace icon highlight on hover is uneven | https://github.com/zen-browser/desktop/issues/3367 | 30 days, 0:55:16 | 30 days, 0:55:16 |
-| The main UI's background doesn't follow the theme sometimes | https://github.com/zen-browser/desktop/issues/3366 | None | None |
-| Shortcut Settings Typo | https://github.com/zen-browser/desktop/issues/3365 | None | None |
+| The main UI's background doesn't follow the theme sometimes | https://github.com/zen-browser/desktop/issues/3366 | None | 37 days, 19:55:37 |
+| Shortcut Settings Typo | https://github.com/zen-browser/desktop/issues/3365 | 34 days, 13:56:04 | 34 days, 16:38:11 |
| Video image freezes when we move the progress video bar, and only the audio is playing | https://github.com/zen-browser/desktop/issues/3363 | 31 days, 7:22:57 | None |
-| ClickTrades Webpage not loading | https://github.com/zen-browser/desktop/issues/3362 | None | None |
+| ClickTrades Webpage not loading | https://github.com/zen-browser/desktop/issues/3362 | None | 38 days, 0:31:43 |
| When switch workspaces, the current-focused tab sometimes also show in the new workspace | https://github.com/zen-browser/desktop/issues/3357 | 3:34:51 | 9 days, 17:41:38 |
| Toggle Floating Sidebar keyboard shortcut not working | https://github.com/zen-browser/desktop/issues/3356 | 6 days, 13:00:18 | None |
| Workspace icon still shows on compact mode even when flag is disabled. | https://github.com/zen-browser/desktop/issues/3355 | 20 days, 3:42:54 | 20 days, 3:42:39 |
| Moving tab across workspace and then right-clicking on it does not render a menu | https://github.com/zen-browser/desktop/issues/3353 | 2 days, 14:33:51 | 18 days, 16:47:56 |
| Glance in compact view opens tabs bar | https://github.com/zen-browser/desktop/issues/3351 | 31 days, 13:55:54 | 33 days, 15:15:20 |
-| Dev Tools Context Menus Broken | https://github.com/zen-browser/desktop/issues/3350 | None | None |
-| Stuck in toolbar customizing mode | https://github.com/zen-browser/desktop/issues/3349 | None | None |
+| Dev Tools Context Menus Broken | https://github.com/zen-browser/desktop/issues/3350 | None | 38 days, 15:57:10 |
+| Stuck in toolbar customizing mode | https://github.com/zen-browser/desktop/issues/3349 | None | 37 days, 16:08:33 |
| Top Bar Padding Increasing in Compact Mode (Twilight) | https://github.com/zen-browser/desktop/issues/3348 | 9:32:10 | 6 days, 22:39:00 |
-| Not using QUIC/http3 | https://github.com/zen-browser/desktop/issues/3346 | None | None |
-| Unable to go to zen-browser.app when using zen-browser, i can go to the website using other browsers | https://github.com/zen-browser/desktop/issues/3345 | None | None |
-| UI Error | https://github.com/zen-browser/desktop/issues/3342 | None | None |
-| Zen Mod - Sorting issue | https://github.com/zen-browser/desktop/issues/3341 | None | None |
+| Not using QUIC/http3 | https://github.com/zen-browser/desktop/issues/3346 | None | 37 days, 22:07:48 |
+| Unable to go to zen-browser.app when using zen-browser, i can go to the website using other browsers | https://github.com/zen-browser/desktop/issues/3345 | None | 39 days, 23:44:19 |
+| UI Error | https://github.com/zen-browser/desktop/issues/3342 | None | 38 days, 1:42:45 |
+| Zen Mod - Sorting issue | https://github.com/zen-browser/desktop/issues/3341 | 36 days, 18:11:48 | 36 days, 18:11:48 |
| YouTube Viewport Issue | https://github.com/zen-browser/desktop/issues/3340 | None | 19:39:03 |
-| Discord capcha and Cloudflare turnstile cannot verify human access | https://github.com/zen-browser/desktop/issues/3339 | None | None |
+| Discord capcha and Cloudflare turnstile cannot verify human access | https://github.com/zen-browser/desktop/issues/3339 | None | 38 days, 13:52:12 |
| `null` in toolbar won't disappear | https://github.com/zen-browser/desktop/issues/3337 | 12:31:11 | 10 days, 11:19:00 |
| Zen Browser Unusable and Stuck on Zen Logo After Changing Keyboard Shortcuts (Issue Across Multiple Platforms) | https://github.com/zen-browser/desktop/issues/3335 | 31 days, 15:12:01 | None |
| Slight translation error for Swedish | https://github.com/zen-browser/desktop/issues/3334 | None | None |
-| GIVE ME SOMETHING TO GRAB ONTO SO I CAN DRAG WINDOWS AROUND!!! | https://github.com/zen-browser/desktop/issues/3333 | 1:31:36 | None |
+| GIVE ME SOMETHING TO GRAB ONTO SO I CAN DRAG WINDOWS AROUND!!! | https://github.com/zen-browser/desktop/issues/3333 | 1:31:36 | 41 days, 20:29:07 |
| GsConnect Not working | https://github.com/zen-browser/desktop/issues/3332 | 2:36:46 | 4:34:49 |
| Fullscreening a tab still has borders on the right and bottom of the browser. | https://github.com/zen-browser/desktop/issues/3331 | None | 1:22:12 |
| Drag-and-Drop Feature | https://github.com/zen-browser/desktop/issues/3329 | 3:02:05 | None |
-| Unresponsive or crashing when different profile is opened | https://github.com/zen-browser/desktop/issues/3327 | None | None |
+| Unresponsive or crashing when different profile is opened | https://github.com/zen-browser/desktop/issues/3327 | None | 38 days, 11:27:50 |
| Some issues with sync between multiple windows | https://github.com/zen-browser/desktop/issues/3326 | None | 32 days, 4:09:34 |
| Google Sign in Stuck | https://github.com/zen-browser/desktop/issues/3325 | None | 10 days, 1:50:46 |
-| SSL reporting as Unknown Issuer when using a valid certificate | https://github.com/zen-browser/desktop/issues/3324 | None | None |
+| SSL reporting as Unknown Issuer when using a valid certificate | https://github.com/zen-browser/desktop/issues/3324 | None | 38 days, 12:53:11 |
| Google Classroom acting funky | https://github.com/zen-browser/desktop/issues/3323 | 13:22:38 | 17:26:18 |
_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_
diff --git a/flatpak/io.github.zen_browser.zen.yml.template b/flatpak/app.zen_browser.zen.template
similarity index 59%
rename from flatpak/io.github.zen_browser.zen.yml.template
rename to flatpak/app.zen_browser.zen.template
index 845498708..e3a70df79 100644
--- a/flatpak/io.github.zen_browser.zen.yml.template
+++ b/flatpak/app.zen_browser.zen.template
@@ -1,4 +1,4 @@
-app-id: io.github.zen_browser.zen
+app-id: app.zen_browser.zen
runtime: org.freedesktop.Platform
runtime-version: '24.08'
sdk: org.freedesktop.Sdk
@@ -28,7 +28,7 @@ finish-args:
- --system-talk-name=org.freedesktop.NetworkManager
- --talk-name=org.a11y.Bus
- --env=GTK_PATH=/app/lib/gtkmodules
- - --env=MESA_SHADER_CACHE_DIR=$XDG_RUNTIME_DIR/app/$FLATPAK_ID/cache/mesa_shader_cache_db
+ - --env=MESA_SHADER_CACHE_DIR=$XDG_RUNTIME_DIR/app/$FLATPAK_ID/cache/mesa_shader_cache_db
modules:
- name: zen_browser
buildsystem: simple
@@ -36,20 +36,29 @@ modules:
- mv zen /app/
- mkdir -p /app/lib/ffmpeg
- - install -Dm0755 metadata/launch-script.sh ${{FLATPAK_DEST}}/bin/launch-script.sh
- - install -Dm0644 metadata/policies.json ${{FLATPAK_DEST}}/bin/distribution/policies.json
- - install -Dm0644 metadata/icons/io.github.zen_browser.zen.svg ${{FLATPAK_DEST}}/share/icons/hicolor/scalable/apps/${{FLATPAK_ID}}.svg
- - install -Dm0644 metadata/io.github.zen_browser.zen.metainfo.xml ${{FLATPAK_DEST}}/share/metainfo/${{FLATPAK_ID}}.metainfo.xml
- - install -Dm0644 metadata/io.github.zen_browser.zen.desktop ${{FLATPAK_DEST}}/share/applications/${{FLATPAK_ID}}.desktop
+ - install -Dm0755 metadata/launch-script.sh ${FLATPAK_DEST}/bin/launch-script.sh
+ - install -Dm0644 metadata/policies.json ${FLATPAK_DEST}/bin/distribution/policies.json
+ - install -Dm0644 metadata/icons/${FLATPAK_ID}.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg
+ - install -Dm0644 metadata/${FLATPAK_ID}.metainfo.xml ${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml
+ - install -Dm0644 metadata/${FLATPAK_ID}.desktop ${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
sources:
- type: archive
- url: https://github.com/zen-browser/desktop/releases/download/{version}/zen.linux-x86_64.tar.bz2
+ url: https://github.com/zen-browser/desktop/releases/download/1.6b/zen.linux-x86_64.tar.bz2
sha256: {linux_sha256}
strip-components: 0
+ only-arches:
+ - x86_64
- type: archive
- url: https://github.com/zen-browser/flatpak/releases/download/{version}/archive.tar
+ url: https://github.com/zen-browser/desktop/releases/download/1.6b/zen.linux-aarch64.tar.bz2
+ sha256: {linux_aarch64_sha256}
+ strip-components: 0
+ only-arches:
+ - aarch64
+
+ - type: archive
+ url: https://github.com/zen-browser/flatpak/releases/download/1.6b/archive.tar
sha256: {flatpak_sha256}
strip-components: 0
dest: metadata
diff --git a/package.json b/package.json
index 4b54c05d6..fcc0defec 100644
--- a/package.json
+++ b/package.json
@@ -12,14 +12,17 @@
"import": "surfer import",
"export": "surfer export",
"init": "npm run bootstrap && npm run import",
- "bootstrap": "surfer download && surfer bootstrap",
+ "download": "surfer download",
+ "bootstrap": "surfer bootstrap && surfer bootstrap",
"package": "surfer package",
"update-ff": "python3 scripts/update_ff.py",
"update-ff:raw": "surfer update",
"update-newtab": "python3 scripts/update_newtab.py",
- "pretty": "prettier . --write",
- "lint": "npx prettier . --check",
- "prepare": "husky"
+ "update-ff:rc": "python3 scripts/update_ff.py --rc",
+ "pretty": "prettier . --write && autopep8 -r --in-place scripts/ src/",
+ "lint": "npx prettier . --check && autopep8 --diff scripts/ src/",
+ "prepare": "husky",
+ "reset-ff": "surfer reset"
},
"repository": {
"type": "git",
@@ -36,7 +39,7 @@
},
"homepage": "https://github.com/zen-browser/core#readme",
"dependencies": {
- "@zen-browser/surfer": "^1.9.6"
+ "@zen-browser/surfer": "^1.9.8"
},
"devDependencies": {
"husky": "^9.1.7",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8d98b70ea..c4a6d2148 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,8 +9,8 @@ importers:
.:
dependencies:
'@zen-browser/surfer':
- specifier: ^1.9.6
- version: 1.9.6(glob@7.2.3)
+ specifier: ^1.9.8
+ version: 1.9.8(glob@7.2.3)
devDependencies:
husky:
specifier: ^9.1.7
@@ -122,8 +122,8 @@ packages:
'@types/node@17.0.45':
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
- '@zen-browser/surfer@1.9.6':
- resolution: {integrity: sha512-UdtxmgQ+ft03cD8YMqRMwrXf3Lj1OUN1vWPcZxJYLnI41N2UXGlpiOg7U/q4pEC/YNRkJnywiqAaCRJ1n1J8ZQ==}
+ '@zen-browser/surfer@1.9.8':
+ resolution: {integrity: sha512-dx4ouPDB7bg+eWR+NNuhdroR0JyZ+bWlxpAFskl9TiR3cvr1qtmZG9bYSF27nLNOUSWgKNamjevP785B08Cxjg==}
hasBin: true
ansi-escapes@7.0.0:
@@ -877,8 +877,8 @@ packages:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
- tar-fs@2.1.1:
- resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
+ tar-fs@2.1.2:
+ resolution: {integrity: sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==}
tar-iterator@1.2.9:
resolution: {integrity: sha512-sHgC+mJjn5n4v1YDQsFyGUg8Up6byOWV8QjQh/ZOYSOVkmg5q9ivpTgIKJrQOUihivsh+4pQlY5gMmXyH4AyOg==}
@@ -1036,7 +1036,7 @@ snapshots:
'@types/node@17.0.45': {}
- '@zen-browser/surfer@1.9.6(glob@7.2.3)':
+ '@zen-browser/surfer@1.9.8(glob@7.2.3)':
dependencies:
'@resvg/resvg-js': 1.4.0
async-icns: 1.0.2
@@ -1606,7 +1606,7 @@ snapshots:
pump: 3.0.2
rc: 1.2.8
simple-get: 4.0.1
- tar-fs: 2.1.1
+ tar-fs: 2.1.2
tunnel-agent: 0.6.0
prettier-plugin-sh@0.14.0(prettier@3.4.2):
@@ -1705,7 +1705,7 @@ snapshots:
prebuild-install: 7.1.2
semver: 7.6.3
simple-get: 4.0.1
- tar-fs: 2.1.1
+ tar-fs: 2.1.2
tunnel-agent: 0.6.0
shebang-command@2.0.0:
@@ -1801,7 +1801,7 @@ snapshots:
dependencies:
has-flag: 4.0.0
- tar-fs@2.1.1:
+ tar-fs@2.1.2:
dependencies:
chownr: 1.1.4
mkdirp-classic: 0.5.3
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..297575f00
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,5 @@
+[tool.autopep8]
+max_line_length = 120
+recursive = true
+aggressive = 3
+indent_size = 2
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..9601d74fe
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,7 @@
+autopep8==2.3.1
+click==8.1.8
+mypy-extensions==1.0.0
+packaging==24.2
+pathspec==0.12.1
+platformdirs==4.3.6
+pycodestyle==2.12.1
diff --git a/scripts/check-rc-response.py b/scripts/check-rc-response.py
deleted file mode 100644
index cf7994951..000000000
--- a/scripts/check-rc-response.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import json
-import sys
-import os
-import requests
-
-RESPONSE = 'rc-response.json'
-METADATA = 'surfer.json'
-
-def get_current_version():
- with open(METADATA) as f:
- metadata = json.load(f)
- return metadata['version']['candidate']
-
-def get_rc_response():
- with open(RESPONSE) as f:
- data = json.load(f)
- for tag_dict in data['tags']:
- tag = tag_dict['tag']
- is_valid_tag = (tag.startswith('FIREFOX') and tag.endswith('_BUILD1')
- and not 'ESR' in tag and not 'b' in tag)
- if is_valid_tag:
- return tag.replace('FIREFOX_', '').replace('_BUILD1', '').replace('_', '.')
- return None
-
-def get_pings():
- pings = ""
- for ping in os.getenv('DISCORD_PING_IDS').split(','):
- pings += "<@%s> " % ping
- return pings
-
-def send_webhook(rc: str):
- text = "||%s|| New Firefox RC version is available: **%s**" % (get_pings(), rc)
- webhook_url = os.getenv('DISCORD_WEBHOOK_URL') #os.getenv('DISCORD_WEBHOOK_URL')
- message = {
- "content": text,
- "username": "Firefox RC Checker",
- "avatar_url": "https://avatars.githubusercontent.com/u/189789277?v=4",
- }
- response = requests.post(webhook_url, json=message)
- if response.status_code == 204:
- print("Message sent successfully!")
- else:
- print(f"Failed to send message: {response.status_code}")
-
-def main():
- current = get_current_version()
- if not current:
- print('Could not find current version')
- return 1
- rc = get_rc_response()
- if not rc:
- print('Could not find RC version')
- return 1
- if current != rc:
- print('Current version is %s, but RC version is %s' % (current, rc))
- # Here, we should update the current version in surfer.json
- send_webhook(rc)
- return 0
- print('Current version is %s, and RC version is %s' % (current, rc))
- return 1
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/scripts/check_rc_response.py b/scripts/check_rc_response.py
new file mode 100644
index 000000000..9ac847d7e
--- /dev/null
+++ b/scripts/check_rc_response.py
@@ -0,0 +1,92 @@
+import json
+import os
+import sys
+import requests
+from typing import Optional
+
+METADATA_FILENAME = "surfer.json"
+TAGS_API_URL = "https://hg.mozilla.org/releases/mozilla-release/json-tags"
+
+
+def get_current_version() -> Optional[str]:
+ """Retrieve the current version from the metadata file."""
+ try:
+ with open(METADATA_FILENAME) as f:
+ metadata = json.load(f)
+ return metadata["version"]["candidate"]
+ except (FileNotFoundError, json.JSONDecodeError) as e:
+ print(f"Error reading current version: {e}")
+ return None
+
+
+def get_repo_data() -> Optional[str]:
+ """Retrieve the repository data from the API."""
+ try:
+ print(f"Retrieving repository data from {TAGS_API_URL}")
+ response = requests.get(TAGS_API_URL)
+ response.raise_for_status() # Raise an error for bad responses
+ return response.json()
+ except requests.RequestException as e:
+ print(f"Error retrieving repository data: {e}")
+ return None
+
+
+def get_rc_response() -> Optional[str]:
+ """Get the release candidate response from the response file."""
+ try:
+ data = get_repo_data()
+ for tag_dict in data["tags"]:
+ tag = tag_dict["tag"]
+ if (tag.startswith("FIREFOX") and tag.endswith("_BUILD1")
+ and "ESR" not in tag and "b" not in tag):
+ return (tag.replace("FIREFOX_", "").replace("_BUILD1",
+ "").replace("_", "."))
+ except (FileNotFoundError, json.JSONDecodeError) as e:
+ print(f"Error reading RC response: {e}")
+ return None
+
+
+def get_pings() -> str:
+ """Build a string of Discord user IDs for mentions."""
+ ping_ids = os.getenv("DISCORD_PING_IDS", "")
+ return " ".join(f"<@{ping.strip()}>" for ping in ping_ids.split(",")
+ if ping.strip())
+
+
+def send_webhook(rc: str) -> None:
+ """Send a message to the Discord webhook."""
+ text = f"||{get_pings()}|| New Firefox RC version is available: **{rc}**"
+ webhook_url = os.getenv("DISCORD_WEBHOOK_URL")
+
+ if webhook_url:
+ message = {
+ "content": text,
+ "username": "Firefox RC Checker",
+ }
+ try:
+ response = requests.post(webhook_url, json=message)
+ response.raise_for_status() # Raise an error for bad responses
+ except requests.RequestException as e:
+ print(f"Error sending webhook: {e}")
+ else:
+ print("Webhook URL not set.")
+
+
+def rc_should_be_updated(rc_response: str, current_version: str) -> bool:
+ return rc_response and rc_response != current_version
+
+
+def main() -> int:
+ current_version = get_current_version()
+ rc_response = get_rc_response()
+
+ if rc_should_be_updated(rc_response, current_version):
+ send_webhook(rc_response)
+ return 0
+
+ print(f"Current version: {current_version}, RC version: {rc_response}")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/scripts/copy-language-pack.sh b/scripts/copy-language-pack.sh
deleted file mode 100644
index 7215efd5d..000000000
--- a/scripts/copy-language-pack.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-browser_locales=engine/browser/locales
-
-copy_browser_locales() {
- langId=$1
- mkdir -p $browser_locales/$langId
- if [ "$langId" = "en-US" ]; then
- find $browser_locales/$langId -type f -name "zen*" -delete
- rsync -av --exclude=.git ./l10n/en-US/browser/ $browser_locales/$langId/
- return
- fi
- rm -rf $browser_locales/$langId/
- # TODO: Copy the rest of the l10n directories to their respective locations
- rsync -av --exclude=.git ./l10n/$langId/ $browser_locales/$langId/
-}
-
-LANG=$1
-echo "Copying language pack for $LANG"
-copy_browser_locales $LANG
diff --git a/scripts/copy_language_pack.py b/scripts/copy_language_pack.py
new file mode 100644
index 000000000..d03b801d5
--- /dev/null
+++ b/scripts/copy_language_pack.py
@@ -0,0 +1,78 @@
+import os
+import shutil
+import sys
+
+# Define the path for browser locales
+BROWSER_LOCALES = "engine/browser/locales"
+
+
+def copy_browser_locales(lang_id: str):
+ """
+ Copies language pack files to the specified browser locale directory.
+
+ :param lang_id: Language identifier (e.g., 'en-US', 'fr', etc.)
+ """
+ lang_path = os.path.join(BROWSER_LOCALES, lang_id)
+
+ # Create the directory for the language pack if it doesn't exist
+ os.makedirs(lang_path, exist_ok=True)
+ print(f"Creating directory: {lang_path}")
+
+ # If the language is 'en-US', handle special processing
+ if lang_id == "en-US":
+ # Remove files starting with "zen" in the 'en-US' directory
+ for root, _, files in os.walk(lang_path):
+ for file in files:
+ if file.startswith("zen"):
+ os.remove(os.path.join(root, file))
+
+ # Copy files from the source directory
+ source_path = "./l10n/en-US/browser/"
+ copy_files(source_path, lang_path)
+ return
+
+ # For other languages, delete the existing directory and copy files anew
+ if os.path.exists(lang_path):
+ shutil.rmtree(lang_path) # Remove existing directory
+
+ source_path = f"./l10n/{lang_id}/"
+ copy_files(source_path, lang_path)
+
+
+def copy_files(source: str, destination: str):
+ """
+ Copies files and directories from the source to the destination.
+
+ :param source: Source directory path
+ :param destination: Destination directory path
+ """
+ if not os.path.exists(source):
+ raise FileNotFoundError(f"Source path '{source}' does not exist.")
+
+ # Recursively copy all files and directories
+ for root, dirs, files in os.walk(source):
+ # Determine relative path to preserve directory structure
+ relative_path = os.path.relpath(root, source)
+ destination_root = os.path.join(destination, relative_path)
+ os.makedirs(destination_root, exist_ok=True)
+
+ # Copy files
+ for file in files:
+ src_file = os.path.join(root, file)
+ dest_file = os.path.join(destination_root, file)
+ print(f"\tCopying {src_file} to {dest_file}")
+ shutil.copy2(src_file, dest_file) # Copy file with metadata
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print("Usage: python script.py ")
+ sys.exit(1)
+
+ lang = sys.argv[1]
+ print(f"Copying language pack for {lang}")
+ try:
+ copy_browser_locales(lang)
+ except Exception as e:
+ print(f"Error: {e}")
+ sys.exit(1)
diff --git a/scripts/download-language-packs.sh b/scripts/download-language-packs.sh
index adff0978f..f59e96312 100644
--- a/scripts/download-language-packs.sh
+++ b/scripts/download-language-packs.sh
@@ -31,9 +31,9 @@ cd $CURRENT_DIR
# Move all the files to the correct location
-sh scripts/copy-language-pack.sh en-US
+python3 scripts/copy_language_pack.py en-US
for lang in $(cat ./l10n/supported-languages); do
- sh scripts/copy-language-pack.sh $lang
+ python3 scripts/copy_language_pack.py $lang
done
wait
diff --git a/scripts/prepare-flatpak-release.py b/scripts/prepare-flatpak-release.py
index 9deec6319..adc15eb4d 100644
--- a/scripts/prepare-flatpak-release.py
+++ b/scripts/prepare-flatpak-release.py
@@ -1,52 +1,75 @@
import hashlib
import argparse
import sys
+import os
-FLATID = "io.github.zen_browser.zen"
+FLATID = "app.zen_browser.zen"
-def get_sha256sum(filename):
- """Calculate the SHA256 checksum of a file."""
- sha256 = hashlib.sha256()
+
+def get_sha256sum(filename):
+ """Calculate the SHA256 checksum of a file."""
+ sha256 = hashlib.sha256()
+ try:
with open(filename, "rb") as f:
- for byte_block in iter(lambda: f.read(4096), b""):
- sha256.update(byte_block)
- return sha256.hexdigest()
+ for byte_block in iter(lambda: f.read(4096), b""):
+ sha256.update(byte_block)
+ except FileNotFoundError:
+ print(f"File {filename} not found.")
+ sys.exit(1)
+ return sha256.hexdigest()
+
+
+def build_template(template, linux_sha256, flatpak_sha256, version, linux_aarch64_sha256):
+ """Build the template with the provided hashes and version."""
+ print(f"Building template with version {version}")
+ print(f"\tLinux archive sha256: {linux_sha256}")
+ print(f"\tLinux aarch64 archive sha256: {linux_aarch64_sha256}")
+ print(f"\tFlatpak archive sha256: {flatpak_sha256}")
+ return template.format(linux_sha256=linux_sha256,
+ flatpak_sha256=flatpak_sha256,
+ version=version,
+ linux_aarch64_sha256=linux_aarch64_sha256)
-def build_template(template, linux_sha256, flatpak_sha256, version):
- print(f"Building template with version {version}")
- print(f"\tLinux archive sha256: {linux_sha256}")
- print(f"\tFlatpak archive sha256: {flatpak_sha256}")
- return template.format(linux_sha256=linux_sha256,
- flatpak_sha256=flatpak_sha256,
- version=version)
def get_template(template_root):
- file = f"{template_root}/{FLATID}.yml.template"
- print(f"Reading template {file}")
- try:
- with open(file, "r") as f:
- return f.read()
- except FileNotFoundError:
- print(f"Template {file} not found")
- sys.exit(1)
+ """Get the template content from the specified root directory."""
+ file = os.path.join(template_root, f"{FLATID}.yml.template")
+ print(f"Reading template {file}")
+ try:
+ with open(file, "r") as f:
+ return f.read()
+ except FileNotFoundError:
+ print(f"Template {file} not found.")
+ sys.exit(1)
+
def main():
- parser = argparse.ArgumentParser(description='Prepare flatpak release')
- parser.add_argument('--version', help='Version of the release', required=True)
- parser.add_argument('--linux-archive', help='Linux archive', required=True)
- parser.add_argument('--flatpak-archive', help='Flatpak archive', required=True)
- parser.add_argument('--output', help='Output file', default=f"{FLATID}.yml")
- parser.add_argument('--template-root', help='Template root', default="flatpak")
- args = parser.parse_args()
+ """Main function to parse arguments and process files."""
+ parser = argparse.ArgumentParser(description="Prepare flatpak release")
+ parser.add_argument("--version",
+ help="Version of the release",
+ required=True)
+ parser.add_argument("--linux-archive", help="Linux archive", required=True)
+ parser.add_argument("--linux-aarch64-archive", help="Linux aarch64 archive", required=True)
+ parser.add_argument("--flatpak-archive",
+ help="Flatpak archive",
+ required=True)
+ parser.add_argument("--output", help="Output file", default=f"{FLATID}.yml")
+ parser.add_argument("--template-root",
+ help="Template root",
+ default="flatpak")
+ args = parser.parse_args()
- linux_sha256 = get_sha256sum(args.linux_archive)
- flatpak_sha256 = get_sha256sum(args.flatpak_archive)
- template = build_template(get_template(args.template_root), linux_sha256, flatpak_sha256, args.version)
+ linux_sha256 = get_sha256sum(args.linux_archive)
+ linux_aarch64_sha256 = get_sha256sum(args.linux_aarch64_archive)
+ flatpak_sha256 = get_sha256sum(args.flatpak_archive)
+ template = build_template(get_template(args.template_root), linux_sha256,
+ flatpak_sha256, args.version, linux_aarch64_sha256)
+
+ print(f"Writing output to {args.output}")
+ with open(args.output, "w") as f:
+ f.write(template)
- print(f"Writing output to {args.output}")
- with open(args.output, "w") as f:
- f.write(template)
if __name__ == "__main__":
- main()
-
+ main()
diff --git a/scripts/remove-failed-jobs.sh b/scripts/remove-failed-jobs.sh
index 925b73197..b168405e2 100644
--- a/scripts/remove-failed-jobs.sh
+++ b/scripts/remove-failed-jobs.sh
@@ -1,5 +1,7 @@
+#!/bin/bash
+
gh_bulk_delete_workflow_runs() {
- repo=$1
+ local repo=$1
# Ensure the repo argument is provided
if [[ -z "$repo" ]]; then
@@ -7,14 +9,37 @@ gh_bulk_delete_workflow_runs() {
return 1
fi
- runs=$(gh api repos/$repo/actions/runs --paginate | jq -r '.workflow_runs[] | select(.conclusion == "cancelled" or .conclusion == "failure" or .conclusion == "timed_out") | .id')
+ # Fetch workflow runs that are cancelled, failed, or timed out
+ local runs
+ runs=$(gh api repos/$repo/actions/runs --paginate \
+ | jq -r '.workflow_runs[] |
+ select(.conclusion == "cancelled" or
+ .conclusion == "failure" or
+ .conclusion == "timed_out") |
+ .id')
+ if [[ -z "$runs" ]]; then
+ echo "No workflow runs found for $repo with the specified conclusions."
+ return 0
+ fi
+
+ # Loop through each run and delete it
while IFS= read -r run; do
- echo "Deleting run https://github.com/$repo/actions/runs/$run"
- gh api -X DELETE repos/$repo/actions/runs/$run --silent
+ echo "Attempting to delete run: https://github.com/$repo/actions/runs/$run"
+
+ # Perform the deletion
+ if gh api -X DELETE repos/$repo/actions/runs/$run --silent; then
+ echo "Successfully deleted run: $run"
+ else
+ echo "Error deleting run: $run" >&2
+ fi
+
+ # Optional delay to avoid hitting API rate limits
+ sleep 1
done <<< "$runs"
- echo "All workflow runs for $repo have been deleted."
+ echo "Completed deletion process for workflow runs in $repo."
}
-gh_bulk_delete_workflow_runs $1
+# Execute the function with the provided argument
+gh_bulk_delete_workflow_runs "$1"
diff --git a/scripts/update-en-US-packs.sh b/scripts/update-en-US-packs.sh
deleted file mode 100644
index 7e37a09ad..000000000
--- a/scripts/update-en-US-packs.sh
+++ /dev/null
@@ -1 +0,0 @@
-sh ./scripts/copy-language-pack.sh en-US
diff --git a/scripts/update_en_US_packs.py b/scripts/update_en_US_packs.py
new file mode 100644
index 000000000..b6ea4b80d
--- /dev/null
+++ b/scripts/update_en_US_packs.py
@@ -0,0 +1,4 @@
+from copy_language_pack import copy_browser_locales
+
+if __name__ == "__main__":
+ copy_browser_locales("en-US")
diff --git a/scripts/update_ff.py b/scripts/update_ff.py
index ff124e587..2cbe1f358 100644
--- a/scripts/update_ff.py
+++ b/scripts/update_ff.py
@@ -1,37 +1,80 @@
-
import os
import json
+import argparse
+import shutil
-last_version = "0.0.0"
-new_version = "0.0.0"
+from check_rc_response import get_rc_response, rc_should_be_updated
-def update_ff():
- os.system("npm run update-ff:raw")
-def get_version_before():
- global last_version
- with open("surfer.json", "r") as f:
- data = json.load(f)
- last_version = data["version"]["version"]
+def update_rc(last_version: str):
+ rc_version = get_rc_response()
+ if rc_should_be_updated(rc_version, last_version):
+ print(f"New Firefox RC version is available: {rc_version}")
+ print("Removing engine directory and updating surfer.json.")
+ if os.path.exists("engine"):
+ shutil.rmtree("engine")
+ with open("surfer.json", "r") as f:
+ data = json.load(f)
+ with open("surfer.json", "w") as f:
+ data["version"]["candidate"] = rc_version
+ json.dump(data, f, indent=2)
+ print("Download the new engine by running 'pnpm download'.")
+ os.system("pnpm download")
+ else:
+ print("No new Firefox RC version available.")
-def get_version_after():
- global new_version
- with open("surfer.json", "r") as f:
- data = json.load(f)
- new_version = data["version"]["version"]
-def update_readme():
- global last_version
- global new_version
- with open("README.md", "r") as f:
- data = f.read()
- data = data.replace(last_version, new_version)
- with open("README.md", "w") as f:
- f.write(data)
+def update_ff(is_rc: bool = False, last_version: str = ""):
+ """Runs the npm command to update the 'ff' component."""
+ if is_rc:
+ return update_rc(last_version)
+ result = os.system("pnpm update-ff:raw")
+ if result != 0:
+ raise RuntimeError("Failed to update 'ff' component.")
+
+
+def get_version_from_file(filename, is_rc):
+ """Retrieves the version from the specified JSON file."""
+ try:
+ with open(filename, "r") as f:
+ data = json.load(f)
+ return data["version"]["version"] if not is_rc else data["version"]["candidate"]
+ except (FileNotFoundError, json.JSONDecodeError) as e:
+ raise RuntimeError(f"Error reading version from {filename}: {e}")
+
+
+def update_readme(last_version, new_version, is_rc=False):
+ """Updates the README.md file to reflect the new version."""
+ prefix = "RC " if is_rc else "`"
+ try:
+ with open("README.md", "r") as f:
+ data = f.read()
+ updated_data = data.replace(prefix + last_version, prefix + new_version)
+
+ with open("README.md", "w") as f:
+ f.write(updated_data)
+ except FileNotFoundError as e:
+ raise RuntimeError(f"README.md file not found: {e}")
+
+
+def main():
+ """Main function to update versions and README."""
+
+ arg_parser = argparse.ArgumentParser()
+ arg_parser.add_argument(
+ "--rc", help="Indicates that this is a release candidate.", default=False, action="store_true")
+ args = arg_parser.parse_args()
+
+ try:
+ last_version = get_version_from_file("surfer.json", args.rc)
+ update_ff(args.rc, last_version)
+ new_version = get_version_from_file("surfer.json", args.rc)
+ update_readme(last_version, new_version, args.rc)
+ print(
+ f"Updated version from {last_version} to {new_version} in README.md.")
+ except Exception as e:
+ print(f"An error occurred: {e}")
+
if __name__ == "__main__":
- get_version_before()
- update_ff()
- get_version_after()
- update_readme()
- print("Updated from version {} to version {}".format(last_version, new_version))
+ main()
diff --git a/scripts/update_newtab.py b/scripts/update_newtab.py
index e98a8ec6e..0a8b4aeb1 100644
--- a/scripts/update_newtab.py
+++ b/scripts/update_newtab.py
@@ -1,9 +1,41 @@
import os
+import subprocess
+import logging
+
+# Set up logging
+logging.basicConfig(level=logging.INFO)
+
+# Constants for paths
+NEW_TAB_DIR = "./engine/browser/components/newtab"
+ENGINE_DIR = "./engine"
+NPM_INSTALL_COMMANDS = ["npm install", "npm install meow@9.0.0"]
+BUNDLE_COMMAND = "npm run bundle --prefix=browser/components/newtab"
+
+
+def install_dependencies():
+ """Install necessary npm packages for the newtab component."""
+ for command in NPM_INSTALL_COMMANDS:
+ logging.info(f"Running command: {command} in {NEW_TAB_DIR}")
+ subprocess.run(command.split(), cwd=NEW_TAB_DIR, check=True)
+
+
+def bundle_newtab_components():
+ """Bundle the newtab components."""
+ logging.info(f"Bundling newtab components in {ENGINE_DIR}")
+ subprocess.run(BUNDLE_COMMAND.split(), cwd=ENGINE_DIR, check=True)
+
def update_newtab(init: bool = True):
- if init:
- os.system("(cd ./engine/browser/components/newtab && ../../../mach npm install && ../../../mach npm install meow@9.0.0)")
- os.system("cd ./engine && ./mach npm run bundle --prefix=browser/components/newtab")
+ """Update the newtab components, optionally initializing dependencies."""
+ try:
+ if init:
+ install_dependencies()
+
+ bundle_newtab_components()
+ except subprocess.CalledProcessError as e:
+ logging.error(f"An error occurred: {e}")
+ raise
+
if __name__ == "__main__":
- update_newtab(False)
+ update_newtab(init=False)
diff --git a/src/browser/app/profile/zen-browser.js b/src/browser/app/profile/zen-browser.js
index 3dcd11da4..aeb920237 100644
--- a/src/browser/app/profile/zen-browser.js
+++ b/src/browser/app/profile/zen-browser.js
@@ -85,6 +85,7 @@ pref('zen.theme.accent-color', "#ffb787");
pref('zen.theme.content-element-separation', 6); // In pixels
pref('zen.theme.pill-button', false);
pref('zen.theme.gradient', true);
+pref('zen.theme.essentials-favicon-bg', true);
pref('zen.tabs.show-newtab-vertical', true);
pref('zen.view.show-newtab-button-border-top', true);
@@ -187,7 +188,7 @@ pref('zen.splitView.change-on-hover', false);
pref('zen.splitView.rearrange-hover-size', 24);
// Startup flags
-pref('zen.startup.smooth-scroll-in-tabs', true);
+pref('zen.startup.smooth-scroll-in-tabs', false);
// Zen Workspaces
pref('zen.workspaces.disabled_for_testing', false);
diff --git a/src/browser/base/content/ZenCustomizableUI.sys.mjs b/src/browser/base/content/ZenCustomizableUI.sys.mjs
index 4cbebcb00..0cedeb0ad 100644
--- a/src/browser/base/content/ZenCustomizableUI.sys.mjs
+++ b/src/browser/base/content/ZenCustomizableUI.sys.mjs
@@ -1,3 +1,5 @@
+import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
+
export var ZenCustomizableUI = new (class {
constructor() {}
@@ -9,7 +11,7 @@ export var ZenCustomizableUI = new (class {
'zen-sidebar-top-buttons',
{
type: this.TYPE_TOOLBAR,
- defaultPlacements: ['zen-sidepanel-button'],
+ defaultPlacements: AppConstants.platform === 'macosx' ? [] : ['zen-sidepanel-button'],
defaultCollapsed: null,
},
true
diff --git a/src/browser/base/content/ZenUIManager.mjs b/src/browser/base/content/ZenUIManager.mjs
index 6e3fbd5d3..80436baeb 100644
--- a/src/browser/base/content/ZenUIManager.mjs
+++ b/src/browser/base/content/ZenUIManager.mjs
@@ -8,6 +8,10 @@ var gZenUIManager = {
XPCOMUtils.defineLazyPreferenceGetter(this, 'sidebarHeightThrottle', 'zen.view.sidebar-height-throttle', 500);
XPCOMUtils.defineLazyPreferenceGetter(this, 'contentElementSeparation', 'zen.theme.content-element-separation', 0);
+ ChromeUtils.defineLazyGetter(this, 'motion', () => {
+ return ChromeUtils.importESModule('chrome://browser/content/zen-vendor/motion.min.mjs', { global: 'current' });
+ });
+
new ResizeObserver(gZenCommonActions.throttle(this.updateTabsToolbar.bind(this), this.sidebarHeightThrottle)).observe(
document.getElementById('tabbrowser-tabs')
);
diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn
index 1db5d1874..11ae40418 100644
--- a/src/browser/base/content/zen-assets.jar.inc.mn
+++ b/src/browser/base/content/zen-assets.jar.inc.mn
@@ -75,3 +75,4 @@
# JS Vendor
content/browser/zen-vendor/tsparticles.confetti.bundle.min.js (content/zen-vendor/tsparticles.confetti.bundle.min.js)
+ content/browser/zen-vendor/motion.min.mjs (content/zen-vendor/motion.min.mjs)
diff --git a/src/browser/base/content/zen-avatars/fetch-all-avatars.py b/src/browser/base/content/zen-avatars/fetch-all-avatars.py
index 8053d54a7..ae6c2f28f 100644
--- a/src/browser/base/content/zen-avatars/fetch-all-avatars.py
+++ b/src/browser/base/content/zen-avatars/fetch-all-avatars.py
@@ -9,12 +9,14 @@ JAR_ENTRY_TEMPLATE = "\tcontent/browser/zen-avatars/{0}\t(content/zen-avatars/{0
URL = "https://source.boringavatars.com/bauhaus/120/${}?colors={}"
COLORS = {
- "dark": ["DDDDDD", "5E9188", "3E5954", "253342", "232226"],
- "light": ["9D9382", "FFC1B2", "FFDBC8", "FFF6C7", "DCD7C2"]
+ "dark": ["DDDDDD", "5E9188", "3E5954", "253342", "232226"],
+ "light": ["9D9382", "FFC1B2", "FFDBC8", "FFF6C7", "DCD7C2"],
}
+
def random_string(length):
- return ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length))
+ return "".join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length))
+
def generate_list_names():
names = []
@@ -22,9 +24,11 @@ def generate_list_names():
names.append(random_string(random.randint(5, 10)))
return names
+
def write_jar_file(jar_file):
with open("jar.inc.mn", "w") as f:
- f.write(jar_file)
+ f.write(jar_file)
+
def fetch_all_avatars():
names = generate_list_names()
@@ -36,15 +40,19 @@ def fetch_all_avatars():
with open(f"avatar-{i}-{theme}.svg", "w") as f:
f.write(response.text)
jar_file += JAR_ENTRY_TEMPLATE.format(f"avatar-{i}-{theme}.svg") + "\n"
- print(f"SUCCESS: Fetched 'avatar-{i}-{theme}.svg' for name '{name}' with theme '{theme}'")
+ print(
+ f"SUCCESS: Fetched 'avatar-{i}-{theme}.svg' for name '{name}' with theme '{theme}'"
+ )
write_jar_file(jar_file)
+
def clear_all_avatars():
for file in os.listdir():
if file.startswith("avatar-") and file.endswith(".svg"):
os.remove(file)
print(f"SUCCESS: Removed '{file}'")
+
def main():
if not os.getcwd().endswith("zen-avatars"):
print("ERROR: Please run this script from the 'zen-avatars' directory")
@@ -52,5 +60,6 @@ def main():
clear_all_avatars()
fetch_all_avatars()
+
if __name__ == "__main__":
main()
diff --git a/src/browser/base/content/zen-styles/zen-animations.css b/src/browser/base/content/zen-styles/zen-animations.css
index 05341ea00..78fa7cfe1 100644
--- a/src/browser/base/content/zen-styles/zen-animations.css
+++ b/src/browser/base/content/zen-styles/zen-animations.css
@@ -100,28 +100,6 @@
}
}
-@keyframes zen-slide-in {
- from {
- transform: translateX(-150%);
- opacity: 0;
- }
- to {
- transform: translateX(0);
- opacity: 1;
- }
-}
-
-@keyframes zen-slide-in-reverse {
- from {
- transform: translateX(150%);
- opacity: 0;
- }
- to {
- transform: translateX(0);
- opacity: 1;
- }
-}
-
@keyframes zen-deck-fadeIn {
0% {
transform: scale(0.9);
@@ -274,38 +252,6 @@
}
}
-@keyframes zen-glance-content-animation {
- /* make the box appear from initial width/height and x/y coordinates */
- 0% {
- opacity: 0;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%) translateZ(0);
- width: 0%;
- height: 0%;
- }
-
- 80% {
- /* make the box grow to full width/height */
- opacity: 1;
- transform: translate(-50%, -50%) translateZ(0);
- top: 50%;
- left: 50%;
- width: 87%;
- height: 102%;
- }
-
- 100% {
- /* make the box shrink to final width/height and x/y coordinates */
- transform: translate(-50%, -50%) translateZ(0);
- opacity: 1;
- width: 85%;
- height: 100%;
- top: 50%;
- left: 50%;
- }
-}
-
@keyframes zen-glance-content-animation-out {
0% {
/* make the box shrink to final width/height and x/y coordinates */
diff --git a/src/browser/base/content/zen-styles/zen-browser-container.css b/src/browser/base/content/zen-styles/zen-browser-container.css
index 9afaef709..024c0d312 100644
--- a/src/browser/base/content/zen-styles/zen-browser-container.css
+++ b/src/browser/base/content/zen-styles/zen-browser-container.css
@@ -6,7 +6,15 @@
:root:not([inDOMFullscreen='true']):not([chromehidden~='location']):not([chromehidden~='toolbar']) {
& #tabbrowser-tabbox #tabbrowser-tabpanels .browserSidebarContainer {
width: -moz-available;
- border-radius: var(--zen-webview-border-radius, var(--zen-border-radius));
+ --zen-native-content-radius: env(-moz-gtk-csd-titlebar-radius, var(--zen-border-radius));
+ border-radius: var(
+ --zen-webview-border-radius,
+ /* Inner radius calculation:
+ * 1. If the native radius - the separation is less than 4px, use 4px.
+ * 2. Otherwise, use the the calculated value (inner radius = outer radius - separation).
+ */
+ max(4px, calc(var(--zen-native-content-radius) - var(--zen-element-separation)))
+ );
position: relative;
/* For glance */
@@ -22,7 +30,7 @@
:root:not([zen-no-padding='true']) & {
margin: 1px;
- box-shadow: 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.5));
+ box-shadow: 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3));
}
}
diff --git a/src/browser/base/content/zen-styles/zen-browser-ui.css b/src/browser/base/content/zen-styles/zen-browser-ui.css
index 3b03740e0..4f6127ce5 100644
--- a/src/browser/base/content/zen-styles/zen-browser-ui.css
+++ b/src/browser/base/content/zen-styles/zen-browser-ui.css
@@ -56,20 +56,7 @@
}
}
- @media (-moz-windows-accent-color-in-titlebar) {
- background-color: ActiveCaption;
- color: CaptionText;
- transition: background-color var(--inactive-window-transition);
- &:-moz-window-inactive {
- background-color: InactiveCaption;
- color: InactiveCaptionText;
- }
- &::after {
- display: none;
- }
- }
-
- @media (not (-moz-windows-accent-color-in-titlebar)) and (not (-moz-windows-mica)) and (-moz-bool-pref: 'zen.view.grey-out-inactive-windows') {
+ @media (not (-moz-windows-mica)) and (-moz-bool-pref: 'zen.view.grey-out-inactive-windows') {
transition: color var(--inactive-window-transition);
&:-moz-window-inactive {
color: var(--toolbox-textcolor-inactive);
@@ -111,6 +98,11 @@
z-index: 2;
}
+#nav-bar {
+ /* For some reason, firefox adds a really small border to the top of the nav-bar */
+ border-top: none !important;
+}
+
#zen-main-app-wrapper {
background: transparent;
overflow: hidden;
@@ -118,6 +110,16 @@
& > * {
z-index: 1;
}
+
+ @media (-moz-windows-accent-color-in-titlebar) and (-moz-windows-mica) {
+ background-color: ActiveCaption;
+ color: CaptionText;
+ transition: background-color var(--inactive-window-transition);
+ &:-moz-window-inactive {
+ background-color: InactiveCaption;
+ color: InactiveCaptionText;
+ }
+ }
}
#zen-appcontent-wrapper {
@@ -141,7 +143,7 @@
margin-right: 0;
}
- &:not([zen-no-padding='true']) #zen-tabbox-wrapper {
+ &:not([zen-no-padding='true'], [zen-right-side='true']) #zen-tabbox-wrapper {
margin-left: 1px;
}
}
diff --git a/src/browser/base/content/zen-styles/zen-compact-mode.css b/src/browser/base/content/zen-styles/zen-compact-mode.css
index 841eeec7a..d1a754cf9 100644
--- a/src/browser/base/content/zen-styles/zen-compact-mode.css
+++ b/src/browser/base/content/zen-styles/zen-compact-mode.css
@@ -131,9 +131,112 @@
*:is([panelopen='true'], [open='true'], #nav-bar:focus-within):not(tab):not(.zen-compact-mode-ignore)
) {
&:not([animate='true']) {
+ --zen-compact-mode-func: linear(
+ 0 0%,
+ 0.002958 1%,
+ 0.01137 2%,
+ 0.024581 3%,
+ 0.041981 4%,
+ 0.063001 5%,
+ 0.087118 6%,
+ 0.113846 7.000000000000001%,
+ 0.14274 8%,
+ 0.173391 9%,
+ 0.205425 10%,
+ 0.238504 11%,
+ 0.272318 12%,
+ 0.30659 13%,
+ 0.341071 14.000000000000002%,
+ 0.375535 15%,
+ 0.409787 16%,
+ 0.44365 17%,
+ 0.476971 18%,
+ 0.509618 19%,
+ 0.541476 20%,
+ 0.572448 21%,
+ 0.602453 22%,
+ 0.631425 23%,
+ 0.65931 24%,
+ 0.686067 25%,
+ 0.711668 26%,
+ 0.736092 27%,
+ 0.759328 28.000000000000004%,
+ 0.781375 28.999999999999996%,
+ 0.802235 30%,
+ 0.821921 31%,
+ 0.840449 32%,
+ 0.857841 33%,
+ 0.874121 34%,
+ 0.88932 35%,
+ 0.903469 36%,
+ 0.916603 37%,
+ 0.928759 38%,
+ 0.939975 39%,
+ 0.950291 40%,
+ 0.959747 41%,
+ 0.968385 42%,
+ 0.976244 43%,
+ 0.983366 44%,
+ 0.989792 45%,
+ 0.995562 46%,
+ 1.000716 47%,
+ 1.005292 48%,
+ 1.009328 49%,
+ 1.01286 50%,
+ 1.015925 51%,
+ 1.018556 52%,
+ 1.020787 53%,
+ 1.022648 54%,
+ 1.024172 55.00000000000001%,
+ 1.025385 56.00000000000001%,
+ 1.026316 56.99999999999999%,
+ 1.026991 57.99999999999999%,
+ 1.027434 59%,
+ 1.027669 60%,
+ 1.027717 61%,
+ 1.027599 62%,
+ 1.027334 63%,
+ 1.02694 64%,
+ 1.026433 65%,
+ 1.025829 66%,
+ 1.025143 67%,
+ 1.024388 68%,
+ 1.023575 69%,
+ 1.022715 70%,
+ 1.02182 71%,
+ 1.020898 72%,
+ 1.019957 73%,
+ 1.019005 74%,
+ 1.018048 75%,
+ 1.017094 76%,
+ 1.016146 77%,
+ 1.015211 78%,
+ 1.014291 79%,
+ 1.013391 80%,
+ 1.012513 81%,
+ 1.01166 82%,
+ 1.010835 83%,
+ 1.010039 84%,
+ 1.009273 85%,
+ 1.008538 86%,
+ 1.007836 87%,
+ 1.007166 88%,
+ 1.00653 89%,
+ 1.005926 90%,
+ 1.005355 91%,
+ 1.004817 92%,
+ 1.00431 93%,
+ 1.003835 94%,
+ 1.003391 95%,
+ 1.002976 96%,
+ 1.002591 97%,
+ 1.002233 98%,
+ 1.001902 99%,
+ 1.001597 100%
+ );
transition:
- left 0.25s ease,
- right 0.25s ease;
+ left 0.3125s var(--zen-compact-mode-func),
+ right 0.3125s var(--zen-compact-mode-func);
opacity: 1;
left: -1px;
diff --git a/src/browser/base/content/zen-styles/zen-glance.css b/src/browser/base/content/zen-styles/zen-glance.css
index 03396a3a7..ce2e4e2f0 100644
--- a/src/browser/base/content/zen-styles/zen-glance.css
+++ b/src/browser/base/content/zen-styles/zen-glance.css
@@ -105,7 +105,6 @@
height: 100%;
opacity: 1;
transition: opacity 0.2s ease-in-out;
- transition-delay: 0.1s;
}
&[animate-full='true'] {
@@ -118,7 +117,8 @@
}
&[animate='true'] {
- animation: zen-glance-content-animation 0.4s ease-in-out forwards;
+ position: absolute;
+ transform: translate(-50%, -50%);
&:not([animate-end='true']) {
pointer-events: none;
diff --git a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
index 3842d0db9..3389b752f 100644
--- a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
+++ b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
@@ -65,10 +65,7 @@
}
#browser {
- --zen-toolbox-padding: calc(var(--zen-element-separation) / 1.5);
- :root[zen-no-padding='true'] & {
- --zen-toolbox-padding: 4px;
- }
+ --zen-toolbox-padding: max(4px, calc(var(--zen-element-separation) / 1.5));
}
:root[zen-single-toolbar='true'] {
@@ -223,6 +220,7 @@
#tabbrowser-tabs {
margin-inline-start: 0 !important;
padding-inline-start: 0 !important;
+ overflow-x: hidden;
--tab-inner-inline-margin: 0;
@@ -242,23 +240,6 @@
& .tabbrowser-tab {
transition: scale 0.07s ease;
#tabbrowser-tabs &:not([zen-essential='true']) {
- #tabbrowser-tabs[zen-workspace-animation='previous'] & {
- animation: zen-slide-in;
- }
-
- #tabbrowser-tabs[zen-workspace-animation='next'] & {
- animation: zen-slide-in-reverse;
- }
-
- #tabbrowser-tabs[zen-workspace-animation] & {
- opacity: 0;
- transform: translateX(-100%);
- animation-delay: 0.2s;
- animation-fill-mode: forwards;
- animation-duration: 0.2s;
- animation-timing-function: ease;
- }
-
#tabbrowser-tabs[dont-animate-tabs] & {
opacity: 0;
}
@@ -460,7 +441,7 @@
padding-left: 0;
}
- &:not([zen-right-side='true']):not([zen-user-hover='true']) {
+ &:not([zen-right-side='true']) {
padding-right: 0;
& #titlebar {
padding-left: var(--zen-toolbox-padding);
@@ -963,6 +944,39 @@
& .tab-icon-overlay {
margin-inline-end: 0 !important;
}
+
+ @media (-moz-bool-pref: 'zen.theme.essentials-favicon-bg') {
+ &[selected] .tab-background {
+ &::after {
+ content: "";
+ inset: -50%;
+ filter: blur(15px);
+ position: absolute;
+ background-size: 100% 100%;
+ background-clip: padding-box;
+ background-image: var(--zen-tab-icon);
+ z-index: -1;
+ }
+
+ background: transparent;
+ overflow: hidden;
+
+ &::before {
+ background: light-dark(rgba(255, 255, 255, 0.8), rgba(68, 64, 64, 0.80));
+ margin: 2px;
+ border-radius: calc(var(--tab-border-radius) - 2px);
+ position: absolute;
+ inset: 0;
+ z-index: 0;
+ content: "";
+ transition: background 0.2s ease-in-out;
+ }
+ }
+
+ &[selected]:hover .tab-background::before {
+ background: light-dark(rgba(255, 255, 255, 0.8), rgba(68, 64, 64, 0.85));
+ }
+ }
}
diff --git a/src/browser/base/content/zen-styles/zen-theme.css b/src/browser/base/content/zen-styles/zen-theme.css
index e311ca693..ca1bcc4e3 100644
--- a/src/browser/base/content/zen-styles/zen-theme.css
+++ b/src/browser/base/content/zen-styles/zen-theme.css
@@ -103,7 +103,7 @@
--zen-button-border-radius: 5px;
--zen-button-padding: 0.6rem 1.2rem;
- --zen-toolbar-element-bg: light-dark(rgba(255, 255, 255, 0.4), rgba(170, 170, 170, 0.2));
+ --zen-toolbar-element-bg: light-dark(rgba(255, 255, 255, 0.65), rgba(170, 170, 170, 0.2));
/* Toolbar */
--zen-toolbar-height: 38px;
diff --git a/src/browser/base/content/zen-styles/zen-urlbar.css b/src/browser/base/content/zen-styles/zen-urlbar.css
index 170f06e5e..f6330a83d 100644
--- a/src/browser/base/content/zen-styles/zen-urlbar.css
+++ b/src/browser/base/content/zen-styles/zen-urlbar.css
@@ -360,6 +360,7 @@ button.popup-notification-dropmarker {
}
#urlbar[open] {
+ z-index: 2;
--urlbar-margin-inline: 5px !important;
& #identity-box {
diff --git a/src/browser/base/content/zen-styles/zen-workspaces.css b/src/browser/base/content/zen-styles/zen-workspaces.css
index d63d2587c..8537fa116 100644
--- a/src/browser/base/content/zen-styles/zen-workspaces.css
+++ b/src/browser/base/content/zen-styles/zen-workspaces.css
@@ -474,6 +474,10 @@
max-width: calc(100% - var(--zen-toolbox-padding) * 4);
}
+ & #zen-current-workspace-indicator-icon {
+ min-height: 16px;
+ }
+
& #zen-current-workspace-indicator-icon:not([hidden]) + #zen-current-workspace-indicator-name {
padding-left: 24px;
}
diff --git a/src/browser/base/content/zen-vendor/motion.min.mjs b/src/browser/base/content/zen-vendor/motion.min.mjs
new file mode 100644
index 000000000..915552945
--- /dev/null
+++ b/src/browser/base/content/zen-vendor/motion.min.mjs
@@ -0,0 +1,7 @@
+/**
+ * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.37.0.
+ * Original file: /npm/motion@11.17.0/dist/es/motion/lib/index.mjs
+ *
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
+ */
+const t=!1;function e(){return t}const n=t=>t;let s=n;const i=(t,e,n)=>{const s=e-t;return 0===s?1:(n-t)/s};function r(t){let e;return()=>(void 0===e&&(e=t()),e)}const o=r((()=>void 0!==window.ScrollTimeline));class a{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map((t=>"finished"in t?t.finished:t)))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let n=0;no()&&n.attachTimeline?n.attachTimeline(t):"function"==typeof e?e(n):void 0));return()=>{n.forEach(((t,e)=>{t&&t(),this.animations[e].stop()}))}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]()))}flatten(){this.runAll("flatten")}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class l extends a{then(t,e){return Promise.all(this.animations).then(t).catch(e)}}const u=t=>1e3*t,c=t=>t/1e3,h=2e4;function d(t){let e=0;let n=t.next(e);for(;!n.done&&e=h?1/0:e}const p=(t,e,n=10)=>{let s="";const r=Math.max(Math.round(e/n),2);for(let e=0;en>e?e:n{const s=e*o,i=s*t,r=s-n,a=R(e,o),l=Math.exp(-i);return C-r/a*l},r=e=>{const s=e*o*t,r=s*n+n,a=Math.pow(o,2)*Math.pow(e,2)*t,l=Math.exp(-s),u=R(Math.pow(e,2),o);return(-i(e)+C>0?-1:1)*((r-a)*l)/u}):(i=e=>Math.exp(-e*t)*((e-n)*t+1)-.001,r=e=>Math.exp(-e*t)*(t*t*(n-e)));const a=function(t,e,n){let s=n;for(let n=1;nvoid 0!==t[e]))}function L(t=S,e=T){const n="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:s,restDelta:i}=n;const r=n.keyframes[0],o=n.keyframes[n.keyframes.length-1],a={done:!1,value:r},{stiffness:l,damping:m,mass:x,duration:M,velocity:P,isResolvedFromDuration:k}=function(t){let e={velocity:b,stiffness:y,damping:v,mass:w,isResolvedFromDuration:!1,...t};if(!D(t,I)&&D(t,B))if(t.visualDuration){const n=t.visualDuration,s=2*Math.PI/(1.2*n),i=s*s,r=2*f(.05,1,1-(t.bounce||0))*Math.sqrt(i);e={...e,mass:w,stiffness:i,damping:r}}else{const n=E(t);e={...e,...n,mass:w},e.isResolvedFromDuration=!0}return e}({...n,velocity:-c(n.velocity||0)}),F=P||0,C=m/(2*Math.sqrt(l*x)),O=o-r,L=c(Math.sqrt(l/x)),W=Math.abs(O)<5;let N;if(s||(s=W?A.granular:A.default),i||(i=W?V.granular:V.default),C<1){const t=R(L,C);N=e=>{const n=Math.exp(-C*L*e);return o-n*((F+C*L*O)/t*Math.sin(t*e)+O*Math.cos(t*e))}}else if(1===C)N=t=>o-Math.exp(-L*t)*(O+(F+L*O)*t);else{const t=L*Math.sqrt(C*C-1);N=e=>{const n=Math.exp(-C*L*e),s=Math.min(t*e,300);return o-n*((F+C*L*O)*Math.sinh(s)+t*O*Math.cosh(s))/t}}const K={calculatedDuration:k&&M||null,next:t=>{const e=N(t);if(k)a.done=t>=M;else{let n=0;C<1&&(n=0===t?u(F):g(N,t,e));const r=Math.abs(n)<=s,l=Math.abs(o-e)<=i;a.done=r&&l}return a.value=a.done?o:e,a},toString:()=>{const t=Math.min(d(K),h),e=p((e=>K.next(t*e).value),t,30);return t+"ms "+e}};return K}function W(t,e=100,n){const s=n({...t,keyframes:[0,e]}),i=Math.min(d(s),h);return{type:"keyframes",ease:t=>s.next(i*t).value/e,duration:c(i)}}function N(t){return"function"==typeof t}const K=(t,e,n)=>{const s=e-t;return((n-t)%s+s)%s+t},$=t=>Array.isArray(t)&&"number"!=typeof t[0];function j(t,e){return $(t)?t[K(0,t.length,e)]:t}const z=(t,e,n)=>t+(e-t)*n;function H(t,e){const n=t[t.length-1];for(let s=1;s<=e;s++){const r=i(0,e,s);t.push(z(n,1,r))}}function U(t){const e=[0];return H(e,t.length-1),e}const Y=t=>Boolean(t&&t.getVelocity);function q(t,e,n){var s;if(t instanceof Element)return[t];if("string"==typeof t){let i=document;e&&(i=e.current);const r=null!==(s=null==n?void 0:n[t])&&void 0!==s?s:i.querySelectorAll(t);return r?Array.from(r):[]}return Array.from(t)}function X(t){return"object"==typeof t&&!Array.isArray(t)}function G(t,e,n,s){return"string"==typeof t&&X(e)?q(t,n,s):t instanceof NodeList?Array.from(t):Array.isArray(t)?t:[t]}function Z(t,e,n){return t*(e+1)}function _(t,e,n,s){var i;return"number"==typeof e?e:e.startsWith("-")||e.startsWith("+")?Math.max(0,t+parseFloat(e)):"<"===e?n:null!==(i=s.get(e))&&void 0!==i?i:t}function J(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}function Q(t,e,n,s,i,r){!function(t,e,n){for(let s=0;se&&i.at"number"==typeof t,at=t=>t.every(ot),lt=new WeakMap;function ut(t,e){return t?t[e]||t.default||t:void 0}const ct=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],ht=new Set(ct),dt=new Set(["width","height","top","left","right","bottom",...ct]),pt=t=>(t=>Array.isArray(t))(t)?t[t.length-1]||0:t,ft=!1;const mt=["read","resolveKeyframes","update","preRender","render","postRender"];const{schedule:gt,cancel:yt,state:vt,steps:wt}=function(t,e){let n=!1,s=!0;const i={delta:0,timestamp:0,isProcessing:!1},r=()=>n=!0,o=mt.reduce(((t,e)=>(t[e]=function(t){let e=new Set,n=new Set,s=!1,i=!1;const r=new WeakSet;let o={delta:0,timestamp:0,isProcessing:!1};function a(e){r.has(e)&&(l.schedule(e),t()),e(o)}const l={schedule:(t,i=!1,o=!1)=>{const a=o&&s?e:n;return i&&r.add(t),a.has(t)||a.add(t),t},cancel:t=>{n.delete(t),r.delete(t)},process:t=>{o=t,s?i=!0:(s=!0,[e,n]=[n,e],e.forEach(a),e.clear(),s=!1,i&&(i=!1,l.process(t)))}};return l}(r),t)),{}),{read:a,resolveKeyframes:l,update:u,preRender:c,render:h,postRender:d}=o,p=()=>{const r=performance.now();n=!1,i.delta=s?1e3/60:Math.max(Math.min(r-i.timestamp,40),1),i.timestamp=r,i.isProcessing=!0,a.process(i),l.process(i),u.process(i),c.process(i),h.process(i),d.process(i),i.isProcessing=!1,n&&e&&(s=!1,t(p))};return{schedule:mt.reduce(((e,r)=>{const a=o[r];return e[r]=(e,r=!1,o=!1)=>(n||(n=!0,s=!0,i.isProcessing||t(p)),a.schedule(e,r,o)),e}),{}),cancel:t=>{for(let e=0;e(void 0===bt&&Tt.set(vt.isProcessing||ft?vt.timestamp:performance.now()),bt),set:t=>{bt=t,queueMicrotask(xt)}};class St{constructor(){this.subscriptions=[]}add(t){var e,n;return e=this.subscriptions,n=t,-1===e.indexOf(n)&&e.push(n),()=>J(this.subscriptions,t)}notify(t,e,n){const s=this.subscriptions.length;if(s)if(1===s)this.subscriptions[0](t,e,n);else for(let i=0;i{const n=Tt.now();this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&this.events.change&&this.events.change.notify(this.current),e&&this.events.renderRequest&&this.events.renderRequest.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=Tt.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new St);const n=this.events[t].add(e);return"change"===t?()=>{n(),gt.read((()=>{this.events.change.getSize()||this.stop()}))}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=Tt.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return m(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise((e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()})).then((()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()}))}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function Vt(t,e){return new At(t,e)}function Mt(t){const e=[{},{}];return null==t||t.values.forEach(((t,n)=>{e[0][n]=t.get(),e[1][n]=t.getVelocity()})),e}function Pt(t,e,n,s){if("function"==typeof e){const[i,r]=Mt(s);e=e(void 0!==n?n:t.custom,i,r)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[i,r]=Mt(s);e=e(void 0!==n?n:t.custom,i,r)}return e}function kt(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,Vt(n))}function Ft(t,e){const n=function(t,e,n){const s=t.getProps();return Pt(s,e,void 0!==n?n:s.custom,t)}(t,e);let{transitionEnd:s={},transition:i={},...r}=n||{};r={...r,...s};for(const e in r){kt(t,e,pt(r[e]))}}function Ct(t,e){const n=t.getValue("willChange");if(s=n,Boolean(Y(s)&&s.add))return n.add(e);var s}const Et=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),Ot="data-"+Et("framerAppearId");function Rt(t){return t.props[Ot]}function Bt(t,e){t.timeline=e,t.onfinish=null}const It=t=>Array.isArray(t)&&"number"==typeof t[0],Dt={linearEasing:void 0};function Lt(t,e){const n=r(t);return()=>{var t;return null!==(t=Dt[e])&&void 0!==t?t:n()}}const Wt=Lt((()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0}),"linearEasing");function Nt(t){return Boolean("function"==typeof t&&Wt()||!t||"string"==typeof t&&(t in $t||Wt())||It(t)||Array.isArray(t)&&t.every(Nt))}const Kt=([t,e,n,s])=>`cubic-bezier(${t}, ${e}, ${n}, ${s})`,$t={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:Kt([0,.65,.55,1]),circOut:Kt([.55,0,1,.45]),backIn:Kt([.31,.01,.66,-.59]),backOut:Kt([.33,1.53,.69,.99])};function jt(t,e){return t?"function"==typeof t&&Wt()?p(t,e):It(t)?Kt(t):Array.isArray(t)?t.map((t=>jt(t,e)||$t.easeOut)):$t[t]:void 0}const zt=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function Ht(t,e,s,i){if(t===e&&s===i)return n;const r=e=>function(t,e,n,s,i){let r,o,a=0;do{o=e+(n-e)/2,r=zt(o,s,i)-t,r>0?n=o:e=o}while(Math.abs(r)>1e-7&&++a<12);return o}(e,0,1,t,s);return t=>0===t||1===t?t:zt(r(t),e,i)}const Ut=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,Yt=t=>e=>1-t(1-e),qt=Ht(.33,1.53,.69,.99),Xt=Yt(qt),Gt=Ut(Xt),Zt=t=>(t*=2)<1?.5*Xt(t):.5*(2-Math.pow(2,-10*(t-1))),_t=t=>1-Math.sin(Math.acos(t)),Jt=Yt(_t),Qt=Ut(_t),te=t=>/^0[^.\s]+$/u.test(t);const ee={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},ne={...ee,transform:t=>f(0,1,t)},se={...ee,default:1},ie=t=>Math.round(1e5*t)/1e5,re=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const oe=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,ae=(t,e)=>n=>Boolean("string"==typeof n&&oe.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),le=(t,e,n)=>s=>{if("string"!=typeof s)return s;const[i,r,o,a]=s.match(re);return{[t]:parseFloat(i),[e]:parseFloat(r),[n]:parseFloat(o),alpha:void 0!==a?parseFloat(a):1}},ue={...ee,transform:t=>Math.round((t=>f(0,255,t))(t))},ce={test:ae("rgb","red"),parse:le("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:s=1})=>"rgba("+ue.transform(t)+", "+ue.transform(e)+", "+ue.transform(n)+", "+ie(ne.transform(s))+")"};const he={test:ae("#"),parse:function(t){let e="",n="",s="",i="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),s=t.substring(5,7),i=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),s=t.substring(3,4),i=t.substring(4,5),e+=e,n+=n,s+=s,i+=i),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(s,16),alpha:i?parseInt(i,16)/255:1}},transform:ce.transform},de=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),pe=de("deg"),fe=de("%"),me=de("px"),ge=de("vh"),ye=de("vw"),ve={...fe,parse:t=>fe.parse(t)/100,transform:t=>fe.transform(100*t)},we={test:ae("hsl","hue"),parse:le("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:s=1})=>"hsla("+Math.round(t)+", "+fe.transform(ie(e))+", "+fe.transform(ie(n))+", "+ie(ne.transform(s))+")"},be={test:t=>ce.test(t)||he.test(t)||we.test(t),parse:t=>ce.test(t)?ce.parse(t):we.test(t)?we.parse(t):he.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?ce.transform(t):we.transform(t)},xe=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const Te="number",Se="color",Ae=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Ve(t){const e=t.toString(),n=[],s={color:[],number:[],var:[]},i=[];let r=0;const o=e.replace(Ae,(t=>(be.test(t)?(s.color.push(r),i.push(Se),n.push(be.parse(t))):t.startsWith("var(")?(s.var.push(r),i.push("var"),n.push(t)):(s.number.push(r),i.push(Te),n.push(parseFloat(t))),++r,"${}"))).split("${}");return{values:n,split:o,indexes:s,types:i}}function Me(t){return Ve(t).values}function Pe(t){const{split:e,types:n}=Ve(t),s=e.length;return t=>{let i="";for(let r=0;r"number"==typeof t?0:t;const Fe={test:function(t){var e,n;return isNaN(t)&&"string"==typeof t&&((null===(e=t.match(re))||void 0===e?void 0:e.length)||0)+((null===(n=t.match(xe))||void 0===n?void 0:n.length)||0)>0},parse:Me,createTransformer:Pe,getAnimatableNone:function(t){const e=Me(t);return Pe(t)(e.map(ke))}},Ce=new Set(["brightness","contrast","saturate","opacity"]);function Ee(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[s]=n.match(re)||[];if(!s)return t;const i=n.replace(s,"");let r=Ce.has(e)?1:0;return s!==n&&(r*=100),e+"("+r+i+")"}const Oe=/\b([a-z-]*)\(.*?\)/gu,Re={...Fe,getAnimatableNone:t=>{const e=t.match(Oe);return e?e.map(Ee).join(" "):t}},Be={borderWidth:me,borderTopWidth:me,borderRightWidth:me,borderBottomWidth:me,borderLeftWidth:me,borderRadius:me,radius:me,borderTopLeftRadius:me,borderTopRightRadius:me,borderBottomRightRadius:me,borderBottomLeftRadius:me,width:me,maxWidth:me,height:me,maxHeight:me,top:me,right:me,bottom:me,left:me,padding:me,paddingTop:me,paddingRight:me,paddingBottom:me,paddingLeft:me,margin:me,marginTop:me,marginRight:me,marginBottom:me,marginLeft:me,backgroundPositionX:me,backgroundPositionY:me},Ie={rotate:pe,rotateX:pe,rotateY:pe,rotateZ:pe,scale:se,scaleX:se,scaleY:se,scaleZ:se,skew:pe,skewX:pe,skewY:pe,distance:me,translateX:me,translateY:me,translateZ:me,x:me,y:me,z:me,perspective:me,transformPerspective:me,opacity:ne,originX:ve,originY:ve,originZ:me},De={...ee,transform:Math.round},Le={...Be,...Ie,zIndex:De,size:me,fillOpacity:ne,strokeOpacity:ne,numOctaves:De},We={...Le,color:be,backgroundColor:be,outlineColor:be,fill:be,stroke:be,borderColor:be,borderTopColor:be,borderRightColor:be,borderBottomColor:be,borderLeftColor:be,filter:Re,WebkitFilter:Re},Ne=t=>We[t];function Ke(t,e){let n=Ne(t);return n!==Re&&(n=Fe),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const $e=new Set(["auto","none","0"]);const je=t=>t===ee||t===me,ze=(t,e)=>parseFloat(t.split(", ")[e]),He=(t,e)=>(n,{transform:s})=>{if("none"===s||!s)return 0;const i=s.match(/^matrix3d\((.+)\)$/u);if(i)return ze(i[1],e);{const e=s.match(/^matrix\((.+)\)$/u);return e?ze(e[1],t):0}},Ue=new Set(["x","y","z"]),Ye=ct.filter((t=>!Ue.has(t)));const qe={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:He(4,13),y:He(5,14)};qe.translateX=qe.x,qe.translateY=qe.y;const Xe=new Set;let Ge=!1,Ze=!1;function _e(){if(Ze){const t=Array.from(Xe).filter((t=>t.needsMeasurement)),e=new Set(t.map((t=>t.element))),n=new Map;e.forEach((t=>{const e=function(t){const e=[];return Ye.forEach((n=>{const s=t.getValue(n);void 0!==s&&(e.push([n,s.get()]),s.set(n.startsWith("scale")?1:0))})),e}(t);e.length&&(n.set(t,e),t.render())})),t.forEach((t=>t.measureInitialState())),e.forEach((t=>{t.render();const e=n.get(t);e&&e.forEach((([e,n])=>{var s;null===(s=t.getValue(e))||void 0===s||s.set(n)}))})),t.forEach((t=>t.measureEndState())),t.forEach((t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)}))}Ze=!1,Ge=!1,Xe.forEach((t=>t.complete())),Xe.clear()}function Je(){Xe.forEach((t=>{t.readKeyframes(),t.needsMeasurement&&(Ze=!0)}))}class Qe{constructor(t,e,n,s,i,r=!1){this.isComplete=!1,this.isAsync=!1,this.needsMeasurement=!1,this.isScheduled=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=s,this.element=i,this.isAsync=r}scheduleResolve(){this.isScheduled=!0,this.isAsync?(Xe.add(this),Ge||(Ge=!0,gt.read(Je),gt.resolveKeyframes(_e))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:s}=this;for(let i=0;i/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t),en=t=>e=>"string"==typeof e&&e.startsWith(t),nn=en("--"),sn=en("var(--"),rn=t=>!!sn(t)&&on.test(t.split("/*")[0].trim()),on=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,an=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function ln(t,e,n=1){const[s,i]=function(t){const e=an.exec(t);if(!e)return[,];const[,n,s,i]=e;return[`--${null!=n?n:s}`,i]}(t);if(!s)return;const r=window.getComputedStyle(e).getPropertyValue(s);if(r){const t=r.trim();return tn(t)?parseFloat(t):t}return rn(i)?ln(i,e,n+1):i}const un=t=>e=>e.test(t),cn=[ee,me,fe,pe,ye,ge,{test:t=>"auto"===t,parse:t=>t}],hn=t=>cn.find(un(t));class dn extends Qe{constructor(t,e,n,s,i){super(t,e,n,s,i,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{e.getValue(t).set(n)})),this.resolveNoneKeyframes()}}const pn=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!Fe.test(t)&&"0"!==t||t.startsWith("url(")));function fn(t,e,n,s){const i=t[0];if(null===i)return!1;if("display"===e||"visibility"===e)return!0;const r=t[t.length-1],o=pn(i,e),a=pn(r,e);return!(!o||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;nnull!==t;function gn(t,{repeat:e,repeatType:n="loop"},s){const i=t.filter(mn),r=e&&"loop"!==n&&e%2==1?0:i.length-1;return r&&void 0!==s?s:i[r]}class yn{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:s=0,repeatDelay:i=0,repeatType:r="loop",...o}){this.isStopped=!1,this.hasAttemptedResolve=!1,this.createdAt=Tt.now(),this.options={autoplay:t,delay:e,type:n,repeat:s,repeatDelay:i,repeatType:r,...o},this.updateFinishedPromise()}calcStartTime(){return this.resolvedAt&&this.resolvedAt-this.createdAt>40?this.resolvedAt:this.createdAt}get resolved(){return this._resolved||this.hasAttemptedResolve||(Je(),_e()),this._resolved}onKeyframesResolved(t,e){this.resolvedAt=Tt.now(),this.hasAttemptedResolve=!0;const{name:n,type:s,velocity:i,delay:r,onComplete:o,onUpdate:a,isGenerator:l}=this.options;if(!l&&!fn(t,n,s,i)){if(!r)return null==a||a(gn(t,this.options,e)),null==o||o(),void this.resolveFinishedPromise();this.options.duration=0}const u=this.initPlayback(t,e);!1!==u&&(this._resolved={keyframes:t,finalKeyframe:e,...u},this.onPostResolved())}onPostResolved(){}then(t,e){return this.currentFinishedPromise.then(t,e)}flatten(){this.options.type="keyframes",this.options.ease="linear"}updateFinishedPromise(){this.currentFinishedPromise=new Promise((t=>{this.resolveFinishedPromise=t}))}}function vn(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function wn(t,e){return n=>n>0?e:t}const bn=(t,e,n)=>{const s=t*t,i=n*(e*e-s)+s;return i<0?0:Math.sqrt(i)},xn=[he,ce,we];function Tn(t){const e=(n=t,xn.find((t=>t.test(n))));var n;if(!Boolean(e))return!1;let s=e.parse(t);return e===we&&(s=function({hue:t,saturation:e,lightness:n,alpha:s}){t/=360,n/=100;let i=0,r=0,o=0;if(e/=100){const s=n<.5?n*(1+e):n+e-n*e,a=2*n-s;i=vn(a,s,t+1/3),r=vn(a,s,t),o=vn(a,s,t-1/3)}else i=r=o=n;return{red:Math.round(255*i),green:Math.round(255*r),blue:Math.round(255*o),alpha:s}}(s)),s}const Sn=(t,e)=>{const n=Tn(t),s=Tn(e);if(!n||!s)return wn(t,e);const i={...n};return t=>(i.red=bn(n.red,s.red,t),i.green=bn(n.green,s.green,t),i.blue=bn(n.blue,s.blue,t),i.alpha=z(n.alpha,s.alpha,t),ce.transform(i))},An=(t,e)=>n=>e(t(n)),Vn=(...t)=>t.reduce(An),Mn=new Set(["none","hidden"]);function Pn(t,e){return n=>z(t,e,n)}function kn(t){return"number"==typeof t?Pn:"string"==typeof t?rn(t)?wn:be.test(t)?Sn:En:Array.isArray(t)?Fn:"object"==typeof t?be.test(t)?Sn:Cn:wn}function Fn(t,e){const n=[...t],s=n.length,i=t.map(((t,n)=>kn(t)(t,e[n])));return t=>{for(let e=0;e{for(const e in s)n[e]=s[e](t);return n}}const En=(t,e)=>{const n=Fe.createTransformer(e),s=Ve(t),i=Ve(e);return s.indexes.var.length===i.indexes.var.length&&s.indexes.color.length===i.indexes.color.length&&s.indexes.number.length>=i.indexes.number.length?Mn.has(t)&&!i.values.length||Mn.has(e)&&!s.values.length?function(t,e){return Mn.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}(t,e):Vn(Fn(function(t,e){var n;const s=[],i={color:0,var:0,number:0};for(let r=0;rvoid 0===a?l:void 0===l||Math.abs(a-t)-f*Math.exp(-t/s),w=t=>y+v(t),b=t=>{const e=v(t),n=w(t);d.done=Math.abs(e)<=u,d.value=d.done?y:n};let x,T;const S=t=>{var e;(e=d.value,void 0!==a&&el)&&(x=t,T=L({keyframes:[d.value,p(d.value)],velocity:g(w,t,d.value),damping:i,stiffness:r,restDelta:u,restSpeed:c}))};return S(0),{calculatedDuration:null,next:t=>{let e=!1;return T||void 0!==x||(e=!0,b(t),S(t)),void 0!==x&&t>=x?T.next(t-x):(!e&&b(t),d)}}}const Bn=Ht(.42,0,1,1),In=Ht(0,0,.58,1),Dn=Ht(.42,0,.58,1),Ln={linear:n,easeIn:Bn,easeInOut:Dn,easeOut:In,circIn:_t,circInOut:Qt,circOut:Jt,backIn:Xt,backInOut:Gt,backOut:qt,anticipate:Zt},Wn=t=>{if(It(t)){s(4===t.length);const[e,n,i,r]=t;return Ht(e,n,i,r)}return"string"==typeof t?Ln[t]:t};function Nn(t,e,{clamp:r=!0,ease:o,mixer:a}={}){const l=t.length;if(s(l===e.length),1===l)return()=>e[0];if(2===l&&e[0]===e[1])return()=>e[1];const u=t[0]===t[1];t[0]>t[l-1]&&(t=[...t].reverse(),e=[...e].reverse());const c=function(t,e,s){const i=[],r=s||On,o=t.length-1;for(let s=0;s{if(u&&n1)for(;sd(f(t[0],t[l-1],e)):d}function Kn({duration:t=300,keyframes:e,times:n,ease:s="easeInOut"}){const i=$(s)?s.map(Wn):Wn(s),r={done:!1,value:e[0]},o=function(t,e){return t.map((t=>t*e))}(n&&n.length===e.length?n:U(e),t),a=Nn(o,e,{ease:Array.isArray(i)?i:(l=e,u=i,l.map((()=>u||Dn)).splice(0,l.length-1))});var l,u;return{calculatedDuration:t,next:e=>(r.value=a(e),r.done=e>=t,r)}}const $n=t=>{const e=({timestamp:e})=>t(e);return{start:()=>gt.update(e,!0),stop:()=>yt(e),now:()=>vt.isProcessing?vt.timestamp:Tt.now()}},jn={decay:Rn,inertia:Rn,tween:Kn,keyframes:Kn,spring:L},zn=t=>t/100;class Hn extends yn{constructor(t){super(t),this.holdTime=null,this.cancelTime=null,this.currentTime=0,this.playbackSpeed=1,this.pendingPlayState="running",this.startTime=null,this.state="idle",this.stop=()=>{if(this.resolver.cancel(),this.isStopped=!0,"idle"===this.state)return;this.teardown();const{onStop:t}=this.options;t&&t()};const{name:e,motionValue:n,element:s,keyframes:i}=this.options,r=(null==s?void 0:s.KeyframeResolver)||Qe;this.resolver=new r(i,((t,e)=>this.onKeyframesResolved(t,e)),e,n,s),this.resolver.scheduleResolve()}flatten(){super.flatten(),this._resolved&&Object.assign(this._resolved,this.initPlayback(this._resolved.keyframes))}initPlayback(t){const{type:e="keyframes",repeat:n=0,repeatDelay:s=0,repeatType:i,velocity:r=0}=this.options,o=N(e)?e:jn[e]||Kn;let a,l;o!==Kn&&"number"!=typeof t[0]&&(a=Vn(zn,On(t[0],t[1])),t=[0,100]);const u=o({...this.options,keyframes:t});"mirror"===i&&(l=o({...this.options,keyframes:[...t].reverse(),velocity:-r})),null===u.calculatedDuration&&(u.calculatedDuration=d(u));const{calculatedDuration:c}=u,h=c+s;return{generator:u,mirroredGenerator:l,mapPercentToKeyframes:a,calculatedDuration:c,resolvedDuration:h,totalDuration:h*(n+1)-s}}onPostResolved(){const{autoplay:t=!0}=this.options;this.play(),"paused"!==this.pendingPlayState&&t?this.state=this.pendingPlayState:this.pause()}tick(t,e=!1){const{resolved:n}=this;if(!n){const{keyframes:t}=this.options;return{done:!0,value:t[t.length-1]}}const{finalKeyframe:s,generator:i,mirroredGenerator:r,mapPercentToKeyframes:o,keyframes:a,calculatedDuration:l,totalDuration:u,resolvedDuration:c}=n;if(null===this.startTime)return i.next(0);const{delay:h,repeat:d,repeatType:p,repeatDelay:m,onUpdate:g}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-u/this.speed,this.startTime)),e?this.currentTime=t:null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=Math.round(t-this.startTime)*this.speed;const y=this.currentTime-h*(this.speed>=0?1:-1),v=this.speed>=0?y<0:y>u;this.currentTime=Math.max(y,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=u);let w=this.currentTime,b=i;if(d){const t=Math.min(this.currentTime,u)/c;let e=Math.floor(t),n=t%1;!n&&t>=1&&(n=1),1===n&&e--,e=Math.min(e,d+1);Boolean(e%2)&&("reverse"===p?(n=1-n,m&&(n-=m/c)):"mirror"===p&&(b=r)),w=f(0,1,n)*c}const x=v?{done:!1,value:a[0]}:b.next(w);o&&(x.value=o(x.value));let{done:T}=x;v||null===l||(T=this.speed>=0?this.currentTime>=u:this.currentTime<=0);const S=null===this.holdTime&&("finished"===this.state||"running"===this.state&&T);return S&&void 0!==s&&(x.value=gn(a,this.options,s)),g&&g(x.value),S&&this.finish(),x}get duration(){const{resolved:t}=this;return t?c(t.calculatedDuration):0}get time(){return c(this.currentTime)}set time(t){t=u(t),this.currentTime=t,null!==this.holdTime||0===this.speed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.speed)}get speed(){return this.playbackSpeed}set speed(t){const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=c(this.currentTime))}play(){if(this.resolver.isScheduled||this.resolver.resume(),!this._resolved)return void(this.pendingPlayState="running");if(this.isStopped)return;const{driver:t=$n,onPlay:e,startTime:n}=this.options;this.driver||(this.driver=t((t=>this.tick(t)))),e&&e();const s=this.driver.now();null!==this.holdTime?this.startTime=s-this.holdTime:this.startTime?"finished"===this.state&&(this.startTime=s):this.startTime=null!=n?n:this.calcStartTime(),"finished"===this.state&&this.updateFinishedPromise(),this.cancelTime=this.startTime,this.holdTime=null,this.state="running",this.driver.start()}pause(){var t;this._resolved?(this.state="paused",this.holdTime=null!==(t=this.currentTime)&&void 0!==t?t:0):this.pendingPlayState="paused"}complete(){"running"!==this.state&&this.play(),this.pendingPlayState=this.state="finished",this.holdTime=null}finish(){this.teardown(),this.state="finished";const{onComplete:t}=this.options;t&&t()}cancel(){null!==this.cancelTime&&this.tick(this.cancelTime),this.teardown(),this.updateFinishedPromise()}teardown(){this.state="idle",this.stopDriver(),this.resolveFinishedPromise(),this.updateFinishedPromise(),this.startTime=this.cancelTime=null,this.resolver.cancel()}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}}const Un=new Set(["opacity","clipPath","filter","transform"]);function Yn(t,e,n,{delay:s=0,duration:i=300,repeat:r=0,repeatType:o="loop",ease:a="easeInOut",times:l}={}){const u={[e]:n};l&&(u.offset=l);const c=jt(a,i);return Array.isArray(c)&&(u.easing=c),t.animate(u,{delay:s,duration:i,easing:Array.isArray(c)?"linear":c,fill:"both",iterations:r+1,direction:"reverse"===o?"alternate":"normal"})}const qn=r((()=>Object.hasOwnProperty.call(Element.prototype,"animate")));const Xn={anticipate:Zt,backInOut:Gt,circInOut:Qt};class Gn extends yn{constructor(t){super(t);const{name:e,motionValue:n,element:s,keyframes:i}=this.options;this.resolver=new dn(i,((t,e)=>this.onKeyframesResolved(t,e)),e,n,s),this.resolver.scheduleResolve()}initPlayback(t,e){var n;let{duration:s=300,times:i,ease:r,type:o,motionValue:a,name:l,startTime:u}=this.options;if(!(null===(n=a.owner)||void 0===n?void 0:n.current))return!1;var c;if("string"==typeof r&&Wt()&&r in Xn&&(r=Xn[r]),N((c=this.options).type)||"spring"===c.type||!Nt(c.ease)){const{onComplete:e,onUpdate:n,motionValue:a,element:l,...u}=this.options,c=function(t,e){const n=new Hn({...e,keyframes:t,repeat:0,delay:0,isGenerator:!0});let s={done:!1,value:t[0]};const i=[];let r=0;for(;!s.done&&r<2e4;)s=n.sample(r),i.push(s.value),r+=10;return{times:void 0,keyframes:i,duration:r-10,ease:"linear"}}(t,u);1===(t=c.keyframes).length&&(t[1]=t[0]),s=c.duration,i=c.times,r=c.ease,o="keyframes"}const h=Yn(a.owner.current,l,t,{...this.options,duration:s,times:i,ease:r});return h.startTime=null!=u?u:this.calcStartTime(),this.pendingTimeline?(Bt(h,this.pendingTimeline),this.pendingTimeline=void 0):h.onfinish=()=>{const{onComplete:n}=this.options;a.set(gn(t,this.options,e)),n&&n(),this.cancel(),this.resolveFinishedPromise()},{animation:h,duration:s,times:i,type:o,ease:r,keyframes:t}}get duration(){const{resolved:t}=this;if(!t)return 0;const{duration:e}=t;return c(e)}get time(){const{resolved:t}=this;if(!t)return 0;const{animation:e}=t;return c(e.currentTime||0)}set time(t){const{resolved:e}=this;if(!e)return;const{animation:n}=e;n.currentTime=u(t)}get speed(){const{resolved:t}=this;if(!t)return 1;const{animation:e}=t;return e.playbackRate}set speed(t){const{resolved:e}=this;if(!e)return;const{animation:n}=e;n.playbackRate=t}get state(){const{resolved:t}=this;if(!t)return"idle";const{animation:e}=t;return e.playState}get startTime(){const{resolved:t}=this;if(!t)return null;const{animation:e}=t;return e.startTime}attachTimeline(t){if(this._resolved){const{resolved:e}=this;if(!e)return n;const{animation:s}=e;Bt(s,t)}else this.pendingTimeline=t;return n}play(){if(this.isStopped)return;const{resolved:t}=this;if(!t)return;const{animation:e}=t;"finished"===e.playState&&this.updateFinishedPromise(),e.play()}pause(){const{resolved:t}=this;if(!t)return;const{animation:e}=t;e.pause()}stop(){if(this.resolver.cancel(),this.isStopped=!0,"idle"===this.state)return;this.resolveFinishedPromise(),this.updateFinishedPromise();const{resolved:t}=this;if(!t)return;const{animation:e,keyframes:n,duration:s,type:i,ease:r,times:o}=t;if("idle"===e.playState||"finished"===e.playState)return;if(this.time){const{motionValue:t,onUpdate:e,onComplete:a,element:l,...c}=this.options,h=new Hn({...c,keyframes:n,duration:s,type:i,ease:r,times:o,isGenerator:!0}),d=u(this.time);t.setWithVelocity(h.sample(d-10).value,h.sample(d).value,10)}const{onStop:a}=this.options;a&&a(),this.cancel()}complete(){const{resolved:t}=this;t&&t.animation.finish()}cancel(){const{resolved:t}=this;t&&t.animation.cancel()}static supports(t){const{motionValue:e,name:n,repeatDelay:s,repeatType:i,damping:r,type:o}=t;return qn()&&n&&Un.has(n)&&e&&e.owner&&e.owner.current instanceof HTMLElement&&!e.owner.getProps().onUpdate&&!s&&"mirror"!==i&&0!==r&&"inertia"!==o}}const Zn={type:"spring",stiffness:500,damping:25,restSpeed:10},_n={type:"keyframes",duration:.8},Jn={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},Qn=(t,{keyframes:e})=>e.length>2?_n:ht.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:Zn:Jn;const ts=(t,e,n,s={},i,r)=>o=>{const a=ut(s,t)||{},c=a.delay||s.delay||0;let{elapsed:h=0}=s;h-=u(c);let d={keyframes:Array.isArray(n)?n:[null,n],ease:"easeOut",velocity:e.getVelocity(),...a,delay:-h,onUpdate:t=>{e.set(t),a.onUpdate&&a.onUpdate(t)},onComplete:()=>{o(),a.onComplete&&a.onComplete()},name:t,motionValue:e,element:r?void 0:i};(function({when:t,delay:e,delayChildren:n,staggerChildren:s,staggerDirection:i,repeat:r,repeatType:o,repeatDelay:a,from:l,elapsed:u,...c}){return!!Object.keys(c).length})(a)||(d={...d,...Qn(t,d)}),d.duration&&(d.duration=u(d.duration)),d.repeatDelay&&(d.repeatDelay=u(d.repeatDelay)),void 0!==d.from&&(d.keyframes[0]=d.from);let p=!1;if((!1===d.type||0===d.duration&&!d.repeatDelay)&&(d.duration=0,0===d.delay&&(p=!0)),p&&!r&&void 0!==e.get()){const t=gn(d.keyframes,a);if(void 0!==t)return gt.update((()=>{d.onUpdate(t),d.onComplete()})),new l([])}return!r&&Gn.supports(d)?new Gn(d):new Hn(d)};function es({protectedKeys:t,needsAnimating:e},n){const s=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,s}function ns(t,e,{delay:n=0,transitionOverride:s,type:i}={}){var r;let{transition:o=t.getDefaultTransition(),transitionEnd:a,...l}=e;s&&(o=s);const u=[],c=i&&t.animationState&&t.animationState.getState()[i];for(const e in l){const s=t.getValue(e,null!==(r=t.latestValues[e])&&void 0!==r?r:null),i=l[e];if(void 0===i||c&&es(c,e))continue;const a={delay:n,...ut(o||{},e)};let h=!1;if(window.MotionHandoffAnimation){const n=Rt(t);if(n){const t=window.MotionHandoffAnimation(n,e,gt);null!==t&&(a.startTime=t,h=!0)}}Ct(t,e),s.start(ts(e,s,i,t.shouldReduceMotion&&dt.has(e)?{type:!1}:a,t,h));const d=s.animation;d&&u.push(d)}return a&&Promise.all(u).then((()=>{gt.update((()=>{a&&Ft(t,a)}))})),u}const ss=()=>({x:{min:0,max:0},y:{min:0,max:0}}),is={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},rs={};for(const t in is)rs[t]={isEnabled:e=>is[t].some((t=>!!e[t]))};const os="undefined"!=typeof window,as={current:null},ls={current:!1};const us=[...cn,be,Fe];const cs=["initial","animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"];function hs(t){return null!==(e=t.animate)&&"object"==typeof e&&"function"==typeof e.start||cs.some((e=>function(t){return"string"==typeof t||Array.isArray(t)}(t[e])));var e}const ds=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class ps{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:s,blockInitialAnimation:i,visualState:r},o={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=Qe,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=Tt.now();this.renderScheduledAtthis.bindToMotionValue(e,t))),ls.current||function(){if(ls.current=!0,os)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>as.current=t.matches;t.addListener(e),e()}else as.current=!1}(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||as.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){lt.delete(this.current),this.projection&&this.projection.unmount(),yt(this.notifyUpdate),yt(this.render),this.valueSubscriptions.forEach((t=>t())),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=ht.has(t),s=e.on("change",(e=>{this.latestValues[t]=e,this.props.onUpdate&>.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0)})),i=e.on("renderRequest",this.scheduleRender);let r;window.MotionCheckAppearSync&&(r=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,(()=>{s(),i(),r&&r(),e.owner&&e.stop()}))}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in rs){const e=rs[t];if(!e)continue;const{isEnabled:n,Feature:s}=e;if(!this.features[t]&&s&&n(this.props)&&(this.features[t]=new s(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=Vt(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){var n;let s=void 0===this.latestValues[t]&&this.current?null!==(n=this.getBaseTargetFromProps(this.props,t))&&void 0!==n?n:this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];var i;return null!=s&&("string"==typeof s&&(tn(s)||te(s))?s=parseFloat(s):(i=s,!us.find(un(i))&&Fe.test(e)&&(s=Ke(t,e))),this.setBaseTarget(t,Y(s)?s.get():s)),Y(s)?s.get():s}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){var e;const{initial:n}=this.props;let s;if("string"==typeof n||"object"==typeof n){const i=Pt(this.props,n,null===(e=this.presenceContext)||void 0===e?void 0:e.custom);i&&(s=i[t])}if(n&&void 0!==s)return s;const i=this.getBaseTargetFromProps(this.props,t);return void 0===i||Y(i)?void 0!==this.initialValues[t]&&void 0===s?void 0:this.baseTarget[t]:i}on(t,e){return this.events[t]||(this.events[t]=new St),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class fs extends ps{constructor(){super(...arguments),this.KeyframeResolver=dn}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;Y(t)&&(this.childSubscription=t.on("change",(t=>{this.current&&(this.current.textContent=`${t}`)})))}}const ms=(t,e)=>e&&"number"==typeof t?e.transform(t):t,gs={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},ys=ct.length;function vs(t,e,n){const{style:s,vars:i,transformOrigin:r}=t;let o=!1,a=!1;for(const t in e){const n=e[t];if(ht.has(t))o=!0;else if(nn(t))i[t]=n;else{const e=ms(n,Le[t]);t.startsWith("origin")?(a=!0,r[t]=e):s[t]=e}}if(e.transform||(o||n?s.transform=function(t,e,n){let s="",i=!0;for(let r=0;r{const l=it(t),{delay:c=0,times:h=U(l),type:d="keyframes",repeat:m,repeatType:g,repeatDelay:v=0,...w}=n;let{ease:b=e.ease||"easeOut",duration:x}=n;const T="function"==typeof c?c(i,a):c,S=l.length,A=N(d)?d:null==r?void 0:r[d];if(S<=2&&A){let t=100;if(2===S&&at(l)){const e=l[1]-l[0];t=Math.abs(e)}const e={...w};void 0!==x&&(e.duration=u(x));const n=W(e,t,A);b=n.ease,x=n.duration}null!=x||(x=o);const V=p+T;1===h.length&&0===h[0]&&(h[1]=1);const M=h.length-l.length;if(M>0&&H(h,M),1===l.length&&l.unshift(null),m){x=Z(x,m);const t=[...l],e=[...h];b=Array.isArray(b)?[...b]:[b];const n=[...b];for(let s=0;s{for(const r in t){const o=t[r];o.sort(et);const l=[],u=[],c=[];for(let t=0;t{s.push(...Rs(n,t,e))})),s}function Is(t){return function(e,n,s){let i=[];var r;r=e,i=Array.isArray(r)&&r.some(Array.isArray)?Bs(e,n,t):Rs(e,n,s,t);const o=new l(i);return t&&t.animations.push(o),o}}const Ds=Is();class Ls{constructor(t){this.animation=t}get duration(){var t,e,n;const s=(null===(e=null===(t=this.animation)||void 0===t?void 0:t.effect)||void 0===e?void 0:e.getComputedTiming().duration)||(null===(n=this.options)||void 0===n?void 0:n.duration)||300;return c(Number(s))}get time(){var t;return this.animation?c((null===(t=this.animation)||void 0===t?void 0:t.currentTime)||0):0}set time(t){this.animation&&(this.animation.currentTime=u(t))}get speed(){return this.animation?this.animation.playbackRate:1}set speed(t){this.animation&&(this.animation.playbackRate=t)}get state(){return this.animation?this.animation.playState:"finished"}get startTime(){return this.animation?this.animation.startTime:null}get finished(){return this.animation?this.animation.finished:Promise.resolve()}play(){this.animation&&this.animation.play()}pause(){this.animation&&this.animation.pause()}stop(){this.animation&&"idle"!==this.state&&"finished"!==this.state&&(this.animation.commitStyles&&this.animation.commitStyles(),this.cancel())}flatten(){var t;this.animation&&(null===(t=this.animation.effect)||void 0===t||t.updateTiming({easing:"linear"}))}attachTimeline(t){return this.animation&&Bt(this.animation,t),n}complete(){this.animation&&this.animation.finish()}cancel(){try{this.animation&&this.animation.cancel()}catch(t){}}}function Ws(t,e,n){t.style.setProperty(`--${e}`,n)}function Ns(t,e,n){t.style[e]=n}const Ks=r((()=>{try{document.createElement("div").animate({opacity:[1]})}catch(t){return!1}return!0})),$s=new WeakMap;const js="easeOut";function zs(t){const e=$s.get(t)||new Map;return $s.set(t,e),$s.get(t)}class Hs extends Ls{constructor(t,e,n,i){const r=e.startsWith("--");s("string"!=typeof i.type);const o=zs(t).get(e);o&&o.stop();if(Array.isArray(n)||(n=[n]),function(t,e,n){for(let s=0;se.startsWith("--")?t.style.getPropertyValue(e):window.getComputedStyle(t)[e])),N(i.type)){const t=W(i,100,i.type);i.ease=Wt()?t.ease:js,i.duration=u(t.duration),i.type="keyframes"}else i.ease=i.ease||js;const a=()=>{this.setValue(t,e,gn(n,i)),this.cancel(),this.resolveFinishedPromise()},l=()=>{this.setValue=r?Ws:Ns,this.options=i,this.updateFinishedPromise(),this.removeAnimation=()=>{var n;return null===(n=$s.get(t))||void 0===n?void 0:n.delete(e)}};qn()?(super(Yn(t,e,n,i)),l(),!1===i.autoplay&&this.animation.pause(),this.animation.onfinish=a,zs(t).set(e,this)):(super(),l(),a())}then(t,e){return this.currentFinishedPromise.then(t,e)}updateFinishedPromise(){this.currentFinishedPromise=new Promise((t=>{this.resolveFinishedPromise=t}))}play(){"finished"===this.state&&this.updateFinishedPromise(),super.play()}cancel(){this.removeAnimation(),super.cancel()}}const Us=t=>function(e,n,s){return new l(function(t,e,n,s){const i=q(t,s),r=i.length,o=[];for(let t=0;t{const{currentTime:s}=e,i=(null===s?0:s.value)/100;n!==i&&t(i),n=i};return gt.update(s,!0),()=>yt(s)}const Xs=new WeakMap;let Gs;function Zs({target:t,contentRect:e,borderBoxSize:n}){var s;null===(s=Xs.get(t))||void 0===s||s.forEach((s=>{s({target:t,contentSize:e,get size(){return function(t,e){if(e){const{inlineSize:t,blockSize:n}=e[0];return{width:t,height:n}}return t instanceof SVGElement&&"getBBox"in t?t.getBBox():{width:t.offsetWidth,height:t.offsetHeight}}(t,n)}})}))}function _s(t){t.forEach(Zs)}function Js(t,e){Gs||"undefined"!=typeof ResizeObserver&&(Gs=new ResizeObserver(_s));const n=q(t);return n.forEach((t=>{let n=Xs.get(t);n||(n=new Set,Xs.set(t,n)),n.add(e),null==Gs||Gs.observe(t)})),()=>{n.forEach((t=>{const n=Xs.get(t);null==n||n.delete(e),(null==n?void 0:n.size)||null==Gs||Gs.unobserve(t)}))}}const Qs=new Set;let ti;function ei(t){return Qs.add(t),ti||(ti=()=>{const t={width:window.innerWidth,height:window.innerHeight},e={target:window,size:t,contentSize:t};Qs.forEach((t=>t(e)))},window.addEventListener("resize",ti)),()=>{Qs.delete(t),!Qs.size&&ti&&(ti=void 0)}}const ni={x:{length:"Width",position:"Left"},y:{length:"Height",position:"Top"}};function si(t,e,n,s){const r=n[e],{length:o,position:a}=ni[e],l=r.current,u=n.time;r.current=t[`scroll${a}`],r.scrollLength=t[`scroll${o}`]-t[`client${o}`],r.offset.length=0,r.offset[0]=0,r.offset[1]=r.scrollLength,r.progress=i(0,r.scrollLength,r.current);const c=s-u;r.velocity=c>50?0:m(r.current-l,c)}const ii={start:0,center:.5,end:1};function ri(t,e,n=0){let s=0;if(t in ii&&(t=ii[t]),"string"==typeof t){const e=parseFloat(t);t.endsWith("px")?s=e:t.endsWith("%")?t=e/100:t.endsWith("vw")?s=e/100*document.documentElement.clientWidth:t.endsWith("vh")?s=e/100*document.documentElement.clientHeight:t=e}return"number"==typeof t&&(s=e*t),n+s}const oi=[0,0];function ai(t,e,n,s){let i=Array.isArray(t)?t:oi,r=0,o=0;return"number"==typeof t?i=[t,t]:"string"==typeof t&&(i=(t=t.trim()).includes(" ")?t.split(" "):[t,ii[t]?t:"0"]),r=ri(i[0],n,s),o=ri(i[1],e),r-o}const li={Enter:[[0,1],[1,1]],Exit:[[0,0],[1,0]],Any:[[1,0],[0,1]],All:[[0,0],[1,1]]},ui={x:0,y:0};function ci(t,e,n){const{offset:s=li.All}=n,{target:i=t,axis:r="y"}=n,o="y"===r?"height":"width",a=i!==t?function(t,e){const n={x:0,y:0};let s=t;for(;s&&s!==e;)if(s instanceof HTMLElement)n.x+=s.offsetLeft,n.y+=s.offsetTop,s=s.offsetParent;else if("svg"===s.tagName){const t=s.getBoundingClientRect();s=s.parentElement;const e=s.getBoundingClientRect();n.x+=t.left-e.left,n.y+=t.top-e.top}else{if(!(s instanceof SVGGraphicsElement))break;{const{x:t,y:e}=s.getBBox();n.x+=t,n.y+=e;let i=null,r=s.parentNode;for(;!i;)"svg"===r.tagName&&(i=r),r=s.parentNode;s=i}}return n}(i,t):ui,l=i===t?{width:t.scrollWidth,height:t.scrollHeight}:function(t){return"getBBox"in t&&"svg"!==t.tagName?t.getBBox():{width:t.clientWidth,height:t.clientHeight}}(i),u={width:t.clientWidth,height:t.clientHeight};e[r].offset.length=0;let c=!e[r].interpolate;const h=s.length;for(let t=0;tfunction(t,e=t,n){if(n.x.targetOffset=0,n.y.targetOffset=0,e!==t){let s=e;for(;s&&s!==t;)n.x.targetOffset+=s.offsetLeft,n.y.targetOffset+=s.offsetTop,s=s.offsetParent}n.x.targetLength=e===t?e.scrollWidth:e.clientWidth,n.y.targetLength=e===t?e.scrollHeight:e.clientHeight,n.x.containerLength=t.clientWidth,n.y.containerLength=t.clientHeight}(t,s.target,n),update:e=>{!function(t,e,n){si(t,"x",e,n),si(t,"y",e,n),e.time=n}(t,n,e),(s.offset||s.target)&&ci(t,n,s)},notify:()=>e(n)}}const di=new WeakMap,pi=new WeakMap,fi=new WeakMap,mi=t=>t===document.documentElement?window:t;function gi(t,{container:e=document.documentElement,...n}={}){let s=fi.get(e);s||(s=new Set,fi.set(e,s));const i=hi(e,t,{time:0,x:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0},y:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0}},n);if(s.add(i),!di.has(e)){const t=()=>{for(const t of s)t.measure()},n=()=>{for(const t of s)t.update(vt.timestamp)},i=()=>{for(const t of s)t.notify()},a=()=>{gt.read(t,!1,!0),gt.read(n,!1,!0),gt.update(i,!1,!0)};di.set(e,a);const l=mi(e);window.addEventListener("resize",a,{passive:!0}),e!==document.documentElement&&pi.set(e,(o=a,"function"==typeof(r=e)?ei(r):Js(r,o))),l.addEventListener("scroll",a,{passive:!0})}var r,o;const a=di.get(e);return gt.read(a,!1,!0),()=>{var t;yt(a);const n=fi.get(e);if(!n)return;if(n.delete(i),n.size)return;const s=di.get(e);di.delete(e),s&&(mi(e).removeEventListener("scroll",s),null===(t=pi.get(e))||void 0===t||t(),window.removeEventListener("resize",s))}}const yi=new Map;function vi({source:t,container:e=document.documentElement,axis:n="y"}={}){t&&(e=t),yi.has(e)||yi.set(e,{});const s=yi.get(e);return s[n]||(s[n]=o()?new ScrollTimeline({source:e,axis:n}):function({source:t,container:e,axis:n="y"}){t&&(e=t);const s={value:0},i=gi((t=>{s.value=100*t[n].progress}),{container:e,axis:n});return{currentTime:s,cancel:i}}({source:e,axis:n})),s[n]}function wi(t){return t&&(t.target||t.offset)}function bi(t,{axis:e="y",...s}={}){const i={axis:e,...s};return"function"==typeof t?function(t,e){return function(t){return 2===t.length}(t)||wi(e)?gi((n=>{t(n[e.axis].progress,n)}),e):qs(t,vi(e))}(t,i):function(t,e){if(t.flatten(),wi(e))return t.pause(),gi((n=>{t.time=t.duration*n[e.axis].progress}),e);{const s=vi(e);return t.attachTimeline?t.attachTimeline(s,(t=>(t.pause(),qs((e=>{t.time=t.duration*e}),s)))):n}}(t,i)}const xi={some:0,all:1};function Ti(t,e,{root:n,margin:s,amount:i="some"}={}){const r=q(t),o=new WeakMap,a=new IntersectionObserver((t=>{t.forEach((t=>{const n=o.get(t.target);if(t.isIntersecting!==Boolean(n))if(t.isIntersecting){const n=e(t);"function"==typeof n?o.set(t.target,n):a.unobserve(t.target)}else"function"==typeof n&&(n(t),o.delete(t.target))}))}),{root:n,rootMargin:s,threshold:"number"==typeof i?i:xi[i]});return r.forEach((t=>a.observe(t))),()=>a.disconnect()}function Si(t,e="end"){return n=>{const s=(n="end"===e?Math.min(n,.999):Math.max(n,.001))*t,i="end"===e?Math.floor(s):Math.ceil(s);return f(0,1,i/t)}}function Ai(t=.1,{startDelay:e=0,from:n=0,ease:s}={}){return(i,r)=>{const o="number"==typeof n?n:function(t,e){if("first"===t)return 0;{const n=e-1;return"last"===t?n:n/2}}(n,r),a=Math.abs(o-i);let l=t*a;if(s){const e=r*t;l=Wn(s)(l/e)*e}return e+l}}function Vi(t,e){return function(t,e){const n=Tt.now(),s=({timestamp:i})=>{const r=i-n;r>=e&&(yt(s),t(r-e))};return gt.read(s,!0),()=>yt(s)}(t,u(e))}const Mi=(t,e)=>Math.abs(t-e);function Pi(t,e){const n=Mi(t.x,e.x),s=Mi(t.y,e.y);return Math.sqrt(n**2+s**2)}function ki(...t){const e=!Array.isArray(t[0]),n=e?0:-1,s=t[0+n],i=t[1+n],r=t[2+n],o=t[3+n],a=Nn(i,r,{mixer:(l=r[0],(t=>t&&"object"==typeof t&&t.mix)(l)?l.mix:void 0),...o});var l;return e?a(s):a}const Fi=gt,Ci=mt.reduce(((t,e)=>(t[e]=t=>yt(t),t)),{});export{At as MotionValue,Ds as animate,Ys as animateMini,Zt as anticipate,Xt as backIn,Gt as backInOut,qt as backOut,yt as cancelFrame,Ci as cancelSync,_t as circIn,Qt as circInOut,Jt as circOut,f as clamp,Is as createScopedAnimate,Ht as cubicBezier,Vi as delay,Mi as distance,Pi as distance2D,Bn as easeIn,Dn as easeInOut,In as easeOut,gt as frame,vt as frameData,wt as frameSteps,Ti as inView,Rn as inertia,Nn as interpolate,s as invariant,e as isDragActive,Kn as keyframes,Ut as mirrorEasing,On as mix,Vt as motionValue,n as noop,Vn as pipe,i as progress,Yt as reverseEasing,bi as scroll,gi as scrollInfo,L as spring,Ai as stagger,Si as steps,Fi as sync,Tt as time,ki as transform,K as wrap};export default null;
diff --git a/src/browser/base/zen-components/ZenCompactMode.mjs b/src/browser/base/zen-components/ZenCompactMode.mjs
index 00b4cd028..ce118d253 100644
--- a/src/browser/base/zen-components/ZenCompactMode.mjs
+++ b/src/browser/base/zen-components/ZenCompactMode.mjs
@@ -274,6 +274,7 @@ var gZenCompactModeManager = {
{
element: this.sidebar,
screenEdge: this.sidebarIsOnRight ? 'right' : 'left',
+ keepHoverDuration: 300,
},
{
element: document.getElementById('zen-appcontent-navbar-container'),
@@ -335,7 +336,7 @@ var gZenCompactModeManager = {
}
if (this.hoverableElements[i].keepHoverDuration) {
- this.flashElement(target, keepHoverDuration, 'has-hover' + target.id, 'zen-has-hover');
+ this.flashElement(target, this.hoverableElements[i].keepHoverDuration, 'has-hover' + target.id, 'zen-has-hover');
} else {
this._removeHoverFrames[target.id] = window.requestAnimationFrame(() => target.removeAttribute('zen-has-hover'));
}
diff --git a/src/browser/base/zen-components/ZenGlanceManager.mjs b/src/browser/base/zen-components/ZenGlanceManager.mjs
index 847123425..224a242ab 100644
--- a/src/browser/base/zen-components/ZenGlanceManager.mjs
+++ b/src/browser/base/zen-components/ZenGlanceManager.mjs
@@ -102,19 +102,36 @@
window.requestAnimationFrame(() => {
this.quickOpenGlance();
- this.browserWrapper.style.setProperty('--initial-x', `${initialX}px`);
- this.browserWrapper.style.setProperty('--initial-y', `${initialY}px`);
- this.browserWrapper.style.setProperty('--initial-width', initialWidth + 'px');
- this.browserWrapper.style.setProperty('--initial-height', initialHeight + 'px');
-
this.overlay.removeAttribute('fade-out');
this.browserWrapper.setAttribute('animate', true);
- setTimeout(() => {
- this.browserWrapper.setAttribute('animate-end', true);
- this.browserWrapper.setAttribute('has-finished-animation', true);
- this._animating = false;
- this.animatingOpen = false;
- }, 500);
+ this.browserWrapper.style.top = `${initialY}px`;
+ this.browserWrapper.style.left = `${initialX}px`;
+ this.browserWrapper.style.width = `${initialWidth}px`;
+ this.browserWrapper.style.height = `${initialHeight}px`;
+ gZenUIManager.motion
+ .animate(
+ this.browserWrapper,
+ {
+ top: [`${initialY}px`, '50%'],
+ left: [`${initialX}px`, '50%'],
+ width: [`${initialWidth}px`, '85%'],
+ height: [`${initialHeight}px`, '100%'],
+ opacity: [0.8, 1],
+ },
+ {
+ duration: 0.5,
+ ease: 'easeIn',
+ type: 'spring',
+ bounce: 0.25,
+ }
+ )
+ .then(() => {
+ this.browserWrapper.removeAttribute('animate');
+ this.browserWrapper.setAttribute('animate-end', true);
+ this.browserWrapper.setAttribute('has-finished-animation', true);
+ this._animating = false;
+ this.animatingOpen = false;
+ });
});
}
@@ -286,6 +303,7 @@
this.animatingFullOpen = true;
this.currentParentTab._visuallySelected = false;
+ this.browserWrapper.removeAttribute('style');
this.browserWrapper.removeAttribute('has-finished-animation');
this.browserWrapper.setAttribute('animate-full', true);
this.#currentTab.removeAttribute('zen-glance-tab');
diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs
index 15b22e43b..24b1c53d0 100644
--- a/src/browser/base/zen-components/ZenGradientGenerator.mjs
+++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs
@@ -658,7 +658,7 @@
setTimeout(() => {
// Reactivate the transition after the animation
appWrapper.removeAttribute('post-animating');
- });
+ }, 100);
}, 700);
});
}
diff --git a/src/browser/base/zen-components/ZenKeyboardShortcuts.mjs b/src/browser/base/zen-components/ZenKeyboardShortcuts.mjs
index d0400ec7d..dee74be78 100644
--- a/src/browser/base/zen-components/ZenKeyboardShortcuts.mjs
+++ b/src/browser/base/zen-components/ZenKeyboardShortcuts.mjs
@@ -315,12 +315,7 @@ class KeyShortcut {
static parseFromSaved(json) {
let rv = [];
for (let key of json) {
- if (this.#shortcutIsValid(key)) {
- rv.push(this.#parseFromJSON(key));
- } else {
- console.warn('[zen CKS]: Invalid shortcut', key['id']);
- throw new Error('Invalid shortcut found');
- }
+ rv.push(this.#parseFromJSON(key));
}
return rv;
@@ -338,12 +333,6 @@ class KeyShortcut {
return 'other';
}
- static #shortcutIsValid(shortcut) {
- // See https://github.com/zen-browser/desktop/issues/4071, some *old* shortcuts dont have
- // any of `key`, `keycode`, `l10nId`. This fix also allows us to avoid any future issues
- return !(shortcut['key'] == '' && shortcut['keycode'] == '' && shortcut['l10nId'] == null);
- }
-
static #parseFromJSON(json) {
return new KeyShortcut(
json['id'],
@@ -1071,7 +1060,7 @@ var gZenKeyboardShortcutsManager = {
//}
for (let key of this._currentShortcutList) {
- if (key.isEmpty() || key.isInternal() || key.isInvalid()) {
+ if (key.isInternal()) {
continue;
}
let child = key.toXHTMLElement(browser);
diff --git a/src/browser/base/zen-components/ZenPinnedTabManager.mjs b/src/browser/base/zen-components/ZenPinnedTabManager.mjs
index 3f25b098d..a4f4a467a 100644
--- a/src/browser/base/zen-components/ZenPinnedTabManager.mjs
+++ b/src/browser/base/zen-components/ZenPinnedTabManager.mjs
@@ -68,6 +68,13 @@
await this._refreshPinnedTabs(newWorkspace, { init: onInit });
}
+ onTabIconChanged(tab, url = null) {
+ const iconUrl = url ?? tab.iconImage.src;
+ if (tab.hasAttribute('zen-essential')) {
+ tab.querySelector('.tab-background').style.setProperty('--zen-tab-icon', `url(${iconUrl})`);
+ }
+ }
+
get enabled() {
if (typeof this._enabled === 'undefined') {
this._enabled = !(
@@ -544,6 +551,7 @@
gBrowser.unpinTab(tab);
}
gBrowser.pinTab(tab);
+ this.onTabIconChanged(tab);
}
}
diff --git a/src/browser/base/zen-components/ZenWorkspaces.mjs b/src/browser/base/zen-components/ZenWorkspaces.mjs
index 572f205a5..c84c654d9 100644
--- a/src/browser/base/zen-components/ZenWorkspaces.mjs
+++ b/src/browser/base/zen-components/ZenWorkspaces.mjs
@@ -391,7 +391,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
activeWorkspace = workspaces.workspaces[0];
this.activeWorkspace = activeWorkspace?.uuid;
}
- await this.changeWorkspace(activeWorkspace, true);
+ await this.changeWorkspace(activeWorkspace, { onInit: true });
}
try {
if (activeWorkspace) {
@@ -415,6 +415,10 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
indicator.addEventListener('click', th);
}
+ shouldCloseWindow() {
+ return !window.toolbar.visible || Services.prefs.getBoolPref('browser.tabs.closeWindowWithLastTab');
+ }
+
handleTabBeforeClose(tab) {
if (!this.workspaceEnabled || this.__contextIsDelete) {
return null;
@@ -425,17 +429,33 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
return null;
}
- const shouldOpenNewTabIfLastUnpinnedTabIsClosed = this.shouldOpenNewTabIfLastUnpinnedTabIsClosed;
-
- let tabs = gBrowser.tabs.filter(
- (t) =>
- t.getAttribute('zen-workspace-id') === workspaceID &&
- (!shouldOpenNewTabIfLastUnpinnedTabIsClosed || !t.pinned || t.getAttribute('pending') !== 'true')
- );
+ let tabs = gBrowser.visibleTabs;
+ let tabsPinned = tabs.filter((t) => !this.shouldOpenNewTabIfLastUnpinnedTabIsClosed || !t.pinned);
+ const shouldCloseWindow = this.shouldCloseWindow();
if (tabs.length === 1 && tabs[0] === tab) {
- let newTab = this._createNewTabForWorkspace({ uuid: workspaceID });
- return newTab;
+ if (shouldCloseWindow) {
+ // We've already called beforeunload on all the relevant tabs if we get here,
+ // so avoid calling it again:
+ window.skipNextCanClose = true;
+
+ // Closing the tab and replacing it with a blank one is notably slower
+ // than closing the window right away. If the caller opts in, take
+ // the fast path.
+ if (!gBrowser._removingTabs.size) {
+ // This call actually closes the window, unless the user
+ // cancels the operation. We are finished here in both cases.
+ this._isClosingWindow = true;
+ // Inside a setTimeout to avoid reentrancy issues.
+ setTimeout(() => {
+ document.getElementById('cmd_closeWindow').doCommand();
+ }, 100);
+ return this._createNewTabForWorkspace({ uuid: workspaceID });
+ }
+ return null;
+ }
+ } else if (tabsPinned.length === 1 && tabsPinned[0] === tab) {
+ return this._createNewTabForWorkspace({ uuid: workspaceID });
}
return null;
@@ -1247,7 +1267,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
this._changeListeners.push(func);
}
- async changeWorkspace(window, onInit = false) {
+ async changeWorkspace(window, ...args) {
if (!this.workspaceEnabled || this._inChangingWorkspace) {
return;
}
@@ -1255,14 +1275,14 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
await SessionStore.promiseInitialized;
this._inChangingWorkspace = true;
try {
- await this._performWorkspaceChange(window, onInit);
+ await this._performWorkspaceChange(window, ...args);
} finally {
this._inChangingWorkspace = false;
this.tabContainer.removeAttribute('dont-animate-tabs');
}
}
- async _performWorkspaceChange(window, onInit) {
+ async _performWorkspaceChange(window, { onInit = false, explicitAnimationDirection = undefined } = {}) {
const previousWorkspace = await this.getActiveWorkspace();
this.activeWorkspace = window.uuid;
@@ -1272,6 +1292,21 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
// Refresh tab cache
this.tabContainer._invalidateCachedTabs();
+ let animationDirection;
+ if (previousWorkspace && !onInit && !this._animatingChange) {
+ animationDirection =
+ explicitAnimationDirection ??
+ (workspaces.workspaces.findIndex((w) => w.uuid === previousWorkspace.uuid) <
+ workspaces.workspaces.findIndex((w) => w.uuid === window.uuid)
+ ? 'right'
+ : 'left');
+ }
+ if (animationDirection) {
+ // Animate tabs out of view before changing workspace, therefor we
+ // need to animate in the opposite direction
+ await this._animateTabs(animationDirection === 'left' ? 'right' : 'left', true);
+ }
+
// First pass: Handle tab visibility and workspace ID assignment
const visibleTabs = this._processTabVisibility(window.uuid, containerId, workspaces);
@@ -1281,23 +1316,47 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
// Update UI and state
await this._updateWorkspaceState(window, onInit);
- // Animate acordingly
- if (previousWorkspace && !this._animatingChange) {
- // we want to know if we are moving forward or backward in sense of animation
- let isNextWorkspace =
- onInit ||
- workspaces.workspaces.findIndex((w) => w.uuid === previousWorkspace.uuid) <
- workspaces.workspaces.findIndex((w) => w.uuid === window.uuid);
- gBrowser.tabContainer.setAttribute('zen-workspace-animation', isNextWorkspace ? 'next' : 'previous');
- this.tabContainer.removeAttribute('dont-animate-tabs');
- this._animatingChange = true;
- setTimeout(() => {
- this._animatingChange = false;
- gBrowser.tabContainer.removeAttribute('zen-workspace-animation');
- }, 600);
+ if (animationDirection) {
+ await this._animateTabs(animationDirection);
}
}
+ _animateElement(element, direction, out = false, callback) {
+ if (out) {
+ element.animate([{ transform: 'translateX(0)' }, { transform: `translateX(${direction === 'left' ? '-' : ''}100%)` }], {
+ duration: 100,
+ easing: 'ease',
+ fill: 'both',
+ }).onfinish = callback;
+ return;
+ }
+ element.animate([{ transform: `translateX(${direction === 'left' ? '-' : ''}100%)` }, { transform: 'translateX(0)' }], {
+ duration: 100,
+ easing: 'ease',
+ fill: 'both',
+ }).onfinish = callback;
+ }
+
+ async _animateTabs(direction, out = false) {
+ const tabs = gBrowser.visibleTabs.filter((tab) => !tab.hasAttribute('zen-essential'));
+ return new Promise((resolve) => {
+ let count = 0;
+ const onAnimationEnd = () => {
+ count++;
+ // +1 for the workspace indicator tab
+ if (count >= tabs.length + 1) {
+ resolve();
+ }
+ };
+ this.tabContainer.removeAttribute('dont-animate-tabs');
+ // Also animate the workspace indicator label
+ this._animateElement(document.getElementById('zen-current-workspace-indicator'), direction, out, () => onAnimationEnd());
+ for (const tab of tabs) {
+ this._animateElement(tab, direction, out, () => onAnimationEnd());
+ }
+ });
+ }
+
_processTabVisibility(workspaceUuid, containerId, workspaces) {
const visibleTabs = new Set();
const lastSelectedTab = this._lastSelectedWorkspaceTabs[workspaceUuid];
@@ -1524,7 +1583,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}
async onLocationChange(browser) {
- if (!this.workspaceEnabled || this._inChangingWorkspace) {
+ if (!this.workspaceEnabled || this._inChangingWorkspace || this._isClosingWindow) {
return;
}
@@ -1663,7 +1722,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}
let nextWorkspace = workspaces.workspaces[targetIndex];
- await this.changeWorkspace(nextWorkspace);
+ await this.changeWorkspace(nextWorkspace, { explicitAnimationDirection: offset > 0 ? 'right' : 'left' });
}
_initializeWorkspaceTabContextMenus() {
diff --git a/src/browser/components/places/content/browserPlacesViews-js.patch b/src/browser/components/places/content/browserPlacesViews-js.patch
index ff4699ac0..c4e00b026 100644
--- a/src/browser/components/places/content/browserPlacesViews-js.patch
+++ b/src/browser/components/places/content/browserPlacesViews-js.patch
@@ -1,5 +1,5 @@
diff --git a/browser/components/places/content/browserPlacesViews.js b/browser/components/places/content/browserPlacesViews.js
-index 1bfa0af16178c9b42172bc1b1e0249d28ff8e9e6..417a9dc4e55208bdc9c1422a3bae14361a4964c5 100644
+index 1bfa0af16178c9b42172bc1b1e0249d28ff8e9e6..cefe17335cb7cc1a22182f2e79c564237b9ae5e8 100644
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -330,12 +330,23 @@ class PlacesViewBase {
@@ -13,7 +13,7 @@ index 1bfa0af16178c9b42172bc1b1e0249d28ff8e9e6..417a9dc4e55208bdc9c1422a3bae1436
+ let child = resultNode.getChild(i);
+ // Skip nodes that don't belong in current workspace
+ if (PlacesUtils.nodeIsURI(child) || PlacesUtils.containerTypes.includes(child.type)) {
-+ if (ZenWorkspaces.isBookmarkInAnotherWorkspace(child)) {
++ if (typeof ZenWorkspaces !== 'undefined' && ZenWorkspaces.isBookmarkInAnotherWorkspace(child)) {
+ continue;
+ }
+ }
@@ -52,7 +52,7 @@ index 1bfa0af16178c9b42172bc1b1e0249d28ff8e9e6..417a9dc4e55208bdc9c1422a3bae1436
+ for (let i = 0; i < cc; i++) {
+ let child = this._resultNode.getChild(i);
+ if (PlacesUtils.nodeIsURI(child) || PlacesUtils.containerTypes.includes(child.type)) {
-+ if (!ZenWorkspaces.isBookmarkInAnotherWorkspace(child)) {
++ if (!(typeof ZenWorkspaces !== 'undefined' && ZenWorkspaces.isBookmarkInAnotherWorkspace(child))) {
+ visibleNodes.push(child);
+ }
+ } else {
diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch
index 954adf14b..fb735b872 100644
--- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch
+++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch
@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
-index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c593a60c7 100644
+index ce68c339f35416574b7bc7ebf8c93378f653242b..ceb292dd9939bd9db12b00673f0c3d54da793e76 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -409,11 +409,39 @@
@@ -53,15 +53,16 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
);
} else {
this.moveTabTo(aTab, this.pinnedTabCount);
-@@ -1052,6 +1080,7 @@
+@@ -1052,6 +1080,8 @@
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
+ try {
++ gZenPinnedTabManager.onTabIconChanged(aTab, aIconURL);
if (
aIconURL &&
!aLoadingPrincipal &&
-@@ -1062,6 +1091,9 @@
+@@ -1062,6 +1092,9 @@
);
return;
}
@@ -71,7 +72,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aIconURL;
-@@ -1291,6 +1323,7 @@
+@@ -1291,6 +1324,7 @@
if (!this._previewMode) {
newTab.recordTimeFromUnloadToReload();
newTab.updateLastAccessed();
@@ -79,7 +80,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
oldTab.updateLastAccessed();
// if this is the foreground window, update the last-seen timestamps.
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
-@@ -2374,7 +2407,7 @@
+@@ -2374,7 +2408,7 @@
let panel = this.getPanel(browser);
let uniqueId = this._generateUniquePanelID();
@@ -88,7 +89,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
aTab.linkedPanel = uniqueId;
// Inject the into the DOM if necessary.
-@@ -2434,7 +2467,7 @@
+@@ -2434,7 +2468,7 @@
// hasSiblings=false on both the existing browser and the new browser.
if (this.tabs.length == 2) {
this.tabs[0].linkedBrowser.browsingContext.hasSiblings = true;
@@ -97,7 +98,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
-@@ -2666,6 +2699,12 @@
+@@ -2666,6 +2700,12 @@
);
}
@@ -110,7 +111,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
if (!UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.start("browser.tabs.opening", "initting", window);
}
-@@ -2735,6 +2774,12 @@
+@@ -2735,6 +2775,12 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -123,7 +124,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
if (insertTab) {
// insert the tab into the tab container in the correct position
this._insertTabAtIndex(t, {
-@@ -2878,6 +2923,13 @@
+@@ -2878,6 +2924,13 @@
}
}
@@ -137,7 +138,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
// Additionally send pinned tab events
if (pinned) {
this._notifyPinnedStatus(t);
-@@ -3389,6 +3441,23 @@
+@@ -3389,6 +3442,23 @@
) {
tabWasReused = true;
tab = this.selectedTab;
@@ -161,7 +162,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
if (!tabData.pinned) {
this.unpinTab(tab);
} else {
-@@ -3402,6 +3471,9 @@
+@@ -3402,6 +3472,9 @@
restoreTabsLazily && !select && !tabData.pinned;
let url = "about:blank";
@@ -171,7 +172,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
if (tabData.entries?.length) {
let activeIndex = (tabData.index || tabData.entries.length) - 1;
// Ensure the index is in bounds.
-@@ -3438,6 +3510,21 @@
+@@ -3438,6 +3511,21 @@
preferredRemoteType,
});
@@ -193,7 +194,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
if (select) {
tabToSelect = tab;
}
-@@ -3491,7 +3578,6 @@
+@@ -3491,7 +3579,6 @@
this.tabContainer._invalidateCachedTabs();
}
}
@@ -201,7 +202,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
tab.initialize();
}
-@@ -4070,6 +4156,10 @@
+@@ -4070,6 +4157,10 @@
return;
}
@@ -212,7 +213,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
this.removeTabs(selectedTabs);
}
-@@ -4389,6 +4479,13 @@
+@@ -4389,6 +4480,13 @@
TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
}
@@ -226,7 +227,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
-@@ -4404,6 +4501,10 @@
+@@ -4404,6 +4502,10 @@
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
@@ -237,7 +238,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
-@@ -4556,7 +4657,7 @@
+@@ -4556,14 +4658,14 @@
!!this.tabsInCollapsedTabGroups.length;
if (
aTab.visible &&
@@ -246,7 +247,15 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
!anyRemainingTabsInCollapsedTabGroups
) {
closeWindow =
-@@ -5411,10 +5512,10 @@
+ closeWindowWithLastTab != null
+ ? closeWindowWithLastTab
+ : !window.toolbar.visible ||
+- Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
++ Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") && !ZenWorkspaces._isClosingWindow;
+
+ if (closeWindow) {
+ // We've already called beforeunload on all the relevant tabs if we get here,
+@@ -5411,10 +5513,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -259,7 +268,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
-@@ -7384,6 +7485,7 @@
+@@ -7384,6 +7486,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -267,7 +276,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
gBrowser.syncThrobberAnimations(this.mTab);
-@@ -8344,7 +8446,7 @@ var TabContextMenu = {
+@@ -8344,7 +8447,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !multiselectionContext;
@@ -276,7 +285,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
// Move Tab items
let contextMoveTabOptions = document.getElementById(
"context_moveTabOptions"
-@@ -8378,7 +8480,7 @@ var TabContextMenu = {
+@@ -8378,7 +8481,7 @@ var TabContextMenu = {
let contextMoveTabToStart = document.getElementById("context_moveToStart");
let isFirstTab =
tabsToMove[0] == visibleTabs[0] ||
@@ -285,7 +294,7 @@ index ce68c339f35416574b7bc7ebf8c93378f653242b..46d25e4381eaae71f3aec1025788684c
contextMoveTabToStart.disabled = isFirstTab && allSelectedTabsAdjacent;
document.getElementById("context_openTabInWindow").disabled =
-@@ -8607,6 +8709,7 @@ var TabContextMenu = {
+@@ -8607,6 +8710,7 @@ var TabContextMenu = {
if (this.contextTab.multiselected) {
gBrowser.removeMultiSelectedTabs();
} else {
diff --git a/src/browser/themes/linux/browser-css.patch b/src/browser/themes/linux/browser-css.patch
index 67bb2f585..8c5ec4c6e 100644
--- a/src/browser/themes/linux/browser-css.patch
+++ b/src/browser/themes/linux/browser-css.patch
@@ -1,8 +1,8 @@
diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css
-index a9276a678f16a67e2a003474203c37cb5c9300ad..892c69950d2a695b890fec9f8ea3f6e64032fe25 100644
+index a9276a678f16a67e2a003474203c37cb5c9300ad..20cb1b022f7a94ad553f5e6df48014ee646f93ed 100644
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
-@@ -42,15 +42,17 @@
+@@ -42,21 +42,25 @@
-moz-default-appearance: -moz-window-decorations;
appearance: auto;
@@ -23,3 +23,12 @@ index a9276a678f16a67e2a003474203c37cb5c9300ad..892c69950d2a695b890fec9f8ea3f6e6
/* The body clip below covers this. */
border-radius: 0;
}
+
+ body,
+- dialog::backdrop {
++ dialog::backdrop,
++ #browser::after,
++ #browser::before {
+ /* Use an uniform clip to allow WebRender to optimize it better */
+ border-radius: env(-moz-gtk-csd-titlebar-radius);
+ }
diff --git a/surfer.json b/surfer.json
index 634e05290..3f46ff71c 100644
--- a/surfer.json
+++ b/surfer.json
@@ -5,8 +5,8 @@
"binaryName": "zen",
"version": {
"product": "firefox",
- "version": "134.0",
- "candidate": "134.0"
+ "version": "134.0.1",
+ "candidate": "134.0.1"
},
"buildOptions": {
"generateBranding": true
@@ -19,7 +19,7 @@
"brandShortName": "Zen",
"brandFullName": "Zen Browser",
"release": {
- "displayVersion": "1.6b",
+ "displayVersion": "1.7b",
"github": {
"repo": "zen-browser/desktop"
},
@@ -47,7 +47,9 @@
}
},
"license": {
- "ignoredFiles": [".*\\.json"],
+ "ignoredFiles": [
+ ".*\\.json"
+ ],
"licenseType": "MPL-2.0"
},
"updateHostname": "updates.zen-browser.app"