Numable 创作文档同一份内容也在终端里:`numable docs <主题>`English

workflow —— 创作流程与纪律

读者:做工具的用户,和替他干活的 AI。两者读同一份。 先读这章,它定义「做一个工具的顺序」与「不能越的线」;能不能做某件事见 numable docs capabilities,具体文件格式见 layout / xwidget / df / rcn 各章。

一个工具是什么

一个工具就是一个目录,里面全是明文 JSON / HTML,没有构建产物、没有编译步骤。App 把工作区目录当根:文件写进去,包就在 App 里可见;改一个字,重渲就生效。

最小的包长这样(numable init 生成的起步包):

my-source/
  manifest.json                # 身份与元数据:id(ULID)/ version / title / category / network / i18n
  xWidget/
    clock.xwidget              # 一个组件的声明:尺寸档、实例参数、取数绑定、刷新节奏、点击行为
    rc/clock.rcn               # 这个组件的画法(几何 + 颜色 + 文本,渲成一张位图)
    flow/clock.df              # 这个组件的取数流(请求 + 加工,产出给 .rcn 用的键)
  page/
    router.json                # 页面路由表(可选:没有页面就不要 page/)
    html/home/index.html       # 点组件进入的详情页
  .numable/                    # 本机夹具与产出,永不进包(numable init 已写进 .gitignore)

四类文件各管一件事,互不越界:

文件 管什么 不管什么
manifest.json 包的身份、商店门面、网络白名单、凭证声明、语言基准 不放任何业务数据
.xwidget 把「画法 + 取数 + 尺寸 + 刷新 + 点击」绑在一起,并给出实例参数默认值 不写画法,不写取数逻辑
.df 取数与加工:请求、解析、算派生量,最后 resultFilter 透出一组键 不碰 UI,不引主题(可以读 ${@app.language}、带自己的 i18n 表,见 numable docs i18n)
.rcn 画:用 .df 透出的键渲一个组件 不发请求,不做重计算

多出来的两类是可选的:page/ 下的详情页(html / xpage / form 三种页型,见 numable docs page),和 .af 交互流(点击、参数写回、刷新,见 numable docs af)。

创作顺序

七步。每一步都有命令能验,不要跳步——跳过第 2 步直接画组件,是返工最常见的原因(组件画完了才发现字段路径不对,几何和判空全得重写)。

1. 克隆起步

numable init my-source
numable init my-source --from ../已有的包目录

从模板或一个已有的包克隆,不要手搓目录:身份 id(26 位 ULID)由命令行生成并写进 manifest.id / manifest.domain,目录布局与文件后缀现成正确。看到 ✓ 新包 … id=… 即成功。

2. 先取数,后画组件

先写 .df,把数据跑通:

numable run my-source --flow clock --full

--full 打印完整输出。取到的键,就是组件上能用的全部变量——包括你要用来判空的旗标、要显示的时间锚、要驱动折线的定长数组。这一步产出的键名清单,是第 3 步的输入。

看到 ✓ clock {...} 且键名与你预期一致算对;看到 ✗ 就先修流,不要往下走。run 会强制网络白名单,打印 · 网络白名单已强制: [...]——这里少一个 host,真机上就是同一处被拦。

3. 画组件

写 .rcn,渲出来看:

numable render my-source --widget clock

产出在 my-source/.numable/render/ 下:每个组件 × 浅色 / 暗色 / 空态 × 语言各一张 PNG,外加一张 index.html 拼图。render 会先跑一遍 run,取数失败的组件会用空数据渲——所以空态那张不是摆设,它就是「源挂了那天用户看到的东西」。只调版式、不想每次都打一轮网络(有日限额的源尤其要省)时加 --no-run,复用上一次取到的数据。同目录下 <组件>.json 记着每张图的尺寸与可点区域(hits:点哪块会触发哪个事件;为空说明组件里没有单独可点的格子,点整张组件走 .xwidget 的 onClick),核对点击范围时用得上。

包里有页面(page/,点组件打开的那一层)时,页面也渲出来看:

numable render my-source --page

router.json 里每条路由按手机宽出浅色、暗色两张整页截图,落在同一目录的 page*.png。页面里的取数与 run 走同一套白名单和夹具,被拦下的请求、页面脚本报错、没模拟的桥方法都会打印出来。form 页和远程页暂不支持,要在 App 里点进去看。细节见 numable docs page。

4. 声明

写 .xwidget,把 rc / df / 尺寸档 / 刷新节奏 / 点击行为绑起来。两处最容易错:

5. 静态闸

numable check my-source

默认 personal 档,要求零 error。它抓的是「不检查就没人发现」的那一类:白名单不闭合、cell 缺 type、op:if 条件键写错、并发结果晚一拍、取数失败没有 error 出口。错误码全表见 numable docs lint-codes。

6. 贴图确认

把 .numable/render/ 下的浅色 / 暗色 / 空态三张图给用户看。用户点头才算做完——check 与 run 证明不了「好不好看」和「这是不是他想要的那个组件」。

7. 持续优化

用户在 App 编辑器里手改、或再让 AI 改,改的都是同一份源文件。每次改完只需重跑受影响的那一层:改 .df 跑 run,改 .rcn 跑 render,改 .xwidget / manifest.json 跑 check。

命令速查

上面七步用到的就是这些,外加几个不常用但省事的开关:

命令 干什么
numable workspace init [目录] 把一个目录变成创作工作区:写一份给 AI 看的指引,之后在这个目录里直接对 AI 说需求即可
numable init <目录> [--from <包目录>|installed:<id>] 新建包(重新生成身份 ULID)。--from 可以是本地任意包目录,也可以是桌面 App 里已装的包(installed:<id>,Mac / Windows 版)
numable init --job <id> --kind static|once|cross|level|changed|task 在当前包目录里加一条提醒 / 后台任务,并按需抬高 manifest.minEngine,见 numable docs alerts
numable check [包…] [--profile personal|publish] 静态闸
numable run [包…] [--flow a,b] [--file x.df] [--full] [--fixtures <目录>] 数据层真跑。--file 跑任意一条 .df(包括没被任何组件绑定的探针流)
numable render [包…] [--widget a,b] [--states light,dark,empty] [--locales zh-CN,en-US] 渲染层出图
numable render [包…] --page [/路由,…] [--locales zh-CN,en-US] 页面出图(html / xpage,浅色 + 暗色整页截图)
numable docs [主题] [--toc] [--section 关键字] [--search 关键字] 读这套文档。长章先 --toc 看目录,--section 只读一节,--search 跨章检索
numable doctor [包…] 环境 / 引擎版本 / 工作区体检

三个通用开关与三个环境变量:

三层验证各管什么

三层不可互替。任何一层绿了都不代表另一层绿。

层 命令 抓什么 抓不到什么
静态 numable check 结构与字段合法性、白名单闭合、凭证声明闭合、已知的静默失效写法(cell 缺 type、双单位后缀、concurrent 晚一拍、parseDate 脏 pattern、取数流没有 error 出口) 取值路径写错、版式难看、判空逻辑反了
数据 numable run 数据源活不活、字段路径对不对、派生量算得对不对、白名单拦不拦、凭证夹具够不够 渲染层的分支写错(值在,但组件上走了兜底那条)
渲染 numable render 溢出与裁切、暗色可读性、空态是不是一片空白、没求值的 $[...] 字面量被画上去 手机上的字形度量与方法差异(render 用的是浏览器引擎)

手机上独有的差异(字形宽度、数字格式化、日期方法)只能装到手机上看。

红线

违反下面任何一条,包不算做完。「检查方式」一栏说明它在哪一层能被抓到。

红线 检查方式 违反时的现象 修法
源文件唯一真相:不写生成脚本,不留夹具 check G1b / G1 夹具随包分发,或文件放在不被加载的位置(写了不生效) 夹具放 .numable/params/;RCN 归 rc/、流归 flow/
网络白名单闭合:.df 请求的 host 集合 == manifest.network,不多不少 check G3 少了:真机静默拦掉,组件恒 --;多了:安装面板列一堆用不上的域名吓用户 按 run 打印的白名单对齐
密钥不进包:不写进 params、不写进 .df 字面量、不写进 data.* check G18 明文密钥随包分发给所有人 走 manifest.credentials 声明 + 本机夹具 .numable/params/_credentials.json,见 numable docs credentials
不编数据:取不到就让流失败,不要用 0 / 空串 / 假时间顶上 check G26 取数失败时流报成功,空数据覆盖掉上一次的好数据,组件上出现一个看着正常的假数字 主干字段判空 → action:"error";「集合为空」是成功,不是失败
时间锚:实时数据组件必须显示数据时间 人审(看 render 出的图) 用户分不清「数据没变」和「三天没刷新了」 把时间戳一路透出到 .rcn;喂 formatDate 前先判空,否则空值会渲成 1970
双分支颜色:.rcn 里所有带 hex 的颜色字段写 浅|深 check G7 暗色下整块看不见,或白底上白字 "textColor": "#1A1A1A|#FFFFFF"
单位写 pt check G28(双单位后缀)+ render 层目检 写 px:内容缩到左上角、字号偏小;写成 14.0ptpt:整个组件渲不出且不报错 所有几何与字号统一 pt
文案走 i18n:用户可见文本写 ${@i18n.key} check G8 / G8b(publish 档) 英文环境下组件上一半中文 文案表放各资产自己的 i18n,见 numable docs i18n
判空用显式旗标:哨兵写法 $[if::(eq::(findNotEmpty::(${x},__none__),__none__),0,1)],不要 eq::(x,) / eq::(x,0),也不要 length::(对数字恒返 0) run 层(把 URL 改成 404 再跑一遍) 空态渲出「有颜色的 ▼ --%」这类自相矛盾的画面 在 .df 里落一个 hasX 旗标,.rcn 只判旗标

个人自用 vs 发布

check 有两档,默认 personal:

personal(默认) publish(--profile publish)
用途 自己用 / 给用户装在自己机器上 上架分发
跑哪些闸 结构、网络、凭证、DSL 静默失效、路由与交互 全部
跳过什么 商店门面(subtitle / 包名宽度 / 英文覆盖)、logo、添加组件按钮版式、组件档位(≥3 个、必备 22 与 42/44)、双语门、首屏缓存、凭证绑定直达入口 不跳
numable check my-source --profile publish

自用包不必现在就满足发布向的要求;想上架时再跑一次 publish 档,缺什么补什么,流程见 numable docs publish。两者是同一个包、同一个 ULID:自用包发布后,用户手上那个组件长按就能升到已发布的版本,数据保留。

接下来读哪章

你要做的事 读
先弄清能做什么、各平台差在哪 numable docs capabilities
从零做出第一个组件(完整走一遍) numable docs first-card
给包加一张详情页 numable docs add-page
加点击、参数编辑、表单 numable docs add-interaction
接需要密钥的数据源 numable docs credentials
做中英双语 numable docs localize
加提醒或后台任务 numable docs alerts
从自用到上架 numable docs publish
查某个文件怎么写 numable docs layout · xwidget · df · rcn · af · page · bridge · i18n · params
做一张声明式页面(八种布局、条件显隐、输入框、触底分页) numable docs xpage
查节点字段 / 方法 / 错误码 numable docs rcn-nodes · methods · lint-codes
查内置变量(@app / @device / @time / @contentInset …)有哪些键 numable docs builtins
组件空了、点了没反应、改了没变化 numable docs pitfalls