+Putting all of this together to make cooperative and [malleable](https://www.inkandswitch.com/essay/malleable-software/) software. More information on the [website](https://elements.diffuse.sh/).
+## Developer usage
-### Integrations
+You can either consume the Diffuse library via the [deployed instance](https://elements.diffuse.sh/elements/) (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 for music storage.
-User layer for user-data storage.
-
-#### 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 }) }} ++ + 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(); + activeFilter.set((() => { + const stored = localStorage.getItem(FILTER_STORAGE_KEY); + return stored === "prelude" || stored === "interface" || stored === "base" + ? stored + : "all"; + })()); + + /** @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 === "base" ? !!c.tags?.includes("base") : (filter === "all" || + (filter === "prelude" + ? c.kind === "prelude" + : c.kind !== "prelude")) && + !c.tags?.includes("base") + ) + .sort((a, b) => { + return a.name.toLocaleLowerCase().localeCompare( + b.name.toLocaleLowerCase(), + ) || a.id.localeCompare(b.id); + }) + : []; + + const selected = output.selected(); + const outputLabel = selected?.label ?? selected?.getAttribute?.("label") ?? + "Local storage"; + + const filterBar = html` +