diff --git a/app/styles/crate/version.module.css b/app/styles/crate/version.module.css index e88e9bf9451..1daa2bd556d 100644 --- a/app/styles/crate/version.module.css +++ b/app/styles/crate/version.module.css @@ -152,3 +152,105 @@ border: 0; } } + +/* alerts */ +:global(.markdown-alert) { + --fg-color-note: #4494f8; + --fg-color-tip: #3fb950; + --fg-color-important: #ab7df8; + --fg-color-warning: #d29922; + --fg-color-caution: #f85149; + + padding: 0.5rem 1rem; + margin-bottom: 1rem; + color: inherit; + border-left: .25em solid var(--gray-border); + + & > :first-child { + margin-top: 0; + } + + & > :last-child { + margin-bottom: 0; + } + + :global(.markdown-alert-title) { + display: flex; + font-weight: 500; + align-items: center; + line-height: 1; + } + + & > :global(.markdown-alert-title)::before { + content: ''; + margin-right: .5rem; + background-color: var(--gray-border); + width: 1em; + height: 1em; + } + + &:global(.markdown-alert-note) { + border-left-color: var(--fg-color-note); + + & > :global(.markdown-alert-title) { + color: var(--fg-color-note); + + &:before { + mask: url("/assets/alert-note.svg"); + background-color: var(--fg-color-note); + } + } + } + + &:global(.markdown-alert-tip) { + border-left-color: var(--fg-color-tip); + + & > :global(.markdown-alert-title) { + color: var(--fg-color-tip); + + &:before { + mask: url("/assets/alert-tip.svg"); + background-color: var(--fg-color-tip); + } + } + } + + &:global(.markdown-alert-important) { + border-left-color: var(--fg-color-important); + + & > :global(.markdown-alert-title) { + color: var(--fg-color-important); + + &:before { + mask: url("/assets/alert-important.svg"); + background-color: var(--fg-color-important); + } + } + } + + &:global(.markdown-alert-warning) { + border-left-color: var(--fg-color-warning); + + & > :global(.markdown-alert-title) { + color: var(--fg-color-warning); + + &:before { + mask: url("/assets/alert-warning.svg"); + background-color: var(--fg-color-warning); + } + } + } + + &:global(.markdown-alert-caution) { + border-left-color: var(--fg-color-caution); + + & > :global(.markdown-alert-title) { + color: var(--fg-color-caution); + + &:before { + mask: url("/assets/alert-caution.svg"); + background-color: var(--fg-color-caution); + } + } + } +} diff --git a/crates/crates_io_markdown/lib.rs b/crates/crates_io_markdown/lib.rs index 6fae87d6c9a..d609b2a8020 100644 --- a/crates/crates_io_markdown/lib.rs +++ b/crates/crates_io_markdown/lib.rs @@ -46,6 +46,18 @@ impl<'a> MarkdownRenderer<'a> { ]), ), ("section", hashset(&["footnotes"])), + ( + "div", + hashset(&[ + "markdown-alert", + "markdown-alert-note", + "markdown-alert-tip", + "markdown-alert-important", + "markdown-alert-warning", + "markdown-alert-caution", + ]), + ), + ("p", hashset(&["markdown-alert-title"])), ]); let sanitize_url = UrlRelative::Custom(Box::new(SanitizeUrl::new(base_url, base_dir))); @@ -77,7 +89,9 @@ impl<'a> MarkdownRenderer<'a> { .build(); let extension_options = ComrakExtensionOptions::builder() + .alerts(true) .autolink(true) + .multiline_block_quotes(true) .strikethrough(true) .table(true) .tagfilter(true) @@ -429,7 +443,7 @@ mod tests { #[test] fn text_with_forbidden_class_attribute() { let text = "

Hello World!

"; - assert_snapshot!(markdown_to_html(text, None, ""), @"

Hello World!

"); + assert_snapshot!(markdown_to_html(text, None, ""), @r#"

Hello World!

"#); } #[test] @@ -684,4 +698,36 @@ There can also be some text in between! "##); } + + #[test] + fn alerts_note() { + let text = r#" +> [!note] +> Hello, world! + "#; + assert_snapshot!(markdown_to_html(text, None, ""), @r#" +
+

Note

+

Hello, world!

+
+ "#); + } + + #[test] + fn alerts_note_multiline_block_quotes() { + let text = r#" +>>> [!note] +Hello, + +world! +>>> +"#; + assert_snapshot!(markdown_to_html(text, None, ""), @r#" +
+

Note

+

Hello,

+

world!

+
+ "#); + } } diff --git a/e2e/acceptance/readme-rendering.spec.ts b/e2e/acceptance/readme-rendering.spec.ts index 40be046ed0f..80ecf826b21 100644 --- a/e2e/acceptance/readme-rendering.spec.ts +++ b/e2e/acceptance/readme-rendering.spec.ts @@ -2,6 +2,39 @@ import { expect, test } from '@/e2e/helper'; import { http, HttpResponse } from 'msw'; const README_HTML = ` +
+

Note

+

Useful information that users should know, even when skimming content.

+
+
+

Tip

+

Helpful advice for doing things better or more easily.

+
+
+

Important

+

Key information users need to know to achieve their goal.

+
+
+

Warning

+

Urgent info that needs immediate user attention to avoid problems.

+
+
+

Caution

+

Advises about risks or negative outcomes of certain actions.

+
+ +
+

Note

+
+

Important

+
+

Caution

+

Rick roll

+

Never gonna give you up

+
+
+
+

Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.


You may be looking for:

diff --git a/public/assets/alert-caution.svg b/public/assets/alert-caution.svg new file mode 100644 index 00000000000..30336823494 --- /dev/null +++ b/public/assets/alert-caution.svg @@ -0,0 +1,3 @@ + diff --git a/public/assets/alert-important.svg b/public/assets/alert-important.svg new file mode 100644 index 00000000000..bbb6be3a8a8 --- /dev/null +++ b/public/assets/alert-important.svg @@ -0,0 +1,3 @@ + diff --git a/public/assets/alert-note.svg b/public/assets/alert-note.svg new file mode 100644 index 00000000000..1be6dc64570 --- /dev/null +++ b/public/assets/alert-note.svg @@ -0,0 +1,3 @@ + diff --git a/public/assets/alert-tip.svg b/public/assets/alert-tip.svg new file mode 100644 index 00000000000..9c381a06dee --- /dev/null +++ b/public/assets/alert-tip.svg @@ -0,0 +1,3 @@ + diff --git a/public/assets/alert-warning.svg b/public/assets/alert-warning.svg new file mode 100644 index 00000000000..176711f243d --- /dev/null +++ b/public/assets/alert-warning.svg @@ -0,0 +1,3 @@ + diff --git a/tests/acceptance/readme-rendering-test.js b/tests/acceptance/readme-rendering-test.js index a8d9de3453f..b39bc1182ee 100644 --- a/tests/acceptance/readme-rendering-test.js +++ b/tests/acceptance/readme-rendering-test.js @@ -9,6 +9,39 @@ import { setupApplicationTest } from 'crates-io/tests/helpers'; import { visit } from '../helpers/visit-ignoring-abort'; const README_HTML = ` +
+

Note

+

Useful information that users should know, even when skimming content.

+
+
+

Tip

+

Helpful advice for doing things better or more easily.

+
+
+

Important

+

Key information users need to know to achieve their goal.

+
+
+

Warning

+

Urgent info that needs immediate user attention to avoid problems.

+
+
+

Caution

+

Advises about risks or negative outcomes of certain actions.

+
+ +
+

Note

+
+

Important

+
+

Caution

+

Rick roll

+

Never gonna give you up

+
+
+
+

Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.


You may be looking for: