Module: JekyllMermaidPrebuild::EmojiCompensator
- Defined in:
- lib/jekyll-mermaid-prebuild/emoji_compensator.rb
Overview
Stateless module: compensates for headless Chromium emoji width undermeasurement by appending padding to emoji-containing node labels before mmdc renders.
Constant Summary collapse
- EMOJI_RE =
Match a single Extended_Pictographic codepoint (emoji).
/\p{Extended_Pictographic}/- BR_RE =
%r{(<br\s*/?>)}i- NBSP =
" "
Class Method Summary collapse
-
.compensate(mermaid_source, diagram_type) ⇒ String
If diagram_type is enabled for compensation, pad emoji-containing labels with non-breaking spaces (2 per emoji).
- .compensate_flowchart_labels(source) ⇒ Object
-
.count_emoji(text) ⇒ Integer
Count Extended_Pictographic codepoints in string.
-
.detect_diagram_type(mermaid_source) ⇒ String?
Detect diagram type from Mermaid source.
-
.pad_label_content(content) ⇒ String
Pad the longest line in a label if it contains emoji.
-
.visual_length(text) ⇒ Integer
Visual length for longest-line comparison.
Class Method Details
.compensate(mermaid_source, diagram_type) ⇒ String
If diagram_type is enabled for compensation, pad emoji-containing labels with non-breaking spaces (2 per emoji). Otherwise return source unchanged.
44 45 46 47 48 |
# File 'lib/jekyll-mermaid-prebuild/emoji_compensator.rb', line 44 def self.compensate(mermaid_source, diagram_type) return compensate_flowchart_labels(mermaid_source) if diagram_type == "flowchart" mermaid_source end |
.compensate_flowchart_labels(source) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/jekyll-mermaid-prebuild/emoji_compensator.rb', line 93 def self.compensate_flowchart_labels(source) result = source [ [/\["(.+?)"\]/m, '["', '"]'], [/\['(.+?)'\]/m, "['", "']"], [/\("(.+?)"\)/m, '("', '")'], [/\{"(.+?)"\}/m, '{"', '"}'] ].each do |regex, open_str, close_str| result = result.gsub(regex) do "#{open_str}#{pad_label_content(Regexp.last_match(1))}#{close_str}" end end result.gsub(%r{\[/"((?:[^"\\]|\\.)+?)"/\]}) do "[/\"#{pad_label_content(Regexp.last_match(1))}\"/]" end end |
.count_emoji(text) ⇒ Integer
Count Extended_Pictographic codepoints in string.
57 58 59 |
# File 'lib/jekyll-mermaid-prebuild/emoji_compensator.rb', line 57 def self.count_emoji(text) text.scan(EMOJI_RE).length end |
.detect_diagram_type(mermaid_source) ⇒ String?
Detect diagram type from Mermaid source. Skips frontmatter (---), %% comments, and blank lines. Returns the first token of the first content line; normalizes "graph" to "flowchart". Returns nil if no diagram type line found.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/jekyll-mermaid-prebuild/emoji_compensator.rb', line 16 def self.detect_diagram_type(mermaid_source) return nil unless mermaid_source in_frontmatter = false mermaid_source.each_line do |line| stripped = line.strip if stripped == "---" in_frontmatter = !in_frontmatter next end next if in_frontmatter next if stripped.empty? next if stripped.start_with?("%%") token = stripped[/\A\S+/] return token == "graph" ? "flowchart" : token end nil end |
.pad_label_content(content) ⇒ String
Pad the longest line in a label if it contains emoji.
Splits on
variants, finds the visually longest line, and only
pads that line (shorter lines center naturally in the wider container).
When no padding is applied, returns the same String object (identity),
not merely an equal copy.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/jekyll-mermaid-prebuild/emoji_compensator.rb', line 78 def self.pad_label_content(content) segments = content.split(BR_RE) # Even indices are text lines; odd indices are the captured <br> delimiters. line_pairs = segments.each_with_index.select { |_segment, index| index.even? } return content if line_pairs.empty? # max_by keeps the first of equal-length ties — pad that one index only. longest_line, longest_idx = line_pairs.max_by { |segment, _index| visual_length(segment) } emoji_count = count_emoji(longest_line) return content unless emoji_count.positive? segments[longest_idx] = "#{longest_line}#{NBSP * (emoji_count * 2)}" segments.join end |
.visual_length(text) ⇒ Integer
Visual length for longest-line comparison. Each emoji counts as 2 because emoji glyphs render roughly double the width of a regular character.
66 67 68 |
# File 'lib/jekyll-mermaid-prebuild/emoji_compensator.rb', line 66 def self.visual_length(text) text.length + count_emoji(text) end |