feat: Typst posts #41
|
@ -0,0 +1,93 @@
|
|||
#import "@preview/cetz:0.3.2"
|
||||
|
||||
#set text(24pt)
|
||||
#metadata((
|
||||
title: "Title",
|
||||
date: "2025-01-01",
|
||||
description: "Example diagram in typst",
|
||||
tags: ("svg", "typst"),
|
||||
))<post>
|
||||
|
||||
This is a diagram.
|
||||
|
||||
#emph(text(blue)[blue text])
|
||||
|
||||
#html.frame(cetz.canvas(length: 3cm, {
|
||||
import cetz.draw: *
|
||||
|
||||
set-style(
|
||||
mark: (fill: black, scale: 2),
|
||||
stroke: (thickness: 0.4pt, cap: "round"),
|
||||
angle: (
|
||||
radius: 0.3,
|
||||
label-radius: .22,
|
||||
fill: green.lighten(80%),
|
||||
stroke: (paint: green.darken(50%))
|
||||
),
|
||||
content: (padding: 1pt)
|
||||
)
|
||||
|
||||
grid((-1.5, -1.5), (1.4, 1.4), step: 0.5, stroke: gray + 0.2pt)
|
||||
|
||||
circle((0,0), radius: 1)
|
||||
|
||||
line((-1.5, 0), (1.5, 0), mark: (end: "stealth"))
|
||||
content((), $ x $, anchor: "west")
|
||||
line((0, -1.5), (0, 1.5), mark: (end: "stealth"))
|
||||
content((), $ y $, anchor: "south")
|
||||
|
||||
for (x, ct) in ((-1, $ -1 $), (-0.5, $ -1/2 $), (1, $ 1 $)) {
|
||||
line((x, 3pt), (x, -3pt))
|
||||
content((), anchor: "north", ct)
|
||||
}
|
||||
|
||||
for (y, ct) in ((-1, $ -1 $), (-0.5, $ -1/2 $), (0.5, $ 1/2 $), (1, $ 1 $)) {
|
||||
line((3pt, y), (-3pt, y))
|
||||
content((), anchor: "east", ct)
|
||||
}
|
||||
|
||||
// Draw the green angle
|
||||
cetz.angle.angle((0,0), (1,0), (1, calc.tan(30deg)),
|
||||
label: text(green, [#sym.alpha]))
|
||||
|
||||
line((0,0), (1, calc.tan(30deg)))
|
||||
|
||||
set-style(stroke: (thickness: 1.2pt))
|
||||
|
||||
line((30deg, 1), ((), "|-", (0,0)), stroke: (paint: red), name: "sin")
|
||||
content(("sin.start", 50%, "sin.end"), text(red)[$ sin alpha $])
|
||||
line("sin.end", (0,0), stroke: (paint: blue), name: "cos")
|
||||
content(("cos.start", 50%, "cos.end"), text(blue)[$ cos alpha $], anchor: "north")
|
||||
line((1, 0), (1, calc.tan(30deg)), name: "tan", stroke: (paint: orange))
|
||||
content("tan.end", $ text(#orange, tan alpha) = text(#red, sin alpha) / text(#blue, cos alpha) $, anchor: "west")
|
||||
}))
|
||||
|
||||
== Heading
|
||||
|
||||
#figure(
|
||||
table(
|
||||
columns: (auto, auto, auto),
|
||||
inset: 10pt,
|
||||
align: horizon,
|
||||
table.header(
|
||||
[*System*], [*Speed*], [*Diagrams*],
|
||||
),
|
||||
[LaTeX], [Slow], [Y],
|
||||
[Markdown], [Fast], [N],
|
||||
[Typst], [Fast], [N],
|
||||
),
|
||||
caption: [Comparison of blog typesetting options]
|
||||
)
|
||||
|
||||
#html.frame[
|
||||
$ lim_(n -> infinity) (1 + 1/n)^n = sum_(n=0)^infinity 1/(n!)
|
||||
$]
|
||||
|
||||
== Code
|
||||
|
||||
#html.frame[```rust
|
||||
fn deposit_cookie() {
|
||||
let cookie = generate();
|
||||
cookie_jar(cookie);
|
||||
}
|
||||
```]
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { Moon, Sun } from 'lucide-svelte';
|
||||
import Moon from 'lucide-svelte/icons/moon';
|
||||
import Sun from 'lucide-svelte/icons/sun';
|
||||
import { theme, setTheme } from '$lib/theme'
|
||||
|
||||
function toggleTheme() {
|
||||
|
|
|
@ -17,13 +17,13 @@ export function transformPostMeta(metadata: PostMetadata, slug: string) : Post {
|
|||
|
||||
// Generates a list of all posts filtering by tags and series, reverse chronologically ordered.
|
||||
export async function getPosts(filter: { tag?: string, series?: string }) : Promise<Post[]> {
|
||||
const allPostFiles = import.meta.glob('$content/post/*.md');
|
||||
const allPostFiles = import.meta.glob(['$content/post/*.md', '$content/post/*.typ']);
|
||||
const iterablePostFiles = Object.entries(allPostFiles);
|
||||
|
||||
let posts: Post[] = await Promise.all(
|
||||
iterablePostFiles.map(async ([pathMarkdown, resolver]) => {
|
||||
const { metadata } = await resolver();
|
||||
const slug = pathMarkdown.slice(pathMarkdown.lastIndexOf("/") + 1, -".md".length);
|
||||
const slug = pathMarkdown.slice(pathMarkdown.lastIndexOf("/") + 1, pathMarkdown.lastIndexOf("."));
|
||||
|
||||
return transformPostMeta(metadata, slug);
|
||||
})
|
||||
|
@ -42,7 +42,7 @@ export async function getPosts(filter: { tag?: string, series?: string }) : Prom
|
|||
}
|
||||
|
||||
export async function getTags() : Promise<Map<string, number>> {
|
||||
const allPostFiles = import.meta.glob('$content/post/*.md');
|
||||
const allPostFiles = import.meta.glob(['$content/post/*.md', '$content/post/*.typ']);
|
||||
const iterablePostFiles = Object.entries(allPostFiles);
|
||||
|
||||
const allPosts: string[][] = await Promise.all(
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { promisify } from 'util';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
const exec_async = promisify(exec);
|
||||
|
||||
export const typstPreprocess = (config) => {
|
||||
const {
|
||||
extensions,
|
||||
} = config;
|
||||
|
||||
return {
|
||||
name: "typst",
|
||||
markup: async ({content: _, filename}) => {
|
||||
|
||||
const extensionsParts = extensions.map((ext) =>
|
||||
ext.startsWith('.') ? ext : '.' + ext
|
||||
);
|
||||
if (!extensionsParts.some((ext) => filename.endsWith(ext))) return;
|
||||
|
||||
const result_metadata = await exec_async(`typst query --features html ${filename} "<post>" --field value --one`);
|
||||
const metadata = String(result_metadata.stdout);
|
||||
|
||||
const result_post = await exec_async(`typst compile --features html --format html ${filename} -`);
|
||||
// strip the body tag
|
||||
const bodyHTML = /<body.*?>([\s\S]*)<\/body>/.exec(result_post.stdout)[1]
|
||||
const code = `<script context='module'>\n export const metadata = ${metadata}\n</script>` + bodyHTML
|
||||
|
||||
return {
|
||||
code,
|
||||
data: {
|
||||
fm: metadata,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { routes } from "$lib/sitemap.ts"
|
||||
import { routes } from "$lib/sitemap"
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
import { Rss } from "lucide-svelte";
|
||||
import Rss from "lucide-svelte/icons/rss";
|
||||
import ThemeSwitch from "$lib/components/ThemeSwitch.svelte"
|
||||
let scrollPosition: number = .5;
|
||||
let scrollHeight: number = 1;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
import { page } from '$app/stores';
|
||||
import { routes } from "$lib/sitemap.ts"
|
||||
import ThemeSwitch from "$lib/components/ThemeSwitch.svelte"
|
||||
import { Rss, Menu } from "lucide-svelte";
|
||||
import Rss from "lucide-svelte/icons/rss";
|
||||
import Menu from "lucide-svelte/icons/menu";
|
||||
|
||||
function isCurrentLink(pathname, route) {
|
||||
return route != "/" && pathname.startsWith(route)
|
||||
|
|
|
@ -53,4 +53,7 @@
|
|||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
:global(svg) {
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,10 +3,21 @@ import type { PageLoad } from './$types';
|
|||
import type { Post } from '$lib/types'
|
||||
import { getPosts, transformPostMeta } from '$lib/posts';
|
||||
|
||||
async function importPost(slug: string) {
|
||||
try {
|
||||
const post = await import(`$content/post/${slug}.md`);
|
||||
return post;
|
||||
}
|
||||
catch(e) {
|
||||
console.log("Could not find .md", e);
|
||||
}
|
||||
const post = await import(`$content/post/${slug}.typ`);
|
||||
return post;
|
||||
}
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
try {
|
||||
const post = await import(`$content/post/${params.slug}.md`);
|
||||
if (!post) throw error(404);
|
||||
const post = await importPost(params.slug);
|
||||
if (!post) throw error(404);
|
||||
const metadata: Post = transformPostMeta(post.metadata, params.slug);
|
||||
const Content = post.default;
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
import fs from 'fs';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||
import adapter from '@sveltejs/adapter-static'
|
||||
import fs from 'fs'
|
||||
|
||||
import { mdsvex, escapeSvelte } from 'mdsvex';
|
||||
import { typstPreprocess } from './src/lib/typst.js'
|
||||
import { mdsvex, escapeSvelte } from 'mdsvex'
|
||||
import remarkAlert from './src/lib/markdown.js'
|
||||
import relativeImages from 'mdsvex-relative-images'
|
||||
import remarkToc from 'remark-toc'
|
||||
import rehypeSlug from 'rehype-slug'
|
||||
import { getHighlighter } from 'shiki'
|
||||
import rehypeKatexSvelte from "rehype-katex-svelte";
|
||||
import rehypeKatexSvelte from "rehype-katex-svelte"
|
||||
import remarkMath from 'remark-math'
|
||||
|
||||
const contentDir = process.env?.CHRYSOBLOG_CONTENT ?? "src/content";
|
||||
|
@ -77,9 +78,12 @@ const config = {
|
|||
}
|
||||
},
|
||||
}),
|
||||
typstPreprocess({
|
||||
extensions: ['.typ'],
|
||||
}),
|
||||
vitePreprocess(),
|
||||
],
|
||||
extensions: [".svelte", ".md"],
|
||||
extensions: [".svelte", ".typ", ".md"],
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
|
|
Loading…
Reference in New Issue