chore: renamed files adac2d9a
Steve · 2025-12-25 22:04 5 file(s) · +26 −26
Titan/Models/TitanLine.swift → Titan/Models/GeminiLine.swift +2 −2
1 1
//
2 -
//  TitanLine.swift
2 +
//  GeminiLine.swift
3 3
//  Titan
4 4
//
5 5
6 6
import Foundation
7 7
8 -
enum TitanLine {
8 +
enum GeminiLine {
9 9
    case text(String)
10 10
    case link(url: String, label: String)
11 11
    case heading1(String)
Titan/Services/TitanClient.swift → Titan/Services/GeminiClient.swift +13 −13
1 1
//
2 -
//  TitanClient.swift
2 +
//  GeminiClient.swift
3 3
//  Titan
4 4
//
5 5
//  Created by Steve Simkins on 12/20/25.
10 10
11 11
// MARK: - Response Types
12 12
13 -
struct TitanResponse {
13 +
struct GeminiResponse {
14 14
    let statusCode: Int
15 15
    let meta: String
16 16
    let body: Data?
34 34
    }
35 35
}
36 36
37 -
enum TitanError: LocalizedError {
37 +
enum GeminiError: LocalizedError {
38 38
    case invalidResponse
39 39
    case invalidURL
40 40
    case cancelled
50 50
51 51
// MARK: - Client
52 52
53 -
class TitanClient {
53 +
class GeminiClient {
54 54
    let rejectUnauthorized: Bool
55 55
    
56 56
    init(rejectUnauthorized: Bool = true) {
61 61
        hostname: String,
62 62
        port: Int = 1965,
63 63
        urlString: String
64 -
    ) async throws -> TitanResponse {
64 +
    ) async throws -> GeminiResponse {
65 65
        let host = NWEndpoint.Host(hostname)
66 66
        let port = NWEndpoint.Port(integerLiteral: UInt16(port))
67 67
87 87
        let state = ConnectionState()
88 88
89 89
        return try await withTaskCancellationHandler {
90 -
            try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<TitanResponse, Error>) in
90 +
            try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<GeminiResponse, Error>) in
91 91
                connection.stateUpdateHandler = { connectionState in
92 92
                    switch connectionState {
93 93
                    case .ready:
96 96
                            connection.cancel()
97 97
                            if !state.continuationResumed {
98 98
                                state.continuationResumed = true
99 -
                                continuation.resume(throwing: TitanError.cancelled)
99 +
                                continuation.resume(throwing: GeminiError.cancelled)
100 100
                            }
101 101
                            return
102 102
                        }
125 125
                            // Check if this was a user-initiated cancellation
126 126
                            if state.wasCancelled {
127 127
                                print("✓ Request cancelled\n")
128 -
                                continuation.resume(throwing: TitanError.cancelled)
128 +
                                continuation.resume(throwing: GeminiError.cancelled)
129 129
                            } else {
130 130
                                print("✓ Connection closed by server\n")
131 131
                                do {
167 167
        }
168 168
    }
169 169
170 -
    private func parseResponse(_ data: Data) throws -> TitanResponse {
170 +
    private func parseResponse(_ data: Data) throws -> GeminiResponse {
171 171
        // Find the first CRLF which separates header from body
172 172
        let crlf = Data([0x0D, 0x0A]) // \r\n
173 173
        guard let crlfRange = data.range(of: crlf) else {
174 -
            throw TitanError.invalidResponse
174 +
            throw GeminiError.invalidResponse
175 175
        }
176 176
177 177
        let headerData = data[..<crlfRange.lowerBound]
178 178
        guard let headerString = String(data: headerData, encoding: .utf8) else {
179 -
            throw TitanError.invalidResponse
179 +
            throw GeminiError.invalidResponse
180 180
        }
181 181
182 182
        // Parse status code (first 2 characters)
183 183
        guard headerString.count >= 2,
184 184
              let statusCode = Int(headerString.prefix(2)) else {
185 -
            throw TitanError.invalidResponse
185 +
            throw GeminiError.invalidResponse
186 186
        }
187 187
188 188
        // Meta is everything after status code and space
199 199
200 200
        print("📥 Status: \(statusCode), Meta: \(meta)")
201 201
202 -
        return TitanResponse(statusCode: statusCode, meta: meta, body: body)
202 +
        return GeminiResponse(statusCode: statusCode, meta: meta, body: body)
203 203
    }
204 204
    
205 205
    // Helper class to manage connection state
Titan/Services/TitanParser.swift → Titan/Services/GeminiParser.swift +4 −4
1 1
//
2 -
//  TitanParser.swift
2 +
//  GeminiParser.swift
3 3
//  Titan
4 4
//
5 5
6 6
import Foundation
7 7
8 -
struct TitanParser {
9 -
    static func parse(_ content: String, baseURL: String) -> [TitanLine] {
10 -
        var lines: [TitanLine] = []
8 +
struct GeminiParser {
9 +
    static func parse(_ content: String, baseURL: String) -> [GeminiLine] {
10 +
        var lines: [GeminiLine] = []
11 11
        var inPreformatted = false
12 12
        var preformattedLines: [String] = []
13 13
        var preformattedAlt = ""
Titan/Views/ContentView.swift +5 −5
197 197
    }
198 198
199 199
    private func extractPageTitle() -> String? {
200 -
        let lines = TitanParser.parse(responseText, baseURL: urlText)
200 +
        let lines = GeminiParser.parse(responseText, baseURL: urlText)
201 201
        for line in lines {
202 202
            switch line {
203 203
            case .heading1(let text), .heading2(let text), .heading3(let text):
340 340
            } catch is CancellationError {
341 341
                // Task was cancelled, don't update UI
342 342
                return
343 -
            } catch let error as TitanError where error == .cancelled {
343 +
            } catch let error as GeminiError where error == .cancelled {
344 344
                // Request was cancelled, don't update UI
345 345
                return
346 346
            } catch {
350 350
        }
351 351
    }
352 352
353 -
    private func fetchWithRedirects(urlString: String, redirectCount: Int) async throws -> (TitanResponse, String) {
353 +
    private func fetchWithRedirects(urlString: String, redirectCount: Int) async throws -> (GeminiResponse, String) {
354 354
        // Check for cancellation before starting
355 355
        try Task.checkCancellation()
356 356
357 357
        guard let url = URL(string: urlString),
358 358
              let host = url.host else {
359 -
            throw TitanError.invalidURL
359 +
            throw GeminiError.invalidURL
360 360
        }
361 361
362 -
        let client = TitanClient(rejectUnauthorized: false)
362 +
        let client = GeminiClient(rejectUnauthorized: false)
363 363
        let port = url.port ?? 1965
364 364
        let response = try await client.connect(
365 365
            hostname: host,
Titan/Views/TitanContentView.swift +2 −2
61 61
    }
62 62
63 63
    var body: some View {
64 -
        let lines = TitanParser.parse(content, baseURL: baseURL)
64 +
        let lines = GeminiParser.parse(content, baseURL: baseURL)
65 65
66 66
        LazyVStack(alignment: .leading, spacing: 4) {
67 67
            ForEach(Array(lines.enumerated()), id: \.offset) { _, line in
72 72
    }
73 73
74 74
    @ViewBuilder
75 -
    private func lineView(for line: TitanLine) -> some View {
75 +
    private func lineView(for line: GeminiLine) -> some View {
76 76
        switch line {
77 77
        case .text(let text):
78 78
            Text(text)