Fix: Link Error on Page 17
#117
by
thliang01
- opened
- README.md +0 -3
- assets/images/moon.svg +0 -4
- assets/images/sun.svg +0 -12
- convert_to_md.py +0 -110
- dist/assets/images/memorycoalescing.png +2 -2
- dist/assets/images/moon.svg +0 -1
- dist/assets/images/sun.svg +0 -1
- dist/distill.bundle.js +1 -1
- dist/distill.bundle.js.map +0 -0
- dist/fragments/banner.html +0 -0
- dist/index.html +0 -0
- dist/main.bundle.js +0 -187
- dist/main.bundle.js.map +0 -0
- dist/style.css +1 -138
- src/distill.js +6 -1
- src/fragments/banner.html +0 -0
- src/index.html +3 -14
- src/index.js +0 -228
- src/style.css +1 -138
- ultra_blog.md +0 -0
README.md
CHANGED
@@ -18,9 +18,6 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
18 |
Instruction to install and run locally
|
19 |
|
20 |
```bash
|
21 |
-
# fetch LFS-tracked files (large assets)
|
22 |
-
git lfs pull
|
23 |
-
|
24 |
npm install
|
25 |
npm run build
|
26 |
npm run dev
|
|
|
18 |
Instruction to install and run locally
|
19 |
|
20 |
```bash
|
|
|
|
|
|
|
21 |
npm install
|
22 |
npm run build
|
23 |
npm run dev
|
assets/images/moon.svg
DELETED
assets/images/sun.svg
DELETED
convert_to_md.py
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
HTML to Markdown Converter
|
4 |
-
|
5 |
-
This script converts HTML files to Markdown format.
|
6 |
-
Usage: python html_to_md.py input.html [output.md]
|
7 |
-
If no output file is specified, it will use the input filename with .md extension.
|
8 |
-
"""
|
9 |
-
|
10 |
-
import sys
|
11 |
-
import os
|
12 |
-
import argparse
|
13 |
-
import html2text
|
14 |
-
import requests
|
15 |
-
from urllib.parse import urlparse
|
16 |
-
|
17 |
-
def is_url(path):
|
18 |
-
"""Check if the given path is a URL."""
|
19 |
-
parsed = urlparse(path)
|
20 |
-
return parsed.scheme != '' and parsed.netloc != ''
|
21 |
-
|
22 |
-
def convert_html_to_markdown(html_content, **options):
|
23 |
-
"""Convert HTML content to Markdown."""
|
24 |
-
converter = html2text.HTML2Text()
|
25 |
-
|
26 |
-
# Configure converter options
|
27 |
-
converter.ignore_links = options.get('ignore_links', False)
|
28 |
-
converter.ignore_images = options.get('ignore_images', False)
|
29 |
-
converter.ignore_tables = options.get('ignore_tables', False)
|
30 |
-
converter.body_width = options.get('body_width', 0) # 0 means no wrapping
|
31 |
-
converter.unicode_snob = options.get('unicode_snob', True) # Use Unicode instead of ASCII
|
32 |
-
converter.wrap_links = options.get('wrap_links', False)
|
33 |
-
converter.inline_links = options.get('inline_links', True)
|
34 |
-
|
35 |
-
# Convert HTML to Markdown
|
36 |
-
return converter.handle(html_content)
|
37 |
-
|
38 |
-
def main():
|
39 |
-
parser = argparse.ArgumentParser(description='Convert HTML to Markdown')
|
40 |
-
parser.add_argument('input', help='Input HTML file or URL')
|
41 |
-
parser.add_argument('output', nargs='?', help='Output Markdown file (optional)')
|
42 |
-
parser.add_argument('--ignore-links', action='store_true', help='Ignore links in the HTML')
|
43 |
-
parser.add_argument('--ignore-images', action='store_true', help='Ignore images in the HTML')
|
44 |
-
parser.add_argument('--ignore-tables', action='store_true', help='Ignore tables in the HTML')
|
45 |
-
parser.add_argument('--body-width', type=int, default=0, help='Wrap text at this width (0 for no wrapping)')
|
46 |
-
parser.add_argument('--unicode', action='store_true', help='Use Unicode characters instead of ASCII approximations')
|
47 |
-
parser.add_argument('--wrap-links', action='store_true', help='Wrap links in angle brackets')
|
48 |
-
parser.add_argument('--reference-links', action='store_true', help='Use reference style links instead of inline links')
|
49 |
-
|
50 |
-
args = parser.parse_args()
|
51 |
-
|
52 |
-
# Determine input
|
53 |
-
if is_url(args.input):
|
54 |
-
try:
|
55 |
-
response = requests.get(args.input)
|
56 |
-
response.raise_for_status()
|
57 |
-
html_content = response.text
|
58 |
-
except requests.exceptions.RequestException as e:
|
59 |
-
print(f"Error fetching URL: {e}", file=sys.stderr)
|
60 |
-
return 1
|
61 |
-
else:
|
62 |
-
try:
|
63 |
-
with open(args.input, 'r', encoding='utf-8') as f:
|
64 |
-
html_content = f.read()
|
65 |
-
except IOError as e:
|
66 |
-
print(f"Error reading file: {e}", file=sys.stderr)
|
67 |
-
return 1
|
68 |
-
|
69 |
-
# Configure conversion options
|
70 |
-
options = {
|
71 |
-
'ignore_links': args.ignore_links,
|
72 |
-
'ignore_images': args.ignore_images,
|
73 |
-
'ignore_tables': args.ignore_tables,
|
74 |
-
'body_width': args.body_width,
|
75 |
-
'unicode_snob': args.unicode,
|
76 |
-
'wrap_links': args.wrap_links,
|
77 |
-
'inline_links': not args.reference_links,
|
78 |
-
}
|
79 |
-
|
80 |
-
# Convert HTML to Markdown
|
81 |
-
markdown_content = convert_html_to_markdown(html_content, **options)
|
82 |
-
|
83 |
-
# Determine output
|
84 |
-
if args.output:
|
85 |
-
output_file = args.output
|
86 |
-
else:
|
87 |
-
if is_url(args.input):
|
88 |
-
# Generate a filename from the URL
|
89 |
-
url_parts = urlparse(args.input)
|
90 |
-
base_name = os.path.basename(url_parts.path) or 'index'
|
91 |
-
if not base_name.endswith('.html'):
|
92 |
-
base_name += '.html'
|
93 |
-
output_file = os.path.splitext(base_name)[0] + '.md'
|
94 |
-
else:
|
95 |
-
# Generate a filename from the input file
|
96 |
-
output_file = os.path.splitext(args.input)[0] + '.md'
|
97 |
-
|
98 |
-
# Write output
|
99 |
-
try:
|
100 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
101 |
-
f.write(markdown_content)
|
102 |
-
print(f"Conversion successful! Output saved to: {output_file}")
|
103 |
-
except IOError as e:
|
104 |
-
print(f"Error writing file: {e}", file=sys.stderr)
|
105 |
-
return 1
|
106 |
-
|
107 |
-
return 0
|
108 |
-
|
109 |
-
if __name__ == "__main__":
|
110 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dist/assets/images/memorycoalescing.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
dist/assets/images/moon.svg
DELETED
dist/assets/images/sun.svg
DELETED
dist/distill.bundle.js
CHANGED
@@ -2146,7 +2146,7 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
2146 |
function bylineTemplate(frontMatter) {
|
2147 |
return "\n <div class=\"byline grid\">\n <div>\n <h3>Authors</h3>\n <div>\n ".concat(frontMatter.authors.map(function (author, i) {
|
2148 |
return "\n <span class=\"author\">\n ".concat(author.personalURL ? "\n <a class=\"name\" href=\"".concat(author.personalURL, "\">").concat(author.name) + (i + 1 < frontMatter.authors.length ? "," : "") + "</a>" : "\n <span class=\"name\">".concat(author.name) + (i + 1 < frontMatter.authors.length ? "," : "") + "</span>", "\n </span>\n ");
|
2149 |
-
}).join(''), "\n </div>\n </div>\n <div >\n <h3>Affiliation</h3>\n <div><a href=\"https://huggingface.co/\">Hugging Face</a>\n </div>\n </div>\n <div >\n <h3>Published</h3>\n <div>Feb 19, 2025</div>\n </div>\n </div>\n\n");
|
2150 |
}
|
2151 |
var Byline = /*#__PURE__*/function (_HTMLElement4) {
|
2152 |
function Byline() {
|
|
|
2146 |
function bylineTemplate(frontMatter) {
|
2147 |
return "\n <div class=\"byline grid\">\n <div>\n <h3>Authors</h3>\n <div>\n ".concat(frontMatter.authors.map(function (author, i) {
|
2148 |
return "\n <span class=\"author\">\n ".concat(author.personalURL ? "\n <a class=\"name\" href=\"".concat(author.personalURL, "\">").concat(author.name) + (i + 1 < frontMatter.authors.length ? "," : "") + "</a>" : "\n <span class=\"name\">".concat(author.name) + (i + 1 < frontMatter.authors.length ? "," : "") + "</span>", "\n </span>\n ");
|
2149 |
+
}).join(''), "\n </div>\n </div>\n <div >\n <h3>Affiliation</h3>\n <div><a href=\"https://huggingface.co/\">Hugging Face</a>\n </div>\n </div>\n <div >\n <h3>Published</h3>\n <div>Feb 19, 2025</div>\n </div>\n </div>\n <div class=\"side pdf-download\">\n <a href=\"https://huggingface.co/spaces/nanotron/ultrascale-playbook/resolve/main/The_Ultra-Scale_Playbook_Training_LLMs_on_GPU_Clusters.pdf\">Download PDF\n <br>\n <img style=\"width: 32px;\" src=\"../assets/images/256px-PDF.png\" alt=\"PDF\"></a>\n \n </div>\n");
|
2150 |
}
|
2151 |
var Byline = /*#__PURE__*/function (_HTMLElement4) {
|
2152 |
function Byline() {
|
dist/distill.bundle.js.map
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
dist/fragments/banner.html
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
dist/index.html
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
dist/main.bundle.js
CHANGED
@@ -5662,199 +5662,12 @@ function postMessageToHFSpaces(elementId) {
|
|
5662 |
}
|
5663 |
|
5664 |
;// ./src/index.js
|
5665 |
-
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = src_unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
5666 |
-
function src_unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return src_arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? src_arrayLikeToArray(r, a) : void 0; } }
|
5667 |
-
function src_arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
5668 |
// import { plotClusters } from './clusters'
|
5669 |
|
5670 |
|
5671 |
|
5672 |
-
// Dark mode is now handled manually via a CSS class on <html> and injected styles
|
5673 |
-
|
5674 |
document.addEventListener("DOMContentLoaded", function () {
|
5675 |
console.log("DOMContentLoaded");
|
5676 |
-
|
5677 |
-
// Inject minimal styles for the theme toggle button
|
5678 |
-
var styleEl = document.createElement('style');
|
5679 |
-
styleEl.textContent = "\n .theme-toggle-btn{position:absolute;top:16px;left:16px;z-index:10000;display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:999px;background:rgba(255,255,255,0.9);backdrop-filter:saturate(150%) blur(6px);cursor:pointer;border:1px solid transparent;outline:none;box-shadow:none;-webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent}\n .theme-toggle-btn:hover{border-color:transparent;box-shadow:none}\n .theme-toggle-btn:focus,.theme-toggle-btn:focus-visible{outline:none;border-color:transparent;box-shadow:none}\n .theme-toggle-btn img{width:22px;height:22px;transition:filter .15s ease}\n .theme-toggle-btn.dark img{filter: brightness(0) invert(1)}\n @media (prefers-color-scheme: dark){.theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent;box-shadow:none}}\n ";
|
5680 |
-
document.head.appendChild(styleEl);
|
5681 |
-
|
5682 |
-
// Inject dark mode CSS (scoped via html.dark)
|
5683 |
-
var darkCSS = "\n html.dark{color-scheme:dark}\n html.dark body{background:#242525;color:#e5e7eb}\n html.dark a{color:#93c5fd}\n html.dark .figure-legend{color:#9ca3af}\n html.dark d-article, html.dark d-article p, html.dark d-article aside{color:white !important;}\n html.dark d-contents{background:#242525}\n html.dark d-contents nav a{color:#cbd5e1}\n html.dark d-contents nav a:hover{text-decoration:underline solid rgba(255,255,255,0.6)}\n html.dark .note-box{background:#111;border-left-color:#888}\n html.dark .note-box-title{color:#d1d5db}\n html.dark .note-box-content{color:#e5e7eb}\n html.dark .large-image-background{background:#242525}\n html.dark .boxed-image{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6)}\n html.dark #graph-all,html.dark #controls,html.dark .memory-block,html.dark .activation-memory,html.dark .gradient-memory{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6);color:#e5e7eb}\n html.dark label,html.dark .memory-title{color:#e5e7eb}\n html.dark .memory-value{color:#93c5fd}\n html.dark input,html.dark select,html.dark textarea{background:#0f0f0f;color:#e5e7eb;border:1px solid #333}\n html.dark input:hover,html.dark select:hover,html.dark textarea:hover{border-color:#60a5fa}\n html.dark input:focus,html.dark select:focus,html.dark textarea:focus{border-color:#3b82f6;box-shadow:0 0 0 2px rgba(59,130,246,0.35)}\n html.dark input[type=range]{background:#333}\n html.dark input[type=range]::-webkit-slider-thumb{background:#3b82f6}\n html.dark .plotly_caption{color:#9ca3af}\n html.dark .theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent}\n html.dark d-article img{background:white}\n html.dark summary {color:black !important;}\n html.dark .katex-container {color:white !important;}\n html.dark d-code {background: white!important;}\n html.dark .code-block div { background: white!important;}\n html.dark .code-block div p { color: black!important;}\n /* Table borders in dark mode */\n html.dark table{border-color:rgba(255,255,255,0.3)}\n html.dark th,html.dark td{border-color:rgba(255,255,255,0.3)}\n html.dark thead tr,html.dark tbody tr{border-color:rgba(255,255,255,0.3)}\n html.dark d-byline, html.dark d-article{border-top: 1px solid rgba(255, 255, 255, 0.5);}\n html.dark d-byline h3{color:white;}\n html.dark d-math *, html.dark span.katex{color:white !important;}\n html.dark d-appendix { color: white}\n html.dark h2 { border-bottom: 1px solid rgba(255, 255, 255, 0.25);}\n html.dark h1, html.dark h2, html.dark h3, html.dark h4, html.dark h5, html.dark h6 { color: white}\n html.dark .code-area { color: black;}\n html.dark .code-area a { color: black!important;}\n \n ";
|
5684 |
-
var darkStyleEl = document.createElement('style');
|
5685 |
-
darkStyleEl.id = 'darkmode-css';
|
5686 |
-
darkStyleEl.textContent = darkCSS;
|
5687 |
-
document.head.appendChild(darkStyleEl);
|
5688 |
-
|
5689 |
-
// Inject equivalent dark CSS into all ShadowRoots using :host-context(.dark)
|
5690 |
-
// This ensures styles also apply inside web components with Shadow DOM
|
5691 |
-
var shadowDarkCSS = darkCSS.replace(/html\.dark/g, ':host-context(.dark)');
|
5692 |
-
var injectDarkStylesIntoRoot = function injectDarkStylesIntoRoot(root) {
|
5693 |
-
// Only target ShadowRoots here
|
5694 |
-
if (!root || !(root instanceof ShadowRoot)) return;
|
5695 |
-
if (root.querySelector('style#darkmode-css-shadow')) return;
|
5696 |
-
var style = document.createElement('style');
|
5697 |
-
style.id = 'darkmode-css-shadow';
|
5698 |
-
style.textContent = shadowDarkCSS;
|
5699 |
-
root.appendChild(style);
|
5700 |
-
};
|
5701 |
-
|
5702 |
-
// Normalize inline SVGs: ensure viewBox and preserveAspectRatio for responsiveness
|
5703 |
-
var normalizeSvgElement = function normalizeSvgElement(svgEl) {
|
5704 |
-
try {
|
5705 |
-
if (!svgEl || svgEl.hasAttribute('viewBox')) return;
|
5706 |
-
var widthAttr = svgEl.getAttribute('width');
|
5707 |
-
var heightAttr = svgEl.getAttribute('height');
|
5708 |
-
if (!widthAttr || !heightAttr) return;
|
5709 |
-
var width = parseFloat(widthAttr);
|
5710 |
-
var height = parseFloat(heightAttr);
|
5711 |
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
|
5712 |
-
svgEl.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
|
5713 |
-
if (!svgEl.hasAttribute('preserveAspectRatio')) {
|
5714 |
-
svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
5715 |
-
}
|
5716 |
-
} catch (_) {
|
5717 |
-
// no-op
|
5718 |
-
}
|
5719 |
-
};
|
5720 |
-
var processRootForSVGs = function processRootForSVGs(root) {
|
5721 |
-
if (!root || typeof root.querySelectorAll !== 'function') return;
|
5722 |
-
var svgs = root.querySelectorAll('svg:not([viewBox])');
|
5723 |
-
svgs.forEach(function (svg) {
|
5724 |
-
return normalizeSvgElement(svg);
|
5725 |
-
});
|
5726 |
-
};
|
5727 |
-
var _scanNodeForShadowRoots = function scanNodeForShadowRoots(node) {
|
5728 |
-
if (!node) return;
|
5729 |
-
if (node.shadowRoot) {
|
5730 |
-
injectDarkStylesIntoRoot(node.shadowRoot);
|
5731 |
-
processRootForSVGs(node.shadowRoot);
|
5732 |
-
}
|
5733 |
-
// Traverse children
|
5734 |
-
if (node.childNodes && node.childNodes.length) {
|
5735 |
-
node.childNodes.forEach(function (child) {
|
5736 |
-
// Process SVGs in this subtree as well
|
5737 |
-
processRootForSVGs(child);
|
5738 |
-
_scanNodeForShadowRoots(child);
|
5739 |
-
});
|
5740 |
-
}
|
5741 |
-
};
|
5742 |
-
|
5743 |
-
// Intercept future shadow roots
|
5744 |
-
var originalAttachShadow = Element.prototype.attachShadow;
|
5745 |
-
Element.prototype.attachShadow = function (init) {
|
5746 |
-
var shadow = originalAttachShadow.call(this, init);
|
5747 |
-
try {
|
5748 |
-
injectDarkStylesIntoRoot(shadow);
|
5749 |
-
processRootForSVGs(shadow);
|
5750 |
-
} catch (e) {}
|
5751 |
-
return shadow;
|
5752 |
-
};
|
5753 |
-
|
5754 |
-
// Initial sweep for any existing shadow roots
|
5755 |
-
_scanNodeForShadowRoots(document.documentElement);
|
5756 |
-
// Initial pass for regular DOM SVGs
|
5757 |
-
processRootForSVGs(document);
|
5758 |
-
|
5759 |
-
// Observe DOM mutations to catch dynamically added components
|
5760 |
-
var mo = new MutationObserver(function (mutations) {
|
5761 |
-
var _iterator = _createForOfIteratorHelper(mutations),
|
5762 |
-
_step;
|
5763 |
-
try {
|
5764 |
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
5765 |
-
var m = _step.value;
|
5766 |
-
m.addedNodes && m.addedNodes.forEach(function (n) {
|
5767 |
-
_scanNodeForShadowRoots(n);
|
5768 |
-
processRootForSVGs(n);
|
5769 |
-
});
|
5770 |
-
}
|
5771 |
-
} catch (err) {
|
5772 |
-
_iterator.e(err);
|
5773 |
-
} finally {
|
5774 |
-
_iterator.f();
|
5775 |
-
}
|
5776 |
-
});
|
5777 |
-
mo.observe(document.documentElement, {
|
5778 |
-
childList: true,
|
5779 |
-
subtree: true
|
5780 |
-
});
|
5781 |
-
|
5782 |
-
// Create the toggle button
|
5783 |
-
var btn = document.createElement('button');
|
5784 |
-
btn.className = 'theme-toggle-btn';
|
5785 |
-
btn.setAttribute('type', 'button');
|
5786 |
-
btn.setAttribute('aria-label', 'Basculer le mode sombre');
|
5787 |
-
// Reuse icons declared in HTML and move them into the button
|
5788 |
-
var sunIcon = document.getElementById('sunIcon');
|
5789 |
-
var moonIcon = document.getElementById('moonIcon');
|
5790 |
-
if (sunIcon && moonIcon) {
|
5791 |
-
// Make sure they adopt button sizing
|
5792 |
-
sunIcon.style.display = 'none';
|
5793 |
-
sunIcon.style.width = '22px';
|
5794 |
-
sunIcon.style.height = '22px';
|
5795 |
-
moonIcon.style.display = 'none';
|
5796 |
-
moonIcon.style.width = '22px';
|
5797 |
-
moonIcon.style.height = '22px';
|
5798 |
-
btn.appendChild(sunIcon);
|
5799 |
-
btn.appendChild(moonIcon);
|
5800 |
-
}
|
5801 |
-
document.body.appendChild(btn);
|
5802 |
-
var setIcon = function setIcon(enabled) {
|
5803 |
-
// enabled = dark mode enabled -> show sun (to indicate turning off), hide moon
|
5804 |
-
sunIcon.style.display = enabled ? '' : 'none';
|
5805 |
-
moonIcon.style.display = enabled ? 'none' : '';
|
5806 |
-
btn.setAttribute('title', enabled ? 'Désactiver le mode sombre' : 'Activer le mode sombre');
|
5807 |
-
btn.setAttribute('aria-pressed', String(enabled));
|
5808 |
-
btn.classList.toggle('dark', enabled);
|
5809 |
-
};
|
5810 |
-
var setDark = function setDark(enabled) {
|
5811 |
-
document.documentElement.classList.toggle('dark', enabled);
|
5812 |
-
setIcon(enabled);
|
5813 |
-
};
|
5814 |
-
var THEME_KEY = 'theme';
|
5815 |
-
var savedTheme = null;
|
5816 |
-
try {
|
5817 |
-
savedTheme = localStorage.getItem(THEME_KEY);
|
5818 |
-
} catch (e) {}
|
5819 |
-
var media = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
|
5820 |
-
var prefersDark = media ? media.matches : false;
|
5821 |
-
// Initialisation: priorité à la préférence sauvegardée, sinon préférence système
|
5822 |
-
if (savedTheme === 'dark') {
|
5823 |
-
setDark(true);
|
5824 |
-
} else if (savedTheme === 'light') {
|
5825 |
-
setDark(false);
|
5826 |
-
} else {
|
5827 |
-
setDark(prefersDark);
|
5828 |
-
}
|
5829 |
-
|
5830 |
-
// Si l'utilisateur a déjà choisi manuellement, on ne suit plus la préférence système
|
5831 |
-
var manualOverride = savedTheme === 'dark' || savedTheme === 'light';
|
5832 |
-
|
5833 |
-
// React to system preference changes dynamically (no persistence)
|
5834 |
-
if (media && typeof media.addEventListener === 'function') {
|
5835 |
-
media.addEventListener('change', function (e) {
|
5836 |
-
if (!manualOverride) {
|
5837 |
-
setDark(e.matches);
|
5838 |
-
}
|
5839 |
-
});
|
5840 |
-
} else if (media && typeof media.addListener === 'function') {
|
5841 |
-
// Fallback for older browsers
|
5842 |
-
media.addListener(function (e) {
|
5843 |
-
if (!manualOverride) {
|
5844 |
-
setDark(e.matches);
|
5845 |
-
}
|
5846 |
-
});
|
5847 |
-
}
|
5848 |
-
|
5849 |
-
// Toggle handler — for réduire les glitches, attendre le next frame avant d'ajuster l'icône
|
5850 |
-
btn.addEventListener('click', function () {
|
5851 |
-
manualOverride = true;
|
5852 |
-
var next = !document.documentElement.classList.contains('dark');
|
5853 |
-
setDark(next);
|
5854 |
-
try {
|
5855 |
-
localStorage.setItem(THEME_KEY, next ? 'dark' : 'light');
|
5856 |
-
} catch (e) {}
|
5857 |
-
});
|
5858 |
loadFragments();
|
5859 |
init_memory_plot();
|
5860 |
syncHFSpacesURLHash();
|
|
|
5662 |
}
|
5663 |
|
5664 |
;// ./src/index.js
|
|
|
|
|
|
|
5665 |
// import { plotClusters } from './clusters'
|
5666 |
|
5667 |
|
5668 |
|
|
|
|
|
5669 |
document.addEventListener("DOMContentLoaded", function () {
|
5670 |
console.log("DOMContentLoaded");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5671 |
loadFragments();
|
5672 |
init_memory_plot();
|
5673 |
syncHFSpacesURLHash();
|
dist/main.bundle.js.map
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
dist/style.css
CHANGED
@@ -304,6 +304,7 @@ d-contents nav > ul > li > a:hover {
|
|
304 |
border-radius: 6px;
|
305 |
/* Add this to ensure the box only takes up needed space */
|
306 |
display: inline-block;
|
|
|
307 |
}
|
308 |
|
309 |
.note-box-title {
|
@@ -596,141 +597,3 @@ select[name="presets"] {
|
|
596 |
border-radius: 8px;
|
597 |
}
|
598 |
|
599 |
-
.order-button-second {
|
600 |
-
background: linear-gradient(135deg, #6DB4C4, #D4A5B8);
|
601 |
-
color: white;
|
602 |
-
font-size: 18px;
|
603 |
-
font-weight: 600;
|
604 |
-
padding: 20px 20px;
|
605 |
-
border: none;
|
606 |
-
border-radius: 12px;
|
607 |
-
cursor: pointer;
|
608 |
-
text-transform: uppercase;
|
609 |
-
letter-spacing: 1px;
|
610 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
611 |
-
transition: all 0.3s ease;
|
612 |
-
position: relative;
|
613 |
-
overflow: hidden;
|
614 |
-
}
|
615 |
-
.order-button-second:hover {
|
616 |
-
transform: translateY(-2px);
|
617 |
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
618 |
-
}
|
619 |
-
|
620 |
-
.order-button:active {
|
621 |
-
transform: translateY(0);
|
622 |
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
623 |
-
}
|
624 |
-
|
625 |
-
.order-button-second::before {
|
626 |
-
content: '';
|
627 |
-
position: absolute;
|
628 |
-
top: 0;
|
629 |
-
left: -100%;
|
630 |
-
width: 100%;
|
631 |
-
height: 100%;
|
632 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0));
|
633 |
-
transition: left 0.5s ease;
|
634 |
-
}
|
635 |
-
|
636 |
-
.order-button-second:hover::before {
|
637 |
-
left: 100%;
|
638 |
-
}
|
639 |
-
|
640 |
-
.order-button {
|
641 |
-
background: linear-gradient(135deg, #6DB4C4, #D4A5B8);
|
642 |
-
color: white;
|
643 |
-
font-size: 18px;
|
644 |
-
font-weight: 600;
|
645 |
-
padding: 16px 32px;
|
646 |
-
border: none;
|
647 |
-
border-radius: 12px;
|
648 |
-
cursor: pointer;
|
649 |
-
text-transform: uppercase;
|
650 |
-
letter-spacing: 1px;
|
651 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
652 |
-
transition: all 0.3s ease;
|
653 |
-
position: relative;
|
654 |
-
overflow: hidden;
|
655 |
-
}
|
656 |
-
|
657 |
-
.order-button:hover {
|
658 |
-
transform: translateY(-2px);
|
659 |
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
660 |
-
}
|
661 |
-
|
662 |
-
.order-button:active {
|
663 |
-
transform: translateY(0);
|
664 |
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
665 |
-
}
|
666 |
-
|
667 |
-
.order-button::before {
|
668 |
-
content: '';
|
669 |
-
position: absolute;
|
670 |
-
top: 0;
|
671 |
-
left: -100%;
|
672 |
-
width: 100%;
|
673 |
-
height: 100%;
|
674 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0));
|
675 |
-
transition: left 0.5s ease;
|
676 |
-
}
|
677 |
-
|
678 |
-
.order-button:hover::before {
|
679 |
-
left: 100%;
|
680 |
-
}
|
681 |
-
.order-button-container-second {
|
682 |
-
/* display: flex; */
|
683 |
-
justify-content: center;
|
684 |
-
margin: 0px 0;
|
685 |
-
}
|
686 |
-
|
687 |
-
.order-button-container {
|
688 |
-
display: flex;
|
689 |
-
justify-content: center;
|
690 |
-
margin: 0px 0 40px 0;
|
691 |
-
}
|
692 |
-
|
693 |
-
d-article img {
|
694 |
-
width: 100%!important;
|
695 |
-
}
|
696 |
-
|
697 |
-
|
698 |
-
iframe, .js-plotly-plot {
|
699 |
-
width: 100%!important;
|
700 |
-
margin-bottom: 20px;
|
701 |
-
}
|
702 |
-
|
703 |
-
.modebar-container {
|
704 |
-
display: none;
|
705 |
-
}
|
706 |
-
|
707 |
-
#graph-container {
|
708 |
-
display: grid; grid-template-columns: 1fr 1fr; align-items: center;
|
709 |
-
}
|
710 |
-
|
711 |
-
@media (max-width: 768px) {
|
712 |
-
#graph-container {
|
713 |
-
grid-template-columns: 1fr;
|
714 |
-
}
|
715 |
-
}
|
716 |
-
|
717 |
-
@media (max-width: 1024px) {
|
718 |
-
#graph-container {
|
719 |
-
grid-template-columns: 1fr;
|
720 |
-
}
|
721 |
-
#graph-all {
|
722 |
-
margin-right: 0px;
|
723 |
-
}
|
724 |
-
#controls {
|
725 |
-
margin-left: 0px;
|
726 |
-
}
|
727 |
-
}
|
728 |
-
|
729 |
-
.main-plot-container svg {
|
730 |
-
background: transparent !important;
|
731 |
-
}
|
732 |
-
|
733 |
-
.large-image-background-transparent {
|
734 |
-
margin-left: 0px;
|
735 |
-
margin-right: 0px;
|
736 |
-
}
|
|
|
304 |
border-radius: 6px;
|
305 |
/* Add this to ensure the box only takes up needed space */
|
306 |
display: inline-block;
|
307 |
+
width: 100%;
|
308 |
}
|
309 |
|
310 |
.note-box-title {
|
|
|
597 |
border-radius: 8px;
|
598 |
}
|
599 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/distill.js
CHANGED
@@ -2105,7 +2105,12 @@ d-appendix > distill-appendix {
|
|
2105 |
<div>Feb 19, 2025</div>
|
2106 |
</div>
|
2107 |
</div>
|
2108 |
-
|
|
|
|
|
|
|
|
|
|
|
2109 |
`;
|
2110 |
}
|
2111 |
|
|
|
2105 |
<div>Feb 19, 2025</div>
|
2106 |
</div>
|
2107 |
</div>
|
2108 |
+
<div class="side pdf-download">
|
2109 |
+
<a href="https://huggingface.co/spaces/nanotron/ultrascale-playbook/resolve/main/The_Ultra-Scale_Playbook_Training_LLMs_on_GPU_Clusters.pdf">Download PDF
|
2110 |
+
<br>
|
2111 |
+
<img style="width: 32px;" src="../assets/images/256px-PDF.png" alt="PDF"></a>
|
2112 |
+
|
2113 |
+
</div>
|
2114 |
`;
|
2115 |
}
|
2116 |
|
src/fragments/banner.html
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
src/index.html
CHANGED
@@ -58,9 +58,6 @@
|
|
58 |
</script>
|
59 |
</d-front-matter>
|
60 |
<d-title>
|
61 |
-
<!-- Theme toggle icons (declared once in DOM for clarity; visibility controlled via JS) -->
|
62 |
-
<img id="sunIcon" src="assets/images/sun.svg" alt="Sun icon" style="display:none;width:22px;height:22px;color:inherit"/>
|
63 |
-
<img id="moonIcon" src="assets/images/moon.svg" alt="Moon icon" style="display:none;width:22px;height:22px;color:inherit"/>
|
64 |
<h1 class="l-page" style="text-align: center;">The Ultra-Scale Playbook:<br>Training LLMs on GPU Clusters</h1>
|
65 |
<div id="title-plot" class="main-plot-container l-screen" style="overflow-x: hidden; width: 100%; text-align: center;">
|
66 |
<div style="display: flex; justify-content: center; position: relative;">
|
@@ -68,14 +65,8 @@
|
|
68 |
</div>
|
69 |
<p style="text-align: cekter; font-style: italic; margin-top: 10px; max-width: 900px; margin-left: auto; margin-right: auto;">We ran over 4,000 scaling experiments on up to 512 GPUs and measured throughput (size of markers) and GPU utilization (color of markers). Note that both are normalized per model size in this visualization.</p>
|
70 |
|
71 |
-
|
72 |
</div>
|
73 |
-
|
74 |
</d-title>
|
75 |
-
<div class="order-button-container">
|
76 |
-
<button class="order-button" style="margin: 0 8px;" onclick="window.open('https://www.lulu.com/shop/nouamane-tazi-and-ferdinand-mom-and-haojun-zhao-and-phuc-nguyen/the-ultra-scale-playbook/paperback/product-45yk9dj.html?page=1&pageSize=4', '_blank')">Order Book</button>
|
77 |
-
<button class="order-button" style="margin: 0 8px;" onclick="window.open('https://huggingface.co/nanotron', '_blank')">Get PDF</button>
|
78 |
-
</div>
|
79 |
<d-byline></d-byline>
|
80 |
<d-article>
|
81 |
<d-contents>
|
@@ -85,12 +76,10 @@
|
|
85 |
Thousands of GPUs humming in perfect harmony. That's what it takes to train today's most powerful AI models – a symphony of computing power that until recently was the exclusive domain of elite research labs. Open source has transformed this landscape, but not completely. Yes, you can download the latest <a href="https://huggingface.co/meta-llama">Llama</a> or <a href="https://huggingface.co/deepseek-ai">DeepSeek</a> models. Yes, you can read their <a href="https://ai.meta.com/research/publications/the-llama-3-herd-of-models/">technical</a> and <a href="https://github.com/deepseek-ai/DeepSeek-R1/blob/main/DeepSeek_R1.pdf">experiment</a> reports. But the most challenging part – the training code, the knowledge and techniques necessary to coordinate GPUs to train these massive systems – remains shrouded in complexity and spread around in a series of disconnected papers and often private codebases.
|
86 |
</p>
|
87 |
<aside>Reading time: 2-4 days. <br>For the best reading experience, we recommend not using a mobile phone.</aside>
|
88 |
-
|
89 |
-
<p>
|
90 |
This open source book is here to change that. Starting from the basics, we'll walk you through the knowledge necessary to scale the training of large language models (LLMs) from one GPU to tens, hundreds, and even thousands of GPUs, illustrating theory with practical code examples and reproducible benchmarks.
|
91 |
</p>
|
92 |
|
93 |
-
|
94 |
<p>As the size of the clusters used to train these models has grown, various techniques, such as data parallelism, tensor parallelism, pipeline parallelism, and context parallelism as well as ZeRO and kernel fusion, have been invented to make sure that GPUs are highly utilized at all times. This significantly reduces training time and makes the most efficient use of this expensive hardware. These distributed training techniques are not only important for building initial models but have also become essential for fine-tuning large models on specialized data, which often produces the best results. In this book, we'll progressively go over all of these techniques – from the simplest to the most refined ones – while maintaining a single story line to help you understand where each method comes from.</p>
|
95 |
|
96 |
<aside>If you have questions or remarks, open a discussion on the <a href="https://huggingface.co/spaces/nanotron/ultrascale-playbook/discussions?status=open&type=discussion">Community tab</a>!</aside>
|
@@ -106,7 +95,7 @@
|
|
106 |
<aside>Note that we're still missing pipeline parallelism in this widget. To be added as an exercise for the reader.</aside>
|
107 |
|
108 |
<div class="large-image-background-transparent">
|
109 |
-
<div
|
110 |
<div id="graph-all">
|
111 |
<div class="figure-legend">Memory usage breakdown</div>
|
112 |
<div id="graph"></div>
|
@@ -2122,7 +2111,7 @@
|
|
2122 |
|
2123 |
<p>To run the kernel you will also need some <strong><em>host code</em></strong>, which is executed on the CPU/host and takes care of preparing data allocations and loading data and code:</p>
|
2124 |
|
2125 |
-
<div class="l-body
|
2126 |
<div>
|
2127 |
<d-code block language="python">
|
2128 |
// Host code
|
|
|
58 |
</script>
|
59 |
</d-front-matter>
|
60 |
<d-title>
|
|
|
|
|
|
|
61 |
<h1 class="l-page" style="text-align: center;">The Ultra-Scale Playbook:<br>Training LLMs on GPU Clusters</h1>
|
62 |
<div id="title-plot" class="main-plot-container l-screen" style="overflow-x: hidden; width: 100%; text-align: center;">
|
63 |
<div style="display: flex; justify-content: center; position: relative;">
|
|
|
65 |
</div>
|
66 |
<p style="text-align: cekter; font-style: italic; margin-top: 10px; max-width: 900px; margin-left: auto; margin-right: auto;">We ran over 4,000 scaling experiments on up to 512 GPUs and measured throughput (size of markers) and GPU utilization (color of markers). Note that both are normalized per model size in this visualization.</p>
|
67 |
|
|
|
68 |
</div>
|
|
|
69 |
</d-title>
|
|
|
|
|
|
|
|
|
70 |
<d-byline></d-byline>
|
71 |
<d-article>
|
72 |
<d-contents>
|
|
|
76 |
Thousands of GPUs humming in perfect harmony. That's what it takes to train today's most powerful AI models – a symphony of computing power that until recently was the exclusive domain of elite research labs. Open source has transformed this landscape, but not completely. Yes, you can download the latest <a href="https://huggingface.co/meta-llama">Llama</a> or <a href="https://huggingface.co/deepseek-ai">DeepSeek</a> models. Yes, you can read their <a href="https://ai.meta.com/research/publications/the-llama-3-herd-of-models/">technical</a> and <a href="https://github.com/deepseek-ai/DeepSeek-R1/blob/main/DeepSeek_R1.pdf">experiment</a> reports. But the most challenging part – the training code, the knowledge and techniques necessary to coordinate GPUs to train these massive systems – remains shrouded in complexity and spread around in a series of disconnected papers and often private codebases.
|
77 |
</p>
|
78 |
<aside>Reading time: 2-4 days. <br>For the best reading experience, we recommend not using a mobile phone.</aside>
|
79 |
+
<p>
|
|
|
80 |
This open source book is here to change that. Starting from the basics, we'll walk you through the knowledge necessary to scale the training of large language models (LLMs) from one GPU to tens, hundreds, and even thousands of GPUs, illustrating theory with practical code examples and reproducible benchmarks.
|
81 |
</p>
|
82 |
|
|
|
83 |
<p>As the size of the clusters used to train these models has grown, various techniques, such as data parallelism, tensor parallelism, pipeline parallelism, and context parallelism as well as ZeRO and kernel fusion, have been invented to make sure that GPUs are highly utilized at all times. This significantly reduces training time and makes the most efficient use of this expensive hardware. These distributed training techniques are not only important for building initial models but have also become essential for fine-tuning large models on specialized data, which often produces the best results. In this book, we'll progressively go over all of these techniques – from the simplest to the most refined ones – while maintaining a single story line to help you understand where each method comes from.</p>
|
84 |
|
85 |
<aside>If you have questions or remarks, open a discussion on the <a href="https://huggingface.co/spaces/nanotron/ultrascale-playbook/discussions?status=open&type=discussion">Community tab</a>!</aside>
|
|
|
95 |
<aside>Note that we're still missing pipeline parallelism in this widget. To be added as an exercise for the reader.</aside>
|
96 |
|
97 |
<div class="large-image-background-transparent">
|
98 |
+
<div style="display: grid; grid-template-columns: 1fr 1fr; align-items: center;">
|
99 |
<div id="graph-all">
|
100 |
<div class="figure-legend">Memory usage breakdown</div>
|
101 |
<div id="graph"></div>
|
|
|
2111 |
|
2112 |
<p>To run the kernel you will also need some <strong><em>host code</em></strong>, which is executed on the CPU/host and takes care of preparing data allocations and loading data and code:</p>
|
2113 |
|
2114 |
+
<div class="l-body" style="display: grid; grid-template-columns: 1fr 1fr; align-items: center;">
|
2115 |
<div>
|
2116 |
<d-code block language="python">
|
2117 |
// Host code
|
src/index.js
CHANGED
@@ -2,237 +2,9 @@
|
|
2 |
import { init_memory_plot } from './memory'
|
3 |
import { loadFragments } from './fragmentLoader'
|
4 |
import { syncHFSpacesURLHash } from './syncHFSpacesURLHash'
|
5 |
-
// Dark mode is now handled manually via a CSS class on <html> and injected styles
|
6 |
|
7 |
document.addEventListener("DOMContentLoaded", () => {
|
8 |
console.log("DOMContentLoaded");
|
9 |
-
|
10 |
-
// Inject minimal styles for the theme toggle button
|
11 |
-
const styleEl = document.createElement('style');
|
12 |
-
styleEl.textContent = `
|
13 |
-
.theme-toggle-btn{position:absolute;top:16px;left:16px;z-index:10000;display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:999px;background:rgba(255,255,255,0.9);backdrop-filter:saturate(150%) blur(6px);cursor:pointer;border:1px solid transparent;outline:none;box-shadow:none;-webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent}
|
14 |
-
.theme-toggle-btn:hover{border-color:transparent;box-shadow:none}
|
15 |
-
.theme-toggle-btn:focus,.theme-toggle-btn:focus-visible{outline:none;border-color:transparent;box-shadow:none}
|
16 |
-
.theme-toggle-btn img{width:22px;height:22px;transition:filter .15s ease}
|
17 |
-
.theme-toggle-btn.dark img{filter: brightness(0) invert(1)}
|
18 |
-
@media (prefers-color-scheme: dark){.theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent;box-shadow:none}}
|
19 |
-
`;
|
20 |
-
document.head.appendChild(styleEl);
|
21 |
-
|
22 |
-
// Inject dark mode CSS (scoped via html.dark)
|
23 |
-
const darkCSS = `
|
24 |
-
html.dark{color-scheme:dark}
|
25 |
-
html.dark body{background:#242525;color:#e5e7eb}
|
26 |
-
html.dark a{color:#93c5fd}
|
27 |
-
html.dark .figure-legend{color:#9ca3af}
|
28 |
-
html.dark d-article, html.dark d-article p, html.dark d-article aside{color:white !important;}
|
29 |
-
html.dark d-contents{background:#242525}
|
30 |
-
html.dark d-contents nav a{color:#cbd5e1}
|
31 |
-
html.dark d-contents nav a:hover{text-decoration:underline solid rgba(255,255,255,0.6)}
|
32 |
-
html.dark .note-box{background:#111;border-left-color:#888}
|
33 |
-
html.dark .note-box-title{color:#d1d5db}
|
34 |
-
html.dark .note-box-content{color:#e5e7eb}
|
35 |
-
html.dark .large-image-background{background:#242525}
|
36 |
-
html.dark .boxed-image{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6)}
|
37 |
-
html.dark #graph-all,html.dark #controls,html.dark .memory-block,html.dark .activation-memory,html.dark .gradient-memory{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6);color:#e5e7eb}
|
38 |
-
html.dark label,html.dark .memory-title{color:#e5e7eb}
|
39 |
-
html.dark .memory-value{color:#93c5fd}
|
40 |
-
html.dark input,html.dark select,html.dark textarea{background:#0f0f0f;color:#e5e7eb;border:1px solid #333}
|
41 |
-
html.dark input:hover,html.dark select:hover,html.dark textarea:hover{border-color:#60a5fa}
|
42 |
-
html.dark input:focus,html.dark select:focus,html.dark textarea:focus{border-color:#3b82f6;box-shadow:0 0 0 2px rgba(59,130,246,0.35)}
|
43 |
-
html.dark input[type=range]{background:#333}
|
44 |
-
html.dark input[type=range]::-webkit-slider-thumb{background:#3b82f6}
|
45 |
-
html.dark .plotly_caption{color:#9ca3af}
|
46 |
-
html.dark .theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent}
|
47 |
-
html.dark d-article img{background:white}
|
48 |
-
html.dark summary {color:black !important;}
|
49 |
-
html.dark .katex-container {color:white !important;}
|
50 |
-
html.dark d-code {background: white!important;}
|
51 |
-
html.dark .code-block div { background: white!important;}
|
52 |
-
html.dark .code-block div p { color: black!important;}
|
53 |
-
/* Table borders in dark mode */
|
54 |
-
html.dark table{border-color:rgba(255,255,255,0.3)}
|
55 |
-
html.dark th,html.dark td{border-color:rgba(255,255,255,0.3)}
|
56 |
-
html.dark thead tr,html.dark tbody tr{border-color:rgba(255,255,255,0.3)}
|
57 |
-
html.dark d-byline, html.dark d-article{border-top: 1px solid rgba(255, 255, 255, 0.5);}
|
58 |
-
html.dark d-byline h3{color:white;}
|
59 |
-
html.dark d-math *, html.dark span.katex{color:white !important;}
|
60 |
-
html.dark d-appendix { color: white}
|
61 |
-
html.dark h2 { border-bottom: 1px solid rgba(255, 255, 255, 0.25);}
|
62 |
-
html.dark h1, html.dark h2, html.dark h3, html.dark h4, html.dark h5, html.dark h6 { color: white}
|
63 |
-
html.dark .code-area { color: black;}
|
64 |
-
html.dark .code-area a { color: black!important;}
|
65 |
-
|
66 |
-
`;
|
67 |
-
const darkStyleEl = document.createElement('style');
|
68 |
-
darkStyleEl.id = 'darkmode-css';
|
69 |
-
darkStyleEl.textContent = darkCSS;
|
70 |
-
document.head.appendChild(darkStyleEl);
|
71 |
-
|
72 |
-
// Inject equivalent dark CSS into all ShadowRoots using :host-context(.dark)
|
73 |
-
// This ensures styles also apply inside web components with Shadow DOM
|
74 |
-
const shadowDarkCSS = darkCSS.replace(/html\.dark/g, ':host-context(.dark)');
|
75 |
-
|
76 |
-
const injectDarkStylesIntoRoot = (root) => {
|
77 |
-
// Only target ShadowRoots here
|
78 |
-
if (!root || !(root instanceof ShadowRoot)) return;
|
79 |
-
if (root.querySelector('style#darkmode-css-shadow')) return;
|
80 |
-
const style = document.createElement('style');
|
81 |
-
style.id = 'darkmode-css-shadow';
|
82 |
-
style.textContent = shadowDarkCSS;
|
83 |
-
root.appendChild(style);
|
84 |
-
};
|
85 |
-
|
86 |
-
// Normalize inline SVGs: ensure viewBox and preserveAspectRatio for responsiveness
|
87 |
-
const normalizeSvgElement = (svgEl) => {
|
88 |
-
try {
|
89 |
-
if (!svgEl || svgEl.hasAttribute('viewBox')) return;
|
90 |
-
const widthAttr = svgEl.getAttribute('width');
|
91 |
-
const heightAttr = svgEl.getAttribute('height');
|
92 |
-
if (!widthAttr || !heightAttr) return;
|
93 |
-
const width = parseFloat(widthAttr);
|
94 |
-
const height = parseFloat(heightAttr);
|
95 |
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
|
96 |
-
svgEl.setAttribute('viewBox', `0 0 ${width} ${height}`);
|
97 |
-
if (!svgEl.hasAttribute('preserveAspectRatio')) {
|
98 |
-
svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
99 |
-
}
|
100 |
-
} catch (_) {
|
101 |
-
// no-op
|
102 |
-
}
|
103 |
-
};
|
104 |
-
|
105 |
-
const processRootForSVGs = (root) => {
|
106 |
-
if (!root || typeof root.querySelectorAll !== 'function') return;
|
107 |
-
const svgs = root.querySelectorAll('svg:not([viewBox])');
|
108 |
-
svgs.forEach((svg) => normalizeSvgElement(svg));
|
109 |
-
};
|
110 |
-
|
111 |
-
const scanNodeForShadowRoots = (node) => {
|
112 |
-
if (!node) return;
|
113 |
-
if (node.shadowRoot) {
|
114 |
-
injectDarkStylesIntoRoot(node.shadowRoot);
|
115 |
-
processRootForSVGs(node.shadowRoot);
|
116 |
-
}
|
117 |
-
// Traverse children
|
118 |
-
if (node.childNodes && node.childNodes.length) {
|
119 |
-
node.childNodes.forEach((child) => {
|
120 |
-
// Process SVGs in this subtree as well
|
121 |
-
processRootForSVGs(child);
|
122 |
-
scanNodeForShadowRoots(child);
|
123 |
-
});
|
124 |
-
}
|
125 |
-
};
|
126 |
-
|
127 |
-
// Intercept future shadow roots
|
128 |
-
const originalAttachShadow = Element.prototype.attachShadow;
|
129 |
-
Element.prototype.attachShadow = function(init) {
|
130 |
-
const shadow = originalAttachShadow.call(this, init);
|
131 |
-
try {
|
132 |
-
injectDarkStylesIntoRoot(shadow);
|
133 |
-
processRootForSVGs(shadow);
|
134 |
-
} catch (e) {}
|
135 |
-
return shadow;
|
136 |
-
};
|
137 |
-
|
138 |
-
// Initial sweep for any existing shadow roots
|
139 |
-
scanNodeForShadowRoots(document.documentElement);
|
140 |
-
// Initial pass for regular DOM SVGs
|
141 |
-
processRootForSVGs(document);
|
142 |
-
|
143 |
-
// Observe DOM mutations to catch dynamically added components
|
144 |
-
const mo = new MutationObserver((mutations) => {
|
145 |
-
for (const m of mutations) {
|
146 |
-
m.addedNodes && m.addedNodes.forEach((n) => {
|
147 |
-
scanNodeForShadowRoots(n);
|
148 |
-
processRootForSVGs(n);
|
149 |
-
});
|
150 |
-
}
|
151 |
-
});
|
152 |
-
mo.observe(document.documentElement, { childList: true, subtree: true });
|
153 |
-
|
154 |
-
// Create the toggle button
|
155 |
-
const btn = document.createElement('button');
|
156 |
-
btn.className = 'theme-toggle-btn';
|
157 |
-
btn.setAttribute('type', 'button');
|
158 |
-
btn.setAttribute('aria-label', 'Basculer le mode sombre');
|
159 |
-
// Reuse icons declared in HTML and move them into the button
|
160 |
-
const sunIcon = document.getElementById('sunIcon');
|
161 |
-
const moonIcon = document.getElementById('moonIcon');
|
162 |
-
|
163 |
-
if (sunIcon && moonIcon) {
|
164 |
-
// Make sure they adopt button sizing
|
165 |
-
sunIcon.style.display = 'none';
|
166 |
-
sunIcon.style.width = '22px';
|
167 |
-
sunIcon.style.height = '22px';
|
168 |
-
moonIcon.style.display = 'none';
|
169 |
-
moonIcon.style.width = '22px';
|
170 |
-
moonIcon.style.height = '22px';
|
171 |
-
btn.appendChild(sunIcon);
|
172 |
-
btn.appendChild(moonIcon);
|
173 |
-
}
|
174 |
-
document.body.appendChild(btn);
|
175 |
-
|
176 |
-
const setIcon = (enabled) => {
|
177 |
-
// enabled = dark mode enabled -> show sun (to indicate turning off), hide moon
|
178 |
-
sunIcon.style.display = enabled ? '' : 'none';
|
179 |
-
moonIcon.style.display = enabled ? 'none' : '';
|
180 |
-
btn.setAttribute('title', enabled ? 'Désactiver le mode sombre' : 'Activer le mode sombre');
|
181 |
-
btn.setAttribute('aria-pressed', String(enabled));
|
182 |
-
btn.classList.toggle('dark', enabled);
|
183 |
-
};
|
184 |
-
|
185 |
-
const setDark = (enabled) => {
|
186 |
-
document.documentElement.classList.toggle('dark', enabled);
|
187 |
-
setIcon(enabled);
|
188 |
-
};
|
189 |
-
|
190 |
-
const THEME_KEY = 'theme';
|
191 |
-
let savedTheme = null;
|
192 |
-
try {
|
193 |
-
savedTheme = localStorage.getItem(THEME_KEY);
|
194 |
-
} catch (e) {}
|
195 |
-
|
196 |
-
const media = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
|
197 |
-
const prefersDark = media ? media.matches : false;
|
198 |
-
// Initialisation: priorité à la préférence sauvegardée, sinon préférence système
|
199 |
-
if (savedTheme === 'dark') {
|
200 |
-
setDark(true);
|
201 |
-
} else if (savedTheme === 'light') {
|
202 |
-
setDark(false);
|
203 |
-
} else {
|
204 |
-
setDark(prefersDark);
|
205 |
-
}
|
206 |
-
|
207 |
-
// Si l'utilisateur a déjà choisi manuellement, on ne suit plus la préférence système
|
208 |
-
let manualOverride = savedTheme === 'dark' || savedTheme === 'light';
|
209 |
-
|
210 |
-
// React to system preference changes dynamically (no persistence)
|
211 |
-
if (media && typeof media.addEventListener === 'function') {
|
212 |
-
media.addEventListener('change', (e) => {
|
213 |
-
if (!manualOverride) {
|
214 |
-
setDark(e.matches);
|
215 |
-
}
|
216 |
-
});
|
217 |
-
} else if (media && typeof media.addListener === 'function') {
|
218 |
-
// Fallback for older browsers
|
219 |
-
media.addListener((e) => {
|
220 |
-
if (!manualOverride) {
|
221 |
-
setDark(e.matches);
|
222 |
-
}
|
223 |
-
});
|
224 |
-
}
|
225 |
-
|
226 |
-
// Toggle handler — for réduire les glitches, attendre le next frame avant d'ajuster l'icône
|
227 |
-
btn.addEventListener('click', () => {
|
228 |
-
manualOverride = true;
|
229 |
-
const next = !document.documentElement.classList.contains('dark');
|
230 |
-
setDark(next);
|
231 |
-
try {
|
232 |
-
localStorage.setItem(THEME_KEY, next ? 'dark' : 'light');
|
233 |
-
} catch (e) {}
|
234 |
-
});
|
235 |
-
|
236 |
loadFragments();
|
237 |
init_memory_plot();
|
238 |
syncHFSpacesURLHash();
|
|
|
2 |
import { init_memory_plot } from './memory'
|
3 |
import { loadFragments } from './fragmentLoader'
|
4 |
import { syncHFSpacesURLHash } from './syncHFSpacesURLHash'
|
|
|
5 |
|
6 |
document.addEventListener("DOMContentLoaded", () => {
|
7 |
console.log("DOMContentLoaded");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
loadFragments();
|
9 |
init_memory_plot();
|
10 |
syncHFSpacesURLHash();
|
src/style.css
CHANGED
@@ -304,6 +304,7 @@ d-contents nav > ul > li > a:hover {
|
|
304 |
border-radius: 6px;
|
305 |
/* Add this to ensure the box only takes up needed space */
|
306 |
display: inline-block;
|
|
|
307 |
}
|
308 |
|
309 |
.note-box-title {
|
@@ -596,141 +597,3 @@ select[name="presets"] {
|
|
596 |
border-radius: 8px;
|
597 |
}
|
598 |
|
599 |
-
.order-button-second {
|
600 |
-
background: linear-gradient(135deg, #6DB4C4, #D4A5B8);
|
601 |
-
color: white;
|
602 |
-
font-size: 18px;
|
603 |
-
font-weight: 600;
|
604 |
-
padding: 20px 20px;
|
605 |
-
border: none;
|
606 |
-
border-radius: 12px;
|
607 |
-
cursor: pointer;
|
608 |
-
text-transform: uppercase;
|
609 |
-
letter-spacing: 1px;
|
610 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
611 |
-
transition: all 0.3s ease;
|
612 |
-
position: relative;
|
613 |
-
overflow: hidden;
|
614 |
-
}
|
615 |
-
.order-button-second:hover {
|
616 |
-
transform: translateY(-2px);
|
617 |
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
618 |
-
}
|
619 |
-
|
620 |
-
.order-button:active {
|
621 |
-
transform: translateY(0);
|
622 |
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
623 |
-
}
|
624 |
-
|
625 |
-
.order-button-second::before {
|
626 |
-
content: '';
|
627 |
-
position: absolute;
|
628 |
-
top: 0;
|
629 |
-
left: -100%;
|
630 |
-
width: 100%;
|
631 |
-
height: 100%;
|
632 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0));
|
633 |
-
transition: left 0.5s ease;
|
634 |
-
}
|
635 |
-
|
636 |
-
.order-button-second:hover::before {
|
637 |
-
left: 100%;
|
638 |
-
}
|
639 |
-
|
640 |
-
.order-button {
|
641 |
-
background: linear-gradient(135deg, #6DB4C4, #D4A5B8);
|
642 |
-
color: white;
|
643 |
-
font-size: 18px;
|
644 |
-
font-weight: 600;
|
645 |
-
padding: 16px 32px;
|
646 |
-
border: none;
|
647 |
-
border-radius: 12px;
|
648 |
-
cursor: pointer;
|
649 |
-
text-transform: uppercase;
|
650 |
-
letter-spacing: 1px;
|
651 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
652 |
-
transition: all 0.3s ease;
|
653 |
-
position: relative;
|
654 |
-
overflow: hidden;
|
655 |
-
}
|
656 |
-
|
657 |
-
.order-button:hover {
|
658 |
-
transform: translateY(-2px);
|
659 |
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
660 |
-
}
|
661 |
-
|
662 |
-
.order-button:active {
|
663 |
-
transform: translateY(0);
|
664 |
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
665 |
-
}
|
666 |
-
|
667 |
-
.order-button::before {
|
668 |
-
content: '';
|
669 |
-
position: absolute;
|
670 |
-
top: 0;
|
671 |
-
left: -100%;
|
672 |
-
width: 100%;
|
673 |
-
height: 100%;
|
674 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0));
|
675 |
-
transition: left 0.5s ease;
|
676 |
-
}
|
677 |
-
|
678 |
-
.order-button:hover::before {
|
679 |
-
left: 100%;
|
680 |
-
}
|
681 |
-
.order-button-container-second {
|
682 |
-
/* display: flex; */
|
683 |
-
justify-content: center;
|
684 |
-
margin: 0px 0;
|
685 |
-
}
|
686 |
-
|
687 |
-
.order-button-container {
|
688 |
-
display: flex;
|
689 |
-
justify-content: center;
|
690 |
-
margin: 0px 0 40px 0;
|
691 |
-
}
|
692 |
-
|
693 |
-
d-article img {
|
694 |
-
width: 100%!important;
|
695 |
-
}
|
696 |
-
|
697 |
-
|
698 |
-
iframe, .js-plotly-plot {
|
699 |
-
width: 100%!important;
|
700 |
-
margin-bottom: 20px;
|
701 |
-
}
|
702 |
-
|
703 |
-
.modebar-container {
|
704 |
-
display: none;
|
705 |
-
}
|
706 |
-
|
707 |
-
#graph-container {
|
708 |
-
display: grid; grid-template-columns: 1fr 1fr; align-items: center;
|
709 |
-
}
|
710 |
-
|
711 |
-
@media (max-width: 768px) {
|
712 |
-
#graph-container {
|
713 |
-
grid-template-columns: 1fr;
|
714 |
-
}
|
715 |
-
}
|
716 |
-
|
717 |
-
@media (max-width: 1024px) {
|
718 |
-
#graph-container {
|
719 |
-
grid-template-columns: 1fr;
|
720 |
-
}
|
721 |
-
#graph-all {
|
722 |
-
margin-right: 0px;
|
723 |
-
}
|
724 |
-
#controls {
|
725 |
-
margin-left: 0px;
|
726 |
-
}
|
727 |
-
}
|
728 |
-
|
729 |
-
.main-plot-container svg {
|
730 |
-
background: transparent !important;
|
731 |
-
}
|
732 |
-
|
733 |
-
.large-image-background-transparent {
|
734 |
-
margin-left: 0px;
|
735 |
-
margin-right: 0px;
|
736 |
-
}
|
|
|
304 |
border-radius: 6px;
|
305 |
/* Add this to ensure the box only takes up needed space */
|
306 |
display: inline-block;
|
307 |
+
width: 100%;
|
308 |
}
|
309 |
|
310 |
.note-box-title {
|
|
|
597 |
border-radius: 8px;
|
598 |
}
|
599 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ultra_blog.md
DELETED
The diff for this file is too large to render.
See raw diff
|
|