///|
/// MoonViz 原型工作台:纯 MoonBit 可运行客户端。
///
/// 运行:moon run playground
/// 场景全部由引擎(core/decl)计算:声明渲染、Agent 硬门槛拒绝、
/// 人类视觉债放行、连同债务一起修复;末尾打印可用 SVG。
fn main {
repl_loop()
}
///|
const MOCKUP : String = "let page = @decl.prototype(name=\"mockup\", width=390, height=844)\npage.add(@decl.rect(id=\"navbar\",x=0,y=0,width=@decl.fixed(390),height=@decl.fixed(88),fill=\"#111827\"))\npage.add(@decl.frame(id=\"hero\",y=88,width=@decl.fixed(390),height=@decl.fixed(160),fill=\"#EEF2FF\"))\npage.add_to(parent_id=\"hero\", node=@decl.rect(id=\"badge\",x=24.0,y=40.0,width=@decl.fixed(96),height=@decl.fixed(36),fill=\"#4B6BFB\",text=\"NEW\"))\npage.add(@decl.frame(id=\"card\",x=24.0,y=280.0,width=@decl.fixed(342.0),height=@decl.fixed(300.0)))\npage.add_to(parent_id=\"card\", node=@decl.rect(id=\"chip-a\",x=16.0,y=16.0,width=@decl.fixed(120),height=@decl.fixed(48),fill=\"#E0E7FF\",text=\"标签 A\"))\npage.add_to(parent_id=\"card\", node=@decl.rect(id=\"chip-b\",x=160.0,y=100.0,width=@decl.fixed(120),height=@decl.fixed(48),fill=\"#CCFBF1\",text=\"标签 B\"))\npage.add(@decl.rect(id=\"fab\",x=318.0,y=772.0,width=@decl.fixed(56),height=@decl.fixed(56),fill=\"#FF7A45\",text=\"+\"))\n"
///|
fn count_lines(s : String) -> Int {
let mut n = 0
for c in s.iter() {
if c == '\n' {
n = n + 1
}
}
n
}
///|
fn replace_first(s : String, old : String, new : String) -> String {
match s.find(old) {
None => s
Some(i) =>
s.substring(start=0, end=i) + new + s.substring(start=i + old.length())
}
}
///|
pub fn scenario_report() -> String {
let sb = StringBuilder::new()
sb.write_string("== MoonViz 原型工作台 \{version()} ==\n")
sb.write_string(
"纯 MoonBit:布局、不崩谓词、SVG、声明往返全部在 moonviz 引擎内计算。\n\n",
)
match @decl.parse_decl(MOCKUP) {
Err(e) => {
sb.write_string("[1] 解析失败(不应发生):\{e.describe()}\n")
return sb.to_string()
}
Ok(page) => {
let violations = page.check()
sb.write_string(
"[1] Agent 声明 → 渲染:不崩谓词违规 \{violations.length()} 条;SVG \{count_lines(page.render())} 行(文末输出全文)\n",
)
}
}
// [2] Agent 越界:走会话协议,硬门槛整体拒绝(结构化错误 JSON)
let overflow = replace_first(
MOCKUP,
"height=@decl.fixed(88)",
"height=@decl.fixed(900)",
)
let resp2 = handle("render\n" + overflow)
sb.write_string(
"[2] 模拟 Agent 越界编辑(导航栏 88→900)→ 会话协议返回:\n \{resp2}\n",
)
// [3] 人类拖拽出内容卡:结构硬 / 视觉软
match @decl.parse_decl(MOCKUP) {
Err(e) => sb.write_string("[3] 解析失败:\{e.describe()}\n")
Ok(p3) => {
let _ = p3.move_node(id="chip-b", x=160.0, y=336.0)
let debt = p3.check()
sb.write_string(
"[3] 人类拖拽 chip-b 出内容卡:合并成功(人类策略:提示不阻断),视觉债 \{debt.length()} 条\n",
)
// [4] Agent 连同视觉债一起修复 → 通过
let fixed_decl = replace_first(p3.to_decl_text(), "y=336", "y=100")
match @decl.parse_decl(fixed_decl) {
Err(e) =>
sb.write_string("[4] 修复声明解析失败:\{e.describe()}\n")
Ok(p4) =>
sb.write_string(
"[4] Agent 修复视觉债后可再提交:违规 \{p4.check().length()} 条 → 通过,画布即恢复\n",
)
}
}
}
sb.write_string(
"\n[5] 声明往返 parse_decl / to_decl_text:双向同步的引擎基座(文本定点可测)\n",
)
sb.write_string("---- [1] 的 SVG 渲染输出 ----\n")
match @decl.parse_decl(MOCKUP) {
Ok(page) => {
sb.write_string(page.render())
sb.write_string("\n---- 渲染输出结束 ----\n")
}
Err(e) => sb.write_string("渲染失败:\{e.describe()}\n")
}
sb.to_string()
}