Autolink bare URLs in lesson comments

Comments render CommonMark, which leaves a pasted https://… as dead text —
students share links constantly and nobody writes [text](url) by hand. Add
remark-gfm so plain URLs, www hosts and emails become links (and tables and
strikethrough come along), plus table styling so a wide table scrolls inside
itself instead of stretching the page.

Sanitizing is unchanged: javascript: is neither linked nor autolinked, raw
HTML is still dropped, and third-party images are still rendered as plain
links by the img component.
This commit is contained in:
2026-08-16 11:55:48 +05:00
parent 8fc9708233
commit a92592ec63
4 changed files with 317 additions and 0 deletions
+18
View File
@@ -243,6 +243,24 @@
}
/* Markdown-разметка в комментариях под уроками (react-markdown) */
/* Таблицы GFM: широкая таблица прокручивается внутри себя, а не растягивает страницу */
.comment-md table {
display: block;
width: max-content;
max-width: 100%;
overflow-x: auto;
border-collapse: collapse;
margin: 0.5rem 0;
font-size: 0.95em;
}
.comment-md th,
.comment-md td {
border: 1px solid var(--border);
padding: 0.35rem 0.6rem;
text-align: left;
}
.comment-md th { font-weight: 700; background: var(--color-surface); }
.comment-md del { opacity: 0.6; }
.comment-md img {
display: block;
max-width: 100%;
@@ -2,6 +2,7 @@
import { useRef, useState, useTransition, type ReactNode } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import { addComment, deleteComment, editComment } from "@/lib/actions/student-actions";
@@ -63,6 +64,9 @@ function CommentBody({ text, imageHost }: { text: string; imageHost: string }) {
return (
<div className="comment-md text-sm leading-relaxed break-words">
<ReactMarkdown
// GFM: голый адрес сам становится ссылкой. Без него студент пишет
// https://… и получает мёртвый текст — разметку никто не проставляет.
remarkPlugins={[remarkGfm]}
rehypePlugins={[[rehypeSanitize, sanitizeSchema]]}
components={{
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- отбрасываем служебный node от react-markdown, чтобы он не протекал в DOM