
    [i|,                       U d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m
Z
 ddlmZ dZeej                  vrej                  j                  de       	 ddlmZmZ  e
d      Zedz  Zedz  ZdZd	ed<    ej2                  d      Z ej2                  d      Ze G d d             Ze G 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%d+dZ&	 	 d,	 	 	 	 	 	 	 	 	 d-d Z'g d!Z(y# e$ r d"d
Zd#dZY w xY w).uS  magazine-ppt-ko / build_deck — 슬라이드별 HTML + manifest.json 빌더.

IDS Phase 2의 G2 구현 게이트 모듈. 외부 API/SDK는 일절 임포트/호출하지 않으며,
Phase 1 ``satori-cardnews`` 스킬의 ``design_md_loader``를 import하여 디자인 토큰을
HTML/CSS에 주입한다.

핵심 계약 (IDS):
- §0.1 한글 100%: manifest 의 한글 슬롯 값이 HTML에 string-match로 존재
- §0.5 외부 API 차단: openai/anthropic/google/urlopen/requests/httpx 사용 금지

Public API:
    build(layout_names, variables_list, brand=None, output_dir=None) -> BuildResult
    )annotationsN)	dataclassfield)Path)Anyz*/home/jay/workspace/skills/satori-cardnews)fallback_tokensload_design_mdstrc                    t        d|        )Nzdesign_md_loader unavailable: )FileNotFoundError)brands    @/home/jay/workspace/skills/magazine-ppt-ko/scripts/build_deck.pyr	   r	   #   s    "@ HII    c                 (    dddddddddd	d
dddg dS )N#0f0f0f#171717#3ecf8e#ffffff#111111#666666
Pretendard          @   )smmdlgxl8px)primary	secondaryaccent
backgroundtext_primarytext_secondaryfont_display	font_bodyspacingborder_radiusshadow r,   r   r   r   r   &   s6     "#%'(%r2>"
 	
r   z*/home/jay/workspace/skills/magazine-ppt-ko	templateszregistry.jsonz('Pretendard', 'Noto Sans KR', sans-serif
FONT_STACKz&\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}u   [가-힣ᄀ-ᇿ㄰-㆏]c                  h    e Zd ZU dZded<   ded<   ded<    ee      Zded<    ee      Z	d	ed
<   y)SlideManifestEntryu    슬라이드별 manifest 항목.r
   filelayoutfamily)default_factory	list[str]korean_stringsdict[str, str]	variablesN)
__name__
__module____qualname____doc____annotations__r   listr6   dictr8   r,   r   r   r0   r0   H   s2    *
IKK %d ;NI; %d ;I~;r   r0   c                  D    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)BuildResultu   build() 반환 객체.r   
output_dirz
list[Path]
html_pathsmanifest_pathdict[str, Any]manifesttokensN)r9   r:   r;   r<   r=   r,   r   r   rA   rA   S   s#     r   rA   c                     t         j                  dd      5 } t        j                  |       }ddd       |S # 1 sw Y   S xY w)u   templates/registry.json 로드.rutf-8encodingN)REGISTRY_PATHopenjsonload)fdatas     r   _load_registryrS   a   s>    			C'		2 ,a#yy|,K,Ks	   8Ac                    | st        t                     S 	 t        |       }t        |      S # t        t        f$ r t        t                     cY S w xY w)u   brand 인자에 따라 design-md 토큰 해석. 실패 시 fallback.

    Args:
        brand: ``resources/design-md/{brand}/`` 디렉토리 이름. None이면 fallback.

    Returns:
        DesignTokens dict — 항상 유효한 토큰 dict 반환.
    )r?   r   r	   r   
ValueError)r   rG   s     r   _resolve_tokensrV   h   sP     O%&&'&F|z* 'O%&&'s   - "AAc                <    dfd}t         j                  ||       S )u   ``{{ key }}`` placeholder를 mapping[key]로 치환. 누락 시 빈 문자열.

    Args:
        template: HTML/CSS 원본 텍스트.
        mapping: 변수명 → 치환값 dict.

    Returns:
        모든 placeholder가 치환된 텍스트.
    c                `    | j                  d      }j                  |d      }t        |      S )N    )groupgetr
   )matchkeyvaluemappings      r   replz"_render_placeholders.<locals>.repl   s*    kk!nC$5zr   )r]   zre.Match[str]returnr
   )_PLACEHOLDER_REsub)templater`   ra   s    ` r   _render_placeholdersrf   z   s    
 tX..r   c                t   t        | j                  dd            t        | j                  dd            t        | j                  dd            t        | j                  dd            t        | j                  d	d
            t        | j                  dd            t        | j                  dd            dS )u>   디자인 토큰을 ``tk_*`` placeholder mapping으로 변환.r!   r   r"   r   r#   r   r$   r   r%   r   r&   r   r*   r    )
tk_primarytk_secondary	tk_accenttk_backgroundtk_text_primarytk_text_secondary	tk_radius)r
   r\   )rG   s    r   _token_mappingro      s     &**Y	:;FJJ{I>?Hi89VZZi@Avzz.)DE ,<i!HIOU;< r   c                >    t        t        j                  |             S )u<   문자열에 한글 문자가 포함되어 있는지 확인.)bool
_KOREAN_REsearch)r_   s    r   _has_koreanrt      s    
!!%())r   c                ~    t        j                  dt         j                        }|j                  dt         d|       S )u   CSS 내 모든 ``font-family`` 선언을 강제 폰트 스택으로 치환.

    - 임의로 추가된 fallback 차단
    - 치환 후 ``'Pretendard', 'Noto Sans KR', sans-serif`` 만 존재
    zfont-family\s*:\s*[^;]+;zfont-family: ;)recompile
IGNORECASErd   r.   )csspatterns     r   _force_font_stackr|      s3     jj4bmmDG;;zl!4c::r   c           
        t         | d   z  }t         | d   z  }|j                  d      }|j                  d      }t        |      }t        ||      }t	        |      }i }	|	j                  |       |	j                  |j                         D 
ci c]  \  }
}|
t        |       c}}
       t        ||	      }t        j                  dd| d|d	      }d
|vr|j                  dd| dd      }t        |vr|j                  d
dt         dd      }| j                  dg       }g }|D ]4  }|j                  |d      }|st        |      s$|j                  |       6 ||fS c c}}
w )uF  단일 슬라이드 HTML(인라인 CSS) 렌더링.

    Args:
        layout_meta: registry.json 의 layout 메타.
        variables: 사용자 입력 변수 dict (한글 가능).
        tokens: 디자인 토큰 dict.

    Returns:
        (rendered_html, korean_strings_list) — HTML 본문 + 한글 슬롯 리스트.
    pathrz   rJ   rK   z*<link[^>]*?rel=[\"']stylesheet[\"'][^>]*?>z<style>
z	
</style>rY   )countz<style>z</head>z
</style>
</head>z<style>
body { font-family: z; }
korean_slotsrZ   )TEMPLATES_DIR	read_textro   rf   r|   updateitemsr
   rw   rd   replacer.   r\   rt   append)layout_metar8   rG   	html_pathcss_pathraw_htmlraw_css	token_maprendered_csshtml_mapkvrendered_htmlr   r6   slotr_   s                    r   _render_slide_htmlr      s    F 33I{511H""G"4H  ' 2Gv&I (;L$\2L  "HOOIOO9??+<=41aQAY=>(8<M FF5
L>,	M %%--<.0CDa

 &%--,ZL?
 ??>26L "N )dB'['!!%() .((C >s   E&
c                 l    t               } | j                  di       }t        |j                               S )u7   registry.json에 등록된 모든 layout 이름 반환.layouts)rS   r\   sortedkeys)registryr   s     r   list_layoutsr      s+    Hll9b)G',,.!!r   c                l    t               }|j                  di       }| |vrt        d|        ||    }|S )u~   layout_name의 registry 메타 반환.

    Raises:
        KeyError: layout_name이 registry에 존재하지 않을 때.
    r   zunknown layout: )rS   r\   KeyError)layout_namer   r   metas       r   get_layout_metar      sF     H&ll9b9G'!)+788";/DKr   c                   t        |       t        |      k7  r$t        dt        |        dt        |       d      |rt        |      n
t        d      }|j                  dd       t	        |      }g }g }t        t        | |      d      D ]  \  }\  }	}
t        |	      }t        ||
|      \  }}|d	d
|	 d}||z  }|j                  |d       |j                  |       |j                  |||	|j                  dd      ||
j                         D ci c]  \  }}|t        |       c}}d        dd||t        |d}|dz  }|j                  t        j                   |dd      d       t#        |||||      S c c}}w )u  슬라이드별 HTML + manifest.json 빌드.

    Args:
        layout_names: registry.json의 layout 이름 리스트 (순서대로 슬라이드 생성).
        variables_list: 각 슬라이드에 주입할 변수 dict 리스트. ``len`` == ``layout_names``.
        brand: ``resources/design-md/{brand}/DESIGN.md`` 의 brand 이름. None이면 fallback.
        output_dir: 슬라이드 HTML/manifest 출력 경로. None이면 ``/tmp/magazine-ppt-ko-build``.

    Returns:
        BuildResult — output_dir, html_paths, manifest_path, manifest dict, tokens.

    Raises:
        ValueError: layout_names와 variables_list 길이 불일치.
        KeyError: 미등록 layout 사용.
    zlayout_names (z) and variables_list (z) must have the same lengthz/tmp/magazine-ppt-ko-buildT)parentsexist_okrY   )start02d_z.htmlrJ   rK   r3   rZ   )indexr1   r2   r3   r6   r8   zmagazine-ppt-koz1.0.0)skillversionr   rG   
font_stackslideszmanifest.jsonF   )ensure_asciiindent)rB   rC   rD   rF   rG   )lenrU   r   mkdirrV   	enumeratezipr   r   
write_textr   r\   r   r
   r.   rO   dumpsrA   )layout_namesvariables_listr   rB   outrG   manifest_slidesrC   idxr   r8   r   r   r6   filename
slide_pathr   r   rF   rD   s                       r   buildr     s   * <C//S./ 0N#$$?A
 	

 )$z
d3O.PCIIdTI*U#F,.OJ)2L.)* 
%%k9 {+(:4F(S%~#Ya}E28^
mg>*% %((8R0"04=OO4EFDAqaQiF		

. # ! H /)M

8%:W   # # Gs   F)rA   r.   r0   r   r   r   )r   r
   rb   rE   )rb   rE   )r   
str | Nonerb   rE   )re   r
   r`   r7   rb   r
   )rG   rE   rb   r7   )r_   r
   rb   rq   )rz   r
   rb   r
   )r   rE   r8   r7   rG   rE   rb   ztuple[str, list[str]])rb   r5   )r   r
   rb   rE   )NN)
r   r5   r   zlist[dict[str, str]]r   r   rB   zstr | Path | Nonerb   rA   ))r<   
__future__r   rO   rw   sysdataclassesr   r   pathlibr   typingr   _SATORI_CARDNEWS_PATHr~   insertdesign_md_loaderr   r	   	Exception
SKILL_ROOTr   rM   r.   r=   rx   rc   rr   r0   rA   rS   rV   rf   ro   rt   r|   r   r   r   r   __all__r,   r   r   <module>r      s   #  	 
 (   E (HHOOA,-
2 >?
[(/ =
C < "**FG RZZ23
 < < <   '$/&
*
;>)>)>) >) 	>)H"" $(	LL(L L "	L
 L^_	  
J
	
s   C; ;D
D