@chengzhao-dev/dsh-quarto-book 0.6.4 → 0.6.5
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 +1 -1
- package/README.md +3 -1
- package/lib/tools/render-check.js +24 -5
- package/package.json +1 -1
- package/skills/designing-theme/SKILL.md +1 -1
- package/skills/designing-theme/assets/theme/css/content.css +16 -8
- package/skills/designing-theme/references/theme-spine.md +1 -1
- package/skills/writing-quarto/SKILL.md +4 -2
- package/skills/writing-quarto/examples/03-block-after.md +29 -0
- package/skills/writing-quarto/examples/03-excerpt-lead.md +10 -0
- package/skills/writing-quarto/examples/04-paragraph-cases.md +13 -0
- package/skills/writing-quarto/examples/07-layout-list.md +34 -0
- package/skills/writing-quarto/quarto/authoring.md +2 -2
- package/skills/writing-quarto/quarto/mermaid.md +1 -1
- package/skills/writing-quarto/quarto/render.md +4 -4
- package/skills/writing-quarto/quarto/tables.md +79 -0
- package/skills/writing-quarto/scripts/checking-links.py +47 -6
- package/skills/writing-quarto/scripts/verifying-content.py +22 -0
- package/skills/writing-quarto/steps/02-structure.md +1 -1
- package/skills/writing-quarto/steps/03-blocks.md +15 -3
- package/skills/writing-quarto/steps/04-paragraphs.md +1 -0
- package/skills/writing-quarto/steps/07-layout.md +5 -1
- package/skills/writing-quarto/steps/08-reread.md +1 -1
package/README.en.md
CHANGED
|
@@ -29,7 +29,7 @@ Both skills are discovered from the package's `skills/` directory and loaded on
|
|
|
29
29
|
2. Open a session in the book repository. `_quarto.yml` needs a standalone `type: book` line, and the book needs at least one `.qmd` or `.md` chapter source; only then do both skill names appear.
|
|
30
30
|
3. Call a skill: `writing-quarto` for prose, `designing-theme` for the theme.
|
|
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 with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin only if the app refuses that write. To pin a version, write `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 with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin only if the app refuses that write. To pin a version, write `npm:@chengzhao-dev/dsh-quarto-book@0.6.5`; to follow a GitHub tag, write `github:chengzhao-dev/dsh-quarto-book#v0.6.5`.
|
|
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
|
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
2. 在书仓库里打开会话。`_quarto.yml` 要有独立一行的 `type: book`,书根至少有一个 `.qmd` 或 `.md` 章节源文件;两个技能名这时才会出现。
|
|
30
30
|
3. 调用技能:写正文用 `writing-quarto`,改主题用 `designing-theme`。
|
|
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.5`,跟 GitHub 标签时写 `github:chengzhao-dev/dsh-quarto-book#v0.6.5`。
|
|
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
|
|
|
@@ -87,10 +87,12 @@ python scripts/checking-names.py --root <book>
|
|
|
87
87
|
|
|
88
88
|
| 文档 | 内容 |
|
|
89
89
|
| --- | --- |
|
|
90
|
+
| [docs/architecture.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/architecture.md) | 两个技能与两个工具的分层、四个写作域与文件职责 |
|
|
90
91
|
| [docs/plugin-flow.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-flow.md) | 从安装到回执的五层结构、文件分流与渲染顺序 |
|
|
91
92
|
| [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) | 工作区检查的六类情形与判定顺序 |
|
|
92
93
|
| [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) | 仓库目录、发布包内容与 Python/Quarto 分工 |
|
|
93
94
|
| [docs/writing-quarto-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/writing-quarto-layout.md) | 写作技能的目录分层 |
|
|
95
|
+
| [docs/triggering.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/triggering.md) | 两个技能的触发判据、四个写作域与正反例 |
|
|
94
96
|
| [docs/versioning.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/versioning.md) | 版本号与发布顺序 |
|
|
95
97
|
|
|
96
98
|
## 开发
|
|
@@ -122,11 +122,15 @@ function killProcessTree(child, cwd, spawnFn) {
|
|
|
122
122
|
if (pid && process.platform === 'win32') {
|
|
123
123
|
// Windows 上只杀直接子进程会留下 Quarto 派生的 pandoc/python。
|
|
124
124
|
try {
|
|
125
|
-
spawnFn('taskkill', ['/pid', String(pid), '/T', '/F'], {
|
|
125
|
+
const killer = spawnFn('taskkill', ['/pid', String(pid), '/T', '/F'], {
|
|
126
126
|
cwd,
|
|
127
127
|
windowsHide: true,
|
|
128
128
|
shell: false,
|
|
129
129
|
});
|
|
130
|
+
// taskkill 启动失败会走异步 error;不监听会成为未处理错误。
|
|
131
|
+
killer.on('error', () => {
|
|
132
|
+
// 下面仍会尝试 child.kill,这里只保证异步错误有出口。
|
|
133
|
+
});
|
|
130
134
|
}
|
|
131
135
|
catch {
|
|
132
136
|
// taskkill 不可用时退回 child.kill。
|
|
@@ -312,13 +316,28 @@ export async function inspectHtml(input) {
|
|
|
312
316
|
const errors = [];
|
|
313
317
|
const checks = [];
|
|
314
318
|
const index = path.join(input.outputDir, 'index.html');
|
|
315
|
-
const
|
|
319
|
+
const indexPath = relativeTo(input.root, index);
|
|
320
|
+
let indexText;
|
|
321
|
+
let indexDetail;
|
|
322
|
+
try {
|
|
323
|
+
const indexStat = await stat(index);
|
|
324
|
+
if (indexStat.size > MAX_HTML_BYTES) {
|
|
325
|
+
indexDetail = `${indexPath} 超过 ${Math.round(MAX_HTML_BYTES / 1024 / 1024)} MiB,未读`;
|
|
326
|
+
errors.push(`${indexPath} 太大,未读`);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
indexText = await readUtf8(index);
|
|
330
|
+
indexDetail =
|
|
331
|
+
indexText === undefined ? `缺少可读的 ${indexPath}` : `${indexPath} 已生成`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
indexDetail = `缺少可读的 ${indexPath}`;
|
|
336
|
+
}
|
|
316
337
|
checks.push({
|
|
317
338
|
id: 'index_html',
|
|
318
339
|
status: indexText === undefined ? 'fail' : 'pass',
|
|
319
|
-
detail:
|
|
320
|
-
? `缺少可读的 ${relativeTo(input.root, index)}`
|
|
321
|
-
: `${relativeTo(input.root, index)} 已生成`,
|
|
340
|
+
detail: indexDetail,
|
|
322
341
|
});
|
|
323
342
|
const broken = [];
|
|
324
343
|
const fileUris = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chengzhao-dev/dsh-quarto-book",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "把一份 Quarto Book 写成可读、可查、可发布的中文技术书:九步章节写作、结构/链接/图表检查、统一主题与渲染验收。 · Write, check, and publish a Chinese Quarto Book: nine-step chapter writing, structure/link/figure checks, a consistent theme, and render acceptance.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* dsh-quarto-book Quarto 主题 · 正文内容(表格 / 引用 / 正文组件与排版;自 common.css 拆分) */
|
|
2
2
|
|
|
3
|
-
/* ---------- 表格(booktabs
|
|
3
|
+
/* ---------- 表格(booktabs:顶/底细线、表头加粗带浅底,无行线与竖线) ---------- */
|
|
4
4
|
.table {
|
|
5
5
|
--bs-table-hover-bg: transparent;
|
|
6
6
|
/* 表体与正文同档:表格字号一旦小于正文,正文里嵌表格就会出现两次字级跳变,
|
|
@@ -12,13 +12,19 @@
|
|
|
12
12
|
max-width: 100%;
|
|
13
13
|
overflow-x: auto;
|
|
14
14
|
-webkit-overflow-scrolling: touch;
|
|
15
|
+
/* 列宽随内容分配:不写固定像素,长单元格换行,整表窄屏横向滚动。 */
|
|
16
|
+
table-layout: auto;
|
|
17
|
+
margin-block: 1.35rem 1.5rem;
|
|
15
18
|
border: none;
|
|
16
19
|
border-radius: 0;
|
|
17
20
|
font-variant-numeric: tabular-nums;
|
|
18
21
|
}
|
|
19
22
|
.table > :not(caption) > * > * {
|
|
20
|
-
padding: 0.55rem 0.
|
|
21
|
-
|
|
23
|
+
padding: 0.55rem 0.95rem;
|
|
24
|
+
/* 多行单元格从顶部起排,同一行里短格子不被拉到垂直居中。 */
|
|
25
|
+
vertical-align: top;
|
|
26
|
+
/* 长 API 名与长句允许在格内折行,避免把整张表撑出内容列。 */
|
|
27
|
+
overflow-wrap: break-word;
|
|
22
28
|
border-bottom: 0;
|
|
23
29
|
}
|
|
24
30
|
/* 首尾列贴边,让表格与正文左缘对齐 */
|
|
@@ -29,13 +35,15 @@
|
|
|
29
35
|
padding-inline-end: 0;
|
|
30
36
|
}
|
|
31
37
|
.table thead th {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
/* 表头用加粗与一道极浅底色标出层级;不套 .compact-table,列宽仍随内容。 */
|
|
39
|
+
background-color: color-mix(in srgb, var(--table-border) 24%, transparent);
|
|
40
|
+
font-weight: 700;
|
|
41
|
+
/* 表头比表体小 1px(17px → 15px),层级靠字重、字号与底色,不靠更粗的线。 */
|
|
35
42
|
font-size: 0.9375rem;
|
|
43
|
+
line-height: 1.6;
|
|
36
44
|
text-align: left;
|
|
37
|
-
padding-top: 0;
|
|
38
|
-
padding-bottom: 0.
|
|
45
|
+
padding-top: 0.5rem;
|
|
46
|
+
padding-bottom: 0.5rem;
|
|
39
47
|
border-top: 1px solid var(--table-border);
|
|
40
48
|
border-bottom: 1px solid var(--table-border);
|
|
41
49
|
}
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
2. 新规则必须先标明属于哪一步:能放进已有 `##` 就放进去;只有尺度真的变了才允许新开一步。禁止再写一条不标明步骤的禁令。
|
|
22
22
|
3. 字体、栏宽、颜色决定的理由写进 `knowledge/reading-surface.md`,不在 reference 里重复论证。
|
|
23
23
|
4. 同步更新本文件、`SKILL.md`、`theme-system.md`、知识文件与检查脚本。
|
|
24
|
-
5. 改完运行 `scripts/checking-layout.py
|
|
24
|
+
5. 改完运行 `scripts/checking-layout.py`,再用 `quarto_render_check` 做整本渲染;两者都通过才算完成。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: writing-quarto
|
|
3
|
-
description:
|
|
3
|
+
description: 写作、布局、排版与润色 Quarto Book 章节:任务与结构、内容块与表格、段落与句子、中英混排与术语、检查与渲染;不用于改主题样式
|
|
4
4
|
metadata:
|
|
5
5
|
short-description: 润色中文 Quarto 图书章节
|
|
6
6
|
---
|
|
@@ -32,9 +32,11 @@ metadata:
|
|
|
32
32
|
|
|
33
33
|
## 九步
|
|
34
34
|
|
|
35
|
+
九步按四个写作域分:写作 1–2,布局 3–4 与 7,润色 5–6 与 8,排版归主题技能;第 9 步是检查与渲染。跨域改动按「写作 → 布局 → 润色」的顺序推进,前一步落定再进下一步。
|
|
36
|
+
|
|
35
37
|
1. 定任务:写出读者做完能完成的那件事,再列这一章要覆盖的任务。规则在 `steps/01-task.md`。
|
|
36
38
|
2. 定结构:排页面骨架、二级标题和篇幅预算,只定删留、标题与章题对齐。规则在 `steps/02-structure.md`。
|
|
37
|
-
3. 排列内容块:导语、围栏、收束与 Callout 的次序,一个围栏只放正在讲的那一块。规则在 `steps/03-blocks.md`。
|
|
39
|
+
3. 排列内容块:导语、围栏、收束与 Callout 的次序,一个围栏只放正在讲的那一块。规则在 `steps/03-blocks.md`,表格写法见 `quarto/tables.md`。
|
|
38
40
|
图注写在图下、图前只写引出关系;三类图的统一写法见 `quarto/figures.md`。
|
|
39
41
|
4. 定段落:一段一个对象,段间衔接落在话题切换处。规则在 `steps/04-paragraphs.md`。
|
|
40
42
|
5. 定句子:主语选执行者,对象先行,一句一动作。规则在 `steps/05-sentences.md`。
|
|
@@ -41,6 +41,35 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
|
|
41
41
|
读取和写入都先检查坐标,两种失败分开处理:出界坐标说明调用方算错了,读取直接抛出 `std::out_of_range`,没有可返回的格子。格子被占用在流程里随时会发生,写入返回 `false`,状态面板保持不变,调用方再选别的格子就行。完整的 `@param`、`@returns`、`@throws` 注释写在 `state.h` 里。
|
|
42
42
|
````
|
|
43
43
|
|
|
44
|
+
块后把函数契约拆成条款列表,是同一禁令的另一种写法。以 井字棋 落子校验节改稿前为对照:
|
|
45
|
+
|
|
46
|
+
````text
|
|
47
|
+
不佳(头文件注释已写清三个拒绝条件,块后仍逐条复述并解释返回值):
|
|
48
|
+
`PlaceCell` 返回 `true` 表示放置成功,返回 `false` 表示拒绝。
|
|
49
|
+
|
|
50
|
+
- **放置被拒**:返回 `false`,棋盘保持原样。坐标越界或格子已被占用时换一个坐标;传入的类型是空格子时换一个非空类型。
|
|
51
|
+
- **放置成功**:返回 `true`,新棋子当场能从 `GetCellType` 读回来。
|
|
52
|
+
|
|
53
|
+
改写(注释覆盖契约,块后只留读者要做的判断):
|
|
54
|
+
`PlaceCell` 用返回值报告结果,三种拒绝都不改棋盘;调用方检查返回值就能决定下一步,不必比较落子前后的棋盘。
|
|
55
|
+
````
|
|
56
|
+
|
|
57
|
+
判定信号:块后列表项与 `@param`、`@return` 一一对应,删掉列表注释仍完整;返回值语义被正文重复解释两遍以上。允许保留的是注释没写的调用方策略:谁该重试、谁该结束、界面要显示什么。
|
|
58
|
+
|
|
59
|
+
## 块后交代注释布局
|
|
60
|
+
|
|
61
|
+
块后正文不交代「类说明没有逐个列出成员」「每个成员的用法写在行尾注释里」这类注释安排:它讲作者怎么组织注释,不讲程序做什么。读者需要的事实直接陈述。以 井字棋 棋盘类型节改稿前为对照:
|
|
62
|
+
|
|
63
|
+
````text
|
|
64
|
+
不佳(块后说明注释怎么分工,没有程序信息):
|
|
65
|
+
类说明没有逐个列出成员的用途,只说清这个类管什么、棋盘为什么可以随便拷贝。每个成员的用法写在各成员自己的注释里,两处不重复。
|
|
66
|
+
|
|
67
|
+
改写(整句删掉,前后段落已经交代了棋盘做什么):
|
|
68
|
+
(无块后段落)
|
|
69
|
+
````
|
|
70
|
+
|
|
71
|
+
判定信号:句子主语是「类说明」「注释」「行尾注释」而不是程序里的对象;删掉后读者对代码的理解不变。
|
|
72
|
+
|
|
44
73
|
## 块后悬空指代
|
|
45
74
|
|
|
46
75
|
块后第一次提到代码块里的常量怎么写:点名标识符,不用「这 N 个 X」。块后段落首次提及围栏里的实体时点名标识符;先行词只出现在代码围栏里、不在任何句中时,「这 N 个 X」「它们」式省称没有可依的先行词,计数指代还容易盖错范围。条文见 `steps/03-blocks.md`「内容块前后的闭环」。以 示例章定义坐标范围节改稿前为对照:
|
|
@@ -171,3 +171,13 @@ constexpr bool IsInRange(Position position) {
|
|
|
171
171
|
改写(两句:第一句接住上文对象,第二句沿用注释的动词和对象,位置写「定义在 state.h 里」):
|
|
172
172
|
操作写进状态面板之后,要知道这一步有没有分出胜负。`HasPlayerLine` 检查状态面板上是否已形成三连获胜线,定义在 `state.h` 里:
|
|
173
173
|
```
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
不佳(章引言一口气挂三个章节链接,读者还没读正文先被导航到别的页):
|
|
177
|
+
[程序结构](viewing-program.qmd) 把棋盘、对局和终端连成一份程序。棋盘是 3 行 3 列。接口写在 [board.h](…),实现放在 [board.cpp](…)。两个文件都在 [工程结构](02-mapping-layout.qmd) 一章。
|
|
178
|
+
|
|
179
|
+
改写(引言只留本章主源文件;跨章链接下沉到正文真正会用到它的句子):
|
|
180
|
+
井字棋棋盘是 3 行 3 列,共九个棋盘格子。`Board` 保存这些棋盘格子,并提供写入与读出的接口;接口写在 [board.h](…),实现放在 [board.cpp](…)。
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
判定信号:章引言(第一个 `##` 之前)出现两个及以上 `.qmd` 链接;除承接上一章的那一个之外,其余都在正文有对应的「首次真正用到」的句子可以迁入。`verifying-content.py` 对引言多余章节链接直接报错。
|
|
@@ -99,3 +99,16 @@
|
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
判定信号:章首或 `##` 首句的主语是下游对象(格子、对局、终端),而不是标题对象;把主语换成标题对象后句子仍成立,说明原句已经漂移。
|
|
102
|
+
## 重复交代可数事实
|
|
103
|
+
|
|
104
|
+
以棋盘章成员与接口节的一段为对照。引言已经写过「3 行 3 列,共九个棋盘格子」,类注释又写「3×3 个格子……格子数量不会改变」,块后散文再讲两遍「九个棋盘格子」「数量从开局到终局不变」「编译期就能确定」,读者读到的是一件事的三种说法:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
不佳(同一事实讲三遍,第二句和第三句还互相重复):
|
|
108
|
+
`Board()` 构造之后就是一张空棋盘。出界坐标不会写入棋盘;`Board` 保存九个棋盘格子的类型,数量从开局到终局不变。九个棋盘格子的数量编译期就能确定,所以 `cells_` 用二维 `std::array` 存放。
|
|
109
|
+
|
|
110
|
+
改写(只保留块后必须新给的实现事实:存放方式与下标含义):
|
|
111
|
+
`Board()` 构造出来是空棋盘。格子数在编译期确定,`cells_` 用二维 `std::array` 存放:外层是行,内层是列,`cells_[row][col]` 对应坐标 `(row, col)`。
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
自查:删掉重复句后,读者还缺哪一条事实?只补缺的那条;「数量不变」「出界不写入」若已在类注释或 `IsInRange` 的契约里写过,就不再复述。
|
|
@@ -180,3 +180,37 @@ enable_testing() 开启 CTest……。add_test 用 NAME 和 COMMAND……。set_
|
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
判定信号:删掉任一项后其余照常成立,那一项只把上句拆短;三项里有一项与另一项同义或只为对齐,读者从列表里取不到三条独立信息。
|
|
183
|
+
|
|
184
|
+
## 规则条件不编号
|
|
185
|
+
|
|
186
|
+
以“落子输入校验”为对照(规则见 `steps/07-layout.md`「列表选择」):
|
|
187
|
+
|
|
188
|
+
```text
|
|
189
|
+
不佳(三个并列条件写成编号步骤,读者以为要按顺序检查):
|
|
190
|
+
落子失败时:
|
|
191
|
+
|
|
192
|
+
1. 行列越界;
|
|
193
|
+
2. 目标棋盘格子已被占用;
|
|
194
|
+
3. 输入格式错误。
|
|
195
|
+
|
|
196
|
+
改写(规则条件写成一段,只有执行动作才编号):
|
|
197
|
+
行列越界、目标棋盘格子已被占用或输入格式错误时,程序说明原因,棋盘保持不变,随后要求重新输入。
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
不佳(把规则句塞进流程编号,一步里同时讲条件和动作):
|
|
202
|
+
1. 如果行列越界、格子已占用或格式错误,就提示;
|
|
203
|
+
2. 否则写入;
|
|
204
|
+
3. 重画棋盘。
|
|
205
|
+
|
|
206
|
+
改写(规则先成段,流程单独编号):
|
|
207
|
+
落子前先检查行列范围、目标格子和输入格式;任一项不通过就说明原因,棋盘不变并重新输入。
|
|
208
|
+
|
|
209
|
+
一轮落子:
|
|
210
|
+
|
|
211
|
+
1. 读入行号和列号;
|
|
212
|
+
2. 检查这次输入;
|
|
213
|
+
3. 写入棋盘格子,重画棋盘。
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
判定信号:编号项之间可以互换顺序而意思不变,说明它是并列条件而不是步骤;列表项以“如果 / 当 / 越界 / 占用”开头,或一项里同时出现条件和动作。规则、条件、允许和禁止用段落;只有“先做什么、再做什么”才是编号列表。
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
局部删除或移动内容后必须回读切口,并为后文仍引用的动作保留「触发时机 → 状态变化及原因 → 可观察结果 → 下一步」的最短闭环。后文出现悬空的「然后、因此、此时、重启、再次」等承接时必须重写切口,不能只按表面冗余删除过渡句。
|
|
30
30
|
|
|
31
|
-
改标题等于改锚点。改完运行 `scripts/verifying-content.py`
|
|
31
|
+
改标题等于改锚点。改完运行 `scripts/verifying-content.py`,再用 `quarto_render_check` 核对交叉引用;只有人工复现问题才在仓库根裸跑 `quarto render`。
|
|
32
32
|
|
|
33
33
|
## 配置文件与构建脚本
|
|
34
34
|
|
|
@@ -122,7 +122,7 @@ Callout 个数见 `steps/02-structure.md`,本段不写数字;`warning` 只
|
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
- 只有读者可以先自行回答、再核对结论的内容才折叠。定义、命令和必经判据留在正文。
|
|
125
|
-
- `.answer`
|
|
125
|
+
- `.answer` 由书内 `filters/answering-disclosure.lua` 渲染为默认收起的原生 `details.answer-disclosure`,摘要固定为「查看答案」。正文不手写这段 HTML。
|
|
126
126
|
- 答案只使用本章已经讲解的概念,内部不使用 `h1`–`h6`。
|
|
127
127
|
|
|
128
128
|
### 交叉引用
|
|
@@ -8,7 +8,7 @@ Mermaid 只承担流程图、分层图与调用链;坐标轴类网格图(状
|
|
|
8
8
|
|
|
9
9
|
Quarto 只执行 ` ```{mermaid} ` 属性围栏;plain 的 ` ```mermaid ` 围栏被当作普通代码块,渲染结果是语法高亮的源码而不是图。`verifying-content.py` 在禁用词扫描中拦截 plain 围栏。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
渲染核对方法:调用 `quarto_render_check`,在返回的 `html_report` 对应 HTML 中应出现 mermaid 图表容器(`mermaid-js` 或 `quarto-diagram` 类名),不应出现 `sourceCode mermaid` 高亮代码块。
|
|
12
12
|
|
|
13
13
|
## 块首固定 init 指令
|
|
14
14
|
|
|
@@ -14,18 +14,18 @@
|
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| `theme` | `light: [cosmo, theme/palettes/flexoki/theme-light.scss]` + `dark: [darkly, theme/palettes/flexoki/theme-dark.scss]` | 内置主题与项目样式叠加,顺序决定覆盖关系;Flexoki 是编译默认 |
|
|
16
16
|
| `highlight-style` | `light:`/`dark:` 指向 `theme/palettes/flexoki/highlight-light.theme` / `highlight-dark.theme` | 明暗分别指定,与 `palettes/flexoki/meta.md` 同步;Solarized 的语法色由 `theme/palettes/solarized/syntax.css` 在 `html[data-palette="solarized"]` 下覆盖,不改这里 |
|
|
17
|
-
| `filters` | `
|
|
17
|
+
| `filters` | `filters/answering-disclosure.lua`(顶层 `filters:`,书内副本) | 将正文 `.answer` 转为默认收起的原生 `details` |
|
|
18
18
|
| `toc` / `toc-depth` / `toc-location` | `true` / `3` / `right` | 右侧目录最多到 H3,窄屏会折叠,不作唯一定位手段 |
|
|
19
19
|
| `number-sections` | `false` | 因此标题不手填序号,见 `quarto/basics.md` |
|
|
20
20
|
| `code-copy` / `code-overflow` | `true` / `wrap` | 长行换行,不让读者横向拖动 |
|
|
21
|
-
| `grid` | sidebar 300 / body
|
|
21
|
+
| `grid` | sidebar 300 / body 872 / margin 272 / gutter 1rem | 正文列 840px,约 50 个汉字;四值是最大基准值,窄视口由 Quarto 响应缩放 |
|
|
22
22
|
| `lang` | `zh` | 影响部分 HTML 行为与提示框默认词 |
|
|
23
23
|
|
|
24
24
|
本书**不开启**代码行号(`code-line-numbers`)与代码折叠(`code-fold`)。站点 QMD 代码块使用 `{.语言 filename="标题"}`;Quarto 会把标题渲染为 `.code-with-filename`,外观与复制按钮位置由主题统一控制。filename 缺失由 `verifying-content.py` 拦截,内容语义与篇幅由 `verifying-content.py` 负责。
|
|
25
25
|
|
|
26
26
|
### 改动约定
|
|
27
27
|
|
|
28
|
-
1. 改 `format: html:`
|
|
28
|
+
1. 改 `format: html:` 任何取值都属于主题级改动,会触发整本渲染,先确认代价,再用 `quarto_render_check` 渲染。
|
|
29
29
|
2. 新增或删减主题样式表(`.agents/skills/designing-theme/assets/theme/css/**` 或书内 `theme/css/**`)时,同步修改配置里的样式表清单,否则新样式不参与渲染。
|
|
30
30
|
3. 页面视觉问题(提示符配色、文件名条、术语色)改 `.agents/skills/designing-theme/assets/theme/**` 或书内 `theme/**` 与设计令牌,不在文档里内联样式,也不改高亮配置。主题系统详见 `../designing-theme/references/theme-system.md`。
|
|
31
31
|
4. 流程图使用 `{mermaid}` 属性围栏,配色由主题样式控制。配置里不指定图表主题名;围栏与 init 指令细则见 `quarto/mermaid.md`。
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
|
|
152
152
|
- include 路径相对于 qmd 文件所在目录解析(`content/<part>/` 下用 `../../src/...` 指向仓库根的 `src/`)。
|
|
153
153
|
- 语言标注决定语法高亮:Python 用 `python`,C++ 用 `cpp`,Shell 用 `bash`。
|
|
154
|
-
- 新增或修改 include
|
|
154
|
+
- 新增或修改 include 后必须用 `quarto_render_check` 渲染,并核对返回的 `html_report`:嵌入内容应出现在 `<pre class="sourceCode ...">` 代码块中,而不是 `<p>` 段落里。
|
|
155
155
|
- include 展示的是 `src/` 下真实源文件,渲染问题只修 qmd 的包裹方式,不改被引用的源文件。
|
|
156
156
|
|
|
157
157
|
## 链接使用约定
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# 表格
|
|
2
|
+
|
|
3
|
+
表格只回答「多个对象按同一组属性怎么比较」。并列步骤用有序列表,两个对象两三条区别用句子;表格一旦只装一两列信息,读者会把它当成大号列表读。
|
|
4
|
+
|
|
5
|
+
## 何时写表
|
|
6
|
+
|
|
7
|
+
- **保留**:三个及以上对象、且每条对象都有两项以上可比属性;属性在各行里位置相同,读者能纵向对照。
|
|
8
|
+
- **改列表或句子**:只有一两列、或右侧大量留白;对象之间没有共同属性,只是并列罗列。
|
|
9
|
+
- **不重复代码**:接口表只写签名读不出的差异(返回什么、边界怎么处理),不把 `@param`、`@return` 再抄一遍。
|
|
10
|
+
|
|
11
|
+
## 固定写法
|
|
12
|
+
|
|
13
|
+
一张表由三段构成,缺一段就是块悬空:
|
|
14
|
+
|
|
15
|
+
1. **表前导语**:一句完整的话,点名要比较的对象与属性,说明为什么此刻看这张表。不写「如下表」「见下表」式空引导,不写「N 个接口」式元计数。
|
|
16
|
+
2. **表格本体**:表头写属性名,首列写被比较的对象;一行一个对象。
|
|
17
|
+
3. **表后结论**:一句读者该取走的判断,如共同约定、例外或分类依据。不复述格子里的字面值。
|
|
18
|
+
|
|
19
|
+
代码块、图、列表与表格之间不能直接相接:中间至少隔一段正文,写清两者关系(代码块给出定义,表格归纳接口差异)。连续两个盒子会让读者分不清边界,也只能靠滚动定位。
|
|
20
|
+
|
|
21
|
+
## 表头与列宽
|
|
22
|
+
|
|
23
|
+
- 表头由主题统一加粗、加浅底色:`.table thead th` 已设 `font-weight: 700`。正文不写 `**接口**` 式加粗,也不写「表头加粗」说明。
|
|
24
|
+
- 列宽只给**相对比例**,不给像素:
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
| 接口 | 返回 | 边界 |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `GetCellType` | 坐标处的 `CellType` | 越界时抛出 `std::out_of_range` |
|
|
30
|
+
: {tbl-colwidths="[24,30,46]"}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
比例按内容分配:名称与接口列 18–26,返回列 22–32,说明列 34–50,三项合计 100。三列以上时最长列不超过 55,避免其余列被挤成一条线。
|
|
34
|
+
- 不写固定宽度(`{width=...}`、`style="width:..."`)。窄屏下整张表横向滚动,列宽仍随内容分配;`table-layout: auto` 是主题默认值,不要覆盖成 `fixed`。
|
|
35
|
+
- 长单元格先改文案:删重复修饰,把条件与结果按「条件 + 结果」排成一句。仍长时允许格内折行,`overflow-wrap: break-word` 已由主题处理。
|
|
36
|
+
|
|
37
|
+
## 单元格
|
|
38
|
+
|
|
39
|
+
- 一格一个事实,对象先行。不写「名词:一句」的伪列表,列已经分出属性名。
|
|
40
|
+
- 行内代码只放读者要输入的标识符或类型名;一格最多两个,多了会把列撑宽,也让比较点被代码淹没。
|
|
41
|
+
- 空值与未知写「无」或「不判断」,不写 `-`、`N/A`。
|
|
42
|
+
- 数字右对齐、文本左对齐由主题按列决定;不要在单元格里手动空格对齐。
|
|
43
|
+
- 中英混排留半角空格,标点用全角,规则见 `quarto/prose.md`。
|
|
44
|
+
|
|
45
|
+
## 表题与引用
|
|
46
|
+
|
|
47
|
+
- 需要正文引用时加表题:表格下一行写 `: 表 3-1 棋盘接口的返回与边界 {#tbl-board-api}`,表题随主题显示在表上方、小一号。
|
|
48
|
+
- 表题只写对象与内容,不写「如下表」「本表说明」。
|
|
49
|
+
- 同一页表题编号连续;正文引用写「表 3-1」,不写「下表」。
|
|
50
|
+
|
|
51
|
+
## 正反例
|
|
52
|
+
|
|
53
|
+
**反例**(代码块后直接接表,表前没有过渡):
|
|
54
|
+
|
|
55
|
+
```markdown
|
|
56
|
+
| 接口 | 返回 | 边界 |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**正例**:
|
|
61
|
+
|
|
62
|
+
```markdown
|
|
63
|
+
三个只读接口都只回答棋盘状态,区别在返回什么、越界怎么处理:
|
|
64
|
+
|
|
65
|
+
| 接口 | 返回 | 边界 |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `GetCellType` | 坐标处的棋盘格子类型 | 越界时抛出 `std::out_of_range` |
|
|
68
|
+
| `IsCellEmpty` | 空格子返回 `true` | 越界或已有棋子时返回 `false` |
|
|
69
|
+
|
|
70
|
+
需要提前判空的调用方用 `IsCellEmpty`,已经做过范围检查的调用方用 `GetCellType`。
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 检查清单
|
|
74
|
+
|
|
75
|
+
- [ ] 表前有导语、表后有一句结论,两句都不复述格子内容。
|
|
76
|
+
- [ ] 代码块、图、列表与表之间隔了一段正文。
|
|
77
|
+
- [ ] 表头没有手写加粗;列宽是相对比例,最长列不超过 55%。
|
|
78
|
+
- [ ] 一格一个事实,没有「名词:说明」和手动空格对齐。
|
|
79
|
+
- [ ] 短表已改回句子或列表。
|
|
@@ -20,6 +20,9 @@ import sys
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
|
|
22
22
|
LINK_RE = re.compile(r"\[[^\]]*\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)")
|
|
23
|
+
REF_USE_RE = re.compile(r"(!?)\[([^\]]*)\]\[([^\]]*)\]")
|
|
24
|
+
REF_DEF_RE = re.compile(r"^ {0,3}\[([^\]]+)\]:\s*(?:<([^>\s]+)>|(\S+))")
|
|
25
|
+
INLINE_CODE_RE = re.compile(r"`+[^`]*`+")
|
|
23
26
|
HTML_LINK_RE = re.compile(r"<(?:a|img)\b[^>]*\b(?:href|src)=\"([^\"]+)\"", re.IGNORECASE)
|
|
24
27
|
FENCE_RE = re.compile(r"^\s*(```+|~~~+)")
|
|
25
28
|
SKIP_PREFIXES = ("http://", "https://", "mailto:", "tel:", "data:", "#", "//")
|
|
@@ -49,7 +52,13 @@ def collect_sources(root: Path, paths: list[str]) -> list[Path]:
|
|
|
49
52
|
)
|
|
50
53
|
|
|
51
54
|
|
|
52
|
-
def
|
|
55
|
+
def normalize_label(label: str) -> str:
|
|
56
|
+
return re.sub(r"\s+", " ", label.strip()).casefold()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def collect_definitions(text: str) -> dict[str, tuple[int, str]]:
|
|
60
|
+
"""收集引用式链接定义 `[label]: target`;围栏内的定义不算。"""
|
|
61
|
+
definitions: dict[str, tuple[int, str]] = {}
|
|
53
62
|
in_fence = False
|
|
54
63
|
for number, line in enumerate(text.splitlines(), start=1):
|
|
55
64
|
if FENCE_RE.match(line):
|
|
@@ -57,10 +66,37 @@ def iter_links(text: str):
|
|
|
57
66
|
continue
|
|
58
67
|
if in_fence:
|
|
59
68
|
continue
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
match = REF_DEF_RE.match(line)
|
|
70
|
+
if not match:
|
|
71
|
+
continue
|
|
72
|
+
label = normalize_label(match.group(1))
|
|
73
|
+
target = match.group(2) or match.group(3) or ""
|
|
74
|
+
if label and label not in definitions:
|
|
75
|
+
definitions[label] = (number, target)
|
|
76
|
+
return definitions
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def iter_links(text: str, definitions: dict[str, tuple[int, str]]):
|
|
80
|
+
in_fence = False
|
|
81
|
+
for number, line in enumerate(text.splitlines(), start=1):
|
|
82
|
+
if FENCE_RE.match(line):
|
|
83
|
+
in_fence = not in_fence
|
|
84
|
+
continue
|
|
85
|
+
if in_fence or REF_DEF_RE.match(line):
|
|
86
|
+
continue
|
|
87
|
+
# 行内代码里的方括号不是链接;先抹掉,避免把示例当引用。
|
|
88
|
+
visible = INLINE_CODE_RE.sub(lambda match: " " * len(match.group(0)), line)
|
|
89
|
+
for match in LINK_RE.finditer(visible):
|
|
90
|
+
yield number, match.group(1), None
|
|
91
|
+
for match in REF_USE_RE.finditer(visible):
|
|
92
|
+
label = match.group(3) or match.group(2)
|
|
93
|
+
key = normalize_label(label)
|
|
94
|
+
if not key:
|
|
95
|
+
continue
|
|
96
|
+
definition = definitions.get(key)
|
|
97
|
+
yield number, definition[1] if definition else None, label
|
|
98
|
+
for match in HTML_LINK_RE.finditer(visible):
|
|
99
|
+
yield number, match.group(1), None
|
|
64
100
|
|
|
65
101
|
|
|
66
102
|
def main() -> int:
|
|
@@ -94,7 +130,12 @@ def main() -> int:
|
|
|
94
130
|
print(f"失败 {source} 不是合法 UTF-8:{exc}", file=sys.stderr)
|
|
95
131
|
return 1
|
|
96
132
|
rel = source.relative_to(root).as_posix()
|
|
97
|
-
|
|
133
|
+
definitions = collect_definitions(text)
|
|
134
|
+
for number, raw, label in iter_links(text, definitions):
|
|
135
|
+
if raw is None:
|
|
136
|
+
checked += 1
|
|
137
|
+
broken.append((rel, number, f"[{label}]", "引用标签未定义"))
|
|
138
|
+
continue
|
|
98
139
|
target = raw.strip()
|
|
99
140
|
if not target or target.startswith(SKIP_PREFIXES):
|
|
100
141
|
continue
|
|
@@ -624,6 +624,28 @@ def check_file(path: Path, root: Path, project: dict, chapters: list[str]) -> li
|
|
|
624
624
|
f"同章重复文件链接:{target}(首次在第 {numbers[0]} 行);每章每条仓库路径至多一条",
|
|
625
625
|
))
|
|
626
626
|
|
|
627
|
+
# 章引言(第一个标题之前)只留承接上一章的一个章节链接;其余章节引用下沉到正文。
|
|
628
|
+
# 着陆页 index.qmd 是目录页,不受这条预算约束。
|
|
629
|
+
if path.name != "index.qmd":
|
|
630
|
+
first_heading = next(
|
|
631
|
+
(n for n, raw in enumerate(lines, start=1) if HEADING_RE.match(raw)),
|
|
632
|
+
len(lines) + 1,
|
|
633
|
+
)
|
|
634
|
+
intro_links: list[tuple[int, str]] = []
|
|
635
|
+
for index, raw in enumerate(lines, start=1):
|
|
636
|
+
if index >= first_heading or index in fenced_lines:
|
|
637
|
+
continue
|
|
638
|
+
for match in LINK_RE.finditer(raw):
|
|
639
|
+
target = match.group(1).strip()
|
|
640
|
+
if target.endswith(".qmd"):
|
|
641
|
+
intro_links.append((index, target))
|
|
642
|
+
if len(intro_links) > 1:
|
|
643
|
+
issues.append(Issue(
|
|
644
|
+
path, intro_links[1][0],
|
|
645
|
+
f"章引言有 {len(intro_links)} 个章节链接({'、'.join(t for _, t in intro_links)});"
|
|
646
|
+
"只留承接上一章的一个,其余移到正文首次用到处",
|
|
647
|
+
))
|
|
648
|
+
|
|
627
649
|
for term in GENERIC_FORBIDDEN_TERMS:
|
|
628
650
|
for index, raw in enumerate(lines, start=1):
|
|
629
651
|
if term in raw and not raw.lstrip().startswith(("```", " ")):
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
**章际衔接(定结构检查项)**:第 N 章的引言、任务节与自测题只用第 1..N-1 章已建立的名字与概念;本章新术语在首次真正使用处就近落术语卡或行内括号;术语卡的升级判据见 `writing-quarto` 技能 `quarto/authoring.md`「Callout 升级判据」,写法见 `steps/03-blocks.md`。某概念接不上前章已建立的名字时,把它下沉回首次使用它的章建立,不在本章临时补课。
|
|
19
19
|
|
|
20
|
-
- **无标题引言**:第一个 `##` 之前,写成**连贯读者文**回答读者三问——从哪来(承接上一章已成立的结果)、这一章做什么(本章要解决的问题与读者将获得的能力,正面陈述;范围能并则并,不写否定式后章预告)、代码在哪(可选:若本章有主源文件,用**同段末句**自然语言点名并挂全章 blob 链接,不另起「代码写在…里」独立段)。第一句写出谁在什么情况下做本章这件事,类章同时写出类名;类型怎么实现放到后面的句子。不另开背景节。**形态硬限(用户决定)**:首选**恰好 1 个自然段**(通常 2–4 句);必要时允许 **1 个自然段 + 1 个列表**(列表前引导句写在同一段末,或段落后紧跟列表;列表不算第二散文段);**禁止**出现两个散文自然段并立,更禁止 ≥3 段、单句孤儿段、结尾导航段。列表仅用于并列范围/对象,任务细节下沉正文 `##`,不用顿号串交付物冒充散文。**摘录免责句永不写进读者正文**:「本章摘录源码服务于讲解」「片段不保证与源文件逐字一致」「不等于文件内顺序」类作者旁白一律删除(旧「示例位置句开口子」已废止),同源保证交给 `verifying-content.py`;摘录免责只保留在 技能的权威文件与检查脚本里。不放 Callout,不写后续章节内容,不用「后面的 X」式前向指称引入后续章节才建立的结构件(如「实现放在后面的编号目录里」)。引言容纳多个独立概念时**并段或改列表**,禁止拆成两个散文段;拆分判据见 `steps/04-paragraphs.md`「拆分判据」。内容量硬限:token 预算见本文件「分层上限」;范围边界能并入承接段时不成列表。承接中引用其他章节一律用章节标题链接,不用「第 N
|
|
20
|
+
- **无标题引言**:第一个 `##` 之前,写成**连贯读者文**回答读者三问——从哪来(承接上一章已成立的结果)、这一章做什么(本章要解决的问题与读者将获得的能力,正面陈述;范围能并则并,不写否定式后章预告)、代码在哪(可选:若本章有主源文件,用**同段末句**自然语言点名并挂全章 blob 链接,不另起「代码写在…里」独立段)。第一句写出谁在什么情况下做本章这件事,类章同时写出类名;类型怎么实现放到后面的句子。不另开背景节。**形态硬限(用户决定)**:首选**恰好 1 个自然段**(通常 2–4 句);必要时允许 **1 个自然段 + 1 个列表**(列表前引导句写在同一段末,或段落后紧跟列表;列表不算第二散文段);**禁止**出现两个散文自然段并立,更禁止 ≥3 段、单句孤儿段、结尾导航段。列表仅用于并列范围/对象,任务细节下沉正文 `##`,不用顿号串交付物冒充散文。**摘录免责句永不写进读者正文**:「本章摘录源码服务于讲解」「片段不保证与源文件逐字一致」「不等于文件内顺序」类作者旁白一律删除(旧「示例位置句开口子」已废止),同源保证交给 `verifying-content.py`;摘录免责只保留在 技能的权威文件与检查脚本里。不放 Callout,不写后续章节内容,不用「后面的 X」式前向指称引入后续章节才建立的结构件(如「实现放在后面的编号目录里」)。引言容纳多个独立概念时**并段或改列表**,禁止拆成两个散文段;拆分判据见 `steps/04-paragraphs.md`「拆分判据」。内容量硬限:token 预算见本文件「分层上限」;范围边界能并入承接段时不成列表。承接中引用其他章节一律用章节标题链接,不用「第 N 章」;**链接预算**:引言只挂承接上一章的那 1 个章节链接和本章主源文件的 blob 链接,其余章节链接下沉到正文真正用到它的句子,每处只挂当下需要的那个目标,同一段不堆多个章节链接(Google 技术写作指南把每个链接视为一次读者可能离开当前页的机会,链接越多,认知负担越重);本章回顾的下一章预告不写链接,直述预告一句(页面导航已有前进按钮),两者均见 `steps/06-wording.md`。任务概览默认并入承接句,正文小节就是任务的展开;预览列表仅当任务没有对应正文小节时使用(示例章四项工作列表已按此删除)。原 front matter `description` 的定位信息并入引言承接句,信息只保留引言一处、不同义两存;章节 qmd 不写 `description`(字段出处见 basics.md)。着陆页 `index` 按 landing 规则组织,不硬套本契约。
|
|
21
21
|
- **任务序列**:每节一个可验证结果。判定标准是任务结束时存在一个读者能自己复述的检查动作;只有「读完了解释」的内容并入相邻任务。概念 → 最小示例 → 操作 → 验证 → 边界,先让读者看到行为再解释规则;单次操作按「做什么 → 执行什么 → 看到什么 → 简短说明」推进。各章的任务节以该章标题里的对象为准,不把每一章都写成「目录、设计、构建、测试」四节。设计范围以这一章能在 `src/` 里核对的源文件收束。内容量硬限:任务级 `##` 通常不超过 4 个;`##` 与 `###` 开头的承接句数见本文件「分层上限」,写完直接进入代码、命令或列表;超出即为凑字数信号,禁令见 `steps/04-paragraphs.md`「凑字数禁令」。有 `###` 子节时,二级标题(`##`)的引言须承接上文;恰好 2 个子节时一句点一个,3 个或 4 个时不再逐个点名,点名交给三级标题的句式见本文件「标题分层」,点名用读者已认识的词,不点后文才定义的类型名;一句之内禁止把两个子节对象配成「由 X 定位,并保存 Y」「分别交给」式的两组对应(对照见 `examples/05-sentence-frame.md`「拉链句」)。交付清单或纯边界说明不单独设 `##`,并入引言或相邻工程总览节;同一头文件中连续的值类型优先按一个可验证结果归并到同一任务节。任务节内的讲述顺序按「类型 → 简单函数 → 更难的函数」排列,最难的组装放在最后,并满足先定义后调用:读者要跟着读懂的行内代码标识符,其行为必须已经出现过——上一章、上文,或同一小节里紧挨着的声明和一句行为——上一章已建立的名字视为已知;未定义名字的调用链、节首概览与编号步骤整段删除。`##` 导语只用白话,不点后文才定义的函数名。章内顺序的依据见 `SKILL.md`「九步」的定结构一条,本文件不复述。
|
|
22
22
|
- **常见错误**:服务失败后回来的读者。每个症状一个 `###`,标题后第一句写触发条件和可观察现象。误用型条目一段成文写原因与正确做法;原因与做法各需一句完整交代时(如原因牵涉私有段的设计动机),用症状句导语加「出错的原因/正确的做法」引导词列表,列表可作为节导语的合法收尾,导语预算只约束症状句;排查型故障展开定位 → 修复 → 验证三步。形态分野与细则是 `writing-quarto` 技能 `quarto/terminal.md`「常见错误条目形态与三步诊断流」。入门章节只保留最可能遇到的 2-3 项,不是完整 FAQ。
|
|
23
23
|
- **自测问题**:服务刚跑通主线、需要确认理解的读者。每章 0-7 道题,题目问本章已经写明的一件可观察结果(会怎样、是哪一个、从几开始),没有这种结果就不设题;只有本章写了原因、而且情景已经说全时,才用「为什么」。题面用本章已经用过的词说全,不用本章未定义的概念,不问「为什么不做成另一种」;题干是合上书的一问,只看本题题干、不看答案与上一题;题干代词的判法见 `steps/05-sentences.md`「代词不能代替未写出的对象」,允许「什么」——它在要一个名字或一句文字,不是指上文没出现的东西;题面里每个细节都要改变答案,答案不受细节影响时不单点某一种情形。题面只描述一个自洽局面,每个事实都要能同时成立,禁止把互相矛盾的局面写成题干(同一句里先说轮到甲方、又说这一步的用户是 O,读者听到的是程序自相矛盾,不是一次非法调用)——「轮到谁」和「这一步的用户」若是同一个对象,只写一次。要考拒绝,用正文已经用过的那条规则问可观察结果(正例:「空状态面板开局,轮到甲方。没轮到的一方来操作时,状态面板会改变吗?」,答案首句「不会。操作的用户不是当前用户,`Place` 返回 `kRejected`,状态面板和轮次都保持原样。」);不为了出题制造正文没走过的矛盾局面。一道题只问一件可观察结果,不把两个观察捆进同一题(如「状态面板是空的吗」与「轮到谁」并列),拆开或删去一个。固定导语「不要展开答案,试着自己把回答完整说出来,写完之后展开对照;说不出来的部分,回到对应小节对照正文。」;答案写在 `::: {.answer}` 围栏 div 内,由 `scripts/answering-disclosure.lua` 渲染为默认收起的原生 `<details>`;答案只使用本章已讲过的概念。题目同样必须能由本章正文回答:答案依据须是正文已写明的内容,正文没讲的概念不设题;若问题值得问,先在正文补上依据再设题。答案先答所问:首句直接回答题面,一到两句,不补题面之外的对比、延伸或重复题面(对照与归属由正文承载)。问答中的文件、路径与标识符指称统一用行内代码,不挂链接(`verifying-content.py` 对自测节链接记 WARN);所属目录在答案前文已确立时,路径可省去重复的顶层前缀(如先写 `example/`,后文写 `include/state.h`)。
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
渐进披露不进正文:语法细节延后展开是 Agent 侧写作策略,禁止把策略写成对读者的话——「语法细节留到后续章节真正用到时再展开」「本章只需认清……」这类元叙述一律不出现在 `content/**`;正文直接陈述对象和结果(如「这段程序对外可见的结果只有两处:……」),需要延后的内容保持沉默即可。改写对照见 `examples/06-wording-cases.md`,禁用词见 `steps/06-wording.md`。
|
|
31
31
|
|
|
32
|
-
列表与表格同律:表格前用一句完整的话交代它回答什么问题,表格后补一行读者该取走的结论;列表通则见 `steps/04-paragraphs.md
|
|
32
|
+
列表与表格同律:表格前用一句完整的话交代它回答什么问题,表格后补一行读者该取走的结论;列表通则见 `steps/04-paragraphs.md`「段落规则」。表格还必须与上一个代码块、图或列表之间隔一段正文,写清两者关系:定义刚给出就接对照表,读者对不上表头和围栏。表头、列宽比例与单元格写法见 `quarto/tables.md`。命令、输出与正文三分见本文件「命令、输出与细节各司其职」。
|
|
33
33
|
|
|
34
34
|
多命令块:块后逐条讲解的格式见 `steps/04-paragraphs.md`「段落规则」,引导词加粗与行内代码不叠加的边界见 `steps/07-layout.md`。引言与收束按上段列表通则,不再按命令分段成多个段落。
|
|
35
35
|
|
|
@@ -57,7 +57,7 @@ int main() {
|
|
|
57
57
|
- **filename 不写链接**:`filename="..."` 是纯文本短名,禁止塞 Markdown 链接;`resolve_fragment_source` 的解析只认正文句子里的链接,标题栏写链接既渲染不出可点样式,也会被 `verifying-content.py` 判失败。
|
|
58
58
|
- **blob 链接嵌进导语句**:GitHub blob 链接写在同节导语的完整句子里(正例:「apps/CMakeLists.txt 用一行目标属性固定运行时库查找」,其中文件名是 GitHub 链接的锚文本),禁止整行仅为链接单独成行贴在代码围栏上方(可夹空行,`verifying-content.py` 判失败)。悬空的裸链接行既不像正文也不像说明,读者扫读时只剩一个锚点。
|
|
59
59
|
- **摘录链接每章每条仓库路径至多一条**:章引言是全章链接位,首次点名源文件并挂链接(如「声明在头文件 `state.h`,实现在源文件 `state.cpp`」,这两个名字是该章仅有的源文件链接);后文和各节导语改成行内代码,不再包 `[]()`,导语讲块里要看的标识符;同一章摘录另一阶段目录的同名文件时,在切换处再挂一条不同 URL(对照见 `examples/03-excerpt-lead.md`「摘录链接密度」;`verifying-content.py` 对同章同 URL 重复判失败)。本条挂在写作步骤的定版式。
|
|
60
|
-
- 片段内注释硬限:每处实现注释(`//` 或 `#`)不超过
|
|
60
|
+
- 片段内注释硬限:每处实现注释(`//` 或 `#`)不超过 3 行(与源文件注释规范的 1–3 行同一口径)、单行不超过 100 列(与源文件 `ColumnLimit: 100` 同一口径;正文散文的 40 字限不套到代码注释),只写这段代码做什么;CMake/Shell 等命令型片段每条命令保留一行意图注释。Doxygen `///` 声明块(概述、空 `///` 与细节)是声明骨架的一部分,摘录随源保留,不受此限。命名惯例、官方建议、未来演化等规范说理一律不进代码块;术语卡和可跳过的操作提醒可以进 callout,注释怎么写、行尾先写什么、`///` 与 `@` 标签怎么分工只留在 `.agents`,面向作者的进知识文件。
|
|
61
61
|
- 首讲某段源码的章节不省略源码既有的职责注释:注释是读者读到的第一层讲解;「注释与空行可以省略」只服务后续章节复现已见代码。源码缺注释时交给项目 C++ 流程补齐,本技能不替源码补注释。
|
|
62
62
|
- 讲解段只解释片段中实际出现的代码;片段没有展示的内容要么补进片段、要么进 callout,不在正文列表里讲代码外面的东西。
|
|
63
63
|
- include 短代码降级为可选手段:仅用于确实需要完整展示且很短的文件,并仍须包在带语言标注的围栏代码块内(包裹规则见 `quarto/render.md`)。
|
|
@@ -78,7 +78,19 @@ int main() {
|
|
|
78
78
|
|
|
79
79
|
摘录 `class` 或多函数片段时**保留**源码中函数之间的空行,禁止为省行数坍缩成函数注释贴着下一声明;省略部分成员时,被保留的相邻函数之间仍空 1 行;同一 access 段内最后一组数据成员与函数之间空 1 行,数据成员组内不空行。与「逐字摘录」冲突时以源码空白为准:教学缩略可删成员,不可删「函数间距」惯例。
|
|
80
80
|
|
|
81
|
-
###
|
|
81
|
+
### 正文与代码注释分工
|
|
82
|
+
|
|
83
|
+
代码块是源文件的逐字摘录,它承载理解这段代码所需的细节:契约、设计理由、边界与失败方式都留在块内注释里。正文不再转述这些细节,只写读者必须带走的判断。
|
|
84
|
+
|
|
85
|
+
- **块内注释负责细节**:`///` 契约、`@param`/`@return`、实现注释里的次序依赖与边界,都随摘录保留。正文删掉注释已写明的返回值和参数含义。
|
|
86
|
+
- **正文只写三类信息**:为什么这样设计(取舍)、读者接下来要用它做什么、几个块之间的工程关系。一条简单的 `return`、一个参数名、一个枚举取值,正文一句带过或直接不写。
|
|
87
|
+
- **不复述成列表**:把注释里的三条契约改写成正文三条项目符号,等于把代码读了两遍。需要并列判断时,先看能不能并成一句;并不成再用列表。
|
|
88
|
+
- **不写注释元叙述**:正文不交代「类说明没有逐个列出成员」「每个成员的用法写在行尾注释里」「理由写在类型注释里」这类关于注释怎么写的策略;读者需要的事实直接陈述,作者怎么组织注释是技能规则,不进正文。
|
|
89
|
+
- **正文长度随注释密度反向走**:块内注释越完整,块后段落越短。块后只剩一句读者该取走的结论时,就只写一句,不必凑段。
|
|
90
|
+
|
|
91
|
+
改写对照见 `examples/03-block-after.md`「块后复述注释」。
|
|
92
|
+
|
|
93
|
+
## 三级标题最小模块
|
|
82
94
|
|
|
83
95
|
一个三级标题下面的正文由一到两个最小模块组成。一个模块是一个整体,次序固定:标题已经给出对象;标题下一句导语;有并列取值时跟着列表,一项一个取值、一项一句;代码块、表格或图放在列表和收束段之间;没有列表时放在导语和收束段之间;收束段只写各项共同的契约;Callout 可有可没有,写了就放在收束段之后。短模块可以只有导语和代码块。示例章「操作与成败」里的 `Move` 与 `PlaceResult` 各是一个短模块,下一个短模块用自己的导语接上。第二个模块用来解释前一个时,衔接句就是第二个模块的导语。示例章「数据项坐标」用「下面的图标出这块状态面板上可用的 `(row, col)`。」接上图。讲一个函数时,先一个模块讲 `.h`,再用导语接 `.cpp`。列表写选法,围栏与源文件逐字一致,收束段不把列表再写一遍。禁止「每个状态码对应」,也禁止以 `` `类型::kName`: `` 开头的释义项。正面例子是 示例章「操作的强度」。
|
|
84
96
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
- 每段只有一个主题或中心句;中心句放在段首,后面的句子都服务于它。段内句子改完后,段首仍是中心句,一段仍只有一个对象;句子的六个成分见 `steps/05-sentences.md`「六个成分」。
|
|
13
13
|
- 一段一对象:同一段不夹杂两个并列对象,对象切换必须新起段;配套列表只服务本段对象,不得跨对象拼装列表项(对照见 `examples/03-block-after.md`「单段混并列对象」)。
|
|
14
14
|
- 段落不超过 7 行,最佳 4 行以内;超过即拆或改列表。
|
|
15
|
+
- 同一可数事实全章只交代一次:格子数、尺寸、类型、成员数量这类事实第一次出现时写全(如引言里的「3 行 3 列,共九个棋盘格子」),后面用「这些格子」「其余格子」回指,不复述数字或同一个名词短语;代码块前后的散文也不重复块内注释已经写过的数量与类型(对照见 `examples/04-paragraph-cases.md`「重复交代可数事实」)。
|
|
15
16
|
- 多个独立概念禁止塞进同一段:承接、目标、范围边界、阅读顺序、术语解释各归各段。
|
|
16
17
|
- 段落之间用一个空行分隔,段首不缩进。
|
|
17
18
|
- 代码块后的逐条讲解用无序列表:每项格式为「行内代码标识符 + 全角冒号 + 一句职责」(如 `` `enable_testing()`:开启 CTest。``),描述从简,全页标记方式一致;列表前保留一句引言交代工程语境,列表后需要时补一句收束或尾置 Callout。有先后顺序的排查步骤仍用有序列表;块前导语直接点名对象做什么,不用「先…再…最后…」,元计数禁令见 `steps/03-blocks.md`。
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
- **无序列表**:并列概念、范围边界、要点枚举——各项之间没有先后或因果;代码块后的逐条讲解格式见 `steps/04-paragraphs.md`「段落规则」。
|
|
22
22
|
- **有序列表**:有先后顺序的步骤或链路——执行顺序本身就是信息(如配置 → 编译 → 测试 → 运行)。
|
|
23
|
+
- **规则和条件不编号**:并列的许可、禁止、判定条件写自然段或分句;只有读者需要按顺序执行动作时才用编号。把「越界 / 占用 / 格式错误」写成 1、2、3,会让读者以为要按顺序检查。一段同时有规则和流程时,规则先成段,流程另起有序列表。
|
|
23
24
|
- 列表项结构平行:起首用同类的词(都是名词短语,或都是动词短语),长度相近。
|
|
24
25
|
- **列表字数带**:同一列表各项保持视觉齐长,避免扫读节奏断裂。计量:引导词汉字数、冒号后汉字数(行内代码按标识符计)。失衡判定:冒号后最长 ≥ 最短 × 2,或引导词相差 ≥ 4。改写顺序:删长项多余中心 → 拆多中心 → 过短项按简洁边界补全 → 停止。豁免:出错原因/正确做法;有序步骤;着陆页卡片。列表齐长服从句段主规则,假拆禁令见 `steps/04-paragraphs.md`。
|
|
25
26
|
- 列表项起首用加粗引导词标记中心语,读者扫读时先看到分类再决定是否细读;关键词取同类词(如「显示状态面板」「读入坐标」),并按对象的发生顺序排列,读者扫读即得顺序与内容。
|
|
@@ -52,13 +53,16 @@
|
|
|
52
53
|
- 华为《中文技术文档写作风格指南》:标题层级与句式;段落长度条文统一指向 `steps/04-paragraphs.md`。
|
|
53
54
|
- GB/T 15834—2011《标点符号用法》:顿号层级与标点用法。
|
|
54
55
|
- 阿里 f2e-spec writing:中英混排与标点。
|
|
56
|
+
- Microsoft Writing Style Guide「Lists」:列表 2–7 项、结构平行;编号只用于有顺序的步骤或优先级。
|
|
57
|
+
- ONS content style guide「Lists」:编号列表只给必须按顺序执行的步骤,其余并列用无顺序列表。
|
|
58
|
+
- Quarto「Lists」:连续列表项之间保留空行;编号与项目符号按语义选择。
|
|
55
59
|
|
|
56
60
|
### 格式取舍
|
|
57
61
|
|
|
58
62
|
- 行内代码与平台名词的版式判定见 `steps/02-structure.md`。
|
|
59
63
|
- 可复制命令使用指定语言的代码围栏;命令与输出分开的判定见 `steps/02-structure.md`。
|
|
60
64
|
- 标题与导航不用行内代码的判定见 `steps/02-structure.md`;中英文之间的空格见 `steps/06-wording.md`。
|
|
61
|
-
-
|
|
65
|
+
- 列表用于并列或顺序关系,选择判定见本文件「列表选择」。表格只用于需要对多个属性进行比较的内容:同一主题的属性合并成一张表,不按小节拆成多张薄表;只有一两列信息或右侧大量留白时改用句子或列表。表头、列宽比例与表格前后的过渡见 `quarto/tables.md`。列表是版式选择,不是新的一段说明。
|
|
62
66
|
- Callout 只承载可跳过提示的判定见 `steps/03-blocks.md`。
|
|
63
67
|
|
|
64
68
|
具体元素写法见 `quarto/authoring.md`,终端步骤见 `quarto/terminal.md`。
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
|
|
60
60
|
### 润色流程:沿着步骤走
|
|
61
61
|
|
|
62
|
-
润色任务沿 `SKILL.md` 的写作步骤执行,从定任务走到通过检查,跳步视为未完成;判定标准见 `steps/06-wording.md
|
|
62
|
+
润色任务沿 `SKILL.md` 的写作步骤执行,从定任务走到通过检查,跳步视为未完成;判定标准见 `steps/06-wording.md`。书根 `README.md` 与 `README.en.md` 是仓库说明页,按 `SKILL.md` 的例外只走第 5–8 步,这个例外优先于本步的跳步判定。两种走法共用 1–9:**增量润色**保持最小改动,只改判为不合格的句子,不做风格重塑;**全章重构**按 `SKILL.md`「两种走法」走——章级(1–2)先定删留不动句子,节级(3–4)只拆块和段,句级(5–7)逐句拆开再写回去,该层全部重组,不走最小改动。执行时遵守定段落节的既有判定:分句骨架在定句子判为不合格,不在定段落里顺手改句子。
|
|
63
63
|
|
|
64
64
|
**先切模块,再套规则。** 任何一步动笔前先把文本切成单位,围栏、YAML 与链接 URL 跳过(脚本按同一口径统计,计数见 `verifying-content.py` 输出):
|
|
65
65
|
|