双击一个 exe,桌面上多出两张纸:左边一张待办,右边一张笔记。没有主界面,没有登录框,也没有项目列表。

PaperTodo v3.40 就是这样一款软件。待办和笔记各自是独立的小窗口,直接摆在壁纸上面,想写字就点一下,勾完就划掉。整个安装过程就是复制一个 exe。

PaperTodo 是干什么的:贴在桌面上的纸,不是笔记软件

它只管两件事:清单和短笔记。清单纸按行勾选,笔记纸写 Markdown。两张纸都没有父级容器,不挂在某个笔记本下面,也不建文件夹。

能力实测状态说明
待办勾选已验证整行加删除线,复选框变实心
笔记 Markdown部分验证标题、列表、引用渲染,粗体与表格保留源码
折叠成胶囊已验证570×630 缩到 576×69,贴 x=0
悬停预览卡未验证README 有,没有触发出来
插件系统未验证目录机制存在,没放插件进去
脚本胶囊未验证笔记首行写 !p 会执行脚本,有安全含义
多端同步不支持数据文件就在 exe 旁边

默认配置把它想成为的东西写得很清楚:纸片不进任务栏,不进 Alt+Tab,折叠功能默认打开。它想融进壁纸,不想变成又一个要切过去的应用。

data.json 里的默认项
  hidePapersFromTaskbar        true     不在任务栏占位
  hidePapersFromWindowSwitcher true     不进 Alt+Tab
  useCapsuleMode               true     支持折叠成胶囊
  useDeepCapsuleMode           true     胶囊贴边自动隐藏
  markdownRenderMode           enhanced 笔记按块渲染
  colorScheme                  warm     暖白纸色

发行版是单文件 exe,双击就跑,机器上不需要装 .NET 运行时。官方给两种打包方式,体积差得离谱。

同一版本的两个发行包
  自包含版(免运行时)   71,357,792 字节
  精简版(要 .NET 10)    2,432,123 字节
  比值                   71357792 / 2432123 = 29.3

装哪种取决于机器上有没有 .NET 10。没有就选自包含版,多出来的体积换的是「不用先装运行时」。

一张纸就是一个窗口

用窗口枚举看这个进程,两张纸各自是独立顶层窗口,类名是 HwndWrapper 加一串 GUID,窗口标题就是纸片标题。位置和尺寸直接落在 data.json 的 x、y、width、height 四个字段里,界面里拖动只是改这四个数。

EnumWindows 结果(物理像素,150% 缩放)
  发布清单    HwndWrapper  (1050, 450, 570, 630)
  便签的边界  HwndWrapper  (1635, 450, 600, 630)
  折叠胶囊    HwndWrapper  (3264, 222, 576, 69)

每张纸能单独置顶、单独挪到另一块屏、单独折叠,彼此不牵连。想同时看清单和资料,把两张并排摆着。

观测项结果怎么得到的
出现两张纸的耗时1.37 秒从进程启动到枚举到两个带标题窗口
常驻内存259,032 KB两张纸稳定后的工作集
首次单张纸281,476 KB冷启动只有一张默认待办时
进程数1 个没有额外的渲染子进程
五轮启动(同一台机器,冷热混跑)
  第 1 轮  1.23 秒
  第 2 轮  1.53 秒
  第 3 轮  1.37 秒
  第 4 轮  1.39 秒
  第 5 轮  1.43 秒
  枚举到的窗口  21 个,其中带内容的纸片 3 个,顶栏按钮 5 个
  顶栏按钮尺寸  384 × 46 三个,56 × 46 两个

内存这一项不算低。两张纸吃掉两百多兆,跟纸片数量关系不大,WPF 加 .NET 的固定开销就在这儿。

待办纸:勾一下,划一道

清单的交互很直白。点复选框,整行文字立刻加删除线,复选框变成实心;再点一下取消。行尾有一个六道杠的把手,按住可以拖动排序;底部固定一行加号,点一下插入新行。

一张 5 行的清单实测
  待办条数      5
  已完成        2(两行都出现删除线)
  未完成        3
  行尾把手      每行 1 个
  底部新增行    1 个

顶栏一共 5 个按钮:图钉、折叠横杠、加待办、加笔记、删除。默认纸片尺寸是 280 × 340,位置 (908, 175)。启动时默认给一张叫「待办1」的空纸,里面已经有一行空待办。这个细节省了一步:不用先找新建按钮,打开就能写。

操作实测表现来源
点复选框整行加删除线,复选框填充截图可见
点底部加号列表尾部插入一行输入框截图可见
行尾把手拖动把手存在,未实际拖动行为未验证
Ctrl+Z 撤销文档写支持 100 步README,未验证
多行粘贴拆分文档写会拆成多条README,未验证

勾掉的项留在原位,不清空,也没有完成动画。

笔记纸的 Markdown 只渲染了一半

笔记支持 Markdown,默认的 enhanced 模式是块级渲染、行内保留。标题会放大加粗,无序列表变成圆点,引用块左边出现一道竖线;粗体星号和表格竖线仍然以源码形态留在正文里。

写进去                     enhanced 模式下的显示
# 它不做什么                大号加粗,井号变灰
- 没有层级                  圆点 + 没有层级
> 一张纸只装一件事         左侧竖线 + 灰字
**data.json** 是唯一文件   星号原样保留
| 项 | 值 |                 竖线原样保留

这份样例的统计
  正文行数      5
  已渲染        3 项(标题、列表、引用)
  保留源码      2 项(粗体、表格)

工具栏上有个写着 MD 的按钮,按设计是切到完整预览。用 UIA 找到这个按钮并触发了一次,截图前后没有变化,正文仍是源码态。可能是触发方式不对,也可能要先把光标放进正文,这一条算未验证,不能据此说按钮失效。

随手记一段东西,enhanced 模式够用;要做带表格的排版,这张纸不合适。

Ctrl+W 之后,一张纸变成一颗胶囊

折叠是它跟普通便签拉开差距的地方。按 Ctrl+W 或者点顶栏那个横杠,纸片会收成一颗胶囊贴在屏幕边缘。胶囊里只剩标题和一个对勾,鼠标移开就缩进边缘,需要时再滑出来。

折叠前   570 × 630 像素   (1050, 450)   显示完整清单
折叠后    576 × 69 像素   (0, 147)      贴左边缘,只显示标题 + ✓
高度变化   630 -> 69     剩 10.9%

上面的数据来自同一个进程,折叠前后各枚举一遍窗口。宽度几乎没变,高度从 630 掉到 69,横坐标贴到了 0。桌面上原本占一块地方的东西,缩成了一条边。

开几张贴纸之后,屏幕边上排一列胶囊,比全收进托盘再一个个翻出来顺手。

数据就在 exe 旁边,遥测默认开着

没有数据库。所有内容写进 exe 同级的 data.json,纯文本,可以直接用编辑器打开改。默认文件 4,483 字节,63 个顶层键,papers 数组里每张纸 17 个字段。

data.json(默认)
  大小        4,483 字节
  顶层键      63
  papers      1 张
  单张纸字段  id / type / title / x / y / width / height / isVisible /
              alwaysOnTop / isCollapsed / textZoom / capsuleSide /
              capsuleMonitorDeviceName / deepCapsuleExpandedSide /
              deepCapsuleExpandedMonitorDeviceName / items / content
  字段数      17
  仓库源文件  152

退出时重写:5,497 字节 -> 5,681 字节(两张纸,含 Markdown 正文)

63 个键里,胶囊相关占了三分之一,这跟它的功能重心是一致的。

键的分组个数举例
胶囊与贴边21useCapsuleMode、deepCapsuleGapSize、capsuleTextSize
笔记与渲染11markdownRenderMode、imageReferenceTextMode、noteTextSize
窗口行为7hidePapersFromTaskbar、showTopBarNewTodoButton
待办6autoClearCompletedTodos、todoVisualSize
外观与快捷键18theme、uiFontPreset、globalHotkeys、maxTitleLength
合计6321 + 11 + 7 + 6 + 18 = 63

换机器只要拷这一个文件。很多便签把数据塞进 SQLite 或者某个私有格式,想迁走得先找导出按钮,它没这个问题。

另一份文件在用户目录里,叫 telemetry.json,默认 enabled 为 true。它记的东西比想象中细:

类别字段举例
设备install_id、locale(zh-CN)、country(China)、timezone_offset(480)、monitor_count(2)
使用launch_count、active_seconds、paper_count、todo_paper_count、note_paper_count
行为paper_created、todo_completed、pill_expand、markdown_preview、hotkey_triggered
崩溃crash_count、crash_exception_type、crash_module、crash_stack_hash

不想要就在设置里关掉,或者把 data.json 里的 telemetryEnabled 改成 false。字段清单里没有文本类字段,只有计数和崩溃类型,正文不会出去。

仓库带了两组自检程序,一组叫 PersistenceChecks,一组叫 ClipboardImageChecks,作者自己在跑落盘和剪贴板图片的回归。152 个源文件里,胶囊相关实现单独拆了十几个类,这块是他投入最多的地方。

什么情况下别用它

它不做层级,当不了知识库;不做账号,没有多端同步;不做提醒,替代不了日历;不做协作,别指望共享清单。

四类需求与它的能力对比
  三级结构(笔记本/分区/页面)   不支持,纸片只有一层
  手机端同步                    不支持,数据在这台机器的 exe 旁边
  到点提醒                      不支持,没有提醒功能
  全局搜索                      不支持,只有纸片标题
  • 要「笔记本 - 分区 - 页面」这套结构的,换 OneNote 或 Obsidian。
  • 要在手机上看同一份清单的,它的数据只在这台机器上。
  • 要响铃提醒的,它连这个功能都没有。
  • 习惯全文搜索的,它只有纸片标题能找。
测试范围
  系统      Windows 10 22H2(10.0.19045.6466)
  程序      PaperTodo v3.40
  文件版本  3.40.0.0
  屏幕      单屏 2560 × 1440 @150%
  README    badge 写 v4.0.0-beta1,release 页是 v3.40,以文件版本为准

只在这一种组合下测过,换系统或者换缩放比例,窗口尺寸那几条会跟着变。

  • 未验证:胶囊的悬停预览卡。文档说鼠标停在胶囊上会弹出可勾选的卡片,这次只看到折叠后的静态胶囊。
  • 未验证:插件系统。文档说把插件文件夹丢进 plugins 目录就能变出时钟或番茄钟,没有实际放进去试。
  • 未验证:脚本胶囊。笔记首行写 !p 或 !power 会变成点一下执行 PowerShell 的胶囊,这条有安全含义,没有实跑。
  • 未验证:MD 按钮的完整预览态,以及粗体和表格的最终渲染效果。
  • 未验证:托盘双击召回全部纸片、多显示器之间拖拽胶囊。

这个 exe 没有数字签名,Authenticode 状态是 NotSigned,第一次运行时 SmartScreen 会拦一下,需要点「更多信息」再放行。