A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: better cover view context menu

+61 -8
+58 -8
src/facets/themes/blur/browser/element.js
··· 103 103 /** @type {import("~/components/orchestrator/favourites/element.js").CLASS | undefined} */ (undefined), 104 104 ); 105 105 106 + $input = signal( 107 + /** @type {import("~/components/configurator/input/element.js").CLASS | undefined} */ (undefined), 108 + ); 109 + 110 + #cachedUris = signal(/** @type {Set<string>} */ (new Set())); 111 + 106 112 // SIGNALS - Pt. 2 107 113 108 114 #viewMode = signal( ··· 228 234 /** @type {import("~/components/orchestrator/favourites/element.js").CLASS} */ 229 235 const favourites = query(this, "favourites-orchestrator-selector"); 230 236 237 + /** @type {import("~/components/configurator/input/element.js").CLASS | null} */ 238 + const input = queryOptional(this, "input-selector"); 239 + 231 240 whenElementsDefined({ output, provider, queue, scope, favourites }).then( 232 241 () => { 233 242 this.$output.value = output; ··· 237 246 this.$favourites.value = favourites; 238 247 }, 239 248 ); 249 + 250 + if (input) { 251 + whenElementsDefined({ input }).then(async () => { 252 + this.$input.value = input; 253 + const uris = await input.listCached(); 254 + this.#cachedUris.value = new Set(uris); 255 + }); 256 + } 240 257 241 258 if (artwork) { 242 259 whenElementsDefined({ artwork }).then(() => { ··· 587 604 if (coverViewMode === "artists") { 588 605 const rawArtistGroups = this.$coverGroups.value?.artistGroups() ?? []; 589 606 const artistGroups = sortDirection === "desc" 590 - ? rawArtistGroups.map((g) => ({ ...g, groups: [...g.groups].reverse() })) 607 + ? rawArtistGroups.map((g) => ({ 608 + ...g, 609 + groups: [...g.groups].reverse(), 610 + })) 591 611 : rawArtistGroups; 592 612 593 613 requestAnimationFrame(() => this.#setupCoverObserver()); ··· 631 651 <div class="cover-art"> 632 652 ${artUrl 633 653 ? html` 634 - <img src="${artUrl}" alt="${artistName}" loading="lazy" @error="${() => { this.#coverArtCache.set(artistKey, null); this.#scheduleArtRender(); }}" /> 654 + <img src="${artUrl}" alt="${artistName}" loading="lazy" @error="${() => { 655 + this.#coverArtCache.set(artistKey, null); 656 + this.#scheduleArtRender(); 657 + }}" /> 635 658 ` 636 659 : html` 637 660 <div class="cover-art-placeholder"><i class="ph-fill ph-vinyl-record"></i></div> ··· 701 724 <div class="cover-art"> 702 725 ${artUrl 703 726 ? html` 704 - <img src="${artUrl}" alt="${albumName}" loading="lazy" @error="${() => { this.#coverArtCache.set(albumKey, null); this.#scheduleArtRender(); }}" /> 727 + <img src="${artUrl}" alt="${albumName}" loading="lazy" @error="${() => { 728 + this.#coverArtCache.set(albumKey, null); 729 + this.#scheduleArtRender(); 730 + }}" /> 705 731 ` 706 732 : html` 707 733 <div class="cover-art-placeholder"><i class="ph-fill ph-vinyl-record"></i></div> ··· 944 970 }), 945 971 ); 946 972 947 - const menuLabel = item.type === "album" ? "Play album" : "Play all"; 948 - 949 973 return html` 950 974 <div class="album-detail"> 951 975 <div class="album-detail-actions"> ··· 972 996 inFront: true, 973 997 trackIds: detailTracks.map((t) => t.id), 974 998 }); 975 - this.$queue.value?.shift(); 976 999 /** @type {HTMLElement | null} */ (this.root().querySelector( 977 1000 `#album-actions-menu`, 978 1001 ))?.hidePopover(); 979 1002 }}"> 980 - ${menuLabel} 1003 + <i class="ph-bold ph-clock-counter-clockwise"></i> 1004 + Play next 981 1005 </button> 982 1006 <button @click="${() => { 983 1007 if (!detailTracks.length) return; ··· 988 1012 `#album-actions-menu`, 989 1013 ))?.hidePopover(); 990 1014 }}"> 1015 + <i class="ph-bold ph-clock-counter-clockwise"></i> 991 1016 Add to queue 992 1017 </button> 1018 + ${(() => { 1019 + const uris = detailTracks.map((t) => t.uri); 1020 + const allCached = uris.length > 0 && uris.every((u) => this.#cachedUris.value.has(u)); 1021 + return html` 1022 + <button @click="${async () => { 1023 + if (!uris.length) return; 1024 + if (allCached) { 1025 + await this.$input.value?.removeFromCache(uris); 1026 + } else { 1027 + await this.$input.value?.cache(uris); 1028 + } 1029 + const updated = await this.$input.value?.listCached(); 1030 + if (updated) this.#cachedUris.value = new Set(updated); 1031 + /** @type {HTMLElement | null} */ (this.root().querySelector( 1032 + `#album-actions-menu`, 1033 + ))?.hidePopover(); 1034 + }}"> 1035 + <i class="ph-fill ph-lightning"></i> 1036 + ${allCached ? `Remove from cache` : `Store in cache`} 1037 + </button> 1038 + `; 1039 + })()} 993 1040 </div> 994 1041 </div> 995 1042 <div class="album-detail-main"> ··· 997 1044 <div class="album-detail-art"> 998 1045 ${artUrl 999 1046 ? html` 1000 - <img src="${artUrl}" alt="${name}" @error="${() => { this.#coverArtCache.set(key, null); this.#scheduleArtRender(); }}" /> 1047 + <img src="${artUrl}" alt="${name}" @error="${() => { 1048 + this.#coverArtCache.set(key, null); 1049 + this.#scheduleArtRender(); 1050 + }}" /> 1001 1051 ` 1002 1052 : html` 1003 1053 <div class="cover-art-placeholder"><i class="ph-fill ph-vinyl-record"></i></div>
+1
src/facets/themes/blur/facet/index.html
··· 36 36 favourites-orchestrator-selector="do-favourites" 37 37 artwork-selector="do-artwork" 38 38 cover-groups-orchestrator-selector="do-cover-groups" 39 + input-selector="#input" 39 40 ></db-browser> 40 41 41 42 <db-artwork-controller
+1
src/facets/themes/blur/facet/index.inline.js
··· 28 28 await foundation.orchestrator.artwork(); 29 29 await foundation.orchestrator.coverGroups(); 30 30 await foundation.orchestrator.favourites(); 31 + await foundation.configurator.input(); 31 32 32 33 await import("~/facets/themes/blur/artwork-controller/element.js"); 33 34 await import("~/facets/themes/blur/browser/element.js");
+1
src/styles/diffuse/facet.css
··· 324 324 min-width: var(--space-3xl); 325 325 padding: var(--space-xs) var(--space-sm); 326 326 text-align: left; 327 + white-space: nowrap; 327 328 width: 100%; 328 329 329 330 & > * {