
    ?iZ                     ~   d Z ddlZddlZddlZddlZddlmZ ej                  j                         Z	 G d d      Z
e
j                   e
j                   de
j                   e
j                   de
j                   dZe
j                   e
j                   d	e
j                   d
e
j                   de
j                   Zg dZg dZg dZg dZg dZg dZg dZg dZg dZg dZd)dedefdZdddZde
j>                   de
j                   dZ dZ!g dZ"g d Z# G d! d"      Z$ G d# d$      Z%defd%Z&d&ed'efd(Z'y)*z+Terminal UI utilities for last30days skill.    N)Optionalc                   0    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zy
)Colorsz[95mz[94mz[96mz[92mz[93mz[91mz[1mz[2mz[0mN)__name__
__module____qualname__PURPLEBLUECYANGREENYELLOWREDBOLDDIMRESET     M/home/ubuntu/.openclaw/workspace/skills/last30days-official/scripts/lib/ui.pyr   r      s/    FDDEF
CD
CEr   r   u1  
  ██╗      █████╗ ███████╗████████╗██████╗  ██████╗ ██████╗  █████╗ ██╗   ██╗███████╗
  ██║     ██╔══██╗██╔════╝╚══██╔══╝╚════██╗██╔═████╗██╔══██╗██╔══██╗╚██╗ ██╔╝██╔════╝
  ██║     ███████║███████╗   ██║    █████╔╝██║██╔██║██║  ██║███████║ ╚████╔╝ ███████╗
  ██║     ██╔══██║╚════██║   ██║    ╚═══██╗████╔╝██║██║  ██║██╔══██║  ╚██╔╝  ╚════██║
  ███████╗██║  ██║███████║   ██║   ██████╔╝╚██████╔╝██████╔╝██║  ██║   ██║   ███████║
  ╚══════╝╚═╝  ╚═╝╚══════╝   ╚═╝   ╚═════╝  ╚═════╝ ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚══════╝
z*  30 days of research. 30 seconds of work.
z/last30days u   · researching...)zDiving into Reddit threads...zScanning subreddits for gold...z$Reading what Redditors are saying...z+Exploring the front page of the internet...zFinding the good discussions...zUpvoting mentally...zScrolling through comments...)z#Checking what X is buzzing about...zReading the timeline...zFinding the hot takes...zScanning tweets and threads...z Discovering trending insights...zFollowing the conversation...zReading between the posts...)zGetting the juicy details...zFetching engagement metrics...zReading top comments...zExtracting insights...zAnalyzing discussions...)zSearching YouTube for videos...z!Finding relevant video content...zScanning YouTube channels...z Discovering video discussions...zFetching transcripts...)z'Searching TikTok for trending videos...z!Finding what's viral on TikTok...z'Scanning TikTok for relevant content...)zSearching Instagram Reels...z'Finding what's trending on Instagram...z(Scanning Instagram for relevant reels...)zSearching Hacker News...z!Scanning HN front page stories...z Finding technical discussions...z&Discovering developer conversations...)zChecking prediction markets...z%Finding what people are betting on...zScanning Polymarket for odds...z!Discovering prediction markets...)zCrunching the data...zScoring and ranking...zFinding patterns...zRemoving duplicates...zOrganizing findings...)zSearching the web...zFinding blogs and docs...zCrawling news sites...zDiscovering tutorials...diagreturnc                     | rc| j                  d      rdnd}| j                  d      rdnd}| j                  d      rdnd}| j                  d      rdnd}d| d| d	| d
| }nd}d| dS )z<Build conversational NUX message with dynamic source status.openaiu   ✓u   ✗x_sourceyoutubeweb_search_backendzReddit z, X z
, YouTube z, Web u'   YouTube ✓, Web ✓, Reddit ✗, X ✗zB
I just researched that for you. Here's what I've got right now:

u  

You can unlock more sources with API keys or by signing in to Codex — just ask me how and I'll walk you through it. More sources means better research, but it works fine as-is.

Some examples of what you can do:
- "last30 what are people saying about Figma"
- "last30 watch my biggest competitor every week"
- "last30 watch Peter Steinberger every 30 days"
- "last30 watch AI video tools monthly"
- "last30 what have you found about AI video?"

Just start with "last30" and talk to me like normal.
)get)r   redditxr   webstatus_lines         r   _build_nux_messager#   s   s    ((8,%XXj)Eu88I.%Exx 45e5xtA3j	uM?   r   ud   
💡 You can unlock Reddit with an OpenAI API key or by running `codex login` — just ask me how.
u@   
💡 You can unlock X with an xAI API key — just ask me how.
)r   r    zBird authentication failed.u   

To fix this:
1. Log into X (twitter.com) in Safari, Chrome, or Firefox
2. Try again — Bird reads your browser cookies automatically.
u   
Bird authentication failed.

To fix this:
1. Log into X (twitter.com) in Safari, Chrome, or Firefox
2. Try again — Bird reads your browser cookies automatically.
)
u   ⠋u   ⠙u   ⠹u   ⠸u   ⠼u   ⠴u   ⠦u   ⠧u   ⠇u   ⠏)z   z.  z.. z...c                   f    e Zd ZdZdej
                  dfdededefdZd Z	d	 Z
defd
ZddefdZy)Spinnerz-Animated spinner for long-running operations.WorkingFmessagecolorquietc                 f    || _         || _        d| _        d | _        d| _        d| _        || _        y )NFr   )r'   r(   runningthread	frame_idxshown_staticr)   )selfr'   r(   r)   s       r   __init__zSpinner.__init__   s4    
26!
r   c           	         | j                   rt        | j                  t        t              z     }t        j
                  j                  d| j                   | t        j                   d| j                   d       t        j
                  j                          | xj                  dz  c_        t        j                  d       | j                   ry y )Nr        g{Gz?)r+   SPINNER_FRAMESr-   lensysstderrwriter(   r   r   r'   flushtimesleep)r/   frames     r   _spinzSpinner._spin   s    ll"4>>C4G#GHEJJr$**eWV\\N!DLL>QSTUJJNNaNJJt llr   c                 x   d| _         t        rAt        j                  | j                  d      | _        | j
                  j                          y | j                  s`| j                  sSt        j                  j                  d| j                   d       t        j                  j                          d| _        y y y )NT)targetdaemon   ⏳ r   )r+   IS_TTY	threadingThreadr>   r,   startr.   r)   r7   r8   r9   r'   r:   r/   s    r   rF   zSpinner.start   s    #**$**TJDKKK $$TZZ

  4~R!89

  "$(! .8$r   c                     || _         t        sO| j                  sBt        j                  j                  d| d       t        j                  j                          y y y )NrB   r   )r'   rC   r.   r7   r8   r9   r:   r/   r'   s     r   updatezSpinner.update   sF    d//JJtG9B/0JJ 0vr   final_messagec                 2   d| _         | j                  r| j                  j                  d       t        rt        j
                  j                  d       |r#t        j
                  j                  d| d       t        j
                  j                          y )NFg?)timeoutzR                                                                                u   ✓ r   )r+   r,   joinrC   r7   r8   r9   r:   )r/   rK   s     r   stopzSpinner.stop   si    ;;KKS)JJ34JJtM?"56

r   N) )r   r   r   __doc__r   r   strboolr0   r>   rF   rJ   rO   r   r   r   r%   r%      sN    7&/fkkY^   RV )c 	# 	r   r%   c                   B   e Zd ZdZd0dedefdZd Zd Zde	fdZ
d	e	d
e	fdZd	e	d
e	fdZd Zd Zde	fdZd Zde	fdZd Zde	fdZd Zde	fdZd Zde	fdZd Zde	fdZd Zd Zd1de	de	de	de	d e	d!e	d"e	fd#Zd2d%efd&Zd'efd(Zd) Z d* Z!d+ Z"d3d,ed-e#fd.Z$d/ Z%y$)4ProgressDisplayz%Progress display for research phases.topicshow_bannerc                 x    || _         d | _        t        j                         | _        |r| j	                          y y N)rV   spinnerr;   
start_time_show_banner)r/   rV   rW   s      r   r0   zProgressDisplay.__init__   s1    
*.))+ r   c           	         t         rt        j                  j                  t        dz          t        j                  j                  t
        j                   dt
        j                   t
        j                   | j                   t
        j                   d       n-t        j                  j                  d| j                   d       t        j                  j                          y )Nr   zTopic: 

u   /last30days · researching: )rC   r7   r8   r9   MINI_BANNERr   r   r   r   rV   r:   rG   s    r   r\   zProgressDisplay._show_banner   s    JJ[4/0JJ

|76<<.djj\Z`ZfZfYggklm JJ;DJJ<rJK

r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y )NRedditr   )	randomchoiceREDDIT_MESSAGESr%   r   r   r   rZ   rF   r/   msgs     r   start_redditzProgressDisplay.start_reddit   K    mmO,&--v||nAcUKV]][r   countc                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nra    Found  threadsrZ   rO   r   r   r   r/   ri   s     r   
end_redditzProgressDisplay.end_reddit   s<    <<LLvfll^75'QYZ[ r   currenttotalc                 H   | j                   r| j                   j                          t        j                  t              }t        t        j                   dt        j                   d| d| d| 	t        j                        | _         | j                   j                          y Nra   z [/z] )
rZ   rO   rb   rc   ENRICHING_MESSAGESr%   r   r   r   rF   r/   rp   rq   rf   s       r   start_reddit_enrichz#ProgressDisplay.start_reddit_enrich   sw    <<LLmm./&--v||nBwiqQVPWWYZ]Y^_agananor   c                     | j                   r_t        j                  t              }| j                   j	                  t
        j                   dt
        j                   d| d| d| 	       y y rs   )rZ   rb   rc   ru   rJ   r   r   r   rv   s       r   update_reddit_enrichz$ProgressDisplay.update_reddit_enrich  sZ    <<-- 23CLL6==/~RyPQRWQXXZ[^Z_ `a r   c                     | j                   r>| j                   j                  t        j                   dt        j                   d       y y )Nra   z Enriched with engagement datarm   rG   s    r   end_reddit_enrichz!ProgressDisplay.end_reddit_enrich  s6    <<LLvfll^Cabc r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y )NXr   )	rb   rc   
X_MESSAGESr%   r   r   r   rZ   rF   re   s     r   start_xzProgressDisplay.start_x  sK    mmJ'&++a~QseDfkkRr   c                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nr}   rk    posts)rZ   rO   r   r   r   rn   s     r   end_xzProgressDisplay.end_x  s;    <<LLQv||nGE7&QR r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y )NYouTuber   )	rb   rc   YOUTUBE_MESSAGESr%   r   r   r   rZ   rF   re   s     r   start_youtubezProgressDisplay.start_youtube  sL    mm,-&**WV\\N!C5I6::Vr   c                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nr   rk    videos)rZ   rO   r   r   r   rn   s     r   end_youtubezProgressDisplay.end_youtube  s;    <<LLGFLL>wWX r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y )NTikTokr   )	rb   rc   TIKTOK_MESSAGESr%   r   r	   r   rZ   rF   re   s     r   start_tiktokzProgressDisplay.start_tiktok  rh   r   c                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nr   rk   r   rZ   rO   r   r	   r   rn   s     r   
end_tiktokzProgressDisplay.end_tiktok#  s<    <<LLvfll^75'QXYZ r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y )N	Instagramr   )	rb   rc   INSTAGRAM_MESSAGESr%   r   r	   r   rZ   rF   re   s     r   start_instagramzProgressDisplay.start_instagram'  sO    mm./&--	&,,qNPVP]P]^r   c                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nr   rk    reelsr   rn   s     r   end_instagramzProgressDisplay.end_instagram,  s<    <<LLygeWTZ[\ r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                  d      | _        | j                  j                          y )NHNr   Tr)   )	rb   rc   HN_MESSAGESr%   r   r   r   rZ   rF   re   s     r   start_hackernewsz ProgressDisplay.start_hackernews0  sN    mmK(&--6<<.#G^bcr   c                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nr   rk    storiesrm   rn   s     r   end_hackernewszProgressDisplay.end_hackernews5  s;    <<LLr&,,wugXVW r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                  d      | _        | j                  j                          y )N
Polymarketr   Tr   )	rb   rc   POLYMARKET_MESSAGESr%   r   r   r   rZ   rF   re   s     r   start_polymarketz ProgressDisplay.start_polymarket9  sR    mm/0&,,z&,,qNPVP\P\dhir   c                     | j                   rA| j                   j                  t        j                   dt        j                   d| d       y y )Nr   rk    marketsrZ   rO   r   r   r   rn   s     r   end_polymarketzProgressDisplay.end_polymarket>  s<    <<LLjgeWT\]^ r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y )N
Processingr   )	rb   rc   PROCESSING_MESSAGESr%   r   r	   r   rZ   rF   re   s     r   start_processingz ProgressDisplay.start_processingB  sO    mm/0&--
6<<.#OQWQ^Q^_r   c                 R    | j                   r| j                   j                          y y rY   )rZ   rO   rG   s    r   end_processingzProgressDisplay.end_processingG  s    <<LL r   reddit_countx_countyoutube_counthn_countpm_counttiktok_countig_countc           	      @   t        j                          | j                  z
  }t        rt        j                  j                  dt        j                   t        j                   dt        j                   d       t        j                  j                  t        j                   d|ddt        j                   d       t        j                  j                  dt        j                   dt        j                   d| d	       t        j                  j                  t        j                   d
t        j                   d| d       |rEt        j                  j                  dt        j                   dt        j                   d| d       |rEt        j                  j                  dt        j                   dt        j                   d| d       |rEt        j                  j                  dt        j                   dt        j                   d| d       |rEt        j                  j                  dt        j                   dt        j                   d| d       |rEt        j                  j                  dt        j                   dt        j                   d| d       t        j                  j                  d       nd| dd| dg}	|r|	j                  d| d       |r|	j                  d| d       |r|	j                  d| d       |r|	j                  d| d       |r|	j                  d| d       t        j                  j                  d|ddd j!                  |	       d       t        j                  j#                          y )!Nr   u   ✓ Research completer   (.1fs)r3   zReddit:z
 threads  zX:r   zYouTube:r   zTikTok:z
Instagram:r   zHN:r   zPolymarket:r   r^   zReddit: rl   zX: z	YouTube: zTikTok: zInstagram: zHN: zPolymarket: u   ✓ Research complete (zs) - z, )r;   r[   rC   r7   r8   r9   r   r   r   r   r   r   r   r   r	   appendrN   r:   )
r/   r   r   r   r   r   r   r   elapsedpartss
             r   show_completezProgressDisplay.show_completeK  s   ))+/JJr&,,}<QRXR^R^Q__`abJJ

|1WSMFLL>LMJJr&--~Q|nT^_`JJ}Bv||nAgYfMN

  2fjj\&,,qW^!_`

  2fmm_GFLL><.X_!`a

  2fmm_Jv||nAhZW]!^_

  2fmm_C~QxjPX!YZ

  2fll^;v||nAhZW_!`aJJV$~X6#gYf8MNEyw?@x~W=>{8*F;<tH:X67|H:X>?JJ6wsm5SXIYHZZ\]^

r   N	age_hoursc                 $   |d|dd}nd}t         j                  j                  t        j                   dt        j
                   dt        j                   d| dt        j
                   d	
       t         j                  j                          y )
Nz (r   zh old)rP   u   ⚡r   zUsing cached resultsz - use --refresh for fresh datar^   )r7   r8   r9   r   r   r   r   r:   )r/   r   age_strs      r   show_cachedzProgressDisplay.show_cachedl  s     9S/0GG

FLL>V\\N!FJJ<G[\c[d  eD  EK  EQ  EQ  DR  RV  W  	X

r   r'   c                     t         j                  j                  t        j                   dt        j
                   d| d       t         j                  j                          y )Nu
   ✗ Error:r   r   )r7   r8   r9   r   r   r   r:   rI   s     r   
show_errorzProgressDisplay.show_errort  s?    

FJJ<z&,,q	LM

r   c                     t        j                  t              }t        t        j
                   dt        j                   d| t        j
                        | _        | j                  j                          y)zShow web-only mode indicator.Webr   N)	rb   rc   WEB_ONLY_MESSAGESr%   r   r   r   rZ   rF   re   s     r   start_web_onlyzProgressDisplay.start_web_onlyx  sL    mm-.&,,s6<<.#GVr   c                     | j                   r>| j                   j                  t        j                   dt        j                   d       yy)zEnd web-only spinner.r   z assistant will search the webNr   rG   s    r   end_web_onlyzProgressDisplay.end_web_only~  s6    <<LLc&,,?]^_ r   c                    t        j                          | j                  z
  }t        rt        j                  j                  dt        j                   t        j                   dt        j                   d       t        j                  j                  t        j                   d|ddt        j                   d       t        j                  j                  dt        j                   dt        j                   d	       n$t        j                  j                  d
|dd       t        j                  j                          y)z"Show completion for web-only mode.r   u   ✓ Ready for web searchr   r   r   r   r3   zWeb:z+ assistant will search blogs, docs & news

u   ✓ Ready for web search (zs)
N)r;   r[   rC   r7   r8   r9   r   r   r   r   r   r:   )r/   r   s     r   show_web_only_completez&ProgressDisplay.show_web_only_complete  s    ))+/JJr&,,}<TU[UaUaTbbcdeJJ

|1WSMFLL>LMJJr&,,tFLL>AnopJJ9'#dKL

r   missingr   c                     |dv r)t         j                  j                  t        |             n.|t        v r&t         j                  j                  t        |          t         j                  j                          y)zShow NUX / promotional message for missing API keys.

        Args:
            missing: 'both', 'all', 'reddit', or 'x' - which keys are missing
            diag: Optional diagnostics dict for dynamic source status
        )bothallN)r7   r8   r9   r#   PROMO_SINGLE_KEYr:   )r/   r   r   s      r   
show_promozProgressDisplay.show_promo  sU     o%JJ/56((JJ-g67

r   c                     t         r$t        j                  j                  t               n#t        j                  j                  t
               t        j                  j                          y)zShow Bird authentication help.N)rC   r7   r8   r9   BIRD_AUTH_HELPBIRD_AUTH_HELP_PLAINr:   rG   s    r   show_bird_auth_helpz#ProgressDisplay.show_bird_auth_help  s:    JJ^,JJ12

r   )T)r   r   r   r   r   rY   )r   N)&r   r   r   rQ   rR   rS   r0   r\   rg   intro   rw   ry   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   floatr   r   r   r   r   dictr   r   r   r   r   rU   rU      s^   / c    
\ \3 s bC b b
d
S3 S
Y Y
[ [
]3 ]
XC X
_C _
 #  C _b ru   JM   ]` BU # `
	# d r   rU   c                    | j                  dd      }| j                  d      du}| j                  dd      }| j                  d      du}|r|r|r|ryg }t        r|j                  t        j                   dt        j
                          |j                  t        j                   dt        j
                   d	t        j                   d
t        j
                   dt        j                   dt        j
                          |j                  t        j                   dt        j
                   dt        j                   dt        j
                          |rw|j                  t        j                   dt        j
                   dt        j                   dt        j
                   dt        j                   dt        j
                          n|j                  t        j                   dt        j
                   dt        j                   dt        j
                   dt        j                   dt        j
                          |j                  t        j                   dt        j
                   dt        j                   dt        j
                          |r| j                  dd      }| j                  dd      }|dk(  r|rd| dn|j                         }|j                  t        j                   dt        j
                   dt        j                   dt        j
                   d| dt        j                   dt        j
                          n|j                  t        j                   dt        j
                   dt        j                   dt        j
                   dt        j                   dt        j
                          | j                  d      r|j                  t        j                   dt        j
                   dt        j                   dt        j
                          |j                  t        j                   dt        j
                   dt        j                   dt        j
                          nT|j                  t        j                   dt        j
                   d t        j                   dt        j
                          |rw|j                  t        j                   dt        j
                   dt        j                   d!t        j
                   d"t        j                   dt        j
                          n|j                  t        j                   dt        j
                   dt        j                   d#t        j
                   d$t        j                   dt        j
                          |j                  t        j                   dt        j
                   d%t        j                   dt        j
                          |r| j                  dd      }	|j                  t        j                   dt        j
                   dt        j                   d&t        j
                   d'|	 d(t        j                   dt        j
                          nv|j                  t        j                   dt        j
                   dt        j                   d)t        j
                   d*t        j                   dt        j
                          |j                  t        j                   dt        j
                   dt        j                   dt        j
                          |j                  t        j                   dt        j
                   d+t        j                   d,t        j
                   d-t        j                   dt        j
                          |j                  t        j                   d.t        j
                          nP|j                  d       |j                  d/       |j                  d0       |r|j                  d1       n"|j                  d2       |j                  d3       |r|j                  d4       nE|j                  d5       | j                  d      r|j                  d6       n|j                  d7       |r|j                  d8       n"|j                  d9       |j                  d:       |r|j                  d;       n|j                  d<       |j                  d0       |j                  d=       |j                  d.       t        j                  j                  d>j                  |      d?z          t        j                  j                          y)@zShow pre-flight source status banner when sources are missing.

    Args:
        diag: Dict from env diagnostics with keys:
            openai, xai, x_source, bird_installed, bird_authenticated,
            bird_username, youtube, web_search_backend
    r   Fr   Nr   r   u   ┌─────────────────────────────────────────────────────┐u   │r   u"   /last30days v2.1 — Source Statusz                    z5                                                     r3   u
   ✅ Redditu,       — OPENAI_API_KEY found                u
   ❌ Redditu-       — No OPENAI_API_KEY                    u8        └─ Add to ~/.config/last30days/.env            rP   bird_usernamebirdzBird ()u   ✅ X/Twitteru    — z                          u   ❌ X/Twitteru)    — No Bird CLI or XAI_API_KEY          bird_installedu9        └─ Bird installed but not authenticated         u9        └─ Log into x.com in your browser, then retry   u:        └─ Needs Node.js 22+ (Bird is bundled)           u   ✅ YouTubeu)      — yt-dlp found                      u   ❌ YouTubeu+      — yt-dlp not installed                u;        └─ Fix: brew install yt-dlp (free)                u   ✅ Webu          — z API                       u   ⚡ Webu/          — Using assistant's search tool       z
  Config: z~/.config/last30days/.envz                  u   └─────────────────────────────────────────────────────┘u=   │ /last30days v2.1 — Source Status                    │u;   │                                                     │u>   │  ✅ Reddit    — OPENAI_API_KEY found                │u?   │  ❌ Reddit    — No OPENAI_API_KEY                    │u>   │     └─ Add to ~/.config/last30days/.env            │u?   │  ✅ X/Twitter — available                            │u>   │  ❌ X/Twitter — No Bird CLI or XAI_API_KEY          │u?   │     └─ Log into x.com in your browser, then retry   │u@   │     └─ Needs Node.js 22+ (Bird is bundled)           │u>   │  ✅ YouTube   — yt-dlp found                        │u>   │  ❌ YouTube   — yt-dlp not installed                │uA   │     └─ Fix: brew install yt-dlp (free)                │u>   │  ✅ Web       — API search available                │u>   │  ⚡ Web       — Using assistant's search tool       │u;   │  Config: ~/.config/last30days/.env                  │r   r^   )r   rC   r   r   r   r   r   r   r   upperr   r7   r8   r9   rN   r:   )
r   
has_openaihas_xhas_youtubehas_weblinessourceusernamelabelbackends
             r   show_diagnostic_bannerr     s/
    (E*JHHZ ,E((9e,Khh+,D8G eE

|  $I  JP  JV  JV  IW  X  	Y

|3v||nAfkk]Bdekeqeqdr  sG  HN  HR  HR  GS  SV  W]  Wc  Wc  Vd  e  	f

|3v||n4ijpjtjtiuuxy  zF  zF  yG  H  	I LLFJJ<s6<<.6<<.
SYS_S_R`  aM  NT  NX  NX  MY  Y\  ]c  ]i  ]i  \j  k  lLLFJJ<s6<<.6::,jQWQ]Q]P^  _L  MS  MW  MW  LX  X[  \b  \h  \h  [i  j  kLLFJJ<s6<<.8pqwq{q{p||  AG  AM  AM  @N  O  P XXj"-Fxx4H,2f,<fXJa(v||~ELLFJJ<s6<<.6<<.V\VbVbUcchinho  pJ  KQ  KU  KU  JV  VY  Z`  Zf  Zf  Yg  h  iLLFJJ<s6<<.6::,mTZT`T`Sa  bK  LR  LV  LV  KW  WZ  [a  [g  [g  Zh  i  jxx()

|3v||n<uv|  wA  wA  vB  BE  FL  FR  FR  ES  T  U

|3v||n<uv|  wA  wA  vB  BE  FL  FR  FR  ES  T  U

|3v||n<vw}  xB  xB  wC  CF  GM  GS  GS  FT  U  V LLFJJ<s6<<.6<<.TZT`T`Sa  bK  LR  LV  LV  KW  WZ  [a  [g  [g  Zh  i  jLLFJJ<s6<<.6::,kRXR^R^Q_  `K  LR  LV  LV  KW  WZ  [a  [g  [g  Zh  i  jLLFJJ<s6<<.8stzt~t~s  @C  DJ  DP  DP  CQ  R  S hh3R8GLLFJJ<s6<<.6<<.PVP\P\~]hiphq  rM  NT  NX  NX  MY  Y\  ]c  ]i  ]i  \j  k  lLLFJJ<s6<<.6==/QWQ]Q]P^  _N  OU  OY  OY  NZ  Z]  ^d  ^j  ^j  ]k  l  m

|3v||n4ijpjtjtiuuxy  zF  zF  yG  H  	I

|3v||nJv{{mKdekeqeqdr  sE  FL  FP  FP  EQ  QT  U[  Ua  Ua  Tb  c  	d

|  $I  JP  JV  JV  IW  X  	Y 	  }  	~TURSLLYZLLZ[LLYZLLZ[LLYZxx()^__`LLYZLLYZLL\]LLYZLLYZRSRS  }  	~JJTYYu%./JJr   phaser'   c                    t         j                  t         j                  t         j                  t         j                  t         j
                  d}|j                  | t         j                        }t        j                  j                  | dt         j                   d| d       t        j                  j                          y)zPrint a phase message.)r   r    processdoneerroru   ▸r   r   N)r   r   r   r	   r   r   r   r   r7   r8   r9   r:   )r   r'   colorsr(   s       r   print_phaser   
  s}     --[[==F JJufll+EJJwc&,,q	<=JJr   rY   )(rQ   r7   r;   rD   rb   typingr   r8   isattyrC   r   r	   r   r   r   BANNERr_   rd   r~   ru   r   r   r   r   r   r   r   r   rR   r#   r   r   r   r   r5   DOTS_FRAMESr%   rU   r   r   r   r   r   <module>r      s   1 
     
				 	 mm_V[[M * vzzlDV\\N S
 ==/&++k&,,qTeflfrfresv
      T S < w	M *6<<. 9  X*1 1hB BJc cLs S r   