diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index 9191ad2..695d6aa 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -30,6 +30,7 @@ struct ConsoleLogsView: View { @State private var isViewActive = false @State private var lastProcessedLineCount = 0 // Track last processed line count @State private var isLoadingLogs = false // Track loading state + @State private var isAtBottom = true // Track if user is at bottom of logs private var accentColor: Color { if customAccentColorHex.isEmpty { @@ -52,19 +53,31 @@ struct ConsoleLogsView: View { ScrollView { VStack(spacing: 0) { // Device Information - ForEach(["Version: \(UIDevice.current.systemVersion)", - "Name: \(UIDevice.current.name)", - "Model: \(UIDevice.current.model)", - "StikJIT Version: App Version: 1.0"], id: \.self) { info in - Text("[\(timeString())] ℹ️ \(info)") + VStack(alignment: .leading, spacing: 4) { + Text("=== DEVICE INFORMATION ===") .font(.system(size: 11, design: .monospaced)) .foregroundColor(colorScheme == .dark ? .white : .black) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.vertical, 2) - .padding(.horizontal, 4) + .padding(.vertical, 4) + + Text("iOS Version: \(UIDevice.current.systemVersion)") + .font(.system(size: 11, design: .monospaced)) + .foregroundColor(colorScheme == .dark ? .white : .black) + + Text("Device: \(UIDevice.current.name)") + .font(.system(size: 11, design: .monospaced)) + .foregroundColor(colorScheme == .dark ? .white : .black) + + Text("Model: \(UIDevice.current.model)") + .font(.system(size: 11, design: .monospaced)) + .foregroundColor(colorScheme == .dark ? .white : .black) + + Text("=== LOG ENTRIES ===") + .font(.system(size: 11, design: .monospaced)) + .foregroundColor(colorScheme == .dark ? .white : .black) + .padding(.vertical, 4) } - - Spacer() + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 4) // Log entries ForEach(logManager.logs) { logEntry in @@ -79,6 +92,29 @@ struct ConsoleLogsView: View { .id(logEntry.id) } } + .background( + GeometryReader { geometry in + Color.clear.preference( + key: ScrollOffsetPreferenceKey.self, + value: geometry.frame(in: .named("scroll")).maxY + ) + } + ) + } + .coordinateSpace(name: "scroll") + .onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in + let threshold: CGFloat = 50 + let scrollViewHeight = UIScreen.main.bounds.height + isAtBottom = offset < (scrollViewHeight + threshold) + } + .onChange(of: logManager.logs.count) { _ in + if isAtBottom { + withAnimation { + if let lastLog = logManager.logs.last { + proxy.scrollTo(lastLog.id, anchor: .bottom) + } + } + } } .onAppear { scrollView = proxy @@ -93,11 +129,6 @@ struct ConsoleLogsView: View { isViewActive = false stopLogCheckTimer() } - .onChange(of: logManager.logs.count) { - if autoScroll, let lastLog = logManager.logs.last { - proxy.scrollTo(lastLog.id, anchor: .bottom) - } - } } Spacer() @@ -366,8 +397,8 @@ struct ConsoleLogsView: View { let logContent = try String(contentsOfFile: logPath, encoding: .utf8) let lines = logContent.components(separatedBy: .newlines) - // Only take the last 300 lines - let maxLines = 300 + // Only take the last 500 lines + let maxLines = 500 let startIndex = max(0, lines.count - maxLines) let recentLines = Array(lines[startIndex.. maxLines { let excessCount = logManager.logs.count - maxLines logManager.removeOldestLogs(count: excessCount) @@ -487,4 +519,11 @@ struct ConsoleLogsView_Previews: PreviewProvider { static var previews: some View { ConsoleLogsView() } +} + +struct ScrollOffsetPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } }