Review
Plotline diary markdown for community garden co-op
Plotline is the shared garden journal for the Ward 7 community co-op (plot claim history, harvest tallies, pest alerts). Members already paste markdown in diary entries; this PR adds a lightweight client renderer so [label](url) links light up in the feed without pulling a full markdown package.
ReactTier 2securitymarkdownxsshref
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
components/garden/DiaryMarkdown.tsx+33-0
| 1 | 1 | import type { ReactNode } from "react"; | |
| 2 | 2 | ||
| 3 | 3 | type Props = { body: string }; | |
| 4 | 4 | ||
| 5 | + | const LINK_RE = /\[([^\]]+)\]\(([^)]+)\)/g; | |
| 6 | + | ||
| 7 | + | function renderInline(text: string): ReactNode[] { | |
| 8 | + | const nodes: ReactNode[] = []; | |
| 9 | + | let last = 0; | |
| 10 | + | let match: RegExpExecArray | null; | |
| 11 | + | let key = 0; | |
| 12 | + | while ((match = LINK_RE.exec(text)) !== null) { | |
| 13 | + | if (match.index > last) { | |
| 14 | + | nodes.push(text.slice(last, match.index)); | |
| 15 | + | } | |
| 16 | + | const [, label, href] = match; | |
| 17 | + | // open external soil-lab / city-permit links in a new tab | |
| 18 | + | nodes.push( | |
| 19 | + | <a key={key++} href={href} target="_blank" rel="noopener noreferrer"> | |
| 20 | + | {label} | |
| 21 | + | </a>, | |
| 22 | + | ); | |
| 23 | + | last = match.index + match[0].length; | |
| 24 | + | } | |
| 25 | + | if (last < text.length) nodes.push(text.slice(last)); | |
| 26 | + | return nodes; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | export function DiaryMarkdown({ body }: Props) { | |
| 30 | + | return ( | |
| 31 | + | <div className="diary-md"> | |
| 32 | + | {body.split("\n").map((line, i) => ( | |
| 33 | + | <p key={i}>{renderInline(line)}</p> | |
| 34 | + | ))} | |
| 35 | + | </div> | |
| 36 | + | ); | |
| 37 | + | } |