Experiment to rebuild Diffuse using web applets.

feat: queue-tracks orchestrator

+155 -110
+1 -1
src/layouts/applet-pico-ui.astro
··· 4 4 import "@styles/fonts.css"; 5 5 import "@styles/icons/iconoir.css"; 6 6 import "@styles/pico.scss"; 7 - import "@styles/applets/common.css"; 7 + import "@styles/applet/common.css"; 8 8 9 9 const { title } = Astro.props; 10 10 ---
+1 -1
src/pages/configurator/input/_applet.astro
··· 31 31 32 32 <script> 33 33 import type { Track } from "@applets/core/types.d.ts"; 34 - import { applet, register } from "@scripts/applets/common"; 34 + import { applet, register } from "@scripts/applet/common"; 35 35 36 36 //////////////////////////////////////////// 37 37 // SETUP
+1 -1
src/pages/configurator/output/_applet.astro
··· 48 48 import { type ElementConfigurator, repeat, text } from "spellcaster/hyperscript.js"; 49 49 50 50 import type { ManagedOutput } from "@applets/core/types"; 51 - import { applet, hs, register } from "@scripts/applets/common"; 51 + import { applet, hs, register } from "@scripts/applet/common"; 52 52 import { INITIAL_MANAGED_OUTPUT } from "@scripts/output/common"; 53 53 54 54 const METHODS = ["browser", "custom", "device"] as const;
+6 -2
src/pages/constituents/blur/artwork-controller/_applet.astro src/pages/constituent/blur/artwork-controller/_applet.astro
··· 314 314 reactive, 315 315 register, 316 316 trackArtworkCacheId, 317 - } from "@scripts/applets/common"; 317 + } from "@scripts/applet/common"; 318 318 import scope from "astro:scope"; 319 319 320 320 //////////////////////////////////////////// ··· 352 352 353 353 const _orchestrator = { 354 354 inputCache: await applet("../../../orchestrator/input-cache"), 355 - queueAudioTracks: await applet("../../../orchestrator/queue-audio-tracks", { 355 + queueAudio: await applet("../../../orchestrator/queue-audio", { 356 356 groupId: groupId(), 357 357 }), 358 + queueTracks: 359 + context.groupId === undefined || context.groupId === "main" 360 + ? applet("../../../orchestrator/queue-tracks") 361 + : undefined, 358 362 }; 359 363 360 364 const processor = {
+1 -1
src/pages/constituents/blur/artwork-controller/_manifest.json src/pages/constituent/blur/artwork-controller/_manifest.json
··· 1 1 { 2 - "name": "diffuse/constituents/blur/artwork-controller", 2 + "name": "diffuse/constituent/blur/artwork-controller", 3 3 "title": "Diffuse Blur Theme | Artwork Controller", 4 4 "entrypoint": "index.html", 5 5 "actions": {
src/pages/constituents/blur/artwork-controller/index.astro src/pages/constituent/blur/artwork-controller/index.astro
+2 -2
src/pages/constituents/pilot/audio/_applet.astro src/pages/constituent/pilot/audio/_applet.astro
··· 3 3 import "@styles/variables.css"; 4 4 import "@styles/fonts.css"; 5 5 import "@styles/icons/iconoir.css"; 6 - import "@styles/themes/pilot/variables.css"; 6 + import "@styles/theme/pilot/variables.css"; 7 7 --- 8 8 9 9 <main> ··· 96 96 import scope from "astro:scope"; 97 97 98 98 import type { State } from "./types.d.ts"; 99 - import { register } from "@scripts/applets/common"; 99 + import { register } from "@scripts/applet/common"; 100 100 101 101 //////////////////////////////////////////// 102 102 // SETUP
+1 -1
src/pages/constituents/pilot/audio/_manifest.json src/pages/constituent/pilot/audio/_manifest.json
··· 1 1 { 2 - "name": "diffuse/constituents/pilot/audio", 2 + "name": "diffuse/constituent/pilot/audio", 3 3 "title": "", 4 4 "entrypoint": "index.html", 5 5 "actions": {
src/pages/constituents/pilot/audio/index.astro src/pages/constituent/pilot/audio/index.astro
src/pages/constituents/pilot/audio/types.d.ts src/pages/constituent/pilot/audio/types.d.ts
+1 -1
src/pages/engine/audio/_applet.astro
··· 2 2 import { effect, signal } from "spellcaster"; 3 3 4 4 import type { State, Audio, AudioState } from "./types"; 5 - import { register } from "@scripts/applets/common"; 5 + import { register } from "@scripts/applet/common"; 6 6 import type { AppletEvent } from "@web-applets/sdk"; 7 7 8 8 ////////////////////////////////////////////
+10 -1
src/pages/engine/queue/_applet.astro
··· 2 2 import type { Track } from "@applets/core/types"; 3 3 import type { State } from "./types.d.ts"; 4 4 5 - import { register } from "@scripts/applets/common"; 5 + import { register } from "@scripts/applet/common"; 6 6 7 7 //////////////////////////////////////////// 8 8 // SETUP ··· 25 25 // ACTIONS 26 26 //////////////////////////////////////////// 27 27 context.setActionHandler("add", add); 28 + context.setActionHandler("fill", fill); 28 29 context.setActionHandler("shift", shift); 29 30 context.setActionHandler("unshift", unshift); 30 31 31 32 function add(items: Track[]) { 32 33 update({ future: [...context.data.future, ...items] }); 34 + } 35 + 36 + // TODO: Shuffle, limit track amount, etc. 37 + function fill(availableItems: Track[]) { 38 + add(availableItems); 39 + 40 + // Automatically insert track if there isn't any 41 + if (!context.data.now) shift(); 33 42 } 34 43 35 44 function shift() {
+6 -6
src/pages/index.astro
··· 3 3 import List from "../components/List.astro"; 4 4 import Page from "../layouts/page.astro"; 5 5 6 - import "../styles/pages/index.css"; 6 + import "../styles/page/index.css"; 7 7 8 8 // Types 9 9 type Ref = { ··· 16 16 17 17 // Themes 18 18 const themes = [ 19 - { url: "themes/blur/", title: "(WIP) Blur" }, 20 - { url: "themes/pilot/", title: "(WIP) Pilot" }, 21 - { url: "themes/webamp/", title: "Webamp" }, 19 + { url: "theme/blur/", title: "(WIP) Blur" }, 20 + { url: "theme/pilot/", title: "(WIP) Pilot" }, 21 + { url: "theme/webamp/", title: "Webamp" }, 22 22 ]; 23 23 24 24 // Abstractions ··· 26 26 27 27 // Constituents 28 28 const constituents = [ 29 - { url: "constituents/blur/artwork-controller", title: "(WIP) Blur ⦚ Artwork Controller" }, 29 + { url: "constituent/blur/artwork-controller", title: "(WIP) Blur ⦚ Artwork Controller" }, 30 30 ]; 31 31 32 32 // Applets ··· 47 47 48 48 const orchestrators = [ 49 49 { url: "orchestrator/input-cache/", title: "Input caching" }, 50 - { url: "orchestrator/queue-audio-tracks/", title: "Queue ⭤ Audio ⭤ Tracks" }, 50 + { url: "orchestrator/queue-audio/", title: "Queue ⭤ Audio" }, 51 51 ]; 52 52 53 53 const output = [
+1 -1
src/pages/input/native-fs/_applet.astro
··· 25 25 26 26 import type { Track } from "@applets/core/types.d.ts"; 27 27 import { isAudioFile } from "@scripts/input/common"; 28 - import { register } from "@scripts/applets/common"; 28 + import { register } from "@scripts/applet/common"; 29 29 30 30 import manifest from "./_manifest.json"; 31 31
+1 -1
src/pages/input/s3/_applet.astro
··· 46 46 47 47 import type { Track } from "@applets/core/types.d.ts"; 48 48 import { isAudioFile } from "@scripts/input/common"; 49 - import { register } from "@scripts/applets/common"; 49 + import { register } from "@scripts/applet/common"; 50 50 import manifest from "./_manifest.json"; 51 51 52 52 type Bucket = {
+1 -1
src/pages/orchestrator/input-cache/_applet.astro
··· 1 1 <script> 2 2 import type { ManagedOutput, ResolvedUri, Track } from "@applets/core/types.d.ts"; 3 - import { applet, register, wait } from "@scripts/applets/common"; 3 + import { applet, register, wait } from "@scripts/applet/common"; 4 4 5 5 //////////////////////////////////////////// 6 6 // SETUP
+1 -1
src/pages/orchestrator/queue-audio-tracks/_applet.astro src/pages/orchestrator/queue-audio/_applet.astro
··· 1 1 <script> 2 2 import type { ManagedOutput } from "@applets/core/types.d.ts"; 3 - import { applet, inputUrl, makeConnect, register } from "@scripts/applets/common"; 3 + import { applet, inputUrl, makeConnect, register } from "@scripts/applet/common"; 4 4 5 5 //////////////////////////////////////////// 6 6 // SETUP
-6
src/pages/orchestrator/queue-audio-tracks/_manifest.json
··· 1 - { 2 - "name": "diffuse/orchestrator/queue-audio-tracks", 3 - "title": "Diffuse Orchestrator | Queue Audio Tracks", 4 - "entrypoint": "index.html", 5 - "actions": {} 6 - }
src/pages/orchestrator/queue-audio-tracks/index.astro src/pages/orchestrator/queue-tracks/index.astro
+6
src/pages/orchestrator/queue-audio/_manifest.json
··· 1 + { 2 + "name": "diffuse/orchestrator/queue-audio", 3 + "title": "Diffuse Orchestrator | Queue Audio", 4 + "entrypoint": "index.html", 5 + "actions": {} 6 + }
+9
src/pages/orchestrator/queue-audio/index.astro
··· 1 + --- 2 + import Layout from "@layouts/applet.astro"; 3 + import Applet from "./_applet.astro"; 4 + import { title } from "./_manifest.json"; 5 + --- 6 + 7 + <Layout title={title}> 8 + <Applet /> 9 + </Layout>
+38
src/pages/orchestrator/queue-tracks/_applet.astro
··· 1 + <script> 2 + import type { ManagedOutput } from "@applets/core/types.d.ts"; 3 + import { applet, makeConnect, register, wait } from "@scripts/applet/common"; 4 + 5 + //////////////////////////////////////////// 6 + // SETUP 7 + //////////////////////////////////////////// 8 + import type * as QueueEngine from "@applets/engine/queue/types.d.ts"; 9 + 10 + // Register applet 11 + const context = register(); 12 + const connect = makeConnect(context); 13 + 14 + // Applet connections 15 + const configurator = { 16 + output: await applet<ManagedOutput>("../../configurator/output"), 17 + }; 18 + 19 + const engine = { 20 + queue: await applet<QueueEngine.State>("../../engine/queue", { groupId: context.groupId }), 21 + }; 22 + 23 + //////////////////////////////////////////// 24 + // Connections 25 + //////////////////////////////////////////// 26 + await context.settled(); 27 + 28 + // Add tracks to the queue once the tracks have been loaded; 29 + // and every time the collection changes. 30 + 31 + wait(configurator.output, (d) => d?.tracks.state === "loaded").then(() => { 32 + connect( 33 + configurator.output, 34 + (data) => data.tracks.cacheId, 35 + () => engine.queue.sendAction("fill", configurator.output.data.tracks.collection), 36 + ); 37 + }); 38 + </script>
+6
src/pages/orchestrator/queue-tracks/_manifest.json
··· 1 + { 2 + "name": "diffuse/orchestrator/queue-tracks", 3 + "title": "Diffuse Orchestrator | Queue Tracks", 4 + "entrypoint": "index.html", 5 + "actions": {} 6 + }
+1 -1
src/pages/output/indexed-db/_applet.astro
··· 2 2 import * as IDB from "idb-keyval"; 3 3 4 4 import type { ManagedOutput, Track } from "@applets/core/types.d.ts"; 5 - import { jsonDecode, jsonEncode, register } from "@scripts/applets/common"; 5 + import { jsonDecode, jsonEncode, register } from "@scripts/applet/common"; 6 6 import { INITIAL_MANAGED_OUTPUT, outputManager } from "@scripts/output/common"; 7 7 8 8 ////////////////////////////////////////////
+1 -1
src/pages/output/native-fs/_applet.astro
··· 3 3 import type * as FSA from "wicg-file-system-access"; 4 4 5 5 import type { ManagedOutput, Track } from "@applets/core/types"; 6 - import { jsonDecode, jsonEncode, register } from "@scripts/applets/common"; 6 + import { jsonDecode, jsonEncode, register } from "@scripts/applet/common"; 7 7 import { INITIAL_MANAGED_OUTPUT, outputManager } from "@scripts/output/common"; 8 8 9 9 ////////////////////////////////////////////
+1 -1
src/pages/output/storacha-automerge/_applet.astro
··· 4 4 import * as Uint8 from "uint8arrays"; 5 5 6 6 import type { ManagedOutput, Track } from "@applets/core/types.d.ts"; 7 - import { cleanUndefinedValuesForTracks, register } from "@scripts/applets/common"; 7 + import { cleanUndefinedValuesForTracks, register } from "@scripts/applet/common"; 8 8 import { INITIAL_MANAGED_OUTPUT } from "@scripts/output/common"; 9 9 10 10 ////////////////////////////////////////////
+1 -1
src/pages/processor/artwork/_applet.astro
··· 2 2 import type { IPicture } from "music-metadata"; 3 3 import * as IDB from "idb-keyval"; 4 4 5 - import { applet, register } from "@scripts/applets/common"; 5 + import { applet, register } from "@scripts/applet/common"; 6 6 import type { ArtworkRequest, State, Artwork } from "./types.d.ts"; 7 7 import type { Extraction } from "../metadata/types.d.ts"; 8 8
+1 -1
src/pages/processor/metadata/_applet.astro
··· 7 7 8 8 import type { TrackStats, TrackTags } from "@applets/core/types"; 9 9 import type { Extraction, Urls } from "./types.d.ts"; 10 - import { register } from "@scripts/applets/common"; 10 + import { register } from "@scripts/applet/common"; 11 11 12 12 //////////////////////////////////////////// 13 13 // SETUP
+1 -1
src/pages/processor/search/_applet.astro
··· 4 4 5 5 import type { Track } from "@applets/core/types"; 6 6 import type { State } from "./types.d.ts"; 7 - import { register } from "@scripts/applets/common"; 7 + import { register } from "@scripts/applet/common"; 8 8 9 9 //////////////////////////////////////////// 10 10 // SETUP
+1 -1
src/pages/themes/blur/index.astro src/pages/theme/blur/index.astro
··· 3 3 --- 4 4 5 5 <Page title="Diffuse"> 6 - <script src="../../../scripts/themes/blur/index.js"></script> 6 + <script src="../../../scripts/theme/blur/index.js"></script> 7 7 8 8 <main></main> 9 9 </Page>
+2 -2
src/pages/themes/pilot/index.astro src/pages/theme/pilot/index.astro
··· 3 3 --- 4 4 5 5 <Page title="Diffuse"> 6 - <script src="../../../scripts/themes/pilot/index.js"></script> 6 + <script src="../../../scripts/theme/pilot/index.js"></script> 7 7 8 8 <!-- Temporary filler to push audio UI down to the bottom --> 9 9 <div class="filler" style="flex: 1;"></div> 10 10 11 11 <!-- Theme applets --> 12 - <iframe id="applet__ui__audio" src="../../constituents/pilot/audio/"></iframe> 12 + <iframe id="applet__ui__audio" src="../../constituent/pilot/audio/"></iframe> 13 13 </Page>
+1 -1
src/pages/themes/webamp/index.astro src/pages/theme/webamp/index.astro
··· 21 21 </a> 22 22 </div> 23 23 </main> 24 - <script src="../../../scripts/themes/webamp/index.js"></script> 24 + <script src="../../../scripts/theme/webamp/index.js"></script> 25 25 </body> 26 26 </html>
src/scripts/applets/common.ts src/scripts/applet/common.ts
+1 -1
src/scripts/output/common.ts
··· 1 1 import type { ManagedOutput, Track } from "@applets/core/types"; 2 - import type { BroadcastedApplet } from "@scripts/applets/common"; 2 + import type { BroadcastedApplet } from "@scripts/applet/common"; 3 3 4 4 export const INITIAL_MANAGED_OUTPUT: ManagedOutput = { 5 5 tracks: {
+43
src/scripts/theme/blur/index.ts
··· 1 + import type { Applet } from "@web-applets/sdk"; 2 + 3 + import type { ManagedOutput } from "@applets/core/types"; 4 + import { applet, reactive, wait } from "@scripts/applet/common"; 5 + 6 + //////////////////////////////////////////// 7 + // 🎨 Styles 8 + //////////////////////////////////////////// 9 + import "@styles/theme/blur/index.css"; 10 + 11 + //////////////////////////////////////////// 12 + // 🗂️ Applets 13 + //////////////////////////////////////////// 14 + import type * as QueueEngine from "@applets/engine/queue/types.d.ts"; 15 + 16 + const container = document.querySelector("main"); 17 + if (!container) throw new Error("Missing container"); 18 + 19 + const labelA = "Deck A"; 20 + const labelB = "Deck B"; 21 + 22 + const configurator = { 23 + output: await applet<ManagedOutput>("../../configurator/output"), 24 + }; 25 + 26 + const _constituent = { 27 + a: applet("../../constituent/blur/artwork-controller", { container, groupId: labelA }), 28 + b: applet("../../constituent/blur/artwork-controller", { container, groupId: labelB }), 29 + }; 30 + 31 + const _orchestrator = { 32 + queueTracks: applet("../../orchestrator/queue-tracks", { groupId: labelA }), 33 + }; 34 + 35 + // const engine = { 36 + // queue: { 37 + // a: await applet<QueueEngine.State>("../../engine/queue", { groupId: labelA }), 38 + // b: await applet<QueueEngine.State>("../../engine/queue", { groupId: labelB }), 39 + // }, 40 + // }; 41 + 42 + // const deckA = engine.queue.a; 43 + // const deckB = engine.queue.b;
-64
src/scripts/themes/blur/index.ts
··· 1 - import type { Applet } from "@web-applets/sdk"; 2 - 3 - import type { ManagedOutput } from "@applets/core/types"; 4 - import { applet, reactive, wait } from "@scripts/applets/common"; 5 - 6 - //////////////////////////////////////////// 7 - // 🎨 Styles 8 - //////////////////////////////////////////// 9 - import "@styles/themes/blur/index.css"; 10 - 11 - //////////////////////////////////////////// 12 - // 🗂️ Applets 13 - //////////////////////////////////////////// 14 - import type * as QueueEngine from "@applets/engine/queue/types.d.ts"; 15 - 16 - const container = document.querySelector("main"); 17 - if (!container) throw new Error("Missing container"); 18 - 19 - const labelA = "Deck A"; 20 - const labelB = "Deck B"; 21 - 22 - const configurator = { 23 - output: await applet<ManagedOutput>("../../configurator/output"), 24 - }; 25 - 26 - const constituents = { 27 - a: applet("../../constituents/blur/artwork-controller", { container, groupId: labelA }), 28 - b: applet("../../constituents/blur/artwork-controller", { container, groupId: labelB }), 29 - }; 30 - 31 - const engine = { 32 - queue: { 33 - a: await applet<QueueEngine.State>("../../engine/queue", { groupId: labelA }), 34 - b: await applet<QueueEngine.State>("../../engine/queue", { groupId: labelB }), 35 - }, 36 - }; 37 - 38 - const deckA = engine.queue.a; 39 - const deckB = engine.queue.b; 40 - 41 - // TODO: Shuffle, limit track amount, etc. 42 - async function fill(deck: Applet<QueueEngine.State>) { 43 - const tracks = configurator.output.data.tracks.collection; 44 - 45 - await deck.sendAction("add", tracks, { 46 - timeoutDuration: 60000, 47 - }); 48 - 49 - // Automatically insert track if there isn't any 50 - if (!deck.data.now) { 51 - await deck.sendAction("shift"); 52 - } 53 - } 54 - 55 - // Add tracks to the queue once the tracks have been loaded; 56 - // and every time the collection changes. 57 - 58 - wait(configurator.output, (d) => d?.tracks.state === "loaded").then(() => { 59 - reactive( 60 - configurator.output, 61 - (data) => data.tracks.cacheId, 62 - () => fill(deckA), 63 - ); 64 - });
+6 -6
src/scripts/themes/pilot/index.ts src/scripts/theme/pilot/index.ts
··· 1 - import { applet, reactive } from "@scripts/applets/common"; 1 + import { applet, reactive } from "@scripts/applet/common"; 2 2 3 3 //////////////////////////////////////////// 4 4 // 🎨 Styles 5 5 //////////////////////////////////////////// 6 - import "@styles/themes/pilot/index.css"; 6 + import "@styles/theme/pilot/index.css"; 7 7 8 8 //////////////////////////////////////////// 9 9 // 🗂️ Applets ··· 11 11 import type * as AudioEngine from "@applets/engine/audio/types.d.ts"; 12 12 import type * as QueueEngine from "@applets/engine/queue/types.d.ts"; 13 13 14 - import type * as AudioUI from "@applets/constituents/pilot/audio/types.d.ts"; 14 + import type * as AudioUI from "@applets/constituent/pilot/audio/types"; 15 15 16 16 const engine = { 17 17 audio: await applet<AudioEngine.State>("../../engine/audio"), 18 18 queue: await applet<QueueEngine.State>("../../engine/queue"), 19 19 }; 20 20 21 - const orchestrator = { 21 + const _orchestrator = { 22 22 input: await applet("../../orchestrator/input-cache"), 23 - queue: await applet("../../orchestrator/queue-audio-tracks"), 23 + queue: await applet("../../orchestrator/queue-audio"), 24 24 }; 25 25 26 26 const ui = { 27 - audio: await applet<AudioUI.State>("../../constituents/pilot/audio/", { setHeight: true }), 27 + audio: await applet<AudioUI.State>("../../constituent/pilot/audio/", { setHeight: true }), 28 28 }; 29 29 30 30 ////////////////////////////////////////////
+2 -2
src/scripts/themes/webamp/index.ts src/scripts/theme/webamp/index.ts
··· 2 2 import Webamp from "webamp"; 3 3 4 4 import type { ManagedOutput, ResolvedUri, Track } from "@applets/core/types.d.ts"; 5 - import { applet, wait } from "@scripts/applets/common"; 5 + import { applet, wait } from "@scripts/applet/common"; 6 6 7 7 //////////////////////////////////////////// 8 8 // 🎨 Styles 9 9 //////////////////////////////////////////// 10 - import "@styles/themes/webamp/index.css"; 10 + import "@styles/theme/webamp/index.css"; 11 11 12 12 //////////////////////////////////////////// 13 13 // 🗂️ Applets
src/styles/applets/common.css src/styles/applet/common.css
src/styles/pages/index.css src/styles/page/index.css
src/styles/themes/blur/index.css src/styles/theme/blur/index.css
src/styles/themes/blur/variables.css src/styles/theme/blur/variables.css
src/styles/themes/pilot/index.css src/styles/theme/pilot/index.css
src/styles/themes/pilot/variables.css src/styles/theme/pilot/variables.css
src/styles/themes/webamp/index.css src/styles/theme/webamp/index.css