资讯详情

Etherpad标题插件ep_headings2安装配置与避坑指南

📅 2026/10/8 10:15:08 | 华诺云谱 👁 阅读
Etherpad标题插件ep_headings2安装配置与避坑指南
简介ep_headings2 是一款为 Etherpad 在线协作文档提供标题层级功能的开源插件适合需要在实时编辑面板中快速完成 h1 至 hn 标题样式设置的个人或团队用户。包内共 54 个文件压缩后仅 86KB核心为 5 个 JavaScript 文件与 38 个 JSON 文件前者负责标题按钮和编辑逻辑后者主要是多区域语言包含中文 zh-cn、繁体 zh-hant 等及配置数据另附 README、LICENSE、测试与 CI 配置方便二次开发或本地化。资源已吸引约 290 人浏览学习可作为了解 Etherpad 插件结构、国际化实现及前端工具栏扩展的轻量级样例。通过阅读源码能掌握 h1 等标题命令的注册方式、活动标题高亮显示、复制粘贴及导入导出兼容处理等细节对希望在 Etherpad 生态中开发同类功能的工程师有直接参考价值。1. ep_headings2 到底解决了什么从编辑器里的“标题”按钮说起在 Etherpad 上维护过内部知识库的人大概率遇到过同一个尴尬多人同时编辑时字号调了层级乱了导出的 HTML 根本分不清谁是标题。ep_headings2 是 Etherpad 生态里专门解决这个问题的标题插件它给工具栏补上“标题 1/2/3”这类真正的标题选择器而不是简单把字号放大。它能做的事刚好是默认编辑器缺的那块让标题层级从视觉样式变成文档结构再支撑后续的样式定制与导出。如果你正打算搭团队协作笔记、公共 Wiki或者已经跑了 Etherpad 但嫌标题太弱这个插件几乎不用改代码就能直接进生产环境。2. 先弄懂 Etherpad 插件机制和 ep_headings2 的设计取舍为什么是属性标记而不是直接改 HTML2.1 Etherpad 插件骨架ep.json、hooks 与客户端资源Etherpad 的插件体系并不复杂但它和普通 CMS 插件很不一样Etherpad 的编辑器是协同编辑器所有内容操作都要通过 called “changeset” 的增量协议同步因此插件不能像改静态页面那样直接往 HTML 里塞标签。常见做法是每个ep_开头的 npm 包都携带三样东西package.json描述插件名和依赖ep.json声明 hooks 入口static/目录放客户端 JS 与 CSS。ep.json 里的 hooks 是插件和 Etherpad 内核之间的约定。比如aceEditorCSS用来注入编辑器样式aceAttribsToClasses用来把文本属性变成 CSS 类名eejsBlock_editbarMenu用来在编辑工具栏里插入控件。一个标题插件如果要做得完整一般会同时用到这几个 hook一部分管“入口在哪里”一部分管“渲染成什么样”还有一部分管“导出时要不要保留语义”。实际看到的 ep_headings2 或同类插件其 ep.json 大致长这样{ parts: [], hooks: { aceEditorCSS: ep_headings2/static/css/headings.css, aceAttribsToClasses: ep_headings2/static/js/hooks, aceInitInnerdocbodyHead: ep_headings2/static/js/hooks, eejsBlock_editbarMenu: ep_headings2/static/js/editbar } }这里每个字段都值得解释aceEditorCSS指向的 CSS 只作用于编辑器画布aceAttribsToClasses在收到带标题属性的变化时把它映射成对应的 CSS 类aceInitInnerdocbodyHead会在文档初始化时往编辑区头部塞必要的 meta 信息eejsBlock_editbarMenu则负责往工具栏下拉或按钮组里加入口。理解这一层后再装插件你就不容易把“插件没装上”误判成“功能坏了”。2.2 ep_headings2 和内置标题功能、手改 HTML 的差别默认 Etherpad 的工具栏里不是完全没有标题按钮而是它通常只能做“近似标题”放大字号、加粗、改颜色本质上都还是文本样式。对协同编辑来说文本样式只影响显示不影响结构导出成 HTML 时别人拿到的也只是一堆strong和span stylefont-size:...根本识别不了目录和层级。ep_headings2 解决的是结构问题它用 Etherpad 的文本属性attribute记录“这行是一级标题还是二级标题”而不是靠视觉样式伪装。有人会问既然要结构为什么不直接把h1写进 pad 内容这是最容易翻车的一条路。Etherpad 的协同文字是带属性序列化的如果直接把 HTML 标签当正文存进去两个人同时编辑标题和正文时changeset 在 OT 合并阶段会互相踩踏轻则多出半个标签重则整段文字在冲突回滚时丢失。ep_headings2 的属性标记方案避开了这个雷标题级别是挂在文本上的一个结构化属性比如h:1、h:2、h:3它会随着文本一起参与合并和撤销而不是变成游离的 HTML 标签。这里面还有一个容易被忽略的好处当 Etherpad 升级或换导出插件时属性可以稳定映射。你可以在编辑器中把标题渲染成蓝色大号字也可以在导出阶段映射成h1如果哪天换了皮肤只需要改 CSS而不必回头去翻 pad 里的历史内容。这正是“属性标记 渲染层映射”比“直接存 HTML”更适合协同场景的根本原因。2.3 标题级别映射从工具栏下拉到文档属性的完整链路我一般会把 ep_headings2 的工作流程拆成四步来看。第一步用户在工具栏的选择器里选择“标题2”第二步客户端脚本把当前光标所在行或选中文本包成一个带h:2属性的 changeset第三步服务端广播给所有在线协同者其它客户端收到后更新本地文档模型第四步渲染层根据属性把该行显示成二级标题的样式。在前端渲染时编辑区并不会直接把h1标签写进协同 DOM它可能只是给对应行加一个类似heading-level-2的 class然后用 CSS 把它画成标题样子。只有走到导出 HTML 这一步才需要真正输出h1、h2语义标签。这也是为什么很多人在 pad 编辑区看着没问题放到导出器里就发现标题丢了因为导出器要知道“属性怎么转成标签”这通常依赖 ep_headings2 或导出插件是否实现了对应的 hook。理解了这条链路后面安装和调参时就不容易懵。你改的每一个配置要么是在影响“选取哪些级别显示在下拉框里”要么是在影响“属性怎么映射成 CSS/HTML 结构”。带着这个认知去读插件 README比对着网上教程机械复制命令可靠得多。3. 用 npm 或 admin 面板装好 ep_headings2两条安装路径和配置文件参数3.1 最小安装installPlugin.sh 与手动 npm 方式假设你的 Etherpad 部署在常见的/opt/etherpad-lite并且已经配置了 systemd 服务。安装 ep_headings2 最简单的方式是直接用官方提供的安装脚本cd /opt/etherpad-lite sudo -u etherpad ./bin/installPlugin.sh ep_headings2installPlugin.sh本质上是对 npm install 的一层封装它会切换到 Etherpad 工作目录并安装插件到node_modules。我特别强调用-u etherpad是因为很多系统里 Etherpad 以独立用户运行如果直接用 root 装了插件node_modules下会出现 root 属主的文件之后服务重启时可能没有权限读取。命令跑完后不要急着干活先重启服务sudo systemctl restart etherpad在无法使用在线 npm registry 的内网环境也可以用离线方式把ep_headings2的 tar 包下载到服务器然后解压到node_modules目录。不过这种情况会把版本依赖搞得很难维护我一般建议还是开一个 npm 代理或者把依赖打进自己的内部 npm 仓库。手动 npm 方式本质上一样区别只是绕过了官方脚本cd /opt/etherpad-lite sudo -u etherpad npm install ep_headings2注意这里的参数没有--save因为installPlugin.sh和 npm install 都会在node_modules里产生包但package.json不一定同步更新升级 Etherpad 时容易丢。稳妥起见装完之后可以把插件名加到容器镜像或部署脚本里这样重新拉代码时不会忘。3.2 确认插件加载从日志到管理界面的检查方法安装完成后最怕的是按钮没出现你误以为失败。先做两个确认第一插件是否真的进了node_modules第二Etherpad 进程是否把插件加载进来了。第一个问题用 ls 就能看比如ls -la /opt/etherpad-lite/node_modules/ep_headings2如果目录存在再看它的package.json确认name字段是ep_headings2。有些情况下安装脚本会把包装到错误目录导致目录存在但 Etherpad 识别不到。确认目录只用了几秒钟能省下后面很多排查时间。第二个问题到 Etherpad 管理后台访问/admin/plugins登录后看已安装插件列表里有没有 ep_headings2。这个页面显示的是启动时真正扫描到的插件比你自己看目录更权威。如果你不想开浏览器也可以看服务日志grep -i headings /var/log/etherpad/etherpad.log正常启动时日志里会出现类似registered plugin ep_headings2或found plugin ep_headings2的记录。如果目录存在但日志里没有说明可能是启动时权限不够或者插件被settings.json里的disablePlugins配置禁用掉了。这个检查点值得养成习惯因为不少人折腾半天最后发现只是没重启服务日志还是旧的。3.3 settings.json 里常用的 ep_headings2 配置参数ep_headings2 并不一定需要配置才能用多数版本装上就能从工具栏下拉里看到标题级别。但当你只想开放“二级标题到四级标题”时就需要看配置。以常见维护版本为例/opt/etherpad-lite/settings.json里可以单独加一个ep_headings2节点{ ep_headings2: { levels: [ { level: 1, label: 标题 1, tag: h1 }, { level: 2, label: 标题 2, tag: h2 }, { level: 3, label: 标题 3, tag: h3 } ], shortcuts: { h1: Ctrl1, h2: Ctrl2, h3: Ctrl3 } } }这里的字段含义很直接levels决定工具栏下拉里出现哪些标题级别以及这些级别在导出 HTML 时要映射成的标签shortcuts给标题级别设置快捷键。需要提醒的是不同版本的 ep_headings2 能识别的字段并不完全一致有些版本只支持levels有些版本根本没有shortcuts配置强行写入会被忽略。先看插件 README 或去 node_modules 里翻一遍源码确认配置项存在再改比在网上复制一段“通用配置”更靠谱。如果 settings.json 里的配置没有被插件响应还有另一个入口工具栏自定义。很多 Etherpad 实例为了精简界面会在启动参数或皮肤配置里自定义工具栏把一些按钮排除掉了。ep_headings2 的标题选择器在自定义工具栏里可能默认不出现这时候需要把对应的按钮加回来。具体按钮名以插件 README 里的editbar说明为准常见版本会提供类似headings的按钮标识。提示配置完 settings.json 后必须重启 Etherpad 才会重新加载。有些版本支持热加载插件列表但不建议依赖这种行为因为在配置频繁改动时你很难判断当前真实生效的是哪一份配置。4. 把标题用起来工具栏操作、样式定制与导出注意4.1 在页面上给一段文本套用标题级别的操作要点ep_headings2 装上之后打开任意 pad光标落到文字所在行然后从工具栏下拉里选择“标题 2”或“标题 3”。它处理的基本单位是“行”也就是 Etherpad 里的 line。这里最容易踩坑的是选区跨度如果只选了行内半句话插件通常会把这半句单独拆成一行并应用标题后半句变成正文视觉上就成了两行。所以我的习惯是先让光标停在该行任意位置不做跨行选区再点标题如果已经出现拆行误操作立刻用 CtrlZ 撤销不要手动拼接。如果你想取消标题一般做法是把下拉选项切回“正文”或“普通文本”。切换回正文后该行内容会保留但结构属性被移除。这一操作对协同者的影响也会实时同步不会把整段格式弄乱。对于新手用户最好在团队内约定标题只用于真正的章节标题不要把整段正文都设成二级标题否则 pad 导出后的目录结构会非常臃肿。另外ep_headings2 通常会把标题样式继承到 text 样式——也就是说你仍然可以对标题文字再调颜色、加粗或斜体。不要惊讶于“标题还带细微格式”因为标题属性只负责“层级结构”其它内联格式互不干涉。如果你发现某一行同时带有标题属性和加粗属性导出 HTML 时可能同时生成h2和strong这是符合预期的不必视为冲突。4.2 用 CSS 重定义标题外观在线自定义与皮肤定制很多人装 ep_headings2 后觉得标题样式太素想改成带下划线、有背景色的块级样式。常见做法是给编辑区写自定义 CSS。不同 Etherpad 版本的自定义样式入口不太一样有的在后台管理界面有 “Custom CSS” 输入框有的则需要到皮肤目录写pad.css。先找出你版本的自定义样式入口再按标签覆盖/* 自定义 ep_headings2 标题外观 */ h1 { font-size: 28px; border-bottom: 2px solid #2d87ff; padding-bottom: 4px; margin: 16px 0 8px; } h2 { font-size: 22px; border-left: 4px solid #2d87ff; padding-left: 8px; margin: 12px 0 6px; }这段 CSS 同时作用于编辑视图和大多数字体导出场景因为它直接针对最终渲染出的h1、h2元素。不过要留意编辑区内某些版本不会渲染出真正的h1而是用class来模拟标题样式。这时上面的代码就不够用了需要加上层级选择器备用/* 如果编辑区不使用 h1/h2 标签改走 class 方案 */ .heading-level-1, .heading1 { font-size: 28px; font-weight: 600; } .heading-level-2, .heading2 { font-size: 22px; font-weight: 600; }用哪一套类名取决于你安装的 ep_headings2 在aceAttribsToClasses里返回了什么映射。建议打开浏览器开发者工具选中一个带标题的段落看它实际挂载的类名再写 CSS。这样比盲目复制别人皮肤里的选择器可靠得多也是标题插件定制中最值得养成的一个习惯。样式定制时还要注意性能。编辑区里的 DOM 节点很多如果给标题写了过于复杂的 CSS 动画或者用了大阴影、滤镜低端电脑在长 pad 里滚动时会明显卡顿。尽量用font-size、border-bottom、padding这类低开销属性避免box-shadow和text-shadow大量使用。4.3 导出 HTML/PDF 时标题样式丢失的常规处理标题属性的价值最终要落到导出。如果你用 Etherpad 自带的导出接口可能会发现导出的 HTML 里标题结构并不完整。常见原因有两个一是 ep_headings2 的导出 hook 没有被当前版本的导出器调用二是你用的是第三方导出插件它读取的是编辑器 DOM而不是原始属性。这时我不建议去改插件源码而是先用 Etherpad API 导出一份 HTML再用文档转换工具处理curl http://localhost:9001/api/1/pad/export?idYOUR_PADformathtmlapikeyYOUR_APIKEY -o pad.html导出的pad.html如果能看到h1、h2说明 ep_headings2 的导出 hook 是好的如果导出的还是纯文本或普通段落可以再试导出.docx格式有些插件对内部属性和导出属性的映射不同。拿到结构正确的 HTML 后再交给 pandoc 转成 PDF 或 DOCX标题层级就能完整保留pandoc pad.html -o result.pdf --toc如果你只希望在线预览 PDF 而不追求文件也可以直接用浏览器的打印功能但需要先保证编辑区没有启用水印、行号等干扰元素。这个方案比在 Etherpad 里反复调导出插件更稳因为导出链路越短失控的中间环节越少。5. ep_headings2 避坑记录安装后没按钮、样式丢失、冲突等 5 个排查实例5.1 现象一安装完成后工具栏没有出现标题下拉第一次安装时我遇到最多的情况是插件显示已安装日志也注册成功但打开 pad 后工具栏里找不到标题选择器。原因通常是 Etherpad 的静态资源被浏览器缓存了或者进程没有真的重启。也可能是你在 settings.json 里自定义过toolbar把默认按钮组覆盖掉导致新插件的按钮没有入口。解决顺序是先强刷浏览器CtrlF5 强制刷新 pad 页面排除缓存。如果没变化就回后台 /admin/plugins 确认状态为 enabled。最后检查 settings.json 的toolbar配置确认标题按钮没有被排除。在我自己的部署里最后一条才是真正原因因为很多模板会在toolbar里手写一组按钮漏掉新增插件。5.2 现象二标题样式一会有一会没有多 pad 不一致有时候同一个浏览器里这个 pad 能看到标题样式另一个 pad 看不到换个浏览器又恢复了。多数情况是 CSS 和静态资源的缓存时间不一致有的 pad 在插件更新前打开过旧的pad.js或headings.css还在缓存里有的是走了 CDNCDN 节点间资源版本没同步。解决方式是在自定义 CSS 里不要使用内联样式或临时写在控制台的测试样式而是把样式固定到皮肤文件并在静态资源响应头里设置合理的max-age或ETag。如果只是临时验证就用无痕窗口开 pad因为无痕窗口不会带入本地缓存能看到最干净的加载结果。这个方法也是我判断“到底是代码问题还是缓存问题”的首选手段。5.3 现象三升级 Etherpad 后 ep_headings2 全部失效Etherpad 版本升级后插件失效非常常见。原因不一定是插件作者不维护了而是 Etherpad 内核的 hooks 名称或客户端初始化流程变了比如某个编辑器加载顺序调整后ep_headings2 的aceInitInnerdocbodyHead再也没有被调用。现象表现为插件还在列表里但工具栏按钮消失、标题样式变成普通文本。解决时不要急着卸载重装先查 Etherpad 的升级日志和当前版本对应的 hook 列表再去 ep_headings2 的 npm 页面看它声明支持的引擎范围。如果插件长期不更新可以考虑转向维护更活跃的同类标题插件。最好的预防办法是把 Etherpad 版本固定在某个小版本不要在团队协作期间随意执行跨大版本升级。5.4 现象四从 pad 复制标题到普通网页标签变成正文有些用户习惯把编辑区内容全选复制然后粘贴到公司文档系统结果标题层级全部丢失。这不是 bug而是 Etherpad 编辑器在设计上不承诺“复制即得 HTML 结构”它复制到剪贴板的内容更多是纯文本或带基础格式的富文本标题属性没有进入剪贴板渲染。解决墙上要靠导出。先从 pad 导出 HTML再将 HTML 粘贴到目标编辑器或者直接用目标系统的导入文件功能。如果目标系统支持 Markdown也可以用 ep_markdown 之类的插件先把 pad 转成 Markdown再复制标题的#标记。这个操作要多走一步但能避免在段落样式上返工。5.5 现象五与其它插件冲突导致编辑器白屏编辑器白屏通常不是 ep_headings2 单独的问题而是多个插件在客户端初始化阶段互相踩。比如两个插件都往aceInitInnerdocbodyHead里插入内容或者都在aceAttribsToClasses里注册同名属性最终导致 JS 异常。现象上pad 页面能打开但编辑区一直是空白或转圈。解决方式靠二分法先临时停用其它 ep_ 插件只保留 ep_headings2看白屏是否消失如果正常再逐个启用来找出冲突源。找到冲突插件后有两种处理一是放弃其中之一二是查看两个插件的 README 里有没有提到共存限制必要时在启动参数里调整加载顺序。这个排查方式不优雅但对付插件黑匣子最有效我每次遇到白屏都这么操作省下的时间足够重新编译一个插件。6. 一个贯穿始终的验证技巧用浏览器开发者工具检查标题属性不管你是刚装好 ep_headings2还是已经在线跑了一段时间最值得掌握的验证手段不是看按钮而是打开开发者工具直接查标题属性。编辑一个 pad选中某个标题行在控制台执行const lines document.querySelectorAll(#innerdocbody .ace-line); lines.forEach(line { const cls line.getAttribute(class) || ; if (cls.includes(heading) || cls.includes(h1) || cls.includes(h2)) { console.log(line.textContent.slice(0, 30), cls); } });这段脚本会把当前 pad 里所有被插件标记为标题的行打印出来。你能清楚看到哪些行带着标题类名哪些没有也能对比不同标题级别的类名规律进而写出更精准的自定义 CSS。把它当成例行检查比反复问“为什么我的标题不同”要快得多。再进一步我还习惯在验证后追加一个导出检查每次改完 CSS 或升级版本都用 API 导出一份 HTML然后 grep 一下导出文件里的标题标签grep -E h[1-6] pad.html | head -20如果导出文件里有完整的h1到h6说明属性到结构的链路是通的如果这里得到的是空结果那前端再好看也是假的。这套组合拳帮我避免过很多次“看着没问题交付后才发现标题全丢”的尴尬。最后说一个我这几年养成的习惯装完 ep_headings2不要去折腾过多神秘参数。先让它默认跑起来把标题层级、快捷键、导出这三件事验证一遍再考虑定制。插件本身的默认行为通常已经足够稳健真正出问题的时机大多出现在你试图“优化”它的时候。希望这套验证思路和踩坑记录能帮到你让你的 Etherpad 标题功能少一点玄学多一点可维护性。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑