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
11import type { ReactNode } from "react";
22
33type Props = { body: string };
44
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+}