Files
chitai/frontend/src/routes/(root)/settings/+layout.svelte
T
patrick 3a29294f96 chore: clear the mechanical lint and type findings
Dead imports and locals removed, each blocks keyed, `any` narrowed to unknown.
Two context setters kept their calls and lost only the unused binding; the
settings redirect no longer awaits a parent whose data it discards. A leading
underscore now marks a binding that only holds a position.
2026-08-17 17:55:21 -04:00

79 lines
2.8 KiB
Svelte

<script lang="ts">
import { page } from '$app/state';
import { resolve } from '$app/paths';
import { getLibraryState } from '$lib/state/library.svelte';
let { children } = $props();
// Set in (root)/+layout.svelte, above this group, so listing the libraries in
// the nav needs no load function.
const libraryState = getLibraryState();
// Route ids rather than paths: resolve() is called in the markup so it stays a
// direct call the lint rule can see, and the active check compares route ids.
// A pathname comparison would miss during SSR, where resolve() returns a
// relative path, and only settle after hydration.
const items = [
{ title: 'Account', routeId: '/(root)/settings/account' },
{ title: 'Appearance', routeId: '/(root)/settings/appearance' },
{ title: 'Libraries', routeId: '/(root)/settings/libraries' },
{ title: 'Devices', routeId: '/(root)/settings/devices' }
] as const;
/**
* Whether a nested library item is the one being looked at.
*
* Both halves are needed: the route id alone is shared by every library, so
* matching on it would light all of them up at once.
*/
function isActiveLibrary(id: number) {
return (
page.route.id?.startsWith('/(root)/settings/libraries/[libraryId]') === true &&
page.params.libraryId === String(id)
);
}
</script>
<div class="flex h-[calc(100vh-var(--header-height)-2rem)] flex-col">
<h1 class="font-serif text-xl font-medium tracking-tight">Settings</h1>
<div class="mt-6 flex flex-1 gap-8 overflow-hidden">
<!-- Scrolls on its own now that it holds a list that grows with the number
of libraries; the row above it is overflow-hidden, so without this a
long list is clipped rather than reachable. -->
<nav class="flex w-48 shrink-0 flex-col gap-1 overflow-y-auto">
{#each items as item (item.routeId)}
<a
href={resolve(item.routeId)}
class="rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-muted {page
.route.id === item.routeId
? 'bg-muted'
: 'text-muted-foreground'}"
>
{item.title}
</a>
<!-- Libraries is the one item with children: a library's own settings
hang off it, so every library and every section it has stay one
click from here. -->
{#if item.routeId === '/(root)/settings/libraries'}
{#each libraryState.libraries as library (library.id)}
<a
href={resolve('/(root)/settings/libraries/[libraryId]/duplicates', {
libraryId: String(library.id)
})}
class="ml-3 truncate rounded-md border-l px-3 py-1.5 text-sm transition-colors hover:bg-muted {isActiveLibrary(
library.id
)
? 'bg-muted font-medium'
: 'text-muted-foreground'}"
>
{library.name}
</a>
{/each}
{/if}
{/each}
</nav>
<div class="flex-1 overflow-auto">{@render children()}</div>
</div>
</div>