feat: added search b4278c60
Steve · 2025-12-25 21:55 4 file(s) · +46 −4
Titan/Settings/ThemeSettings.swift +3 −0
89 89
    /// The home page URL that the browser navigates to on launch and when pressing Home
90 90
    @AppStorage("homePage") var homePage: String = "gemini://geminiprotocol.net/"
91 91
92 +
    /// The search engine URL used for search queries (query appended after ?)
93 +
    @AppStorage("searchEngine") var searchEngine: String = "gemini://kennedy.gemi.dev/search"
94 +
92 95
    /// Computed property for current background color based on system appearance
93 96
    var backgroundColor: Color {
94 97
        switch appearanceMode {
Titan/Views/BrowserToolbar.swift +2 −2
92 92
    }
93 93
94 94
    private var urlTextField: some View {
95 -
        TextField("Enter Gemini URL", text: $urlText)
95 +
        TextField("gemini:// or search", text: $urlText)
96 96
            .focused(isURLFocused)
97 97
            .autocapitalization(.none)
98 98
            .disableAutocorrection(true)
99 -
            .keyboardType(.URL)
99 +
            .keyboardType(.webSearch)
100 100
            .submitLabel(.go)
101 101
            .onSubmit {
102 102
                isURLFocused.wrappedValue = false
Titan/Views/ContentView.swift +27 −2
219 219
        historyIndex < history.count - 1
220 220
    }
221 221
222 +
    private func normalizeURL(_ input: String) -> String {
223 +
        let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines)
224 +
        guard !trimmed.isEmpty else { return trimmed }
225 +
226 +
        // If it already has a scheme, return as-is
227 +
        let lowercased = trimmed.lowercased()
228 +
        if lowercased.hasPrefix("gemini://") ||
229 +
           lowercased.hasPrefix("http://") ||
230 +
           lowercased.hasPrefix("https://") ||
231 +
           lowercased.hasPrefix("mailto:") {
232 +
            return trimmed
233 +
        }
234 +
235 +
        // Check if it looks like a domain (contains a dot, no spaces)
236 +
        if trimmed.contains(".") && !trimmed.contains(" ") {
237 +
            return "gemini://" + trimmed
238 +
        }
239 +
240 +
        // Otherwise treat as a search query
241 +
        let encoded = trimmed.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? trimmed
242 +
        return themeSettings.searchEngine + "?" + encoded
243 +
    }
244 +
222 245
    private func navigateTo(_ url: String) {
246 +
        let normalizedURL = normalizeURL(url)
247 +
223 248
        // Check if this is an external URL (http, https, mailto)
224 -
        if let urlObj = URL(string: url) {
249 +
        if let urlObj = URL(string: normalizedURL) {
225 250
            let scheme = urlObj.scheme?.lowercased() ?? ""
226 251
            if scheme == "http" || scheme == "https" || scheme == "mailto" {
227 252
                UIApplication.shared.open(urlObj)
233 258
            history = Array(history.prefix(historyIndex + 1))
234 259
        }
235 260
236 -
        urlText = url
261 +
        urlText = normalizedURL
237 262
        fetchContent(addToHistory: true)
238 263
    }
239 264
Titan/Views/SettingsView.swift +14 −0
10 10
    @Environment(\.dismiss) private var dismiss
11 11
12 12
    @State private var homePageText: String = ""
13 +
    @State private var searchEngineText: String = ""
13 14
    @State private var selectedAppearanceMode: AppearanceMode = .automatic
14 15
    @State private var selectedAccentColor: Color = .blue
15 16
    @State private var selectedLightBackgroundColor: Color = .white
30 31
                    Text("Home Page")
31 32
                } footer: {
32 33
                    Text("The page that loads when you open the app or tap the Home button.")
34 +
                }
35 +
36 +
                Section {
37 +
                    TextField("Search Engine URL", text: $searchEngineText)
38 +
                        .autocapitalization(.none)
39 +
                        .disableAutocorrection(true)
40 +
                        .keyboardType(.URL)
41 +
                } header: {
42 +
                    Text("Search Engine")
43 +
                } footer: {
44 +
                    Text("The search engine used when typing a search query. Your query will be appended after a \"?\".")
33 45
                }
34 46
35 47
                Section {
80 92
                ToolbarItem(placement: .confirmationAction) {
81 93
                    Button("Save") {
82 94
                        themeSettings.homePage = homePageText
95 +
                        themeSettings.searchEngine = searchEngineText
83 96
                        themeSettings.setAppearanceMode(selectedAppearanceMode)
84 97
                        themeSettings.setAllAccentColors(selectedAccentColor)
85 98
                        themeSettings.setLightBackgroundColor(selectedLightBackgroundColor)
93 106
            }
94 107
            .onAppear {
95 108
                homePageText = themeSettings.homePage
109 +
                searchEngineText = themeSettings.searchEngine
96 110
                selectedAppearanceMode = themeSettings.appearanceMode
97 111
                selectedAccentColor = themeSettings.accentColor
98 112
                selectedLightBackgroundColor = themeSettings.lightBackgroundColor