mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-08 19:19:42 +00:00
Use unified 16pt Dynamic Type body text, clearer Markdown spacing, and a shared reading column. Preserve transcript anchors across typography changes and bound tool command previews to two lines. Add layout and typography regression tests.
83 lines
3.0 KiB
Swift
83 lines
3.0 KiB
Swift
import HapiClient
|
|
import HapiUI
|
|
import SwiftUI
|
|
|
|
/// Switches the app root on pairing state and hosts the presentation that
|
|
/// must survive that switch: the deep-link pairing confirm sheet, the
|
|
/// "already paired" notice, the HapiUI palette resolved from the persisted
|
|
/// theme choice (A-M4e: system follows the OS scheme, explicit modes
|
|
/// override it, OLED = dark palette on pure black), and the app-wide
|
|
/// markdown link handler.
|
|
struct RootView: View {
|
|
@Environment(AppModel.self) private var model
|
|
@Environment(\.colorScheme) private var colorScheme
|
|
@State private var themePrefs = ThemePrefs()
|
|
|
|
var body: some View {
|
|
@Bindable var model = model
|
|
Group {
|
|
switch model.state {
|
|
case .unpaired:
|
|
PairingFlowView()
|
|
case .paired:
|
|
if let session = model.session {
|
|
// `.id` resets navigation + list state on hub switch —
|
|
// each hub gets a fresh HomeView over its own stores.
|
|
HomeView(session: session)
|
|
.id(session.hubUrl)
|
|
} else {
|
|
// Defensive: .paired always carries a session; fall back
|
|
// to pairing rather than a dead screen.
|
|
PairingFlowView()
|
|
}
|
|
}
|
|
}
|
|
.sheet(item: $model.pendingPairing) { pending in
|
|
NavigationStack {
|
|
PairingConfirmView(pending: pending)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Cancel") {
|
|
model.pendingPairing = nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.alert(
|
|
"Hub already paired",
|
|
isPresented: Binding(
|
|
get: { model.infoNotice != nil },
|
|
set: { presented in
|
|
if !presented {
|
|
model.infoNotice = nil
|
|
}
|
|
}
|
|
)
|
|
) {
|
|
Button("OK") {
|
|
model.infoNotice = nil
|
|
}
|
|
} message: {
|
|
Text(model.infoNotice ?? "")
|
|
}
|
|
.hapiTypography()
|
|
.hapiTheme(themePrefs.mode.hapiTheme(systemColorScheme: colorScheme))
|
|
// Explicit modes force the presentation's chrome to match their
|
|
// palette; `.system` passes nil so the OS scheme flows through.
|
|
// (Under an explicit mode `colorScheme` reflects the override, but
|
|
// the palette resolution only consults it in `.system` mode.)
|
|
.preferredColorScheme(themePrefs.mode.preferredColorScheme)
|
|
.environment(themePrefs)
|
|
.handlesHapiLinks()
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
RootView()
|
|
.environment(AppModel(
|
|
registry: HubRegistry(defaults: UserDefaults(suiteName: "preview") ?? .standard),
|
|
credentialStore: InMemoryCredentialStore()
|
|
))
|
|
}
|