@@ -46,6 +46,9 @@ const YOUTUBE_ID_REGEX = /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/))(?<
4646export function blocksToHtml ( blocks : BlockObjectResponse [ ] ) {
4747 let htmlContent = ""
4848
49+ let tableHasColumnHeader = false
50+ let tableHasRowHeader = false
51+
4952 for ( let i = 0 ; i < blocks . length ; i ++ ) {
5053 const block = blocks [ i ]
5154 assert ( block )
@@ -106,26 +109,35 @@ export function blocksToHtml(blocks: BlockObjectResponse[]) {
106109 }
107110 case "table" :
108111 htmlContent += `<table>`
112+ tableHasRowHeader = block . table . has_row_header
113+ tableHasColumnHeader = block . table . has_column_header
109114 break
110- case "table_row" :
111- if ( blocks [ i - 1 ] ?. type === "table" ) {
112- htmlContent += `<thead><tr>`
113- block . table_row . cells . forEach ( cell => {
115+ case "table_row" : {
116+ // Check if this is the first row after a table block
117+ const isFirstRow = blocks [ i - 1 ] ?. type === "table"
118+
119+ if ( isFirstRow ) {
120+ htmlContent += `<tbody>`
121+ }
122+
123+ htmlContent += `<tr>`
124+ block . table_row . cells . forEach ( ( cell , cellIndex ) => {
125+ // Determine if this cell is a header
126+ const isHeaderCell = ( isFirstRow && tableHasColumnHeader ) || ( cellIndex === 0 && tableHasRowHeader )
127+
128+ if ( isHeaderCell ) {
114129 htmlContent += `<th>${ richTextToHtml ( cell ) } </th>`
115- } )
116- htmlContent += `</tr></thead><tbody>`
117- } else {
118- htmlContent += `<tr>`
119- block . table_row . cells . forEach ( cell => {
130+ } else {
120131 htmlContent += `<td>${ richTextToHtml ( cell ) } </td>`
121- } )
122- htmlContent += `</tr>`
123- }
132+ }
133+ } )
134+ htmlContent += `</tr>`
124135
125136 if ( blocks [ i + 1 ] ?. type !== "table_row" ) {
126137 htmlContent += `</tbody></table>`
127138 }
128139 break
140+ }
129141 case "video" : {
130142 if ( block . video . type !== "external" ) {
131143 break
0 commit comments