如果你手上有一个桌面应用要写,并且符合下面任何一条,我建议你花半小时试试 cj-tauri。不装 Node、不捆 Chromium,后端静态编译;九份单文件产物 4.69–14.1MB,双击即用。

📖 官网:https://yangyongzhen.github.io/cj-tauri/ 
📦 仓库:https://atomgit.com/qq8864/cj-tauri 
🧩 样板应用:https://atomgit.com/qq8864/cj-tauri/examples/cjnote/


cjnote是一个使用cj-tauri框架开发的,满足日常办公用的 Markdown 笔记软件AI应用,它的存在本身就是这套框架的能力清单——原生菜单栏、系统副标题、AI 流式对话、笔记增删改查、单文件交付,一样不缺。关键是体积才33M大小左右(因为内嵌了cjh的AI智能体,所以体积才大了点儿)。

还在用Typora?可以玩下这个。体积小但功能一点儿也不弱,因为我将cjh这个仓颉语言写的智能体也嵌入进去啦,打造了一款小巧的AI笔记软件。

在这里插入图片描述
如果你手上有个桌面工具要写,符合下面任何一条,就值得花半小时试试 cj-tauri:

  • 不想为了一个工具装 Node / 捆 Chromium,也不想让用户下 100MB 运行时;
  • 后端逻辑希望是编译成机器码、可审计的,而不是一棵 JS 依赖树;
  • 本来就在仓颉这条技术栈上,希望桌面端和鸿蒙端共用一套后端代码;
  • 想要的安全模型是「没在清单里写的东西,前端就是调不通」;
  • 在用 AI 写代码,希望错误尽早被编译器拦住、改完能自己跑出证据。
  • 不想用Rust,因编译速度慢且还总是占用好几个G的磁盘空间。
  • 不单方便AI,也方便看懂语言语法,仓颉的语法比Rust简单太多,方便审计。

一、cj-tauri 是什么

一句话:用华为仓颉语言实现的 Tauri。前端仍是 HTML/CSS/JS,跑在系统 WebView 里;后端是仓颉静态编译的原生二进制。产物不带 Chromium、不带 Node。

在这里插入图片描述

Tauri 组件cj-tauri 对应说明
WRY + TAOsrc/host.cj + 各平台实现Linux = WebKitGTK,Windows = WebView2,鸿蒙 = ArkWeb
IPC(invoke / event)src/ipc_hub.cjJSON over postMessage,window.__CJ_TAURI__.invoke/listen/emit
capability 模型src/capability.cj + capabilities/*.json命令 / 事件白名单,默认最小权限
@tauri-apps/api注入的桥接 JS宿主在页面脚本之前注入
tauri-clicli/cj-tauricreate / dev / build / run / info / doctor

写一个应用就三步(对应 Tauri 的 command / Builder / capabilities):

public class GreetCommand <: CommandHandler {
    public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
        return JsonString("Hello, ${argString(args, "name", "world")}!")
    }
}

main(): Int64 {
    let app = TauriApp()
        .window(WindowConfig("hello", 1000, 700))
        .register("greet", GreetCommand())
    app.run(html)
    return 0
}
window.__CJ_TAURI__.invoke('greet', { name: '仓颉' }).then(d => console.log(d));

二、为什么另起一套

不是「再写一个 Tauri」,是三件很具体的事:

  1. 仓颉需要一个真实的桌面落地场景。 语言能力(编译、FFI、条件编译)都在,但「能不能用它做完一个能交付的 GUI 应用」只有真做出来才算数。
  2. 混合开发的体积账值得重算。 桌面端最贵的从来不是业务代码,是「为了跑业务代码而捆进来的运行时」——系统 WebView 就在那儿。
  3. 鸿蒙这条线,后端同源最省。 ArkWeb 与桌面 WebView 的差别主要在宿主层,IPC、命令分发、能力校验本该共用。

这三件事决定了框架的形状:平台差异只允许出现在两处——仓颉侧 @When 条件编译、C 桥 native/platforms/*/,其余一切跨平台共用。这条规矩到今天没被破过。

三、五个值得单独说的设计

1. 小,而且小得可核对

单个示例产物 4~10MB;仓库随版本分发九份静态链接的单文件 Windows 工具(4.69–9.79MB,另加内嵌 AI 后端的 cjnote 34.1MB),免安装、双击即用,PE 导入表里只有系统 DLL,双击不弹控制台。

这不是宣传口径,是一条命令能复核的口径:

bash scripts/dist-report.sh     # 体积、依赖、子系统逐项复算

2. 默认拒绝,而且分两层

第一层是命令级白名单:capabilities/*.json 里没声明的命令,前端调了就是 command not allowed,stderr 留一行凭证。

第二层更值钱——命令放行 ≠ 参数放行:

  • 路径参数走 scopes({"fs": {"allow": ["$HOME/notes/**"], "deny": ["**/id_rsa"]}}),先 deny 后 allow、allow 必须显式命中、匹配前先规范化(顺带解析软链接);
  • 出网口(http / ws 插件)走主机白名单,没写 = 全拒,且禁自动重定向;
  • 判定逻辑只有共享层一份,插件要用就调 IpcContext.resolveScopedPath(...),不允许各平台实现里再判一遍。

后果是明确的:页面是可能的攻击面,而页面能碰到的每个字符串,都在进宿主之前被一层统一的判断挡过;想绕过就得改共享层,而共享层是有单测的。

3. 插件:一行接入,不用不链接

14 个官方插件(fs / dialog / shell / serial / store / clipboard / notification / menu / mqtt / redis / log / http / ws / sqlite),接入就一行:

let app = TauriApp().plugin(FsPlugin()).plugin(StorePlugin())

值得说的是**「一个插件一个子包」**这个决定。最早所有插件挤在一个包里,结果只用 fs 的应用会被 sqlite 的 FFI 连坐,Windows 上还要跟着链 -lsqlite3。拆成一个插件一个静态库之后,没引用的插件根本不进链接。

权限也不想当然:插件只声明「我提供什么」,不自动放行——放行永远是应用清单说了算。声明与放行分开,是这套体系里最容易抄错、也最不该抄错的一条。

4. 可验证性是被设计进去的

框架里写着一句硬规矩:行为变更必须给出可观测证据。所以日志不是「调试用的」,是交付凭证:

  • 桥的 stderr 每行即时落盘,端到端验证以它为准(仓颉 println 有缓冲,被强杀会丢日志,诊断一律走 stderr);
  • 应用可以开页面自检:断言经 report 命令回投 stderr,于是「无人值守跑一轮」可行;
  • 命令失败必留 command rejected (...) / command failed (...),前缀不同方便分流——「日志里没有错误行」从来不等于「命令成功了」,这是被实际坑过才写进规范的。

配套的测试也是真在跑:

bash scripts/test.sh              # 302 个单测
bash scripts/test-bridge-core.sh  # C 桥公共核心的桩自检,不依赖 SDK 与图形栈
bash scripts/check-static.sh      # 免 SDK 的静态门禁,CI 跑的就是它(27 项)

test-bridge-core.sh 用桩平台编 C 桥核心,任何 Linux 机器上都能跑——把可验证的部分从平台里剥出来,是我在这个项目里最喜欢的工程习惯。

5. 双平台同名导出,上层不长平台分支

IPC、能力校验、命令分发跨平台共用;平台实现只在 C 桥各写一份 cj_bridge_*,且导出名必须逐字一致。应用代码里不会出现「Linux 这么调、Windows 那么调」。

能力位如实上报:没实现的能力 system:host 就报 0,应用据此优雅降级——cjnote 的副标题就是这么写的(有原生副标题就用,没有就退回页面自己画一条)。平台没这能力就如实说没有,比「假装成功」省掉后面所有排查。

四、AI 时代,选语言本身就是优势

这两年写代码的方式变了:很大一部分是「我描述、AI 落笔、编译器裁决」。这个流程里决定效率的不是 AI 多聪明,而是错误暴露得多早、上下文能不能一次喂清、改完能不能自己证明。系统级语言被重新追捧,根子就在这里——Rust 是最有名的答案,仓颉也在这条线上,而且有几点别处不容易凑齐。

① 静态类型是给 AI 幻觉设的落点。 动态语言里 AI 编错的 API 名字要跑到那一行才炸,仓颉编译期就过不去。框架一侧就有 15 838 行仓颉代码(50 个源文件)、302 个单测、两平台同名的 C 桥导出——编译器的报错比 AI 的自述可信。

② 后端只有一门语言,上下文小。 没有「Node 那一半」要同时记住:命令就是类、能力就是一份 JSON、前端只有 invoke / listen / emit、平台差异被压到两处。AI 说「加个命令」,要改的就是注册处 + 清单各一行。

③ 仓库本身就是按 AI 协作的要求长出来的。 根目录 AGENTS.md 是开发契约,§4 是一份几十条的**「已踩坑清单」**——「编译器报错指向的不是真原因」那种(少一行 import std.convert.* 报 undeclared identifier 'parse';TlsClientConfig 不在 net.http 里,漏 import 报「这个类型不存在」)。配合同样沉淀出的 5 个 AtomCode skills:环境检测 → 应用开发 → 实机取证 → 踩坑速查 → 回贡献。

④ 「改完能自己证明」是收口。 页面自检回投 stderr、桥日志即时落盘、单测 + 桩自检 + 免 SDK 静态门禁三道闸门——一轮 AI 改动是有机器判据的:编得过、跑得出、证据躺在 stderr 里。没有证据的「已完成」不算完成。

⑤ 语法简单、代码短,编出来的东西也小。 类型推断、模式匹配、lambda、字符串插值都在,一个命令处理器六行、一个窗口应用十来行;FFI 一行 foreign func,本仓 SQLite 插件就是直连系统 libsqlite3,中间没有 C 桥包装。产物是静态编译的原生二进制,没有 VM、不要求用户先装运行时。

Rust 的代价也很具体,而这恰好是仓颉能补的地方:编译慢、构建目录吐垃圾——依赖一多、泛型单态化与过程宏一起来,target/ 动辄几百 MB 到几 GB。本机没装 cargo,不做同机对照,只给仓颉侧实测,你自己按比例感受:

项目实测
examples/hello 全量冷编译(静态链入框架)50 秒
同一示例改一行再编25 秒
构建目录 target/(该示例 / 框架本身)8.3M / 36M
产物 main(静态链接)4.5M
独立小程序(leetcode-gui 的判题器,静态链 + strip)冷编译 0.4–1 秒,产物 2.7M

中间产物是几十兆级不是几个 G,全量编译是几十秒级不是几分钟。不过因果要说清:仓颉快,一部分是因为项目本身瘦(依赖少、没有一排第三方 crate 要先编)和工具链轻,不是编译器比 rustc 更聪明。

代价也照实说:库生态远小于 crates.io,很多轮子得自己造(本仓的 MQTT、Redis RESP2 都是纯仓颉手写——好处是零第三方依赖,代价是自己写);工具链还在快速演进,版本配对的坑真实存在(cjc 与 opt 差一代会崩、cjpm 增量缓存会跳过链接),所以本仓的判定统一成「看产物」而不是「看 build success」。

一句话:要「AI 帮你写 + 编译器把关 + 自检收口」这套工作流,仓颉很合适;要现成的海量第三方库,先去 Rust。

五、示例不是演示壳

examples/ 下有 27 个可直接运行的桌面示例(外加 1 个鸿蒙探针工程 ohos-probe),另有三个脚手架模板(零 Node 的纯内联版 / Vue 3 / React 18)。挑几个最能说明问题的:

示例一句话
modbus-pollModbus TCP/RTU 调试工具:多面板并行监控、FC 01-04/05/06/15/16、报文监视、CSV 记录——对标 Modbus Poll 并补齐它让人难受的地方
mqtt-clientMQTT 调试台:协议由 mqtt 插件纯仓颉实现(没用现成库),自带 mock broker,实机 E2E 27 条断言两侧对账
redis-clientRedis 调试台:SCAN 游标分页 + MATCH + TYPE 过滤、类型化取值写值、发布订阅,RESP2 双 socket 同样自研,实机 E2E 47 条断言
sqlite-browserSQLite 浏览器:仓颉 FFI 直连系统 libsqlite3(不经 C 桥),只读走内核级连接、ATTACH 由 authorizer 按语句拒
http-clientHTTP API 客户端:页面零 fetch,请求全由仓颉侧直发(TLS 走系统证书库、不关校验),E2E 55 条断言含截断三侧对账
cj-link串口/网络二合一调试助手:串口 + TCP 客户端/服务器 + UDP 共用收发日志,HEX/ASCII、CRC、定时发送
leetcode-gui桌面版算法练习:点「运行」把代码交给 cjc 编译再逐用例判定——判题器就是编译器,全程离线
clipboard-history剪贴板历史管理器:托盘 + 全局快捷键 + 最小化到托盘的首个消费者,Windows 与 Linux 双实机

再往下还有 music / movie / typing-poem / markdown-notes / serial-waveform / multi-window / ipc-bench……每个示例带自己的 README 与自检方式。

免安装试玩:九份单文件产物

下面每份都是静态链接的单文件 Windows 可执行程序(GUI 子系统、双击不弹控制台、PE 导入表里只有系统 DLL),点文件名进仓库文件页下载即可:

产物体积说明
cj-link.exe5.48MB串口/网络二合一调试助手(串口 + TCP/UDP,HEX/ASCII、CRC、定时发送)
modbus-poll.exe5.59MBModbus TCP/RTU 调试工具(多面板并行监控、报文监视、CSV 记录)
serial-waveform.exe4.69MB串口实时波形示波器(400 Hz 样本流下页面自算速率/上穿/丢帧)
markdown-notes.exe4.78MBMarkdown 笔记(文件树 / 编辑 / 实时预览三态,marked + Highlight.js 内联)
leetcode-gui.exe9.79MB仓颉力扣(题库 + 编辑器 + 判题终端,判题器就是 cjc)
music.exe9.50MB音乐播放器(榜单/歌单/收藏 + 同步歌词 + 4 套主题色)
movie.exe9.42MB观影应用(数据取自第三方公开接口,仅供学习研究)
typing-poem.exe5.50MB古诗词打字练习(three.js 3D 卡面)
cjnote.exe34.1MBAI 原生 Markdown 笔记,cjh 与 4 个运行时 DLL 一并嵌进 exe(下一节详说)

最大的那份是 cjnote——因为它把一整个 AI 后端装进了同一个文件;其余八份 4.69–9.79MB,也就是一个中等网页的体积。体积、依赖与子系统都能一条命令复算。

六、cjnote:把这套能力一次用足

examples/cjnote/ —— 左笔记库、中分屏(代码高亮 + 行号)、右 AI 助手。顶栏只剩「文件 · 状态 · 字数」,因为标题与保存状态是通过 setTitlebarSubtitle 推到了系统菜单行右侧。

它不是为了演示写的壳:笔记是真 .md、笔记库是真目录、AI 是真接后端,日常可以拿来用。推荐它当样板有三个理由:

① 原生能力用得克制且诚实。 文件 / 视图 / 帮助三个顶层菜单、Ctrl+O/P/N/S/Q 全套快捷键、副标题、行号开关,全走宿主能力位;副标题在能力位缺失的平台(如 WebView2)上自动退回页面顶栏,不假装成功也不白屏。菜单勾选与页面状态双向同步。

② AI 只有一条路,密钥不留在应用里。 页面零 fetch,聊天走 ws 插件连本机 cjh 的 ws://127.0.0.1:8765/ws 收流式帧;配置读写走宿主命令,页面没有 ~/.cjh 的写权限。cjh 随包分发、自动拉起、退出收尾(端口上已有活着的就复用、不动别人的进程)。结果是应用自己的 store.json 里一个 API Key 都没有,设置页的密钥框只脱敏回显、留空即不修改。

两个我挺得意的小设计:保存是两条腿——写 cjh 的配置文件(管下次拉起与协议字段)+ 把地址 / 模型 / 密钥即时推给正在运行的那只 cjh,所以哪怕那只 cjh 是别的程序拉起来的,改完也立刻生效;「测试连通」用帧级覆盖——把表单里的值随一条最小对话发过去,单次生效、不落盘、另开会话,配置一个字都没动。

③ AI 上下文只给指针,不给正文。 每一问默认只带当前笔记的绝对路径(外加笔记库根),正文由 AI 自己按需 read_file——省 token,也保证 AI 读到的永远是盘上最新内容(页面持有的文本可能已经落后于磁盘)。勾「问笔记库」才要求这一轮做全库检索。(绝对路径是硬前提:cjh 的 read_file 对相对路径按它当时的工作区解析,给相对路径会静默读错目录。)

实测数字(都能自己复现):

项目值
应用命令18 条(note:* / cjh:* / theme:* / report 等),能力清单放行 32 条
页面自检沙盒实测 68 PASS / 0 FAIL / 1 SKIP(含真实 cjh 的聊天与连通探针,连不上记 SKIP 不算失败)
开发态产物target/release/bin/main 7.6M
单文件产物Linux dist-single/cjnote 11.2M(页面 / 前端库 / 能力清单全内嵌);Windows cjnote.exe 34.1MB(另把 cjh 与 4 个运行时 DLL 嵌进 PE 资源,首次运行释放到 %LOCALAPPDATA%\cjnote\runtime\)
装配插件6 个(store / http / log / clipboard / dialog / ws)

Windows 侧两个形态都实测过:cjnote.exe 在干净目录双击等价启动,cjh 自行释放并拉起、8765 LISTENING、页面 ws 连上,cjh 窗口句柄为 0、可见控制台窗口 0 个。

七、三条上手路径

① 只想先看一眼效果(什么都不装):从上一节下 cjnote.exe 或任意一份单文件产物,双击就用。

② 想写自己的应用:

npx cj-tauri create myapp    # 前置:仓颉 SDK 1.0.5+ 与 stdx
cd myapp
npx cj-tauri doctor          # 缺什么、怎么补,逐行告诉你
npx cj-tauri dev             # 开发模式;Vue/React 模板会接管 Vite dev server,HMR 在窗口内生效
npx cj-tauri build           # 出产物

跨平台链接、GUI 子系统(Windows 下别弹 DOS 黑窗)这些坑,脚手架生成时就替你填好了。

③ 想看完整应用长什么样,把 cjnote 跑起来:

cd examples/cjnote
bash ../../native/build_linux.sh     # 首次编 C 桥(产物落在框架根 native/)
cjpm build
export LD_LIBRARY_PATH=$CANGJIE_HOME/runtime/lib/linux_x86_64_cjnative:$CANGJIE_STDX:../../native:$LD_LIBRARY_PATH
./target/release/bin/main            # 笔记库是当前目录的 notes/,首启种一篇「欢迎.md」

沙盒自检:CJNOTE_SELFCHECK=1 ./target/release/bin/main 2>&1 | grep SELFCHECK;交付成单文件:bash ../../scripts/pack-linux-single.sh。

八、不足,也照实说

推荐一个东西时把短板藏起来没意义。这些希望你用之前就知道:

  • 鸿蒙还在半路:ArkWeb 宿主已在真机闭环(页面往返、多窗口 label 路由都取证过),但 C 桥尚未落地,宿主能力位仍全 0——菜单 / 托盘 / 快捷键 / 拖放 / 对话框在鸿蒙上一律如实拒绝,不是「悄悄不生效」。
  • macOS 没有做。 只有 Linux / Windows 两个宿主实机跑通,鸿蒙是第三个。我不写我没验证过的东西。
  • Linux 拿不到「全静态」:系统 WebKitGTK / GTK 没有静态库,单文件产物仍动态依赖系统图形栈;能静态下来的只是本框架带来的那部分(桥、仓颉运行时、stdx、各插件包)。
  • 插件只有 14 个,生态还在早期,想要的能力可能得自己写一个。好消息是成本很低(一个子包 + name() + commands()),而且不用不链接。
  • 构建有门槛:仓颉 SDK 1.0.5+ 与 stdx 得装好;好在有 doctor 和 5 个 skills 兜着。

九、想一起用起来

  • 官网(先看这个):https://yangyongzhen.github.io/cj-tauri/ —— 中英双语、19 屏实机截图、特性与文档入口
  • 仓库:https://atomgit.com/qq8864/cj-tauri(GitHub 有镜像,国内直连建议 AtomGit)
  • 单文件产物下载:见上文「免安装试玩」一节
  • 上手文档:使用文档 → 前端入门教程 → 插件体系教程(均在本仓 docs/)
  • 想改点什么:CONTRIBUTING、docs/设计决策记录.md(每条决策都写了理由与代价)、docs/需求追踪.md(每个能力做到哪、证据在哪)
  • AI 助手辅助开发:装 5 个 AtomCode skills
atomcode plugin marketplace add https://atomgit.com/qq8864/cj-tauri.git
atomcode plugin install cj-tauri-skills@cj-tauri-skills

全部代码 MIT。你要做的第一件事,我建议不是「从零写」,而是把 examples/cjnote 复制一份、改掉业务逻辑——原生菜单、AI 通路、能力清单、单文件打包,该配的都配好了。

最后

桌面开发这些年一直在两个极端间来回:要么轻(但什么都要自己写),要么方便(但捆进来一整个浏览器)。系统 WebView 这条路证明过能走通,只是它需要一门能静态编译、能直接调 C、还能编译到鸿蒙的语言在后面撑着——仓颉恰好是这门语言,cj-tauri 就是把它用在桌面上的一次认真尝试。

它还不完美(见上一节),但它是能用、能核对、能改的:每个数字都有脚本可复算,每条设计决策都写了理由和代价,每个示例都能自己跑出自检结论。

如果你在找一个「小、快、安全模型清楚、后端可审计」的桌面方案,至少花半小时试一下——从官网进去,或直接从 examples/cjnote 开始。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐