import SwiftUI import UIKit struct ContentView: View { @EnvironmentObject private var client: HoloLakeSyncClient @State private var pairingText = "" @State private var captureTitle = "" @State private var captureBody = "" var body: some View { NavigationStack { ZStack { LinearGradient( colors: [HoloLakeDesign.deep, HoloLakeDesign.lake, HoloLakeDesign.violet.opacity(0.78)], startPoint: .topLeading, endPoint: .bottomTrailing ) .ignoresSafeArea() ScrollView { VStack(spacing: 16) { header if client.session == nil { pairingCard } else { dashboard } boundaryCard } .padding(.horizontal, 18) .padding(.vertical, 16) } } .toolbar(.hidden, for: .navigationBar) .alert("HoloLake", isPresented: Binding( get: { client.errorText != nil }, set: { if !$0 { client.errorText = nil } } )) { Button("知道了", role: .cancel) { client.errorText = nil } } message: { Text(client.errorText ?? "") } } .preferredColorScheme(.dark) } private var header: some View { HStack(alignment: .center) { VStack(alignment: .leading, spacing: 5) { Text("HoloLake").font(.title.weight(.semibold)).tracking(2) Text("同一频道 · iPhone 轻入口") .font(.subheadline) .foregroundStyle(HoloLakeDesign.muted) } Spacer() Circle() .fill(client.session == nil ? HoloLakeDesign.gold : HoloLakeDesign.mint) .frame(width: 11, height: 11) .shadow(color: client.session == nil ? HoloLakeDesign.gold : HoloLakeDesign.mint, radius: 8) } .foregroundStyle(HoloLakeDesign.pearl) .padding(.top, 4) } private var pairingCard: some View { LakeCard { VStack(alignment: .leading, spacing: 14) { Text("连接电脑端").font(.title3.weight(.semibold)) Text("先在电脑端打开“多端同步”,再扫描配对码或粘贴配对链接。配对密钥只保存在这台 iPhone 的钥匙串。") .font(.subheadline) .foregroundStyle(HoloLakeDesign.muted) TextField("hololake://pair?…", text: $pairingText, axis: .vertical) .textInputAutocapitalization(.never) .autocorrectionDisabled() .padding(14) .background(.black.opacity(0.22), in: RoundedRectangle(cornerRadius: 16)) HStack { Button("从剪贴板读取") { pairingText = UIPasteboard.general.string ?? "" } .buttonStyle(.bordered) Button("校验并配对") { Task { await client.pair(from: pairingText) } } .buttonStyle(.borderedProminent) .tint(HoloLakeDesign.gold) .foregroundStyle(HoloLakeDesign.deep) .disabled(pairingText.isEmpty || client.isWorking) } } } .foregroundStyle(HoloLakeDesign.pearl) } private var dashboard: some View { VStack(spacing: 16) { LakeCard { VStack(alignment: .leading, spacing: 14) { HStack { VStack(alignment: .leading, spacing: 4) { Text(client.session?.desktopName ?? "电脑端") .font(.title3.weight(.semibold)) Text(client.stateText) .font(.caption) .foregroundStyle(HoloLakeDesign.muted) } Spacer() Button { Task { await client.sync() } } label: { if client.isWorking { ProgressView() } else { Label("同步", systemImage: "arrow.triangle.2.circlepath") } } .buttonStyle(.borderedProminent) .tint(HoloLakeDesign.mint) .foregroundStyle(HoloLakeDesign.deep) .disabled(client.isWorking) } if let snapshot = client.snapshot { HStack(spacing: 10) { MetricTile(label: "作品", value: "\(snapshot.webNovel.workCount)") MetricTile(label: "章节", value: "\(snapshot.webNovel.chapterCount)") MetricTile(label: "教育表", value: "\(snapshot.education.activeTableCount)") } } else { Text("保持静止;只有你点击同步时才会连接电脑。") .font(.subheadline) .foregroundStyle(HoloLakeDesign.muted) } } } LakeCard { VStack(alignment: .leading, spacing: 12) { Text("随手记回频道").font(.headline) TextField("标题(可选)", text: $captureTitle) .padding(12) .background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14)) TextField("正文", text: $captureBody, axis: .vertical) .lineLimit(4...10) .padding(12) .background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14)) Button("写入电脑端收件箱") { let title = captureTitle let body = captureBody Task { await client.sync(captureTitle: title, captureBody: body) if client.errorText == nil { captureTitle = "" captureBody = "" } } } .buttonStyle(.bordered) .disabled(captureBody.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || client.isWorking) } } if let works = client.snapshot?.webNovel.works, !works.isEmpty { LakeCard { VStack(alignment: .leading, spacing: 12) { Text("最近作品").font(.headline) ForEach(works.prefix(5)) { work in HStack { Text(work.title).lineLimit(1) Spacer() Text(work.status) .font(.caption) .foregroundStyle(HoloLakeDesign.muted) } if work.id != works.prefix(5).last?.id { Divider().overlay(.white.opacity(0.12)) } } } } } Button("解除本机配对", role: .destructive) { client.disconnect() } .font(.footnote) } .foregroundStyle(HoloLakeDesign.pearl) } private var boundaryCard: some View { VStack(alignment: .leading, spacing: 6) { Text("HLP-IOS-CLIENT-001") .font(.caption2.monospaced()) .foregroundStyle(HoloLakeDesign.gold) Text("手机是同一频道的远程身体入口,不复制电脑桌面,不在电脑离线时执行人格或行业任务。") .font(.caption) .foregroundStyle(HoloLakeDesign.muted) } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 4) } }