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 拥挤、读者迷失。改进:


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