Files
tolaria/src/components/MarkdownContent.tsx
Test 4719810b10 feat: render [[wikilinks]] as clickable links in AI chat
- 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>
2026-03-07 01:40:44 +01:00

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>
)
})