From 2564a8973ed7a645ac1477cce24e4aef09c9857e Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Mon, 7 Sep 2026 17:17:18 +0200 Subject: [PATCH] no-bug: Small changes --- src/zen/library/ZenLibrary.mjs | 30 ++++---- src/zen/library/ZenLibrarySections.mjs | 89 +++++++++-------------- src/zen/library/zen-library.css | 99 +++++++++++++++++--------- 3 files changed, 111 insertions(+), 107 deletions(-) diff --git a/src/zen/library/ZenLibrary.mjs b/src/zen/library/ZenLibrary.mjs index 6bd903f07..c694c932d 100644 --- a/src/zen/library/ZenLibrary.mjs +++ b/src/zen/library/ZenLibrary.mjs @@ -86,11 +86,18 @@ export class ZenLibrary extends MozLitElement { this.addEventListener("animationend", this); // Add connected call back and make `appContentWrapper` transform translate the oposite of this element this.#resizeObserver = new ResizeObserver(() => { + if (this._swipeActive) { + this._swipeWrapperTargetPx = this.#computeWrapperTargetPx(); + ZenLibrary.updateSwipeProgress(this._swipeProgress ?? 0); + return; + } if (gZenWorkspaces._swipeManager._swipeState.librarySwiping) { return; } let translateX = this.#computeWrapperTargetPx(); - lazy.appContentWrapper.style.transform = `translateX(${translateX}px)`; + requestAnimationFrame(() => { + lazy.appContentWrapper.style.transform = `translateX(${translateX}px)`; + }); }); this.#resizeObserver.observe(this); for (const Section of Object.values(lazy.ZenLibrarySections)) { @@ -221,7 +228,7 @@ export class ZenLibrary extends MozLitElement { break; } case "animationend": { - if (event.animationName === "zen-library-tab-icon-play") { + if (event.animationName === "zen-library-sprite-play") { event.target.closest(".zen-library-tab")?.removeAttribute("animate"); } break; @@ -313,9 +320,12 @@ export class ZenLibrary extends MozLitElement { */ #computeWrapperTargetPx() { const isRightSide = gZenVerticalTabsManager._prefsRightSide; - let translateX = this.getBoundingClientRect()[ - isRightSide ? "left" : "right" - ]; + const selfTransform = getComputedStyle(this).transform; + const selfOffset = + selfTransform === "none" ? 0 : new DOMMatrix(selfTransform).m41; + let translateX = + this.getBoundingClientRect()[isRightSide ? "left" : "right"] - + selfOffset; const contentPosition = window.windowUtils.getBoundsWithoutFlushing( lazy.appContentWrapper )[isRightSide ? "right" : "left"]; @@ -396,21 +406,11 @@ export class ZenLibrary extends MozLitElement { instance.#cancelActiveAnimations(); const wasOpen = instance.hasAttribute("open"); if (wasOpen) { - // Library is already open; the wrapper's current inline transform IS - // the target — no remeasure needed. instance._swipeWrapperTargetPx = new DOMMatrix(lazy.appContentWrapper.style.transform).m41 || instance.#computeWrapperTargetPx(); } else { - // Measure the open-state wrapper target without flashing: temporarily - // mark [open] so layout reflects the open position, then revert. - instance.setAttribute("open", "true"); - instance.style.visibility = "hidden"; - await new Promise(r => requestAnimationFrame(r)); instance._swipeWrapperTargetPx = instance.#computeWrapperTargetPx(); - instance.style.visibility = ""; - instance.removeAttribute("open"); - lazy.appContentWrapper.style.transform = ""; } instance._swipeActive = true; // Initialize visual state to match the current attribute. diff --git a/src/zen/library/ZenLibrarySections.mjs b/src/zen/library/ZenLibrarySections.mjs index 5cd47c611..5453df1a5 100644 --- a/src/zen/library/ZenLibrarySections.mjs +++ b/src/zen/library/ZenLibrarySections.mjs @@ -475,21 +475,18 @@ class SearchSection extends LibrarySection { } /** - * Open `url` in a Glance overlay anchored to the clicked item. + * Open `url` in a Glance overlay. * - * @param {Event} event * @param {string} url */ - _openInGlance(event, url) { - const itemEl = event.currentTarget; - const rect = window.windowUtils.getBoundsWithoutFlushing(itemEl); + _openInGlance(url) { const tabPanelRect = window.windowUtils.getBoundsWithoutFlushing( window.gBrowser.tabpanels ); window.gZenGlanceManager.openGlance({ url, - clientX: rect.left - tabPanelRect.left, - clientY: rect.top - tabPanelRect.top, + clientX: window.innerWidth / 2 - tabPanelRect.left, + clientY: window.innerHeight / 2 - tabPanelRect.top, width: 0, height: 0, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), @@ -515,6 +512,26 @@ class SearchSection extends LibrarySection { return html``; } + /** + * Shared empty-state markup: the section's sprite icon above the localized + * message. The [data-section] attribute picks the sprite strip, see + * zen-library.css. + * + * @param {string} emptyL10nId - message shown when the section has no data. + */ + _renderEmptyState(emptyL10nId) { + return html` +
+
+
+
+ +
+ `; + } + render() { const hasFilters = !!this._filters().length; return html` @@ -917,12 +934,7 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { return html``; } if (this.isEmpty) { - return html` -
- `; + return this._renderEmptyState("library-history-empty"); } const slice = this._getRenderedSlice(); @@ -960,9 +972,7 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { } /** - * Plain click on a history row opens the page in a Glance overlay; holding - * Ctrl (or any other modifier / middle-click) falls back to the standard - * "where to open" routing. + * Clicking a history row opens the page in a new tab. * * @param {Event} event * @param {object} item @@ -976,21 +986,6 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { } event.preventDefault(); - const hasModifier = - event.ctrlKey || event.metaKey || event.shiftKey || event.altKey; - const isMiddleClick = event.button === 1; - - if ( - !hasModifier && - !isMiddleClick && - Services.prefs.getBoolPref("zen.glance.enabled", true) - ) { - // Glance overlays on top of the library; keep the library open so the - // user can fire another glance without re-opening it. - this._openInGlance(event, item.url); - return; - } - const where = lazy.BrowserUtils.whereToOpenLink(event, false, true); window.openTrustedLinkIn(item.url, where === "current" ? "tab" : where); this._closeLibrary(); @@ -1031,15 +1026,6 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { this._closeLibrary(); }, }, - { - l10nId: "library-item-context-open-glance", - onClick: () => { - // Use the section element as the anchor — context menu opens at - // the cursor and the original item element isn't tracked here. - const fakeEvent = { currentTarget: this }; - this._openInGlance(fakeEvent, item.url); - }, - }, { l10nId: "library-item-context-open-new-window", onClick: () => { @@ -1177,12 +1163,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { const slice = this._getRenderedSlice(); if (this.isEmpty) { - return html` -
- `; + return this._renderEmptyState("library-downloads-empty"); } const groups = this.#groupByDate(slice); @@ -1313,7 +1294,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { const previewUrl = dl.source.referrerInfo?.originalReferrer?.spec || dl.source.url; if (previewUrl) { - this._openInGlance(event, previewUrl); + this._openInGlance(previewUrl); } return; } @@ -1631,12 +1612,7 @@ class ZenLibraryBoostsSection extends SearchSection { renderSearchResults() { const boosts = this.#getBoosts(); if (boosts.length === 0) { - return html` -
- `; + return this._renderEmptyState("library-boosts-empty"); } return html`${boosts.map(b => this.#renderBoost(b))}`; } @@ -1647,7 +1623,7 @@ class ZenLibraryBoostsSection extends SearchSection { class="library-item library-boost-item" data-key=${`${boost.domain}|${boost.id}`} ?active=${boost.isActive} - @click=${e => this.#openBoost(e, boost)} + @click=${() => this.#openBoost(boost)} @contextmenu=${e => this._onItemContextMenu(e, boost)} >
@@ -1693,12 +1669,11 @@ class ZenLibraryBoostsSection extends SearchSection { * Open the boost's domain in a Glance overlay and pop the boost editor * window next to it so the user can tweak the boost while previewing. * - * @param {Event} event * @param {object} boost */ - #openBoost(event, boost) { + #openBoost(boost) { const url = `https://${boost.domain}/`; - this._openInGlance(event, url); + this._openInGlance(url); try { const stored = lazy.gZenBoostsManager.loadBoostFromStore( boost.domain, diff --git a/src/zen/library/zen-library.css b/src/zen/library/zen-library.css index f13d7ec0c..c54f18a88 100644 --- a/src/zen/library/zen-library.css +++ b/src/zen/library/zen-library.css @@ -83,6 +83,8 @@ zen-library[open] { } .zen-library-tab { + --zen-library-sprite-size: 28px; + display: flex; position: relative; justify-content: center; @@ -114,54 +116,56 @@ zen-library[open] { } & .zen-library-tab-icon { - width: 28px; - height: 28px; + width: var(--zen-library-sprite-size); + height: var(--zen-library-sprite-size); overflow: clip; } - & .zen-library-tab-icon-image { - width: calc(36 * 28px); - height: 28px; - fill: rgba(255, 255, 255, 0.8); - stroke: var(--zen-colors-primary); - -moz-context-properties: fill, stroke; - transform: translateX(0); - } - - &[data-section="history"] .zen-library-tab-icon-image { - background-image: url("chrome://browser/skin/zen-icons/library/library-history-sprite.svg"); - } - - &[data-section="downloads"] .zen-library-tab-icon-image { - background-image: url("chrome://browser/skin/zen-icons/library/library-downloads-sprite.svg"); - } - - &[data-section="boosts"] .zen-library-tab-icon-image { - background-image: url("chrome://browser/skin/zen-icons/library/library-boosts-sprite.svg"); - } - - &[data-section="spaces"] .zen-library-tab-icon-image { - background-image: url("chrome://browser/skin/zen-icons/library/library-spaces-sprite.svg"); - } - - &[data-section="media"] .zen-library-tab-icon-image { - background-image: url("chrome://browser/skin/zen-icons/library/library-media-sprite.svg"); - } - @media (prefers-reduced-motion: no-preference) { &[animate] .zen-library-tab-icon-image { - animation: zen-library-tab-icon-play 0.583s steps(36, jump-none); + animation: zen-library-sprite-play 0.583s steps(36, jump-none); } } } -@keyframes zen-library-tab-icon-play { +.zen-library-tab-icon-image, +.empty-state-icon-image { + width: calc(36 * var(--zen-library-sprite-size)); + height: var(--zen-library-sprite-size); + background-size: 100% 100%; + fill: rgba(255, 255, 255, 0.8); + stroke: var(--zen-colors-primary); + -moz-context-properties: fill, stroke; + transform: translateX(0); +} + +[data-section="history"] :is(.zen-library-tab-icon-image, .empty-state-icon-image) { + background-image: url("chrome://browser/skin/zen-icons/library/library-history-sprite.svg"); +} + +[data-section="downloads"] :is(.zen-library-tab-icon-image, .empty-state-icon-image) { + background-image: url("chrome://browser/skin/zen-icons/library/library-downloads-sprite.svg"); +} + +[data-section="boosts"] :is(.zen-library-tab-icon-image, .empty-state-icon-image) { + background-image: url("chrome://browser/skin/zen-icons/library/library-boosts-sprite.svg"); +} + +[data-section="spaces"] :is(.zen-library-tab-icon-image, .empty-state-icon-image) { + background-image: url("chrome://browser/skin/zen-icons/library/library-spaces-sprite.svg"); +} + +[data-section="media"] :is(.zen-library-tab-icon-image, .empty-state-icon-image) { + background-image: url("chrome://browser/skin/zen-icons/library/library-media-sprite.svg"); +} + +@keyframes zen-library-sprite-play { from { transform: translateX(0); } to { - transform: translateX(calc(35 * -28px)); + transform: translateX(calc(35 * -1 * var(--zen-library-sprite-size))); } } @@ -361,7 +365,7 @@ zen-library[open] { } .library-item:hover .library-item-background { - background-color: var(--tab-hover-background-color); + background-color: var(--tab-background-color-hover); outline-color: var(--tab-hover-outline-color); } @@ -573,11 +577,36 @@ zen-library[open] { .empty-state { display: flex; flex: 1; + flex-direction: column; align-items: center; justify-content: center; + gap: 16px; font-size: 2em; font-weight: 600; opacity: 0.5; + + @media (prefers-reduced-motion: no-preference) { + animation: zen-library-empty-state-enter 0.3s cubic-bezier(0.32, 0.72, 0, 1); + + & .empty-state-icon-image { + animation: zen-library-sprite-play 0.583s steps(36, jump-none); + } + } +} + +@keyframes zen-library-empty-state-enter { + from { + opacity: 0; + transform: translateY(12px) scale(0.98); + } +} + +.empty-state-icon { + --zen-library-sprite-size: 56px; + + width: var(--zen-library-sprite-size); + height: var(--zen-library-sprite-size); + overflow: clip; } /* Boosts section */