Spaces:
Running
Running
thibaud frere
commited on
Commit
·
b9e7b9b
1
Parent(s):
98af9a5
update
Browse files- .gitignore +2 -0
- README.md +10 -2
- app/.astro/astro/content.d.ts +17 -1
- app/astro.config.mjs +5 -4
- app/package-lock.json +0 -0
- app/package.json +0 -0
- app/scripts/export-pdf.mjs +78 -0
- app/src/assets/images/{placeholder.jpg → placeholder.png} +2 -2
- app/src/components/MermaidDemo.astro +11 -0
- app/src/components/Meta.astro +14 -3
- app/src/components/Note.astro +23 -0
- app/src/content/article.mdx +50 -330
- app/src/content/chapters/best-pratices.mdx +46 -0
- app/src/content/chapters/writing-you-content.mdx +370 -0
- app/src/content/fragments/banner.html +2 -11
- app/src/content/fragments/bar.html +1 -22
- app/src/content/fragments/color-picker.html +213 -0
- app/src/content/fragments/d3-bar.html +200 -0
- app/src/content/fragments/d3-line.html +429 -0
- app/src/content/fragments/heatmap.html +159 -1
- app/src/content/fragments/line.html +84 -1
- app/src/content/fragments/palettes.html +40 -7
- app/src/pages/index.astro +1 -1
- app/src/styles/{_base.scss → _base.css} +59 -61
- app/src/styles/{_layout.scss → _layout.css} +24 -22
- app/src/styles/{_variables.scss → _variables.css} +22 -13
- app/src/styles/components/{_code.scss → _code.css} +41 -5
- app/src/styles/components/{_footer.scss → _footer.css} +5 -3
- app/src/styles/components/_poltly.css +45 -0
- app/src/styles/{global.scss → global.css} +16 -33
- fragments/d3js/banner.html +34 -30
- fragments/d3js/line.html +356 -0
- fragments/plotly/banner.py +1 -1
- fragments/plotly/bar.py +147 -90
- fragments/plotly/heatmap.py +1 -1
- fragments/plotly/line.py +82 -54
.gitignore
CHANGED
@@ -21,3 +21,5 @@ node_modules/
|
|
21 |
|
22 |
# PDF export
|
23 |
app/public/*.pdf
|
|
|
|
|
|
21 |
|
22 |
# PDF export
|
23 |
app/public/*.pdf
|
24 |
+
app/public/*.png
|
25 |
+
app/public/*.jpg
|
README.md
CHANGED
@@ -7,5 +7,13 @@ sdk: docker
|
|
7 |
pinned: false
|
8 |
header: mini
|
9 |
app_port: 8080
|
10 |
-
thumbnail: https://huggingface.co/spaces/
|
11 |
-
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
pinned: false
|
8 |
header: mini
|
9 |
app_port: 8080
|
10 |
+
thumbnail: https://huggingface.co/spaces/tfrere/research-paper-template/thumb.jpg
|
11 |
+
---
|
12 |
+
|
13 |
+
TO DO :
|
14 |
+
|
15 |
+
- fix banner pour l'export pdf et la thumb
|
16 |
+
- rename le titre ?
|
17 |
+
- Vérifier la biliographie comment elle marche
|
18 |
+
|
19 |
+
- deploy
|
app/.astro/astro/content.d.ts
CHANGED
@@ -151,7 +151,23 @@ declare module 'astro:content' {
|
|
151 |
>;
|
152 |
|
153 |
type ContentEntryMap = {
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
};
|
156 |
|
157 |
type DataEntryMap = {
|
|
|
151 |
>;
|
152 |
|
153 |
type ContentEntryMap = {
|
154 |
+
"chapters": {
|
155 |
+
"best-pratices.mdx": {
|
156 |
+
id: "best-pratices.mdx";
|
157 |
+
slug: "best-pratices";
|
158 |
+
body: string;
|
159 |
+
collection: "chapters";
|
160 |
+
data: any
|
161 |
+
} & { render(): Render[".mdx"] };
|
162 |
+
"writing-you-content.mdx": {
|
163 |
+
id: "writing-you-content.mdx";
|
164 |
+
slug: "writing-you-content";
|
165 |
+
body: string;
|
166 |
+
collection: "chapters";
|
167 |
+
data: any
|
168 |
+
} & { render(): Render[".mdx"] };
|
169 |
+
};
|
170 |
+
|
171 |
};
|
172 |
|
173 |
type DataEntryMap = {
|
app/astro.config.mjs
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import { defineConfig } from 'astro/config';
|
2 |
import mdx from '@astrojs/mdx';
|
|
|
3 |
import remarkMath from 'remark-math';
|
4 |
import rehypeKatex from 'rehype-katex';
|
5 |
import remarkToc from 'remark-toc';
|
@@ -11,8 +12,7 @@ import rehypeCitation from 'rehype-citation';
|
|
11 |
|
12 |
export default defineConfig({
|
13 |
output: 'static',
|
14 |
-
integrations: [mdx()]
|
15 |
-
,
|
16 |
devToolbar: {
|
17 |
enabled: false
|
18 |
},
|
@@ -23,9 +23,10 @@ export default defineConfig({
|
|
23 |
dark: 'github-dark'
|
24 |
},
|
25 |
defaultColor: false,
|
26 |
-
wrap:
|
27 |
langAlias: {
|
28 |
-
|
|
|
29 |
}
|
30 |
},
|
31 |
remarkPlugins: [
|
|
|
1 |
import { defineConfig } from 'astro/config';
|
2 |
import mdx from '@astrojs/mdx';
|
3 |
+
import mermaid from 'astro-mermaid';
|
4 |
import remarkMath from 'remark-math';
|
5 |
import rehypeKatex from 'rehype-katex';
|
6 |
import remarkToc from 'remark-toc';
|
|
|
12 |
|
13 |
export default defineConfig({
|
14 |
output: 'static',
|
15 |
+
integrations: [mermaid({ theme: 'forest', autoTheme: true }), mdx()],
|
|
|
16 |
devToolbar: {
|
17 |
enabled: false
|
18 |
},
|
|
|
23 |
dark: 'github-dark'
|
24 |
},
|
25 |
defaultColor: false,
|
26 |
+
wrap: false,
|
27 |
langAlias: {
|
28 |
+
// Map MDX fences to TSX for better JSX tokenization
|
29 |
+
mdx: 'tsx'
|
30 |
}
|
31 |
},
|
32 |
remarkPlugins: [
|
app/package-lock.json
CHANGED
Binary files a/app/package-lock.json and b/app/package-lock.json differ
|
|
app/package.json
CHANGED
Binary files a/app/package.json and b/app/package.json differ
|
|
app/scripts/export-pdf.mjs
CHANGED
@@ -120,6 +120,29 @@ async function waitForPlotly(page, timeoutMs = 20000) {
|
|
120 |
}, timeoutMs);
|
121 |
}
|
122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
async function waitForStableLayout(page, timeoutMs = 5000) {
|
124 |
const start = Date.now();
|
125 |
let last = await page.evaluate(() => document.scrollingElement ? document.scrollingElement.scrollHeight : document.body.scrollHeight);
|
@@ -195,6 +218,9 @@ async function main() {
|
|
195 |
if (wait === 'images' || wait === 'full') {
|
196 |
await waitForImages(page);
|
197 |
}
|
|
|
|
|
|
|
198 |
if (wait === 'plotly' || wait === 'full') {
|
199 |
await waitForPlotly(page);
|
200 |
}
|
@@ -202,13 +228,65 @@ async function main() {
|
|
202 |
await waitForStableLayout(page);
|
203 |
}
|
204 |
await page.emulateMedia({ media: 'print' });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
const outPath = resolve(cwd, 'dist', `${outFileBase}.pdf`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
206 |
await page.pdf({
|
207 |
path: outPath,
|
208 |
format,
|
209 |
printBackground: true,
|
210 |
margin
|
211 |
});
|
|
|
212 |
console.log(`✅ PDF generated: ${outPath}`);
|
213 |
|
214 |
// Copy into public only under the slugified name
|
|
|
120 |
}, timeoutMs);
|
121 |
}
|
122 |
|
123 |
+
async function waitForD3(page, timeoutMs = 20000) {
|
124 |
+
await page.evaluate(async (timeout) => {
|
125 |
+
const start = Date.now();
|
126 |
+
const isReady = () => {
|
127 |
+
// Prioritize hero banner if present
|
128 |
+
const hero = document.querySelector('.hero .d3-galaxy') || document.querySelector('.d3-galaxy');
|
129 |
+
if (hero) {
|
130 |
+
return !!hero.querySelector('svg circle, svg path, svg rect, svg g');
|
131 |
+
}
|
132 |
+
// Else require all D3 containers on page to have shapes
|
133 |
+
const containers = [
|
134 |
+
...Array.from(document.querySelectorAll('.d3-line')),
|
135 |
+
...Array.from(document.querySelectorAll('.d3-bar'))
|
136 |
+
];
|
137 |
+
if (!containers.length) return true;
|
138 |
+
return containers.every(c => c.querySelector('svg circle, svg path, svg rect, svg g'));
|
139 |
+
};
|
140 |
+
while (!isReady() && (Date.now() - start) < timeout) {
|
141 |
+
await new Promise(r => setTimeout(r, 200));
|
142 |
+
}
|
143 |
+
}, timeoutMs);
|
144 |
+
}
|
145 |
+
|
146 |
async function waitForStableLayout(page, timeoutMs = 5000) {
|
147 |
const start = Date.now();
|
148 |
let last = await page.evaluate(() => document.scrollingElement ? document.scrollingElement.scrollHeight : document.body.scrollHeight);
|
|
|
218 |
if (wait === 'images' || wait === 'full') {
|
219 |
await waitForImages(page);
|
220 |
}
|
221 |
+
if (wait === 'd3' || wait === 'full') {
|
222 |
+
await waitForD3(page);
|
223 |
+
}
|
224 |
if (wait === 'plotly' || wait === 'full') {
|
225 |
await waitForPlotly(page);
|
226 |
}
|
|
|
228 |
await waitForStableLayout(page);
|
229 |
}
|
230 |
await page.emulateMedia({ media: 'print' });
|
231 |
+
|
232 |
+
// Generate OG thumbnail (1200x630)
|
233 |
+
try {
|
234 |
+
const ogW = 1200, ogH = 630;
|
235 |
+
await page.setViewportSize({ width: ogW, height: ogH });
|
236 |
+
// Give layout a tick to adjust
|
237 |
+
await page.waitForTimeout(200);
|
238 |
+
// Ensure layout & D3 re-rendered after viewport change
|
239 |
+
await page.evaluate(() => { window.scrollTo(0, 0); window.dispatchEvent(new Event('resize')); });
|
240 |
+
try { await waitForD3(page, 8000); } catch {}
|
241 |
+
|
242 |
+
// Temporarily improve visibility for light theme thumbnails
|
243 |
+
// - Force normal blend for points
|
244 |
+
// - Ensure an SVG background (CSS background on svg element)
|
245 |
+
const cssHandle = await page.addStyleTag({ content: `
|
246 |
+
.hero .points { mix-blend-mode: normal !important; }
|
247 |
+
.d3-galaxy svg { background: var(--surface-bg); }
|
248 |
+
` });
|
249 |
+
const thumbPath = resolve(cwd, 'dist', 'thumb.jpg');
|
250 |
+
await page.screenshot({ path: thumbPath, type: 'jpeg', quality: 85, fullPage: false });
|
251 |
+
// Also emit PNG for compatibility if needed
|
252 |
+
const thumbPngPath = resolve(cwd, 'dist', 'thumb.png');
|
253 |
+
await page.screenshot({ path: thumbPngPath, type: 'png', fullPage: false });
|
254 |
+
const publicThumb = resolve(cwd, 'public', 'thumb.jpg');
|
255 |
+
const publicThumbPng = resolve(cwd, 'public', 'thumb.png');
|
256 |
+
try { await fs.copyFile(thumbPath, publicThumb); } catch {}
|
257 |
+
try { await fs.copyFile(thumbPngPath, publicThumbPng); } catch {}
|
258 |
+
// Remove temporary style so PDF is unaffected
|
259 |
+
try { await cssHandle.evaluate((el) => el.remove()); } catch {}
|
260 |
+
console.log(`✅ OG thumbnail generated: ${thumbPath}`);
|
261 |
+
} catch (e) {
|
262 |
+
console.warn('Unable to generate OG thumbnail:', e?.message || e);
|
263 |
+
}
|
264 |
const outPath = resolve(cwd, 'dist', `${outFileBase}.pdf`);
|
265 |
+
// Restore viewport to printable width before PDF (thumbnail changed it)
|
266 |
+
try {
|
267 |
+
const fmt2 = getFormatSizeMm(format);
|
268 |
+
const mw2 = fmt2.w - cssLengthToMm(margin.left) - cssLengthToMm(margin.right);
|
269 |
+
const printableWidthPx2 = Math.max(320, Math.round((mw2 / 25.4) * 96));
|
270 |
+
await page.setViewportSize({ width: printableWidthPx2, height: 1400 });
|
271 |
+
await page.evaluate(() => { window.scrollTo(0, 0); window.dispatchEvent(new Event('resize')); });
|
272 |
+
try { await waitForD3(page, 8000); } catch {}
|
273 |
+
await waitForStableLayout(page);
|
274 |
+
} catch {}
|
275 |
+
// Temporarily make D3 banner reliably visible for PDF
|
276 |
+
let pdfCssHandle = null;
|
277 |
+
try {
|
278 |
+
pdfCssHandle = await page.addStyleTag({ content: `
|
279 |
+
.hero .points { mix-blend-mode: normal !important; }
|
280 |
+
.d3-galaxy svg { background: var(--surface-bg); }
|
281 |
+
` });
|
282 |
+
} catch {}
|
283 |
await page.pdf({
|
284 |
path: outPath,
|
285 |
format,
|
286 |
printBackground: true,
|
287 |
margin
|
288 |
});
|
289 |
+
try { if (pdfCssHandle) await pdfCssHandle.evaluate((el) => el.remove()); } catch {}
|
290 |
console.log(`✅ PDF generated: ${outPath}`);
|
291 |
|
292 |
// Copy into public only under the slugified name
|
app/src/assets/images/{placeholder.jpg → placeholder.png}
RENAMED
File without changes
|
app/src/components/MermaidDemo.astro
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
export interface Props {
|
3 |
+
code?: string;
|
4 |
+
}
|
5 |
+
const { code = `graph TD\n A[Start] --> B{Is it working?}\n B -- Yes --> C[Great!]\n B -- No --> D[Fix it]\n D --> B` } = Astro.props;
|
6 |
+
---
|
7 |
+
|
8 |
+
<pre class="mermaid">{code}</pre>
|
9 |
+
|
10 |
+
|
11 |
+
<style>.mermaid { max-width: 100%; }</style>
|
app/src/components/Meta.astro
CHANGED
@@ -6,6 +6,16 @@ interface Props {
|
|
6 |
published?: string;
|
7 |
}
|
8 |
const { title, authors = [], affiliation, published } = Astro.props as Props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
---
|
10 |
<header class="meta">
|
11 |
<div class="meta-container">
|
@@ -29,7 +39,7 @@ const { title, authors = [], affiliation, published } = Astro.props as Props;
|
|
29 |
)}
|
30 |
<div class="meta-container-cell">
|
31 |
<h3>PDF</h3>
|
32 |
-
<p><button id="download-pdf-btn"
|
33 |
</div>
|
34 |
</div>
|
35 |
</header>
|
@@ -42,8 +52,9 @@ const { title, authors = [], affiliation, published } = Astro.props as Props;
|
|
42 |
if (!btn) return;
|
43 |
btn.addEventListener('click', () => {
|
44 |
const a = document.createElement('a');
|
45 |
-
|
46 |
-
a.
|
|
|
47 |
document.body.appendChild(a);
|
48 |
a.click();
|
49 |
a.remove();
|
|
|
6 |
published?: string;
|
7 |
}
|
8 |
const { title, authors = [], affiliation, published } = Astro.props as Props;
|
9 |
+
function slugify(text: string): string {
|
10 |
+
return String(text || '')
|
11 |
+
.normalize('NFKD')
|
12 |
+
.replace(/\p{Diacritic}+/gu, '')
|
13 |
+
.toLowerCase()
|
14 |
+
.replace(/[^a-z0-9]+/g, '-')
|
15 |
+
.replace(/^-+|-+$/g, '')
|
16 |
+
.slice(0, 120) || 'article';
|
17 |
+
}
|
18 |
+
const pdfFilename = `${slugify(title)}.pdf`;
|
19 |
---
|
20 |
<header class="meta">
|
21 |
<div class="meta-container">
|
|
|
39 |
)}
|
40 |
<div class="meta-container-cell">
|
41 |
<h3>PDF</h3>
|
42 |
+
<p><button id="download-pdf-btn" data-pdf-filename={pdfFilename}>Download PDF</button></p>
|
43 |
</div>
|
44 |
</div>
|
45 |
</header>
|
|
|
52 |
if (!btn) return;
|
53 |
btn.addEventListener('click', () => {
|
54 |
const a = document.createElement('a');
|
55 |
+
const pdf = btn.getAttribute('data-pdf-filename') || 'article.pdf';
|
56 |
+
a.href = `/${pdf}`;
|
57 |
+
a.setAttribute('download', pdf);
|
58 |
document.body.appendChild(a);
|
59 |
a.click();
|
60 |
a.remove();
|
app/src/components/Note.astro
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
const { title = "Note", emoji = "📝", class: className, ...props } = Astro.props;
|
3 |
+
const wrapperClass = ["note", className].filter(Boolean).join(" ");
|
4 |
+
---
|
5 |
+
<div class={wrapperClass} {...props}>
|
6 |
+
<div class="note__header">
|
7 |
+
<span class="note__emoji">{emoji}</span>
|
8 |
+
{title && <span class="note__title">{title}</span>}
|
9 |
+
</div>
|
10 |
+
<div class="note__content">
|
11 |
+
<slot />
|
12 |
+
</div>
|
13 |
+
</div>
|
14 |
+
|
15 |
+
<style>
|
16 |
+
.note { background: var(--surface-bg); border-left: 2px solid rgba(0, 0, 0, 0.1); border-radius: 4px; padding: 10px 14px; margin: 12px 0; }
|
17 |
+
.note__header { display: flex; align-items: center; gap: 6px; font-weight: 600; color: var(--text-color); margin-bottom: 6px; }
|
18 |
+
.note__emoji { font-size: 24px; line-height: 1; }
|
19 |
+
.note__title { font-size: 13px; letter-spacing: .2px; }
|
20 |
+
.note__content { color: var(--text-color); font-size: 0.95rem; }
|
21 |
+
</style>
|
22 |
+
|
23 |
+
|
app/src/content/article.mdx
CHANGED
@@ -12,17 +12,19 @@ published: "Feb 19, 2025"
|
|
12 |
tags:
|
13 |
- research
|
14 |
- template
|
15 |
-
ogImage: "
|
16 |
---
|
17 |
|
18 |
import HtmlFragment from "../components/HtmlFragment.astro";
|
19 |
import Wide from "../components/Wide.astro";
|
20 |
import FullBleed from "../components/FullBleed.astro";
|
21 |
import { Image } from 'astro:assets';
|
22 |
-
import placeholder from "../assets/images/placeholder.
|
23 |
import audioDemo from "../assets/audio/audio-example.wav";
|
24 |
import Aside from "../components/Aside.astro";
|
25 |
import visualPoster from "../assets/images/visual-vocabulary-poster.png";
|
|
|
|
|
26 |
|
27 |
<Aside>
|
28 |
Welcome to this single-page research article template built with **Markdown**.
|
@@ -34,15 +36,13 @@ import visualPoster from "../assets/images/visual-vocabulary-poster.png";
|
|
34 |
In this guide, you’ll learn how to install the template,
|
35 |
write content (math, citations, images, code, asides, interactive fragments),
|
36 |
customize styles and behavior, and follow a few **best practices** for publishing.
|
37 |
-
|
38 |
-
If you have questions or remarks open a discussion on the <a href="https://huggingface.co/spaces/tfrere/science-blog-template/discussions?status=open&type=discussion">Community tab</a>!
|
39 |
-
</Fragment>
|
40 |
</Aside>
|
41 |
|
42 |
-
This template is inspired by [**Distill**](https://distill.pub); we aim to preserve the best of it while modernizing the stack. Their work is highly inspiring.
|
43 |
|
44 |
#### Features
|
45 |
|
|
|
46 |
<div className="tag-list">
|
47 |
<span className="tag">Markdown based</span>
|
48 |
<span className="tag">KaTeX math</span>
|
@@ -55,24 +55,64 @@ This template is inspired by [**Distill**](https://distill.pub); we aim to prese
|
|
55 |
<span className="tag">Plotly ready</span>
|
56 |
<span className="tag">D3.js ready</span>
|
57 |
<span className="tag">SEO Friendly</span>
|
|
|
58 |
<span className="tag">Lightweight bundle</span>
|
59 |
<span className="tag">Aside notes</span>
|
60 |
<span className="tag">Mobile friendly</span>
|
61 |
<span className="tag">Optimized images</span>
|
62 |
<span className="tag">Medium like zoomable images</span>
|
63 |
<span className="tag">PDF export</span>
|
|
|
|
|
64 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
## Getting Started
|
67 |
|
68 |
### Installation
|
69 |
|
|
|
70 |
```bash
|
71 |
git lfs install
|
72 |
git lfs pull
|
73 |
cd app
|
74 |
npm install
|
75 |
```
|
|
|
|
|
|
|
|
|
76 |
|
77 |
|
78 |
### Development
|
@@ -89,6 +129,8 @@ npm run build
|
|
89 |
|
90 |
Serving the `dist/` directory on any static host is enough to deliver the site.
|
91 |
|
|
|
|
|
92 |
### Deploy
|
93 |
|
94 |
The easiest way to get online is to clone [this Hugging Face Space](https://huggingface.co/spaces/tfrere/science-blog-template) and push your changes; every push triggers an automatic build and deploy.
|
@@ -98,331 +140,9 @@ The easiest way to get online is to clone [this Hugging Face Space](https://hugg
|
|
98 |
**Track binaries** (e.g., `.png`, `.wav`) with **Git LFS** to keep the repository lean. This project is preconfigured to store such files via **LFS**.
|
99 |
|
100 |
|
101 |
-
|
102 |
-
|
103 |
-
### Introduction
|
104 |
-
|
105 |
-
Your article lives in two places:
|
106 |
-
|
107 |
-
- `app/src/content/` — where you can find the article.mdx, bibliography.bib and html fragments.
|
108 |
-
- `app/src/assets/` — images, audio, and other static assets. (handled by git lfs)
|
109 |
-
|
110 |
-
This is MDX, its basically a markdown file with html and astro components.
|
111 |
-
The **initial skeleton** of an article looks like this.
|
112 |
-
|
113 |
-
```mdx
|
114 |
-
{/* HEADER */}
|
115 |
-
---
|
116 |
-
title: "This is the main title"
|
117 |
-
subtitle: "This will be displayed just below the banner"
|
118 |
-
description: "A modern, MDX-first research article template with math, citations, and interactive figures."
|
119 |
-
authors:
|
120 |
-
- "John Doe"
|
121 |
-
- "Alice Martin"
|
122 |
-
- "Robert Brown"
|
123 |
-
affiliation: "Hugging Face"
|
124 |
-
published: "Feb 19, 2025"
|
125 |
-
tags:
|
126 |
-
- research
|
127 |
-
- template
|
128 |
-
ogImage: "https://example.com/your-og-image.png"
|
129 |
-
---
|
130 |
-
|
131 |
-
{/* IMPORTS */}
|
132 |
-
import { Image } from 'astro:assets';
|
133 |
-
import placeholder from '../assets/images/placeholder.jpg';
|
134 |
-
|
135 |
-
{/* CONTENT */}
|
136 |
-
# Hello, world
|
137 |
-
|
138 |
-
This is a short paragraph written in Markdown. Below is an example image:
|
139 |
-
|
140 |
-
<Image src={placeholder} alt="Example image" />
|
141 |
-
```
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
**Available blocks**:
|
146 |
-
|
147 |
-
<div className="tag-list">
|
148 |
-
<a className="tag" href="#math">Math</a>
|
149 |
-
<a className="tag" href="#images">Images</a>
|
150 |
-
<a className="tag" href="#code-blocks">Code</a>
|
151 |
-
<a className="tag" href="#citations-and-notes">Citations & notes</a>
|
152 |
-
<a className="tag" href="#asides">Asides</a>
|
153 |
-
<a className="tag" href="#minimal-table">Array</a>
|
154 |
-
<a className="tag" href="#audio">Audio</a>
|
155 |
-
<a className="tag" href="#interactive-fragments">Interactive fragments</a>
|
156 |
-
<a className="tag" href="#tracking-training-metrics-with-trackio">TrackIO</a>
|
157 |
-
</div>
|
158 |
-
|
159 |
-
### Math
|
160 |
-
|
161 |
-
**Inline**
|
162 |
-
|
163 |
-
$x^2 + y^2 = z^2$.
|
164 |
-
|
165 |
-
```mdx
|
166 |
-
$x^2 + y^2 = z^2$
|
167 |
-
```
|
168 |
-
|
169 |
-
**Block**
|
170 |
-
|
171 |
-
$$
|
172 |
-
\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right) V
|
173 |
-
$$
|
174 |
-
|
175 |
-
```mdx
|
176 |
-
$$
|
177 |
-
\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right) V
|
178 |
-
$$
|
179 |
-
```
|
180 |
-
|
181 |
-
### Images
|
182 |
-
|
183 |
-
**Responsive images** automatically generate an optimized `srcset` and `sizes` so the browser downloads the most appropriate file for the current viewport and DPR. You can also request multiple output formats (e.g., **AVIF**, **WebP**, fallback **PNG/JPEG**) and control **lazy loading/decoding** for better **performance**.
|
184 |
-
|
185 |
-
**Optional:** Zoomable (Medium-like lightbox): add `data-zoomable` to opt-in. Only images with this attribute will open full-screen on click.
|
186 |
-
|
187 |
-
**Optional:** Lazy loading: add `loading="lazy"` to opt-in.
|
188 |
-
|
189 |
-
**Optional:** Figcaption and credits: add a `figcaption` element with a `span` containing the credit.
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
<figure>
|
194 |
-
<Image
|
195 |
-
src={placeholder}
|
196 |
-
data-zoomable
|
197 |
-
alt="Example with caption and credit"
|
198 |
-
/>
|
199 |
-
<figcaption>
|
200 |
-
Optimized image with a descriptive caption.
|
201 |
-
<span className="image-credit">Credit: Photo by <a href="https://example.com">Author</a></span>
|
202 |
-
</figcaption>
|
203 |
-
</figure>
|
204 |
-
|
205 |
-
```mdx
|
206 |
-
import { Image } from 'astro:assets'
|
207 |
-
import myImage from '../assets/images/placeholder.jpg'
|
208 |
-
|
209 |
-
<Image src={myImage} alt="Responsive, optimized example image" />
|
210 |
-
|
211 |
-
<figure>
|
212 |
-
<Image src={myImage} data-zoomable alt="Example with caption and credit" loading="lazy" />
|
213 |
-
<figcaption>
|
214 |
-
Optimized image with a descriptive caption.
|
215 |
-
<span className="image-credit">Credit: Photo by <a href="https://example.com">Author</a></span>
|
216 |
-
</figcaption>
|
217 |
-
</figure>
|
218 |
-
```
|
219 |
-
|
220 |
-
|
221 |
-
### Code blocks
|
222 |
-
|
223 |
-
Use fenced code blocks with a language for syntax highlighting.
|
224 |
-
|
225 |
-
Python block example:
|
226 |
-
|
227 |
-
```python
|
228 |
-
def greet(name: str) -> None:
|
229 |
-
print(f"Hello, {name}!")
|
230 |
-
|
231 |
-
greet("Astro")
|
232 |
-
```
|
233 |
-
|
234 |
-
How to write it in MDX:
|
235 |
-
|
236 |
-
````mdx
|
237 |
-
```python
|
238 |
-
def greet(name: str) -> None:
|
239 |
-
print(f"Hello, {name}!")
|
240 |
-
|
241 |
-
greet("Astro")
|
242 |
-
```
|
243 |
-
````
|
244 |
-
|
245 |
-
### Citations and notes
|
246 |
-
|
247 |
-
Here are a few variations using the same bibliography:
|
248 |
-
|
249 |
-
1) **In-text citation** with brackets: [@example2023].
|
250 |
-
|
251 |
-
2) **Narrative citation**: As shown by @vaswani2017attention, transformers enable efficient sequence modeling.
|
252 |
-
|
253 |
-
3) **Multiple citations** and a **footnote** together: see [@vaswani2017attention; @example2023] for related work. Also note this footnote[^f1].
|
254 |
-
|
255 |
-
[^f1]: Footnote attached to the sentence above.
|
256 |
-
|
257 |
-
```mdx
|
258 |
-
1) In-text citation with brackets: [@example2023].
|
259 |
-
|
260 |
-
2) Narrative citation: As shown by @vaswani2017attention, transformers enable efficient sequence modeling.
|
261 |
-
|
262 |
-
3) Multiple citations and a footnote together: see [@vaswani2017attention; @example2023] for related work. Also note this footnote[^f1].
|
263 |
-
|
264 |
-
[^f1]: Footnote attached to the sentence above.
|
265 |
-
```
|
266 |
-
|
267 |
-
|
268 |
-
### Asides
|
269 |
-
|
270 |
-
<Aside>
|
271 |
-
This paragraph presents a **key idea** concisely.
|
272 |
-
<Fragment slot="aside">
|
273 |
-
**Side note** for brief context or a definition.
|
274 |
-
</Fragment>
|
275 |
-
</Aside>
|
276 |
-
|
277 |
-
```mdx
|
278 |
-
import Aside from '../components/Aside.astro'
|
279 |
-
|
280 |
-
<Aside>
|
281 |
-
Main paragraph with the core idea.
|
282 |
-
<Fragment slot="aside">Short side note.</Fragment>
|
283 |
-
</Aside>
|
284 |
-
```
|
285 |
-
|
286 |
-
### Width helpers
|
287 |
-
|
288 |
-
Use these helpers to expand content beyond the main column when needed. They will always be centered and displayed above every other content.
|
289 |
-
|
290 |
-
#### Wide example
|
291 |
-
|
292 |
-
<Wide>
|
293 |
-
<div className="demo-wide">demo wide</div>
|
294 |
-
</Wide>
|
295 |
-
|
296 |
-
```mdx
|
297 |
-
import Wide from '../components/Wide.astro'
|
298 |
-
|
299 |
-
<Wide>
|
300 |
-
Your content here...
|
301 |
-
</Wide>
|
302 |
-
```
|
303 |
-
|
304 |
-
#### Full-bleed example
|
305 |
-
|
306 |
-
<FullBleed>
|
307 |
-
<div className="demo-full-bleed">demo full-bleed</div>
|
308 |
-
</FullBleed>
|
309 |
-
|
310 |
-
```mdx
|
311 |
-
import FullBleed from '../components/FullBleed.astro'
|
312 |
-
|
313 |
-
<FullBleed>
|
314 |
-
Your content here...
|
315 |
-
</FullBleed>
|
316 |
-
```
|
317 |
-
|
318 |
-
|
319 |
-
### Minimal table
|
320 |
-
|
321 |
-
| Method | Score |
|
322 |
-
|---|---|
|
323 |
-
| A | 0.78 |
|
324 |
-
| B | 0.86 |
|
325 |
-
|
326 |
-
```mdx
|
327 |
-
| Method | Score |
|
328 |
-
| --- | --- |
|
329 |
-
| A | 0.78 |
|
330 |
-
| B | 0.86 |
|
331 |
-
```
|
332 |
-
|
333 |
-
### Audio
|
334 |
-
|
335 |
-
<audio controls src={audioDemo}>
|
336 |
-
Your browser does not support the audio element.
|
337 |
-
</audio>
|
338 |
-
|
339 |
-
```mdx
|
340 |
-
import audioDemo from '../assets/audio/audio-example.wav'
|
341 |
-
|
342 |
-
<audio controls src={audioDemo}>
|
343 |
-
Your browser does not support the audio element.
|
344 |
-
</audio>
|
345 |
-
```
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
### Embeds
|
350 |
-
|
351 |
-
|
352 |
-
#### Html Fragments
|
353 |
-
|
354 |
-
The main purpose of the ```HtmlFragment``` component is to embed a **Plotly** or **D3.js** chart in your article. Libraries are already imported in the template.
|
355 |
-
|
356 |
-
<div className="plot-card">
|
357 |
-
<HtmlFragment src="line.html" />
|
358 |
-
</div>
|
359 |
-
|
360 |
-
```mdx
|
361 |
-
import HtmlFragment from '../components/HtmlFragment.astro'
|
362 |
-
|
363 |
-
<HtmlFragment src="line.html" />
|
364 |
-
```
|
365 |
-
|
366 |
-
#### Iframes
|
367 |
-
|
368 |
-
You can embed external content in your article using **iframes**. For example, **TrackIO**—a lightweight dashboard to monitor machine learning experiments—can be used this way. The example below opens a demo project and displays a couple of metrics. You can customize the **query parameters** (`project`, `metrics`, `sidebar`, etc.) to fit your needs.
|
369 |
-
|
370 |
-
<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="600" frameborder="0"></iframe>
|
371 |
-
|
372 |
-
To embed TrackIO in your own page, copy the following HTML and adjust the `src` parameters:
|
373 |
-
|
374 |
-
#### GitHub code embeds
|
375 |
-
|
376 |
-
Finally, if you want to include code from GitHub you can use emgithub.com and, for example, create a collapsible widget like this:
|
377 |
-
|
378 |
-
|
379 |
-
<div class="code-embed-container"></div>
|
380 |
-
<script
|
381 |
-
src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F0035cce0e04afd6192763b11efe50010d8ad0f71%2Fpicotron%2Fdata_parallel%2Fdata_parallel.py%23L10-L60&style=github&type=code&showBorder=off&showLineNumbers=on&showFileMeta=on&showCopy=on&showFullPath=on">
|
382 |
-
</script>
|
383 |
-
|
384 |
-
```html
|
385 |
-
<div class="code-embed-container"></div>
|
386 |
-
<script
|
387 |
-
src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F0035cce0e04afd6192763b11efe50010d8ad0f71%2Fpicotron%2Fdata_parallel%2Fdata_parallel.py%23L10-L60&style=github&type=code&showBorder=off&showLineNumbers=on&showFileMeta=on&showCopy=on&showFullPath=on">
|
388 |
-
</script>
|
389 |
-
```
|
390 |
-
|
391 |
-
|
392 |
-
## Best Practices
|
393 |
-
|
394 |
-
### Short sections
|
395 |
-
Break content into **small, purpose‑driven sections**. Each section should answer a **single question** or support one idea. This improves **scanability**, helps readers navigate with the TOC, and makes later edits safer.
|
396 |
-
|
397 |
-
### Clear, minimal annotations
|
398 |
-
Favor **concise captions** and callouts that clarify what to look at and why it matters. In code, **highlight just the lines** that carry the idea; avoid verbose commentary. **Precision beats volume**.
|
399 |
-
|
400 |
-
### Explain math notation
|
401 |
-
**Introduce symbols and variables** the first time they appear, and prefer **well‑known identities** over custom shorthand. When formulas carry the message, add one sentence of **plain‑language interpretation** right after.
|
402 |
-
|
403 |
-
### Use the right color scale
|
404 |
-
|
405 |
-
A **palette** encodes **meaning** (categories, magnitudes, oppositions), preserves **readability** and **accessibility** (**sufficient contrast**, **color‑vision safety**), and ensures **perceptually smooth transitions**.
|
406 |
-
|
407 |
-
The three families below illustrate when to use **categorical**, **sequential**, or **diverging** colors and how they evolve from the same **reference hue**.
|
408 |
-
|
409 |
-
<div className="">
|
410 |
-
<HtmlFragment src="palettes.html" />
|
411 |
-
</div>
|
412 |
-
|
413 |
-
### Use the right chart
|
414 |
-
|
415 |
-
Picking the right visualization depends on your goal (compare values, show distribution, part-to-whole, trends, relationships, etc.). The Visual Vocabulary poster below provides a concise mapping from **analytical task** to **chart types**.
|
416 |
|
417 |
-
<
|
418 |
-
<a href={visualPoster.src} target="_blank" rel="noopener noreferrer">
|
419 |
-
<Image src={visualPoster} alt="Visual Vocabulary: choosing the right chart by task" />
|
420 |
-
</a>
|
421 |
-
<figcaption>
|
422 |
-
A handy reference to select chart types by purpose. Click to enlarge.
|
423 |
-
— <a href="https://ft-interactive.github.io/visual-vocabulary/" target="_blank" rel="noopener noreferrer">Website</a>
|
424 |
-
</figcaption>
|
425 |
-
</figure>
|
426 |
|
427 |
|
428 |
## Conclusions
|
|
|
12 |
tags:
|
13 |
- research
|
14 |
- template
|
15 |
+
ogImage: "/thumb.jpg"
|
16 |
---
|
17 |
|
18 |
import HtmlFragment from "../components/HtmlFragment.astro";
|
19 |
import Wide from "../components/Wide.astro";
|
20 |
import FullBleed from "../components/FullBleed.astro";
|
21 |
import { Image } from 'astro:assets';
|
22 |
+
import placeholder from "../assets/images/placeholder.png";
|
23 |
import audioDemo from "../assets/audio/audio-example.wav";
|
24 |
import Aside from "../components/Aside.astro";
|
25 |
import visualPoster from "../assets/images/visual-vocabulary-poster.png";
|
26 |
+
import BestPractices from "./chapters/best-pratices.mdx";
|
27 |
+
import WritingYourContent from "./chapters/writing-you-content.mdx";
|
28 |
|
29 |
<Aside>
|
30 |
Welcome to this single-page research article template built with **Markdown**.
|
|
|
36 |
In this guide, you’ll learn how to install the template,
|
37 |
write content (math, citations, images, code, asides, interactive fragments),
|
38 |
customize styles and behavior, and follow a few **best practices** for publishing.
|
39 |
+
|
|
|
|
|
40 |
</Aside>
|
41 |
|
|
|
42 |
|
43 |
#### Features
|
44 |
|
45 |
+
<Aside>
|
46 |
<div className="tag-list">
|
47 |
<span className="tag">Markdown based</span>
|
48 |
<span className="tag">KaTeX math</span>
|
|
|
55 |
<span className="tag">Plotly ready</span>
|
56 |
<span className="tag">D3.js ready</span>
|
57 |
<span className="tag">SEO Friendly</span>
|
58 |
+
<span className="tag">Mermaid diagrams</span>
|
59 |
<span className="tag">Lightweight bundle</span>
|
60 |
<span className="tag">Aside notes</span>
|
61 |
<span className="tag">Mobile friendly</span>
|
62 |
<span className="tag">Optimized images</span>
|
63 |
<span className="tag">Medium like zoomable images</span>
|
64 |
<span className="tag">PDF export</span>
|
65 |
+
<span className="tag">To do: Accordion</span>
|
66 |
+
<span className="tag">To do: dataviz color palette generator</span>
|
67 |
</div>
|
68 |
+
<Fragment slot="aside">
|
69 |
+
If you have questions or remarks open a discussion on the <a href="https://huggingface.co/spaces/tfrere/research-blog-template/discussions?status=open&type=discussion">Community tab</a>!
|
70 |
+
</Fragment>
|
71 |
+
</Aside>
|
72 |
+
|
73 |
+
## Introduction
|
74 |
+
A modern medium for presenting research
|
75 |
+
The web enables forms of explanation that static PDFs cannot: **reactive diagrams**, progressive notation, and **exploratory views** that reveal how ideas behave. Use **interactive fragments** to let readers hover, scrub, and inspect—building **intuition**, not just reading results.
|
76 |
+
|
77 |
+
New ways of thinking enable new discoveries
|
78 |
+
Careful notation, **well‑chosen visual encodings**, and **small interactive experiments** deepen understanding. By making these artifacts **first‑class**—alongside text, math, and code—this template helps your audience grasp mechanisms, limits, and trade‑offs.
|
79 |
+
|
80 |
+
Machine learning needs more transparency
|
81 |
+
Clear, **inspectable examples** make methods safer and more comprehensible. Embed live widgets, reveal **intermediate states**, and link to sources so readers can verify claims and **reproduce results**.
|
82 |
+
|
83 |
+
Legitimacy for non‑traditional research artifacts
|
84 |
+
Not every contribution fits a PDF. Treat demos, visualizations, and interactive write‑ups as **real scholarship**: cite them, version them, and ship them together.
|
85 |
+
|
86 |
+
This project is heavely inspired by [**Distill**](https://distill.pub) (2016–2021), which championed clear, web‑native scholarship.
|
87 |
+
|
88 |
+
|
89 |
+
### Notable examples of excellent scientific articles
|
90 |
+
|
91 |
+
A short, curated list of well‑designed and often interactive work:
|
92 |
+
|
93 |
+
- **Distill — The Building Blocks of Interpretability**: [distill.pub/2018/building-blocks](https://distill.pub/2018/building-blocks/)
|
94 |
+
- **R2D3 — A Visual Introduction to Machine Learning (Part 1)**: [r2d3.us/visual-intro-to-machine-learning-part-1](http://www.r2d3.us/visual-intro-to-machine-learning-part-1/)
|
95 |
+
- **Seeing Theory — An interactive introduction to probability and statistics**: [seeing-theory.brown.edu](https://seeing-theory.brown.edu/)
|
96 |
+
- **ConvNetJS — Neural networks in the browser**: [cs.stanford.edu/people/karpathy/convnetjs](http://cs.stanford.edu/people/karpathy/convnetjs/)
|
97 |
+
- **Explorable Explanations — Collection**: [explorableexplanations.com](http://explorableexplanations.com/)
|
98 |
+
- **Distill — Why Momentum Really Works**: [distill.pub/2017/momentum](https://distill.pub/2017/momentum/)
|
99 |
+
|
100 |
|
101 |
## Getting Started
|
102 |
|
103 |
### Installation
|
104 |
|
105 |
+
<Aside>
|
106 |
```bash
|
107 |
git lfs install
|
108 |
git lfs pull
|
109 |
cd app
|
110 |
npm install
|
111 |
```
|
112 |
+
<Fragment slot="aside">
|
113 |
+
You can use yarn alternatively to npm.
|
114 |
+
</Fragment>
|
115 |
+
</Aside>
|
116 |
|
117 |
|
118 |
### Development
|
|
|
129 |
|
130 |
Serving the `dist/` directory on any static host is enough to deliver the site.
|
131 |
|
132 |
+
A [slug-title].pdf and thumb.jpg are also generated at build time. You can find them in the public folder.
|
133 |
+
|
134 |
### Deploy
|
135 |
|
136 |
The easiest way to get online is to clone [this Hugging Face Space](https://huggingface.co/spaces/tfrere/science-blog-template) and push your changes; every push triggers an automatic build and deploy.
|
|
|
140 |
**Track binaries** (e.g., `.png`, `.wav`) with **Git LFS** to keep the repository lean. This project is preconfigured to store such files via **LFS**.
|
141 |
|
142 |
|
143 |
+
<WritingYourContent />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
|
145 |
+
<BestPractices />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
|
147 |
|
148 |
## Conclusions
|
app/src/content/chapters/best-pratices.mdx
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import { Image } from 'astro:assets';
|
3 |
+
import visualPoster from '../../assets/images/visual-vocabulary-poster.png';
|
4 |
+
|
5 |
+
|
6 |
+
## Best Practices
|
7 |
+
|
8 |
+
### Short sections
|
9 |
+
Break content into **small, purpose‑driven sections**. Each section should answer a **single question** or support one idea. This improves **scanability**, helps readers navigate with the TOC, and makes later edits safer.
|
10 |
+
|
11 |
+
### Clear, minimal annotations
|
12 |
+
Favor **concise captions** and callouts that clarify what to look at and why it matters. In code, **highlight just the lines** that carry the idea; avoid verbose commentary. **Precision beats volume**.
|
13 |
+
|
14 |
+
### Explain math notation
|
15 |
+
**Introduce symbols and variables** the first time they appear, and prefer **well‑known identities** over custom shorthand. When formulas carry the message, add one sentence of **plain‑language interpretation** right after.
|
16 |
+
|
17 |
+
|
18 |
+
{/* ### Use the right color
|
19 |
+
A palette encodes **meaning** (categories, magnitudes, oppositions), preserves **readability** and **accessibility** (sufficient contrast, color‑vision safety), and ensures **perceptually smooth transitions**. The three families below illustrate when to use **categorical**, **sequential**, or **diverging** colors and how they evolve from the same **reference hue**.
|
20 |
+
|
21 |
+
<Aside>
|
22 |
+
<div className="">
|
23 |
+
<HtmlFragment src="palettes.html" />
|
24 |
+
</div>
|
25 |
+
<Fragment slot="aside">
|
26 |
+
You can choose a color from the palette to update palettes and copy them to your clipboard.
|
27 |
+
</Fragment>
|
28 |
+
<Fragment slot="aside">
|
29 |
+
It will be applied to the <a href="/" target="_blank">whole page</a>.
|
30 |
+
</Fragment>
|
31 |
+
</Aside> */}
|
32 |
+
|
33 |
+
### Use the right chart
|
34 |
+
|
35 |
+
Picking the right visualization depends on your goal (compare values, show distribution, part-to-whole, trends, relationships, etc.). The Visual Vocabulary poster below provides a concise mapping from **analytical task** to **chart types**.
|
36 |
+
|
37 |
+
<figure>
|
38 |
+
<a href={visualPoster.src} target="_blank" rel="noopener noreferrer">
|
39 |
+
<Image src={visualPoster} alt="Visual Vocabulary: choosing the right chart by task" />
|
40 |
+
</a>
|
41 |
+
<figcaption>
|
42 |
+
A handy reference to select chart types by purpose. Click to enlarge.
|
43 |
+
— <a href="https://ft-interactive.github.io/visual-vocabulary/" target="_blank" rel="noopener noreferrer">Website</a>
|
44 |
+
</figcaption>
|
45 |
+
</figure>
|
46 |
+
|
app/src/content/chapters/writing-you-content.mdx
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{/* IMPORTS */}
|
2 |
+
import { Image } from 'astro:assets';
|
3 |
+
import placeholder from '../../assets/images/placeholder.png';
|
4 |
+
import Aside from '../../components/Aside.astro';
|
5 |
+
import Wide from '../../components/Wide.astro';
|
6 |
+
import FullBleed from '../../components/FullBleed.astro';
|
7 |
+
import HtmlFragment from '../../components/HtmlFragment.astro';
|
8 |
+
import audioDemo from '../../assets/audio/audio-example.wav';
|
9 |
+
|
10 |
+
## Writing Your Content
|
11 |
+
|
12 |
+
### Introduction
|
13 |
+
|
14 |
+
Your article lives in two places:
|
15 |
+
|
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 |
+
This is MDX, its basically a markdown file with html and astro components.
|
20 |
+
The **initial skeleton** of an article looks like this.
|
21 |
+
|
22 |
+
```mdx
|
23 |
+
{/* HEADER */}
|
24 |
+
---
|
25 |
+
title: "This is the main title"
|
26 |
+
subtitle: "This will be displayed just below the banner"
|
27 |
+
description: "A modern, MDX-first research article template with math, citations, and interactive figures."
|
28 |
+
authors:
|
29 |
+
- "John Doe"
|
30 |
+
- "Alice Martin"
|
31 |
+
- "Robert Brown"
|
32 |
+
affiliation: "Hugging Face"
|
33 |
+
published: "Feb 19, 2025"
|
34 |
+
tags:
|
35 |
+
- research
|
36 |
+
- template
|
37 |
+
ogImage: "https://example.com/your-og-image.png"
|
38 |
+
---
|
39 |
+
|
40 |
+
{/* IMPORTS */}
|
41 |
+
import { Image } from 'astro:assets';
|
42 |
+
import placeholder from '../assets/images/placeholder.jpg';
|
43 |
+
|
44 |
+
{/* CONTENT */}
|
45 |
+
# Hello, world
|
46 |
+
|
47 |
+
This is a short paragraph written in Markdown. Below is an example image:
|
48 |
+
|
49 |
+
<Image src={placeholder} alt="Example image" />
|
50 |
+
```
|
51 |
+
|
52 |
+
### Chapters
|
53 |
+
|
54 |
+
|
55 |
+
**If** your article becomes **too long**, you can **organize** it into **separate chapters**.
|
56 |
+
|
57 |
+
Simply **create a new file** in the `app/src/content/chapters` **directory**.
|
58 |
+
Then, **include** your new chapter in the main article by adding the following lines:
|
59 |
+
|
60 |
+
```mdx
|
61 |
+
import MyChapter from './chapters/my-chapter.mdx';
|
62 |
+
<MyChapter />
|
63 |
+
```
|
64 |
+
|
65 |
+
You can see an example of this in the <a href="">`app/src/content/chapters/best-pratices.mdx`</a> file.
|
66 |
+
|
67 |
+
### Theme
|
68 |
+
|
69 |
+
All **interactive elements** (buttons, inputs, cards, etc.) are themed with the **primary color** you choose. Feel free to update this color to match your **brand**.
|
70 |
+
|
71 |
+
You can **override** the theme by changing the main variable in the `app/src/styles/_variables.css` file.
|
72 |
+
|
73 |
+
You can use the **color picker** below to choose the right color.
|
74 |
+
<Aside>
|
75 |
+
<div className="">
|
76 |
+
<HtmlFragment src="color-picker.html" />
|
77 |
+
</div>
|
78 |
+
<Fragment slot="aside">
|
79 |
+
There is also a color <a href="#use-the-right-color">palette generator</a> that will help you choose the right color for your data visualizations.
|
80 |
+
</Fragment>
|
81 |
+
</Aside>
|
82 |
+
|
83 |
+
|
84 |
+
### Available blocks
|
85 |
+
|
86 |
+
All the following blocks are available in the article.mdx file. You can also create your own blocks by creating a new component in the components folder.
|
87 |
+
|
88 |
+
<br/>
|
89 |
+
<div className="button-group">
|
90 |
+
<a className="button" href="#math">Math</a>
|
91 |
+
<a className="button" href="#images">Images</a>
|
92 |
+
<a className="button" href="#code-blocks">Code</a>
|
93 |
+
<a className="button" href="#citations-and-notes">Citations & notes</a>
|
94 |
+
<a className="button" href="#asides">Asides</a>
|
95 |
+
<a className="button" href="#minimal-table">Table</a>
|
96 |
+
<a className="button" href="#audio">Audio</a>
|
97 |
+
<a className="button" href="#embeds">Embeds</a>
|
98 |
+
</div>
|
99 |
+
|
100 |
+
### Math
|
101 |
+
|
102 |
+
KaTeX is used for math rendering.
|
103 |
+
|
104 |
+
**Inline**
|
105 |
+
|
106 |
+
This is an inline math equation: $x^2 + y^2 = z^2$.
|
107 |
+
|
108 |
+
|
109 |
+
<small className="muted">Example</small>
|
110 |
+
```mdx
|
111 |
+
$x^2 + y^2 = z^2$
|
112 |
+
```
|
113 |
+
|
114 |
+
**Block**
|
115 |
+
|
116 |
+
$$
|
117 |
+
\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right) V
|
118 |
+
$$
|
119 |
+
|
120 |
+
<small className="muted">Example</small>
|
121 |
+
```mdx
|
122 |
+
$$
|
123 |
+
\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right) V
|
124 |
+
$$
|
125 |
+
```
|
126 |
+
|
127 |
+
### Images
|
128 |
+
|
129 |
+
**Responsive images** automatically generate an optimized `srcset` and `sizes` so the browser downloads the most appropriate file for the current viewport and DPR. You can also request multiple output formats (e.g., **AVIF**, **WebP**, fallback **PNG/JPEG**) and control **lazy loading/decoding** for better **performance**.
|
130 |
+
|
131 |
+
**Optional:** Zoomable (Medium-like lightbox): add `data-zoomable` to opt-in. Only images with this attribute will open full-screen on click.
|
132 |
+
|
133 |
+
**Optional:** Lazy loading: add `loading="lazy"` to opt-in.
|
134 |
+
|
135 |
+
**Optional:** Figcaption and credits: add a `figcaption` element with a `span` containing the credit.
|
136 |
+
|
137 |
+
|
138 |
+
|
139 |
+
<figure>
|
140 |
+
<Image
|
141 |
+
src={placeholder}
|
142 |
+
data-zoomable
|
143 |
+
alt="Tensor parallelism in a transformer block"
|
144 |
+
/>
|
145 |
+
<figcaption>
|
146 |
+
Tensor parallelism in a transformer block
|
147 |
+
<span className="image-credit">Original work on <a target="_blank" href="https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=tensor_parallelism_in_a_transformer_block">Ultrascale Playbook</a></span>
|
148 |
+
</figcaption>
|
149 |
+
</figure>
|
150 |
+
|
151 |
+
<small className="muted">Example</small>
|
152 |
+
```mdx
|
153 |
+
import { Image } from 'astro:assets'
|
154 |
+
import myImage from '../assets/images/placeholder.jpg'
|
155 |
+
|
156 |
+
<Image src={myImage} alt="Responsive, optimized example image" />
|
157 |
+
|
158 |
+
<figure>
|
159 |
+
<Image src={myImage} data-zoomable alt="Example with caption and credit" loading="lazy" />
|
160 |
+
<figcaption>
|
161 |
+
Optimized image with a descriptive caption.
|
162 |
+
<span className="image-credit">Credit: Photo by <a href="https://example.com">Author</a></span>
|
163 |
+
</figcaption>
|
164 |
+
</figure>
|
165 |
+
```
|
166 |
+
|
167 |
+
|
168 |
+
### Code blocks
|
169 |
+
|
170 |
+
Use fenced code blocks with a language for syntax highlighting.
|
171 |
+
|
172 |
+
```python
|
173 |
+
def greet(name: str) -> None:
|
174 |
+
print(f"Hello, {name}!")
|
175 |
+
|
176 |
+
greet("Astro")
|
177 |
+
```
|
178 |
+
|
179 |
+
<small className="muted">Example</small>
|
180 |
+
````mdx
|
181 |
+
```python
|
182 |
+
def greet(name: str) -> None:
|
183 |
+
print(f"Hello, {name}!")
|
184 |
+
|
185 |
+
greet("Astro")
|
186 |
+
```
|
187 |
+
````
|
188 |
+
|
189 |
+
### Mermaid diagrams
|
190 |
+
|
191 |
+
Native mermaid diagrams are supported. You can use the <a target="_blank" href="https://mermaid.live/edit#pako:eNpVjUFPg0AQhf_KZk6a0AYsCywHE0u1lyZ66EnoYQMDSyy7ZFlSK_DfXWiMOqd58773ZoBcFQgxlGd1yQXXhhx3mSR2ntJE6LozDe9OZLV6HPdoSKMkXkeyvdsr0gnVtrWs7m_8doZIMhxmDIkRtfyYblay5F8ljmSXHnhrVHv66xwvaiTPaf0mbP1_R2i0qZe05HHJVznXJOF6QcCBStcFxEb36ECDuuGzhGF2MzACG8wgtmuBJe_PJoNMTjbWcvmuVPOT1KqvBNj6c2dV3xbc4K7mlea_CMoCdaJ6aSCm3lIB8QCfED94dM2o77ssjFzK3MiBq2WCNWUeiza-H26YvU8OfC0_3XVII9eLQuYFIaVBGEzfyTJ22g"> live editor</a> to create your diagram and copy the code to your article.
|
192 |
+
|
193 |
+
```mermaid
|
194 |
+
graph TD
|
195 |
+
A[Start] --> B{Is it working?}
|
196 |
+
B -- Yes --> C[Great!]
|
197 |
+
B -- No --> D[Fix it]
|
198 |
+
D --> B
|
199 |
+
```
|
200 |
+
|
201 |
+
<small className="muted">Example</small>
|
202 |
+
````mdx
|
203 |
+
```mermaid
|
204 |
+
graph TD
|
205 |
+
A[Start] --> B{Is it working?}
|
206 |
+
B -- Yes --> C[Great!]
|
207 |
+
B -- No --> D[Fix it]
|
208 |
+
D --> B
|
209 |
+
```
|
210 |
+
````
|
211 |
+
|
212 |
+
### Citations and notes
|
213 |
+
|
214 |
+
Here are a few variations using the same bibliography:
|
215 |
+
|
216 |
+
1) **In-text citation** with brackets: [@example2023].
|
217 |
+
|
218 |
+
2) **Narrative citation**: As shown by @vaswani2017attention, transformers enable efficient sequence modeling.
|
219 |
+
|
220 |
+
3) **Multiple citations** and a **footnote** together: see [@vaswani2017attention; @example2023] for related work. Also note this footnote[^f1].
|
221 |
+
|
222 |
+
[^f1]: Footnote attached to the sentence above.
|
223 |
+
|
224 |
+
<small className="muted">Example</small>
|
225 |
+
```mdx
|
226 |
+
1) In-text citation with brackets: [@example2023].
|
227 |
+
|
228 |
+
2) Narrative citation: As shown by @vaswani2017attention, transformers enable efficient sequence modeling.
|
229 |
+
|
230 |
+
3) Multiple citations and a footnote together: see [@vaswani2017attention; @example2023] for related work. Also note this footnote[^f1].
|
231 |
+
|
232 |
+
[^f1]: Footnote attached to the sentence above.
|
233 |
+
```
|
234 |
+
|
235 |
+
|
236 |
+
### Placement
|
237 |
+
|
238 |
+
#### Asides
|
239 |
+
|
240 |
+
<Aside>
|
241 |
+
This paragraph presents a **key idea** concisely.
|
242 |
+
<Fragment slot="aside">
|
243 |
+
**Side note** for brief context or a definition.
|
244 |
+
</Fragment>
|
245 |
+
</Aside>
|
246 |
+
|
247 |
+
<small className="muted">Example</small>
|
248 |
+
```mdx
|
249 |
+
import Aside from '../components/Aside.astro'
|
250 |
+
|
251 |
+
<Aside>
|
252 |
+
Main paragraph with the core idea.
|
253 |
+
<Fragment slot="aside">Short side note.</Fragment>
|
254 |
+
</Aside>
|
255 |
+
```
|
256 |
+
|
257 |
+
Use these helpers to expand content beyond the main column when needed. They will always be centered and displayed above every other content.
|
258 |
+
|
259 |
+
#### Wide example
|
260 |
+
|
261 |
+
<Wide>
|
262 |
+
<div className="demo-wide">demo wide</div>
|
263 |
+
</Wide>
|
264 |
+
|
265 |
+
<small className="muted">Example</small>
|
266 |
+
```mdx
|
267 |
+
import Wide from '../components/Wide.astro'
|
268 |
+
|
269 |
+
<Wide>
|
270 |
+
Your content here...
|
271 |
+
</Wide>
|
272 |
+
```
|
273 |
+
|
274 |
+
#### Full-bleed example
|
275 |
+
|
276 |
+
<FullBleed>
|
277 |
+
<div className="demo-full-bleed">demo full-bleed</div>
|
278 |
+
</FullBleed>
|
279 |
+
|
280 |
+
<small className="muted">Example</small>
|
281 |
+
```mdx
|
282 |
+
import FullBleed from '../components/FullBleed.astro'
|
283 |
+
|
284 |
+
<FullBleed>
|
285 |
+
Your content here...
|
286 |
+
</FullBleed>
|
287 |
+
```
|
288 |
+
|
289 |
+
|
290 |
+
### Minimal table
|
291 |
+
|
292 |
+
| Method | Score |
|
293 |
+
|---|---|
|
294 |
+
| A | 0.78 |
|
295 |
+
| B | 0.86 |
|
296 |
+
|
297 |
+
<small className="muted">Example</small>
|
298 |
+
```mdx
|
299 |
+
| Method | Score |
|
300 |
+
| --- | --- |
|
301 |
+
| A | 0.78 |
|
302 |
+
| B | 0.86 |
|
303 |
+
```
|
304 |
+
|
305 |
+
### Audio
|
306 |
+
|
307 |
+
<audio controls src={audioDemo}>
|
308 |
+
Your browser does not support the audio element.
|
309 |
+
</audio>
|
310 |
+
|
311 |
+
<small className="muted">Example</small>
|
312 |
+
```mdx
|
313 |
+
import audioDemo from '../assets/audio/audio-example.wav'
|
314 |
+
|
315 |
+
<audio controls src={audioDemo}>
|
316 |
+
Your browser does not support the audio element.
|
317 |
+
</audio>
|
318 |
+
```
|
319 |
+
|
320 |
+
|
321 |
+
|
322 |
+
### Embeds
|
323 |
+
|
324 |
+
|
325 |
+
#### Html Fragments
|
326 |
+
|
327 |
+
The main purpose of the ```HtmlFragment``` component is to **embed** a **Plotly** or **D3.js** chart in your article. **Libraries** are already imported in the template.
|
328 |
+
|
329 |
+
They exist in the `app/src/content/fragments` folder.
|
330 |
+
|
331 |
+
Here are some examples of the two **libraries** in the template:
|
332 |
+
|
333 |
+
D3 version
|
334 |
+
<div className="plot-card">
|
335 |
+
<HtmlFragment src="d3-line.html" />
|
336 |
+
</div>
|
337 |
+
|
338 |
+
<div className="plot-card">
|
339 |
+
<HtmlFragment src="d3-bar.html" />
|
340 |
+
</div>
|
341 |
+
|
342 |
+
|
343 |
+
Plotly version
|
344 |
+
<div className="plot-card">
|
345 |
+
<HtmlFragment src="line.html" />
|
346 |
+
</div>
|
347 |
+
<div className="plot-card">
|
348 |
+
<HtmlFragment src="bar.html" />
|
349 |
+
</div>
|
350 |
+
|
351 |
+
<small className="muted">Example</small>
|
352 |
+
```mdx
|
353 |
+
import HtmlFragment from '../components/HtmlFragment.astro'
|
354 |
+
|
355 |
+
<HtmlFragment src="line.html" />
|
356 |
+
```
|
357 |
+
|
358 |
+
#### Iframes
|
359 |
+
|
360 |
+
You can embed external content in your article using **iframes**. For example, **TrackIO or github code embeds** can be used this way.
|
361 |
+
|
362 |
+
<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>
|
363 |
+
|
364 |
+
<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>
|
365 |
+
|
366 |
+
<small className="muted">Example</small>
|
367 |
+
```mdx
|
368 |
+
<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>
|
369 |
+
<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>
|
370 |
+
```
|
app/src/content/fragments/banner.html
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<div class="d3-galaxy" style="width:100%;margin:10px 0;"></div>
|
2 |
<script>
|
3 |
(() => {
|
4 |
const ensureD3 = (cb) => {
|
@@ -105,15 +105,6 @@
|
|
105 |
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
106 |
const strokeColor = isDark ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.12)';
|
107 |
|
108 |
-
// Background rect using gradient
|
109 |
-
const bg = svg.selectAll('rect.d3-bg').data([0]);
|
110 |
-
bg.join('rect')
|
111 |
-
.attr('class', 'd3-bg')
|
112 |
-
.attr('x', 0)
|
113 |
-
.attr('y', 0)
|
114 |
-
.attr('width', width)
|
115 |
-
.attr('height', height)
|
116 |
-
.attr('fill', 'url(#spaceBg)');
|
117 |
|
118 |
// Group with blend mode so points softly accumulate light
|
119 |
const g = svg.selectAll('g.points').data([0]).join('g').attr('class', 'points').style('mix-blend-mode', 'screen');
|
@@ -152,7 +143,7 @@
|
|
152 |
}
|
153 |
|
154 |
// Final filter: remove small dots very close to the galaxy center (after placement)
|
155 |
-
const centerHoleRadius = 0.
|
156 |
const smallSizeThreshold = 7.5; // same notion as Python size cut
|
157 |
const rTotal = idx.map((i) => Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2));
|
158 |
const idxFiltered = idx.filter((i, k) => !(rTotal[k] <= centerHoleRadius && sizesPx[i] < smallSizeThreshold));
|
|
|
1 |
+
<div class="d3-galaxy" style="width:100%;margin:10px 0;aspect-ratio:3/1;min-height:260px;"></div>
|
2 |
<script>
|
3 |
(() => {
|
4 |
const ensureD3 = (cb) => {
|
|
|
105 |
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
106 |
const strokeColor = isDark ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.12)';
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
// Group with blend mode so points softly accumulate light
|
110 |
const g = svg.selectAll('g.points').data([0]).join('g').attr('class', 'points').style('mix-blend-mode', 'screen');
|
|
|
143 |
}
|
144 |
|
145 |
// Final filter: remove small dots very close to the galaxy center (after placement)
|
146 |
+
const centerHoleRadius = 0.48; // elliptical radius threshold
|
147 |
const smallSizeThreshold = 7.5; // same notion as Python size cut
|
148 |
const rTotal = idx.map((i) => Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2));
|
149 |
const idxFiltered = idx.filter((i, k) => !(rTotal[k] <= centerHoleRadius && sizesPx[i] < smallSizeThreshold));
|
app/src/content/fragments/bar.html
CHANGED
@@ -1,22 +1 @@
|
|
1 |
-
<div> <div id="fd0d6e2f-9418-45a8-acf0-f9bc9952180e" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("fd0d6e2f-9418-45a8-acf0-f9bc9952180e")) { Plotly.newPlot( "fd0d6e2f-9418-45a8-acf0-f9bc9952180e", [{"hovertemplate":"\u003cb\u003e%{x}\u003c\u002fb\u003e\u003cbr\u003e%{fullData.name}: %{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"#64748b"},"name":"Baseline","offsetgroup":"grp","x":["A","B","C","D","E"],"y":[0.52,0.61,0.67,0.73,0.78],"type":"bar"},{"hovertemplate":"\u003cb\u003e%{x}\u003c\u002fb\u003e\u003cbr\u003e%{fullData.name}: %{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"#2563eb"},"name":"Improved","offsetgroup":"grp","x":["A","B","C","D","E"],"y":[0.58,0.66,0.72,0.79,0.86],"type":"bar"},{"hovertemplate":"\u003cb\u003e%{x}\u003c\u002fb\u003e\u003cbr\u003e%{fullData.name}: %{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"#4b5563","line":{"color":"#4b5563","width":1},"opacity":0.65},"name":"Target","offsetgroup":"grp","x":["A","B","C","D","E"],"y":[0.6,0.68,0.75,0.82,0.9],"type":"bar"}], {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"margin":{"l":28,"r":12,"t":8,"b":28},"legend":{"orientation":"h","yanchor":"bottom","y":1.02,"xanchor":"left","x":0},"xaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.65)"},"showgrid":false,"zeroline":false,"showline":true,"linecolor":"rgba(0,0,0,0.25)","linewidth":1,"ticks":"outside","ticklen":6,"tickcolor":"rgba(0,0,0,0.25)","title":{},"automargin":true,"fixedrange":true},"yaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.65)"},"showgrid":false,"zeroline":false,"showline":true,"linecolor":"rgba(0,0,0,0.25)","linewidth":1,"ticks":"outside","ticklen":6,"tickcolor":"rgba(0,0,0,0.25)","title":{},"tickformat":".2f","automargin":true,"fixedrange":true},"barmode":"group","autosize":true,"paper_bgcolor":"rgba(0,0,0,0)","plot_bgcolor":"rgba(0,0,0,0)","hovermode":"x unified"}, {"displayModeBar": false, "responsive": true, "scrollZoom": false, "doubleClick": false, "modeBarButtonsToRemove": ["zoom2d", "pan2d", "select2d", "lasso2d", "zoomIn2d", "zoomOut2d", "autoScale2d", "resetScale2d", "toggleSpikelines"]} ).then(function(){
|
2 |
-
|
3 |
-
(function(){
|
4 |
-
var plots = document.querySelectorAll('.js-plotly-plot');
|
5 |
-
plots.forEach(function(gd){
|
6 |
-
function round(){
|
7 |
-
try {
|
8 |
-
var root = gd && gd.parentNode ? gd.parentNode : document;
|
9 |
-
var rects = root.querySelectorAll('.hoverlayer .hovertext rect');
|
10 |
-
rects.forEach(function(r){ r.setAttribute('rx', 8); r.setAttribute('ry', 8); });
|
11 |
-
} catch(e) {}
|
12 |
-
}
|
13 |
-
if (gd && gd.on){
|
14 |
-
gd.on('plotly_hover', round);
|
15 |
-
gd.on('plotly_unhover', round);
|
16 |
-
gd.on('plotly_relayout', round);
|
17 |
-
}
|
18 |
-
setTimeout(round, 0);
|
19 |
-
});
|
20 |
-
})();
|
21 |
-
|
22 |
-
}) }; </script> </div>
|
|
|
1 |
+
<div> <div id="3e4ed4fe-23b4-4ec1-810e-20d6fce0209b" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("3e4ed4fe-23b4-4ec1-810e-20d6fce0209b")) { Plotly.newPlot( "3e4ed4fe-23b4-4ec1-810e-20d6fce0209b", [{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(78, 165, 183)"},"name":"parameters","showlegend":true,"visible":true,"x":["1024","2048","4096","8192"],"y":[4.0,4.0,4.0,4.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(227, 138, 66)"},"name":"gradients","showlegend":true,"visible":true,"x":["1024","2048","4096","8192"],"y":[4.0,4.0,4.0,4.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(232, 137, 171)"},"name":"optimizer","showlegend":true,"visible":true,"x":["1024","2048","4096","8192"],"y":[8.0,8.0,8.0,8.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(206, 192, 250)"},"name":"activations","showlegend":true,"visible":true,"x":["1024","2048","4096","8192"],"y":[3.6,14.4,57.6,230.4],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(78, 165, 183)"},"name":"parameters","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[13.3,13.3,13.3,13.3],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(227, 138, 66)"},"name":"gradients","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[13.3,13.3,13.3,13.3],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(232, 137, 171)"},"name":"optimizer","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[26.6,26.6,26.6,26.6],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(206, 192, 250)"},"name":"activations","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[9.3,37.2,148.8,595.2],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(78, 165, 183)"},"name":"parameters","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[26.0,26.0,26.0,26.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(227, 138, 66)"},"name":"gradients","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[26.0,26.0,26.0,26.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(232, 137, 171)"},"name":"optimizer","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[52.0,52.0,52.0,52.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(206, 192, 250)"},"name":"activations","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[46.2,184.8,739.2,2956.8],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(78, 165, 183)"},"name":"parameters","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[244.0,244.0,244.0,244.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(227, 138, 66)"},"name":"gradients","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[244.0,244.0,244.0,244.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(232, 137, 171)"},"name":"optimizer","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[488.0,488.0,488.0,488.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(206, 192, 250)"},"name":"activations","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[145.7,582.8,2331.2,9324.8],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(78, 165, 183)"},"name":"parameters","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[1520.0,1520.0,1520.0,1520.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(227, 138, 66)"},"name":"gradients","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[1520.0,1520.0,1520.0,1520.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(232, 137, 171)"},"name":"optimizer","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[3040.0,3040.0,3040.0,3040.0],"type":"bar"},{"hovertemplate":"Seq len=%{x}\u003cbr\u003eMem=%{y:.1f}GB\u003cbr\u003e%{data.name}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"rgb(206, 192, 250)"},"name":"activations","showlegend":true,"visible":false,"x":["1024","2048","4096","8192"],"y":[1519.9,6079.6,24318.4,97273.6],"type":"bar"}], {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"margin":{"l":40,"r":28,"t":20,"b":40},"legend":{"orientation":"h","yanchor":"bottom","y":1.02,"xanchor":"left","x":0},"xaxis":{"title":{"text":"Sequence Length"},"fixedrange":true},"yaxis":{"title":{"text":"Memory (GB)"},"fixedrange":true},"barmode":"stack","autosize":true,"paper_bgcolor":"rgba(0,0,0,0)","plot_bgcolor":"rgba(0,0,0,0)","hovermode":"x unified","updatemenus":[{"active":0,"buttons":[{"args":[{"visible":[true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false]},{"yaxis":{"range":[0,258.72]}}],"label":"1B","method":"update"},{"args":[{"visible":[false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false]},{"yaxis":{"range":[0,680.8200000000002]}}],"label":"3B","method":"update"},{"args":[{"visible":[false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false]},{"yaxis":{"range":[0,3213.84]}}],"label":"8B","method":"update"},{"args":[{"visible":[false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false]},{"yaxis":{"range":[0,10815.84]}}],"label":"70B","method":"update"},{"args":[{"visible":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true]},{"yaxis":{"range":[0,108521.28000000001]}}],"label":"405B","method":"update"}],"showactive":true,"type":"dropdown","x":1.03,"xanchor":"left","y":0.6,"yanchor":"top"},{"active":0,"buttons":[{"args":[{"y":[[4.0,4.0,4.0,4.0],[4.0,4.0,4.0,4.0],[8.0,8.0,8.0,8.0],[3.6,14.4,57.6,230.4],[13.3,13.3,13.3,13.3],[13.3,13.3,13.3,13.3],[26.6,26.6,26.6,26.6],[9.3,37.2,148.8,595.2],[26.0,26.0,26.0,26.0],[26.0,26.0,26.0,26.0],[52.0,52.0,52.0,52.0],[46.2,184.8,739.2,2956.8],[244.0,244.0,244.0,244.0],[244.0,244.0,244.0,244.0],[488.0,488.0,488.0,488.0],[145.7,582.8,2331.2,9324.8],[1520.0,1520.0,1520.0,1520.0],[1520.0,1520.0,1520.0,1520.0],[3040.0,3040.0,3040.0,3040.0],[1519.9,6079.6,24318.4,97273.6]]},{"yaxis":{"range":[0,108521.28000000001]}}],"label":"None","method":"update"},{"args":[{"y":[[4.0,4.0,4.0,4.0],[4.0,4.0,4.0,4.0],[8.0,8.0,8.0,8.0],[0.9,3.6,14.4,57.6],[13.3,13.3,13.3,13.3],[13.3,13.3,13.3,13.3],[26.6,26.6,26.6,26.6],[2.325,9.3,37.2,148.8],[26.0,26.0,26.0,26.0],[26.0,26.0,26.0,26.0],[52.0,52.0,52.0,52.0],[11.55,46.2,184.8,739.2],[244.0,244.0,244.0,244.0],[244.0,244.0,244.0,244.0],[488.0,488.0,488.0,488.0],[36.425,145.7,582.8,2331.2],[1520.0,1520.0,1520.0,1520.0],[1520.0,1520.0,1520.0,1520.0],[3040.0,3040.0,3040.0,3040.0],[379.975,1519.9,6079.6,24318.4]]},{"yaxis":{"range":[0,31918.320000000003]}}],"label":"selective","method":"update"},{"args":[{"y":[[4.0,4.0,4.0,4.0],[4.0,4.0,4.0,4.0],[8.0,8.0,8.0,8.0],[0.225,0.9,3.6,14.4],[13.3,13.3,13.3,13.3],[13.3,13.3,13.3,13.3],[26.6,26.6,26.6,26.6],[0.58125,2.325,9.3,37.2],[26.0,26.0,26.0,26.0],[26.0,26.0,26.0,26.0],[52.0,52.0,52.0,52.0],[2.8875,11.55,46.2,184.8],[244.0,244.0,244.0,244.0],[244.0,244.0,244.0,244.0],[488.0,488.0,488.0,488.0],[9.10625,36.425,145.7,582.8],[1520.0,1520.0,1520.0,1520.0],[1520.0,1520.0,1520.0,1520.0],[3040.0,3040.0,3040.0,3040.0],[94.99375,379.975,1519.9,6079.6]]},{"yaxis":{"range":[0,12767.580000000002]}}],"label":"full","method":"update"}],"showactive":true,"type":"dropdown","x":1.03,"xanchor":"left","y":0.4,"yanchor":"top"}],"annotations":[{"showarrow":false,"text":"Model Size:","x":1.03,"xanchor":"left","xref":"paper","y":0.6,"yanchor":"bottom","yref":"paper"},{"showarrow":false,"text":"Recomputation:","x":1.03,"xanchor":"left","xref":"paper","y":0.4,"yanchor":"bottom","yref":"paper"}]}, {"displayModeBar": false, "responsive": true, "scrollZoom": false} ) }; </script> </div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/src/content/fragments/color-picker.html
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="color-picker" style="width:100%; margin: 10px 0;">
|
2 |
+
<style>
|
3 |
+
.color-picker .picker__stack { display:flex; flex-direction:column; gap:12px; }
|
4 |
+
.color-picker .current-card { display:flex; flex-direction: column; align-items:stretch; gap:12px; padding:14px 16px; border:1px solid var(--border-color); background: var(--surface-bg); border-radius: 12px; }
|
5 |
+
.color-picker .current-main { display:flex; align-items:center; gap:12px; min-width: 0; }
|
6 |
+
.color-picker .current-swatch { width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--border-color); }
|
7 |
+
.color-picker .current-text { display:flex; flex-direction: column; line-height: 1.2; min-width: 0; }
|
8 |
+
.color-picker .current-name { font-size: 14px; font-weight: 800; color: var(--text-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: clamp(140px, 28vw, 260px); }
|
9 |
+
.color-picker .current-hex, .color-picker .current-extra { font-size: 11px; color: var(--muted-color); letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: clamp(140px, 28vw, 260px); }
|
10 |
+
/* theme preview styles removed */
|
11 |
+
.color-picker .picker__bar { display:flex; align-items:center; gap:12px; }
|
12 |
+
.color-picker .picker__label { font-weight:700; font-size: 13px; color: var(--text-color); }
|
13 |
+
.color-picker .hue-slider { position:relative; height:16px; border-radius:10px; border:1px solid var(--border-color); background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); cursor: ew-resize; touch-action: none; flex: 1 1 320px; min-width: 200px; }
|
14 |
+
.color-picker .hue-knob { position:absolute; top:50%; left:93.6%; width:14px; height:14px; border-radius:50%; border:2px solid #fff; transform:translate(-50%, -50%); background: var(--surface-bg); z-index: 2; box-shadow: 0 0 0 1px rgba(0,0,0,.05); }
|
15 |
+
.color-picker .hue-slider:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
|
16 |
+
.color-picker .hue-value { font-variant-numeric: tabular-nums; color: var(--muted-color); min-width: 54px; text-align: right; }
|
17 |
+
@media (max-width: 560px) { .color-picker .picker__bar { gap:8px; } }
|
18 |
+
</style>
|
19 |
+
<div class="picker__stack">
|
20 |
+
<div class="current-card">
|
21 |
+
<div class="current-main">
|
22 |
+
<div class="current-swatch" aria-label="Current color" title="Current color"></div>
|
23 |
+
<div class="current-text">
|
24 |
+
<div class="current-name">—</div>
|
25 |
+
<div class="current-hex">—</div>
|
26 |
+
<div class="current-extra current-lch">—</div>
|
27 |
+
<div class="current-extra current-rgb">—</div>
|
28 |
+
</div>
|
29 |
+
</div>
|
30 |
+
<div class="picker__bar">
|
31 |
+
<div class="picker__label">Hue</div>
|
32 |
+
<div class="hue-slider" role="slider" aria-label="Hue" aria-valuemin="0" aria-valuemax="360" aria-valuenow="337" tabindex="0">
|
33 |
+
<div class="hue-knob"></div>
|
34 |
+
</div>
|
35 |
+
<div class="hue-value">337°</div>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
<script>
|
41 |
+
(() => {
|
42 |
+
// Ensure chroma.js is loaded once
|
43 |
+
const ensureChroma = (next) => {
|
44 |
+
if (window.chroma) return next();
|
45 |
+
const loadScript = (id, src, onload, onerror) => {
|
46 |
+
let s = document.getElementById(id);
|
47 |
+
if (s) { return onload && onload(); }
|
48 |
+
s = document.createElement('script');
|
49 |
+
s.id = id; s.src = src; s.async = true;
|
50 |
+
if (onload) s.addEventListener('load', onload, { once: true });
|
51 |
+
if (onerror) s.addEventListener('error', onerror, { once: true });
|
52 |
+
document.head.appendChild(s);
|
53 |
+
};
|
54 |
+
loadScript('chroma-cdn', 'https://unpkg.com/chroma-js@2.4.2/dist/chroma.min.js', next, () => {
|
55 |
+
loadScript('chroma-cdn-fallback', 'https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.4.2/chroma.min.js', next);
|
56 |
+
});
|
57 |
+
};
|
58 |
+
|
59 |
+
// Minimal embedded color-name list (same as palettes)
|
60 |
+
const COLOR_NAMES = [{"name":"Candy Apple Red","hex":"#ff0800"},{"name":"Boiling Magma","hex":"#ff3300"},{"name":"Aerospace Orange","hex":"#ff4f00"},{"name":"Burtuqali Orange","hex":"#ff6700"},{"name":"American Orange","hex":"#ff8b00"},{"name":"Cheese","hex":"#ffa600"},{"name":"Amber","hex":"#ffbf00"},{"name":"Demonic Yellow","hex":"#ffe700"},{"name":"Bat-Signal","hex":"#feff00"},{"name":"Bitter Lime","hex":"#cfff00"},{"name":"Electric Lime","hex":"#ccff00"},{"name":"Bright Yellow Green","hex":"#9dff00"},{"name":"Lasting Lime","hex":"#88ff00"},{"name":"Bright Green","hex":"#66ff00"},{"name":"Chlorophyll Green","hex":"#4aff00"},{"name":"Green Screen","hex":"#22ff00"},{"name":"Electric Pickle","hex":"#00ff04"},{"name":"Acid","hex":"#00ff22"},{"name":"Lucent Lime","hex":"#00ff33"},{"name":"Cathode Green","hex":"#00ff55"},{"name":"Booger Buster","hex":"#00ff77"},{"name":"Green Gas","hex":"#00ff99"},{"name":"Enthusiasm","hex":"#00ffaa"},{"name":"Ice Ice Baby","hex":"#00ffdd"},{"name":"Master Sword Blue","hex":"#00ffee"},{"name":"Agressive Aqua","hex":"#00fbff"},{"name":"Vivid Sky Blue","hex":"#00ccff"},{"name":"Capri","hex":"#00bfff"},{"name":"Sky of Magritte","hex":"#0099ff"},{"name":"Azure","hex":"#007fff"},{"name":"Blue Ribbon","hex":"#0066ff"},{"name":"Blinking Blue","hex":"#0033ff"},{"name":"Icelandic Water","hex":"#0011ff"},{"name":"Blue","hex":"#0000ff"},{"name":"Blue Pencil","hex":"#2200ff"},{"name":"Electric Ultramarine","hex":"#3f00ff"},{"name":"Aladdin's Feather","hex":"#5500ff"},{"name":"Purple Climax","hex":"#8800ff"},{"name":"Amethyst Ganzstar","hex":"#8f00ff"},{"name":"Electric Purple","hex":"#bf00ff"},{"name":"Phlox","hex":"#df00ff"},{"name":"Brusque Pink","hex":"#ee00ff"},{"name":"Bright Magenta","hex":"#ff08e8"},{"name":"Brutal Pink","hex":"#ff00bb"},{"name":"Mean Girls Lipstick","hex":"#ff00ae"},{"name":"Big Bang Pink","hex":"#ff0099"},{"name":"Flaming Hot Flamingoes","hex":"#ff005d"},{"name":"Blazing Dragonfruit","hex":"#ff0054"},{"name":"Carmine Red","hex":"#ff0038"},{"name":"Bright Red","hex":"#ff000d"}];
|
61 |
+
if (!window.__colorNames) window.__colorNames = COLOR_NAMES;
|
62 |
+
|
63 |
+
// Shared event bus so multiple instances stay in sync
|
64 |
+
if (!window.__colorPickerBus) {
|
65 |
+
window.__colorPickerBus = (() => {
|
66 |
+
let hue = 337; // shared initial hue
|
67 |
+
let adjusting = false;
|
68 |
+
const listeners = new Set();
|
69 |
+
return {
|
70 |
+
get: () => ({ hue, adjusting }),
|
71 |
+
publish: (sourceId, nextHue, isAdjusting) => {
|
72 |
+
hue = ((nextHue % 360) + 360) % 360;
|
73 |
+
adjusting = !!isAdjusting;
|
74 |
+
listeners.forEach((fn) => { try { fn({ sourceId, hue, adjusting }); } catch {} });
|
75 |
+
},
|
76 |
+
subscribe: (fn) => { listeners.add(fn); return () => listeners.delete(fn); }
|
77 |
+
};
|
78 |
+
})();
|
79 |
+
}
|
80 |
+
|
81 |
+
const bootstrap = () => {
|
82 |
+
const mount = document.currentScript ? document.currentScript.previousElementSibling : null;
|
83 |
+
const root = mount && mount.closest('.color-picker') ? mount.closest('.color-picker') : document.querySelector('.color-picker');
|
84 |
+
if (!root || root.dataset.mounted) return; root.dataset.mounted = 'true';
|
85 |
+
|
86 |
+
const slider = root.querySelector('.hue-slider');
|
87 |
+
const knob = root.querySelector('.hue-knob');
|
88 |
+
const hueValue = root.querySelector('.hue-value');
|
89 |
+
const currentSwatch = root.querySelector('.current-swatch');
|
90 |
+
const currentName = root.querySelector('.current-name');
|
91 |
+
const currentHex = root.querySelector('.current-hex');
|
92 |
+
const currentLch = root.querySelector('.current-lch');
|
93 |
+
const currentRgb = root.querySelector('.current-rgb');
|
94 |
+
|
95 |
+
const bus = window.__colorPickerBus;
|
96 |
+
const instanceId = Math.random().toString(36).slice(2);
|
97 |
+
|
98 |
+
const getName = (hex) => {
|
99 |
+
const list = (window.__colorNames && window.__colorNames.length) ? window.__colorNames : COLOR_NAMES;
|
100 |
+
if (list && window.chroma) {
|
101 |
+
let bestName = null; let best = Infinity;
|
102 |
+
for (let i = 0; i < list.length; i++) {
|
103 |
+
const item = list[i];
|
104 |
+
const d = (chroma.deltaE ? chroma.deltaE(hex, item.hex) : chroma.distance(hex, item.hex, 'lab'));
|
105 |
+
if (d < best) { best = d; bestName = item.name; }
|
106 |
+
}
|
107 |
+
if (bestName) return bestName;
|
108 |
+
}
|
109 |
+
const hh = chroma(hex).get('hsl.h') || 0;
|
110 |
+
const labels = ['Red','Orange','Yellow','Lime','Green','Cyan','Blue','Indigo','Violet','Magenta'];
|
111 |
+
const idx = Math.round(((hh % 360) / 360) * (labels.length - 1));
|
112 |
+
return labels[idx];
|
113 |
+
};
|
114 |
+
|
115 |
+
const updateUI = (h, adjusting) => {
|
116 |
+
const pct = (h / 360) * 100;
|
117 |
+
if (knob) knob.style.left = pct + '%';
|
118 |
+
if (hueValue) hueValue.textContent = `${Math.round(h)}°`;
|
119 |
+
if (slider) slider.setAttribute('aria-valuenow', String(Math.round(h)));
|
120 |
+
// Use LCH for consistent chroma across hues
|
121 |
+
const L = 70; // lightness
|
122 |
+
const C = 60; // chroma kept within sRGB-friendly range
|
123 |
+
const base = chroma.lch(L, C, h);
|
124 |
+
const baseHex = base.hex();
|
125 |
+
if (currentSwatch) currentSwatch.style.background = baseHex;
|
126 |
+
if (currentName) currentName.textContent = getName(baseHex.toUpperCase());
|
127 |
+
if (currentHex) currentHex.textContent = baseHex.toUpperCase();
|
128 |
+
if (currentLch) {
|
129 |
+
const lc = base.lch();
|
130 |
+
const L = Math.round((lc[0] || 0));
|
131 |
+
const C = Math.round((lc[1] || 0));
|
132 |
+
const H = Math.round(((lc[2] || 0) % 360 + 360) % 360);
|
133 |
+
currentLch.textContent = `LCH ${L}, ${C}, ${H}°`;
|
134 |
+
}
|
135 |
+
if (currentRgb) {
|
136 |
+
const rgb = base.rgb().map(v => Math.round(v));
|
137 |
+
currentRgb.textContent = `RGB ${rgb[0]}, ${rgb[1]}, ${rgb[2]}`;
|
138 |
+
}
|
139 |
+
// Appliquer au thème (toujours, pour refléter la sélection)
|
140 |
+
const hoverL = Math.max(0, Math.min(100, L - 8));
|
141 |
+
const hoverHex = chroma.lch(hoverL, C, h).hex();
|
142 |
+
const rootEl = document.documentElement;
|
143 |
+
rootEl.style.setProperty('--primary', baseHex);
|
144 |
+
rootEl.style.setProperty('--primary-hover', hoverHex);
|
145 |
+
};
|
146 |
+
|
147 |
+
const getHueFromEvent = (ev) => {
|
148 |
+
const rect = slider.getBoundingClientRect();
|
149 |
+
const clientX = ev.touches ? ev.touches[0].clientX : ev.clientX;
|
150 |
+
const x = clientX - rect.left;
|
151 |
+
const t = Math.max(0, Math.min(1, x / rect.width));
|
152 |
+
return t * 360;
|
153 |
+
};
|
154 |
+
|
155 |
+
// Subscribe to bus to sync multiple instances
|
156 |
+
const unsubscribe = bus.subscribe(({ sourceId, hue, adjusting }) => {
|
157 |
+
if (sourceId === instanceId) return; // avoid feedback
|
158 |
+
updateUI(hue, adjusting);
|
159 |
+
});
|
160 |
+
|
161 |
+
// Init depuis la couleur de thème si disponible
|
162 |
+
try {
|
163 |
+
const cssPrimary = getComputedStyle(document.documentElement).getPropertyValue('--primary').trim();
|
164 |
+
if (cssPrimary) {
|
165 |
+
const initH = chroma(cssPrimary).get('hsl.h') || 0;
|
166 |
+
updateUI(initH, false);
|
167 |
+
bus.publish(instanceId, initH, false);
|
168 |
+
} else {
|
169 |
+
const { hue: sharedHue } = bus.get();
|
170 |
+
updateUI(sharedHue, false);
|
171 |
+
}
|
172 |
+
} catch {
|
173 |
+
const { hue: sharedHue } = bus.get();
|
174 |
+
updateUI(sharedHue, false);
|
175 |
+
}
|
176 |
+
|
177 |
+
const onDown = (ev) => {
|
178 |
+
ev.preventDefault();
|
179 |
+
const h = getHueFromEvent(ev);
|
180 |
+
updateUI(h, true);
|
181 |
+
bus.publish(instanceId, h, true);
|
182 |
+
const move = (e) => { e.preventDefault && e.preventDefault(); const hh = getHueFromEvent(e); updateUI(hh, true); bus.publish(instanceId, hh, true); };
|
183 |
+
const up = () => { bus.publish(instanceId, getHueFromEvent(ev), false); window.removeEventListener('mousemove', move); window.removeEventListener('touchmove', move); window.removeEventListener('mouseup', up); window.removeEventListener('touchend', up); };
|
184 |
+
window.addEventListener('mousemove', move, { passive: false });
|
185 |
+
window.addEventListener('touchmove', move, { passive: false });
|
186 |
+
window.addEventListener('mouseup', up, { once: true });
|
187 |
+
window.addEventListener('touchend', up, { once: true });
|
188 |
+
};
|
189 |
+
|
190 |
+
if (slider) {
|
191 |
+
slider.addEventListener('mousedown', onDown);
|
192 |
+
slider.addEventListener('touchstart', onDown, { passive: false });
|
193 |
+
// Minimal support clavier (←/→, ⇧ pour pas trop saccader)
|
194 |
+
slider.addEventListener('keydown', (e) => {
|
195 |
+
const step = e.shiftKey ? 10 : 2;
|
196 |
+
if (e.key === 'ArrowLeft') { e.preventDefault(); const { hue } = bus.get(); const h = hue - step; updateUI(h, true); bus.publish(instanceId, h, true); bus.publish(instanceId, h, false); }
|
197 |
+
if (e.key === 'ArrowRight') { e.preventDefault(); const { hue } = bus.get(); const h = hue + step; updateUI(h, true); bus.publish(instanceId, h, true); bus.publish(instanceId, h, false); }
|
198 |
+
});
|
199 |
+
}
|
200 |
+
|
201 |
+
// Clean up on detach (best-effort)
|
202 |
+
const ro = new MutationObserver(() => {
|
203 |
+
if (!document.body.contains(root)) { unsubscribe && unsubscribe(); ro.disconnect(); }
|
204 |
+
});
|
205 |
+
ro.observe(document.body, { childList: true, subtree: true });
|
206 |
+
};
|
207 |
+
|
208 |
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => ensureChroma(bootstrap), { once: true });
|
209 |
+
else ensureChroma(bootstrap);
|
210 |
+
})();
|
211 |
+
</script>
|
212 |
+
|
213 |
+
|
app/src/content/fragments/d3-bar.html
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="d3-bar" style="width:100%;margin:10px 0;"></div>
|
2 |
+
<style>
|
3 |
+
.d3-bar .controls { margin-top: 12px; display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
|
4 |
+
.d3-bar .controls label { font-size: 12px; color: var(--muted-color); display: flex; align-items: center; gap: 8px; white-space: nowrap; padding: 6px 10px; }
|
5 |
+
.d3-bar .controls select { font-size: 12px; padding: 8px 28px 8px 10px; border: 1px solid var(--border-color); border-radius: 8px; background-color: var(--surface-bg); color: var(--text-color); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%230f1115' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; background-size: 12px; -webkit-appearance: none; -moz-appearance: none; appearance: none; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
|
6 |
+
[data-theme="dark"] .d3-bar .controls select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); }
|
7 |
+
.d3-bar .controls select:hover { border-color: var(--primary); }
|
8 |
+
.d3-bar .controls select:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(232,137,171,.25); outline: none; }
|
9 |
+
.d3-bar .legend { font-size: 12px; line-height: 1.35; color: var(--text-color); }
|
10 |
+
</style>
|
11 |
+
<script>
|
12 |
+
(() => {
|
13 |
+
const ensureD3 = (cb) => {
|
14 |
+
if (window.d3 && typeof window.d3.select === 'function') return cb();
|
15 |
+
let s = document.getElementById('d3-cdn-script');
|
16 |
+
if (!s) { s = document.createElement('script'); s.id = 'd3-cdn-script'; s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js'; document.head.appendChild(s); }
|
17 |
+
const onReady = () => { if (window.d3 && typeof window.d3.select === 'function') cb(); };
|
18 |
+
s.addEventListener('load', onReady, { once: true });
|
19 |
+
if (window.d3) onReady();
|
20 |
+
};
|
21 |
+
|
22 |
+
const bootstrap = () => {
|
23 |
+
const mount = document.currentScript ? document.currentScript.previousElementSibling : null;
|
24 |
+
const container = (mount && mount.querySelector && mount.querySelector('.d3-bar')) || document.querySelector('.d3-bar');
|
25 |
+
if (!container) return;
|
26 |
+
if (container.dataset) { if (container.dataset.mounted === 'true') return; container.dataset.mounted = 'true'; }
|
27 |
+
|
28 |
+
// Data, matching bar.py
|
29 |
+
const seqLabels = ["1024","2048","4096","8192"];
|
30 |
+
const seqScale = [1,2,4,8];
|
31 |
+
const components = [
|
32 |
+
{ key: 'parameters', color: 'rgb(78, 165, 183)' },
|
33 |
+
{ key: 'gradients', color: 'rgb(227, 138, 66)' },
|
34 |
+
{ key: 'optimizer', color: 'var(--primary)' },
|
35 |
+
{ key: 'activations', color: 'rgb(206, 192, 250)' },
|
36 |
+
];
|
37 |
+
const modelSizes = ["1B","3B","8B","70B","405B"];
|
38 |
+
const paramsMem = { "1B":4.0, "3B":13.3, "8B":26.0, "70B":244.0, "405B":1520.0 };
|
39 |
+
const actCoeff = { "1B":3.6, "3B":9.3, "8B":46.2, "70B":145.7, "405B":1519.9 };
|
40 |
+
const recomputeModes = ["none","selective","full"];
|
41 |
+
|
42 |
+
const activationsCurve = (sizeKey, mode) => {
|
43 |
+
const coeff = actCoeff[sizeKey];
|
44 |
+
let arr = seqScale.map((v) => coeff * (v * v));
|
45 |
+
if (mode === 'selective') arr = arr.map((v) => v * 0.25);
|
46 |
+
else if (mode === 'full') arr = arr.map((v) => v * (1 / 16));
|
47 |
+
return arr;
|
48 |
+
};
|
49 |
+
const stackFor = (sizeKey, mode) => {
|
50 |
+
const p = seqScale.map(() => paramsMem[sizeKey]);
|
51 |
+
const g = seqScale.map(() => paramsMem[sizeKey]);
|
52 |
+
const o = seqScale.map(() => 2*paramsMem[sizeKey]);
|
53 |
+
const a = activationsCurve(sizeKey, mode);
|
54 |
+
return { parameters: p, gradients: g, optimizer: o, activations: a };
|
55 |
+
};
|
56 |
+
|
57 |
+
const Y = {}; // Y[mode][size][component] => array
|
58 |
+
recomputeModes.forEach((m) => {
|
59 |
+
Y[m] = {}; modelSizes.forEach((s) => { Y[m][s] = stackFor(s, m); });
|
60 |
+
});
|
61 |
+
|
62 |
+
// Controls
|
63 |
+
const controls = document.createElement('div');
|
64 |
+
controls.className = 'controls';
|
65 |
+
const labelSize = document.createElement('label'); labelSize.textContent = 'Model Size';
|
66 |
+
const selSize = document.createElement('select'); modelSizes.forEach((s) => { const o = document.createElement('option'); o.value = s; o.textContent = s; selSize.appendChild(o); });
|
67 |
+
labelSize.appendChild(selSize);
|
68 |
+
const labelRecomp = document.createElement('label'); labelRecomp.textContent = 'Recomputation';
|
69 |
+
const selRecomp = document.createElement('select'); recomputeModes.forEach((m) => { const o = document.createElement('option'); o.value = m; o.textContent = m; selRecomp.appendChild(o); });
|
70 |
+
labelRecomp.appendChild(selRecomp);
|
71 |
+
|
72 |
+
// SVG scaffolding
|
73 |
+
const svg = d3.select(container).append('svg').attr('width','100%').style('display','block');
|
74 |
+
const gRoot = svg.append('g');
|
75 |
+
const gGrid = gRoot.append('g').attr('class','grid');
|
76 |
+
const gAxes = gRoot.append('g').attr('class','axes');
|
77 |
+
const gBars = gRoot.append('g').attr('class','bars');
|
78 |
+
const gLegend = gRoot.append('foreignObject').attr('class','legend');
|
79 |
+
|
80 |
+
// Tooltip
|
81 |
+
container.style.position = container.style.position || 'relative';
|
82 |
+
let tip = container.querySelector('.d3-tooltip'); let tipInner;
|
83 |
+
if (!tip) { tip = document.createElement('div'); tip.className = 'd3-tooltip'; Object.assign(tip.style,{ position:'absolute', top:'0px', left:'0px', transform:'translate(-9999px, -9999px)', pointerEvents:'none', padding:'8px 10px', borderRadius:'8px', fontSize:'12px', lineHeight:'1.35', border:'1px solid var(--border-color)', background:'var(--surface-bg)', color:'var(--text-color)', boxShadow:'0 4px 24px rgba(0,0,0,.18)', opacity:'0', transition:'opacity .12s ease' }); tipInner = document.createElement('div'); tipInner.className = 'd3-tooltip__inner'; tipInner.style.textAlign='left'; tip.appendChild(tipInner); container.appendChild(tip); } else { tipInner = tip.querySelector('.d3-tooltip__inner') || tip; }
|
84 |
+
|
85 |
+
// State
|
86 |
+
let currentSize = modelSizes[0];
|
87 |
+
let currentMode = 'none';
|
88 |
+
|
89 |
+
// Layout & scales
|
90 |
+
let width=800, height=360; const margin = { top: 16, right: 28, bottom: 56, left: 64 };
|
91 |
+
const x0 = d3.scaleBand().paddingInner(0.25).paddingOuter(0.1); // groups (seq)
|
92 |
+
const y = d3.scaleLinear();
|
93 |
+
const colorOf = (key) => components.find((c)=>c.key===key).color;
|
94 |
+
|
95 |
+
function yMax(sizeKey, mode){
|
96 |
+
const s = Y[mode][sizeKey];
|
97 |
+
let max = 0; for (let i=0;i<seqLabels.length;i++){ const sum = s.parameters[i]+s.gradients[i]+s.optimizer[i]+s.activations[i]; if (sum>max) max=sum; }
|
98 |
+
return max*1.05;
|
99 |
+
}
|
100 |
+
|
101 |
+
function renderLegend(innerWidth, innerHeight){
|
102 |
+
const legendWidth = 160, legendHeight = 84;
|
103 |
+
gLegend.attr('x', 15).attr('y', -3).attr('width', legendWidth).attr('height', legendHeight);
|
104 |
+
const root = gLegend.selectAll('div').data([0]).join('xhtml:div');
|
105 |
+
root.html(`
|
106 |
+
<div style="display:flex;flex-direction:column;gap:6px;">
|
107 |
+
${components.map(c => `<div style="display:flex;align-items:center;gap:8px;"><span style="width:18px;height:10px;background:${c.color};border-radius:2px;display:inline-block"></span><span>${c.key}</span></div>`).join('')}
|
108 |
+
</div>
|
109 |
+
`);
|
110 |
+
}
|
111 |
+
|
112 |
+
function updateScales(){
|
113 |
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
114 |
+
const axisColor = isDark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)';
|
115 |
+
const tickColor = isDark ? 'rgba(255,255,255,0.70)' : 'rgba(0,0,0,0.55)';
|
116 |
+
const gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)';
|
117 |
+
|
118 |
+
width = container.clientWidth || 800; height = Math.max(260, Math.round(width/3)); svg.attr('width', width).attr('height', height);
|
119 |
+
const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
|
120 |
+
|
121 |
+
x0.domain(seqLabels).range([0, innerWidth]);
|
122 |
+
y.domain([0, yMax(currentSize, currentMode)]).range([innerHeight, 0]).nice();
|
123 |
+
|
124 |
+
// Grid
|
125 |
+
gGrid.selectAll('*').remove();
|
126 |
+
gGrid.selectAll('line').data(y.ticks(6)).join('line')
|
127 |
+
.attr('x1', 0).attr('x2', innerWidth).attr('y1', (d)=>y(d)).attr('y2', (d)=>y(d))
|
128 |
+
.attr('stroke', gridColor).attr('stroke-width', 1).attr('shape-rendering', 'crispEdges');
|
129 |
+
|
130 |
+
// Axes
|
131 |
+
gAxes.selectAll('*').remove();
|
132 |
+
gAxes.append('g').attr('transform', `translate(0,${innerHeight})`).call(d3.axisBottom(x0)).call((g)=>{ g.selectAll('path, line').attr('stroke', axisColor); g.selectAll('text').attr('fill', tickColor).style('font-size','12px'); });
|
133 |
+
gAxes.append('g').call(d3.axisLeft(y).ticks(6).tickFormat(d3.format('.2f'))).call((g)=>{ g.selectAll('path, line').attr('stroke', axisColor); g.selectAll('text').attr('fill', tickColor).style('font-size','12px'); });
|
134 |
+
|
135 |
+
// Axis labels
|
136 |
+
gAxes.append('text').attr('class','axis-label axis-label--x').attr('x', innerWidth/2).attr('y', innerHeight + 44).attr('text-anchor','middle').style('font-size','12px').style('fill', tickColor).text('Sequence Length');
|
137 |
+
gAxes.append('text').attr('class','axis-label axis-label--y').attr('text-anchor','middle').attr('transform', `translate(${-52},${innerHeight/2}) rotate(-90)`).style('font-size','12px').style('fill', tickColor).text('Memory (GB)');
|
138 |
+
|
139 |
+
renderLegend(innerWidth, innerHeight);
|
140 |
+
|
141 |
+
return { innerWidth, innerHeight };
|
142 |
+
}
|
143 |
+
|
144 |
+
function drawBars(){
|
145 |
+
const stacks = Y[currentMode][currentSize];
|
146 |
+
const series = components.map((c)=>({ key: c.key, color: c.color, values: stacks[c.key] }));
|
147 |
+
// Stack values
|
148 |
+
const stacked = seqLabels.map((label, i) => {
|
149 |
+
let acc = 0; const items = [];
|
150 |
+
series.forEach((s) => { const y0 = acc; const y1 = acc + s.values[i]; items.push({ key: s.key, color: s.color, i, y0, y1, xLabel: label, value: s.values[i] }); acc = y1; });
|
151 |
+
return { label, items };
|
152 |
+
});
|
153 |
+
|
154 |
+
const { innerWidth, innerHeight } = updateScales();
|
155 |
+
|
156 |
+
const bandWidth = x0.bandwidth();
|
157 |
+
const groups = gBars.selectAll('g.bar-group').data(stacked, d=>d.label);
|
158 |
+
const groupsEnter = groups.enter().append('g').attr('class','bar-group');
|
159 |
+
groupsEnter.merge(groups).attr('transform', (d)=>`translate(${x0(d.label)},0)`);
|
160 |
+
groups.exit().remove();
|
161 |
+
|
162 |
+
const rects = groupsEnter.merge(groups).selectAll('rect.bar').data(d=>d.items, d=>d.key);
|
163 |
+
rects.enter().append('rect').attr('class','bar').attr('x', 0).attr('width', bandWidth)
|
164 |
+
.attr('y', (d)=>y(d.y1)).attr('height', (d)=>Math.max(0.5, y(d.y0) - y(d.y1)))
|
165 |
+
.attr('fill', (d)=>d.color)
|
166 |
+
.on('mouseenter', function(ev, d){
|
167 |
+
d3.select(this).attr('stroke', 'rgba(0,0,0,0.85)').attr('stroke-width', 1);
|
168 |
+
tipInner.innerHTML = `<div><strong>${d.key}</strong></div><div><strong>Seq</strong> ${d.xLabel}</div><div><strong>Mem</strong> ${d.value.toFixed(1)} GB</div>`;
|
169 |
+
tip.style.opacity = '1';
|
170 |
+
})
|
171 |
+
.on('mousemove', function(ev, d){
|
172 |
+
const [mx, my] = d3.pointer(ev, container); const offsetX = 12, offsetY = 12; tip.style.transform = `translate(${Math.round(mx+offsetX)}px, ${Math.round(my+offsetY)}px)`;
|
173 |
+
})
|
174 |
+
.on('mouseleave', function(){ tip.style.opacity='0'; tip.style.transform='translate(-9999px, -9999px)'; d3.select(this).attr('stroke','none'); })
|
175 |
+
.merge(rects)
|
176 |
+
.transition().duration(200)
|
177 |
+
.attr('width', bandWidth)
|
178 |
+
.attr('y', (d)=>y(d.y1)).attr('height', (d)=>Math.max(0.5, y(d.y0) - y(d.y1)))
|
179 |
+
.attr('fill', (d)=>d.color);
|
180 |
+
rects.exit().remove();
|
181 |
+
}
|
182 |
+
|
183 |
+
function update(){ drawBars(); }
|
184 |
+
|
185 |
+
// Boot
|
186 |
+
update();
|
187 |
+
container.appendChild(controls);
|
188 |
+
controls.appendChild(labelSize); controls.appendChild(labelRecomp);
|
189 |
+
selSize.addEventListener('change', (e)=>{ currentSize = e.target.value; update(); });
|
190 |
+
selRecomp.addEventListener('change', (e)=>{ currentMode = e.target.value; update(); });
|
191 |
+
|
192 |
+
const rerender = () => { update(); };
|
193 |
+
if (window.ResizeObserver) { const ro = new ResizeObserver(()=>rerender()); ro.observe(container); } else { window.addEventListener('resize', rerender); }
|
194 |
+
};
|
195 |
+
|
196 |
+
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true }); } else { ensureD3(bootstrap); }
|
197 |
+
})();
|
198 |
+
</script>
|
199 |
+
|
200 |
+
|
app/src/content/fragments/d3-line.html
ADDED
@@ -0,0 +1,429 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="d3-line" style="width:100%;margin:10px 0;"></div>
|
2 |
+
<style>
|
3 |
+
.d3-line .d3-line__controls select {
|
4 |
+
font-size: 12px;
|
5 |
+
padding: 8px 28px 8px 10px;
|
6 |
+
border: 1px solid var(--border-color);
|
7 |
+
border-radius: 8px;
|
8 |
+
background-color: var(--surface-bg);
|
9 |
+
color: var(--text-color);
|
10 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%230f1115' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
11 |
+
background-repeat: no-repeat;
|
12 |
+
background-position: right 8px center;
|
13 |
+
background-size: 12px;
|
14 |
+
-webkit-appearance: none;
|
15 |
+
-moz-appearance: none;
|
16 |
+
appearance: none;
|
17 |
+
cursor: pointer;
|
18 |
+
transition: border-color .15s ease, box-shadow .15s ease;
|
19 |
+
}
|
20 |
+
[data-theme="dark"] .d3-line .d3-line__controls select {
|
21 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
22 |
+
}
|
23 |
+
.d3-line .d3-line__controls select:hover {
|
24 |
+
border-color: var(--primary);
|
25 |
+
}
|
26 |
+
.d3-line .d3-line__controls select:focus {
|
27 |
+
border-color: var(--primary);
|
28 |
+
box-shadow: 0 0 0 3px rgba(232,137,171,.25);
|
29 |
+
outline: none;
|
30 |
+
}
|
31 |
+
.d3-line .d3-line__controls label { gap: 8px; }
|
32 |
+
|
33 |
+
/* Range slider themed with --primary */
|
34 |
+
.d3-line .d3-line__controls input[type="range"] {
|
35 |
+
-webkit-appearance: none;
|
36 |
+
appearance: none;
|
37 |
+
width: 100%;
|
38 |
+
height: 6px;
|
39 |
+
border-radius: 999px;
|
40 |
+
background: var(--border-color);
|
41 |
+
outline: none;
|
42 |
+
}
|
43 |
+
.d3-line .d3-line__controls input[type="range"]::-webkit-slider-runnable-track {
|
44 |
+
height: 6px;
|
45 |
+
background: transparent;
|
46 |
+
border-radius: 999px;
|
47 |
+
}
|
48 |
+
.d3-line .d3-line__controls input[type="range"]::-webkit-slider-thumb {
|
49 |
+
-webkit-appearance: none;
|
50 |
+
appearance: none;
|
51 |
+
width: 16px;
|
52 |
+
height: 16px;
|
53 |
+
border-radius: 50%;
|
54 |
+
background: var(--primary);
|
55 |
+
border: 2px solid var(--on-primary);
|
56 |
+
margin-top: -5px;
|
57 |
+
cursor: pointer;
|
58 |
+
}
|
59 |
+
.d3-line .d3-line__controls input[type="range"]::-moz-range-track {
|
60 |
+
height: 6px;
|
61 |
+
background: transparent;
|
62 |
+
border-radius: 999px;
|
63 |
+
}
|
64 |
+
.d3-line .d3-line__controls input[type="range"]::-moz-range-thumb {
|
65 |
+
width: 16px;
|
66 |
+
height: 16px;
|
67 |
+
border-radius: 50%;
|
68 |
+
background: var(--primary);
|
69 |
+
border: 2px solid var(--on-primary);
|
70 |
+
cursor: pointer;
|
71 |
+
}
|
72 |
+
/* Improved line color via CSS */
|
73 |
+
.d3-line .lines path.improved { stroke: var(--primary); }
|
74 |
+
</style>
|
75 |
+
<script>
|
76 |
+
(() => {
|
77 |
+
const ensureD3 = (cb) => {
|
78 |
+
if (window.d3 && typeof window.d3.select === 'function') return cb();
|
79 |
+
let s = document.getElementById('d3-cdn-script');
|
80 |
+
if (!s) {
|
81 |
+
s = document.createElement('script');
|
82 |
+
s.id = 'd3-cdn-script';
|
83 |
+
s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
|
84 |
+
document.head.appendChild(s);
|
85 |
+
}
|
86 |
+
const onReady = () => { if (window.d3 && typeof window.d3.select === 'function') cb(); };
|
87 |
+
s.addEventListener('load', onReady, { once: true });
|
88 |
+
if (window.d3) onReady();
|
89 |
+
};
|
90 |
+
|
91 |
+
const bootstrap = () => {
|
92 |
+
const mount = document.currentScript ? document.currentScript.previousElementSibling : null;
|
93 |
+
const container = (mount && mount.querySelector && mount.querySelector('.d3-line')) || document.querySelector('.d3-line');
|
94 |
+
if (!container) return;
|
95 |
+
if (container.dataset) {
|
96 |
+
if (container.dataset.mounted === 'true') return;
|
97 |
+
container.dataset.mounted = 'true';
|
98 |
+
}
|
99 |
+
|
100 |
+
// Dataset params matching the Plotly version
|
101 |
+
const datasets = [
|
102 |
+
{ name: 'CIFAR-10', base: { ymin:0.10, ymax:0.90, k:10.0, x0:0.55 }, aug: { ymin:0.15, ymax:0.96, k:12.0, x0:0.40 }, target: 0.97 },
|
103 |
+
{ name: 'CIFAR-100', base: { ymin:0.05, ymax:0.70, k: 9.5, x0:0.60 }, aug: { ymin:0.08, ymax:0.80, k:11.0, x0:0.45 }, target: 0.85 },
|
104 |
+
{ name: 'ImageNet-1K', base: { ymin:0.02, ymax:0.68, k: 8.5, x0:0.65 }, aug: { ymin:0.04, ymax:0.75, k: 9.5, x0:0.50 }, target: 0.82 },
|
105 |
+
];
|
106 |
+
|
107 |
+
// Controls UI
|
108 |
+
const controls = document.createElement('div');
|
109 |
+
controls.className = 'd3-line__controls';
|
110 |
+
Object.assign(controls.style, {
|
111 |
+
marginTop: '12px',
|
112 |
+
display: 'flex',
|
113 |
+
gap: '16px',
|
114 |
+
alignItems: 'center'
|
115 |
+
});
|
116 |
+
|
117 |
+
const labelDs = document.createElement('label');
|
118 |
+
Object.assign(labelDs.style, {
|
119 |
+
fontSize: '12px', color: 'rgba(0,0,0,.65)', display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap', padding: '6px 10px'
|
120 |
+
});
|
121 |
+
labelDs.textContent = 'Dataset';
|
122 |
+
const selectDs = document.createElement('select');
|
123 |
+
Object.assign(selectDs.style, { fontSize: '12px' });
|
124 |
+
datasets.forEach((d, i) => {
|
125 |
+
const o = document.createElement('option');
|
126 |
+
o.value = String(i);
|
127 |
+
o.textContent = d.name;
|
128 |
+
selectDs.appendChild(o);
|
129 |
+
});
|
130 |
+
labelDs.appendChild(selectDs);
|
131 |
+
|
132 |
+
const labelAlpha = document.createElement('label');
|
133 |
+
Object.assign(labelAlpha.style, {
|
134 |
+
fontSize: '12px', color: 'rgba(0,0,0,.65)', display: 'flex', alignItems: 'center', gap: '10px', flex: '1', padding: '6px 10px'
|
135 |
+
});
|
136 |
+
labelAlpha.appendChild(document.createTextNode('Augmentation α'));
|
137 |
+
const slider = document.createElement('input');
|
138 |
+
slider.type = 'range'; slider.min = '0'; slider.max = '1'; slider.step = '0.01'; slider.value = '0.70';
|
139 |
+
Object.assign(slider.style, { flex: '1' });
|
140 |
+
const alphaVal = document.createElement('span'); alphaVal.className = 'alpha-value'; alphaVal.textContent = slider.value;
|
141 |
+
labelAlpha.appendChild(slider);
|
142 |
+
labelAlpha.appendChild(alphaVal);
|
143 |
+
|
144 |
+
controls.appendChild(labelDs);
|
145 |
+
controls.appendChild(labelAlpha);
|
146 |
+
|
147 |
+
// Create SVG
|
148 |
+
const svg = d3.select(container).append('svg')
|
149 |
+
.attr('width', '100%')
|
150 |
+
.style('display', 'block');
|
151 |
+
|
152 |
+
// Groups
|
153 |
+
const gRoot = svg.append('g');
|
154 |
+
const gGrid = gRoot.append('g').attr('class', 'grid');
|
155 |
+
const gAxes = gRoot.append('g').attr('class', 'axes');
|
156 |
+
const gLines = gRoot.append('g').attr('class', 'lines');
|
157 |
+
const gHover = gRoot.append('g').attr('class', 'hover');
|
158 |
+
const gLegend = gRoot.append('foreignObject').attr('class', 'legend');
|
159 |
+
|
160 |
+
// Tooltip
|
161 |
+
container.style.position = container.style.position || 'relative';
|
162 |
+
let tip = container.querySelector('.d3-tooltip');
|
163 |
+
let tipInner;
|
164 |
+
if (!tip) {
|
165 |
+
tip = document.createElement('div');
|
166 |
+
tip.className = 'd3-tooltip';
|
167 |
+
Object.assign(tip.style, {
|
168 |
+
position: 'absolute', top: '0px', left: '0px', transform: 'translate(-9999px, -9999px)', pointerEvents: 'none',
|
169 |
+
padding: '8px 10px', borderRadius: '8px', fontSize: '12px', lineHeight: '1.35', border: '1px solid var(--border-color)',
|
170 |
+
background: 'var(--surface-bg)', color: 'var(--text-color)', boxShadow: '0 4px 24px rgba(0,0,0,.18)', opacity: '0',
|
171 |
+
transition: 'opacity .12s ease'
|
172 |
+
});
|
173 |
+
tipInner = document.createElement('div');
|
174 |
+
tipInner.className = 'd3-tooltip__inner';
|
175 |
+
tipInner.style.textAlign = 'left';
|
176 |
+
tip.appendChild(tipInner);
|
177 |
+
container.appendChild(tip);
|
178 |
+
} else {
|
179 |
+
tipInner = tip.querySelector('.d3-tooltip__inner') || tip;
|
180 |
+
}
|
181 |
+
|
182 |
+
// Colors
|
183 |
+
const colorBase = '#64748b'; // slate-500
|
184 |
+
const colorImproved = 'var(--primary)';
|
185 |
+
const colorTarget = '#4b5563'; // gray-600
|
186 |
+
const legendBgLight = 'rgba(255,255,255,0.85)';
|
187 |
+
const legendBgDark = 'rgba(17,17,23,0.85)';
|
188 |
+
|
189 |
+
// Data and helpers
|
190 |
+
const N = 240;
|
191 |
+
const xs = Array.from({ length: N }, (_, i) => i / (N - 1));
|
192 |
+
const logistic = (x, { ymin, ymax, k, x0 }) => ymin + (ymax - ymin) / (1 + Math.exp(-k * (x - x0)));
|
193 |
+
const blend = (l, e, a) => (1 - a) * l + a * e;
|
194 |
+
|
195 |
+
let datasetIndex = 0;
|
196 |
+
let alpha = parseFloat(slider.value) || 0.7;
|
197 |
+
|
198 |
+
let yBase = [];
|
199 |
+
let yAug = [];
|
200 |
+
let yImp = [];
|
201 |
+
let yTgt = [];
|
202 |
+
|
203 |
+
function computeCurves() {
|
204 |
+
const d = datasets[datasetIndex];
|
205 |
+
yBase = xs.map((x) => logistic(x, d.base));
|
206 |
+
yAug = xs.map((x) => logistic(x, d.aug));
|
207 |
+
yTgt = xs.map(() => d.target);
|
208 |
+
yImp = yBase.map((v, i) => blend(v, yAug[i], alpha));
|
209 |
+
}
|
210 |
+
|
211 |
+
// Scales and layout
|
212 |
+
let width = 800, height = 360;
|
213 |
+
let margin = { top: 16, right: 28, bottom: 56, left: 64 };
|
214 |
+
let xScale = d3.scaleLinear();
|
215 |
+
let yScale = d3.scaleLinear();
|
216 |
+
|
217 |
+
// Paths
|
218 |
+
const lineGen = d3.line()
|
219 |
+
.curve(d3.curveCatmullRom.alpha(0.6))
|
220 |
+
.x((d, i) => xScale(xs[i]))
|
221 |
+
.y((d) => yScale(d));
|
222 |
+
|
223 |
+
const pathBase = gLines.append('path').attr('fill', 'none').attr('stroke', colorBase).attr('stroke-width', 2);
|
224 |
+
const pathImp = gLines.append('path').attr('class', 'improved').attr('fill', 'none').style('stroke', 'var(--primary)').attr('stroke-width', 2);
|
225 |
+
const pathTgt = gLines.append('path').attr('fill', 'none').attr('stroke', colorTarget).attr('stroke-width', 2).attr('stroke-dasharray', '6,6');
|
226 |
+
|
227 |
+
// Hover elements
|
228 |
+
const hoverLine = gHover.append('line').attr('stroke-width', 1);
|
229 |
+
const hoverDotB = gHover.append('circle').attr('r', 3.5).attr('fill', colorBase).attr('stroke', '#fff').attr('stroke-width', 1);
|
230 |
+
const hoverDotI = gHover.append('circle').attr('class', 'improved').attr('r', 3.5).style('fill', 'var(--primary)').attr('stroke', '#fff').attr('stroke-width', 1);
|
231 |
+
const hoverDotT = gHover.append('circle').attr('r', 3.5).attr('fill', colorTarget).attr('stroke', '#fff').attr('stroke-width', 1);
|
232 |
+
|
233 |
+
const overlay = gHover.append('rect').attr('fill', 'transparent').style('cursor', 'crosshair');
|
234 |
+
|
235 |
+
function updateScales() {
|
236 |
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
237 |
+
const axisColor = isDark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)';
|
238 |
+
const tickColor = isDark ? 'rgba(255,255,255,0.70)' : 'rgba(0,0,0,0.55)';
|
239 |
+
const gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)';
|
240 |
+
|
241 |
+
width = container.clientWidth || 800;
|
242 |
+
height = Math.max(260, Math.round(width / 3));
|
243 |
+
svg.attr('width', width).attr('height', height);
|
244 |
+
|
245 |
+
const innerWidth = width - margin.left - margin.right;
|
246 |
+
const innerHeight = height - margin.top - margin.bottom;
|
247 |
+
gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
|
248 |
+
|
249 |
+
xScale.domain([0, 1]).range([0, innerWidth]);
|
250 |
+
yScale.domain([0, 1]).range([innerHeight, 0]);
|
251 |
+
|
252 |
+
// Grid (horizontal)
|
253 |
+
gGrid.selectAll('*').remove();
|
254 |
+
const yTicks = yScale.ticks(6);
|
255 |
+
gGrid.selectAll('line')
|
256 |
+
.data(yTicks)
|
257 |
+
.join('line')
|
258 |
+
.attr('x1', 0)
|
259 |
+
.attr('x2', innerWidth)
|
260 |
+
.attr('y1', (d) => yScale(d))
|
261 |
+
.attr('y2', (d) => yScale(d))
|
262 |
+
.attr('stroke', gridColor)
|
263 |
+
.attr('stroke-width', 1)
|
264 |
+
.attr('shape-rendering', 'crispEdges');
|
265 |
+
|
266 |
+
// Axes
|
267 |
+
gAxes.selectAll('*').remove();
|
268 |
+
const xAxis = d3.axisBottom(xScale).ticks(8).tickSizeOuter(0);
|
269 |
+
const yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0).tickFormat(d3.format('.2f'));
|
270 |
+
gAxes.append('g')
|
271 |
+
.attr('transform', `translate(0,${innerHeight})`)
|
272 |
+
.call(xAxis)
|
273 |
+
.call((g) => {
|
274 |
+
g.selectAll('path, line').attr('stroke', axisColor);
|
275 |
+
g.selectAll('text').attr('fill', tickColor).style('font-size', '12px');
|
276 |
+
});
|
277 |
+
gAxes.append('g')
|
278 |
+
.call(yAxis)
|
279 |
+
.call((g) => {
|
280 |
+
g.selectAll('path, line').attr('stroke', axisColor);
|
281 |
+
g.selectAll('text').attr('fill', tickColor).style('font-size', '12px');
|
282 |
+
});
|
283 |
+
|
284 |
+
// Axis labels (X and Y)
|
285 |
+
gAxes.append('text')
|
286 |
+
.attr('class', 'axis-label axis-label--x')
|
287 |
+
.attr('x', innerWidth / 2)
|
288 |
+
.attr('y', innerHeight + 44)
|
289 |
+
.attr('text-anchor', 'middle')
|
290 |
+
.style('font-size', '12px')
|
291 |
+
.style('fill', tickColor)
|
292 |
+
.text('Epoch');
|
293 |
+
gAxes.append('text')
|
294 |
+
.attr('class', 'axis-label axis-label--y')
|
295 |
+
.attr('text-anchor', 'middle')
|
296 |
+
.attr('transform', `translate(${-52},${innerHeight/2}) rotate(-90)`)
|
297 |
+
.style('font-size', '12px')
|
298 |
+
.style('fill', tickColor)
|
299 |
+
.text('Accuracy');
|
300 |
+
|
301 |
+
overlay.attr('x', 0).attr('y', 0).attr('width', innerWidth).attr('height', innerHeight);
|
302 |
+
hoverLine.attr('y1', 0).attr('y2', innerHeight).attr('stroke', axisColor);
|
303 |
+
|
304 |
+
// Legend inside plot (bottom-right), no background/border/shadow
|
305 |
+
const legendWidth = Math.min(180, Math.max(120, Math.round(innerWidth * 0.22)));
|
306 |
+
const legendHeight = 64;
|
307 |
+
gLegend
|
308 |
+
.attr('x', innerWidth - legendWidth + 42)
|
309 |
+
.attr('y', innerHeight - legendHeight - 12)
|
310 |
+
.attr('width', legendWidth)
|
311 |
+
.attr('height', legendHeight);
|
312 |
+
const legendRoot = gLegend.selectAll('div').data([0]).join('xhtml:div');
|
313 |
+
Object.assign(legendRoot.node().style, {
|
314 |
+
background: 'transparent',
|
315 |
+
border: 'none',
|
316 |
+
borderRadius: '0',
|
317 |
+
padding: '0',
|
318 |
+
fontSize: '12px',
|
319 |
+
lineHeight: '1.35',
|
320 |
+
color: 'var(--text-color)'
|
321 |
+
});
|
322 |
+
legendRoot.html(`
|
323 |
+
<div style="display:flex;flex-direction:column;gap:6px;">
|
324 |
+
<div style="display:flex;align-items:center;gap:8px;">
|
325 |
+
<span style="width:18px;height:3px;background:${colorBase};border-radius:2px;display:inline-block"></span>
|
326 |
+
<span>Baseline</span>
|
327 |
+
</div>
|
328 |
+
<div style="display:flex;align-items:center;gap:8px;">
|
329 |
+
<span style="width:18px;height:3px;background:${colorImproved};border-radius:2px;display:inline-block"></span>
|
330 |
+
<span>Improved</span>
|
331 |
+
</div>
|
332 |
+
<div style="display:flex;align-items:center;gap:8px;">
|
333 |
+
<span style="width:18px;height:0;border-top:2px dashed ${colorTarget};display:inline-block"></span>
|
334 |
+
<span>Target</span>
|
335 |
+
</div>
|
336 |
+
</div>
|
337 |
+
`);
|
338 |
+
}
|
339 |
+
|
340 |
+
function updatePaths() {
|
341 |
+
pathBase.transition().duration(200).attr('d', lineGen(yBase));
|
342 |
+
pathImp.transition().duration(200).attr('d', lineGen(yImp));
|
343 |
+
pathTgt.transition().duration(200).attr('d', lineGen(yTgt));
|
344 |
+
}
|
345 |
+
|
346 |
+
function updateAlpha(a) {
|
347 |
+
alpha = a;
|
348 |
+
alphaVal.textContent = a.toFixed(2);
|
349 |
+
yImp = yBase.map((v, i) => blend(v, yAug[i], alpha));
|
350 |
+
pathImp.transition().duration(80).attr('d', lineGen(yImp));
|
351 |
+
}
|
352 |
+
|
353 |
+
function applyDataset() {
|
354 |
+
computeCurves();
|
355 |
+
updatePaths();
|
356 |
+
}
|
357 |
+
|
358 |
+
// Hover interactions
|
359 |
+
function onMove(event) {
|
360 |
+
const [mx, my] = d3.pointer(event, overlay.node());
|
361 |
+
const xi = Math.max(0, Math.min(N - 1, Math.round(xScale.invert(mx) * (N - 1))));
|
362 |
+
const xpx = xScale(xs[xi]);
|
363 |
+
const yb = yBase[xi], yi = yImp[xi], yt = yTgt[xi];
|
364 |
+
hoverLine.attr('x1', xpx).attr('x2', xpx).style('display', null);
|
365 |
+
hoverDotB.attr('cx', xpx).attr('cy', yScale(yb)).style('display', null);
|
366 |
+
hoverDotI.attr('cx', xpx).attr('cy', yScale(yi)).style('display', null);
|
367 |
+
hoverDotT.attr('cx', xpx).attr('cy', yScale(yt)).style('display', null);
|
368 |
+
|
369 |
+
// Tooltip content
|
370 |
+
const ds = datasets[datasetIndex].name;
|
371 |
+
tipInner.innerHTML = `<div><strong>${ds}</strong></div>` +
|
372 |
+
`<div><strong>x</strong> ${xs[xi].toFixed(2)}</div>` +
|
373 |
+
`<div><span style="display:inline-block;width:10px;height:10px;background:${colorBase};border-radius:50%;margin-right:6px;"></span><strong>Baseline</strong> ${yb.toFixed(3)}</div>` +
|
374 |
+
`<div><span style="display:inline-block;width:10px;height:10px;background:${colorImproved};border-radius:50%;margin-right:6px;"></span><strong>Improved</strong> ${yi.toFixed(3)}</div>` +
|
375 |
+
`<div><span style="display:inline-block;width:10px;height:10px;background:${colorTarget};border-radius:50%;margin-right:6px;"></span><strong>Target</strong> ${yt.toFixed(3)}</div>`;
|
376 |
+
const offsetX = 12, offsetY = 12;
|
377 |
+
tip.style.opacity = '1';
|
378 |
+
tip.style.transform = `translate(${Math.round(mx + offsetX + margin.left)}px, ${Math.round(my + offsetY + margin.top)}px)`;
|
379 |
+
}
|
380 |
+
|
381 |
+
function onLeave() {
|
382 |
+
tip.style.opacity = '0';
|
383 |
+
tip.style.transform = 'translate(-9999px, -9999px)';
|
384 |
+
hoverLine.style('display', 'none');
|
385 |
+
hoverDotB.style('display', 'none');
|
386 |
+
hoverDotI.style('display', 'none');
|
387 |
+
hoverDotT.style('display', 'none');
|
388 |
+
}
|
389 |
+
|
390 |
+
overlay.on('mousemove', onMove).on('mouseleave', onLeave);
|
391 |
+
|
392 |
+
// Init + controls wiring
|
393 |
+
computeCurves();
|
394 |
+
updateScales();
|
395 |
+
updatePaths();
|
396 |
+
|
397 |
+
// Attach controls after SVG for consistency with Plotly fragment
|
398 |
+
container.appendChild(controls);
|
399 |
+
|
400 |
+
selectDs.addEventListener('change', (e) => {
|
401 |
+
datasetIndex = parseInt(e.target.value) || 0;
|
402 |
+
applyDataset();
|
403 |
+
});
|
404 |
+
slider.addEventListener('input', (e) => {
|
405 |
+
const a = parseFloat(e.target.value) || 0;
|
406 |
+
updateAlpha(a);
|
407 |
+
});
|
408 |
+
|
409 |
+
// Resize handling
|
410 |
+
const render = () => {
|
411 |
+
updateScales();
|
412 |
+
updatePaths();
|
413 |
+
};
|
414 |
+
if (window.ResizeObserver) {
|
415 |
+
const ro = new ResizeObserver(() => render());
|
416 |
+
ro.observe(container);
|
417 |
+
} else {
|
418 |
+
window.addEventListener('resize', render);
|
419 |
+
}
|
420 |
+
render();
|
421 |
+
};
|
422 |
+
|
423 |
+
if (document.readyState === 'loading') {
|
424 |
+
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
|
425 |
+
} else { ensureD3(bootstrap); }
|
426 |
+
})();
|
427 |
+
</script>
|
428 |
+
|
429 |
+
|
app/src/content/fragments/heatmap.html
CHANGED
@@ -1 +1,159 @@
|
|
1 |
-
<div> <div id="47076730-9b36-4ef3-9cd9-80fa6f5baf65" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("47076730-9b36-4ef3-9cd9-80fa6f5baf65")) { Plotly.newPlot( "47076730-9b36-4ef3-9cd9-80fa6f5baf65", [{"colorscale":[[0.0,"#e5e7eb"],[0.4,"#64748b"],[0.75,"#2563eb"],[1.0,"#4b5563"]],"customdata":[["2024-09-02","2024-09-09","2024-09-16","2024-09-23","2024-09-30","2024-10-07","2024-10-14","2024-10-21","2024-10-28","2024-11-04","2024-11-11","2024-11-18","2024-11-25","2024-12-02","2024-12-09","2024-12-16","2024-12-23","2024-12-30","2025-01-06","2025-01-13","2025-01-20","2025-01-27","2025-02-03","2025-02-10","2025-02-17","2025-02-24","2025-03-03","2025-03-10","2025-03-17","2025-03-24","2025-03-31","2025-04-07","2025-04-14","2025-04-21","2025-04-28","2025-05-05","2025-05-12","2025-05-19","2025-05-26","2025-06-02","2025-06-09","2025-06-16","2025-06-23","2025-06-30","2025-07-07","2025-07-14","2025-07-21","2025-07-28","2025-08-04","2025-08-11","2025-08-18","2025-08-25"],["2024-09-03","2024-09-10","2024-09-17","2024-09-24","2024-10-01","2024-10-08","2024-10-15","2024-10-22","2024-10-29","2024-11-05","2024-11-12","2024-11-19","2024-11-26","2024-12-03","2024-12-10","2024-12-17","2024-12-24","2024-12-31","2025-01-07","2025-01-14","2025-01-21","2025-01-28","2025-02-04","2025-02-11","2025-02-18","2025-02-25","2025-03-04","2025-03-11","2025-03-18","2025-03-25","2025-04-01","2025-04-08","2025-04-15","2025-04-22","2025-04-29","2025-05-06","2025-05-13","2025-05-20","2025-05-27","2025-06-03","2025-06-10","2025-06-17","2025-06-24","2025-07-01","2025-07-08","2025-07-15","2025-07-22","2025-07-29","2025-08-05","2025-08-12","2025-08-19","2025-08-26"],["2024-09-04","2024-09-11","2024-09-18","2024-09-25","2024-10-02","2024-10-09","2024-10-16","2024-10-23","2024-10-30","2024-11-06","2024-11-13","2024-11-20","2024-11-27","2024-12-04","2024-12-11","2024-12-18","2024-12-25","2025-01-01","2025-01-08","2025-01-15","2025-01-22","2025-01-29","2025-02-05","2025-02-12","2025-02-19","2025-02-26","2025-03-05","2025-03-12","2025-03-19","2025-03-26","2025-04-02","2025-04-09","2025-04-16","2025-04-23","2025-04-30","2025-05-07","2025-05-14","2025-05-21","2025-05-28","2025-06-04","2025-06-11","2025-06-18","2025-06-25","2025-07-02","2025-07-09","2025-07-16","2025-07-23","2025-07-30","2025-08-06","2025-08-13","2025-08-20","2025-08-27"],["2024-09-05","2024-09-12","2024-09-19","2024-09-26","2024-10-03","2024-10-10","2024-10-17","2024-10-24","2024-10-31","2024-11-07","2024-11-14","2024-11-21","2024-11-28","2024-12-05","2024-12-12","2024-12-19","2024-12-26","2025-01-02","2025-01-09","2025-01-16","2025-01-23","2025-01-30","2025-02-06","2025-02-13","2025-02-20","2025-02-27","2025-03-06","2025-03-13","2025-03-20","2025-03-27","2025-04-03","2025-04-10","2025-04-17","2025-04-24","2025-05-01","2025-05-08","2025-05-15","2025-05-22","2025-05-29","2025-06-05","2025-06-12","2025-06-19","2025-06-26","2025-07-03","2025-07-10","2025-07-17","2025-07-24","2025-07-31","2025-08-07","2025-08-14","2025-08-21","2025-08-28"],["2024-09-06","2024-09-13","2024-09-20","2024-09-27","2024-10-04","2024-10-11","2024-10-18","2024-10-25","2024-11-01","2024-11-08","2024-11-15","2024-11-22","2024-11-29","2024-12-06","2024-12-13","2024-12-20","2024-12-27","2025-01-03","2025-01-10","2025-01-17","2025-01-24","2025-01-31","2025-02-07","2025-02-14","2025-02-21","2025-02-28","2025-03-07","2025-03-14","2025-03-21","2025-03-28","2025-04-04","2025-04-11","2025-04-18","2025-04-25","2025-05-02","2025-05-09","2025-05-16","2025-05-23","2025-05-30","2025-06-06","2025-06-13","2025-06-20","2025-06-27","2025-07-04","2025-07-11","2025-07-18","2025-07-25","2025-08-01","2025-08-08","2025-08-15","2025-08-22","2025-08-29"],["2024-09-07","2024-09-14","2024-09-21","2024-09-28","2024-10-05","2024-10-12","2024-10-19","2024-10-26","2024-11-02","2024-11-09","2024-11-16","2024-11-23","2024-11-30","2024-12-07","2024-12-14","2024-12-21","2024-12-28","2025-01-04","2025-01-11","2025-01-18","2025-01-25","2025-02-01","2025-02-08","2025-02-15","2025-02-22","2025-03-01","2025-03-08","2025-03-15","2025-03-22","2025-03-29","2025-04-05","2025-04-12","2025-04-19","2025-04-26","2025-05-03","2025-05-10","2025-05-17","2025-05-24","2025-05-31","2025-06-07","2025-06-14","2025-06-21","2025-06-28","2025-07-05","2025-07-12","2025-07-19","2025-07-26","2025-08-02","2025-08-09","2025-08-16","2025-08-23","2025-08-30"],["2024-09-08","2024-09-15","2024-09-22","2024-09-29","2024-10-06","2024-10-13","2024-10-20","2024-10-27","2024-11-03","2024-11-10","2024-11-17","2024-11-24","2024-12-01","2024-12-08","2024-12-15","2024-12-22","2024-12-29","2025-01-05","2025-01-12","2025-01-19","2025-01-26","2025-02-02","2025-02-09","2025-02-16","2025-02-23","2025-03-02","2025-03-09","2025-03-16","2025-03-23","2025-03-30","2025-04-06","2025-04-13","2025-04-20","2025-04-27","2025-05-04","2025-05-11","2025-05-18","2025-05-25","2025-06-01","2025-06-08","2025-06-15","2025-06-22","2025-06-29","2025-07-06","2025-07-13","2025-07-20","2025-07-27","2025-08-03","2025-08-10","2025-08-17","2025-08-24","2025-08-31"]],"hovertemplate":"Date: %{customdata}\u003cbr\u003eValue: %{z:.2f}\u003cextra\u003e\u003c\u002fextra\u003e","showscale":false,"x":["2024-09-02","2024-09-09","2024-09-16","2024-09-23","2024-09-30","2024-10-07","2024-10-14","2024-10-21","2024-10-28","2024-11-04","2024-11-11","2024-11-18","2024-11-25","2024-12-02","2024-12-09","2024-12-16","2024-12-23","2024-12-30","2025-01-06","2025-01-13","2025-01-20","2025-01-27","2025-02-03","2025-02-10","2025-02-17","2025-02-24","2025-03-03","2025-03-10","2025-03-17","2025-03-24","2025-03-31","2025-04-07","2025-04-14","2025-04-21","2025-04-28","2025-05-05","2025-05-12","2025-05-19","2025-05-26","2025-06-02","2025-06-09","2025-06-16","2025-06-23","2025-06-30","2025-07-07","2025-07-14","2025-07-21","2025-07-28","2025-08-04","2025-08-11","2025-08-18","2025-08-25"],"xgap":2,"y":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],"ygap":2,"z":[[0.13459901272105057,0.07144037267686924,0.0,0.02567169542484793,0.1943701957602955,0.0,0.10315424053432098,0.2223869321144732,0.0101747131633462,0.04624564471870697,0.1866248629409762,0.1619588952957143,0.10179656128853301,0.28127388190834635,0.24363532332682114,0.38314067714644795,0.3904396470156554,0.5871417623089922,0.4494512420063476,0.7075867892330924,0.5854536843986621,0.6749419514555224,0.7305655930672372,0.897409823215684,0.8918983512872566,0.9209354080279966,0.9308865657264072,0.8059584841797863,1.0,0.898048571887148,1.0,1.0,0.8590184058919965,1.0,1.0,0.7732387178889825,0.8912170907522784,0.8184163384586197,0.822475204602678,0.7642543483018442,0.5431644942315057,0.6355115517973967,0.7015969114652315,0.5460237405196436,0.4879670174082411,0.4675485058035454,0.42809456698479686,0.2911841791176736,0.1481675433317543,0.13923730763227324,0.1766461423778808,0.1699281866099175],[0.0,0.0,0.0017899698622748927,0.0,0.09677158638116767,0.12479477625677105,0.10895777790688227,0.15189044557185855,0.09654580653607348,0.1795492865957899,0.3052268504010917,0.32596436590345157,0.18411123726736778,0.35977527304759266,0.35252652681600993,0.5427417269194631,0.5651562981702217,0.47750647994960754,0.4561350741573436,0.7417137309609516,0.5102953771880667,0.8241126373750014,0.7797236509747064,0.9332037061153636,0.9824394294545304,0.8289995839250157,1.0,0.7913308880046568,0.8189055068633593,1.0,0.8223965509972967,0.8348974442606938,0.9564174304426082,0.8985185636493014,1.0,0.8604991326856607,0.9758735705615857,0.6558932159815902,0.7000147719646015,0.5799408830216487,0.7035151530934022,0.7478886919490498,0.6337798522620632,0.4941685362991634,0.4092062677590119,0.4232057215920778,0.2300237268973226,0.3526820715419722,0.3679596483947415,0.10802723932902636,0.25227992652493664,0.13091171870017465],[0.18654843761315884,0.02424509841700278,0.0,0.03388313524346659,0.0,0.12125378247595983,0.0,0.0,0.13088917701073183,0.16910681233669253,0.10310100475666693,0.20052186435225497,0.3816697335862501,0.22282049255664288,0.28520333869041986,0.40340313228078245,0.3690761124240207,0.6115883706700836,0.47033965930665544,0.6007036424693509,0.7598327462235945,0.5773116058876002,0.6237534811100747,0.8691407726978946,0.7375919944566651,0.941330162011735,0.8014934674635664,0.8458330455003304,0.9611035861419381,0.8463915521310343,1.0,1.0,1.0,1.0,1.0,0.7932695891537325,0.9596482462801176,0.915947343178816,0.6136511921161285,0.5624943289814737,0.5541252690852431,0.5344741327070746,0.5169619710495121,0.46374855539511284,0.47539824510653705,0.24291414204185113,0.2543250806739876,0.31026724130890454,0.16093385741803784,0.17923375751178566,0.28907858857606483,0.09118081773759709],[0.04274285618353736,0.16773213722549252,0.0,0.027348236792497493,0.19678388324323698,0.0,0.0,0.0,0.0,0.0067895255930027765,0.2289109950080088,0.12593408541693593,0.11541624142573209,0.17043612043585676,0.4096860328972915,0.5574474536390003,0.484527060997471,0.3920676804861751,0.5784662869674918,0.6995971109532144,0.6467757589136034,0.8109481639584317,0.7425226445007554,0.7478239359283666,0.9666926185706564,0.9587154359758231,0.7863943022510236,0.7986686327668135,1.0,1.0,1.0,1.0,0.8091532345633923,0.9558433252238909,0.8031303542172052,0.8725859202971921,0.7566835148011993,0.7778067221045616,0.8077718658444186,0.7050984538121973,0.6791435853965506,0.7315047589823398,0.6274606401446008,0.6090447486281656,0.47565459019472395,0.3626444151812243,0.3781788689164846,0.1428027187105927,0.13024918340438868,0.24538136440926647,0.25413551726238914,0.2638681222155129],[0.16631123049192714,0.21393461748259215,0.17443172320155878,0.16030598440664023,0.008372431838735095,0.048590697685584064,0.12304248758475622,0.05962196841028138,0.04021069416966182,0.19139690202159018,0.19091509263961848,0.33755767564372935,0.26222784209855,0.3836164558963834,0.4951442089456719,0.42861473393542787,0.6191816151803766,0.6337521044168415,0.5006396943655522,0.613622934507656,0.5840928534076,0.6852784052867706,0.7425115550483108,0.7805104874379335,0.8068752071639289,0.8268975901194805,0.8083965524738981,0.970997420540351,1.0,0.835199310773221,0.8451380333804511,0.8935781712143693,0.9260453704963016,1.0,0.8279150004605469,1.0,0.8523959398966388,0.7719099661835359,0.881081537171595,0.6749144766088301,0.6107016930483002,0.6206214151930902,0.41410447536673967,0.5191072714001534,0.5213551688304456,0.4247676354556676,0.4396179366988354,0.368866379134083,0.16339728503322348,0.1173613402559649,0.09602005004734745,0.07480784435331583],[0.046590987334549576,0.10123262009753725,0.02093213312475304,0.0,0.07125379358885636,0.11217969099637068,0.0,0.04223494562109133,0.0,0.2407179325890179,0.10427870014018772,0.16707365585204992,0.1793360141618333,0.44441581083681436,0.5015112790992493,0.324885132203605,0.4145937619787646,0.5945489220423629,0.49092001333284074,0.7608613987453299,0.62750715549575,0.8547015477375716,0.8346531778992586,0.9684788887645426,1.0,0.8136483326914014,0.807430828663213,0.9557997633645948,0.9341359236245382,0.8658706292175599,0.7999512579226341,0.8315899109479433,1.0,0.7834447149945696,0.821791956125248,0.8837193194656598,0.7717628566232109,0.800652279919202,0.6248520681660523,0.7382895308902923,0.6921482346155182,0.5893888952931976,0.4496816205443064,0.38499598068827606,0.28076008234368877,0.2359389188931954,0.21120429234963584,0.21097085986745998,0.3105027222666124,0.15148478367041826,0.1346761632737261,0.012262819186755189],[0.009666345383968863,0.11116647068498314,0.09294877334825495,0.0,0.0,0.2011845639304187,0.1746125923167111,0.16006896670795023,0.0,0.28572584519939537,0.2106947405688844,0.24717486234760797,0.28443580982016986,0.46446650959504143,0.331714655444355,0.43317910390302933,0.5322084048261323,0.6040016380656543,0.5272867409409122,0.6326146082611722,0.7481764440823108,0.6032879101358065,0.8983906824318192,0.8272461712741,0.7697692673827646,0.7968738421076039,0.8931913970024197,0.9329148302840636,0.904554090664641,0.8543001086485396,0.8838775248978465,0.8560252873023545,0.8122001360665569,1.0,0.7616937704042586,0.9029733179243924,0.8776848090469377,0.9202037297571337,0.6884370069119261,0.8140845758855468,0.7631365695720691,0.5410474299147227,0.6587768738699801,0.5594767226752763,0.39462779575958423,0.4973245647686208,0.23374565469040598,0.14285601938377673,0.0893905690513131,0.320572013689418,0.16675392505723788,0.039142897242472996]],"type":"heatmap"}], {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"margin":{"l":28,"r":12,"t":8,"b":28},"xaxis":{"showgrid":false,"zeroline":false,"showline":false,"ticks":"","showticklabels":false,"fixedrange":true},"yaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.65)"},"showgrid":false,"zeroline":false,"showline":false,"ticks":"","fixedrange":true},"autosize":true,"paper_bgcolor":"rgba(0,0,0,0)","plot_bgcolor":"rgba(0,0,0,0)"}, {"displayModeBar": false, "responsive": true, "scrollZoom": false} ) }; </script> </div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="d3-train-diagram" style="width:100%;margin:10px 0;"></div>
|
2 |
+
<div class="caption">Survolez les blocs pour afficher une explication.</div>
|
3 |
+
<style>
|
4 |
+
.d3-train-diagram + .caption { margin-top: 8px; font-size: 14px; color: var(--muted-color); }
|
5 |
+
</style>
|
6 |
+
<script>
|
7 |
+
(() => {
|
8 |
+
const ensureD3 = (cb) => {
|
9 |
+
if (window.d3 && typeof window.d3.select === 'function') return cb();
|
10 |
+
let s = document.getElementById('d3-cdn-script');
|
11 |
+
if (!s) { s = document.createElement('script'); s.id = 'd3-cdn-script'; s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js'; document.head.appendChild(s); }
|
12 |
+
const onReady = () => { if (window.d3 && typeof window.d3.select === 'function') cb(); };
|
13 |
+
s.addEventListener('load', onReady, { once: true });
|
14 |
+
if (window.d3) onReady();
|
15 |
+
};
|
16 |
+
|
17 |
+
const bootstrap = () => {
|
18 |
+
const mount = document.currentScript ? document.currentScript.previousElementSibling : null;
|
19 |
+
const container = (mount && mount.querySelector && mount.querySelector('.d3-train-diagram')) || document.querySelector('.d3-train-diagram');
|
20 |
+
if (!container) return;
|
21 |
+
if (container.dataset) { if (container.dataset.mounted === 'true') return; container.dataset.mounted = 'true'; }
|
22 |
+
|
23 |
+
// Diagram spec
|
24 |
+
const numBlocks = 7;
|
25 |
+
const rows = [
|
26 |
+
{ key: 'model', label: 'Model', color: '#a78bfa' },
|
27 |
+
{ key: 'forward', label: 'Forward', color: '#14b8a6' },
|
28 |
+
{ key: 'backward', label: 'Backward', color: '#f59e0b' },
|
29 |
+
{ key: 'gradients', label: 'Gradients', color: 'var(--primary)' },
|
30 |
+
{ key: 'optimization', label: 'Optimization', color: '#10b981' },
|
31 |
+
{ key: 'updated', label: 'Updated', color: '#7c3aed' },
|
32 |
+
];
|
33 |
+
const hoverText = {
|
34 |
+
model: 'Chaque bloc représente un sous-module du modèle.',
|
35 |
+
forward: 'Propagation avant: calcul des activations couche par couche.',
|
36 |
+
backward: 'Rétropropagation: calcul des gradients via la chaîne.',
|
37 |
+
gradients: 'Accumulateurs de gradients pour chaque couche.',
|
38 |
+
optimization: 'Étape d’optimisation: mise à jour des poids.',
|
39 |
+
updated: 'Paramètres mis à jour, prêts pour l’itération suivante.'
|
40 |
+
};
|
41 |
+
|
42 |
+
// SVG
|
43 |
+
const svg = d3.select(container).append('svg').attr('width', '100%').style('display','block');
|
44 |
+
const gRoot = svg.append('g');
|
45 |
+
const gLegend = gRoot.append('foreignObject').attr('class','legend');
|
46 |
+
const gArrows = gRoot.append('g').attr('class','arrows');
|
47 |
+
const gBlocks = gRoot.append('g').attr('class','blocks');
|
48 |
+
const gLabels = gRoot.append('g').attr('class','row-labels');
|
49 |
+
|
50 |
+
// Tooltip (reuse style from others)
|
51 |
+
container.style.position = container.style.position || 'relative';
|
52 |
+
let tip = container.querySelector('.d3-tooltip'); let tipInner;
|
53 |
+
if (!tip) { tip = document.createElement('div'); tip.className = 'd3-tooltip'; Object.assign(tip.style,{ position:'absolute', top:'0px', left:'0px', transform:'translate(-9999px, -9999px)', pointerEvents:'none', padding:'8px 10px', borderRadius:'8px', fontSize:'12px', lineHeight:'1.35', border:'1px solid var(--border-color)', background:'var(--surface-bg)', color:'var(--text-color)', boxShadow:'0 4px 24px rgba(0,0,0,.18)', opacity:'0', transition:'opacity .12s ease' }); tipInner = document.createElement('div'); tipInner.className = 'd3-tooltip__inner'; tipInner.style.textAlign='left'; tip.appendChild(tipInner); container.appendChild(tip); } else { tipInner = tip.querySelector('.d3-tooltip__inner') || tip; }
|
54 |
+
|
55 |
+
// Layout
|
56 |
+
let width=800, height=360; const margin = { top: 24, right: 180, bottom: 40, left: 32 };
|
57 |
+
const x = d3.scaleBand().domain(d3.range(numBlocks)).paddingInner(0.2).paddingOuter(0.05);
|
58 |
+
const y = d3.scaleBand().domain(d3.range(rows.length)).paddingInner(0.35);
|
59 |
+
|
60 |
+
function updateScales(){
|
61 |
+
width = container.clientWidth || 800;
|
62 |
+
const rowH = Math.max(54, Math.min(80, Math.round(width / 12)));
|
63 |
+
const innerHeight = rows.length * rowH;
|
64 |
+
height = innerHeight + margin.top + margin.bottom;
|
65 |
+
svg.attr('width', width).attr('height', height);
|
66 |
+
const innerWidth = width - margin.left - margin.right;
|
67 |
+
gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
|
68 |
+
|
69 |
+
x.range([0, innerWidth]);
|
70 |
+
y.range([0, innerHeight]);
|
71 |
+
|
72 |
+
return { innerWidth, innerHeight };
|
73 |
+
}
|
74 |
+
|
75 |
+
function render(){
|
76 |
+
const { innerWidth, innerHeight } = updateScales();
|
77 |
+
|
78 |
+
// Legend right side
|
79 |
+
const legendWidth = 160, legendHeight = rows.length * 20;
|
80 |
+
gLegend.attr('x', innerWidth + 16).attr('y', 0).attr('width', legendWidth).attr('height', legendHeight);
|
81 |
+
const lroot = gLegend.selectAll('div').data([0]).join('xhtml:div');
|
82 |
+
lroot.html(`
|
83 |
+
<div style="display:flex;flex-direction:column;gap:8px;">
|
84 |
+
${rows.map(r => `<div style=\"display:flex;align-items:center;gap:8px;\"><span style=\"width:14px;height:14px;background:${r.color};border-radius:4px;display:inline-block\"></span><span>${r.label}</span></div>`).join('')}
|
85 |
+
</div>
|
86 |
+
`);
|
87 |
+
|
88 |
+
// Row labels on the right side aligned to centers
|
89 |
+
gLabels.selectAll('*').remove();
|
90 |
+
gLabels.selectAll('text').data(rows).join('text')
|
91 |
+
.attr('x', innerWidth + 16)
|
92 |
+
.attr('y', (_,i)=> y(i) + y.bandwidth()/2)
|
93 |
+
.attr('dy','0.35em')
|
94 |
+
.style('font-size','14px')
|
95 |
+
.style('fill','var(--text-color)')
|
96 |
+
.text(d=>d.label);
|
97 |
+
|
98 |
+
// Blocks per row
|
99 |
+
const blockW = Math.min(84, x.bandwidth());
|
100 |
+
const blockH = Math.min(52, Math.round(y.bandwidth() * 0.8));
|
101 |
+
const blocks = [];
|
102 |
+
rows.forEach((row, ri) => {
|
103 |
+
for (let i=0;i<numBlocks;i++) blocks.push({ row, ri, i });
|
104 |
+
});
|
105 |
+
const sel = gBlocks.selectAll('rect.block').data(blocks, d=>`${d.row.key}-${d.i}`);
|
106 |
+
sel.join(
|
107 |
+
enter => enter.append('rect').attr('class','block')
|
108 |
+
.attr('x', d=>x(d.i))
|
109 |
+
.attr('y', d=>y(d.ri) + (y.bandwidth()-blockH)/2)
|
110 |
+
.attr('rx', 12).attr('ry', 12)
|
111 |
+
.attr('width', blockW)
|
112 |
+
.attr('height', blockH)
|
113 |
+
.attr('fill', d=>d.row.color)
|
114 |
+
.attr('opacity', 0.95)
|
115 |
+
.attr('stroke', 'rgba(0,0,0,0.18)')
|
116 |
+
.attr('filter', 'url(#shadow)')
|
117 |
+
.on('mouseenter', function(ev, d){
|
118 |
+
d3.select(this).attr('opacity', 1.0).attr('stroke-width', 1.2);
|
119 |
+
tipInner.innerHTML = `<div><strong>${d.row.label}</strong></div><div>${hoverText[d.row.key]}</div>`;
|
120 |
+
tip.style.opacity = '1';
|
121 |
+
})
|
122 |
+
.on('mousemove', function(ev){ const [mx,my] = d3.pointer(ev, container); tip.style.transform = `translate(${mx+12}px, ${my+12}px)`; })
|
123 |
+
.on('mouseleave', function(){ tip.style.opacity='0'; tip.style.transform='translate(-9999px,-9999px)'; d3.select(this).attr('opacity', 0.95).attr('stroke-width', 1); })
|
124 |
+
);
|
125 |
+
|
126 |
+
// Arrows forward/backward
|
127 |
+
gArrows.selectAll('*').remove();
|
128 |
+
const arrowY = (ri) => y(ri) + y.bandwidth()/2;
|
129 |
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
130 |
+
const arrowColor = isDark ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)';
|
131 |
+
const defs = svg.select('defs').empty() ? svg.append('defs') : svg.select('defs');
|
132 |
+
const marker = defs.append('marker').attr('id','arrow').attr('viewBox','0 0 10 10').attr('refX', 10).attr('refY', 5).attr('markerWidth', 6).attr('markerHeight', 6).attr('orient','auto-start-reverse');
|
133 |
+
marker.append('path').attr('d','M 0 0 L 10 5 L 0 10 z').attr('fill', arrowColor);
|
134 |
+
// drop shadow filter
|
135 |
+
const flt = defs.append('filter').attr('id','shadow').attr('x','-20%').attr('y','-20%').attr('width','140%').attr('height','140%');
|
136 |
+
flt.append('feDropShadow').attr('dx','0').attr('dy','1').attr('stdDeviation','1.5').attr('flood-color','rgba(0,0,0,0.18)');
|
137 |
+
// Forward arrow (top orientation)
|
138 |
+
gArrows.append('line').attr('x1', x(0)).attr('y1', arrowY(1)-28).attr('x2', x(numBlocks-1)+blockW).attr('y2', arrowY(1)-28)
|
139 |
+
.attr('stroke', rows[1].color).attr('stroke-width', 4).attr('marker-end','url(#arrow)');
|
140 |
+
// Backward arrow (orange, reversed)
|
141 |
+
gArrows.append('line').attr('x1', x(numBlocks-1)+blockW).attr('y1', arrowY(2)-20).attr('x2', x(0)).attr('y2', arrowY(2)-20)
|
142 |
+
.attr('stroke', rows[2].color).attr('stroke-width', 4).attr('marker-end','url(#arrow)');
|
143 |
+
// Vertical arrows (gradients down, updated up)
|
144 |
+
const midX = x(3) + blockW/2;
|
145 |
+
gArrows.append('line').attr('x1', midX).attr('y1', arrowY(2)+blockH/2+4).attr('x2', midX).attr('y2', arrowY(3)-blockH/2-6)
|
146 |
+
.attr('stroke', rows[3].color).attr('stroke-width', 3).attr('marker-end','url(#arrow)');
|
147 |
+
gArrows.append('line').attr('x1', midX).attr('y1', arrowY(4)+blockH/2+6).attr('x2', midX).attr('y2', arrowY(5)-blockH/2-6)
|
148 |
+
.attr('stroke', rows[5].color).attr('stroke-width', 3).attr('marker-end','url(#arrow)');
|
149 |
+
}
|
150 |
+
|
151 |
+
render();
|
152 |
+
if (window.ResizeObserver) { const ro = new ResizeObserver(()=>render()); ro.observe(container); } else { window.addEventListener('resize', render); }
|
153 |
+
};
|
154 |
+
|
155 |
+
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true }); } else { ensureD3(bootstrap); }
|
156 |
+
})();
|
157 |
+
</script>
|
158 |
+
|
159 |
+
|
app/src/content/fragments/line.html
CHANGED
@@ -1 +1,84 @@
|
|
1 |
-
<div> <div id="9ede8c7f-7b5c-4431-8616-891d85efdf07" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("9ede8c7f-7b5c-4431-8616-891d85efdf07")) { Plotly.newPlot( "9ede8c7f-7b5c-4431-8616-891d85efdf07", [{"hovertemplate":"\u003cb\u003e%{fullData.name}\u003c\u002fb\u003e\u003cbr\u003ex=%{x:.2f}\u003cbr\u003ey=%{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","line":{"color":"#64748b","shape":"spline","smoothing":0.6,"width":2},"mode":"lines","name":"Baseline","showlegend":true,"x":[0.0,0.0041841004184100415,0.008368200836820083,0.012552301255230124,0.016736401673640166,0.02092050209205021,0.025104602510460247,0.02928870292887029,0.03347280334728033,0.03765690376569037,0.04184100418410042,0.046025104602510455,0.050209205020920494,0.05439330543933054,0.05857740585774058,0.06276150627615062,0.06694560669456066,0.0711297071129707,0.07531380753138074,0.0794979079497908,0.08368200836820083,0.08786610878661087,0.09205020920502091,0.09623430962343095,0.10041841004184099,0.10460251046025104,0.10878661087866108,0.11297071129707112,0.11715481171548116,0.1213389121338912,0.12552301255230125,0.12970711297071127,0.13389121338912133,0.13807531380753138,0.1422594142259414,0.14644351464435146,0.15062761506276148,0.15481171548117154,0.1589958158995816,0.1631799163179916,0.16736401673640167,0.1715481171548117,0.17573221757322174,0.1799163179916318,0.18410041841004182,0.18828451882845187,0.1924686192468619,0.19665271966527195,0.20083682008368198,0.20502092050209203,0.20920502092050208,0.2133891213389121,0.21757322175732216,0.22175732217573219,0.22594142259414224,0.2301255230125523,0.23430962343096232,0.23849372384937237,0.2426778242677824,0.24686192468619245,0.2510460251046025,0.25523012552301255,0.25941422594142255,0.2635983263598326,0.26778242677824265,0.2719665271966527,0.27615062761506276,0.28033472803347276,0.2845188284518828,0.28870292887029286,0.2928870292887029,0.29707112970711297,0.30125523012552297,0.305439330543933,0.30962343096234307,0.3138075313807531,0.3179916317991632,0.3221757322175732,0.3263598326359832,0.3305439330543933,0.33472803347280333,0.3389121338912134,0.3430962343096234,0.34728033472803344,0.3514644351464435,0.35564853556485354,0.3598326359832636,0.3640167364016736,0.36820083682008364,0.3723849372384937,0.37656903765690375,0.3807531380753138,0.3849372384937238,0.38912133891213385,0.3933054393305439,0.39748953974895396,0.40167364016736395,0.405857740585774,0.41004184100418406,0.4142259414225941,0.41841004184100417,0.42259414225941416,0.4267782426778242,0.43096234309623427,0.4351464435146443,0.4393305439330544,0.44351464435146437,0.4476987447698744,0.4518828451882845,0.45606694560669453,0.4602510460251046,0.4644351464435146,0.46861924686192463,0.4728033472803347,0.47698744769874474,0.4811715481171548,0.4853556485355648,0.48953974895397484,0.4937238493723849,0.49790794979079495,0.502092050209205,0.506276150627615,0.5104602510460251,0.5146443514644351,0.5188284518828451,0.5230125523012552,0.5271966527196652,0.5313807531380753,0.5355648535564853,0.5397489539748953,0.5439330543933054,0.5481171548117154,0.5523012552301255,0.5564853556485355,0.5606694560669455,0.5648535564853556,0.5690376569037656,0.5732217573221757,0.5774058577405857,0.5815899581589957,0.5857740585774058,0.5899581589958158,0.5941422594142259,0.5983263598326359,0.6025104602510459,0.606694560669456,0.610878661087866,0.6150627615062761,0.6192468619246861,0.6234309623430961,0.6276150627615062,0.6317991631799162,0.6359832635983264,0.6401673640167364,0.6443514644351463,0.6485355648535565,0.6527196652719665,0.6569037656903766,0.6610878661087866,0.6652719665271966,0.6694560669456067,0.6736401673640167,0.6778242677824268,0.6820083682008368,0.6861924686192468,0.6903765690376569,0.6945606694560669,0.698744769874477,0.702928870292887,0.707112970711297,0.7112970711297071,0.7154811715481171,0.7196652719665272,0.7238493723849372,0.7280334728033472,0.7322175732217573,0.7364016736401673,0.7405857740585774,0.7447698744769874,0.7489539748953974,0.7531380753138075,0.7573221757322175,0.7615062761506276,0.7656903765690376,0.7698744769874476,0.7740585774058577,0.7782426778242677,0.7824267782426778,0.7866108786610878,0.7907949790794978,0.7949790794979079,0.7991631799163179,0.8033472803347279,0.807531380753138,0.811715481171548,0.8158995815899581,0.8200836820083681,0.8242677824267781,0.8284518828451882,0.8326359832635982,0.8368200836820083,0.8410041841004183,0.8451882845188283,0.8493723849372384,0.8535564853556484,0.8577405857740585,0.8619246861924685,0.8661087866108785,0.8702928870292886,0.8744769874476986,0.8786610878661087,0.8828451882845187,0.8870292887029287,0.8912133891213389,0.8953974895397488,0.899581589958159,0.903765690376569,0.907949790794979,0.9121338912133891,0.9163179916317991,0.9205020920502092,0.9246861924686192,0.9288702928870292,0.9330543933054393,0.9372384937238493,0.9414225941422594,0.9456066945606694,0.9497907949790794,0.9539748953974895,0.9581589958158995,0.9623430962343096,0.9665271966527196,0.9707112970711296,0.9748953974895397,0.9790794979079497,0.9832635983263598,0.9874476987447698,0.9916317991631798,0.9958158995815899,1.0],"y":[0.2,0.20251046025104605,0.20502092050209206,0.2075313807531381,0.2100418410041841,0.21255230125523014,0.21506276150627615,0.2175732217573222,0.2200836820083682,0.22259414225941423,0.22510460251046027,0.22761506276150628,0.2301255230125523,0.23263598326359833,0.23514644351464437,0.23765690376569037,0.2401673640167364,0.24267782426778242,0.24518828451882846,0.2476987447698745,0.25020920502092053,0.25271966527196654,0.25523012552301255,0.25774058577405856,0.2602510460251046,0.26276150627615064,0.26527196652719665,0.2677824267782427,0.2702928870292887,0.27280334728033473,0.27531380753138074,0.27782426778242675,0.2803347280334728,0.2828451882845188,0.28535564853556483,0.2878661087866109,0.2903765690376569,0.2928870292887029,0.295397489539749,0.297907949790795,0.300418410041841,0.302928870292887,0.3054393305439331,0.3079497907949791,0.3104602510460251,0.3129707112970711,0.31548117154811717,0.3179916317991632,0.3205020920502092,0.32301255230125525,0.32552301255230126,0.32803347280334727,0.33054393305439334,0.3330543933054393,0.33556485355648535,0.3380753138075314,0.3405857740585774,0.34309623430962344,0.34560669456066945,0.34811715481171546,0.3506276150627615,0.35313807531380753,0.35564853556485354,0.3581589958158996,0.36066945606694556,0.3631799163179916,0.3656903765690377,0.36820083682008364,0.3707112970711297,0.3732217573221757,0.3757322175732217,0.3782426778242678,0.38075313807531375,0.3832635983263598,0.3857740585774059,0.3882845188284519,0.3907949790794979,0.3933054393305439,0.3958158995815899,0.398326359832636,0.40083682008368204,0.403347280334728,0.40585774058577406,0.40836820083682007,0.4108786610878661,0.41338912133891215,0.41589958158995816,0.41841004184100417,0.42092050209205023,0.4234309623430962,0.42594142259414225,0.4284518828451883,0.43096234309623427,0.43347280334728033,0.43598326359832634,0.43849372384937235,0.44100418410041836,0.44351464435146437,0.44602510460251044,0.4485355648535565,0.4510460251046025,0.4535564853556485,0.45606694560669453,0.45857740585774054,0.4610878661087866,0.4635983263598326,0.4661087866108786,0.46861924686192463,0.4711297071129707,0.4736401673640167,0.47615062761506277,0.4786610878661087,0.4811715481171548,0.4836820083682008,0.48619246861924686,0.4887029288702929,0.4912133891213389,0.4937238493723849,0.49623430962343096,0.49874476987447697,0.501255230125523,0.503765690376569,0.506276150627615,0.5087866108786611,0.5112970711297071,0.5138075313807531,0.5163179916317991,0.5188284518828452,0.5213389121338912,0.5238493723849371,0.5263598326359833,0.5288702928870292,0.5313807531380753,0.5338912133891214,0.5364016736401673,0.5389121338912134,0.5414225941422594,0.5439330543933054,0.5464435146443514,0.5489539748953974,0.5514644351464435,0.5539748953974895,0.5564853556485356,0.5589958158995816,0.5615062761506275,0.5640167364016736,0.5665271966527197,0.5690376569037656,0.5715481171548117,0.5740585774058578,0.5765690376569037,0.5790794979079498,0.5815899581589958,0.5841004184100418,0.5866108786610877,0.5891213389121339,0.5916317991631799,0.5941422594142259,0.596652719665272,0.599163179916318,0.601673640167364,0.60418410041841,0.606694560669456,0.609205020920502,0.6117154811715481,0.6142259414225941,0.6167364016736401,0.6192468619246863,0.6217573221757322,0.6242677824267782,0.6267782426778242,0.6292887029288703,0.6317991631799162,0.6343096234309623,0.6368200836820084,0.6393305439330543,0.6418410041841004,0.6443514644351465,0.6468619246861924,0.6493723849372384,0.6518828451882845,0.6543933054393305,0.6569037656903766,0.6594142259414226,0.6619246861924686,0.6644351464435145,0.6669456066945606,0.6694560669456067,0.6719665271966526,0.6744769874476987,0.6769874476987447,0.6794979079497907,0.6820083682008367,0.6845188284518828,0.6870292887029288,0.6895397489539749,0.6920502092050209,0.6945606694560669,0.6970711297071129,0.699581589958159,0.702092050209205,0.7046025104602509,0.7071129707112971,0.709623430962343,0.712133891213389,0.7146443514644352,0.7171548117154811,0.7196652719665271,0.7221757322175733,0.7246861924686192,0.7271966527196652,0.7297071129707113,0.7322175732217573,0.7347280334728032,0.7372384937238492,0.7397489539748954,0.7422594142259413,0.7447698744769873,0.7472803347280335,0.7497907949790794,0.7523012552301256,0.7548117154811715,0.7573221757322175,0.7598326359832634,0.7623430962343096,0.7648535564853556,0.7673640167364015,0.7698744769874477,0.7723849372384937,0.7748953974895396,0.7774058577405858,0.7799163179916317,0.7824267782426777,0.7849372384937239,0.7874476987447698,0.7899581589958158,0.792468619246862,0.7949790794979079,0.7974895397489539,0.8],"type":"scatter"},{"hovertemplate":"\u003cb\u003e%{fullData.name}\u003c\u002fb\u003e\u003cbr\u003ex=%{x:.2f}\u003cbr\u003ey=%{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","line":{"color":"#2563eb","shape":"spline","smoothing":0.6,"width":2},"mode":"lines","name":"Improved","showlegend":true,"x":[0.0,0.0041841004184100415,0.008368200836820083,0.012552301255230124,0.016736401673640166,0.02092050209205021,0.025104602510460247,0.02928870292887029,0.03347280334728033,0.03765690376569037,0.04184100418410042,0.046025104602510455,0.050209205020920494,0.05439330543933054,0.05857740585774058,0.06276150627615062,0.06694560669456066,0.0711297071129707,0.07531380753138074,0.0794979079497908,0.08368200836820083,0.08786610878661087,0.09205020920502091,0.09623430962343095,0.10041841004184099,0.10460251046025104,0.10878661087866108,0.11297071129707112,0.11715481171548116,0.1213389121338912,0.12552301255230125,0.12970711297071127,0.13389121338912133,0.13807531380753138,0.1422594142259414,0.14644351464435146,0.15062761506276148,0.15481171548117154,0.1589958158995816,0.1631799163179916,0.16736401673640167,0.1715481171548117,0.17573221757322174,0.1799163179916318,0.18410041841004182,0.18828451882845187,0.1924686192468619,0.19665271966527195,0.20083682008368198,0.20502092050209203,0.20920502092050208,0.2133891213389121,0.21757322175732216,0.22175732217573219,0.22594142259414224,0.2301255230125523,0.23430962343096232,0.23849372384937237,0.2426778242677824,0.24686192468619245,0.2510460251046025,0.25523012552301255,0.25941422594142255,0.2635983263598326,0.26778242677824265,0.2719665271966527,0.27615062761506276,0.28033472803347276,0.2845188284518828,0.28870292887029286,0.2928870292887029,0.29707112970711297,0.30125523012552297,0.305439330543933,0.30962343096234307,0.3138075313807531,0.3179916317991632,0.3221757322175732,0.3263598326359832,0.3305439330543933,0.33472803347280333,0.3389121338912134,0.3430962343096234,0.34728033472803344,0.3514644351464435,0.35564853556485354,0.3598326359832636,0.3640167364016736,0.36820083682008364,0.3723849372384937,0.37656903765690375,0.3807531380753138,0.3849372384937238,0.38912133891213385,0.3933054393305439,0.39748953974895396,0.40167364016736395,0.405857740585774,0.41004184100418406,0.4142259414225941,0.41841004184100417,0.42259414225941416,0.4267782426778242,0.43096234309623427,0.4351464435146443,0.4393305439330544,0.44351464435146437,0.4476987447698744,0.4518828451882845,0.45606694560669453,0.4602510460251046,0.4644351464435146,0.46861924686192463,0.4728033472803347,0.47698744769874474,0.4811715481171548,0.4853556485355648,0.48953974895397484,0.4937238493723849,0.49790794979079495,0.502092050209205,0.506276150627615,0.5104602510460251,0.5146443514644351,0.5188284518828451,0.5230125523012552,0.5271966527196652,0.5313807531380753,0.5355648535564853,0.5397489539748953,0.5439330543933054,0.5481171548117154,0.5523012552301255,0.5564853556485355,0.5606694560669455,0.5648535564853556,0.5690376569037656,0.5732217573221757,0.5774058577405857,0.5815899581589957,0.5857740585774058,0.5899581589958158,0.5941422594142259,0.5983263598326359,0.6025104602510459,0.606694560669456,0.610878661087866,0.6150627615062761,0.6192468619246861,0.6234309623430961,0.6276150627615062,0.6317991631799162,0.6359832635983264,0.6401673640167364,0.6443514644351463,0.6485355648535565,0.6527196652719665,0.6569037656903766,0.6610878661087866,0.6652719665271966,0.6694560669456067,0.6736401673640167,0.6778242677824268,0.6820083682008368,0.6861924686192468,0.6903765690376569,0.6945606694560669,0.698744769874477,0.702928870292887,0.707112970711297,0.7112970711297071,0.7154811715481171,0.7196652719665272,0.7238493723849372,0.7280334728033472,0.7322175732217573,0.7364016736401673,0.7405857740585774,0.7447698744769874,0.7489539748953974,0.7531380753138075,0.7573221757322175,0.7615062761506276,0.7656903765690376,0.7698744769874476,0.7740585774058577,0.7782426778242677,0.7824267782426778,0.7866108786610878,0.7907949790794978,0.7949790794979079,0.7991631799163179,0.8033472803347279,0.807531380753138,0.811715481171548,0.8158995815899581,0.8200836820083681,0.8242677824267781,0.8284518828451882,0.8326359832635982,0.8368200836820083,0.8410041841004183,0.8451882845188283,0.8493723849372384,0.8535564853556484,0.8577405857740585,0.8619246861924685,0.8661087866108785,0.8702928870292886,0.8744769874476986,0.8786610878661087,0.8828451882845187,0.8870292887029287,0.8912133891213389,0.8953974895397488,0.899581589958159,0.903765690376569,0.907949790794979,0.9121338912133891,0.9163179916317991,0.9205020920502092,0.9246861924686192,0.9288702928870292,0.9330543933054393,0.9372384937238493,0.9414225941422594,0.9456066945606694,0.9497907949790794,0.9539748953974895,0.9581589958158995,0.9623430962343096,0.9665271966527196,0.9707112970711296,0.9748953974895397,0.9790794979079497,0.9832635983263598,0.9874476987447698,0.9916317991631798,0.9958158995815899,1.0],"y":[0.15,0.15292887029288701,0.15585774058577406,0.15878661087866108,0.1617154811715481,0.16464435146443515,0.16757322175732217,0.1705020920502092,0.17343096234309624,0.17635983263598326,0.17928870292887028,0.1822175732217573,0.18514644351464432,0.18807531380753137,0.1910041841004184,0.19393305439330544,0.19686192468619246,0.19979079497907948,0.2027196652719665,0.20564853556485355,0.20857740585774057,0.21150627615062761,0.21443514644351463,0.21736401673640166,0.22029288702928868,0.22322175732217572,0.22615062761506274,0.2290794979079498,0.2320083682008368,0.23493723849372383,0.23786610878661085,0.24079497907949787,0.24372384937238492,0.24665271966527197,0.249581589958159,0.252510460251046,0.25543933054393303,0.25836820083682005,0.2612970711297071,0.26422594142259415,0.26715481171548117,0.2700836820083682,0.2730125523012552,0.2759414225941422,0.27887029288702925,0.28179916317991627,0.2847280334728033,0.28765690376569036,0.29058577405857733,0.2935146443514644,0.2964435146443515,0.2993723849372385,0.3023012552301255,0.30523012552301254,0.30815899581589956,0.3110878661087866,0.3140167364016736,0.3169456066945606,0.31987447698744764,0.32280334728033466,0.32573221757322174,0.32866108786610876,0.3315899581589958,0.3345188284518828,0.3374476987447699,0.3403765690376569,0.3433054393305439,0.34623430962343094,0.34916317991631796,0.352092050209205,0.355020920502092,0.3579497907949791,0.36087866108786604,0.36380753138075306,0.36673640167364013,0.36966527196652715,0.37259414225941423,0.3755230125523012,0.37845188284518827,0.3813807531380753,0.3843096234309623,0.38723849372384933,0.39016736401673635,0.39309623430962337,0.3960251046025104,0.39895397489539747,0.40188284518828454,0.40481171548117145,0.4077405857740586,0.4106694560669456,0.4135983263598326,0.41652719665271964,0.41945606694560666,0.4223849372384937,0.4253138075313807,0.4282426778242677,0.43117154811715475,0.43410041841004177,0.4370292887029288,0.4399581589958158,0.44288702928870294,0.44581589958158985,0.448744769874477,0.451673640167364,0.454602510460251,0.45753138075313804,0.46046025104602506,0.4633891213389121,0.4663179916317991,0.4692468619246861,0.47217573221757314,0.47510460251046016,0.4780334728033472,0.4809623430962342,0.48389121338912133,0.48682008368200835,0.4897489539748954,0.4926778242677824,0.4956066945606694,0.49853556485355643,0.5014644351464435,0.5043933054393305,0.5073221757322175,0.5102510460251045,0.5131799163179915,0.5161087866108787,0.5190376569037656,0.5219665271966527,0.5248953974895397,0.5278242677824266,0.5307531380753138,0.5336820083682008,0.5366108786610878,0.5395397489539748,0.5424686192468618,0.5453974895397489,0.5483263598326359,0.551255230125523,0.5541841004184099,0.557112970711297,0.5600418410041841,0.5629707112970711,0.5658995815899581,0.5688284518828451,0.5717573221757322,0.5746861924686192,0.5776150627615062,0.5805439330543932,0.5834728033472802,0.5864016736401673,0.5893305439330543,0.5922594142259413,0.5951882845188284,0.5981171548117155,0.6010460251046024,0.6039748953974895,0.6069037656903765,0.6098326359832635,0.6127615062761506,0.6156903765690376,0.6186192468619246,0.6215481171548116,0.6244769874476988,0.6274058577405857,0.6303347280334727,0.6332635983263598,0.6361924686192467,0.6391213389121339,0.6420502092050209,0.6449790794979079,0.6479079497907949,0.6508368200836819,0.6537656903765691,0.656694560669456,0.659623430962343,0.6625523012552301,0.6654811715481171,0.6684100418410042,0.6713389121338912,0.6742677824267782,0.6771966527196652,0.6801255230125522,0.6830543933054393,0.6859832635983263,0.6889121338912133,0.6918410041841003,0.6947698744769873,0.6976987447698745,0.7006276150627615,0.7035564853556484,0.7064853556485355,0.7094142259414226,0.7123430962343095,0.7152719665271966,0.7182008368200836,0.7211297071129706,0.7240585774058577,0.7269874476987447,0.7299163179916317,0.7328451882845187,0.7357740585774059,0.7387029288702928,0.7416317991631798,0.7445606694560669,0.7474895397489539,0.750418410041841,0.753347280334728,0.756276150627615,0.759205020920502,0.762133891213389,0.7650627615062761,0.7679916317991631,0.7709205020920501,0.7738493723849372,0.7767782426778241,0.7797071129707113,0.7826359832635983,0.7855648535564852,0.7884937238493723,0.7914225941422594,0.7943514644351464,0.7972803347280334,0.8002092050209204,0.8031380753138074,0.8060669456066945,0.8089958158995816,0.8119246861924685,0.8148535564853555,0.8177824267782426,0.8207112970711297,0.8236401673640167,0.8265690376569037,0.8294979079497907,0.8324267782426777,0.8353556485355648,0.8382845188284518,0.8412133891213388,0.8441422594142258,0.847071129707113,0.85],"type":"scatter"},{"hovertemplate":"\u003cb\u003e%{fullData.name}\u003c\u002fb\u003e\u003cbr\u003ex=%{x:.2f}\u003cbr\u003ey=%{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","line":{"color":"#4b5563","dash":"dash","width":2},"mode":"lines","name":"Target","showlegend":true,"x":[0.0,0.0041841004184100415,0.008368200836820083,0.012552301255230124,0.016736401673640166,0.02092050209205021,0.025104602510460247,0.02928870292887029,0.03347280334728033,0.03765690376569037,0.04184100418410042,0.046025104602510455,0.050209205020920494,0.05439330543933054,0.05857740585774058,0.06276150627615062,0.06694560669456066,0.0711297071129707,0.07531380753138074,0.0794979079497908,0.08368200836820083,0.08786610878661087,0.09205020920502091,0.09623430962343095,0.10041841004184099,0.10460251046025104,0.10878661087866108,0.11297071129707112,0.11715481171548116,0.1213389121338912,0.12552301255230125,0.12970711297071127,0.13389121338912133,0.13807531380753138,0.1422594142259414,0.14644351464435146,0.15062761506276148,0.15481171548117154,0.1589958158995816,0.1631799163179916,0.16736401673640167,0.1715481171548117,0.17573221757322174,0.1799163179916318,0.18410041841004182,0.18828451882845187,0.1924686192468619,0.19665271966527195,0.20083682008368198,0.20502092050209203,0.20920502092050208,0.2133891213389121,0.21757322175732216,0.22175732217573219,0.22594142259414224,0.2301255230125523,0.23430962343096232,0.23849372384937237,0.2426778242677824,0.24686192468619245,0.2510460251046025,0.25523012552301255,0.25941422594142255,0.2635983263598326,0.26778242677824265,0.2719665271966527,0.27615062761506276,0.28033472803347276,0.2845188284518828,0.28870292887029286,0.2928870292887029,0.29707112970711297,0.30125523012552297,0.305439330543933,0.30962343096234307,0.3138075313807531,0.3179916317991632,0.3221757322175732,0.3263598326359832,0.3305439330543933,0.33472803347280333,0.3389121338912134,0.3430962343096234,0.34728033472803344,0.3514644351464435,0.35564853556485354,0.3598326359832636,0.3640167364016736,0.36820083682008364,0.3723849372384937,0.37656903765690375,0.3807531380753138,0.3849372384937238,0.38912133891213385,0.3933054393305439,0.39748953974895396,0.40167364016736395,0.405857740585774,0.41004184100418406,0.4142259414225941,0.41841004184100417,0.42259414225941416,0.4267782426778242,0.43096234309623427,0.4351464435146443,0.4393305439330544,0.44351464435146437,0.4476987447698744,0.4518828451882845,0.45606694560669453,0.4602510460251046,0.4644351464435146,0.46861924686192463,0.4728033472803347,0.47698744769874474,0.4811715481171548,0.4853556485355648,0.48953974895397484,0.4937238493723849,0.49790794979079495,0.502092050209205,0.506276150627615,0.5104602510460251,0.5146443514644351,0.5188284518828451,0.5230125523012552,0.5271966527196652,0.5313807531380753,0.5355648535564853,0.5397489539748953,0.5439330543933054,0.5481171548117154,0.5523012552301255,0.5564853556485355,0.5606694560669455,0.5648535564853556,0.5690376569037656,0.5732217573221757,0.5774058577405857,0.5815899581589957,0.5857740585774058,0.5899581589958158,0.5941422594142259,0.5983263598326359,0.6025104602510459,0.606694560669456,0.610878661087866,0.6150627615062761,0.6192468619246861,0.6234309623430961,0.6276150627615062,0.6317991631799162,0.6359832635983264,0.6401673640167364,0.6443514644351463,0.6485355648535565,0.6527196652719665,0.6569037656903766,0.6610878661087866,0.6652719665271966,0.6694560669456067,0.6736401673640167,0.6778242677824268,0.6820083682008368,0.6861924686192468,0.6903765690376569,0.6945606694560669,0.698744769874477,0.702928870292887,0.707112970711297,0.7112970711297071,0.7154811715481171,0.7196652719665272,0.7238493723849372,0.7280334728033472,0.7322175732217573,0.7364016736401673,0.7405857740585774,0.7447698744769874,0.7489539748953974,0.7531380753138075,0.7573221757322175,0.7615062761506276,0.7656903765690376,0.7698744769874476,0.7740585774058577,0.7782426778242677,0.7824267782426778,0.7866108786610878,0.7907949790794978,0.7949790794979079,0.7991631799163179,0.8033472803347279,0.807531380753138,0.811715481171548,0.8158995815899581,0.8200836820083681,0.8242677824267781,0.8284518828451882,0.8326359832635982,0.8368200836820083,0.8410041841004183,0.8451882845188283,0.8493723849372384,0.8535564853556484,0.8577405857740585,0.8619246861924685,0.8661087866108785,0.8702928870292886,0.8744769874476986,0.8786610878661087,0.8828451882845187,0.8870292887029287,0.8912133891213389,0.8953974895397488,0.899581589958159,0.903765690376569,0.907949790794979,0.9121338912133891,0.9163179916317991,0.9205020920502092,0.9246861924686192,0.9288702928870292,0.9330543933054393,0.9372384937238493,0.9414225941422594,0.9456066945606694,0.9497907949790794,0.9539748953974895,0.9581589958158995,0.9623430962343096,0.9665271966527196,0.9707112970711296,0.9748953974895397,0.9790794979079497,0.9832635983263598,0.9874476987447698,0.9916317991631798,0.9958158995815899,1.0],"y":[0.1,0.10334728033472804,0.10669456066945607,0.1100418410041841,0.11338912133891214,0.11673640167364017,0.1200836820083682,0.12343096234309624,0.12677824267782428,0.1301255230125523,0.13347280334728034,0.13682008368200838,0.1401673640167364,0.14351464435146444,0.14686192468619247,0.1502092050209205,0.15355648535564853,0.15690376569037656,0.1602510460251046,0.16359832635983262,0.16694560669456066,0.1702928870292887,0.17364016736401672,0.17698744769874478,0.1803347280334728,0.18368200836820084,0.18702928870292887,0.1903765690376569,0.19372384937238493,0.19707112970711296,0.20041841004184102,0.20376569037656903,0.20711297071129708,0.21046025104602512,0.21380753138075315,0.21715481171548118,0.2205020920502092,0.22384937238493724,0.22719665271966527,0.2305439330543933,0.23389121338912133,0.23723849372384936,0.2405857740585774,0.24393305439330545,0.24728033472803346,0.25062761506276154,0.2539748953974895,0.2573221757322176,0.2606694560669456,0.26401673640167367,0.26736401673640164,0.27071129707112973,0.2740585774058577,0.2774058577405858,0.28075313807531377,0.28410041841004186,0.28744769874476983,0.2907949790794979,0.2941422594142259,0.297489539748954,0.300836820083682,0.30418410041841004,0.307531380753138,0.3108786610878661,0.31422594142259413,0.31757322175732217,0.32092050209205025,0.32426778242677823,0.32761506276150626,0.3309623430962343,0.3343096234309624,0.33765690376569035,0.3410041841004184,0.3443514644351464,0.3476987447698745,0.3510460251046025,0.35439330543933056,0.35774058577405854,0.3610878661087866,0.3644351464435146,0.3677824267782427,0.3711297071129708,0.37447698744769875,0.3778242677824267,0.3811715481171548,0.3845188284518829,0.3878661087866109,0.39121338912133885,0.39456066945606694,0.397907949790795,0.401255230125523,0.4046025104602511,0.40794979079497906,0.41129707112970715,0.4146443514644351,0.4179916317991632,0.4213389121338912,0.4246861924686193,0.42803347280334725,0.43138075313807533,0.4347280334728033,0.4380753138075314,0.4414225941422594,0.44476987447698746,0.44811715481171543,0.4514644351464435,0.4548117154811715,0.4581589958158996,0.46150627615062756,0.46485355648535565,0.46820083682008373,0.4715481171548117,0.4748953974895397,0.47824267782426777,0.48158995815899586,0.48493723849372383,0.4882845188284518,0.4916317991631799,0.494979079497908,0.49832635983263596,0.501673640167364,0.505020920502092,0.5083682008368201,0.5117154811715481,0.5150627615062761,0.5184100418410041,0.5217573221757322,0.5251046025104603,0.5284518828451883,0.5317991631799163,0.5351464435146444,0.5384937238493723,0.5418410041841004,0.5451882845188284,0.5485355648535565,0.5518828451882846,0.5552301255230125,0.5585774058577406,0.5619246861924686,0.5652719665271966,0.5686192468619247,0.5719665271966526,0.5753138075313807,0.5786610878661088,0.5820083682008368,0.5853556485355649,0.5887029288702929,0.5920502092050209,0.5953974895397489,0.5987447698744769,0.602092050209205,0.605439330543933,0.608786610878661,0.6121338912133891,0.6154811715481171,0.6188284518828452,0.6221757322175732,0.6255230125523012,0.6288702928870292,0.6322175732217572,0.6355648535564853,0.6389121338912134,0.6422594142259415,0.6456066945606694,0.6489539748953974,0.6523012552301255,0.6556485355648535,0.6589958158995816,0.6623430962343095,0.6656903765690376,0.6690376569037657,0.6723849372384937,0.6757322175732218,0.6790794979079497,0.6824267782426777,0.6857740585774058,0.6891213389121338,0.6924686192468619,0.69581589958159,0.6991631799163179,0.702510460251046,0.705857740585774,0.7092050209205021,0.7125523012552301,0.7158995815899581,0.7192468619246862,0.7225941422594142,0.7259414225941423,0.7292887029288703,0.7326359832635982,0.7359832635983263,0.7393305439330543,0.7426778242677824,0.7460251046025105,0.7493723849372385,0.7527196652719665,0.7560669456066945,0.7594142259414225,0.7627615062761506,0.7661087866108786,0.7694560669456066,0.7728033472803347,0.7761506276150627,0.7794979079497908,0.7828451882845188,0.7861924686192469,0.7895397489539748,0.7928870292887028,0.7962343096234309,0.799581589958159,0.8029288702928871,0.806276150627615,0.809623430962343,0.8129707112970711,0.8163179916317991,0.8196652719665272,0.8230125523012551,0.8263598326359832,0.8297071129707113,0.8330543933054393,0.8364016736401674,0.8397489539748954,0.8430962343096233,0.8464435146443514,0.8497907949790794,0.8531380753138075,0.8564853556485356,0.8598326359832635,0.8631799163179916,0.8665271966527196,0.8698744769874477,0.8732217573221757,0.8765690376569036,0.8799163179916317,0.8832635983263598,0.8866108786610879,0.8899581589958159,0.8933054393305438,0.8966527196652719,0.9],"type":"scatter"}], {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"margin":{"l":28,"r":12,"t":8,"b":28},"hoverlabel":{"font":{"color":"#111827","size":12},"bgcolor":"white","bordercolor":"rgba(0,0,0,0.15)","align":"left","namelength":-1},"xaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.55)"},"showgrid":false,"zeroline":false,"showline":true,"linecolor":"rgba(0,0,0,0.25)","linewidth":1,"ticks":"outside","ticklen":6,"tickcolor":"rgba(0,0,0,0.25)","title":{},"automargin":true,"fixedrange":true},"yaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.55)"},"showgrid":false,"zeroline":false,"showline":true,"linecolor":"rgba(0,0,0,0.25)","linewidth":1,"ticks":"outside","ticklen":6,"tickcolor":"rgba(0,0,0,0.25)","title":{},"tickformat":".2f","rangemode":"tozero","automargin":true,"fixedrange":true},"autosize":true,"paper_bgcolor":"rgba(0,0,0,0)","plot_bgcolor":"rgba(0,0,0,0)","hovermode":"x unified"}, {"displayModeBar": false, "responsive": true, "scrollZoom": false} ) }; </script> </div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
<div id="line-ex-container-7f91adbe">
|
3 |
+
<div> <div id="92f51efc-b046-49aa-b59d-742de70d1d52" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("92f51efc-b046-49aa-b59d-742de70d1d52")) { Plotly.newPlot( "92f51efc-b046-49aa-b59d-742de70d1d52", [{"hovertemplate":"\u003cb\u003e%{fullData.name}\u003c\u002fb\u003e\u003cbr\u003ex=%{x:.2f}\u003cbr\u003ey=%{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","line":{"color":"#64748b","shape":"spline","smoothing":0.6,"width":2},"mode":"lines","name":"Baseline","showlegend":true,"x":[0.0,0.0041841004184100415,0.008368200836820083,0.012552301255230124,0.016736401673640166,0.02092050209205021,0.025104602510460247,0.02928870292887029,0.03347280334728033,0.03765690376569037,0.04184100418410042,0.046025104602510455,0.050209205020920494,0.05439330543933054,0.05857740585774058,0.06276150627615062,0.06694560669456066,0.0711297071129707,0.07531380753138074,0.0794979079497908,0.08368200836820083,0.08786610878661087,0.09205020920502091,0.09623430962343095,0.10041841004184099,0.10460251046025104,0.10878661087866108,0.11297071129707112,0.11715481171548116,0.1213389121338912,0.12552301255230125,0.12970711297071127,0.13389121338912133,0.13807531380753138,0.1422594142259414,0.14644351464435146,0.15062761506276148,0.15481171548117154,0.1589958158995816,0.1631799163179916,0.16736401673640167,0.1715481171548117,0.17573221757322174,0.1799163179916318,0.18410041841004182,0.18828451882845187,0.1924686192468619,0.19665271966527195,0.20083682008368198,0.20502092050209203,0.20920502092050208,0.2133891213389121,0.21757322175732216,0.22175732217573219,0.22594142259414224,0.2301255230125523,0.23430962343096232,0.23849372384937237,0.2426778242677824,0.24686192468619245,0.2510460251046025,0.25523012552301255,0.25941422594142255,0.2635983263598326,0.26778242677824265,0.2719665271966527,0.27615062761506276,0.28033472803347276,0.2845188284518828,0.28870292887029286,0.2928870292887029,0.29707112970711297,0.30125523012552297,0.305439330543933,0.30962343096234307,0.3138075313807531,0.3179916317991632,0.3221757322175732,0.3263598326359832,0.3305439330543933,0.33472803347280333,0.3389121338912134,0.3430962343096234,0.34728033472803344,0.3514644351464435,0.35564853556485354,0.3598326359832636,0.3640167364016736,0.36820083682008364,0.3723849372384937,0.37656903765690375,0.3807531380753138,0.3849372384937238,0.38912133891213385,0.3933054393305439,0.39748953974895396,0.40167364016736395,0.405857740585774,0.41004184100418406,0.4142259414225941,0.41841004184100417,0.42259414225941416,0.4267782426778242,0.43096234309623427,0.4351464435146443,0.4393305439330544,0.44351464435146437,0.4476987447698744,0.4518828451882845,0.45606694560669453,0.4602510460251046,0.4644351464435146,0.46861924686192463,0.4728033472803347,0.47698744769874474,0.4811715481171548,0.4853556485355648,0.48953974895397484,0.4937238493723849,0.49790794979079495,0.502092050209205,0.506276150627615,0.5104602510460251,0.5146443514644351,0.5188284518828451,0.5230125523012552,0.5271966527196652,0.5313807531380753,0.5355648535564853,0.5397489539748953,0.5439330543933054,0.5481171548117154,0.5523012552301255,0.5564853556485355,0.5606694560669455,0.5648535564853556,0.5690376569037656,0.5732217573221757,0.5774058577405857,0.5815899581589957,0.5857740585774058,0.5899581589958158,0.5941422594142259,0.5983263598326359,0.6025104602510459,0.606694560669456,0.610878661087866,0.6150627615062761,0.6192468619246861,0.6234309623430961,0.6276150627615062,0.6317991631799162,0.6359832635983264,0.6401673640167364,0.6443514644351463,0.6485355648535565,0.6527196652719665,0.6569037656903766,0.6610878661087866,0.6652719665271966,0.6694560669456067,0.6736401673640167,0.6778242677824268,0.6820083682008368,0.6861924686192468,0.6903765690376569,0.6945606694560669,0.698744769874477,0.702928870292887,0.707112970711297,0.7112970711297071,0.7154811715481171,0.7196652719665272,0.7238493723849372,0.7280334728033472,0.7322175732217573,0.7364016736401673,0.7405857740585774,0.7447698744769874,0.7489539748953974,0.7531380753138075,0.7573221757322175,0.7615062761506276,0.7656903765690376,0.7698744769874476,0.7740585774058577,0.7782426778242677,0.7824267782426778,0.7866108786610878,0.7907949790794978,0.7949790794979079,0.7991631799163179,0.8033472803347279,0.807531380753138,0.811715481171548,0.8158995815899581,0.8200836820083681,0.8242677824267781,0.8284518828451882,0.8326359832635982,0.8368200836820083,0.8410041841004183,0.8451882845188283,0.8493723849372384,0.8535564853556484,0.8577405857740585,0.8619246861924685,0.8661087866108785,0.8702928870292886,0.8744769874476986,0.8786610878661087,0.8828451882845187,0.8870292887029287,0.8912133891213389,0.8953974895397488,0.899581589958159,0.903765690376569,0.907949790794979,0.9121338912133891,0.9163179916317991,0.9205020920502092,0.9246861924686192,0.9288702928870292,0.9330543933054393,0.9372384937238493,0.9414225941422594,0.9456066945606694,0.9497907949790794,0.9539748953974895,0.9581589958158995,0.9623430962343096,0.9665271966527196,0.9707112970711296,0.9748953974895397,0.9790794979079497,0.9832635983263598,0.9874476987447698,0.9916317991631798,0.9958158995815899,1.0],"y":[0.10325611017271691,0.10339464908070704,0.1035390562715586,0.10368957804184283,0.10384647082790888,0.10401000160614818,0.10418044830755235,0.10435810024693962,0.10454325856722096,0.1047362366990729,0.10493736083637589,0.10514697042776965,0.10536541868466405,0.10559307310603182,0.10583031602029194,0.10607754514457322,0.10633517416162493,0.10660363331461449,0.10688337002002223,0.10717484949880761,0.10747855542598257,0.10779499059868251,0.10812467762277514,0.1084681596179913,0.10882600094149918,0.10919878792977272,0.10958712965852876,0.10999165872042038,0.11041303202008067,0.11085193158600631,0.11130906539865677,0.11178516823401995,0.11228100252175863,0.1127973592169032,0.11333505868389485,0.11389495159160672,0.114477919817781,0.11508487736111359,0.11571677125899543,0.11637458250868019,0.11705932698939046,0.117772056382598,0.11851385908741732,0.1192858611277356,0.12008922704736441,0.12092516078913879,0.1217949065535085,0.12269974963176174,0.12364101720859388,0.1246200791282849,0.12563834861827425,0.12669728296342733,0.12779838412376887,0.12894319928791798,0.1301333213539018,0.13137038932844328,0.13265608863522507,0.13399215132202053,0.13538035615596092,0.13682252859557953,0.13832054062763832,0.1398763104561116,0.14149180203007444,0.1431690243966304,0.14491003086442047,0.14671691796269073,0.14859182418036831,0.15053692846911637,0.15255444849391867,0.15464663861439287,0.1568157875797699,0.15906421592030787,0.1613942730178593,0.16380833383839039,0.16630879530947795,0.1688980723262078,0.17157859336947684,0.17435279572149145,0.1772231202642655,0.18019200584818182,0.18326188321920467,0.18643516849514025,0.1897142561834541,0.1931015117355877,0.19659926363548103,0.2002097950231238,0.2039353348574255,0.207778048626525,0.21174002861785352,0.21582328376481186,0.22002972909182084,0.22436117478472664,0.2288193149190735,0.23340571588455722,0.23812180455000417,0.24296885621943393,0.24794798243609284,0.2530601186977304,0.25830601215274074,0.2636862093530299,0.26920104414548673,0.2748506257896357,0.28063482739431467,0.28655327477093173,0.2926053358048954,0.29879011045004616,0.30510642145323597,0.31155280591747114,0.31812750781214627,0.3248284715377472,0.3316533366498935,0.3385994338436497,0.3456637822936055,0.3528430884382674,0.36013374628881456,0.36753183933226063,0.37503314408758337,0.38263313536051513,0.3903269932285346,0.3981096117723113,0.405975609553604,0.4139193418225966,0.42193491442011344,0.43001619932232193,0.4381568517577029,0.446350328808488,0.454589909391767,0.4628687154993053,0.4711797345600831,0.47951584277594816,0.48786982926879496,0.4962344208675833,0.504602307355484,0.5129661669916019,0.5213186921182653,0.5296526146637606,0.5379607313517324,0.5462359284321939,0.5544712057551259,0.5626597000159065,0.5707947070120986,0.5788697027632768,0.586878363359353,0.5948145834180083,0.6026724930481002,0.6104464732329847,0.6181311695653043,0.6257215042826447,0.6332126865712656,0.6406002211226275,0.6478799149443696,0.6550478824435727,0.6621005488153135,0.6690346517835489,0.6758472417541074,0.6825356804508947,0.6890976381162865,0.6955310893650131,0.7018343077876497,0.7080058594051007,0.7140445950792645,0.7199496419874267,0.72572039426896,0.7313565029526626,0.7368578652717028,0.7422246134707049,0.7474571032062077,0.7525559016376057,0.7575217753009073,0.7623556778523443,0.7670587377631131,0.7716322460404988,0.7760776440443701,0.7803965114616772,0.7845905544952088,0.7886615943165318,0.7926115558268515,0.7964424567635111,0.8001563971840802,0.8037555493544745,0.8072421480623625,0.810618481372249,0.813886881834114,0.8170497181533275,0.8201093873257699,0.8230683072386488,0.8259289097344318,0.8286936341325762,0.8313649212013411,0.833945207569883,0.836436920569059,0.8388424734878631,0.8411642612311867,0.8434046563636076,0.8455660055231439,0.8476506261883461,0.8496608037817258,0.8515987890923044,0.8534667959999981,0.8552669994846239,0.8570015339024812,0.8586724915137435,0.8602819212442462,0.8618318276656909,0.8633241701787625,0.8647608623841947,0.8661437716273805,0.8674747187027282,0.8687554777045662,0.869987776012039,0.8711732943960615,0.8723136672370364,0.8734104828426641,0.874465283855802,0.8754795677429297,0.8764547873543801,0.8773923515480664,0.8782936258689954,0.8791599332773964,0.8799925549188086,0.8807927309299669,0.8815616612747921,0.8823005066052454,0.8830103891422267,0.8836923935721009,0.8843475679548162,0.8849769246399327,0.8855814411872175,0.8861620612887775,0.8867196956899914,0.8872552231067824,0.8877694911370229,0.8882633171641062,0.888737489250938,0.889192767022802,0.8896298825377497,0.8900495411433326,0.8904524223186584,0.8908391805009009,0.8912104458955256],"type":"scatter"},{"hovertemplate":"\u003cb\u003e%{fullData.name}\u003c\u002fb\u003e\u003cbr\u003ex=%{x:.2f}\u003cbr\u003ey=%{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","line":{"color":"#2563eb","shape":"spline","smoothing":0.6,"width":2},"mode":"lines","name":"Improved","showlegend":true,"x":[0.0,0.0041841004184100415,0.008368200836820083,0.012552301255230124,0.016736401673640166,0.02092050209205021,0.025104602510460247,0.02928870292887029,0.03347280334728033,0.03765690376569037,0.04184100418410042,0.046025104602510455,0.050209205020920494,0.05439330543933054,0.05857740585774058,0.06276150627615062,0.06694560669456066,0.0711297071129707,0.07531380753138074,0.0794979079497908,0.08368200836820083,0.08786610878661087,0.09205020920502091,0.09623430962343095,0.10041841004184099,0.10460251046025104,0.10878661087866108,0.11297071129707112,0.11715481171548116,0.1213389121338912,0.12552301255230125,0.12970711297071127,0.13389121338912133,0.13807531380753138,0.1422594142259414,0.14644351464435146,0.15062761506276148,0.15481171548117154,0.1589958158995816,0.1631799163179916,0.16736401673640167,0.1715481171548117,0.17573221757322174,0.1799163179916318,0.18410041841004182,0.18828451882845187,0.1924686192468619,0.19665271966527195,0.20083682008368198,0.20502092050209203,0.20920502092050208,0.2133891213389121,0.21757322175732216,0.22175732217573219,0.22594142259414224,0.2301255230125523,0.23430962343096232,0.23849372384937237,0.2426778242677824,0.24686192468619245,0.2510460251046025,0.25523012552301255,0.25941422594142255,0.2635983263598326,0.26778242677824265,0.2719665271966527,0.27615062761506276,0.28033472803347276,0.2845188284518828,0.28870292887029286,0.2928870292887029,0.29707112970711297,0.30125523012552297,0.305439330543933,0.30962343096234307,0.3138075313807531,0.3179916317991632,0.3221757322175732,0.3263598326359832,0.3305439330543933,0.33472803347280333,0.3389121338912134,0.3430962343096234,0.34728033472803344,0.3514644351464435,0.35564853556485354,0.3598326359832636,0.3640167364016736,0.36820083682008364,0.3723849372384937,0.37656903765690375,0.3807531380753138,0.3849372384937238,0.38912133891213385,0.3933054393305439,0.39748953974895396,0.40167364016736395,0.405857740585774,0.41004184100418406,0.4142259414225941,0.41841004184100417,0.42259414225941416,0.4267782426778242,0.43096234309623427,0.4351464435146443,0.4393305439330544,0.44351464435146437,0.4476987447698744,0.4518828451882845,0.45606694560669453,0.4602510460251046,0.4644351464435146,0.46861924686192463,0.4728033472803347,0.47698744769874474,0.4811715481171548,0.4853556485355648,0.48953974895397484,0.4937238493723849,0.49790794979079495,0.502092050209205,0.506276150627615,0.5104602510460251,0.5146443514644351,0.5188284518828451,0.5230125523012552,0.5271966527196652,0.5313807531380753,0.5355648535564853,0.5397489539748953,0.5439330543933054,0.5481171548117154,0.5523012552301255,0.5564853556485355,0.5606694560669455,0.5648535564853556,0.5690376569037656,0.5732217573221757,0.5774058577405857,0.5815899581589957,0.5857740585774058,0.5899581589958158,0.5941422594142259,0.5983263598326359,0.6025104602510459,0.606694560669456,0.610878661087866,0.6150627615062761,0.6192468619246861,0.6234309623430961,0.6276150627615062,0.6317991631799162,0.6359832635983264,0.6401673640167364,0.6443514644351463,0.6485355648535565,0.6527196652719665,0.6569037656903766,0.6610878661087866,0.6652719665271966,0.6694560669456067,0.6736401673640167,0.6778242677824268,0.6820083682008368,0.6861924686192468,0.6903765690376569,0.6945606694560669,0.698744769874477,0.702928870292887,0.707112970711297,0.7112970711297071,0.7154811715481171,0.7196652719665272,0.7238493723849372,0.7280334728033472,0.7322175732217573,0.7364016736401673,0.7405857740585774,0.7447698744769874,0.7489539748953974,0.7531380753138075,0.7573221757322175,0.7615062761506276,0.7656903765690376,0.7698744769874476,0.7740585774058577,0.7782426778242677,0.7824267782426778,0.7866108786610878,0.7907949790794978,0.7949790794979079,0.7991631799163179,0.8033472803347279,0.807531380753138,0.811715481171548,0.8158995815899581,0.8200836820083681,0.8242677824267781,0.8284518828451882,0.8326359832635982,0.8368200836820083,0.8410041841004183,0.8451882845188283,0.8493723849372384,0.8535564853556484,0.8577405857740585,0.8619246861924685,0.8661087866108785,0.8702928870292886,0.8744769874476986,0.8786610878661087,0.8828451882845187,0.8870292887029287,0.8912133891213389,0.8953974895397488,0.899581589958159,0.903765690376569,0.907949790794979,0.9121338912133891,0.9163179916317991,0.9205020920502092,0.9246861924686192,0.9288702928870292,0.9330543933054393,0.9372384937238493,0.9414225941422594,0.9456066945606694,0.9497907949790794,0.9539748953974895,0.9581589958158995,0.9623430962343096,0.9665271966527196,0.9707112970711296,0.9748953974895397,0.9790794979079497,0.9832635983263598,0.9874476987447698,0.9916317991631798,0.9958158995815899,1.0],"y":[0.14060501089565672,0.14088283778515465,0.14117437668125757,0.14148029457499495,0.14180128973406453,0.14213809304132727,0.1424914693767342,0.14286221904252114,0.14325117923128,0.14365922553626193,0.14408727350297595,0.14453628022082632,0.14500724595316689,0.14550121580375022,0.1460192814170989,0.14656258270983058,0.14713230962941984,0.14772970393627682,0.14835606100435697,0.1490127316347919,0.14970112387623702,0.15042270484476727,0.151179002535215,0.15197160761482742,0.15280217518902647,0.15367242652787275,0.15458415074057302,0.15553920638401897,0.1565395229899072,0.15758710249346727,0.15868402054521485,0.15983242768545844,0.1610345503595203,0.16229269174979818,0.16360923239889855,0.16498663059612784,0.1664274224976533,0.16793422194865357,0.16950971997379546,0.17115668390042355,0.17287795607696416,0.17467645214725785,0.17655515883988954,0.17851713123012414,0.1805654894308331,0.18270341466786982,0.18493414469478012,0.18726096850159143,0.18968722027278023,0.1922162725504525,0.1948515285603704,0.19759641366080336,0.20045436587736357,0.20342882549109012,0.20652322365216083,0.20974096999781483,0.21308543926043932,0.21655995686037258,0.2201677834878503,0.22391209868971296,0.22779598348899335,0.23182240207931556,0.23599418265109512,0.24031399742276002,0.2447843419674832,0.24940751394406385,0.25418559135939783,0.25912041050917184,0.264213543762687,0.2694662773766969,0.2748795895414215,0.2804541288790112,0.2861901936301986,0.2920877117781588,0.29814622236917493,0.30436485829703797,0.31074233082165825,0.317276916091671,0.32396644393541485,0.3308082891742017,0.3377993656960141,0.34493612350649583,0.35221454894733234,0.3596301682399747,0.36717805447542784,0.37485283812895587,0.38264872113267095,0.39055949448984884,0.39857855936337727,0.40669895151805197,0.4149133689436421,0.4232142024339775,0.43159356884802497,0.4400433467332665,0.4485552139508604,0.4571206869071671,0.46573116096821093,0.47437795161332363,0.48305233587216456,0.49174559358589653,0.5004490480386277,0.5091541055191895,0.5178522933955252,0.5265352963138058,0.535194990171079,0.5438234735527685,0.5524130963735796,0.5609564855110812,0.5694465672741473,0.5778765866022512,0.586240122945051,0.5945311028235718,0.6027438091235013,0.6108728872166885,0.6189133480480735,0.6268605683613184,0.6347102882669134,0.6424586063811748,0.6501019727832414,0.657637180049939,0.665061352635402,0.6723719348639443,0.679566677801265,0.6866436252611693,0.6936010991931518,0.7004376846810229,0.7071522147649059,0.7137437552790105,0.720211589876207,0.7265552053881889,0.7327742776474424,0.7388686578748471,0.7448383597149452,0.7506835469801073,0.7564045221453197,0.7620017156173483,0.7674756757858036,0.7728270598492599,0.778056625397137,0.7831652227175776,0.7881537877929914,0.7930233359382668,0.7977749560317459,0.8024098052858115,0.8069291045022122,0.8113341337568697,0.8156262284597393,0.8198067757371317,0.8238772110865894,0.8278390152577807,0.8316937113167614,0.8354428618552057,0.8390880663106919,0.842630958368713,0.8460732034216532,0.8494164960644374,0.852662557610846,0.8558131336185033,0.858869991414269,0.8618349176151412,0.8647097156427792,0.8674962032323732,0.870196209938834,0.872811574645113,0.8753441430789634,0.8777957653455672,0.8801682934842785,0.8824635790582229,0.8846834707857396,0.8868298122226409,0.8889044395040612,0.8909091791542693,0.892845845972296,0.8947162410005652,0.8965221495829927,0.8982653395181989,0.8999475593126457,0.901570536537641,0.9031359762932827,0.904645559781565,0.9061009429900344,0.9075037554865942,0.9088555993253137,0.9101580480623906,0.9114126458807956,0.9126209068215325,0.913784314118947,0.9149043196370531,0.9159823434034584,0.9170197732371421,0.918017964466056,0.918978239730313,0.9199018888665516,0.9207901688689508,0.9216443039222985,0.9224654855024788,0.9232548725397511,0.9240135916402323,0.9247427373610504,0.9254433725347414,0.9261165286385616,0.9267632062045259,0.9273843752661248,0.9279809758378323,0.9285539184236826,0.9291040845513728,0.9296323273285203,0.9301394720178946,0.9306263166286217,0.9310936325205441,0.9315421650191003,0.9319726340382659,0.9323857347092799,0.9327821380130337,0.9331624914141858,0.9335274194952079,0.9338775245887276,0.9342133874066783,0.9345355676649034,0.9348446047019983,0.9351410180912925,0.9354253082449973,0.9356979570096523,0.935959428252111,0.9362101684353965,0.9364506071838582,0.9366811578371357,0.9369022179925198,0.9371141700353723,0.9373173816573281,0.9375122063620713,0.9376989839585275,0.9378780410413667,0.9380496914587627,0.9382142367673885,0.9383719666746729,0.9385231594683747,0.9386680824335623,0.9388069922571125,0.9389401354198723],"type":"scatter"},{"hovertemplate":"\u003cb\u003e%{fullData.name}\u003c\u002fb\u003e\u003cbr\u003ex=%{x:.2f}\u003cbr\u003ey=%{y:.3f}\u003cextra\u003e\u003c\u002fextra\u003e","line":{"color":"#4b5563","dash":"dash","width":2},"mode":"lines","name":"Target","showlegend":true,"x":[0.0,0.0041841004184100415,0.008368200836820083,0.012552301255230124,0.016736401673640166,0.02092050209205021,0.025104602510460247,0.02928870292887029,0.03347280334728033,0.03765690376569037,0.04184100418410042,0.046025104602510455,0.050209205020920494,0.05439330543933054,0.05857740585774058,0.06276150627615062,0.06694560669456066,0.0711297071129707,0.07531380753138074,0.0794979079497908,0.08368200836820083,0.08786610878661087,0.09205020920502091,0.09623430962343095,0.10041841004184099,0.10460251046025104,0.10878661087866108,0.11297071129707112,0.11715481171548116,0.1213389121338912,0.12552301255230125,0.12970711297071127,0.13389121338912133,0.13807531380753138,0.1422594142259414,0.14644351464435146,0.15062761506276148,0.15481171548117154,0.1589958158995816,0.1631799163179916,0.16736401673640167,0.1715481171548117,0.17573221757322174,0.1799163179916318,0.18410041841004182,0.18828451882845187,0.1924686192468619,0.19665271966527195,0.20083682008368198,0.20502092050209203,0.20920502092050208,0.2133891213389121,0.21757322175732216,0.22175732217573219,0.22594142259414224,0.2301255230125523,0.23430962343096232,0.23849372384937237,0.2426778242677824,0.24686192468619245,0.2510460251046025,0.25523012552301255,0.25941422594142255,0.2635983263598326,0.26778242677824265,0.2719665271966527,0.27615062761506276,0.28033472803347276,0.2845188284518828,0.28870292887029286,0.2928870292887029,0.29707112970711297,0.30125523012552297,0.305439330543933,0.30962343096234307,0.3138075313807531,0.3179916317991632,0.3221757322175732,0.3263598326359832,0.3305439330543933,0.33472803347280333,0.3389121338912134,0.3430962343096234,0.34728033472803344,0.3514644351464435,0.35564853556485354,0.3598326359832636,0.3640167364016736,0.36820083682008364,0.3723849372384937,0.37656903765690375,0.3807531380753138,0.3849372384937238,0.38912133891213385,0.3933054393305439,0.39748953974895396,0.40167364016736395,0.405857740585774,0.41004184100418406,0.4142259414225941,0.41841004184100417,0.42259414225941416,0.4267782426778242,0.43096234309623427,0.4351464435146443,0.4393305439330544,0.44351464435146437,0.4476987447698744,0.4518828451882845,0.45606694560669453,0.4602510460251046,0.4644351464435146,0.46861924686192463,0.4728033472803347,0.47698744769874474,0.4811715481171548,0.4853556485355648,0.48953974895397484,0.4937238493723849,0.49790794979079495,0.502092050209205,0.506276150627615,0.5104602510460251,0.5146443514644351,0.5188284518828451,0.5230125523012552,0.5271966527196652,0.5313807531380753,0.5355648535564853,0.5397489539748953,0.5439330543933054,0.5481171548117154,0.5523012552301255,0.5564853556485355,0.5606694560669455,0.5648535564853556,0.5690376569037656,0.5732217573221757,0.5774058577405857,0.5815899581589957,0.5857740585774058,0.5899581589958158,0.5941422594142259,0.5983263598326359,0.6025104602510459,0.606694560669456,0.610878661087866,0.6150627615062761,0.6192468619246861,0.6234309623430961,0.6276150627615062,0.6317991631799162,0.6359832635983264,0.6401673640167364,0.6443514644351463,0.6485355648535565,0.6527196652719665,0.6569037656903766,0.6610878661087866,0.6652719665271966,0.6694560669456067,0.6736401673640167,0.6778242677824268,0.6820083682008368,0.6861924686192468,0.6903765690376569,0.6945606694560669,0.698744769874477,0.702928870292887,0.707112970711297,0.7112970711297071,0.7154811715481171,0.7196652719665272,0.7238493723849372,0.7280334728033472,0.7322175732217573,0.7364016736401673,0.7405857740585774,0.7447698744769874,0.7489539748953974,0.7531380753138075,0.7573221757322175,0.7615062761506276,0.7656903765690376,0.7698744769874476,0.7740585774058577,0.7782426778242677,0.7824267782426778,0.7866108786610878,0.7907949790794978,0.7949790794979079,0.7991631799163179,0.8033472803347279,0.807531380753138,0.811715481171548,0.8158995815899581,0.8200836820083681,0.8242677824267781,0.8284518828451882,0.8326359832635982,0.8368200836820083,0.8410041841004183,0.8451882845188283,0.8493723849372384,0.8535564853556484,0.8577405857740585,0.8619246861924685,0.8661087866108785,0.8702928870292886,0.8744769874476986,0.8786610878661087,0.8828451882845187,0.8870292887029287,0.8912133891213389,0.8953974895397488,0.899581589958159,0.903765690376569,0.907949790794979,0.9121338912133891,0.9163179916317991,0.9205020920502092,0.9246861924686192,0.9288702928870292,0.9330543933054393,0.9372384937238493,0.9414225941422594,0.9456066945606694,0.9497907949790794,0.9539748953974895,0.9581589958158995,0.9623430962343096,0.9665271966527196,0.9707112970711296,0.9748953974895397,0.9790794979079497,0.9832635983263598,0.9874476987447698,0.9916317991631798,0.9958158995815899,1.0],"y":[0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97,0.97],"type":"scatter"}], {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"margin":{"l":40,"r":28,"t":20,"b":40},"legend":{"orientation":"v","x":1,"y":0,"xanchor":"right","yanchor":"bottom","bgcolor":"rgba(255,255,255,0)","borderwidth":0},"hoverlabel":{"font":{"color":"#111827","size":12},"bgcolor":"white","bordercolor":"rgba(0,0,0,0.15)","align":"left","namelength":-1},"xaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.55)"},"showgrid":false,"zeroline":false,"showline":true,"linecolor":"rgba(0,0,0,0.25)","linewidth":1,"ticks":"outside","ticklen":6,"tickcolor":"rgba(0,0,0,0.25)","title":{},"automargin":true,"fixedrange":true},"yaxis":{"tickfont":{"size":12,"color":"rgba(0,0,0,0.55)"},"showgrid":false,"zeroline":false,"showline":true,"linecolor":"rgba(0,0,0,0.25)","linewidth":1,"ticks":"outside","ticklen":6,"tickcolor":"rgba(0,0,0,0.25)","title":{},"tickformat":".2f","rangemode":"tozero","automargin":true,"fixedrange":true},"autosize":true,"paper_bgcolor":"rgba(0,0,0,0)","plot_bgcolor":"rgba(0,0,0,0)","hovermode":"x unified"}, {"displayModeBar": false, "responsive": true, "scrollZoom": false, "doubleClick": false, "modeBarButtonsToRemove": ["zoom2d", "pan2d", "select2d", "lasso2d", "zoomIn2d", "zoomOut2d", "autoScale2d", "resetScale2d", "toggleSpikelines"]} ).then(function(){
|
4 |
+
|
5 |
+
(function(){
|
6 |
+
function attach(gd){
|
7 |
+
function round(){
|
8 |
+
try {
|
9 |
+
var root = gd && gd.parentNode ? gd.parentNode : document;
|
10 |
+
var rects = root.querySelectorAll('.hoverlayer .hovertext rect');
|
11 |
+
rects.forEach(function(r){ r.setAttribute('rx', 8); r.setAttribute('ry', 8); });
|
12 |
+
} catch(e) {}
|
13 |
+
}
|
14 |
+
if (gd && gd.on) {
|
15 |
+
gd.on('plotly_hover', round);
|
16 |
+
gd.on('plotly_unhover', round);
|
17 |
+
gd.on('plotly_relayout', round);
|
18 |
+
}
|
19 |
+
setTimeout(round, 0);
|
20 |
+
}
|
21 |
+
var plots = document.querySelectorAll('.js-plotly-plot');
|
22 |
+
plots.forEach(attach);
|
23 |
+
})();
|
24 |
+
|
25 |
+
}) }; </script> </div>
|
26 |
+
<div class="plotly_controls" style="margin-top:12px; display:flex; gap:16px; align-items:center;">
|
27 |
+
<label style="font-size:12px;color:rgba(0,0,0,.65); display:flex; align-items:center; gap:6px; white-space:nowrap; padding:6px 10px;">
|
28 |
+
Dataset
|
29 |
+
<select id="__DSID__" style="font-size:12px; padding:2px 6px;">
|
30 |
+
<option value="0">CIFAR-10</option>
|
31 |
+
<option value="1">CIFAR-100</option>
|
32 |
+
<option value="2">ImageNet-1K</option>
|
33 |
+
</select>
|
34 |
+
</label>
|
35 |
+
<label style="font-size:12px;color:rgba(0,0,0,.65);display:flex;align-items:center;gap:10px; flex:1; padding:6px 10px;">
|
36 |
+
Augmentation α
|
37 |
+
<input id="line-ex-alpha-7f91adbe" type="range" min="0" max="1" step="0.01" value="0.70" style="flex:1;">
|
38 |
+
<span class="alpha-value">0.70</span>
|
39 |
+
</label>
|
40 |
+
</div>
|
41 |
+
</div>
|
42 |
+
<script>
|
43 |
+
(function(){
|
44 |
+
var container = document.getElementById('line-ex-container-7f91adbe');
|
45 |
+
if(!container) return;
|
46 |
+
var gd = container.querySelector('.js-plotly-plot');
|
47 |
+
var slider = document.getElementById('line-ex-alpha-7f91adbe');
|
48 |
+
var dsSelect = document.getElementById('__DSID__');
|
49 |
+
var valueEl = container.querySelector('.alpha-value');
|
50 |
+
var N = 240;
|
51 |
+
var xs = Array.from({length: N}, function(_,i){ return i/(N-1); });
|
52 |
+
function logistic(x, ymin, ymax, k, x0){ return ymin + (ymax - ymin) / (1 + Math.exp(-k*(x - x0))); }
|
53 |
+
function blend(l,e,a){ return (1-a)*l + a*e; }
|
54 |
+
var datasets = [
|
55 |
+
{ name:'CIFAR-10', base:{ymin:0.10,ymax:0.90,k:10.0,x0:0.55}, aug:{ymin:0.15,ymax:0.96,k:12.0,x0:0.40}, target:0.97 },
|
56 |
+
{ name:'CIFAR-100', base:{ymin:0.05,ymax:0.70,k:9.5,x0:0.60}, aug:{ymin:0.08,ymax:0.80,k:11.0,x0:0.45}, target:0.85 },
|
57 |
+
{ name:'ImageNet-1K', base:{ymin:0.02,ymax:0.68,k:8.5,x0:0.65}, aug:{ymin:0.04,ymax:0.75,k:9.5,x0:0.50}, target:0.82 }
|
58 |
+
];
|
59 |
+
var dsi = 0;
|
60 |
+
var yb = xs.map(function(x){ return logistic(x, datasets[dsi].base.ymin, datasets[dsi].base.ymax, datasets[dsi].base.k, datasets[dsi].base.x0); });
|
61 |
+
var ya = xs.map(function(x){ return logistic(x, datasets[dsi].aug.ymin, datasets[dsi].aug.ymax, datasets[dsi].aug.k, datasets[dsi].aug.x0); });
|
62 |
+
var yt = xs.map(function(){ return datasets[dsi].target; });
|
63 |
+
function applyAlpha(a){
|
64 |
+
var yi = yb.map(function(v,i){ return blend(v, ya[i], a); });
|
65 |
+
Plotly.restyle(gd, {y:[yi]}, [1]); // only Improved changes with α
|
66 |
+
if(valueEl) valueEl.textContent = a.toFixed(2);
|
67 |
+
}
|
68 |
+
function applyDataset(){
|
69 |
+
var d = datasets[dsi];
|
70 |
+
yb = xs.map(function(x){ return logistic(x, d.base.ymin, d.base.ymax, d.base.k, d.base.x0); });
|
71 |
+
ya = xs.map(function(x){ return logistic(x, d.aug.ymin, d.aug.ymax, d.aug.k, d.aug.x0); });
|
72 |
+
yt = xs.map(function(){ return d.target; });
|
73 |
+
var a = parseFloat(slider.value)||0;
|
74 |
+
var yi = yb.map(function(v,i){ return blend(v, ya[i], a); });
|
75 |
+
Plotly.restyle(gd, {y:[yb]}, [0]); // Baseline
|
76 |
+
Plotly.restyle(gd, {y:[yi]}, [1]); // Improved (blended)
|
77 |
+
Plotly.restyle(gd, {y:[yt]}, [2]); // Target
|
78 |
+
}
|
79 |
+
var initA = parseFloat(slider.value)||0;
|
80 |
+
slider.addEventListener('input', function(e){ applyAlpha(parseFloat(e.target.value)||0); });
|
81 |
+
dsSelect.addEventListener('change', function(e){ dsi = parseInt(e.target.value)||0; applyDataset(); });
|
82 |
+
setTimeout(function(){ applyDataset(); applyAlpha(initA); }, 0);
|
83 |
+
})();
|
84 |
+
</script>
|
app/src/content/fragments/palettes.html
CHANGED
@@ -12,7 +12,7 @@
|
|
12 |
.palettes .palette-card__swatches .sw:first-child { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }
|
13 |
.palettes .palette-card__swatches .sw:last-child { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }
|
14 |
.palettes .palette-card__footer { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
|
15 |
-
.palettes .copy-btn { width: 100%; margin: 0; padding: 8px 10px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--surface-bg); color: var(--text-color); font-size: 12px; cursor: pointer; }
|
16 |
.palettes .palettes__meta { display: flex; align-items: center; gap: 10px; justify-content: space-between; }
|
17 |
.palettes .current-color { display: flex; align-items: center; gap: 10px; }
|
18 |
.palettes .current-swatch { width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--border-color); }
|
@@ -24,10 +24,10 @@
|
|
24 |
<div class="palettes__controls" style="display:flex; flex-direction:column; gap:10px; margin-bottom:24px;">
|
25 |
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
|
26 |
<div style="font-weight:700;">Pick a hue</div>
|
27 |
-
<div class="hue-value" style="font-variant-numeric: tabular-nums; color: var(--muted-color);">H
|
28 |
</div>
|
29 |
<div class="hue-slider" style="position:relative; height:18px; border-radius:10px; border:1px solid var(--border-color); background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); cursor: ew-resize; touch-action: none;">
|
30 |
-
<div class="hue-knob" style="position:absolute; top:50%; left:
|
31 |
</div>
|
32 |
<div class="palettes__meta">
|
33 |
<div class="current-color">
|
@@ -101,6 +101,20 @@
|
|
101 |
const simSelect = root.querySelector('.cvd-select');
|
102 |
console.log('[palettes] elements', { root: !!root, grid: !!grid, slider: !!slider, knob: !!knob, hueValue: !!hueValue });
|
103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
// Cards data with full descriptions
|
105 |
const cards = [
|
106 |
{ key: 'categorical', title: 'Categorical', desc: 'Categorical colors help users map non-numeric meaning to objects in a visualization. These are designed to be visually distinct from one another. Maximum of six.', generator: (base) => {
|
@@ -168,7 +182,19 @@
|
|
168 |
// Rendering
|
169 |
const renderPalettes = (h) => {
|
170 |
console.log('[palettes] renderPalettes', h);
|
171 |
-
const base = chroma.hsl(h, 0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
const uniformText = (bg) => (chroma(bg).luminance() > 0.45 ? '#111' : '#fff');
|
173 |
|
174 |
// Update current swatch + name
|
@@ -260,12 +286,16 @@
|
|
260 |
};
|
261 |
|
262 |
// Hue slider behavior
|
263 |
-
let hue =
|
|
|
264 |
const setHue = (h) => { hue = (h + 360) % 360; const pct = hue / 360 * 100; if (knob) knob.style.left = pct + '%'; if (hueValue) hueValue.textContent = `H ${Math.round(hue)}°`; console.log('[palettes] setHue', hue, pct); renderPalettes(hue); };
|
265 |
const getHueFromEvent = (ev) => { const rect = slider.getBoundingClientRect(); const clientX = ev.touches ? ev.touches[0].clientX : ev.clientX; const x = clientX - rect.left; const t = Math.max(0, Math.min(1, x / rect.width)); const h = t * 360; console.log('[palettes] getHueFromEvent', { clientX, left: rect.left, width: rect.width, t, h }); return h; };
|
266 |
-
const onDown = (ev) => { console.log('[palettes] onDown', ev.type); ev.preventDefault();
|
267 |
if (slider) { slider.addEventListener('mousedown', onDown); slider.addEventListener('touchstart', onDown, { passive: false }); console.log('[palettes] listeners attached'); } else { console.warn('[palettes] slider not found'); }
|
268 |
|
|
|
|
|
|
|
269 |
// Color-vision simulation filters (SVG)
|
270 |
const injectFilters = () => {
|
271 |
if (document.getElementById('cvd-filters')) return;
|
@@ -290,7 +320,10 @@
|
|
290 |
if (simSelect) simSelect.addEventListener('change', () => applySimulation(simSelect.value));
|
291 |
|
292 |
console.log('[palettes] initial render');
|
293 |
-
|
|
|
|
|
|
|
294 |
applySimulation('none');
|
295 |
|
296 |
// Fixed 3 columns layout
|
|
|
12 |
.palettes .palette-card__swatches .sw:first-child { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }
|
13 |
.palettes .palette-card__swatches .sw:last-child { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }
|
14 |
.palettes .palette-card__footer { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
|
15 |
+
.palettes .copy-btn { width: 100%; margin: 0; padding: 8px 10px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--surface-bg); color: var(--text-color)!important; font-size: 12px; cursor: pointer; }
|
16 |
.palettes .palettes__meta { display: flex; align-items: center; gap: 10px; justify-content: space-between; }
|
17 |
.palettes .current-color { display: flex; align-items: center; gap: 10px; }
|
18 |
.palettes .current-swatch { width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--border-color); }
|
|
|
24 |
<div class="palettes__controls" style="display:flex; flex-direction:column; gap:10px; margin-bottom:24px;">
|
25 |
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
|
26 |
<div style="font-weight:700;">Pick a hue</div>
|
27 |
+
<div class="hue-value" style="font-variant-numeric: tabular-nums; color: var(--muted-color);">H 337°</div>
|
28 |
</div>
|
29 |
<div class="hue-slider" style="position:relative; height:18px; border-radius:10px; border:1px solid var(--border-color); background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); cursor: ew-resize; touch-action: none;">
|
30 |
+
<div class="hue-knob" style="position:absolute; top:50%; left:93.6%; width:14px; height:14px; border-radius:50%; border:2px solid #fff; transform:translate(-50%, -50%); background: var(--surface-bg); z-index: 2;"></div>
|
31 |
</div>
|
32 |
<div class="palettes__meta">
|
33 |
<div class="current-color">
|
|
|
101 |
const simSelect = root.querySelector('.cvd-select');
|
102 |
console.log('[palettes] elements', { root: !!root, grid: !!grid, slider: !!slider, knob: !!knob, hueValue: !!hueValue });
|
103 |
|
104 |
+
// Shared bus to sync with color-picker instances
|
105 |
+
if (!window.__colorPickerBus) {
|
106 |
+
window.__colorPickerBus = (() => {
|
107 |
+
let hue = 337; let adjusting = false; const listeners = new Set();
|
108 |
+
return {
|
109 |
+
get: () => ({ hue, adjusting }),
|
110 |
+
publish: (sourceId, nextHue, isAdjusting) => { hue = ((nextHue % 360) + 360) % 360; adjusting = !!isAdjusting; listeners.forEach(fn => { try { fn({ sourceId, hue, adjusting }); } catch {} }); },
|
111 |
+
subscribe: (fn) => { listeners.add(fn); return () => listeners.delete(fn); }
|
112 |
+
};
|
113 |
+
})();
|
114 |
+
}
|
115 |
+
const bus = window.__colorPickerBus;
|
116 |
+
const instanceId = Math.random().toString(36).slice(2);
|
117 |
+
|
118 |
// Cards data with full descriptions
|
119 |
const cards = [
|
120 |
{ key: 'categorical', title: 'Categorical', desc: 'Categorical colors help users map non-numeric meaning to objects in a visualization. These are designed to be visually distinct from one another. Maximum of six.', generator: (base) => {
|
|
|
182 |
// Rendering
|
183 |
const renderPalettes = (h) => {
|
184 |
console.log('[palettes] renderPalettes', h);
|
185 |
+
const base = chroma.hsl(h, 0.67, 0.72);
|
186 |
+
// Update CSS variables for live theming only during interaction
|
187 |
+
try {
|
188 |
+
if (isAdjusting) {
|
189 |
+
const baseHex = base.hex();
|
190 |
+
const hoverHex = chroma(base).darken(0.6).hex();
|
191 |
+
const rootEl = document.documentElement;
|
192 |
+
rootEl.style.setProperty('--primary', baseHex);
|
193 |
+
rootEl.style.setProperty('--primary-hover', hoverHex);
|
194 |
+
}
|
195 |
+
} catch (e) {
|
196 |
+
console.warn('[palettes] failed updating CSS vars', e);
|
197 |
+
}
|
198 |
const uniformText = (bg) => (chroma(bg).luminance() > 0.45 ? '#111' : '#fff');
|
199 |
|
200 |
// Update current swatch + name
|
|
|
286 |
};
|
287 |
|
288 |
// Hue slider behavior
|
289 |
+
let hue = 337; // initial
|
290 |
+
let isAdjusting = false; // only update theme while interacting
|
291 |
const setHue = (h) => { hue = (h + 360) % 360; const pct = hue / 360 * 100; if (knob) knob.style.left = pct + '%'; if (hueValue) hueValue.textContent = `H ${Math.round(hue)}°`; console.log('[palettes] setHue', hue, pct); renderPalettes(hue); };
|
292 |
const getHueFromEvent = (ev) => { const rect = slider.getBoundingClientRect(); const clientX = ev.touches ? ev.touches[0].clientX : ev.clientX; const x = clientX - rect.left; const t = Math.max(0, Math.min(1, x / rect.width)); const h = t * 360; console.log('[palettes] getHueFromEvent', { clientX, left: rect.left, width: rect.width, t, h }); return h; };
|
293 |
+
const onDown = (ev) => { console.log('[palettes] onDown', ev.type); ev.preventDefault(); isAdjusting = true; const h0 = getHueFromEvent(ev); setHue(h0); bus.publish(instanceId, h0, true); const move = (e) => { e.preventDefault && e.preventDefault(); const hh = getHueFromEvent(e); setHue(hh); bus.publish(instanceId, hh, true); }; const up = () => { console.log('[palettes] onUp'); isAdjusting = false; bus.publish(instanceId, hue, false); window.removeEventListener('mousemove', move); window.removeEventListener('touchmove', move); window.removeEventListener('mouseup', up); window.removeEventListener('touchend', up); }; window.addEventListener('mousemove', move, { passive: false }); window.addEventListener('touchmove', move, { passive: false }); window.addEventListener('mouseup', up, { once: true }); window.addEventListener('touchend', up, { once: true }); };
|
294 |
if (slider) { slider.addEventListener('mousedown', onDown); slider.addEventListener('touchstart', onDown, { passive: false }); console.log('[palettes] listeners attached'); } else { console.warn('[palettes] slider not found'); }
|
295 |
|
296 |
+
// Subscribe to bus to receive updates from other pickers
|
297 |
+
const unsubscribe = bus.subscribe(({ sourceId, hue: H, adjusting }) => { if (sourceId === instanceId) return; isAdjusting = adjusting; setHue(H); });
|
298 |
+
|
299 |
// Color-vision simulation filters (SVG)
|
300 |
const injectFilters = () => {
|
301 |
if (document.getElementById('cvd-filters')) return;
|
|
|
320 |
if (simSelect) simSelect.addEventListener('change', () => applySimulation(simSelect.value));
|
321 |
|
322 |
console.log('[palettes] initial render');
|
323 |
+
// Initialize from shared hue without updating CSS variables
|
324 |
+
const shared = bus.get();
|
325 |
+
isAdjusting = false;
|
326 |
+
setHue(shared && typeof shared.hue === 'number' ? shared.hue : hue);
|
327 |
applySimulation('none');
|
328 |
|
329 |
// Fixed 3 columns layout
|
app/src/pages/index.astro
CHANGED
@@ -7,7 +7,7 @@ import ThemeToggle from '../components/ThemeToggle.astro';
|
|
7 |
import SeoHead from '../components/SeoHead.astro';
|
8 |
import ogDefault from '../assets/images/visual-vocabulary-poster.png';
|
9 |
import 'katex/dist/katex.min.css';
|
10 |
-
import '../styles/global.
|
11 |
const docTitle = articleFM?.title ?? 'Untitled article';
|
12 |
// Allow explicit line breaks in the title via "\n" or YAML newlines
|
13 |
const docTitleHtml = (articleFM?.title ?? 'Untitled article')
|
|
|
7 |
import SeoHead from '../components/SeoHead.astro';
|
8 |
import ogDefault from '../assets/images/visual-vocabulary-poster.png';
|
9 |
import 'katex/dist/katex.min.css';
|
10 |
+
import '../styles/global.css';
|
11 |
const docTitle = articleFM?.title ?? 'Untitled article';
|
12 |
// Allow explicit line breaks in the title via "\n" or YAML newlines
|
13 |
const docTitleHtml = (articleFM?.title ?? 'Untitled article')
|
app/src/styles/{_base.scss → _base.css}
RENAMED
@@ -1,16 +1,16 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
html { box-sizing: border-box; }
|
5 |
*, *::before, *::after { box-sizing: inherit; }
|
6 |
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji; color: var(--text-color); }
|
7 |
-
audio { display: block; }
|
8 |
/* Avoid constraining <main> inside grid; scope container sizing elsewhere if needed */
|
9 |
/* main { max-width: 980px; margin: 24px auto; padding: 16px; } */
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
html { font-size: 14px; line-height: 1.6; }
|
15 |
@media (min-width: 768px) { html { font-size: 16px; } }
|
16 |
@media (min-width: 1024px) { html { font-size: 17px; } }
|
@@ -42,16 +42,16 @@ html { font-size: 14px; line-height: 1.6; }
|
|
42 |
margin: var(--spacing-8) 0 var(--spacing-4);
|
43 |
}
|
44 |
|
45 |
-
.content-grid main a { color:
|
46 |
-
.content-grid main a:hover { border-bottom: 1px solid var(--link-underline-hover); }
|
47 |
|
48 |
/* Do not underline heading links inside the article (not the TOC) */
|
49 |
.content-grid main h2 a,
|
50 |
.content-grid main h3 a,
|
51 |
-
.content-grid main h4 a { border-bottom: none; text-decoration: none; }
|
52 |
.content-grid main h2 a:hover,
|
53 |
.content-grid main h3 a:hover,
|
54 |
-
.content-grid main h4 a:hover { border-bottom: none; text-decoration: none; }
|
55 |
|
56 |
.content-grid main ul,
|
57 |
.content-grid main ol { padding-left: 24px; margin: 0 0 var(--spacing-3); }
|
@@ -119,48 +119,35 @@ html { font-size: 14px; line-height: 1.6; }
|
|
119 |
z-index: 1;
|
120 |
}
|
121 |
|
122 |
-
/* JS fallback chip */
|
123 |
-
.content-grid main pre.has-lang-chip { position: relative; padding-top: 22px; }
|
124 |
-
.content-grid main pre .code-lang-chip {
|
125 |
-
position: absolute;
|
126 |
-
top: 0px; right: 0px;
|
127 |
-
font-size: 10px; line-height: 1;
|
128 |
-
color: rgba(255,255,255,.5);
|
129 |
-
background: rgba(255,255,255,.1);
|
130 |
-
border: none;
|
131 |
-
border-radius: 0px; padding: 6px 6px 4px 4px; pointer-events: none; z-index: 1;
|
132 |
-
}
|
133 |
-
|
134 |
.content-grid main table { border-collapse: collapse; width: 100%; margin: 0 0 var(--spacing-4); }
|
135 |
.content-grid main th, .content-grid main td { border-bottom: 1px solid var(--border-color); padding: 6px 8px; text-align: left; font-size: 15px; }
|
136 |
.content-grid main thead th { border-bottom: 1px solid var(--border-color); }
|
137 |
|
138 |
.content-grid main hr { border: none; border-bottom: 1px solid var(--border-color); margin: var(--spacing-5) 0; }
|
139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
// font-size: 14px;
|
147 |
-
// overflow: auto;
|
148 |
-
// }
|
149 |
-
|
150 |
-
// ============================================================================
|
151 |
-
// Media / Figures
|
152 |
-
// ============================================================================
|
153 |
-
:where(picture, img) {
|
154 |
-
max-width: 100%;
|
155 |
width: 100%;
|
|
|
156 |
height: auto;
|
157 |
display: block;
|
158 |
}
|
159 |
|
160 |
-
|
161 |
-
figcaption { color: var(--muted-color); font-size: 12px; }
|
162 |
-
|
163 |
-
// Inline feature tags
|
164 |
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 16px; }
|
165 |
.tag {
|
166 |
display: inline-flex;
|
@@ -177,47 +164,52 @@ figcaption { color: var(--muted-color); font-size: 12px; }
|
|
177 |
[data-theme="dark"] .tag { background: #1a1f27; border-color: rgba(255,255,255,.15); }
|
178 |
|
179 |
|
180 |
-
|
181 |
-
|
182 |
-
|
|
|
183 |
figure { margin: 12px 0; }
|
184 |
-
figcaption { text-align:
|
185 |
.image-credit { display: block; margin-top: 4px; font-size: 12px; color: var(--muted-color); }
|
186 |
.image-credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
|
187 |
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
appearance: none;
|
193 |
-
background: var(--
|
194 |
-
color:
|
195 |
-
border: 1px solid
|
196 |
border-radius: 6px;
|
197 |
padding: 8px 12px;
|
198 |
font-size: 14px;
|
199 |
line-height: 1;
|
200 |
cursor: pointer;
|
|
|
201 |
transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease, transform .02s ease;
|
202 |
}
|
203 |
-
|
204 |
-
|
205 |
}
|
206 |
-
|
207 |
transform: translateY(1px);
|
208 |
}
|
209 |
-
|
210 |
outline: none;
|
211 |
-
box-shadow: 0 0 0 2px var(--link-underline);
|
212 |
}
|
213 |
-
|
214 |
opacity: .6;
|
215 |
cursor: not-allowed;
|
216 |
}
|
217 |
|
218 |
-
|
219 |
-
|
220 |
-
|
|
|
|
|
|
|
|
|
221 |
@media print {
|
222 |
html, body { background: #fff; }
|
223 |
/* Margins handled by Playwright; avoid extra global margins */
|
@@ -248,3 +240,9 @@ figcaption { text-align: center; font-size: 0.9rem; color: var(--muted-color); m
|
|
248 |
--link-underline-hover: rgba(0,0,0,.4);
|
249 |
}
|
250 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* ============================================================================ */
|
2 |
+
/* Base / Reset */
|
3 |
+
/* ============================================================================ */
|
4 |
html { box-sizing: border-box; }
|
5 |
*, *::before, *::after { box-sizing: inherit; }
|
6 |
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji; color: var(--text-color); }
|
7 |
+
audio { display: block; width: 100%; }
|
8 |
/* Avoid constraining <main> inside grid; scope container sizing elsewhere if needed */
|
9 |
/* main { max-width: 980px; margin: 24px auto; padding: 16px; } */
|
10 |
|
11 |
+
/* ============================================================================ */
|
12 |
+
/* Typography (inspired by Distill) */
|
13 |
+
/* ============================================================================ */
|
14 |
html { font-size: 14px; line-height: 1.6; }
|
15 |
@media (min-width: 768px) { html { font-size: 16px; } }
|
16 |
@media (min-width: 1024px) { html { font-size: 17px; } }
|
|
|
42 |
margin: var(--spacing-8) 0 var(--spacing-4);
|
43 |
}
|
44 |
|
45 |
+
.content-grid main a { color: var(--primary); text-decoration: none; border-bottom: 1px solid var(--link-underline); }
|
46 |
+
.content-grid main a:hover { color: var(--primary-hover); border-bottom: 1px solid var(--link-underline-hover); }
|
47 |
|
48 |
/* Do not underline heading links inside the article (not the TOC) */
|
49 |
.content-grid main h2 a,
|
50 |
.content-grid main h3 a,
|
51 |
+
.content-grid main h4 a { color: inherit; border-bottom: none; text-decoration: none; }
|
52 |
.content-grid main h2 a:hover,
|
53 |
.content-grid main h3 a:hover,
|
54 |
+
.content-grid main h4 a:hover { color: inherit; border-bottom: none; text-decoration: none; }
|
55 |
|
56 |
.content-grid main ul,
|
57 |
.content-grid main ol { padding-left: 24px; margin: 0 0 var(--spacing-3); }
|
|
|
119 |
z-index: 1;
|
120 |
}
|
121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
.content-grid main table { border-collapse: collapse; width: 100%; margin: 0 0 var(--spacing-4); }
|
123 |
.content-grid main th, .content-grid main td { border-bottom: 1px solid var(--border-color); padding: 6px 8px; text-align: left; font-size: 15px; }
|
124 |
.content-grid main thead th { border-bottom: 1px solid var(--border-color); }
|
125 |
|
126 |
.content-grid main hr { border: none; border-bottom: 1px solid var(--border-color); margin: var(--spacing-5) 0; }
|
127 |
|
128 |
+
/*
|
129 |
+
.code-block {
|
130 |
+
background: rgba(120, 120, 120, 0.5);
|
131 |
+
border: 1px solid var(--border-color);
|
132 |
+
border-radius: 6px;
|
133 |
+
padding: var(--spacing-3);
|
134 |
+
font-size: 14px;
|
135 |
+
overflow: auto;
|
136 |
+
}
|
137 |
+
*/
|
138 |
|
139 |
+
/* ============================================================================ */
|
140 |
+
/* Media / Figures */
|
141 |
+
/* ============================================================================ */
|
142 |
+
img,
|
143 |
+
picture {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
width: 100%;
|
145 |
+
max-width: 100%;
|
146 |
height: auto;
|
147 |
display: block;
|
148 |
}
|
149 |
|
150 |
+
/* Inline feature tags */
|
|
|
|
|
|
|
151 |
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 16px; }
|
152 |
.tag {
|
153 |
display: inline-flex;
|
|
|
164 |
[data-theme="dark"] .tag { background: #1a1f27; border-color: rgba(255,255,255,.15); }
|
165 |
|
166 |
|
167 |
+
|
168 |
+
/* ============================================================================ */
|
169 |
+
/* Figures, captions & image credits */
|
170 |
+
/* ============================================================================ */
|
171 |
figure { margin: 12px 0; }
|
172 |
+
figcaption { text-align: left; font-size: 0.9rem; color: var(--muted-color); margin-top: 6px; }
|
173 |
.image-credit { display: block; margin-top: 4px; font-size: 12px; color: var(--muted-color); }
|
174 |
.image-credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
|
175 |
|
176 |
+
/* ============================================================================ */
|
177 |
+
/* Buttons (minimal, clean) */
|
178 |
+
/* ============================================================================ */
|
179 |
+
button, .button {
|
180 |
appearance: none;
|
181 |
+
background: linear-gradient(15deg, var(--primary) 0%, var(--primary-hover) 35%);
|
182 |
+
color: white!important;
|
183 |
+
border: 1px solid transparent;
|
184 |
border-radius: 6px;
|
185 |
padding: 8px 12px;
|
186 |
font-size: 14px;
|
187 |
line-height: 1;
|
188 |
cursor: pointer;
|
189 |
+
display: inline-block;
|
190 |
transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease, transform .02s ease;
|
191 |
}
|
192 |
+
button:hover, .button:hover {
|
193 |
+
filter: brightness(96%);
|
194 |
}
|
195 |
+
button:active, .button:active {
|
196 |
transform: translateY(1px);
|
197 |
}
|
198 |
+
button:focus-visible, .button:focus-visible {
|
199 |
outline: none;
|
|
|
200 |
}
|
201 |
+
button:disabled, .button:disabled {
|
202 |
opacity: .6;
|
203 |
cursor: not-allowed;
|
204 |
}
|
205 |
|
206 |
+
.button-group .button {
|
207 |
+
margin: 5px;
|
208 |
+
}
|
209 |
+
|
210 |
+
/* ============================================================================ */
|
211 |
+
/* Print styles */
|
212 |
+
/* ========================================================================= */
|
213 |
@media print {
|
214 |
html, body { background: #fff; }
|
215 |
/* Margins handled by Playwright; avoid extra global margins */
|
|
|
240 |
--link-underline-hover: rgba(0,0,0,.4);
|
241 |
}
|
242 |
}
|
243 |
+
|
244 |
+
.muted {
|
245 |
+
color: var(--muted-color);
|
246 |
+
}
|
247 |
+
|
248 |
+
|
app/src/styles/{_layout.scss → _layout.css}
RENAMED
@@ -1,18 +1,18 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
.content-grid { max-width: 1280px; margin: 0 auto; padding: 0 16px; margin-top: 40px; display: grid; grid-template-columns: 220px minmax(0, 680px) 260px; gap: 32px; align-items: start; }
|
5 |
.content-grid > main { max-width: 100%; margin: 0; padding: 0; }
|
6 |
|
7 |
-
|
8 |
.toc { position: sticky; top: 24px; }
|
9 |
.toc nav { border-left: 1px solid var(--border-color); padding-left: 16px; font-size: 13px; }
|
10 |
.toc .title { font-weight: 600; font-size: 14px; margin-bottom: 8px; }
|
11 |
|
12 |
-
|
13 |
main > nav:first-of-type { display: none; }
|
14 |
|
15 |
-
|
16 |
.toc nav ul { margin: 0 0 6px; padding-left: 1em; }
|
17 |
.toc nav li { list-style: none; margin: .25em 0; }
|
18 |
.toc nav a { color: var(--text-color); text-decoration: none; border-bottom: none; }
|
@@ -20,7 +20,7 @@ main > nav:first-of-type { display: none; }
|
|
20 |
.toc nav a:hover { text-decoration: underline solid var(--muted-color); }
|
21 |
.toc nav a.active { text-decoration: underline; }
|
22 |
|
23 |
-
|
24 |
.toc-mobile { display: none; margin: 8px 0 16px; }
|
25 |
.toc-mobile > summary { cursor: pointer; list-style: none; padding: 8px 12px; border: 1px solid var(--border-color); border-radius: 8px; color: var(--text-color); font-weight: 600; }
|
26 |
.toc-mobile[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
|
@@ -32,11 +32,11 @@ main > nav:first-of-type { display: none; }
|
|
32 |
.toc-mobile nav a:hover { text-decoration: underline solid var(--muted-color); }
|
33 |
.toc-mobile nav a.active { text-decoration: underline; }
|
34 |
|
35 |
-
|
36 |
.right-aside { position: sticky; top: 24px; }
|
37 |
.right-aside .aside-card { background: var(--surface-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; margin-bottom: 10px; font-size: 0.9rem; color: var(--text-color); }
|
38 |
|
39 |
-
|
40 |
@media (max-width: 1100px) {
|
41 |
.content-grid { grid-template-columns: 1fr; }
|
42 |
.toc { position: static; display: none; }
|
@@ -55,7 +55,7 @@ main > nav:first-of-type { display: none; }
|
|
55 |
right: -260px; /* push into the right grid column (width 260 + gap 32) */
|
56 |
width: 260px;
|
57 |
border-radius: 8px;
|
58 |
-
padding: 0
|
59 |
font-size: 0.9rem;
|
60 |
color: var(--muted-color);
|
61 |
}
|
@@ -68,18 +68,18 @@ main > nav:first-of-type { display: none; }
|
|
68 |
}
|
69 |
|
70 |
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
.wide,
|
84 |
.full-bleed { box-sizing: border-box; }
|
85 |
|
@@ -106,3 +106,5 @@ main > nav:first-of-type { display: none; }
|
|
106 |
}
|
107 |
}
|
108 |
|
|
|
|
|
|
1 |
+
/* ============================================================================ */
|
2 |
+
/* Layout – 3-column grid (TOC / Article / Aside) */
|
3 |
+
/* ============================================================================ */
|
4 |
.content-grid { max-width: 1280px; margin: 0 auto; padding: 0 16px; margin-top: 40px; display: grid; grid-template-columns: 220px minmax(0, 680px) 260px; gap: 32px; align-items: start; }
|
5 |
.content-grid > main { max-width: 100%; margin: 0; padding: 0; }
|
6 |
|
7 |
+
/* TOC (left column) */
|
8 |
.toc { position: sticky; top: 24px; }
|
9 |
.toc nav { border-left: 1px solid var(--border-color); padding-left: 16px; font-size: 13px; }
|
10 |
.toc .title { font-weight: 600; font-size: 14px; margin-bottom: 8px; }
|
11 |
|
12 |
+
/* Hide in-article TOC (duplicated by sticky aside) */
|
13 |
main > nav:first-of-type { display: none; }
|
14 |
|
15 |
+
/* TOC look & feel */
|
16 |
.toc nav ul { margin: 0 0 6px; padding-left: 1em; }
|
17 |
.toc nav li { list-style: none; margin: .25em 0; }
|
18 |
.toc nav a { color: var(--text-color); text-decoration: none; border-bottom: none; }
|
|
|
20 |
.toc nav a:hover { text-decoration: underline solid var(--muted-color); }
|
21 |
.toc nav a.active { text-decoration: underline; }
|
22 |
|
23 |
+
/* Mobile TOC accordion */
|
24 |
.toc-mobile { display: none; margin: 8px 0 16px; }
|
25 |
.toc-mobile > summary { cursor: pointer; list-style: none; padding: 8px 12px; border: 1px solid var(--border-color); border-radius: 8px; color: var(--text-color); font-weight: 600; }
|
26 |
.toc-mobile[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
|
|
|
32 |
.toc-mobile nav a:hover { text-decoration: underline solid var(--muted-color); }
|
33 |
.toc-mobile nav a.active { text-decoration: underline; }
|
34 |
|
35 |
+
/* Right aside (notes) */
|
36 |
.right-aside { position: sticky; top: 24px; }
|
37 |
.right-aside .aside-card { background: var(--surface-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; margin-bottom: 10px; font-size: 0.9rem; color: var(--text-color); }
|
38 |
|
39 |
+
/* Responsive – collapse to single column */
|
40 |
@media (max-width: 1100px) {
|
41 |
.content-grid { grid-template-columns: 1fr; }
|
42 |
.toc { position: static; display: none; }
|
|
|
55 |
right: -260px; /* push into the right grid column (width 260 + gap 32) */
|
56 |
width: 260px;
|
57 |
border-radius: 8px;
|
58 |
+
padding: 0 30px;
|
59 |
font-size: 0.9rem;
|
60 |
color: var(--muted-color);
|
61 |
}
|
|
|
68 |
}
|
69 |
|
70 |
|
71 |
+
/* ============================================================================ */
|
72 |
+
/* Width helpers – slightly wider than main column, and full-bleed to viewport */
|
73 |
+
/* ---------------------------------------------------------------------------- */
|
74 |
+
/* Usage in MDX: */
|
75 |
+
/* <div className="wide"> ... </div> */
|
76 |
+
/* <div className="full-bleed"> ... </div> */
|
77 |
+
/* These center the content relative to the viewport while keeping it responsive. */
|
78 |
+
/* */
|
79 |
+
/* Notes: */
|
80 |
+
/* - These helpers work inside the main article column; they break out visually */
|
81 |
+
/* to be wider or fully span the viewport. On small screens, they fall back to 100%. */
|
82 |
+
/* - Adjust the target width in .wide if desired. */
|
83 |
.wide,
|
84 |
.full-bleed { box-sizing: border-box; }
|
85 |
|
|
|
106 |
}
|
107 |
}
|
108 |
|
109 |
+
|
110 |
+
|
app/src/styles/{_variables.scss → _variables.css}
RENAMED
@@ -1,12 +1,17 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
:root {
|
5 |
-
|
6 |
-
--
|
7 |
-
--
|
8 |
-
--
|
9 |
-
--
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
--text-color: rgba(0,0,0,.85);
|
12 |
--muted-color: rgba(0,0,0,.6);
|
@@ -15,8 +20,8 @@
|
|
15 |
/* Light surfaces & links */
|
16 |
--surface-bg: #fafafa;
|
17 |
--code-bg: #f6f8fa;
|
18 |
-
--link-underline:
|
19 |
-
--link-underline-hover:
|
20 |
|
21 |
--spacing-1: 8px;
|
22 |
--spacing-2: 12px;
|
@@ -29,17 +34,21 @@
|
|
29 |
--spacing-9: 64px;
|
30 |
--spacing-10: 72px;
|
31 |
}
|
32 |
-
|
33 |
[data-theme="dark"] {
|
34 |
--text-color: rgba(255,255,255,.9);
|
35 |
--muted-color: rgba(255,255,255,.7);
|
36 |
--border-color: rgba(255,255,255,.15);
|
37 |
--surface-bg: #12151b;
|
38 |
--code-bg: #12151b;
|
39 |
-
|
40 |
-
--
|
|
|
|
|
|
|
41 |
color-scheme: dark;
|
42 |
background: #0f1115;
|
43 |
}
|
44 |
|
45 |
|
|
|
|
1 |
+
/* ============================================================================ */
|
2 |
+
/* Theme Variables (inspired by Distill) */
|
3 |
+
/* ============================================================================ */
|
4 |
:root {
|
5 |
+
/* Neutral palette */
|
6 |
+
--neutral-600: rgb(107, 114, 128);
|
7 |
+
--neutral-400: rgb(185, 185, 185);
|
8 |
+
--neutral-300: rgb(228, 228, 228);
|
9 |
+
--neutral-200: rgb(245, 245, 245);
|
10 |
+
|
11 |
+
/* Primary brand color */
|
12 |
+
--primary: rgb(232, 137, 171);
|
13 |
+
--primary-hover: rgb(212, 126, 156);
|
14 |
+
--on-primary: #ffffff;
|
15 |
|
16 |
--text-color: rgba(0,0,0,.85);
|
17 |
--muted-color: rgba(0,0,0,.6);
|
|
|
20 |
/* Light surfaces & links */
|
21 |
--surface-bg: #fafafa;
|
22 |
--code-bg: #f6f8fa;
|
23 |
+
--link-underline: var(--primary); /* based on --primary */
|
24 |
+
--link-underline-hover: var(--primary-hover);
|
25 |
|
26 |
--spacing-1: 8px;
|
27 |
--spacing-2: 12px;
|
|
|
34 |
--spacing-9: 64px;
|
35 |
--spacing-10: 72px;
|
36 |
}
|
37 |
+
/* Theme tokens for dark mode */
|
38 |
[data-theme="dark"] {
|
39 |
--text-color: rgba(255,255,255,.9);
|
40 |
--muted-color: rgba(255,255,255,.7);
|
41 |
--border-color: rgba(255,255,255,.15);
|
42 |
--surface-bg: #12151b;
|
43 |
--code-bg: #12151b;
|
44 |
+
/* Primary in dark mode */
|
45 |
+
--primary: rgb(232, 137, 171);
|
46 |
+
--primary-hover: rgb(212, 126, 156);
|
47 |
+
--on-primary: #0f1115;
|
48 |
+
|
49 |
color-scheme: dark;
|
50 |
background: #0f1115;
|
51 |
}
|
52 |
|
53 |
|
54 |
+
|
app/src/styles/components/{_code.scss → _code.css}
RENAMED
@@ -1,17 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
2 |
/* Sync Shiki variables with current theme */
|
3 |
/* Standard wrapper look for code blocks */
|
4 |
-
.astro-code { border: 1px solid var(--border-color); border-radius: 6px; padding: var(--spacing-3);
|
5 |
|
6 |
/* Prevent code blocks from breaking layout on small screens */
|
7 |
.astro-code { overflow-x: auto; width: 100%; max-width: 100%; box-sizing: border-box; -webkit-overflow-scrolling: touch; }
|
8 |
-
section.content-grid pre { overflow-x: auto; width: 100%; max-width: 100%; box-sizing: border-box; -webkit-overflow-scrolling: touch; }
|
9 |
section.content-grid pre code { display: inline-block; min-width: 100%; }
|
10 |
|
11 |
/* Wrap long lines on mobile to avoid overflow (URLs, etc.) */
|
12 |
-
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
15 |
|
16 |
html[data-theme='light'] .astro-code { background-color: var(--code-bg); }
|
17 |
|
@@ -27,3 +41,25 @@ html[data-theme='light'] .astro-code {
|
|
27 |
--shiki-foreground: #24292f;
|
28 |
--shiki-background: #ffffff;
|
29 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
code {
|
2 |
+
font-size: 14px;
|
3 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
4 |
+
background-color: var(--code-bg);
|
5 |
+
padding: 0.2em 0.4em;
|
6 |
+
border-radius: 0.3em;
|
7 |
+
border: 1px solid var(--border-color);
|
8 |
+
color: var(--text-color);
|
9 |
+
font-weight: 400;
|
10 |
+
line-height: 1.5;
|
11 |
+
}
|
12 |
|
13 |
/* Sync Shiki variables with current theme */
|
14 |
/* Standard wrapper look for code blocks */
|
15 |
+
.astro-code { border: 1px solid var(--border-color); border-radius: 6px; padding: var(--spacing-3); font-size: 14px; --code-gutter-width: 2.5em; }
|
16 |
|
17 |
/* Prevent code blocks from breaking layout on small screens */
|
18 |
.astro-code { overflow-x: auto; width: 100%; max-width: 100%; box-sizing: border-box; -webkit-overflow-scrolling: touch; }
|
19 |
+
section.content-grid pre { overflow-x: auto; width: 100%; max-width: 100%; box-sizing: border-box; -webkit-overflow-scrolling: touch; padding: var(--spacing-3); }
|
20 |
section.content-grid pre code { display: inline-block; min-width: 100%; }
|
21 |
|
22 |
/* Wrap long lines on mobile to avoid overflow (URLs, etc.) */
|
23 |
+
/* Wrap long lines only on small screens to prevent layout overflow */
|
24 |
+
@media (max-width: 1100px) {
|
25 |
+
.astro-code,
|
26 |
+
section.content-grid pre { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
|
27 |
+
section.content-grid pre code { white-space: pre-wrap; display: block; min-width: 0; }
|
28 |
+
}
|
29 |
|
30 |
html[data-theme='light'] .astro-code { background-color: var(--code-bg); }
|
31 |
|
|
|
41 |
--shiki-foreground: #24292f;
|
42 |
--shiki-background: #ffffff;
|
43 |
}
|
44 |
+
|
45 |
+
/* Line numbers for Shiki-rendered code blocks */
|
46 |
+
.astro-code code { counter-reset: astro-code-line; display: block; background: none; border: none; }
|
47 |
+
.astro-code .line { display: inline-block; position: relative; padding-left: calc(var(--code-gutter-width) + var(--spacing-3)); min-height: 1.25em; }
|
48 |
+
.astro-code .line::before { counter-increment: astro-code-line; content: counter(astro-code-line); position: absolute; left: 0; top: 0; bottom: 0; width: calc(var(--code-gutter-width)); text-align: right; color: var(--muted-color); opacity: .75; user-select: none; padding-right: var(--spacing-3); border-right: 1px solid var(--border-color); }
|
49 |
+
.astro-code .line:empty::after { content: "\00a0"; }
|
50 |
+
/* Hide trailing empty line added by parsers */
|
51 |
+
.astro-code code > .line:last-child:empty { display: none; }
|
52 |
+
|
53 |
+
/* JS fallback chip */
|
54 |
+
.astro-code.has-lang-chip { position: relative; padding-top: 22px; }
|
55 |
+
.astro-code .code-lang-chip {
|
56 |
+
position: absolute;
|
57 |
+
top: 0px; right: 0px;
|
58 |
+
font-size: 10px; line-height: 1;
|
59 |
+
color: rgba(255,255,255,.5);
|
60 |
+
background: rgba(255,255,255,.1);
|
61 |
+
border: none;
|
62 |
+
border-radius: 0px; padding: 6px 6px 4px 4px; pointer-events: none; z-index: 1;
|
63 |
+
}
|
64 |
+
|
65 |
+
|
app/src/styles/components/{_footer.scss → _footer.css}
RENAMED
@@ -1,7 +1,7 @@
|
|
1 |
.distill-footer { contain: layout style; font-size: 0.8em; line-height: 1.7em; margin-top: 60px; margin-bottom: 0; border-top: 1px solid rgba(0, 0, 0, 0.1); color: rgba(0, 0, 0, 0.5); }
|
2 |
.footer-inner { max-width: 1280px; margin: 0 auto; padding: 60px 16px 48px; display: grid; grid-template-columns: 220px minmax(0, 680px) 260px; gap: 32px; align-items: start; }
|
3 |
|
4 |
-
|
5 |
.citation-block,
|
6 |
.references-block { display: contents; }
|
7 |
.citation-block > h3,
|
@@ -19,7 +19,7 @@
|
|
19 |
margin-top:0;
|
20 |
}
|
21 |
|
22 |
-
|
23 |
.citation {
|
24 |
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
25 |
font-size: 11px;
|
@@ -43,7 +43,7 @@
|
|
43 |
|
44 |
.references-block h3 { margin: 0; }
|
45 |
|
46 |
-
|
47 |
.references-block ol { padding: 0 0 0 15px; }
|
48 |
@media (min-width: 768px) { .references-block ol { padding: 0 0 0 30px; margin-left: -30px; } }
|
49 |
.references-block li { margin-bottom: 1em; }
|
@@ -52,3 +52,5 @@
|
|
52 |
@media (max-width: 1100px) {
|
53 |
.footer-inner { display: block; padding: 40px 16px; }
|
54 |
}
|
|
|
|
|
|
1 |
.distill-footer { contain: layout style; font-size: 0.8em; line-height: 1.7em; margin-top: 60px; margin-bottom: 0; border-top: 1px solid rgba(0, 0, 0, 0.1); color: rgba(0, 0, 0, 0.5); }
|
2 |
.footer-inner { max-width: 1280px; margin: 0 auto; padding: 60px 16px 48px; display: grid; grid-template-columns: 220px minmax(0, 680px) 260px; gap: 32px; align-items: start; }
|
3 |
|
4 |
+
/* Use the parent grid (3 columns like .content-grid) */
|
5 |
.citation-block,
|
6 |
.references-block { display: contents; }
|
7 |
.citation-block > h3,
|
|
|
19 |
margin-top:0;
|
20 |
}
|
21 |
|
22 |
+
/* Distill-like appendix citation styling */
|
23 |
.citation {
|
24 |
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
25 |
font-size: 11px;
|
|
|
43 |
|
44 |
.references-block h3 { margin: 0; }
|
45 |
|
46 |
+
/* Distill-like list styling for references/footnotes */
|
47 |
.references-block ol { padding: 0 0 0 15px; }
|
48 |
@media (min-width: 768px) { .references-block ol { padding: 0 0 0 30px; margin-left: -30px; } }
|
49 |
.references-block li { margin-bottom: 1em; }
|
|
|
52 |
@media (max-width: 1100px) {
|
53 |
.footer-inner { display: block; padding: 40px 16px; }
|
54 |
}
|
55 |
+
|
56 |
+
|
app/src/styles/components/_poltly.css
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* ============================================================================ */
|
2 |
+
/* Plotly – fragments & controls */
|
3 |
+
/* ============================================================================ */
|
4 |
+
.plot-card { background: var(--code-bg); border: 1px solid var(--border-color); border-radius: 10px; padding: 8px; margin: 8px 0; }
|
5 |
+
.plot-card svg text { fill: var(--text-color) !important; }
|
6 |
+
.plot-card label { color: var(--text-color) !important; }
|
7 |
+
.plotly-graph-div { width: 100% !important; min-height: 320px; }
|
8 |
+
@media (max-width: 768px) { .plotly-graph-div { min-height: 260px; } }
|
9 |
+
[id^="plot-"] { display: flex; flex-direction: column; align-items: center; gap: 15px; }
|
10 |
+
.plotly_caption { font-style: italic; margin-top: 10px; }
|
11 |
+
.plotly_controls { display: flex; flex-wrap: wrap; justify-content: center; gap: 30px; }
|
12 |
+
.plotly_input_container { display: flex; align-items: center; flex-direction: column; gap: 10px; }
|
13 |
+
.plotly_input_container > select { padding: 2px 4px; line-height: 1.5em; text-align: center; border-radius: 4px; font-size: 12px; background-color: var(--neutral-200); outline: none; border: 1px solid var(--neutral-300); }
|
14 |
+
.plotly_slider { display: flex; align-items: center; gap: 10px; }
|
15 |
+
.plotly_slider > input[type="range"] { -webkit-appearance: none; appearance: none; height: 2px; background: var(--neutral-400); border-radius: 5px; outline: none; }
|
16 |
+
.plotly_slider > input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--primary); cursor: pointer; }
|
17 |
+
.plotly_slider > input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--primary); cursor: pointer; }
|
18 |
+
.plotly_slider > span { font-size: 14px; line-height: 1.6em; min-width: 16px; }
|
19 |
+
|
20 |
+
/* ---------------------------------------------------------------------------- */
|
21 |
+
/* Dark mode overrides for Plotly readability */
|
22 |
+
/* ---------------------------------------------------------------------------- */
|
23 |
+
[data-theme="dark"] .plot-card .xaxislayer-above text,
|
24 |
+
[data-theme="dark"] .plot-card .yaxislayer-above text,
|
25 |
+
[data-theme="dark"] .plot-card .infolayer text,
|
26 |
+
[data-theme="dark"] .plot-card .legend text,
|
27 |
+
[data-theme="dark"] .plot-card .annotation text,
|
28 |
+
[data-theme="dark"] .plot-card .colorbar text,
|
29 |
+
[data-theme="dark"] .plot-card .hoverlayer text { fill: #fff !important; }
|
30 |
+
|
31 |
+
[data-theme="dark"] .plot-card .xaxislayer-above path,
|
32 |
+
[data-theme="dark"] .plot-card .yaxislayer-above path,
|
33 |
+
[data-theme="dark"] .plot-card .xlines-above,
|
34 |
+
[data-theme="dark"] .plot-card .ylines-above { stroke: rgba(255,255,255,.35) !important; }
|
35 |
+
|
36 |
+
[data-theme="dark"] .plot-card .gridlayer path { stroke: rgba(255,255,255,.15) !important; }
|
37 |
+
|
38 |
+
/* Legend and hover backgrounds */
|
39 |
+
[data-theme="dark"] .plot-card .legend rect.bg { fill: rgba(0,0,0,.25) !important; stroke: rgba(255,255,255,.2) !important; }
|
40 |
+
[data-theme="dark"] .plot-card .hoverlayer .bg { fill: rgba(0,0,0,.8) !important; stroke: rgba(255,255,255,.2) !important; }
|
41 |
+
|
42 |
+
/* Colorbar background (keep gradient intact) */
|
43 |
+
[data-theme="dark"] .plot-card .colorbar .cbbg { fill: rgba(0,0,0,.25) !important; stroke: rgba(255,255,255,.2) !important; }
|
44 |
+
|
45 |
+
|
app/src/styles/{global.scss → global.css}
RENAMED
@@ -1,8 +1,9 @@
|
|
1 |
-
@
|
2 |
-
@
|
3 |
-
@
|
4 |
-
@
|
5 |
-
@
|
|
|
6 |
|
7 |
/* Dark-mode form tweak */
|
8 |
[data-theme="dark"] .plotly_input_container > select { background-color: #1a1f27; border-color: var(--border-color); color: var(--text-color); }
|
@@ -19,10 +20,9 @@
|
|
19 |
img[data-zoomable] { cursor: zoom-in; }
|
20 |
.medium-zoom--opened img[data-zoomable] { cursor: zoom-out; }
|
21 |
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
// ============================================================================
|
26 |
.hero { width: 100%; padding: 48px 16px 16px; text-align: center; }
|
27 |
.hero-title { font-size: clamp(28px, 4vw, 48px); font-weight: 800; line-height: 1.1; margin: 0 0 8px;
|
28 |
|
@@ -31,9 +31,9 @@ img[data-zoomable] { cursor: zoom-in; }
|
|
31 |
.hero-banner { max-width: 980px; margin: 0 auto; }
|
32 |
.hero-desc { color: var(--muted-color); font-style: italic; margin: 0 0 16px 0; }
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
|
38 |
.meta {
|
39 |
border-top: 1px solid var(--border-color);
|
@@ -65,30 +65,13 @@ img[data-zoomable] { cursor: zoom-in; }
|
|
65 |
}
|
66 |
.meta-container-cell p { margin: 0; }
|
67 |
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
.plot-card { background: var(--code-bg); border: 1px solid var(--border-color); border-radius: 10px; padding: 8px; margin: 8px 0; }
|
72 |
-
.plot-card svg text { fill: var(--text-color) !important; }
|
73 |
-
.plotly-graph-div { width: 100% !important; min-height: 320px; }
|
74 |
-
@media (max-width: 768px) { .plotly-graph-div { min-height: 260px; } }
|
75 |
-
[id^="plot-"] { display: flex; flex-direction: column; align-items: center; gap: 15px; }
|
76 |
-
.plotly_caption { font-style: italic; margin-top: 10px; }
|
77 |
-
.plotly_controls { display: flex; flex-wrap: wrap; justify-content: center; gap: 30px; }
|
78 |
-
.plotly_input_container { display: flex; align-items: center; flex-direction: column; gap: 10px; }
|
79 |
-
.plotly_input_container > select { padding: 2px 4px; line-height: 1.5em; text-align: center; border-radius: 4px; font-size: 12px; background-color: var(--distill-gray-lightest); outline: none; border: 1px solid var(--distill-gray-lighter); }
|
80 |
-
.plotly_slider { display: flex; align-items: center; gap: 10px; }
|
81 |
-
.plotly_slider > input[type="range"] { -webkit-appearance: none; appearance: none; height: 2px; background: var(--distill-gray-light); border-radius: 5px; outline: none; }
|
82 |
-
.plotly_slider > input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--distill-blue); cursor: pointer; }
|
83 |
-
.plotly_slider > input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--distill-blue); cursor: pointer; }
|
84 |
-
.plotly_slider > span { font-size: 14px; line-height: 1.6em; min-width: 16px; }
|
85 |
-
|
86 |
-
// ============================================================================
|
87 |
-
// Theme Toggle button (moved from component)
|
88 |
-
// ============================================================================
|
89 |
#theme-toggle { display: inline-flex; align-items: center; gap: 8px; border: none; background: transparent; padding: 6px 10px; border-radius: 8px; cursor: pointer; margin: 12px 16px; }
|
90 |
#theme-toggle .icon.dark { display: none; }
|
91 |
[data-theme="dark"] #theme-toggle .icon.light { display: none; }
|
92 |
[data-theme="dark"] #theme-toggle .icon.dark { display: inline; }
|
93 |
[data-theme="dark"] #theme-toggle .icon { filter: invert(1) brightness(1.2); }
|
94 |
|
|
|
|
1 |
+
@import './_variables.css';
|
2 |
+
@import './_base.css';
|
3 |
+
@import './_layout.css';
|
4 |
+
@import './components/_footer.css';
|
5 |
+
@import './components/_code.css';
|
6 |
+
@import './components/_poltly.css';
|
7 |
|
8 |
/* Dark-mode form tweak */
|
9 |
[data-theme="dark"] .plotly_input_container > select { background-color: #1a1f27; border-color: var(--border-color); color: var(--text-color); }
|
|
|
20 |
img[data-zoomable] { cursor: zoom-in; }
|
21 |
.medium-zoom--opened img[data-zoomable] { cursor: zoom-out; }
|
22 |
|
23 |
+
/* ============================================================================ */
|
24 |
+
/* Hero (full-bleed) */
|
25 |
+
/* ============================================================================ */
|
|
|
26 |
.hero { width: 100%; padding: 48px 16px 16px; text-align: center; }
|
27 |
.hero-title { font-size: clamp(28px, 4vw, 48px); font-weight: 800; line-height: 1.1; margin: 0 0 8px;
|
28 |
|
|
|
31 |
.hero-banner { max-width: 980px; margin: 0 auto; }
|
32 |
.hero-desc { color: var(--muted-color); font-style: italic; margin: 0 0 16px 0; }
|
33 |
|
34 |
+
/* ============================================================================ */
|
35 |
+
/* Meta (byline-like header) */
|
36 |
+
/* ============================================================================ */
|
37 |
|
38 |
.meta {
|
39 |
border-top: 1px solid var(--border-color);
|
|
|
65 |
}
|
66 |
.meta-container-cell p { margin: 0; }
|
67 |
|
68 |
+
/* ============================================================================ */
|
69 |
+
/* Theme Toggle button (moved from component) */
|
70 |
+
/* ============================================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
#theme-toggle { display: inline-flex; align-items: center; gap: 8px; border: none; background: transparent; padding: 6px 10px; border-radius: 8px; cursor: pointer; margin: 12px 16px; }
|
72 |
#theme-toggle .icon.dark { display: none; }
|
73 |
[data-theme="dark"] #theme-toggle .icon.light { display: none; }
|
74 |
[data-theme="dark"] #theme-toggle .icon.dark { display: inline; }
|
75 |
[data-theme="dark"] #theme-toggle .icon { filter: invert(1) brightness(1.2); }
|
76 |
|
77 |
+
|
fragments/d3js/banner.html
CHANGED
@@ -93,17 +93,6 @@
|
|
93 |
.attr('width', '100%')
|
94 |
.style('display', 'block');
|
95 |
|
96 |
-
// Subtle background gradient
|
97 |
-
const defs = svg.append('defs');
|
98 |
-
const grad = defs.append('radialGradient')
|
99 |
-
.attr('id', 'spaceBg')
|
100 |
-
.attr('cx', '50%')
|
101 |
-
.attr('cy', '50%')
|
102 |
-
.attr('r', '65%');
|
103 |
-
grad.append('stop').attr('offset', '0%').attr('stop-color', 'rgba(255,255,255,0.06)');
|
104 |
-
grad.append('stop').attr('offset', '60%').attr('stop-color', 'rgba(160,120,200,0.05)');
|
105 |
-
grad.append('stop').attr('offset', '100%').attr('stop-color', 'rgba(0,0,0,0)');
|
106 |
-
|
107 |
const render = () => {
|
108 |
const width = container.clientWidth || 800;
|
109 |
const height = Math.max(260, Math.round(width / 3)); // keep ~3:1, min height
|
@@ -132,6 +121,7 @@
|
|
132 |
// Ensure container can host an absolute tooltip
|
133 |
container.style.position = container.style.position || 'relative';
|
134 |
let tip = container.querySelector('.d3-tooltip');
|
|
|
135 |
if (!tip) {
|
136 |
tip = document.createElement('div');
|
137 |
tip.className = 'd3-tooltip';
|
@@ -141,22 +131,28 @@
|
|
141 |
left: '0px',
|
142 |
transform: 'translate(-9999px, -9999px)',
|
143 |
pointerEvents: 'none',
|
144 |
-
padding: '
|
145 |
-
borderRadius: '
|
146 |
fontSize: '12px',
|
147 |
-
lineHeight: '1.
|
148 |
border: '1px solid var(--border-color)',
|
149 |
background: 'var(--surface-bg)',
|
150 |
color: 'var(--text-color)',
|
151 |
-
boxShadow: '0
|
152 |
opacity: '0',
|
153 |
transition: 'opacity .12s ease'
|
154 |
});
|
|
|
|
|
|
|
|
|
155 |
container.appendChild(tip);
|
|
|
|
|
156 |
}
|
157 |
|
158 |
// Final filter: remove small dots very close to the galaxy center (after placement)
|
159 |
-
const centerHoleRadius = 0.
|
160 |
const smallSizeThreshold = 7.5; // same notion as Python size cut
|
161 |
const rTotal = idx.map((i) => Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2));
|
162 |
const idxFiltered = idx.filter((i, k) => !(rTotal[k] <= centerHoleRadius && sizesPx[i] < smallSizeThreshold));
|
@@ -171,15 +167,18 @@
|
|
171 |
.attr('fill-opacity', 0.9)
|
172 |
.attr('stroke', strokeColor)
|
173 |
.attr('stroke-width', 0.4)
|
174 |
-
.on('mouseenter', (ev, i)
|
|
|
|
|
|
|
175 |
const r = Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2);
|
176 |
const type = i < lenSpiral ? 'spiral' : 'bulge';
|
177 |
const arm = i < lenSpiral ? (armIndices[i] + 1) : null;
|
178 |
-
|
179 |
-
`<div>Type
|
180 |
-
`<div>Size
|
181 |
-
`<div>X
|
182 |
-
`<div>r
|
183 |
tip.style.opacity = '1';
|
184 |
})
|
185 |
.on('mousemove', (ev, i) => {
|
@@ -187,9 +186,10 @@
|
|
187 |
const offsetX = 10, offsetY = 12;
|
188 |
tip.style.transform = `translate(${Math.round(mx + offsetX)}px, ${Math.round(my + offsetY)}px)`;
|
189 |
})
|
190 |
-
.on('mouseleave', ()
|
191 |
tip.style.opacity = '0';
|
192 |
tip.style.transform = 'translate(-9999px, -9999px)';
|
|
|
193 |
}),
|
194 |
(update) => update
|
195 |
.attr('cx', (i) => xScale(X[i]))
|
@@ -199,15 +199,18 @@
|
|
199 |
.attr('fill-opacity', 0.9)
|
200 |
.attr('stroke', strokeColor)
|
201 |
.attr('stroke-width', 0.4)
|
202 |
-
.on('mouseenter', (ev, i)
|
|
|
|
|
|
|
203 |
const r = Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2);
|
204 |
const type = i < lenSpiral ? 'spiral' : 'bulge';
|
205 |
const arm = i < lenSpiral ? (armIndices[i] + 1) : null;
|
206 |
-
|
207 |
-
`<div>Type
|
208 |
-
`<div>Size
|
209 |
-
`<div>X
|
210 |
-
`<div>r
|
211 |
tip.style.opacity = '1';
|
212 |
})
|
213 |
.on('mousemove', (ev, i) => {
|
@@ -215,9 +218,10 @@
|
|
215 |
const offsetX = 10, offsetY = 12;
|
216 |
tip.style.transform = `translate(${Math.round(mx + offsetX)}px, ${Math.round(my + offsetY)}px)`;
|
217 |
})
|
218 |
-
.on('mouseleave', ()
|
219 |
tip.style.opacity = '0';
|
220 |
tip.style.transform = 'translate(-9999px, -9999px)';
|
|
|
221 |
})
|
222 |
);
|
223 |
};
|
|
|
93 |
.attr('width', '100%')
|
94 |
.style('display', 'block');
|
95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
const render = () => {
|
97 |
const width = container.clientWidth || 800;
|
98 |
const height = Math.max(260, Math.round(width / 3)); // keep ~3:1, min height
|
|
|
121 |
// Ensure container can host an absolute tooltip
|
122 |
container.style.position = container.style.position || 'relative';
|
123 |
let tip = container.querySelector('.d3-tooltip');
|
124 |
+
let tipInner;
|
125 |
if (!tip) {
|
126 |
tip = document.createElement('div');
|
127 |
tip.className = 'd3-tooltip';
|
|
|
131 |
left: '0px',
|
132 |
transform: 'translate(-9999px, -9999px)',
|
133 |
pointerEvents: 'none',
|
134 |
+
padding: '8px 10px',
|
135 |
+
borderRadius: '8px',
|
136 |
fontSize: '12px',
|
137 |
+
lineHeight: '1.35',
|
138 |
border: '1px solid var(--border-color)',
|
139 |
background: 'var(--surface-bg)',
|
140 |
color: 'var(--text-color)',
|
141 |
+
boxShadow: '0 4px 24px rgba(0,0,0,.18)',
|
142 |
opacity: '0',
|
143 |
transition: 'opacity .12s ease'
|
144 |
});
|
145 |
+
tipInner = document.createElement('div');
|
146 |
+
tipInner.className = 'd3-tooltip__inner';
|
147 |
+
tipInner.style.textAlign = 'left';
|
148 |
+
tip.appendChild(tipInner);
|
149 |
container.appendChild(tip);
|
150 |
+
} else {
|
151 |
+
tipInner = tip.querySelector('.d3-tooltip__inner') || tip;
|
152 |
}
|
153 |
|
154 |
// Final filter: remove small dots very close to the galaxy center (after placement)
|
155 |
+
const centerHoleRadius = 0.48; // elliptical radius threshold
|
156 |
const smallSizeThreshold = 7.5; // same notion as Python size cut
|
157 |
const rTotal = idx.map((i) => Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2));
|
158 |
const idxFiltered = idx.filter((i, k) => !(rTotal[k] <= centerHoleRadius && sizesPx[i] < smallSizeThreshold));
|
|
|
167 |
.attr('fill-opacity', 0.9)
|
168 |
.attr('stroke', strokeColor)
|
169 |
.attr('stroke-width', 0.4)
|
170 |
+
.on('mouseenter', function(ev, i) {
|
171 |
+
d3.select(this).raise()
|
172 |
+
.attr('stroke', isDark ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.85)')
|
173 |
+
.attr('stroke-width', 1.2);
|
174 |
const r = Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2);
|
175 |
const type = i < lenSpiral ? 'spiral' : 'bulge';
|
176 |
const arm = i < lenSpiral ? (armIndices[i] + 1) : null;
|
177 |
+
tipInner.innerHTML = `<div><strong>${labelOf(i)}</strong></div>` +
|
178 |
+
`<div><strong>Type</strong> ${type}${arm ? ` (arm ${arm})` : ''}</div>` +
|
179 |
+
`<div><strong>Size</strong> ${sizesPx[i].toFixed(1)} px</div>` +
|
180 |
+
`<div><strong>X</strong> ${X[i].toFixed(2)} · <strong>Y</strong> ${Y[i].toFixed(2)}</div>` +
|
181 |
+
`<div><strong>r</strong> ${r.toFixed(3)} · <strong>z</strong> ${Zraw[i].toFixed(3)}</div>`;
|
182 |
tip.style.opacity = '1';
|
183 |
})
|
184 |
.on('mousemove', (ev, i) => {
|
|
|
186 |
const offsetX = 10, offsetY = 12;
|
187 |
tip.style.transform = `translate(${Math.round(mx + offsetX)}px, ${Math.round(my + offsetY)}px)`;
|
188 |
})
|
189 |
+
.on('mouseleave', function() {
|
190 |
tip.style.opacity = '0';
|
191 |
tip.style.transform = 'translate(-9999px, -9999px)';
|
192 |
+
d3.select(this).attr('stroke', strokeColor).attr('stroke-width', 0.4);
|
193 |
}),
|
194 |
(update) => update
|
195 |
.attr('cx', (i) => xScale(X[i]))
|
|
|
199 |
.attr('fill-opacity', 0.9)
|
200 |
.attr('stroke', strokeColor)
|
201 |
.attr('stroke-width', 0.4)
|
202 |
+
.on('mouseenter', function(ev, i) {
|
203 |
+
d3.select(this).raise()
|
204 |
+
.attr('stroke', isDark ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.85)')
|
205 |
+
.attr('stroke-width', 1.2);
|
206 |
const r = Math.sqrt(((X[i] - cx) / a) ** 2 + ((Y[i] - cy) / b) ** 2);
|
207 |
const type = i < lenSpiral ? 'spiral' : 'bulge';
|
208 |
const arm = i < lenSpiral ? (armIndices[i] + 1) : null;
|
209 |
+
tipInner.innerHTML = `<div><strong>${labelOf(i)}</strong></div>` +
|
210 |
+
`<div><strong>Type</strong> ${type}${arm ? ` (arm ${arm})` : ''}</div>` +
|
211 |
+
`<div><strong>Size</strong> ${sizesPx[i].toFixed(1)} px</div>` +
|
212 |
+
`<div><strong>X</strong> ${X[i].toFixed(2)} · <strong>Y</strong> ${Y[i].toFixed(2)}</div>` +
|
213 |
+
`<div><strong>r</strong> ${r.toFixed(3)} · <strong>z</strong> ${Zraw[i].toFixed(3)}</div>`;
|
214 |
tip.style.opacity = '1';
|
215 |
})
|
216 |
.on('mousemove', (ev, i) => {
|
|
|
218 |
const offsetX = 10, offsetY = 12;
|
219 |
tip.style.transform = `translate(${Math.round(mx + offsetX)}px, ${Math.round(my + offsetY)}px)`;
|
220 |
})
|
221 |
+
.on('mouseleave', function() {
|
222 |
tip.style.opacity = '0';
|
223 |
tip.style.transform = 'translate(-9999px, -9999px)';
|
224 |
+
d3.select(this).attr('stroke', strokeColor).attr('stroke-width', 0.4);
|
225 |
})
|
226 |
);
|
227 |
};
|
fragments/d3js/line.html
ADDED
@@ -0,0 +1,356 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="d3-line" style="width:100%;margin:10px 0;"></div>
|
2 |
+
<script>
|
3 |
+
(() => {
|
4 |
+
const ensureD3 = (cb) => {
|
5 |
+
if (window.d3 && typeof window.d3.select === 'function') return cb();
|
6 |
+
let s = document.getElementById('d3-cdn-script');
|
7 |
+
if (!s) {
|
8 |
+
s = document.createElement('script');
|
9 |
+
s.id = 'd3-cdn-script';
|
10 |
+
s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
|
11 |
+
document.head.appendChild(s);
|
12 |
+
}
|
13 |
+
const onReady = () => { if (window.d3 && typeof window.d3.select === 'function') cb(); };
|
14 |
+
s.addEventListener('load', onReady, { once: true });
|
15 |
+
if (window.d3) onReady();
|
16 |
+
};
|
17 |
+
|
18 |
+
const bootstrap = () => {
|
19 |
+
const mount = document.currentScript ? document.currentScript.previousElementSibling : null;
|
20 |
+
const container = (mount && mount.querySelector && mount.querySelector('.d3-line')) || document.querySelector('.d3-line');
|
21 |
+
if (!container) return;
|
22 |
+
if (container.dataset) {
|
23 |
+
if (container.dataset.mounted === 'true') return;
|
24 |
+
container.dataset.mounted = 'true';
|
25 |
+
}
|
26 |
+
|
27 |
+
// Dataset params matching the Plotly version
|
28 |
+
const datasets = [
|
29 |
+
{ name: 'CIFAR-10', base: { ymin:0.10, ymax:0.90, k:10.0, x0:0.55 }, aug: { ymin:0.15, ymax:0.96, k:12.0, x0:0.40 }, target: 0.97 },
|
30 |
+
{ name: 'CIFAR-100', base: { ymin:0.05, ymax:0.70, k: 9.5, x0:0.60 }, aug: { ymin:0.08, ymax:0.80, k:11.0, x0:0.45 }, target: 0.85 },
|
31 |
+
{ name: 'ImageNet-1K', base: { ymin:0.02, ymax:0.68, k: 8.5, x0:0.65 }, aug: { ymin:0.04, ymax:0.75, k: 9.5, x0:0.50 }, target: 0.82 },
|
32 |
+
];
|
33 |
+
|
34 |
+
// Controls UI
|
35 |
+
const controls = document.createElement('div');
|
36 |
+
controls.className = 'd3-line__controls';
|
37 |
+
Object.assign(controls.style, {
|
38 |
+
marginTop: '12px',
|
39 |
+
display: 'flex',
|
40 |
+
gap: '16px',
|
41 |
+
alignItems: 'center'
|
42 |
+
});
|
43 |
+
|
44 |
+
const labelDs = document.createElement('label');
|
45 |
+
Object.assign(labelDs.style, {
|
46 |
+
fontSize: '12px', color: 'rgba(0,0,0,.65)', display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap', padding: '6px 10px'
|
47 |
+
});
|
48 |
+
labelDs.textContent = 'Dataset';
|
49 |
+
const selectDs = document.createElement('select');
|
50 |
+
Object.assign(selectDs.style, { fontSize: '12px', padding: '2px 6px' });
|
51 |
+
datasets.forEach((d, i) => {
|
52 |
+
const o = document.createElement('option');
|
53 |
+
o.value = String(i);
|
54 |
+
o.textContent = d.name;
|
55 |
+
selectDs.appendChild(o);
|
56 |
+
});
|
57 |
+
labelDs.appendChild(selectDs);
|
58 |
+
|
59 |
+
const labelAlpha = document.createElement('label');
|
60 |
+
Object.assign(labelAlpha.style, {
|
61 |
+
fontSize: '12px', color: 'rgba(0,0,0,.65)', display: 'flex', alignItems: 'center', gap: '10px', flex: '1', padding: '6px 10px'
|
62 |
+
});
|
63 |
+
labelAlpha.appendChild(document.createTextNode('Augmentation α'));
|
64 |
+
const slider = document.createElement('input');
|
65 |
+
slider.type = 'range'; slider.min = '0'; slider.max = '1'; slider.step = '0.01'; slider.value = '0.70';
|
66 |
+
Object.assign(slider.style, { flex: '1' });
|
67 |
+
const alphaVal = document.createElement('span'); alphaVal.className = 'alpha-value'; alphaVal.textContent = slider.value;
|
68 |
+
labelAlpha.appendChild(slider);
|
69 |
+
labelAlpha.appendChild(alphaVal);
|
70 |
+
|
71 |
+
controls.appendChild(labelDs);
|
72 |
+
controls.appendChild(labelAlpha);
|
73 |
+
|
74 |
+
// Create SVG
|
75 |
+
const svg = d3.select(container).append('svg')
|
76 |
+
.attr('width', '100%')
|
77 |
+
.style('display', 'block');
|
78 |
+
|
79 |
+
// Groups
|
80 |
+
const gRoot = svg.append('g');
|
81 |
+
const gGrid = gRoot.append('g').attr('class', 'grid');
|
82 |
+
const gAxes = gRoot.append('g').attr('class', 'axes');
|
83 |
+
const gLines = gRoot.append('g').attr('class', 'lines');
|
84 |
+
const gHover = gRoot.append('g').attr('class', 'hover');
|
85 |
+
const gLegend = gRoot.append('foreignObject').attr('class', 'legend');
|
86 |
+
|
87 |
+
// Tooltip
|
88 |
+
container.style.position = container.style.position || 'relative';
|
89 |
+
let tip = container.querySelector('.d3-tooltip');
|
90 |
+
let tipInner;
|
91 |
+
if (!tip) {
|
92 |
+
tip = document.createElement('div');
|
93 |
+
tip.className = 'd3-tooltip';
|
94 |
+
Object.assign(tip.style, {
|
95 |
+
position: 'absolute', top: '0px', left: '0px', transform: 'translate(-9999px, -9999px)', pointerEvents: 'none',
|
96 |
+
padding: '8px 10px', borderRadius: '8px', fontSize: '12px', lineHeight: '1.35', border: '1px solid var(--border-color)',
|
97 |
+
background: 'var(--surface-bg)', color: 'var(--text-color)', boxShadow: '0 4px 24px rgba(0,0,0,.18)', opacity: '0',
|
98 |
+
transition: 'opacity .12s ease'
|
99 |
+
});
|
100 |
+
tipInner = document.createElement('div');
|
101 |
+
tipInner.className = 'd3-tooltip__inner';
|
102 |
+
tipInner.style.textAlign = 'left';
|
103 |
+
tip.appendChild(tipInner);
|
104 |
+
container.appendChild(tip);
|
105 |
+
} else {
|
106 |
+
tipInner = tip.querySelector('.d3-tooltip__inner') || tip;
|
107 |
+
}
|
108 |
+
|
109 |
+
// Colors
|
110 |
+
const colorBase = '#64748b'; // slate-500
|
111 |
+
const colorImproved = '#2563eb'; // blue-600
|
112 |
+
const colorTarget = '#4b5563'; // gray-600
|
113 |
+
const legendBgLight = 'rgba(255,255,255,0.85)';
|
114 |
+
const legendBgDark = 'rgba(17,17,23,0.85)';
|
115 |
+
|
116 |
+
// Data and helpers
|
117 |
+
const N = 240;
|
118 |
+
const xs = Array.from({ length: N }, (_, i) => i / (N - 1));
|
119 |
+
const logistic = (x, { ymin, ymax, k, x0 }) => ymin + (ymax - ymin) / (1 + Math.exp(-k * (x - x0)));
|
120 |
+
const blend = (l, e, a) => (1 - a) * l + a * e;
|
121 |
+
|
122 |
+
let datasetIndex = 0;
|
123 |
+
let alpha = parseFloat(slider.value) || 0.7;
|
124 |
+
|
125 |
+
let yBase = [];
|
126 |
+
let yAug = [];
|
127 |
+
let yImp = [];
|
128 |
+
let yTgt = [];
|
129 |
+
|
130 |
+
function computeCurves() {
|
131 |
+
const d = datasets[datasetIndex];
|
132 |
+
yBase = xs.map((x) => logistic(x, d.base));
|
133 |
+
yAug = xs.map((x) => logistic(x, d.aug));
|
134 |
+
yTgt = xs.map(() => d.target);
|
135 |
+
yImp = yBase.map((v, i) => blend(v, yAug[i], alpha));
|
136 |
+
}
|
137 |
+
|
138 |
+
// Scales and layout
|
139 |
+
let width = 800, height = 360;
|
140 |
+
let margin = { top: 16, right: 28, bottom: 40, left: 44 };
|
141 |
+
let xScale = d3.scaleLinear();
|
142 |
+
let yScale = d3.scaleLinear();
|
143 |
+
|
144 |
+
// Paths
|
145 |
+
const lineGen = d3.line()
|
146 |
+
.curve(d3.curveCatmullRom.alpha(0.6))
|
147 |
+
.x((d, i) => xScale(xs[i]))
|
148 |
+
.y((d) => yScale(d));
|
149 |
+
|
150 |
+
const pathBase = gLines.append('path').attr('fill', 'none').attr('stroke', colorBase).attr('stroke-width', 2);
|
151 |
+
const pathImp = gLines.append('path').attr('fill', 'none').attr('stroke', colorImproved).attr('stroke-width', 2);
|
152 |
+
const pathTgt = gLines.append('path').attr('fill', 'none').attr('stroke', colorTarget).attr('stroke-width', 2).attr('stroke-dasharray', '6,6');
|
153 |
+
|
154 |
+
// Hover elements
|
155 |
+
const hoverLine = gHover.append('line').attr('stroke-width', 1);
|
156 |
+
const hoverDotB = gHover.append('circle').attr('r', 3.5).attr('fill', colorBase).attr('stroke', '#fff').attr('stroke-width', 1);
|
157 |
+
const hoverDotI = gHover.append('circle').attr('r', 3.5).attr('fill', colorImproved).attr('stroke', '#fff').attr('stroke-width', 1);
|
158 |
+
const hoverDotT = gHover.append('circle').attr('r', 3.5).attr('fill', colorTarget).attr('stroke', '#fff').attr('stroke-width', 1);
|
159 |
+
|
160 |
+
const overlay = gHover.append('rect').attr('fill', 'transparent').style('cursor', 'crosshair');
|
161 |
+
|
162 |
+
function updateScales() {
|
163 |
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
164 |
+
const axisColor = isDark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)';
|
165 |
+
const tickColor = isDark ? 'rgba(255,255,255,0.70)' : 'rgba(0,0,0,0.55)';
|
166 |
+
const gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)';
|
167 |
+
|
168 |
+
width = container.clientWidth || 800;
|
169 |
+
height = Math.max(260, Math.round(width / 3));
|
170 |
+
svg.attr('width', width).attr('height', height);
|
171 |
+
|
172 |
+
const innerWidth = width - margin.left - margin.right;
|
173 |
+
const innerHeight = height - margin.top - margin.bottom;
|
174 |
+
gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
|
175 |
+
|
176 |
+
xScale.domain([0, 1]).range([0, innerWidth]);
|
177 |
+
yScale.domain([0, 1]).range([innerHeight, 0]);
|
178 |
+
|
179 |
+
// Grid (horizontal)
|
180 |
+
gGrid.selectAll('*').remove();
|
181 |
+
const yTicks = yScale.ticks(6);
|
182 |
+
gGrid.selectAll('line')
|
183 |
+
.data(yTicks)
|
184 |
+
.join('line')
|
185 |
+
.attr('x1', 0)
|
186 |
+
.attr('x2', innerWidth)
|
187 |
+
.attr('y1', (d) => yScale(d))
|
188 |
+
.attr('y2', (d) => yScale(d))
|
189 |
+
.attr('stroke', gridColor)
|
190 |
+
.attr('stroke-width', 1)
|
191 |
+
.attr('shape-rendering', 'crispEdges');
|
192 |
+
|
193 |
+
// Axes
|
194 |
+
gAxes.selectAll('*').remove();
|
195 |
+
const xAxis = d3.axisBottom(xScale).ticks(8).tickSizeOuter(0);
|
196 |
+
const yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0).tickFormat(d3.format('.2f'));
|
197 |
+
gAxes.append('g')
|
198 |
+
.attr('transform', `translate(0,${innerHeight})`)
|
199 |
+
.call(xAxis)
|
200 |
+
.call((g) => {
|
201 |
+
g.selectAll('path, line').attr('stroke', axisColor);
|
202 |
+
g.selectAll('text').attr('fill', tickColor).style('font-size', '12px');
|
203 |
+
});
|
204 |
+
gAxes.append('g')
|
205 |
+
.call(yAxis)
|
206 |
+
.call((g) => {
|
207 |
+
g.selectAll('path, line').attr('stroke', axisColor);
|
208 |
+
g.selectAll('text').attr('fill', tickColor).style('font-size', '12px');
|
209 |
+
});
|
210 |
+
|
211 |
+
// Axis labels (X and Y)
|
212 |
+
gAxes.append('text')
|
213 |
+
.attr('class', 'axis-label axis-label--x')
|
214 |
+
.attr('x', innerWidth)
|
215 |
+
.attr('y', innerHeight + 32)
|
216 |
+
.attr('text-anchor', 'end')
|
217 |
+
.style('font-size', '12px')
|
218 |
+
.style('fill', tickColor)
|
219 |
+
.text('x');
|
220 |
+
gAxes.append('text')
|
221 |
+
.attr('class', 'axis-label axis-label--y')
|
222 |
+
.attr('text-anchor', 'middle')
|
223 |
+
.attr('transform', `translate(${-36},${innerHeight/2}) rotate(-90)`)
|
224 |
+
.style('font-size', '12px')
|
225 |
+
.style('fill', tickColor)
|
226 |
+
.text('y');
|
227 |
+
|
228 |
+
overlay.attr('x', 0).attr('y', 0).attr('width', innerWidth).attr('height', innerHeight);
|
229 |
+
hoverLine.attr('y1', 0).attr('y2', innerHeight).attr('stroke', axisColor);
|
230 |
+
|
231 |
+
// Legend inside plot (bottom-right), no background/border/shadow
|
232 |
+
const legendWidth = Math.min(180, Math.max(120, Math.round(innerWidth * 0.22)));
|
233 |
+
const legendHeight = 64;
|
234 |
+
gLegend
|
235 |
+
.attr('x', innerWidth - legendWidth)
|
236 |
+
.attr('y', innerHeight - legendHeight)
|
237 |
+
.attr('width', legendWidth)
|
238 |
+
.attr('height', legendHeight);
|
239 |
+
const legendRoot = gLegend.selectAll('div').data([0]).join('xhtml:div');
|
240 |
+
Object.assign(legendRoot.node().style, {
|
241 |
+
background: 'transparent',
|
242 |
+
border: 'none',
|
243 |
+
borderRadius: '0',
|
244 |
+
padding: '0',
|
245 |
+
fontSize: '12px',
|
246 |
+
lineHeight: '1.35',
|
247 |
+
color: 'var(--text-color)'
|
248 |
+
});
|
249 |
+
legendRoot.html(`
|
250 |
+
<div style="display:flex;flex-direction:column;gap:6px;">
|
251 |
+
<div style="display:flex;align-items:center;gap:8px;">
|
252 |
+
<span style="width:18px;height:3px;background:${colorBase};border-radius:2px;display:inline-block"></span>
|
253 |
+
<span>Baseline</span>
|
254 |
+
</div>
|
255 |
+
<div style="display:flex;align-items:center;gap:8px;">
|
256 |
+
<span style="width:18px;height:3px;background:${colorImproved};border-radius:2px;display:inline-block"></span>
|
257 |
+
<span>Improved</span>
|
258 |
+
</div>
|
259 |
+
<div style="display:flex;align-items:center;gap:8px;">
|
260 |
+
<span style="width:18px;height:0;border-top:2px dashed ${colorTarget};display:inline-block"></span>
|
261 |
+
<span>Target</span>
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
`);
|
265 |
+
}
|
266 |
+
|
267 |
+
function updatePaths() {
|
268 |
+
pathBase.transition().duration(200).attr('d', lineGen(yBase));
|
269 |
+
pathImp.transition().duration(200).attr('d', lineGen(yImp));
|
270 |
+
pathTgt.transition().duration(200).attr('d', lineGen(yTgt));
|
271 |
+
}
|
272 |
+
|
273 |
+
function updateAlpha(a) {
|
274 |
+
alpha = a;
|
275 |
+
alphaVal.textContent = a.toFixed(2);
|
276 |
+
yImp = yBase.map((v, i) => blend(v, yAug[i], alpha));
|
277 |
+
pathImp.transition().duration(80).attr('d', lineGen(yImp));
|
278 |
+
}
|
279 |
+
|
280 |
+
function applyDataset() {
|
281 |
+
computeCurves();
|
282 |
+
updatePaths();
|
283 |
+
}
|
284 |
+
|
285 |
+
// Hover interactions
|
286 |
+
function onMove(event) {
|
287 |
+
const [mx, my] = d3.pointer(event, overlay.node());
|
288 |
+
const xi = Math.max(0, Math.min(N - 1, Math.round(xScale.invert(mx) * (N - 1))));
|
289 |
+
const xpx = xScale(xs[xi]);
|
290 |
+
const yb = yBase[xi], yi = yImp[xi], yt = yTgt[xi];
|
291 |
+
hoverLine.attr('x1', xpx).attr('x2', xpx).style('display', null);
|
292 |
+
hoverDotB.attr('cx', xpx).attr('cy', yScale(yb)).style('display', null);
|
293 |
+
hoverDotI.attr('cx', xpx).attr('cy', yScale(yi)).style('display', null);
|
294 |
+
hoverDotT.attr('cx', xpx).attr('cy', yScale(yt)).style('display', null);
|
295 |
+
|
296 |
+
// Tooltip content
|
297 |
+
const ds = datasets[datasetIndex].name;
|
298 |
+
tipInner.innerHTML = `<div><strong>${ds}</strong></div>` +
|
299 |
+
`<div><strong>x</strong> ${xs[xi].toFixed(2)}</div>` +
|
300 |
+
`<div><span style="display:inline-block;width:10px;height:10px;background:${colorBase};border-radius:50%;margin-right:6px;"></span><strong>Baseline</strong> ${yb.toFixed(3)}</div>` +
|
301 |
+
`<div><span style="display:inline-block;width:10px;height:10px;background:${colorImproved};border-radius:50%;margin-right:6px;"></span><strong>Improved</strong> ${yi.toFixed(3)}</div>` +
|
302 |
+
`<div><span style="display:inline-block;width:10px;height:10px;background:${colorTarget};border-radius:50%;margin-right:6px;"></span><strong>Target</strong> ${yt.toFixed(3)}</div>`;
|
303 |
+
const offsetX = 12, offsetY = 12;
|
304 |
+
tip.style.opacity = '1';
|
305 |
+
tip.style.transform = `translate(${Math.round(mx + offsetX + margin.left)}px, ${Math.round(my + offsetY + margin.top)}px)`;
|
306 |
+
}
|
307 |
+
|
308 |
+
function onLeave() {
|
309 |
+
tip.style.opacity = '0';
|
310 |
+
tip.style.transform = 'translate(-9999px, -9999px)';
|
311 |
+
hoverLine.style('display', 'none');
|
312 |
+
hoverDotB.style('display', 'none');
|
313 |
+
hoverDotI.style('display', 'none');
|
314 |
+
hoverDotT.style('display', 'none');
|
315 |
+
}
|
316 |
+
|
317 |
+
overlay.on('mousemove', onMove).on('mouseleave', onLeave);
|
318 |
+
|
319 |
+
// Init + controls wiring
|
320 |
+
computeCurves();
|
321 |
+
updateScales();
|
322 |
+
updatePaths();
|
323 |
+
|
324 |
+
// Attach controls after SVG for consistency with Plotly fragment
|
325 |
+
container.appendChild(controls);
|
326 |
+
|
327 |
+
selectDs.addEventListener('change', (e) => {
|
328 |
+
datasetIndex = parseInt(e.target.value) || 0;
|
329 |
+
applyDataset();
|
330 |
+
});
|
331 |
+
slider.addEventListener('input', (e) => {
|
332 |
+
const a = parseFloat(e.target.value) || 0;
|
333 |
+
updateAlpha(a);
|
334 |
+
});
|
335 |
+
|
336 |
+
// Resize handling
|
337 |
+
const render = () => {
|
338 |
+
updateScales();
|
339 |
+
updatePaths();
|
340 |
+
};
|
341 |
+
if (window.ResizeObserver) {
|
342 |
+
const ro = new ResizeObserver(() => render());
|
343 |
+
ro.observe(container);
|
344 |
+
} else {
|
345 |
+
window.addEventListener('resize', render);
|
346 |
+
}
|
347 |
+
render();
|
348 |
+
};
|
349 |
+
|
350 |
+
if (document.readyState === 'loading') {
|
351 |
+
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
|
352 |
+
} else { ensureD3(bootstrap); }
|
353 |
+
})();
|
354 |
+
</script>
|
355 |
+
|
356 |
+
|
fragments/plotly/banner.py
CHANGED
@@ -123,7 +123,7 @@ fig.update_layout(
|
|
123 |
# fig.show()
|
124 |
|
125 |
fig.write_html(
|
126 |
-
"../app/src/fragments/banner.html",
|
127 |
include_plotlyjs=False,
|
128 |
full_html=False,
|
129 |
config={
|
|
|
123 |
# fig.show()
|
124 |
|
125 |
fig.write_html(
|
126 |
+
"../app/src/content/fragments/banner.html",
|
127 |
include_plotlyjs=False,
|
128 |
full_html=False,
|
129 |
config={
|
fragments/plotly/bar.py
CHANGED
@@ -1,116 +1,173 @@
|
|
1 |
import plotly.graph_objects as go
|
2 |
import plotly.io as pio
|
3 |
-
import
|
4 |
|
5 |
"""
|
6 |
-
|
7 |
-
Responsive, no zoom/pan, clean hover
|
8 |
"""
|
9 |
|
10 |
-
#
|
11 |
-
|
12 |
-
|
13 |
-
improved = [0.58, 0.66, 0.72, 0.79, 0.86]
|
14 |
-
target = [0.60, 0.68, 0.75, 0.82, 0.90]
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
fig = go.Figure()
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
offsetgroup="grp",
|
36 |
-
hovertemplate="<b>%{x}</b><br>%{fullData.name}: %{y:.3f}<extra></extra>",
|
37 |
-
)
|
38 |
|
39 |
-
|
40 |
-
x=categories,
|
41 |
-
y=target,
|
42 |
-
name="Target",
|
43 |
-
marker=dict(color=color_target, opacity=0.65, line=dict(color=color_target, width=1)),
|
44 |
-
offsetgroup="grp",
|
45 |
-
hovertemplate="<b>%{x}</b><br>%{fullData.name}: %{y:.3f}<extra></extra>",
|
46 |
-
)
|
47 |
|
|
|
48 |
fig.update_layout(
|
49 |
-
barmode="
|
50 |
autosize=True,
|
51 |
paper_bgcolor="rgba(0,0,0,0)",
|
52 |
plot_bgcolor="rgba(0,0,0,0)",
|
53 |
-
margin=dict(l=
|
54 |
hovermode="x unified",
|
55 |
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="left", x=0),
|
56 |
-
xaxis=dict(
|
57 |
-
|
58 |
-
zeroline=False,
|
59 |
-
showline=True,
|
60 |
-
linecolor="rgba(0,0,0,0.25)",
|
61 |
-
linewidth=1,
|
62 |
-
ticks="outside",
|
63 |
-
ticklen=6,
|
64 |
-
tickcolor="rgba(0,0,0,0.25)",
|
65 |
-
tickfont=dict(size=12, color="rgba(0,0,0,0.65)"),
|
66 |
-
title=None,
|
67 |
-
automargin=True,
|
68 |
-
fixedrange=True,
|
69 |
-
),
|
70 |
-
yaxis=dict(
|
71 |
-
showgrid=False,
|
72 |
-
zeroline=False,
|
73 |
-
showline=True,
|
74 |
-
linecolor="rgba(0,0,0,0.25)",
|
75 |
-
linewidth=1,
|
76 |
-
ticks="outside",
|
77 |
-
ticklen=6,
|
78 |
-
tickcolor="rgba(0,0,0,0.25)",
|
79 |
-
tickfont=dict(size=12, color="rgba(0,0,0,0.65)"),
|
80 |
-
title=None,
|
81 |
-
tickformat=".2f",
|
82 |
-
automargin=True,
|
83 |
-
fixedrange=True,
|
84 |
-
),
|
85 |
)
|
86 |
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
|
|
|
111 |
config={
|
112 |
'displayModeBar': False,
|
113 |
-
'responsive': True,
|
114 |
'scrollZoom': False,
|
115 |
})
|
116 |
|
|
|
1 |
import plotly.graph_objects as go
|
2 |
import plotly.io as pio
|
3 |
+
import numpy as np
|
4 |
|
5 |
"""
|
6 |
+
Stacked bar chart: GPU memory breakdown vs sequence length, with menus for Model Size and Recomputation.
|
7 |
+
Responsive, no zoom/pan, clean hover; styled to match the minimal theme.
|
8 |
"""
|
9 |
|
10 |
+
# Axes
|
11 |
+
seq_labels = ["1024", "2048", "4096", "8192"]
|
12 |
+
seq_scale = np.array([1, 2, 4, 8], dtype=float)
|
|
|
|
|
13 |
|
14 |
+
# Components and colors (aligned with the provided example)
|
15 |
+
components = [
|
16 |
+
("parameters", "rgb(78, 165, 183)"),
|
17 |
+
("gradients", "rgb(227, 138, 66)"),
|
18 |
+
("optimizer", "rgb(232, 137, 171)"),
|
19 |
+
("activations", "rgb(206, 192, 250)"),
|
20 |
+
]
|
21 |
|
22 |
+
# Model sizes and base memory (GB) for params/grad/opt (constant vs seq), by size
|
23 |
+
model_sizes = ["1B", "3B", "8B", "70B", "405B"]
|
24 |
+
params_mem = {
|
25 |
+
"1B": 4.0,
|
26 |
+
"3B": 13.3,
|
27 |
+
"8B": 26.0,
|
28 |
+
"70B": 244.0,
|
29 |
+
"405B": 1520.0,
|
30 |
+
}
|
31 |
+
# Optimizer ~= 2x params; gradients ~= params (illustrative)
|
32 |
+
|
33 |
+
# Activations base coefficient per size (growth ~ coeff * (seq/1024)^2)
|
34 |
+
act_coeff = {
|
35 |
+
"1B": 3.6,
|
36 |
+
"3B": 9.3,
|
37 |
+
"8B": 46.2,
|
38 |
+
"70B": 145.7,
|
39 |
+
"405B": 1519.9,
|
40 |
+
}
|
41 |
+
|
42 |
+
def activations_curve(size_key: str, recompute: str) -> np.ndarray:
|
43 |
+
base = act_coeff[size_key] * (seq_scale ** 2)
|
44 |
+
if recompute == "selective":
|
45 |
+
return base * 0.25
|
46 |
+
if recompute == "full":
|
47 |
+
return base * (1.0/16.0)
|
48 |
+
return base
|
49 |
+
|
50 |
+
def stack_for(size_key: str, recompute: str):
|
51 |
+
p = np.full_like(seq_scale, params_mem[size_key], dtype=float)
|
52 |
+
g = np.full_like(seq_scale, params_mem[size_key], dtype=float)
|
53 |
+
o = np.full_like(seq_scale, 2.0 * params_mem[size_key], dtype=float)
|
54 |
+
a = activations_curve(size_key, recompute)
|
55 |
+
return {
|
56 |
+
"parameters": p,
|
57 |
+
"gradients": g,
|
58 |
+
"optimizer": o,
|
59 |
+
"activations": a,
|
60 |
+
}
|
61 |
+
|
62 |
+
# Precompute all combinations
|
63 |
+
recomp_modes = ["none", "selective", "full"]
|
64 |
+
Y = {mode: {size: stack_for(size, mode) for size in model_sizes} for mode in recomp_modes}
|
65 |
+
|
66 |
+
# Build traces: 4 traces per size (20 total). Start with size index 0 visible
|
67 |
fig = go.Figure()
|
68 |
+
for size in model_sizes:
|
69 |
+
for comp_name, color in components:
|
70 |
+
fig.add_bar(
|
71 |
+
x=seq_labels,
|
72 |
+
y=Y["none"][size][comp_name],
|
73 |
+
name=comp_name,
|
74 |
+
marker=dict(color=color),
|
75 |
+
hovertemplate="Seq len=%{x}<br>Mem=%{y:.1f}GB<br>%{data.name}<extra></extra>",
|
76 |
+
showlegend=True,
|
77 |
+
visible=(size == model_sizes[0]),
|
78 |
+
)
|
79 |
|
80 |
+
# Compute y-axis ranges per size and recomputation
|
81 |
+
def max_total(size: str, mode: str) -> float:
|
82 |
+
stacks = Y[mode][size]
|
83 |
+
totals = stacks["parameters"] + stacks["gradients"] + stacks["optimizer"] + stacks["activations"]
|
84 |
+
return float(np.max(totals))
|
|
|
|
|
|
|
85 |
|
86 |
+
layout_y_ranges = {mode: {size: 1.05 * max_total(size, mode) for size in model_sizes} for mode in recomp_modes}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
+
# Layout
|
89 |
fig.update_layout(
|
90 |
+
barmode="stack",
|
91 |
autosize=True,
|
92 |
paper_bgcolor="rgba(0,0,0,0)",
|
93 |
plot_bgcolor="rgba(0,0,0,0)",
|
94 |
+
margin=dict(l=40, r=28, t=20, b=40),
|
95 |
hovermode="x unified",
|
96 |
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="left", x=0),
|
97 |
+
xaxis=dict(title=dict(text="Sequence Length"), fixedrange=True),
|
98 |
+
yaxis=dict(title=dict(text="Memory (GB)"), fixedrange=True),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
)
|
100 |
|
101 |
+
# Updatemenus: Model Size (toggle visibility)
|
102 |
+
buttons_sizes = []
|
103 |
+
for i, size in enumerate(model_sizes):
|
104 |
+
visible = [False] * (len(model_sizes) * len(components))
|
105 |
+
start = i * len(components)
|
106 |
+
for j in range(len(components)):
|
107 |
+
visible[start + j] = True
|
108 |
+
buttons_sizes.append(dict(
|
109 |
+
label=size,
|
110 |
+
method="update",
|
111 |
+
args=[
|
112 |
+
{"visible": visible},
|
113 |
+
{"yaxis": {"range": [0, layout_y_ranges["none"][size]]}},
|
114 |
+
],
|
115 |
+
))
|
116 |
+
|
117 |
+
# Updatemenus: Recomputation (restyle y across all traces)
|
118 |
+
def y_for_mode(mode: str):
|
119 |
+
ys = []
|
120 |
+
for size in model_sizes:
|
121 |
+
stacks = Y[mode][size]
|
122 |
+
for comp_name, _ in components:
|
123 |
+
ys.append(stacks[comp_name])
|
124 |
+
return ys
|
125 |
+
|
126 |
+
buttons_recomp = []
|
127 |
+
for mode, label in [("none", "None"), ("selective", "selective"), ("full", "full")]:
|
128 |
+
ys = y_for_mode(mode)
|
129 |
+
# Flatten into the format expected by Plotly for multiple traces
|
130 |
+
buttons_recomp.append(dict(
|
131 |
+
label=label,
|
132 |
+
method="update",
|
133 |
+
args=[
|
134 |
+
{"y": ys},
|
135 |
+
{"yaxis": {"range": [0, max(layout_y_ranges[mode].values())]}},
|
136 |
+
],
|
137 |
+
))
|
138 |
+
|
139 |
+
fig.update_layout(
|
140 |
+
updatemenus=[
|
141 |
+
dict(
|
142 |
+
type="dropdown",
|
143 |
+
x=1.03, xanchor="left",
|
144 |
+
y=0.60, yanchor="top",
|
145 |
+
showactive=True,
|
146 |
+
active=0,
|
147 |
+
buttons=buttons_sizes,
|
148 |
+
),
|
149 |
+
dict(
|
150 |
+
type="dropdown",
|
151 |
+
x=1.03, xanchor="left",
|
152 |
+
y=0.40, yanchor="top",
|
153 |
+
showactive=True,
|
154 |
+
active=0,
|
155 |
+
buttons=buttons_recomp,
|
156 |
+
),
|
157 |
+
],
|
158 |
+
annotations=[
|
159 |
+
dict(text="Model Size:", x=1.03, xanchor="left", xref="paper", y=0.60, yanchor="bottom", yref="paper", showarrow=False),
|
160 |
+
dict(text="Recomputation:", x=1.03, xanchor="left", xref="paper", y=0.40, yanchor="bottom", yref="paper", showarrow=False),
|
161 |
+
],
|
162 |
+
)
|
163 |
|
164 |
+
# Write fragment
|
165 |
+
fig.write_html("../../app/src/content/fragments/bar.html",
|
166 |
+
include_plotlyjs=False,
|
167 |
+
full_html=False,
|
168 |
config={
|
169 |
'displayModeBar': False,
|
170 |
+
'responsive': True,
|
171 |
'scrollZoom': False,
|
172 |
})
|
173 |
|
fragments/plotly/heatmap.py
CHANGED
@@ -114,7 +114,7 @@ html = pio.to_html(
|
|
114 |
},
|
115 |
)
|
116 |
|
117 |
-
fig.write_html("../app/src/fragments/heatmap.html",
|
118 |
include_plotlyjs=False,
|
119 |
full_html=False,
|
120 |
config={
|
|
|
114 |
},
|
115 |
)
|
116 |
|
117 |
+
fig.write_html("../app/src/content/fragments/heatmap.html",
|
118 |
include_plotlyjs=False,
|
119 |
full_html=False,
|
120 |
config={
|
fragments/plotly/line.py
CHANGED
@@ -5,36 +5,56 @@ import os
|
|
5 |
import uuid
|
6 |
|
7 |
"""
|
8 |
-
Interactive line chart example (
|
9 |
|
10 |
-
|
11 |
-
|
|
|
|
|
12 |
"""
|
13 |
|
14 |
# Grid (x) and parameterization
|
15 |
N = 240
|
16 |
x = np.linspace(0, 1, N)
|
17 |
|
18 |
-
#
|
19 |
-
|
20 |
-
|
21 |
-
lin3 = 0.10 + 0.80 * x
|
22 |
|
23 |
-
#
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
-
#
|
28 |
-
|
29 |
-
|
30 |
-
|
|
|
|
|
31 |
|
32 |
-
#
|
33 |
-
alpha0 = 0.0
|
34 |
blend = lambda l, e, a: (1 - a) * l + a * e
|
35 |
-
y1 =
|
36 |
-
y2 = blend(
|
37 |
-
y3 =
|
38 |
|
39 |
color_base = "#64748b" # slate-500
|
40 |
color_improved = "#2563eb" # blue-600
|
@@ -79,8 +99,17 @@ fig.update_layout(
|
|
79 |
autosize=True,
|
80 |
paper_bgcolor="rgba(0,0,0,0)",
|
81 |
plot_bgcolor="rgba(0,0,0,0)",
|
82 |
-
margin=dict(l=
|
83 |
hovermode="x unified",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
hoverlabel=dict(
|
85 |
bgcolor="white",
|
86 |
font=dict(color="#111827", size=12),
|
@@ -173,17 +202,17 @@ container_id = f"line-ex-container-{uid}"
|
|
173 |
slider_tpl = '''
|
174 |
<div id="__CID__">
|
175 |
__PLOT__
|
176 |
-
<div class="plotly_controls" style="margin-top:
|
177 |
-
<label style="font-size:12px;color:rgba(0,0,0,.65); display:flex; align-items:center; gap:6px; white-space:nowrap;">
|
178 |
Dataset
|
179 |
<select id="__DSID__" style="font-size:12px; padding:2px 6px;">
|
180 |
-
<option value="0">
|
181 |
-
<option value="1">
|
182 |
-
<option value="2">
|
183 |
</select>
|
184 |
</label>
|
185 |
-
<label style="font-size:12px;color:rgba(0,0,0,.65);display:flex;align-items:center;gap:
|
186 |
-
|
187 |
<input id="__SID__" type="range" min="0" max="1" step="0.01" value="__A0__" style="flex:1;">
|
188 |
<span class="alpha-value">__A0__</span>
|
189 |
</label>
|
@@ -199,32 +228,37 @@ slider_tpl = '''
|
|
199 |
var valueEl = container.querySelector('.alpha-value');
|
200 |
var N = __N__;
|
201 |
var xs = Array.from({length: N}, function(_,i){ return i/(N-1); });
|
202 |
-
function
|
203 |
function blend(l,e,a){ return (1-a)*l + a*e; }
|
204 |
var datasets = [
|
205 |
-
{
|
206 |
-
{
|
207 |
-
{
|
208 |
];
|
209 |
var dsi = 0;
|
210 |
-
function
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
var
|
215 |
-
|
216 |
-
}
|
217 |
-
function apply(a){
|
218 |
-
var ys = makeY(a);
|
219 |
-
Plotly.restyle(gd, {y:[ys[0]]}, [0]);
|
220 |
-
Plotly.restyle(gd, {y:[ys[1]]}, [1]);
|
221 |
-
Plotly.restyle(gd, {y:[ys[2]]}, [2]);
|
222 |
if(valueEl) valueEl.textContent = a.toFixed(2);
|
223 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
var initA = parseFloat(slider.value)||0;
|
225 |
-
slider.addEventListener('input', function(e){
|
226 |
-
dsSelect.addEventListener('change', function(e){ dsi = parseInt(e.target.value)||0;
|
227 |
-
setTimeout(function(){
|
228 |
})();
|
229 |
</script>
|
230 |
'''
|
@@ -237,12 +271,6 @@ slider_html = (slider_tpl
|
|
237 |
.replace('__PLOT__', html_plot)
|
238 |
)
|
239 |
|
240 |
-
|
241 |
-
|
242 |
-
full_html=False,
|
243 |
-
config={
|
244 |
-
'displayModeBar': False,
|
245 |
-
'responsive': True,
|
246 |
-
'scrollZoom': False,
|
247 |
-
})
|
248 |
|
|
|
5 |
import uuid
|
6 |
|
7 |
"""
|
8 |
+
Interactive line chart example (Baseline / Improved / Target) with a live slider.
|
9 |
|
10 |
+
Context: research-style training curves for multiple datasets (CIFAR-10, CIFAR-100, ImageNet-1K).
|
11 |
+
The slider "Augmentation α" blends the Improved curve between the Baseline (α=0)
|
12 |
+
and an augmented counterpart (α=1) via a simple mixing equation.
|
13 |
+
Export remains responsive, with no zoom and no mode bar.
|
14 |
"""
|
15 |
|
16 |
# Grid (x) and parameterization
|
17 |
N = 240
|
18 |
x = np.linspace(0, 1, N)
|
19 |
|
20 |
+
# Logistic helper for smooth learning curves
|
21 |
+
def logistic(xv: np.ndarray, ymin: float, ymax: float, k: float, x0: float) -> np.ndarray:
|
22 |
+
return ymin + (ymax - ymin) / (1.0 + np.exp(-k * (xv - x0)))
|
|
|
23 |
|
24 |
+
# Plausible dataset params (baseline vs augmented) + a constant target line
|
25 |
+
datasets_params = [
|
26 |
+
{
|
27 |
+
"name": "CIFAR-10",
|
28 |
+
"base": {"ymin": 0.10, "ymax": 0.90, "k": 10.0, "x0": 0.55},
|
29 |
+
"aug": {"ymin": 0.15, "ymax": 0.96, "k": 12.0, "x0": 0.40},
|
30 |
+
"target": 0.97,
|
31 |
+
},
|
32 |
+
{
|
33 |
+
"name": "CIFAR-100",
|
34 |
+
"base": {"ymin": 0.05, "ymax": 0.70, "k": 9.5, "x0": 0.60},
|
35 |
+
"aug": {"ymin": 0.08, "ymax": 0.80, "k": 11.0, "x0": 0.45},
|
36 |
+
"target": 0.85,
|
37 |
+
},
|
38 |
+
{
|
39 |
+
"name": "ImageNet-1K",
|
40 |
+
"base": {"ymin": 0.02, "ymax": 0.68, "k": 8.5, "x0": 0.65},
|
41 |
+
"aug": {"ymin": 0.04, "ymax": 0.75, "k": 9.5, "x0": 0.50},
|
42 |
+
"target": 0.82,
|
43 |
+
},
|
44 |
+
]
|
45 |
|
46 |
+
# Initial dataset index and alpha
|
47 |
+
alpha0 = 0.7
|
48 |
+
ds0 = datasets_params[0]
|
49 |
+
base0 = logistic(x, **ds0["base"])
|
50 |
+
aug0 = logistic(x, **ds0["aug"])
|
51 |
+
target0 = np.full_like(x, ds0["target"], dtype=float)
|
52 |
|
53 |
+
# Traces: Baseline (fixed), Improved (blended by α), Target (constant goal)
|
|
|
54 |
blend = lambda l, e, a: (1 - a) * l + a * e
|
55 |
+
y1 = base0
|
56 |
+
y2 = blend(base0, aug0, alpha0)
|
57 |
+
y3 = target0
|
58 |
|
59 |
color_base = "#64748b" # slate-500
|
60 |
color_improved = "#2563eb" # blue-600
|
|
|
99 |
autosize=True,
|
100 |
paper_bgcolor="rgba(0,0,0,0)",
|
101 |
plot_bgcolor="rgba(0,0,0,0)",
|
102 |
+
margin=dict(l=40, r=28, t=20, b=40),
|
103 |
hovermode="x unified",
|
104 |
+
legend=dict(
|
105 |
+
orientation="v",
|
106 |
+
x=1,
|
107 |
+
y=0,
|
108 |
+
xanchor="right",
|
109 |
+
yanchor="bottom",
|
110 |
+
bgcolor="rgba(255,255,255,0)",
|
111 |
+
borderwidth=0,
|
112 |
+
),
|
113 |
hoverlabel=dict(
|
114 |
bgcolor="white",
|
115 |
font=dict(color="#111827", size=12),
|
|
|
202 |
slider_tpl = '''
|
203 |
<div id="__CID__">
|
204 |
__PLOT__
|
205 |
+
<div class="plotly_controls" style="margin-top:12px; display:flex; gap:16px; align-items:center;">
|
206 |
+
<label style="font-size:12px;color:rgba(0,0,0,.65); display:flex; align-items:center; gap:6px; white-space:nowrap; padding:6px 10px;">
|
207 |
Dataset
|
208 |
<select id="__DSID__" style="font-size:12px; padding:2px 6px;">
|
209 |
+
<option value="0">CIFAR-10</option>
|
210 |
+
<option value="1">CIFAR-100</option>
|
211 |
+
<option value="2">ImageNet-1K</option>
|
212 |
</select>
|
213 |
</label>
|
214 |
+
<label style="font-size:12px;color:rgba(0,0,0,.65);display:flex;align-items:center;gap:10px; flex:1; padding:6px 10px;">
|
215 |
+
Augmentation α
|
216 |
<input id="__SID__" type="range" min="0" max="1" step="0.01" value="__A0__" style="flex:1;">
|
217 |
<span class="alpha-value">__A0__</span>
|
218 |
</label>
|
|
|
228 |
var valueEl = container.querySelector('.alpha-value');
|
229 |
var N = __N__;
|
230 |
var xs = Array.from({length: N}, function(_,i){ return i/(N-1); });
|
231 |
+
function logistic(x, ymin, ymax, k, x0){ return ymin + (ymax - ymin) / (1 + Math.exp(-k*(x - x0))); }
|
232 |
function blend(l,e,a){ return (1-a)*l + a*e; }
|
233 |
var datasets = [
|
234 |
+
{ name:'CIFAR-10', base:{ymin:0.10,ymax:0.90,k:10.0,x0:0.55}, aug:{ymin:0.15,ymax:0.96,k:12.0,x0:0.40}, target:0.97 },
|
235 |
+
{ name:'CIFAR-100', base:{ymin:0.05,ymax:0.70,k:9.5,x0:0.60}, aug:{ymin:0.08,ymax:0.80,k:11.0,x0:0.45}, target:0.85 },
|
236 |
+
{ name:'ImageNet-1K', base:{ymin:0.02,ymax:0.68,k:8.5,x0:0.65}, aug:{ymin:0.04,ymax:0.75,k:9.5,x0:0.50}, target:0.82 }
|
237 |
];
|
238 |
var dsi = 0;
|
239 |
+
var yb = xs.map(function(x){ return logistic(x, datasets[dsi].base.ymin, datasets[dsi].base.ymax, datasets[dsi].base.k, datasets[dsi].base.x0); });
|
240 |
+
var ya = xs.map(function(x){ return logistic(x, datasets[dsi].aug.ymin, datasets[dsi].aug.ymax, datasets[dsi].aug.k, datasets[dsi].aug.x0); });
|
241 |
+
var yt = xs.map(function(){ return datasets[dsi].target; });
|
242 |
+
function applyAlpha(a){
|
243 |
+
var yi = yb.map(function(v,i){ return blend(v, ya[i], a); });
|
244 |
+
Plotly.restyle(gd, {y:[yi]}, [1]); // only Improved changes with α
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
if(valueEl) valueEl.textContent = a.toFixed(2);
|
246 |
}
|
247 |
+
function applyDataset(){
|
248 |
+
var d = datasets[dsi];
|
249 |
+
yb = xs.map(function(x){ return logistic(x, d.base.ymin, d.base.ymax, d.base.k, d.base.x0); });
|
250 |
+
ya = xs.map(function(x){ return logistic(x, d.aug.ymin, d.aug.ymax, d.aug.k, d.aug.x0); });
|
251 |
+
yt = xs.map(function(){ return d.target; });
|
252 |
+
var a = parseFloat(slider.value)||0;
|
253 |
+
var yi = yb.map(function(v,i){ return blend(v, ya[i], a); });
|
254 |
+
Plotly.restyle(gd, {y:[yb]}, [0]); // Baseline
|
255 |
+
Plotly.restyle(gd, {y:[yi]}, [1]); // Improved (blended)
|
256 |
+
Plotly.restyle(gd, {y:[yt]}, [2]); // Target
|
257 |
+
}
|
258 |
var initA = parseFloat(slider.value)||0;
|
259 |
+
slider.addEventListener('input', function(e){ applyAlpha(parseFloat(e.target.value)||0); });
|
260 |
+
dsSelect.addEventListener('change', function(e){ dsi = parseInt(e.target.value)||0; applyDataset(); });
|
261 |
+
setTimeout(function(){ applyDataset(); applyAlpha(initA); }, 0);
|
262 |
})();
|
263 |
</script>
|
264 |
'''
|
|
|
271 |
.replace('__PLOT__', html_plot)
|
272 |
)
|
273 |
|
274 |
+
with open("../../app/src/content/fragments/line.html", "w", encoding="utf-8") as f:
|
275 |
+
f.write(slider_html)
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
|