diff --git a/.gitignore b/.gitignore index 47d7cd2..1cb103a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ _build /result .pnpm-store + +/public/generated diff --git a/astro.config.ts b/astro.config.ts index 8f01e87..2ab9624 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -15,34 +15,42 @@ import rehypeSlug from "rehype-slug"; import markdoc from "@astrojs/markdoc"; import remarkAgda from "./plugin/remark-agda"; +const outDir = "dist"; +const base = process.env.BASE ?? "/"; +const publicDir = "public"; + // https://astro.build/config export default defineConfig({ - site: "https://mzhang.io", - integrations: [mdx(), sitemap(), markdoc()], - markdown: { - syntaxHighlight: "shiki", - shikiConfig: { - theme: "css-variables", - }, - remarkPlugins: [ - remarkAgda, - remarkMath, - remarkAdmonitions, - remarkReadingTime, - remarkTypst, - remarkEmoji, - [ - remarkDescription, - { - name: "excerpt", - }, - ], - ], - rehypePlugins: [ - [rehypeKatex, {}], - rehypeAccessibleEmojis, - rehypeSlug, - [rehypeLinkHeadings, { behavior: "wrap" }], - ], - }, + site: "https://mzhang.io", + integrations: [mdx(), sitemap(), markdoc()], + + outDir, + base, + trailingSlash: "always", + publicDir, + + markdown: { + syntaxHighlight: "shiki", + shikiConfig: { theme: "css-variables" }, + remarkPlugins: [ + () => remarkAgda({ outDir, base, publicDir }), + remarkMath, + remarkAdmonitions, + remarkReadingTime, + remarkTypst, + remarkEmoji, + [ + remarkDescription, + { + name: "excerpt", + }, + ], + ], + rehypePlugins: [ + [rehypeKatex, {}], + rehypeAccessibleEmojis, + rehypeSlug, + [rehypeLinkHeadings, { behavior: "wrap" }], + ], + }, }); diff --git a/plugin/remark-agda.ts b/plugin/remark-agda.ts index f824894..cae42c3 100644 --- a/plugin/remark-agda.ts +++ b/plugin/remark-agda.ts @@ -1,16 +1,32 @@ -import type { RemarkPlugin } from "@astrojs/markdown-remark"; import type { RootContent } from "hast"; import { fromMarkdown } from "mdast-util-from-markdown"; import { fromHtml } from "hast-util-from-html"; import { toHtml } from "hast-util-to-html"; import { spawnSync } from "node:child_process"; -import { mkdirSync, mkdtempSync, readFileSync } from "node:fs"; +import { + mkdirSync, + mkdtempSync, + readFileSync, + readdirSync, + copyFileSync, + writeFileSync, +} from "node:fs"; import { tmpdir } from "node:os"; import { join, parse } from "node:path"; import { visit } from "unist-util-visit"; +import type { RemarkPlugin } from "@astrojs/markdown-remark"; + +interface Options { + base: string; + outDir: string; + publicDir: string; +} + +const remarkAgda: RemarkPlugin = ({ base, publicDir }: Options) => { + const destDir = join(publicDir, "generated", "agda"); + mkdirSync(destDir, { recursive: true }); -const remarkAgda: RemarkPlugin = () => { return (tree, { history }) => { const path: string = history[history.length - 1]!; if (!(path.endsWith(".lagda.md") || path.endsWith(".agda"))) return; @@ -18,14 +34,14 @@ const remarkAgda: RemarkPlugin = () => { console.log("AGDA:processing path", path); const tempDir = mkdtempSync(join(tmpdir(), "agdaRender.")); - const outDir = join(tempDir, "output"); - mkdirSync(outDir, { recursive: true }); + const agdaOutDir = join(tempDir, "output"); + mkdirSync(agdaOutDir, { recursive: true }); const childOutput = spawnSync( "agda", [ "--html", - `--html-dir=${outDir}`, + `--html-dir=${agdaOutDir}`, "--highlight-occurrences", "--html-highlight=code", path, @@ -34,17 +50,45 @@ const remarkAgda: RemarkPlugin = () => { ); // TODO: Handle child output - console.error("--AGDA OUTPUT--") + console.error("--AGDA OUTPUT--"); console.error(childOutput); console.error(childOutput.stdout?.toString()); console.error(childOutput.stderr?.toString()); - console.error("--AGDA OUTPUT--") - // if (childOutput.status !== 0) - // throw new Error("Agda failed", childOutput.stderr) + console.error("--AGDA OUTPUT--"); + + const referencedFiles = new Set(); + for (const file of readdirSync(agdaOutDir)) { + referencedFiles.add(file); + + const fullPath = join(agdaOutDir, file); + const fullDestPath = join(destDir, file); + + if (file.endsWith(".html")) { + const src = readFileSync(fullPath); + writeFileSync( + fullDestPath, + ` + + + + + + +
+          ${src}
+          
+ + + `, + ); + } else { + copyFileSync(fullPath, fullDestPath); + } + } const filename = parse(path).base.replace(/\.lagda.md/, ".md"); const htmlname = parse(path).base.replace(/\.lagda.md/, ".html"); - const fullOutputPath = join(outDir, filename); + const fullOutputPath = join(agdaOutDir, filename); const doc = readFileSync(fullOutputPath); @@ -60,8 +104,17 @@ const remarkAgda: RemarkPlugin = () => { visit(html, "element", (node) => { if (node.tagName !== "a") return; - if (node.properties.href && node.properties.href.includes(htmlname)) { - node.properties.href = node.properties.href.replace(htmlname, ""); + if (node.properties.href) { + // Trim off end + const [href, hash, ...rest] = node.properties.href.split("#"); + if (rest.length > 0) throw new Error("come look at this"); + + if (href === htmlname) node.properties.href = `#${hash}`; + + if (referencedFiles.has(href)) { + node.properties.href = `${base}generated/agda/${href}${hash ? `#${hash}` : ""}`; + node.properties.target = "_blank"; + } } }); diff --git a/src/content/posts/2024-06-26-agda-rendering.lagda.md b/src/content/posts/2024-06-26-agda-rendering.lagda.md new file mode 100644 index 0000000..82d0563 --- /dev/null +++ b/src/content/posts/2024-06-26-agda-rendering.lagda.md @@ -0,0 +1,26 @@ +--- +title: "Agda rendering in my blog!" +slug: 2024-06-26-agda-rendering +date: 2024-06-27T04:25:15.332Z +tags: ["meta", "agda"] +draft: true +--- + +I finally spent some time today getting Agda syntax highlighting working on my blog. +This took me around 3-4 hours of debugging but I'm very happy with how it turned out. + +First off, a demonstration. Here's the code for function application over a path: + +``` +open import Agda.Primitive + +infix 4 _≡_ +data _≡_ {l} {A : Set l} : (a b : A) → Set l where + instance refl : {x : A} → x ≡ x + +ap : {l1 l2 : Level} {A : Set l1} {B : Set l2} {x y : A} + → (f : A → B) + → (p : x ≡ y) + → f x ≡ f y +ap {l1} {l2} {A} {B} {x} {y} f refl = refl +```