From dd42627bd5808ec6f0cd3caba7615e64985cb12f Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Tue, 11 Nov 2025 17:23:17 +0100 Subject: [PATCH] feat: json string output transformer --- _config.ts | 4 ++ src/common/element.d.ts | 17 ------ src/common/element.js | 2 +- .../orchestrator/process-tracks/element.js | 2 +- .../orchestrator/queue-tracks/element.js | 2 +- .../orchestrator/search-tracks/element.js | 2 +- src/components/output/types.d.ts | 9 +-- .../transformer/output/string/json/element.js | 61 +++++++++++++++++++ src/definitions/index.ts | 2 +- src/index.vto | 9 ++- src/themes/webamp/browser/element.js | 2 +- src/themes/webamp/index.js | 1 + src/themes/webamp/index.vto | 13 ++-- 13 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 src/components/transformer/output/string/json/element.js diff --git a/_config.ts b/_config.ts index 5cae8c27..687fc71c 100644 --- a/_config.ts +++ b/_config.ts @@ -58,6 +58,10 @@ site.remoteFile( ), ); +// DEFINITIONS + +site.add("/definitions"); + // MISC site.use(sourceMaps()); diff --git a/src/common/element.d.ts b/src/common/element.d.ts index fa2b35bf..1e37c064 100644 --- a/src/common/element.d.ts +++ b/src/common/element.d.ts @@ -10,23 +10,6 @@ export type HtmlTagFunction = ( ...values: unknown[] ) => string; -type MorphOptions = { - getNodeKey?: (node: Node) => unknown; - onBeforeNodeAdded?: (node: Node) => false | Node; - onNodeAdded?: (node: Node) => void; - onBeforeElUpdated?: (fromEl: HTMLElement, toEl: HTMLElement) => boolean; - onElUpdated?: (el: HTMLElement) => void; - onBeforeNodeDiscarded?: (node: Node) => boolean; - onNodeDiscarded?: (node: Node) => void; - onBeforeElChildrenUpdated?: ( - fromEl: HTMLElement, - toEl: HTMLElement, - ) => boolean; - skipFromChildren?: (fromEl: HTMLElement) => boolean; - addChild?: (parent: HTMLElement, child: HTMLElement) => void; - childrenOnly?: boolean; -}; - export type RenderArg = { html: HtmlTagFunction; state: State; diff --git a/src/common/element.js b/src/common/element.js index af1f413d..110c6323 100644 --- a/src/common/element.js +++ b/src/common/element.js @@ -4,7 +4,7 @@ import { effect, signal } from "@common/signal.js"; import { define, use } from "@common/worker.js"; /** - * @import {BroadcastingStatus, FnParams, FnReturn, HtmlTagFunction, MorphOptions} from "./element.d.ts" + * @import {BroadcastingStatus, FnParams, FnReturn} from "./element.d.ts" * @import {Signal} from "./signal.d.ts" */ diff --git a/src/components/orchestrator/process-tracks/element.js b/src/components/orchestrator/process-tracks/element.js index 489d7d2e..20791eb7 100644 --- a/src/components/orchestrator/process-tracks/element.js +++ b/src/components/orchestrator/process-tracks/element.js @@ -24,7 +24,7 @@ class ProcessTracksOrchestrator extends DiffuseElement { /** @type {InputElement} */ this.input = query(this, "input-selector"); - /** @type {OutputElement} */ + /** @type {OutputElement} */ this.output = query(this, "output-selector"); /** @type {import("@components/processor/metadata/element.js").CLASS} */ diff --git a/src/components/orchestrator/queue-tracks/element.js b/src/components/orchestrator/queue-tracks/element.js index a71d8d99..0165c6fb 100644 --- a/src/components/orchestrator/queue-tracks/element.js +++ b/src/components/orchestrator/queue-tracks/element.js @@ -22,7 +22,7 @@ class QueueTracksOrchestrator extends DiffuseElement { /** @type {InputElement} */ this.input = query(this, "input-selector"); - /** @type {OutputElement} */ + /** @type {OutputElement} */ this.output = query(this, "output-selector"); /** @type {import("@components/engine/queue/element.js").CLASS} */ diff --git a/src/components/orchestrator/search-tracks/element.js b/src/components/orchestrator/search-tracks/element.js index d0cadfd1..e3bf5ac8 100644 --- a/src/components/orchestrator/search-tracks/element.js +++ b/src/components/orchestrator/search-tracks/element.js @@ -21,7 +21,7 @@ class SearchTracksOrchestrator extends DiffuseElement { /** @type {InputElement} */ this.input = query(this, "input-selector"); - /** @type {OutputElement} */ + /** @type {OutputElement} */ this.output = query(this, "output-selector"); /** @type {import("@components/processor/search/element.js").CLASS} */ diff --git a/src/components/output/types.d.ts b/src/components/output/types.d.ts index 6c1258c1..bf236f6f 100644 --- a/src/components/output/types.d.ts +++ b/src/components/output/types.d.ts @@ -1,13 +1,6 @@ import type { SignalReader } from "@common/signal.d.ts"; -import type { Track } from "@common/types.d.ts"; -// TODO: Do we need this? -// -// export interface Output { -// tracks: Track[]; -// } - -export type OutputElement = HTMLElement & OutputManager; +export type OutputElement = HTMLElement & OutputManager; export type OutputManager = { tracks: { diff --git a/src/components/transformer/output/string/json/element.js b/src/components/transformer/output/string/json/element.js new file mode 100644 index 00000000..e49dd58a --- /dev/null +++ b/src/components/transformer/output/string/json/element.js @@ -0,0 +1,61 @@ +import { DiffuseElement, query } from "@common/element.js"; +import { computed } from "@common/signal.js"; + +/** + * @import { OutputElement, OutputManager } from "../../../../output/types.d.ts" + * @import { Track } from "@common/types.d.ts" + */ + +class JsonStringOutputTransformer extends DiffuseElement { + constructor() { + super(); + + /** @type {OutputElement} */ + this.output = query(this, "output-selector"); + + /** @type {OutputManager} */ + const manager = { + tracks: { + collection: computed(() => { + const json = this.output.tracks?.collection() || []; + + // In addition to the above, Some polymorphic outputs + // use an empty array as the default return value. + if (Array.isArray(json)) return json; + + // Try parsing JSON + try { + return JSON.parse(json); + } catch (err) { + console.error( + "components/transformer/output/string/json: Failed to parse JSON", + ); + return []; + } + }), + reload: () => this.output.tracks.reload(), + save: async (newTracks) => { + const json = JSON.stringify(newTracks); + + await customElements.whenDefined(this.output.localName); + await this.output.tracks.save(json); + }, + state: () => this.output.tracks.state(), + }, + }; + + // Assign manager properties to class + this.tracks = manager.tracks; + } +} + +export default JsonStringOutputTransformer; + +//////////////////////////////////////////// +// REGISTER +//////////////////////////////////////////// + +export const CLASS = JsonStringOutputTransformer; +export const NAME = "dtos-json"; + +customElements.define(NAME, CLASS); diff --git a/src/definitions/index.ts b/src/definitions/index.ts index d2b5362f..483e60a3 100644 --- a/src/definitions/index.ts +++ b/src/definitions/index.ts @@ -1 +1 @@ -export * as ShDiffuseOutputTracks from "./types/sh/diffuse/output/tracks.js"; +export * as ShDiffuseOutputTracks from "./types/sh/diffuse/output/tracks.ts"; diff --git a/src/index.vto b/src/index.vto index 2cebb40c..62b0f9db 100644 --- a/src/index.vto +++ b/src/index.vto @@ -80,8 +80,7 @@ supplements: todo: true transformers: - - url: "components/transformer/output/bytes/cambria/element.js" - title: "Output / Bytes / Cambria lenses" + - title: "Output / Bytes / Cambria lenses" desc: "Uses the Cambria library to seamlessly translate between data schemas so that no data migration is needed." todo: true - title: "Output / Bytes / JSON" @@ -89,7 +88,7 @@ transformers: todo: true - title: "Output / String / JSON" desc: "Raw data schema output ⇄ JSON UTF8 string." - todo: true + url: "components/transformer/output/string/json/element.js" # DEFINITIONS @@ -105,7 +104,7 @@ definitions: todo: true - title: "Output / Tracks" desc: "Represents audio that can be played, or a placeholder for a source of tracks. Contains a URI that will resolve to the audio. This object may be cached if convenient." - todo: true + url: "definitions/output/tracks.json" --- @@ -233,7 +232,7 @@ definitions:

All of the elements here are built with these data definitions in mind. That said, you can mix elements that use different definitions; you just have to put a transformer between them in order to translate between them.

- {{ await comp.list({ items: schemas }) }} + {{ await comp.list({ items: definitions }) }} diff --git a/src/themes/webamp/browser/element.js b/src/themes/webamp/browser/element.js index 2132006c..7213bd43 100644 --- a/src/themes/webamp/browser/element.js +++ b/src/themes/webamp/browser/element.js @@ -16,7 +16,7 @@ class Browser extends DiffuseElement { /** @type {InputElement} */ this.input = query(this, "input-selector"); - /** @type {OutputElement} */ + /** @type {OutputElement} */ this.output = query(this, "output-selector"); /** @type {import("@components/engine/queue/element.js").CLASS} */ diff --git a/src/themes/webamp/index.js b/src/themes/webamp/index.js index 9029c337..43a82b20 100644 --- a/src/themes/webamp/index.js +++ b/src/themes/webamp/index.js @@ -2,6 +2,7 @@ import "@components/orchestrator/process-tracks/element.js"; import "@components/orchestrator/queue-tracks/element.js"; import "@components/output/polymorphic/indexed-db/element.js"; import "@components/processor/metadata/element.js"; +import "@components/transformer/output/string/json/element.js"; import * as Input from "@components/input/opensubsonic/element.js"; import * as Queue from "@components/engine/queue/element.js"; diff --git a/src/themes/webamp/index.vto b/src/themes/webamp/index.vto index a0b5aca6..4c5cabb1 100644 --- a/src/themes/webamp/index.vto +++ b/src/themes/webamp/index.vto @@ -36,7 +36,7 @@ Browse collection @@ -75,23 +75,26 @@ COMPONENTS --> - + - + + + + -- 2.51.2