Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
204 lines (196 loc) · 9.77 KB
/
Copy pathindex.html
File metadata and controls
204 lines (196 loc) · 9.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
<title>PoolPy</title>
<!--
Browser-only build of the PoolPy Streamlit app (streamlit_app.py), run by
stlite: Streamlit compiled to WebAssembly via Pyodide. Nothing is computed
on a server — the Python interpreter, the app and the benchmark database
are all downloaded to the visitor's browser and run there.
The app source and its data files are not copied anywhere: this page sits
at the repository root and fetches streamlit_app.py, data/, assets/ and
user_guide.pdf from beside it at start-up. Serve the repository root
(GitHub Pages set to "/ (root)", or `python3 -m http.server` from here)
and the app is the site's front page. The earlier Shinylive version of
the app is still served from legacy/.
-->
<!--
Browser-tab icon. On a Streamlit server this comes from the page_icon
passed to st.set_page_config; under stlite that icon is handed to the
browser as an internal stlite.invalid URL it cannot load, so the tab
would stay blank. Declared here instead, from the same logo file. The
rel is "icon", not "shortcut icon", because Streamlit's frontend only
rewrites the latter and would replace it with the unreachable URL.
-->
<link rel="icon" type="image/png" href="./assets/poolpy_logo.png" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@stlite/browser@1.8.1/build/stlite.css"
/>
<style>
/* Loading splash. It covers the page until the app's navigation appears,
replacing stlite's own progress toasts (disabled below) with wording
that says what is happening in the visitor's terms. Colours are the
app's own palette, so the hand-over is not a visible jump. */
#pp-splash {
position: fixed; inset: 0; z-index: 9999;
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 1.4rem; padding: 2rem; text-align: center;
background: linear-gradient(180deg, #FBF9F7 0%, #E6EDF6 100%);
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #16202B;
transition: opacity 0.45s ease;
}
#pp-splash.pp-done { opacity: 0; pointer-events: none; }
#pp-splash img { width: 180px; max-width: 55vw; }
#pp-splash-step {
font-size: 1.05rem; font-weight: 600; color: #24466C;
min-height: 1.5em; /* reserve the line so the layout never jumps */
}
#pp-splash-track {
width: min(320px, 70vw); height: 4px; border-radius: 2px;
background: rgba(53, 101, 156, 0.18); overflow: hidden;
}
#pp-splash-bar {
height: 100%; width: 0%; border-radius: 2px; background: #35659C;
transition: width 0.5s ease;
}
/* Fallback links, anchored to the bottom of the splash: if loading stalls
or fails on a visitor's browser, PoolPy is still reachable from here. */
#pp-splash-links {
position: absolute; bottom: 1.5rem; left: 0; right: 0;
font-size: 0.8rem; color: #5A6673;
}
#pp-splash-links a { color: #24466C; text-decoration: none; }
#pp-splash-links a:hover { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
#pp-splash, #pp-splash-bar { transition: none; }
}
</style>
</head>
<body>
<div id="pp-splash">
<img src="./assets/poolpy_logo.png" alt="PoolPy" />
<div id="pp-splash-track"><div id="pp-splash-bar"></div></div>
<div id="pp-splash-step">Starting PoolPy…</div>
<!-- Mirrors LEGACY_APP_URL, GITHUB_URL and PAPER_URL in streamlit_app.py.
The legacy link is relative so it follows whichever domain serves
this page. -->
<div id="pp-splash-links">
<a href="./legacy/">Legacy app</a> ·
<a href="https://github.com/trouillon-lab/PoolPy">Source code</a> ·
<a href="https://www.nature.com/articles/s41467-026-77055-5">Publication</a>
</div>
</div>
<div id="root"></div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/browser@1.8.1/build/stlite.js";
// Local development only. stlite fetches the app from inside a Web
// Worker, and a hard reload (Cmd+Shift+R) does not reliably bypass the
// HTTP cache for worker-initiated requests, so edits to streamlit_app.py
// can silently appear not to take effect. A timestamp forces a fresh copy
// when serving from localhost. The published site keeps a clean URL, so
// GitHub Pages caching is untouched. Only the app file is busted — the
// parquet database and the PDF are large and rarely edited.
const DEV = ["localhost", "127.0.0.1", "[::1]"].includes(location.hostname);
const appUrl = "./streamlit_app.py" + (DEV ? "?v=" + Date.now() : "");
const controller = mount(
{
entrypoint: "streamlit_app.py",
// Pyodide-prebuilt packages; streamlit itself is bundled with stlite.
// Mirrors requirements.txt minus streamlit and pyarrow: stlite's
// Streamlit build ships fastparquet instead, which pandas picks up
// automatically for read_parquet, so pyarrow is not needed here.
requirements: ["pandas", "numpy", "scipy", "altair", "pillow"],
files: {
"streamlit_app.py": { url: appUrl },
"data/precomputed_metrics.parquet": { url: "./data/precomputed_metrics.parquet" },
"assets/poolpy_logo.png": { url: "./assets/poolpy_logo.png" },
"user_guide.pdf": { url: "./user_guide.pdf" },
},
// Same settings as .streamlit/config.toml, which stlite does not read.
streamlitConfig: {
"theme.base": "light",
"theme.primaryColor": "#35659C",
"theme.backgroundColor": "#FBF9F7",
"theme.secondaryBackgroundColor": "#FFFFFF",
"theme.textColor": "#16202B",
"theme.font": "sans serif",
"server.maxUploadSize": 20,
"browser.gatherUsageStats": false,
// "minimal" leaves the three-dot menu (Print, Rerun, Streamlit
// version) out entirely: it keeps only entries added externally or
// via st.set_page_config's menu_items, and this app adds none, so
// the menu has nothing to show and is hidden.
"client.toolbarMode": "minimal",
},
// stlite's own progress toasts, replaced by the splash above. Error
// toasts are governed separately and stay on, so a failure to load
// is still reported rather than hidden behind the splash.
disableProgressToasts: true,
},
document.getElementById("root"),
);
// ── splash progress ───────────────────────────────────────────────
// The kernel emits one "loadProgress" event per start-up step, carrying
// stlite's own wording as event.detail. Known steps are translated to the
// lines below; an unrecognised step leaves the text alone but still moves
// the bar, so a future stlite that renames or reorders its steps degrades
// to a moving bar rather than breaking or showing the wrong thing.
const STEPS = [
[/pyodide/i, "Downloading the Python runtime…"],
[/mounting files/i, "Loading PoolPy and its benchmark database…"],
[/installing/i, "Installing libraries…"],
[/streamlit package/i, "Starting the app…"],
[/booting/i, "Almost ready…"],
];
// Start-up steps counted on stlite 1.8.1; only the bar's pace depends on
// it, and it is clamped so an extra step cannot overshoot.
const EXPECTED_STEPS = 9;
const splash = document.getElementById("pp-splash");
const stepEl = document.getElementById("pp-splash-step");
const barEl = document.getElementById("pp-splash-bar");
let seen = 0;
function onProgress(event) {
const message = String((event && event.detail) || "");
const hit = STEPS.find(([pattern]) => pattern.test(message));
if (hit) stepEl.textContent = hit[1];
seen += 1;
barEl.style.width = Math.min(92, (seen / EXPECTED_STEPS) * 92) + "%";
}
// _kernel is documented as an unstable API, so every use is guarded: if a
// future stlite drops it the app still loads, just without step updates.
const kernel = controller && controller._kernel;
if (kernel && typeof kernel.addEventListener === "function") {
kernel.addEventListener("loadProgress", onProgress);
}
// The splash lifts once the sidebar navigation exists, which means the
// Python script has run and rendered, not merely that Streamlit booted.
const READY = '[data-testid="stSidebar"] input[type="radio"]';
function finish() {
barEl.style.width = "100%";
splash.classList.add("pp-done");
setTimeout(() => splash.remove(), 500);
}
if (document.querySelector(READY)) {
finish();
} else {
const observer = new MutationObserver(() => {
if (document.querySelector(READY)) {
observer.disconnect();
finish();
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Never trap the visitor behind the splash: if the app fails to render,
// drop it anyway so stlite's error toast and the page are reachable.
setTimeout(() => {
observer.disconnect();
if (document.getElementById("pp-splash")) finish();
}, 90000);
}
</script>
</body>
</html>