@ -14,11 +14,11 @@ public final class HUDOverlayController {
public static let shared = HUDOverlayController ( )
public static let shared = HUDOverlayController ( )
private var window : NSPanel ?
private var window : NSPanel ?
private var orbView : SiriOrbView ?
private var orbView : Minimal SiriOrbView?
private var isVisible = false
private var isVisible = false
// L a r g e c a n v a s s o s h a d o w s / h a l o s N E V E R c l i p a t w i n d o w b o u n d a r i e s
private let canvasSize : CGFloat = 2 60
// T r a n s p a r e n t c a n v a s w i t h p l e n t y o f m a r g i n s o h a l o n e v e r c l i p s
private let canvasSize : CGFloat = 1 60
private init ( ) {
private init ( ) {
DispatchQueue . main . async {
DispatchQueue . main . async {
@ -44,10 +44,10 @@ public final class HUDOverlayController {
let container = NSView ( frame : panel . contentView ! . bounds )
let container = NSView ( frame : panel . contentView ! . bounds )
container . wantsLayer = true
container . wantsLayer = true
// S i r i - l i k e C i r c u l a r O r b c e n t e r e d w i t h p l e n t y o f b r e a t h i n g r o o m f o r g l o w i n g h a l o s
let orbSize : CGFloat = 64
let orbOrigin = ( canvasSize - orbSize ) / 2.0
let orb = SiriOrbView ( frame : NSRect ( x : orbOrigin , y : orbOrigin , width : orbSize , height : orbSize ) )
// M i n i m a l S i r i O r b c e n t e r e d i n c a n v a s
let orbDiameter : CGFloat = 52
let orbOrigin = ( canvasSize - orbDiameter ) / 2.0
let orb = Minimal SiriOrbView( frame : NSRect ( x : orbOrigin , y : orbOrigin , width : orbDiameter , height : orbDiameter ) )
container . addSubview ( orb )
container . addSubview ( orb )
self . orbView = orb
self . orbView = orb
@ -59,9 +59,8 @@ public final class HUDOverlayController {
private func reposition ( ) {
private func reposition ( ) {
guard let window = self . window , let screen = NSScreen . main else { return }
guard let window = self . window , let screen = NSScreen . main else { return }
let screenRect = screen . visibleFrame
let screenRect = screen . visibleFrame
// C e n t e r h o r i z o n t a l l y , p o s i t i o n n e a r b o t t o m o f s c r e e n
let x = screenRect . midX - ( canvasSize / 2.0 )
let x = screenRect . midX - ( canvasSize / 2.0 )
let y = screenRect . minY + 1 2
let y = screenRect . minY + 28
window . setFrameOrigin ( NSPoint ( x : x , y : y ) )
window . setFrameOrigin ( NSPoint ( x : x , y : y ) )
}
}
@ -99,13 +98,13 @@ public final class HUDOverlayController {
case . success :
case . success :
orb . showSuccessState ( )
orb . showSuccessState ( )
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.45 ) {
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.40 ) {
self . hide ( )
self . hide ( )
}
}
case . error :
case . error :
orb . showErrorState ( )
orb . showErrorState ( )
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.75 ) {
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.70 ) {
self . hide ( )
self . hide ( )
}
}
}
}
@ -116,7 +115,7 @@ public final class HUDOverlayController {
DispatchQueue . main . async { [ weak self ] in
DispatchQueue . main . async { [ weak self ] in
guard let self = self , let window = self . window , self . isVisible else { return }
guard let self = self , let window = self . window , self . isVisible else { return }
NSAnimationContext . runAnimationGroup ( { context in
NSAnimationContext . runAnimationGroup ( { context in
context . duration = 0.20
context . duration = 0.18
window . animator ( ) . alphaValue = 0.0
window . animator ( ) . alphaValue = 0.0
} , completionHandler : {
} , completionHandler : {
window . orderOut ( nil )
window . orderOut ( nil )
@ -127,15 +126,16 @@ public final class HUDOverlayController {
}
}
}
}
// MARK: - P r e m i u m A p p l e I n t e l l i g e n c e / S i r i M u l t i l a y e r G l o w i n g O r b
// MARK: - M i n i m a l C l e a n S i r i O r b ( S t a t i c , N o n - R o t a t i n g , A u d i o - R e a c t i v e S t r o k e & G l o w )
final class SiriOrbView : NSView {
private let ambientAuraLayer = CALayer ( )
private let haloLayer = CALayer ( )
private let acousticRippleLayer = CALayer ( )
private let glassBackdrop = CALayer ( )
private let siriGradientLayer = CAGradientLayer ( )
private let specularHighlight = CAGradientLayer ( )
final class MinimalSiriOrbView : NSView {
private let glowLayer = CALayer ( )
private let gradientCoreLayer = CAGradientLayer ( )
private let strokeBorderLayer = CALayer ( )
private let baseDiameter : CGFloat = 52.0
private let minStrokeWidth : CGFloat = 1.5
private let maxStrokeWidth : CGFloat = 5.5
override init ( frame frameRect : NSRect ) {
override init ( frame frameRect : NSRect ) {
super . init ( frame : frameRect )
super . init ( frame : frameRect )
@ -156,80 +156,41 @@ final class SiriOrbView: NSView {
let bounds = self . bounds
let bounds = self . bounds
let radius = bounds . width / 2.0
let radius = bounds . width / 2.0
// 1 . D e e p A m b i e n t S o f t A u r a ( D i f f u s e d o u t e r g l o w )
ambientAuraLayer . frame = bounds . insetBy ( dx : - 32 , dy : - 32 )
ambientAuraLayer . cornerRadius = ( bounds . width + 64 ) / 2.0
ambientAuraLayer . backgroundColor = NSColor ( red : 0.0 , green : 0.65 , blue : 1.0 , alpha : 0.22 ) . cgColor
ambientAuraLayer . shadowColor = NSColor ( red : 0.55 , green : 0.0 , blue : 1.0 , alpha : 0.85 ) . cgColor
ambientAuraLayer . shadowRadius = 38
ambientAuraLayer . shadowOpacity = 0.95
ambientAuraLayer . shadowOffset = . zero
layer . addSublayer ( ambientAuraLayer )
// 2 . F o c u s e d N e o n H a l o
haloLayer . frame = bounds . insetBy ( dx : - 14 , dy : - 14 )
haloLayer . cornerRadius = ( bounds . width + 28 ) / 2.0
haloLayer . backgroundColor = NSColor ( red : 0.1 , green : 0.8 , blue : 1.0 , alpha : 0.40 ) . cgColor
haloLayer . shadowColor = NSColor ( red : 0.0 , green : 0.9 , blue : 1.0 , alpha : 0.85 ) . cgColor
haloLayer . shadowRadius = 18
haloLayer . shadowOpacity = 0.9
haloLayer . shadowOffset = . zero
layer . addSublayer ( haloLayer )
// 3 . A c o u s t i c R i p p l e R i n g
acousticRippleLayer . frame = bounds . insetBy ( dx : - 4 , dy : - 4 )
acousticRippleLayer . cornerRadius = ( bounds . width + 8 ) / 2.0
acousticRippleLayer . borderColor = NSColor ( red : 0.0 , green : 0.95 , blue : 1.0 , alpha : 0.35 ) . cgColor
acousticRippleLayer . borderWidth = 1.5
layer . addSublayer ( acousticRippleLayer )
// 4 . D a r k G l a s s S p h e r i c a l B a c k d r o p
glassBackdrop . frame = bounds
glassBackdrop . cornerRadius = radius
glassBackdrop . backgroundColor = NSColor . black . withAlphaComponent ( 0.85 ) . cgColor
glassBackdrop . borderColor = NSColor . white . withAlphaComponent ( 0.35 ) . cgColor
glassBackdrop . borderWidth = 1.0
layer . addSublayer ( glassBackdrop )
// 5 . M u l t i - C o l o r L i q u i d S i r i G r a d i e n t C o r e
siriGradientLayer . frame = bounds . insetBy ( dx : 3 , dy : 3 )
siriGradientLayer . cornerRadius = ( bounds . width - 6 ) / 2.0
siriGradientLayer . type = . radial
siriGradientLayer . colors = [
NSColor ( red : 0.0 , green : 0.95 , blue : 1.0 , alpha : 1.0 ) . cgColor , // E l e c t r i c C y a n C e n t e r
NSColor ( red : 0.50 , green : 0.12 , blue : 1.0 , alpha : 0.95 ) . cgColor , // R o y a l V i o l e t
NSColor ( red : 0.98 , green : 0.08 , blue : 0.65 , alpha : 0.90 ) . cgColor , // H o t M a g e n t a
NSColor ( red : 0.10 , green : 0.20 , blue : 0.95 , alpha : 0.85 ) . cgColor // D e e p B l u e
]
siriGradientLayer . startPoint = CGPoint ( x : 0.35 , y : 0.35 )
siriGradientLayer . endPoint = CGPoint ( x : 1.0 , y : 1.0 )
layer . addSublayer ( siriGradientLayer )
// 6 . 3 D C u r v e d S p e c u l a r G l a s s C r e s c e n t H i g h l i g h t ( A p p l e I n t e l l i g e n c e s t y l e )
specularHighlight . frame = NSRect ( x : bounds . midX - 18 , y : bounds . maxY - 18 , width : 36 , height : 12 )
specularHighlight . cornerRadius = 6
specularHighlight . colors = [
NSColor . white . withAlphaComponent ( 0.65 ) . cgColor ,
NSColor . white . withAlphaComponent ( 0.0 ) . cgColor
// 1 . S o f t O u t e r A t m o s p h e r i c G l o w ( B r e a t h e s w i t h v o i c e v o l u m e )
glowLayer . frame = bounds . insetBy ( dx : - 10 , dy : - 10 )
glowLayer . cornerRadius = ( bounds . width + 20 ) / 2.0
glowLayer . backgroundColor = NSColor ( red : 0.05 , green : 0.65 , blue : 1.0 , alpha : 0.35 ) . cgColor
glowLayer . shadowColor = NSColor ( red : 0.50 , green : 0.10 , blue : 0.95 , alpha : 0.85 ) . cgColor
glowLayer . shadowRadius = 14
glowLayer . shadowOpacity = 0.60
glowLayer . shadowOffset = . zero
layer . addSublayer ( glowLayer )
// 2 . C l e a n , M i n i m a l , N o n - R o t a t i n g S i r i G r a d i e n t S p h e r e
gradientCoreLayer . frame = bounds
gradientCoreLayer . cornerRadius = radius
gradientCoreLayer . type = . radial
gradientCoreLayer . colors = [
NSColor ( red : 0.0 , green : 0.92 , blue : 1.0 , alpha : 1.0 ) . cgColor , // E l e c t r i c C y a n C o r e
NSColor ( red : 0.45 , green : 0.12 , blue : 0.96 , alpha : 0.98 ) . cgColor , // R i c h R o y a l V i o l e t
NSColor ( red : 0.90 , green : 0.08 , blue : 0.58 , alpha : 0.92 ) . cgColor , // D e e p M a g e n t a
NSColor ( red : 0.08 , green : 0.10 , blue : 0.35 , alpha : 0.88 ) . cgColor // D e e p N a v y E d g e
]
]
specularHighlight . startPoint = CGPoint ( x : 0.5 , y : 1.0 )
specularHighlight . endPoint = CGPoint ( x : 0.5 , y : 0.0 )
layer . addSublayer ( specularHighlight )
// S t a r t f l u i d r o t a t i o n
startOrbRotation ( )
}
private func startOrbRotation ( ) {
let rotate = CABasicAnimation ( keyPath : " transform.rotation.z " )
rotate . fromValue = 0.0
rotate . toValue = 2 * Double . pi
rotate . duration = 4.0
rotate . repeatCount = . infinity
rotate . isRemovedOnCompletion = false
siriGradientLayer . add ( rotate , forKey : " siriFluidRotate " )
gradientCoreLayer . startPoint = CGPoint ( x : 0.40 , y : 0.40 )
gradientCoreLayer . endPoint = CGPoint ( x : 1.0 , y : 1.0 )
layer . addSublayer ( gradientCoreLayer )
// 3 . D y n a m i c A u d i o - R e a c t i v e S t r o k e R i n g ( W i d e n i n g & b r i g h t e n i n g w i t h s p e e c h )
strokeBorderLayer . frame = bounds
strokeBorderLayer . cornerRadius = radius
strokeBorderLayer . borderWidth = minStrokeWidth
strokeBorderLayer . borderColor = NSColor . white . withAlphaComponent ( 0.35 ) . cgColor
strokeBorderLayer . backgroundColor = NSColor . clear . cgColor
layer . addSublayer ( strokeBorderLayer )
}
}
// / U p d a t e s a u d i o r e s p o n s e : T h e o r b r e m a i n s s t e a d y ( n o j i g g l i n g / s c a l i n g ) ,
// / w h i l e t h e s t r o k e b o r d e r t h i c k n e s s a n d o u t e r g l o w b l o o m d y n a m i c a l l y w i t h v o i c e .
func updateAudioLevel ( _ level : Float , isLagging : Bool ) {
func updateAudioLevel ( _ level : Float , isLagging : Bool ) {
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( true )
CATransaction . setDisableActions ( true )
@ -237,41 +198,40 @@ final class SiriOrbView: NSView {
let clamped = CGFloat ( min ( max ( level , 0.0 ) , 1.0 ) )
let clamped = CGFloat ( min ( max ( level , 0.0 ) , 1.0 ) )
if isLagging {
if isLagging {
// W a r m a m b e r p u l s e f o r w e a k i n t e r n e t
siriGradient Layer. colors = [
NSColor ( red : 1.0 , green : 0.85 , blue : 0.2 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 1.0 , green : 0.5 5 , blue : 0.0 , alpha : 0.95 ) . cgColor ,
NSColor ( red : 0.9 5 , green : 0.2 5 , blue : 0.0 , alpha : 0.90 ) . cgColor
// W a r m a m b e r i n d i c a t o r f o r n e t w o r k d e g r a d a t i o n
gradientCore Layer. colors = [
NSColor ( red : 1.0 , green : 0.80 , blue : 0.20 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 1.0 , green : 0.4 5 , blue : 0.0 , alpha : 0.95 ) . cgColor ,
NSColor ( red : 0.8 5 , green : 0.1 5 , blue : 0.0 , alpha : 0.90 ) . cgColor
]
]
haloLayer . background Color = NSColor ( red : 1.0 , green : 0.5 , blue : 0.0 , alpha : 0.5 ) . cgColor
haloLayer . shadowColor = NSColor ( red : 1.0 , green : 0.4 , blue : 0.0 , alpha : 0.9 ) . cgColor
ambientAura Layer. shadowColor = NSColor ( red : 1.0 , green : 0.3 , blue : 0.0 , alpha : 0.7 ) . cgColor
let stroke Color = NSColor ( red : 1.0 , green : 0.70 , blue : 0.2 0 , alpha : 0.6 + ( 0.4 * clamped ) )
strokeBorderLayer . borderColor = strokeColor . cgColor
glow Layer. shadowColor = NSColor . orange . cgColor
} else {
} else {
// H i g h - f i d e l i t y S i r i e l e c t r i c g r a d i e n t
siriGradient Layer. colors = [
NSColor ( red : 0.0 , green : 0.95 , blue : 1.0 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 0.50 , green : 0.12 , blue : 1.0 , alpha : 0.95 ) . cgColor ,
NSColor ( red : 0.98 , green : 0.08 , blue : 0.6 5 , alpha : 0.90 ) . cgColor ,
NSColor ( red : 0.1 0 , green : 0.2 0 , blue : 0.9 5 , alpha : 0.85 ) . cgColor
// S t a n d a r d c r i s p S i r i p a l e t t e
gradientCore Layer. colors = [
NSColor ( red : 0.0 , green : 0.92 , blue : 1.0 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 0.4 5 , green : 0.12 , blue : 0.96 , alpha : 0.98 ) . cgColor ,
NSColor ( red : 0.90 , green : 0.08 , blue : 0.58 , alpha : 0.92 ) . cgColor ,
NSColor ( red : 0.08 , green : 0.1 0 , blue : 0.3 5 , alpha : 0.88 ) . cgColor
]
]
haloLayer . backgroundColor = NSColor ( red : 0.1 , green : 0.8 , blue : 1.0 , alpha : 0.40 ) . cgColor
haloLayer . shadowColor = NSColor ( red : 0.0 , green : 0.9 , blue : 1.0 , alpha : 0.85 ) . cgColor
ambientAuraLayer . shadowColor = NSColor ( red : 0.55 , green : 0.0 , blue : 1.0 , alpha : 0.85 ) . cgColor
let strokeColor = NSColor (
red : 0.3 + ( 0.7 * clamped ) ,
green : 0.95 ,
blue : 1.0 ,
alpha : 0.35 + ( 0.65 * clamped )
)
strokeBorderLayer . borderColor = strokeColor . cgColor
glowLayer . shadowColor = NSColor ( red : 0.45 , green : 0.10 , blue : 0.95 , alpha : 0.85 ) . cgColor
}
}
// M a s s i v e A u d i o D y n a m i c S c a l i n g ( O r b e x p a n d s u p t o 1 . 6 5 x w i t h v o i c e )
let scale = 1.0 + ( clamped * 0.65 )
self . layer ? . sublayerTransform = CATransform3DMakeScale ( scale , scale , 1.0 )
// 1 . D y n a m i c S t r o k e W i d t h : f r o m 1 . 5 p x u p t o 5 . 5 p x b a s e d o n v o i c e v o l u m e
let dynamicWidth = minStrokeWidth + ( clamped * ( maxStrokeWidth - minStrokeWidth ) )
strokeBorderLayer . borderWidth = dynamicWidth
// D y n a m i c H a l o b l o o m ( E x p a n d s s m o o t h l y u p t o 5 5 p x r a d i u s )
haloLayer . shadowRadius = 18 + ( clamped * 36 )
haloLayer . shadowOpacity = Float ( 0.75 + ( clamped * 0.25 ) )
ambientAuraLayer . shadowRadius = 38 + ( clamped * 42 )
// A c o u s t i c r i p p l e e x p a n d s o u t w a r d
let rippleScale = 1.0 + ( clamped * 0.35 )
acousticRippleLayer . transform = CATransform3DMakeScale ( rippleScale , rippleScale , 1.0 )
acousticRippleLayer . opacity = Float ( 0.3 + ( clamped * 0.7 ) )
// 2 . D y n a m i c O u t e r G l o w : b l o o m s s o f t l y w i t h s p e e c h
glowLayer . shadowRadius = 14 + ( clamped * 24 )
glowLayer . shadowOpacity = Float ( 0.60 + ( clamped * 0.35 ) )
CATransaction . commit ( )
CATransaction . commit ( )
}
}
@ -279,58 +239,52 @@ final class SiriOrbView: NSView {
func showRetryState ( ) {
func showRetryState ( ) {
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( false )
CATransaction . setDisableActions ( false )
CATransaction . setAnimationDuration ( 0.3 )
siriGradientLayer . colors = [
NSColor ( red : 1.0 , green : 0.85 , blue : 0.2 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 1.0 , green : 0.50 , blue : 0.0 , alpha : 0.95 ) . cgColor ,
NSColor ( red : 0.95 , green : 0.20 , blue : 0.0 , alpha : 0.90 ) . cgColor
]
haloLayer . shadowColor = NSColor . orange . cgColor
ambientAuraLayer . shadowColor = NSColor . orange . cgColor
CATransaction . setAnimationDuration ( 0.25 )
strokeBorderLayer . borderWidth = 3.5
strokeBorderLayer . borderColor = NSColor . orange . cgColor
glowLayer . shadowColor = NSColor . orange . cgColor
glowLayer . shadowRadius = 22
CATransaction . commit ( )
CATransaction . commit ( )
}
}
func showTranscribingState ( ) {
func showTranscribingState ( ) {
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( true )
CATransaction . setDisableActions ( true )
self . layer ? . sublayerTransform = CATransform3DMakeScale ( 1.08 , 1.08 , 1.0 )
strokeBorderLayer . borderWidth = 2.5
strokeBorderLayer . borderColor = NSColor . cyan . cgColor
glowLayer . shadowRadius = 18
CATransaction . commit ( )
CATransaction . commit ( )
}
}
func showSuccessState ( ) {
func showSuccessState ( ) {
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( false )
CATransaction . setDisableActions ( false )
CATransaction . setAnimationDuration ( 0.2 )
siriGradientLayer . colors = [
NSColor ( red : 0.2 , green : 1.0 , blue : 0.5 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 0.0 , green : 0.85 , blue : 0.8 , alpha : 0.95 ) . cgColor
]
haloLayer . shadowColor = NSColor . green . cgColor
ambientAuraLayer . shadowColor = NSColor . green . cgColor
CATransaction . setAnimationDuration ( 0.18 )
strokeBorderLayer . borderWidth = 3.0
strokeBorderLayer . borderColor = NSColor ( red : 0.1 , green : 0.95 , blue : 0.5 , alpha : 1.0 ) . cgColor
glowLayer . shadowColor = NSColor . green . cgColor
glowLayer . shadowRadius = 20
CATransaction . commit ( )
CATransaction . commit ( )
}
}
func showErrorState ( ) {
func showErrorState ( ) {
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( false )
CATransaction . setDisableActions ( false )
CATransaction . setAnimationDuration ( 0.2 )
siriGradientLayer . colors = [
NSColor ( red : 1.0 , green : 0.2 , blue : 0.2 , alpha : 1.0 ) . cgColor ,
NSColor ( red : 0.8 , green : 0.0 , blue : 0.4 , alpha : 0.95 ) . cgColor
]
haloLayer . shadowColor = NSColor . red . cgColor
ambientAuraLayer . shadowColor = NSColor . red . cgColor
CATransaction . setAnimationDuration ( 0.18 )
strokeBorderLayer . borderWidth = 3.0
strokeBorderLayer . borderColor = NSColor . red . cgColor
glowLayer . shadowColor = NSColor . red . cgColor
glowLayer . shadowRadius = 20
CATransaction . commit ( )
CATransaction . commit ( )
}
}
func reset ( ) {
func reset ( ) {
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( true )
CATransaction . setDisableActions ( true )
self . layer ? . sublayerTransform = CATransform3DIdentity
haloLayer . shadowRadius = 18
ambientAuraLayer . shadowRadius = 38
acousticRippleLayer . transform = CATransform3DIdentity
acousticRippleLayer . opacity = 0.3
strokeBorderLayer . borderWidth = minStrokeWidth
strokeBorderLayer . borderColor = NSColor . white . withAlphaComponent ( 0.35 ) . cgColor
glowLayer . shadowRadius = 14
glowLayer . shadowOpacity = 0.60
CATransaction . commit ( )
CATransaction . commit ( )
}
}
}
}