projet crepp_git/crepp-projects/station-meteo/hardware/recepteur/v0 · branche main
generate_icd.cpython-313.pyc Voir sur GitLab
�

4
�j���`�SSKrSSKrSSKJrJr SSKJr \"SS55r\"SS55r\"SS	55r	\"S
S55r
\"SS
55rS\S\
S\4SjrS\S\S\\4SjrS\S\S\4SjrS\S\4SjrS\S\S\4SjrSSSSS S!.rS"S#S$S%S&S!.rS'S(S)S*S+S!.rS\S\\
4S,jrS-\S\\4S.jrS\S\\\44S/jrS\S0\S\\\\\	44S1jrS2\S3\S\\\\44S4jrS5\\S\4S6jrS7\\S\4S8jr S9\S\4S:jr!S5\\S\4S;jr"S<\
S\4S=jr#S>r$S?r%SNS5\\SA\SB\S\4SCjjr&SOS3\S2\SD\S-SB\S\\\\S
44
SEjjr'SPS5\\SA\S\4SFjjr(\)SG:Xa�SSK*r*\+"\*RX5SH:a"\-"SI5 \-"SJ5 \*R\"SK5 \+"\*RX5SL:�a\*RXSLOS@r/\'"\*RXSK\*RXSM\+"\*RX5SH:�a\*RXSHOS\/5 gg)Q�N)�	dataclass�field)�Pathc�<�\rSrSr%\\S'Sr\\S'Sr\\S'Sr	g)	�Variant��nameT�in_bomF�dnp�N)
�__name__�
__module__�__qualname__�__firstlineno__�str�__annotations__r
�boolr�__static_attributes__r��V/home/nico/Documents/crepp_git/CREPP.io/boards/V2.x_pico2/v2.2/scripts/generate_icd.pyrrs��

�I��F�D���C��rrc�n�\rSrSr%\\S'\\S'\\S'\\S'\\S'\\S'\"\S9r	\\
\S	'S
rg)�	Component��	reference�value�	footprint�description�	datasheetr��default_factory�variantsrN)r
rrrrrrr�listr!rrrrrrrs4���N��J��N����N�	
�I�#�D�9�H�d�7�m�9rrc�*�\rSrSr%\\S'\\S'Srg)�	TestPoint�rrrN�r
rrrrrrrrrr$r$s
���N��rr$c�4�\rSrSr%\\S'\\S'\\S'Srg)�HierarchicalLabel� r	�shaperrNr&rrrr(r( s��

�I��J��rr(c��\rSrSr%\\S'\\S'\"\S9r\\	\S'\"\S9r
\\\S'\"\S9r\\
\S'Sr\\S	'S
rg)�Sheet�&r	�filenamer�
components�labels�test_points��svg_contentrN)r
rrrrrrr"r/rr0r(r1r$r3rrrrr,r,&sQ��

�I��M�"'��"=�J��Y��=�&+�D�&A�F�D�"�#�A�#(��#>�K��i��>��K��rr,�content�start�returnc��Sn[XSU5H.up4US:XaUS-
nMUS:XdMUS-nUS:XdM'XUS-s $ XS$)Nr�(��))�	enumerate)r4r5�depth�i�chs     r�
extract_blockr?4s`��
�E��7�6�?�E�2���
��9��Q�J�E�
�3�Y��Q�J�E���z��Q��U�+�+�
3��6�?�r�keywordc���/nSnSU3nURXC5nUS:XaU$U[U5-[U5:aX[U5-OSnUS;aUR[X55 US-nMn)Nrr8���r2)� �	�
�
r9)�find�len�appendr?)r4r@�blocksr=�searchr5�next_chs       r�find_all_blocksrM@s���
�F�	�A���	�]�F�
����V�'���B�;��
�M�	38�#�f�+�2E��G��2T�'�#�f�+�-�.�Z\���-�-��M�M�-��7�8��A�I��r�blockr	c���[R"S[R"U5S3[R5R	U5nU(aURS5R
5$S$)Nz\(property\s+"z
"\s+"([^"]*)"r9r2)�re�compile�escape�
IGNORECASErK�group�strip)rNr	�ms   r�	get_fieldrWOsW��
�
�
��"�)�)�D�/�*�-�8�"�-�-�	��f�U�m��"#�1�7�7�1�:����*��*rrc�`�[[R"SU[R55$)Nz^TP\d+$)rrP�matchrS)rs r�
is_test_pointrZVs�������Y��
�
�>�?�?r�
sheet_name�
search_dirc	��[R"S[R"U5S3[R5n/n[	S5nUR5(a&UR
5(aURU5 UR5(a&UR
5(aURU5 UGHnUR5H�nUR5(dMURUR5(dM<URSSS9n[R"SSU5n[R"S	SU5n[R"S
SUSS
9n[R"SSUSS
9nSUSS;aURSSS5nSUSS;aURSSS5nUR5s s $ GM g)u
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[^?]*\?>r2z<!DOCTYPE[^>]*>z(<svg\b[^>]*?)\s+width="[^"]*"z\1r9)�countz(<svg\b[^>]*?)\s+height="[^"]*"zclass=N��z<svgz<svg class="sheet-svg"zstyle=z%<svg style="background-color: white;")rPrQrRrSr�exists�is_dirrI�iterdir�is_filerYr	�	read_text�subr_rU)r[r\�pattern�folders_to_check�doc_svg_dir�folder�	candidate�raws        r�find_svg_for_sheetrp]s����j�j�D����:�!6� 7�v�>��
�
�N�G����z�"�K������� 2� 2� 4� 4�����,������z�0�0�2�2����
�+�#�����)�I�� � �"�"�w�}�}�Y�^�^�'D�'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	��/n[US5H�n[R"SU5n[R"SU5n[R"SU5nU(aU(dMXU(aURS5R	5OSnUR[
URS5URS5U(aUOSS95 M� [US	S
9$)N�hierarchical_labelz \(hierarchical_label\s+"([^"]+)"z\(shape\s+(\w+)\)z\(property\s+"doc"\s+"([^"]*)"r9r2�
pas de doc)r	r*rc��UR$�N)r	)�ls r�<lambda>�+parse_hierarchical_labels.<locals>.<lambda>�s����r��key)rMrPrKrTrUrIr(�sorted)r4r0rN�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	�N�/n[US5H�n[R"SU5nU(dM#URS5n[	[R"SU55(+n[	[R"SU55nUR[
XEUS95 M� U$)N�variantz\(name\s+"([^"]+)"\)r9z\(in_bom\s+no\)�
\(dnp\s+yes\)�r	r
r)rMrPrKrTrrIr)r�r!�vblockr�r	r
rs       r�parse_variantsr��s����H�!�,�	�:�����2�F�;�������a����"�)�)�$6��?�@�@���b�i�i� 0�&�9�:������T�c�B�C�;��Orc��0nURS5nUS:XaU$[X5n[US5HOn[R"SU5nU(dM#URS5n[
US5[
US5S.X'MQ U$)	Nz(lib_symbolsrB�symbolz\(symbol\s+"([^"]+)"r9�doc�	Datasheet)rr)rGr?rMrPrKrTrW)r4�lib_infor5�	lib_block�	sym_blockr��lib_ids       r�parse_lib_symbolsr��s����H��L�L��(�E���{����g�-�I�$�Y��9�	����2�I�>�������a���$�Y��6�$�Y��<�
���:��Orr�c
�~�/n/n[US5GH�n[R"SU5nU(dM$URS5n[R"SU5nU(aURS5O[	US5nU(a,URS5(dUR
S5(aM�[	US5n	[	US	5n
[	US
5n[	US5n[[R"SU55n
Xa;a6U(dXRS
S5nU(dXRSS5nU=(d SnU=(d Sn[U5(aUR[UUS95 GMr[U5nUR[UU	U
UUU
US95 GM� 0nUH�nURU;aUUUR'M$UURRVs1sHnUR iM nnURH=nUR U;dMUURRRU5 M? M� 0nUHnUR#URU5 M! SnSn[%UR'5US9[%UR'5US94$s snf)Nr�z\(lib_id\s+"([^"]+)"\)r9z\(reference\s+"([^"]+)"\)�	Reference�?�#�Value�	Footprintr�r�r�rr2rrz)rr)rrrrrrr!c���[R"SSUR5n[R"SUR5nX(a[	UR554$S4$)Nz\dr2�\d+r)rPrirrK�intrT)�c�prefix�num_ms   r�
sort_key_comp�6parse_components_and_testpoints.<locals>.sort_key_compsL������r�1�;�;�/�����6�1�;�;�/���e��E�K�K�M�*�;�;��;�;rc��[R"SUR5nU(a[UR	55$S$)Nr�r�rPrKrr�rT)�tr�s  r�sort_key_tp�4parse_components_and_testpoints.<locals>.sort_key_tps.���	�	�&�!�+�+�.��%*�s�5�;�;�=�!�1��1rr�)rMrPrKrTrW�endswith�
startswithr�getrZrIr$r�rrr!r	�
setdefaultr��values)r4r�r/�test_points_rawr��lib_id_mr��ref_mrrrrrrr!�seen�comp�v�existing_names�seen_tp�tpr�r�s                       r�parse_components_and_testpointsr��sn���J��O�$�W�h�7�	��9�9�6�	�B��������"���I�I�:�I�F��&+�E�K�K��N��9�k�1R�	��I�.�.�s�3�3�y�7K�7K�C�7P�7P���	�7�3���	�;�7�	��	�5�1���	�;�7�	��2�9�9�%5�y�A�B�����&�.�2�2�=�"�E���$�,�0�0��b�A�	�!�*�U���*�U�	���#�#��"�"�9�#�'�$�
�
&�i�0�H����i�#��#�'�#��!��
�E8�X"$�D����>�>��%�#'�D���� �.2�4�>�>�.B�.K�.K�L�.K��a�f�f�.K�N�L��]�]���6�6��/�����(�1�1�8�8��;�#��%'�G������2�<�<��,��<�
2�
	�t�{�{�}�-�0��w�~�~��[�1����%Ms�4J:�
sheets_dir�root_schc���/n[URS55HDnUR5UR5:XaM'URURU45 MF U$)Nz*.kicad_sch)r��rglob�resolverI�stem)r�r��sheets�paths    r�collect_sheetsr�(sW��
�F��z�'�'�
�6�7���<�<�>�X�-�-�/�/���
�
�t�y�y�$�'�(�8��Mrr�c���SnSnSnSn/nUH�n[UR5n[SUR55n[UR5n	[SUR55n
Xy-nX�-nU(a[	SU-U-5OSn
URURUUU	U
UUU
S.5 X-
nX(-
nX9-
nXJ-
nM� X-nX$-nU(a[	SU-U-5OSnUUUUUUUUS.$)Nrc3�H# �UHoRS;dMSv� M g7f))rzr2r9N�r)�.0r�s  r�	<genexpr>�'compute_doc_coverage.<locals>.<genexpr>>s���Y�$4�q�
�
�[�8X���$4���"�	"c3�H# �UHoRS:wdMSv� M g7f)r}r9Nr�)r�r�s  rr�r�@s���R�L�q�M�M�\�4Q���L�r��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)rHr/�sumr0�roundrIr	)r�r��global_comp_documentedr��global_sig_documentedr��sheetr�r�r�r�r�r�r�r�r�r�s                 r�compute_doc_coverager�5s?�����������I�����)�)�*�
��Y�E�$4�$4�Y�Y������&�	��R�E�L�L�R�R���&���'�
�16�e�C�*�$�u�,�-�C������J�J�$� �"���$��	
�		�	�+���*���*���(��1�4%�7�L�.�F��BN��s�.�.��=�>�TW�J�!�$�.�.�1�,�/��	�	rr!c�N�U(dg/nUH�n/nUR(dURS5 UR(aURS5 URnU(aUSSR	U5S3-
nURU5 M� SR	U5$)Nrzzhors BOM�DNPz	 <small>(�|z	)</small>�, )r
rIrr	�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�n�URR5RSS5nSnUR(aSU3nSURS3nSURSURS	US
3nUR
(a�US-
nUR
H�n[RURS5n[RURUR5n[RURS5nURS
:XaSOSn	USURSUSUSUSU	SURS3
-
nM� US-
nUR(a�US-
nURH�n
SU
R;aU
RRS5SOU
Rn[U
R 5nU
R"(aSOSn
U
R"(aSOSnUSU
SU
R$USU
R&SU
RSUS US3-
nM� US-
nOUS!-
nUS"-
nU$)#NrC�-r2zsvg-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>
rEu�
      <h3 class="section-label">Signaux hiérarchiques</h3>
      <table>
        <thead>
          <tr>
            <th>Signal</th>
            <th>Direction</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
z#ffffffr}� 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>
�:rBz 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	�lowerr_r3r.r0�SHAPE_COLORr�r*�SHAPE_TO_DIRECTION�
SHAPE_ICONrr/r�splitr�r!rrr)r��anchor�	svg_block�svg_id�html�lbl�color�	direction�icon�no_docr��fpr!�dnp_cls�dnp_tags               r�
sheet_to_htmlr{sq��
�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	�>�/nUH3nURH nURURU45 M" M5 U(dgURSS9 SnUH<upSURS:XaSOSnUSUR
SUS	URS
3-
nM> US-
nU$)Nr2c���[R"SUSR5(a;[[R"SUSR5R	55$S$�Nr�r9rr���xs rr��1global_test_points_section_html.<locals>.<lambda>�sN���I�I�f�a��d�n�n�5�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>
rzr�zD
          <tr class="tp-row">
            <td><code class="tp-ref">z</code></td>
            <tdr�r�z/        </tbody>
      </table>
    </section>
)r1rIr	�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�#���')�~�~��'>�#�B���&�&(�l�l�^�4��y��"�.�.�)�*��	��#�	�@�@�D��Krr�c� �US:�agUS:�agg)N�Pz#22c55e�2z#f59e0bz#ef4444r)r�s r�coverage_bar_colorr�s��
�b�y��	����rus6
    @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); }

    /* ── RECHERCHE GLOBALE ── */
    #search-box {
      padding: 0.7rem 1.2rem 0.6rem;
      border-bottom: 1px solid rgba(255,255,255,0.07);
    }

    #global-search {
      width: 100%;
      background: rgba(255,255,255,0.06);
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 8px;
      padding: 0.5rem 0.7rem;
      color: var(--sidebar-text);
      font-size: 0.8rem;
      font-family: var(--font-sans);
      outline: none;
      transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
    }

    #global-search::placeholder { color: var(--sidebar-text-secondary); }

    #global-search:focus {
      background: rgba(255,255,255,0.1);
      border-color: var(--sidebar-accent);
      box-shadow: 0 0 0 3px rgba(107,140,255,0.18);
    }

    #search-results-count {
      font-size: 0.68rem;
      color: var(--sidebar-text-secondary);
      margin-top: 0.4rem;
      font-family: var(--font-mono);
      min-height: 1em;
    }

    mark.search-hit {
      background: #fde68a;
      color: #1e293b;
      padding: 0 2px;
      border-radius: 3px;
    }

    .nav-sheet-link.nav-dim {
      opacity: 0.25;
      pointer-events: none;
    }

    /* ── 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();

    // ── Recherche globale (composants + signaux) ──────────────────────────
    function escapeRegex(str) {
        return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    function clearHighlights(root) {
        root.querySelectorAll('mark.search-hit').forEach(mark => {
            const parent = mark.parentNode;
            parent.replaceChild(document.createTextNode(mark.textContent), mark);
            parent.normalize();
        });
    }

    function highlightMatches(cell, rawQuery) {
        const regex = new RegExp('(' + escapeRegex(rawQuery) + ')', 'ig');
        const walker = document.createTreeWalker(cell, NodeFilter.SHOW_TEXT, null);
        const textNodes = [];
        let node;
        while ((node = walker.nextNode())) textNodes.push(node);
        textNodes.forEach(textNode => {
            const text = textNode.nodeValue;
            regex.lastIndex = 0;
            if (!regex.test(text)) return;
            regex.lastIndex = 0;
            const span = document.createElement('span');
            span.innerHTML = text.replace(regex, '<mark class="search-hit">$1</mark>');
            const parent = textNode.parentNode;
            parent.replaceChild(span, textNode);
            while (span.firstChild) parent.insertBefore(span.firstChild, span);
            parent.removeChild(span);
        });
    }

    function applyGlobalSearch(rawQuery) {
        const query = rawQuery.trim();
        const q = query.toLowerCase();
        const sections = document.querySelectorAll('#main section');
        const sheetsWithMatches = new Set();
        let totalMatches = 0;

        sections.forEach(section => {
            clearHighlights(section);
            const rows = section.querySelectorAll('tbody tr');
            let sectionMatches = 0;

            rows.forEach(row => {
                if (!q) {
                    row.style.display = '';
                    return;
                }
                const isMatch = row.textContent.toLowerCase().includes(q);
                row.style.display = isMatch ? '' : 'none';
                if (isMatch) {
                    sectionMatches++;
                    totalMatches++;
                    row.querySelectorAll('td').forEach(td => highlightMatches(td, query));
                }
            });

            if (q && rows.length) {
                section.style.display = sectionMatches > 0 ? '' : 'none';
                if (sectionMatches > 0) {
                    const host = section.closest('[id]');
                    if (host && host.id) sheetsWithMatches.add(host.id);
                }
            } else {
                section.style.display = '';
            }
        });

        document.querySelectorAll('.nav-sheet-link').forEach(link => {
            const anchor = link.getAttribute('href').slice(1);
            link.classList.toggle('nav-dim', !!q && !sheetsWithMatches.has(anchor));
        });

        const counter = document.getElementById('search-results-count');
        if (counter) {
            counter.textContent = q ? `${totalMatches} résultat(s)` : '';
        }
    }

    function initGlobalSearch() {
        const input = document.getElementById('global-search');
        if (!input) return;

        input.addEventListener('input', () => applyGlobalSearch(input.value));

        document.addEventListener('keydown', (e) => {
            const tag = document.activeElement ? document.activeElement.tagName : '';
            if (e.key === '/' && tag !== 'INPUT' && tag !== 'TEXTAREA') {
                e.preventDefault();
                input.focus();
            } else if (e.key === 'Escape' && document.activeElement === input) {
                input.value = '';
                applyGlobalSearch('');
                input.blur();
            }
        });
    }

    initGlobalSearch();
�
../index.html�project_name�
pcb_index_urlc���SSKJn [SU55n[U5nUSnUS:aSO	US:aSOS	n[	U5nS
USUSUS
SUSSUSSUSSUSUS3n	SUS3n
USVs0sHo�SUS_M
 nnSn
UH�nUR
R
5RSS5nURUR
S5n[	U5nUR(aSOS	nU
S US!UUR
S"US#USUS$3-
n
M� U(a
U
S%-
n
U
S&-
n
S	nUHEnUR
R
5RSS5nUS'US3[U5-S(--
nMG U[U5-
nS)US*[S+US,UR5R5S-[U5S.US/U	S0U
S1U
S2US3[ S43$s snf)5Nr)�datec3�L# �UHn[UR5v� M g7fr)rHr1)r��ss  rr�� generate_html.<locals>.<genexpr>�s���6�v�!�3�q�}�}�%�%�v�s�"$r�r�dangerr�warnr2z�
    <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�r	r�z.<div class="nav-section-label">Feuilles</div>
rCr�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) · u� TP</p>
    </div>

    <div id="search-box">
      <input type="text" id="global-search" placeholder="Rechercher…  (/)" autocomplete="off" spellcheck="false">
      <div id="search-results-count"></div>
    </div>

    z
    z#

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

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

  <script>
    z
  </script>

</body>
</html>
)�datetimerr�r�rr	r�r_r�r3rr�CSS�today�	isoformatrH�
JAVASCRIPT)r�rrr�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_htmlr0�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�0E�1�V�9�a��h�&�0E�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������!�!�#�+�+�C��5���)�F�8�2�&��u�)=�=��H�H���	�+�F�3�3�D��
)�>�*
�

����.���z�z�|�-�-�/�0��S��[�M��QY�PZ�[��J���M���k��
�F���L��S.�.��?Gs�:Grvc�^�[U5R5n[U5R5nUR5(d[SU35eUR5(d[SU35e[	SU35 [	SUS35 URSS9n[
U5n[X5nU(d[	S5 S	0/4$[	S
[U5S35 /nUGHup�[	SU	S
U
RUR5S35 U
RSS9n[
U5n0UEUEn
[X�5up�[U5nS	nU
RU[S54H&n[U	U5nU(dM[	SU35  O U(d[	SU	S35 [	S[U5S[U5S[U5S35 UR[U	U
R UUUUS95 GM U(a[U5OSnU(a2UR"R%5S:Xa['X�R(S9nO[+X�R(US9nU(aUR-USS9 [	SU35 O[	U5 [/U5nUUU4$)u�Retourne (html_ou_markdown, coverage, sheets) — coverage et sheets sont
utilisés par generate_page.py pour afficher le bouton "Documentation"
(pourcentage de couverture, nombre de feuilles/points de test).uSchéma racine introuvable : zDossier introuvable : uSchéma racine    : zDossier feuilles : rEr^)r`u#  Aucun fichier .kicad_sch trouvé.r2z  u feuille(s) trouvée(s) :
z  Traitement : z (r:�.u    → SVG trouvé dans u!    → Pas de SVG trouvé pour '�'u    → z composant(s), z point(s) de test, u signal(aux) hiérarchique(s))r	r.r/r0r1r3Nz.md)r)rru
Documentation écrite dans : )rr�rd�FileNotFoundError�printrhr�r�rH�relative_to�parentr�r�rprIr,r	�suffixr��generate_markdown_fallbackr�r0�
write_textr�)r�r�rvr�	root_path�root_content�root_lib�
sub_sheetsr�r	r��
sheet_content�	sheet_lib�
merged_libr/r1r0r3r\�output_path�resultr&s                      r�document_projectrD7s����h��'�'�)�I��j�!�)�)�+�J�������"?�	�{� K�L�L�������"8��� E�F�F�	� ���
,�-�	��
�|�2�
.�/��&�&��&�8�L�$�\�2�H��
�6�J��
�3�4��2�r�z��	�B�s�:���:�
;�<�
�F� �
��
���v�R��(8�(8��9J�9J�(K�'L�A�N�O������8�
�)�-�8�	�1�8�1�y�1�
�"A�-�"\��
�1�-�@�����;�;�
�D��I�>�J�,�T�:�>�K��{��1�*��>�?��	?�
��5�d�V�1�=�>�
���Z��)���[�9I�8J�J]�^a�bh�^i�]j�kH�I�	J��
�
�e���Y�Y�!��#�#�

�	�'!�8#)�$�v�,�d�K��{�)�)�/�/�1�U�:�+�F���P���v�N�N�R_�`������v���8�
�/��}�=�>�
�f�
�#�F�+�H��8�V�#�#rc���SU3SSSSS/n[U5nUSUSS3SUS	S
USS3SS
S/-
nUSH9nURSUSSUSSUSS
USSUSS
USS3
5 M; U/SQ-
nUGH�nUSUR3SSURS3S/-
nUR(a�U/SQ-
nURH\n[
R
URUR5nURSURSUSURS35 M^ URS5 UR(dM�U/SQ-
nURH�nS UR;aURRS 5S!OURn	S"RS#UR55=(d S$n
UR(aS%OSnURSURUSUR SURSU	SU
S35 M� URS5 GM� UVVs/sH#oDR"Ho�RU4PM M% n
nnU
(a_U
R%S&S'9 U/S(Q-
nU
H1up�URSURSURS35 M3 URS5 S)RU5$s snnf)*Nu# Documentation composants — r2u4Généré automatiquement depuis les schémas KiCad.�---z)## Couverture de documentation globale : r��%r8r�rr�u éléments documentés)z/| Feuille | Couverture | Composants | Signaux |z/|---------|------------|------------|---------|r�z| r	z | r�z% | r�r�r�r�z |)r2rFr2z## zFichier : `�`)u### Signaux hiérarchiquesr2z$| Signal | Direction | Description |z$|--------|-----------|-------------|)z### Composantsr2u7| Réf. | Valeur | Description | Boîtier | Variantes |z5|------|--------|-------------|---------|-----------|r�rBr�c3�# �UH=nURUR(dSOS-UR(aSOS-v� M? g7f)z (hors BOM)r2z (DNP)Nr�)r�r�s  rr��-generate_markdown_fallback.<locals>.<genexpr>�s9���%�*���F�F�1�8�8�m��D�TU�TY�TY��_a�b�*�s�AArzu
 ⚠️DNPc���[R"SUSR5(a;[[R"SUSR5R	55$S$rr�r	s rr��,generate_markdown_fallback.<locals>.<lambda>�sP���	�	�&�!�A�$�.�.�9�9�$'�r�y�y���1����'H�'N�'N�'P�#Q�$A�?@�$Arr�)rFr2u6## Récapitulatif — Points de test (toutes feuilles)r2u| Réf. | Description |z|------|-------------|rE)r�rIr	r.r0r�r�r*rr/rr�r�r!rrrr1r)r�r�linesr&rr�r�r�r�rr!rr�r
rs               rr9r9s��
)�,��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�
�|�|��.�2�2�3�9�9�c�i�i�H�	����r�#�(�(��3�y�k��S�_�_�<M�R�P�Q�$�
�L�L���������
�E�
�(�(��<?�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�)�
�L�L���9�<&,�D�V��m�m�����|�m�|�V�G�D�����A��	B�
�
�	
�� '�O�K��L�L�2�b�l�l�^�3�r�~�~�.>�b�A�B� '�
���R���9�9�U����Es�*K$�__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.htmlr9��)r2r)Nr)r2)0rP�os�dataclassesrr�pathlibrrrr$r(r,rr�r?r"rMrWrrZrpr�r�r�r�r��dictr��tupler�r�r�r�rrrr!r$r0rDr9r
�sysrH�argvr5�exit�pcb_urlrrr�<module>r[s���	�	�(�������
�:�:��:�����������
�����	�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��k��\L�
�^p�$�u�+�p�S�p�c�p�hk�p�pUd�E$��E$�"�E$�,/�$�J�E$�NQ�E$�
�3��d�7�m�#�$�E$�P>�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