feat: one-click source links on catalyst feed headlines
Every LLM catalyst read's headline now links to the raw article (raw_news_items.raw_url joined by headline), styled as a distinct dotted underline per NN/g citation guidance — any AI verdict traces to its source in one click. Rows without a matching raw item degrade to plain text. Join verified against prod: 0.29s for the full feed query. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
81bf1540ef
commit
0c28cf921f
2 files changed
+15
-3
No files matched your search
@@ -322,6 +322,10 @@
|
||||
.cf-nums { width: 40px; text-align: right; flex: none; color: var(--muted); font: 500 10px var(--mono); }
|
||||
.cf-text { flex: 1; min-width: 0; }
|
||||
.cf-head { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
/* one-click trace-back to the source article (NN/g: citations visible and
|
||||
styled distinctly, next to the claim) */
|
||||
.cf-link { color: inherit; text-decoration: underline dotted; text-decoration-color: var(--accent); text-underline-offset: 3px; }
|
||||
.cf-link:hover { color: var(--accent); }
|
||||
.cf-why { color: var(--muted); font-style: italic; font-size: 10px; overflow: hidden;
|
||||
text-overflow: ellipsis; white-space: nowrap; }
|
||||
/* clickable engine chip + its filter indicator */
|
||||
@@ -1281,7 +1285,9 @@ function renderCatalystFeed(feed) {
|
||||
+ '<div class="cf-nums" title="sentiment ' + sentTxt + ' · conviction ' + conv.toFixed(2) + '">'
|
||||
+ '<span class="cf-conv">' + conv.toFixed(2) + '</span></div>'
|
||||
+ '<div class="cf-text">'
|
||||
+ '<div class="cf-head">' + esc(r.headline || '') + '</div>'
|
||||
+ '<div class="cf-head">' + (r.url
|
||||
? '<a class="cf-link" href="' + esc(r.url) + '" target="_blank" rel="noopener">' + esc(r.headline || '') + ' ↗</a>'
|
||||
: esc(r.headline || '')) + '</div>'
|
||||
+ (r.rationale ? '<div class="cf-why">“' + esc(r.rationale) + '”</div>' : '')
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
|
||||
@@ -984,7 +984,12 @@ def query_catalyst_feed(store, limit: int = 40, lookback_hours: int = 24) -> lis
|
||||
try:
|
||||
rows = store.con.execute(
|
||||
"SELECT symbol, catalyst_type, sentiment, conviction, is_dilutive, "
|
||||
"rationale, headline, source, model, enriched_at "
|
||||
"rationale, headline, source, model, enriched_at, "
|
||||
# source URL for one-click trace-back (cache stores no URL; the raw
|
||||
# item that produced the headline does)
|
||||
"(SELECT r.raw_url FROM raw_news_items r "
|
||||
" WHERE r.raw_title = news_catalyst_cache.headline "
|
||||
" AND r.raw_url IS NOT NULL LIMIT 1) AS url "
|
||||
"FROM news_catalyst_cache WHERE enriched_at >= ? "
|
||||
"ORDER BY enriched_at DESC LIMIT ?",
|
||||
[cutoff, int(limit)],
|
||||
@@ -992,10 +997,11 @@ def query_catalyst_feed(store, limit: int = 40, lookback_hours: int = 24) -> lis
|
||||
except Exception: # noqa: BLE001 — table may not exist / Ollama never ran
|
||||
return []
|
||||
out: list[dict] = []
|
||||
for sym, ctype, sentiment, conv, is_dil, rationale, headline, source, model, ts in rows:
|
||||
for sym, ctype, sentiment, conv, is_dil, rationale, headline, source, model, ts, url in rows:
|
||||
conv_f = float(conv) if conv is not None else None
|
||||
is_dil_b = bool(is_dil)
|
||||
out.append({
|
||||
"url": url,
|
||||
"symbol": (sym or "").upper(),
|
||||
"catalyst_type": ctype,
|
||||
"sentiment": round(float(sentiment), 3) if sentiment is not None else None,
|
||||
|
||||
Reference in new issue
Block a user