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.