SPB Git

spb/zyquo-atlas Public License

The AI-native macOS web browser — every surface, intelligent.

Swift 75.2% JavaScript 22% Shell 2% Makefile 0.9%
3.8 KB · 109 lines swift
Raw Blame History
1//2//  OmniboxView.swift3//  Zyquo Atlas4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//8//  The address/search field. Shows a security indicator, reflects the active9//  tab's URL when not being edited, and on submit resolves URL-vs-search via10//  OmniIntent. The Phase 3 "ask AI" route and answer overlay attach here.11//1213import SwiftUI1415struct OmniboxView: View {16    @ObservedObject var tab: Tab17    let onSubmit: (String) -> Void18    var onAsk: (String) -> Void = { _ in }19    @EnvironmentObject private var themeEngine: ThemeEngine20    @EnvironmentObject private var windowState: WindowState21    private var theme: AtlasTheme { themeEngine.theme }2223    @State private var text: String = ""24    @State private var isEditing: Bool = false25    @FocusState private var focused: Bool2627    var body: some View {28        HStack(spacing: ZyquoSpacing.xs) {29            Image(systemName: securityGlyph)30                .font(.system(size: 11, weight: .semibold))31                .foregroundStyle(securityColor)3233            TextField("Search or enter website name", text: $text)34                .textFieldStyle(.plain)35                .font(ZyquoFont.control)36                .foregroundStyle(theme.textPrimary)37                .focused($focused)38                .onSubmit {39                    onSubmit(text)40                    focused = false41                }42                .onChange(of: focused) { now in43                    isEditing = now44                    if now { selectAll() } else { syncFromTab() }45                }4647            if isEditing && !text.isEmpty {48                Button {49                    onAsk(text)50                    focused = false51                } label: {52                    Label("Ask AI", systemImage: "sparkles")53                        .font(.system(size: 10, weight: .semibold))54                        .padding(.horizontal, ZyquoSpacing.xs)55                        .padding(.vertical, 2)56                        .background(Capsule().fill(theme.accentSubtle))57                        .foregroundStyle(theme.accentIndigo)58                }59                .buttonStyle(.plain)60                .help("Ask AI about this (⌥Return)")61                Button {62                    text = ""63                } label: {64                    Image(systemName: "xmark.circle.fill")65                        .font(.system(size: 12))66                        .foregroundStyle(theme.textTertiary)67                }68                .buttonStyle(.plain)69            }70        }71        .padding(.horizontal, ZyquoSpacing.sm)72        .frame(height: 30)73        .background(74            RoundedRectangle(cornerRadius: ZyquoRadius.small)75                .fill(theme.surfaceSecondary)76        )77        .overlay(78            RoundedRectangle(cornerRadius: ZyquoRadius.small)79                .strokeBorder(focused ? theme.accent : theme.border,80                              lineWidth: focused ? 1.5 : ZyquoMetrics.hairline)81        )82        .onChange(of: tab.url) { _ in if !isEditing { syncFromTab() } }83        .onChange(of: windowState.omniboxFocusToken) { _ in focused = true }84        .onAppear { syncFromTab() }85    }8687    // MARK: - Helpers8889    private var securityGlyph: String {90        guard tab.url != nil else { return "magnifyingglass" }91        return tab.hasSecureConnection ? "lock.fill" : "exclamationmark.triangle.fill"92    }9394    private var securityColor: Color {95        guard tab.url != nil else { return theme.textTertiary }96        return tab.hasSecureConnection ? theme.textSecondary : theme.warning97    }9899    private func syncFromTab() {100        text = tab.url?.absoluteString ?? ""101    }102103    private func selectAll() {104        // Reflect the raw URL for editing; SwiftUI selects on focus via the105        // field's default behavior when text is present.106        if text.isEmpty { syncFromTab() }107    }108}109