play.html,可通过任意 ClickHouse HTTP 端口的 /play 路径访问) 可以将一个选项卡转换为 notebook:即由若干单元按顺序组成的序列,每个单元要么是一个带有独立结果的查询,要么是一段 Markdown 文本。其文档形态与 Jupyter、Observable、Hex 或 Deepnote 相同,用途也一致——将一连串查询及其间的推理过程集中保存在同一个页面中。
Notebook 模式以选项卡为单位启用。一个选项卡可以是 notebook,另一个选项卡则仍可作为普通的单查询编辑器;每个选项卡在页面重新加载后都会保留各自的 notebook。
添加单元
每个选项卡初始时只有一个查询单元,外观与普通编辑器完全相同:没有标题栏,没有单元边框装饰,也看不到任何 notebook 的痕迹。查询返回结果后,结果下方会出现一个较大的[+] 按钮,其样式刻意保持低调,以免喧宾夺主、干扰对结果的查看。点击该按钮后,会展开可添加的两类单元:
- Query — 新的查询单元。它会立即获得编辑器焦点,您可以直接开始输入。
- Text — 新的 Markdown 单元,打开后即处于编辑模式。
单元标题栏
当存在两个或更多单元时,每个单元都会显示一个标题栏,标明其位置和类型,例如1. Query 或 2. Text。查询单元在运行期间还会显示一个状态圆点,运行失败时则显示红色圆点;已完成的单元不做任何标记,因为其结果就显示在正下方。
将鼠标悬停在单元上或单元获得焦点时,标题栏会显示以下操作按钮:
每个选项卡始终至少保留一个查询单元,因为共享编辑器就位于查询单元中;因此,仅剩最后一个查询单元时,其
✕ 按钮处于禁用状态。
查询单元
页面上始终只有一个查询编辑器——即具备语法高亮、自动补全和查询参数输入框的完整编辑器。在 notebook 模式下,它会停靠在当前活跃的查询单元中,并且仅在获得焦点时显示:一旦焦点离开编辑器,该单元也会随之折叠,因此阅读 notebook 时页面上不会出现任何展开的文本区域。单击已折叠的查询即可让它重新获得编辑器 (如果编辑器原本位于其他单元,则会被移到这里) 。焦点在编辑器内部移动 (例如移到查询参数输入框或某个补全候选项) 时,编辑器不会折叠。 折叠后的查询单元会以单行形式显示其查询:保留语法高亮,连续的空白字符 (包括缩进和换行) 被压缩为一个空格,若内容过长,右侧末尾会渐隐淡出。折叠单元没有自己的背景,因此看起来就像页面上的普通文本,而不像另一个输入框。Run 按钮、进度行以及复制/下载操作会从页面标题区移出,停靠在正在编辑的查询下方,即查询与其结果之间。这里正是它们所描述的那次运行所在的位置,也能让人一眼看出如何运行该单元。它们隶属于编辑器,因此会随着单元获得或失去焦点而一同出现和消失。
在 notebook 模式下,结果显示得更加紧凑:字体更小,单元格内边距相应缩小,表格高度最多为视口高度的四分之一。拖动结果的底边即可调整其占用空间,方式与调整查询编辑器大小完全相同;文本单元的 Markdown 编辑器也有同样的拖动手柄。表格本身就是滚动容器 (纵向和横向均是) ,因此其细滚动条位于表格自身的边缘;过长或过宽的结果既不会把 notebook 的其余内容往下推,也不会把页面撑宽;滚动时列标题始终固定在表格顶部。由于标题位于表格最顶部,列类型会悬挂在标题正下方、覆盖在前几行之上,而不像页面滚动的表格那样显示在标题上方。
运行方式一如既往:Ctrl/Cmd+Enter (或 Run 按钮) 会运行编辑器中的查询,也就是活跃单元中的查询。每个选项卡同一时间只能运行一个查询;在同一选项卡的另一个单元中启动运行,或按下 Stop,都会结束前一次运行。
Logs 和 Metrics 视图以及进度行中的资源计量器,隶属于它们所描述的结果所在的单元:每个单元都保留各自的选择和计量器,而共享的开关显示的是活跃单元产生的内容。切换单元后,会恢复该单元自己的日志、指标和计量器。查询运行期间,进度行会一直留在正在运行查询的单元中——即使把编辑器移到另一个单元去编写下一个查询,实时进度也不会被清空,这些开关也不会转而指向另一个单元的结果。
列颜色标记、固定列以及结果形态 (即排序、过滤条件和分页) 同样隶属于单元:它们是针对某个结果设定的,因此跟随该结果。对某个单元的结果进行排序或筛选会重新运行该单元;如果编辑器位于其他单元,会先将其移过来。若某个单元通过 Run all 生成了多个表,这些表共用同一套设置,而每个单元则各自保留自己的设置;共享链接和浏览器历史记录携带的是活跃单元的设置,其他单元的设置则随各自的单元保存。
文本单元
文本单元用于存放 Markdown 内容。编辑时,文本区域会显示源文本并高亮其中的标记——标题、强调、代码片段、链接、列表符号和引用块都会以颜色区分,但不会被渲染隐藏——因此你编辑的始终是源文本。文本区域会随内容增加自动扩展。 离开文本区域 (点击其他位置、按Escape 或 Ctrl/Cmd+Enter) 即可结束编辑,编辑区域随即替换为渲染后的 Markdown。点击渲染后的文本可重新进入编辑状态。
无论处于哪种状态,文本单元都使用与查询相同的等宽字体,因此整个 notebook 读起来浑然一体,单元在源文本与渲染结果之间切换时字体也不会跳变。
支持的语法包括:标题、强调、行内代码和围栏代码块、链接和图片、自动链接的 URL、有序列表和无序列表、引用块、水平分隔线以及管道表格。文本单元中的原始 HTML 会被转义而不会被渲染;链接和图片的 URL 仅限 http、https、mailto 以及相对路径——诸如 [guide](guide.md) 或  之类的普通相对链接可以正常使用,而 javascript: 或 data: 之类的协议方案,以及协议相对形式的 //host/... (它指向另一个源,而非相对路径) ,都会以纯文本形式保留。
持久化
选项卡中的整个 notebook (包括每个单元、单元内容以及小型结果快照) 会与工作区的其余部分一起存储在 IndexedDB 中,并在重新加载页面时恢复。其尽力而为的行为以及结果大小限制均与 Web SQL UI 中所述相同。 URL 和浏览器历史记录始终跟随当前活跃的单元:# 片段携带该单元的查询,后退和前进操作则会在你访问过的单元 (及选项卡) 之间切换。共享 notebook 的 URL 时,共享的只是活跃单元的查询,而非整个 notebook。
复制选项卡会复制整个 notebook,但不包含结果。
将选项卡保存为 Markdown (通过选项卡栏中的保存按钮) 时,会按顺序写出 notebook 中的每个单元:每个查询单元写为一个围栏代码块,其后附上该单元的参数和结果表;每个文本单元则写为一个信息字符串为 markdown 的围栏代码块。加载此类文档时,会恢复每个选项卡及其全部单元;但结果不会恢复,因此加载后的查询单元均处于未运行状态。