feat: group awesomelist by source list, add subcat headings, fix white flash

This commit is contained in:
jae 2026-04-04 03:28:08 +00:00
parent dd6423d9b5
commit ad32ab3d63
32 changed files with 115 additions and 91 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -10,7 +10,7 @@
"slug": "prp-001", "slug": "prp-001",
"entry_count": 25113, "entry_count": 25113,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 1609 "subcategory_count": 73
}, },
{ {
"code": "PRP-002", "code": "PRP-002",
@ -19,7 +19,7 @@
"slug": "prp-002", "slug": "prp-002",
"entry_count": 13343, "entry_count": 13343,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 947 "subcategory_count": 76
}, },
{ {
"code": "PRP-003", "code": "PRP-003",
@ -28,7 +28,7 @@
"slug": "prp-003", "slug": "prp-003",
"entry_count": 4525, "entry_count": 4525,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 333 "subcategory_count": 30
}, },
{ {
"code": "PRP-004", "code": "PRP-004",
@ -37,7 +37,7 @@
"slug": "prp-004", "slug": "prp-004",
"entry_count": 16336, "entry_count": 16336,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 1014 "subcategory_count": 53
}, },
{ {
"code": "PRP-005", "code": "PRP-005",
@ -46,7 +46,7 @@
"slug": "prp-005", "slug": "prp-005",
"entry_count": 8327, "entry_count": 8327,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 415 "subcategory_count": 40
}, },
{ {
"code": "PRP-006", "code": "PRP-006",
@ -55,7 +55,7 @@
"slug": "prp-006", "slug": "prp-006",
"entry_count": 2041, "entry_count": 2041,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 173 "subcategory_count": 15
}, },
{ {
"code": "PRP-007", "code": "PRP-007",
@ -64,7 +64,7 @@
"slug": "prp-007", "slug": "prp-007",
"entry_count": 8038, "entry_count": 8038,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 583 "subcategory_count": 24
}, },
{ {
"code": "PRP-008", "code": "PRP-008",
@ -73,7 +73,7 @@
"slug": "prp-008", "slug": "prp-008",
"entry_count": 6242, "entry_count": 6242,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 479 "subcategory_count": 24
}, },
{ {
"code": "PRP-009", "code": "PRP-009",
@ -82,7 +82,7 @@
"slug": "prp-009", "slug": "prp-009",
"entry_count": 3252, "entry_count": 3252,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 359 "subcategory_count": 24
}, },
{ {
"code": "PRP-010", "code": "PRP-010",
@ -91,7 +91,7 @@
"slug": "prp-010", "slug": "prp-010",
"entry_count": 1283, "entry_count": 1283,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 166 "subcategory_count": 14
}, },
{ {
"code": "PRP-011", "code": "PRP-011",
@ -100,7 +100,7 @@
"slug": "prp-011", "slug": "prp-011",
"entry_count": 705, "entry_count": 705,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 74 "subcategory_count": 10
}, },
{ {
"code": "PRP-012", "code": "PRP-012",
@ -109,7 +109,7 @@
"slug": "prp-012", "slug": "prp-012",
"entry_count": 1419, "entry_count": 1419,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 138 "subcategory_count": 10
}, },
{ {
"code": "PRP-013", "code": "PRP-013",
@ -118,7 +118,7 @@
"slug": "prp-013", "slug": "prp-013",
"entry_count": 2044, "entry_count": 2044,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 118 "subcategory_count": 9
}, },
{ {
"code": "PRP-014", "code": "PRP-014",
@ -127,7 +127,7 @@
"slug": "prp-014", "slug": "prp-014",
"entry_count": 1913, "entry_count": 1913,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 149 "subcategory_count": 16
}, },
{ {
"code": "PRP-015", "code": "PRP-015",
@ -136,7 +136,7 @@
"slug": "prp-015", "slug": "prp-015",
"entry_count": 1127, "entry_count": 1127,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 87 "subcategory_count": 10
}, },
{ {
"code": "PRP-016", "code": "PRP-016",
@ -145,7 +145,7 @@
"slug": "prp-016", "slug": "prp-016",
"entry_count": 1119, "entry_count": 1119,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 112 "subcategory_count": 9
}, },
{ {
"code": "PRP-017", "code": "PRP-017",
@ -154,7 +154,7 @@
"slug": "prp-017", "slug": "prp-017",
"entry_count": 1456, "entry_count": 1456,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 80 "subcategory_count": 9
}, },
{ {
"code": "PRP-018", "code": "PRP-018",
@ -163,7 +163,7 @@
"slug": "prp-018", "slug": "prp-018",
"entry_count": 819, "entry_count": 819,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 112 "subcategory_count": 8
}, },
{ {
"code": "PRP-019", "code": "PRP-019",
@ -172,7 +172,7 @@
"slug": "prp-019", "slug": "prp-019",
"entry_count": 3172, "entry_count": 3172,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 157 "subcategory_count": 10
}, },
{ {
"code": "PRP-020", "code": "PRP-020",
@ -181,7 +181,7 @@
"slug": "prp-020", "slug": "prp-020",
"entry_count": 1216, "entry_count": 1216,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 60 "subcategory_count": 7
}, },
{ {
"code": "PRP-021", "code": "PRP-021",
@ -190,7 +190,7 @@
"slug": "prp-021", "slug": "prp-021",
"entry_count": 1865, "entry_count": 1865,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 145 "subcategory_count": 6
}, },
{ {
"code": "PRP-022", "code": "PRP-022",
@ -199,7 +199,7 @@
"slug": "prp-022", "slug": "prp-022",
"entry_count": 966, "entry_count": 966,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 73 "subcategory_count": 5
}, },
{ {
"code": "PRP-023", "code": "PRP-023",
@ -208,7 +208,7 @@
"slug": "prp-023", "slug": "prp-023",
"entry_count": 893, "entry_count": 893,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 70 "subcategory_count": 4
}, },
{ {
"code": "PRP-024", "code": "PRP-024",
@ -217,7 +217,7 @@
"slug": "prp-024", "slug": "prp-024",
"entry_count": 1989, "entry_count": 1989,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 290 "subcategory_count": 6
}, },
{ {
"code": "PRP-025", "code": "PRP-025",
@ -226,7 +226,7 @@
"slug": "prp-025", "slug": "prp-025",
"entry_count": 81, "entry_count": 81,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 19 "subcategory_count": 3
}, },
{ {
"code": "PRP-026", "code": "PRP-026",
@ -235,7 +235,7 @@
"slug": "prp-026", "slug": "prp-026",
"entry_count": 546, "entry_count": 546,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 50 "subcategory_count": 4
}, },
{ {
"code": "PRP-027", "code": "PRP-027",
@ -244,7 +244,7 @@
"slug": "prp-027", "slug": "prp-027",
"entry_count": 121, "entry_count": 121,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 18 "subcategory_count": 2
}, },
{ {
"code": "PRP-099", "code": "PRP-099",
@ -253,7 +253,8 @@
"slug": "prp-099", "slug": "prp-099",
"entry_count": 25228, "entry_count": 25228,
"starred_count": 0, "starred_count": 0,
"subcategory_count": 2579 "subcategory_count": 154
} }
] ],
"total_lists": 655
} }

View file

@ -592,3 +592,23 @@
gap: 0.4rem; gap: 0.4rem;
} }
} }
/* ─── Subcategory Group Headings (Awesomelist) ─────────── */
.crt-subcat-group {
margin-bottom: 1.5rem;
}
.crt-subcat-heading {
font-family: 'Orbitron', monospace;
font-size: 0.75rem;
color: rgba(255, 170, 0, 0.8);
letter-spacing: 2px;
padding: 0.6rem 0;
border-bottom: 1px solid rgba(255, 170, 0, 0.15);
margin-bottom: 0.5rem;
}
.crt-subcat-count {
font-family: 'JetBrains Mono', monospace;
font-size: 0.65rem;
color: #666;
margin-left: 0.5rem;
}

View file

@ -1,5 +1,5 @@
/* /*
RECON // Curated Lists Controller (flattened) RECON // Curated Lists Controller (grouped by source)
*/ */
(function () { (function () {
'use strict'; 'use strict';
@ -73,7 +73,7 @@
<div class="crt-card-name">${esc(sec.name)}</div> <div class="crt-card-name">${esc(sec.name)}</div>
<div class="crt-card-meta"> <div class="crt-card-meta">
<span class="crt-card-count">${fmt(sec.entry_count)} assets</span> <span class="crt-card-count">${fmt(sec.entry_count)} assets</span>
<span class="crt-card-stars">${sec.subcategory_count} sections</span> <span class="crt-card-stars">${sec.subcategory_count} lists</span>
</div> </div>
<div class="crt-card-status"> ACCESSIBLE</div> <div class="crt-card-status"> ACCESSIBLE</div>
</div>`; </div>`;
@ -98,7 +98,7 @@
<div class="crt-detail-title">${esc(sec.icon)} ${esc(sec.name)}</div> <div class="crt-detail-title">${esc(sec.icon)} ${esc(sec.name)}</div>
<div class="crt-detail-meta"> <div class="crt-detail-meta">
<span>${fmt(sec.total_entries)} ASSETS</span> <span>${fmt(sec.total_entries)} ASSETS</span>
<span>${sec.subcategory_count} SECTIONS</span> <span>${sec.list_count} LISTS</span>
</div> </div>
</div>`; </div>`;
@ -108,15 +108,16 @@
<input type="text" class="crt-search-input" id="crtCatSearch" placeholder="FILTER THIS SECTOR..." autocomplete="off"> <input type="text" class="crt-search-input" id="crtCatSearch" placeholder="FILTER THIS SECTOR..." autocomplete="off">
</div>`; </div>`;
// Subcategory grid (3-col cards) // List cards (one per source list)
html += `<div class="crt-sub-grid" id="crtSubGrid">`; html += `<div class="crt-sub-grid" id="crtSubGrid">`;
for (let i = 0; i < sec.subcategories.length; i++) { for (let i = 0; i < sec.lists.length; i++) {
const sub = sec.subcategories[i]; const lst = sec.lists[i];
if (sub.entries.length === 0) continue; if (lst.total_entries === 0) continue;
html += `<div class="crt-sub-card" data-sub-idx="${i}"> html += `<div class="crt-sub-card" data-list-idx="${i}">
<div class="crt-sub-card-name">${esc(sub.name)}</div> <div class="crt-sub-card-name">${esc(lst.name)}</div>
<div class="crt-sub-card-stats"> <div class="crt-sub-card-stats">
<span>${sub.entries.length} items</span> <span>${lst.total_entries} items</span>
<span>${lst.subcategories.length} sections</span>
</div> </div>
</div>`; </div>`;
} }
@ -201,15 +202,12 @@
// ─── Event Bindings: Index ─────────────────────────── // ─── Event Bindings: Index ───────────────────────────
function bindIndexEvents() { function bindIndexEvents() {
// Card clicks
document.querySelectorAll('.crt-card').forEach(card => { document.querySelectorAll('.crt-card').forEach(card => {
card.addEventListener('click', () => { card.addEventListener('click', () => {
const slug = card.dataset.slug; loadSector(card.dataset.slug);
loadSector(slug);
}); });
}); });
// Global search
const searchInput = document.getElementById('crtSearch'); const searchInput = document.getElementById('crtSearch');
if (searchInput) { if (searchInput) {
searchInput.addEventListener('input', () => { searchInput.addEventListener('input', () => {
@ -238,17 +236,16 @@
loadIndex(); loadIndex();
}); });
// Subcategory card clicks // List card clicks — show all subcategories and entries for that source list
document.querySelectorAll('.crt-sub-card').forEach(card => { document.querySelectorAll('.crt-sub-card').forEach(card => {
card.addEventListener('click', () => { card.addEventListener('click', () => {
const idx = parseInt(card.dataset.subIdx); const idx = parseInt(card.dataset.listIdx);
const sub = sec.subcategories[idx]; const lst = sec.lists[idx];
const panel = document.getElementById('crtSubDetail'); const panel = document.getElementById('crtSubDetail');
const headerEl = document.getElementById('crtSubDetailHeader'); const headerEl = document.getElementById('crtSubDetailHeader');
const notesEl = document.getElementById('crtSubDetailNotes'); const notesEl = document.getElementById('crtSubDetailNotes');
const entriesEl = document.getElementById('crtSubDetailEntries'); const entriesEl = document.getElementById('crtSubDetailEntries');
// If clicking the already active card, collapse
const wasActive = card.classList.contains('active'); const wasActive = card.classList.contains('active');
document.querySelectorAll('.crt-sub-card').forEach(c => c.classList.remove('active')); document.querySelectorAll('.crt-sub-card').forEach(c => c.classList.remove('active'));
@ -259,23 +256,29 @@
card.classList.add('active'); card.classList.add('active');
// Populate header // Header
headerEl.innerHTML = `<span class="crt-sub-toggle">▾</span> <span class="crt-sub-detail-name">${esc(sub.name)}</span> <span class="crt-sub-count">${sub.entries.length} items</span>`; headerEl.innerHTML = `<span class="crt-sub-toggle">▾</span> <span class="crt-sub-detail-name">${esc(lst.name)}</span> <span class="crt-sub-count">${lst.total_entries} items across ${lst.subcategories.length} sections</span>`;
// Populate notes notesEl.innerHTML = '';
if (sub.notes && sub.notes.length > 0) { notesEl.style.display = 'none';
let nh = '';
for (const note of sub.notes) nh += `${esc(note)}<br>`;
notesEl.innerHTML = nh;
notesEl.style.display = '';
} else {
notesEl.innerHTML = '';
notesEl.style.display = 'none';
}
// Populate entries // Build entries grouped by subcategory
let eh = ''; let eh = '';
for (const entry of sub.entries) eh += renderEntry(entry); for (const sub of lst.subcategories) {
if (sub.entries.length === 0) continue;
// Strip the source prefix from subcategory name if it starts with list name
let subName = sub.name;
const prefix = lst.name + ' — ';
if (subName.startsWith(prefix)) {
subName = subName.slice(prefix.length);
}
if (subName === lst.name) subName = 'General';
eh += `<div class="crt-subcat-group">`;
eh += `<div class="crt-subcat-heading">${esc(subName)} <span class="crt-subcat-count">${sub.entries.length}</span></div>`;
for (const entry of sub.entries) eh += renderEntry(entry);
eh += `</div>`;
}
entriesEl.innerHTML = eh; entriesEl.innerHTML = eh;
panel.style.display = ''; panel.style.display = '';
@ -283,7 +286,7 @@
}); });
}); });
// Category search (local filter on subcategory cards) // Local filter on list cards
const catSearch = document.getElementById('crtCatSearch'); const catSearch = document.getElementById('crtCatSearch');
if (catSearch) { if (catSearch) {
catSearch.addEventListener('input', () => { catSearch.addEventListener('input', () => {

View file

@ -10,7 +10,7 @@
<link rel="stylesheet" href="/css/section.css"> <link rel="stylesheet" href="/css/section.css">
<link rel="stylesheet" href="/css/contraband.css"> <link rel="stylesheet" href="/css/contraband.css">
</head> </head>
<body> <body style="background:#0a0a0c;">
<div class="scanline-overlay"></div> <div class="scanline-overlay"></div>
<div class="grid-bg"></div> <div class="grid-bg"></div>