Skip to content

Commit 67fc63f

Browse files
committed
fix(ui): content-keyed diff word parts (biome noArrayIndexKey)
1 parent 00e11ae commit 67fc63f

1 file changed

Lines changed: 17 additions & 13 deletions

File tree

src/ui/Message.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -442,16 +442,12 @@ function buildDiff(oldStr: string, newStr: string): DiffInfo {
442442
hunks.push({
443443
type: "remove",
444444
text: removeLines[j],
445-
wordParts: parts
446-
.filter((p) => !p.added)
447-
.map((p) => ({ text: p.value, highlight: !!p.removed })),
445+
wordParts: parts.filter((p) => !p.added).map((p) => ({ text: p.value, highlight: !!p.removed })),
448446
});
449447
hunks.push({
450448
type: "add",
451449
text: addLines[j],
452-
wordParts: parts
453-
.filter((p) => !p.removed)
454-
.map((p) => ({ text: p.value, highlight: !!p.added })),
450+
wordParts: parts.filter((p) => !p.removed).map((p) => ({ text: p.value, highlight: !!p.added })),
455451
});
456452
}
457453
} else {
@@ -492,6 +488,8 @@ function DiffSummary({ diff, width, keyPrefix }: { diff: DiffInfo; width: number
492488
return (
493489
<Box flexDirection="column" marginLeft={2}>
494490
<Text dimColor>{counts}</Text>
491+
{/* biome-ignore lint/suspicious/noArrayIndexKey: hunks are freshly built per render from
492+
immutable args; no reorder, no insertion, so index is a stable per-render key */}
495493
{diff.hunks.map((h, i) => {
496494
const isRemove = h.type === "remove";
497495
const sign = isRemove ? " - " : " + ";
@@ -513,17 +511,23 @@ function DiffSummary({ diff, width, keyPrefix }: { diff: DiffInfo; width: number
513511
break;
514512
}
515513
}
514+
// Stable keys per-word: counter-suffix is only for collision when
515+
// the same word appears multiple times in a line. Avoids the
516+
// array-index-as-key smell while keeping React's reconciler happy.
517+
const seenCounts = new Map<string, number>();
518+
const keyedParts = visibleParts.map((p) => {
519+
const baseKey = `${p.highlight ? "h" : "n"}:${p.text}`;
520+
const count = seenCounts.get(baseKey) ?? 0;
521+
seenCounts.set(baseKey, count + 1);
522+
return { part: p, k: `${key}-w-${baseKey}-${count}` };
523+
});
516524
return (
517525
<Box key={key}>
518526
<Text color={lineColor}>{sign}</Text>
519527
<Text>
520-
{visibleParts.map((p, j) => (
521-
<Text
522-
key={`${key}-w-${j}`}
523-
color={lineColor}
524-
backgroundColor={p.highlight ? hlBg : undefined}
525-
>
526-
{p.text}
528+
{keyedParts.map(({ part, k }) => (
529+
<Text key={k} color={lineColor} backgroundColor={part.highlight ? hlBg : undefined}>
530+
{part.text}
527531
</Text>
528532
))}
529533
</Text>

0 commit comments

Comments
 (0)