@@ -390,6 +390,8 @@ interface UserSettings {
390390 defaultFontSize : number ; // 10-20, pixels
391391 defaultGapThreshold : number ; // 1-60, seconds
392392 autoAnalyze : boolean ;
393+ minimapVisible : boolean ;
394+ minimapPreview : boolean ;
393395 theme : 'dark' | 'paper' ;
394396 sidebarSections : Record < string , boolean > ; // section-id → visible
395397}
@@ -409,6 +411,8 @@ const DEFAULT_SETTINGS: UserSettings = {
409411 defaultFontSize : 13 ,
410412 defaultGapThreshold : 5 ,
411413 autoAnalyze : false ,
414+ minimapVisible : true ,
415+ minimapPreview : true ,
412416 theme : 'dark' ,
413417 sidebarSections : { ...DEFAULT_SIDEBAR_SECTIONS } ,
414418} ;
@@ -815,6 +819,8 @@ const elements = {
815819 defaultGapThresholdSlider : document . getElementById ( 'default-gap-threshold' ) as HTMLInputElement ,
816820 defaultGapThresholdValue : document . getElementById ( 'default-gap-threshold-value' ) as HTMLSpanElement ,
817821 autoAnalyzeCheckbox : document . getElementById ( 'auto-analyze' ) as HTMLInputElement ,
822+ minimapVisibleCheckbox : document . getElementById ( 'minimap-visible' ) as HTMLInputElement ,
823+ minimapPreviewCheckbox : document . getElementById ( 'minimap-preview-enabled' ) as HTMLInputElement ,
818824 themeSelect : document . getElementById ( 'theme-select' ) as HTMLSelectElement ,
819825 btnResetSettings : document . getElementById ( 'btn-reset-settings' ) as HTMLButtonElement ,
820826 btnCloseSettings : document . getElementById ( 'btn-close-settings' ) as HTMLButtonElement ,
@@ -1952,6 +1958,12 @@ function createLogViewer(): void {
19521958 minimapElement . appendChild ( minimapContentElement ) ;
19531959 minimapElement . appendChild ( minimapViewportElement ) ;
19541960
1961+ // Minimap hover preview box
1962+ const previewEl = document . createElement ( 'div' ) ;
1963+ previewEl . className = 'minimap-preview hidden' ;
1964+ previewEl . id = 'minimap-preview' ;
1965+ minimapElement . appendChild ( previewEl ) ;
1966+
19551967 // Add to wrapper
19561968 logViewerWrapper . appendChild ( logViewerElement ) ;
19571969 logViewerWrapper . appendChild ( minimapElement ) ;
@@ -2016,6 +2028,8 @@ function createLogViewer(): void {
20162028 } , { passive : false } ) ;
20172029 minimapElement . addEventListener ( 'click' , handleMinimapClick ) ;
20182030 minimapElement . addEventListener ( 'mousedown' , handleMinimapDrag ) ;
2031+ minimapElement . addEventListener ( 'mousemove' , handleMinimapHover ) ;
2032+ minimapElement . addEventListener ( 'mouseleave' , hideMinimapPreview ) ;
20192033
20202034 // Use ResizeObserver for responsive updates
20212035 const resizeObserver = new ResizeObserver ( ( ) => {
@@ -3274,6 +3288,65 @@ function handleMinimapClick(event: MouseEvent): void {
32743288 logViewerElement . scrollTop = Math . max ( 0 , targetScrollTop ) ;
32753289}
32763290
3291+ // ─── Minimap Hover Preview ──────────────────────────────────────────────
3292+
3293+ let minimapPreviewTimer : ReturnType < typeof setTimeout > | null = null ;
3294+ let minimapPreviewLine = - 1 ;
3295+
3296+ function isMinimapPreviewEnabled ( ) : boolean {
3297+ return userSettings . minimapPreview ;
3298+ }
3299+
3300+ function handleMinimapHover ( event : MouseEvent ) : void {
3301+ if ( ! minimapElement || isDraggingMinimap || ! isMinimapPreviewEnabled ( ) ) return ;
3302+
3303+ const rect = minimapElement . getBoundingClientRect ( ) ;
3304+ const hoverY = event . clientY - rect . top ;
3305+ const minimapHeight = minimapElement . clientHeight ;
3306+ const totalLines = getTotalLines ( ) ;
3307+ if ( totalLines === 0 ) return ;
3308+
3309+ const targetLine = Math . floor ( ( hoverY / minimapHeight ) * totalLines ) ;
3310+ if ( targetLine === minimapPreviewLine ) return ; // Same line, skip
3311+ minimapPreviewLine = targetLine ;
3312+
3313+ // Debounce to avoid hammering getLines
3314+ if ( minimapPreviewTimer ) clearTimeout ( minimapPreviewTimer ) ;
3315+ minimapPreviewTimer = setTimeout ( async ( ) => {
3316+ const preview = document . getElementById ( 'minimap-preview' ) ;
3317+ if ( ! preview ) return ;
3318+
3319+ const PREVIEW_LINES = 7 ;
3320+ const startLine = Math . max ( 0 , targetLine - Math . floor ( PREVIEW_LINES / 2 ) ) ;
3321+ const result = await window . api . getLines ( startLine , PREVIEW_LINES ) ;
3322+ if ( ! result . success || ! result . lines ?. length ) {
3323+ preview . classList . add ( 'hidden' ) ;
3324+ return ;
3325+ }
3326+
3327+ let html = `<div class="minimap-preview-header">Line ${ targetLine + 1 } </div>` ;
3328+ for ( const line of result . lines ) {
3329+ const isCurrent = line . lineNumber === targetLine ;
3330+ const text = line . text . length > 120 ? line . text . substring ( 0 , 120 ) + '...' : line . text ;
3331+ html += `<div class="minimap-preview-line${ isCurrent ? ' current' : '' } "><span class="minimap-preview-num">${ line . lineNumber + 1 } </span>${ escapeHtml ( text ) } </div>` ;
3332+ }
3333+ preview . innerHTML = html ;
3334+
3335+ // Position: vertically centered on hover Y, to the left of the minimap
3336+ const previewHeight = Math . max ( 120 , ( PREVIEW_LINES + 1 ) * 16 + 8 ) ;
3337+ const top = Math . max ( 0 , Math . min ( hoverY - previewHeight / 2 , minimapHeight - previewHeight ) ) ;
3338+ preview . style . top = `${ top } px` ;
3339+ preview . classList . remove ( 'hidden' ) ;
3340+ } , 80 ) ;
3341+ }
3342+
3343+ function hideMinimapPreview ( ) : void {
3344+ if ( minimapPreviewTimer ) { clearTimeout ( minimapPreviewTimer ) ; minimapPreviewTimer = null ; }
3345+ minimapPreviewLine = - 1 ;
3346+ const preview = document . getElementById ( 'minimap-preview' ) ;
3347+ if ( preview ) preview . classList . add ( 'hidden' ) ;
3348+ }
3349+
32773350let isDraggingMinimap = false ;
32783351
32793352function handleMinimapDrag ( _event : MouseEvent ) : void {
@@ -11929,6 +12002,9 @@ function setupActivityBar(): void {
1192912002 elements . defaultGapThresholdSlider . value = userSettings . defaultGapThreshold . toString ( ) ;
1193012003 elements . defaultGapThresholdValue . textContent = `${ userSettings . defaultGapThreshold } s` ;
1193112004 elements . autoAnalyzeCheckbox . checked = userSettings . autoAnalyze ;
12005+ elements . minimapVisibleCheckbox . checked = userSettings . minimapVisible ;
12006+ elements . minimapPreviewCheckbox . checked = userSettings . minimapPreview ;
12007+ if ( minimapElement ) minimapElement . style . display = userSettings . minimapVisible ? '' : 'none' ;
1193212008 elements . themeSelect . value = userSettings . theme ;
1193312009 populateSidebarSectionToggles ( ) ;
1193412010 // Load Datadog config
@@ -13155,6 +13231,19 @@ function init(): void {
1315513231 saveSettings ( ) ;
1315613232 } ) ;
1315713233
13234+ elements . minimapVisibleCheckbox . addEventListener ( 'change' , ( ) => {
13235+ userSettings . minimapVisible = elements . minimapVisibleCheckbox . checked ;
13236+ saveSettings ( ) ;
13237+ if ( minimapElement ) minimapElement . style . display = userSettings . minimapVisible ? '' : 'none' ;
13238+ if ( ! userSettings . minimapVisible ) hideMinimapPreview ( ) ;
13239+ } ) ;
13240+
13241+ elements . minimapPreviewCheckbox . addEventListener ( 'change' , ( ) => {
13242+ userSettings . minimapPreview = elements . minimapPreviewCheckbox . checked ;
13243+ saveSettings ( ) ;
13244+ if ( ! userSettings . minimapPreview ) hideMinimapPreview ( ) ;
13245+ } ) ;
13246+
1315813247 elements . themeSelect . addEventListener ( 'change' , ( ) => {
1315913248 userSettings . theme = elements . themeSelect . value as 'dark' | 'paper' ;
1316013249 saveSettings ( ) ;
@@ -13172,6 +13261,9 @@ function init(): void {
1317213261 elements . defaultGapThresholdSlider . value = userSettings . defaultGapThreshold . toString ( ) ;
1317313262 elements . defaultGapThresholdValue . textContent = `${ userSettings . defaultGapThreshold } s` ;
1317413263 elements . autoAnalyzeCheckbox . checked = userSettings . autoAnalyze ;
13264+ elements . minimapVisibleCheckbox . checked = userSettings . minimapVisible ;
13265+ elements . minimapPreviewCheckbox . checked = userSettings . minimapPreview ;
13266+ if ( minimapElement ) minimapElement . style . display = userSettings . minimapVisible ? '' : 'none' ;
1317513267 elements . themeSelect . value = userSettings . theme ;
1317613268 applyTheme ( userSettings . theme ) ;
1317713269 populateSidebarSectionToggles ( ) ;
0 commit comments