Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	
		thibaud frere
		
	commited on
		
		
					Commit 
							
							·
						
						9e27095
	
1
								Parent(s):
							
							e1c7423
								
update
Browse files
    	
        app/src/components/Accordion.astro
    CHANGED
    
    | @@ -48,7 +48,7 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" "); | |
| 48 | 
             
                  };
         | 
| 49 |  | 
| 50 | 
             
                  const close = () => {
         | 
| 51 | 
            -
                    const start = content.scrollHeight;
         | 
| 52 | 
             
                    wrapper.style.height = `${start}px`;
         | 
| 53 | 
             
                    void wrapper.offsetHeight; // reflow
         | 
| 54 | 
             
                    wrapper.style.transition = `height ${duration}ms ease`;
         | 
| @@ -83,7 +83,6 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" "); | |
| 83 | 
             
              }
         | 
| 84 |  | 
| 85 | 
             
              .accordion[open] {
         | 
| 86 | 
            -
                box-shadow: 0 6px 22px rgba(0,0,0,0.08);
         | 
| 87 | 
             
                border-color: color-mix(in oklab, var(--border-color), var(--primary-color) 20%);
         | 
| 88 | 
             
              }
         | 
| 89 |  | 
| @@ -92,13 +91,15 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" "); | |
| 92 | 
             
                display: flex;
         | 
| 93 | 
             
                align-items: center;
         | 
| 94 | 
             
                justify-content: space-between;
         | 
| 95 | 
            -
                gap:  | 
| 96 | 
            -
                padding:  | 
| 97 | 
             
                cursor: pointer;
         | 
| 98 | 
             
                color: var(--text-color);
         | 
| 99 | 
             
                user-select: none;
         | 
| 100 | 
             
              }
         | 
| 101 |  | 
|  | |
|  | |
| 102 | 
             
              /* Remove native marker */
         | 
| 103 | 
             
              .accordion__summary::-webkit-details-marker {
         | 
| 104 | 
             
                display: none;
         | 
| @@ -126,10 +127,23 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" "); | |
| 126 | 
             
                overflow: hidden;
         | 
| 127 | 
             
                height: 0px;
         | 
| 128 | 
             
                will-change: height;
         | 
|  | |
| 129 | 
             
              }
         | 
| 130 |  | 
| 131 | 
             
              .accordion__content {
         | 
| 132 | 
            -
                padding:  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 133 | 
             
              }
         | 
| 134 |  | 
| 135 | 
             
              /* Focus styles for accessibility */
         | 
|  | |
| 48 | 
             
                  };
         | 
| 49 |  | 
| 50 | 
             
                  const close = () => {
         | 
| 51 | 
            +
                    const start = wrapper.offsetHeight || content.scrollHeight;
         | 
| 52 | 
             
                    wrapper.style.height = `${start}px`;
         | 
| 53 | 
             
                    void wrapper.offsetHeight; // reflow
         | 
| 54 | 
             
                    wrapper.style.transition = `height ${duration}ms ease`;
         | 
|  | |
| 83 | 
             
              }
         | 
| 84 |  | 
| 85 | 
             
              .accordion[open] {
         | 
|  | |
| 86 | 
             
                border-color: color-mix(in oklab, var(--border-color), var(--primary-color) 20%);
         | 
| 87 | 
             
              }
         | 
| 88 |  | 
|  | |
| 91 | 
             
                display: flex;
         | 
| 92 | 
             
                align-items: center;
         | 
| 93 | 
             
                justify-content: space-between;
         | 
| 94 | 
            +
                gap: 4px;
         | 
| 95 | 
            +
                padding: 4px;
         | 
| 96 | 
             
                cursor: pointer;
         | 
| 97 | 
             
                color: var(--text-color);
         | 
| 98 | 
             
                user-select: none;
         | 
| 99 | 
             
              }
         | 
| 100 |  | 
| 101 | 
            +
              /* Remove conditional padding to avoid jump on close */
         | 
| 102 | 
            +
             | 
| 103 | 
             
              /* Remove native marker */
         | 
| 104 | 
             
              .accordion__summary::-webkit-details-marker {
         | 
| 105 | 
             
                display: none;
         | 
|  | |
| 127 | 
             
                overflow: hidden;
         | 
| 128 | 
             
                height: 0px;
         | 
| 129 | 
             
                will-change: height;
         | 
| 130 | 
            +
                position: relative;
         | 
| 131 | 
             
              }
         | 
| 132 |  | 
| 133 | 
             
              .accordion__content {
         | 
| 134 | 
            +
                padding: 12px 4px 4px;
         | 
| 135 | 
            +
              }
         | 
| 136 | 
            +
             | 
| 137 | 
            +
              /* Separator between header and content when open (edge-to-edge) */
         | 
| 138 | 
            +
              .accordion[open] .accordion__content-wrapper::before {
         | 
| 139 | 
            +
                content: "";
         | 
| 140 | 
            +
                position: absolute;
         | 
| 141 | 
            +
                left: 0;
         | 
| 142 | 
            +
                right: 0;
         | 
| 143 | 
            +
                top: 6px; /* space below header */
         | 
| 144 | 
            +
                height: 1px;
         | 
| 145 | 
            +
                background: var(--neutral-300);
         | 
| 146 | 
            +
                pointer-events: none;
         | 
| 147 | 
             
              }
         | 
| 148 |  | 
| 149 | 
             
              /* Focus styles for accessibility */
         | 
    	
        app/src/content/chapters/available-blocks.mdx
    CHANGED
    
    | @@ -229,8 +229,18 @@ Accessible accordion based on `details/summary`. You can pass any children conte | |
| 229 | 
             
              <p>This one stays collapsed until the user clicks the summary.</p>
         | 
| 230 | 
             
            </Accordion>
         | 
| 231 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 232 | 
             
            <small className="muted">Example</small>
         | 
| 233 | 
            -
             | 
| 234 | 
             
            import Accordion from '../components/Accordion.astro'
         | 
| 235 |  | 
| 236 | 
             
            <Accordion title="Accordion title" open>
         | 
| @@ -243,7 +253,27 @@ import Accordion from '../components/Accordion.astro' | |
| 243 | 
             
                <li>Item B</li>
         | 
| 244 | 
             
              </ul>
         | 
| 245 | 
             
            </Accordion>
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 246 | 
             
            ```
         | 
|  | |
|  | |
| 247 |  | 
| 248 |  | 
| 249 | 
             
            ### Minimal table
         | 
| @@ -283,22 +313,29 @@ The main purpose of the ```HtmlFragment``` component is to **embed** a **Plotly* | |
| 283 |  | 
| 284 | 
             
            They exist in the `app/src/content/fragments` folder.
         | 
| 285 |  | 
|  | |
|  | |
| 286 | 
             
            Here are some examples of the two **libraries** in the template:
         | 
| 287 |  | 
| 288 | 
            -
            D3 version
         | 
| 289 | 
             
              <div className="plot-card">
         | 
| 290 | 
             
                <HtmlFragment src="d3-line.html" />
         | 
| 291 | 
             
            </div>
         | 
|  | |
| 292 |  | 
| 293 | 
             
              <div className="plot-card">
         | 
| 294 | 
             
                <HtmlFragment src="d3-bar.html" />
         | 
| 295 | 
             
            </div>
         | 
|  | |
| 296 |  | 
|  | |
|  | |
|  | |
| 297 |  | 
| 298 | 
            -
            Plotly version
         | 
| 299 | 
             
            <div className="plot-card">
         | 
| 300 | 
             
              <HtmlFragment src="line.html" />
         | 
| 301 | 
             
            </div>
         | 
|  | |
| 302 |  | 
| 303 | 
             
            <small className="muted">Example</small>
         | 
| 304 | 
             
            ```mdx
         | 
| @@ -315,8 +352,22 @@ You can embed external content in your article using **iframes**. For example, * | |
| 315 |  | 
| 316 | 
             
            <iframe className="plot-card" src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="660" frameborder="0"></iframe>
         | 
| 317 |  | 
|  | |
| 318 | 
             
            <small className="muted">Example</small>
         | 
| 319 | 
             
            ```mdx
         | 
| 320 | 
             
            <iframe frameborder="0" scrolling="no" style="width:100%; height:292px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F1004ae37b87887cde597c9060fb067faa060bafe%2Fsetup.py&style=default&type=code&showBorder=on&showLineNumbers=on"></iframe>
         | 
| 321 | 
             
            <iframe src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="600" frameborder="0"></iframe>
         | 
| 322 | 
             
            ```
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 229 | 
             
              <p>This one stays collapsed until the user clicks the summary.</p>
         | 
| 230 | 
             
            </Accordion>
         | 
| 231 |  | 
| 232 | 
            +
            <Accordion title="Accordion with code example">
         | 
| 233 | 
            +
            ```ts
         | 
| 234 | 
            +
            function greet(name: string) {
         | 
| 235 | 
            +
              console.log(`Hello, ${name}`);
         | 
| 236 | 
            +
            }
         | 
| 237 | 
            +
             | 
| 238 | 
            +
            greet("Astro");
         | 
| 239 | 
            +
            ```
         | 
| 240 | 
            +
            </Accordion>
         | 
| 241 | 
            +
             | 
| 242 | 
             
            <small className="muted">Example</small>
         | 
| 243 | 
            +
            ````mdx
         | 
| 244 | 
             
            import Accordion from '../components/Accordion.astro'
         | 
| 245 |  | 
| 246 | 
             
            <Accordion title="Accordion title" open>
         | 
|  | |
| 253 | 
             
                <li>Item B</li>
         | 
| 254 | 
             
              </ul>
         | 
| 255 | 
             
            </Accordion>
         | 
| 256 | 
            +
             | 
| 257 | 
            +
            <Accordion title="Accordion with code example">
         | 
| 258 | 
            +
            ```ts
         | 
| 259 | 
            +
            function greet(name: string) {
         | 
| 260 | 
            +
              console.log(`Hello, ${name}`);
         | 
| 261 | 
            +
            }
         | 
| 262 | 
            +
             | 
| 263 | 
            +
            greet("Astro");
         | 
| 264 | 
            +
            ```
         | 
| 265 | 
            +
            </Accordion>
         | 
| 266 | 
            +
             | 
| 267 | 
            +
            <Accordion title="Accordion with code example">
         | 
| 268 | 
            +
            ```ts
         | 
| 269 | 
            +
            function greet(name: string) {
         | 
| 270 | 
            +
              console.log(`Hello, ${name}`);
         | 
| 271 | 
            +
            }
         | 
| 272 | 
            +
             | 
| 273 | 
            +
            greet("Astro");
         | 
| 274 | 
             
            ```
         | 
| 275 | 
            +
            </Accordion>
         | 
| 276 | 
            +
            ````
         | 
| 277 |  | 
| 278 |  | 
| 279 | 
             
            ### Minimal table
         | 
|  | |
| 313 |  | 
| 314 | 
             
            They exist in the `app/src/content/fragments` folder.
         | 
| 315 |  | 
| 316 | 
            +
            Plotly and D3 are already supported by the template. For researchers who want to stay in **Python** while targeting **D3**, the [d3blocks](https://github.com/d3blocks/d3blocks) library lets you create interactive D3 charts with only a few lines of code. In **2025**, **D3** often provides more flexibility and a more web‑native rendering than **Plotly** for custom visualizations.
         | 
| 317 | 
            +
             | 
| 318 | 
             
            Here are some examples of the two **libraries** in the template:
         | 
| 319 |  | 
| 320 | 
            +
            <small class="muted">D3 version</small>
         | 
| 321 | 
             
              <div className="plot-card">
         | 
| 322 | 
             
                <HtmlFragment src="d3-line.html" />
         | 
| 323 | 
             
            </div>
         | 
| 324 | 
            +
            <caption className="caption">D3 Line chart — simple time series example.</caption>
         | 
| 325 |  | 
| 326 | 
             
              <div className="plot-card">
         | 
| 327 | 
             
                <HtmlFragment src="d3-bar.html" />
         | 
| 328 | 
             
            </div>
         | 
| 329 | 
            +
            <caption className="caption">D3 Bar chart — categorical distribution example.</caption>
         | 
| 330 |  | 
| 331 | 
            +
            --- 
         | 
| 332 | 
            +
             | 
| 333 | 
            +
            <small class="muted">Plotly version</small>
         | 
| 334 |  | 
|  | |
| 335 | 
             
            <div className="plot-card">
         | 
| 336 | 
             
              <HtmlFragment src="line.html" />
         | 
| 337 | 
             
            </div>
         | 
| 338 | 
            +
            <caption className="caption">Plotly Line chart — interactive time series with hover and zoom.</caption>
         | 
| 339 |  | 
| 340 | 
             
            <small className="muted">Example</small>
         | 
| 341 | 
             
            ```mdx
         | 
|  | |
| 352 |  | 
| 353 | 
             
            <iframe className="plot-card" src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="660" frameborder="0"></iframe>
         | 
| 354 |  | 
| 355 | 
            +
             | 
| 356 | 
             
            <small className="muted">Example</small>
         | 
| 357 | 
             
            ```mdx
         | 
| 358 | 
             
            <iframe frameborder="0" scrolling="no" style="width:100%; height:292px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F1004ae37b87887cde597c9060fb067faa060bafe%2Fsetup.py&style=default&type=code&showBorder=on&showLineNumbers=on"></iframe>
         | 
| 359 | 
             
            <iframe src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="600" frameborder="0"></iframe>
         | 
| 360 | 
             
            ```
         | 
| 361 | 
            +
             | 
| 362 | 
            +
            #### gradio
         | 
| 363 | 
            +
             | 
| 364 | 
            +
            You can also embed **gradio** apps.
         | 
| 365 | 
            +
             | 
| 366 | 
            +
            <gradio-app theme_mode="light" space="hebrew-llm-leaderboard/leaderboard"></gradio-app>
         | 
| 367 | 
            +
             | 
| 368 | 
            +
             | 
| 369 | 
            +
             | 
| 370 | 
            +
            <small className="muted">Example</small>
         | 
| 371 | 
            +
            ```mdx
         | 
| 372 | 
            +
            <gradio-app theme_mode="light" space="hebrew-llm-leaderboard/leaderboard"></gradio-app>
         | 
| 373 | 
            +
            ```
         | 
    	
        app/src/content/chapters/writing-your-content.mdx
    CHANGED
    
    | @@ -16,7 +16,7 @@ Your article lives in two places | |
| 16 | 
             
            - `app/src/content/` — where you can find the `article.mdx`, `bibliography.bib` and html fragments.
         | 
| 17 | 
             
            - `app/src/assets/` — images, audio, and other static assets. (handled by git lfs)
         | 
| 18 |  | 
| 19 | 
            -
            The `article.mdx` file is  | 
| 20 |  | 
| 21 | 
             
            <small className="muted">Example</small>
         | 
| 22 | 
             
            ```mdx
         | 
| @@ -49,21 +49,38 @@ This is a short paragraph written in Markdown. Below is an example image: | |
| 49 | 
             
            <Image src={placeholder} alt="Example image" />
         | 
| 50 | 
             
            ```
         | 
| 51 |  | 
| 52 | 
            -
            ###  | 
| 53 |  | 
| 54 | 
            -
             | 
| 55 |  | 
| 56 | 
             
            <small className="muted">Example</small>
         | 
| 57 | 
             
            ```mdx
         | 
| 58 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 59 |  | 
| 60 | 
            -
             | 
|  | |
|  | |
| 61 |  | 
| 62 | 
            -
             | 
| 63 |  | 
| 64 | 
            -
             | 
| 65 | 
            -
            - This is a second list item
         | 
| 66 | 
            -
            - This is a third list item
         | 
| 67 | 
             
            ```
         | 
| 68 |  | 
| 69 |  | 
|  | |
| 16 | 
             
            - `app/src/content/` — where you can find the `article.mdx`, `bibliography.bib` and html fragments.
         | 
| 17 | 
             
            - `app/src/assets/` — images, audio, and other static assets. (handled by git lfs)
         | 
| 18 |  | 
| 19 | 
            +
            The `article.mdx` file is the main file that contains your article.
         | 
| 20 |  | 
| 21 | 
             
            <small className="muted">Example</small>
         | 
| 22 | 
             
            ```mdx
         | 
|  | |
| 49 | 
             
            <Image src={placeholder} alt="Example image" />
         | 
| 50 | 
             
            ```
         | 
| 51 |  | 
| 52 | 
            +
            ### MDX 
         | 
| 53 |  | 
| 54 | 
            +
            MDX is a mix of Markdown and HTML/JSX: write regular Markdown, and embed interactive components inline when needed. We’ll describe the available components you can use later in this guide. For Markdown syntax, see the complete [Markdown documentation](https://www.markdownguide.org/basic-syntax/).
         | 
| 55 |  | 
| 56 | 
             
            <small className="muted">Example</small>
         | 
| 57 | 
             
            ```mdx
         | 
| 58 | 
            +
            {/* IMPORTS */}
         | 
| 59 | 
            +
            import { Image } from 'astro:assets'
         | 
| 60 | 
            +
            import placeholder from '../assets/images/placeholder.png'
         | 
| 61 | 
            +
            import Aside from '../components/Aside.astro'
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            # Mixing Markdown and components
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            This paragraph is written in Markdown.
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            <Aside>A short callout inserted via a component.</Aside>
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            Below is an image imported via Astro and optimized at build time:
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            <Image src={placeholder} alt="Sample image with Astro optimization" />
         | 
| 72 | 
            +
            ```
         | 
| 73 | 
            +
             | 
| 74 | 
            +
             | 
| 75 | 
            +
            Use `---` on its own line to insert a horizontal separator between sections. This is a standard Markdown “thematic break”. Don’t confuse it with the `---` used at the very top of the file to delimit the frontmatter.
         | 
| 76 |  | 
| 77 | 
            +
            <small className="muted">Example</small>
         | 
| 78 | 
            +
            ```mdx
         | 
| 79 | 
            +
            Intro paragraph.
         | 
| 80 |  | 
| 81 | 
            +
            ---
         | 
| 82 |  | 
| 83 | 
            +
            Next section begins here.
         | 
|  | |
|  | |
| 84 | 
             
            ```
         | 
| 85 |  | 
| 86 |  | 
    	
        app/src/content/fragments/palettes.html
    CHANGED
    
    | @@ -44,21 +44,93 @@ | |
| 44 | 
             
                      const base = chroma(baseHex);
         | 
| 45 | 
             
                      const lc = base.lch();
         | 
| 46 | 
             
                      const baseH = base.get('hsl.h') || 0;
         | 
| 47 | 
            -
                      const  | 
| 48 | 
            -
                      const  | 
| 49 | 
            -
                      const  | 
| 50 | 
            -
             | 
| 51 | 
            -
                       | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 52 | 
             
                  }},
         | 
| 53 | 
             
                  { key: 'sequential', title: 'Sequential', desc: 'For <strong>numeric scales</strong>; gradient from <strong>light to dark</strong>; ideal for <strong>heatmaps</strong>.', generator: (baseHex) => {
         | 
| 54 | 
             
                      const c = chroma(baseHex).saturate(0.3);
         | 
| 55 | 
            -
                      return chroma.scale([c. | 
| 56 | 
             
                  }},
         | 
| 57 | 
             
                  { key: 'diverging', title: 'Diverging', desc: 'For <strong>centered ranges</strong> with <strong>two extremes</strong> (e.g., negatives/positives) around a <strong>baseline</strong>.', generator: (baseHex) => {
         | 
| 58 | 
             
                      const baseH = chroma(baseHex).get('hsl.h');
         | 
| 59 | 
             
                      const compH = (baseH + 180) % 360;
         | 
| 60 | 
            -
                      const left = chroma.hsl( | 
| 61 | 
            -
                      const right = chroma.hsl( | 
| 62 | 
             
                      const center = '#ffffff';
         | 
| 63 | 
             
                      const leftRamp = chroma.scale([left, center]).mode('lch').correctLightness(true).colors(4);
         | 
| 64 | 
             
                      const rightRamp = chroma.scale([center, right]).mode('lch').correctLightness(true).colors(4);
         | 
|  | |
| 44 | 
             
                      const base = chroma(baseHex);
         | 
| 45 | 
             
                      const lc = base.lch();
         | 
| 46 | 
             
                      const baseH = base.get('hsl.h') || 0;
         | 
| 47 | 
            +
                      const L0 = Math.max(40, Math.min(85, lc[0] || 70));
         | 
| 48 | 
            +
                      const C0 = Math.max(45, Math.min(75, lc[1] || 70));
         | 
| 49 | 
            +
                      const MIN_DELTA = 18; // distance minimale en Lab
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      const seen = new Set();
         | 
| 52 | 
            +
                      const results = [];
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      const makeSafe = (h, L, C) => {
         | 
| 55 | 
            +
                        let c = C;
         | 
| 56 | 
            +
                        let col = chroma.lch(L, c, h);
         | 
| 57 | 
            +
                        let guard = 0;
         | 
| 58 | 
            +
                        while (col.clipped && typeof col.clipped === 'function' && col.clipped() && c > 30 && guard < 8) {
         | 
| 59 | 
            +
                          c -= 5;
         | 
| 60 | 
            +
                          col = chroma.lch(L, c, h);
         | 
| 61 | 
            +
                          guard++;
         | 
| 62 | 
            +
                        }
         | 
| 63 | 
            +
                        return col;
         | 
| 64 | 
            +
                      };
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      const isFarEnough = (hex) => results.every(prev => chroma.distance(hex, prev, 'lab') >= MIN_DELTA);
         | 
| 67 | 
            +
                      const pushHex = (col) => {
         | 
| 68 | 
            +
                        const hex = col.hex();
         | 
| 69 | 
            +
                        if (!seen.has(hex.toLowerCase())) { results.push(hex); seen.add(hex.toLowerCase()); }
         | 
| 70 | 
            +
                      };
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      // Base en premier
         | 
| 73 | 
            +
                      pushHex(base);
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                      // Cinq couleurs supplémentaires, espacées de 60°
         | 
| 76 | 
            +
                      const angles = [60, 120, 180, 240, 300];
         | 
| 77 | 
            +
                      const hueOffsets = [0, 20, -20, 40, -40, 60, -60, 80, -80];
         | 
| 78 | 
            +
                      const lVariants = [L0, Math.max(40, L0 - 6), Math.min(85, L0 + 6)];
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                      angles.forEach(step => {
         | 
| 81 | 
            +
                        let accepted = false;
         | 
| 82 | 
            +
                        for (let li = 0; li < lVariants.length && !accepted; li++) {
         | 
| 83 | 
            +
                          for (let oi = 0; oi < hueOffsets.length && !accepted; oi++) {
         | 
| 84 | 
            +
                            let h = (baseH + step + hueOffsets[oi] + 360) % 360;
         | 
| 85 | 
            +
                            let col = makeSafe(h, lVariants[li], C0);
         | 
| 86 | 
            +
                            const hex = col.hex();
         | 
| 87 | 
            +
                            if (!seen.has(hex.toLowerCase()) && isFarEnough(hex)) {
         | 
| 88 | 
            +
                              pushHex(col);
         | 
| 89 | 
            +
                              accepted = true;
         | 
| 90 | 
            +
                            }
         | 
| 91 | 
            +
                          }
         | 
| 92 | 
            +
                        }
         | 
| 93 | 
            +
                        if (!accepted) {
         | 
| 94 | 
            +
                          // Réduction de C si nécessaire
         | 
| 95 | 
            +
                          let cTry = C0 - 10;
         | 
| 96 | 
            +
                          let h = (baseH + step + 360) % 360;
         | 
| 97 | 
            +
                          let trials = 0;
         | 
| 98 | 
            +
                          while (!accepted && cTry >= 30 && trials < 6) {
         | 
| 99 | 
            +
                            const col = makeSafe(h, L0, cTry);
         | 
| 100 | 
            +
                            const hex = col.hex();
         | 
| 101 | 
            +
                            if (!seen.has(hex.toLowerCase()) && isFarEnough(hex)) {
         | 
| 102 | 
            +
                              pushHex(col);
         | 
| 103 | 
            +
                              accepted = true;
         | 
| 104 | 
            +
                              break;
         | 
| 105 | 
            +
                            }
         | 
| 106 | 
            +
                            cTry -= 5;
         | 
| 107 | 
            +
                            trials++;
         | 
| 108 | 
            +
                          }
         | 
| 109 | 
            +
                          // Dernier recours: choisir la teinte la plus éloignée possible même si < seuil
         | 
| 110 | 
            +
                          if (!accepted) {
         | 
| 111 | 
            +
                            let bestHex = null; let bestMin = -1;
         | 
| 112 | 
            +
                            hueOffsets.forEach(off => {
         | 
| 113 | 
            +
                              const hh = (baseH + step + off + 360) % 360;
         | 
| 114 | 
            +
                              const cand = makeSafe(hh, L0, C0).hex();
         | 
| 115 | 
            +
                              const minD = results.reduce((m, prev) => Math.min(m, chroma.distance(cand, prev, 'lab')), Infinity);
         | 
| 116 | 
            +
                              if (minD > bestMin && !seen.has(cand.toLowerCase())) { bestMin = minD; bestHex = cand; }
         | 
| 117 | 
            +
                            });
         | 
| 118 | 
            +
                            if (bestHex) { seen.add(bestHex.toLowerCase()); results.push(bestHex); }
         | 
| 119 | 
            +
                          }
         | 
| 120 | 
            +
                        }
         | 
| 121 | 
            +
                      });
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                      return results.slice(0, 6);
         | 
| 124 | 
             
                  }},
         | 
| 125 | 
             
                  { key: 'sequential', title: 'Sequential', desc: 'For <strong>numeric scales</strong>; gradient from <strong>light to dark</strong>; ideal for <strong>heatmaps</strong>.', generator: (baseHex) => {
         | 
| 126 | 
             
                      const c = chroma(baseHex).saturate(0.3);
         | 
| 127 | 
            +
                      return chroma.scale([c.darken(2), c, c.brighten(2)]).mode('lab').correctLightness(true).colors(6);
         | 
| 128 | 
             
                  }},
         | 
| 129 | 
             
                  { key: 'diverging', title: 'Diverging', desc: 'For <strong>centered ranges</strong> with <strong>two extremes</strong> (e.g., negatives/positives) around a <strong>baseline</strong>.', generator: (baseHex) => {
         | 
| 130 | 
             
                      const baseH = chroma(baseHex).get('hsl.h');
         | 
| 131 | 
             
                      const compH = (baseH + 180) % 360;
         | 
| 132 | 
            +
                      const left = chroma.hsl(baseH, 0.75, 0.55);
         | 
| 133 | 
            +
                      const right = chroma.hsl(compH, 0.75, 0.55);
         | 
| 134 | 
             
                      const center = '#ffffff';
         | 
| 135 | 
             
                      const leftRamp = chroma.scale([left, center]).mode('lch').correctLightness(true).colors(4);
         | 
| 136 | 
             
                      const rightRamp = chroma.scale([center, right]).mode('lch').correctLightness(true).colors(4);
         | 
    	
        app/src/pages/index.astro
    CHANGED
    
    | @@ -66,9 +66,10 @@ const bibtex = `@misc{${bibKey},\n  title={${titleFlat}},\n  author={${authorsBi | |
| 66 | 
             
                  })();
         | 
| 67 | 
             
                </script>
         | 
| 68 |  | 
| 69 | 
            -
                
         | 
| 70 | 
             
                <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
         | 
| 71 | 
             
                <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
         | 
|  | |
| 72 | 
             
              </head>
         | 
| 73 | 
             
              <body>
         | 
| 74 | 
             
                <ThemeToggle />
         | 
|  | |
| 66 | 
             
                  })();
         | 
| 67 | 
             
                </script>
         | 
| 68 |  | 
| 69 | 
            +
                <!-- TO MANAGE PROPERLY -->
         | 
| 70 | 
             
                <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
         | 
| 71 | 
             
                <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
         | 
| 72 | 
            +
                <script type="module" src="https://gradio.s3-us-west-2.amazonaws.com/4.4.0/gradio.js"> </script>
         | 
| 73 | 
             
              </head>
         | 
| 74 | 
             
              <body>
         | 
| 75 | 
             
                <ThemeToggle />
         | 
    	
        app/src/styles/components/_code.css
    CHANGED
    
    | @@ -63,3 +63,6 @@ html[data-theme='light'] .astro-code { | |
| 63 | 
             
            }
         | 
| 64 |  | 
| 65 |  | 
|  | |
|  | |
|  | 
|  | |
| 63 | 
             
            }
         | 
| 64 |  | 
| 65 |  | 
| 66 | 
            +
             | 
| 67 | 
            +
            /* Overrides inside Accordion: remove padding and border on code containers */
         | 
| 68 | 
            +
            .accordion .astro-code { padding: 0; border: none; }
         | 
 
			
