feat: apply new branding logo across app and pwa assets
BIN
public/favicon.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 21 KiB |
BIN
public/images/icons/home.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 7.3 KiB |
BIN
public/images/icons/library.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/images/logostripstream-white.png
Normal file
|
After Width: | Height: | Size: 889 KiB |
BIN
public/images/logostripstream.png
Normal file
|
After Width: | Height: | Size: 895 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 4.0 MiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 4.5 MiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 5.8 MiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 2.2 MiB |
@@ -23,7 +23,12 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Segoe UI", "SF Pro Text", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
font-family:
|
||||
"Segoe UI",
|
||||
"SF Pro Text",
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
@@ -60,7 +65,12 @@
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background:
|
||||
linear-gradient(112deg, rgba(79, 70, 229, 0.24) 0%, rgba(6, 182, 212, 0.2) 30%, transparent 56%),
|
||||
linear-gradient(
|
||||
112deg,
|
||||
rgba(79, 70, 229, 0.24) 0%,
|
||||
rgba(6, 182, 212, 0.2) 30%,
|
||||
transparent 56%
|
||||
),
|
||||
linear-gradient(248deg, rgba(244, 114, 182, 0.16) 0%, transparent 46%),
|
||||
repeating-linear-gradient(135deg, rgba(226, 232, 240, 0.03) 0 1px, transparent 1px 11px);
|
||||
}
|
||||
@@ -82,6 +92,15 @@
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
border-radius: 0.6rem;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 0 20px rgba(34, 211, 238, 0.35);
|
||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||
}
|
||||
|
||||
.menu-btn {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
@@ -170,7 +189,12 @@
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background:
|
||||
linear-gradient(160deg, rgba(79, 70, 229, 0.12) 0%, rgba(6, 182, 212, 0.08) 32%, transparent 58%),
|
||||
linear-gradient(
|
||||
160deg,
|
||||
rgba(79, 70, 229, 0.12) 0%,
|
||||
rgba(6, 182, 212, 0.08) 32%,
|
||||
transparent 58%
|
||||
),
|
||||
linear-gradient(332deg, rgba(244, 114, 182, 0.06) 0%, transparent 42%),
|
||||
repeating-linear-gradient(135deg, rgba(226, 232, 240, 0.02) 0 1px, transparent 1px 11px);
|
||||
}
|
||||
@@ -327,8 +351,9 @@
|
||||
<div class="header-inner">
|
||||
<div class="brand">
|
||||
<button class="menu-btn" id="sidebar-toggle" type="button" aria-label="Menu">☰</button>
|
||||
<img class="brand-logo" src="/images/logostripstream.png" alt="StripStream logo" />
|
||||
<div>
|
||||
<div class="brand-title">STRIPSTREAM</div>
|
||||
<div class="brand-title">StripStream</div>
|
||||
<div class="brand-subtitle">comic reader</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -358,16 +383,16 @@
|
||||
<div class="status">● Hors ligne</div>
|
||||
<h1>Cette page n'est pas encore disponible hors ligne.</h1>
|
||||
<p>
|
||||
Tu peux continuer a naviguer sur les pages deja consultees. Cette route sera
|
||||
disponible hors ligne apres une visite en ligne.
|
||||
Tu peux continuer a naviguer sur les pages deja consultees. Cette route sera disponible
|
||||
hors ligne apres une visite en ligne.
|
||||
</p>
|
||||
<div class="actions">
|
||||
<button class="btn btn-secondary" onclick="window.history.back()">Retour</button>
|
||||
<button class="btn btn-primary" onclick="window.location.reload()">Reessayer</button>
|
||||
</div>
|
||||
<div class="hint">
|
||||
Astuce: visite d'abord Accueil, Bibliotheques, Series et pages de lecture quand tu es
|
||||
en ligne.
|
||||
Astuce: visite d'abord Accueil, Bibliotheques, Series et pages de lecture quand tu es en
|
||||
ligne.
|
||||
</div>
|
||||
<div class="footer">StripStream - interface hors ligne</div>
|
||||
</div>
|
||||
|
||||
@@ -3,11 +3,11 @@ const fs = require("fs").promises;
|
||||
const path = require("path");
|
||||
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
const inputSvg = path.join(__dirname, "../public/favicon.svg");
|
||||
const inputAppleSvg = path.join(__dirname, "../public/apple-icon.svg");
|
||||
const sourceLogo = path.join(__dirname, "../public/images/logostripstream.png");
|
||||
const outputDir = path.join(__dirname, "../public/images/icons");
|
||||
const screenshotsDir = path.join(__dirname, "../public/images/screenshots");
|
||||
const splashDir = path.join(__dirname, "../public/images/splash");
|
||||
const faviconPath = path.join(__dirname, "../public/favicon.png");
|
||||
|
||||
// Configuration des splashscreens pour différents appareils
|
||||
const splashScreens = [
|
||||
@@ -24,35 +24,23 @@ const splashScreens = [
|
||||
async function generateSplashScreens() {
|
||||
await fs.mkdir(splashDir, { recursive: true });
|
||||
|
||||
// Créer le SVG de base pour la splashscreen avec le même style que le favicon
|
||||
const splashSvg = `
|
||||
<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="100" height="100" fill="#4F46E5"/>
|
||||
<g transform="translate(25, 20) scale(1.5)">
|
||||
<!-- Lettre S stylisée -->
|
||||
<path
|
||||
d="M21 12C21 10.3431 19.6569 9 18 9H14C12.3431 9 11 10.3431 11 12V12.5C11 14.1569 12.3431 15.5 14 15.5H18C19.6569 15.5 21 16.8431 21 18.5V19C21 20.6569 19.6569 22 18 22H14C12.3431 22 11 20.6569 11 19"
|
||||
stroke="white"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<!-- Points décoratifs -->
|
||||
<circle cx="11" cy="24" r="1.5" fill="white"/>
|
||||
<circle cx="21" cy="8" r="1.5" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
for (const screen of splashScreens) {
|
||||
const outputPath = path.join(splashDir, `splash-${screen.width}x${screen.height}.png`);
|
||||
const darkOverlay = Buffer.from(
|
||||
`<svg width="${screen.width}" height="${screen.height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="100%" height="100%" fill="rgba(4, 8, 20, 0.22)" />
|
||||
</svg>`
|
||||
);
|
||||
|
||||
await sharp(Buffer.from(splashSvg))
|
||||
await sharp(sourceLogo)
|
||||
.resize(screen.width, screen.height, {
|
||||
fit: "contain",
|
||||
background: "#4F46E5",
|
||||
fit: "cover",
|
||||
position: "center",
|
||||
})
|
||||
.composite([{ input: darkOverlay, blend: "over" }])
|
||||
.png({
|
||||
compressionLevel: 9,
|
||||
})
|
||||
.png()
|
||||
.toFile(outputPath);
|
||||
|
||||
console.log(`✓ Splashscreen ${screen.name} (${screen.width}x${screen.height}) générée`);
|
||||
@@ -61,17 +49,19 @@ async function generateSplashScreens() {
|
||||
|
||||
async function generateIcons() {
|
||||
try {
|
||||
await fs.access(sourceLogo);
|
||||
|
||||
// Créer les dossiers de sortie s'ils n'existent pas
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
await fs.mkdir(screenshotsDir, { recursive: true });
|
||||
|
||||
// Générer les icônes Android (avec bords arrondis)
|
||||
// Générer les icônes Android
|
||||
for (const size of sizes) {
|
||||
const outputPath = path.join(outputDir, `icon-${size}x${size}.png`);
|
||||
|
||||
await sharp(inputSvg)
|
||||
await sharp(sourceLogo)
|
||||
.resize(size, size, {
|
||||
fit: "contain",
|
||||
fit: "cover",
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }, // Fond transparent
|
||||
})
|
||||
.png({
|
||||
@@ -88,9 +78,9 @@ async function generateIcons() {
|
||||
for (const size of appleSizes) {
|
||||
const outputPath = path.join(outputDir, `apple-icon-${size}x${size}.png`);
|
||||
|
||||
await sharp(inputAppleSvg)
|
||||
await sharp(sourceLogo)
|
||||
.resize(size, size, {
|
||||
fit: "contain",
|
||||
fit: "cover",
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }, // Fond transparent
|
||||
})
|
||||
.png({
|
||||
@@ -102,26 +92,25 @@ async function generateIcons() {
|
||||
console.log(`✓ Icône Apple ${size}x${size} générée`);
|
||||
}
|
||||
|
||||
// Générer le favicon principal utilisé par Next metadata
|
||||
await sharp(sourceLogo)
|
||||
.resize(64, 64, {
|
||||
fit: "cover",
|
||||
})
|
||||
.png({
|
||||
compressionLevel: 9,
|
||||
palette: true,
|
||||
})
|
||||
.toFile(faviconPath);
|
||||
|
||||
console.log("✓ Favicon principal généré");
|
||||
|
||||
// Générer les icônes de raccourcis
|
||||
const shortcutIcons = [
|
||||
{ name: "home", icon: "Home" },
|
||||
{ name: "library", icon: "Library" },
|
||||
];
|
||||
const shortcutIcons = ["home", "library"];
|
||||
|
||||
for (const shortcut of shortcutIcons) {
|
||||
const outputPath = path.join(outputDir, `${shortcut.name}.png`);
|
||||
|
||||
// Créer une image carrée avec fond indigo et icône blanche
|
||||
const svg = `
|
||||
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="96" height="96" rx="20" fill="#4F46E5"/>
|
||||
<path d="${getIconPath(
|
||||
shortcut.icon
|
||||
)}" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
await sharp(Buffer.from(svg))
|
||||
const outputPath = path.join(outputDir, `${shortcut}.png`);
|
||||
await sharp(sourceLogo)
|
||||
.resize(96, 96)
|
||||
.png({
|
||||
compressionLevel: 9,
|
||||
@@ -129,7 +118,7 @@ async function generateIcons() {
|
||||
})
|
||||
.toFile(outputPath);
|
||||
|
||||
console.log(`✓ Icône de raccourci ${shortcut.name} générée`);
|
||||
console.log(`✓ Icône de raccourci ${shortcut} générée`);
|
||||
}
|
||||
|
||||
// Générer les screenshots de démonstration
|
||||
@@ -166,14 +155,4 @@ async function generateIcons() {
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction helper pour obtenir les chemins SVG des icônes
|
||||
function getIconPath(iconName) {
|
||||
const paths = {
|
||||
Home: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6",
|
||||
Library:
|
||||
"M4 19.5A2.5 2.5 0 0 1 6.5 17H20M4 19.5A2.5 2.5 0 0 0 6.5 22H20M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20",
|
||||
};
|
||||
return paths[iconName] || "";
|
||||
}
|
||||
|
||||
generateIcons();
|
||||
|
||||
@@ -15,7 +15,7 @@ export default async function AccountPage() {
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto space-y-8">
|
||||
<div className="mx-auto max-w-4xl space-y-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">Mon compte</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
|
||||
@@ -36,8 +36,8 @@ export const metadata: Metadata = {
|
||||
icons: {
|
||||
icon: [
|
||||
{
|
||||
url: "/favicon.svg",
|
||||
type: "image/svg+xml",
|
||||
url: "/favicon.png",
|
||||
type: "image/png",
|
||||
},
|
||||
{ url: "/images/icons/icon-72x72.png", sizes: "72x72", type: "image/png" },
|
||||
{ url: "/images/icons/icon-96x96.png", sizes: "96x96", type: "image/png" },
|
||||
@@ -176,7 +176,11 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
<AuthProvider>
|
||||
<I18nProvider locale={locale}>
|
||||
<PreferencesProvider initialPreferences={preferences}>
|
||||
<ClientLayout initialLibraries={libraries} initialFavorites={favorites} userIsAdmin={userIsAdmin}>
|
||||
<ClientLayout
|
||||
initialLibraries={libraries}
|
||||
initialFavorites={favorites}
|
||||
userIsAdmin={userIsAdmin}
|
||||
>
|
||||
{children}
|
||||
</ClientLayout>
|
||||
</PreferencesProvider>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
const SHOW_DELAY_MS = 140;
|
||||
|
||||
@@ -22,8 +23,22 @@ export default function AppLoading() {
|
||||
}`}
|
||||
>
|
||||
<div className="flex w-full max-w-sm flex-col items-center gap-6 text-center">
|
||||
<div className="space-y-2">
|
||||
<p className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-xl font-bold uppercase tracking-[0.12em] text-transparent">
|
||||
<div className="space-y-3">
|
||||
<Image
|
||||
src="/images/logostripstream.png"
|
||||
alt="StripStream Logo"
|
||||
width={80}
|
||||
height={80}
|
||||
className="mx-auto hidden h-20 w-20 rounded-xl object-cover dark:block"
|
||||
/>
|
||||
<Image
|
||||
src="/images/logostripstream-white.png"
|
||||
alt="StripStream Logo"
|
||||
width={80}
|
||||
height={80}
|
||||
className="mx-auto h-20 w-20 rounded-xl object-cover dark:hidden"
|
||||
/>
|
||||
<p className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-xl font-bold tracking-[0.08em] text-transparent">
|
||||
StripStream
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Chargement de votre bibliotheque...</p>
|
||||
|
||||
@@ -43,21 +43,21 @@ export function LoginContent({ searchParams }: LoginContentProps) {
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="relative z-20 flex items-center text-lg font-medium"
|
||||
>
|
||||
<motion.svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="mr-2 h-6 w-6"
|
||||
whileHover={{ rotate: 180 }}
|
||||
<motion.img
|
||||
src="/images/logostripstream.png"
|
||||
alt="StripStream Logo"
|
||||
className="mr-3 hidden h-9 w-9 rounded-md object-cover dark:block"
|
||||
whileHover={{ scale: 1.08, rotate: -3 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
|
||||
</motion.svg>
|
||||
<span className="text-2xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent">
|
||||
/>
|
||||
<motion.img
|
||||
src="/images/logostripstream-white.png"
|
||||
alt="StripStream Logo"
|
||||
className="mr-3 h-9 w-9 rounded-md object-cover dark:hidden"
|
||||
whileHover={{ scale: 1.08, rotate: -3 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
/>
|
||||
<span className="text-2xl font-bold bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-transparent">
|
||||
StripStream
|
||||
</span>
|
||||
</motion.div>
|
||||
@@ -83,12 +83,20 @@ export function LoginContent({ searchParams }: LoginContentProps) {
|
||||
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||
<div className="flex flex-col items-center space-y-4 text-center">
|
||||
<div className="relative">
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-[#4F46E5] to-[#6366F1] rounded-full opacity-75 blur-md animate-pulse"></div>
|
||||
<div className="relative bg-gradient-to-br from-white to-gray-100 dark:from-slate-800 dark:to-slate-900 rounded-full shadow-xl overflow-hidden w-24 h-24 flex items-center justify-center">
|
||||
<div className="relative bg-gradient-to-br from-white to-gray-100 dark:from-slate-800 dark:to-slate-900 rounded-full shadow-xl overflow-hidden w-32 h-32 flex items-center justify-center">
|
||||
<motion.img
|
||||
src="/images/icons/apple-icon-180x180.png"
|
||||
src="/images/logostripstream.png"
|
||||
alt="StripStream Logo"
|
||||
className="w-[100%] h-[100%] object-cover"
|
||||
className="hidden h-[100%] w-[100%] object-cover dark:block"
|
||||
initial={{ scale: 1.8, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
/>
|
||||
<motion.img
|
||||
src="/images/logostripstream-white.png"
|
||||
alt="StripStream Logo"
|
||||
className="h-[100%] w-[100%] object-cover dark:hidden"
|
||||
initial={{ scale: 1.8, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
@@ -99,7 +107,7 @@ export function LoginContent({ searchParams }: LoginContentProps) {
|
||||
<motion.h1
|
||||
initial={{ y: -20 }}
|
||||
animate={{ y: 0 }}
|
||||
className="text-3xl font-bold tracking-tight bg-gradient-to-r from-slate-900 to-slate-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent"
|
||||
className="text-3xl font-bold tracking-tight bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-transparent"
|
||||
>
|
||||
{t("login.title")}
|
||||
</motion.h1>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { RefreshButton } from "@/components/library/RefreshButton";
|
||||
import { PullToRefreshIndicator } from "@/components/common/PullToRefreshIndicator";
|
||||
import { usePullToRefresh } from "@/hooks/usePullToRefresh";
|
||||
import { revalidateForRefresh } from "@/app/actions/refresh";
|
||||
import Image from "next/image";
|
||||
|
||||
interface HomeClientWrapperProps {
|
||||
children: ReactNode;
|
||||
@@ -48,7 +49,25 @@ export function HomeClientWrapper({ children }: HomeClientWrapperProps) {
|
||||
isHiding={pullToRefresh.isHiding}
|
||||
/>
|
||||
<main className="relative isolate overflow-hidden">
|
||||
<div className="container mx-auto space-y-6 px-4 pb-8 pt-3">
|
||||
<div className="pointer-events-none absolute inset-0 z-0 flex items-start justify-center pt-10">
|
||||
<Image
|
||||
src="/images/logostripstream.png"
|
||||
alt=""
|
||||
width={600}
|
||||
height={600}
|
||||
aria-hidden
|
||||
className="hidden h-auto w-[min(78vw,600px)] opacity-[0.08] saturate-125 dark:block"
|
||||
/>
|
||||
<Image
|
||||
src="/images/logostripstream-white.png"
|
||||
alt=""
|
||||
width={600}
|
||||
height={600}
|
||||
aria-hidden
|
||||
className="h-auto w-[min(78vw,600px)] opacity-[0.1] saturate-125 dark:hidden"
|
||||
/>
|
||||
</div>
|
||||
<div className="container relative z-10 mx-auto space-y-6 px-4 pb-8 pt-3">
|
||||
<div className="flex justify-end">
|
||||
<RefreshButton libraryId="home" refreshLibrary={handleRefresh} />
|
||||
</div>
|
||||
|
||||
@@ -48,14 +48,14 @@ export function Header({
|
||||
|
||||
<div className="mr-2 flex items-center md:mr-4">
|
||||
<a className="mr-2 flex items-center md:mr-6" href="/">
|
||||
<span className="inline-flex bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-sm font-bold uppercase tracking-[0.1em] text-transparent sm:hidden">
|
||||
StripStream
|
||||
<span className="inline-flex bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-sm font-bold tracking-[0.06em] text-transparent sm:hidden">
|
||||
Strip
|
||||
</span>
|
||||
<span className="hidden sm:inline-flex flex-col leading-none">
|
||||
<span className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-lg font-bold tracking-[0.1em] text-transparent">
|
||||
STRIPSTREAM
|
||||
<span className="bg-gradient-to-r from-primary via-cyan-500 to-fuchsia-500 bg-clip-text text-lg font-bold tracking-[0.08em] text-transparent">
|
||||
StripStream
|
||||
</span>
|
||||
<span className="mt-1 text-[10px] font-medium uppercase tracking-[0.28em] text-foreground/70">
|
||||
<span className="mt-1 text-[10px] font-medium uppercase tracking-[0.22em] text-foreground/70">
|
||||
comic reader
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -157,8 +157,18 @@ export function Sidebar({
|
||||
id="sidebar"
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(160deg,hsl(var(--primary)/0.12)_0%,hsl(192_85%_55%/0.08)_32%,transparent_58%),linear-gradient(332deg,hsl(338_82%_62%/0.06)_0%,transparent_42%),repeating-linear-gradient(135deg,hsl(var(--foreground)/0.02)_0_1px,transparent_1px_11px)]" />
|
||||
<div className="pointer-events-none absolute inset-0 z-0">
|
||||
<div
|
||||
className="hidden h-full w-full bg-center bg-no-repeat opacity-[0.1] [background-size:260%] dark:block"
|
||||
style={{ backgroundImage: "url('/images/logostripstream.png')" }}
|
||||
/>
|
||||
<div
|
||||
className="h-full w-full bg-center bg-no-repeat opacity-[0.12] [background-size:260%] dark:hidden"
|
||||
style={{ backgroundImage: "url('/images/logostripstream-white.png')" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative flex-1 space-y-4 overflow-y-auto px-3 py-4">
|
||||
<div className="relative z-10 flex-1 space-y-4 overflow-y-auto px-3 py-4">
|
||||
<div className="rounded-xl border border-border/50 bg-background/30 p-2">
|
||||
<div className="space-y-1">
|
||||
<h2 className="mb-2 px-3 text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
||||
|
||||
@@ -42,7 +42,7 @@ export function ClientSettings({ initialConfig, initialLibraries }: ClientSettin
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto space-y-8">
|
||||
<div className="mx-auto max-w-4xl space-y-8">
|
||||
<h1 className="text-3xl font-bold">{t("settings.title")}</h1>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
|
||||
|
||||