diff --git a/locales/en-US/browser/browser/zen-library.ftl b/locales/en-US/browser/browser/zen-library.ftl index 85516aa72..53b517f66 100644 --- a/locales/en-US/browser/browser/zen-library.ftl +++ b/locales/en-US/browser/browser/zen-library.ftl @@ -6,9 +6,12 @@ library-spaces-section-title = Spaces library-downloads-section-title = Downloads library-history-section-title = History -library-search-placeholder = Search… -library-history-search-placeholder = Search History… -library-downloads-search-placeholder = Search Downloads… +library-search-placeholder = + .placeholder = Search… +library-history-search-placeholder = + .placeholder = Search History… +library-downloads-search-placeholder = + .placeholder = Search Downloads… library-history-today = Today library-history-yesterday = Yesterday diff --git a/src/zen/common/sys/ZenCustomizableUI.sys.mjs b/src/zen/common/sys/ZenCustomizableUI.sys.mjs index df639cd71..c5a0e99a6 100644 --- a/src/zen/common/sys/ZenCustomizableUI.sys.mjs +++ b/src/zen/common/sys/ZenCustomizableUI.sys.mjs @@ -8,7 +8,11 @@ export const ZenCustomizableUI = new (class { constructor() {} TYPE_TOOLBAR = "toolbar"; - defaultSidebarIcons = ["zen-library-button", "zen-workspaces-button", "zen-create-new-button"]; + defaultSidebarIcons = [ + "zen-library-button", + "zen-workspaces-button", + "zen-create-new-button", + ]; startup(CustomizableUIInternal) { CustomizableUIInternal.registerArea( diff --git a/src/zen/library/ZenLibrary.mjs b/src/zen/library/ZenLibrary.mjs index d6ea3b683..b71ed7bfb 100644 --- a/src/zen/library/ZenLibrary.mjs +++ b/src/zen/library/ZenLibrary.mjs @@ -8,15 +8,16 @@ import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; let lazy = {}; let gZenLibraryInstance = null; -ChromeUtils.defineESModuleGetters(lazy, { - ZenLibrarySections: "moz-src:///zen/library/ZenLibrarySections.mjs", -}, { global: "current" }); +ChromeUtils.defineESModuleGetters( + lazy, + { + ZenLibrarySections: "moz-src:///zen/library/ZenLibrarySections.mjs", + }, + { global: "current" } +); ChromeUtils.defineLazyGetter(lazy, "l10n", function () { - return new Localization( - ["browser/zen-library.ftl"], - true - ); + return new Localization(["browser/zen-library.ftl"], true); }); ChromeUtils.defineLazyGetter(lazy, "appContentWrapper", function () { @@ -58,14 +59,17 @@ export class ZenLibrary extends MozLitElement { this.#resizeObserver = new ResizeObserver(() => { requestAnimationFrame(() => { let isRightSide = gZenVerticalTabsManager._prefsRightSide; - let translateX = window.windowUtils.getBoundsWithoutFlushing(this)[ - isRightSide ? "left" : "right" - ]; - let contentPosition = window.windowUtils.getBoundsWithoutFlushing(lazy.appContentWrapper)[ - isRightSide ? "right" : "left" - ] - let existingTransform = new DOMMatrix(lazy.appContentWrapper.style.transform).m41; - translateX = translateX-contentPosition + existingTransform; + let translateX = + window.windowUtils.getBoundsWithoutFlushing(this)[ + isRightSide ? "left" : "right" + ]; + let contentPosition = window.windowUtils.getBoundsWithoutFlushing( + lazy.appContentWrapper + )[isRightSide ? "right" : "left"]; + let existingTransform = new DOMMatrix( + lazy.appContentWrapper.style.transform + ).m41; + translateX = translateX - contentPosition + existingTransform; if (isRightSide) { translateX = -translateX; } @@ -96,7 +100,10 @@ export class ZenLibrary extends MozLitElement { render() { return html` - + @@ -105,7 +112,7 @@ export class ZenLibrary extends MozLitElement { this.activeTab = Section.id} + @click=${() => (this.activeTab = Section.id)} > @@ -114,7 +121,11 @@ export class ZenLibrary extends MozLitElement { - + ${this.#sections.find(section => section.constructor.id === this.activeTab)} `; @@ -152,16 +163,19 @@ export class ZenLibrary extends MozLitElement { } static toggle() { - window.docShell.treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIAppWindow).rollupAllPopups(); + window.docShell.treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIAppWindow) + .rollupAllPopups(); let instance = this.getInstance(); instance.toggleAttribute("open"); if (!instance.isOpen) { gNavToolbox.removeAttribute("zen-library-open"); lazy.appContentWrapper.style.transform = ""; if (!instance._deletionIdleCallbackId) { - instance._deletionIdleCallbackId = requestIdleCallback(() => { - this.clearInstance(); - }); + instance._deletionIdleCallbackId = requestIdleCallback(() => { + this.clearInstance(); + }); } } else { if (instance._deletionIdleCallbackId) { diff --git a/src/zen/library/ZenLibrarySections.mjs b/src/zen/library/ZenLibrarySections.mjs index b74039260..333c5f042 100644 --- a/src/zen/library/ZenLibrarySections.mjs +++ b/src/zen/library/ZenLibrarySections.mjs @@ -21,7 +21,7 @@ ChromeUtils.defineESModuleGetters(lazy, { ChromeUtils.defineLazyGetter( lazy, "l10n", - () => new Localization(["browser/zen-library.ftl"], true), + () => new Localization(["browser/zen-library.ftl"], true) ); const SEARCH_DEBOUNCE_MS = 300; @@ -52,8 +52,8 @@ class LibrarySection extends MozLitElement { throw new Error("LibrarySection subclass must define a static `label`."); } - _l10n(key, fallback) { - return lazy.l10n.formatValueSync(key) || fallback; + _l10n(key) { + return lazy.l10n.formatValueSync(key); } } @@ -66,6 +66,7 @@ class SearchSection extends LibrarySection { searchTerm: { type: String }, inputValue: { type: String }, isEmpty: { type: Boolean }, + isScrolledToTop: { type: Boolean }, }; #canDebug = false; @@ -79,6 +80,7 @@ class SearchSection extends LibrarySection { if (this.inputValue === undefined) { this.inputValue = this.searchTerm; } + this.isScrolledToTop = true; this.isEmpty = false; super.connectedCallback(); @@ -86,7 +88,7 @@ class SearchSection extends LibrarySection { this, "#canDebug", "zen.library.debug", - false, + false ); } @@ -131,10 +133,10 @@ class SearchSection extends LibrarySection { const dayStart = new Date(d).setHours(0, 0, 0, 0); const diffDays = Math.round((todayStart - dayStart) / 86_400_000); if (diffDays === 0) { - return this._l10n("library-history-today", "Today"); + return this._l10n("library-history-today"); } if (diffDays === 1) { - return this._l10n("library-history-yesterday", "Yesterday"); + return this._l10n("library-history-yesterday"); } if (diffDays < 7) { return WEEKDAY_FORMATTER.format(d); @@ -143,7 +145,7 @@ class SearchSection extends LibrarySection { } get _searchPlaceholder() { - return this._l10n("library-search-placeholder", "Search…"); + return "library-search-placeholder"; } _formatTime(date) { @@ -164,6 +166,7 @@ class SearchSection extends LibrarySection { rel="stylesheet" href="chrome://browser/content/zen-styles/zen-library.css" /> + -
${this.renderSearchResults()}
+
{ + const el = this.renderRoot.querySelector(".search-results"); + if (!el) { + return; + } + this.isScrolledToTop = el.scrollTop === 0; + }} + ?scrolled-to-top=${this.isScrolledToTop} + > + ${this.renderSearchResults()} +
`; } } @@ -229,7 +244,7 @@ class ProgressiveSearchSection extends SearchSection { _maintainProgressiveRender() { this.#renderedItemCount = Math.max( this.#renderedItemCount, - Math.min(50, this._allItems.length), + Math.min(50, this._allItems.length) ); this.isEmpty = this._allItems.length === 0; this.#scheduleNextBatch(); @@ -243,7 +258,7 @@ class ProgressiveSearchSection extends SearchSection { this.#renderTask = null; this.#renderedItemCount = Math.min( this.#renderedItemCount + 100, - this._allItems.length, + this._allItems.length ); this.requestUpdate(); this.#scheduleNextBatch(); @@ -286,7 +301,7 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { #rebuildTimer = null; get _searchPlaceholder() { - return this._l10n("library-history-search-placeholder", "Search History…"); + return "library-history-search-placeholder"; } async connectedCallback() { @@ -324,6 +339,7 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { this.#teardownResult(); const NHQO = Ci.nsINavHistoryQueryOptions; + // eslint-disable-next-line no-shadow const history = lazy.PlacesUtils.history; const query = history.getNewQuery(); const options = history.getNewQueryOptions(); @@ -383,7 +399,7 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { } this.log( - `#walkAndPopulate — ${this._allItems.length} items, search=${!!this.searchTerm}`, + `#walkAndPopulate — ${this._allItems.length} items, search=${!!this.searchTerm}` ); } finally { this.#walking = false; @@ -458,17 +474,16 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { } if (this.isEmpty) { return html` -
- ${this.searchTerm - ? this._l10n("library-search-no-results", "No results") - : this._l10n("library-history-empty", "No history found")} -
+
`; } const slice = this._getRenderedSlice(); this.log( - `renderSearchResults — ${slice.length}/${this._allItems.length} items`, + `renderSearchResults — ${slice.length}/${this._allItems.length} items` ); const rows = []; @@ -479,9 +494,7 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { if (dayLabel !== lastLabel) { lastLabel = dayLabel; rows.push(html` -
- ${lastLabel} -
+ `); } @@ -499,10 +512,16 @@ class ZenLibraryHistorySection extends ProgressiveSearchSection { />
- ${v.title || v.url} - ${v.url} + +
- ${this._formatTime(v.date)} + @@ -527,10 +546,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { #batchLoading = false; get _searchPlaceholder() { - return this._l10n( - "library-downloads-search-placeholder", - "Search Downloads…", - ); + return "library-downloads-search-placeholder"; } connectedCallback() { @@ -550,7 +566,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { #setupDownloadsView() { this.#downloadsView = { - onDownloadAdded: (dl) => { + onDownloadAdded: dl => { if (this.#batchLoading) { this.#allDownloads.push(dl); return; @@ -566,11 +582,11 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { onDownloadBatchEnded: async () => { this.#batchLoading = false; this.#allDownloads.sort( - (a, b) => this.#downloadTime(b) - this.#downloadTime(a), + (a, b) => this.#downloadTime(b) - this.#downloadTime(a) ); this.#rebuildItems(); this.log( - `batch complete — ${this.#allDownloads.length} downloads loaded`, + `batch complete — ${this.#allDownloads.length} downloads loaded` ); await this.updateComplete; // Guard: library may have closed while the batch was loading. @@ -583,7 +599,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { onDownloadChanged: () => { this.requestUpdate(); }, - onDownloadRemoved: (dl) => { + onDownloadRemoved: dl => { const idx = this.#allDownloads.indexOf(dl); if (idx !== -1) { this.#allDownloads.splice(idx, 1); @@ -610,7 +626,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { _onSearch(term) { this.log( - `_onSearch — term: "${term}", total: ${this.#allDownloads.length}`, + `_onSearch — term: "${term}", total: ${this.#allDownloads.length}` ); this.#rebuildItems(); this._resetProgressiveRender(); @@ -622,11 +638,10 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { if (this.isEmpty) { return html` -
- ${this.searchTerm - ? this._l10n("library-search-no-results", "No results") - : this._l10n("library-downloads-empty", "No downloads found")} -
+
`; } @@ -634,11 +649,11 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { return html` ${Array.from(groups).map( ([key, downloads]) => html` -
- ${this._dayLabel(new Date(key))} -
- ${downloads.map((dl) => this.#renderDownloadItem(dl))} - `, + + ${downloads.map(dl => this.#renderDownloadItem(dl))} + ` )} `; } @@ -660,18 +675,20 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection {
- ${nameStr} - ${this.#getStatusLabel(dl)} + +
- + + @@ -686,6 +703,8 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { /** * Binary insertion into #allDownloads (newest-first). * O(log N) vs O(N log N) for a full sort on every add. + * + * @param {object} dl - Download object from DownloadsCommon.getData() */ #insertSorted(dl) { const time = this.#downloadTime(dl); @@ -705,8 +724,8 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { #rebuildItems() { const term = this.searchTerm?.trim().toLowerCase(); if (term) { - this._allItems = this.#allDownloads.filter((dl) => - this.#matchesSearchTerm(dl, term), + this._allItems = this.#allDownloads.filter(dl => + this.#matchesSearchTerm(dl, term) ); } else { this._allItems = [...this.#allDownloads]; @@ -720,11 +739,7 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { const displayName = lazy.DownloadsViewUI.getDisplayName(dl); const nameStr = typeof displayName === "string" ? displayName.toLowerCase() : ""; - const url = ( - dl.source.originalUrl || - dl.source.url || - "" - ).toLowerCase(); + const url = (dl.source.originalUrl || dl.source.url || "").toLowerCase(); return nameStr.includes(term) || url.includes(term); } @@ -751,10 +766,11 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { switch (state) { case lazy.DownloadsCommon.DOWNLOAD_DOWNLOADING: { const totalBytes = dl.hasProgress ? dl.totalBytes : -1; + // eslint-disable-next-line no-shadow const [status] = lazy.DownloadUtils.getDownloadStatus( dl.currentBytes, totalBytes, - dl.speed, + dl.speed ); return status; } @@ -774,11 +790,11 @@ class ZenLibraryDownloadsSection extends ProgressiveSearchSection { const total = dl.hasProgress ? dl.totalBytes : -1; const transfer = lazy.DownloadUtils.getTransferTotal( dl.currentBytes, - total, + total ); return strings.statusSeparatorBeforeNumber( strings.statePaused, - transfer, + transfer ); } case lazy.DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL: diff --git a/src/zen/library/zen-library.css b/src/zen/library/zen-library.css index eef2f841d..bb32a95fb 100644 --- a/src/zen/library/zen-library.css +++ b/src/zen/library/zen-library.css @@ -12,9 +12,9 @@ } @media (-moz-platform: macos) { - --border-radius-medium: 12px; + --border-radius-medium: 14px; --tab-border-radius: 8px; - font-size: 1.25rem; /* Slightly larger font on macOS */ + font-size: 1rem; /* Slightly larger font on macOS */ } display: flex; @@ -39,13 +39,14 @@ height: 100%; padding: 8px; background: rgba(255, 255, 255, 0.05); - box-shadow: var(--zen-big-shadow); + box-shadow: rgba(0, 0, 0, 0.1) 0 10px 50px; + border-right: 1px solid rgba(0,0,0,.08); -moz-window-dragging: drag; } #zen-library-content { display: flex; - width: 25rem; + width: 27rem; max-width: 80vh; overflow: hidden; transition: width 0.15s ease-in-out; @@ -62,8 +63,8 @@ */ display: flex; flex-direction: column; - width: 25rem; - min-width: 25rem; + width: 27rem; + min-width: 27rem; flex-shrink: 0; overflow: hidden; } @@ -102,10 +103,6 @@ &[active]::before { background: color-mix(in srgb, currentColor 10%, transparent); } - - &:active:hover::before { - transform: scale(0.95); - } } /* Search Bar */ @@ -120,7 +117,7 @@ box-sizing: border-box; @media (-moz-platform: macos) { - font-size: 1.25rem; /* Slightly larger font on macOS */ + font-size: 1rem; /* Slightly larger font on macOS */ } } @@ -162,8 +159,8 @@ overflow: hidden; & .search-icon { - width: 16px; - height: 16px; + width: 18px; + height: 18px; margin-inline-end: 6px; padding: 4px; flex-shrink: 0; @@ -216,15 +213,15 @@ fill: currentColor; -moz-context-properties: fill, fill-opacity; + & img { + width: 14px; + height: 14px; + } + &:hover { background: var(--urlbar-box-hover-bgcolor, light-dark(rgba(0,0,0,0.08), rgba(255,255,255,0.1))); } } - - &:has(.search-input:not(:placeholder-shown)) .search-clear-button { - opacity: 0.5; - pointer-events: auto; - } } .search-filter-button { @@ -262,7 +259,7 @@ flex: 1; flex-direction: column; align-items: stretch; - padding: var(--tab-inline-padding); + padding: 10px; padding-top: 0 !important; overflow-y: auto; scrollbar-width: auto; @@ -271,6 +268,10 @@ @media (-moz-platform: macos) { font-size: 1.25rem; /* Slightly larger font on macOS */ } + + &:not([scrolled-to-top]) { + box-shadow: inset 0 10px 10px -10px var(--zen-toolbar-element-bg); + } } /* unfinished */ @@ -280,8 +281,9 @@ flex-shrink: 0; gap: 8px; padding: 4px; - font-size: 1em; - font-weight: 700; + padding-top: 12px; + font-size: .9em; + font-weight: 500; letter-spacing: 0.05em; opacity: 0.65; @@ -297,10 +299,9 @@ .library-item { --tab-label-mask-size: 1em; - + display: flex; align-items: stretch; - margin: 2px 0 2px 0; appearance: none; background-color: transparent; border-width: 0; @@ -308,10 +309,6 @@ color: inherit; color-scheme: unset; overflow: clip; - - &:active { - scale: var(--zen-active-tab-scale); - } } .library-item-stack { @@ -341,8 +338,8 @@ position: relative; align-items: center; min-width: 0; - height: 36px; - padding: 0 var(--tab-inline-padding); + padding: 12px 10px; + overflow: clip; } .library-item-icon-stack { @@ -354,11 +351,11 @@ .library-item-icon-image { flex-shrink: 0; - width: var(--size-item-small); - height: var(--size-item-small); - margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 3) !important; - margin-inline-end: var(--toolbarbutton-inner-padding) !important; - padding: 0 !important; + width: 18px; + height: 18px; + margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 2); + margin-inline-end: calc(var(--toolbarbutton-inner-padding) * 1.5); + padding: 0; border-radius: 4px; fill: currentColor; -moz-context-properties: fill, stroke; @@ -373,21 +370,21 @@ direction: ltr; mask-image: linear-gradient(to left, transparent, black var(--tab-label-mask-size)); overflow: hidden; + gap: 4px; } .library-item-label { margin-inline: 0; - line-height: var(--tab-label-line-height); white-space: nowrap; + line-height: 1; } /* finished (i hope so) */ .library-item-sublabel { margin: 0; - font-size: x-small; + font-size: 11px; white-space: nowrap; - opacity: 0.65; /* what opacity should i keep? */ - transform: translateY(-4px); + opacity: 0.6; /* what opacity should i keep? */ pointer-events: none; overflow: hidden; } @@ -395,12 +392,17 @@ /* unfinished */ .library-item-time { flex-shrink: 0; - margin-inline-start: calc(var(--toolbarbutton-inner-padding) - 0.5em); /* idk why this calc makes sense to me + margin-inline-start: calc(var(--toolbarbutton-inner-padding) - 0.5em); /* idk why this calc makes sense to me but it is inconsistant so please fix it if you have an idea */ padding-inline-end: calc(var(--toolbarbutton-inner-padding) / 3); font-size: x-small; white-space: nowrap; opacity: 0.65; + display: none; + + .library-item-content:hover & { + display: flex; + } } /* unfinished */