metanorma-document 0.2.12 → 0.2.13

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 (391) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +21 -0
  3. data/.github/workflows/release.yml +25 -0
  4. data/.github/workflows/roundtrip-samples.yml +132 -0
  5. data/.gitignore +19 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +22 -0
  8. data/.rubocop_todo.yml +66 -164
  9. data/CHANGELOG.md +1 -0
  10. data/Gemfile +32 -0
  11. data/README.adoc +0 -144
  12. data/Rakefile +2 -14
  13. data/bin/console +15 -0
  14. data/bin/setup +8 -0
  15. data/data/javascripts/components/mn-lightbox.js +2 -18
  16. data/data/javascripts/components/mn-shortcuts.js +0 -2
  17. data/data/javascripts/core/mn-navigation.js +0 -8
  18. data/data/javascripts/core/mn-theme.js +9 -9
  19. data/data/logos/pdfa-logo.svg +94 -31
  20. data/data/stylesheets/base/_layout.css +0 -3
  21. data/data/stylesheets/base/_reset.css +3 -3
  22. data/data/stylesheets/base/_typography.css +5 -0
  23. data/data/stylesheets/components/bibliography.css +3 -3
  24. data/data/stylesheets/components/cover.css +1 -3
  25. data/data/stylesheets/components/figure.css +1 -13
  26. data/data/stylesheets/components/footnote.css +5 -65
  27. data/data/stylesheets/components/formula.css +0 -44
  28. data/data/stylesheets/components/header.css +2 -17
  29. data/data/stylesheets/components/inline.css +1 -1
  30. data/data/stylesheets/components/table.css +2 -15
  31. data/data/stylesheets/components/term.css +1 -0
  32. data/lib/metanorma/bipm_document/metadata/bipm_bibliographic_item.rb +14 -0
  33. data/lib/metanorma/bipm_document/metadata.rb +1 -1
  34. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_block.rb +65 -0
  35. data/lib/metanorma/document/components/ancillary_blocks.rb +0 -4
  36. data/lib/metanorma/document/components/bib_data/bib_data.rb +0 -7
  37. data/lib/metanorma/document/components/blocks/requirement_model.rb +131 -0
  38. data/lib/metanorma/document/components/blocks.rb +7 -8
  39. data/lib/metanorma/document/components/id_elements/image.rb +0 -2
  40. data/lib/metanorma/document/components/inline/asciimath_element.rb +0 -2
  41. data/lib/metanorma/document/components/inline/attribution_element.rb +0 -2
  42. data/lib/metanorma/document/components/inline/bcp14_element.rb +0 -2
  43. data/lib/metanorma/document/components/inline/biblio_tag_element.rb +0 -2
  44. data/lib/metanorma/document/components/inline/br_element.rb +0 -2
  45. data/lib/metanorma/document/components/inline/comma_element.rb +0 -2
  46. data/lib/metanorma/document/components/inline/concept_element.rb +0 -2
  47. data/lib/metanorma/document/components/inline/display_text_element.rb +0 -2
  48. data/lib/metanorma/document/components/inline/em_raw_element.rb +0 -2
  49. data/lib/metanorma/document/components/inline/enum_comma_element.rb +0 -2
  50. data/lib/metanorma/document/components/inline/eref_element.rb +0 -2
  51. data/lib/metanorma/document/components/inline/fmt_admitted_element.rb +0 -4
  52. data/lib/metanorma/document/components/inline/fmt_annotation_body_element.rb +0 -4
  53. data/lib/metanorma/document/components/inline/fmt_annotation_end_element.rb +0 -4
  54. data/lib/metanorma/document/components/inline/fmt_annotation_start_element.rb +0 -4
  55. data/lib/metanorma/document/components/inline/fmt_concept_element.rb +0 -4
  56. data/lib/metanorma/document/components/inline/fmt_definition_element.rb +0 -4
  57. data/lib/metanorma/document/components/inline/fmt_fn_body_element.rb +0 -4
  58. data/lib/metanorma/document/components/inline/fmt_fn_label_element.rb +0 -4
  59. data/lib/metanorma/document/components/inline/fmt_footnote_container_element.rb +0 -4
  60. data/lib/metanorma/document/components/inline/fmt_identifier_element.rb +0 -4
  61. data/lib/metanorma/document/components/inline/fmt_name_element.rb +0 -4
  62. data/lib/metanorma/document/components/inline/fmt_preferred_element.rb +0 -4
  63. data/lib/metanorma/document/components/inline/fmt_sourcecode_element.rb +0 -4
  64. data/lib/metanorma/document/components/inline/fmt_stem_element.rb +0 -10
  65. data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +0 -4
  66. data/lib/metanorma/document/components/inline/fmt_title_element.rb +0 -4
  67. data/lib/metanorma/document/components/inline/fmt_xref_element.rb +0 -4
  68. data/lib/metanorma/document/components/inline/fmt_xref_label_element.rb +0 -4
  69. data/lib/metanorma/document/components/inline/fn_element.rb +0 -2
  70. data/lib/metanorma/document/components/inline/link_element.rb +0 -2
  71. data/lib/metanorma/document/components/inline/localized_string_element.rb +0 -2
  72. data/lib/metanorma/document/components/inline/localized_strings_element.rb +0 -2
  73. data/lib/metanorma/document/components/inline/math_element.rb +0 -2
  74. data/lib/metanorma/document/components/inline/name_with_id_element.rb +0 -4
  75. data/lib/metanorma/document/components/inline/semx_child_element.rb +0 -2
  76. data/lib/metanorma/document/components/inline/semx_element.rb +1 -13
  77. data/lib/metanorma/document/components/inline/small_cap_element.rb +0 -2
  78. data/lib/metanorma/document/components/inline/span_element.rb +15 -10
  79. data/lib/metanorma/document/components/inline/stem_inline_element.rb +1 -7
  80. data/lib/metanorma/document/components/inline/strong_raw_element.rb +0 -2
  81. data/lib/metanorma/document/components/inline/sub_element.rb +0 -2
  82. data/lib/metanorma/document/components/inline/sup_element.rb +0 -2
  83. data/lib/metanorma/document/components/inline/tab_element.rb +0 -2
  84. data/lib/metanorma/document/components/inline/title_with_annotation_element.rb +0 -4
  85. data/lib/metanorma/document/components/inline/tt_element.rb +0 -2
  86. data/lib/metanorma/document/components/inline/variant_title_element.rb +0 -2
  87. data/lib/metanorma/document/components/inline/xref_element.rb +0 -10
  88. data/lib/metanorma/document/components/inline.rb +0 -12
  89. data/lib/metanorma/document/components/multi_paragraph/quote_block.rb +29 -0
  90. data/lib/metanorma/document/components/multi_paragraph.rb +0 -2
  91. data/lib/metanorma/document/components/reference_elements/source_element.rb +31 -0
  92. data/lib/metanorma/document/components/reference_elements.rb +2 -2
  93. data/lib/metanorma/document/components/tables/table_block.rb +22 -0
  94. data/lib/metanorma/document/components/tables/table_section.rb +25 -0
  95. data/lib/metanorma/document/components/tables.rb +3 -5
  96. data/lib/metanorma/document/relaton/address.rb +27 -0
  97. data/lib/metanorma/document/relaton/bib_item_locality.rb +12 -3
  98. data/lib/metanorma/document/relaton/copyright_association.rb +10 -0
  99. data/lib/metanorma/document/relaton/edition.rb +7 -4
  100. data/lib/metanorma/document/relaton/organization.rb +14 -0
  101. data/lib/metanorma/document/relaton/phone.rb +9 -3
  102. data/lib/metanorma/document/relaton/place_type.rb +13 -0
  103. data/lib/metanorma/document/relaton/price_type.rb +9 -3
  104. data/lib/metanorma/document/relaton.rb +1 -6
  105. data/lib/metanorma/document/version.rb +1 -1
  106. data/lib/metanorma/document.rb +0 -2
  107. data/lib/metanorma/html/asset_pipeline.rb +0 -1
  108. data/lib/metanorma/html/base_renderer.rb +1008 -261
  109. data/lib/metanorma/html/bipm_renderer.rb +144 -15
  110. data/lib/metanorma/html/cc_renderer.rb +45 -1
  111. data/lib/metanorma/html/component/base.rb +7 -2
  112. data/lib/metanorma/html/component/index_section.rb +43 -41
  113. data/lib/metanorma/html/drops/admonition_drop.rb +4 -1
  114. data/lib/metanorma/html/drops/block_element_drop.rb +2 -7
  115. data/lib/metanorma/html/drops/example_drop.rb +4 -1
  116. data/lib/metanorma/html/drops/figure_drop.rb +22 -31
  117. data/lib/metanorma/html/drops/footnote_drop.rb +1 -1
  118. data/lib/metanorma/html/drops/formula_drop.rb +16 -45
  119. data/lib/metanorma/html/drops/note_drop.rb +7 -9
  120. data/lib/metanorma/html/drops/sourcecode_drop.rb +5 -12
  121. data/lib/metanorma/html/drops.rb +0 -2
  122. data/lib/metanorma/html/generator.rb +2 -2
  123. data/lib/metanorma/html/icc_renderer.rb +39 -0
  124. data/lib/metanorma/html/iec_renderer.rb +44 -1
  125. data/lib/metanorma/html/ieee_renderer.rb +45 -1
  126. data/lib/metanorma/html/ietf_renderer.rb +48 -1
  127. data/lib/metanorma/html/iho_renderer.rb +46 -1
  128. data/lib/metanorma/html/iso_renderer.rb +343 -290
  129. data/lib/metanorma/html/itu_renderer.rb +45 -1
  130. data/lib/metanorma/html/ogc_renderer.rb +76 -15
  131. data/lib/metanorma/html/oiml_renderer.rb +38 -18
  132. data/lib/metanorma/html/pdfa_renderer.rb +39 -0
  133. data/lib/metanorma/html/ribose_renderer.rb +45 -1
  134. data/lib/metanorma/html/standard_renderer.rb +229 -341
  135. data/lib/metanorma/html/templates/_footnotes.html.liquid +6 -6
  136. data/lib/metanorma/html/templates/_formula.html.liquid +1 -1
  137. data/lib/metanorma/html/templates/_header.html.liquid +1 -1
  138. data/lib/metanorma/html/templates/_iso_cover.html.liquid +1 -1
  139. data/lib/metanorma/html/templates/document.html.liquid +0 -4
  140. data/lib/metanorma/html/theme.rb +107 -180
  141. data/lib/metanorma/html.rb +3 -14
  142. data/lib/metanorma/ietf_document/metadata/ietf_bib_data_extension_type.rb +25 -0
  143. data/lib/metanorma/ietf_document/metadata.rb +0 -2
  144. data/lib/metanorma/iso_document/metadata/iso_bib_data_extension_type.rb +48 -0
  145. data/lib/metanorma/iso_document/metadata/iso_bibliographic_item.rb +72 -0
  146. data/lib/metanorma/iso_document/metadata/iso_document_status.rb +30 -0
  147. data/lib/metanorma/iso_document/metadata/iso_localized_title.rb +1 -4
  148. data/lib/metanorma/iso_document/metadata/structured_identifier.rb +17 -0
  149. data/lib/metanorma/iso_document/metadata/title_collection.rb +0 -1
  150. data/lib/metanorma/iso_document/metadata.rb +4 -9
  151. data/lib/metanorma/iso_document/terms/iso_term.rb +13 -0
  152. data/lib/metanorma/iso_document/terms.rb +0 -1
  153. data/lib/metanorma/itu_document/metadata/itu_bib_data_extension_type.rb +37 -0
  154. data/lib/metanorma/itu_document/metadata.rb +4 -3
  155. data/lib/metanorma/standard_document/block_attributes.rb +9 -5
  156. data/lib/metanorma/standard_document/blocks/block_source.rb +23 -0
  157. data/lib/metanorma/standard_document/blocks.rb +1 -1
  158. data/lib/metanorma/standard_document/terms/term_expression.rb +31 -0
  159. data/lib/metanorma/standard_document/terms.rb +0 -1
  160. data/metanorma-document.gemspec +35 -0
  161. metadata +13 -262
  162. data/CLAUDE.md +0 -26
  163. data/TODO.finalize/01-split-multi-class-files.md +0 -8
  164. data/TODO.finalize/02-remove-data-dist-iso-document-frontend.md +0 -6
  165. data/TODO.finalize/03-align-frontend-iife-and-gha-build.md +0 -11
  166. data/TODO.finalize/04-theme-per-flavor-config-driven.md +0 -9
  167. data/TODO.finalize/05-migrate-all-html-to-liquid.md +0 -16
  168. data/TODO.finalize/06-integrate-pubid-library.md +0 -16
  169. data/TODO.finalize/07-replace-relaton-models-with-relaton-bib.md +0 -39
  170. data/TODO.finalize/08-refactor-renderer-architecture.md +0 -25
  171. data/TODO.finalize/09-eliminate-code-smells.md +0 -14
  172. data/TODO.finalize/10-comprehensive-spec-coverage.md +0 -24
  173. data/TODO.finalize/11-config-driven-flavor-metadata.md +0 -20
  174. data/TODO.finalize/12-add-frozen-string-literal.md +0 -6
  175. data/TODO.finalize/13-extract-composition-from-god-class.md +0 -96
  176. data/TODO.finalize/14-config-driven-section-ordering.md +0 -31
  177. data/TODO.finalize/15-theme-directories-per-flavor.md +0 -46
  178. data/TODO.finalize/16-theme-lutaml-model-schema.md +0 -44
  179. data/data/javascripts/components/mn-footnotes.js +0 -68
  180. data/data/logos/oiml-logo-full-dark.svg +0 -1
  181. data/data/logos/oiml-logo-full-light.svg +0 -1
  182. data/data/logos/oiml-logo-icon-dark.svg +0 -1
  183. data/data/logos/oiml-logo-icon-light.svg +0 -1
  184. data/data/logos/oiml-logo.svg +0 -1
  185. data/data/themes/bipm.yaml +0 -60
  186. data/data/themes/cc.yaml +0 -43
  187. data/data/themes/icc.yaml +0 -39
  188. data/data/themes/iec.yaml +0 -44
  189. data/data/themes/ieee.yaml +0 -43
  190. data/data/themes/ietf.yaml +0 -45
  191. data/data/themes/iho.yaml +0 -46
  192. data/data/themes/iso.yaml +0 -40
  193. data/data/themes/itu.yaml +0 -45
  194. data/data/themes/ogc.yaml +0 -51
  195. data/data/themes/oiml.yaml +0 -56
  196. data/data/themes/pdfa.yaml +0 -44
  197. data/data/themes/ribose.yaml +0 -43
  198. data/exe/metanorma-document +0 -11
  199. data/frontend/e2e/sample-html.test.ts +0 -256
  200. data/frontend/index.html +0 -12
  201. data/frontend/package-lock.json +0 -4725
  202. data/frontend/package.json +0 -32
  203. data/frontend/postcss.config.js +0 -6
  204. data/frontend/src/App.vue +0 -79
  205. data/frontend/src/app.ts +0 -21
  206. data/frontend/src/components/AppSidebar.vue +0 -77
  207. data/frontend/src/components/DocumentReader.vue +0 -180
  208. data/frontend/src/components/MirrorRenderer.vue +0 -23
  209. data/frontend/src/components/ReadingProgressBar.vue +0 -29
  210. data/frontend/src/components/TextRenderer.vue +0 -131
  211. data/frontend/src/components/TocItem.vue +0 -57
  212. data/frontend/src/components/TopBar.vue +0 -49
  213. data/frontend/src/components/__tests__/MirrorRenderer.test.ts +0 -178
  214. data/frontend/src/components/blocks/AdmonitionBlock.vue +0 -74
  215. data/frontend/src/components/blocks/ClauseBlock.vue +0 -53
  216. data/frontend/src/components/blocks/ExampleBlock.vue +0 -21
  217. data/frontend/src/components/blocks/FallbackBlock.vue +0 -18
  218. data/frontend/src/components/blocks/FigureBlock.vue +0 -26
  219. data/frontend/src/components/blocks/FootnotesBlock.vue +0 -25
  220. data/frontend/src/components/blocks/ListBlocks.vue +0 -62
  221. data/frontend/src/components/blocks/NoteBlock.vue +0 -21
  222. data/frontend/src/components/blocks/ParagraphBlock.vue +0 -25
  223. data/frontend/src/components/blocks/QuoteBlock.vue +0 -25
  224. data/frontend/src/components/blocks/ReviewBlock.vue +0 -39
  225. data/frontend/src/components/blocks/SourcecodeBlock.vue +0 -52
  226. data/frontend/src/components/blocks/TableBlock.vue +0 -48
  227. data/frontend/src/components/blocks/index.ts +0 -36
  228. data/frontend/src/stores/__tests__/documentStore.fixture.test.ts +0 -272
  229. data/frontend/src/stores/__tests__/documentStore.test.ts +0 -143
  230. data/frontend/src/stores/__tests__/ebookStore.test.ts +0 -145
  231. data/frontend/src/stores/__tests__/uiStore.test.ts +0 -42
  232. data/frontend/src/stores/documentStore.ts +0 -153
  233. data/frontend/src/stores/ebookStore.ts +0 -179
  234. data/frontend/src/stores/uiStore.ts +0 -32
  235. data/frontend/src/styles.css +0 -156
  236. data/frontend/src/types/__tests__/rendering-contract.test.ts +0 -172
  237. data/frontend/src/types/rendering-contract.ts +0 -209
  238. data/frontend/src/utils/__tests__/typeMetadata.test.ts +0 -102
  239. data/frontend/src/utils/typeMetadata.ts +0 -121
  240. data/frontend/tailwind.config.js +0 -18
  241. data/frontend/test/fixtures/iso-document.json +0 -271
  242. data/frontend/tmp/debug_e2e.cjs +0 -46
  243. data/frontend/tsconfig.json +0 -25
  244. data/frontend/tsconfig.node.json +0 -11
  245. data/frontend/vite.config.ts +0 -37
  246. data/frontend/vitest.e2e.config.ts +0 -9
  247. data/lib/metanorma/bipm_document/metadata/depiction_element.rb +0 -18
  248. data/lib/metanorma/document/cli.rb +0 -116
  249. data/lib/metanorma/document/components/ancillary_blocks/callout_annotation.rb +0 -27
  250. data/lib/metanorma/document/components/ancillary_blocks/sourcecode_body.rb +0 -18
  251. data/lib/metanorma/document/components/blocks/classification_value.rb +0 -21
  252. data/lib/metanorma/document/components/blocks/permission_model.rb +0 -28
  253. data/lib/metanorma/document/components/blocks/recommendation_model.rb +0 -28
  254. data/lib/metanorma/document/components/blocks/requirement_base.rb +0 -30
  255. data/lib/metanorma/document/components/blocks/requirement_classification.rb +0 -20
  256. data/lib/metanorma/document/components/blocks/requirement_description.rb +0 -43
  257. data/lib/metanorma/document/components/blocks/requirement_inherit.rb +0 -24
  258. data/lib/metanorma/document/components/inline/location_element.rb +0 -24
  259. data/lib/metanorma/document/components/inline/rendered_display.rb +0 -23
  260. data/lib/metanorma/document/components/inline/rendered_math_element.rb +0 -21
  261. data/lib/metanorma/document/components/inline/semantic_content.rb +0 -51
  262. data/lib/metanorma/document/components/inline/semantic_math_element.rb +0 -20
  263. data/lib/metanorma/document/components/inline/vocabulary.rb +0 -184
  264. data/lib/metanorma/document/components/multi_paragraph/quote_author_element.rb +0 -20
  265. data/lib/metanorma/document/components/reference_elements/source_modification.rb +0 -21
  266. data/lib/metanorma/document/components/reference_elements/source_origin.rb +0 -28
  267. data/lib/metanorma/document/components/tables/col_element.rb +0 -18
  268. data/lib/metanorma/document/components/tables/col_group_element.rb +0 -18
  269. data/lib/metanorma/document/components/tables/table_body_section.rb +0 -16
  270. data/lib/metanorma/document/components/tables/table_foot_section.rb +0 -16
  271. data/lib/metanorma/document/components/tables/table_head_section.rb +0 -16
  272. data/lib/metanorma/document/relaton/copyright_owner.rb +0 -16
  273. data/lib/metanorma/document/relaton/formatted_address.rb +0 -29
  274. data/lib/metanorma/document/relaton/logo_element.rb +0 -18
  275. data/lib/metanorma/document/relaton/region_element.rb +0 -18
  276. data/lib/metanorma/html/drops/biblio_entry_drop.rb +0 -50
  277. data/lib/metanorma/html/renderers/block_renderer.rb +0 -372
  278. data/lib/metanorma/html/renderers/inline_renderer.rb +0 -655
  279. data/lib/metanorma/html/renderers/pubid_renderer.rb +0 -69
  280. data/lib/metanorma/html/renderers/section_renderer.rb +0 -193
  281. data/lib/metanorma/html/renderers.rb +0 -8
  282. data/lib/metanorma/html/templates/_audio.html.liquid +0 -1
  283. data/lib/metanorma/html/templates/_biblio_entry.html.liquid +0 -1
  284. data/lib/metanorma/html/templates/_bipm_cover.html.liquid +0 -27
  285. data/lib/metanorma/html/templates/_boilerplate.html.liquid +0 -1
  286. data/lib/metanorma/html/templates/_bookmark.html.liquid +0 -1
  287. data/lib/metanorma/html/templates/_br.html.liquid +0 -1
  288. data/lib/metanorma/html/templates/_definition_list.html.liquid +0 -6
  289. data/lib/metanorma/html/templates/_element.html.liquid +0 -1
  290. data/lib/metanorma/html/templates/_fn_marker.html.liquid +0 -1
  291. data/lib/metanorma/html/templates/_heading.html.liquid +0 -1
  292. data/lib/metanorma/html/templates/_image.html.liquid +0 -1
  293. data/lib/metanorma/html/templates/_inline_span.html.liquid +0 -1
  294. data/lib/metanorma/html/templates/_link.html.liquid +0 -1
  295. data/lib/metanorma/html/templates/_list.html.liquid +0 -3
  296. data/lib/metanorma/html/templates/_list_item.html.liquid +0 -1
  297. data/lib/metanorma/html/templates/_main_content.html.liquid +0 -1
  298. data/lib/metanorma/html/templates/_math_container.html.liquid +0 -1
  299. data/lib/metanorma/html/templates/_paragraph.html.liquid +0 -1
  300. data/lib/metanorma/html/templates/_prefatory_section.html.liquid +0 -1
  301. data/lib/metanorma/html/templates/_pubid_identifier.html.liquid +0 -1
  302. data/lib/metanorma/html/templates/_quote.html.liquid +0 -4
  303. data/lib/metanorma/html/templates/_ref_date.html.liquid +0 -1
  304. data/lib/metanorma/html/templates/_ref_title.html.liquid +0 -1
  305. data/lib/metanorma/html/templates/_section.html.liquid +0 -5
  306. data/lib/metanorma/html/templates/_standard_cover.html.liquid +0 -31
  307. data/lib/metanorma/html/templates/_standard_doc_title.html.liquid +0 -3
  308. data/lib/metanorma/html/templates/_stem_span.html.liquid +0 -1
  309. data/lib/metanorma/html/templates/_table.html.liquid +0 -9
  310. data/lib/metanorma/html/templates/_term_domain.html.liquid +0 -1
  311. data/lib/metanorma/html/templates/_term_example.html.liquid +0 -1
  312. data/lib/metanorma/html/templates/_term_note.html.liquid +0 -1
  313. data/lib/metanorma/html/templates/_term_number.html.liquid +0 -1
  314. data/lib/metanorma/html/templates/_term_source.html.liquid +0 -1
  315. data/lib/metanorma/html/templates/_term_text_note.html.liquid +0 -1
  316. data/lib/metanorma/html/templates/_video.html.liquid +0 -1
  317. data/lib/metanorma/html/templates/_wrapped_preface.html.liquid +0 -1
  318. data/lib/metanorma/ietf_document/metadata/pi_settings.rb +0 -32
  319. data/lib/metanorma/iso_document/metadata/doctype_element.rb +0 -20
  320. data/lib/metanorma/iso_document/metadata/language_element.rb +0 -18
  321. data/lib/metanorma/iso_document/metadata/project_number.rb +0 -22
  322. data/lib/metanorma/iso_document/metadata/script_element.rb +0 -18
  323. data/lib/metanorma/iso_document/metadata/stage_element.rb +0 -26
  324. data/lib/metanorma/iso_document/metadata/stagename_element.rb +0 -18
  325. data/lib/metanorma/iso_document/terms/term_domain_element.rb +0 -18
  326. data/lib/metanorma/itu_document/metadata/meeting_date.rb +0 -18
  327. data/lib/metanorma/itu_document/metadata/meeting_element.rb +0 -18
  328. data/lib/metanorma/itu_document/metadata/study_period.rb +0 -18
  329. data/lib/metanorma/mirror/default_registry.rb +0 -170
  330. data/lib/metanorma/mirror/handler_registry.rb +0 -60
  331. data/lib/metanorma/mirror/handler_result.rb +0 -37
  332. data/lib/metanorma/mirror/handlers/admonition.rb +0 -18
  333. data/lib/metanorma/mirror/handlers/example.rb +0 -21
  334. data/lib/metanorma/mirror/handlers/figure.rb +0 -53
  335. data/lib/metanorma/mirror/handlers/formula.rb +0 -35
  336. data/lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb +0 -159
  337. data/lib/metanorma/mirror/handlers/inline/text_extractor.rb +0 -116
  338. data/lib/metanorma/mirror/handlers/inline.rb +0 -225
  339. data/lib/metanorma/mirror/handlers/list.rb +0 -110
  340. data/lib/metanorma/mirror/handlers/note.rb +0 -26
  341. data/lib/metanorma/mirror/handlers/paragraph.rb +0 -19
  342. data/lib/metanorma/mirror/handlers/quote.rb +0 -16
  343. data/lib/metanorma/mirror/handlers/review.rb +0 -19
  344. data/lib/metanorma/mirror/handlers/section.rb +0 -149
  345. data/lib/metanorma/mirror/handlers/sourcecode.rb +0 -27
  346. data/lib/metanorma/mirror/handlers/structural.rb +0 -68
  347. data/lib/metanorma/mirror/handlers/table.rb +0 -81
  348. data/lib/metanorma/mirror/handlers/term.rb +0 -100
  349. data/lib/metanorma/mirror/handlers.rb +0 -60
  350. data/lib/metanorma/mirror/id_strategy/positional.rb +0 -166
  351. data/lib/metanorma/mirror/id_strategy/preserve.rb +0 -12
  352. data/lib/metanorma/mirror/id_strategy.rb +0 -34
  353. data/lib/metanorma/mirror/math_util.rb +0 -53
  354. data/lib/metanorma/mirror/metadata.rb +0 -37
  355. data/lib/metanorma/mirror/metanorma_to_mirror.rb +0 -101
  356. data/lib/metanorma/mirror/model/container.rb +0 -47
  357. data/lib/metanorma/mirror/model/factory.rb +0 -52
  358. data/lib/metanorma/mirror/model/guide.rb +0 -25
  359. data/lib/metanorma/mirror/model/leaf.rb +0 -17
  360. data/lib/metanorma/mirror/model/mark.rb +0 -53
  361. data/lib/metanorma/mirror/model/node.rb +0 -47
  362. data/lib/metanorma/mirror/model/soft_break.rb +0 -33
  363. data/lib/metanorma/mirror/model/text.rb +0 -34
  364. data/lib/metanorma/mirror/model.rb +0 -16
  365. data/lib/metanorma/mirror/output/builder.rb +0 -39
  366. data/lib/metanorma/mirror/output/formats/base_format.rb +0 -90
  367. data/lib/metanorma/mirror/output/formats/inline_format.rb +0 -81
  368. data/lib/metanorma/mirror/output/formats.rb +0 -53
  369. data/lib/metanorma/mirror/output/html_renderer.rb +0 -95
  370. data/lib/metanorma/mirror/output/html_renderers/block_renderers.rb +0 -178
  371. data/lib/metanorma/mirror/output/html_renderers/inline_renderer.rb +0 -61
  372. data/lib/metanorma/mirror/output/html_renderers/list_renderers.rb +0 -96
  373. data/lib/metanorma/mirror/output/html_renderers/mark_renderers.rb +0 -64
  374. data/lib/metanorma/mirror/output/html_renderers/section_renderers.rb +0 -129
  375. data/lib/metanorma/mirror/output/html_renderers/structural_renderers.rb +0 -80
  376. data/lib/metanorma/mirror/output/html_renderers/table_renderers.rb +0 -87
  377. data/lib/metanorma/mirror/output/html_renderers.rb +0 -76
  378. data/lib/metanorma/mirror/output/pipeline.rb +0 -92
  379. data/lib/metanorma/mirror/output/pipeline_context.rb +0 -22
  380. data/lib/metanorma/mirror/output.rb +0 -14
  381. data/lib/metanorma/mirror/rewriter.rb +0 -121
  382. data/lib/metanorma/mirror/safe_attr.rb +0 -16
  383. data/lib/metanorma/mirror/serialization/json_serializer.rb +0 -25
  384. data/lib/metanorma/mirror/serialization/yaml_serializer.rb +0 -20
  385. data/lib/metanorma/mirror/serialization.rb +0 -10
  386. data/lib/metanorma/mirror/transformer.rb +0 -22
  387. data/lib/metanorma/mirror.rb +0 -32
  388. data/lib/metanorma/standard_document/annotation.rb +0 -37
  389. data/lib/metanorma/standard_document/blocks/source_origin_element.rb +0 -23
  390. data/lib/metanorma/standard_document/terms/term_name_element.rb +0 -29
  391. data/script/render_both.rb +0 -18
@@ -10,6 +10,47 @@ module Metanorma
10
10
  class IsoRenderer < StandardRenderer
11
11
  # --- Public hooks for flavor customization ---
12
12
 
13
+ def flavor_publishers(_doc_id)
14
+ ["ISO"]
15
+ end
16
+
17
+ def flavor_publisher_name
18
+ "ISO"
19
+ end
20
+
21
+ def publisher_logo_map
22
+ { "ISO" => "iso-logo.svg" }
23
+ end
24
+
25
+ # ISO brand: #e3000f red from logo
26
+ def theme
27
+ @theme ||= Theme.new.tap do |t|
28
+ t.primary = "#b3000c"
29
+ t.accent = "#e3000f"
30
+ t.accent_deep = "#b3000c"
31
+ t.gradient = "linear-gradient(135deg, #8a0009 0%, #b3000c 50%, #e3000f 100%)"
32
+ t.primary_light = "#fef0f1"
33
+ t.accent_light = "#fde8ea"
34
+ t.warm = "#1a1a1a"
35
+ t.warm_light = "#f5f5f5"
36
+ t.header_background = "linear-gradient(135deg, #8a0009 0%, #b3000c 40%, #e3000f 100%)"
37
+ t.cover_background = "linear-gradient(175deg, #5a0006 0%, #8a0009 25%, #b3000c 50%, #e3000f 80%, #ff4d4d 100%)"
38
+ t.cover_before_bg = "background: radial-gradient(ellipse at 25% 20%, rgba(255,77,77,0.15) 0%, transparent 50%), radial-gradient(ellipse at 75% 80%, rgba(26,26,26,0.1) 0%, transparent 40%)"
39
+ t.cover_after_bg = "height: 3px; background: linear-gradient(90deg, transparent, #e3000f, #1a1a1a, transparent)"
40
+ t.progress_bar_color = "#e3000f"
41
+ t.note_border = "#e3000f"
42
+ t.note_bg = "#fde8ea"
43
+ t.note_color = "#e3000f"
44
+ t.example_border = "#b3000c"
45
+ t.example_bg = "#fef0f1"
46
+ t.example_color = "#b3000c"
47
+ t.admonition_border = "#1a1a1a"
48
+ t.admonition_color = "#1a1a1a"
49
+ t.footer_border_color = "#e3000f"
50
+ t.cover_separator_color = "rgba(227,0,15,0.25)"
51
+ end
52
+ end
53
+
13
54
  register_render Metanorma::IsoDocument::Root, :render_document
14
55
  register_render Metanorma::IsoDocument::Sections::IsoPreface,
15
56
  :render_preface
@@ -36,12 +77,9 @@ module Metanorma
36
77
 
37
78
  def render_term_origin(element)
38
79
  text = extract_text_value(element)
39
- return nil unless text
80
+ return unless text
40
81
 
41
- render_liquid("_inline_span.html.liquid", {
42
- "attrs" => " class=\"term-source\"",
43
- "content" => escape_html(text),
44
- })
82
+ @output << "<span class=\"term-source\">#{escape_html(text)}</span>"
45
83
  end
46
84
 
47
85
  def extract_display_title(bibdata)
@@ -69,7 +107,6 @@ module Metanorma
69
107
  raw_id = extract_text_value(identifiers.first).to_s.strip
70
108
  return nil if raw_id.empty?
71
109
 
72
- raw_id = strip_doc_id_prefix(raw_id)
73
110
  pub = flavor_publisher_name
74
111
  if pub && !raw_id.start_with?(pub)
75
112
  "#{pub} #{raw_id}"
@@ -78,25 +115,21 @@ module Metanorma
78
115
  end
79
116
  end
80
117
 
81
- def strip_doc_id_prefix(raw_id)
82
- prefix = theme.doc_id_strip_prefix
83
- return raw_id unless prefix
84
-
85
- raw_id.to_s.gsub(/\A#{Regexp.escape(prefix)}\s+/, "").strip
86
- end
87
-
118
+ # Extract English stage text, deduplicating duplicate language variants.
88
119
  def extract_stage(bibdata)
89
120
  return nil unless bibdata.status&.stage
90
121
 
91
122
  stages = Array(bibdata.status.stage)
92
123
  return nil if stages.empty?
93
124
 
125
+ # Prefer English-language stage
94
126
  en_stage = stages.find do |s|
95
127
  lang = safe_attr(s, :language)
96
128
  lang == "en" if lang
97
129
  end
98
130
  return Array(en_stage.value).join.strip if en_stage&.value
99
131
 
132
+ # Fallback: first non-empty, deduplicated
100
133
  seen = Set.new
101
134
  stage_text = stages.filter_map do |s|
102
135
  val = Array(s.value).join.strip
@@ -109,6 +142,7 @@ module Metanorma
109
142
  stage_text.empty? ? nil : stage_text
110
143
  end
111
144
 
145
+ # Extract document type from ext.doctype.
112
146
  def extract_doctype(bibdata)
113
147
  return nil unless bibdata.is_a?(Metanorma::IsoDocument::Metadata::IsoBibliographicItem)
114
148
 
@@ -118,12 +152,14 @@ module Metanorma
118
152
  doctypes = ext.doctype
119
153
  return nil unless doctypes && !doctypes.empty?
120
154
 
155
+ # Prefer English-language doctype
121
156
  en_dt = doctypes.find do |d|
122
157
  lang = safe_attr(d, :language)
123
158
  lang == "en" if lang
124
159
  end
125
160
  return en_dt.value.to_s if en_dt&.value
126
161
 
162
+ # Fallback: first doctype
127
163
  dt = doctypes.first
128
164
  val = dt&.value.to_s
129
165
  val.strip.empty? ? nil : val
@@ -132,33 +168,38 @@ module Metanorma
132
168
  # --- Top-level document rendering ---
133
169
 
134
170
  def render_document(doc, **_opts)
135
- parts = []
136
- parts << (render_coverpage(doc) || "")
137
- parts << (render_boilerplate_section(doc) || "")
171
+ render_coverpage(doc)
172
+ render_boilerplate_section(doc)
173
+
174
+ @output << "<main class=\"main-section\">"
138
175
 
139
- parts << (render(doc.preface) || "") if doc.preface
140
- parts << (render_doc_title(doc) || "")
176
+ # Preface sections
177
+ render(doc.preface) if doc.preface
141
178
 
179
+ # Document title line
180
+ render_doc_title(doc)
181
+
182
+ # Collect ALL top-level content (sections + normative refs + annexes + bibliography)
183
+ # and render in displayorder for correct document order.
142
184
  all_items = collect_document_children(doc)
185
+
143
186
  all_items.each do |node|
144
187
  next if node.is_a?(String)
145
188
  next if is_title_element?(node, doc.sections)
146
189
 
147
- parts << (render(node) || "")
190
+ render(node)
148
191
  end
149
192
 
150
- parts << (render_footnotes_section || "")
193
+ render_footnotes_section
151
194
 
152
- render_liquid("_main_content.html.liquid", {
153
- "content" => parts.join,
154
- })
195
+ @output << "</main>"
155
196
  end
156
197
 
157
198
  # --- Cover page ---
158
199
 
159
200
  def render_coverpage(doc)
160
201
  bibdata = doc.bibdata
161
- return "" unless bibdata
202
+ return unless bibdata
162
203
 
163
204
  logos = publisher_logos_html(doc) || []
164
205
  doc_id = formatted_doc_id(bibdata)
@@ -182,150 +223,141 @@ module Metanorma
182
223
  title_text = nil
183
224
  if bibdata.titles
184
225
  en_title = bibdata.title_for("en")
185
- if en_title
186
- title_text = if en_title.is_a?(Metanorma::IsoDocument::Metadata::AbstractTitle) && en_title.value
187
- en_title.value.to_s
188
- else
189
- en_title.to_s
190
- end
191
- end
226
+ title_text = en_title.to_s if en_title
192
227
  end
193
228
 
194
229
  stage_text = extract_stage(bibdata)
195
230
 
196
- render_liquid("_standard_cover.html.liquid", {
197
- "publisher_logos" => logos,
198
- "doc_id" => doc_id,
199
- "pub_date" => pub_date,
200
- "doctype" => doctype,
201
- "title" => title_text,
202
- "stage" => stage_text,
203
- })
231
+ @output << render_liquid("_iso_cover.html.liquid", {
232
+ "publisher_logos" => logos,
233
+ "doc_id" => doc_id,
234
+ "pub_date" => pub_date,
235
+ "doctype" => doctype,
236
+ "title" => title_text,
237
+ "stage" => stage_text,
238
+ })
204
239
  end
205
240
 
206
241
  def render_doc_title(doc)
207
242
  bibdata = doc.bibdata
208
- return nil unless bibdata
243
+ return unless bibdata
209
244
 
210
245
  titles = bibdata.titles
211
- return nil unless titles
246
+ return unless titles
212
247
 
213
248
  en_title = bibdata.title_for("en")
214
- return nil unless en_title
249
+ return unless en_title
215
250
 
216
- render_liquid("_standard_doc_title.html.liquid", {
217
- "title" => en_title.to_s,
218
- })
251
+ @output << render_liquid("_iso_doc_title.html.liquid", {
252
+ "title" => en_title.to_s,
253
+ })
219
254
  end
220
255
 
221
256
  def render_boilerplate_section(doc)
222
- return nil unless doc.boilerplate
257
+ return unless doc.boilerplate
223
258
 
224
- content = render(doc.boilerplate)
225
- render_liquid("_prefatory_section.html.liquid", {
226
- "content" => content,
227
- })
259
+ @output << "<div class=\"prefatory-section\">"
260
+ render(doc.boilerplate)
261
+ @output << "</div><hr class=\"cover-separator\" />"
262
+ end
263
+
264
+ def render_preface(preface, **_opts)
265
+ render(preface.foreword) if preface.foreword
266
+ render(preface.introduction) if preface.introduction
267
+ render(preface.abstract) if preface.abstract
268
+ preface.clause&.each { |cl| render(cl, level: 1) }
269
+ render(preface.acknowledgements) if preface.acknowledgements
270
+ render(preface.executivesummary) if preface.executivesummary
228
271
  end
229
272
 
230
273
  def render_foreword(fw, level: 1, **_opts)
231
274
  attrs = element_attrs(id: safe_attr(fw, :id))
232
- title = safe_attr(fw, :fmt_title) || safe_attr(fw, :title)
233
- parts = []
234
- if title
235
- fw_id = safe_attr(fw, :id)
236
- title_content = render_mixed_inline(title)
237
- register_toc_entry(id: fw_id, level: level,
238
- text: extract_plain_text(title))
239
- parts << render_liquid("_heading.html.liquid", {
240
- "tag" => "h1",
241
- "class_attr" => " class=\"foreword-title\"",
242
- "content" => title_content,
243
- })
275
+ tag("div", attrs) do
276
+ title = safe_attr(fw, :fmt_title) || safe_attr(fw, :title)
277
+ if title
278
+ fw_id = safe_attr(fw, :id)
279
+ title_text = extract_plain_text(title)
280
+ register_toc_entry(id: fw_id, level: level, text: title_text)
281
+ @output << "<h1 class=\"foreword-title\">"
282
+ render_mixed_inline(title)
283
+ @output << "</h1>"
284
+ end
285
+ render_ordered_content(fw)
244
286
  end
245
- parts << (render_ordered_content(fw) || "")
246
- render_liquid("_element.html.liquid", "tag" => "div",
247
- "extra_attrs" => attrs, "content" => parts.join)
248
287
  end
249
288
 
250
289
  def render_abstract(section, level: 1, **_opts)
251
290
  attrs = element_attrs(id: safe_attr(section, :id))
252
- title = safe_attr(section, :fmt_title) || safe_attr(section, :title)
253
- parts = []
254
- if title
255
- sec_id = safe_attr(section, :id)
256
- title_content = render_mixed_inline(title)
257
- register_toc_entry(id: sec_id, level: level,
258
- text: extract_plain_text(title))
259
- parts << render_liquid("_heading.html.liquid", {
260
- "tag" => "h1",
261
- "class_attr" => " class=\"intro-title\"",
262
- "content" => title_content,
263
- })
291
+ tag("div", attrs) do
292
+ title = safe_attr(section, :fmt_title) || safe_attr(section, :title)
293
+ if title
294
+ sec_id = safe_attr(section, :id)
295
+ title_text = extract_plain_text(title)
296
+ register_toc_entry(id: sec_id, level: level, text: title_text)
297
+ @output << "<h1 class=\"intro-title\">"
298
+ render_mixed_inline(title)
299
+ @output << "</h1>"
300
+ end
301
+ render_ordered_content(section)
264
302
  end
265
- parts << (render_ordered_content(section) || "")
266
- render_liquid("_element.html.liquid", "tag" => "div",
267
- "extra_attrs" => attrs, "content" => parts.join)
268
303
  end
269
304
 
270
305
  # --- Main sections rendering ---
271
306
 
272
307
  def render_sections(sections, **_opts)
308
+ # Collect ALL section-level children (from mixed content AND typed attributes)
309
+ # then sort by displayorder for correct document order.
273
310
  children = collect_ordered_children(sections)
274
- parts = []
275
311
  children.each do |node|
276
312
  next if node.is_a?(String)
277
313
  next if is_title_element?(node, sections)
278
314
 
279
- parts << (render(node, level: 1) || "")
315
+ render(node, level: 1)
280
316
  end
281
- parts.join
282
317
  end
283
318
 
284
319
  # --- Clause rendering ---
285
320
 
286
321
  def render_clause(clause, level: 1, **_opts)
287
322
  attrs = element_attrs(id: safe_attr(clause, :id))
288
- title = render_title(clause, level)
289
- content = render_ordered_content(clause, level)
290
- render_liquid("_element.html.liquid", "tag" => "div",
291
- "extra_attrs" => attrs, "content" => "#{title}#{content}")
323
+ tag("div", attrs) do
324
+ render_title(clause, level)
325
+ render_ordered_content(clause, level)
326
+ end
292
327
  end
293
328
 
294
329
  # --- Annex rendering ---
295
330
 
296
331
  def render_annex(annex, level: 1, **_opts)
297
332
  attrs = element_attrs(id: safe_attr(annex, :id), class: "section-sub")
298
- title = render_annex_title(annex, level)
299
- content = render_ordered_content(annex, level)
300
- render_liquid("_element.html.liquid", "tag" => "div",
301
- "extra_attrs" => attrs, "content" => "#{title}#{content}")
333
+ tag("div", attrs) do
334
+ render_annex_title(annex, level)
335
+ render_ordered_content(annex, level)
336
+ end
302
337
  end
303
338
 
304
339
  def render_annex_title(annex, level)
305
340
  title_element = safe_attr(annex, :fmt_title) || safe_attr(annex, :title)
306
- return nil unless title_element
341
+ return unless title_element
307
342
 
308
343
  annex_id = safe_attr(annex, :id)
309
- title_content = render_mixed_inline(title_element)
310
- register_toc_entry(id: annex_id, level: level,
311
- text: extract_plain_text(title_element))
344
+ title_text = extract_plain_text(title_element)
345
+ register_toc_entry(id: annex_id, level: level, text: title_text)
312
346
 
313
347
  h = "h#{[[level, 6].min, 1].max}"
314
- render_liquid("_heading.html.liquid", {
315
- "tag" => h,
316
- "class_attr" => " class=\"annex-title\"",
317
- "content" => title_content,
318
- })
348
+ @output << "<#{h} class=\"annex-title\">"
349
+ render_mixed_inline(title_element)
350
+ @output << "</#{h}>"
319
351
  end
320
352
 
321
353
  # --- Terms section rendering ---
322
354
 
323
355
  def render_terms_section(terms, level: 1, **_opts)
324
356
  attrs = element_attrs(id: safe_attr(terms, :id))
325
- title = render_title(terms, level)
326
- content = render_ordered_content(terms, level)
327
- render_liquid("_element.html.liquid", "tag" => "div",
328
- "extra_attrs" => attrs, "content" => "#{title}#{content}")
357
+ tag("div", attrs) do
358
+ render_title(terms, level)
359
+ render_ordered_content(terms, level)
360
+ end
329
361
  end
330
362
 
331
363
  # --- ISO Term rendering ---
@@ -335,120 +367,112 @@ module Metanorma
335
367
  term_def = extract_term_definition(term)
336
368
  data_attrs = {}
337
369
  if term_name && !term_name.empty?
338
- data_attrs["data-term-name"] = term_name
370
+ data_attrs["data-term-name"] =
371
+ term_name
339
372
  end
340
373
  if term_def && !term_def.empty?
341
- data_attrs["data-term-definition"] = term_def
374
+ data_attrs["data-term-definition"] =
375
+ term_def
342
376
  end
343
377
  attrs = element_attrs(id: safe_attr(term, :id), **data_attrs)
344
- fmt_name = safe_attr(term, :fmt_name)
345
- fmt_preferred = safe_attr(term, :fmt_preferred)
346
- fmt_admitted = safe_attr(term, :fmt_admitted)
347
- fmt_deprecates = safe_attr(term, :fmt_deprecates)
348
- fmt_definition = safe_attr(term, :fmt_definition)
349
- fmt_termsource = safe_attr(term, :fmt_termsource)
350
- term_number = safe_attr(term, :term_number)
351
- term_p = safe_attr(term, :p)
352
- term_note = safe_attr(term, :termnote) || Array(safe_attr(term, :note))
353
- term_example = safe_attr(term,
354
- :termexample) || Array(safe_attr(term,
355
- :example))
356
- term_termsource = safe_attr(term, :termsource)
357
- term_admonition = safe_attr(term, :admonition)
358
- term_nested = safe_attr(term, :term)
359
-
360
- parts = []
361
- if fmt_name
362
- tn_content = render_inline_element(fmt_name)
363
- parts << render_liquid("_term_number.html.liquid",
364
- { "content" => tn_content })
365
- elsif term_number
366
- tn_text = if term_number.is_a?(String)
367
- term_number
368
- else
369
- extract_text_value(term_number)
370
- end
371
- parts << render_liquid("_term_number.html.liquid",
372
- { "content" => escape_html(tn_text) })
373
- end
374
-
375
- if fmt_preferred && !fmt_preferred.empty?
376
- fmt_preferred.each do |fp|
377
- fp.p&.each { |para| parts << (render_paragraph(para) || "") }
378
+ tag("div", attrs) do
379
+ # In presentation mode, use fmt_* elements
380
+ if term.fmt_name
381
+ # Render term number (e.g. "3.1")
382
+ @output << "<p class=\"term-number\">"
383
+ render_inline_element(term.fmt_name)
384
+ @output << "</p>"
385
+ elsif term.term_number
386
+ tn = term.term_number
387
+ tn_text = if tn.is_a?(String)
388
+ tn
389
+ else
390
+ extract_text_value(tn)
391
+ end
392
+ @output << "<p class=\"term-number\">#{escape_html(tn_text)}</p>"
378
393
  end
379
- elsif term.preferred && !term.preferred.empty?
380
- term.preferred&.each do |designation|
381
- parts << (render_term_designation(designation, "preferred") || "")
382
- end
383
- end
384
394
 
385
- if fmt_admitted && !fmt_admitted.empty?
386
- fmt_admitted.each do |fa|
387
- fa.p&.each { |para| parts << (render_paragraph(para) || "") }
388
- end
389
- elsif term.admitted && !term.admitted.empty?
390
- term.admitted&.each do |designation|
391
- parts << (render_term_designation(designation, "admitted") || "")
395
+ # Preferred designations — use fmt-preferred if available
396
+ if term.fmt_preferred && !term.fmt_preferred.empty?
397
+ term.fmt_preferred.each do |fp|
398
+ fp.p&.each do |para|
399
+ render_paragraph(para)
400
+ end
401
+ end
402
+ elsif term.preferred && !term.preferred.empty?
403
+ term.preferred&.each do |designation|
404
+ render_term_designation(designation, "preferred")
405
+ end
392
406
  end
393
- end
394
407
 
395
- if fmt_deprecates
396
- fmt_deprecates.p&.each do |para|
397
- parts << (render_paragraph(para) || "")
408
+ # Admitted designations — use fmt-admitted if available
409
+ if term.fmt_admitted && !term.fmt_admitted.empty?
410
+ term.fmt_admitted.each do |fa|
411
+ fa.p&.each do |para|
412
+ render_paragraph(para)
413
+ end
414
+ end
415
+ elsif term.admitted && !term.admitted.empty?
416
+ term.admitted&.each do |designation|
417
+ render_term_designation(designation, "admitted")
418
+ end
398
419
  end
399
- elsif term.deprecates && !term.deprecates.empty?
400
- term.deprecates&.each do |designation|
401
- parts << (render_term_designation(designation, "deprecated") || "")
420
+
421
+ # Deprecated designations — use fmt-deprecates if available
422
+ if term.fmt_deprecates
423
+ term.fmt_deprecates.p&.each { |para| render_paragraph(para) }
424
+ elsif term.deprecates && !term.deprecates.empty?
425
+ term.deprecates&.each do |designation|
426
+ render_term_designation(designation, "deprecated")
427
+ end
402
428
  end
403
- end
404
429
 
405
- if term.domain && !fmt_definition
406
- domain_text = safe_attr(term.domain, :text)
407
- if domain_text
408
- parts << render_liquid("_term_domain.html.liquid", {
409
- "text" => escape_html(domain_text),
410
- })
430
+ # Domain only render separately when fmt-definition is absent
431
+ # (fmt-definition already includes domain text in its content)
432
+ if term.domain && !term.fmt_definition
433
+ domain_text = safe_attr(term.domain, :text)
434
+ @output << "<p class=\"term-domain\">&lt;#{escape_html(domain_text)}&gt;</p>" if domain_text
411
435
  end
412
- end
413
436
 
414
- if fmt_definition
415
- parts << (render_ordered_content(fmt_definition) || "")
416
- else
417
- term_p&.each { |para| parts << (render_paragraph(para) || "") }
418
- term.definition&.each do |defn|
419
- parts << (render_term_definition(defn) || "")
437
+ # Definition — use fmt-definition if available
438
+ if term.fmt_definition
439
+ render_ordered_content(term.fmt_definition)
440
+ else
441
+ # Definition paragraphs
442
+ term.p&.each { |para| render_paragraph(para) }
443
+ # Structured definitions
444
+ term.definition&.each { |defn| render_term_definition(defn) }
420
445
  end
421
- end
422
446
 
423
- term_note&.each { |note| parts << (render_term_note(note) || "") }
424
- term_example&.each { |ex| parts << (render_term_example(ex) || "") }
447
+ # Term notes
448
+ term.termnote&.each { |note| render_term_note(note) }
425
449
 
426
- if fmt_termsource && !fmt_termsource.empty?
427
- fmt_termsource.each do |fts|
428
- src_content = render_mixed_inline(fts)
429
- parts << render_liquid("_paragraph.html.liquid", {
430
- "attrs" => " class=\"term-source\"",
431
- "content" => src_content,
432
- })
433
- end
434
- else
435
- term.source&.each { |src| parts << (render_term_source(src) || "") }
436
- term_termsource&.each do |src|
437
- parts << (render_term_source_element(src) || "")
450
+ # Term examples
451
+ term.termexample&.each { |ex| render_term_example(ex) }
452
+
453
+ # Source references — use fmt-termsource if available
454
+ if term.fmt_termsource && !term.fmt_termsource.empty?
455
+ term.fmt_termsource.each do |fts|
456
+ @output << "<p class=\"term-source\">"
457
+ render_mixed_inline(fts)
458
+ @output << "</p>"
459
+ end
460
+ else
461
+ term.source&.each { |src| render_term_source(src) }
462
+ term.termsource&.each { |src| render_term_source_element(src) }
438
463
  end
439
- end
440
464
 
441
- term_admonition&.each { |adm| parts << (render_admonition(adm) || "") }
442
- term_nested&.each { |sub| parts << (render_term(sub) || "") }
465
+ # Admonitions
466
+ term.admonition&.each { |adm| render_admonition(adm) }
443
467
 
444
- render_liquid("_element.html.liquid", "tag" => "div",
445
- "extra_attrs" => attrs, "content" => parts.join)
468
+ # Nested terms
469
+ term.term&.each { |sub| render_term(sub) }
470
+ end
446
471
  end
447
472
 
448
473
  def extract_term_name(term)
449
- fmt_pref = safe_attr(term, :fmt_preferred)
450
- if fmt_pref && !fmt_pref.empty?
451
- fp = fmt_pref.first
474
+ if term.fmt_preferred && !term.fmt_preferred.empty?
475
+ fp = term.fmt_preferred.first
452
476
  if fp.p && !fp.p.empty?
453
477
  return extract_plain_text(fp.p.first)
454
478
  end
@@ -461,47 +485,49 @@ module Metanorma
461
485
  end
462
486
 
463
487
  def extract_term_definition(term)
464
- fmt_def = safe_attr(term, :fmt_definition)
465
- return extract_plain_text(fmt_def) if fmt_def
466
-
467
- term_p = safe_attr(term, :p)
468
- if term_p && !term_p.empty?
469
- text = term_p.map { |para| extract_plain_text(para) }.join(" ")
470
- return text.strip unless text.strip.empty?
488
+ if term.fmt_definition
489
+ rendered = capture_output do
490
+ render_ordered_content(term.fmt_definition)
491
+ end
492
+ return strip_html(rendered).gsub(/\s+/, " ").strip
493
+ end
494
+ if term.p && !term.p.empty?
495
+ rendered = capture_output do
496
+ term.p.each do |para|
497
+ render_paragraph(para)
498
+ end
499
+ end
500
+ return strip_html(rendered).gsub(/\s+/, " ").strip
471
501
  end
472
502
  nil
473
503
  end
474
504
 
505
+ def strip_html(html)
506
+ html.gsub(/<[^>]+>/, "").gsub("&lt;", "<").gsub("&gt;", ">")
507
+ .gsub("&amp;", "&").gsub("&nbsp;", " ")
508
+ end
509
+
475
510
  def render_term_designation(designation, type)
476
511
  css_class = type == "deprecated" ? "term-deprecated" : "term-name"
477
- name = extract_designation_name(designation)
478
- inner = if name
479
- escape_html(name)
480
- else
481
- render_mixed_inline(designation) || ""
482
- end
512
+ @output << "<p class=\"#{css_class}\" style=\"text-align:left;\">"
513
+ @output << "<del>" if type == "deprecated"
514
+ @output << "<b><dfn>"
483
515
 
484
- dfn_content = render_liquid("_element.html.liquid",
485
- { "tag" => "dfn", "extra_attrs" => "",
486
- "content" => inner })
487
- b_content = render_liquid("_element.html.liquid",
488
- { "tag" => "b", "extra_attrs" => "",
489
- "content" => dfn_content })
490
-
491
- if type == "deprecated"
492
- b_content = render_liquid("_element.html.liquid",
493
- { "tag" => "del", "extra_attrs" => "",
494
- "content" => b_content })
516
+ name = extract_designation_name(designation)
517
+ if name
518
+ @output << escape_html(name)
519
+ else
520
+ # Fallback: render mixed content (e.g. <strong> element)
521
+ render_mixed_inline(designation)
495
522
  end
496
523
 
497
- render_liquid("_element.html.liquid", {
498
- "tag" => "p",
499
- "extra_attrs" => " class=\"#{css_class}\" style=\"text-align:left;\"",
500
- "content" => b_content,
501
- })
524
+ @output << "</dfn></b>"
525
+ @output << "</del>" if type == "deprecated"
526
+ @output << "</p>"
502
527
  end
503
528
 
504
529
  def extract_designation_name(designation)
530
+ # Try expression.name first (TermNameElement objects with text)
505
531
  expr = designation.expression
506
532
  if expr.is_a?(Metanorma::IsoDocument::Terms::TermExpression) && expr.name
507
533
  names = Array(expr.name)
@@ -509,6 +535,7 @@ module Metanorma
509
535
  return text unless text.strip.empty?
510
536
  end
511
537
 
538
+ # Try text attribute from mixed content
512
539
  texts = safe_attr(designation, :text)
513
540
  if texts
514
541
  joined = texts.is_a?(Array) ? texts.join : texts.to_s
@@ -531,82 +558,113 @@ module Metanorma
531
558
  end
532
559
 
533
560
  def render_term_definition(definition)
534
- return nil unless definition
561
+ return unless definition
535
562
 
536
- parts = []
537
563
  rendered = false
538
564
 
539
- vd = safe_attr(definition,
540
- :verbal_definition) || safe_attr(definition,
541
- :verbalexpression)
565
+ # Try verbal_definition first (ISO semantic format)
566
+ vd = definition.verbal_definition
542
567
  if vd
543
- vd_p = safe_attr(vd, :p) || safe_attr(vd, :paragraph)
544
- vd_p&.each { |para| parts << (render_paragraph(para) || "") }
545
- vd.ul&.each { |ul| parts << (render_unordered_list(ul) || "") }
546
- vd.ol&.each { |ol| parts << (render_ordered_list(ol) || "") }
568
+ vd.p&.each { |para| render_paragraph(para) }
569
+ vd.ul&.each { |ul| render_unordered_list(ul) }
570
+ vd.ol&.each { |ol| render_ordered_list(ol) }
547
571
  rendered = true
548
572
  end
549
573
 
574
+ # Try direct p (presentation XML format where <p> is inside <definition>)
550
575
  p_children = safe_attr(definition, :p)
551
576
  if !rendered && p_children && !p_children.empty?
552
- p_children.each { |para| parts << (render_paragraph(para) || "") }
577
+ p_children.each { |para| render_paragraph(para) }
553
578
  rendered = true
554
579
  end
555
580
 
581
+ # Fallback: render via element_order
556
582
  unless rendered
557
- parts << (render_ordered_content(definition) || "")
583
+ render_ordered_content(definition)
558
584
  end
559
- parts.join
560
585
  end
561
586
 
562
587
  # --- Boilerplate rendering ---
563
588
 
564
589
  def render_boilerplate(boilerplate, **_opts)
565
- return nil unless boilerplate
590
+ return unless boilerplate
566
591
 
567
- parts = []
568
- parts << (render_boilerplate_items(boilerplate.copyright_statement) || "")
569
- parts << (render_boilerplate_items(boilerplate.license_statement) || "")
570
- parts << (render_boilerplate_items(boilerplate.legal_statement) || "")
571
- parts << (render_boilerplate_items(boilerplate.feedback_statement) || "")
572
- parts << (render_boilerplate_items(boilerplate.clause) || "")
592
+ content = boilerplate_to_xml(boilerplate)
593
+ return unless content
573
594
 
574
- render_liquid("_boilerplate.html.liquid", content: parts.join)
575
- end
595
+ @output << "<div class=\"boilerplate-copyright\">"
576
596
 
577
- def render_boilerplate_items(items)
578
- return nil unless items
597
+ raw = content.to_s
598
+ clean = convert_boilerplate_links(raw)
599
+ .gsub(/<fmt-title[^>]*>.*?<\/fmt-title>/m, "")
600
+ .gsub(/<semx[^>]*>.*?<\/semx>/m, "")
601
+ .gsub(/<title[^>]*>.*?<\/title>/m, "")
602
+ .gsub(/<fmt-xref-label[^>]*>.*?<\/fmt-xref-label>/m, "")
603
+ .gsub(/<variant-title[^>]*>.*?<\/variant-title>/m, "")
604
+ .gsub(/<\/?(?:copyright-statement|clause)[^>]*>/, "")
579
605
 
580
- Array(items).filter_map { |item| render_boilerplate_clause(item) }.join
581
- end
582
-
583
- def render_boilerplate_clause(item)
584
- return nil unless item
585
- return nil unless item.is_a?(Lutaml::Model::Serializable)
606
+ # Remap XML class names to HTML-specific class names
607
+ boilerplate_doc = Nokogiri::HTML::DocumentFragment.parse(clean)
608
+ boilerplate_doc.css("[class]").each do |el|
609
+ el["class"] = el["class"].split(/\s+/).map do |c|
610
+ html_class_for_span(c)
611
+ end.join(" ")
612
+ end
586
613
 
587
- inner = safe_attr(item, :subsection)
588
- targets = if inner && !Array(inner).empty?
589
- Array(inner)
590
- else
591
- [item]
592
- end
614
+ @output << boilerplate_doc.inner_html.strip
593
615
 
594
- targets.filter_map do |section|
595
- render_boilerplate_clause_content(section)
596
- end.join
616
+ @output << "</div>"
597
617
  end
598
618
 
599
- def render_boilerplate_clause_content(section)
600
- fmt_title = safe_attr(section, :fmt_title)
619
+ def boilerplate_to_xml(boilerplate)
601
620
  parts = []
602
- section.each_mixed_content do |child|
603
- next if child.is_a?(String)
604
- next if is_title_element?(child, section)
605
- next if child.equal?(fmt_title)
621
+ %i[copyright_statement license_statement legal_statement
622
+ feedback_statement clause].each do |attr|
623
+ items = boilerplate.public_send(attr)
624
+ next unless items
625
+
626
+ tag = attr.to_s.gsub("_", "-")
627
+ items.each do |item|
628
+ inner = item.to_xml
629
+ next if !inner || inner.strip.empty?
630
+
631
+ # Strip the outer <clause> wrapper — the inner content is what matters
632
+ stripped = inner.strip
633
+ stripped = stripped.sub(%r{^<clause[^>]*>\n?}, "").sub(%r{\n?</clause>$}, "")
634
+ parts << "<#{tag}>#{stripped}</#{tag}>"
635
+ end
636
+ end
637
+ return nil if parts.empty?
606
638
 
607
- parts << (render(child) || "")
639
+ parts.join("\n")
640
+ end
641
+
642
+ def convert_boilerplate_links(raw)
643
+ doc = Nokogiri::XML.fragment(raw)
644
+ doc.css("link").each do |link|
645
+ target = link["target"] || link["href"]
646
+ next_sib = link.next_sibling
647
+ while next_sib.is_a?(Nokogiri::XML::Text) && next_sib.text.strip.empty?
648
+ next_sib = next_sib.next_sibling
649
+ end
650
+
651
+ display_text = if next_sib&.element? && next_sib.name == "semx"
652
+ fmt_link = next_sib.at_css("fmt-link")
653
+ if fmt_link
654
+ fmt_target = fmt_link["target"] || fmt_link["href"] || target
655
+ display_text = fmt_target.to_s.delete_prefix("mailto:")
656
+ next_sib.remove
657
+ display_text
658
+ end
659
+ end
660
+
661
+ display_text ||= target.to_s.delete_prefix("mailto:")
662
+ a_tag = Nokogiri::HTML::DocumentFragment.parse(
663
+ "<a href=\"#{CGI.escapeHTML(target.to_s)}\">#{CGI.escapeHTML(display_text)}</a>",
664
+ )
665
+ link.replace(a_tag)
608
666
  end
609
- parts.join
667
+ doc.inner_html
610
668
  end
611
669
 
612
670
  # --- Helpers ---
@@ -614,18 +672,22 @@ module Metanorma
614
672
  def render_title(section, level)
615
673
  title_element = safe_attr(section,
616
674
  :fmt_title) || safe_attr(section, :title)
617
- return nil unless title_element
675
+ return unless title_element
618
676
 
619
677
  section_id = safe_attr(section, :id)
620
- title_content = render_mixed_inline(title_element)
621
- register_toc_entry(id: section_id, level: level,
622
- text: extract_plain_text(title_element))
678
+ title_text = extract_plain_text(title_element)
679
+ register_toc_entry(id: section_id, level: level, text: title_text)
623
680
 
624
681
  h = "h#{[[level, 6].min, 1].max}"
625
- render_liquid("_heading.html.liquid", tag: h, class_attr: "",
626
- content: title_content)
682
+ @output << "<#{h}>"
683
+ render_mixed_inline(title_element)
684
+ @output << "</#{h}>"
627
685
  end
628
686
 
687
+ # Collect all document-level children (sections, normative refs, annexes,
688
+ # bibliography) sorted by displayorder for correct document order.
689
+ # Top-level paragraphs in sections (title paragraphs) are excluded —
690
+ # they are rendered separately by render_doc_title.
629
691
  def collect_document_children(doc)
630
692
  items = []
631
693
 
@@ -649,7 +711,6 @@ module Metanorma
649
711
  logo_map = publisher_logo_map
650
712
  return [] if publishers.empty? && logo_map.empty?
651
713
 
652
- dark_logo_map = theme.logos_dark
653
714
  display_pubs = publishers.empty? ? logo_map.keys : publishers
654
715
  display_pubs.filter_map do |pub|
655
716
  filename = logo_map[pub]
@@ -658,18 +719,10 @@ module Metanorma
658
719
  svg = load_logo_svg(filename, height: 48)
659
720
  next unless svg
660
721
 
661
- light_span = "<span class=\"cover-logo cover-logo-light\">#{svg}</span>"
662
-
663
- dark_span = ""
664
- dark_filename = dark_logo_map[pub]
665
- if dark_filename
666
- dark_svg = load_logo_svg(dark_filename, height: 48)
667
- if dark_svg
668
- dark_span = "<span class=\"cover-logo cover-logo-dark\">#{dark_svg}</span>"
669
- end
670
- end
671
-
672
- dark_span.empty? ? light_span : "#{light_span}\n#{dark_span}"
722
+ # White fill for dark cover background
723
+ svg = svg.gsub("fill:#00b1ff", "fill:white") if pub == "OGC"
724
+ svg = svg.gsub("fill:#e3000f", "fill:white") if pub == "ISO"
725
+ svg
673
726
  end
674
727
  end
675
728
  end