Skip to content

Commit 58dc1c7

Browse files
ozgesolidkeyclaude
andcommitted
Improve minimap search markers with bucket aggregation and click-snap
- Bucket aggregation: every search match contributes to a position bucket (no more 100-result sampling cap), intensity by density - Click-snap: clicking near a search marker jumps to the actual nearest match line (within ~6px) instead of just proportional position - Updates search counter when snapping so navigation stays in sync Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eb1b285 commit 58dc1c7

1 file changed

Lines changed: 51 additions & 13 deletions

File tree

src/renderer/renderer.ts

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,9 +3239,36 @@ function handleMinimapClick(event: MouseEvent): void {
32393239

32403240
// Map click position to line number
32413241
const totalLines = getTotalLines();
3242-
const targetLine = Math.floor((clickY / minimapHeight) * totalLines);
3243-
const targetScrollTop = lineToScrollTop(targetLine);
3242+
const proportionalLine = Math.floor((clickY / minimapHeight) * totalLines);
3243+
3244+
// Snap to nearest search result if clicking near one (within ~3 buckets / ~6px)
3245+
let targetLine = proportionalLine;
3246+
if (state.searchResults.length > 0) {
3247+
const SNAP_RADIUS_LINES = Math.max(50, Math.floor((6 / minimapHeight) * totalLines));
3248+
let nearestResult: typeof state.searchResults[0] | null = null;
3249+
let nearestDist = Infinity;
3250+
for (const r of state.searchResults) {
3251+
const pos = state.isFiltered && r.displayIndex != null ? r.displayIndex : r.lineNumber;
3252+
const dist = Math.abs(pos - proportionalLine);
3253+
if (dist < nearestDist) {
3254+
nearestDist = dist;
3255+
nearestResult = r;
3256+
}
3257+
}
3258+
if (nearestResult && nearestDist <= SNAP_RADIUS_LINES) {
3259+
targetLine = state.isFiltered && nearestResult.displayIndex != null
3260+
? nearestResult.displayIndex : nearestResult.lineNumber;
3261+
// Update current search index too
3262+
const idx = state.searchResults.indexOf(nearestResult);
3263+
if (idx >= 0) {
3264+
state.currentSearchIndex = idx;
3265+
updateSearchUI();
3266+
updateSearchResultsCurrent();
3267+
}
3268+
}
3269+
}
32443270

3271+
const targetScrollTop = lineToScrollTop(targetLine);
32453272
logViewerElement.scrollTop = Math.max(0, targetScrollTop);
32463273
}
32473274

@@ -3458,17 +3485,28 @@ function renderMinimapMarkers(): void {
34583485
fragment.appendChild(marker);
34593486
}
34603487

3461-
// Add search result markers (limit to prevent performance issues)
3462-
const maxSearchMarkers = 100;
3463-
const searchStep = Math.max(1, Math.floor(state.searchResults.length / maxSearchMarkers));
3464-
for (let i = 0; i < state.searchResults.length; i += searchStep) {
3465-
const result = state.searchResults[i];
3466-
const marker = document.createElement('div');
3467-
marker.className = 'minimap-search-marker';
3468-
const markerPos = state.isFiltered && result.displayIndex != null
3469-
? result.displayIndex : result.lineNumber;
3470-
marker.style.top = `${(markerPos / totalLines) * minimapHeight}px`;
3471-
fragment.appendChild(marker);
3488+
// Add search result markers — bucket aggregation to show ALL matches accurately
3489+
if (state.searchResults.length > 0) {
3490+
const SEARCH_BUCKETS = Math.max(50, Math.floor(minimapHeight / 2));
3491+
const buckets = new Uint32Array(SEARCH_BUCKETS);
3492+
let maxBucket = 0;
3493+
for (const result of state.searchResults) {
3494+
const markerPos = state.isFiltered && result.displayIndex != null
3495+
? result.displayIndex : result.lineNumber;
3496+
const idx = Math.min(SEARCH_BUCKETS - 1, Math.floor((markerPos / totalLines) * SEARCH_BUCKETS));
3497+
buckets[idx]++;
3498+
if (buckets[idx] > maxBucket) maxBucket = buckets[idx];
3499+
}
3500+
const bucketHeight = minimapHeight / SEARCH_BUCKETS;
3501+
for (let i = 0; i < SEARCH_BUCKETS; i++) {
3502+
if (buckets[i] === 0) continue;
3503+
const intensity = Math.min(1, 0.4 + (buckets[i] / maxBucket) * 0.6);
3504+
const marker = document.createElement('div');
3505+
marker.className = 'minimap-search-marker';
3506+
marker.style.cssText = `position:absolute;left:0;right:0;top:${i * bucketHeight}px;height:${Math.max(2, Math.ceil(bucketHeight))}px;background-color:rgba(255,255,0,${intensity});`;
3507+
marker.title = `${buckets[i]} match${buckets[i] !== 1 ? 'es' : ''}`;
3508+
fragment.appendChild(marker);
3509+
}
34723510
}
34733511

34743512
// Add search config markers (colored by config)

0 commit comments

Comments
 (0)