
    `gj9L                       d Z ddlmZ ddlZddlmZmZmZmZm	Z	 dZ
dZdZdZd	Zd
Zeeef   Z ej$                  d      Z ej$                  d      Z ej$                  d      Z ej$                  d      Z ej$                  d      Z ej$                  d      Z ej$                  d      Zd'dZd(dZ ej$                  d      Z ej$                  d      Z ej$                  d      Z ej$                  d      Z ej$                  d      Z d)dZ!d*dZ"d+dZ#d*dZ$d,dZ%d-dZ&d*dZ'd.d Z(d/d!Z)d0d"Z*d1d#Z+d2d$Z,	 d3	 	 	 d4d%Z-d5d&Z.y)6u  Render agent markdown into Slack Block Kit blocks.

Opt-in (``slack.extra.rich_blocks: true``) alternative to the flat mrkdwn
``text`` payload produced by :meth:`SlackAdapter.format_message`.  Block Kit
gives us real structural primitives — section headers, dividers, and true
*nested* lists via ``rich_text`` — that plain mrkdwn can only approximate.

Design constraints (why this module is deliberately conservative):

* **Markdown pipe-tables render as native ``table`` blocks** — real grid
  cells with per-column alignment and inline-formatted ``rich_text`` content.
  A table that exceeds Slack's limits (100 rows / 20 cols / 10k aggregate
  cell chars) or won't parse falls back to aligned monospace
  ``rich_text_preformatted`` so a large table never breaks the message.
* **Slack caps a message at 50 blocks** and a ``section``/text object at 3000
  characters.  :func:`render_blocks` enforces both and, if the content simply
  cannot be expressed within them, returns ``None`` so the caller falls back
  to the plain-text path.  A rich render is a nice-to-have; it must never lose
  a message.
* **Every blocks payload MUST ship a ``text`` fallback.**  Slack uses it for
  notifications, screen readers, and old clients.  This module only builds the
  ``blocks`` list; the adapter pairs it with the existing mrkdwn string.

The renderer never raises: any unexpected input degrades to ``None`` (caller
uses plain text).  It is a pure function of its input — no Slack client, no
adapter state — so it is trivially unit-testable.
    )annotationsN)AnyDictListOptionalTuple2   i     d      i'  z ^\s{0,3}([-*_])(?:\s*\1){2,}\s*$z!^\s{0,3}(#{1,6})\s+(.+?)\s*#*\s*$z^\s*(`{3,}|~{3,})(.*)$z^(\s*)(\d+)[.)]\s+(.*)$z^(\s*)[-*+]\s+(.*)$z^\s{0,3}>\s?(.*)$z1^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)+\|?\s*$c                l    t        t        j                  |       xs t        j                  |             S )z=True if ``line`` is a markdown list item (bullet or ordered).)bool
_BULLET_REmatch_ORDERED_RE)lines    T/root/.hermes/venv/lib/python3.12/site-packages/plugins/platforms/slack/block_kit.py_is_list_liner   :   s(    
  &A+*;*;D*ABB    c                J    d}| D ]  }||dk(  rdndz  } t        |dz  d      S )zHMap leading whitespace to a nesting level (2 spaces or 1 tab per level).r   	            )min)spaceswidthchs      r   _indent_levelr    ?   s9    E (bDja'(uz1r   z	`([^`]+)`z7(?<!!)\[([^\]]+)\]\(([^()\s]+(?:\([^()]*\)[^()\s]*)*)\)z(?:\*\*|__)(.+?)(?:\*\*|__)z?(?<![\*_])(?:\*|_)(?![\*_\s])(.+?)(?<![\*_\s])(?:\*|_)(?![\*_])z	~~(.+?)~~c                l    g ddfdd	fd}d	fdd	fd || i        xs d| dgS )
a
  Parse a run of inline markdown into rich_text section child elements.

    Produces ``text`` elements (optionally styled bold/italic/strike/code) and
    ``link`` elements.  Unmatched markup is emitted verbatim as plain text, so
    this never loses characters.
    c                F    | sy d| d}|r||d<   j                  |       y )Ntexttyper#   style)append)sr&   elelementss      r   	emit_textz#_inline_elements.<locals>.emit_text\   s+    &,a8BwKr   c                   d}t         j                  |       D ]X  } | ||j                          |       t        |      }d|d<    |j	                  d      |xs d        |j                         }Z  | |d  |       y )Nr   Tcoder   )_INLINE_CODE_REfinditerstartdictgroupend)r(   r&   posm
code_style_walk_linksr+   s        r   walkz_inline_elements.<locals>.walkg   s     ))!, 	A#aggi(%0eJ!%Jvaggaj*"45%%'C	 	AcdGU#r   c                4   d}t         j                  |       D ]q  } | ||j                          |       d|j                  d      |j                  d      d}|rt	        |      |d<   j                  |       |j                         }s  | |d  |       y )Nr   linkr   r   )r%   urlr#   r&   )_LINK_REr/   r0   r2   r1   r'   r3   )r(   r&   r4   r5   link_el_walk_emphasisr*   s        r   r7   z%_inline_elements.<locals>._walk_linksr   s    ""1% 	A1S+U3/5aggajRSRYRYZ[R\&]G#'; OOG$%%'C	 	qw&r   c                b   | sy t         dft        dft        dffD ]v  \  }}|j                  |       }|s | d |j	                          |       t        |      }d||<    |j                  d      |        | |j                         d  |        y   | |rt        |             y d        y )NboldstrikeitalicTr   )_BOLD_RE
_STRIKE_RE
_ITALIC_REsearchr0   r1   r2   r3   )r(   r&   rxkeyr5   inner_styler>   r+   s         r   r>   z(_inline_elements.<locals>._walk_emphasis}   s    !6*Z,BZQYDZ[ 	GB		!Aq!'')}e4"5k#'C qwwqz;7q{E2	 	!ET%[4t4r   r#   r$   N)r(   strr&   zOptional[Dict[str, bool]]returnNone)r(   rK   r&   zDict[str, bool]rL   rM    )r#   r8   r>   r7   r*   r+   s     @@@@r   _inline_elementsrO   S   s<     &(H	$	'5 	rN7677r   c                    t        j                  dd|       j                         }t        |      t        kD  r|d t        dz
   dz   }dd|ddd	S )
Nz[*_~`] r   u   …header
plain_textT)r%   r#   emojir$   )resubstriplenMAX_HEADER_TEXT)r#   cleans     r   _header_blockr[      sS    FF9b$'--/E
5zO#+!+,u4|UUY&Z[[r   c                 
    ddiS )Nr%   dividerrN   rN   r   r   _divider_blockr^      s    Ir   c                :    ddd| j                  d      dgdgdS )N	rich_textrich_text_preformattedr#   
r$   r%   r*   )rstripr#   s    r   _preformatted_blockrf      s4      1&,dkk$6GHI
 r   c                    g }t        |       D ]5  \  }}|r|j                  ddd       |j                  t        |             7 dd|dgdS )Nr#   rb   r$   r`   rich_text_quoterc   )	enumerater'   extendrO   )linessection_childrenilns       r   _quote_blockro      se    -/5! 62##VT$BC 0 456
 /=MNO r   c                    g }d}d}| D ]Q  \  }}}||f}||k7  rd|rdnd|g d}|j                  |       |}|J |d   j                  dt        |      d       S d	|dS )
a0  Build ONE rich_text block from consecutive list items.

    ``items`` is a list of ``(indent, ordered, text)``.  Each contiguous run
    sharing the same (indent, ordered) becomes a ``rich_text_list`` element;
    indentation changes start a new element, which is how Slack renders true
    nesting.
    Nrich_text_listorderedbullet)r%   r&   indentr*   r*   rich_text_sectionrc   r`   )r'   rO   )itemsr*   curcur_keyrt   rr   r#   rH   s           r   _list_blockry      s     &(H$(C*.G!& 
w'>(&-8 	C OOC GJ(6Ft6LM	

  X66r   c                    dd| ddS )Nsectionmrkdwnr$   rN   re   s    r   _section_blockr}      s    $'GHHr   c                J   g }| j                         j                  d      j                  d      D ]o  }|j                         }|j                  d      }|j                  d      }|r|r|j	                  d       K|r|j	                  d       _|j	                  d       q |S )zParse a markdown separator row (``|:--|:-:|--:|``) into column aligns.

    Returns a list of ``"left"``/``"center"``/``"right"`` per column.
    |:centerrightleft)rW   split
startswithendswithr'   )sep_linealignscellcr   r   s         r   _parse_alignmentr      s    
 F &&s+11#6 	"JJL||C 

3EMM(#MM'"MM&!	" Mr   c                    | j                         j                  d      j                  dd      }|j                  d      D cg c]"  }|j                         j                  dd      $ c}S c c}w )zSplit a markdown table row into trimmed cell strings.

    Respects backslash-escaped pipes (``\|``) so they aren't treated as
    column separators.
    r   z\|z PIPE )rW   replacer   )row	protectedr   s      r   
_split_rowr      sW     		!!#&..unEI<EOOC<PQqAGGInc2QQQs   'A-c                &    ddt        |       dgdS )z=A ``rich_text`` table cell carrying inline-formatted content.r`   ru   rc   )rO   re   s    r   _rich_text_cellr      s$     (6Ft6LM
 r   c                   | D cg c]  }|j                         st        |        }}|syt        d |D              }t        |      t        kD  s	|t
        kD  ry|D ]#  }|j                  dg|t        |      z
  z         % t        d |D              }|t        kD  ryt        |      }g }t        t        |t
                    D ]1  }|t        |      k  r||   nd}	|j                  |	dk7  rd|	ind       3 d|D 
cg c]  }
|
D cg c]  }t        |       c} c}}
d}t        d	 |D              r||d
<   |S c c}w c c}w c c}}
w )u  Build a native Slack ``table`` block from markdown pipe-table rows.

    ``rows`` includes the header row (index 0) and body rows; ``sep_line`` is
    the ``|---|`` alignment row (already consumed by the caller).  Returns
    ``None`` when the table exceeds Slack's limits (100 rows / 20 cols /
    10,000 aggregate cell chars) or parses to nothing — the caller then falls
    back to the monospace preformatted rendering.
    Nc              3  2   K   | ]  }t        |        y wrJ   rX   .0rs     r   	<genexpr>z_table_block.<locals>.<genexpr>       '1A'   rQ   c              3  @   K   | ]  }|D ]  }t        |         y wrJ   r   r   r   r   s      r   r   z_table_block.<locals>.<genexpr>  s      8a8c!f8f8s   r   aligntable)r%   rowsc              3  $   K   | ]  }|d u 
 y wrJ   rN   )r   css     r   r   z_table_block.<locals>.<genexpr>+  s     
4b2T>
4s   column_settings)rW   r   maxrX   MAX_TABLE_ROWSMAX_TABLE_COLSrj   sumMAX_TABLE_CHARSr   ranger   r'   r   any)r   r   r   parsedncolstotal_charsr   r   r   r   r   r   blocks                r   _table_blockr     sK    &*7QWWYjm7F7'''E
6{^#u~'= *	"Q()* 8f88K_$h'F68O3un-. NV_q	& 	5F?/M	N DJKSC8D/$'8KE 
4O
44#2 L9 80 9Ks"   D>D>?	EEEEc           	       	 g }| D ]  t              }|j                  |         |sdj                  |       S t        d |D              }|D ]#  j	                  dg|t              z
  z         % t        |      D cg c]  t        fd|D               c}	g }t        |      D ]  \  }dj                  	fdt        |      D              }|j                  |j                                |dk(  sO|j                  dj                  	fd	t        |      D                      dj                  |      S c c}w )
zERender markdown pipe-table rows as aligned monospace text (fallback).rb   c              3  2   K   | ]  }t        |        y wrJ   r   r   s     r   r   z _render_table.<locals>.<genexpr>8  r   r   rQ   c              3  :   K   | ]  }t        |           y wrJ   r   r   s     r   r   z _render_table.<locals>.<genexpr>;  s     ,#ad),s   z | c              3  L   K   | ]  }|   j                  |           y wrJ   )ljust)r   r   r   widthss     r   r   z _render_table.<locals>.<genexpr>>  s"     FA!A$**VAY/Fs   !$r   z-+-c              3  .   K   | ]  }d |   z    yw)-NrN   )r   r   r   s     r   r   z _render_table.<locals>.<genexpr>A  s     'NAfQi'Ns   )	r   r'   joinr   rj   rX   r   ri   rd   )
r   r   cellsr   r   	out_linesrir   r   r   s
       `   @@r   _render_tabler   0  s"    F 1e yy'''E *	"Q()*6;ElCc,V,,CFI6" PAzzFuFF'7UZZ'Nu'NNO	P
 99Y Ds   Ec                r
   | r| j                         sy|xs d 	 g | j                  dd      j                  d      }d}t        |      }g dfd}||k  r||   }|j                         s |        |dz  }(t        j                  |      }|r |        |j                  d      }g }	|dz  }||k  rc||   j                         j                  |      sA|	j                  ||          |dz  }||k  r#||   j                         j                  |      sA|dz  }j                  t        dj                  |	                   t        j                  |      r' |        j                  t                      |dz  }1t        j                  |      }
|
r7 |        j                  t        |
j                  d                   |dz  }d	|v r|dz   |k  rt         j                  ||dz            r |        |}||dz      }|g}|dz  }||k  rSd	||   v rL||   j                         r9|j                  ||          |dz  }||k  rd	||   v r||   j                         r9t#        ||      }|j                  |       n#j                  t        t%        |                   \t&        j                  |      rp |        g }||k  rFt&        j                  ||         }|sn+|j                  |j                  d             |dz  }||k  rFj                  t)        |             t+        |      r |        g }||k  r}t,        j                  ||         }t.        j                  ||         }|rB|j                  t1        |j                  d            d
|j                  d      f       |dz  }n|rA|j                  t1        |j                  d            d|j                  d      f       |dz  }n||   j                         rD||   j                  d      r0|r.|d   \  }}}|||dz   ||   j                         z   f|d<   |dz  }nh||   j                         sT|rR|dz   }||k  r1||   j                         s|dz  }||k  r||   j                         s||k  rt+        ||         r|}nnn||k  r}j                  t3        |             j                  |       |dz  }||k  r |        syt              t4        kD  ryS # t6        $ r Y yw xY w)uc  Convert agent markdown to a Slack Block Kit ``blocks`` list.

    Args:
        markdown: The agent's response text (standard markdown).
        mrkdwn_fn: Optional callable converting a markdown paragraph to Slack
            mrkdwn for ``section`` blocks (the adapter passes
            ``format_message``).  When ``None``, the raw paragraph text is used.

    Returns:
        A list of Block Kit block dicts, or ``None`` when the content is empty,
        exceeds Slack's structural limits, or hits an unexpected shape — the
        caller then falls back to the flat ``text`` payload.  Never raises.
    Nc                    | S rJ   rN   )r(   s    r   <lambda>zrender_blocks.<locals>.<lambda>^  s    ! r   z
rb   r   c                     sy dj                        j                         } j                          | sy  |       }t        |t              D ]  }j                  t        |              y )Nrb   )r   rW   clear_split_textMAX_SECTION_TEXTr'   r}   )r#   renderedchunkblocksfmtparas      r   
flush_paraz!render_blocks.<locals>.flush_parag  s_    99T?((*DJJL4yH$X/?@ 5nU345r   r   r   r   FT   ) r   r   )rL   rM   )rW   r   r   rX   	_FENCE_REr   r2   lstripr   r'   rf   r   _HR_REr^   
_HEADER_REr[   _TABLE_SEP_REr   r   	_QUOTE_REro   r   r   r   r    ry   
MAX_BLOCKS	Exception)markdown	mrkdwn_fnrk   rm   nr   r   fencemarkerbodyhm
header_rowr   trowsr   qlinesqmrv   bmomrt   rr   txtjr   r   r   s                           @@@r   render_blocksr   J  s   " 8>>+

$CO   .44T:J
	5 !e8D ::<Q OOD)EQ"$Q!eE!HOO$5$@$@$HKKa)FA !eE!HOO$5$@$@$H Q1$))D/BC ||D!n./Q !!$'BmBHHQK89Q d{q1uqy]-@-@q1u-N!
 Q<#Q!euQxE!HNN4DLLq*FA !euQxE!HNN4D
 %UH5$MM%(MM"5mE6J"KL t$$&!e"q2BMM"((1+.FA !e l623 T"57!e#))%(3B$**584BmBHHQK&@%RS%UVQmBHHQK&@$QR%TUQq)eAh.A.A+.NSX/4Ry,%+WcCi%(..BR6R$Sb	Q"1X^^-% E!eE!HNN,<FA  !eE!HNN,<q5]58%< !A!? !e@ k%01 KKFA_ !eb 	v;#  sE   C?T* ET* 0B-T* FT* 3T* 7T* 	T* T* (T* *	T65T6c                   t        |       |k  r| gS g }| }t        |      |kD  rQ|j                  dd|      }|dk  r|}|j                  |d|        ||d j                  d      }t        |      |kD  rQ|r|j                  |       |S )zLSplit ``text`` into <= ``limit``-char chunks on line, then hard, boundaries.rb   r   N)rX   rfindr'   r   )r#   limitout	remainingcuts        r   r   r     s    
4yEvCI
i.5
 oodAu-!8C

9Tc?#cdO**40	 i.5
  

9Jr   )r   rK   rL   r   )r   rK   rL   int)r#   rK   rL   zList[Dict[str, Any]])r#   rK   rL   Block)rL   r   )rk   	List[str]rL   r   )rv   zList[Tuple[int, bool, str]]rL   r   )r   rK   rL   r   )r   rK   rL   r   )r#   rK   rL   zDict[str, Any])r   r   r   rK   rL   zOptional[Block])r   r   rL   rK   rJ   )r   rK   rL   zOptional[List[Block]])r#   rK   r   r   rL   r   )/__doc__
__future__r   rU   typingr   r   r   r   r   r   r   rY   r   r   r   rK   r   compiler   r   r   r   r   r   r   r   r    r.   r<   rC   rE   rD   rO   r[   r^   rf   ro   ry   r}   r   r   r   r   r   r   r   rN   r   r   <module>r      s  8 # 	 3 3 
 S#X 
7	8RZZ<=
BJJ01	bjj34RZZ./
BJJ+,	

OPC
 "**\*2::PQ2::45RZZZ[
RZZ%
:8D\
	7:I&R%P 8 ee ePr   