@@ -7,21 +7,64 @@ import type {
77 ListNode ,
88 MathBlockNode ,
99 CodeBlockNode ,
10+ InlineNode ,
1011} from "../types" ;
11- import { RenderInline } from "./InlineNodes" ;
12+ import { RenderInline , renderInlineNode } from "./InlineNodes" ;
13+ import { slugify , inlineToText } from "../utils" ;
14+
15+ const Marker = ( { children } : { children : React . ReactNode } ) => (
16+ < span className = "md-marker" > { children } </ span >
17+ ) ;
18+
19+ // Split inline nodes on <br> boundaries and render each segment as a > prefixed line.
20+ function renderBlockquoteLines ( nodes : InlineNode [ ] ) : React . ReactNode {
21+ const segments : InlineNode [ ] [ ] = [ ] ;
22+ let current : InlineNode [ ] = [ ] ;
23+ for ( const node of nodes ) {
24+ if ( node . type === "br" ) {
25+ segments . push ( current ) ;
26+ current = [ ] ;
27+ } else {
28+ current . push ( node ) ;
29+ }
30+ }
31+ segments . push ( current ) ;
32+ return segments . map ( ( seg , i ) => (
33+ < span key = { i } className = "bq-line" >
34+ < Marker > { ">" }  </ Marker >
35+ { seg . map ( ( n , j ) => renderInlineNode ( n , j ) ) }
36+ </ span >
37+ ) ) ;
38+ }
39+
40+ function renderBlockquoteChild ( node : BlockNode , key : number ) : React . ReactNode {
41+ if ( node . type === "paragraph" ) {
42+ return < p key = { key } > { renderBlockquoteLines ( ( node as ParagraphNode ) . children ) } </ p > ;
43+ }
44+ // Non-paragraph blocks (headings, nested blockquotes, etc.) get a single > prefix
45+ return (
46+ < React . Fragment key = { key } >
47+ < Marker > { ">" }  </ Marker >
48+ { renderBlockNode ( node , key ) }
49+ </ React . Fragment >
50+ ) ;
51+ }
1252
1353export function renderBlockNode ( node : BlockNode , key : string | number ) : React . ReactNode {
1454 switch ( node . type ) {
1555 case "heading" : {
1656 const n = node as HeadingNode ;
57+ const marker = "#" . repeat ( n . level ) + " " ;
1758 const inner = < RenderInline nodes = { n . children } /> ;
59+ const id = slugify ( inlineToText ( n . children ) ) ;
60+ const anchor = < a href = { `#${ id } ` } className = "heading-anchor" aria-label = "Link to section" > §</ a > ;
1861 switch ( n . level ) {
19- case 1 : return < h1 key = { key } > { inner } </ h1 > ;
20- case 2 : return < h2 key = { key } > { inner } </ h2 > ;
21- case 3 : return < h3 key = { key } > { inner } </ h3 > ;
22- case 4 : return < h4 key = { key } > { inner } </ h4 > ;
23- case 5 : return < h5 key = { key } > { inner } </ h5 > ;
24- case 6 : return < h6 key = { key } > { inner } </ h6 > ;
62+ case 1 : return < h1 key = { key } id = { id } > < Marker > { marker } </ Marker > { inner } { anchor } </ h1 > ;
63+ case 2 : return < h2 key = { key } id = { id } > < Marker > { marker } </ Marker > { inner } { anchor } </ h2 > ;
64+ case 3 : return < h3 key = { key } id = { id } > < Marker > { marker } </ Marker > { inner } { anchor } </ h3 > ;
65+ case 4 : return < h4 key = { key } id = { id } > < Marker > { marker } </ Marker > { inner } { anchor } </ h4 > ;
66+ case 5 : return < h5 key = { key } id = { id } > < Marker > { marker } </ Marker > { inner } { anchor } </ h5 > ;
67+ case 6 : return < h6 key = { key } id = { id } > < Marker > { marker } </ Marker > { inner } { anchor } </ h6 > ;
2568 }
2669 break ;
2770 }
@@ -36,14 +79,15 @@ export function renderBlockNode(node: BlockNode, key: string | number): React.Re
3679 case "blockquote" :
3780 return (
3881 < blockquote key = { key } >
39- { ( node as BlockquoteNode ) . children . map ( ( c , i ) => renderBlockNode ( c , i ) ) }
82+ { ( node as BlockquoteNode ) . children . map ( ( c , i ) => renderBlockquoteChild ( c , i ) ) }
4083 </ blockquote >
4184 ) ;
4285
4386 case "list" : {
4487 const n = node as ListNode ;
4588 const items = n . items . map ( ( item , i ) => (
4689 < li key = { i } >
90+ < span className = "md-marker" > { n . ordered ? `${ i + 1 } . ` : "- " } </ span >
4791 < RenderInline nodes = { item . children } />
4892 </ li >
4993 ) ) ;
0 commit comments