@@ -127,15 +127,66 @@ function hideIncompleteCodeBlockMarkers(text: string): string {
127127 * Returns the text with incomplete components removed and the buffered component text
128128 */
129129function hideIncompleteComponents ( text : string ) : { cleanText : string ; bufferedComponent : string } {
130- // Find the last occurrence of {{component:
131- const lastComponentStart = text . lastIndexOf ( '{{component:' ) ;
130+ // Find the last occurrence of {{ (any component start, not just {{component:)
131+ const lastComponentStart = text . lastIndexOf ( '{{' ) ;
132+
133+ // Check if the last {{ is complete
134+ let lastComponentIsComplete = false ;
135+
136+ if ( lastComponentStart !== - 1 ) {
137+ const textAfterComponentStart = text . substring ( lastComponentStart ) ;
138+
139+ // Quick check: does it have closing }}?
140+ let braceCount = 0 ;
141+ let inString = false ;
142+ let escapeNext = false ;
143+
144+ for ( let i = 0 ; i < textAfterComponentStart . length ; i ++ ) {
145+ const char = textAfterComponentStart [ i ] ;
146+ const nextChar = textAfterComponentStart [ i + 1 ] ;
147+
148+ if ( escapeNext ) {
149+ escapeNext = false ;
150+ continue ;
151+ }
152+
153+ if ( char === '\\' ) {
154+ escapeNext = true ;
155+ continue ;
156+ }
157+
158+ if ( char === '"' ) {
159+ inString = ! inString ;
160+ continue ;
161+ }
162+
163+ if ( inString ) continue ;
164+
165+ if ( char === '{' ) {
166+ braceCount ++ ;
167+ } else if ( char === '}' ) {
168+ braceCount -- ;
169+
170+ if ( braceCount === 1 && nextChar === '}' ) {
171+ lastComponentIsComplete = true ;
172+ break ;
173+ }
174+ }
175+ }
176+ }
177+
178+ // Hide single trailing { if no unclosed component
179+ if ( text . endsWith ( '{' ) && ! text . endsWith ( '{{' ) && ( lastComponentStart === - 1 || lastComponentIsComplete ) ) {
180+ console . log ( '🚫 Hiding single trailing {' ) ;
181+ return { cleanText : text . slice ( 0 , - 1 ) , bufferedComponent : '{' } ;
182+ }
132183
133184 if ( lastComponentStart === - 1 ) {
134185 // No component syntax found
135186 return { cleanText : text , bufferedComponent : '' } ;
136187 }
137188
138- // Check if there's a complete component (closing }}) after the last opening
189+ // Re-check completion (we already did this above, but keep for clarity)
139190 const textAfterComponentStart = text . substring ( lastComponentStart ) ;
140191
141192 // Count braces to find matching closing }}
@@ -181,14 +232,16 @@ function hideIncompleteComponents(text: string): { cleanText: string; bufferedCo
181232
182233 if ( foundClosing ) {
183234 // Component is complete, don't hide anything
235+ console . log ( '✅ Complete component found, not hiding' ) ;
184236 return { cleanText : text , bufferedComponent : '' } ;
185237 }
186238
187- // Component is incomplete, hide everything from {{component: onwards
239+ // Component is incomplete, hide everything from {{ onwards
188240 const cleanText = text . substring ( 0 , lastComponentStart ) ;
189241 const bufferedComponent = text . substring ( lastComponentStart ) ;
190242
191243 console . log ( '🔒 Buffering incomplete component:' , {
244+ cleanText : cleanText . substring ( Math . max ( 0 , cleanText . length - 50 ) ) ,
192245 bufferLength : bufferedComponent . length ,
193246 bufferPreview : bufferedComponent . substring ( 0 , 100 )
194247 } ) ;
@@ -660,6 +713,8 @@ export function updateIncompleteTagState(
660713 // Start with current state
661714 let stack = [ ...state . stack ] ;
662715 let tagCounts = { ...state . tagCounts } ;
716+ let inCodeBlock = state . inCodeBlock ;
717+ let inInlineCode = state . inInlineCode ;
663718
664719 // Process each new character
665720 for ( let i = 0 ; i < newChars . length ; i ++ ) {
@@ -681,6 +736,7 @@ export function updateIncompleteTagState(
681736 // Close the most recent code block
682737 stack . splice ( codeBlockIndex , 1 ) ;
683738 tagCounts . codeBlock -- ;
739+ inCodeBlock = false ; // Exit code block context
684740 } else {
685741 // Open new code block
686742 stack . push ( {
@@ -690,6 +746,7 @@ export function updateIncompleteTagState(
690746 openingText : newText . slice ( Math . max ( 0 , position - 10 ) , position + 13 ) ,
691747 } ) ;
692748 tagCounts . codeBlock ++ ;
749+ inCodeBlock = true ; // Enter code block context
693750 }
694751 i += 2 ; // Skip next 2 characters
695752 continue ;
@@ -764,6 +821,7 @@ export function updateIncompleteTagState(
764821 // Close the most recent code
765822 stack . splice ( codeIndex , 1 ) ;
766823 tagCounts . code -- ;
824+ inInlineCode = false ; // Exit inline code context
767825 } else {
768826 // Open new code
769827 stack . push ( {
@@ -773,6 +831,7 @@ export function updateIncompleteTagState(
773831 openingText : newText . slice ( Math . max ( 0 , position - 10 ) , position + 11 ) ,
774832 } ) ;
775833 tagCounts . code ++ ;
834+ inInlineCode = true ; // Enter inline code context
776835 }
777836 continue ;
778837 }
@@ -812,8 +871,8 @@ export function updateIncompleteTagState(
812871 continue ;
813872 }
814873
815- // Links: [
816- if ( char === '[' ) {
874+ // Links: [ (but only if NOT in code context)
875+ if ( char === '[' && ! inCodeBlock && ! inInlineCode ) {
817876 // Open new link
818877 stack . push ( {
819878 type : 'link' ,
@@ -843,22 +902,18 @@ export function updateIncompleteTagState(
843902 continue ;
844903 }
845904
846- // Components: {{
847- if ( char === '{' && nextChar === '{' ) {
848- // Check if next chars are 'component:'
849- const ahead = newText . slice ( position , position + 12 ) ;
850- if ( ahead . startsWith ( '{{component:' ) ) {
851- // Open new component
852- stack . push ( {
853- type : 'component' ,
854- position,
855- marker : '{{' ,
856- openingText : newText . slice ( Math . max ( 0 , position - 10 ) , position + 22 ) ,
857- } ) ;
858- tagCounts . component ++ ;
859- i += 1 ; // Skip next character
860- continue ;
861- }
905+ // Components: {{ (but only if NOT in code context)
906+ if ( char === '{' && nextChar === '{' && ! inCodeBlock && ! inInlineCode ) {
907+ // Track ANY {{ as potential component when not in code
908+ stack . push ( {
909+ type : 'component' ,
910+ position,
911+ marker : '{{' ,
912+ openingText : newText . slice ( Math . max ( 0 , position - 10 ) , position + 22 ) ,
913+ } ) ;
914+ tagCounts . component ++ ;
915+ i += 1 ; // Skip next character
916+ continue ;
862917 }
863918
864919 // Components: closing }}
@@ -885,6 +940,8 @@ export function updateIncompleteTagState(
885940 earliestPosition,
886941 previousTextLength : newText . length ,
887942 tagCounts,
943+ inCodeBlock,
944+ inInlineCode,
888945 } ;
889946}
890947
0 commit comments