From fa1d74f6012b9518927375cfe86b05b40acc8a0f Mon Sep 17 00:00:00 2001 From: Raymond Yee Date: Fri, 10 Jul 2026 06:35:19 -0700 Subject: [PATCH 1/2] #323: PID + Source URL columns in samples table and CSV export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Andrea's ask (#323): 1. PID column in the samples table (was CSV-only) — placed after Label, ellipsized via CSS (pids like ark:/28722/… are long) with the full pid on hover and always complete in the CSV. 2. Source URL column — a dedicated external link (n2t.net resolver via the existing unit-tested sourceUrl() helper) to the sample's landing page at its source repository, in both the table and the CSV (appended as the LAST csv column so #312-era consumers keep their column positions). Does not reopen #226 (which removed the label cell's href): the external link lives in its own honestly-labeled cell, and activateRow now ignores clicks originating inside an so a link click opens the record without also selecting/flying the row. Refs #323. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AjnkWb4HpuLeDbYfzmY3X9 --- explorer.qmd | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/explorer.qmd b/explorer.qmd index bfa6390..c55957f 100644 --- a/explorer.qmd +++ b/explorer.qmd @@ -540,6 +540,26 @@ format: white-space: nowrap; } .samples-table tr:last-child td { border-bottom: 0; } + /* #323: PID column — pids are long (ark:/28722/…); keep the column + compact with ellipsis, full pid available on hover (title attr) and + always complete in the CSV export. */ + .samples-table td.pid-cell { + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 11px; + color: #555; + } + /* #323: Source URL column — dedicated external link (see activateRow's + anchor guard: clicking it must not also select/fly the row). */ + .samples-table a.row-source-link { + color: #1565c0; + white-space: nowrap; + text-decoration: none; + } + .samples-table a.row-source-link:hover { text-decoration: underline; } .table-badge { color: white; padding: 2px 7px; @@ -2654,6 +2674,18 @@ tableView = { const labelHtml = `${safeLabel}`; const pidAttr = escapeHtml(r.pid || ''); const selectedClass = (r.pid && r.pid === selectedPid) ? ' class="selected"' : ''; + // #323: PID column (Andrea) — pids are long (ark:/28722/…), so + // the cell ellipsizes via CSS with the full pid in title/hover. + // Source URL column: an explicit external link to the sample's + // landing page at its source repository. This does NOT reopen + // #226 (which removed the *label* cell's href because click- + // anywhere = open card): here the external link is its own + // dedicated, honestly-labeled cell; activateRow ignores clicks + // that originate inside an so the row doesn't also fly. + const rowSrcUrl = sourceUrl(r.pid); + const srcUrlHtml = rowSrcUrl + ? `record ↗` + : ''; // #311: material/context/object_type are URIs (from facets_url, // joined in loadPage()); resolve to human labels the same way // the facet tree and search results already do. context is @@ -2666,6 +2698,7 @@ tableView = { return ` ${tableSourceBadge(r.source)} ${labelHtml} + ${pidAttr} ${escapeHtml(place)} ${escapeHtml(r.result_time || '')} ${uriLabel(r.material)} @@ -2673,11 +2706,12 @@ tableView = { ${uriLabel(r.context)} ${escapeHtml(lat)} ${escapeHtml(lng)} + ${srcUrlHtml} `; }).join(''); tableEl.innerHTML = `
- + ${body}
SourceLabelPlaceDateMaterialObject typeSampled featureLatLon
SourceLabelPIDPlaceDateMaterialObject typeSampled featureLatLonSource URL
`; @@ -2695,6 +2729,10 @@ tableView = { // #226: there is no longer an in the row, so // the activation is uniform across surfaces. const activateRow = async (e) => { + // #323: a click on the Source URL (or any future in-row + // anchor) means "open the external record", not "select this + // row" — let the browser handle the link alone. + if (e && e.target && e.target.closest && e.target.closest('a')) return; const pid = tr.dataset.pid; const sample = pid ? pageRowsByPid.get(pid) : null; if (!sample || sample.latitude == null || sample.longitude == null) return; @@ -2998,7 +3036,10 @@ tableView = { // (same "honesty" convention as the rest of this table) rather than // silently truncating. const CSV_ROW_CAP = 50000; - const CSV_HEADERS = ['pid', 'source', 'label', 'place', 'date', 'material', 'object_type', 'sampled_feature', 'latitude', 'longitude']; + // #323: source_url appended LAST so existing consumers of the #312 column + // order keep working; it's the same n2t.net resolver link the table's + // "Source URL" column shows. + const CSV_HEADERS = ['pid', 'source', 'label', 'place', 'date', 'material', 'object_type', 'sampled_feature', 'latitude', 'longitude', 'source_url']; function csvField(v) { let s = v == null ? '' : String(v); @@ -3023,7 +3064,7 @@ tableView = { : ''; return [r.pid, r.source, r.label, place, r.result_time, labelFor(r.material), labelFor(r.object_type), labelFor(r.context), - r.latitude, r.longitude].map(csvField).join(','); + r.latitude, r.longitude, sourceUrl(r.pid) || ''].map(csvField).join(','); } let downloadInFlight = false; From 9ed195c2de0020c4a217d07c1064171afc3afb52 Mon Sep 17 00:00:00 2001 From: Raymond Yee Date: Fri, 10 Jul 2026 07:05:47 -0700 Subject: [PATCH 2/2] #323: row-specific aria-label on Source URL links (Codex review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Screen-reader users tabbing down the column otherwise hear an undifferentiated list of identical "record ↗" links. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AjnkWb4HpuLeDbYfzmY3X9 --- explorer.qmd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/explorer.qmd b/explorer.qmd index c55957f..d975e05 100644 --- a/explorer.qmd +++ b/explorer.qmd @@ -2683,8 +2683,11 @@ tableView = { // dedicated, honestly-labeled cell; activateRow ignores clicks // that originate inside an so the row doesn't also fly. const rowSrcUrl = sourceUrl(r.pid); + // Row-specific aria-label (Codex review): screen-reader users + // tabbing the column hear WHICH record each identical + // "record ↗" link opens. const srcUrlHtml = rowSrcUrl - ? `record ↗` + ? `record ↗` : ''; // #311: material/context/object_type are URIs (from facets_url, // joined in loadPage()); resolve to human labels the same way