You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:06:19