dsh-document-mode · DSH 公文模式
让 DeepSeek Harness 像党政机关一样向你呈递公文:回复自动排成 A4 公文页,可圈阅批示,
并可一键导出为真正的 .docx。
本项目复刻 codex-document-mode(B站视频 《让Codex向你呈递公文并接受批示圈阅》)的三块能力,并在 DeepSeek Harness Web GUI 上实现 热启用 / 热禁用;公文版面依据 GB/T 9704—2012《党政机关公文格式》。
| 能力 | 实现 |
|---|---|
| ① 回复呈递为公文 | 作用域样式表 + MutationObserver,把助手回复行改造成 A4 公文页:发文机关标志(红头)、发文字号、红色分隔线、标题、主送机关、正文、发文机关署名、成文日期。不替换任何 React 组件。每轮只给最终答复套纸,思考链的中间步骤保持原样。 |
| ② 正式文风与申论语气 | 向 systemPrompt 注册动态段落:公文文风为基底,可叠加申论笔法(提出问题—分析原因—提出对策—总结升华)。关闭时返回空串,空段落不会被装配。 |
| ⑤ 中文思考(合并自 dsh-zh-thinking) | 再注册一个 systemPrompt 段落,约束内部思考使用简体中文并按【步骤 N|阶段名】组织,含 [调用]/[风险]/[修复] 事件标记与【结论】收束。只约束思考,不改回复内容。 |
| ③ 圈阅 | 输入框上方全宽圈阅栏内 240×80 绘制板:画圈 = 同意,画叉 = 不同意;识别成功写入 【反馈:同意】 / 【反馈:不同意】。栏可关闭(右上角 ×),工具栏「圈阅」按钮可重新展开,开合状态记在 localStorage。 |
| ④ 导出 DOCX | 工具栏「导出」按钮把最近一份公文导出为真正的 .docx(OOXML):A4、GB/T 9704 页边距、红头、发文字号、红色分隔线、标题、主送机关、正文、落款、表格一应俱全。纯前端生成,无第三方依赖、无需主机参与。 |
关于中文思考:本能力原为独立插件
dsh-zh-thinking,现已合并进本插件并统一受 公文模式总开关节制——公文模式开启即自动带中文思考,关闭即两者同时静默。 原插件目录可保留作备份,但不应再在 profile 中注册,否则思考规范会被注入两次。
一、版面依据 GB/T 9704—2012
基准取正文 3 号仿宋(16pt),故 1em = 16pt = 5.6444mm;全部几何量由标准毫米值换算为
em,页面随 --dsh-doc-scale 等比缩放(窄屏自动降档)。
| 标准要素 | 规定 | 本插件实现 |
|---|---|---|
| 幅面 | A4 210×297mm | width: min(37.2em, 100%)(=版心156 + 左28 + 右26) |
| 版心 | 156×225mm | 内容区宽度恒为 27.632em ≈ 156mm |
| 页边距 | 标准上 37、左 28mm;本渲染左右各 27mm(保证版心 156mm),上下按对话场景收敛 | padding: 1.8em 4.784em 3.4em 4.784em |
| 行数×字数 | 每面 22 行、每行 28 字 | line-height: 1.812(225mm÷22≈29pt;标准未规定固定磅值) |
| 正文 | 3 号仿宋 | font-size: calc(16pt * var(--dsh-doc-scale)) |
| 发文机关标志(红头) | 红色小标宋;标准为"上边缘至版心上边缘 35mm" | margin-top: 1.812em(收为空一行,理由见下) |
| 发文字号 | 3 号仿宋,红头下空二行,居中;年份用六角括号〔〕,序号不编虚位 | ::before(节点锚点),margin-top: 3.624em;格式 代字〔年份〕N号,N 取该回复的会话轮次,故依次为 1号、2号、3号…… |
| 红色分隔线 | 发文字号下 4mm,与版心等宽 | padding-bottom: .7087em + border-bottom: 2px solid var(--dsh-doc-red) |
| 标题 | 2 号小标宋,红线下空二行,居中 | ::before(正文容器),font-size: 1.375em,margin-top: 3.624em |
| 主送机关 | 3 号仿宋,标题下空一行,居左顶格,末尾全角冒号 | ::after(正文容器,用 order 提到正文之前) |
| 正文 | 每自然段左空二字,回行顶格 | p { text-indent: 2em } |
| 层次序数 | 一级黑体、二级楷体、三/四级仿宋 | 段首行内序数按 data-doc-level 套字体;markdown 标题按 h1/h2→黑体、h3→楷体、h4-h6→仿宋 |
| 发文机关署名/成文日期 | 阿拉伯数字标全、年份全称、月日不编虚位;一般右空四字 | ::after(行级),padding-right: 4em,2026年10月11日 |
| 文字颜色 | 均为黑色 | 卡片 color: #000 |
未实现的标准要素:页码、份号、密级和保密期限、紧急程度、签发人、附件说明、附注、抄送、 印发机关与日期——这些要素在无分页的对话流中没有对应物,强行渲染反而失真。
一处刻意的版面收敛:标准规定红头上边缘距版心上边缘 35mm,该空间是给上述 份号、密级和保密期限、紧急程度三行预留的。这三项在对话场景不适用且未渲染,若照搬, 叠加页面上白边后每页顶部会出现约 72mm 的空白。故本渲染把版头区收敛为空一行, 并把上下纸边压到必要值——这是省略不适用要素的结果,不是对红头字体的改动。
两处已排除的常见误传:
- 红头 25mm/80mm 属 GB/T 9704—1999 旧规,2012 版统一为 35mm;
- 成文日期用汉字、"零"写为"○"属 2012 年 7 月前后过渡期说法,2012 版已改为阿拉伯数字。
二、字体
本机已装入完整的公文字体族(来源:下载/796f80c8-….zip 字体包,用户级安装于
~/.local/share/fonts/gongwen/):
| 用途 | 字体栈 | 本机实际命中 |
|---|---|---|
| 发文机关标志、标题 | 方正小标宋简体, 方正小标宋_GBK, FZXiaoBiaoSong-B05S, STZhongsong, 华文中宋, SimSun |
方正小标宋简体 |
| 正文、发文字号、主送机关、署名 | 仿宋, 仿宋_GB2312, FangSong, FangSong_GB2312, 方正仿宋_GBK, 方正仿宋简体 |
仿宋(仿真仿宋,非替代字体) |
| 一级层次序数 | 黑体, SimHei, 方正黑体简体, 方正黑体_GBK |
黑体 |
| 二级层次序数 | 楷体, 楷体_GB2312, KaiTi, KaiTi_GB2312, 方正楷体简体, 方正楷体_GBK |
楷体 |
早期版本在本机缺字体时曾用开源替代字体(朱雀仿宋、霞鹜文楷)。现已全部移除, 改用真正的公文字体族。
国标规定的字体与字号(本实现对照)
GB/T 9704—2012 只规定中文号数,不含磅值;下表磅值为通用印刷换算(二号 22pt、三号 16pt、
四号 14pt),实现中以三号 16pt 为基准,故 1em = 16pt = 5.6444mm。
| 要素 | 国标字体 | 国标字号 | 本实现字体 | 本实现字号 |
|---|---|---|---|---|
| 发文机关标志(红头) | 红色小标宋 | 标准未规定,由机关按"醒目庄重"自定 | 方正小标宋简体 | 26pt(一号,本实现自选) |
| 发文字号 | 仿宋 | 三号 | 仿宋 | 16pt |
| 标题 | 小标宋 | 二号 | 方正小标宋简体 | 22pt |
| 主送机关 | 仿宋 | 三号 | 仿宋 | 16pt |
| 正文 | 仿宋 | 三号 | 仿宋 | 16pt |
一级层次序数(一、) |
黑体 | 三号 | 黑体 | 16pt |
二级层次序数((一)) |
楷体 | 三号 | 楷体 | 16pt |
三级(1.)/四级((1)) |
仿宋 | 三号 | 仿宋 | 16pt |
| 发文机关署名 | 仿宋 | 三号 | 仿宋 | 16pt |
| 成文日期 | 仿宋 | 三号 | 仿宋 | 16pt |
| 页码 | 宋体 | 四号 | —(无分页,未实现) | — |
两处与标准的取舍已在正文说明:红头字号标准未定,本实现取一号;页码在无分页的对话流中 没有对应物,未实现。
字体栈为何以标准名打头
Linux 的 fontconfig 在找不到某个族名时,会把该名字悄悄替换成 Noto Sans CJK SC(黑体)。 因此字体栈必须写标准族名、且本机确实装有这些字体;否则"font-family 写了却不生效"。
本机已装齐仿宋、仿宋_GB2312、黑体、楷体、楷体_GB2312、方正小标宋简体,故栈序直接以
标准名打头。换到未装公文字体的机器前,请先 fc-match 仿宋 确认命中,否则正文会显示
成黑体。
已安装的字体
~/.local/share/fonts/gongwen/仿宋.ttf
~/.local/share/fonts/gongwen/仿宋_GB2312.ttf
~/.local/share/fonts/gongwen/黑体.ttf
~/.local/share/fonts/gongwen/楷体.ttf
~/.local/share/fonts/gongwen/楷体_GB2312.ttf
~/.local/share/fonts/方正小标宋简体.ttf
均为用户级安装(无需 root),安装后已执行 fc-cache -f。
需要重启浏览器:Firefox / Chrome 在启动时缓存系统字体清单,新装字体要重启浏览器 才会被识别(DSH 服务端不必重启)。
字体授权由使用者自行负责:商用字体请确保取得合法授权。
三、热开关
三处入口绑定同一份实时状态:
- 输入框工具栏按钮(
conversation.input.left,文字「公文」) - 设置 → 通用 → 公文模式(
settings.general.item:发文机关、主送机关、机关代字、发文字号、 圈阅后自动发送、显示题头、显示落款) - 多标签页同步:主机侧 SSE 广播
切换路径:浏览器立即更新 html[data-dsh-doc-mode] → POST /plugins/dsh-document-mode/config
→ 所有字段均为 schemastery .volatile(),Loader 就地提交(只发 loader/volatile-update,
不重挂插件)→ 值写回当前 profile 的 Cordis patch,重启后保持。
圈阅栏仅在本模式开启时渲染;关闭模式即整条消失。
四、安装
从 GitHub 安装(推荐)
dsh plugin --profile web add github:GOhaochen/dsh-document-mode
本地开发安装
git clone https://github.com/GOhaochen/dsh-document-mode.git
dsh plugin --profile web add link:$(pwd)/dsh-document-mode
dsh plugin add 会同时写入 profile 的 dependencies 与 dsh.profile.bundles,并应用本包的
cordis.patch.yml(insert 一行插件)。
关于
link:协议:pnpm 在link:下不解析 peer 依赖,若裸模块解析报错,可在包内建node_modules/@deepseek-ai/{schemastery,cordis,cosmokit}软链指向 profile 的同名包。github:安装无此问题。
安装后需让 DSH 重组一次插件树:重启一次 dsh web,或在设置 → 插件里切换任意插件开关。
热重载范围:客户端 bundle(lib/client.js)由 dsh-client-hmr 轮询,改动约 1 秒内热替换,
无需刷新;主机侧代码(lib/index.js)不在 HMR 根内,改动配置字段后需重启一次 dsh web。
五、配置项
| 字段 | 默认 | 含义 |
|---|---|---|
enabled |
true |
总开关(热切换) |
signature |
DeepSeek Harness |
发文机关标志与署名的机关全称/规范化简称 |
salutation |
尊敬的领导: |
主送机关,末尾用全角冒号 |
issueCode |
DSH |
发文机关代字,用于自动生成发文字号 |
issueNumber |
'' |
发文字号;留空则按「代字〔年份〕1号」自动生成 |
autoSend |
true |
圈阅识别成功后自动发送(仅当草稿为空) |
showHeader |
true |
显示红头与发文字号、红色分隔线 |
showSignature |
true |
显示发文机关署名与成文日期 |
essayTone |
true |
申论语气:按“提出问题—分析原因—提出对策—总结升华”组织回复 |
thinkingZh |
true |
中文思考:思考过程用简体中文,并按【步骤 N|阶段名】组织 |
六、端点(均只允许本机回环)
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /plugins/dsh-document-mode/config |
读取当前配置 |
| POST | /plugins/dsh-document-mode/config |
合并一个配置补丁 |
| GET | /plugins/dsh-document-mode/events |
SSE 状态广播 |
七、与主题 / 皮肤 / 壁纸的隔离
公文纸恒为"白纸黑字",而 DSH 官方 markdown 组件(MarkdownText、CodeBlock)取色一律走
--dsw-alias-label-* 与 --dsw-alias-markdown-* 令牌。当应用处于暗色主题、或安装了
会重映射令牌的皮肤(例如 blue-fantasy 在 body[data-ds-dark-theme] 下把
--dsw-alias-label-primary 改为 #dbe2f2、把 --dsw-alias-markdown-inline-code 改为
#1e27409e)时,浅色文字会落在白纸上近乎不可见,深色半透明底纹会变成灰块——表现为
"白字 + 半透明轮廓"。
本插件据此在公文页子树内就地重声明这批令牌(正文黑、代码类浅灰、边框与遮罩中性化),
并追加 -webkit-text-fill-color: currentColor 与 text-shadow: none 兜底。这样公文页
自带完整配色,任何主题、皮肤、壁纸下都保持标准要求的白纸黑字,无需停用你的主题插件。
令牌覆盖之外的兜底:!important 白纸黑字归位
实测发现仍有插件会在正文块上留下运行时生成的底色(静态样式表里检索不到),令牌覆盖 压不住,表现为每个段落各自带一块灰色圆角底。故在公文页子树内再加一层强制归位:
/* 正文块一律无底色、黑字、无阴影 */
html[data-dsh-doc-mode="on"] … :is(p, li, blockquote, h1…h6, table, span, strong, em, div…) {
background-color: transparent !important;
background-image: none !important;
box-shadow: none !important;
text-shadow: none !important;
backdrop-filter: none !important;
}
/* 代码块与行内代码再单独恢复浅灰底,保持可读 */
html[data-dsh-doc-mode="on"] … pre,
html[data-dsh-doc-mode="on"] … :not(pre) > code { background-color: #f4f4f4 !important; }
这一层与具体插件无关:无论底色来自主题、皮肤还是外观类插件,公文页都只会是白纸黑字。
关于"启用公文模式时自动停用某插件":技术上可行——主机侧
plugin-manager的setPluginEnabled(id, enabled)会把disabled覆盖写入 profile 的cordis.patch.yml, 配合 HMR 即时重组。但此处不建议:皮肤/主题是全局外观,停用它会连带改变整个界面, 且每次切换都要改写 profile 并触发重组,成本与风险都高。令牌覆盖 +!important归位 是局部、即时且与皮肤无关的正解。若确有需要,可作为可选配置另加。
标题:由"事由 + 文种"合成,文种按内容推断
标准 §7.3.1 要求标题为"发文机关 + 关于 + 事由 + 文种"。原实现直接取回复首句,会出现 "关于收到。字体安装一事收束。本次共完成四项……"这类病句与被截断的括号。现按优先级取事由:
- 回复内的标题元素(
h1–h6); - 会话标题——它代表整场对话的题旨,比截断首句更接近"事由";
- 回复首句,剥掉"收到/好的/明白"等口语开场,过长时退到第一个逗号分句;
- 兜底"工作情况 + 文种"。
文种不写死为"报告",由 detectGenre() 按《党政机关公文处理工作条例》的法定文种与
公文惯用收束语推断:
| 回复特征 | 文种 |
|---|---|
妥否,请批示/请予批准/请审批 |
请示 |
此复/现批复如下 |
批复 |
特此通知/通知如下/请遵照执行 |
通知 |
特此函告/敬请函复/请予支持 |
函 |
特此通报/通报如下 |
通报 |
会议纪要/纪要如下 |
纪要 |
提出如下意见/实施意见 |
意见 |
| 其余(陈述事项、汇报进展) | 报告 |
因此标题会随内容变化,例如"关于事项审批的请示"、"关于来函答复的批复"、 "关于工作安排的通知",默认情况下仍是"关于……的报告"。事由已以"关于"开头时只补文种; 事由本身已以文种收尾时原样采用,避免"关于……的报告的报告"。
八、导出 DOCX
工具栏「导出」按钮把最近一份公文导出为真正的 .docx(Office Open XML)。生成在
浏览器内完成,不需要主机接口、不需要额外依赖,也不触发重启。
为什么不用主机侧转换
本机虽装有 LibreOffice,但主机侧转换要新增端点、多一次进程调用,且必须重启 DSH 才生效。 改为前端直接产出 OOXML:即时、离线可用、也不受部署环境影响。
产出内容
| 部分 | 实现 |
|---|---|
| 页面 | w:pgSz A4 11906×16838 twips;w:pgMar 上 2098 / 右 1474 / 下 1984 / 左 1587 twips(37/26/35/28 mm) |
| 行距 | w:line="580" w:lineRule="exact"(29pt 固定值);w:docGrid w:linePitch="580" |
| 红头 | 红色(E60012)方正小标宋简体,居中 |
| 发文字号 | 3 号仿宋,居中,格式 代字〔年份〕N号 |
| 红色分隔线 | 段落底边框 w:pBdr/w:bottom,与版心等宽 |
| 标题 | 2 号方正小标宋简体,居中 |
| 主送机关 | 3 号仿宋,顶格 |
| 正文 | 3 号仿宋,w:firstLineChars="200"(首行缩进 2 字),两端对齐 |
| 层次序数 | h1/h2 黑体、h3 楷体、h4–h6 仿宋加粗 |
| 列表/代码/表格 | 列表加项目符号;代码块用 Consolas;表格转为带细边框的 w:tbl |
| 落款 | 3 号仿宋,w:rightChars="400"(右空 4 字),阿拉伯数字成文日期 |
字体:导出件写入标准公文字体名
导出件直接写标准族名,本机 Word / LibreOffice 打开即所见即所得,在中文版 Windows 与 WPS 上同样可用:
| 用途 | 写入 .docx 的族名 |
|---|---|
| 正文、发文字号、主送机关、署名的仿宋 | 仿宋 |
| 红头与标题的小标宋 | 方正小标宋简体 |
| 一级层次序数的黑体 | 黑体 |
| 二级层次序数的楷体 | 楷体 |
若需更严格地贴合标准名,可把 DOCX_FONT.fangsong 改为 仿宋_GB2312、kaiti 改为
楷体_GB2312(两者本机均已安装)。
技术实现
.docx 本质是一个 ZIP,含 [Content_Types].xml、_rels/.rels、
word/_rels/document.xml.rels、word/styles.xml、word/document.xml。本插件用
**STORE(不压缩)**方式自建 ZIP 容器(自带 CRC32 表与中央目录),因此不依赖
CompressionStream,在任意浏览器上都能用;单份文档约 15–70 KB。
验证
node test/docx.mjs /tmp/sample.docx # 用真实发行包生成样例
unzip -t /tmp/sample.docx # 容器完整性
file /tmp/sample.docx # 应为 "Microsoft Word 2007+"
soffice --headless --convert-to txt --outdir /tmp /tmp/sample.docx # 解析验证
实测:unzip -t 无错误,file 识别为 Microsoft Word 2007+,LibreOffice 可正常解析并
导出全文;页边距、行距、首行缩进、右空四字、红头色值、公文字体名均正确写入。
九、实现要点
- DOM 锚点全部使用 DSH 官方稳定契约,不依赖 CSS-module 哈希类名。
- 伪元素分工:行级
::before/::after承担红头与落款;槽位锚点承载发文字号与红色分隔线; 正文容器承载标题与主送机关。三者都是纵向弹性盒,用order把::after提到正文之前—— 这是同一元素只能有一个::before时的必要技巧。 - 样式表带
data-plugin="dsh-document-mode",HMR 卸载时由模块系统回收。 - 公文纸门控在
data-doc-paper上,而该属性只由decorateAll()打在「每轮最后一个节点」上。 官方用kind === 'assistant-step' && step === answerStep判定最终答复,意味着思考链的每个 分步也是assistant-step;若按 kind 直接套版面,每个思考步骤都会变成一整张红头白纸。 故按data-chat-turn分组,只保留每轮 DOM 顺序里的最后一个节点,其余节点用clearDecoration()撤掉装饰。 - 失败降级:主机侧挂载动作全部包在 try/catch 内,任一服务缺失时插件保持惰性。
- 代码块、命令、日志、表格在公文页内仍保持等宽原格式。
十、验证
node test/verify.mjs
103 项检查,覆盖:bundle 以经典脚本求值、模块 id 与包名一致、inject/apply 形状、根属性、
样式表注入与 data-plugin 标记、选择器作用域无泄漏、四处 attr() 装饰、
display:contents 提升为弹性盒、14 项 GB/T 9704—2012 版面参数(幅面/页边距/字号/行距/
红头 35mm/分隔线 4mm/标题空二行/主送机关空一行/首行缩进 2 字/右空四字/字体栈)、
4 项公文要素内容规则(红头仅机关名、发文字号六角括号且不编虚位、阿拉伯数字日期、
汉字日期已移除)、7 项层次序数分层规则(一、/(一)/1./(1)→ 黑体/楷体/仿宋,
且序数与正文同段时不误涂),圈阅画板尺寸与槽位注册数量,以及从发行源码中提取的
圈阅分类器。
十一、已知边界
- 圈阅使用笔画几何启发式(质心半径比 + 角覆盖 + 过心次数),非机器学习模型;信心不足时 不写入任何内容,避免误判。
- 公文标题按"事由 + 文种"合成(详见第七节)。事由优先取回复标题,其次取会话标题,再次取 回复首句;因此同一会话内各条回复的标题通常相同——这符合"一场对话即一份公文"的语义, 但若你希望每条回复各自成题,可调整优先级。GB/T 9704 规定的"梯形/菱形"回行排布仍由 浏览器断行决定,未做人工断行。
- 发文机关署名与成文日期统一按"右空四字"右对齐;标准 §7.3.5.2 针对不加盖印章公文另有 "日期首字比署名首字右移二字"的弹性算法,需实测文本宽度,未实现。
- 层次序数只在整段为标题行(序数之后无句末标点)时套用对应字体。若写成 "一、总体要求。坚持稳中求进……"这类序数与正文同段的形式,整段仍是仿宋——CSS 无法只给 行首几个字换字体,这是浏览器渲染的硬限制。
十二、变更记录
v0.2.0(2026-11-07)
- 合并
dsh-zh-thinking:中文思考规范并入本插件,作为第二个systemPrompt段落 (dsh-zh-thinking:reasoning-style,order 450,沿用原标识),随公文模式统一启停。 - 新增申论语气:
renderStyleGuide()追加「提出问题—分析原因—提出对策—总结升华」行文脉络, 并附不改写事实的护栏。 - 新增配置项:
thinkingZh、essayTone,可在设置面板独立关闭。 - 新增测试:
test/host.mjs,29 项主机半断言(配置投影、文本渲染、section 注册与开关联动)。 - 原
dsh-zh-thinking插件应从 profile 中移除,避免思考规范重复注入。
v0.1.0(2026-10-26)
- 首次发布:GB/T 9704—2012 公文版面、正式文风约束、圈阅反馈、DOCX 导出、 热启用/热禁用。
免责声明
- 本项目为个人技术实践,与 DeepSeek、OpenAI、任何党政机关均无关联,亦非官方产品。
- 生成的公文仅为格式演示,不具备任何法定效力,不得用于伪造真实公文或冒充机关行文。
- 版式依据 GB/T 9704—2012 公开标准,但属于近似实现,不保证与正式排版软件逐像素一致。
- 字体授权由使用者自行负责:公文常用字体(方正小标宋、仿宋、黑体、楷体等)多为商业 字体,请确保已取得合法授权后再安装使用。
许可
本项目采用 GNU General Public License v3.0 许可,全文见 LICENSE。
Copyright (C) 2026 GOhaochen
本程序是自由软件:你可以依据自由软件基金会发布的 GNU 通用公共许可证(第 3 版或你选择的 任何更高版本)条款重新发布和/或修改它。本程序基于"有用"的目的发布,但不提供任何担保, 甚至不包含适销性或特定用途适用性的默示担保。详见 GNU 通用公共许可证。
注:仓库内
LICENSE为 GPLv3 标准全文;package.json中 SPDX 标识记为GPL-3.0-only。若你希望采用"第 3 版或更高版本"的表述,把该字段改为GPL-3.0-or-later并在源码头部补充相应声明即可。
No comments yet. Be the first to write one.