diff --git a/src/facets/connect/atproto/index.html b/src/facets/connect/atproto/index.html index 73a40a7a..5af1bed6 100644 --- a/src/facets/connect/atproto/index.html +++ b/src/facets/connect/atproto/index.html @@ -1,10 +1,12 @@ -
+
diff --git a/src/facets/connect/atproto/index.inline.js b/src/facets/connect/atproto/index.inline.js index a186e820..11c82249 100644 --- a/src/facets/connect/atproto/index.inline.js +++ b/src/facets/connect/atproto/index.inline.js @@ -1,6 +1,3 @@ -import "@awesome.me/webawesome/dist/components/callout/callout.js"; -import "@awesome.me/webawesome/dist/components/input/input.js"; - import { html, nothing, render as litRender } from "lit-html"; import { NAME as ATPROTO_NAME } from "~/components/output/raw/atproto/element.js"; @@ -13,7 +10,6 @@ import { setup } from "~/facets/connect/common.js"; foundation.setup({ title: "Connect AT Protocol | Diffuse" }); /** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" * @import { ATProtoOutputElement } from "~/components/output/raw/atproto/types.d.ts" * @import TrackUriPasskeyTransformer from "~/components/transformer/output/refiner/track-uri-passkey/element.js" */ @@ -23,22 +19,39 @@ foundation.setup({ title: "Connect AT Protocol | Diffuse" }); //////////////////////////////////////////// const outputOrchestrator = await foundation.orchestrator.output(); + await customElements.whenDefined(outputOrchestrator.localName); +await customElements.whenDefined(ATPROTO_NAME); + +// The AT Protocol option is added dynamically by the output-bundle facet, which +// runs in a separate script and appends the element to the output configurator +// via a signal effect. That effect may not have fired yet when this facet loads, +// so we observe the configurator for child-list mutations and resolve as soon as +// the option appears. +const outputConfigurator = outputOrchestrator.outputConfigurator; + +const atprotoOption = await new Promise((resolve) => { + const check = async () => { + const opt = (await outputOrchestrator.options()).find( + (o) => o.label === "AT Protocol", + ); + if (opt) { + observer.disconnect(); + resolve(opt); + } + }; + + const observer = new MutationObserver(check); + observer.observe(outputConfigurator, { childList: true }); + check(); +}); -const atprotoOption = (await outputOrchestrator.options()).find( - (o) => o.label === "AT Protocol", -); +const ATPROTO_OUTPUT_ID = atprotoOption.id; const atprotoEl = /** @type {ATProtoOutputElement | undefined} */ ( outputOrchestrator.root().querySelector(ATPROTO_NAME) ); -if (!atprotoOption) { - throw new Error("AT Protocol output was not enabled!"); -} - -const ATPROTO_OUTPUT_ID = atprotoOption.id; - const atprotoPasskeyEl = /** @type {TrackUriPasskeyTransformer | null} */ ( outputOrchestrator.root().querySelector( `${PASSKEY_NAME}[namespace="atproto"]`, @@ -64,19 +77,13 @@ const { setItems } = setup({

Connect to your AT Protocol identity to use it as user-data storage.

-

+

Your data is stored as lexicon records in your personal data server (PDS).

`, formFields: html` - -

* Required fields

+ `, onSubmit: (_mode) => connect(), @@ -87,7 +94,7 @@ const { setItems } = setup({ }); const handleInput = - /** @type {WaInput} */ (document.querySelector("#atproto-handle")); + /** @type {HTMLInputElement} */ (document.querySelector("#atproto-handle")); //////////////////////////////////////////// // REACTIVE LIST @@ -120,8 +127,7 @@ effect(() => { if (atprotoPasskeyEl) { const passkeyRoot = document.createElement("div"); - passkeyRoot.classList.add("wa-stack"); - document.querySelector("main .card-body")?.appendChild(passkeyRoot); + document.querySelector("main .facet__right")?.appendChild(passkeyRoot); effect(() => { const passkeyActive = atprotoPasskeyEl.passkeyActive() ?? false; @@ -131,7 +137,7 @@ if (atprotoPasskeyEl) { litRender( html` - +
Passkey encryption (optional) @@ -142,61 +148,44 @@ if (atprotoPasskeyEl) {

Passkey active — Track URIs are encrypted.

${passkeyError - ? html` - ${passkeyError} - ` + ? html`
${passkeyError}
` : nothing}
- Remove passkey +
-

+

Removing the passkey will expose all the sensitive information that was previously encrypted.

` : html` -

+

Track URIs can optionally be encrypted so that passwords and other sensitive authentication details are kept private. Note that, with this enabled, other people cannot play audio listed on your account.

${passkeyError - ? html` - ${passkeyError} - ` + ? html`
${passkeyError}
` : nothing}
- ${passkeyWorking - ? "Setting up …" - : "Set up passkey encryption"} - ${passkeyWorking - ? "Authenticating …" - : "Use existing passkey"} + +
- `} ${lockedTracksCount > 0 + `} + ${lockedTracksCount > 0 ? html` - +
${lockedTracksCount} encrypted track(s) cannot be played until you unlock them with your passkey. - +
` : nothing} `, diff --git a/src/facets/connect/common.css b/src/facets/connect/common.css index 54a52250..1c7ca20a 100644 --- a/src/facets/connect/common.css +++ b/src/facets/connect/common.css @@ -1,116 +1,70 @@ -main { - display: flex; - align-items: center; - justify-content: center; - min-height: 100dvh; - padding: var(--wa-space-m); -} - -wa-card { - width: min(400px, calc(100vw - 2rem)); -} - -.card-header { - display: flex; - align-items: center; - justify-content: space-between; -} +@import "../../styles/diffuse/facet.css"; -.card-body { - display: flex; - flex-direction: column; - gap: var(--wa-space-m); -} - -.button-row { - display: flex; - gap: var(--wa-space-s); - flex-wrap: wrap; -} - -.dialog-body { +.connect-list { display: flex; flex-direction: column; - gap: var(--wa-space-m); -} - -.dialog-footer { - display: flex; - gap: var(--wa-space-s); -} - -.connect-list { + gap: var(--space-xs); list-style: none; - padding: 0; margin: 0; - display: flex; - flex-direction: column; - gap: var(--wa-space-s); + padding: 0; } .connect-item { - display: flex; align-items: center; - gap: var(--wa-space-s); + display: flex; + gap: var(--space-xs); margin-left: 0; } .connect-item__info { display: flex; flex-direction: column; - gap: var(--wa-space-2xs); flex: 1; + gap: var(--space-3xs); min-width: 0; } .connect-item__name { - font-weight: var(--wa-font-weight-semibold); - white-space: nowrap; + font-weight: 600; overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } .connect-item__detail { - font-size: var(--wa-font-size-xs); - color: var(--wa-color-text-quiet); - white-space: nowrap; + color: oklch(from var(--text-color) l c h / 0.55); + font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } .connect-item__tags { display: flex; - gap: var(--wa-space-2xs); flex-shrink: 0; + gap: var(--space-3xs); } .dropzone { align-items: center; - border: 2px dashed var(--wa-color-neutral-border-quiet); - border-radius: var(--wa-border-radius-m); - color: var(--wa-color-text-quiet); + border: 2px dashed var(--border-color); + border-radius: var(--radius-md); + color: oklch(from var(--text-color) l c h / 0.55); + cursor: pointer; display: flex; flex-direction: column; - gap: var(--wa-space-xs); - font-size: var(--wa-font-size-s); + font-size: var(--fs-sm); + gap: var(--space-2xs); justify-content: center; - padding: var(--wa-space-l); + padding: var(--space-md); transition: background-color 150ms, border-color 150ms, color 150ms; &.dropzone--active { - background-color: var(--wa-color-surface-sunken); - border-color: var(--wa-color-brand-500); - color: var(--wa-color-text-normal); + background-color: var(--form-color); + border-color: var(--accent); + color: var(--text-color); } } - -[hidden] { - display: none !important; -} - -p { - margin: 0; -} diff --git a/src/facets/connect/common.js b/src/facets/connect/common.js index 12ee180a..c986d3c8 100644 --- a/src/facets/connect/common.js +++ b/src/facets/connect/common.js @@ -1,19 +1,6 @@ -import "@awesome.me/webawesome/dist/components/badge/badge.js"; -import "@awesome.me/webawesome/dist/components/button/button.js"; -import "@awesome.me/webawesome/dist/components/callout/callout.js"; -import "@awesome.me/webawesome/dist/components/card/card.js"; -import "@awesome.me/webawesome/dist/components/dialog/dialog.js"; -import "@awesome.me/webawesome/dist/components/divider/divider.js"; -import "@awesome.me/webawesome/dist/components/icon/icon.js"; - -import "~/common/webawesome/detect-dark.js"; -import "~/common/webawesome/phosphor/bold.js"; -import "~/common/webawesome/phosphor/fill.js"; - import { html, nothing, render as litRender } from "lit-html"; /** - * @import { default as WaDialog } from "@awesome.me/webawesome/dist/components/dialog/dialog.js" * @import { TemplateResult } from "lit-html" */ @@ -28,7 +15,8 @@ import { html, nothing, render as litRender } from "lit-html"; * * @param {Object} config * @param {string} config.title - Card header title - * @param {TemplateResult | string} config.description - Content above the buttons + * @param {TemplateResult | string} config.description - Content shown on the left side + * @param {TemplateResult} [config.rightContent] - Extra content shown at the top of the right side * @param {TemplateResult} config.formFields - Form body content (inputs, footnotes, etc.) * @param {(mode: 'input' | 'output') => Promise} config.onSubmit * @param {boolean} [config.hasInput] - Whether to show the "Add audio input" button (default: true) @@ -41,6 +29,7 @@ export function setup( { title, description, + rightContent = nothing, formFields, onSubmit, hasInput = true, @@ -53,64 +42,67 @@ export function setup( litRender( html` - -
- ${title} +
+ -
- ${description} -
- ${hasInput - ? html` - - - Add audio input - - ` - : nothing} - ${hasOutput - ? html` - - - Use as userdata storage - - ` - : nothing} -
- - - +

${title}

+ ${description} +
+
+ ${rightContent} +
+ ${hasInput + ? html` + + ` + : nothing} + ${hasOutput + ? html` + + ` + : nothing} +
+ + + +
+ + +
+
- - -
${formFields} - +
-
`, main, ); const dialog = - /** @type {WaDialog} */ (main.querySelector("#connect-dialog")); + /** @type {HTMLDialogElement} */ (main.querySelector("#connect-dialog")); + const dialogTitleEl = + /** @type {HTMLElement} */ (main.querySelector("#connect-dialog-title")); const form = /** @type {HTMLFormElement} */ (main.querySelector("#connect-form")); const dialogErrorEl = @@ -144,12 +136,12 @@ export function setup( /** @param {'input' | 'output'} m */ const openDialog = (m) => { mode = m; - dialog.label = m === "input" + dialogTitleEl.textContent = m === "input" ? "Add audio input" : "Use as userdata storage"; form.reset(); setDialogError(null); - dialog.open = true; + dialog.showModal(); }; if (hasInput) { @@ -170,7 +162,7 @@ export function setup( main.querySelector("#connect-cancel-btn")?.addEventListener("click", () => { setDialogError(null); - dialog.open = false; + dialog.close(); }); const submitBtn = @@ -183,7 +175,7 @@ export function setup( submitBtn.textContent = "Loading …"; try { await onSubmit(mode); - dialog.open = false; + dialog.close(); } catch (err) { setDialogError( err instanceof Error ? err.message : "Something went wrong", @@ -220,25 +212,19 @@ export function setup(
${isInput - ? html` - Input - ` - : nothing} ${isOutput - ? html` - Output - ` + ? html`Input` + : nothing} + ${isOutput + ? html`Output` : nothing}
- - - + + `, )} diff --git a/src/facets/connect/https/index.html b/src/facets/connect/https/index.html index 560cd9e1..9bb4b724 100644 --- a/src/facets/connect/https/index.html +++ b/src/facets/connect/https/index.html @@ -1,10 +1,12 @@ -
+
diff --git a/src/facets/connect/https/index.inline.js b/src/facets/connect/https/index.inline.js index 8515619e..1f778aad 100644 --- a/src/facets/connect/https/index.inline.js +++ b/src/facets/connect/https/index.inline.js @@ -1,5 +1,3 @@ -import "@awesome.me/webawesome/dist/components/input/input.js"; - import * as TID from "@atcute/tid"; import { html } from "lit-html"; @@ -11,10 +9,6 @@ import foundation from "~/common/foundation.js"; import { setup } from "~/facets/connect/common.js"; -/** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" - */ - foundation.setup({ title: "Connect HTTPS | Diffuse" }); //////////////////////////////////////////// @@ -47,19 +41,13 @@ const { setItems, setError } = setup({ `, formFields: html` - + `, onSubmit: () => addUrl(), }); -const urlInput = /** @type {WaInput} */ (document.querySelector("#https-url")); +const urlInput = /** @type {HTMLInputElement} */ (document.querySelector("#https-url")); //////////////////////////////////////////// // REACTIVE LIST diff --git a/src/facets/connect/icecast/index.html b/src/facets/connect/icecast/index.html index 6d78971d..cae96406 100644 --- a/src/facets/connect/icecast/index.html +++ b/src/facets/connect/icecast/index.html @@ -1,10 +1,12 @@ -
+
diff --git a/src/facets/connect/icecast/index.inline.js b/src/facets/connect/icecast/index.inline.js index b92b688b..cda31002 100644 --- a/src/facets/connect/icecast/index.inline.js +++ b/src/facets/connect/icecast/index.inline.js @@ -1,5 +1,3 @@ -import "@awesome.me/webawesome/dist/components/input/input.js"; - import * as TID from "@atcute/tid"; import { html } from "lit-html"; @@ -11,10 +9,6 @@ import foundation from "~/common/foundation.js"; import { setup } from "~/facets/connect/common.js"; -/** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" - */ - foundation.setup({ title: "Connect Icecast | Diffuse" }); //////////////////////////////////////////// @@ -47,20 +41,14 @@ const { setItems, setError } = setup({ `, formFields: html` - + `, onSubmit: () => addStream(), }); const urlInput = - /** @type {WaInput} */ (document.querySelector("#icecast-url")); + /** @type {HTMLInputElement} */ (document.querySelector("#icecast-url")); //////////////////////////////////////////// // REACTIVE LIST diff --git a/src/facets/connect/index.inline.js b/src/facets/connect/index.inline.js index 2bd91e6d..27fa7a1f 100644 --- a/src/facets/connect/index.inline.js +++ b/src/facets/connect/index.inline.js @@ -37,16 +37,18 @@ if (!main) throw new Error("No
element"); litRender( html`
- - - Diffuse - - - - +

Connect

These are some of the options available to add as an audio source, or to use diff --git a/src/facets/connect/local/index.html b/src/facets/connect/local/index.html index 81ffbc2e..ffe23f7b 100644 --- a/src/facets/connect/local/index.html +++ b/src/facets/connect/local/index.html @@ -1,10 +1,12 @@ -

+
diff --git a/src/facets/connect/local/index.inline.js b/src/facets/connect/local/index.inline.js index 831a7acc..b70b0657 100644 --- a/src/facets/connect/local/index.inline.js +++ b/src/facets/connect/local/index.inline.js @@ -49,48 +49,46 @@ const { setItems, setError } = setup({ description: html`

Add local directories or files as audio input.

+ `, + rightContent: html` - + ${supported ? html` - +
- - + +
` : nothing} `, - formFields: html` - - `, + formFields: html``, onSubmit: async () => {}, }); @@ -113,7 +111,9 @@ const dropzoneInput = )); dropzoneInput?.addEventListener("change", async () => { - const files = Array.from(dropzoneInput.files ?? []); + const files = Array.from(dropzoneInput.files ?? []).filter((f) => + f.type.startsWith("audio/") + ); dropzoneInput.value = ""; if (files.length === 0) return; await cacheFiles(files); @@ -323,7 +323,7 @@ async function collectFiles(items) { files.push(...dirFiles); } else { const file = item.getAsFile(); - if (file) files.push(file); + if (file?.type.startsWith("audio/")) files.push(file); } }), ); @@ -362,7 +362,7 @@ async function readDirectoryEntry(dir) { (res, rej) => /** @type {FileSystemFileEntry} */ (entry).file(res, rej), ); - files.push(file); + if (file.type.startsWith("audio/")) files.push(file); } }), ); diff --git a/src/facets/connect/opensubsonic/index.html b/src/facets/connect/opensubsonic/index.html index d7a6f669..ea092430 100644 --- a/src/facets/connect/opensubsonic/index.html +++ b/src/facets/connect/opensubsonic/index.html @@ -1,10 +1,12 @@ -
+
diff --git a/src/facets/connect/opensubsonic/index.inline.js b/src/facets/connect/opensubsonic/index.inline.js index daa51a9c..b6113258 100644 --- a/src/facets/connect/opensubsonic/index.inline.js +++ b/src/facets/connect/opensubsonic/index.inline.js @@ -1,7 +1,3 @@ -import "@awesome.me/webawesome/dist/components/input/input.js"; -import "@awesome.me/webawesome/dist/components/select/select.js"; -import "@awesome.me/webawesome/dist/components/option/option.js"; - import * as TID from "@atcute/tid"; import { html } from "lit-html"; @@ -18,8 +14,6 @@ import foundation from "~/common/foundation.js"; import { setup } from "~/facets/connect/common.js"; /** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" - * @import { default as WaSelect } from "@awesome.me/webawesome/dist/components/select/select.js" * @import { Server } from "~/components/input/opensubsonic/types.d.ts" */ @@ -54,40 +48,37 @@ const { setItems, setError } = setup({

Connect to an OpenSubsonic server to use it as audio input.

-

Supports authentication via username + password or an API key.

`, formFields: html` - - - Yes - No - - - - - - + + + + +

Or use an API key instead of username + password:

+ +

* Host is required

`, onSubmit: () => addServer(), }); -const hostInput = /** @type {WaInput} */ (document.querySelector("#oss-host")); -const tlsSelect = /** @type {WaSelect} */ (document.querySelector("#oss-tls")); +const hostInput = /** @type {HTMLInputElement} */ (document.querySelector("#oss-host")); +const tlsSelect = /** @type {HTMLSelectElement} */ (document.querySelector("#oss-tls")); const usernameInput = - /** @type {WaInput} */ (document.querySelector("#oss-username")); + /** @type {HTMLInputElement} */ (document.querySelector("#oss-username")); const passwordInput = - /** @type {WaInput} */ (document.querySelector("#oss-password")); + /** @type {HTMLInputElement} */ (document.querySelector("#oss-password")); const apikeyInput = - /** @type {WaInput} */ (document.querySelector("#oss-apikey")); + /** @type {HTMLInputElement} */ (document.querySelector("#oss-apikey")); //////////////////////////////////////////// // REACTIVE LIST diff --git a/src/facets/connect/s3/index.html b/src/facets/connect/s3/index.html index 936da497..5a3f9230 100644 --- a/src/facets/connect/s3/index.html +++ b/src/facets/connect/s3/index.html @@ -1,10 +1,12 @@ -
+
diff --git a/src/facets/connect/s3/index.inline.js b/src/facets/connect/s3/index.inline.js index d395533d..4124a4f1 100644 --- a/src/facets/connect/s3/index.inline.js +++ b/src/facets/connect/s3/index.inline.js @@ -1,5 +1,3 @@ -import "@awesome.me/webawesome/dist/components/input/input.js"; - import * as TID from "@atcute/tid"; import { html } from "lit-html"; @@ -15,7 +13,6 @@ import { setup } from "~/facets/connect/common.js"; foundation.setup({ title: "Connect S3 | Diffuse" }); /** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" * @import { Bucket } from "~/components/input/s3/types.d.ts" * @import { S3OutputElement } from "~/components/output/bytes/s3/types.d.ts" */ @@ -63,30 +60,20 @@ const { setItems, setError } = setup({ Connect to an S3-compatible storage to use it as audio input or user-data storage.

-

A custom syncing strategy is used for the user-data storage, tracking what was added and removed so conflicts can be resolved.

`, formFields: html` - - - - - - - + + + + + + +

* Required fields

`, onSubmit: (mode) => addBucket(mode), @@ -97,15 +84,15 @@ const { setItems, setError } = setup({ }); const accessKeyInput = - /** @type {WaInput} */ (document.querySelector("#s3-access-key")); + /** @type {HTMLInputElement} */ (document.querySelector("#s3-access-key")); const secretKeyInput = - /** @type {WaInput} */ (document.querySelector("#s3-secret-key")); + /** @type {HTMLInputElement} */ (document.querySelector("#s3-secret-key")); const bucketNameInput = - /** @type {WaInput} */ (document.querySelector("#s3-bucket-name")); -const hostInput = /** @type {WaInput} */ (document.querySelector("#s3-host")); + /** @type {HTMLInputElement} */ (document.querySelector("#s3-bucket-name")); +const hostInput = /** @type {HTMLInputElement} */ (document.querySelector("#s3-host")); const regionInput = - /** @type {WaInput} */ (document.querySelector("#s3-region")); -const pathInput = /** @type {WaInput} */ (document.querySelector("#s3-path")); + /** @type {HTMLInputElement} */ (document.querySelector("#s3-region")); +const pathInput = /** @type {HTMLInputElement} */ (document.querySelector("#s3-path")); //////////////////////////////////////////// // REACTIVE LIST diff --git a/src/facets/misc/scrobble/last.fm/index.html b/src/facets/misc/scrobble/last.fm/index.html index 2d7c2900..60a56d50 100644 --- a/src/facets/misc/scrobble/last.fm/index.html +++ b/src/facets/misc/scrobble/last.fm/index.html @@ -1,96 +1,69 @@ -
- -
- Last.fm - +
+
+ +

Last.fm

+

Connect your Last.fm account to scrobble tracks.

+
-
-

Connect your Last.fm account to start scrobbling.

- - - Connect - +
+
+
+ +
- - -
- - + +
+ API Credentials +
+
+ +
-
diff --git a/src/facets/misc/scrobble/last.fm/index.inline.js b/src/facets/misc/scrobble/last.fm/index.inline.js index 38e7c9a1..75572b49 100644 --- a/src/facets/misc/scrobble/last.fm/index.inline.js +++ b/src/facets/misc/scrobble/last.fm/index.inline.js @@ -1,23 +1,9 @@ -import "@awesome.me/webawesome/dist/components/card/card.js"; -import "@awesome.me/webawesome/dist/components/button/button.js"; -import "@awesome.me/webawesome/dist/components/drawer/drawer.js"; -import "@awesome.me/webawesome/dist/components/input/input.js"; -import "@awesome.me/webawesome/dist/components/icon/icon.js"; - -import "~/common/webawesome/detect-dark.js"; -import "~/common/webawesome/phosphor/bold.js"; - import foundation from "~/common/foundation.js"; import { effect } from "~/common/signal.js"; // Set doc title foundation.setup({ title: "Last.fm | Scrobble | Diffuse" }); -/** - * @import { default as WaDrawer } from "@awesome.me/webawesome/dist/components/drawer/drawer.js" - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" - */ - //////////////////////////////////////////// // SETUP //////////////////////////////////////////// @@ -47,12 +33,6 @@ if (!lastFm) { configurator.append(lastFm); } -// const creds = loadCredentials(); -// if (creds) { -// lastFm.setAttribute("api-key", creds.apiKey); -// lastFm.setAttribute("api-secret", creds.apiSecret); -// } - await customElements.whenDefined(lastFm.localName); //////////////////////////////////////////// @@ -87,15 +67,15 @@ const signOutBtn = /** @type {HTMLElement} */ ( document.querySelector("#sign-out-btn") ); -const credentialsDrawer = /** @type {WaDrawer} */ ( - document.querySelector("#credentials-drawer") +const credentialsDialog = /** @type {HTMLDialogElement} */ ( + document.querySelector("#credentials-dialog") ); -const apiKeyInput = /** @type {WaInput} */ ( +const apiKeyInput = /** @type {HTMLInputElement} */ ( document.querySelector("#api-key-input") ); -const apiSecretInput = /** @type {WaInput} */ ( +const apiSecretInput = /** @type {HTMLInputElement} */ ( document.querySelector("#api-secret-input") ); @@ -107,7 +87,7 @@ const resetCredsBtn = /** @type {HTMLElement} */ ( document.querySelector("#reset-creds-btn") ); -// Pre-fill drawer inputs with stored credentials +// Pre-fill dialog inputs with stored credentials const existingCreds = loadCredentials(); if (existingCreds) { apiKeyInput.value = existingCreds.apiKey; @@ -140,7 +120,7 @@ effect(() => { settingsBtn?.addEventListener("click", (e) => { e.stopPropagation(); - credentialsDrawer.open = true; + credentialsDialog.showModal(); }); signInBtn.onclick = () => lastFm.signIn(); diff --git a/src/facets/misc/scrobble/listenbrainz/index.html b/src/facets/misc/scrobble/listenbrainz/index.html index 29cd9f7b..82156f94 100644 --- a/src/facets/misc/scrobble/listenbrainz/index.html +++ b/src/facets/misc/scrobble/listenbrainz/index.html @@ -1,69 +1,60 @@ -
- -
- ListenBrainz +
+
+ - -
-

Enter your ListenBrainz user token to start scrobbling.

- - - - Connect - +

ListenBrainz

+

Connect your ListenBrainz account to scrobble tracks.

+
+ +
+
+ +
+ +
-
diff --git a/src/facets/misc/scrobble/listenbrainz/index.inline.js b/src/facets/misc/scrobble/listenbrainz/index.inline.js index fed08ef6..6a78794c 100644 --- a/src/facets/misc/scrobble/listenbrainz/index.inline.js +++ b/src/facets/misc/scrobble/listenbrainz/index.inline.js @@ -1,18 +1,6 @@ -import "@awesome.me/webawesome/dist/components/card/card.js"; -import "@awesome.me/webawesome/dist/components/button/button.js"; -import "@awesome.me/webawesome/dist/components/input/input.js"; -import "@awesome.me/webawesome/dist/components/icon/icon.js"; - -import "~/common/webawesome/detect-dark.js"; -import "~/common/webawesome/phosphor/bold.js"; - import foundation from "~/common/foundation.js"; import { effect } from "~/common/signal.js"; -/** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" - */ - //////////////////////////////////////////// // SETUP //////////////////////////////////////////// @@ -55,7 +43,7 @@ const handleText = /** @type {HTMLElement} */ ( document.querySelector("#handle-text") ); -const tokenInput = /** @type {WaInput} */ ( +const tokenInput = /** @type {HTMLInputElement} */ ( document.querySelector("#token-input") ); @@ -85,7 +73,6 @@ effect(() => { // @ts-ignore signInBtn.disabled = isAuthenticating; - // @ts-ignore tokenInput.disabled = isAuthenticating; }); diff --git a/src/facets/misc/scrobble/rocksky/index.html b/src/facets/misc/scrobble/rocksky/index.html index 8991d95b..b635e424 100644 --- a/src/facets/misc/scrobble/rocksky/index.html +++ b/src/facets/misc/scrobble/rocksky/index.html @@ -1,74 +1,69 @@ -
- -
- Rocksky +
+
+ +

Rocksky

+

Scrobble tracks to Rocksky using your AT Protocol identity.

+
-
-
+
+
+

Sign in with your AT Protocol identity first, then connect to Rocksky.

- - - - Sign in with AT Protocol - + +

+ +

- -
diff --git a/src/facets/misc/scrobble/rocksky/index.inline.js b/src/facets/misc/scrobble/rocksky/index.inline.js index d9374bb5..3c1f8333 100644 --- a/src/facets/misc/scrobble/rocksky/index.inline.js +++ b/src/facets/misc/scrobble/rocksky/index.inline.js @@ -1,21 +1,9 @@ -import "@awesome.me/webawesome/dist/components/card/card.js"; -import "@awesome.me/webawesome/dist/components/button/button.js"; -import "@awesome.me/webawesome/dist/components/input/input.js"; -import "@awesome.me/webawesome/dist/components/icon/icon.js"; - -import "~/common/webawesome/detect-dark.js"; -import "~/common/webawesome/phosphor/bold.js"; - import { login } from "~/components/output/raw/atproto/oauth.js"; import { finalizeAuthorization } from "@atcute/oauth-browser-client"; import foundation from "~/common/foundation.js"; import { effect, signal } from "~/common/signal.js"; -/** - * @import { default as WaInput } from "@awesome.me/webawesome/dist/components/input/input.js" - */ - //////////////////////////////////////////// // SETUP //////////////////////////////////////////// @@ -92,7 +80,7 @@ const handleText = /** @type {HTMLElement} */ ( document.querySelector("#handle-text") ); -const handleInput = /** @type {WaInput} */ ( +const handleInput = /** @type {HTMLInputElement} */ ( document.querySelector("#handle-input") ); diff --git a/src/styles/diffuse/facet.css b/src/styles/diffuse/facet.css new file mode 100644 index 00000000..665a6974 --- /dev/null +++ b/src/styles/diffuse/facet.css @@ -0,0 +1,255 @@ +body { + background-color: var(--bg-color); + color: var(--text-color); +} + +main { + padding: var(--space-2xl) var(--space-xl); + + @media (min-width: 48rem) { + align-items: center; + display: flex; + flex-direction: row; + gap: var(--space-2xl); + justify-content: center; + min-height: 100dvh; + } +} + +.facet__left { + container-type: inline-size; + flex: 1; + margin-bottom: var(--space-lg); + max-width: 24rem; + + @media (min-width: 48rem) { + margin-bottom: 0; + } +} + +.facet__right { + max-width: 24rem; +} + +.diffuse-logo-container { + display: inline-block; + margin-bottom: var(--space-lg); + + svg { + display: block; + fill: oklch(from var(--bg-color) calc(l - 0.5) c h); + opacity: 0.2; + width: 5em; + + @media (prefers-color-scheme: dark) { + fill: var(--text-color); + opacity: 0.25; + } + } +} + +h1 { + font-size: var(--fs-2xl); + font-size: min(14cqi, var(--fs-2xl)); + font-weight: 700; + letter-spacing: -0.02em; + line-height: 1.2; + margin: 0 0 var(--space-sm); + overflow: hidden; + white-space: nowrap; +} + +.facet__left p { + color: oklch(from var(--text-color) l c h / 0.55); + font-size: var(--fs-sm); + line-height: var(--leading-relaxed); + max-width: 22rem; +} + +.button-row { + display: flex; + flex-wrap: wrap; + gap: var(--space-2xs); +} + +.callout { + border-radius: var(--radius-md); + font-size: var(--fs-sm); + padding: var(--space-2xs) var(--space-xs); +} + +.callout--danger { + background: oklch(from var(--accent-twist-4) l c h / 0.12); + border: 1px solid oklch(from var(--accent-twist-4) l c h / 0.3); + color: var(--accent-twist-4); +} + +.callout--warning { + background: oklch(from var(--accent-twist-1) l c h / 0.12); + border: 1px solid oklch(from var(--accent-twist-1) l c h / 0.3); + color: var(--accent-twist-1); +} + +.badge { + border: 1px solid currentColor; + border-radius: var(--radius-xs); + font-size: var(--fs-3xs); + letter-spacing: var(--tracking-wider); + line-height: 0.75; + opacity: 0.6; + padding: 0.1em 0.35em; + text-box: trim-both cap alphabetic; + text-transform: uppercase; +} + +.badge--brand { + color: var(--accent); + opacity: 1; +} + +.badge--warning { + color: var(--accent-twist-1); + opacity: 1; +} + +button { + align-items: center; + background: var(--form-color); + border: 1px solid var(--border-color); + border-radius: 9999px; + color: var(--text-color); + cursor: pointer; + display: inline-flex; + font-family: inherit; + font-size: var(--fs-sm); + gap: var(--space-2xs); + padding: var(--space-2xs) var(--space-sm); + + &:hover:not(:disabled) { + opacity: 0.8; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } + + &.button--brand { + background: var(--accent); + border-color: transparent; + color: var(--bg-color); + } + + &.button--outlined { + background: transparent; + } + + &.button--danger { + border-color: var(--accent-twist-4); + color: var(--accent-twist-4); + } + + &.button--plain { + background: transparent; + border-color: transparent; + padding: var(--space-3xs); + } + + &.button--small { + font-size: var(--fs-xs); + padding: var(--space-3xs) var(--space-2xs); + } +} + +label { + display: flex; + flex-direction: column; + font-size: var(--fs-sm); + gap: var(--space-3xs); +} + +input, +select { + background: var(--form-color); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + color: var(--text-color); + font-family: inherit; + font-size: var(--fs-sm); + padding: var(--space-2xs) var(--space-xs); + width: 100%; +} + +dialog { + background: var(--bg-color); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + color: var(--text-color); + left: 50%; + max-height: 85dvh; + max-width: min(400px, calc(100vw - 2rem)); + overflow: hidden; + padding: 0; + position: fixed; + top: 50%; + transform: translate(-50%, -50%); + width: 100%; + + &[open] { + display: flex; + flex-direction: column; + } + + &::backdrop { + background: oklch(0 0 0 / 0.5); + } +} + +.dialog-header { + align-items: center; + border-bottom: 1px solid var(--border-color); + display: flex; + flex-shrink: 0; + justify-content: space-between; + padding: var(--space-xs) var(--space-sm); +} + +.dialog-body { + display: flex; + flex-direction: column; + gap: var(--space-sm); + overflow-y: auto; + padding: var(--space-sm); +} + +.dialog-footer { + border-top: 1px solid var(--border-color); + display: flex; + flex-shrink: 0; + gap: var(--space-2xs); + padding: var(--space-xs) var(--space-sm); +} + +hr { + border: none; + border-top: 1px solid var(--border-color); + margin: var(--space-md) 0; +} + +.caption { + color: oklch(from var(--text-color) l c h / 0.55); + font-size: var(--fs-xs); +} + +[hidden] { + display: none !important; +} + +p, +.callout { + margin: var(--space-sm) 0; +} + +.facet__right p:first-child { + margin-top: 0; +}