docbook 0.1.0

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 (271) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CLAUDE.md +19 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/README.adoc +335 -0
  6. data/Rakefile +12 -0
  7. data/docs/.lycheeignore +33 -0
  8. data/docs/Gemfile +10 -0
  9. data/docs/INDEX.adoc +67 -0
  10. data/docs/_config.yml +186 -0
  11. data/docs/advanced/element-classes.adoc +185 -0
  12. data/docs/advanced/frontend-customization.adoc +193 -0
  13. data/docs/advanced/index.adoc +14 -0
  14. data/docs/advanced/templates.adoc +123 -0
  15. data/docs/features/element-coverage.adoc +373 -0
  16. data/docs/features/html-output/data-model.adoc +285 -0
  17. data/docs/features/html-output/directory-mode.adoc +180 -0
  18. data/docs/features/html-output/index.adoc +90 -0
  19. data/docs/features/html-output/single-file-mode.adoc +125 -0
  20. data/docs/features/index-generation.adoc +197 -0
  21. data/docs/features/index.adoc +63 -0
  22. data/docs/features/numbering.adoc +183 -0
  23. data/docs/features/toc-generation.adoc +150 -0
  24. data/docs/features/xinclude/fragid-schemes.adoc +287 -0
  25. data/docs/features/xinclude/index.adoc +119 -0
  26. data/docs/features/xinclude/text-includes.adoc +123 -0
  27. data/docs/features/xinclude/xml-includes.adoc +167 -0
  28. data/docs/getting-started/index.adoc +50 -0
  29. data/docs/getting-started/installation.adoc +113 -0
  30. data/docs/getting-started/quick-start.adoc +161 -0
  31. data/docs/guides/converting-article.adoc +188 -0
  32. data/docs/guides/converting-book.adoc +192 -0
  33. data/docs/guides/index.adoc +12 -0
  34. data/docs/guides/roundtrip-testing.adoc +129 -0
  35. data/docs/interfaces/cli/format-command.adoc +109 -0
  36. data/docs/interfaces/cli/index.adoc +73 -0
  37. data/docs/interfaces/cli/roundtrip-command.adoc +125 -0
  38. data/docs/interfaces/cli/to-html-command.adoc +178 -0
  39. data/docs/interfaces/cli/validate-command.adoc +104 -0
  40. data/docs/interfaces/index.adoc +101 -0
  41. data/docs/interfaces/ruby-api/html-output.adoc +186 -0
  42. data/docs/interfaces/ruby-api/index.adoc +111 -0
  43. data/docs/interfaces/ruby-api/parsing.adoc +202 -0
  44. data/docs/interfaces/ruby-api/xinclude.adoc +162 -0
  45. data/docs/interfaces/ruby-api/xref-resolution.adoc +156 -0
  46. data/docs/lychee.toml +42 -0
  47. data/docs/reference/cli-options.adoc +155 -0
  48. data/docs/reference/content-block-types.adoc +243 -0
  49. data/docs/reference/glossary.adoc +119 -0
  50. data/docs/reference/index.adoc +12 -0
  51. data/docs/reference/supported-elements.adoc +749 -0
  52. data/docs/understanding/architecture.adoc +145 -0
  53. data/docs/understanding/content-pipeline.adoc +102 -0
  54. data/docs/understanding/data-models.adoc +156 -0
  55. data/docs/understanding/index.adoc +34 -0
  56. data/exe/docbook +7 -0
  57. data/frontend/dist/app.css +1 -0
  58. data/frontend/dist/app.iife.js +24 -0
  59. data/frontend/package-lock.json +1445 -0
  60. data/frontend/package.json +22 -0
  61. data/frontend/src/App.vue +230 -0
  62. data/frontend/src/app.ts +8 -0
  63. data/frontend/src/components/AppSidebar.vue +116 -0
  64. data/frontend/src/components/AppendixSection.vue +39 -0
  65. data/frontend/src/components/BlockRenderer.vue +358 -0
  66. data/frontend/src/components/ChapterSection.vue +32 -0
  67. data/frontend/src/components/ContentRenderer.vue +13 -0
  68. data/frontend/src/components/EbookContainer.vue +147 -0
  69. data/frontend/src/components/EbookTopBar.vue +116 -0
  70. data/frontend/src/components/PartSection.vue +44 -0
  71. data/frontend/src/components/ReferenceEntry.vue +80 -0
  72. data/frontend/src/components/SearchModal.vue +286 -0
  73. data/frontend/src/components/SectionContent.vue +31 -0
  74. data/frontend/src/components/SettingsPanel.vue +236 -0
  75. data/frontend/src/components/TocTreeItem.vue +135 -0
  76. data/frontend/src/composables/useEbookStore.ts +191 -0
  77. data/frontend/src/composables/useSearch.ts +249 -0
  78. data/frontend/src/env.d.ts +7 -0
  79. data/frontend/src/stores/documentStore.ts +221 -0
  80. data/frontend/src/stores/uiStore.ts +98 -0
  81. data/frontend/src/styles.css +253 -0
  82. data/frontend/tsconfig.json +24 -0
  83. data/frontend/tsconfig.node.json +11 -0
  84. data/frontend/vite.config.ts +30 -0
  85. data/lib/docbook/cli.rb +123 -0
  86. data/lib/docbook/document.rb +67 -0
  87. data/lib/docbook/elements/abbrev.rb +16 -0
  88. data/lib/docbook/elements/acknowledgements.rb +22 -0
  89. data/lib/docbook/elements/address.rb +18 -0
  90. data/lib/docbook/elements/alt.rb +16 -0
  91. data/lib/docbook/elements/annotation.rb +18 -0
  92. data/lib/docbook/elements/appendix.rb +34 -0
  93. data/lib/docbook/elements/article.rb +31 -0
  94. data/lib/docbook/elements/att.rb +15 -0
  95. data/lib/docbook/elements/attribution.rb +20 -0
  96. data/lib/docbook/elements/audioobject.rb +18 -0
  97. data/lib/docbook/elements/author.rb +18 -0
  98. data/lib/docbook/elements/bibliography.rb +24 -0
  99. data/lib/docbook/elements/bibliomixed.rb +40 -0
  100. data/lib/docbook/elements/biblioref.rb +20 -0
  101. data/lib/docbook/elements/blockquote.rb +26 -0
  102. data/lib/docbook/elements/book.rb +36 -0
  103. data/lib/docbook/elements/buildtarget.rb +16 -0
  104. data/lib/docbook/elements/callout.rb +22 -0
  105. data/lib/docbook/elements/calloutlist.rb +22 -0
  106. data/lib/docbook/elements/caution.rb +30 -0
  107. data/lib/docbook/elements/chapter.rb +62 -0
  108. data/lib/docbook/elements/citation.rb +16 -0
  109. data/lib/docbook/elements/citerefentry.rb +26 -0
  110. data/lib/docbook/elements/citetitle.rb +20 -0
  111. data/lib/docbook/elements/classname.rb +16 -0
  112. data/lib/docbook/elements/code.rb +16 -0
  113. data/lib/docbook/elements/colophon.rb +22 -0
  114. data/lib/docbook/elements/computeroutput.rb +18 -0
  115. data/lib/docbook/elements/copyright.rb +17 -0
  116. data/lib/docbook/elements/danger.rb +28 -0
  117. data/lib/docbook/elements/date.rb +16 -0
  118. data/lib/docbook/elements/dedication.rb +22 -0
  119. data/lib/docbook/elements/dir.rb +16 -0
  120. data/lib/docbook/elements/emphasis.rb +18 -0
  121. data/lib/docbook/elements/entry.rb +30 -0
  122. data/lib/docbook/elements/entrytbl.rb +22 -0
  123. data/lib/docbook/elements/equation.rb +26 -0
  124. data/lib/docbook/elements/example.rb +30 -0
  125. data/lib/docbook/elements/fieldsynopsis.rb +21 -0
  126. data/lib/docbook/elements/figure.rb +28 -0
  127. data/lib/docbook/elements/filename.rb +16 -0
  128. data/lib/docbook/elements/firstname.rb +16 -0
  129. data/lib/docbook/elements/firstterm.rb +18 -0
  130. data/lib/docbook/elements/footnote.rb +22 -0
  131. data/lib/docbook/elements/footnoteref.rb +21 -0
  132. data/lib/docbook/elements/foreignphrase.rb +18 -0
  133. data/lib/docbook/elements/formalpara.rb +20 -0
  134. data/lib/docbook/elements/function.rb +16 -0
  135. data/lib/docbook/elements/glossary.rb +24 -0
  136. data/lib/docbook/elements/glossdef.rb +18 -0
  137. data/lib/docbook/elements/glossentry.rb +26 -0
  138. data/lib/docbook/elements/glosssee.rb +18 -0
  139. data/lib/docbook/elements/glossseealso.rb +18 -0
  140. data/lib/docbook/elements/glossterm.rb +18 -0
  141. data/lib/docbook/elements/holder.rb +16 -0
  142. data/lib/docbook/elements/honorific.rb +16 -0
  143. data/lib/docbook/elements/imagedata.rb +29 -0
  144. data/lib/docbook/elements/imageobject.rb +22 -0
  145. data/lib/docbook/elements/important.rb +30 -0
  146. data/lib/docbook/elements/index.rb +26 -0
  147. data/lib/docbook/elements/indexdiv.rb +22 -0
  148. data/lib/docbook/elements/indexentry.rb +22 -0
  149. data/lib/docbook/elements/indexterm.rb +34 -0
  150. data/lib/docbook/elements/info.rb +25 -0
  151. data/lib/docbook/elements/informalexample.rb +24 -0
  152. data/lib/docbook/elements/informalfigure.rb +20 -0
  153. data/lib/docbook/elements/inlinemediaobject.rb +22 -0
  154. data/lib/docbook/elements/itemizedlist.rb +21 -0
  155. data/lib/docbook/elements/legalnotice.rb +22 -0
  156. data/lib/docbook/elements/link.rb +26 -0
  157. data/lib/docbook/elements/listitem.rb +32 -0
  158. data/lib/docbook/elements/literal.rb +16 -0
  159. data/lib/docbook/elements/literallayout.rb +22 -0
  160. data/lib/docbook/elements/mediaobject.rb +26 -0
  161. data/lib/docbook/elements/msg.rb +20 -0
  162. data/lib/docbook/elements/msgexplan.rb +22 -0
  163. data/lib/docbook/elements/msgset.rb +22 -0
  164. data/lib/docbook/elements/note.rb +30 -0
  165. data/lib/docbook/elements/orderedlist.rb +23 -0
  166. data/lib/docbook/elements/orgname.rb +16 -0
  167. data/lib/docbook/elements/para.rb +61 -0
  168. data/lib/docbook/elements/paragraph_like.rb +18 -0
  169. data/lib/docbook/elements/parameter.rb +16 -0
  170. data/lib/docbook/elements/part.rb +38 -0
  171. data/lib/docbook/elements/personname.rb +22 -0
  172. data/lib/docbook/elements/phrase.rb +20 -0
  173. data/lib/docbook/elements/preface.rb +58 -0
  174. data/lib/docbook/elements/primary.rb +16 -0
  175. data/lib/docbook/elements/procedure.rb +24 -0
  176. data/lib/docbook/elements/productname.rb +18 -0
  177. data/lib/docbook/elements/productnumber.rb +16 -0
  178. data/lib/docbook/elements/programlisting.rb +28 -0
  179. data/lib/docbook/elements/property.rb +16 -0
  180. data/lib/docbook/elements/pubdate.rb +16 -0
  181. data/lib/docbook/elements/publishername.rb +16 -0
  182. data/lib/docbook/elements/quotation.rb +24 -0
  183. data/lib/docbook/elements/quote.rb +18 -0
  184. data/lib/docbook/elements/refentry.rb +32 -0
  185. data/lib/docbook/elements/refentrytitle.rb +16 -0
  186. data/lib/docbook/elements/reference.rb +26 -0
  187. data/lib/docbook/elements/refmeta.rb +29 -0
  188. data/lib/docbook/elements/refmiscinfo.rb +16 -0
  189. data/lib/docbook/elements/refname.rb +16 -0
  190. data/lib/docbook/elements/refnamediv.rb +22 -0
  191. data/lib/docbook/elements/refpurpose.rb +16 -0
  192. data/lib/docbook/elements/refsect1.rb +22 -0
  193. data/lib/docbook/elements/refsect2.rb +22 -0
  194. data/lib/docbook/elements/refsect3.rb +22 -0
  195. data/lib/docbook/elements/refsection.rb +32 -0
  196. data/lib/docbook/elements/releaseinfo.rb +16 -0
  197. data/lib/docbook/elements/remark.rb +20 -0
  198. data/lib/docbook/elements/replaceable.rb +16 -0
  199. data/lib/docbook/elements/row.rb +15 -0
  200. data/lib/docbook/elements/screen.rb +28 -0
  201. data/lib/docbook/elements/secondary.rb +16 -0
  202. data/lib/docbook/elements/sect1.rb +26 -0
  203. data/lib/docbook/elements/sect2.rb +24 -0
  204. data/lib/docbook/elements/sect3.rb +24 -0
  205. data/lib/docbook/elements/sect4.rb +24 -0
  206. data/lib/docbook/elements/sect5.rb +22 -0
  207. data/lib/docbook/elements/section.rb +66 -0
  208. data/lib/docbook/elements/see.rb +16 -0
  209. data/lib/docbook/elements/seealso.rb +18 -0
  210. data/lib/docbook/elements/set.rb +26 -0
  211. data/lib/docbook/elements/setindex.rb +24 -0
  212. data/lib/docbook/elements/sidebar.rb +22 -0
  213. data/lib/docbook/elements/simplesect.rb +32 -0
  214. data/lib/docbook/elements/step.rb +26 -0
  215. data/lib/docbook/elements/substeps.rb +18 -0
  216. data/lib/docbook/elements/subtitle.rb +16 -0
  217. data/lib/docbook/elements/surname.rb +16 -0
  218. data/lib/docbook/elements/table.rb +24 -0
  219. data/lib/docbook/elements/tag.rb +20 -0
  220. data/lib/docbook/elements/tbody.rb +15 -0
  221. data/lib/docbook/elements/term.rb +37 -0
  222. data/lib/docbook/elements/tertiary.rb +16 -0
  223. data/lib/docbook/elements/textobject.rb +18 -0
  224. data/lib/docbook/elements/tfoot.rb +15 -0
  225. data/lib/docbook/elements/tgroup.rb +21 -0
  226. data/lib/docbook/elements/thead.rb +15 -0
  227. data/lib/docbook/elements/tip.rb +30 -0
  228. data/lib/docbook/elements/title.rb +16 -0
  229. data/lib/docbook/elements/toc.rb +24 -0
  230. data/lib/docbook/elements/tocdiv.rb +22 -0
  231. data/lib/docbook/elements/tocentry.rb +20 -0
  232. data/lib/docbook/elements/topic.rb +26 -0
  233. data/lib/docbook/elements/type.rb +16 -0
  234. data/lib/docbook/elements/uri.rb +16 -0
  235. data/lib/docbook/elements/userinput.rb +18 -0
  236. data/lib/docbook/elements/variable.rb +16 -0
  237. data/lib/docbook/elements/variablelist.rb +19 -0
  238. data/lib/docbook/elements/varlistentry.rb +17 -0
  239. data/lib/docbook/elements/version.rb +16 -0
  240. data/lib/docbook/elements/videoobject.rb +18 -0
  241. data/lib/docbook/elements/warning.rb +30 -0
  242. data/lib/docbook/elements/xref.rb +18 -0
  243. data/lib/docbook/elements/year.rb +16 -0
  244. data/lib/docbook/elements.rb +204 -0
  245. data/lib/docbook/models/document_metadata.rb +30 -0
  246. data/lib/docbook/models/document_root.rb +33 -0
  247. data/lib/docbook/models/index_entry.rb +28 -0
  248. data/lib/docbook/models/reading_position.rb +22 -0
  249. data/lib/docbook/models/section_number.rb +18 -0
  250. data/lib/docbook/models/section_root.rb +29 -0
  251. data/lib/docbook/models/toc_node.rb +24 -0
  252. data/lib/docbook/models.rb +16 -0
  253. data/lib/docbook/output/data.rb +196 -0
  254. data/lib/docbook/output/html.rb +1450 -0
  255. data/lib/docbook/output/index_generator.rb +281 -0
  256. data/lib/docbook/output.rb +8 -0
  257. data/lib/docbook/services/document_builder.rb +258 -0
  258. data/lib/docbook/services/element_to_hash.rb +287 -0
  259. data/lib/docbook/services/index_generator.rb +134 -0
  260. data/lib/docbook/services/numbering_service.rb +186 -0
  261. data/lib/docbook/services/toc_generator.rb +138 -0
  262. data/lib/docbook/services.rb +14 -0
  263. data/lib/docbook/templates/document.html.liquid +20 -0
  264. data/lib/docbook/templates/partials/_head.liquid +39 -0
  265. data/lib/docbook/templates/partials/_scripts.liquid +10 -0
  266. data/lib/docbook/version.rb +5 -0
  267. data/lib/docbook/xinclude_resolver.rb +217 -0
  268. data/lib/docbook/xref_resolver.rb +78 -0
  269. data/lib/docbook.rb +17 -0
  270. data/sig/docbook.rbs +4 -0
  271. metadata +385 -0
@@ -0,0 +1,183 @@
1
+ ---
2
+ layout: default
3
+ title: Section Numbering
4
+ parent: Features
5
+ nav_order: 3
6
+ ---
7
+
8
+ # Section Numbering
9
+
10
+ The `NumberingBuilder` class computes formatted section numbers for all numbered document divisions. Numbering follows DocBook conventions: Roman numerals for Parts, Arabic numerals for Chapters, hierarchical dot notation for Sections, and Alpha letters for Appendices.
11
+
12
+ ## Numbering Schemes
13
+
14
+ [cols="1,2,3"]
15
+ |===
16
+ | Division | Scheme | Examples
17
+
18
+ | Part | Roman numerals (I, II, III, ...)
19
+ | Part I, Part II, Part III
20
+
21
+ | Chapter | Arabic numerals scoped per Part
22
+ | 1, 2, 3 (resets at each Part)
23
+
24
+ | Section | Hierarchical dot notation
25
+ | 1.1, 1.2, 1.2.3, 2.1
26
+
27
+ | Appendix | Alpha letters (A, B, C, ...)
28
+ | Appendix A, Appendix B, Appendix C
29
+
30
+ | Preface | Unnumbered
31
+ | (no number)
32
+
33
+ | Glossary | Unnumbered
34
+ | (no number)
35
+
36
+ | Bibliography | Unnumbered
37
+ | (no number)
38
+
39
+ | Index | Unnumbered
40
+ | (no number)
41
+ |===
42
+
43
+ ## NumberingBuilder Class
44
+
45
+ The `NumberingBuilder` maintains independent counters for each numbering scope:
46
+
47
+ [source,ruby]
48
+ ----
49
+ class NumberingBuilder
50
+ def initialize
51
+ @numbering = {} # id => formatted_number_string
52
+ @part_counter = 0 # Global part counter
53
+ @chapter_counters = {} # part_index => chapter_counter
54
+ @appendix_counter = 0 # Global appendix counter
55
+ @section_counters = {} # scope_id => sequential_counter
56
+ end
57
+ end
58
+ ----
59
+
60
+ ### Part Numbering
61
+
62
+ Parts use uppercase Roman numerals. The counter is global across the entire document:
63
+
64
+ [source,ruby]
65
+ ----
66
+ def next_part
67
+ @part_counter += 1
68
+ roman_numeral(@part_counter) # "I", "II", "III", "IV", "V", ...
69
+ end
70
+ ----
71
+
72
+ Roman numerals are computed using the standard subtractive notation algorithm with mappings for 1000 (M), 900 (CM), 500 (D), 400 (CD), 100 (C), 90 (XC), 50 (L), 40 (XL), 10 (X), 9 (IX), 5 (V), 4 (IV), 1 (I).
73
+
74
+ ### Chapter Numbering
75
+
76
+ Chapters use Arabic numerals scoped per Part. Each Part maintains its own chapter counter, which resets when a new Part begins. Chapters outside of Parts use a global counter:
77
+
78
+ [source,ruby]
79
+ ----
80
+ def next_chapter(part_index)
81
+ @chapter_counters[part_index] ||= 0
82
+ @chapter_counters[part_index] += 1
83
+ @chapter_counters[part_index].to_s
84
+ end
85
+ ----
86
+
87
+ This means:
88
+
89
+ - Part I contains Chapters 1, 2, 3
90
+ - Part II contains Chapters 1, 2 (reset)
91
+ - Chapters without a Part share counter scope 0
92
+
93
+ ### Section Numbering
94
+
95
+ Sections use flat sequential numbering per parent scope, combined into hierarchical dot notation:
96
+
97
+ [source,ruby]
98
+ ----
99
+ def next_section(scope_id)
100
+ @section_counters[scope_id] ||= 0
101
+ @section_counters[scope_id] += 1
102
+ @section_counters[scope_id].to_s
103
+ end
104
+ ----
105
+
106
+ The hierarchical number is built by prefixing with the parent's number:
107
+
108
+ [source,ruby]
109
+ ----
110
+ # For a section in Chapter 1:
111
+ # scope_id = "ch01" -> 1
112
+ # Child scope = "ch01_s1" -> 1.1
113
+ # Grandchild scope = "ch01_s1_ss1" -> 1.1.1
114
+
115
+ prefix = parent_number || chapter_num
116
+ full_num = prefix ? "#{prefix}.#{section_num}" : section_num
117
+ ----
118
+
119
+ ### Appendix Numbering
120
+
121
+ Appendices use uppercase Alpha letters:
122
+
123
+ [source,ruby]
124
+ ----
125
+ def next_appendix
126
+ @appendix_counter += 1
127
+ alpha_numeral(@appendix_counter) # "A", "B", "C", ..., "Z", "AA", "AB"
128
+ end
129
+ ----
130
+
131
+ The Alpha algorithm supports double-letter notation for more than 26 appendices (A through Z, then AA, AB, etc.).
132
+
133
+ Appendix sections are prefixed with the appendix letter: `A.1`, `A.2`, `B.1`, etc.
134
+
135
+ ## Numbering Map
136
+
137
+ The computed numbering is stored as a flat hash mapping section `xml:id` to formatted number string:
138
+
139
+ [source,ruby]
140
+ ----
141
+ numbering = {
142
+ "part1" => "I",
143
+ "ch-intro" => "1",
144
+ "ch-intro-s1" => "1.1",
145
+ "ch-intro-s2" => "1.2",
146
+ "ch-intro-s2a" => "1.2.1",
147
+ "part2" => "II",
148
+ "ch-advanced" => "1", # Reset because new Part
149
+ "ch-advanced-s1" => "1.1",
150
+ "app-changes" => "Appendix A",
151
+ "app-changes-s1" => "A.1",
152
+ "app-license" => "Appendix B",
153
+ }
154
+ ----
155
+
156
+ This flat map is serialized as an array of `NumberingEntry` objects in the output JSON, and the Vue frontend converts it back to a `Record<string, string>` for O(1) lookup during rendering.
157
+
158
+ ## Unnumbered Sections
159
+
160
+ The following section types are explicitly not numbered:
161
+
162
+ - **Preface** -- Front matter that precedes numbered content
163
+ - **Glossary** -- Alphabetical term list
164
+ - **Bibliography** -- Reference list
165
+ - **Index** -- Generated back-of-book index
166
+
167
+ When the numbering traversal encounters these types, it skips numbering assignment but still recurses into their children (which may contain numbered elements in some document structures).
168
+
169
+ ## Integration with TOC
170
+
171
+ The numbering map is computed alongside the TOC tree and stored in the `Toc` model:
172
+
173
+ [source,ruby]
174
+ ----
175
+ output = DocbookOutput.new(
176
+ title: extract_title,
177
+ toc: Toc.new(
178
+ sections: sections_data,
179
+ numbering: numbering_hash.map { |id, value| NumberingEntry.new(id: id, value: value) }
180
+ ),
181
+ # ...
182
+ )
183
+ ----
@@ -0,0 +1,150 @@
1
+ ---
2
+ layout: default
3
+ title: TOC Generation
4
+ parent: Features
5
+ nav_order: 4
6
+ ---
7
+
8
+ # TOC Generation
9
+
10
+ The table of contents is generated as a recursive tree of `SectionData` objects. Each node carries an ID, title, section type, and children. The Vue SPA renders this tree as a collapsible sidebar with type badges and section numbering.
11
+
12
+ ## SectionData Tree
13
+
14
+ Each node in the TOC is a `SectionData` with four fields:
15
+
16
+ [cols="1,1,3"]
17
+ |===
18
+ | Field | Type | Description
19
+
20
+ | `id` | `String` | Section's `xml:id` attribute or a generated slug from the title
21
+ | `title` | `String` | Section title (from `info/title` or direct `title` child)
22
+ | `type` | `String` | Section type used for badge rendering and numbering
23
+ | `children` | `SectionData[]` | Nested child sections
24
+ |===
25
+
26
+ ### Type Values
27
+
28
+ The `type` field determines how the section is displayed in the sidebar:
29
+
30
+ [cols="1,3,2"]
31
+ |===
32
+ | Type | Badge | Color
33
+
34
+ | `part` | Pt | Purple
35
+ | `chapter` | Ch | Blue
36
+ | `appendix` | App | Green
37
+ | `section` | (none) | Gray
38
+ | `glossary` | Gl | Yellow
39
+ | `bibliography` | Bib | Red
40
+ | `index` | Idx | Indigo
41
+ | `preface` | Pref | Orange
42
+ | `reference` | Ref | Cyan
43
+ |===
44
+
45
+ ## Tree Structure by Document Type
46
+
47
+ The TOC tree varies depending on the root document element.
48
+
49
+ ### Book
50
+
51
+ For `<book>` documents, the top-level sections include:
52
+
53
+ - **Preface** -- Direct children of the book
54
+ - **Parts** -- Contain chapters, references, appendices, glossary, bibliography, index
55
+ - **Chapters** -- Direct children of the book (when no Parts are used), contain nested sections
56
+ - **Appendices** -- May contain nested sections
57
+ - **Glossary** -- Leaf node
58
+ - **Bibliography** -- Leaf node
59
+ - **Index** -- Leaf node
60
+
61
+ [source,ruby]
62
+ ----
63
+ # Book TOC collection
64
+ def collect_sections
65
+ sections = []
66
+ case @document
67
+ when Docbook::Elements::Book
68
+ each_attr(@document, :preface) { |pf| sections << SectionData.new(type: 'preface', ...) }
69
+ each_attr(@document, :part) { |p| sections << SectionData.new(type: 'part', children: collect_part_children(p)) }
70
+ each_attr(@document, :chapter) { |ch| sections << SectionData.new(type: 'chapter', ...) }
71
+ each_attr(@document, :appendix){ |ap| sections << SectionData.new(type: 'appendix', children: collect_appendix_children(ap)) }
72
+ each_attr(@document, :glossary){ |g| sections << SectionData.new(type: 'glossary', ...) }
73
+ each_attr(@document, :bibliography){ |b| sections << SectionData.new(type: 'bibliography', ...) }
74
+ each_attr(@document, :index) { |i| sections << SectionData.new(type: 'index', ...) }
75
+ end
76
+ sections
77
+ end
78
+ ----
79
+
80
+ ### Article
81
+
82
+ For `<article>` documents, the TOC contains top-level sections and their recursive children:
83
+
84
+ - **Sections** -- Nested recursively via `collect_section_children`
85
+
86
+ ### Reference
87
+
88
+ For `<reference>` documents, the TOC contains reference entries:
89
+
90
+ - **RefEntry** -- Each `refentry` becomes a top-level TOC item with type `reference`
91
+
92
+ ## Recursive Collection
93
+
94
+ Section children are collected recursively through dedicated methods:
95
+
96
+ - `collect_part_children` -- Collects chapters, references, appendices, prefaces, glossary, bibliography, and index within a Part
97
+ - `collect_section_children` -- Recursively collects nested `section` elements
98
+ - `collect_appendix_children` -- Collects sections within an Appendix
99
+
100
+ The recursion depth is unbounded, supporting arbitrarily nested section hierarchies.
101
+
102
+ ## Vue Sidebar Rendering
103
+
104
+ The Vue frontend renders the TOC tree using the recursive `TocTreeItem` component:
105
+
106
+ [source,typescript]
107
+ ----
108
+ // TocTreeItem.vue renders each TOC node:
109
+ // - Collapsible toggle for nodes with children
110
+ // - Type badge (Pt, Ch, App, etc.) based on item.type
111
+ // - Section number from the numbering map
112
+ // - Title text
113
+ // - Active state highlighting
114
+ // - Recursive rendering of children with indentation
115
+ ----
116
+ ----
117
+
118
+ The sidebar features:
119
+
120
+ - **Collapsible tree** -- Items with children show an expand/collapse toggle
121
+ - **Type badges** -- Color-coded labels indicating section type
122
+ - **Section numbers** -- Right-aligned monospace numbers from the numbering map
123
+ - **Active tracking** -- The currently visible section is highlighted via `IntersectionObserver`
124
+ - **Deep linking** -- Clicking a TOC item scrolls to the section via anchor link
125
+
126
+ ## Title Resolution
127
+
128
+ Section titles are resolved using a priority chain:
129
+
130
+ . `element.info.title.content` -- Title inside an `info` element
131
+ . `element.title.content` -- Direct title child
132
+ . Fallback string (e.g., "Preface", "Chapter", "Glossary")
133
+
134
+ [source,ruby]
135
+ ----
136
+ def best_title(el)
137
+ title_of(el)
138
+ end
139
+
140
+ def title_of(el)
141
+ case el
142
+ when Elements::Book, Elements::Article
143
+ el.info&.title&.content
144
+ when Elements::Chapter, Elements::Appendix, Elements::Section,
145
+ Elements::Part, Elements::Preface
146
+ el.info&.title&.content || el.title&.content
147
+ # ...
148
+ end
149
+ end
150
+ ----
@@ -0,0 +1,287 @@
1
+ ---
2
+ layout: default
3
+ title: fragid Schemes
4
+ parent: XInclude Resolution
5
+ grand_parent: Features
6
+ nav_order: 3
7
+ ---
8
+
9
+ # fragid Schemes
10
+
11
+ The `fragid` attribute on text-mode XIncludes enables selective extraction of file content. Three filtering schemes are supported: `search=` for pattern-based extraction, `line=` for line-number ranges, and `char=` for character offsets. This is a key feature for including specific portions of source files in documentation.
12
+
13
+ ## Overview
14
+
15
+ The `fragid` attribute is applied to `xi:include` elements with `parse="text"`:
16
+
17
+ [source,xml]
18
+ ----
19
+ <programlisting language="ruby">
20
+ <xi:include href="lib/example.rb" parse="text" fragid="line=10,25"/>
21
+ </programlisting>
22
+ ----
23
+
24
+ The resolver dispatches to the appropriate filter based on the scheme prefix:
25
+
26
+ [source,ruby]
27
+ ----
28
+ def self.apply_fragid(content, fragid)
29
+ if fragid.start_with?("search=")
30
+ apply_search_fragid(content, fragid[7..])
31
+ elsif fragid.start_with?("line=")
32
+ apply_line_fragid(content, fragid[5..])
33
+ elsif fragid.start_with?("char=")
34
+ apply_char_fragid(content, fragid[5..])
35
+ end
36
+ end
37
+ ----
38
+
39
+ ## search= Scheme (DocBook xslTNG Extension)
40
+
41
+ The `search=` scheme is an extension inspired by the https://github.com/docbook/xslTNG[DocBook xslTNG stylesheets]. It extracts content by matching patterns, with support for literal and regex matching, directional modifiers, and stop patterns.
42
+
43
+ ### Syntax
44
+
45
+ ----
46
+ search=DELIMITER pattern DELIMITER [;after|;before] [,DELIMITER stop_pattern DELIMITER]
47
+ ----
48
+
49
+ Where `DELIMITER` is `#` (literal match) or `/` (regex match).
50
+
51
+ ### Literal Match with `#`
52
+
53
+ The `#` delimiter performs literal string matching:
54
+
55
+ [source,xml]
56
+ ----
57
+ <!-- Include lines containing "xmlns:css" up to lines containing "a border" -->
58
+ <programlisting language="xml">
59
+ <xi:include href="stylesheets/main.xsl" parse="text"
60
+ fragid="search=#xmlns:css#,#a border#"/>
61
+ </programlisting>
62
+ ----
63
+
64
+ This extracts all lines from the first line containing `xmlns:css` up to (but not including) the first line containing `a border`.
65
+
66
+ ### Regex Match with `/`
67
+
68
+ The `/` delimiter performs regular expression matching:
69
+
70
+ [source,xml]
71
+ ----
72
+ <!-- Include lines matching a regex pattern -->
73
+ <programlisting language="ruby">
74
+ <xi:include href="lib/docbook.rb" parse="text"
75
+ fragid="search=/def process_image/"/>
76
+ </programlisting>
77
+ ----
78
+
79
+ This extracts the first line matching the regex `def process_image`.
80
+
81
+ ### Modifiers
82
+
83
+ Two modifiers control which lines around the match are included:
84
+
85
+ **`;after`** -- Extract lines *after* the match (excludes the matching line itself):
86
+
87
+ [source,xml]
88
+ ----
89
+ <!-- Include lines after the "class Html" match, up to the "end" match -->
90
+ <programlisting language="ruby">
91
+ <xi:include href="lib/docbook/output/html.rb" parse="text"
92
+ fragid="search=#class Html#;after,#private#"/>
93
+ </programlisting>
94
+ ----
95
+
96
+ **`;before`** -- Extract lines *before* the match (excludes the matching line itself):
97
+
98
+ [source,xml]
99
+ ----
100
+ <!-- Include all lines before the "Copyright" notice -->
101
+ <programlisting language="ruby">
102
+ <xi:include href="lib/docbook.rb" parse="text"
103
+ fragid="search=#Copyright#;before"/>
104
+ </programlisting>
105
+ ----
106
+
107
+ ### Stop Patterns
108
+
109
+ A stop pattern terminates extraction at the first matching line (exclusive -- the stop line is not included):
110
+
111
+ [source,xml]
112
+ ----
113
+ <!-- Extract from "class Html" to "def initialize" -->
114
+ <programlisting language="ruby">
115
+ <xi:include href="lib/docbook/output/html.rb" parse="text"
116
+ fragid="search=#class Html#,#def initialize#"/>
117
+ </programlisting>
118
+ ----
119
+
120
+ [source,xml]
121
+ ----
122
+ <!-- Extract from regex match to regex stop -->
123
+ <programlisting language="ruby">
124
+ <xi:include href="lib/docbook/output/html.rb" parse="text"
125
+ fragid="search=/def to_html/,/end$/"/>
126
+ </programlisting>
127
+ ----
128
+
129
+ ### Complete search= Examples
130
+
131
+ [source,xml]
132
+ ----
133
+ <!-- Example 1: Single line literal match -->
134
+ <programlisting>
135
+ <xi:include href="config.yaml" parse="text"
136
+ fragid="search=#database:#"/>
137
+ </programlisting>
138
+
139
+ <!-- Example 2: Range from literal start to literal stop -->
140
+ <programlisting language="ruby">
141
+ <xi:include href="lib/parser.rb" parse="text"
142
+ fragid="search=#class Parser#,#class SyntaxError#"/>
143
+ </programlisting>
144
+
145
+ <!-- Example 3: Lines after a match, with stop -->
146
+ <programlisting language="xml">
147
+ <xi:include href="schema.xsd" parse="text"
148
+ fragid="search=#complexType name=&quot;Book&quot;#;after,#</complexType>#"/>
149
+ </programlisting>
150
+
151
+ <!-- Example 4: Regex range -->
152
+ <programlisting language="ruby">
153
+ <xi:include href="spec/example_spec.rb" parse="text"
154
+ fragid="search=/describe.*Html/,/end$/"/>
155
+ </programlisting>
156
+
157
+ <!-- Example 5: Lines before a marker -->
158
+ <programlisting language="yaml">
159
+ <xi:include href="docker-compose.yml" parse="text"
160
+ fragid="search=#volumes:#;before"/>
161
+ </programlisting>
162
+ ----
163
+
164
+ ## line= Scheme (RFC 5147)
165
+
166
+ The `line=` scheme implements the https://www.rfc-editor.org/rfc/rfc5147[RFC 5147] line-based text range fragment identifier. It extracts a range of lines by 1-based line number.
167
+
168
+ ### Syntax
169
+
170
+ ----
171
+ line=START,END[;length=N]
172
+ ----
173
+
174
+ - `START` -- First line to include (1-based)
175
+ - `END` -- Last line to include (inclusive)
176
+ - `length=N` -- Optional maximum character length for the extracted content
177
+
178
+ ### Basic Line Range
179
+
180
+ [source,xml]
181
+ ----
182
+ <!-- Include lines 10 through 25 of the file -->
183
+ <programlisting language="ruby">
184
+ <xi:include href="lib/docbook.rb" parse="text" fragid="line=10,25"/>
185
+ </programlisting>
186
+ ----
187
+
188
+ This extracts lines 10, 11, 12, ..., 25 from the file (1-based indexing).
189
+
190
+ ### Line Range with Length Truncation
191
+
192
+ [source,xml]
193
+ ----
194
+ <!-- Include lines 3 through 50, truncated to 889 characters -->
195
+ <programlisting language="ruby">
196
+ <xi:include href="lib/docbook/output/html.rb" parse="text"
197
+ fragid="line=3,50;length=889"/>
198
+ </programlisting>
199
+ ----
200
+
201
+ The `length=` modifier truncates the output to at most N characters. This is useful for:
202
+
203
+ - Showing a representative sample of a long file
204
+ - Limiting output size in generated documentation
205
+ - Ensuring consistent example lengths
206
+
207
+ ### Examples
208
+
209
+ [source,xml]
210
+ ----
211
+ <!-- First 5 lines of a file -->
212
+ <programlisting>
213
+ <xi:include href="README.md" parse="text" fragid="line=1,5"/>
214
+ </programlisting>
215
+
216
+ <!-- Lines 100-120 (a specific function) -->
217
+ <programlisting language="python">
218
+ <xi:include href="app.py" parse="text" fragid="line=100,120"/>
219
+ </programlisting>
220
+
221
+ <!-- A specific range, max 500 characters -->
222
+ <programlisting language="javascript">
223
+ <xi:include href="index.js" parse="text" fragid="line=1,100;length=500"/>
224
+ </programlisting>
225
+ ----
226
+
227
+ ## char= Scheme (RFC 5147)
228
+
229
+ The `char=` scheme implements the https://www.rfc-editor.org/rfc/rfc5147[RFC 5147] character-based text range fragment identifier. It extracts content by character offset.
230
+
231
+ ### Syntax
232
+
233
+ ----
234
+ char=START,END
235
+ ----
236
+
237
+ - `START` -- Starting character offset (0-based)
238
+ - `END` -- Ending character offset (exclusive)
239
+
240
+ ### Usage
241
+
242
+ [source,xml]
243
+ ----
244
+ <!-- Extract characters 0 through 500 (first 500 characters) -->
245
+ <programlisting>
246
+ <xi:include href="data/output.txt" parse="text" fragid="char=0,500"/>
247
+ </programlisting>
248
+ ----
249
+
250
+ [source,xml]
251
+ ----
252
+ <!-- Extract a specific section by character offset -->
253
+ <programlisting>
254
+ <xi:include href="large-file.txt" parse="text" fragid="char=1024,2048"/>
255
+ </programlisting>
256
+ ----
257
+
258
+ ### Examples
259
+
260
+ [source,xml]
261
+ ----
262
+ <!-- First 1000 characters -->
263
+ <programlisting>
264
+ <xi:include href="CHANGELOG.md" parse="text" fragid="char=0,1000"/>
265
+ </programlisting>
266
+
267
+ <!-- Characters from offset 500 to 1000 -->
268
+ <programlisting language="json">
269
+ <xi:include href="package.json" parse="text" fragid="char=500,1000"/>
270
+ </programlisting>
271
+ ----
272
+
273
+ ## Scheme Comparison
274
+
275
+ [cols="1,1,3,3"]
276
+ |===
277
+ | Scheme | Standard | Best For | Example
278
+
279
+ | `search=` | DocBook xslTNG | Pattern-based extraction, code with markers
280
+ | `search=#def process#;after,#end$#`
281
+
282
+ | `line=` | RFC 5147 | Known line ranges, fixed-position content
283
+ | `line=10,25;length=889`
284
+
285
+ | `char=` | RFC 5147 | Known byte offsets, binary-adjacent content
286
+ | `char=0,500`
287
+ |===