未来日记 Future Journal
每日三分钟的书写工具——生成一本可离线填写的电子手账:先描(或照着打)一句引导句,再用过去时写下今天希望发生的事,最后挑一个心情词。49 天一轮、每周一个主题,含 49 句原创引导句库、质量闸门与页面回归测试。默认全离线、不发任何请求;可选的跨设备同步只在使用者自备后端配置后才会联网,日记正文在本地加密完才上传,所用同步组件按钉死版本 + SRI 完整性校验…
Bonnie Geng
@bonniegeng-max
Install
$ openclaw skills install @bonniegeng-max/future-journal未来日记 · Future Journal
这是什么
一个单页、离线、手机优先的电子手账。每天三分钟,三步:
- 描摹一句浅灰引导句
- 用过去时写下今天希望发生的一件事
- 句末挑一个心情词
49 天为一轮,每周换一个主题(小确幸 → 身体节奏 → 关系 → 作品 → 金钱 → 空间 → 未来已来的我)。
方法与节奏参考山田弘美、滨田真由美所著的日记实践书。本工具不是该书的官方产品, 49 句引导句全部为原创,书中正文内容未被使用。
目录
| 文件 | 作用 |
|---|---|
assets/index.html | 完整的手账页面。单文件、双击即可打开;不配同步时全程离线、不发任何请求 |
references/DESIGN.md | 暖纸手账设计系统 + 无障碍基线。改页面前先读这个 |
scripts/today.js | 算今天是第几天、本周主题、今天要描哪一句 |
scripts/check-prompts.js | 引导句质量闸门。改句库后必跑 |
以上 5 个文件(含 SKILL.md 自身)是 skill 的完整能力面,发放给别人的就是这些。
维护者工具(只在本仓库,不进 ClawHub 发布包)
| 文件 | 作用 |
|---|---|
scripts/test-journal.js | 页面回归测试(数据不丢 / 键盘可达 / aria / 降级路径) |
它依赖 jsdom(开发期依赖),属于「改这个仓库的人用的」,不是「装这个 skill 的人用的」。
不要把它打进 ClawHub 发布包 —— 维护者工具混进发布包被判过
suspicious / DO_NOT_INSTALL(见 MAINTAINING.md 里记的那次教训)。
改页面前后必跑的闸门
改句库:
node scripts/check-prompts.js
改页面逻辑或样式(仓库内,需要 jsdom):
node scripts/check-prompts.js
NODE_PATH=~/.workbuddy/binaries/node/workspace/node_modules node scripts/test-journal.js
两个都要退出码 0。
test-journal.js 覆盖三组:A 数据不丢(打字自动落盘 / 刷新还原 / 防抖窗口内切 tab)、
B 无障碍与键盘可达、C 拿不到 2D 上下文时的降级。
一个顺手的巧合:jsdom 不实现 canvas 2D,
getContext('2d')恒返回null, 所以 C 组那条降级分支在 jsdom 里是被真实执行的,不是 mock 出来的。
交付页面给用户
把这个文件复制到用户指定的位置就行,它没有任何依赖:
cp assets/index.html ~/Desktop/未来日记.html
- 双击用浏览器打开即可使用(
file://协议下完整可用) - 手机端:把文件传到手机后用 Safari 打开
- 数据存在浏览器 localStorage,键名
future-journal-v1
不要为它起本地服务器。 页面不依赖服务器就能完整使用(唯一的例外是可选同步), 起服务器只会带来额外故障点。
例外:想用「跨设备同步」就必须走线上网址 —— 见下面的「同步」一节。
回答「今天写哪一句」
node scripts/today.js 2026-09-17 # 以该日期为第 1 天,算到今天
node scripts/today.js 2026-09-17 --day 8 # 直接指定第几天
输出第几天、本周主题、今天要描的句子。用户想写在实体本子上时用这个。
改引导句库
49 句内嵌在 assets/index.html 的 <script id="prompt-data"> JSON 块里。
编辑后必须跑校验,退出码 0 才算通过:
node scripts/check-prompts.js
检查项:7 周 × 7 句的结构、重复句、将来时标志词、句末情绪词、单句长度 ≤ 30 字。
新句子的公式
时间锚点 + 一件具体的好事 + 完成态 + 句末情绪词
- 对:
今天的活一件件推进了,我很安心。 - 错:
我希望今天工作顺利。← 将来时,方法明确要求写过去时
中文没有时态标记,肉眼判断「是不是过去时」极不可靠,所以校验脚本用 禁止将来时标志词这条硬规则兜底。不要跳过。
数据在哪(重要,别承诺错)
默认情况下,页面数据只存在用户浏览器的 localStorage 里,skill 读不到。因此:
- 用户问「我写了什么」→ 让他在页面的「回顾」页看,或从「设置」导出 JSON
- 用户要备份 / 换设备(不登录)→ 页面「设置」里有导出与导入按钮
- 清缓存 / 换浏览器,本机数据就没了 —— localStorage 的固有特性,务必如实告知
- 「进度」「3 个想要的」都在同一个 state 里,导出 / 导入是整包,不拆分
跨设备同步是可选功能,且必须先发布到线上网址,见下一节。
同步(可选,需先发布到线上)
页面内置了「账号登录 + 端到端加密」的跨设备同步,但有三条硬约束:
-
只在线上网址可用。 判断依据是
location.protocol:file:开头时页面会直接提示 「联网同步要在发布后的网址上用」,且不加载 SDK。双击本地文件打开 = 纯本地模式, 这和以前完全一样,不要因此认为页面坏了。 -
需要先激活云服务并建表。 表是
journal_sync,一人一行,只存密文:id BIGINT identity PK / owner_id TEXT DEFAULT auth.uid() UNIQUE cipher TEXT NOT NULL / updated_at TIMESTAMPTZowner_id由数据库默认值填充(TEXT,不是 uuid),客户端永远不要发这个字段。 RLS 要同时给 GRANT 和 POLICY 两道,缺一不可。 -
加密在浏览器里做,服务器只有密文。 PBKDF2 20 万次派生 AES-GCM-256 密钥, 密钥存本机 localStorage(
fj-dk),不上传。所以同步密码丢了,云端那份就解不开, 本机记录不受影响 —— 这句话必须原样告诉用户,不能含糊。
其他约定:
- 首次登录后要用户自己设「同步密码」,不自动生成 —— 他要能在别的设备上手输同一个
- 推送是写完后延迟合并触发(
schedulePush),不是每次按键 - 冲突用
mergeStates合并:按天取at较新的一条,「3 个想要的」按文本取并集 - 云端写入走「先查后定 insert/update」,不用
upsert—— 因为owner_id是数据库默认值, 客户端不知道值,onConflict无从下手
配置串(endpoint + publishableKey)存在本机 fj-cfg,用户换设备时粘贴一次即可。
⚠️ 不要把配置串写死进页面
看起来最顺手的「优化」是把 endpoint / publishableKey 直接内嵌进 assets/index.html,
这样用户就不用粘贴了。绝对不要这么做。
这个页面是要分发给别人的 skill 资产。一旦写死,任何装了这个 skill 的人打开页面都会连上 同一个后端——虽然 RLS 按用户隔离、彼此看不到内容,但陌生人会在号主的环境里注册账号、 消耗号主的配额。作者自己用着舒服的代价,是所有人的数据都涌向同一个人的账上。
配置串必须由使用者自己提供(fj-cfg 或设置页粘贴)。号主自己的那份也走同一条路——
把配置串放在 skill 目录外面的文件里,这样它永远不会随 skill 分发出去。
这个 skill 会碰网络吗(如实清单)
发布审核最容易被卡的点不是「有联网」,而是声称与实际不符。所以这里列全, 一个不漏 —— 如实罗列比写得含糊安全。
| 行为 | 何时发生 | 去向 | 传了什么 |
|---|---|---|---|
| 下载云同步 SDK | 仅当使用者自己粘贴了配置串、且页面从 http(s):// 打开 | cdn.jsdelivr.net 上的 @tencent-ai/workbuddy-cloud-sdk(版本号钉死 + SRI 完整性校验) | 只下载代码,不上传 |
| 发验证码 / 维持登录态 | 使用者主动点登录 | 使用者自己配置的后端 | 邮箱、会话令牌(会离开设备) |
| 上传日记 | 使用者设了同步密码之后写入内容 | 使用者自己配置的后端 | 只有密文(在本机加密完才发) |
这个表以外,没有别的:
- 无统计、无埋点、无第三方字体 / 图标 CDN
- 无硬编码的服务器地址或密钥:
endpoint/publishableKey一律由使用者自备 - 不配同步时:页面从
file://打开,一个网络请求都不发(SDK 根本不加载) scripts/today.js、scripts/check-prompts.js、references/DESIGN.md全程纯本地
远端 SDK 必须钉版本 + 校验完整性
assets/index.html 里的 SDK_URL / SDK_SRI 是一对必须同进同退的常量:
var SDK_URL = '…/@tencent-ai/workbuddy-cloud-sdk@<具体版本>/lib/index.global.js';
var SDK_SRI = 'sha384-<该版本文件的 sha384>';
为什么这条是硬约束:这段远端代码和页面同源执行,能读到 localStorage 里的日记正文、
fj-cfg 里的后端地址与 key、以及导出的加密密钥 fj-dk。所以「加载哪一份」不能由 CDN
当下决定。用 @dev 这类漂移标签,等于审核时的那份代码和用户实际执行的可以是两份。
- 改版本 → 必须用
hashlib.sha384对新文件重算哈希并写回SDK_SRI - 忘了改 → 同步会静默失效(
onerror会给出提示,不会留半截界面) scripts/test-journal.js的 D 组会卡住这个约定;@dev/@latest一出现就失败<script>上的三个属性缺一不可:integrity(不放行改动过的字节)、crossorigin="anonymous"(SRI 需要 CORS 取文件)、referrerpolicy="no-referrer"- 用
setAttribute写而不是s.integrity = …:后者的 IDL 反射不是所有引擎都有
那句话「加载远程脚本」确实是扫描器会多看两眼的模式——所以页面把它做成 必须先有人粘贴配置串才会发生,不是打开就跑。这是刻意的,改的时候别把它提前。
登录用的「账号」到底是什么(别答错)
不是 WorkBuddy 账号,也不是微信 / Google / Apple 之类 —— 是使用者自己那个云服务环境里的 终端用户账号,用邮箱当唯一标识,登录靠邮箱收到的 6 位验证码(也可以另外设密码)。
要说准的四点:
- 邮箱只存在使用者自己配的那个后端里。分发 skill 的人拿不到任何东西。
- 平台目前不提供匿名登录,所以只要想跨设备,邮箱就是唯一的身份选项,没有更轻的替代。
- 让人填邮箱确实是审核会留意的一类信号(收集个人信息)。三条缓解都已到位: 完全可选(不登录 = 纯本地,功能一个不缺)、不是作者的服务器、日记内容端到端加密。
- 反过来:配置串被别人拿到,他的邮箱就会进那个人的后端 —— 这是「配置串必须使用者自备」 的又一个理由。
页面的几个设计决定(改动前先理解)
- 回看是延迟解锁的:满 7 天才打开上一周。这是刻意设计,不是 bug —— 方法本身建议不要天天回头翻。不要把解锁提前。
- 「过一遍」有两种方式,不要拿掉任何一种:有触控笔走「用笔描」(canvas,只记录笔迹、
不做对错判定,描歪也算过);鼠标或触控板走「照着打一遍」(照着灰字逐字输入,打对变深、
打错标红但不拦)。两种都记
traced: true,traceMode存方式。 触控板描摹是手眼分离,实测无法控制 —— 打字模式不是可选项,是必需项。 - 笔迹按归一化坐标存储(0–1 区间),所以换设备、换屏幕尺寸后不会错位。
- 输入框字号锁 16px:低于此值会触发 iOS Safari 自动缩放。不要改。
- 写作框必须自动落盘,不许退回「点按钮才存」。 曾经只有「收好了」按钮会写
S.entries[day].text,而renderToday()又会用已存的值覆盖输入框 —— 于是「打了一半切个 tab」或「直接刷新」就把正在写的内容丢了。现在有 5 个落盘点:输入防抖 500ms、 切 tab、pagehide、beforeunload、页面隐藏。切 tab 前必须先flushWrite()再渲染。 描摹和心情词本来即时存,写作框过去是唯一的例外,别再制造这个例外。 - 同步卡片的 4 个状态都在
renderSyncCard()一处渲染:未填配置串 / 已填未登录 / 已登录待设同步密码 / 已连接。改同步相关文案只改这一个函数,别散到各处。 - 可读性优先于纸感。 正文一律 ≥ 4.5:1、UI ≥ 3:1。唯一豁免是描摹引导字
(
--trace,#ABA492,2.20:1)—— 它的身份是"盖着描的底稿"不是正文,真按 4.5:1 加深, 描上去的笔迹就和底稿分不清了。等价路径已备齐:打字模式用弱墨(4.55:1)、打印版加深到 6.71:1。 别在别处复用--color-trace。 - 状态不能只靠颜色。 已写 / 今天 / 未来的差异落到
disabled+aria-label上; 心情词的选中态落到aria-pressed;当前 tab 落到aria-current。 读屏读不出"底色深浅",只写 CSS 等于没做。 - 能力探测要在动 DOM 之前做。 典型例子:
canvas.getContext('2d')会返回null(浏览器禁用画布、或 iOS Safari 画布上下文超限)。原来openTrace()直接.font = …就抛TypeError,表现为点「用笔描」毫无反应。现在先探、拿不到就原地退回打字模式 并给一句人话。"点了没反应"比报错还差。 - 焦点环必须是一层额外描边,不能只靠改边框色(触屏上看不见)。
全站
:focus-visible用 2px 赤陶 + 2px offset;赤陶底上的按钮换成主墨描边。 不要写outline: none。
不做什么
- 不默认开同步。 不登录 = 纯本地,这条不能被改动破坏。同步必须由用户主动粘贴配置串开启
- 不做推送提醒(页面关闭即止,做不到)。想每天被提醒,用 WorkBuddy 的定时任务推当天引导句
- 不做暗色模式(纸质手账没有暗色版)
- 不引入任何第三方图标库、字体 CDN、统计脚本 —— 离线可用是硬要求 (云同步 SDK 是唯一例外,且只在线上模式按需加载,版本钉死 + SRI 校验)
语言
本 skill 刻意面向中文使用者:界面文案、引导句、设计文档全部中文。这不是疏漏 ——
这套方法的语感(「过去时」在中文里靠词汇而非词形、「心情词」的调子)建立在中文上,
做双语反而两头都不到位。英文摘要放在 skill-card.md,供英文语境的人判断要不要装。
版本历史
- 1.0.1 — 修掉 ClawHub 安全扫描的
suspicious:云同步 SDK 从漂移的@dev标签改为 钉死具体版本 + SRI 完整性校验(扫描报告SDI-2);补齐「开启同步后哪些数据会离开设备」的 前端告知与文档说明(SDI-1);声明中文优先是有意为之(SQP-3)。回归测试新增 D 组,44 条全过。 - 1.0.0 — 首次发布。
Top skills in this category
Weather
@steipeteGet current weather and forecasts (no API key required).
Shop
@shopifyUltimate personal shopping assistant: find, compare, buy, gift, and reorder products across the Shop catalog containing millions of stores. Tracks orders and...
healthcheck
@stellarhold170ntTrack water and sleep with JSON file storage
ClawQuest: Agent Mine - OpenClaw Managed Mining
@zhzai30The managed automated mining server interface supports OpenClaw session mode and incremental event retrieval, enabling mining startup, status query, settlement, and stamina management.
FlyAI — Travel, Flight & Hotel Search and Booking
@yealexchenSearch flights, hotels, attractions, concerts, and travel deals with natural language. FlyAI connects to Fliggy MCP for real-time search and booking across h...