@@ -23,6 +23,8 @@ import androidx.compose.ui.text.withStyle
2323import androidx.compose.ui.unit.dp
2424import androidx.compose.ui.graphics.Color
2525import androidx.compose.material3.LocalContentColor
26+ import androidx.compose.foundation.background
27+ import androidx.compose.foundation.border
2628import androidx.documentfile.provider.DocumentFile
2729import co.rustworkshop.markdownneuraxis.io.readFileContent
2830import co.rustworkshop.markdownneuraxis.io.resolveDocumentFile
@@ -131,8 +133,9 @@ private fun RenderBlockTree(
131133) {
132134 for (block in blocks) {
133135 RenderBlock (block, depth, onWikiLinkClick)
134- // List blocks handle their own children internally
135- if (block.kind != " list" && block.children.isNotEmpty()) {
136+ // List and table blocks handle their own children internally
137+ val handlesOwnChildren = block.kind in listOf (" list" , " table" , " table_header_row" , " table_row" )
138+ if (! handlesOwnChildren && block.children.isNotEmpty()) {
136139 RenderBlockTree (block.children, depth + 1 , onWikiLinkClick)
137140 }
138141 }
@@ -299,6 +302,34 @@ private fun RenderBlock(block: Block, depth: Int, onWikiLinkClick: (String) -> U
299302 " thematic_break" -> {
300303 HorizontalDivider (modifier = Modifier .padding(vertical = 8 .dp))
301304 }
305+ " table" -> {
306+ // Table container - calculate column count for consistent widths
307+ val columnCount = block.children.firstOrNull()?.children?.size ? : 1
308+ Column (
309+ modifier = Modifier
310+ .padding(vertical = 8 .dp)
311+ .border(1 .dp, MaterialTheme .colorScheme.outline)
312+ ) {
313+ block.children.forEachIndexed { index, row ->
314+ RenderTableRow (row, columnCount, onWikiLinkClick)
315+ // Add divider between rows (not after last)
316+ if (index < block.children.size - 1 ) {
317+ HorizontalDivider (color = MaterialTheme .colorScheme.outline)
318+ }
319+ }
320+ }
321+ }
322+ " table_header_row" , " table_row" -> {
323+ // Rows are rendered via RenderTableRow, this handles standalone case
324+ RenderTableRow (block, block.children.size, onWikiLinkClick)
325+ }
326+ " table_cell" -> {
327+ // Standalone cell (shouldn't happen - rows handle cells)
328+ RenderSegments (
329+ segments = block.segments,
330+ onWikiLinkClick = onWikiLinkClick
331+ )
332+ }
302333 else -> {
303334 Text (
304335 text = segmentsToText(block.segments),
@@ -308,6 +339,54 @@ private fun RenderBlock(block: Block, depth: Int, onWikiLinkClick: (String) -> U
308339 }
309340}
310341
342+ @Composable
343+ private fun RenderTableRow (
344+ block : Block ,
345+ columnCount : Int ,
346+ onWikiLinkClick : (String ) -> Unit
347+ ) {
348+ val isHeader = block.kind == " table_header_row"
349+ val cells = block.children
350+
351+ Row (
352+ modifier = Modifier
353+ .fillMaxWidth()
354+ .height(IntrinsicSize .Min )
355+ .then(
356+ if (isHeader) Modifier .background(MaterialTheme .colorScheme.surfaceVariant)
357+ else Modifier
358+ )
359+ ) {
360+ // Render each cell, padding to columnCount if needed
361+ for (i in 0 until columnCount) {
362+ if (i > 0 ) {
363+ // Vertical divider between cells
364+ VerticalDivider (
365+ modifier = Modifier .fillMaxHeight(),
366+ color = MaterialTheme .colorScheme.outline
367+ )
368+ }
369+ Box (
370+ modifier = Modifier
371+ .weight(1f )
372+ .padding(horizontal = 8 .dp, vertical = 6 .dp)
373+ ) {
374+ if (i < cells.size) {
375+ RenderSegments (
376+ segments = cells[i].segments,
377+ style = if (isHeader) {
378+ MaterialTheme .typography.bodyMedium.copy(fontWeight = FontWeight .Bold )
379+ } else {
380+ MaterialTheme .typography.bodyMedium
381+ },
382+ onWikiLinkClick = onWikiLinkClick
383+ )
384+ }
385+ }
386+ }
387+ }
388+ }
389+
311390private fun parseDocument (content : String ): List <Block >? {
312391 return try {
313392 val doc = DocumentHandle .fromString(content)
0 commit comments