如何为Quarto的Callout块创建带共享计数器的自定义交叉引用?
问题描述
使用Quarto编写在线书籍时,需要实现五种自定义带框环境(示例、练习、备注、定理、定义),要求章节内共享计数器,并支持文本交叉引用。尝试使用样式优良的Callout Blocks实现,但无法创建满足需求的共享自定义计数器,之前也尝试过Quarto的标准amsthm环境。
尝试的代码示例(效果不符合预期):
第一个定义是@callout-1。 :::{.callout-note} ## Definition {#callout-1} 这应为定义1.1。 ::: 随后是第一个示例,@callout-2 :::{.callout-tip} ## Example {#callout-2} 这应为示例1.2。 :::
可行解决方案
方案一:自定义Callout块 + Lua过滤器实现共享计数器
通过Lua过滤器统一管理共享计数器,自动为各类Callout生成章节内连续编号,并生成可交叉引用的标签,同时保留原生Callout的样式。
步骤1:创建Lua过滤器文件
新建shared-callout-counter.lua文件,内容如下:
local counter = {} function Div(el) -- 匹配自定义的callout环境类(可按需调整) local env_types = { ["callout-definition"] = "Definition", ["callout-example"] = "Example", ["callout-exercise"] = "Exercise", ["callout-note"] = "Note", ["callout-theorem"] = "Theorem" } for class, title in pairs(env_types) do if el.classes:includes(class) then -- 初始化章节计数器 local chapter = quarto.doc.current_ref_context().chapter if not counter[chapter] then counter[chapter] = 0 end counter[chapter] = counter[chapter] + 1 -- 生成带编号的标题(格式:类型 章节.编号) local numbered_title = string.format("%s %d.%d", title, chapter, counter[chapter]) -- 生成引用ID local ref_id = string.format("callout-%d-%d", chapter, counter[chapter]) -- 替换Callout内的标题 for i, block in ipairs(el.content) do if block.t == "Header" and block.level == 2 then block.content = pandoc.Str(numbered_title) block.attributes["id"] = ref_id el.content[i] = block break end end -- 为Div添加引用ID(可选,方便直接引用块) el.attributes["id"] = ref_id return el end end return el end
步骤2:在Quarto文档中引用过滤器
在文档的YAML头部添加:
filters: - shared-callout-counter.lua
步骤3:在文档中使用自定义Callout
第一个定义是@callout-1-1。 :::{.callout-definition} ## Definition 这是定义1.1。 ::: 随后是第一个示例,@callout-1-2 :::{.callout-example} ## Example 这是示例1.2。 :::
渲染后会自动生成章节内连续的编号,且@callout-章节号-序号可正确交叉引用。
方案二:基于amsthm环境适配Callout样式
利用amsthm的共享计数器功能,再通过CSS将amsthm环境的样式修改为与原生Callout一致,兼顾计数器/引用功能和视觉效果。
步骤1:配置amsthm共享计数器
在文档YAML头部添加:
format: html: amsthm: theoremstyle: plain environments: - name: definition title: Definition within: section shared: true - name: example title: Example within: section shared: true - name: exercise title: Exercise within: section shared: true - name: note title: Note within: section shared: true - name: theorem title: Theorem within: section shared: true
shared: true表示所有环境共享同一个章节内的计数器。
步骤2:添加CSS适配Callout样式
在文档中添加CSS块,将amsthm盒子样式改成Callout风格:
<style> /* 基础Callout样式适配 */ .amsthm { padding: 1rem; margin: 1rem 0; border-radius: 0.5rem; border-left: 4px solid; } /* 不同环境对应不同颜色(匹配原生Callout) */ .amsthm.definition { background-color: #f8f9fa; border-color: #0d6efd; } .amsthm.example { background-color: #f0fdf4; border-color: #10b981; } .amsthm.exercise { background-color: #fffbeb; border-color: #f59e0b; } .amsthm.note { background-color: #f8f9fa; border-color: #6c757d; } .amsthm.theorem { background-color: #f0f9ff; border-color: #3b82f6; } /* 标题样式调整 */ .amsthm .amsthm-title { font-weight: bold; margin-bottom: 0.5rem; } </style>
步骤3:使用环境并交叉引用
第一个定义是@definition-1。 :::{definition} 这是定义1.1。 ::: 随后是第一个示例,@example-2 :::{example} 这是示例1.2。 :::
渲染后既拥有amsthm的共享计数器和可靠引用,又具备与原生Callout一致的视觉效果。
内容的提问来源于stack exchange,提问作者Achim Zeileis
相关产品推荐
相关产品推荐

