@@ -1525,6 +1525,91 @@ export const HelpPanel = ({
15251525 } ;
15261526 } , [ isLoading ] ) ;
15271527
1528+ // Stable remarkPlugins array to prevent re-renders
1529+ const remarkPlugins = useMemo ( ( ) => [ remarkGfm ] , [ ] ) ;
1530+
1531+ // Stable ReactMarkdown components to prevent image remounting on re-render
1532+ const markdownComponents = useMemo ( ( ) => ( {
1533+ img ( { src, alt, ...props } ) {
1534+ const displayName = alt && alt !== 'uploaded image'
1535+ ? alt . replace ( / \. [ ^ . ] + $ / , '' ) : null ;
1536+ return (
1537+ < span className = "block mt-2" >
1538+ < img
1539+ src = { src }
1540+ alt = { alt || 'uploaded image' }
1541+ className = "max-w-full h-auto rounded"
1542+ style = { { maxHeight : '300px' , objectFit : 'contain' as const } }
1543+ loading = "lazy"
1544+ { ...props }
1545+ />
1546+ { displayName && (
1547+ < span className = "block text-xs text-gray-500 -mt-0.5 leading-tight" > { displayName } </ span >
1548+ ) }
1549+ </ span >
1550+ ) ;
1551+ } ,
1552+ code ( { className, children, ...props } ) {
1553+ const match = / l a n g u a g e - ( \w + ) / . exec ( className || '' ) ;
1554+ return match ? (
1555+ < SyntaxHighlighter
1556+ style = { tomorrow }
1557+ language = { match [ 1 ] }
1558+ PreTag = "div"
1559+ { ...props }
1560+ > { String ( children ) . replace ( / \n $ / , '' ) } </ SyntaxHighlighter >
1561+ ) : (
1562+ < code className = { className } { ...props } >
1563+ { children }
1564+ </ code >
1565+ ) ;
1566+ } ,
1567+ table ( { node, className, children, ...props } ) {
1568+ return (
1569+ < div className = "overflow-x-auto my-2 w-full" >
1570+ < table className = "table-auto border-collapse w-full text-xs" { ...props } >
1571+ { children }
1572+ </ table >
1573+ </ div >
1574+ ) ;
1575+ } ,
1576+ thead ( { node, children, ...props } ) {
1577+ return (
1578+ < thead className = "bg-gray-50" { ...props } >
1579+ { children }
1580+ </ thead >
1581+ ) ;
1582+ } ,
1583+ tbody ( { node, children, ...props } ) {
1584+ return (
1585+ < tbody className = "divide-y divide-gray-100" { ...props } >
1586+ { children }
1587+ </ tbody >
1588+ ) ;
1589+ } ,
1590+ tr ( { node, children, ...props } ) {
1591+ return (
1592+ < tr className = "hover:bg-gray-50" { ...props } >
1593+ { children }
1594+ </ tr >
1595+ ) ;
1596+ } ,
1597+ th ( { node, children, ...props } ) {
1598+ return (
1599+ < th className = "px-2 py-1 text-left text-xs font-medium text-gray-600 uppercase tracking-wider border border-gray-200 whitespace-nowrap" { ...props } >
1600+ { children }
1601+ </ th >
1602+ ) ;
1603+ } ,
1604+ td ( { node, children, ...props } ) {
1605+ return (
1606+ < td className = "px-2 py-1 text-xs text-gray-700 border border-gray-200" { ...props } >
1607+ { children }
1608+ </ td >
1609+ ) ;
1610+ }
1611+ } ) , [ ] ) ;
1612+
15281613 // Function to prepare messages for display
15291614 const prepareMessagesForDisplay = ( ) => {
15301615 // Find all user and system messages in chronological order (oldest to newest)
@@ -2388,87 +2473,8 @@ export const HelpPanel = ({
23882473 < div className = { `px-3 pb-3 ${ message . timestamp ? 'pt-0' : 'pt-3' } ` } >
23892474 < div className = "text-sm prose prose-sm prose-blue max-w-none" >
23902475 < ReactMarkdown
2391- remarkPlugins = { [ remarkGfm ] }
2392- components = { {
2393- img ( { src, alt, ...props } ) {
2394- const displayName = alt && alt !== 'uploaded image'
2395- ? alt . replace ( / \. [ ^ . ] + $ / , '' ) : null ;
2396- return (
2397- < span className = "block mt-2" >
2398- < img
2399- src = { src }
2400- alt = { alt || 'uploaded image' }
2401- className = "max-w-full h-auto rounded"
2402- style = { { maxHeight : '300px' , objectFit : 'contain' as const } }
2403- loading = "lazy"
2404- { ...props }
2405- />
2406- { displayName && (
2407- < span className = "block text-xs text-gray-500 -mt-0.5 leading-tight" > { displayName } </ span >
2408- ) }
2409- </ span >
2410- ) ;
2411- } ,
2412- code ( { className, children, ...props } ) {
2413- const match = / l a n g u a g e - ( \w + ) / . exec ( className || '' ) ;
2414- return match ? (
2415- < SyntaxHighlighter
2416- style = { tomorrow }
2417- language = { match [ 1 ] }
2418- PreTag = "div"
2419- { ...props }
2420- > { String ( children ) . replace ( / \n $ / , '' ) } </ SyntaxHighlighter >
2421- ) : (
2422- < code className = { className } { ...props } >
2423- { children }
2424- </ code >
2425- ) ;
2426- } ,
2427- table ( { node, className, children, ...props } ) {
2428- return (
2429- < div className = "overflow-x-auto my-2 w-full" >
2430- < table className = "table-auto border-collapse w-full text-xs" { ...props } >
2431- { children }
2432- </ table >
2433- </ div >
2434- ) ;
2435- } ,
2436- thead ( { node, children, ...props } ) {
2437- return (
2438- < thead className = "bg-gray-50" { ...props } >
2439- { children }
2440- </ thead >
2441- ) ;
2442- } ,
2443- tbody ( { node, children, ...props } ) {
2444- return (
2445- < tbody className = "divide-y divide-gray-100" { ...props } >
2446- { children }
2447- </ tbody >
2448- ) ;
2449- } ,
2450- tr ( { node, children, ...props } ) {
2451- return (
2452- < tr className = "hover:bg-gray-50" { ...props } >
2453- { children }
2454- </ tr >
2455- ) ;
2456- } ,
2457- th ( { node, children, ...props } ) {
2458- return (
2459- < th className = "px-2 py-1 text-left text-xs font-medium text-gray-600 uppercase tracking-wider border border-gray-200 whitespace-nowrap" { ...props } >
2460- { children }
2461- </ th >
2462- ) ;
2463- } ,
2464- td ( { node, children, ...props } ) {
2465- return (
2466- < td className = "px-2 py-1 text-xs text-gray-700 border border-gray-200" { ...props } >
2467- { children }
2468- </ td >
2469- ) ;
2470- }
2471- } }
2476+ remarkPlugins = { remarkPlugins }
2477+ components = { markdownComponents }
24722478 >
24732479 { message . role === 'system' ? message . content : message . user }
24742480 </ ReactMarkdown >
0 commit comments