@@ -656,7 +656,6 @@ const elements = {
656656 btnNextResult : document . getElementById ( 'btn-next-result' ) as HTMLButtonElement ,
657657 btnFilter : document . getElementById ( 'btn-filter' ) as HTMLButtonElement ,
658658 btnAnalyze : document . getElementById ( 'btn-analyze' ) as HTMLButtonElement ,
659- btnToggleSidebar : document . getElementById ( 'btn-toggle-sidebar' ) as HTMLButtonElement ,
660659 searchInput : document . getElementById ( 'search-input' ) as HTMLInputElement ,
661660 searchRegex : document . getElementById ( 'search-regex' ) as HTMLInputElement ,
662661 searchWildcard : document . getElementById ( 'search-wildcard' ) as HTMLInputElement ,
@@ -768,7 +767,6 @@ const elements = {
768767 helpModal : document . getElementById ( 'help-modal' ) as HTMLDivElement ,
769768 btnCloseHelp : document . getElementById ( 'btn-close-help' ) as HTMLButtonElement ,
770769 // Settings
771- btnSettings : document . getElementById ( 'btn-settings' ) as HTMLButtonElement ,
772770 settingsModal : document . getElementById ( 'settings-modal' ) as HTMLDivElement ,
773771 scrollSpeedSlider : document . getElementById ( 'scroll-speed' ) as HTMLInputElement ,
774772 scrollSpeedValue : document . getElementById ( 'scroll-speed-value' ) as HTMLSpanElement ,
@@ -1868,20 +1866,26 @@ function createLogViewer(): void {
18681866 let deltaX = e . deltaX ;
18691867
18701868 if ( e . deltaMode === 0 ) {
1871- // Pixel-based scrolling (trackpad) - apply user's scroll speed setting
1869+ // Pixel-based scrolling (trackpad) - apply user's scroll speed setting for vertical only
18721870 const speedFactor = userSettings . scrollSpeed / 100 ;
18731871 deltaY = deltaY * speedFactor ;
1874- deltaX = deltaX * speedFactor ;
1872+ // Horizontal scroll uses raw pixel values for natural trackpad feel
18751873 } else if ( e . deltaMode === 1 ) {
18761874 // Line-based scrolling (mouse wheel) - convert to pixels
18771875 deltaY = deltaY * getLineHeight ( ) ;
1878- deltaX = deltaX * getLineHeight ( ) ;
1876+ deltaX = deltaX * getLineHeight ( ) * 3 ; // 3x multiplier for faster horizontal nav
18791877 } else if ( e . deltaMode === 2 ) {
18801878 // Page-based scrolling - convert to pixels
18811879 deltaY = deltaY * logViewerElement ! . clientHeight ;
18821880 deltaX = deltaX * logViewerElement ! . clientWidth ;
18831881 }
18841882
1883+ // Shift+Scroll: convert vertical scroll to horizontal for easy line navigation
1884+ if ( e . shiftKey && deltaY !== 0 && deltaX === 0 ) {
1885+ deltaX = deltaY ;
1886+ deltaY = 0 ;
1887+ }
1888+
18851889 // Apply normalized vertical scroll
18861890 const newScrollTop = logViewerElement ! . scrollTop + deltaY ;
18871891 const maxScrollY = logViewerElement ! . scrollHeight - logViewerElement ! . clientHeight ;
@@ -4246,22 +4250,9 @@ function showNotesDrawer(): void {
42464250 if ( state . searchConfigsPanelVisible ) {
42474251 closeSearchConfigsPanel ( ) ;
42484252 }
4249- const overlay = elements . notesOverlay ;
4250- overlay . classList . remove ( 'hidden' ) ;
4251- // Force reflow so the transition triggers from initial state
4252- void overlay . offsetHeight ;
4253- overlay . classList . add ( 'visible' ) ;
4253+ elements . notesOverlay . classList . remove ( 'hidden' ) ;
42544254 elements . btnNotesToggle . classList . add ( 'active' ) ;
4255-
4256- // Focus textarea after slide animation completes
4257- const drawer = overlay . querySelector ( '.notes-drawer' ) as HTMLElement ;
4258- if ( drawer ) {
4259- const onShown = ( ) => {
4260- drawer . removeEventListener ( 'transitionend' , onShown ) ;
4261- elements . notesTextarea . focus ( ) ;
4262- } ;
4263- drawer . addEventListener ( 'transitionend' , onShown ) ;
4264- }
4255+ elements . notesTextarea . focus ( ) ;
42654256
42664257 // Load notes content
42674258 window . api . loadNotes ( ) . then ( ( result ) => {
@@ -4274,20 +4265,8 @@ function showNotesDrawer(): void {
42744265}
42754266
42764267function hideNotesDrawer ( ) : void {
4277- const overlay = elements . notesOverlay ;
4278- overlay . classList . remove ( 'visible' ) ;
4268+ elements . notesOverlay . classList . add ( 'hidden' ) ;
42794269 elements . btnNotesToggle . classList . remove ( 'active' ) ;
4280- // Fallback if transitionend doesn't fire (e.g., display:none, no transition)
4281- const fallback = setTimeout ( ( ) => hide ( ) , 350 ) ;
4282- const hide = ( ) => {
4283- clearTimeout ( fallback ) ;
4284- overlay . removeEventListener ( 'transitionend' , onEnd ) ;
4285- if ( ! overlay . classList . contains ( 'visible' ) ) {
4286- overlay . classList . add ( 'hidden' ) ;
4287- }
4288- } ;
4289- const onEnd = ( ) => hide ( ) ;
4290- overlay . addEventListener ( 'transitionend' , onEnd ) ;
42914270}
42924271
42934272function toggleNotesDrawer ( ) : void {
@@ -4372,25 +4351,11 @@ function showSearchResultsPanel(): void {
43724351 closeSearchConfigsPanel ( ) ;
43734352 }
43744353 state . searchResultsPanelVisible = true ;
4375- const overlay = elements . searchResultsOverlay ;
4376- overlay . classList . remove ( 'hidden' ) ;
4377- void overlay . offsetHeight ;
4378- overlay . classList . add ( 'visible' ) ;
4354+ elements . searchResultsOverlay . classList . remove ( 'hidden' ) ;
43794355}
43804356
43814357function hideSearchResultsPanel ( ) : void {
4382- const overlay = elements . searchResultsOverlay ;
4383- overlay . classList . remove ( 'visible' ) ;
4384- const fallback = setTimeout ( ( ) => hide ( ) , 350 ) ;
4385- const hide = ( ) => {
4386- clearTimeout ( fallback ) ;
4387- overlay . removeEventListener ( 'transitionend' , onEnd ) ;
4388- if ( ! overlay . classList . contains ( 'visible' ) ) {
4389- overlay . classList . add ( 'hidden' ) ;
4390- }
4391- } ;
4392- const onEnd = ( ) => hide ( ) ;
4393- overlay . addEventListener ( 'transitionend' , onEnd ) ;
4358+ elements . searchResultsOverlay . classList . add ( 'hidden' ) ;
43944359}
43954360
43964361function toggleSearchResultsPanel ( ) : void {
@@ -4537,27 +4502,13 @@ function showSearchConfigsPanel(): void {
45374502 if ( state . notesVisible ) closeNotesDrawer ( ) ;
45384503 if ( state . searchResultsPanelVisible ) closeSearchResultsPanel ( ) ;
45394504 state . searchConfigsPanelVisible = true ;
4540- const overlay = elements . searchConfigsOverlay ;
4541- overlay . classList . remove ( 'hidden' ) ;
4542- void overlay . offsetHeight ;
4543- overlay . classList . add ( 'visible' ) ;
4505+ elements . searchConfigsOverlay . classList . remove ( 'hidden' ) ;
45444506 elements . btnSearchConfigs . classList . add ( 'active' ) ;
45454507}
45464508
45474509function hideSearchConfigsPanel ( ) : void {
4548- const overlay = elements . searchConfigsOverlay ;
4549- overlay . classList . remove ( 'visible' ) ;
4510+ elements . searchConfigsOverlay . classList . add ( 'hidden' ) ;
45504511 elements . btnSearchConfigs . classList . remove ( 'active' ) ;
4551- const fallback = setTimeout ( ( ) => hide ( ) , 350 ) ;
4552- const hide = ( ) => {
4553- clearTimeout ( fallback ) ;
4554- overlay . removeEventListener ( 'transitionend' , onEnd ) ;
4555- if ( ! overlay . classList . contains ( 'visible' ) ) {
4556- overlay . classList . add ( 'hidden' ) ;
4557- }
4558- } ;
4559- const onEnd = ( ) => hide ( ) ;
4560- overlay . addEventListener ( 'transitionend' , onEnd ) ;
45614512}
45624513
45634514function toggleSearchConfigsPanel ( ) : void {
@@ -5087,8 +5038,10 @@ async function loadFile(filePath: string, createNewTab: boolean = true): Promise
50875038 elements . btnColumns . disabled = false ;
50885039 state . columnConfig = null ; // Reset column config for new file
50895040
5090- // Show warning for files with long lines
5091- if ( result . hasLongLines ) {
5041+ // Show warning for files with long lines (only for JSON-like files where reformatting helps)
5042+ const lowerPath = filePath . toLowerCase ( ) ;
5043+ const isJsonLike = lowerPath . endsWith ( '.json' ) || lowerPath . endsWith ( '.jsonl' ) || lowerPath . endsWith ( '.ndjson' ) ;
5044+ if ( result . hasLongLines && isJsonLike ) {
50925045 elements . longLinesWarning . classList . remove ( 'hidden' ) ;
50935046 } else {
50945047 elements . longLinesWarning . classList . add ( 'hidden' ) ;
@@ -7927,8 +7880,33 @@ function setupActivityBar(): void {
79277880 elements . btnPinPanel . addEventListener ( 'click' , togglePinPanel ) ;
79287881
79297882 // Settings button in activity bar
7930- document . getElementById ( 'btn-activity-settings' ) ?. addEventListener ( 'click' , ( ) => {
7931- document . getElementById ( 'settings-modal' ) ?. classList . remove ( 'hidden' ) ;
7883+ document . getElementById ( 'btn-activity-settings' ) ?. addEventListener ( 'click' , async ( ) => {
7884+ // Load current settings into UI
7885+ elements . scrollSpeedSlider . value = userSettings . scrollSpeed . toString ( ) ;
7886+ elements . scrollSpeedValue . textContent = `${ userSettings . scrollSpeed } %` ;
7887+ elements . defaultFontSizeSlider . value = userSettings . defaultFontSize . toString ( ) ;
7888+ elements . defaultFontSizeValue . textContent = `${ userSettings . defaultFontSize } px` ;
7889+ elements . defaultGapThresholdSlider . value = userSettings . defaultGapThreshold . toString ( ) ;
7890+ elements . defaultGapThresholdValue . textContent = `${ userSettings . defaultGapThreshold } s` ;
7891+ elements . autoAnalyzeCheckbox . checked = userSettings . autoAnalyze ;
7892+ elements . themeSelect . value = userSettings . theme ;
7893+ populateSidebarSectionToggles ( ) ;
7894+ // Load Datadog config
7895+ const result = await window . api . datadogLoadConfig ( ) ;
7896+ if ( result . success && result . config ) {
7897+ elements . ddSiteSelect . value = result . config . site || 'US1' ;
7898+ elements . ddApiKey . value = result . config . hasApiKey ? '••••••••' : '' ;
7899+ elements . ddAppKey . value = result . config . hasAppKey ? '••••••••' : '' ;
7900+ elements . ddConfigStatus . textContent = 'Configured' ;
7901+ elements . ddConfigStatus . style . color = 'var(--debug-color)' ;
7902+ } else {
7903+ elements . ddSiteSelect . value = 'US1' ;
7904+ elements . ddApiKey . value = '' ;
7905+ elements . ddAppKey . value = '' ;
7906+ elements . ddConfigStatus . textContent = 'Not configured' ;
7907+ elements . ddConfigStatus . style . color = 'var(--text-muted)' ;
7908+ }
7909+ elements . settingsModal . classList . remove ( 'hidden' ) ;
79327910 } ) ;
79337911
79347912 // History panel controls
@@ -8500,11 +8478,6 @@ function init(): void {
85008478 // Notes drawer events
85018479 elements . btnNotesToggle . addEventListener ( 'click' , toggleNotesDrawer ) ;
85028480 elements . btnNotesClose . addEventListener ( 'click' , closeNotesDrawer ) ;
8503- elements . notesOverlay . addEventListener ( 'click' , ( e ) => {
8504- if ( e . target === elements . notesOverlay ) {
8505- closeNotesDrawer ( ) ;
8506- }
8507- } ) ;
85088481 elements . notesTextarea . addEventListener ( 'input' , saveNotesDebounced ) ;
85098482 setupNotesDrawerResize ( ) ;
85108483
@@ -8782,24 +8755,6 @@ function init(): void {
87828755 }
87838756 } ) ;
87848757
8785- // Datadog settings - load config when settings open
8786- const origSettingsClick = elements . btnSettings . onclick ;
8787- elements . btnSettings . addEventListener ( 'click' , async ( ) => {
8788- const result = await window . api . datadogLoadConfig ( ) ;
8789- if ( result . success && result . config ) {
8790- elements . ddSiteSelect . value = result . config . site || 'US1' ;
8791- elements . ddApiKey . value = result . config . hasApiKey ? '••••••••' : '' ;
8792- elements . ddAppKey . value = result . config . hasAppKey ? '••••••••' : '' ;
8793- elements . ddConfigStatus . textContent = 'Configured' ;
8794- elements . ddConfigStatus . style . color = 'var(--debug-color)' ;
8795- } else {
8796- elements . ddSiteSelect . value = 'US1' ;
8797- elements . ddApiKey . value = '' ;
8798- elements . ddAppKey . value = '' ;
8799- elements . ddConfigStatus . textContent = 'Not configured' ;
8800- elements . ddConfigStatus . style . color = 'var(--text-muted)' ;
8801- }
8802- } ) ;
88038758
88048759 elements . btnDdSaveConfig . addEventListener ( 'click' , async ( ) => {
88058760 const apiKey = elements . ddApiKey . value . trim ( ) ;
@@ -8840,9 +8795,6 @@ function init(): void {
88408795 } ) ;
88418796 elements . splitValue . addEventListener ( 'input' , updateSplitPreview ) ;
88428797
8843- // Sidebar
8844- elements . btnToggleSidebar . addEventListener ( 'click' , togglePanelVisibility ) ;
8845-
88468798 // Highlights
88478799 elements . btnAddHighlight . addEventListener ( 'click' , showHighlightModal ) ;
88488800 elements . btnSaveHighlight . addEventListener ( 'click' , saveHighlight ) ;
@@ -8868,20 +8820,6 @@ function init(): void {
88688820 elements . helpModal . classList . add ( 'hidden' ) ;
88698821 } ) ;
88708822
8871- // Settings modal
8872- elements . btnSettings . addEventListener ( 'click' , ( ) => {
8873- // Load current settings into UI
8874- elements . scrollSpeedSlider . value = userSettings . scrollSpeed . toString ( ) ;
8875- elements . scrollSpeedValue . textContent = `${ userSettings . scrollSpeed } %` ;
8876- elements . defaultFontSizeSlider . value = userSettings . defaultFontSize . toString ( ) ;
8877- elements . defaultFontSizeValue . textContent = `${ userSettings . defaultFontSize } px` ;
8878- elements . defaultGapThresholdSlider . value = userSettings . defaultGapThreshold . toString ( ) ;
8879- elements . defaultGapThresholdValue . textContent = `${ userSettings . defaultGapThreshold } s` ;
8880- elements . autoAnalyzeCheckbox . checked = userSettings . autoAnalyze ;
8881- elements . themeSelect . value = userSettings . theme ;
8882- populateSidebarSectionToggles ( ) ;
8883- elements . settingsModal . classList . remove ( 'hidden' ) ;
8884- } ) ;
88858823
88868824 elements . scrollSpeedSlider . addEventListener ( 'input' , ( ) => {
88878825 const value = parseInt ( elements . scrollSpeedSlider . value , 10 ) ;
0 commit comments