Your first interactive .md

跟着这篇做完,你会有一个真实可用的复利计算器——含 3 个滑块、自动计算的派生值、随滑块实时重绘的 chart。 全程不写一行 JS,只写 markdown。 预计时间:10 分钟。


你需要的

  • 任意 markdown 编辑器(VS Code / Sublime / Obsidian / 普通文本编辑器都行)
  • Rho MD app(免费——Windows、Linux、Android)

还没装 Rho?参考 Install Rho app。


我们要做什么

一个复利计算器:

输入本金、年利率、年限 → 实时显示终值 / 净收益 / 增长曲线。

最终它会长这样(伪图):

┌─────────────────────────────────────┐
│ 本金     [────●────────] $10,000   │
│ 年利率   [──●──────────] 5.0%      │
│ 年限     [──────●──────] 10 年     │
├─────────────────────────────────────┤
│ 复利终值 = $16,289                  │
│ 净收益   = $6,289                   │
│                                     │
│ [增长曲线 chart 实时重绘]           │
└─────────────────────────────────────┘

我们不一次性写完——分 6 步加,每步加一个能力。


Step 1:从纯 markdown 开始

先写一份完全没有交互的复利说明文档。新建 compound.md:

# 复利计算器

复利是把上期利息加进本金、下期一起再生利息的计算方式。

公式:

```
终值 = 本金 × (1 + 利率)^年限
```

例子:本金 $10,000、年利率 5%、年限 10 年 → 终值 ≈ $16,289。

在 Rho MD 里打开看效果——只是个普通文档。

读者要算自己的情况,得自己拿计算器。我们要改这个。


Step 2:用 callout 强调公式

公式是核心,单独标出来。把公式段改成 Callout:

# 复利计算器

复利是把上期利息加进本金、下期一起再生利息的计算方式。

> [!INFO]
> **公式**:终值 = 本金 × (1 + 利率)^年限

例子:本金 $10,000、年利率 5%、年限 10 年 → 终值 ≈ $16,289。

刷新看效果——公式现在是蓝色高亮块,扫页时一眼就找得到。

这步学到的:callout 是给"读者必读"的内容加视觉权重。详见 Callout 文档。


Step 3:把"本金"变成滑块

把"$10,000"从死数字变成可拖动滑块。在公式下面加一个 interact 块:

> [!INFO]
> **公式**:终值 = 本金 × (1 + 利率)^年限

```interact
slider principal 1000 100000 10000 1000
template stl:
[本金] -> [${principal:.0f}]
```

刷新——现在屏幕上多了一行可拖动的滑块和一行自动更新的文本:"本金 = $10,000"。拖滑块 → 数字实时变。

语法解读:

  • slider principal 1000 100000 10000 1000 创建一个滑块:变量名 principal,最小 1000,最大 100000,初始值 10000,步长 1000
  • template stl: 告诉 Rho 接下来是 STL 输出模板
  • ${principal:.0f} 是 mini DSL 表达式——principal 是滑块的当前值,:.0f 是格式化(0 位小数)

这步学到的:interact 块 = 控件 + 模板。控件驱动模板里的 {var} 占位符。详见 Interact controls 文档。


Step 4:加利率 + 年限滑块 + computed 派生

继续扩展同一个 interact 块——加两个滑块 + 一个 computed 派生值:

```interact
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
computed compound = principal * pow(1+rate, years)
computed gain = compound - principal
template stl:
[本金] -> [${principal:.0f}] ::mod(rate="{(rate*100):.1f}%", years="{years} 年")
[复利终值] -> [${compound:.0f}]
[净收益] -> [${gain:.0f}]
```

刷新——现在有 3 个滑块 + 3 行实时输出。拖任何一个 slider,所有 3 行都重算重显。

新概念:

  • computed compound = principal * pow(1+rate, years) —— 派生变量,自动跟随源滑块更新
  • 派生值可以基于派生值(gain 用了 compound)—— Rho 自动算依赖顺序
  • 表达式 mini DSL 支持 + - * / pow sqrt sin cos pi log exp 等基础运算

⚠️ 注意 mini DSL 不是 JavaScript——它只支持安全的纯函数运算,没有 eval、没有 IO、没有 DOM 访问。这是 Rho 的安全设计。

这步学到的:computed 让交互不止"显示输入值"——能真正算东西。详见 Computed 文档。


Step 5:加 live chart

数字看着满意,但长期增长有多陡?加一个随利率/年限实时重绘的 chart。在同一 interact 块里把 template 从 stl 换成 vega-lite,或者新加一个块:

```interact
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
computed compound = principal * pow(1+rate, years)
computed gain = compound - principal
template stl:
[本金] -> [${principal:.0f}] ::mod(rate="{(rate*100):.1f}%", years="{years} 年")
[复利终值] -> [${compound:.0f}]
[净收益] -> [${gain:.0f}]
```

```interact
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
template vega-lite:
{
  "data": {"sequence": {"start": 0, "stop": {years}, "step": 1, "as": "year"}},
  "transform": [
    {"calculate": "{principal} * pow(1+{rate}, datum.year)", "as": "value"}
  ],
  "mark": {"type": "line", "point": true},
  "encoding": {
    "x": {"field": "year", "type": "quantitative", "title": "年份"},
    "y": {"field": "value", "type": "quantitative", "title": "金额 ($)"}
  }
}
```

刷新——现在第二块是一条增长曲线,年份做 x、金额做 y。拖任何滑块,曲线实时重绘。

问题:滑块写了两遍(每个块一份),而且两个块的滑块是独立的——拖第一块的滑块不影响第二块的 chart。

下一步解决。


Step 6:用 namespace 共享 state

加一行 namespace finance 让两个块共享同一组变量:

```interact namespace finance
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
computed compound = principal * pow(1+rate, years)
computed gain = compound - principal
template stl:
[本金] -> [${principal:.0f}] ::mod(rate="{(rate*100):.1f}%", years="{years} 年")
[复利终值] -> [${compound:.0f}]
[净收益] -> [${gain:.0f}]
```

```interact namespace finance
template vega-lite:
{
  "data": {"sequence": {"start": 0, "stop": {years}, "step": 1, "as": "year"}},
  "transform": [
    {"calculate": "{principal} * pow(1+{rate}, datum.year)", "as": "value"}
  ],
  "mark": {"type": "line", "point": true},
  "encoding": {
    "x": {"field": "year", "type": "quantitative", "title": "年份"},
    "y": {"field": "value", "type": "quantitative", "title": "金额 ($)"}
  }
}
```

注意第二块只有 template,没有 sliders / computed——它复用 namespace finance 里第一块定义的所有变量。

刷新——现在拖任何一个滑块,文本和 chart 同时更新。

这步学到的:namespace 让多个 interact 块共享 state——避免重复定义。详见 Shared state 文档。


Step 7(可选):用 layout 把它做漂亮

最后用 Layout grid + cards 把控件 + 输出 + chart 按 2 列布局:

# 复利计算器

> [!INFO]
> **公式**:终值 = 本金 × (1 + 利率)^年限

```layout grid cols=2

:::card accent=blue
**输入参数**

\```interact namespace finance
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
computed compound = principal * pow(1+rate, years)
computed gain = compound - principal
template stl:
[本金] -> [${principal:.0f}]
[复利终值] -> [${compound:.0f}]
[净收益] -> [${gain:.0f}]
\```
:::

:::card accent=green
**增长曲线**

\```interact namespace finance
template vega-lite:
{
  "data": {"sequence": {"start": 0, "stop": {years}, "step": 1, "as": "year"}},
  "transform": [{"calculate": "{principal} * pow(1+{rate}, datum.year)", "as": "value"}],
  "mark": {"type": "line", "point": true},
  "encoding": {"x": {"field": "year"}, "y": {"field": "value", "type": "quantitative"}}
}
\```
:::
```

刷新——现在它像个产品了:左侧蓝卡控件 + 输出,右侧绿卡 chart,整页清爽对称。

这步学到的:任意块可嵌套任意块——layout 套 interact、callout 套 interact、stepper 套 layout 都行。详见 Nested DSL 文档。


你做完了什么

10 分钟里你用了 Rho 的 5 项能力:

能力 在哪
Callout (> [!INFO]) 公式高亮
Layout grid + cards 双列布局
Interact controls (slider) 3 个滑块
Interact text template (stl) 文本输出
Interact + Vega-Lite 增长曲线
Computed compound / gain 派生
Shared state (namespace) 文本块和 chart 块共享变量
Nested DSL layout 里嵌 interact

整个文档没有一行 JS——纯声明式。任何懂 markdown 的人都能改。


Plain-text fallback 验证

把这份 compound.md 在不支持 Rho 的 reader 里打开(GitHub web / VS Code 默认预览 / cat):

  • 公式 callout:在 GitHub 上仍正常渲染(GFM 兼容标准 callout)
  • Layout cards::::card 显示为字面量,但卡片内的 markdown(粗体、列表)正常
  • Interact 块:显示为带 interact lang 的代码块——读者看得到 sliders / computed / template,能猜到这文档原本是交互式的

读者永远不会看到坏掉的白屏——这是 Rho 的核心承诺。


接下来

你想…… 入口
把这个例子玩出花 改 chart type、加 toggle 切对比方案、加 timer 自动播放…
看更多 demo(30+ 完整例子) → Examples gallery
系统学每个能力 → Writer's Guide
查某个具体语法 → AINP 协议(v0.7)
把 Rho 集成到自己的 reader → Developer Reference
让 LLM 帮你生成 Rho 文档 → Rho AI / LLM Guide

See also