PathLogs UI
English

Компоненты

Markdown

Ограниченная разметка без зависимостей и без сырого HTML.

Ограниченный Markdown для текста, который пишут пользователи: описания задач, комментарии, заметки. Без зависимостей и без сырого HTML.

Пример

Что сделано

Переписали разбор входящих заявок: теперь они попадают сразу в колонку проекта, а не в общий список.

  • проверка домена отправителя
  • склейка дублей по теме письма
  • X-Request-Id в каждом ответе

Осталось решить, что делать с письмами без темы.

Подробности — в описании задачи.

Что поддерживается

md
# Заголовок          (уровни 1–3)
**жирный** *курсив* ~~зачёркнутый~~ `код`

- маркированный список
1. нумерованный список

> цитата

---

[ссылка](https://example.com)

```ts
блок кода
```

Уровень заголовка ограничен тремя, а сам заголовок рендерится абзацем с крупным начертанием: пользовательский текст не должен вмешиваться в структуру заголовков страницы и ломать её вёрстку.

Безопасность

<script>alert(1)</script> — остаётся текстом.

[Опасная ссылка](javascript:alert(1)) — ссылкой не становится.

Обычная ссылка — становится.

Картинок нет намеренно: они позволяют утащить IP читателя на чужой сервер и ломают вёрстку списков.

Упоминания

Список известных имён подсвечивает @упоминания в тексте:

tsx
<Markdown text={comment.body} mentions={project.members.map((m) => m.name)} />

Имена сортируются от длинных к коротким: иначе «@Иван» съел бы начало «@Иван Петров», и упоминание распалось бы на подсветку и хвост текста. Спецсимволы в именах экранируются — имя с точкой не превратится в «любой символ».

Разбор отдельно

text*string

Исходный текст.

mentionsstring[]

Имена для подсветки @упоминаний.

classNamestring

Дополнительные классы контейнера.

Грамматика доступна и без React — например, для оглавления или поиска:

tsx
import { parseBlocks, parseInline, parseInlineWithMentions } from "@toimetdev/pathlogs-core";

parseBlocks("# Заголовок\n\nтекст");
// [{ kind: "h", level: 1, text: "Заголовок" }, { kind: "p", lines: ["текст"] }]

parseInline("**жирный**");
// [{ kind: "strong", children: [{ kind: "text", text: "жирный" }] }]