mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Safe areas. The installed app draws under the status bar and beside the notch, and each 4rem header band padded the inset inside its own border-box height: on a phone with a 59 px inset the top bar kept a 5 px content box and the logo and account menu straddled its bottom border. The authenticated shell now takes the top and side insets once on its outer wrapper (`shell-safe-area`, the strip painted in the chrome colour), so the top bar, the sidebar band and any docked panel start below the status bar together and the seam between them holds. The two Coach full-height pages subtract the inset at every width, the full- height day sheet keeps its handle clear of the Dynamic Island, side sheets keep clear of the status bar and the notch, the bottom bar and the Coach button take the side insets of a phone held sideways, a footer-less responsive sheet keeps its last row above the home indicator, and the sign-in and onboarding shells pad themselves. The sign-in shell grows with its content (`min-h-dvh`), so a phone held sideways can scroll to the top of the card. Keyboard. iOS Safari and Chrome on Android shrink only the visual viewport when the keyboard opens, so a bottom sheet kept Save and the Coach composer behind it. `KeyboardInsetBridge` publishes the covered height (`--keyboard-inset`, `data-keyboard` on <html>); bottom sheets stand on the keyboard and give up its height to their scrolling body, and the bottom bar and the Coach button step aside while it is open. Pinch-zoom and toolbar-sized changes do not count as a keyboard. The sheet primitive names its side in `data-sheet-side`, which consumers do not override the way they replace `data-slot`. pwa-mobile-shell-guard.test.ts pins the structure no headless browser can show (no browser in CI reports a safe-area inset).
src/
Application source. HealthLog is a Next.js 16 App Router project (output: "standalone", React 19, TypeScript strict). The path alias @/* resolves to this directory. Everything a request touches lives under here; the layout below follows the request flow rather than alphabetical order.
Request flow
client → src/proxy.ts → src/app/<route>/route.ts → src/lib/api-handler.ts → handler body → src/lib/api-response.ts → client
proxy.ts— the HTTP edge (Next.js 16's renamedmiddleware). Public-path allowlist, demo-mode mutation block, server-side onboarding redirect, worker-only refusal,x-request-id+ CSP nonce generation, every security header.app/— App Router pages (RSC by default) and API routes. Every API route wraps its handler inapiHandler. Seeapp/README.md.lib/— the server/runtime toolkit:apiHandler, auth, crypto, rollups, AI, notifications, pg-boss jobs, integrations, rate limiting. Seelib/README.md.components/— React components (shadcn/ui new-york, kebab-case files). Seecomponents/README.md.
Other directories
hooks/— shared React hooks (use-prefix, kebab-case).types/— shared TypeScript declarations.generated/prisma/— the generated Prisma client (do not edit; regenerated byprisma generate). Imported as@/generated/prisma.instrumentation.ts— Next.js startup hook; boots the pg-boss worker registry and boot-time backfill queues.__tests__/— cross-cutting unit tests; most tests live in__tests__/folders next to the code they cover.
Conventions
See ../CLAUDE.md for the full set. The load-bearing ones: RSC by default ("use client" only when a hook/state/browser API needs it); every API route wraps apiHandler and parses its body with Zod safeParse; userId is always narrowed from session or Bearer, never accepted as a body field; TanStack Query keys come only from src/lib/query-keys/; outbound HTTP goes through src/lib/safe-fetch.ts.