做了个读长文用的光带,顺带发现 bookmarklet 不受页面 CSP 管,但 loader 受

先说毛病:我读长文章,读到第三行就不知道自己在读哪一行了。

不是走神——注意力还在文章上,是"我刚才读到哪"这件事本身...,得回去重新扫一遍找那句。一篇长文读下来,大概三分之一的时间花在找行上。

所以写了一个: https://spotlightreading.com

贴个链接或者粘段文字进去就能读,也能直接开 txt / pdf / epub 。有条光带压在正在读的几行上,可以拖,可以点一句读一句,也可以按 F 让它跟着鼠标自动贴合整句或整段。

三个跟直觉相反的地方

  1. 把其它行遮住是错的。

第一版我把光带以外的内容整个遮掉了,结果更难受——你会失去"自己在这一页什么位置"的空间感。现在的做法是光带以外的文字不遮,只让对比度沿梯度连续衰减。轮廓还在,但不抢注意力。

  1. 虚化比变暗更糟。

早期版本是把周围文字 blur 掉,用起来明显更累。因为人看到模糊的东西会下意识地努力辨认,这个本能关不掉。改成单纯降对比之后,那些字读起来像"现在不轮到你",而不是"这里坏了"。

  1. 真正起作用的大头不是光带,是排版。

把文字重排成窄栏(中文一行 23 字左右、英文 65 字符左右)、行距拉到 2 倍上下,这一项的收益比光带还大。丢行主要发生在回扫那一瞬间——眼睛读完一行跳回下一行行首,行越长,落点误差越大。现在大部分网页一行四五十字,那是为了塞广告排的,不是为了给人读的。

几个实现上的坑

bookmarklet 不受页面 CSP 约束,但 loader 受。

书签工具我一开始按常规写法,在 bookmarklet 里 append 一个 <script src="...">。本地测得好好的,一到 Wikipedia 就死——Wikipedia 有 script-src CSP ,外部脚本直接被拦。而 Wikipedia 恰恰是最需要阅读辅助的地方。

解法是把整个压缩后的脚本内联进 javascript: URL 里,不走资源加载。因为 bookmarklet 是作为用户手势执行的,不算页面发起的资源请求,所以不受该页 CSP 管辖。6KB 塞在 href 里毫无压力。Wikipedia / MDN / BBC 实测都能跑。

CSS 自定义属性不 @property 注册,就没有过渡动画。

变暗的区域是一整个 gradient ,色标位置是 var(--t) 和 var(--b)。给它加 transition 完全没反应——自定义属性默认是无类型的,浏览器不知道怎么插值,只能在两个值之间硬跳,光带就是瞬移。用 @property { syntax: '<length>' } 注册之后才开始滑动。

贴链接那条走 Worker 代抓。

浏览器有 CORS 进不去别人的站,所以 /api/read 由 Cloudflare Worker 代抓,用 HTMLRewriter 流式提取正文,扔掉导航、广告、侧栏、目录、语言列表。带 SSRF 防护(拒 localhost 、私有网段、非 http 协议)。

PDF 的断行要重新缝合。

pdf.js 拿到的文本是按排版切的,一句话经常被断成好几段。得按行高和右边界判断哪些是硬换行、哪些是排版导致的软换行,再缝回去,不然光带没法按句子贴合。

隐私

纯客户端。文件全部在浏览器里解析,不上传。没有账号,没有统计。只按文本指纹记住你上次读到哪——文本本身不存,只存位置。

做不到的

Canvas 渲染的阅读器( Kindle 网页版这类把文字画成图的)只能整页染色,没法吸附句子。原生 App (微信读书、多看、Apple Books )完全没有入口——这不是我偷懒,是不存在通道。

想听听:

你们读长文档丢行吗,还是只有我这样 手机上光带的拖动手感怎么样 还有哪些站的 CSP 会把书签工具挡住的,我想收集一下

(是我自己做的。)