我用仓颉写了个 Markdown 笔记应用,现在日常真的在用它。cjnote 是 cj-tauri 里最能打的样板:后端静态编译、前端就是系统 WebView,11.2M 单文件,AI 走本机后端,密钥不落在应用里。
先把关系说清楚,免得读着像软文:**cjnote 是我在 cj-tauri 仓库里写的示例应用,框架也是我写的。**所以这篇不是评测别人家的东西,是我把「为什么我认为它值得你花半小时」讲一遍——优点讲透,短板也照实留着,你自己判断。
一句话说清它是什么
一个用仓颉写的桌面 Markdown 笔记应用。三栏布局:左边笔记库,中间编辑与实时预览,右边 AI 助手;原生 GTK 菜单栏;AI 能真流式对话、能读你盘上的笔记、还能改你选中的段落。
架构上走的是 Tauri 那条路,也是我认定的那条:后端静态编译,前端就是系统自带的 WebView(Linux WebKitGTK / Windows WebView2)。不捆 Chromium、不带 Node、没有npm install。
开发态产物7.6M;打包成单文件11.2M——运行时、stdx、插件、页面、前端库、能力清单全在一个文件里,换台机器只要系统有 WebKitGTK / GTK 就能直接跑。
如果你也想体验,windows下的二进制可执行文件地址:https://atomgit.com/qq8864/cj-tauri/tree/main/examples/cjnote/dist-single
我认为它值得一看的七个理由
① 它是「活样板」,不是那种演示用的壳
演示程序只证明 API 能调通;样板得证明这套东西能撑住一个真应用。
cjnote 里笔记是真的.md文件、笔记库是真的目录、AI 是真接后端。日常拿来记东西完全够用——我这几个月写稿子的素材就是这么攒的。你要评估这套框架能不能做正经产品,看这一个应用就够,不需要从 hello world 一步步去推演「它大概能做到哪」。
② 原生能力用得足,而且难得地「诚实」
很多跨平台方案一碰原生就露怯:菜单是页面画的、标题栏是假的、能力缺失就直接白屏。cjnote 反过来:
- 文件 / 视图 / 帮助三个顶层原生菜单,
Ctrl+N/S/O/P/Q全套快捷键,与页面内操作同权—— 不是「菜单里摆着好看」; - 顶栏那个「标题 · 已保存/未保存 · 字数」不是页面画的,是走
setTitlebarSubtitle推进系统菜单行右侧的,所以页面自己的顶栏整条都能省掉; - 视图菜单的三态(编辑 / 分屏 / 预览)和界面主题都是真的勾选项,和页面状态双向同步:在页面里按
Ctrl+2,菜单里的勾跟着跳;在菜单里点「预览」,页面跟着切。
最能说明态度的是能力位降级:不是所有平台都有原生副标题(比如 WebView2 就没有),这时候它自动退回页面自己画一条,而不是假装成功、更不是白屏。能力没有就报没有,这条规矩省掉的是后面所有的排查工夫。
③ 安全模型是真在工作,不是文档里写着好看
这条是最容易被讲成口号的,所以在 cjnote 里我特意让它「看得见」:
- 页面对文件系统没有任何直接访问,只能调注册过的命令;
- 命令默认不放行:应用注册了 18 条命令,能力清单里明确放行 32 条(含插件命令);没放行的前端一调就被拒,并在 stderr 留一行
尚未授权——你启动时看到的那几条dialog:save提示,就是这套机制在正常工作的证据; - 路径判定只有一处实现(框架共享层里的一个文件),前端不做任何路径检查,把路径原样交上去。 没有第二份会写错的实现,也就没有「某个入口忘了校验」这种经典事故。
④ AI 只有一条路,而且密钥根本不落在应用里
这是 cjnote 里我最想让人抄的一条设计。
页面上零fetch——不是「尽量别用」,是真的一条都没有。聊天走ws插件连本机后端ws://127.0.0.1:8765/ws,流式帧逐字落进对话(delta逐字、工具调用压成一行、审批请求内联成卡片、done收尾)。配置读写走宿主命令,页面没有~/.cjh的写权限。
后端 cjh 是随包分发的:应用启动先探127.0.0.1:8765,端口上已经有活着的就直接复用、不动别人的进程,没有才用随包二进制自己拉起,退出时只收掉自己拉起的那只。所以用户不需要装后端、不需要配路径,而应用自己的store.json里一个 API Key 都没有——密钥框只脱敏回显,留空即不修改。
两个细节我认为最能体现「想清楚了」:
- 保存是两条腿:一条写后端配置文件(管下次拉起、以及协议字段),另一条把地址 / 模型 / 密钥
即时推给正在运行的那只后端。好处是哪怕端口上那只后端是别的程序拉起来的,你在设置页改完立刻生效,不用等重启;只有「接入协议」这一项只认配置文件、才需要重启。 - 「测试连通」不写配置:它利用后端聊天帧支持单次覆盖(
model/baseUrl/apiKey随这一帧生效、不落盘、不粘连),把表单里的值随一条最小对话发出去,另开会话、不污染当前对话。于是它测的就是「我填的这组值能不能用」,而你的配置一个字都没动过。
⑤ AI 上下文只给指针,不给正文
这一条在别处很少见,但它是省 token 和「不读旧内容」的同时解。
每一问默认只带当前笔记的绝对路径(外加笔记库根),正文由 AI 自己按需read_file去读。
为什么不把正文塞进去?因为页面持有的文本可能已经落后于磁盘——你刚存了盘、或者别的编辑器改过,快照就是错的。让 AI 自己去读盘,读到的永远是最新内容,顺带还省了 token。
勾上「问笔记库」才显式要求这一轮做全库检索,否则它不会替你偷偷预取一堆东西。
绝对路径是硬前提:后端的read_file对相对路径是按它当时的工作区解析的,给相对路径会静默读错目录——这种坑不写下来,下一个接入的人一定踩。
⑥ 那些「只有每天用才会疼」的细节,它都处理了
样板和真应用的分界线就在这类地方,我列几个:
- 外部文件不乱写盘:
Ctrl+O打开笔记库外面的.md只进内存,首次Ctrl+S才落成库里的新笔记,
绝不静默改写磁盘原文件; - 重命名是真重命名:保留原来的修改时间、拒绝覆盖已存在的目标名、删除带页内确认,外部文件直接拒绝;
- 大纲跳过围栏:侧栏大纲抽 1~3 级标题,代码块里的
#不算标题,点一条跳一行,编辑器光标和预览区同步滚动; - 最近打开会自愈:库外打开的文件并进左侧列表(只显示文件名、悬停看完整路径),文件被删了那一条自动清掉,最近 8 条持久化,重启还会恢复上次那篇;
- 侧栏能拖宽:160~480px 夹取,双击复位 220px,偏好落盘,长内容的省略号不受影响;
- 选区动作有确认:选中正文 → 重写 / 续写 / 翻译,结果先进 diff 确认条,你接受才改文档,丢弃则原样不动。
⑦ 数字和证据都能自己复现,不靠「我这边跑过了」
我最不想给的就是一句「跑过了」。所以 cjnote 的每个关键结论都留了可复现的取证方式:
- 应用注册18 条命令,能力清单放行32 条(含插件命令),装配6 个插件
(store / http / log / clipboard / dialog / ws); - 页面自检沙盒实测68 PASS / 0 FAIL / 1 SKIP——那 1 条 SKIP 是「环境里没有后端」的探针,连不上就记 SKIP,不会把环境问题算成回归;
- 开发态产物7.6M,单文件产物11.2M,多出来的 3.6M 就是被静态链进来的运行时与插件包,外加内嵌的页面资源。
想自己验一遍,就两条命令(自检结论会回投 stderr):
CJNOTE_SELFCHECK=1./target/release/bin/main2>&1|grepSELFCHECK# SELFCHECK DONE passed=68 failed=0我特别喜欢这套自检的一点:它不只是点按钮测 UI,里面有两条是真连后端的通路——流式聊天探针、以及设置页的测试连通。也就是说,AI 那条链路是被自动测过的,不是「我手动试了下能聊天」就算完。
想跑起来,三步
依赖和框架一样:仓颉 SDK 1.2.0 + 同代 stdx;Linux 另需gcc pkg-config libwebkit2gtk-4.1-dev libgtk-3-dev。
cdexamples/cjnotebash../../native/build_linux.sh# 首次编 C 桥(产物落在框架根 native/)cjpm buildexportLD_LIBRARY_PATH=$CANGJIE_HOME/runtime/lib/linux_x86_64_cjnative:$CANGJIE_STDX:../../native:$LD_LIBRARY_PATH./target/release/bin/main工作目录就是笔记库根,首次启动会在notes/下播种一篇欢迎.md。想要一个文件的交付形态:
bash../../scripts/pack-linux-single.sh# → dist-single/cjnote顺带说清一个最容易读岔的地方:7.6M 和 11.2M 不是两个程序,是同一个应用的两种产物,差别只在「依赖有没有被编进文件里」。另外 Linux 侧有个硬天花板——系统 WebKitGTK / GTK 没有静态库,所以「全静态 ELF」在 Linux 上不可得,能静态下来的只是框架自己带来的那部分依赖。
它的短板,我照实说
- Windows 侧只到「代码齐备、同名导出完整」,没有实机验证。副标题这类能力位会如实降级到页面顶栏,但你如果主力是 Windows,请把它当「预期可用、未取证」看待;
- AI 只认 cjh 一个后端。没有「随便填个 OpenAI 地址就直连」的旁路——这是刻意的(密钥不落进应用),但你要接别的客户端,就得走它提供的这套通路;
- 全库检索是很朴素的
contains(标题 + 正文)。你拿自然语言问一句,通常 0 命中,所以我干脆把检索这件事交给 AI 自己去做(列目录 +grep),页面不做预取; - 插件生态还早,你要的能力可能得自己写一个。好消息是写插件成本很低,而且不用不链接;
- 构建有门槛:仓颉 SDK 得装好、Linux 图形开发包得齐,第一次编 C 桥要等一会儿。
谁适合拿它花半小时
- 你在学仓颉,想知道这门语言写真实桌面应用是什么手感——它是个能跑能用的完整样本;
- 你想要「后端静态编译 + 前端 WebView」这种轻量方案,想先看到一个 11M 单文件的成品长什么样;
- 你在做一个要调本地 AI 后端的应用,想抄一份「页面不碰密钥、上下文给指针、流式帧怎么落」的接线方式。
三条都成立的话,我建议你的第一步不是从零写,而是把examples/cjnote复制一份、改掉业务逻辑——原生菜单、AI 通路、能力清单、单文件打包,该配的都配好了,剩下的才是你自己的东西。
全部代码MIT。框架在 AtomGit 上,cjnote 就在examples/cjnote/;它的 README里连沙盒自检那条命令都写好了。
我不觉得「用新语言重写一个笔记应用」有多了不起——笔记应用满地都是。真正值钱的是它顺带证明的东西:这套框架能让一个应用同时做到「原生能力用得真、安全边界守得住、AI 接得干净、还只要一个 11M 的文件」。
如果你在意的是后者,那 cjnote 值得你看一眼;如果你只想找个笔记软件,那用你顺手的就好。