Skip to content

[Bug]: Composer doesn't format inline code when text is typed between existing backticks #17098

Description

@grigolet

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. Open the composer (rich text on).
  2. Type ` ` (both backticks), then move the cursor between them.
  3. Type not working, then move the cursor past the closing backtick (End / Right arrow).

For comparison, these both format correctly:

  • Typing `working` left to right.
  • Typing working, moving to the start and typing `, then moving to the end and typing `.

Expected behavior

`not working` becomes inline code, the same as when the closing backtick is typed last.

Actual behavior

The text stays plain with the backticks showing (`not working`). Formatting seems to run only when the closing backtick is the last character typed. Text typed inside an already closed pair never gets checked.

Related but separate: #14870 fixes wrapping a selection in backticks. This report is about typing inside an existing pair of backticks.

Impact

Minor bug or occasional failure

Version or commit

0.0.45

Environment

Windows 11 Pro (10.0.26200), T3 Code desktop app

Logs or stack traces

Screenshots, recordings, or supporting files

Screen.Recording.2026-10-08.084459.mp4

Workaround

Type the closing backtick last.

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 8, 2026
  2. juliusmarminge commented on Oct 8, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the clear repro. I can confirm the mechanism from the code at main (73e097b8).

    Root cause

    • While you type, inline code is applied only by Tiptap's stock Code input rule. ComposerCodeExtension extends @tiptap/extension-code and doesn't override addInputRules. It's registered at ComposerPromptEditorTiptap.tsx#L1030.
    • In @tiptap/core 3.31.3, input rules run on text input only. They match against the text before the caret plus the character just typed (getTextContentFromNodes($from) + text). The code finder is anchored at the end: /`([^`]+)`(?!`)$/. So the rule only fires when the keystroke is the closing backtick.
    • In your failing sequence, every keystroke inside the pair sees `n, `no, and so on, with no closing backtick before the caret. Moving past the closing backtick with End or Right changes only the selection, which runs no input rule. Both of your working sequences end with the closing backtick, which is why they format.
    • The composer's own markdown parser, parseInlineMarkdown, would pair those backticks. But it only runs when the doc is rebuilt from the stored value (buildDocJson on mount, or setContent when the value changes outside the editor). A normal keystroke updates the snapshot first (L952-L957), so no rebuild happens. Because of that, the same draft likely shows as inline code after a remount (switching threads or reloading). I haven't tested that.
    • The wrap-selection handleTextInput (L1321-L1324) exits early on a collapsed caret, so it isn't involved here.

    Related

    • fix(web): wrapping a selection in backticks formats it as code #14870 handles typing a backtick over a non-empty selection (same handleTextInput + composer-rich-text-doc.ts), so it doesn't fix this case. Its diff was written against an older ComposerCodeExtension (before exitable: false was added), so it may need a rebase. A fix here would likely touch the same files.
    • Bold, italic, and strike probably have the same limitation, since StarterKit's mark input rules are also anchored at the end. I haven't checked that.

    Suggested fix
    Add a small ProseMirror plugin on ComposerCodeExtension in apps/web/src/composer-rich-text-doc.ts (addProseMirrorPlugins). On a typed-text transaction (skip paste and controlled setContent), it would check whether the caret's textblock now has an unmarked single-backtick pair around the inserted text. If so, it would apply the code mark and drop the backticks, the same way markInputRule does. Reusing parseInlineMarkdown to decide the pairing would keep the live behavior consistent with the rebuild path. The rules it would carry over: it handles ambiguous cases like `a` b `c`, stays on a single line, and ignores adjacent backticks. This is an untested direction. A unit test in composer-rich-text-doc.test.ts that inserts text between two backticks and asserts the serialized markdown and the marks would pin it down.

  3. added
    via-triageFiled through npx t3 triage
    and removed
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 8, 2026
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