三个官方插件的源码在仓库 plugins/ 下,每个都是一个标准的 SDK 工程(plugin.json + src/ + panel/ + locales/ + test/),
与你用 uniflow-plugin init 生成的项目结构相同,用同一个 uniflow-plugin build 打包。每个插件的 README.md 都是一篇开发者导读,
这里只列出最值得借鉴的设计,按「你想做的事」索引。
| 插件 | 规模 | 权限 | 适合学什么 |
|---|---|---|---|
闪卡 flashcard |
22 个命令(10 个给 UniMind)、106 个测试 | courses、storage、files.open/save、ui.panel | 存储建模、配额、软删除、批量写入、面板状态机、面板自己的撤销、导入解析、多种卡型 |
考试规划 exam |
12 个命令(8 个给 UniMind)、82 个测试 | courses、calendar.read/write、storage、ui.panel | 写日历、与用户编辑共存、撤销、不靠 AI 的面板工作流 |
日历同步 ics |
11 个命令(8 个给 UniMind)、138 个测试 | courses、assignments、calendar、storage、storage.device、network、files、ui.panel | 联网、凭据保存、后台任务、跨设备一致性、解析不可信数据 |
我想……
把用户的数据存好 → 闪卡 README「数据模型」「删除:软删除 + 最近删除」「配额与结果预算」:一张卡一个文档、cardCount 只是缓存、
删除只记 deletedAt、在 describe 里就按剩余配额拦截。
让 UniMind 用得准 → 闪卡的 list_decks / get_deck:7000 字预算、nextOffset 翻页、sort 参数;面板要整批数据时另开
agentVisible:false 的 list_all_decks,不让模型拿到超大结果。
写日历但不和用户打架 → 考试规划 README「日历为准:resolveBlocks」:用户会在日历里拖动、删除复习块,插件读回时以日历为准, 而不是以自己存的为准;「calendar.ready() 的三态」:分清「用户删光了」与「还没加载完」。
并发安全 → 考试规划的 serialByPlan、闪卡与日历同步的 exclusive(id):沙箱里每次能力调用都是异步的,读改写要按对象串行。
可靠的撤销 → 考试规划「撤销删除为什么换新 id」(云端墓碑会把同 id 的恢复再删掉);闪卡「卡片删除写撤销日志」
(删除前把原样数据写进 trash 集合,撤销不依赖内存)。
联网与凭据 → 日历同步「地址等同密码」:订阅地址只存 storage.device,云端只放不含地址的登记;确认卡片显示完整地址,
结果与错误里一律脱敏。
跨设备一致 → 日历同步「墓碑与代」:不比较设备时钟,用「代」(epoch) 与 supersedes 判断谁新谁旧。
处理不可信输入 → 日历同步「解析工作量预算」(恶意 .ics 能让解析跑几小时:按工作量而不是墙钟判「太复杂」);
闪卡 src/csv.ts(Anki / Quizlet 导出的各种边界情况,纯函数单独测试)。
后台任务 → 日历同步的 refresh:并发 3、总时限 45 秒、写入阶段 ≤ 10 秒;源突然变空或少了一大半时「这次先不删」。
面板 → 闪卡:今日计划(进度环)、16 周热力图、到期预测、卡组网格、按卡型复习(选择题当场判对错、完形逐空揭开、朗读、撤销上一张)、
不写排期的练习模式(测验 / 默写 / 听写)、卡片详情的记忆记录;考试规划:进度环、时间线、行内表单、
「先 find_free_time 再本地排期再 create_plan」的手动工作流(面板只用命令组合出功能);
日历同步:嵌入设置页的面板(embedded)、导入预览(pick_file → preview_import → import_text 三个无状态命令)。
测试 → 三个插件的 test/:宿主测试夹具跑在真实的权限裁剪与执行器上;需要控制时间时用 mock.timers 冻结 Date。
自己动手改一个
cp -r plugins/flashcard my-cards
# 1. plugin.json 里把 id 改成你自己的(官方 id 只能由官方包使用)、author 改成你
# 2. npm run dev,在开发者模式里加载 my-cards/dist