11const VIDEO_ASPECT_RATIO = 56.25 ; // 16:9 aspect ratio
22
3+ // Create a wrapper for all our custom content
4+ function createCustomContentWrapper ( ) {
5+ const wrapper = createStyledElement ( 'div' , {
6+ width : '100%' ,
7+ maxWidth : '800px' ,
8+ margin : '0 auto 32px auto' ,
9+ position : 'relative' ,
10+ zIndex : '1'
11+ } ) ;
12+ wrapper . classList . add ( 'leetcode-explained-wrapper' ) ;
13+ return wrapper ;
14+ }
15+ 316// Utility function to create a styled button
417function createStyledButton ( text : string , isActive : boolean = false ) : HTMLButtonElement {
518 const button = document . createElement ( 'button' ) ;
619 button . textContent = text ;
20+ button . classList . add ( 'nav-button' ) ;
21+ if ( isActive ) button . classList . add ( 'active' ) ;
722
823 chrome . storage . local . get ( [ 'isDarkTheme' ] , ( result ) => {
924 const isDark = result . isDarkTheme ;
1025 button . style . backgroundColor = isDark ? '#373737' : '#f3f4f5' ;
1126 button . style . color = isDark ? '#fff' : '#1a1a1a' ;
1227 button . style . border = `1px solid ${ isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)' } ` ;
1328
14- 15- // on hover just make the background a few shades darker or lighter
1629 button . addEventListener ( 'mouseenter' , ( ) => {
17- button . style . backgroundColor = isDark ? '#424242' : '#e6e6e6' ;
30+ if ( ! button . classList . contains ( 'active' ) ) {
31+ button . style . backgroundColor = isDark ? '#424242' : '#e6e6e6' ;
32+ }
1833 } ) ;
1934 button . addEventListener ( 'mouseleave' , ( ) => {
20- button . style . backgroundColor = isDark ? '#373737' : '#f3f4f5' ;
35+ if ( ! button . classList . contains ( 'active' ) ) {
36+ button . style . backgroundColor = isDark ? '#373737' : '#f3f4f5' ;
37+ }
2138 } ) ;
2239 } ) ;
2340
@@ -28,6 +45,7 @@ function createStyledButton(text: string, isActive: boolean = false): HTMLButton
2845 button . style . fontSize = '11px' ;
2946 button . style . transition = 'all 0.2s ease' ;
3047 button . style . letterSpacing = '0.5px' ;
48+ button . style . cursor = 'pointer' ;
3149
3250 return button ;
3351}
@@ -47,7 +65,7 @@ function createVideoContainer(problem: any) {
4765 maxWidth : '800px' ,
4866 margin : '0 auto' ,
4967 } ) ;
50- container . classList . add ( 'video-container' ) ;
68+ container . classList . add ( 'video-container' , 'content-section' ) ;
5169
5270 const controlsContainer = createStyledElement ( 'div' , {
5371 display : 'flex' ,
@@ -101,7 +119,6 @@ function createVideoContainer(problem: any) {
101119
102120 chrome . storage . local . get ( [ 'isDarkTheme' ] , ( result ) => {
103121 const isDark = result . isDarkTheme ;
104- // channel element is white on dark mode and black on light mode
105122 channelElement . style . color = isDark ? '#fff' : '#1a1a1a' ;
106123 } ) ;
107124
@@ -153,17 +170,26 @@ function updateVideo(iframe: HTMLIFrameElement, videoUrl: string) {
153170}
154171
155172function createCodeContainer ( ) {
173+ const container = createStyledElement ( 'div' , {
174+ display : 'none' ,
175+ width : '100%' ,
176+ maxWidth : '800px' ,
177+ margin : '0 auto' ,
178+ position : 'relative'
179+ } ) ;
180+ container . classList . add ( 'code-section' , 'content-section' ) ;
181+ 156182 const codeElement = document . createElement ( 'pre' ) ;
157183 codeElement . classList . add ( 'code-container' ) ;
158- codeElement . style . display = 'none ' ;
184+ codeElement . style . display = 'block ' ;
159185 codeElement . style . borderRadius = '8px' ;
160186 codeElement . style . padding = '16px' ;
161187 codeElement . style . marginTop = '24px' ;
162- codeElement . style . width = '95 %' ;
188+ codeElement . style . width = '100 %' ;
163189 codeElement . style . fontSize = '14px' ;
164- codeElement . style . marginLeft = '2.5%' ;
165190 codeElement . style . maxHeight = '500px' ;
166191 codeElement . style . overflowY = 'auto' ;
192+ codeElement . style . boxSizing = 'border-box' ;
167193 codeElement . style . boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)' ;
168194
169195 chrome . storage . local . get ( [ 'isDarkTheme' ] , ( result ) => {
@@ -173,24 +199,52 @@ function createCodeContainer() {
173199 codeElement . style . color = isDark ? '#fff' : '#1a1a1a' ;
174200 } ) ;
175201
176- return codeElement ;
202+ container . appendChild ( codeElement ) ;
203+ return container ;
177204}
178205
179- function hideContent ( ) {
180- const elements = [
181- '.code-container' ,
182- '.language-buttons-container' ,
183- '.video-container'
184- ] . map ( selector => document . querySelector ( selector ) as HTMLElement ) ;
206+ function showContent ( type : 'Discussion' | 'Video' | 'Code' ) {
207+ // Hide all content sections first
208+ const contentSections = document . querySelectorAll ( '.content-section' ) ;
209+ contentSections . forEach ( section => {
210+ ( section as HTMLElement ) . style . display = 'none' ;
211+ } ) ;
185212
186- elements . forEach ( element => {
187- if ( element ) {
188- if ( element . classList . contains ( 'video-container' ) ) {
189- element . style . paddingBottom = '0' ;
213+ // Show the selected content
214+ switch ( type ) {
215+ case 'Video' :
216+ const videoContainer = document . querySelector ( '.video-container' ) as HTMLElement ;
217+ if ( videoContainer ) {
218+ videoContainer . style . display = 'flex' ;
219+ videoContainer . style . paddingBottom = `${ VIDEO_ASPECT_RATIO } %` ;
190220 }
191- element . style . display = 'none' ;
221+ break ;
222+ case 'Code' :
223+ const codeSection = document . querySelector ( '.code-section' ) as HTMLElement ;
224+ const languageButtons = document . querySelector ( '.language-buttons-container' ) as HTMLElement ;
225+ if ( codeSection ) codeSection . style . display = 'block' ;
226+ if ( languageButtons ) languageButtons . style . display = 'flex' ;
227+ break ;
228+ case 'Discussion' :
229+ // No need to do anything as the discussion is the default content
230+ break ;
231+ }
232+ 233+ // Update button states
234+ const buttons = document . querySelectorAll ( '.nav-button' ) ;
235+ buttons . forEach ( button => {
236+ if ( button . textContent === type ) {
237+ button . classList . add ( 'active' ) ;
238+ } else {
239+ button . classList . remove ( 'active' ) ;
192240 }
193241 } ) ;
242+ 243+ // Show/hide the discussion section
244+ const discussionSection = document . querySelector ( '.discuss-markdown' ) as HTMLElement ;
245+ if ( discussionSection ) {
246+ discussionSection . style . display = type === 'Discussion' ? 'block' : 'none' ;
247+ }
194248}
195249
196250function createNavContainer ( problem : any ) {
@@ -212,24 +266,12 @@ function createNavContainer(problem: any) {
212266 { text : 'Code' , show : problem . languages ?. length > 0 }
213267 ] ;
214268
215- const activeButton = buttons [ 0 ] ;
216- buttons . forEach ( ( { text, show } ) => {
269+ buttons . forEach ( ( { text, show } , index ) => {
217270 if ( ! show ) return ;
218271
219- const button = createStyledButton ( text , text === activeButton . text ) ;
272+ const button = createStyledButton ( text , index === 0 ) ;
220273 button . addEventListener ( 'click' , ( ) => {
221- hideContent ( ) ;
222- if ( text === 'Video' ) {
223- const videoContainer = document . querySelector ( '.video-container' ) as HTMLElement ;
224- if ( videoContainer ) {
225- videoContainer . style . display = 'flex' ;
226- videoContainer . style . paddingBottom = `${ VIDEO_ASPECT_RATIO } %` ;
227- }
228- } else if ( text === 'Code' ) {
229- const elements = [ '.code-container' , '.language-buttons-container' ]
230- . map ( selector => document . querySelector ( selector ) as HTMLElement ) ;
231- elements . forEach ( el => el && ( el . style . display = 'flex' ) ) ;
232- }
274+ showContent ( text as 'Discussion' | 'Video' | 'Code' ) ;
233275 } ) ;
234276 navContainer . append ( button ) ;
235277 } ) ;
@@ -530,55 +572,53 @@ function updateAllElements(isDark: boolean) {
530572}
531573
532574chrome . runtime . onMessage . addListener ( ( request ) => {
533- // get discussion tab so we can insert the content before it
534575 if ( request . action === 'updateSolutions' ) {
535576 chrome . storage . local . get ( [ 'leetcodeProblems' ] , ( result ) => {
536- const searchBar = document . querySelectorAll ( 'input.block' ) [ 0 ] . parentElement ?. parentElement ?. parentElement ;
577+ const searchBar = document . querySelectorAll ( 'input.block' ) [ 0 ] ?. parentElement ?. parentElement ?. parentElement ;
578+ if ( ! searchBar ) return ;
579+ 537580 const title = request . title . split ( '-' ) [ 0 ] . trim ( ) ;
538581 const problem = result . leetcodeProblems . questions . find ( ( problem : { title : string } ) => problem . title === title ) ;
539582
540- // If no solution code or videos exist, dont do anything.
583+ // If no solution code or videos exist, don't do anything
541584 if ( ! problem ?. videos && ! problem ?. languages ) return ;
542- if ( problem . videos ?. length == 0 && problem . languages ?. length == 0 ) {
543- return ;
544- }
585+ if ( problem . videos ?. length === 0 && problem . languages ?. length === 0 ) return ;
545586
546- // Always remove existing containers when updating solutions
547- const existingContainers = [
548- '.nav-container' ,
549- '.video-container' ,
550- '.code-container' ,
551- '.language-buttons-container'
552- ] . forEach ( selector => {
553- const element = document . querySelector ( selector ) ;
554- if ( element ) element . remove ( ) ;
555- } ) ;
587+ // Remove any existing containers
588+ const existingWrapper = document . querySelector ( '.leetcode-explained-wrapper' ) ;
589+ if ( existingWrapper ) existingWrapper . remove ( ) ;
556590
557- // Create new nav container
558- const newNavContainer = createNavContainer ( problem ) ;
559- searchBar ?. insertBefore ( newNavContainer , searchBar . firstChild ) ;
591+ // Create wrapper for all our custom content
592+ const wrapper = createCustomContentWrapper ( ) ;
593+ 594+ // Create and add nav container
595+ const navContainer = createNavContainer ( problem ) ;
596+ wrapper . appendChild ( navContainer ) ;
560597
561598 // Add video container if videos exist
562599 if ( problem . videos ?. length > 0 ) {
563- let videoContainer = createVideoContainer ( problem ) ;
564- if ( searchBar ) searchBar . insertBefore ( videoContainer , searchBar . children [ 1 ] ) ;
565- }
566- 567- // Add code container if languages exist
568- if ( problem . languages ?. length > 0 ) {
569- let codeContainer = createCodeContainer ( ) ;
570- if ( searchBar ) searchBar . insertBefore ( codeContainer , searchBar . children [ 1 ] ) ;
600+ const videoContainer = createVideoContainer ( problem ) ;
601+ wrapper . appendChild ( videoContainer ) ;
571602 }
572603
573- // Add language buttons container if languages exist
604+ // Add code container and language buttons if languages exist
574605 if ( problem . languages ?. length > 0 ) {
575- let languageButtonsContainer = createLanguageButtons ( problem ) ;
606+ const codeContainer = createCodeContainer ( ) ;
607+ const languageButtonsContainer = createLanguageButtons ( problem ) ;
576608 languageButtonsContainer . classList . add ( 'language-buttons-container' ) ;
577609 languageButtonsContainer . style . display = 'none' ;
578- if ( searchBar ) searchBar . insertBefore ( languageButtonsContainer , searchBar . children [ 1 ] ) ;
610+ 611+ wrapper . appendChild ( languageButtonsContainer ) ;
612+ wrapper . appendChild ( codeContainer ) ;
579613 }
580614
581- // Add theme change listener after creating containers
615+ // Insert the wrapper at the top of the solutions tab
616+ searchBar . insertBefore ( wrapper , searchBar . firstChild ) ;
617+ 618+ // Show discussion by default
619+ showContent ( 'Discussion' ) ;
620+ 621+ // Set up theme change listener
582622 setupThemeChangeListener ( ) ;
583623 } ) ;
584624 }
0 commit comments