@@ -87,7 +87,7 @@ function parseMarkdown(markdown) {
8787 // 3. Parse Tables
8888 const tableRegex = / ^ \s * \| ( .+ ) \| \s * $ \n ^ \s * \| (?: \s * [ - : ] + [ - \s | : ] * ) + \| \s * $ \n ( (?: ^ \s * \| .+ \| \s * $ \n ? ) + ) / gm;
8989 processedMarkdown = processedMarkdown . replace ( tableRegex , ( match ) => {
90- try { // Keep existing table parsing logic
90+ try { // Keep existing table parsing logic
9191 const lines = match . trim ( ) . split ( '\n' ) ;
9292 if ( lines . length < 3 ) return match ;
9393 const headerLine = lines [ 0 ] . trim ( ) ;
@@ -153,53 +153,169 @@ function parseMarkdown(markdown) {
153153 }
154154 return `<a href="${ url } " target="_blank">${ text } </a>` ;
155155 } ) ;
156- // Lists
157- processedMarkdown = processedMarkdown . replace ( / ^ \s * - \s + ( .* $ ) / gm, '<li class="ul-item">$1</li>' ) ; // Added space after -
158- processedMarkdown = processedMarkdown . replace ( / ^ \s * \d + \. \s + ( .* $ ) / gm, '<li class="ol-item">$1</li>' ) ; // Added space after .
159- // Wrap consecutive list items (handle potential empty lines between items)
160- processedMarkdown = processedMarkdown . replace ( / (?: < l i c l a s s = " u l - i t e m " > .* ?< \/ l i > \s * ) + / g, ( match ) => `<ul>${ match . replace ( / \s * $ / , '' ) } </ul>` ) ;
161- processedMarkdown = processedMarkdown . replace ( / (?: < l i c l a s s = " o l - i t e m " > .* ?< \/ l i > \s * ) + / g, ( match ) => `<ol>${ match . replace ( / \s * $ / , '' ) } </ol>` ) ;
162- processedMarkdown = processedMarkdown . replace ( / c l a s s = " u l - i t e m " / g, '' ) ;
163- processedMarkdown = processedMarkdown . replace ( / c l a s s = " o l - i t e m " / g, '' ) ;
164- // Emphasis
156+ // Emphasis (moved before list processing for simplicity, can be refined if needed)
165157 processedMarkdown = processedMarkdown . replace ( / \* \* ( .* ?) \* \* / g, '<strong>$1</strong>' ) ;
166158 processedMarkdown = processedMarkdown . replace ( / \* ( .* ?) \* / g, '<em>$1</em>' ) ;
167159
168- // 5. Replace Code Block Placeholders
160+ // 5. Advanced List Processing
161+ function processListSegment ( segment ) {
162+ const lines = segment . split ( '\n' ) ;
163+ let html = '' ;
164+ const listStack = [ ] ; // { type: 'ul' | 'ol', indent: number }
165+
166+ for ( const line of lines ) {
167+ const trimmedLine = line . trim ( ) ;
168+ if ( trimmedLine === '' ) { // Preserve intentional empty lines within list processing logic
169+ if ( listStack . length > 0 ) html += '\n' ; // Could be between items or part of multi-line item
170+ continue ;
171+ }
172+
173+ const itemRegex = / ^ ( \s * ) ( [ - * + ] | \d + \. ) \s + ( .* ) / ;
174+ const match = line . match ( itemRegex ) ; // Match on original line to get correct indentation
175+
176+ if ( match ) {
177+ const indentLength = match [ 1 ] . length ;
178+ const bulletOrOrder = match [ 2 ] ;
179+ let itemContent = match [ 3 ] ; // This is the rest of the line
180+
181+ const listType = isNaN ( parseInt ( bulletOrOrder , 10 ) ) ? 'ul' : 'ol' ;
182+
183+ // Close nested lists if current indent is less
184+ while ( listStack . length > 0 && indentLength < listStack [ listStack . length - 1 ] . indent ) {
185+ html += `</${ listStack . pop ( ) . type } >\n</li>\n` ;
186+ }
187+
188+ // Start a new list or a nested list
189+ if ( listStack . length === 0 || listType !== listStack [ listStack . length - 1 ] . type || indentLength > listStack [ listStack . length - 1 ] . indent ) {
190+ if ( listStack . length > 0 && listType === listStack [ listStack . length - 1 ] . type && indentLength < listStack [ listStack . length - 1 ] . indent ) {
191+ //This case should be handled by the while loop above, but as a safeguard:
192+ html += `</${ listStack . pop ( ) . type } >\n</li>\n` ;
193+ }
194+ if ( listStack . length > 0 && indentLength === listStack [ listStack . length - 1 ] . indent && listType !== listStack [ listStack . length - 1 ] . type ) {
195+ // Different list type at same indent level, close previous
196+ html += `</${ listStack . pop ( ) . type } >\n</li>\n` ;
197+ }
198+
199+ html += ( listStack . length > 0 ? '' : '' ) + `<${ listType } >\n` ;
200+ listStack . push ( { type : listType , indent : indentLength } ) ;
201+ } else if ( indentLength < listStack [ listStack . length - 1 ] . indent ) {
202+ // This case should be handled by the while loop above.
203+ // It implies de-indenting to a previous list level of the same type.
204+ }
205+
206+
207+ html += `<li>${ itemContent } ` ;
208+ // Subsequent lines that are more indented are part of this list item
209+ // This simple parser doesn't handle complex multi-line items elegantly without further lookahead or state.
210+ // For now, we assume a list item ends at a newline unless the next line is also a list item.
211+ // The paragraph logic later will handle non-list text.
212+ html += `</li>\n` ; // Close li immediately for this simplified model
213+
214+ } else {
215+ // Not a list item, close all open lists
216+ while ( listStack . length > 0 ) {
217+ html += `</${ listStack . pop ( ) . type } >\n` + ( listStack . length > 0 ? '</li>\n' : '' ) ;
218+ }
219+ // This line will be handled by paragraph logic if it's not empty
220+ // We need to ensure this non-list content is preserved.
221+ // The current logic processes the *whole* markdown for lists at once.
222+ // It might be better to split by non-list blocks first.
223+ // For now, we output the non-list line to be handled later.
224+ html += line + '\n' ; // Add non-list line back
225+ }
226+ }
227+
228+ // Close any remaining open lists
229+ while ( listStack . length > 0 ) {
230+ html += `</${ listStack . pop ( ) . type } >\n` + ( listStack . length > 0 ? '</li>\n' : '' ) ;
231+ }
232+ return html . trim ( ) ;
233+ }
234+
235+
236+ // We need to split the markdown by sections that are definitively NOT lists,
237+ // like headings, code blocks, tables, hr, etc., and then process potential list segments.
238+ // This is a more complex tokenization problem.
239+ // A simpler, yet potentially flawed approach for now:
240+ // Let the paragraph logic run, and then try to fix lists. This is what was failing.
241+
242+ // New Strategy: Process lists on lines that look like list items,
243+ // and then ensure paragraph logic doesn't mess them up.
244+
245+ let listLines = [ ] ;
246+ let nonListAccumulator = [ ] ;
247+ const lines = processedMarkdown . split ( '\n' ) ;
248+ let finalProcessedLines = [ ] ;
249+
250+ for ( let i = 0 ; i < lines . length ; i ++ ) {
251+ const line = lines [ i ] ;
252+ if ( / ^ \s * ( [ - * + ] | \d + \. ) \s + / . test ( line ) ) {
253+ if ( nonListAccumulator . length > 0 ) {
254+ finalProcessedLines . push ( nonListAccumulator . join ( '\n' ) ) ;
255+ nonListAccumulator = [ ] ;
256+ }
257+ listLines . push ( line ) ;
258+ } else {
259+ if ( listLines . length > 0 ) {
260+ finalProcessedLines . push ( processListSegment ( listLines . join ( '\n' ) ) ) ;
261+ listLines = [ ] ;
262+ }
263+ nonListAccumulator . push ( line ) ;
264+ }
265+ }
266+ if ( listLines . length > 0 ) {
267+ finalProcessedLines . push ( processListSegment ( listLines . join ( '\n' ) ) ) ;
268+ }
269+ if ( nonListAccumulator . length > 0 ) {
270+ finalProcessedLines . push ( nonListAccumulator . join ( '\n' ) ) ;
271+ }
272+ processedMarkdown = finalProcessedLines . join ( '\n' ) ;
273+
274+
275+ // 6. Replace Code Block Placeholders
169276 codePlaceholders . forEach ( ( placeholder , index ) => {
170277 const placeholderString = `__CODE_BLOCK_${ index } __` ;
171278 if ( placeholder . type === 'code' ) { // Interactive Python
172279 const id = `code-block-${ window . codeBlocks . length } ` ; // Use global counter
173- const language = placeholder . language ; // Should be 'python'
280+ const language = placeholder . language ;
174281 console . log ( `Creating interactive code block ID ${ id } for slide ${ slideIndex + 1 } ` ) ;
175282 window . codeBlocks . push ( { id, code : placeholder . code , language : language } ) ;
176- // Ensure the placeholder is replaced even if surrounded by <p> tags initially
177283 processedMarkdown = processedMarkdown . replace ( new RegExp ( `<p>\\s*${ placeholderString } \\s*<\\/p>|${ placeholderString } ` ) ,
178- `<div id="${ id } " class="code-editor-container" data-language="${ language } "></div>` ) ;
284+ `<div id="${ id } " class="code-editor-container" data-language="${ language } "></div>` ) ;
179285 } else { // Non-interactive pre/code
180286 const language = placeholder . language ;
181287 const languageClass = language ? ` class="language-${ language } "` : '' ;
182- const escapedCode = placeholder . code . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ;
183- // Ensure the placeholder is replaced even if surrounded by <p> tags initially
288+ const escapedCode = placeholder . code . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ; // Corrected escaping
184289 processedMarkdown = processedMarkdown . replace ( new RegExp ( `<p>\\s*${ placeholderString } \\s*<\\/p>|${ placeholderString } ` ) ,
185- `<pre><code${ languageClass } >${ escapedCode } </code></pre>` ) ;
290+ `<pre><code${ languageClass } >${ escapedCode } </code></pre>` ) ;
186291 }
187292 } ) ;
188293
189- // 6 . Paragraphs (apply last, carefully)
294+ // 7 . Paragraphs (apply last, carefully)
190295 processedMarkdown = processedMarkdown . split ( '\n' ) . map ( line => {
191296 const trimmedLine = line . trim ( ) ;
192- // Wrap line in <p> only if it's not empty, not already a tag, and not just whitespace
193- if ( trimmedLine && ! trimmedLine . startsWith ( '<' ) && ! trimmedLine . endsWith ( '>' ) ) {
194- return `<p>${ trimmedLine } </p>` ;
297+ if ( trimmedLine === '' ) {
298+ return '' ; // Preserve or remove empty lines based on desired behavior.
195299 }
196- return line ; // Keep lines that are empty or already tags
300+ // Check if the line is already part of a known block structure or is a block tag itself
301+ const isBlockTag = / ^ \s * < ( u l | o l | l i | h [ 1 - 6 ] | t a b l e | t h e a d | t b o d y | t r | t h | t d | p r e | d i v | f i g u r e | f i g c a p t i o n | b l o c k q u o t e | h r | p ) ( \s | > ) / i. test ( trimmedLine ) ;
302+ const isClosingBlockTag = / ^ \s * < \/ ( u l | o l | l i | h [ 1 - 6 ] | t a b l e | t h e a d | t b o d y | t r | t h | t d | p r e | d i v | f i g u r e | f i g c a p t i o n | b l o c k q u o t e | h r | p ) > / i. test ( trimmedLine ) ;
303+
304+ if ( trimmedLine && ! isBlockTag && ! isClosingBlockTag ) {
305+ // Further check: if it's inside a list item, don't wrap. This is hard without full context.
306+ // The list processing should ideally handle multi-line list items internally.
307+ return `<p>${ trimmedLine } </p>` ;
308+ }
309+ return line ; // Keep lines that are empty, already tags, or part of block elements
197310 } ) . join ( '\n' ) ;
198- // Clean up paragraphs around block elements more aggressively
199- processedMarkdown = processedMarkdown . replace ( / < p > \s * ( < (?: u l | o l | h [ 1 - 6 ] | d i v | p r e | t a b l e ) [ ^ > ] * > [ \s \S ] * ?< \/ (?: u l | o l | h [ 1 - 6 ] | d i v | p r e | t a b l e ) > ) \s * < \/ p > / g, '$1' ) ;
200- processedMarkdown = processedMarkdown . replace ( / < p > \s * < \/ p > / g, '' ) ; // Remove empty paragraphs
201311
202- // 7. Wrap slide content with the necessary wrapper and add to final HTML
312+ // Cleanup: Remove <p> tags that might have been wrongly inserted around block elements or are empty.
313+ processedMarkdown = processedMarkdown . replace ( / < p > \s * ( < ( u l | o l | h [ 1 - 6 ] | d i v | p r e | t a b l e | b l o c k q u o t e | h r ) (?: [ ^ > ] * > [ \s \S ] * ?< \/ \2> ) ? ) \s * < \/ p > / gi, '$1' ) ;
314+ // Remove <p> tags around list items if the list processing didn't prevent it.
315+ processedMarkdown = processedMarkdown . replace ( / < p > \s * ( < l i (?: [ ^ > ] * ) ? > [ \s \S ] * ?< \/ l i > ) \s * < \/ p > / gi, '$1' ) ;
316+ processedMarkdown = processedMarkdown . replace ( / < p > \s * < \/ p > / gi, '' ) ; // Remove empty p tags
317+
318+ // 8. Wrap slide content
203319 // Add data-index for potential JS targeting
204320 const slideContent = processedMarkdown . trim ( ) ;
205321 finalHtml += `<div class="board-slide" data-index="${ slideIndex } ">
0 commit comments