@@ -3951,32 +3951,52 @@ This is a fully client-side application. Your content never leaves your browser
39513951 } ) ;
39523952 }
39533953
3954+ let frPreferredDocked = false ;
3955+
39543956 function toggleFrDockMode ( ) {
39553957 const panel = document . getElementById ( 'find-replace-modal' ) ;
39563958 const dockBtn = document . getElementById ( 'find-replace-dock' ) ;
3957- const editorWrapper = document . querySelector ( '.editor-dock-wrapper' ) || document . querySelector ( '.content-container' ) ;
3958- if ( ! panel || ! dockBtn || ! editorWrapper ) return ;
3959+ const contentCont = document . querySelector ( '.content-container' ) ;
3960+ if ( ! panel || ! dockBtn || ! contentCont ) return ;
39593961
39603962 isFrDocked = ! isFrDocked ;
3963+
3964+ // Save preference to localStorage
3965+ frPreferredDocked = isFrDocked ;
3966+ localStorage . setItem ( 'find-replace-docked' , frPreferredDocked ? 'true' : 'false' ) ;
3967+
39613968 if ( isFrDocked ) {
39623969 panel . classList . add ( 'docked' ) ;
39633970 panel . style . left = 'auto' ;
39643971 panel . style . top = 'auto' ;
39653972 panel . style . right = 'auto' ;
3966- // Append panel to dock container
3967- editorWrapper . appendChild ( panel ) ;
3973+
3974+ // Append panel to dock container (.content-container)
3975+ contentCont . appendChild ( panel ) ;
3976+ contentCont . classList . add ( 'fr-docked' ) ;
3977+ contentCont . style . setProperty ( '--dock-width' , '340px' ) ;
3978+
39683979 dockBtn . innerHTML = '<i class="bi bi-window"></i>' ;
39693980 dockBtn . title = "Toggle Floating Mode" ;
39703981 } else {
39713982 panel . classList . remove ( 'docked' ) ;
3983+
39723984 // Reset position and float on body
39733985 document . body . appendChild ( panel ) ;
3974- panel . style . top = '100px' ;
3975- panel . style . right = '20px' ;
3976- panel . style . left = 'auto' ;
3986+ contentCont . classList . remove ( 'fr-docked' ) ;
3987+ contentCont . style . setProperty ( '--dock-width' , '0px' ) ;
3988+
3989+ panel . style . top = '' ;
3990+ panel . style . left = '' ;
3991+ panel . style . right = '' ;
3992+
39773993 dockBtn . innerHTML = '<i class="bi bi-layout-sidebar-reverse"></i>' ;
39783994 dockBtn . title = "Toggle Dock Mode" ;
39793995 }
3996+
3997+ // Ensure display is flex and recalculate split panes
3998+ panel . style . display = 'flex' ;
3999+ applyPaneWidths ( ) ;
39804000 }
39814001
39824002 function updateFindControls ( ) {
@@ -4059,16 +4079,21 @@ This is a fully client-side application. Your content never leaves your browser
40594079 markdownEditor . focus ( ) ;
40604080 markdownEditor . setSelectionRange ( match . start , match . end ) ;
40614081
4062- // Auto-scroll logic if editor cursor is near find-replace panel
4063- requestAnimationFrame ( ( ) => {
4064- const panel = document . getElementById ( 'find-replace-modal' ) ;
4065- if ( ! isFrDocked && panel && panel . style . display !== 'none' ) {
4066- const panelRect = panel . getBoundingClientRect ( ) ;
4067- // Check if selection coords intersect
4068- // Simplification: if panel is right-aligned, push editor view left if needed,
4069- // or just let native setSelectionRange scroll to cursor naturally.
4070- }
4071- } ) ;
4082+ // Explicitly scroll editor to center active match in viewport
4083+ try {
4084+ const styles = window . getComputedStyle ( markdownEditor ) ;
4085+ const lineHeight = parseFloat ( styles . lineHeight ) || 21 ;
4086+ const textBefore = markdownEditor . value . slice ( 0 , match . start ) ;
4087+ const lineIndex = textBefore . split ( '\n' ) . length - 1 ;
4088+ const editorHeight = markdownEditor . clientHeight ;
4089+ const targetScrollTop = Math . max ( 0 , ( lineIndex * lineHeight ) - ( editorHeight / 2 ) + ( lineHeight / 2 ) ) ;
4090+
4091+ markdownEditor . scrollTop = targetScrollTop ;
4092+ syncHighlightScroll ( ) ;
4093+ syncLineNumberScroll ( ) ;
4094+ } catch ( e ) {
4095+ console . warn ( "Viewport centering scroll failed:" , e ) ;
4096+ }
40724097 }
40734098
40744099 function cycleFindMatch ( direction ) {
@@ -4093,6 +4118,17 @@ This is a fully client-side application. Your content never leaves your browser
40934118 findReplaceInput . value = selected ;
40944119 }
40954120
4121+ // Restore docked/floating mode preference
4122+ const wasDockedPref = localStorage . getItem ( 'find-replace-docked' ) === 'true' ;
4123+
4124+ if ( wasDockedPref ) {
4125+ isFrDocked = false ; // Set false so toggleFrDockMode() turns it to true
4126+ toggleFrDockMode ( ) ;
4127+ } else {
4128+ isFrDocked = true ; // Set true so toggleFrDockMode() turns it to false
4129+ toggleFrDockMode ( ) ;
4130+ }
4131+
40964132 findReplaceModal . style . display = 'flex' ;
40974133
40984134 requestAnimationFrame ( function ( ) {
@@ -4109,11 +4145,16 @@ This is a fully client-side application. Your content never leaves your browser
41094145 function closeFindReplaceModal ( ) {
41104146 isFindModalOpen = false ;
41114147 const panel = document . getElementById ( 'find-replace-modal' ) ;
4148+ const contentCont = document . querySelector ( '.content-container' ) ;
41124149 if ( panel ) {
41134150 panel . style . display = 'none' ;
41144151 if ( isFrDocked ) {
4115- // Undock and return to body
4116- toggleFrDockMode ( ) ;
4152+ // Reset split layout styles when closed
4153+ if ( contentCont ) {
4154+ contentCont . classList . remove ( 'fr-docked' ) ;
4155+ contentCont . style . setProperty ( '--dock-width' , '0px' ) ;
4156+ applyPaneWidths ( ) ;
4157+ }
41174158 }
41184159 }
41194160 findMatches = [ ] ;
@@ -4587,8 +4628,8 @@ This is a fully client-side application. Your content never leaves your browser
45874628 if ( currentViewMode !== 'split' ) return ;
45884629
45894630 const previewPercent = 100 - editorWidthPercent ;
4590- editorPaneElement . style . flex = `0 0 calc(${ editorWidthPercent } % - 4px)` ;
4591- previewPaneElement . style . flex = `0 0 calc(${ previewPercent } % - 4px)` ;
4631+ editorPaneElement . style . flex = `0 0 calc((100% - var(--dock-width, 0px)) * ${ editorWidthPercent / 100 } - 4px)` ;
4632+ previewPaneElement . style . flex = `0 0 calc((100% - var(--dock-width, 0px)) * ${ previewPercent / 100 } - 4px)` ;
45924633 scheduleLineNumberUpdate ( ) ;
45934634 }
45944635
@@ -4731,11 +4772,6 @@ This is a fully client-side application. Your content never leaves your browser
47314772
47324773 // Editor key handlers for list continuation and indentation
47334774 markdownEditor . addEventListener ( "keydown" , function ( e ) {
4734- if ( ( e . ctrlKey || e . metaKey ) && e . key . toLowerCase ( ) === 'f' ) {
4735- e . preventDefault ( ) ;
4736- openFindReplaceModal ( ) ;
4737- return ;
4738- }
47394775 if ( handleListEnter ( e ) ) {
47404776 return ;
47414777 }
@@ -5954,7 +5990,40 @@ This is a fully client-side application. Your content never leaves your browser
59545990 if ( e . target === shareModal ) closeShareModal ( ) ;
59555991 } ) ;
59565992 document . addEventListener ( 'keydown' , function ( e ) {
5957- if ( e . key === 'Escape' && shareModal . classList . contains ( 'is-visible' ) ) closeShareModal ( ) ;
5993+ if ( e . key === 'Escape' && shareModal . classList . contains ( 'is-visible' ) ) {
5994+ closeShareModal ( ) ;
5995+ }
5996+
5997+ // Global Ctrl+F / Cmd+F interception
5998+ if ( ( e . ctrlKey || e . metaKey ) && e . key . toLowerCase ( ) === 'f' ) {
5999+ e . preventDefault ( ) ;
6000+ openFindReplaceModal ( ) ;
6001+ const findInput = document . getElementById ( 'find-replace-input' ) ;
6002+ if ( findInput ) {
6003+ findInput . focus ( ) ;
6004+ findInput . select ( ) ;
6005+ }
6006+ return ;
6007+ }
6008+
6009+ // Global Ctrl+H / Cmd+H interception
6010+ if ( ( e . ctrlKey || e . metaKey ) && e . key . toLowerCase ( ) === 'h' ) {
6011+ e . preventDefault ( ) ;
6012+ openFindReplaceModal ( ) ;
6013+ const replaceInput = document . getElementById ( 'find-replace-with' ) ;
6014+ if ( replaceInput ) {
6015+ replaceInput . focus ( ) ;
6016+ replaceInput . select ( ) ;
6017+ }
6018+ return ;
6019+ }
6020+
6021+ // Global Escape dismissal for find-replace panel
6022+ if ( e . key === 'Escape' && isFindModalOpen ) {
6023+ e . preventDefault ( ) ;
6024+ closeFindReplaceModal ( ) ;
6025+ return ;
6026+ }
59586027 } ) ;
59596028
59606029 shareButton . addEventListener ( 'click' , openShareModal ) ;
0 commit comments