Skip to content

Commit 47e3a25

Browse files
perf: polish the web UI and shrink cold start (#131)
## Summary - redesign the browser UI with a cleaner responsive layout, dark-mode support, example chips, clearer progress, and a better result/copy experience - remove the external GitHub ribbon and Prism assets so the page has fewer blocking CDN requests - preload the website-local Pyodide bundle and start downloading it in parallel with the Pyodide runtime - stream the bundle response and surface real downloaded byte counts in the progress message - remove `requests`, `urllib3`, `certifi`, `charset-normalizer`, and `idna` from the browser-only bundle by keeping native `requests` imports off the Emscripten path - enforce a `< 1,000,000 byte` compressed Web bundle budget in CI - remove the obsolete browser deprecation notice from the README ## Cold-start optimization The currently deployed Pages bundle is 1,474,423 bytes. CI builds the new bundle at **845,131 bytes (about 825 KiB)**, a **42.7% reduction**. The browser networking path uses `pyfetch`, so the Requests stack in the old archive was unnecessary. This PR reduces the locked browser dependency set to `pyiso4`, `pylatexenc`, and `unidecode`, while native Python continues to use Requests normally. The Worker also no longer waits for `loadPyodide()` to finish before requesting the local bundle. The bundle and Pyodide runtime start downloading together, and HTML preloads the bundle as early as possible. Streamed reads report actual downloaded bytes in the progress UI. ## Validation - Web unit tests cover streamed bundle progress and verify the bundle request starts before `loadPyodide` - Chromium E2E performs a real DOI lookup with the 845,131-byte bundle and succeeds without contacting PyPI/pythonhosted - CI builds the exact browser bundle and rejects it if it reaches 1 MB - Python 3.10 and 3.13 tests verify the native Requests path remains functional - pre-commit remains part of the required aggregate test workflow - Codecov continues to receive separate Web and Python coverage uploads Agent: ChatGPT Model: GPT-5.6 Sol --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 7d115f4 commit 47e3a25

15 files changed

Lines changed: 864 additions & 298 deletions

.github/workflows/pages.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,18 @@ jobs:
5353
test -f _site/index.html
5454
test -f _site/webworker.js
5555
test -f _site/wenxian-web-packages.tar.gz
56+
bundle_bytes=$(stat -c%s _site/wenxian-web-packages.tar.gz)
57+
echo "Web bundle: $bundle_bytes bytes"
58+
test "$bundle_bytes" -lt 1000000
5659
mkdir bundle-check
5760
tar -xzf _site/wenxian-web-packages.tar.gz -C bundle-check
5861
PYTHONPATH=bundle-check python - <<'PY'
5962
import json
6063
from importlib.metadata import version
6164
from pathlib import Path
6265
66+
import pyiso4
6367
import pylatexenc
64-
import requests
6568
import unidecode
6669
import wenxian
6770
@@ -71,7 +74,12 @@ jobs:
7174
assert manifest["format"] == 1
7275
assert manifest["packages"]["wenxian"] == version("wenxian")
7376
assert manifest["requirements_lock"] == "web-requirements.lock"
74-
assert "requests==2.34.2" in manifest["requirements"]
77+
assert "requests" not in {name.lower() for name in manifest["packages"]}
78+
assert manifest["requirements"] == [
79+
"pyiso4==0.1.6",
80+
"pylatexenc==3.0a21",
81+
"unidecode==1.4.0",
82+
]
7583
print(json.dumps(manifest, indent=2, sort_keys=True))
7684
PY
7785
- name: Upload website artifact

.github/workflows/test.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ jobs:
8383
uv build --wheel
8484
wheel=$(echo dist/wenxian-*.whl)
8585
python scripts/build_web_bundle.py "$wheel" test-web-bundle.tar.gz
86+
bundle_bytes=$(stat -c%s test-web-bundle.tar.gz)
87+
echo "Web bundle: $bundle_bytes bytes"
88+
test "$bundle_bytes" -lt 1000000
8689
- name: Install Chromium
8790
run: npx playwright install --with-deps chromium
8891
- name: Run browser end-to-end smoke test

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
1010
## Usage
1111

12-
### Use wenxian in the browser (Deprecated)
12+
### Use wenxian in the browser
1313

14-
> [!CAUTION]
15-
> Deprecated, as several websites that serve the API have disabled CORS.
16-
17-
Visit [wenxian.njzjz.win](https://wenxian.njzjz.win) to use wenxian in the browser.
14+
Visit [wenxian.njzjz.win](https://wenxian.njzjz.win) to use wenxian directly in the browser.
1815

1916
### Command line interface
2017

docs/index.html

Lines changed: 109 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -6,103 +6,127 @@
66
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
77
<meta
88
name="description"
9-
content="Generate BibTeX from DOI/PMID/arXiv identifier"
9+
content="Generate BibTeX from a DOI, PMID, arXiv identifier, or paper title"
1010
/>
1111
<meta
1212
name="keywords"
1313
content="BibTeX, DOI, PMID, arXiv, citation, reference"
1414
/>
1515
<meta name="author" content="Jinzhe Zeng" />
16-
<meta name="theme-color" content="#03A89E" />
17-
<link rel="icon" type="image/svg" href="logo.svg" />
18-
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
19-
<link rel="preconnect" href="https://pypi.org" />
20-
<link rel="preconnect" href="https://files.pythonhosted.org" />
16+
<meta name="theme-color" content="#0f766e" />
17+
<link rel="icon" type="image/svg+xml" href="logo.svg" />
18+
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
2119
<link
22-
href="
23-
https://cdn.jsdelivr.net/npm/github-fork-ribbon-css@0.2.3/gh-fork-ribbon.min.css
24-
"
25-
rel="stylesheet"
20+
rel="preload"
21+
href="./wenxian-web-packages.tar.gz"
22+
as="fetch"
23+
type="application/gzip"
24+
crossorigin="anonymous"
2625
/>
2726
<link rel="stylesheet" href="style.css" />
28-
<link
29-
rel="stylesheet"
30-
href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-solarizedlight.min.css"
31-
/>
32-
<title>wenxian</title>
27+
<title>wenxian · BibTeX from identifiers</title>
3328
</head>
3429

35-
<body class="flex">
36-
<a
37-
class="github-fork-ribbon"
38-
href="https://github.com/njzjz/wenxian"
39-
data-ribbon="Fork me on GitHub"
40-
title="Fork me on GitHub"
41-
>Fork me on GitHub</a
42-
>
43-
<h1 style="margin-bottom: 50px; color: #03a89e">
44-
<img
45-
src="logo.svg"
46-
height="100px"
47-
width="100px"
48-
style="margin-bottom: -1em"
49-
title="Logo"
50-
/>
51-
wenxian
52-
</h1>
53-
<form style="padding-bottom: 20px" target="_blank" class="flex">
54-
<label class="block" style="width: fit-content">
55-
<input
56-
class="block identifier"
57-
id="identifier"
58-
type="text"
59-
placeholder="DOI/PMID/arXiv identifier"
60-
required
61-
/>
62-
<div class="bar"></div>
63-
</label>
64-
<input
65-
type="submit"
66-
id="submit"
67-
class="block btn"
68-
value="Generate BibTeX"
69-
/>
70-
</form>
71-
<div id="progress-container" class="progress-container" hidden>
72-
<progress id="progress-bar" max="100" value="0"></progress>
73-
<div id="progress-text" class="progress-text" aria-live="polite"></div>
74-
</div>
75-
<div class="examples" id="message">
76-
<p><b>Examples:</b></p>
77-
<ul>
78-
<li>
79-
<a href="javascript:run_example('10.1063/5.0155600')"
80-
>10.1063/5.0155600</a
81-
>
82-
</li>
83-
<li><a href="javascript:run_example('37526163')">37526163</a></li>
84-
<li><a href="javascript:run_example('2304.09409')">2304.09409</a></li>
85-
</ul>
86-
</div>
87-
<div id="output" class="output" style="display: none">
88-
<button id="copy_button" onclick="copy_bibtex()" class="copy-button">
89-
Copy BibTeX
90-
</button>
91-
<pre><code class="language-bib" id="bibtex"></code></pre>
30+
<body>
31+
<div class="page-shell">
32+
<header class="site-header">
33+
<a class="brand" href="./" aria-label="wenxian home">
34+
<img src="logo.svg" width="44" height="44" alt="" />
35+
<span>wenxian</span>
36+
</a>
37+
<a
38+
class="github-link"
39+
href="https://github.com/njzjz/wenxian"
40+
target="_blank"
41+
rel="noreferrer"
42+
>
43+
GitHub
44+
<span aria-hidden="true"></span>
45+
</a>
46+
</header>
47+
48+
<main>
49+
<section class="hero" aria-labelledby="hero-title">
50+
<div class="eyebrow">
51+
<span class="status-dot" aria-hidden="true"></span>
52+
Runs entirely in your browser
53+
</div>
54+
<h1 id="hero-title">Turn a paper identifier into clean BibTeX.</h1>
55+
<p class="hero-copy">
56+
Paste a DOI, PMID, arXiv ID, or paper title. wenxian checks multiple
57+
scholarly sources and formats the result for you.
58+
</p>
59+
</section>
60+
61+
<section class="lookup-card" aria-label="Generate a reference">
62+
<form id="lookup-form">
63+
<label class="input-label" for="identifier">Paper identifier</label>
64+
<div class="input-row">
65+
<input
66+
class="identifier"
67+
id="identifier"
68+
name="identifier"
69+
type="text"
70+
placeholder="e.g. 10.1063/5.0155600"
71+
autocomplete="off"
72+
autocapitalize="off"
73+
spellcheck="false"
74+
required
75+
/>
76+
<button type="submit" id="submit" class="btn">
77+
<span>Generate BibTeX</span>
78+
<span class="button-arrow" aria-hidden="true"></span>
79+
</button>
80+
</div>
81+
</form>
82+
83+
<div class="examples">
84+
<span class="examples-label">Try an example</span>
85+
<button type="button" onclick="run_example('10.1063/5.0155600')">
86+
DOI
87+
</button>
88+
<button type="button" onclick="run_example('37526163')">
89+
PMID
90+
</button>
91+
<button type="button" onclick="run_example('2304.09409')">
92+
arXiv
93+
</button>
94+
</div>
95+
<div id="message" class="message" aria-live="polite"></div>
96+
97+
<div id="progress-container" class="progress-container" hidden>
98+
<div class="progress-meta">
99+
<span id="progress-text" aria-live="polite">Preparing…</span>
100+
<span id="progress-percent">0%</span>
101+
</div>
102+
<progress id="progress-bar" max="100" value="0"></progress>
103+
</div>
104+
</section>
105+
106+
<section id="output" class="output" style="display: none">
107+
<div class="output-header">
108+
<div>
109+
<span class="result-label">Result</span>
110+
<h2>BibTeX</h2>
111+
</div>
112+
<button
113+
id="copy_button"
114+
onclick="copy_bibtex()"
115+
class="copy-button"
116+
>
117+
Copy BibTeX
118+
</button>
119+
</div>
120+
<pre><code id="bibtex"></code></pre>
121+
</section>
122+
</main>
123+
124+
<footer>
125+
<span>Open source · powered by</span>
126+
<a href="https://github.com/njzjz/wenxian">njzjz/wenxian</a>
127+
</footer>
92128
</div>
93-
<p style="position: sticky; top: calc(100% - 2.5em)">
94-
Powered by
95-
<a style="color: #3294ea" href="https://github.com/njzjz/wenxian"
96-
>njzjz/wenxian</a
97-
>
98-
</p>
99-
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
100-
<script
101-
src="https://cdn.jsdelivr.net/npm/prismjs-bibtex@2.1.0/prism-bibtex.js"
102-
integrity="sha256-A5GMUmGHpY8mVpfcaRLQFeHtmdjZLumKBOMpf81FXX0="
103-
crossorigin="anonymous"
104-
referrerpolicy="no-referrer"
105-
></script>
129+
106130
<script src="wenxian.js" type="module"></script>
107131
</body>
108132
</html>

0 commit comments

Comments
 (0)