# geschichte (frontend) UI for family stories (Geschichte) and reading journeys (Lesereise): the rich-text editor, story/journey readers, type badge, and list rows. ## What this domain owns Components: `GeschichteEditor.svelte`, `GeschichtenCard.svelte`, `GeschichteListRow.svelte`, `StoryReader.svelte`, `JourneyReader.svelte`, `JourneyItemCard.svelte`, `JourneyInterlude.svelte`. Utilities: `utils.ts`. ## What this domain does NOT own - Comment/discussion UI — shared via `shared/discussion/` (same component used for document comments) - Person display — `person/PersonChip.svelte` is used inside story content (cross-domain import) - Document display — document references in stories use components from `document/` ## Key components | Component | Used in | Notes | | -------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------- | | `GeschichteEditor.svelte` | `/geschichten/new`, `/geschichten/[id]/edit` | Rich-text editor with person/document @-mentions and inline embeds | | `GeschichtenCard.svelte` | Person-detail sidebar | Sidebar card showing the 3 most recent stories linked to a person — NOT the list page | | `GeschichteListRow.svelte` | `/geschichten` (list) | Row component for the list; shows JOURNEY type badge (`text-xs` orange pill) | | `StoryReader.svelte` | `/geschichten/[id]` (STORY branch) | Renders sanitised rich-text body, persons section, documents section, and author actions | | `JourneyReader.svelte` | `/geschichten/[id]` (JOURNEY branch) | Renders intro paragraph, ordered items list, empty-state; delegates to ItemCard/Interlude | | `JourneyItemCard.svelte` | `JourneyReader.svelte` | Whole-card `` for a document item; dated/undated aria-label, ✎ annotation glyph | | `JourneyInterlude.svelte` | `JourneyReader.svelte` | Typographic aside between letters; ❦ glyph, `aria-label="Kuratorennotiz"` | ## utils.ts `formatAuthorName(author)` — joins `firstName + lastName`, falls back to `email` (for list/summary shapes). `formatAuthorDisplayName(author)` — returns `displayName` (for detail `AuthorView` shape). `formatPublishedAt(publishedAt, style)` — wraps `formatDate` with null check; `style` is `'short'` (list) or `'long'` (detail). ## Public list is PUBLISHED-only `GET /api/geschichten` constrains `status=PUBLISHED`, so DRAFT journeys never appear in the list. The REISE badge is only ever seen for published journeys. Empty-state and draft-preview paths are reachable ONLY via the **detail route** (`/geschichten/[id]`), not the list. Wire empty-state E2E tests through the detail route, not by expecting a draft journey in the list. ## TypeSelector route component `TypeSelector.svelte` lives in `src/routes/geschichten/new/` (single-use route UI). It is NOT in `$lib/geschichte/` — route-specific, not reused elsewhere. `StoryCreate.svelte` (also in `new/`) wraps `GeschichteEditor` so tree-shaking excludes TipTap from the JOURNEY placeholder path. ## Audience note The `/geschichten` route primarily serves readers (younger family members on mobile). Cards must have ≥ 44 px touch targets. Status must not rely on color alone. JourneyReader mobile layout is Critical; TypeSelector is Minor. ## Cross-domain imports - `person/PersonChip.svelte` — inline person references in story content - `document/DocumentThumbnail.svelte` — inline document references - `shared/discussion/` — comment thread below published stories ## Backend counterpart `backend/src/main/java/org/raddatz/familienarchiv/geschichte/README.md`