larpaso-website/admin/public/index.html
Pascal Scholz 7c67ae1ff5 Add WYSIWYG rich-text editor and reorganize admin UI into tabs
Rich-text fields (hero lead, paragraphs, FAQ-style copy, datenschutz
sections, etc.) now use a proper toolbar editor - bold/italic/underline
buttons plus a per-selection font picker drawn from the same curated
font list as the Design tab - instead of typed **markdown** syntax.

Content is stored as a small allowlisted HTML subset (strong/em/u/span
with a validated data-font/br) and sanitized server-side with
sanitize-html on every save (admin/lib/rich-sanitize.js), independent
of what the client sends - verified this rejects script tags, event
handlers, and made-up font names while preserving legitimate markup,
and that re-saving already-sanitized content is a no-op.

The Astro site mirrors the same allowlist at render time for
defense-in-depth (src/lib/rich-sanitize.ts) and only requests Google
Fonts for fonts actually referenced somewhere in content, in addition
to the two Design-tab fonts - verified a font used via a data-font span
gets its own Google Fonts request and [data-font] CSS rule in the
built page, and that unused fonts don't get requested.

Migrated the three existing fields that used the old markdown-lite
syntax (**bold**/*italic*) to real HTML so they keep rendering
correctly under the new system.

Also replaces the single long scrolling admin page with a tabbed
sidebar (grouped "Inhalte"/"Design") to cut down on clutter, per
feedback that the all-in-one-page layout was hard to navigate.
2026-09-21 23:11:19 +02:00

36 lines
1.2 KiB
HTML

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex, nofollow" />
<title>larpaso · Editor</title>
<link rel="stylesheet" href="/admin/style.css" />
</head>
<body>
<header class="topbar">
<h1>larpaso · Editor</h1>
<div class="topbar-actions">
<span id="status" class="status" aria-live="polite"></span>
<button id="save-btn" type="button">Speichern</button>
<button id="publish-btn" type="button" class="btn-accent">Speichern &amp; veröffentlichen</button>
<form method="POST" action="/admin/logout"><button type="submit" class="btn-ghost">Abmelden</button></form>
</div>
</header>
<div class="layout">
<nav id="section-nav" class="section-nav" aria-label="Abschnitte"></nav>
<main id="sections"></main>
</div>
<div id="log" class="build-log" hidden>
<div class="build-log-header">
<span>Build-Ausgabe</span>
<button id="log-close" type="button" class="btn-ghost">Schließen</button>
</div>
<pre id="log-output"></pre>
</div>
<script src="/admin/app.js"></script>
</body>
</html>