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,145 @@
1
+ ---
2
+ layout: default
3
+ title: Architecture
4
+ parent: Understanding
5
+ nav_order: 1
6
+ ---
7
+
8
+ # Architecture
9
+
10
+ The Metanorma DocBook gem is organized into distinct modules, each
11
+ responsible for a specific phase of document processing. This page
12
+ describes the purpose of each module and how they interact.
13
+
14
+ == Module structure
15
+
16
+ === Docbook::Document
17
+
18
+ The entry point for parsing. `Document.from_xml` accepts an XML string,
19
+ detects the root element name (such as `book` or `article`), looks up
20
+ the corresponding element class in `ROOT_ELEMENT_MAP`, and delegates
21
+ to that class's `from_xml` method.
22
+
23
+ === Docbook::Elements
24
+
25
+ Contains 157 element classes, one per supported DocBook XML element.
26
+ Each class inherits from `Lutaml::Model::Serializable` and declares
27
+ typed attributes and XML mappings using the lutaml-model DSL. Classes
28
+ are autoloaded on demand from `lib/docbook/elements/`.
29
+
30
+ === Docbook::XIncludeResolver
31
+
32
+ Resolves XInclude directives before model parsing. It iterates through
33
+ `xi:include` elements in a Nokogiri document, fetching referenced files
34
+ and replacing the include elements with the included content. Supports
35
+ both XML and text parse modes, plus `fragid` extensions for line-range
36
+ and search-based text extraction.
37
+
38
+ === Docbook::XrefResolver
39
+
40
+ After parsing, builds an O(1) hash map of `xml:id` values to their
41
+ corresponding element objects. The resolver walks the element tree
42
+ recursively, collecting every element that has an `xml_id`. Callers
43
+ then use `title_for(linkend)` to resolve cross-reference text.
44
+
45
+ === Docbook::Output::Html
46
+
47
+ The HTML generation engine. It takes a parsed element tree and an
48
+ optional `XrefResolver`, then walks the tree to produce structured
49
+ data (TOC, content blocks, numbering, index). The final output is
50
+ rendered through a Liquid template with inline or linked JSON data
51
+ for the Vue SPA frontend.
52
+
53
+ === Docbook::Output::Data
54
+
55
+ Defines the data model classes used by the HTML generator:
56
+ `ContentBlock`, `SectionData`, `NumberingBuilder`, `DocbookOutput`,
57
+ and related types. These classes hold the intermediate representation
58
+ between the element tree and JSON serialization.
59
+
60
+ === Docbook::Models
61
+
62
+ A parallel set of pre-computed models (`DocumentRoot`, `TocNode`,
63
+ `SectionRoot`, etc.) that represent the final JSON structure consumed
64
+ by the Vue SPA. These are separate from `Output::Data` because they
65
+ include additional metadata like reading position and generator version.
66
+
67
+ === Docbook::CLI
68
+
69
+ A Thor-based command-line interface that provides the `format`,
70
+ `validate`, `to-html`, and `roundtrip` commands. It wires together
71
+ the parsing, resolution, and output modules based on user options.
72
+
73
+ == Data flow
74
+
75
+ The following diagram shows the complete data flow from XML input to
76
+ final HTML output:
77
+
78
+ ....
79
+ +-----------------+
80
+ | XML Input |
81
+ | (string/file) |
82
+ +--------+--------+
83
+ |
84
+ v
85
+ +--------+--------+
86
+ | XIncludeResolver | (optional)
87
+ | resolve xi: |
88
+ | include refs |
89
+ +--------+--------+
90
+ |
91
+ v
92
+ +--------+--------+
93
+ | Document.from_xml|
94
+ | Nokogiri parse |
95
+ | -> Element model |
96
+ +--------+--------+
97
+ |
98
+ v
99
+ +--------+--------+
100
+ | XrefResolver |
101
+ | Build O(1) ID |
102
+ | lookup hash |
103
+ +--------+--------+
104
+ |
105
+ v
106
+ +--------+--------+
107
+ | Output::Html |
108
+ | Section tree |
109
+ | Numbering |
110
+ | Content blocks |
111
+ | Index collection|
112
+ +--------+--------+
113
+ |
114
+ v
115
+ +--------+--------+
116
+ | Liquid Template |
117
+ | Inline JSON |
118
+ | or linked JSON |
119
+ +--------+--------+
120
+ |
121
+ v
122
+ +--------+--------+
123
+ | HTML Output |
124
+ | (single file or |
125
+ | directory) |
126
+ +-----------------+
127
+ ....
128
+
129
+ In single-file mode, all JSON data and CSS/JS assets are embedded
130
+ directly into the HTML. In directory mode, the JSON is written to
131
+ `docbook.data.json` and assets are copied to an `assets/` subdirectory.
132
+
133
+ == Element model system
134
+
135
+ Every DocBook element is represented by a Ruby class that inherits from
136
+ `Lutaml::Model::Serializable`. This provides:
137
+
138
+ * **Typed attributes** via the `attribute` DSL method
139
+ * **Mixed content** support via the `mixed_content` declaration
140
+ * **XML serialization** via `from_xml` / `to_xml`
141
+ * **JSON serialization** via `to_json`
142
+
143
+ The `each_mixed_content` method yields child nodes (both `String` and
144
+ element objects) in document order, which is essential for correct
145
+ inline content rendering.
@@ -0,0 +1,102 @@
1
+ ---
2
+ layout: default
3
+ title: Content Pipeline
4
+ parent: Understanding
5
+ nav_order: 2
6
+ ---
7
+
8
+ # Content Pipeline
9
+
10
+ The content pipeline transforms a DocBook XML document into a rendered
11
+ HTML page with an embedded Vue SPA. Each step produces a well-defined
12
+ intermediate representation. Understanding this pipeline is essential
13
+ for debugging output or extending the gem.
14
+
15
+ == Step 1: XML input
16
+
17
+ The pipeline begins with raw XML, provided either as a file path or a
18
+ string. The CLI reads the file and passes the string to the parsing
19
+ layer. The XML may reference external files via XInclude or contain
20
+ `xml:id` attributes for cross-referencing.
21
+
22
+ == Step 2: XInclude resolution
23
+
24
+ If XInclude processing is enabled (the default for `to-html`), the
25
+ `XIncludeResolver` preprocesses the Nokogiri document. It iterates
26
+ through all `xi:include` elements, fetches the referenced files, and
27
+ replaces the include directives with the actual content. This step
28
+ handles nested includes by re-scanning after each resolution. Text
29
+ includes with `fragid` attributes support line-range extraction and
30
+ search-based filtering.
31
+
32
+ == Step 3: Parsing into element model
33
+
34
+ `Document.from_xml` parses the (possibly resolved) XML string. It
35
+ detects the root element name and delegates to the corresponding
36
+ element class (for example, `Elements::Book` for `<book>`). The
37
+ lutaml-model library maps each XML element to a typed Ruby object
38
+ with declared attributes, collections, and mixed content.
39
+
40
+ == Step 4: XRef resolution
41
+
42
+ The `XrefResolver` walks the parsed element tree and builds a hash
43
+ map of `xml:id` values to element objects. This provides O(1) lookup
44
+ for resolving `<xref linkend="...">` references. The resolver also
45
+ extracts the best available title text from each target element.
46
+
47
+ == Step 5: Section data collection
48
+
49
+ The HTML generator performs a recursive tree walk over the parsed
50
+ document, collecting `SectionData` objects. Each `SectionData` records
51
+ the section's ID, title, type (part, chapter, section, appendix, etc.),
52
+ and child sections. The resulting tree mirrors the document's logical
53
+ structure and is used for the table of contents.
54
+
55
+ == Step 6: Numbering
56
+
57
+ The `NumberingBuilder` assigns numbers to each section in the tree.
58
+ It uses different numbering schemes depending on the section type:
59
+
60
+ * **Parts** receive uppercase Roman numerals (I, II, III).
61
+ * **Chapters** receive Arabic numerals scoped to their parent part
62
+ (1, 2, 3).
63
+ * **Sections** receive hierarchical dotted numbers (1.1, 1.2.3).
64
+ * **Appendices** receive uppercase letters (A, B, C).
65
+
66
+ The numbering map is a simple hash of ID to formatted number string.
67
+
68
+ == Step 7: Content map building
69
+
70
+ For each section, the HTML generator calls `each_mixed_content` on the
71
+ corresponding element object. This yields `String` nodes and typed
72
+ element objects in document order. A `case` statement dispatches each
73
+ element to a builder method that produces `ContentBlock` instances.
74
+ Paragraphs become `paragraph` blocks with inline children; code
75
+ listings become `code` blocks with a `language` attribute; figures
76
+ become `image` blocks with `src` and `alt` attributes.
77
+
78
+ == Step 8: Index collection and generation
79
+
80
+ The `IndexCollector` traverses the element tree looking for
81
+ `<indexterm>` elements. It collects primary, secondary, and tertiary
82
+ terms along with their containing section IDs. The `IndexGenerator`
83
+ then groups collected terms by first letter, producing an `Index` model
84
+ with `IndexGroup` children, each containing sorted `IndexTerm` entries.
85
+
86
+ == Step 9: Template rendering
87
+
88
+ The assembled `DocbookOutput` model (containing title, TOC, content,
89
+ and index) is serialized to JSON and inserted into a Liquid template.
90
+ In single-file mode, the JSON replaces placeholder tokens
91
+ (`[[DOCBOOK_DATA]]`, `[[DOCBOOK_TOC]]`, `[[DOCBOOK_CONTENT]]`,
92
+ `[[DOCBOOK_INDEX]]`) directly in the HTML. In directory mode, the
93
+ placeholders are set to `null` and the JSON is written to a separate
94
+ file that the Vue SPA fetches at runtime.
95
+
96
+ == Step 10: Output
97
+
98
+ The final step writes the rendered HTML to disk. Single-file mode
99
+ produces one self-contained HTML file with all data inlined. Directory
100
+ mode creates an output directory with `index.html`, `docbook.data.json`,
101
+ and an `assets/` directory containing `app.css` and `app.iife.js` copied
102
+ from the frontend build.
@@ -0,0 +1,156 @@
1
+ ---
2
+ layout: default
3
+ title: Data Models
4
+ parent: Understanding
5
+ nav_order: 3
6
+ ---
7
+
8
+ # Data Models
9
+
10
+ The gem uses two separate model namespaces, each serving a distinct
11
+ purpose in the pipeline. All model classes inherit from
12
+ `Lutaml::Model::Serializable`, which provides XML and JSON
13
+ serialization through a declarative DSL.
14
+
15
+ == Output::Data models (`Docbook::Output`)
16
+
17
+ These classes are defined in `lib/docbook/output/data.rb` and are used
18
+ by the HTML generator to build the intermediate representation that
19
+ becomes the JSON payload for the Vue SPA.
20
+
21
+ === DocbookOutput
22
+
23
+ The top-level output model. Contains `title`, `toc` (a `Toc` instance),
24
+ `content` (a `ContentData` instance), and `index` (an `Index`
25
+ instance). This single model encapsulates everything the Vue SPA needs
26
+ to render the document.
27
+
28
+ === ContentBlock
29
+
30
+ A recursive tree node representing a single block of content. Each
31
+ block has a `type` (such as `:paragraph`, `:code`, `:image`), optional
32
+ text, source URL, alt text, title, language, and CSS class name. Block
33
+ types that can contain nested content use the `children` attribute,
34
+ which holds an array of child `ContentBlock` instances.
35
+
36
+ === SectionData
37
+
38
+ Represents a section in the table of contents tree. Each entry has an
39
+ `id`, `title`, `type` (part, chapter, section, appendix, etc.), an
40
+ optional `number`, and `children` (an array of nested `SectionData`
41
+ instances).
42
+
43
+ === NumberingEntry
44
+
45
+ A simple key-value pair mapping a section ID to its formatted number
46
+ string. These are collected into an array within the `Toc` model.
47
+
48
+ === ContentData and ContentEntry
49
+
50
+ `ContentData` wraps a collection of `ContentEntry` objects. Each
51
+ `ContentEntry` maps a section ID (the `key`) to a `SectionContent`
52
+ instance containing the section's rendered content blocks. This
53
+ key-value structure serializes cleanly to JSON and provides O(1)
54
+ section lookup in the Vue SPA.
55
+
56
+ === SectionContent
57
+
58
+ Holds the content for a single section: a `section_id` and an array
59
+ of `ContentBlock` instances representing the section's body.
60
+
61
+ === Toc
62
+
63
+ Combines the `SectionData` tree (the recursive TOC structure) with the
64
+ `NumberingEntry` collection (the computed numbering map).
65
+
66
+ === IndexTerm
67
+
68
+ Represents a single index entry with `primary`, `secondary`, and
69
+ `tertiary` term text, plus the `section_id` and `section_title` where
70
+ the term appears. Also holds `sees` and `see_alsos` arrays for
71
+ cross-references.
72
+
73
+ === IndexGroup
74
+
75
+ Groups index terms under a `letter` heading. Contains an array of
76
+ `IndexTerm` entries.
77
+
78
+ === Index
79
+
80
+ The top-level index model with a `title` and an array of `IndexGroup`
81
+ instances, one per letter of the alphabet.
82
+
83
+ === DocumentData
84
+
85
+ A minimal model holding only the document `title`. Used in single-file
86
+ mode to initialize the `DOCBOOK_DATA` JavaScript object before merging
87
+ in the TOC, content, and index.
88
+
89
+ === NumberingBuilder
90
+
91
+ A stateful builder that tracks counters for parts (Roman numerals),
92
+ chapters (Arabic, scoped per part), sections (hierarchical, scoped per
93
+ parent), and appendices (uppercase letters). It exposes methods like
94
+ `next_part`, `next_chapter`, `next_section`, and `next_appendix` that
95
+ increment the appropriate counter and return the formatted number. The
96
+ final `numbering` hash maps section IDs to their computed numbers.
97
+
98
+ == Models namespace (`Docbook::Models`)
99
+
100
+ Defined in `lib/docbook/models/`, these classes represent the full
101
+ document structure as consumed by the Vue SPA. They include additional
102
+ metadata beyond what the output data models carry.
103
+
104
+ === DocumentRoot
105
+
106
+ The top-level model for the complete document. Contains `title`,
107
+ `metadata` (a `DocumentMetadata` instance), `toc` (an array of
108
+ `TocNode`), `sections` (an array of `SectionRoot`), `index` (an array
109
+ of `IndexEntry`), `numbering` (an array of `SectionNumber`), and a
110
+ `reading_position`. Also includes `generator` and `generated_at`
111
+ strings for debugging.
112
+
113
+ === DocumentMetadata
114
+
115
+ Stores document-level information: `author`, `author_name`, `title`,
116
+ `subtitle`, `productname`, `version`, `pubdate`, `release_info`, and
117
+ `abstract`. All fields are strings.
118
+
119
+ === TocNode
120
+
121
+ A recursive tree node for the table of contents. Each node has an `id`,
122
+ `title`, `type`, `number`, optional `page` (for PDF navigation), and
123
+ `children` (an array of nested `TocNode` instances).
124
+
125
+ === SectionRoot
126
+
127
+ Represents a section in the pre-computed document structure. Contains
128
+ `id`, `type`, `title`, `number`, and `children` (nested `SectionRoot`
129
+ instances). For reference entries, it also holds a `refname` string.
130
+
131
+ === SectionNumber
132
+
133
+ A simple mapping of section `id` to its computed `number` string and
134
+ `type`. Used by the Vue SPA to display section numbers in the UI.
135
+
136
+ === IndexEntry
137
+
138
+ Represents an index term with `primary`, `secondary`, `tertiary`,
139
+ `see_also`, `section_id`, `section_title`, and a `sort_key` for
140
+ alphabetical ordering.
141
+
142
+ === ReadingPosition
143
+
144
+ Tracks the user's reading progress. Contains `section_id`,
145
+ `percentage`, `last_read_at`, `reading_mode`, and `scroll_position`.
146
+ This data is persisted in the Vue SPA via localStorage.
147
+
148
+ == Serialization
149
+
150
+ Both model namespaces serialize to JSON for the Vue SPA. The output
151
+ data models (`Output::Data`) are what the Ruby HTML generator
152
+ constructs and serializes. The `Models` namespace provides the
153
+ TypeScript-compatible schema that the Vue SPA expects. In practice,
154
+ the HTML generator uses the `Output::Data` classes directly, and the
155
+ JSON they produce is consumed by the Pinia `documentStore` in the
156
+ frontend.
@@ -0,0 +1,34 @@
1
+ ---
2
+ layout: default
3
+ title: Understanding
4
+ nav_order: 5
5
+ has_children: true
6
+ ---
7
+
8
+ # Understanding
9
+
10
+ This section explains how the Metanorma DocBook gem works internally.
11
+ It covers the architecture, data flow, and design decisions that shape
12
+ the library.
13
+
14
+ == When to read this section
15
+
16
+ Read these pages when you want to do any of the following:
17
+
18
+ * **Extend the gem** -- Adding new DocBook element types, custom output
19
+ formats, or additional template variables requires understanding the
20
+ module structure and element class design.
21
+ * **Debug output** -- If the HTML output does not match your expectations,
22
+ the content pipeline and data model pages will help you trace where a
23
+ particular element is transformed or lost.
24
+ * **Contribute** -- Contributors should understand the full pipeline from
25
+ XML input to rendered HTML before submitting patches.
26
+
27
+ == What is covered
28
+
29
+ * <<architecture, Architecture>> -- The major modules and how they fit
30
+ together.
31
+ * <<content-pipeline, Content Pipeline>> -- The step-by-step data flow
32
+ from XML input to final output.
33
+ * <<data-models, Data Models>> -- The Ruby classes that represent
34
+ parsed and computed document data.
data/exe/docbook ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "docbook"
5
+ require "docbook/cli"
6
+
7
+ Docbook::CLI.start(ARGV)
@@ -0,0 +1 @@
1
+ .modal-enter-active[data-v-bc71f312],.modal-leave-active[data-v-bc71f312]{transition:opacity .2s ease}.modal-enter-from[data-v-bc71f312],.modal-leave-to[data-v-bc71f312]{opacity:0}.modal-enter-active>div[data-v-bc71f312]:last-child,.modal-leave-active>div[data-v-bc71f312]:last-child{transition:transform .2s ease,opacity .2s ease}.modal-enter-from>div[data-v-bc71f312]:last-child,.modal-leave-to>div[data-v-bc71f312]:last-child{transform:scale(.95);opacity:0}.reference-entry[data-v-3a1b793d]{font-family:Inter,system-ui,-apple-system,sans-serif}.reference-name[data-v-3a1b793d]{font-family:JetBrains Mono,Fira Code,ui-monospace,monospace}.reference-badge[data-v-3a1b793d]{letter-spacing:.05em}.ebook-topbar[data-v-b409adda]{position:fixed;top:0;left:0;right:0;z-index:30;background:#ffffffe6;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-bottom:1px solid rgba(0,0,0,.05);transition:left .2s ease}.dark{background:#111827e6;border-bottom-color:#ffffff1a}.ebook-topbar--with-sidebar[data-v-b409adda]{left:280px}@media (max-width: 1023px){.ebook-topbar--with-sidebar[data-v-b409adda]{left:0}}.ebook-topbar button[data-v-b409adda]{min-width:44px;min-height:44px}.theme-day{--ebook-bg: #ffffff;--ebook-bg-secondary: #f9fafb;--ebook-text: #1f2937;--ebook-text-muted: #6b7280;--ebook-accent: #0891b2;--ebook-accent-light: #ecfeff;--ebook-border: #e5e7eb;--ebook-code-bg: #1f2937;--ebook-code-text: #e5e7eb;--ebook-example-bg: #f0fdf4;--ebook-example-border: #22c55e;--ebook-reference-accent: #0891b2;--ebook-reference-bg: rgba(8, 145, 178, .03);--ebook-reference-name: #0e7490;--ebook-reference-badge-bg: #f3f4f6;--ebook-reference-badge-text: #6b7280}.theme-sepia{--ebook-bg: #fdf6e3;--ebook-bg-secondary: #f5efe0;--ebook-text: #5c5347;--ebook-text-muted: #8b7355;--ebook-accent: #b8860b;--ebook-accent-light: #fef9e7;--ebook-border: #e0d5c0;--ebook-code-bg: #3d3d3d;--ebook-code-text: #f5f5dc;--ebook-example-bg: #f0ead6;--ebook-example-border: #8b7355;--ebook-reference-accent: #b8860b;--ebook-reference-bg: rgba(184, 134, 11, .05);--ebook-reference-name: #8b6914;--ebook-reference-badge-bg: #e8dcc8;--ebook-reference-badge-text: #6b5d4d}.theme-sepia body,.theme-sepia{background-color:var(--ebook-bg);color:var(--ebook-text)}.theme-night{--ebook-bg: #111827;--ebook-bg-secondary: #1f2937;--ebook-text: #e5e7eb;--ebook-text-muted: #9ca3af;--ebook-accent: #22d3ee;--ebook-accent-light: rgba(34, 211, 238, .1);--ebook-border: #374151;--ebook-code-bg: #030712;--ebook-code-text: #e5e7eb;--ebook-example-bg: rgba(34, 197, 94, .1);--ebook-example-border: #22c55e;--ebook-reference-accent: #22d3ee;--ebook-reference-bg: rgba(34, 211, 238, .05);--ebook-reference-name: #22d3ee;--ebook-reference-badge-bg: #374151;--ebook-reference-badge-text: #9ca3af}.theme-night body,.theme-night{background-color:var(--ebook-bg);color:var(--ebook-text)}.theme-oled{--ebook-bg: #000000;--ebook-bg-secondary: #0a0a0a;--ebook-text: #ffffff;--ebook-text-muted: #a1a1aa;--ebook-accent: #60a5fa;--ebook-accent-light: rgba(96, 165, 250, .1);--ebook-border: #27272a;--ebook-code-bg: #18181b;--ebook-code-text: #fafafa;--ebook-example-bg: rgba(34, 197, 94, .05);--ebook-example-border: #22c55e;--ebook-reference-accent: #60a5fa;--ebook-reference-bg: rgba(96, 165, 250, .03);--ebook-reference-name: #60a5fa;--ebook-reference-badge-bg: #27272a;--ebook-reference-badge-text: #a1a1aa}.theme-oled body,.theme-oled{background-color:var(--ebook-bg);color:var(--ebook-text)}body{background-color:var(--ebook-bg, #ffffff);color:var(--ebook-text, #1f2937);transition:background-color .2s ease,color .2s ease}.ebook-container{font-size:var(--ebook-font-size, 18px);font-weight:var(--ebook-font-weight, 400);line-height:var(--ebook-line-height, 1.6)}.ebook-container.theme-day,.ebook-container.theme-sepia,.ebook-container.theme-night,.ebook-container.theme-oled{--bg: var(--ebook-bg);--text: var(--ebook-text)}*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:Inter,system-ui,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body.font-serif{font-family:Merriweather,Georgia,serif}body.font-sans{font-family:Inter,system-ui,sans-serif}.font-mono{font-family:JetBrains Mono,ui-monospace,monospace}.dark{color-scheme:dark}@media print{.lg\:translate-x-0,#sidebar,#sidebar-toggle,.sticky{display:none!important}}.toc-active{color:#2563eb;background:#dbeafe}.dark .toc-active{color:#60a5fa;background:#1e3a5f}.db-bibentry{padding-left:2em;text-indent:-2em;margin-bottom:.5em}.db-changelog-release{border-left:3px solid #60a5fa;padding-left:1rem;margin:1.5rem 0}.dark .db-changelog-release{border-left-color:#3b82f6}.db-appendix-wrap{border-left:4px solid #8b5cf6;padding-left:1rem;margin-left:-1rem}.dark .db-appendix-wrap{border-left-color:#a78bfa}.db-preface-wrap{border-left:4px solid #22c55e;padding-left:1rem;margin-left:-1rem}.dark .db-preface-wrap{border-left-color:#16a34a}mark.bg-yellow-200,mark.bg-yellow-600{padding:0 2px;border-radius:2px}[x-cloak]{display:none!important}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.dark ::-webkit-scrollbar-thumb{background:#4b5563}::-webkit-scrollbar-thumb:hover{background:#94a3b8}.dark ::-webkit-scrollbar-thumb:hover{background:#6b7280}.db-content h1{font-size:1.875rem;font-weight:700;margin-bottom:1rem}.db-content h2{font-size:1.25rem;font-weight:600;margin-top:2rem;margin-bottom:.75rem}.db-content h3{font-size:1.125rem;font-weight:600;margin-top:1.5rem;margin-bottom:.5rem}.db-content p{margin-bottom:1rem;line-height:1.75}.db-content ul,.db-content ol{margin-bottom:1rem;padding-left:1.5rem}.db-content li{margin-bottom:.25rem;line-height:1.6}.db-content code{font-family:JetBrains Mono,ui-monospace,monospace;font-size:.875em;background:#f1f5f9;padding:.125rem .375rem;border-radius:.25rem}.dark .db-content code{background:#374151}.db-content pre{font-family:JetBrains Mono,ui-monospace,monospace;font-size:.875rem;background:#f1f5f9;padding:1rem;border-radius:.5rem;overflow-x:auto;margin-bottom:1rem}.dark .db-content pre{background:#1f2937}.db-content a{color:#2563eb;text-decoration:underline}.dark .db-content a{color:#60a5fa}.db-content img{max-width:100%;height:auto;border-radius:.5rem;box-shadow:0 4px 6px -1px #0000001a}.dark .db-content img{box-shadow:0 4px 6px -1px #0000004d}.db-content blockquote{border-left:4px solid #d1d5db;padding-left:1rem;margin:1rem 0;font-style:italic;color:#6b7280}.dark .db-content blockquote{border-left-color:#4b5563;color:#9ca3af}.db-content table{width:100%;border-collapse:collapse;margin-bottom:1rem}.db-content th,.db-content td{border:1px solid #e5e7eb;padding:.5rem;text-align:left}.dark .db-content th,.dark .db-content td{border-color:#374151}.db-content th{background:#f9fafb;font-weight:600}.dark .db-content th{background:#1f2937}