Change the way admonitions work
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Michael Zhang 2024-06-29 15:15:48 -05:00
parent bbfe4f67ca
commit 06d483b3dd
14 changed files with 94 additions and 83 deletions

View file

@ -5,7 +5,7 @@ import { rehypeAccessibleEmojis } from "rehype-accessible-emojis";
import remarkReadingTime from "./plugin/remark-reading-time"; import remarkReadingTime from "./plugin/remark-reading-time";
import remarkEmoji from "remark-emoji"; import remarkEmoji from "remark-emoji";
import remarkDescription from "astro-remark-description"; import remarkDescription from "astro-remark-description";
import remarkAdmonitions from "./plugin/remark-admonitions"; import remarkAdmonitions, { mkdocsConfig } from "./plugin/remark-admonitions";
import remarkMath from "remark-math"; import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex"; import rehypeKatex from "rehype-katex";
import remarkTypst from "./plugin/remark-typst"; import remarkTypst from "./plugin/remark-typst";
@ -35,7 +35,7 @@ export default defineConfig({
remarkPlugins: [ remarkPlugins: [
() => remarkAgda({ outDir, base, publicDir }), () => remarkAgda({ outDir, base, publicDir }),
remarkMath, remarkMath,
remarkAdmonitions, [remarkAdmonitions, mkdocsConfig],
remarkReadingTime, remarkReadingTime,
remarkTypst, remarkTypst,
remarkEmoji, remarkEmoji,

View file

@ -6,6 +6,7 @@ import type { Data } from "unist";
import type { BuildVisitor } from "unist-util-visit"; import type { BuildVisitor } from "unist-util-visit";
import type { Blockquote, Paragraph, Text } from "mdast"; import type { Blockquote, Paragraph, Text } from "mdast";
import type { RemarkPlugin } from "@astrojs/markdown-remark"; import type { RemarkPlugin } from "@astrojs/markdown-remark";
import classNames from "classnames";
const remarkAdmonitions: RemarkPlugin = const remarkAdmonitions: RemarkPlugin =
(providedConfig?: Partial<Config>) => (tree) => { (providedConfig?: Partial<Config>) => (tree) => {
@ -14,59 +15,64 @@ const remarkAdmonitions: RemarkPlugin =
export default remarkAdmonitions; export default remarkAdmonitions;
const handleNode = (config: Config): BuildVisitor => (node) => { const handleNode =
// Filter required elems (config: Config): BuildVisitor =>
if (node.type !== "blockquote") return; (node) => {
const blockquote = node as Blockquote; // Filter required elems
if (node.type !== "blockquote") return;
const blockquote = node as Blockquote;
if (blockquote.children[0]?.type !== "paragraph") return; if (blockquote.children[0]?.type !== "paragraph") return;
const paragraph = blockquote.children[0]; const paragraph = blockquote.children[0];
if (paragraph.children[0]?.type !== "text") return; if (paragraph.children[0]?.type !== "text") return;
const text = paragraph.children[0]; const text = paragraph.children[0];
// A link break after the title is explicitly required by GitHub // A link break after the title is explicitly required by GitHub
const titleEnd = text.value.indexOf("\n"); const titleEnd = text.value.indexOf("\n");
if (titleEnd < 0) return; if (titleEnd < 0) return;
const textBody = text.value.substring(titleEnd + 1); const textBody = text.value.substring(titleEnd + 1);
let title = text.value.substring(0, titleEnd); let title = text.value.substring(0, titleEnd);
// Handle whitespaces after the title. // Handle whitespaces after the title.
// Whitespace characters are defined by GFM // Whitespace characters are defined by GFM
const m = /[ \t\v\f\r]+$/.exec(title); const m = /[ \t\v\f\r]+$/.exec(title);
if (m && !config.titleKeepTrailingWhitespaces) { if (m && !config.titleKeepTrailingWhitespaces) {
title = title.substring(0, title.length - m[0].length); title = title.substring(0, title.length - m[0].length);
} }
if (!nameFilter(config.titleFilter)(title)) return; if (!nameFilter(config.titleFilter)(title)) return;
const { displayTitle, checkedTitle } = config.titleTextMap(title); const { displayTitle, checkedTitle } = config.titleTextMap(title);
// Update the text body // Update the text body
text.value = textBody; text.value = textBody;
// Insert the title element and add classes for the title // Insert the title element and add classes for the title
const paragraphTitleText: Text = { type: "text", value: displayTitle }; const paragraphTitleText: Text = { type: "text", value: displayTitle };
const paragraphTitle: Paragraph = { const paragraphTitle: Paragraph = {
type: "paragraph", type: "paragraph",
children: [paragraphTitleText], children: [paragraphTitleText],
data: config.dataMaps.title({ data: config.dataMaps.title({
hProperties: {
className: classNameMap(config.classNameMaps.title)(checkedTitle),
},
}),
};
blockquote.children.unshift(paragraphTitle);
// Add classes for the block
blockquote.data = config.dataMaps.block({
...blockquote.data,
hProperties: { hProperties: {
className: classNameMap(config.classNameMaps.title)(checkedTitle), className: classNameMap(config.classNameMaps.block)(checkedTitle),
}, },
}), // The blockquote should be rendered as a div, which is explicitly required by GitHub
hName: "div",
});
}; };
blockquote.children.unshift(paragraphTitle);
// Add classes for the block const TITLE_PATTERN =
blockquote.data = config.dataMaps.block({ /\[\!admonition: (attention|caution|danger|error|hint|important|note|tip|warning)\]/i;
...blockquote.data,
hProperties: {
className: classNameMap(config.classNameMaps.block)(checkedTitle),
},
// The blockquote should be rendered as a div, which is explicitly required by GitHub
hName: "div",
});
};
export const mkdocsConfig: Partial<Config> = { export const mkdocsConfig: Partial<Config> = {
classNameMaps: { classNameMaps: {
@ -74,40 +80,34 @@ export const mkdocsConfig: Partial<Config> = {
"admonition", "admonition",
...(title.startsWith("admonition: ") ...(title.startsWith("admonition: ")
? title.substring("admonition: ".length) ? title.substring("admonition: ".length)
: title).split( : title
" ", ).split(" "),
),
], ],
title: "admonition-title", title: classNames("admonition-title"),
}, },
titleFilter: (title) =>
(title.startsWith("[!admonition: ") && title.endsWith("]")) || titleFilter: (title) => Boolean(title.match(TITLE_PATTERN)),
(Boolean(
title.match( titleTextMap: (title: string) => {
/^\[!(attention|caution|danger|error|hint|important|note|tip|warning)/, console.log("title", title);
), const match = title.match(TITLE_PATTERN);
) && console.log("matches", match);
title.endsWith("]")), const displayTitle = match?.[1] ?? "";
titleTextMap: (title) => { const checkedTitle = displayTitle;
title = title.substring(2, title.length - 1);
// ' "' will not occur in classes
const i = title.indexOf(' "');
const displayTitle = i >= 0
? title.substring(i + 2, title.length - 1) // Display title is wrapped with ""
: "";
const checkedTitle = title.substring(0, i);
return { displayTitle, checkedTitle }; return { displayTitle, checkedTitle };
}, },
}; };
export interface Config { export interface Config {
classNameMaps: { classNameMaps: {
block: ClassNameMap; block: ClassNameMap;
title: ClassNameMap; title: ClassNameMap;
}; };
titleFilter: NameFilter; titleFilter: NameFilter;
titleTextMap: ( titleTextMap: (title: string) => {
title: string, displayTitle: string;
) => { displayTitle: string; checkedTitle: string }; checkedTitle: string;
};
dataMaps: { dataMaps: {
block: (data: Data) => Data; block: (data: Data) => Data;
title: (data: Data) => Data; title: (data: Data) => Data;
@ -121,7 +121,9 @@ export const defaultConfig: Config = {
block: "admonition", block: "admonition",
title: "admonition-title", title: "admonition-title",
}, },
titleFilter: ["[!NOTE]", "[!IMPORTANT]", "[!WARNING]"], titleFilter: ["[!NOTE]", "[!IMPORTANT]", "[!WARNING]"],
titleTextMap: (title) => ({ titleTextMap: (title) => ({
displayTitle: title.substring(2, title.length - 1), displayTitle: title.substring(2, title.length - 1),
checkedTitle: title.substring(2, title.length - 1), checkedTitle: title.substring(2, title.length - 1),

View file

@ -12,7 +12,7 @@ container without exiting it. Some Docker containers are incredibly stripped
down to optimize away bloat (which is good!) but this may make debugging them down to optimize away bloat (which is good!) but this may make debugging them
relatively annoying. relatively annoying.
> [!NOTE] > [!admonition: NOTE]
> These are my specific steps for running it, please replace the paths and > These are my specific steps for running it, please replace the paths and
> container names with the ones relevant to your specific use-case. > container names with the ones relevant to your specific use-case.

View file

@ -61,7 +61,7 @@ I shove new config files in their root directory.
 flake.nix ✗  flake.nix ✗
``` ```
> [!NOTE] > [!admonition: NOTE]
> The `✗` indicates that I added the file to the project, and it hasn't been > The `✗` indicates that I added the file to the project, and it hasn't been
> committed to the repo yet. > committed to the repo yet.
@ -131,7 +131,7 @@ project structure should look a bit more like this:
 flake.nix  flake.nix
``` ```
> [!NOTE] > [!admonition: NOTE]
> Remember, since you moved the `.envrc` file, you will need to run `direnv allow` > Remember, since you moved the `.envrc` file, you will need to run `direnv allow`
> again. Depending on how you moved it, you might also need to change the path you > again. Depending on how you moved it, you might also need to change the path you
> wrote in the `use flake` command. > wrote in the `use flake` command.

View file

@ -108,7 +108,7 @@ true≢false2 p = transport bool-map2 p refl
## Note on proving divergence on equivalent values ## Note on proving divergence on equivalent values
> [!NOTE] > [!admonition: NOTE]
> Update: some of these have been commented out since regular Agda doesn't support higher inductive types > Update: some of these have been commented out since regular Agda doesn't support higher inductive types
Let's make sure this isn't broken by trying to apply this to something that's Let's make sure this isn't broken by trying to apply this to something that's

View file

@ -29,7 +29,7 @@ my personal schedule, yet they only see a small slice of your life. The only
things calendars can see automatically with no intervention on my part are things calendars can see automatically with no intervention on my part are
emails that are sent from airlines. emails that are sent from airlines.
> [!NOTE] > [!admonition: NOTE]
> I'm sure Google or Apple could probably ritz up their services to scan text > I'm sure Google or Apple could probably ritz up their services to scan text
> and guess events to put on your calendar, but that's missing the point. The vast > and guess events to put on your calendar, but that's missing the point. The vast
> majority of people I associate with rarely coordinate events over email in the > majority of people I associate with rarely coordinate events over email in the
@ -118,7 +118,7 @@ data using various visualizations for this purpose.
[2]: https://git.mzhang.io/michael/logseq-calendar [2]: https://git.mzhang.io/michael/logseq-calendar
> [!NOTE] > [!admonition: NOTE]
> As an aside, this isn't sponsored in any way. While this post makes me sound > As an aside, this isn't sponsored in any way. While this post makes me sound
> like just a Logseq shill, it's actually quite the opposite: they're an > like just a Logseq shill, it's actually quite the opposite: they're an
> open-source project solely funded by donations. I've been donating to them > open-source project solely funded by donations. I've been donating to them

View file

@ -142,7 +142,7 @@ That's quite a mouthful. If you tried calling $Y$ on some term, you will find
that evaluation will quickly expand infinitely. That makes sense given its that evaluation will quickly expand infinitely. That makes sense given its
purpose: to find a _fixed point_ of whatever function you pass in. purpose: to find a _fixed point_ of whatever function you pass in.
> [!NOTE] > [!admonition: NOTE]
> As an example, the fixed-point of the function $f(x) = \sqrt{x}$ is $1$. > As an example, the fixed-point of the function $f(x) = \sqrt{x}$ is $1$.
> That's because $f(1) = 1$, and applying $f$ to any other number sort of > That's because $f(1) = 1$, and applying $f$ to any other number sort of
> converges in on this value. If you took any number and applied $f$ infinitely > converges in on this value. If you took any number and applied $f$ infinitely
@ -221,7 +221,7 @@ much. But as a result of this tiny change, _every_ term now has a type:
- $λ(x:\mathbb{N}).2x :: \mathbb{N} \rightarrow \mathbb{N}$ - $λ(x:\mathbb{N}).2x :: \mathbb{N} \rightarrow \mathbb{N}$
- $isEven(3) :: (\mathbb{N} \rightarrow \mathrm{Bool}) · \mathbb{N} = \mathrm{Bool}$ - $isEven(3) :: (\mathbb{N} \rightarrow \mathrm{Bool}) · \mathbb{N} = \mathrm{Bool}$
> [!NOTE] > [!admonition: NOTE]
> Some notation: > Some notation:
> >
> - $x :: T$ means $x$ has type $T$, and > - $x :: T$ means $x$ has type $T$, and

View file

@ -16,7 +16,7 @@ to, since they're very different concepts.
to be the same, so anytime you see $x$, you can replace it with $y$ and vice to be the same, so anytime you see $x$, you can replace it with $y$ and vice
versa." versa."
> [!NOTE] > [!admonition: NOTE]
> Technically speaking, definitional and judgmental are two separate concepts > Technically speaking, definitional and judgmental are two separate concepts
> but coincide in many type theories. You could think of multiple levels of > but coincide in many type theories. You could think of multiple levels of
> equalities like this: > equalities like this:

View file

@ -70,7 +70,7 @@ data Type where
Monotypes (usually denoted $\tau$) are types that aren't universally quantified. Monotypes (usually denoted $\tau$) are types that aren't universally quantified.
> [!NOTE] > [!admonition: NOTE]
> In the declarative version of this algorithm, monotypes don't have existential quantifiers either, > In the declarative version of this algorithm, monotypes don't have existential quantifiers either,
> but the algorithmic type system includes it. > but the algorithmic type system includes it.
> TODO: Explain why > TODO: Explain why

View file

@ -22,7 +22,7 @@ data _≡_ {l} {A : Set l} : (a b : A) → Set l where
</details> </details>
> [!NOTE] > [!admonition: NOTE]
> This content is a writeup from a weekend discussion session for the fall 2023 special-topics course CSCI 8980 at the University of Minnesota taught by [Favonia], who provided the examples. > This content is a writeup from a weekend discussion session for the fall 2023 special-topics course CSCI 8980 at the University of Minnesota taught by [Favonia], who provided the examples.
> This post is primarily a summary of the concepts discussed. > This post is primarily a summary of the concepts discussed.

View file

@ -130,7 +130,7 @@ With this knowledge, when you see a 200 BPM song, you can divide $300 / 200 = 1.
Some tools like [3icecream] have a BPM calculator for charts if you enter in your reading speed, but often using your phone's calculator or just memorizing some common benchmark BPMs will do just fine. Some tools like [3icecream] have a BPM calculator for charts if you enter in your reading speed, but often using your phone's calculator or just memorizing some common benchmark BPMs will do just fine.
> [!NOTE] > [!admonition: NOTE]
> Since every song you play might be a different BPM, always check to make sure the scroll speed is what you want! > Since every song you play might be a different BPM, always check to make sure the scroll speed is what you want!
If a song's BPM is variable, it will show a range instead. If a song's BPM is variable, it will show a range instead.

View file

@ -55,7 +55,7 @@ boat load of files to hide your potential complexity into, consider whether the
cost of adding that abstraction is worth the pain it will take to change it cost of adding that abstraction is worth the pain it will take to change it
later. later.
> [!NOTE] > [!admonition: NOTE]
> As a bonus, if your language has a good enough type system, you probably don't > As a bonus, if your language has a good enough type system, you probably don't
> need the strategy pattern at all. Just create a function signature and pass > need the strategy pattern at all. Just create a function signature and pass
functions as values! functions as values!

View file

@ -22,6 +22,8 @@
// Admonition // Admonition
--note-color: #{$linkColor}; --note-color: #{$linkColor};
--note-background-color: var(--link-hover-color); --note-background-color: var(--link-hover-color);
--warning-color: rgb(208, 126, 25);
--warning-bg-color: rgb(255, 250, 202);
// Syntax Highlighting // Syntax Highlighting
--astro-code-color-text: #24292e; --astro-code-color-text: #24292e;
@ -62,6 +64,8 @@
// Admonition // Admonition
--note-color: #{$linkColor}; --note-color: #{$linkColor};
--note-background-color: var(--link-hover-color); --note-background-color: var(--link-hover-color);
--warning-color: rgb(211, 185, 107);
--warning-bg-color: rgb(121, 81, 21);
// Syntax Highlighting // Syntax Highlighting
--astro-code-color-text: #e1e4e8; --astro-code-color-text: #e1e4e8;

View file

@ -245,10 +245,10 @@ hr.endline {
} }
.admonition { .admonition {
// TODO: Figure out how to distinguish admonitions
--admonition-color: var(--note-color); --admonition-color: var(--note-color);
--admonition-bg-color: var(--note-background-color);
background-color: var(--note-background-color); background-color: var(--admonition-bg-color);
border-left: 4px solid var(--admonition-color); border-left: 4px solid var(--admonition-color);
padding: 8px; padding: 8px;
font-size: 0.9rem; font-size: 0.9rem;
@ -269,4 +269,9 @@ hr.endline {
:last-child { :last-child {
margin-bottom: 0; margin-bottom: 0;
} }
&.WARNING {
--admonition-color: var(--warning-color);
--admonition-bg-color: var(--warning-bg-color);
}
} }