| 项目 | 内容 |
|---|---|
| 本周目标 | 反思关键概念,把信心记在进度旁边 |
| 本周简介 | 收束七个关键概念,并把信心与进度并排记录——因为不敢开口的学生还没做完这个单元 |
画布的 Value Propositions 那一格写着:confidence is measured alongside progress,因为一个不愿意开口尝试的学生,无论答对答错,都还没有做完它。
这句话是本单元真正的判据。过去两周我们一直在推概念和英语;如果这周只收概念不收信心,那么这个单元最后会塌回”孩子能定义 resilience,但不会用它解释身边真实的决定”——也就是画布一开始就说没做完的那种状态。
1. 关键概念收束 七个关键概念逐一落到这个单元上,每个都要能反向回指:它是靠哪条探究线索建立起来的?靠哪段泛读文本?靠哪个易混概念的拆解?
2. 反思环节(Reflection) 不是复述知识点,而是让学生说出一个他原本以为、现在改了的判断。这是 PYP 反思的实质,也是唯一能暴露”概念真的动了”还是”概念只是被讲过”的证据。
3. 信心与进度并排 这两列必须同时存在、同时更新。缺了信心那列,进度就是假的;缺了进度那列,信心就是自我感觉。
给 W44 的交接:W44 要把这个单元打包成可售卖的学习包,爸妈看到的就是这两列的并排结果。如果这两列读起来像一份成绩单而不是一个学习者,这周的活就白干了。
前两份说明讲的是这个单元是什么、答案怎么自己找出来。今天这份讲你手上这台机器怎么用——它不是一个播放器,是一套帮你把书读进去的工具。
你的单元是一圈一圈转的:预读扫描 → 泛读 → 找一找 → 理一理 → 反思关键概念。App 不会替你转这个圈,但它在每一圈上留了一处机关。认全这五处,你就知道什么时候用哪个。
| 圈 | 你要做的动作 | App 在这一步给你什么 | 这一圈留下什么 |
|---|---|---|---|
| 预读扫描 | 还没开始读,先听个开头 | 封面右上角那个 BenBen 小头像,点一下就有中文开场 | 知道这篇大概讲什么 |
| 泛读 | 别管难词,先把整篇过一遍 | 设置里的自动播放 + 自动翻页 | 耳朵先过一遍 |
| 泛读 | 读不懂时,先用中文弄懂 | 左上角 EN/CN 圆标:点一下换中文讲解,长按进中文讲故事 | 中文里站稳的理解 |
| 找一找 | 不懂的词、句子,当场问清楚 | 选中文字 → 图中间浮出的 BenBen 小圆标(意思 / 其他意思 / 语法) | 意思 + 语法 |
| 找一找 | 查过的词,收进自己的本子 | 解释卡片上的 Add word to notebook | 你标过的生词表 |
| 找一找 | 整页有疑问,直接问 BenBen | 右下角的问号圆标 | 答案落回这一页 |
| 理一理 | 把两件很像的事分开,找那条分界线 | 这一步没有按钮。App 只负责把原文和页码留在那儿 | 一句带「还是」的问句,和它指的那一页 |
| 反思 | 说出「我本来以为……后来……」 | 这一步也没有按钮:你说,Benben 记 | 一句你原话 |
| 反思 | 顺便说出你有多确定 | 跟进度并排记在一起,缺一个另一个就不算数 | 信心 |
先把自动播放和自动翻页都打开。它会一页一页念下去,你不用动手,眼睛跟着看就行。这是磨耳朵——一天十分钟,耳朵会先记住这些句子的声音,比你先背下中文意思更管用。
左上角那个 EN/CN 小圆标,点一下就换成中文讲解。长按大概半秒,会进入「中文讲故事」模式:整个故事用中文从头讲一遍给你听。这比逐句翻译有用得多,因为它把整篇连起来了。
这里有一件事要讲清楚:中文不是英文的替代品。中文是你思考的地方,英文是你交付的地方。先用中文把这篇讲的内容弄懂,再回头用英文读同一段——顺序反了,英文就只是背下来的句子,不是你的。
看到一个词或一句话不懂,按住选中它,图的中间会浮出一个小圆标(上面是 BenBen 的头像)。点它,下面弹出一张解释卡片:
第三项是别处很少给的:知道意思,和知道它为什么在这句话里长成那样,是两件事。语法跟进补的就是后面这一件。
解释卡片下面有一个 Add word to notebook——点一下,这个词就进了你自己的生词本。你的生词本是一张可勾选的清单:学过了就打勾,没学完的先留着。
为什么值得你自己动手标:难词不标出来,就永远只是「刚才那个词我不认识」,过三天你连它长什么样都忘了。标下来,它才变成一个你能一项一项攻掉的东西。标的时候顺手再问一句「它在这一页是什么意思」——这比抄十遍都管用。
右下角有个问号圆标。点它,说出你的问题——它在听(红点),在想(转圈),然后回答落回你正在读的这一页。
这一条最重要,因为它对应探究里最贵的那一步:问题在原地问,答案在原地答。合上书去问,等于把问题本身丢掉了。
读完以后,你只要能说出「我在第几页看到这句话」,这一篇就算读懂了。说不出来,就再回去翻一次——那不是你笨,是还没翻到那一页。
这份说明写给刚拿到新单元的爸妈。你要知道的只有三件事:内容怎么来、怎么收钱、怎么把阅读变成习惯,另外还有一件五分钟能做完的事——把它装到孩子的主屏幕上。
学校给了一份新单元的 PDF,或者你想让孩子读某个主题(火山、木马、法老的猫)——不用学任何后台,在微信群里告诉 Benben 就行。两条路:
新内容会自动出现在孩子所在单元的书架里——哪些孩子能看到,由单元的级别和孩子的 level 决定,不需要你做任何设置。你只负责提需求;写、画、录、发,都是 Benben 的活。
现在是内部试用,免费到 2026 年底。之后的收费方式我们还没定死,方向是两条:按单元买学习包(一个单元一份,含有内容、导读和练习),或者按学期订阅(家里几个孩子都能用)。学校和班级统一授权在更后面。
所以如果你在考虑这件事,现在是最划算的时候——先把一个完整单元跑完,看看这套东西对你家孩子到底有没有用,再决定要不要买后面的。
大多数阅读习惯毁在两件事上:一是要求太高,第二是每次都要大人陪着。你手上的这个 App 恰好能避开这两件——它把「教」那一段吃掉了,留给爸妈的只有「听」。
具体这么用:
还有三件不要做的事:不要检查她记住了多少;不要在她读的时候教(那是 BenBen 的活);不要把阅读变成要签字的任务。
最后说一个我们内部用来验收的判据:爸妈看完之后的第一反应,应该是「这个替我省事了」,而不是「这个我可以自己教」。如果你的反应是后者,说明你正在把自己变成第二个老师——那不是这个产品想要的。
「全屏阅读」不用在 App 里找按钮——把它装成 App 就自带全屏。做法是五分钟的一次性设置:
再补两件事:
微信群里说一句「谁 + 哪一篇 + 什么设备」,Benben 会上门修。不用截图技巧,随手一拍就行。
前两页讲怎么读和怎么存。第三页讲更前面的那个问题:内容从哪来。
孩子的新单元是 IB PYP Unit 2「How We Organize Ourselves」:岛屿生存、资源管理、社区怎么组织起来。适用 G3,8–9 岁。
官方绘本系列 1 条、生存技能 6 条、真实故事 3 条、文学与寓言 3 条、兴趣拓展 1 条。三条线索各有归属:「挑战与变化」归技能类,「角色与责任」归真实故事,「合作与创造力」归文学与寓言。
孩子打开 reading.im,看到的第一屏不是一堆按钮,是一块黑板。
黑板背后是 10 月 16 日到 20 日的五个工作日。团队没有先做界面,是先把"孩子读得到的东西"一样一样备齐:两个单元、29 篇故事,每一篇都配齐封面、逐页插画、英文朗读、中文讲述和一张能勾的生词表。这五天铺的,就是从"点开图标"到"自己读完一篇"之间的那段路。
| 这一段 | 现在到哪一步 | 孩子和爸妈能拿到什么 |
|---|---|---|
| 内容的地基 | 已完成 | 内容有统一格式,往后再加一个新单元不用从头重做一遍 |
| 阅读器 | 已完成 | 点开就是能读的一页一页,不是文件列表 |
| 账号 | 已完成 | 一次登录,reading.im 和 benben.im 都认 |
| 装到平板、断网读 | 测试中 | 装到主屏幕像 App 一样全屏;出门前下好,飞机上也能读 |
| 装满内容 | 进行中 | 两个单元、29 篇,全部配齐插画、英文朗读和中文讲述 |
| 每篇先读一遍 | 进行中 | BenBen 自己先读一遍,问题在那一页标出来 |
下面两条讲的都是已经能用的东西,不是计划。现在还是内部试用,免费到 2026 年底——想先带一个孩子把一个单元从头走完,是最划算的时候。
这一段做的是"从点开图标到读完一篇"之间的每一段路。站在孩子的角度是三步:打开、读、带走;站在爸妈的角度,这三步都不用大人插手——最多最后听他说两句。
点开一个单元,摆在眼前的就是全部:故事、杂志、绘本、老师给的参考 PDF,每张卡上有封面和页数——不用问"我该点哪个"。页面上出现的东西也只有孩子该看的那些:BenBen 的头像、词典、提问的入口。
账号这一层也做省了:reading.im 和 benben.im 是同一套登录,一次登录两边都认,孩子不用多记一个账号。
读的时候卡住,也不用喊大人。选中一个词,意思、它还有哪些意思、它在这句话里的语法,一起给出来;整页有疑问,右下角问一句,答案落回这一页,孩子不用把书合上跑去问人。
这周最实的一次返工就发生在这台 iPad 上:打开很慢。原因是取内容绕了远路——每一张插画、每一段录音都要跑一趟别的地方去拿,孩子点了图标要干等。做法是把整套内容搬到离孩子更近的地方,慢的问题就没了。
搬完之后,一批"像个产品"的事也一起补上了:出门前把一周的故事下好,飞机上、高铁上、回老家都能读;装到主屏幕,打开就是全屏,没有地址栏,也没有浏览器按钮;下过的东西会向系统申请保留,不会被悄悄清掉;断网时顶上只挂一条安静的横幅,不弹窗吓人;整节、整单元都能一次下完。
进度这件事也定了:一个孩子一个进度,记在他自己那台设备上,家里两个孩子各记各的,不会混在一起。
最后两件事:把内容装满,然后每一篇都审过一遍。
每一篇都带齐五样:封面、逐页插画、英文朗读、中文讲述、还有一张能勾掉的生词表。插画是这一篇自己的,不是从别处套来的;朗读和中文讲述也是一篇一篇配的——有些是真实故事,有些是寓言,讲法本来就该不一样。
来源这一关也没松。选材只从少数几个站里挑,每一篇都要打开看一遍、量一量正文有多长、确认整篇真的能读完才收;收不下来的记在账本上——那本账本比"做了多少篇"更能说明这套做法靠不靠谱。
内容不是一次做完的,所以要有第二条路:做完了,先审一遍再给孩子。BenBen 有一把自己的审阅页面,别人看不见。左边英文、右边中文,一页一页翻:哪一页的录音不对、哪一页的朗读念错字、哪一页的图画偏题,就在那一页打一个标。英文那边写一句,中文讲述那边写一句,看完这一页直接翻到下一页。
这一版还在原型阶段:保存按钮只在本地的开发环境里真的存得住,部署出去的版本会明说"这个部署不保存"。写在明面上,用的人才不会白按一次保存。
10-20 那天还顺手修了:封面上多了一枚生词表角标,点开是整篇的词表——词、释义、它第一次出现的位置;右上角还多了一个批注入口,只有 BenBen 账号看得到。
今天的活不新,也不炫:把 reading.im「每个孩子一页」这个项目的进度,从头到尾对了一遍代码。
先说最扎眼的一条。项目的验证说明里写着"两条进度测试全绿",我实跑了一遍:15 过、2 跳、2 红,红的两条正是它。而且上一次留档的跑批(10-18)也是红的——所以那句"绿",至少错了两天。
但红的不是功能,是断言。我把应用跑起来自己看:读了一页,进度确实记在这个孩子名下(reading:progress:<kid> 里躺着 page 5),单元页那道进度条也画出来了。断的是测试在找已经不在的老东西——10-20 那天,卡片上的页码文字换成了进度条,故事页也改成了"先问再续",测试还在找那几个字。
所以该修的是断言,不是把功能改回去。这一条比"全绿"本身更值得写下来:红灯要分清是产品坏了,还是尺子旧了。分不清的时候,为了让报表好看,是很容易有人去改一个本来正确的功能的。
这个项目在 10-20 改了主意。原来是"按等级筛单元",现在改成"每个孩子自己一页,也可以借别人的那一页"。这一版,代码里一行都还没有:
config/kids.json)里只有等级,没有每个孩子自己的单元清单;剩下的活按先后排在这里:
| 优先 | 做什么 | 落在哪 |
|---|---|---|
| P0 | 给每个孩子的名单加显式单元清单(自己的 / 借来的),等级退回做兜底 | reading.im 的 config/kids.json |
| P0 | build-index 按名单生成每个人的那一页,没名单才按等级筛 | scripts/build-index.mts |
| P0 | 关掉全局兜底:孩子那一页取不到就报错,不掉回全部单元 | src/routes/index.tsx |
| P0 | 重写两条进度测试的断言:认进度条,认"先问再续" | testing/e2e/reading-im/progress.spec.ts |
| P1 | 网格按名单顺序排,标出"自己的"和"借来的",第三个单元也有位置 | src/lib/home-units.ts 与黑板 |
| P1 | 上传脚本不再写死孩子名单,加一个孩子不用改代码 | walkingweekend 的上传脚本 |
| P1 | 建号时把孩子的编号和进度密钥一起写上,邮箱前缀只当开发时的方便 | 账号管理 |
| P1 | 进度文件在孩子这边加密,密钥只跟着账号走,服务器存的是密文 | reading.im 的进度存储 |
| P1 | benben.im 上开一个进度接口,读写都走它,路径由登录态决定 | benben.im 的 API |
| P1 | 本地进度当快路径、文件当准;按条合并,读完的不会被改回没读完 | reading.im 的同步层 |
| P1 | 镜像脚本跳过进度目录,别把每个孩子的密文拷进发布包 | pull-mirror |
| P2 | 人工验收:一台 iPad 两个账号、同一账号两台浏览器、第三个孩子从零开通 | 真机 |
这一串里最要紧的一条,是这个项目真正要交出去的东西:一个孩子的进度,得跟着这个孩子走。今天离它还差一整层,但现在至少知道差在哪、差多少了——知道自己欠在哪里,本身就是今天最值得记下来的产出。
孩子打开一本故事,正文里有些词被标成了“生词”——就是难一点、值得查一查的那些。它们不是死的黑字,是可以点开的:点一下,右边就浮出这个词是什么意思。这套动作今天从头调了一遍,因为有几天它别扭得让人不想用。
第一件事是看得见。生词按钮上的字淡得像没写,孩子得眯着眼才找得到它在哪儿。一个看不清的按钮,孩子当然不会去点——他只会觉得“这页没什么好玩的”。
第二件事是认得出。选中的词、右边浮出来的意思、正文里被划亮的那一句,以前是三种颜色。现在统一成同一种暖黄:眼睛扫过去就知道“我选的就是它”,不用再在页面里找“到底是哪个黄”。
第三件事是读得下去。意思那一栏以前的字号和行距都跟正文不一样,读起来像从别的文章里搬过来的一段。现在它和正文用同样的大小和行距:字大一点、行距松一点,孩子从正文跳到词义,眼睛不用重新适应。
还有两个小毛病一起收了:长按一个词想删掉它,删完它还站在原地——现在真的会消失;宽屏看书的时候,图片按自己有多高摆,不再被裁掉半张。
光标也改了。以前指着图片是一个放大镜,像是在说“点我会放大”;现在是一只手,说的是“点我”。图片上的提示箭头也分了两边:电脑上是往外的,手机上是往里的——因为两个屏幕上,点一下图本来就不是同一件事。
说到底就一件事:眼睛要调的从来不是一处。底色和字色得一起动,只动一边,字还是看不清。
reading.im 里可以对着正在读的这一页问一句,问完有答案。今天修的其实是好几扇门——问题都在门口,不在屋里。
第一扇门:线上有一阵子一直报一句话,the answer failed to load(答案没能加载出来)。看着像 AI 答不上来,其实话根本没进门:浏览器要往另一个地方递话之前,会先打个招呼问一句“我能过去吗”,那边的门没开这个招呼,话就被挡在门外,回来一句“没加载出来”。今天这扇门开了,同一句提问,答案正常回来。
第二扇门更隐蔽。家里那台会读书的小机器人,它的麦克风好久没被叫到了:它那条路上被套了一层“要登录”的壳,一开口问就被拦下来。而程序很聪明——见机器人答不上来,就悄悄改用电脑的麦克风接着用。所以没有人发现机器人哑了,因为它看起来一直在工作。今天把它自己的约定还了回去:这条路本来就不该要登录。真机实测,机器人的声音被采到了,也被转成了字。
第三件事是给这些门上规矩。今天补了一道测试:reading.im 问话要用到的每一个接口,都必须能接住浏览器那声招呼;而且这份清单要和前端实际在用的那份对得上——这样第三个接口再掉链子的时候,会在测试里先炸,而不是等孩子问了一句才炸。
顺带说一句今天的暗线:bearlabs.net 这个站自己也被一个括号绊了一跤——一句散文里的括号写漏了,整站的构建就垮了,那个页面也跟着点不开。今天这件事也补上了:一个括号,值一次全站重建;以后这类问题会在上线前就被拦下来,而不是等整站重建完才发现。
今天做的头一件事,是给 reading.im 找一个新地址。听着像搬家,其实不是——一册书都没动过。
书一直放在同一面架子上。架子的后面有一间工作间:管书的人在那儿上架、改名字、把不要的撤下来;架子前面是给孩子看书的地方。今天动的只是前面——它有了一扇自己的门,而门后头,还是同一面架子、同一批书。
一棵树,两个名字,中间不用来回搬一次,也不用担心两边搬岔了:孩子打开门看到的就是管书的人刚放上去的那一本。
孩子这一侧什么都没变:还是那个地址,打开就是书。
倒是后面那间工作间,今天补了三道锁。有人想从后门绕进去——溜出该待的目录、把自己改成能执行的东西、拿别处的钥匙硬闯——四条试探全被挡了回来;正经上架还是通的。锁门不能把门自己堵死,这一条今天一起验过。
另外还做了一份"能直接装"的包,送到那台机器上。包里是应用本身,加上一张写好的单子:放哪儿、怎么开、叫什么名字,全写在里面,到了不用现场想。
这个包的名字里带着做它的时间,所以下一个包出来的时候,谁都不用记这是哪一版——取最新的那一个就行。
包落在那台机器上一个只有机器自己能打开的目录里。里面写的是这台机器上的路径,不该躺在一个谁都能列出来的目录中。
出包之后,公开的那扇门自己更新了——这一版已经上到线上,孩子打开就是新的。故事没有多一份,也没有少一份。
顺带把"从门里怎么出来"这件事也理了一遍。手机上现在只有一个按钮,就是往上一级:在故事里,它带你去故事那一页;在第一层,它就是回首页。以前它被涂成了"你就在这儿"的样子,可它明明是往外走的路标,涂成当前位置,等于反过来说话。现在它就是干干净净一个图标,点一下回到上面那一层。桌面上还是四个按钮——首页、故事、杂志、书——一个都没少。
reading.im 以前是"借住"在门户那边的:孩子的账号和大人用的账号住同一栋楼,用的是同一把钥匙。今天它搬出来了——自己的门牌、自己的钥匙、自己的柜子。
听着只是搬了个地址,其实是一件事:以后改这一边的规矩,不会碰到另一边。今天动的是账号和存进度的那张桌子;回答孩子问题的那一头还留在老地方,没动——那部分不归这次动。
顺带把建那张桌子的说明书改成可以重复贴的:贴第二遍也不会因为重名撞车。因为它是手工贴进去的,不是机器推的——所以必须经得起手抖。
今天把那张名单当真了。以前两个孩子打开首页,看到的是同一份目录——像进了一间公共图书馆,谁都能看见全部的书架。现在每个孩子有一份自己的名单:哪些是自己的,哪个是借的。
顺序也听名单的:先写哪个,就先看到哪个。借来的那一格会挂一个小标签,写着"这是 Chen 的"——一眼就知道那本书不是自己的,用完要还。
名单上写了、东西却还没备齐的时候,页面不会把整间图书馆推给孩子,它会老实说"还没准备好"。这个洞今天特意堵上了:以前只要程序卡住一下,整份目录就摊开在所有人眼前——不在名单上的账号也看得见全部单元。
名单现在有三个人:Mei、Chen,还有 Benben。大人也有一份自己的书架——两个单元都算自己的,因为大人要看得见全部,不然没法陪读。
两个孩子的真账号也开好了:各自绑上自己的名字,各发一把自己的进度钥匙。密码只显示一次,爸妈把地址和密码改掉就行。
首页上那行小字也换了个说法。以前写"Brown 的清单——2 个单元",像一份报表的抬头;现在写"2 Units for Brown",同一件事,读起来是一句人话。
黑板也重画了一遍。每一格里原来是一个圆盘,盘里是那个单元的图案,名字压在下面、鼠标移上去才看得见——在手机上等于看不见。现在格子里就是单元的名字,摆在中间,一眼就认得出来;空着的月份还是那个虚线圆盘,因为那正是"这里还没有"的意思。
月份后面那个小标记也换了。原来是一个房子(是自己的单元)或一个交换的箭头(是借的),说的是"谁的",可孩子看不懂那个箭头;现在放的是这个单元自己的图案,说的是"哪一个"。借来的单元,名字下面会直接把"从谁那儿借的"写出来,不藏了。
"读到第 5 页"这件事,以前只记在这台设备上。孩子在 iPad 上读到第 5 页,换到电脑上来,还是第 1 页——进度跟着机器走,没跟着人走。
现在进度记在孩子自己名下,跟着账号走。
但它存的地方谁都看不了:文件是锁着的,钥匙在孩子的设备上,跟着登录一起走。连替它存文件的那台机器都读不懂——它只会原样收着。别人就算在目录里翻到它,也只看到一个打不开的包。
两台设备同时读怎么办:以新的那一份为准。有一条例外——读完了就是读完了,不会因为某台机器上的记录旧了,又变回"没读完"。
今天还有一件事差点出事:进度文件要放在书架里专门放进度的那一格里,而树上有一份"什么才算内容"的约定,验收的人是按它来查的。差点把每个孩子的进度当成野文件发到网上——现在约定里多了一种:存在,但永远不发布,下载整棵树的时候也不进那个目录。
存和取都只认一个入口,文件的名字由账号算出来,孩子那边连碰都碰不到。这个入口今天真的上线了,线上跑通了一遍:写进去,再读回来,是同一份。
还没做完的:两个账号现在还是占位地址(mei@reading.im、chen@reading.im),爸妈要改成真实邮箱并改密码;最后还需要两个孩子、两台设备一起验收一次。
今天还有三件事,是孩子拿着手机才会碰到的。
第一件是按钮看不见。故事页上有一个"+"和一个问号,在电脑上它们藏着,等鼠标移上去才出来——因为不想要一堆按钮压在那张画上。手机没有鼠标,于是孩子看到的就是一张画,什么都没有。现在手机上它们一直在那儿,电脑上还是等鼠标。
第二件是声音不出来。点一个词,卡片出来了,解释也在,可就是没声。查下来是三件事叠在一起:手机只肯在孩子碰过屏幕之后才出声,而这张卡的声音要等答案回来才播,那一下早就过去了;声音是整段跟着答案一起送过来的,手机对这么一大坨音频最没辙;再加上偶尔是网络的事。所以现在是:孩子点词的那一下先把手机叫醒,播放的时候换成手机认得的方式,自己再试两遍——都还不行,卡片才开口,而且只说一句很短的话:"点一下听"。那个播放按钮就在旁边,一按就好。
第三件是中文讲故事的:长按左上角那个语言圆标进"中文讲故事",现在这个模式会自己把"自动朗读"和"自动翻页"都勾上——这两个开关本来就是给"躺着听"准备的,进了这个模式还让人去找,就是白让人找。退出去也不替你取消,你要留就留着。
还有问答卡片里的那块白底板,去掉了。卡片本身就是一张浅色的纸,底下还透着那幅画,再套一个白盒子等于两层底子。现在提问和回答直接写在卡片上,画还在四周透出来。
顺带把提问那一行的颜色重新挑了一遍。原来那个棕色,其实是这两行里更弱的一个——亮的地方勉强能看,黑的地方几乎看不见,比旁边回答的黑色差了一截。换成同样深的暖色之后,它和黑色一样稳当,看着却还是暖的:孩子的话是暖的,老师的话是本色的。
黑板原来是十二个格子,每格里一个圆盘,盘上是这个月的名字。今晚这十二个格子改了脾气:整个月就是门。
把鼠标停在一个月上,两秒钟,这个单元就整块压上来——不是一张小卡片,是一整块板盖住了整块板。左上是月和它的小标记,右上是年份,底下是它的名字,写成粉笔字。
这个盖法先试过透明的版本,后来否掉了:透明的卡片底下还看得见原来的黑板,于是孩子面前摆着两块板。一张要说清楚一件事的卡片,应该是盖住,而不是透过去——它说的是"你现在在这里"。
年份是一个数字,全站只有这一个:黑板标题上的 2026,和卡片右上角那个 2026,是同一个数字。一张卡片不说自己属于哪一年,它就没法被信任——尤其在故事已经堆了好几年的情况下。
单元的小标记也只出现一次:窄屏上它在卡片角落里,宽屏上它站在月份旁边。以前两处都出现,看久了总有一个像贴错了。
还有一处小事:手机的格子改成了正方形。手机宽度的三分之一只有 82 像素,一个格子里要塞月份、名字和一个圆盘,本来就挤;现在方方正正,月份写在上面,圆盘缩到下面去,两个不再打架。
故事卡片底下那条进度线,以前只能看,不能碰。它画着"你读到哪儿了",可孩子有办法读完一整本书而应用不知道——他念出声、跟大人一起读、或者干脆翻到最后一页。一条只能看的线,说不出这些事。
所以它现在是个能拖的东西:手指按住,故事跟着手指走;松手的时候才写一次,所以拖一遍不会把进度改上三十遍。旁边的数字也跟着变——从 0/10 到 5/10 到 10/10。以前写的是"共 10 页",那说的是故事有多长,不是人走到哪儿了。
卡片的顺序也改了:正在读的那本排在最前面,没动过的按原来的顺序排在中间,读完的沉到最后。理由很简单——一个孩子回到单元页,最想找的是他昨天停下来的那一本,不是上个月读完的那十本。
读完的故事会往后退一步:它的画、它的字,都退到五分之一的亮度,指针移过去再回来。但它那条进度线不跟着退——那条线是孩子自己走过的路的记录,是一张读完的卡片上唯一还有话可说的东西。孩子永远逃不出大人的透明度,所以要退的是墨,不是卡片。
孩子问一句,等不到答案。以前所有等不到都长得一样:"出了点问题"。今天分开了四种:网断了、麦克风被拒绝了、什么都没听清、问题本身没送出去——每一种都直接说它自己,也告诉孩子现在能做什么。
接住问题的那一头也补了一刀:有一段录音,一个引擎不肯收,就换另一个引擎再试一次;有一段根本不是音频的,在送出去之前就挡住了。
还有一句"再说一次":以前一道问过的题要重讲一遍,孩子得把话再说一遍;现在服务器记得住答过什么,点一下就能再听一次。
最后是一个小到不好意思单开一段的改动:小卡片里那张画的左右边不一样宽。右边宽了四像素,因为那段留白是给平板上的名字准备的;到了手机上没有名字,那张画就偏了。现在两边一样,都是薄薄的一条边。
这种毛病不会有人来报——它不碍事,不点也不坏。但孩子每天要在这一屏上划很多次,一格一格歪着的东西,看久了是硌人的。
这周做的每一件事都是从代码那头看的:改一处、跑一遍闸、推上去。今天反过来——借一个真浏览器,用一个孩子账号从登录开始,把 reading.im 真的用一遍。
走的是首页 → 单元 → 故事 → 选词 → 提问 → 离线下载。账号是 guest,因为它本来就是给测试留的那个。
走着走着发现一件不太好看的事:主要问题不在我们的代码,在源站。reading.im/reading/ 底下那些静态文件,慢的时候会慢到几十秒,快的时候两毫秒,中间还夹着完全连不上的。
代码这边自己的毛病只有两个,都不小:把整本故事下载到本地离线读这件事,实际上不可用;提问只有一个入口,那个入口一旦卡住就再也回不来。
| 级别 | 问题 | 现象与证据 | 定位 | 对孩子的影响 |
|---|---|---|---|---|
| P0 | 内容源极慢,还会中途卡死 | 同一份 24KB 的单元索引连测三次:7.7 秒 / 16.4 秒 / 60 秒超时(只收到 4KB);浏览器里同一个文件 75.5 秒;CDN 头显示缓存命中,照样慢 | 源站回源链路,不是应用代码 | 杂志页一直停在"Loading…";首页三张单元图要 40–45 秒;故事页第一次能点要 26 秒 |
| P0 | 离线下载实际上不可用 | 点"Download for offline reading",按钮变成"Downloading…"就再也不变:两分钟内故事包缓存 0 条,九分钟后只有 3–4 条(应该约 126 个文件);"downloading N/M" 的进度一次都没出现;下载中按钮被禁用,没有取消 | src/lib/offline.ts 的 downloadPack 串行下载,进度只在单个文件完成后才报 | 孩子点了像没反应,也不知道要等多久,更停不下来 |
| P1 | 提问只有语音,没有文字兜底 | 机器人语音接口回 {"path":"pc"}(没有机器人),于是改走麦克风;权限弹窗没人回应时,问号按钮一直停在"Listening",没有超时、没有取消、没有报错,再点也没反应;整页 0 个输入框 | src/reader/pages.tsx 的 handleAskClick:状态不是 idle 就直接返回,按钮只在"思考中"才禁用,看着能点其实点了没用 | 麦克风不可用、被拒绝、或弹窗被忽略的孩子,完全没法提问 |
| P1 | 续读弹窗写"page 16 of 0" | 打开读过一半的故事:"You stopped reading at page 16 of 0."——这本书其实有 31 页;等一分钟还是 0 | src/routes/$unit.stories.$slug.tsx 的 lastPage 取自异步的单元索引,索引没到就是 0 | 孩子看到"0 页",会以为这本书是空的 |
| P2 | 每次打开都报 React #418 | 全程 24 次"服务端渲染的 HTML 和客户端不一致",每次进站必现 | 预渲染的 HTML 和客户端渲染对不上,React 把整棵树丢掉重画 | 每次进站多一次整页重画 |
| P2 | 首页先闪一句"你还没有阅读单元" | 冷启动抓包:第 5 秒渲染"No units on this list yet",第 8 秒才拿到单元列表 | src/routes/index.tsx 没有 loading 态,列表没到就先按空列表渲染 | 孩子第一眼看到的是"你没有单元" |
| P2 | CDN 健康灯误报 | 探测用的是 HEAD 请求,而这个源对 HEAD 经常挂着不回应(curl 28 秒无响应),浏览器里常被 10 秒超时掐掉(全程 30 次) | src/hooks/use-cdn-health.ts | 内容明明好着,灯却灭了 |
| P3 | "Add words" 按钮在电脑上找不到 | 按钮只有 title 没有 aria-label(别的图标按钮都有);电脑上默认 opacity-0,只有鼠标移上去才显形;点下去图标还是那个加号,看不出模式变了 | src/reader/highlight.tsx | 生词本是唯一的入口,却几乎没人找得到;点了也看不出发生了什么 |
| P3 | 地震单元的杂志和书是空的 | 页面写"No magazines in this unit yet.";索引里这一单元 magazines=0、books=0(ancient-heritage 有 1 本杂志) | 内容还没铺,不是代码问题 | 首页三个单元里有一个点进去是空的 |
| ✅ | 审阅页对不是编辑的人关得死 | 访客访问 /stories/…/review 被弹回故事页 | 权限收口是对的 | 没有越权 |
这十条里只有两条是我们代码自己的事:下载和提问。其余的要么在源站那一侧,要么是别的工具的默认行为。
修的顺序也就照这个来:先修源站——它一变慢,杂志页、首页的图片、续读弹窗上的数字、下载的进度,全都跟着好;再把下载改成并行、进度照实报、并且能取消;然后给提问留一条文字的路,哪怕只是"打字问一句",也比只有一个麦克风靠谱;最后是首页那个空态,和 React #418——那个得拿非压缩的构建去定位是哪儿对不上。
走坏的时候也走了一遍好的,不然这张清单会显得比实际更糟。
登录是通的:guest 这个账号进去,首页写着"3 Units for Friend",每个单元标着"Borrowed · Brown"。故事页的正文、插图和声音都在——翻到第 16 页,声音 33 秒。选词讲解也通:鼠标拖住一个词,BenBen 的小圆标浮出来,点一下,中文释义、这个词在这篇故事里的用法、还有一句例句,一样不缺。
左上角那个语言圆标点一下,"正在用中文讲解"变成"Explaining in English",再点回来。上一页下一页、播放静音,都在。编辑审阅页对不是审阅人的账号是关着的——这条没坏,而且是刻意关的。
还有一个好消息是:这套东西的骨架是对的。选词、讲解、提问、下载,四个入口都在,也都通到后端。今天撞到的多半是路上有几段年久失修的路,而不是路本身没修。
昨天我贴了一张很扎心的图:一个孩子抱着平板,屏幕上的圈一直转,转不完。那张图是真的,那是我昨天在 reading.im 上看到的东西。今天该换一张了——同一个傍晚,同一个孩子,屏幕上终于有一条会走的进度线,旁边还有一个能按的 STOP。
先说给孩子们。点那个下载按钮之后,屏幕上会出现一行字:downloading 3/12 · 2.0 MB。3 是已经拿到的,12 是这一篇一共要拿的文件数(一页一张图、一段一段声音,一篇下来几十上百个),2.0 MB 是已经稳稳躺进你平板里的量。你不用再对着一根不动的圈猜它是不是坏了——它要么在走,要么你能看见它停。
还有一个你大概用得上的本事:再按一次那个按钮,下载就停了。不是关掉页面那种停,是真的停。已经下好的那些还留在平板上,不会被扔掉;下次再点,它会接着上次的地方往下走。
说给爸妈,按要紧程度排三条。第一,孩子现在能自己决定"还要不要继续等"——这件事比"要等多久"更要紧。昨天那种按下去就没有回音、也没法拉回来的按钮,才是真正让人不安的地方。第二,昨天那个"九分钟只下到 3 个文件"的情况不会再有了:现在一次同时拿 6 个,而且一个文件坏掉不会再把整本书拖下水。只有故事自己的那份数据不行——缺了它故事根本打不开,这个我们不敢假装没事。第三,有些故事本来就是纯文字的,某一页没有插画不算错误,现在也不会再被当成错误。
所以如果你家孩子平板上留着昨天那个下了一半的包——不用管它,也不用删。下次点下载,已经下好的不会重下,它会接着往下走。在它下完之前,那个故事不会显示"离线可读",这是故意的:我们宁可让你看见它还没好,也不想假装好了。
还有一件得说清楚:它现在在哪儿。新版本已经发到试点用的那个地址(reading-im-spa.pages.dev)。reading.im 这个地址暂时还是旧的——它读的是我们自己家里那台机器上的一份文件包,我们还没有把新包送过去的通道。所以你在 reading.im 上还没看到进度条,不是你看错了,是它还没换。
昨天那份清单里最吓人的一条,是"同一个文件有时要等几十秒"。先说人话版:慢的不是这个应用,是我们自己那台放在家里的机器,到 Cloudflare 边缘之间的一段路。那段路是家里宽带出去的一段,会堵——堵的时候孩子在等的就是它。用我们写代码的人的口气说,结论对我们有点难堪:问题不在 reading.im 的代码,在那条路上(那台机器是跑内容的,不是孩子的平板)。
证据是同一份文件在不同时间的同一张脸。10-24 傍晚七点,它 0.15 秒就出来;10-25 凌晨四点,它要 25 到 40 秒,十次里有三次干脆连不上;今天早上四点四十五,它又是 0.58 到 0.70 秒,十次十次都到。同一份文件、同一套缓存规则、同一个边缘——变的只有那条路堵没堵。
我们能改的那一半改了:给缓存规则加上了 serve_stale,边缘现在手里有副本就先回答,副本刷新放到后台去做。内容规则本来十分钟过期一次,以前每次过期都有一个孩子被那条路绑住,现在没有了。
还有一件事得说清楚,免得以后有人再往里钻:Cloudflare 那个 response_buffering 开关改不了,而且不是权限问题——加了 Zone Settings → Edit 权限之后,另外两个设置都改得动,就它不行,因为这个 zone 挂在 Free 套餐上,Cloudflare 直接把它标成不可编辑。它就算能改,也只是换个送数据的方式,不改变要等多久。真正决定孩子等多久的,还是那条路。
所以给爸妈一句实话:昨天那种慢,不是"孩子用得多了才会慢",是那条链路偶尔会堵。我们这一侧能做的已经做了;剩下的要么等那条路自己通,要么把内容搬到离孩子更近的地方去——后者是下一步,还没做。
昨天那十条里,今天关掉了两条:下载,以及慢的那条路的成因。还欠着的,老实列一下。
第一,提问还是只有一个入口——麦克风。孩子点问号,浏览器要麦克风权限;权限弹窗没人管的时候,那个问号会一直停在"Listening",没有超时、没有取消、没有报错,再点也没反应。对没有麦克风、被爸妈拒绝过权限、或者干脆不理那个弹窗的孩子,提问这条路是断的。改法已经想清楚了:给麦克风一个超时、给同一个按钮第二次点击一个含义、再留一条能打字的兜底。这条还没动手。
第二,首页冷启动会先闪一句"你还没有阅读单元",两秒后单元卡片才到。空列表和"还没加载出来"在界面上长得一模一样,孩子会先看见前者。
第三,每次打开页面都会报一次 React #418(服务端渲染出来的 HTML 和客户端对不上)。它现在只是多画一次页面,不影响能不能读,但它是那种"不查就会一直长"的问题。要查它得拿一个非压缩的生产构建来对,这个我还没做。
给爸妈的版本是:这三件都不影响今天读故事,但第一件影响"用得下去"——它跟下载是同一类问题,都是孩子被卡在一个不给路的地方。所以它排在下一个。
一个 IB PYP 单元有两半:英语和单元领域。两半必须一起往前走,把它们留在同一处的,是那个导师——同一个导师要同时管住中心概念、探究线索、七个关键概念,和从这些文本里长出来的学术英语。底下是一条探究循环:预读扫描、泛读、找一找、理一理,最后回到关键概念上反思;每一圈都为了把孩子往上挪一级。
这一周走到最后一步:七个关键概念逐条收束,反思旁边还要并排记下信心——不敢开口尝试的学生,答对答错都还没做完这个单元。