From 3f07559219b4d9d63ed0abf565843b7474d704e5 Mon Sep 17 00:00:00 2001 From: Young Liu Date: Thu, 15 Jan 2026 23:37:44 +0800 Subject: [PATCH 1/3] enhance 9X9 multiplication table --- Resources/en.lproj/Localizable.strings | 9 + Resources/zh-Hans.lproj/Localizable.strings | 9 + Views/MultiplicationTableView.swift | 409 +++++++++++++++----- 3 files changed, 329 insertions(+), 98 deletions(-) diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings index 62ec3b9..368153c 100644 --- a/Resources/en.lproj/Localizable.strings +++ b/Resources/en.lproj/Localizable.strings @@ -136,6 +136,15 @@ "multiplication_table.title" = "Multiplication Table"; "multiplication_table.instructions" = "This is the complete 9×9 multiplication table, containing all multiplication operations from 1 to 9. Different colors represent different difficulty levels."; "multiplication_table.formula" = "%@ × %@ = %@"; +"multiplication_table.legend" = "Difficulty Legend"; +"multiplication_table.perfect_square" = "Square"; +"multiplication_table.easy" = "Easy"; +"multiplication_table.medium" = "Medium"; +"multiplication_table.hard" = "Hard"; +"multiplication_table.total_facts" = "Total Facts"; +"multiplication_table.perfect_squares" = "Squares"; +"multiplication_table.range" = "Range"; +"multiplication_table.tap_hint" = "Tap any cell to hear it spoken aloud"; "button.back" = "Back"; /* Other Options */ diff --git a/Resources/zh-Hans.lproj/Localizable.strings b/Resources/zh-Hans.lproj/Localizable.strings index 9dbb1c5..baa9721 100644 --- a/Resources/zh-Hans.lproj/Localizable.strings +++ b/Resources/zh-Hans.lproj/Localizable.strings @@ -129,6 +129,15 @@ "multiplication_table.title" = "九九乘法表"; "multiplication_table.instructions" = "这是完整的九九乘法表,包含1至9的所有乘法运算。不同颜色代表不同难度级别。"; "multiplication_table.formula" = "%@ × %@ = %@"; +"multiplication_table.legend" = "难度说明"; +"multiplication_table.perfect_square" = "平方数"; +"multiplication_table.easy" = "简单"; +"multiplication_table.medium" = "中等"; +"multiplication_table.hard" = "较难"; +"multiplication_table.total_facts" = "算式总数"; +"multiplication_table.perfect_squares" = "平方数"; +"multiplication_table.range" = "结果范围"; +"multiplication_table.tap_hint" = "点击任意格子可朗读算式"; "button.back" = "返回"; /* Other Options */ diff --git a/Views/MultiplicationTableView.swift b/Views/MultiplicationTableView.swift index 979c465..8640c98 100644 --- a/Views/MultiplicationTableView.swift +++ b/Views/MultiplicationTableView.swift @@ -7,127 +7,339 @@ struct MultiplicationTableView: View { @EnvironmentObject var localizationManager: LocalizationManager @Environment(\.presentationMode) var presentationMode @StateObject private var ttsHelper = TTSHelper() - - // Grid configuration for different device types - private var gridColumns: [GridItem] { - let deviceColumns: Int - + @State private var animateHeader = false + @State private var selectedCell: String? = nil + @State private var showLegend = true + + // MARK: - Difficulty Level Colors + private struct DifficultyColors { + static let perfect = LinearGradient( + colors: [Color.purple.opacity(0.3), Color.purple.opacity(0.5)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + static let easy = LinearGradient( + colors: [Color.green.opacity(0.2), Color.green.opacity(0.35)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + static let medium = LinearGradient( + colors: [Color.orange.opacity(0.2), Color.orange.opacity(0.35)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + static let hard = LinearGradient( + colors: [Color.red.opacity(0.2), Color.red.opacity(0.35)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + + static func solidColor(for i: Int, j: Int, result: Int) -> Color { + if i == j { return .purple } + else if result <= 10 { return .green } + else if result <= 50 { return .orange } + else { return .red } + } + + static func gradient(for i: Int, j: Int, result: Int) -> LinearGradient { + if i == j { return perfect } + else if result <= 10 { return easy } + else if result <= 50 { return medium } + else { return hard } + } + } + + // Cell size based on device + private var cellSize: CGSize { if DeviceUtils.isIPad { if DeviceUtils.isLandscape(with: (horizontalSizeClass, verticalSizeClass)) { - deviceColumns = 9 // iPad landscape: show all 9 columns + return CGSize(width: 95, height: 75) } else { - deviceColumns = 6 // iPad portrait: 6 columns for better readability + return CGSize(width: 85, height: 70) } } else { if DeviceUtils.isLandscape(with: (horizontalSizeClass, verticalSizeClass)) { - deviceColumns = 6 // iPhone landscape: 6 columns + return CGSize(width: 80, height: 65) } else { - deviceColumns = 3 // iPhone portrait: 3 columns + return CGSize(width: 105, height: 70) } } - - return Array(repeating: GridItem(.flexible(), spacing: 8), count: deviceColumns) } - - // Generate all multiplication results - private var multiplicationResults: [(String, Color)] { - var results: [(String, Color)] = [] - - for i in 1...9 { - for j in 1...9 { - let result = i * j - let formula = "multiplication_table.formula".localizedFormat(String(i), String(j), String(result)) - - // Color coding for better visual distinction - let color: Color - if i == j { - color = .blue.opacity(0.2) // Same numbers (1×1, 2×2, etc.) - } else if result <= 10 { - color = .green.opacity(0.15) // Easy results - } else if result <= 50 { - color = .orange.opacity(0.15) // Medium results - } else { - color = .red.opacity(0.15) // Challenging results - } - - results.append((formula, color)) - } - } - - return results - } - - // Extract mathematical expression from index - private func extractMathExpression(from formattedString: String, at index: Int) -> String { - // Calculate i and j from index (0-based to 1-based) - let i = (index / 9) + 1 - let j = (index % 9) + 1 + + // Create multiplication cell view for triangular layout + private func multiplicationCell(i: Int, j: Int) -> some View { let result = i * j - - // Return the mathematical expression in standard format - return "\(i) × \(j) = \(result)" - } - - // Create multiplication button view - private func multiplicationButton(for item: (String, Color), at index: Int) -> some View { - Button(action: { + let cellId = "\(i)x\(j)" + let isSelected = selectedCell == cellId + let solidColor = DifficultyColors.solidColor(for: i, j: j, result: result) + + return Button(action: { + // Haptic feedback + let impactFeedback = UIImpactFeedbackGenerator(style: .light) + impactFeedback.impactOccurred() + + withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) { + selectedCell = isSelected ? nil : cellId + } + guard UserDefaults.standard.bool(forKey: "isTtsEnabled") else { return } - let mathExpression = extractMathExpression(from: item.0, at: index) + let mathExpression = "\(i) × \(j) = \(result)" ttsHelper.speakMathExpression(mathExpression, language: localizationManager.currentLanguage) }) { - VStack(spacing: 4) { - Text(item.0) - .font(.adaptiveBody()) - .fontWeight(.medium) - .multilineTextAlignment(.center) - .lineLimit(2) - .minimumScaleFactor(0.8) - .foregroundColor(.primary) + VStack(spacing: 2) { + // Formula: i × j + HStack(spacing: 2) { + Text("\(i)") + .font(.system(size: 14, weight: .bold, design: .rounded)) + .foregroundColor(solidColor) + Text("×") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(.adaptiveSecondaryText) + Text("\(j)") + .font(.system(size: 14, weight: .bold, design: .rounded)) + .foregroundColor(solidColor) + } + + // Result with equals + HStack(spacing: 2) { + Text("=") + .font(.system(size: 11, weight: .medium)) + .foregroundColor(.adaptiveSecondaryText) + Text("\(result)") + .font(.system(size: 18, weight: .bold, design: .rounded)) + .foregroundColor(.adaptiveText) + } } - .padding(.vertical, 12) - .padding(.horizontal, 8) - .frame(minHeight: 60) - .frame(maxWidth: .infinity) + .frame(width: cellSize.width, height: cellSize.height) .background( - RoundedRectangle(cornerRadius: .adaptiveCornerRadius) - .fill(item.1) - .overlay( - RoundedRectangle(cornerRadius: .adaptiveCornerRadius) - .stroke(Color.gray.opacity(0.3), lineWidth: 1) - ) + ZStack { + // Base gradient + RoundedRectangle(cornerRadius: AppTheme.smallCornerRadius) + .fill(DifficultyColors.gradient(for: i, j: j, result: result)) + + // Highlight overlay for perfect squares (diagonal) + if i == j { + RoundedRectangle(cornerRadius: AppTheme.smallCornerRadius) + .strokeBorder( + LinearGradient( + colors: [Color.purple.opacity(0.7), Color.purple.opacity(0.4)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ), + lineWidth: 2 + ) + } + } + ) + .overlay( + RoundedRectangle(cornerRadius: AppTheme.smallCornerRadius) + .stroke(Color.adaptiveBorder, lineWidth: 0.5) ) + .shadow( + color: isSelected ? solidColor.opacity(0.5) : Color.adaptiveShadow.opacity(0.5), + radius: isSelected ? 6 : 2, + x: 0, + y: isSelected ? 3 : 1 + ) + .scaleEffect(isSelected ? 1.08 : 1.0) } .buttonStyle(PlainButtonStyle()) } - - var body: some View { - VStack(spacing: 0) { - // Fixed header - VStack { - Text("multiplication_table.title".localized) - .font(.adaptiveTitle()) - .padding() - - // Instructions - Text("multiplication_table.instructions".localized) - .font(.adaptiveBody()) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - .padding(.horizontal) - .padding(.bottom) + + // MARK: - Triangular Table View + private var triangularTableView: some View { + VStack(alignment: .leading, spacing: 8) { + // Row n contains: 1×n, 2×n, 3×n, ..., n×n + ForEach(1...9, id: \.self) { row in + HStack(spacing: 6) { + // Row label + Text("\(row)") + .font(.system(size: 14, weight: .bold, design: .rounded)) + .foregroundColor(.accent) + .frame(width: 24, height: 24) + .background( + Circle() + .fill(Color.accent.opacity(0.15)) + ) + + // Cells for this row: 1×row, 2×row, ..., row×row + ForEach(1...row, id: \.self) { col in + multiplicationCell(i: col, j: row) + } + + Spacer(minLength: 0) + } + .padding(.leading, 8) } - .background(Color(.systemBackground)) - - // Scrollable multiplication table - ScrollView([.vertical, .horizontal], showsIndicators: true) { - LazyVGrid(columns: gridColumns, spacing: 12) { - ForEach(Array(multiplicationResults.enumerated()), id: \.offset) { index, item in - multiplicationButton(for: item, at: index) + } + .padding(.vertical, 12) + .padding(.horizontal, 8) + } + + // MARK: - Legend View + private var legendView: some View { + VStack(spacing: 12) { + HStack { + Text("multiplication_table.legend".localized) + .font(.system(size: 14, weight: .semibold)) + .foregroundColor(.adaptiveSecondaryText) + + Spacer() + + Button(action: { + withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) { + showLegend.toggle() } + }) { + Image(systemName: showLegend ? "chevron.up.circle.fill" : "chevron.down.circle.fill") + .font(.system(size: 20)) + .foregroundColor(.accent) } - .padding() + } + + if showLegend { + HStack(spacing: 12) { + legendItem(color: .purple, label: "multiplication_table.perfect_square".localized, icon: "star.fill") + legendItem(color: .green, label: "multiplication_table.easy".localized, icon: "leaf.fill") + legendItem(color: .orange, label: "multiplication_table.medium".localized, icon: "flame.fill") + legendItem(color: .red, label: "multiplication_table.hard".localized, icon: "bolt.fill") + } + .transition(.opacity.combined(with: .move(edge: .top))) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background( + RoundedRectangle(cornerRadius: AppTheme.cornerRadius) + .fill(Color.adaptiveBackground) + .shadow(color: Color.adaptiveShadow, radius: AppTheme.lightShadowRadius, x: 0, y: 2) + ) + .padding(.horizontal, 16) + } + + private func legendItem(color: Color, label: String, icon: String) -> some View { + VStack(spacing: 4) { + ZStack { + Circle() + .fill(color.opacity(0.2)) + .frame(width: 32, height: 32) + + Image(systemName: icon) + .font(.system(size: 14)) + .foregroundColor(color) + } + + Text(label) + .font(.system(size: 10, weight: .medium)) + .foregroundColor(.adaptiveSecondaryText) + .lineLimit(1) + .minimumScaleFactor(0.8) + } + .frame(maxWidth: .infinity) + } + + // MARK: - Stats Header + private var statsHeader: some View { + HStack(spacing: 16) { + statCard( + value: "45", + label: "multiplication_table.total_facts".localized, + icon: "number.circle.fill", + color: .accent + ) + + statCard( + value: "9", + label: "multiplication_table.perfect_squares".localized, + icon: "star.circle.fill", + color: .purple + ) + + statCard( + value: "1-81", + label: "multiplication_table.range".localized, + icon: "arrow.left.arrow.right.circle.fill", + color: .success + ) + } + .padding(.horizontal, 16) + .opacity(animateHeader ? 1 : 0) + .offset(y: animateHeader ? 0 : -20) + } + + private func statCard(value: String, label: String, icon: String, color: Color) -> some View { + VStack(spacing: 6) { + HStack(spacing: 4) { + Image(systemName: icon) + .font(.system(size: 14)) + .foregroundColor(color) + + Text(value) + .font(.system(size: 18, weight: .bold, design: .rounded)) + .foregroundColor(.adaptiveText) + } + + Text(label) + .font(.system(size: 10, weight: .medium)) + .foregroundColor(.adaptiveSecondaryText) + .lineLimit(1) + .minimumScaleFactor(0.8) + } + .frame(maxWidth: .infinity) + .padding(.vertical, 10) + .background( + RoundedRectangle(cornerRadius: AppTheme.smallCornerRadius) + .fill(color.opacity(0.1)) + ) + .overlay( + RoundedRectangle(cornerRadius: AppTheme.smallCornerRadius) + .stroke(color.opacity(0.3), lineWidth: 1) + ) + } + + // MARK: - Body + var body: some View { + ZStack { + // Background gradient + LinearGradient( + colors: [Color(.systemBackground), Color.adaptiveSecondaryBackground], + startPoint: .top, + endPoint: .bottom + ) + .ignoresSafeArea() + + VStack(spacing: 0) { + // Stats header + statsHeader + .padding(.top, 8) + .padding(.bottom, 12) + + // Legend + legendView + .padding(.bottom, 12) + + // Tip text + HStack(spacing: 6) { + Image(systemName: "hand.tap.fill") + .font(.system(size: 12)) + .foregroundColor(.accent) + Text("multiplication_table.tap_hint".localized) + .font(.system(size: 12, weight: .medium)) + .foregroundColor(.adaptiveSecondaryText) + } + .padding(.bottom, 8) + + // Scrollable triangular multiplication table + ScrollView([.vertical, .horizontal], showsIndicators: true) { + triangularTableView + } + } + } + .onAppear { + withAnimation(.easeOut(duration: 0.5)) { + animateHeader = true } } .navigationTitle("multiplication_table.title".localized) @@ -138,11 +350,12 @@ struct MultiplicationTableView: View { Button(action: { presentationMode.wrappedValue.dismiss() }) { - HStack { + HStack(spacing: 4) { Image(systemName: "chevron.left") + .font(.system(size: 16, weight: .semibold)) Text("button.back".localized) } - .foregroundColor(.blue) + .foregroundColor(.accent) } } } From fffefb93044a1d70fbf0957657722d34f27397d9 Mon Sep 17 00:00:00 2001 From: Young Liu Date: Sat, 17 Jan 2026 20:38:06 +0800 Subject: [PATCH 2/3] enhance System Information --- Resources/en.lproj/Localizable.strings | 5 + Resources/zh-Hans.lproj/Localizable.strings | 5 + Views/SystemInfoComponents.swift | 801 ++++++++++++++++++-- Views/SystemInfoView.swift | 693 +++++++---------- 4 files changed, 1034 insertions(+), 470 deletions(-) diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings index 368153c..bdf771e 100644 --- a/Resources/en.lproj/Localizable.strings +++ b/Resources/en.lproj/Localizable.strings @@ -419,6 +419,11 @@ "system.info.scale_factor" = "Scale Factor"; "system.info.refresh_rate" = "Refresh Rate"; "system.info.physical_size" = "Physical Size"; +"system.info.realtime_monitoring" = "Real-time monitoring"; +"system.info.live" = "LIVE"; +"system.info.date" = "Date"; +"system.info.time" = "Time"; +"system.info.realtime" = "Real-time"; "profit" = "Profit"; "selling_price" = "Selling Price"; "cost" = "Cost"; diff --git a/Resources/zh-Hans.lproj/Localizable.strings b/Resources/zh-Hans.lproj/Localizable.strings index baa9721..330a618 100644 --- a/Resources/zh-Hans.lproj/Localizable.strings +++ b/Resources/zh-Hans.lproj/Localizable.strings @@ -411,6 +411,11 @@ "system.info.scale_factor" = "缩放因子"; "system.info.refresh_rate" = "刷新率"; "system.info.physical_size" = "物理尺寸"; +"system.info.realtime_monitoring" = "实时监控"; +"system.info.live" = "实时"; +"system.info.date" = "日期"; +"system.info.time" = "时间"; +"system.info.realtime" = "实时更新"; "profit" = "利润"; "selling_price" = "售出价"; "cost" = "成本"; diff --git a/Views/SystemInfoComponents.swift b/Views/SystemInfoComponents.swift index 5977537..868fcd9 100644 --- a/Views/SystemInfoComponents.swift +++ b/Views/SystemInfoComponents.swift @@ -4,35 +4,214 @@ // import SwiftUI +// MARK: - Animated Gradient Background +struct TechGradientBackground: View { + let colors: [Color] + @State private var animateGradient = false + + var body: some View { + LinearGradient( + colors: colors, + startPoint: animateGradient ? .topLeading : .bottomLeading, + endPoint: animateGradient ? .bottomTrailing : .topTrailing + ) + .onAppear { + withAnimation(.easeInOut(duration: 3).repeatForever(autoreverses: true)) { + animateGradient.toggle() + } + } + } +} + +// MARK: - Glowing Icon +struct GlowingIcon: View { + let systemName: String + let color: Color + let size: CGFloat + @State private var isGlowing = false + + var body: some View { + ZStack { + // Outer glow + Image(systemName: systemName) + .font(.system(size: size)) + .foregroundColor(color) + .blur(radius: isGlowing ? 8 : 4) + .opacity(isGlowing ? 0.8 : 0.4) + + // Inner icon + Image(systemName: systemName) + .font(.system(size: size)) + .foregroundColor(color) + } + .frame(width: size + 16, height: size + 16) + .onAppear { + withAnimation(.easeInOut(duration: 1.5).repeatForever(autoreverses: true)) { + isGlowing.toggle() + } + } + } +} + +// MARK: - Circular Progress Ring +struct CircularProgressRing: View { + let progress: Double + let color: Color + let lineWidth: CGFloat + + @State private var animatedProgress: Double = 0 + + var body: some View { + ZStack { + // Background ring + Circle() + .stroke(color.opacity(0.2), lineWidth: lineWidth) + + // Progress ring + Circle() + .trim(from: 0, to: animatedProgress / 100) + .stroke( + AngularGradient( + colors: [color.opacity(0.5), color, color.opacity(0.8)], + center: .center + ), + style: StrokeStyle(lineWidth: lineWidth, lineCap: .round) + ) + .rotationEffect(.degrees(-90)) + .shadow(color: color.opacity(0.5), radius: 4, x: 0, y: 0) + } + .onAppear { + withAnimation(.easeOut(duration: 1.0)) { + animatedProgress = progress + } + } + .onChange(of: progress) { newValue in + withAnimation(.easeOut(duration: 0.5)) { + animatedProgress = newValue + } + } + } +} + +// MARK: - Tech Card Container +struct TechCard: View { + let content: Content + let accentColor: Color + + init(accentColor: Color = .blue, @ViewBuilder content: () -> Content) { + self.accentColor = accentColor + self.content = content() + } + + var body: some View { + content + .padding(.vertical, 12) + .padding(.horizontal, 16) + .background( + ZStack { + // Glass background + RoundedRectangle(cornerRadius: 16) + .fill(.ultraThinMaterial) + + // Border glow + RoundedRectangle(cornerRadius: 16) + .stroke( + LinearGradient( + colors: [accentColor.opacity(0.6), accentColor.opacity(0.1), accentColor.opacity(0.3)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ), + lineWidth: 1 + ) + } + ) + .shadow(color: accentColor.opacity(0.15), radius: 8, x: 0, y: 4) + } +} + +// MARK: - Animated Value Display +struct AnimatedValueText: View { + let value: String + let color: Color + @State private var opacity: Double = 0.7 + + var body: some View { + Text(value) + .font(.adaptiveBody()) + .fontWeight(.semibold) + .foregroundColor(color) + .opacity(opacity) + .onAppear { + withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) { + opacity = 1.0 + } + } + } +} + // MARK: - System Info Row Component struct SystemInfoRow: View { let title: String let value: String let icon: String var isRealTime: Bool = false - + var accentColor: Color = .cyan + var body: some View { - HStack { - Image(systemName: icon) - .foregroundColor(isRealTime ? .blue : .primary) - .frame(width: 20) - - Text(title) - .font(.adaptiveBody()) - .fontWeight(.medium) - - Spacer() - - Text(value) - .font(.adaptiveBody()) - .fontWeight(.medium) - .foregroundColor(isRealTime ? .blue : .secondary) - .multilineTextAlignment(.trailing) - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) + TechCard(accentColor: accentColor) { + VStack(alignment: .leading, spacing: 10) { + // Header row with icon and title + HStack(spacing: 12) { + GlowingIcon(systemName: icon, color: accentColor, size: 18) + + Text(title) + .font(.adaptiveBody()) + .fontWeight(.medium) + .foregroundColor(.primary) + + Spacer() + } + + // Divider + Rectangle() + .fill( + LinearGradient( + colors: [accentColor.opacity(0.3), accentColor.opacity(0.05), .clear], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 0.5) + + // Value row + HStack { + Spacer() + + if isRealTime { + AnimatedValueText(value: value, color: accentColor) + .font(.system(.body, design: .monospaced)) + } else { + Text(value) + .font(.system(.body, design: .monospaced)) + .fontWeight(.semibold) + .foregroundColor(accentColor.opacity(0.9)) + .multilineTextAlignment(.trailing) + .lineLimit(2) + .minimumScaleFactor(0.8) + } + } + .padding(.horizontal, 8) + .padding(.vertical, 6) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(accentColor.opacity(0.06)) + ) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(accentColor.opacity(0.12), lineWidth: 0.5) + ) + } + } } } @@ -43,60 +222,550 @@ struct SystemInfoProgressRow: View { let unit: String let icon: String let color: Color - + + @State private var animatedValue: Double = 0 + + var body: some View { + TechCard(accentColor: color) { + VStack(spacing: 14) { + // Header row + HStack(spacing: 12) { + // Circular progress indicator with icon + ZStack { + CircularProgressRing(progress: value, color: color, lineWidth: 4) + .frame(width: 48, height: 48) + + GlowingIcon(systemName: icon, color: color, size: 16) + } + + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.adaptiveBody()) + .fontWeight(.semibold) + .foregroundColor(.primary) + + Text("system.info.realtime_monitoring".localized) + .font(.adaptiveCaption()) + .foregroundColor(.secondary) + } + + Spacer() + + // Value display with badge style + HStack(alignment: .firstTextBaseline, spacing: 2) { + Text(String(format: "%.1f", animatedValue)) + .font(.system(.title2, design: .monospaced)) + .fontWeight(.bold) + .foregroundColor(color) + + Text(unit) + .font(.system(.caption, design: .monospaced)) + .fontWeight(.medium) + .foregroundColor(color.opacity(0.7)) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(color.opacity(0.1)) + ) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(color.opacity(0.25), lineWidth: 1) + ) + } + + // Divider + Rectangle() + .fill( + LinearGradient( + colors: [color.opacity(0.3), color.opacity(0.1), .clear], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 0.5) + + // Progress bar section + VStack(alignment: .leading, spacing: 8) { + // Linear progress bar with gradient + GeometryReader { geometry in + ZStack(alignment: .leading) { + // Background track + RoundedRectangle(cornerRadius: 5) + .fill(color.opacity(0.12)) + .frame(height: 10) + + // Progress fill + RoundedRectangle(cornerRadius: 5) + .fill( + LinearGradient( + colors: [color.opacity(0.5), color, color.opacity(0.8)], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(width: max(0, geometry.size.width * (animatedValue / 100)), height: 10) + .shadow(color: color.opacity(0.5), radius: 4, x: 0, y: 0) + + // Glow dot at progress end + if animatedValue > 0 { + Circle() + .fill(color) + .frame(width: 10, height: 10) + .shadow(color: color, radius: 5, x: 0, y: 0) + .offset(x: max(0, geometry.size.width * (animatedValue / 100) - 5)) + } + } + } + .frame(height: 10) + + // Scale markers + HStack { + Text("0%") + .font(.system(.caption2, design: .monospaced)) + .foregroundColor(.secondary) + + Spacer() + + Text("50%") + .font(.system(.caption2, design: .monospaced)) + .foregroundColor(.secondary) + + Spacer() + + Text("100%") + .font(.system(.caption2, design: .monospaced)) + .foregroundColor(.secondary) + } + } + } + } + .onAppear { + withAnimation(.easeOut(duration: 1.0)) { + animatedValue = value + } + } + .onChange(of: value) { newValue in + withAnimation(.easeOut(duration: 0.5)) { + animatedValue = newValue + } + } + } +} + +// MARK: - Detail Info Item Row +struct DetailInfoItemRow: View { + let label: String + let value: String + let color: Color + let isLast: Bool + + var body: some View { + VStack(spacing: 0) { + HStack(alignment: .center, spacing: 8) { + // Label with dot indicator + HStack(spacing: 6) { + Circle() + .fill(color.opacity(0.6)) + .frame(width: 4, height: 4) + + Text(label) + .font(.adaptiveCaption()) + .foregroundColor(.secondary) + .lineLimit(1) + } + + Spacer() + + // Value with styled background + Text(value) + .font(.system(.caption, design: .monospaced)) + .fontWeight(.semibold) + .foregroundColor(.primary) + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(color.opacity(0.08)) + ) + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(color.opacity(0.15), lineWidth: 0.5) + ) + } + .padding(.vertical, 8) + + // Separator line (except for last item) + if !isLast { + Rectangle() + .fill( + LinearGradient( + colors: [color.opacity(0.2), color.opacity(0.05), .clear], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 0.5) + .padding(.leading, 10) + } + } + } +} + +// MARK: - Detail Info Section +struct DetailInfoSection: View { + let title: String + let icon: String + let color: Color + let items: [(String, String)] + var showProgressBar: Bool = false + var progressValue: Double = 0 + + @State private var animatedProgress: Double = 0 + var body: some View { - VStack(alignment: .leading, spacing: 8) { + TechCard(accentColor: color) { + VStack(alignment: .leading, spacing: 14) { + // Header + HStack(spacing: 12) { + GlowingIcon(systemName: icon, color: color, size: 18) + + Text(title) + .font(.adaptiveBody()) + .fontWeight(.semibold) + .foregroundColor(.primary) + + Spacer() + + if showProgressBar { + // Percentage badge + HStack(spacing: 4) { + Text(String(format: "%.1f", animatedProgress)) + .font(.system(.caption, design: .monospaced)) + .fontWeight(.bold) + .foregroundColor(color) + Text("%") + .font(.system(.caption2, design: .monospaced)) + .foregroundColor(color.opacity(0.7)) + } + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background( + Capsule() + .fill(color.opacity(0.12)) + ) + .overlay( + Capsule() + .stroke(color.opacity(0.3), lineWidth: 1) + ) + } + } + + // Progress bar if enabled + if showProgressBar { + VStack(spacing: 6) { + // Progress bar with animated fill + GeometryReader { geometry in + ZStack(alignment: .leading) { + // Background track + RoundedRectangle(cornerRadius: 4) + .fill(color.opacity(0.1)) + .frame(height: 8) + + // Progress fill with gradient + RoundedRectangle(cornerRadius: 4) + .fill( + LinearGradient( + colors: [color.opacity(0.5), color, color.opacity(0.8)], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(width: max(0, geometry.size.width * (animatedProgress / 100)), height: 8) + .shadow(color: color.opacity(0.4), radius: 3, x: 0, y: 0) + + // Glow effect at progress end + if animatedProgress > 0 { + Circle() + .fill(color) + .frame(width: 8, height: 8) + .shadow(color: color, radius: 4, x: 0, y: 0) + .offset(x: max(0, geometry.size.width * (animatedProgress / 100) - 4)) + } + } + } + .frame(height: 8) + } + } + + // Divider with gradient + Rectangle() + .fill( + LinearGradient( + colors: [color.opacity(0.4), color.opacity(0.1), .clear], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 1) + + // Detail items with improved layout + VStack(spacing: 0) { + ForEach(Array(items.enumerated()), id: \.offset) { index, item in + DetailInfoItemRow( + label: item.0, + value: item.1, + color: color, + isLast: index == items.count - 1 + ) + } + } + } + } + .onAppear { + withAnimation(.easeOut(duration: 1.0)) { + animatedProgress = progressValue + } + } + .onChange(of: progressValue) { newValue in + withAnimation(.easeOut(duration: 0.5)) { + animatedProgress = newValue + } + } + } +} + +// MARK: - Real-time Clock Display +struct RealtimeClockDisplay: View { + let date: String + let time: String + let color: Color + + @State private var pulseAnimation = false + @State private var colonVisible = true + + var body: some View { + TechCard(accentColor: color) { + VStack(spacing: 14) { + // Header + HStack(spacing: 12) { + // Animated clock icon + ZStack { + Circle() + .stroke(color.opacity(0.2), lineWidth: 2) + .frame(width: 44, height: 44) + + Circle() + .stroke(color.opacity(0.6), lineWidth: 2) + .frame(width: 44, height: 44) + .scaleEffect(pulseAnimation ? 1.15 : 1.0) + .opacity(pulseAnimation ? 0.3 : 0.8) + + GlowingIcon(systemName: "clock.fill", color: color, size: 18) + } + + VStack(alignment: .leading, spacing: 2) { + Text("system.info.current_time".localized) + .font(.adaptiveBody()) + .fontWeight(.semibold) + .foregroundColor(.primary) + + Text("system.info.realtime".localized) + .font(.adaptiveCaption()) + .foregroundColor(.secondary) + } + + Spacer() + + // Live indicator + HStack(spacing: 4) { + Circle() + .fill(Color.green) + .frame(width: 6, height: 6) + .opacity(pulseAnimation ? 1.0 : 0.5) + + Text("system.info.live".localized) + .font(.system(.caption2, design: .monospaced)) + .fontWeight(.bold) + .foregroundColor(.green) + } + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + Capsule() + .fill(Color.green.opacity(0.1)) + ) + } + .onAppear { + withAnimation(.easeInOut(duration: 1.0).repeatForever(autoreverses: true)) { + pulseAnimation.toggle() + } + } + + // Divider + Rectangle() + .fill( + LinearGradient( + colors: [color.opacity(0.4), color.opacity(0.1), .clear], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 1) + + // Date and Time Display + HStack(spacing: 16) { + // Date display + VStack(alignment: .leading, spacing: 4) { + Text("system.info.date".localized) + .font(.adaptiveCaption()) + .foregroundColor(.secondary) + + Text(date) + .font(.system(.callout, design: .monospaced)) + .fontWeight(.medium) + .foregroundColor(.primary) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(color.opacity(0.06)) + ) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(color.opacity(0.15), lineWidth: 0.5) + ) + + Spacer() + + // Time display with glowing effect + VStack(alignment: .trailing, spacing: 4) { + Text("system.info.time".localized) + .font(.adaptiveCaption()) + .foregroundColor(.secondary) + + Text(time) + .font(.system(.title2, design: .monospaced)) + .fontWeight(.bold) + .foregroundColor(color) + .shadow(color: color.opacity(0.4), radius: 6, x: 0, y: 0) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(color.opacity(0.1)) + ) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(color.opacity(0.25), lineWidth: 1) + ) + } + } + } + } +} + +// MARK: - Section Header +struct TechSectionHeader: View { + let title: String + let subtitle: String + + var body: some View { + VStack(spacing: 8) { HStack { - Image(systemName: icon) - .foregroundColor(color) - .frame(width: 20) + Rectangle() + .fill( + LinearGradient( + colors: [.clear, .cyan.opacity(0.5)], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 1) + Text(title) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - Text(String(format: "%.1f%@", value, unit)) - .font(.adaptiveBody()) - .fontWeight(.medium) - .foregroundColor(color) - } - - ProgressView(value: value, total: 100) - .progressViewStyle(LinearProgressViewStyle(tint: color)) - .scaleEffect(x: 1, y: 0.8, anchor: .center) - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) + .font(.adaptiveTitle()) + .fontWeight(.bold) + .foregroundColor(.primary) + + Rectangle() + .fill( + LinearGradient( + colors: [.cyan.opacity(0.5), .clear], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(height: 1) + } + + Text(subtitle) + .font(.adaptiveBody()) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + .padding(.horizontal) } } // MARK: - Preview struct SystemInfoComponents_Previews: PreviewProvider { static var previews: some View { - VStack(spacing: 16) { - SystemInfoRow( - title: "Device Name", - value: "iPhone 15 Pro", - icon: "iphone" - ) - - SystemInfoRow( - title: "Current Time", - value: "2:30 PM", - icon: "clock", - isRealTime: true - ) - - SystemInfoProgressRow( - title: "CPU Usage", - value: 25.5, - unit: "%", - icon: "speedometer", - color: .orange - ) + ScrollView { + VStack(spacing: 16) { + TechSectionHeader( + title: "System Info", + subtitle: "Real-time device monitoring" + ) + + SystemInfoRow( + title: "Device Name", + value: "iPhone 15 Pro", + icon: "iphone", + accentColor: .cyan + ) + + SystemInfoRow( + title: "Current Time", + value: "2:30 PM", + icon: "clock", + isRealTime: true, + accentColor: .blue + ) + + SystemInfoProgressRow( + title: "CPU Usage", + value: 45.5, + unit: "%", + icon: "cpu", + color: .orange + ) + + DetailInfoSection( + title: "Memory Usage", + icon: "memorychip", + color: .green, + items: [ + ("Used", "4.2 GB"), + ("Total", "8.0 GB"), + ("Available", "3.8 GB") + ], + showProgressBar: true, + progressValue: 52.5 + ) + + RealtimeClockDisplay( + date: "2024-01-15", + time: "14:30:25", + color: .blue + ) + } + .padding() } - .padding() + .background(Color(.systemBackground)) .previewLayout(.sizeThatFits) } } diff --git a/Views/SystemInfoView.swift b/Views/SystemInfoView.swift index 49f9826..dabc75b 100644 --- a/Views/SystemInfoView.swift +++ b/Views/SystemInfoView.swift @@ -9,415 +9,119 @@ struct SystemInfoView: View { @Environment(\.presentationMode) var presentationMode @Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.verticalSizeClass) var verticalSizeClass + @Environment(\.colorScheme) var colorScheme @StateObject private var systemInfoManager = SystemInfoManager() - + + // Tech color palette + private let deviceColor: Color = .cyan + private let cpuColor: Color = .orange + private let memoryColor: Color = .green + private let diskColor: Color = .purple + private let networkColor: Color = .blue + private let batteryColor: Color = .mint + private let screenColor: Color = .indigo + private let timeColor: Color = .pink + var body: some View { - ScrollView { - VStack(spacing: 20) { - // System Information Title - VStack(spacing: 15) { - Text("system.info.title".localized) - .font(.adaptiveTitle()) - .fontWeight(.bold) - .multilineTextAlignment(.center) - .padding(.horizontal) - - Text("system.info.description".localized) - .font(.adaptiveBody()) - .multilineTextAlignment(.center) - .foregroundColor(.secondary) - .padding(.horizontal, 20) - } - .padding(.top, 20) - - // System Info Content - VStack(spacing: 15) { - // Device Name - SystemInfoRow( - title: "system.info.device_name".localized, - value: systemInfoManager.deviceName, - icon: "iphone" - ) - - // CPU Info - SystemInfoRow( - title: "system.info.cpu_info".localized, - value: systemInfoManager.cpuInfo, - icon: "cpu" - ) - - // CPU Usage (Real-time) - SystemInfoProgressRow( - title: "system.info.cpu_usage".localized, - value: systemInfoManager.cpuUsage, - unit: "%", - icon: "speedometer", - color: .orange - ) - - // Memory Usage (Real-time) - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "memorychip") - .foregroundColor(.green) - .frame(width: 20) - Text("system.info.memory_usage".localized) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - } - - VStack(alignment: .leading, spacing: 4) { - HStack { - Text("system.info.memory_used".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.memoryUsage.usedMemoryMB) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.memory_total".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.memoryUsage.totalMemoryMB) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.memory_available".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.memoryUsage.availableMemoryMB) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - // Memory Usage Progress Bar - ProgressView(value: systemInfoManager.memoryUsage.usagePercentage, total: 100) - .progressViewStyle(LinearProgressViewStyle(tint: .green)) - .scaleEffect(x: 1, y: 0.8, anchor: .center) - - HStack { - Spacer() - Text(String(format: "%.1f%%", systemInfoManager.memoryUsage.usagePercentage)) - .font(.adaptiveCaption()) - .fontWeight(.medium) - .foregroundColor(.green) - } - } - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) - - // Disk Usage (Real-time) - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "internaldrive") - .foregroundColor(.purple) - .frame(width: 20) - Text("system.info.disk_usage".localized) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - } - - VStack(alignment: .leading, spacing: 4) { - HStack { - Text("system.info.disk_used".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.diskUsage.usedDiskGB) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.disk_total".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.diskUsage.totalDiskGB) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.disk_available".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.diskUsage.availableDiskGB) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - // Disk Usage Progress Bar - ProgressView(value: systemInfoManager.diskUsage.usagePercentage, total: 100) - .progressViewStyle(LinearProgressViewStyle(tint: .purple)) - .scaleEffect(x: 1, y: 0.8, anchor: .center) - - HStack { - Spacer() - Text(String(format: "%.1f%%", systemInfoManager.diskUsage.usagePercentage)) - .font(.adaptiveCaption()) - .fontWeight(.medium) - .foregroundColor(.purple) - } - } - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) - - // System Version - SystemInfoRow( - title: "system.info.system_version".localized, - value: systemInfoManager.systemVersion, - icon: "gear" + ZStack { + // Animated background + TechBackground(colorScheme: colorScheme) + .ignoresSafeArea() + + ScrollView { + VStack(spacing: 20) { + // Header Section + TechSectionHeader( + title: "system.info.title".localized, + subtitle: "system.info.description".localized ) + .padding(.top, 20) - // Network Information - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "wifi") - .foregroundColor(.cyan) - .frame(width: 20) - Text("system.info.network_info".localized) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - } - - VStack(alignment: .leading, spacing: 4) { - HStack { - Text("system.info.connection_type".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.networkInfo.connectionType) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - if !systemInfoManager.networkInfo.wifiSSID.isEmpty { - HStack { - Text("system.info.wifi_ssid".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.networkInfo.wifiSSID) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - } - - if !systemInfoManager.networkInfo.cellularCarrier.isEmpty && systemInfoManager.networkInfo.cellularCarrier != "Unknown" { - HStack { - Text("system.info.cellular_carrier".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.networkInfo.cellularCarrier) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - } - } - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) - - // Battery Information - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "battery.100") - .foregroundColor(.green) - .frame(width: 20) - Text("system.info.battery_info".localized) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - } - - VStack(alignment: .leading, spacing: 4) { - HStack { - Text("system.info.battery_level".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(String(format: "%.0f%%", systemInfoManager.batteryInfo.level * 100)) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.battery_state".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.batteryInfo.state) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.power_source".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.batteryInfo.powerSourceState) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.boot_time".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.batteryInfo.bootTimeString) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.uptime".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.batteryInfo.uptimeString) - .font(.adaptiveCaption()) - .fontWeight(.medium) - .foregroundColor(.green) - } - } - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) - - // Screen Information - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "display") - .foregroundColor(.purple) - .frame(width: 20) - Text("system.info.screen_info".localized) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - } - - VStack(alignment: .leading, spacing: 4) { - HStack { - Text("system.info.screen_resolution".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.screenInfo.screenResolution) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.screen_size".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(String(format: "%.0f × %.0f", systemInfoManager.screenInfo.screenSize.width, systemInfoManager.screenInfo.screenSize.height)) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.scale_factor".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(String(format: "%.0fx", systemInfoManager.screenInfo.scaleFactor)) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.refresh_rate".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(String(format: "%.0f Hz", systemInfoManager.screenInfo.refreshRate)) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - - HStack { - Text("system.info.physical_size".localized + ":") - .font(.adaptiveCaption()) - .foregroundColor(.secondary) - Spacer() - Text(systemInfoManager.screenInfo.physicalSize) - .font(.adaptiveCaption()) - .fontWeight(.medium) - } - } - } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) - - // Current Date and Time (Real-time) - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "clock") - .foregroundColor(.blue) - .frame(width: 20) - Text("system.info.current_time".localized) - .font(.adaptiveBody()) - .fontWeight(.medium) - Spacer() - } - - VStack(alignment: .trailing, spacing: 4) { - HStack { - Spacer() - Text(systemInfoManager.currentDate) - .font(.adaptiveBody()) - .fontWeight(.medium) - .foregroundColor(.blue) - } - - HStack { - Spacer() - Text(systemInfoManager.currentTime) - .font(.adaptiveBody()) - .fontWeight(.medium) - .foregroundColor(.blue) - } - } + // System Info Content + VStack(spacing: 16) { + // Device Name + SystemInfoRow( + title: "system.info.device_name".localized, + value: systemInfoManager.deviceName, + icon: "iphone", + accentColor: deviceColor + ) + + // CPU Info + SystemInfoRow( + title: "system.info.cpu_info".localized, + value: systemInfoManager.cpuInfo, + icon: "cpu", + accentColor: cpuColor + ) + + // CPU Usage (Real-time) + SystemInfoProgressRow( + title: "system.info.cpu_usage".localized, + value: systemInfoManager.cpuUsage, + unit: "%", + icon: "speedometer", + color: cpuColor + ) + + // Memory Usage Section + DetailInfoSection( + title: "system.info.memory_usage".localized, + icon: "memorychip", + color: memoryColor, + items: [ + ("system.info.memory_used".localized, systemInfoManager.memoryUsage.usedMemoryMB), + ("system.info.memory_total".localized, systemInfoManager.memoryUsage.totalMemoryMB), + ("system.info.memory_available".localized, systemInfoManager.memoryUsage.availableMemoryMB) + ], + showProgressBar: true, + progressValue: systemInfoManager.memoryUsage.usagePercentage + ) + + // Disk Usage Section + DetailInfoSection( + title: "system.info.disk_usage".localized, + icon: "internaldrive", + color: diskColor, + items: [ + ("system.info.disk_used".localized, systemInfoManager.diskUsage.usedDiskGB), + ("system.info.disk_total".localized, systemInfoManager.diskUsage.totalDiskGB), + ("system.info.disk_available".localized, systemInfoManager.diskUsage.availableDiskGB) + ], + showProgressBar: true, + progressValue: systemInfoManager.diskUsage.usagePercentage + ) + + // System Version + SystemInfoRow( + title: "system.info.system_version".localized, + value: systemInfoManager.systemVersion, + icon: "gear", + accentColor: .gray + ) + + // Network Information + networkInfoSection + + // Battery Information + batteryInfoSection + + // Screen Information + screenInfoSection + + // Current Date and Time (Real-time) + RealtimeClockDisplay( + date: systemInfoManager.currentDate, + time: systemInfoManager.currentTime, + color: timeColor + ) } - .padding(.vertical, 8) - .padding(.horizontal, 16) - .background(Color.secondary.opacity(0.1)) - .cornerRadius(8) + .padding(.horizontal, 20) + + Spacer(minLength: 50) } - .padding(.horizontal, 20) - - Spacer(minLength: 50) + .padding() } - .padding() } .navigationTitle("system.info.title".localized) .navigationBarTitleDisplayMode(.large) @@ -427,13 +131,194 @@ struct SystemInfoView: View { Button(action: { presentationMode.wrappedValue.dismiss() }) { - HStack { + HStack(spacing: 6) { Image(systemName: "chevron.left") + .font(.system(size: 14, weight: .semibold)) Text("button.back".localized) } - .foregroundColor(.blue) + .foregroundColor(.cyan) + } + } + } + } + + // MARK: - Network Info Section + private var networkInfoSection: some View { + var networkItems: [(String, String)] = [ + ("system.info.connection_type".localized, systemInfoManager.networkInfo.connectionType) + ] + + if !systemInfoManager.networkInfo.wifiSSID.isEmpty { + networkItems.append(("system.info.wifi_ssid".localized, systemInfoManager.networkInfo.wifiSSID)) + } + + if !systemInfoManager.networkInfo.cellularCarrier.isEmpty && systemInfoManager.networkInfo.cellularCarrier != "Unknown" { + networkItems.append(("system.info.cellular_carrier".localized, systemInfoManager.networkInfo.cellularCarrier)) + } + + return DetailInfoSection( + title: "system.info.network_info".localized, + icon: "wifi", + color: networkColor, + items: networkItems + ) + } + + // MARK: - Battery Info Section + private var batteryInfoSection: some View { + DetailInfoSection( + title: "system.info.battery_info".localized, + icon: batteryIconName, + color: batteryColor, + items: [ + ("system.info.battery_level".localized, String(format: "%.0f%%", systemInfoManager.batteryInfo.level * 100)), + ("system.info.battery_state".localized, systemInfoManager.batteryInfo.state), + ("system.info.power_source".localized, systemInfoManager.batteryInfo.powerSourceState), + ("system.info.boot_time".localized, systemInfoManager.batteryInfo.bootTimeString), + ("system.info.uptime".localized, systemInfoManager.batteryInfo.uptimeString) + ], + showProgressBar: true, + progressValue: Double(systemInfoManager.batteryInfo.level * 100) + ) + } + + private var batteryIconName: String { + let level = systemInfoManager.batteryInfo.level + if level >= 0.75 { + return "battery.100" + } else if level >= 0.5 { + return "battery.75" + } else if level >= 0.25 { + return "battery.50" + } else { + return "battery.25" + } + } + + // MARK: - Screen Info Section + private var screenInfoSection: some View { + DetailInfoSection( + title: "system.info.screen_info".localized, + icon: "display", + color: screenColor, + items: [ + ("system.info.screen_resolution".localized, systemInfoManager.screenInfo.screenResolution), + ("system.info.screen_size".localized, String(format: "%.0f × %.0f", systemInfoManager.screenInfo.screenSize.width, systemInfoManager.screenInfo.screenSize.height)), + ("system.info.scale_factor".localized, String(format: "%.0fx", systemInfoManager.screenInfo.scaleFactor)), + ("system.info.refresh_rate".localized, String(format: "%.0f Hz", systemInfoManager.screenInfo.refreshRate)), + ("system.info.physical_size".localized, systemInfoManager.screenInfo.physicalSize) + ] + ) + } +} + +// MARK: - Tech Background +struct TechBackground: View { + let colorScheme: ColorScheme + @State private var animateGradient = false + + var body: some View { + ZStack { + // Base gradient + LinearGradient( + colors: colorScheme == .dark + ? [Color(red: 0.05, green: 0.05, blue: 0.15), Color(red: 0.1, green: 0.1, blue: 0.2)] + : [Color(red: 0.95, green: 0.97, blue: 1.0), Color(red: 0.9, green: 0.95, blue: 1.0)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + + // Animated orbs + GeometryReader { geometry in + ZStack { + // Cyan orb + Circle() + .fill( + RadialGradient( + colors: [Color.cyan.opacity(0.3), Color.cyan.opacity(0)], + center: .center, + startRadius: 0, + endRadius: 150 + ) + ) + .frame(width: 300, height: 300) + .offset( + x: animateGradient ? geometry.size.width * 0.2 : geometry.size.width * 0.1, + y: animateGradient ? geometry.size.height * 0.1 : geometry.size.height * 0.2 + ) + + // Purple orb + Circle() + .fill( + RadialGradient( + colors: [Color.purple.opacity(0.2), Color.purple.opacity(0)], + center: .center, + startRadius: 0, + endRadius: 120 + ) + ) + .frame(width: 240, height: 240) + .offset( + x: animateGradient ? geometry.size.width * 0.6 : geometry.size.width * 0.7, + y: animateGradient ? geometry.size.height * 0.6 : geometry.size.height * 0.5 + ) + + // Blue orb + Circle() + .fill( + RadialGradient( + colors: [Color.blue.opacity(0.15), Color.blue.opacity(0)], + center: .center, + startRadius: 0, + endRadius: 100 + ) + ) + .frame(width: 200, height: 200) + .offset( + x: animateGradient ? geometry.size.width * 0.3 : geometry.size.width * 0.4, + y: animateGradient ? geometry.size.height * 0.8 : geometry.size.height * 0.7 + ) + } + } + + // Grid pattern overlay + GridPatternView() + .opacity(colorScheme == .dark ? 0.1 : 0.05) + } + .onAppear { + withAnimation(.easeInOut(duration: 8).repeatForever(autoreverses: true)) { + animateGradient.toggle() + } + } + } +} + +// MARK: - Grid Pattern +struct GridPatternView: View { + var body: some View { + GeometryReader { geometry in + Path { path in + let spacing: CGFloat = 30 + let width = geometry.size.width + let height = geometry.size.height + + // Vertical lines + var x: CGFloat = 0 + while x <= width { + path.move(to: CGPoint(x: x, y: 0)) + path.addLine(to: CGPoint(x: x, y: height)) + x += spacing + } + + // Horizontal lines + var y: CGFloat = 0 + while y <= height { + path.move(to: CGPoint(x: 0, y: y)) + path.addLine(to: CGPoint(x: width, y: y)) + y += spacing } } + .stroke(Color.cyan, lineWidth: 0.5) } } } From 506f2c54666ce7d5fca7a886cfc670f4cec4a0e7 Mon Sep 17 00:00:00 2001 From: Young Liu Date: Sat, 17 Jan 2026 20:55:26 +0800 Subject: [PATCH 3/3] Update README --- ChangeLogs.md | 4 +++ README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/ChangeLogs.md b/ChangeLogs.md index 49d4852..b04c4cc 100644 --- a/ChangeLogs.md +++ b/ChangeLogs.md @@ -1,5 +1,9 @@ # Change Log +### 🌟 2026-01-17 (项目文档更新 / Project Documentation Update) +- **📄 README更新 (README Update)** - 更新README.md文件,确保包含最新的功能特性和项目信息 (Updated README.md file to include latest features and project information) +- **🔄 ChangeLog更新 (ChangeLog Update)** - 更新ChangeLogs.md文件,同步最新项目变更 (Updated ChangeLogs.md file to sync latest project changes) + ### 🌟 2026-01-09 (PDF排版优化 / PDF Layout Optimization) - **📄 PDF题库排版优化 (PDF Problem Bank Layout Optimization)** - 全面优化PDF生成排版,最大化A4纸张利用率 (Comprehensive PDF generation layout optimization to maximize A4 paper utilization) diff --git a/README.md b/README.md index 108356b..9427033 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # 🧮 小学生算术学习应用 ## Elementary Arithmetic Learning App -*Version: 1.0.2* | *Updated: January 9, 2026* +*Version: 1.0.2* | *Updated: January 17, 2026* [![Demo](https://github.com/tobecrazy/Arithmetic/blob/main/Arithmetic.gif)](https://github.com/tobecrazy/Arithmetic) @@ -16,7 +16,7 @@ *An intelligent arithmetic learning application built with SwiftUI to help elementary students master basic mathematical operations* -[🚀 快速开始](#-快速开始-quick-start) • [✨ 功能特点](#-功能特点-features) • [📥 安装说明](#-安装说明-installation) • [🏗️ 技术实现](#️-技术实现-technical-implementation) • [📊 系统信息监控](#️-系统信息监控-system-information-monitoring) • [🔄 更新日志](#-最近更新-recent-updates) +[🚀 快速开始](#-快速开始-quick-start) • [✨ 功能特点](#-功能特点-features) • [📥 安装说明](#-安装说明-installation) • [🏗️ 技术实现](#️-技术实现-technical-implementation) • [📊 系统信息监控](#️-系统信息监控-system-information-monitoring) • [🔄 更新日志](#-最近更新-recent-updates) • [📖 详细更新日志](ChangeLogs.md) @@ -364,6 +364,75 @@ - **开源支持 (Open Source Support)** - 用户可直接访问项目开源地址,了解开发进展 (Users can directly access the project's open source repository to understand development progress) - **中英双语 (Bilingual Support)** - 支持中文"点击访问我的Github仓库"和英文"Visit GitHub Repository"本地化文本 (Supports localized text in Chinese "点击访问我的Github仓库" and English "Visit GitHub Repository") +[⬆️ 返回目录](#-目录-table-of-contents) + +--- + +## 🔄 最近更新 (Recent Updates) + +For a detailed history of updates, see [ChangeLogs.md](ChangeLogs.md). + +[⬆️ 返回目录](#-目录-table-of-contents) + +--- + +## 🤝 贡献指南 (Contributing Guidelines) + +We welcome all forms of contributions! 🎉 + +### 🌟 贡献方式 (Ways to Contribute) +- 🐛 报告问题和错误 (Report issues and bugs) +- 💡 提交功能请求 (Submit feature requests) +- 🔧 提交代码改进 (Submit code improvements) +- 📝 改进文档 (Improve documentation) +- 🌐 协助翻译 (Help with translations) +- 📊 查看更新历史 (Review update history in [ChangeLogs.md](ChangeLogs.md)) + +### 📋 贡献流程 (Contribution Process) + +1. **フォーク (Fork Repository)** + ```bash + # Fork this repository to your GitHub account + ``` + +2. **🌿 创建特性分支 (Create Feature Branch)** + ```bash + git checkout -b feature/your-feature + ``` + +3. **💻 提交更改 (Commit Changes)** + ```bash + git commit -m "feat: add your feature description" + ``` + +4. **🚀 推送分支 (Push Branch)** + ```bash + git push origin feature/your-feature + ``` + +5. **📬 创建 Pull Request (Create Pull Request)** + - 详细描述你的更改 (Detailed description of your changes) + - 包含相关的测试用例 (Include relevant test cases) + - 确保代码符合项目规范 (Ensure code follows project standards) + +### 📏 代码规范 (Code Standards) +- 遵循Swift官方编码规范 (Follow Swift official coding standards) +- 使用有意义的变量和函数命名 (Use meaningful variable and function names) +- 添加必要的注释和文档 (Add necessary comments and documentation) +- 确保代码通过所有测试 (Ensure code passes all tests) + +### 🐛 问题报告 (Issue Reporting) +使用GitHub Issues报告问题时,请包含:(When reporting issues via GitHub Issues, please include:) +- 详细的问题描述 (Detailed problem description) +- 重现步骤 (Reproduction steps) +- 预期行为vs实际行为 (Expected vs. actual behavior) +- 设备和系统版本信息 (Device and system version information) +- 相关截图或日志 (Relevant screenshots or logs) + +[⬆️ 返回目录](#-目录-table-of-contents) + +--- + ### 📷 QR码扫描工具 (QR Code Scanning Tool) - **📱 扫描功能 (Scanning Functionality)** - 集成相机扫描和相册扫描功能,用于扫描二维码 - 实时相机预览,带绿色扫描框指示