managarten/apps/mana/apps
Till JS f24438f778 feat(mana/web): who module — frontend (game store + UI + routes)
Client side of the who module. Standard Mana module pattern: types,
collections (Dexie), queries (live), store (mutations), UI components,
routes. Plus three integration points (data layer registries).

Module files
------------
  types.ts
    Two Dexie record interfaces (LocalWhoGame, LocalWhoMessage) and
    matching view types. Server response shapes (WhoChatResponse,
    WhoRandomResponse, WhoGuessResponse) live here too so the store
    and UI both type-check against the same wire contract.

  collections.ts
    Dexie table accessors. No guest seed — the picker handles empty
    state directly.

  queries.ts
    Three liveQueries (allGames$, gameByIdLive, messagesForGameLive)
    that decrypt the encrypted-at-rest fields before returning view
    types. The messages query uses the [gameId+createdAt] composite
    index for ordering. toWhoGame / toWhoMessage converters bridge
    the BaseRecord-extended local types to the public view types.

  module.config.ts
    Standard ModuleConfig: appId='who', tables=[whoGames as 'games',
    whoMessages as 'messages']. The syncName remap means the unified
    Dexie table whoGames syncs to mana-sync's `games` collection
    under appId 'who' — keeps the wire format clean.

  stores/games.svelte.ts
    The mutation surface. Five public methods:
      - start(deckId)        → POST /who/random + insert LocalWhoGame
      - sendMessage(id, txt) → optimistic insert + POST /who/chat +
                               insert NPC reply + (on win) flip status
      - submitGuess(id, txt) → POST /who/guess + (on match) flip
      - surrender(id)        → status=surrendered + finishedAt
      - setNotes(id, notes)  → encrypted post-game notes
      - deleteGame(id)       → soft-delete game + cascade messages
    All writes go through encryptRecord for encrypted-at-rest fields.

UI components
-------------
  ListView.svelte
    Module landing page. Header + 4 deck cards (loaded from
    GET /api/v1/who/decks on mount) + past-games list. Picking a
    deck calls store.start() and navigates to the play view. Past
    games are clickable (read-only for finished games) and
    deletable.

  views/PlayView.svelte
    The chat-loop screen. Header with deck/difficulty + back button
    + Tippen/Aufgeben actions while playing. Scrollable message
    area with bubbles (user purple-tinted, NPC white-tinted).
    Textarea input with Enter-to-send + sending disabled state.
    On reveal: result banner with "Erraten in N Nachrichten!" and
    the resolved name. Post-game: input area swaps to a notes
    textarea with debounced auto-save. Explicit guess modal as
    fallback when the LLM forgets to emit the sentinel.

Routes
------
  /(app)/who                 → ListView wrapper
  /(app)/who/play/[gameId]   → PlayView wrapper, $page.params.gameId

Registry plumbing
-----------------
  database.ts
    Two new Dexie tables in version(1):
      whoGames: 'id, status, deckId, startedAt, finishedAt, [status+startedAt]'
      whoMessages: 'id, gameId, sender, createdAt, [gameId+createdAt]'

  module-registry.ts
    Imports whoModuleConfig and adds to MODULE_CONFIGS. The sync
    engine picks up the appId/table mapping automatically — no
    edits needed in sync.ts.

  crypto/registry.ts
    Two entries:
      whoGames:    { enabled: true, fields: ['revealedName', 'notes'] }
      whoMessages: { enabled: true, fields: ['content'] }
    All other fields stay plaintext for index/sort/filter.

Closes Phase A.2 / A.3 / A.4 / A.5 of docs/WHO_MODULE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:10:34 +02:00
..
landing docs(devlog): voice quick-add pipeline + LLM parsing live in prod 2026-04-08 18:01:21 +02:00
mobile chore: complete ManaCore → Mana rename (docs, go modules, plists, images) 2026-04-07 12:26:10 +02:00
web feat(mana/web): who module — frontend (game store + UI + routes) 2026-04-09 13:10:34 +02:00