Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/chainlit/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
# Process and display mathematical expressions. This can clash with "$" characters in messages.
latex = false

# Enable rendering of user messages markdown
user_message_markdown = true

# Autoscroll new user messages at the top of the window
user_message_autoscroll = true

Expand Down Expand Up @@ -310,6 +313,7 @@ class FeaturesSettings(BaseModel):
mcp: McpFeature = Field(default_factory=McpFeature)
slack: SlackFeature = Field(default_factory=SlackFeature)
latex: bool = False
user_message_markdown: bool = True
user_message_autoscroll: bool = True
assistant_message_autoscroll: bool = True
unsafe_allow_html: bool = False
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Elements/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const TextElement = ({ element }: TextElementProps) => {
<Markdown
allowHtml={allowHtml}
latex={latex}
renderMarkdown={true}
className={`${element.display}-text`}
>
{content}
Expand Down
26 changes: 23 additions & 3 deletions frontend/src/components/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
interface Props {
allowHtml?: boolean;
latex?: boolean;
renderMarkdown?: boolean;
refElements?: IMessageElement[];
children: string;
className?: string;
Expand Down Expand Up @@ -91,12 +92,24 @@ const cursorPlugin = () => {
const Markdown = ({
allowHtml,
latex,
renderMarkdown,
refElements,
className,
children
}: Props) => {
const apiClient = useContext(ChainlitContext);

if (renderMarkdown === false) {
return (
<pre
className={cn('whitespace-pre-wrap break-words', className)}
style={{ fontFamily: 'inherit' }}
>
{children}
</pre>
);
}

const rehypePlugins = useMemo(() => {
let rehypePlugins: PluggableList = [];
if (allowHtml) {
Expand Down Expand Up @@ -162,9 +175,16 @@ const Markdown = ({
const src = image.src.startsWith('/public')
? apiClient.buildEndpoint(image.src)
: image.src;

const videoExtensions = ['.mp4', '.webm', '.mov', '.avi', '.ogv', '.m4v'];
const isVideo = videoExtensions.some(ext =>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had to include this to pass the linter checks.

const videoExtensions = [
'.mp4',
'.webm',
'.mov',
'.avi',
'.ogv',
'.m4v'
];
const isVideo = videoExtensions.some((ext) =>
src.toLowerCase().split(/[?#]/)[0].endsWith(ext)
);

Expand Down
10 changes: 8 additions & 2 deletions frontend/src/components/ReadOnlyThread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ const ReadOnlyThread = ({ id }: Props) => {
error: threadError,
isLoading
} = useApi<IThread>(
id ? (isSharedRoute ? `/project/share/${id}` : `/project/thread/${id}`) : null,
id
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had to include this to pass the linter checks.

? isSharedRoute
? `/project/share/${id}`
: `/project/thread/${id}`
: null,
{
revalidateOnFocus: false
revalidateOnFocus: false
}
);
const navigate = useNavigate();
Expand Down Expand Up @@ -153,6 +157,7 @@ const ReadOnlyThread = ({ id }: Props) => {
return {
allowHtml: config?.features?.unsafe_allow_html,
latex: config?.features?.latex,
renderMarkdown: config?.features?.user_message_markdown,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 15, 2025

Choose a reason for hiding this comment

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

P2: Missing config?.features?.user_message_markdown in the useMemo dependency array. If this config value changes, the memoized context won't update, causing stale renderMarkdown values to be used.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/ReadOnlyThread.tsx, line 160:

<comment>Missing `config?.features?.user_message_markdown` in the `useMemo` dependency array. If this config value changes, the memoized context won&#39;t update, causing stale `renderMarkdown` values to be used.</comment>

<file context>
@@ -153,6 +157,7 @@ const ReadOnlyThread = ({ id }: Props) =&gt; {
     return {
       allowHtml: config?.features?.unsafe_allow_html,
       latex: config?.features?.latex,
+      renderMarkdown: config?.features?.user_message_markdown,
       editable: false,
       loading: false,
</file context>

✅ Addressed in 853c6b5

editable: false,
loading: false,
showFeedbackButtons: !!config?.dataPersistence,
Expand All @@ -167,6 +172,7 @@ const ReadOnlyThread = ({ id }: Props) => {
config?.ui?.name,
config?.ui?.cot,
config?.features?.unsafe_allow_html,
config?.features?.user_message_markdown,
onElementRefClick,
onError,
onFeedbackUpdated,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface Props {
message: IStep;
allowHtml?: boolean;
latex?: boolean;
renderMarkdown?: boolean;
sections?: ContentSection[];
}

Expand All @@ -31,7 +32,10 @@ const getMessageRenderProps = (message: IStep) => ({

const MessageContent = memo(
forwardRef<HTMLDivElement, Props>(
({ message, elements, allowHtml, latex, sections }, ref) => {
(
{ message, elements, allowHtml, latex, renderMarkdown, sections },
ref
) => {
const outputContent =
message.streaming && message.output
? message.output + CURSOR_PLACEHOLDER
Expand Down Expand Up @@ -68,6 +72,7 @@ const MessageContent = memo(
<Markdown
allowHtml={allowHtml}
latex={latex}
renderMarkdown={renderMarkdown}
refElements={outputRefElements}
>
{output}
Expand Down Expand Up @@ -98,6 +103,7 @@ const MessageContent = memo(
<Markdown
allowHtml={allowHtml}
latex={latex}
renderMarkdown={renderMarkdown}
refElements={inputRefElements}
>
{input}
Expand Down Expand Up @@ -127,6 +133,7 @@ const MessageContent = memo(
return (
prevProps.allowHtml === nextProps.allowHtml &&
prevProps.latex === nextProps.latex &&
prevProps.renderMarkdown === nextProps.renderMarkdown &&
prevProps.elements === nextProps.elements &&
isEqual(
prevProps.sections ?? ['input', 'output'],
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/chat/Messages/Message/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const Message = memo(
isScorable,
scorableRun
}: Props) => {
const { allowHtml, cot, latex, onError } = useContext(MessageContext);
const { allowHtml, cot, latex, renderUserMarkdown, onError } =
useContext(MessageContext);
const layoutMaxWidth = useLayoutMaxWidth();
const contentRef = useRef<HTMLDivElement>(null);
const isUserMessage = message.type === 'user_message';
Expand All @@ -63,6 +64,7 @@ const Message = memo(
message={message}
allowHtml={allowHtml}
latex={latex}
renderMarkdown={renderUserMarkdown}
/>
),
[message, allowHtml, latex]
Expand Down Expand Up @@ -121,6 +123,7 @@ const Message = memo(
message={message}
allowHtml={allowHtml}
latex={latex}
renderMarkdown={true}
sections={['input']}
/>
) : null}
Expand All @@ -142,6 +145,7 @@ const Message = memo(
message={message}
allowHtml={allowHtml}
latex={latex}
renderMarkdown={true}
sections={showInputSection ? ['output'] : undefined}
/>
) : null}
Expand All @@ -160,6 +164,7 @@ const Message = memo(
message={message}
allowHtml={allowHtml}
latex={latex}
renderMarkdown={true}
/>

<AskFileButton messageId={message.id} onError={onError} />
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/chat/MessagesContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const MessagesContainer = ({ navigate }: Props) => {
askUser,
allowHtml: config?.features?.unsafe_allow_html,
latex: config?.features?.latex,
renderUserMarkdown: config?.features?.user_message_markdown,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 15, 2025

Choose a reason for hiding this comment

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

P1: Missing config?.features?.user_message_markdown in the useMemo dependency array. If this config value changes, the memoized context won't update, causing stale renderUserMarkdown values to be passed to children. Add it to the dependency array like unsafe_allow_html is handled.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/chat/MessagesContainer/index.tsx, line 127:

<comment>Missing `config?.features?.user_message_markdown` in the `useMemo` dependency array. If this config value changes, the memoized context won&#39;t update, causing stale `renderUserMarkdown` values to be passed to children. Add it to the dependency array like `unsafe_allow_html` is handled.</comment>

<file context>
@@ -124,6 +124,7 @@ const MessagesContainer = ({ navigate }: Props) =&gt; {
       askUser,
       allowHtml: config?.features?.unsafe_allow_html,
       latex: config?.features?.latex,
+      renderUserMarkdown: config?.features?.user_message_markdown,
       editable: !!config?.features.edit_message,
       loading,
</file context>

✅ Addressed in 853c6b5

editable: !!config?.features.edit_message,
loading,
showFeedbackButtons: enableFeedback,
Expand All @@ -141,6 +142,7 @@ const MessagesContainer = ({ navigate }: Props) => {
config?.ui?.name,
config?.ui?.cot,
config?.features?.unsafe_allow_html,
config?.features?.user_message_markdown,
onElementRefClick,
onError,
onFeedbackUpdated
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/chat/WelcomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ export default function WelcomeScreen(props: Props) {
}
/>
{currentChatProfile?.markdown_description ? (
<Markdown allowHtml={allowHtml} latex={latex}>
<Markdown
allowHtml={allowHtml}
latex={latex}
renderMarkdown={true}
>
{currentChatProfile.markdown_description}
</Markdown>
) : null}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/header/ChatProfiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ export default function ChatProfiles({ navigate }: Props) {
className="w-80 overflow-visible"
sideOffset={10}
>
<Markdown allowHtml={allowHtml} latex={latex}>
<Markdown
allowHtml={allowHtml}
latex={latex}
renderMarkdown={true}
>
{profile.markdown_description}
</Markdown>
</HoverCardContent>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/header/Readme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default function ReadmeButton() {
className="flex flex-col flex-grow overflow-y-auto"
allowHtml={config?.features?.unsafe_allow_html}
latex={config?.features?.latex}
renderMarkdown={true}
>
{config.markdown}
</Markdown>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types/messageContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface IMessageContext {
uiName: string;
allowHtml?: boolean;
latex?: boolean;
renderUserMarkdown?: boolean;
onElementRefClick?: (element: IMessageElement) => void;
onFeedbackUpdated?: (
message: IStep,
Expand Down
1 change: 1 addition & 0 deletions libs/react-client/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface IChainlitConfig {
user_message_autoscroll?: boolean;
assistant_message_autoscroll?: boolean;
latex?: boolean;
user_message_markdown?: boolean;
edit_message?: boolean;
mcp?: {
enabled?: boolean;
Expand Down
Loading