Уценка

Легкий и свободный от зависимостей способ отрисовки уценки в Reflex с использованием Tailwind Typography.

Большинство библиотек компонентов, включая Radix, поставляют компонент Markdown, который анализирует и отображает уценку на клиенте, в браузере, при каждом рендеринге. Это разумный вариант по умолчанию, но это также означает доставку анализатора клиенту и сопоставление каждого токена уценки с компонентом.

На этой странице показан другой шаблон: один раз проанализировать уценку в HTML на сервере с помощью библиотеки Python markdown, а затем передать необработанный HTML в rx.html с прикрепленным классом Tailwind Typography. Здесь нет синтаксического анализатора на стороне клиента и сопоставления компонентов с токенами, только HTML и CSS.

Это не компонент с API, который нужно изучить. Это небольшая служебная функция плюс класс CSS, которые вы должны скопировать и адаптировать.

Как это работает

Три части работают вместе:

  1. markdown — это библиотека Python, которая преобразует строку уценки в строку HTML.
  2. Класс prose, построенный на @tailwindcss/typography, стилизует необработанные HTML-элементы (h1, p, table, blockquote и т. д.) в соответствии с вашим пользовательским интерфейсом.
  3. rx.html отображает необработанную HTML-строку как компонент с примененным классом prose.
def render_markdown(text: str) -> rx.Component:
    if not text.strip():
        return rx.el.div()

    html = markdown.markdown(text, extensions=["fenced_code", "tables"])
    return rx.html(html, class_name="prose-content")

Вот и весь шаблон. Все, что происходит после этого момента, включая то, какие расширения включить, как стилизовать класс и вообще выполнять ли постобработку HTML, не является обязательным и зависит от вашего проекта.

Настройка

Установить уценку

pip install markdown

Расширения, о которых стоит знать:

Расширение Цель
fenced_code Включает блоки кода с тройной обратной кавычкой
tables Включает таблицы каналов в стиле GitHub
toc Добавляет атрибуты id к заголовкам, полезные для якорных ссылок

Полный список в Python-Markdown extension docs.

Добавить типографику попутного ветра

Поскольку @tailwindcss/typography обрабатывается как файл JavaScript, его следует добавлять непосредственно в плагины rxconfig.py.

import reflex as rx
from reflex.plugins.shared_tailwind import TailwindConfig

config = rx.Config(
    ...,
    plugins=[
        rx.plugins.TailwindV4Plugin(
            config=TailwindConfig(plugins=["@tailwindcss/typography"])
        ),
    ],
)

Написать урок прозы

Класс prose из Typography не имеет стиля, но имеет собственную структуру; большинство проектов добавляют класс поверх него, чтобы добавить свою собственную тему. Ниже приведен один из примеров. Считайте имя класса, цвета и интервалы заполнителями, а не требованиями.

Два правила стоит соблюдать независимо от темы, поскольку они исправляют особенности рендеринга, а не выражают мнение о стиле:

  • Сброс содержимого ::before/::after на code и blockquote. Типографика по умолчанию добавляет декоративные кавычки, чего не хотят большинство нередакционных пользовательских интерфейсов.
  • Сброс отступов и фона pre > code. Без него вы получите двойной фон, поскольку как pre, так и code внутри него стилизованы.
@layer components {
  /* name and theme this however fits your project */
  .prose-content {
    @apply prose dark:prose-invert max-w-none w-full;

    /* fixes double-styling on code blocks */
    @apply prose-pre:p-0
            prose-pre:overflow-x-auto
            prose-pre:before:content-none
            prose-pre:after:content-none
            prose-code:before:content-none
            prose-code:after:content-none;

    /* everything below is a styling choice, not a requirement */
    @apply prose-code:rounded-md
            prose-code:bg-muted
            prose-code:px-1.5
            prose-code:py-0.5
            prose-code:text-sm
            prose-pre:rounded-lg
            prose-pre:bg-muted
            prose-pre:border;
  }
}

Собери это вместе

from components.markdown import render_markdown

def page() -> rx.Component:
    return render_markdown("""
# Welcome

Some **markdown** content, rendered straight to HTML.
""")

Как выглядят элементы уценки

Все, что приведено ниже, представляет собой реальную уценку, прошедшую через render_markdown и оформленную с использованием собственного класса прозы этого сайта. Это один из возможных вариантов, а не единственный.

Заголовки

Типографские стили h1h6 прямо из коробки. При необходимости переопределите модификаторы prose-h1:* / prose-h2:*.

# Page title

## Section heading

Форматирование текста

Маркеры кода, выделенные жирным шрифтом, курсивом и строчные, сопоставляются со своими HTML-эквивалентами.

Вы можете использовать жирный текст, курсив или inline code внутри обычного абзаца.

You can use **bold text**, _italic text_, or `inline code`.

Списки

  • Маркеры списка можно изменить с помощью prose-li:marker:*.
  • Упорядоченные и неупорядоченные списки наследуют стандартный типографский интервал.
  • Вложенные списки работают «из коробки»
- First item
- Second item
  - Nested item

Ссылки

Links наследует стандартную обработку подчеркивания при наведении в Typography. Сделайте рестайлинг с помощью prose-a:*.

Блоки кода

Блоки изолированного кода происходят из расширения fenced_code. Типографика объединяет их в пару pre > code, а исправление двойного стиля из приведенного выше CSS сохраняет эту пару чистой.

def hello(name: str) -> str:
    return f"Hello, {name}!"

Блоковые кавычки

Цитата по умолчанию в Typography представляет собой курсив по левой границе. Некоторые проекты предпочитают вместо этого выноску с рамкой. Оба разумны; измените стиль с помощью prose-blockquote:*.

Блоковая цитата, оформленная в соответствии с вашим содержанием.

> A blockquote, styled however fits your content.

Необязательно: обработка переполнения таблицы

Таблицы Markdown отображаются как простые элементы <table>, которые могут переполнять свой контейнер на узких экранах или при длинном контенте. Это не характерно для данного подхода к рендерингу; это общая проблема с таблицами HTML. Но это достаточно распространено в таблицах с уценкой, поэтому на это стоит обратить внимание.

Один из способов справиться с этим — выполнить постобработку сгенерированного HTML и обернуть каждый <table> в прокручиваемый контейнер до того, как он достигнет rx.html. Это один из нескольких вариантов. С таким же успехом вы можете воспользоваться утилитой overflow-x-auto для родительского элемента, запросом контейнера CSS или оставить таблицы развернутыми, если в вашем контенте никогда не бывает широких таблиц.

from bs4 import BeautifulSoup

def wrap_tables(html: str) -> str:
    soup = BeautifulSoup(html, "html.parser")

    for table in soup.find_all("table"):
        wrapper = soup.new_tag("div")
        wrapper["class"] = "w-full overflow-x-auto"
        table.wrap(wrapper)

    return str(soup)
| Column A | Column B |
| -------- | -------- |
| Value 1  | Value 2  |

Если вы пойдете по этому пути, вам потребуется beautifulsoup4:

pip install beautifulsoup4
def render_markdown(text: str) -> rx.Component:
    if not text.strip():
        return rx.el.div()

    html = markdown.markdown(text, extensions=["fenced_code", "tables"])
    html = wrap_tables(html)  # optional
    return rx.html(html, class_name="prose-content")