Experiment to rebuild Diffuse using web applets.
0
fork

Configure Feed

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

feat: fade in/out artwork

+52 -23
+47 -16
src/pages/constituent/blur/artwork-controller/_applet.astro
··· 31 31 </main> 32 32 33 33 <style> 34 + :root { 35 + --transition-durition: 500ms; 36 + } 37 + 34 38 main { 35 39 background: var(--color-3); 36 40 color: white; ··· 42 46 overflow: hidden; 43 47 position: relative; 44 48 transition: 45 - background-color 500ms, 46 - color 500ms; 49 + background-color var(--transition-durition), 50 + color var(--transition-durition); 47 51 } 48 52 49 53 /* Artwork */ ··· 57 61 height: 100%; 58 62 left: 0; 59 63 object-fit: cover; 64 + opacity: 0; 60 65 pointer-events: none; 61 66 position: absolute; 62 67 top: 0; 68 + transition-duration: var(--transition-durition); 69 + transition-property: opacity; 63 70 width: 100%; 64 71 z-index: 0; 65 72 } ··· 78 85 text-transform: uppercase; 79 86 top: var(--space-xs); 80 87 transition: 81 - background-color 500ms, 82 - color 500ms; 88 + background-color var(--transition-durition), 89 + color var(--transition-durition); 83 90 z-index: 1; 84 91 } 85 92 ··· 122 129 inset: 0; 123 130 opacity: 0.5; 124 131 position: absolute; 125 - transition: background-color 500ms; 132 + transition: background-color var(--transition-durition); 126 133 z-index: 1; 127 134 } 128 135 ··· 306 313 import scope from "astro:scope"; 307 314 import { FastAverageColor } from "fast-average-color"; 308 315 import { Temporal } from "@js-temporal/polyfill"; 316 + import { xxh32r } from "xxh32/dist/raw.js"; 309 317 310 318 import { computed, effect, type Signal, signal } from "spellcaster"; 311 319 import { tags, text, type ElementConfigurator } from "spellcaster/hyperscript.js"; ··· 485 493 //////////////////////////////////////////// 486 494 // UI ░ ARTWORK 487 495 //////////////////////////////////////////// 496 + 497 + const timeouts: Record<string, ReturnType<typeof setTimeout>> = {}; 488 498 489 499 effect(() => { 490 500 const art = artwork(); 491 501 492 - console.log("ART", art[0]); 502 + // No artwork, fade out existing. 503 + if (art.length === 0) { 504 + showcase.querySelectorAll("img").forEach((node) => { 505 + node.style.opacity = "0"; 506 + timeouts[hash] = setTimeout(() => node.remove(), 1000); 507 + }); 508 + return; 509 + } 510 + 511 + // Determine if the current artwork needs to be replaced. 512 + const hash = xxh32r(art[0].bytes).toString(); 513 + const existingArtwork = showcase.querySelector<HTMLImageElement>(`img[data-hash="${hash}"]`); 493 514 494 - // TODO: Remove existing art? 495 - if (art.length === 0) { 515 + // If the artwork is the same, stop here. 516 + if (existingArtwork) { 517 + const timeoutId = timeouts[hash]; 518 + if (timeoutId) clearTimeout(timeoutId); 519 + existingArtwork.style.opacity = "1"; 496 520 return; 497 521 } 498 522 499 - // Show artwork 523 + // Add new artwork 500 524 const blob = new Blob([art[0].bytes], { type: art[0].mime }); 501 525 const url = URL.createObjectURL(blob); 502 526 503 - // Remove existing artwork 504 - // TODO: Fade in new artwork and then remove other 505 - showcase.querySelectorAll("img").forEach((node) => { 506 - showcase.removeChild(node); 527 + // Create img for new artwork 528 + const img = h("img", { 529 + src: url, 530 + className: "artwork", 531 + attrs: { 532 + "data-hash": hash, 533 + }, 507 534 }); 508 - 509 - // Create img for new artwork 510 - const img = h("img", { src: url, className: "artwork" }); 511 535 512 536 // Extract average color 513 537 img.onload = () => { ··· 516 540 setArtworkColor(color.rgba); 517 541 bg.style.backgroundColor = color.rgba; 518 542 main.style.backgroundColor = color.rgba; 543 + img.style.opacity = "1"; 544 + 545 + showcase.querySelectorAll("img").forEach((node) => { 546 + if (node === img) return; 547 + node.style.opacity = "0"; 548 + timeouts[hash] = setTimeout(() => node.remove(), 1000); 549 + }); 519 550 }; 520 551 521 552 // Insert new artwork
+5 -5
src/pages/orchestrator/input-cache/_applet.astro
··· 23 23 }; 24 24 25 25 // Start processing once settled and tracks are loaded 26 - context 27 - .settled() 28 - .then(() => configurator.output) 29 - .then((output) => wait(output, (d) => d?.tracks.state === "loaded")) 30 - .then(() => (context.isMainInstance() ? process() : undefined)); 26 + // context 27 + // .settled() 28 + // .then(() => configurator.output) 29 + // .then((output) => wait(output, (d) => d?.tracks.state === "loaded")) 30 + // .then(() => (context.isMainInstance() ? process() : undefined)); 31 31 32 32 //////////////////////////////////////////// 33 33 // ACTIONS
-2
src/scripts/processor/artwork/worker.ts
··· 121 121 let art: Artwork[] = []; 122 122 123 123 // Get metadata + possible artwork from file metadata 124 - console.log("ART REQ", req); 125 124 const meta = await musicMetadataTags({ ...req, includeArtwork: true }); 126 - console.log("ART META", meta); 127 125 if (!req.tags) req.tags = meta.tags; 128 126 129 127 // Add artwork from metadata