feat(geschichten): frontend foundation — canBlogWrite, sanitize util, nav, i18n

- Derives canBlogWrite in +layout.server.ts the same way as canAnnotate.
- Adds Geschichten link to AppNav (desktop + mobile, between Stammbaum and Admin).
- Adds error_geschichte_not_found mapping to errors.ts and translation keys
  for the Geschichten index, detail, editor, and confirmation copy in
  de/en/es.
- Adds isomorphic-dompurify-backed safeHtml() helper with allow-list
  matching the backend OWASP policy (p/br/strong/em/h2/h3/ul/ol/li),
  plus Vitest spec.
- Updates legacy spec test data so the new required canBlogWrite layout
  prop type-checks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-02 17:43:29 +02:00
parent afd6d0b20d
commit 9e7861fa03
18 changed files with 998 additions and 28 deletions

View File

@@ -68,6 +68,16 @@ function handleOverlayKeydown(event: KeyboardEvent) {
>
{m.nav_stammbaum()}
</a>
<a
href="/geschichten"
class="my-2 inline-flex items-center px-3 font-sans text-xs font-bold tracking-widest uppercase transition-colors focus:outline-none focus-visible:rounded focus-visible:ring-2 focus-visible:ring-focus-ring
{page.url.pathname.startsWith('/geschichten')
? 'border-b-2 border-accent text-white'
: 'text-white/70 hover:text-white'}"
>
{m.nav_geschichten()}
</a>
{#if isAdmin}
<a
href="/admin"
@@ -170,6 +180,16 @@ function handleOverlayKeydown(event: KeyboardEvent) {
{m.nav_stammbaum()}
</a>
<a
href="/geschichten"
class="block flex min-h-[44px] w-full items-center px-4 py-3 font-sans text-sm font-bold tracking-widest uppercase transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-inset
{page.url.pathname.startsWith('/geschichten')
? 'bg-accent-bg text-ink'
: 'text-ink-2 hover:bg-muted hover:text-ink'}"
>
{m.nav_geschichten()}
</a>
{#if isAdmin}
<a
href="/admin"