fix: resolve a citation by its number, not its position in the array
A marker names a source by the `number` dedupeSources assigns server-side. Rendering looked it up as sources[n - 1], which works only while the array order and the numbers agree. Nothing breaks that today, but it is an implicit contract across a network boundary: any later filtering or reordering of the list — hiding low-score sources, say — would point citations at the wrong source silently, which is worse than not linking at all. Matching on the number cannot drift. Positional lookup remains as the fallback for a list whose entries carry no number. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
This commit is contained in:
parent
e2444066a0
commit
6dcdf36c81
1 changed files with 17 additions and 3 deletions
|
|
@ -88,6 +88,20 @@ export function wrapTables(html) {
|
||||||
.replace(/<\/table>/g, '</table></div>');
|
.replace(/<\/table>/g, '</table></div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A citation marker names a source by its `number`, which dedupeSources assigns
|
||||||
|
// server-side. Resolving it by array position happens to work today because the
|
||||||
|
// two agree, but any future filtering or reordering of the list between the
|
||||||
|
// server and here would silently point citations at the wrong source. Matching
|
||||||
|
// on the number cannot drift.
|
||||||
|
function sourceByNumber(sources, n) {
|
||||||
|
var list = sources || [];
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
if (list[i] && Number(list[i].number) === Number(n)) return list[i];
|
||||||
|
}
|
||||||
|
// Sources predating the numbering, or a caller passing a bare list.
|
||||||
|
return list[n - 1];
|
||||||
|
}
|
||||||
|
|
||||||
export function renderCitationLinks(html, sources, options) {
|
export function renderCitationLinks(html, sources, options) {
|
||||||
var opts = options || {};
|
var opts = options || {};
|
||||||
return String(html || '').replace(new RegExp(
|
return String(html || '').replace(new RegExp(
|
||||||
|
|
@ -95,9 +109,9 @@ export function renderCitationLinks(html, sources, options) {
|
||||||
'gi'), function (match, tag, cluster) {
|
'gi'), function (match, tag, cluster) {
|
||||||
if (!cluster) return match;
|
if (!cluster) return match;
|
||||||
var nums = cluster.split(',').map(function (n) { return Number(n.trim()); }).filter(function (n) { return Number.isInteger(n) && n > 0; });
|
var nums = cluster.split(',').map(function (n) { return Number(n.trim()); }).filter(function (n) { return Number.isInteger(n) && n > 0; });
|
||||||
if (!nums.length || nums.some(function (n) { return !sources[n - 1]; })) return match;
|
if (!nums.length || nums.some(function (n) { return !sourceByNumber(sources, n); })) return match;
|
||||||
return nums.map(function (n) {
|
return nums.map(function (n) {
|
||||||
var source = sources[n - 1];
|
var source = sourceByNumber(sources, n);
|
||||||
var title = source ? source.title || source.resource || 'Source' : 'Source';
|
var title = source ? source.title || source.resource || 'Source' : 'Source';
|
||||||
var page = source && (source.page || source.page_number || source.pageNumber);
|
var page = source && (source.page || source.page_number || source.pageNumber);
|
||||||
var label = 'Source ' + n + ': ' + title + (page ? ', page ' + page : '');
|
var label = 'Source ' + n + ': ' + title + (page ? ', page ' + page : '');
|
||||||
|
|
@ -136,7 +150,7 @@ function parseCitationCluster(cluster) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function allCitationsAvailable(nums, sources) {
|
function allCitationsAvailable(nums, sources) {
|
||||||
return nums.length > 0 && nums.every(function(n) { return sources[n - 1]; });
|
return nums.length > 0 && nums.every(function(n) { return sourceByNumber(sources, n); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCitationCluster(nums) {
|
function formatCitationCluster(nums) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue