From ffaf968940f92e9d757e4b87b40bac1b3bdf9d13 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 8 Dec 2025 20:50:34 +0100 Subject: [PATCH] VoiceWake: streamline chimes, default to Glass --- apps/macos/Package.swift | 1 - apps/macos/Sources/Clawdis/AppState.swift | 10 ++----- apps/macos/Sources/Clawdis/Constants.swift | 3 --- .../Resources/Sounds/startrek-computer.wav | Bin 37706 -> 0 bytes .../Sources/Clawdis/VoicePushToTalk.swift | 6 ++--- .../Sources/Clawdis/VoiceWakeChime.swift | 24 +++++------------ .../Sources/Clawdis/VoiceWakeRuntime.swift | 6 ++--- .../Sources/Clawdis/VoiceWakeSettings.swift | 25 ++++++------------ docs/mac/voicewake.md | 2 +- 9 files changed, 22 insertions(+), 55 deletions(-) delete mode 100644 apps/macos/Sources/Clawdis/Resources/Sounds/startrek-computer.wav diff --git a/apps/macos/Package.swift b/apps/macos/Package.swift index 1289345ea..16e797344 100644 --- a/apps/macos/Package.swift +++ b/apps/macos/Package.swift @@ -37,7 +37,6 @@ let package = Package( ], resources: [ .copy("Resources/Clawdis.icns"), - .copy("Resources/Sounds"), .copy("Resources/WebChat"), ], swiftSettings: [ diff --git a/apps/macos/Sources/Clawdis/AppState.swift b/apps/macos/Sources/Clawdis/AppState.swift index 1e5076bc8..b17873434 100644 --- a/apps/macos/Sources/Clawdis/AppState.swift +++ b/apps/macos/Sources/Clawdis/AppState.swift @@ -43,10 +43,6 @@ final class AppState: ObservableObject { } } - @Published var voiceWakeChimeEnabled: Bool { - didSet { UserDefaults.standard.set(self.voiceWakeChimeEnabled, forKey: voiceWakeChimeEnabledKey) } - } - @Published var voiceWakeTriggerChime: VoiceWakeChime { didSet { self.storeChime(self.voiceWakeTriggerChime, key: voiceWakeTriggerChimeKey) } } @@ -152,14 +148,12 @@ final class AppState: ObservableObject { self.swabbleEnabled = voiceWakeSupported ? savedVoiceWake : false self.swabbleTriggerWords = UserDefaults.standard .stringArray(forKey: swabbleTriggersKey) ?? defaultVoiceWakeTriggers - self.voiceWakeChimeEnabled = UserDefaults.standard - .object(forKey: voiceWakeChimeEnabledKey) as? Bool ?? true self.voiceWakeTriggerChime = Self.loadChime( key: voiceWakeTriggerChimeKey, - fallback: .system(name: defaultVoiceWakeChimeName)) + fallback: .system(name: "Glass")) self.voiceWakeSendChime = Self.loadChime( key: voiceWakeSendChimeKey, - fallback: .system(name: defaultVoiceWakeChimeName)) + fallback: .system(name: "Glass")) if let storedIconAnimations = UserDefaults.standard.object(forKey: iconAnimationsEnabledKey) as? Bool { self.iconAnimationsEnabled = storedIconAnimations } else { diff --git a/apps/macos/Sources/Clawdis/Constants.swift b/apps/macos/Sources/Clawdis/Constants.swift index b3e8711ba..e703d3ee3 100644 --- a/apps/macos/Sources/Clawdis/Constants.swift +++ b/apps/macos/Sources/Clawdis/Constants.swift @@ -8,13 +8,10 @@ let pauseDefaultsKey = "clawdis.pauseEnabled" let iconAnimationsEnabledKey = "clawdis.iconAnimationsEnabled" let swabbleEnabledKey = "clawdis.swabbleEnabled" let swabbleTriggersKey = "clawdis.swabbleTriggers" -let voiceWakeChimeEnabledKey = "clawdis.voiceWakeChimeEnabled" let voiceWakeTriggerChimeKey = "clawdis.voiceWakeTriggerChime" let voiceWakeSendChimeKey = "clawdis.voiceWakeSendChime" let showDockIconKey = "clawdis.showDockIcon" let defaultVoiceWakeTriggers = ["clawd", "claude"] -let defaultVoiceWakeChimeName = "startrek-computer" -let defaultVoiceWakeChimeExtension = "wav" let voiceWakeMicKey = "clawdis.voiceWakeMicID" let voiceWakeLocaleKey = "clawdis.voiceWakeLocaleID" let voiceWakeAdditionalLocalesKey = "clawdis.voiceWakeAdditionalLocaleIDs" diff --git a/apps/macos/Sources/Clawdis/Resources/Sounds/startrek-computer.wav b/apps/macos/Sources/Clawdis/Resources/Sounds/startrek-computer.wav deleted file mode 100644 index 395c360ea8228ce9da122ab7cf1ee7a925133bcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37706 zcmW(+1DIS(({3Ei%+4ydZQFKob7R|fZfx7OZtNS|w(ZSkF*s`c^?%Pk*-0{|Pj^=r z-g>M0uX?p>S6_-yt72-Vz+5JAzXdzax|?kqv*kBpOFertUpm+mW$+WnCC96 z^1l24yAsj|^E5+kQGVD@WmEy>LxtfpCknzW?_tb$_`EHz$nA2uJO(rVlorgB8Rn>r z>Y(0eFsh2u!tVSaKLtJ&SYsGw*ewsk&o+5fo|V^Og+5q)A$WQ?DgwK&iE5y9@RkZN zTL#4B9eGuLlDFhynCGcHCzrs#Q^3li;T(#gCa|Xhs3jT#e=85`bfhCA(R-NV1?+1j ztn96PB~QYi5X|I)yfeetzffOzzAnhEB&vckq7a;rB@fAsAi?W$1FY^j{OuX+5y9FD zf}HZesw%Gxv zmP2Gexfg7~h7?d0&pVQ3h-=vuMxk#JSVJ=51UBGN`%MQCo|axe@Ms4tu6clnDAk%yx!2!(|cmo;n%N;I&wQH4wkh z0Ohl?9W``S=LtVf&L|aVRg^)zVnf(HF^gu@qG%*v=EU-IXek*)ijgO18n5G;ECoqW zE|RRWDqjrpDTK0;XnF@<6yw=IJ_T(hhe>AqL;S6Z;D!f9xW|c*CT!CByYi^ImNKLF1hfo$W6AzZD#S_sNY^WeQitd3| zHpPG9i5!a;<0W#hSRnI3EK3jewGn-j@5MDy3Dv-h&_%Gvf2Aw_lM$F=^M5wGSH_7_ zG7{g%(Wt#BEJLU|x-Qnp1$Z*fD4+9D@?V?W_*dSJ*;aJN0 zqONR%b7H?NCYqvmcnYc~=87D6FFqv~i1g?do{idxIU)rvP1>Rw;+K%bm1MyQ> zq6JJd2m031XZqgm`2t8IbQg&inl^^ zNCiltIhrJ=3LBjziDEYYiH6~HQiXUk3}wQVP!k>}J`n}C;~QmNG7~)$0r5-@#V7GH z@rbX&Yw$+#U5rLk(Nc)SKX^l#l8!?M*ai88JdlM!K8KJ;u10=bS>%?-$bA&c10tFH zhkmiOaucbIj_{)BCq6H>iYNFXy2&rf_hggw@N{@987@+ZE@TC+&f~>d@=#i=9QM)< z;yrthw~*|-obb^?xE1d!Ux1C(=b1=r@|1TJFG(d_g*A}@`c9T%`%pW&Q5eq@K7e`s=r(E&c;p4@Ci8H1MYYX zBmR>I+7Y#y-&-NOfYPnib)gF4Aog5f#n zFn^13;N!9_MCD(2I#}2Q@<=-34eVQ>+Wa4sgNzpI0eL#$4-D}b4?qvZ2y{VSL2cy} z=_Ov!_c!PjtjsSPq8Yf1$Ozg$P(DJx#0AtEZ4gy(3BZ@Pkre5{kNhi7Aw%ZENitcK z!Yg2(vG8p2jAsj8vqR9}`2%0BH;()x#H8Kw!5I13W18_a?Y9mQ$ zIZwPsPtXyGL^-UIBm9SKhY{K-Ux`z&(hOoJY6GalFNy3zD$2h^2^TzroIQ8APSfXmJ$1Li^<*u@JS!xy4=C4Ns5*WDFn@CQAe6 z*&uaX67>~V(MQ}t+!C3|B($HWL=DLyF;*PJZO~@1TIRv2LCdm$U3+1CC2<6g#(BhX zafN(CmDp!gg>s%p)FT*o=9}a{q^yhqN!>$dWHnh_ZpU3w3-CY3@eK5d-9n>iT7Fby z1$$BXzp|V%R(y4{)4U`9-ov?AlnAPaWD%!5aj8e#aBGnIs&r1XWc4oU;r12d)hc+K zm6kkL_cDb|CtOK(OS)~f6r`RrS8h_KBEt!yEP5|?(AiJh&`G>7`$+$g{>~}+m`+C_ z{+(|i-2rjDz|TSFvO!jGl1!4zMR(BVPGHeXP&K@iYY?vn1JYY7ZpdHoI}QE@m~V`X z6z6aa{9WWk%j6KW4l=6CfaqR|B`6D^?jdppSY0-e0euCX{0ygd3S!Vg-VHCprnn-e zqHLtLXeWA-3#bqH+pqWyD!}uhPGq*MD4L)IoJtyEF)oeAi1xA-euC17k+K=1(1V{0FVDe9>)KP#c~p4J2k*6ULq5n5|sg$S3>utU-p5xdtCg4JU~H{ z|3A;duS88*2Rv9=ltcDLEie)t&?mGBwE2q|fB~V4&bT!i3=!%E*qbe?$Pbvo9=byA z{|Z$RlfYKmh+5#UYD2b~UtGXLQBUz7LUYL9fHmsKW_TUsJFbXEOYu0-9Q;rr z*$UYr1n+psQ{d)kHOwwWSKLEB5V!F($bWyy&axzaA|A`eI42Ix`Sqm zJCHAxfb9F7$d0E1#{Yz_iXu2YS_6Cj0EmAaEG%Bxd82Xp$&~dLkP#yP$Z2YT-R% ziY$#=qE~#EOi#Co6MQ@Qf)?>saseTzFCT^1;{&1(M3n_-i|hoSui@mDiPjKT`iQz> z6*+*)@~UVPF~vw;hV;k1d3p36_Iy;VMc=^7w1sF;2fXJunIw8a&M`oC6xDE1jKxWL z8-D?u6EDx>cQRJQp|-d-c=W00F77WTi$SCbY9toQyLbq4*;CY*OcTlC4Qhb@6@5hw z(hE@FdQ=+!k|RV8$XBL|b0UE2pbXL@Yv7xxHFwb!{2ErTqo(+qSS1>h!*aj4jZOms zsVwFr6K5Cuu=K`b89( z0rc(=PDEwIK)eyPOQNIr21r+v-?)d=$3Hni1}+R5cvA{oRb~MX-vack zzB~dxhyzOhj<(9RkjWNSEI6}V4_rcHKm@(fF_a3P9e^Rf z5q{)CyjcYiyAr-Dgvg5TqoX1nVCA`xUsgvG@Hsh9bU|szd9h8bhYa;^Zt8C657X@dU6>4BCqC^0#suad~g$ z=4JQ!Z>2dp;r-aq5uc$_rM6ZKRcGZTPmX`WYQ_8lzbCmH6aTA?uYX>;WsKG$d2qiK2*_ z8u{J&xPVecq+)AHXPTe=gqG9bQ4Ym>4`=HlXOHB1HLE^ zzKx%u7vN8IF$=IpZCsIT##13@Sj+eGnz8~ON!F3dxTGA$Z!ldCq`avIGW+KG##tD7_V$JzUX^MRd&?uZl+=HNu2)7 zc%)PleXU>NbIt^`U;E%Gs=blh9KV^;nk+u5@kT8j(;RGAc(9qkY|`DR<}qnqzRY?O zuJ8Q9i}hKCSF4Gt*uBGF?Cvs~ddah2|AIQXgUrt6JpNfJV%*Y;D9_o>@HTTQtBJel z>AY{0SaHmL63%RYMz6IdMhX2oo@f^jAF>;XMf8{_WE{XvSa+*yc&U4f9@NVkOkFGL zndwdJc90FVyWZZ~KH1a#5somgiYsb5qot8ond#067qC=_OSg_x%7b~kl~=c!g#?T25PMFK;eyCj`@SAXE~ z)3Ijr;8JI#sE>bnWW;o`!P;iEwuf5fP~(UjK8+L?VfS+|XXqH-;Po5T)ZxkkK3&?4!PxS@4dcN*8dkHyBM$WSN1glp(k{|(VEuU;pe$py1cBDaUCTvLoIo^7O7xO?&j3piJ8x-Zsy7N@tHgo>J{ z*cbZT)84yH>B4uLzk)Tay!eVIXT)@UrQB%F3G_7UqUHK)?54w*L4N1&@U-{!zW+4R~tNFV3vctI#3$Jbmg}<{hmfj-*0*faG+4zdfNtrj zd~=LmxUc;&cp|)rmsQ7jt9hT%RjwW$7P{>er;|Nvy{WZEVgzW>RjUD7sps^yFkYY` zcAe14@In4w9qV1=IYCFc3qt>fmVxvedro-c)zdtq*&vk5zK*0G^nTPwqC9qsP?z0W-DXjk`1 zXh!I}lbIg$JoIMNDv1*2u3$~8Df+7GzQx8eeB4e6_LG?#>JaY&?|;eyRxNA><842& zjV!)seY(tJ%?_3_Ux>_l5nnUUYcj++5L_7go6S}-dp~*KsEv4~@RDF_dkJ1?)b(k` zEVRIi4UP)`B^GI=d=cIO^sw_jxGA*D9Z%1D?s!!#pQvmGga2CHP*5M>+h!!FwA*!PP46@B7IhK7 z6}}&QYVAh>J*}^f@f%gObA%p)7K~RLdG~w#N)c8)d@Gdf93~k&OS~1dz2dA{I5fsO zC-dr?y$y}6IIpuav@HCDg_I253!bjZ0~QEh4^?*vIcCiFuGad1$BLG#wZYyw zMm93r`3ZW{iKSMid#-wfvVmh+z302O51DrJa8WCjIH24$avLS-Sk~1{ zYwmE{k*j)U&j!^ImF+s_KKq~~nr-O%cs#^CVwSTo?@gcTzw}jfBEM~IGLxL*_?Xtr z*rDb}ot$^(Z#z-!Qt})1^iHH68)40`CbJIYmUdPjqbv~<>|A)4nFwBOpF`F=V= z2PjL&^NMx~`y+F(N4ulFr}3h$^Tul77DDrt4B7-`A{xVbI1%n-@sJEv|5E#thGM+i z+&#t-K20Ah8R#B4on>?zvDI=ZIZKby;dnTo;WlP1#8xcmUOJz&0|t4qyMX6`Hi298 z6Rm{{3gNDC6ZmAjfcBx)$tu~3ZE#(-Lk^|g)pK+ZMAvlg0XL&WWUbOrD@1*Kx4p=T z7AZ*+WtUQeJ`g#aOpeXFka6lSB{d$*OSlc)JE9d`s9MS$yo;RzY?MbVC+#(=W+<=_+p1 z68aT&34YD$*=KFWw~$3@W~~f`dWzG-8Oh$Gc;&RZm41*Z*$}53yD16%rP!3A^SqCH z&MgAj%_zD-$wPn2t4ug5+an*7O3E^2Dw!k}x?i37tPdKg%usvwqpfWoq@kK0&`hj=LMo=GFwpNlRnBQAJ7SRjeK1y-qLuR(oxf)K<$z zc3e1x9V4T)EuIznd7RI^8$Mv}tIQAXI^r`r8Ij6ozS=5mHgY3K zHhsKNTm3A0+8@mcPIVNiWizg-!%A^zK?!a*_G%f9y&;Kk1~U*XPia zBII)J5(NrikyJ)v@tIK5aK#kQ#ky~6kU+o)i zMw|nxVsn%}DCoSfE;>iW4q8Ny(k|kbe3f0z9>8Cc*;*kzgHm3Mw6|Hi+~&BmHdGJO zQ6kCCYTb2SqsM9&y}J5PmU5a|*d2u&HJ@=(t$?ODS*=>mBw0iKt)EZ_BcDsH;dVfD zQUY3eZ8V z#<_{&2sx&lQ0kE^!g01bh}Xoe)GAsFT3ohwd)S-Z9%!7>Q!A>rL);l`|LvBO*_A2U zX5~3lh%-2+oh#x6$)h$`o07Hsrt`vS#W&%MY6i6&xhs<0XHH>YHVZ086_YfQA-9>E z33#fb^o4STER)aJ2=@#tfRf1?dIvbH8+-}d#!JbXI4gZY5N;<+Lxx*Vv_s8+0h^9T zpdzxrm@e*%Yw%2e>_83VmfR|ah(S;Z8;&!P@93CpCB5Q3za-1yb2u}pg*S^jd>3yn z8lm3gB3XnV%93IR|11pLlH356dIxlpRq?%)b|!~zW&J;9~&6LC+Y$J+bDlT zcAP)dCh>XlPdgVx)OG%S{>J)V_efHS#808^qKm%7pB(wrb4B=)U;M@a#@iZ6QQnw! zo;56Q(wF$Xfmrd-=owQtwuy1cUY(HeXIuC!T4^Z$z5d7gYL=XoB4Kwpf+(Jp-j#-< z8n~qWEh%pxop@v1^Bpi!sd2a`s}=GkFS0HuQ4t~E1MQ?-Y>x}Q3!bsc;Z>f$BBprO zknK*p(5j$ox#*3jb7W@UcKX^G6)2tj);xv3c$-8P_6`CrohGMDiZ@4-UcL*FJG{^E zE2~=IP~f*cf>w@b9%Xu0 zULMY9UUEN^-JXsSn?3935qCsrNARb05luDz_1!bd;Q7{sqEkl%?4qU9+G=7C1D>;|dIqvsEOWaxtPWOfXO!qq{rYnK1kEMmoR8Kg z_chv~ee*W(wo>2n3D$w|P0KH5DJ}E@T7R_LnGk*(I%7ALnmSOAS7(wk;)7d(HIR$w zJ$0IvS`~PgY|A^dc6=-_l!c)iXQPY~CVTI;cIU9eP}lN;S1w3XK=mV9&Xz66E~TWJ zSvi2GL-isrE5ZF@DAbuU$pn6#b!Yq89^MH!%_sN~?xx{ZCU^m%B)|wv>JE4wM2N%Z$u!S;^jie(TqCBHMx(ruGbLApY6ga_) zVzju$3Nyqos|+)*meJ62(g?b#uAoGGhKyG>DH)X|v?*DF+W`wT2F;SWWfFhJs<-(& z(($>xicCNp*QGP)SlWm}Jc6-}6hT{(D5%D@6{mQRHRWCTK30O2a|^m>+{5gOcrH`o zvILPejn5if$%U1Ds{5kImGWmgv(NxM1^^I~! z!OCQMiEbsc$R3;^4+w`%VY|Ug>1?a3v9InO_dVOeTSE^|X)>Aipf6~IvIH9Jjw;`k zUo?S?2Orf@l!LC81?;tZ%bo8mc9NU{u8%9?k-Uj!;X`BviBaMes@_+7s43K0G>VkN zbD(E#sc6NE^4abt=eX^0GP~{E3Ou(+0o+v~6h-pWwaO87yINiIs-2W%dYSYkOMySS zDhjdXFy@c*+3Dllb{Dxr*=z9rpMeFXw6@Y&8Kjn1ETycvR=KTaP@-vFd|5F5!mY~w zxaXaJU5NblG^eyXmvMUG7fc+kpjsiU-nbN^13h zx=%B-jp}UWES(OFY5=;%8bids;ry{X*TnQ^9^eTJz(wGbNjjV&a7&UbWibds4@PG&fzKeUouD;p|v!c=tnhIX-n4O z3{Wo+z%@4DrCrl5Wv{n-*nOS+>;|tavOqs#VO*J%pefXoT1mZ%-blNq%%jl%1KoVb z?S92r66Lf%!yypKH65jo>5O*2!3ZhX-+dzg)YGl_&fK2 zz0X<+$Ou9YE9q`?Te$%i5Cw2gMNwy~)73soKDDI!Ky9vJHI?!cwH0M~b@z)q-F@ZM zcdywS>?U>!hwx6~66`A(f2Iq-dz94@wXb@jngHH04<w`fml}a9^}C@oPVG?(v1%jNp2PQ zA}da_d3G2T4j+s5VRgDBXKtKnmHA1;ro)72P&c(N@gzHD5Hz- zot*2mSHAg|YtRAe5s`~&t8i1h5`8XCgpaydOHJA(T}ep8{_vUR4yUIleMBKX$vlb+ zdZy9YDAvC?1)QFhgAwf)+G#aJH9VQ{^~8>e&sh||8@z0_(emrjR^6b7+%#_B8!}5o zY-~40bvz`8uZL39{Q#eRQNiSYg2!bqzAHHxu0SW@-NCKFrSy|Flpo|@JZU2L;YMx> z?S!AHoK2%$eIIZ*^uWHZ)WHXXx5GiGq~=R*oOn;h>aEyW)1%&uNQG}YOSQzv3R(&_ zm|pfDRO>s3<#%H>N=nXQj-~B!;o!?aL9|=$PQIDEoeuMT!}qyYpX{~7d-E=R7iD_Z z$x#Z5uu+#l8#_T`N;>OnxP$oO)c%t-p)ua?>4n;`pXvjnx?47UMyVBj(AyE2w6OOi z$`C%l`}qrcuYxu9K*ODJzx)5mjE`vDSxajRuBhIz28NOLF{+(WPb4V4eH3@LO5pBM zt3A=+ahE9D#P`JWN#*ev5+B|bIthJ|`OKohVPvECDT!t~wXBhaX&<{h?(Xlb2l##U zL&V>Ba8mE!Wi(GT37!hiR4S@Tq3%hy_&T+sXz8v3yxEpMwSLQzzGK=UIZtaCeN3+t z>KMv_(_z)jWg%^_`YMzzaj}z)nD#YuwKBu2pgrLw_?Q2x?!)Dc3o#pvLgp5Wlh)X4 z?z9G|CF%E2r(kJRSbYH{^>(rRjGE}H=l(HEljit+Fni#f z%uVN6Bh6B3F8>;G+-i+hXm8w0iK*;J}Y_dD(7Gk#U(_4(%3Y^!#XRcnV*j_aFuAO!7{|cJEB_il0*6lRn}2r0ecS zw86=14W%t2RCS3sq>l8r)P~sa?ZI-F-QtfYv7-!XKj~g=UiAH#0qQe8n%t+a?9PdG zgXNt}p>>Jx?9rb45o7db-fvNtyodNTy8)^t`-SrbTe9=$rM=9u$QSQ8y$yb&)HJ#S zM|R2mO?sdz)@1vuJSiHRyR1UAfc~0%#Gi~LUkBx+yhR&9f6UWBeCV{@I@~8P$ca;b z8$+}x&&a4|{>o&fs0?*DH=%yK8j5sw*==}7<9hTo|9#JR|9*cHrM-1M_&Bs7X+eD9 zWP^7i#Yua;Uu2W0N#5=HXX7zVX}3wP73d${7~p|d{Iurkz118>qA%W9t#qckQd=Gi zjS2h;cQGf0^4RNW8#wJ2Q03r8I`uoAOP`VE>|tn3_<}voYHJl0E7VzfO(j-osCUyh z(GIu>xgmY_zED=Hv{TI<>ikBJv}kQD{iZS_tNxK}!UOOqe#(3oT5ip^@0*vMY9zIO zUK^pE_k8qz)$)@>`GVK9CIzMjtC-u&bru(!_542O9qOb0-M-dJIsVaJ8Qz?fIiXFU zs@;+uM~w|5>Uz|ih`nB}kHTf_DZ#nHM}fo1Cqt{*b&^+2VMO~|MHccdS2p92s14Pg zzTt6ptW(r!CC1aA%2pby9MBGE1?hI^;K+^?zTAyxb$Der(VoJtL%;bL`GF+qtBqLg zt!k-H$v{5J2J$ITFa~-X8O62g z>SR)sf3)_RL(Oa9rzU|8%l>4Aa$3*qU2c?D=g~sYCvn8SXtuCyYl%I`EhO)gMG9BB zu6gEZ7sw9W7R_T(_8u$0GY9bSU|t&kMR$@1WR23DUX*>HyXB6oCXPaHPcu@3woq!R z$CXqhEmTJREVDf`yxmmXXY3Teil!(BLC`7G_Nqh5!wrRg&<(c%It%W#c zMOQbCoy}@(-L;B1(6ND&XhZdkR>_moQ(jHN^>8NiFQ3YnLhnUI;3Gca8|WBM%htP_ z?6+on>xzBLjeySBI8`?qLUkyu@k#qvU5+QQhwcxixucx7tiAZfck{t;lR{_pormrx zHo<+u#*-y#dabRV$Mc`(tKM1Lrkp~JcqX^EeH?WAp3}|A>P7%6zsBD}-%k$cW!Q%P zA@4{YI!1Y}uF}#1W-CFbV=jk^9Xyfsbo)3VyS|gdt>w00i}@7jWEp`R{FZDXZ|EH* zNiDC%D+iPwv=LzX{?PZ(hFk6tx4qNe(Va_9B(=De8yKrv(l+<^F7{R8`%tq#%VKpqxF3)8o# zgGeb|w$3f?wsw%S%Sz`?aEV=!?ULWnPpRNFU{C32Q#GZhg0UR!!)-h{)Xz{0$Fvk# zW-bX&=V@fnY84#lcEgp~5pxx9s}E5W7D4j)TY7!ajZxKq!5a%VBrfVft+w?xG?~mG z^}>$=pI8dK!@3%5$+LsTopSEVcgA$hhVIZ_-YgN<(PHt=nC%@X)|eXg82#+4Nq@0& zN{qGSk92J%lba?uLCp6wAz9qpq=0X?p`*0qnQxY-wdl{9>R&wt*^AIHv4)16@5ybQ z7dY0@lTX9<MtBDsVeDet&Pt$EI7 zGF`(`W4byZB9gAQ`)H-2E7M_CH#J>sIovdPEK23;@3#9r$?l~2?8XVYJ63BhzlEzq z9sC1*2=@;@DgPQ+Yl;Su$`SpHX)FqD@g6756W4_f>+jIV-_MhJ>c44<#J8cX-ckC2 zP$P%>QW(jsJBsx6@E*YnQL=W}^GRk6&*gD?9o#p0f3PQgL^g#dhql12pA4Z1p}9(3 z&mMlts%hx4F~%J^T+<_$)05@^@k+}k|4u?-p$sIW!tcz-N@3M+UJLEUFO`zc{or_v zef^c(;+!U9mU#BCY~;09A#;x3-e+lnA`(LxW(g;^h;>mS@2gz;kaz<$HybbUdvyEtEjFjUN2iSwWe~}8I z$#{CqE#s*(0?+fUqTR!O^CfhZRknAU7vvHBsM3Wk5-mJM^jv1+&=cjZ+9sGc*wE86 zV!wUgiuPZP++|k{UC}mqI$C1_Rq!OO6~AkrMFF)e9l*b^Po%f{7b_5&C#QO5i#maB zXm(UpdOYv~HHvOUawne@wjl36f0GjHH)@4c&(_Wcu`+8E2? zSOI#StARerGu`EA6no1SD?jyYbb{=np7hKmFYPjHfD%B%!;j2S*hCM)$Zn`*)60ue zXqzu{L>F=kHB`?MZcPdJ&H9cVjtx>4qh~WNXgPfSqr$#(^t1Sn^|gy7hvI96y1P~F z%`Bz&dd%F25#9m*%-%Ts$XXt39_kY~`=@r&7?DO%@do2<433gUc~3wO@_XSM!3BX| zq21wQp(4%I&H@sn`7Kq+=X95b_9SIKDoBza7l zc>eKprn$);#gE=w4eZR08*X84cNYTFQAAtsJ*|FGWBrAEC81ZQKg#QD|1&x{=pwsE zuqFD}U(DN1yXU7dSocGh)DU-7VyeUqp_QSM;W)Wmf36krWr{8DU#r|w-qQnSri2>7 zpt&qy23m`3`fB~TQ8)63|E{_ZQ>8BAq3i*}`VWvnM%E8^Bi+d$b%7)wxV^SosVY7}mwXqf|E{nSodPc0hl3k`Bh>u*U8AOc5;&y4 zaBAnI`N*-XKf$r~aJU&;9}kCq5Wn7EeW-;&ho{@ctSZ(+^PsuXEsc8M97-9_6i=dh zTdk;0m%CUWo@fmUUbL$4)6O3EFXg0uj~-Q-@4Jx>x|h2uulS5mO1pumYYzxV%D40x z^pEz|ifcLKT-lQPWFt2-%0(mKPUr#6(Wc|C8?W?Ht3tQ7P(7%E-H}C#9pMJS`|cz@&wefL86UOJXo23? z|CD}UWoQ>=rDNL1X?s%1iIrZx20m+7f_hk5GZ4(qC-G0_7XDrD35@;@ZN{LV zpGPy=yP%rBkjGow99OJ_J4`d=30}o5;pXHsMNd8lDzE94@%kiBXHQ9^x%Nz%36V<=K^x=xsZ^J0h0XoW{4ZYNp3t0jyGG zyA-acUVuACAGEukOu(pC@P5}C;cBFvRt1B8$!RjLo!$P$a;^yX8?um%g zii(DGe&lj*QFpgY=P&Oq>{c`vE3MUC=BwaO(S$^X?k8jyT~wdTlLKskCoNUo4D>^U zYy2fNdL;6eXD83Vzv$WZfp$f+1!_UAhsP(aWqZj`Yh2P@wp>$WL30Eu=ksbi`4)WF zKfzZ9y0f*2w!Td6bUUw}pxg~xflbgeJvux)DWhANUUHu#ujbjjg>WtF2e}?ORo8h_ zWuO15_qW`{2I((6UD(^;Y;;6-!rA{U;F|hhNQuA1=Ns>wk%3A0N5oL2zFSHu9XZwW zQ>MlBeC2)R<#eZj^2Sq?ok|?%URBfEH-2GrqCSt!i67M87_|e#zO}Uhw}_>2*_`FkvplbZ{p|cHro_Hw z2id~dO{voIs#ZRKhnR(y_xFBDyxGH z8F|E=7@L%KqsS6WZJdeS>jvX8h)v!Z_OTyh!*z|dZnp2klP`D!v~ql<(D9g^QTOdq z0;e9I`m7kk#zn`b*y>JCxI||}uCiMG$l;dvZ?oHdi?vEebYMHa4>OxYo}j}MLyV;Q z88zBHDOab8NLiZiGbea@N3OMuKk>3wM1N=A&mZpg$SZ8g_l#zT$YwI``&hGZ%n!X_ zauvQI&Egot8P2s7X=B@2^#f%+s(+Z-D1I`&?w@Rb{9TJ(@I{E_KktUhdplF*&&fdg zm{C!+Lhr0hX*#B9YqklWOJT;whf*XD^Q`mt4SNz2@GDOV`{%FW&ROpxSNvKMS{gA~ zYm+qCt`%KAT5*0lYg2Sc`Pj`Iz8f(i=8>J8pePd}>YF023)|`&<7EAH*c#)>PuBl& z%;Ub!+OBYDIy8dIRnH%c2wUQp$9V5?b zD?-Dq%HB7g{vq8wVx*2ri;uI#Q4dot)iSwl)G84Z9beq%qB9NY1#c#~=uh|XXi}f`i$5B0p{u-C z@I$z=w^GDMHkt2=D4e2;a#JMvrbS)CX~N%S1wFUjC*BUXQVz4JiA5add8yPjXE_;- z0m|%P%kVrpU(MpIaNb1>k4e(%lREx{6#Zxc@QQw=o8|oYp1c4pvuh=uwm*5Ys2%M` zvWxGxp5G~IFVf~4bJ<<%t-3v`K6RbCp3BiS$<9z|e9_YnPCkQ`1%I+q{Mj3RLFSTl z_BlJs=Z(xGzPOe@ON?L>?H|5G|2sF*nx>xf7_4mYgUF-4b#Y=wYlJ$UwM&c)AJmR1 z`N9R={oYOye?bd+kw_Lj8Gm!W8Ih5hQ93}=)r}v{nZ!$$rX6+H#@{uUDNXRr=xN6OL=Za9;YpXMO>A`iT*wLtiIy@5!{rp#a67M zb2MRPprbLGa{s(QC--wtRb)>xhZTz4qwYF2!jd9}>wESK^RZ`@D4IM)zT)j1Z+y5v zw=qtRNc_{^!uo6s^w)3(dM<|4P>1FBPLPH(LGUwCAAHvGY84- z@u~f5*bb6Ad4T`66>El}!aE{#N5n~*TQ+rH2`|U0*dOVSkmWS5|A_q8oTqmuyiSfM z7R-*Rl53lhRtvj~Hudz0S&1CzX9j@gOTg~21ByHl(tI8%`NO?+ksC7vl zeRokupY6}ldDAZ{) z6X!z%Q;jiOD!D)E zck+;x-V-O!28z={uJz=Em~WZkBV9h|OM05)#kQo`Da)XNXc){XY9i*}EMx2ix-;Dq zT;hXQ%-t2WjiR2H5qr&c&P6eoH&f@KC$D7+qp|#~HkuDeixip^>5P`M1CdT$YiVd# zvo6hPDc55CS71N(x5H>hwaHMS_gp@C#c8FVIfIj`Cda|M`-eOxhgi4V|B^<|TRDRE z7u|y`n9uXcRgzsazK4Vw1A>+9UGT3>^2OkJJ82A-vqTHJlTd%OKc8MlQ{!iOtWzPC zjUl88yt%0eGUR%_Ig=MxTrV-JuobOV@q#1K3SLvQ|dPzvM_V z%gDsNv@~nLPUv#MF#+2tYi|rRlp$s+7ia(QOV)8x3aZgac)zp5pVG(`W!*658u7e` zXd1|64-Hne)2hB~0FPpict8GC-iOkmi!;%l03~*cb=C~J9IGVjA53x6$tQM;ZO%)Y2Wo-= ztR!tkVxW2*DvQfL@+c|CpBm@c7dDMI&~xQh*%um@4>}(^#d_0~)B|^LZ}~tF^;KR{ zaMzMAv@`TVw?PH@6T5^$@aS%YZ~rU4TYws5I=qWqRnxijICxsC=+|IXTvfP>pe=2J zkMI~d0l(Zlx(6=TPrNT?3Sp+3t;%7|jnt&DfZ!HxUK z_`@n_WH3B5k8JCtv#*JA@4cGN4T!#DpQXgcrYXkCa6=S%oGIOm3wXJV6>D09K5c9%5dY57w+ zhK|>1R5^J@P6St}BWXo%K#_HZ#OR*trJM~?%WJ)qJRs@V7Wy8(<5+whs;`69@C;d4 zKlwyT(~oKxcB@_CVau-IiX^9WI+_~ng3;tMm=k}i-}N#Y29i?)GC%|EMiRlAxFUyw zSuqNHsXRK4bk}F#yDkZbUng3Y?jutE&eR4dYWjqIoYB!z950h9`m$uN$#eP+St|bqY2Xui8WFm-7W7JW3 zP8J34WIUNb2eU))3Ko&z%)l}D7fGZmSRB2>(m0FlO$$8R)uQdCD(BuZg=BT)AuM+X9Gx^2#@YP&GvD#L*kaoyFPmTsgQeaC*|1Ly&rO~t4y z%9ejYn{%0FLc#{AZX04Lsfeben>|}sf{uR2tJe9efpU0LQQOq zZlF@B$6}h=38mLh&?VN=GVn~#mm#vY)cOeR$ad1>v^g(JKgbiJnWzd)RXW<8oucLO zB;=!z7$A;(EaT-XsJb4qe*dR@e5Wdilk&D)F4Dm5@8Q*;1Z)d$@eTNf8Q3juH4LNj z7F|J?lH=rmOpzi`9t~5Yw7^$y)d6^L?~tN&BA6QiklTjnoaz-6zaLdaFjb1NynHhY z2V>x}d@AaQOLCDeLG$oj#w}ikRV4jYS1}4Iq`F|yd}S?+JH}3a7(9@<&>YUf3R+0l zqiK0wXqfBqs_^iZ72TjH41te6Et}2%G|KX>biVdWADCEkp=eAdbKr2T3pQ6Ll2<>J zE5sMkN&VDW;M+V8?Phy8%RkA>qK+&IxLXXI^okbY@7Qv(L?uhVd?Fjb_gRfpVl#Me zwuH3Tr&JI13)gHVyqyQxVfL7QBJXuCy;$v$edT(L?yBq_Z9vZT;yrNrV4>?C2AgACa9ZZYB`@deVm+!@2a=9KxbF*jk3Z2BpfZO!9C?^WY zH2N{wN;|Wm$Ypg#YWb45<}{brRYCoM)aRO~<^@@-cFCM_oop={$p6SP_LV(=j{6}- z>wYMHYKWgQgDwKs`+DZ4qiAC$p`ROxvDQ;o0o$xGSTWz}IJSdkrd6Sl?XJdS+%8vl zp_|-7T`V=r&yIoeFiNI~r(%el4XQ*{mKln^yR0YKuDZ)9;=C9EqEKnvpJ&Khw`4Wp zIKLs*3M!kb{7`y6WSpPGC!{4!!K1n)PD?L%Mepf(HV&G@v*eJzt4gbrsGG)tAyJ2> zh3>R49SCX@6f1Zpj;pS^Fy^=680)X#Z=bD4s@|YEGy_Xu7N|!zK_Y5E7lWx&N>@+? z6a)KbA*q1cU_p>0?&~;J8@_p|dg!wx9GtKhnD+?zh~ES)0Yr*hBoj3-hdzO7vYF}( z?QIQ^mwMBGK~H=O-i)EQLrK~OuRcwW(d}T{Eh5LYgL`Sg$-Wpl)uJpn?&k}P9Z(5m zb$J*5_us%eD#@qgYCJ^NaUk3R>!ED?PKxqBi~!DWwLS@#=T5l`3gt0m42$A%SidsD z%|2XjmL)`ji~uVr4V%Mru+H=g%}ZkBZdqSdmSsTgT0%48>a~Khz7$yu)n+LbgK<6Xi}F|gu?d??M1)p8mg zv{VOW8g&Xv%C(r0chTSJQ4lxZAp?3{9Z-d!jc5j+y-hYlxvUU%q{9C%>L^_I)A*AH zq^mqo$6h6W(n)asmjS^di_Qav7&4+b;#Qz~#bDkpuTs<-NxbA3cncxqm3jq-_&NB&&p-ocf#~>!EZ6r? zKOaYyfreEFSELZAN|#Yx7$OU*W4aREgEhJjiB}syZ_K48Vn$6;<>+U=mbazMW{`94Vt^K+Mxk*+QPr z=)?WwJ+51=j^hWgtb4q!+ zRcrvi?JGaVuha9o5_I2l^a)v6NH~fnv2%PYi~Bz!=Sw|9C5z8uGe|GBz>P}*L##2B z;MYJg^x*!S)slRpeh@ffNM)=i=T!~85R9>NAQ9as-@y&dre|TcYY7rVL;9K?#ufH~ zo0$&GwC}1r*kt|SVb1^(P8q#kjfR)LHRiTx(uu{e^B7SsJsIBkar&4XjtF2fl<>Gms1m^K7h$9*iqQNf_qJ z+sN;))3MCt_h=809tNlxAl{6XJH;3EJ1xlT8%@|ete4ACFPbN}%iQoxm*vvDz>5=0 z7lbnUwlvjg;ZbX$B8@d-_&`z;^Lj=#S#}VIXK~lH(Ef z95Y)_^;kZX1uzm*fuM1f1=wpuYgN@((G7L3f*^$qrymf}x1cl0HmL6>fPr=jt9x0D zs&?SgZqT>XCd`4=;LWMZdKw|%o5a8waZ5EMdsQ~@CppGW8D50lgcoO+u1Y#1{teUH z>04-IbFnsfcH2`^cW_RLqabG7#nbx}CLyc2vvACh8>BG`1YRYnI z0~_yZZv6n_b+(KX`>{GS6@I5T{cM$RO*T%GgJPN!3qAZt=c+SV@8`p;2UZ$hMcotc zehO=8~>T+q*&i>Cfh{b6Dx>f#$N4d~k-?yVObA2kTTu%+W_#lo=qVlf!8W%GY7~ zT3-{Zby}$5i!pvPq+;iSvxXc z6jA}5N@Wm#ismva4R?jOAF+w*j_fHL$Xl|MxMXM1C9E#)1hWJqYCHDHe}Q_?%Klsa z$GTd(TwjfuIQJ9MrDlsq&LR<^i}2jg!0u%Oq3G%iuSS^sh2vYHZCcM5ZI&|@LQOSX z%#b@(7u7-@mnG?J^CbM3Ra6==OuofBvPw2{rplr`HJBIAi7g(9J&5r)gP$=Ie9kQ9 zL~B0(L${abK#@C-DnV*-QcYzJ)cMJD8E9G(PLJ|3uUdjA@Fv^9eQXpRs|(1(&K*$< zgu6rZFHjP8gYH;~ERdg^ZK4#Or3$=*F#_C}c>M=d?CDe!9g3>uo6lU*EVA(~` zJqEIE@_Nt~X%YJ<60VAia-_XA+03XDoDN_tIdmEQS0o1?tET32CZVjks9u5Zl7_S z)DUT%ONb{bI3ZxJHZ{7r_PP4=m$HypDCfx-#L{!*AkxxUZ5d`PouY4|Cb>%FkR4@X z)f{npsJY4}%{n{%oIG^DxC{2Iz^3Z@D2cx+-I9)l^f1-hK z^jBlId5OJ8*5jr;h<)#50f)ZUJkhwum(o>WPt_o)bW`WFW02--2Xpa{h+9TsKfG4F z3QmTz@*3vKlAsQz(BkGo_QJ_zmsTrj4(t-D$wsK|KC((1rBxlcL(`F_>=)Sw{ag(E z$UV%(pjzD&$7DUwOpiDh?TzXmw$1zxXP;Ic6)|9>okK2cop4l5^H0wc(*^qMB2gFX zSbzE5UMeo|$L_lB-Nq>W#C~rdLZo@orou;dnRDE0Tv6rRLA9OyvIA&3PsMz@ij3iNp&A%SYsv^2398j{+~p7AIyr8{!{f7u zOvTVzeSpMD!Yj5_6l~(XOaDb~0J%O;CU8^Vw!^QUup?)AJA6d&Uprn-nt_5itnWx{XowUYG` zC$DhH+;F@t7wJH7{bp(FBmWy*ysBa-cDip6iA>U+m}QQ!hMI%ub8!(fwk^l0M=~?` zk^9V8*LCBQ`e|=46(==sg9GndZ}YL1N3zBqHC40RNoe7 zhk{#`8JdfwV1!K-6`Y5n7k2#bz=rL_ zp1=~h9p7d+X{Dz@`KJjFaC%nN#n zJ=N}sUGQ-GjPr|l&2pX?cSp8ETz29_WzpRE>SR(kk-?t@^}!s^AvZp0!IH| zbb!&oDvakmTou3*H(LIw{+07(BspSuT^X$r{k&h5pbcCv9KG_d% zV5rdxtgXhNOEb|&^#(ifZ+3}gVh`yl5~ZFv*~DzQ9t5;&$g6B8XR+hztct6)_}l?9 zly|lgj4Q}HG?G13b`aBs*h55BR>{n7j^g3u5uVj2I)wV%vQ;`zM;Y`j!SCn@f>yyqVK>8e(w}U zF6Jz>SQXe=_W)0B>kC=zjIoPg-JfhH+D~;}^P2l1p2ObqALl*d#Qx4@+-rx-GPkw!j=&?zl-5wHU z!MvYH%eyPOtFyAIgj^sl;wgy~m9e~rQls7p@`-; zlIl?LSD}?eqFr42$Q9<*QQ|5%^=VL3O;8z}QO-#6oEM-M#5b{oeCN-Nt8fQs`wsY} z@nDSJ2Tk-W{lQ1Dwd$MLCFA5V^?_(`%^fK~L+z|KU{8>@}~xqI($)3u4^bgujR29sod?uA;uE$00i)_S8EdE{gY4shOr(9lh{ z$7<1*S207(%%r{D%TA|0Bg4~O?^1F4ys_H#o`)=pGs z%h)@d71+UEkey^za*C(3@|(5E3#XX42<3V#-0f>rN5s`RjXdm`2E`R?{21^WKVrp* zg7-Hcj=n8h0*AX>pF~%O)hbN5z>uk7WHvw36UgPIrdt{E1kTmK6DJP~niq@=;LJnQ z%fs1danFAt&`zZ?l<5VVyc|8nlISftKDa0_+-XnB80E||a6iojOWnkt<*Ln`x^fPg zz`L3o%mo}dKje0%iFD$=Xs%|G#e9SL(9m=g$bMzSVF9I?`l=?7Gpw4i0F}6|>Nlq+ zGH*5*#mhmQEyC8DiN+^gK=_5JPol2!09B4+^rMj)Jpq0Lk$e=$rj=w{5r&DOg zy$Ci4Zr*CDr`;oX1~uve@SCpFKiCntVLriO_1u?5q-9sIck=5zY^PblH3b^WX+D=- zQ~SZ!NFdM153a3JMk9G9a5(U&Jx z{1Goin%E};%SC6}o6ll7NDci2E7elAQ0}m2IhR#{!cB+F4JaMp9Bd%*vX6+8_4Ro0 z3_?(my^R`G8`g-r5p}mhzPgHTi|F_bsIr|9?d@To;pRL6r(aF_kyWD)ki#EhY%sI3 zbIyJLVD#iXsYs{W5r#&kbY|p_Y88)qY>hay;oe8)kR;YyK?wi_dR!3gMcPe z#))_GBjfc9MBRMWY1eAA3aufJ+V6tZQ1yz}E6_LMiqXX!Xeg3aJPX_iF2cSt33ZHm zv^76t&NpIoQt(%xw?K6emD|y33`sPOSsD0C(J5HOxuP?$A36dVvcK6TE2CAB))e)f zy>cC%<__|Pd`4CnS*)7I1(HKO#Qv))BH0Fbsxn}H62?=EevUaelU>r;B1e$NEWdHp zxL~y7mBISI9khbQop!n&cbl7yF?=^;P>44~4L`+SUDRhjbFrz=o1hi);9;8Cr~QNM z=g>g+gdgcByNoREUR@P>&@Rpr)UZz>$K?Wzp}28b*8%+^1RT$RVB(FMWZZF0HN!x& z>Zpp+Ze+W|0t0L>&Fp@P*>Zvs>IwQ#90y&go&S)mWY%$?GtZ-!z+AeVo{-f8b$yNO zBXowVn)@>%zis3p$*GfV?Q7!CCR3SPJp#`sLYXjQ}~f zhT0&RGdHj%cu(~*zqwnwg6yE4qBhHnqPJbsPA?@5z-^V=bg_?WBeIKKghoG^>bgHI zXk@oio0{wsWu1Qo1z&Zxs)+SP8VxPUuFE2*oaF-X`Lk+*T+D9P%c^JQAWNNtn4i0_ z-2|fx%955wd#gT=fAvW+PPByUTN6>((|~nk9Hw(nMVmnVo3+y3>zT zF#T2~bZuF!tLV{cnlmPVt}og#liZW6-XI3QQp>bk#s+c)zKTu88BcL{ZuUX8QQ!4i zb;bTIu+&~oE4t6SpYb-RK%+Z=ZY6&OI|MU<7WLU0YmP81$Kj#4 z8XOpm6#G;(J7oRg{%Vw<>GVO76TFOV_G3F%r*RGN^sq`JyOtF-%AT@^O@evUZ)P9& zb=MPK7SV1!DAaR0uY+Z!O-oss+^vmFYPTqWPD)K=f8=8>AscneYQ^WH4zLxjij2@` z%s@3T9wfI5WT7e!{!AolQ9Vf$ktn*N7Wq|WM6RYNId6Qhrn79aBjT^-Sl8lZOYES2 zn&~`s%qPg|T(Ikk7tqPIcb2Lq=2Ul1R|9@P>RDTovKMOxx9E+Fs5%o!vPG7K_U zEZ9!YW=D7kYFT;EF`yS;t$y=o_n9)5moh6O%kx}4H+H!1vYUZ1DJSh!thV(ho8`=Q z2Jn@hlNN`UWp`kdK5kTJ51iVzPe;0{d6pX(sewjLLC_-ltH#b&v6*>1IYL~g5N;C$WuJ4J{D1$OyN6$)SOwY72RWeCr1q z>fRO7+1e~~`}6rbI=OWhG9Q$lTgE4NLO+mZPUk>%Q1_C^GQJI&i{H_IryaYa4>->Q zo$Yh_nQ_dVz_OzLl0bZ*4NY~L+3#f=p4E!u)%8;GpFDxuYBXsf4VkF-@^jF!^p?@^ znP#SY;Oe}Hj#6$NV66?X9h_UiS#m2cW1V9K ztu%=>C#jT)`~KVtu#?Ut|C8VbeGF@JTXZpaX|8b9=9iq!{*A$CrNBxqOv)M)zz5z$ zpV%)0C7lNHKAa2@I%+!d8pvPG6Z#)fUU_fg(UVxdylw8C7&I)QOL(ed>f*<#^?1w%00V8KB7B z75R}HZLD`Y!|Z=$d%o7XX_O~FoJAX%4hV4v_!RO=rV%sj;lZ}pU8FWTxO?MhJg5Q` zkX=P1+X_x|PSB&S;+|vXEyO)nLipdR&|`p&O!as2%4+eJ7_4rp*p)< zKeJhItmw#Yn(K`YG!`y{0&qcx3K!ITTHWMV%^k*2mV$0^Ep!JN<&>}!oE)GOHgH`u zAJ8GHlMGX+F*%*=N5aRtxfi;N^E_Z1g+rNAO5L#kv#XI3uBx6v#tQkLoeZaU6rS4s z&Q~?e$n08gbk`4@k|F_h)D!wI6^#)wkKgBQXe>BWKg3pae<{aGk!7k3i2Ju#kYt8S z=ZCz^N|<+!_s=rtP-dS$|F=zy}tSn}0ecz6CMv&5c4DBinu}LF$X~xhVB9HTj zD!`6nrAs4=sI#;bczA7fI&n~BA-kE$(!yUO<_P6xtJy932+?C}wgoQK6ZU%%3x-xu|4khJ-700?QpW?80xr1` zmCJ2v8~PCC_e=-Pmj_m-tOf;i3)`rFQ#Q5S&pZzAX#10Q`dg|MEI{|74*kn`=H6|+ zQX~COQij;i$Pyl)#i2e}V7gt+`4gwVZ<9Y(cHr;17ad?W(0Ar&>pf`~oab9@FQ$p8 zA~XX>i*egZZ{|=F0=a{SRTka>1n{CD^j|P1Sof)PuwRpFP`xgp?u-69i?PePY*ZuP z{+AP%Em1@5DPJne9vbsZ!?5UM)Urllhuc8r)LCh5Uf;ZLUgbY^6EV=fj*M|XbO=bx z?i)S8z8uZI$jVF=~@jfG%gFL^&tDx~M|!eWztBie288tf~g1wAA zu2@hu%A(gsck-GQ5XXHBoJM2?by-zhwUF!1&X4G6_Hw7X((-`?R4S;DQJv!-*7!qneG2oYxAo)a=P=? zF3(Q7x0tJ;7cum3c}=gE-2>B{e<@={`5|i~AC1my1IWx^UVAOHYRCZ^H_bI{x>>{= zCra8~)MIC8u&2I(ZY)@gJ>#Lru0#?d0hEQ_v@3G6&qUV1n_xjy-xcfR&gx2nq9%!~x4Q(A9OQw; zJ3hx%%{XRkGeh+tI|X&+bipXOk5;9AKFK}EtdE{I9aXAezb_3tF#UP!lafHFg)Aik(?C)WZ5et5j0$l*LqU^aCwPJF!XN z_rV#ft11TWVh2@3A4aw_6Dx$3I~A<>J;BXu2kmkOl7K8*Y1V|cA=Pz7l>`-A3RH)j zenZB2EqzL6>bX!?HdgmlP^W@^Yd!iSK1KJmp-^z#kXlVgz3Bo?hK_6`asp%2dl{}g zs4Ii#h`Q^%|IyTR5vx-JxED|s}3NeiGJ^uHBw7I$n2Ha2HQYv#Ru>uCv@}BtK~Mv*2xw zBRq*d*XLzUvB&va%u=VxJ(k0ed=)#2{x$jJT!CmE9N#%d332pyt}A3%r5a;hV8c88$f8Au+}@9Y;#rjId7TF6l%v+RzZNHMGx zf5i5obI)y64jIQeD!baHEGSynvwnO$^NN8^JDuVA%tix)gNL^{GJ^ zvmeo|WHN}5lR()z3kAegq11fpK!>%KnODGjss-(Z%SMmIrHKb)A);H9MIl**Ce%u4Si{*T_uv z3*9SAlEIYoeJnd^Dr22ZPB(EzEo6PIbEu|_QU!vsfzom(oy&gdZt@Jc4q07|tje^f z(=t#hxL<4}J9#IwteKTRMt8Y6q8K8Id3vU7CcuN`n_TJK*LZU|FOVK__YHEG58yTE z1brJF)r#=`>=axPSAtRYK1BAntlF**JXw8rLeM33F1!if)f(tTFY9r}e%CYWHn_}Q z|AfE^5z2&74{-@P1)?`!B3s2u=*#vWfpftk`n*})bHp{9`NZeoUAw<(M4QpV*uw{9 zA9~h2>8frP(|5s!nHngjTbcD-Ka8K~EmTy^AuFMB{4Dndn+5hb%h)YX5G=r>R z_p=v@ztwhh%K1z>vN}eLX&Irkl)Msb63A*FRs;DT)-r3l(Sja_wsVUZ2)b!eISaYi zH~g}hVhWy>90CieTQEvQgGBzx=w;l69<%_-gRwD8EP$FigIY#2ny;Zn17k6jQ%rI0iRn5$Trgvxr^|#+Z8KERNwBMldy0Z7y^8+?59ufJ{-5kI zHW9DnaqVIgjlE!kmSVm2bm;p}qJl2vS$LBgSqIak^kfI0ZV9BK)*b*sN?m;HSt#+1Oc!{o@015#5{{TAQsX!w+?B0nrcy)X8Fm znoge@C&0mUczSvm6{kAzMAa4*$w5BC$OZQ8MmiYlO%qWWgs=M0y>4YE!Hf;zA@omO zMf_vi&Ujp{$;iZwF)Dy!laC&ekH8_l>nxXX;TCpnVFll8qPOtcNC_NvUk4P4&@}&aRnw zKKY|~=|>1{n)YkDY@W1^C#e%$$T!{1qs-7SJI#-_KjzEVQO(n* zL=W%x<+Pv6+D&%gU+h3J|YYo^+&zQ#}Z zHN#)j{42a<=xw#%n;bt(^a{V2wohb;9vYwbdr5D8@-8Ag^##v)dqtwlzkpd`gG1+$ zl1UGKW=-<3{?Vt>XZ0`Fl|5%88;8%+t$(%taxC$k z`)FDcm0(nL>ZRmOp5qL5w+bJ?y-6G5rUmPVY>n<6@eeN&xDz-3*UaFZ(3WYIgif(N z-U7Z!%oF-0WV?|I1jXpM8^I6Z4>H`1%q#ZA<%z!-xXfK)`@$=k=Y1+J!S~CxE~-b^ zSG6{&YQnIT?=&tVF6x?#2dc)`O=< zff@-LytSPko+@c(L=Q3ECbjsv(py{Q3DM!5T=|{vNmlTHxg`8rNK;zDzb8I_{Cm4> z`0#XPBTCBNN#|08*w0Er%e7uDNtu~+NG*s=NZUBHmVG$BR(zq9_3DNvTdF~!*%csH z{@-}M(8jK!vWqV+xr=XuNOp&%$roLetxVh%U*BI@&tdnB##VklSWJRTG<(Pj_hyxp zGCny8&hTNz+>n1lUz(F-q2LfZKYS;R(HC{CSff(d5^J+7oArg}a^5GGO?f4z8YNKG zugQ)emwg>-g7;WOd^YpF3pAjmT~AzJt!8F2>1MC)r&LGeX|)3nJ)|5ZG*XlMrUj2_>Fm> z-|Z!};jBn4Px=>wt5^p9gW7zud!2{Fc^Mn1Agilv4(NFNxNEQRNPi*=jS9?bAMj6e zZljm!Db5*IPpFb&P@s#Q2kXmwvmml3%Xtg(EV#|TP85U!y%~B`E->p` zeNkbYu0Lsym=Vb3)TYa!4(ZC%8~J#mQuYP=g;=F_ps!|aUKafeKBB^LLv|As-P|f8 zv)q@SFxZU7U~L8+0NRDt*vfb1&3lyVxJ>A04kQ zgJY9bt%bs=6I_S0^-+2W-TW4-f6#BOkh)<1CVjLyJTQf*X-+ayt0uvJ9jQ6AMn^%E zuZ5j%L0JbqiTi4W*|#p43N@dt>#?F{dwx@9N3^>`)kD3$ILTvC_e6d`?S{6o7kXOk zcQ~DHJc9!<3m?Uglaq*lq&4^) ztX+bwL*MbfViV}%Mdcf~#gh1H<09`4eN}#ySLAn=qT+W<7lg)Ow0VG6rPcLg@z6;t z%E+9kHSt}Y!Hy)!sAR5%PX4%k&@PXBL`hV?KN;jj zp{H@ZiaNdQG|oAl!1o&M%!cMg_Citd#wm$fNglP6WZ`eYH>+iSC9PFDu{3zf{znYN zE@&aoV^zZ$ts^zywmk;!>UDI*PT}?OT>fAa(3$X?6Bld+jY4Ymmi}c#TTht{h08i; zyzO;Hftf#8|7RqiN*-VqE3S_@mUBhjfG+$#-)$V{q1exEBOG<97%iY-2&L)ZDeSAa z@Iy4SY$LPDEqbb4q)K5uUrC3u+vJ0;21QwEatjLOc+l`)p_eV_AEX3)44V)qMZptP zn58Bqu%oR7gF zfVx8o^yB-BRF`)G<%7G)UaOUv229CW!sSoxuca^YN9F-fC-bg6?{iXmgFIQs`W`yh z6{~mo8>R4IAvw${5Pre4id_l(OgfX2+TLX53tu1hxAmVrETvRZAKzG3H>5-ay6LDr z!G98$B>%4Ox>|)FasSSmi(lU2Nl#=8t94kfkRw)-d>DVwo5v}_tAq{;-xU_8$|v+o z^!S&nBJLNF36VwhsN}uLZA{!B=6=}^y)(3cT@cROFcc%F39E^RCb{WH37$dN$#q{zfj-|wVb z$h6eeLaSicaWmmsVjl6$)iXLWYz#l<1QMPlB>AhG^CAa@zjXglX_6+#ljOs6WLV3H zgs@R8t8aAt)r1x*#{E}#om7!lZ17$D;P`2vaOMh2j!N;&m07*{6DIjT%ElpUqU(mA zCuve1dG95D3N&}WifR?n(r9L9Preh^BxQ;9CSq?yN%s=HF6G9L=;Sf{dC0|xMPac< zQQyBmsw6d0Wvo9UKBXGqMpLu6Rf$^y6X?b8C(-pn-r56`ViJ2M%~kEg{*JyG`bl*R zJWeTP(i<7KJ0%M(8p0#vB5amEd3F5h z#O30GRWPzrbU9P|Pk4(bEDF?Mvm)}PMnzv1N}dqkJGq*A88$g;Wyl^j(SI-A<<0G6 z4~dM73)}8mWxr0iAD1Dp$66M;G%`8#uxy^R@7J%C`Sh`CVN|Dx5wwtRRb1-iZBC4N z0R*D#=J{Y+?!3DNc!vsL7`R2R(dVsBFF zxN5!#_Bi}hssf(WtV&AnUwM<}sHBix5o^MqSf%{E;>vq_ImEgWRwOEI$cx~&gdE@)rE{%NO9i zL;6Q-4F5)NB~FODkn)cTSVpR4sq*m^DP_IE#8Sa&hC9lL>R<-#t4Y}teoMZ?{)`+E zIn^_URPd#XyXn0^28VQvI3A7`X#SFMv%Ry#pQaI!HOh8Xuq|&wLM{Jcav{8E^i_{f z-Ak^QFg|gb>=Cv%vRT+@qjZ2Ktd93NY25w7wnRpG(m4kbmc-`|x>>`J!O*UOg4 zCll@^ml4%GgHpc^{fABs&h}nR$Q$r@9;cce+TS%=p7Y-SwaRzSdK=m(ym5GAQZzB? z*NT)h)N8$t>>ss(S}AW6<|jS0*Yo#LeWJdy8}|65R|%_97P3K6b0d_c$j9WmacdIS z(1ft=VQ<6Y&1)%j~T35nchF`xkmAjr;&}q8#u4M2NS7phC1agn0k5m zT$$hxPYU&34UTZ{jxHQlh)k3xx&vVk!k@Y425-e*^VSX4=7&R{q}m-)Q+!O?>@`xjUK4UP)vD0nqs0$=t^cGK9E62F&M1Bby!u)|XNvjk01h<&| z!bgT(G_T8lQ(k$ce=n=5FU6q6AnwwOg z{uljP=fl6U<9i?B1buA;;$MzD0hf46XGZKj-aDx|;m*m9n zN8qy*jz#L4grX<2ADO)mIE$hC!k0H;9Fa zxFQ;VBhUx@j=qadOgTvy$%dc3*5~v)T?#($4Dcd;!_O|@c&ZZv49L9DG~dAW%mngF z1b!NgW4nzjg%?EDdTl^#v zU;o6l4?5G!e1d!PO#i~UWJG>8Gmc%rUHu5h#1IwcL=W{e@brBIIsG$^-o)7y#x+YrKH_8zVi~tB+y;GAZpzXc6ccmC63OFBS+Z-=OOWz;TlrhdxlR3CIaUc zjVl!VzgL=gT`F8dFUBHDr8tLVj1GnO2*=1JxN-{DJs!s!fh!k+KQ3I6FE|H{@0)n1 z+&HUPjD-LF4#9cn!#z&G-ATaFFdS=oydn%zNHj1(Bzar`NG7j%`u*KYhZaon*Oqa3et{oiv2i4x8r z_yG String { - if raw == defaultVoiceWakeChimeName { return "Startrek Computer" } return raw } } @@ -50,11 +51,9 @@ enum VoiceWakeChimePlayer { private static func sound(for chime: VoiceWakeChime) -> NSSound? { switch chime { + case .none: + return nil case let .system(name): - // Prefer bundled tone if present. - if let bundled = bundledSound(named: name) { - return bundled - } return NSSound(named: NSSound.Name(name)) case let .custom(_, bookmark): @@ -70,13 +69,4 @@ enum VoiceWakeChimePlayer { return NSSound(contentsOf: url, byReference: false) } } - - private static func bundledSound(named name: String) -> NSSound? { - guard let url = Bundle.main.url( - forResource: name, - withExtension: defaultVoiceWakeChimeExtension, - subdirectory: "Resources/Sounds") - else { return nil } - return NSSound(contentsOf: url, byReference: false) - } } diff --git a/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift b/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift index a7a08b17c..0e8e14e28 100644 --- a/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift +++ b/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift @@ -40,7 +40,6 @@ actor VoiceWakeRuntime { let triggers: [String] let micID: String? let localeID: String? - let chimeEnabled: Bool let triggerChime: VoiceWakeChime let sendChime: VoiceWakeChime } @@ -52,7 +51,6 @@ actor VoiceWakeRuntime { triggers: sanitizeVoiceWakeTriggers(state.swabbleTriggerWords), micID: state.voiceWakeMicID.isEmpty ? nil : state.voiceWakeMicID, localeID: state.voiceWakeLocaleID.isEmpty ? nil : state.voiceWakeLocaleID, - chimeEnabled: state.voiceWakeChimeEnabled, triggerChime: state.voiceWakeTriggerChime, sendChime: state.voiceWakeSendChime) return (enabled, config) @@ -205,7 +203,7 @@ actor VoiceWakeRuntime { private func beginCapture(transcript: String, config: RuntimeConfig) async { self.isCapturing = true - if config.chimeEnabled { + if config.triggerChime != .none { await MainActor.run { VoiceWakeChimePlayer.play(config.triggerChime) } } let trimmed = Self.trimmedAfterTrigger(transcript, triggers: config.triggers) @@ -277,7 +275,7 @@ actor VoiceWakeRuntime { committed: finalTranscript, volatile: "", isFinal: true) - if config.chimeEnabled { + if config.sendChime != .none { await MainActor.run { VoiceWakeChimePlayer.play(config.sendChime) } } await MainActor.run { diff --git a/apps/macos/Sources/Clawdis/VoiceWakeSettings.swift b/apps/macos/Sources/Clawdis/VoiceWakeSettings.swift index 7ff94fb58..c4ebf9cb7 100644 --- a/apps/macos/Sources/Clawdis/VoiceWakeSettings.swift +++ b/apps/macos/Sources/Clawdis/VoiceWakeSettings.swift @@ -148,28 +148,18 @@ struct VoiceWakeSettings: View { private var chimeSection: some View { VStack(alignment: .leading, spacing: 10) { HStack(alignment: .firstTextBaseline, spacing: 10) { - Toggle(isOn: self.$state.voiceWakeChimeEnabled) { - VStack(alignment: .leading, spacing: 2) { - Text("Play sounds") - .font(.callout.weight(.semibold)) - Text("Chimes for wake-word and push-to-talk events.") - .font(.footnote) - .foregroundStyle(.secondary) - } - } - .toggleStyle(.switch) + Text("Sounds") + .font(.callout.weight(.semibold)) Spacer() } self.chimeRow( title: "Trigger sound", selection: self.$state.voiceWakeTriggerChime) - .disabled(!self.state.voiceWakeChimeEnabled) self.chimeRow( title: "Send sound", selection: self.$state.voiceWakeSendChime) - .disabled(!self.state.voiceWakeChimeEnabled) } .padding(.top, 4) } @@ -245,14 +235,19 @@ struct VoiceWakeSettings: View { .frame(width: self.fieldLabelWidth, alignment: .leading) Menu { + Button("No Sound") { selection.wrappedValue = .none } + Divider() ForEach(VoiceWakeChimeCatalog.systemOptions, id: \.self) { option in Button(VoiceWakeChimeCatalog.displayName(for: option)) { selection.wrappedValue = .system(name: option) } } + Divider() + Button("Choose file…") { self.chooseCustomChime(for: selection) } } label: { HStack(spacing: 6) { Text(selection.wrappedValue.displayLabel) + Spacer() Image(systemName: "chevron.down") .font(.caption) .foregroundStyle(.secondary) @@ -266,11 +261,7 @@ struct VoiceWakeSettings: View { .clipShape(RoundedRectangle(cornerRadius: 6)) } - Button("Choose file…") { - self.chooseCustomChime(for: selection) - } - - Button("Test") { + Button("Play") { VoiceWakeChimePlayer.play(selection.wrappedValue) } .keyboardShortcut(.space, modifiers: [.command]) diff --git a/docs/mac/voicewake.md b/docs/mac/voicewake.md index 793b2d0e6..a08b37fde 100644 --- a/docs/mac/voicewake.md +++ b/docs/mac/voicewake.md @@ -25,7 +25,7 @@ Updated: 2025-12-08 · Owners: mac app - **Voice Wake** toggle: enables wake-word runtime. - **Hold Cmd+Fn to talk**: enables the push-to-talk monitor. Disabled on macOS < 26. - Language & mic pickers, live level meter, trigger-word table, tester, forward target/command all remain unchanged. -- **Sounds**: optional chimes on trigger detect and on send; defaults to a bundled `startrek-computer.wav`. You can pick any `NSSound`-loadable file (e.g. MP3/WAV/AIFF) for each event. +- **Sounds**: chimes on trigger detect and on send; defaults to the macOS “Glass” system sound. You can pick any `NSSound`-loadable file (e.g. MP3/WAV/AIFF) for each event or choose **No Sound**. ## Forwarding payload - `VoiceWakeForwarder.prefixedTranscript(_:)` prepends the machine hint before sending. Shared between wake-word and push-to-talk paths.