Add search input, filter chips, no-results block, and back-to-top
button to category/group/subcategory pages. Pass filter_urls_json to
all page types so tag-chip navigation works site-wide. Fix JS so
filter-clear and no-results-clear redirect to / on non-index pages
instead of trying to filter a non-existent local table. Remove the
now-redundant .category-results CSS overrides.
Co-Authored-By: Claude <noreply@anthropic.com>
Removes inline .category-row-desc from the name cell and renders
entry.description inside .expand-content instead, matching the
index page pattern. Drops the now-unused CSS rules for
.category-row-desc and the overridden .category-table .expand-content
padding.
Co-Authored-By: Claude <noreply@anthropic.com>
The results-intro grid (1fr + 28rem note column) squeezed the heading on
category pages with long names, e.g. "Python Projects in Environment
Management" wrapped onto two lines.
Scope a single-column override to .category-results so the heading takes
the full row and the note drops below right-aligned. Index page layout
is untouched since its heading is short.
The "All projects" link in the category-page topbar pointed to
/#library-index so the browser would scroll to the library section on
arrival. The hash stayed in the URL, which looked like an internal anchor
state rather than a clean homepage URL.
On homepage load, if the hash is #library-index, scroll to the section
explicitly and use history.replaceState to drop the hash from the URL.
The scrollIntoView call covers the case where the script runs before the
browser's native anchor scroll, since replaceState removes the hash the
browser would have used.
The category template rendered a tag for `category.name` plus a tag for
`entry.groups[0]`, which duplicated the group name on group pages where
those values are identical (e.g. /categories/python-language/ showing
"Python Language" twice). It also never rendered `entry.categories`, so
group pages omitted each project's actual category.
Mirror the index template's tag rendering on category, group, and
subcategory pages, and mark whichever tag matches the current page URL
as active. Pass `category_urls` and `current_path` to each render call
so the template can match by URL.
Tag clicks on / pushState a category/group/subcategory path; on static
pages they fully navigate. Search and sort stay in querystring. Built-in
source tag has no data-url and stays as an in-page filter. The
isIndexDocument flag is captured at load time so toggling on the index
keeps working after pushState changes location.pathname.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`| safe` bypasses Jinja autoescape. If a category name ever contained
"</script>", the literal substring would close the script block early,
leaking JSON content into the DOM and creating an XSS vector. Replace
"</" with "<\\/" (still valid JSON) and pass ensure_ascii=False so
non-ASCII names render readably. Also add a group_path() helper to
parallel category_path()/subcategory_path() and reuse category_urls
when seeding filter_urls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds filter_urls dict (categories, groups, subcategories) in build.py,
passes filter_urls_json to the template, and injects a JSON script block
before the results section in index.html. Covered by a new test that
verifies all three URL types are present and correctly resolved.
Co-Authored-By: Claude <noreply@anthropic.com>
Membership-only assertions wouldn't catch phantom URLs added by future
build changes. Tighten back to an exact-list assertion now that we know
the fixture's exact output, and assert lastmod count tracks loc count.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the .category-subtitle a underline style for visual cohesion in
the hero, and locks in the gating behavior with a negative assertion so
a regression that drops the page_kind guard would be caught.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use the precomputed sub["url"] to identify which subcategories belong
to a category. Avoids parsing the "Cat > Sub" value string, which would
silently misfire if a category name ever contained " > ".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Categories and groups will share the /categories/ URL namespace.
Fail the build with a clear error message if a future README change
introduces a collision.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Avoids a slug collision between the group "Miscellaneous" and the
category of the same name once both share the /categories/ URL
namespace introduced in the upcoming filter-URL refactor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add llms.txt Jinja2 template with a categories_md placeholder
- Extract categories body from README and inject it into the template
- Annotate bullet-entry lines with GitHub star counts (N GitHub stars)
for the main index.md and bare numbers for llms.txt
- Add TestAnnotateEntriesWithStars unit tests
Co-Authored-By: Claude <noreply@anthropic.com>
* update gitignore
* feat: tighten homepage metadata
* fix: trim generated HTML whitespace
* feat(website): add discovery files and markdown alternate
* feat(website): add sitemap lastmod
* feat(seo): add Content-Signal directive to robots.txt
Signals search, ai-input, and ai-train to crawlers
via the experimental Content-Signal header in robots.txt.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Set exclude-newer to 3 days and only-binary/:all: in pyproject.toml to
limit dependency freshness window and block source builds
- Switch uv sync to --locked in Makefile, ci.yml, and deploy-website.yml
to enforce the lockfile rather than re-resolving on each install
- Regenerate uv.lock with exclude-newer snapshot recorded
Co-Authored-By: Claude <noreply@anthropic.com>
- Rising Star: reduce star-growth window from 2 years to 1 year
- Hidden Gem: reduce minimum repo age from 6 months to 3 months
- Rejection rule: reduce minimum repo age from 3 months to 1 month
Co-Authored-By: Claude <noreply@anthropic.com>
Note the editorial-independence policy so sponsor placements are never conflated with curated listings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>