Skip to content

fix(tui): detect nested markdown lists structurally, not by ANSI color#6359

Merged
jeremymcs merged 3 commits into
gsd-build:mainfrom
jeremymcs:tui-p0/03-markdown-nested
May 18, 2026
Merged

fix(tui): detect nested markdown lists structurally, not by ANSI color#6359
jeremymcs merged 3 commits into
gsd-build:mainfrom
jeremymcs:tui-p0/03-markdown-nested

Conversation

@jeremymcs

@jeremymcs jeremymcs commented May 18, 2026

Copy link
Copy Markdown
Contributor

Stack — TUI Phase 0 (4/8)

Builds on #6358. Stacked branch — diff also includes commits below it until they merge.

Problem

renderList() identified nested-list lines with the regex /^\s+\x1b\[36m[-\d]/ — assuming the list bullet is always ANSI cyan. Any theme with a different bullet color silently broke nested-list indentation.

Fix

renderListItem() now returns ListItemLine[] with a nested flag set on lines produced by recursive renderList() calls. renderList() keys off structure instead of pattern-matching theme output.

Test

Verified manually with a non-cyan (magenta) bullet theme — nested items indent correctly. packages/pi-tui suite passes.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed overlays not being erased from the terminal display when component state remained unchanged between frames
    • Fixed navigation errors that occurred when search or filter operations returned zero matching results
    • Improved markdown list rendering structure for better handling of nested list items

Review Change Stack

@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

🟡 PR Risk Report — MEDIUM

Files changed 4
Systems affected 1
Overall risk 🟡 MEDIUM

Affected Systems

Risk System
🟡 medium TUI Components
File Breakdown
Risk File Systems
🟡 packages/pi-tui/src/components/markdown.ts TUI Components
🟡 packages/pi-tui/src/components/select-list.ts TUI Components
🟡 packages/pi-tui/src/tui.ts TUI Components
packages/pi-tui/src/__tests__/tui.test.ts (unclassified)

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e1b02ad8-b776-44a9-8e85-84b300acf46a

📥 Commits

Reviewing files that changed from the base of the PR and between 297e099 and 224fb40.

📒 Files selected for processing (4)
  • packages/pi-tui/src/__tests__/tui.test.ts
  • packages/pi-tui/src/components/markdown.ts
  • packages/pi-tui/src/components/select-list.ts
  • packages/pi-tui/src/tui.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/pi-tui/src/components/select-list.ts
  • packages/pi-tui/src/components/markdown.ts

📝 Walkthrough

Walkthrough

This PR fixes three P0 issues from a TUI audit: overlay cache invalidation preventing dismissal redraws, select-list state corruption on empty filters, and markdown nested-list detection pitfalls. Changes span render-engine state tracking, input handling guards, and rendering logic refactors.

Changes

TUI Audit Phase 0 Fixes

Layer / File(s) Summary
TUI overlay dismissal cache invalidation
packages/pi-tui/src/tui.ts, packages/pi-tui/src/__tests__/tui.test.ts
TUI.doRender() tracks previous-frame overlay presence via _lastFrameHadOverlays flag and avoids skipping render when overlays were present, ensuring overlay erasure is not masked by unchanged component output. Regression test validates redraw occurs when overlay is dismissed.
SelectList empty-state input guard
packages/pi-tui/src/components/select-list.ts
SelectList.handleInput returns early when filteredItems is empty, preventing invalid selectedIndex updates. Triggers onCancel callback if set and selectCancel key is pressed.
Markdown list nested-structure refactor
packages/pi-tui/src/components/markdown.ts
New internal ListItemLine interface marks lines as nested by structure (recursion origin) rather than ANSI/bullet pattern. renderListItem returns structured lines; renderList applies indentation and bullets based on the nested flag instead of regex matching.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • gsd-build/gsd-2#5801: Both PRs modify TUI.doRender() render-skip logic to address cache invalidation and viewport refresh scenarios, so the render-control flow changes overlap.

Poem

Nested lists no longer fret with regex blues,
Overlays dismiss without confusion in hues,
SelectLists guard against the empty-filter plight,
Structure speaks truth where patterns once hid the light.
🐰 Four fixes stride forward with confidence bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(tui): detect nested markdown lists structurally, not by ANSI color' is directly aligned with the main change in the changeset—refactoring markdown list rendering to use structural nested-list handling instead of ANSI color pattern matching. It is concise, specific, and clearly summarizes the primary change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

jeremymcs added 3 commits May 18, 2026 08:45
The doRender() short-circuit skipped all post-processing when component
output was byte-identical to the previous frame. If the previous frame
composited an overlay onto the screen, an identical next frame with no
overlay would short-circuit and never erase it, leaving the overlay
stuck on screen.

Track _lastFrameHadOverlays and force a redraw when the previous frame
drew an overlay, even if base component output is unchanged.
When a filter matched no items, selectUp set selectedIndex to
filteredItems.length - 1 (i.e. -1), corrupting list state until the
next setFilter() reset it. Skip navigation and selection entirely when
the filtered list is empty; still honor cancel.
renderList() identified nested-list lines with the regex
/^\s+\x1b\[36m[-\d]/ — assuming the list bullet is always ANSI cyan.
Any theme with a different bullet color silently broke nested-list
indentation.

renderListItem() now returns ListItemLine[] with a 'nested' flag set on
lines produced by recursive renderList() calls, so renderList() keys off
structure instead of pattern-matching theme output.
@jeremymcs jeremymcs force-pushed the tui-p0/03-markdown-nested branch from 297e099 to 224fb40 Compare May 18, 2026 13:46
@jeremymcs jeremymcs merged commit c952d42 into gsd-build:main May 18, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant