OneNote Word 自动编号往返(v3:Word COM + Ctrl+V,三个时序修复)
把 OneNote 单页(含图片 / 表格 / 多级编号)→ 渲染成 Word docx(w:numPr 真多级
自动编号,图片作为无编号正文、左缩进对齐上一级不打断续号)→ Word 全选复制 → OneNote 新页 Ctrl+V 粘贴
→ OneNote 转换成它自己的列表结构(活的自动编号:Enter 续号、Tab 切层级)。
本 skill 中文名:OneNote Word 自动编号往返(目录名仍用 onenote-word-roundtrip
英文;frontmatter name 字段为中文;WorkBuddy skill 列表显示中文名)。
为什么需要这个 skill
OneNote(2016/365 桌面端)没有 Word 那种"按 Enter 自动续号 + 删段自动重排"的
多级自动编号。直接用 OneNote COM 写的编号是死的文字前缀(编辑时不会续号)。
但 OneNote 粘贴 Word 多级列表时会把它转换成自己的列表结构——粘完后用户按
Enter 自动续号、Tab 切层级。这个 skill 就是把"渲染 Word → 粘回 OneNote"全自动化。
v3 三个关键时序修复(血泪史)
这条路 v1 时灵时不灵(5 页全空、桌面十几个 Word 残留),根因是三个时序问题:
- 复制后 Word 必须保持活着(Word 剪贴板是延迟渲染 delayed rendering,进程
一退出剪贴板数据就失效)。v1 后期加的EmptyClipboard()+ 立即Quit()是
粘空页的真实原因(当时误诊为 Win10/11 前台劫持)。v3:复制后只最小化
Word(word.WindowState = 2),粘贴验证后才Quit()+taskkill兜底。 - Word 文档窗口开在前台会截胡 Ctrl+V(快捷键发给 Word 自己而不是 OneNote)。
v3:复制完成立即最小化 Word 窗口 → OneNote 拿前台 → Ctrl+V 稳定。 - 粘贴完成判定要轮询(图片多的页粘贴耗时 10s+;OneNote 窗口可能被用户
当前应用如浏览器抢走前台)。v3:轮询读回(4 次 × 3s)→ 失败则检查前台窗口
是不是 OneNote(不是就重新激活)→ 重发 Ctrl+V(最多 2 次)。
工作流(8 步)
- 抽取 OneNote 单页:
onenote_com.get_page_content(page_id, page_info=1)
(piAll=1拿含图 base64 的 XML),遍历所有非优化版 Outline(按文档顺序
拼接——v4 修复:原来只取最大 Outline,多 Outline 页会丢整块内容),递归嵌套
OEChildren 记录 OneNote 原生缩进深度 indent + 检测原始加粗(orig_bold):
文本 OE → text 项;含 Image OE → image 项(带database64);含 Table OE →
table 项。 - 智能层级 + 编号:
text_rules.renumber_with_window(raw)做"父级窗口"层级
推断 + 1./1.1./1.1.1. 重排 + no_number 防伪序号 + 子枚举(方式1/方式2…)归位
到父级下 + 标题子级模式(标题后的(1)/①/方式1/1、等统一挂为子级;
详见 onenote-direct-edit 的 SKILL.md 坑点 11/12/13)。
页面使用 OneNote 原生缩进时(任一 text 项 indent>0),追加
apply_indent_levels():缩进深度重建层级——编号项 level=indent+推断层级,
原加粗项 level=indent,普通正文 level=indent+1,逐级钳制(>+1 收到 prev+1),
上限 4 级。适合"第X章/第X节/一、二、+ 缩进正文"型学习笔记页。 - 渲染 docx:
build_list_docx_with_images.py(fork 自 onenote skill 的
build_list_docx.py,多支持 image 字段项——渲染为 1×1 表格 + cell 内嵌图片,
不打断 w:numPr 续号)。渲染规范:中文宋体 + 西文 Times New Roman + 18pt +
单倍行距 + 统一 0.25″ 缩进 + w:numPr 多级自动编号。
加粗规则(用户 2026-08-23 新规):先看整页文本层级最大值——
页面 ≤3 级标题(max level ≤2)→ 任何标题都不加粗;页面 >3 级标题
(max level ≥3)→ 仅加粗一级(level 0,最前面的大标题)。同一层级
全体同待遇。is_title标记已由 roundtrip 上游判定好,build 端直接信任、
不再用 level 二次门控(避免越权加粗)。
源页颜色 / 源加粗 / 高亮(真实色)保留(v1.1 强化):逐 run 透传 one:T 里的
color(重点标注红#FF0000/蓝/绿等)、源font-weight:bold与荧光高亮
(用户原页用荧光笔标的重点,如"有关物质…"页(3)标准曲线法整段标黄)。
优化版不再丢失用户原页做的重点颜色/高亮标注,且 v1.1 起高亮按真实颜色落位
(绿→green、黄→yellow、蓝→blue;Word 命名高亮不支持的自定义色改用
w:shd底纹填充对应 hex,不再一律强行变黄)。build 端make_run_with_color
经_apply_highlight映射;_drop_prefix跨 run 剥离手动编号前缀时连同
highlight一起透传。
全量剥离手动编号 + 项目符号(2026-08-23 新增):源页行首的手动编号
(1.1.1.1)1、(1)(一)①②③a)等)与项目符号
(•·○以及行首-/*后接空白)一律渲染前剥掉——自动编号接管
序号,用户不必再手动删旧号。OneNote 常把(1)拆成(/1/)…多
run,build 端改为「拼接所有 run → 算前缀总长 → 跨 run 消费」彻底清干净。 - Word 打开 + 复制 + 最小化:
DispatchEx("Word.Application")Visible=True →
Documents.Open(ReadOnly=True)→Selection.WholeStory()→Selection.Copy()
→word.WindowState = 2最小化(进程活着 = 剪贴板延迟渲染有效;窗口
最小化 = 不挡 OneNote 拿前台)。 - OneNote 建新页:
CreateNewPage(section_id)拿 ID →build_page_xml写
标题 →update_page。幂等:跑前删同分区下<原页>_优化版旧页。 - 切页 + 激活 + Ctrl+V:
refresh_ui(new_page_id, section_id)切到新页 →
win32gui.EnumWindows找标题含新页名的 OneNote 窗口(类名 OneNote/CFrame)
→SetForegroundWindow(带AllowSetForegroundWindow借权限)→
pywinauto.keyboard.send_keys("^v")(内部处理焦点注入,比 keybd_event 稳)。 - 轮询验证 + 重试:读回新页文本(
page_to_text),>2 行即成功;否则每 3s
读一次共 4 次 → 仍失败则检查前台窗口(非 OneNote 就重新激活)重发 Ctrl+V
(最多 2 轮)。此时 Word 还开着,剪贴板仍有效。 - 粘贴完成后才关 Word:
doc.Close(SaveChanges=False)→word.Quit()→
taskkill /F /IM WINWORD.EXE兜底强杀(防"是否保留"对话框挂起导致残留)。
V15 结构化页面生成器(v1.2.9 改名 drive_v15.py;v1.2.10 修 emoji 标题丢编号;v1.2.11 生成后自动终刷目录;v1.2.12 批量能力并入,批量不再依赖 roundtrip.py;v1.2.13 修白色高亮误转白字——白底白字整段隐身,表现即"大片空行/行内空洞",根因为 highlight 兜底成 color 时未排除白色;v1.2.14 ①生成前"源页体检"(双读比对防 COM 缓存"半份"假象——曾致图片 8→0 全丢且保真假合格 0→0)②refresh_toc.py 入口 _结合版_v14→_结合版_v15 残留修复(手动刷目录不再落空)③"以下为脚本生成说明"分隔线与正文间距 60→120pt;v1.2.15 ①自动剥离源页残留的旧"脚本生成说明"块(从其分隔线行到页尾)——否则新页出现多条分隔线,120pt 间距落在残留块与新页脚之间的错误位置而非"正文→分隔线"处;剥离必须放在指纹补全层(flatten_table_images)之后,否则剥掉的行被补全层当缺失项重新插回(实测剥 17 条→补回 17 条白剥;strip_old_toc_items 同理顺带后移);划线判据须含 ━(U+2501);②保真归一化 _FID_LEAD 尾部分隔符补全角/半角冒号——2.2.3:标题 行转活列表后序号不在文本里,旧正则剥不掉 : 致保真假阳性"丢 1";③保真核对输出丢失行明细(最多 5 条,配合"逐项追问"审计习惯);v1.2.16 中文序号洗稿——行首 一、/十二、 前缀洗掉转活列表(定级 L1,用户拍板:目录"1. 一、核心矛盾"双号叠加难看);同版①行首装饰符号(emoji 等)跳过识别但保留(💼一、核心 照样洗稿定级——emoji 挡行首曾使全部行首锚定序号识别失效)②strip_prefix_from_runs 改 _split_number_token 精确区间删除(旧"首尾字符差"法会吞掉序号前方的 emoji)③保真归一化对称剥行首 emoji+中文序号。注:生成页内容本身仍按 v4 以来 strip_emoji 既有规则不含 emoji(源页标题 emoji 装饰不进生成页,如需保留须单独立规);v1.2.17 数据行防误判——N.N/N.N.N 行首序号后紧跟运算符或 ASCII 字母(13.56×2=27.12、13.56MHz)判为数据行:不定级、不洗稿、不进目录(实证 ICP仪器原理 页射频频率换算行被误判 L2 且 13.56 前缀被洗掉;depth_signal 与 match_level 双入口同修,真标题 2.1 单点法 点后空格/CJK 不受影响);v1.2.18 OneNote 摄入吞字符修复(安捷伦/HPLC 页批量实测)——①OneNote UpdatePageContent 对 CDATA 实体先解码再标签扫描:&lt;1000</span> 解码成 <1000</span> 被当伪标签整段吞掉(&lt;621&gt; 同理,<621> 通则记号曾静默丢失且保真假通过);解法=&lt; 独占 span 末尾(解码后 < 紧跟 </span> 的 <,判为数据),&gt; 无需处理;②run 级"去行尾>"清洗改为仅对整行最后一个 run 生效(run 边界≠行尾,>>>> 曾被吃成 >>>);③保真归一化先剥标签后解码 + 半角括号对称归一(FMEA) 洗稿补全不再计丢失);④凡 v1.2.17 及更早版本生成、正文含 </> 记号的页面须用本版重跑修复);v1.2.19 加粗解析修复(roundtrip.py _SpanParser)——OneNote 序列化会在 style 属性内部插硬换行(font-weight:\r\nbold,仪器校验页实测),旧判定只去空格漏掉换行变体致加粗丢失;改为 re.sub(r"\s+","",style.lower()) 全空白压缩,_parse_styles 颜色/高亮解析同样加固;另:bash 向 python -c 传含 \r\n 的测试串会被 MSYS 路径转换污染成 /r/n,单测须用真实 COM 数据或写临时脚本文件);v1.2.20 前导符号跳过收窄——_LEAD_SYM 从"非\w 字符"收窄为"非 ASCII 字符"(emoji/★●• 等):ASCII 运算符 = + - > < 绝不能跳,否则 =33.45(铜原子量得数行)会被跳过 = 后按 N.N 误判成二级标题、33.45 被当序号洗掉(ICP-MS 页实测数字丢失且进目录成"1.1 =")。v1.2.16~1.2.19 生成的页面凡含"运算符+数字"行均有此损伤,须用本版重跑);v1.2.21 原位重建——drive_v15.py "X_结合版_v15" [分区] 允许以优化页自身为源、同名覆盖(防 _结合版_v15_结合版_v15 双层后缀;早删推迟到抽取完成之后防"先删后读"丢内容;strip_old_toc_items/strip_old_footer_block 已有逻辑自动剥旧目录块与旧页脚,用户手动调整随抽取保留——L1 编号项无条件补加粗,L2+ 尊重原加粗状态);同版页脚「生成说明」标题补写规则集版本号(v1.2.19 起页脚丢版本号违反"页脚必写规则集版本+运行时间"约定,本版起恢复);v1.2.22 新增 scripts/hydrate_and_rerun.py——云端 CallbackID 图片懒加载治理:OneNote 只给"视口可见"的云端图下载二进制,COM 抽取时未拉全的图整个缺失(保真假性合格);工具两模式 audit [分区](盘点源页真总数 vs 生成页差异 + 手调检测,有手调的页自动排除重跑)/ batch [分区](前台化 OneNote → 导航源页 → Ctrl+End 跳页尾强制全页渲染 → 轮询 piAll Data 数追平 piBasic 元素数 → 重跑丢失页);同场实测:piAll 连续读图片数 2→3→4 递增(懒加载进行中)、单次最多 4 轮未追平的页留待再次 batch(每轮 1→2 张逐张进);解释器须带 comtypes/pywin32/pywinauto;v1.2.23 裸数字兜底规则收窄(HPLC维护保养页实测的文本损伤修复)——_split_number_token 末条兜底原为 ^\d+[A-Za-z]?\s*,不要求任何分隔符,带上 OneNote 标题样式的行首仪器型号数字被当手写序号吃掉:2695报错:lost prime→报错:lost prime、2487/2489开机报错…→/2489开机报错…(只吃前半截留孤零零 /)、2414开机报错…→开机报错…,实测 14 行受损(型号行均带 h2 样式,走「样式行→strip_prefix_from_runs」分支)。收窄为两条约束:① (?=\s|$) 数字后须紧跟空白或行尾(与既有约定「序号与标题间留空格/顿号/冒号」一致,粘连如 1实验样品的制备 本就不可消歧不洗);② 裸数字限 \d{1,2} 1~2 位——2695 开机设置流速…(数字后带空格)靠①挡不住、靠②挡得住,3 位以上行首数字几乎必是型号/量值(2695/2487/2489/2414、515/717/996 型号、1000 量值)。代价(已接受):100 项检查 这类 3 位裸序号标题不再自动洗号,会与自动编号叠加双号,源页改用 100、 即可正常洗稿。方案(高亮语义分离)未在本版处理,留待单独拍板。v1.2.24 三项修复(用户拍板"全部改吧")——①高亮语义分离(缺陷 2):源页的荧光笔高亮(序列化为 background:<色>;mso-highlight:<色>)被旧版 hl = color or highlight 合并成单通道、再一律写成 color: → 黄底变黄字=白底黄字≈整段隐身(HPLC维护保养页「反梯度补偿技术」两段实测,5 个 run 全丢)。改为分两路:新增 _highlight_style(v)(#rrggbb → background-color:;色名 → background:<c>;mso-highlight:<c>,与 OneNote 自身序列化同形最稳),_span_style(kind, bold, color, highlight, size_pt) 颜色走 color:、高亮走 background:;白色(color/highlight 两侧)仍一律置空(承 v1.2.13);text_to_xhtml(hl=…) 的 hl 语义收窄为文字颜色。全库影响面 34/135 页含荧光高亮,须用本版重跑;②样式模式数据行守卫(v1.2.17 残留缺口):v1.2.17 的 _is_data_line 守卫只加在无样式分支(depth_signal 入口),样式分支(correct_v5 里 if slv:)无条件洗号 → 带标题样式的 13.56×2=27.12 行首 13.56 照样被吃。样式行前置放行:if slv and _is_data_line(o): level=last_num_lv; numbered=False; continue(不定级/不编号/不洗号,按正文跟随最近编号层级)。全库扫描显示存量源页该场景 0 例(ICP 13.56×2 行所在页 样式表={} 无标题样式,v1.2.17 已覆盖)→ 本项为纯防御性修复、零存量影响、零回归风险;③保真报告误报治理:v1.2.23 批跑 2 页报"丢字"实为归一化未覆盖的假阳性,三项治理——(a) 公式行不参与文本逐行比对(源页公式是 MathML 壳,orig_text() 展平字符序与生成页回写形态不同,实测多肽页源侧 =A×Vε×m *1000 vs 生成侧 =(A×V)/(ε×m)*1000 其实生成侧才对),改由「公式 N → N」计数单独核对,页脚注明"另有 N 条公式行按公式计数核对";(b) _fid_norm 补全角句点 .(U+FF0E) 与全角数字(源页写 4.职业暴露限值…,旧式只吃 4 留 . → 假丢失);(c) _fid_norm 尾部叠 strip_emoji(生成页按既有规则不含 emoji,源页句中 emoji 旧式剥不掉);(d) 页脚文案「⚠ 丢失示例/丢失 N 条」→「⚠ 疑似差异示例/疑似差异 N 条」,避免未覆盖的边角被当"真丢字"写进页面。同版页脚「生成说明」版本号与 SKILL.md frontmatter 同步升 1.2.24(页脚必备规则集版本+运行时间约定)。
scripts/drive_v15.py:从源页生成「{源页名}_结合版_v15」结构化新页
(不动源页),一体化输出:活列表自动编号、顶部📑目录(多级序号 + 官方跳转链接)、
编号标题加粗、表格渲染、统一字体(宋体/新罗马 18pt)、固定页宽、末尾脚本说明。
> 注:内部逻辑自始就是 V15 终版,旧文件名 drive_v14_final.py 里的 v14 是
> 早期命名残留,v1.2.9 正式改名消除"文件名 v14 vs 实际 v15"的误会。
> v1.2.23 起已把该死文件从技能包移除(无人引用;副本留档在
> ~/.workbuddy/skills_backup/onenote-removed-20261008/drive_v14_final.py),
> 防止再被误当入口调用。(scripts/diag_v14_levels.py 是层级诊断工具,名里的
> v14 同属历史残留,实际 import 的是 drive_v15,保留可用。)
> v1.2.12 起 drive_v15.py 自带批量模式,原本只存在于 roundtrip.py 的
> 「批量跑整分区」能力已并入;roundtrip.py(v3 活列表)仅保留单页往返用途,
> 不再需要它来批量生成(见下方 batch 用法)。
python scripts/drive_v15.py # 单页缺省「元素杂质风险评估」/ 分区「仪器」
python scripts/drive_v15.py "页面名称" # 单页(分区「仪器」)
python scripts/drive_v15.py "页面名称" "分区名" # 单页(指定分区)
python scripts/drive_v15.py batch # 批量:处理「仪器」分区内所有非生成页
python scripts/drive_v15.py batch "清洁验证" # 批量:处理指定分区内所有非生成页
python scripts/refresh_toc.py "页面名称" # 单独刷新目录(原地编辑,保留手动修改)
> 批量模式跳过的页:名称命中 _结合版_v15 / _优化版 / _优化说明 / _方案B对比
> 后缀,或名为「无标题页 / Untitled Page / 空」的页——避免对已生成页二次生成陷入自循环。
> 批量中单页失败不中断,末尾汇总「成功 N/M」,失败的页单独重跑即可。
层级识别规则(样式与序号混合,样式优先)
- 页面含 OneNote 标题样式(标题1/2/3,QuickStyleDef h1/h2/h3)→ 整页走样式模式:
- 样式行:层级 = 样式级别;行上手写序号仅洗掉
- 样式块内手写序号行:层级 = 最近样式层级 + 序号深度(1、=+1 低一级起步)
- 纯文本行:正文,跟随最近标题 - 无样式页面:纯序号规则——行首
1、/1.=一级、1.1=二级、1.1.1=三级、
1)/1)/(1)=四级;序号洗掉转活列表;1类/2A类分类标签保留原文
约定与坑(详见 drive_v15.py 头部 docstring)
- 序号写行首,与标题文字之间留空格/顿号/冒号(数字粘连如
2.1.24种无法消歧) - 生成页不可作为再次生成的输入(活列表序号提取不到,层级会丢失)
- 成对括号(
<232>〈233〉《》等)整段保护不清洗;run 级处理前先拼整段判断 - 页内跳转链接:OneNote 拒收 OE 级 objectID/hyperlink 属性,必须两遍法
(先写正文让 OneNote 分配 objectID,再用 CDATA<a href="onenote:...">写目录) - 回收站("删除的页面"分区)内容对 COM 只读,无法脚本删除
用法
PY=C:/Users/Administrator/.workbuddy/binaries/python/versions/3.13.12/python.exe
# ── drive_v15.py(结构化结合版生成器,推荐;v1.2.12 起自带批量)──
$PY scripts/drive_v15.py "GC分析方法开发" # 单页(缺省分区 仪器)
$PY scripts/drive_v15.py "元素杂质风险评估" "仪器" # 单页(指定分区)
$PY scripts/drive_v15.py batch "仪器" # 批量:分区内所有非生成页
$PY scripts/drive_v15.py batch "清洁验证" # 批量:指定分区
# ── roundtrip.py(v3 活列表单页往返,保留)──
$PY scripts/roundtrip.py "工作相关" "清洁验证" "OEL、PDE" # 单页活列表
# 批量(跑该分区所有非 _优化版 / _优化说明 / 无标题页)——仅 roundtrip 保留此入口
$PY scripts/roundtrip.py "工作相关" "清洁验证"
# 不带参数:默认 工作相关 / 清洁验证 / OEL、PDE
$PY scripts/roundtrip.py
输出:OneNote 同分区下 <原页>_优化版 新页,含 Word 多级列表转换成的 OneNote
活列表(Enter 续号、Tab 切层级,OneNote 默认 scheme 1./a./i.,用户已接受)。
依赖
- 复用
onenote-direct-edit/scripts/onenote_com.py(OneNote COM 封装)和
text_rules.py(智能规则集)—— 通过 sys.path 复用,不重复维护。 python-docx:渲染 Word docx(已装在 managed python)。pywin32:Word COM(win32com.client)+win32gui窗口操作。pywinauto:keyboard.send_keys模拟 Ctrl+V(首选,比 keybd_event 稳)。
局限
- OneNote 多级 scheme 是固定的 1./a./i./1)/a)——不接受 Word 自定义
numFmt(全数字 1./1.1./1.1.1. 不行,用户已接受方案 A)。 - 批量跑偶发单页失败(约 1-2/5,前台被用户当前应用抢走、或粘贴时序)。
已内置轮询 + 重发逻辑兜底;仍失败时单独重跑该页即可。 - 新页位置:
CreateNewPage默认建到分区末尾,用户可手动拖到原页右边。 - 表格:v1.1 起 OneNote 原页
<one:Table>直接渲染成真正的 Word 表格
(保留行列与表头),粘回 OneNote 后变成可编辑的真表格,不再退化成
"行1/行2…"占位文字;列宽跟随层级缩进。合并单元格等高级特性暂未支持
(源页合并单元格会按普通单元格展开)。 - 桌面 docx 副产物:
outputs/<页名>_列表版.docx保留(可删,不影响 skill)。
坑点速查(2026-08-22 实测,v3 全踩过)
- Word 剪贴板延迟渲染:
Selection.Copy()后立即word.Quit()→ 剪贴板
数据随进程死掉 → Ctrl+V 粘空。修:复制后只最小化(进程活着),粘贴完成才退。 - 绝对不要 EmptyClipboard():那是待粘的数据!v1 曾为防「是否保留」对话框
清了剪贴板,直接导致 5 页全空(当时误诊为前台劫持)。「是否保留」对话框正
确解法是把 Quit 挪到粘贴之后 + taskkill 强杀。 - Word 窗口在前台会截胡 Ctrl+V(发给 Word 自己)。修:复制完立即
word.WindowState = 2最小化。 - 粘贴完成判定不能一次读回:图片多的页(50+ 张)粘贴耗时 10s+。修:
轮询 4 次 × 3s,>2 行即成功。 - 前台被其他应用抢走(如用户正在用的浏览器/WorkBuddy):重试前必须
GetForegroundWindow检查类名/标题是否 OneNote,不是就重新激活再发 Ctrl+V。 - SetForegroundWindow error(0):Win10/11 前台劫持。缓解:先
AllowSetForegroundWindow(前台进程PID);win32api没有AttachThreadInput
(AttributeError),别试。pywinauto send_keys 对焦点注入更宽容,优先用它。 - Word 残留:Quit 可能弹「是否保留复制的最后一项」挂起进程。修:粘贴完成
后 Quit +taskkill /F /IM WINWORD.EXE兜底(此时剪贴板已无用,杀掉无副作用)。 - OE 级 fontSize 属性被 OneNote 拒收(hrInvalidXML 0x80042001):字号走
style="font-family:宋体;font-size:18.0pt;color:#000000"(color 必须显式)。
(v2 直写 XML 时踩的,记着备用。) - 图片跳出框(1×1 表格左缩进比文字段更靠左):
tbl.alignment=0左对齐 +
tblPr/tblInd w:w="360" w:type="dxa"(0.25″ 缩进跟列表段一致)。 - 同分区重名原页(如 2 个 OEL、PDE)→ batch 跑两次。脚本幂等(同分区
<原页>_优化版跑前删),但建议用户删多余原页。 - COMError(-2147418111) 被呼叫方拒绝接收呼叫:Word COM 关闭后立即调
OneNote COM 的时序冲突。修:脚本间 sleep 或重跑该页。 - pywin32 只在 managed 基础解释器有:
binaries/python/versions/3.13.12/ python.exe有 pywin32;envs/defaultvenv 里没有(会回退手动模式)。
跑本 skill 一律用 3.13.12 基础解释器。 - 批量模式连续调 OneNote COM 会 RPC_E_CALL_REJECTED(OneNote 忙,常见于
上一页粘贴/同步未完):单页失败不致命,单独重跑该页即成功。 - Word 锁文件
~$xxx.docx残留在 outputs/:taskkill 强杀的副产物,
glob 遍历 outputs 时要跳过~$开头文件(不是 zip,解析会炸)。 - OneNote 粘贴按【段落缩进量】映射列表层级(v4 核心发现):docx 里
w:numPr 各级 ilvl 若w:ind left全相同,粘贴后全部拍平成单级 1..N
(清洁验证 5 页曾经全平的根因——Word 里只靠编号文本 1.1.1 区分层级,
视觉无缩进,OneNote 就当同级)。修:w:lvl 各级 ind 递增
360/720/1080/1440 dxa;图片表格 tblInd 也要按 level×360+360 递增。 - 多 Outline 页面:OneNote 一页可有多个 Outline(如分章节笔记),
只取"条目最多"的那个会丢其余全部内容。修:extract_raw v4 按文档顺序
遍历所有非优化版 Outline。 - OneNote 原生缩进型页面(嵌套 OEChildren,常见于学习笔记):缩进信息
在 XML 嵌套层级里,不在文字编号里,renumber_with_window 摸不到。修:
extract_raw v4 递归记录 indent + orig_bold,apply_indent_levels 合成层级。 - 同名重复页会让按名取页不可靠:分区里存在 2 个同名源页时,
next(name==…)
会随机命中,可能取空页→生成空壳 _优化版 残页。清理这类残页前务必按
objectID 核对内容,别用名字匹配误删真实源页(已在稳定性 24 页批量跑时踩过:
误删仅含标题的源页,已重建)。批量/清理一律优先按 objectID 精确操作。 - 「两种逻辑」根因澄清(2026-08-23):用户对比两页优化版——例1
TOC检测
手动 Tab 后显示 OneNote 自带1./a./i.逻辑;例2有关物质…显示 Word 十进制
1.1.1.逻辑——误以为"从 Word 复制只能用 OneNote 自带逻辑"是错的、其实有两种。
真相:两页本质都成了 OneNote 自己的活列表(默认 scheme 0/4/2 = 1./a./i.),
用户看到的差别只是层级是否真的嵌套:源页扁平单级(如 TOC检测 67 条全
level0)→ OneNote 全当同级1./2./3.,Tab 才展开a./i.(看着像"OneNote 自带");
源页有真实嵌套(父编号 +(1)/①/a)/一、子点,如 有关物质 52 L0 + 8 L1)→
OneNote 映射成多层级 → 呈现结构化十进制外观(看着像"Word 逻辑")。修复:
renumber_with_window次级编号归位((1)/①/a)/一、自动挂到最近父级下作子级)- build 各级缩进递增(360/720/1080/1440 dxa)→ 凡源页有结构的,一律得到结构化
嵌套外观,两种逻辑的差异因此消除。扁平单级源页(无子结构)天然就是单级列表,
不存在"十进制",这是数据本身决定的,并非 bug。
- build 各级缩进递增(360/720/1080/1440 dxa)→ 凡源页有结构的,一律得到结构化
- 源页手动编号跨 run 残留(2026-08-23 修复):OneNote 把
(1)总有机碳…
拆成 3 个独立 run((/1/)总有机碳…)。若只在首 run 跑strip_leading_*
正则,因首 run 只是(匹配不到完整(1)→1、)残留在 docx。修:build
端先''.join所有 run 文本 →_strip_prefix_len()算被编号+符号吃掉的字符数 L
→_drop_prefix()跨 run 按字符数消费前 L 个,剩余 run 颜色/加粗原样保留。实测
(1)/●/-/*全清干净、颜色(含被拆 run 的红色)不丢。 _SpanParser吞掉最后一个</span>之后的文字(2026-08-24 修复,曾致丢字):
roundtrip.py/fmt_compare.py里的_SpanParser(HTMLParser 子类)只在
handle_endtag('span')时_flush()一次。若某段文字里行内格式(<span>)后还跟
普通文字(典型:上标/下标,如峰面积(A<span style='vertical-align:sub'>A</span>) 按下式计算…里的),按下式计算…),这段"尾随文字"留在_buf里、没有闭合标签
触发 flush → 直接丢失。后果:有关物质页单点法正文断在(AB、多点法整段正文
消失;肉眼看优化页像"截断"。修:在_parse_oneT的p.close()之后补一句
p._flush(),把残留 buffer 吐出。判据:凡源页有上/下标、彩色/加粗 span 后又接正文,
必须保证尾部不丢。验证手段见fmt_compare.py。fmt_compare.py端到端格式对账工具(2026-08-24 新增):比对"源页 vs 优化页"
两页 OneNote XML,自动核对 (a) 文本保真(去行首编号/符号后,源片段是否都还在、
有无多余/重复——用全文子串包含法,块边界差异不算丢字);(b) 高亮保真;(c) 加粗保真
(只计"源有优化无"为丢失,优化多出加粗多为标题加粗属设计预期);(d) 层级断言
(用户点名的父子关系,如对仪器的一般要求/对照品溶液的制备/问题:…的后代是否含
预期子项)。跑法:python fmt_compare.py(默认比内置两测试页)或
python fmt_compare.py <源页名> <优化页名>。每次改完抽取/编号/渲染逻辑,先用它
回归,VERDICT=PASS 才算数,别只靠肉眼看截图。- 多级手工编号
3.1被部分剥离残留1(2026-08-24 修复):源页常用手写编号
3.1 对照品赋值不准确、1.2.3 测试等。build 端_LEADING_NUM_PATTERNS首条原为
^\s*\d+(?:\.\d+)*\.\s*(必须以点+空白结尾),遇到3.1时只能匹配3.,
残留.1→ 优化页显示成3.1 对照品赋值不准确(Word 列表编号3.+ 残留文本1),
用户误以为层级错成"3.1"。修:增加首条^\s*\d+(?:\.\d+)+(?:\.\s*|\s+)专剥
3.1/1.2.3/1.2.3.多级编号,并放单级1.规则之前,避免抢先。同步更新
fmt_compare.py的剥离规则,保持对账口径一致。修复后该条显示为3. 对照品赋值不准确
(与前面1./2.同级),fmt_compareVERDICT=PASS。 <621>被当 HTML 标签吃掉 → 指纹补全插重复 → 样式对齐全错位(2026-08-29 修复,
有关物质页实测):通则<621> Chromatography里的<621>是 USP 通则引用记号。
extract_raw 走 HTMLParser 路径原文保留;但build_style_levels/
flatten_table_images的文本归一化用re.sub(r"<[^>]+>", "", s),把<621>当
标签删了。后果链:flatten 指纹比对判该段「缺失」→ 插入一条重复段落 → raw 文本
条目比 XML 多 1 →build_style_levels位置对齐从错位点起全体偏移一格 →
后半页 h1/h2 样式全落到错误行上(目录出现2.1.1.1 多点法、3. 标准曲线法…
这类层级崩坏)。修:新增strip_html_tags()——标签名必须字母开头
(<621>数字开头 → 保留);build_style_levels同时改用 difflib 序列对齐
(文本匹配),raw 多/少一条只影响该条本身,不再整体错位;flatten 加
used[]消费标记 + ptr 后找不到回退全表再找,防误插。- 样式模式定级规则·用户定稿(2026-08-29,参照页「111」):样式底下无样式行带
手写序号((1)单点法、3.1 对照品赋值不准确等)→ 一律挂「最近样式行的
下一级」,不按序号深度(1、=+1/1.1=+2/(1)=+4)细分——细分曾产出
2.1.1.1 多点法这类过深层级。样式行本身仍按样式级别(h1→L1/h2→L2/h3→L3,
源页把3.5检测波长不合适误设为 h3 → 正确落为 3.4 的子级 3.4.1)。 -
生成说明区版本标签有两处硬编码:
build_clean_diffs_section(有洗稿/洗前缀
时走)与build_page_xml的 prefix_xml 兜底分支(无洗稿时走)。曾一处写死
"v13 单一 fmt" → 两个 _结合版_v14 页脚版本号不一致(用户发现)。已统一为
"V15 生成器 · <时间戳>"。以后改版本号两处都要改(当前为 V15:2026-08-29 合并双对话修复)。 -
行首仪器型号数字被当序号洗掉(v1.2.23 修复,HPLC维护保养页实测):源页给报错行
套了 OneNote 标题样式(h2),2695报错:lost prime/2487/2489开机报错…/
2414开机报错…走「样式行 → strip_prefix_from_runs」分支;_split_number_token
末条裸数字兜底^\d+[A-Za-z]?\s*不要求任何分隔符 → 型号数字被当手写序号删除,
14 行受损(2487/2489…更惨,只吃前半截留下孤零零/)。修:兜底改为
^\d{1,2}[A-Za-z]?(?=\s|$)\s*(①数字后须空白/行尾 ②裸数字限 1~2 位)。
⚠️ 凡 v1.2.22 及更早版本生成、行首出现 3~4 位型号/量值数字的页面须用本版重跑。
另注:strip_number_token对13.56×2=27.12这类数据行的保护不在本函数内,
靠调用方的_is_data_line;样式模式下带标题样式的数据行不受该保护(v1.2.24 已补齐,见 #29)。 -
荧光高亮被渲染成同色文字(v1.2.24 修复,缺陷 2):源页荧光笔高亮序列化为
background:yellow;mso-highlight:yellow。旧版runs_to_xhtml里hl = color or highlight
把文字颜色与背景高亮合并成单通道,再一律写color:→ 黄底变黄字,
白底黄字整段看不见(用户报"反梯度补偿技术那两段像是空的")。修:_highlight_style()
专门产background:/mso-highlight:,_span_style(kind, bold, color, highlight)分两路;
白色两侧仍置空。判据:只要源页有荧光笔高亮就受影响——全库扫描 34/135 页命中
(色值 yellow / aqua / #00B0F0 / #33CCCC / #99CCFF),须用本版重跑。
验证法:对比源/生成页 XML 里(background|mso-highlight):<非白色>的条数应一致
(HPLC 页实测 10 = 10、mso-highlight 5 = 5)。 -
样式模式数据行守卫缺失(v1.2.24 补齐,v1.2.17 残留缺口):
_is_data_line守卫
原先只加在无样式分支(depth_signal)。correct_v5的样式分支if slv:无条件
洗号 → 给数据行套了标题样式时,行首13.56照样被吃。修:样式行前置
if slv and _is_data_line(o): level=last_num_lv; numbered=False; continue。
(存量源页实测 0 例——ICP13.56×2=27.12行所在页无标题样式,v1.2.17 已覆盖;
本项为防御性,防"用户日后给数据行加样式"再踩。) -
保真报告"丢失"误报(v1.2.24 治理):v1.2.23 批跑 2 页报丢字,核查全是假阳性——
①公式行:源页 MathML 壳经orig_text()展平的字符序 ≠ 生成页回写形态(多肽页
源侧=A×Vε×m *1000vs 生成侧=(A×V)/(ε×m)*1000,生成侧才对)→ 公式行跳过文本
比对,走「公式 N→N」计数;②全角句点./全角数字前导未归一(4.职业暴露限值);
③句中 emoji 未剥 →_fid_norm尾巴叠strip_emoji。同时把页脚文案
「丢失示例」改「疑似差异示例」——归一化仍覆盖不到的边角不再被当"真丢字"。
⚠️ 排查保真告警时先看是不是公式行/归一化边角,别急着当丢内容。
关联
- OneNote 直改(
onenote-direct-editskill):直改 OneNote 页面(原地重排),
文字前缀方案——适合"不建新页、不需要活续号"的场景。 - OneNote 笔记排版格式化器(
onenoteskill):从 .docx 或粘贴文本生成 Word
列表版/优化版(不自动粘回 OneNote)。 - 本 skill(
OneNote Word 自动编号往返):上面两个的"自动往返"——Word 真编
号 + 自动粘回 OneNote 活列表。 - v2 直写 XML 路径(已废弃但代码思路记档):绕开 Word 直接 UpdatePage 写
OEChildren 嵌套 + 文字前缀。无活续号,但零依赖(无 Word/剪贴板/键盘模拟),
在"Ctrl+V 环境彻底不可用"时可回退。
输出页深度校验(scripts/verify_output.py)
生成后独立复核,不依赖生成器自身保真报告:
python scripts/verify_output.py <源页名> <输出页名> [分区关键字]
四项:①编号对齐(OE 直接T文本+编号文本 多重集,避开嵌套合并/表格拍平/目录块伪影)
②目录链接(CDATA 里 & 转义 + 链接seq十六进制→int换算后比 objectID 十进制seq)
③无编号加粗差异(应0)④编号条目加粗统计(标题样式,仅展示)。返回码 0/1 可自动化。
(2026-08-30 沉淀,分析页实战全绿)