🌱 Stage 1 metaarchitecture

Reveriel:一个交互式知识系统的设计

本文概念: reveriel-architectureknowledge-graphinteractive-componentsmdx-authoringstatic-site-seo

问题:为什么书本不够好

翻开一本教科书。它假设你会从第一页读到最后一页,它不检查你是否真的理解了,它不能回应你的错误。你跳过困难的部分,它不抗议;你误解了一个概念,它不纠正;你失去兴趣,它不挽留。

书是死的。 它在纸上等待你翻阅,而翻阅是一种单向的、不需要反馈的行为。

另一方面,一个好的游戏——比如《传送门》或《Baba Is You》——根本不会给你一本规则手册。它给你一个安全的、可以试错的环境,让你通过来学习。失败没有惩罚,成功有即时的反馈。难度逐步增加,每一个新关卡都建立在上一个关卡学到的技能之上。

🎯 知识挑战 (1 题) 0 / 1

加载挑战中...

这个博客的信仰是:游戏是比死书本更先进的知识载体。 它不是一个”技术博客”——它是一个交互式知识系统,伪装成博客的样子。


整体架构:一个目录 + 一个构建脚本

这个框架没有数据库,没有管理后台,没有黑盒。它的全部输入是文件系统上的一个目录,输出是一堆可以用浏览器打开的 HTML 文件。

作者写的(输入)              框架处理的              读者看到的(输出)
─────────────────      ────────────────      ─────────────────

src/content/posts/                             https://reveriel.com/
  shader-intro/                               /posts/shader-intro/
    index.mdx         →  MDX → HTML       →   带有交互组件的页面
    demos/                                    ShaderPlayground 可编辑
      circle.wgsl     →  资源打包         →   WGSL 代码嵌入页面
      sim.py          →  Pyodide 引用     →   读者浏览器里跑 Python

核心原则只有五个:

  1. 文件即文章。 一个目录 = 一篇文 = 一个自包含的知识单元。目录名就是 URL。
  2. 组件即交互。 需要互动的地方,插入一个组件标签(如 <ShaderPlayground />),而不是写一堆 <script> 标签。
  3. 元数据即图谱。 frontmatter 里的 prerequisitesconcepts 自动生成知识依赖关系。
  4. 按需加载。 WASM、shader、Pyodide 只在使用时加载。首页的速度和纯文本博客一样快。
  5. 对搜索引擎友好。 静态生成 + 语义 HTML + Open Graph 标签 + sitemap,让知识能被发现。

品牌:为什么叫 Reveriel

Reveriel 这个名字来自 “reverie”(沉思 / 幻想)—— 知识不应该是枯燥的、单向的灌输,而应该是一种沉浸式的、可以玩的体验。

每篇文章的页面 title 格式是 {文章标题} — Reveriel,Open Graph 标签里 site_name 是 Reveriel · Interactive Knowledge。这些标签决定了:

  • 搜索引擎结果页上你的链接长什么样
  • 分享到 Twitter / Slack / Telegram 时卡片怎么展示
  • RSS 阅读器里标题怎么显示

Tagline 是 “知识是游戏,阅读是游玩”。这个 tagline 本身就在回击传统的”读书”概念——这里没有书,只有关卡。


目录结构:每篇文章是一个项目

src/content/posts/
  shader-intro/                  ← 目录名 = 文章 slug = URL
    index.mdx                    ← 正文(唯一必需的文件)
    demos/                       ← 属于这篇文章的交互式 demo 代码
      circle.wgsl                ← ShaderPlayground 引用的 shader
      raymarch.wgsl
      attention-viz.ts           ← Canvas 2D 可视化脚本
      ml-sim.py                  ← Pyodide 跑的 Python
    assets/                      ← 静态资源
      diagram.png
      model.glb                  ← 3D 模型

每篇文章的代码资源和文章放在一起,不是散落在全局的 /static//public/ 里。这样你写完一篇文章后,它就是一个完整的、可以独立理解的知识包。

为什么这样设计

传统的 CMS(比如 WordPress)把内容和资源分开:文章在数据库里,图片在一个媒体库,代码片段嵌在 HTML 里。时间长了,它们之间的关系就丢了——你没法把一篇文章的完整上下文(文字 + 代码 + 资源)打包成一个目录复制给别人。

把文章目录当作项目的根意味着:

  • Git 可以精确追踪一篇文章的所有修改(包括代码)
  • 你可以把一篇文章的目录整个复制给别人,对方就能构建和运行
  • 不会有”这张图到底在哪个文章用到了”的问题

内容管线:从 MDX 到交互式页面

数据流是这样的:

index.mdx (你写的)

    ├── YAML frontmatter    →  元数据(title, stage, concepts...)
    │                         →  知识图谱数据库

    ├── Markdown 正文       →  Astro MDX 解析器
    │                         →  Shiki 语法高亮
    │                         →  HTML

    └── 组件标签             →  Astro 组件渲染
        <ShaderPlayground />  →  WebGPU 初始化脚本
        <CodeCell />          →  Pyodide / JS 运行时
        <QuizChain />         →  交互式测验逻辑
        <ProofStep />         →  可折叠的 <details>
        <GiscusComments />    →  GitHub Discussions 评论区

构建时(npm run build),Astro 遍历所有 .mdx 文件,提取 frontmatter,渲染 Markdown 为 HTML,把组件标签替换为真实的交互式组件代码,然后输出纯静态文件到 dist/ 目录。

关键点:输出的是静态 HTML + CSS + JS,不需要服务器。 部署到 GitHub Pages、Cloudflare Pages 或任何一个能托管静态文件的地方就行。


知识图谱:概念 DAG

每篇文章的 frontmatter 里有两个关键字段:

prerequisites:   # 读者在读本文之前应该先理解的概念
  - coordinate-systems
  - vectors
concepts:        # 本文会教给读者的概念
  - signed-distance-functions
  - fragment-shaders

框架在构建时自动分析所有文章的 prerequisitesconcepts,构建一个有向无环图(DAG):

coordinate-systems ──→ fragment-shaders ──→ signed-distance-functions ──→ ray-marching
vectors ──────────────┘                    └── matrices ──────────────────┘

访问 /graph 可以看到这个图谱。读者可以:

  • 看到每个概念的依赖链
  • 按 Stage 递进学习
  • 知道自己已经”解锁”了哪些概念

概念 ID 怎么起

概念 ID 是短横线连接的小写英文,比如 signed-distance-functions。同一个概念 ID 可以在多篇文章中出现——这意味着同一概念有多种解释方式,读者可以选择适合自己的路线。


组件系统:交互是怎么工作的

这个框架提供了一套交互式组件库。每个组件解决一类特定的教学需求:

下面是一个 Shader Playground。试着改里面 WGSL 代码的最后一行的 0.5,改成 0.0,然后按 Ctrl+Enter。

✏️ Shader Playground

你刚才改的是一个 fragment shader 的代码。ShaderPlayground 组件在读者的浏览器里做三件事:

  1. 请求 WebGPU 设备
  2. 把你写的 WGSL 编译为 GPU shader module
  3. 渲染一个全屏四边形,把 shader 的结果画到 canvas 上

如果读者的浏览器不支持 WebGPU,组件会显示降级信息。如果读者根本没有滚动到这个组件的位置,它不会初始化——IntersectionObserver 懒加载

组件一览

组件解决的问题读者体验
<ShaderPlayground>如何直观地理解 shader改代码 → 实时看效果
<CodeCell>代码不是用来”看”的在浏览器里运行 JS / Python
<QuizChain>如何确认读者真的懂了递进式问答,答对解锁下一题
<ProofStep>数学证明容易让人迷失读者自己控制展开节奏
<Simulation>参数变化的意义无法用文字描述拖滑块看实时变化
<WasmDemo>如何跑高性能计算Rust → WASM,按需加载
<GiscusComments>读者想讨论、提问、质疑GitHub Discussions 评论,懒加载

一个 CodeCell 的实例——不只是展示代码,读者可以编辑和运行:

🟨 JavaScript
点击"运行"以执行代码

什么是 Astro 组件?

你可能会问:ShaderPlaygroundCodeCell 这些标签是从哪来的?

它们是 Astro 组件——后缀为 .astro 的文件,存放在 src/components/ 目录下。一个 .astro 文件长这样:

---
// 前半部分(--- 之间):JavaScript / TypeScript,在构建时运行
interface Props {
  width?: number
  height?: number
}
const { width = 640, height = 360 } = Astro.props
---

<!-- 后半部分:HTML 模板,构建时输出为静态 HTML -->
<div class="interactive-block">
  <canvas id="my-canvas" width={width} height={height}></canvas>
</div>

<script>
  // <script> 标签里的 JS 在读者浏览器里运行
  const canvas = document.getElementById('my-canvas')
  // ... WebGPU 初始化 ...
</script>

关键理解: Astro 组件有两个执行环境——

在哪里运行什么时候做什么
--- 之间的代码构建时(服务器/你的电脑上)处理 props、读取文件、生成 HTML
<script> 标签里的代码运行时(读者浏览器里)操作 DOM、初始化 WebGPU、响应用户输入

构建完成后,<script> 之外的模板部分已经变成了纯 HTML 字符串。这也是为什么这个博客不需要服务器——所有的逻辑要么在构建时执行完了,要么在读者浏览器里按需加载。

WASM:在浏览器里跑 Rust

当交互需要的计算量超过 JavaScript 的能力范围时(比如物理模拟、图像处理、数值计算),可以把 Rust 代码编译为 WebAssembly,直接在浏览器里运行。

下面是一个 Mandelbrot 集的 WASM 渲染器。核心计算用 Rust 写,编译为 .wasm 文件,通过 <WasmDemo> 组件加载:

🦀 WASM Demo mandelbrot_bg.wasm

加载 WASM 模块中...

💡 点击画布放大,右键缩小

WASM 的构建流程:

# 1. 在文章目录下创建 Rust 项目
src/content/posts/hello-interactive-world/demos/mandelbrot/
  ├── Cargo.toml
  └── src/lib.rs

# 2. 编译
cd demos/mandelbrot
wasm-pack build --target web --out-dir ../pkg

# 3. 在 MDX 中引用产物
<WasmDemo wasm="./demos/pkg/xxx_bg.wasm" js="./demos/pkg/xxx.js" />

WASM 模块也是懒加载的——只有读者滚动到它的位置时才开始下载和初始化。

SEO:让知识被找到

一个博客如果没有人读到,交互做得再好也没有意义。Reveriel 从第一天起就内置了 SEO 基础设施:

每篇文章自动生成:

  • <title><meta description>(从 frontmatter summary 读取)
  • Open Graph 标签og:title, og:description, og:image, og:type: article)—— 决定分享到社交媒体的卡片
  • Twitter Cardsummary_large_image)—— Twitter / Slack / Telegram 的链接预览
  • article:published_timearticle:tag —— 搜索引擎的结构化时间 + 标签
  • 语义 HTML(<article>, <header>, <time>, <nav>)—— 搜索引擎理解页面结构

待规划:

  • sitemap.xml@astrojs/sitemap 自动生成
  • RSS Feed — @astrojs/rss 让文章被 RSS 阅读器抓取
  • JSON-LD 结构化数据 — Google Rich Results(Article, BreadcrumbList

评论区:知识需要讨论

每篇文章底部集成了 Giscus——一个基于 GitHub Discussions 的评论系统:

  • 读者用 GitHub 账号登录 → 在文章下留言
  • 评论数据存在你的 GitHub repo 的 Discussions 里(不依赖第三方数据库)
  • 通过 IntersectionObserver 懒加载——不滚动到评论区不加载脚本
  • 暗色主题,中文界面

如果没有配置 Giscus,评论区显示提示文字,不影响页面。


系列(Series):线性学习路径

有些知识天然的应该按顺序阅读。比如一篇”Shader 入门”之后是”SDF 画形状”,然后是”光线步进做 3D 渲染”——这三篇构成了一个线性系列。

在文章的 frontmatter 里声明系列:

---
title: "SDF 入门"
series: "shader-from-scratch"
seriesOrder: 2    # 在系列中的位置
---

同一个系列的所有文章会被自动收集和排序。访问 /graph?series=shader-from-scratch 可以看到这个系列的线性阅读顺序。

Stage 和 Series 是正交的:

  • Stage 描述难度(1→5 递进,跨系列的)
  • Series 描述主题(同一主题的线性阅读顺序)

一篇 Stage 4 的文章可能属于某个系列的第 3 篇——说明这个系列到后面会变得很难。


递进式难度:Stage 系统

不是所有知识都应该用同样的交互密度呈现。框架定义了 5 个 Stage:

Stage 1 🌱:  90% 阅读 + 10% 演示          (读者刚入门,先看)
Stage 2 🌿:  70% 阅读 + 20% 交互 + 10% 挑战  (开始动手)
Stage 3 🌳:  40% 阅读 + 40% 交互 + 20% 挑战  (动手为主)
Stage 4 ⚡:  20% 阅读 + 50% 交互 + 30% 挑战  (综合运用)
Stage 5 🔥:  10% 方向 + 70% 自由探索 + 20% 创造 (读者自己探索前沿)

脚手架的逐渐撤走是游戏设计的核心原则:

  • Stage 1 的文章几乎没有前置知识要求,交互以”看”为主
  • Stage 3 的文章开始有递归的前置知识链,交互以”做”为主
  • Stage 5 的文章给出方向而不是答案——读者应该能自己写一个 path tracer,而不是跟着教程敲

这篇文章本身是 Stage 1。它不需要任何前置知识,交互密度很低——你只需要读。


一个完整的文章是怎么写出来的

假设你要写一篇”Shader 入门:从像素理解 GPU”。你做的事情:

第一步:创建目录

mkdir -p src/content/posts/shader-from-pixels/demos
touch src/content/posts/shader-from-pixels/index.mdx

第二步:写 frontmatter

---
title: "Shader 入门:从像素理解 GPU"
date: 2026-07-30
stage: 1
prerequisites: []
concepts: [fragment-shaders, gpu-paradigm, uv-coordinates]
tags: [graphics, shader]
summary: "每一个像素都是一个独立的小程序。理解这一点是图形学的第一道门。"
---

第三步:写正文 + 嵌入组件

import ShaderPlayground from '@components/ShaderPlayground.astro'

## 像素是并行的

当你在屏幕上看到 1920×1080 个像素时,GPU 并没有一个一个地计算它们的颜色。
每个像素有自己的"微型程序"在同时运行。

<ShaderPlayground shader="./demos/first-pixel.wgsl" editable />

第四步:把 shader 代码放在 demos/ 里

// demos/first-pixel.wgsl
@fragment
fn main(@builtin(position) coord: vec4f) -> @location(0) vec4f {
  let uv = coord.xy / vec2f(1920.0, 1080.0);
  return vec4f(uv.x, uv.y, 0.5, 1.0);
}

第五步:npm run build → 部署


证明:这个设计是正确的

📐 证明:文件驱动的交互式博客比数据库驱动的 CMS 更适合知识传播

前提 1:知识应该可复现。 一篇文章 + 它的交互式代码应该构成一个封闭的、可执行的系统。读文章的人可以在自己浏览器里重新运行文章中的所有计算。

前提 2:知识应该有结构。 概念之间有前置依赖关系。一个读者不知道”向量”就看不懂”矩阵乘法”——这个依赖关系应该被显式建模,而不是靠读者自己猜。

前提 3:知识应该被验证。 读完不等于学会。交互式挑战(QuizChain、CodeCell 的修改任务)让读者运用刚学到的概念,从而确认自己真的理解了。

文件驱动(而不是数据库驱动)满足前提 1:文章目录是自包含的、可版本控制的、可复制的。知识图谱满足前提 2。交互式组件满足前提 3。

因此,这个设计比传统的 Markdown → HTML 静态博客更适合知识传播。∎


接下来

以下是 Reveriel 的路线图:

状态内容说明
✅ 完成框架骨架Astro 5 + MDX + Shiki 高亮
✅ 完成交互式组件库ShaderPlayground, CodeCell, QuizChain, ProofStep, WasmDemo, Simulation
✅ 完成知识图谱概念 DAG + Stage 分组 + Series 过滤,/graph 可视化
✅ 完成品牌 & SEOOG / Twitter Card 标签、语义 HTML、Giscus 评论系统
✅ 完成旧文章迁移10 篇非数学类文章已迁入,数学类文章留在 archive/ 待 math 渲染方案就绪
🔜 现在sitemap + RSS@astrojs/sitemap + @astrojs/rss
🔜 现在第一篇真正的交互式文章Shader 入门或线性代数可视化
📋 近期数学渲染解决 MDX + LaTeX 的构建时解析问题,迁入 archive/ 里的数学文章
📋 近期WebGPU compute shader不只是渲染,还能在 GPU 上做并行计算
📋 远期读者进度追踪localStorage 记录已解锁的概念
📋 远期多语言支持概念 ID 跨语言共享,zh.mdx / en.mdx

Reveriel 不是用来展示技术栈的。 它探索一个问题:如果知识不是印在纸上等你读,而是像一个精心设计的游戏一样等你玩——会发生什么?

💬 评论加载中...