
    )jv                        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.dS )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                     |                                  }|                                                                 }|sdS |D ].}d}|D ]'}|                    ||          }|dk     r  dS |dz  }(/dS )zEReturn True when every query token is a case-insensitive subsequence.Tr   F   )lowersplitfind)r
   r   
normalizedtokenstokenposchs          ;/home/wildlama/.hermes/hermes-agent/hermes_cli/curses_ui.py_query_matchesr      s    J[[]]  ""F t 	 	 	 	B//"c**CQwwuuu1HCC	 4    z-_/. targetindexc                     |dk    rdS | |dz
           }|t           v rdS | |         }||                                k    o/||                                k    o||                                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!   '   sr     zzt%!)D~t -C4::<<MC399;;$6M3#))++;MMr   origr   r   c                    d}d}d}g }|D ]}|                     ||          }|dk     r dS |                    |           |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   ;   sS    EDKI  jj[))7744
199qQJEEQYYSta+++Ec"" 	QJE!88QJEAg  Yq\Q&&9R=C	NNQ<N+N+N
 ~~ 
SZZ$ELr   c                     |                                  }|                                                                 }|sdS d}|D ]}t          | ||          }| dS ||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   sz     KKMME[[]]  ""F sE  "5%7744Lr   itemsc                 F   |                                 }|s)t          t          t          |                               S g }t	          |           D ].\  }}t          ||          }||                    ||f           /|                    d            d |D             S )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.
    Nc                 $    | d          | d         fS )Nr   r    )pairs    r   <lambda>z!_filter_indices.<locals>.<lambda>   s    47(DG!4 r   )keyc                     g | ]\  }}|S r9   r9   ).0i_s      r   
<listcomp>z#_filter_indices.<locals>.<listcomp>   s    !!!$!QA!!!r   )striplistranger,   	enumerater5   r*   sort)r6   r   qscoredr?   r
   r-   s          r   _filter_indicesrI      s     	A 'E#e**%%&&&Fe$$ & &5UA&&MM1e*%%%
KK44K555!!&!!!!r   c                   0    e Zd ZU dZdZeed<   dZeed<   dS )_SearchStatez3Mutable search state shared by curses picker loops.Factive r   N)	__name__
__module____qualname____doc__rL   bool__annotations__r   strr9   r   r   rK   rK      s3         ==FDE3OOOOOr   rK   filteredcursorc                 T    | s|dfS || vr| d         }||                      |          fS )z?Return ``(cursor, cursor_pos)`` inside the filtered index list.r   )r   )rU   rV   s     r   _reconcile_cursorrX      s?     qyX!8>>&))))r   
cursor_posdeltac                 @    | s|S | ||z   t          |           z           S )zEMove through the filtered index list, wrapping like the legacy menus.)r,   )rU   rV   rY   rZ   s       r   _move_filtered_cursorr\      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+   )r]   rY   r^   r_   s       r   _scroll_for_cursorrb      sm     q,''LM!!"	}|3	3	3"\1A5q#mSJ,E%F%FGGHHHr   r<   searchc                 N   |j         sdS |dk    r't          |j                  }d|_         d|_        dd|fS || j        ddfv r|j        dd	         |_        d
S |dk    r	d|_        d
S || j        ddfv rdS d|cxk    rdk     r"n n|xj        t          |          z  c_        d
S dS )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   FrM   T   r(   Nr%   )TFT   
      )TTF    )rL   rR   r   KEY_BACKSPACE	KEY_ENTERchr)
curses_modr<   rc   	had_querys       r   _handle_active_search_keyrp      s     = #""
byy &&	UI%%
z'a000|CRC(  
byy  
z#R,,,  	S3C   r   c                      	 t           j                                        sdS ddl} |                     t           j        | j                   dS # t          $ r Y dS 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)ru   s    r   flush_stdinry      sl    y!! 	F	7#344444   s   A )A 
AAupdownselecttogglecancelnonec                 F    t          | |                                           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      FFLLNN3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*	 | 
                    d
           |                                 }| 
                    d           n# | 
                    d           w xY w|dk    rt          S |t          d          t          d          fv r|                                 }|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 n#|                                 }d|cxk    rdk    !n t          S t          S t          S )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jrh   ri    rG   re   <   r%   [OABrj   ?   )cursesKEY_UPordNAV_UPKEY_DOWNNAV_DOWNrl   
NAV_SELECT
NAV_TOGGLE
NAV_CANCELtimeoutr   NAV_NONE)r   r<   r   nxtfinals        r   r   r   '  s    MMM
v}c#hh'''
vC)))
vR(((
c#hh
c#hh
byy	NN2,,..CNN2FNN2"993s88SXX&&&LLNNES3s88,,,S3s88,,, %''''4''''' %''''4''''OOs   )C C(r   F)reserve_bottomdraw_footerextra_color_pairs
searchablesearch_labelsc                     t           j                                        s|	S |
oduot                    k    	 ddlt
          g fd}                    |           t                       d         t
          urd         n|	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           
                              d                                           r                                                                                      dj        d                               dj        d           r)                    dj        dk    rdnj        d           }d}t                      } rng }	 | 
                                 |                                 \  }} rt          ||j                  nt          t                              }t!          ||          \  }}	  | |||          }	n# t"          $ r  | ||          }	Y nw xY wt%          d||	z
  z
            }
t'          |||
t)          |                    } r<|j        r5|s3	 |                     |	dd	|dz
  j                   n# j        $ r Y nw xY wt1          t          |t3          t)          |          ||
z                                 D ]0\  }}||         }||	z   }||z
  k    r n | ||||k    |           1 | ||           |                                   r|                                 }|j        r{t;          ||          \  }}}|r(d}t!          t          |j                  |          \  }}|r%|r! t<          |          }|t>          ur|d<   d S %|r)tA          | |          }n<|tC          d
          k    r	d|_        VtA          | |          }ntE          |           }|tF          k    rtI          |||d          }na|tJ          k    rtI          |||d          }nC|t<          tL          tN          fv r-|t<          k    r r|s ||          }|t>          ur|d<   d S )Nr   r   r%      r'   r(   T)rc   z  No matches/)(curs_set
has_colorsstart_coloruse_default_colors	init_pairCOLOR_GREENCOLOR_YELLOWCOLORSCOLOR_WHITErK   cleargetmaxyxrI   r   rC   rD   rX   	TypeErrorra   rb   r,   addnstrA_DIMerrorrE   r+   refreshr   rL   rp   r   _KEEPr   r   r   r   r\   r   r   r   )!r   rV   r]   rc   labelsmax_ymax_xrU   rY   items_startr^   draw_ifiltered_posr?   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;;;  F$7<<<$ $$ 1 111v7I2   $FM!^^F #-S1JQS U%00u "1OFFL999eJ//00 
 &7x%H%H"
D"-+feU6"R"R"RKK  D D D"-+feU"C"CKKKD  #1ek&9N&JKK 2!:|S]]! !  &, x {A~uqyRXR^____!<    -6-S]]ML<X)Y)YZZ- - ? ?(FL !.A,AEN222HVQ1;>>>>*Ku555    3 ,,..C} ? 5N"C5 51' # ,-M1B /v| L Lf2 2.FJ # %' +*3)J*G*G#*%#7#77>M!$4$*F$" %$!1&#!>!>C(, !1&#!>!>*622FV##28VZQSTTFFx''28VZQRSSFF
J
CCC++
+8+ 'i77Ge+++2a(kUs$   E E-,E-1!G 
G G )
rr   rs   rt   r,   r   r   wrapperry   KeyboardInterruptrx   )r   r   r   r   r   r   r   r   fallbackcancel_valuer   r   r   r   r   r   s   ````````   ` @@@r   _run_curses_menur   ^  sM   n 9 ^T 9^c->P>PT^>^Jvi	 i	 i	 i	 i	 i	 i	 i	 i	 i	 i	 i	 i	 i	 i	 i	V 	u#0#35#@#@}QlR      xzzs   AB C-CC)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        }|                                r||                    d          z  }|                     dd|dz
  |           |                     ddd|dz
  |j                   n# |j        $ r Y nw xY wdS )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    	ME  "" .**1---NN1a	5999NN1L	6<   
 | 	 	 	D	qs   A0A8 8
BBc                 "   dd l }|
v rdnd}|rdnd}d| d| d|          }|j        }	|r3|j        }	|                                r|	|                    d          z  }		 |                     |d||dz
  |	           d S # |j        $ r Y d S w xY w)Nr   u   ✓r      →z [z] r   r   A_NORMALr   r   r   r   r   )r   r   r?   	is_cursorr   r   checkarrowlineattrchosenr6   s             r   	_draw_rowz#curses_checklist.<locals>._draw_row;  s    f#"+/5//E//U1X// 	-=D  "" -))!,,,	NN1auqy$77777| 	 	 	DD	s   "B   
BBc                 B   dd l }	            }|rzt          d|t          |          z
  dz
            }|j        }|                                r||                    d          z  }|                     |dz
  ||||z
  dz
  |           d S d S # |j        $ r Y d S w xY w)Nr   r   r'   )r   ra   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 REC$4$44q899$$&& 2V..q111Euqy"k52:>5QQQQQR R | 	 	 	DD	s   BB 
BBc                     | t           k    r                    |h           t          S | t          k    rt	                    S S N)r   symmetric_difference_updater   r   set)r   rV   r   r   s     r   
_on_actionz$curses_checklist.<locals>._on_actionX  sF    Z..x88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,   rR   )
r   r6   r   r   r   r   r   r   r   r   s
   `````    @r   curses_checklistr     s   & X]]F                     u:: &-A$-7LL4y//^^^^^^^^#   r   )r   descriptionr   r   r   c                    	 g 	|r|                                 	d	 fd	}fd}fd}t          t                    |||d fdrt                    n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        }|                                r||                    d          z  }|                     |d|dz
  |           |dz  }	D ]3}||dz
  k    r n'|                     |d||dz
  |j                   |dz  }4
r||j        rd|j         d}n
rd}nd}|                     |d||dz
  |j                   |dz  }n# |j	        $ r Y nw xY w|dz   S )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   rL   r   r   r   )r   r   r   rc   r   rowr   dlinehint
desc_linesr   r   s            r   r   z&curses_radiolist.<locals>._draw_header  sV   	ME  "" .**1---NN35%!)U;;;1HC $  %!)##EsAueaiIIIq Qf0V]0`FL``` QZPNN34FLAAA1HCC| 	 	 	D	 Qws   CC 
C%$C%c                 &   dd l }|k    rdnd}|rdnd}d| d| d
|          }|j        }	|r3|j        }	|                                r|	|                    d          z  }		 |                     |d||dz
  |	           d S # |j        $ r Y d S w xY w)	Nr   u   ●u   ○r   r   z (z) r   r   )r   r   r?   r   r   r   radior   r   r   r6   r   s             r   r   z#curses_radiolist.<locals>._draw_row  s    MMx%.3/5//E//U1X// 	-=D  "" -))!,,,	NN1auqy$77777| 	 	 	DD	s   $B 
BBc                 ,    | t           t          fv r|S S r   )r   r   )r   rV   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Y r   
r   r   r   r   r   r   r   r   r   r   r   )
splitlinesr   r,   rC   )
r   r6   r   r   r   r   r   r   r   r   s
   ```` `   @r   curses_radiolistr  n  s    . !J . ++--
       <         
 u:: YYYYYYY#%/9d5kkkT   r   c           	         t          t          d|  t          j                             t          t          dt          j                             t          |          D ]C\  }}||k    rt          dt          j                  nd}t          d| d|dz   dd	|            Dt                       	 t          t          d
|dz    dt          j                                                            }|s|S t          |          dz
  }d|cxk    rt          |          k     rn n|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DIMrE   GREENinputrB   intr,   
ValueErrorr   EOFError)	r   r6   r   r   r?   r
   markervalr0   s	            r   r   r     si    
%u
.
.///	%96:
F
FGGGe$$ 1 1545MMz6<000z/6//AE/////0000	GGG	E@x!|@@@&*MMNNTTVV 	O#hhl    c%jj     J)84   s   4AD+ 80D+ )D+ +EE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                    n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                 P   dd l }	 |j        }|                                r||                    d          z  }|                     dd|dz
  |           r||j        rd|j         d}nrd}nd}|                     dd||dz
  |j                   n# |j        $ r Y nw xY wdS )	Nr   r   r   r   r   u8     ↑↓ navigate  ENTER confirm  / search  ESC/q cancelu.     ↑↓ navigate  ENTER confirm  ESC/q cancelr'   )	r   r   r   r   r   rL   r   r   r   )	r   r   r   rc   r   r   r   r   r   s	          r   r   z*curses_single_select.<locals>._draw_header  s    	ME  "" .**1---NN1a	5999 Hf0V]0`FL``` HQGNN1auqy&,????| 	 	 	D	qs   BB 
B#"B#c                    dd l }|rdnd}d| d	|          }|j        }|r3|j        }|                                r||                    d          z  }	 |                     |d||dz
  |           d S # |j        $ r Y d S w xY w)Nr   r   r   r   r   )
r   r   r?   r   r   r   r   r   r   	all_itemss
            r   r   z'curses_single_select.<locals>._draw_row  s    "+)5))9Q<)) 	-=D  "" -))!,,,	NN1auqy$77777| 	 	 	DD	s   A5 5
BBc                 V    | t           k    r
|k    rd n|S | t          k    rd S t          S r   )r   r   r   )r   rV   
cancel_idxs     r   r   z(curses_single_select.<locals>._on_action  s9    Z "Z//44V;Z4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P r   r   r   )rC   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PPPPP)3=d9ooo   r   r  c                    t          d|  d           t          |d          D ]\  }}t          d| d|            t                       	 t          dt          |           d                                          }|sdS t          |          dz
  }d	|cxk    rt          |          k     rn n||k     r|S ||k    rdS n# t          t          t          f$ r Y nw xY wdS )
z/Text-based numbered fallback for single-select.r  
r   r  r  z  Choice [1-r  Nr   )	r  rE   r  r,   rB   r  r  r   r  )r   r6   r  r?   r
   r  r0   s          r   r  r  0  s+    




eQ'' ! !51    	GGG
23u::2223399;; 	4#hhl    c%jj     S:%5%5J*4 )84   4s   4C 6C ;C CCc           	      \   t          |          }t          t          d|  t          j                             t          t          dt          j                             	 t          |          D ]A\  }}||v rt          dt          j                  nd}t          d| d|dz   d	d
|            B|r7 ||          }	|	r*t          t          d|	 t          j                             t                       	 t          t          dt          j                            	                                }
|
snut          |
          dz
  }d|cxk    rt          |          k     rn n|                    |h           n# t          t          t          f$ r |cY S w xY wt                       J|S )z8Text-based toggle fallback for terminals without curses.r  z&  Toggle by number, Enter to confirm.
Tu   [✓]z[ ]r  r   r   r  r  z"  Toggle # (or Enter to confirm): r   )r   r  r	   r   r	  r
  rE   r  r  rB   r  r,   r   r  r   r  )r   r6   r   r   r   r   r?   r
   r  r   r  r0   s               r   r   r   H  s    ]]F	%u
.
.///	%96:
F
FGGG!%(( 	5 	5HAu56&[[U7FL111eF3v33A333E334444 	?#)F++K ?e0;00&*==>>>	"BFJOOPPVVXXC c((Q,CC$$$$#e**$$$$$22C5999-x8 	" 	" 	"!!!!	"%( Ms   ;;E= 7AE= =FF)r   N)r   r   )/rQ   rr   dataclassesr   typingr   r   r   r   hermes_cli.colorsr   r	   rT   rR   r   	frozensetr   r  r!   floatr1   r5   rI   rK   tuplerX   r\   rb   rp   ry   r   r   r   r   r   r   r   r   objectr   r   r   r  r   r  r  r   r9   r   r   <module>r(     s    


 ! ! ! ! ! ! 0 0 0 0 0 0 0 0 0 0 0 0 + + + + + + + +# c d    , 7##N NS NT N N N N(3s 33 3s 3ut| 3 3 3 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 I I I##".#
4t# # # #L   0 



4S 4 4 4 4&/# /# / / / /h 	  r r r r rt '+59X X XX9X #hX
 HtOX #c(S12X 	XX X X X| [
 "&"[ [ [[9[ [
 $J[ t[ [ 	[ [ [ [|9  	
 	   : G
 !G G GG9G G
 G G 	4ZG G G GT9  	4Z	   : 6:    9  #h  H	 
 #c(S12  	X           r   