b72c5ff0c2
Co-authored-by: Kandev Agent <agent@kandev.dev>
65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { memo, useContext, useMemo } from "react";
|
|
import ReactMarkdown, { type Components } from "react-markdown";
|
|
import {
|
|
MarkdownFileLinkContext,
|
|
MarkdownTaskContext,
|
|
markdownComponents,
|
|
remarkPlugins,
|
|
type MarkdownFileLinkContextValue,
|
|
} from "@/components/shared/markdown-components";
|
|
import { normalizeCached } from "@/lib/markdown/normalize-cache";
|
|
|
|
/**
|
|
* Markdown renderer behind a `memo` boundary keyed on the `content` string.
|
|
*
|
|
* `content` is a primitive, so React compares it by value. Keep optional
|
|
* file-link props stable at the caller when possible; identical props bail out
|
|
* of the memo and re-use the previously parsed element tree. The normalized
|
|
* string itself is also cached (LRU) so two messages with the same content
|
|
* share a single normalize pass.
|
|
*/
|
|
type MemoizedMarkdownProps = MarkdownFileLinkContextValue & {
|
|
content: string;
|
|
components?: Components;
|
|
taskId?: string | null;
|
|
};
|
|
|
|
export const MemoizedMarkdown = memo(function MemoizedMarkdown({
|
|
content,
|
|
worktreePath,
|
|
onOpenFile,
|
|
fileRootAliases,
|
|
components,
|
|
taskId = null,
|
|
}: MemoizedMarkdownProps) {
|
|
const inheritedContext = useContext(MarkdownFileLinkContext);
|
|
const fileLinkContext = useMemo(
|
|
() => ({
|
|
worktreePath: worktreePath ?? inheritedContext.worktreePath,
|
|
onOpenFile: onOpenFile ?? inheritedContext.onOpenFile,
|
|
fileRootAliases: fileRootAliases ?? inheritedContext.fileRootAliases,
|
|
}),
|
|
[
|
|
fileRootAliases,
|
|
inheritedContext.fileRootAliases,
|
|
inheritedContext.onOpenFile,
|
|
inheritedContext.worktreePath,
|
|
onOpenFile,
|
|
worktreePath,
|
|
],
|
|
);
|
|
const resolvedComponents = components ?? markdownComponents;
|
|
|
|
return (
|
|
<MarkdownTaskContext.Provider value={taskId}>
|
|
<MarkdownFileLinkContext.Provider value={fileLinkContext}>
|
|
<ReactMarkdown remarkPlugins={remarkPlugins} components={resolvedComponents}>
|
|
{normalizeCached(content)}
|
|
</ReactMarkdown>
|
|
</MarkdownFileLinkContext.Provider>
|
|
</MarkdownTaskContext.Provider>
|
|
);
|
|
});
|