feat: add code block support to message composer#788
Merged
Conversation
The formatting toolbars on desktop and mobile had buttons for inline code but no way to insert code blocks without typing triple backticks manually. Adds a Code Block button (SquareCode icon) to both platforms, positioned after the inline Code button. Also fixes Shift+Enter inside code blocks on desktop — hardBreak nodes aren't valid inside ProseMirror code block nodes, so the smartShiftEnter handler now inserts a literal \n text character instead of falling through to the hardBreak extension.
submitOnEnter intercepted all Enter key presses, preventing Tiptap's CodeBlock input rule from ever firing. The rule also only matched at the start of a paragraph, so typing ``` after Shift+Enter (a hard break mid-message) never triggered it. Extracts code block extensions into codeBlockExtensions.ts: a fence detector in submitOnEnter that yields to the existing input rule (start of paragraph) or splits-and-converts directly (after hard break), plus a custom InputRule for the Space trigger after hard breaks. Also removes the non-functional ⌘⌥C shortcut from the Code Block toolbar tooltip.
…rendering The CodeBlockAfterHardBreak InputRule regex used U+FFFC to match hard breaks, but Tiptap's InputRule engine represents them as \n via renderText(). Single-line code blocks without a language specifier fell through to inline-code styling because neither the language-class check nor the newline-in-content check caught them.
Collaborator
|
@wpfleger96 when you have a sec, can you add some screenshots for this one to see what this change will look like? |
Collaborator
Author
Collaborator
Author
Collaborator
Author
|
@wesbillman this is good to go now! |
wesbillman
approved these changes
May 29, 2026
tlongwell-block
pushed a commit
that referenced
this pull request
May 29, 2026
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
wpfleger96
added a commit
that referenced
this pull request
Jun 1, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
wpfleger96
added a commit
that referenced
this pull request
Jun 1, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
wpfleger96
added a commit
that referenced
this pull request
Jun 1, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
wpfleger96
added a commit
that referenced
this pull request
Jun 2, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
wpfleger96
added a commit
that referenced
this pull request
Jun 2, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
wpfleger96
added a commit
that referenced
this pull request
Jun 2, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
wpfleger96
added a commit
that referenced
this pull request
Jun 2, 2026
…ence Desktop's code block support (PR #788) landed with a toolbar button but no equivalent rendering or composer UX on mobile. The sent messages used gpt_markdown's default CodeField with hard-coded Material styling that didn't match the design system. Adds _MessageCodeBlock (via gpt_markdown's codeBuilder callback) with rounded border, muted bg, GeistMono font, horizontal scroll, optional language label, and an always-visible copy-to-clipboard button. Also adds _expandFenceIfNeeded to auto-expand ```lang + Enter into a full fenced block template in the composer. A useRef isModifyingText guard prevents the controller listener from re-entering _expandFenceIfNeeded while applyCodeBlock or the expander itself is mutating the controller, which was causing a StackOverflow crash.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.







Adds Code Block formatting support to the desktop and mobile message composers — both as a toolbar button and as a ``` input trigger that creates WYSIWYG code blocks inline.
The formatting toolbars already had inline code buttons, but creating a multi-line code block required manually typing fenced markdown syntax that only rendered after sending. On desktop, Tiptap's built-in CodeBlock input rule (``` + Space) existed but was blocked in two ways:
submitOnEnterintercepted Enter before the rule could fire, and the rule's regex only matched at the start of a paragraph — not after a hard break mid-message. Separately, single-line code blocks without a language specifier rendered with both code-block and inline-code styling.SquareCodetoolbar button on desktop (FormattingToolbar.tsx) and mobile (compose_bar.dart) positioned after inline Code, withtoggleCodeBlock()and triple-backtick wrapping respectivelyShift+Enterinside code blocks on desktop — inserts a literal\ntext character sincehardBreaknodes aren't valid in ProseMirror code block schemacodeBlockExtensions.tswith: a fence detector insubmitOnEnterthat yields to the existing input rule (start of paragraph) or splits-and-converts directly (after hard break), plus a customInputRulefor the Space trigger after hard breaksCodeBlockAfterHardBreakInputRule regex to use\ninstead of U+FFFC — Tiptap's InputRule engine represents hard breaks viarenderText()which returns\n, not the object replacement charactermarkdown.tsx— detects fenced blocks by checking for a trailing\nin the rawchildrenstring (react-markdown always appends one to fenced block content, never to inline code)