Skip to content

fix(tui): keep nested list indentation in rendered markdown - #7030

Merged
Hmbown merged 1 commit into
codewhale-hq:mainfrom
Lstarsky0:ux/markdown-nested-list-indent
Oct 21, 2026
Merged

Hmbown merged 1 commit into
codewhale-hq:mainfrom
Lstarsky0:ux/markdown-nested-list-indent

Conversation

@Lstarsky0

Copy link
Copy Markdown
Contributor

What and why

The transcript's markdown parser trims each line before it looks for a list marker and keeps only the marker and the text, so a nested list renders flat. A reply with

- Top level item
  - Second level, indented two spaces
    - Third level, indented four spaces, long enough to wrap at forty columns
- Another top level item

1. Step one
   - detail a under step one
   - detail b under step one
2. Step two

renders at 80×24, before:

▏ - Top level item
▏ - Second level, indented two spaces
▏ - Third level, indented four spaces, long enough to wrap at forty columns
▏ - Another top level item
▏
▏ 1. Step one
▏ - detail a under step one
▏ - detail b under step one
▏ 2. Step two

After:

▏ - Top level item
▏   - Second level, indented two spaces
▏     - Third level, indented four spaces, long enough to wrap at forty
▏       columns
▏ - Another top level item
▏
▏ 1. Step one
▏    - detail a under step one
▏    - detail b under step one
▏ 2. Step two

Before, the details under "1." read like siblings of the numbered steps. ListItem now records the source line's leading whitespace and the renderer paints it before the bullet, so wrapped rows still hang under the item's text. The indent is capped at half the width so a pathological line can't push the text out of the pane. Lists without leading whitespace render as before.

What it doesn't do: a list item's own continuation line in the source (- item then an indented line of prose) is still a paragraph and renders flush left, and a tab counts as one column. A top-level list the model indents by one to three spaces now shifts right by that much, since the renderer paints what was written.

Issue

No-Issue: nested markdown lists render flat in the transcript; I searched and found no issue for it

How I tested it

  • Ran a debug build in a pseudo-terminal with an empty HOME against a local OpenAI-compatible mock that streams a canned reply, with this change and with a build from before it. The screens above are from the nested reply at 80×24; 40×12 shows the same indentation. A reply whose lists all start at column 0 (-, *, 1. to 10., with wrapped items) renders the same list rows before and after at 80×24 and 40×12. In a longer reply, - nested bullet under 2. Second item now sits under "Second item", with its wrapped path row under it.
  • Checked that nothing outside crates/tui/src matches on ListItem.
  • cargo fmt --all -- --check, cargo clippy -p codewhale-tui --all-targets --all-features with CI's flags, and cargo check -p codewhale-tui --all-targets: clean. Clippy also on 1.99, plus cargo +1.89.0 check -p codewhale-tui.
  • cargo nextest run -p codewhale-tui --lib: 15347 passed, none failed, including the markdown render tests and the incremental-vs-full reparse test, whose corpus has a nested list.

Checklist

  • One focused change, on cc4b96d; it merges cleanly into current main
  • cargo fmt --all passes, and tests cover new or changed behavior
  • If this adds or changes a user-facing feature, I updated its docs and its row in docs/features.toml (cargo test -p codewhale-tui --test feature_registry checks it) — n/a

The transcript's markdown parser trims each line before it looks for a
list marker and keeps only the marker and the text, so a nested list
renders flat. A reply like

    - Top level item
      - Second level
        - Third level
    1. Step one
       - detail a

shows every bullet in the same column, and the details under "1." look
like siblings of the numbered steps.

Record the source line's leading whitespace on the list item and paint
it before the bullet, so wrapped rows keep hanging under the item's
text as before. The indent is capped at half the width so a
pathological line can't push the text out of the pane. Lists without
leading whitespace render as before.

Signed-off-by: Lstarsky0 <[email protected]>
@Lstarsky0
Lstarsky0 requested a review from Hmbown as a code owner October 20, 2026 16:01

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@github-actions github-actions Bot added the contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm label Oct 20, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T16:07:50.345675Z d4b54cc PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Hmbown
Hmbown merged commit ccd8d97 into codewhale-hq:main Oct 21, 2026
28 checks passed
@Hmbown

Hmbown commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

🤖 This reply is from Claude Opus 5.5, an AI agent working on behalf of the Codewhale maintainers. It is posted through @Hmbown's account, but Hunter did not write it.

Thanks @Lstarsky0. You traced the flat nested lists to the parser trimming each line before it looks for a list marker. Recording the source indent on ListItem and painting it before the bullet keeps wrapped rows hanging under their item, and the half-width cap stops a pathological indent from pushing the text out of the pane. Details under a numbered step no longer read like sibling steps. Thanks as well for listing what it doesn't cover (continuation prose, tabs as one column, the shift for 1 to 3 space top-level lists). Merged in ccd8d97 for 0.10.2, which hasn't shipped yet.

Hmbown pushed a commit that referenced this pull request Oct 21, 2026
Every 0.10.2 line now sits under [0.10.2] > Latest fixes in user voice:
the proxy-aware install guard, local-state link hardening, the two
command-splitter fixes, read-only network (#7040), MCP status and plugin
servers at session start (#7035), plugin vocabulary and the marketplace
pin 2e3a6fb (#7036), the declared output limit and published reasoning
levels (#6396), #6955, #6978, #7007, #6478, the thread read marker
(#6164), the stale Runtime owner record, HarmonyOS docs and npm (#1072),
the three web-tool fixes (#7038, #7039, #6969), and the Lstarsky0 fixes
#7030, #7033 and #7034. Internal tracker ids are removed from the public
section, the citation line references #7039, Maintenance records the
mirrors and the CI lane, and Contributors adds the six report helpers.
crates/tui/CHANGELOG.md is regenerated with sync-changelog.sh;
docs/CONTRIBUTORS.md and web/lib/release-credits.ts carry the same
credits; web/data/agent-docs.generated.json and features.generated.json
are regenerated from the landed docs and registry.

Batch evidence (one combined tree, input hash a47518092a4c49de37d93381d392aec4fc4c28fd612f6e6192d327acf4736d95; the intermediate
commits of this batch were not compiled on their own, only the combined
tree): cargo fmt --check clean; tui lib nextest over the lane filters 4096
passed, 0 failed, plus 1268 passed after the changelog edit; fp55
shared-process cargo test 161 passed, 0 failed, 1 ignored; tui integration
install::/skill_cli:: 42 passed, 0 failed; conformance goldens regenerated, 33
passed; execpolicy 89 command_safety and 256 crate tests passed; lane 74;
runtime continual_harness 8; config private_directory 12, route/models_dev
121, configured_models 9; app-server daemon_client 1, daemon_socket 15;
localization 54; clippy -D warnings on tui, execpolicy, lane, runtime,
app-server and config clean with CI's exact flags (the plan's stricter run flagged 26 pre-existing sites outside the batch: 25 are lints CI allows, one collapsible_if is fixed in this batch); cargo check tui --all-targets
clean (a first run failed on a half-applied call-site change of the citation fix; the rerun after the fix passed); aarch64-unknown-linux-gnu check of tui, app-server and config
not run: the ring and libdbus-sys build scripts fail for the cross target on this Mac, so the Linux-only bubblewrap and boot-id paths were not type-checked here and need a Linux host; blocking-call budget 540 sites within budget; dead-code budget
214 at budget; sync-changelog --check, check-versions,
check-bundled-plugin-claims, sync-marketplace --check and web check-facts
all passed; npm wrapper node --test 101 passed, 0 failed. No hosted CI ran;
final validation owns that.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants