@@ -3680,8 +3680,8 @@ document.addEventListener("DOMContentLoaded", function () {
36803680 return ;
36813681 }
36823682 const text = markdownEditor . value || '' ;
3683- const scrollTop = markdownEditor . scrollTop ;
3684- const scrollLeft = markdownEditor . scrollLeft ;
3683+ const scrollTop = cachedScrollTop ;
3684+ const scrollLeft = cachedScrollLeft ;
36853685 const fragment = document . createDocumentFragment ( ) ;
36863686 let lastIndex = 0 ;
36873687 findMatches . forEach ( function ( match , index ) {
@@ -3701,8 +3701,8 @@ document.addEventListener("DOMContentLoaded", function () {
37013701
37023702 function syncHighlightScroll ( ) {
37033703 if ( ! editorHighlightLayer ) return ;
3704- editorHighlightLayer . scrollTop = markdownEditor . scrollTop ;
3705- editorHighlightLayer . scrollLeft = markdownEditor . scrollLeft ;
3704+ editorHighlightLayer . scrollTop = cachedScrollTop ;
3705+ editorHighlightLayer . scrollLeft = cachedScrollLeft ;
37063706 }
37073707
37083708 function updateLineNumberGutter ( lineCount ) {
@@ -3765,42 +3765,71 @@ document.addEventListener("DOMContentLoaded", function () {
37653765 }
37663766
37673767 const lineCache = new Map ( ) ;
3768- let lastEditorWidth = 0 ;
3769- let charWidth = 0 ;
3770- let maxCharsPerLine = 0 ;
3768+ let cachedPaddingLeft = 10 ;
3769+ let cachedPaddingRight = 10 ;
3770+ let cachedCharWidth = 0 ;
3771+ let cachedLineHeight = 21 ;
3772+ let cachedEditorWidth = 0 ;
3773+ let cachedMaxCharsPerLine = 80 ;
3774+ let cachedScrollTop = 0 ;
3775+ let cachedScrollLeft = 0 ;
3776+ let isGeometryInitialized = false ;
3777+
3778+ function initEditorGeometry ( ) {
3779+ if ( ! markdownEditor ) return ;
3780+ const styles = window . getComputedStyle ( markdownEditor ) ;
3781+ cachedPaddingLeft = parseFloat ( styles . paddingLeft ) || 10 ;
3782+ cachedPaddingRight = parseFloat ( styles . paddingRight ) || 10 ;
3783+
3784+ // Measure character width
3785+ const testSpan = document . createElement ( 'span' ) ;
3786+ testSpan . style . fontFamily = styles . fontFamily ;
3787+ testSpan . style . fontSize = styles . fontSize ;
3788+ testSpan . style . visibility = 'hidden' ;
3789+ testSpan . style . position = 'absolute' ;
3790+ testSpan . style . whiteSpace = 'pre' ;
3791+ testSpan . textContent = 'a' . repeat ( 100 ) ;
3792+ document . body . appendChild ( testSpan ) ;
3793+ cachedCharWidth = testSpan . getBoundingClientRect ( ) . width / 100 ;
3794+ document . body . removeChild ( testSpan ) ;
3795+
3796+ // Calculate line height
3797+ const computed = parseFloat ( styles . lineHeight ) ;
3798+ if ( ! Number . isNaN ( computed ) ) {
3799+ cachedLineHeight = computed ;
3800+ } else {
3801+ const fontSize = parseFloat ( styles . fontSize ) || 14 ;
3802+ cachedLineHeight = fontSize * 1.5 ;
3803+ }
3804+
3805+ isGeometryInitialized = true ;
3806+ lineCache . clear ( ) ;
3807+ }
3808+
3809+ function refreshEditorWidth ( ) {
3810+ if ( ! markdownEditor ) return ;
3811+ if ( ! isGeometryInitialized ) {
3812+ initEditorGeometry ( ) ;
3813+ }
3814+ cachedEditorWidth = markdownEditor . clientWidth ;
3815+ const availableWidth = cachedEditorWidth - cachedPaddingLeft - cachedPaddingRight ;
3816+ cachedMaxCharsPerLine = Math . max ( 1 , Math . floor ( availableWidth / cachedCharWidth ) ) ;
3817+
3818+ cachedScrollTop = markdownEditor . scrollTop ;
3819+ cachedScrollLeft = markdownEditor . scrollLeft ;
3820+ }
37713821
37723822 function updateLineNumbers ( ) {
37733823 if ( ! lineNumbers || ! markdownEditor ) return ;
37743824 const lines = ( markdownEditor . value || '' ) . split ( '\n' ) ;
37753825 const lineCount = Math . max ( 1 , lines . length ) ;
37763826
3777- const currentWidth = markdownEditor . clientWidth ;
3778- const styles = window . getComputedStyle ( markdownEditor ) ;
3779- const paddingLeft = parseFloat ( styles . paddingLeft ) || 10 ;
3780- const paddingRight = parseFloat ( styles . paddingRight ) || 10 ;
3781- const availableWidth = currentWidth - paddingLeft - paddingRight ;
3782-
3783- // Measure character width exactly once per resize / layout width change
3784- if ( currentWidth !== lastEditorWidth || charWidth === 0 ) {
3785- lineCache . clear ( ) ;
3786- lastEditorWidth = currentWidth ;
3787-
3788- const testSpan = document . createElement ( 'span' ) ;
3789- testSpan . style . fontFamily = styles . fontFamily ;
3790- testSpan . style . fontSize = styles . fontSize ;
3791- testSpan . style . visibility = 'hidden' ;
3792- testSpan . style . position = 'absolute' ;
3793- testSpan . style . whiteSpace = 'pre' ;
3794- testSpan . textContent = 'a' . repeat ( 100 ) ;
3795- document . body . appendChild ( testSpan ) ;
3796- charWidth = testSpan . getBoundingClientRect ( ) . width / 100 ;
3797- document . body . removeChild ( testSpan ) ;
3798-
3799- maxCharsPerLine = Math . max ( 1 , Math . floor ( availableWidth / charWidth ) ) ;
3827+ if ( cachedEditorWidth === 0 ) {
3828+ refreshEditorWidth ( ) ;
38003829 }
38013830
38023831 updateLineNumberGutter ( lineCount ) ;
3803- const lineHeight = getLineHeight ( styles ) ;
3832+ const lineHeight = cachedLineHeight ;
38043833
38053834 const existingItems = lineNumbers . children ;
38063835 const existingCount = existingItems . length ;
@@ -3825,7 +3854,7 @@ document.addEventListener("DOMContentLoaded", function () {
38253854 const lineText = lines [ i ] ;
38263855 let wrapHeight = lineCache . get ( lineText ) ;
38273856 if ( wrapHeight === undefined ) {
3828- const wrapCount = getWrappedLineCountMonospace ( lineText , maxCharsPerLine ) ;
3857+ const wrapCount = getWrappedLineCountMonospace ( lineText , cachedMaxCharsPerLine ) ;
38293858 wrapHeight = wrapCount * lineHeight ;
38303859 lineCache . set ( lineText , wrapHeight ) ;
38313860 }
@@ -3855,7 +3884,7 @@ document.addEventListener("DOMContentLoaded", function () {
38553884
38563885 function syncLineNumberScroll ( ) {
38573886 if ( ! lineNumbers ) return ;
3858- lineNumbers . scrollTop = markdownEditor . scrollTop ;
3887+ lineNumbers . scrollTop = cachedScrollTop ;
38593888 }
38603889
38613890 // Class encapsulating Search & Replace Engine
@@ -4344,6 +4373,7 @@ document.addEventListener("DOMContentLoaded", function () {
43444373 const targetScrollTop = Math . max ( 0 , ( lineIndex * lineHeight ) - ( editorHeight / 2 ) + ( lineHeight / 2 ) ) ;
43454374
43464375 markdownEditor . scrollTop = targetScrollTop ;
4376+ cachedScrollTop = targetScrollTop ;
43474377 syncHighlightScroll ( ) ;
43484378 syncLineNumberScroll ( ) ;
43494379 } catch ( e ) {
@@ -4938,12 +4968,14 @@ document.addEventListener("DOMContentLoaded", function () {
49384968 const previewPercent = 100 - editorWidthPercent ;
49394969 editorPaneElement . style . flex = `0 0 calc((100% - var(--dock-width, 0px)) * ${ editorWidthPercent / 100 } - 4px)` ;
49404970 previewPaneElement . style . flex = `0 0 calc((100% - var(--dock-width, 0px)) * ${ previewPercent / 100 } - 4px)` ;
4971+ refreshEditorWidth ( ) ;
49414972 scheduleLineNumberUpdate ( ) ;
49424973 }
49434974
49444975 function resetPaneWidths ( ) {
49454976 editorPaneElement . style . flex = '' ;
49464977 previewPaneElement . style . flex = '' ;
4978+ refreshEditorWidth ( ) ;
49474979 }
49484980
49494981 function openMobileMenu ( ) {
@@ -5035,6 +5067,8 @@ document.addEventListener("DOMContentLoaded", function () {
50355067
50365068 initTabs ( ) ;
50375069 if ( loadGlobalState ( ) . syncScrollingEnabled === false ) toggleSyncScrolling ( ) ;
5070+ initEditorGeometry ( ) ;
5071+ refreshEditorWidth ( ) ;
50385072 updateMobileStats ( ) ;
50395073 updateFindHighlights ( ) ;
50405074 syncHighlightScroll ( ) ;
@@ -5066,6 +5100,8 @@ document.addEventListener("DOMContentLoaded", function () {
50665100 }
50675101
50685102 window . addEventListener ( 'resize' , ( ) => {
5103+ initEditorGeometry ( ) ;
5104+ refreshEditorWidth ( ) ;
50695105 scheduleLineNumberUpdate ( ) ;
50705106 if ( window . innerWidth < 1080 && isFrDocked && isFindModalOpen ) {
50715107 toggleFrDockMode ( true ) ;
@@ -5136,6 +5172,8 @@ document.addEventListener("DOMContentLoaded", function () {
51365172 } ) ;
51375173
51385174 editorPane . addEventListener ( "scroll" , function ( ) {
5175+ cachedScrollTop = this . scrollTop ;
5176+ cachedScrollLeft = this . scrollLeft ;
51395177 syncEditorToPreview ( ) ;
51405178 syncHighlightScroll ( ) ;
51415179 syncLineNumberScroll ( ) ;
0 commit comments