added color/ theme settings, new config window

This commit is contained in:
2026-02-20 14:43:35 -05:00
parent fa6506ed5d
commit 88c3736110
43 changed files with 2314 additions and 1207 deletions
@@ -58,11 +58,11 @@ export class PlayerControls extends LitElement {
}
button:hover {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.active {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.repeat-one {
@@ -22,23 +22,23 @@ export class SeekBar extends LitElement {
--track-size: 6px;
flex: 1;
margin: 0 1em;
--wa-tooltip-background-color: #343a40;
--wa-tooltip-content-color: white;
--wa-tooltip-border-color: #343a40;
--wa-tooltip-background-color: var(--yj-bg-elevated, #343a40);
--wa-tooltip-content-color: var(--yj-text-primary, white);
--wa-tooltip-border-color: var(--yj-bg-elevated, #343a40);
--wa-tooltip-border-radius: 4px;
--wa-tooltip-font-size: 0.875em;
}
wa-slider::part(track) {
background: white;
background: var(--yj-text-primary, white);
}
wa-slider::part(indicator) {
background: yellow;
background: var(--yj-accent, yellow);
}
wa-slider::part(thumb) {
background: black;
background: var(--yj-bg-base, black);
}
#seek-bar-container {
@@ -35,8 +35,8 @@ export class VolumeControl extends LitElement {
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #1a1a1a;
border: 1px solid #333;
background: var(--yj-bg-surface, #1a1a1a);
border: 1px solid var(--yj-border-subtle, #333);
border-radius: 8px;
padding: 1em 0.5em;
margin-bottom: 0.5em;
@@ -52,16 +52,16 @@ export class VolumeControl extends LitElement {
}
wa-slider::part(track) {
background: white;
background: var(--yj-text-primary, white);
height: 120px;
}
wa-slider::part(indicator) {
background: yellow;
background: var(--yj-accent, yellow);
}
wa-slider::part(thumb) {
background: black;
background: var(--yj-bg-base, black);
}
`;
@@ -0,0 +1,410 @@
import { LitElement, html, css, nothing } from 'lit';
import { customElement, property } from 'lit/decorators.js';
/**
* Schema describing a single config field.
*
* The `type` controls which input widget is rendered:
* - `text` plain text input
* - `select` dropdown with `options`
* - `color` colour picker swatch
* - `directory` readonly text + browse button
* - `number` numeric input
* - `toggle` on/off switch
*/
export interface ConfigFieldSchema {
key: string;
label: string;
description?: string;
type:
| 'text'
| 'select'
| 'color'
| 'directory'
| 'number'
| 'toggle';
options?: { value: string; label: string }[];
disabled?: boolean;
}
export interface ConfigFieldChangeEvent {
key: string;
value: unknown;
}
@customElement('config-field')
export class ConfigField extends LitElement {
static override styles = css`
:host {
display: block;
margin-bottom: 1em;
color-scheme: inherit;
}
.field {
display: flex;
flex-direction: column;
gap: 0.35em;
}
label {
font-weight: 600;
font-size: 0.85em;
color: var(--yj-text-primary, #fff);
}
.description {
font-size: 0.75em;
color: var(--yj-text-tertiary, #888);
margin: 0;
}
.input-row {
display: flex;
align-items: center;
gap: 0.5em;
}
input[type='text'],
input[type='number'] {
background: var(--yj-bg-elevated, #343a40);
color: var(--yj-text-primary, #fff);
border: 1px solid var(--yj-border-subtle, #333);
border-radius: 4px;
padding: 0.4em 0.6em;
font-size: 0.85em;
font-family: inherit;
min-width: 0;
flex: 1;
}
input:focus {
outline: 1px solid var(--yj-accent, #ffd43b);
border-color: var(--yj-accent, #ffd43b);
}
input[readonly] {
opacity: 0.8;
cursor: default;
}
select {
background: var(--yj-bg-elevated, #343a40);
color: var(--yj-text-primary, #fff);
border: 1px solid var(--yj-border-subtle, #333);
border-radius: 4px;
padding: 0.4em 0.6em;
font-size: 0.85em;
font-family: inherit;
cursor: pointer;
flex: 1;
}
select:focus {
outline: 1px solid var(--yj-accent, #ffd43b);
border-color: var(--yj-accent, #ffd43b);
}
select option {
background: var(--yj-bg-elevated, #343a40);
color: var(--yj-text-primary, #fff);
}
button {
background: var(--yj-info, #4263eb);
color: #fff;
border: none;
border-radius: 4px;
padding: 0.4em 0.8em;
font-size: 0.85em;
cursor: pointer;
white-space: nowrap;
}
button:hover {
background: var(--yj-info-hover, #3b5bdb);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Colour picker */
.color-wrapper {
display: flex;
align-items: center;
gap: 0.75em;
}
input[type='color'] {
width: 2.5em;
height: 2.5em;
border: 2px solid var(--yj-border, #444);
border-radius: 4px;
padding: 0;
cursor: pointer;
background: none;
}
input[type='color']::-webkit-color-swatch-wrapper {
padding: 2px;
}
input[type='color']::-webkit-color-swatch {
border: none;
border-radius: 2px;
}
.color-hex {
font-family: monospace;
font-size: 0.85em;
color: var(--yj-text-secondary, #b3b3b3);
}
/* Toggle */
.toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.toggle-switch {
position: relative;
width: 2.5em;
height: 1.4em;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: var(--yj-bg-overlay, #495057);
border-radius: 1em;
transition: background 0.2s;
}
.toggle-slider::before {
content: '';
position: absolute;
height: 1em;
width: 1em;
left: 0.2em;
bottom: 0.2em;
background: white;
border-radius: 50%;
transition: transform 0.2s;
}
.toggle-switch input:checked + .toggle-slider {
background: var(--yj-accent, #ffd43b);
}
.toggle-switch input:checked + .toggle-slider::before {
transform: translateX(1.1em);
}
`;
@property({ attribute: false })
schema!: ConfigFieldSchema;
@property({ attribute: false })
value: unknown = '';
override render() {
if (!this.schema) return nothing;
return html`
<div class="field">
${this.schema.type === 'toggle'
? this.renderToggle()
: html`
<label>${this.schema.label}</label>
${this.renderInput()}
`}
${this.schema.description
? html`<p class="description">
${this.schema.description}
</p>`
: nothing}
</div>
`;
}
private renderInput() {
switch (this.schema.type) {
case 'text':
return this.renderText();
case 'number':
return this.renderNumber();
case 'select':
return this.renderSelect();
case 'color':
return this.renderColor();
case 'directory':
return this.renderDirectory();
default:
return html`<p>Unsupported field type</p>`;
}
}
private renderText() {
return html`
<input
type="text"
.value=${String(this.value ?? '')}
?disabled=${this.schema.disabled}
@change=${this.onTextChange}
/>
`;
}
private renderNumber() {
return html`
<input
type="number"
.value=${String(this.value ?? '')}
?disabled=${this.schema.disabled}
@change=${this.onTextChange}
/>
`;
}
private renderSelect() {
const current = String(this.value ?? '');
return html`
<select
?disabled=${this.schema.disabled}
@change=${this.onSelectChange}
>
${(this.schema.options ?? []).map(
(opt) => html`
<option
value=${opt.value}
?selected=${opt.value === current}
>
${opt.label}
</option>
`,
)}
</select>
`;
}
private renderColor() {
const hex = String(this.value ?? '#ffffff');
return html`
<div class="color-wrapper">
<input
type="color"
.value=${hex}
?disabled=${this.schema.disabled}
@input=${this.onColorInput}
/>
<span class="color-hex">${hex}</span>
</div>
`;
}
private renderDirectory() {
return html`
<div class="input-row">
<input
type="text"
.value=${String(this.value ?? '')}
readonly
/>
<button
?disabled=${this.schema.disabled}
@click=${this.onBrowseClick}
>
Browse
</button>
</div>
`;
}
private renderToggle() {
const checked = Boolean(this.value);
return html`
<div class="toggle-row">
<label>${this.schema.label}</label>
<label class="toggle-switch">
<input
type="checkbox"
?checked=${checked}
?disabled=${this.schema.disabled}
@change=${this.onToggleChange}
/>
<span class="toggle-slider"></span>
</label>
</div>
`;
}
// ===================================================================
// EVENT DISPATCHERS
// ===================================================================
private emitChange(value: unknown): void {
this.dispatchEvent(
new CustomEvent<ConfigFieldChangeEvent>(
'config-change',
{
detail: {
key: this.schema.key,
value,
},
bubbles: true,
composed: true,
},
),
);
}
private onTextChange = (e: Event) => {
const input = e.target as HTMLInputElement;
this.emitChange(input.value);
};
private onSelectChange = (e: Event) => {
const select = e.target as HTMLSelectElement;
this.emitChange(select.value);
};
private onColorInput = (e: Event) => {
const input = e.target as HTMLInputElement;
this.value = input.value;
this.emitChange(input.value);
};
private onBrowseClick = () => {
this.dispatchEvent(
new CustomEvent('config-browse', {
detail: { key: this.schema.key },
bubbles: true,
composed: true,
}),
);
};
private onToggleChange = (e: Event) => {
const input = e.target as HTMLInputElement;
this.emitChange(input.checked);
};
}
declare global {
interface HTMLElementTagNameMap {
'config-field': ConfigField;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,69 @@
import { LitElement, html, css, nothing } from 'lit';
import { customElement, property } from 'lit/decorators.js';
/**
* A visual grouping wrapper for config fields.
* Renders a heading, optional description, and a slot for fields.
*/
@customElement('config-section')
export class ConfigSection extends LitElement {
static override styles = css`
:host {
display: block;
margin-bottom: 1.5em;
}
.section {
background: var(--yj-bg-surface, #212529);
border: 1px solid var(--yj-border-subtle, #333);
border-radius: 6px;
padding: 1.25em;
}
h3 {
margin: 0 0 0.25em;
font-size: 1em;
font-weight: 700;
color: var(--yj-text-primary, #fff);
}
.description {
font-size: 0.8em;
color: var(--yj-text-tertiary, #888);
margin: 0 0 1em;
}
.fields {
display: flex;
flex-direction: column;
}
`;
@property({ type: String })
heading = '';
@property({ type: String })
description = '';
override render() {
return html`
<div class="section">
<h3>${this.heading}</h3>
${this.description
? html`<p class="description">
${this.description}
</p>`
: nothing}
<div class="fields">
<slot></slot>
</div>
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'config-section': ConfigSection;
}
}
@@ -67,7 +67,7 @@ export class AlbumDropdown extends LitElement {
}
.album-dropdown {
background-color: #343a40;
background-color: var(--yj-bg-elevated, #343a40);
border-radius: 0 0 4px 4px;
padding: 12px 16px;
box-sizing: border-box;
@@ -81,7 +81,7 @@ export class AlbumDropdown extends LitElement {
height: 0;
border-left: 9px solid transparent;
border-right: 9px solid transparent;
border-bottom: 8px solid #343a40;
border-bottom: 8px solid var(--yj-bg-elevated, #343a40);
}
.dropdown-tracks {
@@ -103,11 +103,9 @@ export class AlbumDropdown extends LitElement {
}
.track-row:hover {
background-color: rgba(
255,
255,
255,
0.05
background-color: var(
--yj-hover-overlay,
rgba(255, 255, 255, 0.05)
);
}
@@ -121,13 +119,11 @@ export class AlbumDropdown extends LitElement {
}
.track-row.active {
background-color: rgba(
255,
212,
59,
0.1
background-color: var(
--yj-accent-bg,
rgba(255, 212, 59, 0.1)
);
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.track-row.selected.active {
@@ -140,14 +136,14 @@ export class AlbumDropdown extends LitElement {
}
.track-number {
color: #888;
color: var(--yj-text-tertiary, #888);
min-width: 22px;
text-align: right;
flex-shrink: 0;
}
.track-title {
color: #fff;
color: var(--yj-text-primary, #fff);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -156,7 +152,7 @@ export class AlbumDropdown extends LitElement {
}
.track-duration {
color: #888;
color: var(--yj-text-tertiary, #888);
flex-shrink: 0;
margin-left: auto;
}
@@ -203,21 +203,21 @@ export class CoverGrid extends LitElement {
}
.album-card:hover {
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.1));
}
.album-card.selected {
outline: 2px solid #ffd43b;
outline: 2px solid var(--yj-accent, #ffd43b);
outline-offset: 2px;
}
.album-card.expanded {
background-color: #343a40;
background-color: var(--yj-bg-elevated, #343a40);
border-radius: 8px 8px 0 0;
}
.album-card:focus-visible {
outline: 2px solid #ffd43b;
outline: 2px solid var(--yj-accent, #ffd43b);
outline-offset: 2px;
}
@@ -227,7 +227,7 @@ export class CoverGrid extends LitElement {
aspect-ratio: 1;
border-radius: 4px;
overflow: hidden;
background-color: #282828;
background-color: var(--yj-bg-surface, #282828);
transition: scale 0.15s ease;
}
@@ -263,8 +263,8 @@ export class CoverGrid extends LitElement {
.select-toggle.checked {
opacity: 1;
background-color: #ffd43b;
border-color: #ffd43b;
background-color: var(--yj-accent, #ffd43b);
border-color: var(--yj-accent, #ffd43b);
}
.check-icon {
@@ -289,10 +289,10 @@ export class CoverGrid extends LitElement {
justify-content: center;
background: linear-gradient(
135deg,
#404040 0%,
#282828 100%
var(--yj-bg-overlay, #404040) 0%,
var(--yj-bg-surface, #282828) 100%
);
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
font-size: var(--placeholder-font, 48px);
}
@@ -310,7 +310,7 @@ export class CoverGrid extends LitElement {
.album-name {
font-size: var(--album-name-font, 14px);
font-weight: 400;
color: #fff;
color: var(--yj-text-primary, #fff);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -318,7 +318,7 @@ export class CoverGrid extends LitElement {
.artist-name {
font-size: var(--artist-name-font, 12px);
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -326,7 +326,7 @@ export class CoverGrid extends LitElement {
}
.album-year {
color: #888;
color: var(--yj-text-tertiary, #888);
}
/* ========================================
@@ -338,7 +338,7 @@ export class CoverGrid extends LitElement {
justify-content: center;
align-items: center;
padding: 32px;
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
}
.empty-state {
@@ -347,7 +347,7 @@ export class CoverGrid extends LitElement {
justify-content: center;
align-items: center;
padding: 48px;
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
text-align: center;
}
@@ -364,8 +364,8 @@ export class CoverGrid extends LitElement {
}
.context-menu-panel {
background-color: #343a40;
border: 1px solid #444;
background-color: var(--yj-bg-elevated, #343a40);
border: 1px solid var(--yj-border, #444);
border-radius: 6px;
padding: 4px 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
@@ -377,16 +377,14 @@ export class CoverGrid extends LitElement {
}
.context-menu-panel wa-dropdown-item {
--wa-color-text-normal: #fff;
--wa-color-text-normal: var(--yj-text-primary, #fff);
font-size: 13px;
}
.context-menu-panel wa-dropdown-item:hover {
background-color: rgba(
255,
255,
255,
0.1
background-color: var(
--yj-hover-overlay,
rgba(255, 255, 255, 0.1)
);
}
@@ -242,7 +242,7 @@ export class LibraryManager extends LitElement {
:host {
display: block;
padding: 1.5em;
color: #e9ecef;
color: var(--yj-text-primary, #e9ecef);
font-family: system-ui, -apple-system, sans-serif;
overflow-y: auto;
}
@@ -251,13 +251,13 @@ export class LibraryManager extends LitElement {
margin: 0 0 1em 0;
font-size: 1.4em;
font-weight: 600;
color: #f8f9fa;
color: var(--yj-text-primary, #f8f9fa);
}
.section {
margin-bottom: 2em;
padding: 1.25em;
background: #2b3035;
background: var(--yj-bg-surface, #2b3035);
border-radius: 8px;
}
@@ -265,7 +265,7 @@ export class LibraryManager extends LitElement {
margin: 0 0 0.75em 0;
font-size: 1em;
font-weight: 600;
color: #dee2e6;
color: var(--yj-text-primary, #dee2e6);
}
.section-header {
@@ -282,7 +282,7 @@ export class LibraryManager extends LitElement {
.section-description {
margin: 0 0 1em 0;
font-size: 0.85em;
color: #868e96;
color: var(--yj-text-tertiary, #868e96);
line-height: 1.4;
}
@@ -296,10 +296,10 @@ export class LibraryManager extends LitElement {
.directory-path {
flex: 1;
padding: 0.5em 0.75em;
background: #1a1d20;
border: 1px solid #495057;
background: var(--yj-bg-elevated, #1a1d20);
border: 1px solid var(--yj-bg-overlay, #495057);
border-radius: 4px;
color: #adb5bd;
color: var(--yj-text-secondary, #adb5bd);
font-size: 0.85em;
font-family: monospace;
overflow: hidden;
@@ -309,7 +309,7 @@ export class LibraryManager extends LitElement {
}
.directory-path.has-value {
color: #e9ecef;
color: var(--yj-text-primary, #e9ecef);
}
button {
@@ -329,57 +329,57 @@ export class LibraryManager extends LitElement {
}
.btn-primary {
background: #4263eb;
background: var(--yj-info, #4263eb);
color: white;
}
.btn-primary:hover:not(:disabled) {
background: #3b5bdb;
background: var(--yj-info-hover, #3b5bdb);
}
.btn-success {
background: #2f9e44;
background: var(--yj-success, #2f9e44);
color: white;
}
.btn-success:hover:not(:disabled) {
background: #2b8a3e;
background: var(--yj-success-hover, #2b8a3e);
}
.btn-warning {
background: #e8590c;
background: var(--yj-warning, #e8590c);
color: white;
}
.btn-warning:hover:not(:disabled) {
background: #d9480f;
background: var(--yj-warning-hover, #d9480f);
}
.btn-danger {
background: #e03131;
background: var(--yj-error, #e03131);
color: white;
}
.btn-danger:hover:not(:disabled) {
background: #c92a2a;
background: var(--yj-error-hover, #c92a2a);
}
.btn-ghost {
background: transparent;
color: #868e96;
color: var(--yj-text-tertiary, #868e96);
padding: 0.3em 0.75em;
font-size: 0.75em;
border: 1px solid #495057;
border: 1px solid var(--yj-bg-overlay, #495057);
}
.btn-ghost:hover:not(:disabled) {
background: #495057;
color: #e9ecef;
background: var(--yj-bg-overlay, #495057);
color: var(--yj-text-primary, #e9ecef);
}
.btn-ghost.copied {
border-color: #2f9e44;
color: #2f9e44;
border-color: var(--yj-success, #2f9e44);
color: var(--yj-success, #2f9e44);
}
.setting-row {
@@ -390,16 +390,16 @@ export class LibraryManager extends LitElement {
}
.setting-row label {
color: #adb5bd;
color: var(--yj-text-secondary, #adb5bd);
min-width: 8em;
}
.setting-row select {
padding: 0.4em 0.6em;
background: #1a1d20;
border: 1px solid #495057;
background: var(--yj-bg-elevated, #1a1d20);
border: 1px solid var(--yj-bg-overlay, #495057);
border-radius: 4px;
color: #e9ecef;
color: var(--yj-text-primary, #e9ecef);
font-size: 1em;
font-family: inherit;
cursor: pointer;
@@ -408,12 +408,12 @@ export class LibraryManager extends LitElement {
.setting-row select:focus {
outline: none;
border-color: #4263eb;
border-color: var(--yj-info, #4263eb);
}
.setting-row select option {
background: #2b3035;
color: #e9ecef;
background: var(--yj-bg-surface, #2b3035);
color: var(--yj-text-primary, #e9ecef);
}
.scan-actions {
@@ -425,15 +425,15 @@ export class LibraryManager extends LitElement {
.status-bar {
margin-top: 1.5em;
padding: 0.75em 1em;
background: #1a1d20;
background: var(--yj-bg-elevated, #1a1d20);
border-radius: 4px;
font-size: 0.85em;
color: #868e96;
color: var(--yj-text-tertiary, #868e96);
min-height: 1.2em;
}
.status-bar.active {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
/* --- Metrics tree --- */
@@ -453,7 +453,7 @@ export class LibraryManager extends LitElement {
cursor: pointer;
padding: 0.25em 0;
font-size: 0.85em;
color: #ced4da;
color: var(--yj-text-secondary, #ced4da);
list-style: none;
}
@@ -484,21 +484,21 @@ export class LibraryManager extends LitElement {
}
.metric-label {
color: #adb5bd;
color: var(--yj-text-secondary, #adb5bd);
}
.metric-value {
color: #e9ecef;
color: var(--yj-text-primary, #e9ecef);
font-family: monospace;
font-weight: 500;
}
.metric-value.highlight {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.metric-note {
color: #868e96;
color: var(--yj-text-tertiary, #868e96);
font-size: 0.75em;
font-style: italic;
padding-left: 1.35em;
@@ -513,11 +513,11 @@ export class LibraryManager extends LitElement {
}
.count-label {
color: #adb5bd;
color: var(--yj-text-secondary, #adb5bd);
}
.count-value {
color: #e9ecef;
color: var(--yj-text-primary, #e9ecef);
font-family: monospace;
}
`;
@@ -48,14 +48,14 @@ export class NowPlaying extends LitElement {
.cover-placeholder {
width: 100%;
height: 100%;
background-color: #000;
background-color: var(--yj-bg-base, #000);
display: flex;
align-items: center;
justify-content: center;
}
.cover-placeholder wa-icon {
color: #fff;
color: var(--yj-text-primary, #fff);
font-size: 24px;
}
@@ -76,7 +76,7 @@ export class NowPlaying extends LitElement {
.track-artist {
font-size: 12px;
color: #666;
color: var(--yj-text-tertiary, #666);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -96,7 +96,7 @@ export class NowPlaying extends LitElement {
.resize-handle:hover,
.resize-handle.dragging {
background-color: #6c757d;
background-color: var(--yj-text-tertiary, #6c757d);
}
`;
@@ -37,8 +37,8 @@ export class PlaylistPicker extends LitElement {
}
.picker-panel {
background-color: #343a40;
border: 1px solid #444;
background-color: var(--yj-bg-elevated, #343a40);
border: 1px solid var(--yj-border, #444);
border-radius: 6px;
padding: 4px 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
@@ -49,17 +49,17 @@ export class PlaylistPicker extends LitElement {
.picker-panel wa-dropdown-item {
cursor: pointer;
--wa-color-text-normal: #fff;
--wa-color-text-normal: var(--yj-text-primary, #fff);
font-size: 13px;
}
.picker-panel wa-dropdown-item:hover {
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
}
.separator {
height: 1px;
background: #555;
background: var(--yj-border-subtle, #555);
margin: 4px 0;
}
@@ -71,10 +71,10 @@ export class PlaylistPicker extends LitElement {
}
.create-form input {
background: #2a2d30;
border: 1px solid #555;
background: var(--yj-bg-surface, #2b3035);
border: 1px solid var(--yj-border-subtle, #555);
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 6px 8px;
font-size: 13px;
outline: none;
@@ -82,11 +82,11 @@ export class PlaylistPicker extends LitElement {
}
.create-form input:focus {
border-color: #ffd43b;
border-color: var(--yj-accent, #ffd43b);
}
.create-form input::placeholder {
color: #888;
color: var(--yj-text-tertiary, #888);
}
.button-row {
@@ -96,10 +96,10 @@ export class PlaylistPicker extends LitElement {
}
.button-row button {
background: #495057;
background: var(--yj-bg-overlay, #495057);
border: none;
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
@@ -111,23 +111,23 @@ export class PlaylistPicker extends LitElement {
}
.button-row button.primary {
background: #ffd43b;
background: var(--yj-accent, #ffd43b);
color: #000;
}
.button-row button.primary:hover {
background: #ffe066;
background: var(--yj-accent-hover, #ffe066);
}
.button-row button.primary:disabled {
background: #665a1e;
color: #888;
background: var(--yj-accent-muted, #665a1e);
color: var(--yj-text-tertiary, #888);
cursor: not-allowed;
}
.empty-message {
padding: 8px 12px;
color: #888;
color: var(--yj-text-tertiary, #888);
font-size: 13px;
}
`;
@@ -205,21 +205,21 @@ export class PlaylistView
align-items: center;
padding: 16px;
flex-shrink: 0;
border-bottom: 1px solid #333;
border-bottom: 1px solid var(--yj-border-subtle, #333);
}
.header h2 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #fff;
color: var(--yj-text-primary, #fff);
}
.new-playlist-button {
background: none;
border: 1px solid #555;
border: 1px solid var(--yj-border-subtle, #555);
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 6px 12px;
font-size: 13px;
cursor: pointer;
@@ -230,8 +230,8 @@ export class PlaylistView
}
.new-playlist-button:hover {
border-color: #ffd43b;
color: #ffd43b;
border-color: var(--yj-accent, #ffd43b);
color: var(--yj-accent, #ffd43b);
}
.create-form {
@@ -239,16 +239,16 @@ export class PlaylistView
align-items: center;
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid #333;
border-bottom: 1px solid var(--yj-border-subtle, #333);
flex-shrink: 0;
}
.create-form input {
flex: 1;
background: #2a2d30;
border: 1px solid #555;
background: var(--yj-bg-surface, #2b3035);
border: 1px solid var(--yj-border-subtle, #555);
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 6px 10px;
font-size: 13px;
outline: none;
@@ -256,18 +256,18 @@ export class PlaylistView
}
.create-form input:focus {
border-color: #ffd43b;
border-color: var(--yj-accent, #ffd43b);
}
.create-form input::placeholder {
color: #888;
color: var(--yj-text-tertiary, #888);
}
.create-form button {
background: #495057;
background: var(--yj-bg-overlay, #495057);
border: none;
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 6px 12px;
font-size: 13px;
cursor: pointer;
@@ -275,21 +275,21 @@ export class PlaylistView
}
.create-form button:hover {
background: #5a6268;
background: var(--yj-bg-overlay, #5a6268);
}
.create-form button.primary {
background: #ffd43b;
background: var(--yj-accent, #ffd43b);
color: #000;
}
.create-form button.primary:hover {
background: #ffe066;
background: var(--yj-accent-hover, #ffe066);
}
.create-form button.primary:disabled {
background: #665a1e;
color: #888;
background: var(--yj-accent-muted, #665a1e);
color: var(--yj-text-tertiary, #888);
cursor: not-allowed;
}
@@ -303,7 +303,7 @@ export class PlaylistView
.playlist-item {
border-bottom: 1px solid
rgba(255, 255, 255, 0.05);
var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
}
.playlist-header {
@@ -316,18 +316,18 @@ export class PlaylistView
}
.playlist-header:hover {
background-color: rgba(255, 255, 255, 0.05);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
}
.playlist-item.drag-over > .playlist-header {
background-color: rgba(255, 212, 59, 0.15);
outline: 1px dashed #ffd43b;
background-color: var(--yj-accent-bg-strong, rgba(255, 212, 59, 0.15));
outline: 1px dashed var(--yj-accent, #ffd43b);
outline-offset: -1px;
}
.chevron {
font-size: 14px;
color: #888;
color: var(--yj-text-tertiary, #888);
flex-shrink: 0;
transition: transform 0.15s ease;
}
@@ -338,13 +338,13 @@ export class PlaylistView
.playlist-icon {
font-size: 18px;
color: #888;
color: var(--yj-text-tertiary, #888);
flex-shrink: 0;
}
.playlist-name {
font-size: 14px;
color: #fff;
color: var(--yj-text-primary, #fff);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -353,7 +353,7 @@ export class PlaylistView
.track-count {
font-size: 11px;
color: #666;
color: var(--yj-text-tertiary, #666);
flex-shrink: 0;
}
@@ -370,9 +370,9 @@ export class PlaylistView
.play-all-button {
background: none;
border: 1px solid #555;
border: 1px solid var(--yj-border-subtle, #555);
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
@@ -383,8 +383,8 @@ export class PlaylistView
}
.play-all-button:hover {
border-color: #ffd43b;
color: #ffd43b;
border-color: var(--yj-accent, #ffd43b);
color: var(--yj-accent, #ffd43b);
}
.track-item {
@@ -396,20 +396,20 @@ export class PlaylistView
}
.track-item:hover {
background-color: rgba(255, 255, 255, 0.05);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
}
.track-item.selected {
background-color: rgba(100, 160, 255, 0.15);
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
.track-item.active {
background-color: rgba(255, 212, 59, 0.1);
color: #ffd43b;
background-color: var(--yj-accent-bg, rgba(255, 212, 59, 0.1));
color: var(--yj-accent, #ffd43b);
}
.track-item.selected.active {
background-color: rgba(100, 160, 255, 0.15);
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
.track-item.phantom {
@@ -424,7 +424,7 @@ export class PlaylistView
.phantom-badge {
display: inline-block;
font-size: 10px;
color: #e67700;
color: var(--yj-warning, #e67700);
background: rgba(230, 119, 0, 0.15);
padding: 1px 6px;
border-radius: 3px;
@@ -438,7 +438,7 @@ export class PlaylistView
.tracks-empty {
padding: 12px 0;
color: #666;
color: var(--yj-text-tertiary, #666);
font-size: 12px;
}
@@ -447,7 +447,7 @@ export class PlaylistView
justify-content: center;
align-items: center;
padding: 32px;
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
}
.empty-state {
@@ -456,7 +456,7 @@ export class PlaylistView
align-items: center;
justify-content: center;
padding: 48px 20px;
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
text-align: center;
gap: 8px;
}
@@ -474,8 +474,8 @@ export class PlaylistView
}
.context-menu-panel {
background-color: #343a40;
border: 1px solid #444;
background-color: var(--yj-bg-elevated, #343a40);
border: 1px solid var(--yj-border, #444);
border-radius: 6px;
padding: 4px 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
@@ -484,7 +484,7 @@ export class PlaylistView
.context-menu-panel wa-dropdown-item {
cursor: pointer;
--wa-color-text-normal: #fff;
--wa-color-text-normal: var(--yj-text-primary, #fff);
font-size: 13px;
}
@@ -512,10 +512,10 @@ export class PlaylistView
.rename-input {
flex: 1;
background: #2a2d30;
border: 1px solid #ffd43b;
background: var(--yj-bg-surface, #2b3035);
border: 1px solid var(--yj-accent, #ffd43b);
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 4px 8px;
font-size: 14px;
outline: none;
@@ -525,9 +525,9 @@ export class PlaylistView
.import-button {
background: none;
border: 1px solid #555;
border: 1px solid var(--yj-border-subtle, #555);
border-radius: 4px;
color: #fff;
color: var(--yj-text-primary, #fff);
padding: 6px 12px;
font-size: 13px;
cursor: pointer;
@@ -538,8 +538,8 @@ export class PlaylistView
}
.import-button:hover {
border-color: #ffd43b;
color: #ffd43b;
border-color: var(--yj-accent, #ffd43b);
color: var(--yj-accent, #ffd43b);
}
`;
@@ -153,7 +153,7 @@ export class QueuePanel
flex-shrink: 0;
width: 0;
overflow: hidden;
background-color: #212529;
background-color: var(--yj-bg-surface, #212529);
display: flex;
flex-direction: row;
}
@@ -163,7 +163,7 @@ export class QueuePanel
--queue-width,
${unsafeCSS(DEFAULT_WIDTH)}px
);
border-left: 1px solid #333;
border-left: 1px solid var(--yj-border-subtle, #333);
}
.resize-handle {
@@ -180,7 +180,7 @@ export class QueuePanel
.resize-handle:hover,
.resize-handle.dragging {
background-color: #6c757d;
background-color: var(--yj-text-tertiary, #6c757d);
}
.panel-content {
@@ -196,7 +196,7 @@ export class QueuePanel
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #333;
border-bottom: 1px solid var(--yj-border-subtle, #333);
flex-shrink: 0;
}
@@ -223,11 +223,11 @@ export class QueuePanel
}
.header-action-button:hover {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.header-action-button:disabled {
color: #555;
color: var(--yj-border-subtle, #555);
cursor: not-allowed;
}
@@ -247,7 +247,7 @@ export class QueuePanel
padding: 8px 16px;
gap: 12px;
border-bottom: 1px solid
rgba(255, 255, 255, 0.05);
var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
cursor: default;
user-select: none;
width: 100%;
@@ -255,30 +255,30 @@ export class QueuePanel
}
.track-item:hover {
background-color: rgba(255, 255, 255, 0.05);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
}
.track-item.selected {
background-color: rgba(100, 160, 255, 0.15);
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
.track-item.active {
background-color: rgba(255, 212, 59, 0.1);
background-color: var(--yj-accent-bg, rgba(255, 212, 59, 0.1));
}
.track-item.selected.active {
background-color: rgba(100, 160, 255, 0.15);
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
.track-position {
font-size: 12px;
color: #888;
color: var(--yj-text-tertiary, #888);
min-width: 20px;
text-align: right;
}
.track-item.active .track-position {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.track-details {
@@ -297,12 +297,12 @@ export class QueuePanel
}
.track-item.active .track-title {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.track-artist {
font-size: 11px;
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -311,7 +311,7 @@ export class QueuePanel
.remove-button {
background: none;
border: none;
color: #888;
color: var(--yj-text-tertiary, #888);
cursor: pointer;
padding: 4px;
display: flex;
@@ -325,11 +325,11 @@ export class QueuePanel
}
.remove-button:hover {
color: #ff6b6b;
color: var(--yj-error, #ff6b6b);
}
.panel-content.drag-over {
outline: 2px dashed #ffd43b;
outline: 2px dashed var(--yj-accent, #ffd43b);
outline-offset: -2px;
}
@@ -338,7 +338,7 @@ export class QueuePanel
padding: 12px 16px;
text-align: center;
font-size: 12px;
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
border-bottom: 1px solid
rgba(255, 212, 59, 0.2);
}
@@ -355,7 +355,7 @@ export class QueuePanel
left: 8px;
right: 8px;
height: 2px;
background: #ffd43b;
background: var(--yj-accent, #ffd43b);
border-radius: 1px;
z-index: 5;
pointer-events: none;
@@ -368,7 +368,7 @@ export class QueuePanel
left: 8px;
right: 8px;
height: 2px;
background: #ffd43b;
background: var(--yj-accent, #ffd43b);
border-radius: 1px;
z-index: 5;
pointer-events: none;
@@ -380,7 +380,7 @@ export class QueuePanel
align-items: center;
justify-content: center;
padding: 40px 20px;
color: #b3b3b3;
color: var(--yj-text-secondary, #b3b3b3);
text-align: center;
gap: 8px;
}
@@ -394,8 +394,8 @@ export class QueuePanel
}
.context-menu-panel {
background-color: #343a40;
border: 1px solid #444;
background-color: var(--yj-bg-elevated, #343a40);
border: 1px solid var(--yj-border, #444);
border-radius: 6px;
padding: 4px 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
@@ -404,7 +404,7 @@ export class QueuePanel
.context-menu-panel wa-dropdown-item {
cursor: pointer;
--wa-color-text-normal: #fff;
--wa-color-text-normal: var(--yj-text-primary, #fff);
font-size: 13px;
}
+29 -14
View File
@@ -1,13 +1,15 @@
import { LitElement, html, css } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@awesome.me/webawesome/dist/components/icon/icon.js';
import type { DragActiveDetail } from '@utils/drag-controller';
type View = 'home' | 'libraries' | 'playlists' | 'artists' | 'albums' | 'tracks';
type View = 'home' | 'libraries' | 'playlists' | 'artists' | 'albums' | 'tracks' | 'settings';
interface NavItem {
id: View;
label: string;
icon: string;
}
const MIN_WIDTH = 120;
@@ -21,7 +23,7 @@ export class AppSidebar extends LitElement {
display: block;
position: relative;
height: 100%;
background-color: #212529;
background-color: var(--yj-bg-surface, #212529);
min-width: ${MIN_WIDTH}px;
max-width: ${MAX_WIDTH}px;
}
@@ -40,7 +42,7 @@ export class AppSidebar extends LitElement {
.resize-handle:hover,
.resize-handle.dragging {
background-color: #6c757d;
background-color: var(--yj-text-tertiary, #6c757d);
}
ul {
@@ -50,19 +52,28 @@ export class AppSidebar extends LitElement {
}
li {
text-align: left;
display: flex;
align-items: center;
gap: 0.6em;
border-radius: 5px;
padding: 0.5em;
cursor: pointer;
transition: background-color 0.15s ease;
}
li wa-icon {
font-size: 0.9em;
flex-shrink: 0;
width: 1.2em;
text-align: center;
}
li:hover {
background-color: #343a40;
background-color: var(--yj-bg-elevated, #343a40);
}
li.active {
background-color: #495057;
background-color: var(--yj-bg-overlay, #495057);
}
li p {
@@ -73,8 +84,8 @@ export class AppSidebar extends LitElement {
}
li.drag-hover {
background-color: rgba(255, 212, 59, 0.15);
outline: 1px dashed #ffd43b;
background-color: var(--yj-accent-bg-strong, rgba(255, 212, 59, 0.15));
outline: 1px dashed var(--yj-accent, #ffd43b);
outline-offset: -1px;
}
`;
@@ -101,12 +112,13 @@ export class AppSidebar extends LitElement {
> | null = null;
private navItems: NavItem[] = [
{ id: 'home', label: 'Home' },
{ id: 'libraries', label: 'Libraries' },
{ id: 'playlists', label: 'Playlists' },
{ id: 'artists', label: 'Artists' },
{ id: 'albums', label: 'Albums' },
{ id: 'tracks', label: 'Tracks' },
{ id: 'home', label: 'Home', icon: 'house' },
{ id: 'libraries', label: 'Libraries', icon: 'folder-open' },
{ id: 'playlists', label: 'Playlists', icon: 'list' },
{ id: 'artists', label: 'Artists', icon: 'user-group' },
{ id: 'albums', label: 'Albums', icon: 'compact-disc' },
{ id: 'tracks', label: 'Tracks', icon: 'music' },
{ id: 'settings', label: 'Settings', icon: 'gear' },
];
override connectedCallback() {
@@ -179,6 +191,9 @@ export class AppSidebar extends LitElement {
@drop=${(e: DragEvent) =>
this.onNavDrop(e)}
>
<wa-icon
name=${item.icon}
></wa-icon>
<p>${item.label}</p>
</li>
`;
@@ -63,14 +63,14 @@ export class TrackInfo extends LitElement {
.cover-placeholder {
width: 100%;
height: 100%;
background-color: #2a2d30;
background-color: var(--yj-bg-elevated, #2a2d30);
display: flex;
align-items: center;
justify-content: center;
}
.cover-placeholder wa-icon {
color: #666;
color: var(--yj-text-tertiary, #666);
font-size: 18px;
}
@@ -85,7 +85,7 @@ export class TrackInfo extends LitElement {
.title {
font-size: 13px;
font-weight: 500;
color: #fff;
color: var(--yj-text-primary, #fff);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -93,7 +93,7 @@ export class TrackInfo extends LitElement {
.secondary {
font-size: 11px;
color: #888;
color: var(--yj-text-tertiary, #888);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -101,7 +101,7 @@ export class TrackInfo extends LitElement {
.duration {
font-size: 12px;
color: #888;
color: var(--yj-text-tertiary, #888);
flex-shrink: 0;
font-variant-numeric: tabular-nums;
}
@@ -252,8 +252,8 @@ export class TrackList extends LitElement implements SelectionHost {
grid-template-columns: var(--grid-cols, 1fr 1fr 80px);
padding: 8px;
font-weight: bold;
color: #fff;
border-bottom: 1px solid #666;
color: var(--yj-text-primary, #fff);
border-bottom: 1px solid var(--yj-text-tertiary, #666);
flex-shrink: 0;
}
@@ -277,7 +277,7 @@ export class TrackList extends LitElement implements SelectionHost {
width: 1px;
cursor: col-resize;
pointer-events: auto;
background-color: #444;
background-color: var(--yj-border, #444);
transition: background-color 0.15s ease;
}
@@ -292,7 +292,7 @@ export class TrackList extends LitElement implements SelectionHost {
.col-resize-handle:hover,
.col-resize-handle.active {
background-color: #6c757d;
background-color: var(--yj-text-tertiary, #6c757d);
}
lit-virtualizer {
@@ -307,7 +307,7 @@ export class TrackList extends LitElement implements SelectionHost {
grid-template-columns: var(--grid-cols, 1fr 1fr 80px);
font-size: 12px;
padding: 8px;
border-bottom: 1px solid #333;
border-bottom: 1px solid var(--yj-border-subtle, #333);
align-items: center;
width: 100%;
cursor: default;
@@ -324,23 +324,23 @@ export class TrackList extends LitElement implements SelectionHost {
}
.track-row:hover {
background-color: rgba(255, 255, 255, 0.05);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.05));
}
.track-row.selected {
background-color: rgba(100, 160, 255, 0.15);
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
.track-row.active {
background-color: rgba(255, 212, 59, 0.1);
background-color: var(--yj-accent-bg, rgba(255, 212, 59, 0.1));
}
.track-row.active {
color: #ffd43b;
color: var(--yj-accent, #ffd43b);
}
.track-row.selected.active {
background-color: rgba(100, 160, 255, 0.15);
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
.track-name {
@@ -363,8 +363,8 @@ export class TrackList extends LitElement implements SelectionHost {
}
.context-menu-panel {
background-color: #343a40;
border: 1px solid #444;
background-color: var(--yj-bg-elevated, #343a40);
border: 1px solid var(--yj-border, #444);
border-radius: 6px;
padding: 4px 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
@@ -376,12 +376,12 @@ export class TrackList extends LitElement implements SelectionHost {
}
.context-menu-panel wa-dropdown-item {
--wa-color-text-normal: #fff;
--wa-color-text-normal: var(--yj-text-primary, #fff);
font-size: 13px;
}
.context-menu-panel wa-dropdown-item:hover {
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--yj-hover-overlay, rgba(255, 255, 255, 0.1));
}
.submenu-item {