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

Configure Feed

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

feat: repeat & shuffle buttons for artwork controller

+96 -24
+5
_config.ts
··· 115 115 phosphor("fill/Phosphor-Fill.ttf"); 116 116 phosphor("fill/Phosphor-Fill.woff"); 117 117 phosphor("fill/Phosphor-Fill.woff2"); 118 + phosphor("bold/style.css"); 119 + phosphor("bold/Phosphor-Bold.svg"); 120 + phosphor("bold/Phosphor-Bold.ttf"); 121 + phosphor("bold/Phosphor-Bold.woff"); 122 + phosphor("bold/Phosphor-Bold.woff2"); 118 123 119 124 // MISC 120 125
+1 -1
deno.jsonc
··· 41 41 "@common/": "./src/common/", 42 42 "@components/": "./src/components/", 43 43 "@definitions/": "./src/definitions/", 44 - "@dotenv-run/esbuild": "npm:@dotenv-run/esbuild@^1.5.1", 45 44 "@styles/": "./src/styles/", 46 45 "@themes/": "./src/themes/", 47 46 48 47 // Build 49 48 "@atcute/lex-cli": "npm:@atcute/lex-cli@^2.3.1", 49 + "@dotenv-run/esbuild": "npm:@dotenv-run/esbuild@^1.5.1", 50 50 "@std/fs": "jsr:@std/fs@^1.0.19", 51 51 "@std/path": "jsr:@std/path@^1.1.2", 52 52 "esbuild-plugins-node-modules-polyfill": "npm:esbuild-plugins-node-modules-polyfill@^1.7.1",
+45 -3
src/themes/blur/artwork-controller/element.css
··· 113 113 color: rgba(0, 0, 0, 0.6); 114 114 } 115 115 116 + /* Button row */ 117 + 118 + .button-row { 119 + display: inline-flex; 120 + gap: 1px; 121 + 122 + button { 123 + background: color-mix(in oklch, currentColor 7.5%, transparent); 124 + border: 0; 125 + color: color-mix(in oklch, currentColor 50%, transparent); 126 + cursor: pointer; 127 + font-size: var(--fs-base); 128 + font-size: calc(var(--fs-base) * 0.9); 129 + padding: calc(var(--space-2xs) + 1px) var(--space-sm) var(--space-2xs); 130 + 131 + &[data-enabled="t"] { 132 + background-color: color-mix(in oklch, currentColor 25%, transparent); 133 + color: currentColor; 134 + } 135 + } 136 + 137 + button:first-child { 138 + border-bottom-left-radius: var(--radius-xl); 139 + border-top-left-radius: var(--radius-xl); 140 + } 141 + 142 + button:last-child { 143 + border-bottom-right-radius: var(--radius-xl); 144 + border-top-right-radius: var(--radius-xl); 145 + } 146 + } 147 + 116 148 /* Now playing */ 117 149 118 150 cite { ··· 189 221 190 222 /* Volume */ 191 223 192 - footer { 224 + .volume { 193 225 align-items: center; 194 226 display: flex; 195 227 font-size: var(--fs-xs); ··· 197 229 justify-content: space-between; 198 230 } 199 231 200 - footer .progress-bar { 232 + .volume .progress-bar { 201 233 cursor: pointer; 202 234 flex: 1; 203 235 padding: var(--space-2xs) 0; 204 236 } 205 237 206 - footer i { 238 + .volume i { 207 239 cursor: pointer; 240 + } 241 + 242 + /* Footer */ 243 + 244 + footer { 245 + align-items: center; 246 + display: flex; 247 + gap: var(--space-2xs); 248 + justify-content: center; 249 + margin-top: var(--space-sm); 208 250 } 209 251 210 252 /* Gradient blur */
+43 -20
src/themes/blur/artwork-controller/element.js
··· 22 22 * @import AudioEngine from "@components/engine/audio/element.js" 23 23 * @import QueueEngine from "@components/engine/queue/element.js" 24 24 * @import ArtworkProcessor from "@components/processor/artwork/element.js" 25 + * @import RepeatShuffleOrchestrator from "@components/orchestrator/repeat-shuffle/element.js" 25 26 */ 26 27 27 28 class ArtworkController extends DiffuseElement { 28 29 constructor() { 29 30 super(); 30 31 this.attachShadow({ mode: "open" }); 31 - 32 - // Bind event handlers to self 33 - this.artworkLoaded = this.artworkLoaded.bind(this); 34 - this.fullVolume = this.fullVolume.bind(this); 35 - this.mute = this.mute.bind(this); 36 - this.next = this.next.bind(this); 37 - this.playPause = this.playPause.bind(this); 38 - this.previous = this.previous.bind(this); 39 - this.seek = this.seek.bind(this); 40 - this.setVolume = this.setVolume.bind(this); 41 32 } 42 33 43 34 // VARIABLES ··· 67 58 $audio = signal(/** @type {AudioEngine | undefined} */ (undefined)); 68 59 $input = signal(/** @type {InputElement | undefined} */ (undefined)); 69 60 $queue = signal(/** @type {QueueEngine | undefined} */ (undefined)); 61 + $repeatShuffle = signal(/** @type {RepeatShuffleOrchestrator | undefined} */ (undefined)); 70 62 71 63 // SIGNALS - COMPUTED 72 64 ··· 99 91 /** @type {QueueEngine} */ 100 92 const queue = query(this, "queue-engine-selector"); 101 93 94 + /** @type {RepeatShuffleOrchestrator} */ 95 + const repeatShuffle = query(this, "repeat-shuffle-orchestrator-selector"); 96 + 102 97 this.$artwork.value = artwork; 103 98 this.$audio.value = audio; 104 99 this.$input.value = input; 105 100 this.$queue.value = queue; 101 + this.$repeatShuffle.value = repeatShuffle; 106 102 107 - whenElementsDefined({ audio, artwork, input, queue }).then(() => { 103 + whenElementsDefined({ audio, artwork, input, queue, repeatShuffle }).then(() => { 108 104 // Changed artwork based on active queue item. 109 105 const debouncedChangeArtwork = debounce( 110 106 1000, ··· 288 284 /** 289 285 * @param {Event} event 290 286 */ 291 - artworkLoaded(event) { 287 + artworkLoaded = (event) => { 292 288 if (!(event.target instanceof HTMLImageElement)) return; 293 289 294 290 const hash = event.target.getAttribute("data-hash"); ··· 312 308 }; 313 309 } 314 310 315 - fullVolume() { 311 + fullVolume = () => { 316 312 this.$audio.value?.adjustVolume({ volume: 1 }); 317 313 } 318 314 319 - mute() { 315 + mute = () => { 320 316 this.$audio.value?.adjustVolume({ volume: 0 }); 321 317 } 322 318 323 - next() { 319 + next = () => { 324 320 this.$queue.value?.shift(); 325 321 } 326 322 327 - playPause() { 323 + playPause = () => { 328 324 const audioId = this.$queue.value?.now()?.id; 329 325 330 326 if (this.#isPlaying() && audioId) { ··· 334 330 } 335 331 } 336 332 337 - previous() { 333 + previous = () => { 338 334 this.$queue.value?.unshift(); 339 335 } 340 336 341 337 /** 342 338 * @param {MouseEvent} event 343 339 */ 344 - seek(event) { 340 + seek = (event) => { 345 341 const target = event.target 346 342 ? /** @type {HTMLProgressElement} */ (event.target) 347 343 : null; ··· 354 350 /** 355 351 * @param {MouseEvent} event 356 352 */ 357 - setVolume(event) { 353 + setVolume = (event) => { 358 354 const target = event.target 359 355 ? /** @type {HTMLProgressElement} */ (event.target) 360 356 : null; 361 357 362 358 const percentage = target ? event.offsetX / target.clientWidth : 0; 363 359 this.$audio.value?.adjustVolume({ volume: percentage }); 360 + } 361 + 362 + toggleRepeat = () => { 363 + const rs = this.$repeatShuffle.value 364 + if (!rs) return 365 + rs.setRepeat(!rs.repeat()) 366 + } 367 + 368 + toggleShuffle = () => { 369 + const rs = this.$repeatShuffle.value 370 + if (!rs) return 371 + rs.setShuffle(!rs.shuffle()) 364 372 } 365 373 366 374 // RENDER ··· 396 404 }); 397 405 398 406 return html` 407 + <link rel="stylesheet" href="styles/vendor/phosphor/bold/style.css" /> 399 408 <link rel="stylesheet" href="styles/vendor/phosphor/fill/style.css" /> 400 409 <link rel="stylesheet" href="styles/animations.css" /> 401 410 <link rel="stylesheet" href="themes/blur/artwork-controller/element.css" /> ··· 502 511 503 512 <!-- VOLUME --> 504 513 505 - <footer> 514 + <div class="volume"> 506 515 <i @click="${this.mute}" class="ph-fill ph-speaker-none"></i> 507 516 <div @click="${this.setVolume}" class="progress-bar"> 508 517 <progress max="100" value="${(this.$audio.value?.volume() ?? ··· 510 519 </div> 511 520 <i @click="${this 512 521 .fullVolume}" class="ph-fill ph-speaker-high"></i> 522 + </div> 523 + 524 + <footer> 525 + <div class="button-row"> 526 + <button title="Toggle repeat" @click="${this.toggleRepeat}" data-enabled="${this.$repeatShuffle.value?.repeat() ? 't' : 'f'}"> 527 + <i class="ph-bold ph-repeat"></i> 528 + </button> 529 + <!--<button title="Toggle favourite"> 530 + <i class="ph-bold ph-star"></i> 531 + </button>--> 532 + <button title="Toggle shuffle" @click="${this.toggleShuffle}" data-enabled="${this.$repeatShuffle.value?.shuffle() ? 't' : 'f'}"> 533 + <i class="ph-bold ph-shuffle"></i> 534 + </button> 535 + </div> 513 536 </footer> 514 537 </section> 515 538 </section>
+1
src/themes/blur/artwork-controller/index.js
··· 21 21 dac.setAttribute("audio-engine-selector", aud.selector); 22 22 dac.setAttribute("input-selector", defaults.orchestrator.input.selector); 23 23 dac.setAttribute("queue-engine-selector", queue.selector); 24 + dac.setAttribute("repeat-shuffle-orchestrator-selector", rso.selector); 24 25 25 26 // Add to DOM 26 27 document.body.append(dac);
+1
src/themes/blur/artwork-controller/index.vto
··· 3 3 base: ../../../ 4 4 5 5 styles: 6 + - styles/vendor/phosphor/bold/style.css 6 7 - styles/vendor/phosphor/fill/style.css 7 8 - styles/base.css 8 9