mirror of
https://github.com/vinta/awesome-python.git
synced 2026-05-18 18:35:15 +08:00
feat: migrate README parser to markdown-it-py and refresh website
Switch readme_parser.py from regex-based parsing to markdown-it-py for more robust and maintainable Markdown AST traversal. Update build pipeline, templates, styles, and JS to support the new parser output. Refresh GitHub stars data and update tests to match new parser behavior. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+142
-4
@@ -1,15 +1,44 @@
|
||||
// State
|
||||
var activeFilter = null; // { type: "cat"|"group", value: "..." }
|
||||
var activeSort = { col: 'stars', order: 'desc' };
|
||||
var searchInput = document.querySelector('.search');
|
||||
var filterBar = document.querySelector('.filter-bar');
|
||||
var filterValue = document.querySelector('.filter-value');
|
||||
var filterClear = document.querySelector('.filter-clear');
|
||||
var noResults = document.querySelector('.no-results');
|
||||
var countEl = document.querySelector('.count');
|
||||
var rows = document.querySelectorAll('.table tbody tr.row');
|
||||
var tags = document.querySelectorAll('.tag');
|
||||
var tbody = document.querySelector('.table tbody');
|
||||
|
||||
// Relative time formatting
|
||||
function relativeTime(isoStr) {
|
||||
var date = new Date(isoStr);
|
||||
var now = new Date();
|
||||
var diffMs = now - date;
|
||||
var diffHours = Math.floor(diffMs / 3600000);
|
||||
var diffDays = Math.floor(diffMs / 86400000);
|
||||
if (diffHours < 1) return 'just now';
|
||||
if (diffHours < 24) return diffHours === 1 ? '1 hour ago' : diffHours + ' hours ago';
|
||||
if (diffDays === 1) return 'yesterday';
|
||||
if (diffDays < 30) return diffDays + ' days ago';
|
||||
var diffMonths = Math.floor(diffDays / 30);
|
||||
if (diffMonths < 12) return diffMonths === 1 ? '1 month ago' : diffMonths + ' months ago';
|
||||
var diffYears = Math.floor(diffDays / 365);
|
||||
return diffYears === 1 ? '1 year ago' : diffYears + ' years ago';
|
||||
}
|
||||
|
||||
// Format all commit date cells
|
||||
document.querySelectorAll('.col-commit[data-commit]').forEach(function (td) {
|
||||
var time = td.querySelector('time');
|
||||
if (time) time.textContent = relativeTime(td.dataset.commit);
|
||||
});
|
||||
|
||||
// Store original row order for sort reset
|
||||
rows.forEach(function (row, i) {
|
||||
row._origIndex = i;
|
||||
row._expandRow = row.nextElementSibling;
|
||||
});
|
||||
|
||||
function collapseAll() {
|
||||
var openRows = document.querySelectorAll('.table tbody tr.row.open');
|
||||
openRows.forEach(function (row) {
|
||||
@@ -46,16 +75,18 @@ function applyFilters() {
|
||||
show = row._searchText.includes(query);
|
||||
}
|
||||
|
||||
row.hidden = !show;
|
||||
if (row.hidden !== !show) row.hidden = !show;
|
||||
|
||||
if (show) {
|
||||
visibleCount++;
|
||||
row.querySelector('.col-num').textContent = String(visibleCount);
|
||||
var numCell = row.cells[0];
|
||||
if (numCell.textContent !== String(visibleCount)) {
|
||||
numCell.textContent = String(visibleCount);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (noResults) noResults.hidden = visibleCount > 0;
|
||||
if (countEl) countEl.textContent = visibleCount;
|
||||
|
||||
// Update tag highlights
|
||||
tags.forEach(function (tag) {
|
||||
@@ -74,6 +105,76 @@ function applyFilters() {
|
||||
filterBar.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateURL();
|
||||
}
|
||||
|
||||
function updateURL() {
|
||||
var params = new URLSearchParams();
|
||||
var query = searchInput ? searchInput.value.trim() : '';
|
||||
if (query) params.set('q', query);
|
||||
if (activeFilter) {
|
||||
params.set(activeFilter.type === 'cat' ? 'category' : 'group', activeFilter.value);
|
||||
}
|
||||
if (activeSort.col !== 'stars' || activeSort.order !== 'desc') {
|
||||
params.set('sort', activeSort.col);
|
||||
params.set('order', activeSort.order);
|
||||
}
|
||||
var qs = params.toString();
|
||||
history.replaceState(null, '', qs ? '?' + qs : location.pathname);
|
||||
}
|
||||
|
||||
function getSortValue(row, col) {
|
||||
if (col === 'name') {
|
||||
return row.querySelector('.col-name a').textContent.trim().toLowerCase();
|
||||
}
|
||||
if (col === 'stars') {
|
||||
var text = row.querySelector('.col-stars').textContent.trim().replace(/,/g, '');
|
||||
var num = parseInt(text, 10);
|
||||
return isNaN(num) ? -1 : num;
|
||||
}
|
||||
if (col === 'commit-time') {
|
||||
var attr = row.querySelector('.col-commit').getAttribute('data-commit');
|
||||
return attr ? new Date(attr).getTime() : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function sortRows() {
|
||||
var arr = Array.prototype.slice.call(rows);
|
||||
if (activeSort) {
|
||||
arr.sort(function (a, b) {
|
||||
var aVal = getSortValue(a, activeSort.col);
|
||||
var bVal = getSortValue(b, activeSort.col);
|
||||
if (activeSort.col === 'name') {
|
||||
var cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
||||
if (cmp === 0) return a._origIndex - b._origIndex;
|
||||
return activeSort.order === 'desc' ? -cmp : cmp;
|
||||
}
|
||||
if (aVal <= 0 && bVal <= 0) return a._origIndex - b._origIndex;
|
||||
if (aVal <= 0) return 1;
|
||||
if (bVal <= 0) return -1;
|
||||
var cmp = aVal - bVal;
|
||||
if (cmp === 0) return a._origIndex - b._origIndex;
|
||||
return activeSort.order === 'desc' ? -cmp : cmp;
|
||||
});
|
||||
} else {
|
||||
arr.sort(function (a, b) { return a._origIndex - b._origIndex; });
|
||||
}
|
||||
arr.forEach(function (row) {
|
||||
tbody.appendChild(row);
|
||||
tbody.appendChild(row._expandRow);
|
||||
});
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
function updateSortIndicators() {
|
||||
document.querySelectorAll('th[data-sort]').forEach(function (th) {
|
||||
th.classList.remove('sort-asc', 'sort-desc');
|
||||
if (activeSort && th.dataset.sort === activeSort.col) {
|
||||
th.classList.add('sort-' + activeSort.order);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Expand/collapse: event delegation on tbody
|
||||
@@ -130,6 +231,23 @@ if (filterClear) {
|
||||
});
|
||||
}
|
||||
|
||||
// Column sorting
|
||||
document.querySelectorAll('th[data-sort]').forEach(function (th) {
|
||||
th.addEventListener('click', function () {
|
||||
var col = th.dataset.sort;
|
||||
var defaultOrder = col === 'name' ? 'asc' : 'desc';
|
||||
var altOrder = defaultOrder === 'asc' ? 'desc' : 'asc';
|
||||
if (activeSort && activeSort.col === col) {
|
||||
if (activeSort.order === defaultOrder) activeSort = { col: col, order: altOrder };
|
||||
else activeSort = { col: 'stars', order: 'desc' };
|
||||
} else {
|
||||
activeSort = { col: col, order: defaultOrder };
|
||||
}
|
||||
sortRows();
|
||||
updateSortIndicators();
|
||||
});
|
||||
});
|
||||
|
||||
// Search input
|
||||
if (searchInput) {
|
||||
var searchTimer;
|
||||
@@ -152,3 +270,23 @@ if (searchInput) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Restore state from URL
|
||||
(function () {
|
||||
var params = new URLSearchParams(location.search);
|
||||
var q = params.get('q');
|
||||
var cat = params.get('category');
|
||||
var group = params.get('group');
|
||||
var sort = params.get('sort');
|
||||
var order = params.get('order');
|
||||
if (q && searchInput) searchInput.value = q;
|
||||
if (cat) activeFilter = { type: 'cat', value: cat };
|
||||
else if (group) activeFilter = { type: 'group', value: group };
|
||||
if ((sort === 'name' || sort === 'stars' || sort === 'commit-time') && (order === 'desc' || order === 'asc')) {
|
||||
activeSort = { col: sort, order: order };
|
||||
}
|
||||
if (q || cat || group || sort) {
|
||||
sortRows();
|
||||
}
|
||||
updateSortIndicators();
|
||||
})();
|
||||
|
||||
+111
-20
@@ -23,6 +23,8 @@
|
||||
--accent-light: oklch(97% 0.015 240);
|
||||
--highlight: oklch(93% 0.10 90);
|
||||
--highlight-text: oklch(35% 0.10 90);
|
||||
--tag-text: oklch(45% 0.06 240);
|
||||
--tag-hover-bg: oklch(93% 0.025 240);
|
||||
}
|
||||
|
||||
html { font-size: 16px; }
|
||||
@@ -65,8 +67,10 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
|
||||
.hero-main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hero-submit {
|
||||
@@ -78,14 +82,21 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: border-color 0.2s, background 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.hero-submit:hover {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-light);
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hero-submit:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(2rem, 5vw, 3rem);
|
||||
@@ -144,6 +155,7 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text);
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.search::placeholder { color: var(--text-muted); }
|
||||
@@ -174,11 +186,12 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.15rem 0.5rem;
|
||||
padding: 0.35rem 0.65rem;
|
||||
font-family: inherit;
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.filter-clear:hover {
|
||||
@@ -186,14 +199,11 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.stats {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
.filter-clear:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.stats strong { color: var(--text-secondary); }
|
||||
|
||||
/* === Table === */
|
||||
.table-wrap {
|
||||
width: 100%;
|
||||
@@ -241,6 +251,7 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
padding: 0.7rem 0.75rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
vertical-align: top;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.table tbody tr.row:not(.open):hover td {
|
||||
@@ -258,9 +269,7 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
|
||||
.col-name {
|
||||
width: 35%;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.col-name > a {
|
||||
@@ -271,12 +280,47 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
|
||||
.col-name > a:hover { text-decoration: underline; color: var(--accent-hover); }
|
||||
|
||||
/* === Sortable Headers === */
|
||||
th[data-sort] {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
th[data-sort]:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
th[data-sort]::after {
|
||||
content: " ▼";
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
th[data-sort="name"]::after {
|
||||
content: " ▲";
|
||||
}
|
||||
|
||||
th[data-sort]:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
th[data-sort].sort-desc::after {
|
||||
content: " ▼";
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
th[data-sort].sort-asc::after {
|
||||
content: " ▲";
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* === Stars Column === */
|
||||
.col-stars {
|
||||
width: 5rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
color: var(--text-secondary);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* === Arrow Column === */
|
||||
@@ -299,6 +343,12 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
/* === Row Click === */
|
||||
.row { cursor: pointer; }
|
||||
|
||||
.row:focus-visible td {
|
||||
outline: none;
|
||||
background: var(--bg-hover);
|
||||
box-shadow: inset 2px 0 0 var(--accent);
|
||||
}
|
||||
|
||||
/* === Expand Row === */
|
||||
.expand-row {
|
||||
display: none;
|
||||
@@ -320,21 +370,33 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
@keyframes expand-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.expand-content {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
animation: expand-in 0.2s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
.expand-tags-mobile {
|
||||
display: none;
|
||||
.expand-tags {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.expand-tag {
|
||||
font-size: var(--text-xs);
|
||||
color: oklch(45% 0.06 240);
|
||||
color: var(--tag-text);
|
||||
background: var(--bg);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
@@ -376,35 +438,63 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
color: var(--border);
|
||||
}
|
||||
|
||||
.col-cat, .col-group {
|
||||
.col-cat {
|
||||
width: 13%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* === Last Commit Column === */
|
||||
.col-commit {
|
||||
width: 9rem;
|
||||
white-space: nowrap;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* === Tags === */
|
||||
.tag {
|
||||
position: relative;
|
||||
background: var(--accent-light);
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
font-size: var(--text-xs);
|
||||
color: oklch(45% 0.06 240);
|
||||
color: var(--tag-text);
|
||||
cursor: pointer;
|
||||
padding: 0.15rem 0.35rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
/* Expand touch target to 44x44px minimum */
|
||||
.tag::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -0.5rem -0.25rem;
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
background: var(--accent-light);
|
||||
background: var(--tag-hover-bg);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.tag:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.tag.active {
|
||||
background: var(--highlight);
|
||||
color: var(--highlight-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* === Noscript === */
|
||||
.noscript-msg {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* === No Results === */
|
||||
.no-results {
|
||||
max-width: 1400px;
|
||||
@@ -437,8 +527,7 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
|
||||
/* === Responsive === */
|
||||
@media (max-width: 900px) {
|
||||
.col-group { display: none; }
|
||||
.expand-tags-mobile { display: flex; }
|
||||
.col-commit { display: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@@ -453,7 +542,7 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
|
||||
.col-cat { display: none; }
|
||||
.col-name { white-space: normal; }
|
||||
.footer { padding: 1.25rem; flex-direction: column; gap: 0.5rem; }
|
||||
.footer { padding: 1.25rem; justify-content: center; flex-wrap: wrap; }
|
||||
}
|
||||
|
||||
/* === Screen Reader Only === */
|
||||
@@ -472,6 +561,8 @@ a:hover { color: var(--accent-hover); text-decoration: underline; }
|
||||
/* === Reduced Motion === */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user