:PROPERTIES:
:ID: 25156589-749c-4de1-9304-b3f8f2894b88
:EXPORT_FILE_NAME: notes/org-syntax-showcase
:LOAM_KIND: note
:LOAM_STATUS: stable
:LOAM_ORDER: 10
:LOAM_PUBLISHED_AT: 2026-08-08
:END:
#+TITLE: Org 语法陈列室
#+DESCRIPTION: ox-edn 和 Loam 当前支持的 Org 结构，也是内容管线的真实回归测试。
#+FILETAGS: :org:emacs:loam:testing:

这个页面专门检验 Org 语义能否走完整条发布管线。它尽可能多地使用 strict renderer 已经支持的 Org 结构，既是展示，也是留在真实内容里的回归测试。以后哪次修改让这里坏掉，内容管线也就退化了。

下面的结构都来自 Org AST，并经过 ox-edn 和 Loam 的正常路径；页面没有直接插入任意 HTML。

* 行内语义
:PROPERTIES:
:CUSTOM_ID: org-showcase-inline
:END:

Org 会为行内标记保留各自的节点。比如：*粗体*、/斜体/、_下划线_、+删除线+、~code~ 与 =verbatim= 在 AST 中都有不同的类型。

上下标也保留结构：水可以写成 H_{2}O，平方可以写成 x^{2}。Org entity 也能进入 AST，例如 \alpha、\beta 和 \rightarrow；这一行的末尾还故意放一个显式换行，\\
这句话因此从新的一行开始，换行来自 Org 节点。

键盘操作可以用专门的宏表示，例如 {{{kbd(C-c C-c)}}}、{{{kbd(M-x)}}} 和 {{{kbd(RET)}}}。现在 Loam 只允许直接渲染 =kbd=；未知 macro 会产生 deferred diagnostic。

时间戳也是语义节点：active timestamp 是 <2026-08-08 Sat>，inactive timestamp 是 [2026-08-08 Sat]。

* 链接、锚点和跨页关系
:PROPERTIES:
:CUSTOM_ID: org-showcase-links
:END:

普通外链可以直接写成 [[https://orgmode.org/][Org Mode]]。站内关系则来自 Org ID：这条 [[id:034150e3-07f3-44a9-946b-d70dee8586ba][Majutsu]] 链接会让 Loam 生成 outgoing link、backlink 和 graph edge。

跨文章也不需要退回手写 URL。这里先用 [[id:0d3edb2c-00ce-4e89-8f69-bcd6acd34bb3][建站文章]] 指向另一篇 Org 页面本身；再用 [[页面身份、内容修订和构建结果分开计算][跨文章 fuzzy link]] 直接指向那篇文章里的具体 headline。两条链接指向同一页面，但属于两个独立的 backlink occurrence。

同一页也可以链接到稳定的 =CUSTOM_ID=，例如直接跳到 [[#org-showcase-table][下面的表格示例]]。

Org 还有显式 target。这里放一个不可见但可寻址的 <<org-showcase-target>> target，然后这条 [[org-showcase-target][fuzzy link]] 再跳回来。下面这个 <<<radio target>>> 属于另一种 Org 原生锚点类型，有自己的解析规则。

* 列表的多种结构
:PROPERTIES:
:CUSTOM_ID: org-showcase-lists
:END:

无序列表可以嵌套：

- 内容层
  - Org source
  - ox-edn Envelope
- 编译层
  - Loam index
  - link resolution
- 表现层
  - SvelteKit
  - static HTML

有序列表也保留顺序：

1. parse Org；
2. validate semantics；
3. resolve the corpus；
4. render artifacts。

Checkbox 状态保存在 item 节点上：

- [X] Org 仍然是 source of truth
- [-] 语法覆盖仍在继续扩展
- [ ] citation 和部分更少用的 Org 节点还没有进入 release-grade renderer

description list 则适合写术语：

- =ID= :: 全局稳定 UUID，用于 =id:= link。
- =CUSTOM_ID= :: 人类可读的局部 anchor。
- =EXPORT_FILE_NAME= :: 发布路径，不承担内容身份。

* Quote、verse、fixed-width 和 center
:PROPERTIES:
:CUSTOM_ID: org-showcase-blocks
:END:

#+begin_quote
内容管线应该保留作者写下的结构，不满足于一段「看起来差不多」的文字。
#+end_quote

Verse block 会保留人为控制的换行：

#+begin_verse
Org 是源，
AST 是边界，
链接在 corpus 中重新相遇，
最后才轮到浏览器。
#+end_verse

Fixed-width 很适合不需要 syntax highlighting、但又必须保留空白的文本：

: Org
:   -> ox-edn
:      -> Loam
:         -> SvelteKit

#+begin_center
这一句来自 Org 的 center block，源码里没有手写 ~<div style="text-align:center">~。
#+end_center

* Callout / special block
:PROPERTIES:
:CUSTOM_ID: org-showcase-callouts
:END:

Loam 通过 allowlist 约束 special block，当前允许 =note=、=tip=、=warning=、=danger=、=experimental= 和 =compatibility=。扩展语法因此有了明确边界。

#+begin_note
这是一个 note。适合补充背景，而不打断正文主线。
#+end_note

#+begin_tip
这是一个 tip。比如：文章自己的 =ID= 应该使用 UUID，而 URL 交给 =EXPORT_FILE_NAME=。
#+end_tip

#+begin_warning
这是一个 warning。把整个知识库自动公开，比少写一个 feature 危险得多。
#+end_warning

#+begin_danger
这是一个 danger。strict renderer 会拒绝任意 =#+begin_export html=，阻止它直接进入页面。
#+end_danger

#+begin_experimental
这是一个 experimental block。之后加入 interactive island 时，我希望先定义对应的 Org 语义，让 Svelte 组件名留在 Web 层。
#+end_experimental

#+begin_compatibility
这是 compatibility block。它适合记录协议、版本或旧内容迁移相关的兼容说明。
#+end_compatibility

* LaTeX：让 Emacs 先把公式变成 SVG
:PROPERTIES:
:CUSTOM_ID: org-showcase-latex
:END:

LaTeX 在构建阶段完成排版。比如行内公式 $e^{i\pi}+1=0$ 会先交给 Org 的 LaTeX preview 系统，网页直接拿到 SVG，无需等待 MathJax 或 KaTeX。

Display math 也走同一条路径：

\[
\int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}
\]

完整的 LaTeX environment 同样可以成为 Org AST 节点：

\begin{equation}
\nabla \cdot \mathbf{E} = \frac{\rho}{\varepsilon_0}
\end{equation}

这里直接复用 Org 10 的 =org-latex-preview-cache-images=。它会根据 preamble、公式内容、转换器、背景和 equation number 等信息计算 preview key，然后查询 =org-persist=；只有 cache miss 才启动 LaTeX 和 =dvisvgm=。dvisvgm 生成的 SVG 又会把公式前景变成 =currentColor=，所以 Loam 可以严格清洗 SVG，再把它 inline 到正文中并继承网站的明暗主题。

缓存留在 Emacs/Org 的编译环境，SVG 进入最终内容产物。Envelope 里不会出现 =~/.config/emacs/.local/cache/org/persist/...= 这样的本机路径。

* Source block、example 和不同语言
:PROPERTIES:
:CUSTOM_ID: org-showcase-source
:END:

Source block 会保留语言、font-lock face 和字符区间。构建时，Emacs 根据 =org-src-get-lang-mode= 选择 major mode，ox-edn 再把结果写进 Envelope；Loam 把这些区间安全地变成 =span=，网站主题决定颜色。下面的 Svelte 由我配置里的 =svelte-ts-mode= 处理：

#+begin_src emacs-lisp
(defun publish-current-org-file ()
  "The real pipeline is deliberately less magical than this demo."
  (interactive)
  (message "Org -> ox-edn -> Loam -> SvelteKit"))
#+end_src

#+begin_src clojure
(-> documents
    validate
    partition-pages
    build-index
    resolve-links
    render-page-fragments)
#+end_src

#+begin_src svelte
{#each page.backlinks as backlink}
  <a href={backlink.route}>{backlink.title}</a>
{/each}
#+end_src

Example block 用于原样示例文本，其中的内容不需要语言高亮：

#+begin_example
manifest.json
search-index.json
graph.json
pages/*.html
source/**/*.org
#+end_example

* 表格
:PROPERTIES:
:CUSTOM_ID: org-showcase-table
:END:

Org table 会生成带有行列结构的 HTML table：

| 层 | 输入 | 输出 | 是否理解全站关系 |
|----+------+------|------------------|
| ox-edn | 单份 Org | Envelope v1 | 否 |
| Loam | 全部 Envelope | Manifest / Graph / HTML | 是 |
| SvelteKit | 编译产物 | Static site | 不重新解释 |

* 脚注
:PROPERTIES:
:CUSTOM_ID: org-showcase-footnotes
:END:

脚注也有自己的 AST 节点。比如这句话后面有一个具名脚注[fn:semantic-source]，渲染器会根据节点生成脚注标记。

[fn:semantic-source] 「Org 是 source of truth」指下游直接使用这份 =.org= 文件，不再维护 Markdown 内容副本。

* Horizontal rule，以及「隐藏也是语义」
:PROPERTIES:
:CUSTOM_ID: org-showcase-hidden
:DEMO_METADATA:
:owner: building-this-site
:purpose: prove-that-drawers-stay-hidden
:END:

上面这个 heading 的 source 里故意带了一个 drawer。Loam 知道它存在，但不会把内部 metadata 渲染进公开正文。它仍然保留在公开的 Org source 中。下面还有 Org comment 和 comment block，它们同样不会进入正文。

# 这是一条只留在源文件中的 Org comment。

#+begin_comment
如果你在最终网页正文中看见这段话，说明 hide disposition 失效了。
#+end_comment

-----

这条横线来自 Org horizontal rule 节点。

radio target
