projet crepp_git/crepp-projects/station-meteo/hardware/transmetteur/base/v1.0 · branche main
pcba_doc_generator.cpython-312.pyc Voir sur GitLab
�

lVj�����ddlZddlZddlmZmZddlmZeGd�d��ZeGd�d��ZeGd�d	��Z	eGd
�d��Z
eGd�d
��Zdede
defd�Zdededeefd�Zdededefd�Zdedefd�Zdededefd�Zddddd d!�Zd"d#d$d%d&d!�Zd'd(d)d*d+d!�Zdedee
fd,�Zd-edeefd.�Zdedeeeffd/�Zded0edeeeee	ffd1�Zd2ed3edeeeeffd4�Zd5eedefd6�Zd7eedefd8�Z d9edefd:�Z!d5eedefd;�Z"d<e
defd=�Z#d>Z$d?Z%dNd5eedAedBedefdC�Z&dOd3ed2edDedzdBedef
dE�Z'dPd5eedAedefdF�Z(e)dGk(r�ddl*Z*e+e*jX�dHkr"e-dI�e-dJ�e*j\dK�e+e*jX�dLkDre*jXdLnd@Z/e'e*jXdKe*jXdMe+e*jX�dHkDre*jXdHnde/�yy)Q�N)�	dataclass�field)�Pathc�4�eZdZUeed<dZeed<dZeed<y)�Variant�nameT�in_bomF�dnpN)�__name__�
__module__�__qualname__�str�__annotations__r	�boolr
���D/home/claude/kicad-board-repo-template/scripts/pcba_doc_generator.pyrrs��

�I��F�D���C��rrc�l�eZdZUeed<eed<eed<eed<eed<eed<ee��Zee	ed<y	)
�	Component�	reference�value�	footprint�description�	datasheetr
��default_factory�variantsN)
rrr
rrrr�listrrrrrrrs4���N��J��N����N�	
�I�#�D�9�H�d�7�m�9rrc�"�eZdZUeed<eed<y)�	TestPointrrN�rrr
rrrrrr r s
���N��rr c�,�eZdZUeed<eed<eed<y)�HierarchicalLabelr�shaperNr!rrrr#r# s��

�I��J��rr#c��eZdZUeed<eed<ee��Zeeed<ee��Z	ee
ed<ee��Zeeed<dZ
eed<y	)
�Sheetr�filenamer�
components�labels�test_points��svg_contentN)rrr
rrrrr(rr)r#r*r r,rrrr&r&&sQ��

�I��M�"'��"=�J��Y��=�&+�D�&A�F�D�"�#�A�#(��#>�K��i��>��K��rr&�content�start�returnc��d}t||d|�D]*\}}|dk(r|dz
}�|dk(s�|dz}|dk(s�"|||dzcS||dS)Nr�(��))�	enumerate)r-r.�depth�i�chs     r�
extract_blockr84sm��
�E��7�5�6�?�E�2�,���2�
��9��Q�J�E�
�3�Y��Q�J�E���z��u�Q��U�+�+�
,��5�6�?�r�keywordc���g}d}d|��}	|j||�}|dk(r	|S|t|�zt|�kr||t|�znd}|dvr|jt||��|dz}�l)Nrr1���r+)� �	�
�
r2)�find�len�appendr8)r-r9�blocksr6�searchr.�next_chs       r�find_all_blocksrF@s���
�F�	�A���	�]�F�
����V�Q�'���B�;��
�M�	38�#�f�+�2E��G��2T�'�%�#�f�+�-�.�Z\���-�-��M�M�-���7�8��A�I��r�blockrc���tjdtj|��d�tj�j	|�}|r|jd�j
�SdS)Nz\(property\s+"z
"\s+"([^"]*)"r2r+)�re�compile�escape�
IGNORECASErD�group�strip)rGr�ms   r�	get_fieldrPOsW��
�
�
��"�)�)�D�/�*�-�8�"�-�-�	��f�U�m��"#�1�7�7�1�:����*��*rrc�^�ttjd|tj��S)Nz^TP\d+$)rrI�matchrL)rs r�
is_test_pointrSVs�������Y��
�
�>�?�?r�
sheet_name�
search_dirc�`�tjdtj|��d�tj�}g}t	d�}|j�r!|j
�r|j|�|j�r!|j
�r|j|�|D]�}|j�D]�}|j�s�|j|j�s�0|jdd��}tjdd|�}tjd	d|�}tjd
d|d�
�}tjdd|d�
�}d|ddvr|jddd�}d|ddvr|jddd�}|j�ccS��y)u1
    Cherche un fichier SVG dont le nom se termine par -<sheet_name>.svg
    (insensible à la casse, le préfixe yyy peut être quelconque).
    La recherche se fait d'abord dans _doc/svg, puis dans le dossier passé en paramètre.
    Retourne le contenu SVG nettoyé (sans déclaration XML) ou "".
    z^.+-z\.svg$z_doc/svg�utf-8�replace)�encoding�errorsz<\?xml[^?]*\?>r+z<!DOCTYPE[^>]*>z(<svg\b[^>]*?)\s+width="[^"]*"z\1r2)�countz(<svg\b[^>]*?)\s+height="[^"]*"zclass=N��z<svgz<svg class="sheet-svg"zstyle=z%<svg style="background-color: white;")rIrJrKrLr�exists�is_dirrB�iterdir�is_filerRr�	read_text�subrXrN)rTrU�pattern�folders_to_check�doc_svg_dir�folder�	candidate�raws        r�find_svg_for_sheetri]s����j�j�D����:�!6� 7�v�>��
�
�N�G����z�"�K������ 2� 2� 4�����,�����z�0�0�2����
�+�#�#�����)�	#�I�� � �"�w�}�}�Y�^�^�'D��)�)�7�9�)�M���f�f�.��C�8���f�f�/��S�9���f�f�5�u�c�����f�f�6��s�!����3�t��9�,��+�+�f�.F��J�C��3�t��9�,��+�+�f�.U�WX�Y�C��y�y�{�"�'	#�#�,r�Input�Output�Passive�
Bidirectionalz	Tri-State)�input�output�passive�
bidirectional�	tri_statez#d0e8ffz#d4f7d4z#f0f0f0z#fff3cdz#f8d7dau→u←�—u⇄u↕c	��g}t|d�D]�}tjd|�}tjd|�}tjd|�}|r|s�J|r|jd�j	�nd}|jt
|jd�|jd�|r|nd�����t|d	��
�S)N�hierarchical_labelz \(hierarchical_label\s+"([^"]+)"z\(shape\s+(\w+)\)z\(property\s+"doc"\s+"([^"]*)"r2r+�
pas de doc)rr$rc��|jS�N)r)�ls r�<lambda>z+parse_hierarchical_labels.<locals>.<lambda>�s
�����r��key)rFrIrDrMrNrBr#�sorted)r-r)rG�name_m�shape_m�desc_m�raw_descs       r�parse_hierarchical_labelsr��s���
�F� ��*>�?������>��F���)�)�0�%�8���)�)�=�u�E���W��.4�6�<�<��?�(�(�*�"���
�
�'����a���-�-��"�$,��,�
�	���&�.�/�/r�symbol_blockc�8�g}t|d�D]�}tjd|�}|s�|jd�}t	tjd|��}t	tjd|��}|jt
|||�����|S)N�variantz\(name\s+"([^"]+)"\)r2z\(in_bom\s+no\)�
\(dnp\s+yes\)�rr	r
)rFrIrDrMrrBr)r�r�vblockr~rr	r
s       r�parse_variantsr��s����H�!�,�	�:�D�����2�F�;�������a����"�)�)�$6��?�@�@���b�i�i� 0�&�9�:������T�&�c�B�C�D��Orc��i}|jd�}|dk(r|St||�}t|d�D]H}tjd|�}|s�|jd�}t
|d�t
|d�d�||<�J|S)	Nz(lib_symbolsr;�symbolz\(symbol\s+"([^"]+)"r2�doc�	Datasheet)rr)r@r8rFrIrDrMrP)r-�lib_infor.�	lib_block�	sym_blockr~�lib_ids       r�parse_lib_symbolsr��s����H��L�L��(�E���{����g�u�-�I�$�Y��9�
�	����2�I�>�������a���$�Y��6�$�Y��<�
����
��Orr�c��g}g}t|d�D�]i}tjd|�}|s�|jd�}tjd|�}|r|jd�nt	|d�}|r"|jd�s|j
d�r��t	|d�}	t	|d	�}
t	|d
�}t	|d�}ttjd|��}
||vr.|s||jd
d�}|s||jdd�}|xsd}|xsd}t|�r|jt||�����>t|�}|jt||	|
|||
|�����li}|D]�}|j|vr|||j<�!||jjD�chc]}|j ��}}|jD]9}|j |vs�||jjj|��;��i}|D]}|j#|j|�� d�}d�}t%|j'�|��t%|j'�|��fScc}w)Nr�z\(lib_id\s+"([^"]+)"\)r2z\(reference\s+"([^"]+)"\)�	Reference�?�#�Value�	Footprintr�r�r�rr+rrs)rr)rrrrrr
rc���tjdd|j�}tjd|j�}||rt	|j��fSdfS)Nz\dr+�\d+r)rIrbrrD�intrM)�c�prefix�num_ms   r�
sort_key_compz6parse_components_and_testpoints.<locals>.sort_key_compsL������r�1�;�;�/�����6�1�;�;�/���e��E�K�K�M�*�;�;��;�;rc�|�tjd|j�}|rt|j	��SdS)Nr�r�rIrDrr�rM)�tr�s  r�sort_key_tpz4parse_components_and_testpoints.<locals>.sort_key_tps.���	�	�&�!�+�+�.��%*�s�5�;�;�=�!�1��1rr{)rFrIrDrMrP�endswith�
startswithr�getrSrBr r�rrrr�
setdefaultr}�values)r-r�r(�test_points_rawr��lib_id_mr��ref_mrrrrrr
r�seen�comp�v�existing_names�seen_tp�tpr�r�s                       r�parse_components_and_testpointsr��s|���J��O�$�W�h�7�*�	��9�9�6�	�B��������"���I�I�:�I�F��&+�E�K�K��N��9�k�1R�	��I�.�.�s�3�y�7K�7K�C�7P���	�7�3���	�;�7�	��	�5�1���	�;�7�	��2�9�9�%5�y�A�B���X���&�v�.�2�2�=�"�E���$�V�,�0�0��b�A�	�!�*�U���*�U�	���#��"�"�9�#�'�$�
�
&�i�0�H����i�#��#�'�#��!��
�E*�X"$�D��<���>�>��%�#'�D���� �.2�4�>�>�.B�.K�.K�L��a�f�f�L�N�L��]�]�
<���6�6��/�����(�1�1�8�8��;�
<�<�%'�G��-�����2�<�<��,�-�<�
2�
	�t�{�{�}�-�0��w�~�~��[�1����%Ms�>I>�
sheets_dir�root_schc���g}t|jd��D]A}|j�|j�k(r�%|j|j|f��C|S)Nz*.kicad_sch)r}�rglob�resolverB�stem)r�r��sheets�paths    r�collect_sheetsr�(s\��
�F��z�'�'�
�6�7�)���<�<�>�X�-�-�/�/���
�
�t�y�y�$�'�(�)��Mrr�c���d}d}d}d}g}|D]�}t|j�}td�|jD��}t|j�}	td�|jD��}
||	z}||
z}|rt	d|z|z�nd}
|j|j|||	|
|||
d��||z
}||z
}||	z
}||
z
}��||z}||z}|rt	d|z|z�nd}||||||||d�S)Nrc3�>K�|]}|jdvs�d���y�w))rsr+r2N�r)�.0r�s  r�	<genexpr>z'compute_doc_coverage.<locals>.<genexpr>>s����Y�q��
�
�[�8X��Y�s��c3�@K�|]}|jdk7s�d���y�w)rvr2Nr�)r�rys  rr�z'compute_doc_coverage.<locals>.<genexpr>@s����R�q�A�M�M�\�4Q��R�s���d)r�
comp_total�comp_doc�	sig_total�sig_doc�total�
documented�pct)�
global_pct�global_total�global_documented�global_comp_total�global_comp_doc�global_sig_total�global_sig_doc�	per_sheet)rAr(�sumr)�roundrBr)r�r��global_comp_documentedr��global_sig_documentedr��sheetr�r�r�r�r�r�r�r�r�r�s                 r�compute_doc_coverager�5sV�����������I��)����)�)�*�
��Y�E�$4�$4�Y�Y������&�	��R�E�L�L�R�R���Y�&����'�
�16�e�C�*�$�u�,�-�C������J�J�$� �"���$��	
�		�	��+���(�*����*����(��1)�4%�'7�7�L�.�1F�F��BN��s�.�.��=�>�TW�J�!�$�.�.�1�,�/��	�	rrc�"�|syg}|D]u}g}|js|jd�|jr|jd�|j}|r|ddj	|��d�z
}|j|��wdj	|�S)Nrszhors BOM�DNPz	 <small>(�|z	)</small>�, )r	rBr
r�join)r�partsr��flags�labels     r�format_variantsr�js������E�
�	�����x�x��L�L��$��5�5��L�L���������y����%�� 1��;�;�E�
���U��	��9�9�U��rr�c�.�|jj�jdd�}d}|jrd|��}d|j�d�}d|j�d|j�d	|�d
�}|j
r�|dz
}|j
D]�}tj|jd�}tj|j|j�}tj|jd�}|jd
k(rdnd}	|d|j�d|�d|�d|�d|	�d|j�d�
z
}��|dz
}|jr�|dz
}|jD]�}
d|
jvr|
jjd�dn|
j}t|
j �}|
j"rdnd}
|
j"rdnd}|d|
�d|
j$�|�d|
j&�d|
j�d|�d |�d�z
}��|dz
}n|d!z
}|d"z
}|S)#Nr<�-r+zsvg-toggle-u
      <details class="svg-accordion" open>
        <summary class="svg-summary">
          <span class="svg-icon">◈</span>
          Schéma hiérarchique
          <span class="svg-badge">SVG</span>
        </summary>
        <div class="svg-wrap">
          z!
        </div>
      </details>
z
    <section>
      <h2>z <span class="filename">z
</span></h2>
r>u�
      <h3 class="section-label">Signaux hiérarchiques</h3>
      <table>
        <thead>
          <tr>
            <th>Signal</th>
            <th>Direction</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
z#ffffffrv� class="no-doc"z&
          <tr>
            <td><code>zC</code></td>
            <td><span class="badge" style="background:�">z</span></td>
            <td�>�</td>
          </tr>
z         </tbody>
      </table>
u$
      <h3 class="section-label">Composants</h3>
      <table>
        <thead>
          <tr>
            <th>Réf.</th>
            <th>Valeur</th>
            <th>Description</th>
            <th>Boîtier</th>
            <th>Variantes</th>
          </tr>
        </thead>
        <tbody>
�:r;z class="dnp"z# <span class="dnp-badge">DNP</span>z
          <trz>
            <td><code>z%</code></td>
            <td><strong>z</strong></td>
            <td>z</td>
            <td><small>z</small></td>
            <td>u4      <p class='empty'>Aucun composant trouvé.</p>
z    </section>
)r�lowerrXr,r'r)�SHAPE_COLORr�r$�SHAPE_TO_DIRECTION�
SHAPE_ICONrr(r�splitr�rr
rr)r��anchor�	svg_block�svg_id�html�lbl�color�	direction�icon�no_docr��fpr�dnp_cls�dnp_tags               r�
sheet_to_htmlr�{s{��
�Z�Z�
�
�
�
'�
'��S�
1�F��I�����v�h�'������
���	���:�:�,�.�u�~�~�.>�?�
����D�
�|�|���	���<�<�	�C�#����	�	�9�=�E�*�.�.�s�y�y�#�)�)�D�I�"���s�y�y�"�5�D�-0�_�_��-L�)�RT�F����x�x�j�!7�7<�g�R��v�Q�y�k�R��x�q����)�*�	�
�D�	�	
�4�4������
�
	���$�$�
	�D�8;�t�~�~�8M�t�~�~�+�+�C�0��4�SW�Sa�Sa�B�&�t�}�}�5�H�)-���~�r�G�@D���<�b�G����Y���~�~�&�w�i�0�����%��!�!�"�#��t���
��
�
�D�
	�	
�4�4���G�G�����D��Krc	�.�g}|D]0}|jD]}|j|j|f��!�2|sy|jd���d}|D];\}}|jdk(rdnd}|d|j
�d|�d	|j�d
�z
}�=|dz
}|S)Nr+c��tjd|dj�r:ttjd|dj�j	��SdS�Nr�r2rr���xs rrzz1global_test_points_section_html.<locals>.<lambda>�sH���I�I�f�a��d�n�n�5� #�2�9�9�V�Q�q�T�^�^�#D�#J�#J�#L�M��;<�rr{u
    <section id="recap-test-points">
      <h2>Récapitulatif — Points de test <span class="filename">toutes feuilles</span></h2>
      <table>
        <thead>
          <tr>
            <th>Réf.</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
rsr�zD
          <tr class="tp-row">
            <td><code class="tp-ref">z</code></td>
            <tdr�r�z/        </tbody>
      </table>
    </section>
)r*rBr�sortrr)r��all_tpsr�r�r��_sheet_name�no_descs       r�global_test_points_section_htmlr�s���+-�G��-���#�#�	-�B��N�N�E�J�J��+�,�	-�-����L�L�=�L�>��D�#����R�')�~�~��'>�#�B���&�&(�l�l�^�4��y��"�.�.�)�*��	���	�@�@�D��Krr�c��|dk\ry|dk\ryy)N�Pz#22c55e�2z#f59e0bz#ef4444r)r�s r�coverage_bar_colorr�s��
�b�y��	����ru�1
    @import url('https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap');

    * { box-sizing: border-box; margin: 0; padding: 0; }

    /* Variables du thème clair (par défaut) */
    :root {
      --bg-primary: #f5f7fb;
      --bg-secondary: #ffffff;
      --bg-card: #ffffff;
      --sidebar-bg: #1a1a2e;
      --sidebar-text: #e8edff;
      --sidebar-text-secondary: #8b95c9;
      --sidebar-accent: #6b8cff;
      --primary: #1a3a5c;
      --accent: #4f46e5;
      --text-primary: #1e293b;
      --text-secondary: #475569;
      --text-muted: #94a3b8;
      --text-white: #1e293b;
      --border: #e2e8f0;
      --border-hover: #cbd5e1;
      --accent-primary: #4f46e5;
      --accent-secondary: #818cf8;
      --accent-glow: rgba(79, 70, 229, 0.1);
      --success: #10b981;
      --success-bg: rgba(16, 185, 129, 0.1);
      --warning: #f59e0b;
      --warning-bg: rgba(245, 158, 11, 0.1);
      --info: #3b82f6;
      --info-bg: rgba(59, 130, 246, 0.1);
      --gold: #fbbf24;
      --gold-bg: rgba(251, 191, 36, 0.1);
      --step: #8b5cf6;
      --step-bg: rgba(139, 92, 246, 0.1);
      --tp-color: #ea580c;
      --danger: #ef4444;
      --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      --font-mono: 'JetBrains Mono', 'SF Mono', Monaco, monospace;
      --radius: 16px;
      --radius-sm: 10px;
      --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
      --shadow-hover: 0 4px 6px rgba(0, 0, 0, 0.1);
    }

    /* Thème sombre */
    body.dark-theme {
      --bg-primary: #0f172a;
      --bg-secondary: #1e293b;
      --bg-card: #1e293b;
      --sidebar-bg: #020617;
      --sidebar-text: #e2e8f0;
      --sidebar-text-secondary: #64748b;
      --text-primary: #f1f5f9;
      --text-secondary: #cbd5e1;
      --text-muted: #64748b;
      --text-white: #f1f5f9;
      --border: #334155;
      --border-hover: #475569;
      --accent-primary: #818cf8;
      --accent-secondary: #a5b4fc;
      --accent-glow: rgba(129, 140, 248, 0.15);
      --shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
      --shadow-hover: 0 4px 6px rgba(0, 0, 0, 0.4);
    }

    body {
      font-family: var(--font-sans);
      font-size: 14px;
      background: var(--bg-primary);
      color: var(--text-primary);
      display: flex;
      min-height: 100vh;
      transition: background-color 0.3s ease, color 0.3s ease;
    }

    /* ── BOUTON THÈME ── */
    .theme-toggle {
      position: fixed;
      bottom: 20px;
      right: 20px;
      z-index: 1000;
      background: var(--accent-primary);
      color: white;
      border: none;
      border-radius: 50px;
      padding: 12px 20px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 600;
      display: flex;
      align-items: center;
      gap: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
      transition: all 0.3s ease;
      font-family: var(--font-sans);
    }

    .theme-toggle:hover {
      transform: scale(1.05);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    }

    /* ── SIDEBAR ─────────────────────────────── */
    #sidebar {
      position: fixed;
      top: 0; left: 0; bottom: 0;
      width: 280px;
      background: var(--sidebar-bg);
      color: var(--sidebar-text);
      display: flex;
      flex-direction: column;
      overflow-y: auto;
      z-index: 100;
      box-shadow: 2px 0 12px rgba(0,0,0,0.1);
      transition: background-color 0.3s ease;
    }

    #sidebar-header {
      padding: 1.5rem 1.2rem 1rem;
      border-bottom: 1px solid rgba(255,255,255,0.08);
      background: linear-gradient(135deg, var(--sidebar-bg) 0%, rgba(255,255,255,0.05) 100%);
    }

    #sidebar-header h1 {
      font-size: 1rem;
      font-weight: 700;
      color: #fff;
      letter-spacing: 0.02em;
      line-height: 1.3;
    }

    #sidebar-header p {
      font-size: 0.7rem;
      color: var(--sidebar-text-secondary);
      margin-top: 0.3rem;
      font-family: var(--font-mono);
    }

    /* ── COVERAGE WIDGET ── */
    #coverage-widget {
      padding: 0.85rem 1.2rem;
      border-bottom: 1px solid rgba(255,255,255,0.07);
    }

    #coverage-widget .cov-title {
      font-size: 0.64rem;
      text-transform: uppercase;
      letter-spacing: 0.1em;
      color: var(--sidebar-text-secondary);
      margin-bottom: 0.5rem;
      font-weight: 600;
    }

    .cov-global-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 0.45rem;
    }

    .cov-pct-label {
      font-family: var(--font-mono);
      font-size: 1.3rem;
      font-weight: 600;
      color: #fff;
      line-height: 1;
    }
    .cov-pct-label.warn   { color: #fbbf24; }
    .cov-pct-label.danger { color: #f87171; }

    .cov-sub {
      font-size: 0.65rem;
      color: var(--sidebar-text-secondary);
      text-align: right;
      line-height: 1.5;
    }

    .cov-bar-wrap {
      background: rgba(255,255,255,0.07);
      border-radius: 3px;
      height: 4px;
      overflow: hidden;
    }

    .cov-bar-fill {
      height: 100%;
      border-radius: 3px;
      transition: width 0.4s ease;
    }

    /* ── BOUTON RETOUR ── */
    .back-button {
      margin: 1rem 1.2rem;
      padding: 0.6rem 1rem;
      background: rgba(107, 140, 255, 0.15);
      border: 1px solid var(--accent);
      border-radius: 10px;
      text-decoration: none;
      color: var(--accent);
      font-size: 0.8rem;
      font-weight: 500;
      display: flex;
      align-items: center;
      gap: 0.5rem;
      transition: all 0.25s;
    }

    .back-button:hover {
      background: rgba(107, 140, 255, 0.25);
      transform: translateX(-4px);
    }

    /* ── NAV LINKS ── */
    #sidebar-nav {
      padding: 0.6rem 0;
      flex: 1;
    }

    #sidebar-nav .nav-section-label {
      font-size: 0.62rem;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--sidebar-text-secondary);
      padding: 0.5rem 1.2rem 0.25rem;
      font-weight: 600;
    }

    .nav-sheet-link {
      display: block;
      padding: 0.4rem 1.2rem 0.45rem;
      text-decoration: none;
      color: var(--sidebar-text);
      font-size: 0.82rem;
      transition: background 0.15s, color 0.15s;
      border-left: 3px solid transparent;
      position: relative;
    }

    .nav-sheet-link:hover {
      background: rgba(107,140,255,0.08);
      color: #fff;
      border-left-color: var(--sidebar-accent);
    }

    .nav-sheet-inner {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 0.4rem;
    }

    .nav-sheet-name {
      flex: 1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .nav-sheet-pct {
      font-family: var(--font-mono);
      font-size: 0.65rem;
      opacity: 0.55;
      flex-shrink: 0;
    }

    .nav-sheet-bar {
      height: 2px;
      border-radius: 2px;
      margin-top: 0.25rem;
      background: rgba(255,255,255,0.05);
      overflow: hidden;
    }

    .nav-sheet-bar-fill {
      height: 100%;
      border-radius: 2px;
      opacity: 0.6;
    }

    #sidebar-nav a.tp-link {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      padding: 0.45rem 1.2rem;
      text-decoration: none;
      color: #fb923c;
      font-size: 0.82rem;
      transition: background 0.15s, color 0.15s;
      border-left: 3px solid transparent;
    }

    #sidebar-nav a.tp-link:hover {
      background: rgba(249,115,22,0.12);
      color: #fdba74;
      border-left-color: var(--tp-color);
    }

    #sidebar-nav a .nav-icon {
      font-size: 0.75rem;
      opacity: 0.7;
      width: 16px;
      text-align: center;
    }

    /* ── MAIN CONTENT ── */
    #main {
      margin-left: 280px;
      flex: 1;
      padding: 2rem 2.5rem;
      max-width: calc(100vw - 280px);
    }

    section {
      background: var(--bg-card);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 1.5rem 2rem;
      margin-bottom: 1.5rem;
      box-shadow: var(--shadow);
      transition: all 0.3s ease;
    }

    section:hover {
      border-color: var(--border-hover);
      box-shadow: var(--shadow-hover);
    }

    section h2 {
      font-size: 1.1rem;
      color: var(--text-primary);
      border-left: 4px solid var(--accent);
      padding-left: 0.8rem;
      margin-bottom: 1.2rem;
      font-weight: 600;
    }

    #recap-test-points h2 {
      border-left-color: var(--tp-color);
    }

    .filename {
      font-size: 0.7rem;
      font-weight: normal;
      color: var(--text-secondary);
      font-family: var(--font-mono);
      margin-left: 0.5rem;
    }

    .section-label {
      font-size: 0.75rem;
      color: var(--text-secondary);
      margin: 1.2rem 0 0.6rem;
      text-transform: uppercase;
      letter-spacing: 0.07em;
      font-weight: 600;
    }

    /* ── ACCORDÉON SVG (ouvert par défaut) ── */
    .svg-accordion {
      margin-bottom: 1.2rem;
      border: 1px solid var(--border);
      border-radius: 12px;
      overflow: hidden;
    }

    .svg-summary {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      padding: 0.6rem 1rem;
      cursor: pointer;
      font-size: 0.82rem;
      font-weight: 600;
      color: var(--text-primary);
      background: rgba(0,0,0,0.02);
      user-select: none;
      list-style: none;
      transition: background 0.15s;
    }

    body.dark-theme .svg-summary {
      background: rgba(255,255,255,0.03);
    }

    .svg-summary::-webkit-details-marker { display: none; }
    .svg-summary::marker { display: none; }

    .svg-summary:hover {
      background: rgba(79, 70, 229, 0.08);
    }

    details[open] .svg-summary {
      background: rgba(79, 70, 229, 0.05);
      border-bottom: 1px solid var(--border);
    }

    .svg-summary::after {
      content: "▸";
      margin-left: auto;
      font-size: 0.7rem;
      color: var(--text-secondary);
      transition: transform 0.2s;
    }

    details[open] .svg-summary::after {
      transform: rotate(90deg);
    }

    .svg-icon {
      color: var(--accent);
      font-size: 0.9rem;
    }

    .svg-badge {
      font-family: var(--font-mono);
      font-size: 0.6rem;
      background: var(--accent);
      color: white;
      padding: 0.1rem 0.4rem;
      border-radius: 6px;
      letter-spacing: 0.05em;
    }

    .svg-wrap {
      padding: 1rem;
      background: white;
      overflow-x: auto;
      transition: background 0.3s ease;
    }

    body.dark-theme .svg-wrap {
      background: #ffffff;
    }

    .svg-wrap .sheet-svg,
    .svg-wrap svg {
      width: 100%;
      height: auto;
      max-height: 900px;
      display: block;
      background: white;
    }

    /* ── TABLES ── */
    table {
      width: 100%;
      border-collapse: collapse;
      margin-bottom: 0.5rem;
      font-size: 0.85rem;
    }

    thead tr { 
      background: linear-gradient(135deg, var(--sidebar-bg) 0%, var(--bg-secondary) 100%);
      color: var(--text-white);
    }
    thead th { 
      padding: 0.6rem 0.9rem; 
      text-align: left; 
      font-weight: 600; 
      font-size: 0.8rem; 
      letter-spacing: 0.03em;
    }

    tbody tr:nth-child(even) { background: rgba(0,0,0,0.02); }
    body.dark-theme tbody tr:nth-child(even) { background: rgba(255,255,255,0.02); }
    tbody tr:hover { background: rgba(79, 70, 229, 0.08); }
    tbody td { padding: 0.5rem 0.9rem; border-bottom: 1px solid var(--border); vertical-align: middle; }

    .tp-row { border-left: 3px solid var(--tp-color); }
    .tp-ref { color: var(--tp-color); font-weight: bold; }

    code {
      font-family: var(--font-mono);
      font-size: 0.85em;
    }

    .badge {
      display: inline-block;
      padding: 0.2rem 0.6rem;
      border-radius: 20px;
      font-size: 0.7rem;
      font-weight: 600;
      color: #333;
    }

    .no-doc  { color: var(--text-muted); font-style: italic; }
    .empty   { color: var(--text-muted); font-style: italic; padding: 0.5rem 0; }

    .dnp     { opacity: 0.45; }
    .dnp-badge {
      background: var(--danger);
      color: white;
      font-size: 0.6rem;
      padding: 0.1rem 0.4rem;
      border-radius: 4px;
      margin-left: 0.3rem;
      vertical-align: middle;
    }

    small { color: var(--text-secondary); }

    /* ── RESPONSIVE ── */
    @media (max-width: 768px) {
      #sidebar { width: 0px; display: none; }
      #main { margin-left: 0; padding: 1rem; max-width: 100vw; }
      .theme-toggle { bottom: 10px; right: 10px; padding: 8px 16px; font-size: 12px; }
    }
u�
    // Gestion du thème clair/sombre
    function initTheme() {
        const savedTheme = localStorage.getItem('theme');
        if (savedTheme === 'dark') {
            document.body.classList.add('dark-theme');
            updateThemeButton(true);
        } else {
            document.body.classList.remove('dark-theme');
            updateThemeButton(false);
        }
    }
    
    function updateThemeButton(isDark) {
        const btn = document.getElementById('theme-toggle-btn');
        if (btn) {
            if (isDark) {
                btn.innerHTML = '☀️ Thème clair';
            } else {
                btn.innerHTML = '🌙 Thème sombre';
            }
        }
    }
    
    function toggleTheme() {
        if (document.body.classList.contains('dark-theme')) {
            document.body.classList.remove('dark-theme');
            localStorage.setItem('theme', 'light');
            updateThemeButton(false);
        } else {
            document.body.classList.add('dark-theme');
            localStorage.setItem('theme', 'dark');
            updateThemeButton(true);
        }
    }
    
    // Initialiser le thème au chargement
    initTheme();
�
../index.html�project_name�
pcb_index_urlc��ddlm}td�|D��}t|�}|d}|dkrdn|dkrdnd	}t	|�}d
|�d|�d|d
�d|d�d|d�d|d�d|�d|�d�}	d|�d�}
|dD�cic]}|d|d��
}}d}
|D]�}|j
j
�jdd�}|j|j
d�}t	|�}|jrdnd	}|
d |�d!|�|j
�d"|�d#|�d|�d$�z
}
��|r
|
d%z
}
|
d&z
}
d	}|D]D}|j
j
�jdd�}|d'|�d�t|�zd(zz
}�F|t|�z
}d)|�d*t�d+|�d,|j�j��d-t|��d.|�d/|	�d0|
�d1|
�d2|�d3t �d4�Scc}w)5Nr)�datec3�FK�|]}t|j����y�wrx)rAr*)r��ss  rr�z generate_html.<locals>.<genexpr>.s����6�!�3�q�}�}�%�6�s�!r�r�dangerr�warnr+z�
    <div id="coverage-widget">
      <div class="cov-title">Couverture globale</div>
      <div class="cov-global-row">
        <div class="cov-pct-label r�z0%</div>
        <div class="cov-sub">
          r��/r�z cmp<br>
          r�r�zq sig
        </div>
      </div>
      <div class="cov-bar-wrap">
        <div class="cov-bar-fill" style="width:z%; background:z!"></div>
      </div>
    </div>
z
    <a href="uF" class="back-button">
      <span>←</span> Retour aux PCB
    </a>
r�rr�z.<div class="nav-section-label">Feuilles</div>
r<r�r�u◈ z    <a href="#zc" class="nav-sheet-link">
      <div class="nav-sheet-inner">
        <span class="nav-sheet-name">z,</span>
        <span class="nav-sheet-pct">zm%</span>
      </div>
      <div class="nav-sheet-bar">
        <div class="nav-sheet-bar-fill" style="width:z"></div>
      </div>
    </a>
zR    <div class="nav-section-label" style="margin-top:0.6rem">Points de test</div>
ui    <a href="#recap-test-points" class="tp-link"><span class="nav-icon">🔍</span>Récapitulatif TP</a>
z	<div id="z</div>u�<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Documentation — z</title>
  <style>u </style>
</head>
<body>

  <!-- ── BOUTON CHANGEMENT DE THÈME ── -->
  <button id="theme-toggle-btn" class="theme-toggle" onclick="toggleTheme()">
    🌙 Thème sombre
  </button>

  <!-- ── SIDEBAR ── -->
  <aside id="sidebar">
    <div id="sidebar-header">
      <h1>u</h1>
      <p>Généré le z<br>u feuille(s) · z TP</p>
    </div>

    z
    z#

    <nav id="sidebar-nav">
      uM
    </nav>
  </aside>

  <!-- ── MAIN ── -->
  <main id="main">
    z
  </main>

  <script>
    z
  </script>

</body>
</html>
)�datetimerr�r�rrr�rXr�r,r�r�CSS�today�	isoformatrA�
JAVASCRIPT)r�r	r
r�total_tp�coverage�g_pct�pct_cls�bar_col�cov_html�back_buttonr�pct_by_name�	nav_linksr�r�r�r��has_svg�bodys                    r�
generate_htmlr"+s�����6�v�6�6�H�$�V�,�H���%�E� �2�:�x�e�b�j�F�b�G�!�%�(�G�#�$+�)�2�e�W�5��%�&�
'�q��2E�)F�(G�H��$�%�
&�a��1C�(D�'E�F0�16�w�n�W�I�N��H�"��_���K�19��0E�F�1�1�V�9�a��h�&�F�K�F�A�I�����*�*�"�"�$�,�,�S�#�6���/�/�%�*�*�c�2��$�S�)��!�-�-�&�2�����x�0&�&-�Y�u�z�z�l�;%�%(�E�*6�7:�U�.���P�
	�		�	�� ��j�j�	��B�	B�	��D��I�����!�!�#�+�+�C��5���)�F�8�2�&��u�)=�=��H�H��I�	�+�F�3�3�D��
)�>�*
�

����.���z�z�|�-�-�/�0��S��[�M��QY�PZ�[��J���M���k��
�F���L��I)�)��?Gs�8Groc��t|�j�}t|�j�}|j�std|����|j�std|����t	d|���t	d|�d��|jd��}t
|�}t||�}|s�t	d�t||�\}}	t|�}
d	}|j|td
�fD]}t|j|�}|s�nt|j|j||
|	|��g}
t	dt|��d
t|	��dt|
��d���n6t	dt|��d��g}
|D�]\}}t	d|�d|j!|j��d��|jd��}t
|�}i|�|�}t||�\}}	t|�}
d	}|j|td
�fD] }t||�}|s�t	d|���n|st	d|�d��t	dt|��d
t|	��dt|
��d��|
j#t||j||
|	|�����|rt|�nd}t%|
�}|r5|j&j)�dk(rt+|
|j��}nt-|
|j|��}|r"|j/|d��t	d|���nt	|�|||
fS)NuSchéma racine introuvable : zDossier introuvable : uSchéma racine    : zDossier feuilles : r>rW)rYuY  Aucune sous-feuille hiérarchique trouvée : documentation de la feuille racine seule.
r+�.)rr'r(r)r*r,u  → z composant(s), z point(s) de test, u signal(aux) hiérarchique(s)z  u feuille(s) trouvée(s) :
z  Traitement : z (r3u    → SVG trouvé dans u!    → Pas de SVG trouvé pour '�'u    → z.md)r	)r	r
u
Documentation écrite dans : )rr�r]�FileNotFoundError�printrar�r�r�r��parentrir�r&rrA�relative_torBr��suffixr��generate_markdown_fallbackr"�
write_text)r�r�ror
�	root_path�root_content�root_lib�
sub_sheetsr(r*r)r,rUr�rr��
sheet_content�	sheet_lib�
merged_lib�output_pathr�results                      r�document_projectr6�sn���h��'�'�)�I��j�!�)�)�+�J������"?�	�{� K�L�L������"8��� E�F�F�	� ���
,�-�	��
�|�2�
.�/��&�&��&�8�L�$�\�2�H��
�I�6�J��	�j�k�"A�,�PX�"Y��
�K�*�<�8����$�+�+�Z��c��C�	�J�,�Y�^�^�Z�H�K���	������^�^�!��#�#�

���	��s�:��'��s�;�7G�6H�H[�\_�`f�\g�[h�iF�G�	H�
��3�z�?�#�#>�?�@���$�	�J�D�$��O�D�6��D�,<�,<�Z�=N�=N�,O�+P�PQ�R�S� �N�N�G�N�<�M�-�m�<�I�5�x�5�9�5�J�&E�m�U_�&`�#�J��5�m�D�F��K�#�{�{�J��S�	�B�
�
�0��z�B����5�j�\�B�C��	
�
��9�$��q�A�B��H�S��_�-�_�S��=M�<N�Na�be�fl�bm�an�oL�M�
N��M�M�%�����%��'�'�
�
�'	�8#)�$�v�,�d�K�#�F�+�H��{�)�)�/�/�1�U�:�+�F����P���v�I�N�N�R_�`������v���8�
�/��}�=�>�
�f�
��8�V�#�#rc��d|��dddddg}t|�}|d|d�d�d|d	�d
|d�d�dd
dgz
}|dD]8}|jd|d�d|d�d|d�d
|d�d|d�d
|d�d�
��:|gd�z
}|D�]�}|d|j��dd|j�d�dgz
}|jr�|gd�z
}|jD][}t
j
|j|j�}|jd|j�d|�d|j�d���]|jd�|js��|gd�z
}|jD]�}d |jvr|jjd �d!n|j}	d"jd#�|jD��xsd$}
|jrd%nd}|jd|j�|�d|j �d|j�d|	�d|
�d����|jd����|D��cgc]!}|j"D]}|j|f���#}
}}|
ra|
j%d&��'�|gd(�z
}|
D]1\}}|jd|j�d|j�d���3|jd�d)j|�Scc}}w)*Nu# Documentation composants — r+u4Généré automatiquement depuis les schémas KiCad.�---z)## Couverture de documentation globale : r��%r1r�rr�u éléments documentés)z/| Feuille | Couverture | Composants | Signaux |z/|---------|------------|------------|---------|r�z| rz | r�z% | r�r�r�r�z |)r+r8r+z## zFichier : `�`)u### Signaux hiérarchiquesr+z$| Signal | Direction | Description |z$|--------|-----------|-------------|)z### Composantsr+u7| Réf. | Valeur | Description | Boîtier | Variantes |z5|------|--------|-------------|---------|-----------|r�r;r�c3�xK�|]2}|j|jsdndz|jrdndz���4y�w)z (hors BOM)r+z (DNP)Nr�)r�r�s  rr�z-generate_markdown_fallback.<locals>.<genexpr>s:����%���F�F�1�8�8�m��D�TU�TY�TY��_a�b�%�s�8:rsu
 ⚠️DNPc��tjd|dj�r:ttjd|dj�j	��SdSr�r�r�s rrzz,generate_markdown_fallback.<locals>.<lambda>#sH���	�	�&�!�A�$�.�.�9�$'�r�y�y���1����'H�'N�'N�'P�#Q��?@�rr{)r8r+u6## Récapitulatif — Points de test (toutes feuilles)r+u| Réf. | Description |z|------|-------------|r>)r�rBrr'r)r�r�r$rr(rr�r�rr
rrr*r�)r�r	�linesrrr�r�r�r�r�rr
r�rrs               rr+r+�sF��
)�,��8�"�>��E�2�
�E�
$�F�+�H�	�
3�H�\�4J�3K�1�M�
�H�(�)�*�!�H�^�,D�+E�E]�^�
�9�9���E��k�
"�}��
���r�!�F�)��C��%��z��a�
�m�_�A�a��o�EV�VY�Z[�\e�Zf�Yg�gh�ij�kv�iw�hx�xz�{�|�}�	�_��E����
�C��
�
�|�$�b�K����7G�q�*I�2�N�N���<�<���
�E�
�|�|�
R��.�2�2�3�9�9�c�i�i�H�	����r�#�(�(��3�y�k��S�_�_�<M�R�P�Q�
R�
�L�L��������
�E�
�(�(�
s��<?�4�>�>�<Q�4�>�>�/�/��4�R�8�W[�We�We���9�9�%�!�]�]�%�����'+�h�h�l�B�����r�$�.�.�!1�#��c�$�*�*��S��IY�IY�HZ�Z]�^`�]a�ad�em�dn�np�q�r�
s�
�L�L���9�<&,�D��a�m�m�D������|�D�|�D�G�D�����A��	B�
�
�	
�� '�	C�O�K���L�L�2�b�l�l�^�3�r�~�~�.>�b�A�B�	C�
���R���9�9�U����Es�!&J>�__main__�zUUsage : py list_pinout.py <root.kicad_sch> <dossier_feuilles> [output.html|output.md]zJExemple : py list_pinout.py CREPP.io.kicad_sch Modules/ documentation.htmlr2��)r+r)Nr)r+)0rI�os�dataclassesrr�pathlibrrrr r#r&rr�r8rrFrPrrSrir�r�r�r�r��dictr��tupler�r�r�r�r�rrrrr"r6r+r�sysrA�argvr'�exit�pcb_urlrrr�<module>rKs���	�	�(�������
�:�:��:�����������
�����	�3�	�s�	�s�	��S��3��4��9��+�S�+��+��+�@�S�@�T�@�,�3�,�D�,�S�,�h���$� ����������������
�0�s�0�t�4E�/F�0�"
��
��g��
��s��t�C��I���$L�
�L� �L�
�4�	�?�D��O�+�,�L�^�t��t���U�3��9�=M�8N��.��e��.��.�j�d�7�m����"W��W�3�W�t!�D��K�!�C�!�H�C��C��|��~&�
�Rk�$�u�+�k�S�k�c�k�hk�k�dQ$�s�Q$��Q$�S�4�Z�Q$�_b�Q$�y|�Q$�h>�t�E�{�>�#�>�s�>�B�z���
�3�8�8�}�q��
�e�f�
�Z�[�������!����]�Q�.�c�h�h�q�k�O�G��S�X�X�a�[�#�(�(�1�+�c�#�(�(�m�a�>O�s�x�x��{�UY�[b�c�r