From 5170d3adf4a89816c4b8c845beec56e5a3b4dfb2 Mon Sep 17 00:00:00 2001 From: Alex Dunmow Date: Thu, 18 Jun 2026 21:59:30 +0800 Subject: [PATCH] fix(render): unwrap BlockNote tableCell objects so table cells render content BlockNote >=0.15 (the editor ships 0.47) stores each table cell as a {type:"tableCell", content:[...]} object. The renderer passed each cell straight to inlineContentFromRaw, which only understands strings and bare inline arrays, so object-form cells rendered an empty grid (correct rows and columns, no content). Unwrap content[] from tableCell objects while keeping the legacy bare-array/string cell formats working. Co-Authored-By: Claude Opus 4.8 (1M context) --- render/blocknote.go | 11 ++++++++++- render/blocknote_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/render/blocknote.go b/render/blocknote.go index 921cad4..2089689 100644 --- a/render/blocknote.go +++ b/render/blocknote.go @@ -374,8 +374,17 @@ func renderBlock(ctx context.Context, block map[string]any) string { cellTag = "th" cellClass = "px-4 py-3 text-left text-sm font-semibold" } + // BlockNote >=0.15 wraps each cell as a tableCell object + // {type:"tableCell", content:[...]}; older docs store the + // cell's inline content (string or array) directly. + cellContent := cell + if cellMap, ok := cell.(map[string]any); ok { + if t, _ := cellMap["type"].(string); t == "tableCell" { + cellContent = cellMap["content"] + } + } fmt.Fprintf(&sb, "<%s class=\"%s\">", cellTag, cellClass) - sb.WriteString(renderInlineContent(inlineContentFromRaw(cell), false)) + sb.WriteString(renderInlineContent(inlineContentFromRaw(cellContent), false)) fmt.Fprintf(&sb, "", cellTag) } } diff --git a/render/blocknote_test.go b/render/blocknote_test.go index 6bcc063..8f4cd35 100644 --- a/render/blocknote_test.go +++ b/render/blocknote_test.go @@ -520,6 +520,40 @@ func TestTableCellStringContent(t *testing.T) { } } +// BlockNote >=0.15 (the editor ships 0.47) stores each table cell as a +// tableCell object: {type:"tableCell", props:{...}, content:[...inline]}. +// The renderer must unwrap content[] from these objects, not just bare +// inline arrays/strings. +func TestTableCellObjectContent(t *testing.T) { + cell := func(text string) map[string]any { + return map[string]any{ + "type": "tableCell", + "props": map[string]any{}, + "content": []any{map[string]any{"type": "text", "text": text}}, + } + } + doc := map[string]any{ + "blocks": []any{ + map[string]any{ + "type": "table", + "content": map[string]any{ + "type": "tableContent", + "rows": []any{ + map[string]any{"cells": []any{cell("Header A"), cell("Header B")}}, + map[string]any{"cells": []any{cell("Cell A"), cell("Cell B")}}, + }, + }, + }, + }, + } + html := BlockNoteToHTML(context.Background(), doc) + for _, want := range []string{"Header A", "Header B", "Cell A", "Cell B"} { + if !strings.Contains(html, want) { + t.Errorf("expected table cell content %q in output: %s", want, html) + } + } +} + func TestNestedListRendering(t *testing.T) { doc := map[string]any{ "blocks": []any{