iOS update: SettingsView.swift
This commit is contained in:
parent
4f5f24f132
commit
4d28739503
@ -1,61 +1,58 @@
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
final class SettingsStore: ObservableObject {
|
||||
static let shared = SettingsStore()
|
||||
@AppStorage("settings.notifications") var notifications: Bool = true
|
||||
@AppStorage("settings.faceID") var faceID: Bool = true
|
||||
@AppStorage("settings.silentMode") var silentMode: Bool = false
|
||||
}
|
||||
|
||||
struct SettingsView: View {
|
||||
@StateObject private var store = SettingsStore.shared
|
||||
@State private var showAbout = false
|
||||
@EnvironmentObject private var identity: IdentityManager
|
||||
@State private var showAccountID = false
|
||||
@State private var showResetAlert = false
|
||||
@State private var showAbout = false
|
||||
@State private var showExport = false
|
||||
@State private var showWipe = false
|
||||
@State private var editingName = false
|
||||
@State private var nameInput = ""
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(spacing: ClaudeTheme.Spacing.xl) {
|
||||
ProfileHeader()
|
||||
if let id = identity.identity {
|
||||
ProfileHeader(identity: id, onEditName: {
|
||||
nameInput = id.displayName
|
||||
editingName = true
|
||||
})
|
||||
.padding(.top, ClaudeTheme.Spacing.xl)
|
||||
.padding(.bottom, ClaudeTheme.Spacing.sm)
|
||||
|
||||
SettingsSection(title: "Идентичность") {
|
||||
SettingsRow(icon: "key.fill", title: "Mnemonic фраза", subtitle: "12 слов · BIP-39") {}
|
||||
SettingsRow(icon: "person.text.rectangle.fill", title: "Account ID", subtitle: "0x7a3f…b921") {
|
||||
SettingsRow(icon: "person.text.rectangle.fill",
|
||||
title: "Account ID",
|
||||
subtitle: id.accountID) {
|
||||
showAccountID = true
|
||||
}
|
||||
SettingsRow(icon: "qrcode", title: "Поделиться контактом") {}
|
||||
SettingsRow(icon: "key.fill",
|
||||
title: "Экспорт ключей",
|
||||
subtitle: "только на новое устройство") {
|
||||
showExport = true
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection(title: "Безопасность") {
|
||||
SettingsToggle(icon: "faceid", title: "Face ID", isOn: $store.faceID)
|
||||
SettingsRow(icon: "lock.shield.fill", title: "PIN-код", subtitle: "Включён") {}
|
||||
SettingsRow(icon: "shield.lefthalf.filled", title: "Постквантовая криптография", subtitle: "ML-DSA-65") {}
|
||||
}
|
||||
|
||||
SettingsSection(title: "Уведомления") {
|
||||
SettingsToggle(icon: "bell.fill", title: "Push-уведомления", isOn: $store.notifications)
|
||||
SettingsToggle(icon: "moon.fill", title: "Не беспокоить", isOn: $store.silentMode)
|
||||
}
|
||||
|
||||
SettingsSection(title: "Сеть Montana") {
|
||||
SettingsRow(icon: "network", title: "Узлы", subtitle: "мос · фра · зел") {}
|
||||
SettingsRow(icon: "clock.arrow.2.circlepath", title: "TimeChain", subtitle: "Окно 2891844") {}
|
||||
SettingsRow(icon: "antenna.radiowaves.left.and.right", title: "Соединение", subtitle: "Helsinki Reality") {}
|
||||
SettingsSection(title: "Сеть") {
|
||||
SettingsInfo(icon: "antenna.radiowaves.left.and.right",
|
||||
title: "Сервер",
|
||||
value: "mess.montana.quest")
|
||||
SettingsInfo(icon: "shield.lefthalf.filled",
|
||||
title: "Шифрование",
|
||||
value: "X25519 + ChaCha20-Poly1305")
|
||||
}
|
||||
|
||||
SettingsSection(title: "О приложении") {
|
||||
SettingsRow(icon: "info.circle.fill", title: "Версия", subtitle: "0.1.0 (1)") {}
|
||||
SettingsRow(icon: "doc.text.fill", title: "Манифест Монтаны") { showAbout = true }
|
||||
SettingsRow(icon: "heart.fill", title: "Автор", subtitle: "Алик Монтана") {}
|
||||
SettingsRow(icon: "doc.text.fill",
|
||||
title: "Манифест Монтаны") { showAbout = true }
|
||||
SettingsInfo(icon: "info.circle.fill",
|
||||
title: "Версия",
|
||||
value: "1.0.0 (1)")
|
||||
}
|
||||
|
||||
Button { showResetAlert = true } label: {
|
||||
Text("Сбросить идентичность")
|
||||
Button(role: .destructive) { showWipe = true } label: {
|
||||
Text("Стереть идентичность")
|
||||
.font(ClaudeTheme.Typography.headline)
|
||||
.foregroundStyle(ClaudeTheme.Palette.danger)
|
||||
.frame(maxWidth: .infinity)
|
||||
@ -74,35 +71,57 @@ struct SettingsView: View {
|
||||
Footer().padding(.bottom, ClaudeTheme.Spacing.xxl)
|
||||
}
|
||||
}
|
||||
}
|
||||
.claudeBackground()
|
||||
.navigationTitle("Настройки")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.alert("Сбросить идентичность?", isPresented: $showResetAlert) {
|
||||
.alert("Стереть идентичность?", isPresented: $showWipe) {
|
||||
Button("Отмена", role: .cancel) {}
|
||||
Button("Сбросить", role: .destructive) {}
|
||||
Button("Стереть", role: .destructive) { identity.wipe() }
|
||||
} message: {
|
||||
Text("Все ключи и сообщения будут стёрты с устройства.")
|
||||
Text("Все ключи и переписка будут удалены с устройства. Если у тебя нет резервной копии ключей — восстановить аккаунт будет невозможно.")
|
||||
}
|
||||
.alert("Изменить имя", isPresented: $editingName) {
|
||||
TextField("Имя", text: $nameInput)
|
||||
Button("Сохранить") {
|
||||
let trimmed = nameInput.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !trimmed.isEmpty { identity.setDisplayName(trimmed) }
|
||||
}
|
||||
Button("Отмена", role: .cancel) {}
|
||||
}
|
||||
.sheet(isPresented: $showAbout) { AboutView() }
|
||||
.sheet(isPresented: $showAccountID) {
|
||||
if let id = identity.identity { AccountIDView(identity: id) }
|
||||
}
|
||||
.sheet(isPresented: $showExport) {
|
||||
if let id = identity.identity { ExportKeysView(identity: id) }
|
||||
}
|
||||
.sheet(isPresented: $showAbout) { AboutMontanaView() }
|
||||
.sheet(isPresented: $showAccountID) { AccountIDView() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ProfileHeader: View {
|
||||
let identity: Identity
|
||||
let onEditName: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: ClaudeTheme.Spacing.md) {
|
||||
AvatarView(name: "Алик Монтана", size: 96)
|
||||
AvatarView(name: identity.displayName, size: 96)
|
||||
.shadow(color: ClaudeTheme.Palette.gold.opacity(0.35), radius: 18, y: 4)
|
||||
VStack(spacing: 4) {
|
||||
Text("Алик Монтана")
|
||||
Button(action: onEditName) {
|
||||
HStack(spacing: 6) {
|
||||
Text(identity.displayName)
|
||||
.font(.system(size: 24, weight: .semibold, design: .serif))
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
Text("@alik · 0x7a3f…b921")
|
||||
.font(ClaudeTheme.Typography.caption)
|
||||
Image(systemName: "pencil")
|
||||
.font(.system(size: 12))
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
}
|
||||
}
|
||||
Text(identity.accountID)
|
||||
.font(ClaudeTheme.Typography.mono)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold.opacity(0.7))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
@ -113,12 +132,12 @@ private struct Footer: View {
|
||||
Text("Ӂ")
|
||||
.font(.system(size: 36, weight: .semibold, design: .serif))
|
||||
.foregroundStyle(ClaudeTheme.Palette.goldGradient)
|
||||
Text("MONTANA · TIMECHAIN")
|
||||
Text("MONTANA")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.tracking(3)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
Text("XXIIVIIIMMXXXI")
|
||||
.font(.system(size: 9, weight: .regular, design: .serif))
|
||||
.font(.system(size: 9, design: .serif))
|
||||
.tracking(2)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold.opacity(0.45))
|
||||
}
|
||||
@ -159,7 +178,10 @@ private struct SettingsRow: View {
|
||||
let action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
Button(action: action) { rowBody(showChevron: true) }
|
||||
}
|
||||
|
||||
private func rowBody(showChevron: Bool) -> some View {
|
||||
HStack(spacing: ClaudeTheme.Spacing.md) {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 14))
|
||||
@ -177,30 +199,29 @@ private struct SettingsRow: View {
|
||||
Text(subtitle)
|
||||
.font(ClaudeTheme.Typography.caption)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
if showChevron {
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, ClaudeTheme.Spacing.md)
|
||||
.padding(.vertical, ClaudeTheme.Spacing.md)
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle()
|
||||
.fill(ClaudeTheme.Palette.divider)
|
||||
.frame(height: 0.5)
|
||||
.padding(.leading, 56)
|
||||
Rectangle().fill(ClaudeTheme.Palette.divider).frame(height: 0.5).padding(.leading, 56)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct SettingsToggle: View {
|
||||
private struct SettingsInfo: View {
|
||||
let icon: String
|
||||
let title: String
|
||||
@Binding var isOn: Bool
|
||||
let value: String
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: ClaudeTheme.Spacing.md) {
|
||||
@ -216,98 +237,30 @@ private struct SettingsToggle: View {
|
||||
.font(ClaudeTheme.Typography.body)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
Spacer()
|
||||
Toggle("", isOn: $isOn)
|
||||
.labelsHidden()
|
||||
.tint(ClaudeTheme.Palette.gold)
|
||||
Text(value)
|
||||
.font(ClaudeTheme.Typography.caption)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
}
|
||||
.padding(.horizontal, ClaudeTheme.Spacing.md)
|
||||
.padding(.vertical, ClaudeTheme.Spacing.md)
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle()
|
||||
.fill(ClaudeTheme.Palette.divider)
|
||||
.frame(height: 0.5)
|
||||
.padding(.leading, 56)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AboutMontanaView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: ClaudeTheme.Spacing.lg) {
|
||||
Text("Ӂ")
|
||||
.font(.system(size: 80, weight: .semibold, design: .serif))
|
||||
.foregroundStyle(ClaudeTheme.Palette.goldGradient)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, ClaudeTheme.Spacing.xl)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text("MONTANA")
|
||||
.font(.system(size: 28, weight: .bold, design: .serif))
|
||||
.tracking(8)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
Text("TIMECHAIN PROTOCOL")
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.tracking(4)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Цифровая собственность.")
|
||||
Text("Постквантовая криптография.")
|
||||
Text("Твой ключ — твоя идентичность.")
|
||||
Text("Никто не читает твои сообщения. Никто не знает, кто ты. Это нормально.")
|
||||
.padding(.top, 8)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textSecondary)
|
||||
}
|
||||
.font(ClaudeTheme.Typography.serif)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
.padding(.horizontal, ClaudeTheme.Spacing.xl)
|
||||
.padding(.top, ClaudeTheme.Spacing.lg)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text("XXIIVIIIMMXXXI")
|
||||
.font(.system(size: 11, design: .serif))
|
||||
.tracking(2)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold.opacity(0.6))
|
||||
Text("Genesis · 9.01.2026")
|
||||
.font(.system(size: 10))
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.bottom, ClaudeTheme.Spacing.xl)
|
||||
}
|
||||
}
|
||||
.claudeBackground()
|
||||
.navigationTitle("Манифест")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Готово") { dismiss() }
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
}
|
||||
}
|
||||
Rectangle().fill(ClaudeTheme.Palette.divider).frame(height: 0.5).padding(.leading, 56)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AccountIDView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
private let accountID = "0x7a3f9b1c8d4e2f0a5b6c9d8e7f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f921"
|
||||
let identity: Identity
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: ClaudeTheme.Spacing.xl) {
|
||||
AvatarView(name: "Алик Монтана", size: 120)
|
||||
AvatarView(name: identity.displayName, size: 120)
|
||||
.padding(.top, ClaudeTheme.Spacing.xxl)
|
||||
.shadow(color: ClaudeTheme.Palette.gold.opacity(0.4), radius: 22, y: 6)
|
||||
|
||||
Text("Алик Монтана")
|
||||
Text(identity.displayName)
|
||||
.font(ClaudeTheme.Typography.title)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
|
||||
@ -316,10 +269,9 @@ private struct AccountIDView: View {
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.tracking(2)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
Text(accountID)
|
||||
.font(ClaudeTheme.Typography.mono)
|
||||
Text(identity.accountID)
|
||||
.font(.system(size: 18, design: .monospaced))
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(ClaudeTheme.Spacing.md)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md, style: .continuous)
|
||||
@ -332,8 +284,14 @@ private struct AccountIDView: View {
|
||||
}
|
||||
.padding(.horizontal, ClaudeTheme.Spacing.lg)
|
||||
|
||||
Text("Поделись этим Account ID с тем, кто хочет тебе написать.")
|
||||
.font(ClaudeTheme.Typography.caption)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, ClaudeTheme.Spacing.xl)
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = accountID
|
||||
UIPasteboard.general.string = identity.accountID
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "doc.on.doc")
|
||||
@ -360,3 +318,145 @@ private struct AccountIDView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ExportKeysView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
let identity: Identity
|
||||
@State private var revealed = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: ClaudeTheme.Spacing.lg) {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundStyle(ClaudeTheme.Palette.danger)
|
||||
Text("Кто угодно с этими ключами может выдать себя за тебя. Не делись ими ни с кем кроме своих устройств.")
|
||||
.font(ClaudeTheme.Typography.callout)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textSecondary)
|
||||
}
|
||||
.padding(ClaudeTheme.Spacing.md)
|
||||
.background(RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md).fill(ClaudeTheme.Palette.danger.opacity(0.08)))
|
||||
|
||||
if revealed {
|
||||
keyBlock(title: "ed_seed", value: identity.edSeedB64)
|
||||
keyBlock(title: "x_seed", value: identity.xSeedB64)
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = "ed_seed=\(identity.edSeedB64)\nx_seed=\(identity.xSeedB64)\nname=\(identity.displayName)"
|
||||
} label: {
|
||||
HStack { Image(systemName: "doc.on.doc"); Text("Копировать оба ключа") }
|
||||
.font(.system(size: 15, weight: .semibold))
|
||||
.foregroundStyle(ClaudeTheme.Palette.bubbleOutText)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 12)
|
||||
.background(Capsule().fill(ClaudeTheme.Palette.goldGradient))
|
||||
}
|
||||
} else {
|
||||
Button { revealed = true } label: {
|
||||
Text("Показать ключи")
|
||||
.font(ClaudeTheme.Typography.headline)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 14)
|
||||
.background(RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md).fill(ClaudeTheme.Palette.surface))
|
||||
.overlay(RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md).strokeBorder(ClaudeTheme.Palette.goldHairline, lineWidth: 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(ClaudeTheme.Spacing.lg)
|
||||
}
|
||||
.claudeBackground()
|
||||
.navigationTitle("Экспорт ключей")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Готово") { dismiss() }
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func keyBlock(title: String, value: String) -> some View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text(title)
|
||||
.font(.system(size: 11, weight: .semibold))
|
||||
.tracking(2)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
Text(value)
|
||||
.font(.system(size: 12, design: .monospaced))
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
.textSelection(.enabled)
|
||||
.padding(ClaudeTheme.Spacing.md)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md).fill(ClaudeTheme.Palette.surface))
|
||||
.overlay(RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md).strokeBorder(ClaudeTheme.Palette.goldHairline, lineWidth: 0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AboutView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: ClaudeTheme.Spacing.lg) {
|
||||
Text("Ӂ")
|
||||
.font(.system(size: 80, weight: .semibold, design: .serif))
|
||||
.foregroundStyle(ClaudeTheme.Palette.goldGradient)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, ClaudeTheme.Spacing.xl)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text("MONTANA")
|
||||
.font(.system(size: 28, weight: .bold, design: .serif))
|
||||
.tracking(8)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
Text("MESSENGER")
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.tracking(4)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Цифровая собственность.")
|
||||
Text("Постквантовая криптография.")
|
||||
Text("Твой ключ — твоя идентичность.")
|
||||
Text("Никто не читает твои сообщения. Никто не знает, кто ты. Это нормально.")
|
||||
.padding(.top, 8)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textSecondary)
|
||||
}
|
||||
.font(ClaudeTheme.Typography.serif)
|
||||
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
||||
.padding(.horizontal, ClaudeTheme.Spacing.xl)
|
||||
.padding(.top, ClaudeTheme.Spacing.lg)
|
||||
|
||||
Spacer(minLength: 40)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text("XXIIVIIIMMXXXI")
|
||||
.font(.system(size: 11, design: .serif))
|
||||
.tracking(2)
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold.opacity(0.6))
|
||||
Text("Genesis · 9.01.2026")
|
||||
.font(.system(size: 10))
|
||||
.foregroundStyle(ClaudeTheme.Palette.textTertiary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.bottom, ClaudeTheme.Spacing.xl)
|
||||
}
|
||||
}
|
||||
.claudeBackground()
|
||||
.navigationTitle("Манифест")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Готово") { dismiss() }
|
||||
.foregroundStyle(ClaudeTheme.Palette.gold)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user