Skip to content

Commit ee01a0b

Browse files
vintaclaude
andcommitted
refactor(website): extract render_category, replace slugify filter with filter_urls map
- Extract render_category() helper to deduplicate the three category/group/builtin rendering blocks in build.py - Replace synthetic dict literals with synthetic_category() helper - Rewrite subcategory rendering to avoid O(n²) loop using precomputed dicts - Pass filter_urls (not just JSON) to templates so Jinja can look up group URLs directly instead of applying the slugify filter at render time - Remove slugify from env.filters (no longer used in templates) - Replace isIndexPage() wrapper with isIndexDocument constant in main.js - Fix: call applyFilters() on page load when activeFilter is set - Remove dead else branch in tag click handler (category pages with no URL) - Switch .hero-category-links from CSS columns to CSS grid for more even layout - Remove max-width cap on .category-subtitle Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8a32d27 commit ee01a0b

5 files changed

Lines changed: 91 additions & 122 deletions

File tree

website/build.py

Lines changed: 71 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import shutil
77
import xml.etree.ElementTree as ET
8+
from collections import Counter
89
from collections.abc import Sequence
910
from datetime import UTC, datetime
1011
from pathlib import Path
@@ -116,6 +117,10 @@ def subcategory_public_url(category_slug: str, subcategory_slug: str) -> str:
116117
return f"{SITE_URL}categories/{category_slug}/{subcategory_slug}/"
117118

118119

120+
def synthetic_category(name: str, slug: str) -> dict[str, str]:
121+
return {"name": name, "slug": slug, "description": "", "description_html": ""}
122+
123+
119124
def write_sitemap_xml(path: Path, urls: Sequence[tuple[str, str]]) -> None:
120125
ET.register_namespace("", SITEMAP_NS)
121126
urlset = ET.Element(f"{{{SITEMAP_NS}}}urlset")
@@ -296,7 +301,7 @@ def build(repo_root: Path) -> None:
296301
cat_slugs = [cat["slug"] for cat in categories]
297302
group_slugs = [g["slug"] for g in parsed_groups]
298303
all_top_level_slugs = cat_slugs + group_slugs + [BUILTIN_SLUG]
299-
duplicates = {s for s in all_top_level_slugs if all_top_level_slugs.count(s) > 1}
304+
duplicates = {s for s, n in Counter(all_top_level_slugs).items() if n > 1}
300305
if duplicates:
301306
raise ValueError(
302307
f"slug collision in /categories/ namespace: {sorted(duplicates)}. "
@@ -343,8 +348,6 @@ def build(repo_root: Path) -> None:
343348
trim_blocks=True,
344349
lstrip_blocks=True,
345350
)
346-
env.filters["slugify"] = slugify
347-
348351
site_dir = website / "output"
349352
if site_dir.exists():
350353
shutil.rmtree(site_dir)
@@ -364,77 +367,68 @@ def build(repo_root: Path) -> None:
364367
build_date=build_date.strftime("%B %d, %Y"),
365368
sponsors=sponsors,
366369
category_urls=category_urls,
370+
filter_urls=filter_urls,
367371
filter_urls_json=filter_urls_json,
368372
),
369373
encoding="utf-8",
370374
)
371375

372376
tpl_category = env.get_template("category.html")
373377
categories_dir = site_dir / "categories"
374-
for category in categories:
375-
category_entries = [entry for entry in entries if category["name"] in entry["categories"]]
376-
page_dir = categories_dir / category["slug"]
378+
379+
def render_category(
380+
category: dict,
381+
*,
382+
category_url: str,
383+
entries: list[dict],
384+
current_path: str,
385+
page_dir: Path,
386+
parent_category: dict | None = None,
387+
group_categories: list | None = None,
388+
) -> None:
377389
page_dir.mkdir(parents=True, exist_ok=True)
378390
(page_dir / "index.html").write_text(
379391
tpl_category.render(
380392
category=category,
381-
category_url=category_public_url(category),
382-
entries=category_entries,
393+
category_url=category_url,
394+
entries=entries,
383395
total_categories=len(categories),
384-
page_kind="category",
385396
category_urls=category_urls,
386-
current_path=category_path(category),
397+
current_path=current_path,
398+
filter_urls=filter_urls,
387399
filter_urls_json=filter_urls_json,
400+
parent_category=parent_category,
401+
group_categories=group_categories,
388402
),
389403
encoding="utf-8",
390404
)
391405

406+
for category in categories:
407+
render_category(
408+
category,
409+
category_url=category_public_url(category),
410+
entries=[e for e in entries if category["name"] in e["categories"]],
411+
current_path=category_path(category),
412+
page_dir=categories_dir / category["slug"],
413+
)
414+
392415
for group in parsed_groups:
393-
group_entries = [entry for entry in entries if group["name"] in entry["groups"]]
394-
page_dir = categories_dir / group["slug"]
395-
page_dir.mkdir(parents=True, exist_ok=True)
396-
synthetic = {
397-
"name": group["name"],
398-
"slug": group["slug"],
399-
"description": "",
400-
"description_html": "",
401-
}
402-
(page_dir / "index.html").write_text(
403-
tpl_category.render(
404-
category=synthetic,
405-
category_url=group_public_url(group["slug"]),
406-
entries=group_entries,
407-
total_categories=len(categories),
408-
page_kind="group",
409-
category_urls=category_urls,
410-
current_path=group_path(group["slug"]),
411-
filter_urls_json=filter_urls_json,
412-
group_categories=group["categories"],
413-
),
414-
encoding="utf-8",
416+
render_category(
417+
synthetic_category(group["name"], group["slug"]),
418+
category_url=group_public_url(group["slug"]),
419+
entries=[e for e in entries if group["name"] in e["groups"]],
420+
current_path=group_path(group["slug"]),
421+
page_dir=categories_dir / group["slug"],
422+
group_categories=group["categories"],
415423
)
416424

417425
if builtin_entries:
418-
page_dir = categories_dir / BUILTIN_SLUG
419-
page_dir.mkdir(parents=True, exist_ok=True)
420-
synthetic = {
421-
"name": BUILTIN_FILTER,
422-
"slug": BUILTIN_SLUG,
423-
"description": "",
424-
"description_html": "",
425-
}
426-
(page_dir / "index.html").write_text(
427-
tpl_category.render(
428-
category=synthetic,
429-
category_url=BUILTIN_PUBLIC_URL,
430-
entries=builtin_entries,
431-
total_categories=len(categories),
432-
page_kind="built-in",
433-
category_urls=category_urls,
434-
current_path=BUILTIN_PATH,
435-
filter_urls_json=filter_urls_json,
436-
),
437-
encoding="utf-8",
426+
render_category(
427+
synthetic_category(BUILTIN_FILTER, BUILTIN_SLUG),
428+
category_url=BUILTIN_PUBLIC_URL,
429+
entries=builtin_entries,
430+
current_path=BUILTIN_PATH,
431+
page_dir=categories_dir / BUILTIN_SLUG,
438432
)
439433

440434
sponsorship_dir = site_dir / "sponsorship"
@@ -446,51 +440,33 @@ def build(repo_root: Path) -> None:
446440
hero_stats.append(f"{total_entries}+ curated projects")
447441
hero_stats.append(f"Updated {build_date.strftime('%B %d, %Y')}")
448442
(sponsorship_dir / "index.html").write_text(
449-
tpl_sponsorship.render(
450-
total_entries=total_entries,
451-
total_categories=len(categories),
452-
hero_stats=hero_stats,
453-
),
443+
tpl_sponsorship.render(hero_stats=hero_stats),
454444
encoding="utf-8",
455445
)
456446

457-
seen_subcats: set[tuple[str, str]] = set()
458-
for category in categories:
459-
cat_url_prefix = f"/categories/{category['slug']}/"
460-
for entry in entries:
461-
for sub in entry.get("subcategories", []):
462-
if not sub["url"].startswith(cat_url_prefix):
463-
continue
464-
key = (category["slug"], sub["slug"])
465-
if key in seen_subcats:
466-
continue
467-
seen_subcats.add(key)
468-
sub_entries = [
469-
e for e in entries
470-
if any(s["value"] == sub["value"] for s in e.get("subcategories", []))
471-
]
472-
page_dir = categories_dir / category["slug"] / sub["slug"]
473-
page_dir.mkdir(parents=True, exist_ok=True)
474-
synthetic = {
475-
"name": sub["name"],
476-
"slug": sub["slug"],
477-
"description": "",
478-
"description_html": "",
479-
}
480-
(page_dir / "index.html").write_text(
481-
tpl_category.render(
482-
category=synthetic,
483-
category_url=subcategory_public_url(category["slug"], sub["slug"]),
484-
entries=sub_entries,
485-
total_categories=len(categories),
486-
page_kind="subcategory",
487-
parent_category=category,
488-
category_urls=category_urls,
489-
current_path=subcategory_path(category["slug"], sub["slug"]),
490-
filter_urls_json=filter_urls_json,
491-
),
492-
encoding="utf-8",
493-
)
447+
subcat_to_entries: dict[str, list[dict]] = {}
448+
subcat_meta: dict[str, tuple[str, str, str]] = {} # value -> (cat_slug, sub_slug, sub_name)
449+
cat_slug_by_url_prefix = {f"/categories/{c['slug']}/": c["slug"] for c in categories}
450+
cat_by_slug = {c["slug"]: c for c in categories}
451+
for entry in entries:
452+
for sub in entry.get("subcategories", []):
453+
value = sub["value"]
454+
subcat_to_entries.setdefault(value, []).append(entry)
455+
if value not in subcat_meta:
456+
for prefix, cat_slug in cat_slug_by_url_prefix.items():
457+
if sub["url"].startswith(prefix):
458+
subcat_meta[value] = (cat_slug, sub["slug"], sub["name"])
459+
break
460+
461+
for value, (cat_slug, sub_slug, sub_name) in subcat_meta.items():
462+
render_category(
463+
synthetic_category(sub_name, sub_slug),
464+
category_url=subcategory_public_url(cat_slug, sub_slug),
465+
entries=subcat_to_entries[value],
466+
current_path=subcategory_path(cat_slug, sub_slug),
467+
page_dir=categories_dir / cat_slug / sub_slug,
468+
parent_category=cat_by_slug[cat_slug],
469+
)
494470

495471
static_src = website / "static"
496472
static_dst = site_dir / "static"
@@ -509,7 +485,7 @@ def build(repo_root: Path) -> None:
509485
sitemap_urls.extend((group_public_url(g["slug"]), sitemap_date) for g in parsed_groups)
510486
if builtin_entries:
511487
sitemap_urls.append((BUILTIN_PUBLIC_URL, sitemap_date))
512-
for cat_slug, sub_slug in sorted(seen_subcats):
488+
for cat_slug, sub_slug, _ in sorted(subcat_meta.values()):
513489
sitemap_urls.append((subcategory_public_url(cat_slug, sub_slug), sitemap_date))
514490
sitemap_urls.append((SPONSORSHIP_PUBLIC_URL, sitemap_date))
515491
write_sitemap_xml(site_dir / "sitemap.xml", sitemap_urls)

website/static/main.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,14 @@ const filterUrlsScript = document.getElementById("filter-urls");
184184
const filterToUrl = filterUrlsScript
185185
? JSON.parse(filterUrlsScript.textContent)
186186
: {};
187-
const urlToFilter = {};
188-
Object.keys(filterToUrl).forEach(function (k) {
189-
urlToFilter[filterToUrl[k]] = k;
190-
});
191187

192188
const isIndexDocument =
193189
location.pathname === "/" || location.pathname === "/index.html";
194190

195-
function isIndexPage() {
196-
return isIndexDocument;
197-
}
191+
const urlToFilter = {};
192+
Object.keys(filterToUrl).forEach(function (k) {
193+
urlToFilter[filterToUrl[k]] = k;
194+
});
198195

199196
function buildQueryString() {
200197
const params = new URLSearchParams();
@@ -209,7 +206,7 @@ function buildQueryString() {
209206
}
210207

211208
function updateURL() {
212-
if (!isIndexPage()) return;
209+
if (!isIndexDocument) return;
213210
const path =
214211
activeFilter && filterToUrl[activeFilter] ? filterToUrl[activeFilter] : "/";
215212
history.replaceState(null, "", path + buildQueryString());
@@ -322,7 +319,7 @@ tags.forEach(function (tag) {
322319
e.preventDefault();
323320
const value = tag.dataset.value;
324321
const url = tag.dataset.url;
325-
if (isIndexPage()) {
322+
if (isIndexDocument) {
326323
activeFilter = activeFilter === value ? null : value;
327324
if (activeFilter && url) {
328325
history.pushState(null, "", url + buildQueryString());
@@ -332,16 +329,13 @@ tags.forEach(function (tag) {
332329
applyFilters();
333330
} else if (url) {
334331
window.location.href = url;
335-
} else {
336-
activeFilter = activeFilter === value ? null : value;
337-
applyFilters();
338332
}
339333
});
340334
});
341335

342336
if (filterClear) {
343337
filterClear.addEventListener("click", function () {
344-
if (!isIndexPage()) {
338+
if (!isIndexDocument) {
345339
window.location.href = "/#library-index";
346340
return;
347341
}
@@ -353,7 +347,7 @@ if (filterClear) {
353347
const noResultsClear = document.querySelector(".no-results-clear");
354348
if (noResultsClear) {
355349
noResultsClear.addEventListener("click", function () {
356-
if (!isIndexPage()) {
350+
if (!isIndexDocument) {
357351
window.location.href = "/";
358352
return;
359353
}
@@ -464,11 +458,14 @@ if (backToTop) {
464458
if (q || activeFilter || sort) {
465459
sortRows();
466460
}
461+
if (activeFilter) {
462+
applyFilters();
463+
}
467464
updateSortIndicators();
468465
})();
469466

470467
window.addEventListener("popstate", function () {
471-
if (!isIndexPage()) return;
468+
if (!isIndexDocument) return;
472469
const matched = urlToFilter[location.pathname];
473470
activeFilter = matched || null;
474471
applyFilters();

website/static/style.css

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -354,13 +354,10 @@ kbd {
354354

355355
.hero-category-links {
356356
list-style: none;
357-
columns: 5 9.5rem;
357+
display: grid;
358+
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
358359
column-gap: clamp(1.25rem, 3vw, 2.5rem);
359-
}
360-
361-
.hero-category-links li {
362-
break-inside: avoid;
363-
margin-bottom: 0.28rem;
360+
row-gap: 0.28rem;
364361
}
365362

366363
.hero-category-link {
@@ -480,7 +477,6 @@ kbd {
480477
}
481478

482479
.category-subtitle {
483-
max-width: 68ch;
484480
margin-top: 1.1rem;
485481
color: var(--hero-muted);
486482
font-size: clamp(1rem, 1.8vw, 1.18rem);
@@ -1544,7 +1540,7 @@ th[data-sort].sort-asc::after {
15441540
}
15451541

15461542
.hero-category-links {
1547-
columns: 3 8rem;
1543+
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
15481544
}
15491545

15501546
.results-note {
@@ -1612,7 +1608,7 @@ th[data-sort].sort-asc::after {
16121608
}
16131609

16141610
.hero-category-links {
1615-
columns: 3 6.3rem;
1611+
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
16161612
column-gap: 0.75rem;
16171613
}
16181614

website/templates/category.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</nav>
2525

2626
<div class="category-hero-copy">
27-
{% if page_kind == "subcategory" and parent_category %}
27+
{% if parent_category %}
2828
<p class="category-breadcrumb">
2929
<a href="/categories/{{ parent_category.slug }}/">{{ parent_category.name }}</a>
3030
</p>
@@ -183,7 +183,7 @@ <h2 class="sr-only">Results</h2>
183183
>
184184
{% endfor %}
185185
{% if entry.groups %}
186-
{% set group_url = "/categories/" ~ (entry.groups[0] | slugify) ~ "/" %}
186+
{% set group_url = filter_urls[entry.groups[0]] %}
187187
<button
188188
class="tag tag-group{% if group_url == current_path %} active{% endif %}"
189189
data-value="{{ entry.groups[0] }}"

website/templates/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ <h2 class="sr-only">Results</h2>
235235
<button
236236
class="tag tag-group"
237237
data-value="{{ entry.groups[0] }}"
238-
data-url="/categories/{{ entry.groups[0] | slugify }}/"
238+
data-url="{{ filter_urls[entry.groups[0]] }}"
239239
>
240240
{{ entry.groups[0] }}
241241
</button>

0 commit comments

Comments
 (0)