all repos — weimar @ 30c984bbd9a52787565d44cc121088cf1ee25029

Unnamed repository; edit this file 'description' to name the repository.

feat: dynamic tab titles based on route

Uses /state +  in layout to set document.title per route
(browse, upload, settings, login, image/#id). Also updates title in
image viewer overlay when opening/closing/navigating between images.
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 18:09:14 +0200
commit

30c984bbd9a52787565d44cc121088cf1ee25029

parent

9b2b569d75975b2ae6868e6708a18ee78a305125

3 files changed, 38 insertions(+), 1 deletions(-)

jump to
M web/src/lib/ImageViewer.svelteweb/src/lib/ImageViewer.svelte

@@ -87,6 +87,8 @@ function prev() {

if (currentIndex > 0) { currentIndex--; loadImage(); + history.replaceState(null, '', '/image/' + current.id); + document.title = document.title.replace(/ - Image #\d+$/, '') + ' - Image #' + current.id; } }

@@ -94,6 +96,8 @@ function next() {

if (currentIndex < images.length - 1) { currentIndex++; loadImage(); + history.replaceState(null, '', '/image/' + current.id); + document.title = document.title.replace(/ - Image #\d+$/, '') + ' - Image #' + current.id; } }
M web/src/routes/+layout.svelteweb/src/routes/+layout.svelte

@@ -1,5 +1,6 @@

<script lang="ts"> import { onMount } from 'svelte'; + import { page } from '$app/state'; import { logout, getMe, getConfig, type CurrentUser, type SiteConfig } from '$lib/api'; import Avatar from '$lib/Avatar.svelte';

@@ -8,15 +9,44 @@

let user = $state<CurrentUser | null>(null); let checking = $state(true); let siteConfig = $state<SiteConfig | null>(null); + let baseTitle = $state('weimar'); onMount(() => { getConfig() - .then((c) => { siteConfig = c; document.title = c.site_title; }) + .then((c) => { + siteConfig = c; + baseTitle = c.site_title; + }) .catch(() => {}); getMe() .then((u) => { user = u; }) .catch(() => {}) .finally(() => { checking = false; }); + }); + + $effect(() => { + const path = page.url.pathname; + const base = baseTitle; + switch (path) { + case '/browse': + document.title = base + ' - Browse'; + break; + case '/upload': + document.title = base + ' - Upload'; + break; + case '/settings': + document.title = base + ' - Settings'; + break; + case '/login': + document.title = base + ' - Login'; + break; + default: + if (path.startsWith('/image/')) { + document.title = base + ' - Image #' + path.split('/')[2]; + } else { + document.title = base; + } + } }); async function handleLogout() {
M web/src/routes/browse/+page.svelteweb/src/routes/browse/+page.svelte

@@ -145,6 +145,7 @@

function openViewer(index: number) { viewerIndex = index; history.replaceState(null, '', '/image/' + images[index].id); + document.title = document.title.replace(/ - .*$/, '') + ' - Image #' + images[index].id; } function closeViewer() {

@@ -152,7 +153,9 @@ viewerIndex = -1;

const params = new URLSearchParams(); if (filterTag) params.set('tag', filterTag); if (filterUploader) params.set('uploader', filterUploader); + const baseTitle = document.title.replace(/ - .*$/, ''); history.replaceState(null, '', params.toString() ? '/browse?' + params.toString() : '/browse'); + document.title = baseTitle + ' - Browse'; // Restore main layout style. const mainEl = document.querySelector('.main') as HTMLElement; if (mainEl) {