1.1.4 - Design tweaks
This commit is contained in:
+16
-17
@@ -9,7 +9,7 @@
|
||||
import HistoryPanel from "./components/HistoryPanel.svelte";
|
||||
import LibraryPanel from "./components/library/LibraryPanel.svelte";
|
||||
import AutomationList from "./routes/automation/AutomationList.svelte";
|
||||
import { Menu } from "lucide-svelte";
|
||||
import { Menu, AlertTriangle, AlertCircle } from "lucide-svelte";
|
||||
import ToastHost from "./components/ToastHost.svelte";
|
||||
import { healthCheck } from "./lib/api.js";
|
||||
import { currentTheme, themes } from "./lib/themeStore.js";
|
||||
@@ -124,7 +124,7 @@
|
||||
>
|
||||
{#if isMobile && sidebarOpen}
|
||||
<div
|
||||
class="fixed inset-0 z-30 bg-black/40 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-30 bg-black/50 backdrop-blur-sm"
|
||||
on:click={() => (sidebarOpen = false)}
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
@@ -139,39 +139,38 @@
|
||||
/>
|
||||
<Sidebar.Inset>
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1">
|
||||
<main class="flex-1 min-h-0">
|
||||
{#if isMobile && !sidebarOpen}
|
||||
<div class="px-4 sm:px-6 md:px-8 pt-4">
|
||||
<div class="px-4 pt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-white/15 text-text-secondary hover:bg-white/10"
|
||||
className="gap-2"
|
||||
on:click={() => (sidebarOpen = true)}
|
||||
aria-label="Show sidebar"
|
||||
>
|
||||
<Menu class="h-4 w-4" />
|
||||
<Menu class="h-3.5 w-3.5" />
|
||||
Menu
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Status banners -->
|
||||
{#if !apiReachable}
|
||||
<div class="border-b border-red-500/20 bg-red-500/10">
|
||||
<div class="px-6 md:px-8 py-3">
|
||||
<p class="text-[13px] text-red-200">{apiErrorMessage}</p>
|
||||
</div>
|
||||
<div class="mx-4 sm:mx-6 md:mx-8 mt-4 flex items-start gap-3 rounded-xl border border-red-500/20 bg-red-500/8 px-4 py-3">
|
||||
<AlertCircle class="h-4 w-4 text-red-400 mt-0.5 shrink-0" />
|
||||
<p class="text-[13px] text-red-300 leading-relaxed">{apiErrorMessage}</p>
|
||||
</div>
|
||||
{:else if !apiConfigured && currentView === "scanner"}
|
||||
<div class="border-b border-yellow-500/10 bg-yellow-500/5">
|
||||
<div class="px-6 md:px-8 py-3">
|
||||
<p class="text-[14px] text-red-100">
|
||||
Configure a metadata source in Settings > Integrations to get
|
||||
started
|
||||
<div class="mx-4 sm:mx-6 md:mx-8 mt-4 flex items-start gap-3 rounded-xl border border-yellow-500/15 bg-yellow-500/6 px-4 py-3">
|
||||
<AlertTriangle class="h-4 w-4 text-yellow-400 mt-0.5 shrink-0" />
|
||||
<p class="text-[13px] text-yellow-200/80 leading-relaxed">
|
||||
Configure a metadata source in Settings › Integrations to get started
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="px-4 sm:px-6 md:px-8 py-6 sm:py-8 md:py-10">
|
||||
<div class="px-4 sm:px-6 md:px-8 py-7 sm:py-9 md:py-10">
|
||||
{#if currentView === "settings"}
|
||||
<SettingsPanel />
|
||||
{:else if currentView === "history"}
|
||||
|
||||
@@ -21,186 +21,136 @@
|
||||
export let open = true;
|
||||
export let collapsed = false;
|
||||
export let isMobile = false;
|
||||
|
||||
const navItems = [
|
||||
{ id: "scanner", label: "Scanner", icon: Scan },
|
||||
{ id: "automation", label: "Automations", icon: Zap },
|
||||
{ id: "history", label: "History", icon: History },
|
||||
{ id: "library", label: "Library", icon: Library },
|
||||
{ id: "settings", label: "Settings", icon: Settings },
|
||||
];
|
||||
</script>
|
||||
|
||||
<aside
|
||||
class={`fixed inset-y-0 left-0 z-40 h-screen w-[--sidebar-width] border-r border-border bg-[color:var(--bg-primary)] bg-gradient-to-b from-white/12 via-white/5 to-transparent text-text-primary transition-transform duration-200 ease-out md:sticky md:top-0 ${
|
||||
class={`fixed inset-y-0 left-0 z-40 h-screen w-[--sidebar-width] border-r border-border bg-[color:var(--bg-secondary)] text-text-primary transition-transform duration-200 ease-out md:sticky md:top-0 flex flex-col ${
|
||||
!open && isMobile
|
||||
? "-translate-x-full pointer-events-none"
|
||||
: "translate-x-0"
|
||||
}`}
|
||||
>
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<!-- Logo area -->
|
||||
<div
|
||||
class={`relative flex items-center gap-3 py-5 ${collapsed ? "px-2" : "px-4"}`}
|
||||
class={`relative flex items-center gap-3 py-5 border-b border-border ${collapsed ? "px-2 justify-center" : "px-4"}`}
|
||||
>
|
||||
{#if collapsed}
|
||||
<div
|
||||
class="relative flex h-9 w-9 items-center justify-center rounded-lg border border-white/10 bg-black/40 overflow-hidden"
|
||||
>
|
||||
<span class="absolute inset-0 rounded-lg bg-blue-500/10 blur-md"
|
||||
></span>
|
||||
|
||||
<div class="h-8 w-8 rounded-lg overflow-hidden">
|
||||
<img
|
||||
src={sublogueLogo}
|
||||
alt="Sublogue"
|
||||
class="relative h-full w-full object-cover"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center -ml-6">
|
||||
<div class="flex items-center -ml-5 flex-1 min-w-0">
|
||||
<img
|
||||
src={sublogueLogo}
|
||||
alt="Sublogue"
|
||||
class="h-9 w-auto max-w-[220px] object-contain"
|
||||
class="h-8 w-auto max-w-[200px] object-contain"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<button
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 h-8 w-8 rounded-full border border-white/10 bg-white/5 text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)] transition-colors"
|
||||
class="shrink-0 h-7 w-7 rounded-lg flex items-center justify-center text-text-tertiary hover:text-text-primary hover:bg-bg-hover transition-all duration-150"
|
||||
on:click={onToggleSidebar}
|
||||
aria-label={collapsed ? "Show sidebar" : "Hide sidebar"}
|
||||
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
>
|
||||
{#if collapsed}
|
||||
<ChevronRight class="h-4 w-4 mx-auto" />
|
||||
<ChevronRight class="h-3.5 w-3.5" />
|
||||
{:else}
|
||||
<ChevronLeft class="h-4 w-4 mx-auto" />
|
||||
<ChevronLeft class="h-3.5 w-3.5" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav
|
||||
class={`sidebar-nav flex-1 min-h-0 overflow-y-auto py-3 ${collapsed ? "px-1.5" : "px-3"} space-y-1`}
|
||||
class={`flex-1 min-h-0 overflow-y-auto py-3 space-y-0.5 ${collapsed ? "px-2" : "px-2"}`}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`w-full rounded-md py-1.5 text-[13px] font-semibold leading-none ${
|
||||
collapsed ? "justify-center px-0" : "justify-start px-2 gap-2"
|
||||
} ${
|
||||
currentView === "scanner"
|
||||
? "bg-[color:var(--bg-hover)] text-white font-bold"
|
||||
: "text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)]"
|
||||
{#each navItems as item}
|
||||
{@const isActive = currentView === item.id}
|
||||
<button
|
||||
class={`w-full flex items-center gap-2.5 rounded-lg text-[13px] font-medium transition-all duration-150 relative
|
||||
${collapsed ? "justify-center px-0 h-9" : "px-3 h-9"}
|
||||
${
|
||||
isActive
|
||||
? "bg-bg-hover text-text-primary"
|
||||
: "text-text-secondary hover:text-text-primary hover:bg-bg-hover/60"
|
||||
}`}
|
||||
on:click={() => onNavigate("scanner")}
|
||||
aria-current={currentView === "scanner" ? "page" : undefined}
|
||||
on:click={() => onNavigate(item.id)}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
>
|
||||
<Scan class="h-4 w-4" />
|
||||
{#if !collapsed}
|
||||
Scanner
|
||||
{#if isActive && !collapsed}
|
||||
<span
|
||||
class="absolute left-0 top-1/2 -translate-y-1/2 h-4 w-0.5 rounded-full bg-accent"
|
||||
></span>
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`w-full rounded-md py-1.5 text-[13px] font-semibold leading-none ${
|
||||
collapsed ? "justify-center px-0" : "justify-start px-2 gap-2"
|
||||
} ${
|
||||
currentView === "automation"
|
||||
? "bg-[color:var(--bg-hover)] text-white font-bold"
|
||||
: "text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)]"
|
||||
}`}
|
||||
on:click={() => onNavigate("automation")}
|
||||
aria-current={currentView === "automation" ? "page" : undefined}
|
||||
>
|
||||
<Zap class="h-4 w-4" />
|
||||
<svelte:component this={item.icon} class="h-4 w-4 shrink-0" />
|
||||
{#if !collapsed}
|
||||
Automations
|
||||
<span>{item.label}</span>
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`w-full rounded-md py-1.5 text-[13px] font-semibold leading-none ${
|
||||
collapsed ? "justify-center px-0" : "justify-start px-2 gap-2"
|
||||
} ${
|
||||
currentView === "history"
|
||||
? "bg-[color:var(--bg-hover)] text-white font-bold"
|
||||
: "text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)]"
|
||||
}`}
|
||||
on:click={() => onNavigate("history")}
|
||||
aria-current={currentView === "history" ? "page" : undefined}
|
||||
>
|
||||
<History class="h-4 w-4" />
|
||||
{#if !collapsed}
|
||||
History
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`w-full rounded-md py-1.5 text-[13px] font-semibold leading-none ${
|
||||
collapsed ? "justify-center px-0" : "justify-start px-2 gap-2"
|
||||
} ${
|
||||
currentView === "library"
|
||||
? "bg-[color:var(--bg-hover)] text-white font-bold"
|
||||
: "text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)]"
|
||||
}`}
|
||||
on:click={() => onNavigate("library")}
|
||||
aria-current={currentView === "library" ? "page" : undefined}
|
||||
>
|
||||
<Library class="h-4 w-4" />
|
||||
{#if !collapsed}
|
||||
Library
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`w-full rounded-md py-1.5 text-[13px] font-semibold leading-none ${
|
||||
collapsed ? "justify-center px-0" : "justify-start px-2 gap-2"
|
||||
} ${
|
||||
currentView === "settings"
|
||||
? "bg-[color:var(--bg-hover)] text-white font-bold"
|
||||
: "text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)]"
|
||||
}`}
|
||||
on:click={() => onNavigate("settings")}
|
||||
aria-current={currentView === "settings" ? "page" : undefined}
|
||||
>
|
||||
<Settings class="h-4 w-4" />
|
||||
{#if !collapsed}
|
||||
Settings
|
||||
{/if}
|
||||
</Button>
|
||||
</button>
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<div class={`pb-5 space-y-3 ${collapsed ? "px-2" : "px-3"}`}>
|
||||
<Separator className="bg-white/10" />
|
||||
<!-- Footer -->
|
||||
<div
|
||||
class={`border-t border-border pb-4 pt-3 space-y-2 ${collapsed ? "px-2" : "px-2"}`}
|
||||
>
|
||||
{#if !collapsed}
|
||||
<div class="px-1">
|
||||
<ThemeSelector className="w-full" />
|
||||
{/if}
|
||||
<div
|
||||
class={`flex items-center px-3 py-2 text-xs ${collapsed ? "justify-center" : "justify-between"}`}
|
||||
>
|
||||
{#if !collapsed}
|
||||
<Badge className="text-text-secondary">Version: v1.1.3</Badge>
|
||||
{:else}
|
||||
<Badge className="text-text-secondary">v</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class={`flex items-center px-3 py-2 text-xs ${collapsed ? "justify-center" : "justify-between"}`}
|
||||
class={`flex items-center px-1 ${collapsed ? "justify-center" : "justify-between"}`}
|
||||
>
|
||||
{#if !collapsed}
|
||||
<span class="text-text-tertiary">© 2026 ponzischeme89</span>
|
||||
<span class="text-[11px] text-text-tertiary"
|
||||
>© 2026 ponzischeme89</span
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge
|
||||
variant="outline"
|
||||
class="text-[10px] text-text-tertiary border-border py-0"
|
||||
>v1.1.4</Badge
|
||||
>
|
||||
<a
|
||||
href="https://github.com/ponzischeme89/Sublogue"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center text-text-tertiary hover:text-white transition-colors"
|
||||
class="text-text-tertiary hover:text-text-primary transition-colors"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
<Github class="h-4 w-4" />
|
||||
<Github class="h-3.5 w-3.5" />
|
||||
</a>
|
||||
{:else}
|
||||
<span class="text-text-tertiary">©</span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<a
|
||||
href="https://github.com/ponzischeme89/Sublogue"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-text-tertiary hover:text-text-primary transition-colors"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
<Github class="h-3.5 w-3.5" />
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
.sidebar-nav {
|
||||
nav button {
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
let successMessage = null;
|
||||
|
||||
const sections = [
|
||||
{ id: "general", label: "General", icon: "settings" },
|
||||
{ id: "folder-rules", label: "Folder Rules", icon: "folder" },
|
||||
{ id: "cleanup", label: "Cleanup", icon: "wand" },
|
||||
{ id: "integrations", label: "Integrations", icon: "plug" },
|
||||
{ id: "tasks", label: "Tasks", icon: "bolt" },
|
||||
{ id: "general", label: "General", icon: Settings },
|
||||
{ id: "folder-rules", label: "Folder Rules", icon: Folder },
|
||||
{ id: "cleanup", label: "Cleanup", icon: Wand2 },
|
||||
{ id: "integrations", label: "Integrations", icon: Plug },
|
||||
{ id: "tasks", label: "Tasks", icon: Bolt },
|
||||
];
|
||||
|
||||
onMount(async () => {
|
||||
@@ -81,98 +81,80 @@
|
||||
<Skeleton className="h-6 w-32 mb-2" />
|
||||
<Skeleton className="h-4 w-64" />
|
||||
</div>
|
||||
<div class="flex gap-12">
|
||||
<div class="w-48 space-y-2">
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<div class="flex gap-10">
|
||||
<div class="w-44 space-y-1.5">
|
||||
{#each Array(5) as _}
|
||||
<Skeleton className="h-9 w-full rounded-lg" />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex-1 space-y-4">
|
||||
<div class="rounded-lg border border-border bg-card p-6 space-y-3">
|
||||
<div class="rounded-xl border border-border bg-card p-6 space-y-3">
|
||||
<Skeleton className="h-4 w-40" />
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<Skeleton className="h-4 w-2/3" />
|
||||
</div>
|
||||
<div class="rounded-lg border border-border bg-card p-6 space-y-3">
|
||||
<div class="rounded-xl border border-border bg-card p-6 space-y-3">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
<Skeleton className="h-9 w-full" />
|
||||
<Skeleton className="h-9 w-full" />
|
||||
</div>
|
||||
<div class="rounded-lg border border-border bg-card p-6 space-y-3">
|
||||
<Skeleton className="h-4 w-28" />
|
||||
<Skeleton className="h-4 w-full" />
|
||||
<Skeleton className="h-4 w-5/6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-8">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-2 text-text-primary">Settings</h2>
|
||||
<h2 class="text-xl font-bold mb-1.5 text-text-primary">Settings</h2>
|
||||
<p class="text-[13px] text-text-secondary leading-relaxed">
|
||||
Configure metadata sources, cleanup rules, and scheduled scans.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col lg:flex-row gap-8 lg:gap-12">
|
||||
<div class="flex flex-col lg:flex-row gap-8 lg:gap-10">
|
||||
<!-- Sidebar Navigation -->
|
||||
<aside class="w-full lg:w-48 flex-shrink-0">
|
||||
<aside class="w-full lg:w-44 shrink-0">
|
||||
<nav class="space-y-0.5">
|
||||
{#each sections as section}
|
||||
{@const isActive = currentSection === section.id}
|
||||
<button
|
||||
class="w-full flex items-center gap-3 px-3 py-2 rounded-lg text-left transition-all border
|
||||
{currentSection === section.id
|
||||
? 'bg-white text-black border-white'
|
||||
: 'text-text-secondary hover:text-white hover:bg-bg-hover border-transparent'}"
|
||||
class={`w-full flex items-center gap-2.5 px-3 py-2 rounded-lg text-left text-[13px] font-medium transition-all duration-150 relative
|
||||
${isActive
|
||||
? "bg-bg-hover text-text-primary"
|
||||
: "text-text-secondary hover:text-text-primary hover:bg-bg-hover/60"
|
||||
}`}
|
||||
on:click={() => (currentSection = section.id)}
|
||||
>
|
||||
{#if section.icon === "settings"}
|
||||
<Settings class="w-4 h-4" />
|
||||
{:else if section.icon === "calendar"}
|
||||
<Calendar class="w-4 h-4" />
|
||||
{:else if section.icon === "bolt"}
|
||||
<Bolt class="w-4 h-4" />
|
||||
{:else if section.icon === "wand"}
|
||||
<Wand2 class="w-4 h-4" />
|
||||
{:else if section.icon === "plug"}
|
||||
<Plug class="w-4 h-4" />
|
||||
{:else if section.icon === "folder"}
|
||||
<Folder class="w-4 h-4" />
|
||||
{#if isActive}
|
||||
<span class="absolute left-0 top-1/2 -translate-y-1/2 h-4 w-0.5 rounded-full bg-accent"></span>
|
||||
{/if}
|
||||
<span class="text-[13px] font-medium">{section.label}</span>
|
||||
<svelte:component this={section.icon} class="w-4 h-4 shrink-0" />
|
||||
<span>{section.label}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex-1 min-w-0 space-y-4">
|
||||
{#if error}
|
||||
<div
|
||||
class="mb-6 px-5 py-4 bg-red-500/5 border border-red-500/20 rounded-xl"
|
||||
>
|
||||
<div class="px-4 py-3 bg-red-500/8 border border-red-500/20 rounded-xl">
|
||||
<p class="text-[13px] text-red-300">{error}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if successMessage}
|
||||
<div
|
||||
class="mb-6 px-5 py-4 bg-green-500/5 border border-green-500/20 rounded-xl"
|
||||
>
|
||||
<p class="text-[13px] text-green-300">{successMessage}</p>
|
||||
<div class="px-4 py-3 bg-emerald-500/8 border border-emerald-500/20 rounded-xl">
|
||||
<p class="text-[13px] text-emerald-300">{successMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="rounded-xl border border-border bg-card/60 p-6 lg:p-8 shadow-sm"
|
||||
>
|
||||
<div class="rounded-xl border border-border bg-card shadow-[0_2px_8px_rgba(0,0,0,0.3),0_0_0_1px_rgba(255,255,255,0.04)] p-6 lg:p-8">
|
||||
{#if currentSection === "general"}
|
||||
<GeneralSettings {settings} {saving} onSave={handleSave} />
|
||||
{:else if currentSection === "folder-rules"}
|
||||
<FolderRulesSettings {settings} />
|
||||
{:else if currentSection === "scheduled"}
|
||||
<ScheduledScansSettings {settings} />
|
||||
<TasksSettings />
|
||||
{:else if currentSection === "cleanup"}
|
||||
<FilenameCleaningSettings {settings} {saving} onSave={handleSave} />
|
||||
{:else if currentSection === "integrations"}
|
||||
|
||||
@@ -6,20 +6,20 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 active:scale-[0.97]',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
outline: 'border border-input bg-background hover:bg-accent/10 hover:text-foreground',
|
||||
ghost: 'hover:bg-accent/10 hover:text-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-[0_1px_3px_rgba(0,0,0,0.3)]',
|
||||
secondary: 'bg-secondary text-secondary-foreground border border-border hover:bg-bg-hover hover:text-foreground',
|
||||
outline: 'border border-border bg-transparent hover:bg-white/5 hover:text-foreground',
|
||||
ghost: 'hover:bg-white/6 hover:text-foreground',
|
||||
link: 'text-accent underline-offset-4 hover:underline',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/85 shadow-[0_1px_3px_rgba(0,0,0,0.3)]',
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 px-3',
|
||||
sm: 'h-8 px-3 text-xs rounded-lg',
|
||||
lg: 'h-11 px-6',
|
||||
icon: 'h-10 w-10',
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<div
|
||||
class={cn(
|
||||
'rounded-lg border border-border bg-card text-card-foreground shadow-sm',
|
||||
'rounded-xl border border-border bg-card text-card-foreground shadow-[0_2px_8px_rgba(0,0,0,0.3),0_0_0_1px_rgba(255,255,255,0.04)]',
|
||||
showSkeleton ? 'relative overflow-hidden' : '',
|
||||
className,
|
||||
restClass
|
||||
@@ -31,6 +31,6 @@
|
||||
>
|
||||
<slot />
|
||||
{#if showSkeleton}
|
||||
<div class="pointer-events-none absolute inset-0 bg-[color:var(--bg-hover)] opacity-40 animate-pulse"></div>
|
||||
<div class="pointer-events-none absolute inset-0 bg-[color:var(--bg-hover)] opacity-30 animate-pulse rounded-xl"></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -2,28 +2,28 @@ import { writable } from 'svelte/store'
|
||||
|
||||
export const themes = {
|
||||
oled: {
|
||||
name: 'OLED',
|
||||
name: 'Midnight',
|
||||
colors: {
|
||||
// Backgrounds (true OLED but layered)
|
||||
'bg-primary': '#000000',
|
||||
'bg-secondary': '#1a1a1a',
|
||||
'bg-card': '#0b0b0b',
|
||||
'bg-hover': '#2a2a2a',
|
||||
// Midnight backgrounds — sophisticated dark, not pure black
|
||||
'bg-primary': '#0d0e11',
|
||||
'bg-secondary': '#13141a',
|
||||
'bg-card': '#17181f',
|
||||
'bg-hover': '#1e2028',
|
||||
|
||||
// Text
|
||||
'text-primary': '#ffffff',
|
||||
'text-secondary': '#b0b0b0',
|
||||
'text-tertiary': '#7a7a7a',
|
||||
// Text — slightly warm off-white with clear hierarchy
|
||||
'text-primary': '#edeef3',
|
||||
'text-secondary': '#888ba8',
|
||||
'text-tertiary': '#50526b',
|
||||
|
||||
// UI chrome
|
||||
'border': 'rgba(255, 255, 255, 0.08)',
|
||||
|
||||
// Accents & interaction
|
||||
'accent': '#3b82f6', // restrained blue
|
||||
'button-bg': '#0f0f0f',
|
||||
'button-hover': '#1a1a1a',
|
||||
'button-text': '#ffffff',
|
||||
'focus-ring': 'rgba(255, 255, 255, 0.25)',
|
||||
'accent': '#a5b4fc', // soft indigo for links/highlights
|
||||
'button-bg': '#1a1b22',
|
||||
'button-hover': '#1e2028',
|
||||
'button-text': '#edeef3',
|
||||
'focus-ring': 'rgba(165, 180, 252, 0.35)',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -42,36 +42,36 @@ export const themes = {
|
||||
'text-tertiary': '#6f8fb6',
|
||||
|
||||
// Borders
|
||||
'border': 'rgba(120, 170, 220, 0.18)',
|
||||
'border': 'rgba(120, 170, 220, 0.15)',
|
||||
|
||||
// Accents & interaction (this is the magic)
|
||||
// Accents & interaction
|
||||
'accent': '#5fa8ff', // beautiful ocean blue
|
||||
'button-bg': '#132646',
|
||||
'button-hover': '#1b3560',
|
||||
'button-text': '#eaf3ff',
|
||||
'focus-ring': 'rgba(95, 168, 255, 0.45)',
|
||||
'focus-ring': 'rgba(95, 168, 255, 0.4)',
|
||||
}
|
||||
},
|
||||
|
||||
light: {
|
||||
name: 'Light',
|
||||
colors: {
|
||||
'bg-primary': '#f8f9fa',
|
||||
'bg-secondary': '#f1f3f5',
|
||||
'bg-primary': '#f6f7f9',
|
||||
'bg-secondary': '#eef0f3',
|
||||
'bg-card': '#ffffff',
|
||||
'bg-hover': '#e9ecef',
|
||||
'bg-hover': '#e8eaee',
|
||||
|
||||
'text-primary': '#1a1a1a',
|
||||
'text-secondary': '#5c5f66',
|
||||
'text-tertiary': '#868e96',
|
||||
'text-primary': '#111318',
|
||||
'text-secondary': '#5c5f73',
|
||||
'text-tertiary': '#9094aa',
|
||||
|
||||
'border': '#dee2e6',
|
||||
'border': 'rgba(0, 0, 0, 0.08)',
|
||||
|
||||
'accent': '#2563eb',
|
||||
'accent': '#4f46e5', // indigo accent
|
||||
'button-bg': '#ffffff',
|
||||
'button-hover': '#f1f3f5',
|
||||
'button-text': '#1a1a1a',
|
||||
'focus-ring': 'rgba(37, 99, 235, 0.35)',
|
||||
'button-hover': '#f0f1f5',
|
||||
'button-text': '#111318',
|
||||
'focus-ring': 'rgba(79, 70, 229, 0.3)',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,300..700;1,14..32,300..700&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@@ -6,22 +6,23 @@
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
/* Default OLED theme */
|
||||
--bg-primary: #000000;
|
||||
--bg-secondary: #050505;
|
||||
--bg-card: #0a0a0a;
|
||||
--bg-hover: #141414;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #999999;
|
||||
--text-tertiary: #666666;
|
||||
--border: rgba(255, 255, 255, 0.06);
|
||||
--accent: #3b82f6;
|
||||
--focus-ring: rgba(255, 255, 255, 0.25);
|
||||
/* Default Midnight theme (replaces harsh OLED black) */
|
||||
--bg-primary: #0d0e11;
|
||||
--bg-secondary: #13141a;
|
||||
--bg-card: #17181f;
|
||||
--bg-hover: #1e2028;
|
||||
--text-primary: #edeef3;
|
||||
--text-secondary: #888ba8;
|
||||
--text-tertiary: #50526b;
|
||||
--border: rgba(255, 255, 255, 0.08);
|
||||
--accent: #a5b4fc;
|
||||
--focus-ring: rgba(165, 180, 252, 0.35);
|
||||
|
||||
/* Shadows for depth */
|
||||
--shadow-sm: inset 0 1px 0 rgba(255, 255, 255, 0.03);
|
||||
--shadow-card: 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||
--shadow-elevated: 0 0 0 1px rgba(255, 255, 255, 0.06), 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||
--shadow-elevated: 0 8px 24px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.08);
|
||||
--shadow-modal: 0 24px 64px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.08);
|
||||
|
||||
/* Shadcn-style tokens mapped to theme variables */
|
||||
--background: var(--bg-primary);
|
||||
@@ -41,7 +42,7 @@
|
||||
--destructive-foreground: #fef2f2;
|
||||
--input: var(--border);
|
||||
--ring: var(--focus-ring);
|
||||
--radius: 0.5rem;
|
||||
--radius: 0.75rem;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -49,7 +50,8 @@
|
||||
color: var(--foreground);
|
||||
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.25rem;
|
||||
line-height: 1.6;
|
||||
letter-spacing: -0.011em;
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@@ -61,8 +63,19 @@
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
/* Heading scale */
|
||||
h1 { font-size: 1.5rem; line-height: 1.25; font-weight: 700; letter-spacing: -0.025em; }
|
||||
h2 { font-size: 1.25rem; line-height: 1.3; font-weight: 700; letter-spacing: -0.02em; }
|
||||
h3 { font-size: 1rem; line-height: 1.35; font-weight: 600; letter-spacing: -0.015em; }
|
||||
h4 { font-size: 0.9375rem;line-height: 1.4; font-weight: 600; letter-spacing: -0.01em; }
|
||||
|
||||
/* Interactive elements — smooth transitions */
|
||||
a, button {
|
||||
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
@apply w-2 h-2;
|
||||
@apply w-1.5 h-1.5;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@@ -71,19 +84,31 @@
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
@apply rounded;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
/* Light theme scrollbar adjustment */
|
||||
.light-theme ::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
background: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.light-theme ::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.shadow-card {
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.shadow-elevated {
|
||||
box-shadow: var(--shadow-elevated);
|
||||
}
|
||||
.shadow-modal {
|
||||
box-shadow: var(--shadow-modal);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user