Browse Source

feat: solid obsidian core with single high-amplitude dynamic electric violet stroke (zero static secondary rings)

main
Ali Alavi 2 weeks ago
parent
commit
9bc1afb87e
  1. 178
      src/HUDOverlay.swift

178
src/HUDOverlay.swift

@ -14,10 +14,10 @@ public final class HUDOverlayController {
public static let shared = HUDOverlayController()
private var window: NSPanel?
private var orbView: AppleIntelligenceOrbView?
private var orbView: MinimalSolidOrbView?
private var isVisible = false
// Large transparent canvas so audio-reactive halo never clips
// Transparent canvas with plenty of margin
private let canvasSize: CGFloat = 200
private init() {
@ -44,10 +44,10 @@ public final class HUDOverlayController {
let container = NSView(frame: panel.contentView!.bounds)
container.wantsLayer = true
// Professional Apple Intelligence Orb centered in canvas
let orbDiameter: CGFloat = 54
// Solid Minimal Orb centered in canvas
let orbDiameter: CGFloat = 52
let orbOrigin = (canvasSize - orbDiameter) / 2.0
let orb = AppleIntelligenceOrbView(frame: NSRect(x: orbOrigin, y: orbOrigin, width: orbDiameter, height: orbDiameter))
let orb = MinimalSolidOrbView(frame: NSRect(x: orbOrigin, y: orbOrigin, width: orbDiameter, height: orbDiameter))
container.addSubview(orb)
self.orbView = orb
@ -127,15 +127,15 @@ public final class HUDOverlayController {
}
}
// MARK: - Professional Apple Intelligence Orb View
// MARK: - Minimal Solid-Color Orb with Single High-Amplitude Dynamic Stroke (Zero Static Rings)
final class AppleIntelligenceOrbView: NSView {
private let glowLayer = CALayer()
private let coreGradientLayer = CAGradientLayer()
private let strokeBorderLayer = CALayer()
final class MinimalSolidOrbView: NSView {
private let coreSolidLayer = CALayer()
private let dynamicStrokeLayer = CALayer()
private let minStrokeWidth: CGFloat = 2.0
private let maxStrokeWidth: CGFloat = 7.5
// Wide dynamic range: from a delicate 1.5px ring up to a bold 11.5px pulse!
private let minStrokeWidth: CGFloat = 1.5
private let maxStrokeWidth: CGFloat = 11.5
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
@ -156,85 +156,62 @@ final class AppleIntelligenceOrbView: NSView {
let bounds = self.bounds
let radius = bounds.width / 2.0
// 1. Atmospheric Ambient Glow (Expands and blooms with decibel level)
glowLayer.frame = bounds.insetBy(dx: -10, dy: -10)
glowLayer.cornerRadius = (bounds.width + 20) / 2.0
glowLayer.backgroundColor = NSColor(red: 0.0, green: 0.70, blue: 1.0, alpha: 0.25).cgColor
glowLayer.shadowColor = NSColor(red: 0.0, green: 0.85, blue: 1.0, alpha: 0.85).cgColor
glowLayer.shadowRadius = 12
glowLayer.shadowOpacity = 0.45
glowLayer.shadowOffset = .zero
layer.addSublayer(glowLayer)
// 2. Deep Sapphire/Midnight Blue Radial Core (Clean, minimal, luxury Apple design)
coreGradientLayer.frame = bounds
coreGradientLayer.cornerRadius = radius
coreGradientLayer.type = .radial
coreGradientLayer.colors = [
NSColor(red: 0.05, green: 0.55, blue: 0.95, alpha: 1.0).cgColor, // Vibrant Cerulean Center
NSColor(red: 0.12, green: 0.18, blue: 0.55, alpha: 0.98).cgColor, // Deep Royal Blue
NSColor(red: 0.06, green: 0.08, blue: 0.22, alpha: 0.98).cgColor // Midnight Obsidian Edge
]
coreGradientLayer.startPoint = CGPoint(x: 0.40, y: 0.40)
coreGradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
layer.addSublayer(coreGradientLayer)
// 3. Audio-Reactive Neon Stroke (Widening & Brightening dynamically with voice)
strokeBorderLayer.frame = bounds
strokeBorderLayer.cornerRadius = radius
strokeBorderLayer.borderWidth = minStrokeWidth
strokeBorderLayer.borderColor = NSColor(red: 0.30, green: 0.90, blue: 1.0, alpha: 0.75).cgColor
strokeBorderLayer.backgroundColor = NSColor.clear.cgColor
layer.addSublayer(strokeBorderLayer)
// 1. Chic, Minimal Solid-Color Core (Apple Obsidian Charcoal Solid Disc)
// 100% solid, perfectly flat, clean and matte
coreSolidLayer.frame = bounds
coreSolidLayer.cornerRadius = radius
coreSolidLayer.backgroundColor = NSColor(red: 0.09, green: 0.10, blue: 0.13, alpha: 0.98).cgColor
layer.addSublayer(coreSolidLayer)
// 2. Single Dynamic Audio-Reactive Stroke Ring (NO SECOND RING / NO STATIC BACKGROUND)
// Solid Electric Amethyst Violet (#B066FF)
dynamicStrokeLayer.frame = bounds
dynamicStrokeLayer.cornerRadius = radius
dynamicStrokeLayer.borderWidth = minStrokeWidth
dynamicStrokeLayer.borderColor = NSColor(red: 0.70, green: 0.38, blue: 1.0, alpha: 0.75).cgColor
dynamicStrokeLayer.backgroundColor = NSColor.clear.cgColor
dynamicStrokeLayer.shadowColor = NSColor(red: 0.65, green: 0.25, blue: 1.0, alpha: 0.85).cgColor
dynamicStrokeLayer.shadowOffset = .zero
dynamicStrokeLayer.shadowRadius = 4.0
dynamicStrokeLayer.shadowOpacity = 0.40
layer.addSublayer(dynamicStrokeLayer)
}
/// Updates audio response:
/// In silence (level ~ 0.0): crisp minimal 2px stroke, subtle soft glow.
/// When speaking: stroke powerfully thickens up to 7.5px, glow blooms to 42px, smooth gentle scale breathing.
/// Updates audio response with boosted logarithmic curve for wide dynamic amplitude:
/// In quiet: minimal 1.5px stroke.
/// When speaking: expands dramatically up to 11.5px with bright electric violet glow.
func updateAudioLevel(_ level: Float, isLagging: Bool) {
CATransaction.begin()
CATransaction.setDisableActions(true)
let clamped = CGFloat(min(max(level, 0.0), 1.0))
// Perceptual boost power curve so lower/normal voice levels trigger a bold visible response
let dynamicFactor = pow(clamped, 0.70)
if isLagging {
// Amber warm warning for degraded internet
coreGradientLayer.colors = [
NSColor(red: 1.0, green: 0.75, blue: 0.15, alpha: 1.0).cgColor,
NSColor(red: 0.85, green: 0.40, blue: 0.05, alpha: 0.98).cgColor,
NSColor(red: 0.25, green: 0.10, blue: 0.02, alpha: 0.98).cgColor
]
let amberStroke = NSColor(red: 1.0, green: 0.80, blue: 0.25, alpha: 0.7 + (0.3 * clamped)).cgColor
strokeBorderLayer.borderColor = amberStroke
glowLayer.shadowColor = NSColor.orange.cgColor
// Warm amber gold highlight for degraded network
let amber = NSColor(red: 1.0, green: 0.68, blue: 0.15, alpha: 0.75 + (0.25 * dynamicFactor)).cgColor
dynamicStrokeLayer.borderColor = amber
dynamicStrokeLayer.shadowColor = amber
} else {
// Standard crisp Apple Intelligence sapphire palette
coreGradientLayer.colors = [
NSColor(red: 0.05, green: 0.55 + (0.40 * clamped), blue: 0.95 + (0.05 * clamped), alpha: 1.0).cgColor,
NSColor(red: 0.12, green: 0.18, blue: 0.55, alpha: 0.98).cgColor,
NSColor(red: 0.06, green: 0.08, blue: 0.22, alpha: 0.98).cgColor
]
let neonStroke = NSColor(
red: 0.30 + (0.60 * clamped),
green: 0.90 + (0.10 * clamped),
// Solid Electric Amethyst Violet palette: brightens to glowing neon with volume
let violetStroke = NSColor(
red: 0.70 + (0.20 * dynamicFactor),
green: 0.38 + (0.50 * dynamicFactor),
blue: 1.0,
alpha: 0.65 + (0.35 * clamped)
alpha: 0.75 + (0.25 * dynamicFactor)
).cgColor
strokeBorderLayer.borderColor = neonStroke
glowLayer.shadowColor = NSColor(red: 0.0, green: 0.85, blue: 1.0, alpha: 0.90).cgColor
dynamicStrokeLayer.borderColor = violetStroke
dynamicStrokeLayer.shadowColor = NSColor(red: 0.65, green: 0.25, blue: 1.0, alpha: 0.85).cgColor
}
// 1. Dynamic Stroke Expansion: thickens from 2.0px to 7.5px noticeably
let dynamicWidth = minStrokeWidth + (clamped * (maxStrokeWidth - minStrokeWidth))
strokeBorderLayer.borderWidth = dynamicWidth
// 1. High Dynamic Amplitude Stroke Expansion (1.5px to 11.5px)
let dynamicWidth = minStrokeWidth + (dynamicFactor * (maxStrokeWidth - minStrokeWidth))
dynamicStrokeLayer.borderWidth = dynamicWidth
// 2. Dynamic Bloom Halo: blooms from 12px up to 42px
glowLayer.shadowRadius = 12.0 + (clamped * 30.0)
glowLayer.shadowOpacity = Float(0.45 + (0.50 * clamped))
// 3. Subtle organic breathing scale (1.0 to 1.14x max, smooth and stable without shaking)
let subtleScale = 1.0 + (clamped * 0.14)
self.layer?.sublayerTransform = CATransform3DMakeScale(subtleScale, subtleScale, 1.0)
// 2. Soft Dynamic Bloom Glow (From 4px to 26px radius, strictly tracking voice)
dynamicStrokeLayer.shadowRadius = 4.0 + (dynamicFactor * 22.0)
dynamicStrokeLayer.shadowOpacity = Float(0.40 + (0.55 * dynamicFactor))
CATransaction.commit()
}
@ -243,21 +220,23 @@ final class AppleIntelligenceOrbView: NSView {
CATransaction.begin()
CATransaction.setDisableActions(false)
CATransaction.setAnimationDuration(0.20)
strokeBorderLayer.borderWidth = 4.0
strokeBorderLayer.borderColor = NSColor.orange.cgColor
glowLayer.shadowColor = NSColor.orange.cgColor
glowLayer.shadowRadius = 24
glowLayer.shadowOpacity = 0.9
let amber = NSColor(red: 1.0, green: 0.65, blue: 0.10, alpha: 1.0).cgColor
dynamicStrokeLayer.borderWidth = 5.0
dynamicStrokeLayer.borderColor = amber
dynamicStrokeLayer.shadowColor = amber
dynamicStrokeLayer.shadowRadius = 18
dynamicStrokeLayer.shadowOpacity = 0.90
CATransaction.commit()
}
func showTranscribingState() {
CATransaction.begin()
CATransaction.setDisableActions(true)
strokeBorderLayer.borderWidth = 3.0
strokeBorderLayer.borderColor = NSColor.cyan.cgColor
glowLayer.shadowRadius = 18
glowLayer.shadowOpacity = 0.8
let softLilac = NSColor(red: 0.80, green: 0.50, blue: 1.0, alpha: 1.0).cgColor
dynamicStrokeLayer.borderWidth = 3.5
dynamicStrokeLayer.borderColor = softLilac
dynamicStrokeLayer.shadowRadius = 10
dynamicStrokeLayer.shadowOpacity = 0.65
CATransaction.commit()
}
@ -266,11 +245,11 @@ final class AppleIntelligenceOrbView: NSView {
CATransaction.setDisableActions(false)
CATransaction.setAnimationDuration(0.18)
let green = NSColor(red: 0.15, green: 0.95, blue: 0.45, alpha: 1.0).cgColor
strokeBorderLayer.borderWidth = 4.0
strokeBorderLayer.borderColor = green
glowLayer.shadowColor = green
glowLayer.shadowRadius = 24
glowLayer.shadowOpacity = 0.95
dynamicStrokeLayer.borderWidth = 5.0
dynamicStrokeLayer.borderColor = green
dynamicStrokeLayer.shadowColor = green
dynamicStrokeLayer.shadowRadius = 20
dynamicStrokeLayer.shadowOpacity = 0.95
CATransaction.commit()
}
@ -279,22 +258,21 @@ final class AppleIntelligenceOrbView: NSView {
CATransaction.setDisableActions(false)
CATransaction.setAnimationDuration(0.18)
let red = NSColor(red: 1.0, green: 0.20, blue: 0.20, alpha: 1.0).cgColor
strokeBorderLayer.borderWidth = 4.0
strokeBorderLayer.borderColor = red
glowLayer.shadowColor = red
glowLayer.shadowRadius = 24
glowLayer.shadowOpacity = 0.95
dynamicStrokeLayer.borderWidth = 5.0
dynamicStrokeLayer.borderColor = red
dynamicStrokeLayer.shadowColor = red
dynamicStrokeLayer.shadowRadius = 20
dynamicStrokeLayer.shadowOpacity = 0.95
CATransaction.commit()
}
func reset() {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.layer?.sublayerTransform = CATransform3DIdentity
strokeBorderLayer.borderWidth = minStrokeWidth
strokeBorderLayer.borderColor = NSColor(red: 0.30, green: 0.90, blue: 1.0, alpha: 0.75).cgColor
glowLayer.shadowRadius = 12
glowLayer.shadowOpacity = 0.45
dynamicStrokeLayer.borderWidth = minStrokeWidth
dynamicStrokeLayer.borderColor = NSColor(red: 0.70, green: 0.38, blue: 1.0, alpha: 0.75).cgColor
dynamicStrokeLayer.shadowRadius = 4.0
dynamicStrokeLayer.shadowOpacity = 0.40
CATransaction.commit()
}
}
Loading…
Cancel
Save