来源: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-canvasmax-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,改的是外壳这一层,所以它跟着一起正过来,不需要另写一遍。

现在:正文 640 贴在页的左边缘,右边 844px 空着(线上 1920 实测)

定案:画布居中,右侧多一条目录轨道;页边 72 与正文 640 一个没动

同一个笔记面 · 夜纸:主题由系统偏好给,轨道跟着换一套值

1280:页不够宽,轨道不出,只做居中

窄屏 430:沿用现有的窄屏降级,画布不再加左右留白

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 字一行,两边都不好用。

现在:日程页点一条笔记就换模块,日历与那天的上下文一起消失(线上 1920 实测)

定案:左栏收到 844,日历与当天流水留在原地,右叶 712 滑出笔记

同一天往下滚:被点开的那条笔记有时间、有朱砂点,读完不用后退

同一屏 · 夜纸

页不够宽(1280):叶整宽滑出,盖住日程,这里给的是「返回」而不是并排

窄屏 430:同一套 DOM、同一段动画,只是左栏不收

280ms 冻在三帧上(0 / 140 / 280ms):日历是挤压,叶是推进,正文全程不重排

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-canvasmain.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 从工作区与本地预览接口抽的一份快照。