@chengzhao-dev/dsh-quarto-book 0.6.2 → 0.6.4

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.
Files changed (53) hide show
  1. package/README.en.md +60 -31
  2. package/README.md +53 -24
  3. package/lib/index.js +49 -7
  4. package/lib/tools/pass-report.js +12 -12
  5. package/lib/tools/render-check.js +231 -62
  6. package/lib/workspace.js +1 -0
  7. package/locale/en.json +1 -1
  8. package/locale/zh.json +1 -1
  9. package/package.json +13 -7
  10. package/skills/designing-theme/assets/theme/assets/fonts/lxgw-bright-code-common.woff2 +0 -0
  11. package/skills/designing-theme/assets/theme/assets/fonts/lxgw-bright-medium-common.woff2 +0 -0
  12. package/skills/designing-theme/assets/theme/css/content.css +15 -0
  13. package/skills/designing-theme/assets/theme/css/mermaid.css +5 -6
  14. package/skills/designing-theme/knowledge/reading-surface.md +1 -1
  15. package/skills/designing-theme/references/theme-system.md +5 -5
  16. package/skills/designing-theme/scripts/checking-layout.py +60 -12
  17. package/skills/designing-theme/scripts/measuring-pages.mjs +2 -2
  18. package/skills/writing-quarto/SKILL.md +7 -0
  19. package/skills/writing-quarto/examples/02-structure-shape.md +26 -12
  20. package/skills/writing-quarto/examples/03-block-after.md +14 -0
  21. package/skills/writing-quarto/examples/03-qmd-elements.md +45 -2
  22. package/skills/writing-quarto/examples/04-paragraph-cases.md +14 -0
  23. package/skills/writing-quarto/examples/04-paragraph-opening.md +2 -2
  24. package/skills/writing-quarto/examples/05-sentence-frame.md +4 -4
  25. package/skills/writing-quarto/examples/06-wording-cases.md +23 -0
  26. package/skills/writing-quarto/examples/07-layout-list.md +3 -3
  27. package/skills/writing-quarto/examples/08-appendix.md +1 -1
  28. package/skills/writing-quarto/quarto/authoring.md +7 -6
  29. package/skills/writing-quarto/quarto/basics.md +17 -16
  30. package/skills/writing-quarto/quarto/coords.md +2 -1
  31. package/skills/writing-quarto/quarto/figures.md +49 -0
  32. package/skills/writing-quarto/quarto/mermaid.md +41 -5
  33. package/skills/writing-quarto/quarto/prose.md +4 -4
  34. package/skills/writing-quarto/quarto/refactor.md +1 -1
  35. package/skills/writing-quarto/quarto/terminal.md +1 -1
  36. package/skills/writing-quarto/references/book-journey.md +38 -0
  37. package/skills/writing-quarto/references/token-budget.md +26 -0
  38. package/skills/writing-quarto/scripts/checking-callouts.py +2 -2
  39. package/skills/writing-quarto/scripts/checking-dom.py +12 -6
  40. package/skills/writing-quarto/scripts/checking-links.py +29 -13
  41. package/skills/writing-quarto/scripts/checking-names.py +3 -1
  42. package/skills/writing-quarto/scripts/checking-output.py +3 -3
  43. package/skills/writing-quarto/scripts/checking-prose.py +11 -7
  44. package/skills/writing-quarto/scripts/testing-content.py +264 -1
  45. package/skills/writing-quarto/scripts/verifying-content.py +442 -36
  46. package/skills/writing-quarto/steps/03-blocks.md +20 -8
  47. package/skills/writing-quarto/steps/04-paragraphs.md +19 -2
  48. package/skills/writing-quarto/steps/06-wording.md +53 -0
  49. package/skills/writing-quarto/steps/07-layout.md +1 -1
  50. package/skills/writing-quarto/steps/08-reread.md +1 -0
  51. package/skills/writing-quarto/steps/09-check.md +3 -3
  52. package/templates/quarto-book-project/SKILL.md +1 -1
  53. package/templates/quarto-book-project/checks.json +5 -0
@@ -52,6 +52,101 @@ title: 入门
52
52
  先准备输入,再运行检查,最后对照输出。
53
53
  """
54
54
 
55
+ TREE_CHAPTER = """---
56
+ title: 入门
57
+ ---
58
+
59
+ ## 目标
60
+
61
+ 下面是工程目录树,行尾注释说明每个节点在工程里的用途,读者可以对照磁盘检查。
62
+
63
+ ```text
64
+ ├── apps/ #/ 终端程序
65
+ │ └── cli.h # 读入坐标
66
+ └── tests/ #/ 单元测试
67
+ ```
68
+ """
69
+
70
+
71
+ HTML_FIGURE_BAD = """---
72
+ title: 入门
73
+ ---
74
+
75
+ ## 目标
76
+
77
+ 这一章说明一个可以重复验证的目标。
78
+
79
+ ```{=html}
80
+ <svg class="coords-grid" role="img" aria-label="棋盘坐标。"><rect width="10" height="10"/></svg>
81
+ ```
82
+ """
83
+
84
+ HTML_FIGURE_GOOD = """---
85
+ title: 入门
86
+ ---
87
+
88
+ ## 目标
89
+
90
+ 这一章说明一个可以重复验证的目标。
91
+
92
+ ```{=html}
93
+ <figure class="book-figure">
94
+ <svg class="coords-grid" role="img" aria-label="棋盘坐标。"><rect width="10" height="10"/></svg>
95
+ <figcaption>棋盘坐标图:3 行 3 列。</figcaption>
96
+ </figure>
97
+ ```
98
+ """
99
+
100
+
101
+ TREE_GAP_CHAPTER = """---
102
+ title: 入门
103
+ ---
104
+
105
+ ## 目标
106
+
107
+ 下面是工程目录树,行尾注释说明每个节点在工程里的用途。
108
+
109
+ ```text
110
+ ├── lib/ #/ 源码
111
+ └── main.c # 入口
112
+ ```
113
+ """
114
+
115
+
116
+ MERMAID_CHAPTER = """---
117
+ title: 入门
118
+ ---
119
+
120
+ ## 目标
121
+
122
+ 下面是一张只画类型和边的类图,读者可以对照箭头理解类型之间的依赖。
123
+
124
+ ::: {.class-diagram}
125
+ ```{mermaid}
126
+ %%| fig-cap: "类型 A 依赖类型 B。实线箭头从使用者指向被使用者。"
127
+ %%{init: {"fontFamily": "LXGW Bright Medium, system-ui, sans-serif", "class": {"hideEmptyMembersBox": true}}}%%
128
+ classDiagram
129
+ direction LR
130
+ class A
131
+ class B
132
+ A --> B
133
+ ```
134
+ :::
135
+ """
136
+
137
+
138
+ def long_tree_chapter(lines: int = 21) -> str:
139
+ rows = []
140
+ for index in range(lines):
141
+ prefix = f"├── file{index}.txt"
142
+ rows.append(prefix + " " * (20 - len(prefix)) + f"# 用途{index}")
143
+ tree = "\n".join(rows)
144
+ return (
145
+ "---\ntitle: 入门\n---\n\n## 目标\n\n"
146
+ "下面是工程目录树,行尾注释说明每个节点在工程里的用途。\n\n"
147
+ f"```text\n{tree}\n```\n"
148
+ )
149
+
55
150
 
56
151
  def run_case(tmp: Path) -> subprocess.CompletedProcess[str]:
57
152
  return subprocess.run(
@@ -74,8 +169,11 @@ def main() -> int:
74
169
  pass
75
170
 
76
171
  failures: list[str] = []
172
+ passed_cases = 0
77
173
 
78
174
  def expect(name: str, chapter: str, code: int, needle: str, project: bool = False) -> None:
175
+ nonlocal passed_cases
176
+ passed_cases += 1
79
177
  with tempfile.TemporaryDirectory() as raw:
80
178
  root = Path(raw)
81
179
  make_book(root, chapter)
@@ -97,6 +195,46 @@ def main() -> int:
97
195
  failures.append(f"{name}: 输出缺少「{needle}」\n{output[:600]}")
98
196
 
99
197
  expect("合法样书", VALID_CHAPTER, 0, "OK")
198
+ expect(
199
+ "正文问答式讲解",
200
+ VALID_CHAPTER + "\n`Board` 只回答四类问题:坐标、满盘、三连与赢家。\n",
201
+ 1, "问答式讲解",
202
+ )
203
+ expect(
204
+ "代码字面复述",
205
+ VALID_CHAPTER + "\n对应的代码里,`kBoardRows` 和 `kBoardCols` 都取 3。\n",
206
+ 1, "代码字面复述",
207
+ )
208
+ expect(
209
+ "自造关系词",
210
+ VALID_CHAPTER + "\n`Game` 与 `Board` 之间的组成边表示什么?\n",
211
+ 1, "自造关系词",
212
+ )
213
+ expect(
214
+ "类型承载",
215
+ VALID_CHAPTER + "\n坐标由 `CellPosition` 承载,取值由 `CellType` 承载。\n",
216
+ 1, "承载",
217
+ )
218
+ expect(
219
+ "小节导航",
220
+ VALID_CHAPTER + "\n下一节说明落子顺序。\n",
221
+ 1, "小节导航",
222
+ )
223
+ expect(
224
+ "拉链句",
225
+ VALID_CHAPTER + "\n两个类型分别交给坐标与取值。\n",
226
+ 1, "拉链句",
227
+ )
228
+ duplicate_link = (
229
+ "https://github.com/chengzhao-dev/cpp-board-games/blob/main/"
230
+ "games/tictactoe/tictactoe/include/board.h"
231
+ )
232
+ expect(
233
+ "同章重复文件链接",
234
+ VALID_CHAPTER
235
+ + f"\n[board.h]({duplicate_link})\n\n[board.h]({duplicate_link})\n",
236
+ 1, "同章重复文件链接",
237
+ )
100
238
  expect("标题口语化", VALID_CHAPTER.replace("## 目标", "## 自己走一局"), 1, "标题带人称")
101
239
  expect("标题带行内代码", VALID_CHAPTER.replace("## 目标", "## 运行 `app`"), 1, "标题带行内代码")
102
240
  expect("正文 H1", VALID_CHAPTER.replace("## 目标", "# 目标"), 1, "正文出现 H1")
@@ -116,6 +254,12 @@ def main() -> int:
116
254
  "---\ntitle: 入门\n---\n\n## 目标\n\n```\nprint(1)\n```\n",
117
255
  1, "未声明语言",
118
256
  )
257
+ expect(
258
+ "相邻围栏缺少空行",
259
+ "---\ntitle: 入门\n---\n\n## 目标\n\n"
260
+ "```text\na\n```\n```text\nb\n```\n",
261
+ 1, "DOC-E11",
262
+ )
119
263
  expect(
120
264
  "项目禁用词生效",
121
265
  "---\ntitle: 入门\n---\n\n## 目标\n\n棋盘格子按行列编号,程序读取当前状态。\n",
@@ -128,7 +272,126 @@ def main() -> int:
128
272
  0, "OK",
129
273
  )
130
274
 
275
+ expect("目录树合格", TREE_CHAPTER, 0, "OK")
276
+ expect("原生 SVG 缺图注", HTML_FIGURE_BAD, 1, "figcaption")
277
+ expect("原生 SVG 图注合格", HTML_FIGURE_GOOD, 0, "OK")
278
+ expect(
279
+ "标记后多空格",
280
+ TREE_CHAPTER.replace("# 读入坐标", "# 读入坐标"),
281
+ 1, "标记后只留一个空格",
282
+ )
283
+ expect(
284
+ "注释文字未对齐",
285
+ TREE_CHAPTER.replace("#/ 终端程序", "#/ 终端程序"),
286
+ 1, "注释文字未对齐",
287
+ )
288
+ expect(
289
+ "树注释错列",
290
+ TREE_CHAPTER.replace("│ └── cli.h #", "│ └── cli.h #"),
291
+ 1, "右移一列",
292
+ )
293
+ expect("节点与注释太近", TREE_GAP_CHAPTER, 1, "至少留四个空格")
294
+ expect(
295
+ "文件 # 未右移",
296
+ TREE_CHAPTER.replace("│ └── cli.h #", "│ └── cli.h #"),
297
+ 1, "右移一列",
298
+ )
299
+ expect("树注释只写类别词", TREE_CHAPTER.replace("# 读入坐标", "# 头文件"), 1, "类别词")
300
+ expect(
301
+ "目录注释带冒号",
302
+ TREE_CHAPTER.replace("#/ 终端程序", "#/ 终端程序:打印棋盘"),
303
+ 1, "目录注释只写这里装什么",
304
+ )
305
+ expect(
306
+ "目录注释过长",
307
+ TREE_CHAPTER.replace("#/ 终端程序", "#/ 终端程序与它的打印读入"),
308
+ 1, "超过 16 列",
309
+ )
310
+ expect("树注释列了三项", TREE_CHAPTER.replace("# 读入坐标", "# 读入、打印与写快照"), 1, "压到 1 到 2 项")
311
+ expect("目录行缺少 #/", TREE_CHAPTER.replace("#/ 终端程序", "# 终端程序"), 1, "目录行的注释以 #/")
312
+ expect("文件行误写 #/", TREE_CHAPTER.replace("# 读入坐标", "#/ 读入坐标"), 1, "文件行的注释以 #")
313
+ expect("树行缺少注释", TREE_CHAPTER.replace("# 读入坐标", ""), 1, "缺少行尾注释")
314
+ expect(
315
+ "树注释过长",
316
+ TREE_CHAPTER.replace("# 读入坐标", "# 读入坐标并打印当前棋盘的全部状态"),
317
+ 1, "超过 28 列",
318
+ )
319
+ expect("树行数过多", long_tree_chapter(), 1, "超过 20 行")
320
+
321
+ expect("类图合格", MERMAID_CHAPTER, 0, "OK")
322
+ expect(
323
+ "类图缺少图注",
324
+ MERMAID_CHAPTER.replace('%%| fig-cap: "类型 A 依赖类型 B。实线箭头从使用者指向被使用者。"\n', ""),
325
+ 1, "fig-cap",
326
+ )
327
+ wrong_order = MERMAID_CHAPTER.replace(
328
+ '%%| fig-cap: "类型 A 依赖类型 B。实线箭头从使用者指向被使用者。"\n%%{init: ',
329
+ "%%{init: ",
330
+ ).replace(
331
+ "classDiagram",
332
+ '%%| fig-cap: "类型 A 依赖类型 B。实线箭头从使用者指向被使用者。"\nclassDiagram',
333
+ 1,
334
+ )
335
+ expect("类图图注写在 init 之后", wrong_order, 1, "init 之前")
336
+ expect(
337
+ "类图缺少页面容器",
338
+ MERMAID_CHAPTER.replace("::: {.class-diagram}\n```{mermaid}", "```{mermaid}").replace(
339
+ "```\n:::", "```"
340
+ ),
341
+ 1, ".class-diagram",
342
+ )
343
+ expect(
344
+ "类图缺少空成员配置",
345
+ MERMAID_CHAPTER.replace(', "class": {"hideEmptyMembersBox": true}', ""),
346
+ 1, "hideEmptyMembersBox",
347
+ )
348
+ expect(
349
+ "类图有成员却用 LR",
350
+ MERMAID_CHAPTER.replace(
351
+ " class B\n",
352
+ " class B {\n int value\n }\n",
353
+ ),
354
+ 1, "有成员却用 LR",
355
+ )
356
+ expect(
357
+ "类图类型过多",
358
+ MERMAID_CHAPTER.replace(
359
+ " class B\n",
360
+ " class B\n class C\n class D\n class E\n class F\n class G\n class H\n",
361
+ ),
362
+ 1, "超过 6 个",
363
+ )
364
+ expect(
365
+ "类图成员行过长",
366
+ MERMAID_CHAPTER.replace(
367
+ " class A\n",
368
+ " class A {\n +bool CompletesLine(CellPosition position, CellType cell_type) const\n }\n",
369
+ ).replace("direction LR", "direction TB"),
370
+ 1, "超过 40 列",
371
+ )
372
+ expect(
373
+ "单个类成员过多",
374
+ MERMAID_CHAPTER.replace(
375
+ " class A\n",
376
+ " class A {\n int a\n int b\n int c\n int d\n int e\n int f\n }\n",
377
+ ).replace("direction LR", "direction TB"),
378
+ 1, "超过 5 行",
379
+ )
380
+ expect(
381
+ "类图成员总数过多",
382
+ MERMAID_CHAPTER.replace(
383
+ " class A\n class B\n",
384
+ " class A {\n int a1\n int a2\n int a3\n }\n"
385
+ " class B {\n int b1\n int b2\n int b3\n }\n"
386
+ " class C {\n int c1\n int c2\n int c3\n }\n"
387
+ " class D {\n int d1\n int d2\n int d3\n }\n"
388
+ " class E {\n int e1\n int e2\n int e3\n }\n",
389
+ ).replace("direction LR", "direction TB"),
390
+ 1, "超过 14 行",
391
+ )
392
+
131
393
  with tempfile.TemporaryDirectory() as raw:
394
+ passed_cases += 1
132
395
  root = Path(raw)
133
396
  make_book(root)
134
397
  (root / "content" / "part" / "01-intro.qmd").unlink()
@@ -143,7 +406,7 @@ def main() -> int:
143
406
  print(f" FAIL {line}")
144
407
  return 1
145
408
 
146
- print("PASS:11 个用例全部通过")
409
+ print(f"PASS:{passed_cases} 个用例全部通过")
147
410
  return 0
148
411
 
149
412