chore(deps): Tailwind 4 (#5021)
release / govulncheck (push) Successful in 42s
release / web (push) Successful in 1m39s
release / go (push) Successful in 1m53s
release / integration (push) Successful in 5m37s
release / android (push) Successful in 6m54s
release / Build signed APK (releases and dev) (push) Successful in 7m27s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Failing after 34s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 42s
release / web (push) Successful in 1m39s
release / go (push) Successful in 1m53s
release / integration (push) Successful in 5m37s
release / android (push) Successful in 6m54s
release / Build signed APK (releases and dev) (push) Successful in 7m27s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Failing after 34s
release / Verify release artifacts (tag releases only) (push) Skipped
Renovate's tailwindcss bump (PR #150) plus the migration it needs: - Theme moves from tailwind.config.js into app.css as `@theme inline`, mapping the same FabledSword tokens. tailwind.config.js is gone. - PostCSS runs @tailwindcss/postcss; autoprefixer is dropped, since Tailwind 4 prefixes through Lightning CSS. - Class renames from @tailwindcss/upgrade 4.3.3, reviewed: outline-none -> outline-hidden, focus-visible:outline -> outline-solid, shadow -> shadow-sm, shadow-sm -> shadow-xs, flex-shrink-0 -> shrink-0. Bare `rounded` stays: v4 keeps it at 0.25rem, as before. - Three v3 preflight defaults kept in a base layer so nothing changes on screen: gray-200 default border colour, gray-400 placeholder text and the pointer cursor on buttons. - The unused class-based dark variant is not carried over; no template uses `dark:`. Clears the five high and two moderate npm audit findings that came in through Tailwind 3 (braces, chokidar, micromatch, fast-glob, postcss-selector-parser). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Generated
+606
-983
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -15,15 +15,15 @@
|
|||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
"@sveltejs/kit": "^2.8.0",
|
"@sveltejs/kit": "^2.8.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
|
"@tailwindcss/postcss": "^4.3.3",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/svelte": "^5.3.1",
|
"@testing-library/svelte": "^5.3.1",
|
||||||
"@types/node": "^25.9.1",
|
"@types/node": "^25.9.1",
|
||||||
"autoprefixer": "^10.5.0",
|
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^25.0.1",
|
||||||
"postcss": "^8.5.15",
|
"postcss": "^8.5.15",
|
||||||
"svelte": "^5.1.9",
|
"svelte": "^5.1.9",
|
||||||
"svelte-check": "^4.4.8",
|
"svelte-check": "^4.4.8",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.3.3",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.0",
|
||||||
"vitest": "^5.0.0"
|
"vitest": "^5.0.0"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
'@tailwindcss/postcss': {},
|
||||||
autoprefixer: {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+71
-4
@@ -1,8 +1,75 @@
|
|||||||
@import './lib/styles/tokens.generated.css';
|
@import './lib/styles/tokens.generated.css' layer(base);
|
||||||
|
|
||||||
@tailwind base;
|
@import 'tailwindcss';
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
/* The FabledSword tokens as Tailwind's theme. `inline` makes each utility emit
|
||||||
|
* the var(--fs-*) itself, so a theme swap that redefines the tokens reaches the
|
||||||
|
* utilities without going through a second variable. */
|
||||||
|
@theme inline {
|
||||||
|
--color-background: var(--fs-obsidian);
|
||||||
|
|
||||||
|
--color-surface: var(--fs-iron);
|
||||||
|
--color-surface-hover: var(--fs-slate);
|
||||||
|
|
||||||
|
--color-border: var(--fs-pewter);
|
||||||
|
|
||||||
|
--color-text-primary: var(--fs-parchment);
|
||||||
|
--color-text-secondary: var(--fs-vellum);
|
||||||
|
--color-text-muted: var(--fs-ash);
|
||||||
|
|
||||||
|
--color-action-primary: var(--fs-moss);
|
||||||
|
--color-action-secondary: var(--fs-bronze);
|
||||||
|
--color-action-destructive: var(--fs-oxblood);
|
||||||
|
--color-action-fg: var(--fs-on-action);
|
||||||
|
|
||||||
|
--color-accent: var(--fs-accent);
|
||||||
|
--color-accent-tint: color-mix(in srgb, var(--fs-accent) 12%, transparent);
|
||||||
|
--color-accent-fg: var(--fs-accent-fg);
|
||||||
|
|
||||||
|
--color-warning: var(--fs-warning);
|
||||||
|
--color-warning-fg: var(--fs-warning-fg);
|
||||||
|
|
||||||
|
--color-error: var(--fs-error);
|
||||||
|
--color-error-fg: var(--fs-error-fg);
|
||||||
|
|
||||||
|
--color-info: var(--fs-info);
|
||||||
|
--color-info-fg: var(--fs-info-fg);
|
||||||
|
|
||||||
|
--radius-sm: var(--fs-radius-sm);
|
||||||
|
--radius-md: var(--fs-radius-md);
|
||||||
|
--radius-lg: var(--fs-radius-lg);
|
||||||
|
--radius-xl: var(--fs-radius-xl);
|
||||||
|
|
||||||
|
--font-display: var(--fs-font-display);
|
||||||
|
--font-sans: var(--fs-font-body);
|
||||||
|
--font-mono: var(--fs-font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tailwind 3's preflight defaults that 4 changed (#5021). Kept so the upgrade
|
||||||
|
* changes nothing on screen; each can go once the elements relying on it name
|
||||||
|
* the value themselves.
|
||||||
|
* - A border with no colour utility was gray-200; 4 makes it currentcolor.
|
||||||
|
* - Placeholder text was gray-400; 4 makes it the text colour at 50%.
|
||||||
|
* - Buttons had a pointer cursor; 4 leaves the browser default. */
|
||||||
|
@layer base {
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before,
|
||||||
|
::backdrop,
|
||||||
|
::file-selector-button {
|
||||||
|
border-color: var(--color-gray-200, currentcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: var(--color-gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled),
|
||||||
|
[role='button']:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
const rowClass = 'flex items-center justify-between gap-4 py-2';
|
const rowClass = 'flex items-center justify-between gap-4 py-2';
|
||||||
const inputClass =
|
const inputClass =
|
||||||
'rounded-md border border-border bg-background px-3 py-1.5 text-sm text-text-primary ' +
|
'rounded-md border border-border bg-background px-3 py-1.5 text-sm text-text-primary ' +
|
||||||
'placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent';
|
'placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="space-y-4 rounded-xl border border-border bg-surface p-5" data-testid="acoustid-card">
|
<section class="space-y-4 rounded-xl border border-border bg-surface p-5" data-testid="acoustid-card">
|
||||||
@@ -238,7 +238,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent
|
||||||
disabled:cursor-not-allowed disabled:opacity-50"
|
disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!dirty || saving || !scoreOk}
|
disabled={!dirty || saving || !scoreOk}
|
||||||
onclick={save}
|
onclick={save}
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-md border border-border px-4 py-2 text-sm text-text-primary hover:bg-background
|
class="rounded-md border border-border px-4 py-2 text-sm text-text-primary hover:bg-background
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent
|
||||||
disabled:cursor-not-allowed disabled:opacity-50"
|
disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!overview.ready || overview.status.running || starting}
|
disabled={!overview.ready || overview.status.running || starting}
|
||||||
title={overview.ready ? undefined : 'Switch the lookup on and add a key first'}
|
title={overview.ready ? undefined : 'Switch the lookup on and add a key first'}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
<li class="flex items-start gap-3 py-3">
|
<li class="flex items-start gap-3 py-3">
|
||||||
<MonitorSmartphone
|
<MonitorSmartphone
|
||||||
size={18}
|
size={18}
|
||||||
class="mt-0.5 flex-shrink-0 text-text-secondary"
|
class="mt-0.5 shrink-0 text-text-secondary"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
@@ -173,7 +173,7 @@
|
|||||||
{#if !s.current}
|
{#if !s.current}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex-shrink-0 rounded border border-border px-2 py-1 text-sm
|
class="shrink-0 rounded border border-border px-2 py-1 text-sm
|
||||||
hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent
|
hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent
|
||||||
disabled:opacity-50"
|
disabled:opacity-50"
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="art-wrap relative aspect-square w-full overflow-hidden rounded-md
|
class="art-wrap relative aspect-square w-full overflow-hidden rounded-md
|
||||||
shadow-sm transition-all duration-150
|
shadow-xs transition-all duration-150
|
||||||
group-hover:shadow-lg group-hover:ring-1 group-hover:ring-accent/40"
|
group-hover:shadow-lg group-hover:ring-1 group-hover:ring-accent/40"
|
||||||
>
|
>
|
||||||
<Cover
|
<Cover
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
class="flex w-full items-center gap-2 rounded-md p-1 pr-24 text-left
|
class="flex w-full items-center gap-2 rounded-md p-1 pr-24 text-left
|
||||||
hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent"
|
hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-accent"
|
||||||
>
|
>
|
||||||
<div class="h-12 w-12 flex-shrink-0 overflow-hidden rounded bg-surface-hover">
|
<div class="h-12 w-12 shrink-0 overflow-hidden rounded bg-surface-hover">
|
||||||
<Cover src={cover} />
|
<Cover src={cover} />
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={`Bring ${title} back`}
|
aria-label={`Bring ${title} back`}
|
||||||
class="rounded-md border border-border px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover focus:outline-none focus:ring-2 focus:ring-accent"
|
class="rounded-md border border-border px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
onclick={() => onUnsnooze?.()}
|
onclick={() => onUnsnooze?.()}
|
||||||
>
|
>
|
||||||
Undo
|
Undo
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
aria-label={`Not right now — hide ${title} for a while`}
|
aria-label={`Not right now — hide ${title} for a while`}
|
||||||
title="Not right now"
|
title="Not right now"
|
||||||
class="rounded-md border border-border p-1.5 text-text-secondary hover:bg-surface-hover hover:text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
|
class="rounded-md border border-border p-1.5 text-text-secondary hover:bg-surface-hover hover:text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
onclick={() => onSnooze?.()}
|
onclick={() => onSnooze?.()}
|
||||||
>
|
>
|
||||||
<Clock size={16} strokeWidth={1} />
|
<Clock size={16} strokeWidth={1} />
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
const inputClass =
|
const inputClass =
|
||||||
'mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm text-text-primary ' +
|
'mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm text-text-primary ' +
|
||||||
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent';
|
'focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="space-y-4 rounded-xl border border-border bg-surface p-5">
|
<section class="space-y-4 rounded-xl border border-border bg-surface p-5">
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
class="flex items-start gap-2 rounded-md bg-surface-hover px-3 py-2 text-xs text-text-secondary"
|
class="flex items-start gap-2 rounded-md bg-surface-hover px-3 py-2 text-xs text-text-secondary"
|
||||||
data-testid="length-warning"
|
data-testid="length-warning"
|
||||||
>
|
>
|
||||||
<TriangleAlert size={14} class="mt-0.5 flex-shrink-0 text-action-destructive" aria-hidden="true" />
|
<TriangleAlert size={14} class="mt-0.5 shrink-0 text-action-destructive" aria-hidden="true" />
|
||||||
<span>
|
<span>
|
||||||
Saving re-fingerprints {libraryTotal > 0
|
Saving re-fingerprints {libraryTotal > 0
|
||||||
? `all ${libraryTotal.toLocaleString()} tracks`
|
? `all ${libraryTotal.toLocaleString()} tracks`
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent
|
||||||
disabled:cursor-not-allowed disabled:opacity-50"
|
disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!dirty || saving || problems.length > 0}
|
disabled={!dirty || saving || problems.length > 0}
|
||||||
onclick={save}
|
onclick={save}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<span class="block text-xs text-text-secondary">Reason</span>
|
<span class="block text-xs text-text-secondary">Reason</span>
|
||||||
<select
|
<select
|
||||||
bind:value={reason}
|
bind:value={reason}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
>
|
>
|
||||||
<option value="bad_rip">Bad rip</option>
|
<option value="bad_rip">Bad rip</option>
|
||||||
<option value="wrong_file">Wrong file</option>
|
<option value="wrong_file">Wrong file</option>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
bind:value={notes}
|
bind:value={notes}
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
placeholder="What's wrong with it?"
|
placeholder="What's wrong with it?"
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
rows="2"
|
rows="2"
|
||||||
></textarea>
|
></textarea>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<img
|
<img
|
||||||
src={coverUrl(event.track.album_id)}
|
src={coverUrl(event.track.album_id)}
|
||||||
alt=""
|
alt=""
|
||||||
class="h-11 w-11 flex-shrink-0 rounded object-cover"
|
class="h-11 w-11 shrink-0 rounded object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
{event.track.artist_name} · {event.track.album_title}
|
{event.track.artist_name} · {event.track.album_title}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span title={tooltip} class="flex-shrink-0 text-xs text-text-secondary">
|
<span title={tooltip} class="shrink-0 text-xs text-text-secondary">
|
||||||
{timestamp}
|
{timestamp}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
each shelf a clear chapter break without adding chrome. -->
|
each shelf a clear chapter break without adding chrome. -->
|
||||||
<h2
|
<h2
|
||||||
class="font-display text-2xl font-medium text-text-primary
|
class="font-display text-2xl font-medium text-text-primary
|
||||||
flex items-baseline gap-3 after:h-[2px] after:w-12 after:flex-shrink-0
|
flex items-baseline gap-3 after:h-[2px] after:w-12 after:shrink-0
|
||||||
after:rounded-full after:bg-accent/60 after:content-['']"
|
after:rounded-full after:bg-accent/60 after:content-['']"
|
||||||
>{title}</h2>
|
>{title}</h2>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
|
|
||||||
const inputClass =
|
const inputClass =
|
||||||
'mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm text-text-primary ' +
|
'mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm text-text-primary ' +
|
||||||
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent';
|
'focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="space-y-4 rounded border border-border bg-surface p-4">
|
<section class="space-y-4 rounded border border-border bg-surface p-4">
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent
|
||||||
disabled:cursor-not-allowed disabled:opacity-50"
|
disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!dirty || saving || !valid}
|
disabled={!dirty || saving || !valid}
|
||||||
onclick={save}
|
onclick={save}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
max={settings.max_hops}
|
max={settings.max_hops}
|
||||||
bind:value={hops}
|
bind:value={hops}
|
||||||
class="w-24 rounded border border-border bg-background px-2 py-1
|
class="w-24 rounded border border-border bg-background px-2 py-1
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
<div class="space-y-2 rounded border border-border bg-background p-3 text-sm">
|
<div class="space-y-2 rounded border border-border bg-background p-3 text-sm">
|
||||||
<p class="flex items-start gap-2 text-text-secondary">
|
<p class="flex items-start gap-2 text-text-secondary">
|
||||||
<TriangleAlert size={14} class="mt-0.5 flex-shrink-0 text-action-destructive" aria-hidden="true" />
|
<TriangleAlert size={14} class="mt-0.5 shrink-0 text-action-destructive" aria-hidden="true" />
|
||||||
<span>
|
<span>
|
||||||
Count your proxies — don't guess high. This number tells Minstrel how much of the
|
Count your proxies — don't guess high. This number tells Minstrel how much of the
|
||||||
<span class="font-mono">X-Forwarded-For</span> header to believe, and that header is
|
<span class="font-mono">X-Forwarded-For</span> header to believe, and that header is
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
<svelte:window onclick={handleWindowClick} onkeydown={(e) => e.key === 'Escape' && (open = false)} />
|
<svelte:window onclick={handleWindowClick} onkeydown={(e) => e.key === 'Escape' && (open = false)} />
|
||||||
|
|
||||||
<div class="relative flex-shrink-0">
|
<div class="relative shrink-0">
|
||||||
<button
|
<button
|
||||||
bind:this={buttonRef}
|
bind:this={buttonRef}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
{#if open}
|
{#if open}
|
||||||
<div
|
<div
|
||||||
bind:this={panelRef}
|
bind:this={panelRef}
|
||||||
class="absolute right-0 z-50 mt-1 flex max-h-[70vh] w-[min(22rem,calc(100vw-2rem))] flex-col rounded border border-border bg-surface shadow"
|
class="absolute right-0 z-50 mt-1 flex max-h-[70vh] w-[min(22rem,calc(100vw-2rem))] flex-col rounded border border-border bg-surface shadow-sm"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-label="Notifications"
|
aria-label="Notifications"
|
||||||
>
|
>
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
onclick={() => openItem(item)}
|
onclick={() => openItem(item)}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="mt-1.5 h-2 w-2 flex-shrink-0 rounded-full {item.read_at ? '' : 'bg-text-primary'}"
|
class="mt-1.5 h-2 w-2 shrink-0 rounded-full {item.read_at ? '' : 'bg-text-primary'}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></span>
|
></span>
|
||||||
<span class="min-w-0 flex-1">
|
<span class="min-w-0 flex-1">
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<select
|
<select
|
||||||
bind:value={form.summary_hour}
|
bind:value={form.summary_hour}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
>
|
>
|
||||||
{#each hours as h (h)}
|
{#each hours as h (h)}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
max="1440"
|
max="1440"
|
||||||
bind:value={form.batch_window_minutes}
|
bind:value={form.batch_window_minutes}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent
|
||||||
disabled:cursor-not-allowed disabled:opacity-50"
|
disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!dirty || saving}
|
disabled={!dirty || saving}
|
||||||
onclick={save}
|
onclick={save}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/now-playing"
|
href="/now-playing"
|
||||||
aria-label="Open now playing"
|
aria-label="Open now playing"
|
||||||
class="flex min-w-0 items-center gap-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
class="flex min-w-0 items-center gap-2 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
style="flex: 1 1 35%;"
|
style="flex: 1 1 35%;"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
aria-label={player.isPlaying ? 'Pause' : 'Play'}
|
aria-label={player.isPlaying ? 'Pause' : 'Play'}
|
||||||
onclick={togglePlay}
|
onclick={togglePlay}
|
||||||
class="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-text-primary shadow transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-accent"
|
class="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-text-primary shadow-sm transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-accent"
|
||||||
>
|
>
|
||||||
{#if player.state === 'loading' || player.state === 'idle'}
|
{#if player.state === 'loading' || player.state === 'idle'}
|
||||||
<Loader2 data-testid="play-spinner" size={22} strokeWidth={1.5} class="animate-spin" />
|
<Loader2 data-testid="play-spinner" size={22} strokeWidth={1.5} class="animate-spin" />
|
||||||
@@ -281,7 +281,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/now-playing"
|
href="/now-playing"
|
||||||
aria-label="Open now playing"
|
aria-label="Open now playing"
|
||||||
class="shrink-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
class="shrink-0 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={coverUrl(current.album_id)}
|
src={coverUrl(current.album_id)}
|
||||||
@@ -341,7 +341,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
aria-label={player.isPlaying ? 'Pause' : 'Play'}
|
aria-label={player.isPlaying ? 'Pause' : 'Play'}
|
||||||
onclick={togglePlay}
|
onclick={togglePlay}
|
||||||
class="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-text-primary shadow transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-accent"
|
class="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-text-primary shadow-sm transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-accent"
|
||||||
>
|
>
|
||||||
{#if player.state === 'loading' || player.state === 'idle'}
|
{#if player.state === 'loading' || player.state === 'idle'}
|
||||||
<Loader2 size={24} strokeWidth={1.5} class="animate-spin" />
|
<Loader2 size={24} strokeWidth={1.5} class="animate-spin" />
|
||||||
|
|||||||
@@ -184,7 +184,7 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="art-wrap relative aspect-square w-full overflow-hidden rounded-md
|
class="art-wrap relative aspect-square w-full overflow-hidden rounded-md
|
||||||
bg-surface-hover shadow-sm transition-all duration-150
|
bg-surface-hover shadow-xs transition-all duration-150
|
||||||
group-hover:shadow-lg group-hover:ring-1 group-hover:ring-accent/40"
|
group-hover:shadow-lg group-hover:ring-1 group-hover:ring-accent/40"
|
||||||
>
|
>
|
||||||
{#if playlist.cover_url}
|
{#if playlist.cover_url}
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
is often a 2x2 collage; the gradient mutes the collage so
|
is often a 2x2 collage; the gradient mutes the collage so
|
||||||
the playlist NAME becomes the dominant element. -->
|
the playlist NAME becomes the dominant element. -->
|
||||||
<div
|
<div
|
||||||
class="pointer-events-none absolute inset-0 bg-gradient-to-t
|
class="pointer-events-none absolute inset-0 bg-linear-to-t
|
||||||
from-black/85 via-black/40 to-transparent"
|
from-black/85 via-black/40 to-transparent"
|
||||||
></div>
|
></div>
|
||||||
<!-- Title burned in at the bottom of the cover. Two lines max;
|
<!-- Title burned in at the bottom of the cover. Two lines max;
|
||||||
|
|||||||
@@ -103,8 +103,8 @@
|
|||||||
aria-label="Reorder track (drag or use arrow keys)"
|
aria-label="Reorder track (drag or use arrow keys)"
|
||||||
aria-keyshortcuts="ArrowUp ArrowDown"
|
aria-keyshortcuts="ArrowUp ArrowDown"
|
||||||
onkeydown={handleHandleKeydown}
|
onkeydown={handleHandleKeydown}
|
||||||
class="flex-shrink-0 cursor-grab text-text-muted hover:text-text-primary
|
class="shrink-0 cursor-grab text-text-muted hover:text-text-primary
|
||||||
active:cursor-grabbing focus-visible:outline focus-visible:outline-2
|
active:cursor-grabbing focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-50"
|
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<GripVertical size={14} strokeWidth={1} />
|
<GripVertical size={14} strokeWidth={1} />
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span class="flex-shrink-0 text-xs text-text-muted">{format(row.duration_sec)}</span>
|
<span class="shrink-0 text-xs text-text-muted">{format(row.duration_sec)}</span>
|
||||||
|
|
||||||
{#if !isUnavailable && liveTrack}
|
{#if !isUnavailable && liveTrack}
|
||||||
<LikeButton entityType="track" entityId={liveTrack.id} />
|
<LikeButton entityType="track" entityId={liveTrack.id} />
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={() => onRemove(row.position)}
|
onclick={() => onRemove(row.position)}
|
||||||
aria-label={`Remove ${row.title} from playlist`}
|
aria-label={`Remove ${row.title} from playlist`}
|
||||||
class="flex-shrink-0 rounded p-1 text-text-muted hover:bg-surface hover:text-action-destructive"
|
class="shrink-0 rounded p-1 text-text-muted hover:bg-surface hover:text-action-destructive"
|
||||||
>
|
>
|
||||||
<X size={14} strokeWidth={1} />
|
<X size={14} strokeWidth={1} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
placeholder="https://music.example.com"
|
placeholder="https://music.example.com"
|
||||||
bind:value
|
bind:value
|
||||||
class="w-full rounded border border-border bg-background px-2 py-1 font-mono
|
class="w-full rounded border border-border bg-background px-2 py-1 font-mono
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
{#if here && value.trim() !== here}
|
{#if here && value.trim() !== here}
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
|
|
||||||
{#if !saved}
|
{#if !saved}
|
||||||
<p class="flex items-start gap-2 text-sm text-text-secondary" role="status">
|
<p class="flex items-start gap-2 text-sm text-text-secondary" role="status">
|
||||||
<TriangleAlert size={14} class="mt-0.5 flex-shrink-0 text-action-destructive" aria-hidden="true" />
|
<TriangleAlert size={14} class="mt-0.5 shrink-0 text-action-destructive" aria-hidden="true" />
|
||||||
<span>
|
<span>
|
||||||
Not set, so password-reset emails are not being sent. Minstrel won't build the link from
|
Not set, so password-reset emails are not being sent. Minstrel won't build the link from
|
||||||
whatever address a request claims, because anyone can claim any address.
|
whatever address a request claims, because anyone can claim any address.
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
hover/focus so the artwork stays legible the rest of the time; the drop
|
hover/focus so the artwork stays legible the rest of the time; the drop
|
||||||
shadow is what keeps the glyph readable over pale covers without one.
|
shadow is what keeps the glyph readable over pale covers without one.
|
||||||
-->
|
-->
|
||||||
<div class="relative h-10 w-10 flex-shrink-0">
|
<div class="relative h-10 w-10 shrink-0">
|
||||||
<img
|
<img
|
||||||
src={coverUrl(track.album_id)}
|
src={coverUrl(track.album_id)}
|
||||||
alt=""
|
alt=""
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
class="group absolute inset-0 flex cursor-grab items-center justify-center rounded
|
class="group absolute inset-0 flex cursor-grab items-center justify-center rounded
|
||||||
text-white/70 transition hover:bg-black/45 hover:text-white
|
text-white/70 transition hover:bg-black/45 hover:text-white
|
||||||
focus-visible:bg-black/45 focus-visible:text-white
|
focus-visible:bg-black/45 focus-visible:text-white
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
style="filter: drop-shadow(0 1px 1px rgb(0 0 0 / 0.9))"
|
style="filter: drop-shadow(0 1px 1px rgb(0 0 0 / 0.9))"
|
||||||
>
|
>
|
||||||
<GripVertical size={16} />
|
<GripVertical size={16} />
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
{#if isCurrent}
|
{#if isCurrent}
|
||||||
<span class="text-xs text-accent-fg uppercase tracking-wide flex-shrink-0">Now playing</span>
|
<span class="text-xs text-accent-fg uppercase tracking-wide shrink-0">Now playing</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="font-medium truncate text-text-primary">{track.title}</span>
|
<span class="font-medium truncate text-text-primary">{track.title}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={handleRemove}
|
onclick={handleRemove}
|
||||||
aria-label="Remove from queue"
|
aria-label="Remove from queue"
|
||||||
class="text-text-secondary hover:text-text-primary flex-shrink-0"
|
class="text-text-secondary hover:text-text-primary shrink-0"
|
||||||
>
|
>
|
||||||
<X size={16} />
|
<X size={16} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
oninput={onInput}
|
oninput={onInput}
|
||||||
onkeydown={onKey}
|
onkeydown={onKey}
|
||||||
class="w-full rounded border border-border bg-surface pl-7 pr-7 py-1 text-sm
|
class="w-full rounded border border-border bg-surface pl-7 pr-7 py-1 text-sm
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
{#if raw}
|
{#if raw}
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
max="720"
|
max="720"
|
||||||
bind:value={form.grace_hours}
|
bind:value={form.grace_hours}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
max="10"
|
max="10"
|
||||||
bind:value={form.max_attempts}
|
bind:value={form.max_attempts}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
max="168"
|
max="168"
|
||||||
bind:value={form.backoff_base_hours}
|
bind:value={form.backoff_base_hours}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
max="720"
|
max="720"
|
||||||
bind:value={form.backoff_max_hours}
|
bind:value={form.backoff_max_hours}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
max="200"
|
max="200"
|
||||||
bind:value={form.max_per_pass}
|
bind:value={form.max_per_pass}
|
||||||
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
class="mt-1 w-28 rounded border border-border bg-background px-2 py-1 text-sm
|
||||||
text-text-primary focus-visible:outline focus-visible:outline-2
|
text-text-primary focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent"
|
focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
watching those rows never get a request would reasonably conclude
|
watching those rows never get a request would reasonably conclude
|
||||||
the feature is broken. -->
|
the feature is broken. -->
|
||||||
<p class="flex items-start gap-2 rounded-md bg-surface-hover px-3 py-2 text-xs text-text-secondary">
|
<p class="flex items-start gap-2 rounded-md bg-surface-hover px-3 py-2 text-xs text-text-secondary">
|
||||||
<TriangleAlert size={14} class="mt-0.5 flex-shrink-0 text-action-destructive" aria-hidden="true" />
|
<TriangleAlert size={14} class="mt-0.5 shrink-0 text-action-destructive" aria-hidden="true" />
|
||||||
<span>
|
<span>
|
||||||
{saved.unnameable_albums}
|
{saved.unnameable_albums}
|
||||||
{saved.unnameable_albums === 1 ? 'album has' : 'albums have'} missing files but no
|
{saved.unnameable_albums === 1 ? 'album has' : 'albums have'} missing files but no
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
class="rounded-md bg-action-secondary px-4 py-2 text-sm text-action-fg hover:opacity-90
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent
|
||||||
disabled:cursor-not-allowed disabled:opacity-50"
|
disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!dirty || saving}
|
disabled={!dirty || saving}
|
||||||
onclick={save}
|
onclick={save}
|
||||||
|
|||||||
@@ -66,6 +66,6 @@
|
|||||||
bind:value
|
bind:value
|
||||||
oninput={onInput}
|
oninput={onInput}
|
||||||
onkeydown={onKey}
|
onkeydown={onKey}
|
||||||
class="w-full rounded border border-border bg-surface pl-7 pr-3 py-1 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
class="w-full rounded border border-border bg-surface pl-7 pr-3 py-1 text-sm focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -94,13 +94,13 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="flex items-center gap-1 md:gap-3 justify-self-end min-w-0">
|
<div class="flex items-center gap-1 md:gap-3 justify-self-end min-w-0">
|
||||||
<div class="hidden md:block min-w-0 max-w-xs flex-shrink">
|
<div class="hidden md:block min-w-0 max-w-xs shrink">
|
||||||
<SearchInput />
|
<SearchInput />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NotificationBell />
|
<NotificationBell />
|
||||||
|
|
||||||
<div class="relative flex-shrink-0">
|
<div class="relative shrink-0">
|
||||||
<button
|
<button
|
||||||
bind:this={menuButtonRef}
|
bind:this={menuButtonRef}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
{#if menuOpen}
|
{#if menuOpen}
|
||||||
<div
|
<div
|
||||||
bind:this={menuRef}
|
bind:this={menuRef}
|
||||||
class="absolute right-0 z-50 mt-1 w-40 rounded border border-border bg-surface shadow"
|
class="absolute right-0 z-50 mt-1 w-40 rounded border border-border bg-surface shadow-sm"
|
||||||
role="menu"
|
role="menu"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -199,7 +199,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={`Bring ${snoozed.name} back now`}
|
aria-label={`Bring ${snoozed.name} back now`}
|
||||||
class="shrink-0 rounded-md border border-border px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover focus:outline-none focus:ring-2 focus:ring-accent"
|
class="shrink-0 rounded-md border border-border px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
onclick={() => onUnsnooze(snoozed.mbid, snoozed.name)}
|
onclick={() => onUnsnooze(snoozed.mbid, snoozed.name)}
|
||||||
>
|
>
|
||||||
Bring back
|
Bring back
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
data-selected={isSelected}
|
data-selected={isSelected}
|
||||||
onclick={onRowClick}
|
onclick={onRowClick}
|
||||||
onkeydown={onRowKey}
|
onkeydown={onRowKey}
|
||||||
class="group grid w-full cursor-pointer grid-cols-[32px_1fr_auto_auto_auto_auto_auto] items-center gap-4 px-3 py-2 text-left text-sm odd:bg-surface/50 hover:bg-surface focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent {isCurrent ? 'border-l-2 border-l-accent bg-surface-hover' : ''} {isSelected ? 'bg-surface-hover' : ''}"
|
class="group grid w-full cursor-pointer grid-cols-[32px_1fr_auto_auto_auto_auto_auto] items-center gap-4 px-3 py-2 text-left text-sm odd:bg-surface/50 hover:bg-surface focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent {isCurrent ? 'border-l-2 border-l-accent bg-surface-hover' : ''} {isSelected ? 'bg-surface-hover' : ''}"
|
||||||
>
|
>
|
||||||
<span class="relative flex h-6 w-8 items-center justify-end">
|
<span class="relative flex h-6 w-8 items-center justify-end">
|
||||||
<!-- Track number: visible by default; hidden when the row is hovered
|
<!-- Track number: visible by default; hidden when the row is hovered
|
||||||
|
|||||||
@@ -285,7 +285,7 @@
|
|||||||
class="flex items-start gap-2 border-b border-border bg-surface-hover px-4 py-3 text-xs text-text-secondary"
|
class="flex items-start gap-2 border-b border-border bg-surface-hover px-4 py-3 text-xs text-text-secondary"
|
||||||
data-testid="title-mismatch"
|
data-testid="title-mismatch"
|
||||||
>
|
>
|
||||||
<TriangleAlert size={14} class="mt-0.5 flex-shrink-0 text-warning-fg" aria-hidden="true" />
|
<TriangleAlert size={14} class="mt-0.5 shrink-0 text-warning-fg" aria-hidden="true" />
|
||||||
<span>
|
<span>
|
||||||
The audio is identical, but the copies are titled {quotedList(titles)}. If those
|
The audio is identical, but the copies are titled {quotedList(titles)}. If those
|
||||||
name different recordings, one file is mislabelled and the other recording isn't in
|
name different recordings, one file is mislabelled and the other recording isn't in
|
||||||
|
|||||||
@@ -436,7 +436,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={baseUrl}
|
bind:value={baseUrl}
|
||||||
placeholder="http://lidarr.lan:8686"
|
placeholder="http://lidarr.lan:8686"
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -448,7 +448,7 @@
|
|||||||
placeholder={config.data?.api_key === '***'
|
placeholder={config.data?.api_key === '***'
|
||||||
? '••• (saved — leave empty to keep)'
|
? '••• (saved — leave empty to keep)'
|
||||||
: 'Paste API key'}
|
: 'Paste API key'}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
<select
|
<select
|
||||||
bind:value={qualityId}
|
bind:value={qualityId}
|
||||||
disabled={!profilesEnabled && testQualityProfiles.length === 0}
|
disabled={!profilesEnabled && testQualityProfiles.length === 0}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{#each (testQualityProfiles.length ? testQualityProfiles : (profiles.data ?? [])) as p (p.id)}
|
{#each (testQualityProfiles.length ? testQualityProfiles : (profiles.data ?? [])) as p (p.id)}
|
||||||
<option value={p.id}>{p.name}</option>
|
<option value={p.id}>{p.name}</option>
|
||||||
@@ -475,7 +475,7 @@
|
|||||||
<select
|
<select
|
||||||
bind:value={metadataId}
|
bind:value={metadataId}
|
||||||
disabled={!profilesEnabled && testMetadataProfiles.length === 0}
|
disabled={!profilesEnabled && testMetadataProfiles.length === 0}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{#each (testMetadataProfiles.length ? testMetadataProfiles : (metadataProfiles.data ?? [])) as p (p.id)}
|
{#each (testMetadataProfiles.length ? testMetadataProfiles : (metadataProfiles.data ?? [])) as p (p.id)}
|
||||||
<option value={p.id}>{p.name}</option>
|
<option value={p.id}>{p.name}</option>
|
||||||
@@ -493,7 +493,7 @@
|
|||||||
<select
|
<select
|
||||||
bind:value={rootPath}
|
bind:value={rootPath}
|
||||||
disabled={!profilesEnabled && testRootFolders.length === 0}
|
disabled={!profilesEnabled && testRootFolders.length === 0}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{#each (testRootFolders.length ? testRootFolders : (folders.data ?? [])) as f (f.path)}
|
{#each (testRootFolders.length ? testRootFolders : (folders.data ?? [])) as f (f.path)}
|
||||||
<option value={f.path}
|
<option value={f.path}
|
||||||
@@ -597,7 +597,7 @@
|
|||||||
<input id="key-{provider.id}" type="password"
|
<input id="key-{provider.id}" type="password"
|
||||||
placeholder={provider.api_key_set ? '••• (saved — leave empty to keep)' : 'Leave blank to use the public test key'}
|
placeholder={provider.api_key_set ? '••• (saved — leave empty to keep)' : 'Leave blank to use the public test key'}
|
||||||
bind:value={coverLocalState[provider.id].apiKey}
|
bind:value={coverLocalState[provider.id].apiKey}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent flex-1" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent flex-1" />
|
||||||
{#if provider.api_key_set}
|
{#if provider.api_key_set}
|
||||||
<span class="text-xs text-text-secondary whitespace-nowrap">✓ Set</span>
|
<span class="text-xs text-text-secondary whitespace-nowrap">✓ Set</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -694,7 +694,7 @@
|
|||||||
<input id="tag-key-{provider.id}" type="password"
|
<input id="tag-key-{provider.id}" type="password"
|
||||||
placeholder={provider.api_key_set ? '••• (saved — leave empty to keep)' : 'Paste your API key to enable this source'}
|
placeholder={provider.api_key_set ? '••• (saved — leave empty to keep)' : 'Paste your API key to enable this source'}
|
||||||
bind:value={tagLocalState[provider.id].apiKey}
|
bind:value={tagLocalState[provider.id].apiKey}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent flex-1" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent flex-1" />
|
||||||
{#if provider.api_key_set}
|
{#if provider.api_key_set}
|
||||||
<span class="text-xs text-text-secondary whitespace-nowrap">✓ Set</span>
|
<span class="text-xs text-text-secondary whitespace-nowrap">✓ Set</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -768,30 +768,30 @@
|
|||||||
<div class="grid gap-3 sm:grid-cols-[auto_1fr] items-center">
|
<div class="grid gap-3 sm:grid-cols-[auto_1fr] items-center">
|
||||||
<label for="smtp-host" class="text-sm text-text-secondary">Host</label>
|
<label for="smtp-host" class="text-sm text-text-secondary">Host</label>
|
||||||
<input id="smtp-host" type="text" bind:value={smtpForm.host}
|
<input id="smtp-host" type="text" bind:value={smtpForm.host}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent" />
|
||||||
|
|
||||||
<label for="smtp-port" class="text-sm text-text-secondary">Port</label>
|
<label for="smtp-port" class="text-sm text-text-secondary">Port</label>
|
||||||
<input id="smtp-port" type="number" min="1" max="65535"
|
<input id="smtp-port" type="number" min="1" max="65535"
|
||||||
bind:value={smtpForm.port}
|
bind:value={smtpForm.port}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent" />
|
||||||
|
|
||||||
<label for="smtp-username" class="text-sm text-text-secondary">Username</label>
|
<label for="smtp-username" class="text-sm text-text-secondary">Username</label>
|
||||||
<input id="smtp-username" type="text" bind:value={smtpForm.username}
|
<input id="smtp-username" type="text" bind:value={smtpForm.username}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent" />
|
||||||
|
|
||||||
<label for="smtp-password" class="text-sm text-text-secondary">Password</label>
|
<label for="smtp-password" class="text-sm text-text-secondary">Password</label>
|
||||||
<input id="smtp-password" type="password"
|
<input id="smtp-password" type="password"
|
||||||
placeholder={smtpForm.password === '***' ? 'Leave blank to keep current' : ''}
|
placeholder={smtpForm.password === '***' ? 'Leave blank to keep current' : ''}
|
||||||
bind:value={smtpPasswordInput}
|
bind:value={smtpPasswordInput}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent" />
|
||||||
|
|
||||||
<label for="smtp-from" class="text-sm text-text-secondary">From address</label>
|
<label for="smtp-from" class="text-sm text-text-secondary">From address</label>
|
||||||
<input id="smtp-from" type="email" bind:value={smtpForm.from_address}
|
<input id="smtp-from" type="email" bind:value={smtpForm.from_address}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent" />
|
||||||
|
|
||||||
<label for="smtp-from-name" class="text-sm text-text-secondary">From name</label>
|
<label for="smtp-from-name" class="text-sm text-text-secondary">From name</label>
|
||||||
<input id="smtp-from-name" type="text" bind:value={smtpForm.from_name}
|
<input id="smtp-from-name" type="text" bind:value={smtpForm.from_name}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent" />
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent" />
|
||||||
|
|
||||||
<span class="text-sm text-text-secondary">TLS</span>
|
<span class="text-sm text-text-secondary">TLS</span>
|
||||||
<label class="flex items-center gap-2 text-sm">
|
<label class="flex items-center gap-2 text-sm">
|
||||||
@@ -837,7 +837,7 @@
|
|||||||
bind:value={disconnectInput}
|
bind:value={disconnectInput}
|
||||||
placeholder="DISCONNECT"
|
placeholder="DISCONNECT"
|
||||||
aria-label="Type DISCONNECT to confirm"
|
aria-label="Type DISCONNECT to confirm"
|
||||||
class="mt-3 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-3 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
{#if disconnectError}
|
{#if disconnectError}
|
||||||
<p class="mt-2 text-sm text-error-fg">Disconnect failed — {disconnectError}</p>
|
<p class="mt-2 text-sm text-error-fg">Disconnect failed — {disconnectError}</p>
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded border border-border px-3 py-1.5 text-sm text-text-primary
|
class="rounded border border-border px-3 py-1.5 text-sm text-text-primary
|
||||||
hover:bg-surface-hover focus-visible:outline focus-visible:outline-2
|
hover:bg-surface-hover focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-40"
|
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
disabled={offset === 0}
|
disabled={offset === 0}
|
||||||
onclick={() => (offset = Math.max(0, offset - PAGE_SIZE))}
|
onclick={() => (offset = Math.max(0, offset - PAGE_SIZE))}
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded border border-border px-3 py-1.5 text-sm text-text-primary
|
class="rounded border border-border px-3 py-1.5 text-sm text-text-primary
|
||||||
hover:bg-surface-hover focus-visible:outline focus-visible:outline-2
|
hover:bg-surface-hover focus-visible:outline-solid focus-visible:outline-2
|
||||||
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-40"
|
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
disabled={!hasMore}
|
disabled={!hasMore}
|
||||||
onclick={() => (offset += PAGE_SIZE)}
|
onclick={() => (offset += PAGE_SIZE)}
|
||||||
|
|||||||
@@ -351,7 +351,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={deleteLidarrInput}
|
bind:value={deleteLidarrInput}
|
||||||
placeholder="DELETE"
|
placeholder="DELETE"
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
aria-label="Type DELETE to confirm"
|
aria-label="Type DELETE to confirm"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -291,7 +291,7 @@
|
|||||||
<span class="block text-sm text-text-secondary">Quality profile</span>
|
<span class="block text-sm text-text-secondary">Quality profile</span>
|
||||||
<select
|
<select
|
||||||
bind:value={qualityOverride}
|
bind:value={qualityOverride}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
>
|
>
|
||||||
<option value="">Use default</option>
|
<option value="">Use default</option>
|
||||||
{#each profiles.data ?? [] as p (p.id)}
|
{#each profiles.data ?? [] as p (p.id)}
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
<span class="block text-sm text-text-secondary">Root folder</span>
|
<span class="block text-sm text-text-secondary">Root folder</span>
|
||||||
<select
|
<select
|
||||||
bind:value={rootOverride}
|
bind:value={rootOverride}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm text-text-primary focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
>
|
>
|
||||||
<option value="">Use default</option>
|
<option value="">Use default</option>
|
||||||
{#each folders.data ?? [] as f (f.path)}
|
{#each folders.data ?? [] as f (f.path)}
|
||||||
@@ -346,7 +346,7 @@
|
|||||||
bind:value={rejectNotes}
|
bind:value={rejectNotes}
|
||||||
rows="3"
|
rows="3"
|
||||||
placeholder="Optional — why this is being set aside"
|
placeholder="Optional — why this is being set aside"
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
></textarea>
|
></textarea>
|
||||||
</label>
|
</label>
|
||||||
<div class="mt-5 flex justify-end gap-2">
|
<div class="mt-5 flex justify-end gap-2">
|
||||||
|
|||||||
@@ -293,7 +293,7 @@
|
|||||||
type="number"
|
type="number"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
bind:value={form[p.scope][f.key]}
|
bind:value={form[p.scope][f.key]}
|
||||||
class="w-full rounded border border-border bg-background px-2 py-1 text-right text-sm tabular-nums outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-2 py-1 text-right text-sm tabular-nums outline-hidden focus:border-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -346,7 +346,7 @@
|
|||||||
type="number"
|
type="number"
|
||||||
step={f.key === 'half_life_days' ? '1' : '0.05'}
|
step={f.key === 'half_life_days' ? '1' : '0.05'}
|
||||||
bind:value={form.taste[f.key]}
|
bind:value={form.taste[f.key]}
|
||||||
class="w-full rounded border border-border bg-background px-2 py-1 text-right text-sm tabular-nums outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-2 py-1 text-right text-sm tabular-nums outline-hidden focus:border-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -402,7 +402,7 @@
|
|||||||
step={f.key === 'snooze_days' ? '1' : '0.1'}
|
step={f.key === 'snooze_days' ? '1' : '0.1'}
|
||||||
min="0"
|
min="0"
|
||||||
bind:value={form.discover[f.key]}
|
bind:value={form.discover[f.key]}
|
||||||
class="w-full rounded border border-border bg-background px-2 py-1 text-right text-sm tabular-nums outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-2 py-1 text-right text-sm tabular-nums outline-hidden focus:border-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -369,7 +369,7 @@
|
|||||||
minlength="3"
|
minlength="3"
|
||||||
maxlength="32"
|
maxlength="32"
|
||||||
bind:value={createForm.username}
|
bind:value={createForm.username}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -381,7 +381,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
maxlength="64"
|
maxlength="64"
|
||||||
bind:value={createForm.display_name}
|
bind:value={createForm.display_name}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -392,7 +392,7 @@
|
|||||||
required
|
required
|
||||||
minlength="8"
|
minlength="8"
|
||||||
bind:value={createForm.password}
|
bind:value={createForm.password}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -403,7 +403,7 @@
|
|||||||
required
|
required
|
||||||
minlength="8"
|
minlength="8"
|
||||||
bind:value={createForm.confirmPassword}
|
bind:value={createForm.confirmPassword}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<label class="flex items-center gap-2 text-sm text-text-secondary">
|
<label class="flex items-center gap-2 text-sm text-text-secondary">
|
||||||
@@ -447,7 +447,7 @@
|
|||||||
required
|
required
|
||||||
minlength="8"
|
minlength="8"
|
||||||
bind:value={resetPasswordValue}
|
bind:value={resetPasswordValue}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -458,7 +458,7 @@
|
|||||||
required
|
required
|
||||||
minlength="8"
|
minlength="8"
|
||||||
bind:value={resetPasswordConfirm}
|
bind:value={resetPasswordConfirm}
|
||||||
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end gap-2 pt-2">
|
<div class="flex justify-end gap-2 pt-2">
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
aria-label={`Play ${detail.name}`}
|
aria-label={`Play ${detail.name}`}
|
||||||
onclick={onPlay}
|
onclick={onPlay}
|
||||||
disabled={isStartingPlay || detail.album_count === 0}
|
disabled={isStartingPlay || detail.album_count === 0}
|
||||||
class="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-text-primary shadow transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-accent disabled:opacity-50 disabled:hover:scale-100"
|
class="flex h-12 w-12 items-center justify-center rounded-full bg-accent text-text-primary shadow-sm transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-accent disabled:opacity-50 disabled:hover:scale-100"
|
||||||
>
|
>
|
||||||
<Play size={24} strokeWidth={1.5} fill="currentColor" class="ml-0.5" />
|
<Play size={24} strokeWidth={1.5} fill="currentColor" class="ml-0.5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
aria-label="Search Lidarr"
|
aria-label="Search Lidarr"
|
||||||
placeholder="Search artists, albums, or tracks"
|
placeholder="Search artists, albums, or tracks"
|
||||||
bind:value={inputValue}
|
bind:value={inputValue}
|
||||||
class="w-full rounded-md border border-border bg-background px-3 py-2 text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-accent"
|
class="w-full rounded-md border border-border bg-background px-3 py-2 text-text-primary placeholder:text-text-muted focus:outline-hidden focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if debouncedQ === ''}
|
{#if debouncedQ === ''}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<svelte:head><title>{pageTitle('Forgot password')}</title></svelte:head>
|
<svelte:head><title>{pageTitle('Forgot password')}</title></svelte:head>
|
||||||
|
|
||||||
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
||||||
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow">
|
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow-sm">
|
||||||
<h1 class="mb-6 text-center text-2xl font-semibold">Forgot password</h1>
|
<h1 class="mb-6 text-center text-2xl font-semibold">Forgot password</h1>
|
||||||
|
|
||||||
{#if !submitted}
|
{#if !submitted}
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
required
|
required
|
||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
bind:value={email}
|
bind:value={email}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -195,7 +195,7 @@
|
|||||||
bind:value={sortMode}
|
bind:value={sortMode}
|
||||||
aria-label="Sort genres"
|
aria-label="Sort genres"
|
||||||
class="rounded border border-border bg-surface px-2 py-1 text-sm text-text-primary
|
class="rounded border border-border bg-surface px-2 py-1 text-sm text-text-primary
|
||||||
focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
||||||
>
|
>
|
||||||
<option value="count">Most tracks</option>
|
<option value="count">Most tracks</option>
|
||||||
<option value="name">A–Z</option>
|
<option value="name">A–Z</option>
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
focus-visible:ring-2 focus-visible:ring-accent"
|
focus-visible:ring-2 focus-visible:ring-accent"
|
||||||
>
|
>
|
||||||
<span class="truncate text-text-primary">{g.genre}</span>
|
<span class="truncate text-text-primary">{g.genre}</span>
|
||||||
<span class="flex-shrink-0 text-sm text-text-secondary">
|
<span class="shrink-0 text-sm text-text-secondary">
|
||||||
{g.track_count}
|
{g.track_count}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<svelte:head><title>{pageTitle('Sign in')}</title></svelte:head>
|
<svelte:head><title>{pageTitle('Sign in')}</title></svelte:head>
|
||||||
|
|
||||||
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
||||||
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow">
|
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow-sm">
|
||||||
<h1 class="mb-6 text-center text-2xl font-semibold">Minstrel</h1>
|
<h1 class="mb-6 text-center text-2xl font-semibold">Minstrel</h1>
|
||||||
<form class="space-y-4" onsubmit={handleSubmit}>
|
<form class="space-y-4" onsubmit={handleSubmit}>
|
||||||
<label class="block">
|
<label class="block">
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
autocomplete="username"
|
autocomplete="username"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={username}
|
bind:value={username}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
autocomplete="current-password"
|
autocomplete="current-password"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -262,7 +262,7 @@
|
|||||||
</section>
|
</section>
|
||||||
<aside
|
<aside
|
||||||
aria-label="Queue"
|
aria-label="Queue"
|
||||||
class="hidden lg:flex w-96 xl:w-[28rem] flex-col border-l border-border bg-surface"
|
class="hidden lg:flex w-96 xl:w-md flex-col border-l border-border bg-surface"
|
||||||
>
|
>
|
||||||
<QueueList />
|
<QueueList />
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -235,7 +235,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={onReloadStale}
|
onclick={onReloadStale}
|
||||||
disabled={reloadingStale}
|
disabled={reloadingStale}
|
||||||
class="flex-shrink-0 rounded-md bg-action-secondary px-3 py-1 text-xs font-medium
|
class="shrink-0 rounded-md bg-action-secondary px-3 py-1 text-xs font-medium
|
||||||
text-action-fg disabled:opacity-50"
|
text-action-fg disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{reloadingStale ? 'Refreshing…' : 'Refresh'}
|
{reloadingStale ? 'Refreshing…' : 'Refresh'}
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if !editing}
|
{#if !editing}
|
||||||
<header class="mb-6 flex items-start gap-4">
|
<header class="mb-6 flex items-start gap-4">
|
||||||
<div class="h-32 w-32 flex-shrink-0 overflow-hidden rounded-md bg-surface-hover">
|
<div class="h-32 w-32 shrink-0 overflow-hidden rounded-md bg-surface-hover">
|
||||||
{#if pl.cover_url}
|
{#if pl.cover_url}
|
||||||
<img src={pl.cover_url} alt="" class="h-full w-full object-cover" />
|
<img src={pl.cover_url} alt="" class="h-full w-full object-cover" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
<svelte:head><title>{pageTitle('Register')}</title></svelte:head>
|
<svelte:head><title>{pageTitle('Register')}</title></svelte:head>
|
||||||
|
|
||||||
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
||||||
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow">
|
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow-sm">
|
||||||
<h1 class="mb-6 text-center text-2xl font-semibold">Create account</h1>
|
<h1 class="mb-6 text-center text-2xl font-semibold">Create account</h1>
|
||||||
<form class="space-y-4" onsubmit={handleSubmit}>
|
<form class="space-y-4" onsubmit={handleSubmit}>
|
||||||
<label class="block">
|
<label class="block">
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
required
|
required
|
||||||
minlength="3"
|
minlength="3"
|
||||||
maxlength="32"
|
maxlength="32"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={username}
|
bind:value={username}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
autocomplete="name"
|
autocomplete="name"
|
||||||
maxlength="64"
|
maxlength="64"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={displayName}
|
bind:value={displayName}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
required
|
required
|
||||||
minlength="8"
|
minlength="8"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
required
|
required
|
||||||
minlength="8"
|
minlength="8"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={confirmPassword}
|
bind:value={confirmPassword}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
required
|
required
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 font-mono outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 font-mono outline-hidden focus:border-accent"
|
||||||
bind:value={setupToken}
|
bind:value={setupToken}
|
||||||
/>
|
/>
|
||||||
<span class="mt-1 block text-xs text-text-secondary">
|
<span class="mt-1 block text-xs text-text-secondary">
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
bind:value={inviteToken}
|
bind:value={inviteToken}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<svelte:head><title>{pageTitle('Reset password')}</title></svelte:head>
|
<svelte:head><title>{pageTitle('Reset password')}</title></svelte:head>
|
||||||
|
|
||||||
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
<main class="flex min-h-screen items-center justify-center bg-background text-text-primary">
|
||||||
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow">
|
<div class="w-full max-w-sm rounded-lg border border-border bg-surface p-6 shadow-sm">
|
||||||
<h1 class="mb-6 text-center text-2xl font-semibold">Set a new password</h1>
|
<h1 class="mb-6 text-center text-2xl font-semibold">Set a new password</h1>
|
||||||
<form class="space-y-4" onsubmit={onSubmit}>
|
<form class="space-y-4" onsubmit={onSubmit}>
|
||||||
<label class="block">
|
<label class="block">
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
minlength="8"
|
minlength="8"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
bind:value={newPassword}
|
bind:value={newPassword}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label class="block">
|
<label class="block">
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
minlength="8"
|
minlength="8"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
bind:value={confirmPassword}
|
bind:value={confirmPassword}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent"
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
{#if error}
|
{#if error}
|
||||||
|
|||||||
@@ -467,7 +467,7 @@
|
|||||||
{#if (m.breakdown ?? []).length > 0}
|
{#if (m.breakdown ?? []).length > 0}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-1 rounded outline-none focus-visible:ring-1 focus-visible:ring-accent"
|
class="inline-flex items-center gap-1 rounded outline-hidden focus-visible:ring-1 focus-visible:ring-accent"
|
||||||
aria-expanded={expandedBreakdowns.has(m.key)}
|
aria-expanded={expandedBreakdowns.has(m.key)}
|
||||||
onclick={() => toggleBreakdown(m.key)}
|
onclick={() => toggleBreakdown(m.key)}
|
||||||
>
|
>
|
||||||
@@ -564,12 +564,12 @@
|
|||||||
<label class="text-sm text-text-secondary" for="profile-displayname">Display name</label>
|
<label class="text-sm text-text-secondary" for="profile-displayname">Display name</label>
|
||||||
<input id="profile-displayname" type="text" maxlength="64"
|
<input id="profile-displayname" type="text" maxlength="64"
|
||||||
bind:value={profileForm.displayName}
|
bind:value={profileForm.displayName}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent" />
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent" />
|
||||||
|
|
||||||
<label class="text-sm text-text-secondary" for="profile-email">Email</label>
|
<label class="text-sm text-text-secondary" for="profile-email">Email</label>
|
||||||
<input id="profile-email" type="email" autocomplete="email"
|
<input id="profile-email" type="email" autocomplete="email"
|
||||||
bind:value={profileForm.email}
|
bind:value={profileForm.email}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent" />
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent" />
|
||||||
</div>
|
</div>
|
||||||
<button type="button" disabled={profileSaving}
|
<button type="button" disabled={profileSaving}
|
||||||
onclick={onSaveProfile}
|
onclick={onSaveProfile}
|
||||||
@@ -587,18 +587,18 @@
|
|||||||
<input id="password-current" type="password" required minlength="8"
|
<input id="password-current" type="password" required minlength="8"
|
||||||
autocomplete="current-password"
|
autocomplete="current-password"
|
||||||
bind:value={passwordForm.current}
|
bind:value={passwordForm.current}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent" />
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent" />
|
||||||
|
|
||||||
<label class="text-sm text-text-secondary" for="password-new">New password</label>
|
<label class="text-sm text-text-secondary" for="password-new">New password</label>
|
||||||
<input id="password-new" type="password" required minlength="8"
|
<input id="password-new" type="password" required minlength="8"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
bind:value={passwordForm.new}
|
bind:value={passwordForm.new}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent" />
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent" />
|
||||||
|
|
||||||
<label class="text-sm text-text-secondary" for="password-confirm">Confirm</label>
|
<label class="text-sm text-text-secondary" for="password-confirm">Confirm</label>
|
||||||
<input id="password-confirm" type="password" required minlength="8"
|
<input id="password-confirm" type="password" required minlength="8"
|
||||||
bind:value={passwordForm.confirm}
|
bind:value={passwordForm.confirm}
|
||||||
class="w-full rounded border border-border bg-background px-3 py-2 outline-none focus:border-accent" />
|
class="w-full rounded border border-border bg-background px-3 py-2 outline-hidden focus:border-accent" />
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" disabled={passwordSaving}
|
<button type="submit" disabled={passwordSaving}
|
||||||
class="rounded bg-accent px-3 py-2 text-sm font-medium text-background disabled:opacity-60">
|
class="rounded bg-accent px-3 py-2 text-sm font-medium text-background disabled:opacity-60">
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
export default {
|
|
||||||
content: ['./src/**/*.{html,js,ts,svelte}'],
|
|
||||||
darkMode: 'class',
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
background: 'var(--fs-obsidian)',
|
|
||||||
surface: {
|
|
||||||
DEFAULT: 'var(--fs-iron)',
|
|
||||||
hover: 'var(--fs-slate)'
|
|
||||||
},
|
|
||||||
border: {
|
|
||||||
DEFAULT: 'var(--fs-pewter)'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
primary: 'var(--fs-parchment)',
|
|
||||||
secondary: 'var(--fs-vellum)',
|
|
||||||
muted: 'var(--fs-ash)'
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
primary: 'var(--fs-moss)',
|
|
||||||
secondary: 'var(--fs-bronze)',
|
|
||||||
destructive: 'var(--fs-oxblood)',
|
|
||||||
fg: 'var(--fs-on-action)'
|
|
||||||
},
|
|
||||||
accent: {
|
|
||||||
DEFAULT: 'var(--fs-accent)',
|
|
||||||
tint: 'color-mix(in srgb, var(--fs-accent) 12%, transparent)',
|
|
||||||
// All accent TEXT and icons use this, on a tint or not (#3150,
|
|
||||||
// #5318). The raw accent measures 1.97:1 on its own tint and 2.21 on
|
|
||||||
// slate at worst; this measures 5.03 and 5.62. The raw accent stays
|
|
||||||
// for borders, rings and fills, where it is a graphic, not text.
|
|
||||||
fg: 'var(--fs-accent-fg)'
|
|
||||||
},
|
|
||||||
warning: { DEFAULT: 'var(--fs-warning)', fg: 'var(--fs-warning-fg)' },
|
|
||||||
error: { DEFAULT: 'var(--fs-error)', fg: 'var(--fs-error-fg)' },
|
|
||||||
info: { DEFAULT: 'var(--fs-info)', fg: 'var(--fs-info-fg)' }
|
|
||||||
},
|
|
||||||
borderRadius: {
|
|
||||||
sm: 'var(--fs-radius-sm)',
|
|
||||||
md: 'var(--fs-radius-md)',
|
|
||||||
lg: 'var(--fs-radius-lg)',
|
|
||||||
xl: 'var(--fs-radius-xl)'
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
display: ['var(--fs-font-display)'],
|
|
||||||
sans: ['var(--fs-font-body)'],
|
|
||||||
mono: ['var(--fs-font-mono)']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: []
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user