Skip to content

Commit c9d2f11

Browse files
committed
fix(docs): bypass SPA routing for formula links to fix click→404
Symptom: on /browse/, clicking a formula changed the URL to /browse/<slug>/ but the page showed 404. Refreshing loaded it correctly. Root cause: VitePress generates per-batch router manifests. Each CI build-batch job only includes ITS pages in the manifest. When you load /browse/ (from batch-N's app chunk) and click a link to a formula in batch-M (M!=N), Vue Router can't find the route in batch-N's manifest, immediately shows NotFound, and never even fetches the formula's chunk. This is a fundamental issue with multi-batch VitePress builds — not fixable without single-pass build (which OOMs on 4,283 pages). Fix: bypass SPA routing for formula navigation. Add @click.stop.prevent='goToFormula(slug)' to the formula links in FormulaBrowser.vue and index.md autocomplete. goToFormula() sets window.location.href, triggering a full page load. The server sends the correct HTML which loads the correct app chunk for that formula's batch. Same behavior as the user's manual 'refresh' workaround, just automatic. Preserves accessibility: <a href> is still set, so right-click 'open in new tab' and Ctrl+click work normally. SPA routing still works for all other internal links (guide, licenses, etc.). Only formula cross-batch navigation is affected.
1 parent c4aa9a2 commit c9d2f11

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

docs/.vitepress/theme/FormulaBrowser.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,17 @@ function scrollToLetter(letter) {
204204
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
205205
}
206206
207+
function goToFormula(slug) {
208+
// Formula pages are rendered in CI batches (docs.yml build-batch matrix),
209+
// and each batch's VitePress router manifest only includes its own pages.
210+
// SPA navigation from /browse/ to a formula in a different batch hits a
211+
// "Page not found" 404 even though the SSR HTML exists on the server.
212+
// Bypass SPA routing for formula clicks — full page load fetches the
213+
// correct HTML which loads the correct app chunk for that formula's batch.
214+
const basePath = import.meta.env.BASE_URL || '/'
215+
window.location.href = `${basePath}browse/${slug}/`
216+
}
217+
207218
function toggleLicense(value) {
208219
if (value === 'all') {
209220
selectedLicenses.value = ['all']
@@ -255,7 +266,7 @@ function toggleSource(value) {
255266
<div v-for="letter in activeLetters" :key="letter" :id="'letter-' + letter" class="letter-group">
256267
<h3 class="letter-heading">{{ letter }}</h3>
257268
<div class="formula-items">
258-
<a v-for="f in groupedFormulas[letter]" :key="f.slug" :href="f.slug + '/'" class="formula-item">
269+
<a v-for="f in groupedFormulas[letter]" :key="f.slug" :href="f.slug + '/'" class="formula-item" @click.stop.prevent="goToFormula(f.slug)">
259270
<div class="formula-main">
260271
<span class="formula-name">{{ f.name }}</span>
261272
<span class="formula-key">{{ f.formulaName }}</span>

docs/index.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ function getLicenseBadge(f) {
8282
return `<img src="${basePath}licenses/unknown.svg" alt="Unknown" class="license-icon" title="Unknown">`
8383
}
8484

85+
function goToFormula(slug) {
86+
// Formula pages are rendered in CI batches, and each batch's VitePress
87+
// router manifest only knows about its own pages. SPA navigation from
88+
// the homepage to a formula in a different batch 404s. Force a full page
89+
// load so the server sends the correct HTML with the correct app chunk.
90+
window.location.href = `${basePath}browse/${slug}/`
91+
}
92+
8593
function getSourceBadge(f) {
8694
if (!f) return `<img src="${basePath}sources/fontist.svg" alt="Expert Curated" class="source-icon" title="Expert Curated">`
8795
if (f.sourceType === 'google') return `<img src="${basePath}sources/google.svg" alt="Google Fonts" class="source-icon" title="Google Fonts">`
@@ -159,6 +167,7 @@ watch(searchQuery, (val) => {
159167
:href="basePath + 'browse/' + item.slug + '/'"
160168
:class="getItemClass(idx)"
161169
@mouseover="selectedIndex = idx"
170+
@click.stop.prevent="goToFormula(item.slug)"
162171
>
163172
<div class="autocomplete-main">
164173
<span class="autocomplete-name">{{ item.name }}</span>

0 commit comments

Comments
 (0)