Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 12 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ with the exception that 0.x versions can break between minor versions.
## [Unreleased]
### Added
- Allow customizing HTML attributes for alert title `<p>` tag via `AttributeProvider`
- New configuration for `AlertsExtension` to allow authors to provide custom
titles per alert. See the
[custom titles section of the alerts README](./commonmark-ext-gfm-alerts/README.md#custom-alert-titles)
for more information.
- New configuration for `AlertsExtension` to allow alerts to be nested within
other blocks (including other alerts). See
[this section of the alerts README](./commonmark-ext-gfm-alerts/README.md#nesting-alerts)
for more information.

## [0.28.0] - 2026-03-31
### Added
- New extension for alerts (aka callouts/admonitions)
- Syntax:
```
```markdown
> [!NOTE]
> The text of the note.
```
Expand Down Expand Up @@ -102,9 +110,9 @@ with the exception that 0.x versions can break between minor versions.
### Added
- New extension for footnotes!
- Syntax:
```
```markdown
Main text[^1]

[^1]: Additional text in a footnote
```
- Inline footnotes like `^[inline footnote]` are also supported when enabled
Expand Down Expand Up @@ -269,7 +277,7 @@ with the exception that 0.x versions can break between minor versions.
- Use class `ImageAttributesExtension` in artifact `commonmark-ext-image-attributes`
- Extension for task lists (GitHub-style), thanks @dohertyfjatl
- Syntax:
```
```markdown
- [x] task #1
- [ ] task #2
```
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,21 +337,22 @@ Use class `TablesExtension` in artifact `commonmark-ext-gfm-tables`.

Adds support for GitHub-style alerts (also known as callouts or admonitions) as described [here](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts), e.g.:

```
```markdown
> [!NOTE]
> The text of the note.
```

As types you can use NOTE, TIP, IMPORTANT, WARNING, CAUTION; or configure the extension to add additional ones.

Use class `AlertsExtension` in artifact `commonmark-ext-gfm-alerts`.
Use class `AlertsExtension` in artifact `commonmark-ext-gfm-alerts`. See the
[`AlertsExtension` README](./commonmark-ext-gfm-alerts/README.md) for more information.

### Footnotes

Enables footnotes like in [GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes)
or [Pandoc](https://pandoc.org/MANUAL.html#footnotes):

```
```markdown
Main text[^1]

[^1]: Additional text in a footnote
Expand All @@ -368,7 +369,7 @@ is based on the text of the heading.

`# Heading` will be rendered as:

```
```html
<h1 id="heading">Heading</h1>
```

Expand All @@ -389,7 +390,7 @@ Use class `InsExtension` in artifact `commonmark-ext-ins`.

Adds support for metadata through a YAML front matter block. This extension only supports a subset of YAML syntax. Here's an example of what's supported:

```
```markdown
---
key: value
list:
Expand All @@ -412,11 +413,11 @@ Adds support for specifying attributes (specifically height and width) for image

The attribute elements are given as `key=value` pairs inside curly braces `{ }` after the image node to which they apply,
for example:
```
```markdown
![text](/url.png){width=640 height=480}
```
will be rendered as:
```
```html
<img src="/url.png" alt="text" width="640" height="480" />
```

Expand All @@ -434,12 +435,12 @@ whitespace character or the letter `x` in lowercase or uppercase, then a right b
whitespace before any other content.

For example:
```
```markdown
- [ ] task #1
- [x] task #2
```
will be rendered as:
```
```html
<ul>
<li><input type="checkbox" disabled=""> task #1</li>
<li><input type="checkbox" disabled="" checked=""> task #2</li>
Expand Down
52 changes: 45 additions & 7 deletions commonmark-ext-gfm-alerts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Enables highlighting important information using blockquote syntax with five sta

## Usage

#### Markdown Syntax
### Markdown Syntax

```markdown
> [!NOTE]
Expand All @@ -16,15 +16,15 @@ Enables highlighting important information using blockquote syntax with five sta
> Critical information
```

#### Standard GFM Types
### Standard GFM Types

```java
var extension = AlertsExtension.create();
var parser = Parser.builder().extensions(List.of(extension)).build();
var renderer = HtmlRenderer.builder().extensions(List.of(extension)).build();
```

#### Custom Alert Types
### Custom Alert Types

Add custom types beyond the five standard GFM types:

Expand All @@ -36,7 +36,45 @@ var extension = AlertsExtension.builder()

Custom types must be UPPERCASE. Standard type titles can also be overridden for localization.

#### Styling
### Custom Alert Titles

Allow authors to provide custom titles per alert by adding text after the alert
marker on the same line:

```java
var extension = AlertsExtension.builder().allowCustomTitles().build();
```

```markdown
> [!NOTE] Keep in mind <!-- Overrides default title of "Note" -->
> Useful information

> [!WARNING] Be **very** careful <!-- Inline formatting is supported -->
> Critical information
```

### Nesting Alerts

By default, alerts cannot be nested within other blocks. Alerts within other
blocks are parsed as regular block quotes.

```markdown
<!-- Allowed -->
> [!NOTE]
> Useful information

<!-- Not allowed -->
- > [!NOTE]
> Useful information
```

This behavior can be changed to allow nested alerts:

```java
var extension = AlertsExtension.builder().allowNestedAlerts().build();
```

### Styling

Alerts render as `<div>` elements with CSS classes:

Expand All @@ -51,9 +89,9 @@ Basic CSS example:

```css
.markdown-alert {
padding: 0.5rem 1rem;
margin-bottom: 1rem;
border-left: 4px solid;
padding: 0.5rem 1rem;
margin-bottom: 1rem;
border-left: 4px solid;
}

.markdown-alert-note { border-color: #0969da; background-color: #ddf4ff; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

/**
* Alert block for highlighting important information using {@code [!TYPE]} syntax.
*
* @see AlertTitle
*/
public class Alert extends CustomBlock {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.commonmark.ext.gfm.alerts;

import org.commonmark.node.CustomNode;

/**
* Inline content container for the optional custom title of an {@link Alert}.
*
* <p>
* When present, an {@code AlertTitle} is always the first child of an {@link Alert}.
* Its own children are the parsed inline nodes of the title (i.e., the text after
* the {@code [!TYPE]} marker on the same line). For example, in
*
* <pre>{@code
* > [!NOTE] Custom _title_
* > Body text
* }</pre>
*
* the {@code AlertTitle} contains a {@code Text} node ({@code "Custom "}) followed
* by an {@code Emphasis} node wrapping {@code "title"}.
*
* @see AlertsExtension.Builder#allowCustomTitles()
* @see AlertsExtension.Builder#disallowCustomTitles()
*/
public class AlertTitle extends CustomNode {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.commonmark.ext.gfm.alerts;

import org.commonmark.Extension;
import org.commonmark.ext.gfm.alerts.internal.AlertPostProcessor;
import org.commonmark.ext.gfm.alerts.internal.AlertBlockParser;
import org.commonmark.ext.gfm.alerts.internal.AlertHtmlNodeRenderer;
import org.commonmark.ext.gfm.alerts.internal.AlertMarkdownNodeRenderer;
import org.commonmark.parser.Parser;
Expand All @@ -26,16 +26,31 @@
* ({@link org.commonmark.parser.Parser.Builder#extensions(Iterable)},
* {@link HtmlRenderer.Builder#extensions(Iterable)}).
* Parsed alerts become {@link Alert} blocks.
*
* The {@link #create() default configuration} of this extension will match GFM
* exactly, with the following exceptions:
*
* - Alert markers take precedence over link reference definitions.
* - Lazy continuation is not allowed between the marker and the body text. Example:
*
* <pre>{@code
* > [!NOTE]
* Lazy body text will be parsed as a new paragraph
* }</pre>
*/
public class AlertsExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension,
MarkdownRenderer.MarkdownRendererExtension {

static final Set<String> STANDARD_TYPES = Set.of("NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION");

private final Map<String, String> customTypes;
private final boolean customTitlesAllowed;
private final boolean nestedAlertsAllowed;

private AlertsExtension(Builder builder) {
this.customTypes = new HashMap<>(builder.customTypes);
this.customTitlesAllowed = builder.customTitlesAllowed;
this.nestedAlertsAllowed = builder.nestedAlertsAllowed;
}

public static Extension create() {
Expand All @@ -50,7 +65,8 @@ public static Builder builder() {
public void extend(Parser.Builder parserBuilder) {
var allowedTypes = new HashSet<>(STANDARD_TYPES);
allowedTypes.addAll(customTypes.keySet());
parserBuilder.postProcessor(new AlertPostProcessor(allowedTypes));
parserBuilder.customBlockParserFactory(
new AlertBlockParser.Factory(allowedTypes, customTitlesAllowed, nestedAlertsAllowed));
}

@Override
Expand Down Expand Up @@ -83,6 +99,8 @@ public Set<Character> getSpecialCharacters() {
*/
public static class Builder {
private final Map<String, String> customTypes = new HashMap<>();
private boolean customTitlesAllowed = false;
private boolean nestedAlertsAllowed = false;

/**
* Adds a custom alert type with a display title.
Expand All @@ -108,6 +126,47 @@ public Builder addCustomType(String type, String title) {
return this;
}

/**
* Allows custom titles on alerts. See {@link AlertTitle} for more information.
* @return {@code this}
*/
public Builder allowCustomTitles() {
customTitlesAllowed = true;
return this;
}

/**
* Disallows custom titles on alerts. See {@link AlertTitle} for more information.
* @return {@code this}
*/
public Builder disallowCustomTitles() {
customTitlesAllowed = false;
return this;
}

/**
* Allows alerts to be parsed within blocks other than {@code Document} (the root).
* <p>
* Note that even with this enabled, {@link Parser.Builder#maxOpenBlockParsers(int)}
* will be respected.
* @return {@code this}
*/
public Builder allowNestedAlerts() {
nestedAlertsAllowed = true;
return this;
}

/**
* Prevents alerts from being parsed within blocks other than {@code Document}
* (the root). If an alert appears within another block, it will be parsed as
* a regular {@code BlockQuote}.
* @return {@code this}
*/
public Builder disallowNestedAlerts() {
nestedAlertsAllowed = false;
return this;
}

/**
* @return a configured {@link Extension}
*/
Expand Down
Loading
Loading