wip: inline diagram SVG

This commit is contained in:
2026-01-12 18:32:47 +00:00
parent 4b72967556
commit 5739a3c998
35 changed files with 99 additions and 104 deletions

View File

@@ -30,6 +30,7 @@
"eslint-plugin-astro": "^1.5.0",
"prettier": "^3.7.4",
"prettier-plugin-astro": "^0.14.1",
"svgo": "^4.0.0",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3",
"typescript-eslint": "^8.52.0",

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark");
}
})();
</script><link rel="stylesheet" href="/_astro/index.D1CCjkm0.css"></head> <body class="min-h-screen"> <div class="flex flex-col items-center justify-center min-h-screen p-8"> <div class="text-center"> <h1 class="text-[8rem] sm:text-[12rem] font-display font-bold leading-none
</script><link rel="stylesheet" href="/_astro/index.fpsHJ0rs.css"></head> <body class="min-h-screen"> <div class="flex flex-col items-center justify-center min-h-screen p-8"> <div class="text-center"> <h1 class="text-[8rem] sm:text-[12rem] font-display font-bold leading-none
text-gray-300 dark:text-neutral-700">
404
</h1> <p class="text-xl mb-2 text-gray-600 dark:text-neutral-400">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark");
}
})();
</script><link rel="stylesheet" href="/_astro/index.D1CCjkm0.css">
</script><link rel="stylesheet" href="/_astro/index.fpsHJ0rs.css">
<style>.dark .shiki{color:var(--shiki-dark)!important;background-color:var(--shiki-dark-bg)!important}.dark .shiki span{color:var(--shiki-dark)!important}
</style></head> <body class="min-h-screen"> <header class="sticky top-0 z-50 border-b
border-gray-200 dark:border-neutral-800

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark");
}
})();
</script><link rel="stylesheet" href="/_astro/index.D1CCjkm0.css">
</script><link rel="stylesheet" href="/_astro/index.fpsHJ0rs.css">
<style>.dark .shiki{color:var(--shiki-dark)!important;background-color:var(--shiki-dark-bg)!important}.dark .shiki span{color:var(--shiki-dark)!important}
</style></head> <body class="min-h-screen"> <header class="sticky top-0 z-50 border-b
border-gray-200 dark:border-neutral-800

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark");
}
})();
</script><link rel="stylesheet" href="/_astro/index.D1CCjkm0.css">
</script><link rel="stylesheet" href="/_astro/index.fpsHJ0rs.css">
<style>.dark .shiki{color:var(--shiki-dark)!important;background-color:var(--shiki-dark-bg)!important}.dark .shiki span{color:var(--shiki-dark)!important}
</style></head> <body class="min-h-screen"> <header class="sticky top-0 z-50 border-b
border-gray-200 dark:border-neutral-800

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark");
}
})();
</script><link rel="stylesheet" href="/_astro/index.D1CCjkm0.css">
</script><link rel="stylesheet" href="/_astro/index.fpsHJ0rs.css">
<style>.dark .shiki{color:var(--shiki-dark)!important;background-color:var(--shiki-dark-bg)!important}.dark .shiki span{color:var(--shiki-dark)!important}
</style></head> <body class="min-h-screen"> <header class="sticky top-0 z-50 border-b
border-gray-200 dark:border-neutral-800

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark");
}
})();
</script><link rel="stylesheet" href="/_astro/index.D1CCjkm0.css">
</script><link rel="stylesheet" href="/_astro/index.fpsHJ0rs.css">
<style>.dark .shiki{color:var(--shiki-dark)!important;background-color:var(--shiki-dark-bg)!important}.dark .shiki span{color:var(--shiki-dark)!important}
</style></head> <body class="min-h-screen"> <header class="sticky top-0 z-50 border-b
border-gray-200 dark:border-neutral-800

View File

@@ -39,6 +39,7 @@
"eslint-plugin-astro": "^1.5.0",
"prettier": "^3.7.4",
"prettier-plugin-astro": "^0.14.1",
"svgo": "^4.0.0",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3",
"typescript-eslint": "^8.52.0"

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -9,6 +9,7 @@ import * as fs from "node:fs";
import * as path from "node:path";
import * as semver from "semver";
import { $ } from "bun";
import { optimize as optimizeSvg } from "svgo";
import { config } from "../src/config";
const updateConfig = {
@@ -18,7 +19,6 @@ version: {{version}}
---
{{content}}`,
outputDir: "src/content/spec",
publicDir: "public/spec",
};
/**
@@ -119,22 +119,20 @@ function removeStaleSpecs(versionsToKeep: string[]): void {
const keepSet = new Set(versionsToKeep);
let removedAny = false;
for (const dir of [updateConfig.outputDir, updateConfig.publicDir]) {
if (!fs.existsSync(dir)) continue;
if (!fs.existsSync(updateConfig.outputDir)) return;
const files = fs.readdirSync(dir);
for (const file of files) {
// Extract version from filename (e.g., "1.0.0-rc.1.md" -> "1.0.0-rc.1")
const version = path.basename(file, path.extname(file));
if (!keepSet.has(version)) {
if (!removedAny) {
console.log("\nRemoving stale spec files:");
removedAny = true;
}
const filePath = path.join(dir, file);
fs.unlinkSync(filePath);
console.log(` ${filePath}`);
const files = fs.readdirSync(updateConfig.outputDir);
for (const file of files) {
// Extract version from filename (e.g., "1.0.0-rc.1.md" -> "1.0.0-rc.1")
const version = path.basename(file, path.extname(file));
if (!keepSet.has(version)) {
if (!removedAny) {
console.log("\nRemoving stale spec files:");
removedAny = true;
}
const filePath = path.join(updateConfig.outputDir, file);
fs.unlinkSync(filePath);
console.log(` ${filePath}`);
}
}
@@ -211,10 +209,11 @@ async function main(): Promise<void> {
const mdPath = path.join(updateConfig.outputDir, `${version}.md`);
writeFile(mdPath, spec.body);
// Write SVG diagram to public directory
// Write SVG diagram next to markdown (with metadata stripped)
if (spec.diagram) {
const svgPath = path.join(updateConfig.publicDir, `${version}.svg`);
writeFile(svgPath, spec.diagram);
const svgPath = path.join(updateConfig.outputDir, `${version}.svg`);
const optimizedSvg = optimizeSvg(spec.diagram).data;
writeFile(svgPath, optimizedSvg);
}
} catch (error) {
console.error(`Error processing version ${version}:`, error);

View File

@@ -7,10 +7,10 @@ import { config } from "../config";
interface Props {
version: string;
versions: string[];
svgPath: string;
svgContent?: string | null;
}
const { version, versions, svgPath } = Astro.props;
const { version, versions, svgContent } = Astro.props;
const navItems = [
{ id: "about", label: "About" },
@@ -112,20 +112,23 @@ const secondaryClasses = `text-gray-600 dark:text-neutral-400
</div>
<!-- SVG Diagram -->
<div
class="animate-fade-in-up delay-300
relative mx-auto mb-12 py-8 px-6 sm:py-16 sm:px-14
bg-white dark:bg-neutral-900
rounded-2xl shadow-lg dark:shadow-none
border border-gray-200 dark:border-neutral-800"
>
<img
src={svgPath}
alt="Git Common-Flow diagram"
class="w-full h-auto max-w-3xl mx-auto
dark:invert dark:hue-rotate-180 dark:contrast-90"
/>
</div>
{
svgContent && (
<div
class="animate-fade-in-up delay-300
relative mx-auto mb-12 py-8 px-6 sm:py-16 sm:px-14
bg-white dark:bg-neutral-900
rounded-2xl shadow-lg dark:shadow-none
border border-gray-200 dark:border-neutral-800"
>
<div
class="w-full max-w-3xl mx-auto [&>svg]:w-full [&>svg]:h-auto
dark:invert dark:hue-rotate-180 dark:contrast-90"
set:html={svgContent}
/>
</div>
)
}
<!-- Navigation links -->
<nav

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -28,14 +28,25 @@ const content = fs.readFileSync(filePath, "utf-8");
const markdown = content.replace(/^---[\s\S]*?---\n/, "");
// Parse the content into sections (handles markdown -> HTML internally)
const parsed = await parseSpecContent(markdown, version);
const parsed = await parseSpecContent(markdown);
// Read SVG content for inline embedding
const svgFilePath = path.join(process.cwd(), "src/content/spec", `${version}.svg`);
let svgContent: string | null = null;
if (fs.existsSync(svgFilePath)) {
svgContent = fs.readFileSync(svgFilePath, "utf-8");
}
---
<BaseLayout title={spec.data.title}>
<Header version={version} versions={versions} />
<main>
<Hero version={version} versions={versions} svgPath={parsed.svgPath} />
<Hero
version={version}
versions={versions}
svgContent={svgContent}
/>
<AboutSection
introduction={parsed.introduction}

View File

@@ -32,7 +32,6 @@ export interface SpecSection {
}
export interface ParsedSpec {
svgPath: string;
introduction: string;
summary: string;
terminology: string;
@@ -340,10 +339,7 @@ function buildTocItems(parsed: Partial<ParsedSpec>): TocItem[] {
*/
export async function parseSpecContent(
markdown: string,
version: string,
): Promise<ParsedSpec> {
const svgPath = `/spec/${version}.svg`;
// Parse markdown to AST
const tree = unified().use(remarkParse).parse(markdown) as Root;
@@ -423,7 +419,6 @@ export async function parseSpecContent(
}));
const parsed: ParsedSpec = {
svgPath,
introduction,
summary,
terminology,