来源:
notes/documents/网页版阅读面自适应与日程分栏阅读.md网页版阅读面自适应与日程分栏阅读:原型与方案
Status: ready-for-agent
上游依据:notes/documents/网页版多模块改版spec.md(note_874c5a3ff88243cf)、
网页版多模块设计:外壳契约、页边档号与日程模块、网页版笔记面体检报告。
外壳与路由的两条约束仍是 web/docs/adr/0009(新模块只准替换索引与页)与
web/docs/adr/0010(查询参数是唯一状态)。原型代码在
.scratch/web-reading-column-and-daily-split/prototype/,图在
.scratch/web-reading-column-and-daily-split/assets/(本笔记引用的是同一批图的副本)。
Problem Statement
一、笔记的正文被钉死在页的左边缘。 正文 640px 是这套设计定下的度量(宋体 17/1.9,一行 34 字), 这个数字没问题;有问题的是它在页里的位置。页这一列在 1920 的屏上是 1556px (1920 − 脊 64 − 索引 300),正文只吃掉左边 712px(页边 72 + 正文 640),右边 844px 全空着。 屏幕越宽越失衡:1440 的屏上空 364px,1920 的屏上空 844px。这不是「窄」,是没有主张—— 一段量得很准的正文,配了一个没人做决定的空白。
二、从日程点一条笔记,会把整个模块换掉。 现在点日程流水里的任何一条笔记,路由换成
?m=notes&n=<noteId>,脊上的当前模块从「日程」跳到「笔记」、日历整块消失、页左边那条
索引也换成笔记索引。读一句 12:48 的笔记,代价是丢掉「这是哪天」「当天前后还发生了什么」这层上下文,
读完要回来得按浏览器后退。日程里那条笔记本来就是当天的注脚,不该以换掉当天为代价。
Solution
A. 阅读面:页里加一层「画布」,宽屏多一条目录轨道
度量一个都不动——页边仍然 72,正文仍然 640,行宽仍然 34 字。动的是它在页里的位置:
- 页里加一层
.page-canvas:max-width: calc(var(--lane) + var(--measure))、margin-inline: auto、 左右各留 40px。于是「页边 + 正文」这一整块在页里居中,左右两侧的空白一样多。 - 页面列 ≥ 1400px 视口且这篇笔记有 ≥2 个标题时,画布加宽到
72 + 640 + 48 + 240 = 1000px,右列是目录轨道:240px,position: sticky粘在页的滚动容器上, 列 h2 / h3(h3 缩进 12px)。这条轨道的上缘与档号那一行对齐(都是距页顶 26px)。 - 当前小节用强调色 + 页边一道朱砂标记,与索引里的当前项同一个手法(不是加底色、不是加粗)。 点一行把对应小节滚到页顶,滚动时当前小节跟着换。
- 标题不足两个、或者页不够宽,画布退回 792px 只做居中,轨道整条不出现—— 空的右边宁可空着,也不放一条只有一行的目录。
度量对照(实测,device_scale_factor=1.5 出图时量的 CSS 像素):
| 视口 | 页这一列 | 画布 | 正文列位置 | 右边留白 | 目录轨道 |
|---|---|---|---|---|---|
| 1920(现在) | 1556 | 无 | 436 – 1076 | 844 | 无 |
| 1920(定案) | 1556 | 1000,居中 | 714 – 1354 | 278 | 240,在正文右侧 48px 处 |
| 1280(定案) | 916 | 712,居中 | 538 – 1178 | 65 | 不出(页不够宽) |
| 430(窄屏) | 430 | 同窄屏降级 | 16 – 414 | 0 | 不出 |
其他文档 模块走同一套 .content / .measure,改的是外壳这一层,所以它跟着一起正过来,不需要另写一遍。





B. 日程:左栏收缩,右叶滑出
点一条笔记,模块不换,日历不消失,页在 280ms 内分成两栏:
- 路由仍是查询参数:
?m=daily&d=2026-09-22&n=note_…。模块还是daily,日期不丢, 浏览器后退就是「关掉这叶」,收藏/分享这一条链接能还原现场。 - 左栏收到
calc(100% - var(--leaf)),--leaf: min(46%, 712px);右叶是position: absolute; right: 0,宽度一开始就是终宽,只做translateX(100%) → 0。 两段动画同一个时长同一条缓动(280ms cubic-bezier(.2,.6,.3,1)),缝就一直是缝。 - 因为叶的宽度从头到尾不变,滑动过程中正文不重新排版——只是从右边推进来。 左栏(日历)是窗口收窄,它会重新排,这正是「收缩」该有的样子。
- 被点开的那一条:时间转强调色 + 页边一个朱砂墨点,与索引当前项同一个手法; 叶里显示同一条笔记的档号、创建、更新与正文,正文仍是页边 72 + 640 度量。
- 关:叶左上角一个「关闭」,Esc 也行;关掉后焦点回到那条笔记行(行上有
aria-expanded),role="complementary"+aria-label="笔记详情"。 - 左栏与右叶各自滚,离开时不把用户滚回月历顶部:日程页的页改成
.page { overflow: hidden }+.split-left { overflow-y: auto; height: 100% }, 开合前后滚动位置都在同一个元素上,不会跳。
页不够宽就退回整宽滑入:页面列装不下「一个能用的日历 + 一个能读的叶」时,叶整宽盖在日程上滑出来 (同一套 DOM、同一段动画)。并排的门槛定在视口 1560px,也就是页面列 1196px——再窄,日历只剩 7 列 × 65px, 叶里的正文只剩 25 字一行,两边都不好用。







Implementation Decisions
- 一处读:
NotesPage现在把「取单篇 + 渲染」写在自己身上,日程的叶要的是同一个东西。 把它拆成NoteReader(props:id / pending / missing,内部负责取数、渲染、档号), 笔记模块的页与日程的叶都用它。叶不要另写一份渲染。 - 路由只加读法,不加参数:
?m=daily&d=…&n=…已经在Route里存在(App.tsx已经算matchedNote),改动落在日程页的onNote:从navigate({ module: 'notes', note: id, date: null })改成navigate({ note: id })。date必须留着。 - 开合不是路由以外的新状态:叶开不开由
route.note决定,不做第二个useState。 动画进出靠 class(is-open)+ CSS transition,出叶时不立刻卸载:先在transitionend/ 280ms 之后卸载,否则关的那一下会硬切。 - 尊重
prefers-reduced-motion:两条 transition 都关掉,只剩状态切换。 - 画布只包页,不包外壳:
.page-canvas是main.page里的一层,脊、索引、页这条竖节律不动 (ADR-0009 的「页边 gutter 由外壳提供」照旧,72 还是 72)。 - 要落一条 ADR:这条改动第一次让「页」有了页内横向构图(居中 + 可选的右侧轨道),
也第一次让两个模块同时占住一页。写进
web/docs/adr/0013,把「页内可以有自己的横向分栏」 与「轨道只在 ≥1400 出现」这两条固定下来,否则下一个模块会各自发明一套。
Considered Options
- 把正文加宽到 900/1200。最省事,但 640px 一行 34 字是这套设计量过的度量(
--measure), 中国读者一行 25–45 字,1200px 一行 70 字是另一种排版(更适合 code 或者表格)。 用户说「窄」,可量出来的是位置失衡而不是行宽不够,所以动位置不动度量。 - 只居中,不加轨道。六行 CSS 就完了,也解决「偏左」。代价是 1920 上右侧仍留 278px 死白, 而一篇 108 节的长笔记本来就需要一个「有哪些节」的入口。轨道是拿空出来的地方做正事。
- 轨道改成页内目录横条(正文顶上)。会顶掉档号那一行的节律,且长笔记里滚动时要一直粘着, 比侧栏更占地方。
- 叶用固定 712px、左栏吃剩下的。1920 上好看(左 844),1440 上左栏只剩 364px, 日历直接不可用。改成百分比 + 上限。
- 叶做成 hover/预览弹层。日程里点笔记是要读的,不是看一眼标题。
- 不同屏宽一律并排。1024 上并排的结果是日历与正文都不能用,不如整宽滑出。
边界与没做的
- 轨道只有 h2/h3 两级,且只在 ≥2 个标题时出现;不显示 h4,也不做折叠。
- 不支持同时开两篇笔记(日程的叶只有一个),不做叶内跳笔记。
任务模块还没装订,本轮不动;它的「关联笔记」将来复用的是同一个叶。- 窄屏没有做手势(左右滑关闭),只留「关闭」按钮与 Esc。
- 原型是静态 HTML(真 CSS、真度量、真数据,
?layout=current渲染出来就是线上现在这一版), 没有 React、没有接口、没有路由;数据是 2026-09-23 从工作区与本地预览接口抽的一份快照。