From 469475a36d8feef995d08821c39eb1f4a5afbef6 Mon Sep 17 00:00:00 2001 From: Anjo Vahldiek Date: Wed, 22 Jul 2026 11:57:50 +0200 Subject: [PATCH] search: add badge keywords and keyword-help link --- src/assets/css/reprodb-search.css | 23 +++++++++++++ src/assets/js/reprodb-search.js | 55 +++++++++++++++++++++++++----- src/index.md | 3 ++ src/methodology/search-keywords.md | 8 +++++ 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/assets/css/reprodb-search.css b/src/assets/css/reprodb-search.css index 17fcdf36..33570a9d 100644 --- a/src/assets/css/reprodb-search.css +++ b/src/assets/css/reprodb-search.css @@ -60,6 +60,25 @@ html { overflow-y: scroll; } .rdb-icon-btn svg { display: block; } +.rdb-help-link { + display: inline-flex; + align-items: center; + justify-content: center; + height: 28px; + padding: 0 9px; + border: 1px solid #ccc; + border-radius: 4px; + background: #fff; + color: #555; + font-size: 0.82em; + font-weight: 600; + text-decoration: none; + transition: background 0.15s, color 0.15s; +} +.rdb-help-link:hover { + background: #f0f0f0; + color: #333; +} /* ── Profile cards (author/institution cards in search results) ─────── */ #profileCards { @@ -119,6 +138,8 @@ html { overflow-y: scroll; } html:not([data-theme="light"]) #filters select { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } html:not([data-theme="light"]) .rdb-icon-btn { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } html:not([data-theme="light"]) .rdb-icon-btn:hover { background: #2a3038; color: #fff; } + html:not([data-theme="light"]) .rdb-help-link { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } + html:not([data-theme="light"]) .rdb-help-link:hover { background: #2a3038; color: #fff; } html:not([data-theme="light"]) #pagination button { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } html:not([data-theme="light"]) .profile-card { background: #23272d; border-color: #4a4f57; color: #d6d9dc; } html:not([data-theme="light"]) .profile-card:hover { border-color: #5a9cf5; } @@ -134,6 +155,8 @@ html { overflow-y: scroll; } html[data-theme="dark"] #filters select { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } html[data-theme="dark"] .rdb-icon-btn { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } html[data-theme="dark"] .rdb-icon-btn:hover { background: #2a3038; color: #fff; } +html[data-theme="dark"] .rdb-help-link { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } +html[data-theme="dark"] .rdb-help-link:hover { background: #2a3038; color: #fff; } html[data-theme="dark"] #pagination button { background: #1e2127; color: #d6d9dc; border-color: #4a4f57; } html[data-theme="dark"] .profile-card { background: #23272d; border-color: #4a4f57; color: #d6d9dc; } html[data-theme="dark"] .profile-card:hover { border-color: #5a9cf5; } diff --git a/src/assets/js/reprodb-search.js b/src/assets/js/reprodb-search.js index 4fab4a6b..bf676c18 100644 --- a/src/assets/js/reprodb-search.js +++ b/src/assets/js/reprodb-search.js @@ -29,6 +29,34 @@ return (s || '').toLowerCase().replace(/[^a-z0-9 ]/g, ' '); } + function normalizeBadgeForFilter(v) { + var t = String(v || '').toLowerCase().replace('badges: ', '').trim(); + if (t === 'reproducible') return 'reproduced'; + if (t === 'artifact evaluated') return 'evaluated'; + return t; + } + + function extractBadgeKeywords(raw) { + var matches = String(raw || '').toLowerCase().match(/#[a-z]+/g) || []; + var seen = {}; + var selected = []; + matches.forEach(function(tag) { + var t = tag.replace(/^#/, ''); + if (t === 'reproducible') t = 'reproduced'; + if (['available', 'reproduced', 'functional', 'reusable', 'evaluated'].indexOf(t) !== -1 && !seen[t]) { + seen[t] = true; + selected.push(t); + } + }); + return selected; + } + + function stripMagicKeywords(raw) { + return String(raw || '') + .replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder|available|reproduced|reproducible|functional|reusable|evaluated)/gi, '') + .trim(); + } + function buildSearchIndex(data) { data.forEach(function(d) { d._search = normalizeText(d.title) + ' ' + @@ -62,14 +90,16 @@ function doSearch() { var raw = document.getElementById('searchBox').value.trim(); + var rawLower = raw.toLowerCase(); // Parse magic keywords - var onlyUnavail = raw.indexOf('#unavailable') !== -1; - var onlyAwarded = raw.indexOf('#awarded') !== -1; - var onlyGithub = raw.indexOf('#github') !== -1; - var onlyZenodo = raw.indexOf('#zenodo') !== -1; - var onlyNourl = raw.indexOf('#nourl') !== -1; - var onlyArtifinder = raw.indexOf('#artifinder') !== -1; - var cleaned = raw.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder)/g, '').trim(); + var onlyUnavail = rawLower.indexOf('#unavailable') !== -1; + var onlyAwarded = rawLower.indexOf('#awarded') !== -1; + var onlyGithub = rawLower.indexOf('#github') !== -1; + var onlyZenodo = rawLower.indexOf('#zenodo') !== -1; + var onlyNourl = rawLower.indexOf('#nourl') !== -1; + var onlyArtifinder = rawLower.indexOf('#artifinder') !== -1; + var selectedBadges = extractBadgeKeywords(rawLower); + var cleaned = stripMagicKeywords(raw); var query = normalizeText(cleaned); var yearVal = document.getElementById('yearFilter').value; var venueVal = document.getElementById('venueFilter').value; @@ -106,6 +136,13 @@ if (onlyArtifinder) { if (String(d.source || '').toLowerCase() !== 'artifinder') return false; } + if (selectedBadges.length > 0) { + var recBadges = (d.badges || []).map(normalizeBadgeForFilter); + var hasAllSelectedBadges = selectedBadges.every(function(b) { + return recBadges.indexOf(b) !== -1; + }); + if (!hasAllSelectedBadges) return false; + } if (terms.length === 0) return true; return terms.every(function(t) { return d._search.indexOf(t) !== -1; }); }); @@ -175,7 +212,7 @@ var status = document.getElementById('searchStatus'); var hero = document.getElementById('search-hero'); var query = document.getElementById('searchBox').value.trim(); - var cleaned = query.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder)/g, '').trim(); + var cleaned = stripMagicKeywords(query); var terms = normalizeText(cleaned).split(/\s+/).filter(function(t) { return t.length > 0; }); var yearVal = document.getElementById('yearFilter').value; var venueVal = document.getElementById('venueFilter').value; @@ -514,7 +551,7 @@ // Re-render profile cards if the user is already searching if (filtered.length > 0 || document.getElementById('searchBox').value.trim().length >= 2) { var raw = document.getElementById('searchBox').value.trim(); - var cleaned = raw.replace(/#(unavailable|awarded|github|zenodo|nourl|artifinder)/g, '').trim(); + var cleaned = stripMagicKeywords(raw); var terms = normalizeText(cleaned).split(/\s+/).filter(function(t) { return t.length > 0; }); renderProfileCards(raw, terms); } diff --git a/src/index.md b/src/index.md index 62ec5757..cf3c3101 100644 --- a/src/index.md +++ b/src/index.md @@ -45,6 +45,9 @@ skip_chartjs: true + + + diff --git a/src/methodology/search-keywords.md b/src/methodology/search-keywords.md index 07e65e78..dc82a681 100644 --- a/src/methodology/search-keywords.md +++ b/src/methodology/search-keywords.md @@ -14,6 +14,12 @@ The [search bar]({{ '/' | relative_url }}) supports special `#` keywords that fi | `#zenodo` | Artifacts hosted on Zenodo | | `#nourl` | Artifacts with no artifact URL recorded | | `#artifinder` | Results tagged as ArtiFinder-discovered only | +| `#available` | Artifacts with the Available badge | +| `#functional` | Artifacts with the Functional badge | +| `#reproduced` | Artifacts with the Reproduced badge | +| `#reproducible` | Alias for `#reproduced` | +| `#reusable` | Artifacts with the Reusable badge | +| `#evaluated` | Artifacts with the Artifact Evaluated badge | **Examples:** @@ -22,6 +28,8 @@ The [search bar]({{ '/' | relative_url }}) supports special `#` keywords that fi - `#github #unavailable 2022` - GitHub-hosted artifacts from 2022 with dead links - `#zenodo fuzzing` - Zenodo-hosted fuzzing artifacts - `#artifinder malware` - ArtiFinder-tagged artifacts matching "malware" +- `#available #functional` - artifacts that are both available and functional +- `#reproducible usenixsec` - reproduced artifacts for USENIX Security Keywords also work alongside the year, venue, and area dropdown filters.