
    `gjv                     F   d Z ddlZddlmZ ddlmZmZmZmZ ddl	m
Z
mZ dededefd	Z ed
      ZdededefdZdededededz  fdZdedededz  fdZdee   dedee   fdZe G d d             Zdee   dedeeef   fdZdee   dedededef
dZdededededef
d Zd!ed"edeeeef   fd#ZdDd$Zd%Zd&Zd'Z d(Z!d)Z"d*Z#defd+Z$d!edefd,Z% e&       Z'd-dd.d.dd/d0Z(ddd1d2edee   d3ee   d4ee   dz  d5eeee   gef      dee   fd6Z)	 dEddd.d7d2edee   d3ed4edz  d8edz  d9edefd:Z*d2edee   d3ed4edef
d;Z+	 dEd<d.d=d2edee   d>ed?ed9ededz  fd@Z,d2edee   dAededz  fdBZ-	 dFd2edee   d3ee   d4ee   d5eeee   gef      dee   fdCZ.y)GzShared curses-based UI components for Hermes CLI.

Used by `hermes tools` and `hermes skills` for interactive checklists.
Provides a curses multi-select with keyboard navigation, plus a
text-based numbered fallback for terminals without curses support.
    N)	dataclass)CallableListOptionalSet)Colorscolorlabelqueryreturnc                     | j                         }|j                         j                         }|sy|D ]*  }d}|D ]!  }|j                  ||      }|dk  r  y|dz  }# , y)zEReturn True when every query token is a case-insensitive subsequence.Tr   F   )lowersplitfind)r
   r   
normalizedtokenstokenposchs          G/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/curses_ui.py_query_matchesr      sr    J[[]  "F 	 	B//"c*CQw1HC		     z-_/. targetindexc                     |dk(  ry| |dz
     }|t         v ry| |   }||j                         k(  xr( ||j                         k7  xr ||j                         k(  S )zTrue if position ``index`` in ``target`` starts a word.

    Mirrors ``isBoundary`` in the TS scorer: start-of-string, after a
    separator char, or a lower->upper camelCase transition.
    r   Tr   )_WORD_BOUNDARYr   upper)r   r   prevcurs       r   _is_boundaryr!   '   sc     z%!)D~ -C4::<MC399;$6M3#))+;MMr   origr   r   c                    d}d}d}g }|D ]  }|j                  ||      }|dk  r y|j                  |       |dz  }|dk\  r||dz   k(  r|dz  }n|dk\  r|t        ||z
  dz
  d      z  }t        | |      r|dz  }|dk(  r|dz  }|}|dz   } |r!|d   dk(  r|d   t	        |      dz
  k(  r|dz  }||k(  r|d	z  }|t	        |      d
z  z  }|S )a#  Score one token against a target. None if the token isn't a subsequence.

    A faithful port of ``fuzzyScore`` in ui-tui/src/lib/fuzzy.ts and
    web/src/lib/fuzzy.ts so all three surfaces rank model ids identically:
    contiguous runs, word-boundary / first-char starts, prefix matches, and
    exact matches all score higher than scattered subsequence hits.

    ``lower`` is ``orig`` lowercased; matching is done against ``lower`` while
    boundary detection uses ``orig`` (so the camelCase rule works), exactly as
    in the TS scorer.
            r   Nr               g{Gz?)r   appendminr!   len)	r"   r   r   scorer   search_from	positionsr   idxs	            r   _token_scorer1   ;   s    EDKI jj[)7
19qQJEQYSta++Ec"QJE!8QJEAg+0 Yq\Q&9R=C	NQ<N+N
 ~ 
SZ$ELr   c                     | j                         }|j                         j                         }|syd}|D ]  }t        | ||      }| y||z  } |S )zAggregate score for a multi-token query (AND). None if any token fails.

    Mirrors ``fuzzyScoreMulti`` in the TS scorer: every whitespace-separated
    token must match; per-token scores are summed.
    r$   N)r   r   r1   )r
   r   r   r   totalr   token_scores          r   _fuzzy_scorer5   q   se     KKME[[]  "FE "5%7 Lr   itemsc                 *   |j                         }|st        t        t        |                   S g }t	        |       D ]'  \  }}t        ||      }||j                  ||f       ) |j                  d        |D cg c]  \  }}|	 c}}S c c}}w )a  Return item indices matching *query*, ranked best-first.

    An empty query keeps every item in original order. Otherwise items are
    filtered to fuzzy matches and sorted by score descending, ties broken by
    original index so equal-scoring rows keep their catalog order.
    c                     | d    | d   fS )Nr   r    )pairs    r   <lambda>z!_filter_indices.<locals>.<lambda>   s    47(DG!4 r   )key)striplistranger,   	enumerater5   r*   sort)r6   r   qscoredir
   r-   _s           r   _filter_indicesrF      s     	AE#e*%&&Fe$ &5UA&MM1e*%	& KK4K5 !$!QA!!!s   ?Bc                   .    e Zd ZU dZdZeed<   dZeed<   y)_SearchStatez3Mutable search state shared by curses picker loops.Factive r   N)	__name__
__module____qualname____doc__rI   bool__annotations__r   strr9   r   r   rH   rH      s    =FDE3Or   rH   filteredcursorc                 F    | s|dfS || vr| d   }|| j                  |      fS )z?Return ``(cursor, cursor_pos)`` inside the filtered index list.r   )r   )rR   rS   s     r   _reconcile_cursorrU      s4    qyX!8>>&)))r   
cursor_posdeltac                 2    | s|S | ||z   t        |       z     S )zEMove through the filtered index list, wrapping like the legacy menus.)r,   )rR   rS   rV   rW   s       r   _move_filtered_cursorrY      s%     Z%'3x=899r   scroll_offsetvisible_rows
total_rowsc                     t        d|      }|| k  r|} n|| |z   k\  r||z
  dz   } t        dt        | t        d||z
                    S )z2Clamp scroll offset so the cursor remains visible.r   r   )maxr+   )rZ   rV   r[   r\   s       r   _scroll_for_cursorr_      sY     q,'LM!"	}|3	3"\1A5q#mSJ,E%FGHHr   r<   searchc                 \   |j                   sy|dk(  r(t        |j                        }d|_         d|_        dd|fS || j                  ddfv r|j                  dd	 |_        y
|dk(  rd|_        y
|| j                  ddfv ryd|cxk  rdk  r"n y|xj                  t        |      z  c_        y
y)zHandle a key while the search prompt is active.

    Returns ``(handled, confirm, changed)``. Active search consumes query
    editing keys, but leaves navigation keys for the menu loop to handle.
    )FFF   FrJ   T   r(   Nr%   )TFT   
      )TTF    )rI   rO   r   KEY_BACKSPACE	KEY_ENTERchr)
curses_modr<   r`   	had_querys       r   _handle_active_search_keyrm      s     =="
by &	UI%%
z''a00||CR( 
by 
z##R,, 	S3  	C  r   c                      	 t         j                  j                         syddl} | j	                  t         j                  | j
                         y# t        $ r Y yw xY w)u  Flush any stray bytes from the stdin input buffer.

    Must be called after ``curses.wrapper()`` (or any terminal-mode library
    like simple_term_menu) returns, **before** the next ``input()`` /
    ``getpass.getpass()`` call.  ``curses.endwin()`` restores the terminal
    but does NOT drain the OS input buffer — leftover escape-sequence bytes
    (from arrow keys, terminal mode-switch responses, or rapid keypresses)
    remain buffered and silently get consumed by the next ``input()`` call,
    corrupting user data (e.g. writing ``^[^[`` into .env files).

    On non-TTY stdin (piped, redirected) or Windows, this is a no-op.
    Nr   )sysstdinisattytermiostcflushTCIFLUSH	Exception)rr   s    r   flush_stdinrv      sG    yy!		7#3#34 s   A .A 	AAupdownselecttogglecancelnonec                 6    t        | | j                               S )uV  Read one keypress and normalize it to a menu action.

    Decodes raw arrow-key escape sequences in addition to the translated
    ``curses.KEY_*`` values.  Even with ``keypad(True)`` (which
    ``curses.wrapper`` sets), some terminals/terminfo entries deliver cursor
    keys as raw CSI/SS3 byte sequences — ``getch()`` then returns ``27`` (ESC)
    followed by e.g. ``[`` ``A``.  Treating that leading ``27`` as a cancel is
    what made the setup wizard's provider/model pickers bail to the numbered
    fallback the moment a user pressed up/down.

    Returns one of the ``NAV_*`` constants.  A lone ESC (no continuation byte
    within a short window) is the only thing that maps to ``NAV_CANCEL`` via
    the escape path; ``q`` also cancels.  Unknown sequences map to
    ``NAV_NONE`` so the caller simply ignores them rather than misfiring.
    )_decode_menu_keygetch)stdscrs    r   read_menu_keyr     s      FFLLN33r   c                    ddl }||j                  t        d      fv rt        S ||j                  t        d      fv rt
        S ||j                  ddfv rt        S |t        d      k(  rt        S |t        d      k(  rt        S |d	k(  r	 | j                  d
       | j                         }| j                  d       |dk(  rt        S |t        d      t        d      fv r| j                         }|t        d      t        d      fv rt        S |t        d      t        d      fv rt
        S d|cxk  rdk  r+n t        S | j                         }d|cxk  rdk  rt        S  t        S t        S t        S # | j                  d       w xY w)zNormalize an already-read keypress to a menu action.

    Split out from ``read_menu_key`` so search-aware loops can peek the raw
    key (e.g. to catch ``/``) before falling back to nav decoding.
    r   Nkjre   rf    rB   rb   <   r%   [OABrg   ?   )cursesKEY_UPordNAV_UPKEY_DOWNNAV_DOWNri   
NAV_SELECT
NAV_TOGGLE
NAV_CANCELtimeoutr   NAV_NONE)r   r<   r   nxtfinals        r   r~   r~   '  sa    
v}}c#h''
vC))
vR((
c#h
c#h
by	NN2,,.CNN2"93s8SX&&LLNES3s8,,S3s8,, %'4'O  %'4'O (OO) NN2s   !E1 1Fr   F)reserve_bottomdraw_footerextra_color_pairs
searchablesearch_labelsc                 `    t         j                  j                         s|	S |
xr duxr t              k(  	 ddlt
        g fd}j                  |       t                d   t
        urd   S |	S # t        $ r |	cY S t        $ r
  |       cY S w xY w)a2	  Shared curses single-/multi-select event loop.

    Owns every piece the three public menus used to duplicate verbatim:
    the non-TTY guard, ``curses.wrapper`` setup (cursor hide + color pairs),
    the per-frame ``clear``/``getmaxyx``/``refresh`` cycle, scroll-offset math,
    row iteration, the ``read_menu_key`` dispatch with ``NAV_UP``/``NAV_DOWN``
    cursor wrap, ``flush_stdin``, and the ``KeyboardInterrupt`` / curses-
    unavailable fallback. Per-menu behavior is supplied as callbacks so the
    rendered output stays byte-identical to the old hand-rolled loops.

    Callbacks / params:
        draw_header(stdscr, max_y, max_x) -> int
            Draw the title/hint/description rows. Returns the first screen row
            index where the scrollable item list should start. When search is
            active it receives the live ``_SearchState`` via the optional
            ``search`` keyword (drawn by the menu so the hint line can show it).
        draw_row(stdscr, y, idx, is_cursor, max_x) -> None
            Draw one item row. ``idx`` is always the ORIGINAL item index, so
            per-menu rendering is unchanged whether or not a filter is active.
        on_action(action, cursor) -> value
            Reducer for SELECT/TOGGLE/CANCEL. Return ``_KEEP`` to continue the
            loop; return anything else to resolve the menu with that value.
            (UP/DOWN cursor movement is handled by the driver itself.)
        reserve_bottom: number of bottom screen rows kept clear of items
            (1 = leave the final row blank, matching the old loops).
        draw_footer(stdscr, max_y, max_x) -> None
            Optional bottom-row painter (e.g. a status bar). Drawn after the
            item rows; its row budget must be included in ``reserve_bottom``.
        extra_color_pairs: also init pair 3 (dim gray) for status bars.
        fallback() -> value
            Called when curses errors out on a real TTY (curses unavailable).
        cancel_value: returned on non-TTY stdin, ESC/cancel, or KeyboardInterrupt.
        searchable: when true, ``/`` opens a type-to-filter prompt over
            ``search_labels``. Returned values are always ORIGINAL item indices.
        search_labels: per-item text used for filtering (required when
            ``searchable`` is true; length must equal ``item_count``).
    Nr   c           
         j                  d       j                         rj                          j                          j	                  dj
                  d       j	                  dj                  d       r.j	                  dj                  dkD  rdnj                  d       }d}t               } rng }	 | j                          | j                         \  }} rt        ||j                        nt        t                    }t!        ||      \  }}	  | |||      }	t%        d||	z
  z
        }
t'        |||
t)        |            } r1|j                  r%|s#	 | j+                  |	dd	|dz
  j,                         t1        t        |t3        t)        |      ||
z                     D ](  \  }}||   }||	z   }||z
  k\  r n | ||||k(  |       * 
 | ||       | j5                           r| j7                         }|j8                  rjt;        ||      \  }}}|r%d}t!        t        |j                        |      \  }}|r|r t<        |      }|t>        ur|d<   y |rtA        | |      }n/|tC        d
      k(  r	d|_        tA        | |      }ntE        |       }|tF        k(  rtI        |||d      }nQ|tJ        k(  rtI        |||d      }n9|t<        tL        tN        fv r&|t<        k(  r r|sC ||      }|t>        ur|d<   y [# t"        $ r  | ||      }	Y w xY w# j.                  $ r Y w xY w)Nr   r   r%      r'   r(   T)r`   z  No matches/)(curs_set
has_colorsstart_coloruse_default_colors	init_pairCOLOR_GREENCOLOR_YELLOWCOLORSCOLOR_WHITErH   cleargetmaxyxrF   r   r>   r?   rU   	TypeErrorr^   r_   r,   addnstrA_DIMerrorr@   r+   refreshr   rI   rm   r   _KEEPr~   r   r   r   rY   r   r   r   )!r   rS   rZ   r`   labelsmax_ymax_xrR   rV   items_startr[   draw_ifiltered_posrD   yr<   handledconfirmchangedoutcomeactionr   r   draw_headerdraw_rowr   initial_cursor
item_count	on_actionr   result_holderr   
use_searchs!                        r   _drawz_run_curses_menu.<locals>._draw  s   OOA  """$))+  F$6$6;  F$7$7<$$$ 11v7I7I2 $FM!^F #-1JQS  %0u " $FFLL9eJ/0 
 &7x%H"
D"-feU6"RK  #1ek&9N&JK 2!:|S]! &,,x{A~uqyRXR^R^_ -6-S]ML<X)YZ- ?(FL !.A,AEN22VQ1;>? *u5  ,,.C}} 5N"C51' #,-M1B /v|| Lf2.FJ #'*3J*G#*%#77>M!$4$*$"$!1&#!>C(, !1&#!>*62FV#28VZQSTFx'28VZQRSF
J
CC+
8 '7Ge++2a(k  ! D"-feU"CKD "<< s$   #L )"L9 L65L69MM)
ro   rp   rq   r,   r   r   wrapperrv   KeyboardInterruptru   )r   r   r   r   r   r   r   r   fallbackcancel_valuer   r   r   r   r   r   s   ````````   ` @@@r   _run_curses_menur   ^  s    n 99^T 9^c->PT^>^Jvi	 i	V 	u#0#35#@}QRlR  zs   AB B B-B-,B-)cancel_returns	status_fntitleselectedr   r   c                    	 t              t              	 fd}	fd}	fd}	fd}t        dt              |||rdndr|ndt               fd	

      S )a  Curses multi-select checklist. Returns set of selected indices.

    Args:
        title: Header line displayed above the checklist.
        items: Display labels for each row.
        selected: Indices that start checked (pre-selected).
        cancel_returns: Returned on ESC/q. Defaults to the original *selected*.
        status_fn: Optional callback ``f(chosen_indices) -> str`` whose return
            value is rendered on the bottom row of the terminal.  Use this for
            live aggregate info (e.g. estimated token counts).
    Nc                    dd l }	 |j                  }|j                         r||j                  d      z  }| j	                  dd|dz
  |       | j	                  ddd|dz
  |j
                         y# |j                  $ r Y yw xY w)Nr   r   r   u:     ↑↓ navigate  SPACE toggle  ENTER confirm  ESC cancelr'   )r   A_BOLDr   
color_pairr   r   r   )r   r   r   r   hattrr   s        r   _draw_headerz&curses_checklist.<locals>._draw_header+  s    	MME  "**1--NN1a	59NN1L	6<<  || 		s   A*A2 2BBc                    dd l }|
v rdnd}|rdnd}d| d| d|    }|j                  }	|r0|j                  }	|j                         r|	|j	                  d      z  }		 | j                  |d||dz
  |	       y # |j                  $ r Y y w xY w)Nr   u   ✓r      →z [z] r   r   A_NORMALr   r   r   r   r   )r   r   rD   	is_cursorr   r   checkarrowlineattrchosenr6   s             r   	_draw_rowz#curses_checklist.<locals>._draw_row;  s    f#"5'E7"U1XJ/==D  "))!,,	NN1auqy$7|| 		s   !A: :BBc                 "   dd l }	        }|rjt        d|t        |      z
  dz
        }|j                  }|j	                         r||j                  d      z  }| j                  |dz
  ||||z
  dz
  |       y y # |j                  $ r Y y w xY w)Nr   r   r'   )r   r^   r,   r   r   r   r   r   )	r   r   r   r   status_textsxsattrr   r   s	          r   _draw_footerz&curses_checklist.<locals>._draw_footerJ  s    
	#F+KEC$44q89$$&V..q11Euqy"k52:>5Q  || 		s   A3A< <BBc                 r    | t         k(  rj                  |h       t        S | t        k(  rt	              S S N)r   symmetric_difference_updater   r   set)r   rS   r   r   s     r   
_on_actionz$curses_checklist.<locals>._on_actionX  s8    Z..x8LZv;r   r   r   r   c                  "    t               S r   )_numbered_fallback)r   r6   r   r   r   s   r   r;   z"curses_checklist.<locals>.<lambda>i  s    +E5(NT]^ r   )
r   r   r   r   r   r   r   r   r   r   )r   r   r,   rO   )
r   r6   r   r   r   r   r   r   r   r   s
   `````    @r   curses_checklistr     sn    & X]F  u: &A$-L4y/^# r   )r   descriptionr   r   r   c                    	 g 	|r|j                         	d	 fd	}fd}fd}t        t              |||d fdrt              
      S d
      S )	a  Curses single-select radio list. Returns the selected index.

    Args:
        title: Header line displayed above the list.
        items: Display labels for each row.
        selected: Index that starts selected (pre-selected).
        cancel_returns: Returned on ESC/q. Defaults to the original *selected*.
        description: Optional multi-line text shown between the title and
            the item list.  Useful for context that should survive the
            curses screen clear.
        searchable: When true, ``/`` opens a type-to-filter prompt. The
            returned value is always the original item index, not a filtered
            row position.
    Nc                    dd l }d}	 |j                  }|j                         r||j                  d      z  }| j	                  |d|dz
  |       |dz  }	D ]3  }||dz
  k\  r n)| j	                  |d||dz
  |j
                         |dz  }5 
r||j                  rd|j                   d}n
rd}nd}| j	                  |d||dz
  |j                         |dz  }|dz   S # |j                  $ r Y |dz   S w xY w)Nr   r   r   
  Search: +   ▎  BACKSPACE edit  Ctrl+U clear  ESC stopu;     ↑↓ navigate  ENTER/SPACE select  / search  ESC cancelu1     ↑↓ navigate  ENTER/SPACE select  ESC cancel)
r   r   r   r   r   r   rI   r   r   r   )r   r   r   r`   r   rowr   dlinehint
desc_linesr   r   s            r   r   z&curses_radiolist.<locals>._draw_header  s!   	MME  "**1--NN35%!)U;1HC $ %!)#sAueaiIq	 f0V]]#FLL>1_`ZPNN34FLLA1HC Qw || 	Qw	s   CC" "C87C8c                     dd l }|k(  rdnd}|rdnd}d| d| d
|    }|j                  }	|r0|j                  }	|j                         r|	|j	                  d      z  }		 | j                  |d||dz
  |	       y # |j                  $ r Y y w xY w)	Nr   u   ●u   ○r   r   z (z) r   r   )r   r   rD   r   r   r   radior   r   r   r6   r   s             r   r   z#curses_radiolist.<locals>._draw_row  s    Mx%35'E7"U1XJ/==D  "))!,,	NN1auqy$7|| 		s   "A; ;BBc                 (    | t         t        fv r|S S r   )r   r   )r   rS   r   s     r   r   z$curses_radiolist.<locals>._on_action  s    j*--Mr   r   c                       t               S r   )_radio_numbered_fallback)r   r6   r   r   s   r   r;   z"curses_radiolist.<locals>.<lambda>  s    1%.Y r   
r   r   r   r   r   r   r   r   r   r   r   )
splitlinesr   r,   r>   )
r   r6   r   r   r   r   r   r   r   r   s
   ```` `   @r   curses_radiolistr   n  s{    . !J ++-
<
 u: Y#%/d5k  6: r   c           	      D   t        t        d|  t        j                               t        t        dt        j                               t        |      D ]>  \  }}||k(  rt        dt        j                        nd}t        d| d|dz   dd	|        @ t                	 t        t        d
|dz    dt        j                              j                         }|s|S t        |      dz
  }d|cxk  rt        |      k  r|S  |S |S # t        t        t        f$ r |cY S w xY w)z1Text-based numbered fallback for radio selection.
  z&  Select by number, Enter to confirm.
u   (●)u   (○)  r   r   >2. z  Choice [default ]: r   )printr	   r   YELLOWDIMr@   GREENinputr=   intr,   
ValueErrorr   EOFError)	r   r6   r   r   rD   r
   markervalr0   s	            r   r   r     s    
%$ug
./	%96::
FGe$ 1545Mz6<<0z6(!AE":Rw/01 
G	E.x!|nC@&**MNTTVO#hl c%j J !x)84 s$   !;D #D D D DDCancel)cancel_labelr   default_indexr  c                    	 t        |      |gz   t        |      	d fd	}fd}	fd}t        t        |t              dz
        t              |||d	 fddrt              
      S d
      S )	ar  Curses single-select menu. Returns selected index or None on cancel.

    Works inside prompt_toolkit because curses.wrapper() restores the terminal
    safely, unlike simple_term_menu which conflicts with /dev/tty.

    When ``searchable`` is true, ``/`` opens a type-to-filter prompt; the
    returned value is always the original item index (or None for cancel).
    Nc                 ^   dd l }	 |j                  }|j                         r||j                  d      z  }| j	                  dd|dz
  |       r||j
                  rd|j                   d}nrd}nd}| j	                  dd||dz
  |j                         y# |j                  $ r Y yw xY w)	Nr   r   r   r   r   u8     ↑↓ navigate  ENTER confirm  / search  ESC/q cancelu.     ↑↓ navigate  ENTER confirm  ESC/q cancelr'   )	r   r   r   r   r   rI   r   r   r   )	r   r   r   r`   r   r   r   r   r   s	          r   r   z*curses_single_select.<locals>._draw_header  s    	MME  "**1--NN1a	59f0V]]#FLL>1_`QGNN1auqy&,,?  || 		s   BB B,+B,c                    dd l }|rdnd}d| d	|    }|j                  }|r0|j                  }|j                         r||j	                  d      z  }	 | j                  |d||dz
  |       y # |j                  $ r Y y w xY w)Nr   r   r   r   r   )
r   r   rD   r   r   r   r   r   r   	all_itemss
            r   r   z'curses_single_select.<locals>._draw_row  s    "5'9Q<.)==D  "))!,,	NN1auqy$7|| 		s   A/ /B Bc                 H    | t         k(  r	|k\  rd S |S | t        k(  ry t        S r   )r   r   r   )r   rS   
cancel_idxs     r   r   z(curses_single_select.<locals>._on_action  s/    Z "Z/4;V;Zr   r   c                      t               S r   )_numbered_single_fallback)r  r  r   s   r   r;   z&curses_single_select.<locals>.<lambda>)  s    25)ZP r   r   r   )r>   r,   r   r+   )
r   r6   r  r  r   r   r   r   r  r  s
   `   `   @@r   curses_single_selectr    s      U|n,IUJ$ =#i.1*<=y> P)3d9o  :> r   r  c                 n   t        d|  d       t        |d      D ]  \  }}t        d| d|         t                	 t        dt        |       d      j	                         }|syt        |      dz
  }d	|cxk  rt        |      k  r
n n||k  r|S ||k(  ry	 y# t        t        t        f$ r Y yw xY w)
z/Text-based numbered fallback for single-select.r   
r   r  r  z  Choice [1-r  Nr   )	r  r@   r	  r,   r=   r
  r  r   r  )r   r6   r  rD   r
   r  r0   s          r   r  r  0  s     
Dr
eQ' !51#Rw !	G
l3u:,c2399;#hl c%j S:%5J*   )84 s    (B )+B B B43B4c           	         t        |      }t        t        d|  t        j                               t        t        dt        j
                               	 t        |      D ]=  \  }}||v rt        dt        j                        nd}t        d| d|dz   dd	|        ? |r0 ||      }	|	r&t        t        d|	 t        j
                               t                	 t        t        d
t        j
                              j                         }
|
s	 |S t        |
      dz
  }d|cxk  rt        |      k  rn n|j                  |h       t                # t        t        t        f$ r |cY S w xY w)z8Text-based toggle fallback for terminals without curses.r   z&  Toggle by number, Enter to confirm.
u   [✓]z[ ]r  r   r   r  r  z"  Toggle # (or Enter to confirm): r   )r   r  r	   r   r  r  r@   r  r	  r=   r
  r,   r   r  r   r  )r   r6   r   r   r   r   rD   r
   r  r   r  r0   s               r   r   r   H  sK    ]F	%$ug
./	%96::
FG
!%( 	5HAu56&[U7FLL1eFBvhaAbzE734	5 #F+Ked;-0&**=>	"BFJJOPVVXC M c(Q,CC$#e*$22C59 	%   -x8 	"!!	"s   3E 7E E0/E0)r   N)r   r   )/rN   ro   dataclassesr   typingr   r   r   r   hermes_cli.colorsr   r	   rQ   rO   r   	frozensetr   r
  r!   floatr1   r5   rF   rH   tuplerU   rY   r_   rm   rv   r   r   r   r   r   r   r   r~   objectr   r   r   r   r   r  r  r   r9   r   r   <module>r%     s    ! 0 0 +# c d , 7#N NS NT N(3s 33 3s 3ut| 3l C EDL 2"49 "S "T#Y "2   *S	 *3 *5c? *:3i:!$:25:>A::II$'I7:IHKII##".#
4t#L0 



4S 4&/# /# /h 	  rt '+59XX9X #hX
 HtOX #c(S12X 	XX| [
 "&"[[9[ [
 $J[ t[ [ 	[|9  	
 	: G
 !GG9G G
 G G 	4ZGT9  	4Z	: 6:  9  #h  H	 
 #c(S12  	X r   