
    "<i7                        d Z ddlmZ ddlZddlmZ dZdZdZdZ	d	Z
d
ZdZdZdZdZ	 	 	 d3d4dZd5d6dZd7d8d!Z	 	 	 d9d:d&Zd;d)Zd<d,Zd=d>d2ZdS )?z
Shared UI utilities for FastMCP HTML pages.

This module provides reusable HTML/CSS components for OAuth callbacks,
consent pages, and other user-facing interfaces.
    )annotationsNHTMLResponsez0https://gofastmcp.com/assets/brand/blue-logo.pnga  
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }

    body {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
        margin: 0;
        padding: 0;
        min-height: 100vh;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #f9fafb;
        color: #0a0a0a;
    }

    .container {
        background: #ffffff;
        border: 1px solid #e5e7eb;
        padding: 3rem 2.5rem;
        border-radius: 1rem;
        box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
        text-align: center;
        max-width: 36rem;
        margin: 1rem;
        width: 100%;
    }

    @media (max-width: 640px) {
        .container {
            padding: 2rem 1.5rem;
            margin: 0.5rem;
        }
    }

    .logo {
        width: 64px;
        height: auto;
        margin-bottom: 1.5rem;
        display: block;
        margin-left: auto;
        margin-right: auto;
    }

    h1 {
        font-size: 1.5rem;
        font-weight: 600;
        margin-bottom: 1.5rem;
        color: #111827;
    }
a  
    .button-group {
        display: flex;
        gap: 0.75rem;
        margin-top: 1.5rem;
        justify-content: center;
    }

    button {
        padding: 0.75rem 2rem;
        font-size: 0.9375rem;
        font-weight: 500;
        border-radius: 0.5rem;
        border: none;
        cursor: pointer;
        transition: all 0.15s;
        font-family: inherit;
    }

    button:hover {
        transform: translateY(-1px);
        box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    }

    .btn-approve, .btn-primary {
        background: #10b981;
        color: #ffffff;
        min-width: 120px;
    }

    .btn-deny, .btn-secondary {
        background: #6b7280;
        color: #ffffff;
        min-width: 120px;
    }
a  
    .info-box {
        background: #f0f9ff;
        border: 1px solid #bae6fd;
        border-radius: 0.5rem;
        padding: 1rem;
        margin-bottom: 1.5rem;
        text-align: left;
        font-size: 0.9375rem;
        line-height: 1.5;
        color: #374151;
    }

    .info-box p {
        margin-bottom: 0.5rem;
    }

    .info-box p:last-child {
        margin-bottom: 0;
    }

    .info-box.centered {
        text-align: center;
    }

    .info-box.error {
        background: #fef2f2;
        border-color: #fecaca;
        color: #991b1b;
    }

    .info-box strong {
        color: #0ea5e9;
        font-weight: 600;
    }

    .info-box .server-name-link {
        color: #0ea5e9;
        text-decoration: underline;
        font-weight: 600;
        cursor: pointer;
        transition: opacity 0.15s;
    }

    .info-box .server-name-link:hover {
        opacity: 0.8;
    }

    /* Monospace info box - gray styling with code font */
    .info-box-mono {
        background: #f9fafb;
        border: 1px solid #e5e7eb;
        border-radius: 0.5rem;
        padding: 0.875rem;
        margin: 1.25rem 0;
        font-size: 0.875rem;
        color: #6b7280;
        font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
        text-align: left;
    }

    .info-box-mono.centered {
        text-align: center;
    }

    .info-box-mono.error {
        background: #fef2f2;
        border-color: #fecaca;
        color: #991b1b;
    }

    .info-box-mono strong {
        color: #111827;
        font-weight: 600;
    }

    .warning-box {
        background: #f0f9ff;
        border: 1px solid #bae6fd;
        border-radius: 0.5rem;
        padding: 1rem;
        margin-bottom: 1.5rem;
        text-align: center;
    }

    .warning-box p {
        margin-bottom: 0.5rem;
        line-height: 1.5;
        color: #6b7280;
        font-size: 0.9375rem;
    }

    .warning-box p:last-child {
        margin-bottom: 0;
    }

    .warning-box strong {
        color: #0ea5e9;
        font-weight: 600;
    }

    .warning-box a {
        color: #0ea5e9;
        text-decoration: underline;
        font-weight: 600;
    }

    .warning-box a:hover {
        color: #0284c7;
        text-decoration: underline;
    }
a  
    .status-message {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.75rem;
        margin-bottom: 1.5rem;
    }

    .status-icon {
        font-size: 1.5rem;
        line-height: 1;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2rem;
        height: 2rem;
        border-radius: 0.5rem;
        flex-shrink: 0;
    }

    .status-icon.success {
        background: #10b98120;
    }

    .status-icon.error {
        background: #ef444420;
    }

    .message {
        font-size: 1.125rem;
        line-height: 1.75;
        color: #111827;
        font-weight: 600;
        text-align: left;
    }
a)  
    .detail-box {
        background: #f9fafb;
        border: 1px solid #e5e7eb;
        border-radius: 0.5rem;
        padding: 1rem;
        margin-bottom: 1.5rem;
        text-align: left;
    }

    .detail-row {
        display: flex;
        padding: 0.5rem 0;
        border-bottom: 1px solid #e5e7eb;
    }

    .detail-row:last-child {
        border-bottom: none;
    }

    .detail-label {
        font-weight: 600;
        min-width: 160px;
        color: #6b7280;
        font-size: 0.875rem;
        flex-shrink: 0;
        padding-right: 1rem;
    }

    .detail-value {
        flex: 1;
        font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
        font-size: 0.75rem;
        color: #111827;
        word-break: break-all;
        overflow-wrap: break-word;
    }
aa  
    .redirect-section {
        background: #fffbeb;
        border: 1px solid #fcd34d;
        border-radius: 0.5rem;
        padding: 1rem;
        margin-bottom: 1.5rem;
        text-align: left;
    }

    .redirect-section .label {
        font-size: 0.875rem;
        color: #6b7280;
        font-weight: 600;
        margin-bottom: 0.5rem;
        display: block;
    }

    .redirect-section .value {
        font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
        font-size: 0.875rem;
        color: #111827;
        word-break: break-all;
        margin-top: 0.25rem;
    }
u  
    details {
        margin-bottom: 1.5rem;
        text-align: left;
    }

    summary {
        cursor: pointer;
        font-size: 0.875rem;
        color: #6b7280;
        font-weight: 600;
        list-style: none;
        padding: 0.5rem;
        border-radius: 0.25rem;
    }

    summary:hover {
        background: #f9fafb;
    }

    summary::marker {
        display: none;
    }

    summary::before {
        content: "▶";
        display: inline-block;
        margin-right: 0.5rem;
        transition: transform 0.2s;
        font-size: 0.75rem;
    }

    details[open] summary::before {
        transform: rotate(90deg);
    }
z}
    .close-instruction, .help-text {
        font-size: 0.875rem;
        color: #6b7280;
        margin-top: 1.5rem;
    }
a.  
    .help-link-container {
        position: fixed;
        bottom: 1.5rem;
        right: 1.5rem;
        font-size: 0.875rem;
    }

    .help-link {
        color: #6b7280;
        text-decoration: none;
        cursor: help;
        position: relative;
        display: inline-block;
        border-bottom: 1px dotted #9ca3af;
    }

    @media (max-width: 640px) {
        .help-link {
            background: #ffffff;
            padding: 0.25rem 0.5rem;
            border-radius: 0.25rem;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        }
    }

    .help-link:hover {
        color: #111827;
        border-bottom-color: #111827;
    }

    .help-link:hover .tooltip {
        opacity: 1;
        visibility: visible;
    }

    .tooltip {
        position: absolute;
        bottom: 100%;
        right: 0;
        left: auto;
        margin-bottom: 0.5rem;
        background: #1f2937;
        color: #ffffff;
        padding: 0.75rem 1rem;
        border-radius: 0.5rem;
        font-size: 0.8125rem;
        line-height: 1.5;
        width: 280px;
        max-width: calc(100vw - 3rem);
        opacity: 0;
        visibility: hidden;
        transition: opacity 0.2s, visibility 0.2s;
        box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
        text-align: left;
    }

    .tooltip::after {
        content: '';
        position: absolute;
        top: 100%;
        right: 1rem;
        border: 6px solid transparent;
        border-top-color: #1f2937;
    }

    .tooltip-link {
        color: #60a5fa;
        text-decoration: underline;
    }
FastMCP Tdefault-src 'none'; style-src 'unsafe-inline'; img-src https: data:; base-uri 'none'contentstrtitleadditional_styles
csp_policyreturnc                    t          j        |          }|rdt          j        |d           dnd}d| dt           d| d	| d
|  dS )az  
    Create a complete HTML page with FastMCP styling.

    Args:
        content: HTML content to place inside the page
        title: Page title
        additional_styles: Extra CSS to include
        csp_policy: Content Security Policy header value.
            If empty string "", the CSP meta tag is omitted entirely.

    Returns:
        Complete HTML page as string
    z4<meta http-equiv="Content-Security-Policy" content="T)quotez" />r   z
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>z%</title>
        <style>
            z
            z
        </style>
        z 
    </head>
    <body>
        z
    </body>
    </html>
    )htmlescapeBASE_STYLES)r	   r   r   r   csp_metas        /Users/kimhansen/Desktop/03 Workspace/ceo-agents/chl-effectiveness/mcp-servers/whoop/.venv/lib/python3.11/site-packages/fastmcp/utilities/ui.pycreate_pager     s    & KE
 	ht{:]a?b?b?bhhhh        
  
       icon_url
str | Nonealt_textc                r    | pt           }t          j        |          }dt          j        |           d| dS )zCreate logo HTML.

    Args:
        icon_url: Optional custom icon URL. If not provided, uses the FastMCP logo.
        alt_text: Alt text for the logo image.

    Returns:
        HTML for logo image tag.
    z
<img src="z" alt="z" class="logo" />)FASTMCP_LOGO_URLr   r   )r   r   urlalts       r   create_logor     sA     
&&C
+h

CGC((GGGGGGr   Tmessage
is_successboolc                Z    t          j        |           } |rdnd}|rdnd}d| d| d|  dS )	u   
    Create a status message with icon.

    Args:
        message: Status message text
        is_success: True for success (✓), False for error (✕)

    Returns:
        HTML for status message
    u   ✓u   ✕successerrorzK
        <div class="status-message">
            <span class="status-icon ">z)</span>
            <div class="message">z</div>
        </div>
    r   r   )r    r!   icon
icon_classs       r   create_status_messager*     sg     k'""G)55ED(5gJ&0 48  #*   r   Fis_errorcentered	monospacec                    t          j        |           } |rdnd}|g}|r|                    d           |r|                    d           d                    |          }d| d|  dS )	aG  
    Create an info box.

    Args:
        content: HTML content for the info box
        is_error: True for error styling, False for normal
        centered: True to center the text, False for left-aligned
        monospace: True to use gray monospace font styling instead of blue

    Returns:
        HTML for info box
    zinfo-box-monozinfo-boxr%   r,    z<div class="r&   </div>)r   r   appendjoin)r	   r+   r,   r-   
base_classclasses	class_strs          r   create_info_boxr6     s    $ k'""G$-=:JlG  w #z"""!!I6)66w6666r   rowslist[tuple[str, str]]c                L    d                     d | D                       }d| dS )z
    Create a detail box with key-value pairs.

    Args:
        rows: List of (label, value) tuples

    Returns:
        HTML for detail box
    
c              3  v   K   | ]4\  }}d t          j        |           dt          j        |           dV  5dS )zH
        <div class="detail-row">
            <div class="detail-label">z.:</div>
            <div class="detail-value">z</div>
        </div>
        Nr'   ).0labelvalues      r   	<genexpr>z$create_detail_box.<locals>.<genexpr>B  sq         E5	'+{5'9'9	 	 (,{5'9'9	 	 	     r   z<div class="detail-box">r0   r2   )r7   	rows_htmls     r   create_detail_boxrB   8  sH     		   !    I 8i7777r   buttonslist[tuple[str, str, str]]c                L    d                     d | D                       }d| dS )z
    Create a group of buttons.

    Args:
        buttons: List of (text, value, css_class) tuples

    Returns:
        HTML for button group
    r:   c              3  6   K   | ]\  }}}d | d| d| dV  dS )z+<button type="submit" name="action" value="z	" class="r&   z	</button>N )r<   textr>   	css_classs       r   r?   z&create_button_group.<locals>.<genexpr>Y  sY        "D% 	cebbibbSWbbb     r   z<div class="button-group">r0   r@   )rC   buttons_htmls     r   create_button_grouprK   O  sF     99  &-    L
 =<<<<r      r   status_codeintr   c                *    t          | |ddi          S )a'  
    Create an HTMLResponse with security headers.

    Adds X-Frame-Options: DENY to prevent clickjacking attacks per MCP security best practices.

    Args:
        html: HTML content to return
        status_code: HTTP status code

    Returns:
        HTMLResponse with security headers
    zX-Frame-OptionsDENY)r	   rM   headersr   )r   rM   s     r   create_secure_html_responserR   a  s)     "F+   r   )r   r   r   )
r	   r
   r   r
   r   r
   r   r
   r   r
   )Nr   )r   r   r   r
   r   r
   )T)r    r
   r!   r"   r   r
   )FFF)
r	   r
   r+   r"   r,   r"   r-   r"   r   r
   )r7   r8   r   r
   )rC   rD   r   r
   )rL   )r   r
   rM   rN   r   r   )__doc__
__future__r   r   starlette.responsesr   r   r   BUTTON_STYLESINFO_BOX_STYLESSTATUS_MESSAGE_STYLESDETAIL_BOX_STYLESREDIRECT_SECTION_STYLESDETAILS_STYLESHELPER_TEXT_STYLESTOOLTIP_STYLESr   r   r*   r6   rB   rK   rR   rG   r   r   <module>r^      s^    # " " " " "  , , , , , , F 5p#Lod$ N% P 8#L FV l	- - - - -`H H H H H    2 	7 7 7 7 7:8 8 8 8.= = = =$      r   