import Link from "next/link"; import { cn } from "@/lib/utils"; import { inlineSegments, toBlocks, type Segment } from "@/lib/markdown-lite"; /** * The little Markdown this app renders: headings, lists, paragraphs, bold and * code. No dependency, because markdown-lite already parses it. * * Shared by the background reader and the assistant drawer. The drawer turns * in-app paths into links, because its system prompt asks the model to write * them as /applications/ — and a path that is not a link is a dead end. */ /** The app's own top-level screens. A path is linked only when it starts with one. */ const APP_PATH = /(^|[\s(])(\/(?:applications|me|crm|resumes|tasks|archive|settings|analytics|docs)(?:\/[\w-]+)*)/g; function withPathLinks(text: string) { const parts: React.ReactNode[] = []; let last = 0; for (const match of text.matchAll(APP_PATH)) { const at = match.index! + match[1].length; if (at > last) parts.push(text.slice(last, at)); parts.push( {match[2]} , ); last = at + match[2].length; } if (last === 0) return text; if (last < text.length) parts.push(text.slice(last)); return parts; } export function Markdown({ text, linkPaths = false }: { text: string; linkPaths?: boolean }) { return (
{toBlocks(text).map((block, index) => { if (block.type === "heading") { return (

{block.text}

); } if (block.type === "bullets" || block.type === "ordered") { const List = block.type === "bullets" ? "ul" : "ol"; return ( {block.items.map((item, itemIndex) => (
  • ))}
    ); } return (

    ); })}
    ); } function Inline({ segments, linkPaths }: { segments: Segment[]; linkPaths: boolean }) { return ( <> {segments.map((segment, index) => { if (segment.bold) return {segment.text}; if (segment.code) { return ( {segment.text} ); } return {linkPaths ? withPathLinks(segment.text) : segment.text}; })} ); }