Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions compendium/compendium/page/docs/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,41 @@
margin-bottom: 0;
}

.docs-reading-pane .docs-alert-title {
display: flex;
align-items: center;
gap: 0.4em;
margin: 0 0 0.4em;
font-weight: 600;
line-height: 1.2;
color: var(--docs-alert-color, var(--text-color));
}

.docs-reading-pane blockquote.docs-alert-note {
--docs-alert-color: var(--alert-text-info);
border-left-color: var(--docs-alert-color);
}

.docs-reading-pane blockquote.docs-alert-tip {
--docs-alert-color: var(--alert-text-success);
border-left-color: var(--docs-alert-color);
}

.docs-reading-pane blockquote.docs-alert-important {
--docs-alert-color: var(--purple-500);
border-left-color: var(--docs-alert-color);
}

.docs-reading-pane blockquote.docs-alert-warning {
--docs-alert-color: var(--alert-text-warning);
border-left-color: var(--docs-alert-color);
}

.docs-reading-pane blockquote.docs-alert-caution {
--docs-alert-color: var(--alert-text-danger);
border-left-color: var(--docs-alert-color);
}

.docs-state {
display: flex;
align-items: center;
Expand Down
71 changes: 70 additions & 1 deletion compendium/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
DEFAULT_ROLE = "Desk User"
ALLOWED_FRONTMATTER_KEYS = ("title", "order", "roles")
IMAGE_SRC_PATTERN = re.compile(r'(<img[^>]+src=["\'])([^"\']+)(["\'])', re.IGNORECASE)
GITHUB_ALERT_MARKER = re.compile(r"^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*", re.IGNORECASE)
GITHUB_ALERTS = {
"NOTE": ("Note", "info"),
"TIP": ("Tip", "light-bulb"),
"IMPORTANT": ("Important", "megaphone"),
"WARNING": ("Warning", "alert"),
"CAUTION": ("Caution", "stop"),
}


@frappe.whitelist()
Expand Down Expand Up @@ -796,10 +804,71 @@ def sort_tree_nodes(nodes):

def render_page_content(body, page_path="", locale=None):
html = frappe.utils.md_to_html(body or "")
content = sanitize_html(str(html), linkify=True)
content = apply_github_alerts(str(html))
content = sanitize_html(content, linkify=True)
return rewrite_asset_urls(content, page_path, locale)


def apply_github_alerts(html):
"""Turn GitHub alert markers into a titled, typed blockquote."""
from bs4 import BeautifulSoup, NavigableString

if not html or "[!" not in html:
return html

soup = BeautifulSoup(html, "html.parser")
for blockquote in soup.find_all("blockquote"):
paragraph = next((child for child in blockquote.children if child.name), None)
if not paragraph or paragraph.name != "p":
continue

text_node = next(
(
child
for child in paragraph.children
if isinstance(child, NavigableString) and str(child).strip()
),
None,
)
if text_node is None:
continue

Comment thread
greptile-apps[bot] marked this conversation as resolved.
leading = str(text_node).lstrip()
match = GITHUB_ALERT_MARKER.match(leading)
if not match:
continue

alert_type = match.group(1).upper()
remainder = leading[match.end() :].lstrip()
blockquote["class"] = [
*(blockquote.get("class") or []),
"docs-alert",
f"docs-alert-{alert_type.lower()}",
]
title = build_alert_title(soup, alert_type)

if remainder:
text_node.replace_with(remainder)
paragraph.insert_before(title)
else:
text_node.extract()
if paragraph.get_text(strip=True) or paragraph.find(True):
paragraph.insert_before(title)
else:
paragraph.replace_with(title)

return str(soup)


def build_alert_title(soup, alert_type):
label, icon = GITHUB_ALERTS[alert_type]
title = soup.new_tag("p", attrs={"class": "docs-alert-title"})
icon_tag = soup.new_tag("i", attrs={"class": f"octicon octicon-{icon}"})
title.append(icon_tag)
title.append(label)
return title


def rewrite_asset_urls(html, page_path, locale=None):
def replace(match):
prefix, src, suffix = match.groups()
Expand Down
28 changes: 28 additions & 0 deletions compendium/docs/de/compendium/docs/authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,34 @@ def hello():
print("Hello")
```

## Alerts

Die GitHub-Alert-Syntax macht aus einem Blockzitat einen Hinweis. Die Zeile
`[!TYPE]` wird durch ein Symbol und eine fette Überschrift ersetzt. Fünf Typen
stehen zur Verfügung:

> [!NOTE]
> Zusatzinformation, die auch beim Überfliegen nützlich ist.

> [!TIP]
> Ein Rat, der eine Aufgabe leichter macht.

> [!IMPORTANT]
> Information, die der Leser braucht, um sein Ziel zu erreichen.

> [!WARNING]
> Dringende Information. Handeln Sie, um ein Problem zu vermeiden.

> [!CAUTION]
> Ein Risiko oder eine negative Folge einer Handlung.

Schreiben Sie sie als Blockzitat, dessen erste Zeile der Typmarker ist:

```md
> [!NOTE]
> Zusatzinformation, die auch beim Überfliegen nützlich ist.
```

## Beispielseite

```md
Expand Down
27 changes: 27 additions & 0 deletions compendium/docs/en/compendium/docs/authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,33 @@ def hello():
print("Hello")
```

## Alerts

GitHub alert syntax turns a blockquote into a callout. The `[!TYPE]` line
becomes an icon and a bold heading. Five types are available:

> [!NOTE]
> Extra information that is useful even when skimming.

> [!TIP]
> Advice that makes a task easier.

> [!IMPORTANT]
> Information the reader needs to reach their goal.

> [!WARNING]
> Urgent information. Act on it to avoid a problem.

> [!CAUTION]
> A risk or a negative outcome of an action.

Write them as a blockquote whose first line is the type marker:

```md
> [!NOTE]
> Extra information that is useful even when skimming.
```

## Example page

```md
Expand Down
47 changes: 47 additions & 0 deletions compendium/tests/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,53 @@ def test_mermaid_fence_keeps_language_class(self):
self.assertIn("language-mermaid", html)
self.assertIn("flowchart LR", html)

def test_github_alert_replaces_marker_with_title(self):
html = render_page_content("> [!NOTE]\n> Useful information")
self.assertIn("docs-alert-note", html)
self.assertIn("octicon-info", html)
self.assertIn("Note", html)
self.assertIn("Useful information", html)
self.assertNotIn("[!NOTE]", html)

def test_github_alert_types(self):
cases = {
"TIP": ("docs-alert-tip", "octicon-light-bulb", "Tip"),
"IMPORTANT": ("docs-alert-important", "octicon-megaphone", "Important"),
"WARNING": ("docs-alert-warning", "octicon-alert", "Warning"),
"CAUTION": ("docs-alert-caution", "octicon-stop", "Caution"),
}
for marker, (css_class, icon, heading) in cases.items():
html = render_page_content(f"> [!{marker}]\n> Body")
self.assertIn(css_class, html)
self.assertIn(icon, html)
self.assertIn(heading, html)
self.assertIn("Body", html)
self.assertNotIn(f"[!{marker}]", html)

def test_github_alert_with_following_list(self):
html = render_page_content("> [!NOTE]\n>\n> - first\n> - second")
self.assertIn("docs-alert-note", html)
self.assertIn("octicon-info", html)
self.assertNotIn("[!NOTE]", html)
self.assertIn("<li>", html)

def test_unknown_alert_marker_stays_blockquote(self):
html = render_page_content("> [!UNKNOWN]\n> Body")
self.assertNotIn("docs-alert", html)
self.assertIn("[!UNKNOWN]", html)
self.assertIn("Body", html)

def test_plain_blockquote_is_unchanged(self):
html = render_page_content("> just a quote")
self.assertNotIn("docs-alert", html)
self.assertNotIn("octicon", html)
self.assertIn("just a quote", html)

def test_github_alert_type_is_case_insensitive(self):
html = render_page_content("> [!note]\n> Useful")
self.assertIn("docs-alert-note", html)
self.assertNotIn("[!note]", html)

def test_protected_assets_require_page_access(self):
with self.docs_environment(
{
Expand Down
Loading