feat: add iOS support and harden receipt scanning

This commit is contained in:
Timo
2026-08-20 23:48:26 +02:00
parent f5e06c32b0
commit cf1b799e1b
107 changed files with 11755 additions and 122 deletions

View File

@@ -0,0 +1,93 @@
import SwiftUI
/// The web app's three-font type system (see `DESIGN (1).md` Typography,
/// and `tailwind.config.ts`'s `fontFamily`), ported with the SAME font
/// files: `Resources/Fonts/*.ttf` are the real Hanken Grotesk / Inter /
/// JetBrains Mono variable fonts (downloaded from the canonical
/// google/fonts repository the same open-source files the web app loads
/// from Google Fonts' CDN), registered via `UIAppFonts` in `project.yml`.
///
/// PostScript names below were read directly out of each font file's `name`
/// table (not guessed) see the font files themselves if these ever need
/// re-verifying after a font update.
///
/// - Hanken Grotesk headlines, set tight (negative tracking) at large
/// sizes for the "locked, architectural" feel the design system asks for.
/// - Inter body copy.
/// - JetBrains Mono labels, captions, technical/numeric data. `labelCaps`
/// is additionally uppercased with wide tracking apply
/// `.zenithLabelCapsStyle()`, not just the bare font, or the case/tracking
/// won't happen (SwiftUI doesn't derive that from the font itself).
enum ZenithFont {
static let display = "HankenGrotesk-Regular"
static let body = "Inter-Regular"
static let mono = "JetBrainsMono-Regular"
}
extension Font {
/// Rarely needed on iPhone (72pt) kept for completeness / a future
/// iPad or marketing surface.
static var zenithDisplayLg: Font { .custom(ZenithFont.display, size: 72).weight(.bold) }
/// Screen/section titles. Sized at the web system's "headline-lg-mobile"
/// (32/40) rather than the 48pt desktop size, since this app is
/// iPhone-only (see project.yml's TARGETED_DEVICE_FAMILY).
static var zenithHeadlineLg: Font { .custom(ZenithFont.display, size: 32).weight(.semibold) }
/// Card/sub-section titles.
static var zenithHeadlineMd: Font { .custom(ZenithFont.display, size: 24).weight(.medium) }
static var zenithBodyLg: Font { .custom(ZenithFont.body, size: 18) }
/// Default body/UI text size.
static var zenithBodyMd: Font { .custom(ZenithFont.body, size: 16) }
static var zenithBodySm: Font { .custom(ZenithFont.body, size: 14) }
/// Field labels, eyebrows, section headers use with
/// `.zenithLabelCapsStyle()` for the uppercase + tracking, not bare.
static var zenithLabelCaps: Font { .custom(ZenithFont.mono, size: 12).weight(.medium) }
/// Chips, technical/tabular data (amounts, dates, IDs).
static var zenithLabelMd: Font { .custom(ZenithFont.mono, size: 14) }
static var zenithLabelSm: Font { .custom(ZenithFont.mono, size: 12) }
}
/// Complete text styles (font + tracking + default color) for each type
/// role. Tracking values are the design system's em values
/// (`DESIGN (1).md` Typography) converted to points AT EACH ROLE'S OWN
/// size `SwiftUI.tracking(_:)` takes absolute points, not em, so these
/// can't be a single generic modifier parameterized only by a ratio.
/// Prefer these over composing `.font(.zenith...)` by hand.
extension View {
/// Screen/section titles (32pt, -0.02em -0.64pt tracking).
func zenithHeadlineLgStyle(color: Color = .zenithText) -> some View {
font(.zenithHeadlineLg).tracking(-0.64).foregroundStyle(color)
}
/// Card/sub-section titles (24pt, -0.01em -0.24pt tracking).
func zenithHeadlineMdStyle(color: Color = .zenithText) -> some View {
font(.zenithHeadlineMd).tracking(-0.24).foregroundStyle(color)
}
/// Default body text (16pt, 0 tracking).
func zenithBodyStyle(color: Color = .zenithText) -> some View {
font(.zenithBodyMd).foregroundStyle(color)
}
func zenithBodySmStyle(color: Color = .zenithMuted) -> some View {
font(.zenithBodySm).foregroundStyle(color)
}
/// `label-caps` role: JetBrains Mono, uppercase, wide tracking
/// (12pt, 0.1em 1.2pt). This is the style used above every
/// `ZenithTextField` and for chip/badge/eyebrow text.
func zenithLabelCapsStyle(color: Color = .zenithSubtle) -> some View {
font(.zenithLabelCaps)
.textCase(.uppercase)
.tracking(1.2)
.foregroundStyle(color)
}
/// Technical/tabular data amounts, dates, IDs (14pt, 0 tracking).
func zenithLabelMdStyle(color: Color = .zenithText) -> some View {
font(.zenithLabelMd).foregroundStyle(color)
}
}