feat(shell): Routen, Kopfzeile, Hell/Dunkel, Spenden-Banner

- Router: Routen home, gestalten, cards, community/<id>, reset; Umleitung
  /?karte=<id> nach /gestalten?karte=<id>; Titel je Route (titleFor);
  Hash-Ziele (/#community) scrollen den Anker ins Bild, nur-Hash-Wechsel
  bauen die Seite nicht neu auf.
- Kopfzeile: Navigation, Suche (Enter -> /?q=...#community), Spenden-Knopf,
  Hell/Dunkel-Schalter, Menue unter 900 px, Konto als Avatar-Kreis mit Menue.
- Theme: data-theme auf <html> (Skript im <head> gegen Aufblitzen), helle
  Tokens, danger-/warn-/ok-text-Tokens statt fester Farben.
- Spenden-Banner (schliessbar); ohne DONATE_URL bleiben Banner und Knopf weg.
- User.labels und isAdmin; Platzhalter fuer Startseite und Detailseite.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 13:39:09 +00:00
1 parent 1a64e12bc5
commit 25ff496548
30 files changed
+1783 -189

No files matched your search

+32 -7
View File
@@ -2,18 +2,37 @@ import '@fontsource-variable/nunito-sans';
import './styles.css';
import { mountAccountMenu } from './account/account-menu';
import { getCurrentUser } from './account/auth';
import { startRouter } from './pages/router';
import { DONATE_URL } from './config';
import { legacyRedirect, startRouter } from './pages/router';
import { mountCardsPage } from './pages/cards-page';
import { mountCommunityPage } from './pages/community-page';
import { mountHomePage } from './pages/home-page';
import { mountResetPage } from './pages/reset-page';
import { mountEditor } from './editor/editor-page';
import { bindEditorToAuth } from './editor/auth-reset';
import { createStore, initialEditorState } from './editor/state';
import { mountDonateBanner } from './shell/donate-banner';
import { mountHeader } from './shell/header';
import { initTheme } from './shell/theme';
// Hell/Dunkel: das Skript in index.html hat es vor dem ersten Paint gesetzt; hier wird nur die Browserleiste angeglichen.
initTheme();
// Alte Editor-Links (`/?karte=<id>`) zeigen jetzt auf `/gestalten?karte=<id>`; der Verlauf bekommt keinen Eintrag dazu.
const legacyTarget = legacyRedirect(new URL(location.href));
if (legacyTarget) history.replaceState(null, '', legacyTarget);
// Der Editor-Zustand lebt außerhalb der Seiten und überlebt Seitenwechsel.
const editorStore = createStore(initialEditorState());
// … und gehört zum angemeldeten Konto: beim Abmelden oder Kontowechsel wird er geleert bzw. entkoppelt.
bindEditorToAuth(editorStore);
// Spenden-Banner (ohne DONATE_URL bleibt der Platz leer) und Kopfzeile mit Navigation, Suche und Knöpfen.
const bannerSlot = document.getElementById('donate-banner');
if (bannerSlot) mountDonateBanner(bannerSlot, DONATE_URL);
const header = document.getElementById('app-header');
if (header) mountHeader(header, { donateUrl: DONATE_URL });
// Konto-Menü im Seitenkopf. Der Anmeldestatus wird einmal beim Start geholt; schlägt das fehl
// (außer 401, das getCurrentUser selbst als „abgemeldet“ meldet), gilt der Nutzer als abgemeldet.
const accountSlot = document.getElementById('account-slot');
@@ -26,12 +45,18 @@ if (accountSlot) {
.finally(() => menu.ready());
}
// Der Router setzt `document.title` je Seite (titleFor) und ruft für jede Route die passende Mount-Funktion.
startRouter((route, root) => {
if (route === 'editor') {
return mountEditor(root, editorStore);
switch (route.id) {
case 'gestalten':
return mountEditor(root, editorStore);
case 'cards':
return mountCardsPage(root, editorStore);
case 'community':
return mountCommunityPage(root, route.params.id ?? '');
case 'reset':
return mountResetPage(root);
case 'home':
return mountHomePage(root);
}
if (route === 'reset') {
return mountResetPage(root);
}
return mountCardsPage(root, editorStore);
});