feat: add file name transformer for fenced code blocks (#535)

* feat: add file name transformer for fenced code blocks

* chore: update file names in fenced code blocks
This commit is contained in:
Sat Naing
2025-06-14 00:14:07 +07:00
committed by GitHub
parent 38ebf6e1b6
commit cb8a4794be
9 changed files with 156 additions and 72 deletions
+4 -2
View File
@@ -3,12 +3,13 @@ import tailwindcss from "@tailwindcss/vite";
import sitemap from "@astrojs/sitemap";
import remarkToc from "remark-toc";
import remarkCollapse from "remark-collapse";
import { SITE } from "./src/config";
import {
transformerNotationDiff,
transformerNotationHighlight,
transformerNotationWordHighlight,
} from "@shikijs/transformers";
import { transformerFileName } from "./src/utils/transformers/fileName";
import { SITE } from "./src/config";
// https://astro.build/config
export default defineConfig({
@@ -26,9 +27,10 @@ export default defineConfig({
defaultColor: false,
wrap: false,
transformers: [
transformerNotationDiff({ matchAlgorithm: "v3" }),
transformerFileName(),
transformerNotationHighlight(),
transformerNotationWordHighlight(),
transformerNotationDiff({ matchAlgorithm: "v3" }),
],
},
},
+46 -9
View File
@@ -1,7 +1,7 @@
---
author: Sat Naing
pubDatetime: 2022-09-23T15:22:00Z
modDatetime: 2025-03-22T06:25:46.734Z
modDatetime: 2025-06-13T16:52:45.934Z
title: Adding new posts in AstroPaper theme
slug: adding-new-posts-in-astropaper-theme
featured: true
@@ -84,16 +84,15 @@ Title and description (excerpt) are important for search engine optimization (SE
For example, if the blog file name is `adding-new-post.md` and you don't specify the slug in your frontmatter, Astro will automatically create a slug for the blog post using the file name. Thus, the slug will be `adding-new-post`. But if you specify the `slug` in the frontmatter, this will override the default slug. You can read more about this in [Astro Docs](https://docs.astro.build/en/guides/content-collections/#defining-custom-slugs).
If you omit `tags` in a blog post (in other words, if no tag is specified), the default tag `others` will be used as a tag for that post. You can set the default tag in the `/src/content/config.ts` file.
If you omit `tags` in a blog post (in other words, if no tag is specified), the default tag `others` will be used as a tag for that post. You can set the default tag in the `content.config.ts` file.
```ts
// src/content/config.ts
```ts file="src/content.config.ts"
export const blogSchema = z.object({
// ---
// ...
draft: z.boolean().optional(),
// [!code highlight:1]
tags: z.array(z.string()).default(["others"]), // replace "others" with whatever you want
// ---
// ...
});
```
@@ -101,8 +100,7 @@ export const blogSchema = z.object({
Here is the sample frontmatter for a post.
```yaml
# src/content/blog/sample-post.md
```yaml file="src/data/blog/sample-post.md"
---
title: The title of the post
author: your name
@@ -132,7 +130,7 @@ For instance, if you want to place your table of contents just under the intro p
<!-- prettier-ignore-start -->
```md
---
# some frontmatter
# frontmatter
---
Here are some recommendations, tips & ticks for creating new posts in AstroPaper blog theme.
@@ -150,6 +148,45 @@ There's one thing to note about headings. The AstroPaper blog posts use title (t
This rule is not mandatory, but highly recommended for visual, accessibility and SEO purposes.
## Syntax Highlighting
AstroPaper uses [Shiki](https://shiki.style/) as the default syntax highlighting. Starting from AstroPaper v5.4, [@shikijs/transformers](https://shiki.style/packages/transformers) is used to enhance better fenced code blocks. If you don't want to use it, you can simply remove it like this
```bash
pnpm remove @shikijs/transformers
```
```js file="astro.config.ts"
// ...
// [!code --:5]
import {
transformerNotationDiff,
transformerNotationHighlight,
transformerNotationWordHighlight,
} from "@shikijs/transformers";
export default defineConfig({
// ...
markdown: {
remarkPlugins: [remarkToc, [remarkCollapse, { test: "Table of contents" }]],
shikiConfig: {
// For more themes, visit https://shiki.style/themes
themes: { light: "min-light", dark: "night-owl" },
defaultColor: false,
wrap: false,
transformers: [
transformerFileName(),
// [!code --:3]
transformerNotationHighlight(),
transformerNotationWordHighlight(),
transformerNotationDiff({ matchAlgorithm: "v3" }),
],
},
},
// ...
}
```
## Storing Images for Blog Content
Here are two methods for storing images and displaying them inside a markdown file.
@@ -1,7 +1,7 @@
---
author: Sat Naing
pubDatetime: 2022-09-25T15:20:35Z
modDatetime: 2025-06-09T07:42:54.791Z
modDatetime: 2025-06-13T16:46:34.155Z
title: Customizing AstroPaper theme color schemes
featured: false
draft: false
@@ -19,19 +19,31 @@ This post will explain how you can enable/disable light & dark mode for the webs
## Enable/disable light & dark mode
AstroPaper theme will include light and dark mode by default. In other words, there will be two color schemes\_ one for light mode and another for dark mode. This default behavior can be disabled in SITE configuration object of the `src/config.ts` file.
AstroPaper theme will include light and dark mode by default. In other words, there will be two color schemes\_ one for light mode and another for dark mode. This default behavior can be disabled in `SITE` configuration object.
```js
// file: src/config.ts
```js file="src/config.ts"
export const SITE = {
website: "https://astro-paper.pages.dev/",
website: "https://astro-paper.pages.dev/", // replace this with your deployed domain
author: "Sat Naing",
profile: "https://satnaing.dev/",
desc: "A minimal, responsive and SEO-friendly Astro blog theme.",
title: "AstroPaper",
ogImage: "astropaper-og.jpg",
lightAndDarkMode: true, // true by default
postPerPage: 3,
};
lightAndDarkMode: true, // [!code highlight]
postPerIndex: 4,
postPerPage: 4,
scheduledPostMargin: 15 * 60 * 1000, // 15 minutes
showArchives: true,
showBackButton: true, // show back button in post detail
editPost: {
enabled: true,
text: "Suggest Changes",
url: "https://github.com/satnaing/astro-paper/edit/main/",
},
dynamicOgImage: true,
lang: "en", // html lang code. Set this empty and default will be "en"
timezone: "Asia/Bangkok", // Default global timezone (IANA format) https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
} as const;
```
To disable `light & dark mode` set `SITE.lightAndDarkMode` to `false`.
@@ -40,16 +52,15 @@ To disable `light & dark mode` set `SITE.lightAndDarkMode` to `false`.
By default, if we disable `SITE.lightAndDarkMode`, we will only get system's prefers-color-scheme.
Thus, to choose primary color scheme instead of prefers-color-scheme, we have to set color scheme in the primaryColorScheme variable inside `public/toggle-theme.js`.
Thus, to choose primary color scheme instead of prefers-color-scheme, we have to set color scheme in the `primaryColorScheme` variable inside `toggle-theme.js`.
```js
/* file: public/toggle-theme.js */
const primaryColorScheme = ""; // "light" | "dark"
```js file="public/toggle-theme.js"
const primaryColorScheme = ""; // "light" | "dark" // [!code hl]
// Get theme data from local storage
const currentTheme = localStorage.getItem("theme");
// other codes etc...
// ...
```
The **primaryColorScheme** variable can hold two values\_ `"light"`, `"dark"`. You can leave the empty string (default) if you don't want to specify the primary color scheme.
@@ -58,20 +69,16 @@ The **primaryColorScheme** variable can hold two values\_ `"light"`, `"dark"`. Y
- `"light"` - use light mode as primary color scheme.
- `"dark"` - use dark mode as primary color scheme.
<details><summary>Why 'primaryColorScheme' is not inside config.ts?</summary>
> To avoid color flickering on page reload, we have to place the toggle-switch JavaScript codes as early as possible when the page loads. It solves the problem of flickering, but as a trade-off, we cannot use ESM imports anymore.
[Click here](https://docs.astro.build/en/reference/directives-reference/#isinline) to know more about Astro's `is:inline` script.
<details>
<summary>Why primaryColorScheme' is not inside config.ts?</summary>
To avoid color flickering on page reload, we have to place the toggle-switch JavaScript codes as early as possible when the page loads. It solves the problem of flickering, but as a trade-off, we cannot use ESM imports anymore.
</details>
## Customize color schemes
Both light & dark color schemes of AstroPaper theme can be customized. You can do this in `src/styles/global.css` file.
Both light & dark color schemes of AstroPaper theme can be customized in the `global.css` file.
```css
/* file: src/styles/global.css */
```css file="src/styles/global.css"
@import "tailwindcss";
@import "./typography.css";
@@ -93,7 +100,7 @@ html[data-theme="dark"] {
--muted: #343f60bf;
--border: #ab4b08;
}
/* other styles */
/* ... */
```
In the AstroPaper theme, the `:root` and `html[data-theme="light"]` selectors define the light color scheme, while `html[data-theme="dark"]` defines the dark color scheme.
@@ -112,8 +119,8 @@ Here is the detail explanation of color properties.
Here is an example of changing the light color scheme.
```css
/* other styles */
```css file="src/styles/global.css"
/* ... */
:root,
html[data-theme="light"] {
--background: #f6eee1;
@@ -122,7 +129,7 @@ html[data-theme="light"] {
--muted: #efd8b0;
--border: #dc9891;
}
/* other styles */
/* ... */
```
> Check out some [predefined color schemes](https://astro-paper.pages.dev/posts/predefined-color-schemes/) AstroPaper has already crafted for you.
+2 -4
View File
@@ -46,9 +46,7 @@ Dynamic OG image of AstroPaper includes _the blog post title_, _author name_ and
Titles with non-latin characters won't display properly out of the box. To resolve this, we have to replace `fontsConfig` inside `loadGoogleFont.ts` with your preferred font.
```ts
// file: loadGoogleFont.ts
```ts file=src/utils/loadGoogleFont.ts
async function loadGoogleFonts(
text: string
): Promise<
@@ -75,7 +73,7 @@ async function loadGoogleFonts(
style: "normal",
},
];
// other codes
// ...
}
```
@@ -32,15 +32,15 @@ In this section, you will find instructions on how to add support for LaTeX in y
pnpm install rehype-katex remark-math katex
```
2. Update the Astro configuration (`astro.config.ts`) to use the these plugins:
2. Update the Astro configuration to use the these plugins:
```ts
// other imports
```ts file=astro.config.ts
// ...
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
export default defineConfig({
// other configs
// ...
markdown: {
remarkPlugins: [
remarkMath, // [!code ++]
@@ -54,13 +54,13 @@ In this section, you will find instructions on how to add support for LaTeX in y
wrap: false,
},
},
// other configs
// ...
});
```
3. Import KaTeX CSS in the main layout file `src/layouts/Layout.astro`
3. Import KaTeX CSS in the main layout file
```astro
```astro file=src/layouts/Layout.astro
---
import { SITE } from "@config";
@@ -82,9 +82,9 @@ In this section, you will find instructions on how to add support for LaTeX in y
</body>
```
4. As the last step, add a text-color for `katex` in `src/styles/typography.css`.
4. As the last step, add a text-color for `katex` in `typography.css`.
```css
```css file=src/styles/typography.css
@plugin '@tailwindcss/typography';
@layer base {
@@ -22,8 +22,7 @@ The important configurations resides in `src/config.ts` file. Within that file,
During development, it's okay to leave `SITE.website` empty. But in production mode, you should specify your deployed url in `SITE.website` option since this will be used for canonical URL, social card URL etc.. which are important for SEO.
```js
// file: src/config.ts
```js file=src/config.ts
export const SITE = {
website: "https://astro-paper.pages.dev/", // replace this with your deployed domain
author: "Sat Naing",
@@ -73,12 +72,14 @@ Here are SITE configuration options
## Update layout width
The default `max-width` for the entire blog is `768px` (`max-w-3xl`). If you'd like to change it, you can easily update the `max-w-app` utility in your `src/styles/global.css` file:
The default `max-width` for the entire blog is `768px` (`max-w-3xl`). If you'd like to change it, you can easily update the `max-w-app` utility in your `global.css`. For instance:
```css
```css file=src/styles/global.css
@utility max-w-app {
/* [!code --:1] */
@apply max-w-3xl;
/* [!code ++:1] */
@apply max-w-4xl xl:max-w-5xl;
/* eg: max-w-4xl xl:max-w-5xl */
}
```
@@ -101,11 +102,11 @@ This is the easiest option. You just have to update `SITE.title` in `src/config.
You might want to use this option if you want to use an SVG logo.
- First add an SVG inside `src/assets` directory. (eg: `src/assets/dummy-logo.svg`)
- Then import that SVG inside `src/components/Header.astro`
- Then import that SVG inside `Header.astro`
```astro
```astro file=src/components/Header.astro
---
// other imports
// ...
import DummyLogo from "@/assets/dummy-logo.svg";
---
```
@@ -129,11 +130,11 @@ The best part of this approach is that you can customize your SVG styles as need
If your logo is an image but not SVG, you can use Astro's Image component.
- Add your logo inside `src/assets` directory. (eg: `src/assets/dummy-logo.png`)
- Import `Image` and your logo in `src/components/Header.astro`
- Import `Image` and your logo in `Header.astro`
```astro
```astro file=src/components/Header.astro
---
// other imports
// ...
import { Image } from "astro:assets";
import dummyLogo from "@/assets/dummy-logo.png";
---
@@ -155,11 +156,11 @@ With this approach, you can still adjust your image's appearance using CSS class
## Configuring social links
You can configure social links in `SOCIALS` object inside `src/constants.ts`.
![An arrow pointing at social link icons](https://github.com/user-attachments/assets/8b895400-d088-442f-881b-02d2443e00cf)
```ts
You can configure social links in `SOCIALS` object inside `constants.ts`.
```ts file=src/constants.ts
export const SOCIALS = [
{
name: "Github",
@@ -75,9 +75,9 @@ You should now have a script tag that looks like this:
></script>
```
Simply add that to the source code of the site. Most likely, if you're using _AstroPaper_ and want to enable comments on posts, navigate to `src/layouts/PostDetails.astro` and paste it into the desired location where you want the comments to appear, perhaps underneath the `Share this post on:` buttons.
Simply add that to the source code of the site. Most likely, if you're using _AstroPaper_ and want to enable comments on posts, navigate to `PostDetails.astro` and paste it into the desired location where you want the comments to appear, perhaps underneath the `Share this post on:` buttons.
```astro
```astro file=src/layouts/PostDetails.astro
<Layout {...layoutProps}>
<main>
<ShareLinks />
@@ -108,7 +108,7 @@ npm i @giscus/react && npx astro add react
Then we create a new `Comments.tsx` React component in `src/components`:
```tsx
```tsx file=src/components/Comments.tsx
import Giscus, { type Theme } from "@giscus/react";
import { GISCUS } from "@/constants";
import { useEffect, useState } from "react";
@@ -164,9 +164,9 @@ export default function Comments({
This _React_ component not only wraps the native _Giscus_ component, but also introduces additional props, namely `lightTheme` and `darkTheme`. Leveraging two event listeners, the _Giscus_ comments will align with the site's theme, dynamically switching between dark and light themes whenever the site or browser theme is changed.
We also need to define the `GISCUS` config, for which the optimal location is in `src/constants.ts`:
We also need to define the `GISCUS` config, for which the optimal location is in `constants.ts`:
```ts
```ts file=src/constants.ts
import type { GiscusProps } from "@giscus/react";
...
@@ -187,9 +187,9 @@ export const GISCUS: GiscusProps = {
Note that specifying a `theme` here will override the `lightTheme` and `darkTheme` props, resulting in a static theme setting, similar to the previous approach of embedding _Giscus_ with the `<script>` tag.
To complete the process, add the new Comments component to `src/layouts/PostDetails.astro` (replacing the `script` tag from the previous step).
To complete the process, add the new Comments component to `PostDetails.astro` (replacing the `script` tag from the previous step).
```jsx
```jsx file=src/layouts/PostDetails.astro
// [!code ++:1]
import Comments from "@/components/Comments";
+1 -1
View File
@@ -235,7 +235,7 @@ const nextPost =
const copyButton = document.createElement("button");
copyButton.className =
"copy-code absolute end-3 -top-3 rounded bg-muted px-2 py-1 text-xs leading-4 text-foreground font-medium";
"copy-code absolute end-3 top-3 rounded bg-muted/80 px-2 py-1 text-xs leading-4 text-foreground font-medium";
copyButton.innerHTML = copyButtonLabel;
codeBlock.setAttribute("tabindex", "0");
codeBlock.appendChild(copyButton);
+39
View File
@@ -0,0 +1,39 @@
export const transformerFileName = () => ({
pre(node) {
const raw = this.options.meta?.__raw?.split(" ");
if (!raw) return;
const metaMap = new Map();
for (const item of raw) {
const [key, value] = item.split("=");
metaMap.set(key, value.replace(/["'`]/g, ""));
}
const file = metaMap.get("file");
if (!file) return;
node.children.push({
type: "element",
tagName: "span",
properties: {
class: [
"px-2 py-1",
"absolute left-0 -top-6",
"rounded-t-md border border-b-0",
"bg-muted/50 text-foreground text-xs font-medium leading-4",
],
},
children: [
{
type: "text",
value: file,
},
],
});
this.addClassToHast(node, "mt-12 rounded-tl-none");
},
});