From 2ee1275f2847d0e917233d8ccd496f58a83755c8 Mon Sep 17 00:00:00 2001 From: fen4flo <75260616+FlorianButz@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:03:20 +0200 Subject: [PATCH] gh-15438: Implementing Zen Library (gh-15282) Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com> Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com> --- locales/en-US/browser/browser/zen-library.ftl | 88 + prefs/zen/library.yaml | 9 + src/-stylelintignore.patch | 5 +- .../content/navigator-toolbox-inc-xhtml.patch | 10 +- .../base/content/zen-assets.jar.inc.mn | 1 + .../base/content/zen-commands.inc.xhtml | 2 + .../base/content/zen-locales.inc.xhtml | 1 + .../urlbar/content/UrlbarInputBase-mjs.patch | 90 +- .../common/library/library-boosts-sprite.svg | 2634 +++++++++++++++++ .../library/library-downloads-sprite.svg | 294 ++ .../common/library/library-history-sprite.svg | 1014 +++++++ .../common/library/library-media-sprite.svg | 1302 ++++++++ .../common/library/library-spaces-sprite.svg | 1014 +++++++ .../common/library/library-sprite.svg | 1059 +++++++ .../zen-icons/common/library/library.svg | 68 + .../themes/shared/zen-icons/jar.inc.mn | 7 + .../shared/zen-icons/update-resources.sh | 7 + src/zen/boosts/ZenBoostsEditor.mjs | 4 +- src/zen/boosts/ZenBoostsManager.sys.mjs | 63 +- src/zen/common/jar.inc.mn | 2 +- src/zen/common/styles/zen-animations.css | 32 + src/zen/common/styles/zen-browser-ui.css | 2 +- src/zen/common/styles/zen-library-widget.css | 297 ++ .../common/styles/zen-single-components.css | 12 + src/zen/common/sys/ZenCustomizableUI.sys.mjs | 10 +- src/zen/common/zen-sets.js | 8 + src/zen/compact-mode/ZenCompactMode.mjs | 9 + src/zen/downloads/ZenDownloadAnimation.mjs | 8 +- .../downloads/zen-download-box-animation.css | 2 +- src/zen/drag-and-drop/ZenDragAndDrop.js | 189 +- src/zen/glance/ZenGlanceManager.mjs | 167 +- src/zen/glance/zen-glance.css | 21 +- src/zen/library/ZenLibrary.mjs | 592 ++++ src/zen/library/ZenLibraryDragAndDrop.mjs | 347 +++ src/zen/library/ZenLibraryWidget.sys.mjs | 611 ++++ src/zen/library/jar.inc.mn | 5 + src/zen/library/moz.build | 15 + .../sections/ZenLibraryBoostsSection.mjs | 278 ++ .../sections/ZenLibraryDownloadsSection.mjs | 664 +++++ .../sections/ZenLibraryHistorySection.mjs | 361 +++ .../sections/ZenLibrarySearchSection.mjs | 265 ++ .../sections/ZenLibrarySpacesSection.mjs | 820 +++++ src/zen/library/zen-library.css | 984 ++++++ src/zen/moz.build | 1 + src/zen/sessionstore/ZenWindowSync.sys.mjs | 6 + src/zen/spaces/ZenGradientGenerator.mjs | 167 +- src/zen/spaces/ZenSpacesSwipe.mjs | 140 +- src/zen/split-view/ZenViewSplitter.mjs | 9 +- src/zen/tabs/zen-tabs/vertical-tabs.css | 18 +- src/zen/urlbar/ZenUBGlobalActions.sys.mjs | 2 +- 50 files changed, 13534 insertions(+), 182 deletions(-) create mode 100644 locales/en-US/browser/browser/zen-library.ftl create mode 100644 prefs/zen/library.yaml create mode 100644 src/browser/themes/shared/zen-icons/common/library/library-boosts-sprite.svg create mode 100644 src/browser/themes/shared/zen-icons/common/library/library-downloads-sprite.svg create mode 100644 src/browser/themes/shared/zen-icons/common/library/library-history-sprite.svg create mode 100644 src/browser/themes/shared/zen-icons/common/library/library-media-sprite.svg create mode 100644 src/browser/themes/shared/zen-icons/common/library/library-spaces-sprite.svg create mode 100644 src/browser/themes/shared/zen-icons/common/library/library-sprite.svg create mode 100644 src/browser/themes/shared/zen-icons/common/library/library.svg create mode 100644 src/zen/common/styles/zen-library-widget.css create mode 100644 src/zen/library/ZenLibrary.mjs create mode 100644 src/zen/library/ZenLibraryDragAndDrop.mjs create mode 100644 src/zen/library/ZenLibraryWidget.sys.mjs create mode 100644 src/zen/library/jar.inc.mn create mode 100644 src/zen/library/moz.build create mode 100644 src/zen/library/sections/ZenLibraryBoostsSection.mjs create mode 100644 src/zen/library/sections/ZenLibraryDownloadsSection.mjs create mode 100644 src/zen/library/sections/ZenLibraryHistorySection.mjs create mode 100644 src/zen/library/sections/ZenLibrarySearchSection.mjs create mode 100644 src/zen/library/sections/ZenLibrarySpacesSection.mjs create mode 100644 src/zen/library/zen-library.css diff --git a/locales/en-US/browser/browser/zen-library.ftl b/locales/en-US/browser/browser/zen-library.ftl new file mode 100644 index 000000000..5ce0ce675 --- /dev/null +++ b/locales/en-US/browser/browser/zen-library.ftl @@ -0,0 +1,88 @@ +# 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/. + +zen-library-button = + .label = Library + .tooltiptext = Library + +library-footer-close-button = + .tooltiptext = Close library +library-footer-donate-button = + .tooltiptext = Donate to Zen + +library-history-section-title = History +library-downloads-section-title = Downloads +library-boosts-section-title = Boosts +library-spaces-section-title = Spaces + +library-filter-button = Filter +library-filter-done = Done + +library-history-filter-title = Filter History… +library-history-filter-when = When was it visited? +library-filter-today = Today +library-filter-week = This Week +library-filter-month = This Month +library-history-filter-sort = Sort by +library-history-sort-date = By Date +library-history-sort-site = By Site +library-history-sort-most-visited = By Most Visited +library-history-sort-last-visited = By Last Visited +library-history-site-other = Other +library-history-search-placeholder = + .placeholder = Search History… +library-history-empty = No history found +library-history-forget-button = + .title = Remove from history +library-history-reopen-button = + .title = Reopen page + +library-downloads-empty = No downloads found +library-downloads-more-button = + .title = More options +library-downloads-menu-open = + .label = Open { $name } +library-downloads-menu-copy = + .label = Copy { $name } +library-downloads-menu-hide = + .label = Hide from Zen +library-downloads-menu-trash = + .label = Move to Trash +library-downloads-opening-in = { PLATFORM() -> + [macos] Opening in Finder… + [windows] Opening in File Explorer… + *[other] Opening in file manager… +} +library-downloads-cancel-button = + .title = Cancel download +library-downloads-filter-title = Filter Downloads… +library-downloads-filter-type = File type +library-downloads-filter-when = When was it downloaded? +library-downloads-type-images = Images +library-downloads-type-video = Video +library-downloads-type-audio = Audio +library-downloads-type-documents = Documents +library-downloads-type-archives = Archives +library-downloads-type-apps = Apps +library-downloads-retry-button = + .title = Retry download + +library-spaces-theme-button = + .title = Change theme +library-spaces-move-button = + .title = Drag to reorder +library-spaces-actions-button = + .tooltiptext = More options +library-spaces-icon-button = + .title = Change icon +library-spaces-name-button = + .title = Rename space + +library-boosts-search-placeholder = + .placeholder = Search Boosts… +library-boosts-empty = No boosts yet +library-boosts-toggle = + .aria-label = Enable boost +library-boosts-menu-edit = + .label = Edit Boost diff --git a/prefs/zen/library.yaml b/prefs/zen/library.yaml new file mode 100644 index 000000000..9f607decc --- /dev/null +++ b/prefs/zen/library.yaml @@ -0,0 +1,9 @@ +# 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.library.enabled + value: "@IS_TWILIGHT@" + +- name: zen.library.last-tab + value: "history" diff --git a/src/-stylelintignore.patch b/src/-stylelintignore.patch index 050539456..f36325074 100644 --- a/src/-stylelintignore.patch +++ b/src/-stylelintignore.patch @@ -1,8 +1,8 @@ diff --git a/.stylelintignore b/.stylelintignore -index 185490999507b8a5032977237af50f5e61c71df1..77c1635d90c6480af4ebd3c6e3ec49780eaca571 100644 +index 185490999507b8a5032977237af50f5e61c71df1..4b1b1dc76d316f38f60c90e6cbd74d9569800d04 100644 --- a/.stylelintignore +++ b/.stylelintignore -@@ -106,3 +106,19 @@ build/pgo/blueprint/**/*.css +@@ -106,3 +106,20 @@ build/pgo/blueprint/**/*.css # under our control or we don't want to modify at this point: testing/web-platform/mozilla/ testing/web-platform/tests/ @@ -20,5 +20,6 @@ index 185490999507b8a5032977237af50f5e61c71df1..77c1635d90c6480af4ebd3c6e3ec4978 +zen/split-view/zen-split-view.css +zen/spaces/zen-workspaces.css +zen/common/styles/zen-toolbar.css ++zen/common/styles/zen-single-components.css + +*.inc diff --git a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch index 9c1a5c529..00572b417 100644 --- a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch +++ b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml -index a76bb7aae7636c07989a4acb35281777d3103505..c5eafbdad5270cd9926ed4f1d850d134440d775b 100644 +index a76bb7aae7636c07989a4acb35281777d3103505..d35aa0457307e95ad00464861fcee2827325193e 100644 --- a/browser/base/content/navigator-toolbox.inc.xhtml +++ b/browser/base/content/navigator-toolbox.inc.xhtml @@ -2,7 +2,7 @@ @@ -55,3 +55,11 @@ index a76bb7aae7636c07989a4acb35281777d3103505..c5eafbdad5270cd9926ed4f1d850d134 + diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn index a663368a2..82db58287 100644 --- a/src/browser/base/content/zen-assets.jar.inc.mn +++ b/src/browser/base/content/zen-assets.jar.inc.mn @@ -22,3 +22,4 @@ #include ../../../zen/live-folders/jar.inc.mn #include ../../../zen/space-routing/jar.inc.mn #include ../../../zen/share/jar.inc.mn +#include ../../../zen/library/jar.inc.mn diff --git a/src/browser/base/content/zen-commands.inc.xhtml b/src/browser/base/content/zen-commands.inc.xhtml index d3e11d053..1f4b2aa03 100644 --- a/src/browser/base/content/zen-commands.inc.xhtml +++ b/src/browser/base/content/zen-commands.inc.xhtml @@ -72,4 +72,6 @@ + + diff --git a/src/browser/base/content/zen-locales.inc.xhtml b/src/browser/base/content/zen-locales.inc.xhtml index fca9b29ea..13d22300a 100644 --- a/src/browser/base/content/zen-locales.inc.xhtml +++ b/src/browser/base/content/zen-locales.inc.xhtml @@ -13,4 +13,5 @@ + diff --git a/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch b/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch index 09797f002..0266dbb0d 100644 --- a/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/urlbar/content/UrlbarInputBase.mjs b/browser/components/urlbar/content/UrlbarInputBase.mjs -index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc4292d4e6 100644 +index 8be35b58c5369e123ca69426e852e3849bf75513..9357aaadee0f8d2dab170e2d5e0b57282d909bf5 100644 --- a/browser/components/urlbar/content/UrlbarInputBase.mjs +++ b/browser/components/urlbar/content/UrlbarInputBase.mjs @@ -83,6 +83,12 @@ if (lazy) { @@ -126,8 +126,11 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc startLayoutExtend() { if (!this.#allowBreakout || this.hasAttribute("breakout-extend")) { // Do not expand if the Urlbar does not support being expanded or it is -@@ -3195,6 +3266,13 @@ ${ +@@ -3193,8 +3264,16 @@ ${ + } + this.toggleAttribute("breakout-extend", true); ++ this.#showBreakoutPopover(); this.#updateTextboxPosition(); + this.window.gZenUIManager.onUrlbarOpen(); @@ -140,7 +143,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc // Enable the animation only after the first extend call to ensure it // doesn't run when opening a new window. if (!this.hasAttribute("breakout-extend-animate")) { -@@ -3218,6 +3296,29 @@ ${ +@@ -3218,7 +3297,33 @@ ${ return; } @@ -168,9 +171,13 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc + this.removeAttribute("zen-floating-urlbar"); + this.toggleAttribute("breakout-extend", false); ++ if (this.#isAddressbar) { ++ this.#hideBreakoutPopover(); ++ } this.#updateTextboxPosition(); } -@@ -3256,7 +3357,7 @@ ${ + +@@ -3256,7 +3361,7 @@ ${ forceUnifiedSearchButtonAvailable = false ) { let prevState = this.getAttribute("pageproxystate"); @@ -179,7 +186,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc this.setAttribute("pageproxystate", state); this._inputContainer.setAttribute("pageproxystate", state); this._identityBox?.setAttribute("pageproxystate", state); -@@ -3533,7 +3634,11 @@ ${ +@@ -3533,7 +3638,11 @@ ${ return; } @@ -192,7 +199,38 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc } #updateTextboxPositionNextFrame() { -@@ -3591,9 +3696,10 @@ ${ +@@ -3553,12 +3662,29 @@ ${ + this.removeAttribute("breakout"); + this.parentNode.removeAttribute("breakout"); + this.style.top = ""; ++ this.#hideBreakoutPopover(); ++ this._layoutBreakoutUpdateKey = {}; ++ } ++ ++ #showBreakoutPopover() { ++ if (this.#isAddressbar) { ++ this.setAttribute("popover", "manual"); ++ } ++ if (!this.matches(":popover-open")) { ++ this.showPopover(); ++ } ++ this.#fixAddressbarSearchbarOrder(); ++ } ++ ++ #hideBreakoutPopover() { + try { + this.hidePopover(); + } catch (ex) { + // No big deal if not a popover already. + } +- this._layoutBreakoutUpdateKey = {}; ++ if (this.#isAddressbar) { ++ this.removeAttribute("popover"); ++ } + } + + incrementBreakoutBlockerCount() { +@@ -3591,9 +3717,10 @@ ${ return; } @@ -204,7 +242,19 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc ); if (this.#breakoutBlockerCount) { -@@ -4108,6 +4214,7 @@ ${ +@@ -3602,8 +3729,9 @@ ${ + + this.setAttribute("breakout", "true"); + this.parentNode.setAttribute("breakout", "true"); +- this.showPopover(); +- this.#fixAddressbarSearchbarOrder(); ++ if (!this.#isAddressbar || this.hasAttribute("breakout-extend")) { ++ this.#showBreakoutPopover(); ++ } + this.#updateTextboxPosition(); + + resolve(); +@@ -4108,6 +4236,7 @@ ${ } _toggleActionOverride(event) { @@ -212,7 +262,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc if ( event.keyCode == KeyEvent.DOM_VK_SHIFT || event.keyCode == KeyEvent.DOM_VK_ALT || -@@ -4206,9 +4313,10 @@ ${ +@@ -4206,9 +4335,10 @@ ${ if (!this.#isAddressbar) { return val; } @@ -226,7 +276,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc // Only trim value if the directionality doesn't change to RTL and we're not // showing a strikeout https protocol. return UrlbarContentUtils.isTextDirectionRTL(trimmedValue, window) || -@@ -4410,6 +4518,11 @@ ${ +@@ -4410,6 +4540,11 @@ ${ keepViewOpen = false, browserId = null, }) { @@ -238,7 +288,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc let keyDownEnterDeferred; if ( this._keyDownEnterDeferred && -@@ -4731,6 +4844,7 @@ ${ +@@ -4731,6 +4866,7 @@ ${ this.setResultForCurrentValue(null); this.handleCommand(); this.controller.clearLastQueryContextCache(); @@ -246,7 +296,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc this._suppressStartQuery = false; }); -@@ -4742,7 +4856,6 @@ ${ +@@ -4742,7 +4878,6 @@ ${ // Close the results pane, because paste and go doesn't want a result // selection. This has to happen before the menu opens: ending // breakout-extend once it's open keeps it from showing (bug 2037468). @@ -254,7 +304,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc let controller = this.document.commandDispatcher.getControllerForCommand("cmd_paste"); -@@ -5025,7 +5138,11 @@ ${ +@@ -5025,7 +5160,11 @@ ${ if (!engineName && !source && !this.hasAttribute("searchmode")) { return; } @@ -267,7 +317,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc if (this._searchModeIndicatorTitle) { this._searchModeIndicatorTitle.textContent = ""; this._searchModeIndicatorTitle.removeAttribute("data-l10n-id"); -@@ -5335,6 +5452,7 @@ ${ +@@ -5335,6 +5474,7 @@ ${ this.document.l10n.setAttributes( this.inputField, @@ -275,7 +325,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc l10nId, l10nId == "urlbar-placeholder-with-name" ? { name: engineName } -@@ -5385,6 +5503,12 @@ ${ +@@ -5385,6 +5525,12 @@ ${ } logger().debug("Blur Event"); @@ -288,7 +338,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc // We cannot count every blur events after a missed engagement as abandoment // because the user may have clicked on some view element that executes // a command causing a focus change. For example opening preferences from -@@ -5464,6 +5588,11 @@ ${ +@@ -5464,6 +5610,11 @@ ${ } _on_click(event) { @@ -300,7 +350,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc switch (event.target) { case this.inputField: case this._inputContainer: -@@ -5558,10 +5687,11 @@ ${ +@@ -5558,10 +5709,11 @@ ${ } if (untrim) { this.setValue(this._untrimmedValue); @@ -313,7 +363,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc this.view.autoOpen({ event }); } else { if (this._untrimOnFocusAfterKeydown) { -@@ -5605,9 +5735,16 @@ ${ +@@ -5605,9 +5757,16 @@ ${ } _on_mousedown(event) { @@ -331,7 +381,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc if ( event.composedTarget != this.inputField && event.composedTarget != this._inputContainer -@@ -5617,6 +5754,10 @@ ${ +@@ -5617,6 +5776,10 @@ ${ this.focusedViaMousedown = !this.focused; this.#preventClickSelectsAll = this.focused; @@ -342,7 +392,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc // Keep the focus status, since the attribute may be changed // upon calling this.focus(). -@@ -5654,7 +5795,7 @@ ${ +@@ -5654,7 +5817,7 @@ ${ // view open on tab switch, and the TabSelect event arrived earlier. // Also ignore mousedown on the urlbarView context menu: opening/closing the // view is already handled by the result opening flow. @@ -351,7 +401,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..170cffbdd12a72dab15976cdab9207dc break; } -@@ -5948,7 +6089,7 @@ ${ +@@ -5948,7 +6111,7 @@ ${ // When we are in actions search mode we can show more results so // increase the limit. let maxResults = diff --git a/src/browser/themes/shared/zen-icons/common/library/library-boosts-sprite.svg b/src/browser/themes/shared/zen-icons/common/library/library-boosts-sprite.svg new file mode 100644 index 000000000..1f52512bb --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library-boosts-sprite.svg @@ -0,0 +1,2634 @@ +#filter dumbComments emptyLines substitution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/common/library/library-downloads-sprite.svg b/src/browser/themes/shared/zen-icons/common/library/library-downloads-sprite.svg new file mode 100644 index 000000000..88c2ae897 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library-downloads-sprite.svg @@ -0,0 +1,294 @@ +#filter dumbComments emptyLines substitution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/common/library/library-history-sprite.svg b/src/browser/themes/shared/zen-icons/common/library/library-history-sprite.svg new file mode 100644 index 000000000..42cf5d0e3 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library-history-sprite.svg @@ -0,0 +1,1014 @@ +#filter dumbComments emptyLines substitution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/common/library/library-media-sprite.svg b/src/browser/themes/shared/zen-icons/common/library/library-media-sprite.svg new file mode 100644 index 000000000..6a69f7db1 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library-media-sprite.svg @@ -0,0 +1,1302 @@ +#filter dumbComments emptyLines substitution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/common/library/library-spaces-sprite.svg b/src/browser/themes/shared/zen-icons/common/library/library-spaces-sprite.svg new file mode 100644 index 000000000..6241a29c0 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library-spaces-sprite.svg @@ -0,0 +1,1014 @@ +#filter dumbComments emptyLines substitution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/common/library/library-sprite.svg b/src/browser/themes/shared/zen-icons/common/library/library-sprite.svg new file mode 100644 index 000000000..6fae5bd81 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library-sprite.svg @@ -0,0 +1,1059 @@ +#filter dumbComments emptyLines substitution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/common/library/library.svg b/src/browser/themes/shared/zen-icons/common/library/library.svg new file mode 100644 index 000000000..ca64c23e0 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/common/library/library.svg @@ -0,0 +1,68 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/browser/themes/shared/zen-icons/jar.inc.mn b/src/browser/themes/shared/zen-icons/jar.inc.mn index 145e97214..3821e3ee2 100644 --- a/src/browser/themes/shared/zen-icons/jar.inc.mn +++ b/src/browser/themes/shared/zen-icons/jar.inc.mn @@ -543,4 +543,11 @@ * skin/classic/browser/zen-icons/selectable/warning.svg (../shared/zen-icons/common/selectable/warning.svg) * skin/classic/browser/zen-icons/selectable/water.svg (../shared/zen-icons/common/selectable/water.svg) * skin/classic/browser/zen-icons/selectable/weight.svg (../shared/zen-icons/common/selectable/weight.svg) +* skin/classic/browser/zen-icons/library/library-boosts-sprite.svg (../shared/zen-icons/common/library/library-boosts-sprite.svg) +* skin/classic/browser/zen-icons/library/library-downloads-sprite.svg (../shared/zen-icons/common/library/library-downloads-sprite.svg) +* skin/classic/browser/zen-icons/library/library-history-sprite.svg (../shared/zen-icons/common/library/library-history-sprite.svg) +* skin/classic/browser/zen-icons/library/library-media-sprite.svg (../shared/zen-icons/common/library/library-media-sprite.svg) +* skin/classic/browser/zen-icons/library/library-spaces-sprite.svg (../shared/zen-icons/common/library/library-spaces-sprite.svg) +* skin/classic/browser/zen-icons/library/library-sprite.svg (../shared/zen-icons/common/library/library-sprite.svg) +* skin/classic/browser/zen-icons/library/library.svg (../shared/zen-icons/common/library/library.svg) skin/classic/browser/zen-icons/icons.css (../shared/zen-icons/icons.css) diff --git a/src/browser/themes/shared/zen-icons/update-resources.sh b/src/browser/themes/shared/zen-icons/update-resources.sh index 33a6ef63d..1789f9d42 100755 --- a/src/browser/themes/shared/zen-icons/update-resources.sh +++ b/src/browser/themes/shared/zen-icons/update-resources.sh @@ -60,6 +60,13 @@ do_common_icons() { echo "Working on $filename" echo "* skin/classic/browser/zen-icons/selectable/$filename (../shared/zen-icons/common/selectable/$filename) " >> jar.inc.mn done + for filename in common/library/*.svg; do + # remove the os/ prefix + add_header_to_file $filename + filename=$(basename $filename) + echo "Working on $filename" + echo "* skin/classic/browser/zen-icons/library/$filename (../shared/zen-icons/common/library/$filename) " >> jar.inc.mn + done } do_icons nucleo WIN # TODO: use windows icons diff --git a/src/zen/boosts/ZenBoostsEditor.mjs b/src/zen/boosts/ZenBoostsEditor.mjs index 07ca761bf..2f79e1f24 100644 --- a/src/zen/boosts/ZenBoostsEditor.mjs +++ b/src/zen/boosts/ZenBoostsEditor.mjs @@ -69,7 +69,9 @@ export class nsZenBoostEditor { * @returns {ZenBoostsParent} Boost JSActor parent */ get zenBoostsParent() { - const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser; + const linkedBrowser = + this.editorWindow.browser ?? + this.openerWindow.gBrowser.selectedTab.linkedBrowser; const actor = linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts"); return actor; diff --git a/src/zen/boosts/ZenBoostsManager.sys.mjs b/src/zen/boosts/ZenBoostsManager.sys.mjs index 4878ae8fd..2016a34a8 100644 --- a/src/zen/boosts/ZenBoostsManager.sys.mjs +++ b/src/zen/boosts/ZenBoostsManager.sys.mjs @@ -606,9 +606,12 @@ class nsZenBoostsManager { * @param {Window} parentWindow - The parent browser window * @param {Boost} boost - The boost which will be edited * @param {nsIURI} domainUri - The boost which will be edited + * @param {object} [options] + * @param {Element} [options.browser] - Browser showing the site when it is + * not the selected tab, e.g. a detached glance * @returns {Window|null} The instanced editor window */ - openBoostWindow(parentWindow, boost, domainUri) { + openBoostWindow(parentWindow, boost, domainUri, { browser = null } = {}) { if (!this.canBoostSite(domainUri)) { console.error( "[ZenBoostsManager] Cannot open editor for boost with invalid domain." @@ -648,38 +651,48 @@ class nsZenBoostsManager { null ); - // Close the editor if the tab is switched - const onTabSelect = editor.close.bind(editor); - parentWindow.gBrowser.tabContainer.addEventListener( - "TabSelect", - onTabSelect, - { - once: true, - } - ); - const progressListener = { + QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener"]), onLocationChange: webProgress => { if (webProgress.isTopLevel) { editor.close(); } }, }; + const onTabSelect = editor.close.bind(editor); + if (browser) { + browser.addProgressListener(progressListener); + editor.addEventListener( + "unload", + () => { + browser.removeProgressListener(progressListener); + editor.browser = null; + }, + { once: true } + ); + } else { + // Close the editor if the tab is switched or navigates. + parentWindow.gBrowser.tabContainer.addEventListener( + "TabSelect", + onTabSelect, + { once: true } + ); + parentWindow.gBrowser.addProgressListener(progressListener); + editor.addEventListener( + "unload", + () => { + parentWindow.gBrowser.tabContainer.removeEventListener( + "TabSelect", + onTabSelect + ); + parentWindow.gBrowser.removeProgressListener(progressListener); + editor.browser = null; + }, + { once: true } + ); + } - parentWindow.gBrowser.addProgressListener(progressListener); - - editor.addEventListener( - "unload", - () => { - parentWindow.gBrowser.tabContainer.removeEventListener( - "TabSelect", - onTabSelect - ); - parentWindow.gBrowser.removeProgressListener(progressListener); - }, - { once: true } - ); - + editor.browser = browser; // Give the domain editor.domain = domain; editor.openerWindow = parentWindow; diff --git a/src/zen/common/jar.inc.mn b/src/zen/common/jar.inc.mn index bd87aa4b1..bfddd2790 100644 --- a/src/zen/common/jar.inc.mn +++ b/src/zen/common/jar.inc.mn @@ -23,7 +23,7 @@ content/browser/zen-styles/zen-browser-ui.css (../../zen/common/styles/zen-browser-ui.css) content/browser/zen-styles/zen-animations.css (../../zen/common/styles/zen-animations.css) content/browser/zen-styles/zen-panel-ui.css (../../zen/common/styles/zen-panel-ui.css) - content/browser/zen-styles/zen-single-components.css (../../zen/common/styles/zen-single-components.css) +* content/browser/zen-styles/zen-single-components.css (../../zen/common/styles/zen-single-components.css) content/browser/zen-styles/zen-sidebar.css (../../zen/common/styles/zen-sidebar.css) * content/browser/zen-styles/zen-toolbar.css (../../zen/common/styles/zen-toolbar.css) content/browser/zen-styles/zen-browser-container.css (../../zen/common/styles/zen-browser-container.css) diff --git a/src/zen/common/styles/zen-animations.css b/src/zen/common/styles/zen-animations.css index 7784ef08f..c606869d2 100644 --- a/src/zen/common/styles/zen-animations.css +++ b/src/zen/common/styles/zen-animations.css @@ -4,6 +4,38 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +@keyframes library-badge-download { + 0% { + transform: translateY(0); + } + 20% { + transform: translateY(12px); + } + 100% { + transform: translateY(0); + } +} + +@keyframes zen-library-sprite-hover { + from { + background-position-x: 0%; + } + + to { + background-position-x: 100%; + } +} + +@keyframes zen-library-sprite-unhover { + from { + background-position-x: 100%; + } + + to { + background-position-x: 0%; + } +} + @keyframes zen-back-and-forth-text { 0%, 10% { diff --git a/src/zen/common/styles/zen-browser-ui.css b/src/zen/common/styles/zen-browser-ui.css index 82d34f7a5..8504e5483 100644 --- a/src/zen/common/styles/zen-browser-ui.css +++ b/src/zen/common/styles/zen-browser-ui.css @@ -152,7 +152,7 @@ body, } & > * { - z-index: 1; + z-index: var(--zen-main-app-child-z-index, 1); } /* stylelint-disable-next-line media-query-no-invalid */ diff --git a/src/zen/common/styles/zen-library-widget.css b/src/zen/common/styles/zen-library-widget.css new file mode 100644 index 000000000..0631a1b82 --- /dev/null +++ b/src/zen/common/styles/zen-library-widget.css @@ -0,0 +1,297 @@ + +@property --zen-library-progress { + syntax: ""; + inherits: true; + initial-value: 0; +} + +#zen-sidebar-foot-buttons, +#tabbrowser-tabs { + --zen-library-open-duration: 0.3s; + --zen-library-open-easing: linear(0, 0.34 12%, 0.7 24%, 0.93 38%, 1.03 55%, 1); + transition: --zen-library-progress var(--zen-library-open-duration) var(--zen-library-open-easing); + + &[zen-library-stack-open] { + --zen-library-progress: 1; + } +} + +#zen-library-button { + position: relative; + + --zen-library-sprite-size: 18px; + &::before { + content: ""; + position: absolute; + width: var(--zen-library-sprite-size); + height: var(--zen-library-sprite-size); + background-image: url("chrome://browser/skin/zen-icons/library/library-sprite.svg"); + } + + @media (prefers-reduced-motion: no-preference) { + &::before { + animation: zen-library-sprite-unhover 0.3s steps(36, jump-none); + } + &:hover::before { + animation: zen-library-sprite-hover 0.35s steps(36, jump-none) forwards; + } + } +} + +#zen-library-button > .toolbarbutton-badge-stack { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + background: none !important; + pointer-events: none; + + &[downloading] { + animation: 0.4s library-badge-download ease; + } + + /* The badge flies from its spot on the button to the newest entry's badge + * along an arc, the target being set as the list opens. */ + & #library-button-badge { + position: absolute; + margin: 0 !important; + transform-origin: left top; + + --zen-library-badge-x: 14px; + --zen-library-badge-y: -8px; + --zen-library-badge-to-x: var(--zen-library-badge-x); + --zen-library-badge-to-y: var(--zen-library-badge-y); + --zen-library-badge-mid-x: calc( + (var(--zen-library-badge-x) + var(--zen-library-badge-to-x)) / 2 + + abs(var(--zen-library-badge-to-y) - var(--zen-library-badge-y)) / 2 + ); + --zen-library-badge-mid-y: calc( + (var(--zen-library-badge-y) + var(--zen-library-badge-to-y)) / 2 - + abs(var(--zen-library-badge-to-x) - var(--zen-library-badge-x)) / 2 + ); + --zen-library-left: calc(1 - var(--zen-library-progress)); + left: calc( + var(--zen-library-left) * var(--zen-library-left) * var(--zen-library-badge-x) + + 2 * var(--zen-library-left) * var(--zen-library-progress) * var(--zen-library-badge-mid-x) + + var(--zen-library-progress) * var(--zen-library-progress) * var(--zen-library-badge-to-x) + ); + top: calc( + var(--zen-library-left) * var(--zen-library-left) * var(--zen-library-badge-y) + + 2 * var(--zen-library-left) * var(--zen-library-progress) * var(--zen-library-badge-mid-y) + + var(--zen-library-progress) * var(--zen-library-progress) * var(--zen-library-badge-to-y) + ); + + &:not([downloading]) { + transform: scale(calc((var(--zen-library-progress) * 0.5 + 0.5) * 100%)); + } + + &[downloading] { + transform: scale(calc((var(--zen-library-progress) * 0.2 + 0.8) * 100%)); + } + } +} + +#library-button-badge, +#zen-sidebar-foot-buttons[zen-library-badge] #zen-library-download-list > :last-child .zen-library-download-badge { + visibility: hidden; +} + +#zen-sidebar-foot-buttons[zen-library-badge] { + & #library-button-badge { + visibility: visible; + } + + &[zen-library-stack-open] { + & #library-button-badge { + visibility: hidden; + transition: visibility 0s var(--zen-library-open-duration); + } + + & #zen-library-download-list > :last-child .zen-library-download-badge { + visibility: visible; + transition: visibility 0s var(--zen-library-open-duration); + } + } +} + +.zen-library-download-badge { + pointer-events: none; +} + +.zen-library-download-badge:not([downloading]) { + background-image: var(--download-image); + width: 20px; + height: 20px; + border-radius: 8px; + background-size: cover; + scale: 2; + + & .zen-library-download-progress { + display: none !important; + } +} + +.zen-library-download-badge[downloading] { + will-change: transform; + align-self: start; + transform: translateX(-2px); + + --border-color: color-mix(in srgb, var(--zen-primary-color), light-dark(rgba(0,0,0,.8), white) 70%); + + background-color: color-mix(in srgb, var(--zen-primary-color), light-dark(white, rgba(0,0,0,.5)) 80%); + border: 3px solid var(--border-color); + box-shadow: 0 0 6px 2px rgba(0,0,0,.1); + color: var(--border-color); + width: 25px; + height: 25px; + border-radius: 100%; + + position: relative; +} + +.zen-library-download-badge[downloading]::after { + background-image: url("chrome://browser/skin/zen-icons/forward.svg"); + rotate: 90deg; + + position: absolute; + content: ''; + background-size: contain; + inset: 2px; + fill: currentColor; + -moz-context-properties: fill; +} + +.zen-library-download-progress { + position: absolute; + inset: 1px; + border-radius: 50%; + + --badge-size: 25px; + --border-width: 4px; + --size: calc(var(--badge-size) + 2 * var(--border-width)); + margin: calc(-1 * var(--border-width)); + + --thickness: 3px; + --progress: var(--zen-accent-button-background); + + background: conic-gradient( + from -0deg, + var(--progress) calc(var(--value) * 1%), + transparent 0 + ); + + mask: radial-gradient( + circle closest-side, + transparent calc(100% - var(--thickness)), + black calc(100% - var(--thickness)) + ); +} + +#zen-library-download-list { + width: 100%; + height: auto; + + position: absolute; + bottom: 100%; + + visibility: hidden; + transition: visibility 0s var(--zen-library-open-duration); + + #zen-sidebar-foot-buttons[zen-library-stack-open] & { + visibility: visible; + transition: none; + } + + display: flex; + flex-direction: column; + padding: 0; + padding-top: 10px; + gap: 4px; + + .zen-library-download-list-download { + container-type: inline-size; + -moz-window-dragging: no-drag; + + height: 52px; + border-radius: 16px; + display: flex; + position: relative; + padding: 0 4px; + + --zen-library-progress: 0; + --zen-library-entry-index: 0; + transition: --zen-library-progress 0.22s var(--zen-library-open-easing) + calc(var(--zen-library-entry-index) * 12ms); + + &:nth-last-child(2) { + --zen-library-entry-index: 1; + } + &:nth-last-child(3) { + --zen-library-entry-index: 2; + } + &:nth-last-child(4) { + --zen-library-entry-index: 3; + } + + #zen-sidebar-foot-buttons[zen-library-stack-open] & { + --zen-library-progress: 1; + } + + transform: translateY(calc((1 - var(--zen-library-progress)) * 25px)); + opacity: var(--zen-library-progress); + + &:hover { + background: var(--zen-toolbar-element-bg); + } + + & .zen-library-download-list-title-container { + @container (max-width: 50px) { + display: none !important; + } + + margin: auto 0; + min-width: 0; + flex: 1; + + & .zen-library-download-list-title { + font-size: 11pt; + opacity: 0.9; + + text-overflow: ellipsis; + display: block; + overflow: hidden; + white-space: nowrap; + } + & .zen-library-download-list-subtitle { + font-size: 9pt; + opacity: 0.6; + + text-overflow: ellipsis; + display: block; + overflow: hidden; + white-space: nowrap; + } + } + + & .zen-library-download-badge { + margin: auto 20px auto 8px; + @container (max-width: 50px) { + margin: auto; + } + } + + & .zen-library-download-action { + display: none; + margin: auto 4px auto 0; + + @container (max-width: 50px) { + display: none !important; + } + } + + &[downloading] .zen-library-download-action { + display: flex; + list-style-image: url("chrome://browser/skin/zen-icons/close.svg"); + } + } +} diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index f6f846c73..713c791a3 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -800,3 +800,15 @@ display: none; } } + +#tabbrowser-tabs:is([zen-library-stack-open], [zen-library-stack-closing]) { + --gradient-pos: calc(100% - var(--zen-library-stack-height, 0px)); + mask-image: linear-gradient( + to bottom, + black + calc(var(--gradient-pos) - 60px), + color-mix(transparent calc(var(--zen-library-progress) * 100%), black) + var(--gradient-pos)); +} + +%include zen-library-widget.css diff --git a/src/zen/common/sys/ZenCustomizableUI.sys.mjs b/src/zen/common/sys/ZenCustomizableUI.sys.mjs index 7a48b7f5c..6be57a3a8 100644 --- a/src/zen/common/sys/ZenCustomizableUI.sys.mjs +++ b/src/zen/common/sys/ZenCustomizableUI.sys.mjs @@ -4,17 +4,25 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + ZenLibraryWidget: "moz-src:///zen/library/ZenLibraryWidget.sys.mjs", +}); + export const ZenCustomizableUI = new (class { constructor() {} TYPE_TOOLBAR = "toolbar"; defaultSidebarIcons = [ - "downloads-button", + Services.prefs.getBoolPref("zen.library.enabled") + ? "zen-library-button" + : "downloads-button", "zen-workspaces-button", "zen-create-new-button", ]; startup(CustomizableUIInternal) { + CustomizableUIInternal.createBuiltinWidget(lazy.ZenLibraryWidget); CustomizableUIInternal.registerArea( "zen-sidebar-top-buttons", { diff --git a/src/zen/common/zen-sets.js b/src/zen/common/zen-sets.js index fedaa580f..b1baabe08 100644 --- a/src/zen/common/zen-sets.js +++ b/src/zen/common/zen-sets.js @@ -166,6 +166,14 @@ document.addEventListener( } break; } + case "cmd_zenToggleLibrary": { + const { ZenLibrary } = ChromeUtils.importESModule( + "moz-src:///zen/library/ZenLibrary.mjs", + { global: "current" } + ); + ZenLibrary.toggle(); + break; + } default: gZenGlanceManager.handleMainCommandSet(event); if (event.target.id.startsWith("cmd_zenWorkspaceSwitch")) { diff --git a/src/zen/compact-mode/ZenCompactMode.mjs b/src/zen/compact-mode/ZenCompactMode.mjs index 4ba7ad996..9a1604330 100644 --- a/src/zen/compact-mode/ZenCompactMode.mjs +++ b/src/zen/compact-mode/ZenCompactMode.mjs @@ -61,6 +61,12 @@ ChromeUtils.defineLazyGetter(lazy, "mainAppWrapper", () => document.getElementById("zen-main-app-wrapper") ); +ChromeUtils.defineESModuleGetters( + lazy, + { ZenLibrary: "moz-src:///zen/library/ZenLibrary.mjs" }, + { global: "current" } +); + window.gZenCompactModeManager = { _flashTimeouts: {}, _eventListeners: [], @@ -169,6 +175,9 @@ window.gZenCompactModeManager = { }, set preference(value) { + if (lazy.ZenLibrary.isLibrarySlightlyOpen) { + return; + } if (!this.shouldBeCompact) { value = false; } diff --git a/src/zen/downloads/ZenDownloadAnimation.mjs b/src/zen/downloads/ZenDownloadAnimation.mjs index 8ef63f2bf..ce67ffdb0 100644 --- a/src/zen/downloads/ZenDownloadAnimation.mjs +++ b/src/zen/downloads/ZenDownloadAnimation.mjs @@ -148,9 +148,11 @@ class nsZenDownloadAnimationElement extends HTMLElement { } #determineEndPosition() { - const downloadsButton = document.getElementById("downloads-button"); - const isDownloadButtonVisible = - downloadsButton && this.#isElementVisible(downloadsButton); + const downloadsButton = [ + document.getElementById("zen-library-button"), + document.getElementById("downloads-button"), + ].find(button => button && this.#isElementVisible(button)); + const isDownloadButtonVisible = !!downloadsButton; let endPosition = { clientX: 0, clientY: 0 }; diff --git a/src/zen/downloads/zen-download-box-animation.css b/src/zen/downloads/zen-download-box-animation.css index 90bc5ad63..44ff897d3 100644 --- a/src/zen/downloads/zen-download-box-animation.css +++ b/src/zen/downloads/zen-download-box-animation.css @@ -21,7 +21,7 @@ width: 50%; height: 50%; background-color: var(--zen-primary-color); - mask: url("chrome://browser/content/zen-images/downloads/archive.svg") no-repeat center; + mask: url("chrome://browser/skin/zen-icons/library/library.svg") no-repeat center; mask-size: contain; display: block; } diff --git a/src/zen/drag-and-drop/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js index 7097732b2..e1cd8b8ee 100644 --- a/src/zen/drag-and-drop/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -137,6 +137,7 @@ } startTabDrag(event, tab, ...args) { + const options = args[0] ?? {}; this.ZenDragAndDropService.onDragStart(1); this.#isOutOfWindow = false; gZenCompactModeManager._isTabBeingDragged = true; @@ -145,13 +146,19 @@ if (isTabGroupLabel(tab)) { tab = tab.group; } - const draggingTabs = tab.multiselected ? gBrowser.selectedTabs : [tab]; + const shown = options.dragImageSource ?? tab; + let draggingTabs; + if (options.dragImageSource) { + draggingTabs = [shown]; + } else { + draggingTabs = tab.multiselected ? gBrowser.selectedTabs : [tab]; + } const { offsetX, offsetY } = this.#getDragImageOffset( event, - tab, + shown, draggingTabs ); - const dragImage = this.#createDragImageForTabs(draggingTabs); + const dragImage = this._createDragImageForTabs(draggingTabs); this.originalDragImageArgs = [dragImage, offsetX, offsetY]; this.#dragImageEssential = tab.hasAttribute("zen-essential"); dt.setDragImage(...this.originalDragImageArgs); @@ -159,11 +166,13 @@ tab.style.visibility = "hidden"; } this._dropLandingArmed = - !args[0]?.fromTabList && this.getDropEffectForTabDrag(event) == "move"; + options.armLanding ?? + (!args[0]?.fromTabList && + this.getDropEffectForTabDrag(event) == "move"); this.ZenDragAndDropService.armDropLanding(this._dropLandingArmed); } - #createDragImageForTabs(movingTabs) { + _createDragImageForTabs(movingTabs) { const periphery = gZenWorkspaces.activeWorkspaceElement.querySelector( "#tabbrowser-arrowscrollbox-periphery" ); @@ -675,7 +684,7 @@ return; } this.#handle_sidebarDragOver(event); - this.#handle_tabDragOverToSplit(event); + this._handle_tabDragOverToSplit(event); } #shouldSwitchSpace(event) { @@ -721,16 +730,30 @@ #onSpaceChanged(spaceChanged, dt) { if (AppConstants.platform !== "macosx") { - // See the hack in #createDragImageForTabs for more details which + // See the hack in _createDragImageForTabs for more details which // explains why we need to do this on non-macOS platforms. return; } - let tabs = this.originalDragImageArgs[0].children; + this._recolorDragImage(spaceChanged); + this._refreshDragImage(dt); + } + + /** + * Recolours the current drag image's tab clones for a workspace's theme, + * so the image reads as the space it is over. + * + * @param {object} workspace + */ + _recolorDragImage(workspace) { + const wrapper = this.originalDragImageArgs?.[0]; + if (!wrapper || !workspace) { + return; + } const { isDarkMode, isExplicitMode } = - gZenThemePicker.getGradientForWorkspace(spaceChanged, { + gZenThemePicker.getGradientForWorkspace(workspace, { getGradient: false, }); - for (let tab of tabs) { + for (const tab of wrapper.querySelectorAll("tab")) { if (isExplicitMode) { tab.style.colorScheme = isDarkMode ? "dark" : "light"; tab.style.setProperty( @@ -742,9 +765,21 @@ tab.style.removeProperty("--tab-selected-textcolor"); } } + } + + /** + * Re-hands the drag image to the drag session once its new styles have + * been laid out. + * + * @param {DataTransfer} dt + */ + _refreshDragImage(dt) { requestAnimationFrame(() => { requestAnimationFrame(() => { - dt.updateDragImage(...this.originalDragImageArgs); + // The drag may have ended over the two frames, clearing the image. + if (this.originalDragImageArgs.length) { + dt.updateDragImage(...this.originalDragImageArgs); + } }); }); } @@ -802,7 +837,60 @@ }); } - #handle_tabDragOverToSplit(event) { + /** + * The element a drop on `element` acts on. + * + * @param {Element} element + * @returns {Element} + */ + _targetForDrop(element) { + return element; + } + + /** + * Whether a drop lands before (vs after) an element, from how far down its + * box the pointer sits and the move-over threshold pref. + * + * @param {DragEvent} event + * @param {DOMRect} rect - The element's bounds + * @returns {boolean} + */ + _dropsBefore(event, rect) { + const threshold = + Services.prefs.getIntPref( + "browser.tabs.dragDrop.moveOverThresholdPercent" + ) / 100; + return (event.clientY - rect.top) / rect.height <= threshold; + } + + /** + * Lays the shared drop indicator across an element, horizontally. + * + * @param {object} options + * @param {Element} [options.parent] - Reparent the indicator here first + * @param {number} options.left - Inset from the left, in px + * @param {number} options.width - Indicator width, in px + * @param {number} options.top - Top offset, in px + * @returns {Element} The indicator + */ + _placeDropIndicator({ parent = null, left, width, top }) { + const indicator = gZenPinnedTabManager.dragIndicator; + if (parent && indicator.parentNode !== parent) { + parent.appendChild(indicator); + } + indicator.setAttribute("orientation", "horizontal"); + indicator.style.setProperty("--indicator-left", `${left}px`); + indicator.style.setProperty("--indicator-width", `${width}px`); + indicator.style.top = `${Math.round(top)}px`; + indicator.style.removeProperty("left"); + return indicator; + } + + get _splitDropReady() { + return !!this.#dragOverSplit.canDrop; + } + + _handle_tabDragOverToSplit(event) { if (!this._dndSplitEnabled) { return; } @@ -831,7 +919,7 @@ } if ( - movingTabsSet.has(dropElement) || + movingTabsSet.has(this._targetForDrop(dropElement)) || !isTab(draggedTab) || draggedTab?.group?.hasAttribute("split-view-group") || draggedTab.hasAttribute("zen-live-folder-item-id") || @@ -1049,13 +1137,13 @@ const placesBefore = new Map( this.#draggedElements(event).map(element => [ element, - this.#placeOf(element), + this._placeOf(element), ]) ); super.handle_drop(event); this.#maybeClearVerticalPinnedGridDragOver(); this.#handle_dropSwitchSpace(event); - this.#handle_dropCreateSplit(event); + this._handle_dropCreateSplit(event); this._clearDragOverSplit(); if (!toSplit) { this.#landDragImage(event, placesBefore); @@ -1075,7 +1163,7 @@ return (this._dragImageTabs ?? [draggedTab]).map(elementToMove); } - #placeOf(element) { + _placeOf(element) { return `${element.screenX},${element.screenY}`; } @@ -1094,8 +1182,20 @@ if (gReduceMotion) { return; } + this._landDragImageOnElements(this.#draggedElements(event), placesBefore); + } + + /** + * Marks where the drag image lands, one item per element in order, and + * keeps those elements out of sight until the drag ends. + * + * @param {Element[]} elements - Where the dropped items land + * @param {Map} [placesBefore] - Where each element was + * before the drop; one still there stays in sight under the image + */ + _landDragImageOnElements(elements, placesBefore = null) { const landing = []; - for (const element of this.#draggedElements(event)) { + for (const element of elements) { const { width, height } = element.getBoundingClientRect(); this.ZenDragAndDropService.addDropLandingRect( Math.round(element.screenX), @@ -1103,7 +1203,7 @@ Math.round(width), Math.round(height) ); - if (placesBefore.get(element) !== this.#placeOf(element)) { + if (placesBefore?.get(element) !== this._placeOf(element)) { element.style.visibility = "hidden"; landing.push(element); } @@ -1137,9 +1237,16 @@ gZenWorkspaces.updateTabsContainers(); } - #handle_dropCreateSplit(event) { + /** + * @param {DragEvent} event + * @param {object} [options] + * @param {boolean} [options.activate] - Whether to select the created split + * @returns {boolean} Whether the drop split the dragged tab with the tab + * it was held over + */ + _handle_dropCreateSplit(event, { activate = true } = {}) { if (!this.#dragOverSplit.canDrop) { - return; + return false; } const dragData = this.#dragOverSplit.data; @@ -1147,11 +1254,11 @@ const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); if (!dragData || !draggedTab) { - return; + return false; } this._dontAnimateTabMove = true; - const droppedOnTab = dragData.dropElement; + const droppedOnTab = this._targetForDrop(dragData.dropElement); const dropSide = dragData.dropSide; // Clear any visuals and timer @@ -1161,8 +1268,10 @@ gZenViewSplitter.splitTabs( isLeft ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab], "vsep", - isLeft ? 0 : 1 + isLeft ? 0 : 1, + { activate } ); + return true; } // eslint-disable-next-line complexity @@ -1550,33 +1659,17 @@ this.#dragShiftableItems.at(-1) ); } - const indicator = gZenPinnedTabManager.dragIndicator; - let top = 0; - threshold = - Services.prefs.getIntPref( - "browser.tabs.dragDrop.moveOverThresholdPercent" - ) / 100; - if (overlapPercent > threshold || showIndicatorUnderNewTabButton) { - top = Math.round(rect.top + rect.height) + "px"; - dropBefore = false; - } else { - top = Math.round(rect.top) + "px"; - dropBefore = true; - } - if (indicator.style.top !== top) { + dropBefore = + !showIndicatorUnderNewTabButton && this._dropsBefore(event, rect); + const top = Math.round(dropBefore ? rect.top : rect.top + rect.height); + if (gZenPinnedTabManager.dragIndicator.style.top !== `${top}px`) { shouldPlayHapticFeedback = true; } - indicator.setAttribute("orientation", "horizontal"); - indicator.style.setProperty( - "--indicator-left", - rect.left + separation / 2 + "px" - ); - indicator.style.setProperty( - "--indicator-width", - rect.width - separation + "px" - ); - indicator.style.top = top; - indicator.style.removeProperty("left"); + this._placeDropIndicator({ + left: rect.left + separation / 2, + width: rect.width - separation, + top, + }); this.#removeDragOverBackground(); if (!isTab(dropElement) && dropElement?.parentElement?.isZenFolder) { dropElement = dropElement.parentElement; diff --git a/src/zen/glance/ZenGlanceManager.mjs b/src/zen/glance/ZenGlanceManager.mjs index 4742604f4..b243bd146 100644 --- a/src/zen/glance/ZenGlanceManager.mjs +++ b/src/zen/glance/ZenGlanceManager.mjs @@ -63,6 +63,153 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { .addEventListener("click", this.onOverlayClick.bind(this)); } + #detached = null; + + get detachedBrowser() { + return this.#detached?.browser ?? null; + } + + get #detachedHost() { + let host = document.getElementById("zen-glance-detached"); + if (!host) { + host = window.MozXULElement.parseXULToFragment(` +