Interact — controls
在
```interact块里声明可交互控件——slider / input / select / toggle / button——读者操作控件,驱动同块或他块的实时输出。 这是 Rho 所有"动态内容"的核心引擎。
When to use
- ✅ 参数化展示(财务计算器 / 物理仿真 / 配色试看 / API 调用试调)
- ✅ 教学公式可视化(拖滑块看公式怎么变)
- ✅ 轻量计算工具(BMI / 复利 / 单位换算 / 房贷月供)
- ✅ 数据探索(slider 调参驱动 chart 重绘——配合 Vega-Lite chart)
- ❌ 真表单提交(state 只在 reader 内存里,不持久化)
- ❌ 复杂业务逻辑(mini DSL 不是 JS,没
if/else/loop) - ❌ 跨页面共享 state(state 限于单页内)
Basic syntax
interact 块包含两部分:控件声明 + template 输出。
\```interact
slider x 0 100 50 1
template stl:
[当前值] -> [{x}]
\```
渲染:一个滑块(min 0、max 100、初始 50、步长 1)+ 一行实时输出 "当前值 = 50"。拖滑块 → 数字变。
All control types
Rho 提供 5 种控件:
| 控件 | 用途 | 语法 |
|---|---|---|
| slider | 数值滑块 | slider name min max initial step |
| input | 文本输入框 | input name "default text" |
| select | 下拉选项 | select name "opt1" "opt2" "opt3" |
| toggle | 布尔开关 | `toggle name <true |
| button | 按钮(点击触发值变化) | button name "label" <value> |
Control 详解
slider
slider <name> <min> <max> <initial> <step>
| 参数 | 类型 | 说明 |
|---|---|---|
name |
identifier | 变量名(在 template 里用 {name} 引用) |
min |
number | 最小值 |
max |
number | 最大值 |
initial |
number | 初始值(必须在 [min, max] 内) |
step |
number | 步长(拖动每次跳多少) |
slider rate 0 0.2 0.05 0.01 ← 利率 0%-20%,步长 1%
slider weight 40 200 70 0.5 ← 体重 40-200 kg,0.5 kg 精度
slider temperature -20 40 20 1 ← 温度 -20°C 到 40°C,1°C 精度
input
input <name> "<default text>"
| 参数 | 类型 | 说明 |
|---|---|---|
name |
identifier | 变量名 |
default |
string | 默认填充内容 |
input email "[email protected]"
input city "上海"
input apiKey "" ← 空默认
input 接受任意文本——纯字符串,不做格式校验。如果要数字输入,用
slider或加文档级提示让用户自己确保格式。
select
select <name> "<opt1>" "<opt2>" "<opt3>" ...
| 参数 | 类型 | 说明 |
|---|---|---|
name |
identifier | 变量名 |
opt* |
string | 选项列表,第一个是默认值 |
select theme "dark" "light" "system"
select size "S" "M" "L" "XL"
select region "us-east" "us-west" "eu-west" "ap-northeast"
toggle
toggle <name> <true|false>
| 参数 | 类型 | 说明 |
|---|---|---|
name |
identifier | 变量名 |
default |
bool | 初始状态 |
toggle darkMode false
toggle notifications true
toggle showAdvanced false
toggle 在 template 里展开为
true/false(字符串)。要做条件展示,配合 computed 的三元式或多个 template 块。
button
button <name> "<label>" <value>
| 参数 | 类型 | 说明 |
|---|---|---|
name |
identifier | 变量名 |
label |
string | 按钮上显示的文字 |
value |
any | 点击后赋给 name 的值 |
button reset "重置" 0
button increment "+1" "{counter+1}" ← 配合 computed 做计数器
button trigger "触发动画" "running"
button 跟其他控件不同——它只在点击时改 state,不像 slider 持续变化。适合"重置"、"触发"、"步进"等离散动作。
Examples
Example 1:最简单 slider
\```interact
slider x 0 10 5 1
template stl:
[选中值] -> [{x}]
\```
渲染:滑块(0-10,初始 5),下方一行 "选中值 = 5"。
Example 2:多控件组合(综合参数面板)
\```interact
slider weight 40 120 70 1
input name "Alice"
select activity "sedentary" "moderate" "active"
toggle metric true
template stl:
[姓名] -> [{name}]
[体重] -> [{weight} kg]
[活动量] -> [{activity}]
[使用公制] -> [{metric}]
\```
渲染:4 个不同类型控件(滑块 + 文本框 + 下拉 + 开关)整齐排在上方;下方 4 行实时输出。
Example 3:button + computed 做计数器
\```interact
button inc "+1" "{counter+1}"
button reset "重置" 0
computed counter = 0 # 初始值
template stl:
[计数] -> [{counter}]
\```
渲染:两个按钮 + 一行 "计数 = 0"。点 +1 → "计数 = 1",再点 → "计数 = 2",点 重置 → "计数 = 0"。
注意:button 的 value 用
{counter+1}引用了 computed 自身——这是合法的 self-reference 模式,专门为 button 设计。
Example 4:select + toggle 切换显示模式
\```interact
select unit "metric" "imperial"
slider weight 40 120 70 1
computed display = unit == "metric" ? weight : weight * 2.20462
computed unitLabel = unit == "metric" ? "kg" : "lb"
template stl:
[体重] -> [{display:.1f} {unitLabel}]
\```
渲染:下拉选公制/英制 + slider,下方一行随单位切换显示 70 kg 或 154.3 lb。
mini DSL 支持三元式
cond ? a : b,但不支持 if/else 语句。
Plain-text fallback behavior
```interact 是 fenced code block 含 interact lang。在不支持的 reader 里:
```interact
slider x 0 10 5 1
template stl:
[选中值] -> [{x}]
→ 显示为**带语法高亮的代码块**——读者看得到所有 sliders / inputs / template 声明,**能完全理解原文档想表达什么**(含 min/max/初始值等参数)。
具体各 reader:
| Reader | 渲染效果 |
|---|---|
| **Rho** | 完整交互(控件 + 实时模板输出) |
| **GitHub web** | 代码块(slider/input/select 字面量 + template 字面量都可见) |
| **Obsidian** | 同上 |
| **VS Code 默认预览** | 同上 |
| **cat / less** | plain text |
**信息无损**——读者能看到可调参数空间 + 模板结构,只是不能拖。
---
## Common pitfalls
### 1. slider 参数顺序错
```markdown
slider x 100 0 50 1 ← ❌ min > max,行为未定义
slider x 0 100 1000 1 ← ❌ initial > max
slider x 0 100 50 0 ← ❌ step=0,无法拖动
slider x 0 100 50 1 ← ✅
顺序固定:min max initial step,min ≤ initial ≤ max,step > 0。
2. variable name 含空格 / 特殊字符
slider my var 0 10 5 1 ← ❌ "var" 会被当成 max
slider my-var 0 10 5 1 ← ⚠️ 部分解析器不支持连字符
slider my_var 0 10 5 1 ← ✅
slider myVar 0 10 5 1 ← ✅
变量名只用 [A-Za-z0-9_],跟标准编程语言变量名规则一致。
3. 在 template 里用了未声明的变量
\```interact
slider x 0 10 5 1
template stl:
[结果] -> [{y}] ← ❌ y 未声明
\```
→ template 渲染时 {y} 会显示为字面量 {y} 或抛出 "undefined variable" 错误。template 引用的变量必须先用 slider/input/select/toggle/computed 声明。
4. select 选项含特殊字符
select theme "dark" "light" ← ✅
select theme "dark mode" "light mode" ← ⚠️ 含空格的选项必须双引号包
select theme "with"quotes" ← ❌ 嵌套引号,解析失败
含空格的选项必须双引号包;嵌套引号用全角 "" 或转义 \"。
5. button value 引用变量但没正确闭合
button inc "+1" {counter+1} ← ❌ value 不带引号且含 {expr},解析未定义
button inc "+1" "{counter+1}" ← ✅ value 是字符串字面量含表达式
button value 含表达式必须放双引号里。
6. input 接受数字但当字符串处理
\```interact
input age "30"
template stl:
[年龄+1] -> [{age + 1}] ← ❌ "30" + 1 = "301"(字符串拼接)
\```
→ input 的值永远是字符串。要做数学运算需要先 cast:用 mini DSL parseFloat({age}) 或用 slider 而不是 input。
最佳实践:数字输入用 slider;自由文本用 input。
7. 控件数量过多
\```interact
slider a 0 10 5 1
slider b 0 10 5 1
slider c 0 10 5 1
slider d 0 10 5 1
slider e 0 10 5 1
slider f 0 10 5 1
... (12 个 slider)
template stl:
[结果] -> [{a + b + c + d + e + f + ...}]
\```
→ 控件超过 6-7 个,UI 拥挤、读者迷失。改进:
- 用 Layout grid 把控件分组分卡片
- 拆成多个 interact 块(用 Shared state namespace 共享变量)
- 重新审视:真的需要这么多控件?
Where to next
| 想…… | 入口 |
|---|---|
| 学 template 怎么写 | → Interact text template |
| 加派生计算(不只是显示原值) | → Computed |
| 加 live chart(Vega-Lite) | → Interact + Vega-Lite |
| 多个 interact 块共享变量 | → Shared state (namespace) |
| 加自动播放动画 | → Timer |
| 画 SVG 场景 | → SVG scenes |
See also
- Interact text template — template 语法详解
- Computed — 派生值计算
- Interact + Vega-Lite chart — slider 驱动 live chart
- Timer — 自动播放(取代手动拖)
- Shared state (namespace)
- Plain-text fallback principle