mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 01:09:56 -05:00
Feat: Re-did Layout & Sidebar
This commit is contained in:
@@ -11,32 +11,38 @@
|
||||
import Tracking from "../pages/Tracking.svelte";
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<Sidebar />
|
||||
<main class="main">
|
||||
{#if store.activeManga}
|
||||
<SeriesDetail />
|
||||
{:else if store.navPage === "home"}
|
||||
<Home />
|
||||
{:else if store.navPage === "library"}
|
||||
<Library />
|
||||
{:else if store.navPage === "search"}
|
||||
<Search />
|
||||
{:else if store.navPage === "history"}
|
||||
<RecentActivity />
|
||||
{:else if store.navPage === "downloads"}
|
||||
<Downloads />
|
||||
{:else if store.navPage === "extensions"}
|
||||
<Extensions />
|
||||
{:else if store.navPage === "tracking"}
|
||||
<Tracking />
|
||||
{:else}
|
||||
<Home />
|
||||
{/if}
|
||||
</main>
|
||||
<div class="frame">
|
||||
<div class="shell">
|
||||
<Sidebar />
|
||||
<main class="main">
|
||||
{#if store.activeManga}
|
||||
<SeriesDetail />
|
||||
{:else if store.navPage === "home"}
|
||||
<Home />
|
||||
{:else if store.navPage === "library"}
|
||||
<Library />
|
||||
{:else if store.navPage === "search"}
|
||||
<Search />
|
||||
{:else if store.navPage === "history"}
|
||||
<RecentActivity />
|
||||
{:else if store.navPage === "downloads"}
|
||||
<Downloads />
|
||||
{:else if store.navPage === "extensions"}
|
||||
<Extensions />
|
||||
{:else if store.navPage === "tracking"}
|
||||
<Tracking />
|
||||
{:else}
|
||||
<Home />
|
||||
{/if}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root { display: flex; height: 100%; background: var(--bg-base); overflow: hidden; }
|
||||
.main { flex: 1; overflow: hidden; background: var(--bg-surface); transform: translateZ(0); contain: layout style; }
|
||||
</style>
|
||||
:global(*, *::before, *::after) { scrollbar-width: none; }
|
||||
:global(*::-webkit-scrollbar) { display: none; }
|
||||
|
||||
.frame { display: flex; padding: 0 10px 10px; width: 100%; height: 100%; box-sizing: border-box; overflow: hidden; }
|
||||
.shell { display: flex; flex: 1; border-radius: 14px; overflow: hidden; border: 1px solid var(--border-dim); background: var(--bg-base); min-height: 0; min-width: 0; }
|
||||
.main { flex: 1; overflow: hidden; background: var(--bg-surface); transform: translateZ(0); contain: layout style; min-width: 0; }
|
||||
</style>
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
{ id: "tracking", label: "Tracking", icon: ChartLineUp },
|
||||
];
|
||||
|
||||
const TAB_SIZE = 36;
|
||||
const TAB_GAP = 4;
|
||||
|
||||
const anims = $derived(store.settings.qolAnimations ?? true);
|
||||
const activeIndex = $derived(TABS.findIndex(t => t.id === store.navPage));
|
||||
const indicatorY = $derived(activeIndex * (TAB_SIZE + TAB_GAP));
|
||||
|
||||
function navigate(id: NavPage) {
|
||||
store.navPage = id;
|
||||
@@ -38,7 +42,7 @@
|
||||
</button>
|
||||
<nav class="nav">
|
||||
{#if activeIndex >= 0}
|
||||
<div class="slide-indicator" class:anims style="--idx: {activeIndex}"></div>
|
||||
<div class="slide-indicator" class:anims style="transform: translateX(-50%) translateY({indicatorY}px)"></div>
|
||||
{/if}
|
||||
{#each TABS as tab}
|
||||
<button class="tab" class:active={store.navPage === tab.id} class:anims
|
||||
@@ -55,47 +59,36 @@
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
.root { width: var(--sidebar-width); flex-shrink: 0; background: var(--bg-void); display: flex; flex-direction: column; align-items: center; padding: var(--sp-4) 0; overflow: hidden; min-height: 0; height: 100%; }
|
||||
.root { width: var(--sidebar-width); flex-shrink: 0; background: transparent; display: flex; flex-direction: column; align-items: center; padding: var(--sp-4) 0; overflow: hidden; min-height: 0; height: 100%; }
|
||||
|
||||
/* Logo */
|
||||
.logo { width: 80px; height: 80px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; margin-bottom: var(--sp-3); background: none; border: none; outline: none; cursor: pointer; border-radius: var(--radius-lg); padding: 0; appearance: none; -webkit-appearance: none; }
|
||||
.logo { width: 56px; height: 56px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; margin-bottom: var(--sp-4); background: none; border: none; outline: none; cursor: pointer; border-radius: var(--radius-lg); padding: 0; appearance: none; -webkit-appearance: none; }
|
||||
.logo:hover { opacity: 0.8; }
|
||||
.logo:active { }
|
||||
.logo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||||
/* QOL: logo press + hover scale */
|
||||
.logo.anims { transition: opacity var(--t-base), transform var(--t-base); }
|
||||
.logo.anims:hover { transform: scale(0.96); }
|
||||
.logo.anims:active { transform: scale(0.92); }
|
||||
|
||||
.logo-icon { width: 67px; height: 67px; background-color: var(--accent); mask-image: url("../../assets/moku-icon-wordmark.svg"); mask-repeat: no-repeat; mask-position: center; mask-size: contain; -webkit-mask-image: url("../../assets/moku-icon-wordmark.svg"); -webkit-mask-repeat: no-repeat; -webkit-mask-position: center; -webkit-mask-size: contain; filter: drop-shadow(0 0 8px rgba(107,143,107,0.35)); pointer-events: none; }
|
||||
.logo-icon { width: 52px; height: 52px; background-color: var(--accent); mask-image: url("../../assets/moku-icon-wordmark.svg"); mask-repeat: no-repeat; mask-position: center; mask-size: contain; -webkit-mask-image: url("../../assets/moku-icon-wordmark.svg"); -webkit-mask-repeat: no-repeat; -webkit-mask-position: center; -webkit-mask-size: contain; filter: drop-shadow(0 0 8px rgba(107,143,107,0.35)); pointer-events: none; }
|
||||
|
||||
/* Nav */
|
||||
.nav { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); width: 100%; padding: 0 var(--sp-2); overflow-y: auto; overflow-x: hidden; scrollbar-width: none; }
|
||||
.nav { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; width: 100%; padding: 0 var(--sp-2); overflow-y: auto; overflow-x: hidden; scrollbar-width: none; }
|
||||
.nav::-webkit-scrollbar { display: none; }
|
||||
|
||||
.slide-indicator { position: absolute; width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--accent-muted); pointer-events: none; top: 0; left: 50%; translate: -50% calc(var(--idx) * (36px + var(--sp-1))); z-index: 0; }
|
||||
.slide-indicator.anims { transition: translate 0.22s cubic-bezier(0.16,1,0.3,1); }
|
||||
.slide-indicator { position: absolute; width: 36px; height: 36px; border-radius: var(--radius-md); background: var(--accent-muted); pointer-events: none; top: 0; left: 50%; transform: translateX(-50%) translateY(0px); z-index: 0; }
|
||||
.slide-indicator.anims { transition: transform 0.22s cubic-bezier(0.16,1,0.3,1); }
|
||||
|
||||
/* Tab base — no transitions by default */
|
||||
.tab { position: relative; z-index: 1; width: 36px; height: 36px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); color: var(--text-faint); background: none; border: none; outline: none; cursor: pointer; padding: 0; appearance: none; -webkit-appearance: none; }
|
||||
.tab:hover { color: var(--text-muted); background: var(--bg-raised); }
|
||||
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
||||
.tab.active { color: var(--accent-fg); background: transparent; }
|
||||
.tab.active:hover { color: var(--accent-fg); background: transparent; }
|
||||
|
||||
/* QOL: smooth color + bg transitions, subtle scale on click */
|
||||
.tab.anims { transition: color var(--t-base), background var(--t-base), transform 80ms ease, box-shadow var(--t-base); }
|
||||
.tab.anims:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0,0,0,0.25); }
|
||||
.tab.anims { transition: color var(--t-base), background var(--t-base); }
|
||||
.tab.anims:active { transform: scale(0.88); }
|
||||
.tab.anims.active { box-shadow: none; }
|
||||
|
||||
/* Bottom / settings */
|
||||
.bottom { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; width: 100%; padding: var(--sp-3) var(--sp-2) 0; border-top: 1px solid var(--border-dim); margin-top: var(--sp-3); }
|
||||
|
||||
.settings-btn { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); color: var(--text-faint); background: none; border: none; outline: none; cursor: pointer; padding: 0; appearance: none; -webkit-appearance: none; }
|
||||
.settings-btn:hover { color: var(--text-muted); background: var(--bg-raised); }
|
||||
.settings-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
||||
/* QOL: gear spin on hover */
|
||||
.settings-btn.anims { transition: color var(--t-base), background var(--t-base), transform var(--t-slow); }
|
||||
.settings-btn.anims:hover { transform: rotate(30deg); }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else if isWindows}
|
||||
<!-- On Windows, fullscreen hides the native titlebar — show a hoverable overlay so the user isn't locked in -->
|
||||
<div class="fullscreen-controls">
|
||||
<button onclick={() => win.setFullscreen(false)} title="Exit Fullscreen" aria-label="Exit Fullscreen">
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
@@ -65,6 +64,55 @@
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 36px;
|
||||
padding: 0 6px 0 var(--sp-4);
|
||||
background: transparent;
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.mac-spacer {
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--text-2xs);
|
||||
color: var(--text-faint);
|
||||
letter-spacing: var(--tracking-wider);
|
||||
text-transform: uppercase;
|
||||
opacity: 0.5;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-faint);
|
||||
transition: color var(--t-base), background var(--t-base);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
button:hover { color: var(--text-muted); background: rgba(255,255,255,0.06); }
|
||||
.close:hover { color: #fff; background: #c0392b; }
|
||||
|
||||
.fullscreen-controls {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -79,49 +127,4 @@
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
.fullscreen-controls:hover { opacity: 1; }
|
||||
|
||||
.bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 32px;
|
||||
padding: 0 var(--sp-3) 0 var(--sp-4);
|
||||
background: var(--bg-void);
|
||||
border-bottom: 1px solid var(--border-dim);
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
/* Spacer to clear the native macOS traffic lights (~70px) */
|
||||
.mac-spacer {
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
.title {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--text-2xs);
|
||||
color: var(--text-faint);
|
||||
letter-spacing: var(--tracking-wider);
|
||||
text-transform: uppercase;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px; height: 28px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-faint);
|
||||
transition: color var(--t-base), background var(--t-base);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
button:hover { color: var(--text-muted); background: var(--bg-raised); }
|
||||
.close:hover { color: #fff; background: #c0392b; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user