metanorma-document 0.2.3 → 0.2.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2f0bcb971c3831683b2786c9596f336f6c80597b8747ed05fb7f195eb6dc492
4
- data.tar.gz: bf6154b258dda4bc3e5c6eb8073001d214ee6268f2b5747bb9f73683950f6fa7
3
+ metadata.gz: '08953283a82f46382002daac11ebb5befc241c798702b21d189db6943ba91f46'
4
+ data.tar.gz: 56e998c5b5b02599f863c29136491711f0dcb2b78da77c5c81bdc62967619e70
5
5
  SHA512:
6
- metadata.gz: 74d4cf3024d3096c93df42af04695cbf01581e82ef3e4ac5771900e1fc729bba43f53a5070f748d640755c69a4c54df4cc242558407e26ccad67be91ceac4faa
7
- data.tar.gz: 821daad319b1f3a38457082d3719af6cc23c7f7843bae9eb50841784b9c4a4f8466b9b75ef61aaf3c0079f2081b91194292a3a574cf15b5e2f12f73a01d1f902
6
+ metadata.gz: acdce24a10e9919a5dd2396ed6c51a30387686671f3ec8e17125bfea76adaba020709007ff36cbbc9697e0a5c95e02155a4c8993f95406e895a2958d2d7b7bd6
7
+ data.tar.gz: 3d59fb7d487a8ac32042f4827054c1f2bd301359073bf27840a215f8eae267b91130232124041cec706662e189503b0206c8449cccf57dc562ff2cc6d1104598
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-06 10:09:39 UTC using RuboCop version 1.86.1.
3
+ # on 2026-05-06 16:15:52 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -11,13 +11,14 @@ Gemspec/RequiredRubyVersion:
11
11
  Exclude:
12
12
  - 'metanorma-document.gemspec'
13
13
 
14
- # Offense count: 1
14
+ # Offense count: 5
15
15
  # This cop supports safe autocorrection (--autocorrect).
16
16
  # Configuration parameters: EnforcedStyle, IndentationWidth.
17
17
  # SupportedStyles: with_first_argument, with_fixed_indentation
18
18
  Layout/ArgumentAlignment:
19
19
  Exclude:
20
20
  - 'lib/metanorma/html/component/index_term_collector.rb'
21
+ - 'spec/metanorma/html/renderer/architecture_spec.rb'
21
22
 
22
23
  # Offense count: 5
23
24
  # This cop supports safe autocorrection (--autocorrect).
@@ -54,7 +55,7 @@ Layout/ExtraSpacing:
54
55
  - 'lib/metanorma/html/iho_renderer.rb'
55
56
  - 'lib/metanorma/html/theme.rb'
56
57
 
57
- # Offense count: 809
58
+ # Offense count: 801
58
59
  # This cop supports safe autocorrection (--autocorrect).
59
60
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
60
61
  # URISchemes: http, https
@@ -92,12 +93,14 @@ Lint/EmptyBlock:
92
93
  - 'lib/metanorma/html/drops/formula_drop.rb'
93
94
  - 'spec/metanorma/iso_document/metadata/title_roundtrip_spec.rb'
94
95
 
95
- # Offense count: 2
96
+ # Offense count: 4
96
97
  # Configuration parameters: AllowedParentClasses.
97
98
  Lint/MissingSuper:
98
99
  Exclude:
99
100
  - 'lib/metanorma/html/drops/block_element_drop.rb'
101
+ - 'lib/metanorma/html/drops/figure_list_entry_drop.rb'
100
102
  - 'lib/metanorma/html/drops/footnote_drop.rb'
103
+ - 'lib/metanorma/html/drops/toc_entry_drop.rb'
101
104
 
102
105
  # Offense count: 1
103
106
  Lint/StructNewOverride:
@@ -118,7 +121,7 @@ Lint/UselessConstantScoping:
118
121
  Exclude:
119
122
  - 'lib/metanorma/html/base_renderer.rb'
120
123
 
121
- # Offense count: 80
124
+ # Offense count: 78
122
125
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
123
126
  Metrics/AbcSize:
124
127
  Enabled: false
@@ -134,7 +137,7 @@ Metrics/BlockLength:
134
137
  Metrics/BlockNesting:
135
138
  Max: 4
136
139
 
137
- # Offense count: 60
140
+ # Offense count: 58
138
141
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
139
142
  Metrics/CyclomaticComplexity:
140
143
  Exclude:
@@ -153,7 +156,7 @@ Metrics/CyclomaticComplexity:
153
156
  - 'lib/metanorma/html/theme.rb'
154
157
  - 'lib/metanorma/iso_document/metadata/iso_localized_title.rb'
155
158
 
156
- # Offense count: 85
159
+ # Offense count: 84
157
160
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
158
161
  Metrics/MethodLength:
159
162
  Max: 75
@@ -163,7 +166,7 @@ Metrics/MethodLength:
163
166
  Metrics/ParameterLists:
164
167
  Max: 7
165
168
 
166
- # Offense count: 54
169
+ # Offense count: 53
167
170
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
168
171
  Metrics/PerceivedComplexity:
169
172
  Exclude:
@@ -182,15 +185,16 @@ Metrics/PerceivedComplexity:
182
185
  - 'lib/metanorma/html/theme.rb'
183
186
  - 'lib/metanorma/iso_document/metadata/iso_localized_title.rb'
184
187
 
185
- # Offense count: 2
188
+ # Offense count: 4
186
189
  # This cop supports safe autocorrection (--autocorrect).
187
190
  # Configuration parameters: EnforcedStyle, BlockForwardingName.
188
191
  # SupportedStyles: anonymous, explicit
189
192
  Naming/BlockForwarding:
190
193
  Exclude:
194
+ - 'lib/metanorma/html/base_renderer.rb'
191
195
  - 'lib/metanorma/html/component/footnote_collector.rb'
192
196
 
193
- # Offense count: 39
197
+ # Offense count: 28
194
198
  # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
195
199
  # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
196
200
  Naming/MethodParameterName:
@@ -219,11 +223,12 @@ Naming/PredicatePrefix:
219
223
  Exclude:
220
224
  - 'lib/metanorma/html/standard_renderer.rb'
221
225
 
222
- # Offense count: 14
226
+ # Offense count: 15
223
227
  # Configuration parameters: IgnoredMetadata.
224
228
  RSpec/DescribeClass:
225
229
  Exclude:
226
230
  - 'spec/metanorma/document_spec.rb'
231
+ - 'spec/metanorma/html/renderer/architecture_spec.rb'
227
232
  - 'spec/metanorma/html/renderer/class_ownership_spec.rb'
228
233
  - 'spec/metanorma/html/renderer/dead_code_removal_spec.rb'
229
234
  - 'spec/metanorma/html/renderer/section_rendering_spec.rb'
@@ -253,7 +258,12 @@ RSpec/MultipleDescribes:
253
258
  Exclude:
254
259
  - 'spec/metanorma/document_spec.rb'
255
260
 
256
- # Offense count: 303
261
+ # Offense count: 1
262
+ # Configuration parameters: AllowSubject.
263
+ RSpec/MultipleMemoizedHelpers:
264
+ Max: 7
265
+
266
+ # Offense count: 322
257
267
  # Configuration parameters: AllowedPatterns.
258
268
  # AllowedPatterns: ^expect_, ^assert_
259
269
  RSpec/NoExpectationExample:
@@ -265,7 +275,7 @@ RSpec/NoExpectationExample:
265
275
  RSpec/SpecFilePathFormat:
266
276
  Enabled: false
267
277
 
268
- # Offense count: 2
278
+ # Offense count: 4
269
279
  # This cop supports safe autocorrection (--autocorrect).
270
280
  # Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames.
271
281
  # RedundantRestArgumentNames: args, arguments
@@ -273,6 +283,7 @@ RSpec/SpecFilePathFormat:
273
283
  # RedundantBlockArgumentNames: blk, block, proc
274
284
  Style/ArgumentsForwarding:
275
285
  Exclude:
286
+ - 'lib/metanorma/html/base_renderer.rb'
276
287
  - 'lib/metanorma/html/component/footnote_collector.rb'
277
288
 
278
289
  # Offense count: 1
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Metanorma
4
4
  module Document
5
- VERSION = "0.2.3"
5
+ VERSION = "0.2.5"
6
6
  Version = VERSION
7
7
  end
8
8
  end
@@ -83,33 +83,37 @@ module Metanorma
83
83
  # --- Public API ---
84
84
 
85
85
  # Facade object for Drops to call renderer methods without exposing
86
- # the full private interface. Delegates to the renderer internally.
86
+ # the full private interface. Delegates via method_missing with an
87
+ # explicit allowlist — adding new delegations only requires updating
88
+ # DELEGATED_METHODS, not writing a new one-liner.
87
89
  class RendererContext
90
+ DELEGATED_METHODS = %i[
91
+ safe_attr escape_html extract_block_label extract_plain_text
92
+ capture_output render_paragraph render_mixed_inline
93
+ render_inline_element render_unordered_list render_ordered_list
94
+ render_definition_list render_sourcecode render_table
95
+ render_figure render_quote render_formula render_note
96
+ render_image render_stem_content register_figure_entry
97
+ render_liquid
98
+ ].freeze
99
+
88
100
  def initialize(renderer)
89
101
  @renderer = renderer
90
102
  end
91
103
 
92
- def safe_attr(obj, method_name) = @renderer.send(:safe_attr, obj, method_name)
93
- def escape_html(text) = @renderer.send(:escape_html, text)
94
- def extract_block_label(block, default) = @renderer.send(:extract_block_label, block, default)
95
- def extract_plain_text(node) = @renderer.send(:extract_plain_text, node)
96
- def capture_output(&) = @renderer.send(:capture_output, &)
97
- def render_paragraph(p) = @renderer.send(:render_paragraph, p)
98
- def render_mixed_inline(node) = @renderer.send(:render_mixed_inline, node)
99
- def render_inline_element(el) = @renderer.send(:render_inline_element, el)
100
- def render_unordered_list(ul) = @renderer.send(:render_unordered_list, ul)
101
- def render_ordered_list(ol) = @renderer.send(:render_ordered_list, ol)
102
- def render_definition_list(dl) = @renderer.send(:render_definition_list, dl)
103
- def render_sourcecode(sc) = @renderer.send(:render_sourcecode, sc)
104
- def render_table(t) = @renderer.send(:render_table, t)
105
- def render_figure(f) = @renderer.send(:render_figure, f)
106
- def render_quote(q) = @renderer.send(:render_quote, q)
107
- def render_formula(f) = @renderer.send(:render_formula, f)
108
- def render_note(n) = @renderer.send(:render_note, n)
109
- def render_image(img) = @renderer.send(:render_image, img)
110
- def render_stem_content(stem) = @renderer.send(:render_stem_content, stem)
111
- def register_figure_entry(...) = @renderer.send(:register_figure_entry, ...)
112
- def render_liquid(template_name, assigns) = @renderer.send(:render_liquid, template_name, assigns)
104
+ def respond_to_missing?(method_name, include_private = false)
105
+ DELEGATED_METHODS.include?(method_name) || super
106
+ end
107
+
108
+ private
109
+
110
+ def method_missing(method_name, *args, **kwargs, &block)
111
+ if DELEGATED_METHODS.include?(method_name)
112
+ @renderer.public_send(method_name, *args, **kwargs, &block)
113
+ else
114
+ super
115
+ end
116
+ end
113
117
  end
114
118
 
115
119
  def renderer_context
@@ -265,41 +269,17 @@ module Metanorma
265
269
  # --- ToC generation ---
266
270
 
267
271
  def build_toc_html(entries)
268
- top_lines = []
269
- main_lines = if entries.empty?
270
- ["<li class=\"toc-empty\">No entries</li>"]
271
- else
272
- entries.map do |e|
273
- id = e[:id].to_s
274
- text = escape_html(e[:text].to_s)
275
- lvl = e[:level]
276
- "<li class=\"toc-level-#{lvl}\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
277
- end
278
- end
279
-
280
- # List of Figures — at top of sidebar
281
- unless @figure_entries.empty?
282
- top_lines << "<li class=\"toc-list-header\" data-list=\"figures\"><button class=\"toc-list-toggle\" aria-expanded=\"false\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><rect x=\"1\" y=\"2\" width=\"14\" height=\"12\" rx=\"1\"/><circle cx=\"5\" cy=\"6.5\" r=\"1.5\"/><path d=\"M1 12l4-4 2 2 3-3 5 5\"/></svg> Figures <span class=\"toc-list-count\">(#{@figure_entries.size})</span></button></li>"
283
- @figure_entries.each do |f|
284
- id = f[:id].to_s
285
- text = escape_html(f[:text].to_s)
286
- top_lines << "<li class=\"toc-list-item toc-figures\" style=\"display:none\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
287
- end
288
- end
289
-
290
- # List of Tables — at top of sidebar
291
- unless @table_entries.empty?
292
- top_lines << "<li class=\"toc-list-header\" data-list=\"tables\"><button class=\"toc-list-toggle\" aria-expanded=\"false\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><rect x=\"1\" y=\"2\" width=\"14\" height=\"12\" rx=\"1\"/><line x1=\"1\" y1=\"6\" x2=\"15\" y2=\"6\"/><line x1=\"1\" y1=\"10\" x2=\"15\" y2=\"10\"/><line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"14\"/></svg> Tables <span class=\"toc-list-count\">(#{@table_entries.size})</span></button></li>"
293
- @table_entries.each do |t|
294
- id = t[:id].to_s
295
- text = escape_html(t[:text].to_s)
296
- top_lines << "<li class=\"toc-list-item toc-tables\" style=\"display:none\"><a href=\"##{id}\" class=\"toc-link\" data-target=\"#{id}\">#{text}</a></li>"
297
- end
298
- end
299
-
300
- top_lines << "<li class=\"toc-divider\"></li>" unless top_lines.empty?
301
-
302
- (top_lines + main_lines).join("\n")
272
+ entry_drops = entries.map { |e| Drops::TocEntryDrop.new(e) }
273
+ figure_drops = @figure_entries.map { |f| Drops::FigureListEntryDrop.new(f) }
274
+ table_drops = @table_entries.map { |t| Drops::FigureListEntryDrop.new(t) }
275
+ has_special_lists = !@figure_entries.empty? || !@table_entries.empty?
276
+
277
+ render_liquid("_toc.html.liquid", {
278
+ "entries" => entry_drops,
279
+ "figures" => figure_drops,
280
+ "tables" => table_drops,
281
+ "has_special_lists" => has_special_lists,
282
+ })
303
283
  end
304
284
 
305
285
  # --- Scripts ---
@@ -496,7 +476,7 @@ module Metanorma
496
476
  return escape_html(node) if node.is_a?(String)
497
477
 
498
478
  method = lookup_dispatch(node.class, :render_registry)
499
- method ? send(method, node, **) : ""
479
+ method ? public_send(method, node, **) : ""
500
480
  end
501
481
 
502
482
  # Dispatch to the appropriate inline render method via type registry.
@@ -506,7 +486,7 @@ module Metanorma
506
486
 
507
487
  method = lookup_dispatch(element.class, :inline_registry)
508
488
  if method
509
- send(method, element)
489
+ public_send(method, element)
510
490
  elsif element.is_a?(Lutaml::Model::Serializable) && element.mixed?
511
491
  render_mixed_inline(element)
512
492
  end
@@ -587,13 +567,11 @@ module Metanorma
587
567
  register_inline_render Metanorma::Document::Components::Inline::FmtIdentifierElement, :render_mixed_inline
588
568
  register_inline_render Metanorma::Document::Components::Inline::FmtSourcecodeElement, :render_mixed_inline
589
569
 
590
- private
591
-
592
570
  def lookup_dispatch(type_class, registry_method)
593
571
  self.class.ancestors.each do |ancestor|
594
- next unless ancestor.respond_to?(registry_method)
572
+ next unless ancestor.is_a?(Class) && (ancestor == BaseRenderer || ancestor < BaseRenderer)
595
573
 
596
- registry = ancestor.send(registry_method)
574
+ registry = ancestor.public_send(registry_method)
597
575
  method_name = registry[type_class]
598
576
  return method_name if method_name
599
577
  end
@@ -982,7 +960,7 @@ module Metanorma
982
960
  # Apply optional filter (used by semx to skip semantic attrs)
983
961
  next if allow_filter && !allow_filter.include?(attr_name)
984
962
 
985
- coll = node.send(attr_name)
963
+ coll = node.public_send(attr_name)
986
964
  obj = if coll.is_a?(Array)
987
965
  idx = indices[attr_name]
988
966
  indices[attr_name] += 1
@@ -1076,8 +1054,9 @@ module Metanorma
1076
1054
 
1077
1055
  def raw_content_node?(node)
1078
1056
  node.is_a?(Lutaml::Model::Serializable) &&
1079
- node.respond_to?(:content) &&
1080
1057
  node.content.is_a?(String)
1058
+ rescue NoMethodError
1059
+ false
1081
1060
  end
1082
1061
 
1083
1062
  # Iterate element_order directly, preserving whitespace text nodes
@@ -1103,15 +1082,6 @@ module Metanorma
1103
1082
  end
1104
1083
  end
1105
1084
  end
1106
- rescue StandardError
1107
- node.each_mixed_content do |child|
1108
- case child
1109
- when String
1110
- @output << escape_html(child)
1111
- else
1112
- render_inline_element(child)
1113
- end
1114
- end
1115
1085
  end
1116
1086
 
1117
1087
  # Inline adapter methods for registry dispatch
@@ -1207,17 +1177,14 @@ module Metanorma
1207
1177
  end
1208
1178
 
1209
1179
  def render_inline_collections(node)
1210
- # Fallback: render text and inline collections sequentially
1211
1180
  texts = node.text
1212
1181
  if texts.is_a?(Array)
1213
1182
  texts.each do |t|
1214
- @output << if t.is_a?(Metanorma::Document::Components::Inline::MathElement)
1215
- t.content.to_s
1216
- elsif t.is_a?(Metanorma::Document::Components::Inline::AsciimathElement)
1217
- %(<span class="stem">#{escape_html(Array(t.text).join)}</span>)
1218
- else
1219
- escape_html(t.to_s)
1220
- end
1183
+ if t.is_a?(String)
1184
+ @output << escape_html(t)
1185
+ else
1186
+ render_inline_element(t)
1187
+ end
1221
1188
  end
1222
1189
  elsif texts.is_a?(String)
1223
1190
  @output << escape_html(texts)
@@ -1464,7 +1431,7 @@ module Metanorma
1464
1431
  return stem.math.to_xml
1465
1432
  rescue StandardError
1466
1433
  math_items = Array(stem.math)
1467
- return math_items.map { |m| m.respond_to?(:content) ? m.content.to_s : m.to_s }.join
1434
+ return math_items.map { |m| m.is_a?(Lutaml::Model::Serializable) ? m.content.to_s : m.to_s }.join
1468
1435
  end
1469
1436
  end
1470
1437
  if stem.asciimath
@@ -1499,34 +1466,36 @@ module Metanorma
1499
1466
  parts.join
1500
1467
  end
1501
1468
 
1502
- BLOCK_TYPES = Set[
1503
- Metanorma::Document::Components::Paragraphs::ParagraphBlock,
1504
- Metanorma::Document::Components::Tables::TableBlock,
1505
- Metanorma::Document::Components::Lists::UnorderedList,
1506
- Metanorma::Document::Components::Lists::OrderedList,
1507
- Metanorma::Document::Components::Lists::DefinitionList,
1508
- Metanorma::Document::Components::AncillaryBlocks::FigureBlock,
1509
- Metanorma::Document::Components::Blocks::NoteBlock,
1510
- Metanorma::Document::Components::AncillaryBlocks::ExampleBlock,
1511
- Metanorma::Document::Components::AncillaryBlocks::SourcecodeBlock,
1512
- Metanorma::Document::Components::AncillaryBlocks::FormulaBlock,
1513
- Metanorma::Document::Components::MultiParagraph::QuoteBlock,
1514
- Metanorma::Document::Components::MultiParagraph::AdmonitionBlock,
1515
- Metanorma::Document::Components::Sections::HierarchicalSection,
1516
- Metanorma::Document::Components::Sections::BasicSection,
1517
- Metanorma::Document::Components::Sections::ContentSection,
1518
- ].freeze
1469
+ BLOCK_TYPES = {
1470
+ Metanorma::Document::Components::Paragraphs::ParagraphBlock => true,
1471
+ Metanorma::Document::Components::Tables::TableBlock => true,
1472
+ Metanorma::Document::Components::Lists::UnorderedList => true,
1473
+ Metanorma::Document::Components::Lists::OrderedList => true,
1474
+ Metanorma::Document::Components::Lists::DefinitionList => true,
1475
+ Metanorma::Document::Components::AncillaryBlocks::FigureBlock => true,
1476
+ Metanorma::Document::Components::Blocks::NoteBlock => true,
1477
+ Metanorma::Document::Components::AncillaryBlocks::ExampleBlock => true,
1478
+ Metanorma::Document::Components::AncillaryBlocks::SourcecodeBlock => true,
1479
+ Metanorma::Document::Components::AncillaryBlocks::FormulaBlock => true,
1480
+ Metanorma::Document::Components::MultiParagraph::QuoteBlock => true,
1481
+ Metanorma::Document::Components::MultiParagraph::AdmonitionBlock => true,
1482
+ Metanorma::Document::Components::Sections::HierarchicalSection => true,
1483
+ Metanorma::Document::Components::Sections::BasicSection => true,
1484
+ Metanorma::Document::Components::Sections::ContentSection => true,
1485
+ }.freeze
1519
1486
 
1520
1487
  def html_class_for_span(xml_class)
1521
1488
  SPAN_ROLE_CLASSES[xml_class] || "span-#{xml_class}"
1522
1489
  end
1523
1490
 
1524
1491
  def block_element?(obj)
1525
- BLOCK_TYPES.any? { |type| obj.is_a?(type) }
1492
+ BLOCK_TYPES[obj.class] || BLOCK_TYPES.any? { |type, _| obj.is_a?(type) }
1526
1493
  end
1527
1494
 
1528
1495
  def safe_attr(obj, method_name)
1529
- obj.public_send(method_name) if obj.respond_to?(method_name)
1496
+ obj.public_send(method_name)
1497
+ rescue NoMethodError
1498
+ nil
1530
1499
  end
1531
1500
 
1532
1501
  def collect_index_term(element)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Html
5
+ module Drops
6
+ class FigureListEntryDrop < Liquid::Drop
7
+ def initialize(entry)
8
+ @entry = entry
9
+ end
10
+
11
+ def id = @entry[:id]
12
+ def text = @entry[:text]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Html
5
+ module Drops
6
+ class TocEntryDrop < Liquid::Drop
7
+ def initialize(entry)
8
+ @entry = entry
9
+ end
10
+
11
+ def id = @entry[:id]
12
+ def text = @entry[:text]
13
+ def level = @entry[:level]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -11,6 +11,8 @@ module Metanorma
11
11
  autoload :SourcecodeDrop, "metanorma/html/drops/sourcecode_drop"
12
12
  autoload :FormulaDrop, "metanorma/html/drops/formula_drop"
13
13
  autoload :FigureDrop, "metanorma/html/drops/figure_drop"
14
+ autoload :TocEntryDrop, "metanorma/html/drops/toc_entry_drop"
15
+ autoload :FigureListEntryDrop, "metanorma/html/drops/figure_list_entry_drop"
14
16
  end
15
17
  end
16
18
  end
@@ -133,7 +133,7 @@ module Metanorma
133
133
 
134
134
  # Extract document type from ext.doctype.
135
135
  def extract_doctype(bibdata)
136
- return nil unless bibdata.respond_to?(:ext)
136
+ return nil unless bibdata.is_a?(Metanorma::IsoDocument::Metadata::IsoBibliographicItem)
137
137
 
138
138
  ext = bibdata.ext
139
139
  return nil unless ext
@@ -154,8 +154,6 @@ module Metanorma
154
154
  val.strip.empty? ? nil : val
155
155
  end
156
156
 
157
- private
158
-
159
157
  # --- Top-level document rendering ---
160
158
 
161
159
  def render_document(doc, **_opts)
@@ -19,8 +19,6 @@ module Metanorma
19
19
  register_render Metanorma::StandardDocument::Sections::FloatingTitle, :render_floating_title
20
20
  register_render Metanorma::StandardDocument::Blocks::AmendBlock, :render_amend_block
21
21
 
22
- private
23
-
24
22
  # --- Top-level document rendering ---
25
23
 
26
24
  def render_standard_document(doc, **_opts)
@@ -50,7 +48,7 @@ module Metanorma
50
48
  end
51
49
 
52
50
  # Index section from indexsect element (presentation XML)
53
- if doc.respond_to?(:indexsect) && doc.indexsect
51
+ if doc.is_a?(Metanorma::IsoDocument::Root) && doc.indexsect
54
52
  render(doc.indexsect)
55
53
  end
56
54
 
@@ -0,0 +1,27 @@
1
+ {% for fig in figures %}
2
+ <li class="toc-list-header" data-list="figures"><button class="toc-list-toggle" aria-expanded="false"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="2" width="14" height="12" rx="1"/><circle cx="5" cy="6.5" r="1.5"/><path d="M1 12l4-4 2 2 3-3 5 5"/></svg> Figures <span class="toc-list-count">({{ figures.size }})</span></button></li>
3
+ {% break %}
4
+ {% endfor %}
5
+ {% for fig in figures %}
6
+ <li class="toc-list-item toc-figures" style="display:none"><a href="#{{ fig.id }}" class="toc-link" data-target="{{ fig.id }}">{{ fig.text | escape }}</a></li>
7
+ {% endfor %}
8
+
9
+ {% for tbl in tables %}
10
+ <li class="toc-list-header" data-list="tables"><button class="toc-list-toggle" aria-expanded="false"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="2" width="14" height="12" rx="1"/><line x1="1" y1="6" x2="15" y2="6"/><line x1="1" y1="10" x2="15" y2="10"/><line x1="7" y1="2" x2="7" y2="14"/></svg> Tables <span class="toc-list-count">({{ tables.size }})</span></button></li>
11
+ {% break %}
12
+ {% endfor %}
13
+ {% for tbl in tables %}
14
+ <li class="toc-list-item toc-tables" style="display:none"><a href="#{{ tbl.id }}" class="toc-link" data-target="{{ tbl.id }}">{{ tbl.text | escape }}</a></li>
15
+ {% endfor %}
16
+
17
+ {% if has_special_lists %}
18
+ <li class="toc-divider"></li>
19
+ {% endif %}
20
+
21
+ {% if entries.size == 0 %}
22
+ <li class="toc-empty">No entries</li>
23
+ {% else %}
24
+ {% for entry in entries %}
25
+ <li class="toc-level-{{ entry.level }}"><a href="#{{ entry.id }}" class="toc-link" data-target="{{ entry.id }}">{{ entry.text | escape }}</a></li>
26
+ {% endfor %}
27
+ {% endif %}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-document
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-06 00:00:00.000000000 Z
11
+ date: 2026-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -486,10 +486,12 @@ files:
486
486
  - lib/metanorma/html/drops/block_element_drop.rb
487
487
  - lib/metanorma/html/drops/example_drop.rb
488
488
  - lib/metanorma/html/drops/figure_drop.rb
489
+ - lib/metanorma/html/drops/figure_list_entry_drop.rb
489
490
  - lib/metanorma/html/drops/footnote_drop.rb
490
491
  - lib/metanorma/html/drops/formula_drop.rb
491
492
  - lib/metanorma/html/drops/note_drop.rb
492
493
  - lib/metanorma/html/drops/sourcecode_drop.rb
494
+ - lib/metanorma/html/drops/toc_entry_drop.rb
493
495
  - lib/metanorma/html/generator.rb
494
496
  - lib/metanorma/html/icc_renderer.rb
495
497
  - lib/metanorma/html/iec_renderer.rb
@@ -516,6 +518,7 @@ files:
516
518
  - lib/metanorma/html/templates/_iso_doc_title.html.liquid
517
519
  - lib/metanorma/html/templates/_note.html.liquid
518
520
  - lib/metanorma/html/templates/_sourcecode.html.liquid
521
+ - lib/metanorma/html/templates/_toc.html.liquid
519
522
  - lib/metanorma/html/templates/document.html.liquid
520
523
  - lib/metanorma/html/theme.rb
521
524
  - lib/metanorma/html/whitespace_patch.rb