+It is also aimed at consumers, providing themes and facets, preconfigured component compositions; while simultaneously trying to be [malleable software](https://www.inkandswitch.com/essay/malleable-software/).
+More information on the [website](https://elements.diffuse.sh/latest/).
-### Integrations
+## Developer usage
-Music layer for music storage.
-User layer for user-data storage.
+You can either consume the Diffuse library via the [deployed instance](https://elements.diffuse.sh/latest/) (the listed elements link to Javascript files) or the [Javascript package](https://jsr.io/@toko/diffuse). From there you can use the custom elements as with any other custom DOM element, by writing HTML or creating a `Class` instance.
-#### Music layer
-
-- [Amazon S3](https://aws.amazon.com/s3/)
-- [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/)
-- [Azure File Storage](https://azure.microsoft.com/en-us/services/storage/files/)
-- [Dropbox](https://dropbox.com/)
-- [IPFS](https://ipfs.io/)
-- [WebDAV](https://en.wikipedia.org/wiki/WebDAV)
-
-#### User layer
-
-- [Dropbox](https://www.dropbox.com/)
-- [IPFS](https://ipfs.io/) (using MFS)
-- [RemoteStorage](https://remotestorage.io/)
-
-
-
----
-
-
-
-### Hosting on your own server
-
-Diffuse is a static web application, which means it's just HTML, CSS, and Javascript. No REST API, database, or anything backend-related involved. The app uses a hash (aka. fragment-based) routing system, so you don't need any special server rules for routing. You can download a pre-build web-only version of Diffuse on the [releases](https://github.com/icidasset/diffuse/releases) page. Diffuse uses service workers, so you may need HTTPS for it to work smoothly in certain browsers.
-
-I should also note that some source services use OAuth, so you'll need to use your own application credentials (eg. Dropbox). That said, if you're working locally, you can use `http://localhost:8000` or `http://127.0.0.1:44999` to use the default ones, that's what the old Electron app was using.
-
-In short:
-- Diffuse is a static, serverless web application
-- Routing is done using hashes/fragments (eg. `diffuse.sh/#/sources`)
-- Download a web build on the [releases](https://github.com/icidasset/diffuse/releases) page
-- Uses service workers (use HTTPS if possible)
-- May need own OAuth application credentials for some source services
+```html
+
+- ${text} -
- ` - - document.body.appendChild(note) - - // Remove loader - const elm = document.querySelector("#elm") - elm?.parentNode?.removeChild(elm) -} diff --git a/src/Javascript/UI/index.d.ts b/src/Javascript/UI/index.d.ts deleted file mode 100644 index 6e505a797..000000000 --- a/src/Javascript/UI/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ElmPorts } from "./elm/types" - -export { } - -declare global { - const BUILD_TIMESTAMP: string - - const Elm: { UI: ElmMain+ The goal is for every user, no matter their knowledge level, to have agency over their data and their software. One can start with making small changes and gradually progress to making big changes. +
+ ++ You can store your user-data in various places, and easily export, import and sync it. +
+ ++ Level 1: Pick your restaurant, food comes in all shapes and sizes. The equivalent of choosing a Diffuse theme. +
++ Level 2: Take out food from various places, eg. cheese shop + bakery. You choose how you combine the foods and from where you order them. That's facets. +
++ Level 3: Now that you know which food is good and how to make combinations, you might make a slight customization, add a little something of your own (eg. add some spice). However, you're not quite confident enough which spice to pick, so you hire some help. +
++ This can be done using the facets builder which allows you to build on top of a familiar preconfigured foundation and load custom facets. People might share their own, or maybe you use an LLM to generate something for you (eg. an album art gallery). +
++ Level 4: You learned a bit from watching and talking to the help you hired, so you decide to take things in your own hands. +
++ You continue to use the facets builder but learn a bit of HTML, Javascript and CSS; so you're able to write your own facet. +
++ Level 5: At this point you're confident enough to make a meal from scratch. You can start very simple, eg. just throwing a steak in the pan with some butter and some salt to it. Then later add some baked potatoes and go from there. +
++ A similar tool comes into play here, the themes builder. Same concept as the facets builder, but now you need to specify the foundation yourself. You can use the elements listed below to do so. The code for these is available from this website or through the Javascript package. +
++ Level 6: You open your own restaurant. +
++ You can host the theme you made on any web server, it's only HTML after all. Only difference is that you'll have to create the entire HTML tree, not just the body element, as is the case with the theme builder. +
++ Level 7: You got promoted to master chef. Time to open your own restaurant chain. +
++ You can self-host Diffuse, it's open-source! Or you present your own collection of elements. +
++ {{content}} +
+ + {{ await comp.list({ items }) }} ++ Diffuse comes with a foundation that preconfigures a lot of elements so you don't have to set them up yourself, along with a combination of elements for certain features. It internally tracks the DOM addition of the custom elements, so no need to worry about setting up an element multiple times. It also provides signals which can be used to track element creations. An example of this can be found in the "scrobbles" feature. +
++ That said, you are not required not use this! You can, for example, setup a Diffuse audio engine element yourself that's in a different group so that it doesn't communicate with the default one; in case you want to make a dj-deck, or something like that, which would have multiple audio items playing at the same time. This does mean you will need to pay attention to more things, such as, how does this interact with other features the user has enabled. +
++ Refer to the elements index to find out what each element does. +
+
+ {{- echo -}}
+ import foundation from "common/foundation.js"
+ {{ /echo }}
+{{ echo -}}await foundation.configurator.artwork(){{- /echo }}
+{{ echo -}}await foundation.configurator.input(){{- /echo }}
+{{ echo -}}await foundation.configurator.metadata(){{- /echo }}
+{{ echo -}}await foundation.configurator.scrobbles(){{- /echo }}
+
+{{ echo -}}await foundation.engine.audio(){{- /echo }}
+{{ echo -}}await foundation.engine.queue(){{- /echo }}
+{{ echo -}}await foundation.engine.repeatShuffle(){{- /echo }}
+{{ echo -}}await foundation.engine.scope(){{- /echo }}
+
+{{ echo -}}await foundation.orchestrator.autoQueue(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.favourites(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.mediaSession(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.output(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.queueAudio(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.processTracks(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.scopedTracks(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.scrobbleAudio(){{- /echo }}
+{{ echo -}}await foundation.orchestrator.sources(){{- /echo }}
+
+{{ echo -}}await foundation.orchestrator.artwork(){{- /echo -}}
+
+ + Some simple examples to help you understand how to build your own facet. Click the edit button to load them into the code editor above. +
+ + {{ await comp.examples({ id: "examples", items: examples }) }} + ++ While you have the ability to do whatever you want in a custom facet, the existing facets are designed to work a certain way; so here's some things to keep in mind: +
++ + You haven't saved anything yet. Add a facet by browsing the featured ones or any of the other categories. You can click the toggle + to quickly add or remove from your collection. Alternatively, add one using + an URI: + +
+ `; + +//////////////////////////////////////////// +// LIST +//////////////////////////////////////////// + +/** @type {() => void | undefined} */ +let stopMonitor; + +/** */ +export async function renderList() { + if (stopMonitor) stopMonitor(); + + /** @type {HTMLElement | null} */ + const listEl = document.querySelector("#list"); + if (!listEl) throw new Error("List element not found"); + + if (listEl.getAttribute("data-rendered") === "f") { + listEl.innerHTML = ""; + listEl.removeAttribute("data-rendered"); + } + + const out = await output(); + + stopMonitor = effect(() => { + _renderList(out, listEl); + }); +} + +/** + * @param {OutputOrchestrator} output + * @param {HTMLElement} listEl + */ +function _renderList(output, listEl) { + const facetsCol = output.facets.collection(); + + if (facetsCol.state !== "loaded") { + const loading = html` + + `; + + render(loading, listEl); + return; + } + + const filter = activeFilter.get(); + + const col = facetsCol.state === "loaded" + ? [...facetsCol.data] + .filter((c) => + filter === "all" || + (filter === "prelude" ? c.kind === "prelude" : c.kind !== "prelude") + ) + .sort((a, b) => { + return a.name.toLocaleLowerCase().localeCompare( + b.name.toLocaleLowerCase(), + ); + }) + : []; + + const selected = output.selected(); + const outputLabel = selected?.label ?? selected?.getAttribute?.("label") ?? + "Local storage"; + + const filterBar = html` +