- System prompts instruct AI to use [[Note Title]] wikilink syntax - preprocessWikilinks converts [[Target]] to markdown links - Custom urlTransform allows wikilink:// scheme through sanitizer - Click handler resolves target via findEntryByTarget and opens note - Styled as colored chips matching primary accent - Works in both AiPanel (agent) and AIChatPanel (legacy chat) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
64 lines
2.0 KiB
TypeScript
64 lines
2.0 KiB
TypeScript
import { memo, useMemo, useCallback, type MouseEvent } from 'react'
|
|
import Markdown, { defaultUrlTransform } from 'react-markdown'
|
|
import remarkGfm from 'remark-gfm'
|
|
import rehypeHighlight from 'rehype-highlight'
|
|
import { preprocessWikilinks, WIKILINK_SCHEME } from '../utils/chatWikilinks'
|
|
|
|
const REMARK_PLUGINS = [remarkGfm]
|
|
const REHYPE_PLUGINS = [rehypeHighlight]
|
|
|
|
function wikilinkUrlTransform(url: string): string {
|
|
if (url.startsWith(WIKILINK_SCHEME)) return url
|
|
return defaultUrlTransform(url)
|
|
}
|
|
|
|
interface MarkdownContentProps {
|
|
content: string
|
|
onWikilinkClick?: (target: string) => void
|
|
}
|
|
|
|
export const MarkdownContent = memo(function MarkdownContent({ content, onWikilinkClick }: MarkdownContentProps) {
|
|
const processedContent = useMemo(
|
|
() => onWikilinkClick ? preprocessWikilinks(content) : content,
|
|
[content, onWikilinkClick],
|
|
)
|
|
|
|
const handleClick = useCallback((e: MouseEvent) => {
|
|
const el = (e.target as HTMLElement).closest<HTMLElement>('[data-wikilink-target]')
|
|
if (el) {
|
|
e.preventDefault()
|
|
onWikilinkClick?.(el.dataset.wikilinkTarget!)
|
|
}
|
|
}, [onWikilinkClick])
|
|
|
|
const components = useMemo(() => {
|
|
if (!onWikilinkClick) return undefined
|
|
return {
|
|
a: ({ href, children }: { href?: string; children?: React.ReactNode }) => {
|
|
if (href?.startsWith(WIKILINK_SCHEME)) {
|
|
const target = decodeURIComponent(href.slice(WIKILINK_SCHEME.length))
|
|
return (
|
|
<span className="chat-wikilink" data-wikilink-target={target} role="link" tabIndex={0}>
|
|
{children}
|
|
</span>
|
|
)
|
|
}
|
|
return <a href={href}>{children}</a>
|
|
},
|
|
}
|
|
}, [onWikilinkClick])
|
|
|
|
return (
|
|
<div className="ai-markdown" onClick={onWikilinkClick ? handleClick : undefined} role="presentation">
|
|
<Markdown
|
|
remarkPlugins={REMARK_PLUGINS}
|
|
rehypePlugins={REHYPE_PLUGINS}
|
|
components={components}
|
|
urlTransform={onWikilinkClick ? wikilinkUrlTransform : undefined}
|
|
>
|
|
{processedContent}
|
|
</Markdown>
|
|
</div>
|
|
)
|
|
})
|