Files
dobetternorge-tools/citations.php
T
daveadmin 04555a96b1 Add Citation Explorer tool and graph-expansion badges to Advocate results
- citations.php + assets/js/citations.js: new tool page for browsing the
  FalkorDB citation graph by title/ID, with autocomplete, action pills
  (cites/cited_by/implements/chain), hop-by-hop navigation, and exploration trail
- advocate.js: tag graph-expanded source cards with 'via citation graph' badge
- DeepResearchAgent: propagate _graph_expanded flag through normalizeCorpusChunk
  and top_sources serialization so it reaches the frontend
- tools.css: add .dr-source-tag--graph variant (green pill)
- i18n.php: register 'citations' tool in all 4 languages with CIT icon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:30:04 +02:00

139 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
$toolName = 'citations';
$extraScripts = ['assets/js/citations.js'];
ob_start();
?>
<div class="reasoning-head">
<p class="eyebrow">Exploration trail</p>
<h2 id="reasoningTitle">Navigation history</h2>
</div>
<ol id="trailList" class="trace-list" aria-live="polite">
<li id="trailPlaceholder">
<span class="trace-status waiting"></span>
<div>
<strong>Nothing explored yet</strong>
<p>Search for a document to start.</p>
</div>
</li>
</ol>
<?php
$reasoningPanelOverride = ob_get_clean();
require_once __DIR__ . '/includes/layout.php';
?>
<style>
.cit-search-wrap { position: relative; }
.cit-autocomplete {
position: absolute; top: 100%; left: 0; right: 0; z-index: 50;
background: var(--panel); border: 1px solid var(--line);
border-radius: 0 0 8px 8px; box-shadow: 0 6px 20px rgba(0,0,0,0.12);
list-style: none; margin: 0; padding: 0; max-height: 280px; overflow-y: auto;
}
.cit-autocomplete li {
padding: 9px 14px; cursor: pointer; font-size: 0.87rem;
border-bottom: 1px solid var(--line);
}
.cit-autocomplete li:last-child { border-bottom: none; }
.cit-autocomplete li:hover, .cit-autocomplete li.is-focused { background: var(--soft-teal); }
.cit-autocomplete .cit-ac-atype { font-size: 0.72rem; color: var(--muted); margin-left: 6px; }
.cit-source-card {
background: var(--panel); border: 1px solid var(--teal);
border-radius: 8px; padding: 14px 16px; margin-bottom: 16px;
}
.cit-source-card__title { font-size: 1rem; font-weight: 700; color: var(--ink); margin: 0 0 6px; }
.cit-source-card__meta {
display: flex; flex-wrap: wrap; gap: 8px;
align-items: center; font-size: 0.78rem; color: var(--muted);
}
.cit-results-header {
display: flex; align-items: baseline;
justify-content: space-between; margin-bottom: 10px;
}
.cit-results-count { font-size: 0.78rem; color: var(--muted); }
.cit-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 10px;
}
.cit-card {
background: var(--panel); border: 1px solid var(--line);
border-radius: 8px; padding: 12px 14px;
display: flex; flex-direction: column; gap: 6px;
}
.cit-card--unresolved { opacity: 0.72; }
.rel-chip {
display: inline-block; font-size: 0.68rem; font-weight: 700;
letter-spacing: 0.04em; text-transform: uppercase;
padding: 2px 8px; border-radius: 99px; align-self: flex-start;
}
.rel-chip--cites { background: var(--soft-teal); color: var(--teal-dark); }
.rel-chip--cited-by { background: #dbeafe; color: #1d4ed8; }
.rel-chip--implements { background: #fef3cd; color: var(--amber); }
.rel-chip--repeals { background: var(--soft-coral); color: var(--coral); }
.rel-chip--reachable { background: #eef0f5; color: var(--muted); }
.cit-card__title { font-size: 0.87rem; font-weight: 700; color: var(--ink); line-height: 1.3; }
.cit-card__ref { font-size: 0.82rem; color: var(--muted); font-style: italic; }
.cit-card__meta { font-size: 0.75rem; color: var(--muted); display: flex; gap: 8px; }
.cit-explore-btn {
margin-top: auto; align-self: flex-start;
font-size: 0.78rem; padding: 3px 12px;
border: 1px solid var(--teal); border-radius: 4px;
background: var(--soft-teal); color: var(--teal-dark); cursor: pointer;
}
.cit-explore-btn:hover { background: var(--teal); color: #fff; }
.cit-no-results { font-size: 0.88rem; color: var(--muted); font-style: italic; padding: 12px 0; }
.cit-trail-item { cursor: pointer; }
.cit-trail-item:hover strong { text-decoration: underline; color: var(--teal); }
.cit-trail-item.is-active { background: var(--soft-teal); border-radius: 6px; padding: 4px 6px; }
</style>
<form id="citationsForm" class="tool-form" autocomplete="off" novalidate>
<label class="input-label" for="titleInput">Document title or ID</label>
<div class="cit-search-wrap">
<input type="text" id="titleInput" class="corpus-search-input"
placeholder="e.g. Barnevernloven, HR-2020-661-A, LOV-1999-07-02-63, 12345"
autocomplete="off" spellcheck="false"
aria-autocomplete="list" aria-haspopup="listbox" aria-controls="citAutocomplete">
<ul id="citAutocomplete" class="cit-autocomplete" hidden role="listbox" aria-label="Matching documents"></ul>
</div>
<input type="hidden" id="docIdHidden">
<div class="control-row" role="group" aria-label="Relationship direction">
<label><input type="radio" name="citAction" value="cites" checked> Cites</label>
<label><input type="radio" name="citAction" value="cited_by"> Cited by</label>
<label><input type="radio" name="citAction" value="implements"> Implements / amends</label>
<label><input type="radio" name="citAction" value="chain"> Citation chain</label>
</div>
<div id="depthRow" class="control-row" hidden>
<label class="input-label" for="depthRange" style="margin:0">
Chain depth: <strong id="depthValue">2</strong> hops
</label>
<input type="range" id="depthRange" min="1" max="3" value="2" style="width:120px">
</div>
<div class="form-footer">
<button type="submit" id="citeRunBtn" class="secondary-button">Explore</button>
<span id="citStatus" class="form-status"></span>
</div>
</form>
<div id="citationResults" hidden>
<div id="citSourceCard" class="cit-source-card"></div>
<div id="citResultsArea"></div>
</div>
<?php require_once __DIR__ . '/includes/layout_footer.php'; ?>