diff --git a/public/images/icons/windows_98/cd_audio_cd_a-4.png b/public/images/icons/windows_98/cd_audio_cd_a-4.png new file mode 100644 index 0000000000000000000000000000000000000000..fc07a9dea1c1cc5ef206eb76d3b2a971d0ae5674 GIT binary patch literal 641 zcme7R@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-u%tWsIx;Y9?C1WI$jZRL zppfhl_!@hljQC0!j_n}qn3ezfxX1j z*OmPdi!h^-#MF#mHyIchBRyRlLo80Oz2cpBSV4d-;S6U_>dM z!`dAVOG5V5rMD$4*km3W86h^mPL6q-z>Qg9Z??N_buUoh`0cbo+t+&&_alKLiXHnI z5^mp|*HpMg?4I!#o}jZV*K#gdFjWT4Q}kwLNuPhfFLmLQSqHaC9+8yijOhw^V5oRT z(CNfioA2|bEw;P1T)4Zzkt5OL@1>xd$_WQ%m`wX}k5 zy1P=PqMqJ%D{a0O{aXvx`H5>hkeT*mOUQvn0i6Sv!xbzWWFJ4>B@%pLlg5Du-U0&5 z9RB;COe~lrp|HS%O_G`8wW#vXUkNIQc0|1|POuhYky*`go71dsFt`!l%yncTId>> z=o(sv7#La^n^+l{=o*+<85mr0{F#NKAvZrIGp!Q0hQ%_D*Fed^)78&qol`;+04kL4 AC;$Ke literal 0 HcmV?d00001 diff --git a/src/pages/configurator/input/_applet.astro b/src/pages/configurator/input/_applet.astro new file mode 100644 index 0000000..7b3e42a --- /dev/null +++ b/src/pages/configurator/input/_applet.astro @@ -0,0 +1,106 @@ +
+

Input configuration

+

+ Here you can add your audio from various places. +
Add audio from: +

+ +

+ More options coming soon!
S3-compatible APIs, Dropbox, etc.
+

+
+ +
+ + + + diff --git a/src/pages/configurator/input/_manifest.json b/src/pages/configurator/input/_manifest.json new file mode 100644 index 0000000..ec9a97d --- /dev/null +++ b/src/pages/configurator/input/_manifest.json @@ -0,0 +1,31 @@ +{ + "name": "diffuse/configurator/input", + "title": "Diffuse Configurator | Input", + "entrypoint": "index.html", + "actions": { + "list": { + "title": "List", + "description": "List tracks from all inputs.", + "params_schema": { + "type": "object", + "properties": { + "tracks": { + "type": "array", + "description": "A list of (cached) tracks", + "items": { + "type": "object" + } + } + } + } + }, + "resolve": { + "title": "Resolve", + "description": "Potentially translates a track uri with a matching scheme into a URL pointing at the audio bytes. If it can be resolved that is, otherwise you'll get `undefined`.", + "params_schema": { + "type": "string", + "description": "The uri to resolve" + } + } + } +} diff --git a/src/pages/configurator/input/index.astro b/src/pages/configurator/input/index.astro new file mode 100644 index 0000000..f6d0270 --- /dev/null +++ b/src/pages/configurator/input/index.astro @@ -0,0 +1,9 @@ +--- +import Layout from "@layouts/applet-pico-ui.astro"; +import Applet from "./_applet.astro"; +import { title } from "./_manifest.json"; +--- + + + + diff --git a/src/pages/index.astro b/src/pages/index.astro index 3ade5eb..32f4e9c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -24,7 +24,10 @@ const themes = [ // TODO // Applets -const configurators = [{ url: "configurator/output/", title: "Output" }]; +const configurators = [ + { url: "configurator/input/", title: "Input" }, + { url: "configurator/output/", title: "Output" }, +]; const engines = [ { url: "engine/audio/", title: "Audio" }, @@ -121,7 +124,7 @@ const processors = [ Applets that serve as an intermediate in order to make a particular kind of applet configurable. In other words, these allow for an applet to be swapped out with another - that takes the same actions and data output. + that takes the same, or a subset of the actions and data output. diff --git a/src/pages/input/native-fs/_applet.astro b/src/pages/input/native-fs/_applet.astro index 3bfaa8b..f05670f 100644 --- a/src/pages/input/native-fs/_applet.astro +++ b/src/pages/input/native-fs/_applet.astro @@ -1,6 +1,9 @@
-

Native File System Input

-

Add music from your device.

+

Native file system input

+

+ Add music from your device. +
Music added so far: +

@@ -16,7 +19,7 @@ import * as IDB from "idb-keyval"; import { applets } from "@web-applets/sdk"; - import { computed, Signal, signal } from "spellcaster"; + import { computed, effect, Signal, signal } from "spellcaster"; import { repeat, tags, text } from "spellcaster/hyperscript.js"; import { type FileSystemDirectoryHandle, showDirectoryPicker } from "native-file-system-adapter"; import * as URI from "uri-js"; @@ -36,7 +39,7 @@ //////////////////////////////////////////// const IDB_PREFIX = "@applets/input/native-fs"; const IDB_HANDLES = `${IDB_PREFIX}/handles`; - const INPUT_SCHEME = manifest.input_properties.scheme; + const SCHEME = manifest.input_properties.scheme; const context = applets.register(); @@ -69,27 +72,40 @@ }; const Directories = computed(() => { + if (mounts().length === 0) { + return tags.p({ id: "directories" }, [ + tags.small({}, [ + tags.em({}, text("No audio added yet, click the button below to add some.")), + ]), + ]); + } + return tags.ul({ id: "directories" }, repeat(dirList, Item)); }); // Add to DOM - document.getElementById("directories")?.replaceWith(Directories()); + effect(() => { + document.getElementById("directories")?.replaceWith(Directories()); + }); //////////////////////////////////////////// // ACTIONS //////////////////////////////////////////// - const isAvailable = async (fileUri: string) => { + const consult = async (fileUriOrScheme: string) => { const isSupported = !!(globalThis as any).showDirectoryPicker; if (!isSupported) { - console.warn( - "`input/native-fs` is not supported on this platform, missing File System Access API.", - ); - return false; + return { supported: false, reason: "File System Access API is not supported" }; + } + + if (!fileUriOrScheme.includes(":")) { + if (fileUriOrScheme !== SCHEME) return { supported: false, reason: "Scheme does not match" }; + return { supported: true }; } const handles = await fetchHandles(); - const uri = URI.parse(fileUri); - return uri.host && !!handles[uri.host]; + const uri = URI.parse(fileUriOrScheme); + if (uri.scheme !== SCHEME) return { supported: false, reason: "Scheme does not match" }; + return { supported: true, consultation: uri.host && !!handles[uri.host] }; }; const list = async (cachedTracks: Track[] = []) => { @@ -137,8 +153,11 @@ }; const resolve = async (fileUri: string) => { + const isSupported = !!(globalThis as any).showDirectoryPicker; + if (!isSupported) return undefined; + const uri = URI.parse(fileUri); - if (uri.scheme !== INPUT_SCHEME) return undefined; + if (uri.scheme !== SCHEME) return undefined; if (!uri.host || !uri.path) return undefined; const handles = await fetchHandles(); @@ -191,7 +210,7 @@ setMounts(await fetchHandlesList()); }; - context.setActionHandler("isAvailable", isAvailable); + context.setActionHandler("consult", consult); context.setActionHandler("list", list); context.setActionHandler("resolve", resolve); context.setActionHandler("mount", mount); @@ -231,7 +250,7 @@ for await (const item of dir.values()) { if (item.kind === "file" && isAudioFile(item.name)) { const uri = URI.serialize({ - scheme: INPUT_SCHEME, + scheme: SCHEME, host: rootHandleId, path: `${path.length ? "/" + path.join("/") : ""}/${item.name}`, }); diff --git a/src/pages/input/native-fs/_manifest.json b/src/pages/input/native-fs/_manifest.json index bb38c85..3482dae 100644 --- a/src/pages/input/native-fs/_manifest.json +++ b/src/pages/input/native-fs/_manifest.json @@ -32,7 +32,7 @@ }, "resolve": { "title": "Resolve", - "description": "Potentially translates a track uri with a matching scheme into a URL pointing at the audio bytes. If it can be resolved that is, otherwise you'll get `undefined`.", + "description": "Potentially translates a track uri with a matching scheme into a URL pointing at the audio bytes. If it can be resolved that is, otherwise you'll get `undefined`. Use the `consult` action to get a more detailed answer.", "params_schema": { "type": "string", "description": "The uri to resolve" diff --git a/src/pages/orchestrator/input-cache/_applet.astro b/src/pages/orchestrator/input-cache/_applet.astro new file mode 100644 index 0000000..4e8784d --- /dev/null +++ b/src/pages/orchestrator/input-cache/_applet.astro @@ -0,0 +1,25 @@ + diff --git a/src/pages/orchestrator/input-cache/_manifest.json b/src/pages/orchestrator/input-cache/_manifest.json new file mode 100644 index 0000000..5177246 --- /dev/null +++ b/src/pages/orchestrator/input-cache/_manifest.json @@ -0,0 +1,6 @@ +{ + "name": "diffuse/orchestrator/input-cache", + "title": "Diffuse Orchestrator | Input cache", + "entrypoint": "index.html", + "actions": {} +} diff --git a/src/pages/orchestrator/input-cache/index.astro b/src/pages/orchestrator/input-cache/index.astro new file mode 100644 index 0000000..dfb1e1a --- /dev/null +++ b/src/pages/orchestrator/input-cache/index.astro @@ -0,0 +1,9 @@ +--- +import Layout from "@layouts/applet.astro"; +import Applet from "./_applet.astro"; +import { title } from "./_manifest.json"; +--- + + + + diff --git a/src/pages/themes/webamp/index.astro b/src/pages/themes/webamp/index.astro index 530ba09..f46c7f9 100644 --- a/src/pages/themes/webamp/index.astro +++ b/src/pages/themes/webamp/index.astro @@ -9,15 +9,15 @@
diff --git a/src/scripts/theme.ts b/src/scripts/theme.ts index 15c36fd..01b2ee5 100644 --- a/src/scripts/theme.ts +++ b/src/scripts/theme.ts @@ -13,7 +13,7 @@ export async function applet( opts: { addSlashSuffix?: boolean; context?: Window; - container?: HTMLElement; + container?: HTMLElement | Element; id?: string; setHeight?: boolean; } = {}, diff --git a/src/scripts/themes/webamp/index.ts b/src/scripts/themes/webamp/index.ts index bb141bb..83aba45 100644 --- a/src/scripts/themes/webamp/index.ts +++ b/src/scripts/themes/webamp/index.ts @@ -13,38 +13,19 @@ import "@styles/themes/webamp/index.css"; // 🗂️ Applets //////////////////////////////////////////// -// const _configurator = { -// output: await applet("../../configurator/output"), -// }; - -const input = { - nativeFs: await applet("../../input/native-fs"), +const configurator = { + input: await applet("../../configurator/input"), }; -const _orchestrator = { - output: await applet("../../orchestrator/output-management"), -}; +// const orchestrator = { +// output: await applet("../../orchestrator/output-management"), +// }; //////////////////////////////////////////// // ⚡ //////////////////////////////////////////// const amp = new Webamp({ - filePickers: [ - { - contextMenuName: "(applet) Mount directory", - filePicker: async () => { - try { - await input.nativeFs.sendAction("mount"); - } catch (_err) {} - amp.setTracksToPlay(await listMountDirectories()); - return []; - }, - requiresNetwork: false, - }, - ], - - // Tracks - initialTracks: await listMountDirectories(), + initialTracks: await makeList(), }); const ampNode = document.createElement("div"); @@ -56,13 +37,14 @@ amp.renderWhenReady(ampNode); // 🛠️ //////////////////////////////////////////// -async function listMountDirectories() { - await input.nativeFs.sendAction("list"); - const list = input.nativeFs.data as Track[]; +async function makeList() { + await configurator.input.sendAction("list"); + const list = configurator.input.data as Track[]; return list.reduce(async (acc: Promise>, track: Track) => { - await input.nativeFs.sendAction("resolve", track.uri); - const url = input.nativeFs.data as string; - return [...(await acc), { url }]; + await configurator.input.sendAction("resolve", track.uri); + const url = configurator.input.data as string | undefined; + if (url) return [...(await acc), { url }]; + return await acc; }, Promise.resolve([])); }