diff --git a/django/poetry.lock b/django/poetry.lock index 4d2cdd0ef..1176846c5 100644 --- a/django/poetry.lock +++ b/django/poetry.lock @@ -1714,6 +1714,26 @@ files = [ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +description = "Collection of plugins for markdown-it-py" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, + {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "mdurl" version = "0.1.1" @@ -3217,4 +3237,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "37aed7fc870cc58107f5ad71a680d4d2e2172fbb1dd4fd8c4470f21f505efe3e" +content-hash = "654b8166f7ad5d3559fd4cc65422d2238c7132fc1eef8a8e0616e1fe1381935d" diff --git a/django/pyproject.toml b/django/pyproject.toml index f3aa00a1b..b07cface3 100644 --- a/django/pyproject.toml +++ b/django/pyproject.toml @@ -32,6 +32,7 @@ django-cors-headers = "^3.7.0" boto3 = "^1.17.47" bleach = "^3.3.0" markdown-it-py = {version = "2.1.0", extras = ["linkify"]} +mdit-py-plugins = "0.4.2" abyss = {git = "https://github.com/akx/abyss.git", rev = "4352e557f25a303f718ec1bf82ea0ca545ebc077"} lxml = "^4.9.1" pygments = "^2.16.1" diff --git a/django/thunderstore/api/cyberstorm/tests/test_markdown.py b/django/thunderstore/api/cyberstorm/tests/test_markdown.py index 04435ca34..a124bd13e 100644 --- a/django/thunderstore/api/cyberstorm/tests/test_markdown.py +++ b/django/thunderstore/api/cyberstorm/tests/test_markdown.py @@ -67,8 +67,8 @@ def test_readme_api_view__prerenders_markup(api_client: APIClient) -> None: f"/api/cyberstorm/package/{v.package.namespace}/{v.package.name}/latest/readme/", ) actual = response.json() - - assert actual["html"] == "
this is a description
""" diff --git a/django/thunderstore/frontend/templates/base.html b/django/thunderstore/frontend/templates/base.html index 3792b3563..dd3150c99 100644 --- a/django/thunderstore/frontend/templates/base.html +++ b/django/thunderstore/frontend/templates/base.html @@ -29,6 +29,50 @@ {# CSS and JS #} + + {# Anchor navigation JS #} + {% if community.background_image %} diff --git a/django/thunderstore/markdown/allowed_tags.py b/django/thunderstore/markdown/allowed_tags.py index dd1d113ea..c03b6fa2b 100644 --- a/django/thunderstore/markdown/allowed_tags.py +++ b/django/thunderstore/markdown/allowed_tags.py @@ -79,12 +79,12 @@ "img": ["src", "width", "height", "alt", "align"], "th": ["align", "rowspan", "colspan"], "td": ["align", "rowspan", "colspan"], - "h1": ["align"], - "h2": ["align"], - "h3": ["align"], - "h4": ["align"], - "h5": ["align"], - "h6": ["align"], + "h1": ["align", "id"], + "h2": ["align", "id"], + "h3": ["align", "id"], + "h4": ["align", "id"], + "h5": ["align", "id"], + "h6": ["align", "id"], "p": ["align"], "details": ["open"], } diff --git a/django/thunderstore/markdown/templatetags/markdownify.py b/django/thunderstore/markdown/templatetags/markdownify.py index ba061347e..0e6b4b6ac 100644 --- a/django/thunderstore/markdown/templatetags/markdownify.py +++ b/django/thunderstore/markdown/templatetags/markdownify.py @@ -3,6 +3,7 @@ from django.template.defaultfilters import stringfilter from django.utils.safestring import mark_safe from markdown_it import MarkdownIt +from mdit_py_plugins.anchors import anchors_plugin from thunderstore.markdown.allowed_tags import ( ALLOWED_ATTRIBUTES, @@ -10,8 +11,17 @@ ALLOWED_TAGS, ) + +def slugger(text: str) -> str: + import re + + slug = re.sub(r"\s+", "-", text) + slug = re.sub(r"[^\w\-]", "", slug) + return f"user-content-{slug.lower()}" + + register = template.Library() -md = MarkdownIt("gfm-like") +md = MarkdownIt("gfm-like").use(anchors_plugin, slug_func=slugger, max_level=6) def render_markdown(value: str):