chore: customized loading bar 35bdc59b
Steve · 2025-12-23 21:14 1 file(s) · +44 −21
Titan/Views/ContentView.swift +44 −21
5 5
6 6
import SwiftUI
7 7
8 +
struct IndeterminateProgressBar: View {
9 +
    @State private var animationOffset: CGFloat = -1.0
10 +
11 +
    var body: some View {
12 +
        GeometryReader { geometry in
13 +
            Rectangle()
14 +
                .fill(Color.orange)
15 +
                .frame(width: geometry.size.width * 0.3)
16 +
                .offset(x: animationOffset * geometry.size.width)
17 +
        }
18 +
        .frame(height: 3)
19 +
        .clipped()
20 +
        .onAppear {
21 +
            withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) {
22 +
                animationOffset = 1.0
23 +
            }
24 +
        }
25 +
    }
26 +
}
27 +
8 28
struct ContentView: View {
9 29
    @State private var urlText = "gemini://geminiprotocol.net/"
10 30
    @State private var responseText = ""
47 67
                .contentMargins(.top, 60, for: .scrollContent)
48 68
            }
49 69
50 -
            HStack(spacing: 12) {
51 -
                // Navigation buttons - always visible, grayed out when disabled
52 -
                Button(action: goBack) {
53 -
                    Image(systemName: "chevron.left")
54 -
                        .font(.title2)
55 -
                        .foregroundColor(canGoBack && !isLoading ? .orange : .gray.opacity(0.4))
70 +
            VStack(spacing: 0) {
71 +
                if isLoading {
72 +
                    IndeterminateProgressBar()
73 +
                } else {
74 +
                    Color.clear
75 +
                        .frame(height: 3)
56 76
                }
57 -
                .disabled(!canGoBack || isLoading)
58 -
                .padding(.trailing, 8)
77 +
78 +
                HStack(spacing: 12) {
79 +
                    // Navigation buttons - always visible, grayed out when disabled
80 +
                    Button(action: goBack) {
81 +
                        Image(systemName: "chevron.left")
82 +
                            .font(.title2)
83 +
                            .foregroundColor(canGoBack && !isLoading ? .orange : .gray.opacity(0.4))
84 +
                    }
85 +
                    .disabled(!canGoBack || isLoading)
86 +
                    .padding(.trailing, 8)
59 87
60 -
                Button(action: goForward) {
61 -
                    Image(systemName: "chevron.right")
62 -
                        .font(.title2)
63 -
                        .foregroundColor(canGoForward && !isLoading ? .orange : .gray.opacity(0.4))
64 -
                }
65 -
                .disabled(!canGoForward || isLoading)
66 -
                
88 +
                    Button(action: goForward) {
89 +
                        Image(systemName: "chevron.right")
90 +
                            .font(.title2)
91 +
                            .foregroundColor(canGoForward && !isLoading ? .orange : .gray.opacity(0.4))
92 +
                    }
93 +
                    .disabled(!canGoForward || isLoading)
67 94
68 -
                ZStack(alignment: .trailing) {
95 +
69 96
                    TextField("Enter Gemini URL", text: $urlText)
70 97
                        .textFieldStyle(.roundedBorder)
71 98
                        .autocapitalization(.none)
75 102
                        .onSubmit {
76 103
                            navigateTo(urlText)
77 104
                        }
78 -
                    if isLoading {
79 -
                        ProgressView()
80 -
                            .progressViewStyle(CircularProgressViewStyle())
81 -
                            .padding(.trailing, 8)
82 -
                    }
83 105
                }
106 +
                .padding(.top, 8)
84 107
            }
85 108
            .padding(.horizontal, 30)
86 109
            .padding(.bottom, 8)