jeffboudier HF Staff commited on
Commit
dde6bca
·
0 Parent(s):

fresh start

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
.gitattributes ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
11
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
12
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
13
+ *.model filter=lfs diff=lfs merge=lfs -text
14
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
15
+ *.npy filter=lfs diff=lfs merge=lfs -text
16
+ *.npz filter=lfs diff=lfs merge=lfs -text
17
+ *.onnx filter=lfs diff=lfs merge=lfs -text
18
+ *.ot filter=lfs diff=lfs merge=lfs -text
19
+ *.parquet filter=lfs diff=lfs merge=lfs -text
20
+ *.pb filter=lfs diff=lfs merge=lfs -text
21
+ *.pdf filter=lfs diff=lfs merge=lfs -text
22
+ *.pickle filter=lfs diff=lfs merge=lfs -text
23
+ *.pkl filter=lfs diff=lfs merge=lfs -text
24
+ *.png filter=lfs diff=lfs merge=lfs -text
25
+ *.pt filter=lfs diff=lfs merge=lfs -text
26
+ *.pth filter=lfs diff=lfs merge=lfs -text
27
+ *.rar filter=lfs diff=lfs merge=lfs -text
28
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
29
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
30
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
31
+ *.tar filter=lfs diff=lfs merge=lfs -text
32
+ *.tflite filter=lfs diff=lfs merge=lfs -text
33
+ *.tgz filter=lfs diff=lfs merge=lfs -text
34
+ *.wasm filter=lfs diff=lfs merge=lfs -text
35
+ *.wav filter=lfs diff=lfs merge=lfs -text
36
+ *.xz filter=lfs diff=lfs merge=lfs -text
37
+ *.zip filter=lfs diff=lfs merge=lfs -text
38
+ *.zst filter=lfs diff=lfs merge=lfs -text
39
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ node_modules/
2
+ *.log
3
+ *.env
4
+ *.cache
5
+ python/**/__pycache__
README.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: The Distill Template
3
+ emoji: 🌌
4
+ colorFrom: yellow
5
+ colorTo: purple
6
+ sdk: static
7
+ pinned: true
8
+ license: apache-2.0
9
+ header: mini
10
+ app_file: dist/index.html
11
+ thumbnail: https://huggingface.co/spaces/nanotron/distill-blog-template/resolve/main/thumbnail.png
12
+ short_description: Craft Beautiful Blogs
13
+ ---
14
+
15
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
16
+
17
+
18
+ Instruction to install and run locally
19
+
20
+ ```bash
21
+ npm install
22
+ npm run build
23
+ npm run dev
24
+
25
+ // If you want to change something change it in src/....
26
+
27
+ // Once you are finished
28
+ npm run build
29
+ // And commit the dist folder
30
+ ```
31
+
32
+ ## Loading HTML fragments:
33
+ There are two way to load HTML fragments:
34
+ 1. Compile them into html during build time
35
+ 2. Fetch them and insert them during run-time
36
+
37
+ ## When to use what
38
+ - Use compile time fragments only on parts which you want to ensure are seen by every user right after page load (e.g logo)
39
+ - Use run-time fragments for everything else so that the final HTML is of reasonable size (<1MB idealy)
40
+
41
+ ## How to add a new fragment
42
+ - Add it to the `src/fragments` folder (e.g. `src/fragments/banner.html`)
43
+ - For run-time fragments, add {{{fragment-name}}} to appropriate place in `src/index.html` (e.g. {{{fragment-banner}}})
44
+ - For compile-time fragments, add <div id="fragment-name"></div> to `src/index.html` where you want to insert the fragment (e.g. <div id="fragment-banner"></div>)
45
+
46
+
47
+ ## How to know which fragments are available
48
+ - Run `npm run dev` and look at the console for available fragments
assets/.DS_Store ADDED
Binary file (6.15 kB). View file
 
assets/images/.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.gif filter=lfs diff=lfs merge=lfs -text
2
+ *.png filter=lfs diff=lfs merge=lfs -text
assets/images/GPU_Huggy_small.png ADDED
dist/assets/.DS_Store ADDED
Binary file (6.15 kB). View file
 
dist/assets/images/GPU_Huggy_small.png ADDED
dist/bibliography.bib ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @article{example2023,
2
+ title={Example Paper Title},
3
+ author={Example, Author and Another, Author},
4
+ journal={Journal of Examples},
5
+ volume={1},
6
+ number={1},
7
+ pages={1--10},
8
+ year={2023},
9
+ publisher={Example Publisher}
10
+ }
11
+
12
+ @online{distill,
13
+ title={Distill},
14
+ url={https://distill.pub},
15
+ year={2016},
16
+ publisher={Distill Working Group}
17
+ }
18
+
19
+ @book{textbook2022,
20
+ title={An Example Textbook},
21
+ author={Author, Example},
22
+ year={2022},
23
+ publisher={Example Academic Press}
24
+ }
dist/distill.bundle.js ADDED
The diff for this file is too large to render. See raw diff
 
dist/distill.bundle.js.map ADDED
The diff for this file is too large to render. See raw diff
 
dist/fragments/banner.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <div> <div id=88bd2bde-2a9d-42b6-bea3-2a6d06b034ea class=plotly-graph-div style="height:400px; width:1200px;"></div> <script>window.PLOTLYENV=window.PLOTLYENV||{},document.getElementById("88bd2bde-2a9d-42b6-bea3-2a6d06b034ea")&&Plotly.newPlot("88bd2bde-2a9d-42b6-bea3-2a6d06b034ea",[{customdata:[["biiig dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["a dot"],["ok-ish dot"],["smol dot"],["a dot"],["a dot"],["ok-ish dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["smol dot"],["smol dot"],["biiig dot"],["a dot"],["smol dot"],["biiig dot"],["a dot"],["smol dot"],["a dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["smol dot"],["smol dot"],["smol dot"],["smol dot"],["smol dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["a dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["a dot"],["a dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["biiig dot"],["smol dot"],["biiig dot"],["biiig dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["biiig dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["a dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["a dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["biiig dot"],["a dot"],["biiig dot"],["a dot"],["a dot"],["ok-ish dot"],["a dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["smol dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["a dot"],["biiig dot"],["biiig dot"],["a dot"],["a dot"],["a dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["a dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["a dot"],["smol dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["a dot"],["biiig dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["a dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["a dot"],["smol dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["a dot"],["biiig dot"],["biiig dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["a dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["smol dot"],["ok-ish dot"],["a dot"]],hoverlabel:{namelength:0},hovertemplate:"Dot category: %{customdata[0]}",marker:{color:{dtype:"f8",bdata:"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"},colorscale:[[0,"rgb(78, 165, 183)"],[.5,"rgb(206, 192, 250)"],[1,"rgb(232, 137, 171)"]],opacity:.9,size:{dtype:"f8",bdata:"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"}},mode:"markers",showlegend:!1,x:{dtype:"f8",bdata:"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"},y:{dtype:"f8",bdata:"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"},type:"scatter"}],{template:{data:{histogram2dcontour:[{type:"histogram2dcontour",colorbar:{outlinewidth:0,ticks:""},colorscale:[[0,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]]}],choropleth:[{type:"choropleth",colorbar:{outlinewidth:0,ticks:""}}],histogram2d:[{type:"histogram2d",colorbar:{outlinewidth:0,ticks:""},colorscale:[[0,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]]}],heatmap:[{type:"heatmap",colorbar:{outlinewidth:0,ticks:""},colorscale:[[0,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]]}],contourcarpet:[{type:"contourcarpet",colorbar:{outlinewidth:0,ticks:""}}],contour:[{type:"contour",colorbar:{outlinewidth:0,ticks:""},colorscale:[[0,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]]}],surface:[{type:"surface",colorbar:{outlinewidth:0,ticks:""},colorscale:[[0,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]]}],mesh3d:[{type:"mesh3d",colorbar:{outlinewidth:0,ticks:""}}],scatter:[{fillpattern:{fillmode:"overlay",size:10,solidity:.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:.5},pattern:{fillmode:"overlay",size:10,solidity:.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:.2}},type:"histogram"}],scattergl:[{type:"scattergl",marker:{colorbar:{outlinewidth:0,ticks:""}}}],scatter3d:[{type:"scatter3d",line:{colorbar:{outlinewidth:0,ticks:""}},marker:{colorbar:{outlinewidth:0,ticks:""}}}],scattermap:[{type:"scattermap",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:.5},pattern:{fillmode:"overlay",size:10,solidity:.2}},type:"barpolar"}],pie:[{automargin:!0,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,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]],sequentialminus:[[0,"#0d0887"],[.1111111111111111,"#46039f"],[.2222222222222222,"#7201a8"],[.3333333333333333,"#9c179e"],[.4444444444444444,"#bd3786"],[.5555555555555556,"#d8576b"],[.6666666666666666,"#ed7953"],[.7777777777777778,"#fb9f3a"],[.8888888888888888,"#fdca26"],[1,"#f0f921"]],diverging:[[0,"#8e0152"],[.1,"#c51b7d"],[.2,"#de77ae"],[.3,"#f1b6da"],[.4,"#fde0ef"],[.5,"#f7f7f7"],[.6,"#e6f5d0"],[.7,"#b8e186"],[.8,"#7fbc41"],[.9,"#4d9221"],[1,"#276419"]]},xaxis:{gridcolor:"white",linecolor:"white",ticks:"",title:{standoff:15},zerolinecolor:"white",automargin:!0,zerolinewidth:2},yaxis:{gridcolor:"white",linecolor:"white",ticks:"",title:{standoff:15},zerolinecolor:"white",automargin:!0,zerolinewidth:2},scene:{xaxis:{backgroundcolor:"#E5ECF6",gridcolor:"white",linecolor:"white",showbackground:!0,ticks:"",zerolinecolor:"white",gridwidth:2},yaxis:{backgroundcolor:"#E5ECF6",gridcolor:"white",linecolor:"white",showbackground:!0,ticks:"",zerolinecolor:"white",gridwidth:2},zaxis:{backgroundcolor:"#E5ECF6",gridcolor:"white",linecolor:"white",showbackground:!0,ticks:"",zerolinecolor:"white",gridwidth:2}},shapedefaults:{line:{color:"#2a3f5f"}},annotationdefaults:{arrowcolor:"#2a3f5f",arrowhead:0,arrowwidth:1},geo:{bgcolor:"white",landcolor:"#E5ECF6",subunitcolor:"white",showland:!0,showlakes:!0,lakecolor:"white"},title:{x:.05},mapbox:{style:"light"}}},margin:{l:0,r:0,t:0,b:0},xaxis:{showgrid:!1,zeroline:!1,showticklabels:!1,range:[0,3]},yaxis:{showgrid:!1,zeroline:!1,showticklabels:!1,scaleanchor:"x",scaleratio:1,range:[0,1]},width:1200,height:400,paper_bgcolor:"white",plot_bgcolor:"white",showlegend:!1},{displayModeBar:!1,responsive:!0,scrollZoom:!1})</script> </div>
dist/fragments/enterprisebanner.html ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <div style="height: 200px; width: 100%; overflow: hidden; background: linear-gradient(270deg, red, orange, yellow, green, blue, indigo, violet); background-size: 1400% 1400%; animation: rainbow 10s ease infinite;">
2
+
3
+ </div>
4
+
5
+ <style>@keyframes rainbow{0%{background-position:0%}50%{background-position:100%}to{background-position:0%}}</style>
dist/index.html ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <script src="distill.bundle.js" type="module" fetchpriority="high" blocking></script>
6
+ <script src="main.bundle.js" type="module" fetchpriority="low" defer></script>
7
+ <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <meta charset="utf8">
10
+ <base target="_blank">
11
+ <title>Enterprise Hub - Why you need it, how to get it</title>
12
+ <link rel="stylesheet" href="style.css">
13
+ </head>
14
+
15
+ <body>
16
+ <d-front-matter>
17
+ <script id='distill-front-matter' type="text/json">{
18
+ "title": "Enterprise Hub",
19
+ "description": "Why you need it, how to get it",
20
+ "published": "Apr 8, 2025",
21
+ "affiliation": {"name": "Hugging Face"},
22
+ "authors": [
23
+ {
24
+ "author":"Jeff Boudier",
25
+ "authorURL":"https://huggingface.co/jeffboudier"
26
+ }
27
+ ],
28
+ "katex": {
29
+ "delimiters": [
30
+ {"left": "$$", "right": "$$", "display": false}
31
+ ]
32
+ }
33
+ }
34
+ </script>
35
+ </d-front-matter>
36
+ <d-title>
37
+ <h1 class="l-page" style="text-align: center;">Enterprise Hub:<br>Why you need it, how to get it</h1>
38
+ <div id="title-plot" class="main-plot-container l-screen" style="overflow-x: hidden; width: 100%; text-align: center;">
39
+ <div style="display: flex; justify-content: center; position: relative;">
40
+ <div style="height: 200px; width: 100%; overflow: hidden; background: linear-gradient(270deg, red, orange, yellow, green, blue, indigo, violet); background-size: 1400% 1400%; animation: rainbow 10s ease infinite;">
41
+
42
+ </div>
43
+
44
+ <style>@keyframes rainbow{0%{background-position:0%}50%{background-position:100%}to{background-position:0%}}</style>
45
+ </div>
46
+ <!--<p style="text-align: cekter; font-style: italic; margin-top: 10px; max-width: 900px; margin-left: auto; margin-right: auto;">banner subtitle</p>-->
47
+
48
+ </div>
49
+ </d-title>
50
+ <d-byline></d-byline>
51
+ <d-article>
52
+ <d-contents>
53
+ </d-contents>
54
+
55
+ <p>Welcome! <br/>In this article, you will learn all about the Enterprise features of Hugging Face, and how to get them.</p>
56
+
57
+ <aside>Reading time: 10-15 minutes.</aside>
58
+
59
+ <h2>Executive Summary</h2>
60
+
61
+ <p>Hugging Face is the leading open platform for AI Builders - researchers, data scientists, machine learning engineers and software developers. 8 million AI Builders use Hugging Face, and created over 200,000 organizations to collaborate privately on models, datasets and applications.</p>
62
+
63
+ <p>Free organizations are designed for small teams who need to collaborate and share transparently, but do not scale to medium sized companies: in free organizations, members join with their personal accounts, and everyone has access to everything.</p>
64
+
65
+ <p>For companies whose teams need privacy, security, compliance and governance when building AI, Hugging Face offers Enterprise Hub and Enterprise Plus.</p>
66
+
67
+ <p><strong>Enterprise Hub</strong> provides advanced collaboration, compute, security and compliance features for organizations. <br/>
68
+ <strong>Enterprise Plus</strong> adds exclusive security and governance features, and direct expert support from the Hugging Face team.</p>
69
+ <aside> Companies in regulated industries should consider Enterprise Plus</aside>
70
+
71
+ <table>
72
+ <thead>
73
+ <tr>
74
+ <th></th>
75
+ <th><strong>Free Organizations</strong></th>
76
+ <th><strong>Enterprise Hub</strong></th>
77
+ <th><strong>Enterprise Plus</strong></th>
78
+ </tr>
79
+ </thead>
80
+ <tbody>
81
+ <tr>
82
+ <td>Collaboration</td>
83
+ <td>DIY Admin</td>
84
+ <td>Resource Groups<br/>
85
+ Private Datasets Viewer<br/>
86
+ Download Analytics<br/>
87
+ Extended Private Storage</td>
88
+ <td>Resource Groups<br/>
89
+ Private Datasets Viewer<br/>
90
+ Download Analytics<br/>
91
+ Extended Private Storage</td>
92
+ </tr>
93
+ <tr>
94
+ <td>Compute</td>
95
+ <td></td>
96
+ <td>Inference Providers<br/>
97
+ Inference Endpoints GPU Quotas<br/>
98
+ Spaces ZeroGPU, Dev Mode</td>
99
+ <td>Inference Providers<br/>
100
+ Inference Endpoints GPU Quotas<br/>
101
+ Spaces ZeroGPU, Dev Mode</td>
102
+ </tr>
103
+ <tr>
104
+ <td>Security</td>
105
+ <td></td>
106
+ <td>Standard SSO<br/>
107
+ 2 Factor Authentification<br/>
108
+ Access Token Management</td>
109
+ <td><strong>Advanced SSO</strong><br/>
110
+ 2 Factor Authentification<br/>
111
+ Access Token Management</td>
112
+ </tr>
113
+ <tr>
114
+ <td>Governance</td>
115
+ <td></td>
116
+ <td>Audit Logs</td>
117
+ <td>Audit Logs<br/>
118
+ <strong>HF Model Gateway (preview)</strong></td>
119
+ </tr>
120
+ <tr>
121
+ <td>Compliance</td>
122
+ <td></td>
123
+ <td>Storage Regions</td>
124
+ <td>Storage Regions</br/>
125
+ <strong>Network Security</strong></td>
126
+ </tr>
127
+ <tr>
128
+ <td>Support</td>
129
+ <td>Community Support (Forum)</td>
130
+ <td>Email Support</td>
131
+ <td><strong>Direct Expert Support (Slack)</strong></td>
132
+ </tr>
133
+ </tbody>
134
+ </table>
135
+
136
+ <p>Enterprise Hub can be purchased with a credit card at <a href="http://hf.co/enterprise">hf.co/enterprise</a>, or via a standard 1-year contract subject to a $50k / 50 seats minimum.</p>
137
+ <p>Enterprise Plus can be purchased via a custom 1-year contract subject to a $120k / 50 seats minimum.</p>
138
+
139
+ <p>How do all these features benefit your organization? Let's dive in.</p>
140
+
141
+ <h2>Why upgrade to an Enterprise organization</h2>
142
+
143
+ <h3>Advanced Collaboration</h3>
144
+
145
+ <p>Enterprise Hub and Enterprise Plus organizations allow their members to privately host and share unlimited models, datasets and Spaces repositories. Members of Enterprise organizations benefit from <strong>higher rate limits</strong> accessing all Hugging Face services from the Hub and the Hugging Face open source libraries.</p>
146
+
147
+ <p>In addition, Enterprise organizations offer high storage limits for private repositories, including <strong>1TB of private storage per member</strong> ; for instance an Enterprise organization with 1,000 members can store up to 1 Petabyte of private files at no additional cost!</p>
148
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/storage-limits">Storage Limits</a>.</aside>
149
+
150
+ <p>In free organizations, everyone has access to everything. To organize resources across teams and projects, Enterprise organizations have access to <strong>Resource Groups</strong>. Enterprise organization admins can create new Resource Groups to assign specific repositories, add members and assign them specific permissions. This is great for teams who want to work on a specific project privately from the rest of the organization, or for different business units to segregate the models, datasets and Spaces they are working on.</p>
151
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-resource-groups">Resource Groups</a>.</aside>
152
+
153
+ <p>A favourite feature of Hugging Face for data scientists is the Datasets Viewer and the <strong>Data Studio</strong>. With Datasets Viewer makes it easy to preview the content of public datasets, and with Data Studio you can apply SQL queries to the dataset, or even ask AI to create and run them for you! Enterprise organizations make Dataset Viewer and Data Studio available to private datasets hosted on the organization.</p>
154
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-datasets">Private Dataset Viewer</a>.</aside>
155
+
156
+ <p>Hugging Face is the home of over 2 million public models, datasets contributed by the community. A great way for organizations to have impact and gain followers is by publicly sharing useful models and datasets. Enterprise organizations have access to power features for model and dataset contributors. Enterprise Analytics provides detailed <strong>download analytics</strong> for public repositories, easily exported via CSV. Enterprise organizations can also set up advanced <strong>model gating</strong> processes for Hugging Face users to provide information while requesting access to their public models.</p>
157
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-analytics">Download Analytics</a>.<br/><br/>
158
+ Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-gating-group-collections">Gating Group Collections</a>.</aside>
159
+
160
+ <h3>Advanced Compute</h3>
161
+
162
+ <p>When upgrading their organization to Enterprise, companies provide their employees with powerful compute options to build and host AI applications.</p>
163
+
164
+ <p>Enterprise organization members can use <strong>Inference Providers</strong> to use and integrate some of the most popular open models into their AI applications, with serverless inference powered by Hugging Face and Inference Providers, including <a href="https://huggingface.co/models?inference_provider=together">Together AI</a>, <a href="https://huggingface.co/models?inference_provider=fal-ai">fal</a>, <a href="https://huggingface.co/models?inference_provider=novita">Novita AI</a>, <a href="https://huggingface.co/models?inference_provider=cerebras">Cerebras</a>. Enterprise organizations get $2 free credit each month toward Inference Providers, for instance a 1,000 member organization can use for up to $2,000 of Inference Providers for free. Enterprise organization admins can set a monthly spend limit to control their costs.</p>
165
+ <aside>Learn more about <a href="https://huggingface.co/docs/inference-providers/index">Inference Providers</a>.</aside>
166
+
167
+ <p>To show what an AI model can do, AI Builders use Hugging Face <strong>Spaces</strong>. Spaces are hosted, interactive AI applications - over 1M public and private Spaces were created on Hugging Face. For companies, private Spaces is an ideal way to demo a model or application to colleagues. Enterprise organizations have access to advanced compute options for their Spaces:<br/>
168
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/spaces-zerogpu">ZeroGPU</a>.<br/>
169
+ Learn more about <a href="https://huggingface.co/docs/hub/spaces-gpus">Spaces GPU upgrades</a>.<br/>
170
+ Learn more about <a href="https://huggingface.co/docs/hub/en/spaces-dev-mode">Spaces Dev Mode</a>.</aside>
171
+
172
+ <ul>
173
+ <li><strong>ZeroGPU</strong> - Spaces can be powered by powerful A100 NVIDIA GPUs that spin up when the Space is used and down right after.</li>
174
+ <li><strong>GPU Upgrades</strong> - Spaces can be upgraded to exclusive large GPU options for the most demanding applications, including 4xA10G and A100 Large instances.</li>
175
+ <li><strong>Dev Mode</strong> - Dev Mode makes developing and debugging Spaces faster, connecting to the underlying container via SSH or VS Code.</li>
176
+ </ul>
177
+ </p>
178
+
179
+ <h3>Advanced Security</h3>
180
+
181
+ <p>Free organizations are designed for minimum friction adding collaborators, students, professionals to foster transparent collaboration. For companies that require security processes, Enterprise organizations offer many features to enforce security rules and make sure appropriate access controls and rules are in place.</p>
182
+
183
+ <p>To secure accounts with access to the organization's private resources, Enterprise organization admins can require <strong>2-Factor Authentication</strong> for all members of the organization, and require admin approval for new members.</p>
184
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-advanced-security#advanced-security">2FA</a>.</aside>
185
+
186
+ <p>To reduce the risk of members inadvertently publishing confidential information as public repositories, Enterprise organization admins can make new repositories <strong>private by default</strong>, or <strong>disable public repositories</strong> altogether.</p>
187
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-advanced-security#repository-visibility-controls">repository visibility</a>.</aside>
188
+
189
+ <p>User Access Tokens are keys users can create to give applications the permission to access private models, datasets and applications on Hugging Face. It’s very important to create and manage User Access Tokens with best security practices in mind - only give the minimum required scope of access, regularly rotate tokens and invalidate unnecessary tokens. For organization admins, User Access Tokens can be a source of concern as each token is a potential attack vector. Enterprise organizations can reduce risk and enforce best practices with <strong>Access Token Management</strong>.<br/>
190
+ With Access Token Management, organization admins have a centralized view of every User Access Token created by members of the organization. Each token can be inspected, and revoked. Admins can set up organization policies, for instance to disallow the creation of broad access tokens and require fine-grained tokens, and/or to require admin approval for any new token created by users of the organization.</p>
191
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-tokens-management">Access Token Management</a>.</aside>
192
+
193
+ <p>To make sure that members with access to the organization’s private resources are actually employees of the company, Enterprise organizations can set up <strong>Single Sign-On</strong> (SSO). With SSO, user authentication to access Enterprise organization resources is managed by the company Identity Provider, such as Okta or Azure Active Directory. Roles and Teams mapping is available to automatically give specific access control permissions to employee groups, and/or assign them to Resource Groups.</p>
194
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/enterprise-sso">SSO</a>.</aside>
195
+
196
+ <p><strong>Enterprise Plus</strong> organizations have access to <strong>Advanced SSO</strong> features. With Advanced SSO, the Hugging Face user accounts who are members of the organization are fully provisioned by the company Identity Provider.</p>
197
+
198
+ <p>With <strong>Enterprise Plus</strong>, the user accounts belonging to the organization are <strong>Enterprise Managed Users</strong>, fully controlled by the company. With Enterprise Managed Users, SSO authentication manages not only the access to organization resources, but also the access to all Hugging Face Hub public resources. Enterprise Managed Users only need to login once to access all public and private resources on the Hugging Face Hub.</p>
199
+
200
+ <p>With Enterprise Managed Users, Enterprise Plus customers can drastically reduce the risk of confidential information being published:<br/>
201
+ <ul>
202
+ <li>Enterprise Managed Users cannot create repositories under their personal profile</li>
203
+ <li>Enterprise Managed Users cannot join other organizations beside their Enterprise Plus organization</li>
204
+ <li>Enterprise Managed Users cannot subscribe to a PRO subscription for their personal account</li>
205
+ <li>Enterprise Managed Users cannot change their account email address, or set a password for their account</li>
206
+ </ul>
207
+ </p>
208
+
209
+ <h3>Advanced Governance and Compliance</h3>
210
+
211
+ <p>Enterprises need traceability and auditability of their confidential information. Enterprise organization admins on Hugging Face have access to <strong>Audit Logs</strong> to inspect and retrieve all changes made to their organization, such as new users, access tokens or repositories, changes to resource groups, access controls and more. Each log entry includes information about the user, their location, the action, change and time. Lots can be easily downloaded as JSON files for analysis.</p>
212
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/audit-logs">Audit Logs</a>.</aside>
213
+
214
+ <p>Enterprise organizations can choose where their data is hosted with <strong>Storage Regions</strong>. Models, Datasets, Spaces can be stored in either US-based servers or Europe-based servers, allowing European companies to use Hugging Face in a GDPR compliant way.</p>
215
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/storage-regions">Storage Regions</a>.</aside>
216
+
217
+ <p><strong>Enterprise Plus</strong> offers organizations advanced governance and compliance features, with Network Security and Model Gateway.</p>
218
+
219
+ <p>With <strong>Network Security</strong>, Enterprise Plus organizations can define their corporate network and precisely define which public models, datasets, organizations and applications should be accessible to their employees. Admins can require authentication to access Hugging Face, so that only authorized employees can access it. In addition, admins can define exactly which resources should be allowed or blocked, which is useful to enforce company model governance rules, for instance to restrict access to certain models or organizations disallowed per company policy. Also with Network Security, admins can enable higher rate limits for their organization, and unlock power users of Hugging Face.</p>
220
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/enterprise-hub-network-security">Network Security</a>.</aside>
221
+
222
+ <p><strong>Model Gateway</strong> is an on-premise proxy of the Hugging Face Hub for companies with strict security, compliance and governance requirements. The Model Gateway is deployed by the company within their environment, and acts as a caching, governance and security layer between the employees network and the public Hugging Face Hub. With Model Gateway, companies can:</p>
223
+ <ul>
224
+ <li>Allow and restrict specific model, datasets and Spaces applications</li>
225
+ <li>Define the allowed licenses for models to prevent downloads of non-compliant models (for instance, to prevent employees from downloading GPL licensed or Non-Commercial licensed models)</li>
226
+ <li>Define the allowed file formats for models to reduce security risks (for instance, to prevent employees from downloading models stored as Pickle files)</li>
227
+ <li>Define model security scanning requirements, and implement custom model security scanners into the Model Gateway registry</li>
228
+ <li>Guarantee model supply chain robustness with models and versioning cached in local company storage (for instance, to prevent models from becoming unavailable if their provider on Hugging Face decided to delete them)</li>
229
+ <li>Simplify team model access for Gated models like Meta Llama, Google Gemma or Mistral models - permission is asked once and the model is then available to everyone at the company via the Model Gateway local registry.</li>
230
+ <li>Increase productivity for machine learning engineers with fast downloads from the local storage system</li>
231
+ <li>Provide auditability with logs documenting and attributing model downloads by employees</li>
232
+ </ul>
233
+ <p>Model Gateway is a new feature currently in preview for select Enterprise Plus organizations.</p>
234
+
235
+ <h3>Advanced Support</h3>
236
+
237
+ <p>Enterprise Hub organization members enjoy priority support covering their usage of Hugging Face via email requests.</p>
238
+ <p>Enterprise Plus organization members enjoy direct expert support from the Hugging Face team via a dedicated, private Slack channel hosted by Hugging Face.</p>
239
+
240
+ <h2>How to upgrade to Enterprise</h2>
241
+
242
+ <h3>Pricing</h3>
243
+
244
+ <p><strong>Enterprise Hub</strong> can be purchased directly at <a href="https://huggingface.co/enterprise">hf.co/enterprise</a> with a credit card as a $20/seat monthly subscription, or $216/seat yearly subscription.<br/>
245
+ Companies requiring a contract can request a standard 1-year contract subject to a $50k / 50 seats minimum.</p>
246
+
247
+ <p><strong>Enterprise Plus</strong> can only be purchased via a custom 1-year contract subject to a $120k / 50 seats minimum.</p>
248
+
249
+ <p>When purchased through a contract, Enterprise Hub and Enterprise Plus licenses include a number of seats corresponding to the number of members expected to be onboarded in the organization during the first year of the contract. Additional members joining the organization during the yearly term are not charged, and the contract value is updated upon yearly renewal to the number of members of the organization at renewal.</p>
250
+
251
+ <p>Enterprise organizations can purchase <strong>Compute Credits</strong> toward usage of premium features by their members, such as Inference Providers, Inference Endpoints and Spaces premium hardware. Compute Credits can be purchased upon request via an Order Form, are applied to the organization upon payment, and are valid for one year. Compute Credits purchases of $200k or higher qualify for volume discounts.</p>
252
+ <aside>See <a href="https://huggingface.co/pricing">pricing</a> for Hub premium usage-based features</aside>
253
+
254
+ <p>To contact the Hugging Face sales team, please email <a href=mailto:enterprise-contracts@huggingface.co>enterprise-contracts@huggingface.co</a></p>
255
+
256
+ <h3>Contracting</h3>
257
+
258
+ <p>To keep the process fast and smooth, Enterprise Hub and Enterprise Plus are governed by our standard online <a href="https://huggingface.co/terms-of-service">Terms of Service</a>, complemented by brief <a href="https://cdn-media.huggingface.co/landing/assets/Supplemental+Terms.pdf">Supplemental Terms</a>. These terms follow industry standards and are regularly updated to ensure they remain clear and fair. </p>
259
+
260
+ <h3>Frequently Asked Questions</h3>
261
+
262
+ <p><strong><i>Can I get on a call with a sales representative?</i></strong><br/>
263
+ Hugging Face sells and onboards customers via a streamlined process relying on written async communication (email and Slack). We are happy to answer any question you may have via email, but do not offer sales calls.</p>
264
+
265
+ <p><strong><i>Can I get a free trial of Enterprise Hub?</i></strong><br/>
266
+ Enterprise Hub features can be tested for as little as $20 with a credit card, and we do not offer free trials.</p>
267
+
268
+ <p><strong><i>How do I follow my internal new vendor onboarding processes?</i></strong><br/>
269
+ Any vendor, security or legal review process requires to purchase Enterprise Hub or Enterprise Plus via a contract. Our team will be happy to follow your new vendor onboarding process requirements at <a href=mailto:enterprise-contracts@huggingface.co>enterprise-contracts@huggingface.co</a></p>
270
+
271
+ <p><strong><i>How do I set up a security review of Hugging Face prior to purchase?</i></strong><br/>
272
+ Security reviews are only possible when purchasing Enterprise Hub or Enterprise Plus via a contract. Please contact our team at <a href=mailto:enterprise-contracts@huggingface.co>enterprise-contracts@huggingface.co</a>, upon signing a unilateral NDA, Hugging Face will share our SOC2 Type2 report detailing all the information required in security reviews. Our security team can accommodate custom security questionnaires for contract values above $120k.</p>
273
+
274
+ <p><strong><i>Can the standard contract terms be modified after my legal team reviews them?</i></strong><br/>
275
+ We only allow legal review for contract values above $120k, the Hugging Face legal team can review change requests to be added to the Order Form, which includes all contract legal terms. </p>
276
+
277
+ </d-article>
278
+
279
+ <d-appendix>
280
+ <h3 id="citation">Get Started</h3>
281
+ <p>Upgrade your organization to Enterprise at <a href="https://huggingface.co/enterprise">hf.co/enterprise</a></p>
282
+ </d-appendix>
283
+ <script>
284
+ function toggleTOC() {
285
+ const content = document.querySelector('.toc-content');
286
+ const icon = document.querySelector('.toggle-icon');
287
+
288
+ content.classList.toggle('collapsed');
289
+ icon.classList.toggle('collapsed');
290
+ }
291
+ </script>
292
+
293
+ <script>
294
+ const article = document.querySelector('d-article');
295
+ const toc = document.querySelector('d-contents');
296
+ if (toc) {
297
+ const headings = article.querySelectorAll('h2, h3, h4');
298
+ // let ToC = `<nav role="navigation" class="l-text figcaption"><h3>Table of contents</h3>`;
299
+ let ToC = `<nav role="navigation" class="l-text figcaption"><div class="toc-header" onclick="toggleTOC()">
300
+ <span class="toc-title">Table of Contents</span>
301
+ <span class="toggle-icon">▼</span>
302
+ </div><div class="toc-content">`;
303
+ let prevLevel = 0;
304
+
305
+ for (const el of headings) {
306
+ // should element be included in TOC?
307
+ const isInTitle = el.parentElement.tagName == 'D-TITLE';
308
+ const isException = el.getAttribute('no-toc');
309
+ if (isInTitle || isException) continue;
310
+ el.setAttribute('id', el.textContent.toLowerCase().replaceAll(" ", "_"))
311
+ const link = '<a target="_self" href="' + '#' + el.getAttribute('id') + '">' + el.textContent + '</a>';
312
+
313
+ const level = el.tagName === 'H2' ? 0 : (el.tagName === 'H3' ? 1 : 2);
314
+ while (prevLevel < level) {
315
+ ToC += '<ul>'
316
+ prevLevel++;
317
+ }
318
+ while (prevLevel > level) {
319
+ ToC += '</ul>'
320
+ prevLevel--;
321
+ }
322
+ if (level === 0)
323
+ ToC += '<div>' + link + '</div>';
324
+ else
325
+ // else if (level === 1)
326
+ ToC += '<li>' + link + '</li>';
327
+ }
328
+
329
+ while (prevLevel > 0) {
330
+ ToC += '</ul>'
331
+ prevLevel--;
332
+ }
333
+ ToC += '</div></nav>';
334
+ toc.innerHTML = ToC;
335
+ toc.setAttribute('prerendered', 'true');
336
+ const toc_links = document.querySelectorAll('d-contents > nav div a');
337
+
338
+ window.addEventListener('scroll', (_event) => {
339
+ if (typeof (headings) != 'undefined' && headings != null && typeof (toc_links) != 'undefined' && toc_links != null) {
340
+ find_active: {
341
+ for (let i = headings.length - 1; i >= 0; i--) {
342
+ const heading = headings[i];
343
+ // Skip headings that shouldn't be in TOC
344
+ if (heading.parentElement.tagName == 'D-TITLE' || heading.getAttribute('no-toc')) {
345
+ continue;
346
+ }
347
+
348
+ if (heading.getBoundingClientRect().top - 50 <= 0) {
349
+ // Find matching TOC link by href
350
+ const headingId = heading.getAttribute('id');
351
+ const activeLink = Array.from(toc_links).find(link =>
352
+ link.getAttribute('href') === '#' + headingId
353
+ );
354
+
355
+ if (activeLink && !activeLink.classList.contains("active")) {
356
+ toc_links.forEach(link => link.classList.remove("active"));
357
+ activeLink.classList.add('active');
358
+ }
359
+ break find_active;
360
+ }
361
+ }
362
+ toc_links.forEach(link => link.classList.remove("active"));
363
+ }
364
+ }
365
+ });
366
+ }
367
+ </script>
368
+
369
+ </body>
370
+
371
+ </html>
dist/main.bundle.js ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ "use strict";
3
+
4
+ ;// ./src/fragmentLoader.js
5
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
7
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
8
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
9
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
10
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
11
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
12
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
13
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
14
+ function loadFragments() {
15
+ return _loadFragments.apply(this, arguments);
16
+ }
17
+ function _loadFragments() {
18
+ _loadFragments = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
19
+ var fragmentElements, FetchQueue, fetchQueue, currentIndex, elements;
20
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
21
+ while (1) switch (_context7.prev = _context7.next) {
22
+ case 0:
23
+ // Find all elements with ids starting with 'fragment-'
24
+ fragmentElements = Array.from(document.querySelectorAll('[id^="fragment-"]'));
25
+ FetchQueue = /*#__PURE__*/function () {
26
+ function FetchQueue() {
27
+ var maxConcurrent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 3;
28
+ _classCallCheck(this, FetchQueue);
29
+ this.queue = [];
30
+ this.maxConcurrent = maxConcurrent;
31
+ this.activeFetches = 0;
32
+ this.maxRetries = 3; // Maximum number of retry attempts
33
+ this.baseDelay = 1000; // Base delay in milliseconds (1 second)
34
+ }
35
+ return _createClass(FetchQueue, [{
36
+ key: "sleep",
37
+ value: function () {
38
+ var _sleep = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(ms) {
39
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
40
+ while (1) switch (_context.prev = _context.next) {
41
+ case 0:
42
+ return _context.abrupt("return", new Promise(function (resolve) {
43
+ return setTimeout(resolve, ms);
44
+ }));
45
+ case 1:
46
+ case "end":
47
+ return _context.stop();
48
+ }
49
+ }, _callee);
50
+ }));
51
+ function sleep(_x) {
52
+ return _sleep.apply(this, arguments);
53
+ }
54
+ return sleep;
55
+ }()
56
+ }, {
57
+ key: "fetchWithRetry",
58
+ value: function () {
59
+ var _fetchWithRetry = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(fragmentPath) {
60
+ var retryCount,
61
+ response,
62
+ delay,
63
+ _args2 = arguments;
64
+ return _regeneratorRuntime().wrap(function _callee2$(_context2) {
65
+ while (1) switch (_context2.prev = _context2.next) {
66
+ case 0:
67
+ retryCount = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : 0;
68
+ _context2.prev = 1;
69
+ _context2.next = 4;
70
+ return fetch(fragmentPath);
71
+ case 4:
72
+ response = _context2.sent;
73
+ if (response.ok) {
74
+ _context2.next = 7;
75
+ break;
76
+ }
77
+ throw new Error("HTTP error! status: ".concat(response.status));
78
+ case 7:
79
+ _context2.next = 9;
80
+ return response.text();
81
+ case 9:
82
+ return _context2.abrupt("return", _context2.sent);
83
+ case 12:
84
+ _context2.prev = 12;
85
+ _context2.t0 = _context2["catch"](1);
86
+ if (!(retryCount < this.maxRetries)) {
87
+ _context2.next = 20;
88
+ break;
89
+ }
90
+ // Exponential backoff: 1s, 2s, 4s
91
+ delay = this.baseDelay * Math.pow(2, retryCount);
92
+ console.warn("Retry ".concat(retryCount + 1, "/").concat(this.maxRetries, " for ").concat(fragmentPath, " after ").concat(delay, "ms"));
93
+ _context2.next = 19;
94
+ return this.sleep(delay);
95
+ case 19:
96
+ return _context2.abrupt("return", this.fetchWithRetry(fragmentPath, retryCount + 1));
97
+ case 20:
98
+ throw _context2.t0;
99
+ case 21:
100
+ case "end":
101
+ return _context2.stop();
102
+ }
103
+ }, _callee2, this, [[1, 12]]);
104
+ }));
105
+ function fetchWithRetry(_x2) {
106
+ return _fetchWithRetry.apply(this, arguments);
107
+ }
108
+ return fetchWithRetry;
109
+ }()
110
+ }, {
111
+ key: "addFetch",
112
+ value: function () {
113
+ var _addFetch = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee5(element) {
114
+ var _this = this;
115
+ var fragmentName, fragmentPath;
116
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
117
+ while (1) switch (_context5.prev = _context5.next) {
118
+ case 0:
119
+ fragmentName = element.id.replace('fragment-', '');
120
+ fragmentPath = "fragments/".concat(fragmentName, ".html");
121
+ return _context5.abrupt("return", new Promise(/*#__PURE__*/function () {
122
+ var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4(resolve, reject) {
123
+ var fetchPromise;
124
+ return _regeneratorRuntime().wrap(function _callee4$(_context4) {
125
+ while (1) switch (_context4.prev = _context4.next) {
126
+ case 0:
127
+ try {
128
+ fetchPromise = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
129
+ var html, temp, scripts;
130
+ return _regeneratorRuntime().wrap(function _callee3$(_context3) {
131
+ while (1) switch (_context3.prev = _context3.next) {
132
+ case 0:
133
+ _context3.prev = 0;
134
+ _context3.next = 3;
135
+ return _this.fetchWithRetry(fragmentPath);
136
+ case 3:
137
+ html = _context3.sent;
138
+ // Process the fragment
139
+ temp = document.createElement('div');
140
+ temp.innerHTML = html;
141
+ element.innerHTML = temp.innerHTML;
142
+
143
+ // Handle scripts
144
+ scripts = temp.getElementsByTagName('script');
145
+ Array.from(scripts).forEach(function (oldScript) {
146
+ var newScript = document.createElement('script');
147
+ Array.from(oldScript.attributes).forEach(function (attr) {
148
+ newScript.setAttribute(attr.name, attr.value);
149
+ });
150
+ newScript.textContent = oldScript.textContent;
151
+ oldScript.parentNode.removeChild(oldScript);
152
+ document.body.appendChild(newScript);
153
+ });
154
+ _this.activeFetches--;
155
+ resolve();
156
+ _context3.next = 18;
157
+ break;
158
+ case 13:
159
+ _context3.prev = 13;
160
+ _context3.t0 = _context3["catch"](0);
161
+ console.error("Failed to load fragment ".concat(fragmentPath, " after ").concat(_this.maxRetries, " retries:"), _context3.t0);
162
+ _this.activeFetches--;
163
+ reject(_context3.t0);
164
+ case 18:
165
+ case "end":
166
+ return _context3.stop();
167
+ }
168
+ }, _callee3, null, [[0, 13]]);
169
+ }))();
170
+ _this.queue.push(fetchPromise);
171
+ _this.activeFetches++;
172
+ } catch (error) {
173
+ reject(error);
174
+ }
175
+ case 1:
176
+ case "end":
177
+ return _context4.stop();
178
+ }
179
+ }, _callee4);
180
+ }));
181
+ return function (_x4, _x5) {
182
+ return _ref.apply(this, arguments);
183
+ };
184
+ }()));
185
+ case 3:
186
+ case "end":
187
+ return _context5.stop();
188
+ }
189
+ }, _callee5);
190
+ }));
191
+ function addFetch(_x3) {
192
+ return _addFetch.apply(this, arguments);
193
+ }
194
+ return addFetch;
195
+ }()
196
+ }, {
197
+ key: "processNext",
198
+ value: function () {
199
+ var _processNext = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee6(element) {
200
+ return _regeneratorRuntime().wrap(function _callee6$(_context6) {
201
+ while (1) switch (_context6.prev = _context6.next) {
202
+ case 0:
203
+ if (!(this.activeFetches < this.maxConcurrent && element)) {
204
+ _context6.next = 3;
205
+ break;
206
+ }
207
+ _context6.next = 3;
208
+ return this.addFetch(element);
209
+ case 3:
210
+ case "end":
211
+ return _context6.stop();
212
+ }
213
+ }, _callee6, this);
214
+ }));
215
+ function processNext(_x6) {
216
+ return _processNext.apply(this, arguments);
217
+ }
218
+ return processNext;
219
+ }()
220
+ }]);
221
+ }(); // Initialize queue
222
+ fetchQueue = new FetchQueue(3);
223
+ currentIndex = 0;
224
+ elements = fragmentElements; // Assuming this is defined elsewhere
225
+ // Initial loading of first 3 elements
226
+ case 5:
227
+ if (!(currentIndex < elements.length && currentIndex < 3)) {
228
+ _context7.next = 11;
229
+ break;
230
+ }
231
+ _context7.next = 8;
232
+ return fetchQueue.processNext(elements[currentIndex]);
233
+ case 8:
234
+ currentIndex++;
235
+ _context7.next = 5;
236
+ break;
237
+ case 11:
238
+ if (!(currentIndex < elements.length)) {
239
+ _context7.next = 20;
240
+ break;
241
+ }
242
+ _context7.next = 14;
243
+ return Promise.race(fetchQueue.queue);
244
+ case 14:
245
+ // Remove completed fetch from queue
246
+ fetchQueue.queue = fetchQueue.queue.filter(function (p) {
247
+ return p.status === 'pending';
248
+ });
249
+ // Add next element to queue
250
+ _context7.next = 17;
251
+ return fetchQueue.processNext(elements[currentIndex]);
252
+ case 17:
253
+ currentIndex++;
254
+ _context7.next = 11;
255
+ break;
256
+ case 20:
257
+ _context7.next = 22;
258
+ return Promise.all(fetchQueue.queue);
259
+ case 22:
260
+ case "end":
261
+ return _context7.stop();
262
+ }
263
+ }, _callee7);
264
+ }));
265
+ return _loadFragments.apply(this, arguments);
266
+ }
267
+
268
+ ;// ./src/syncHFSpacesURLHash.js
269
+ var queryArg = "section";
270
+ function syncHFSpacesURLHash() {
271
+ // Handle explicit section requests (don't update hash automatically on load)
272
+ var hasExplicitRequest = handleExplicitSectionRequest();
273
+
274
+ // Set up hash change monitoring
275
+ updateHashBasedOnHashChange();
276
+
277
+ // Always set up scroll monitoring to update hash during scrolling
278
+ setupScrollMonitoring();
279
+
280
+ // If no explicit request, we don't update the hash on initial load
281
+ // The hash will only start updating when the user scrolls
282
+ }
283
+ function handleExplicitSectionRequest() {
284
+ // Check for section parameter in URL
285
+ var urlParams = new URLSearchParams(window.location.search);
286
+ var sectionId = urlParams.get(queryArg);
287
+
288
+ // If we have an explicit section request
289
+ if (sectionId) {
290
+ var targetElement = document.getElementById(sectionId);
291
+ if (targetElement) {
292
+ // Slight delay to ensure the browser doesn't try to do its own scrolling first
293
+ setTimeout(function () {
294
+ targetElement.scrollIntoView();
295
+ history.replaceState(null, null, "#".concat(sectionId));
296
+ }, 100);
297
+ }
298
+ return true;
299
+ }
300
+
301
+ // No explicit section parameter found
302
+ return false;
303
+ }
304
+ function setupScrollMonitoring() {
305
+ // Variables to manage throttling
306
+ var isScrolling = false;
307
+ var lastKnownScrollPosition = 0;
308
+ var initialScroll = true;
309
+
310
+ // Add the scroll event listener
311
+ window.addEventListener('scroll', function () {
312
+ lastKnownScrollPosition = window.scrollY;
313
+ if (!isScrolling) {
314
+ window.requestAnimationFrame(function () {
315
+ // Skip the first scroll event which might be browser's automatic scroll
316
+ // to a hash on page load
317
+ if (initialScroll) {
318
+ initialScroll = false;
319
+ } else {
320
+ updateHashBasedOnScroll(lastKnownScrollPosition);
321
+ }
322
+ isScrolling = false;
323
+ });
324
+ }
325
+ isScrolling = true;
326
+ });
327
+ }
328
+
329
+ // Function to update the URL hash based on scroll position
330
+ function updateHashBasedOnScroll(scrollPosition) {
331
+ var closestHeading = findClosestHeading(scrollPosition);
332
+
333
+ // Update the URL hash if we found a closest element
334
+ if (closestHeading && closestHeading.id) {
335
+ // Only update if the hash is different to avoid unnecessary operations
336
+ if (window.location.hash !== "#".concat(closestHeading.id)) {
337
+ silentlyUpdateHash(closestHeading.id);
338
+ postMessageToHFSpaces(closestHeading.id);
339
+ }
340
+ }
341
+ }
342
+
343
+ // Find the closest heading to the current scroll position
344
+ function findClosestHeading(scrollPosition) {
345
+ // Get only heading elements with IDs that we want to track
346
+ var headingsWithIds = Array.from(document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'));
347
+
348
+ // Skip if there are no headings with IDs
349
+ if (headingsWithIds.length === 0) return null;
350
+
351
+ // Find the element closest to the middle of the viewport
352
+ var closestHeading = null;
353
+ var closestDistance = Infinity;
354
+ var viewportMiddle = scrollPosition + window.innerHeight / 2;
355
+
356
+ // Iterate through all headings to find the closest one
357
+ headingsWithIds.forEach(function (heading) {
358
+ var headingTop = heading.getBoundingClientRect().top + scrollPosition;
359
+ var distance = Math.abs(headingTop - viewportMiddle);
360
+ if (distance < closestDistance) {
361
+ closestDistance = distance;
362
+ closestHeading = heading;
363
+ }
364
+ });
365
+ return closestHeading;
366
+ }
367
+
368
+ // Update hash without triggering scroll or other side effects
369
+ function silentlyUpdateHash(id) {
370
+ history.replaceState(null, null, "#".concat(id));
371
+ }
372
+ function updateHashBasedOnHashChange() {
373
+ window.addEventListener('hashchange', function () {
374
+ var elementId = window.location.hash.slice(1);
375
+ postMessageToHFSpaces(elementId);
376
+ });
377
+ }
378
+ function postMessageToHFSpaces(elementId) {
379
+ var parentOrigin = "https://huggingface.co";
380
+ window.parent.postMessage({
381
+ queryString: "".concat(queryArg, "=").concat(elementId)
382
+ }, parentOrigin);
383
+ }
384
+
385
+ ;// ./src/index.js
386
+ // import { plotClusters } from './clusters'
387
+
388
+
389
+ document.addEventListener("DOMContentLoaded", function () {
390
+ console.log("DOMContentLoaded");
391
+ loadFragments();
392
+ init_memory_plot();
393
+ syncHFSpacesURLHash();
394
+ }, {
395
+ once: true
396
+ });
397
+ /******/ })()
398
+ ;
399
+ //# sourceMappingURL=main.bundle.js.map
dist/main.bundle.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"main.bundle.js","mappings":";;;;;+CACA,qJAAAA,mBAAA,YAAAA,oBAAA,WAAAC,CAAA,SAAAC,CAAA,EAAAD,CAAA,OAAAE,CAAA,GAAAC,MAAA,CAAAC,SAAA,EAAAC,CAAA,GAAAH,CAAA,CAAAI,cAAA,EAAAC,CAAA,GAAAJ,MAAA,CAAAK,cAAA,cAAAP,CAAA,EAAAD,CAAA,EAAAE,CAAA,IAAAD,CAAA,CAAAD,CAAA,IAAAE,CAAA,CAAAO,KAAA,KAAAC,CAAA,wBAAAC,MAAA,GAAAA,MAAA,OAAAC,CAAA,GAAAF,CAAA,CAAAG,QAAA,kBAAAC,CAAA,GAAAJ,CAAA,CAAAK,aAAA,uBAAAC,CAAA,GAAAN,CAAA,CAAAO,WAAA,8BAAAC,OAAAjB,CAAA,EAAAD,CAAA,EAAAE,CAAA,WAAAC,MAAA,CAAAK,cAAA,CAAAP,CAAA,EAAAD,CAAA,IAAAS,KAAA,EAAAP,CAAA,EAAAiB,UAAA,MAAAC,YAAA,MAAAC,QAAA,SAAApB,CAAA,CAAAD,CAAA,WAAAkB,MAAA,mBAAAjB,CAAA,IAAAiB,MAAA,YAAAA,OAAAjB,CAAA,EAAAD,CAAA,EAAAE,CAAA,WAAAD,CAAA,CAAAD,CAAA,IAAAE,CAAA,gBAAAoB,KAAArB,CAAA,EAAAD,CAAA,EAAAE,CAAA,EAAAG,CAAA,QAAAK,CAAA,GAAAV,CAAA,IAAAA,CAAA,CAAAI,SAAA,YAAAmB,SAAA,GAAAvB,CAAA,GAAAuB,SAAA,EAAAX,CAAA,GAAAT,MAAA,CAAAqB,MAAA,CAAAd,CAAA,CAAAN,SAAA,GAAAU,CAAA,OAAAW,OAAA,CAAApB,CAAA,gBAAAE,CAAA,CAAAK,CAAA,eAAAH,KAAA,EAAAiB,gBAAA,CAAAzB,CAAA,EAAAC,CAAA,EAAAY,CAAA,MAAAF,CAAA,aAAAe,SAAA1B,CAAA,EAAAD,CAAA,EAAAE,CAAA,mBAAA0B,IAAA,YAAAC,GAAA,EAAA5B,CAAA,CAAA6B,IAAA,CAAA9B,CAAA,EAAAE,CAAA,cAAAD,CAAA,aAAA2B,IAAA,WAAAC,GAAA,EAAA5B,CAAA,QAAAD,CAAA,CAAAsB,IAAA,GAAAA,IAAA,MAAAS,CAAA,qBAAAC,CAAA,qBAAAC,CAAA,gBAAAC,CAAA,gBAAAC,CAAA,gBAAAZ,UAAA,cAAAa,kBAAA,cAAAC,2BAAA,SAAAC,CAAA,OAAApB,MAAA,CAAAoB,CAAA,EAAA1B,CAAA,qCAAA2B,CAAA,GAAApC,MAAA,CAAAqC,cAAA,EAAAC,CAAA,GAAAF,CAAA,IAAAA,CAAA,CAAAA,CAAA,CAAAG,MAAA,QAAAD,CAAA,IAAAA,CAAA,KAAAvC,CAAA,IAAAG,CAAA,CAAAyB,IAAA,CAAAW,CAAA,EAAA7B,CAAA,MAAA0B,CAAA,GAAAG,CAAA,OAAAE,CAAA,GAAAN,0BAAA,CAAAjC,SAAA,GAAAmB,SAAA,CAAAnB,SAAA,GAAAD,MAAA,CAAAqB,MAAA,CAAAc,CAAA,YAAAM,sBAAA3C,CAAA,gCAAA4C,OAAA,WAAA7C,CAAA,IAAAkB,MAAA,CAAAjB,CAAA,EAAAD,CAAA,YAAAC,CAAA,gBAAA6C,OAAA,CAAA9C,CAAA,EAAAC,CAAA,sBAAA8C,cAAA9C,CAAA,EAAAD,CAAA,aAAAgD,OAAA9C,CAAA,EAAAK,CAAA,EAAAG,CAAA,EAAAE,CAAA,QAAAE,CAAA,GAAAa,QAAA,CAAA1B,CAAA,CAAAC,CAAA,GAAAD,CAAA,EAAAM,CAAA,mBAAAO,CAAA,CAAAc,IAAA,QAAAZ,CAAA,GAAAF,CAAA,CAAAe,GAAA,EAAAE,CAAA,GAAAf,CAAA,CAAAP,KAAA,SAAAsB,CAAA,gBAAAkB,OAAA,CAAAlB,CAAA,KAAA1B,CAAA,CAAAyB,IAAA,CAAAC,CAAA,eAAA/B,CAAA,CAAAkD,OAAA,CAAAnB,CAAA,CAAAoB,OAAA,EAAAC,IAAA,WAAAnD,CAAA,IAAA+C,MAAA,SAAA/C,CAAA,EAAAS,CAAA,EAAAE,CAAA,gBAAAX,CAAA,IAAA+C,MAAA,UAAA/C,CAAA,EAAAS,CAAA,EAAAE,CAAA,QAAAZ,CAAA,CAAAkD,OAAA,CAAAnB,CAAA,EAAAqB,IAAA,WAAAnD,CAAA,IAAAe,CAAA,CAAAP,KAAA,GAAAR,CAAA,EAAAS,CAAA,CAAAM,CAAA,gBAAAf,CAAA,WAAA+C,MAAA,UAAA/C,CAAA,EAAAS,CAAA,EAAAE,CAAA,SAAAA,CAAA,CAAAE,CAAA,CAAAe,GAAA,SAAA3B,CAAA,EAAAK,CAAA,oBAAAE,KAAA,WAAAA,MAAAR,CAAA,EAAAI,CAAA,aAAAgD,2BAAA,eAAArD,CAAA,WAAAA,CAAA,EAAAE,CAAA,IAAA8C,MAAA,CAAA/C,CAAA,EAAAI,CAAA,EAAAL,CAAA,EAAAE,CAAA,gBAAAA,CAAA,GAAAA,CAAA,GAAAA,CAAA,CAAAkD,IAAA,CAAAC,0BAAA,EAAAA,0BAAA,IAAAA,0BAAA,qBAAA3B,iBAAA1B,CAAA,EAAAE,CAAA,EAAAG,CAAA,QAAAE,CAAA,GAAAwB,CAAA,mBAAArB,CAAA,EAAAE,CAAA,QAAAL,CAAA,KAAA0B,CAAA,QAAAqB,KAAA,sCAAA/C,CAAA,KAAA2B,CAAA,oBAAAxB,CAAA,QAAAE,CAAA,WAAAH,KAAA,EAAAR,CAAA,EAAAsD,IAAA,eAAAlD,CAAA,CAAAmD,MAAA,GAAA9C,CAAA,EAAAL,CAAA,CAAAwB,GAAA,GAAAjB,CAAA,UAAAE,CAAA,GAAAT,CAAA,CAAAoD,QAAA,MAAA3C,CAAA,QAAAE,CAAA,GAAA0C,mBAAA,CAAA5C,CAAA,EAAAT,CAAA,OAAAW,CAAA,QAAAA,CAAA,KAAAmB,CAAA,mBAAAnB,CAAA,qBAAAX,CAAA,CAAAmD,MAAA,EAAAnD,CAAA,CAAAsD,IAAA,GAAAtD,CAAA,CAAAuD,KAAA,GAAAvD,CAAA,CAAAwB,GAAA,sBAAAxB,CAAA,CAAAmD,MAAA,QAAAjD,CAAA,KAAAwB,CAAA,QAAAxB,CAAA,GAAA2B,CAAA,EAAA7B,CAAA,CAAAwB,GAAA,EAAAxB,CAAA,CAAAwD,iBAAA,CAAAxD,CAAA,CAAAwB,GAAA,uBAAAxB,CAAA,CAAAmD,MAAA,IAAAnD,CAAA,CAAAyD,MAAA,WAAAzD,CAAA,CAAAwB,GAAA,GAAAtB,CAAA,GAAA0B,CAAA,MAAAK,CAAA,GAAAX,QAAA,CAAA3B,CAAA,EAAAE,CAAA,EAAAG,CAAA,oBAAAiC,CAAA,CAAAV,IAAA,QAAArB,CAAA,GAAAF,CAAA,CAAAkD,IAAA,GAAArB,CAAA,GAAAF,CAAA,EAAAM,CAAA,CAAAT,GAAA,KAAAM,CAAA,qBAAA1B,KAAA,EAAA6B,CAAA,CAAAT,GAAA,EAAA0B,IAAA,EAAAlD,CAAA,CAAAkD,IAAA,kBAAAjB,CAAA,CAAAV,IAAA,KAAArB,CAAA,GAAA2B,CAAA,EAAA7B,CAAA,CAAAmD,MAAA,YAAAnD,CAAA,CAAAwB,GAAA,GAAAS,CAAA,CAAAT,GAAA,mBAAA6B,oBAAA1D,CAAA,EAAAE,CAAA,QAAAG,CAAA,GAAAH,CAAA,CAAAsD,MAAA,EAAAjD,CAAA,GAAAP,CAAA,CAAAa,QAAA,CAAAR,CAAA,OAAAE,CAAA,KAAAN,CAAA,SAAAC,CAAA,CAAAuD,QAAA,qBAAApD,CAAA,IAAAL,CAAA,CAAAa,QAAA,eAAAX,CAAA,CAAAsD,MAAA,aAAAtD,CAAA,CAAA2B,GAAA,GAAA5B,CAAA,EAAAyD,mBAAA,CAAA1D,CAAA,EAAAE,CAAA,eAAAA,CAAA,CAAAsD,MAAA,kBAAAnD,CAAA,KAAAH,CAAA,CAAAsD,MAAA,YAAAtD,CAAA,CAAA2B,GAAA,OAAAkC,SAAA,uCAAA1D,CAAA,iBAAA8B,CAAA,MAAAzB,CAAA,GAAAiB,QAAA,CAAApB,CAAA,EAAAP,CAAA,CAAAa,QAAA,EAAAX,CAAA,CAAA2B,GAAA,mBAAAnB,CAAA,CAAAkB,IAAA,SAAA1B,CAAA,CAAAsD,MAAA,YAAAtD,CAAA,CAAA2B,GAAA,GAAAnB,CAAA,CAAAmB,GAAA,EAAA3B,CAAA,CAAAuD,QAAA,SAAAtB,CAAA,MAAAvB,CAAA,GAAAF,CAAA,CAAAmB,GAAA,SAAAjB,CAAA,GAAAA,CAAA,CAAA2C,IAAA,IAAArD,CAAA,CAAAF,CAAA,CAAAgE,UAAA,IAAApD,CAAA,CAAAH,KAAA,EAAAP,CAAA,CAAA+D,IAAA,GAAAjE,CAAA,CAAAkE,OAAA,eAAAhE,CAAA,CAAAsD,MAAA,KAAAtD,CAAA,CAAAsD,MAAA,WAAAtD,CAAA,CAAA2B,GAAA,GAAA5B,CAAA,GAAAC,CAAA,CAAAuD,QAAA,SAAAtB,CAAA,IAAAvB,CAAA,IAAAV,CAAA,CAAAsD,MAAA,YAAAtD,CAAA,CAAA2B,GAAA,OAAAkC,SAAA,sCAAA7D,CAAA,CAAAuD,QAAA,SAAAtB,CAAA,cAAAgC,aAAAlE,CAAA,QAAAD,CAAA,KAAAoE,MAAA,EAAAnE,CAAA,YAAAA,CAAA,KAAAD,CAAA,CAAAqE,QAAA,GAAApE,CAAA,WAAAA,CAAA,KAAAD,CAAA,CAAAsE,UAAA,GAAArE,CAAA,KAAAD,CAAA,CAAAuE,QAAA,GAAAtE,CAAA,WAAAuE,UAAA,CAAAC,IAAA,CAAAzE,CAAA,cAAA0E,cAAAzE,CAAA,QAAAD,CAAA,GAAAC,CAAA,CAAA0E,UAAA,QAAA3E,CAAA,CAAA4B,IAAA,oBAAA5B,CAAA,CAAA6B,GAAA,EAAA5B,CAAA,CAAA0E,UAAA,GAAA3E,CAAA,aAAAyB,QAAAxB,CAAA,SAAAuE,UAAA,MAAAJ,MAAA,aAAAnE,CAAA,CAAA4C,OAAA,CAAAsB,YAAA,cAAAS,KAAA,iBAAAlC,OAAA1C,CAAA,QAAAA,CAAA,WAAAA,CAAA,QAAAE,CAAA,GAAAF,CAAA,CAAAY,CAAA,OAAAV,CAAA,SAAAA,CAAA,CAAA4B,IAAA,CAAA9B,CAAA,4BAAAA,CAAA,CAAAiE,IAAA,SAAAjE,CAAA,OAAA6E,KAAA,CAAA7E,CAAA,CAAA8E,MAAA,SAAAvE,CAAA,OAAAG,CAAA,YAAAuD,KAAA,aAAA1D,CAAA,GAAAP,CAAA,CAAA8E,MAAA,OAAAzE,CAAA,CAAAyB,IAAA,CAAA9B,CAAA,EAAAO,CAAA,UAAA0D,IAAA,CAAAxD,KAAA,GAAAT,CAAA,CAAAO,CAAA,GAAA0D,IAAA,CAAAV,IAAA,OAAAU,IAAA,SAAAA,IAAA,CAAAxD,KAAA,GAAAR,CAAA,EAAAgE,IAAA,CAAAV,IAAA,OAAAU,IAAA,YAAAvD,CAAA,CAAAuD,IAAA,GAAAvD,CAAA,gBAAAqD,SAAA,CAAAd,OAAA,CAAAjD,CAAA,kCAAAoC,iBAAA,CAAAhC,SAAA,GAAAiC,0BAAA,EAAA9B,CAAA,CAAAoC,CAAA,mBAAAlC,KAAA,EAAA4B,0BAAA,EAAAjB,YAAA,SAAAb,CAAA,CAAA8B,0BAAA,mBAAA5B,KAAA,EAAA2B,iBAAA,EAAAhB,YAAA,SAAAgB,iBAAA,CAAA2C,WAAA,GAAA7D,MAAA,CAAAmB,0BAAA,EAAArB,CAAA,wBAAAhB,CAAA,CAAAgF,mBAAA,aAAA/E,CAAA,QAAAD,CAAA,wBAAAC,CAAA,IAAAA,CAAA,CAAAgF,WAAA,WAAAjF,CAAA,KAAAA,CAAA,KAAAoC,iBAAA,6BAAApC,CAAA,CAAA+E,WAAA,IAAA/E,CAAA,CAAAkF,IAAA,OAAAlF,CAAA,CAAAmF,IAAA,aAAAlF,CAAA,WAAAE,MAAA,CAAAiF,cAAA,GAAAjF,MAAA,CAAAiF,cAAA,CAAAnF,CAAA,EAAAoC,0BAAA,KAAApC,CAAA,CAAAoF,SAAA,GAAAhD,0BAAA,EAAAnB,MAAA,CAAAjB,CAAA,EAAAe,CAAA,yBAAAf,CAAA,CAAAG,SAAA,GAAAD,MAAA,CAAAqB,MAAA,CAAAmB,CAAA,GAAA1C,CAAA,KAAAD,CAAA,CAAAsF,KAAA,aAAArF,CAAA,aAAAkD,OAAA,EAAAlD,CAAA,OAAA2C,qBAAA,CAAAG,aAAA,CAAA3C,SAAA,GAAAc,MAAA,CAAA6B,aAAA,CAAA3C,SAAA,EAAAU,CAAA,iCAAAd,CAAA,CAAA+C,aAAA,GAAAA,aAAA,EAAA/C,CAAA,CAAAuF,KAAA,aAAAtF,CAAA,EAAAC,CAAA,EAAAG,CAAA,EAAAE,CAAA,EAAAG,CAAA,eAAAA,CAAA,KAAAA,CAAA,GAAA8E,OAAA,OAAA5E,CAAA,OAAAmC,aAAA,CAAAzB,IAAA,CAAArB,CAAA,EAAAC,CAAA,EAAAG,CAAA,EAAAE,CAAA,GAAAG,CAAA,UAAAV,CAAA,CAAAgF,mBAAA,CAAA9E,CAAA,IAAAU,CAAA,GAAAA,CAAA,CAAAqD,IAAA,GAAAb,IAAA,WAAAnD,CAAA,WAAAA,CAAA,CAAAsD,IAAA,GAAAtD,CAAA,CAAAQ,KAAA,GAAAG,CAAA,CAAAqD,IAAA,WAAArB,qBAAA,CAAAD,CAAA,GAAAzB,MAAA,CAAAyB,CAAA,EAAA3B,CAAA,gBAAAE,MAAA,CAAAyB,CAAA,EAAA/B,CAAA,iCAAAM,MAAA,CAAAyB,CAAA,6DAAA3C,CAAA,CAAAyF,IAAA,aAAAxF,CAAA,QAAAD,CAAA,GAAAG,MAAA,CAAAF,CAAA,GAAAC,CAAA,gBAAAG,CAAA,IAAAL,CAAA,EAAAE,CAAA,CAAAuE,IAAA,CAAApE,CAAA,UAAAH,CAAA,CAAAwF,OAAA,aAAAzB,KAAA,WAAA/D,CAAA,CAAA4E,MAAA,SAAA7E,CAAA,GAAAC,CAAA,CAAAyF,GAAA,QAAA1F,CAAA,IAAAD,CAAA,SAAAiE,IAAA,CAAAxD,KAAA,GAAAR,CAAA,EAAAgE,IAAA,CAAAV,IAAA,OAAAU,IAAA,WAAAA,IAAA,CAAAV,IAAA,OAAAU,IAAA,QAAAjE,CAAA,CAAA0C,MAAA,GAAAA,MAAA,EAAAjB,OAAA,CAAArB,SAAA,KAAA6E,WAAA,EAAAxD,OAAA,EAAAmD,KAAA,WAAAA,MAAA5E,CAAA,aAAA4F,IAAA,WAAA3B,IAAA,WAAAN,IAAA,QAAAC,KAAA,GAAA3D,CAAA,OAAAsD,IAAA,YAAAE,QAAA,cAAAD,MAAA,gBAAA3B,GAAA,GAAA5B,CAAA,OAAAuE,UAAA,CAAA3B,OAAA,CAAA6B,aAAA,IAAA1E,CAAA,WAAAE,CAAA,kBAAAA,CAAA,CAAA2F,MAAA,OAAAxF,CAAA,CAAAyB,IAAA,OAAA5B,CAAA,MAAA2E,KAAA,EAAA3E,CAAA,CAAA4F,KAAA,cAAA5F,CAAA,IAAAD,CAAA,MAAA8F,IAAA,WAAAA,KAAA,SAAAxC,IAAA,WAAAtD,CAAA,QAAAuE,UAAA,IAAAG,UAAA,kBAAA1E,CAAA,CAAA2B,IAAA,QAAA3B,CAAA,CAAA4B,GAAA,cAAAmE,IAAA,KAAAnC,iBAAA,WAAAA,kBAAA7D,CAAA,aAAAuD,IAAA,QAAAvD,CAAA,MAAAE,CAAA,kBAAA+F,OAAA5F,CAAA,EAAAE,CAAA,WAAAK,CAAA,CAAAgB,IAAA,YAAAhB,CAAA,CAAAiB,GAAA,GAAA7B,CAAA,EAAAE,CAAA,CAAA+D,IAAA,GAAA5D,CAAA,EAAAE,CAAA,KAAAL,CAAA,CAAAsD,MAAA,WAAAtD,CAAA,CAAA2B,GAAA,GAAA5B,CAAA,KAAAM,CAAA,aAAAA,CAAA,QAAAiE,UAAA,CAAAM,MAAA,MAAAvE,CAAA,SAAAA,CAAA,QAAAG,CAAA,QAAA8D,UAAA,CAAAjE,CAAA,GAAAK,CAAA,GAAAF,CAAA,CAAAiE,UAAA,iBAAAjE,CAAA,CAAA0D,MAAA,SAAA6B,MAAA,aAAAvF,CAAA,CAAA0D,MAAA,SAAAwB,IAAA,QAAA9E,CAAA,GAAAT,CAAA,CAAAyB,IAAA,CAAApB,CAAA,eAAAM,CAAA,GAAAX,CAAA,CAAAyB,IAAA,CAAApB,CAAA,qBAAAI,CAAA,IAAAE,CAAA,aAAA4E,IAAA,GAAAlF,CAAA,CAAA2D,QAAA,SAAA4B,MAAA,CAAAvF,CAAA,CAAA2D,QAAA,gBAAAuB,IAAA,GAAAlF,CAAA,CAAA4D,UAAA,SAAA2B,MAAA,CAAAvF,CAAA,CAAA4D,UAAA,cAAAxD,CAAA,aAAA8E,IAAA,GAAAlF,CAAA,CAAA2D,QAAA,SAAA4B,MAAA,CAAAvF,CAAA,CAAA2D,QAAA,qBAAArD,CAAA,QAAAsC,KAAA,qDAAAsC,IAAA,GAAAlF,CAAA,CAAA4D,UAAA,SAAA2B,MAAA,CAAAvF,CAAA,CAAA4D,UAAA,YAAAR,MAAA,WAAAA,OAAA7D,CAAA,EAAAD,CAAA,aAAAE,CAAA,QAAAsE,UAAA,CAAAM,MAAA,MAAA5E,CAAA,SAAAA,CAAA,QAAAK,CAAA,QAAAiE,UAAA,CAAAtE,CAAA,OAAAK,CAAA,CAAA6D,MAAA,SAAAwB,IAAA,IAAAvF,CAAA,CAAAyB,IAAA,CAAAvB,CAAA,wBAAAqF,IAAA,GAAArF,CAAA,CAAA+D,UAAA,QAAA5D,CAAA,GAAAH,CAAA,aAAAG,CAAA,iBAAAT,CAAA,mBAAAA,CAAA,KAAAS,CAAA,CAAA0D,MAAA,IAAApE,CAAA,IAAAA,CAAA,IAAAU,CAAA,CAAA4D,UAAA,KAAA5D,CAAA,cAAAE,CAAA,GAAAF,CAAA,GAAAA,CAAA,CAAAiE,UAAA,cAAA/D,CAAA,CAAAgB,IAAA,GAAA3B,CAAA,EAAAW,CAAA,CAAAiB,GAAA,GAAA7B,CAAA,EAAAU,CAAA,SAAA8C,MAAA,gBAAAS,IAAA,GAAAvD,CAAA,CAAA4D,UAAA,EAAAnC,CAAA,SAAA+D,QAAA,CAAAtF,CAAA,MAAAsF,QAAA,WAAAA,SAAAjG,CAAA,EAAAD,CAAA,oBAAAC,CAAA,CAAA2B,IAAA,QAAA3B,CAAA,CAAA4B,GAAA,qBAAA5B,CAAA,CAAA2B,IAAA,mBAAA3B,CAAA,CAAA2B,IAAA,QAAAqC,IAAA,GAAAhE,CAAA,CAAA4B,GAAA,gBAAA5B,CAAA,CAAA2B,IAAA,SAAAoE,IAAA,QAAAnE,GAAA,GAAA5B,CAAA,CAAA4B,GAAA,OAAA2B,MAAA,kBAAAS,IAAA,yBAAAhE,CAAA,CAAA2B,IAAA,IAAA5B,CAAA,UAAAiE,IAAA,GAAAjE,CAAA,GAAAmC,CAAA,KAAAgE,MAAA,WAAAA,OAAAlG,CAAA,aAAAD,CAAA,QAAAwE,UAAA,CAAAM,MAAA,MAAA9E,CAAA,SAAAA,CAAA,QAAAE,CAAA,QAAAsE,UAAA,CAAAxE,CAAA,OAAAE,CAAA,CAAAoE,UAAA,KAAArE,CAAA,cAAAiG,QAAA,CAAAhG,CAAA,CAAAyE,UAAA,EAAAzE,CAAA,CAAAqE,QAAA,GAAAG,aAAA,CAAAxE,CAAA,GAAAiC,CAAA,yBAAAiE,OAAAnG,CAAA,aAAAD,CAAA,QAAAwE,UAAA,CAAAM,MAAA,MAAA9E,CAAA,SAAAA,CAAA,QAAAE,CAAA,QAAAsE,UAAA,CAAAxE,CAAA,OAAAE,CAAA,CAAAkE,MAAA,KAAAnE,CAAA,QAAAI,CAAA,GAAAH,CAAA,CAAAyE,UAAA,kBAAAtE,CAAA,CAAAuB,IAAA,QAAArB,CAAA,GAAAF,CAAA,CAAAwB,GAAA,EAAA6C,aAAA,CAAAxE,CAAA,YAAAK,CAAA,YAAA+C,KAAA,8BAAA+C,aAAA,WAAAA,cAAArG,CAAA,EAAAE,CAAA,EAAAG,CAAA,gBAAAoD,QAAA,KAAA5C,QAAA,EAAA6B,MAAA,CAAA1C,CAAA,GAAAgE,UAAA,EAAA9D,CAAA,EAAAgE,OAAA,EAAA7D,CAAA,oBAAAmD,MAAA,UAAA3B,GAAA,GAAA5B,CAAA,GAAAkC,CAAA,OAAAnC,CAAA;AAAA,SAAAsG,gBAAA1F,CAAA,EAAAP,CAAA,UAAAO,CAAA,YAAAP,CAAA,aAAA0D,SAAA;AAAA,SAAAwC,kBAAAvG,CAAA,EAAAE,CAAA,aAAAD,CAAA,MAAAA,CAAA,GAAAC,CAAA,CAAA4E,MAAA,EAAA7E,CAAA,UAAAM,CAAA,GAAAL,CAAA,CAAAD,CAAA,GAAAM,CAAA,CAAAY,UAAA,GAAAZ,CAAA,CAAAY,UAAA,QAAAZ,CAAA,CAAAa,YAAA,kBAAAb,CAAA,KAAAA,CAAA,CAAAc,QAAA,QAAAlB,MAAA,CAAAK,cAAA,CAAAR,CAAA,EAAAwG,cAAA,CAAAjG,CAAA,CAAAkG,GAAA,GAAAlG,CAAA;AAAA,SAAAmG,aAAA1G,CAAA,EAAAE,CAAA,EAAAD,CAAA,WAAAC,CAAA,IAAAqG,iBAAA,CAAAvG,CAAA,CAAAI,SAAA,EAAAF,CAAA,GAAAD,CAAA,IAAAsG,iBAAA,CAAAvG,CAAA,EAAAC,CAAA,GAAAE,MAAA,CAAAK,cAAA,CAAAR,CAAA,iBAAAqB,QAAA,SAAArB,CAAA;AAAA,SAAAwG,eAAAvG,CAAA,QAAAS,CAAA,GAAAiG,YAAA,CAAA1G,CAAA,gCAAAgD,OAAA,CAAAvC,CAAA,IAAAA,CAAA,GAAAA,CAAA;AAAA,SAAAiG,aAAA1G,CAAA,EAAAC,CAAA,oBAAA+C,OAAA,CAAAhD,CAAA,MAAAA,CAAA,SAAAA,CAAA,MAAAD,CAAA,GAAAC,CAAA,CAAAU,MAAA,CAAAiG,WAAA,kBAAA5G,CAAA,QAAAU,CAAA,GAAAV,CAAA,CAAA8B,IAAA,CAAA7B,CAAA,EAAAC,CAAA,gCAAA+C,OAAA,CAAAvC,CAAA,UAAAA,CAAA,YAAAqD,SAAA,yEAAA7D,CAAA,GAAA2G,MAAA,GAAAC,MAAA,EAAA7G,CAAA;AAAA,SAAA8G,mBAAA1G,CAAA,EAAAJ,CAAA,EAAAD,CAAA,EAAAE,CAAA,EAAAK,CAAA,EAAAK,CAAA,EAAAE,CAAA,cAAAJ,CAAA,GAAAL,CAAA,CAAAO,CAAA,EAAAE,CAAA,GAAAE,CAAA,GAAAN,CAAA,CAAAD,KAAA,WAAAJ,CAAA,gBAAAL,CAAA,CAAAK,CAAA,KAAAK,CAAA,CAAA6C,IAAA,GAAAtD,CAAA,CAAAe,CAAA,IAAAwE,OAAA,CAAAtC,OAAA,CAAAlC,CAAA,EAAAoC,IAAA,CAAAlD,CAAA,EAAAK,CAAA;AAAA,SAAAyG,kBAAA3G,CAAA,6BAAAJ,CAAA,SAAAD,CAAA,GAAAiH,SAAA,aAAAzB,OAAA,WAAAtF,CAAA,EAAAK,CAAA,QAAAK,CAAA,GAAAP,CAAA,CAAA6G,KAAA,CAAAjH,CAAA,EAAAD,CAAA,YAAAmH,MAAA9G,CAAA,IAAA0G,kBAAA,CAAAnG,CAAA,EAAAV,CAAA,EAAAK,CAAA,EAAA4G,KAAA,EAAAC,MAAA,UAAA/G,CAAA,cAAA+G,OAAA/G,CAAA,IAAA0G,kBAAA,CAAAnG,CAAA,EAAAV,CAAA,EAAAK,CAAA,EAAA4G,KAAA,EAAAC,MAAA,WAAA/G,CAAA,KAAA8G,KAAA;AAAA,SADeE,aAAaA,CAAA;EAAA,OAAAC,cAAA,CAAAJ,KAAA,OAAAD,SAAA;AAAA;AAAA,SAAAK,eAAA;EAAAA,cAAA,GAAAN,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CAA5B,SAAAoC,SAAA;IAAA,IAAAC,gBAAA,EAAAC,UAAA,EAAAC,UAAA,EAAAC,YAAA,EAAAC,QAAA;IAAA,OAAA7H,mBAAA,GAAAuB,IAAA,UAAAuG,UAAAC,SAAA;MAAA,kBAAAA,SAAA,CAAAlC,IAAA,GAAAkC,SAAA,CAAA7D,IAAA;QAAA;UACI;UACMuD,gBAAgB,GAAGO,KAAK,CAACC,IAAI,CAACC,QAAQ,CAACC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;UAE7ET,UAAU;YACZ,SAAAA,WAAA,EAA+B;cAAA,IAAnBU,aAAa,GAAAlB,SAAA,CAAAnC,MAAA,QAAAmC,SAAA,QAAAmB,SAAA,GAAAnB,SAAA,MAAG,CAAC;cAAAX,eAAA,OAAAmB,UAAA;cACzB,IAAI,CAACY,KAAK,GAAG,EAAE;cACf,IAAI,CAACF,aAAa,GAAGA,aAAa;cAClC,IAAI,CAACG,aAAa,GAAG,CAAC;cACtB,IAAI,CAACC,UAAU,GAAG,CAAC,CAAC,CAAC;cACrB,IAAI,CAACC,SAAS,GAAG,IAAI,CAAC,CAAC;YAC3B;YAAC,OAAA9B,YAAA,CAAAe,UAAA;cAAAhB,GAAA;cAAAhG,KAAA;gBAAA,IAAAgI,MAAA,GAAAzB,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CAED,SAAAuD,QAAYC,EAAE;kBAAA,OAAA5I,mBAAA,GAAAuB,IAAA,UAAAsH,SAAAC,QAAA;oBAAA,kBAAAA,QAAA,CAAAjD,IAAA,GAAAiD,QAAA,CAAA5E,IAAA;sBAAA;wBAAA,OAAA4E,QAAA,CAAA/E,MAAA,WACH,IAAI0B,OAAO,CAAC,UAAAtC,OAAO;0BAAA,OAAI4F,UAAU,CAAC5F,OAAO,EAAEyF,EAAE,CAAC;wBAAA,EAAC;sBAAA;sBAAA;wBAAA,OAAAE,QAAA,CAAA9C,IAAA;oBAAA;kBAAA,GAAA2C,OAAA;gBAAA,CACzD;gBAAA,SAFKK,KAAKA,CAAAC,EAAA;kBAAA,OAAAP,MAAA,CAAAvB,KAAA,OAAAD,SAAA;gBAAA;gBAAA,OAAL8B,KAAK;cAAA;YAAA;cAAAtC,GAAA;cAAAhG,KAAA;gBAAA,IAAAwI,eAAA,GAAAjC,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CAIX,SAAA+D,SAAqBC,YAAY;kBAAA,IAAAC,UAAA;oBAAAC,QAAA;oBAAAC,KAAA;oBAAAC,MAAA,GAAAtC,SAAA;kBAAA,OAAAlH,mBAAA,GAAAuB,IAAA,UAAAkI,UAAAC,SAAA;oBAAA,kBAAAA,SAAA,CAAA7D,IAAA,GAAA6D,SAAA,CAAAxF,IAAA;sBAAA;wBAAEmF,UAAU,GAAAG,MAAA,CAAAzE,MAAA,QAAAyE,MAAA,QAAAnB,SAAA,GAAAmB,MAAA,MAAG,CAAC;wBAAAE,SAAA,CAAA7D,IAAA;wBAAA6D,SAAA,CAAAxF,IAAA;wBAAA,OAElByF,KAAK,CAACP,YAAY,CAAC;sBAAA;wBAApCE,QAAQ,GAAAI,SAAA,CAAA9F,IAAA;wBAAA,IACT0F,QAAQ,CAACM,EAAE;0BAAAF,SAAA,CAAAxF,IAAA;0BAAA;wBAAA;wBAAA,MACN,IAAIX,KAAK,wBAAAsG,MAAA,CAAwBP,QAAQ,CAACQ,MAAM,CAAE,CAAC;sBAAA;wBAAAJ,SAAA,CAAAxF,IAAA;wBAAA,OAEhDoF,QAAQ,CAACS,IAAI,CAAC,CAAC;sBAAA;wBAAA,OAAAL,SAAA,CAAA3F,MAAA,WAAA2F,SAAA,CAAA9F,IAAA;sBAAA;wBAAA8F,SAAA,CAAA7D,IAAA;wBAAA6D,SAAA,CAAAM,EAAA,GAAAN,SAAA;wBAAA,MAExBL,UAAU,GAAG,IAAI,CAACb,UAAU;0BAAAkB,SAAA,CAAAxF,IAAA;0BAAA;wBAAA;wBAC5B;wBACMqF,KAAK,GAAG,IAAI,CAACd,SAAS,GAAGwB,IAAI,CAACC,GAAG,CAAC,CAAC,EAAEb,UAAU,CAAC;wBACtDc,OAAO,CAACC,IAAI,UAAAP,MAAA,CAAUR,UAAU,GAAG,CAAC,OAAAQ,MAAA,CAAI,IAAI,CAACrB,UAAU,WAAAqB,MAAA,CAAQT,YAAY,aAAAS,MAAA,CAAUN,KAAK,OAAI,CAAC;wBAACG,SAAA,CAAAxF,IAAA;wBAAA,OAC1F,IAAI,CAAC8E,KAAK,CAACO,KAAK,CAAC;sBAAA;wBAAA,OAAAG,SAAA,CAAA3F,MAAA,WAChB,IAAI,CAACsG,cAAc,CAACjB,YAAY,EAAEC,UAAU,GAAG,CAAC,CAAC;sBAAA;wBAAA,MAAAK,SAAA,CAAAM,EAAA;sBAAA;sBAAA;wBAAA,OAAAN,SAAA,CAAA1D,IAAA;oBAAA;kBAAA,GAAAmD,QAAA;gBAAA,CAInE;gBAAA,SAjBKkB,cAAcA,CAAAC,GAAA;kBAAA,OAAApB,eAAA,CAAA/B,KAAA,OAAAD,SAAA;gBAAA;gBAAA,OAAdmD,cAAc;cAAA;YAAA;cAAA3D,GAAA;cAAAhG,KAAA;gBAAA,IAAA6J,SAAA,GAAAtD,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CAmBpB,SAAAoF,SAAeC,OAAO;kBAAA,IAAAC,KAAA;kBAAA,IAAAC,YAAA,EAAAvB,YAAA;kBAAA,OAAApJ,mBAAA,GAAAuB,IAAA,UAAAqJ,UAAAC,SAAA;oBAAA,kBAAAA,SAAA,CAAAhF,IAAA,GAAAgF,SAAA,CAAA3G,IAAA;sBAAA;wBACZyG,YAAY,GAAGF,OAAO,CAACK,EAAE,CAACC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;wBAClD3B,YAAY,gBAAAS,MAAA,CAAgBc,YAAY;wBAAA,OAAAE,SAAA,CAAA9G,MAAA,WAEvC,IAAI0B,OAAO;0BAAA,IAAAuF,IAAA,GAAA/D,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CAAC,SAAA6F,SAAO9H,OAAO,EAAE+H,MAAM;4BAAA,IAAAC,YAAA;4BAAA,OAAAnL,mBAAA,GAAAuB,IAAA,UAAA6J,UAAAC,SAAA;8BAAA,kBAAAA,SAAA,CAAAxF,IAAA,GAAAwF,SAAA,CAAAnH,IAAA;gCAAA;kCACrC,IAAI;oCACMiH,YAAY,GAAGlE,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CAAC,SAAAkG,SAAA;sCAAA,IAAAC,IAAA,EAAAC,IAAA,EAAAC,OAAA;sCAAA,OAAAzL,mBAAA,GAAAuB,IAAA,UAAAmK,UAAAC,SAAA;wCAAA,kBAAAA,SAAA,CAAA9F,IAAA,GAAA8F,SAAA,CAAAzH,IAAA;0CAAA;4CAAAyH,SAAA,CAAA9F,IAAA;4CAAA8F,SAAA,CAAAzH,IAAA;4CAAA,OAEKwG,KAAI,CAACL,cAAc,CAACjB,YAAY,CAAC;0CAAA;4CAA9CmC,IAAI,GAAAI,SAAA,CAAA/H,IAAA;4CAEV;4CACM4H,IAAI,GAAGtD,QAAQ,CAAC0D,aAAa,CAAC,KAAK,CAAC;4CAC1CJ,IAAI,CAACK,SAAS,GAAGN,IAAI;4CACrBd,OAAO,CAACoB,SAAS,GAAGL,IAAI,CAACK,SAAS;;4CAElC;4CACMJ,OAAO,GAAGD,IAAI,CAACM,oBAAoB,CAAC,QAAQ,CAAC;4CACnD9D,KAAK,CAACC,IAAI,CAACwD,OAAO,CAAC,CAAC3I,OAAO,CAAC,UAAAiJ,SAAS,EAAI;8CACrC,IAAMC,SAAS,GAAG9D,QAAQ,CAAC0D,aAAa,CAAC,QAAQ,CAAC;8CAClD5D,KAAK,CAACC,IAAI,CAAC8D,SAAS,CAACE,UAAU,CAAC,CAACnJ,OAAO,CAAC,UAAAoJ,IAAI,EAAI;gDAC7CF,SAAS,CAACG,YAAY,CAACD,IAAI,CAAC/G,IAAI,EAAE+G,IAAI,CAACxL,KAAK,CAAC;8CACjD,CAAC,CAAC;8CACFsL,SAAS,CAACI,WAAW,GAAGL,SAAS,CAACK,WAAW;8CAC7CL,SAAS,CAACM,UAAU,CAACC,WAAW,CAACP,SAAS,CAAC;8CAC3C7D,QAAQ,CAACqE,IAAI,CAACC,WAAW,CAACR,SAAS,CAAC;4CACxC,CAAC,CAAC;4CAEFtB,KAAI,CAACnC,aAAa,EAAE;4CACpBpF,OAAO,CAAC,CAAC;4CAACwI,SAAA,CAAAzH,IAAA;4CAAA;0CAAA;4CAAAyH,SAAA,CAAA9F,IAAA;4CAAA8F,SAAA,CAAA3B,EAAA,GAAA2B,SAAA;4CAEVxB,OAAO,CAACsC,KAAK,4BAAA5C,MAAA,CAA4BT,YAAY,aAAAS,MAAA,CAAUa,KAAI,CAAClC,UAAU,gBAAAmD,SAAA,CAAA3B,EAAkB,CAAC;4CACjGU,KAAI,CAACnC,aAAa,EAAE;4CACpB2C,MAAM,CAAAS,SAAA,CAAA3B,EAAM,CAAC;0CAAC;0CAAA;4CAAA,OAAA2B,SAAA,CAAA3F,IAAA;wCAAA;sCAAA,GAAAsF,QAAA;oCAAA,CAErB,GAAE,CAAC;oCAEJZ,KAAI,CAACpC,KAAK,CAAC5D,IAAI,CAACyG,YAAY,CAAC;oCAC7BT,KAAI,CAACnC,aAAa,EAAE;kCACxB,CAAC,CAAC,OAAOkE,KAAK,EAAE;oCACZvB,MAAM,CAACuB,KAAK,CAAC;kCACjB;gCAAC;gCAAA;kCAAA,OAAApB,SAAA,CAAArF,IAAA;8BAAA;4BAAA,GAAAiF,QAAA;0BAAA,CACJ;0BAAA,iBAAAyB,GAAA,EAAAC,GAAA;4BAAA,OAAA3B,IAAA,CAAA7D,KAAA,OAAAD,SAAA;0BAAA;wBAAA,IAAC;sBAAA;sBAAA;wBAAA,OAAA2D,SAAA,CAAA7E,IAAA;oBAAA;kBAAA,GAAAwE,QAAA;gBAAA,CACL;gBAAA,SA1CKoC,QAAQA,CAAAC,GAAA;kBAAA,OAAAtC,SAAA,CAAApD,KAAA,OAAAD,SAAA;gBAAA;gBAAA,OAAR0F,QAAQ;cAAA;YAAA;cAAAlG,GAAA;cAAAhG,KAAA;gBAAA,IAAAoM,YAAA,GAAA7F,iBAAA,cAAAjH,mBAAA,GAAAoF,IAAA,CA4Cd,SAAA2H,SAAkBtC,OAAO;kBAAA,OAAAzK,mBAAA,GAAAuB,IAAA,UAAAyL,UAAAC,SAAA;oBAAA,kBAAAA,SAAA,CAAApH,IAAA,GAAAoH,SAAA,CAAA/I,IAAA;sBAAA;wBAAA,MACjB,IAAI,CAACqE,aAAa,GAAG,IAAI,CAACH,aAAa,IAAIqC,OAAO;0BAAAwC,SAAA,CAAA/I,IAAA;0BAAA;wBAAA;wBAAA+I,SAAA,CAAA/I,IAAA;wBAAA,OAC5C,IAAI,CAAC0I,QAAQ,CAACnC,OAAO,CAAC;sBAAA;sBAAA;wBAAA,OAAAwC,SAAA,CAAAjH,IAAA;oBAAA;kBAAA,GAAA+G,QAAA;gBAAA,CAEnC;gBAAA,SAJKG,WAAWA,CAAAC,GAAA;kBAAA,OAAAL,YAAA,CAAA3F,KAAA,OAAAD,SAAA;gBAAA;gBAAA,OAAXgG,WAAW;cAAA;YAAA;UAAA,KAOrB;UACMvF,UAAU,GAAG,IAAID,UAAU,CAAC,CAAC,CAAC;UAChCE,YAAY,GAAG,CAAC;UACdC,QAAQ,GAAGJ,gBAAgB,EAAE;UAEnC;QAAA;UAAA,MACOG,YAAY,GAAGC,QAAQ,CAAC9C,MAAM,IAAI6C,YAAY,GAAG,CAAC;YAAAG,SAAA,CAAA7D,IAAA;YAAA;UAAA;UAAA6D,SAAA,CAAA7D,IAAA;UAAA,OAC/CyD,UAAU,CAACuF,WAAW,CAACrF,QAAQ,CAACD,YAAY,CAAC,CAAC;QAAA;UACpDA,YAAY,EAAE;UAACG,SAAA,CAAA7D,IAAA;UAAA;QAAA;UAAA,MAIZ0D,YAAY,GAAGC,QAAQ,CAAC9C,MAAM;YAAAgD,SAAA,CAAA7D,IAAA;YAAA;UAAA;UAAA6D,SAAA,CAAA7D,IAAA;UAAA,OAE3BuB,OAAO,CAAC2H,IAAI,CAACzF,UAAU,CAACW,KAAK,CAAC;QAAA;UACpC;UACAX,UAAU,CAACW,KAAK,GAAGX,UAAU,CAACW,KAAK,CAAC+E,MAAM,CAAC,UAAA9K,CAAC;YAAA,OAAIA,CAAC,CAACuH,MAAM,KAAK,SAAS;UAAA,EAAC;UACvE;UAAA/B,SAAA,CAAA7D,IAAA;UAAA,OACMyD,UAAU,CAACuF,WAAW,CAACrF,QAAQ,CAACD,YAAY,CAAC,CAAC;QAAA;UACpDA,YAAY,EAAE;UAACG,SAAA,CAAA7D,IAAA;UAAA;QAAA;UAAA6D,SAAA,CAAA7D,IAAA;UAAA,OAIbuB,OAAO,CAAC6H,GAAG,CAAC3F,UAAU,CAACW,KAAK,CAAC;QAAA;QAAA;UAAA,OAAAP,SAAA,CAAA/B,IAAA;MAAA;IAAA,GAAAwB,QAAA;EAAA,CACtC;EAAA,OAAAD,cAAA,CAAAJ,KAAA,OAAAD,SAAA;AAAA;;;AC/GD,IAAMqG,QAAQ,GAAG,SAAS;AAE1B,SAASC,mBAAmBA,CAAA,EAAG;EAC7B;EACA,IAAMC,kBAAkB,GAAGC,4BAA4B,CAAC,CAAC;;EAEzD;EACAC,2BAA2B,CAAC,CAAC;;EAE7B;EACAC,qBAAqB,CAAC,CAAC;;EAEvB;EACA;AACF;AAEA,SAASF,4BAA4BA,CAAA,EAAG;EACtC;EACA,IAAMG,SAAS,GAAG,IAAIC,eAAe,CAACC,MAAM,CAACC,QAAQ,CAACC,MAAM,CAAC;EAC7D,IAAMC,SAAS,GAAGL,SAAS,CAACM,GAAG,CAACZ,QAAQ,CAAC;;EAEzC;EACA,IAAIW,SAAS,EAAE;IACb,IAAME,aAAa,GAAGlG,QAAQ,CAACmG,cAAc,CAACH,SAAS,CAAC;IACxD,IAAIE,aAAa,EAAE;MACjB;MACArF,UAAU,CAAC,YAAM;QACfqF,aAAa,CAACE,cAAc,CAAC,CAAC;QAC9BC,OAAO,CAACC,YAAY,CAAC,IAAI,EAAE,IAAI,MAAA3E,MAAA,CAAMqE,SAAS,CAAE,CAAC;MACnD,CAAC,EAAE,GAAG,CAAC;IACT;IACA,OAAO,IAAI;EACb;;EAEA;EACA,OAAO,KAAK;AACd;AAEA,SAASN,qBAAqBA,CAAA,EAAG;EAC/B;EACA,IAAIa,WAAW,GAAG,KAAK;EACvB,IAAIC,uBAAuB,GAAG,CAAC;EAC/B,IAAIC,aAAa,GAAG,IAAI;;EAExB;EACAZ,MAAM,CAACa,gBAAgB,CAAC,QAAQ,EAAE,YAAW;IAC3CF,uBAAuB,GAAGX,MAAM,CAACc,OAAO;IAExC,IAAI,CAACJ,WAAW,EAAE;MAChBV,MAAM,CAACe,qBAAqB,CAAC,YAAW;QACtC;QACA;QACA,IAAIH,aAAa,EAAE;UACjBA,aAAa,GAAG,KAAK;QACvB,CAAC,MAAM;UACLI,uBAAuB,CAACL,uBAAuB,CAAC;QAClD;QACAD,WAAW,GAAG,KAAK;MACrB,CAAC,CAAC;IACJ;IAEAA,WAAW,GAAG,IAAI;EACpB,CAAC,CAAC;AACJ;;AAEA;AACA,SAASM,uBAAuBA,CAACC,cAAc,EAAE;EAC/C,IAAMC,cAAc,GAAGC,kBAAkB,CAACF,cAAc,CAAC;;EAEzD;EACA,IAAIC,cAAc,IAAIA,cAAc,CAACnE,EAAE,EAAE;IACvC;IACA,IAAIiD,MAAM,CAACC,QAAQ,CAACmB,IAAI,SAAAtF,MAAA,CAASoF,cAAc,CAACnE,EAAE,CAAE,EAAE;MACpDsE,kBAAkB,CAACH,cAAc,CAACnE,EAAE,CAAC;MACrCuE,qBAAqB,CAACJ,cAAc,CAACnE,EAAE,CAAC;IAC1C;EACF;AACF;;AAEA;AACA,SAASoE,kBAAkBA,CAACF,cAAc,EAAE;EAC1C;EACA,IAAMM,eAAe,GAAGtH,KAAK,CAACC,IAAI,CAACC,QAAQ,CAACC,gBAAgB,CAAC,gDAAgD,CAAC,CAAC;;EAE/G;EACA,IAAImH,eAAe,CAACvK,MAAM,KAAK,CAAC,EAAE,OAAO,IAAI;;EAE7C;EACA,IAAIkK,cAAc,GAAG,IAAI;EACzB,IAAIM,eAAe,GAAGC,QAAQ;EAC9B,IAAMC,cAAc,GAAGT,cAAc,GAAGjB,MAAM,CAAC2B,WAAW,GAAG,CAAC;;EAE9D;EACAJ,eAAe,CAACxM,OAAO,CAAC,UAAA6M,OAAO,EAAI;IACjC,IAAMC,UAAU,GAAGD,OAAO,CAACE,qBAAqB,CAAC,CAAC,CAACC,GAAG,GAAGd,cAAc;IACvE,IAAMe,QAAQ,GAAG9F,IAAI,CAAC+F,GAAG,CAACJ,UAAU,GAAGH,cAAc,CAAC;IAEtD,IAAIM,QAAQ,GAAGR,eAAe,EAAE;MAC9BA,eAAe,GAAGQ,QAAQ;MAC1Bd,cAAc,GAAGU,OAAO;IAC1B;EACF,CAAC,CAAC;EAEF,OAAOV,cAAc;AACvB;;AAEA;AACA,SAASG,kBAAkBA,CAACtE,EAAE,EAAE;EAC9ByD,OAAO,CAACC,YAAY,CAAC,IAAI,EAAE,IAAI,MAAA3E,MAAA,CAAMiB,EAAE,CAAE,CAAC;AAC5C;AAEA,SAAS6C,2BAA2BA,CAAA,EAAG;EACrCI,MAAM,CAACa,gBAAgB,CAAC,YAAY,EAAE,YAAM;IAC1C,IAAMqB,SAAS,GAAGlC,MAAM,CAACC,QAAQ,CAACmB,IAAI,CAACpJ,KAAK,CAAC,CAAC,CAAC;IAC/CsJ,qBAAqB,CAACY,SAAS,CAAC;EAClC,CAAC,CAAC;AACJ;AAEA,SAASZ,qBAAqBA,CAACY,SAAS,EAAE;EACxC,IAAMC,YAAY,GAAG,wBAAwB;EAC7CnC,MAAM,CAACoC,MAAM,CAACC,WAAW,CAAC;IAAEC,WAAW,KAAAxG,MAAA,CAAK0D,QAAQ,OAAA1D,MAAA,CAAIoG,SAAS;EAAG,CAAC,EAAEC,YAAY,CAAC;AACtF;;;ACzHA;AACgD;AACW;AAE3DhI,QAAQ,CAAC0G,gBAAgB,CAAC,kBAAkB,EAAE,YAAM;EAChDzE,OAAO,CAACmG,GAAG,CAAC,kBAAkB,CAAC;EAC/BhJ,aAAa,CAAC,CAAC;EACfiJ,gBAAgB,CAAC,CAAC;EAClB/C,mBAAmB,CAAC,CAAC;AACzB,CAAC,EAAE;EAAEgD,IAAI,EAAE;AAAK,CAAC,CAAC,C","sources":["webpack://blogpost/./src/fragmentLoader.js","webpack://blogpost/./src/syncHFSpacesURLHash.js","webpack://blogpost/./src/index.js"],"sourcesContent":["async function loadFragments() {\n // Find all elements with ids starting with 'fragment-'\n const fragmentElements = Array.from(document.querySelectorAll('[id^=\"fragment-\"]'));\n \n class FetchQueue {\n constructor(maxConcurrent = 3) {\n this.queue = [];\n this.maxConcurrent = maxConcurrent;\n this.activeFetches = 0;\n this.maxRetries = 3; // Maximum number of retry attempts\n this.baseDelay = 1000; // Base delay in milliseconds (1 second)\n }\n\n async sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n async fetchWithRetry(fragmentPath, retryCount = 0) {\n try {\n const response = await fetch(fragmentPath);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n return await response.text();\n } catch (error) {\n if (retryCount < this.maxRetries) {\n // Exponential backoff: 1s, 2s, 4s\n const delay = this.baseDelay * Math.pow(2, retryCount);\n console.warn(`Retry ${retryCount + 1}/${this.maxRetries} for ${fragmentPath} after ${delay}ms`);\n await this.sleep(delay);\n return this.fetchWithRetry(fragmentPath, retryCount + 1);\n }\n throw error;\n }\n }\n\n async addFetch(element) {\n const fragmentName = element.id.replace('fragment-', '');\n const fragmentPath = `fragments/${fragmentName}.html`;\n \n return new Promise(async (resolve, reject) => {\n try {\n const fetchPromise = (async () => {\n try {\n const html = await this.fetchWithRetry(fragmentPath);\n \n // Process the fragment\n const temp = document.createElement('div');\n temp.innerHTML = html;\n element.innerHTML = temp.innerHTML;\n \n // Handle scripts\n const scripts = temp.getElementsByTagName('script');\n Array.from(scripts).forEach(oldScript => {\n const newScript = document.createElement('script');\n Array.from(oldScript.attributes).forEach(attr => {\n newScript.setAttribute(attr.name, attr.value);\n });\n newScript.textContent = oldScript.textContent;\n oldScript.parentNode.removeChild(oldScript);\n document.body.appendChild(newScript);\n });\n \n this.activeFetches--;\n resolve();\n } catch (error) {\n console.error(`Failed to load fragment ${fragmentPath} after ${this.maxRetries} retries:`, error);\n this.activeFetches--;\n reject(error);\n }\n })();\n\n this.queue.push(fetchPromise);\n this.activeFetches++;\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async processNext(element) {\n if (this.activeFetches < this.maxConcurrent && element) {\n await this.addFetch(element);\n }\n }\n }\n\n // Initialize queue\n const fetchQueue = new FetchQueue(3);\n let currentIndex = 0;\n const elements = fragmentElements; // Assuming this is defined elsewhere\n\n // Initial loading of first 3 elements\n while (currentIndex < elements.length && currentIndex < 3) {\n await fetchQueue.processNext(elements[currentIndex]);\n currentIndex++;\n }\n\n // Process remaining elements as fetches complete\n while (currentIndex < elements.length) {\n // Wait for any fetch to complete\n await Promise.race(fetchQueue.queue);\n // Remove completed fetch from queue\n fetchQueue.queue = fetchQueue.queue.filter(p => p.status === 'pending');\n // Add next element to queue\n await fetchQueue.processNext(elements[currentIndex]);\n currentIndex++;\n }\n\n // Wait for remaining fetches to complete\n await Promise.all(fetchQueue.queue);\n}\n\nexport { loadFragments }","const queryArg = \"section\";\n\nfunction syncHFSpacesURLHash() {\n // Handle explicit section requests (don't update hash automatically on load)\n const hasExplicitRequest = handleExplicitSectionRequest();\n \n // Set up hash change monitoring\n updateHashBasedOnHashChange();\n \n // Always set up scroll monitoring to update hash during scrolling\n setupScrollMonitoring();\n \n // If no explicit request, we don't update the hash on initial load\n // The hash will only start updating when the user scrolls\n}\n\nfunction handleExplicitSectionRequest() {\n // Check for section parameter in URL\n const urlParams = new URLSearchParams(window.location.search);\n const sectionId = urlParams.get(queryArg);\n \n // If we have an explicit section request\n if (sectionId) {\n const targetElement = document.getElementById(sectionId);\n if (targetElement) {\n // Slight delay to ensure the browser doesn't try to do its own scrolling first\n setTimeout(() => {\n targetElement.scrollIntoView();\n history.replaceState(null, null, `#${sectionId}`);\n }, 100);\n }\n return true;\n }\n \n // No explicit section parameter found\n return false;\n}\n\nfunction setupScrollMonitoring() {\n // Variables to manage throttling\n let isScrolling = false;\n let lastKnownScrollPosition = 0;\n let initialScroll = true;\n \n // Add the scroll event listener\n window.addEventListener('scroll', function() {\n lastKnownScrollPosition = window.scrollY;\n \n if (!isScrolling) {\n window.requestAnimationFrame(function() {\n // Skip the first scroll event which might be browser's automatic scroll\n // to a hash on page load\n if (initialScroll) {\n initialScroll = false;\n } else {\n updateHashBasedOnScroll(lastKnownScrollPosition);\n }\n isScrolling = false;\n });\n }\n \n isScrolling = true;\n });\n}\n\n// Function to update the URL hash based on scroll position\nfunction updateHashBasedOnScroll(scrollPosition) {\n const closestHeading = findClosestHeading(scrollPosition);\n \n // Update the URL hash if we found a closest element\n if (closestHeading && closestHeading.id) {\n // Only update if the hash is different to avoid unnecessary operations\n if (window.location.hash !== `#${closestHeading.id}`) {\n silentlyUpdateHash(closestHeading.id);\n postMessageToHFSpaces(closestHeading.id);\n }\n }\n}\n\n// Find the closest heading to the current scroll position\nfunction findClosestHeading(scrollPosition) {\n // Get only heading elements with IDs that we want to track\n const headingsWithIds = Array.from(document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'));\n\n // Skip if there are no headings with IDs\n if (headingsWithIds.length === 0) return null;\n\n // Find the element closest to the middle of the viewport\n let closestHeading = null;\n let closestDistance = Infinity;\n const viewportMiddle = scrollPosition + window.innerHeight / 2;\n \n // Iterate through all headings to find the closest one\n headingsWithIds.forEach(heading => {\n const headingTop = heading.getBoundingClientRect().top + scrollPosition;\n const distance = Math.abs(headingTop - viewportMiddle);\n \n if (distance < closestDistance) {\n closestDistance = distance;\n closestHeading = heading;\n }\n });\n \n return closestHeading;\n}\n\n// Update hash without triggering scroll or other side effects\nfunction silentlyUpdateHash(id) {\n history.replaceState(null, null, `#${id}`);\n}\n\nfunction updateHashBasedOnHashChange() {\n window.addEventListener('hashchange', () => {\n const elementId = window.location.hash.slice(1);\n postMessageToHFSpaces(elementId);\n });\n}\n\nfunction postMessageToHFSpaces(elementId) {\n const parentOrigin = \"https://huggingface.co\";\n window.parent.postMessage({ queryString: `${queryArg}=${elementId}` }, parentOrigin);\n}\n\nexport { syncHFSpacesURLHash };\n","// import { plotClusters } from './clusters'\nimport { loadFragments } from './fragmentLoader'\nimport { syncHFSpacesURLHash } from './syncHFSpacesURLHash'\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n console.log(\"DOMContentLoaded\");\n loadFragments();\n init_memory_plot();\n syncHFSpacesURLHash();\n}, { once: true });\n"],"names":["_regeneratorRuntime","e","t","r","Object","prototype","n","hasOwnProperty","o","defineProperty","value","i","Symbol","a","iterator","c","asyncIterator","u","toStringTag","define","enumerable","configurable","writable","wrap","Generator","create","Context","makeInvokeMethod","tryCatch","type","arg","call","h","l","f","s","y","GeneratorFunction","GeneratorFunctionPrototype","p","d","getPrototypeOf","v","values","g","defineIteratorMethods","forEach","_invoke","AsyncIterator","invoke","_typeof","resolve","__await","then","callInvokeWithMethodAndArg","Error","done","method","delegate","maybeInvokeDelegate","sent","_sent","dispatchException","abrupt","TypeError","resultName","next","nextLoc","pushTryEntry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","isNaN","length","displayName","isGeneratorFunction","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","keys","reverse","pop","prev","charAt","slice","stop","rval","handle","complete","finish","_catch","delegateYield","_classCallCheck","_defineProperties","_toPropertyKey","key","_createClass","_toPrimitive","toPrimitive","String","Number","asyncGeneratorStep","_asyncToGenerator","arguments","apply","_next","_throw","loadFragments","_loadFragments","_callee7","fragmentElements","FetchQueue","fetchQueue","currentIndex","elements","_callee7$","_context7","Array","from","document","querySelectorAll","maxConcurrent","undefined","queue","activeFetches","maxRetries","baseDelay","_sleep","_callee","ms","_callee$","_context","setTimeout","sleep","_x","_fetchWithRetry","_callee2","fragmentPath","retryCount","response","delay","_args2","_callee2$","_context2","fetch","ok","concat","status","text","t0","Math","pow","console","warn","fetchWithRetry","_x2","_addFetch","_callee5","element","_this","fragmentName","_callee5$","_context5","id","replace","_ref","_callee4","reject","fetchPromise","_callee4$","_context4","_callee3","html","temp","scripts","_callee3$","_context3","createElement","innerHTML","getElementsByTagName","oldScript","newScript","attributes","attr","setAttribute","textContent","parentNode","removeChild","body","appendChild","error","_x4","_x5","addFetch","_x3","_processNext","_callee6","_callee6$","_context6","processNext","_x6","race","filter","all","queryArg","syncHFSpacesURLHash","hasExplicitRequest","handleExplicitSectionRequest","updateHashBasedOnHashChange","setupScrollMonitoring","urlParams","URLSearchParams","window","location","search","sectionId","get","targetElement","getElementById","scrollIntoView","history","replaceState","isScrolling","lastKnownScrollPosition","initialScroll","addEventListener","scrollY","requestAnimationFrame","updateHashBasedOnScroll","scrollPosition","closestHeading","findClosestHeading","hash","silentlyUpdateHash","postMessageToHFSpaces","headingsWithIds","closestDistance","Infinity","viewportMiddle","innerHeight","heading","headingTop","getBoundingClientRect","top","distance","abs","elementId","parentOrigin","parent","postMessage","queryString","log","init_memory_plot","once"],"sourceRoot":""}
dist/style.css ADDED
@@ -0,0 +1,599 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* style.css */
2
+ /* Define colors */
3
+ :root {
4
+ --distill-gray: rgb(107, 114, 128);
5
+ --distill-gray-light: rgb(185, 185, 185);
6
+ --distill-gray-lighter: rgb(228, 228, 228);
7
+ --distill-gray-lightest: rgb(245, 245, 245);
8
+ --distill-blue: #007BFF;
9
+ }
10
+
11
+ /* Container for the controls */
12
+ [id^="plot-"] {
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: center;
16
+ gap: 15px; /* Adjust the gap between controls as needed */
17
+ }
18
+ [id^="plot-"] figure {
19
+ margin-bottom: 0px;
20
+ margin-top: 0px;
21
+ padding: 0px;
22
+ }
23
+ .plotly_caption {
24
+ font-style: italic;
25
+ margin-top: 10px;
26
+ }
27
+
28
+ .plotly_controls {
29
+ display: flex;
30
+ flex-wrap: wrap;
31
+ flex-direction: row;
32
+ justify-content: center;
33
+ align-items: flex-start;
34
+ gap: 30px;
35
+ }
36
+
37
+
38
+ .plotly_input_container {
39
+ display: flex;
40
+ align-items: center;
41
+ flex-direction: column;
42
+ gap: 10px;
43
+ }
44
+
45
+ /* Style for the select dropdown */
46
+ .plotly_input_container > select {
47
+ padding: 2px 4px;
48
+ /* border: 1px solid #ccc; */
49
+ line-height: 1.5em;
50
+ text-align: center;
51
+ border-radius: 4px;
52
+ font-size: 12px;
53
+ background-color: var(--distill-gray-lightest);
54
+ outline: none;
55
+ }
56
+
57
+ /* Style for the range input */
58
+
59
+ .plotly_slider {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 10px;
63
+ }
64
+
65
+ .plotly_slider > input[type="range"] {
66
+ -webkit-appearance: none;
67
+ height: 2px;
68
+ background: var(--distill-gray-light);
69
+ border-radius: 5px;
70
+ outline: none;
71
+ }
72
+
73
+ .plotly_slider > span {
74
+ font-size: 14px;
75
+ line-height: 1.6em;
76
+ min-width: 16px;
77
+ }
78
+
79
+ .plotly_slider > input[type="range"]::-webkit-slider-thumb {
80
+ -webkit-appearance: none;
81
+ appearance: none;
82
+ width: 18px;
83
+ height: 18px;
84
+ border-radius: 50%;
85
+ background: var(--distill-blue);
86
+ cursor: pointer;
87
+ }
88
+
89
+ .plotly_slider > input[type="range"]::-moz-range-thumb {
90
+ width: 18px;
91
+ height: 18px;
92
+ border-radius: 50%;
93
+ background: var(--distill-blue);
94
+ cursor: pointer;
95
+ }
96
+
97
+ /* Style for the labels */
98
+ .plotly_input_container > label {
99
+ font-size: 14px;
100
+ font-weight: bold;
101
+ }
102
+
103
+ .main-plot-container {
104
+ margin-top: 21px;
105
+ margin-bottom: 35px;
106
+ }
107
+
108
+ .main-plot-container > figure {
109
+ display: block !important;
110
+ /* Let this be handled by graph-container */
111
+ margin-bottom: 0px;
112
+ margin-top: 0px;
113
+ }
114
+ .main-plot-container > div {
115
+ display: none !important;
116
+ }
117
+
118
+
119
+ @media (min-width: 768px) {
120
+ .main-plot-container > figure {
121
+ display: none !important;
122
+ }
123
+ .main-plot-container > div {
124
+ display: flex !important;
125
+ }
126
+ }
127
+
128
+ d-byline .byline {
129
+ grid-template-columns: 1fr;
130
+ grid-column: text;
131
+ font-size: 0.9rem;
132
+ line-height: 1.8em;
133
+ }
134
+
135
+ @media (min-width: 768px) {
136
+ d-byline .byline {
137
+ grid-template-columns: 5fr 1fr 1fr;
138
+ }
139
+ }
140
+
141
+ #title-plot {
142
+ margin-top: 0px;
143
+ margin-bottom: 0px;
144
+ }
145
+
146
+ d-contents > nav a.active {
147
+ text-decoration: underline;
148
+ }
149
+
150
+ @media (max-width: 1199px) {
151
+ d-contents {
152
+ display: none;
153
+ background: white;
154
+ justify-self: start;
155
+ align-self: start;
156
+ padding-bottom: 0.5em;
157
+ margin-bottom: 1em;
158
+ padding-left: 0.25em;
159
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
160
+ border-bottom-width: 1px;
161
+ border-bottom-style: solid;
162
+ border-bottom-color: rgba(0, 0, 0, 0.1);
163
+ overflow-y: scroll;
164
+ height: calc(100vh - 40px);
165
+ scrollbar-width: none;
166
+ z-index: -100;
167
+ }
168
+ }
169
+
170
+ d-contents a:hover {
171
+ border-bottom: none;
172
+ }
173
+
174
+ toc-title {
175
+ font-weight: bold;
176
+ font-size: 1.2em;
177
+ color: #333;
178
+ }
179
+
180
+ toggle-icon {
181
+ transition: transform 0.3s;
182
+ }
183
+
184
+ toggle-icon.collapsed {
185
+ transform: rotate(90deg);
186
+ }
187
+
188
+ .toc-content {
189
+ margin-top: 15px;
190
+ overflow: hidden;
191
+ /* max-height: 1000px; */
192
+ transition: max-height 0.3s ease-out;
193
+ }
194
+
195
+ .toc-content.collapsed {
196
+ max-height: 0;
197
+ margin-top: 0;
198
+ }
199
+
200
+ @media (min-width: 1200px) {
201
+ d-article {
202
+ /* Ensure d-article does not prevent sticky positioning */
203
+ overflow: visible;
204
+ }
205
+
206
+ d-contents {
207
+ align-self: start;
208
+ background: white;
209
+ grid-column-start: 1 !important;
210
+ grid-column-end: 4 !important;
211
+ grid-row: auto / span 6;
212
+ justify-self: end;
213
+ margin-top: 0em;
214
+ padding-right: 3em;
215
+ padding-left: 2em;
216
+ /* border-right: 1px solid rgba(0, 0, 0, 0.1);
217
+ border-right-width: 1px;
218
+ border-right-style: solid;
219
+ border-right-color: rgba(0, 0, 0, 0.1); */
220
+ position: -webkit-sticky; /* For Safari */
221
+ position: sticky;
222
+ top: 10px; /* Adjust this value if needed */
223
+ overflow-y: auto;
224
+ height: calc(100vh - 40px);
225
+ scrollbar-width: none;
226
+ transition: max-height 0.3s ease-out;
227
+ z-index: -100;
228
+ }
229
+ }
230
+
231
+ d-contents nav h3 {
232
+ margin-top: 0;
233
+ margin-bottom: 1em;
234
+ }
235
+
236
+ d-contents nav div div {
237
+ color: rgba(0, 0, 0, 0.8);
238
+ font-weight: bold;
239
+ }
240
+
241
+ d-contents nav a {
242
+ color: rgba(0, 0, 0, 0.8);
243
+ border-bottom: none;
244
+ text-decoration: none;
245
+ }
246
+
247
+ d-contents li {
248
+ list-style-type: none;
249
+ }
250
+
251
+ d-contents ul, d-article d-contents ul {
252
+ padding-left: 1em;
253
+ }
254
+
255
+ d-contents nav ul li {
256
+ margin-bottom: .25em;
257
+ }
258
+
259
+ d-contents nav a:hover {
260
+ text-decoration: underline solid rgba(0, 0, 0, 0.6);
261
+ }
262
+
263
+ d-contents nav ul {
264
+ margin-top: 0;
265
+ margin-bottom: 6px;
266
+ }
267
+
268
+
269
+ d-contents nav > div {
270
+ display: block;
271
+ outline: none;
272
+ margin-bottom: 0.5em;
273
+ }
274
+
275
+ d-contents nav > div > a {
276
+ font-size: 13px;
277
+ font-weight: 600;
278
+ }
279
+
280
+ d-article aside {
281
+ margin-bottom: 1em;
282
+ }
283
+
284
+ d-article img {
285
+ max-width: 100%;
286
+ }
287
+
288
+ @media (min-width: 768px) {
289
+ d-article aside {
290
+ margin-bottom: 0;
291
+ }
292
+ }
293
+
294
+ d-contents nav > div > a:hover,
295
+ d-contents nav > ul > li > a:hover {
296
+ text-decoration: none;
297
+ }
298
+
299
+ .note-box {
300
+ background-color: #f6f8fa;
301
+ border-left: 4px solid #444444;
302
+ padding: 1rem;
303
+ margin: 1rem 0; /* Keep this modest margin */
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 {
311
+ margin: 0;
312
+ color: #444444;
313
+ font-weight: 600;
314
+ font-size: 1em;
315
+ }
316
+
317
+ .note-box-content {
318
+ margin-top: 0.5rem;
319
+ margin-bottom: 0; /* Ensure no bottom margin */
320
+ color: #24292f;
321
+ font-size: 0.9em;
322
+ line-height: 1.5em;
323
+ }
324
+
325
+ /* For dark mode support */
326
+ @media (prefers-color-scheme: dark) {
327
+ .note-box {
328
+ background-color: #1c1c1c;
329
+ border-left-color: #888888;
330
+ }
331
+ .note-box-title {
332
+ color: #888888;
333
+ }
334
+ .note-box-content {
335
+ color: #d4d4d4;
336
+ }
337
+ }
338
+
339
+ d-article {
340
+ font-size: 1.0em;
341
+ }
342
+
343
+ .figure-legend {
344
+ font-size: 0.9em;
345
+ font-style: italic;
346
+ color: var(--distill-gray);
347
+ line-height: 1.5em;
348
+ }
349
+
350
+ d-code {
351
+ font-size: 12px;
352
+ }
353
+
354
+ .large-image-background {
355
+ width: 100vw;
356
+ padding-top: 10px;
357
+ padding-bottom: 10px;
358
+ margin-left: calc(-50vw + 50%);
359
+ margin-right: calc(-50vw + 50%);
360
+ background: white;
361
+ height: fit-content; /* This will make it match the image height */
362
+ display: flex;
363
+ justify-content: center; /* This will center your image */
364
+ }
365
+
366
+ .large-image-background-transparent {
367
+ /* width: 100vw; */
368
+ padding-top: 10px;
369
+ padding-bottom: 10px;
370
+ /* margin-left: calc(-50vw + 50%); */
371
+ margin-left:-100px;
372
+ margin-right: -100px;
373
+ /* margin-right: calc(-50vw + 50%); */
374
+ /* background: white; */
375
+ height: fit-content; /* This will make it match the image height */
376
+ display: flex;
377
+ justify-content: center; /* This will center your image */
378
+ }
379
+
380
+ .boxed-image {
381
+ padding: 0.5rem;
382
+ background: white;
383
+ border-radius: 12px;
384
+ border: 1px solid #e5e7eb;
385
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
386
+ }
387
+
388
+ d-article li {
389
+ margin-bottom: 0.0em;
390
+ }
391
+
392
+ d-article ul ul {
393
+ margin-bottom: 0.0em;
394
+ }
395
+
396
+ d-article ol ol {
397
+ margin-bottom: 0.0em;
398
+ }
399
+
400
+ d-article hr {
401
+ grid-column: text;
402
+ }
403
+
404
+ /* Memory visualization */
405
+ #graph-all {
406
+ min-width: 500px;
407
+ margin-right: 10px;
408
+ margin-bottom: 2rem;
409
+ padding: 0.5rem;
410
+ background: #f9fafb;
411
+ border-radius: 12px;
412
+ border: 1px solid #e5e7eb;
413
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
414
+ }
415
+
416
+
417
+ /* Main container styles */
418
+ #controls {
419
+ max-width: 1200px;
420
+ /* margin: 2rem auto; */
421
+ margin-bottom: 2rem;
422
+ margin-left: 10px;
423
+ padding: 0.6rem;
424
+ background: #f9fafb;
425
+ border-radius: 12px;
426
+ border: 1px solid #e5e7eb;
427
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
428
+ }
429
+
430
+ /* Grid layout */
431
+ #controls {
432
+ display: grid;
433
+ grid-template-columns: 1fr 1fr;
434
+ /* gap: 2rem; */
435
+ }
436
+
437
+ /* Cell styles */
438
+ .cell {
439
+ margin-bottom: 0.2rem;
440
+ }
441
+
442
+ /* Label styles */
443
+ label {
444
+ display: block;
445
+ /* margin-bottom: 0.5rem; */
446
+ font-size: 0.8rem;
447
+ font-weight: 500;
448
+ color: #374151;
449
+ }
450
+
451
+ /* Input container for range + number combination */
452
+ .input-container {
453
+ display: flex;
454
+ gap: 1rem;
455
+ align-items: center;
456
+ }
457
+
458
+ /* Range input styling */
459
+ input[type="range"] {
460
+ flex: 1;
461
+ height: 6px;
462
+ background: #e5e7eb;
463
+ border-radius: 3px;
464
+ appearance: none;
465
+ outline: none;
466
+ }
467
+
468
+ input[type="range"]::-webkit-slider-thumb {
469
+ appearance: none;
470
+ width: 16px;
471
+ height: 16px;
472
+ background: #3b82f6;
473
+ border-radius: 50%;
474
+ cursor: pointer;
475
+ transition: background 0.15s ease;
476
+ }
477
+
478
+ input[type="range"]::-webkit-slider-thumb:hover {
479
+ background: #2563eb;
480
+ }
481
+
482
+ /* Number input styling */
483
+ input[type="number"] {
484
+ width: 80px;
485
+ padding: 0.5rem;
486
+ border: 1px solid #e5e7eb;
487
+ border-radius: 6px;
488
+ font-size: 0.9rem;
489
+ color: #374151;
490
+ }
491
+
492
+ /* Select styling */
493
+ select {
494
+ width: 100%;
495
+ padding: 0.5rem;
496
+ border: 1px solid #e5e7eb;
497
+ border-radius: 6px;
498
+ background: white;
499
+ font-size: 0.9rem;
500
+ color: #374151;
501
+ cursor: pointer;
502
+ }
503
+
504
+ /* Checkbox styling */
505
+ input[type="checkbox"] {
506
+ width: 1.2rem;
507
+ height: 1.2rem;
508
+ margin-right: 0.5rem;
509
+ border: 2px solid #e5e7eb;
510
+ border-radius: 4px;
511
+ cursor: pointer;
512
+ }
513
+
514
+ /* Column specific styles */
515
+ .column-1 {
516
+ padding-right: 0.5rem;
517
+ }
518
+
519
+ .column-2 {
520
+ padding-left: 0.5rem;
521
+ }
522
+
523
+ /* Checkbox container */
524
+ .checkbox-container {
525
+ display: flex;
526
+ align-items: center;
527
+ margin-bottom: 1rem;
528
+ }
529
+
530
+ /* Memory visualization styles */
531
+ .memory-block {
532
+ background: #fff;
533
+ border-radius: 8px;
534
+ padding: 1rem;
535
+ margin-bottom: 1rem;
536
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
537
+ }
538
+
539
+ .memory-title {
540
+ font-size: 1.1rem;
541
+ font-weight: 500;
542
+ color: #374151;
543
+ margin-bottom: 0.5rem;
544
+ }
545
+
546
+ .memory-value {
547
+ font-size: 1.5rem;
548
+ font-weight: 600;
549
+ color: #3b82f6;
550
+ }
551
+
552
+ /* Responsive adjustments */
553
+ @media (max-width: 768px) {
554
+ #controls {
555
+ grid-template-columns: 1fr;
556
+ padding: 1rem;
557
+ }
558
+
559
+ .column-1, .column-2 {
560
+ padding: 0;
561
+ }
562
+ }
563
+
564
+ /* Hover states and transitions */
565
+ input:hover, select:hover {
566
+ border-color: #3b82f6;
567
+ }
568
+
569
+ input:focus, select:focus {
570
+ border-color: #2563eb;
571
+ outline: none;
572
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
573
+ }
574
+
575
+ /* Add smooth transitions */
576
+ input, select, button {
577
+ transition: all 0.15s ease;
578
+ }
579
+
580
+ /* Preset dropdown special styling */
581
+ select[name="presets"] {
582
+ background-color: #f3f4f6;
583
+ font-weight: 500;
584
+ }
585
+
586
+ /* Memory graph enhancements */
587
+ .activation-memory {
588
+ background: #dbeafe;
589
+ padding: 1rem;
590
+ border-radius: 8px;
591
+ margin-bottom: 1rem;
592
+ }
593
+
594
+ .gradient-memory {
595
+ background: #ede9fe;
596
+ padding: 1rem;
597
+ border-radius: 8px;
598
+ }
599
+
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "dependencies": {
3
+ "d3": "^7.9.0",
4
+ "katex": "^0.16.11",
5
+ "lodash": "^4.17.21",
6
+ "papaparse": "^5.4.1",
7
+ "plotly.js-basic-dist-min": "^2.33.0"
8
+ },
9
+ "name": "blogpost",
10
+ "version": "1.0.0",
11
+ "description": "--- title: \"The Nanotron Gigablogpost\" emoji: 🍷 colorFrom: pink colorTo: red sdk: static pinned: false header: mini ---",
12
+ "main": "index.js",
13
+ "scripts": {
14
+ "dev": "webpack serve --open",
15
+ "build": "NODE_ENV=production webpack"
16
+ },
17
+ "author": "",
18
+ "license": "ISC",
19
+ "devDependencies": {
20
+ "@babel/preset-env": "^7.24.6",
21
+ "@swc/html": "^1.10.17",
22
+ "babel-loader": "^9.1.3",
23
+ "clean-webpack-plugin": "^4.0.0",
24
+ "compression-webpack-plugin": "^11.1.0",
25
+ "copy-webpack-plugin": "^12.0.2",
26
+ "css-loader": "^7.1.2",
27
+ "fs": "^0.0.1-security",
28
+ "handlebars": "^4.7.8",
29
+ "html-minimizer-webpack-plugin": "^5.0.0",
30
+ "html-webpack-change-assets-extension-plugin": "^1.3.1",
31
+ "html-webpack-plugin": "^5.6.0",
32
+ "image-minimizer-webpack-plugin": "^4.1.3",
33
+ "sharp": "^0.33.5",
34
+ "style-loader": "^4.0.0",
35
+ "svgo": "^3.3.2",
36
+ "webpack": "^5.91.0",
37
+ "webpack-bundle-analyzer": "^4.10.2",
38
+ "webpack-cli": "^5.1.4",
39
+ "webpack-dev-server": "^5.0.4"
40
+ }
41
+ }
python/memory/formulas/__init__.py ADDED
File without changes
python/memory/formulas/explorations.ipynb ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "from utils import activation_memory, param_grads_opt"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": 48,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "def activation_memory(\n",
19
+ " a, # attention heads\n",
20
+ " b, # micro batch size\n",
21
+ " h, # hidden dimension size\n",
22
+ " h_ff, # feedforward dimension size (often h_ff = 4h)\n",
23
+ " L, # number of layers\n",
24
+ " s, # sequence length\n",
25
+ " mixed=True,\n",
26
+ " recomputation=\"none\",\n",
27
+ " ff_activation=\"relu\"\n",
28
+ " ):\n",
29
+ " \n",
30
+ " # https://arxiv.org/pdf/2205.05198\n",
31
+ " if mixed:\n",
32
+ " bytes_per_value = 2 \n",
33
+ " else:\n",
34
+ " bytes_per_value = 4\n",
35
+ "\n",
36
+ " one_layer_attention = s * b * h * (bytes_per_value * 5 + 1) + ((2 * bytes_per_value + 1) * a * s * s * b) # eq (2)\n",
37
+ "\n",
38
+ " if ff_activation == \"relu\":\n",
39
+ " one_layer_feedforward = (s * b * h * bytes_per_value + (s * b * h_ff * bytes_per_value) # inputs of 1st/2nd linear layers\n",
40
+ " + s * b * h) # dropout\n",
41
+ " elif ff_activation == \"gelu\":\n",
42
+ " one_layer_feedforward = (s * b * h * bytes_per_value + (s * b * h_ff * bytes_per_value) # inputs of 1st/2nd linear layers\n",
43
+ " + s * b * h_ff * bytes_per_value # inputs of activation function (not really necessary for Relu)\n",
44
+ " + s * b * h) # dropout\n",
45
+ " elif ff_activation == \"swiglu\":\n",
46
+ " one_layer_feedforward = (s * b * h * bytes_per_value + (s * b * h_ff * bytes_per_value) # inputs of input/output linear layers\n",
47
+ " + s * b * h_ff * bytes_per_value * 3 # inputs of activation function\n",
48
+ " + s * b * h) # dropout (note that dropout is lower-precision - boolean)\n",
49
+ "\n",
50
+ "\n",
51
+ " layer_norm = s * b * h * bytes_per_value\n",
52
+ "\n",
53
+ " if recomputation == \"none\":\n",
54
+ " one_layer = one_layer_attention + one_layer_feedforward + 2 * layer_norm # eq (2)\n",
55
+ " elif recomputation ==\"selective\":\n",
56
+ " one_layer = s * b * h * 34 # eq (6)\n",
57
+ " elif recomputation ==\"full\":\n",
58
+ " one_layer = s * b * h * 2\n",
59
+ " else:\n",
60
+ " raise ValueError()\n",
61
+ " \n",
62
+ " input_dropout = s * b * h # section 4.3\n",
63
+ "\n",
64
+ " total = L * one_layer + input_dropout\n",
65
+ " \n",
66
+ " return total\n",
67
+ "\n",
68
+ "\n"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": 51,
74
+ "metadata": {},
75
+ "outputs": [],
76
+ "source": [
77
+ "a = 16\n",
78
+ "b = 3\n",
79
+ "h = 1024\n",
80
+ "h_ff = 4 * h\n",
81
+ "L = 1\n",
82
+ "s = 7 # 128000\n",
83
+ "recomputation = \"none\"\n",
84
+ "mixed = True\n",
85
+ "ff_activation = \"swiglu\"\n"
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": 52,
91
+ "metadata": {},
92
+ "outputs": [
93
+ {
94
+ "data": {
95
+ "text/plain": [
96
+ "1086960"
97
+ ]
98
+ },
99
+ "execution_count": 52,
100
+ "metadata": {},
101
+ "output_type": "execute_result"
102
+ }
103
+ ],
104
+ "source": [
105
+ "activation_memory(a=a, b=b, h=h, h_ff=h_ff, L=L, s=s, recomputation=recomputation, mixed=mixed, ff_activation=ff_activation)"
106
+ ]
107
+ },
108
+ {
109
+ "cell_type": "code",
110
+ "execution_count": 18,
111
+ "metadata": {},
112
+ "outputs": [],
113
+ "source": [
114
+ "from math import log\n",
115
+ "\n",
116
+ "def format_bytes(bytes):\n",
117
+ " sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']\n",
118
+ " if bytes == 0:\n",
119
+ " return '0 Bytes'\n",
120
+ " i = int(log(bytes, 1024))\n",
121
+ " print(i)\n",
122
+ " p = 1024 ** i\n",
123
+ " s = round(bytes / p, 2)\n",
124
+ " return f\"{s} {sizes[i]}\"\n",
125
+ "\n"
126
+ ]
127
+ },
128
+ {
129
+ "cell_type": "code",
130
+ "execution_count": 19,
131
+ "metadata": {},
132
+ "outputs": [
133
+ {
134
+ "name": "stdout",
135
+ "output_type": "stream",
136
+ "text": [
137
+ "4\n"
138
+ ]
139
+ },
140
+ {
141
+ "data": {
142
+ "text/plain": [
143
+ "'22.13 TB'"
144
+ ]
145
+ },
146
+ "execution_count": 19,
147
+ "metadata": {},
148
+ "output_type": "execute_result"
149
+ }
150
+ ],
151
+ "source": [
152
+ "format_bytes(activation_memory(a, b, h, L, s, recomputation))"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "code",
157
+ "execution_count": null,
158
+ "metadata": {},
159
+ "outputs": [],
160
+ "source": []
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": null,
165
+ "metadata": {},
166
+ "outputs": [],
167
+ "source": []
168
+ }
169
+ ],
170
+ "metadata": {
171
+ "kernelspec": {
172
+ "display_name": "jupyter",
173
+ "language": "python",
174
+ "name": "python3"
175
+ },
176
+ "language_info": {
177
+ "codemirror_mode": {
178
+ "name": "ipython",
179
+ "version": 3
180
+ },
181
+ "file_extension": ".py",
182
+ "mimetype": "text/x-python",
183
+ "name": "python",
184
+ "nbconvert_exporter": "python",
185
+ "pygments_lexer": "ipython3",
186
+ "version": "3.10.14"
187
+ }
188
+ },
189
+ "nbformat": 4,
190
+ "nbformat_minor": 2
191
+ }
python/memory/formulas/utils.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def activation_memory(
2
+ a, # attention heads
3
+ b, # micro batch size
4
+ h, # hidden dimension size
5
+ h_ff, # feedforward dimension size (often h_ff = 4h)
6
+ L, # number of layers
7
+ s, # sequence length
8
+ mixed=True,
9
+ recomputation="none"
10
+ ):
11
+
12
+ # https://arxiv.org/pdf/2205.05198
13
+ if mixed:
14
+ bytes_per_value = 2
15
+ else:
16
+ bytes_per_value = 4
17
+
18
+ one_layer_attention = s * b * h * (bytes_per_value * 5 + 1) + ((2 * bytes_per_value + 1) * a * s * s * b) # eq (2)
19
+ one_layer_feedforward_mlp = (s * b * h * bytes_per_value + (s * b * h_ff * bytes_per_value) # inputs of 1st/2nd linear layers
20
+ + s * b * h_ff * bytes_per_value # inputs of activation function (not really necessary for Relu though)
21
+ + s * b * h) # dropout
22
+ one_layer_feedforward_swiglu = (s * b * h * bytes_per_value + (s * b * h_ff * bytes_per_value) # inputs of input/output linear layers
23
+ + s * b * h_ff * bytes_per_value * 3 # inputs of activation function
24
+ + s * b * h) # dropout (note that dropout is lower-precision - boolean)
25
+
26
+
27
+ if recomputation == "none":
28
+ one_layer = one_layer_attention # eq (2)
29
+ elif recomputation =="selective":
30
+ one_layer = s * b * h * 34 # eq (6)
31
+ elif recomputation =="full":
32
+ one_layer = s * b * h * 2
33
+ else:
34
+ raise ValueError()
35
+
36
+ input_dropout = 0 # s * b * h # section 4.3
37
+
38
+ total = L * one_layer + input_dropout
39
+
40
+ return total
41
+
42
+
43
+ def param_grads_opt(
44
+ h, # hidden dimension size
45
+ L, # number of layers
46
+ s, # sequence length
47
+ v, # vocab size
48
+ k=8, # parameters for optimizer (Adam: 8 = 4 bytes moments + 4 bytes variance)
49
+ mixed=True # mixed precision training
50
+ ):
51
+
52
+ # https://michaelwornow.net/2024/01/18/counting-params-in-transformer
53
+ # note: this is without GQA or MQA
54
+
55
+ emb = h*(v+s)
56
+ one_layer = 12 * h**2 + 13*h
57
+ other = 2*h
58
+
59
+ n = emb + L * one_layer + other
60
+
61
+ # 3.1 https://arxiv.org/pdf/1910.02054
62
+
63
+ if mixed:
64
+ k += 4 # additional full precision weights
65
+ bytes_per_paramter = 2
66
+ else:
67
+ bytes_per_paramter = 4
68
+
69
+ return bytes_per_paramter*n, bytes_per_paramter*n, k*n
python/memory/measurments/act-mem-2/README.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Activation Memory: Part 2
2
+
3
+ Code accompanying the deep-dive [blog post on activation memory](https://determined.ai/blog/act-mem-2).
4
+
5
+ - The main utility code is in `act_mem.py`.
6
+ - Basic transformer layers are implemented in `layers.py`.
7
+ - The scripts `{block,mlp}_script.py` demonstrate how replacing `GELU` with `ReLU` affects activation
8
+ memory.
9
+ - `attn_script.py` shows the cost of activation memory in the attention layer.
10
+ - Tests of the code are in `test.py`.
11
+ - See `requirements.txt` for versions the code was built against.
12
+
13
+
14
+ ## Contributors
15
+
16
+ - [Garrett Goon](https://github.com/garrett361)
python/memory/measurments/act-mem-2/act_mem.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Iterable, Optional, Union
2
+
3
+ import torch
4
+
5
+
6
+ def B_to_GiB(bytes: Union[int, float]) -> float:
7
+ return bytes / 2**30
8
+
9
+
10
+ def get_tensor_bytes(tensor: torch.Tensor) -> int:
11
+ """
12
+ Returns the bytes of storage a given tensor takes up. If `tensor` is a view of a larger tensor,
13
+ this function only returns the bytes associated with the view.
14
+ """
15
+ tensor_bytes = tensor.numel() * tensor.element_size()
16
+ return tensor_bytes
17
+
18
+
19
+ class AllocatedMemContext:
20
+ """
21
+ Context manager which captures the allocated GPU memory at context exit and the change between
22
+ enter and exit.
23
+
24
+ Only includes `allocated_bytes.all.`-prefixed keys in `memory_stats` with all readings converted
25
+ to GiB.
26
+
27
+ Example:
28
+
29
+ ```python
30
+
31
+ ```
32
+ """
33
+
34
+ def __init__(self) -> None:
35
+ # Ensure CUDA libraries are loaded:
36
+ torch.cuda.current_blas_handle()
37
+
38
+ self.before: dict[str, int] = {}
39
+ self.after: dict[str, int] = {}
40
+ self.delta: dict[str, int] = {}
41
+
42
+ self._mem_key_prefix = "allocated_bytes.all."
43
+
44
+ def _get_mem_dict(self) -> dict[str, int]:
45
+ return {
46
+ k.replace(self._mem_key_prefix, ""): v
47
+ for k, v in torch.cuda.memory_stats().items()
48
+ if self._mem_key_prefix in k
49
+ }
50
+
51
+ def __enter__(self) -> "AllocatedMemContext":
52
+ self.before = self._get_mem_dict()
53
+ return self
54
+
55
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
56
+ self.after = self._get_mem_dict()
57
+ self.delta = {k: v - self.before[k] for k, v in self.after.items()}
58
+
59
+
60
+ class SavedTensorContext:
61
+ """
62
+ Context manager which captures all tensors which are registered as being saved for backwards
63
+ within the context window. Does not work with `meta`-device tensors.
64
+
65
+ All saved tensors are stored in the `saved_tensor_dict` attr, which is an instance of torch's
66
+ WeakTensorKeyDictionary with tensor/data_ptr key/value pairs. Some of these tensors may be
67
+ views of the same underlying storage. The total memory of all saved tensors in bytes, accounting
68
+ for redundant views, can be accessed through `saved_tensor_mem`.
69
+
70
+ Use:
71
+ ```
72
+ model = ...
73
+ with SavedTensorContext(ignored_tensors=model.parameters()) as saved:
74
+ # Do some computation with `model` and capture saved tensors which are not model weights
75
+
76
+ ```
77
+ saved.saved_tensor_dict # WeakTensorKeyDictionary of all saved tensors.
78
+ saved.saved_tensor_mem # bytes from all saved tensors (activation memory).
79
+ """
80
+
81
+ def __init__(
82
+ self,
83
+ ignored_tensors: Optional[Iterable[torch.Tensor]] = None,
84
+ ) -> None:
85
+ # Track ignored tensors by their storage's data_ptr. Important to use storage's data_ptr,
86
+ # not just the data_ptr of the tensor itself.
87
+ self._ignored_data_ptrs = (
88
+ set()
89
+ if ignored_tensors is None
90
+ else {t.untyped_storage().data_ptr() for t in ignored_tensors}
91
+ )
92
+
93
+ # Use WeakTensorKeyDictionary instances to save non-trivial tensor references, since these
94
+ # won't keep the tensor alive if the only references to the tensor are within this object.
95
+ self.saved_tensor_dict = torch.utils.weak.WeakTensorKeyDictionary()
96
+
97
+ def pack_hook(saved_tensor: torch.Tensor) -> torch.Tensor:
98
+ data_ptr = saved_tensor.untyped_storage().data_ptr()
99
+ if data_ptr not in self._ignored_data_ptrs:
100
+ self.saved_tensor_dict[saved_tensor] = data_ptr
101
+ return saved_tensor
102
+
103
+ def unpack_hook(saved_tensor: torch.Tensor) -> torch.Tensor:
104
+ return saved_tensor
105
+
106
+ self._saved_tensors_hook = torch.autograd.graph.saved_tensors_hooks(pack_hook, unpack_hook)
107
+
108
+ def __enter__(self) -> "SavedTensorContext":
109
+ self._saved_tensors_hook.__enter__()
110
+ return self
111
+
112
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
113
+ self._saved_tensors_hook.__exit__(*args, **kwargs)
114
+
115
+ @property
116
+ def saved_tensor_mem(self) -> int:
117
+ """
118
+ The memory in bytes of all saved tensors, accounting for views into the same storage.
119
+ """
120
+ accounted_for = self._ignored_data_ptrs.copy()
121
+ total_bytes = 0
122
+ for t in self.saved_tensor_dict:
123
+ data_ptr = t.untyped_storage().data_ptr()
124
+ if data_ptr not in accounted_for:
125
+ print(f"Tensor ptr: {t.untyped_storage().data_ptr()}, "
126
+ f"shape: {t.shape}, "
127
+ f"dtype: {t.dtype}, "
128
+ f"device: {t.device}"
129
+ )
130
+ total_bytes += t.untyped_storage().nbytes()
131
+ accounted_for.add(data_ptr)
132
+ return total_bytes
python/memory/measurments/act-mem-2/attn_script.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ import act_mem
4
+ import layers
5
+
6
+ if __name__ == "__main__":
7
+ batch_size, seq_len, d_model, n_heads = 1, 128, 1024, 32
8
+ print(f"Batch size: {batch_size}, sequence length: {seq_len}, d_model: {d_model}, n_heads: {n_heads}")
9
+ dtype = torch.bfloat16
10
+ inputs = torch.randn(
11
+ batch_size,
12
+ seq_len,
13
+ d_model,
14
+ device="cuda",
15
+ requires_grad=True,
16
+ dtype=dtype,
17
+ )
18
+
19
+ attn = layers.Attention(
20
+ d_model=d_model,
21
+ n_heads=n_heads,
22
+ device="cuda",
23
+ dtype=dtype,
24
+ )
25
+ with act_mem.AllocatedMemContext() as mem, act_mem.SavedTensorContext(
26
+ ignored_tensors=attn.parameters()
27
+ ) as saved:
28
+ out = attn(inputs)
29
+ stm = saved.saved_tensor_mem
30
+ print(f'{mem.delta["current"]=}')
31
+ print(f"{stm=}")
32
+ print(f"{stm/out.numel()=}")
python/memory/measurments/act-mem-2/block_script.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Prints out the ratio of activation memory for the a transformer Block when using ReLU vs GELU.
3
+ """
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+ import act_mem
9
+ import layers
10
+
11
+ if __name__ == "__main__":
12
+ batch_size, seq_len, d_model, n_heads = 2, 4096, 1024, 2
13
+ dtype = torch.bfloat16
14
+ inputs = torch.randn(
15
+ batch_size,
16
+ seq_len,
17
+ d_model,
18
+ device="cuda",
19
+ requires_grad=True,
20
+ dtype=dtype,
21
+ )
22
+
23
+ act_fn_dict = {"ReLU": nn.ReLU(), "GELU": nn.GELU()}
24
+ # Append outputs to a list to keep tensors alive
25
+ outputs = []
26
+ mem_bytes = []
27
+
28
+ for name, act_fn in act_fn_dict.items():
29
+ block = layers.Block(
30
+ d_model=d_model,
31
+ act_fn=act_fn,
32
+ n_heads=n_heads,
33
+ device="cuda",
34
+ dtype=dtype,
35
+ )
36
+ with act_mem.AllocatedMemContext() as mem, act_mem.SavedTensorContext(
37
+ ignored_tensors=block.parameters()
38
+ ) as saved:
39
+ out = block(inputs)
40
+ outputs.append(out)
41
+ print(f"{name} block bytes: {saved.saved_tensor_mem}")
42
+ mem_bytes.append(saved.saved_tensor_mem)
43
+
44
+ print(f"ReLU/GeLU block act mem ratio: {mem_bytes[0]/mem_bytes[1]}")
python/memory/measurments/act-mem-2/layers.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Union
2
+
3
+ import einops
4
+ import torch
5
+ import torch.nn as nn
6
+ import torch.nn.functional as F
7
+
8
+
9
+ class Attention(nn.Module):
10
+ """
11
+ Minimal multi-head attention layer.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ d_model: int,
17
+ n_heads: int,
18
+ device: Optional[Union[str, torch.device]] = None,
19
+ dtype: Optional[torch.dtype] = None,
20
+ ):
21
+ super().__init__()
22
+ self.d_model = d_model
23
+ self.n_heads = n_heads
24
+ factory_kwargs = {"device": device, "dtype": dtype}
25
+
26
+ self.d_head, remainder = divmod(self.d_model, self.n_heads)
27
+ assert not remainder, f"{n_heads=} must divide {d_model=} evenly"
28
+
29
+ self.lin_qkv = nn.Linear(
30
+ self.d_model,
31
+ 3 * self.d_model,
32
+ **factory_kwargs,
33
+ )
34
+
35
+ self.lin_out = nn.Linear(self.d_model, self.d_model, **factory_kwargs)
36
+
37
+ def forward(
38
+ self,
39
+ inputs: torch.Tensor,
40
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
41
+ bsz, seq_len, _ = inputs.size()
42
+
43
+ # Create the queries, keys, values
44
+ qkv = einops.rearrange(
45
+ self.lin_qkv(inputs),
46
+ "b s (three n_h d_h) -> three b s n_h d_h",
47
+ b=bsz,
48
+ s=seq_len,
49
+ three=3,
50
+ n_h=self.n_heads,
51
+ d_h=self.d_head,
52
+ )
53
+ q, k, v = qkv
54
+
55
+ bsz, seq_len, n_heads, d_head = q.shape
56
+
57
+ shape_kwargs = dict(b=bsz, n_h=n_heads, s=seq_len, d_h=d_head)
58
+ q = einops.rearrange(q, "b s n_h d_h -> b n_h s d_h", **shape_kwargs)
59
+ k = einops.rearrange(k, "b s n_h d_h -> b n_h s d_h", **shape_kwargs)
60
+ v = einops.rearrange(v, "b s n_h d_h -> b n_h s d_h", **shape_kwargs)
61
+
62
+ # Multi-head self-attention
63
+ attn_output = F.scaled_dot_product_attention(q, k, v, is_causal=True)
64
+ attn_output = einops.rearrange(
65
+ attn_output,
66
+ "b n_h s d_h -> b s (n_h d_h)",
67
+ b=bsz,
68
+ n_h=n_heads,
69
+ s=seq_len,
70
+ d_h=d_head,
71
+ )
72
+
73
+ # Final projection
74
+ out = self.lin_out(attn_output)
75
+
76
+ return out
77
+
78
+
79
+ class MLP(nn.Module):
80
+ """
81
+ Basic MLP layer with optional Dropout.
82
+ """
83
+
84
+ def __init__(
85
+ self,
86
+ d_model: int,
87
+ act_fn: nn.Module,
88
+ dropout_prob: Optional[float] = None,
89
+ device: Optional[Union[str, torch.device]] = None,
90
+ dtype: Optional[torch.dtype] = None,
91
+ ) -> None:
92
+ super().__init__()
93
+ print(f"Shapes: d_model: {d_model}, act_fn: {act_fn}, dropout_prob: {dropout_prob}, device: {device}, dtype: {dtype}")
94
+ self.d_model = d_model
95
+ self.act_fn = act_fn
96
+ self.dropout_prob = dropout_prob
97
+ factory_kwargs = {"device": device, "dtype": dtype}
98
+
99
+ self.lin_0 = nn.Linear(self.d_model, 4 * self.d_model, **factory_kwargs)
100
+ self.lin_1 = nn.Linear(4 * self.d_model, self.d_model, **factory_kwargs)
101
+ self.dropout = nn.Dropout(self.dropout_prob) if self.dropout_prob else None
102
+
103
+ def forward(self, inputs: torch.Tensor) -> torch.Tensor:
104
+ x = self.lin_0(inputs)
105
+ x = self.act_fn(x)
106
+ x = self.lin_1(x)
107
+ if self.dropout is not None:
108
+ x = self.dropout(x)
109
+ return x
110
+
111
+
112
+ class SwiGLUMLP(nn.Module):
113
+ """
114
+ Llama 3 SwiGLU MLP layer with optional Dropout.
115
+ """
116
+
117
+ def __init__(
118
+ self,
119
+ d_model: int,
120
+ intermediate_size: int,
121
+ act_fn: nn.Module,
122
+ dropout_prob: Optional[float] = None,
123
+ device: Optional[Union[str, torch.device]] = None,
124
+ dtype: Optional[torch.dtype] = None,
125
+ ) -> None:
126
+ super().__init__()
127
+ print(f"Shapes: d_model: {d_model}, intermediate_size: {intermediate_size}, act_fn: {act_fn}, dropout_prob: {dropout_prob}, device: {device}, dtype: {dtype}")
128
+ self.d_model = d_model
129
+ self.intermediate_size = intermediate_size
130
+ self.act_fn = act_fn
131
+ self.dropout_prob = dropout_prob
132
+ factory_kwargs = {"device": device, "dtype": dtype}
133
+
134
+ self.gate_proj = nn.Linear(self.d_model, self.intermediate_size, **factory_kwargs)
135
+ self.up_proj = nn.Linear(self.d_model, self.intermediate_size, **factory_kwargs)
136
+ self.down_proj = nn.Linear(self.intermediate_size, self.d_model, **factory_kwargs)
137
+ self.dropout = nn.Dropout(self.dropout_prob) if self.dropout_prob else None
138
+
139
+ def forward(self, inputs: torch.Tensor) -> torch.Tensor:
140
+ x = self.down_proj(self.act_fn(self.gate_proj(inputs)) * self.up_proj(inputs))
141
+ if self.dropout is not None:
142
+ x = self.dropout(x)
143
+ return x
144
+
145
+
146
+ class Block(nn.Module):
147
+ """
148
+ Basic transformer block.
149
+
150
+ Schematic:
151
+ ┌──────┐
152
+ │inputs│
153
+ └┬─┬───┘
154
+ │┌▽───────────┐
155
+ ││norm_0, attn│
156
+ │└┬───────────┘
157
+ ┌▽─▽──┐
158
+ │ add │
159
+ └┬─┬──┘
160
+ │┌▽──────────┐
161
+ ││norm_1, mlp│
162
+ │└┬──────────┘
163
+ ┌▽─▽──┐
164
+ │ add │
165
+ └┬────┘
166
+ ┌▽──────┐
167
+ │outputs│
168
+ └───────┘
169
+ """
170
+
171
+ def __init__(
172
+ self,
173
+ d_model: int,
174
+ n_heads: int,
175
+ act_fn: nn.Module,
176
+ dropout_prob: Optional[float] = None,
177
+ dtype: Optional[torch.dtype] = None,
178
+ device: Optional[Union[str, torch.device]] = None,
179
+ ):
180
+ super().__init__()
181
+ factory_kwargs = {"device": device, "dtype": dtype}
182
+ self.attn = Attention(d_model=d_model, n_heads=n_heads, **factory_kwargs)
183
+ self.mlp = MLP(d_model=d_model, act_fn=act_fn, dropout_prob=dropout_prob, **factory_kwargs)
184
+ self.norm_0 = nn.LayerNorm(d_model, **factory_kwargs)
185
+ self.norm_1 = nn.LayerNorm(d_model, **factory_kwargs)
186
+
187
+ def forward(self, inputs: torch.Tensor) -> torch.Tensor:
188
+ outputs = self.attn(self.norm_0(inputs)) + inputs
189
+ outputs = self.mlp(self.norm_1(outputs)) + outputs
190
+ return outputs
python/memory/measurments/act-mem-2/mlp_script.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Prints out the ratio of activation memory for the MLP layer when using ReLU vs GELU.
3
+ """
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+ import act_mem
9
+ import layers
10
+
11
+ if __name__ == "__main__":
12
+ batch_size, seq_len, d_model, dropout_prob = 1, 128, 1024, 0.1
13
+ print(f"Batch size: {batch_size}, sequence length: {seq_len}, d_model: {d_model}, dropout_prob: {dropout_prob} ")
14
+ dtype = torch.bfloat16
15
+ inputs = torch.randn(
16
+ batch_size,
17
+ seq_len,
18
+ d_model,
19
+ device="cuda",
20
+ requires_grad=True,
21
+ dtype=dtype,
22
+ )
23
+
24
+ act_fn_dict = {"ReLU": nn.ReLU() , "GELU": nn.GELU(), "silu": nn.SiLU()}
25
+ # Append outputs to a list to keep tensors alive
26
+ outputs = []
27
+ mem_bytes = []
28
+
29
+ for name, act_fn in act_fn_dict.items():
30
+ if name == "silu":
31
+ mlp = layers.SwiGLUMLP(
32
+ d_model=d_model,
33
+ intermediate_size=4 * d_model,
34
+ act_fn=act_fn,
35
+ dropout_prob=dropout_prob,
36
+ device="cuda",
37
+ dtype=dtype,
38
+ )
39
+ else:
40
+ mlp = layers.MLP(
41
+ d_model=d_model,
42
+ act_fn=act_fn,
43
+ dropout_prob=dropout_prob,
44
+ device="cuda",
45
+ dtype=dtype,
46
+ )
47
+ with act_mem.AllocatedMemContext() as mem, act_mem.SavedTensorContext(
48
+ ignored_tensors=mlp.parameters()
49
+ ) as saved:
50
+ out = mlp(inputs)
51
+ outputs.append(out)
52
+ stm = saved.saved_tensor_mem
53
+ assert mem.delta["current"] == stm
54
+ print(f"{name} bytes: {act_mem.B_to_GiB(stm)}")
55
+ mem_bytes.append(stm)
56
+
57
+ print(f"ReLU/GELU act mem ratio: {mem_bytes[0]/mem_bytes[1]}")
python/memory/measurments/act-mem-2/requirements.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ einops==0.8.0
2
+ filelock==3.14.0
3
+ fsspec==2024.5.0
4
+ iniconfig==2.0.0
5
+ Jinja2==3.1.4
6
+ MarkupSafe==2.1.5
7
+ mpmath==1.3.0
8
+ networkx==3.3
9
+ packaging==24.0
10
+ pluggy==1.5.0
11
+ pytest==8.2.1
12
+ sympy==1.12.1
13
+ torch==2.3.0
14
+ typing_extensions==4.12.0
python/memory/measurments/act-mem-2/test.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import pytest
4
+ import torch
5
+ import torch.nn as nn
6
+
7
+ import act_mem
8
+ import layers
9
+
10
+ BATCH_SIZES = (1, 2)
11
+ D_MODELS = (128, 256)
12
+ SEQ_LENS = (64, 128)
13
+ N_HEADS = (2, 4)
14
+
15
+
16
+ DEVICES = ["cpu"]
17
+ if torch.cuda.is_available():
18
+ DEVICES.append("cuda")
19
+
20
+
21
+ ZERO_MEM_ACT_FNS = [
22
+ nn.ReLU(),
23
+ nn.Sigmoid(),
24
+ nn.Tanh(),
25
+ nn.LeakyReLU(inplace=True),
26
+ nn.Sigmoid(),
27
+ ]
28
+ ALL_ACT_FNS = ZERO_MEM_ACT_FNS + [
29
+ nn.ELU(),
30
+ nn.GELU(),
31
+ nn.Hardshrink(),
32
+ nn.Hardsigmoid(),
33
+ nn.Hardswish(),
34
+ nn.Hardtanh(),
35
+ nn.LeakyReLU(),
36
+ nn.SELU(),
37
+ nn.SiLU(),
38
+ ]
39
+
40
+
41
+ class TestSavedTensorContext:
42
+ @pytest.mark.parametrize("device", DEVICES)
43
+ @pytest.mark.parametrize("d_model", D_MODELS)
44
+ @pytest.mark.parametrize("batch_size", BATCH_SIZES)
45
+ def test_linear(self, device: str, d_model: int, batch_size: int) -> None:
46
+ """
47
+ Test a simple linear layer. The inputs should be saved for backwards
48
+ """
49
+ inputs = torch.randn(batch_size, d_model, requires_grad=True, device=device)
50
+ lin = nn.Linear(d_model, d_model, device=device)
51
+ with act_mem.SavedTensorContext(ignored_tensors=lin.parameters()) as saved:
52
+ _ = lin(inputs)
53
+ assert saved.saved_tensor_mem == inputs.numel() * inputs.element_size()
54
+
55
+ @pytest.mark.parametrize("device", DEVICES)
56
+ @pytest.mark.parametrize("d_model", D_MODELS)
57
+ @pytest.mark.parametrize("batch_size", BATCH_SIZES)
58
+ def test_linear_amp(self, device: str, d_model: int, batch_size: int) -> None:
59
+ """
60
+ Test a linear layer with AMP. The saved tensors should now be a low-precision version of the
61
+ inputs and the low-precision version of the weights version of the weights
62
+ """
63
+ inputs = torch.randn(batch_size, d_model, requires_grad=True, device=device)
64
+ lin = nn.Linear(d_model, d_model, device=device)
65
+ dtype = torch.bfloat16
66
+ with torch.autocast(device_type=device, dtype=dtype):
67
+ with act_mem.SavedTensorContext(ignored_tensors=lin.parameters()) as saved:
68
+ out = lin(inputs)
69
+ assert (
70
+ saved.saved_tensor_mem
71
+ == out.numel() * out.element_size() + lin.weight.numel() * dtype.itemsize
72
+ )
73
+
74
+ @pytest.mark.parametrize("act_fn", ALL_ACT_FNS)
75
+ @pytest.mark.parametrize("dropout_prob", (None, 0.5))
76
+ @pytest.mark.parametrize("device", DEVICES)
77
+ @pytest.mark.parametrize("d_model", D_MODELS)
78
+ @pytest.mark.parametrize("batch_size", BATCH_SIZES)
79
+ @pytest.mark.parametrize("seq_len", SEQ_LENS)
80
+ def test_mlp(
81
+ self,
82
+ act_fn: nn.Module,
83
+ dropout_prob: Optional[float],
84
+ device: str,
85
+ d_model: int,
86
+ batch_size: int,
87
+ seq_len: int,
88
+ ) -> None:
89
+ """
90
+ For the transformer MLP layer with a ReLU non-linearity, the initial inputs and the inputs
91
+ to the final linear layer (which are four times as large) must always be saved. If the
92
+ derivative of the activation function cannot be expressed in terms of the activation
93
+ function's *outputs*, then the activation inputs must also be saved (which are again four
94
+ times as large as the MLP's inputs). The MLP activation memory can be nearly halved by a
95
+ choice of activation function.
96
+ """
97
+ inputs = torch.randn(
98
+ batch_size, seq_len, d_model, requires_grad=True, device=device
99
+ )
100
+ expansion_factor = 4
101
+ mlp = layers.MLP(
102
+ d_model=d_model, act_fn=act_fn, dropout_prob=dropout_prob, device=device
103
+ )
104
+ with act_mem.SavedTensorContext(ignored_tensors=mlp.parameters()) as saved:
105
+ _ = mlp(inputs)
106
+
107
+ # Compare measured memory against expected
108
+ first_lin_input_mem = act_mem.get_tensor_bytes(inputs)
109
+ second_lin_input_mem = expansion_factor * first_lin_input_mem
110
+ # Only some activations require additional activation memory
111
+ activation_input_mem = 0 if act_fn in ZERO_MEM_ACT_FNS else second_lin_input_mem
112
+ dropout_act_mem = (
113
+ 0 if not dropout_prob else inputs.numel() * (4 if device == "cpu" else 1)
114
+ )
115
+
116
+ expected_mem = (
117
+ first_lin_input_mem
118
+ + second_lin_input_mem
119
+ + activation_input_mem
120
+ + dropout_act_mem
121
+ )
122
+ assert saved.saved_tensor_mem == expected_mem
123
+
124
+ @pytest.mark.parametrize("act_fn", ALL_ACT_FNS)
125
+ @pytest.mark.parametrize("dropout_prob", (None, 0.5))
126
+ @pytest.mark.parametrize("device", DEVICES)
127
+ @pytest.mark.parametrize("d_model", D_MODELS)
128
+ @pytest.mark.parametrize("batch_size", BATCH_SIZES)
129
+ @pytest.mark.parametrize("seq_len", SEQ_LENS)
130
+ def test_mlp_amp(
131
+ self,
132
+ act_fn: nn.Module,
133
+ dropout_prob: Optional[float],
134
+ device: str,
135
+ d_model: int,
136
+ batch_size: int,
137
+ seq_len: int,
138
+ ) -> None:
139
+ """
140
+ Similar story with AMP. The only changes come from the modified dtypes and needing to also
141
+ save references to the low-precision weights in the Linear layers.
142
+ """
143
+ inputs = torch.randn(
144
+ batch_size, seq_len, d_model, requires_grad=True, device=device
145
+ )
146
+ expansion_factor = 4
147
+ mlp = layers.MLP(
148
+ d_model=d_model, act_fn=act_fn, dropout_prob=dropout_prob, device=device
149
+ )
150
+ dtype = torch.bfloat16
151
+ with torch.autocast(device_type=device, dtype=dtype):
152
+ with act_mem.SavedTensorContext(ignored_tensors=mlp.parameters()) as saved:
153
+ _ = mlp(inputs)
154
+
155
+ # Compare measured memory against expected
156
+ amp_weight_mem = 2 * expansion_factor * d_model**2 * dtype.itemsize
157
+ first_lin_input_mem = inputs.numel() * dtype.itemsize
158
+ second_lin_input_mem = expansion_factor * inputs.numel() * dtype.itemsize
159
+ # Only some activations require additional activation memory
160
+ activation_input_mem = 0 if act_fn in ZERO_MEM_ACT_FNS else second_lin_input_mem
161
+ dropout_act_mem = (
162
+ 0
163
+ if not dropout_prob
164
+ else inputs.numel() * (dtype.itemsize if device == "cpu" else 1)
165
+ )
166
+
167
+ expected_mem = (
168
+ amp_weight_mem
169
+ + first_lin_input_mem
170
+ + second_lin_input_mem
171
+ + activation_input_mem
172
+ + dropout_act_mem
173
+ )
174
+ assert (
175
+ saved.saved_tensor_mem == expected_mem
176
+ ), f"Failed on {act_fn=}, {dropout_prob=}"
177
+
178
+
179
+ @pytest.mark.skipif(not torch.cuda.is_available(), reason="cuda not available")
180
+ class TestCUDAMemReadings:
181
+ @pytest.mark.parametrize("d_model", D_MODELS)
182
+ @pytest.mark.parametrize("batch_size", BATCH_SIZES)
183
+ @pytest.mark.parametrize("seq_len", SEQ_LENS)
184
+ @pytest.mark.parametrize("act_fn", ALL_ACT_FNS)
185
+ def test_mlp(
186
+ self, d_model: int, batch_size: int, seq_len: int, act_fn: nn.Module
187
+ ) -> None:
188
+ """
189
+ Track saved tensors and allocated memory and verify they agree.
190
+ """
191
+
192
+ inputs = torch.randn(batch_size, seq_len, d_model, device="cuda")
193
+ mlp = layers.MLP(d_model=d_model, act_fn=act_fn, device="cuda")
194
+
195
+ with act_mem.AllocatedMemContext() as mem, act_mem.SavedTensorContext(
196
+ ignored_tensors=mlp.parameters()
197
+ ) as saved:
198
+ outputs = mlp(inputs)
199
+
200
+ # AllocatedMemContext captures the outputs, but not inputs, while SavedTensorContext
201
+ # captures inputs and not outputs. Nevertheless, the readings agree because inputs and
202
+ # outputs are tensors of the same size and `dtype`.
203
+ assert mem.delta["current"] == saved.saved_tensor_mem
src/bibliography.bib ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @article{example2023,
2
+ title={Example Paper Title},
3
+ author={Example, Author and Another, Author},
4
+ journal={Journal of Examples},
5
+ volume={1},
6
+ number={1},
7
+ pages={1--10},
8
+ year={2023},
9
+ publisher={Example Publisher}
10
+ }
11
+
12
+ @online{distill,
13
+ title={Distill},
14
+ url={https://distill.pub},
15
+ year={2016},
16
+ publisher={Distill Working Group}
17
+ }
18
+
19
+ @book{textbook2022,
20
+ title={An Example Textbook},
21
+ author={Author, Example},
22
+ year={2022},
23
+ publisher={Example Academic Press}
24
+ }
src/distill.js ADDED
The diff for this file is too large to render. See raw diff
 
src/fragmentLoader.js ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function loadFragments() {
2
+ // Find all elements with ids starting with 'fragment-'
3
+ const fragmentElements = Array.from(document.querySelectorAll('[id^="fragment-"]'));
4
+
5
+ class FetchQueue {
6
+ constructor(maxConcurrent = 3) {
7
+ this.queue = [];
8
+ this.maxConcurrent = maxConcurrent;
9
+ this.activeFetches = 0;
10
+ this.maxRetries = 3; // Maximum number of retry attempts
11
+ this.baseDelay = 1000; // Base delay in milliseconds (1 second)
12
+ }
13
+
14
+ async sleep(ms) {
15
+ return new Promise(resolve => setTimeout(resolve, ms));
16
+ }
17
+
18
+ async fetchWithRetry(fragmentPath, retryCount = 0) {
19
+ try {
20
+ const response = await fetch(fragmentPath);
21
+ if (!response.ok) {
22
+ throw new Error(`HTTP error! status: ${response.status}`);
23
+ }
24
+ return await response.text();
25
+ } catch (error) {
26
+ if (retryCount < this.maxRetries) {
27
+ // Exponential backoff: 1s, 2s, 4s
28
+ const delay = this.baseDelay * Math.pow(2, retryCount);
29
+ console.warn(`Retry ${retryCount + 1}/${this.maxRetries} for ${fragmentPath} after ${delay}ms`);
30
+ await this.sleep(delay);
31
+ return this.fetchWithRetry(fragmentPath, retryCount + 1);
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ async addFetch(element) {
38
+ const fragmentName = element.id.replace('fragment-', '');
39
+ const fragmentPath = `fragments/${fragmentName}.html`;
40
+
41
+ return new Promise(async (resolve, reject) => {
42
+ try {
43
+ const fetchPromise = (async () => {
44
+ try {
45
+ const html = await this.fetchWithRetry(fragmentPath);
46
+
47
+ // Process the fragment
48
+ const temp = document.createElement('div');
49
+ temp.innerHTML = html;
50
+ element.innerHTML = temp.innerHTML;
51
+
52
+ // Handle scripts
53
+ const scripts = temp.getElementsByTagName('script');
54
+ Array.from(scripts).forEach(oldScript => {
55
+ const newScript = document.createElement('script');
56
+ Array.from(oldScript.attributes).forEach(attr => {
57
+ newScript.setAttribute(attr.name, attr.value);
58
+ });
59
+ newScript.textContent = oldScript.textContent;
60
+ oldScript.parentNode.removeChild(oldScript);
61
+ document.body.appendChild(newScript);
62
+ });
63
+
64
+ this.activeFetches--;
65
+ resolve();
66
+ } catch (error) {
67
+ console.error(`Failed to load fragment ${fragmentPath} after ${this.maxRetries} retries:`, error);
68
+ this.activeFetches--;
69
+ reject(error);
70
+ }
71
+ })();
72
+
73
+ this.queue.push(fetchPromise);
74
+ this.activeFetches++;
75
+ } catch (error) {
76
+ reject(error);
77
+ }
78
+ });
79
+ }
80
+
81
+ async processNext(element) {
82
+ if (this.activeFetches < this.maxConcurrent && element) {
83
+ await this.addFetch(element);
84
+ }
85
+ }
86
+ }
87
+
88
+ // Initialize queue
89
+ const fetchQueue = new FetchQueue(3);
90
+ let currentIndex = 0;
91
+ const elements = fragmentElements; // Assuming this is defined elsewhere
92
+
93
+ // Initial loading of first 3 elements
94
+ while (currentIndex < elements.length && currentIndex < 3) {
95
+ await fetchQueue.processNext(elements[currentIndex]);
96
+ currentIndex++;
97
+ }
98
+
99
+ // Process remaining elements as fetches complete
100
+ while (currentIndex < elements.length) {
101
+ // Wait for any fetch to complete
102
+ await Promise.race(fetchQueue.queue);
103
+ // Remove completed fetch from queue
104
+ fetchQueue.queue = fetchQueue.queue.filter(p => p.status === 'pending');
105
+ // Add next element to queue
106
+ await fetchQueue.processNext(elements[currentIndex]);
107
+ currentIndex++;
108
+ }
109
+
110
+ // Wait for remaining fetches to complete
111
+ await Promise.all(fetchQueue.queue);
112
+ }
113
+
114
+ export { loadFragments }
src/fragments/banner.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <div> <div id="88bd2bde-2a9d-42b6-bea3-2a6d06b034ea" class="plotly-graph-div" style="height:400px; width:1200px;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("88bd2bde-2a9d-42b6-bea3-2a6d06b034ea")) { Plotly.newPlot( "88bd2bde-2a9d-42b6-bea3-2a6d06b034ea", [{"customdata":[["biiig dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["a dot"],["ok-ish dot"],["smol dot"],["a dot"],["a dot"],["ok-ish dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["smol dot"],["smol dot"],["biiig dot"],["a dot"],["smol dot"],["biiig dot"],["a dot"],["smol dot"],["a dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["smol dot"],["smol dot"],["smol dot"],["smol dot"],["smol dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["a dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["a dot"],["a dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["biiig dot"],["smol dot"],["biiig dot"],["biiig dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["biiig dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["a dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["a dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["biiig dot"],["a dot"],["biiig dot"],["a dot"],["a dot"],["ok-ish dot"],["a dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["smol dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["biiig dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["a dot"],["biiig dot"],["biiig dot"],["a dot"],["a dot"],["a dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["a dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["a dot"],["smol dot"],["a dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["a dot"],["biiig dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["a dot"],["biiig dot"],["a dot"],["biiig dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["ok-ish dot"],["a dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["a dot"],["smol dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["a dot"],["smol dot"],["smol dot"],["biiig dot"],["smol dot"],["smol dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["a dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["a dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["ok-ish dot"],["biiig dot"],["a dot"],["biiig dot"],["biiig dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["smol dot"],["biiig dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["a dot"],["biiig dot"],["smol dot"],["a dot"],["smol dot"],["ok-ish dot"],["biiig dot"],["biiig dot"],["biiig dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["smol dot"],["smol dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["ok-ish dot"],["smol dot"],["smol dot"],["smol dot"],["a dot"],["a dot"],["a dot"],["a dot"],["smol dot"],["a dot"],["a dot"],["biiig dot"],["ok-ish dot"],["ok-ish dot"],["smol dot"],["a dot"],["ok-ish dot"],["smol dot"],["smol dot"],["ok-ish dot"],["a dot"]],"hoverlabel":{"namelength":0},"hovertemplate":"Dot category: %{customdata[0]}","marker":{"color":{"dtype":"f8","bdata":"NSuP9zcJI0DzwFYGn0gaQLZzmkZHByFA+P+aDpbfHkCwxIAvKbMiQLxO7ywDOSFAkrQilW9qGkA42CJpbWcUQBsoeWzpYyBAQjfOKzOZHkAirl5Y47kZQHo9O10vahVA9tL3qjtYIEDDffk1l7UjQO4gDWWi9RhAbl4O21pGHEC2NukPe44cQEBdAmUkehhAaO9vYTNPGEAkKuTDTKQeQCwfNyVGoRZA7AXlPkQVFUBBS1LPtqEhQHjQxmS+fSBAzIXDZfEoGEDwmCYU3FQjQNBMefzIwx5APDOqSw25FUCJceoo8vQeQP7yGSlEKyNAQTLBxzJtHUDBmCp6+7oiQBoQiWhE8yFA8nURfbx6IUBmXmxn2ucZQCV81yB8MR1APe9aowa5IED5TDni5\u002fMiQE8Bv4u6zhxAuahCRUDHGUAuRAEnvokVQK9yTdCItxdAS9Rm\u002fEJxFUAkVNMOqH0XQLUcVRC3QhVAr9JC7d6BFECx6VQKAsMZQJiTu8jHAiBASNpbXE+iIEBuNZJTAhkjQDf4LRmEZRxAAc\u002fXZA4iGEBoI3er6SwbQDzSHSGc\u002fiBALJKQx8UFGUBnxhDVR5kZQNxdroKZTRVAmIzuoRi0G0DXi1x1ruEXQH4OD4n5ghhA2GYHR1L1HkBGY0\u002fvvsMgQFQklZCQ6x5AUoEYHYbFFEC9SZgflvgbQDubyvxZJR9Al0AR8t+YG0BjEwai1O8jQIm9fGx9DB9AJ\u002fViqUi9FkCP01kOAW8ZQF\u002fwJr5pmCBAsN8BVrmfHECAA0NocFweQEK4F2+EXxtA6ie8cVJaIUADJI6NekMdQB2bndnvwxdAjTccD2guFUBBMr6RvxEhQHnH3ClkthtASKBd2FYgIkAOCefEnMwiQB71sK8twR5AKix3z3LvIECge2K95\u002fQjQKKwiqhdnhdApBqwIX9fGkBjBAhPEWAbQNxyN8qJEBZAFRHupGPBHEBM28zFik0gQHsg478ihhlAx1QGiQRgIkB193H6ybMjQAAXe2\u002fpiiJAFofW\u002fyalIEAvhq2s3p8bQCAw71hf8SNAOICnOo\u002fwGUABkT73IBIUQDqKRFRq7iFAWuOaJsGiGkCdNtGT1T0eQPwUzYZdhhxA226rezPlIEAK2rKtZCYhQM6e29Px6RlALDlNZmBcIkBF51ygESIbQARdbLsmGiBAcPPGqWmoI0AA9vbuxj0fQFLy6PqqKSJAX2nTXbL2FkDk6uyOBjsgQBIKnNMwzSFADkUJljHpFUBRWFONQhkfQF7mpoKcuR5AS6GPzhyaIECWv1cvA3QiQCTJYvjoRh5AFSMr+II+I0DAVgHpCp0ZQDLZRRIwCx5AToyx0piwIECo1p8Myr0fQOSPrLgYpBZAM1UrnbGlIEDy552AKr4YQE8C39hIuiJAmPTyZN1jG0B9p4nAEOscQDBHdvtiPBZAATP1FnlSIUC9lsGRS2sUQHTHAX8sViBAxsOQHXPOFUD8JQEmPBgcQPrz2LeufxZAhNMjenowHUBT+7k7bO8YQASsYAOMfhlAef5a6KU8F0BMqKTYMfQZQIBwogq5mh5AsyQGXIyzHECm+hE9MjwiQLKGFnhocB1ADTMLPgRxFUCO9NdLNYEhQMy\u002fTuw\u002fwxpAeqAQGXgoGEDg1wTLsCwcQMC+x+kqLCNAQy3+JdgJFkDajzrsrUUUQCi4qErh6RxASsFgzXDsGkAtogkxtf8jQPLsdJgkxhxAKjnbSbMrIECWefuN6fMiQKbpd2eHxh9AbEz3spPGI0CA3RaqTq4UQJhk6nfe8xRAWbiqkx5oHkBbUE1wC4MjQLr9sg+1+CFAaXQ1CDK4GEBi8yblEzcjQOUy6xGLuyJA2vM+aS8eIUBDzKhWJ4UjQM2IV\u002fUqhxpAgZjH335aGUC\u002fCbav8yQYQBN9THrmgxZAykj1ErUCHkANqY5Z1e4hQPjVtvSFrSFAIIEFQhyPHkCW76\u002fZUPEaQEo256Q8ORxAblmeP4aiIkCueeDEm3UjQEXS4+kRdiJAjeNXaVm9GkDYxejIj50fQKO+bcSJdiNAr5cChkQcF0Dvt+qkTPcVQPShkHR4MR9AonlqcvnTGEBvTbvwc\u002fggQB\u002fmUxg9DBRA36HYc0QHI0A\u002fOMEnWU4cQI\u002f5IjbVpSBADkL2EA7EGkA7x\u002f0ADSEhQGOxHli7QBpAElvxWLuVIEAEPS8PokEaQGoP7+CiqhtAelK\u002fncH7FUC6io+f4MgeQI8O806nQR1AAJb6Oue1FEDH30BZI3gZQLSKZLXNqSNAt0nJeolIIkBu3y6iCqsXQLINEatrzRZAIvn0fdw4FEDA2IWf83UeQKEy+iUjBCBArE1hmsbGI0BklE0I9kwZQGM7pV3HHCBA\u002fhDNXYsOH0AhE22eArIgQIYTUgxigRdAXitsu7eYGkA9RXE5IPYjQDxwmauxkRlAfDYQL6rVI0BUgV+Ks3kgQG6AnJhXXR1A8or\u002f2hwSFUAIgfUlIwsZQDTcZVLumhxA6QjirXc3FUA10SE2ppkaQG\u002fchBsurRZAEKqnjjRRG0DbXS+XVOMjQCR+klfDvCBAcvT5sjPmG0AuEZiM9VkiQE7\u002f9nPMwxhAzUNaGpsYIkAMFl+iM4geQAbi0IVJkSJAXxa114t\u002fH0A4LZs7SxMhQCPR49QEkxtA1Jk77+3lIECI0aR07toVQIsuW1P1hBdAR4MfDSR2IECzOdYNDI4hQOpD59D\u002fEBZAiERnFZezHECEaCFOJH0iQJ6LD6MStRpAIPg8FEDaFkC77pOHxHsXQIKYaKpe0hxACHYgLLeVH0CMjGdWsu0gQAp8kRBXiiFABnaqzwIiHED4o8ohaUYjQB6ez25KZhVAODyihKQZGEAVl18M61sVQGTFlTpa1iBANQ9RIDogI0A0b3ba87kVQAxFuokw1xlAVn0YdOV5IkCZF1qq3xEYQF70crxSgRhAmjv\u002fDYqqI0A2Tf\u002f9wFcUQF0zMnYpyBVAp+Va3dnnI0DrnbA6MMwgQK2PaoBKXx9AvDk4yCcMHkCMHtGK3hUiQOBifysgRyJATjUTFEe3IEAgYn34c1QgQEvJtnn6nB9ArB0t3VRRHkCp64NUq68jQOwK\u002foo\u002fQiFAfOBrePfqI0DdiBMwugIdQOw4g57jZiJA\u002fmRSrrobIUDgwI46TQMcQLbiHMtOfyNA\u002fKVcwF8gFEC+RWrXTB8eQOZRReUF0xRAJG5asaiVIEDakyC8ttwcQGbJjrgiCyJAFUL1ynSOHEBCG81wE28UQCrioPQFRyNA2PjDcDBsGUB4mAX+diMVQFdwfQyOxiBA9OHZFopzHUDVGh8\u002frCMjQMrNAYaphxVAaZIfeQx9HkBDOPxNx0IjQE9mdsWUJyJAX\u002fQc\u002f+2mFEAfi1InuigXQFDGmnvezBZA6PU\u002fkkx1IkCftvrATAcYQJhU2m0ywhhAa+DPV2YBFkBjxzSzkyYhQAdYVtH4tBtAQnTQUPrvH0Blcff07xgjQOomWdPlGRVAHftwUzmWF0Cu3UU8\u002fQMjQGNEGODnGyFAI9CO58OXIkC4uGz9+GkeQL9mwKcK2SFAiCDh8DB0HkA\u002ffgMr1AoXQCqEn79enR5A5\u002f4QbQF\u002fFUDo63HexOYbQKGlPexgAiNA7w5z3\u002fe7FUD7w0ihBSwWQPObsdTv1xlARBUncGKNIkBziwdUxmAhQIJc4VeMchZAyNWlhm3JGkD1cSz66tgWQH3dkv6F9hlAnJZtjukzHkC+gt+\u002fyXceQGuFps\u002fhsyJA5RxPbzDJHUBngvyx1AkcQPq8r9pjxhlA6lCggb+pIEAkljYd5PkUQAoR+A4uZBxAepH5CPOWF0D2xOpi0owXQOzzMRoF\u002fRVAimoq9\u002fDjHkBCnaUXrbgbQBvV9Dlc+RxAzhvtX0bDIUBeDedw3NgiQI9ETzyi9h9ASi4PNTLaHUCaJ5fLdK8gQMj5UGSR6xVAUmrCJdbYIkAZMNcR95keQKkPSNYCCCBAXFQQu7B0IUDjTOlgmtAVQDw40sx3lyBAHcAuNNttF0Cz7w7mKb0hQH2I2dYpoBVArnEF66FxFUDiyzTxOfsVQD10LW8mDRpApmGJNN4AIkDWaZ3JVGcdQDlgj\u002fvhrhxAso53sF8EIkB4DnaJeHkiQIxYL6+8JyBAWHPi7bYIFUBHMGhAEY4dQH\u002fu5BowQCBA3KbYZVFMGkDeyXgP+iwhQCIhfAhIyBlApYieNe1bHkBjEq8JlEcZQIsVC53D8x1AKiHzjaXiHkAcYAKff1EeQOIpVwfIViFArTruYthVG0DnODsdngYeQIqpzBDaiSJAetNrtQUVF0Cs5RrCv1MXQPy\u002fs1wPBx9AcVZi3gmMG0AAcSNO7GAhQO7DrwgelBdA4I9\u002fCbbRFUD02rnmUXYiQA0nAofWmxZA9lpQ2knmGEBIzd08bWMjQHRwoyEaExxAnDR0LaLVFUANmCOh5FUhQJSfonbdfxtAUjbX2LijF0DaEYkjMwgfQKvTLhO59x9AAdAuHiVqFkD+wSLgFSYhQM4UO56kBBpAwBrh7AVxFEBnSg21iIIdQEU3tP5vTiFA\u002fH7caY8FGEA1ZCiKw6MaQNRXiCFsAhxAHKfehkc5G0CMiz3qO38XQCjWrBo0vB1AHUNHqkXNGED42SOQswwWQINqqlljQCFASO0cCsKpHUDXqSebilQZQDc5kkkoYxlAQvk0RIkGG0CCYbCOrWgeQIA\u002fqE12PhtAcXFUY4VkI0AOyXgkkBIeQKJvQPHYtiFAe0EZtDaYIUD8\u002fQlH7V0WQHM7peTEYyJAijXMZdN3H0C+JmlrDr8eQB5qjSf0CxhA9uOfyXiRIUDyfwSTBPkgQEfe0AG38CBAZrKTYsMAFUBS7s8vM4YdQP8kigLgehdAKT1cehiLIECGFsISqFcZQEtuU8KPBSFADHYpuE\u002fUH0Bzii+nNJogQBKLWoeEPRZA6JhcA0n8HECo043e+PsaQKiwTkQ\u002fhiBAYIbQ2Ag+I0BAUs3h0hUVQFMSLlE9sCBAa858w+OnFEBUZbCvilMZQLXSXhGnYiNAXrgm5HR0I0AtIFA36OMiQL7jBurC8BpA+IKdy5PfIUCyBWnn55gaQCBSf8phhhdA0Wa39GivHUAWl8V29VsdQN0iq6mx7CFAgOs7Eh6sFED2xGaxhMkVQMnA1q8QoBtAKjYhWCp0GkD+bx8TmFMZQI3ZsRBNnCJAqemy4d0BHEAm4+7jnmAbQC7zeSXclRpA6d6Q6mIlG0Cp0Zf1k10WQNQhSlVqBxxAHAGbxJfFFUC2J3iKX1cYQJwxxnwaMRRA7ZKHBBWwIEDKKimWtkohQHaPE1U0cyFAM3OghPxkHkAgTj3nsEoYQNTXKBxhYh9AOxQ8ny92IUAM9NL\u002fsfIjQHSF5K38QBpA2FyPhk6SGkAk3MpoeFAUQAPDfMo61B9AuD4ny5LdHEBcOdiDMTgUQEageTdcbBVAkiFjKsK8GkDdjkY8xpkgQA=="},"colorscale":[[0,"rgb(78, 165, 183)"],[0.5,"rgb(206, 192, 250)"],[1,"rgb(232, 137, 171)"]],"opacity":0.9,"size":{"dtype":"f8","bdata":"NSuP9zcJI0DzwFYGn0gaQLZzmkZHByFA+P+aDpbfHkCwxIAvKbMiQLxO7ywDOSFAkrQilW9qGkA42CJpbWcUQBsoeWzpYyBAQjfOKzOZHkAirl5Y47kZQHo9O10vahVA9tL3qjtYIEDDffk1l7UjQO4gDWWi9RhAbl4O21pGHEC2NukPe44cQEBdAmUkehhAaO9vYTNPGEAkKuTDTKQeQCwfNyVGoRZA7AXlPkQVFUBBS1LPtqEhQHjQxmS+fSBAzIXDZfEoGEDwmCYU3FQjQNBMefzIwx5APDOqSw25FUCJceoo8vQeQP7yGSlEKyNAQTLBxzJtHUDBmCp6+7oiQBoQiWhE8yFA8nURfbx6IUBmXmxn2ucZQCV81yB8MR1APe9aowa5IED5TDni5\u002fMiQE8Bv4u6zhxAuahCRUDHGUAuRAEnvokVQK9yTdCItxdAS9Rm\u002fEJxFUAkVNMOqH0XQLUcVRC3QhVAr9JC7d6BFECx6VQKAsMZQJiTu8jHAiBASNpbXE+iIEBuNZJTAhkjQDf4LRmEZRxAAc\u002fXZA4iGEBoI3er6SwbQDzSHSGc\u002fiBALJKQx8UFGUBnxhDVR5kZQNxdroKZTRVAmIzuoRi0G0DXi1x1ruEXQH4OD4n5ghhA2GYHR1L1HkBGY0\u002fvvsMgQFQklZCQ6x5AUoEYHYbFFEC9SZgflvgbQDubyvxZJR9Al0AR8t+YG0BjEwai1O8jQIm9fGx9DB9AJ\u002fViqUi9FkCP01kOAW8ZQF\u002fwJr5pmCBAsN8BVrmfHECAA0NocFweQEK4F2+EXxtA6ie8cVJaIUADJI6NekMdQB2bndnvwxdAjTccD2guFUBBMr6RvxEhQHnH3ClkthtASKBd2FYgIkAOCefEnMwiQB71sK8twR5AKix3z3LvIECge2K95\u002fQjQKKwiqhdnhdApBqwIX9fGkBjBAhPEWAbQNxyN8qJEBZAFRHupGPBHEBM28zFik0gQHsg478ihhlAx1QGiQRgIkB193H6ybMjQAAXe2\u002fpiiJAFofW\u002fyalIEAvhq2s3p8bQCAw71hf8SNAOICnOo\u002fwGUABkT73IBIUQDqKRFRq7iFAWuOaJsGiGkCdNtGT1T0eQPwUzYZdhhxA226rezPlIEAK2rKtZCYhQM6e29Px6RlALDlNZmBcIkBF51ygESIbQARdbLsmGiBAcPPGqWmoI0AA9vbuxj0fQFLy6PqqKSJAX2nTXbL2FkDk6uyOBjsgQBIKnNMwzSFADkUJljHpFUBRWFONQhkfQF7mpoKcuR5AS6GPzhyaIECWv1cvA3QiQCTJYvjoRh5AFSMr+II+I0DAVgHpCp0ZQDLZRRIwCx5AToyx0piwIECo1p8Myr0fQOSPrLgYpBZAM1UrnbGlIEDy552AKr4YQE8C39hIuiJAmPTyZN1jG0B9p4nAEOscQDBHdvtiPBZAATP1FnlSIUC9lsGRS2sUQHTHAX8sViBAxsOQHXPOFUD8JQEmPBgcQPrz2LeufxZAhNMjenowHUBT+7k7bO8YQASsYAOMfhlAef5a6KU8F0BMqKTYMfQZQIBwogq5mh5AsyQGXIyzHECm+hE9MjwiQLKGFnhocB1ADTMLPgRxFUCO9NdLNYEhQMy\u002fTuw\u002fwxpAeqAQGXgoGEDg1wTLsCwcQMC+x+kqLCNAQy3+JdgJFkDajzrsrUUUQCi4qErh6RxASsFgzXDsGkAtogkxtf8jQPLsdJgkxhxAKjnbSbMrIECWefuN6fMiQKbpd2eHxh9AbEz3spPGI0CA3RaqTq4UQJhk6nfe8xRAWbiqkx5oHkBbUE1wC4MjQLr9sg+1+CFAaXQ1CDK4GEBi8yblEzcjQOUy6xGLuyJA2vM+aS8eIUBDzKhWJ4UjQM2IV\u002fUqhxpAgZjH335aGUC\u002fCbav8yQYQBN9THrmgxZAykj1ErUCHkANqY5Z1e4hQPjVtvSFrSFAIIEFQhyPHkCW76\u002fZUPEaQEo256Q8ORxAblmeP4aiIkCueeDEm3UjQEXS4+kRdiJAjeNXaVm9GkDYxejIj50fQKO+bcSJdiNAr5cChkQcF0Dvt+qkTPcVQPShkHR4MR9AonlqcvnTGEBvTbvwc\u002fggQB\u002fmUxg9DBRA36HYc0QHI0A\u002fOMEnWU4cQI\u002f5IjbVpSBADkL2EA7EGkA7x\u002f0ADSEhQGOxHli7QBpAElvxWLuVIEAEPS8PokEaQGoP7+CiqhtAelK\u002fncH7FUC6io+f4MgeQI8O806nQR1AAJb6Oue1FEDH30BZI3gZQLSKZLXNqSNAt0nJeolIIkBu3y6iCqsXQLINEatrzRZAIvn0fdw4FEDA2IWf83UeQKEy+iUjBCBArE1hmsbGI0BklE0I9kwZQGM7pV3HHCBA\u002fhDNXYsOH0AhE22eArIgQIYTUgxigRdAXitsu7eYGkA9RXE5IPYjQDxwmauxkRlAfDYQL6rVI0BUgV+Ks3kgQG6AnJhXXR1A8or\u002f2hwSFUAIgfUlIwsZQDTcZVLumhxA6QjirXc3FUA10SE2ppkaQG\u002fchBsurRZAEKqnjjRRG0DbXS+XVOMjQCR+klfDvCBAcvT5sjPmG0AuEZiM9VkiQE7\u002f9nPMwxhAzUNaGpsYIkAMFl+iM4geQAbi0IVJkSJAXxa114t\u002fH0A4LZs7SxMhQCPR49QEkxtA1Jk77+3lIECI0aR07toVQIsuW1P1hBdAR4MfDSR2IECzOdYNDI4hQOpD59D\u002fEBZAiERnFZezHECEaCFOJH0iQJ6LD6MStRpAIPg8FEDaFkC77pOHxHsXQIKYaKpe0hxACHYgLLeVH0CMjGdWsu0gQAp8kRBXiiFABnaqzwIiHED4o8ohaUYjQB6ez25KZhVAODyihKQZGEAVl18M61sVQGTFlTpa1iBANQ9RIDogI0A0b3ba87kVQAxFuokw1xlAVn0YdOV5IkCZF1qq3xEYQF70crxSgRhAmjv\u002fDYqqI0A2Tf\u002f9wFcUQF0zMnYpyBVAp+Va3dnnI0DrnbA6MMwgQK2PaoBKXx9AvDk4yCcMHkCMHtGK3hUiQOBifysgRyJATjUTFEe3IEAgYn34c1QgQEvJtnn6nB9ArB0t3VRRHkCp64NUq68jQOwK\u002foo\u002fQiFAfOBrePfqI0DdiBMwugIdQOw4g57jZiJA\u002fmRSrrobIUDgwI46TQMcQLbiHMtOfyNA\u002fKVcwF8gFEC+RWrXTB8eQOZRReUF0xRAJG5asaiVIEDakyC8ttwcQGbJjrgiCyJAFUL1ynSOHEBCG81wE28UQCrioPQFRyNA2PjDcDBsGUB4mAX+diMVQFdwfQyOxiBA9OHZFopzHUDVGh8\u002frCMjQMrNAYaphxVAaZIfeQx9HkBDOPxNx0IjQE9mdsWUJyJAX\u002fQc\u002f+2mFEAfi1InuigXQFDGmnvezBZA6PU\u002fkkx1IkCftvrATAcYQJhU2m0ywhhAa+DPV2YBFkBjxzSzkyYhQAdYVtH4tBtAQnTQUPrvH0Blcff07xgjQOomWdPlGRVAHftwUzmWF0Cu3UU8\u002fQMjQGNEGODnGyFAI9CO58OXIkC4uGz9+GkeQL9mwKcK2SFAiCDh8DB0HkA\u002ffgMr1AoXQCqEn79enR5A5\u002f4QbQF\u002fFUDo63HexOYbQKGlPexgAiNA7w5z3\u002fe7FUD7w0ihBSwWQPObsdTv1xlARBUncGKNIkBziwdUxmAhQIJc4VeMchZAyNWlhm3JGkD1cSz66tgWQH3dkv6F9hlAnJZtjukzHkC+gt+\u002fyXceQGuFps\u002fhsyJA5RxPbzDJHUBngvyx1AkcQPq8r9pjxhlA6lCggb+pIEAkljYd5PkUQAoR+A4uZBxAepH5CPOWF0D2xOpi0owXQOzzMRoF\u002fRVAimoq9\u002fDjHkBCnaUXrbgbQBvV9Dlc+RxAzhvtX0bDIUBeDedw3NgiQI9ETzyi9h9ASi4PNTLaHUCaJ5fLdK8gQMj5UGSR6xVAUmrCJdbYIkAZMNcR95keQKkPSNYCCCBAXFQQu7B0IUDjTOlgmtAVQDw40sx3lyBAHcAuNNttF0Cz7w7mKb0hQH2I2dYpoBVArnEF66FxFUDiyzTxOfsVQD10LW8mDRpApmGJNN4AIkDWaZ3JVGcdQDlgj\u002fvhrhxAso53sF8EIkB4DnaJeHkiQIxYL6+8JyBAWHPi7bYIFUBHMGhAEY4dQH\u002fu5BowQCBA3KbYZVFMGkDeyXgP+iwhQCIhfAhIyBlApYieNe1bHkBjEq8JlEcZQIsVC53D8x1AKiHzjaXiHkAcYAKff1EeQOIpVwfIViFArTruYthVG0DnODsdngYeQIqpzBDaiSJAetNrtQUVF0Cs5RrCv1MXQPy\u002fs1wPBx9AcVZi3gmMG0AAcSNO7GAhQO7DrwgelBdA4I9\u002fCbbRFUD02rnmUXYiQA0nAofWmxZA9lpQ2knmGEBIzd08bWMjQHRwoyEaExxAnDR0LaLVFUANmCOh5FUhQJSfonbdfxtAUjbX2LijF0DaEYkjMwgfQKvTLhO59x9AAdAuHiVqFkD+wSLgFSYhQM4UO56kBBpAwBrh7AVxFEBnSg21iIIdQEU3tP5vTiFA\u002fH7caY8FGEA1ZCiKw6MaQNRXiCFsAhxAHKfehkc5G0CMiz3qO38XQCjWrBo0vB1AHUNHqkXNGED42SOQswwWQINqqlljQCFASO0cCsKpHUDXqSebilQZQDc5kkkoYxlAQvk0RIkGG0CCYbCOrWgeQIA\u002fqE12PhtAcXFUY4VkI0AOyXgkkBIeQKJvQPHYtiFAe0EZtDaYIUD8\u002fQlH7V0WQHM7peTEYyJAijXMZdN3H0C+JmlrDr8eQB5qjSf0CxhA9uOfyXiRIUDyfwSTBPkgQEfe0AG38CBAZrKTYsMAFUBS7s8vM4YdQP8kigLgehdAKT1cehiLIECGFsISqFcZQEtuU8KPBSFADHYpuE\u002fUH0Bzii+nNJogQBKLWoeEPRZA6JhcA0n8HECo043e+PsaQKiwTkQ\u002fhiBAYIbQ2Ag+I0BAUs3h0hUVQFMSLlE9sCBAa858w+OnFEBUZbCvilMZQLXSXhGnYiNAXrgm5HR0I0AtIFA36OMiQL7jBurC8BpA+IKdy5PfIUCyBWnn55gaQCBSf8phhhdA0Wa39GivHUAWl8V29VsdQN0iq6mx7CFAgOs7Eh6sFED2xGaxhMkVQMnA1q8QoBtAKjYhWCp0GkD+bx8TmFMZQI3ZsRBNnCJAqemy4d0BHEAm4+7jnmAbQC7zeSXclRpA6d6Q6mIlG0Cp0Zf1k10WQNQhSlVqBxxAHAGbxJfFFUC2J3iKX1cYQJwxxnwaMRRA7ZKHBBWwIEDKKimWtkohQHaPE1U0cyFAM3OghPxkHkAgTj3nsEoYQNTXKBxhYh9AOxQ8ny92IUAM9NL\u002fsfIjQHSF5K38QBpA2FyPhk6SGkAk3MpoeFAUQAPDfMo61B9AuD4ny5LdHEBcOdiDMTgUQEageTdcbBVAkiFjKsK8GkDdjkY8xpkgQA=="}},"mode":"markers","showlegend":false,"x":{"dtype":"f8","bdata":"ZaZJYVHlA0DmuLID8asAQLKPz+BPTdA\u002f5f5a+KGH6j80JHzLf1HsP2LrYV2TLwNAzIeQle4k8T+KwUeudAkEQH6AjqCL0QVAaja7ae7M\u002fj+a\u002fNdmJ+r0P6oQfmdibfg\u002fynzdDP+b2z9k07IszIvlPwL6k9GlJQNADJnTzltQB0AVAYZ2zG4DQDIaHeZofQRAE6ge4I1d+z+UCLa4WIHzPwjSkmGUDrA\u002fFP\u002fx8mgF8z+A0ydwAF7PP8+iGoSjlwNAXXn9e8rl5z\u002fgCExiRNIBQONJe\u002fR0+eg\u002f1LRMUcCR9T+57Wnf+0cBQOxPFX6s8gJA3YnoU3hsAkCqzPCNmPz1P179nEf9P\u002f8\u002f1+uIDtAVAkBqDbwN8eMFQMBw0mhajPM\u002fLJMqd6O+8T8Ea\u002fPpNGnCPyDwxEL+vgRATJnORxoBBUA6JZUI\u002f8v2P\u002flJS3BKWwBAbUkXrjhJ5z+O8Mtkq\u002fj5P9pKkjl3cPQ\u002fqFbcxuHy3D82CojwFjcGQIatSsHD5+0\u002fUyL0tx40\u002fz\u002fm8YEgKnH+P0zlKQoR\u002fgJA2GY9A3PI\u002fz+m+zSwPu\u002f+P7mcLhcCdf0\u002f4UxKp4IVAEBeBvaxAZEGQKD8JBQ+b+M\u002fMI9ipyinrj+G8JNULukFQES\u002fNrlICf0\u002fijKrOxMK6D909Bir4Rn8P4UTS8y0BgFA3O0Dbt209z9hG0KsbnQCQJ7QJYDoOwZA59YRMkP6AUBDY1\u002fPlOgGQPDJnszvLuM\u002fkPnByo5l8T+FqLY241r\u002fPzQaYz5vxAZAtmf\u002f9SfX\u002fj+8M1SHJrUFQHT45EAd9QJA8X8jCst5\u002fD9rW6dEZz4AQGqsYNCPUQBAIglU6pkc+j8O0Lx8tdDjPyKuYuGqftk\u002fEWhIG\u002fkS6j+nubUD7nIEQKxJsvivteQ\u002fZh5lXNUQ9D+Ma4fa7ET3P\u002fRJzCV1Vfg\u002fPXNWwDyX+T\u002fZcT\u002f9z\u002f\u002f9P5oQ+dID5vI\u002fcHO5LZ\u002fB+j+u2ezZnxf0P6p4eMrFh\u002fs\u002fvhwSxSV21T+GT9OIjx0AQDu+GFcwzwJAuuSNDdef+T+vGUl9XZPsPxqOXI4fk\u002fk\u002fEt3q5iAlAkCaUelOu48GQKFruUniL+o\u002fOHPEHrGc\u002fj81roPeI5vzP4CXfoUegPo\u002fFl4eqEOb8j\u002fyYx1BWBnjP0mh5fVJevI\u002fmt84a6b6B0DmaDZRgOfxP7yQ+ZKK\u002fQJAhR6q4jXUA0AwUHFh9ZrmP6bEkeD24PI\u002fpfqGG0ro6D9SrmZZvfn5P617cj6t+Pk\u002fAtKPombCAEAIz7j+22kHQIjwJicF0v8\u002f9FIlLAGaBkA4ae+Vo+XsP5kRN6ETqQFAQ+ZMMmqR8D\u002fpc+KGkxHzP0mupFe20eQ\u002fRycll1mH6D8bcc7al3PsPzoCPtsuFgNAUPC20QQjAUCfG2ekqeYDQMPopcVK4QFAavX9OsN88z\u002fOPq1Xqfj+P\u002fuZAfDKRgRAfI7EhOMVBUD4\u002f07cyQy4P\u002fuaEjay3gVAGtOVviZU9T9Skr0xq6HiP2gHXiBiFwJAtJdZLE+34j81FmF63Z3oPykCwpQxWfQ\u002fEIQ6gkeB+D+AMmtJGi32P7WGN9J1rPY\u002fGcDF2Drb5z8igcQg9pPwPyhaBeicsrY\u002fwM37H1fQoz9cHNBkuLHIP+Q4u+aKBfs\u002fN5SBADuYB0DN\u002fBCTwZcGQES7vYWJXMY\u002fVtFO6L0g+T\u002fmxu9uy+baP12ufoz5n+k\u002frKQNF67tBkCCo75Ksm\u002f+P3p0cBefIOI\u002fQKq1f9q3ij8jHp9hryT6P2+VWVrCDvk\u002f+FsJwpvt+j\u002fOzCYOeoYDQCaqMtD+LPg\u002fJrPNvMZk0z9wCfpDsbiqP6qCqyYCbAJAaCutMuUb9D9cLTnOZb8HQDg+gHvAsNk\u002fjF0bhK0Y1z+yZCGPqfIFQPRsXUxxo8w\u002fRriHyWe4+T+QLhULnafOP64IXtao\u002fQFASuS4VtNf2T+Q3HPLLNUGQDh1Dgu8Rek\u002fHkeoCKPU0T8IQhqMit\u002fdP4cO8zGUdeo\u002f+clG2KP1AkCslyTMFWP\u002fP32GaiCo7ABA0H5Q8uGBsz9GyJDmg9YBQCCPlLZDogRAVki1\u002fTXFAUC8oS+JjQb0PyDi+08GybU\u002fipMGiOd9BkAgZizYBgj4P+y78YN0HdQ\u002fg\u002f8UgZy\u002fAkAVLYKorQnrP16zwP1WWANAvTDFI4nB+j8QrgzrNt6pP5kD0z\u002feNAdAbPge8aX6AUD5O6mjSdnrP8h9Wcuz9Mk\u002fLIV4NetsA0Ds0wRETFr\u002fP0555dkm\u002fAdA\u002fPtRWHT28j\u002f6bPRHyP4CQLB+lKbkdeo\u002fvfpAOHly8z9NoZMPtj7pP8oXWpPrbf0\u002fz6M6CLQ3AkDuSDgupw0FQLNtC\u002fcUbeI\u002fSLoRyYIn2z8rmTuT6hMBQAlENslpRek\u002fPM2USDkR4T86FStCNKwHQKq88OSwaPY\u002fA+e1HcyNAUDAhKPPzqXeP\u002foNZOIssPs\u002fg+Zj1RFPA0AAdIQK522dP\u002fJOKJKKNfI\u002fYLIkqq4KA0BeBsTEPYn3PzTg\u002f+87PsI\u002fFpdHapie8z8uv3FZ8Q8EQEr9\u002frd\u002f+fY\u002fkJz\u002f9xquBkBpFwBsgtzqPwjV2j7ei9E\u002ffyNZyKYk\u002fz+y4nNxi0YGQN0eeVwVRAFAyyLKWh6o6j8esYZ5hDUBQJWaq\u002flDTO8\u002fhHEqIgKd\u002fz+OHcs6Sp74P\u002fEoJXwjKgNAOsV1hUXi8D9J9PSp1er9PydnLA8O4AZACPMIgnQEzD9b6zAb023qPw3pYE5o0fo\u002fVtNm\u002ffuu1T9yPp2fAr\u002fkP09hxV5pD\u002fw\u002fBnImapkf3T\u002f4LIEgbn7APygmZ\u002fCNu88\u002fmI7faU1A0T\u002fmupa79zXxP5GkSZ9FMPE\u002fQFE+uLiZqT9\u002f2dbLXX72P8oZF2c5Lfg\u002feLsMehR5B0CSjvD007UCQNmd1FisI+Y\u002f8m5qYyTu9j9KWWwqZD7aPwzh7jbxegZAcMNvLry6pD98f6w0Fp0CQK56fBZo7\u002fA\u002fOJ4B0UPOyj9MdghIojDqPwiA2KmYbLc\u002f9PHUk15+3D8uxGCJUjoEQE1JNnuEzeQ\u002f+h17pQDn5T9o+Ikb\u002fdTwPynhfnTnm\u002f4\u002fxwYRUcAp6T+u3v12g8PyP3JiXGiQgv8\u002fGOo\u002fxnVi4z+UqqfijHAEQMzNCL56gv0\u002fShUSt7LmAkDN5MfgXPP1P3N5aEoeYPI\u002fZCashnM2+j9OTBnRqsHhP782zjXuMgZAQoomp\u002fxC9z+gEqhOFisFQLBsDwOvRO8\u002fHmTPoaZS9j+sNWdC7AvMPwsO0L+ix+Y\u002fMVF5sL4A5j+CnBF9Q2f9PzPgSzI6hgZAFrS5UvdC0z+U+2DouW3qP1IOZfE0lO8\u002f9FpG5gIS9T\u002fLsS7iLMz4P1peCFO4wQJAOLZuHf6o2j\u002fsNYwTLSEDQPjA30y\u002fFQRAtVOFZEAG6D9YLls6YbrwPyM7oRBXsOI\u002fMlddUCKcA0D4nHf+iebkP+\u002fAQhmt+\u002f0\u002fY41NdjuUAkDcKRCr5Vr7P4gjP4CYv\u002fU\u002fDiA6xnouAUADgVH8mg3sP2QyTLegi\u002fQ\u002fXAWrj4pW\u002fT9beN3LcjwHQCszw3hjVANAQKfKhlmeqj+FHx2URL\u002flP55Mnu7b9fE\u002f2eqQqlkP8z+iLCRTK8LUP+0M6Hf5YABARCxLaXoe8D+EOdTVgEz5P7wKgJa3\u002fwFAcHBGqWGy8D8i4Y9e4SD\u002fP63jGAbHseA\u002fCviL2Sw30D+khCIrLqsDQGBlwLQdQwRAWFXE3YabAECq72ZYCtvlPxSQjSeXO\u002fo\u002fEEZZEInb8z8HO4ZR1EzsP2j0r\u002fEuowVAggrmnJow1j8IZ2gUas38PyaZ\u002fbKxIQFAWh2VLtyP4D\u002fWl31g8lvzP3wnt7\u002fQePk\u002foiet2Y6y1j8LxqFBAjP\u002fPz5FUoX73wJAUr5qqjiVAUBKb5R43af\u002fP5qBTZbguARAgZCvVAW39T8mvYLxJy\u002f4P8hzIOTqzeg\u002fI\u002ffr81iR9T\u002fSfVPvAjrrPyhJOprDhP8\u002fdsolZwIL9T9omss5bbX7P1oQj1FJVvU\u002fjSTnPjk3\u002fj9l6cZYsMTpP3Rp8FK7egRA0l8u2Vcf3z99KxuGsSDqP0fl3OaDdAFAzuB1\u002feUMBkCOVnwZvgftP7LD92e0Ods\u002fHulOdAKlA0Akmu9SeSLwPwC3q53ldME\u002faK3P5wF+AUAspXt4xzL8PzR0KxEgMPQ\u002fEOnVj1TfAkB9WTlMeTblP1xK5GkCFPw\u002fiCRctYE8A0Dq+zywrlftP0CCKEgUgdY\u002fynt13lPj5j8X0B3X+QP0PwpPSjtzV\u002f4\u002fAF2zRIHtkT+SoCw5TgYAQGYyiUpOD\u002fU\u002fTLCxrbOABkCmlLv8qhLcP2CezFgBEAVAIKW3NV+GAkDgVaiqNwfXP0KLh8LBzvw\u002f87WfwG6pB0CcVXKTnMPzP+blzMYJRARA\u002fZJxBmDP6T+QsiBIf7aoP1y1gTxv\u002f\u002fE\u002fiouTYkOZAEDR2XLfTfvmP60psePbuwJARgypgY1fAEBwucPgLuIAQH49bZjbzwBALecwHu\u002f+BkD093bca1vaP3Q2rYG+Mto\u002fKDuV2kWJ3j9COoWSEbsAQPhEdxHmZrc\u002fej4QF71Q8T9yrx183WEFQIq7+I5Qj\u002fo\u002fxJmZYqnm1T\u002fUAVD74jDfP\u002fRUaSjRQ\u002fA\u002foeL9w4FR4T9waOCchiH8P9yC2Tz2SQdAqFCvs\u002fKyAUBPoDpMEdvlPwAtf\u002fOeLN8\u002fMqEGusm4AkDIaOIln1jKPxI3j1Tr\u002fOE\u002fKzASUGRGAEB\u002fdTTfozIBQPD4EklwJgRAxAE8m2a4AkBWKndw37P2P2p9ffzHBvQ\u002fdg8g7HB63D\u002ftvJc3H1H8P+J3mo3ANwRAT5MUl3\u002feAUAmcEUrIvPxP8rrFYUoZ98\u002f0zAMR1OJ\u002fT+CPWZRIjXsP+hHML9YPMU\u002fxK5SKoZrBkCWahlpkN3kP\u002fIfpXo4d\u002fA\u002f0EOUVbjZB0ApWEMiyiEBQGWsC2FRhgVAOOaDmen72j8W7rd\u002fUTAHQCIB68ebPfA\u002fcWv47kEg4z8YKyDH+WgCQKjllCbQmbM\u002fV9QlCQu7BkBAAJ+s4BQBQGJL+EvHjgRAYqgl\u002f96fAkAr5J1taEr3PzAHwmpZv\u002fA\u002f9L7Tu0Uczz+9Vf6h6z3hPzcMkKLz2ARACGw1Mxl\u002fxT+UxnlotAj5P5Dj2GnbJso\u002fSk4gOj0yBkD4UJLHEn8GQFzq40Y3pvg\u002fBBCl1pghBUAE6XAEyWj8P3xKlFEJIMs\u002fCUOD5Z2k+j9aj6Dfx3HhPxBXLjdYias\u002fD7JdzbwV4T8+PsttGwQHQCD\u002fTb8NCdE\u002fNbRQAzrtBUAsWSyrK7HRP0B79I8oNY0\u002fdFb4B0PKxT8Cx6nZrd7dP75VB2h8u\u002f4\u002fHmwpK9Pp+D\u002f0S7xenYgDQDrD6a1bXeg\u002fbbcRR34r9T\u002fTSbkebCnyPwUmQObrE+g\u002fXv5T4U5u9j964epGVXvnP6H1O7DRrQRA2\u002ftfX8VB5D9094tpfITgP\u002ft14u6zwO4\u002feJnMO\u002f0N7T81G8OpMkfhP4xrBTCPTNI\u002fyg79\u002f8gc9z9TXlNcxDEDQA=="},"y":{"dtype":"f8","bdata":"cH9iISbfuD+Gjzada6LYP1SVraWrBOQ\u002fECAcF5Ozoz+Il+rvJGPBP5jdCsyiguw\u002f\u002fpXtDf1J7T8toa9MU0HlP9Bl09IPQMo\u002frJaFQHoB2T8EfztEDL7fP1B4biaYqKs\u002f8Y5ciKeu4T+BmmzIr\u002fnjP\u002f17i9rnHuQ\u002fYJ6kIeG45D9YM\u002fkgeKfpPwJEWuewVdE\u002fk9sZfvw37z9UTeu+\u002fKfPP1TKGwjRM+I\u002fdNlJRqr67j+oT7Pb+erNP7UrdQP5j+4\u002fqHphX9b82z\u002fe7m4XazLaP4uHWj6bSeE\u002fWN0LYH5O5j\u002f7h6NNjVTjP2LNky4\u002f0OI\u002fAOSoFsH+4T9MEdoEV7HdP4zdo5o8acs\u002fWLXLEtb71D\u002fcUdd\u002fiInoPyIYSEPvH+E\u002fzl+Vsd\u002fs3z\u002faVgqX193UP+e3xz2mauo\u002fmlqZDd533D\u002ffLu3FKQzkP+AQ74rtHq0\u002fyErpWFMi2D9cQrY7AbnvPzfEmPqu6+4\u002f8AFGDuCL0T9MwLmT2CDbPwIH4KACqdc\u002f+5uufM7d6j84+1xqANPLP+i2Mfi7o7Y\u002fm2Kn6Jz07j9809mlCPjOP54\u002fIDVCieI\u002fmDDJiWvT0T8UW1KYRN7FP+CjraUE67Y\u002ftKYkr2Oq7z+CdXpZ4ZDQP9pyrPmXj9k\u002fZCVaCHVI5j+UuIKpHrPNP8FJh4XB+eE\u002fWFrH33rawz91vyrPiQPnP060T1Ztd+o\u002fkD2WzmWY7z92xccJ6OzuPxjlL+h\u002fILg\u002f3jfF3XJA7j\u002fQBAairMjGP\u002fyppg9LtuI\u002f1gtecfMu2z9grdrfO++3P1rp\u002fL4E3NE\u002ficU\u002f9sDl4j\u002fI\u002fkfy5NPLP9ASF3TbpLE\u002f1xnsJqN26j\u002fh\u002f8fVBCPoPzzzFtLmWu4\u002fONxzPlIAvz9EE3j3ml3KP4ioPoq+lLY\u002fHGUtBbx2wz+8TDIpyQjPP+TvEU\u002f13Mo\u002fztcZP8v71z8kebOR93nfP\u002f4MxL09I9M\u002fwPGgtHS67T9Aud3x86m7P\u002fENg7ueveU\u002fftxPg45h2j\u002fnKhdSL\u002fnkP\u002fDIcMkyI8s\u002flJYcUnB67z\u002fAEmujlo6GPzYIGwfLgdg\u002fuESpPDAQ7z+A6X0SLZN2P07yR25fO+4\u002f57ZHr7Q26j8hTqrmioLpP2RGC86\u002fJOs\u002fEKJAXARZ4z\u002f0Nrxn7mrRPwTnRDGi8so\u002fSks1\u002fVHp4z9ZHnJOCvPoPzCvYHSL18g\u002fEm37s7wI7z+TOF2Qor\u002frP9MKm1HRPuA\u002fmoI6\u002fCAh0D9yupQQ4v7aP7hdpAPzcN0\u002fzBLTXgEI2j9Qk6ity4S0P\u002fBAycg7P+s\u002fUK3jxFxWuj+UVccMwdDcP+DqHB6Cfpg\u002fEKy5CQCyuT9yU2MzVTToP9myjRnB0uM\u002fyLFVl1Wm0D\u002fgDcPiu9GfPxH9fO8zruo\u002frggXDEvr6j8g67riwCebP1I1ud+piO8\u002fWIKX21Uf4j9GmSLnQ7vdP5geXK1iKdU\u002friR6MrW70j8ifdiFQTDiP6QWLSZuMN0\u002fdA7Ugoff6D\u002fS5KvSFlrdP+FylSZtMuI\u002ftlJUCo2P5z\u002f8ezEULwbuP2BayNz4qZ4\u002fp1UXea4g7z+dk5g0dnXhP7CwkMi+Yus\u002fXsyhqhia1j\u002fSSWnRyxzcP3A2uJ\u002f9b9I\u002f4MVYpjxs4D9rpsVMgz\u002fvP7ue6UoXWug\u002fjYWHKkVm5D9QBeQ5OTHMP+0UktAT4eM\u002fGtxIwRGJ2j\u002f+eAKX0d7iPyHVIt4\u002fuOs\u002fu+HucEwi7z\u002fQsI13+L7YPwUsDru6keI\u002fBNfetb8+7j+JinLERcvgPyppFoPWz9o\u002fwMnh6ho+0z8xiddjfj\u002fqP\u002fiHRnJzTrA\u002fyGrZCaSQ4T++qptXy7XnP8CbBlsaJN0\u002fUyDnGQrN7z8gD4gxk1GZP96CTrjbouc\u002feLc17B5m5T\u002fg0BSUYFnjP9QZzqkKycY\u002fRTi6X3cK4D+M7mg+qWLQP9SiS3EVr8A\u002fDfe+V\u002fDx5z9OsA8satfhP24zYk5BNtc\u002fuQHk1sOA6T+YC1MIh9+1P3Gkh8mZDOQ\u002fiBC\u002fI30nzT8PwGvS2aXvP8q13tQj\u002f+o\u002fQf7MoTNq5z8wd6Ve8IrLPxMD+kAaoeU\u002fEKsRujmfpD8IjAKNKeLcP3bbgmh9ct0\u002f7CP0SutO3T+6L0FewzraP94r23k4Gug\u002fkA7Zs4D01z966TgQoJXZP\u002fQOOWuAt8o\u002flH2MHOm3xj985pPXuQrqPz1bPZ6jGeU\u002fuAJJtvpv2D8UgiSPNo\u002fhP9h3D6rGqL8\u002femiHLsqG1T+QjyuG7kiuPyhROUq46s0\u002fxideE6GQ6z+MMh\u002fXaTHUP9+Ar+yC6Os\u002fABDP+TiDVD9wpfLBg36oP6snBDazge0\u002fYbDcu0Kx5z\u002fEX6O3epLuP6ch2UUe1uI\u002fmBJDPCd\u002f5j8hdewsyxTrPyAx+0UR2t0\u002f8Hi0XGUlqD90A1XI\u002f47uPzjoENg4Suc\u002f2+FEXcL97T8nsWUbyNzoP1\u002fSgreNIeY\u002fAYhGXhAY4T9wBJim1hKlPzS3xEW8se8\u002fh3K5iGei5D8hiNiqc0XrPzyVssiuVdY\u002fC+uxdA0Q5T\u002foygoEQK+4P5jKznpljME\u002f8C6L61U4vz+IXlE9P8nZP1hSbOM\u002fneI\u002ffPNQ66Pt4D8aejNXdtHTP3uClad6kOE\u002fyLfTs6Gt0T9nmWXJJzzoPyhKT1wBlNI\u002fwI\u002fNFdX2qD84nvHFGUPkPylxyUVyYOI\u002fDRZKQz045z+sqZ7+7WjcPwCMdWKOssA\u002fhLJXtHdM6D\u002flKuNZTybqP4ShawGGAdQ\u002fBGRNkQ\u002fd5T+8ud+JkoTGP0SPwP6ZBe8\u002ffldNWju+0D8wuXC7pXe2P4ANy+iR73Y\u002fOPEeEL3J0D9ovzKSle+xP3zhxpYR2Ng\u002fqHSQD4rrtD\u002fIdod774TKP60+bRLAou4\u002fHJPFTgLH1j\u002fo36zGcti5P5J6QiV9IO4\u002f8I7UIBAHsT\u002f3oXyXEFvjP3Yi4lCrS9s\u002fPXF4AAwn5D834YLRLefsPwAtfN5KvL8\u002fxg2AXmhx0D\u002fwtaT1GGTaP9fqpbQjA+M\u002fKTe+UfOO6T+6VsN4r4nVP9gYYrSI5dc\u002fdD9XVU0LyD+WyLbUe6jlP6pC7bx7gOM\u002fNnyTie3s0j9llOohdgXmPwzHPhqb7dc\u002fymccnhfW4j+WPguxVNfQPzTJENPY1u8\u002f7EjLTNid6T8Gmn1jCk\u002fXP7yUknDeat4\u002fvLU+7Vw5zz9g0nw9KHmkP1jNvtYGJ9s\u002fhPL5Xydz0T8tq8zuPyHsP9WorCa\u002fXek\u002f+yp9nS+D4z+L5nsCTCzqP1x+ul+5S9U\u002fGOGvX4ss2D\u002fNlsUH86zlP6GEdPrT9+g\u002fxzc2vxjB6j\u002f3TZXHGvfiP\u002fqS2rRvlNU\u002fS2pHvDZw6z+7GMLh3gjnP6a9x2Pkpus\u002f1JmpT1pEyT82h2kTdEjvPwRhR9aBC+8\u002ft+6jIIBI5D+1bAwI8K\u002fqP8JAPZFRT+c\u002fF9c7tfGS4j94Y9BCflrHP\u002fAu4cshTtY\u002fQCfDNwoyzj+ECan+0k7TPz7jqM8VBus\u002fsry0Btuj2T+xEei0XG7hP2+p5pw8We8\u002fCvTCJcvQ2T\u002fwfouU627bPzbL0rxeSNE\u002fSvZDmHWd2T\u002fOPaySqGDsP\u002fDMavHKVaI\u002fotuclkDR4z+7658CHo7oPzoYtbNUdu0\u002fLYe\u002fKfc45D8tN2BsZTXnP7Qt+yVxA+4\u002fktIsLr3r0D9LPsWs7NLgPynH+wIJDek\u002fEz4WIunp5D\u002fMJsCb3wPaP+BdtHmEka8\u002f1KKh5c4w4T9Qz1Pi9Q7NPyCSkwTGiKI\u002fAHd1k59Jmj\u002fETynUKPfQP8yuZX4PL8Q\u002fO34TJFJl5T\u002f493MDCMzlP\u002fP8xwg5ue4\u002f4kwd07cs7z8R4i9OWOPlP2zQQhWUPMk\u002fgZDjica65j\u002f5buYTMw\u002fqP1wWrHambsg\u002fBmmP26wL6z8IOCL6pqHAP+l99vABZ+E\u002fpn0TJ4f95j+o\u002fE+7ary7P+48GjvJ2e0\u002f7KoB3R317D+4Fk13HA7eP+KPVIJYB9Y\u002frMwFaa0v1j8YJEwtU1G8P+ANgKuJzJc\u002fhNeUC3cA7z9ouBLOqhHIPwwWxX87bMs\u002ftNqvNXZTzD8T+0lIctjtP+VSqo8GF+s\u002fLCmwuSJS2j+4BaK5THXNP2AMmPbWMaM\u002fEMWQhPFLsD+GsvylVHTfP3Avp7NfE+s\u002fuGlXKH5B1z9CH+Z8AZXpPyQlnXKBTdY\u002fknVPVpdZ7T8GCn73ApjmP76+hgRAHOc\u002fQFh\u002fVeUMlj968sO6rhHoP2okUF9bEOc\u002faF4i93G42T9sjO9tATvjP0j30CRKfr8\u002fmItrKbjVtT9EMx5fNGLYP1MCmbF8beI\u002fEN9P0nKp7j\u002fokXOePEPrPzQLIcE\u002fysY\u002fAdPXAz8B6T+oUQMEaQDjP8SwQhBw6c4\u002fHK826V3M1j8g2A3piu7HP7z+tsYFKOY\u002fcMY32CBdyj9Q1f\u002fP8cTPP7pT3dtmxe8\u002fuOkUhe4Xwj9IZ79sJ5jcP6agvaiiUeA\u002fQJTXOuicxT8oI53C2N\u002fBP6onWAhfWdY\u002firqTNAz57z\u002fA1W6genWbP9kF6+qQQek\u002fqzmLJeAk5T+gt63ohemjP4BDryTkCag\u002fQJbkO5iFmT\u002fgUkSQHtWaPxxFMGEpEMs\u002fFiyHyWiE2T+Mmq6o4sHZP1cp8LhVZOw\u002fgc9gLcDT4j9Z2o0L2E3kPyZciQJCg9Y\u002fuMj3k8Kgsz+2hlIUbdvWP+RQp+OPPcY\u002fTt6vELP27T9WrCHNd1rrP5rAm9VuYtQ\u002fsori2n2L1D9y54Y7rX7eP2JaN3aTGe8\u002fyDFd\u002fdAszj8QdeVhZ7W5P0gFtI0k988\u002feo85vECE7j9inVyB81LuP3aVZMxledw\u002fIITWrUyl7T+vCO68XrziP3itbaIKA8Y\u002f3XgAe1vg6T8NQQDyv\u002fvoP9dJLYOM4+s\u002fw\u002f\u002fi7cYB4j9cD0m9c8nOP\u002feO9umqvOE\u002fZv8cZmZK5z9zeJFnt2PtP9vWRWTGEeM\u002fqMiVW22o2D8tvsAd\u002f4TkPzx385td19M\u002f4OqVv3zL7j8K6vYrATnrPzVfIJsis+w\u002f0T3W94ln5j+mwAcnzd3cP9hFs4pCO7w\u002fgik4z5Aq0j+4uMxSGuHAP6vFk0IUzug\u002fnHA74II2zD+oajhg2ZDfP0ire\u002fEy\u002fe4\u002fYIfRmK4Psj8KtKBD77XSP5AcJF5CFbU\u002fXDgDiW7l0T8YgkZUOWDKP75viylnQNc\u002fmODKQLSItT\u002f1GidFidTuPxNjBwjXN+k\u002feJoeeJs7vT+CHWu7SCLTPyCuqVjs\u002f5A\u002f3snCbF3+0z+AMzads2vRP\u002fpBsnkcsdY\u002fdG0oeU+AzD8ubss5DivdP83wQNRTQuw\u002f0HMxgeyxqT\u002fAVCzJ\u002fgC6Px\u002fF0Hf\u002fh+s\u002fDCqSMDpm1T+iU4Lx4gffP4ZdjcLVmdM\u002fuMyyKr1DyT\u002fIEwq60TXoP2aog8ykL9A\u002fzYZh8G1K6D\u002fpO4sG7CvrP9RIFLkqYdE\u002fcGClag11zT8AiLuKcY3ZP3htdc47Kss\u002fnqaBMP2C1T+qTzG670nrPx5wmwWI9do\u002fQDGvQ0Vjij+n6KEqWC\u002fnPw=="},"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"]]}],"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":""}}}],"scattermap":[{"type":"scattermap","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":0,"r":0,"t":0,"b":0},"xaxis":{"showgrid":false,"zeroline":false,"showticklabels":false,"range":[0,3]},"yaxis":{"showgrid":false,"zeroline":false,"showticklabels":false,"scaleanchor":"x","scaleratio":1,"range":[0,1]},"width":1200,"height":400,"paper_bgcolor":"white","plot_bgcolor":"white","showlegend":false}, {"displayModeBar": false, "responsive": true, "scrollZoom": false} ) }; </script> </div>
src/fragments/enterprisebanner.html ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div style="height: 200px; width: 100%; overflow: hidden; background: linear-gradient(270deg, red, orange, yellow, green, blue, indigo, violet); background-size: 1400% 1400%; animation: rainbow 10s ease infinite;">
2
+
3
+ </div>
4
+
5
+ <style>
6
+ @keyframes rainbow {
7
+ 0% { background-position: 0% 50%; }
8
+ 50% { background-position: 100% 50%; }
9
+ 100% { background-position: 0% 50%; }
10
+ }
11
+ </style>
src/index.html ADDED
@@ -0,0 +1,367 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <script src="distill.bundle.js" type="module" fetchpriority="high" blocking></script>
6
+ <script src="main.bundle.js" type="module" fetchpriority="low" defer></script>
7
+ <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <meta charset="utf8">
10
+ <base target="_blank">
11
+ <title>Enterprise Hub - Why you need it, how to get it</title>
12
+ <link rel="stylesheet" href="style.css">
13
+ </head>
14
+
15
+ <body>
16
+ <d-front-matter>
17
+ <script id='distill-front-matter' type="text/json">{
18
+ "title": "Enterprise Hub",
19
+ "description": "Why you need it, how to get it",
20
+ "published": "Apr 8, 2025",
21
+ "affiliation": {"name": "Hugging Face"},
22
+ "authors": [
23
+ {
24
+ "author":"Jeff Boudier",
25
+ "authorURL":"https://huggingface.co/jeffboudier"
26
+ }
27
+ ],
28
+ "katex": {
29
+ "delimiters": [
30
+ {"left": "$$", "right": "$$", "display": false}
31
+ ]
32
+ }
33
+ }
34
+ </script>
35
+ </d-front-matter>
36
+ <d-title>
37
+ <h1 class="l-page" style="text-align: center;">Enterprise Hub:<br>Why you need it, how to get it</h1>
38
+ <div id="title-plot" class="main-plot-container l-screen" style="overflow-x: hidden; width: 100%; text-align: center;">
39
+ <div style="display: flex; justify-content: center; position: relative;">
40
+ {{{fragment-enterprisebanner}}}
41
+ </div>
42
+ <!--<p style="text-align: cekter; font-style: italic; margin-top: 10px; max-width: 900px; margin-left: auto; margin-right: auto;">banner subtitle</p>-->
43
+
44
+ </div>
45
+ </d-title>
46
+ <d-byline></d-byline>
47
+ <d-article>
48
+ <d-contents>
49
+ </d-contents>
50
+
51
+ <p>Welcome! <br/>In this article, you will learn all about the Enterprise features of Hugging Face, and how to get them.</p>
52
+
53
+ <aside>Reading time: 10-15 minutes.</aside>
54
+
55
+ <h2>Executive Summary</h2>
56
+
57
+ <p>Hugging Face is the leading open platform for AI Builders - researchers, data scientists, machine learning engineers and software developers. 8 million AI Builders use Hugging Face, and created over 200,000 organizations to collaborate privately on models, datasets and applications.</p>
58
+
59
+ <p>Free organizations are designed for small teams who need to collaborate and share transparently, but do not scale to medium sized companies: in free organizations, members join with their personal accounts, and everyone has access to everything.</p>
60
+
61
+ <p>For companies whose teams need privacy, security, compliance and governance when building AI, Hugging Face offers Enterprise Hub and Enterprise Plus.</p>
62
+
63
+ <p><strong>Enterprise Hub</strong> provides advanced collaboration, compute, security and compliance features for organizations. <br/>
64
+ <strong>Enterprise Plus</strong> adds exclusive security and governance features, and direct expert support from the Hugging Face team.</p>
65
+ <aside> Companies in regulated industries should consider Enterprise Plus</aside>
66
+
67
+ <table>
68
+ <thead>
69
+ <tr>
70
+ <th></th>
71
+ <th><strong>Free Organizations</strong></th>
72
+ <th><strong>Enterprise Hub</strong></th>
73
+ <th><strong>Enterprise Plus</strong></th>
74
+ </tr>
75
+ </thead>
76
+ <tbody>
77
+ <tr>
78
+ <td>Collaboration</td>
79
+ <td>DIY Admin</td>
80
+ <td>Resource Groups<br/>
81
+ Private Datasets Viewer<br/>
82
+ Download Analytics<br/>
83
+ Extended Private Storage</td>
84
+ <td>Resource Groups<br/>
85
+ Private Datasets Viewer<br/>
86
+ Download Analytics<br/>
87
+ Extended Private Storage</td>
88
+ </tr>
89
+ <tr>
90
+ <td>Compute</td>
91
+ <td></td>
92
+ <td>Inference Providers<br/>
93
+ Inference Endpoints GPU Quotas<br/>
94
+ Spaces ZeroGPU, Dev Mode</td>
95
+ <td>Inference Providers<br/>
96
+ Inference Endpoints GPU Quotas<br/>
97
+ Spaces ZeroGPU, Dev Mode</td>
98
+ </tr>
99
+ <tr>
100
+ <td>Security</td>
101
+ <td></td>
102
+ <td>Standard SSO<br/>
103
+ 2 Factor Authentification<br/>
104
+ Access Token Management</td>
105
+ <td><strong>Advanced SSO</strong><br/>
106
+ 2 Factor Authentification<br/>
107
+ Access Token Management</td>
108
+ </tr>
109
+ <tr>
110
+ <td>Governance</td>
111
+ <td></td>
112
+ <td>Audit Logs</td>
113
+ <td>Audit Logs<br/>
114
+ <strong>HF Model Gateway (preview)</strong></td>
115
+ </tr>
116
+ <tr>
117
+ <td>Compliance</td>
118
+ <td></td>
119
+ <td>Storage Regions</td>
120
+ <td>Storage Regions</br/>
121
+ <strong>Network Security</strong></td>
122
+ </tr>
123
+ <tr>
124
+ <td>Support</td>
125
+ <td>Community Support (Forum)</td>
126
+ <td>Email Support</td>
127
+ <td><strong>Direct Expert Support (Slack)</strong></td>
128
+ </tr>
129
+ </tbody>
130
+ </table>
131
+
132
+ <p>Enterprise Hub can be purchased with a credit card at <a href="http://hf.co/enterprise">hf.co/enterprise</a>, or via a standard 1-year contract subject to a $50k / 50 seats minimum.</p>
133
+ <p>Enterprise Plus can be purchased via a custom 1-year contract subject to a $120k / 50 seats minimum.</p>
134
+
135
+ <p>How do all these features benefit your organization? Let's dive in.</p>
136
+
137
+ <h2>Why upgrade to an Enterprise organization</h2>
138
+
139
+ <h3>Advanced Collaboration</h3>
140
+
141
+ <p>Enterprise Hub and Enterprise Plus organizations allow their members to privately host and share unlimited models, datasets and Spaces repositories. Members of Enterprise organizations benefit from <strong>higher rate limits</strong> accessing all Hugging Face services from the Hub and the Hugging Face open source libraries.</p>
142
+
143
+ <p>In addition, Enterprise organizations offer high storage limits for private repositories, including <strong>1TB of private storage per member</strong> ; for instance an Enterprise organization with 1,000 members can store up to 1 Petabyte of private files at no additional cost!</p>
144
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/storage-limits">Storage Limits</a>.</aside>
145
+
146
+ <p>In free organizations, everyone has access to everything. To organize resources across teams and projects, Enterprise organizations have access to <strong>Resource Groups</strong>. Enterprise organization admins can create new Resource Groups to assign specific repositories, add members and assign them specific permissions. This is great for teams who want to work on a specific project privately from the rest of the organization, or for different business units to segregate the models, datasets and Spaces they are working on.</p>
147
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-resource-groups">Resource Groups</a>.</aside>
148
+
149
+ <p>A favourite feature of Hugging Face for data scientists is the Datasets Viewer and the <strong>Data Studio</strong>. With Datasets Viewer makes it easy to preview the content of public datasets, and with Data Studio you can apply SQL queries to the dataset, or even ask AI to create and run them for you! Enterprise organizations make Dataset Viewer and Data Studio available to private datasets hosted on the organization.</p>
150
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-datasets">Private Dataset Viewer</a>.</aside>
151
+
152
+ <p>Hugging Face is the home of over 2 million public models, datasets contributed by the community. A great way for organizations to have impact and gain followers is by publicly sharing useful models and datasets. Enterprise organizations have access to power features for model and dataset contributors. Enterprise Analytics provides detailed <strong>download analytics</strong> for public repositories, easily exported via CSV. Enterprise organizations can also set up advanced <strong>model gating</strong> processes for Hugging Face users to provide information while requesting access to their public models.</p>
153
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-analytics">Download Analytics</a>.<br/><br/>
154
+ Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-gating-group-collections">Gating Group Collections</a>.</aside>
155
+
156
+ <h3>Advanced Compute</h3>
157
+
158
+ <p>When upgrading their organization to Enterprise, companies provide their employees with powerful compute options to build and host AI applications.</p>
159
+
160
+ <p>Enterprise organization members can use <strong>Inference Providers</strong> to use and integrate some of the most popular open models into their AI applications, with serverless inference powered by Hugging Face and Inference Providers, including <a href="https://huggingface.co/models?inference_provider=together">Together AI</a>, <a href="https://huggingface.co/models?inference_provider=fal-ai">fal</a>, <a href="https://huggingface.co/models?inference_provider=novita">Novita AI</a>, <a href="https://huggingface.co/models?inference_provider=cerebras">Cerebras</a>. Enterprise organizations get $2 free credit each month toward Inference Providers, for instance a 1,000 member organization can use for up to $2,000 of Inference Providers for free. Enterprise organization admins can set a monthly spend limit to control their costs.</p>
161
+ <aside>Learn more about <a href="https://huggingface.co/docs/inference-providers/index">Inference Providers</a>.</aside>
162
+
163
+ <p>To show what an AI model can do, AI Builders use Hugging Face <strong>Spaces</strong>. Spaces are hosted, interactive AI applications - over 1M public and private Spaces were created on Hugging Face. For companies, private Spaces is an ideal way to demo a model or application to colleagues. Enterprise organizations have access to advanced compute options for their Spaces:<br/>
164
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/spaces-zerogpu">ZeroGPU</a>.<br/>
165
+ Learn more about <a href="https://huggingface.co/docs/hub/spaces-gpus">Spaces GPU upgrades</a>.<br/>
166
+ Learn more about <a href="https://huggingface.co/docs/hub/en/spaces-dev-mode">Spaces Dev Mode</a>.</aside>
167
+
168
+ <ul>
169
+ <li><strong>ZeroGPU</strong> - Spaces can be powered by powerful A100 NVIDIA GPUs that spin up when the Space is used and down right after.</li>
170
+ <li><strong>GPU Upgrades</strong> - Spaces can be upgraded to exclusive large GPU options for the most demanding applications, including 4xA10G and A100 Large instances.</li>
171
+ <li><strong>Dev Mode</strong> - Dev Mode makes developing and debugging Spaces faster, connecting to the underlying container via SSH or VS Code.</li>
172
+ </ul>
173
+ </p>
174
+
175
+ <h3>Advanced Security</h3>
176
+
177
+ <p>Free organizations are designed for minimum friction adding collaborators, students, professionals to foster transparent collaboration. For companies that require security processes, Enterprise organizations offer many features to enforce security rules and make sure appropriate access controls and rules are in place.</p>
178
+
179
+ <p>To secure accounts with access to the organization's private resources, Enterprise organization admins can require <strong>2-Factor Authentication</strong> for all members of the organization, and require admin approval for new members.</p>
180
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-advanced-security#advanced-security">2FA</a>.</aside>
181
+
182
+ <p>To reduce the risk of members inadvertently publishing confidential information as public repositories, Enterprise organization admins can make new repositories <strong>private by default</strong>, or <strong>disable public repositories</strong> altogether.</p>
183
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-advanced-security#repository-visibility-controls">repository visibility</a>.</aside>
184
+
185
+ <p>User Access Tokens are keys users can create to give applications the permission to access private models, datasets and applications on Hugging Face. It’s very important to create and manage User Access Tokens with best security practices in mind - only give the minimum required scope of access, regularly rotate tokens and invalidate unnecessary tokens. For organization admins, User Access Tokens can be a source of concern as each token is a potential attack vector. Enterprise organizations can reduce risk and enforce best practices with <strong>Access Token Management</strong>.<br/>
186
+ With Access Token Management, organization admins have a centralized view of every User Access Token created by members of the organization. Each token can be inspected, and revoked. Admins can set up organization policies, for instance to disallow the creation of broad access tokens and require fine-grained tokens, and/or to require admin approval for any new token created by users of the organization.</p>
187
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/en/enterprise-hub-tokens-management">Access Token Management</a>.</aside>
188
+
189
+ <p>To make sure that members with access to the organization’s private resources are actually employees of the company, Enterprise organizations can set up <strong>Single Sign-On</strong> (SSO). With SSO, user authentication to access Enterprise organization resources is managed by the company Identity Provider, such as Okta or Azure Active Directory. Roles and Teams mapping is available to automatically give specific access control permissions to employee groups, and/or assign them to Resource Groups.</p>
190
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/enterprise-sso">SSO</a>.</aside>
191
+
192
+ <p><strong>Enterprise Plus</strong> organizations have access to <strong>Advanced SSO</strong> features. With Advanced SSO, the Hugging Face user accounts who are members of the organization are fully provisioned by the company Identity Provider.</p>
193
+
194
+ <p>With <strong>Enterprise Plus</strong>, the user accounts belonging to the organization are <strong>Enterprise Managed Users</strong>, fully controlled by the company. With Enterprise Managed Users, SSO authentication manages not only the access to organization resources, but also the access to all Hugging Face Hub public resources. Enterprise Managed Users only need to login once to access all public and private resources on the Hugging Face Hub.</p>
195
+
196
+ <p>With Enterprise Managed Users, Enterprise Plus customers can drastically reduce the risk of confidential information being published:<br/>
197
+ <ul>
198
+ <li>Enterprise Managed Users cannot create repositories under their personal profile</li>
199
+ <li>Enterprise Managed Users cannot join other organizations beside their Enterprise Plus organization</li>
200
+ <li>Enterprise Managed Users cannot subscribe to a PRO subscription for their personal account</li>
201
+ <li>Enterprise Managed Users cannot change their account email address, or set a password for their account</li>
202
+ </ul>
203
+ </p>
204
+
205
+ <h3>Advanced Governance and Compliance</h3>
206
+
207
+ <p>Enterprises need traceability and auditability of their confidential information. Enterprise organization admins on Hugging Face have access to <strong>Audit Logs</strong> to inspect and retrieve all changes made to their organization, such as new users, access tokens or repositories, changes to resource groups, access controls and more. Each log entry includes information about the user, their location, the action, change and time. Lots can be easily downloaded as JSON files for analysis.</p>
208
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/audit-logs">Audit Logs</a>.</aside>
209
+
210
+ <p>Enterprise organizations can choose where their data is hosted with <strong>Storage Regions</strong>. Models, Datasets, Spaces can be stored in either US-based servers or Europe-based servers, allowing European companies to use Hugging Face in a GDPR compliant way.</p>
211
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/storage-regions">Storage Regions</a>.</aside>
212
+
213
+ <p><strong>Enterprise Plus</strong> offers organizations advanced governance and compliance features, with Network Security and Model Gateway.</p>
214
+
215
+ <p>With <strong>Network Security</strong>, Enterprise Plus organizations can define their corporate network and precisely define which public models, datasets, organizations and applications should be accessible to their employees. Admins can require authentication to access Hugging Face, so that only authorized employees can access it. In addition, admins can define exactly which resources should be allowed or blocked, which is useful to enforce company model governance rules, for instance to restrict access to certain models or organizations disallowed per company policy. Also with Network Security, admins can enable higher rate limits for their organization, and unlock power users of Hugging Face.</p>
216
+ <aside>Learn more about <a href="https://huggingface.co/docs/hub/enterprise-hub-network-security">Network Security</a>.</aside>
217
+
218
+ <p><strong>Model Gateway</strong> is an on-premise proxy of the Hugging Face Hub for companies with strict security, compliance and governance requirements. The Model Gateway is deployed by the company within their environment, and acts as a caching, governance and security layer between the employees network and the public Hugging Face Hub. With Model Gateway, companies can:</p>
219
+ <ul>
220
+ <li>Allow and restrict specific model, datasets and Spaces applications</li>
221
+ <li>Define the allowed licenses for models to prevent downloads of non-compliant models (for instance, to prevent employees from downloading GPL licensed or Non-Commercial licensed models)</li>
222
+ <li>Define the allowed file formats for models to reduce security risks (for instance, to prevent employees from downloading models stored as Pickle files)</li>
223
+ <li>Define model security scanning requirements, and implement custom model security scanners into the Model Gateway registry</li>
224
+ <li>Guarantee model supply chain robustness with models and versioning cached in local company storage (for instance, to prevent models from becoming unavailable if their provider on Hugging Face decided to delete them)</li>
225
+ <li>Simplify team model access for Gated models like Meta Llama, Google Gemma or Mistral models - permission is asked once and the model is then available to everyone at the company via the Model Gateway local registry.</li>
226
+ <li>Increase productivity for machine learning engineers with fast downloads from the local storage system</li>
227
+ <li>Provide auditability with logs documenting and attributing model downloads by employees</li>
228
+ </ul>
229
+ <p>Model Gateway is a new feature currently in preview for select Enterprise Plus organizations.</p>
230
+
231
+ <h3>Advanced Support</h3>
232
+
233
+ <p>Enterprise Hub organization members enjoy priority support covering their usage of Hugging Face via email requests.</p>
234
+ <p>Enterprise Plus organization members enjoy direct expert support from the Hugging Face team via a dedicated, private Slack channel hosted by Hugging Face.</p>
235
+
236
+ <h2>How to upgrade to Enterprise</h2>
237
+
238
+ <h3>Pricing</h3>
239
+
240
+ <p><strong>Enterprise Hub</strong> can be purchased directly at <a href="https://huggingface.co/enterprise">hf.co/enterprise</a> with a credit card as a $20/seat monthly subscription, or $216/seat yearly subscription.<br/>
241
+ Companies requiring a contract can request a standard 1-year contract subject to a $50k / 50 seats minimum.</p>
242
+
243
+ <p><strong>Enterprise Plus</strong> can only be purchased via a custom 1-year contract subject to a $120k / 50 seats minimum.</p>
244
+
245
+ <p>When purchased through a contract, Enterprise Hub and Enterprise Plus licenses include a number of seats corresponding to the number of members expected to be onboarded in the organization during the first year of the contract. Additional members joining the organization during the yearly term are not charged, and the contract value is updated upon yearly renewal to the number of members of the organization at renewal.</p>
246
+
247
+ <p>Enterprise organizations can purchase <strong>Compute Credits</strong> toward usage of premium features by their members, such as Inference Providers, Inference Endpoints and Spaces premium hardware. Compute Credits can be purchased upon request via an Order Form, are applied to the organization upon payment, and are valid for one year. Compute Credits purchases of $200k or higher qualify for volume discounts.</p>
248
+ <aside>See <a href="https://huggingface.co/pricing">pricing</a> for Hub premium usage-based features</aside>
249
+
250
+ <p>To contact the Hugging Face sales team, please email <a href=mailto:enterprise-contracts@huggingface.co>enterprise-contracts@huggingface.co</a></p>
251
+
252
+ <h3>Contracting</h3>
253
+
254
+ <p>To keep the process fast and smooth, Enterprise Hub and Enterprise Plus are governed by our standard online <a href="https://huggingface.co/terms-of-service">Terms of Service</a>, complemented by brief <a href="https://cdn-media.huggingface.co/landing/assets/Supplemental+Terms.pdf">Supplemental Terms</a>. These terms follow industry standards and are regularly updated to ensure they remain clear and fair. </p>
255
+
256
+ <h3>Frequently Asked Questions</h3>
257
+
258
+ <p><strong><i>Can I get on a call with a sales representative?</i></strong><br/>
259
+ Hugging Face sells and onboards customers via a streamlined process relying on written async communication (email and Slack). We are happy to answer any question you may have via email, but do not offer sales calls.</p>
260
+
261
+ <p><strong><i>Can I get a free trial of Enterprise Hub?</i></strong><br/>
262
+ Enterprise Hub features can be tested for as little as $20 with a credit card, and we do not offer free trials.</p>
263
+
264
+ <p><strong><i>How do I follow my internal new vendor onboarding processes?</i></strong><br/>
265
+ Any vendor, security or legal review process requires to purchase Enterprise Hub or Enterprise Plus via a contract. Our team will be happy to follow your new vendor onboarding process requirements at <a href=mailto:enterprise-contracts@huggingface.co>enterprise-contracts@huggingface.co</a></p>
266
+
267
+ <p><strong><i>How do I set up a security review of Hugging Face prior to purchase?</i></strong><br/>
268
+ Security reviews are only possible when purchasing Enterprise Hub or Enterprise Plus via a contract. Please contact our team at <a href=mailto:enterprise-contracts@huggingface.co>enterprise-contracts@huggingface.co</a>, upon signing a unilateral NDA, Hugging Face will share our SOC2 Type2 report detailing all the information required in security reviews. Our security team can accommodate custom security questionnaires for contract values above $120k.</p>
269
+
270
+ <p><strong><i>Can the standard contract terms be modified after my legal team reviews them?</i></strong><br/>
271
+ We only allow legal review for contract values above $120k, the Hugging Face legal team can review change requests to be added to the Order Form, which includes all contract legal terms. </p>
272
+
273
+ </d-article>
274
+
275
+ <d-appendix>
276
+ <h3 id="citation">Get Started</h3>
277
+ <p>Upgrade your organization to Enterprise at <a href="https://huggingface.co/enterprise">hf.co/enterprise</a></p>
278
+ </d-appendix>
279
+ <script>
280
+ function toggleTOC() {
281
+ const content = document.querySelector('.toc-content');
282
+ const icon = document.querySelector('.toggle-icon');
283
+
284
+ content.classList.toggle('collapsed');
285
+ icon.classList.toggle('collapsed');
286
+ }
287
+ </script>
288
+
289
+ <script>
290
+ const article = document.querySelector('d-article');
291
+ const toc = document.querySelector('d-contents');
292
+ if (toc) {
293
+ const headings = article.querySelectorAll('h2, h3, h4');
294
+ // let ToC = `<nav role="navigation" class="l-text figcaption"><h3>Table of contents</h3>`;
295
+ let ToC = `<nav role="navigation" class="l-text figcaption"><div class="toc-header" onclick="toggleTOC()">
296
+ <span class="toc-title">Table of Contents</span>
297
+ <span class="toggle-icon">▼</span>
298
+ </div><div class="toc-content">`;
299
+ let prevLevel = 0;
300
+
301
+ for (const el of headings) {
302
+ // should element be included in TOC?
303
+ const isInTitle = el.parentElement.tagName == 'D-TITLE';
304
+ const isException = el.getAttribute('no-toc');
305
+ if (isInTitle || isException) continue;
306
+ el.setAttribute('id', el.textContent.toLowerCase().replaceAll(" ", "_"))
307
+ const link = '<a target="_self" href="' + '#' + el.getAttribute('id') + '">' + el.textContent + '</a>';
308
+
309
+ const level = el.tagName === 'H2' ? 0 : (el.tagName === 'H3' ? 1 : 2);
310
+ while (prevLevel < level) {
311
+ ToC += '<ul>'
312
+ prevLevel++;
313
+ }
314
+ while (prevLevel > level) {
315
+ ToC += '</ul>'
316
+ prevLevel--;
317
+ }
318
+ if (level === 0)
319
+ ToC += '<div>' + link + '</div>';
320
+ else
321
+ // else if (level === 1)
322
+ ToC += '<li>' + link + '</li>';
323
+ }
324
+
325
+ while (prevLevel > 0) {
326
+ ToC += '</ul>'
327
+ prevLevel--;
328
+ }
329
+ ToC += '</div></nav>';
330
+ toc.innerHTML = ToC;
331
+ toc.setAttribute('prerendered', 'true');
332
+ const toc_links = document.querySelectorAll('d-contents > nav div a');
333
+
334
+ window.addEventListener('scroll', (_event) => {
335
+ if (typeof (headings) != 'undefined' && headings != null && typeof (toc_links) != 'undefined' && toc_links != null) {
336
+ find_active: {
337
+ for (let i = headings.length - 1; i >= 0; i--) {
338
+ const heading = headings[i];
339
+ // Skip headings that shouldn't be in TOC
340
+ if (heading.parentElement.tagName == 'D-TITLE' || heading.getAttribute('no-toc')) {
341
+ continue;
342
+ }
343
+
344
+ if (heading.getBoundingClientRect().top - 50 <= 0) {
345
+ // Find matching TOC link by href
346
+ const headingId = heading.getAttribute('id');
347
+ const activeLink = Array.from(toc_links).find(link =>
348
+ link.getAttribute('href') === '#' + headingId
349
+ );
350
+
351
+ if (activeLink && !activeLink.classList.contains("active")) {
352
+ toc_links.forEach(link => link.classList.remove("active"));
353
+ activeLink.classList.add('active');
354
+ }
355
+ break find_active;
356
+ }
357
+ }
358
+ toc_links.forEach(link => link.classList.remove("active"));
359
+ }
360
+ }
361
+ });
362
+ }
363
+ </script>
364
+
365
+ </body>
366
+
367
+ </html>
src/index.js ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // import { plotClusters } from './clusters'
2
+ import { loadFragments } from './fragmentLoader'
3
+ import { syncHFSpacesURLHash } from './syncHFSpacesURLHash'
4
+
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ console.log("DOMContentLoaded");
7
+ loadFragments();
8
+ init_memory_plot();
9
+ syncHFSpacesURLHash();
10
+ }, { once: true });
src/plotly_example.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import plotly.graph_objects as go
2
+ import numpy as np
3
+ import pandas as pd
4
+
5
+ data = {
6
+ "x": 3*np.random.rand(512),
7
+ "y": np.random.rand(512),
8
+ "z": np.random.random(512),
9
+ }
10
+
11
+ df = pd.DataFrame(data)
12
+
13
+ def get_label(z):
14
+ if z<0.25:
15
+ return "smol dot"
16
+ if z<0.5:
17
+ return "ok-ish dot"
18
+ if z<0.75:
19
+ return "a dot"
20
+ else:
21
+ return "biiig dot"
22
+
23
+ df["label"] = df["z"].apply(get_label)
24
+
25
+ df["z"] = (df["z"]+1)*5
26
+
27
+ fig = go.Figure()
28
+
29
+ fig.add_trace(go.Scatter(
30
+ x=df['x'],
31
+ y=df['y'],
32
+ mode='markers',
33
+ marker=dict(
34
+ size=df['z'],
35
+ color=df['z'],
36
+ colorscale=[
37
+ [0, 'rgb(78, 165, 183)'], # Light blue
38
+ [0.5, 'rgb(206, 192, 250)'], # Purple
39
+ [1, 'rgb(232, 137, 171)'] # Pink
40
+ ],
41
+ opacity=0.9,
42
+ ),
43
+ customdata=df[["label"]],
44
+ hovertemplate="Dot category: %{customdata[0]}",
45
+ hoverlabel=dict(namelength=0),
46
+ showlegend=False
47
+ ))
48
+
49
+
50
+ fig.update_layout(
51
+ width=1200,
52
+ height=400,
53
+ paper_bgcolor='white',
54
+ plot_bgcolor='white',
55
+ showlegend=False,
56
+ margin=dict(l=0, r=0, t=0, b=0),
57
+ xaxis=dict(
58
+ showgrid=False,
59
+ zeroline=False,
60
+ showticklabels=False,
61
+ range=[0, 3]
62
+ ),
63
+ yaxis=dict(
64
+ showgrid=False,
65
+ zeroline=False,
66
+ showticklabels=False,
67
+ scaleanchor="x",
68
+ scaleratio=1,
69
+ range=[0, 1]
70
+ )
71
+ )
72
+
73
+ fig.show()
74
+
75
+ fig.write_html("fragments/banner.html",
76
+ include_plotlyjs=False,
77
+ full_html=False,
78
+ config={
79
+ 'displayModeBar': False,
80
+ 'responsive': True,
81
+ 'scrollZoom': False,
82
+ })
src/style.css ADDED
@@ -0,0 +1,599 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* style.css */
2
+ /* Define colors */
3
+ :root {
4
+ --distill-gray: rgb(107, 114, 128);
5
+ --distill-gray-light: rgb(185, 185, 185);
6
+ --distill-gray-lighter: rgb(228, 228, 228);
7
+ --distill-gray-lightest: rgb(245, 245, 245);
8
+ --distill-blue: #007BFF;
9
+ }
10
+
11
+ /* Container for the controls */
12
+ [id^="plot-"] {
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: center;
16
+ gap: 15px; /* Adjust the gap between controls as needed */
17
+ }
18
+ [id^="plot-"] figure {
19
+ margin-bottom: 0px;
20
+ margin-top: 0px;
21
+ padding: 0px;
22
+ }
23
+ .plotly_caption {
24
+ font-style: italic;
25
+ margin-top: 10px;
26
+ }
27
+
28
+ .plotly_controls {
29
+ display: flex;
30
+ flex-wrap: wrap;
31
+ flex-direction: row;
32
+ justify-content: center;
33
+ align-items: flex-start;
34
+ gap: 30px;
35
+ }
36
+
37
+
38
+ .plotly_input_container {
39
+ display: flex;
40
+ align-items: center;
41
+ flex-direction: column;
42
+ gap: 10px;
43
+ }
44
+
45
+ /* Style for the select dropdown */
46
+ .plotly_input_container > select {
47
+ padding: 2px 4px;
48
+ /* border: 1px solid #ccc; */
49
+ line-height: 1.5em;
50
+ text-align: center;
51
+ border-radius: 4px;
52
+ font-size: 12px;
53
+ background-color: var(--distill-gray-lightest);
54
+ outline: none;
55
+ }
56
+
57
+ /* Style for the range input */
58
+
59
+ .plotly_slider {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 10px;
63
+ }
64
+
65
+ .plotly_slider > input[type="range"] {
66
+ -webkit-appearance: none;
67
+ height: 2px;
68
+ background: var(--distill-gray-light);
69
+ border-radius: 5px;
70
+ outline: none;
71
+ }
72
+
73
+ .plotly_slider > span {
74
+ font-size: 14px;
75
+ line-height: 1.6em;
76
+ min-width: 16px;
77
+ }
78
+
79
+ .plotly_slider > input[type="range"]::-webkit-slider-thumb {
80
+ -webkit-appearance: none;
81
+ appearance: none;
82
+ width: 18px;
83
+ height: 18px;
84
+ border-radius: 50%;
85
+ background: var(--distill-blue);
86
+ cursor: pointer;
87
+ }
88
+
89
+ .plotly_slider > input[type="range"]::-moz-range-thumb {
90
+ width: 18px;
91
+ height: 18px;
92
+ border-radius: 50%;
93
+ background: var(--distill-blue);
94
+ cursor: pointer;
95
+ }
96
+
97
+ /* Style for the labels */
98
+ .plotly_input_container > label {
99
+ font-size: 14px;
100
+ font-weight: bold;
101
+ }
102
+
103
+ .main-plot-container {
104
+ margin-top: 21px;
105
+ margin-bottom: 35px;
106
+ }
107
+
108
+ .main-plot-container > figure {
109
+ display: block !important;
110
+ /* Let this be handled by graph-container */
111
+ margin-bottom: 0px;
112
+ margin-top: 0px;
113
+ }
114
+ .main-plot-container > div {
115
+ display: none !important;
116
+ }
117
+
118
+
119
+ @media (min-width: 768px) {
120
+ .main-plot-container > figure {
121
+ display: none !important;
122
+ }
123
+ .main-plot-container > div {
124
+ display: flex !important;
125
+ }
126
+ }
127
+
128
+ d-byline .byline {
129
+ grid-template-columns: 1fr;
130
+ grid-column: text;
131
+ font-size: 0.9rem;
132
+ line-height: 1.8em;
133
+ }
134
+
135
+ @media (min-width: 768px) {
136
+ d-byline .byline {
137
+ grid-template-columns: 5fr 1fr 1fr;
138
+ }
139
+ }
140
+
141
+ #title-plot {
142
+ margin-top: 0px;
143
+ margin-bottom: 0px;
144
+ }
145
+
146
+ d-contents > nav a.active {
147
+ text-decoration: underline;
148
+ }
149
+
150
+ @media (max-width: 1199px) {
151
+ d-contents {
152
+ display: none;
153
+ background: white;
154
+ justify-self: start;
155
+ align-self: start;
156
+ padding-bottom: 0.5em;
157
+ margin-bottom: 1em;
158
+ padding-left: 0.25em;
159
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
160
+ border-bottom-width: 1px;
161
+ border-bottom-style: solid;
162
+ border-bottom-color: rgba(0, 0, 0, 0.1);
163
+ overflow-y: scroll;
164
+ height: calc(100vh - 40px);
165
+ scrollbar-width: none;
166
+ z-index: -100;
167
+ }
168
+ }
169
+
170
+ d-contents a:hover {
171
+ border-bottom: none;
172
+ }
173
+
174
+ toc-title {
175
+ font-weight: bold;
176
+ font-size: 1.2em;
177
+ color: #333;
178
+ }
179
+
180
+ toggle-icon {
181
+ transition: transform 0.3s;
182
+ }
183
+
184
+ toggle-icon.collapsed {
185
+ transform: rotate(90deg);
186
+ }
187
+
188
+ .toc-content {
189
+ margin-top: 15px;
190
+ overflow: hidden;
191
+ /* max-height: 1000px; */
192
+ transition: max-height 0.3s ease-out;
193
+ }
194
+
195
+ .toc-content.collapsed {
196
+ max-height: 0;
197
+ margin-top: 0;
198
+ }
199
+
200
+ @media (min-width: 1200px) {
201
+ d-article {
202
+ /* Ensure d-article does not prevent sticky positioning */
203
+ overflow: visible;
204
+ }
205
+
206
+ d-contents {
207
+ align-self: start;
208
+ background: white;
209
+ grid-column-start: 1 !important;
210
+ grid-column-end: 4 !important;
211
+ grid-row: auto / span 6;
212
+ justify-self: end;
213
+ margin-top: 0em;
214
+ padding-right: 3em;
215
+ padding-left: 2em;
216
+ /* border-right: 1px solid rgba(0, 0, 0, 0.1);
217
+ border-right-width: 1px;
218
+ border-right-style: solid;
219
+ border-right-color: rgba(0, 0, 0, 0.1); */
220
+ position: -webkit-sticky; /* For Safari */
221
+ position: sticky;
222
+ top: 10px; /* Adjust this value if needed */
223
+ overflow-y: auto;
224
+ height: calc(100vh - 40px);
225
+ scrollbar-width: none;
226
+ transition: max-height 0.3s ease-out;
227
+ z-index: -100;
228
+ }
229
+ }
230
+
231
+ d-contents nav h3 {
232
+ margin-top: 0;
233
+ margin-bottom: 1em;
234
+ }
235
+
236
+ d-contents nav div div {
237
+ color: rgba(0, 0, 0, 0.8);
238
+ font-weight: bold;
239
+ }
240
+
241
+ d-contents nav a {
242
+ color: rgba(0, 0, 0, 0.8);
243
+ border-bottom: none;
244
+ text-decoration: none;
245
+ }
246
+
247
+ d-contents li {
248
+ list-style-type: none;
249
+ }
250
+
251
+ d-contents ul, d-article d-contents ul {
252
+ padding-left: 1em;
253
+ }
254
+
255
+ d-contents nav ul li {
256
+ margin-bottom: .25em;
257
+ }
258
+
259
+ d-contents nav a:hover {
260
+ text-decoration: underline solid rgba(0, 0, 0, 0.6);
261
+ }
262
+
263
+ d-contents nav ul {
264
+ margin-top: 0;
265
+ margin-bottom: 6px;
266
+ }
267
+
268
+
269
+ d-contents nav > div {
270
+ display: block;
271
+ outline: none;
272
+ margin-bottom: 0.5em;
273
+ }
274
+
275
+ d-contents nav > div > a {
276
+ font-size: 13px;
277
+ font-weight: 600;
278
+ }
279
+
280
+ d-article aside {
281
+ margin-bottom: 1em;
282
+ }
283
+
284
+ d-article img {
285
+ max-width: 100%;
286
+ }
287
+
288
+ @media (min-width: 768px) {
289
+ d-article aside {
290
+ margin-bottom: 0;
291
+ }
292
+ }
293
+
294
+ d-contents nav > div > a:hover,
295
+ d-contents nav > ul > li > a:hover {
296
+ text-decoration: none;
297
+ }
298
+
299
+ .note-box {
300
+ background-color: #f6f8fa;
301
+ border-left: 4px solid #444444;
302
+ padding: 1rem;
303
+ margin: 1rem 0; /* Keep this modest margin */
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 {
311
+ margin: 0;
312
+ color: #444444;
313
+ font-weight: 600;
314
+ font-size: 1em;
315
+ }
316
+
317
+ .note-box-content {
318
+ margin-top: 0.5rem;
319
+ margin-bottom: 0; /* Ensure no bottom margin */
320
+ color: #24292f;
321
+ font-size: 0.9em;
322
+ line-height: 1.5em;
323
+ }
324
+
325
+ /* For dark mode support */
326
+ @media (prefers-color-scheme: dark) {
327
+ .note-box {
328
+ background-color: #1c1c1c;
329
+ border-left-color: #888888;
330
+ }
331
+ .note-box-title {
332
+ color: #888888;
333
+ }
334
+ .note-box-content {
335
+ color: #d4d4d4;
336
+ }
337
+ }
338
+
339
+ d-article {
340
+ font-size: 1.0em;
341
+ }
342
+
343
+ .figure-legend {
344
+ font-size: 0.9em;
345
+ font-style: italic;
346
+ color: var(--distill-gray);
347
+ line-height: 1.5em;
348
+ }
349
+
350
+ d-code {
351
+ font-size: 12px;
352
+ }
353
+
354
+ .large-image-background {
355
+ width: 100vw;
356
+ padding-top: 10px;
357
+ padding-bottom: 10px;
358
+ margin-left: calc(-50vw + 50%);
359
+ margin-right: calc(-50vw + 50%);
360
+ background: white;
361
+ height: fit-content; /* This will make it match the image height */
362
+ display: flex;
363
+ justify-content: center; /* This will center your image */
364
+ }
365
+
366
+ .large-image-background-transparent {
367
+ /* width: 100vw; */
368
+ padding-top: 10px;
369
+ padding-bottom: 10px;
370
+ /* margin-left: calc(-50vw + 50%); */
371
+ margin-left:-100px;
372
+ margin-right: -100px;
373
+ /* margin-right: calc(-50vw + 50%); */
374
+ /* background: white; */
375
+ height: fit-content; /* This will make it match the image height */
376
+ display: flex;
377
+ justify-content: center; /* This will center your image */
378
+ }
379
+
380
+ .boxed-image {
381
+ padding: 0.5rem;
382
+ background: white;
383
+ border-radius: 12px;
384
+ border: 1px solid #e5e7eb;
385
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
386
+ }
387
+
388
+ d-article li {
389
+ margin-bottom: 0.0em;
390
+ }
391
+
392
+ d-article ul ul {
393
+ margin-bottom: 0.0em;
394
+ }
395
+
396
+ d-article ol ol {
397
+ margin-bottom: 0.0em;
398
+ }
399
+
400
+ d-article hr {
401
+ grid-column: text;
402
+ }
403
+
404
+ /* Memory visualization */
405
+ #graph-all {
406
+ min-width: 500px;
407
+ margin-right: 10px;
408
+ margin-bottom: 2rem;
409
+ padding: 0.5rem;
410
+ background: #f9fafb;
411
+ border-radius: 12px;
412
+ border: 1px solid #e5e7eb;
413
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
414
+ }
415
+
416
+
417
+ /* Main container styles */
418
+ #controls {
419
+ max-width: 1200px;
420
+ /* margin: 2rem auto; */
421
+ margin-bottom: 2rem;
422
+ margin-left: 10px;
423
+ padding: 0.6rem;
424
+ background: #f9fafb;
425
+ border-radius: 12px;
426
+ border: 1px solid #e5e7eb;
427
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
428
+ }
429
+
430
+ /* Grid layout */
431
+ #controls {
432
+ display: grid;
433
+ grid-template-columns: 1fr 1fr;
434
+ /* gap: 2rem; */
435
+ }
436
+
437
+ /* Cell styles */
438
+ .cell {
439
+ margin-bottom: 0.2rem;
440
+ }
441
+
442
+ /* Label styles */
443
+ label {
444
+ display: block;
445
+ /* margin-bottom: 0.5rem; */
446
+ font-size: 0.8rem;
447
+ font-weight: 500;
448
+ color: #374151;
449
+ }
450
+
451
+ /* Input container for range + number combination */
452
+ .input-container {
453
+ display: flex;
454
+ gap: 1rem;
455
+ align-items: center;
456
+ }
457
+
458
+ /* Range input styling */
459
+ input[type="range"] {
460
+ flex: 1;
461
+ height: 6px;
462
+ background: #e5e7eb;
463
+ border-radius: 3px;
464
+ appearance: none;
465
+ outline: none;
466
+ }
467
+
468
+ input[type="range"]::-webkit-slider-thumb {
469
+ appearance: none;
470
+ width: 16px;
471
+ height: 16px;
472
+ background: #3b82f6;
473
+ border-radius: 50%;
474
+ cursor: pointer;
475
+ transition: background 0.15s ease;
476
+ }
477
+
478
+ input[type="range"]::-webkit-slider-thumb:hover {
479
+ background: #2563eb;
480
+ }
481
+
482
+ /* Number input styling */
483
+ input[type="number"] {
484
+ width: 80px;
485
+ padding: 0.5rem;
486
+ border: 1px solid #e5e7eb;
487
+ border-radius: 6px;
488
+ font-size: 0.9rem;
489
+ color: #374151;
490
+ }
491
+
492
+ /* Select styling */
493
+ select {
494
+ width: 100%;
495
+ padding: 0.5rem;
496
+ border: 1px solid #e5e7eb;
497
+ border-radius: 6px;
498
+ background: white;
499
+ font-size: 0.9rem;
500
+ color: #374151;
501
+ cursor: pointer;
502
+ }
503
+
504
+ /* Checkbox styling */
505
+ input[type="checkbox"] {
506
+ width: 1.2rem;
507
+ height: 1.2rem;
508
+ margin-right: 0.5rem;
509
+ border: 2px solid #e5e7eb;
510
+ border-radius: 4px;
511
+ cursor: pointer;
512
+ }
513
+
514
+ /* Column specific styles */
515
+ .column-1 {
516
+ padding-right: 0.5rem;
517
+ }
518
+
519
+ .column-2 {
520
+ padding-left: 0.5rem;
521
+ }
522
+
523
+ /* Checkbox container */
524
+ .checkbox-container {
525
+ display: flex;
526
+ align-items: center;
527
+ margin-bottom: 1rem;
528
+ }
529
+
530
+ /* Memory visualization styles */
531
+ .memory-block {
532
+ background: #fff;
533
+ border-radius: 8px;
534
+ padding: 1rem;
535
+ margin-bottom: 1rem;
536
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
537
+ }
538
+
539
+ .memory-title {
540
+ font-size: 1.1rem;
541
+ font-weight: 500;
542
+ color: #374151;
543
+ margin-bottom: 0.5rem;
544
+ }
545
+
546
+ .memory-value {
547
+ font-size: 1.5rem;
548
+ font-weight: 600;
549
+ color: #3b82f6;
550
+ }
551
+
552
+ /* Responsive adjustments */
553
+ @media (max-width: 768px) {
554
+ #controls {
555
+ grid-template-columns: 1fr;
556
+ padding: 1rem;
557
+ }
558
+
559
+ .column-1, .column-2 {
560
+ padding: 0;
561
+ }
562
+ }
563
+
564
+ /* Hover states and transitions */
565
+ input:hover, select:hover {
566
+ border-color: #3b82f6;
567
+ }
568
+
569
+ input:focus, select:focus {
570
+ border-color: #2563eb;
571
+ outline: none;
572
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
573
+ }
574
+
575
+ /* Add smooth transitions */
576
+ input, select, button {
577
+ transition: all 0.15s ease;
578
+ }
579
+
580
+ /* Preset dropdown special styling */
581
+ select[name="presets"] {
582
+ background-color: #f3f4f6;
583
+ font-weight: 500;
584
+ }
585
+
586
+ /* Memory graph enhancements */
587
+ .activation-memory {
588
+ background: #dbeafe;
589
+ padding: 1rem;
590
+ border-radius: 8px;
591
+ margin-bottom: 1rem;
592
+ }
593
+
594
+ .gradient-memory {
595
+ background: #ede9fe;
596
+ padding: 1rem;
597
+ border-radius: 8px;
598
+ }
599
+
src/syncHFSpacesURLHash.js ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const queryArg = "section";
2
+
3
+ function syncHFSpacesURLHash() {
4
+ // Handle explicit section requests (don't update hash automatically on load)
5
+ const hasExplicitRequest = handleExplicitSectionRequest();
6
+
7
+ // Set up hash change monitoring
8
+ updateHashBasedOnHashChange();
9
+
10
+ // Always set up scroll monitoring to update hash during scrolling
11
+ setupScrollMonitoring();
12
+
13
+ // If no explicit request, we don't update the hash on initial load
14
+ // The hash will only start updating when the user scrolls
15
+ }
16
+
17
+ function handleExplicitSectionRequest() {
18
+ // Check for section parameter in URL
19
+ const urlParams = new URLSearchParams(window.location.search);
20
+ const sectionId = urlParams.get(queryArg);
21
+
22
+ // If we have an explicit section request
23
+ if (sectionId) {
24
+ const targetElement = document.getElementById(sectionId);
25
+ if (targetElement) {
26
+ // Slight delay to ensure the browser doesn't try to do its own scrolling first
27
+ setTimeout(() => {
28
+ targetElement.scrollIntoView();
29
+ history.replaceState(null, null, `#${sectionId}`);
30
+ }, 100);
31
+ }
32
+ return true;
33
+ }
34
+
35
+ // No explicit section parameter found
36
+ return false;
37
+ }
38
+
39
+ function setupScrollMonitoring() {
40
+ // Variables to manage throttling
41
+ let isScrolling = false;
42
+ let lastKnownScrollPosition = 0;
43
+ let initialScroll = true;
44
+
45
+ // Add the scroll event listener
46
+ window.addEventListener('scroll', function() {
47
+ lastKnownScrollPosition = window.scrollY;
48
+
49
+ if (!isScrolling) {
50
+ window.requestAnimationFrame(function() {
51
+ // Skip the first scroll event which might be browser's automatic scroll
52
+ // to a hash on page load
53
+ if (initialScroll) {
54
+ initialScroll = false;
55
+ } else {
56
+ updateHashBasedOnScroll(lastKnownScrollPosition);
57
+ }
58
+ isScrolling = false;
59
+ });
60
+ }
61
+
62
+ isScrolling = true;
63
+ });
64
+ }
65
+
66
+ // Function to update the URL hash based on scroll position
67
+ function updateHashBasedOnScroll(scrollPosition) {
68
+ const closestHeading = findClosestHeading(scrollPosition);
69
+
70
+ // Update the URL hash if we found a closest element
71
+ if (closestHeading && closestHeading.id) {
72
+ // Only update if the hash is different to avoid unnecessary operations
73
+ if (window.location.hash !== `#${closestHeading.id}`) {
74
+ silentlyUpdateHash(closestHeading.id);
75
+ postMessageToHFSpaces(closestHeading.id);
76
+ }
77
+ }
78
+ }
79
+
80
+ // Find the closest heading to the current scroll position
81
+ function findClosestHeading(scrollPosition) {
82
+ // Get only heading elements with IDs that we want to track
83
+ const headingsWithIds = Array.from(document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'));
84
+
85
+ // Skip if there are no headings with IDs
86
+ if (headingsWithIds.length === 0) return null;
87
+
88
+ // Find the element closest to the middle of the viewport
89
+ let closestHeading = null;
90
+ let closestDistance = Infinity;
91
+ const viewportMiddle = scrollPosition + window.innerHeight / 2;
92
+
93
+ // Iterate through all headings to find the closest one
94
+ headingsWithIds.forEach(heading => {
95
+ const headingTop = heading.getBoundingClientRect().top + scrollPosition;
96
+ const distance = Math.abs(headingTop - viewportMiddle);
97
+
98
+ if (distance < closestDistance) {
99
+ closestDistance = distance;
100
+ closestHeading = heading;
101
+ }
102
+ });
103
+
104
+ return closestHeading;
105
+ }
106
+
107
+ // Update hash without triggering scroll or other side effects
108
+ function silentlyUpdateHash(id) {
109
+ history.replaceState(null, null, `#${id}`);
110
+ }
111
+
112
+ function updateHashBasedOnHashChange() {
113
+ window.addEventListener('hashchange', () => {
114
+ const elementId = window.location.hash.slice(1);
115
+ postMessageToHFSpaces(elementId);
116
+ });
117
+ }
118
+
119
+ function postMessageToHFSpaces(elementId) {
120
+ const parentOrigin = "https://huggingface.co";
121
+ window.parent.postMessage({ queryString: `${queryArg}=${elementId}` }, parentOrigin);
122
+ }
123
+
124
+ export { syncHFSpacesURLHash };
webpack.config.js ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require("path");
2
+ const { CleanWebpackPlugin } = require("clean-webpack-plugin");
3
+ const CopyPlugin = require("copy-webpack-plugin");
4
+ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
5
+ const Handlebars = require("handlebars");
6
+ const fs = require("fs");
7
+ const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
8
+ const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
9
+
10
+
11
+ const FRAGMENTS_PATH = "src/fragments";
12
+
13
+ // Load the fragments from the fragments directory and caches it
14
+ const loadFragmentsMap = (() => {
15
+ let cachedFragments = null;
16
+ return async () => {
17
+ if (cachedFragments === null) {
18
+ cachedFragments = {};
19
+ const walkDir = async (dir, basePath = '') => {
20
+ const files = fs.readdirSync(dir);
21
+ await Promise.all(files.map(async file => {
22
+ const filePath = path.join(dir, file);
23
+ const relativePath = path.join(basePath, file);
24
+ if (fs.statSync(filePath).isDirectory()) {
25
+ await walkDir(filePath, relativePath);
26
+ } else {
27
+ // Remove the .html extension before creating the dotted path
28
+ const nameWithoutExt = relativePath.replace(/\.html$/, '');
29
+ const dottedPath = 'fragment-' + nameWithoutExt.replace(/\\/g, '-').replace(/\//g, '-').replace(/\./g, '-');
30
+ const content = fs.readFileSync(filePath, "utf8");
31
+ // Minify the HTML content using swcMinifyFragment
32
+ const minifiedRes = await HtmlMinimizerPlugin.swcMinifyFragment({"tmp.html": content})
33
+ if (minifiedRes.errors) {
34
+ console.error(minifiedRes.errors)
35
+ }
36
+ const minifiedContent = minifiedRes.code;
37
+ cachedFragments[dottedPath] = minifiedContent;
38
+ }
39
+ }));
40
+ };
41
+ await walkDir(FRAGMENTS_PATH);
42
+ }
43
+ return cachedFragments;
44
+ };
45
+ })();
46
+
47
+ const transformHandlebars = async (data, path) => {
48
+ const fragments = await loadFragmentsMap();
49
+ console.log(`Available fragments: ${Object.keys(fragments).join(', ')}`);
50
+ // Load the template file
51
+ const template = Handlebars.compile(data.toString('utf8'));
52
+ const html = template(fragments);
53
+ return html;
54
+ };
55
+
56
+ module.exports = {
57
+ entry: {
58
+ distill: "./src/distill.js",
59
+ main: "./src/index.js",
60
+ },
61
+ output: {
62
+ filename: "[name].bundle.js", // The output file
63
+ path: path.resolve(__dirname, "dist"), // Output directory
64
+ },
65
+ module: {
66
+ rules: [
67
+ { test: /\.css$/, use: ["style-loader", "css-loader"] },
68
+ {
69
+ test: /\.(js|mjs)$/,
70
+ exclude: /node_modules/,
71
+ use: {
72
+ loader: "babel-loader",
73
+ options: {
74
+ presets: ["@babel/preset-env"],
75
+ },
76
+ },
77
+ },
78
+ {}
79
+ ],
80
+ },
81
+ plugins: [
82
+ new CleanWebpackPlugin(),
83
+ new CopyPlugin({
84
+ patterns: [
85
+ {
86
+ from: "assets",
87
+ to: "assets",
88
+ },
89
+ { from: "src/fragments/*", to: "fragments/[name].html" },
90
+ { from: "src/style.css", to: "style.css" },
91
+ { from: "src/bibliography.bib", to: "bibliography.bib" },
92
+ {
93
+ from: "src/index.html",
94
+ to: "index.html",
95
+ transform: transformHandlebars,
96
+ },
97
+ ],
98
+ }),
99
+ ],
100
+ devtool: process.env.NODE_ENV === 'production' ? 'source-map' : 'eval-source-map',
101
+ devServer: {
102
+ static: "./dist", // Serve files from the 'dist' directory
103
+ open: process.env.NODE_ENV !== 'production', // Automatically open the browser unless in production
104
+ hot: process.env.NODE_ENV !== 'production', // Enable hot module replacement unless in production
105
+ },
106
+ mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
107
+ optimization: {
108
+ minimizer: [
109
+ // Hynek: Ideally we would convert all images to webp and just use webp, but I don't have time
110
+ // to write script which would also modify html to reflect the new extensions.
111
+ new ImageMinimizerPlugin({
112
+ minimizer: [{
113
+ implementation: ImageMinimizerPlugin.sharpMinify,
114
+ options: {
115
+ encodeOptions: {
116
+ // For JPG
117
+ jpeg: {
118
+ quality: 80
119
+ },
120
+ // For PNG
121
+ png: {
122
+ quality: 80
123
+ },
124
+ // For WebP
125
+ webp: {
126
+ quality: 80
127
+ }
128
+ }
129
+ }
130
+ },
131
+ {
132
+ implementation: ImageMinimizerPlugin.svgoMinify,
133
+ options: {
134
+ encodeOptions: {
135
+ multipass: true,
136
+ plugins: [
137
+ 'preset-default',
138
+ ]
139
+ }
140
+ }
141
+ }
142
+ ]
143
+ }),
144
+ //Hynek: Ideally we don't run this twice but we
145
+ new HtmlMinimizerPlugin({
146
+ test: /fragments\/.*\.html$/i,
147
+ minify: HtmlMinimizerPlugin.swcMinifyFragment,
148
+ })
149
+ ]
150
+ },
151
+ };
152
+
153
+ console.log(process.env.NODE_ENV)