feat: add navigation to modified lines in prompt comparison (#723)

# PR Checklist
- [ ] Have you checked if it works normally in all models? *Ignore this
if it doesn't use models.*
- [x] Have you checked if it works normally in all web, local, and node
hosted versions? If it doesn't, have you blocked it in those versions?
- [x] Have you added type definitions?

# Description
This PR adds a small improvement to the prompt comparison functionality.

When the two prompts are different, the tooltip overview now enables
clicking on modified parts to navigate directly to the corresponding
lines.

Previously, finding the modified parts required manual scrolling. With
this update, it becomes much easier to locate and review the changes,
even for very long prompts.

When navigating to a modified part, the target line is highlighted in
yellow for 1 second to indicate the clicked location.



https://github.com/user-attachments/assets/577973cc-18d0-4e73-814e-7ba01bb9950c


For mobile devices, this functionality has been tested on iOS 16.7.

If there are any suggestions for improvement or concerns about the
implementation, feedback is greatly appreciated. It’s completely
understandable if this PR needs to be rejected or revised further.

Thank you for taking the time to review this!
This commit is contained in:
kwaroran
2025-01-17 22:53:57 +09:00
committed by GitHub

View File

@@ -121,6 +121,14 @@
let addedLinesCount = 0
let removedLinesCount = 0
const forTooltip = (line: string, idx: number): string => {
return line.replace('<div', `<div class="prompt-diff-hover" data-line-id="prompt-diff-line-${idx}"`)
}
const withId = (line: string, idx: number): string => {
return line.replace('<div', `<div id="prompt-diff-line-${idx}"`)
}
for (let i = 0; i < lineDiffs.length; i++) {
const linePart = lineDiffs[i]
@@ -128,22 +136,22 @@
const nextPart = lineDiffs[i + 1]
if(nextPart?.added){
const modifiedLine = `<div style="border-left: 4px solid blue; padding-left: 8px;">${await highlightChanges(linePart.value, nextPart.value)}</div>`
changedLines.push(modifiedLine)
resultHtml += modifiedLine
changedLines.push(forTooltip(modifiedLine, i))
resultHtml += withId(modifiedLine, i)
i++;
modifiedLinesCount += 1
}
else{
const removedLine = `<div style="color: red; background-color: #ffe6e6; border-left: 4px solid red; padding-left: 8px;">${escapeHtml(linePart.value)}</div>`
changedLines.push(removedLine)
resultHtml += removedLine
changedLines.push(forTooltip(removedLine, i))
resultHtml += withId(removedLine, i)
removedLinesCount += 1
}
}
else if(linePart.added){
const addedLine = `<div style="color: green; background-color: #e6ffe6; border-left: 4px solid green; padding-left: 8px;">${escapeHtml(linePart.value)}</div>`
changedLines.push(addedLine)
resultHtml += addedLine
changedLines.push(forTooltip(addedLine, i))
resultHtml += withId(addedLine, i)
addedLinesCount += 1
}
else{
@@ -227,6 +235,26 @@
tooltip.style.display = 'block'
})
tooltip.addEventListener('click', (e) => {
const target = (e.target as HTMLElement).closest('.prompt-diff-hover')
const lineId = target?.getAttribute('data-line-id')
if(!lineId){
return
}
const targetElement = document.getElementById(lineId)
if(!targetElement){
return
}
targetElement.classList.add('prompt-diff-highlight')
targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' })
setTimeout(() => {
targetElement.classList.remove('prompt-diff-highlight')
}, 1000)
})
differencesDetected.addEventListener('mouseleave', () => {
tooltip.style.display = 'none'
})
@@ -393,4 +421,19 @@
word-break: normal;
overflow-wrap: anywhere;
}
:global(.prompt-diff-hover){
border-radius: 8px;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
:global(.prompt-diff-hover:hover){
transform: translateY(-4px);
box-shadow: 0px 8px 12px rgba(0, 0, 0, 0.2);
}
:global(.prompt-diff-highlight){
background-color: yellow !important;
}
</style>