AgentDock icon indicating copy to clipboard operation
AgentDock copied to clipboard

Refactor Markdown Rendering & Syntax Highlighting

Open oguzserdar opened this issue 10 months ago • 0 comments

Problem Description

The current system for rendering Markdown and highlighting code blocks, primarily within src/components/chat/chat-markdown.tsx and src/components/ui/markdown-renderer.tsx (specifically the HighlightedPre component), relies heavily on client-side processing using the shiki library within a useEffect hook.

This approach leads to several issues:

  1. Flash of Unstyled Code (FOUC): Users briefly see un-highlighted code blocks before shiki loads and processes the code on the client-side, especially noticeable on initial load or slower connections.
  2. Performance Impact:
    • Bundle Size: shiki, especially with multiple languages/themes, can significantly increase the client-side JavaScript bundle size.
    • Client Load: The user's browser must perform the computationally intensive task of tokenizing and highlighting code, impacting rendering performance, especially on lower-end devices or with large code blocks.
  3. Layout Shifts: Minor layout shifts can occur when the code block is re-rendered with highlighting applied.
  4. Inefficiency: We have libraries like rehype-pretty-code, unified, remark-parse, and remark-rehype installed (package.json), which are designed for server-side Markdown processing and syntax highlighting, but they are not being leveraged effectively for this purpose in the current rendering flow.

Proposed Solution

Refactor the Markdown rendering and syntax highlighting process to prioritize server-side rendering where possible and optimize the client-side handling for streaming content.

  1. Implement Server-Side Highlighting (SSR/SSG):

    • For any Markdown rendered statically or outside the live chat stream (e.g., potential uses of MarkdownRenderer in documentation, static pages, etc.), implement server-side syntax highlighting.
    • Utilize the existing unified ecosystem (remark-parse -> remark-rehype -> rehype-pretty-code -> rehype-stringify) to process Markdown into pre-highlighted HTML on the server before sending it to the client.
    • This would likely involve creating a dedicated server utility function or leveraging Next.js data fetching methods / Server Components.
    • The client-side HighlightedPre component should be bypassed for this server-rendered content.
  2. Optimize Client-Side Highlighting (Streaming Chat - ChatMarkdown):

    • Since live streaming necessitates some client-side processing, review and optimize the current implementation in ChatMarkdown and HighlightedPre.
    • Ensure Single Execution: Verify that the highlighting logic runs only once per message after the stream has fully completed (the current use of finalContent state in ChatMarkdown should help achieve this).
    • Improve Loading State: Replace the brief display of unstyled code with a more explicit loading indicator or skeleton (like MermaidSkeleton used for diagrams) while shiki is processing asynchronously.
    • (Optional) Bundle Size Reduction: Investigate strategies to minimize the bundle size impact of shiki (e.g., asynchronously loading shiki itself, loading only necessary languages/themes).

Acceptance Criteria

  • Markdown rendered outside of live chat streams (if any) uses server-side highlighting, eliminating FOUC.
  • Reduced client-side JavaScript bundle size attributable to syntax highlighting libraries.
  • Improved perceived rendering performance for pages with code blocks.
  • Code blocks within streaming ChatMarkdown display a clear loading state instead of raw code while highlighting is processed.
  • Highlighting within ChatMarkdown is applied reliably once the stream finishes.
  • Minimal layout shifts during rendering and highlighting.

References

  • rehype-pretty-code documentation.
  • unified, remark, rehype ecosystem documentation.

oguzserdar avatar Apr 07 '25 22:04 oguzserdar