diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5cd9cd53a..7a5ebc971 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -26,6 +26,11 @@ on:
required: true
type: "boolean"
default: false
+ disable-pgo:
+ description: "Build without PGO (skips the profile generation stages)"
+ required: false
+ type: "boolean"
+ default: false
workflow_call:
inputs:
create_release:
@@ -48,6 +53,11 @@ on:
required: true
type: "boolean"
default: false
+ disable-pgo:
+ description: "Build without PGO (skips the profile generation stages)"
+ required: false
+ type: "boolean"
+ default: false
jobs:
buildid:
@@ -73,6 +83,7 @@ jobs:
echo "create_release: ${{ inputs.create_release }}"
echo "update_version: ${{ inputs.update_version }}"
echo "use sccache: ${{ inputs.use-sccache }}"
+ echo "disable-pgo: ${{ inputs.disable-pgo }}"
echo "update_branch: ${{ inputs.update_branch }}"
echo "GITHUB_REPOSITORY: ${{ github.repository }}"
echo "GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}"
@@ -102,14 +113,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download aws-cli
- if: ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ if: ${{ inputs.update_branch == 'release' }}
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' }}
+ if: ${{ inputs.update_branch == 'release' }}
run: |
echo "Starting self-hosted runner"
echo "${{ secrets.SELF_HOSTED_RUNNER_START_SCRIPT }}" | base64 -d > start.sh
@@ -117,7 +128,7 @@ jobs:
bash ./start.sh
- name: Remove self-hosted runner script
- if: always() && ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ if: always() && ${{ inputs.update_branch == 'release' }}
run: |
echo "Removing self-hosted runner script"
rm start.sh || true
@@ -280,6 +291,7 @@ jobs:
windows-step-1:
name: Windows build step 1 (PGO build)
uses: ./.github/workflows/windows-release-build.yml
+ if: ${{ !inputs.disable-pgo }}
needs: [build-data, buildid]
permissions:
contents: write
@@ -295,6 +307,7 @@ jobs:
windows-step-2:
name: Windows build step 2 (Generate profile data)
uses: ./.github/workflows/windows-profile-build.yml
+ if: ${{ !inputs.disable-pgo }}
permissions:
contents: write
secrets: inherit
@@ -310,10 +323,13 @@ jobs:
permissions:
contents: write
secrets: inherit
+ # windows-step-2 is skipped when PGO is disabled, so don't inherit its skip
+ if: ${{ !failure() && !cancelled() }}
needs: [build-data, windows-step-2, start-self-host, buildid]
with:
build-version: ${{ needs.build-data.outputs.version }}
generate-pgo: false
+ disable-pgo: ${{ inputs.disable-pgo }}
release-branch: ${{ inputs.update_branch }}
MOZ_BUILD_DATE: ${{needs.buildid.outputs.buildids}}
use-sccache: ${{ inputs.use-sccache }}
@@ -330,10 +346,12 @@ jobs:
release-branch: ${{ inputs.update_branch }}
MOZ_BUILD_DATE: ${{needs.buildid.outputs.buildids}}
use-sccache: ${{ inputs.use-sccache }}
+ disable-pgo: ${{ inputs.disable-pgo }}
mac-step-1:
name: macOS build step 1 (PGO build)
uses: ./.github/workflows/macos-release-build.yml
+ if: ${{ !inputs.disable-pgo }}
needs: [build-data, buildid]
permissions:
contents: write
@@ -348,6 +366,7 @@ jobs:
mac-step-2:
name: macOS build step 2 (Generate profile data)
uses: ./.github/workflows/macos-profile-build.yml
+ if: ${{ !inputs.disable-pgo }}
permissions:
contents: write
secrets: inherit
@@ -362,10 +381,13 @@ jobs:
permissions:
contents: write
secrets: inherit
+ # mac-step-2 is skipped when PGO is disabled, so don't inherit its skip
+ if: ${{ !failure() && !cancelled() }}
needs: [build-data, mac-step-2, start-self-host, buildid]
with:
build-version: ${{ needs.build-data.outputs.version }}
generate-pgo: false
+ disable-pgo: ${{ inputs.disable-pgo }}
release-branch: ${{ inputs.update_branch }}
MOZ_BUILD_DATE: ${{needs.buildid.outputs.buildids}}
use-sccache: ${{ inputs.use-sccache }}
@@ -470,14 +492,14 @@ jobs:
if: always()
steps:
- name: Download aws-cli
- if: ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ if: ${{ inputs.update_branch == 'release' }}
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' }}
+ if: ${{ inputs.update_branch == 'release' }}
run: |
echo "Stopping self-hosted runner"
echo "${{ secrets.SELF_HOSTED_RUNNER_STOP_SCRIPT }}" | base64 -d > stop.sh
@@ -485,13 +507,12 @@ jobs:
bash ./stop.sh > /dev/null
- name: Remove self-hosted runner script
- if: always() && ${{ inputs.create_release && inputs.update_branch == 'release' }}
+ if: always() && ${{ 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:
@@ -585,6 +606,7 @@ jobs:
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
+ if: ${{ inputs.create_release || inputs.update_branch == 'twilight' }}
with:
commit_message: 🚀 Update update manifests
commit_user_name: Zen Browser Robot
@@ -628,9 +650,22 @@ jobs:
env:
GITHUB_REPOSITORY: ${{ github.repository }}
+ - name: Upload signed MAR files to workflow artifacts
+ if: ${{ inputs.update_branch == 'release' && !inputs.create_release }}
+ uses: actions/upload-artifact@v4
+ with:
+ retention-days: 5
+ name: signed-mar-files
+ path: |
+ ./linux.mar/*
+ ./linux-aarch64.mar/*
+ ./.github/workflows/object/windows-x64-signed-x86_64/windows.mar
+ ./.github/workflows/object/windows-x64-signed-arm64/windows-arm64.mar
+ ./macos.mar/*
+
- name: Release
uses: softprops/action-gh-release@v2.2.2
- if: ${{ inputs.update_branch == 'release' }}
+ if: ${{ inputs.update_branch == 'release' && inputs.create_release }}
with:
token: ${{ secrets.DEPLOY_KEY }}
tag_name: ${{ needs.build-data.outputs.version }}
diff --git a/.github/workflows/linux-release-build.yml b/.github/workflows/linux-release-build.yml
index baaea9dbf..4841eea82 100644
--- a/.github/workflows/linux-release-build.yml
+++ b/.github/workflows/linux-release-build.yml
@@ -20,6 +20,11 @@ on:
required: true
type: boolean
default: false
+ disable-pgo:
+ description: "Build without PGO at all"
+ required: false
+ type: boolean
+ default: false
jobs:
build-linux:
@@ -122,7 +127,7 @@ jobs:
cd ..
- name: Build (PGO stage 1 - generate)
- if: ${{ matrix.arch == 'x86_64' }}
+ if: ${{ matrix.arch == 'x86_64' && !inputs.disable-pgo }}
env:
SURFER_COMPAT: ${{ matrix.arch }}
ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }}
@@ -139,7 +144,7 @@ jobs:
bash .github/workflows/src/release-build.sh
- name: Generate PGO profile data
- if: ${{ matrix.arch == 'x86_64' }}
+ if: ${{ matrix.arch == 'x86_64' && !inputs.disable-pgo }}
run: |
set -x
export DISPLAY=:2
@@ -157,6 +162,7 @@ jobs:
env:
SURFER_COMPAT: ${{ matrix.arch }}
ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }}
+ ZEN_GA_DISABLE_PGO: ${{ inputs.disable-pgo && '1' || '' }}
ZEN_SAFEBROWSING_API_KEY: ${{ secrets.ZEN_SAFEBROWSING_API_KEY }}
ZEN_MOZILLA_API_KEY: ${{ secrets.ZEN_MOZILLA_API_KEY }}
ZEN_GOOGLE_LOCATION_SERVICE_API_KEY: ${{ secrets.ZEN_GOOGLE_LOCATION_SERVICE_API_KEY }}
diff --git a/.github/workflows/macos-release-build.yml b/.github/workflows/macos-release-build.yml
index 0423c566e..72d7f2ee9 100644
--- a/.github/workflows/macos-release-build.yml
+++ b/.github/workflows/macos-release-build.yml
@@ -10,6 +10,11 @@ on:
required: true
type: boolean
default: false
+ disable-pgo:
+ description: "Build without PGO at all"
+ required: false
+ type: boolean
+ default: false
build-version:
description: "The version to build"
required: true
@@ -140,13 +145,13 @@ jobs:
- name: Download artifact (if use profdata)
uses: actions/download-artifact@v4
- if: ${{ !inputs.generate-pgo }}
+ if: ${{ !inputs.generate-pgo && !inputs.disable-pgo }}
with:
path: ~/artifact
name: macos-profdata-${{ matrix.arch }}
- name: Show artifact info
- if: ${{ !inputs.generate-pgo }}
+ if: ${{ !inputs.generate-pgo && !inputs.disable-pgo }}
run: |
ls ~/artifact
ls ~/artifact/en-US.log
@@ -166,6 +171,9 @@ jobs:
if test ${{ inputs.generate-pgo }} = true; then
export ZEN_GA_GENERATE_PROFILE=1
fi
+ if test ${{ inputs.disable-pgo }} = true; then
+ export ZEN_GA_DISABLE_PGO=1
+ fi
if [[ -n ${{ inputs.MOZ_BUILD_DATE }} ]];then
export MOZ_BUILD_DATE=${{ inputs.MOZ_BUILD_DATE }}
fi
diff --git a/.github/workflows/windows-release-build.yml b/.github/workflows/windows-release-build.yml
index e27d50589..970636be3 100644
--- a/.github/workflows/windows-release-build.yml
+++ b/.github/workflows/windows-release-build.yml
@@ -10,6 +10,11 @@ on:
required: true
type: boolean
default: false
+ disable-pgo:
+ description: "Build without PGO at all"
+ required: false
+ type: boolean
+ default: false
build-version:
description: "The version to build"
required: true
@@ -215,13 +220,13 @@ jobs:
- name: Download artifact (if use profdata)
uses: actions/download-artifact@v4
- if: ${{ !inputs.generate-pgo && matrix.arch == 'x86_64' }}
+ if: ${{ !inputs.generate-pgo && !inputs.disable-pgo && matrix.arch == 'x86_64' }}
with:
path: ~/artifact
name: windows-profdata-${{ matrix.arch == 'aarch64' && 'arm64' || matrix.arch }}
- name: Show artifact info
- if: ${{ !inputs.generate-pgo && matrix.arch == 'x86_64' }}
+ if: ${{ !inputs.generate-pgo && !inputs.disable-pgo && matrix.arch == 'x86_64' }}
run: |
ls ~/artifact
ls ~/artifact/en-US.log
@@ -246,6 +251,9 @@ jobs:
if test ${{ inputs.generate-pgo }} = true; then
export ZEN_GA_GENERATE_PROFILE=1
fi
+ if test ${{ inputs.disable-pgo }} = true; then
+ export ZEN_GA_DISABLE_PGO=1
+ fi
if [[ -n ${{ inputs.MOZ_BUILD_DATE }} ]];then
export MOZ_BUILD_DATE=${{ inputs.MOZ_BUILD_DATE }}
fi
diff --git a/README.md b/README.md
index 434e58315..45db3aa51 100644
--- a/README.md
+++ b/README.md
@@ -34,8 +34,8 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne
### Firefox Versions
-- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `154.0.1`!
-- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 154.0.1`!
+- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `155.0`!
+- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 155.0`!
### Contributing
diff --git a/build/firefox-cache/l10n-last-commit-hash b/build/firefox-cache/l10n-last-commit-hash
index 2dbfec531..24e25043a 100644
--- a/build/firefox-cache/l10n-last-commit-hash
+++ b/build/firefox-cache/l10n-last-commit-hash
@@ -1 +1 @@
-bf4b57fd7fe94054457be7018e8e7beaf754c520
\ No newline at end of file
+20e5abbb642ab21d943bc1261864d9c531ba421a
\ No newline at end of file
diff --git a/locales/en-US/browser/browser/preferences/zen-preferences.ftl b/locales/en-US/browser/browser/preferences/zen-preferences.ftl
index 473bfb740..bb31d6c47 100644
--- a/locales/en-US/browser/browser/preferences/zen-preferences.ftl
+++ b/locales/en-US/browser/browser/preferences/zen-preferences.ftl
@@ -64,6 +64,10 @@ zen-tabs-select-recently-used-on-close =
zen-tabs-close-on-back-with-no-history =
.label = Close tab and switch to its owner tab (or most recently used tab) when going back with no history
+zen-settings-workspaces-sync =
+ .label = Sync your sidebar across devices
+ .description = Keep your workspaces, pinned tabs and folders in sync on all your devices through your Mozilla account.
+
zen-settings-workspaces-sync-unpinned-tabs =
.label = Sync only pinned tabs in workspaces
diff --git a/locales/en-US/browser/browser/zen-welcome.ftl b/locales/en-US/browser/browser/zen-welcome.ftl
index 1967595a9..c351f271a 100644
--- a/locales/en-US/browser/browser/zen-welcome.ftl
+++ b/locales/en-US/browser/browser/zen-welcome.ftl
@@ -5,29 +5,28 @@
zen-welcome-title-line1 = Welcome to
zen-welcome-title-line2 = a calmer internet
-zen-welcome-import-title = A Fresh Start, Same Bookmarks
-zen-welcome-import-description-1 = Your bookmarks, history, and passwords are like a trail of breadcrumbs through the internet—don’t leave them behind!
-zen-welcome-import-description-2 = Easily bring them over from another browser and pick up right where you left off.
-zen-welcome-import-button = Import now
+zen-welcome-back = Back
+zen-welcome-skip = Skip for now
-zen-welcome-set-default-browser = Set { -brand-short-name } as your default browser
-zen-welcome-dont-set-default-browser = DON’T set { -brand-short-name } as your default browser
+zen-welcome-import-title = Bring your stuff along.
+zen-welcome-import-description = Import your bookmarks, history and passwords from another browser and pick up right where you left off.
+zen-welcome-import-yes = YES, import from another browser.
+zen-welcome-import-no = NO, start fresh.
-zen-welcome-initial-essentials-title = Your Key Tabs, Always Within Reach
-zen-welcome-initial-essentials-description-1 = Keep your most important tabs easily accessible and always at hand, no matter how many you open.
-zen-welcome-initial-essentials-description-2 = Essential tabs are always visible, no matter what workspace you are in.
+zen-welcome-default-browser-title = Make { -brand-short-name } your default?
+zen-welcome-default-browser-description = Links from other apps will open in { -brand-short-name }. You can always change this later.
+zen-welcome-set-default-browser = YES, make { -brand-short-name } default.
+zen-welcome-dont-set-default-browser = NO, still exploring my options.
-zen-welcome-workspace-colors-title = Your Workspaces, Your Colors
-zen-welcome-workspace-colors-description = Personalize your browser by giving each workspace its own unique color identity.
-
-zen-welcome-start-browsing-title = All set?
- Let’s get rolling!
-zen-welcome-start-browsing-description-1 = You’re all set up and ready to go. Click the button below to start browsing with { -brand-short-name }.
-zen-welcome-start-browsing = Dive in!
-
-zen-welcome-default-search-title = Your Default Search Engine
+zen-welcome-default-search-title = Pick your search engine.
zen-welcome-default-search-description = Choose your default search engine. You can always change it later!
-zen-welcome-skip-button = Skip
+zen-welcome-essentials-title = Choose the apps you use most.
+zen-welcome-essentials-description = Select your favorite apps to keep them handy in your sidebar.
-zen-welcome-finished = Your Zen has been set up correctly!
+zen-welcome-workspace-colors-title = Your space, your colors.
+zen-welcome-workspace-colors-description = Personalize your browser by giving each space its own unique color identity.
+
+zen-welcome-start-browsing-title = All set? Let’s get rolling!
+zen-welcome-start-browsing-description-1 = You’re all set up and ready to go. Click the button below to start browsing with { -brand-short-name }.
+zen-welcome-start-browsing = Dive in!
diff --git a/package-lock.json b/package-lock.json
index 484c33d77..7a0bc5651 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
"devDependencies": {
"@babel/preset-typescript": "^7.27.0",
"@zen-browser/prettier": "^3.9.3",
- "@zen-browser/surfer": "^1.14.8",
+ "@zen-browser/surfer": "^1.14.9",
"formal-git": "^1.2.9",
"globals": "^16.3.0",
"husky": "^9.1.7",
@@ -860,9 +860,9 @@
}
},
"node_modules/@zen-browser/surfer": {
- "version": "1.14.8",
- "resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.14.8.tgz",
- "integrity": "sha512-5HHcfibwHIK5n3huhJL2TLBX2+8odj6ebCMA6U2EcUj9ROSrcz7/bjY2n5MIa9oIRL9gtV3ott0jwJj6SlapxA==",
+ "version": "1.14.9",
+ "resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.14.9.tgz",
+ "integrity": "sha512-XbMzG4GHSRoGzKR/DkpyywcbTYemieDRD1A6qgJ8CaVDixenj/uzMhp6HXAUksRTeh1u9j3lzNcVrI99axcZLw==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
diff --git a/package.json b/package.json
index c0ed6239c..9265a194b 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
"devDependencies": {
"@babel/preset-typescript": "^7.27.0",
"@zen-browser/prettier": "^3.9.3",
- "@zen-browser/surfer": "^1.14.8",
+ "@zen-browser/surfer": "^1.14.9",
"formal-git": "^1.2.9",
"globals": "^16.3.0",
"husky": "^9.1.7",
diff --git a/prefs/firefox/browser.yaml b/prefs/firefox/browser.yaml
index cd66abc63..92032c989 100644
--- a/prefs/firefox/browser.yaml
+++ b/prefs/firefox/browser.yaml
@@ -95,6 +95,9 @@
- name: browser.search.widget.new
value: true
+- name: layout.css.corner-shape.enabled
+ value: true
+
# Disabled from https://searchfox.org/firefox-main/rev/d6bfff43852356ca98af848b4705d37f8d41856f/modules/libpref/init/StaticPrefList.yaml#2008
# Only enabled for windows, doesn't really fit inside Zen.
- name: browser.startup.preXulSkeletonUI
diff --git a/prefs/zen/sync.yaml b/prefs/zen/sync.yaml
index 9ed0efc35..8424b23f8 100644
--- a/prefs/zen/sync.yaml
+++ b/prefs/zen/sync.yaml
@@ -3,13 +3,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: services.sync.engine.spaces
- value: true
+ value: "@cond"
condition: "@IS_TWILIGHT@"
-- name: services.sync.engine.spaces
- value: false
- locked: true
- condition: "!@IS_TWILIGHT@"
-
- name: zen.spaces-sync.debug
value: false
diff --git a/src/-stylelintrc-js.patch b/src/-stylelintrc-js.patch
index baadd6208..75a6e5999 100644
--- a/src/-stylelintrc-js.patch
+++ b/src/-stylelintrc-js.patch
@@ -1,5 +1,5 @@
diff --git a/.stylelintrc.js b/.stylelintrc.js
-index b484bf6600a7e1b8ad2aed8c8b7fe5e84cc3d023..f89712e2c13dfa0bcb48e55cd3872c67f5c98dad 100644
+index b5f8245fa04ffb8fc82240261d63f893f4629f19..1b6ead24adbe69fcb5aeb95cfd3e7fa9662d89d8 100644
--- a/.stylelintrc.js
+++ b/.stylelintrc.js
@@ -67,7 +67,7 @@ module.exports = {
@@ -11,10 +11,10 @@ index b484bf6600a7e1b8ad2aed8c8b7fe5e84cc3d023..f89712e2c13dfa0bcb48e55cd3872c67
{
ignore: ["blockless-at-rules"],
},
-@@ -280,7 +280,7 @@ module.exports = {
- "media-query-no-invalid": null,
+@@ -281,7 +281,7 @@ module.exports = {
"stylelint-plugin-mozilla/media-query-no-invalid": true,
"stylelint-plugin-mozilla/no-base-design-tokens": true,
+ "stylelint-plugin-mozilla/no-has-selector": true,
- "stylelint-plugin-mozilla/use-design-tokens": true,
+ "stylelint-plugin-mozilla/use-design-tokens": false,
},
diff --git a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch
index 38ad5946e..a9c01739c 100644
--- a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch
+++ b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch
@@ -19,7 +19,7 @@ index dd2f391096968564086911c532f696dc7ffb8ae1..c3e809a543397ab360fb83b3f61ef52d
+
-+
++
+
+
+
diff --git a/src/browser/base/content/zen-panels/site-data.inc b/src/browser/base/content/zen-panels/site-data.inc
index aa95fb9f8..2e798a4da 100644
--- a/src/browser/base/content/zen-panels/site-data.inc
+++ b/src/browser/base/content/zen-panels/site-data.inc
@@ -15,19 +15,23 @@
mainview-with-header="true">
`;
- const fragment = window.MozXULElement.parseXULToFragment(XUL);
- document.getElementById("browser").appendChild(fragment);
+ document.getElementById("browser").appendChild(parseXUL(XUL));
+ const video = document.getElementById("zen-welcome-video");
+ video.play().catch(() => {});
window.MozXULElement.insertFTLIfNeeded("browser/zen-welcome.ftl");
}
@@ -82,175 +104,212 @@
return data;
}
+ function createOption({ id, group, checked, l10n, text, extra }) {
+ const label = document.createElement("label");
+ label.className = "zen-welcome-option";
+ label.setAttribute("for", id);
+ const input = document.createElement("input");
+ input.type = "radio";
+ input.id = id;
+ input.name = group;
+ input.checked = !!checked;
+ label.appendChild(input);
+ const radio = document.createElement("span");
+ radio.className = "zen-welcome-option-radio no-squircles";
+ label.appendChild(radio);
+ if (extra) {
+ label.appendChild(extra);
+ }
+ const labelText = document.createElement("span");
+ labelText.className = "zen-welcome-option-label";
+ if (l10n) {
+ document.l10n.setAttributes(labelText, l10n);
+ } else {
+ labelText.textContent = text;
+ }
+ label.appendChild(labelText);
+ return label;
+ }
+
+ function removeVideoBackground() {
+ const video = document.getElementById("zen-welcome-video");
+ if (!video) {
+ return;
+ }
+ animate(video, { opacity: 0 }, { duration: 0.6, ease: "easeOut" }).then(
+ () => {
+ video.pause();
+ video.remove();
+ }
+ );
+ }
+
+ const kSpring = { type: "spring", bounce: 0.35, visualDuration: 0.35 };
+ const kExit = { duration: 0.12, ease: "easeIn" };
+ const kFade = { duration: 0.25, ease: "easeOut" };
+
class nsZenWelcomePages {
+ #index = -1;
+ #pages;
+ #content = null;
+ #renderToken = 0;
+ #buttonsToken = 0;
+
constructor(pages) {
- this._currentPage = -1;
- this._pages = pages;
+ this.#pages = pages;
this.init();
this.next();
}
+ get textContainer() {
+ return document.getElementById("zen-welcome-page-sidebar-content");
+ }
+
+ get contentContainer() {
+ return document.getElementById("zen-welcome-page-content");
+ }
+
+ get buttonsContainer() {
+ return document.getElementById("zen-welcome-page-sidebar-buttons");
+ }
+
+ get backButton() {
+ return document.getElementById("zen-welcome-back");
+ }
+
+ get currentPage() {
+ return this.#pages[this.#index];
+ }
+
+ get content() {
+ return this.#content;
+ }
+
init() {
- document.getElementById("zen-welcome-pages").style.display = "flex";
document.getElementById("zen-welcome-start").remove();
- window.maximize();
- animate(
- "#zen-welcome-pages",
- { opacity: [0, 1] },
- { delay: 0.2, duration: 0.1 }
- );
+ const pages = document.getElementById("zen-welcome-pages");
+ pages.style.display = "flex";
+ animate(pages, { opacity: [0, 1] }, { ...kFade, duration: 0.45 });
+ this.backButton.addEventListener("click", () => this.back());
}
- async fadeInTitles(page) {
- const [title1, description1, description2] =
- await document.l10n.formatValues(page.text);
- const titleElement = document.getElementById(
- "zen-welcome-page-sidebar-content"
- );
- /* eslint-disable no-unsanitized/property */
- titleElement.innerHTML =
- `${title1}${description1}` +
- (description2 ? `${description2}` : "");
- await animate(
- "#zen-welcome-page-sidebar-content > *",
- { x: ["150%", 0] },
- {
- delay: getMotion().stagger(0.05),
- type: "spring",
- bounce: 0.2,
- }
- );
+ next() {
+ this.currentPage?.commit?.(this.#content);
+ this.#show(this.#index + 1, 1);
}
- async fadeInButtons(page) {
- const buttons = document.getElementById(
- "zen-welcome-page-sidebar-buttons"
- );
- let i = 0;
- const insertedButtons = [];
- for (const button of page.buttons) {
- const buttonElement = document.createXULElement("button");
- document.l10n.setAttributes(buttonElement, button.l10n);
- if (i++ === 0) {
- buttonElement.classList.add("primary");
- }
- buttonElement.classList.add("footer-button");
- buttonElement.addEventListener("click", async () => {
- const shouldSkip = await button.onclick();
- if (shouldSkip) {
- this.next();
- }
- });
- buttonElement.style.pointerEvents = "none"; // Disable pointer events until animation is done
- insertedButtons.push(buttonElement);
- buttons.appendChild(buttonElement);
- }
- await animate(
- "#zen-welcome-page-sidebar-buttons button",
- { x: ["150%", 0] },
- {
- delay: getMotion().stagger(0.1, { startDelay: 0.4 }),
- type: "spring",
- bounce: 0.2,
- }
- );
- for (const button of insertedButtons) {
- button.style.pointerEvents = ""; // Enable pointer events after animation
+ back() {
+ if (this.#index <= 0) {
+ return;
}
+ this.#show(this.#index - 1, -1);
}
- async fadeInContent() {
- await animate(
- "#zen-welcome-page-content > *",
- { opacity: [0, 1] },
- {
- delay: getMotion().stagger(0.1),
- type: "spring",
- bounce: 0.2,
- }
- );
- }
-
- async fadeOutButtons() {
- await animate(
- "#zen-welcome-page-sidebar-buttons button",
- { x: [0, "-150%"] },
- {
- type: "spring",
- bounce: 0,
- delay: getMotion().stagger(0.1, { startDelay: 0.4 }),
- }
- );
- document.getElementById("zen-welcome-page-sidebar-buttons").innerHTML =
- "";
- document.getElementById("zen-welcome-page-sidebar-content").innerHTML =
- "";
- }
-
- async fadeOutTitles() {
- await animate(
- "#zen-welcome-page-sidebar-content > *",
- { x: [0, "-150%"] },
- {
- delay: getMotion().stagger(0.05, { startDelay: 0.3 }),
- type: "spring",
- bounce: 0,
- }
- );
- }
-
- async fadeOutContent() {
- await animate(
- "#zen-welcome-page-content > *",
- { opacity: [1, 0] },
- {
- delay: getMotion().stagger(0.05, { startDelay: 0.3 }),
- type: "spring",
- bounce: 0,
- duration: 0.1,
- }
- );
- }
-
- async next() {
- if (this._currentPage !== -1) {
- const previousPage = this._pages[this._currentPage];
- const promises = [this.fadeOutTitles(), this.fadeOutButtons()];
- if (!previousPage.dontFadeOut) {
- promises.push(this.fadeOutContent());
- }
- await Promise.all(promises);
- await previousPage.fadeOut();
- document.getElementById("zen-welcome-page-content").innerHTML = "";
- }
- this._currentPage++;
- const currentPage = this._pages[this._currentPage];
- if (!currentPage) {
+ #show(index, direction) {
+ const previous = this.currentPage;
+ this.#index = index;
+ const page = this.currentPage;
+ if (!page) {
this.finish();
return;
}
- await Promise.all([
- this.fadeInTitles(currentPage),
- this.fadeInButtons(currentPage),
- ]);
- await currentPage.fadeIn();
- await this.fadeInContent();
+ previous?.leave?.();
+ this.#exit(this.textContainer, { x: [0, -80 * direction] });
+ this.#exit(this.contentContainer, {});
+ this.backButton.toggleAttribute("disabled", index === 0);
+ this.updateButtons();
+ this.#renderText(page, direction);
+ this.#renderContent(page);
+ }
+
+ #exit(container, keyframes) {
+ for (const element of container.children) {
+ if (element.hasAttribute("exiting")) {
+ continue;
+ }
+ element.setAttribute("exiting", "");
+ animate(element, { opacity: [1, 0], ...keyframes }, kExit).then(() =>
+ element.remove()
+ );
+ }
+ }
+
+ async #renderText(page, direction) {
+ const token = ++this.#renderToken;
+ const text = document.createElement("div");
+ text.className = "zen-welcome-text";
+ const title = document.createElement("h1");
+ document.l10n.setAttributes(title, page.title);
+ text.appendChild(title);
+ for (const id of page.descriptions ?? []) {
+ const p = document.createElement("p");
+ document.l10n.setAttributes(p, id);
+ text.appendChild(p);
+ }
+ await document.l10n.translateFragment(text);
+ if (token !== this.#renderToken) {
+ return;
+ }
+ this.textContainer.appendChild(text);
+ animate(
+ [...text.children],
+ { opacity: [0, 1], x: [120 * direction, 0] },
+ {
+ ...kSpring,
+ bounce: 0.4,
+ visualDuration: 0.3,
+ delay: getMotion().stagger(0.03),
+ }
+ );
+ }
+
+ #renderContent(page) {
+ const content = document.createElement("div");
+ content.className = "zen-welcome-page";
+ if (page.id) {
+ content.setAttribute("page", page.id);
+ }
+ page.render(content);
+ this.contentContainer.appendChild(content);
+ this.#content = content;
+ animate(content, { opacity: [0, 1] }, kFade);
+ }
+
+ async updateButtons() {
+ const token = ++this.#buttonsToken;
+ const fragment = document.createDocumentFragment();
+ for (const button of this.currentPage.buttons) {
+ const element = document.createElement("button");
+ element.className = button.primary
+ ? "zen-welcome-button primary"
+ : "zen-welcome-button";
+ document.l10n.setAttributes(element, button.l10n);
+ element.addEventListener("click", () => {
+ if (button.onclick?.(this) !== false) {
+ this.next();
+ }
+ });
+ fragment.appendChild(element);
+ }
+ await document.l10n.translateFragment(fragment);
+ if (token === this.#buttonsToken) {
+ this.buttonsContainer.replaceChildren(fragment);
+ }
}
async finish() {
- _iconToData = undefined; // Unload icon data
+ ++this.#buttonsToken;
+ this.buttonsContainer.replaceChildren();
+ this.backButton.toggleAttribute("disabled", true);
gZenWorkspaces.reorganizeTabsAfterWelcome();
- await animate(
- "#zen-welcome-page-content",
- { x: [0, "100%"] },
- { bounce: 0 }
- );
- document.getElementById("zen-welcome-page-content").remove();
- await this.animHeart();
- await this.#pinRemainingTabs();
- await animate("#zen-welcome-pages", { opacity: [1, 0] });
+ const promises = [this.#applyChoices()];
+ await animate("#zen-welcome", { opacity: [1, 0] });
+ this.contentContainer.remove();
+ await Promise.all(promises);
+ _iconToData = undefined; // Unload icon data
document.getElementById("zen-welcome").remove();
document.documentElement.removeAttribute("zen-welcome-stage");
+ unlockWindowSize();
for (const element of document.getElementById("browser").children) {
if (kZenElementsToIgnore.includes(element.id)) {
continue;
@@ -265,14 +324,10 @@
await animate(`#browser > *:not(${elementsToIgnore})`, {
opacity: [0, 1],
});
- gZenUIManager.showToast("zen-welcome-finished");
}
- async #pinRemainingTabs() {
- for (const tab of _tabsToPinEssentials) {
- tab.removeAttribute("pending"); // Make it appear loaded
- gZenPinnedTabManager.addToEssentials(tab);
- }
+ async #applyChoices() {
+ await this.#pinEssentials();
let tabsToGroup = [];
if (!gBrowser.selectedTab.hasAttribute("zen-empty-tab")) {
tabsToGroup.push(gBrowser.selectedTab);
@@ -283,22 +338,39 @@
});
}
- async animHeart() {
- const heart = document.createElement("div");
- heart.id = "zen-welcome-heart";
- const sidebar = document.getElementById("zen-welcome-page-sidebar");
- sidebar.style.width = "100%";
- sidebar.appendChild(heart);
- sidebar.setAttribute("animate-heart", "true");
- await animate(
- "#zen-welcome-heart",
- { opacity: [0, 1, 1, 1, 0], scale: [0.5, 1, 1.2, 1, 1.2] },
- {
- duration: 1.5,
- delay: 0.2,
- bounce: 0,
- }
+ async #pinEssentials() {
+ const apps = kEssentialApps.filter(app =>
+ gChoices.essentials.has(app.url)
);
+ if (!apps.length) {
+ return;
+ }
+ await PlacesUtils.history.insertMany(
+ apps.map(app => ({
+ url: app.url,
+ visits: [{ transition: PlacesUtils.history.TRANSITIONS.TYPED }],
+ }))
+ );
+ const { TabStateCache } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/TabStateCache.sys.mjs"
+ );
+ for (const app of apps) {
+ const tab = window.gBrowser.addTrustedTab(app.url, {
+ inBackground: true,
+ createLazyBrowser: true,
+ });
+ const icon = await getIconData(
+ `chrome://browser/content/zen-images/favicons/${app.icon}.svg`
+ );
+ // Update the persistent tab state cache with |tabData| information.
+ TabStateCache.update(tab.linkedBrowser.permanentKey, {
+ history: { entries: [{ url: app.url }], index: 0 },
+ image: icon,
+ });
+ gBrowser.setIcon(tab, icon);
+ tab.removeAttribute("pending"); // Make it appear loaded
+ gZenPinnedTabManager.addToEssentials(tab);
+ }
}
}
@@ -362,363 +434,230 @@
}
}
+ const kNextButton = { l10n: "zen-generic-next", primary: true };
+
+ async function setDefaultBrowser() {
+ const shellSvc =
+ AppConstants.HAVE_SHELL_SERVICE && window.getShellService();
+ if (!shellSvc) {
+ return;
+ }
+ try {
+ await shellSvc.setDefaultBrowser(false);
+ } catch (ex) {
+ console.error(ex);
+ }
+ }
+
function getWelcomePages() {
return [
{
- text: [
- {
- id: "zen-welcome-import-title",
- },
- {
- id: "zen-welcome-import-description-1",
- },
- {
- id: "zen-welcome-import-description-2",
- },
- ],
- buttons: [
- {
- l10n: "zen-welcome-import-button",
- onclick: async () => {
- MigrationUtils.showMigrationWizard(window, {
- isStartupMigration: true,
- });
- document
- .querySelector("#zen-welcome-page-sidebar-buttons button")
- .remove();
- const newButton = document.querySelector(
- "#zen-welcome-page-sidebar-buttons button"
- );
- newButton.classList.add("primary");
- document.l10n.setAttributes(newButton, "zen-generic-next");
- return false;
- },
- },
- {
- l10n: "zen-welcome-skip-button",
- onclick: async () => {
- return true;
- },
- },
- ],
- fadeIn() {
- const xul = `
-
-
-
-
-
-
-
-
- `;
- const fragment = window.MozXULElement.parseXULToFragment(xul);
- document
- .getElementById("zen-welcome-page-content")
- .appendChild(fragment);
- },
- async fadeOut() {
- const shouldSetDefault = document.getElementById(
- "zen-welcome-set-default-browser"
- ).checked;
- if (AppConstants.HAVE_SHELL_SERVICE && shouldSetDefault) {
- let shellSvc = window.getShellService();
- if (!shellSvc) {
- return;
- }
-
- try {
- await shellSvc.setDefaultBrowser(false);
- } catch (ex) {
- console.error(ex);
- }
- }
- },
- },
- {
- text: [
- {
- id: "zen-welcome-default-search-title",
- },
- {
- id: "zen-welcome-default-search-description",
- },
- ],
- buttons: [
- {
- l10n: "zen-generic-next",
- onclick: async () => {
- return true;
- },
- },
- ],
- async fadeIn() {
- const content = document.getElementById("zen-welcome-page-content");
- const engineStore = new ZenSearchEngineStore();
- engineStore.init();
-
- content.setAttribute("select-engine", "true");
-
- const defaultEngine = await lazy.SearchService.getDefault();
- const promises = [];
- engineStore.getEngines().forEach(engine => {
- const label = document.createElement("label");
- const engineId = engine.name.replace(/\s+/g, "-").toLowerCase();
- label.setAttribute("for", engineId);
- const input = document.createElement("input");
- input.setAttribute("type", "radio");
- input.setAttribute("id", engineId);
- input.setAttribute("name", "zen-welcome-set-default-browser");
- input.setAttribute("hidden", "true");
- if (engine.name === defaultEngine.name) {
- input.setAttribute("checked", true);
- }
- label.appendChild(input);
- const engineLabel = document.createXULElement("label");
- engineLabel.textContent = engine.name;
- const icon = document.createElement("img");
- promises.push(
- (async () => {
- icon.setAttribute(
- "src",
- await engine.originalEngine.getIconURL()
- );
- })()
- );
- icon.setAttribute("width", "32");
- icon.setAttribute("height", "32");
- icon.setAttribute("class", "engine-icon");
- label.appendChild(icon);
- label.appendChild(engineLabel);
- content.appendChild(label);
- label.addEventListener("click", async () => {
- const selectedEngine = engineStore.getEngineByName(engine.name);
- if (selectedEngine) {
- await engineStore.setDefaultEngine(selectedEngine);
- }
- });
+ id: "import",
+ title: "zen-welcome-import-title",
+ descriptions: ["zen-welcome-import-description"],
+ buttons: [kNextButton],
+ render(content) {
+ const yes = createOption({
+ id: "zen-welcome-import-yes",
+ group: "zen-welcome-import",
+ l10n: "zen-welcome-import-yes",
});
- await Promise.all(promises);
- },
- async fadeOut() {
- document
- .getElementById("zen-welcome-page-content")
- .removeAttribute("select-engine");
- },
- },
- {
- text: [
- {
- id: "zen-welcome-initial-essentials-title",
- },
- {
- id: "zen-welcome-initial-essentials-description-1",
- },
- {
- id: "zen-welcome-initial-essentials-description-2",
- },
- ],
- buttons: [
- {
- l10n: "zen-generic-next",
- onclick: async () => {
- return true;
- },
- },
- ],
- fadeIn() {
- const xul = `
-
-
-
- `;
- const fragment = window.MozXULElement.parseXULToFragment(xul);
- document
- .getElementById("zen-welcome-page-content")
- .appendChild(fragment);
- document
- .getElementById(
- "zen-welcome-initial-essentials-browser-sidebar-essentials"
- )
- .addEventListener("click", async event => {
- const tab = event.target.closest(".tabbrowser-tab");
- if (!tab) {
- return;
- }
- tab.toggleAttribute("visuallyselected");
- });
- },
- async fadeOut() {
- const selectedTabs = document
- .getElementById(
- "zen-welcome-initial-essentials-browser-sidebar-essentials"
- )
- .querySelectorAll(".tabbrowser-tab[visuallyselected]");
-
- if (selectedTabs.length) {
- await PlacesUtils.history.insertMany(
- [...selectedTabs].map(tab => ({
- url: tab.getAttribute("data-url"),
- visits: [
- {
- transition: PlacesUtils.history.TRANSITIONS.TYPED,
- },
- ],
- }))
- );
- }
-
- const { TabStateCache } = ChromeUtils.importESModule(
- "resource:///modules/sessionstore/TabStateCache.sys.mjs"
+ content.appendChild(yes);
+ content.appendChild(
+ createOption({
+ id: "zen-welcome-import-no",
+ group: "zen-welcome-import",
+ l10n: "zen-welcome-import-no",
+ checked: true,
+ })
);
- for (const tab of selectedTabs) {
- const url = tab.getAttribute("data-url");
- const createdTab = window.gBrowser.addTrustedTab(url, {
- inBackground: true,
- createLazyBrowser: true,
+ },
+ commit(content) {
+ if (content.querySelector("#zen-welcome-import-yes").checked) {
+ MigrationUtils.showMigrationWizard(window, {
+ isStartupMigration: true,
});
- let essentialIconUrl = tab.style.getPropertyValue(
- "--zen-essential-tab-icon"
- );
- // Remove url() from the icon URL
- essentialIconUrl = essentialIconUrl
- .replace(/url\(['"]?/, "")
- .replace(/['"]?\)/, "");
- essentialIconUrl = await getIconData(essentialIconUrl);
- // Update the persistent tab state cache with |tabData| information.
- TabStateCache.update(createdTab.linkedBrowser.permanentKey, {
- history: { entries: [{ url }], index: 0 },
- image: essentialIconUrl,
- });
- gBrowser.setIcon(createdTab, essentialIconUrl);
- _tabsToPinEssentials.push(createdTab);
}
},
},
{
- text: [
- {
- id: "zen-welcome-workspace-colors-title",
- },
- {
- id: "zen-welcome-workspace-colors-description",
- },
- ],
- buttons: [
- {
- l10n: "zen-generic-next",
- onclick: async () => {
- return true;
- },
- },
- ],
- fadeIn() {
+ id: "colors",
+ title: "zen-welcome-workspace-colors-title",
+ descriptions: ["zen-welcome-workspace-colors-description"],
+ buttons: [kNextButton],
+ render(content) {
+ removeVideoBackground();
const anchor = document.createElement("div");
anchor.id = "zen-welcome-workspace-colors-anchor";
- document
- .getElementById("zen-welcome-page-content")
- .appendChild(anchor);
- gZenThemePicker.panel.setAttribute("noautohide", "true");
- gZenThemePicker.panel.setAttribute("consumeoutsideclicks", "false");
- gZenThemePicker.panel.setAttribute("nonnative", "");
- gZenThemePicker.panel.addEventListener(
+ content.appendChild(anchor);
+ const panel = gZenThemePicker.panel;
+ panel.setAttribute("noautohide", "true");
+ panel.setAttribute("consumeoutsideclicks", "false");
+ panel.setAttribute("nonnative", "");
+ panel.addEventListener(
"popupshowing",
() => {
- const panelRect = gZenThemePicker.panel.getBoundingClientRect();
+ const panelRect = panel.getBoundingClientRect();
// 20 is the shadow width * 2
- anchor.style.height = panelRect.height - 20 + "px";
+ anchor.style.height =
+ panelRect.height -
+ (AppConstants.platform == "macosx" ? -90 : 20) +
+ "px";
anchor.style.width = panelRect.width - 20 + "px";
},
{ once: true }
);
- PanelMultiView.openPopup(gZenThemePicker.panel, anchor, {
- position: "overlap",
- });
+ PanelMultiView.openPopup(panel, anchor, { position: "overlap" });
},
- dontFadeOut: true,
- async fadeOut() {
- gZenThemePicker.panel.removeAttribute("noautohide");
- gZenThemePicker.panel.removeAttribute("consumeoutsideclicks");
- gZenThemePicker.panel.removeAttribute("nonnative");
- await animate(gZenThemePicker.panel, { opacity: [1, 0] });
- gZenThemePicker.panel.hidePopup();
- gZenThemePicker.panel.removeAttribute("style");
+ leave() {
+ const panel = gZenThemePicker.panel;
+ panel.removeAttribute("noautohide");
+ panel.removeAttribute("consumeoutsideclicks");
+ panel.removeAttribute("nonnative");
+ animate(panel, { opacity: [1, 0] }, kExit).then(() => {
+ panel.hidePopup();
+ panel.removeAttribute("style");
+ });
},
},
{
- text: [
- {
- id: "zen-welcome-start-browsing-title",
- },
- {
- id: "zen-welcome-start-browsing-description-1",
- },
- ],
+ id: "search",
+ title: "zen-welcome-default-search-title",
+ descriptions: ["zen-welcome-default-search-description"],
+ buttons: [kNextButton],
+ async render(content) {
+ const engineStore = new ZenSearchEngineStore();
+ await engineStore.init();
+ const defaultEngine = await lazy.SearchService.getDefault();
+ for (const engine of engineStore.getEngines()) {
+ const iconWrapper = document.createElement("div");
+ iconWrapper.className = "engine-icon-wrapper";
+ const iconBackdrop = document.createElement("img");
+ iconBackdrop.className = "engine-icon-backdrop";
+ const icon = document.createElement("img");
+ icon.className = "engine-icon";
+ icon.width = icon.height = 40;
+ iconWrapper.append(iconBackdrop, icon);
+ engine.originalEngine.getIconURL(96).then(url => {
+ icon.src = url;
+ iconBackdrop.src = url;
+ });
+ const option = createOption({
+ id: "zen-welcome-engine-" + engine.name.replace(/\s+/g, "-"),
+ group: "zen-welcome-search-engine",
+ text: engine.name,
+ checked: engine.name === defaultEngine.name,
+ extra: iconWrapper,
+ });
+ option
+ .querySelector("input")
+ .addEventListener("change", () =>
+ engineStore.setDefaultEngine(engine)
+ );
+ content.appendChild(option);
+ }
+ },
+ },
+ {
+ id: "essentials",
+ title: "zen-welcome-essentials-title",
+ descriptions: ["zen-welcome-essentials-description"],
buttons: [
+ kNextButton,
{
- l10n: "zen-welcome-start-browsing",
- onclick: async () => {
- return true;
+ l10n: "zen-welcome-skip",
+ onclick(pages) {
+ for (const button of pages.content.querySelectorAll(
+ ".zen-welcome-essential[selected]"
+ )) {
+ button.removeAttribute("selected");
+ }
},
},
],
- fadeIn() {},
- fadeOut() {},
+ render(content) {
+ content.appendChild(
+ parseXUL(`
+
+
+
+ `)
+ );
+ const grid = content.querySelector("#zen-welcome-essentials");
+ for (const app of kEssentialApps) {
+ const button = document.createElement("button");
+ button.className = "zen-welcome-essential";
+ button.dataset.url = app.url;
+ button.style.setProperty(
+ "--zen-welcome-app-icon",
+ `url("chrome://browser/content/zen-images/favicons/${app.icon}.svg")`
+ );
+ button.style.setProperty("--zen-welcome-app-color", app.color);
+ button.toggleAttribute(
+ "selected",
+ gChoices.essentials.has(app.url)
+ );
+ button.addEventListener("click", () => {
+ button.toggleAttribute("selected");
+ });
+ grid.appendChild(button);
+ }
+ },
+ commit(content) {
+ gChoices.essentials = new Set(
+ [
+ ...content.querySelectorAll(".zen-welcome-essential[selected]"),
+ ].map(button => button.dataset.url)
+ );
+ },
+ },
+ {
+ id: "default-browser",
+ title: "zen-welcome-default-browser-title",
+ descriptions: ["zen-welcome-default-browser-description"],
+ buttons: [kNextButton],
+ render(content) {
+ content.appendChild(
+ createOption({
+ id: "zen-welcome-set-default-browser",
+ group: "zen-welcome-default-browser",
+ l10n: "zen-welcome-set-default-browser",
+ checked: gChoices.setDefaultBrowser,
+ })
+ );
+ content.appendChild(
+ createOption({
+ id: "zen-welcome-dont-set-default-browser",
+ group: "zen-welcome-default-browser",
+ l10n: "zen-welcome-dont-set-default-browser",
+ checked: !gChoices.setDefaultBrowser,
+ })
+ );
+ },
+ commit(content) {
+ gChoices.setDefaultBrowser = content.querySelector(
+ "#zen-welcome-set-default-browser"
+ ).checked;
+ if (gChoices.setDefaultBrowser) {
+ setDefaultBrowser();
+ }
+ },
+ },
+ {
+ id: "finish",
+ title: "zen-welcome-start-browsing-title",
+ descriptions: ["zen-welcome-start-browsing-description-1"],
+ buttons: [{ l10n: "zen-welcome-start-browsing", primary: true }],
+ render() {},
},
];
}
@@ -729,33 +668,48 @@
{ id: "zen-welcome-title-line2" },
]);
const titleElement = document.getElementById("zen-welcome-title");
- /* eslint-disable no-unsanitized/property */
- titleElement.innerHTML = `${title1}${title2}`;
+ for (const line of [title1, title2]) {
+ const lineElement = document.createElement("span");
+ for (const char of line) {
+ if (char === " ") {
+ lineElement.append(" ");
+ continue;
+ }
+ const charElement = document.createElement("span");
+ charElement.className = "zen-welcome-char";
+ charElement.textContent = char;
+ lineElement.appendChild(charElement);
+ }
+ titleElement.appendChild(lineElement);
+ }
+ const chars = titleElement.querySelectorAll(".zen-welcome-char");
await animate(
- "#zen-welcome-title span",
- { opacity: [0, 1], y: [20, 0], filter: ["blur(2px)", "blur(0px)"] },
+ chars,
+ { opacity: [0, 1], y: [50, 0] },
{
- delay: getMotion().stagger(0.6, { startDelay: 0.2 }),
+ delay: getMotion().stagger(0.035, { startDelay: 0.8 }),
type: "spring",
- stiffness: 300,
- damping: 20,
- mass: 1.8,
+ bounce: 0.3,
+ visualDuration: 0.45,
}
);
const button = document.getElementById("zen-welcome-start-button");
- button.addEventListener("click", async () => {
- await animate(
- "#zen-welcome-title span, #zen-welcome-start-button",
- { opacity: [1, 0], y: [0, -10], filter: ["blur(0px)", "blur(2px)"] },
- {
- type: "spring",
- ease: [0.755, 0.05, 0.855, 0.06],
- bounce: 0.4,
- delay: getMotion().stagger(0.4),
- }
- );
- new nsZenWelcomePages(getWelcomePages());
- });
+ button.addEventListener(
+ "click",
+ async () => {
+ await animate(
+ "#zen-welcome-title .zen-welcome-char, #zen-welcome-start-button",
+ { opacity: [1, 0], y: [0, -14] },
+ {
+ duration: 0.3,
+ ease: "easeIn",
+ delay: getMotion().stagger(0.012),
+ }
+ );
+ new nsZenWelcomePages(getWelcomePages());
+ },
+ { once: true }
+ );
await animate(
button,
{ opacity: [0, 1], y: [20, 0], filter: ["blur(2px)", "blur(0px)"] },
@@ -769,20 +723,45 @@
);
}
+ const kSizeLockProperties = [
+ "min-width",
+ "max-width",
+ "min-height",
+ "max-height",
+ ];
+
+ function lockWindowSize(width, height) {
+ const style = document.documentElement.style;
+ style.setProperty("min-width", `${width}px`, "important");
+ style.setProperty("max-width", `${width}px`, "important");
+ style.setProperty("min-height", `${height}px`, "important");
+ style.setProperty("max-height", `${height}px`, "important");
+ }
+
+ function unlockWindowSize() {
+ const style = document.documentElement.style;
+ for (const property of kSizeLockProperties) {
+ style.removeProperty(property);
+ }
+ }
+
function centerWindowOnScreen() {
window.addEventListener(
"MozAfterPaint",
function () {
- window.resizeTo(875, 560);
+ const width = Math.min(1200, screen.availWidth);
+ const height = Math.min(720, screen.availHeight);
+ window.resizeTo(width, height);
window.focus();
const appWin = window.docShell.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIAppWindow);
appWin.rollupAllPopups();
window.moveTo(
- screen.availLeft + (screen.availWidth - outerWidth) / 2,
- screen.availTop + (screen.availHeight - outerHeight) / 2
+ screen.availLeft + (screen.availWidth - width) / 2,
+ screen.availTop + (screen.availHeight - height) / 2
);
+ lockWindowSize(width, height);
},
{ once: true }
);
diff --git a/src/zen/welcome/jar.inc.mn b/src/zen/welcome/jar.inc.mn
index 7295cfbaf..dc8cc2305 100644
--- a/src/zen/welcome/jar.inc.mn
+++ b/src/zen/welcome/jar.inc.mn
@@ -4,3 +4,4 @@
content/browser/zen-components/ZenWelcome.mjs (../../zen/welcome/ZenWelcome.mjs)
content/browser/zen-styles/zen-welcome.css (../../zen/welcome/zen-welcome.css)
+ content/browser/zen-videos/welcome-background.mp4 (../../zen/welcome/welcome-background.mp4)
diff --git a/src/zen/welcome/welcome-background.mp4 b/src/zen/welcome/welcome-background.mp4
new file mode 100644
index 000000000..6619322d8
Binary files /dev/null and b/src/zen/welcome/welcome-background.mp4 differ
diff --git a/src/zen/welcome/zen-welcome.css b/src/zen/welcome/zen-welcome.css
index 54d2c7f10..6b697eb24 100644
--- a/src/zen/welcome/zen-welcome.css
+++ b/src/zen/welcome/zen-welcome.css
@@ -15,6 +15,21 @@
-moz-window-dragging: drag;
}
+#zen-welcome {
+ position: relative;
+ isolation: isolate;
+}
+
+#zen-welcome-video {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ pointer-events: none;
+ will-change: opacity;
+}
+
:root[zen-welcome-stage] {
min-width: 875px;
min-height: 560px;
@@ -24,32 +39,111 @@
}
}
+#zen-welcome {
+ --zen-welcome-accent: light-dark(
+ oklch(from var(--zen-primary-color) clamp(0.35, l, 0.6) c h),
+ oklch(from var(--zen-primary-color) clamp(0.7, l, 0.85) c h)
+ );
+ --zen-welcome-accent-button: light-dark(
+ oklch(from var(--zen-primary-color) clamp(0.35, l, 0.6) c h),
+ oklch(from var(--zen-primary-color) clamp(0.55, l, 0.7) c h)
+ );
+ --zen-welcome-sidebar-bg: light-dark(#ffffff, #1c1c1e);
+ --zen-welcome-content-bg: light-dark(#f6f6f8f5, #2b2b30ed);
+ --zen-welcome-surface-bg: light-dark(#ffffff, #1c1c1e);
+ --zen-welcome-tile-bg: light-dark(#ececee, #333338);
+ --zen-welcome-border: light-dark(
+ rgba(0, 0, 0, 0.14),
+ rgba(255, 255, 255, 0.16)
+ );
+ --zen-welcome-text-secondary: light-dark(
+ rgba(0, 0, 0, 0.6),
+ rgba(255, 255, 255, 0.6)
+ );
+}
+
+.zen-welcome-button {
+ appearance: none;
+ border: none;
+ border-radius: 12px;
+ padding: 1.2rem 1rem;
+ background: transparent;
+ color: var(--zen-welcome-accent);
+ font: inherit;
+ font-size: 1.2rem;
+ font-weight: 600;
+ cursor: pointer;
+ will-change: transform;
+ transition: scale 0.15s ease-out;
+
+ &:hover {
+ scale: 1.03;
+ filter: brightness(1.1);
+ }
+
+ &:active {
+ scale: 1;
+ }
+
+ &.primary {
+ background: var(--zen-welcome-accent-button);
+ color: white;
+
+ &:hover {
+ filter: brightness(1.1);
+ }
+ }
+}
+
#zen-welcome-start {
flex-direction: column;
-moz-window-dragging: drag;
- --zen-primary-color: light-dark(black, white);
+ color: white;
#zen-welcome-start-button {
opacity: 0;
list-style-image: url(chrome://browser/skin/zen-icons/forward.svg);
- position: absolute;
- bottom: 10%;
padding: 1em !important;
min-width: 100px;
+ background: rgb(62, 115, 62) !important;
+ fill: white !important;
+
+ will-change: transform;
+ transition: scale 0.15s ease-out;
+
+ &:hover {
+ scale: 1.03;
+ filter: brightness(1.1);
+ }
+
+ &:active {
+ scale: 1;
+ transform: none;
+ }
}
#zen-welcome-title {
text-align: center;
- font-size: 5rem;
- line-height: 1.1;
+ font-size: 5.2rem;
+ font-weight: 700;
+ line-height: 1.05;
+ letter-spacing: -0.03em;
max-width: 50%;
- font-weight: 500;
white-space: nowrap;
+ /* Layered (transformed) glyphs are drawn with grayscale AA on macOS;
+ * use it for the whole title so animating characters don't look bolder. */
+ -moz-osx-font-smoothing: grayscale;
+ transform: translateY(-5%);
& > span {
display: block;
+ }
+
+ .zen-welcome-char {
+ display: inline-block;
opacity: 0;
+ will-change: transform;
}
}
}
@@ -57,28 +151,18 @@
#zen-welcome-pages {
-moz-window-dragging: no-drag;
opacity: 0;
- justify-content: start;
- align-items: start;
display: none;
- background: var(--zen-branding-bg);
- border-radius: 1em;
+ justify-content: start;
+ align-items: stretch;
+ background: var(--zen-welcome-content-bg);
+ color: light-dark(#111111, #ffffff);
+ border-radius: 1.4rem;
position: relative;
- width: 60%;
- height: 60%;
- box-shadow: var(--zen-big-shadow);
+ width: 86%;
+ height: 82%;
+ box-shadow: 0 3px 8px rgba(0,0,0,.05);
overflow: hidden;
- /* Small screens */
- @media (max-width: 1400px) {
- width: 80%;
- height: 80%;
- }
-
- @media (max-width: 1200px) {
- width: 90%;
- height: 90%;
- }
-
@media (max-width: 1000px) {
width: 100%;
height: 100%;
@@ -86,216 +170,339 @@
}
#zen-welcome-page-sidebar {
+ position: relative;
+ z-index: 1;
+ display: flex;
flex-direction: column;
- justify-content: space-between;
- padding: 3.8rem;
- width: 40%;
+ width: 38%;
height: 100%;
- overflow-x: hidden;
- overflow-y: auto;
+ padding: 4rem 3.8rem 3.6rem;
+ box-sizing: border-box;
+ overflow: hidden;
+ background: var(--zen-welcome-sidebar-bg);
+ box-shadow: 4px 0 24px rgba(0, 0, 0, 0.05);
+ }
- & #zen-welcome-heart {
- width: 100%;
- height: 100%;
- opacity: 0;
- color: currentColor;
+ #zen-welcome-back {
+ align-self: flex-start;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin: 0 0 2.2rem;
+ padding: 0;
+ appearance: none;
+ border: none;
+ background: none;
+ font: inherit;
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: var(--zen-welcome-accent);
+
+ &::before {
+ content: "";
+ width: 16px;
+ height: 16px;
+ background: url(chrome://browser/skin/zen-icons/back.svg) center / contain
+ no-repeat;
+ -moz-context-properties: fill;
fill: currentColor;
- -moz-context-properties: fill, fill-opacity;
- background-image: url(chrome://browser/skin/zen-icons/essential-add.svg);
- background-size: 15%;
- background-repeat: no-repeat;
- background-position: center;
}
- &[animate-heart] {
- overflow: hidden;
+ &[disabled] {
+ visibility: hidden;
+ }
+ }
+
+ #zen-welcome-page-sidebar-content {
+ flex: 1;
+ position: relative;
+ }
+
+ .zen-welcome-text {
+ position: absolute;
+ inset: 0;
+
+ & > * {
+ opacity: 0;
+ will-change: transform, opacity;
+ }
+
+ & h1 {
+ margin: 0 0 2.4rem;
+ font-size: 3rem;
+ font-weight: 700;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ }
+
+ & p {
+ margin: 0 0 1.2rem;
+ font-size: 1.25rem;
+ line-height: 1.6;
+ color: var(--zen-welcome-text-secondary);
}
}
#zen-welcome-page-sidebar-buttons {
+ display: flex;
flex-direction: column;
- gap: 10px;
- }
-
- #zen-welcome-page-sidebar-content {
- & h1 {
- font-size: xx-large;
- font-weight: 600;
- margin-bottom: 1rem;
- }
-
- & p {
- margin: 0 0 1.1rem 0;
- color: light-dark(rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.6));
- }
-
- & > * {
- transform: translate(300%);
- }
- }
-
- & button {
- justify-content: center;
- align-items: center;
- transform: translate(300%);
+ gap: 1rem;
}
#zen-welcome-page-content {
- background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
- width: 60%;
- max-width: 80rem;
- height: 100%;
+ flex: 1;
position: relative;
+ height: 100%;
overflow: hidden;
- justify-content: center;
- align-items: center;
+
+ &::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ z-index: 0;
+ background-image: url(chrome://browser/content/zen-images/grain-bg.png);
+ opacity: 0.3;
+ mix-blend-mode: overlay;
+ pointer-events: none;
+ }
+ }
+
+ .zen-welcome-page {
+ position: absolute;
+ inset: 0;
+ opacity: 0;
display: flex;
flex-direction: column;
- gap: 1.6rem;
+ justify-content: center;
+ align-items: center;
+ gap: 2.5rem;
+ padding: 3rem 4rem;
+ box-sizing: border-box;
- &[select-engine="true"] {
- display: grid;
- grid-template-columns: 1fr 1fr;
- box-sizing: border-box;
- padding: 5rem;
+ &[exiting] {
+ pointer-events: none;
}
- & > label {
- opacity: 0;
- transition:
- scale 0.1s,
- box-shadow 0.1s;
- padding: 1.5rem 1.1rem;
- border-radius: 0.6rem;
- width: 50%;
- gap: 0.8rem;
- display: flex;
- border: 2px solid var(--zen-colors-border);
- background: light-dark(rgba(255, 255, 255, 0.7), rgba(0, 0, 0, 0.4));
- align-items: center;
+ &[page="essentials"] {
+ justify-content: flex-end;
+ align-items: flex-end;
+ padding: 3rem 0 0 6rem;
+ }
- &:hover {
- box-shadow: var(--zen-big-shadow);
+ &[page="search"] {
+ justify-content: safe center;
+ overflow-y: auto;
+ padding-top: 6rem;
+ padding-bottom: 6rem;
+ scrollbar-width: none;
+
+ mask-image: linear-gradient(
+ transparent,
+ black 6rem,
+ black calc(100% - 6rem),
+ transparent
+ );
+
+ .zen-welcome-option {
+ max-width: 24rem;
+ min-height: 7.5rem;
+ padding: 0 0 0 1.8rem;
+ overflow: hidden;
+ font-weight: 600;
}
- &:has(:checked) {
- box-shadow: var(--zen-big-shadow);
- border: 2px solid var(--zen-primary-color);
- scale: 1.03;
+ .engine-icon-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ align-self: stretch;
+ flex-shrink: 0;
+ width: 34%;
+ margin-left: auto;
+ overflow: hidden;
+ background: var(--zen-welcome-tile-bg);
}
- }
- &[select-engine="true"] > label {
- flex-direction: column;
- font-weight: 600;
- aspect-ratio: 1 / 0.7;
- align-items: center;
- width: 60%;
- justify-self: center;
- align-self: center;
- justify-content: space-around;
- }
-
- /* 3 or more engines */
- &[select-engine="true"]:has(label:nth-of-type(3)) > label {
- height: 50%;
- }
-
- #zen-welcome-workspace-colors-anchor {
- width: 1px;
- height: 1px;
- }
-
- #zen-welcome-initial-essentials-browser {
- min-width: 70%;
- min-height: 80%;
- display: flex;
- margin-left: auto;
- margin-top: auto;
- border-top-left-radius: 1.2em;
- box-shadow: var(--zen-big-shadow);
- background: light-dark(rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.2));
- padding-right: 20%;
- overflow: hidden;
- opacity: 0;
- border: 1px solid var(--zen-colors-border);
- border-bottom-width: 0;
- border-right-width: 0;
-
- #zen-welcome-initial-essentials-browser-sidebar {
+ .engine-icon-backdrop {
+ position: absolute;
+ inset: 0;
width: 100%;
- padding: 1.4rem;
- gap: 1.2rem;
- background: light-dark(rgba(255, 255, 255, 0.7), rgba(0, 0, 0, 0.4));
+ height: 100%;
+ object-fit: cover;
+ scale: 3;
+ filter: blur(30px) saturate(1.8);
+ opacity: 0.2;
+ }
- #zen-welcome-initial-essentials-browser-sidebar-win-buttons {
- gap: 0.5rem;
- align-items: center;
+ .engine-icon {
+ position: relative;
+ width: 26px;
+ height: 26px;
+ border-radius: 10px;
+ }
- & > div {
- width: 15px;
- height: 15px;
- border-radius: 50%;
- background: var(--zen-toolbar-element-bg);
- }
- }
+ .zen-welcome-option-label {
+ order: -1;
+ padding-right: 1rem;
+ }
- #zen-welcome-initial-essentials-browser-sidebar-essentials {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 1rem;
- visibility: visible;
+ .zen-welcome-option-radio {
+ order: -2;
+ }
+ }
+ }
- & * {
- visibility: visible;
- }
+ .zen-welcome-option {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ width: 100%;
+ max-width: 30rem;
+ padding: 1.8rem 1.9rem;
+ font-size: 1.3rem;
+ box-sizing: border-box;
+ border-radius: 20px;
+ border: 1px solid var(--zen-welcome-border);
+ background: var(--zen-welcome-surface-bg);
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
+ cursor: pointer;
+ will-change: transform;
+ transition: scale 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
- & .extra-tab {
- width: 100%;
- height: 4rem;
- border-radius: var(--border-radius-large);
- margin-top: 0.5rem;
- background: var(--zen-toolbar-element-bg);
- grid-column: span 3;
- }
+ &:hover {
+ scale: 1.03;
+ }
- & .tabbrowser-tab {
- --tab-min-height: 6rem !important;
- min-width: 5rem !important;
- transition: transform 0.1s;
- position: relative;
+ & input {
+ display: none;
+ }
- &::after {
- position: absolute;
- content: "";
- width: 1.8rem;
- height: 1.8rem;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: var(--zen-essential-tab-icon);
- background-position: center;
- background-size: contain;
- background-repeat: no-repeat;
- border-radius: 4px;
- }
+ & strong {
+ font-weight: 700;
+ }
- --border-radius-medium: 1rem;
+ & .engine-icon {
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+ }
- &[visuallyselected] {
- transform: scale(1.06);
- }
+ .zen-welcome-option-radio {
+ position: relative;
+ flex-shrink: 0;
+ width: 22px;
+ height: 22px;
+ box-sizing: border-box;
+ border-radius: 50%;
+ border: 1px solid
+ light-dark(rgba(0, 0, 0, 0.45), rgba(255, 255, 255, 0.5));
- & .tab-background {
- margin: 0 !important;
- }
+ &::after {
+ content: "";
+ position: absolute;
+ inset: 3px;
+ border-radius: 50%;
+ background: currentColor;
+ opacity: 0;
+ }
+ }
- & .tab-background::after {
- filter: blur(30px) brightness(1.2);
- }
- }
+ /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */
+ &:has(:checked) {
+ scale: 1.03;
+ border-color: var(--zen-welcome-accent);
+ color: var(--zen-welcome-accent);
+
+ .zen-welcome-option-radio {
+ border-color: currentColor;
+
+ &::after {
+ opacity: 1;
}
}
}
}
+
+ #zen-welcome-workspace-colors-anchor {
+ width: 1px;
+ height: 1px;
+ }
+
+ .zen-welcome-mock-browser {
+ width: 92%;
+ height: 100%;
+ border-top-left-radius: 2.4rem;
+ background: light-dark(#e3e3e5, #232326);
+ box-shadow: 0 8px 40px rgba(0, 0, 0, 0.12);
+ overflow: hidden;
+
+ .zen-welcome-mock-browser-sidebar {
+ display: flex;
+ flex-direction: column;
+ gap: 1.6rem;
+ width: 80%;
+ height: 100%;
+ padding: 2.6rem 2.4rem;
+ box-sizing: border-box;
+ background: var(--zen-welcome-surface-bg);
+ }
+
+ .zen-welcome-mock-browser-dots {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 0.6rem;
+
+ & > div {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: var(--zen-welcome-tile-bg);
+ }
+ }
+
+ #zen-welcome-essentials {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.5rem;
+ }
+
+ .zen-welcome-essential {
+ appearance: none;
+ aspect-ratio: 1;
+ padding: 0;
+ border-radius: 2rem;
+ border: 3px solid transparent;
+ background: var(--zen-welcome-tile-bg) var(--zen-welcome-app-icon)
+ center / 35% no-repeat;
+ cursor: pointer;
+ will-change: transform;
+ transition: scale 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
+
+ &:hover,
+ &[selected] {
+ scale: 1.04;
+ }
+
+ &[selected] {
+ background-color: color-mix(
+ in srgb,
+ var(--zen-welcome-app-color) 22%,
+ var(--zen-welcome-surface-bg)
+ );
+ border-color: color-mix(
+ in srgb,
+ var(--zen-welcome-app-color) 60%,
+ transparent
+ );
+ }
+ }
+
+ .zen-welcome-mock-tab {
+ flex-shrink: 0;
+ height: 4.5rem;
+ border-radius: 1.6rem;
+ background: var(--zen-welcome-tile-bg);
+ }
+ }
}
diff --git a/surfer.json b/surfer.json
index a1a87a88a..f144a9df1 100644
--- a/surfer.json
+++ b/surfer.json
@@ -5,8 +5,8 @@
"binaryName": "zen",
"version": {
"product": "firefox",
- "version": "154.0.1",
- "candidate": "154.0.1",
+ "version": "155.0",
+ "candidate": "155.0",
"candidateBuild": 1
},
"buildOptions": {
@@ -20,7 +20,7 @@
"brandShortName": "Zen",
"brandFullName": "Zen Browser",
"release": {
- "displayVersion": "1.21.16b",
+ "displayVersion": "1.22b",
"github": {
"repo": "zen-browser/desktop"
},
@@ -40,7 +40,7 @@
"brandShortName": "Twilight",
"brandFullName": "Zen Twilight",
"release": {
- "displayVersion": "1.22t",
+ "displayVersion": "1.23t",
"github": {
"repo": "zen-browser/desktop"
}