Private
Public Access
0
0

fix(md_renderer_py): inline code uses text_wrapped not small_button (fix ID conflict)

ROOT CAUSE: imgui.small_button(text) uses the text as the widget ID.
When the same inline code text appears multiple times in a rendered
markdown (e.g., the same function name in a table), imgui triggers
'3 visible items with conflicting ID' warning.

The C++ imgui-md SPAN_CODE callback is empty (renders as plain text).
Match that behavior: use text_wrapped with a slightly dimmed color
to indicate code spans. No widget ID, no conflicts.

ALSO: Added token cache to avoid re-parsing markdown every frame.
Each markdown-it-py parse is ~1ms; for static content re-rendered
every frame during scroll, the cache is the difference between smooth
and choppy. Cache is bounded to 64 entries (LRU-ish clear when full).

TESTS:
- test_renderer_renders_inline_code_with_button -> renamed intent,
  now asserts text_wrapped is called and a dimmed color is pushed
- test_render_handles_inline_code -> same update
- 2 new tests: test_renderer_caches_parsed_tokens, test_renderer_cache_invalidates_on_text_change

43/43 markdown tests pass.
This commit is contained in:
Conductor
2026-06-03 23:17:13 -04:00
parent 2d1d37779f
commit be5dffa4f0
3 changed files with 45 additions and 13 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ def test_render_handles_inline_code():
with patch("src.md_renderer_py.imgui") as mock_imgui:
_mock_imgui(mock_imgui)
MarkdownRenderer().render(md, context_id="code")
assert mock_imgui.small_button.called, "inline code should use small_button"
assert mock_imgui.text_wrapped.called, "inline code should use text_wrapped (not small_button, which causes ID conflicts for repeated spans)"
def test_render_handles_table():
md = "| A | B |\n|---|---|\n| 1 | 2 |"
+17 -2
View File
@@ -98,8 +98,8 @@ def test_renderer_renders_inline_code_with_button():
with patch("src.md_renderer_py.imgui") as mock_imgui:
_mock_imgui(mock_imgui)
r.render("Use `code` inline.")
assert mock_imgui.small_button.called, "inline code should use small_button"
assert mock_imgui.same_line.called
assert mock_imgui.text_wrapped.called, "inline code should use text_wrapped (not small_button, which causes ID conflicts for repeated spans)"
assert mock_imgui.push_style_color.called, "inline code should push a dimmed text color"
def test_renderer_renders_table_with_columns_and_rows():
r = MarkdownRenderer()
@@ -179,3 +179,18 @@ def test_renderer_render_unindented_strips_common_indent():
_mock_imgui(mock_imgui)
r.render_unindented(" - a\n - b\n - c")
assert mock_imgui.bullet.call_count == 3
def test_renderer_caches_parsed_tokens():
"""Cache the markdown-it-py parse result. Each parse is ~1ms; for
static text re-rendered every frame during scroll, the cache is the
difference between smooth and choppy.
"""
r = MarkdownRenderer()
with patch("src.md_renderer_py.imgui") as mock_imgui:
_mock_imgui(mock_imgui)
r.render("hello")
r.render("hello")
r.render("hello")
assert len(r._token_cache) == 1, f"expected 1 cached entry for 3 calls with same text, got {len(r._token_cache)}"
r.render("world")
assert len(r._token_cache) == 2, f"expected 2 cached entries after 2 distinct texts, got {len(r._token_cache)}"