Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 52 additions & 36 deletions content-scripts/minimap.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,23 @@ class MinimapManager {
const relativePosition = absoluteTop / documentHeight;
const markerPosition = relativePosition * minimapHeight;

const groupId = highlightElement.dataset.groupId;
const highlightElements = groupId
? Array.from(document.querySelectorAll(`.text-highlighter-extension[data-group-id='${groupId}']`))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Escape groupId before querySelectorAll selector interpolation

groupId is inserted raw into an attribute selector here, so a valid imported ID containing selector metacharacters (for example ' or ]) will cause querySelectorAll(...) to throw a SyntaxError and abort updateMarkers(), which leaves the minimap broken for that page. This is reachable because highlight import currently accepts any non-empty string for groupId; use CSS.escape(groupId) (or a non-selector lookup) before building the selector (and apply the same fix to the identical pattern in highlightTemporarily).

Useful? React with 👍 / 👎.

: [highlightElement];
const snippet = highlightElements
.map((element) => (element.textContent || '').trim())
.filter(Boolean)
.join(' ')
.replace(/\s+/g, ' ');
const tooltipText = snippet.length > 60 ? `${snippet.slice(0, 57)}...` : snippet;

// Create marker element
const marker = document.createElement('div');
marker.className = 'text-highlighter-minimap-marker';
marker.style.backgroundColor = highlightElement.style.backgroundColor;
marker.style.top = `${markerPosition}px`;
marker.dataset.highlightId = highlightElement.dataset.highlightId;
const snippet = (highlightElement.textContent || '').trim().replace(/\s+/g, ' ');
const tooltipText = snippet.length > 60 ? `${snippet.slice(0, 57)}...` : snippet;
// Avoid generic `data-tooltip` because some sites attach global tooltip
// styles to that attribute and override our minimap preview.
marker.dataset.minimapTooltip = tooltipText || 'Highlight';
Expand Down Expand Up @@ -214,53 +223,60 @@ class MinimapManager {
});
}

// Temporary emphasis effect for highlight
// Temporary emphasis effect for highlight group
highlightTemporarily(highlightElement) {
if (!highlightElement) return;

const elementKey = highlightElement;
const groupId = highlightElement.dataset.groupId;
const highlightElements = groupId
? Array.from(document.querySelectorAll(`.text-highlighter-extension[data-group-id='${groupId}']`))
: [highlightElement];

if (this.highlightTimers.has(elementKey)) {
clearTimeout(this.highlightTimers.get(elementKey));
this.highlightTimers.delete(elementKey);
}
highlightElements.forEach((element) => {
const elementKey = element;

if (this.highlightTimers.has(elementKey)) {
clearTimeout(this.highlightTimers.get(elementKey));
this.highlightTimers.delete(elementKey);
}

const isAlreadyHighlighted = highlightElement.hasAttribute('data-highlighted');
const isAlreadyHighlighted = element.hasAttribute('data-highlighted');

if (!isAlreadyHighlighted) {
const originalStyles = {
boxShadow: highlightElement.style.boxShadow,
transition: highlightElement.style.transition,
zIndex: highlightElement.style.zIndex
};
if (!isAlreadyHighlighted) {
const originalStyles = {
boxShadow: element.style.boxShadow,
transition: element.style.transition,
zIndex: element.style.zIndex
};

highlightElement.dataset.originalBoxShadow = originalStyles.boxShadow;
highlightElement.dataset.originalTransition = originalStyles.transition;
highlightElement.dataset.originalZIndex = originalStyles.zIndex;
element.dataset.originalBoxShadow = originalStyles.boxShadow;
element.dataset.originalTransition = originalStyles.transition;
element.dataset.originalZIndex = originalStyles.zIndex;

highlightElement.setAttribute('data-highlighted', 'true');
}
element.setAttribute('data-highlighted', 'true');
}

highlightElement.style.boxShadow = '0 0 0 3px rgba(255, 255, 255, 0.7), 0 0 0 6px rgba(0, 0, 0, 0.3)';
highlightElement.style.transition = 'box-shadow 0.3s';
highlightElement.style.zIndex = '10000'; // Display above other elements
element.style.boxShadow = '0 0 0 3px rgba(255, 255, 255, 0.7), 0 0 0 6px rgba(0, 0, 0, 0.3)';
element.style.transition = 'box-shadow 0.3s';
element.style.zIndex = '10000'; // Display above other elements

const timerId = setTimeout(() => {
if (highlightElement.hasAttribute('data-highlighted')) {
highlightElement.style.boxShadow = highlightElement.dataset.originalBoxShadow || '';
highlightElement.style.transition = highlightElement.dataset.originalTransition || '';
highlightElement.style.zIndex = highlightElement.dataset.originalZIndex || '';
const timerId = setTimeout(() => {
if (element.hasAttribute('data-highlighted')) {
element.style.boxShadow = element.dataset.originalBoxShadow || '';
element.style.transition = element.dataset.originalTransition || '';
element.style.zIndex = element.dataset.originalZIndex || '';

highlightElement.removeAttribute('data-highlighted');
delete highlightElement.dataset.originalBoxShadow;
delete highlightElement.dataset.originalTransition;
delete highlightElement.dataset.originalZIndex;
}
element.removeAttribute('data-highlighted');
delete element.dataset.originalBoxShadow;
delete element.dataset.originalTransition;
delete element.dataset.originalZIndex;
}

this.highlightTimers.delete(elementKey);
}, 1500);
this.highlightTimers.delete(elementKey);
}, 1500);

this.highlightTimers.set(elementKey, timerId);
this.highlightTimers.set(elementKey, timerId);
});
}

// Set minimap visibility
Expand Down
Loading