Skip to content

feat: GitHub style callouts #2487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
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
Next Next commit
Add support for GitHub style callouts
  • Loading branch information
jhildenbiddle authored and sy-records committed Aug 11, 2024
commit f9950e0506c4d26368a9a100e631a3ea625749d3
78 changes: 56 additions & 22 deletions docs/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,106 @@ docsify extends Markdown syntax to make your documents more readable.

## Callouts

### Important content
Docsify supports [GitHub style](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) callouts (also known as "admonitions" or "alerts").

Important content like:
<!-- prettier-ignore -->
> [!CAUTION]
> **Caution** callouts communicate negative potential consequences of an action.

```markdown
!> **Time** is money, my friend!
```
<!-- prettier-ignore -->
> [!IMPORTANT]
> **Important** callouts communicate information necessary for users to succeed.

is rendered as:
<!-- prettier-ignore -->
> [!NOTE]
> **Note** callouts communicate information that users should take into account.

!> **Time** is money, my friend!
<!-- prettier-ignore -->
> [!TIP]
> **Tip** callouts communicate optional information to help a user be more successful.

### Tips
<!-- prettier-ignore -->
> [!WARNING]
> **Warning** callouts communicate potential risks user should be aware of.

General tips like:
**Markdown**

<!-- prettier-ignore -->
```markdown
?> _TODO_ unit test
> [!CAUTION]
> **Caution** callouts communicate negative potential consequences of an action.

> [!IMPORTANT]
> **Important** callouts communicate information necessary for users to succeed.

> [!NOTE]
> **Note** callouts communicate information that users should take into account.

> [!TIP]
> **Tip** callouts communicate optional information to help a user be more successful.

> [!WARNING]
> **Warning** callouts communicate potential risks user should be aware of.
```

are rendered as:
### Legacy Style ⚠️

?> _TODO_ unit test
The following Docsify v4 callout syntax has been deprecated and will be removed in a future version.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be great to add a console.warn when the respective piece of code runs, so people will know. We could also tell people to use major versions in deprecation messages, to start to get versionless people knowing what will be happening, so they can prepare. Wdyt?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be great to add a console.warn when the respective piece of code runs

Easy enough. Happy to add it. I may consolidate console deprecation warnings as well so they are easier to track and the output can be standardized.

We could also tell people to use major versions in deprecation messages, to start to get versionless people knowing what will be happening

This one may introduce some false positives simply because of the many different ways Docsify can be loaded, but I think a simple search for a <script> tag with a src value that matches the following criteria could work:

  • Source domain is different than route domain (i.e., markdown page domain)
  • Source domain contains a TLD (e.g. .com, .net, .org, etc.)
  • Source value matches \\docsify(\.min)?\.js
  • Source match is not preceded by a number (covers @version/ and /version/ CDN URLs)

This would match the following URLs and trigger the console warning:

The following URLs would be ignored:

FWIW, I may opt to implement this in a separate PR. Replying here for convenience. :)

Copy link
Member

@Koooooo-7 Koooooo-7 Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also tell people to use major versions in deprecation messages, to start to get versionless people knowing what will be happening

This one may introduce some false positives simply because of the many different ways Docsify can be loaded, but I think a simple search for a <script> tag with a src value that matches the following criteria could work...

As per the experience of I work the compatibility release, it seems too complicated for us to handle too much cases.

“There is an old saying: ‘Better to wrongfully kill a thousand than to let one escape.’”

If we can ensure that we notify as many people as possible who will be affected, notifying additional people who may not be impacted is acceptable.

So I suppose we could make the versionDetector simply like this:

Firstly, We check some common CDN providers (unpack, jsdelivr) import path format with versions.

Then, for other importing cases, we only try to find the docsify.min.js and the docsify.js content. No matter what the path folder /5/ nor the domain is, cos we don't know how users host docsify on which server, which place, which ip, which domain, which CDN...As long as it retrieves new docsify resources without versioning, it gets caught by the versionDetector.

When the site contains either resources name of them (docsify.min.js / docsify.js), we throw out a warning such as:

[docsify] versionless importing detect:
Use versionless may get the unpredictable breaking changes, please use the major versions or strict to one version instead.
More details: https://docsifyjs.org/docs/quickstart.md#specifying-docsify-versions

Conclusion:
If users import the resource online (host site on a domain or a raw IP are both okay ), they would get the warning.
If users deploy docsify internally or locally, they won't get a docsify warning, cos they won't get any docsify update also. version control doesn't matter for them. Once they manually upgrade docsify with versionDetector, they would get the warning then.


Besides, we provide a versionDetector config to manually suppress the warning and checking, when they don't care about any breaking changes or the resource import place is not a generic resources versioning control path (e.g. https://dontblameme.com/libs/update/20240401/docsify.min.js).

window.$docsify = {
  versionDetector: false
};

or am I too straightforward?


!> **Important** callouts communicate information necessary for users to succeed.

?> **Tip** callouts communicate optional information to help a user be more successful.

**Markdown**

```markdown
!> **Important** callouts communicate information necessary for users to succeed.

?> **Tip** callouts communicate optional information to help a user be more successful.
```

## Link attributes

### disabled

```md
```markdown
[link](/demo ':disabled')
```

### href

Sometimes we will use some other relative path for the link, and we have to tell docsify that we don't need to compile this link. For example:

```md
```markdown
[link](/demo/)
```

It will be compiled to `<a href="/#/demo/">link</a>` and will load `/demo/README.md`. Maybe you want to jump to `/demo/index.html`.

Now you can do that

```md
```markdown
[link](/demo/ ':ignore')
```

You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set the title for the link.

```md
```markdown
[link](/demo/ ':ignore title')

<a href="/demo/" title="title">link</a>
```

### target

```md
```markdown
[link](/demo ':target=_blank')
[link](/demo2 ':target=_self')
```

## Task lists

```md
```markdown
- [ ] foo
- bar
- [x] baz
Expand All @@ -91,19 +125,19 @@ You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set th

### Class names

```md
```markdown
![logo](https://docsify.js.org/_media/icon.svg ':class=someCssClass')
```

### IDs

```md
```markdown
![logo](https://docsify.js.org/_media/icon.svg ':id=someCssId')
```

### Sizes

```md
```markdown
![logo](https://docsify.js.org/_media/icon.svg ':size=WIDTHxHEIGHT')
![logo](https://docsify.js.org/_media/icon.svg ':size=50x100')
![logo](https://docsify.js.org/_media/icon.svg ':size=100')
Expand All @@ -119,7 +153,7 @@ You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set th

## Heading IDs

```md
```markdown
### Hello, world! :id=hello-world
```

Expand Down
34 changes: 34 additions & 0 deletions docs/ui-kit.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,40 @@

## Callouts

### Github Style

<!-- prettier-ignore -->
> [!CAUTION]
> **Caution** callouts communicate negative potential consequences of an action.

<!-- prettier-ignore -->
> [!IMPORTANT]
> **Important** callouts communicate information necessary for users to succeed.

<!-- prettier-ignore -->
> [!NOTE]
> **Note** callouts communicate information that users should take into account.

<!-- prettier-ignore -->
> [!TIP]
> **Tip** callouts communicate optional information to help a user be more successful.

<!-- prettier-ignore -->
> [!WARNING]
> **Warning** callouts communicate potential risks user should be aware of.

**Nested**

<!-- prettier-ignore -->
> [!CAUTION]
> **Caution** callouts communicate negative potential consequences of an action.
> > [!IMPORTANT]
> > **Important** callouts communicate information necessary for users to succeed.
> > > [!NOTE]
> > > **Note** callouts communicate information that users should take into account.

#### Legacy Docsify Style

!> **Important** callout with `inline code` and additional placeholder text used
to force the content to wrap and span multiple lines.

Expand Down
2 changes: 2 additions & 0 deletions src/core/render/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { imageCompiler } from './compiler/image.js';
import { highlightCodeCompiler } from './compiler/code.js';
import { paragraphCompiler } from './compiler/paragraph.js';
import { blockquoteCompiler } from './compiler/blockquote.js';
import { taskListCompiler } from './compiler/taskList.js';
import { taskListItemCompiler } from './compiler/taskListItem.js';
import { linkCompiler } from './compiler/link.js';
Expand Down Expand Up @@ -232,6 +233,7 @@ export class Compiler {
return `<h${depth} id="${slug}" tabindex="-1"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${depth}>`;
};

origin.blockquoteCompiler = blockquoteCompiler({ renderer });
origin.code = highlightCodeCompiler({ renderer });
origin.link = linkCompiler({
renderer,
Expand Down
36 changes: 36 additions & 0 deletions src/core/render/compiler/blockquote.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export const blockquoteCompiler = ({ renderer }) =>
(renderer.blockquote = function ({ tokens }) {
const calloutData =
tokens[0].type === 'paragraph' &&
// 0: Match "[!TIP] My Title"
// 1: Mark "[!TIP]"
// 2: Type "TIP"
tokens[0].raw.match(/^(\[!(\w+)\])/);
Copy link
Member

@Koooooo-7 Koooooo-7 Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tokens[0].raw.match(/^(\[!(\w+)\])/);
tokens[0].raw.match(/^(\[!(CAUTION|IMPORTANT|NOTE|TIP|WARNING)\])/);

I think we need strict match the callout tags in case of users use something like ![DUCK] in some reason.
And we have no callout class to handle it either.

Copy link
Member Author

@jhildenbiddle jhildenbiddle Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional. :)

Callouts (or "admonitions" or "alerts") are found in many other apps and the list of callouts supported is often much larger than what GitHub supports. By capturing the string contained with the brackets ([!STRING]) and using it as a separate styling-only class (<div class="callout string">) it simplified adding new callouts for third parties.

For example:

> [!BUG]
> This is a bug callout
.callout.bug {
  --callout-bg: orange;
}

Copy link
Member

@Koooooo-7 Koooooo-7 Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. so it means the ![words] is a callout syntax hook (I think I can call it as an reserved word/block) in docsify and users could do the extension.Meanwhile, it will disable to allow user uses a ![not-callout] as a plain start of blockquote.

> [!question] Should we mention it in `UI-KIT`?
> > [!advantage] users can understand there is a way to do extension for callout block (so am I xD ).
> > > [!example]  or we could also add an example.

Copy link
Member Author

@jhildenbiddle jhildenbiddle Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct.

The .callout class handles all of the base styling. The additional class on the callout element (.caution, .important, .note, etc.) is used to adjust styles like colors and icons. To extend the system to support the example you provided above they would simply need to create CSS declarations for each:

callout.question {
  --callout-bg: red;
  --callout-color: white;
}

I can add a note about custom classes to the docs along with a note explaining that in most cases custom classes will not work outside of Docsify.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a note about custom classes to the docs along with a note explaining that in most cases custom classes will not work outside of Docsify.

Hi, @jhildenbiddle Do you still need to modify the document? If not, then you can merge this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sy-records Yep. Haven't had a chance to complete the updated docs, but I'll have that in the next or two.

Copy link
Member Author

@jhildenbiddle jhildenbiddle Aug 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Koooooo-7 @sy-records --

One other enhancements I'd like to make is the ability to define localized labels. I make a conscious decision to not implement this initially for a few reasons, but after thinking it over for a bit I've decided it makes sense to offer this feature.

The implementation will follow the same pattern we've used elsewhere in our configuration. The default behavior will be to not render text labels (as shown in screenshots above):

window.$docsify = {
  // Default: do not render text labels
  callouts: {},
};

If users want to render text labels automatically as is done in markdown environments like GitHub and Obsidian, they can define them as part of their configuration:

window.$docsify = {
  // Render same label for all route paths
  callouts: {
    caution: 'Caution',
    important: 'Important',
    note: 'Note',
    tip: 'Tip',
    warning: 'Warning',
  },
};
window.$docsify = {
  // Render localized labels based on route paths
  callouts: {
    caution: {
      '/es/': 'Precaución',
      '/de-de/': 'Vorsicht',
      '/ru-ru/': 'Осторожность',
      '/zh-cn/': '警告',
      '/': 'Caution',
    },
    // ...
  },
};

Copy link
Member

@Koooooo-7 Koooooo-7 Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

----- @jhildenbiddle , Currently, we simply add a icon per callout config. That looks good to me.
TIP


I don't propose that we need introduce a label into docsify with the i18n support either:

  • We need extract the content from the [!HERE] and match it to the $.callouts config by path and by content.
    i.e. if user has the ![example], they absolutely want to config it like this:
window.$docsify = {
  // Render same label for all route paths
  callouts: {
    example: {
      '/zh-cn/': '例子',
      '/': 'Example',
    },
  },

It makes the config more complex and messy, they need config options size is: i18n-paths * callouts.


IMO, the icon already has the info, we don't need mention it specially with a label.
In a common sense, a light lamp is tips, a i is info/note, a red thing is forbidden/attention, a yellow triangle is warning. user can simply use it as it default meanings.

The only different thing is we introduce some icon more docsify style (such as important is a cute star.). thats fine since we don't fu*k any things up.

If user do wanna enrich the label in their multi lang sites for each icon, they can simply add something like this:

  • > [!IMPORTANT] **Important** in EN site.
  • > [!IMPORTANT] **重要** in ZH site.

Besides.
We don't need stuck in the [!IMPORTANT] is an IMPORTANT icon.
User can treat the [!SOMEKEY] just a tag or a snytax that docsify would provide some callout icons.

Which means, user could just know something like:

If you config the [!TIP], docsify will give a lamp.

Then, they can append all the extra info for the icon they want or fully "rename" them.
No matter user is an EN speaker or not and how they categorize the icons in their site with their customized icon and docsify builtin icons.

> [!IMPORTANT] **Bonus Time** When you see the Star icon, it means we brings you bonus surprise now!

bunus


let openTag = '<blockquote>';
let closeTag = '</blockquote>';

if (calloutData) {
const calloutMark = calloutData[1]; // "[!TIP]"
const calloutType = calloutData[2].toLowerCase(); // "tip"
const token = tokens[0].tokens[0];

// Remove callout mark from tokens
['raw', 'text'].forEach(key => {
token[key] = token[key].replace(calloutMark, '').trimStart();
});

// Remove empty paragraph
if (tokens.length > 1 && !token.raw.trim()) {
tokens = tokens.slice(1);
}

openTag = `<div class="callout ${calloutType}">`;
closeTag = `</div>`;
}

const body = this.parser.parse(tokens);
const html = `${openTag}${body}${closeTag}`;

return html;
});
3 changes: 3 additions & 0 deletions src/core/render/tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ export function tree(
return tpl.replace('{inner}', innerHTML);
}

/**
* @deprecated
*/
export function helper(className, content) {
return /* html */ `<p class="${className}">${content.slice(5).trim()}</p>`;
}
Expand Down
37 changes: 37 additions & 0 deletions src/themes/addons/core-dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,46 @@
color-scheme: dark;
}

/* Cover */
/* ========================================================================== */
.cover-main {
a.button.secondary {
color: var(--color-text);
border-color: rgba(255, 255, 255, 0.5);
}
}

/* Markdown */
/* ========================================================================== */
.markdown-section {
.callout {
&[class] {
--callout-bg: unset;
}

&.caution {
--callout-border-color: #991b1b; /* Tailwind: red 800 */
}

&.important {
--callout-border-color: #5b21b6; /* Tailwind: violet 800 */
}

&.note {
--callout-border-color: var(--theme-color-4);
}

&.tip {
--callout-border-color: #115e59; /* Tailwind: teal 800 */
}

&.warning {
--callout-border-color: #a16207; /* Tailwind: yellow 700 */
}

code,
pre:where([data-lang]) {
background: var(--color-mono-min);
}
}
}
20 changes: 18 additions & 2 deletions src/themes/shared/_markdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,29 @@
text-align: center;
}

> :first-child {
margin-top: 0;
}

> :last-child {
margin-bottom: 0;
}

code,
strong {
color: inherit;
}

code {
background: rgba(0, 0, 0, 0.08);
background: rgba(0, 0, 0, 0.05);
}

pre:where([data-lang]) {
background: rgba(255, 255, 255, 0.4);
}

.callout {
margin-block: var(--margin-block);
}
}

Expand Down Expand Up @@ -226,7 +242,7 @@
padding: 0 !important;
padding-block: 1.5rem !important;
padding-inline: 1.5rem !important;
background: inherit;
background: transparent;
color: inherit;
font-size: inherit;
white-space: inherit;
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy