Skip to main content
Browse the docs

The developer docs are currently available in Chinese only. The SDK, the CLI and every code sample work the same in any language.

Chapter 15

官方插件导读

闪卡 / 考试规划 / 日历同步:值得借鉴的设计

三个官方插件的源码在仓库 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。

自己动手改一个

Terminal
cp -r plugins/flashcard my-cards
# 1. plugin.json 里把 id 改成你自己的(官方 id 只能由官方包使用)、author 改成你
# 2. npm run dev,在开发者模式里加载 my-cards/dist