@@ -321,9 +321,270 @@ codebuff "implement feature" --verbose
321321
322322 const inlineCode = nodes [ 1 ] as React . ReactElement
323323 const inlineContent = flattenChildren ( inlineCode . props . children ) . join ( '' )
324-
324+
325325 // Should preserve quotes and special characters within inline code
326326 expect ( inlineContent ) . toContain ( 'git commit -m "fix: bug"' )
327327 expect ( nodes [ 2 ] ) . toBe ( ' to commit.' )
328328 } )
329+
330+ describe ( 'lettered sub-item indentation' , ( ) => {
331+ test ( 'renders numbered questions with lettered sub-items (real world example)' , ( ) => {
332+ const markdown = `Questions:**
333+ 1. What is your preferred storage backend for real-time metrics aggregation?
334+ a) (DEFAULT) PostgreSQL with time-series optimized indexes (leverages existing infrastructure)
335+ b) Dedicated time-series database (InfluxDB/TimescaleDB) for better performance at scale
336+ c) Redis for real-time aggregation with periodic PostgreSQL sync
337+ 2. For the metrics dashboard visualization library:
338+ a) (DEFAULT) Recharts (already used in the project, consistent with existing charts)
339+ b) Apache ECharts (more powerful for complex visualizations)
340+ c) D3.js (maximum flexibility, steeper learning curve)
341+ 3. Alert webhook delivery priority:
342+ a) (DEFAULT) Implement webhook system first (most flexible for enterprise customers)
343+ b) Focus on email/in-app notifications first (simpler to implement)`
344+
345+ const output = renderMarkdown ( markdown )
346+ const nodes = flattenNodes ( output )
347+
348+ // Convert all nodes to text to check indentation
349+ const textContent = nodes
350+ . map ( ( node ) => {
351+ if ( typeof node === 'string' ) return node
352+ if ( React . isValidElement ( node ) ) {
353+ return flattenChildren ( node . props . children ) . join ( '' )
354+ }
355+ return ''
356+ } )
357+ . join ( '' )
358+
359+ // Lettered items should be indented (3 spaces when under numbered lists)
360+ expect ( textContent ) . toContain ( ' a) (DEFAULT) PostgreSQL' )
361+ expect ( textContent ) . toContain ( ' b) Dedicated time-series' )
362+ expect ( textContent ) . toContain ( ' c) Redis for real-time' )
363+ expect ( textContent ) . toContain ( ' a) (DEFAULT) Recharts' )
364+ expect ( textContent ) . toContain ( ' b) Apache ECharts' )
365+ expect ( textContent ) . toContain ( ' c) D3.js' )
366+ expect ( textContent ) . toContain ( ' a) (DEFAULT) Implement webhook' )
367+ expect ( textContent ) . toContain ( ' b) Focus on email' )
368+ } )
369+
370+ test ( 'renders simple numbered list with lettered sub-items' , ( ) => {
371+ const markdown = `1. First question?
372+ a) First option
373+ b) Second option
374+ c) Third option
375+ 2. Second question?
376+ a) Another option
377+ b) One more option`
378+
379+ const output = renderMarkdown ( markdown )
380+ const nodes = flattenNodes ( output )
381+
382+ const textContent = nodes
383+ . map ( ( node ) => {
384+ if ( typeof node === 'string' ) return node
385+ if ( React . isValidElement ( node ) ) {
386+ return flattenChildren ( node . props . children ) . join ( '' )
387+ }
388+ return ''
389+ } )
390+ . join ( '' )
391+
392+ // All lettered items should have 3 spaces of indentation (under numbered lists)
393+ expect ( textContent ) . toContain ( ' a) First option' )
394+ expect ( textContent ) . toContain ( ' b) Second option' )
395+ expect ( textContent ) . toContain ( ' c) Third option' )
396+ expect ( textContent ) . toContain ( ' a) Another option' )
397+ expect ( textContent ) . toContain ( ' b) One more option' )
398+ } )
399+
400+ test ( 'renders lettered items without numbered parents' , ( ) => {
401+ const markdown = `a) First standalone option
402+ b) Second standalone option
403+ c) Third standalone option`
404+
405+ const output = renderMarkdown ( markdown )
406+ const nodes = flattenNodes ( output )
407+
408+ const textContent = nodes
409+ . map ( ( node ) => {
410+ if ( typeof node === 'string' ) return node
411+ if ( React . isValidElement ( node ) ) {
412+ return flattenChildren ( node . props . children ) . join ( '' )
413+ }
414+ return ''
415+ } )
416+ . join ( '' )
417+
418+ // Should still be indented even without numbered parents
419+ expect ( textContent ) . toContain ( ' a) First standalone' )
420+ expect ( textContent ) . toContain ( ' b) Second standalone' )
421+ expect ( textContent ) . toContain ( ' c) Third standalone' )
422+ } )
423+
424+ test ( 'renders lettered items with DEFAULT markers' , ( ) => {
425+ const markdown = `1. Choose your option:
426+ a) (DEFAULT) Standard configuration
427+ b) Custom configuration
428+ c) Advanced configuration`
429+
430+ const output = renderMarkdown ( markdown )
431+ const nodes = flattenNodes ( output )
432+
433+ const textContent = nodes
434+ . map ( ( node ) => {
435+ if ( typeof node === 'string' ) return node
436+ if ( React . isValidElement ( node ) ) {
437+ return flattenChildren ( node . props . children ) . join ( '' )
438+ }
439+ return ''
440+ } )
441+ . join ( '' )
442+
443+ // Should preserve DEFAULT markers and apply indentation (3 spaces under list)
444+ expect ( textContent ) . toContain ( ' a) (DEFAULT) Standard' )
445+ expect ( textContent ) . toContain ( ' b) Custom' )
446+ expect ( textContent ) . toContain ( ' c) Advanced' )
447+ } )
448+
449+ test ( 'renders lettered items with long text' , ( ) => {
450+ const markdown = `1. Which approach do you prefer?
451+ a) This is a very long option that contains lots of detailed information about the approach and its benefits
452+ b) Short option
453+ c) Another very detailed option explaining all the trade-offs and considerations you should think about`
454+
455+ const output = renderMarkdown ( markdown )
456+ const nodes = flattenNodes ( output )
457+
458+ const textContent = nodes
459+ . map ( ( node ) => {
460+ if ( typeof node === 'string' ) return node
461+ if ( React . isValidElement ( node ) ) {
462+ return flattenChildren ( node . props . children ) . join ( '' )
463+ }
464+ return ''
465+ } )
466+ . join ( '' )
467+
468+ // Long text should still be indented (3 spaces under list)
469+ expect ( textContent ) . toContain ( ' a) This is a very long option' )
470+ expect ( textContent ) . toContain ( ' b) Short option' )
471+ expect ( textContent ) . toContain ( ' c) Another very detailed' )
472+ } )
473+
474+ test ( 'renders extended lettered lists (d, e, f)' , ( ) => {
475+ const markdown = `1. Pick one:
476+ a) Option A
477+ b) Option B
478+ c) Option C
479+ d) Option D
480+ e) Option E
481+ f) Option F`
482+
483+ const output = renderMarkdown ( markdown )
484+ const nodes = flattenNodes ( output )
485+
486+ const textContent = nodes
487+ . map ( ( node ) => {
488+ if ( typeof node === 'string' ) return node
489+ if ( React . isValidElement ( node ) ) {
490+ return flattenChildren ( node . props . children ) . join ( '' )
491+ }
492+ return ''
493+ } )
494+ . join ( '' )
495+
496+ // All lettered items a-f should be indented (3 spaces under list)
497+ expect ( textContent ) . toContain ( ' a) Option A' )
498+ expect ( textContent ) . toContain ( ' b) Option B' )
499+ expect ( textContent ) . toContain ( ' c) Option C' )
500+ expect ( textContent ) . toContain ( ' d) Option D' )
501+ expect ( textContent ) . toContain ( ' e) Option E' )
502+ expect ( textContent ) . toContain ( ' f) Option F' )
503+ } )
504+
505+ test ( 'does not indent uppercase lettered items' , ( ) => {
506+ const markdown = `A) This should not be indented
507+ B) Neither should this`
508+
509+ const output = renderMarkdown ( markdown )
510+ const nodes = flattenNodes ( output )
511+
512+ const textContent = nodes
513+ . map ( ( node ) => {
514+ if ( typeof node === 'string' ) return node
515+ if ( React . isValidElement ( node ) ) {
516+ return flattenChildren ( node . props . children ) . join ( '' )
517+ }
518+ return ''
519+ } )
520+ . join ( '' )
521+
522+ // Uppercase should NOT be indented (only lowercase a-z)
523+ expect ( textContent ) . not . toContain ( ' A)' )
524+ expect ( textContent ) . not . toContain ( ' B)' )
525+ expect ( textContent ) . toContain ( 'A) This should not' )
526+ expect ( textContent ) . toContain ( 'B) Neither should' )
527+ } )
528+
529+ test ( 'renders mixed content with paragraphs and lettered items' , ( ) => {
530+ const markdown = `Here's some context before the questions.
531+
532+ 1. First question?
533+ a) Option one
534+ b) Option two
535+
536+ And some text in between questions.
537+
538+ 2. Second question?
539+ a) Another option
540+ b) Final option
541+
542+ Conclusion text at the end.`
543+
544+ const output = renderMarkdown ( markdown )
545+ const nodes = flattenNodes ( output )
546+
547+ const textContent = nodes
548+ . map ( ( node ) => {
549+ if ( typeof node === 'string' ) return node
550+ if ( React . isValidElement ( node ) ) {
551+ return flattenChildren ( node . props . children ) . join ( '' )
552+ }
553+ return ''
554+ } )
555+ . join ( '' )
556+
557+ // Context and conclusion should not be indented
558+ expect ( textContent ) . toContain ( 'Here\'s some context' )
559+ expect ( textContent ) . toContain ( 'And some text in between' )
560+ expect ( textContent ) . toContain ( 'Conclusion text at the end' )
561+
562+ // Lettered items should be indented (3 spaces under list)
563+ expect ( textContent ) . toContain ( ' a) Option one' )
564+ expect ( textContent ) . toContain ( ' b) Option two' )
565+ expect ( textContent ) . toContain ( ' a) Another option' )
566+ expect ( textContent ) . toContain ( ' b) Final option' )
567+ } )
568+
569+ test ( 'does not indent text that happens to start with letter and parenthesis mid-sentence' , ( ) => {
570+ const markdown = `This is a sentence that mentions a) something in the middle.`
571+
572+ const output = renderMarkdown ( markdown )
573+ const nodes = flattenNodes ( output )
574+
575+ const textContent = nodes
576+ . map ( ( node ) => {
577+ if ( typeof node === 'string' ) return node
578+ if ( React . isValidElement ( node ) ) {
579+ return flattenChildren ( node . props . children ) . join ( '' )
580+ }
581+ return ''
582+ } )
583+ . join ( '' )
584+
585+ // Should not add indentation for a) in the middle of a sentence
586+ expect ( textContent ) . not . toContain ( ' a) something' )
587+ expect ( textContent ) . toContain ( 'a) something in the middle' )
588+ } )
589+ } )
329590} )
0 commit comments