// // Components.swift // Zyquo Agent // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // // Small reusable design-system components and interaction modifiers shared // across all screens: hover states, press scaling, badges, provider glyphs. // import SwiftUI // MARK: - Interaction modifiers /// Standard hover feedback: background tint fades in over 80ms. struct HoverHighlight: ViewModifier { var cornerRadius: CGFloat = ZyquoRadius.small @State private var hovering = false func body(content: Content) -> some View { content .background( RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) .fill(hovering ? ZyquoColor.surfaceSecondary : .clear) ) .onHover { inside in withAnimation(ZyquoMotion.hover) { hovering = inside } } } } /// Button press feedback: scale to 0.97. struct PressableButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .scaleEffect(configuration.isPressed ? ZyquoMotion.pressedScale : 1) .animation(ZyquoMotion.hover, value: configuration.isPressed) } } extension View { func zyquoHoverHighlight(cornerRadius: CGFloat = ZyquoRadius.small) -> some View { modifier(HoverHighlight(cornerRadius: cornerRadius)) } } // MARK: - Badges /// Small capsule badge (model chips metadata, capability tags). struct ZyquoBadge: View { let text: String var color: Color = ZyquoColor.textSecondary var body: some View { Text(text) .font(ZyquoFont.caption) .foregroundStyle(color) .padding(.horizontal, ZyquoSpacing.xs) .padding(.vertical, 2) .background( Capsule().fill(ZyquoColor.surfaceSecondary) ) } } /// Colored status dot (provider key state: verified / unset / failed). struct StatusDot: View { enum Status { case verified, unset, failed var color: Color { switch self { case .verified: return ZyquoColor.success case .unset: return ZyquoColor.textTertiary case .failed: return ZyquoColor.danger } } } let status: Status var body: some View { Circle() .fill(status.color) .frame(width: 8, height: 8) } } // MARK: - Provider glyphs extension ProviderID { /// SF Symbol used as the provider's glyph in chips, avatars, and Settings. /// (Custom vector logos can replace these later without touching call sites.) var symbolName: String { switch self { case .openai: return "sparkle" case .anthropic: return "asterisk" case .xai: return "xmark.diamond" case .mistral: return "wind" case .gemini: return "diamond.lefthalf.filled" case .qwen: return "q.circle" case .deepseek: return "water.waves" case .kimi: return "moon.stars" case .perplexity: return "magnifyingglass.circle" case .together: return "circle.hexagongrid" case .deepinfra: return "cube.transparent" case .cerebras: return "bolt.circle" case .custom: return "wrench.and.screwdriver" } } }