Super Productivity 从 Todoist 导入数据:完整指南与实现原理 Super Productivity 从 Todoist 导入数据完整指南与实现原理【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivitySuper Productivity 内置了基于官方插件机制的 Todoist 一次性导入能力只需一个 API Token就能把 Todoist 中处于活跃状态的项目、任务、子任务、标签与截止日期一次性地迁移进 Super Productivity并且整个过程是纯增量的——只新建项目、标签和任务绝不改动或删除你已有的任何数据。读完本文你将掌握从获取 Token 到执行导入的完整操作步骤、优先级Priority映射的两种策略、导入边界哪些内容会被有意舍弃以及这套导入流程在源码层面的实现细节与隐私边界。一、导入功能概览与前置条件Todoist 导入是 Super Productivity 随主应用打包的一次性导入器bundled plugin它的定位与一个持续同步的 Todoist 集成有本质区别一次性执行一次把当前活跃数据搬入 Super Productivity之后两边不再保持同步纯增量additive导入只会创建新的项目、标签和任务永远不会修改或移除 Super Productivity 中已有的数据非事务性导入按项目逐个进行中途失败最多留下一个不完整的项目错误信息会明确告诉你该删除哪个项目后再重试。这个定位在插件清单中写得很清楚见 manifest.json插件名Todoist Import要求主应用版本minSupVersion: 18.0.0描述为 “One-time import of your active Todoist projects, tasks, sub-tasks, labels and due dates. Additive — your existing Super Productivity data is never touched.”。你需要准备什么开始之前只需要一样东西Todoist 账号及其 API Token。获取方式登录 Todoist打开Settings设置→ Integrations集成→ Developer开发者复制页面上的 API Token 即可。关于 Token 的安全性插件 READMEpackages/plugin-dev/todoist-import/README.md明确了数据与隐私边界Token 只存在于导入会话的 iframe 内存中它只被发送到api.todoist.com绝不存储、绝不同步、绝不写入日志因此插件 manifest 声明了http权限并且allowedHosts被严格限制为[api.todoist.com]任何其他域名的请求都不会被放行。二、操作步骤从 Token 到导入完成整个导入界面由三个步骤构成token 输入 → 预览与选择 → 导入进度与汇总对应的界面逻辑实现在 packages/plugin-dev/todoist-import/src/ui/main.ts。第 1 步打开导入入口点击侧边栏的齿轮图标打开Settings设置打开Sync Backup同步与备份标签页找到Import/Export导入/导出区域点击Import from Todoist从 Todoist 导入。第 2 步粘贴 Token 并加载预览在弹出的导入面板中把 Todoist API Token 粘贴到密码输入框中点击Load preview加载预览。这一步的底层动作是调用 Todoist 的 unified Sync API v1https://api.todoist.com/api/v1/sync请求过程分两次第一次以sync_token*发起全量快照请求第二次用返回的sync_token发起增量请求把延迟窗口内发生变更的数据一并合并进来确保预览足够新。两次响应的合并逻辑见 from-api.ts 中的mergeSyncResponses增量资源按 id 覆盖全量快照中的旧资源包括删除墓碑既不会重复也不会复活已删除的数据。真正的请求封装在 load-todoist-data.ts。第 3 步预览、选择项目与优先级策略预览界面会列出从 Todoist 拉取到的所有项目每行显示项目标题及其任务/子任务数量并给出复选框默认勾选尚未与现有 Super Productivity 项目重名的项目项目名已存在时例如上一次导入运行创建过同名项目该项目会被标记为 “already exists已存在” 并默认取消勾选避免重复导入。预览界面还会让你选择 Todoist 优先级的处理方式默认关闭详见下文“优先级映射”一节并实时列出本次导入将无法承载的内容清单lossy notes。第 4 步执行导入并核对汇总点击Import导入后界面按项目逐个执行导入并展示进度当前项目、第几个项目、处于“建项目/建任务/补细节”哪个阶段。完成后展示汇总内容包括每个项目计划创建与实际落地的任务数、子任务数——若两者不一致会给出 shortfall 警告新创建的标签列表一切未能承载过去的内容清单见下文“不会导入什么”。在 run-import.ts 的实现中导入执行完毕后会重新读取全部任务并对比计划数量与实际数量countLanded因为批量创建接口本身是“发出即忘”的fire-and-forget结果并不权威如果重读失败汇总会显示 “count unverified” 而不是把未知当作零。三、会导入什么字段映射全景导入范围由解析器 from-api.ts 与规范化模型 normalized-model.ts 共同决定。逐项对应关系如下Todoist 数据导入后的形态活跃项目Active projects作为项目导入嵌套项目层级会被拍平Todoist 收件箱Inbox会成为名为 “Inbox (Todoist)” 的项目避免与 Super Productivity 自带的收件箱混淆活跃任务与子任务任务与子任务导入Super Productivity 只嵌套两层更深层的子任务会被提升为其顶层任务的直接子任务任务描述与评论Comments合并写入任务笔记task notes评论中的 HTTP(S) 文件附件会保留其链接标签Labels转为标签tag只加在顶层任务上截止日期Due dates转为截止日期带具体时刻的截止时间一并保留分钟级时长Duration转为时间估算time estimate按分钟换算为毫秒循环任务Recurring保留下一次截止日期循环规则文本追加到任务笔记例如Repeats: every 3 days从源码看解析器在规范化阶段就完成了以下处理parseSyncResponse过滤已完成/已删除的数据checked与is_deleted为真的任务、is_archived/is_deleted的项目及其任务全部跳过子任务两层化任务树按 DFS深度优先读取顺序遍历任何深度大于 1 的任务都被重新挂到其根祖先之下wasDemoted: true父任务缺失的子任务其父已完成或已删除会被当作顶层任务处理父子顺序保证任务列表最终顺序保证“父任务总是先于子任务出现”且同一父级下的子任务按child_order排序这是后续批量创建能安全分块的前提截止日期与期限deadline的取舍有截止日期而无 due 日期时deadline 直接充当 dueDay二者同时存在时deadline 会以Deadline: YYYY-MM-DD的形式写入笔记不静默丢弃。一个值得注意的实现细节Todoist 的时间语义——纯日期YYYY-MM-DD按本地时间处理带时区的日期尾随Z按 UTC 瞬间处理源码注释明确写了这是 “Todoists floating vs fixed semantics” 的精确对应。四、优先级映射两种可选的携带方式Todoist 的优先级默认不导入off by default。在预览界面中你可以从三种互斥选项中任选其一在 ui/main.ts 中以单选按钮实现对应的常量定义在 plan-import.ts方案 Ap1–p3 标签给每个顶层任务打上p1、p2或p3标签。Todoist 默认的 p4即 API 优先级值 1保持不打标签。源码中的映射表为const PRIORITY_TAG_BY_API_VALUE: Recordnumber, string { 4: p1, // Todoist UI 中的 p1最高 3: p2, 2: p3, // API 值 1 Todoist 默认 p4刻意不打标签 };方案 B艾森豪威尔矩阵Eisenhower matrix复用 Super Productivity 内置的urgent紧急/important重要标签让导入的任务直接出现在艾森豪威尔矩阵看板Eisenhower Matrix board中Todoist 优先级API 值映射标签矩阵象限p14urgentimportant重要且紧急p23important重要不紧急p32urgent紧急不重要p41无都不打标签源码注释明确提醒Todoist 的优先级是单轴概念把它拆到矩阵的“紧急/重要”两轴上本来就是一种有损映射“这是合理的默认切分而非精确翻译”。由于 Super Productivity 内置标签按标题复用ensureTags大小写不敏感匹配导入的任务会落进已有的EM_URGENT/EM_IMPORTANT象限而不会新建重复标签。无论选择哪种方案优先级标签都只加在顶层任务上——Super Productivity 的子任务不能挂标签这是宿主数据模型决定的插件在taskTagTitles中对parentExtId非空的任务直接返回空数组。预览界面会统计“因是子任务而无法获得所选优先级标签”的任务数量并如实报告。五、不会导入什么明确的有损边界受限于 Super Productivity 的数据模型与导入的一次性定位以下 Todoist 数据不会被导入预览与汇总界面都会诚实列出已完成的任务与任务历史completed tasks and task history嵌套项目层级与分区nested project hierarchy and sections——但项目与任务的顺序会保留子任务上的标签与映射后的优先级提醒reminders、全天时长full-day durations、协作者指派collaborator assignees与附件文件attachment files作为“真正的循环任务”的循环规则——重要的循环任务请用 2.06-Manage-Repeating-Tasks 手动重建从源码可以精确印证这些边界isDayDurationSkipped标记了以“天”为单位的时长unit 为day被刻意跳过只有minute单位且数值在 1 到 525600一年之间、且为有限数值的时长才会换算为时间估算hasAssignee记录了任务是否有responsible_uid指派对象但导入时不落地评论的文件本体不会下载只有合法的http:/https:附件 URL 会以文件名: URL的形式保留在笔记文本中isSupportedAttachmentUrl会先做协议过滤与空白字符校验防止非安全链接进入会被渲染成 Markdown 的笔记。安全长度上限为了不污染同步状态所有导入数据都会进入每个客户端必须重放的操作日志解析器对远程数据做了防御性截断字段上限处理方式项目名 / 任务标题1000 字符超长截断并追加…标签名200 字符同上任务笔记50,000 字符同上截止日期时间戳1970 3000 年超出范围视为无效并丢弃预览界面会报告本次选中的值中有多少被截断影响truncatedFieldCount。同样的防御还体现在日期校验上isValidDayStr不仅校验YYYY-MM-DD的形态还会回读Date对象比对拒绝2026-99-99这类“形状合法但日历非法”的值时长数值用Number.isFinite拦截1e999 → Infinity避免不同客户端序列化结果不一致。六、中途失败、重复运行与撤销导入按项目逐个进行不是事务性的。理解以下三条规则能帮你安全地处理异常情况1. 中途失败时如果导入中途停止例如网络断开错误信息会明确指出当前那个项目名。请删除这个名字所指的项目再重跑——它可能是残缺的。已完成导入的其他项目是完整的不需要动。2. 重复运行时已经成功导入、且仍处于活跃状态的项目会在预览界面中被识别并默认取消勾选。要注意重名检测只能看到活跃的 Super Productivity 项目。如果之前导入的项目已被归档archived预览无法识别到它请先恢复restore或删除它再重新运行导入才能被正确检测并避免重复。3. 撤销导入导入是增量的、可逆的直接删除导入时创建的那些项目即可完全撤销。汇总界面也会提示这一点SUMMARY.UNDO。七、实现原理导入如何一步步落地理解了操作层再来看导入在源码里是如何被安全地执行的。整个管线分三个阶段每个阶段都有对应的纯函数与配套单元测试测试文件与实现同目录存放解析parseloadTodoistData拉取 →mergeSyncResponses合并全量/增量 →parseSyncResponse规整成中间模型TodoistImportModel见 normalized-model.ts规划planplanImport把中间模型转换成可执行计划ImportPlan——项目列表、每个项目的批量创建操作分块、以及到期日/标签等后续补写操作见 plan-import.ts执行runrunImport按项目执行计划、记录落地数量、产出汇总见 run-import.ts。批量操作的不变量batch invariants由于宿主对批量更新接口有严格的同步规则run-import.ts 与 plan-import.ts 的注释明确列出了四条必须遵守的不变量每次batchUpdateForProject调用最多发送 50 个操作BATCH_CHUNK_SIZE 50必须小于宿主的MAX_BATCH_OPERATIONS_SIZE并且是串行 await——每次调用只派发一个 action落在独立的事件循环 tick 里如果让桥接层自己分块所有块会挤在同一 tick 内派发父任务先于子任务创建未解析的父任务使用temp-前缀的临时 IDtempId (extId) \temp-${extId}——批量 reducer 只解析这个前缀的父引用其他任何写法都会导致子任务被孤悬而遭删除在发起下一次批量调用之前把上一批返回的真实 ID 回填替换掉父任务的临时 IDresolveKnownParents。因为桥接层每次调用是独立构建临时 ID 映射的跨调用残留的temp-引用会在一致性检查中被当作悬空引用直接删除子任务。两段式落地先创建再补写细节由于批量创建契约不携带到期日和标签导入采用两段式第一批次只创建项目、任务/子任务的标题、笔记与时间估算随后逐个updateTask补写dueDay/dueWithTime与tagIds。补写细节阶段每 25 个任务上报一次进度DETAIL_PROGRESS_STEP 25。标签写入还有一个专门的保护Super Productivity 有一个虚拟的 “Today” 标签TODAY_TAG_ID它绝不能被写入任务的tagIds同步规则 #5。因此ensureTags在为标签建映射时会把TODAY跳过——如果你的 Todoist 里恰好有个叫 “Today” 的标签它会作为一个真实标签单独创建而不会误映射到虚拟标签上。落地核对计划 vs 实际批量创建接口“总是报告成功并静默跳过非法操作”所以导入完成后countLanded会重新读取全部任务按项目统计顶层任务与子任务的实际数量与计划数量对比后呈现在汇总里若重读失败则标记isCountUnverified不会把“未知”伪装成“零”。八、隐私与权限边界总结最后回到安全模型。插件在 manifest 中声明的权限manifest.json是理解它行为边界的关键permissions: [http, getTasks, getAllProjects, addProject, getAllTags, addTag, batchUpdateForProject, updateTask, showSnack], allowedHosts: [api.todoist.com]网络权限只有http且allowedHosts只有api.todoist.com一个域名Token 不会流向任何第三方Token 只存活在导入会话的 iframe 内存中不落盘、不记录、不参与同步插件拥有的是“读 Todoist、写 Super Productivity”的最小权限集没有钩子hooks且isSkipMenuEntry: true——它不会出现在普通插件菜单里只能通过设置页的导入入口触发导入数据会进入同步操作日志op-log因此对远程字段的大小截断与日期合法性校验本质上是保护所有同步客户端的重放一致性。至此无论你是想“一把梭”把 Todoist 里的活跃任务迁入 Super Productivity还是想了解这套导入在增量语义、批量一致性、隐私边界上的工程取舍都能按图索骥操作路径见本文第二至六节底层实现与测试见 packages/plugin-dev/todoist-import/ 目录下的parse/、map/、ui/三组源码。【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考