feat: replace server-rendered index with JS-rendered shell page

- Rewrites indexPage to render a shell page with embedded JSON + JS
- Adds searchJs with vanilla JS for client-side list rendering, filtering, sorting
- Hash-based sort modes (#alpha, #tags, #course)
- Search filters by title and filename (case-insensitive)
- Fix: recipe links use /recipes/ prefix and encodeURIComponent for # encoding
- Fix: avoid double-render on initial page load
This commit is contained in:
2026-05-19 21:40:02 -04:00
parent 954bad87bd
commit d5e9c7e0d3
+3 -4
View File
@@ -255,7 +255,7 @@ searchJs = T.unlines
, " if (items.length === 0) return '<p>No recipes found.</p>';" , " if (items.length === 0) return '<p>No recipes found.</p>';"
, " let html = '<ul class=\'roux-recipes\'>';" , " let html = '<ul class=\'roux-recipes\'>';"
, " items.forEach(r => {" , " items.forEach(r => {"
, " html += '<li><a href=\"' + encodeURI(r.filename) + '\">' + escapeHtml(r.title) + '</a></li>';" , " html += '<li><a href=\"/recipes/' + encodeURIComponent(r.filename) + '\">' + escapeHtml(r.title) + '</a></li>';"
, " });" , " });"
, " html += '</ul>';" , " html += '</ul>';"
, " return html;" , " return html;"
@@ -317,10 +317,9 @@ searchJs = T.unlines
, "}" , "}"
, "window.addEventListener('hashchange', updateActiveNav);" , "window.addEventListener('hashchange', updateActiveNav);"
, "" , ""
, "// Initial render" , "// Initial render -- setting hash triggers hashchange"
, "if (!location.hash) location.hash = 'alpha';" , "if (!location.hash) location.hash = 'alpha';"
, "updateActiveNav();" , "else { updateActiveNav(); render(); }"
, "render();"
, "" , ""
, "})();" , "})();"
] ]