chore: update Angular cache and TypeScript build info files
This commit is contained in:
parent
3f04191623
commit
5d6aa17b81
2
.angular/cache/20.2.2/app/.tsbuildinfo
vendored
2
.angular/cache/20.2.2/app/.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
BIN
db/newtube.db
BIN
db/newtube.db
Binary file not shown.
@ -251,37 +251,40 @@ export class HeaderComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Theme helpers for styling based on current theme
|
// Theme helpers for styling based on current theme
|
||||||
|
private resolveThemeSlug(slug: string | null | undefined): 'light' | 'dark' | 'black' | 'blue' | 'system' {
|
||||||
|
const v = (slug || 'system') as string;
|
||||||
|
if (v === 'system') return (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light';
|
||||||
|
if (v === 'light' || v === 'dark' || v === 'black' || v === 'blue') return v;
|
||||||
|
return 'light';
|
||||||
|
}
|
||||||
|
|
||||||
getCurrentTheme(): string {
|
getCurrentTheme(): string {
|
||||||
return this.currentTheme() || 'system';
|
return this.currentTheme() || 'system';
|
||||||
}
|
}
|
||||||
|
|
||||||
isLightTheme(): boolean {
|
isLightTheme(): boolean {
|
||||||
const t = this.getCurrentTheme();
|
return this.resolveThemeSlug(this.getCurrentTheme()) === 'light';
|
||||||
return t === 'light' || t === 'system';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isDarkTheme(): boolean {
|
isDarkTheme(): boolean {
|
||||||
const t = this.getCurrentTheme();
|
return this.resolveThemeSlug(this.getCurrentTheme()) === 'dark';
|
||||||
return t === 'dark';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isBlackTheme(): boolean {
|
isBlackTheme(): boolean {
|
||||||
const t = this.getCurrentTheme();
|
return this.resolveThemeSlug(this.getCurrentTheme()) === 'black';
|
||||||
return t === 'black';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isBlueTheme(): boolean {
|
isBlueTheme(): boolean {
|
||||||
const t = this.getCurrentTheme();
|
return this.resolveThemeSlug(this.getCurrentTheme()) === 'blue';
|
||||||
return t === 'blue';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getThemeClasses(): { [key: string]: boolean } {
|
getThemeClasses(): { [key: string]: boolean } {
|
||||||
const theme = this.getCurrentTheme();
|
const rt = this.resolveThemeSlug(this.getCurrentTheme());
|
||||||
return {
|
return {
|
||||||
'theme-light': theme === 'light' || theme === 'system',
|
'theme-light': rt === 'light',
|
||||||
'theme-dark': theme === 'dark',
|
'theme-dark': rt === 'dark',
|
||||||
'theme-black': theme === 'black',
|
'theme-black': rt === 'black',
|
||||||
'theme-blue': theme === 'blue'
|
'theme-blue': rt === 'blue'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
<nav class="sticky top-16 left-0 right-0 z-40 w-full relative"
|
<nav class="sticky top-16 left-0 right-0 z-40 w-full relative"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'bg-slate-100 border-b border-slate-200': isLightTheme(),
|
'bg-slate-800 border-b border-slate-700': isDarkTheme(),
|
||||||
'bg-slate-900 border-b border-slate-800': isDarkTheme(),
|
'bg-slate-900 border-b border-slate-800': isBlackTheme(),
|
||||||
'bg-black border-b border-slate-900': isBlackTheme(),
|
'bg-slate-700 border-b border-slate-600': isBlueTheme()
|
||||||
'bg-blue-950 border-b border-blue-900': isBlueTheme()
|
|
||||||
}">
|
}">
|
||||||
<!-- Left scroll button -->
|
<!-- Left scroll button -->
|
||||||
<button type="button" (click)="scrollBy(-320)" aria-label="Scroll left"
|
<button type="button" (click)="scrollBy(-320)" aria-label="Scroll left"
|
||||||
class="hidden md:flex items-center justify-center absolute left-2 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full shadow z-10"
|
class="hidden md:flex items-center justify-center absolute left-2 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full shadow z-10"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'bg-white/90 border border-slate-300 text-slate-700 hover:bg-slate-100': isLightTheme(),
|
'bg-white/90 border border-slate-300 text-slate-700 hover:bg-slate-100': isLightTheme(),
|
||||||
'bg-slate-800/90 border border-slate-700 text-slate-200 hover:bg-slate-700': !isLightTheme()
|
'bg-slate-800/90 border border-slate-700 text-slate-200 hover:bg-slate-700': isDarkTheme() || isBlackTheme() || isBlueTheme()
|
||||||
}">
|
}">
|
||||||
«
|
«
|
||||||
</button>
|
</button>
|
||||||
@ -20,7 +19,7 @@
|
|||||||
class="hidden md:flex items-center justify-center absolute right-2 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full shadow z-10"
|
class="hidden md:flex items-center justify-center absolute right-2 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full shadow z-10"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'bg-white/90 border border-slate-300 text-slate-700 hover:bg-slate-100': isLightTheme(),
|
'bg-white/90 border border-slate-300 text-slate-700 hover:bg-slate-100': isLightTheme(),
|
||||||
'bg-slate-800/90 border border-slate-700 text-slate-200 hover:bg-slate-700': !isLightTheme()
|
'bg-slate-800/90 border border-slate-700 text-slate-200 hover:bg-slate-700': isDarkTheme() || isBlackTheme() || isBlueTheme()
|
||||||
}">
|
}">
|
||||||
»
|
»
|
||||||
</button>
|
</button>
|
||||||
|
50
src/components/themes/themes-nav.component.html.bak
Normal file
50
src/components/themes/themes-nav.component.html.bak
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<nav class="sticky top-16 left-0 right-0 z-40 w-full relative"
|
||||||
|
[ngClass]="{
|
||||||
|
'bg-slate-800 border-b border-slate-700': isDarkTheme(),
|
||||||
|
'bg-slate-900 border-b border-slate-800': isBlackTheme(),
|
||||||
|
'bg-slate-700 border-b border-slate-600': isBlueTheme()
|
||||||
|
}">
|
||||||
|
<!-- Left scroll button -->
|
||||||
|
<button type="button" (click)="scrollBy(-320)" aria-label="Scroll left"
|
||||||
|
class="hidden md:flex items-center justify-center absolute left-2 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full shadow z-10"
|
||||||
|
[ngClass]="{
|
||||||
|
'bg-white/90 border border-slate-300 text-slate-700 hover:bg-slate-100': isLightTheme(),
|
||||||
|
'bg-slate-800/90 border border-slate-700 text-slate-200 hover:bg-slate-700': !isLightTheme()
|
||||||
|
}">
|
||||||
|
«
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Right scroll button -->
|
||||||
|
<button type="button" (click)="scrollBy(320)" aria-label="Scroll right"
|
||||||
|
class="hidden md:flex items-center justify-center absolute right-2 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full shadow z-10"
|
||||||
|
[ngClass]="{
|
||||||
|
'bg-white/90 border border-slate-300 text-slate-700 hover:bg-slate-100': isLightTheme(),
|
||||||
|
'bg-slate-800/90 border border-slate-700 text-slate-200 hover:bg-slate-700': !isLightTheme()
|
||||||
|
}">
|
||||||
|
»
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="w-full overflow-x-auto no-scrollbar" #scroll (wheel)="onWheel($event)">
|
||||||
|
<ul role="tablist" aria-label="Themes" class="flex gap-2 px-12 py-2 min-w-max items-center">
|
||||||
|
<li *ngFor="let t of displayedThemes(); let i = index" class="shrink-0">
|
||||||
|
<button
|
||||||
|
#pill
|
||||||
|
role="tab"
|
||||||
|
type="button"
|
||||||
|
class="px-3 py-1.5 rounded-full text-sm font-medium border transition-colors whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-red-500"
|
||||||
|
[class.bg-slate-100/10]="activeSlug() === t.slug"
|
||||||
|
[class.text-white]="activeSlug() === t.slug"
|
||||||
|
[class.border-red-500]="activeSlug() === t.slug"
|
||||||
|
[class.text-slate-300]="activeSlug() !== t.slug"
|
||||||
|
[class.border-slate-700]="activeSlug() !== t.slug"
|
||||||
|
[attr.aria-selected]="activeSlug() === t.slug"
|
||||||
|
(click)="goToTheme(t.slug)"
|
||||||
|
(keydown)="onKeydown($event, i)"
|
||||||
|
>
|
||||||
|
<span class="mr-1 select-none">{{ t.emoji }}</span>
|
||||||
|
<span>{{ themesSvc.i18nLabel(t) }}</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, ElementRef, QueryList, ViewChild, ViewChildren, inject, signal, effect } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, ElementRef, QueryList, ViewChild, ViewChildren, inject, signal, effect, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
|
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
|
||||||
import { ThemesService } from '../../services/themes.service';
|
import { ThemesService } from '../../services/themes.service';
|
||||||
@ -10,7 +10,7 @@ import { ThemesService } from '../../services/themes.service';
|
|||||||
imports: [CommonModule],
|
imports: [CommonModule],
|
||||||
templateUrl: './themes-nav.component.html'
|
templateUrl: './themes-nav.component.html'
|
||||||
})
|
})
|
||||||
export class ThemesNavComponent {
|
export class ThemesNavComponent implements OnDestroy {
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
themesSvc = inject(ThemesService);
|
themesSvc = inject(ThemesService);
|
||||||
@ -22,6 +22,9 @@ export class ThemesNavComponent {
|
|||||||
@ViewChildren('pill', { read: ElementRef }) pills!: QueryList<ElementRef<HTMLButtonElement>>;
|
@ViewChildren('pill', { read: ElementRef }) pills!: QueryList<ElementRef<HTMLButtonElement>>;
|
||||||
@ViewChild('scroll', { static: false }) scrollRef?: ElementRef<HTMLDivElement>;
|
@ViewChild('scroll', { static: false }) scrollRef?: ElementRef<HTMLDivElement>;
|
||||||
|
|
||||||
|
// Signal to force re-evaluation when data-theme changes
|
||||||
|
private themeChanged = signal<number>(0);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Reflect current URL into active slug
|
// Reflect current URL into active slug
|
||||||
this.router.events.subscribe((e) => {
|
this.router.events.subscribe((e) => {
|
||||||
@ -29,6 +32,17 @@ export class ThemesNavComponent {
|
|||||||
});
|
});
|
||||||
// Also on init
|
// Also on init
|
||||||
setTimeout(() => this.syncFromUrl(), 0);
|
setTimeout(() => this.syncFromUrl(), 0);
|
||||||
|
|
||||||
|
// Effect to listen for theme changes on document.documentElement
|
||||||
|
effect(() => {
|
||||||
|
const currentTheme = document.documentElement.getAttribute('data-theme') || 'system';
|
||||||
|
// Trigger re-evaluation when theme changes
|
||||||
|
this.themeChanged.update(n => n + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// Nothing to cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
private syncFromUrl() {
|
private syncFromUrl() {
|
||||||
@ -75,30 +89,40 @@ export class ThemesNavComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Theme helpers (read from document attribute/localStorage)
|
// Theme helpers (read from document attribute/localStorage)
|
||||||
private getCurrentTheme(): string {
|
private getCurrentTheme(): 'light' | 'dark' | 'black' | 'blue' | 'system' {
|
||||||
try {
|
try {
|
||||||
const fromAttr = document.documentElement.getAttribute('data-theme');
|
const fromAttr = document.documentElement.getAttribute('data-theme');
|
||||||
if (fromAttr) return fromAttr;
|
if (fromAttr) return fromAttr as any;
|
||||||
const fromStorage = localStorage.getItem('newtube.theme');
|
const fromStorage = localStorage.getItem('newtube.theme');
|
||||||
const val = fromStorage || 'system';
|
return (fromStorage as any) || 'system';
|
||||||
if (val === 'system') {
|
|
||||||
// Resolve to light/dark using media query so styles can be deterministic
|
|
||||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
return prefersDark ? 'dark' : 'light';
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
} catch {
|
} catch {
|
||||||
// Safe fallback: prefer dark for safety in most UIs
|
return 'system';
|
||||||
const prefersDark = typeof window !== 'undefined' && (window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false);
|
|
||||||
return prefersDark ? 'dark' : 'light';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resolveThemeSlug(): 'light' | 'dark' | 'black' | 'blue' {
|
||||||
|
const cur = this.getCurrentTheme();
|
||||||
|
if (cur === 'system') {
|
||||||
|
const prefersDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
||||||
|
return prefersDark ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
return cur as any;
|
||||||
|
}
|
||||||
|
|
||||||
isLightTheme(): boolean {
|
isLightTheme(): boolean {
|
||||||
const t = this.getCurrentTheme();
|
this.themeChanged(); // Trigger re-evaluation
|
||||||
return t === 'light' || t === 'system';
|
return this.resolveThemeSlug() === 'light';
|
||||||
|
}
|
||||||
|
isDarkTheme(): boolean {
|
||||||
|
this.themeChanged(); // Trigger re-evaluation
|
||||||
|
return this.resolveThemeSlug() === 'dark';
|
||||||
|
}
|
||||||
|
isBlackTheme(): boolean {
|
||||||
|
this.themeChanged(); // Trigger re-evaluation
|
||||||
|
return this.resolveThemeSlug() === 'black';
|
||||||
|
}
|
||||||
|
isBlueTheme(): boolean {
|
||||||
|
this.themeChanged(); // Trigger re-evaluation
|
||||||
|
return this.resolveThemeSlug() === 'blue';
|
||||||
}
|
}
|
||||||
isDarkTheme(): boolean { return this.getCurrentTheme() === 'dark'; }
|
|
||||||
isBlackTheme(): boolean { return this.getCurrentTheme() === 'black'; }
|
|
||||||
isBlueTheme(): boolean { return this.getCurrentTheme() === 'blue'; }
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user