Compare commits

..

24 Commits

Author SHA1 Message Date
mr. m
f67fa2339b feat: Fix data path, b=no-bug, c=workflows 2026-01-08 14:31:07 +01:00
mr. m
7f29800982 feat: Start implementing PGO profiling on different steps, b=no-bug, c=workflows, configs 2026-01-07 13:42:42 +01:00
mr. m
3c7fb093db feat: Make sure dragged tab does not allow the title to overflow, b=no-bug, c=no-component 2026-01-07 01:05:08 +01:00
mr. m
ec4a55e1f8 chore: Revert linking with ld64 due to big compile times and resource usage, b=no-bug, c=configs 2026-01-07 00:28:56 +01:00
mr. m
b0f3839426 feat: Limit the number of threads when building macos releases, b=no-bug, c=configs 2026-01-06 20:20:38 +01:00
mr. m
a537f0f91d feat: Ignore errors when trying to import external mochitests while trying to sync upstream, b=no-bug, c=workflows 2026-01-06 13:02:03 +01:00
mr. m
11f29c12c5 chore: Install autopep8 for code formatting in the upstream workflow, b=no-bug, c=workflows 2026-01-06 12:53:27 +01:00
mr. m
e7631ce9cc feat: Make sure upstream sync workflow initializes git before downloading, b=no-bug, c=workflows 2026-01-06 12:48:26 +01:00
mr. m
481163a756 feat: Correctly initialize new restored windows, p=#11821
* feat: Correctly initialize new restored windows, b=no-bug, c=no-component

* chore: Experiment with different build flags for optimization and build time, b=no-bug, c=common, configs

* chore: Format, b=no-bug, c=no-component
2026-01-06 12:38:22 +01:00
mr. m
281ec6693d feat: Correctly initialize new restored windows, b=no-bug, c=no-component 2026-01-06 12:35:35 +01:00
mr. m
9820bd5772 test: Started adding tests for window sync, b=no-bug, c=tests, workspaces 2026-01-06 01:23:20 +01:00
mr. m
75f4f0c3e6 feat: Created share files, schemas and prefs, p=#11814, c=no-component 2026-01-05 19:42:01 +01:00
mr. m
591193b748 fix: Correctly restore unsynced windows, b=no-bug, c=workspaces 2026-01-05 19:29:22 +01:00
mr. m
2628ba7dd8 chore: Don't imply MOZ_LD64_KNOWN_GOOD from automation flag, b=no-bug, c=no-component 2026-01-05 18:58:35 +01:00
mr. m
2fd0935bf5 chore: Mark ld64 as known for automated macos builds, b=no-bug, c=configs 2026-01-05 18:37:30 +01:00
mr. m
7683b2675a fix: Fixed title overlapping with essential tabs after unlock, b=closes #11808, c=workspaces 2026-01-05 13:35:43 +01:00
mr. m
ea7024aae4 feat: Start using ld64 for macos builds, b=no-bug, c=configs, compact-mode 2026-01-05 13:31:08 +01:00
mr. m
be8cbce23e fix: Fixed 'focus on' not working on twilight, b=closes #11804, c=no-component 2026-01-05 13:30:16 +01:00
mr. m
9c2b8426a9 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-01-05 02:22:53 +01:00
mr. m
beb4f94fd2 chore: Change compile flags for macos to fix potential memory leaks, b=bug #9649, c=configs 2026-01-05 02:22:44 +01:00
Andrey Bochkarev
f39ca33aa5 fix: Correctly identify child active groups for collapsible pins, b=closes https://github.com/zen-browser/desktop/issues/11799, p=#11809, c=folders
* fix: Correctly identify child active groups for `zen-workspace-collapsible-pins` (#11799)

* fix: Formatting
2026-01-04 23:44:13 +01:00
mr. m
7743ad968c fix: Fixed deleting a space when its currently selected, b=no-bug, c=common, workspaces 2026-01-04 23:43:29 +01:00
mr. m
7470df4cf6 feat: Fixed creating new unsynced windows rendering the window blank, b=no-bug, c=common, tabs, workspaces 2026-01-04 19:15:18 +01:00
mr. m
aef8400841 feat: Improved gradient rendering for three colors, b=no-bug, c=workspaces 2026-01-03 23:11:08 +01:00
46 changed files with 761 additions and 297 deletions

View File

@@ -336,7 +336,7 @@ jobs:
MOZ_BUILD_DATE: ${{needs.buildid.outputs.buildids}}
use-sccache: ${{ inputs.use-sccache }}
mac:
mac-pgo-generate:
name: macOS build
uses: ./.github/workflows/macos-release-build.yml
permissions:
@@ -348,6 +348,21 @@ jobs:
release-branch: ${{ inputs.update_branch }}
MOZ_BUILD_DATE: ${{needs.buildid.outputs.buildids}}
use-sccache: ${{ inputs.use-sccache }}
generate-pgo-data: true
mac:
name: macOS build
uses: ./.github/workflows/macos-release-build.yml
permissions:
contents: write
secrets: inherit
needs: [build-data, buildid, mac-pgo-generate]
with:
build-version: ${{ needs.build-data.outputs.version }}
release-branch: ${{ inputs.update_branch }}
MOZ_BUILD_DATE: ${{needs.buildid.outputs.buildids}}
use-sccache: ${{ inputs.use-sccache }}
generate-pgo-data: false
mac-uni:
name: macOS build (Universal)

View File

@@ -22,6 +22,11 @@ on:
required: true
type: boolean
default: false
generate-pgo-data:
description: 'Generate PGO data'
required: false
type: boolean
default: false
jobs:
mac-build:
@@ -68,8 +73,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.email "mr-cheffy@users.noreply.github.com"
git config --global user.name "mr-cheffy"
- name: Install system dependencies
run: |
@@ -120,6 +125,20 @@ jobs:
SURFER_COMPAT: ${{ matrix.arch }}
run: npm run import -- --verbose
- name: Download PGO data
if: ${{ inputs.generate-pgo-data == false }}
uses: actions/download-artifact@v4
with:
name: zen-macos-pgo-data-${{ matrix.arch }}
- name: Move PGO data
if: ${{ inputs.generate-pgo-data == false }}
run: |
mkdir -p $(echo ~)/artifact
mv $GITHUB_WORKSPACE/zen-macos-pgo-data-${{ matrix.arch }}/* $(echo ~)/artifact
chmod +x ~/artifact/en-US.log
chmod +x ~/artifact/merged.profdata
- name: Bootstrap
run: |
cd engine
@@ -141,6 +160,7 @@ jobs:
env:
SURFER_COMPAT: ${{ matrix.arch }}
ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }}
ZEN_GENERATE_PGO_DATA: ${{ inputs.generate-pgo-data && '1' || '0' }}
run: |
export SURFER_PLATFORM="darwin"
if [[ -n ${{ inputs.MOZ_BUILD_DATE }} ]];then
@@ -149,6 +169,7 @@ jobs:
bash .github/workflows/src/release-build.sh
- name: Package
if: ${{ !inputs.generate-pgo-data }}
env:
SURFER_COMPAT: ${{ matrix.arch }}
ZEN_GA_DISABLE_PGO: true
@@ -162,6 +183,7 @@ jobs:
rm -rf ~/.zen-keys
- name: Rename artifacts
if: ${{ !inputs.generate-pgo-data }}
run: |
echo "Tarballing DMG"
set -ex
@@ -171,6 +193,7 @@ jobs:
- name: Upload dist dmg
uses: actions/upload-artifact@v4
if: ${{ !inputs.generate-pgo-data }}
with:
retention-days: 1
name: zen-${{ matrix.arch }}-apple-darwin-dist.dmg
@@ -178,7 +201,7 @@ jobs:
- name: Upload host mar
uses: actions/upload-artifact@v4
if: matrix.arch == 'aarch64'
if: matrix.arch == 'aarch64' && !inputs.generate-pgo-data
with:
retention-days: 1
name: zen-macos-host-mar
@@ -186,8 +209,16 @@ jobs:
- name: Upload platform.ini
uses: actions/upload-artifact@v4
if: matrix.arch == 'x86_64'
if: matrix.arch == 'x86_64' && !inputs.generate-pgo-data
with:
retention-days: 1
name: platform.ini
path: ./platform.ini
- name: Upload PGO data
uses: actions/upload-artifact@v4
if: inputs.generate-pgo-data
with:
retention-days: 1
name: zen-macos-pgo-data-${{ matrix.arch }}
path: ./zen-macos-pgo-data

View File

@@ -28,3 +28,22 @@ else
export ZEN_RELEASE=1
npm run build
fi
if test "$ZEN_GENERATE_PGO_DATA" = "1"; then
cd engine
export UPLOAD_PATH=../zen-macos-pgo-data
export MOZ_FETCHES_DIR=/Users/runner/.mozbuild
mkdir -p $UPLOAD_PATH
export JARLOG_FILE="en-US.log"
export LLVM_PROFDATA=$MOZ_FETCHES_DIR/clang/bin/llvm-profdata
set -v
./mach python build/pgo/profileserver.py --binary obj-*-apple-darwin/dist/*.app/Contents/MacOS/zen
mv merged.profdata $UPLOAD_PATH/
mv $JARLOG_FILE $UPLOAD_PATH/
cd ..
fi

View File

@@ -47,6 +47,12 @@ jobs:
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: npm ci
- name: Install dependencies
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: |
git config --global user.email "mr-cheffy@users.noreply.github.com"
git config --global user.name "mr-cheffy"
- name: Setup surfer CI
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: |
@@ -67,6 +73,12 @@ jobs:
npm run sync
fi
- name: Run formatter
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: |
sudo apt install python3-autopep8
npm run pretty
- name: Check if any files changed
id: git-check
run: |
@@ -97,7 +109,7 @@ jobs:
- name: Import external tests
if: steps.git-check.outputs.files_changed == 'true'
run: python3 scripts/import_external_tests.py
run: python3 scripts/import_external_tests.py || true
- name: Create pull request
uses: peter-evans/create-pull-request@v7

View File

@@ -79,12 +79,6 @@ if test "$ZEN_RELEASE"; then
MOZILLA_OFFICIAL=1
export MOZILLA_OFFICIAL=1
export OPT_LEVEL="3"
ac_add_options OPT_LEVEL="3"
export RUSTC_OPT_LEVEL="3"
ac_add_options RUSTC_OPT_LEVEL="3"
mk_add_options AUTOCLOBBER=1
export AUTOCLOBBER=1

View File

@@ -14,42 +14,44 @@ if test "$ZEN_RELEASE"; then
ac_add_options --enable-lto=cross,thin
fi
if test "$ZEN_RELEASE"; then
if test "$ZEN_GA_DISABLE_PGO"; then
export ZEN_DUMMY=1
else
export MOZ_PGO=1
ac_add_options MOZ_PGO=1
fi
fi
if test "$SURFER_COMPAT" = "x86_64"; then
ac_add_options --target=x86_64-apple-darwin
if test "$ZEN_RELEASE"; then
ac_add_options --enable-wasm-avx
ac_add_options --enable-optimize="-march=nehalem -mtune=haswell -O3 -w"
export CFLAGS="-O3 -march=nehalem"
export CPPFLAGS="-O3 -march=nehalem"
export CXXFLAGS="-O3 -march=nehalem"
export LDFLAGS="-Wl,-O3 -march=nehalem"
export RUSTFLAGS="-Ctarget-cpu=nehalem"
ac_add_options --enable-optimize="-march=nehalem -mtune=haswell -O2 -w"
fi
else
ac_add_options --enable-clang-plugin
ac_add_options --target=aarch64-apple-darwin
if test "$ZEN_RELEASE"; then
ac_add_options --enable-optimize="-O3 -mcpu=apple-m1 -march=armv8.3-a+simd"
ac_add_options --enable-optimize="-O2 -mcpu=apple-m1"
# As of Clang 13, the default is -mcpu=apple-m1 when using a aarch64-apple-macos target,
# but we're using apple64-apple-darwin, which defaults to -mcpu=apple-a7, which disables
# a bunch of # performance-enabling CPU features.
export CFLAGS="-O3 -march=armv8.3-a+simd -mcpu=apple-m1"
export CPPFLAGS="-O3 -march=armv8.3-a+simd -mcpu=apple-m1"
export CXXFLAGS="-O3 -march=armv8.3-a+simd -mcpu=apple-m1"
export LDFLAGS="-Wl,-O3 -march=armv8.3-a+simd -mcpu=apple-m1"
export RUSTFLAGS="-C target-feature=+v8.3a -Ctarget-cpu=apple-m1"
# TODO: We'll want to switch to aarch64-apple-macos eventually.
export CFLAGS="$CFLAGS -mcpu=apple-m1"
export CXXFLAGS="$CXXFLAGS -mcpu=apple-m1"
fi
fi
# Keep using ld64 on PGO/LTO builds because of performance regressions when using lld.
# Mozilla sets "MOZ_LD64_KNOWN_GOOD" to true when they do automated builds with PGO/LTO on macOS.
# See https://searchfox.org/firefox-main/rev/e61d59b5c9a651fd7bf28043f87c0dc669833496/build/moz.configure/lto-pgo.configure#261
export MOZ_LD64_KNOWN_GOOD=1
ac_add_options --enable-linker=ld64
if test "$ZEN_RELEASE"; then
if ! test "$ZEN_GA_DISABLE_PGO"; then
if test "$ZEN_GENERATE_PGO_DATA"; then
mk_add_options "export MOZ_AUTOMATION_PACKAGE_GENERATED_SOURCES=0"
ac_add_options --enable-profile-generate=cross
else
ac_add_options --enable-profile-use=cross
ac_add_options --with-pgo-profile-path=$(echo ~)/artifact/merged.profdata
ac_add_options --with-pgo-jarlog=$(echo ~)/artifact/en-US.log
fi
fi
fi

View File

@@ -36,8 +36,8 @@ if test "$SURFER_COMPAT" = "x86_64"; then
ac_add_options --enable-optimize="-O3 -w -ftree-vectorize -mfpmath=sse -mprfchw -msse3 -mcx16 -msahf"
export LDFLAGS="-Wl,-O3"
export RUSTFLAGS="-Clink-args=--icf=safe"
export LDFLAGS="$LDFLAGS -Wl,-O3"
export RUSTFLAGS="$RUSTFLAGS -Clink-args=--icf=safe"
elif test "$SURFER_COMPAT" = "aarch64"; then
ac_add_options --target=aarch64-pc-windows-msvc
ac_add_options --enable-eme=widevine
@@ -62,9 +62,7 @@ if test "$ZEN_CROSS_COMPILING"; then
ac_add_options --enable-profile-generate=cross
elif test "$SURFER_COMPAT" = "x86_64"; then
# Dont use PGO on aarch64 builds and the ZEN_GA_DISABLE_PGO flag is not set
if test "$ZEN_GA_DISABLE_PGO"; then
export ZEN_DUMMY=1
else
if ! test "$ZEN_GA_DISABLE_PGO"; then
ac_add_options --enable-profile-use=cross
ac_add_options --with-pgo-profile-path=$(echo ~)/artifact/merged.profdata
ac_add_options --with-pgo-jarlog=$(echo ~)/artifact/en-US.log

View File

@@ -10,7 +10,10 @@ zen-menubar-toggle-pinned-tabs =
}
zen-menubar-appearance =
.label = Website Appearance
.label = Appearance
zen-menubar-appearance-description =
.label = Websites will use:
zen-menubar-appearance-auto =
.label = Automatic

6
prefs/zen/share.yaml Normal file
View File

@@ -0,0 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.share.enabled
value: true

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..41bddb36fef7bb74212c261f1c07e40b30ab5f08 100644
index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..429a26849cec23836f5bff3bd1a6376050311377 100644
--- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -127,6 +127,9 @@ const TAB_EVENTS = [
@@ -211,17 +211,20 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..41bddb36fef7bb74212c261f1c07e40b
tabbrowser.removeTab(tabbrowser.tabs[i]);
}
}
@@ -5821,6 +5851,9 @@ var SessionStoreInternal = {
@@ -5821,6 +5851,12 @@ var SessionStoreInternal = {
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id)
);
}
+ if (winData.isZenUnsynced) {
+ aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true");
+ }
+ aWindow.gZenFolders?.restoreDataFromSessionStore(winData.folders);
+ aWindow.gZenViewSplitter?.restoreDataFromSessionStore(winData.splitViewData);
+ aWindow.gZenWorkspaces?.restoreWorkspacesFromSessionStore(winData);
// Move the originally open tabs to the end.
if (initialTabs) {
@@ -6372,6 +6405,25 @@ var SessionStoreInternal = {
@@ -6372,6 +6408,25 @@ var SessionStoreInternal = {
// Most of tabData has been restored, now continue with restoring
// attributes that may trigger external events.
@@ -247,7 +250,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..41bddb36fef7bb74212c261f1c07e40b
if (tabData.pinned) {
tabbrowser.pinTab(tab);
@@ -7290,7 +7342,7 @@ var SessionStoreInternal = {
@@ -7290,7 +7345,7 @@ var SessionStoreInternal = {
let groupsToSave = new Map();
for (let tIndex = 0; tIndex < window.tabs.length; ) {
@@ -256,7 +259,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..41bddb36fef7bb74212c261f1c07e40b
// Adjust window.selected
if (tIndex + 1 < window.selected) {
window.selected -= 1;
@@ -7305,7 +7357,7 @@ var SessionStoreInternal = {
@@ -7305,7 +7360,7 @@ var SessionStoreInternal = {
);
// We don't want to increment tIndex here.
continue;

View File

@@ -0,0 +1,12 @@
diff --git a/build/moz.configure/lto-pgo.configure b/build/moz.configure/lto-pgo.configure
index 165541b79842672d8bac48722220532f3913556d..6781b84b9c06ff26a41a0e7d8be5a958600fda62 100644
--- a/build/moz.configure/lto-pgo.configure
+++ b/build/moz.configure/lto-pgo.configure
@@ -258,7 +258,6 @@ option(
help="Indicate that ld64 is free of symbol aliasing bugs",
)
-imply_option("MOZ_LD64_KNOWN_GOOD", moz_automation)
use_fat_lto = cxx_compiler.try_link(
ldflags=depends(stlport_libs)(

View File

@@ -0,0 +1,13 @@
diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure
index 3d8d2ea651fa1320306b32b0f7b1b73e0da1df61..257f75a92a26d7a724353c614365924d230aef83 100644
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1991,7 +1991,7 @@ def select_linker_tmpl(host_or_target):
# ensure consistent output.
env["LC_ALL"] = "C"
retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
- if retcode == 1 and "Logging ld64 options" in stderr:
+ if retcode == 1 and "ld: unknown options: --version" in stderr:
kind = "ld64"
elif retcode != 0:

View File

@@ -0,0 +1,13 @@
diff --git a/toolkit/components/prompts/content/commonDialog.css b/toolkit/components/prompts/content/commonDialog.css
index d811fb62d502cf6fc0bf8163f11e1d264dee9e82..0095175bce6fb4520aca357e5209e1ad7329bbdb 100644
--- a/toolkit/components/prompts/content/commonDialog.css
+++ b/toolkit/components/prompts/content/commonDialog.css
@@ -88,7 +88,7 @@ dialog[insecureauth] {
/* Fix padding/spacing */
dialog {
- --grid-padding: 16px;
+ --grid-padding: 24px;
/* All the inner items should have 4px inline margin, leading to 1.16em spacing
* between the dialog and its contents, and 8px horizontal spacing between items. */
padding: var(--grid-padding) calc(var(--grid-padding) - 4px);

View File

@@ -0,0 +1,12 @@
diff --git a/toolkit/themes/shared/design-system/tokens-brand.css b/toolkit/themes/shared/design-system/tokens-brand.css
index 3ad65bc44fe06be77c4e5e679bb52bd0813cc972..3906b0bbcbbe6149ed7131460305074f079090a7 100644
--- a/toolkit/themes/shared/design-system/tokens-brand.css
+++ b/toolkit/themes/shared/design-system/tokens-brand.css
@@ -6,6 +6,7 @@
* and run `npm run build` to see your changes. */
@import url("chrome://global/skin/design-system/tokens-shared.css");
+@import url("chrome://browser/content/zen-styles/zen-theme.css");
@layer tokens-foundation {
:root,

View File

@@ -1,16 +1,7 @@
diff --git a/toolkit/themes/shared/in-content/common-shared.css b/toolkit/themes/shared/in-content/common-shared.css
index 41f65a1f2c3065631780b02d820f632abdf542af..c01c2957367da968d106c83117e45800bb6b15e5 100644
index 41f65a1f2c3065631780b02d820f632abdf542af..d15ff979c5d2deecca3d232f68551bec10ba6b8f 100644
--- a/toolkit/themes/shared/in-content/common-shared.css
+++ b/toolkit/themes/shared/in-content/common-shared.css
@@ -4,7 +4,7 @@
@import url("chrome://global/skin/design-system/tokens-brand.css");
@import url("chrome://global/skin/design-system/text-and-typography.css");
-
+@import url("chrome://browser/content/zen-styles/zen-theme.css");
@namespace html "http://www.w3.org/1999/xhtml";
@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@@ -66,7 +66,7 @@
* this in forced colors mode, as we should be using system colours then.
*/

View File

@@ -19,6 +19,7 @@ class nsZenMenuBar {
let appearanceMenu = window.MozXULElement.parseXULToFragment(`
<menu data-l10n-id="zen-menubar-appearance">
<menupopup>
<menuitem data-l10n-id="zen-menubar-appearance-description" disabled="true" />
<menuitem data-l10n-id="zen-menubar-appearance-auto" data-type="auto" type="radio" checked="true" />
<menuitem data-l10n-id="zen-menubar-appearance-light" data-type="light" type="radio" />
<menuitem data-l10n-id="zen-menubar-appearance-dark" data-type="dark" type="radio" />

View File

@@ -86,3 +86,15 @@
box-shadow: 0 0 250px color-mix(in srgb, var(--zen-primary-color), transparent 100%);
}
}
@keyframes zen-dialog-fade-in {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

View File

@@ -58,6 +58,7 @@
background: var(--zen-main-browser-background);
opacity: var(--zen-background-opacity);
transition: 0s;
background-blend-mode: screen;
}
&:is(.zen-toolbar-background) {
@@ -70,6 +71,7 @@
background: var(--zen-main-browser-background-old);
opacity: calc(1 - var(--zen-background-opacity));
transition: 0s;
background-blend-mode: screen;
}
&:is(.zen-toolbar-background) {

View File

@@ -11,6 +11,8 @@
/** These types of buttons look INSAINELY bad in the preferences page */
xul|button {
--size-item-large: 34px;
border-radius: var(--zen-button-border-radius) !important;
padding: var(--zen-button-padding) !important;
transition: 0.1s;
@@ -18,6 +20,7 @@ xul|button {
font-weight: 500 !important;
border: 1px solid var(--zen-colors-border);
border-bottom-width: 2px;
}
button:not(#zen-workspaces-button):active {

View File

@@ -3,12 +3,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Zen Welcome dialog override */
@media (prefers-color-scheme: dark) {
.dialogBox:not(.spotlightBox) {
border: 1px solid var(--zen-colors-border);
.dialogBox {
border: none;
outline: 1px solid #a8a8a9;
animation: zen-dialog-fade-in 0.3s ease-out;
border-radius: 12px !important;
@media (prefers-color-scheme: dark) {
outline-color: #44495a;
outline-offset: -2px;
border: 1px solid black;
}
}
.dialogBox:not(.spotlightBox) {
min-width: min(80vw, 376px);
}

View File

@@ -281,7 +281,7 @@ body > #confetti {
/* Customizable modes */
#customization-container {
--toolbar-bgcolor: var(--zen-dialog-background);
--toolbar-bgcolor: var(--arrowpanel-background);
}
/* Site Data popup */

View File

@@ -17,8 +17,8 @@
--zen-background-opacity: 1;
/* Branding */
--zen-branding-dark: #1d1d1d;
--zen-branding-paper: #ebebeb;
--zen-branding-dark: #101010;
--zen-branding-paper: #e2e2e2;
--zen-branding-bg: light-dark(var(--zen-branding-paper), var(--zen-branding-dark));
--zen-branding-bg-reverse: light-dark(var(--zen-branding-dark), var(--zen-branding-paper));
@@ -46,8 +46,8 @@
color-mix(in srgb, var(--zen-primary-color) 80%, white 20%)
);
--zen-colors-border: light-dark(
color-mix(in srgb, var(--zen-colors-secondary) 97%, black 3%),
color-mix(in srgb, var(--zen-colors-secondary) 20%, rgb(79, 79, 79) 80%)
color-mix(in srgb, var(--zen-colors-secondary) 50%, transparent),
color-mix(in srgb, var(--zen-colors-secondary) 20%, rgb(79, 79, 79))
);
--zen-colors-border-contrast: light-dark(
color-mix(in srgb, var(--zen-colors-secondary) 10%, rgba(181, 181, 181, 0.11) 90%),
@@ -59,7 +59,7 @@
color-mix(in srgb, var(--zen-primary-color) 1%, var(--zen-branding-bg) 99%)
);
--zen-dialog-background: light-dark(rgb(244, 244, 244), rgb(31, 31, 31));
--zen-dialog-background: light-dark(#FAFBFF, #161C31);
--zen-urlbar-background: light-dark(
color-mix(in srgb, var(--zen-primary-color) 3%, #f4f4f4 97%),
color-mix(in srgb, var(--zen-primary-color) 4%, rgb(24, 24, 24) 96%)
@@ -113,6 +113,9 @@
--button-primary-active-bgcolor: var(--in-content-primary-button-background-active) !important;
--button-primary-color: var(--in-content-primary-button-text-color) !important;
/* For dialogs / modals */
--button-background-color-primary: var(--button-primary-bgcolor) !important;
--button-background-color: var(--in-content-button-background) !important;
--button-background-color-hover: var(--in-content-button-background-hover) !important;
--button-background-color-active: color-mix(in srgb, currentColor 5%, transparent 95%) !important;
@@ -247,7 +250,7 @@
}
--toolbar-field-background-color: var(--zen-colors-input-bg) !important;
--arrowpanel-background: var(--zen-dialog-background) !important;
--arrowpanel-background: light-dark(rgb(244, 244, 244), rgb(31, 31, 31)) !important;
--panel-separator-color: color-mix(in srgb, currentColor 15%, transparent) !important;

View File

@@ -433,18 +433,16 @@ window.gZenCompactModeManager = {
}
if (canHideSidebar && isCompactMode) {
this._setElementExpandAttribute(this.sidebar, false);
gZenUIManager.motion
.animate(
gZenUIManager
.elementAnimate(
this.sidebar,
{
marginRight: [0, this.sidebarIsOnRight ? `-${sidebarWidth}px` : 0],
marginLeft: [0, this.sidebarIsOnRight ? 0 : `-${sidebarWidth}px`],
marginRight: ['0px', this.sidebarIsOnRight ? `-${sidebarWidth}px` : '0px'],
marginLeft: ['0px', this.sidebarIsOnRight ? '0px' : `-${sidebarWidth}px`],
},
{
ease: 'easeIn',
type: 'spring',
bounce: 0,
duration: 0.12,
easing: 'ease-in',
duration: 120,
}
)
.then(() => {
@@ -467,8 +465,6 @@ window.gZenCompactModeManager = {
});
}
this.sidebar.style.removeProperty('margin-right');
this.sidebar.style.removeProperty('margin-left');
this.sidebar.style.removeProperty('transition');
this.sidebar.style.removeProperty('transform');
this.sidebar.style.removeProperty('point-events');
@@ -716,14 +712,8 @@ window.gZenCompactModeManager = {
// If we want the toolbars to be draggable, we need to make sure to check the hover state after a short delay.
// This is because the mouse is left to be handled natively so firefox thinks the mouse left the window for a split second.
setTimeout(() => {
let isHovered = event.target.matches(':hover');
MousePosTracker._callListener({
onMouseEnter: () => (isHovered = true),
onMouseLeave: () => {},
getMouseTargetRect: () => target.getBoundingClientRect(),
});
// Let's double check if the mouse is still hovering over the element, see the bug above.
if (isHovered) {
if (event.target.matches(':hover')) {
return;
}

View File

@@ -122,12 +122,15 @@
}
// Apply a transform translate to the tab in order to center it within the drag image
// based on the event coordinates.
tabClone.style.transform = `translate(${(tabRect.width - dragData.offsetX) / 2}px, ${(tabRect.height - dragData.offsetY) / 2}px)`;
if (!movingTabs.length > 1) {
tabClone.style.transform = `translate(${(tabRect.width - dragData.offsetX) / 2}px, ${(tabRect.height - dragData.offsetY) / 2}px)`;
}
wrapper.appendChild(tabClone);
}
this.#maybeCreateDragImageDot(movingTabs, wrapper);
wrapper.style.width = tabRect.width + 'px';
wrapper.style.height = tabRect.height * movingTabs.length + 'px';
wrapper.style.overflow = 'clip';
wrapper.style.position = 'fixed';
wrapper.style.top = '-9999px';
periphery.appendChild(wrapper);

View File

@@ -118,6 +118,9 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
}
get childActiveGroups() {
if (this.tagName === 'zen-workspace-collapsible-pins') {
return Array.from(this.parentElement.querySelectorAll('zen-folder[has-active]'));
}
return Array.from(this.querySelectorAll('zen-folder[has-active]'));
}

View File

@@ -35,7 +35,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
ARC_HEIGHT_RATIO: 0.2, // Arc height = distance * ratio (capped at MAX_ARC_HEIGHT)
});
#GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref('zen.glance.animation-duration');
#GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref('zen.glance.animation-duration') / 1000;
init() {
this.#setupEventListeners();
@@ -253,21 +253,19 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
const xOffset = gZenVerticalTabsManager._prefsRightSide ? 20 : -20;
setTimeout(() => {
gZenUIManager.elementAnimate(
container,
{
opacity: [0, 1],
x: [xOffset, 0],
},
{
duration: 200,
easing: 'ease-in-out',
fill: 'forwards',
delay: this.#GLANCE_ANIMATION_DURATION - 200,
}
);
});
gZenUIManager.motion.animate(
container,
{
opacity: [0, 1],
x: [xOffset, 0],
},
{
duration: 0.2,
type: 'spring',
delay: this.#GLANCE_ANIMATION_DURATION - 0.2,
bounce: 0,
}
);
}
/**
@@ -405,7 +403,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
'.browserSidebarContainer'
);
gZenUIManager.elementAnimate(
gZenUIManager.motion.animate(
parentSidebarContainer,
{
scale: [1, 0.98],
@@ -413,8 +411,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
},
{
duration: this.#GLANCE_ANIMATION_DURATION,
easing: 'ease-in-out',
fill: 'forwards',
type: 'spring',
bounce: 0.2,
}
);
}
@@ -430,7 +428,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.overlay.removeAttribute('fade-out');
this.browserWrapper.setAttribute('animate', true);
this.browserWrapper.style.transform = `translate(${left}px, ${top}px)`;
this.browserWrapper.style.top = `${top}px`;
this.browserWrapper.style.left = `${left}px`;
this.browserWrapper.style.width = `${width}px`;
this.browserWrapper.style.height = `${height}px`;
@@ -442,18 +441,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* Store the original position for later restoration
*/
#storeOriginalPosition() {
let transform = this.browserWrapper.style.transform;
let [top, left] = [0, 0];
if (transform && transform.startsWith('translate(')) {
const match = transform.match(/translate\(([-\d.]+)px,\s*([-\d.]+)px\)/);
if (match) {
left = parseFloat(match[1]);
top = parseFloat(match[2]);
}
}
this.#glances.get(this.#currentGlanceID).originalPosition = {
top,
left,
top: this.browserWrapper.style.top,
left: this.browserWrapper.style.left,
width: this.browserWrapper.style.width,
height: this.browserWrapper.style.height,
};
@@ -483,14 +473,14 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.browserWrapper.prepend(imageDataElement);
this.#glances.get(this.#currentGlanceID).elementImageData = data.elementData;
gZenUIManager.elementAnimate(
gZenUIManager.motion.animate(
imageDataElement,
{
opacity: [1, 0],
},
{
duration: this.#GLANCE_ANIMATION_DURATION / 2,
easing: 'ease-in-out',
easing: 'easeInOut',
}
);
@@ -510,6 +500,16 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
browserElement.style.minHeight = `${minHeight}px`;
}
/**
* Get the transform origin for the animation
* @param {Object} data - Glance data with position and dimensions
* @returns {string} The transform origin CSS value
*/
#getTransformOrigin(data) {
const { clientX, clientY } = data;
return `${clientX}px ${clientY}px`;
}
/**
* Execute the main glance animation
* @param {Object} data - Glance data
@@ -521,18 +521,21 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
// Create curved animation sequence
const arcSequence = this.#createGlanceArcSequence(data, 'opening');
const transformOrigin = this.#getTransformOrigin(data);
this.browserWrapper.style.transformOrigin = transformOrigin;
// Only animate if there is element data, so we can apply a
// nice fade-in effect to the content. But if it doesn't exist,
// we just fall back to always showing the browser directly.
if (data.elementData) {
gZenUIManager
.elementAnimate(
gZenUIManager.motion
.animate(
this.contentWrapper,
{ opacity: [0, 1] },
{
duration: this.#GLANCE_ANIMATION_DURATION / 2,
easing: 'ease-in-out',
easing: 'easeInOut',
}
)
.then(() => {
@@ -541,9 +544,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
}
this.#animateParentBackground();
gZenUIManager
.elementAnimate(this.browserWrapper, arcSequence, {
gZenUIManager.motion
.animate(this.browserWrapper, arcSequence, {
duration: gZenUIManager.testingEnabled ? 0 : this.#GLANCE_ANIMATION_DURATION,
ease: 'easeInOut',
})
.then(() => {
this.#finalizeGlanceOpening(imageDataElement, browserElement, resolve);
@@ -603,29 +607,28 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
);
const sequence = {
transform: [],
top: [],
left: [],
width: [],
height: [],
transform: [],
};
const steps = this.#ARC_CONFIG.ARC_STEPS;
const arcDirection = shouldArcDownward ? 1 : -1;
function easeOutBack(x) {
const c1 = 0.4;
const c3 = c1 + 1;
return 1 + c3 * (x - 1) ** 3 + c1 * (x - 1) ** 2;
function easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
function easeInQuint(x) {
return x * x * x * x * x;
function easeOutCubic(t) {
return 1 - Math.pow(1 - t, 6);
}
// First, create the main animation steps
for (let i = 0; i <= steps; i++) {
const progress = i / steps;
const eased = direction === 'opening' ? easeOutBack(progress) : easeInQuint(progress);
const eased = direction === 'opening' ? easeInOutQuad(progress) : easeOutCubic(progress);
// Calculate size interpolation
const currentWidth = startPosition.width + (endPosition.width - startPosition.width) * eased;
@@ -640,11 +643,32 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
const y =
startPosition.y + distanceY * eased + arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
sequence.transform.push(`translate(${x}px, ${y}px)`);
sequence.transform.push(`translate(-50%, -50%) scale(1)`);
sequence.top.push(`${y}px`);
sequence.left.push(`${x}px`);
sequence.width.push(`${currentWidth}px`);
sequence.height.push(`${currentHeight}px`);
}
let scale = 1;
const bounceSteps = 60;
if (direction === 'opening') {
for (let i = 0; i < bounceSteps; i++) {
const progress = i / bounceSteps;
// Scale up slightly then back to normal
scale = 1 + 0.003 * Math.sin(progress * Math.PI);
// If we are at the last step, ensure scale is exactly 1
if (i === bounceSteps - 1) {
scale = 1;
}
sequence.transform.push(`translate(-50%, -50%) scale(${scale})`);
sequence.top.push(sequence.top[sequence.top.length - 1]);
sequence.left.push(sequence.left[sequence.left.length - 1]);
sequence.width.push(sequence.width[sequence.width.length - 1]);
sequence.height.push(sequence.height[sequence.height.length - 1]);
}
}
return sequence;
}
@@ -700,6 +724,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
imageDataElement.remove();
}
this.browserWrapper.style.transformOrigin = '';
browserElement.style.minWidth = '';
browserElement.style.minHeight = '';
@@ -866,14 +892,14 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
*/
#animateSidebarButtons(sidebarButtons) {
if (sidebarButtons) {
gZenUIManager
.elementAnimate(
gZenUIManager.motion
.animate(
sidebarButtons,
{ opacity: [1, 0] },
{
duration: 100,
easing: 'ease-in-out',
fill: 'forwards',
duration: 0.2,
type: 'spring',
bounce: this.#GLANCE_ANIMATION_DURATION - 0.1,
}
)
.then(() => {
@@ -902,8 +928,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @param {Element} browserSidebarContainer - The sidebar container
*/
#animateParentBackgroundClose(browserSidebarContainer) {
gZenUIManager
.elementAnimate(
gZenUIManager.motion
.animate(
browserSidebarContainer,
{
scale: [0.98, 1],
@@ -911,7 +937,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
},
{
duration: this.#GLANCE_ANIMATION_DURATION / 1.5,
easing: 'ease-in-out',
type: 'spring',
bounce: 0,
}
)
.then(() => {
@@ -938,9 +965,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
const closingData = this.#createClosingDataFromOriginalPosition(originalPosition);
const arcSequence = this.#createGlanceArcSequence(closingData, 'closing');
gZenUIManager
.elementAnimate(this.browserWrapper, arcSequence, {
gZenUIManager.motion
.animate(this.browserWrapper, arcSequence, {
duration: this.#GLANCE_ANIMATION_DURATION,
ease: 'easeOut',
})
.then(() => {
// Remove element preview after closing animation
@@ -1457,7 +1485,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.browserWrapper.style.width = `${browserRect.width}px`;
this.browserWrapper.style.height = `${browserRect.height}px`;
await gZenUIManager.elementAnimate(
await gZenUIManager.motion.animate(
this.browserWrapper,
{
width: ['85%', '100%'],
@@ -1465,7 +1493,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
},
{
duration: this.#GLANCE_ANIMATION_DURATION,
easing: 'ease-in-out',
type: 'spring',
bounce: 0,
}
);

View File

@@ -125,7 +125,7 @@
left: 0;
flex: unset !important;
/* Promote to its own layer during transitions to reduce jank */
will-change: transform;
will-change: transform, top, left;
width: 85%;
height: 100%;
@@ -148,10 +148,6 @@
box-shadow: var(--zen-big-shadow);
}
&:not([has-finished-animation='true']) .browserStack {
transform: translate(-50%, -50%);
}
& browser {
background: light-dark(rgb(255, 255, 255), rgb(32, 32, 32)) !important;
width: 100%;

View File

@@ -15,4 +15,5 @@ DIRS += [
"urlbar",
"toolkit",
"sessionstore",
"share",
]

View File

@@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
SessionStore: 'resource:///modules/sessionstore/SessionStore.sys.mjs',
SessionSaver: 'resource:///modules/sessionstore/SessionSaver.sys.mjs',
setTimeout: 'resource://gre/modules/Timer.sys.mjs',
gWindowSyncEnabled: 'resource:///modules/zen/ZenWindowSync.sys.mjs',
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gShouldLog', 'zen.session-store.log', true);
@@ -71,10 +72,6 @@ export class nsZenSessionManager {
compression: SHOULD_COMPRESS_FILE ? 'lz4' : undefined,
backupFile,
});
lazy.SessionStore.promiseAllWindowsRestored.then(() => {
delete this._migrationData;
});
}
log(...args) {
@@ -125,6 +122,7 @@ export class nsZenSessionManager {
*/
async readFile() {
try {
this.log('Reading Zen session file from disk');
let promises = [];
promises.push(this.#file.load());
if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) {
@@ -145,6 +143,7 @@ export class nsZenSessionManager {
* The initial session state read from the session file.
*/
onFileRead(initialState) {
if (!lazy.gWindowSyncEnabled) return;
// For the first time after migration, we restore the tabs
// That where going to be restored by SessionStore. The sidebar
// object will always be empty after migration because we haven't
@@ -175,6 +174,7 @@ export class nsZenSessionManager {
// Save the state to the sidebar object so that it gets written
// to the session file.
this.saveState(initialState);
delete this._migrationData;
return;
}
// If there are no windows, we create an empty one. By default,
@@ -233,7 +233,7 @@ export class nsZenSessionManager {
* @param state The current session state.
*/
saveState(state) {
if (!state?.windows?.length) {
if (!state?.windows?.length || !lazy.gWindowSyncEnabled) {
// Don't save (or even collect) anything in permanent private
// browsing mode. We also don't want to save if there are no windows.
return;
@@ -350,7 +350,7 @@ export class nsZenSessionManager {
* Whether this new window is being restored from a closed window.
*/
restoreNewWindow(aWindow, SessionStoreInternal, fromClosedWindow = false) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled || !lazy.gWindowSyncEnabled) {
return;
}
this.log('Restoring new window with Zen session data');
@@ -401,6 +401,7 @@ export class nsZenSessionManager {
* @returns
*/
onNewEmptySession(aWindow) {
this.log('Restoring empty session with Zen session data');
aWindow.gZenWorkspaces.restoreWorkspacesFromSessionStore({
spaces: this.#sidebar.spaces || [],
});

View File

@@ -68,6 +68,12 @@ class nsZenWindowSync {
lastHandlerPromise: Promise.resolve(),
};
/**
* Map of sync handlers for different event types.
* Each handler is a function that takes the event as an argument.
*/
#syncHandlers = new Set();
/**
* Last focused window.
* Used to determine which window to sync tab contents visibility from.
@@ -90,12 +96,7 @@ class nsZenWindowSync {
#browserWindows = {
*[Symbol.iterator]() {
for (let window of lazy.BrowserWindowTracker.orderedWindows) {
if (
window.__SSi &&
!window.closed &&
window.gZenStartup.isReady &&
!window.gZenWorkspaces?.privateWindowOrDisabled
) {
if (window.__SSi && !window.closed && !window.gZenWorkspaces?.privateWindowOrDisabled) {
yield window;
}
}
@@ -280,6 +281,25 @@ class nsZenWindowSync {
});
}
/**
* Adds a sync handler for a specific event type.
* @param {Function} aHandler - The sync handler function to add.
*/
addSyncHandler(aHandler) {
if (!aHandler || this.#syncHandlers.has(aHandler)) {
return;
}
this.#syncHandlers.add(aHandler);
}
/**
* Removes a sync handler for a specific event type.
* @param {Function} aHandler - The sync handler function to remove.
*/
removeSyncHandler(aHandler) {
this.#syncHandlers.delete(aHandler);
}
/**
* Handles the next event by calling the appropriate handler method.
*
@@ -289,7 +309,17 @@ class nsZenWindowSync {
const handler = `on_${aEvent.type}`;
try {
if (typeof this[handler] === 'function') {
return this[handler](aEvent) || Promise.resolve();
let promise = this[handler](aEvent) || Promise.resolve();
promise.then(() => {
for (let syncHandler of this.#syncHandlers) {
try {
syncHandler(aEvent);
} catch (e) {
console.error(e);
}
}
});
return promise;
} else {
throw new Error(`No handler for event type: ${aEvent.type}`);
}
@@ -308,7 +338,7 @@ class nsZenWindowSync {
}
let permanentKey = aTab.linkedBrowser.permanentKey;
this.#runOnAllWindows(null, (win) => {
const tab = this.#getItemFromWindow(win, aTab.id);
const tab = this.getItemFromWindow(win, aTab.id);
if (tab) {
tab.linkedBrowser.permanentKey = permanentKey;
tab.permanentKey = permanentKey;
@@ -323,7 +353,7 @@ class nsZenWindowSync {
* @param {string} aItemId - The ID of the item to retrieve.
* @returns {MozTabbrowserTab|MozTabbrowserTabGroup|null} The item element if found, otherwise null.
*/
#getItemFromWindow(aWindow, aItemId) {
getItemFromWindow(aWindow, aItemId) {
if (!aItemId) {
return null;
}
@@ -453,7 +483,7 @@ class nsZenWindowSync {
let container;
const parentGroup = aOriginalItem.group;
if (parentGroup?.hasAttribute('id')) {
container = this.#getItemFromWindow(aWindow, parentGroup.getAttribute('id'));
container = this.getItemFromWindow(aWindow, parentGroup.getAttribute('id'));
if (container) {
if (container?.tabs?.length) {
// First tab in folders is the empty tab placeholder.
@@ -480,7 +510,7 @@ class nsZenWindowSync {
}
return;
}
const relativeTab = this.#getItemFromWindow(aWindow, originalSibling.id);
const relativeTab = this.getItemFromWindow(aWindow, originalSibling.id);
if (relativeTab) {
gBrowser.tabContainer.tabDragAndDrop.handle_drop_transition(
relativeTab,
@@ -502,7 +532,7 @@ class nsZenWindowSync {
#syncItemForAllWindows(aItem, flags = 0) {
const window = aItem.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
this.#syncItemWithOriginal(aItem, this.#getItemFromWindow(win, aItem.id), win, flags);
this.#syncItemWithOriginal(aItem, this.getItemFromWindow(win, aItem.id), win, flags);
});
}
@@ -713,7 +743,7 @@ class nsZenWindowSync {
*/
#getActiveTabFromOtherWindows(aWindow, aTabId, filter = (tab) => tab?._zenContentsVisible) {
return this.#runOnAllWindows(aWindow, (win) => {
const tab = this.#getItemFromWindow(win, aTabId);
const tab = this.getItemFromWindow(win, aTabId);
if (filter(tab)) {
return tab;
}
@@ -735,7 +765,7 @@ class nsZenWindowSync {
(tab) => tab._zenContentsVisible
);
for (let tab of activeTabsOnClosedWindow) {
const targetTab = this.#getItemFromWindow(mostRecentWindow, tab.id);
const targetTab = this.getItemFromWindow(mostRecentWindow, tab.id);
if (targetTab) {
targetTab._zenContentsVisible = true;
this.log(`Moving active tab ${tab.id} to most recent window on close`);
@@ -830,7 +860,7 @@ class nsZenWindowSync {
image: state.image,
};
this.#runOnAllWindows(null, (win) => {
const targetTab = this.#getItemFromWindow(win, aTab.id);
const targetTab = this.getItemFromWindow(win, aTab.id);
if (targetTab) {
targetTab._zenPinnedInitialState = initialState;
}
@@ -958,7 +988,7 @@ class nsZenWindowSync {
on_TabUnpinned(aEvent) {
const tab = aEvent.target;
this.#runOnAllWindows(null, (win) => {
const targetTab = this.#getItemFromWindow(win, tab.id);
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab) {
delete targetTab._zenPinnedInitialState;
}
@@ -978,7 +1008,7 @@ class nsZenWindowSync {
const tab = aEvent.target;
const window = tab.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
const targetTab = this.#getItemFromWindow(win, tab.id);
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab) {
win.gBrowser.removeTab(targetTab, { animate: true });
}
@@ -1052,7 +1082,7 @@ class nsZenWindowSync {
const tabGroup = aEvent.target;
const window = tabGroup.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
const targetGroup = this.#getItemFromWindow(win, tabGroup.id);
const targetGroup = this.getItemFromWindow(win, tabGroup.id);
if (targetGroup) {
if (targetGroup.isZenFolder) {
targetGroup.delete();
@@ -1075,7 +1105,7 @@ class nsZenWindowSync {
const tab = aEvent.target;
const window = tab.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
const targetTab = this.#getItemFromWindow(win, tab.id);
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab && win.gZenViewSplitter) {
win.gZenViewSplitter.removeTabFromGroup(targetTab);
}
@@ -1088,7 +1118,7 @@ class nsZenWindowSync {
const tabs = tabGroup.tabs;
this.#runOnAllWindows(window, (win) => {
const otherWindowTabs = tabs
.map((tab) => this.#getItemFromWindow(win, tab.id))
.map((tab) => this.getItemFromWindow(win, tab.id))
.filter(Boolean);
if (otherWindowTabs.length > 0 && win.gZenViewSplitter) {
const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, 'grid', -1);
@@ -1104,4 +1134,5 @@ class nsZenWindowSync {
}
}
export const gWindowSyncEnabled = lazy.gWindowSyncEnabled;
export const ZenWindowSync = new nsZenWindowSync();

7
src/zen/share/moz.build Normal file
View File

@@ -0,0 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES.zen.share += [
"share.schema.json",
]

View File

@@ -0,0 +1,154 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Share Schema",
"type": "object",
"properties": {
"version": {
"type": "string"
},
"shared": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/shareable"
}
}
},
"required": ["shared"],
"additionalProperties": false,
"$defs": {
"shareable": {
"oneOf": [
{ "$ref": "#/$defs/space" },
{ "$ref": "#/$defs/folder" },
{ "$ref": "#/$defs/splitView" },
{ "$ref": "#/$defs/gradientPreset" }
]
},
"gradientPreset": {
"type": "object",
"properties": {
"type": { "const": "gradientPreset" },
"theme": { "$ref": "#/$defs/theme" }
},
"required": ["type", "theme"],
"additionalProperties": false
},
"space": {
"type": "object",
"properties": {
"type": { "const": "space" },
"name": { "type": "string" },
"theme": { "$ref": "#/$defs/theme" },
"items": {
"type": "array",
"items": { "$ref": "#/$defs/spaceItem" }
}
},
"required": ["type", "name", "items"],
"additionalProperties": false
},
"theme": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["gradient"]
},
"gradientColors": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/gradientColor" }
}
},
"required": ["type", "gradientColors"],
"additionalProperties": false
},
"gradientColor": {
"type": "object",
"properties": {
"c": {
"type": "array",
"items": { "type": "number" },
"minItems": 3,
"maxItems": 3
},
"isCustom": { "type": "boolean" },
"algorithm": { "type": "string" },
"isPrimary": { "type": "boolean" },
"lightness": { "type": "string" },
"position": {
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" }
},
"required": ["x", "y"],
"additionalProperties": false
},
"type": { "type": "string" }
},
"required": ["c", "type"],
"additionalProperties": false
},
"folder": {
"type": "object",
"properties": {
"type": { "const": "folder" },
"name": { "type": "string" },
"icon": { "type": "string" },
"items": {
"type": "array",
"items": { "$ref": "#/$defs/folderItem" }
}
},
"required": ["type", "name", "items"],
"additionalProperties": false
},
"tab": {
"type": "object",
"properties": {
"type": { "const": "tab" },
"url": {
"type": "string",
"format": "uri"
},
"label": { "type": "string" },
"image": { "type": "string" },
"isPinned": { "type": "boolean" }
},
"required": ["type", "url"],
"additionalProperties": false
},
"splitView": {
"type": "object",
"properties": {
"type": { "const": "splitView" },
"tabs": {
"type": "array",
"minItems": 2,
"maxItems": 4,
"items": { "$ref": "#/$defs/tab" }
}
},
"required": ["type", "tabs"],
"additionalProperties": false
},
"spaceItem": {
"oneOf": [{ "$ref": "#/$defs/tab" }, { "$ref": "#/$defs/folder" }]
},
"folderItem": {
"oneOf": [{ "$ref": "#/$defs/tab" }, { "$ref": "#/$defs/folder" }]
}
}
}

View File

@@ -1063,6 +1063,7 @@
@media -moz-pref('zen.tabs.show-newtab-vertical') {
#tabs-newtab-button {
max-height: var(--tab-min-height);
display: flex !important;
transition: scale 0.1s ease;
#tabbrowser-tabs[movingtab] & {

View File

@@ -13,6 +13,7 @@ BROWSER_CHROME_MANIFESTS += [
"ub-actions/browser.toml",
"urlbar/browser.toml",
"welcome/browser.toml",
"window_sync/browser.toml",
"workspaces/browser.toml",
]

View File

@@ -0,0 +1,12 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[DEFAULT]
prefs = ["zen.window-sync.enabled=true", "zen.urlbar.replace-newtab=false"]
support-files = [
"head.js",
]
["browser_sync_tab_open.js"]
["browser_sync_tab_label.js"]

View File

@@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_SimpleLabelChange() {
let newLabel = 'Test Label';
await withNewTabAndWindow(async (newTab, win) => {
let otherTab = gZenWindowSync.getItemFromWindow(win, newTab.id);
await runSyncAction(
() => {
gBrowser._setTabLabel(newTab, newLabel);
Assert.equal(newTab.label, newLabel, 'The original tab label should be changed');
},
async () => {
Assert.equal(
otherTab.label,
newLabel,
'The synced tab label should match the changed label'
);
},
'ZenTabLabelChanged'
);
});
});
add_task(async function test_DontChangeBluredTabLabel() {
let newLabel = 'Test Label';
await withNewTabAndWindow(async (newTab, win) => {
let otherTab = gZenWindowSync.getItemFromWindow(win, newTab.id);
Assert.ok(!otherTab._zenContentsVisible, 'The synced tab should be blured');
gBrowser._setTabLabel(newTab, newLabel);
Assert.notEqual(
otherTab.label,
newLabel,
'The synced tab label should NOT match the changed label'
);
});
});

View File

@@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_SimpleTabOpen() {
await withNewTabAndWindow(async (newTab, win) => {
let tabId = newTab.id;
let otherTab = gZenWindowSync.getItemFromWindow(win, tabId);
Assert.ok(otherTab, 'The opened tab should be found in the synced window');
Assert.ok(newTab._zenContentsVisible, 'The opened tab should be visible');
Assert.equal(otherTab.id, tabId, 'The opened tab ID should match the synced tab ID');
});
});

View File

@@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
async function withNewSyncedWindow(action) {
await gZenWorkspaces.promiseInitialized;
const win = await BrowserTestUtils.openNewBrowserWindow();
await win.gZenWorkspaces.promiseInitialized;
await action(win);
await BrowserTestUtils.closeWindow(win);
}
async function runSyncAction(action, callback, type) {
await new Promise((resolve) => {
window.gZenWindowSync.addSyncHandler(async function handler(aEvent) {
if (aEvent.type === type) {
window.gZenWindowSync.removeSyncHandler(handler);
await callback(aEvent);
resolve();
}
});
action();
});
}
function getTabState(tab) {
return JSON.parse(SessionStore.getTabState(tab));
}
async function withNewTabAndWindow(action) {
let newTab = null;
await withNewSyncedWindow(async (win) => {
await runSyncAction(
() => {
newTab = gBrowser.addTrustedTab('https://example.com/', { inBackground: true });
},
async (aEvent) => {
Assert.equal(aEvent.type, 'TabOpen', 'Event type should be TabOpen');
await action(newTab, win);
},
'TabOpen'
);
});
let portalTabClosing = BrowserTestUtils.waitForTabClosing(newTab);
BrowserTestUtils.removeTab(newTab);
await portalTabClosing;
}

View File

@@ -80,7 +80,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
if (window.gZenWorkspaces.privateWindowOrDisabled) {
return [];
}
const workspaces = window.gZenWorkspaces._workspaceCache?.workspaces;
const workspaces = window.gZenWorkspaces.getWorkspaces();
if (!workspaces?.length) {
return [];
}

View File

@@ -465,7 +465,7 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
let hue = (angle / 360) * 360; // Normalize angle to [0, 360)
let saturation = normalizedDistance * 100; // stays high even in center
if (type !== EXPLICIT_LIGHTNESS_TYPE) {
saturation = 80 + (1 - normalizedDistance) * 20;
saturation = 90 + (1 - normalizedDistance) * 10;
// Set the current lightness to how far we are from the center of the circle
// For example, moving the dot outside will have higher lightness, while moving it inside will have lower lightness
this.#currentLightness = Math.round((1 - normalizedDistance) * 100);
@@ -1216,9 +1216,9 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
let color2 = this.getSingleRGBColor(themedColors[0], forToolbar);
let color3 = this.getSingleRGBColor(themedColors[1], forToolbar);
return [
`radial-gradient(circle at 0% 0%, ${color2} -10%, transparent 100%)`,
`linear-gradient(to top, ${color1} -50%, transparent 125%)`,
`radial-gradient(circle at 100% -100%, ${color3} -100%, transparent 400%)`,
`linear-gradient(-5deg, ${color1} 10%, transparent 80%)`,
`radial-gradient(circle at 95% 0%, ${color3} 0%, transparent 75%)`,
`radial-gradient(circle at 0% 0%, ${color2} 10%, transparent 70%)`,
].join(', ');
}
}

View File

@@ -158,9 +158,9 @@ export class nsZenWorkspace extends MozXULElement {
});
this.indicator.addEventListener('click', (event) => {
if (this.hasPinnedTabs) {
if (this.hasPinnedTabs && event.button === 0) {
event.stopPropagation();
this.collapsiblePins.collapsed = !this.collapsiblePins.collapsed;
this.collapsiblePins.toggle();
}
});

View File

@@ -207,8 +207,8 @@ class nsZenWorkspaceCreation extends MozXULElement {
await this.#cleanup();
await gZenWorkspaces._organizeWorkspaceStripLocations(workspace, true);
await gZenWorkspaces.updateTabsContainers();
gZenWorkspaces._organizeWorkspaceStripLocations(workspace, true);
gZenWorkspaces.updateTabsContainers();
gBrowser.tabContainer._invalidateCachedTabs();
}
@@ -321,8 +321,8 @@ class nsZenWorkspaceCreation extends MozXULElement {
gZenUIManager.updateTabsToolbar();
const workspace = gZenWorkspaces.getActiveWorkspace();
await gZenWorkspaces._organizeWorkspaceStripLocations(workspace);
await gZenWorkspaces.updateTabsContainers();
gZenWorkspaces._organizeWorkspaceStripLocations(workspace);
gZenWorkspaces.updateTabsContainers();
await gZenUIManager.motion.animate(
[gBrowser.tabContainer, gURLBar.textbox],

View File

@@ -119,8 +119,10 @@ class nsZenWorkspaces {
window.addEventListener('resize', this.onWindowResize.bind(this));
this.addPopupListeners();
await this.#waitForPromises();
await this.afterLoadInit();
if (this.privateWindowOrDisabled) {
await this.#waitForPromises();
await this.restoreWorkspacesFromSessionStore({});
}
}
log(...args) {
@@ -129,11 +131,15 @@ class nsZenWorkspaces {
}
}
async afterLoadInit() {
if (!this._hasInitializedTabsStrip) {
await this.delayedStartup();
}
this._initializeWorkspaceTabContextMenus();
async #afterLoadInit() {
const onResize = (...args) => {
requestAnimationFrame(() => {
this.onPinnedTabsResize(...args);
});
};
this._pinnedTabsResizeObserver = new ResizeObserver(onResize);
this.registerPinnedResizeObserver();
this.#initializeWorkspaceTabContextMenus();
// Non UI related initializations
if (
@@ -274,21 +280,6 @@ class nsZenWorkspaces {
}
}
async delayedStartup() {
if (!this.workspaceEnabled) {
return;
}
const onResize = (...args) => {
requestAnimationFrame(() => {
this.onPinnedTabsResize(...args);
});
};
this._pinnedTabsResizeObserver = new ResizeObserver(onResize);
if (this.privateWindowOrDisabled) {
await this.restoreWorkspacesFromSessionStore({});
}
}
#initializeEmptyTab() {
for (const tab of gBrowser.tabs) {
// Check if session store has an empty tab
@@ -310,9 +301,6 @@ class nsZenWorkspaces {
return;
}
this._pinnedTabsResizeObserver.disconnect();
for (let element of document.querySelectorAll('.zen-workspace-pinned-tabs-section')) {
this._pinnedTabsResizeObserver.observe(element, { box: 'border-box' });
}
for (let element of document.getElementById('zen-essentials').children) {
if (element.classList.contains('tabbrowser-tab')) {
continue;
@@ -402,7 +390,6 @@ class nsZenWorkspaces {
}
perifery.setAttribute('hidden', 'true');
this._hasInitializedTabsStrip = true;
this.registerPinnedResizeObserver();
this._fixIndicatorsNames(workspaces);
}
@@ -438,7 +425,9 @@ class nsZenWorkspaces {
getCurrentSpaceContainerId() {
const currentWorkspace = this.getActiveWorkspaceFromCache();
return currentWorkspace?.containerTabId || 0;
return typeof currentWorkspace?.containerTabId === 'number'
? currentWorkspace.containerTabId
: 0;
}
getCurrentEssentialsContainer() {
@@ -812,10 +801,6 @@ class nsZenWorkspaces {
);
return ZenSessionStore.getClonedSpaces();
}
if (!this.currentWindowIsSyncing) {
this._workspaceCache = this._tempWorkspace ? [this._tempWorkspace] : [];
this.#activeWorkspace = this._tempWorkspace?.uuid;
}
return [...this._workspaceCache];
}
@@ -863,7 +848,7 @@ class nsZenWorkspaces {
const spacesFromStore = aWinData.spaces || [];
this._workspaceCache = spacesFromStore.length
? [...spacesFromStore]
: [this.#createWorkspaceData('Space', undefined, true)];
: [this.#createWorkspaceData('Space', undefined)];
this.activeWorkspace = aWinData.activeZenSpace || this._workspaceCache[0].uuid;
let promise = this.#initializeWorkspaces();
for (const workspace of spacesFromStore) {
@@ -901,6 +886,8 @@ class nsZenWorkspaces {
this.#initializeTabsStripSections();
this.#initializeEmptyTab();
return (async () => {
await this.#waitForPromises();
await this.#afterLoadInit();
await this.workspaceBookmarks();
await this.changeWorkspace(activeWorkspace, { onInit: true });
this.#fixTabPositions();
@@ -925,7 +912,7 @@ class nsZenWorkspaces {
}
async selectStartPage() {
if (!this.workspaceEnabled) {
if (!this.workspaceEnabled || gZenUIManager.testingEnabled) {
return;
}
await this.promiseInitialized;
@@ -1336,7 +1323,7 @@ class nsZenWorkspaces {
propagateWorkspaces(aWorkspaces) {
const previousWorkspaces = this._workspaceCache || [];
this._workspaceCache = aWorkspaces;
let promises = [];
let hasChanged = false;
// Remove any workspace elements here that no longer exist
for (const previousWorkspace of previousWorkspaces) {
@@ -1344,14 +1331,18 @@ class nsZenWorkspaces {
this.workspaceElement(previousWorkspace.uuid) &&
!aWorkspaces.find((w) => w.uuid === previousWorkspace.uuid)
) {
let promise = Promise.resolve();
if (this.isWorkspaceActive(previousWorkspace)) {
// If the removed workspace was active, switch to another one
const newActiveWorkspace =
aWorkspaces.find((w) => w.uuid !== previousWorkspace.uuid) || null;
this.changeWorkspace(newActiveWorkspace);
promise = this.changeWorkspace(newActiveWorkspace);
}
this.workspaceElement(previousWorkspace.uuid)?.remove();
delete this.lastSelectedWorkspaceTabs[previousWorkspace.uuid];
promise = promise.then(() => {
this.workspaceElement(previousWorkspace.uuid)?.remove();
delete this.lastSelectedWorkspaceTabs[previousWorkspace.uuid];
});
promises.push(promise);
hasChanged = true;
}
}
@@ -1378,13 +1369,15 @@ class nsZenWorkspaces {
previousElement = workspaceElement;
}
}
if (hasChanged) {
this.#fireSpaceUIUpdate();
}
this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache()).finally(() => {
return Promise.all(promises).then(() => {
this._workspaceCache = aWorkspaces;
if (hasChanged) {
this.#fireSpaceUIUpdate();
}
this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache());
this.updateTabsContainers();
this.updateWorkspacesChangeContextMenu();
});
this.updateWorkspacesChangeContextMenu();
}
async reorderWorkspace(id, newPosition) {
@@ -1616,13 +1609,15 @@ class nsZenWorkspaces {
this.tabContainer._invalidateCachedTabs();
if (!whileScrolling) {
await this._organizeWorkspaceStripLocations(previousWorkspace);
this._organizeWorkspaceStripLocations(previousWorkspace);
}
// Second pass: Handle tab selection
this.tabContainer._invalidateCachedTabs();
const tabToSelect = await this._handleTabSelection(workspace, onInit, previousWorkspace.uuid);
gBrowser.warmupTab(tabToSelect);
if (tabToSelect.linkedBrowser) {
gBrowser.warmupTab(tabToSelect);
}
// Update UI and state
const previousWorkspaceIndex = workspaces.findIndex((w) => w.uuid === previousWorkspace.uuid);
@@ -1675,7 +1670,7 @@ class nsZenWorkspaces {
}
}
_updatePaddingTopOnTabs(
#updatePaddingTopOnTabs(
workspaceElement,
essentialContainer,
forAnimation = false,
@@ -1707,7 +1702,7 @@ class nsZenWorkspaces {
}
}
async _organizeWorkspaceStripLocations(workspace, justMove = false, offsetPixels = 0) {
_organizeWorkspaceStripLocations(workspace, justMove = false, offsetPixels = 0) {
if (document.documentElement.hasAttribute('zen-creating-workspace')) {
// If we are creating a workspace, we don't want to animate the strip
return;
@@ -1769,24 +1764,19 @@ class nsZenWorkspaces {
} = gZenThemePicker.getGradientForWorkspace(nextWorkspace);
const existingGrain = gZenThemePicker.getGradientForWorkspace(workspace).grain;
const percentage = Math.abs(offsetPixels) / 200;
await new Promise((resolve) => {
requestAnimationFrame(() => {
document.documentElement.style.setProperty('--zen-background-opacity', 1 - percentage);
if (!this._hasAnimatedBackgrounds) {
this._hasAnimatedBackgrounds = true;
document.documentElement.style.setProperty(
'--zen-main-browser-background-old',
nextGradient
);
document.documentElement.style.setProperty(
'--zen-main-browser-background-toolbar-old',
nextToolbarGradient
);
document.documentElement.setAttribute('animating-background', 'true');
}
resolve();
});
});
document.documentElement.style.setProperty('--zen-background-opacity', 1 - percentage);
if (!this._hasAnimatedBackgrounds) {
this._hasAnimatedBackgrounds = true;
document.documentElement.style.setProperty(
'--zen-main-browser-background-old',
nextGradient
);
document.documentElement.style.setProperty(
'--zen-main-browser-background-toolbar-old',
nextToolbarGradient
);
document.documentElement.setAttribute('animating-background', 'true');
}
// Fit the offsetPixels into the grain limits. Both ends may be nextGrain and existingGrain,
// so we need to use the min and max of both. For example, existing may be 0.2 and next may be 0.5,
// meaning we should convert the offset to a percentage between 0.2 and 0.5. BUT if existingGrain
@@ -1894,28 +1884,23 @@ class nsZenWorkspaces {
previousBackgroundOpacity = 1 - previousBackgroundOpacity;
}
gZenThemePicker.previousBackgroundOpacity = previousBackgroundOpacity;
await new Promise((resolve) => {
requestAnimationFrame(() => {
document.documentElement.style.setProperty(
'--zen-background-opacity',
previousBackgroundOpacity
);
animations.push(
gZenUIManager.motion.animate(
document.documentElement,
{
'--zen-background-opacity': [previousBackgroundOpacity, 1],
},
{
type: 'spring',
bounce: 0,
duration: kGlobalAnimationDuration,
}
)
);
resolve();
});
});
document.documentElement.style.setProperty(
'--zen-background-opacity',
previousBackgroundOpacity
);
animations.push(
gZenUIManager.motion.animate(
document.documentElement,
{
'--zen-background-opacity': [previousBackgroundOpacity, 1],
},
{
type: 'spring',
bounce: 0,
duration: kGlobalAnimationDuration,
}
)
);
}
for (const element of document.querySelectorAll('zen-workspace')) {
if (element.classList.contains('zen-essentials-container')) {
@@ -2129,7 +2114,7 @@ class nsZenWorkspaces {
cloned.container.remove();
}
this._alwaysAnimatePaddingTop = true;
await this.updateTabsContainers();
this.updateTabsContainers();
}
const essentialsContainer = this.getEssentialsSection(newWorkspace.containerTabId);
essentialsContainer.removeAttribute('hidden');
@@ -2266,7 +2251,9 @@ class nsZenWorkspaces {
gBrowser.tabContainer.arrowScrollbox = this.activeScrollbox;
// Update workspace UI
gZenThemePicker.onWorkspaceChange(workspace);
requestAnimationFrame(() => {
gZenThemePicker.onWorkspaceChange(workspace);
});
gZenUIManager.tabsWrapper.scrollbarWidth = 'none';
this.workspaceIcons.activeIndex = workspace.uuid;
@@ -2275,7 +2262,7 @@ class nsZenWorkspaces {
previousWorkspace,
onInit,
});
await this._organizeWorkspaceStripLocations(workspace, true);
this._organizeWorkspaceStripLocations(workspace, true);
gZenUIManager.tabsWrapper.style.scrollbarWidth = '';
// Notify listeners
@@ -2385,6 +2372,11 @@ class nsZenWorkspaces {
}
#createWorkspaceData(name, icon, containerTabId = 0) {
if (!this.currentWindowIsSyncing) {
containerTabId = parseInt(gBrowser.selectedTab.getAttribute('usercontextid')) || 0;
let label = ContextualIdentityService.getUserContextLabel(containerTabId) || 'Default';
name = this.isPrivateWindow ? 'Private ' + name : label;
}
let workspace = {
uuid: gZenUIManager.generateUuidv4(),
icon: icon,
@@ -2405,11 +2397,6 @@ class nsZenWorkspaces {
if (!this.workspaceEnabled) {
return;
}
if (!this.currentWindowIsSyncing) {
containerTabId = parseInt(gBrowser.selectedTab.getAttribute('usercontextid')) || 0;
let label = ContextualIdentityService.getUserContextLabel(containerTabId) || 'Default';
name = this.isPrivateWindow ? 'Private ' + name : label;
}
// get extra tabs remaning (e.g. on new profiles) and just move them to the new workspace
const extraTabs = Array.from(gBrowser.tabContainer.arrowScrollbox.children).filter(
(child) =>
@@ -2422,13 +2409,9 @@ class nsZenWorkspaces {
if (!dontChange) {
this.#prepareNewWorkspace(workspaceData);
this.#createWorkspaceTabsSection(workspaceData, extraTabs);
await this._organizeWorkspaceStripLocations(workspaceData);
}
if (!this.currentWindowIsSyncing) {
this._tempWorkspace = workspaceData;
} else {
this.saveWorkspace(workspaceData);
this._organizeWorkspaceStripLocations(workspaceData);
}
this.saveWorkspace(workspaceData);
if (!dontChange) {
if (beforeChangeCallback) {
try {
@@ -2438,7 +2421,7 @@ class nsZenWorkspaces {
}
}
this.registerPinnedResizeObserver();
await this.updateTabsContainers({
this.updateTabsContainers({
target: this.workspaceElement(workspaceData.uuid).pinnedTabsContainer,
});
let changed = extraTabs.length > 0;
@@ -2532,8 +2515,7 @@ class nsZenWorkspaces {
const workspacesIds = [];
if (entry.target.closest('#zen-essentials')) {
// Get all workspaces that have the same userContextId
const activeWorkspace = this.getActiveWorkspace();
const userContextId = activeWorkspace.containerTabId;
const userContextId = parseInt(entry.target.getAttribute('container') || '0');
const workspaces = this.getWorkspaces().filter((w) => w.containerTabId === userContextId);
workspacesIds.push(...workspaces.map((w) => w.uuid));
} else {
@@ -2563,7 +2545,7 @@ class nsZenWorkspaces {
} else {
essentialContainer.removeAttribute('data-hack-type');
}
this._updatePaddingTopOnTabs(
this.#updatePaddingTopOnTabs(
workspaceElement,
essentialContainer,
forAnimation,
@@ -2727,7 +2709,7 @@ class nsZenWorkspaces {
await this.changeWorkspace(nextWorkspace, { whileScrolling });
}
_initializeWorkspaceTabContextMenus() {
#initializeWorkspaceTabContextMenus() {
if (this.privateWindowOrDisabled) {
const commandsToDisable = [
'cmd_zenOpenFolderCreation',

View File

@@ -41,9 +41,12 @@ zen-workspace-creation {
}
& xul|button {
border: none;
margin: 0;
font-weight: 400 !important;
&:not(.footer-button.primary) {
border: none;
}
}
& .zen-workspace-creation-name-wrapper {

View File

@@ -188,6 +188,10 @@
pointer-events: none;
}
:root[zen-unsynced-window='true'][zen-sidebar-expanded='true'] & {
padding: calc(-2px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
}
:root:not([zen-unsynced-window]) & {
&:hover,
&[open='true'] {
@@ -243,7 +247,6 @@
padding: 4px 8px;
border-radius: 99px;
font-size: 10px;
margin-inline-start: -6px;
}
}

View File

@@ -326,7 +326,7 @@ fn is_twilight_build() -> bool {
if let Ok(content) = fs::read_to_string(&dynamic_config_path) {
return !content.contains("\"release\"");
}
false
true
}
fn get_env_values() -> HashMap<String, bool> {