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 remarkEmoji from "remark-emoji";
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 rehypeKatex from "rehype-katex";
import remarkTypst from "./plugin/remark-typst";
@ -35,7 +35,7 @@ export default defineConfig({
remarkPlugins: [
() => remarkAgda({ outDir, base, publicDir }),
remarkMath,
remarkAdmonitions,
[remarkAdmonitions, mkdocsConfig],
remarkReadingTime,
remarkTypst,
remarkEmoji,

View file

@ -6,6 +6,7 @@ import type { Data } from "unist";
import type { BuildVisitor } from "unist-util-visit";
import type { Blockquote, Paragraph, Text } from "mdast";
import type { RemarkPlugin } from "@astrojs/markdown-remark";
import classNames from "classnames";
const remarkAdmonitions: RemarkPlugin =
(providedConfig?: Partial<Config>) => (tree) => {
@ -14,59 +15,64 @@ const remarkAdmonitions: RemarkPlugin =
export default remarkAdmonitions;
const handleNode = (config: Config): BuildVisitor => (node) => {
// Filter required elems
if (node.type !== "blockquote") return;
const blockquote = node as Blockquote;
const handleNode =
(config: Config): BuildVisitor =>
(node) => {
// 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];
if (paragraph.children[0]?.type !== "text") return;
const paragraph = blockquote.children[0];
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
const titleEnd = text.value.indexOf("\n");
if (titleEnd < 0) return;
// A link break after the title is explicitly required by GitHub
const titleEnd = text.value.indexOf("\n");
if (titleEnd < 0) return;
const textBody = text.value.substring(titleEnd + 1);
let title = text.value.substring(0, titleEnd);
// Handle whitespaces after the title.
// Whitespace characters are defined by GFM
const m = /[ \t\v\f\r]+$/.exec(title);
if (m && !config.titleKeepTrailingWhitespaces) {
title = title.substring(0, title.length - m[0].length);
}
if (!nameFilter(config.titleFilter)(title)) return;
const { displayTitle, checkedTitle } = config.titleTextMap(title);
const textBody = text.value.substring(titleEnd + 1);
let title = text.value.substring(0, titleEnd);
// Handle whitespaces after the title.
// Whitespace characters are defined by GFM
const m = /[ \t\v\f\r]+$/.exec(title);
if (m && !config.titleKeepTrailingWhitespaces) {
title = title.substring(0, title.length - m[0].length);
}
if (!nameFilter(config.titleFilter)(title)) return;
const { displayTitle, checkedTitle } = config.titleTextMap(title);
// Update the text body
text.value = textBody;
// Update the text body
text.value = textBody;
// Insert the title element and add classes for the title
const paragraphTitleText: Text = { type: "text", value: displayTitle };
const paragraphTitle: Paragraph = {
type: "paragraph",
children: [paragraphTitleText],
data: config.dataMaps.title({
// Insert the title element and add classes for the title
const paragraphTitleText: Text = { type: "text", value: displayTitle };
const paragraphTitle: Paragraph = {
type: "paragraph",
children: [paragraphTitleText],
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: {
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
blockquote.data = config.dataMaps.block({
...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",
});
};
const TITLE_PATTERN =
/\[\!admonition: (attention|caution|danger|error|hint|important|note|tip|warning)\]/i;
export const mkdocsConfig: Partial<Config> = {
classNameMaps: {
@ -74,40 +80,34 @@ export const mkdocsConfig: Partial<Config> = {
"admonition",
...(title.startsWith("admonition: ")
? title.substring("admonition: ".length)
: title).split(
" ",
),
: title
).split(" "),
],
title: "admonition-title",
title: classNames("admonition-title"),
},
titleFilter: (title) =>
(title.startsWith("[!admonition: ") && title.endsWith("]")) ||
(Boolean(
title.match(
/^\[!(attention|caution|danger|error|hint|important|note|tip|warning)/,
),
) &&
title.endsWith("]")),
titleTextMap: (title) => {
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);
titleFilter: (title) => Boolean(title.match(TITLE_PATTERN)),
titleTextMap: (title: string) => {
console.log("title", title);
const match = title.match(TITLE_PATTERN);
console.log("matches", match);
const displayTitle = match?.[1] ?? "";
const checkedTitle = displayTitle;
return { displayTitle, checkedTitle };
},
};
export interface Config {
classNameMaps: {
block: ClassNameMap;
title: ClassNameMap;
};
titleFilter: NameFilter;
titleTextMap: (
title: string,
) => { displayTitle: string; checkedTitle: string };
titleTextMap: (title: string) => {
displayTitle: string;
checkedTitle: string;
};
dataMaps: {
block: (data: Data) => Data;
title: (data: Data) => Data;
@ -121,7 +121,9 @@ export const defaultConfig: Config = {
block: "admonition",
title: "admonition-title",
},
titleFilter: ["[!NOTE]", "[!IMPORTANT]", "[!WARNING]"],
titleTextMap: (title) => ({
displayTitle: 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
relatively annoying.
> [!NOTE]
> [!admonition: NOTE]
> These are my specific steps for running it, please replace the paths and
> 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 ✗
```
> [!NOTE]
> [!admonition: NOTE]
> The `✗` indicates that I added the file to the project, and it hasn't been
> committed to the repo yet.
@ -131,7 +131,7 @@ project structure should look a bit more like this:
 flake.nix
```
> [!NOTE]
> [!admonition: NOTE]
> 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
> 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]
> [!admonition: NOTE]
> 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

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
emails that are sent from airlines.
> [!NOTE]
> [!admonition: NOTE]
> 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
> 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
> [!NOTE]
> [!admonition: NOTE]
> 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
> 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
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$.
> 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
@ -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}$
- $isEven(3) :: (\mathbb{N} \rightarrow \mathrm{Bool}) · \mathbb{N} = \mathrm{Bool}$
> [!NOTE]
> [!admonition: NOTE]
> Some notation:
>
> - $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
versa."
> [!NOTE]
> [!admonition: NOTE]
> Technically speaking, definitional and judgmental are two separate concepts
> but coincide in many type theories. You could think of multiple levels of
> equalities like this:

View file

@ -70,7 +70,7 @@ data Type where
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,
> but the algorithmic type system includes it.
> TODO: Explain why

View file

@ -22,7 +22,7 @@ data _≡_ {l} {A : Set l} : (a b : A) → Set l where
</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 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.
> [!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!
If a song's BPM is variable, it will show a range instead.
@ -312,4 +312,4 @@ There's an incredible wealth of information across the internet about this game,
While I'm certainly not even close to the end of my journey with DDR, I hope that you got to learn something from my experience.
If there's something I got wrong, or some resources or information you want me to add, please let me know in the comments.
**See you next time on the dance floor!**
**See you next time on the dance floor!**

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
later.
> [!NOTE]
> [!admonition: NOTE]
> 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
functions as values!

View file

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

View file

@ -245,10 +245,10 @@ hr.endline {
}
.admonition {
// TODO: Figure out how to distinguish admonitions
--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);
padding: 8px;
font-size: 0.9rem;
@ -269,4 +269,9 @@ hr.endline {
:last-child {
margin-bottom: 0;
}
&.WARNING {
--admonition-color: var(--warning-color);
--admonition-bg-color: var(--warning-bg-color);
}
}