@chengzhao-dev/dsh-quarto-book 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +6 -5
- package/README.md +6 -5
- package/locale/en.json +2 -2
- package/locale/zh.json +2 -2
- package/package.json +4 -4
- package/skills/designing-theme/SKILL.md +1 -1
- package/skills/designing-theme/assets/theme/palettes/hill-light/tokens.css +1 -1
- package/skills/writing-quarto/SKILL.md +4 -2
- package/skills/writing-quarto/quarto/prose.md +12 -3
- package/skills/writing-quarto/quarto/readability.md +55 -0
- package/skills/writing-quarto/quarto/render.md +17 -0
- package/skills/writing-quarto/scripts/checking-links.py +4 -1
- package/skills/writing-quarto/scripts/checking-prose.py +163 -0
- package/skills/writing-quarto/steps/01-task.md +1 -1
- package/skills/writing-quarto/steps/08-reread.md +2 -0
- package/skills/writing-quarto/steps/09-check.md +4 -4
- package/templates/quarto-book-project/SKILL.md +2 -0
package/README.en.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
**English** | [简体中文](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/README.md)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Polish Chinese technical chapters, check book structure and mixed Chinese-English prose, and maintain a consistent, readable theme; only inside `type: book` repositories.
|
|
8
8
|
|
|
9
|
-
- Write `.qmd`: `writing-quarto` covers the chapter skeleton, structure and prose.
|
|
9
|
+
- Write `.qmd`: `writing-quarto` covers the chapter skeleton, structure and Chinese prose.
|
|
10
10
|
- Change the theme: `designing-theme` covers CSS, fonts and palettes.
|
|
11
11
|
- Check rendered HTML: `quarto_render_check` judges publishability once, and `quarto_pass_report` prints the closing receipt.
|
|
12
12
|
|
|
@@ -26,10 +26,10 @@ Both skills are discovered from the package's `skills/` directory and loaded on
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.6.
|
|
29
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.6.1
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
Replace `--profile` with the profile you actually run. The desktop app always runs the `desktop` profile, and that profile is owned by the app, so `dsh --profile desktop` is rejected; install or upgrade a plugin with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin with `npm:@chengzhao-dev/dsh-quarto-book@0.6.
|
|
32
|
+
Replace `--profile` with the profile you actually run. The desktop app always runs the `desktop` profile, and that profile is owned by the app, so `dsh --profile desktop` is rejected; install or upgrade a plugin with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin with `npm:@chengzhao-dev/dsh-quarto-book@0.6.1` only if the app refuses that write. To follow a GitHub tag instead, use `github:chengzhao-dev/dsh-quarto-book#v0.6.1`.
|
|
33
33
|
|
|
34
34
|
Upgrading from the old unscoped package `dsh-quarto-book` (removed from npm with 0.3.4, so this only affects profiles that installed it earlier): remove it from the same profile first with `dsh plugin --profile <profile> remove dsh-quarto-book`, then add the scoped package. Keeping both installed registers `writing-quarto` and `designing-theme` twice at the same rank 550.
|
|
35
35
|
|
|
@@ -50,10 +50,11 @@ Run the content and link checks from the `writing-quarto` skill directory:
|
|
|
50
50
|
```bash
|
|
51
51
|
python scripts/verifying-content.py --root <book>
|
|
52
52
|
python scripts/checking-links.py --root <book>
|
|
53
|
+
python scripts/checking-prose.py --root <book>
|
|
53
54
|
python scripts/checking-names.py --root <book>
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
Add `checking-names.py` only for a whole-repository refactor; ordinary chapter checks run the first
|
|
57
|
+
`checking-prose.py` reports only high-confidence CJK/Latin spacing and punctuation issues. Add `checking-names.py` only for a whole-repository refactor; ordinary chapter checks run the first three.
|
|
57
58
|
|
|
58
59
|
Callouts, HTML output and DOM contracts after a render are checked by `quarto_render_check`, which invokes the packaged scripts; do not run `checking-callouts.py`, `checking-output.py` or `checking-dom.py` by hand. `designing-theme`'s `scripts/checking-layout.py` inspects rendered HTML for overflow, contrast and page structure. `apply()` runs none of these and never runs `quarto render`.
|
|
59
60
|
|
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
[English](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/README.en.md) | **简体中文**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
润色中文技术章节、检查 Book 结构与中英混排,并维护一套统一、可读的阅读主题;只在 `type: book` 的仓库里出现。
|
|
8
8
|
|
|
9
|
-
- 写 `.qmd`:`writing-quarto`
|
|
9
|
+
- 写 `.qmd`:`writing-quarto` 管章节骨架、结构与中文行文。
|
|
10
10
|
- 改主题:`designing-theme` 管 CSS、字体与 palette。
|
|
11
11
|
- 渲染后检查 HTML:`quarto_render_check` 判一次可发布性,`quarto_pass_report` 出收尾回执。
|
|
12
12
|
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
## 安装
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.6.
|
|
29
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.6.1
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级插件可用 `dsh plugin --profile desktop add <包>`,如果应用拒绝写入,再走「设置 → 插件 → 添加插件」,地址填 `npm:@chengzhao-dev/dsh-quarto-book@0.6.
|
|
32
|
+
`--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级插件可用 `dsh plugin --profile desktop add <包>`,如果应用拒绝写入,再走「设置 → 插件 → 添加插件」,地址填 `npm:@chengzhao-dev/dsh-quarto-book@0.6.1`。想跟 GitHub 标签,也可以写 `github:chengzhao-dev/dsh-quarto-book#v0.6.1`。
|
|
33
33
|
|
|
34
34
|
从旧的无作用域包 `dsh-quarto-book` 升级时(该包已随 0.3.4 从 npm 删除,只影响之前装过它的 profile),先在同一个 profile 里 `dsh plugin --profile <profile> remove dsh-quarto-book` 再装新包。两个包同时留着会以相同的 rank 550 各注册一遍 `writing-quarto` 和 `designing-theme`。
|
|
35
35
|
|
|
@@ -50,10 +50,11 @@ dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.6.0
|
|
|
50
50
|
```bash
|
|
51
51
|
python scripts/verifying-content.py --root <book>
|
|
52
52
|
python scripts/checking-links.py --root <book>
|
|
53
|
+
python scripts/checking-prose.py --root <book>
|
|
53
54
|
python scripts/checking-names.py --root <book>
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
`checking-names.py`
|
|
57
|
+
`checking-prose.py` 只报中文混排里能直接判断的问题;`checking-names.py` 只在全仓重构时加跑。日常章节检查跑前三条即可。
|
|
57
58
|
|
|
58
59
|
渲染后的 Callout、HTML 产物与 DOM 契约由 `quarto_render_check` 调包内脚本完成,不手跑 `checking-callouts.py`、`checking-output.py` 或 `checking-dom.py`;`designing-theme` 的 `scripts/checking-layout.py` 在渲染后的 HTML 上检查溢出、对比度和页面结构。`apply()` 不在加载时跑这些脚本,也不跑 `quarto render`。
|
|
59
60
|
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
|
-
"title": "Quarto Book Writing",
|
|
4
|
-
"description": "
|
|
3
|
+
"title": "Quarto Book Writing & Theme",
|
|
4
|
+
"description": "Polish Chinese technical chapters, check book structure and mixed Chinese-English prose, and maintain a consistent, readable theme."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chengzhao-dev/dsh-quarto-book",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "润色中文技术章节,检查 Book 结构与中英混排,并维护一套统一、可读的阅读主题。 · Polish Chinese technical chapters, check book structure and mixed Chinese-English prose, and maintain a consistent, readable theme.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"catalog": {
|
|
33
33
|
"category": "skill",
|
|
34
34
|
"summary": {
|
|
35
|
-
"zh": "
|
|
36
|
-
"en": "
|
|
35
|
+
"zh": "润色中文技术章节,检查 Book 结构与中英混排,并维护一套统一、可读的阅读主题。",
|
|
36
|
+
"en": "Polish Chinese technical chapters, check book structure and mixed Chinese-English prose, and maintain a consistent, readable theme."
|
|
37
37
|
},
|
|
38
38
|
"capabilities": [
|
|
39
39
|
"skills",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: writing-quarto
|
|
3
|
-
description:
|
|
3
|
+
description: 润色与重构 Quarto Book 章节:任务与结构、术语与中英混排、检查与渲染;不用于改主题样式
|
|
4
4
|
metadata:
|
|
5
|
-
short-description:
|
|
5
|
+
short-description: 润色中文 Quarto 图书章节
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Skill: writing-quarto
|
|
@@ -40,5 +40,7 @@ metadata:
|
|
|
40
40
|
|
|
41
41
|
- [ ] 九步走完,每一步的产出都落在正文里。
|
|
42
42
|
- [ ] 内容与链接核对通过:项目技能列了检查命令时按它执行,否则跑本技能 `scripts/verifying-content.py` 与 `checking-links.py`。
|
|
43
|
+
- [ ] 新人能按正文独立完成任务:前置、动作、结果与失败出口齐全,术语首用有解释;清单见 `quarto/readability.md`。
|
|
44
|
+
- [ ] 混排自查通过:`scripts/checking-prose.py --root <book>` 无高置信问题。
|
|
43
45
|
- [ ] 调 `quarto_render_check`:`dry_run: true` 最多 2 次,确认 `safe_to_render`;`dry_run: false` 最多 1 次,确认 `html_report.usable` 为 true。不手跑 `quarto render`,也不截图 PNG。
|
|
44
46
|
- [ ] 收尾调用一次 `quarto_pass_report`:`skill` 填 `writing-quarto`,`mode` 填 `polish` 或 `rewrite`,文件与步骤的改动次数、未改项、检查结论照实填。卡片就是回执,回复里不再复述;没改也传 0,当前文件不是章节时传 `executed: false`。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 速查:中文句用中文标点 · 英文句子内部保留英文标点 · 中西文之间一个半角空格 · 数字与单位之间一个空格 · 行内代码只标记要逐字识别的对象
|
|
4
4
|
|
|
5
|
-
本文件是标点、空格、引号、数字和英文术语写法的唯一出处;句子结构与用词仍归 `steps/05-sentences.md` 与 `steps/06-wording.md`,版式列表与加粗归 `steps/07-layout.md`。标点依据 GB/T 15834—2011 与 W3C CLREQ,空格与润色取舍是本仓约定。
|
|
5
|
+
本文件是标点、空格、引号、数字和英文术语写法的唯一出处;句子结构与用词仍归 `steps/05-sentences.md` 与 `steps/06-wording.md`,版式列表与加粗归 `steps/07-layout.md`,新人可读性与任务完整性归 `quarto/readability.md`。标点依据 GB/T 15834—2011 与 W3C CLREQ,空格与润色取舍是本仓约定。
|
|
6
6
|
|
|
7
7
|
## 一条主轴
|
|
8
8
|
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
|
|
31
31
|
## 引号与术语
|
|
32
32
|
|
|
33
|
-
- 术语首次出现写「中文译名(English original
|
|
33
|
+
- 术语首次出现写「中文译名(English original)」,紧跟一句可观察行为或用途解释;后文统一用中文或原文,全书只选一种。
|
|
34
|
+
- 缩写首次出现写全称,再写括号缩写;读者不需要额外查表就能继续读。
|
|
34
35
|
- API、类型、函数、参数、命令、路径和取值一律用行内代码,不翻译、不改大小写。
|
|
35
36
|
- 英文句子独立成句时,句末用英文标点;中文句子里夹一个英文词时,句末仍用中文标点。
|
|
36
37
|
- 不用全角拉丁字母或数字;不用中文引号包代码;不在术语中间插入空格。
|
|
@@ -42,6 +43,13 @@
|
|
|
42
43
|
- 版本号、坐标、代码里的数字保持源码形式,不补空格、不换全角。
|
|
43
44
|
- 单位符号与数字之间加空格;角度、百分比按行业习惯,同一本书内保持一致。
|
|
44
45
|
|
|
46
|
+
## 条件与步骤
|
|
47
|
+
|
|
48
|
+
- 条件写在动作前:「若要删除整个文档,先点 Delete」,不写「点 Delete,若你想删除整个文档」。
|
|
49
|
+
- 这条只约束操作步骤;解释分支结果时仍先写可观察结果,再补条件。
|
|
50
|
+
- 一步一个动作,动作后紧跟结果或验收方式;不要把三个动作塞进一句。
|
|
51
|
+
- 步骤只写读者此刻要做的事,设计取舍另起一段放在动作完成后。
|
|
52
|
+
|
|
45
53
|
## 去套话
|
|
46
54
|
|
|
47
55
|
- 删「进行/作出+名词」「值得注意的是」「不难发现」「综上所述」「首先、其次、最后」的堆叠。
|
|
@@ -75,7 +83,8 @@
|
|
|
75
83
|
- 段首是否用「这一章」「接下来」「首先」代替了具体对象。
|
|
76
84
|
- 是否出现「进行」「负责」「完成」加名词的空心动作,能换成具体动词吗。
|
|
77
85
|
- 段内是否至少有一句 20 字以内的短句。
|
|
78
|
-
-
|
|
86
|
+
- 首次出现的英文术语是否给了中文,是否紧跟一句可观察解释,后文是否统一。
|
|
87
|
+
- 条件是否写在动作前,一句是否只做一个动作。
|
|
79
88
|
|
|
80
89
|
## 依据
|
|
81
90
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 新人可读性与任务完整性
|
|
2
|
+
|
|
3
|
+
> 速查:读者能独立做完 = 前置 + 动作 + 结果 + 失败出口;术语首次出现给定义,条件写在动作前。
|
|
4
|
+
|
|
5
|
+
本文件补句子规则之外的两件事:新人能不能独立完成任务,术语有没有解释。句子、段落与版式仍归 `steps/05-sentences.md`、`steps/04-paragraphs.md` 与 `steps/07-layout.md`;本文件不重复它们的规则。
|
|
6
|
+
|
|
7
|
+
## 读者三问
|
|
8
|
+
|
|
9
|
+
每个任务节要让读者回答三个问题:我在哪里、我要做什么、做完看到什么。
|
|
10
|
+
|
|
11
|
+
- **前置**:开始前需要哪一章的结果、哪个文件、哪个环境或哪条命令。缺一项就补一句。
|
|
12
|
+
- **动作**:一个步骤一个动作,动词开头,对象明确。
|
|
13
|
+
- **结果**:命令输出、文件变化、界面变化、返回值或测试结果,至少有一项可观察。
|
|
14
|
+
- **失败出口**:出错时看哪条信息、回到哪一节、下一步试什么。
|
|
15
|
+
|
|
16
|
+
只写「运行脚本」而不写运行后看到什么,读者无法判断自己是否成功。
|
|
17
|
+
|
|
18
|
+
## 术语
|
|
19
|
+
|
|
20
|
+
术语的写法(首次「中文(English)」、一句可观察解释、缩写先全称、后文统一)以 `quarto/prose.md`「引号与术语」为准。本节只管解释放在哪里:
|
|
21
|
+
|
|
22
|
+
- 解释放在首次真正使用的位置,不放章首清单,也不留给下一章补课。
|
|
23
|
+
- 能换成日常词就不造术语;不为「显得专业」引入新词。
|
|
24
|
+
- 判定句:把术语换成「这个东西」,句子是否仍然成立?不成立说明解释不够。
|
|
25
|
+
|
|
26
|
+
## 步骤完整
|
|
27
|
+
|
|
28
|
+
- 动作之后紧跟结果或验收方式,结果不要等到节末才出现。
|
|
29
|
+
- 失败路径写触发条件、可观察现象与恢复动作,不写空泛的「注意报错」。
|
|
30
|
+
- 操作节不插入与当前动作无关的设计辩论;取舍另起一段放在动作完成后。
|
|
31
|
+
- 条件与动作的先后见 `quarto/prose.md`「条件与步骤」。
|
|
32
|
+
|
|
33
|
+
## 完整性清单
|
|
34
|
+
|
|
35
|
+
- [ ] 读者知道开始前需要什么:环境、文件、上一章产物。
|
|
36
|
+
- [ ] 每个任务有可观察的完成标志,不只写「运行一下」。
|
|
37
|
+
- [ ] 新术语首用有解释,缩写有全称。
|
|
38
|
+
- [ ] 条件在动作前,一句只做一个动作。
|
|
39
|
+
- [ ] 失败时知道看什么、回到哪一节。
|
|
40
|
+
- [ ] 没有「以后会讲」但当前就必须使用的名字。
|
|
41
|
+
- [ ] 删掉本段后读者仍能完成任务的内容已经删除。
|
|
42
|
+
- [ ] 读者不需要作者的补充说明,就能把这一节独立走完。
|
|
43
|
+
|
|
44
|
+
## 混排自查
|
|
45
|
+
|
|
46
|
+
`python <skill>/scripts/checking-prose.py --root <book>` 检查中文与拉丁字母、数字、行内代码之间的空格,以及中文句里的半角标点。脚本只报高置信项,不替代人工回读。
|
|
47
|
+
|
|
48
|
+
## 依据
|
|
49
|
+
|
|
50
|
+
- Google developer documentation style guide:https://developers.google.com/style/highlights
|
|
51
|
+
- Google Jargon:https://developers.google.com/style/jargon
|
|
52
|
+
- Google sentence structure(条件先于指令):https://developers.google.com/style/sentence-structure
|
|
53
|
+
- Diátaxis 质量模型:https://diataxis.fr/quality/
|
|
54
|
+
- Digital.gov Plain language:https://digital.gov/guides/plain-language/principles
|
|
55
|
+
- 中文技术文档写作风格指南:https://zh-style-guide.readthedocs.io/
|
|
@@ -35,6 +35,23 @@
|
|
|
35
35
|
|
|
36
36
|
渲染后确认:目录深度与位置符合预期、明暗两套高亮都可读。渲染产物结构由 `checking-output.py` 与 `checking-callouts.py` 自动核对;页面宽度与视觉依据见 `steps/02-structure.md`。主题或目录看起来没生效时先硬刷新排除缓存,再核对选项嵌套层级。
|
|
37
37
|
|
|
38
|
+
## 排查顺序
|
|
39
|
+
|
|
40
|
+
先确定失败在哪一层,再查症状。一次只改一层,改完复跑同一层;不要用改 YAML 或重装依赖来碰运气。
|
|
41
|
+
|
|
42
|
+
1. **内容层**:先跑 `verifying-content.py`(或项目技能的内容检查命令)。章节表、标题、句段长度、围栏与链接的失败都在这一层修。
|
|
43
|
+
2. **混排层**:跑 `checking-prose.py`。缺空格、中文句里的半角标点在这一层修;不要顺手改句子结构。
|
|
44
|
+
3. **渲染层**:调 `quarto_render_check` 的 `dry_run: false`。失败时先读 `render_result` 末尾的报错块,判断是 YAML、Lua 过滤器还是代码执行。
|
|
45
|
+
4. **HTML 层**:读 `html_report.checks`。`index_html`、`utf8`、`charset` 失败回写作层;`dom` 或版式失败转 `designing-theme`。
|
|
46
|
+
|
|
47
|
+
排查时守住三条:
|
|
48
|
+
|
|
49
|
+
- **首个错误优先**:只修报错里的第一处,修完复跑;不要一次改五处再猜是哪处生效。
|
|
50
|
+
- **缩小范围**:整书失败时先只渲染当前章节,确认单章能过,再看全书配置与跨章链接。
|
|
51
|
+
- **不做破坏性验证**:不删 `_book/`、不关检查、不改成 `warning` 绕过;失败原因写进回复或 `temp/` 台账。
|
|
52
|
+
|
|
53
|
+
找不到失败层时,把完整命令、首个错误原文与最近一次改动列出来,再按上表从上往下复跑。
|
|
54
|
+
|
|
38
55
|
## 渲染与发布排查索引
|
|
39
56
|
|
|
40
57
|
按症状查此表:每条只给可执行处置。行为成因与取舍的唯一出处是知识库,用
|
|
@@ -40,7 +40,10 @@ def collect_sources(root: Path, paths: list[str]) -> list[Path]:
|
|
|
40
40
|
found = list(root.rglob("*.qmd")) + list(root.rglob("*.md"))
|
|
41
41
|
return sorted(
|
|
42
42
|
path for path in found
|
|
43
|
-
if not any(
|
|
43
|
+
if not any(
|
|
44
|
+
part in {".git", ".cache", ".quarto", "_book", "_freeze", "_site", "build", "node_modules", "temp"}
|
|
45
|
+
for part in path.parts
|
|
46
|
+
)
|
|
44
47
|
)
|
|
45
48
|
|
|
46
49
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""检查 Quarto 章节里的高置信中英混排问题。
|
|
3
|
+
|
|
4
|
+
只报能直接判断的错误:中文与拉丁字母、数字或行内代码之间缺空格,
|
|
5
|
+
中文句里混入半角标点,中文标点前多了空格。句子长短、术语解释等
|
|
6
|
+
仍需按 writing-quarto 的步骤人工回读。
|
|
7
|
+
|
|
8
|
+
用法:
|
|
9
|
+
python checking-prose.py --root <book>
|
|
10
|
+
python checking-prose.py --root <book> --json
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import json
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
CJK = r"\u4e00-\u9fff"
|
|
23
|
+
CJK_CHAR = re.compile(f"[{CJK}]")
|
|
24
|
+
CJK_LATIN = re.compile(f"([{CJK}])([A-Za-z0-9])")
|
|
25
|
+
LATIN_CJK = re.compile(f"([A-Za-z0-9])([{CJK}])")
|
|
26
|
+
HALF_PUNCT_IN_CJK = re.compile(f"([{CJK}])[,.;:?!]([{CJK}])")
|
|
27
|
+
SPACE_BEFORE_PUNCT = re.compile(f"([^\\s])[ \\t]+([,。;:!?、])")
|
|
28
|
+
DOUBLE_SPACE = re.compile(r"(?<=\S)[ \t]{2,}(?=\S)")
|
|
29
|
+
FENCE = re.compile(r"^\s*(```+|~~~+)")
|
|
30
|
+
INLINE_CODE = re.compile(r"`[^`\n]*`")
|
|
31
|
+
URL = re.compile(r"https?://[^\s)>\]]+")
|
|
32
|
+
LINK_TARGET = re.compile(r"\]\([^)]*\)")
|
|
33
|
+
HTML_TAG = re.compile(r"<[^>]+>")
|
|
34
|
+
SKIP_PREFIXES = (":::", "| ---", "|---")
|
|
35
|
+
SKIP_DIRS = {"_book", ".quarto", "node_modules", ".git", "temp", ".cache"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class Issue:
|
|
40
|
+
path: str
|
|
41
|
+
line: int
|
|
42
|
+
message: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def strip_frontmatter(text: str) -> str:
|
|
46
|
+
lines = text.splitlines()
|
|
47
|
+
if lines and lines[0].strip() == "---":
|
|
48
|
+
for index in range(1, len(lines)):
|
|
49
|
+
if lines[index].strip() == "---":
|
|
50
|
+
return "\n".join(lines[index + 1 :])
|
|
51
|
+
return text
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def mask_links_and_tags(line: str) -> str:
|
|
55
|
+
line = URL.sub("\x00", line)
|
|
56
|
+
line = LINK_TARGET.sub("\x00", line)
|
|
57
|
+
line = HTML_TAG.sub("\x00", line)
|
|
58
|
+
return line
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def mask_inline_code(line: str) -> str:
|
|
62
|
+
return INLINE_CODE.sub("\x00", line)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def check_line(path: str, number: int, line: str) -> list[Issue]:
|
|
66
|
+
stripped = line.strip()
|
|
67
|
+
if not stripped or stripped.startswith(SKIP_PREFIXES) or stripped.startswith("<!--"):
|
|
68
|
+
return []
|
|
69
|
+
if stripped.startswith("#") and stripped.endswith("#"):
|
|
70
|
+
return []
|
|
71
|
+
if stripped.startswith("|") and set(stripped) <= set("|-: "):
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
issues: list[Issue] = []
|
|
75
|
+
base = mask_links_and_tags(line)
|
|
76
|
+
visible = mask_inline_code(base)
|
|
77
|
+
|
|
78
|
+
for match in CJK_LATIN.finditer(visible):
|
|
79
|
+
issues.append(Issue(path, number, f"中文与拉丁字母/数字之间缺空格:{match.group(0)}"))
|
|
80
|
+
for match in LATIN_CJK.finditer(visible):
|
|
81
|
+
issues.append(Issue(path, number, f"拉丁字母/数字与中文之间缺空格:{match.group(0)}"))
|
|
82
|
+
for match in HALF_PUNCT_IN_CJK.finditer(visible):
|
|
83
|
+
issues.append(Issue(path, number, f"中文句里混入半角标点:{match.group(0)}"))
|
|
84
|
+
for match in SPACE_BEFORE_PUNCT.finditer(visible):
|
|
85
|
+
issues.append(Issue(path, number, f"中文标点前多了空格:{match.group(0)}"))
|
|
86
|
+
for match in DOUBLE_SPACE.finditer(visible):
|
|
87
|
+
issues.append(Issue(path, number, f"正文里有连续空格:{match.group(0)!r}"))
|
|
88
|
+
|
|
89
|
+
for match in INLINE_CODE.finditer(base):
|
|
90
|
+
before = base[match.start() - 1] if match.start() else ""
|
|
91
|
+
after = base[match.end()] if match.end() < len(base) else ""
|
|
92
|
+
if before and CJK_CHAR.match(before):
|
|
93
|
+
issues.append(Issue(path, number, "中文与行内代码之间缺空格"))
|
|
94
|
+
if after and CJK_CHAR.match(after):
|
|
95
|
+
issues.append(Issue(path, number, "行内代码与中文之间缺空格"))
|
|
96
|
+
|
|
97
|
+
return issues
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def scan_file(root: Path, path: Path) -> list[Issue]:
|
|
101
|
+
text = path.read_text(encoding="utf-8", errors="replace")
|
|
102
|
+
text = strip_frontmatter(text)
|
|
103
|
+
issues: list[Issue] = []
|
|
104
|
+
in_fence = False
|
|
105
|
+
for number, line in enumerate(text.splitlines(), start=1):
|
|
106
|
+
if FENCE.match(line):
|
|
107
|
+
in_fence = not in_fence
|
|
108
|
+
continue
|
|
109
|
+
if in_fence:
|
|
110
|
+
continue
|
|
111
|
+
issues.extend(check_line(path.relative_to(root).as_posix(), number, line))
|
|
112
|
+
return issues
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def collect_files(root: Path) -> list[Path]:
|
|
116
|
+
files: list[Path] = []
|
|
117
|
+
for path in sorted(root.rglob("*.qmd")):
|
|
118
|
+
if any(part in SKIP_DIRS for part in path.relative_to(root).parts):
|
|
119
|
+
continue
|
|
120
|
+
files.append(path)
|
|
121
|
+
return files
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def main() -> int:
|
|
125
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
126
|
+
parser.add_argument("--root", default=".", help="书仓库根目录")
|
|
127
|
+
parser.add_argument("--json", action="store_true", help="输出 JSON")
|
|
128
|
+
parser.add_argument("--quiet", action="store_true", help="没有问题时也保持安静")
|
|
129
|
+
args = parser.parse_args()
|
|
130
|
+
|
|
131
|
+
root = Path(args.root).resolve()
|
|
132
|
+
if not root.is_dir():
|
|
133
|
+
print(f"失败 根目录不存在:{root}", file=sys.stderr)
|
|
134
|
+
return 2
|
|
135
|
+
|
|
136
|
+
issues: list[Issue] = []
|
|
137
|
+
for path in collect_files(root):
|
|
138
|
+
issues.extend(scan_file(root, path))
|
|
139
|
+
|
|
140
|
+
if args.json:
|
|
141
|
+
print(
|
|
142
|
+
json.dumps(
|
|
143
|
+
{
|
|
144
|
+
"root": str(root),
|
|
145
|
+
"files": len(collect_files(root)),
|
|
146
|
+
"issues": [issue.__dict__ for issue in issues],
|
|
147
|
+
},
|
|
148
|
+
ensure_ascii=False,
|
|
149
|
+
indent=2,
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
elif issues:
|
|
153
|
+
print(f"发现 {len(issues)} 个混排问题:")
|
|
154
|
+
for issue in issues:
|
|
155
|
+
print(f"{issue.path}:{issue.line}: {issue.message}")
|
|
156
|
+
elif not args.quiet:
|
|
157
|
+
print(f"OK:{len(collect_files(root))} 个章节通过混排检查。")
|
|
158
|
+
|
|
159
|
+
return 1 if issues else 0
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
raise SystemExit(main())
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
| 定句子 | 没点名载体(「一个下标」「这个下标」「这次代入」),或一句里同时有跳过整行、列偏移和得数,或公式项列表 | 按列表选择换算条写成同一段短句 | `steps/07-layout.md`「列表选择」 |
|
|
37
37
|
| 定句子 | `Get*` 概述纯名词句(`/// 当前流程结果。`) | 改「获取…」,动词与函数名前缀硬对齐;只对齐围栏里的声明,不改源码注释 | `steps/03-blocks.md` |
|
|
38
38
|
| 定句子 | 分支解释把条件压在句首(拒绝、终局句读到句末才知道结果) | 主句先写可观察结果,条件跟在后面或另起一句 | `steps/05-sentences.md`「句子规则」 |
|
|
39
|
+
| 定句子 | 操作步骤先写动作、条件放在后面(「点 Delete,若你想…」) | 把条件句移到动作前,一步只留一个动作 | Google sentence structure;`quarto/readability.md` |
|
|
40
|
+
| 定用词 | 新术语首次出现只有名称或只有英文缩写,没有一句可观察解释 | 首用补「中文(English)」与一句定义,缩写先全称 | Google Jargon;`quarto/readability.md` |
|
|
39
41
|
| 定句子 | 行尾把格子状态、先手或后手、操作动作写进同一句 | 换成术语表「数据项类型」里的三句,与头文件逐字一致 | `steps/05-sentences.md`、`steps/06-wording.md` |
|
|
40
42
|
| 定句子 | 一句里的量被「确定、体现、界定、表达」盖住(时间义「已经确定」合格) | 改写成相等、乘除加减、上下界或成员记法;「由 A 得到 B」「将 A 转换为 B」的换算注释不在此改 | `steps/05-sentences.md`「关系写成能核对的运算」 |
|
|
41
43
|
| 定段落 | 散文伪标签冒号(话题 + `:` 词条式冒号);多核混段 | 词条式冒号改完整主谓句或真列表项;多核混段按拆核四步改写 | `steps/04-paragraphs.md`「散文伪标签冒号禁令」「拆核四步(多核混段的拆段方法)」 |
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
产出:内容核对结果、一次渲染检查和 HTML 可用性报告。这一步只跑命令,不按报告改写法。
|
|
4
4
|
|
|
5
|
-
内容与链接检查需要 Python 3;`dry_run: false` 的渲染还需要 Quarto CLI。缺少依赖时停住并报告,不改回手跑 `quarto render
|
|
5
|
+
内容与链接检查需要 Python 3;`dry_run: false` 的渲染还需要 Quarto CLI。缺少依赖时停住并报告,不改回手跑 `quarto render`。跨层失败先按 `quarto/render.md`「排查顺序」定位,再回到对应步骤。
|
|
6
6
|
|
|
7
7
|
## 通用情况
|
|
8
8
|
|
|
9
9
|
### 内容与链接
|
|
10
10
|
|
|
11
|
-
项目技能列了内容检查命令时,只跑它列出的命令。没有项目技能时,在书根跑本技能 `scripts/` 下的 `verifying-content.py` 与 `checking-
|
|
11
|
+
项目技能列了内容检查命令时,只跑它列出的命令。没有项目技能时,在书根跑本技能 `scripts/` 下的 `verifying-content.py`、`checking-links.py` 与 `checking-prose.py`。
|
|
12
12
|
|
|
13
|
-
`verifying-content.py` 查章节表、YAML 标题、标题层级、句段长度、围栏语言、Mermaid
|
|
13
|
+
`verifying-content.py` 查章节表、YAML 标题、标题层级、句段长度、围栏语言、Mermaid 围栏、占位文字和本地链接;`checking-prose.py` 查中文与拉丁字母、数字、行内代码之间的空格,以及中文句里的半角标点。项目专属检查只在书的 `.agents/skills/quarto-book-project/` 存在时启用。
|
|
14
14
|
|
|
15
15
|
### 渲染与 HTML
|
|
16
16
|
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
|
|
70
70
|
## 完成判据
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
内容命令与混排检查通过,`dry_run: false` 返回 `render_result.ok: true`,且 `html_report.usable: true`,并调用一次 `quarto_pass_report` 出回执,才算完成。任一条件不满足,回到报告指向的步骤。
|
|
73
73
|
|
|
74
74
|
## 正反例
|
|
75
75
|
|