You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix tool toggle rendering by wrapping content in text element
Tool content wasn't wrapped in <text> element, causing OpenTUI reconciler errors when renderMarkdown() returned Fragments with raw text nodes. Agent content worked because it was already wrapped. Now all content is consistently wrapped in <text> elements.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Copy file name to clipboardExpand all lines: cli/knowledge.md
+62Lines changed: 62 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -353,3 +353,65 @@ const Parent = () => {
353
353
```
354
354
355
355
This pattern allows multiple styled components to be composed together within a single `<text>` element while avoiding the "Text must be created inside of a text node" error.
356
+
357
+
### Markdown Renderer Fragment Issue
358
+
359
+
**CRITICAL**: When `renderMarkdown()` returns a Fragment, it contains a **mix of JSX elements AND raw text strings** (newlines, text content, etc.). These raw strings become text nodes that violate OpenTUI's reconciler rules if not wrapped properly.
360
+
361
+
**The problem:**
362
+
```tsx
363
+
// renderMarkdown() returns something like:
364
+
<>
365
+
<strong>Bold text</strong>
366
+
'\n' // ⚠️ Raw string!
367
+
<span>More content</span>
368
+
'\n' // ⚠️ Raw string!
369
+
</>
370
+
371
+
// ❌ WRONG: Passing directly to <box>
372
+
<box>
373
+
{renderMarkdown(content)} // Raw strings create text nodes outside <text>
374
+
</box>
375
+
```
376
+
377
+
**The solution:**
378
+
```tsx
379
+
// ✅ CORRECT: Always wrap markdown output in <text>
380
+
<box>
381
+
<textwrap>
382
+
{renderMarkdown(content)} // Raw strings now inside <text> element
383
+
</text>
384
+
</box>
385
+
```
386
+
387
+
**Real-world example from BranchItem component:**
388
+
389
+
The bug occurred when tool toggles were rendered. Agent toggles worked fine, but tool toggles crashed.
390
+
391
+
**Why agents worked:**
392
+
```tsx
393
+
// Agent content always wrapped in <text>
394
+
<textwrapstyle={{ fg: theme.agentText }}>
395
+
{nestedBlock.content}
396
+
</text>
397
+
```
398
+
399
+
**Why tools failed before fix:**
400
+
```tsx
401
+
// Tool content passed directly to <box> - raw strings violated reconciler rules!
402
+
<box>
403
+
{displayContent} // Could be renderMarkdown() output with raw strings
404
+
</box>
405
+
```
406
+
407
+
**The fix:**
408
+
```tsx
409
+
// Always wrap ALL content in <text>, whether string or ReactNode
410
+
<box>
411
+
<textwrapfg={theme.agentText}>
412
+
{content} // Safe for both strings and markdown Fragments
413
+
</text>
414
+
</box>
415
+
```
416
+
417
+
**Key lesson:** Any component that receives content from `renderMarkdown()` or `renderStreamingMarkdown()` MUST wrap it in a `<text>` element, even if the content might be ReactNode. The Fragment can contain raw strings that need the text wrapper to be valid.
0 commit comments