diff --git a/_backup/components/Applet.astro b/_backup/components/Applet.astro deleted file mode 100644 index 5a6bdac8..00000000 --- a/_backup/components/Applet.astro +++ /dev/null @@ -1,15 +0,0 @@ ---- -import List from "../components/List.astro"; - -const { list, title } = Astro.props; ---- - -
-

{title}

- -

- -

- - -
diff --git a/_backup/components/List.astro b/_backup/components/List.astro deleted file mode 100644 index 478b2041..00000000 --- a/_backup/components/List.astro +++ /dev/null @@ -1,17 +0,0 @@ ---- -const { items } = Astro.props; ---- - - diff --git a/_backup/content.config.ts b/_backup/content.config.ts deleted file mode 100644 index 6cf35bee..00000000 --- a/_backup/content.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineCollection } from "astro:content"; -import { glob } from "astro/loaders"; - -const manifests = defineCollection({ - loader: glob({ pattern: "**/_manifest.json", base: "./src/pages" }), -}); - -export const collections = { manifests }; diff --git a/_backup/env.d.ts b/_backup/env.d.ts deleted file mode 100644 index 5b0a7e0c..00000000 --- a/_backup/env.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// \ No newline at end of file diff --git a/_backup/layouts/applet-pico-ui.astro b/_backup/layouts/applet-pico-ui.astro deleted file mode 100644 index a1ddec0b..00000000 --- a/_backup/layouts/applet-pico-ui.astro +++ /dev/null @@ -1,24 +0,0 @@ ---- -import "@styles/reset.css"; -import "@styles/variables.css"; -import "@styles/fonts.css"; -import "@styles/icons/iconoir.css"; -import "@styles/pico.scss"; -import "@styles/applet/common.css"; - -const { title } = Astro.props; ---- - - - - - - - - - {title} - - - - - diff --git a/_backup/layouts/applet.astro b/_backup/layouts/applet.astro deleted file mode 100644 index e7016c1e..00000000 --- a/_backup/layouts/applet.astro +++ /dev/null @@ -1,17 +0,0 @@ ---- -const { title } = Astro.props; ---- - - - - - - - - - {title} - - - - - diff --git a/_backup/pages/[...manifest].json.ts b/_backup/pages/[...manifest].json.ts deleted file mode 100644 index 934aedf9..00000000 --- a/_backup/pages/[...manifest].json.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { APIRoute } from "astro"; -import { getCollection } from "astro:content"; - -// API Route -export const GET: APIRoute = ({ params, props, request }) => { - return new Response(JSON.stringify(props.manifest)); -}; - -// Generate static paths -export async function getStaticPaths() { - const manifests = await getCollection("manifests"); - - return manifests.map((manifest) => { - return { - params: { manifest: manifest.id.replace("/_manifest", "/manifest") }, - props: { manifest: manifest.data }, - }; - }); -} diff --git a/_backup/pages/engine/audio/_applet.astro b/_backup/pages/engine/audio/_applet.astro deleted file mode 100644 index 481585ca..00000000 --- a/_backup/pages/engine/audio/_applet.astro +++ /dev/null @@ -1,360 +0,0 @@ - diff --git a/_backup/pages/engine/audio/_manifest.json b/_backup/pages/engine/audio/_manifest.json deleted file mode 100644 index 200987dd..00000000 --- a/_backup/pages/engine/audio/_manifest.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "name": "diffuse/engine/audio", - "title": "Diffuse Audio", - "entrypoint": "index.html", - "actions": { - "pause": { - "title": "Pause", - "description": "Pause audio", - "params_schema": { - "type": "object", - "properties": { - "audioId": { - "type": "string" - } - }, - "required": ["audioId"] - } - }, - "play": { - "title": "Play", - "description": "Play audio", - "params_schema": { - "type": "object", - "properties": { - "audioId": { - "type": "string" - }, - "volume": { - "type": "number", - "default": 0.5 - } - }, - "required": ["audioId"] - } - }, - "render": { - "title": "Render", - "description": "Determine the active set of audio elements.", - "params_schema": { - "type": "object", - "properties": { - "audio": { - "type": "array", - "description": "The audio items we want to render. These represent the audio elements that are in the DOM.", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "isPreload": { "type": "boolean" }, - "mimeType": { "type": "string" }, - "progress": { "type": "number" }, - "url": { "type": "string" } - }, - "required": ["id", "url"] - } - }, - "play": { - "type": "object", - "description": "Pass in this object to immediately start playing one of the rendered audio items.", - "properties": { - "audioId": { - "type": "string", - "description": "The id of the rendered audio item we want to play." - }, - "volume": { - "type": "number", - "default": 0.5, - "description": "A number equal to, or between, 0 and 1, that determines how loud the audio should play." - } - }, - "required": ["audioId"] - } - }, - "required": ["audio"] - } - }, - "reload": { - "title": "Reload", - "description": "Make sure the audio with the given id is loading properly. This should be used when for example, the internet connection comes back and the loading of the audio depended on said internet connection.", - "params_schema": { - "type": "object", - "properties": { - "audioId": { - "type": "string" - }, - "play": { - "type": "boolean" - }, - "progress": { - "type": "number" - } - }, - "required": ["audioId", "percentage"] - } - }, - "seek": { - "title": "Seek", - "description": "Seek audio to a given position", - "params_schema": { - "type": "object", - "properties": { - "audioId": { - "type": "string" - }, - "percentage": { - "type": "number", - "description": "A number between 0 and 1 that determines the new current position in the audio" - } - }, - "required": ["audioId", "percentage"] - } - }, - "volume": { - "title": "Volume", - "description": "Set the volume of all audio and the default value, or a specific audio node.", - "params_schema": { - "type": "object", - "properties": { - "audioId": { - "type": "string" - }, - "volume": { - "type": "number", - "description": "A number between 0 and 1 that determines the new volume of all audio elements" - } - }, - "required": ["volume"] - } - } - } -} diff --git a/_backup/pages/engine/audio/index.astro b/_backup/pages/engine/audio/index.astro deleted file mode 100644 index dfb1e1a8..00000000 --- a/_backup/pages/engine/audio/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import Layout from "@layouts/applet.astro"; -import Applet from "./_applet.astro"; -import { title } from "./_manifest.json"; ---- - - - - diff --git a/_backup/pages/engine/queue/_applet.astro b/_backup/pages/engine/queue/_applet.astro deleted file mode 100644 index cac59300..00000000 --- a/_backup/pages/engine/queue/_applet.astro +++ /dev/null @@ -1,59 +0,0 @@ - diff --git a/_backup/pages/engine/queue/_manifest.json b/_backup/pages/engine/queue/_manifest.json deleted file mode 100644 index 8c64b832..00000000 --- a/_backup/pages/engine/queue/_manifest.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "diffuse/engine/queue", - "title": "Diffuse Queue", - "entrypoint": "index.html", - "actions": { - "add": { - "title": "Add", - "description": "Add tracks to the queue.", - "params_schema": { - "type": "array", - "description": "Array of tracks", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "uri": { "type": "string" } - }, - "required": ["id", "uri"] - } - } - }, - "pool": { - "title": "Pool", - "description": "Set the queue pool.", - "params_schema": { - "type": "array", - "description": "Array of tracks", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "uri": { "type": "string" } - }, - "required": ["id", "uri"] - } - } - }, - "shift": { - "title": "Shift", - "description": "Shift the queue, picking the first item from the up next array and putting the currently playing item into the history list." - }, - "unshift": { - "title": "Unshift", - "description": "Unshift the queue, going backwards in time, picking the last item from the history array and putting the currently playing item into the up next list." - } - } -} diff --git a/_backup/pages/engine/queue/index.astro b/_backup/pages/engine/queue/index.astro deleted file mode 100644 index dfb1e1a8..00000000 --- a/_backup/pages/engine/queue/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import Layout from "@layouts/applet.astro"; -import Applet from "./_applet.astro"; -import { title } from "./_manifest.json"; ---- - - - - diff --git a/_backup/pages/engine/queue/types.d.ts b/_backup/pages/engine/queue/types.d.ts deleted file mode 100644 index fddaddd5..00000000 --- a/_backup/pages/engine/queue/types.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "@scripts/engine/queue/types.d.ts"; diff --git a/_backup/pages/index.astro b/_backup/pages/index.astro deleted file mode 100644 index a000c85f..00000000 --- a/_backup/pages/index.astro +++ /dev/null @@ -1,265 +0,0 @@ ---- -import Applet from "../components/Applet.astro"; -import List from "../components/List.astro"; -import Page from "../layouts/page.astro"; - -import "../styles/page/index.css"; - -// Types -type Ref = { - url: string; - title: string; -}; - -// Links -const WEB_APPLETS_HREF = "https://unternet.co/docs/web-applets/introduction"; - -// Themes -const themes = [ - { url: "theme/blur/", title: "(WIP) Blur" }, - { url: "theme/pilot/", title: "(WIP) Pilot" }, - { url: "theme/webamp/", title: "Webamp" }, -]; - -// Abstractions -// TODO - -// Constituents -const constituents = [ - { url: "constituent/blur/artwork-controller/", title: "Blur ⦚ Artwork Controller" }, - { url: "constituent/blur/browser/", title: "(WIP) Blur ⦚ Browser" }, -]; - -// Applets -const configurators = [ - { url: "configurator/input/", title: "Input" }, - { url: "configurator/output/", title: "Output" }, -]; - -const engines = [ - { url: "engine/audio/", title: "Audio" }, - { url: "engine/queue/", title: "Queue" }, -]; - -const input = [ - { url: "input/native-fs/", title: "Native File System" }, - { url: "input/opensubsonic/", title: "Opensubsonic" }, - { url: "input/s3/", title: "S3-Compatible API" }, -]; - -const orchestrators = [ - { url: "orchestrator/queue-audio/", title: "Queue ⭤ Audio" }, - { url: "orchestrator/queue-tracks/", title: "Queue ⭤ Tracks" }, - { url: "orchestrator/process-tracks/", title: "Process inputs into tracks" }, -]; - -const output = [ - { url: "output/indexed-db/", title: "IndexedDB" }, - { url: "output/native-fs/", title: "Native File System" }, - { url: "output/storacha-automerge/", title: "(WIP) Storacha Storage + Automerge CRDT" }, - { url: "output/todo/", title: "(TODO) Keyhive/Beelay" }, - { url: "output/todo/", title: "(TODO) Dialog DB" }, -]; - -const processors = [ - { url: "processor/artwork/", title: "Artwork retrieval" }, - { url: "processor/metadata/", title: "Metadata retrieval" }, - { url: "processor/search/", title: "Search" }, -]; - -// Demos -const demos = [{ url: "demo/s3-tracks/", title: "Add sample S3 music" }]; ---- - - -
-

- - Diffuse - - -

-

- Diffuse is a collection of web applets that make it possible to - listen to audio from various sources on your devices and the web, and to create the ideal digital - listening experience for you. -

-

These applets can be used in various ways. The main ways so far are through:

-
    -
  • Themes, a traditional browser (web application) approach.
  • -
  • Abstractions, for non-browser systems.
  • -
  • - Using various parts of themes and abstractions separately in different - browser tabs. -
  • -
-

- ⚠️ Heavily experimental -

-
-
-
- -
-

Themes

- -

- Themes are “applet compositions” and provide a traditional browser web application way of - using them. Each theme is unique, not just a skin (eg. not like winamp skins). -

- -

- For example, most themes here will limit the currently playing audio tracks to one item, - but you might as well create a DJ theme that can play multiple items at the same time. -

- - -
- - -
-

Abstractions

- -

- These are applet configurations that enable certain use cases outside the traditional web - app experience. Just like themes, these include various assumptions of how certain parts - of the system should interact. -

- -

TODO: Enable intelligent user (ai) agent use-case.

- - -
-
- - -
-

Constituents

- -

- Constituents are UI applets that are used in themes and abstractions. These are organised - per theme or abstraction, but that doesn't mean they are restricted to that theme or - abstraction, you can mix and match as you like. You can even use them on their own. -

- -

- Some themes may be constructed out of various applets that are not listed here. The reason - for that is those applets cannot be used solely on their own, they require an external - context to coordinate them. -

- -

- There's tradeoffs to both approaches. A particular tradeoff to keep in mind for constituents - is that they'll have nested dependencies. So when overriding applets dependencies, the - overrides need to be passed down the tree. -

- - -
- - -
-

Applets

- -

- Applets are web applets, the components of the system. These - are then recombined into an entire music player experience, or whatever you want to build. -

- -
- - 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, or a subset of the actions and data output. - - - - Applets with each a singular purpose and don't have any UI. There are specialised UI and - orchestrators applets that control these. - - - - Inputs are sources of audio tracks. Each track is an entry in the list of possible items - to play. These can be files or streams, static or dynamic. - - - - These too are applet compositions. However, unlike themes, these are purely logical. - Mostly exist in order to construct sensible defaults to use across themes and - abstractions. - - - - Output is application-derived data such as playlists. These applets can receive such data - and keep it around. - - - - These applets work with the tracks generated by the input applets to add more data to - them, or process them in some other way. - - - Additional applets, such as scrobblers. -
-
- - -
-

Demos

- -

Just some utility web pages to help demo the system.

- - -
- - -
-

Cross-Origin

- -

- You can use applets from different origins. However, one important caveat to remember is state partitioning. This causes local state (eg. IndexedDB) to be different when the applet is used on a - different domain versus using the applet directly. There might be ways around state - partitioning, but I haven't found a user-friendly way of doing so. -

- -

- That said, there isn't that much local state that needs to be shared cross origin. Much of it is associated with the input applets and that configuration is saved in your chosen - output applet. So essentially, state partitioning behaves as if the two domains are on different - devices; so using an output applet to sync data between devices works nicely. It does mean you - have to somehow configure the same output applet in both contexts. -

-
- - -
-

Build your own

- -

- Ideally you'd be able to mix and match these applets to build your own music player. There - are still a few todos and unknowns here: -

- -
    -
  • - This is currently using a fork of the web applets SDK that adds a few features (waiting on - PRs). Meaning that it won't be as easy to write your own applets in the same way as done - here. -
  • -
  • - I wrote some custom code on top of the web applets SDK that makes the applets talk to each - other over a broadcast channel. This should ideally become a JS library or added to the - web applets SDK. Though you can just copy-paste from the Diffuse repo if you want (it's - not that much code). -
  • -
  • ... Probably a bunch of stuff I'm forgetting, teaching materials, etc ...
  • -
-
-
-
diff --git a/_backup/pages/test/cross-origin/blur.astro b/_backup/pages/test/cross-origin/blur.astro deleted file mode 100644 index eda7ec95..00000000 --- a/_backup/pages/test/cross-origin/blur.astro +++ /dev/null @@ -1,31 +0,0 @@ ---- -import Page from "../../../layouts/page.astro"; -import "@styles/theme/blur/index.css"; ---- - - - - -
-
diff --git a/_backup/pages/test/cross-origin/output.astro b/_backup/pages/test/cross-origin/output.astro deleted file mode 100644 index b1b62f9d..00000000 --- a/_backup/pages/test/cross-origin/output.astro +++ /dev/null @@ -1,15 +0,0 @@ ---- -import Page from "../../../layouts/page.astro"; ---- - - - - - - diff --git a/_backup/pages/theme/pilot/audio/_applet.astro b/_backup/pages/theme/pilot/audio/_applet.astro deleted file mode 100644 index 90574c88..00000000 --- a/_backup/pages/theme/pilot/audio/_applet.astro +++ /dev/null @@ -1,147 +0,0 @@ ---- -import "@styles/reset.css"; -import "@styles/variables.css"; -import "@styles/fonts.css"; -import "@styles/icons/iconoir.css"; -import "@styles/theme/pilot/variables.css"; ---- - -
-
-
-
- -
-
- -
-
-
-
- - - - diff --git a/_backup/pages/theme/pilot/audio/_manifest.json b/_backup/pages/theme/pilot/audio/_manifest.json deleted file mode 100644 index 70cb090d..00000000 --- a/_backup/pages/theme/pilot/audio/_manifest.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "diffuse/constituent/pilot/audio", - "title": "", - "entrypoint": "index.html", - "actions": { - "modifyIsPlaying": { - "title": "Set is-playing state", - "description": "Indicate if audio is playing or not.", - "params_schema": { - "type": "boolean" - } - }, - "modifyProgress": { - "title": "Set progress", - "description": "Indicate how far the audio has progressed.", - "params_schema": { - "type": "number" - } - } - } -} diff --git a/_backup/pages/theme/pilot/audio/index.astro b/_backup/pages/theme/pilot/audio/index.astro deleted file mode 100644 index dfb1e1a8..00000000 --- a/_backup/pages/theme/pilot/audio/index.astro +++ /dev/null @@ -1,9 +0,0 @@ ---- -import Layout from "@layouts/applet.astro"; -import Applet from "./_applet.astro"; -import { title } from "./_manifest.json"; ---- - - - - diff --git a/_backup/pages/theme/pilot/audio/types.d.ts b/_backup/pages/theme/pilot/audio/types.d.ts deleted file mode 100644 index abb4cae9..00000000 --- a/_backup/pages/theme/pilot/audio/types.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface State { - isPlaying: boolean; - seekPosition?: number; -} diff --git a/_backup/pages/theme/pilot/index.astro b/_backup/pages/theme/pilot/index.astro deleted file mode 100644 index 157211b2..00000000 --- a/_backup/pages/theme/pilot/index.astro +++ /dev/null @@ -1,11 +0,0 @@ ---- -import Page from "../../../layouts/page.astro"; -import "@styles/theme/pilot/index.css"; ---- - - - - - -
-
diff --git a/_backup/scripts/applet/common.ts b/_backup/scripts/applet/common.ts deleted file mode 100644 index 838bda20..00000000 --- a/_backup/scripts/applet/common.ts +++ /dev/null @@ -1,496 +0,0 @@ -import type { Applet, AppletEvent, AppletScope } from "@web-applets/sdk"; -import * as Comlink from "comlink"; - -import { applets } from "@web-applets/sdk"; -import QS from "query-string"; - -import { type ElementConfigurator, h as hyperscript } from "@scripts/spellcaster/hyperscript.js"; -import { isSignal, type Signal, signal } from "@scripts/spellcaster"; - -import type { ResolvedUri } from "@applets/core/types"; -import { transfer, type WorkerTasks } from "@scripts/common"; - -//////////////////////////////////////////// -// 🪟 Applet connecting -//////////////////////////////////////////// -export async function applet( - src: string, - opts: { - addSlashSuffix?: boolean; - container?: HTMLElement | Element; - context?: Window; - frameId?: string; - groupId?: string; - newInstance?: boolean; - setHeight?: boolean; - } = {}, -): Promise> { - src = `${src}${ - src.endsWith("/") - ? "" - : opts.addSlashSuffix === undefined || opts.addSlashSuffix === true - ? "/" - : "" - }`; - - let query: undefined | Record; - query = { groupId: opts.groupId || "main" }; - - if (query) { - src = QS.stringifyUrl({ url: src, query }); - } - - let context = opts.newInstance ? self : opts.context || self.top || self.parent; - - let existingFrame: HTMLIFrameElement | null; - - // TODO: Ideally we do some cross-origin detection here - try { - existingFrame = opts.newInstance ? null : context.document.querySelector(`[src="${src}"]`); - } catch (err) { - existingFrame = null; - context = self; - } - - let frame; - - if (existingFrame) { - frame = existingFrame; - } else { - frame = document.createElement("iframe"); - frame.loading = "eager"; - frame.src = src; - if (opts.frameId) frame.id = opts.frameId; - - if (opts.container) { - opts.container.appendChild(frame); - } else { - context.document.body.appendChild(frame); - } - } - - if (frame.contentWindow === null) { - throw new Error("iframe does not have a contentWindow"); - } - - const applet = await applets.connect(frame.contentWindow, { context }).catch((err) => { - console.error("Error connecting to " + src, err); - throw err; - }); - - if (opts.setHeight) { - applet.onresize = () => { - frame.height = `${applet.height}px`; - frame.classList.add("has-loaded"); - }; - } else { - if (frame.contentDocument?.readyState === "complete") { - frame.classList.add("has-loaded"); - } - - frame.addEventListener("load", () => { - frame.classList.add("has-loaded"); - }); - } - - return applet; -} - -export function tunnel( - worker: Comlink.Remote, - connections: Record>, -) { - Object.entries(connections).forEach(([scheme, promise]) => { - Promise.resolve(promise).then((conn) => { - return worker._manage(scheme, transfer(conn.ports.worker)); - }); - }); -} - -//////////////////////////////////////////// -// 🪟 Applet registration -//////////////////////////////////////////// -export type DiffuseApplet = { - groupId: string | undefined; - scope: AppletScope; - - settled(): Promise; - - get instanceId(): string; - set data(data: T); - - codec: Codec; - unloadHandler?: () => void; - - isMainInstance(): boolean | null; - setActionHandler(actionId: string, actionHandler: H): void; -}; - -export type Codec = { - decode(data: any): T; - encode(data: T): any; -}; - -export function lookupGroupId() { - const url = new URL(location.href); - return url.searchParams.get("groupId") || "main"; -} - -export function register( - options: { mode?: "broadcast" | "shared-worker"; worker?: Comlink.Remote } = {}, -): DiffuseApplet { - const mode = options.mode ?? "broadcast"; - const scope = applets.register(); - - const groupId = lookupGroupId(); - const channelId = `${location.host}${location.pathname}/${groupId}`; - const instanceId = crypto.randomUUID(); - - // Codec - const codec = { - decode: (data: any) => data as DataType, - encode: (data: DataType) => data as any, - }; - - // Context - const context: DiffuseApplet = { - groupId, - scope, - - settled() { - return channelContext?.promise.then(() => {}) ?? Promise.resolve(); - }, - - get instanceId() { - return instanceId; - }, - - get data() { - return scope.data; - }, - - set data(data: DataType) { - scope.data = data; - }, - - codec, - - isMainInstance() { - return channelContext?.mainSignal() ?? null; - }, - - setActionHandler: (actionId: string, actionHandler: H) => { - switch (mode) { - case "broadcast": - return channelContext?.setActionHandler(actionId, actionHandler); - - case "shared-worker": - return scope.setActionHandler(actionId, actionHandler); - } - }, - }; - - if (options.worker) { - context.scope.onworkerport = (event) => { - if (!event.port) return; - options.worker?._listen(transfer(event.port)); - }; - } - - // Channel - const channelContext = - mode === "broadcast" - ? broadcastChannel({ - channelId, - context, - instanceId, - scope, - }) - : undefined; - - return context; -} - -function broadcastChannel({ - channelId, - context, - instanceId, - scope, -}: { - channelId: string; - context: DiffuseApplet; - instanceId: string; - scope: AppletScope; -}) { - const isMain = signal(true); - - // One instance to rule them all - // - // Ping other instances to see if there are any. - // As long as there aren't any, it is considered the main instance. - // - // Actions are performed on the main instance, - // and data is replicated from main to the other instances. - const channel = new BroadcastChannel(channelId); - - channel.addEventListener("message", async (event) => { - switch (event.data?.type) { - case "PING": { - channel.postMessage({ - type: "PONG", - instanceId: event.data.instanceId, - originInstanceId: instanceId, - }); - - if (isMain() && event.data?.isInitialPing === true) { - channel.postMessage({ - type: "data", - data: context.codec.encode(scope.data), - }); - } - break; - } - - case "PONG": { - if (event.data.instanceId === instanceId) { - isMain(false); - } - break; - } - - case "UNLOADED": { - if (!context.isMainInstance()) { - // We need to wait until the other side is actually unloaded 🤷‍♀️ - setTimeout(async () => { - const promised = await makeMainPromise(); - isMain(promised.isMain); - if (promised.isMain) context.unloadHandler?.(); - }, 250); - } - break; - } - - case "action": { - if (isMain()) { - const result = await scope.actionHandlers[event.data.actionId]?.(...event.data.arguments); - channel.postMessage({ - type: "actioncomplete", - actionInstanceId: event.data.actionInstanceId, - result, - }); - } - break; - } - - case "data": { - scope.data = context.codec.decode(event.data.data); - break; - } - } - }); - - // Promise that fullfills whenever it figures out its the main instance or not. - let pinged = false; - - function makeMainPromise(timeoutDuration: number = 500) { - return new Promise<{ isMain: boolean }>((resolve) => { - const timeoutId = setTimeout(() => { - channel.removeEventListener("message", handler); - resolve({ isMain: true }); - }, timeoutDuration); - - const handler = (event: MessageEvent) => { - if ( - (event.data?.type === "PONG" || event.data?.type === "PING") && - event.data?.instanceId === instanceId - ) { - clearTimeout(timeoutId); - channel.removeEventListener("message", handler); - resolve({ isMain: false }); - } - }; - - channel.addEventListener("message", handler); - channel.postMessage({ - type: "PING", - instanceId, - isInitialPing: !pinged, - }); - - pinged = true; - }); - } - - const promise = makeMainPromise(); - - // If the data on the main instance changes, - // pass it on to other instances. - scope.addEventListener("data", async (event: AppletEvent) => { - await promise; - - if (isMain()) { - channel.postMessage({ - type: "data", - data: context.codec.encode(event.data), - }); - } - }); - - // Action handler - const setActionHandler = (actionId: string, actionHandler: H) => { - const handler = async (...args: any) => { - if (isMain()) { - return actionHandler(...args); - } - - // Check if a main instance is still available, - // if not, then this is the new main. - const promised = await makeMainPromise(); - isMain(promised.isMain); - - if (isMain()) { - return actionHandler(...args); - } - - const actionMessage = { - actionInstanceId: crypto.randomUUID(), - actionId, - type: "action", - arguments: args, - }; - - return await new Promise((resolve) => { - const actionCallback = (event: MessageEvent) => { - if ( - event.data?.type === "actioncomplete" && - event.data?.actionInstanceId === actionMessage.actionInstanceId - ) { - channel.removeEventListener("message", actionCallback); - resolve(event.data.result); - } - }; - - channel.addEventListener("message", actionCallback); - channel.postMessage(actionMessage); - }); - }; - - scope.setActionHandler(actionId, handler); - }; - - // Before unload - self.addEventListener("beforeunload", (event) => { - if (context.isMainInstance()) { - channel.postMessage({ - type: "UNLOADED", - }); - } - }); - - // Fin - return { - channel, - mainSignal: isMain, - promise, - setActionHandler, - }; -} - -//////////////////////////////////////////// -// 🔮 Reactive state management -//////////////////////////////////////////// -export function reactive( - applet: Applet | AppletScope, - dataFn: (data: D) => T, - effectFn: (t: T) => void, -) { - let value = dataFn(applet.data); - effectFn(value); - - applet.addEventListener("data", (event: AppletEvent) => { - const newData = dataFn(event.data); - if (newData !== value) { - value = newData; - effectFn(value); - } - }); -} - -//////////////////////////////////////////// -// ⚡️ COMMON ACTION CALLS -//////////////////////////////////////////// - -export async function inputUrl(input: Applet, uri: string, method = "GET") { - return await input.sendAction( - "resolve", - { - method, - uri, - }, - { - timeoutDuration: 60000 * 5, - worker: true, - }, - ); -} - -//////////////////////////////////////////// -// 🛠️ -//////////////////////////////////////////// -export function addScope(astroScope: string, object: O): O { - return { - ...object, - attrs: { - ...((object as any).attrs || {}), - [`data-astro-cid-${astroScope}`]: "", - }, - }; -} - -export function appletScopePort() { - let port: MessagePort | undefined; - - function connection(event: AppletEvent) { - if (event.data?.type === "appletconnect") { - window.removeEventListener("message", connection); - port = (event as any).ports[0]; - } - } - - window.addEventListener("message", connection); - - return () => port; -} - -export const hs = - (astroScope: string) => - ( - tag: string, - props?: Record | (() => Record), - configure?: ElementConfigurator, - ) => { - const propsWithScope = - props && isSignal(props) - ? () => addScope(astroScope, props()) - : addScope(astroScope, props || {}); - - return hyperscript(tag, propsWithScope, configure); - }; - -export function wait(applet: Applet, dataFn: (a: A | undefined) => boolean): Promise { - return new Promise((resolve) => { - if (dataFn(applet.data) === true) { - resolve(); - return; - } - - const callback = (event: AppletEvent) => { - if (dataFn(event.data) === true) { - applet.removeEventListener("data", callback); - resolve(); - } - }; - - applet.addEventListener("data", callback); - }); -} diff --git a/_backup/scripts/engine/queue/types.d.ts b/_backup/scripts/engine/queue/types.d.ts deleted file mode 100644 index 6038dc1e..00000000 --- a/_backup/scripts/engine/queue/types.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Track } from "@applets/core/types"; - -export type Item = Track & { - manualEntry?: boolean; -}; - -export interface State { - past: Item[]; - now: Item | null; - future: Item[]; -} diff --git a/_backup/scripts/engine/queue/worker.ts b/_backup/scripts/engine/queue/worker.ts deleted file mode 100644 index ba31164d..00000000 --- a/_backup/scripts/engine/queue/worker.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { getTransferables } from "@okikio/transferables"; - -import type { Track } from "@applets/core/types.js"; -import type { Item, State } from "./types"; -import { arrayShuffle, postMessages, provide } from "@scripts/common.ts"; - -//////////////////////////////////////////// -// SETUP -//////////////////////////////////////////// - -const actions = { - add, - pool, - shift, - unshift, -}; - -const { ports, tasks } = provide({ - actions, - tasks: { ...actions, data }, -}); - -export type Actions = typeof actions; -export type Tasks = typeof tasks; - -//////////////////////////////////////////// -// STATE -//////////////////////////////////////////// - -const QUEUE_SIZE = 25; - -const _internal: Record = {}; -const _state: Record = {}; - -function data(groupId: string) { - return state(groupId); -} - -function emptyState(groupId: string): State { - return { - future: [], - now: null, - past: [], - }; -} - -function notify(groupId: string) { - const d = data(groupId); - - postMessages({ - data: { - type: "data", - data: d, - groupId, - }, - ports: ports.applets, - transfer: getTransferables(d), - }); -} - -function internal(groupId: string) { - _internal[groupId] ??= { pool: [] }; - return _internal[groupId]; -} - -function state(groupId: string) { - _state[groupId] ??= emptyState(groupId); - return _state[groupId]; -} - -//////////////////////////////////////////// -// ACTIONS -//////////////////////////////////////////// - -function add({ groupId, items }: { groupId: string; items: Item[] }) { - state(groupId).future = [...state(groupId).future, ...items]; - notify(groupId); -} - -function pool({ groupId, tracks }: { groupId: string; tracks: Track[] }) { - internal(groupId).pool = tracks; - const queue = state(groupId); - - // TODO: If the pool changes, only remove non-existing tracks - // instead of resetting the whole future queue. - // - // What about past queue items? - - queue.future = []; - fill(groupId); - - // Automatically insert track if there isn't any - if (!queue.now) return shift({ groupId }); - else notify(groupId); -} - -function shift({ groupId }: { groupId: string }) { - const queue = state(groupId); - const now = queue.future[0] ?? null; - queue.now = now; - - queue.future = queue.future.slice(1); - queue.past = now ? [...queue.past, now] : queue.past; - - fill(groupId); -} - -function unshift({ groupId }: { groupId: string }) { - const queue = state(groupId); - if (queue.past.length === 0) return; - - const [last] = queue.past.splice(queue.past.length - 1, 1); - const now = last ?? null; - - queue.now = now; - queue.future = now ? [now, ...queue.future] : queue.future; - - notify(groupId); -} - -// 🛠️ - -// TODO: Most likely there's a more performant solution -function fill(groupId: string) { - const queue = state(groupId); - if (queue.future.length >= QUEUE_SIZE) return; - - const pool: Track[] = []; - - let past = new Set(queue.past.map((t) => t.id)); - let reducedPool = pool; - - internal(groupId).pool.forEach((track: Track) => { - if (past.has(track.id)) { - past = past.difference(new Set(track.id)); - } else { - pool.push(track); - } - }); - - if (reducedPool.length === 0) { - reducedPool = internal(groupId).pool; - } - - const poolSelection = arrayShuffle(reducedPool).slice(0, QUEUE_SIZE - queue.future.length); - add({ groupId, items: poolSelection }); -} diff --git a/_backup/scripts/theme/blur/index.ts b/_backup/scripts/theme/blur/index.ts deleted file mode 100644 index 67918ad7..00000000 --- a/_backup/scripts/theme/blur/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { ManagedOutput } from "@applets/core/types"; -import { applet } from "@scripts/applet/common"; - -//////////////////////////////////////////// -// 🗂️ Applets -//////////////////////////////////////////// -import type * as QueueEngine from "@applets/engine/queue/types.d.ts"; - -const container = document.querySelector("main"); -if (!container) throw new Error("Missing container"); - -const labelA = "Deck A"; -const labelB = "Deck B"; - -// const configurator = { -// output: await applet("/configurator/output"), -// }; - -const constituent = { - a: applet("/constituent/blur/artwork-controller", { - container, - groupId: labelA, - }), - b: applet("/constituent/blur/artwork-controller", { - container, - groupId: labelB, - }), - browser: applet("/constituent/blur/browser", { - container, - }), -}; - -// TODO: -// const _orchestrator = { -// primary: applet("/orchestrator/primary", { groupId: labelA }), -// }; - -// const engine = { -// queue: { -// a: await applet("/engine/queue", { groupId: labelA }), -// b: await applet("/engine/queue", { groupId: labelB }), -// }, -// }; - -// const deckA = engine.queue.a; -// const deckB = engine.queue.b; diff --git a/_backup/scripts/theme/pilot/index.ts b/_backup/scripts/theme/pilot/index.ts deleted file mode 100644 index ef53b72e..00000000 --- a/_backup/scripts/theme/pilot/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { applet, reactive } from "@scripts/applet/common"; - -//////////////////////////////////////////// -// 🗂️ Applets -//////////////////////////////////////////// -import type * as AudioEngine from "@applets/engine/audio/types.d.ts"; -import type * as QueueEngine from "@applets/engine/queue/types.d.ts"; - -import type * as AudioUI from "@applets/theme/pilot/audio/types"; - -const engine = { - audio: await applet("/engine/audio"), - queue: await applet("/engine/queue"), -}; - -const orchestrator = { - queueAudio: applet("/orchestrator/queue-audio"), - queueTracks: applet("/orchestrator/queue-tracks"), - processTracks: applet("/orchestrator/process-tracks"), -}; - -const ui = { - audio: await applet("/theme/pilot/audio/", { setHeight: true }), -}; - -//////////////////////////////////////////// -// ⚙️ [Connections → Engines] -// 🔉 AUDIO -//////////////////////////////////////////// - -// NOTE: -// These could probably be optimised, but it works. - -reactive( - engine.audio, - (data) => - data.isPlaying && (data.items[engine.queue.data.now?.id ?? Infinity]?.isPlaying ?? false), - (isPlaying) => ui.audio.sendAction("modifyIsPlaying", isPlaying), -); - -reactive( - engine.audio, - (data) => data.items[engine.queue.data.now?.id ?? Infinity]?.progress ?? 0, - (progress: number) => ui.audio.sendAction("modifyProgress", progress), -); - -//////////////////////////////////////////// -// 🌅 [Connections → UI] -// 🔉 AUDIO -//////////////////////////////////////////// - -let initialAudioChecked = false; - -reactive( - ui.audio, - (data) => data.isPlaying, - async (isPlaying) => { - const audioId = engine.queue.data.now?.id; - - // Sync audio state and ui state - // TODO: Figure out a better way to do this - if (!initialAudioChecked) { - if (engine.audio.data.isPlaying && !isPlaying) { - ui.audio.sendAction("modifyIsPlaying", true); - initialAudioChecked = true; - return; - } - } - - // Otherwise just control the audio - if (isPlaying) { - engine.audio.sendAction("play", { audioId }); - } else { - engine.audio.sendAction("pause", { audioId }); - } - }, -); - -reactive( - ui.audio, - (data: AudioUI.State) => data.seekPosition, - (seekPosition) => { - if (seekPosition !== undefined && engine.queue.data.now?.id) { - engine.audio.sendAction("seek", { - percentage: seekPosition, - audioId: engine.queue.data.now.id, - }); - } - }, -); diff --git a/_config.ts b/_config.ts index 6ee956c3..0978ba11 100644 --- a/_config.ts +++ b/_config.ts @@ -26,3 +26,9 @@ export default site; site.use(postcss({ includes: false })); site.add([".css"]); + +// BINARY ASSETS + +site.add("/favicons"); +site.add("/fonts"); +site.add("/images"); diff --git a/src/_components/element.vto b/src/_components/element.vto new file mode 100644 index 00000000..43ad6650 --- /dev/null +++ b/src/_components/element.vto @@ -0,0 +1,9 @@ +
+

{{title}}

+ +

+ {{content}} +

+ + {{ await comp.list({ items }) }} +
diff --git a/src/_components/list.vto b/src/_components/list.vto new file mode 100644 index 00000000..77c6fa43 --- /dev/null +++ b/src/_components/list.vto @@ -0,0 +1,11 @@ +
diff --git a/_backup/layouts/page.astro b/src/_includes/layouts/diffuse.vto similarity index 52% rename from _backup/layouts/page.astro rename to src/_includes/layouts/diffuse.vto index 2d33e424..82ccd526 100644 --- a/_backup/layouts/page.astro +++ b/src/_includes/layouts/diffuse.vto @@ -1,5 +1,5 @@ --- -const { title } = Astro.props; +title: "Diffuse" --- @@ -7,15 +7,15 @@ const { title } = Astro.props; - {title} + {{title}} - + {{ content }} diff --git a/public/favicons/android-chrome-192x192.png b/src/favicons/android-chrome-192x192.png similarity index 100% rename from public/favicons/android-chrome-192x192.png rename to src/favicons/android-chrome-192x192.png diff --git a/public/favicons/android-chrome-512x512.png b/src/favicons/android-chrome-512x512.png similarity index 100% rename from public/favicons/android-chrome-512x512.png rename to src/favicons/android-chrome-512x512.png diff --git a/public/favicons/apple-touch-icon.png b/src/favicons/apple-touch-icon.png similarity index 100% rename from public/favicons/apple-touch-icon.png rename to src/favicons/apple-touch-icon.png diff --git a/public/favicons/browserconfig.xml b/src/favicons/browserconfig.xml similarity index 100% rename from public/favicons/browserconfig.xml rename to src/favicons/browserconfig.xml diff --git a/public/favicons/favicon-16x16.png b/src/favicons/favicon-16x16.png similarity index 100% rename from public/favicons/favicon-16x16.png rename to src/favicons/favicon-16x16.png diff --git a/public/favicons/favicon-32x32.png b/src/favicons/favicon-32x32.png similarity index 100% rename from public/favicons/favicon-32x32.png rename to src/favicons/favicon-32x32.png diff --git a/public/favicons/favicon.ico b/src/favicons/favicon.ico similarity index 100% rename from public/favicons/favicon.ico rename to src/favicons/favicon.ico diff --git a/public/favicons/mstile-150x150.png b/src/favicons/mstile-150x150.png similarity index 100% rename from public/favicons/mstile-150x150.png rename to src/favicons/mstile-150x150.png diff --git a/public/favicons/safari-pinned-tab.svg b/src/favicons/safari-pinned-tab.svg similarity index 100% rename from public/favicons/safari-pinned-tab.svg rename to src/favicons/safari-pinned-tab.svg diff --git a/public/fonts/InterVariable-Italic.woff2 b/src/fonts/InterVariable-Italic.woff2 similarity index 100% rename from public/fonts/InterVariable-Italic.woff2 rename to src/fonts/InterVariable-Italic.woff2 diff --git a/public/fonts/InterVariable.woff2 b/src/fonts/InterVariable.woff2 similarity index 100% rename from public/fonts/InterVariable.woff2 rename to src/fonts/InterVariable.woff2 diff --git a/public/images/background/1.jpg b/src/images/background/1.jpg similarity index 100% rename from public/images/background/1.jpg rename to src/images/background/1.jpg diff --git a/public/images/background/10.jpg b/src/images/background/10.jpg similarity index 100% rename from public/images/background/10.jpg rename to src/images/background/10.jpg diff --git a/public/images/background/11.jpg b/src/images/background/11.jpg similarity index 100% rename from public/images/background/11.jpg rename to src/images/background/11.jpg diff --git a/public/images/background/12.jpg b/src/images/background/12.jpg similarity index 100% rename from public/images/background/12.jpg rename to src/images/background/12.jpg diff --git a/public/images/background/13.jpg b/src/images/background/13.jpg similarity index 100% rename from public/images/background/13.jpg rename to src/images/background/13.jpg diff --git a/public/images/background/14.jpg b/src/images/background/14.jpg similarity index 100% rename from public/images/background/14.jpg rename to src/images/background/14.jpg diff --git a/public/images/background/15.jpg b/src/images/background/15.jpg similarity index 100% rename from public/images/background/15.jpg rename to src/images/background/15.jpg diff --git a/public/images/background/16.jpg b/src/images/background/16.jpg similarity index 100% rename from public/images/background/16.jpg rename to src/images/background/16.jpg diff --git a/public/images/background/17.jpg b/src/images/background/17.jpg similarity index 100% rename from public/images/background/17.jpg rename to src/images/background/17.jpg diff --git a/public/images/background/18.jpg b/src/images/background/18.jpg similarity index 100% rename from public/images/background/18.jpg rename to src/images/background/18.jpg diff --git a/public/images/background/19.jpg b/src/images/background/19.jpg similarity index 100% rename from public/images/background/19.jpg rename to src/images/background/19.jpg diff --git a/public/images/background/2.jpg b/src/images/background/2.jpg similarity index 100% rename from public/images/background/2.jpg rename to src/images/background/2.jpg diff --git a/public/images/background/20.jpg b/src/images/background/20.jpg similarity index 100% rename from public/images/background/20.jpg rename to src/images/background/20.jpg diff --git a/public/images/background/21.jpg b/src/images/background/21.jpg similarity index 100% rename from public/images/background/21.jpg rename to src/images/background/21.jpg diff --git a/public/images/background/22.jpg b/src/images/background/22.jpg similarity index 100% rename from public/images/background/22.jpg rename to src/images/background/22.jpg diff --git a/public/images/background/23.jpg b/src/images/background/23.jpg similarity index 100% rename from public/images/background/23.jpg rename to src/images/background/23.jpg diff --git a/public/images/background/24.jpg b/src/images/background/24.jpg similarity index 100% rename from public/images/background/24.jpg rename to src/images/background/24.jpg diff --git a/public/images/background/25.jpg b/src/images/background/25.jpg similarity index 100% rename from public/images/background/25.jpg rename to src/images/background/25.jpg diff --git a/public/images/background/26.jpg b/src/images/background/26.jpg similarity index 100% rename from public/images/background/26.jpg rename to src/images/background/26.jpg diff --git a/public/images/background/27.jpg b/src/images/background/27.jpg similarity index 100% rename from public/images/background/27.jpg rename to src/images/background/27.jpg diff --git a/public/images/background/28.jpg b/src/images/background/28.jpg similarity index 100% rename from public/images/background/28.jpg rename to src/images/background/28.jpg diff --git a/public/images/background/29.jpg b/src/images/background/29.jpg similarity index 100% rename from public/images/background/29.jpg rename to src/images/background/29.jpg diff --git a/public/images/background/3.jpg b/src/images/background/3.jpg similarity index 100% rename from public/images/background/3.jpg rename to src/images/background/3.jpg diff --git a/public/images/background/30.jpg b/src/images/background/30.jpg similarity index 100% rename from public/images/background/30.jpg rename to src/images/background/30.jpg diff --git a/public/images/background/4.jpg b/src/images/background/4.jpg similarity index 100% rename from public/images/background/4.jpg rename to src/images/background/4.jpg diff --git a/public/images/background/5.jpg b/src/images/background/5.jpg similarity index 100% rename from public/images/background/5.jpg rename to src/images/background/5.jpg diff --git a/public/images/background/6.jpg b/src/images/background/6.jpg similarity index 100% rename from public/images/background/6.jpg rename to src/images/background/6.jpg diff --git a/public/images/background/7.jpg b/src/images/background/7.jpg similarity index 100% rename from public/images/background/7.jpg rename to src/images/background/7.jpg diff --git a/public/images/background/8.jpg b/src/images/background/8.jpg similarity index 100% rename from public/images/background/8.jpg rename to src/images/background/8.jpg diff --git a/public/images/background/9.jpg b/src/images/background/9.jpg similarity index 100% rename from public/images/background/9.jpg rename to src/images/background/9.jpg diff --git a/public/images/background/thumbnails/1.jpg b/src/images/background/thumbnails/1.jpg similarity index 100% rename from public/images/background/thumbnails/1.jpg rename to src/images/background/thumbnails/1.jpg diff --git a/public/images/background/thumbnails/10.jpg b/src/images/background/thumbnails/10.jpg similarity index 100% rename from public/images/background/thumbnails/10.jpg rename to src/images/background/thumbnails/10.jpg diff --git a/public/images/background/thumbnails/11.jpg b/src/images/background/thumbnails/11.jpg similarity index 100% rename from public/images/background/thumbnails/11.jpg rename to src/images/background/thumbnails/11.jpg diff --git a/public/images/background/thumbnails/12.jpg b/src/images/background/thumbnails/12.jpg similarity index 100% rename from public/images/background/thumbnails/12.jpg rename to src/images/background/thumbnails/12.jpg diff --git a/public/images/background/thumbnails/13.jpg b/src/images/background/thumbnails/13.jpg similarity index 100% rename from public/images/background/thumbnails/13.jpg rename to src/images/background/thumbnails/13.jpg diff --git a/public/images/background/thumbnails/14.jpg b/src/images/background/thumbnails/14.jpg similarity index 100% rename from public/images/background/thumbnails/14.jpg rename to src/images/background/thumbnails/14.jpg diff --git a/public/images/background/thumbnails/15.jpg b/src/images/background/thumbnails/15.jpg similarity index 100% rename from public/images/background/thumbnails/15.jpg rename to src/images/background/thumbnails/15.jpg diff --git a/public/images/background/thumbnails/16.jpg b/src/images/background/thumbnails/16.jpg similarity index 100% rename from public/images/background/thumbnails/16.jpg rename to src/images/background/thumbnails/16.jpg diff --git a/public/images/background/thumbnails/17.jpg b/src/images/background/thumbnails/17.jpg similarity index 100% rename from public/images/background/thumbnails/17.jpg rename to src/images/background/thumbnails/17.jpg diff --git a/public/images/background/thumbnails/18.jpg b/src/images/background/thumbnails/18.jpg similarity index 100% rename from public/images/background/thumbnails/18.jpg rename to src/images/background/thumbnails/18.jpg diff --git a/public/images/background/thumbnails/19.jpg b/src/images/background/thumbnails/19.jpg similarity index 100% rename from public/images/background/thumbnails/19.jpg rename to src/images/background/thumbnails/19.jpg diff --git a/public/images/background/thumbnails/2.jpg b/src/images/background/thumbnails/2.jpg similarity index 100% rename from public/images/background/thumbnails/2.jpg rename to src/images/background/thumbnails/2.jpg diff --git a/public/images/background/thumbnails/20.jpg b/src/images/background/thumbnails/20.jpg similarity index 100% rename from public/images/background/thumbnails/20.jpg rename to src/images/background/thumbnails/20.jpg diff --git a/public/images/background/thumbnails/21.jpg b/src/images/background/thumbnails/21.jpg similarity index 100% rename from public/images/background/thumbnails/21.jpg rename to src/images/background/thumbnails/21.jpg diff --git a/public/images/background/thumbnails/22.jpg b/src/images/background/thumbnails/22.jpg similarity index 100% rename from public/images/background/thumbnails/22.jpg rename to src/images/background/thumbnails/22.jpg diff --git a/public/images/background/thumbnails/23.jpg b/src/images/background/thumbnails/23.jpg similarity index 100% rename from public/images/background/thumbnails/23.jpg rename to src/images/background/thumbnails/23.jpg diff --git a/public/images/background/thumbnails/24.jpg b/src/images/background/thumbnails/24.jpg similarity index 100% rename from public/images/background/thumbnails/24.jpg rename to src/images/background/thumbnails/24.jpg diff --git a/public/images/background/thumbnails/25.jpg b/src/images/background/thumbnails/25.jpg similarity index 100% rename from public/images/background/thumbnails/25.jpg rename to src/images/background/thumbnails/25.jpg diff --git a/public/images/background/thumbnails/26.jpg b/src/images/background/thumbnails/26.jpg similarity index 100% rename from public/images/background/thumbnails/26.jpg rename to src/images/background/thumbnails/26.jpg diff --git a/public/images/background/thumbnails/27.jpg b/src/images/background/thumbnails/27.jpg similarity index 100% rename from public/images/background/thumbnails/27.jpg rename to src/images/background/thumbnails/27.jpg diff --git a/public/images/background/thumbnails/28.jpg b/src/images/background/thumbnails/28.jpg similarity index 100% rename from public/images/background/thumbnails/28.jpg rename to src/images/background/thumbnails/28.jpg diff --git a/public/images/background/thumbnails/29.jpg b/src/images/background/thumbnails/29.jpg similarity index 100% rename from public/images/background/thumbnails/29.jpg rename to src/images/background/thumbnails/29.jpg diff --git a/public/images/background/thumbnails/3.jpg b/src/images/background/thumbnails/3.jpg similarity index 100% rename from public/images/background/thumbnails/3.jpg rename to src/images/background/thumbnails/3.jpg diff --git a/public/images/background/thumbnails/30.jpg b/src/images/background/thumbnails/30.jpg similarity index 100% rename from public/images/background/thumbnails/30.jpg rename to src/images/background/thumbnails/30.jpg diff --git a/public/images/background/thumbnails/4.jpg b/src/images/background/thumbnails/4.jpg similarity index 100% rename from public/images/background/thumbnails/4.jpg rename to src/images/background/thumbnails/4.jpg diff --git a/public/images/background/thumbnails/5.jpg b/src/images/background/thumbnails/5.jpg similarity index 100% rename from public/images/background/thumbnails/5.jpg rename to src/images/background/thumbnails/5.jpg diff --git a/public/images/background/thumbnails/6.jpg b/src/images/background/thumbnails/6.jpg similarity index 100% rename from public/images/background/thumbnails/6.jpg rename to src/images/background/thumbnails/6.jpg diff --git a/public/images/background/thumbnails/7.jpg b/src/images/background/thumbnails/7.jpg similarity index 100% rename from public/images/background/thumbnails/7.jpg rename to src/images/background/thumbnails/7.jpg diff --git a/public/images/background/thumbnails/8.jpg b/src/images/background/thumbnails/8.jpg similarity index 100% rename from public/images/background/thumbnails/8.jpg rename to src/images/background/thumbnails/8.jpg diff --git a/public/images/background/thumbnails/9.jpg b/src/images/background/thumbnails/9.jpg similarity index 100% rename from public/images/background/thumbnails/9.jpg rename to src/images/background/thumbnails/9.jpg diff --git a/public/images/diffuse-current.svg b/src/images/diffuse-current.svg similarity index 100% rename from public/images/diffuse-current.svg rename to src/images/diffuse-current.svg diff --git a/public/images/diffuse-dark.svg b/src/images/diffuse-dark.svg similarity index 100% rename from public/images/diffuse-dark.svg rename to src/images/diffuse-dark.svg diff --git a/public/images/diffuse-grey.svg b/src/images/diffuse-grey.svg similarity index 100% rename from public/images/diffuse-grey.svg rename to src/images/diffuse-grey.svg diff --git a/public/images/diffuse-light.svg b/src/images/diffuse-light.svg similarity index 100% rename from public/images/diffuse-light.svg rename to src/images/diffuse-light.svg diff --git a/public/images/diffuse__icon-dark.svg b/src/images/diffuse__icon-dark.svg similarity index 100% rename from public/images/diffuse__icon-dark.svg rename to src/images/diffuse__icon-dark.svg diff --git a/public/images/diffuse__icon-grey.svg b/src/images/diffuse__icon-grey.svg similarity index 100% rename from public/images/diffuse__icon-grey.svg rename to src/images/diffuse__icon-grey.svg diff --git a/public/images/diffuse__icon-light.svg b/src/images/diffuse__icon-light.svg similarity index 100% rename from public/images/diffuse__icon-light.svg rename to src/images/diffuse__icon-light.svg diff --git a/public/images/icon-square-ws.png b/src/images/icon-square-ws.png similarity index 100% rename from public/images/icon-square-ws.png rename to src/images/icon-square-ws.png diff --git a/public/images/icon-square.png b/src/images/icon-square.png similarity index 100% rename from public/images/icon-square.png rename to src/images/icon-square.png diff --git a/public/images/icon.png b/src/images/icon.png similarity index 100% rename from public/images/icon.png rename to src/images/icon.png diff --git a/public/images/icons/windows_98/cd_audio_cd_a-4.png b/src/images/icons/windows_98/cd_audio_cd_a-4.png similarity index 100% rename from public/images/icons/windows_98/cd_audio_cd_a-4.png rename to src/images/icons/windows_98/cd_audio_cd_a-4.png diff --git a/public/images/icons/windows_98/directory_open_cool-0.png b/src/images/icons/windows_98/directory_open_cool-0.png similarity index 100% rename from public/images/icons/windows_98/directory_open_cool-0.png rename to src/images/icons/windows_98/directory_open_cool-0.png diff --git a/public/images/icons/windows_98/directory_open_file_mydocs_2k-2.png b/src/images/icons/windows_98/directory_open_file_mydocs_2k-2.png similarity index 100% rename from public/images/icons/windows_98/directory_open_file_mydocs_2k-2.png rename to src/images/icons/windows_98/directory_open_file_mydocs_2k-2.png diff --git a/src/index.vto b/src/index.vto new file mode 100644 index 00000000..1ca3479b --- /dev/null +++ b/src/index.vto @@ -0,0 +1,116 @@ +--- +layout: layouts/diffuse.vto + +engines: + - url: "engine/audio/" + title: "Audio" + - url: "engine/queue/" + title: "Queue" + +--- + + +
+

+ + Diffuse + + +

+

+ Diffuse is a collection of custom elements that make it possible to + listen to audio from various sources on your devices and the web, and to create the ideal digital + listening experience for you. +

+

+ ⚠️ Heavily experimental +

+
+
+
+ +
+

Themes

+ +

+ Themes are compositions and provide a traditional browser web application way of + using them. Each theme is unique, not just a skin (ie. not like winamp skins). +

+ +

+ For example, most themes here will limit the currently playing audio tracks to one item, + but you might as well create a DJ theme that can play multiple items at the same time. +

+
+ + + +
+ + + + + +
+

Elements

+ +

+ The (web) components of the system. These + are then recombined into an entire music player experience, or whatever you want to build. +

+ +
+ {{ await comp.element({ + title: "Engines", + items: engines, + content: ` + Elements with each a singular purpose and don't have any UI. There are specialised UI and + orchestrator elements that control these. + ` + }) }} + + +
+
+ + + + + + +
diff --git a/src/styles/applet/common.css b/src/styles/applet/common.css deleted file mode 100644 index 86c12e42..00000000 --- a/src/styles/applet/common.css +++ /dev/null @@ -1,5 +0,0 @@ -.with-icon { - align-items: center; - display: inline-flex; - gap: 0.75em; -} diff --git a/src/styles/fonts.css b/src/styles/fonts.css index 62a900f8..ade90e09 100644 --- a/src/styles/fonts.css +++ b/src/styles/fonts.css @@ -3,7 +3,7 @@ font-family: InterVariable; font-style: normal; font-weight: 100 900; - src: url("/fonts/InterVariable.woff2") format("woff2"); + src: url("../fonts/InterVariable.woff2") format("woff2"); } @font-face { @@ -11,5 +11,5 @@ font-family: InterVariable; font-style: italic; font-weight: 100 900; - src: url("/fonts/InterVariable-Italic.woff2") format("woff2"); + src: url("../fonts/InterVariable-Italic.woff2") format("woff2"); } diff --git a/src/styles/page/index.css b/src/styles/page/index.css index d1a2355d..19b05a7b 100644 --- a/src/styles/page/index.css +++ b/src/styles/page/index.css @@ -16,6 +16,11 @@ a { text-underline-offset: 6px; } +h1 { + margin: var(--space-lg) 0 var(--space-lg); + padding-top: var(--space-2xs); +} + h1 svg { fill: oklch(from var(--bg-color) calc(l - 0.5) c h); opacity: 0.125; @@ -70,7 +75,7 @@ ol { gap: 0 var(--space-3xl); } -.applet { +.element { min-width: min(var(--container-xs), 100%); width: 32.5%; } diff --git a/src/styles/theme/pilot/index.css b/src/styles/theme/pilot/index.css deleted file mode 100644 index daf99920..00000000 --- a/src/styles/theme/pilot/index.css +++ /dev/null @@ -1,78 +0,0 @@ -@import "./variables.css"; - -/*********************************** - * Fonts - ***********************************/ -:root { - font-family: "Inter", sans-serif; - font-size: var(--fs-base); -} - -@supports (font-variation-settings: normal) { - :root { - font-family: "InterVariable", sans-serif; - font-feature-settings: - "ss03" 2, - "ss02" 2; - font-optical-sizing: auto; - } -} - -body { - background-color: var(--delicate-cloud); - color: var(--made-in-the-shade); - display: flex; - flex-direction: column; - overflow: hidden; - height: 100dvh; -} - -iframe { - border: 0; -} - -/*********************************** - * Applets (UI) - ***********************************/ -#applet__ui__audio { - opacity: 0; - pointer-events: none; - transition: 375ms opacity; - transition-delay: 250ms; - width: 100%; - - &.has-loaded { - opacity: 1; - pointer-events: initial; - } -} - -/*********************************** - * Applets (No UI) - ***********************************/ -iframe[src*="/configurator/"], -iframe[src*="/engine/"], -iframe[src*="/input/"], -iframe[src*="/orchestrator/"], -iframe[src*="/processor/"], -iframe[src*="/output/"] { - height: 0; - left: 110vw; - opacity: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 110vh; - width: 0; -} - -/* Audio is a special case, iframe needs to be "visible" in order to play the audio. */ -#applet__engine__audio { - height: 1px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - width: 1px; -} diff --git a/src/styles/theme/pilot/variables.css b/src/styles/theme/pilot/variables.css deleted file mode 100644 index 89795a72..00000000 --- a/src/styles/theme/pilot/variables.css +++ /dev/null @@ -1,26 +0,0 @@ -:root { - /* Colors */ - /* https://farbvelo.elastiq.ch/?s=eyJzIjoiZTBjNjIyMTdiNTcxZSIsImEiOjYsImNnIjo0LCJoZyI6dHJ1ZSwiaGIiOmZhbHNlLCJobyI6ZmFsc2UsImhjIjpmYWxzZSwiaHQiOmZhbHNlLCJiIjpmYWxzZSwicCI6MC4xNzUsIm1kIjo2MCwiY20iOiJsYWIiLCJmIjoiTGVnYWN5IiwiYyI6ImhzbHV2Iiwic2MiOmZhbHNlLCJidyI6dHJ1ZSwiYWgiOmZhbHNlLCJpdSI6IiIsImxtIjp0cnVlLCJzbSI6ZmFsc2UsImN2IjoiaGV4IiwicW0iOiJhcnQtcGFsZXR0ZSIsIm5sIjoiYmVzdE9mIn0= */ - --moonscape: #7f6c71; - --grandma’s-pink-tiles: #e1bac0; - --cinderella: #f8d1c6; - --young-apricot: #f8d7b6; - --cereal-flake: #f0d8ad; - --oatmeal: #cdc5b9; - - /* https://farbvelo.elastiq.ch/?s=eyJzIjoiZmZjY2JkZDg2ZjEzYiIsImEiOjYsImNnIjo0LCJoZyI6dHJ1ZSwiaGIiOmZhbHNlLCJobyI6ZmFsc2UsImhjIjpmYWxzZSwiaHQiOmZhbHNlLCJiIjpmYWxzZSwicCI6MC4xNzgzMDcwODQxNjMzNDY2LCJtZCI6NjAsImNtIjoibGFiIiwiZiI6IkxlZ2FjeSIsImMiOiJoc2x1diIsInNjIjpmYWxzZSwiYnciOnRydWUsImFoIjpmYWxzZSwiaXUiOiIiLCJsbSI6dHJ1ZSwic20iOmZhbHNlLCJjdiI6ImhzbCIsInFtIjoiYXJ0LXBhbGV0dGUiLCJubCI6ImJlc3RPZiJ9 */ - --made-in-the-shade: #67717c; - --misty-mountains: #b8cce0; - --lucid-dreams: #c7e6f4; - --icy-breeze: #c2eff1; - --crushed-ice: #bdf5ed; - --water-leaf: #b7efe7; - - /* https://farbvelo.elastiq.ch/?s=eyJzIjoiODJiN2FjMjU1ODRiOCIsImEiOjYsImNnIjo0LCJoZyI6dHJ1ZSwiaGIiOmZhbHNlLCJobyI6ZmFsc2UsImhjIjpmYWxzZSwiaHQiOmZhbHNlLCJiIjpmYWxzZSwicCI6MC4yMTkxOTgyMDcxNzEzMTQ3LCJtZCI6NjAsImNtIjoibGFiIiwiZiI6IkxlZ2FjeSIsImMiOiJoc2x1diIsInNjIjpmYWxzZSwiYnciOnRydWUsImFoIjpmYWxzZSwiaXUiOiIiLCJsbSI6dHJ1ZSwic20iOmZhbHNlLCJjdiI6ImhleCIsInFtIjoiYXJ0LXBhbGV0dGUiLCJubCI6ImJlc3RPZiJ9 */ - --wizards-brew: #9d8bb3; - --innocent-snowdrop: #cec0fa; - --foggy-plateau: #d5d2fb; - --puffy-cloud: #dce3fb; - --diamond-white: #e1f4fb; - --delicate-cloud: #d9dbe4; -}