Skip to content

[Bug]: Tab in a composer numbered list keeps the number and does not nest the item in the sent Markdown #17124

Description

@michal-billtech

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. In the composer, type 1. first.
  2. Press Shift+Enter. The composer continues the list with 2. (correct).
  3. Press Tab to nest the new item.
  4. Type nested and send the message.

Expected behavior

Tab nests the item as a sub-item and starts a nested numbered list, i.e. the draft becomes

1. first
   1. nested

which renders as a nested list (a. in the chat, via ol ol { list-style-type: lower-alpha }), and the agent receives a nested list.

There should also be a way back out of the nesting: Shift+Enter on an empty nested item should move it up one level and continue the parent's numbering (2.), as it already does in the rich-text composer.

Actual behavior

Tab only prepends two spaces and keeps the number, so the draft becomes

1. first
  2. nested
  • The item keeps 2. instead of starting a nested list.
  • Two spaces is not nesting under 1. in CommonMark: a child must reach the parent's content column (3 spaces for 1. , 4 for 10. ). The sent Markdown is therefore a flat two-item list. Checked with mdast-util-from-markdown: "1. foo\n 2. bar" parses as one list with 2 items, "1. foo\n 1. bar" as a nested list. The rich-text composer draws it nested, but the agent and the sent message see a flat list.
  • In the plain (non rich-text) composer, Shift+Enter on an empty nested item removes the marker and leaves the whole list instead of moving up one level.
  • Shift+Tab does nothing to the list. It toggles plan mode, or moves focus to the previous element when plan mode is unavailable (e.g. "start without a project").

Bullet lists are unaffected by the indent width (- has a content column of 2).

Cause: listIndentForTab in apps/web/src/composer-list-continuation.ts always inserts " " at the line start and never touches the marker; both composer modes route Tab through it.

Impact

Minor bug or occasional failure

Version or commit

main @ 30cc788

Environment

macOS 26 (Darwin 25.6.0), Node 24.15.0, web client via vp run dev; reproduced in both the rich-text and the plain composer.

Workaround

Indent manually with 3 spaces and type 1. yourself.

Proposed fix

I have a fix with focused tests on my fork, and I'm happy to open a PR once you agree with the direction:
main...michal-billtech:t3code:fix/composer-nested-ordered-list

  • Tab nests the item at the content column of the item above, numbering a nested ordered list from 1. or continuing an existing sublist. With nothing to nest under it keeps today's two-space indent.
  • Shift+Enter on an empty nested item moves it up one level and continues the parent's numbering in both composer modes (top-level empty items still leave the list).
  • Shift+Tab on a nested list item moves it back out, so Tab is reversible from the keyboard. Elsewhere Shift+Tab keeps toggling plan mode.

The Shift+Tab part changes an existing shortcut (plan mode toggle) on nested list lines, so I'd like your call on it. Without it, Shift+Enter on an empty nested item still provides the way out, and I can drop it from the PR.

Activity

  1. juliusmarminge commented on Oct 8, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the detailed report. I checked it against current main (30cc78897), and the Tab and Shift+Enter parts hold up.

    What the code does (confirmed)

    • Tab: listIndentForTab always inserts " " at the start of the line and leaves the marker alone, so 2. stays 2. . The existing test only checks a bullet (- foo → - foo).
    • Both modes use it: the Tiptap editor passes Tab to the composer's key handler in plain and rich mode, and Tiptap's own list Tab/Shift-Tab are turned off. ChatComposer then calls listIndentForTab.
    • Why rich mode looks nested: the rich-text parser treats any deeper indent prefix as a child list, with no content-column check. Serialization then writes the stored indent back as typed. So the editor draws 1. first\n 2. nested as nested, but the prompt text that gets sent is exactly that string. I ran it through mdast-util-from-markdown with GFM (the same micromark stack react-markdown/remark-gfm use). "1. first\n 2. nested" comes out as one flat list with two items, and "1. first\n 1. nested" comes out nested. - a\n - b nests fine, as you said.
    • Plain-mode Shift+Enter on an empty item: listContinuationForEnter removes the marker no matter how deep the indent is. Rich mode handles list items through splitOrLiftListItem, which lifts an empty item one level. That's why the two modes behave differently.
    • Shift+Tab: ChatComposer toggles plan mode, or returns false when plan mode isn't available, which lets the browser move focus back. Nothing outdents a list item.

    The user docs say Tab nests a list item and that what you type is what the agent receives. That makes the flat result a bug and not intended behavior.

    Inferred / not verified

    • I didn't check how the sent message bubble renders this in the app. With the parser above it would likely show as a flat list.
    • How a given model reads 2. nested is outside our control. The sent text is simply not a nested list in CommonMark.

    Possible fix direction (untested)

    • Tab on a list line should indent to the item above's content column (marker width plus its following space, so 3 for 1. and 4 for 10. ). For ordered items it should restart at 1. or continue an existing sublist. Because rich mode serializes the stored indent, fixing the source edit would likely fix both modes.
    • Plain-mode Shift+Enter on an empty nested item could outdent to the parent's indent and continue the parent's numbering, matching what rich mode already does.
    • Whether Shift+Tab should outdent nested list lines is a separate maintainer call, since it changes the plan-mode shortcut on those lines. It isn't needed for the Tab/Shift+Enter fix.

    Related

    On scope, your fork branch is one commit. It touches composer-list-continuation.ts and its tests, ChatComposer.tsx, and docs/user/composer.md, which covers the Tab nesting, the Shift+Enter outdent, and the Shift+Tab outdent. I haven't run it.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 8, 2026
  3. added a commit that references this issue on Oct 8, 2026
    0b4e119
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions