metanorma-document 0.2.9 → 0.2.12

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 (390) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +164 -66
  3. data/CLAUDE.md +26 -0
  4. data/README.adoc +144 -0
  5. data/Rakefile +14 -2
  6. data/TODO.finalize/01-split-multi-class-files.md +8 -0
  7. data/TODO.finalize/02-remove-data-dist-iso-document-frontend.md +6 -0
  8. data/TODO.finalize/03-align-frontend-iife-and-gha-build.md +11 -0
  9. data/TODO.finalize/04-theme-per-flavor-config-driven.md +9 -0
  10. data/TODO.finalize/05-migrate-all-html-to-liquid.md +16 -0
  11. data/TODO.finalize/06-integrate-pubid-library.md +16 -0
  12. data/TODO.finalize/07-replace-relaton-models-with-relaton-bib.md +39 -0
  13. data/TODO.finalize/08-refactor-renderer-architecture.md +25 -0
  14. data/TODO.finalize/09-eliminate-code-smells.md +14 -0
  15. data/TODO.finalize/10-comprehensive-spec-coverage.md +24 -0
  16. data/TODO.finalize/11-config-driven-flavor-metadata.md +20 -0
  17. data/TODO.finalize/12-add-frozen-string-literal.md +6 -0
  18. data/TODO.finalize/13-extract-composition-from-god-class.md +96 -0
  19. data/TODO.finalize/14-config-driven-section-ordering.md +31 -0
  20. data/TODO.finalize/15-theme-directories-per-flavor.md +46 -0
  21. data/TODO.finalize/16-theme-lutaml-model-schema.md +44 -0
  22. data/data/javascripts/components/mn-footnotes.js +68 -0
  23. data/data/javascripts/components/mn-lightbox.js +18 -2
  24. data/data/javascripts/components/mn-shortcuts.js +2 -0
  25. data/data/javascripts/core/mn-navigation.js +8 -0
  26. data/data/javascripts/core/mn-theme.js +9 -9
  27. data/data/logos/oiml-logo-full-dark.svg +1 -0
  28. data/data/logos/oiml-logo-full-light.svg +1 -0
  29. data/data/logos/oiml-logo-icon-dark.svg +1 -0
  30. data/data/logos/oiml-logo-icon-light.svg +1 -0
  31. data/data/logos/oiml-logo.svg +1 -0
  32. data/data/logos/pdfa-logo.svg +31 -94
  33. data/data/stylesheets/base/_layout.css +3 -0
  34. data/data/stylesheets/base/_reset.css +3 -3
  35. data/data/stylesheets/base/_typography.css +0 -5
  36. data/data/stylesheets/components/bibliography.css +3 -3
  37. data/data/stylesheets/components/cover.css +3 -1
  38. data/data/stylesheets/components/figure.css +13 -1
  39. data/data/stylesheets/components/footnote.css +65 -5
  40. data/data/stylesheets/components/formula.css +44 -0
  41. data/data/stylesheets/components/header.css +17 -2
  42. data/data/stylesheets/components/inline.css +1 -1
  43. data/data/stylesheets/components/table.css +15 -2
  44. data/data/stylesheets/components/term.css +0 -1
  45. data/data/themes/bipm.yaml +60 -0
  46. data/data/themes/cc.yaml +43 -0
  47. data/data/themes/icc.yaml +39 -0
  48. data/data/themes/iec.yaml +44 -0
  49. data/data/themes/ieee.yaml +43 -0
  50. data/data/themes/ietf.yaml +45 -0
  51. data/data/themes/iho.yaml +46 -0
  52. data/data/themes/iso.yaml +40 -0
  53. data/data/themes/itu.yaml +45 -0
  54. data/data/themes/ogc.yaml +51 -0
  55. data/data/themes/oiml.yaml +56 -0
  56. data/data/themes/pdfa.yaml +44 -0
  57. data/data/themes/ribose.yaml +43 -0
  58. data/exe/metanorma-document +11 -0
  59. data/frontend/e2e/sample-html.test.ts +256 -0
  60. data/frontend/index.html +12 -0
  61. data/frontend/package-lock.json +4725 -0
  62. data/frontend/package.json +32 -0
  63. data/frontend/postcss.config.js +6 -0
  64. data/frontend/src/App.vue +79 -0
  65. data/frontend/src/app.ts +21 -0
  66. data/frontend/src/components/AppSidebar.vue +77 -0
  67. data/frontend/src/components/DocumentReader.vue +180 -0
  68. data/frontend/src/components/MirrorRenderer.vue +23 -0
  69. data/frontend/src/components/ReadingProgressBar.vue +29 -0
  70. data/frontend/src/components/TextRenderer.vue +131 -0
  71. data/frontend/src/components/TocItem.vue +57 -0
  72. data/frontend/src/components/TopBar.vue +49 -0
  73. data/frontend/src/components/__tests__/MirrorRenderer.test.ts +178 -0
  74. data/frontend/src/components/blocks/AdmonitionBlock.vue +74 -0
  75. data/frontend/src/components/blocks/ClauseBlock.vue +53 -0
  76. data/frontend/src/components/blocks/ExampleBlock.vue +21 -0
  77. data/frontend/src/components/blocks/FallbackBlock.vue +18 -0
  78. data/frontend/src/components/blocks/FigureBlock.vue +26 -0
  79. data/frontend/src/components/blocks/FootnotesBlock.vue +25 -0
  80. data/frontend/src/components/blocks/ListBlocks.vue +62 -0
  81. data/frontend/src/components/blocks/NoteBlock.vue +21 -0
  82. data/frontend/src/components/blocks/ParagraphBlock.vue +25 -0
  83. data/frontend/src/components/blocks/QuoteBlock.vue +25 -0
  84. data/frontend/src/components/blocks/ReviewBlock.vue +39 -0
  85. data/frontend/src/components/blocks/SourcecodeBlock.vue +52 -0
  86. data/frontend/src/components/blocks/TableBlock.vue +48 -0
  87. data/frontend/src/components/blocks/index.ts +36 -0
  88. data/frontend/src/stores/__tests__/documentStore.fixture.test.ts +272 -0
  89. data/frontend/src/stores/__tests__/documentStore.test.ts +143 -0
  90. data/frontend/src/stores/__tests__/ebookStore.test.ts +145 -0
  91. data/frontend/src/stores/__tests__/uiStore.test.ts +42 -0
  92. data/frontend/src/stores/documentStore.ts +153 -0
  93. data/frontend/src/stores/ebookStore.ts +179 -0
  94. data/frontend/src/stores/uiStore.ts +32 -0
  95. data/frontend/src/styles.css +156 -0
  96. data/frontend/src/types/__tests__/rendering-contract.test.ts +172 -0
  97. data/frontend/src/types/rendering-contract.ts +209 -0
  98. data/frontend/src/utils/__tests__/typeMetadata.test.ts +102 -0
  99. data/frontend/src/utils/typeMetadata.ts +121 -0
  100. data/frontend/tailwind.config.js +18 -0
  101. data/frontend/test/fixtures/iso-document.json +271 -0
  102. data/frontend/tmp/debug_e2e.cjs +46 -0
  103. data/frontend/tsconfig.json +25 -0
  104. data/frontend/tsconfig.node.json +11 -0
  105. data/frontend/vite.config.ts +37 -0
  106. data/frontend/vitest.e2e.config.ts +9 -0
  107. data/lib/metanorma/bipm_document/metadata/bipm_bibliographic_item.rb +0 -14
  108. data/lib/metanorma/bipm_document/metadata/depiction_element.rb +18 -0
  109. data/lib/metanorma/bipm_document/metadata.rb +1 -1
  110. data/lib/metanorma/document/cli.rb +116 -0
  111. data/lib/metanorma/document/components/ancillary_blocks/callout_annotation.rb +27 -0
  112. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_block.rb +0 -65
  113. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_body.rb +18 -0
  114. data/lib/metanorma/document/components/ancillary_blocks.rb +4 -0
  115. data/lib/metanorma/document/components/bib_data/bib_data.rb +7 -0
  116. data/lib/metanorma/document/components/blocks/classification_value.rb +21 -0
  117. data/lib/metanorma/document/components/blocks/permission_model.rb +28 -0
  118. data/lib/metanorma/document/components/blocks/recommendation_model.rb +28 -0
  119. data/lib/metanorma/document/components/blocks/requirement_base.rb +30 -0
  120. data/lib/metanorma/document/components/blocks/requirement_classification.rb +20 -0
  121. data/lib/metanorma/document/components/blocks/requirement_description.rb +43 -0
  122. data/lib/metanorma/document/components/blocks/requirement_inherit.rb +24 -0
  123. data/lib/metanorma/document/components/blocks/requirement_model.rb +0 -131
  124. data/lib/metanorma/document/components/blocks.rb +8 -7
  125. data/lib/metanorma/document/components/id_elements/image.rb +2 -0
  126. data/lib/metanorma/document/components/inline/asciimath_element.rb +2 -0
  127. data/lib/metanorma/document/components/inline/attribution_element.rb +2 -0
  128. data/lib/metanorma/document/components/inline/bcp14_element.rb +2 -0
  129. data/lib/metanorma/document/components/inline/biblio_tag_element.rb +2 -0
  130. data/lib/metanorma/document/components/inline/br_element.rb +2 -0
  131. data/lib/metanorma/document/components/inline/comma_element.rb +2 -0
  132. data/lib/metanorma/document/components/inline/concept_element.rb +2 -0
  133. data/lib/metanorma/document/components/inline/display_text_element.rb +2 -0
  134. data/lib/metanorma/document/components/inline/em_raw_element.rb +2 -0
  135. data/lib/metanorma/document/components/inline/enum_comma_element.rb +2 -0
  136. data/lib/metanorma/document/components/inline/eref_element.rb +2 -0
  137. data/lib/metanorma/document/components/inline/fmt_admitted_element.rb +4 -0
  138. data/lib/metanorma/document/components/inline/fmt_annotation_body_element.rb +4 -0
  139. data/lib/metanorma/document/components/inline/fmt_annotation_end_element.rb +4 -0
  140. data/lib/metanorma/document/components/inline/fmt_annotation_start_element.rb +4 -0
  141. data/lib/metanorma/document/components/inline/fmt_concept_element.rb +4 -0
  142. data/lib/metanorma/document/components/inline/fmt_definition_element.rb +4 -0
  143. data/lib/metanorma/document/components/inline/fmt_fn_body_element.rb +4 -0
  144. data/lib/metanorma/document/components/inline/fmt_fn_label_element.rb +4 -0
  145. data/lib/metanorma/document/components/inline/fmt_footnote_container_element.rb +4 -0
  146. data/lib/metanorma/document/components/inline/fmt_identifier_element.rb +4 -0
  147. data/lib/metanorma/document/components/inline/fmt_name_element.rb +4 -0
  148. data/lib/metanorma/document/components/inline/fmt_preferred_element.rb +4 -0
  149. data/lib/metanorma/document/components/inline/fmt_sourcecode_element.rb +4 -0
  150. data/lib/metanorma/document/components/inline/fmt_stem_element.rb +10 -0
  151. data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +4 -0
  152. data/lib/metanorma/document/components/inline/fmt_title_element.rb +4 -0
  153. data/lib/metanorma/document/components/inline/fmt_xref_element.rb +4 -0
  154. data/lib/metanorma/document/components/inline/fmt_xref_label_element.rb +4 -0
  155. data/lib/metanorma/document/components/inline/fn_element.rb +2 -0
  156. data/lib/metanorma/document/components/inline/link_element.rb +2 -0
  157. data/lib/metanorma/document/components/inline/localized_string_element.rb +2 -0
  158. data/lib/metanorma/document/components/inline/localized_strings_element.rb +2 -0
  159. data/lib/metanorma/document/components/inline/location_element.rb +24 -0
  160. data/lib/metanorma/document/components/inline/math_element.rb +2 -0
  161. data/lib/metanorma/document/components/inline/name_with_id_element.rb +4 -0
  162. data/lib/metanorma/document/components/inline/rendered_display.rb +23 -0
  163. data/lib/metanorma/document/components/inline/rendered_math_element.rb +21 -0
  164. data/lib/metanorma/document/components/inline/semantic_content.rb +51 -0
  165. data/lib/metanorma/document/components/inline/semantic_math_element.rb +20 -0
  166. data/lib/metanorma/document/components/inline/semx_child_element.rb +2 -0
  167. data/lib/metanorma/document/components/inline/semx_element.rb +13 -1
  168. data/lib/metanorma/document/components/inline/small_cap_element.rb +2 -0
  169. data/lib/metanorma/document/components/inline/span_element.rb +10 -15
  170. data/lib/metanorma/document/components/inline/stem_inline_element.rb +7 -1
  171. data/lib/metanorma/document/components/inline/strong_raw_element.rb +2 -0
  172. data/lib/metanorma/document/components/inline/sub_element.rb +2 -0
  173. data/lib/metanorma/document/components/inline/sup_element.rb +2 -0
  174. data/lib/metanorma/document/components/inline/tab_element.rb +2 -0
  175. data/lib/metanorma/document/components/inline/title_with_annotation_element.rb +4 -0
  176. data/lib/metanorma/document/components/inline/tt_element.rb +2 -0
  177. data/lib/metanorma/document/components/inline/variant_title_element.rb +2 -0
  178. data/lib/metanorma/document/components/inline/vocabulary.rb +184 -0
  179. data/lib/metanorma/document/components/inline/xref_element.rb +10 -0
  180. data/lib/metanorma/document/components/inline.rb +12 -0
  181. data/lib/metanorma/document/components/multi_paragraph/quote_author_element.rb +20 -0
  182. data/lib/metanorma/document/components/multi_paragraph/quote_block.rb +0 -29
  183. data/lib/metanorma/document/components/multi_paragraph.rb +2 -0
  184. data/lib/metanorma/document/components/reference_elements/source_element.rb +0 -31
  185. data/lib/metanorma/document/components/reference_elements/source_modification.rb +21 -0
  186. data/lib/metanorma/document/components/reference_elements/source_origin.rb +28 -0
  187. data/lib/metanorma/document/components/reference_elements.rb +2 -2
  188. data/lib/metanorma/document/components/tables/col_element.rb +18 -0
  189. data/lib/metanorma/document/components/tables/col_group_element.rb +18 -0
  190. data/lib/metanorma/document/components/tables/table_block.rb +0 -22
  191. data/lib/metanorma/document/components/tables/table_body_section.rb +16 -0
  192. data/lib/metanorma/document/components/tables/table_foot_section.rb +16 -0
  193. data/lib/metanorma/document/components/tables/table_head_section.rb +16 -0
  194. data/lib/metanorma/document/components/tables/table_section.rb +0 -25
  195. data/lib/metanorma/document/components/tables.rb +5 -3
  196. data/lib/metanorma/document/relaton/address.rb +0 -27
  197. data/lib/metanorma/document/relaton/bib_item_locality.rb +3 -12
  198. data/lib/metanorma/document/relaton/copyright_association.rb +0 -10
  199. data/lib/metanorma/document/relaton/copyright_owner.rb +16 -0
  200. data/lib/metanorma/document/relaton/edition.rb +4 -7
  201. data/lib/metanorma/document/relaton/formatted_address.rb +29 -0
  202. data/lib/metanorma/document/relaton/logo_element.rb +18 -0
  203. data/lib/metanorma/document/relaton/organization.rb +0 -14
  204. data/lib/metanorma/document/relaton/phone.rb +3 -9
  205. data/lib/metanorma/document/relaton/place_type.rb +0 -13
  206. data/lib/metanorma/document/relaton/price_type.rb +3 -9
  207. data/lib/metanorma/document/relaton/region_element.rb +18 -0
  208. data/lib/metanorma/document/relaton.rb +6 -1
  209. data/lib/metanorma/document/version.rb +1 -1
  210. data/lib/metanorma/document.rb +2 -0
  211. data/lib/metanorma/html/asset_pipeline.rb +1 -0
  212. data/lib/metanorma/html/base_renderer.rb +261 -1008
  213. data/lib/metanorma/html/bipm_renderer.rb +15 -144
  214. data/lib/metanorma/html/cc_renderer.rb +1 -45
  215. data/lib/metanorma/html/component/base.rb +2 -7
  216. data/lib/metanorma/html/component/index_section.rb +41 -43
  217. data/lib/metanorma/html/drops/admonition_drop.rb +1 -4
  218. data/lib/metanorma/html/drops/biblio_entry_drop.rb +50 -0
  219. data/lib/metanorma/html/drops/block_element_drop.rb +7 -2
  220. data/lib/metanorma/html/drops/example_drop.rb +1 -4
  221. data/lib/metanorma/html/drops/figure_drop.rb +31 -22
  222. data/lib/metanorma/html/drops/footnote_drop.rb +1 -1
  223. data/lib/metanorma/html/drops/formula_drop.rb +45 -16
  224. data/lib/metanorma/html/drops/note_drop.rb +9 -7
  225. data/lib/metanorma/html/drops/sourcecode_drop.rb +12 -5
  226. data/lib/metanorma/html/drops.rb +2 -0
  227. data/lib/metanorma/html/generator.rb +2 -2
  228. data/lib/metanorma/html/icc_renderer.rb +0 -39
  229. data/lib/metanorma/html/iec_renderer.rb +1 -44
  230. data/lib/metanorma/html/ieee_renderer.rb +1 -45
  231. data/lib/metanorma/html/ietf_renderer.rb +1 -48
  232. data/lib/metanorma/html/iho_renderer.rb +1 -46
  233. data/lib/metanorma/html/iso_renderer.rb +290 -343
  234. data/lib/metanorma/html/itu_renderer.rb +1 -45
  235. data/lib/metanorma/html/ogc_renderer.rb +15 -76
  236. data/lib/metanorma/html/oiml_renderer.rb +18 -38
  237. data/lib/metanorma/html/pdfa_renderer.rb +0 -39
  238. data/lib/metanorma/html/renderers/block_renderer.rb +372 -0
  239. data/lib/metanorma/html/renderers/inline_renderer.rb +655 -0
  240. data/lib/metanorma/html/renderers/pubid_renderer.rb +69 -0
  241. data/lib/metanorma/html/renderers/section_renderer.rb +193 -0
  242. data/lib/metanorma/html/renderers.rb +8 -0
  243. data/lib/metanorma/html/ribose_renderer.rb +1 -45
  244. data/lib/metanorma/html/standard_renderer.rb +341 -229
  245. data/lib/metanorma/html/templates/_audio.html.liquid +1 -0
  246. data/lib/metanorma/html/templates/_biblio_entry.html.liquid +1 -0
  247. data/lib/metanorma/html/templates/_bipm_cover.html.liquid +27 -0
  248. data/lib/metanorma/html/templates/_boilerplate.html.liquid +1 -0
  249. data/lib/metanorma/html/templates/_bookmark.html.liquid +1 -0
  250. data/lib/metanorma/html/templates/_br.html.liquid +1 -0
  251. data/lib/metanorma/html/templates/_definition_list.html.liquid +6 -0
  252. data/lib/metanorma/html/templates/_element.html.liquid +1 -0
  253. data/lib/metanorma/html/templates/_fn_marker.html.liquid +1 -0
  254. data/lib/metanorma/html/templates/_footnotes.html.liquid +6 -6
  255. data/lib/metanorma/html/templates/_formula.html.liquid +1 -1
  256. data/lib/metanorma/html/templates/_header.html.liquid +1 -1
  257. data/lib/metanorma/html/templates/_heading.html.liquid +1 -0
  258. data/lib/metanorma/html/templates/_image.html.liquid +1 -0
  259. data/lib/metanorma/html/templates/_inline_span.html.liquid +1 -0
  260. data/lib/metanorma/html/templates/_iso_cover.html.liquid +1 -1
  261. data/lib/metanorma/html/templates/_link.html.liquid +1 -0
  262. data/lib/metanorma/html/templates/_list.html.liquid +3 -0
  263. data/lib/metanorma/html/templates/_list_item.html.liquid +1 -0
  264. data/lib/metanorma/html/templates/_main_content.html.liquid +1 -0
  265. data/lib/metanorma/html/templates/_math_container.html.liquid +1 -0
  266. data/lib/metanorma/html/templates/_paragraph.html.liquid +1 -0
  267. data/lib/metanorma/html/templates/_prefatory_section.html.liquid +1 -0
  268. data/lib/metanorma/html/templates/_pubid_identifier.html.liquid +1 -0
  269. data/lib/metanorma/html/templates/_quote.html.liquid +4 -0
  270. data/lib/metanorma/html/templates/_ref_date.html.liquid +1 -0
  271. data/lib/metanorma/html/templates/_ref_title.html.liquid +1 -0
  272. data/lib/metanorma/html/templates/_section.html.liquid +5 -0
  273. data/lib/metanorma/html/templates/_standard_cover.html.liquid +31 -0
  274. data/lib/metanorma/html/templates/_standard_doc_title.html.liquid +3 -0
  275. data/lib/metanorma/html/templates/_stem_span.html.liquid +1 -0
  276. data/lib/metanorma/html/templates/_table.html.liquid +9 -0
  277. data/lib/metanorma/html/templates/_term_domain.html.liquid +1 -0
  278. data/lib/metanorma/html/templates/_term_example.html.liquid +1 -0
  279. data/lib/metanorma/html/templates/_term_note.html.liquid +1 -0
  280. data/lib/metanorma/html/templates/_term_number.html.liquid +1 -0
  281. data/lib/metanorma/html/templates/_term_source.html.liquid +1 -0
  282. data/lib/metanorma/html/templates/_term_text_note.html.liquid +1 -0
  283. data/lib/metanorma/html/templates/_video.html.liquid +1 -0
  284. data/lib/metanorma/html/templates/_wrapped_preface.html.liquid +1 -0
  285. data/lib/metanorma/html/templates/document.html.liquid +4 -0
  286. data/lib/metanorma/html/theme.rb +180 -107
  287. data/lib/metanorma/html.rb +13 -0
  288. data/lib/metanorma/ietf_document/metadata/ietf_bib_data_extension_type.rb +0 -25
  289. data/lib/metanorma/ietf_document/metadata/pi_settings.rb +32 -0
  290. data/lib/metanorma/ietf_document/metadata.rb +2 -0
  291. data/lib/metanorma/iso_document/metadata/doctype_element.rb +20 -0
  292. data/lib/metanorma/iso_document/metadata/iso_bib_data_extension_type.rb +0 -48
  293. data/lib/metanorma/iso_document/metadata/iso_bibliographic_item.rb +0 -72
  294. data/lib/metanorma/iso_document/metadata/iso_document_status.rb +0 -30
  295. data/lib/metanorma/iso_document/metadata/iso_localized_title.rb +4 -1
  296. data/lib/metanorma/iso_document/metadata/language_element.rb +18 -0
  297. data/lib/metanorma/iso_document/metadata/project_number.rb +22 -0
  298. data/lib/metanorma/iso_document/metadata/script_element.rb +18 -0
  299. data/lib/metanorma/iso_document/metadata/stage_element.rb +26 -0
  300. data/lib/metanorma/iso_document/metadata/stagename_element.rb +18 -0
  301. data/lib/metanorma/iso_document/metadata/structured_identifier.rb +0 -17
  302. data/lib/metanorma/iso_document/metadata/title_collection.rb +1 -0
  303. data/lib/metanorma/iso_document/metadata.rb +9 -4
  304. data/lib/metanorma/iso_document/terms/iso_term.rb +0 -13
  305. data/lib/metanorma/iso_document/terms/term_domain_element.rb +18 -0
  306. data/lib/metanorma/iso_document/terms.rb +1 -0
  307. data/lib/metanorma/itu_document/metadata/itu_bib_data_extension_type.rb +0 -37
  308. data/lib/metanorma/itu_document/metadata/meeting_date.rb +18 -0
  309. data/lib/metanorma/itu_document/metadata/meeting_element.rb +18 -0
  310. data/lib/metanorma/itu_document/metadata/study_period.rb +18 -0
  311. data/lib/metanorma/itu_document/metadata.rb +3 -4
  312. data/lib/metanorma/mirror/default_registry.rb +170 -0
  313. data/lib/metanorma/mirror/handler_registry.rb +60 -0
  314. data/lib/metanorma/mirror/handler_result.rb +37 -0
  315. data/lib/metanorma/mirror/handlers/admonition.rb +18 -0
  316. data/lib/metanorma/mirror/handlers/example.rb +21 -0
  317. data/lib/metanorma/mirror/handlers/figure.rb +53 -0
  318. data/lib/metanorma/mirror/handlers/formula.rb +35 -0
  319. data/lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb +159 -0
  320. data/lib/metanorma/mirror/handlers/inline/text_extractor.rb +116 -0
  321. data/lib/metanorma/mirror/handlers/inline.rb +225 -0
  322. data/lib/metanorma/mirror/handlers/list.rb +110 -0
  323. data/lib/metanorma/mirror/handlers/note.rb +26 -0
  324. data/lib/metanorma/mirror/handlers/paragraph.rb +19 -0
  325. data/lib/metanorma/mirror/handlers/quote.rb +16 -0
  326. data/lib/metanorma/mirror/handlers/review.rb +19 -0
  327. data/lib/metanorma/mirror/handlers/section.rb +149 -0
  328. data/lib/metanorma/mirror/handlers/sourcecode.rb +27 -0
  329. data/lib/metanorma/mirror/handlers/structural.rb +68 -0
  330. data/lib/metanorma/mirror/handlers/table.rb +81 -0
  331. data/lib/metanorma/mirror/handlers/term.rb +100 -0
  332. data/lib/metanorma/mirror/handlers.rb +60 -0
  333. data/lib/metanorma/mirror/id_strategy/positional.rb +166 -0
  334. data/lib/metanorma/mirror/id_strategy/preserve.rb +12 -0
  335. data/lib/metanorma/mirror/id_strategy.rb +34 -0
  336. data/lib/metanorma/mirror/math_util.rb +53 -0
  337. data/lib/metanorma/mirror/metadata.rb +37 -0
  338. data/lib/metanorma/mirror/metanorma_to_mirror.rb +101 -0
  339. data/lib/metanorma/mirror/model/container.rb +47 -0
  340. data/lib/metanorma/mirror/model/factory.rb +52 -0
  341. data/lib/metanorma/mirror/model/guide.rb +25 -0
  342. data/lib/metanorma/mirror/model/leaf.rb +17 -0
  343. data/lib/metanorma/mirror/model/mark.rb +53 -0
  344. data/lib/metanorma/mirror/model/node.rb +47 -0
  345. data/lib/metanorma/mirror/model/soft_break.rb +33 -0
  346. data/lib/metanorma/mirror/model/text.rb +34 -0
  347. data/lib/metanorma/mirror/model.rb +16 -0
  348. data/lib/metanorma/mirror/output/builder.rb +39 -0
  349. data/lib/metanorma/mirror/output/formats/base_format.rb +90 -0
  350. data/lib/metanorma/mirror/output/formats/inline_format.rb +81 -0
  351. data/lib/metanorma/mirror/output/formats.rb +53 -0
  352. data/lib/metanorma/mirror/output/html_renderer.rb +95 -0
  353. data/lib/metanorma/mirror/output/html_renderers/block_renderers.rb +178 -0
  354. data/lib/metanorma/mirror/output/html_renderers/inline_renderer.rb +61 -0
  355. data/lib/metanorma/mirror/output/html_renderers/list_renderers.rb +96 -0
  356. data/lib/metanorma/mirror/output/html_renderers/mark_renderers.rb +64 -0
  357. data/lib/metanorma/mirror/output/html_renderers/section_renderers.rb +129 -0
  358. data/lib/metanorma/mirror/output/html_renderers/structural_renderers.rb +80 -0
  359. data/lib/metanorma/mirror/output/html_renderers/table_renderers.rb +87 -0
  360. data/lib/metanorma/mirror/output/html_renderers.rb +76 -0
  361. data/lib/metanorma/mirror/output/pipeline.rb +92 -0
  362. data/lib/metanorma/mirror/output/pipeline_context.rb +22 -0
  363. data/lib/metanorma/mirror/output.rb +14 -0
  364. data/lib/metanorma/mirror/rewriter.rb +121 -0
  365. data/lib/metanorma/mirror/safe_attr.rb +16 -0
  366. data/lib/metanorma/mirror/serialization/json_serializer.rb +25 -0
  367. data/lib/metanorma/mirror/serialization/yaml_serializer.rb +20 -0
  368. data/lib/metanorma/mirror/serialization.rb +10 -0
  369. data/lib/metanorma/mirror/transformer.rb +22 -0
  370. data/lib/metanorma/mirror.rb +32 -0
  371. data/lib/metanorma/standard_document/annotation.rb +37 -0
  372. data/lib/metanorma/standard_document/block_attributes.rb +15 -2
  373. data/lib/metanorma/standard_document/blocks/block_source.rb +0 -23
  374. data/lib/metanorma/standard_document/blocks/source_origin_element.rb +23 -0
  375. data/lib/metanorma/standard_document/blocks.rb +1 -1
  376. data/lib/metanorma/standard_document/terms/term_expression.rb +0 -31
  377. data/lib/metanorma/standard_document/terms/term_name_element.rb +29 -0
  378. data/lib/metanorma/standard_document/terms.rb +1 -0
  379. data/script/render_both.rb +18 -0
  380. metadata +262 -13
  381. data/.github/workflows/rake.yml +0 -21
  382. data/.github/workflows/release.yml +0 -25
  383. data/.github/workflows/roundtrip-samples.yml +0 -132
  384. data/.gitignore +0 -19
  385. data/.rspec +0 -3
  386. data/.rubocop.yml +0 -22
  387. data/Gemfile +0 -28
  388. data/bin/console +0 -15
  389. data/bin/setup +0 -8
  390. data/metanorma-document.gemspec +0 -35
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Factory
7
+ INVALID_INPUT = "Factory.from_h expects a Hash, got %<class>s"
8
+
9
+ def self.from_h(hash)
10
+ unless hash.is_a?(Hash)
11
+ raise ArgumentError,
12
+ format(INVALID_INPUT, class: hash.class)
13
+ end
14
+
15
+ type = hash["type"]
16
+
17
+ case type
18
+ when "text"
19
+ build_text(hash)
20
+ when "soft_break"
21
+ SoftBreak.new
22
+ when nil
23
+ raise ArgumentError,
24
+ "Factory.from_h requires a 'type' key, got #{hash.inspect}"
25
+ else
26
+ build_node(hash, type)
27
+ end
28
+ end
29
+
30
+ class << self
31
+ private
32
+
33
+ def build_text(hash)
34
+ marks = Array(hash["marks"]).map { |m| Mark.from_h(m) }
35
+ Text.new(text: hash["text"] || "", marks: marks)
36
+ end
37
+
38
+ def build_node(hash, type)
39
+ content = hash["content"]
40
+ if content.is_a?(Array)
41
+ children = content.map { |c| c.is_a?(Hash) ? from_h(c) : c }
42
+ Container.new(type: type, attrs: hash["attrs"] || {},
43
+ content: children)
44
+ else
45
+ Leaf.new(type: type, attrs: hash["attrs"] || {})
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Guide
7
+ attr_reader :content, :meta, :title
8
+
9
+ def initialize(content:, meta: {}, title: nil)
10
+ @content = content
11
+ @meta = meta
12
+ @title = title
13
+ end
14
+
15
+ def to_h
16
+ h = {}
17
+ h["content"] = @content.is_a?(Container) ? @content.to_h : @content
18
+ h["meta"] = @meta unless @meta.nil? || @meta.empty?
19
+ h["title"] = @title if @title
20
+ h
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Leaf < Node
7
+ def leaf?
8
+ true
9
+ end
10
+
11
+ def accept_rewriter(rewriter)
12
+ rewriter.rewrite_leaf(self)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Mark
7
+ attr_reader :type, :attrs
8
+
9
+ def initialize(type:, attrs: {})
10
+ @type = type
11
+ @attrs = normalize_attrs(attrs)
12
+ end
13
+
14
+ def to_h
15
+ h = { "type" => type }
16
+ h["attrs"] = @attrs.dup unless @attrs.empty?
17
+ h
18
+ end
19
+
20
+ def [](key)
21
+ @attrs[key.to_s]
22
+ end
23
+
24
+ def []=(key, value)
25
+ @attrs[key.to_s] = value
26
+ end
27
+
28
+ def set_attr(key, value)
29
+ @attrs[key.to_s] = value
30
+ self
31
+ end
32
+
33
+ def fetch(key, default = nil, &)
34
+ @attrs.fetch(key.to_s, default, &)
35
+ end
36
+
37
+ def self.from_h(hash)
38
+ return nil unless hash
39
+
40
+ new(type: hash["type"], attrs: hash["attrs"] || {})
41
+ end
42
+
43
+ private
44
+
45
+ def normalize_attrs(attrs)
46
+ return {} if attrs.nil?
47
+
48
+ attrs.transform_keys(&:to_s)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Node
7
+ attr_reader :type, :attrs
8
+
9
+ def initialize(type:, attrs: {})
10
+ @type = type
11
+ @attrs = normalize_attrs(attrs)
12
+ end
13
+
14
+ def to_h
15
+ h = { "type" => type }
16
+ h["attrs"] = @attrs.dup unless @attrs.empty?
17
+ h
18
+ end
19
+
20
+ def leaf?
21
+ false
22
+ end
23
+
24
+ def container?
25
+ false
26
+ end
27
+
28
+ def text_content
29
+ ""
30
+ end
31
+
32
+ def accept_rewriter(_rewriter)
33
+ raise NotImplementedError,
34
+ "#{self.class}#accept_rewriter not implemented"
35
+ end
36
+
37
+ private
38
+
39
+ def normalize_attrs(attrs)
40
+ return {} if attrs.nil?
41
+
42
+ attrs.transform_keys(&:to_s)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class SoftBreak
7
+ def type
8
+ "soft_break"
9
+ end
10
+
11
+ def to_h
12
+ { "type" => "soft_break" }
13
+ end
14
+
15
+ def attrs
16
+ {}
17
+ end
18
+
19
+ def content
20
+ []
21
+ end
22
+
23
+ def text_content
24
+ ""
25
+ end
26
+
27
+ def accept_rewriter(rewriter)
28
+ rewriter.rewrite_soft_break(self)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ class Text
7
+ attr_reader :text, :marks
8
+
9
+ def initialize(text:, marks: [])
10
+ @text = text.to_s
11
+ @marks = Array(marks)
12
+ end
13
+
14
+ def type
15
+ "text"
16
+ end
17
+
18
+ def to_h
19
+ h = { "type" => "text", "text" => @text }
20
+ h["marks"] = @marks.map(&:to_h) unless @marks.empty?
21
+ h
22
+ end
23
+
24
+ def text_content
25
+ @text
26
+ end
27
+
28
+ def accept_rewriter(rewriter)
29
+ rewriter.rewrite_text(self)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Model
6
+ autoload :Node, "#{__dir__}/model/node"
7
+ autoload :Container, "#{__dir__}/model/container"
8
+ autoload :Leaf, "#{__dir__}/model/leaf"
9
+ autoload :Text, "#{__dir__}/model/text"
10
+ autoload :SoftBreak, "#{__dir__}/model/soft_break"
11
+ autoload :Mark, "#{__dir__}/model/mark"
12
+ autoload :Guide, "#{__dir__}/model/guide"
13
+ autoload :Factory, "#{__dir__}/model/factory"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Metanorma
6
+ module Mirror
7
+ module Output
8
+ class Builder
9
+ attr_reader :xml_path, :output_path, :format, :options
10
+
11
+ def initialize(xml_path:, output_path:, format: :inline, **options)
12
+ @xml_path = xml_path
13
+ @output_path = output_path
14
+ @format = format
15
+ @options = options
16
+ end
17
+
18
+ def build
19
+ pipeline = Pipeline.new(
20
+ xml_path: @xml_path,
21
+ flavor: @options[:flavor],
22
+ title: @options[:title] || File.basename(@xml_path, ".*"),
23
+ id_strategy: @options[:id_strategy],
24
+ )
25
+
26
+ guide = pipeline.process
27
+
28
+ format_class = Formats.lookup(@format)
29
+ raise ArgumentError, "Unknown format: #{@format}" unless format_class
30
+
31
+ formatter = format_class.new(dist_dir: @options[:dist_dir])
32
+ formatter.write(@output_path, guide, title: @options[:title])
33
+
34
+ @output_path
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Metanorma
6
+ module Mirror
7
+ module Output
8
+ module Formats
9
+ class BaseFormat
10
+ attr_reader :dist_dir
11
+
12
+ def initialize(dist_dir: nil)
13
+ @dist_dir = dist_dir || self.class.default_dist_dir
14
+ end
15
+
16
+ def write(output_path, guide, title: "Metanorma")
17
+ raise NotImplementedError, "#{self.class}#write not implemented"
18
+ end
19
+
20
+ class << self
21
+ attr_accessor :configured_dist_dir
22
+
23
+ def default_dist_dir
24
+ @configured_dist_dir || File.expand_path(
25
+ "../../../../../frontend/dist", __dir__
26
+ )
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def safe_json(data)
33
+ JSON.generate(data).gsub("</script", '<\\/script')
34
+ end
35
+
36
+ def html_boilerplate(title:, body_content:, head_extra: "",
37
+ script_data: nil, app_mount_id: nil)
38
+ Nokogiri::HTML5::Builder.new do |doc|
39
+ doc.html(lang: "en") do
40
+ build_head(doc, title:, head_extra:, script_data:)
41
+ build_body(doc, body_content:, app_mount_id:)
42
+ end
43
+ end.doc.to_html
44
+ end
45
+
46
+ def iife_bundle_path
47
+ File.join(@dist_dir, "app.iife.js")
48
+ end
49
+
50
+ def iife_css_path
51
+ File.join(@dist_dir, "app.css")
52
+ end
53
+
54
+ def iife_bundle_exists?
55
+ File.exist?(iife_bundle_path)
56
+ end
57
+
58
+ private
59
+
60
+ def build_head(doc, title:, head_extra:, script_data:)
61
+ doc.head do
62
+ doc.meta(charset: "UTF-8")
63
+ doc.meta(name: "viewport",
64
+ content: "width=device-width, initial-scale=1.0")
65
+ doc.title { doc.text title.to_s }
66
+ if script_data
67
+ doc.script { doc.text script_data }
68
+ end
69
+ next unless head_extra && !head_extra.empty?
70
+
71
+ doc << Nokogiri::HTML5::DocumentFragment.parse(head_extra)
72
+ end
73
+ end
74
+
75
+ def build_body(doc, body_content:, app_mount_id:)
76
+ doc.body do
77
+ if app_mount_id
78
+ doc.div(id: app_mount_id) do
79
+ doc << Nokogiri::HTML5::DocumentFragment.parse(body_content.to_s)
80
+ end
81
+ else
82
+ doc << Nokogiri::HTML5::DocumentFragment.parse(body_content.to_s)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "nokogiri"
5
+
6
+ module Metanorma
7
+ module Mirror
8
+ module Output
9
+ module Formats
10
+ class InlineFormat < BaseFormat
11
+ def write(output_path, guide, title: "Metanorma")
12
+ FileUtils.mkdir_p(File.dirname(output_path))
13
+
14
+ data_script = "window.METANORMA_DATA = #{safe_json(guide)};"
15
+ ssr_body = HtmlRenderer.new(guide).render
16
+ head_parts = []
17
+ css_inline = read_css_inline
18
+ if css_inline && !css_inline.empty?
19
+ head_parts << build_style(css_inline)
20
+ end
21
+ head_parts << build_script_src("app.iife.js") if iife_bundle_exists?
22
+ head_extra = head_parts.join("\n")
23
+
24
+ html = html_boilerplate(
25
+ title: title,
26
+ body_content: ssr_body,
27
+ head_extra: head_extra,
28
+ script_data: data_script,
29
+ app_mount_id: iife_bundle_exists? ? "metanorma-app" : nil,
30
+ )
31
+
32
+ File.write(output_path, html)
33
+
34
+ if iife_bundle_exists?
35
+ copy_if_exists(iife_bundle_path,
36
+ File.join(File.dirname(output_path),
37
+ "app.iife.js"))
38
+ end
39
+
40
+ output_path
41
+ end
42
+
43
+ private
44
+
45
+ def build_style(css)
46
+ Nokogiri::HTML5::Builder.new do |doc|
47
+ doc.style { doc.text css }
48
+ end.doc.root.to_html
49
+ end
50
+
51
+ def build_script_src(src)
52
+ Nokogiri::HTML5::Builder.new do |doc|
53
+ doc.script(src: src)
54
+ end.doc.root.to_html
55
+ end
56
+
57
+ def read_css_inline
58
+ path = iife_css_path
59
+ File.exist?(path) ? File.read(path) : ""
60
+ end
61
+
62
+ def copy_if_exists(src, dst)
63
+ return unless File.exist?(src)
64
+
65
+ unless File.exist?(dst) && FileUtils.identical?(
66
+ src, dst
67
+ )
68
+ FileUtils.cp(src,
69
+ dst)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ Metanorma::Mirror::Output::Formats.register(
79
+ :inline,
80
+ Metanorma::Mirror::Output::Formats::InlineFormat,
81
+ )
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Output
6
+ module Formats
7
+ autoload :BaseFormat, "#{__dir__}/formats/base_format"
8
+ autoload :InlineFormat, "#{__dir__}/formats/inline_format"
9
+
10
+ # All format modules that should be auto-registered. Adding a new
11
+ # format = adding one entry here. No edits to lookup logic.
12
+ REGISTERED = %i[InlineFormat].freeze
13
+
14
+ class << self
15
+ # Returns the format class registered under `name`, or nil.
16
+ # Triggers autoload of all known format modules on first call.
17
+ def lookup(name)
18
+ ensure_loaded
19
+ format_map[name]
20
+ end
21
+
22
+ def registered?(name)
23
+ ensure_loaded
24
+ format_map.key?(name)
25
+ end
26
+
27
+ def register(name, format_class)
28
+ format_map[name] = format_class
29
+ self
30
+ end
31
+
32
+ def unregister(name)
33
+ format_map.delete(name)
34
+ self
35
+ end
36
+
37
+ private
38
+
39
+ def format_map
40
+ @format_map ||= {}
41
+ end
42
+
43
+ def ensure_loaded
44
+ return if @loaded
45
+
46
+ REGISTERED.each { |mod_name| const_get(mod_name) }
47
+ @loaded = true
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Mirror
5
+ module Output
6
+ class HtmlRenderer
7
+ include HtmlRenderers::StructuralRenderers
8
+ include HtmlRenderers::SectionRenderers
9
+ include HtmlRenderers::BlockRenderers
10
+ include HtmlRenderers::ListRenderers
11
+ include HtmlRenderers::TableRenderers
12
+ include HtmlRenderers::InlineRenderer
13
+ include HtmlRenderers::MarkRenderers
14
+
15
+ class << self
16
+ def node_handlers
17
+ @node_handlers ||= {}
18
+ end
19
+
20
+ def mark_handlers
21
+ @mark_handlers ||= {}
22
+ end
23
+
24
+ def register_node_handler(type, unbound_method)
25
+ node_handlers[type] = unbound_method
26
+ end
27
+
28
+ def register_mark_handler(mark_type, handler)
29
+ mark_handlers[mark_type] = handler
30
+ end
31
+ end
32
+
33
+ def initialize(guide, numbering: {})
34
+ @content = extract_content(guide)
35
+ @numbering = numbering
36
+ end
37
+
38
+ def render
39
+ render_nodes(@content)
40
+ end
41
+
42
+ def render_nodes(nodes, depth: 0)
43
+ nodes.filter_map { |node| render_node(node, depth:) }.join("\n")
44
+ end
45
+
46
+ def render_node(node, depth: 0)
47
+ case node
48
+ when String
49
+ HtmlRenderers.escape_text(node)
50
+ when Model::Text
51
+ render_text_node(node)
52
+ when Model::Container, Model::Leaf, Model::SoftBreak
53
+ render_typed_node(node, depth:)
54
+ else
55
+ ""
56
+ end
57
+ end
58
+
59
+ def render_typed_node(node, depth: 0)
60
+ unbound = self.class.node_handlers[node.type]
61
+ return unbound.bind_call(self, node, depth:) if unbound
62
+
63
+ render_generic(node, depth:)
64
+ end
65
+
66
+ def render_generic(node, depth: 0)
67
+ children = node.content if node.is_a?(Model::Container)
68
+ return "" unless children && !children.empty?
69
+
70
+ render_children(node, depth:)
71
+ end
72
+
73
+ def render_children(node, depth: 0)
74
+ children = node.is_a?(Model::Container) ? node.content : []
75
+ render_nodes(children, depth:)
76
+ end
77
+
78
+ private
79
+
80
+ def extract_content(guide)
81
+ case guide
82
+ when Model::Guide
83
+ guide.content.is_a?(Model::Container) ? guide.content.content : Array(guide.content)
84
+ when Model::Container
85
+ guide.content
86
+ else
87
+ raise ArgumentError, "Unsupported guide type: #{guide.class}"
88
+ end
89
+ end
90
+ end
91
+
92
+ HtmlRenderers.register_all(HtmlRenderer)
93
+ end
94
+ end
95
+ end