Skip to content

Commit 0ad6fce

Browse files
committed
add tags, info, and warnings to processor catalogue
1 parent 5381d92 commit 0ad6fce

4 files changed

Lines changed: 99 additions & 22 deletions

File tree

common/lib/processor_map.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ def _entry(self, ptype):
173173
"title": getattr(processor, "title", ptype),
174174
"category": getattr(processor, "category", None),
175175
"description": getattr(processor, "description", None),
176+
"tags": list(getattr(processor, "tags", []) or []),
177+
"info": list(getattr(processor, "info", []) or []),
178+
"warnings": list(getattr(processor, "warnings", []) or []),
179+
"references": list(getattr(processor, "references", []) or []),
180+
"icon": getattr(processor, "icon", "") or "",
176181
"is_datasource": self._collector[ptype],
177182
"is_filter": self._filter[ptype],
178183
"has_override": not self._declarative[ptype],

webtool/static/css/processor-catalogue.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,33 @@ summary.pc-sub:hover { color: #485ba6; }
8282
.pc-muted { color: #888; }
8383
.pc-spec { background: #f6f7f9; border: 1px solid #e5e7eb; border-radius: 6px; padding: 8px;
8484
font-size: .8em; white-space: pre-wrap; overflow: auto; }
85+
86+
/* --- processor detail "card" -- mirrors the run-processor card treatment --- */
87+
.pc-detail-card { margin-bottom: 12px; }
88+
.pc-detail-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
89+
.pc-detail-head h2 { margin: 0; display: flex; align-items: center; gap: 8px; }
90+
.pc-detail-desc { margin: 8px 0 10px; color: #333; line-height: 1.5; }
91+
92+
/* info / warning notice boxes (blue = helpful extra, yellow = pitfall) */
93+
.pc-notice { display: flex; gap: 10px; align-items: flex-start; margin: 6px 0; padding: 9px 13px;
94+
border-radius: 6px; font-size: .9em; line-height: 1.45; }
95+
.pc-notice i { margin-top: 2px; flex: none; }
96+
.pc-notice span { min-width: 0; }
97+
.pc-notice-info { background: #e8f1fc; color: #1d3f7a; }
98+
.pc-notice-info i { color: #2563eb; }
99+
.pc-notice-warning { background: #fbf3d3; color: #6f5210; }
100+
.pc-notice-warning i { color: #c08a1e; }
101+
102+
/* footer: what it produces + references */
103+
.pc-detail-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px;
104+
flex-wrap: wrap; margin-top: 12px; padding-top: 8px; border-top: 1px solid #eee;
105+
font-size: .85em; color: #555; }
106+
.pc-produces { font-family: monospace; }
107+
.pc-refs { display: flex; flex-wrap: wrap; gap: 4px 12px; justify-content: flex-end; }
108+
.pc-refs a { color: #485ba6; text-decoration: underline; }
109+
.pc-meta { margin: 4px 0 8px; }
110+
111+
/* tag chips (browse cards + detail) */
112+
.pc-tags { display: flex; flex-wrap: wrap; gap: 4px; }
113+
.pc-tag { font-size: .75em; padding: 2px 8px; border-radius: 10px;
114+
background: #eef1f7; color: #3b4a6b; border: 1px solid #dfe3ee; }

webtool/static/js/processor-catalogue.js

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@
5454
CATALOGUE = (data.processors || []).slice().sort((a, b) => (a.title || a.type).localeCompare(b.title || b.type));
5555
CATALOGUE.forEach(p => { TITLE[p.type] = p.title || p.type; });
5656

57-
populateCategories();
57+
populateTags();
5858
document.getElementById("pc-search").addEventListener("input", renderCatalogue);
59-
document.getElementById("pc-category").addEventListener("change", renderCatalogue);
59+
document.getElementById("pc-tag").addEventListener("change", renderCatalogue);
6060

6161
// Remember the empty-state markup (before anything overwrites it) so back/forward can
6262
// restore it when the visitor navigates back past every processor they opened.
@@ -81,14 +81,16 @@
8181
renderCatalogue();
8282
}
8383

84-
// Fill the category dropdown with the categories actually present in the catalogue.
85-
function populateCategories() {
86-
const categories = [...new Set(CATALOGUE.map(p => p.category || "(uncategorised)"))].sort();
87-
const select = document.getElementById("pc-category");
88-
categories.forEach(category => {
84+
// Fill the tag dropdown with every tag present in the catalogue. Tags are the new way
85+
// to filter (a superset of categories -- a processor's first tag is its category), so
86+
// this lists them all, alphabetically.
87+
function populateTags() {
88+
const tags = [...new Set(CATALOGUE.flatMap(p => p.tags || []))].sort();
89+
const select = document.getElementById("pc-tag");
90+
tags.forEach(tag => {
8991
const option = document.createElement("option");
90-
option.value = category;
91-
option.textContent = category;
92+
option.value = tag;
93+
option.textContent = tag;
9294
select.appendChild(option);
9395
});
9496
}
@@ -100,12 +102,12 @@
100102
// "N of M" count and re-attaches the click handler that opens a card's detail.
101103
function renderCatalogue() {
102104
const query = document.getElementById("pc-search").value.trim().toLowerCase();
103-
const category = document.getElementById("pc-category").value;
105+
const tag = document.getElementById("pc-tag").value;
104106

105107
let items = CATALOGUE;
106-
if (category) items = items.filter(p => (p.category || "(uncategorised)") === category);
108+
if (tag) items = items.filter(p => (p.tags || []).includes(tag));
107109
if (query) items = items.filter(p =>
108-
`${p.type} ${p.title || ""} ${p.category || ""} ${p.description || ""}`.toLowerCase().includes(query));
110+
`${p.type} ${p.title || ""} ${(p.tags || []).join(" ")} ${p.description || ""}`.toLowerCase().includes(query));
109111

110112
const groups = {};
111113
items.forEach(p => { const c = p.category || "(uncategorised)"; (groups[c] = groups[c] || []).push(p); });
@@ -128,9 +130,12 @@
128130
p.is_filter ? '<span class="inline-label property-badge pc-filter">filter</span>' : "",
129131
p.has_override ? '<span class="inline-label property-badge pc-approx">override</span>' : ""
130132
].join("");
133+
// the first tag is the category (already the group heading), so show only the rest
134+
const tags = (p.tags || []).slice(1).map(t => `<span class="pc-tag">${esc(t)}</span>`).join("");
131135
return `<button class="pc-card" data-type="${esc(p.type)}">
132136
<div class="pc-card-head"><h4>${esc(p.title || p.type)}</h4><span class="pc-badges">${badges}</span></div>
133137
<p class="pc-desc">${esc(p.description || "")}</p>
138+
${tags ? `<div class="pc-tags">${tags}</div>` : ""}
134139
</button>`;
135140
}
136141

@@ -296,9 +301,31 @@
296301
return next || '<p class="pc-muted">Nothing runs on this output.</p>';
297302
}
298303

299-
// Assemble the whole detail pane for one processor: heading and badges, a short
300-
// metadata list (type / category / what it produces / description), then the three
301-
// blocks. "Produces" is left out when the output shape didn't pin down a format.
304+
// Render a references-style string: turn any [text](url) markdown links into anchors,
305+
// escaping everything else. Plain citations (no link) pass through unchanged.
306+
function mdLinks(s) {
307+
let out = "", last = 0, re = /\[([^\]]+)\]\(([^)]+)\)/g, m;
308+
while ((m = re.exec(s))) {
309+
out += esc(s.slice(last, m.index));
310+
out += `<a href="${esc(m[2])}" target="_blank" rel="noopener">${esc(m[1])}</a>`;
311+
last = m.index + m[0].length;
312+
}
313+
return out + esc(s.slice(last));
314+
}
315+
316+
// Info / warning notice boxes, styled like the run-processor card: warnings (pitfalls)
317+
// in yellow, info (helpful extras) in blue. One box per item, matching the design mockup.
318+
function noticeBoxes(items, kind) {
319+
if (!items || !items.length) return "";
320+
const icon = kind === "warning" ? "triangle-exclamation" : "circle-info";
321+
return items.map(t =>
322+
`<p class="pc-notice pc-notice-${kind}"><i class="fa fa-${icon}" aria-hidden="true"></i><span>${esc(t)}</span></p>`
323+
).join("");
324+
}
325+
326+
// Assemble the whole detail pane for one processor: a card (icon + title + badges/tags,
327+
// description, warning then info boxes, and a footer of what it produces + references),
328+
// then a compact type/category line and the three collapsible blocks.
302329
function detailHtml(info) {
303330
const shape = info.output_shape || {};
304331

@@ -314,14 +341,29 @@
314341
: '<p class="pc-muted">No declared compatibility (defaults to top-level datasets).</p>';
315342
const produces = [shape.extension, shape.media_type].filter(v => v && v !== "unknown").join(" · ");
316343

344+
// the first tag is the category (shown below), so chip only the rest next to the title
345+
const tags = (info.tags || []).slice(1).map(t => `<span class="pc-tag">${esc(t)}</span>`).join("");
346+
const icon = info.icon ? `<i class="fa fa-fw fa-${esc(info.icon)}" aria-hidden="true"></i> ` : "";
347+
const references = (info.references || []).length
348+
? `<div class="pc-refs">${info.references.map(r => `<span>${mdLinks(r)}</span>`).join("")}</div>` : "";
349+
const foot = (produces || references)
350+
? `<div class="pc-detail-foot">${produces ? `<span class="pc-produces"><i class="fa fa-table" aria-hidden="true"></i> ${esc(produces)}</span>` : "<span></span>"}${references}</div>`
351+
: "";
352+
317353
return `
318-
<h2><span>${esc(info.title || info.type)}</span></h2>
319-
<p class="pc-badges">${badges}</p>
320-
<dl class="metadata-wrapper">
354+
<div class="pc-detail-card">
355+
<header class="pc-detail-head">
356+
<h2>${icon}<span>${esc(info.title || info.type)}</span></h2>
357+
<span class="pc-badges">${badges}${tags}</span>
358+
</header>
359+
${info.description ? `<p class="pc-detail-desc">${esc(info.description)}</p>` : ""}
360+
${noticeBoxes(info.warnings, "warning")}
361+
${noticeBoxes(info.info, "info")}
362+
${foot}
363+
</div>
364+
<dl class="metadata-wrapper pc-meta">
321365
<div class="fullwidth"><dt>Type</dt><dd><code>${esc(info.type)}</code></dd></div>
322366
${info.category ? `<div class="fullwidth"><dt>Category</dt><dd>${esc(info.category)}</dd></div>` : ""}
323-
${produces ? `<div class="fullwidth"><dt>Produces</dt><dd>${esc(produces)}</dd></div>` : ""}
324-
${info.description ? `<div class="fullwidth"><dt>About</dt><dd>${esc(info.description)}</dd></div>` : ""}
325367
</dl>
326368
${section("How to run", true, howToRunHtml(info))}
327369
${section("What can run on this", false, followupsHtml(info))}

webtool/templates/processor-catalogue.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ <h2 class="pc-browse-title">Browse processors</h2>
2020
<ul>
2121
<li><input id="pc-search" aria-label="Search processors" placeholder="Search processors…" autocomplete="off" spellcheck="false"></li>
2222
<li>
23-
<select id="pc-category" aria-label="Filter by category">
24-
<option value="">All categories</option>
23+
<select id="pc-tag" aria-label="Filter by tag">
24+
<option value="">All tags</option>
2525
</select>
2626
</li>
2727
<li><span id="pc-count" class="pc-count"></span></li>

0 commit comments

Comments
 (0)