Sign in

    #MoUI Studio — 用人话描述,用母语编程,用积木理解

    MoUI Studio 是一个中英双语的可视化编程环境:同一份程序有三种同源视图—— 易语言式的窗体设计器、Scratch 式的积木编排、中英双关键字的 代码编辑器。自然语言交给 AI,但它只能产出结构化提案(可预览、 可 diff、可拒绝),结构上不存在"生成任意代码直接运行"的路径。产物可以 一键导出为独立 MoUI 应用(Web / macOS 双端)。

    #五分钟上手(Web,零安装)

    git clone https://github.com/wzzc-dev/moui_studio.git cd moui_studio moon build web_wasm --target wasm-gc # ES module 不能用 file:// 打开,需要起一个静态服务: python3 -m http.server 8766 --directory web_wasm # 浏览器打开 http://127.0.0.1:8766 即见 IDE

    web_wasm/index.html 会自己探测 _build/ 下的 wasm 产物,debug 与 --release 两种 profile 都能直接用。

    macOS 原生入口:moon run macos_skia --target native

    #界面地图(v4 IDE 壳)

    ┌─ 无边框顶带(仅 native 沉浸式)────────────────────────────────┐ │ [系统信号灯] 按住此条拖动窗口 │ ├─ 顶栏 ─────────────────────────────────────────────────────────┤ │ 运行 · 单步 · 编译 MoonBit 撤销/重做 · 语言 · 搜索 · 设置 │ ├────┬──────────┬────────────────────────────┬──────────────────┤ │图标│ 左栏 │ 中心工作区 │ 右栏 │ │栏 │ 程序结构 │ 实时应用 │ 设计态 │ 属性/状态/数据/ │ │ │ 本地数据 │ ──────────────────────── │ 动作/依赖 │ │ │ ──────── │ 积木(分类 · 选中描边) │ ──────────────── │ │ │ 最近修改 │ ──────────────────────── │ 编辑积木(选中时)│ │ │ │ 代码(行号 ┼ IR 映射竖条) │ 快速操作 │ │ │ │ │ 运行时监视 │ ├────┴──────────┴────────────────────────────┴──────────────────┤ │ 运行审计 │ Console warn 0 · err 0 │ └───────────────────────────────────────────────────────────────┘

    • 切语言:顶栏 English / 中文(代码与界面一起切换:如果…则…结束 ↔ if…then…end)
    • 换模板:空舞台按钮(问候 / 点名册 / 空白程序)或 ⌘K 命令面板 (四模板 + 新建空白程序 + 打开/保存/导出)——教案里的口算训练营、 班级小卖部从命令面板可达
    • 工作区:图标栏切换可视化 / 代码 / 空舞台;再点当前项收起成空舞台
    • 积木视图:画布左侧是竖排分类轨(色点 + 名称,选中项带强调边)——分类 不占画布纵向空间;画布支持同层拖拽重排;窗格脚注说明画布底部是为 AI 层 预留的避让通道;长语句按块宽以省略号收尾。 语句在右栏改(IDEA 式「选中在画布、编辑在检查器」):选中一块积木后 右栏顶部出现「编辑积木」卡(种类徽章 + 语句输入 + 应用/复制/删除), 链条永远是一条连续的链——不再因选中被切成三段。旧版把编辑卡嵌在链条 中间,块一多上下两段就只剩几行,读到的现象是「点积木后内容显示不全」
    • 代码视图:左侧行号 + 代码,右侧 IR 映射竖条;悬停代码行或色块即 双向高亮对应路径(离开还原,与点击的驻留高亮互不干扰)(按行号对齐的分类色块, 点一下高亮对应积木;与「点代码行高亮积木」是同一状态的两个入口)
    • 左栏:程序结构树(可搜索、可折叠)、本地数据(从 IR 派生的文件名/表头预览)、 最近修改(区分「你」与「Agent」)
      • 创建入口在各分组头的 +(不是面板底部的全局按钮块):窗体 / 控件 / 变量 / 事件子程序 四组有 +,点开是内联菜单;本地数据没有——它的 条目从列表/表格控件的行派生,没有独立创建语义,所以不给一个点了没用的 按钮。创建入口贴着它创建的东西,与右栏「动作」页签建子程序、积木分类轨 建语句是同一范式
      • 控件组的菜单与 ControlKind 枚举等长:按钮 / 标签 / 输入框 / 选择框 / 列表框 / 表格 / 下拉框 / 进度条 / 图片。变量此前没有任何新建入口(只来自 AI 提案或手打代码), 现在变量组有「+ 新建变量」,改名会同步改写所有引用它的语句
      • 树上的每一类行都可点选:窗体 → 窗体属性(标题可改)、控件 → 控件属性、 事件子程序 → 代码/积木、变量 → 变量属性(改名/初值/删除)、数据条目 → 只读来源与内容预览。选中是单选:点一处会清掉其他两条槽 (控件 / 子程序 / 非控件对象),右栏永远只显示当前那一个对象的属性
    • 沉浸式(native):macos_skia 入口开 transparent_titlebar —— 系统标题栏透明、 内容延伸到窗口顶端;系统信号灯照常可用(关闭/最小化/缩放都是真的,app 不 重画它们),app 只让出左侧 78pt 并把这条顶带登记为拖拽区。Web 恒关闭
    • 右栏:属性/状态/数据/动作/依赖五个页签;选中积木时页签体顶部是编辑积木卡 (见上);快速操作是设计稿口径的单选控件对齐 (左 x=24、居中 x=(640-w)/2、右 x=640-w-24)+ 复制 + 删除 + AI 优化; 运行时监视列出被监视变量(未运行时显示空态)
    • 底栏:运行审计时间线(编译 / 启动 / 副作用 / 错误,副作用卡就地确认) 与 Console 原始日志两页签;点审计条目可跳到对应积木
    • 命令面板:⌘K 过滤 Studio 命令(每条都是既有消息的封装,不引入新状态通道)
    • AI 浮动层:默认居中贴底,可切左/右/停靠右栏,可收成药丸; 浮动时自动让出避让带,不会压住窗格内容
    • AI 会话线程:每次生成提案推一张 diff 线程卡(事件驱动,不是摆设), 折叠条显示「Agent 待审计 N」,采纳/拒绝后卡片标记已应用/已拒绝; 线程超过 3 条给「查看全部会话 →」直达 AI 历史;收成药丸后药丸本身显示 Agent 待审计 N——收起状态下这是唯一的待办读数
    • AI 输入条工具行:锚点读数(底部居中 / 左下角 / 右下角 / 停靠 · Context 侧栏)
      • 切换锚点 / 停靠右栏 / 收起三个带名字的按钮(停靠态自动改显「浮出」)
    • AI 输入条:模式段 修改 / 生成 / 解释 / 修复 决定「发送」走哪条既有通道 (解释=本地确定性讲解、修复=草稿回到 IR 规范渲染、生成=清空后走同一条提案 校验链、修改=直接提案)——模式只改入口路由,不新增执行路径;右侧是发送
    • 空舞台(三个工作区都收起)时输入框升格为 hero:加宽居中偏上,并列出三条 示例指令;点击示例只把文字填进输入框,按不按发送由你决定
    • 运行:点「运行」,按钮真的能点;提交数据 会让程序停下等你确认。 运行页里的舞台控件逐个事件真的会重新执行:随机数 每次给出新值 (种子跨事件连续推进),所以连点同一个按钮每次都能看到不同结果—— 两轨(解释/真编译)从同一个种子起算,逐位可比
    • 停止:点「停止」回到可视化工作区继续编辑(不是停在空白运行页)
    • 真编译:点「编译 MoonBit」在 TMPDIR/moui_studio-compile/<app> 里真跑 moon update / check / build,七步进度逐格可见(准备工作目录 → 解析依赖 → 类型检查 → 构建 Web 产物 → 构建 native 产物 → 启动产物自检 → 收集产物, 已完成点满、进行中高亮);产物是磁盘上的真实文件
    • 产物卡:native GUI 应用(可执行窗口程序)排在第一位,带 运行产物(一键开窗)/ 复制路径 / 在文件夹中打开 三个动作; 长路径做中间省略(保头保尾,文件名永远读得到)并给出真实字节数。 native_smoke 是无头启动自检(打印标记就退出,不开窗),单独标注, 不会被误当成要运行的应用
    • 「运行产物」是脱离式启动:进程 reparent 到系统 init,Studio 退出或 被关掉都不会带走正在用的导出应用(实测:杀掉 Studio 后窗口仍在)
    • 导出应用视口自适应:窗口按项目声明尺寸开,舞台在可用空间内等比缩放 (fit_scale,下限 0.4),所以任何窗口尺寸下设计稿都完整可见、不会被裁。 首屏是用户设计的表单 + 一行状态条,变量/消息/审计收进默认收起的 「变量与日志」面板(导出的是应用,不是调试台)

    #四个内置样例(各附教案)

    样例教什么教案
    问候小程序三视图与运行闭环(最小起点)samples/hello/README.md
    班级点名册随机数、列表框取行samples/roll_call/README.md
    口算训练营分支、变量、算术samples/drill/README.md
    班级小卖部跨事件状态、外发闸门samples/store/README.md

    #AI(真实模型)与新建工程

    • AI 恒走真实 OpenAI 兼容 provider:没有离线假模型。未配置密钥时 composer 下方给出接通引导(不弹假回复);endpoint/model/密钥在设置面板里热改。
    • 真实模型默认指向阶跃星辰(https://api.stepfun.com/step_plan/v1,step-5-preview)。
    • 凭据放在 gitignored 的 moui_studio/.config.json(可直接在 json 里换 key):

    { "provider": { "endpoint": "...", "model": "...", "api_key": "..." } }

    • Web 入口(浏览器)没有 HTTP 通道:AI 输入面显式降级为「需要 native 版本」 引导,不出假按钮。
    • 新建示例项目 = 落盘本地文件夹(空舞台按钮 / ⌘K 的模板与「新建」命令): 选一个空文件夹,Studio 写入 moon.mod + 可编译的 MoonBit 应用骨架 (内嵌领域内核,moon build 即过)+ .studio/program.json IR 快照。 关掉 Studio 再打开时激活该工程即可恢复程序,AI 可以继续在上面改 (比如「把按钮字号调大」——提案采纳后 handlers.mbt 与内嵌 IR 直接 写回工程文件夹)。

    #规范文档

    #包结构

    domain/ir/ 程序 IR + 不变量 + 预算(零依赖) domain/studio_lang/ 双语 DSL:关键字表/词法/解析/打印/指令机解释器 domain/codec/ .studio.json 编解码 + 版本门 domain/proposals/ AI 提案契约 + 校验链 + diff + 内置模板样例 domain/blocks/ 语句 → 积木规格表 kernel/ 插件内核:服务容器 / 事件总线 / 贡献点 kernel/plugin_contract/ WASM 插件契约(清单 / 视图协议 / 宿主 API 面) chrome/ 共享视觉原子(主题 + 尺寸常量 + ChipTone) plugins/blocks/ 积木几何 + 绘制计划 + 洞/值命中(零 Model/Msg) plugins/project/ 工程域:行模型 + ProjectState + ProjectMsg plugins/run/ 预览数据层 + 运行态结构 + 预览视图贡献 plugins/ai/ AI 会话切片 + 几何/视图贡献 plugins/markdown/ plugins/terminal/ services/export/ 独立应用导出(内核快照 + bundle 生成) services/model_provider/ OpenAI 兼容协议(纯) services/provider_native/ native async worker + .config.json 读取 services/plugin_runtime/ WASM 插件运行时(native,wasmoon 宿主 + 配额) services/plugin_runtime_web/ WASM 插件运行时(web,浏览器原生 wasm) app/ TEA shell + 组合根 + 壳级组装视图 + 中英 catalog web_wasm/ macos_skia/ 薄组合根 samples/plugin_fixture/ 最小 WASM 插件(参考实现 + E2E 用) tools/sync_kernel/ tools/emit_bundle/

    #WASM 插件系统

    Studio 可以加载运行时插件:第三方代码以 WebAssembly 形式被加载, 贡献命令、面板与界面。

    • 架构、清单格式、视图协议、宿主 API 白名单、安全边界: docs/plugin-system.md
    • ⚠️ 插件运行时不是安全边界(wasmoon 自述 experimental + insecure)。 只安装可信来源的插件。

    #最小验证循环

    本仓库是独立模块(wzzc-dev/moui_studio),包路径不带 moui_studio/ 前缀——那是它作为 MoUI 主仓库 submodule 时的写法。

    moon check --target native # 0 错误 0 警告 moon fmt --check # domain 是多个包(不是单包路径),一次传完 moon test domain/ir domain/studio_lang domain/codec domain/blocks \ domain/proposals --target native moon test kernel chrome plugins/ai plugins/markdown plugins/project \ plugins/run plugins/terminal plugins/blocks --target native moon test services/compiled_runtime services/diff services/compile_native \ services/export --target native moon test app --target native # 474 个测试 moon test app --target wasm-gc # WASM 插件系统:契约 + 运行时 + E2E(真实加载一个 .wasm 插件) sh scripts/build_plugin.sh # 先造 fixture 的 wasm 产物 moon test kernel/plugin_contract services/plugin_runtime --target native # 导出内核漂移门:改了 domain 忘了同步 → 红 moon run tools/sync_kernel --target native -- --check moon build web_wasm --target wasm-gc

    tools/sync_kernel 的路径表按母仓库根书写(带 moui_studio/ 前缀), 但工具会按当前根自动解析:在本仓库根跑 moon run tools/sync_kernel, 在母仓库根跑 moon run moui_studio/tools/sync_kernel,两者等价。

    上面这一套就是 .github/workflows/ci.yml 的全部内容——push / PR 自动跑, 单独 clone 本仓库也有回归网。不在本仓库 CI 里的两件事:i18n catalog 生成检查(生成器是母仓库的 tools/moui/generate_i18n_catalogs 工具,漂移由 母仓库 pr profile 把关)与真机 / 真 Skia 冒烟(路径触发,非每日门)。


    #MoUI Studio — describe in words, code in your language, understand with blocks

    (中文说明见上。)

    MoUI Studio is a bilingual visual programming IDE. One program, three same-source views: an E-language-style form designer, Scratch-style typed blocks, and a code editor whose keywords follow the UI language (如果…则…结束 ↔ if…then…end). AI may only produce structured proposals (previewable, diffable, rejectable) — there is no path from "generate arbitrary code" to "run it". Projects export to standalone MoUI apps (Web wasm-gc / macOS Skia).

    Try it in five minutes (Web, zero install):

    git clone https://github.com/wzzc-dev/moui_studio.git cd moui_studio moon build web_wasm --target wasm-gc

    • Switch language from the top bar (English / 中文); the DSL keywords switch with it.
    • New sample project = a real folder on disk: pick an empty folder from the empty stage or the ⌘K palette (all four samples plus a blank program). Studio writes moon.mod, a compiling MoonBit app skeleton (vendored domain kernel — moon build passes), and a .studio/program.json IR snapshot. Re-open later by activating the project; the AI can keep editing it (e.g. "make the button font bigger" — accepting the proposal rewrites handlers.mbt and the embedded IR right in that folder).
    • AI always uses a real OpenAI-compatible provider — there is no offline fake model. Without a key the composer shows a connect hint; Web has no HTTP channel, so its AI surface degrades to a "needs the native app" note.
    • Run: buttons really work, and submit_data pauses the program until you confirm — nothing is sent before you do.

    macOS entry:

    moon run macos_skia --target native

    Provider configuration (default StepFun step-5-preview):

    Credentials live only in the gitignored moui_studio/.config.json:

    { "provider": { "endpoint": "...", "model": "...", "api_key": "..." } }